Make bash completion faster by requesting only config

This commit is contained in:
Martchus 2018-02-19 03:22:47 +01:00
parent 6be44cc93f
commit 914aac6e39
5 changed files with 112 additions and 31 deletions

View File

@ -216,14 +216,23 @@ int Application::loadConfig()
return 0; return 0;
} }
void Application::waitForConnected(int timeout) bool Application::waitForConnected(int timeout)
{ {
using namespace TestUtilities; using namespace TestUtilities;
bool isConnected = m_connection.isConnected(); bool isConnected = m_connection.isConnected();
const function<void(SyncthingStatus)> checkStatus([this, &isConnected](SyncthingStatus) { isConnected = m_connection.isConnected(); }); const function<void(SyncthingStatus)> checkStatus([this, &isConnected](SyncthingStatus) { isConnected = m_connection.isConnected(); });
waitForSignals(bind(static_cast<void (SyncthingConnection::*)(SyncthingConnectionSettings &)>(&SyncthingConnection::reconnect), ref(m_connection), return waitForSignalsOrFail(bind(static_cast<void (SyncthingConnection::*)(SyncthingConnectionSettings &)>(&SyncthingConnection::reconnect),
ref(m_settings)), ref(m_connection), ref(m_settings)),
timeout, signalInfo(&m_connection, &SyncthingConnection::statusChanged, checkStatus, &isConnected)); 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) void Application::handleStatusChanged(SyncthingStatus newStatus)
@ -723,9 +732,10 @@ void Application::initDirCompletion(Argument &arg, const ArgumentOccurrence &)
} }
// load config and wait for connected // load config and wait for connected
loadConfig(); loadConfig();
waitForConnected(2000); waitForConfig(2000);
// set directory IDs as completion values // 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 &) void Application::initDevCompletion(Argument &arg, const ArgumentOccurrence &)
@ -736,7 +746,7 @@ void Application::initDevCompletion(Argument &arg, const ArgumentOccurrence &)
} }
// load config and wait for connected // load config and wait for connected
loadConfig(); loadConfig();
waitForConnected(2000); waitForConfig(2000);
// set device IDs and names as completion values // set device IDs and names as completion values
QStringList completionValues; QStringList completionValues;
const size_t valueCount = m_connection.devInfo().size() << 2; 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()) { for (const SyncthingDev &dev : m_connection.devInfo()) {
completionValues << dev.id << dev.name; 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) RelevantDir Application::findDirectory(const QString &dirIdentifier)

View File

@ -53,7 +53,8 @@ private slots:
private: private:
int loadConfig(); int loadConfig();
void waitForConnected(int timeout); bool waitForConnected(int timeout);
bool waitForConfig(int timeout);
void requestLog(const ArgumentOccurrence &); void requestLog(const ArgumentOccurrence &);
void requestShutdown(const ArgumentOccurrence &); void requestShutdown(const ArgumentOccurrence &);
void requestRestart(const ArgumentOccurrence &); void requestRestart(const ArgumentOccurrence &);
@ -84,6 +85,8 @@ private:
std::vector<RelevantDir> m_relevantDirs; std::vector<RelevantDir> m_relevantDirs;
std::vector<const Data::SyncthingDev *> m_relevantDevs; std::vector<const Data::SyncthingDev *> m_relevantDevs;
RelevantDir m_pwd; RelevantDir m_pwd;
QByteArray m_dirCompletion;
QByteArray m_devCompletion;
int m_idleDuration; int m_idleDuration;
int m_idleTimeout; int m_idleTimeout;
bool m_argsRead; bool m_argsRead;

View File

@ -182,7 +182,7 @@ void SyncthingConnection::connect(SyncthingConnectionSettings &connectionSetting
*/ */
void SyncthingConnection::disconnect() void SyncthingConnection::disconnect()
{ {
m_reconnecting = m_hasConfig = m_hasStatus = false; m_reconnecting = m_hasConfig = m_hasStatus = m_keepPolling = false;
m_autoReconnectTries = 0; m_autoReconnectTries = 0;
abortAllRequests(); abortAllRequests();
} }
@ -1007,9 +1007,15 @@ void SyncthingConnection::readConfig()
} }
m_rawConfig = replyDoc.object(); m_rawConfig = replyDoc.object();
emit newConfig(m_rawConfig);
m_hasConfig = true; 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; break;
} }
case QNetworkReply::OperationCanceledError: case QNetworkReply::OperationCanceledError:
@ -1124,7 +1130,10 @@ void SyncthingConnection::readStatus()
emitMyIdChanged(replyDoc.object().value(QStringLiteral("myID")).toString()); emitMyIdChanged(replyDoc.object().value(QStringLiteral("myID")).toString());
// other values are currently not interesting // other values are currently not interesting
m_hasStatus = true; m_hasStatus = true;
concludeReadingConfigAndStatus();
if (m_keepPolling) {
concludeReadingConfigAndStatus();
}
break; break;
} }
case QNetworkReply::OperationCanceledError: case QNetworkReply::OperationCanceledError:

View File

