Test SyncthingConnection class

This commit is contained in:
Martchus 2016-09-21 21:05:31 +02:00
parent 536f140fcb
commit a3887749a5
8 changed files with 424 additions and 39 deletions

View File

@ -30,6 +30,13 @@ set(SRC_FILES
utils.cpp
)
set(TEST_HEADER_FILES
)
set(TEST_SRC_FILES
tests/cppunit.cpp
tests/connectiontests.cpp
)
set(TS_FILES
translations/${META_PROJECT_NAME}_de_DE.ts
translations/${META_PROJECT_NAME}_en_US.ts
@ -74,5 +81,6 @@ include(BasicConfig)
include(QtConfig)
include(WindowsResources)
include(LibraryTarget)
include(TestTarget)
include(Doxygen)
include(ConfigHeader)

View File

@ -985,7 +985,9 @@ void SyncthingConnection::readStatus()
}
// other values are currently not interesting
m_hasStatus = true;
continueConnecting();
if(!isConnected()) {
continueConnecting();
}
} else {
emit error(tr("Unable to parse Syncthing status: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError);
}
@ -1671,8 +1673,9 @@ void SyncthingConnection::readShutdown()
/*!
* \brief Sets the connection status. Ensures statusChanged() is emitted.
* \param status Specifies the status; should be either SyncthingStatus::Disconnected or SyncthingStatus::Default. There is no use
* in specifying other values such as SyncthingStatus::Synchronizing as these are determined automatically within the method.
* \param status Specifies the status; should be either SyncthingStatus::Disconnected, SyncthingStatus::Reconnecting, or
* SyncthingStatus::Idle. There is no use in specifying other values such as SyncthingStatus::Synchronizing as
* these are determined automatically within the method.
*/
void SyncthingConnection::setStatus(SyncthingStatus status)
{

View File

@ -0,0 +1,91 @@
<configuration version="18">
<folder id="test1" label="" path="/tmp/some/path/1" type="readwrite" rescanIntervalS="7200" ignorePerms="false" autoNormalize="true">
<device id="6EIS2PN-J2IHWGS-AXS3YUL-HC5FT3K-77ZXTLL-AKQLJ4C-7SWVPUS-AZW4RQ4" introducedBy=""></device>
<device id="MMGUI6U-WUEZQCP-XZZ6VYB-LCT4TVC-ER2HAVX-QYT6X7D-S6ZSG2B-323KLQ7" introducedBy=""></device>
<minDiskFreePct>1</minDiskFreePct>
<versioning></versioning>
<copiers>1</copiers>
<pullers>16</pullers>
<hashers>0</hashers>
<order>random</order>
<ignoreDelete>false</ignoreDelete>
<scanProgressIntervalS>0</scanProgressIntervalS>
<pullerSleepS>0</pullerSleepS>
<pullerPauseS>0</pullerPauseS>
<maxConflicts>-1</maxConflicts>
<disableSparseFiles>false</disableSparseFiles>
<disableTempIndexes>false</disableTempIndexes>
<fsync>true</fsync>
<paused>false</paused>
<weakHashThresholdPct>25</weakHashThresholdPct>
</folder>
<folder id="test2" label="Test dir 2" path="/tmp/some/path/2" type="readwrite" rescanIntervalS="31536000" ignorePerms="false" autoNormalize="true">
<device id="MMGUI6U-WUEZQCP-XZZ6VYB-LCT4TVC-ER2HAVX-QYT6X7D-S6ZSG2B-323KLQ7" introducedBy=""></device>
<minDiskFreePct>1</minDiskFreePct>
<versioning></versioning>
<copiers>1</copiers>
<pullers>16</pullers>
<hashers>0</hashers>
<order>random</order>
<ignoreDelete>false</ignoreDelete>
<scanProgressIntervalS>0</scanProgressIntervalS>
<pullerSleepS>0</pullerSleepS>
<pullerPauseS>0</pullerPauseS>
<maxConflicts>-1</maxConflicts>
<disableSparseFiles>false</disableSparseFiles>
<disableTempIndexes>false</disableTempIndexes>
<fsync>true</fsync>
<paused>true</paused>
<weakHashThresholdPct>25</weakHashThresholdPct>
</folder>
<device id="6EIS2PN-J2IHWGS-AXS3YUL-HC5FT3K-77ZXTLL-AKQLJ4C-7SWVPUS-AZW4RQ4" name="Test dev 1" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>dynamic</address>
<paused>false</paused>
</device>
<device id="MMGUI6U-WUEZQCP-XZZ6VYB-LCT4TVC-ER2HAVX-QYT6X7D-S6ZSG2B-323KLQ7" name="Test dev 2" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>tcp://192.168.2.2:22000</address>
<paused>true</paused>
</device>
<gui enabled="true" tls="false" debugging="false">
<address>127.0.0.1:4001</address>
<apikey>syncthingconnectortest</apikey>
<theme>default</theme>
</gui>
<options>
<listenAddress>default</listenAddress>
<globalAnnounceServer>default</globalAnnounceServer>
<globalAnnounceEnabled>true</globalAnnounceEnabled>
<localAnnounceEnabled>true</localAnnounceEnabled>
<localAnnouncePort>21027</localAnnouncePort>
<localAnnounceMCAddr>[ff12::8384]:21027</localAnnounceMCAddr>
<maxSendKbps>0</maxSendKbps>
<maxRecvKbps>0</maxRecvKbps>
<reconnectionIntervalS>60</reconnectionIntervalS>
<relaysEnabled>true</relaysEnabled>
<relayReconnectIntervalM>10</relayReconnectIntervalM>
<startBrowser>true</startBrowser>
<natEnabled>true</natEnabled>
<natLeaseMinutes>0</natLeaseMinutes>
<natRenewalMinutes>30</natRenewalMinutes>
<natTimeoutSeconds>10</natTimeoutSeconds>
<urAccepted>2</urAccepted>
<urUniqueID>fhjO-ICd</urUniqueID>
<urURL>https://data.syncthing.net/newdata</urURL>
<urPostInsecurely>false</urPostInsecurely>
<urInitialDelayS>1800</urInitialDelayS>
<restartOnWakeup>true</restartOnWakeup>
<autoUpgradeIntervalH>12</autoUpgradeIntervalH>
<upgradeToPreReleases>true</upgradeToPreReleases>
<keepTemporariesH>24</keepTemporariesH>
<cacheIgnoredFiles>false</cacheIgnoredFiles>
<progressUpdateIntervalS>5</progressUpdateIntervalS>
<symlinksEnabled>true</symlinksEnabled>
<limitBandwidthInLan>false</limitBandwidthInLan>
<minHomeDiskFreePct>1</minHomeDiskFreePct>
<releasesURL>https://upgrades.syncthing.net/meta.json</releasesURL>
<overwriteRemoteDeviceNamesOnConnect>false</overwriteRemoteDeviceNamesOnConnect>
<tempIndexMinBlocks>10</tempIndexMinBlocks>
<trafficClass>0</trafficClass>
<weakHashSelectionMethod>auto</weakHashSelectionMethod>
</options>
</configuration>

View File

@ -0,0 +1,282 @@
#include "./syncthingconnection.h"
#include <c++utilities/tests/testutils.h>
#include <c++utilities/conversion/stringbuilder.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestFixture.h>
#include <QCoreApplication>
#include <QProcess>
#include <QFileInfo>
#include <QDir>
#include <QMetaMethod>
using namespace std;
using namespace Data;
using namespace TestUtilities;
using namespace ConversionUtilities;
using namespace CPPUNIT_NS;
/*!
* \brief The ConnectionTests class tests the SyncthingConnector.
*/
class ConnectionTests : public TestFixture
{
CPPUNIT_TEST_SUITE(ConnectionTests);
CPPUNIT_TEST(testConnection);
CPPUNIT_TEST_SUITE_END();
public:
ConnectionTests();
void testConnection();
void setUp();
void tearDown();
private:
template<typename Signal, typename Action, typename Handler = function<void(void)> >
void waitForConnection(Signal signal, Action action, int timeout = 2500, Handler handler = nullptr);
template<typename Signal, typename Handler = function<void(void)> >
void waitForConnection(Signal signal, int timeout = 2500, Handler handler = nullptr);
QString m_apiKey;
QCoreApplication m_app;
QProcess m_syncthingProcess;
SyncthingConnection m_connection;
};
CPPUNIT_TEST_SUITE_REGISTRATION(ConnectionTests);
int dummy1 = 0;
char *dummy2;
ConnectionTests::ConnectionTests() :
m_apiKey(QStringLiteral("syncthingconnectortest")),
m_app(dummy1, &dummy2)
{}
//
// test setup
//
/*!
* \brief Starts Syncthing and prepares connecting.
*/
void ConnectionTests::setUp()
{
cerr << "\n - Launching Syncthing and setup connection ..." << endl;
// setup st config
const string configFilePath = workingCopyPath("testconfig/config.xml");
if(configFilePath.empty()) {
throw runtime_error("Unable to setup Syncthing config directory.");
}
const QFileInfo configFile(QString::fromLocal8Bit(configFilePath.data()));
// clean config dir
const QDir configDir(configFile.dir());
for(QFileInfo &configEntry : configDir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot)) {
if(configEntry.isDir()) {
QDir(configEntry.absoluteFilePath()).removeRecursively();
} else if(configEntry.fileName() != QStringLiteral("config.xml")) {
QFile::remove(configEntry.absoluteFilePath());
}
}
// determine st path
const QByteArray syncthingPathFromEnv(qgetenv("SYNCTHING_PATH"));
const QString syncthingPath(syncthingPathFromEnv.isEmpty() ? QStringLiteral("syncthing") : QString::fromLocal8Bit(syncthingPathFromEnv));
// determine st port
const int syncthingPortFromEnv(qEnvironmentVariableIntValue("SYNCTHING_PORT"));
const QString syncthingPort(!syncthingPortFromEnv ? QStringLiteral("4001") : QString::number(syncthingPortFromEnv));
// start st
QStringList args;
args.reserve(2);
args << QStringLiteral("-gui-address=http://localhost:") + syncthingPort;
args << QStringLiteral("-gui-apikey=") + m_apiKey;
args << QStringLiteral("-home=") + configFile.absolutePath();
args << QStringLiteral("-no-browser");
args << QStringLiteral("-verbose");
m_syncthingProcess.start(syncthingPath, args);
// setup connection
m_connection.setSyncthingUrl(QStringLiteral("http://localhost:") + syncthingPort);
// keep track of status changes
QObject::connect(&m_connection, &SyncthingConnection::statusChanged, [this] {
cerr << " - Connection status changed to: " << m_connection.statusText().toLocal8Bit().data() << endl;
});
}
/*!
* \brief Terminates Syncthing and prints stdout/stderr from Syncthing.
*/
void ConnectionTests::tearDown()
{
if(m_syncthingProcess.state() == QProcess::Running) {
cerr << "\n - Waiting for Syncthing to terminate ..." << endl;
m_syncthingProcess.terminate();
m_syncthingProcess.waitForFinished();
}
if(m_syncthingProcess.isOpen()) {
cerr << "\n - Syncthing terminated with exit code " << m_syncthingProcess.exitCode() << ".\n";
cerr << "\n - Syncthing stdout during the testrun:\n" << m_syncthingProcess.readAllStandardOutput().data();
cerr << "\n - Syncthing stderr during the testrun:\n" << m_syncthingProcess.readAllStandardError().data();
}
}
//
// test helper
//
/*!
* \brief Prints a QString; required to use QString with CPPUNIT_ASSERT_EQUAL_MESSAGE.
*/
std::ostream &operator <<(std::ostream &o, const QString &qstring)
{
o << qstring.toLocal8Bit().data();
return o;
}
/*!
* \brief Waits for the in ms specified \a duration keeping the event loop running.
*/
void wait(int duration)
{
QEventLoop loop;
QTimer::singleShot(duration, &loop, &QEventLoop::quit);
loop.exec();
}
void noop()
{}
/*!
* \brief Waits until the \a signal is emitted by \a sender when performing \a action and connects \a signal with \a handler if specified.
* \throws Fails if \a signal is not emitted in at least \a timeout milliseconds or when at least one of the required
* connections can not be established.
*/
template<typename Signal, typename Action, typename Handler = std::function<void(void)> >
void waitForSignal(typename QtPrivate::FunctionPointer<Signal>::Object *sender, Signal signal, Action action, int timeout = 2500, Handler handler = nullptr)
{
// determine name of the signal for error messages
const QByteArray signalName(QMetaMethod::fromSignal(signal).name());
// create loop and connect the signal to its quit slot
QEventLoop loop;
if(!QObject::connect(sender, signal, &loop, &QEventLoop::quit, Qt::DirectConnection)) {
CPPUNIT_FAIL(argsToString("Unable to connect signal ", signalName.data(), " for waiting"));
}
// if specified, also connect handler to signal
if(handler) {
if(!QObject::connect(sender, signal, sender, handler, Qt::DirectConnection)) {
CPPUNIT_FAIL(argsToString("Unable to connect signal ", signalName.data(), " to handler"));
}
}
// handle case when signal is directly emitted
bool signalDirectlyEmitted = false;
if(!QObject::connect(sender, signal, sender, [&signalDirectlyEmitted] {
signalDirectlyEmitted = true;
}, Qt::DirectConnection)) {
CPPUNIT_FAIL(argsToString("Unable to connect signal ", signalName.data(), " to check for direct emmitation"));
}
// perform specified action
action();
// no reason to enter event loop when signal has been emitted directly
if(signalDirectlyEmitted) {
return;
}
// also connect and start a timer if a timeout has been specified
QTimer timer;
if(timeout) {
QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit, Qt::DirectConnection);
timer.setSingleShot(true);
timer.setInterval(timeout);
timer.start();
}
// enter event loop
loop.exec();
// check whether a timeout occured
if(timeout && !timer.isActive()) {
CPPUNIT_FAIL(argsToString("Signal ", signalName.data(), " has not emmitted within at least ", timeout, " ms."));
}
}
/*!
* \brief Variant of waitForSignal() where sender is the connection and the action is a method of the connection.
*/
template<typename Signal, typename Action, typename Handler>
void ConnectionTests::waitForConnection(Signal signal, Action action, int timeout, Handler handler)
{
waitForSignal(&m_connection, signal, bind(action, &m_connection), timeout, handler);
}
/*!
* \brief Variant of waitForSignal() where sender is the connection and the action is a method of the connection.
*/
template<typename Signal, typename Handler>
void ConnectionTests::waitForConnection(Signal signal, int timeout, Handler handler)
{
waitForSignal(&m_connection, signal, noop, timeout, handler);
}
//
// actual test
//
/*!
* \brief Tests basic behaviour of the SyncthingConnection class.
*/
void ConnectionTests::testConnection()
{
cerr << "\n - Connecting initially ..." << endl;
// error in case of wrong API key
m_connection.setAutoReconnectInterval(200);
m_connection.setApiKey(QByteArray("wrong API key"));
waitForConnection(&SyncthingConnection::error, static_cast<void(SyncthingConnection::*)(void)>(&SyncthingConnection::connect), 10000, [] (const QString &errorMessage) {
if(errorMessage != QStringLiteral("Unable to request Syncthing config: Connection refused")
&& errorMessage != QStringLiteral("Unable to request Syncthing status: Connection refused")) {
CPPUNIT_FAIL(argsToString("wrong error message in case of wrong API key: ", errorMessage.toLocal8Bit().data()));
}
});
// initial connection
m_connection.setApiKey(m_apiKey.toUtf8());
waitForConnection(&SyncthingConnection::statusChanged, static_cast<void(SyncthingConnection::*)(void)>(&SyncthingConnection::connect), 10000);
CPPUNIT_ASSERT_EQUAL_MESSAGE("connected and paused (one dev is initially paused)", QStringLiteral("connected, paused"), m_connection.statusText());
// dirs present
const auto &dirInfo = m_connection.dirInfo();
CPPUNIT_ASSERT_EQUAL_MESSAGE("2 dirs present", 2ul, dirInfo.size());
CPPUNIT_ASSERT_EQUAL(QStringLiteral("test1"), dirInfo.front().id);
CPPUNIT_ASSERT_EQUAL(QStringLiteral("test2"), dirInfo.back().id);
// devs present
const auto &devInfo = m_connection.devInfo();
CPPUNIT_ASSERT_EQUAL_MESSAGE("3 devs present", 3ul, devInfo.size());
// reconnecting
cerr << "\n - Reconnecting ..." << endl;
waitForConnection(&SyncthingConnection::statusChanged, static_cast<void(SyncthingConnection::*)(void)>(&SyncthingConnection::reconnect), 1000);
CPPUNIT_ASSERT_EQUAL_MESSAGE("reconnecting", QStringLiteral("reconnecting"), m_connection.statusText());
waitForConnection(&SyncthingConnection::statusChanged, 1000);
CPPUNIT_ASSERT_EQUAL_MESSAGE("connected again", QStringLiteral("connected, paused"), m_connection.statusText());
// disconnecting
cerr << "\n - Disconnecting ..." << endl;
waitForConnection(&SyncthingConnection::statusChanged, static_cast<void(SyncthingConnection::*)(void)>(&SyncthingConnection::disconnect), 5000);
CPPUNIT_ASSERT_EQUAL_MESSAGE("disconnected", QStringLiteral("disconnected"), m_connection.statusText());
}

View File

@ -0,0 +1 @@
#include <c++utilities/tests/cppunit.h>

View File

@ -80,82 +80,82 @@
<translation>Fehler beim Abfragen der Syncthing-Konfiguration: </translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="990"/>
<location filename="../syncthingconnection.cpp" line="992"/>
<source>Unable to parse Syncthing status: </source>
<translation>Fehler beim Auslesen des Syncthing-Status: </translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="996"/>
<location filename="../syncthingconnection.cpp" line="998"/>
<source>Unable to request Syncthing status: </source>
<translation>Fehler beim Abfragen des Syncthing-Status: </translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1075"/>
<location filename="../syncthingconnection.cpp" line="1077"/>
<source>Unable to parse connections: </source>
<translation>Fehler beim Auslesen der Verbindungen: </translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1081"/>
<location filename="../syncthingconnection.cpp" line="1083"/>
<source>Unable to request connections: </source>
<translation>Fehler beim Abfragen der Verbindungen: </translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1135"/>
<location filename="../syncthingconnection.cpp" line="1137"/>
<source>Unable to parse directory statistics: </source>
<translation>Fehler beim Auslesen der Verzeichnisstatistiken: </translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1141"/>
<location filename="../syncthingconnection.cpp" line="1143"/>
<source>Unable to request directory statistics: </source>
<translation>Fehler beim Abfragen der Verzeichnisstatistiken: </translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1177"/>
<location filename="../syncthingconnection.cpp" line="1179"/>
<source>Unable to parse device statistics: </source>
<translation>Fehler beim Auslesen der Gerätestatistiken: </translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1183"/>
<location filename="../syncthingconnection.cpp" line="1185"/>
<source>Unable to request device statistics: </source>
<translation>Fehler beim Abfragen der Gerätestatistiken: </translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1221"/>
<location filename="../syncthingconnection.cpp" line="1223"/>
<source>Unable to parse errors: </source>
<translation>Fehler beim Auslesen der Syncthing-Fehlermeldungen: </translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1232"/>
<location filename="../syncthingconnection.cpp" line="1234"/>
<source>Unable to request errors: </source>
<translation>Fehler beim Abfragen der Syncthing-Fehlermeldungen: </translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1248"/>
<location filename="../syncthingconnection.cpp" line="1250"/>
<source>Unable to request clearing errors: </source>
<translation>Fehler beim Löschen der Fehlermeldungen: </translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1301"/>
<location filename="../syncthingconnection.cpp" line="1303"/>
<source>Unable to parse Syncthing events: </source>
<translation>Fehler beim Auslesen der Syncthing-Ereignisse: </translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1323"/>
<location filename="../syncthingconnection.cpp" line="1325"/>
<source>Unable to request Syncthing events: </source>
<translation>Fehler beim Abfragen der Syncthing-Ereignisse: </translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1599"/>
<location filename="../syncthingconnection.cpp" line="1601"/>
<source>Unable to request rescan: </source>
<translation>Fehler beim Anfordern eines Verzeichnis-Rescans: </translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1619"/>
<location filename="../syncthingconnection.cpp" line="1621"/>
<source>Unable to request device pause/resume: </source>
<translation>Fehler beim Anfordern Gerät zu Pausieren/Fortzusetzen: </translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1636"/>
<location filename="../syncthingconnection.cpp" line="1638"/>
<source>Unable to request directory pause/resume: </source>
<translation>Fehler beim Anfordern Verzeichnis zu Pausieren/Fortzusetzen: </translation>
</message>
@ -164,12 +164,12 @@
<translation type="vanished">Fehler beim Anfordern Gerät zu Pausieren/Fortzusetzen: </translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1652"/>
<location filename="../syncthingconnection.cpp" line="1654"/>
<source>Unable to request restart: </source>
<translation>Fehler beim Anfordern eines Neustarts: </translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1668"/>
<location filename="../syncthingconnection.cpp" line="1670"/>
<source>Unable to request shutdown: </source>
<translation>Fehler beim Anfordern Syncthing zu beenden: </translation>
</message>

View File

@ -80,92 +80,92 @@
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="990"/>
<location filename="../syncthingconnection.cpp" line="992"/>
<source>Unable to parse Syncthing status: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="996"/>
<location filename="../syncthingconnection.cpp" line="998"/>
<source>Unable to request Syncthing status: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1075"/>
<location filename="../syncthingconnection.cpp" line="1077"/>
<source>Unable to parse connections: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1081"/>
<location filename="../syncthingconnection.cpp" line="1083"/>
<source>Unable to request connections: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1135"/>
<location filename="../syncthingconnection.cpp" line="1137"/>
<source>Unable to parse directory statistics: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1141"/>
<location filename="../syncthingconnection.cpp" line="1143"/>
<source>Unable to request directory statistics: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1177"/>
<location filename="../syncthingconnection.cpp" line="1179"/>
<source>Unable to parse device statistics: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1183"/>
<location filename="../syncthingconnection.cpp" line="1185"/>
<source>Unable to request device statistics: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1221"/>
<location filename="../syncthingconnection.cpp" line="1223"/>
<source>Unable to parse errors: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1232"/>
<location filename="../syncthingconnection.cpp" line="1234"/>
<source>Unable to request errors: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1248"/>
<location filename="../syncthingconnection.cpp" line="1250"/>
<source>Unable to request clearing errors: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1301"/>
<location filename="../syncthingconnection.cpp" line="1303"/>
<source>Unable to parse Syncthing events: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1323"/>
<location filename="../syncthingconnection.cpp" line="1325"/>
<source>Unable to request Syncthing events: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1599"/>
<location filename="../syncthingconnection.cpp" line="1601"/>
<source>Unable to request rescan: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1619"/>
<location filename="../syncthingconnection.cpp" line="1621"/>
<source>Unable to request device pause/resume: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1636"/>
<location filename="../syncthingconnection.cpp" line="1638"/>
<source>Unable to request directory pause/resume: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1652"/>
<location filename="../syncthingconnection.cpp" line="1654"/>
<source>Unable to request restart: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../syncthingconnection.cpp" line="1668"/>
<location filename="../syncthingconnection.cpp" line="1670"/>
<source>Unable to request shutdown: </source>
<translation type="unfinished"></translation>
</message>