forked from qt-creator/qt-creator
		
	...as it is not needed. Just provide the compilation arguments as part of the Document. As a side effect, re-initializing the backend after a crash is cheaper and will not freeze the UI anymore (referenced bug). Task-number: QTCREATORBUG-21097 Change-Id: I866e25ef1fd5e4d318df16612a7564469e6baa11 Reviewed-by: Ivan Donchevskii <ivan.donchevskii@qt.io>
		
			
				
	
	
		
			312 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			312 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /****************************************************************************
 | |
| **
 | |
| ** 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 <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:
 | |
|     Document getDocument(const Utf8String &filePath);
 | |
|     void categorizeDocuments(int hotDocumentsSize);
 | |
|     SuspendResumeJobs createSuspendResumeJobs(int hotDocumentsSize = -1);
 | |
|     static void setParsed(Document &document);
 | |
| 
 | |
| protected:
 | |
|     ClangBackEnd::UnsavedFiles unsavedFiles;
 | |
|     ClangBackEnd::Documents documents{unsavedFiles};
 | |
|     DummyIpcClient dummyIpcClient;
 | |
|     ClangBackEnd::DocumentProcessors documentProcessors{documents, unsavedFiles, dummyIpcClient};
 | |
| 
 | |
|     const Utf8String filePath1 = Utf8StringLiteral(TESTDATA_DIR"/empty1.cpp");
 | |
|     const ClangBackEnd::FileContainer fileContainer1{filePath1, Utf8String(), true};
 | |
| 
 | |
|     const Utf8String filePath2 = Utf8StringLiteral(TESTDATA_DIR"/empty2.cpp");
 | |
|     const ClangBackEnd::FileContainer fileContainer2{filePath2, Utf8String(), true};
 | |
| 
 | |
|     const Utf8String filePath3 = Utf8StringLiteral(TESTDATA_DIR"/empty3.cpp");
 | |
|     const ClangBackEnd::FileContainer fileContainer3{filePath3, 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());
 | |
|     setParsed(document);
 | |
| 
 | |
|     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, IsEmpty());
 | |
| }
 | |
| 
 | |
| TEST_F(DocumentSuspenderResumer, DoNotCreateSuspendJobForUnparsed)
 | |
| {
 | |
|     Document document = documents.create({fileContainer1})[0];
 | |
|     document.setIsSuspended(false);
 | |
|     document.setIsVisibleInEditor(true, Clock::now());
 | |
| 
 | |
|     const SuspendResumeJobs jobs = createSuspendResumeJobs(/*hotDocumentsSize=*/ 0);
 | |
| 
 | |
|     ASSERT_THAT(jobs, IsEmpty());
 | |
| }
 | |
| 
 | |
| 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
 | |
|     setParsed(document);
 | |
|     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, IsEmpty());
 | |
| }
 | |
| 
 | |
| 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];
 | |
|     setParsed(coldDocument);
 | |
|     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));
 | |
| }
 | |
| 
 | |
| ClangBackEnd::Document DocumentSuspenderResumer::getDocument(const Utf8String &filePath)
 | |
| {
 | |
|     return documents.document(filePath);
 | |
| }
 | |
| 
 | |
| void DocumentSuspenderResumer::categorizeDocuments(int hotDocumentsSize)
 | |
| {
 | |
|     categorizeHotColdDocuments(hotDocumentsSize, documents.documents(), hotDocuments,
 | |
|                                coldDocuments);
 | |
| }
 | |
| 
 | |
| ClangBackEnd::SuspendResumeJobs
 | |
| DocumentSuspenderResumer::createSuspendResumeJobs(int hotDocumentsSize)
 | |
| {
 | |
|     return ClangBackEnd::createSuspendResumeJobs(documents.documents(), hotDocumentsSize);
 | |
| }
 | |
| 
 | |
| void DocumentSuspenderResumer::setParsed(ClangBackEnd::Document &document)
 | |
| {
 | |
|     const Utf8String first = document.translationUnit().id();
 | |
|     document.translationUnits().updateParseTimePoint(first, Clock::now());
 | |
| 
 | |
|     const Utf8String second
 | |
|             = document.translationUnit(PreferredTranslationUnit::LastUninitialized).id();
 | |
|     if (second != first)
 | |
|         document.translationUnits().updateParseTimePoint(second, Clock::now());
 | |
| }
 | |
| 
 | |
| } // anonymous
 |