diff options
28 files changed, 1021 insertions, 6 deletions
diff --git a/src/tools/clangbackend/ipcsource/clangbackend_global.h b/src/tools/clangbackend/ipcsource/clangbackend_global.h index 0dccaa87ad..d806afc98c 100644 --- a/src/tools/clangbackend/ipcsource/clangbackend_global.h +++ b/src/tools/clangbackend/ipcsource/clangbackend_global.h @@ -25,6 +25,8 @@ #pragma once +#include <clang-c/Index.h> + namespace ClangBackEnd { enum class PreferredTranslationUnit @@ -34,4 +36,9 @@ enum class PreferredTranslationUnit LastUninitialized, }; +// CLANG-UPGRADE-CHECK: Remove IS_SUSPEND_SUPPORTED once we require clang >= 5.0 +#if defined(CINDEX_VERSION_HAS_BACKPORTED_SUSPEND) || CINDEX_VERSION_MINOR >= 41 +# define IS_SUSPEND_SUPPORTED +#endif + } // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri index 06be9be18d..877d63ca9c 100644 --- a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri +++ b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri @@ -12,6 +12,7 @@ HEADERS += \ $$PWD/clangdocumentprocessor.h \ $$PWD/clangdocumentprocessors.h \ $$PWD/clangdocuments.h \ + $$PWD/clangdocumentsuspenderresumer.h \ $$PWD/clangexceptions.h \ $$PWD/clangfilepath.h \ $$PWD/clangfilesystemwatcher.h \ @@ -25,8 +26,10 @@ HEADERS += \ $$PWD/clangreparsesupportivetranslationunitjob.h \ $$PWD/clangrequestdocumentannotationsjob.h \ $$PWD/clangrequestreferencesjob.h \ + $$PWD/clangresumedocumentjob.h \ $$PWD/clangstring.h \ $$PWD/clangsupportivetranslationunitinitializer.h \ + $$PWD/clangsuspenddocumentjob.h \ $$PWD/clangtranslationunit.h \ $$PWD/clangtranslationunits.h \ $$PWD/clangtranslationunitupdater.h \ @@ -63,6 +66,7 @@ SOURCES += \ $$PWD/clangdocumentprocessor.cpp \ $$PWD/clangdocumentprocessors.cpp \ $$PWD/clangdocuments.cpp \ + $$PWD/clangdocumentsuspenderresumer.cpp \ $$PWD/clangexceptions.cpp \ $$PWD/clangfilepath.cpp \ $$PWD/clangfilesystemwatcher.cpp \ @@ -72,10 +76,12 @@ SOURCES += \ $$PWD/clangjobrequest.cpp \ $$PWD/clangjobs.cpp \ $$PWD/clangparsesupportivetranslationunitjob.cpp \ + $$PWD/clangresumedocumentjob.cpp \ $$PWD/clangreferencescollector.cpp \ $$PWD/clangreparsesupportivetranslationunitjob.cpp \ $$PWD/clangrequestdocumentannotationsjob.cpp \ $$PWD/clangrequestreferencesjob.cpp \ + $$PWD/clangsuspenddocumentjob.cpp \ $$PWD/clangsupportivetranslationunitinitializer.cpp \ $$PWD/clangtranslationunit.cpp \ $$PWD/clangtranslationunits.cpp \ diff --git a/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp b/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp index ffbf7fd94c..0e54fce5c3 100644 --- a/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp +++ b/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp @@ -26,6 +26,7 @@ #include "clangcodemodelserver.h" #include "clangdocuments.h" +#include "clangdocumentsuspenderresumer.h" #include "clangfilesystemwatcher.h" #include "clangtranslationunits.h" #include "codecompleter.h" @@ -89,6 +90,7 @@ void ClangCodeModelServer::registerTranslationUnitsForEditor(const ClangBackEnd: unsavedFiles.createOrUpdate(message.fileContainers()); documents.setUsedByCurrentEditor(message.currentEditorFilePath()); documents.setVisibleInEditors(message.visibleEditorFilePaths()); + processSuspendResumeJobs(documents.documents()); processInitialJobsForDocuments(createdDocuments); } catch (const std::exception &exception) { @@ -264,6 +266,8 @@ void ClangCodeModelServer::updateVisibleTranslationUnits(const UpdateVisibleTran try { documents.setUsedByCurrentEditor(message.currentEditorFilePath()); documents.setVisibleInEditors(message.visibleEditorFilePaths()); + processSuspendResumeJobs(documents.documents()); + updateDocumentAnnotationsTimer.start(0); } catch (const std::exception &exception) { qWarning() << "Error in ClangCodeModelServer::updateVisibleTranslationUnits:" << exception.what(); @@ -347,6 +351,16 @@ void ClangCodeModelServer::processJobsForDirtyAndVisibleButNotCurrentDocuments() addAndRunUpdateJobs(documents.dirtyAndVisibleButNotCurrentDocuments()); } +void ClangCodeModelServer::processSuspendResumeJobs(const std::vector<Document> &documents) +{ + const SuspendResumeJobs suspendResumeJobs = createSuspendResumeJobs(documents); + for (const SuspendResumeJobsEntry &entry : suspendResumeJobs) { + DocumentProcessor processor = documentProcessors().processor(entry.document); + processor.addJob(entry.jobRequestType, entry.preferredTranslationUnit); + processor.process(); + } +} + void ClangCodeModelServer::processInitialJobsForDocuments(const std::vector<Document> &documents) { for (const auto &document : documents) { diff --git a/src/tools/clangbackend/ipcsource/clangcodemodelserver.h b/src/tools/clangbackend/ipcsource/clangcodemodelserver.h index 70d99b5afa..d5fc33250b 100644 --- a/src/tools/clangbackend/ipcsource/clangcodemodelserver.h +++ b/src/tools/clangbackend/ipcsource/clangcodemodelserver.h @@ -77,6 +77,7 @@ private: void processJobsForDirtyCurrentDocument(); void processTimerForVisibleButNotCurrentDocuments(); void processJobsForDirtyAndVisibleButNotCurrentDocuments(); + void processSuspendResumeJobs(const std::vector<Document> &documents); void addAndRunUpdateJobs(std::vector<Document> documents); diff --git a/src/tools/clangbackend/ipcsource/clangdocument.cpp b/src/tools/clangbackend/ipcsource/clangdocument.cpp index 933bf61276..a4f95d17b8 100644 --- a/src/tools/clangbackend/ipcsource/clangdocument.cpp +++ b/src/tools/clangbackend/ipcsource/clangdocument.cpp @@ -80,6 +80,7 @@ public: bool isUsedByCurrentEditor = false; bool isVisibleInEditor = false; bool increaseResponsiveness = false; + bool isSuspended = false; }; DocumentData::DocumentData(const Utf8String &filePath, @@ -226,6 +227,20 @@ void Document::setResponsivenessIncreaseNeeded(bool responsivenessIncreaseNeeded d->increaseResponsiveness = responsivenessIncreaseNeeded; } +bool Document::isSuspended() const +{ + checkIfNull(); + + return d->isSuspended; +} + +void Document::setIsSuspended(bool isSuspended) +{ + checkIfNull(); + + d->isSuspended = isSuspended; +} + bool Document::isUsedByCurrentEditor() const { checkIfNull(); diff --git a/src/tools/clangbackend/ipcsource/clangdocument.h b/src/tools/clangbackend/ipcsource/clangdocument.h index 48dc32acee..260705c135 100644 --- a/src/tools/clangbackend/ipcsource/clangdocument.h +++ b/src/tools/clangbackend/ipcsource/clangdocument.h @@ -93,6 +93,9 @@ public: bool isResponsivenessIncreaseNeeded() const; void setResponsivenessIncreaseNeeded(bool responsivenessIncreaseNeeded); + bool isSuspended() const; + void setIsSuspended(bool isSuspended); + bool isUsedByCurrentEditor() const; void setIsUsedByCurrentEditor(bool isUsedByCurrentEditor); diff --git a/src/tools/clangbackend/ipcsource/clangdocumentsuspenderresumer.cpp b/src/tools/clangbackend/ipcsource/clangdocumentsuspenderresumer.cpp new file mode 100644 index 0000000000..8edde48fd6 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangdocumentsuspenderresumer.cpp @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** 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 "clangdocumentsuspenderresumer.h" + +#include "clangbackendipc_global.h" +#include "clangdocumentprocessors.h" +#include "clangdocuments.h" + +#include <utils/algorithm.h> + +#include <algorithm> + +namespace ClangBackEnd { + +constexpr int DefaultHotDocumentsSize = 7; + +void categorizeHotColdDocuments(int hotDocumentsSize, + const std::vector<Document> &inDocuments, + std::vector<Document> &hotDocuments, + std::vector<Document> &coldDocuments) +{ + // Sort documents, most recently used/visible at top + std::vector<Document> documents = inDocuments; + std::stable_sort(documents.begin(), documents.end(), [](const Document &a, const Document &b) { + return a.visibleTimePoint() > b.visibleTimePoint(); + }); + + // Ensure that visible documents are always hot, otherwise not all visible + // documents will be resumed. + const auto isVisible = [](const Document &document) { return document.isVisibleInEditor(); }; + const int visibleDocumentsSize = Utils::count(documents, isVisible); + hotDocumentsSize = std::max(hotDocumentsSize, visibleDocumentsSize); + + if (documents.size() <= uint(hotDocumentsSize)) { + hotDocuments = documents; + coldDocuments.clear(); + } else { + const auto firstColdIterator = documents.begin() + hotDocumentsSize; + hotDocuments = std::vector<Document>(documents.begin(), firstColdIterator); + coldDocuments = std::vector<Document>(firstColdIterator, documents.end()); + } +} + +#ifdef IS_SUSPEND_SUPPORTED +static int hotDocumentsSize() +{ + static int hotDocuments = -1; + if (hotDocuments == -1) { + bool ok = false; + const int fromEnvironment = qEnvironmentVariableIntValue("QTC_CLANG_HOT_DOCUMENTS", &ok); + hotDocuments = ok && fromEnvironment >= 1 ? fromEnvironment : DefaultHotDocumentsSize; + } + + return hotDocuments; +} + +static SuspendResumeJobs createJobs(const Document &document, JobRequest::Type type) +{ + SuspendResumeJobs jobs; + + jobs.append({document, type, PreferredTranslationUnit::RecentlyParsed}); + if (document.isResponsivenessIncreased()) + jobs.append({document, type, PreferredTranslationUnit::PreviouslyParsed}); + + return jobs; +} + +static bool isFineDocument(const Document &document) +{ + return !document.isNull() && document.isIntact(); +} + +static bool isSuspendable(const Document &document) +{ + return isFineDocument(document) + && !document.isSuspended() + && !document.isVisibleInEditor(); +} + +static bool isResumable(const Document &document) +{ + return isFineDocument(document) + && document.isSuspended() + && document.isVisibleInEditor(); +} + +#endif // IS_SUSPEND_SUPPORTED + +SuspendResumeJobs createSuspendResumeJobs(const std::vector<Document> &documents, + int customHotDocumentSize) +{ + Q_UNUSED(documents); + Q_UNUSED(customHotDocumentSize); + + SuspendResumeJobs jobs; + +#ifdef IS_SUSPEND_SUPPORTED + std::vector<Document> hotDocuments; + std::vector<Document> coldDocuments; + + const int size = (customHotDocumentSize == -1) ? hotDocumentsSize() : customHotDocumentSize; + categorizeHotColdDocuments(size, documents, hotDocuments, coldDocuments); + + // Cold documents should be suspended... + const std::vector<Document> toSuspend = Utils::filtered(coldDocuments, &isSuspendable); + for (const Document &document : toSuspend) + jobs += createJobs(document, JobRequest::Type::SuspendDocument); + + // ...and hot documents that were suspended should be resumed + const std::vector<Document> toResume = Utils::filtered(hotDocuments, &isResumable); + for (const Document &document : toResume) + jobs += createJobs(document, JobRequest::Type::ResumeDocument); +#endif // IS_SUSPEND_SUPPORTED + + return jobs; +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangdocumentsuspenderresumer.h b/src/tools/clangbackend/ipcsource/clangdocumentsuspenderresumer.h new file mode 100644 index 0000000000..7e2bd8722d --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangdocumentsuspenderresumer.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** 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 "clangdocument.h" +#include "clangjobrequest.h" + +#include <vector> + +namespace ClangBackEnd { + +class SuspendResumeJobsEntry { +public: + SuspendResumeJobsEntry() = default; + SuspendResumeJobsEntry(const Document &document, + JobRequest::Type jobRequestType, + PreferredTranslationUnit preferredTranslationUnit) + : document(document) + , jobRequestType(jobRequestType) + , preferredTranslationUnit(preferredTranslationUnit) + { + } + + Document document; + JobRequest::Type jobRequestType = JobRequest::Type::SuspendDocument; + PreferredTranslationUnit preferredTranslationUnit = PreferredTranslationUnit::RecentlyParsed; +}; +using SuspendResumeJobs = QVector<SuspendResumeJobsEntry>; + +SuspendResumeJobs createSuspendResumeJobs(const std::vector<Document> &documents, + int customHotDocumentCounts = -1); + +// for tests +void categorizeHotColdDocuments(int hotDocumentsSize, + const std::vector<Document> &inDocuments, + std::vector<Document> &hotDocuments, + std::vector<Document> &coldDocuments); + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangiasyncjob.cpp b/src/tools/clangbackend/ipcsource/clangiasyncjob.cpp index 14de731eb6..db3aebfe8e 100644 --- a/src/tools/clangbackend/ipcsource/clangiasyncjob.cpp +++ b/src/tools/clangbackend/ipcsource/clangiasyncjob.cpp @@ -31,6 +31,8 @@ #include "clangreparsesupportivetranslationunitjob.h" #include "clangrequestdocumentannotationsjob.h" #include "clangrequestreferencesjob.h" +#include "clangresumedocumentjob.h" +#include "clangsuspenddocumentjob.h" #include "clangupdatedocumentannotationsjob.h" Q_LOGGING_CATEGORY(jobsLog, "qtc.clangbackend.jobs"); @@ -54,6 +56,10 @@ IAsyncJob *IAsyncJob::create(JobRequest::Type type) return new RequestDocumentAnnotationsJob(); case JobRequest::Type::RequestReferences: return new RequestReferencesJob(); + case JobRequest::Type::SuspendDocument: + return new SuspendDocumentJob(); + case JobRequest::Type::ResumeDocument: + return new ResumeDocumentJob(); } return nullptr; diff --git a/src/tools/clangbackend/ipcsource/clangjobqueue.cpp b/src/tools/clangbackend/ipcsource/clangjobqueue.cpp index 7af76ce32d..8c9ace68e6 100644 --- a/src/tools/clangbackend/ipcsource/clangjobqueue.cpp +++ b/src/tools/clangbackend/ipcsource/clangjobqueue.cpp @@ -179,6 +179,16 @@ static bool passesPreconditions(const JobRequest &request, const Document &docum using Condition = JobRequest::Condition; const JobRequest::Conditions conditions = request.conditions; + if (conditions.testFlag(Condition::DocumentSuspended) && !document.isSuspended()) { + qCDebug(jobsLog) << "Not choosing due to unsuspended document:" << request; + return false; + } + + if (conditions.testFlag(Condition::DocumentUnsuspended) && document.isSuspended()) { + qCDebug(jobsLog) << "Not choosing due to suspended document:" << request; + return false; + } + if (conditions.testFlag(Condition::DocumentVisible) && !document.isVisibleInEditor()) { qCDebug(jobsLog) << "Not choosing due to invisble document:" << request; return false; diff --git a/src/tools/clangbackend/ipcsource/clangjobrequest.cpp b/src/tools/clangbackend/ipcsource/clangjobrequest.cpp index c9162b2125..1f6bf17ee8 100644 --- a/src/tools/clangbackend/ipcsource/clangjobrequest.cpp +++ b/src/tools/clangbackend/ipcsource/clangjobrequest.cpp @@ -40,6 +40,8 @@ static const char *JobRequestTypeToText(JobRequest::Type type) RETURN_TEXT_FOR_CASE(CompleteCode); RETURN_TEXT_FOR_CASE(RequestDocumentAnnotations); RETURN_TEXT_FOR_CASE(RequestReferences); + RETURN_TEXT_FOR_CASE(SuspendDocument); + RETURN_TEXT_FOR_CASE(ResumeDocument); } return "UnhandledJobRequestType"; @@ -121,7 +123,19 @@ JobRequest::ExpirationReasons JobRequest::expirationReasonsForType(Type type) JobRequest::Conditions JobRequest::conditionsForType(JobRequest::Type type) { - Conditions conditions = Condition::DocumentVisible; + if (type == Type::SuspendDocument) { + return Conditions(Condition::DocumentUnsuspended) + | Conditions(Condition::DocumentNotVisible); + } + + if (type == Type::ResumeDocument) { + return Conditions(Condition::DocumentSuspended) + | Conditions(Condition::DocumentVisible); + } + + Conditions conditions = Conditions(Condition::DocumentUnsuspended) + | Conditions(Condition::DocumentVisible); + if (type == Type::RequestReferences) conditions |= Condition::CurrentDocumentRevision; diff --git a/src/tools/clangbackend/ipcsource/clangjobrequest.h b/src/tools/clangbackend/ipcsource/clangjobrequest.h index b484dee7ce..b9bbf0a3be 100644 --- a/src/tools/clangbackend/ipcsource/clangjobrequest.h +++ b/src/tools/clangbackend/ipcsource/clangjobrequest.h @@ -53,13 +53,18 @@ public: CompleteCode, RequestDocumentAnnotations, RequestReferences, + + SuspendDocument, + ResumeDocument, }; enum class Condition { NoCondition = 1 << 0, DocumentVisible = 1 << 1, DocumentNotVisible = 1 << 2, - CurrentDocumentRevision = 1 << 3, + DocumentSuspended = 1 << 3, + DocumentUnsuspended = 1 << 4, + CurrentDocumentRevision = 1 << 5, }; Q_DECLARE_FLAGS(Conditions, Condition) diff --git a/src/tools/clangbackend/ipcsource/clangresumedocumentjob.cpp b/src/tools/clangbackend/ipcsource/clangresumedocumentjob.cpp new file mode 100644 index 0000000000..c628d4444a --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangresumedocumentjob.cpp @@ -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. +** +****************************************************************************/ + +#include "clangresumedocumentjob.h" + +#include <utils/qtcassert.h> + +namespace ClangBackEnd { + +void ResumeDocumentJob::finalizeAsyncRun() +{ + if (context().isDocumentOpen()) { + if (QTC_GUARD(asyncResult().updateResult.hasReparsed())) + m_pinnedDocument.setIsSuspended(false); + } + + UpdateDocumentAnnotationsJob::finalizeAsyncRun(); +} + +bool ResumeDocumentJob::isExpectedJobRequestType(const JobRequest &jobRequest) const +{ + return jobRequest.type == JobRequest::Type::ResumeDocument; +} + +TranslationUnitUpdateInput ResumeDocumentJob::createUpdateInput(const Document &document) const +{ + TranslationUnitUpdateInput input = UpdateDocumentAnnotationsJob::createUpdateInput(document); + input.reparseNeeded = true; + return input; +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangresumedocumentjob.h b/src/tools/clangbackend/ipcsource/clangresumedocumentjob.h new file mode 100644 index 0000000000..aff8e780f7 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangresumedocumentjob.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** 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 "clangasyncjob.h" +#include "clangdocument.h" +#include "clangupdatedocumentannotationsjob.h" + +namespace ClangBackEnd { + +class ResumeDocumentJob : public UpdateDocumentAnnotationsJob +{ +public: + void finalizeAsyncRun() override; + +private: + bool isExpectedJobRequestType(const JobRequest &jobRequest) const override; + TranslationUnitUpdateInput createUpdateInput(const Document &document) const override; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangsuspenddocumentjob.cpp b/src/tools/clangbackend/ipcsource/clangsuspenddocumentjob.cpp new file mode 100644 index 0000000000..e3406fcf32 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangsuspenddocumentjob.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** 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 "clangsuspenddocumentjob.h" + +#include <clangbackendipc/clangbackendipcdebugutils.h> + +#include <utils/qtcassert.h> + +namespace ClangBackEnd { + +static bool runAsyncHelper(const TranslationUnit &translationUnit) +{ + TIME_SCOPE_DURATION("SuspendDocumentJobRunner"); + + return translationUnit.suspend(); +} + +IAsyncJob::AsyncPrepareResult SuspendDocumentJob::prepareAsyncRun() +{ + const JobRequest jobRequest = context().jobRequest; + QTC_ASSERT(jobRequest.type == JobRequest::Type::SuspendDocument, return AsyncPrepareResult()); + + try { + m_pinnedDocument = context().documentForJobRequest(); + m_pinnedFileContainer = m_pinnedDocument.fileContainer(); + + TranslationUnit translationUnit + = m_pinnedDocument.translationUnit(jobRequest.preferredTranslationUnit); + setRunner([translationUnit]() { + return runAsyncHelper(translationUnit); + }); + return AsyncPrepareResult{translationUnit.id()}; + + } catch (const std::exception &exception) { + qWarning() << "Error in SuspendDocumentJob::prepareAsyncRun:" << exception.what(); + return AsyncPrepareResult(); + } +} + +void SuspendDocumentJob::finalizeAsyncRun() +{ + if (context().isDocumentOpen()) { + const bool suspendSucceeded = asyncResult(); + if (QTC_GUARD(suspendSucceeded)) { + m_pinnedDocument.setIsSuspended(true); + } + } +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangsuspenddocumentjob.h b/src/tools/clangbackend/ipcsource/clangsuspenddocumentjob.h new file mode 100644 index 0000000000..16d5b52a1e --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangsuspenddocumentjob.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** 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 "clangasyncjob.h" +#include "clangdocument.h" + +namespace ClangBackEnd { + +class SuspendDocumentJob : public AsyncJob<bool> +{ +public: + AsyncPrepareResult prepareAsyncRun() override; + void finalizeAsyncRun() override; + +private: + Document m_pinnedDocument; + FileContainer m_pinnedFileContainer; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp b/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp index ca95c5f190..0bc0509a5e 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp +++ b/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp @@ -25,6 +25,7 @@ #include "clangtranslationunit.h" +#include "clangbackend_global.h" #include "clangreferencescollector.h" #include "clangtranslationunitupdater.h" @@ -38,6 +39,8 @@ #include <sourcelocation.h> #include <sourcerange.h> +#include <utils/qtcassert.h> + namespace ClangBackEnd { TranslationUnit::TranslationUnit(const Utf8String &id, @@ -100,6 +103,16 @@ TranslationUnitUpdateResult TranslationUnit::reparse( return updater.update(TranslationUnitUpdater::UpdateMode::ForceReparse); } +bool TranslationUnit::suspend() const +{ +#ifdef IS_SUSPEND_SUPPORTED + return clang_suspendTranslationUnit(cxTranslationUnit()); +#else + QTC_CHECK(false && "clang_suspendTranslationUnit() not supported."); + return false; +#endif +} + TranslationUnit::CodeCompletionResult TranslationUnit::complete( UnsavedFiles &unsavedFiles, uint line, diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunit.h b/src/tools/clangbackend/ipcsource/clangtranslationunit.h index 5e6d28736a..cc398e9a72 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunit.h +++ b/src/tools/clangbackend/ipcsource/clangtranslationunit.h @@ -71,6 +71,8 @@ public: CXIndex &cxIndex() const; CXTranslationUnit &cxTranslationUnit() const; + bool suspend() const; + TranslationUnitUpdateResult update(const TranslationUnitUpdateInput &parseInput) const; TranslationUnitUpdateResult parse(const TranslationUnitUpdateInput &parseInput) const; TranslationUnitUpdateResult reparse(const TranslationUnitUpdateInput &parseInput) const; diff --git a/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.cpp b/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.cpp index fee622964d..b4b56fe534 100644 --- a/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.cpp +++ b/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.cpp @@ -56,8 +56,7 @@ static UpdateDocumentAnnotationsJob::AsyncResult runAsyncHelper( IAsyncJob::AsyncPrepareResult UpdateDocumentAnnotationsJob::prepareAsyncRun() { const JobRequest jobRequest = context().jobRequest; - QTC_ASSERT(jobRequest.type == JobRequest::Type::UpdateDocumentAnnotations, - return AsyncPrepareResult()); + QTC_ASSERT(isExpectedJobRequestType(jobRequest), return AsyncPrepareResult()); try { m_pinnedDocument = context().documentForJobRequest(); @@ -65,7 +64,7 @@ IAsyncJob::AsyncPrepareResult UpdateDocumentAnnotationsJob::prepareAsyncRun() const TranslationUnit translationUnit = m_pinnedDocument.translationUnit(jobRequest.preferredTranslationUnit); - const TranslationUnitUpdateInput updateInput = m_pinnedDocument.createUpdateInput(); + const TranslationUnitUpdateInput updateInput = createUpdateInput(m_pinnedDocument); setRunner([translationUnit, updateInput]() { return runAsyncHelper(translationUnit, updateInput); }); @@ -87,6 +86,17 @@ void UpdateDocumentAnnotationsJob::finalizeAsyncRun() } } +bool UpdateDocumentAnnotationsJob::isExpectedJobRequestType(const JobRequest &jobRequest) const +{ + return jobRequest.type == JobRequest::Type::UpdateDocumentAnnotations; +} + +TranslationUnitUpdateInput +UpdateDocumentAnnotationsJob::createUpdateInput(const Document &document) const +{ + return document.createUpdateInput(); +} + void UpdateDocumentAnnotationsJob::incorporateUpdaterResult(const AsyncResult &result) { m_pinnedDocument.incorporateUpdaterResult(result.updateResult); diff --git a/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.h b/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.h index 00fe3be311..fdbe9068a6 100644 --- a/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.h +++ b/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.h @@ -53,12 +53,18 @@ public: AsyncPrepareResult prepareAsyncRun() override; void finalizeAsyncRun() override; +protected: + virtual bool isExpectedJobRequestType(const JobRequest &jobRequest) const; + virtual TranslationUnitUpdateInput createUpdateInput(const Document &document) const; + private: void incorporateUpdaterResult(const AsyncResult &result); void sendAnnotations(const AsyncResult &result); -private: +protected: Document m_pinnedDocument; + +private: FileContainer m_pinnedFileContainer; }; diff --git a/tests/unit/unittest/clangdocumentsuspenderresumer-test.cpp b/tests/unit/unittest/clangdocumentsuspenderresumer-test.cpp new file mode 100644 index 0000000000..8ab78ebded --- /dev/null +++ b/tests/unit/unittest/clangdocumentsuspenderresumer-test.cpp @@ -0,0 +1,295 @@ +/**************************************************************************** +** +** 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 "googletest.h" + +#include "dummyclangipcclient.h" + +#include <clangclock.h> +#include <clangdocument.h> +#include <clangdocumentprocessors.h> +#include <clangdocuments.h> +#include <clangdocumentsuspenderresumer.h> +#include <clangtranslationunits.h> +#include <projects.h> +#include <unsavedfiles.h> +#include <utf8string.h> + +#include <utils/algorithm.h> + +#include <clang-c/Index.h> + +using ClangBackEnd::Clock; +using ClangBackEnd::Document; +using ClangBackEnd::JobRequest; +using ClangBackEnd::PreferredTranslationUnit; +using ClangBackEnd::SuspendResumeJobs; +using ClangBackEnd::SuspendResumeJobsEntry; +using ClangBackEnd::TimePoint; + +using testing::ContainerEq; +using testing::ElementsAre; +using testing::IsEmpty; + +namespace ClangBackEnd { + +bool operator==(const SuspendResumeJobsEntry &a, const SuspendResumeJobsEntry &b) +{ + return a.document == b.document + && a.jobRequestType == b.jobRequestType + && a.preferredTranslationUnit == b.preferredTranslationUnit; +} + +} // ClangBackEnd + +namespace { + +class DocumentSuspenderResumer : public ::testing::Test +{ +protected: + void SetUp() override; + Document getDocument(const Utf8String &filePath); + void categorizeDocuments(int hotDocumentsSize); + SuspendResumeJobs createSuspendResumeJobs(int hotDocumentsSize = -1); + +protected: + ClangBackEnd::ProjectParts projects; + ClangBackEnd::UnsavedFiles unsavedFiles; + ClangBackEnd::Documents documents{projects, unsavedFiles}; + DummyIpcClient dummyIpcClient; + ClangBackEnd::DocumentProcessors documentProcessors{documents, unsavedFiles, projects, + dummyIpcClient}; + + const Utf8String projectPartId = Utf8StringLiteral("projectPartId"); + + const Utf8String filePath1 = Utf8StringLiteral(TESTDATA_DIR"/empty1.cpp"); + const ClangBackEnd::FileContainer fileContainer1{filePath1, projectPartId, Utf8String(), true}; + + const Utf8String filePath2 = Utf8StringLiteral(TESTDATA_DIR"/empty2.cpp"); + const ClangBackEnd::FileContainer fileContainer2{filePath2, projectPartId, Utf8String(), true}; + + const Utf8String filePath3 = Utf8StringLiteral(TESTDATA_DIR"/empty3.cpp"); + const ClangBackEnd::FileContainer fileContainer3{filePath3, projectPartId, Utf8String(), true}; + + std::vector<Document> hotDocuments; + std::vector<Document> coldDocuments; +}; + +TEST_F(DocumentSuspenderResumer, CategorizeNoDocuments) +{ + categorizeDocuments(99); + + ASSERT_THAT(hotDocuments, IsEmpty()); + ASSERT_THAT(coldDocuments, IsEmpty()); +} + +TEST_F(DocumentSuspenderResumer, CategorizeSingleDocument) +{ + documents.create({fileContainer1}); + + categorizeDocuments(99); + + ASSERT_THAT(hotDocuments, ElementsAre(getDocument(filePath1))); + ASSERT_THAT(coldDocuments, IsEmpty()); +} + +TEST_F(DocumentSuspenderResumer, CategorizeKeepsStableOrder) +{ + documents.create({fileContainer1, fileContainer2}); + + categorizeDocuments(99); + + ASSERT_THAT(hotDocuments, ElementsAre(getDocument(filePath1), + getDocument(filePath2))); +} + +TEST_F(DocumentSuspenderResumer, CategorizePutsLastVisibleToTopOfHotDocuments) +{ + documents.create({fileContainer1, fileContainer2}); + documents.setVisibleInEditors({filePath1}); + documents.setVisibleInEditors({filePath2}); + + categorizeDocuments(99); + + ASSERT_THAT(hotDocuments, ElementsAre(getDocument(filePath2), + getDocument(filePath1))); +} + +TEST_F(DocumentSuspenderResumer, CategorizeWithLessDocumentsThanWeCareFor) +{ + documents.create({fileContainer1}); + + categorizeDocuments(2); + + ASSERT_THAT(hotDocuments, ElementsAre(getDocument(filePath1))); + ASSERT_THAT(coldDocuments, IsEmpty()); +} + +TEST_F(DocumentSuspenderResumer, CategorizeWithZeroHotDocuments) +{ + documents.create({fileContainer1}); + + categorizeDocuments(0); + + ASSERT_THAT(hotDocuments, IsEmpty()); + ASSERT_THAT(coldDocuments, ElementsAre(getDocument(filePath1))); +} + +TEST_F(DocumentSuspenderResumer, CategorizeWithMoreVisibleDocumentsThanHotDocuments) +{ + const TimePoint timePoint = Clock::now(); + Document document1 = documents.create({fileContainer1})[0]; + document1.setIsVisibleInEditor(true, timePoint); + Document document2 = documents.create({fileContainer2})[0]; + document2.setIsVisibleInEditor(true, timePoint); + + categorizeDocuments(1); + + ASSERT_THAT(hotDocuments, ElementsAre(getDocument(filePath1), getDocument(filePath2))); + ASSERT_THAT(coldDocuments, IsEmpty()); +} + +TEST_F(DocumentSuspenderResumer, CreateSuspendJobForInvisible) +{ + Document document = documents.create({fileContainer1})[0]; + document.setIsSuspended(false); + document.setIsVisibleInEditor(false, Clock::now()); + const SuspendResumeJobs expectedJobs = { + {document, JobRequest::Type::SuspendDocument, PreferredTranslationUnit::RecentlyParsed} + }; + + const SuspendResumeJobs jobs = createSuspendResumeJobs(/*hotDocumentsSize=*/ 0); + + ASSERT_THAT(jobs, ContainerEq(expectedJobs)); +} + +TEST_F(DocumentSuspenderResumer, DoNotCreateSuspendJobForVisible) +{ + Document document = documents.create({fileContainer1})[0]; + document.setIsSuspended(false); + document.setIsVisibleInEditor(true, Clock::now()); + + const SuspendResumeJobs jobs = createSuspendResumeJobs(/*hotDocumentsSize=*/ 0); + + ASSERT_THAT(jobs, ContainerEq(SuspendResumeJobs())); +} + +TEST_F(DocumentSuspenderResumer, CreateSuspendJobsForDocumentWithSupportiveTranslationUnit) +{ + Document document = documents.create({fileContainer1})[0]; + document.setIsSuspended(false); + document.setIsVisibleInEditor(false, Clock::now()); + document.translationUnits().createAndAppend(); // Add supportive translation unit + const SuspendResumeJobs expectedJobs = { + {document, JobRequest::Type::SuspendDocument, PreferredTranslationUnit::RecentlyParsed}, + {document, JobRequest::Type::SuspendDocument, PreferredTranslationUnit::PreviouslyParsed}, + }; + + const SuspendResumeJobs jobs = createSuspendResumeJobs(/*hotDocumentsSize=*/ 0); + + ASSERT_THAT(jobs, ContainerEq(expectedJobs)); +} + +TEST_F(DocumentSuspenderResumer, CreateResumeJob) +{ + Document document = documents.create({fileContainer1})[0]; + document.setIsSuspended(true); + document.setIsVisibleInEditor(true, Clock::now()); + const SuspendResumeJobs expectedJobs = { + {document, JobRequest::Type::ResumeDocument, PreferredTranslationUnit::RecentlyParsed} + }; + + const SuspendResumeJobs jobs = createSuspendResumeJobs(); + + ASSERT_THAT(jobs, ContainerEq(expectedJobs)); +} + +TEST_F(DocumentSuspenderResumer, DoNotCreateResumeJobForInvisible) +{ + Document document = documents.create({fileContainer1})[0]; + document.setIsSuspended(true); + document.setIsVisibleInEditor(false, Clock::now()); + + const SuspendResumeJobs jobs = createSuspendResumeJobs(/*hotDocumentsSize=*/ 0); + + ASSERT_THAT(jobs, ContainerEq(SuspendResumeJobs())); +} + +TEST_F(DocumentSuspenderResumer, CreateResumeJobsForDocumentWithSupportiveTranslationUnit) +{ + Document document = documents.create({fileContainer1})[0]; + document.setIsSuspended(true); + document.setIsVisibleInEditor(true, Clock::now()); + document.translationUnits().createAndAppend(); // Add supportive translation unit + const SuspendResumeJobs expectedJobs = { + {document, JobRequest::Type::ResumeDocument, PreferredTranslationUnit::RecentlyParsed}, + {document, JobRequest::Type::ResumeDocument, PreferredTranslationUnit::PreviouslyParsed}, + }; + + const SuspendResumeJobs jobs = createSuspendResumeJobs(); + + ASSERT_THAT(jobs, ContainerEq(expectedJobs)); +} + +TEST_F(DocumentSuspenderResumer, CreateSuspendAndResumeJobs) +{ + Document hotDocument = documents.create({fileContainer1})[0]; + hotDocument.setIsSuspended(true); + Document coldDocument = documents.create({fileContainer2})[0]; + coldDocument.setIsSuspended(false); + documents.setVisibleInEditors({filePath1}); + const SuspendResumeJobs expectedJobs = { + {coldDocument, JobRequest::Type::SuspendDocument, PreferredTranslationUnit::RecentlyParsed}, + {hotDocument, JobRequest::Type::ResumeDocument, PreferredTranslationUnit::RecentlyParsed}, + }; + + const SuspendResumeJobs jobs = createSuspendResumeJobs(/*hotDocumentsSize=*/ 1); + + ASSERT_THAT(jobs, ContainerEq(expectedJobs)); +} + +void DocumentSuspenderResumer::SetUp() +{ + projects.createOrUpdate({ClangBackEnd::ProjectPartContainer(projectPartId)}); +} + +ClangBackEnd::Document DocumentSuspenderResumer::getDocument(const Utf8String &filePath) +{ + return documents.document(filePath, projectPartId); +} + +void DocumentSuspenderResumer::categorizeDocuments(int hotDocumentsSize) +{ + categorizeHotColdDocuments(hotDocumentsSize, documents.documents(), hotDocuments, + coldDocuments); +} + +ClangBackEnd::SuspendResumeJobs +DocumentSuspenderResumer::createSuspendResumeJobs(int hotDocumentsSize) +{ + return ClangBackEnd::createSuspendResumeJobs(documents.documents(), hotDocumentsSize); +} + +} // anonymous diff --git a/tests/unit/unittest/clangjobqueue-test.cpp b/tests/unit/unittest/clangjobqueue-test.cpp index 1b295acf76..ea786e87f1 100644 --- a/tests/unit/unittest/clangjobqueue-test.cpp +++ b/tests/unit/unittest/clangjobqueue-test.cpp @@ -430,6 +430,28 @@ TEST_F(JobQueue, RequestReferencesOutdatableByDocumentClose) ASSERT_THAT(jobQueue.size(), Eq(0)); } +TEST_F(JobQueue, RequestReferencesDoesNotRunOnSuspendedDocument) +{ + jobQueue.add(createJobRequest(filePath1, JobRequest::Type::RequestReferences)); + document.setIsSuspended(true); + + const JobRequests jobsToStart = jobQueue.processQueue(); + + ASSERT_THAT(jobsToStart.size(), Eq(0)); + ASSERT_THAT(jobQueue.size(), Eq(1)); +} + +TEST_F(JobQueue, ResumeDocumentDoesNotRunOnUnsuspended) +{ + jobQueue.add(createJobRequest(filePath1, JobRequest::Type::ResumeDocument)); + document.setIsSuspended(false); + + const JobRequests jobsToStart = jobQueue.processQueue(); + + ASSERT_THAT(jobsToStart.size(), Eq(0)); + ASSERT_THAT(jobQueue.size(), Eq(1)); +} + void JobQueue::SetUp() { projects.createOrUpdate({ProjectPartContainer(projectPartId)}); diff --git a/tests/unit/unittest/clangresumedocumentjob-test.cpp b/tests/unit/unittest/clangresumedocumentjob-test.cpp new file mode 100644 index 0000000000..11a9e49072 --- /dev/null +++ b/tests/unit/unittest/clangresumedocumentjob-test.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** 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 "clangasyncjob-base.h" + +#include <clangresumedocumentjob.h> + +using namespace ClangBackEnd; + +using testing::_; + +namespace { + +class ResumeDocumentJob : public ClangAsyncJobTest +{ +protected: + void SetUp() override { BaseSetUp(JobRequest::Type::ResumeDocument, job); } + void suspendDocument() + { + document.parse(); + document.translationUnit().suspend(); + document.setIsSuspended(true); + } + +protected: + ClangBackEnd::ResumeDocumentJob job; +}; + +TEST_F(ResumeDocumentJob, PrepareAsyncRun) +{ + job.setContext(jobContext); + + ASSERT_TRUE(job.prepareAsyncRun()); +} + +TEST_F(ResumeDocumentJob, RunAsync) +{ + suspendDocument(); + job.setContext(jobContext); + job.prepareAsyncRun(); + + job.runAsync(); + + ASSERT_TRUE(waitUntilJobFinished(job)); +} + +TEST_F(ResumeDocumentJob, DocumentIsResumedAfterRun) +{ + suspendDocument(); + job.setContext(jobContext); + job.prepareAsyncRun(); + + job.runAsync(); + ASSERT_TRUE(waitUntilJobFinished(job)); + + ASSERT_FALSE(document.isSuspended()); +} + +TEST_F(ResumeDocumentJob, SendsAnnotationsAfterResume) +{ + suspendDocument(); + job.setContext(jobContextWithMockClient); + job.prepareAsyncRun(); + EXPECT_CALL(mockIpcClient, documentAnnotationsChanged(_)).Times(1); + + job.runAsync(); + ASSERT_TRUE(waitUntilJobFinished(job)); +} + +} // anonymous diff --git a/tests/unit/unittest/clangsuspenddocumentjob-test.cpp b/tests/unit/unittest/clangsuspenddocumentjob-test.cpp new file mode 100644 index 0000000000..b9f92f3beb --- /dev/null +++ b/tests/unit/unittest/clangsuspenddocumentjob-test.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** 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 "clangasyncjob-base.h" + +#include <clangsuspenddocumentjob.h> + +using namespace ClangBackEnd; + +namespace { + +class SuspendDocumentJob : public ClangAsyncJobTest +{ +protected: + void SetUp() override { BaseSetUp(JobRequest::Type::SuspendDocument, job); } + +protected: + ClangBackEnd::SuspendDocumentJob job; +}; + +TEST_F(SuspendDocumentJob, PrepareAsyncRun) +{ + job.setContext(jobContext); + + ASSERT_TRUE(job.prepareAsyncRun()); +} + +TEST_F(SuspendDocumentJob, RunAsync) +{ + document.parse(); + job.setContext(jobContext); + job.prepareAsyncRun(); + + job.runAsync(); + + ASSERT_TRUE(waitUntilJobFinished(job)); +} + +TEST_F(SuspendDocumentJob, DocumentIsSuspendedAfterRun) +{ + document.parse(); + job.setContext(jobContext); + job.prepareAsyncRun(); + + job.runAsync(); + ASSERT_TRUE(waitUntilJobFinished(job)); + + ASSERT_TRUE(document.isSuspended()); +} + +} // anonymous diff --git a/tests/unit/unittest/data/empty1.cpp b/tests/unit/unittest/data/empty1.cpp new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/unit/unittest/data/empty1.cpp diff --git a/tests/unit/unittest/data/empty2.cpp b/tests/unit/unittest/data/empty2.cpp new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/unit/unittest/data/empty2.cpp diff --git a/tests/unit/unittest/data/empty3.cpp b/tests/unit/unittest/data/empty3.cpp new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/unit/unittest/data/empty3.cpp diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index be7632ecd5..67226416af 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -84,6 +84,7 @@ SOURCES += \ clangdocumentprocessors-test.cpp \ clangdocumentprocessor-test.cpp \ clangdocuments-test.cpp \ + clangdocumentsuspenderresumer-test.cpp \ clangdocument-test.cpp \ clangfixitoperation-test.cpp \ clangisdiagnosticrelatedtolocation-test.cpp \ @@ -94,8 +95,10 @@ SOURCES += \ clangreparsesupportivetranslationunitjob-test.cpp \ clangrequestdocumentannotationsjob-test.cpp \ clangrequestreferencesjob-test.cpp \ + clangresumedocumentjob-test.cpp \ clangstring-test.cpp \ clangsupportivetranslationunitinitializer-test.cpp \ + clangsuspenddocumentjob-test.cpp \ clangtranslationunits-test.cpp \ clangtranslationunit-test.cpp \ clangupdatedocumentannotationsjob-test.cpp \ |