diff options
author | Marco Bubke <[email protected]> | 2016-08-17 15:29:37 +0200 |
---|---|---|
committer | Marco Bubke <[email protected]> | 2017-11-28 15:08:58 +0000 |
commit | f70bf3d2d16be3f4a7812bd0352da70fb7e0fa3b (patch) | |
tree | b22b54b5208093bed790f2b8b9d3728a7389b2f4 | |
parent | 45c667d52cffdd4bfd01f89e9794aef6f5f7298b (diff) |
Clang: Move QLocalServer in ConnectionClient
Before the QLocalServer was in the ConnectionServer so more than one
client could connect to the server. But we never used that possibility
which made the hand shaking much more difficult. It is now moved
in the client, so that there is always a QLocalServer.
Change-Id: Ifa357074b0c0809434c49d23b1cee38496f72f43
Reviewed-by: Ivan Donchevskii <[email protected]>
Reviewed-by: Nikolai Kosjar <[email protected]>
44 files changed, 1162 insertions, 343 deletions
diff --git a/src/libs/clangsupport/baseserverproxy.cpp b/src/libs/clangsupport/baseserverproxy.cpp new file mode 100644 index 00000000000..2c9f3071fd0 --- /dev/null +++ b/src/libs/clangsupport/baseserverproxy.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "baseserverproxy.h" +#include "messageenvelop.h" + +#include <QIODevice> + +namespace ClangBackEnd { + +BaseServerProxy::BaseServerProxy(IpcClientInterface *client, QIODevice *ioDevice) + : m_writeMessageBlock(ioDevice), + m_readMessageBlock(ioDevice), + m_client(client) +{ + if (ioDevice) + QObject::connect(ioDevice, &QIODevice::readyRead, [this] () { BaseServerProxy::readMessages(); }); +} + +void BaseServerProxy::readMessages() +{ + for (const auto &message : m_readMessageBlock.readAll()) + m_client->dispatch(message); +} + +void BaseServerProxy::resetCounter() +{ + m_writeMessageBlock.resetCounter(); + m_readMessageBlock.resetCounter(); +} + +void BaseServerProxy::setIoDevice(QIODevice *ioDevice) +{ + QObject::connect(ioDevice, &QIODevice::readyRead, [this] () { BaseServerProxy::readMessages(); }); + m_writeMessageBlock.setIoDevice(ioDevice); + m_readMessageBlock.setIoDevice(ioDevice); +} + +} // namespace ClangBackEnd diff --git a/src/libs/clangsupport/baseserverproxy.h b/src/libs/clangsupport/baseserverproxy.h new file mode 100644 index 00000000000..2211f9da256 --- /dev/null +++ b/src/libs/clangsupport/baseserverproxy.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "ipcclientinterface.h" +#include "readmessageblock.h" +#include "writemessageblock.h" + +namespace ClangBackEnd { + +class CLANGSUPPORT_EXPORT BaseServerProxy +{ + BaseServerProxy(const BaseServerProxy&) = delete; + BaseServerProxy &operator=(const BaseServerProxy&) = delete; + +public: + BaseServerProxy(IpcClientInterface *client, QIODevice *ioDevice); + + void readMessages(); + + void resetCounter(); + + void setIoDevice(QIODevice *ioDevice); + +protected: + ClangBackEnd::WriteMessageBlock m_writeMessageBlock; + ClangBackEnd::ReadMessageBlock m_readMessageBlock; + IpcClientInterface *m_client; +}; + +} // namespace ClangBackEnd diff --git a/src/libs/clangsupport/clangcodemodelconnectionclient.cpp b/src/libs/clangsupport/clangcodemodelconnectionclient.cpp index 254f650f227..88421eb6139 100644 --- a/src/libs/clangsupport/clangcodemodelconnectionclient.cpp +++ b/src/libs/clangsupport/clangcodemodelconnectionclient.cpp @@ -25,6 +25,8 @@ #include "clangcodemodelconnectionclient.h" +#include <utils/temporarydirectory.h> + #include <QCoreApplication> namespace ClangBackEnd { @@ -40,8 +42,14 @@ QString currentProcessId() ClangCodeModelConnectionClient::ClangCodeModelConnectionClient( ClangCodeModelClientInterface *client) - : serverProxy_(client, ioDevice()) + : ConnectionClient(Utils::TemporaryDirectory::masterDirectoryPath() + + QStringLiteral("/ClangBackEnd-") + + currentProcessId()), + m_serverProxy(client, nullptr), + m_client(client) { + m_processCreator.setTemporaryDirectoryPattern("clangbackend-XXXXXX"); + stdErrPrefixer().setPrefix("clangbackend.stderr: "); stdOutPrefixer().setPrefix("clangbackend.stdout: "); } @@ -53,27 +61,27 @@ ClangCodeModelConnectionClient::~ClangCodeModelConnectionClient() ClangCodeModelServerProxy &ClangCodeModelConnectionClient::serverProxy() { - return serverProxy_; + return m_serverProxy; } void ClangCodeModelConnectionClient::sendEndCommand() { - serverProxy_.end(); + m_serverProxy.end(); } void ClangCodeModelConnectionClient::resetCounter() { - serverProxy_.resetCounter(); + m_serverProxy.resetCounter(); } -QString ClangCodeModelConnectionClient::connectionName() const +QString ClangCodeModelConnectionClient::outputName() const { - return temporaryDirectory().path() + QStringLiteral("/ClangBackEnd-") + currentProcessId(); + return QStringLiteral("ClangCodeModelConnectionClient"); } -QString ClangCodeModelConnectionClient::outputName() const +void ClangCodeModelConnectionClient::newConnectedServer(QIODevice *ioDevice) { - return QStringLiteral("ClangCodeModelConnectionClient"); + m_serverProxy.setIoDevice(ioDevice); } } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/clangcodemodelconnectionclient.h b/src/libs/clangsupport/clangcodemodelconnectionclient.h index b9fdbdffc76..c5a4d37f38b 100644 --- a/src/libs/clangsupport/clangcodemodelconnectionclient.h +++ b/src/libs/clangsupport/clangcodemodelconnectionclient.h @@ -35,17 +35,17 @@ public: ClangCodeModelConnectionClient(ClangCodeModelClientInterface *client); ~ClangCodeModelConnectionClient(); - ClangCodeModelServerProxy &serverProxy(); protected: void sendEndCommand() override; void resetCounter() override; - QString connectionName() const override; QString outputName() const override; + void newConnectedServer(QIODevice *ioDevice) override; private: - ClangCodeModelServerProxy serverProxy_; + ClangCodeModelServerProxy m_serverProxy; + ClangCodeModelClientInterface *m_client; }; } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/clangcodemodelserverproxy.cpp b/src/libs/clangsupport/clangcodemodelserverproxy.cpp index a5b6411e752..f51bcb2802b 100644 --- a/src/libs/clangsupport/clangcodemodelserverproxy.cpp +++ b/src/libs/clangsupport/clangcodemodelserverproxy.cpp @@ -29,30 +29,11 @@ #include <clangcodemodelservermessages.h> #include <messageenvelop.h> -#include <QLocalServer> -#include <QLocalSocket> -#include <QProcess> - namespace ClangBackEnd { ClangCodeModelServerProxy::ClangCodeModelServerProxy(ClangCodeModelClientInterface *client, QIODevice *ioDevice) - : m_writeMessageBlock(ioDevice), - m_readMessageBlock(ioDevice), - m_client(client) -{ - QObject::connect(ioDevice, &QIODevice::readyRead, [this] () {ClangCodeModelServerProxy::readMessages();}); -} - -void ClangCodeModelServerProxy::readMessages() -{ - for (const auto &message : m_readMessageBlock.readAll()) - m_client->dispatch(message); -} - -void ClangCodeModelServerProxy::resetCounter() + : BaseServerProxy(client, ioDevice) { - m_writeMessageBlock.resetCounter(); - m_readMessageBlock.resetCounter(); } void ClangCodeModelServerProxy::end() diff --git a/src/libs/clangsupport/clangcodemodelserverproxy.h b/src/libs/clangsupport/clangcodemodelserverproxy.h index 88badf55509..e93e05d3f54 100644 --- a/src/libs/clangsupport/clangcodemodelserverproxy.h +++ b/src/libs/clangsupport/clangcodemodelserverproxy.h @@ -24,9 +24,9 @@ ****************************************************************************/ #pragma once + +#include "baseserverproxy.h" #include "clangcodemodelserverinterface.h" -#include "readmessageblock.h" -#include "writemessageblock.h" #include <QtGlobal> #include <QTimer> @@ -42,12 +42,11 @@ QT_END_NAMESPACE namespace ClangBackEnd { -class CLANGSUPPORT_EXPORT ClangCodeModelServerProxy : public ClangCodeModelServerInterface +class CLANGSUPPORT_EXPORT ClangCodeModelServerProxy : public BaseServerProxy, + public ClangCodeModelServerInterface { public: ClangCodeModelServerProxy(ClangCodeModelClientInterface *client, QIODevice *ioDevice); - ClangCodeModelServerProxy(const ClangCodeModelServerProxy&) = delete; - ClangCodeModelServerProxy &operator=(const ClangCodeModelServerProxy&) = delete; void end() override; void registerTranslationUnitsForEditor(const RegisterTranslationUnitForEditorMessage &message) override; @@ -62,15 +61,6 @@ public: void requestReferences(const RequestReferencesMessage &message) override; void requestFollowSymbol(const RequestFollowSymbolMessage &message) override; void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override; - - void readMessages(); - - void resetCounter(); - -private: - ClangBackEnd::WriteMessageBlock m_writeMessageBlock; - ClangBackEnd::ReadMessageBlock m_readMessageBlock; - ClangCodeModelClientInterface *m_client; }; } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/clangsupport-lib.pri b/src/libs/clangsupport/clangsupport-lib.pri index 2e6130eeddc..65d3ec6946c 100644 --- a/src/libs/clangsupport/clangsupport-lib.pri +++ b/src/libs/clangsupport/clangsupport-lib.pri @@ -74,6 +74,9 @@ SOURCES += \ $$PWD/sourcelocationscontainer.cpp \ $$PWD/sourcelocationsforrenamingmessage.cpp \ $$PWD/sourcerangecontainer.cpp \ + $$PWD/processcreator.cpp \ + $$PWD/processexception.cpp \ + $$PWD/processstartedevent.cpp \ $$PWD/sourcerangecontainerv2.cpp \ $$PWD/sourcerangesanddiagnosticsforquerymessage.cpp \ $$PWD/sourcerangescontainer.cpp \ @@ -85,7 +88,8 @@ SOURCES += \ $$PWD/updatevisibletranslationunitsmessage.cpp \ $$PWD/writemessageblock.cpp \ $$PWD/filepathcaching.cpp \ - $$PWD/filepathid.cpp + $$PWD/filepathid.cpp \ + $$PWD/baseserverproxy.cpp HEADERS += \ $$PWD/cancelmessage.h \ @@ -161,6 +165,11 @@ HEADERS += \ $$PWD/sourcelocationscontainer.h \ $$PWD/sourcelocationsforrenamingmessage.h \ $$PWD/sourcerangecontainer.h \ + $$PWD/filepath.h \ + $$PWD/processcreator.h \ + $$PWD/processexception.h \ + $$PWD/processhandle.h \ + $$PWD/processstartedevent.h \ $$PWD/sourcerangecontainerv2.h \ $$PWD/sourcerangesanddiagnosticsforquerymessage.h \ $$PWD/sourcerangescontainer.h \ @@ -187,6 +196,7 @@ HEADERS += \ $$PWD/filepathcachinginterface.h \ $$PWD/filepathcaching.h \ $$PWD/filepathcachingfwd.h \ + $$PWD/baseserverproxy.h \ $$PWD/nativefilepathview.h \ $$PWD/filepath.h \ $$PWD/nativefilepath.h \ diff --git a/src/libs/clangsupport/connectionclient.cpp b/src/libs/clangsupport/connectionclient.cpp index e53fac18102..92028bc7bec 100644 --- a/src/libs/clangsupport/connectionclient.cpp +++ b/src/libs/clangsupport/connectionclient.cpp @@ -26,6 +26,10 @@ #include "connectionclient.h" #include "clangsupportdebugutils.h" +#include "processstartedevent.h" +#include "processexception.h" + +#include <utils/hostosinfo.h> #include <QCoreApplication> #include <QMetaMethod> @@ -34,50 +38,52 @@ namespace ClangBackEnd { -ConnectionClient::ConnectionClient() +ConnectionClient::ConnectionClient(const QString &connectionName) + : m_connectionName(connectionName) { + m_processCreator.setArguments({connectionName}); + m_processCreator.setObserver(this); + + listenForConnections(); + m_processAliveTimer.setInterval(10000); - resetTemporaryDir(); + resetTemporaryDirectory(); static const bool startAliveTimer = !qEnvironmentVariableIntValue("QTC_CLANG_NO_ALIVE_TIMER"); if (startAliveTimer) connectAliveTimer(); - connectLocalSocketError(); - connectLocalSocketConnected(); - connectLocalSocketDisconnected(); + connectNewConnection(); } -void ConnectionClient::startProcessAndConnectToServerAsynchronously() +ConnectionClient::~ConnectionClient() { - m_process = startProcess(); + QLocalServer::removeServer(connectionName()); } -bool ConnectionClient::disconnectFromServer() +void ConnectionClient::startProcessAndConnectToServerAsynchronously() { - localSocket.disconnectFromServer(); - if (localSocket.state() != QLocalSocket::UnconnectedState) - return localSocket.waitForDisconnected(); + m_processIsStarting = true; - return true; + m_processFuture = m_processCreator.createProcess(); } bool ConnectionClient::isConnected() const { - return localSocket.state() == QLocalSocket::ConnectedState; + return m_localSocket && m_localSocket->state() == QLocalSocket::ConnectedState; } void ConnectionClient::ensureMessageIsWritten() { - while (isConnected() && localSocket.bytesToWrite() > 0) - localSocket.waitForBytesWritten(50); + while (isConnected() && m_localSocket->bytesToWrite() > 0) + m_localSocket->waitForBytesWritten(50); } void ConnectionClient::sendEndMessage() { sendEndCommand(); - localSocket.flush(); + m_localSocket->flush(); ensureMessageIsWritten(); } @@ -108,7 +114,7 @@ QProcessEnvironment ConnectionClient::processEnvironment() const const QTemporaryDir &ConnectionClient::temporaryDirectory() const { - return *m_temporaryDirectory; + return m_processCreator.temporaryDirectory(); } LinePrefixer &ConnectionClient::stdErrPrefixer() @@ -121,29 +127,31 @@ LinePrefixer &ConnectionClient::stdOutPrefixer() return m_stdOutPrefixer; } -std::unique_ptr<QProcess> ConnectionClient::startProcess() +QString ConnectionClient::connectionName() const { - m_processIsStarting = true; + return m_connectionName; +} - auto process = std::unique_ptr<QProcess>(new QProcess); - connectProcessFinished(process.get()); - connectProcessStarted(process.get()); - connectStandardOutputAndError(process.get()); - process->setProcessEnvironment(processEnvironment()); - process->start(processPath(), {connectionName()}); - resetProcessAliveTimer(); +bool ConnectionClient::event(QEvent *event) +{ + if (event->type() == int(ProcessStartedEvent::ProcessStarted)) { + getProcessFromFuture(); + + return true; + }; - return process; + return false; } void ConnectionClient::restartProcessAsynchronously() { - if (!m_processIsStarting) { - finishProcess(std::move(m_process)); - resetTemporaryDir(); // clear left-over preambles + getProcessFromFuture(); + + finishProcess(std::move(m_process)); + resetTemporaryDirectory(); // clear left-over preambles + + startProcessAndConnectToServerAsynchronously(); - startProcessAndConnectToServerAsynchronously(); - } } void ConnectionClient::restartProcessIfTimerIsNotResettedAndSocketIsEmpty() @@ -153,23 +161,15 @@ void ConnectionClient::restartProcessIfTimerIsNotResettedAndSocketIsEmpty() return; // Already reset, but we were scheduled after. } - if (localSocket.bytesAvailable() > 0) + if (!m_localSocket || m_localSocket->bytesAvailable() > 0) return; // We come first, the incoming data was not yet processed. restartProcessAsynchronously(); } -void ConnectionClient::connectToLocalSocket() -{ - if (!isConnected()) { - localSocket.connectToServer(connectionName()); - QTimer::singleShot(20, this, &ConnectionClient::connectToLocalSocket); - } -} - void ConnectionClient::endProcess(QProcess *process) { - if (isProcessIsRunning() && isConnected()) { + if (isProcessRunning(process) && isConnected()) { sendEndMessage(); process->waitForFinished(); } @@ -177,32 +177,33 @@ void ConnectionClient::endProcess(QProcess *process) void ConnectionClient::terminateProcess(QProcess *process) { - Q_UNUSED(process) -#ifndef Q_OS_WIN32 - if (isProcessIsRunning()) { + if (!Utils::HostOsInfo::isWindowsHost() && isProcessRunning()) { process->terminate(); - process->waitForFinished(); + process->waitForFinished(1000); } -#endif } void ConnectionClient::killProcess(QProcess *process) { - if (isProcessIsRunning()) { + if (isProcessRunning(process)) { process->kill(); - process->waitForFinished(); + process->waitForFinished(1000); } } -void ConnectionClient::resetProcessIsStarting() +void ConnectionClient::finishConnection() { - m_processIsStarting = false; + if (m_localSocket) { + if (m_localSocket->state() != QLocalSocket::UnconnectedState) + m_localSocket->disconnectFromServer(); + m_localSocket = nullptr; + } } void ConnectionClient::printLocalSocketError(QLocalSocket::LocalSocketError socketError) { - if (socketError != QLocalSocket::ServerNotFoundError) - qWarning() << outputName() << "LocalSocket Error:" << localSocket.errorString(); + if (m_localSocket && socketError != QLocalSocket::ServerNotFoundError) + qWarning() << outputName() << "LocalSocket Error:" << m_localSocket->errorString(); } void ConnectionClient::printStandardOutput() @@ -215,55 +216,74 @@ void ConnectionClient::printStandardError() qDebug("%s", m_stdErrPrefixer.prefix(m_process->readAllStandardError()).constData()); } -void ConnectionClient::resetTemporaryDir() +void ConnectionClient::resetTemporaryDirectory() { - m_temporaryDirectory = std::make_unique<Utils::TemporaryDirectory>("clang-XXXXXX"); + m_processCreator.resetTemporaryDirectory(); } -void ConnectionClient::connectLocalSocketConnected() +void ConnectionClient::initializeProcess(QProcess *process) { - connect(&localSocket, - &QLocalSocket::connected, - this, - &ConnectionClient::connectedToLocalSocket); + connectStandardOutputAndError(process); - connect(&localSocket, - &QLocalSocket::connected, - this, - &ConnectionClient::resetProcessIsStarting); + resetProcessAliveTimer(); } void ConnectionClient::connectLocalSocketDisconnected() { - connect(&localSocket, + connect(m_localSocket, &QLocalSocket::disconnected, this, &ConnectionClient::disconnectedFromLocalSocket); + connect(m_localSocket, + &QLocalSocket::disconnected, + this, + &ConnectionClient::restartProcessAsynchronously); +} + +void ConnectionClient::disconnectLocalSocketDisconnected() +{ + if (m_localSocket) { + disconnect(m_localSocket, + &QLocalSocket::disconnected, + this, + &ConnectionClient::restartProcessAsynchronously); + } } void ConnectionClient::finishProcess() { finishProcess(std::move(m_process)); + emit processFinished(); } -void ConnectionClient::finishProcess(std::unique_ptr<QProcess> &&process) +bool ConnectionClient::isProcessRunning() { + getProcessFromFuture(); + + return isProcessRunning(m_process.get()); +} + +void ConnectionClient::finishProcess(QProcessUniquePointer &&process) +{ + disconnectLocalSocketDisconnected(); + if (process) { m_processAliveTimer.stop(); - disconnectProcessFinished(process.get()); endProcess(process.get()); - disconnectFromServer(); + finishConnection(); terminateProcess(process.get()); killProcess(process.get()); resetCounter(); + } else { + finishConnection(); } } bool ConnectionClient::waitForEcho() { - return localSocket.waitForReadyRead(); + return m_localSocket->waitForReadyRead(); } bool ConnectionClient::waitForConnected() @@ -271,7 +291,7 @@ bool ConnectionClient::waitForConnected() bool isConnected = false; for (int counter = 0; counter < 100; counter++) { - isConnected = localSocket.waitForConnected(20); + isConnected = m_localSocket && m_localSocket->waitForConnected(20); if (isConnected) return isConnected; else { @@ -280,52 +300,28 @@ bool ConnectionClient::waitForConnected() } } - qWarning() << outputName() << "cannot connect:" << localSocket.errorString(); + if (m_localSocket) + qWarning() << outputName() << "cannot connect:" << m_localSocket->errorString(); return isConnected; } -QProcess *ConnectionClient::processForTestOnly() const +QProcess *ConnectionClient::processForTestOnly() { + getProcessFromFuture(); + return m_process.get(); } QIODevice *ConnectionClient::ioDevice() { - return &localSocket; -} - -bool ConnectionClient::isProcessIsRunning() const -{ - return m_process && m_process->state() == QProcess::Running; + return m_localSocket; } -void ConnectionClient::connectProcessFinished(QProcess *process) const +bool ConnectionClient::isProcessRunning(QProcess *process) { - connect(process, - static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), - this, - &ConnectionClient::restartProcessAsynchronously); - -} - -void ConnectionClient::connectProcessStarted(QProcess *process) const -{ - connect(process, - &QProcess::started, - this, - &ConnectionClient::connectToLocalSocket); -} - -void ConnectionClient::disconnectProcessFinished(QProcess *process) const -{ - if (process) { - disconnect(process, - static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), - this, - &ConnectionClient::restartProcessAsynchronously); - } + return process && process->state() == QProcess::Running; } void ConnectionClient::connectStandardOutputAndError(QProcess *process) const @@ -336,7 +332,7 @@ void ConnectionClient::connectStandardOutputAndError(QProcess *process) const void ConnectionClient::connectLocalSocketError() const { - connect(&localSocket, + connect(m_localSocket, static_cast<void (QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error), this, &ConnectionClient::printLocalSocketError); @@ -350,14 +346,52 @@ void ConnectionClient::connectAliveTimer() &ConnectionClient::restartProcessIfTimerIsNotResettedAndSocketIsEmpty); } -const QString &ConnectionClient::processPath() const +void ConnectionClient::connectNewConnection() { - return m_processPath; + QObject::connect(&m_localServer, + &QLocalServer::newConnection, + this, + &ConnectionClient::handleNewConnection); +} + +void ConnectionClient::handleNewConnection() +{ + m_localSocket = m_localServer.nextPendingConnection(); + + connectLocalSocketError(); + connectLocalSocketDisconnected(); + + newConnectedServer(m_localSocket); + + emit connectedToLocalSocket(); +} + +void ConnectionClient::getProcessFromFuture() +{ + try { + if (m_processFuture.valid()) { + m_process = m_processFuture.get(); + m_processIsStarting = false; + + initializeProcess(m_process.get()); + } + } catch (const ProcessException &processExeption) { + qWarning() << "Clang backend process is not working." + << QLatin1String(processExeption.what()); + } +} + +void ConnectionClient::listenForConnections() +{ + bool isListing = m_localServer.listen(connectionName()); + + if (!isListing) + qWarning() << "ConnectionClient: QLocalServer is not listing for connections!"; } void ConnectionClient::setProcessPath(const QString &processPath) { - m_processPath = processPath; + m_processCreator.setProcessPath(processPath); } } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/connectionclient.h b/src/libs/clangsupport/connectionclient.h index cfa7a67b2f2..6cd79956f5f 100644 --- a/src/libs/clangsupport/connectionclient.h +++ b/src/libs/clangsupport/connectionclient.h @@ -27,14 +27,15 @@ #include "clangcodemodelserverproxy.h" #include "lineprefixer.h" +#include "processcreator.h" -#include <utils/temporarydirectory.h> - +#include <QLocalServer> #include <QLocalSocket> #include <QProcessEnvironment> #include <QScopedPointer> #include <QTemporaryDir> +#include <future> #include <memory> QT_BEGIN_NAMESPACE @@ -53,10 +54,10 @@ class CLANGSUPPORT_EXPORT ConnectionClient : public QObject Q_OBJECT public: - ConnectionClient(); + ConnectionClient(const QString &connectionName); + virtual ~ConnectionClient(); void startProcessAndConnectToServerAsynchronously(); - bool disconnectFromServer(); bool isConnected() const; void sendEndMessage(); @@ -64,18 +65,17 @@ public: void resetProcessAliveTimer(); void setProcessAliveTimerInterval(int processTimerInterval); - const QString &processPath() const; void setProcessPath(const QString &processPath); void restartProcessAsynchronously(); void restartProcessIfTimerIsNotResettedAndSocketIsEmpty(); void finishProcess(); - bool isProcessIsRunning() const; + bool isProcessRunning(); bool waitForEcho(); bool waitForConnected(); - QProcess *processForTestOnly() const; + QProcess *processForTestOnly(); signals: void connectedToLocalSocket(); @@ -90,48 +90,55 @@ protected: virtual void sendEndCommand() = 0; virtual void resetCounter() = 0; - virtual QString connectionName() const = 0; virtual QString outputName() const = 0; + QString connectionName() const; + bool event(QEvent* event); + + virtual void newConnectedServer(QIODevice *ioDevice) = 0; + private: - std::unique_ptr<QProcess> startProcess(); - void finishProcess(std::unique_ptr<QProcess> &&process); - void connectToLocalSocket(); + static bool isProcessRunning(QProcess *process); + void finishProcess(QProcessUniquePointer &&process); void endProcess(QProcess *process); void terminateProcess(QProcess *process); void killProcess(QProcess *process); - void resetProcessIsStarting(); + void finishConnection(); void printLocalSocketError(QLocalSocket::LocalSocketError socketError); void printStandardOutput(); void printStandardError(); + void initializeProcess(QProcess *process); - void resetTemporaryDir(); + void resetTemporaryDirectory(); - void connectLocalSocketConnected(); void connectLocalSocketDisconnected(); - void connectProcessFinished(QProcess *process) const; - void connectProcessStarted(QProcess *process) const; - void disconnectProcessFinished(QProcess *process) const; + void disconnectLocalSocketDisconnected(); void connectStandardOutputAndError(QProcess *process) const; void connectLocalSocketError() const; void connectAliveTimer(); + void connectNewConnection(); + void handleNewConnection(); + void getProcessFromFuture(); + void listenForConnections(); void ensureMessageIsWritten(); QProcessEnvironment processEnvironment() const; +protected: + ProcessCreator m_processCreator; + private: LinePrefixer m_stdErrPrefixer; LinePrefixer m_stdOutPrefixer; - - mutable std::unique_ptr<QProcess> m_process; - QLocalSocket localSocket; - std::unique_ptr<Utils::TemporaryDirectory> m_temporaryDirectory; + QLocalServer m_localServer; + std::future<QProcessUniquePointer> m_processFuture; + mutable QProcessUniquePointer m_process; + QLocalSocket *m_localSocket = nullptr; QTimer m_processAliveTimer; - QString m_processPath; + QString m_connectionName; bool m_isAliveTimerResetted = false; bool m_processIsStarting = false; - }; } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/connectionserver.cpp b/src/libs/clangsupport/connectionserver.cpp index 6828041de81..ae6a3a11360 100644 --- a/src/libs/clangsupport/connectionserver.cpp +++ b/src/libs/clangsupport/connectionserver.cpp @@ -27,7 +27,5 @@ namespace ClangBackEnd { -QString ConnectionName::connectionName; - } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/connectionserver.h b/src/libs/clangsupport/connectionserver.h index 715a2b9caa7..ecc0c4f2d72 100644 --- a/src/libs/clangsupport/connectionserver.h +++ b/src/libs/clangsupport/connectionserver.h @@ -31,6 +31,7 @@ #include <QLocalServer> #include <QLocalSocket> #include <QTimer> +#include <QDebug> #include <cstdlib> #include <memory> @@ -41,46 +42,28 @@ namespace ClangBackEnd { class ClangCodeModelServerInterface; class ClangCodeModelClientProxy; -struct CLANGSUPPORT_EXPORT ConnectionName { - static QString connectionName; -}; - template <typename ServerInterface, typename ClientProxy> class ConnectionServer { public: - ConnectionServer(const QString &connectionName) + ConnectionServer() { - ConnectionName::connectionName = connectionName; - m_aliveTimer.start(5000); - m_localServer.setMaxPendingConnections(1); - - QObject::connect(&m_localServer, - &QLocalServer::newConnection, - [&] { handleNewConnection(); }); - QObject::connect(&m_aliveTimer, - &QTimer::timeout, - [&] { sendAliveMessage(); }); - - std::atexit(&ConnectionServer::removeServer); - #if defined(_GLIBCXX_HAVE_AT_QUICK_EXIT) - std::at_quick_exit(&ConnectionServer::removeServer); - #endif - std::set_terminate(&ConnectionServer::removeServer); + connectAliveTimer(); + connectLocalSocketDisconnet(); } ~ConnectionServer() { - removeServer(); + if (m_localSocket.state() != QLocalSocket::UnconnectedState) + m_localSocket.disconnectFromServer(); } - void start() + void start(const QString &connectionName) { - QLocalServer::removeServer(ConnectionName::connectionName); - m_localServer.listen(ConnectionName::connectionName); + connectToLocalServer(connectionName); } void setServer(ServerInterface *ipcServer) @@ -89,17 +72,17 @@ public: } - static void removeServer() - { - QLocalServer::removeServer(ConnectionName::connectionName); - } - private: - void handleNewConnection() + void connectToLocalServer(const QString &connectionName) { - m_localSocket = nextPendingConnection(); + QObject::connect(&m_localSocket, + static_cast<void (QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error), + [&] (QLocalSocket::LocalSocketError) { + qWarning() << "ConnectionServer error:" << m_localSocket.errorString() << connectionName; + }); - m_ipcClientProxy.reset(new ClientProxy(m_ipcServer, m_localSocket)); + m_localSocket.connectToServer(connectionName); + m_ipcClientProxy = std::make_unique<ClientProxy>(m_ipcServer, &m_localSocket); m_ipcServer->setClient(m_ipcClientProxy.get()); } @@ -114,40 +97,40 @@ private: { m_ipcClientProxy.reset(); - m_localSocket = nullptr; - delayedExitApplicationIfNoSockedIsConnected(); } - QLocalSocket *nextPendingConnection() + void delayedExitApplicationIfNoSockedIsConnected() { - QLocalSocket *localSocket = m_localServer.nextPendingConnection(); - - QObject::connect(localSocket, - &QLocalSocket::disconnected, - [&] { handleSocketDisconnect(); }); + QTimer::singleShot(60000, [&] { exitApplicationIfNoSockedIsConnected(); }); + } - return localSocket; + void exitApplicationIfNoSockedIsConnected() + { + if (m_localSocket.state() != QLocalSocket::UnconnectedState) + m_localSocket.disconnectFromServer(); + QCoreApplication::exit(); } - void delayedExitApplicationIfNoSockedIsConnected() + void connectAliveTimer() { - if (m_localSocket == nullptr) - QTimer::singleShot(60000, [&] { exitApplicationIfNoSockedIsConnected(); }); + QObject::connect(&m_aliveTimer, + &QTimer::timeout, + [&] { sendAliveMessage(); }); } - void exitApplicationIfNoSockedIsConnected() + void connectLocalSocketDisconnet() { - if (m_localSocket == nullptr) - QCoreApplication::exit(); + QObject::connect(&m_localSocket, + &QLocalSocket::disconnected, + [&] { handleSocketDisconnect(); }); } private: + QLocalSocket m_localSocket; + QTimer m_aliveTimer; std::unique_ptr<ClientProxy> m_ipcClientProxy; - QLocalSocket* m_localSocket; ServerInterface *m_ipcServer; - QLocalServer m_localServer; - QTimer m_aliveTimer; }; } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/pchmanagerserverproxy.cpp b/src/libs/clangsupport/pchmanagerserverproxy.cpp index d513fddc87d..3a181fd3dc3 100644 --- a/src/libs/clangsupport/pchmanagerserverproxy.cpp +++ b/src/libs/clangsupport/pchmanagerserverproxy.cpp @@ -37,38 +37,23 @@ namespace ClangBackEnd { PchManagerServerProxy::PchManagerServerProxy(PchManagerClientInterface *client, QIODevice *ioDevice) - : writeMessageBlock(ioDevice), - readMessageBlock(ioDevice), - client(client) + : BaseServerProxy(client, ioDevice) { - QObject::connect(ioDevice, &QIODevice::readyRead, [this] () { readMessages(); }); } void PchManagerServerProxy::end() { - writeMessageBlock.write(EndMessage()); + m_writeMessageBlock.write(EndMessage()); } void PchManagerServerProxy::updatePchProjectParts(UpdatePchProjectPartsMessage &&message) { - writeMessageBlock.write(message); + m_writeMessageBlock.write(message); } void PchManagerServerProxy::removePchProjectParts(RemovePchProjectPartsMessage &&message) { - writeMessageBlock.write(message); -} - -void PchManagerServerProxy::readMessages() -{ - for (const auto &message : readMessageBlock.readAll()) - client->dispatch(message); -} - -void PchManagerServerProxy::resetCounter() -{ - writeMessageBlock.resetCounter(); - readMessageBlock.resetCounter(); + m_writeMessageBlock.write(message); } } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/pchmanagerserverproxy.h b/src/libs/clangsupport/pchmanagerserverproxy.h index 300d439278f..8f9164f9d5a 100644 --- a/src/libs/clangsupport/pchmanagerserverproxy.h +++ b/src/libs/clangsupport/pchmanagerserverproxy.h @@ -25,6 +25,7 @@ #pragma once +#include "baseserverproxy.h" #include "clangsupport_global.h" #include "pchmanagerserverinterface.h" #include "readmessageblock.h" @@ -42,25 +43,15 @@ namespace ClangBackEnd { class PchManagerClientInterface; -class CLANGSUPPORT_EXPORT PchManagerServerProxy final : public PchManagerServerInterface +class CLANGSUPPORT_EXPORT PchManagerServerProxy final : public BaseServerProxy, + public PchManagerServerInterface { public: explicit PchManagerServerProxy(PchManagerClientInterface *client, QIODevice *ioDevice); - PchManagerServerProxy(const PchManagerServerProxy&) = delete; - const PchManagerServerProxy &operator=(const PchManagerServerProxy&) = delete; void end() override; void updatePchProjectParts(UpdatePchProjectPartsMessage &&message) override; void removePchProjectParts(RemovePchProjectPartsMessage &&message) override; - - void readMessages(); - - void resetCounter(); - -private: - ClangBackEnd::WriteMessageBlock writeMessageBlock; - ClangBackEnd::ReadMessageBlock readMessageBlock; - PchManagerClientInterface *client = nullptr; }; } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/processcreator.cpp b/src/libs/clangsupport/processcreator.cpp new file mode 100644 index 00000000000..9b21ba298e3 --- /dev/null +++ b/src/libs/clangsupport/processcreator.cpp @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "processcreator.h" + +#include "processexception.h" +#include "processstartedevent.h" + +#include <QCoreApplication> +#include <QFileInfo> +#include <QTemporaryDir> + +namespace ClangBackEnd { + +using namespace std::chrono_literals; + +ProcessCreator::ProcessCreator() +{ +} + +void ProcessCreator::setTemporaryDirectoryPattern(const QString &temporaryDirectoryPattern) +{ + m_temporaryDirectoryPattern = temporaryDirectoryPattern; + resetTemporaryDirectory(); +} + +void ProcessCreator::setProcessPath(const QString &processPath) +{ + m_processPath = processPath; +} + +void ProcessCreator::setArguments(const QStringList &arguments) +{ + m_arguments = arguments; +} + +std::future<QProcessUniquePointer> ProcessCreator::createProcess() const +{ + return std::async(std::launch::async, [&] { + checkIfProcessPathExists(); + auto process = QProcessUniquePointer(new QProcess); + process->setProcessChannelMode(QProcess::QProcess::ForwardedChannels); + process->setProcessEnvironment(processEnvironment()); + process->start(m_processPath, m_arguments); + process->waitForStarted(5000); + + checkIfProcessWasStartingSuccessful(process.get()); + + postProcessStartedEvent(); + + process->moveToThread(QCoreApplication::instance()->thread()); + + return process; + }); +} + +void ProcessCreator::setObserver(QObject *observer) +{ + this->m_observer = observer; +} + +void ProcessCreator::checkIfProcessPathExists() const +{ + if (!QFileInfo::exists(m_processPath)) { + const QString messageTemplate = QCoreApplication::translate("ProcessCreator", + "Executable does not exists: %1"); + throwProcessException(messageTemplate.arg(m_processPath)); + } +} + +void ProcessCreator::checkIfProcessWasStartingSuccessful(QProcess *process) const +{ + if (process->exitStatus() == QProcess::CrashExit || process->exitCode() != 0) + dispatchProcessError(process); +} + +void ProcessCreator::dispatchProcessError(QProcess *process) const +{ + switch (process->error()) { + case QProcess::UnknownError: { + const QString message = QCoreApplication::translate("ProcessCreator", + "Unknown error happend."); + throwProcessException(message); + }; + case QProcess::Crashed: { + const QString message = QCoreApplication::translate("ProcessCreator", + "Process crashed."); + throwProcessException(message); + }; + case QProcess::FailedToStart: { + const QString message = QCoreApplication::translate("ProcessCreator", + "Process failed at startup."); + throwProcessException(message); + }; + case QProcess::Timedout: { + const QString message = QCoreApplication::translate("ProcessCreator", + "Process timeouted."); + throwProcessException(message); + }; + case QProcess::WriteError: { + const QString message = QCoreApplication::translate("ProcessCreator", + "Cannot write to process."); + throwProcessException(message); + }; + case QProcess::ReadError: { + const QString message = QCoreApplication::translate("ProcessCreator", + "Cannot read from process."); + throwProcessException(message); + }; + } + + throwProcessException("Internal impossible error!"); +} + +void ProcessCreator::postProcessStartedEvent() const +{ + if (m_observer) + QCoreApplication::postEvent(m_observer, new ProcessStartedEvent); +} + +void ProcessCreator::throwProcessException(const QString &message) const +{ + postProcessStartedEvent(); + throw ProcessException(message); +} + +const QTemporaryDir &ProcessCreator::temporaryDirectory() const +{ + return *m_temporaryDirectory.get(); +} + +void ProcessCreator::resetTemporaryDirectory() +{ + m_temporaryDirectory = std::make_unique<Utils::TemporaryDirectory>(m_temporaryDirectoryPattern); +} + +QProcessEnvironment ProcessCreator::processEnvironment() const +{ + auto processEnvironment = QProcessEnvironment::systemEnvironment(); + + if (temporaryDirectory().isValid()) { + const QString temporaryDirectoryPath = temporaryDirectory().path(); + processEnvironment.insert("TMPDIR", temporaryDirectoryPath); + processEnvironment.insert("TMP", temporaryDirectoryPath); + processEnvironment.insert("TEMP", temporaryDirectoryPath); + } + + return processEnvironment; +} + +} // namespace ClangBackEnd diff --git a/src/libs/clangsupport/processcreator.h b/src/libs/clangsupport/processcreator.h new file mode 100644 index 00000000000..97429921751 --- /dev/null +++ b/src/libs/clangsupport/processcreator.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <clangsupport_global.h> + +#include "processhandle.h" + +#include <utils/temporarydirectory.h> + +#include <QStringList> + +#include <future> +#include <memory> + +QT_BEGIN_NAMESPACE +class QTemporaryDir; +class QProcessEnvironment; +QT_END_NAMESPACE + +namespace ClangBackEnd { + +class CLANGSUPPORT_EXPORT ProcessCreator +{ +public: + ProcessCreator(); + + void setTemporaryDirectoryPattern(const QString &temporaryDirectoryPattern); + void setProcessPath(const QString &m_processPath); + void setArguments(const QStringList &m_arguments); + void setObserver(QObject *m_observer); + + std::future<QProcessUniquePointer> createProcess() const; + + const QTemporaryDir &temporaryDirectory() const; + void resetTemporaryDirectory(); + +private: + void checkIfProcessPathExists() const; + void checkIfProcessWasStartingSuccessful(QProcess *process) const; + [[noreturn]] void dispatchProcessError(QProcess *process) const; + void postProcessStartedEvent() const; + [[noreturn]] void throwProcessException(const QString &message) const; + + QProcessEnvironment processEnvironment() const; + +private: + std::unique_ptr<Utils::TemporaryDirectory> m_temporaryDirectory; + QString m_processPath; + QString m_temporaryDirectoryPattern; + QStringList m_arguments; + QObject *m_observer = nullptr; +}; + +} // namespace ClangBackEnd diff --git a/src/libs/clangsupport/processexception.cpp b/src/libs/clangsupport/processexception.cpp new file mode 100644 index 00000000000..e96367e53cf --- /dev/null +++ b/src/libs/clangsupport/processexception.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "processexception.h" + +namespace ClangBackEnd { + +ProcessException::ProcessException(Utils::SmallString &&what) + : what_(std::move(what)) +{ +} + +const char *ProcessException::what() const noexcept +{ + return what_.data(); +} + +} // namespace ClangBackEnd diff --git a/src/libs/clangsupport/processexception.h b/src/libs/clangsupport/processexception.h new file mode 100644 index 00000000000..7dbcad17bb5 --- /dev/null +++ b/src/libs/clangsupport/processexception.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <utils/smallstring.h> + +#include <exception> + +namespace ClangBackEnd { + +class ProcessException : public std::exception +{ +public: + ProcessException() = default; + ProcessException(Utils::SmallString &&what); + + const char *what() const noexcept final; + +private: + Utils::SmallString what_; +}; + +} // namespace ClangBackEnd diff --git a/src/libs/clangsupport/processhandle.h b/src/libs/clangsupport/processhandle.h new file mode 100644 index 00000000000..b2eebb29d8e --- /dev/null +++ b/src/libs/clangsupport/processhandle.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QProcess> + +#include <memory> + +namespace ClangBackEnd { + +class QProcessUniquePointerDeleter +{ +public: + void operator()(QProcess* process) + { + process->kill(); + process->waitForFinished(); + } +}; + +using QProcessUniquePointer = std::unique_ptr<QProcess, QProcessUniquePointerDeleter>; + +} // namespace ClangBackEnd diff --git a/src/libs/clangsupport/processstartedevent.cpp b/src/libs/clangsupport/processstartedevent.cpp new file mode 100644 index 00000000000..3db8c128c4d --- /dev/null +++ b/src/libs/clangsupport/processstartedevent.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "processstartedevent.h" + +namespace ClangBackEnd { + +ProcessStartedEvent::~ProcessStartedEvent() +{ +} + +} // namespace ClangBackEnd diff --git a/src/libs/clangsupport/processstartedevent.h b/src/libs/clangsupport/processstartedevent.h new file mode 100644 index 00000000000..4e02f3d8d5a --- /dev/null +++ b/src/libs/clangsupport/processstartedevent.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include <QEvent> + +namespace ClangBackEnd { + +class ProcessStartedEvent : public QEvent +{ +public: + enum Type { + ProcessStarted = QEvent::User + 3456 + }; + + ProcessStartedEvent() + : QEvent(static_cast<QEvent::Type>(ProcessStarted)) + {} + + ~ProcessStartedEvent() override; +}; + +} // namespace ClangBackEnd diff --git a/src/libs/clangsupport/readmessageblock.cpp b/src/libs/clangsupport/readmessageblock.cpp index e566093074c..9c93934eaf6 100644 --- a/src/libs/clangsupport/readmessageblock.cpp +++ b/src/libs/clangsupport/readmessageblock.cpp @@ -77,14 +77,14 @@ MessageEnvelop ReadMessageBlock::read() return message; } -QVector<MessageEnvelop> ReadMessageBlock::readAll() +std::vector<MessageEnvelop> ReadMessageBlock::readAll() { - QVector<MessageEnvelop> messages; + std::vector<MessageEnvelop> messages; while (true) { - const MessageEnvelop message = read(); + MessageEnvelop message = read(); if (message.isValid()) - messages.append(message); + messages.push_back(std::move(message)); else return messages; } @@ -97,6 +97,11 @@ void ReadMessageBlock::resetCounter() m_messageCounter = 0; } +void ReadMessageBlock::setIoDevice(QIODevice *ioDevice) +{ + m_ioDevice = ioDevice; +} + bool ReadMessageBlock::isTheWholeMessageReadable(QDataStream &in) { if (m_ioDevice->bytesAvailable() < qint64(sizeof(m_blockSize))) diff --git a/src/libs/clangsupport/readmessageblock.h b/src/libs/clangsupport/readmessageblock.h index 54c06b9fe6f..6e838e0b485 100644 --- a/src/libs/clangsupport/readmessageblock.h +++ b/src/libs/clangsupport/readmessageblock.h @@ -27,6 +27,8 @@ #include <QtGlobal> +#include <vector> + QT_BEGIN_NAMESPACE class QDataStream; class QIODevice; @@ -42,10 +44,12 @@ public: ReadMessageBlock(QIODevice *ioDevice = nullptr); MessageEnvelop read(); - QVector<MessageEnvelop> readAll(); + std::vector<MessageEnvelop> readAll(); void resetCounter(); + void setIoDevice(QIODevice *ioDevice); + private: bool isTheWholeMessageReadable(QDataStream &in); bool checkIfMessageIsLost(QDataStream &in); diff --git a/src/libs/clangsupport/refactoringserverproxy.cpp b/src/libs/clangsupport/refactoringserverproxy.cpp index cc6741b118c..47a61fbb315 100644 --- a/src/libs/clangsupport/refactoringserverproxy.cpp +++ b/src/libs/clangsupport/refactoringserverproxy.cpp @@ -35,58 +35,43 @@ namespace ClangBackEnd { RefactoringServerProxy::RefactoringServerProxy(RefactoringClientInterface *client, QIODevice *ioDevice) - : writeMessageBlock(ioDevice), - readMessageBlock(ioDevice), - client(client) + : BaseServerProxy(client, ioDevice) { - QObject::connect(ioDevice, &QIODevice::readyRead, [this] () { readMessages(); }); } void RefactoringServerProxy::end() { - writeMessageBlock.write(EndMessage()); + m_writeMessageBlock.write(EndMessage()); } void RefactoringServerProxy::requestSourceLocationsForRenamingMessage(RequestSourceLocationsForRenamingMessage &&message) { - writeMessageBlock.write(message); + m_writeMessageBlock.write(message); } void RefactoringServerProxy::requestSourceRangesAndDiagnosticsForQueryMessage(RequestSourceRangesAndDiagnosticsForQueryMessage &&message) { - writeMessageBlock.write(message); + m_writeMessageBlock.write(message); } void RefactoringServerProxy::requestSourceRangesForQueryMessage(RequestSourceRangesForQueryMessage &&message) { - writeMessageBlock.write(message); + m_writeMessageBlock.write(message); } void RefactoringServerProxy::updatePchProjectParts(UpdatePchProjectPartsMessage &&message) { - writeMessageBlock.write(message); + m_writeMessageBlock.write(message); } void RefactoringServerProxy::removePchProjectParts(RemovePchProjectPartsMessage &&message) { - writeMessageBlock.write(message); + m_writeMessageBlock.write(message); } void RefactoringServerProxy::cancel() { - writeMessageBlock.write(CancelMessage()); -} - -void RefactoringServerProxy::readMessages() -{ - for (const auto &message : readMessageBlock.readAll()) - client->dispatch(message); -} - -void RefactoringServerProxy::resetCounter() -{ - writeMessageBlock.resetCounter(); - readMessageBlock.resetCounter(); + m_writeMessageBlock.write(CancelMessage()); } } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/refactoringserverproxy.h b/src/libs/clangsupport/refactoringserverproxy.h index cfe21938495..8af6e426168 100644 --- a/src/libs/clangsupport/refactoringserverproxy.h +++ b/src/libs/clangsupport/refactoringserverproxy.h @@ -25,6 +25,7 @@ #pragma once +#include "baseserverproxy.h" #include "clangsupport_global.h" #include "refactoringserverinterface.h" #include "readmessageblock.h" @@ -42,12 +43,11 @@ namespace ClangBackEnd { class RefactoringClientInterface; -class CLANGSUPPORT_EXPORT RefactoringServerProxy final : public RefactoringServerInterface +class CLANGSUPPORT_EXPORT RefactoringServerProxy final : public BaseServerProxy, + public RefactoringServerInterface { public: explicit RefactoringServerProxy(RefactoringClientInterface *client, QIODevice *ioDevice); - RefactoringServerProxy(const RefactoringServerProxy&) = delete; - const RefactoringServerProxy &operator=(const RefactoringServerProxy&) = delete; void end() override; void requestSourceLocationsForRenamingMessage(RequestSourceLocationsForRenamingMessage &&message) override; @@ -56,15 +56,6 @@ public: void updatePchProjectParts(UpdatePchProjectPartsMessage &&message) override; void removePchProjectParts(RemovePchProjectPartsMessage &&message) override; void cancel() override; - - void readMessages(); - - void resetCounter(); - -private: - ClangBackEnd::WriteMessageBlock writeMessageBlock; - ClangBackEnd::ReadMessageBlock readMessageBlock; - RefactoringClientInterface *client = nullptr; }; } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/writemessageblock.cpp b/src/libs/clangsupport/writemessageblock.cpp index f891f83c293..fe5ef43a375 100644 --- a/src/libs/clangsupport/writemessageblock.cpp +++ b/src/libs/clangsupport/writemessageblock.cpp @@ -72,6 +72,10 @@ void WriteMessageBlock::resetCounter() m_messageCounter = 0; } +void WriteMessageBlock::setIoDevice(QIODevice *ioDevice) +{ + m_ioDevice = ioDevice; +} } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/writemessageblock.h b/src/libs/clangsupport/writemessageblock.h index edf1d74a1c7..d7b4aca204c 100644 --- a/src/libs/clangsupport/writemessageblock.h +++ b/src/libs/clangsupport/writemessageblock.h @@ -48,6 +48,8 @@ public: void resetCounter(); + void setIoDevice(QIODevice *ioDevice); + private: qint64 m_messageCounter; QIODevice *m_ioDevice; diff --git a/src/plugins/clangpchmanager/pchmanagerconnectionclient.cpp b/src/plugins/clangpchmanager/pchmanagerconnectionclient.cpp index 9c4baa40c1b..bb41391645e 100644 --- a/src/plugins/clangpchmanager/pchmanagerconnectionclient.cpp +++ b/src/plugins/clangpchmanager/pchmanagerconnectionclient.cpp @@ -25,8 +25,9 @@ #include "pchmanagerconnectionclient.h" +#include <utils/temporarydirectory.h> + #include <QCoreApplication> -#include <QTemporaryDir> namespace ClangPchManager { @@ -41,35 +42,45 @@ QString currentProcessId() ClangPchManager::PchManagerConnectionClient::PchManagerConnectionClient( ClangBackEnd::PchManagerClientInterface *client) - : serverProxy_(client, ioDevice()) + : ConnectionClient(Utils::TemporaryDirectory::masterDirectoryPath() + + QStringLiteral("/ClangPchManagerBackEnd-") + + currentProcessId()), + m_serverProxy(client, ioDevice()) { + m_processCreator.setTemporaryDirectoryPattern("clangpchmanagerbackend-XXXXXX"); + stdErrPrefixer().setPrefix("PchManagerConnectionClient.stderr: "); stdOutPrefixer().setPrefix("PchManagerConnectionClient.stdout: "); } +PchManagerConnectionClient::~PchManagerConnectionClient() +{ + finishProcess(); +} + ClangBackEnd::PchManagerServerProxy &ClangPchManager::PchManagerConnectionClient::serverProxy() { - return serverProxy_; + return m_serverProxy; } void ClangPchManager::PchManagerConnectionClient::sendEndCommand() { - serverProxy_.end(); + m_serverProxy.end(); } void PchManagerConnectionClient::resetCounter() { - serverProxy_.resetCounter(); + m_serverProxy.resetCounter(); } -QString ClangPchManager::PchManagerConnectionClient::connectionName() const +QString PchManagerConnectionClient::outputName() const { - return temporaryDirectory().path() + QStringLiteral("/ClangPchManagerBackEnd-") + currentProcessId(); + return QStringLiteral("PchManagerConnectionClient"); } -QString PchManagerConnectionClient::outputName() const +void PchManagerConnectionClient::newConnectedServer(QIODevice *ioDevice) { - return QStringLiteral("PchManagerConnectionClient"); + m_serverProxy.setIoDevice(ioDevice); } } // namespace ClangPchManager diff --git a/src/plugins/clangpchmanager/pchmanagerconnectionclient.h b/src/plugins/clangpchmanager/pchmanagerconnectionclient.h index 72c04965e91..1566b08f47c 100644 --- a/src/plugins/clangpchmanager/pchmanagerconnectionclient.h +++ b/src/plugins/clangpchmanager/pchmanagerconnectionclient.h @@ -34,17 +34,18 @@ class PchManagerConnectionClient : public ClangBackEnd::ConnectionClient { public: PchManagerConnectionClient(ClangBackEnd::PchManagerClientInterface *client); + ~PchManagerConnectionClient(); ClangBackEnd::PchManagerServerProxy &serverProxy(); protected: void sendEndCommand() override; void resetCounter() override; - QString connectionName() const override; QString outputName() const override; + void newConnectedServer(QIODevice *ioDevice) override; private: - ClangBackEnd::PchManagerServerProxy serverProxy_; + ClangBackEnd::PchManagerServerProxy m_serverProxy; }; } // namespace ClangPchManager diff --git a/src/plugins/clangrefactoring/refactoringconnectionclient.cpp b/src/plugins/clangrefactoring/refactoringconnectionclient.cpp index f1c58978d36..36d864352fa 100644 --- a/src/plugins/clangrefactoring/refactoringconnectionclient.cpp +++ b/src/plugins/clangrefactoring/refactoringconnectionclient.cpp @@ -25,6 +25,8 @@ #include "refactoringconnectionclient.h" +#include <utils/temporarydirectory.h> + #include <QCoreApplication> namespace ClangBackEnd { @@ -39,8 +41,13 @@ QString currentProcessId() } RefactoringConnectionClient::RefactoringConnectionClient(RefactoringClientInterface *client) - : serverProxy_(client, ioDevice()) + : ConnectionClient(Utils::TemporaryDirectory::masterDirectoryPath() + + QStringLiteral("/ClangRefactoringBackEnd-") + + currentProcessId()), + m_serverProxy(client, nullptr) { + m_processCreator.setTemporaryDirectoryPattern("clangrefactoringbackend-XXXXXX"); + stdErrPrefixer().setPrefix("RefactoringConnectionClient.stderr: "); stdOutPrefixer().setPrefix("RefactoringConnectionClient.stdout: "); } @@ -52,27 +59,27 @@ RefactoringConnectionClient::~RefactoringConnectionClient() RefactoringServerProxy &RefactoringConnectionClient::serverProxy() { - return serverProxy_; + return m_serverProxy; } void RefactoringConnectionClient::sendEndCommand() { - serverProxy_.end(); + m_serverProxy.end(); } void RefactoringConnectionClient::resetCounter() { - serverProxy_.resetCounter(); + m_serverProxy.resetCounter(); } -QString RefactoringConnectionClient::connectionName() const +QString RefactoringConnectionClient::outputName() const { - return temporaryDirectory().path() + QStringLiteral("/ClangRefactoringBackEnd-") + currentProcessId(); + return QStringLiteral("RefactoringConnectionClient"); } -QString RefactoringConnectionClient::outputName() const +void RefactoringConnectionClient::newConnectedServer(QIODevice *ioDevice) { - return QStringLiteral("RefactoringConnectionClient"); + m_serverProxy.setIoDevice(ioDevice); } } // namespace ClangBackEnd diff --git a/src/plugins/clangrefactoring/refactoringconnectionclient.h b/src/plugins/clangrefactoring/refactoringconnectionclient.h index bec3a4ea09e..55c9270ecca 100644 --- a/src/plugins/clangrefactoring/refactoringconnectionclient.h +++ b/src/plugins/clangrefactoring/refactoringconnectionclient.h @@ -43,11 +43,11 @@ public: protected: void sendEndCommand() override; void resetCounter() override; - QString connectionName() const override; QString outputName() const override; + void newConnectedServer(QIODevice *ioDevice) override; private: - RefactoringServerProxy serverProxy_; + RefactoringServerProxy m_serverProxy; }; } // namespace ClangBackEnd diff --git a/src/plugins/clangrefactoring/refactoringengine.h b/src/plugins/clangrefactoring/refactoringengine.h index 8bda8b6173a..9f8450e961f 100644 --- a/src/plugins/clangrefactoring/refactoringengine.h +++ b/src/plugins/clangrefactoring/refactoringengine.h @@ -41,8 +41,8 @@ namespace ClangRefactoring { class RefactoringEngine : public CppTools::RefactoringEngineInterface { public: - RefactoringEngine(ClangBackEnd::RefactoringServerInterface &m_server, - ClangBackEnd::RefactoringClientInterface &m_client, + RefactoringEngine(ClangBackEnd::RefactoringServerInterface &server, + ClangBackEnd::RefactoringClientInterface &client, ClangBackEnd::FilePathCachingInterface &filePathCache, SymbolQueryInterface &symbolQuery); ~RefactoringEngine() override; diff --git a/src/tools/clangbackend/clangbackendmain.cpp b/src/tools/clangbackend/clangbackendmain.cpp index 33a824c3d2f..432f3cb52c4 100644 --- a/src/tools/clangbackend/clangbackendmain.cpp +++ b/src/tools/clangbackend/clangbackendmain.cpp @@ -72,9 +72,9 @@ int main(int argc, char *argv[]) clang_enableStackTraces(); ClangCodeModelServer clangCodeModelServer; - ConnectionServer<ClangCodeModelServer, ClangCodeModelClientProxy> connectionServer(connection); - connectionServer.start(); + ConnectionServer<ClangCodeModelServer, ClangCodeModelClientProxy> connectionServer; connectionServer.setServer(&clangCodeModelServer); + connectionServer.start(connection); return application.exec(); } diff --git a/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp b/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp index d17fbd2bd97..c143daf662e 100644 --- a/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp +++ b/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp @@ -103,7 +103,7 @@ int main(int argc, char *argv[]) QCoreApplication application(argc, argv); - const QString connection = processArguments(application); + const QString connectionName = processArguments(application); Sqlite::Database database{Utils::PathString{QDir::tempPath() + "/symbol.db"}}; ClangBackEnd::RefactoringDatabaseInitializer<Sqlite::Database> databaseInitializer{database}; @@ -120,9 +120,9 @@ int main(int argc, char *argv[]) includeWatcher.setNotifier(&clangPchManagerServer); pchGenerator.setNotifier(&clangPchManagerServer); - ConnectionServer<PchManagerServer, PchManagerClientProxy> connectionServer(connection); - connectionServer.start(); + ConnectionServer<PchManagerServer, PchManagerClientProxy> connectionServer; connectionServer.setServer(&clangPchManagerServer); + connectionServer.start(connectionName); return application.exec(); } diff --git a/src/tools/clangrefactoringbackend/clangrefactoringbackendmain.cpp b/src/tools/clangrefactoringbackend/clangrefactoringbackendmain.cpp index 4b9b1a02f02..e09b892bea7 100644 --- a/src/tools/clangrefactoringbackend/clangrefactoringbackendmain.cpp +++ b/src/tools/clangrefactoringbackend/clangrefactoringbackendmain.cpp @@ -76,9 +76,9 @@ try { FilePathCaching filePathCache{database}; SymbolIndexing symbolIndexing{database, filePathCache}; RefactoringServer clangCodeModelServer{symbolIndexing, filePathCache}; - ConnectionServer<RefactoringServer, RefactoringClientProxy> connectionServer(connection); - connectionServer.start(); + ConnectionServer<RefactoringServer, RefactoringClientProxy> connectionServer; connectionServer.setServer(&clangCodeModelServer); + connectionServer.start(connection); return application.exec(); diff --git a/tests/unit/echoserver/echoclangcodemodelserver.cpp b/tests/unit/echoserver/echoclangcodemodelserver.cpp index b5de4ba5392..2c0fea98c93 100644 --- a/tests/unit/echoserver/echoclangcodemodelserver.cpp +++ b/tests/unit/echoserver/echoclangcodemodelserver.cpp @@ -31,7 +31,6 @@ #include <QCoreApplication> #include <QDebug> - namespace ClangBackEnd { void EchoClangCodeModelServer::dispatch(const MessageEnvelop &message) @@ -41,7 +40,6 @@ void EchoClangCodeModelServer::dispatch(const MessageEnvelop &message) void EchoClangCodeModelServer::end() { - ConnectionServer<EchoClangCodeModelServer, ClangCodeModelClientProxy>::removeServer(); QCoreApplication::quit(); } diff --git a/tests/unit/echoserver/echoserverprocessmain.cpp b/tests/unit/echoserver/echoserverprocessmain.cpp index a153feaf155..07010b0b918 100644 --- a/tests/unit/echoserver/echoserverprocessmain.cpp +++ b/tests/unit/echoserver/echoserverprocessmain.cpp @@ -43,15 +43,18 @@ int main(int argc, char *argv[]) QCoreApplication application(argc, argv); - if (application.arguments().count() != 2) { - qWarning() << "wrong argument count"; + + if (application.arguments().count() < 2) return 1; - } + else if (application.arguments().count() == 3) + *(int*)0 = 0; + else if (application.arguments().contains("connectionName")) + return 0; EchoClangCodeModelServer echoClangCodeModelServer; - ConnectionServer<EchoClangCodeModelServer, ClangCodeModelClientProxy> connectionServer(application.arguments()[1]); - connectionServer.start(); + ConnectionServer<EchoClangCodeModelServer, ClangCodeModelClientProxy> connectionServer; connectionServer.setServer(&echoClangCodeModelServer); + connectionServer.start(application.arguments()[1]); return application.exec(); } diff --git a/tests/unit/unittest/clientserveroutsideprocess-test.cpp b/tests/unit/unittest/clientserveroutsideprocess-test.cpp index 915632c9c28..871507f8df1 100644 --- a/tests/unit/unittest/clientserveroutsideprocess-test.cpp +++ b/tests/unit/unittest/clientserveroutsideprocess-test.cpp @@ -51,7 +51,7 @@ using ::testing::SizeIs; struct Data { Data() : client(&mockClangCodeModelClient) {} - MockClangCodeModelClient mockClangCodeModelClient; + NiceMock<MockClangCodeModelClient> mockClangCodeModelClient; ClangBackEnd::ClangCodeModelConnectionClient client; }; @@ -80,7 +80,7 @@ TEST_F(ClientServerOutsideProcessSlowTest, RestartProcessAsynchronously) client.restartProcessAsynchronously(); ASSERT_TRUE(clientSpy.wait(100000)); - ASSERT_TRUE(client.isProcessIsRunning()); + ASSERT_TRUE(client.isProcessRunning()); ASSERT_TRUE(client.isConnected()); } @@ -200,6 +200,6 @@ void ClientServerOutsideProcess::TearDown() client.setProcessAliveTimerInterval(1000000); client.waitForConnected(); - ASSERT_TRUE(client.isProcessIsRunning()); + ASSERT_TRUE(client.isProcessRunning()); ASSERT_TRUE(client.isConnected()); } diff --git a/tests/unit/unittest/eventspy.cpp b/tests/unit/unittest/eventspy.cpp new file mode 100644 index 00000000000..69a4a1e5c36 --- /dev/null +++ b/tests/unit/unittest/eventspy.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "eventspy.h" + +#include <QCoreApplication> +#include <QEvent> + +using namespace std::literals::chrono_literals; + +EventSpy::EventSpy(uint eventType) + : startTime(std::chrono::steady_clock::now()), + eventType(eventType) +{ +} + +bool EventSpy::waitForEvent() +{ + while (shouldRun()) + QCoreApplication::processEvents(); + + return eventHappened; +} + +bool EventSpy::event(QEvent *event) +{ + if (event->type() == eventType) { + eventHappened = true; + + return true; + } + + return false; +} + +bool EventSpy::shouldRun() const +{ + return !eventHappened + && (std::chrono::steady_clock::now() - startTime) < 1s; +} diff --git a/tests/unit/unittest/eventspy.h b/tests/unit/unittest/eventspy.h new file mode 100644 index 00000000000..ac8a780d12b --- /dev/null +++ b/tests/unit/unittest/eventspy.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QObject> + +#include <chrono> + +class EventSpy : public QObject +{ + Q_OBJECT + +public: + EventSpy(uint eventType); + + bool waitForEvent(); + +protected: + bool event(QEvent *event) override; + +private: + bool shouldRun() const; + +private: + std::chrono::steady_clock::time_point startTime; + uint eventType; + bool eventHappened = false; +}; diff --git a/tests/unit/unittest/google-using-declarations.h b/tests/unit/unittest/google-using-declarations.h index 0b58af54237..73994bd2bc2 100644 --- a/tests/unit/unittest/google-using-declarations.h +++ b/tests/unit/unittest/google-using-declarations.h @@ -33,7 +33,6 @@ using testing::AnyNumber; using testing::AnyOf; using testing::Contains; using testing::ElementsAre; -using testing::Eq; using testing::Field; using testing::HasSubstr; using testing::InSequence; @@ -51,3 +50,10 @@ using testing::SizeIs; using testing::StrEq; using testing::Throw; using testing::UnorderedElementsAre; + +using testing::Eq; +using testing::Ge; +using testing::Gt; +using testing::Le; +using testing::Lt; +using testing::Ne; diff --git a/tests/unit/unittest/processcreator-test.cpp b/tests/unit/unittest/processcreator-test.cpp new file mode 100644 index 00000000000..0b5abfdc4a4 --- /dev/null +++ b/tests/unit/unittest/processcreator-test.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "googletest.h" + +#include "eventspy.h" + +#include <processcreator.h> +#include <processexception.h> +#include <processstartedevent.h> + +#include <utils/hostosinfo.h> + +#include <QProcess> + +#include <future> + +using testing::NotNull; + +using ClangBackEnd::ProcessCreator; +using ClangBackEnd::ProcessException; +using ClangBackEnd::ProcessStartedEvent; + +namespace { + +class ProcessCreator : public testing::Test +{ +protected: + void SetUp(); + +protected: + ::ProcessCreator processCreator; + QStringList m_arguments = {QStringLiteral("connectionName")}; +}; + +TEST_F(ProcessCreator, ProcessIsNotNull) +{ + auto future = processCreator.createProcess(); + auto process = future.get(); + + ASSERT_THAT(process.get(), NotNull()); +} + +TEST_F(ProcessCreator, ProcessIsRunning) +{ + auto future = processCreator.createProcess(); + auto process = future.get(); + + ASSERT_THAT(process->state(), QProcess::Running); +} + +TEST_F(ProcessCreator, ProcessPathIsNotExisting) +{ + processCreator.setProcessPath(Utils::HostOsInfo::withExecutableSuffix(ECHOSERVER"fail")); + + auto future = processCreator.createProcess(); + ASSERT_THROW(future.get(), ProcessException); +} + +TEST_F(ProcessCreator, ProcessStartIsSucessfull) +{ + auto future = processCreator.createProcess(); + ASSERT_NO_THROW(future.get()); +} + +TEST_F(ProcessCreator, ProcessObserverGetsEvent) +{ + EventSpy eventSpy(ProcessStartedEvent::ProcessStarted); + processCreator.setObserver(&eventSpy); + auto future = processCreator.createProcess(); + + eventSpy.waitForEvent(); +} + +TEST_F(ProcessCreator, TemporayPathIsSetForDefaultInitialization) +{ + QString path = processCreator.temporaryDirectory().path(); + + ASSERT_THAT(path.size(), Gt(0)); +} + +TEST_F(ProcessCreator, TemporayPathIsResetted) +{ + std::string oldPath = processCreator.temporaryDirectory().path().toStdString(); + + processCreator.resetTemporaryDirectory(); + + ASSERT_THAT(processCreator.temporaryDirectory().path().toStdString(), + AllOf(Not(IsEmpty()), Ne(oldPath))); +} + +void ProcessCreator::SetUp() +{ + processCreator.setTemporaryDirectoryPattern("process-XXXXXXX"); + processCreator.resetTemporaryDirectory(); + processCreator.setProcessPath(Utils::HostOsInfo::withExecutableSuffix(ECHOSERVER)); + processCreator.setArguments(m_arguments); +} +} diff --git a/tests/unit/unittest/readandwritemessageblock-test.cpp b/tests/unit/unittest/readandwritemessageblock-test.cpp index 445743543a7..0c9e9db18d5 100644 --- a/tests/unit/unittest/readandwritemessageblock-test.cpp +++ b/tests/unit/unittest/readandwritemessageblock-test.cpp @@ -119,7 +119,7 @@ TEST_F(ReadAndWriteMessageBlock, ReadThreeMessagesAndTestCount) writeMessageBlock.write(ClangBackEnd::EndMessage()); buffer.seek(0); - ASSERT_EQ(3, readMessageBlock.readAll().count()); + ASSERT_THAT(readMessageBlock.readAll(), SizeIs(3)); } TEST_F(ReadAndWriteMessageBlock, CompareEndMessage) diff --git a/tests/unit/unittest/refactoringclient-test.cpp b/tests/unit/unittest/refactoringclient-test.cpp index a28d27b3e9a..c6ed203a559 100644 --- a/tests/unit/unittest/refactoringclient-test.cpp +++ b/tests/unit/unittest/refactoringclient-test.cpp @@ -41,6 +41,7 @@ #include <utils/smallstringvector.h> +#include <QBuffer> #include <QTextCursor> #include <QTextDocument> @@ -74,9 +75,10 @@ protected: NiceMock<MockSearchHandle> mockSearchHandle; NiceMock<MockSymbolQuery> mockSymbolQuery; MockRefactoringClientCallBack callbackMock; + QBuffer ioDevice; ClangRefactoring::RefactoringClient client; - ClangBackEnd::RefactoringConnectionClient connectionClient{&client}; - RefactoringEngine engine{connectionClient.serverProxy(), client, mockFilePathCaching, mockSymbolQuery}; + ClangBackEnd::RefactoringServerProxy serverProxy{&client, &ioDevice}; + RefactoringEngine engine{serverProxy, client, mockFilePathCaching, mockSymbolQuery}; QString fileContent{QStringLiteral("int x;\nint y;")}; QTextDocument textDocument{fileContent}; QTextCursor cursor{&textDocument}; diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index 9d8c4eafbae..8ca648b7662 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -70,6 +70,7 @@ SOURCES += \ spydummy.cpp \ symbolindexer-test.cpp \ stringcache-test.cpp \ + eventspy.cpp \ unittests-main.cpp \ utf8-test.cpp \ symbolstorage-test.cpp \ @@ -82,6 +83,7 @@ SOURCES += \ filepathcache-test.cpp \ filepathstorage-test.cpp \ filepathstoragesqlitestatementfactory-test.cpp \ + processcreator-test.cpp \ nativefilepath-test.cpp \ nativefilepathview-test.cpp @@ -176,6 +178,7 @@ HEADERS += \ conditionally-disabled-tests.h \ dummyclangipcclient.h \ dynamicastmatcherdiagnosticcontainer-matcher.h \ + eventspy.h \ fakeprocess.h \ faketimer.h \ filesystem-utilities.h \ |