From 914aac6e399c31ef6c97a92b2c58002afd0f3928 Mon Sep 17 00:00:00 2001 From: Martchus Date: Mon, 19 Feb 2018 03:22:47 +0100 Subject: [PATCH] Make bash completion faster by requesting only config --- cli/application.cpp | 27 +++++++---- cli/application.h | 5 ++- connector/syncthingconnection.cpp | 17 +++++-- connector/syncthingconnection.h | 20 ++++----- testhelper/helper.h | 74 +++++++++++++++++++++++++++---- 5 files changed, 112 insertions(+), 31 deletions(-) diff --git a/cli/application.cpp b/cli/application.cpp index 190bf7c..70784ff 100644 --- a/cli/application.cpp +++ b/cli/application.cpp @@ -216,14 +216,23 @@ int Application::loadConfig() return 0; } -void Application::waitForConnected(int timeout) +bool Application::waitForConnected(int timeout) { using namespace TestUtilities; bool isConnected = m_connection.isConnected(); const function checkStatus([this, &isConnected](SyncthingStatus) { isConnected = m_connection.isConnected(); }); - waitForSignals(bind(static_cast(&SyncthingConnection::reconnect), ref(m_connection), - ref(m_settings)), - timeout, signalInfo(&m_connection, &SyncthingConnection::statusChanged, checkStatus, &isConnected)); + return waitForSignalsOrFail(bind(static_cast(&SyncthingConnection::reconnect), + ref(m_connection), ref(m_settings)), + timeout, signalInfo(&m_connection, &SyncthingConnection::error), + signalInfo(&m_connection, &SyncthingConnection::statusChanged, checkStatus, &isConnected)); +} + +bool Application::waitForConfig(int timeout) +{ + using namespace TestUtilities; + return waitForSignalsOrFail(bind(&SyncthingConnection::requestConfig, ref(m_connection)), timeout, + signalInfo(&m_connection, &SyncthingConnection::error), signalInfo(&m_connection, &SyncthingConnection::newConfig), + signalInfo(&m_connection, &SyncthingConnection::newDirs), signalInfo(&m_connection, &SyncthingConnection::newDevices)); } void Application::handleStatusChanged(SyncthingStatus newStatus) @@ -723,9 +732,10 @@ void Application::initDirCompletion(Argument &arg, const ArgumentOccurrence &) } // load config and wait for connected loadConfig(); - waitForConnected(2000); + waitForConfig(2000); // set directory IDs as completion values - arg.setPreDefinedCompletionValues(m_connection.directoryIds().join(QChar(' ')).toUtf8().data()); + m_dirCompletion = m_connection.directoryIds().join(QChar(' ')).toUtf8(); + arg.setPreDefinedCompletionValues(m_dirCompletion.data()); } void Application::initDevCompletion(Argument &arg, const ArgumentOccurrence &) @@ -736,7 +746,7 @@ void Application::initDevCompletion(Argument &arg, const ArgumentOccurrence &) } // load config and wait for connected loadConfig(); - waitForConnected(2000); + waitForConfig(2000); // set device IDs and names as completion values QStringList completionValues; const size_t valueCount = m_connection.devInfo().size() << 2; @@ -747,7 +757,8 @@ void Application::initDevCompletion(Argument &arg, const ArgumentOccurrence &) for (const SyncthingDev &dev : m_connection.devInfo()) { completionValues << dev.id << dev.name; } - arg.setPreDefinedCompletionValues(completionValues.join(QChar(' ')).toUtf8().data()); + m_devCompletion = completionValues.join(QChar(' ')).toUtf8(); + arg.setPreDefinedCompletionValues(m_devCompletion.data()); } RelevantDir Application::findDirectory(const QString &dirIdentifier) diff --git a/cli/application.h b/cli/application.h index bfcb43f..21e8a64 100644 --- a/cli/application.h +++ b/cli/application.h @@ -53,7 +53,8 @@ private slots: private: int loadConfig(); - void waitForConnected(int timeout); + bool waitForConnected(int timeout); + bool waitForConfig(int timeout); void requestLog(const ArgumentOccurrence &); void requestShutdown(const ArgumentOccurrence &); void requestRestart(const ArgumentOccurrence &); @@ -84,6 +85,8 @@ private: std::vector m_relevantDirs; std::vector m_relevantDevs; RelevantDir m_pwd; + QByteArray m_dirCompletion; + QByteArray m_devCompletion; int m_idleDuration; int m_idleTimeout; bool m_argsRead; diff --git a/connector/syncthingconnection.cpp b/connector/syncthingconnection.cpp index 2513e43..19f361a 100644 --- a/connector/syncthingconnection.cpp +++ b/connector/syncthingconnection.cpp @@ -182,7 +182,7 @@ void SyncthingConnection::connect(SyncthingConnectionSettings &connectionSetting */ void SyncthingConnection::disconnect() { - m_reconnecting = m_hasConfig = m_hasStatus = false; + m_reconnecting = m_hasConfig = m_hasStatus = m_keepPolling = false; m_autoReconnectTries = 0; abortAllRequests(); } @@ -1007,9 +1007,15 @@ void SyncthingConnection::readConfig() } m_rawConfig = replyDoc.object(); - emit newConfig(m_rawConfig); m_hasConfig = true; - concludeReadingConfigAndStatus(); + emit newConfig(m_rawConfig); + + if (m_keepPolling) { + concludeReadingConfigAndStatus(); + } else { + readDevs(m_rawConfig.value(QStringLiteral("devices")).toArray()); + readDirs(m_rawConfig.value(QStringLiteral("folders")).toArray()); + } break; } case QNetworkReply::OperationCanceledError: @@ -1124,7 +1130,10 @@ void SyncthingConnection::readStatus() emitMyIdChanged(replyDoc.object().value(QStringLiteral("myID")).toString()); // other values are currently not interesting m_hasStatus = true; - concludeReadingConfigAndStatus(); + + if (m_keepPolling) { + concludeReadingConfigAndStatus(); + } break; } case QNetworkReply::OperationCanceledError: diff --git a/connector/syncthingconnection.h b/connector/syncthingconnection.h index 871084b..e526d32 100644 --- a/connector/syncthingconnection.h +++ b/connector/syncthingconnection.h @@ -152,6 +152,16 @@ public Q_SLOTS: void shutdown(); void considerAllNotificationsRead(); + void requestConfig(); + void requestStatus(); + void requestErrors(); + void requestConnections(); + void requestClearingErrors(); + void requestDirStatistics(); + void requestDirStatus(const QString &dirId); + void requestCompletion(const QString &devId, const QString &dirId); + void requestDeviceStatistics(); + Q_SIGNALS: void newConfig(const QJsonObject &config); void newDirs(const std::vector &dirs); @@ -177,16 +187,6 @@ Q_SIGNALS: void shutdownTriggered(); private Q_SLOTS: - void requestConfig(); - void requestStatus(); - void requestConnections(); - void requestErrors(); - void requestClearingErrors(); - void requestDirStatistics(); - void requestDirStatus(const QString &dirId); - void requestCompletion(const QString &devId, const QString &dirId); - - void requestDeviceStatistics(); void requestEvents(); void abortAllRequests(); diff --git a/testhelper/helper.h b/testhelper/helper.h index 3e64e57..d8e3580 100644 --- a/testhelper/helper.h +++ b/testhelper/helper.h @@ -103,6 +103,17 @@ private: */ template class SignalInfo { public: + /*! + * \brief Constructs a dummy SignalInfo which will never be considered emitted. + */ + SignalInfo() + : m_sender(nullptr) + , m_signal(nullptr) + , m_correctSignalEmitted(nullptr) + , m_signalEmitted(false) + { + } + /*! * \brief Constructs a SignalInfo with handler and automatically connects the handler to the signal. * \param sender Specifies the object which will emit \a signal. @@ -182,6 +193,9 @@ public: */ void connectToLoop(QEventLoop *loop) const { + if (!m_sender) { + return; + } QObject::disconnect(m_loopConnection); m_loopConnection = QObject::connect(m_sender, m_signal, loop, &QEventLoop::quit, Qt::DirectConnection); #ifndef SYNCTHINGTESTHELPER_FOR_CLI @@ -205,12 +219,28 @@ private: * \brief Constructs a new SignalInfo. */ template -inline SignalInfo signalInfo(typename QtPrivate::FunctionPointer::Object *sender, Signal signal, - const Handler &handler = Handler(), bool *correctSignalEmitted = nullptr) +inline auto signalInfo(typename QtPrivate::FunctionPointer::Object *sender, Signal signal, const Handler &handler = Handler(), + bool *correctSignalEmitted = nullptr) { return SignalInfo(sender, signal, handler, correctSignalEmitted); } +/*! + * \brief Constructs a new SignalInfo. + */ +template inline auto signalInfo(typename QtPrivate::FunctionPointer::Object *sender, Signal signal) +{ + return SignalInfo>(sender, signal, std::function(), nullptr); +} + +/*! + * \brief Constructs a new SignalInfo. + */ +inline auto dummySignalInfo() +{ + return SignalInfo>(); +} + /*! * \brief Connects the specified signal infos the \a loop via SignalInfo::connectToLoop(). */ @@ -275,13 +305,31 @@ inline QByteArray failedSignalNames(const SignalInfo &firstSignalInfo, const Sig * \arg signalInfos Specifies the signals to wait for. * \throws Fails if not all signals have been emitted in at least \a timeout milliseconds or when at least one of the * required connections can not be established. + * \returns Returns true if all \a signalInfos have been omitted before the \a timeout exceeded. */ -template void waitForSignals(Action action, int timeout, const SignalInfos &... signalInfos) +template bool waitForSignals(Action action, int timeout, const SignalInfos &... signalInfos) +{ + return waitForSignalsOrFail(action, timeout, dummySignalInfo(), signalInfos...); +} + +/*! + * \brief Waits until the specified signals have been emitted when performing async operations triggered by \a action. Aborts when \a failure is emitted. + * \arg action Specifies a method to trigger the action to run when waiting. + * \arg timeout Specifies the max. time to wait. Set to zero to wait forever. + * \arg failure Specifies the signal indicating an error occured. + * \arg signalInfos Specifies the signals to wait for. + * \throws Fails if not all signals have been emitted in at least \a timeout milliseconds or when at least one of the + * required connections can not be established. + * \returns Returns true if all \a signalInfos have been omitted before \a failure as been emitted or the \a timeout exceeded. + */ +template +bool waitForSignalsOrFail(Action action, int timeout, const SignalInfo &failure, const SignalInfos &... signalInfos) { // use loop for waiting QEventLoop loop; // connect all signals to loop so loop is interrupted when one of the signals is emitted + connectSignalInfosToLoop(&loop, failure); connectSignalInfosToLoop(&loop, signalInfos...); // perform specified action @@ -289,7 +337,10 @@ template void waitForSignals(Action a // no reason to enter event loop when all signals have been emitted directly if (checkWhetherAllSignalsEmitted(signalInfos...)) { - return; + return true; + } + if (checkWhetherAllSignalsEmitted(failure)) { + return false; } // also connect and start a timer if a timeout has been specified @@ -302,19 +353,26 @@ template void waitForSignals(Action a } // exec event loop as long as the right signal has not been emitted yet and there is still time - bool allSignalsEmitted = false; + bool allSignalsEmitted = false, failureEmitted = false; do { loop.exec(); - } while (!(allSignalsEmitted = checkWhetherAllSignalsEmitted(signalInfos...)) && (!timeout || timer.isActive())); + } while (!(failureEmitted = checkWhetherAllSignalsEmitted(failure)) && !(allSignalsEmitted = checkWhetherAllSignalsEmitted(signalInfos...)) + && (!timeout || timer.isActive())); -// check whether a timeout occured + // check whether a timeout occured + const bool timeoutFailed(!allSignalsEmitted && timeout && !timer.isActive()); #ifndef SYNCTHINGTESTHELPER_FOR_CLI - if (!allSignalsEmitted && timeout && !timer.isActive()) { + if (failureEmitted) { + CPPUNIT_FAIL( + argsToString("Signal(s) ", failedSignalNames(signalInfos...).data(), " has/have not emmitted before ", failure.signalName().data(), '.')); + } else if (timeoutFailed) { CPPUNIT_FAIL(argsToString("Signal(s) ", failedSignalNames(signalInfos...).data(), " has/have not emmitted within at least ", timer.interval(), " ms.", timeoutFactor != 1.0 ? argsToString(" (original timeout: ", timeout, " ms)") : std::string())); } #endif + return !failureEmitted && !timeoutFailed; } + } // namespace TestUtilities #endif // SYNCTHINGTESTHELPER_H