Fix/improve CLI tests

* Adapt to interleaved output previously enabled in SyncthingTestInstance
* Make it work when verbose output for debugging is enabled
* Show which status line didn't match in case of an failure
* Disable flaky rescan tests in release mode
This commit is contained in:
Martchus 2018-10-20 23:48:24 +02:00
parent 1c6bd37b9d
commit 703225b410
6 changed files with 88 additions and 22 deletions

View File

@ -1,3 +1,12 @@
Overall statistics
Status idle
Global 0 file\(s\), 0 dir\(s\), 0 bytes
Local 0 file\(s\), 0 dir\(s\), 0 bytes
Incoming traffic 0 bytes
Outgoing traffic 0 bytes
Connected to no other devices
Version syncthing v.*
Directories
- test2
Label Test dir 2
@ -34,7 +43,7 @@ Devices
- Test dev 2
ID MMGUI6U-WUEZQCP-XZZ6VYB-LCT4TVC-ER2HAVX-QYT6X7D-S6ZSG2B-323KLQ7
Status paused
Addresses tcp://.*22000
Addresses tcp://.*22001
Compression metadata
- .*

View File

@ -76,6 +76,7 @@ void ApplicationTests::tearDown()
#ifdef PLATFORM_UNIX
/*!
* \brief Tests the overall CLI application.
* \remarks Tests for rescan are currently disabled for release mode because they sometimes fail.
*/
void ApplicationTests::test()
{
@ -88,8 +89,14 @@ void ApplicationTests::test()
setenv("ENABLE_ESCAPE_CODES", "0", true);
// load expected status
const auto expectedStatusData(readFile(testFilePath("expected-status.txt"), 2048));
const regex expectedStatus(expectedStatusData);
const auto expectedStatusData(readFile(testFilePath("expected-status.txt"), 4000));
const auto expectedStatusLines(splitString<vector<string>>(expectedStatusData, "\n"));
vector<regex> expectedStatusPatterns;
expectedStatusPatterns.reserve(expectedStatusLines.size());
for (const auto &line : expectedStatusLines) {
expectedStatusPatterns.emplace_back(line);
}
CPPUNIT_ASSERT(!expectedStatusPatterns.empty());
// save cwd (to restore later)
char cwd[1024];
@ -112,13 +119,28 @@ void ApplicationTests::test()
syncthingOutput.append(syncthingProcess().readAll());
} while (!syncthingOutput.contains("Access the GUI via the following URL"));
setInterleavedOutputEnabledFromEnv();
// test status for all dirs and devs
const char *const statusArgs[] = { "syncthingctl", "status", "--api-key", apiKey.data(), "--url", url.data(), nullptr };
const char *const statusArgs[] = { "syncthingctl", "status", "--api-key", apiKey.data(), "--url", url.data(), "--no-color", nullptr };
TESTUTILS_ASSERT_EXEC(statusArgs);
cout << stdout;
if (!regex_search(stdout, expectedStatus)) {
cout << "expected status: " << expectedStatusData << '\n';
CPPUNIT_FAIL("Actual status does not match expected status.");
const auto statusLines(splitString(stdout, "\n"));
auto currentStatusLine = statusLines.cbegin();
auto currentPattern = 0_st;
for (const auto &expectedPattern : expectedStatusPatterns) {
bool patternFound = false;
while (currentStatusLine != statusLines.cend()) {
patternFound = regex_search(*currentStatusLine, expectedPattern);
++currentStatusLine;
if (patternFound) {
break;
}
}
if (!patternFound) {
CPPUNIT_FAIL(argsToString("Line ", expectedStatusLines[currentPattern], " could not be found in output."));
}
++currentPattern;
}
// test log
@ -139,16 +161,18 @@ void ApplicationTests::test()
const char *const statusDirsOnlyArgs[] = { "syncthingctl", "status", "--all-dirs", nullptr };
TESTUTILS_ASSERT_EXEC(resumeArgs);
TESTUTILS_ASSERT_EXEC(statusDirsOnlyArgs);
CPPUNIT_ASSERT(stdout.find("test2") != string::npos);
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", nullptr };
const char *const statusTest2Args[] = { "syncthingctl", "status", "--dir", "test2", nullptr };
TESTUTILS_ASSERT_EXEC(pauseArgs);
cout << stdout;
TESTUTILS_ASSERT_EXEC(statusTest2Args);
CPPUNIT_ASSERT(stdout.find("test1") == string::npos);
CPPUNIT_ASSERT(stdout.find("test2") != string::npos);
cout << stdout;
CPPUNIT_ASSERT(stdout.find(" - test1") == string::npos);
CPPUNIT_ASSERT(stdout.find(" - test2") != string::npos);
CPPUNIT_ASSERT(stdout.find("paused") != string::npos);
// test cat
@ -171,19 +195,20 @@ void ApplicationTests::test()
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(" - test1") != string::npos);
CPPUNIT_ASSERT(stdout.find(" - test2") == string::npos);
CPPUNIT_ASSERT(stdout.find("Rescan interval file system watcher and periodic rescan disabled") != string::npos);
#endif
// test rescan: create new file, trigger rescan, check status
#ifdef DEBUG_BUILD
CPPUNIT_ASSERT(ofstream("/tmp/some/path/1/new-file.txt") << "foo");
const char *const rescanArgs[] = { "syncthingctl", "rescan", "test1", nullptr };
TESTUTILS_ASSERT_EXEC(rescanArgs);
cout << stdout;
TESTUTILS_ASSERT_EXEC(statusTest1Args);
cout << stdout;
CPPUNIT_ASSERT(stdout.find("test1") != string::npos);
CPPUNIT_ASSERT(stdout.find(" - test1") != string::npos);
CPPUNIT_ASSERT(stdout.find("Local 1 file(s), 0 dir(s), 3 bytes") != string::npos);
// test pwd
@ -209,12 +234,13 @@ void ApplicationTests::test()
const char *const pwdStatusArgs[] = { "syncthingctl", "pwd", "status", nullptr };
TESTUTILS_ASSERT_EXEC(pwdStatusArgs);
cout << stdout;
CPPUNIT_ASSERT(stdout.find("test1") != string::npos);
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
}
#endif