@ -152,6 +152,16 @@ public Q_SLOTS:
void shutdown(); void shutdown();
void considerAllNotificationsRead(); 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: Q_SIGNALS:
void newConfig(const QJsonObject &config); void newConfig(const QJsonObject &config);
void newDirs(const std::vector<SyncthingDir> &dirs); void newDirs(const std::vector<SyncthingDir> &dirs);
@ -177,16 +187,6 @@ Q_SIGNALS:
void shutdownTriggered(); void shutdownTriggered();
private Q_SLOTS: 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 requestEvents();
void abortAllRequests(); void abortAllRequests();

View File

@ -103,6 +103,17 @@ private:
*/ */
template <typename Signal, typename Handler> class SignalInfo { template <typename Signal, typename Handler> class SignalInfo {
public: 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. * \brief Constructs a SignalInfo with handler and automatically connects the handler to the signal.
* \param sender Specifies the object which will emit \a signal. * \param sender Specifies the object which will emit \a signal.
@ -182,6 +193,9 @@ public:
*/ */
void connectToLoop(QEventLoop *loop) const void connectToLoop(QEventLoop *loop) const
{ {
if (!m_sender) {
return;
}
QObject::disconnect(m_loopConnection); QObject::disconnect(m_loopConnection);
m_loopConnection = QObject::connect(m_sender, m_signal, loop, &QEventLoop::quit, Qt::DirectConnection); m_loopConnection = QObject::connect(m_sender, m_signal, loop, &QEventLoop::quit, Qt::DirectConnection);
#ifndef SYNCTHINGTESTHELPER_FOR_CLI #ifndef SYNCTHINGTESTHELPER_FOR_CLI
@ -205,12 +219,28 @@ private:
* \brief Constructs a new SignalInfo. * \brief Constructs a new SignalInfo.
*/ */
template <typename Signal, typename Handler> template <typename Signal, typename Handler>
inline SignalInfo<Signal, Handler> signalInfo(typename QtPrivate::FunctionPointer<Signal>::Object *sender, Signal signal, inline auto signalInfo(typename QtPrivate::FunctionPointer<Signal>::Object *sender, Signal signal, const Handler &handler = Handler(),
const Handler &handler = Handler(), bool *correctSignalEmitted = nullptr) bool *correctSignalEmitted = nullptr)
{ {
return SignalInfo<Signal, Handler>(sender, signal, handler, correctSignalEmitted); return SignalInfo<Signal, Handler>(sender, signal, handler, correctSignalEmitted);
} }
/*!
* \brief Constructs a new SignalInfo.
*/
template <typename Signal> inline auto signalInfo(typename QtPrivate::FunctionPointer<Signal>::Object *sender, Signal signal)
{
return SignalInfo<Signal, std::function<void(void)>>(sender, signal, std::function<void(void)>(), nullptr);
}
/*!
* \brief Constructs a new SignalInfo.
*/
inline auto dummySignalInfo()
{
return SignalInfo<decltype(&QObject::destroyed), std::function<void(void)>>();
}
/*! /*!
* \brief Connects the specified signal infos the \a loop via SignalInfo::connectToLoop(). * \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. * \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 * \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. * required connections can not be established.
* \returns Returns true if all \a signalInfos have been omitted before the \a timeout exceeded.
*/ */
template <typename Action, typename... SignalInfos> void waitForSignals(Action action, int timeout, const SignalInfos &... signalInfos) template <typename Action, typename... SignalInfos> 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 <typename Action, typename SignalInfo, typename... SignalInfos>
bool waitForSignalsOrFail(Action action, int timeout, const SignalInfo &failure, const SignalInfos &... signalInfos)
{ {
// use loop for waiting // use loop for waiting
QEventLoop loop; QEventLoop loop;
// connect all signals to loop so loop is interrupted when one of the signals is emitted // connect all signals to loop so loop is interrupted when one of the signals is emitted
connectSignalInfosToLoop(&loop, failure);
connectSignalInfosToLoop(&loop, signalInfos...); connectSignalInfosToLoop(&loop, signalInfos...);
// perform specified action // perform specified action
@ -289,7 +337,10 @@ template <typename Action, typename... SignalInfos> void waitForSignals(Action a
// no reason to enter event loop when all signals have been emitted directly // no reason to enter event loop when all signals have been emitted directly
if (checkWhetherAllSignalsEmitted(signalInfos...)) { if (checkWhetherAllSignalsEmitted(signalInfos...)) {
return; return true;
}
if (checkWhetherAllSignalsEmitted(failure)) {
return false;
} }
// also connect and start a timer if a timeout has been specified // also connect and start a timer if a timeout has been specified
@ -302,19 +353,26 @@ template <typename Action, typename... SignalInfos> void waitForSignals(Action a
} }
// exec event loop as long as the right signal has not been emitted yet and there is still time // 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 { do {
loop.exec(); 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 #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(), 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())); " ms.", timeoutFactor != 1.0 ? argsToString(" (original timeout: ", timeout, " ms)") : std::string()));
} }
#endif #endif
return !failureEmitted && !timeoutFailed;
} }
} // namespace TestUtilities } // namespace TestUtilities
#endif // SYNCTHINGTESTHELPER_H #endif // SYNCTHINGTESTHELPER_H