Clang: Add job queue for each document

...in preparation for follow-up changes. This will enable e.g. a timer
per document.

This does not change any behavior yet.

Change-Id: Ic1dc06de602373c666d47ce7a95ab99e56d389d5
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Nikolai Kosjar
2016-09-08 15:49:54 +02:00
parent b64bb0a7e3
commit c12d01fb41
18 changed files with 771 additions and 43 deletions

View File

@@ -64,6 +64,11 @@ public:
return future; return future;
} }
void preventFinalization() override
{
m_futureWatcher.disconnect();
}
private: private:
Runner m_runner; Runner m_runner;
QFutureWatcher<Result> m_futureWatcher; QFutureWatcher<Result> m_futureWatcher;

View File

@@ -41,7 +41,9 @@ HEADERS += $$PWD/clangcodemodelserver.h \
$$PWD/clangtranslationunit.h \ $$PWD/clangtranslationunit.h \
$$PWD/clangunsavedfilesshallowarguments.h \ $$PWD/clangunsavedfilesshallowarguments.h \
$$PWD/clangupdatedocumentannotationsjob.h \ $$PWD/clangupdatedocumentannotationsjob.h \
$$PWD/clangexceptions.h $$PWD/clangexceptions.h \
$$PWD/clangdocumentprocessor.h \
$$PWD/clangdocumentprocessors.h \
SOURCES += $$PWD/clangcodemodelserver.cpp \ SOURCES += $$PWD/clangcodemodelserver.cpp \
$$PWD/codecompleter.cpp \ $$PWD/codecompleter.cpp \
@@ -80,4 +82,6 @@ SOURCES += $$PWD/clangcodemodelserver.cpp \
$$PWD/clangtranslationunit.cpp \ $$PWD/clangtranslationunit.cpp \
$$PWD/clangunsavedfilesshallowarguments.cpp \ $$PWD/clangunsavedfilesshallowarguments.cpp \
$$PWD/clangupdatedocumentannotationsjob.cpp \ $$PWD/clangupdatedocumentannotationsjob.cpp \
$$PWD/clangexceptions.cpp $$PWD/clangexceptions.cpp \
$$PWD/clangdocumentprocessor.cpp \
$$PWD/clangdocumentprocessors.cpp \

View File

