diff --git a/src/tools/clangbackend/ipcsource/clangasyncjob.h b/src/tools/clangbackend/ipcsource/clangasyncjob.h index b44227041e5..c8a7b799473 100644 --- a/src/tools/clangbackend/ipcsource/clangasyncjob.h +++ b/src/tools/clangbackend/ipcsource/clangasyncjob.h @@ -64,6 +64,11 @@ public: return future; } + void preventFinalization() override + { + m_futureWatcher.disconnect(); + } + private: Runner m_runner; QFutureWatcher m_futureWatcher; diff --git a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri index b247735deda..dc468470f56 100644 --- a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri +++ b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri @@ -41,7 +41,9 @@ HEADERS += $$PWD/clangcodemodelserver.h \ $$PWD/clangtranslationunit.h \ $$PWD/clangunsavedfilesshallowarguments.h \ $$PWD/clangupdatedocumentannotationsjob.h \ - $$PWD/clangexceptions.h + $$PWD/clangexceptions.h \ + $$PWD/clangdocumentprocessor.h \ + $$PWD/clangdocumentprocessors.h \ SOURCES += $$PWD/clangcodemodelserver.cpp \ $$PWD/codecompleter.cpp \ @@ -80,4 +82,6 @@ SOURCES += $$PWD/clangcodemodelserver.cpp \ $$PWD/clangtranslationunit.cpp \ $$PWD/clangunsavedfilesshallowarguments.cpp \ $$PWD/clangupdatedocumentannotationsjob.cpp \ - $$PWD/clangexceptions.cpp + $$PWD/clangexceptions.cpp \ + $$PWD/clangdocumentprocessor.cpp \ + $$PWD/clangdocumentprocessors.cpp \ diff --git a/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp b/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp index 3c1cee59777..1b26c39fadf 100644 --- a/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp +++ b/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp @@ -141,6 +141,10 @@ void ClangCodeModelServer::unregisterTranslationUnitsForEditor(const ClangBackEn TIME_SCOPE_DURATION("ClangCodeModelServer::unregisterTranslationUnitsForEditor"); try { + for (const auto &fileContainer : message.fileContainers()) { + const Document &document = documents.document(fileContainer); + documentProcessors().remove(document); + } documents.remove(message.fileContainers()); unsavedFiles.remove(message.fileContainers()); } catch (const std::exception &exception) { @@ -211,8 +215,9 @@ void ClangCodeModelServer::completeCode(const ClangBackEnd::CompleteCodeMessage jobRequest.column = message.column(); jobRequest.ticketNumber = message.ticketNumber(); - jobs().add(jobRequest); - jobs().process(); + DocumentProcessor processor = documentProcessors().processor(document); + processor.addJob(jobRequest); + processor.process(); } catch (const std::exception &exception) { qWarning() << "Error in ClangCodeModelServer::completeCode:" << exception.what(); } @@ -229,8 +234,9 @@ void ClangCodeModelServer::requestDocumentAnnotations(const RequestDocumentAnnot const JobRequest jobRequest = createJobRequest(document, JobRequest::Type::RequestDocumentAnnotations); - jobs().add(jobRequest); - jobs().process(); + DocumentProcessor processor = documentProcessors().processor(document); + processor.addJob(jobRequest); + processor.process(); } catch (const std::exception &exception) { qWarning() << "Error in ClangCodeModelServer::requestDocumentAnnotations:" << exception.what(); } @@ -260,9 +266,14 @@ void ClangCodeModelServer::startDocumentAnnotationsTimerIfFileIsNotOpenAsDocumen updateDocumentAnnotationsTimer.start(0); } -const Jobs &ClangCodeModelServer::jobsForTestOnly() +QList ClangCodeModelServer::runningJobsForTestsOnly() { - return jobs(); + return documentProcessors().runningJobs(); +} + +int ClangCodeModelServer::queueSizeForTestsOnly() +{ + return documentProcessors().queueSize(); } bool ClangCodeModelServer::isTimerRunningForTestOnly() const @@ -270,28 +281,26 @@ bool ClangCodeModelServer::isTimerRunningForTestOnly() const return updateDocumentAnnotationsTimer.isActive(); } -void ClangCodeModelServer::addJobRequestsForDirtyAndVisibleDocuments() -{ - for (const auto &document : documents.documents()) { - if (document.isNeedingReparse() && document.isVisibleInEditor()) - jobs().add(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations)); - } -} - void ClangCodeModelServer::processJobsForDirtyAndVisibleDocuments() { - addJobRequestsForDirtyAndVisibleDocuments(); - jobs().process(); + for (const auto &document : documents.documents()) { + if (document.isNeedingReparse() && document.isVisibleInEditor()) { + DocumentProcessor processor = documentProcessors().processor(document); + processor.addJob(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations)); + } + } + + documentProcessors().process(); } void ClangCodeModelServer::processInitialJobsForDocuments(const std::vector &documents) { for (const auto &document : documents) { - jobs().add(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations)); - jobs().add(createJobRequest(document, JobRequest::Type::CreateInitialDocumentPreamble)); + DocumentProcessor processor = documentProcessors().create(document); + processor.addJob(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations)); + processor.addJob(createJobRequest(document, JobRequest::Type::CreateInitialDocumentPreamble)); + processor.process(); } - - jobs().process(); } JobRequest ClangCodeModelServer::createJobRequest(const Document &document, @@ -315,16 +324,16 @@ void ClangCodeModelServer::setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(i updateDocumentAnnotationsTimeOutInMs = value; } -Jobs &ClangCodeModelServer::jobs() +DocumentProcessors &ClangCodeModelServer::documentProcessors() { - if (!jobs_) { - // Jobs needs a reference to the client, but the client is not known at - // construction time of ClangCodeModelServer, so construct Jobs in a - // lazy manner. - jobs_.reset(new Jobs(documents, unsavedFiles, projects, *client())); + if (!documentProcessors_) { + // DocumentProcessors needs a reference to the client, but the client + // is not known at construction time of ClangCodeModelServer, so + // construct DocumentProcessors in a lazy manner. + documentProcessors_.reset(new DocumentProcessors(documents, unsavedFiles, projects, *client())); } - return *jobs_.data(); + return *documentProcessors_.data(); } } // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangcodemodelserver.h b/src/tools/clangbackend/ipcsource/clangcodemodelserver.h index ff0db141f9c..efebb054da9 100644 --- a/src/tools/clangbackend/ipcsource/clangcodemodelserver.h +++ b/src/tools/clangbackend/ipcsource/clangcodemodelserver.h @@ -31,8 +31,9 @@ #include "projects.h" #include "clangdocument.h" #include "clangdocuments.h" +#include "clangdocumentprocessors.h" +#include "clangjobrequest.h" #include "unsavedfiles.h" -#include "clangjobs.h" #include @@ -58,14 +59,15 @@ public: void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override; void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override; -public /*for tests*/: +public: // for tests const Documents &documentsForTestOnly() const; - const Jobs &jobsForTestOnly(); + QList runningJobsForTestsOnly(); + int queueSizeForTestsOnly(); bool isTimerRunningForTestOnly() const; void setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(int value); private: - Jobs &jobs(); + DocumentProcessors &documentProcessors(); void startDocumentAnnotationsTimerIfFileIsNotOpenAsDocument(const Utf8String &filePath); void addJobRequestsForDirtyAndVisibleDocuments(); @@ -78,7 +80,8 @@ private: ProjectParts projects; UnsavedFiles unsavedFiles; Documents documents; - QScopedPointer jobs_; + + QScopedPointer documentProcessors_; // Delayed initialization QTimer updateDocumentAnnotationsTimer; int updateDocumentAnnotationsTimeOutInMs; diff --git a/src/tools/clangbackend/ipcsource/clangdocumentprocessor.cpp b/src/tools/clangbackend/ipcsource/clangdocumentprocessor.cpp new file mode 100644 index 00000000000..0b4faf9251f --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangdocumentprocessor.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** 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 "clangdocumentprocessor.h" + +#include "clangjobs.h" + +#include "clangdocument.h" + +namespace ClangBackEnd { + +class DocumentProcessorData +{ +public: + DocumentProcessorData(const Document &document, + Documents &documents, + UnsavedFiles &unsavedFiles, + ProjectParts &projects, + ClangCodeModelClientInterface &client) + : document(document) + , jobs(documents, unsavedFiles, projects, client) + {} + +public: + Document document; + Jobs jobs; +}; + +DocumentProcessor::DocumentProcessor(const Document &document, + Documents &documents, + UnsavedFiles &unsavedFiles, + ProjectParts &projects, + ClangCodeModelClientInterface &client) + : d(std::make_shared(document, + documents, + unsavedFiles, + projects, + client)) +{ +} + +void DocumentProcessor::addJob(const JobRequest &jobRequest) +{ + d->jobs.add(jobRequest); +} + +JobRequests DocumentProcessor::process() +{ + return d->jobs.process(); +} + +Document DocumentProcessor::document() const +{ + return d->document; +} + +QList DocumentProcessor::runningJobs() const +{ + return d->jobs.runningJobs(); +} + +int DocumentProcessor::queueSize() const +{ + return d->jobs.queue().size(); +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangdocumentprocessor.h b/src/tools/clangbackend/ipcsource/clangdocumentprocessor.h new file mode 100644 index 00000000000..0ad452b314a --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangdocumentprocessor.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** 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 "clangjobrequest.h" +#include "clangjobs.h" + +#include + +namespace ClangBackEnd { + +class ClangCodeModelClientInterface; +class Document; +class Documents; +class DocumentProcessorData; +class JobRequest; +class ProjectParts; +class UnsavedFiles; + +class DocumentProcessor +{ +public: + DocumentProcessor(const Document &document, + Documents &documents, + UnsavedFiles &unsavedFiles, + ProjectParts &projects, + ClangCodeModelClientInterface &client); + + void addJob(const JobRequest &jobRequest); + JobRequests process(); + + Document document() const; + +public: // for tests + QList runningJobs() const; + int queueSize() const; + +private: + std::shared_ptr d; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangdocumentprocessors.cpp b/src/tools/clangbackend/ipcsource/clangdocumentprocessors.cpp new file mode 100644 index 00000000000..3e465f8173d --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangdocumentprocessors.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** 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 "clangdocumentprocessors.h" +#include "clangdocument.h" +#include "clangexceptions.h" + +namespace ClangBackEnd { + +DocumentProcessors::DocumentProcessors(Documents &documents, + UnsavedFiles &unsavedFiles, + ProjectParts &projects, + ClangCodeModelClientInterface &client) + : m_documents(documents) + , m_unsavedFiles(unsavedFiles) + , m_projects(projects) + , m_client(client) +{ +} + +static bool operator<(const DocumentId &lhs, const DocumentId &rhs) +{ + return lhs.filePath < rhs.filePath + || (lhs.filePath == rhs.filePath && lhs.projectPartId < lhs.projectPartId); +} + +DocumentProcessor DocumentProcessors::create(const Document &document) +{ + const DocumentId id{document.filePath(), document.projectPartId()}; + if (m_processors.contains(id)) + throw DocumentProcessorAlreadyExists(document.filePath(), document.projectPartId()); + + const DocumentProcessor element(document, m_documents, m_unsavedFiles, m_projects, m_client); + m_processors.insert(id, element); + + return element; +} + +DocumentProcessor DocumentProcessors::processor(const Document &document) +{ + const DocumentId id{document.filePath(), document.projectPartId()}; + + const auto it = m_processors.find(id); + if (it == m_processors.end()) + throw DocumentProcessorDoesNotExist(document.filePath(), document.projectPartId()); + + return *it; +} + +QList DocumentProcessors::processors() const +{ + return m_processors.values(); +} + +void DocumentProcessors::remove(const Document &document) +{ + const DocumentId id{document.filePath(), document.projectPartId()}; + + const int itemsRemoved = m_processors.remove(id); + if (itemsRemoved != 1) + throw DocumentProcessorDoesNotExist(document.filePath(), document.projectPartId()); +} + +JobRequests DocumentProcessors::process() +{ + JobRequests jobsStarted; + for (auto &processor : m_processors) + jobsStarted += processor.process(); + + return jobsStarted; +} + +QList DocumentProcessors::runningJobs() const +{ + QList jobs; + for (auto &processor : m_processors) + jobs += processor.runningJobs(); + + return jobs; +} + +int DocumentProcessors::queueSize() const +{ + int total = 0; + for (auto &processor : m_processors) + total += processor.queueSize(); + + return total; +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangdocumentprocessors.h b/src/tools/clangbackend/ipcsource/clangdocumentprocessors.h new file mode 100644 index 00000000000..83e37f52766 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangdocumentprocessors.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** 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 "clangdocumentprocessor.h" +#include "clangjobs.h" + +#include + +#include + +namespace ClangBackEnd { + +class Document; +class DocumentProcessor; + +class DocumentId { +public: + Utf8String filePath; + Utf8String projectPartId; +}; + +class DocumentProcessors +{ +public: + DocumentProcessors(Documents &documents, + UnsavedFiles &unsavedFiles, + ProjectParts &projects, + ClangCodeModelClientInterface &client); + + DocumentProcessor create(const Document &document); + DocumentProcessor processor(const Document &document); + void remove(const Document &document); + + JobRequests process(); + +public: // for tests + QList processors() const; + QList runningJobs() const; + int queueSize() const; + +private: + Documents &m_documents; + UnsavedFiles &m_unsavedFiles; + ProjectParts &m_projects; + ClangCodeModelClientInterface &m_client; + + QMap m_processors; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangexceptions.cpp b/src/tools/clangbackend/ipcsource/clangexceptions.cpp index e6d9218a943..4a85df4129a 100644 --- a/src/tools/clangbackend/ipcsource/clangexceptions.cpp +++ b/src/tools/clangbackend/ipcsource/clangexceptions.cpp @@ -74,4 +74,24 @@ DocumentIsNullException::DocumentIsNullException() m_info = Utf8String::fromUtf8("Tried to access a null Document!"); } +DocumentProcessorAlreadyExists::DocumentProcessorAlreadyExists(const Utf8String &filePath, + const Utf8String &projectPartId) +{ + m_info = Utf8StringLiteral("Document processor for file '") + + filePath + + Utf8StringLiteral("' and project part id '") + + projectPartId + + Utf8StringLiteral("' already exists!"); +} + +DocumentProcessorDoesNotExist::DocumentProcessorDoesNotExist(const Utf8String &filePath, + const Utf8String &projectPartId) +{ + m_info = Utf8StringLiteral("Document processor for file '") + + filePath + + Utf8StringLiteral("' and project part id '") + + projectPartId + + Utf8StringLiteral("' does not exist!"); +} + } // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangexceptions.h b/src/tools/clangbackend/ipcsource/clangexceptions.h index 6b1938cdfd8..1fbedccb38b 100644 --- a/src/tools/clangbackend/ipcsource/clangexceptions.h +++ b/src/tools/clangbackend/ipcsource/clangexceptions.h @@ -74,4 +74,18 @@ public: DocumentIsNullException(); }; +class DocumentProcessorAlreadyExists : public ClangBaseException +{ +public: + DocumentProcessorAlreadyExists(const Utf8String &filePath, + const Utf8String &projectPartId); +}; + +class DocumentProcessorDoesNotExist : public ClangBaseException +{ +public: + DocumentProcessorDoesNotExist(const Utf8String &filePath, + const Utf8String &projectPartId); +}; + } // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangiasyncjob.h b/src/tools/clangbackend/ipcsource/clangiasyncjob.h index 44f503f3cb8..dcc7df1c16f 100644 --- a/src/tools/clangbackend/ipcsource/clangiasyncjob.h +++ b/src/tools/clangbackend/ipcsource/clangiasyncjob.h @@ -56,6 +56,8 @@ public: virtual QFuture runAsync() = 0; virtual void finalizeAsyncRun() = 0; + virtual void preventFinalization() = 0; + public: // for tests bool isFinished() const; void setIsFinished(bool isFinished); diff --git a/src/tools/clangbackend/ipcsource/clangjobs.cpp b/src/tools/clangbackend/ipcsource/clangjobs.cpp index ac787c36fa5..c5202c15858 100644 --- a/src/tools/clangbackend/ipcsource/clangjobs.cpp +++ b/src/tools/clangbackend/ipcsource/clangjobs.cpp @@ -53,9 +53,15 @@ Jobs::Jobs(Documents &documents, Jobs::~Jobs() { + foreach (IAsyncJob *asyncJob, m_running.keys()) + asyncJob->preventFinalization(); + QFutureSynchronizer waitForFinishedJobs; foreach (const RunningJob &runningJob, m_running.values()) waitForFinishedJobs.addFuture(runningJob.future); + + foreach (IAsyncJob *asyncJob, m_running.keys()) + delete asyncJob; } void Jobs::add(const JobRequest &job) @@ -118,9 +124,9 @@ void Jobs::onJobFinished(IAsyncJob *asyncJob) process(); } -int Jobs::runningJobs() const +QList Jobs::runningJobs() const { - return m_running.size(); + return m_running.values(); } JobRequests Jobs::queue() const diff --git a/src/tools/clangbackend/ipcsource/clangjobs.h b/src/tools/clangbackend/ipcsource/clangjobs.h index 70b6dd80f6f..85d47488580 100644 --- a/src/tools/clangbackend/ipcsource/clangjobs.h +++ b/src/tools/clangbackend/ipcsource/clangjobs.h @@ -59,7 +59,7 @@ public: JobRequests process(); public /*for tests*/: - int runningJobs() const; + QList runningJobs() const; JobRequests queue() const; bool isJobRunning(const Utf8String &filePath, const Utf8String &projectPartId) const; diff --git a/tests/unit/unittest/clangdocumentprocessor-test.cpp b/tests/unit/unittest/clangdocumentprocessor-test.cpp new file mode 100644 index 00000000000..1674b224781 --- /dev/null +++ b/tests/unit/unittest/clangdocumentprocessor-test.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** 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 "clangiasyncjob.h" +#include "dummyclangipcclient.h" +#include "processevents-utilities.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "gtest-qt-printing.h" + +using namespace ClangBackEnd; + +namespace { + +class DocumentProcessor : public ::testing::Test +{ +protected: + void SetUp() override; + void TearDown() override; + + ClangBackEnd::JobRequest createJobRequest(ClangBackEnd::JobRequest::Type type) const; + + bool waitUntilAllJobsFinished(int timeOutInMs = 10000) const; + +protected: + ClangBackEnd::ProjectParts projects; + ClangBackEnd::UnsavedFiles unsavedFiles; + ClangBackEnd::Documents documents{projects, unsavedFiles}; + ClangBackEnd::Document document; + + DummyIpcClient dummyIpcClient; + + Utf8String filePath{Utf8StringLiteral(TESTDATA_DIR"/translationunits.cpp")}; + Utf8String projectPartId{Utf8StringLiteral("/path/to/projectfile")}; + + ClangBackEnd::DocumentProcessor documentProcessor{document, + documents, + unsavedFiles, + projects, + dummyIpcClient}; +}; + +TEST_F(DocumentProcessor, ProcessEmpty) +{ + const JobRequests jobsStarted = documentProcessor.process(); + + ASSERT_THAT(jobsStarted.size(), 0); +} + +TEST_F(DocumentProcessor, ProcessSingleJob) +{ + const JobRequest jobRequest = createJobRequest(JobRequest::Type::UpdateDocumentAnnotations); + documentProcessor.addJob(jobRequest); + + const JobRequests jobsStarted = documentProcessor.process(); + + ASSERT_THAT(jobsStarted.size(), 1); +} + +void DocumentProcessor::SetUp() +{ + projects.createOrUpdate({ProjectPartContainer(projectPartId)}); + + const QVector fileContainer{FileContainer(filePath, projectPartId)}; + document = documents.create(fileContainer).front(); + documents.setVisibleInEditors({filePath}); + documents.setUsedByCurrentEditor(filePath); +} + +void DocumentProcessor::TearDown() +{ + ASSERT_TRUE(waitUntilAllJobsFinished()); // QFuture/QFutureWatcher is implemented with events +} + +JobRequest DocumentProcessor::createJobRequest(JobRequest::Type type) const +{ + JobRequest jobRequest; + jobRequest.type = type; + jobRequest.requirements = JobRequest::requirementsForType(type); + jobRequest.filePath = filePath; + jobRequest.projectPartId = projectPartId; + jobRequest.unsavedFilesChangeTimePoint = unsavedFiles.lastChangeTimePoint(); + jobRequest.documentRevision = document.documentRevision(); + jobRequest.projectChangeTimePoint = projects.project(projectPartId).lastChangeTimePoint(); + + return jobRequest; +} + +bool DocumentProcessor::waitUntilAllJobsFinished(int timeOutInMs) const +{ + const auto noJobsRunningAnymore = [this](){ return documentProcessor.runningJobs().isEmpty(); }; + + return ProcessEventUtilities::processEventsUntilTrue(noJobsRunningAnymore, timeOutInMs); +} + +} // anonymous diff --git a/tests/unit/unittest/clangdocumentprocessors-test.cpp b/tests/unit/unittest/clangdocumentprocessors-test.cpp new file mode 100644 index 00000000000..7c36146aaba --- /dev/null +++ b/tests/unit/unittest/clangdocumentprocessors-test.cpp @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** 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 "clangiasyncjob.h" +#include "dummyclangipcclient.h" +#include "processevents-utilities.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "gtest-qt-printing.h" + +using testing::Eq; + +using namespace ClangBackEnd; + +namespace { + +class DocumentProcessors : public ::testing::Test +{ +protected: + void SetUp() override; + void TearDown() override; + + ClangBackEnd::JobRequest createJobRequest(ClangBackEnd::JobRequest::Type type) const; + + bool waitUntilAllJobsFinished(int timeOutInMs = 10000) const; + +protected: + ClangBackEnd::ProjectParts projects; + ClangBackEnd::UnsavedFiles unsavedFiles; + ClangBackEnd::Documents documents{projects, unsavedFiles}; + ClangBackEnd::Document document; + + DummyIpcClient dummyIpcClient; + + Utf8String filePath{Utf8StringLiteral(TESTDATA_DIR"/translationunits.cpp")}; + Utf8String projectPartId{Utf8StringLiteral("/path/to/projectfile")}; + + ClangBackEnd::JobRequest jobRequest; + ClangBackEnd::JobContext jobContext; + + ClangBackEnd::DocumentProcessors documentProcessors{documents, + unsavedFiles, + projects, + dummyIpcClient}; +}; + +TEST_F(DocumentProcessors, HasNoItemsInitially) +{ + ASSERT_TRUE(documentProcessors.processors().empty()); +} + +TEST_F(DocumentProcessors, CreateAddsADocumentProcessor) +{ + documentProcessors.create(document); + + ASSERT_THAT(documentProcessors.processors().size(), Eq(1)); +} + +TEST_F(DocumentProcessors, CreateReturnsDocumentProcessor) +{ + const DocumentProcessor documentProcessor = documentProcessors.create(document); + + ASSERT_THAT(documentProcessor.document(), Eq(document)); +} + +TEST_F(DocumentProcessors, CreateThrowsForAlreadyExisting) +{ + documentProcessors.create(document); + + ASSERT_THROW(documentProcessors.create(document), + ClangBackEnd::DocumentProcessorAlreadyExists); +} + +TEST_F(DocumentProcessors, Access) +{ + documentProcessors.create(document); + + const DocumentProcessor documentProcessor = documentProcessors.processor(document); + + ASSERT_THAT(documentProcessor.document(), Eq(document)); +} + +TEST_F(DocumentProcessors, AccessThrowsForNotExisting) +{ + ASSERT_THROW(documentProcessors.processor(document), + ClangBackEnd::DocumentProcessorDoesNotExist); +} + +TEST_F(DocumentProcessors, Remove) +{ + documentProcessors.create(document); + + documentProcessors.remove(document); + + ASSERT_TRUE(documentProcessors.processors().empty()); +} + +TEST_F(DocumentProcessors, RemoveThrowsForNotExisting) +{ + ASSERT_THROW(documentProcessors.remove(document), + ClangBackEnd::DocumentProcessorDoesNotExist); +} + +TEST_F(DocumentProcessors, ProcessEmpty) +{ + documentProcessors.create(document); + + const JobRequests jobsStarted = documentProcessors.process(); + + ASSERT_TRUE(jobsStarted.isEmpty()); +} + +TEST_F(DocumentProcessors, ProcessSingle) +{ + DocumentProcessor documentProcessor = documentProcessors.create(document); + const JobRequest jobRequest = createJobRequest(JobRequest::Type::UpdateDocumentAnnotations); + documentProcessor.addJob(jobRequest); + + const JobRequests jobsStarted = documentProcessors.process(); + + ASSERT_THAT(jobsStarted.size(), 1); +} + +void DocumentProcessors::SetUp() +{ + projects.createOrUpdate({ProjectPartContainer(projectPartId)}); + + const QVector fileContainer{FileContainer(filePath, projectPartId)}; + document = documents.create(fileContainer).front(); + documents.setVisibleInEditors({filePath}); + documents.setUsedByCurrentEditor(filePath); +} + +void DocumentProcessors::TearDown() +{ + ASSERT_TRUE(waitUntilAllJobsFinished()); // QFuture/QFutureWatcher is implemented with events +} + +JobRequest DocumentProcessors::createJobRequest(JobRequest::Type type) const +{ + JobRequest jobRequest; + jobRequest.type = type; + jobRequest.requirements = JobRequest::requirementsForType(type); + jobRequest.filePath = filePath; + jobRequest.projectPartId = projectPartId; + jobRequest.unsavedFilesChangeTimePoint = unsavedFiles.lastChangeTimePoint(); + jobRequest.documentRevision = document.documentRevision(); + jobRequest.projectChangeTimePoint = projects.project(projectPartId).lastChangeTimePoint(); + + return jobRequest; +} + +bool DocumentProcessors::waitUntilAllJobsFinished(int timeOutInMs) const +{ + const auto noJobsRunningAnymore = [this](){ return documentProcessors.runningJobs().isEmpty(); }; + + return ProcessEventUtilities::processEventsUntilTrue(noJobsRunningAnymore, timeOutInMs); +} + +} // anonymous diff --git a/tests/unit/unittest/clangipcserver-test.cpp b/tests/unit/unittest/clangipcserver-test.cpp index 44e631323af..3aa7ed33f38 100644 --- a/tests/unit/unittest/clangipcserver-test.cpp +++ b/tests/unit/unittest/clangipcserver-test.cpp @@ -323,8 +323,8 @@ void ClangClangCodeModelServer::TearDown() bool ClangClangCodeModelServer::waitUntilAllJobsFinished(int timeOutInMs) { const auto noJobsRunningAnymore = [this]() { - return clangServer.jobsForTestOnly().runningJobs() == 0 - && clangServer.jobsForTestOnly().queue().size() == 0 + return clangServer.runningJobsForTestsOnly().isEmpty() + && clangServer.queueSizeForTestsOnly() == 0 && !clangServer.isTimerRunningForTestOnly(); }; diff --git a/tests/unit/unittest/clangjobs-test.cpp b/tests/unit/unittest/clangjobs-test.cpp index 3c642343351..d5ef57155b5 100644 --- a/tests/unit/unittest/clangjobs-test.cpp +++ b/tests/unit/unittest/clangjobs-test.cpp @@ -79,7 +79,7 @@ TEST_F(Jobs, ProcessEmptyQueue) const JobRequests jobsStarted = jobs.process(); ASSERT_THAT(jobsStarted.size(), Eq(0)); - ASSERT_THAT(jobs.runningJobs(), Eq(0)); + ASSERT_TRUE(jobs.runningJobs().isEmpty()); } TEST_F(Jobs, ProcessQueueWithSingleJob) @@ -89,7 +89,7 @@ TEST_F(Jobs, ProcessQueueWithSingleJob) const JobRequests jobsStarted = jobs.process(); ASSERT_THAT(jobsStarted.size(), Eq(1)); - ASSERT_THAT(jobs.runningJobs(), Eq(1)); + ASSERT_THAT(jobs.runningJobs().size(), Eq(1)); } TEST_F(Jobs, ProcessQueueUntilEmpty) @@ -130,7 +130,7 @@ void Jobs::TearDown() bool Jobs::waitUntilAllJobsFinished(int timeOutInMs) const { - const auto noJobsRunningAnymore = [this](){ return jobs.runningJobs() == 0; }; + const auto noJobsRunningAnymore = [this](){ return jobs.runningJobs().isEmpty(); }; return ProcessEventUtilities::processEventsUntilTrue(noJobsRunningAnymore, timeOutInMs); } @@ -138,7 +138,7 @@ bool Jobs::waitUntilAllJobsFinished(int timeOutInMs) const bool Jobs::waitUntilJobChainFinished(int timeOutInMs) const { const auto noJobsRunningAnymore = [this]() { - return jobs.runningJobs() == 0 && jobs.queue().isEmpty(); + return jobs.runningJobs().isEmpty() && jobs.queue().isEmpty(); }; return ProcessEventUtilities::processEventsUntilTrue(noJobsRunningAnymore, timeOutInMs); diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index 6c40fbb8ece..48bd0a59fa8 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -49,6 +49,8 @@ SOURCES += \ clangdiagnosticfilter-test.cpp \ clangdocuments-test.cpp \ clangdocument-test.cpp \ + clangdocumentprocessor-test.cpp \ + clangdocumentprocessors-test.cpp \ clangfixitoperation-test.cpp \ clangipcserver-test.cpp \ clangisdiagnosticrelatedtolocation-test.cpp \