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;
}
void Application::waitForConnected(int timeout)
bool Application::waitForConnected(int timeout)
{
using namespace TestUtilities;
bool 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),
ref(m_settings)),
timeout, signalInfo(&m_connection, &SyncthingConnection::statusChanged, checkStatus, &isConnected));
return waitForSignalsOrFail(bind(static_cast<void (SyncthingConnection::*)(SyncthingConnectionSettings &)>(&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)

View File

@ -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<RelevantDir> m_relevantDirs;
std::vector<const Data::SyncthingDev *> m_relevantDevs;
RelevantDir m_pwd;
QByteArray m_dirCompletion;
QByteArray m_devCompletion;
int m_idleDuration;
int m_idleTimeout;
bool m_argsRead;

View File

@ -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:

View File

@ -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<SyncthingDir> &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();

View File

@ -103,6 +103,17 @@ private:
*/
template <typename Signal, typename Handler> 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 <typename Signal, typename Handler>
inline SignalInfo<Signal, Handler> signalInfo(typename QtPrivate::FunctionPointer<Signal>::Object *sender, Signal signal,
const Handler &handler = Handler(), bool *correctSignalEmitted = nullptr)
inline auto signalInfo(typename QtPrivate::FunctionPointer<Signal>::Object *sender, Signal signal, const Handler &handler = Handler(),
bool *correctSignalEmitted = nullptr)
{
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().
*/
@ -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 <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
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 <typename Action, typename... SignalInfos> 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 <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
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