@@ -141,6 +141,10 @@ void ClangCodeModelServer::unregisterTranslationUnitsForEditor(const ClangBackEn
TIME_SCOPE_DURATION("ClangCodeModelServer::unregisterTranslationUnitsForEditor"); TIME_SCOPE_DURATION("ClangCodeModelServer::unregisterTranslationUnitsForEditor");
try { try {
for (const auto &fileContainer : message.fileContainers()) {
const Document &document = documents.document(fileContainer);
documentProcessors().remove(document);
}
documents.remove(message.fileContainers()); documents.remove(message.fileContainers());
unsavedFiles.remove(message.fileContainers()); unsavedFiles.remove(message.fileContainers());
} catch (const std::exception &exception) { } catch (const std::exception &exception) {
@@ -211,8 +215,9 @@ void ClangCodeModelServer::completeCode(const ClangBackEnd::CompleteCodeMessage
jobRequest.column = message.column(); jobRequest.column = message.column();
jobRequest.ticketNumber = message.ticketNumber(); jobRequest.ticketNumber = message.ticketNumber();
jobs().add(jobRequest); DocumentProcessor processor = documentProcessors().processor(document);
jobs().process(); processor.addJob(jobRequest);
processor.process();
} catch (const std::exception &exception) { } catch (const std::exception &exception) {
qWarning() << "Error in ClangCodeModelServer::completeCode:" << exception.what(); qWarning() << "Error in ClangCodeModelServer::completeCode:" << exception.what();
} }
@@ -229,8 +234,9 @@ void ClangCodeModelServer::requestDocumentAnnotations(const RequestDocumentAnnot
const JobRequest jobRequest = createJobRequest(document, const JobRequest jobRequest = createJobRequest(document,
JobRequest::Type::RequestDocumentAnnotations); JobRequest::Type::RequestDocumentAnnotations);
jobs().add(jobRequest); DocumentProcessor processor = documentProcessors().processor(document);
jobs().process(); processor.addJob(jobRequest);
processor.process();
} catch (const std::exception &exception) { } catch (const std::exception &exception) {
qWarning() << "Error in ClangCodeModelServer::requestDocumentAnnotations:" << exception.what(); qWarning() << "Error in ClangCodeModelServer::requestDocumentAnnotations:" << exception.what();
} }
@@ -260,9 +266,14 @@ void ClangCodeModelServer::startDocumentAnnotationsTimerIfFileIsNotOpenAsDocumen
updateDocumentAnnotationsTimer.start(0); updateDocumentAnnotationsTimer.start(0);
} }
const Jobs &ClangCodeModelServer::jobsForTestOnly() QList<Jobs::RunningJob> ClangCodeModelServer::runningJobsForTestsOnly()
{ {
return jobs(); return documentProcessors().runningJobs();
}
int ClangCodeModelServer::queueSizeForTestsOnly()
{
return documentProcessors().queueSize();
} }
bool ClangCodeModelServer::isTimerRunningForTestOnly() const bool ClangCodeModelServer::isTimerRunningForTestOnly() const
@@ -270,28 +281,26 @@ bool ClangCodeModelServer::isTimerRunningForTestOnly() const
return updateDocumentAnnotationsTimer.isActive(); 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() void ClangCodeModelServer::processJobsForDirtyAndVisibleDocuments()
{ {
addJobRequestsForDirtyAndVisibleDocuments(); for (const auto &document : documents.documents()) {
jobs().process(); 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<Document> &documents) void ClangCodeModelServer::processInitialJobsForDocuments(const std::vector<Document> &documents)
{ {
for (const auto &document : documents) { for (const auto &document : documents) {
jobs().add(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations)); DocumentProcessor processor = documentProcessors().create(document);
jobs().add(createJobRequest(document, JobRequest::Type::CreateInitialDocumentPreamble)); processor.addJob(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations));
processor.addJob(createJobRequest(document, JobRequest::Type::CreateInitialDocumentPreamble));
processor.process();
} }
jobs().process();
} }
JobRequest ClangCodeModelServer::createJobRequest(const Document &document, JobRequest ClangCodeModelServer::createJobRequest(const Document &document,
@@ -315,16 +324,16 @@ void ClangCodeModelServer::setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(i
updateDocumentAnnotationsTimeOutInMs = value; updateDocumentAnnotationsTimeOutInMs = value;
} }
Jobs &ClangCodeModelServer::jobs() DocumentProcessors &ClangCodeModelServer::documentProcessors()
{ {
if (!jobs_) { if (!documentProcessors_) {
// Jobs needs a reference to the client, but the client is not known at // DocumentProcessors needs a reference to the client, but the client
// construction time of ClangCodeModelServer, so construct Jobs in a // is not known at construction time of ClangCodeModelServer, so
// lazy manner. // construct DocumentProcessors in a lazy manner.
jobs_.reset(new Jobs(documents, unsavedFiles, projects, *client())); documentProcessors_.reset(new DocumentProcessors(documents, unsavedFiles, projects, *client()));
} }
return *jobs_.data(); return *documentProcessors_.data();
} }
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -31,8 +31,9 @@
#include "projects.h" #include "projects.h"
#include "clangdocument.h" #include "clangdocument.h"
#include "clangdocuments.h" #include "clangdocuments.h"
#include "clangdocumentprocessors.h"
#include "clangjobrequest.h"
#include "unsavedfiles.h" #include "unsavedfiles.h"
#include "clangjobs.h"
#include <utf8string.h> #include <utf8string.h>
@@ -58,14 +59,15 @@ public:
void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override; void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override;
void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override; void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override;
public /*for tests*/: public: // for tests
const Documents &documentsForTestOnly() const; const Documents &documentsForTestOnly() const;
const Jobs &jobsForTestOnly(); QList<Jobs::RunningJob> runningJobsForTestsOnly();
int queueSizeForTestsOnly();
bool isTimerRunningForTestOnly() const; bool isTimerRunningForTestOnly() const;
void setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(int value); void setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(int value);
private: private:
Jobs &jobs(); DocumentProcessors &documentProcessors();
void startDocumentAnnotationsTimerIfFileIsNotOpenAsDocument(const Utf8String &filePath); void startDocumentAnnotationsTimerIfFileIsNotOpenAsDocument(const Utf8String &filePath);
void addJobRequestsForDirtyAndVisibleDocuments(); void addJobRequestsForDirtyAndVisibleDocuments();
@@ -78,7 +80,8 @@ private:
ProjectParts projects; ProjectParts projects;
UnsavedFiles unsavedFiles; UnsavedFiles unsavedFiles;
Documents documents; Documents documents;
QScopedPointer<Jobs> jobs_;
QScopedPointer<DocumentProcessors> documentProcessors_; // Delayed initialization
QTimer updateDocumentAnnotationsTimer; QTimer updateDocumentAnnotationsTimer;
int updateDocumentAnnotationsTimeOutInMs; int updateDocumentAnnotationsTimeOutInMs;

View File

@@ -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<DocumentProcessorData>(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<Jobs::RunningJob> DocumentProcessor::runningJobs() const
{
return d->jobs.runningJobs();
}
int DocumentProcessor::queueSize() const
{
return d->jobs.queue().size();
}
} // namespace ClangBackEnd

View File

@@ -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 <memory>
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<Jobs::RunningJob> runningJobs() const;
int queueSize() const;
private:
std::shared_ptr<DocumentProcessorData> d;
};
} // namespace ClangBackEnd