View File

@ -458,7 +458,7 @@ QNetworkReply *SyncthingConnection::requestData(const QString &path, const QUrlQ
#ifndef LIB_SYNCTHING_CONNECTOR_CONNECTION_MOCKED
auto *const reply = networkAccessManager().get(prepareRequest(path, query, rest));
#ifdef LIB_SYNCTHING_CONNECTOR_LOG_API_CALLS
cout << Phrases::Info << "GETing: " << reply->url().toString().toStdString() << Phrases::EndFlush;
cerr << Phrases::Info << "GETing: " << reply->url().toString().toStdString() << Phrases::EndFlush;
#endif
reply->ignoreSslErrors(m_expectedSslErrors);
return reply;
@ -474,7 +474,7 @@ QNetworkReply *SyncthingConnection::postData(const QString &path, const QUrlQuer
{
auto *const reply = networkAccessManager().post(prepareRequest(path, query), data);
#ifdef LIB_SYNCTHING_CONNECTOR_LOG_API_CALLS
cout << Phrases::Info << "POSTing: " << reply->url().toString().toStdString() << Phrases::End << data.data() << endl;
cerr << Phrases::Info << "POSTing: " << reply->url().toString().toStdString() << Phrases::End << data.data() << endl;
#endif
reply->ignoreSslErrors(m_expectedSslErrors);
return reply;
@ -1550,7 +1550,7 @@ void SyncthingConnection::readEvents()
#ifdef LIB_SYNCTHING_CONNECTOR_LOG_SYNCTHING_EVENTS
if (!replyArray.isEmpty()) {
cout << Phrases::Info << "Received " << replyArray.size() << " Syncthing events:" << Phrases::End
cerr << Phrases::Info << "Received " << replyArray.size() << " Syncthing events:" << Phrases::End
<< replyDoc.toJson(QJsonDocument::Indented).data() << endl;
}
#endif

View File

@ -115,6 +115,7 @@ ConnectionTests::ConnectionTests()
*/
void ConnectionTests::setUp()
{
setInterleavedOutputEnabledFromEnv();
SyncthingTestInstance::start();
cerr << "\n - Preparing connection ..." << endl;

View File

@ -19,6 +19,7 @@ static char *dummy2;
SyncthingTestInstance::SyncthingTestInstance()
: m_apiKey(QStringLiteral("syncthingtestinstance"))
, m_app(dummy1, &dummy2)
, m_interleavedOutput(false)
{
}
@ -73,11 +74,6 @@ void SyncthingTestInstance::start()
const int syncthingPortFromEnv(qEnvironmentVariableIntValue("SYNCTHING_PORT"));
m_syncthingPort = !syncthingPortFromEnv ? QStringLiteral("4001") : QString::number(syncthingPortFromEnv);
// forward Syncthing's output to see what Syncthing is doing and what the test is doing at the same time
if (qEnvironmentVariableIsEmpty("SYNCTHING_TEST_NO_INTERLEAVED_OUTPUT")) {
m_syncthingProcess.setReadChannelMode(QProcess::ForwardedChannels);
}
// start st
// clang-format off
const QStringList args{
@ -121,4 +117,26 @@ void SyncthingTestInstance::stop()
}
}
}
/*!
* \brief Sets whether Syncthing's output should be forwarded to see what Syncthing and the test is doing at the same time.
*/
void SyncthingTestInstance::setInterleavedOutputEnabled(bool interleavedOutputEnabled)
{
if (interleavedOutputEnabled == m_interleavedOutput) {
return;
}
m_interleavedOutput = interleavedOutputEnabled;
m_syncthingProcess.setReadChannelMode(interleavedOutputEnabled ? QProcess::ForwardedChannels : QProcess::SeparateChannels);
}
/*!
* \brief Applies the default for isInterleavedOutputEnabled() considering environment variable SYNCTHING_TEST_NO_INTERLEAVED_OUTPUT.
*/
void SyncthingTestInstance::setInterleavedOutputEnabledFromEnv()
{
if (qEnvironmentVariableIsEmpty("SYNCTHING_TEST_NO_INTERLEAVED_OUTPUT")) {
setInterleavedOutputEnabled(true);
}
}
} // namespace TestUtilities

View File

@ -30,12 +30,16 @@ public:
public Q_SLOTS:
void start();
void stop();
bool isInterleavedOutputEnabled() const;
void setInterleavedOutputEnabled(bool interleavedOutputEnabled);
void setInterleavedOutputEnabledFromEnv();
private:
QString m_apiKey;
QString m_syncthingPort;
QCoreApplication m_app;
Data::SyncthingProcess m_syncthingProcess;
bool m_interleavedOutput;
};
inline const QString &SyncthingTestInstance::apiKey() const
@ -57,6 +61,14 @@ inline QProcess &SyncthingTestInstance::syncthingProcess()
{
return m_syncthingProcess;
}
/*!
* \brief Whether Syncthing's output should be forwarded to see what Syncthing and the test is doing at the same time.
*/
inline bool SyncthingTestInstance::isInterleavedOutputEnabled() const
{
return m_interleavedOutput;
}
} // namespace TestUtilities
#endif // SYNCTHINGTESTHELPER_SYNCTHINGTESTINSTANCE_H