Allow passing raw CLI arguments in libsyncthing interface

This commit is contained in:
Martchus 2018-04-17 23:54:43 +02:00
parent f7dc8c93bd
commit fbbf533a37
4 changed files with 116 additions and 28 deletions

@ -1 +1 @@
Subproject commit c5832cc1a8997ca06bfcf8a72a3b056f2e6c823d
Subproject commit bdd181e36aaf1a7cc6ac817cea770df1d8e746e6

View File

@ -8,6 +8,8 @@ using namespace std;
namespace LibSyncthing {
///! \cond
/*!
* \brief Holds the user provided logging callback which is assigned via setLoggingCallback().
*/
@ -63,6 +65,38 @@ void handleLoggingCallback(int logLevelInt, const char *msg, size_t msgSize)
loggingCallback(logLevel, msg, msgSize);
}
class RunningState {
public:
RunningState();
~RunningState();
operator bool() const;
private:
bool m_invalid;
};
inline RunningState::RunningState()
{
if ((m_invalid = syncthingRunning.load())) {
return;
}
::libst_loggingCallbackFunction = handleLoggingCallback;
syncthingRunning.store(true);
}
inline RunningState::~RunningState()
{
::libst_loggingCallbackFunction = nullptr;
syncthingRunning.store(false);
}
inline RunningState::operator bool() const
{
return !m_invalid;
}
///! \endcond
/*!
* \brief Sets the callback function for logging. It will be called when a new log message becomes available.
* \remarks The callback is not necessarily invoked in the same thread it was registered.
@ -91,15 +125,12 @@ void setLoggingCallback(LoggingCallback &&callback)
*/
long long runSyncthing(const RuntimeOptions &options)
{
if (syncthingRunning.load()) {
const RunningState runningState;
if (!runningState) {
return -1;
}
syncthingRunning.store(true);
::libst_loggingCallbackFunction = handleLoggingCallback;
const auto exitCode = ::libst_runSyncthing(
return ::libst_runSyncthingWithConfig(
gostr(options.configDir), gostr(options.guiAddress), gostr(options.guiApiKey), gostr(options.logFile), options.verbose);
syncthingRunning.store(false);
return exitCode;
}
/*!
@ -112,15 +143,35 @@ long long runSyncthing(const RuntimeOptions &options)
*/
long long runSyncthing(const std::string &configDir)
{
if (syncthingRunning.load()) {
const RunningState runningState;
if (!runningState) {
return -1;
}
syncthingRunning.store(true);
::libst_loggingCallbackFunction = handleLoggingCallback;
const string empty;
const auto exitCode = ::libst_runSyncthing(gostr(configDir), gostr(empty), gostr(empty), gostr(empty), false);
syncthingRunning.store(false);
return exitCode;
return ::libst_runSyncthingWithConfig(gostr(configDir), gostr(empty), gostr(empty), gostr(empty), false);
}
/*!
* \brief Runs a Syncthing instance using the specified raw \a cliArguments.
* \return Returns the exit code (as usual, zero means no error).
* \remark
* - Does nothing if Syncthing is already running.
* - Blocks the current thread as long as the instance is running.
* Use eg. std::thread(runSyncthing, options) to run it in another thread.
*/
long long runSyncthing(const std::vector<string> &cliArguments)
{
const RunningState runningState;
if (!runningState) {
return -1;
}
vector<const char *> argsAsGoStrings;
argsAsGoStrings.reserve(cliArguments.size());
for (const auto &arg : cliArguments) {
argsAsGoStrings.emplace_back(arg.data());
}
const GoSlice slice{ argsAsGoStrings.data(), static_cast<GoInt>(argsAsGoStrings.size()), static_cast<GoInt>(argsAsGoStrings.capacity()) };
return ::libst_runSyncthingWithArgs(slice);
}
/*!

View File

@ -5,6 +5,7 @@
#include <functional>
#include <string>
#include <vector>
namespace LibSyncthing {
@ -32,6 +33,7 @@ void LIB_SYNCTHING_EXPORT setLoggingCallback(const LoggingCallback &callback);
void LIB_SYNCTHING_EXPORT setLoggingCallback(LoggingCallback &&callback);
long long LIB_SYNCTHING_EXPORT runSyncthing(const RuntimeOptions &options);
long long LIB_SYNCTHING_EXPORT runSyncthing(const std::string &configDir);
long long LIB_SYNCTHING_EXPORT runSyncthing(const std::vector<std::string> &cliArguments);
bool LIB_SYNCTHING_EXPORT isSyncthingRunning();
void LIB_SYNCTHING_EXPORT stopSyncthing();
void LIB_SYNCTHING_EXPORT restartSyncthing();

View File

@ -9,6 +9,9 @@
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cstdlib>
#include <functional>
#include <unistd.h>
using namespace std;
@ -25,20 +28,25 @@ using namespace CPPUNIT_NS;
*/
class InterfaceTests : public TestFixture {
CPPUNIT_TEST_SUITE(InterfaceTests);
CPPUNIT_TEST(testRun);
CPPUNIT_TEST(testRunWidthConfig);
CPPUNIT_TEST(testRunWithArgs);
CPPUNIT_TEST(testVersion);
CPPUNIT_TEST_SUITE_END();
public:
InterfaceTests();
void testRun();
void testInitialState();
void testRunWidthConfig();
void testRunWithArgs();
void testVersion();
void setUp();
void tearDown();
private:
static std::string setupConfigDir();
void testRun(const std::function<long long(void)> &runFunction);
};
CPPUNIT_TEST_SUITE_REGISTRATION(InterfaceTests);
@ -57,26 +65,36 @@ void InterfaceTests::tearDown()
}
/*!
* \brief Tests running Syncthing.
* \brief Initializes the Syncthing config for this fixture (currently using same config as in connector test).
* \returns Returns the config directory.
*/
void InterfaceTests::testRun()
string InterfaceTests::setupConfigDir()
{
// setup Syncthing config (currently using same config as in connector test)
const auto configFilePath(workingCopyPath("testconfig/config.xml"));
if (configFilePath.empty()) {
throw runtime_error("Unable to setup Syncthing config directory.");
}
return directory(configFilePath);
}
/*!
* \brief Tests behavior in initial state, when Syncthing isn't supposed to be running.
*/
void InterfaceTests::testInitialState()
{
CPPUNIT_ASSERT_MESSAGE("initially not running", !isSyncthingRunning());
// stopping and restarting Syncthing when not running should not cause any trouble
stopSyncthing();
restartSyncthing();
}
// setup Syncthing config (currently using same config as in connector test)
const auto configFilePath(workingCopyPath("testconfig/config.xml"));
if (configFilePath.empty()) {
throw runtime_error("Unable to setup Syncthing config directory.");
}
// setup runtime options
RuntimeOptions options;
options.configDir = directory(configFilePath);
/*!
* \brief Tests running Syncthing.
*/
void InterfaceTests::testRun(const std::function<long long()> &runFunction)
{
// keep track of certain log messages
const auto startTime(DateTime::gmtNow());
bool myIdAnnounced = false, performanceAnnounced = false;
@ -127,7 +145,7 @@ void InterfaceTests::testRun()
}
});
CPPUNIT_ASSERT_EQUAL_MESSAGE("Syncthing exited without error", 0ll, runSyncthing(options));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Syncthing exited without error", 0ll, runFunction());
// assert whether all expected log messages were present
CPPUNIT_ASSERT(myIdAnnounced);
@ -142,6 +160,23 @@ void InterfaceTests::testRun()
// FIXME: make this test pass, stop Syncthing correctly
sleep(5);
}
void InterfaceTests::testRunWidthConfig()
{
RuntimeOptions options;
options.configDir = setupConfigDir();
testRun(bind(static_cast<long long (*)(const RuntimeOptions &)>(&runSyncthing), cref(options)));
}
void InterfaceTests::testRunWithArgs()
{
const std::vector<std::string> args{
"-no-restart",
"-no-browser",
"-home",
setupConfigDir(),
};
testRun(bind(static_cast<long long (*)(const decltype(args) &)>(&runSyncthing), cref(args)));
}
/*!