Add tests for syncthingctl
This commit is contained in:
parent
f6428e4ff7
commit
72e1377c43
|
@ -18,6 +18,12 @@ set(SRC_FILES
|
|||
args.cpp
|
||||
application.cpp
|
||||
)
|
||||
set(TEST_HEADER_FILES
|
||||
)
|
||||
set(TEST_SRC_FILES
|
||||
tests/cppunit.cpp
|
||||
tests/application.cpp
|
||||
)
|
||||
|
||||
# find c++utilities
|
||||
find_package(c++utilities 4.14.0 REQUIRED)
|
||||
|
@ -31,6 +37,15 @@ list(APPEND CMAKE_MODULE_PATH ${QT_UTILITIES_MODULE_DIRS})
|
|||
find_package(syncthingconnector ${META_APP_VERSION} REQUIRED)
|
||||
use_syncthingconnector()
|
||||
|
||||
find_package(syncthingtesthelper ${META_APP_VERSION} REQUIRED)
|
||||
if(SYNCTHINGTESTHELPER_HAS_SHARED_LIB)
|
||||
list(APPEND TEST_LIBRARIES syncthingtesthelper)
|
||||
elseif(SYNCTHINGTESTHELPER_HAS_STATIC_LIB)
|
||||
list(APPEND TEST_LIBRARIES syncthingtesthelper_static)
|
||||
else()
|
||||
message(WARNING "Unable to build tests. Testhelper not found.")
|
||||
endif()
|
||||
|
||||
# include modules to apply configuration
|
||||
include(BasicConfig)
|
||||
include(JsProviderConfig)
|
||||
|
@ -42,6 +57,9 @@ endif()
|
|||
include(QtConfig)
|
||||
include(WindowsResources)
|
||||
include(AppTarget)
|
||||
if(SYNCTHINGTESTHELPER_HAS_SHARED_LIB OR SYNCTHINGTESTHELPER_HAS_STATIC_LIB)
|
||||
include(TestTarget)
|
||||
endif()
|
||||
include(ShellCompletion)
|
||||
include(Doxygen)
|
||||
include(ConfigHeader)
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
Directories
|
||||
- test2
|
||||
Label Test dir 2
|
||||
Path /tmp/some/path/2/
|
||||
Status paused
|
||||
Global 0 file\(s\), 0 dir\(s\), 0 bytes
|
||||
Local 0 file\(s\), 0 dir\(s\), 0 bytes
|
||||
Shared with Test dev 2
|
||||
Read-only no
|
||||
Ignore permissions no
|
||||
Auto-normalize yes
|
||||
Rescan interval 365 d
|
||||
|
||||
- test1
|
||||
Path /tmp/some/path/1/
|
||||
Status (idle|scanning)
|
||||
Global 0 file\(s\), 0 dir\(s\), 0 bytes
|
||||
Local 0 file\(s\), 0 dir\(s\), 0 bytes
|
||||
Last scan time \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d.*
|
||||
Shared with Test dev 2
|
||||
Test dev 1
|
||||
Read-only no
|
||||
Ignore permissions no
|
||||
Auto-normalize yes
|
||||
Rescan interval 2 h
|
||||
|
||||
Devices
|
||||
- Test dev 1
|
||||
ID 6EIS2PN-J2IHWGS-AXS3YUL-HC5FT3K-77ZXTLL-AKQLJ4C-7SWVPUS-AZW4RQ4
|
||||
Status disconnected
|
||||
Addresses dynamic
|
||||
Compression metadata
|
||||
|
||||
- Test dev 2
|
||||
ID MMGUI6U-WUEZQCP-XZZ6VYB-LCT4TVC-ER2HAVX-QYT6X7D-S6ZSG2B-323KLQ7
|
||||
Status paused
|
||||
Addresses tcp://.*22000
|
||||
Compression metadata
|
||||
|
||||
- martchus-arch
|
||||
ID \w\w\w\w\w\w\w-\w\w\w\w\w\w\w-\w\w\w\w\w\w\w-\w\w\w\w\w\w\w-\w\w\w\w\w\w\w-\w\w\w\w\w\w\w-\w\w\w\w\w\w\w-\w\w\w\w\w\w\w
|
||||
Status own device
|
||||
Addresses dynamic
|
||||
Compression metadata
|
|
@ -0,0 +1 @@
|
|||
../../connector/testfiles/testconfig
|
|
@ -0,0 +1,212 @@
|
|||
#include "../../testhelper/syncthingtestinstance.h"
|
||||
|
||||
#include <c++utilities/io/misc.h>
|
||||
#include <c++utilities/tests/testutils.h>
|
||||
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
|
||||
#include <regex>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace Data;
|
||||
using namespace ChronoUtilities;
|
||||
using namespace IoUtilities;
|
||||
using namespace TestUtilities;
|
||||
using namespace TestUtilities::Literals;
|
||||
|
||||
using namespace CPPUNIT_NS;
|
||||
|
||||
/*!
|
||||
* \brief The ApplicationTests class tests the overall CLI application.
|
||||
*/
|
||||
class ApplicationTests : public TestFixture, private SyncthingTestInstance {
|
||||
CPPUNIT_TEST_SUITE(ApplicationTests);
|
||||
#ifdef PLATFORM_UNIX
|
||||
CPPUNIT_TEST(test);
|
||||
#endif
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
ApplicationTests();
|
||||
|
||||
void test();
|
||||
|
||||
void setUp();
|
||||
void tearDown();
|
||||
|
||||
private:
|
||||
DateTime m_startTime;
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(ApplicationTests);
|
||||
|
||||
ApplicationTests::ApplicationTests()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Ensures Syncthing dirs are empty and starts Syncthing.
|
||||
*/
|
||||
void ApplicationTests::setUp()
|
||||
{
|
||||
remove("/tmp/some/path/1/new-file.txt");
|
||||
remove("/tmp/some/path/1/newdir/yet-another-file.txt");
|
||||
remove("/tmp/some/path/1/newdir/default.profraw");
|
||||
rmdir("/tmp/some/path/1/newdir");
|
||||
|
||||
SyncthingTestInstance::start();
|
||||
m_startTime = DateTime::gmtNow();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Terminates Syncthing and prints stdout/stderr from Syncthing.
|
||||
*/
|
||||
void ApplicationTests::tearDown()
|
||||
{
|
||||
SyncthingTestInstance::stop();
|
||||
}
|
||||
|
||||
#ifdef PLATFORM_UNIX
|
||||
/*!
|
||||
* \brief Tests the overall CLI application.
|
||||
*/
|
||||
void ApplicationTests::test()
|
||||
{
|
||||
// prepare executing syncthingctl
|
||||
string stdout, stderr;
|
||||
const auto apiKey(this->apiKey().toLocal8Bit());
|
||||
const auto url(argsToString("http://localhost:", syncthingPort().toInt()));
|
||||
|
||||
// disable colorful output
|
||||
setenv("ENABLE_ESCAPE_CODES", "0", true);
|
||||
|
||||
// load expected status
|
||||
const regex expectedStatus(readFile(testFilePath("expected-status.txt"), 2048));
|
||||
|
||||
// save cwd (to restore later)
|
||||
char cwd[1024];
|
||||
const bool hasCwd(getcwd(cwd, sizeof(cwd)));
|
||||
|
||||
// wait till Syncthing GUI becomes available
|
||||
cerr << "\nWaiting till Syncthing GUI becomes available ...";
|
||||
QByteArray syncthingOutput;
|
||||
do {
|
||||
// wait for output
|
||||
if (!syncthingProcess().bytesAvailable()) {
|
||||
// fail when already waiting for over 15 seconds
|
||||
const auto waitingTime(DateTime::gmtNow() - m_startTime);
|
||||
if (waitingTime.seconds() > 15) {
|
||||
CPPUNIT_FAIL("Syncthing needs longer than 15 seconds to become available.");
|
||||
}
|
||||
syncthingProcess().waitForReadyRead(15000 - waitingTime.milliseconds());
|
||||
}
|
||||
syncthingOutput.append(syncthingProcess().readAll());
|
||||
} while (!syncthingOutput.contains("Access the GUI via the following URL"));
|
||||
|
||||
// test status for all dirs and devs
|
||||
const char *const statusArgs[] = { "syncthingctl", "status", "--api-key", apiKey.data(), "--url", url.data(), nullptr };
|
||||
TESTUTILS_ASSERT_EXEC(statusArgs);
|
||||
cout << stdout;
|
||||
CPPUNIT_ASSERT(regex_search(stdout, expectedStatus));
|
||||
|
||||
// test log
|
||||
const char *const logArgs[] = { "syncthingctl", "log", "--api-key", apiKey.data(), "--url", url.data(), nullptr };
|
||||
TESTUTILS_ASSERT_EXEC(logArgs);
|
||||
cout << stdout;
|
||||
CPPUNIT_ASSERT(stdout.find("syncthing v") != string::npos);
|
||||
CPPUNIT_ASSERT(stdout.find("My ID") != string::npos);
|
||||
CPPUNIT_ASSERT(stdout.find("Startup complete") != string::npos);
|
||||
CPPUNIT_ASSERT(stdout.find("Access the GUI via the following URL") != string::npos);
|
||||
|
||||
// test resume, verify via status for dirs only
|
||||
const char *const resumeArgs[] = { "syncthingctl", "resume", "--dir", "test2", "--api-key", apiKey.data(), "--url", url.data(), nullptr };
|
||||
const char *const statusDirsOnlyArgs[] = { "syncthingctl", "status", "--all-dirs", "--api-key", apiKey.data(), "--url", url.data(), nullptr };
|
||||
TESTUTILS_ASSERT_EXEC(resumeArgs);
|
||||
TESTUTILS_ASSERT_EXEC(statusDirsOnlyArgs);
|
||||
CPPUNIT_ASSERT(stdout.find("test2") != string::npos);
|
||||
CPPUNIT_ASSERT(stdout.find("paused") == string::npos);
|
||||
|
||||
// test pause, verify via status on specific dir
|
||||
const char *const pauseArgs[] = { "syncthingctl", "pause", "--dir", "test2", "--api-key", apiKey.data(), "--url", url.data(), nullptr };
|
||||
const char *const statusTest2Args[] = { "syncthingctl", "status", "--dir", "test2", "--api-key", apiKey.data(), "--url", url.data(), nullptr };
|
||||
TESTUTILS_ASSERT_EXEC(pauseArgs);
|
||||
TESTUTILS_ASSERT_EXEC(statusTest2Args);
|
||||
CPPUNIT_ASSERT(stdout.find("test1") == string::npos);
|
||||
CPPUNIT_ASSERT(stdout.find("test2") != string::npos);
|
||||
CPPUNIT_ASSERT(stdout.find("paused") != string::npos);
|
||||
|
||||
// test cat
|
||||
const char *const catArgs[] = { "syncthingctl", "cat", nullptr };
|
||||
TESTUTILS_ASSERT_EXEC(catArgs);
|
||||
cout << stdout;
|
||||
QJsonParseError error;
|
||||
const auto doc(QJsonDocument::fromJson(QByteArray(stdout.data(), stdout.size()), &error));
|
||||
CPPUNIT_ASSERT_EQUAL(QJsonParseError::NoError, error.error);
|
||||
const auto object(doc.object());
|
||||
CPPUNIT_ASSERT(object.value(QLatin1String("options")).isObject());
|
||||
CPPUNIT_ASSERT(object.value(QLatin1String("devices")).isArray());
|
||||
CPPUNIT_ASSERT(object.value(QLatin1String("folders")).isArray());
|
||||
|
||||
// test edit
|
||||
#if defined(SYNCTHINGCTL_USE_JSENGINE) || defined(SYNCTHINGCTL_USE_SCRIPT)
|
||||
const char *const editArgs[] = { "syncthingctl", "edit", "--js-lines", "assignIfPresent(findFolder('test1'), 'rescanIntervalS', 0);", "--api-key",
|
||||
apiKey.data(), "--url", url.data(), nullptr };
|
||||
const char *const statusTest1Args[] = { "syncthingctl", "status", "--dir", "test1", "--api-key", apiKey.data(), "--url", url.data(), nullptr };
|
||||
TESTUTILS_ASSERT_EXEC(editArgs);
|
||||
cout << stdout;
|
||||
TESTUTILS_ASSERT_EXEC(statusTest1Args);
|
||||
cout << stdout;
|
||||
CPPUNIT_ASSERT(stdout.find("test1") != string::npos);
|
||||
CPPUNIT_ASSERT(stdout.find("test2") == string::npos);
|
||||
CPPUNIT_ASSERT(stdout.find("Rescan interval") == string::npos);
|
||||
#endif
|
||||
|
||||
// test rescan: create new file, trigger rescan, check status
|
||||
CPPUNIT_ASSERT(ofstream("/tmp/some/path/1/new-file.txt") << "foo");
|
||||
const char *const rescanArgs[] = { "syncthingctl", "rescan", "test1", "--api-key", apiKey.data(), "--url", url.data(), nullptr };
|
||||
TESTUTILS_ASSERT_EXEC(rescanArgs);
|
||||
cout << stdout;
|
||||
TESTUTILS_ASSERT_EXEC(statusTest1Args);
|
||||
cout << stdout;
|
||||
CPPUNIT_ASSERT(stdout.find("test1") != string::npos);
|
||||
CPPUNIT_ASSERT(stdout.find("Local 1 file(s), 0 dir(s), 3 bytes") != string::npos);
|
||||
|
||||
// test pwd
|
||||
// -> create and enter new dir, also create a 2nd file in it
|
||||
chdir("/tmp/some/path/1");
|
||||
mkdir("newdir", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
chdir("newdir");
|
||||
CPPUNIT_ASSERT(ofstream("yet-another-file.txt") << "bar");
|
||||
// -> change LLVM_PROFILE_FILE to prevent default.profraw file being created in the new directory
|
||||
const char *const llvmProfileFile(getenv("LLVM_PROFILE_FILE"));
|
||||
setenv("LLVM_PROFILE_FILE", "/tmp/syncthingctl-%p.profraw", true);
|
||||
// -> do actual test
|
||||
const char *const pwdRescanArgs[] = { "syncthingctl", "pwd", "rescan", "--api-key", apiKey.data(), "--url", url.data(), nullptr };
|
||||
TESTUTILS_ASSERT_EXEC(pwdRescanArgs);
|
||||
cout << stdout;
|
||||
// -> restore LLVM_PROFILE_FILE
|
||||
if (llvmProfileFile) {
|
||||
setenv("LLVM_PROFILE_FILE", llvmProfileFile, true);
|
||||
} else {
|
||||
unsetenv("LLVM_PROFILE_FILE");
|
||||
}
|
||||
// -> verify result
|
||||
const char *const pwdStatusArgs[] = { "syncthingctl", "pwd", "status", "--api-key", apiKey.data(), "--url", url.data(), nullptr };
|
||||
TESTUTILS_ASSERT_EXEC(pwdStatusArgs);
|
||||
cout << stdout;
|
||||
CPPUNIT_ASSERT(stdout.find("test1") != string::npos);
|
||||
CPPUNIT_ASSERT(stdout.find("Local 2 file(s), 1 dir(s)") != string::npos);
|
||||
|
||||
// switch back to initial working dir
|
||||
if (hasCwd) {
|
||||
chdir(cwd);
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1 @@
|
|||
#include <c++utilities/tests/cppunit.h>
|
|
@ -1,8 +1,8 @@
|
|||
#include "../syncthingconnection.h"
|
||||
#include "../syncthingconnectionsettings.h"
|
||||
|
||||
#include "../testhelper/helper.h"
|
||||
#include "../testhelper/syncthingtestinstance.h"
|
||||
#include "../../testhelper/helper.h"
|
||||
#include "../../testhelper/syncthingtestinstance.h"
|
||||
|
||||
#include <c++utilities/tests/testutils.h>
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include <c++utilities/chrono/timespan.h>
|
||||
#include <c++utilities/tests/testutils.h>
|
||||
|
||||
#include "../testhelper/helper.h"
|
||||
#include "../../testhelper/helper.h"
|
||||
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
|
|
|
@ -71,14 +71,16 @@ void SyncthingTestInstance::start()
|
|||
|
||||
// start st
|
||||
cerr << "\n - Launching Syncthing ..." << endl;
|
||||
QStringList args;
|
||||
args.reserve(2);
|
||||
args << QStringLiteral("-gui-address=http://localhost:") + m_syncthingPort;
|
||||
args << QStringLiteral("-gui-apikey=") + m_apiKey;
|
||||
args << QStringLiteral("-home=") + configFile.absolutePath();
|
||||
args << QStringLiteral("-no-browser");
|
||||
args << QStringLiteral("-verbose");
|
||||
// clang-format off
|
||||
const QStringList args{
|
||||
QStringLiteral("-gui-address=http://localhost:") + m_syncthingPort,
|
||||
QStringLiteral("-gui-apikey=") + m_apiKey,
|
||||
QStringLiteral("-home=") + configFile.absolutePath(),
|
||||
QStringLiteral("-no-browser"),
|
||||
QStringLiteral("-verbose"),
|
||||
};
|
||||
m_syncthingProcess.start(syncthingPath, args);
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
Loading…
Reference in New Issue