View File

@@ -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<DocumentProcessor> 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<Jobs::RunningJob> DocumentProcessors::runningJobs() const
{
QList<Jobs::RunningJob> 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

View File

@@ -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 <utf8string.h>
#include <QMap>
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<DocumentProcessor> processors() const;
QList<Jobs::RunningJob> runningJobs() const;
int queueSize() const;
private:
Documents &m_documents;
UnsavedFiles &m_unsavedFiles;
ProjectParts &m_projects;
ClangCodeModelClientInterface &m_client;
QMap<DocumentId, DocumentProcessor> m_processors;
};
} // namespace ClangBackEnd

View File

@@ -74,4 +74,24 @@ DocumentIsNullException::DocumentIsNullException()
m_info = Utf8String::fromUtf8("Tried to access a null Document!"); 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 } // namespace ClangBackEnd

View File

@@ -74,4 +74,18 @@ public:
DocumentIsNullException(); 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 } // namespace ClangBackEnd

View File

@@ -56,6 +56,8 @@ public:
virtual QFuture<void> runAsync() = 0; virtual QFuture<void> runAsync() = 0;
virtual void finalizeAsyncRun() = 0; virtual void finalizeAsyncRun() = 0;
virtual void preventFinalization() = 0;
public: // for tests public: // for tests
bool isFinished() const; bool isFinished() const;
void setIsFinished(bool isFinished); void setIsFinished(bool isFinished);

View File

@@ -53,9 +53,15 @@ Jobs::Jobs(Documents &documents,
Jobs::~Jobs() Jobs::~Jobs()
{ {
foreach (IAsyncJob *asyncJob, m_running.keys())
asyncJob->preventFinalization();
QFutureSynchronizer<void> waitForFinishedJobs; QFutureSynchronizer<void> waitForFinishedJobs;
foreach (const RunningJob &runningJob, m_running.values()) foreach (const RunningJob &runningJob, m_running.values())
waitForFinishedJobs.addFuture(runningJob.future); waitForFinishedJobs.addFuture(runningJob.future);
foreach (IAsyncJob *asyncJob, m_running.keys())
delete asyncJob;
} }
void Jobs::add(const JobRequest &job) void Jobs::add(const JobRequest &job)
@@ -118,9 +124,9 @@ void Jobs::onJobFinished(IAsyncJob *asyncJob)
process(); process();
} }
int Jobs::runningJobs() const QList<Jobs::RunningJob> Jobs::runningJobs() const
{ {
return m_running.size(); return m_running.values();
} }
JobRequests Jobs::queue() const JobRequests Jobs::queue() const

View File

@@ -59,7 +59,7 @@ public:
JobRequests process(); JobRequests process();
public /*for tests*/: public /*for tests*/:
int runningJobs() const; QList<RunningJob> runningJobs() const;
JobRequests queue() const; JobRequests queue() const;
bool isJobRunning(const Utf8String &filePath, const Utf8String &projectPartId) const; bool isJobRunning(const Utf8String &filePath, const Utf8String &projectPartId) const;

View File

@@ -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 <clangdocument.h>
#include <clangdocumentprocessor.h>
#include <clangdocuments.h>
#include <clangjobrequest.h>
#include <clangjobs.h>
#include <projects.h>
#include <unsavedfiles.h>
#include <gmock/gmock.h>
#include <gmock/gmock-matchers.h>
#include <gtest/gtest.h>
#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{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

