Improve libsyncthing interface
* Allow to stop * Provide callback for logging * Add basic tests
This commit is contained in:
parent
2b1c993a64
commit
aedf7fe1fd
|
@ -16,6 +16,13 @@ set(SRC_FILES
|
||||||
interface.cpp
|
interface.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(TEST_HEADER_FILES
|
||||||
|
)
|
||||||
|
set(TEST_SRC_FILES
|
||||||
|
tests/cppunit.cpp
|
||||||
|
tests/interfacetests.cpp
|
||||||
|
)
|
||||||
|
|
||||||
# find the go binary
|
# find the go binary
|
||||||
find_program(GO_BIN go)
|
find_program(GO_BIN go)
|
||||||
if(NOT GO_BIN)
|
if(NOT GO_BIN)
|
||||||
|
@ -79,6 +86,8 @@ file(GLOB_RECURSE SRC_FILES_SYNCTHING
|
||||||
LIST_DIRECTORIES false
|
LIST_DIRECTORIES false
|
||||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
"${SYNCTHING_PATH}/cmd/*.go"
|
"${SYNCTHING_PATH}/cmd/*.go"
|
||||||
|
"${SYNCTHING_PATH}/cmd/*.h"
|
||||||
|
"${SYNCTHING_PATH}/cmd/*.c"
|
||||||
)
|
)
|
||||||
if(NOT SRC_FILES_SYNCTHING)
|
if(NOT SRC_FILES_SYNCTHING)
|
||||||
message(FATAL_ERROR "No *.go files found in Syncthing checkout \"${CMAKE_CURRENT_SOURCE_DIR}/go/src/github.com/syncthing/syncthing\".")
|
message(FATAL_ERROR "No *.go files found in Syncthing checkout \"${CMAKE_CURRENT_SOURCE_DIR}/go/src/github.com/syncthing/syncthing\".")
|
||||||
|
@ -122,11 +131,17 @@ add_custom_command(
|
||||||
|
|
||||||
# do not use this library directly but depend on it
|
# do not use this library directly but depend on it
|
||||||
list(APPEND PRIVATE_LIBRARIES "${CMAKE_CURRENT_BINARY_DIR}/libsyncthinginternal.a")
|
list(APPEND PRIVATE_LIBRARIES "${CMAKE_CURRENT_BINARY_DIR}/libsyncthinginternal.a")
|
||||||
|
# add additional libraries (not sure if go build could provide this list somehow to make this more generic)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
# add additional libraries for Windows (not sure if go build could provide this list somehow to make this more generic)
|
|
||||||
list(APPEND PRIVATE_LIBRARIES -lws2_32 -lwinmm)
|
list(APPEND PRIVATE_LIBRARIES -lws2_32 -lwinmm)
|
||||||
|
elseif(UNIX)
|
||||||
|
list(APPEND PRIVATE_LIBRARIES -lpthread)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# ensure we can find libsyncthing.h from Syncthing's source tree when building the interface
|
||||||
|
list(APPEND PRIVATE_SHARED_INCLUDE_DIRS "${SYNCTHING_PATH}/cmd/syncthing")
|
||||||
|
list(APPEND PRIVATE_STATIC_INCLUDE_DIRS "${SYNCTHING_PATH}/cmd/syncthing")
|
||||||
|
|
||||||
# find c++utilities
|
# find c++utilities
|
||||||
find_package(c++utilities 4.9.0 REQUIRED)
|
find_package(c++utilities 4.9.0 REQUIRED)
|
||||||
list(APPEND CMAKE_MODULE_PATH ${CPP_UTILITIES_MODULE_DIRS})
|
list(APPEND CMAKE_MODULE_PATH ${CPP_UTILITIES_MODULE_DIRS})
|
||||||
|
@ -137,6 +152,7 @@ list(APPEND PRIVATE_STATIC_INCLUDE_DIRS ${CPP_UTILITIES_INCLUDE_DIRS})
|
||||||
include(BasicConfig)
|
include(BasicConfig)
|
||||||
include(WindowsResources)
|
include(WindowsResources)
|
||||||
include(LibraryTarget)
|
include(LibraryTarget)
|
||||||
|
include(TestTarget)
|
||||||
include(ConfigHeader)
|
include(ConfigHeader)
|
||||||
|
|
||||||
# create install target for static libsyncthinginternal.a if we're also creating a static libsyncthing.a
|
# create install target for static libsyncthinginternal.a if we're also creating a static libsyncthing.a
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 8557b9ca2062f4c9de418c965cb8eaee56f706ea
|
Subproject commit c5832cc1a8997ca06bfcf8a72a3b056f2e6c823d
|
|
@ -2,21 +2,92 @@
|
||||||
|
|
||||||
#include "libsyncthinginternal.h"
|
#include "libsyncthinginternal.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
namespace LibSyncthing {
|
namespace LibSyncthing {
|
||||||
|
|
||||||
inline _GoString_ gostr(const std::string &str)
|
static LoggingCallback loggingCallback;
|
||||||
|
|
||||||
|
inline _GoString_ gostr(const string &str)
|
||||||
{
|
{
|
||||||
return _GoString_{ str.data(), static_cast<std::ptrdiff_t>(str.size()) };
|
return _GoString_{ str.data(), static_cast<ptrdiff_t>(str.size()) };
|
||||||
}
|
}
|
||||||
|
|
||||||
void runSyncthing(const RuntimeOptions &options)
|
inline string stdstr(char *str)
|
||||||
{
|
{
|
||||||
::runSyncthing(gostr(options.configDir), gostr(options.guiAddress), gostr(options.guiApiKey), gostr(options.logFile), options.verbose);
|
const string copy(str);
|
||||||
|
free(reinterpret_cast<void *>(str));
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
void generate(const std::string &generateDir)
|
inline string stdstr(_GoString_ gostr)
|
||||||
{
|
{
|
||||||
::generate(gostr(generateDir));
|
return string(gostr.p, static_cast<size_t>(gostr.n));
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleLoggingCallback(int logLevelInt, const char *msg, size_t msgSize)
|
||||||
|
{
|
||||||
|
// ignore callback when no callback registered
|
||||||
|
if (!loggingCallback) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore invalid/unknown log level
|
||||||
|
const auto logLevel = static_cast<LogLevel>(logLevelInt);
|
||||||
|
if (logLevel < lowestLogLevel || logLevel > highestLogLevel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loggingCallback(logLevel, msg, msgSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLoggingCallback(const LoggingCallback &callback)
|
||||||
|
{
|
||||||
|
loggingCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLoggingCallback(LoggingCallback &&callback)
|
||||||
|
{
|
||||||
|
loggingCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
long long runSyncthing(const RuntimeOptions &options)
|
||||||
|
{
|
||||||
|
::libst_loggingCallbackFunction = handleLoggingCallback;
|
||||||
|
return ::libst_runSyncthing(
|
||||||
|
gostr(options.configDir), gostr(options.guiAddress), gostr(options.guiApiKey), gostr(options.logFile), options.verbose);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopSyncthing()
|
||||||
|
{
|
||||||
|
::libst_stopSyncthing();
|
||||||
|
}
|
||||||
|
|
||||||
|
void restartSyncthing()
|
||||||
|
{
|
||||||
|
::libst_restartSyncthing();
|
||||||
|
}
|
||||||
|
|
||||||
|
void generateCertFiles(const std::string &generateDir)
|
||||||
|
{
|
||||||
|
::libst_generateCertFiles(gostr(generateDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
void openGUI()
|
||||||
|
{
|
||||||
|
::libst_openGUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
string syncthingVersion()
|
||||||
|
{
|
||||||
|
return stdstr(::libst_syncthingVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
string longSyncthingVersion()
|
||||||
|
{
|
||||||
|
return stdstr(::libst_longSyncthingVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace LibSyncthing
|
} // namespace LibSyncthing
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "./global.h"
|
#include "./global.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace LibSyncthing {
|
namespace LibSyncthing {
|
||||||
|
@ -15,8 +16,27 @@ struct RuntimeOptions {
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
void LIB_SYNCTHING_EXPORT runSyncthing(const RuntimeOptions &options);
|
enum class LogLevel : int {
|
||||||
void LIB_SYNCTHING_EXPORT generate(const std::string &generateDir);
|
Debug,
|
||||||
|
Verbose,
|
||||||
|
Info,
|
||||||
|
Warning,
|
||||||
|
Fatal,
|
||||||
|
};
|
||||||
|
constexpr auto lowestLogLevel = LogLevel::Debug;
|
||||||
|
constexpr auto highestLogLevel = LogLevel::Fatal;
|
||||||
|
|
||||||
|
using LoggingCallback = std::function<void(LogLevel, const char *message, std::size_t messageSize)>;
|
||||||
|
|
||||||
|
void LIB_SYNCTHING_EXPORT setLoggingCallback(const LoggingCallback &callback);
|
||||||
|
void LIB_SYNCTHING_EXPORT setLoggingCallback(LoggingCallback &&callback);
|
||||||
|
long long runSyncthing(const RuntimeOptions &options);
|
||||||
|
void LIB_SYNCTHING_EXPORT stopSyncthing();
|
||||||
|
void LIB_SYNCTHING_EXPORT restartSyncthing();
|
||||||
|
void LIB_SYNCTHING_EXPORT generateCertFiles(const std::string &generateDir);
|
||||||
|
void LIB_SYNCTHING_EXPORT openGUI();
|
||||||
|
std::string LIB_SYNCTHING_EXPORT syncthingVersion();
|
||||||
|
std::string LIB_SYNCTHING_EXPORT longSyncthingVersion();
|
||||||
|
|
||||||
} // namespace LibSyncthing
|
} // namespace LibSyncthing
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
../../connector/testfiles/testconfig
|
|
@ -0,0 +1 @@
|
||||||
|
#include <c++utilities/tests/cppunit.h>
|
|
@ -0,0 +1,138 @@
|
||||||
|
#include "../interface.h"
|
||||||
|
|
||||||
|
#include <c++utilities/chrono/datetime.h>
|
||||||
|
#include <c++utilities/chrono/timespan.h>
|
||||||
|
#include <c++utilities/conversion/stringconversion.h>
|
||||||
|
#include <c++utilities/io/path.h>
|
||||||
|
#include <c++utilities/tests/testutils.h>
|
||||||
|
|
||||||
|
#include <cppunit/TestFixture.h>
|
||||||
|
#include <cppunit/extensions/HelperMacros.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace ChronoUtilities;
|
||||||
|
using namespace ConversionUtilities;
|
||||||
|
using namespace IoUtilities;
|
||||||
|
using namespace TestUtilities;
|
||||||
|
using namespace LibSyncthing;
|
||||||
|
|
||||||
|
using namespace CPPUNIT_NS;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The InterfaceTests class tests the SyncthingConnector.
|
||||||
|
*/
|
||||||
|
class InterfaceTests : public TestFixture {
|
||||||
|
CPPUNIT_TEST_SUITE(InterfaceTests);
|
||||||
|
CPPUNIT_TEST(testRun);
|
||||||
|
CPPUNIT_TEST(testVersion);
|
||||||
|
CPPUNIT_TEST_SUITE_END();
|
||||||
|
|
||||||
|
public:
|
||||||
|
InterfaceTests();
|
||||||
|
|
||||||
|
void testRun();
|
||||||
|
void testVersion();
|
||||||
|
|
||||||
|
void setUp();
|
||||||
|
void tearDown();
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
CPPUNIT_TEST_SUITE_REGISTRATION(InterfaceTests);
|
||||||
|
|
||||||
|
InterfaceTests::InterfaceTests()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceTests::setUp()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceTests::tearDown()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Tests running Syncthing.
|
||||||
|
*/
|
||||||
|
void InterfaceTests::testRun()
|
||||||
|
{
|
||||||
|
const auto configFilePath(workingCopyPath("testconfig/config.xml"));
|
||||||
|
if (configFilePath.empty()) {
|
||||||
|
throw runtime_error("Unable to setup Syncthing config directory.");
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeOptions options;
|
||||||
|
options.configDir = directory(configFilePath);
|
||||||
|
|
||||||
|
const auto startTime(DateTime::gmtNow());
|
||||||
|
bool myIdAnnounced = false, performanceAnnounced = false;
|
||||||
|
bool testDir1Ready = false, testDir2Ready = false;
|
||||||
|
bool testDev1Ready = false, testDev2Ready = false;
|
||||||
|
bool shuttingDown = false, shutDownLogged = false;
|
||||||
|
|
||||||
|
setLoggingCallback([&](LogLevel logLevel, const char *message, std::size_t messageSize) {
|
||||||
|
// ignore debug/verbose messages
|
||||||
|
if (logLevel < LogLevel::Info) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether the usual log messages appear
|
||||||
|
const string msg(message, messageSize);
|
||||||
|
if (startsWith(msg, "My ID: ")) {
|
||||||
|
myIdAnnounced = true;
|
||||||
|
} else if (startsWith(msg, "Single thread SHA256 performance is")) {
|
||||||
|
performanceAnnounced = true;
|
||||||
|
} else if (msg == "Ready to synchronize test1 (readwrite)") {
|
||||||
|
testDir1Ready = true;
|
||||||
|
} else if (msg == "Ready to synchronize test2 (readwrite)") {
|
||||||
|
testDir2Ready = true;
|
||||||
|
} else if (msg == "Device 6EIS2PN-J2IHWGS-AXS3YUL-HC5FT3K-77ZXTLL-AKQLJ4C-7SWVPUS-AZW4RQ4 is \"Test dev 1\" at [dynamic]") {
|
||||||
|
testDev1Ready = true;
|
||||||
|
} else if (msg == "Device MMGUI6U-WUEZQCP-XZZ6VYB-LCT4TVC-ER2HAVX-QYT6X7D-S6ZSG2B-323KLQ7 is \"Test dev 2\" at [tcp://192.168.2.2:22000]") {
|
||||||
|
testDev2Ready = true;
|
||||||
|
} else if (msg == "Shutting down") {
|
||||||
|
shutDownLogged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// print the message on cout (which results in duplicated messages, but allows to check whether we've got everything)
|
||||||
|
cout << "logging callback (" << static_cast<std::underlying_type<LogLevel>::type>(logLevel) << ": ";
|
||||||
|
cout.write(message, static_cast<std::streamsize>(messageSize));
|
||||||
|
cout << endl;
|
||||||
|
|
||||||
|
// stop Syncthing again if the found the messages we've been looking for or we've timed out
|
||||||
|
const auto timeout((DateTime::gmtNow() - startTime) > TimeSpan::fromSeconds(30));
|
||||||
|
if (!timeout && (!myIdAnnounced || !performanceAnnounced || !testDir1Ready || !testDev1Ready || !testDev2Ready)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!shuttingDown) {
|
||||||
|
cerr << "stopping Syncthing again" << endl;
|
||||||
|
shuttingDown = true;
|
||||||
|
stopSyncthing();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT_EQUAL(0ll, runSyncthing(options));
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT(myIdAnnounced);
|
||||||
|
CPPUNIT_ASSERT(performanceAnnounced);
|
||||||
|
CPPUNIT_ASSERT(testDir1Ready);
|
||||||
|
CPPUNIT_ASSERT(!testDir2Ready);
|
||||||
|
CPPUNIT_ASSERT(testDev1Ready);
|
||||||
|
CPPUNIT_ASSERT(testDev2Ready);
|
||||||
|
CPPUNIT_ASSERT(shutDownLogged);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Tests whether the version() functions at least return something.
|
||||||
|
*/
|
||||||
|
void InterfaceTests::testVersion()
|
||||||
|
{
|
||||||
|
const auto version(syncthingVersion());
|
||||||
|
const auto longVersion(longSyncthingVersion());
|
||||||
|
cout << "\nversion: " << version;
|
||||||
|
cout << "\nlong version: " << longVersion << endl;
|
||||||
|
CPPUNIT_ASSERT(!version.empty());
|
||||||
|
CPPUNIT_ASSERT(!longVersion.empty());
|
||||||
|
}
|
Loading…
Reference in New Issue