Use signals for logAvailable() and qrCodeAvailable()
This commit is contained in:
parent
c7bccf023a
commit
2e67e6b2de
|
@ -316,7 +316,8 @@ void Application::handleError(
|
|||
|
||||
void Application::requestLog(const ArgumentOccurrence &)
|
||||
{
|
||||
m_connection.requestLog(&Application::printLog);
|
||||
connect(&m_connection, &SyncthingConnection::logAvailable, printLog);
|
||||
m_connection.requestLog();
|
||||
cerr << "Request log from " << m_settings.syncthingUrl.toLocal8Bit().data() << " ...";
|
||||
cerr.flush();
|
||||
}
|
||||
|
|
|
@ -78,6 +78,7 @@ SyncthingConnection::SyncthingConnection(const QString &syncthingUrl, const QByt
|
|||
, m_eventsReply(nullptr)
|
||||
, m_versionReply(nullptr)
|
||||
, m_diskEventsReply(nullptr)
|
||||
, m_logReply(nullptr)
|
||||
, m_unreadNotifications(false)
|
||||
, m_hasConfig(false)
|
||||
, m_hasStatus(false)
|
||||
|
@ -775,6 +776,9 @@ void SyncthingConnection::abortAllRequests()
|
|||
if (m_diskEventsReply) {
|
||||
m_diskEventsReply->abort();
|
||||
}
|
||||
if (m_logReply) {
|
||||
m_logReply->abort();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -944,58 +948,30 @@ void SyncthingConnection::requestEvents()
|
|||
/*!
|
||||
* \brief Requests a QR code for the specified \a text.
|
||||
*
|
||||
* The specified \a callback is called on success; otherwise error() is emitted.
|
||||
* qrCodeAvailable() is emitted on success; otherwise error() is emitted.
|
||||
*/
|
||||
QMetaObject::Connection SyncthingConnection::requestQrCode(const QString &text, const std::function<void(const QByteArray &)> &callback)
|
||||
void SyncthingConnection::requestQrCode(const QString &text)
|
||||
{
|
||||
QUrlQuery query;
|
||||
query.addQueryItem(QStringLiteral("text"), text);
|
||||
QNetworkReply *const reply = requestData(QStringLiteral("/qr/"), query, false);
|
||||
return QObject::connect(reply, &QNetworkReply::finished, [this, reply, &callback] {
|
||||
reply->deleteLater();
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError:
|
||||
callback(reply->readAll());
|
||||
break;
|
||||
default:
|
||||
emit error(tr("Unable to request QR-Code: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest, reply->error());
|
||||
}
|
||||
});
|
||||
QNetworkReply *reply = requestData(QStringLiteral("/qr/"), query, false);
|
||||
reply->setProperty("qrText", text);
|
||||
QObject::connect(reply, &QNetworkReply::finished, this, &SyncthingConnection::readQrCode);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Requests the Syncthing log.
|
||||
*
|
||||
* The specified \a callback is called on success; otherwise error() is emitted.
|
||||
* logAvailable() is emitted on success; otherwise error() is emitted.
|
||||
*/
|
||||
QMetaObject::Connection SyncthingConnection::requestLog(const std::function<void(const std::vector<SyncthingLogEntry> &)> &callback)
|
||||
void SyncthingConnection::requestLog()
|
||||
{
|
||||
QNetworkReply *const reply = requestData(QStringLiteral("system/log"), QUrlQuery());
|
||||
return QObject::connect(reply, &QNetworkReply::finished, [this, reply, &callback] {
|
||||
reply->deleteLater();
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError: {
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(reply->readAll(), &jsonError);
|
||||
if (jsonError.error != QJsonParseError::NoError) {
|
||||
emit error(tr("Unable to parse Syncthing log: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError);
|
||||
return;
|
||||
}
|
||||
|
||||
const QJsonArray log(replyDoc.object().value(QLatin1String("messages")).toArray());
|
||||
vector<SyncthingLogEntry> logEntries;
|
||||
logEntries.reserve(static_cast<size_t>(log.size()));
|
||||
for (const QJsonValue &logVal : log) {
|
||||
const QJsonObject logObj(logVal.toObject());
|
||||
logEntries.emplace_back(logObj.value(QLatin1String("when")).toString(), logObj.value(QLatin1String("message")).toString());
|
||||
}
|
||||
callback(logEntries);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
emit error(tr("Unable to request Syncthing log: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest, reply->error());
|
||||
}
|
||||
});
|
||||
// skip if already requesting log
|
||||
if (m_logReply != nullptr) {
|
||||
return;
|
||||
}
|
||||
m_logReply = requestData(QStringLiteral("system/log"), QUrlQuery());
|
||||
QObject::connect(m_logReply, &QNetworkReply::finished, this, &SyncthingConnection::readLog);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -2397,6 +2373,62 @@ void SyncthingConnection::readChangeEvent(DateTime eventTime, const QString &eve
|
|||
emit dirStatusChanged(*dirInfo, index);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Reads log entries queried via requestLog().
|
||||
*/
|
||||
void SyncthingConnection::readLog()
|
||||
{
|
||||
auto *const reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
if (reply == m_logReply) {
|
||||
m_logReply = nullptr;
|
||||
}
|
||||
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError: {
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(reply->readAll(), &jsonError);
|
||||
if (jsonError.error != QJsonParseError::NoError) {
|
||||
emit error(tr("Unable to parse Syncthing log: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError);
|
||||
return;
|
||||
}
|
||||
|
||||
const QJsonArray log(replyDoc.object().value(QLatin1String("messages")).toArray());
|
||||
vector<SyncthingLogEntry> logEntries;
|
||||
logEntries.reserve(static_cast<size_t>(log.size()));
|
||||
for (const QJsonValue &logVal : log) {
|
||||
const QJsonObject logObj(logVal.toObject());
|
||||
logEntries.emplace_back(logObj.value(QLatin1String("when")).toString(), logObj.value(QLatin1String("message")).toString());
|
||||
}
|
||||
emit logAvailable(logEntries);
|
||||
break;
|
||||
}
|
||||
case QNetworkReply::OperationCanceledError:
|
||||
break;
|
||||
default:
|
||||
emit error(tr("Unable to request Syncthing log: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest, reply->error());
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Reads the QR code queried via requestQrCode().
|
||||
*/
|
||||
void SyncthingConnection::readQrCode()
|
||||
{
|
||||
auto *const reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError:
|
||||
emit qrCodeAvailable(reply->property("qrText").toString(), reply->readAll());
|
||||
break;
|
||||
case QNetworkReply::OperationCanceledError:
|
||||
break;
|
||||
default:
|
||||
emit error(tr("Unable to request QR-Code: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest, reply->error());
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets the connection status. Ensures statusChanged() is emitted.
|
||||
* \param status Specifies the status; should be either SyncthingStatus::Disconnected, SyncthingStatus::Reconnecting, or
|
||||
|
|
|
@ -136,8 +136,8 @@ public:
|
|||
ChronoUtilities::DateTime startTime() const;
|
||||
ChronoUtilities::TimeSpan uptime() const;
|
||||
const QString &syncthingVersion() const;
|
||||
QMetaObject::Connection requestQrCode(const QString &text, const std::function<void(const QByteArray &)> &callback);
|
||||
QMetaObject::Connection requestLog(const std::function<void(const std::vector<SyncthingLogEntry> &)> &callback);
|
||||
void requestQrCode(const QString &text);
|
||||
void requestLog();
|
||||
const QList<QSslError> &expectedSslErrors() const;
|
||||
SyncthingDir *findDirInfo(const QString &dirId, int &row);
|
||||
SyncthingDir *findDirInfo(QLatin1String key, const QJsonObject &object, int *row = nullptr);
|
||||
|
@ -217,6 +217,8 @@ Q_SIGNALS:
|
|||
void directoryResumeTriggered(const QStringList &dirIds);
|
||||
void restartTriggered();
|
||||
void shutdownTriggered();
|
||||
void logAvailable(const std::vector<SyncthingLogEntry> &logEntries);
|
||||
void qrCodeAvailable(const QString &text, const QByteArray &qrCodeData);
|
||||
|
||||
private Q_SLOTS:
|
||||
void abortAllRequests();
|
||||
|
@ -262,6 +264,8 @@ private Q_SLOTS:
|
|||
void readVersion();
|
||||
void readDiskEvents();
|
||||
void readChangeEvent(ChronoUtilities::DateTime eventTime, const QString &eventType, const QJsonObject &eventData);
|
||||
void readLog();
|
||||
void readQrCode();
|
||||
|
||||
void continueConnecting();
|
||||
void continueReconnecting();
|
||||
|
@ -311,6 +315,7 @@ private:
|
|||
QNetworkReply *m_eventsReply;
|
||||
QNetworkReply *m_versionReply;
|
||||
QNetworkReply *m_diskEventsReply;
|
||||
QNetworkReply *m_logReply;
|
||||
bool m_unreadNotifications;
|
||||
bool m_hasConfig;
|
||||
bool m_hasStatus;
|
||||
|
|
|
@ -79,7 +79,9 @@ public:
|
|||
void tearDown();
|
||||
|
||||
private:
|
||||
template <typename Action, typename... Signalinfo> void waitForConnection(Action action, int timeout, const Signalinfo &... signalinfo);
|
||||
template <typename Action, typename... Signalinfo> void waitForConnection(Action action, int timeout, const Signalinfo &... signalInfo);
|
||||
template <typename Action, typename FailureSignalInfo, typename... Signalinfo>
|
||||
void waitForConnectionOrFail(Action action, int timeout, const FailureSignalInfo &failureSignalInfo, const Signalinfo &... signalInfo);
|
||||
template <typename Signal, typename Handler = function<void(void)>>
|
||||
SignalInfo<Signal, Handler> connectionSignal(
|
||||
Signal signal, const Handler &handler = function<void(void)>(), bool *correctSignalEmitted = nullptr);
|
||||
|
@ -149,12 +151,22 @@ void ConnectionTests::tearDown()
|
|||
//
|
||||
|
||||
/*!
|
||||
* \brief Variant of waitForSignal() where sender is the connection and the action is a method of the connection.
|
||||
* \brief Variant of waitForSignal() where the sender is the connection and the action is a method of the connection.
|
||||
*/
|
||||
template <typename Action, typename... Signalinfo>
|
||||
void ConnectionTests::waitForConnection(Action action, int timeout, const Signalinfo &... signalinfo)
|
||||
template <typename Action, typename... SignalInfo>
|
||||
void ConnectionTests::waitForConnection(Action action, int timeout, const SignalInfo &... signalInfo)
|
||||
{
|
||||
waitForSignals(bind(action, &m_connection), timeout, signalinfo...);
|
||||
waitForSignals(bind(action, &m_connection), timeout, signalInfo...);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Variant of waitForSignalOrFail() where the sender is the connection and the action is a method of the connection.
|
||||
*/
|
||||
template <typename Action, typename FailureSignalInfo, typename... SignalInfo>
|
||||
void ConnectionTests::waitForConnectionOrFail(
|
||||
Action action, int timeout, const FailureSignalInfo &failureSignalInfo, const SignalInfo &... signalInfo)
|
||||
{
|
||||
waitForSignalsOrFail(bind(action, &m_connection), timeout, failureSignalInfo, signalInfo...);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -555,30 +567,13 @@ void ConnectionTests::testRequestingLog()
|
|||
cerr << "\n - Requesting log ..." << endl;
|
||||
waitForConnected();
|
||||
|
||||
// timeout after 1 second
|
||||
QTimer timeout;
|
||||
timeout.setSingleShot(true);
|
||||
timeout.setInterval(SYNCTHINGTESTHELPER_TIMEOUT(5000));
|
||||
QEventLoop loop;
|
||||
CPPUNIT_ASSERT(QObject::connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit));
|
||||
CPPUNIT_ASSERT(QObject::connect(&m_connection, &SyncthingConnection::error, &loop, &QEventLoop::quit));
|
||||
|
||||
bool callbackOk = false;
|
||||
const auto request = m_connection.requestLog([&callbackOk, &loop](const std::vector<SyncthingLogEntry> &logEntries) {
|
||||
callbackOk = true;
|
||||
const function<void(const vector<SyncthingLogEntry> &)> handleLogAvailable = [&](const vector<SyncthingLogEntry> &logEntries) {
|
||||
CPPUNIT_ASSERT(!logEntries.empty());
|
||||
CPPUNIT_ASSERT(!logEntries[0].when.isEmpty());
|
||||
CPPUNIT_ASSERT(!logEntries[0].message.isEmpty());
|
||||
loop.quit();
|
||||
});
|
||||
CPPUNIT_ASSERT(request);
|
||||
|
||||
timeout.start();
|
||||
loop.exec();
|
||||
QObject::disconnect(request); // ensure callback is not called after return (in error case)
|
||||
CPPUNIT_ASSERT_MESSAGE(
|
||||
argsToString("log entries returned before timeout of ", timeout.interval(), " ms (set SYNCTHING_TEST_TIMEOUT_FACTOR to increase timeout)"),
|
||||
callbackOk);
|
||||
};
|
||||
waitForConnectionOrFail(&SyncthingConnection::requestLog, 5000, connectionSignal(&SyncthingConnection::error),
|
||||
connectionSignal(&SyncthingConnection::logAvailable, handleLogAvailable));
|
||||
}
|
||||
|
||||
void ConnectionTests::testRequestingQrCode()
|
||||
|
@ -586,28 +581,12 @@ void ConnectionTests::testRequestingQrCode()
|
|||
cerr << "\n - Requesting QR-Code for own device ID ..." << endl;
|
||||
waitForConnected();
|
||||
|
||||
// timeout after 2 seconds
|
||||
QTimer timeout;
|
||||
timeout.setSingleShot(true);
|
||||
timeout.setInterval(SYNCTHINGTESTHELPER_TIMEOUT(5000));
|
||||
QEventLoop loop;
|
||||
CPPUNIT_ASSERT(QObject::connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit));
|
||||
CPPUNIT_ASSERT(QObject::connect(&m_connection, &SyncthingConnection::error, &loop, &QEventLoop::quit));
|
||||
|
||||
bool callbackOk = false;
|
||||
const auto request = m_connection.requestQrCode(m_ownDevId, [&callbackOk, &loop](const QByteArray &data) {
|
||||
callbackOk = true;
|
||||
const function<void(const QString &, const QByteArray &)> handleQrCodeAvailable = [&](const QString &qrText, const QByteArray &data) {
|
||||
CPPUNIT_ASSERT_EQUAL(QStringLiteral("some text"), qrText);
|
||||
CPPUNIT_ASSERT(!data.isEmpty());
|
||||
loop.quit();
|
||||
});
|
||||
CPPUNIT_ASSERT(request);
|
||||
|
||||
timeout.start();
|
||||
loop.exec();
|
||||
QObject::disconnect(request); // ensure callback is not called after return (in error case)
|
||||
CPPUNIT_ASSERT_MESSAGE(argsToString("QR code returned before an error occurred or timeout of ", timeout.interval(),
|
||||
" ms (set SYNCTHING_TEST_TIMEOUT_FACTOR to increase timeout)"),
|
||||
callbackOk);
|
||||
};
|
||||
waitForSignalsOrFail(bind(&SyncthingConnection::requestQrCode, &m_connection, QStringLiteral("some text")), 5000,
|
||||
connectionSignal(&SyncthingConnection::error), connectionSignal(&SyncthingConnection::qrCodeAvailable, handleQrCodeAvailable));
|
||||
}
|
||||
|
||||
void ConnectionTests::testDisconnecting()
|
||||
|
|
|
@ -46,13 +46,18 @@ QDialog *ownDeviceIdDialog(Data::SyncthingConnection &connection)
|
|||
QObject::connect(
|
||||
copyPushButton, &QPushButton::clicked, bind(&QClipboard::setText, QGuiApplication::clipboard(), connection.myId(), QClipboard::Clipboard));
|
||||
layout->addWidget(copyPushButton);
|
||||
QObject::connect(dlg, &QWidget::destroyed,
|
||||
connection.requestQrCode(connection.myId());
|
||||
QObject::connect(dlg, &QObject::destroyed,
|
||||
bind(static_cast<bool (*)(const QMetaObject::Connection &)>(&QObject::disconnect),
|
||||
connection.requestQrCode(connection.myId(), [pixmapLabel](const QByteArray &data) {
|
||||
QPixmap pixmap;
|
||||
pixmap.loadFromData(data);
|
||||
pixmapLabel->setPixmap(pixmap);
|
||||
})));
|
||||
QObject::connect(&connection, &SyncthingConnection::qrCodeAvailable,
|
||||
[pixmapLabel, devId = connection.myId()](const QString &text, const QByteArray &data) {
|
||||
if (text != devId) {
|
||||
return;
|
||||
}
|
||||
QPixmap pixmap;
|
||||
pixmap.loadFromData(data);
|
||||
pixmapLabel->setPixmap(pixmap);
|
||||
})));
|
||||
dlg->setLayout(layout);
|
||||
return dlg;
|
||||
}
|
||||
|
|
|
@ -142,13 +142,9 @@ TextViewDialog *TextViewDialog::forDirectoryErrors(const Data::SyncthingDir &dir
|
|||
TextViewDialog *TextViewDialog::forLogEntries(SyncthingConnection &connection)
|
||||
{
|
||||
auto *const dlg = new TextViewDialog(tr("Log"));
|
||||
const auto loadLog = [dlg, &connection] {
|
||||
connect(dlg, &QWidget::destroyed,
|
||||
bind(static_cast<bool (*)(const QMetaObject::Connection &)>(&QObject::disconnect),
|
||||
connection.requestLog(bind(&TextViewDialog::showLogEntries, dlg, _1))));
|
||||
};
|
||||
connect(dlg, &TextViewDialog::reload, loadLog);
|
||||
loadLog();
|
||||
QObject::connect(&connection, &SyncthingConnection::logAvailable, dlg, &TextViewDialog::showLogEntries);
|
||||
connect(dlg, &TextViewDialog::reload, &connection, &SyncthingConnection::requestLog);
|
||||
connection.requestLog();
|
||||
return dlg;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue