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 + +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 + #include 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,22 +61,17 @@ ClangCodeModelConnectionClient::~ClangCodeModelConnectionClient() ClangCodeModelServerProxy &ClangCodeModelConnectionClient::serverProxy() { - return serverProxy_; + return m_serverProxy; } void ClangCodeModelConnectionClient::sendEndCommand() { - serverProxy_.end(); + m_serverProxy.end(); } void ClangCodeModelConnectionClient::resetCounter() { - serverProxy_.resetCounter(); -} - -QString ClangCodeModelConnectionClient::connectionName() const -{ - return temporaryDirectory().path() + QStringLiteral("/ClangBackEnd-") + currentProcessId(); + m_serverProxy.resetCounter(); } QString ClangCodeModelConnectionClient::outputName() const @@ -76,4 +79,9 @@ QString ClangCodeModelConnectionClient::outputName() const return QStringLiteral("ClangCodeModelConnectionClient"); } +void ClangCodeModelConnectionClient::newConnectedServer(QIODevice *ioDevice) +{ + 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 #include -#include -#include -#include - namespace ClangBackEnd { ClangCodeModelServerProxy::ClangCodeModelServerProxy(ClangCodeModelClientInterface *client, QIODevice *ioDevice) - : m_writeMessageBlock(ioDevice), - m_readMessageBlock(ioDevice), - m_client(client) + : BaseServerProxy(client, ioDevice) { - 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() -{ - 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 #include @@ -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 #include #include @@ -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(); +} + +ConnectionClient::~ConnectionClient() +{ + QLocalServer::removeServer(connectionName()); } void ConnectionClient::startProcessAndConnectToServerAsynchronously() { - m_process = startProcess(); -} + m_processIsStarting = true; -bool ConnectionClient::disconnectFromServer() -{ - localSocket.disconnectFromServer(); - if (localSocket.state() != QLocalSocket::UnconnectedState) - return localSocket.waitForDisconnected(); - - 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 ConnectionClient::startProcess() +QString ConnectionClient::connectionName() const { - m_processIsStarting = true; + return m_connectionName; +} - auto process = std::unique_ptr(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 process; + return true; + }; + + 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("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 &&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; + return m_localSocket; } -bool ConnectionClient::isProcessIsRunning() const +bool ConnectionClient::isProcessRunning(QProcess *process) { - return m_process && m_process->state() == QProcess::Running; -} - -void ConnectionClient::connectProcessFinished(QProcess *process) const -{ - connect(process, - static_cast(&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(&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(&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 - +#include #include #include #include #include +#include #include 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 startProcess(); - void finishProcess(std::unique_ptr &&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 m_process; - QLocalSocket localSocket; - std::unique_ptr m_temporaryDirectory; + QLocalServer m_localServer; + std::future 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 #include #include +#include #include #include @@ -41,46 +42,28 @@ namespace ClangBackEnd { class ClangCodeModelServerInterface; class ClangCodeModelClientProxy; -struct CLANGSUPPORT_EXPORT ConnectionName { - static QString connectionName; -}; - template 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(&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(m_ipcServer, &m_localSocket); m_ipcServer->setClient(m_ipcClientProxy.get()); } @@ -114,40 +97,40 @@ private: { m_ipcClientProxy.reset(); - m_localSocket = nullptr; - delayedExitApplicationIfNoSockedIsConnected(); } - QLocalSocket *nextPendingConnection() - { - QLocalSocket *localSocket = m_localServer.nextPendingConnection(); - - QObject::connect(localSocket, - &QLocalSocket::disconnected, - [&] { handleSocketDisconnect(); }); - - return localSocket; - } - void delayedExitApplicationIfNoSockedIsConnected() { - if (m_localSocket == nullptr) - QTimer::singleShot(60000, [&] { exitApplicationIfNoSockedIsConnected(); }); + QTimer::singleShot(60000, [&] { exitApplicationIfNoSockedIsConnected(); }); } void exitApplicationIfNoSockedIsConnected() { - if (m_localSocket == nullptr) - QCoreApplication::exit(); + if (m_localSocket.state() != QLocalSocket::UnconnectedState) + m_localSocket.disconnectFromServer(); + QCoreApplication::exit(); + } + + void connectAliveTimer() + { + QObject::connect(&m_aliveTimer, + &QTimer::timeout, + [&] { sendAliveMessage(); }); + } + + void connectLocalSocketDisconnet() + { + QObject::connect(&m_localSocket, + &QLocalSocket::disconnected, + [&] { handleSocketDisconnect(); }); } private: - std::unique_ptr m_ipcClientProxy; - QLocalSocket* m_localSocket; - ServerInterface *m_ipcServer; - QLocalServer m_localServer; + QLocalSocket m_localSocket; QTimer m_aliveTimer; + std::unique_ptr m_ipcClientProxy; + ServerInterface *m_ipcServer; }; } // 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 +#include +#include + +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 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(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 + +#include "processhandle.h" + +#include + +#include + +#include +#include + +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 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 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 + +#include + +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 + +#include + +namespace ClangBackEnd { + +class QProcessUniquePointerDeleter +{ +public: + void operator()(QProcess* process) + { + process->kill(); + process->waitForFinished(); + } +}; + +using QProcessUniquePointer = std::unique_ptr; + +} // 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 + +namespace ClangBackEnd { + +class ProcessStartedEvent : public QEvent +{ +public: + enum Type { + ProcessStarted = QEvent::User + 3456 + }; + + ProcessStartedEvent() + : QEvent(static_cast(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 ReadMessageBlock::readAll() +std::vector ReadMessageBlock::readAll() { - QVector messages; + std::vector 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 +#include + QT_BEGIN_NAMESPACE class QDataStream; class QIODevice; @@ -42,10 +44,12 @@ public: ReadMessageBlock(QIODevice *ioDevice = nullptr); MessageEnvelop read(); - QVector readAll(); + std::vector 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 + #include -#include namespace ClangPchManager { @@ -41,30 +42,35 @@ 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(); -} - -QString ClangPchManager::PchManagerConnectionClient::connectionName() const -{ - return temporaryDirectory().path() + QStringLiteral("/ClangPchManagerBackEnd-") + currentProcessId(); + m_serverProxy.resetCounter(); } QString PchManagerConnectionClient::outputName() const @@ -72,4 +78,9 @@ QString PchManagerConnectionClient::outputName() const return QStringLiteral("PchManagerConnectionClient"); } +void PchManagerConnectionClient::newConnectedServer(QIODevice *ioDevice) +{ + 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 + #include 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,22 +59,17 @@ RefactoringConnectionClient::~RefactoringConnectionClient() RefactoringServerProxy &RefactoringConnectionClient::serverProxy() { - return serverProxy_; + return m_serverProxy; } void RefactoringConnectionClient::sendEndCommand() { - serverProxy_.end(); + m_serverProxy.end(); } void RefactoringConnectionClient::resetCounter() { - serverProxy_.resetCounter(); -} - -QString RefactoringConnectionClient::connectionName() const -{ - return temporaryDirectory().path() + QStringLiteral("/ClangRefactoringBackEnd-") + currentProcessId(); + m_serverProxy.resetCounter(); } QString RefactoringConnectionClient::outputName() const @@ -75,4 +77,9 @@ QString RefactoringConnectionClient::outputName() const return QStringLiteral("RefactoringConnectionClient"); } +void RefactoringConnectionClient::newConnectedServer(QIODevice *ioDevice) +{ + 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 connectionServer(connection); - connectionServer.start(); + ConnectionServer 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 databaseInitializer{database}; @@ -120,9 +120,9 @@ int main(int argc, char *argv[]) includeWatcher.setNotifier(&clangPchManagerServer); pchGenerator.setNotifier(&clangPchManagerServer); - ConnectionServer connectionServer(connection); - connectionServer.start(); + ConnectionServer 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 connectionServer(connection); - connectionServer.start(); + ConnectionServer 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 #include - namespace ClangBackEnd { void EchoClangCodeModelServer::dispatch(const MessageEnvelop &message) @@ -41,7 +40,6 @@ void EchoClangCodeModelServer::dispatch(const MessageEnvelop &message) void EchoClangCodeModelServer::end() { - ConnectionServer::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 connectionServer(application.arguments()[1]); - connectionServer.start(); + ConnectionServer 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; 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 +#include + +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 + +#include + +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 +#include +#include + +#include + +#include + +#include + +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 +#include #include #include @@ -74,9 +75,10 @@ protected: NiceMock mockSearchHandle; NiceMock 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 \