View File

@@ -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 <clangdocument.h>
#include <clangdocumentprocessor.h>
#include <clangdocumentprocessors.h>
#include <clangdocuments.h>
#include <clangexceptions.h>
#include <clangjobrequest.h>
#include <clangjobs.h>
#include <projects.h>
#include <unsavedfiles.h>
#include <gmock/gmock.h>
#include <gmock/gmock-matchers.h>
#include <gtest/gtest.h>
#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{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

View File

@@ -323,8 +323,8 @@ void ClangClangCodeModelServer::TearDown()
bool ClangClangCodeModelServer::waitUntilAllJobsFinished(int timeOutInMs) bool ClangClangCodeModelServer::waitUntilAllJobsFinished(int timeOutInMs)
{ {
const auto noJobsRunningAnymore = [this]() { const auto noJobsRunningAnymore = [this]() {
return clangServer.jobsForTestOnly().runningJobs() == 0 return clangServer.runningJobsForTestsOnly().isEmpty()
&& clangServer.jobsForTestOnly().queue().size() == 0 && clangServer.queueSizeForTestsOnly() == 0
&& !clangServer.isTimerRunningForTestOnly(); && !clangServer.isTimerRunningForTestOnly();
}; };

View File

@@ -79,7 +79,7 @@ TEST_F(Jobs, ProcessEmptyQueue)
const JobRequests jobsStarted = jobs.process(); const JobRequests jobsStarted = jobs.process();
ASSERT_THAT(jobsStarted.size(), Eq(0)); ASSERT_THAT(jobsStarted.size(), Eq(0));
ASSERT_THAT(jobs.runningJobs(), Eq(0)); ASSERT_TRUE(jobs.runningJobs().isEmpty());
} }
TEST_F(Jobs, ProcessQueueWithSingleJob) TEST_F(Jobs, ProcessQueueWithSingleJob)
@@ -89,7 +89,7 @@ TEST_F(Jobs, ProcessQueueWithSingleJob)
const JobRequests jobsStarted = jobs.process(); const JobRequests jobsStarted = jobs.process();
ASSERT_THAT(jobsStarted.size(), Eq(1)); ASSERT_THAT(jobsStarted.size(), Eq(1));
ASSERT_THAT(jobs.runningJobs(), Eq(1)); ASSERT_THAT(jobs.runningJobs().size(), Eq(1));
} }
TEST_F(Jobs, ProcessQueueUntilEmpty) TEST_F(Jobs, ProcessQueueUntilEmpty)
@@ -130,7 +130,7 @@ void Jobs::TearDown()
bool Jobs::waitUntilAllJobsFinished(int timeOutInMs) const 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); return ProcessEventUtilities::processEventsUntilTrue(noJobsRunningAnymore, timeOutInMs);
} }
@@ -138,7 +138,7 @@ bool Jobs::waitUntilAllJobsFinished(int timeOutInMs) const
bool Jobs::waitUntilJobChainFinished(int timeOutInMs) const bool Jobs::waitUntilJobChainFinished(int timeOutInMs) const
{ {
const auto noJobsRunningAnymore = [this]() { const auto noJobsRunningAnymore = [this]() {
return jobs.runningJobs() == 0 && jobs.queue().isEmpty(); return jobs.runningJobs().isEmpty() && jobs.queue().isEmpty();
}; };
return ProcessEventUtilities::processEventsUntilTrue(noJobsRunningAnymore, timeOutInMs); return ProcessEventUtilities::processEventsUntilTrue(noJobsRunningAnymore, timeOutInMs);

View File

@@ -49,6 +49,8 @@ SOURCES += \
clangdiagnosticfilter-test.cpp \ clangdiagnosticfilter-test.cpp \
clangdocuments-test.cpp \ clangdocuments-test.cpp \
clangdocument-test.cpp \ clangdocument-test.cpp \
clangdocumentprocessor-test.cpp \
clangdocumentprocessors-test.cpp \
clangfixitoperation-test.cpp \ clangfixitoperation-test.cpp \
clangipcserver-test.cpp \ clangipcserver-test.cpp \
clangisdiagnosticrelatedtolocation-test.cpp \ clangisdiagnosticrelatedtolocation-test.cpp \