diff options
author | Marco Bubke <marco.bubke@qt.io> | 2019-03-21 17:55:24 +0100 |
---|---|---|
committer | Marco Bubke <marco.bubke@qt.io> | 2019-04-02 13:08:44 +0000 |
commit | 56b01f74633c310807cf3fc00766cfa01002297f (patch) | |
tree | a8e0a09154f4c95dd8b16db2a8512c2f5b5b0946 | |
parent | 7595c9f3052cf7c2ca05f3157c945371952ca0b5 (diff) | |
download | qt-creator-56b01f74633c310807cf3fc00766cfa01002297f.tar.gz |
Clang: Minimize reindexing
We optimal indexer is only reindexing if the index would be changed. This
patch is a step in that direction. We only reindex now if the file or
project has changed. It fixes some typos too.
Task-number: QTCREATORBUG-21150
Change-Id: I6ea1c13282fbcd70253b9b2939aed37580dbd160
Reviewed-by: Ivan Donchevskii <ivan.donchevskii@qt.io>
37 files changed, 736 insertions, 120 deletions
diff --git a/src/libs/clangsupport/clangsupport-lib.pri b/src/libs/clangsupport/clangsupport-lib.pri index 0668382803..086755d5dc 100644 --- a/src/libs/clangsupport/clangsupport-lib.pri +++ b/src/libs/clangsupport/clangsupport-lib.pri @@ -213,6 +213,9 @@ HEADERS += \ $$PWD/includesearchpath.h \ $$PWD/commandlinebuilder.h \ $$PWD/projectpartartefact.h \ - $$PWD/projectpartcontainer.h + $$PWD/projectpartcontainer.h \ + $$PWD/sourceentry.h \ + $$PWD/modifiedtimecheckerinterface.h \ + $$PWD/modifiedtimechecker.h contains(QT_CONFIG, reduce_exports):CONFIG += hide_symbols diff --git a/src/tools/clangpchmanagerbackend/source/modifiedtimechecker.h b/src/libs/clangsupport/modifiedtimechecker.h index d641de054c..c9b62c4673 100644 --- a/src/tools/clangpchmanagerbackend/source/modifiedtimechecker.h +++ b/src/libs/clangsupport/modifiedtimechecker.h @@ -25,17 +25,18 @@ #pragma once +#include "filepathcachinginterface.h" #include "modifiedtimecheckerinterface.h" -#include <filepathcachinginterface.h> - #include <algorithm> #include <iterator> namespace ClangBackEnd { - -class ModifiedTimeChecker final : public ModifiedTimeCheckerInterface +template<typename SourceEntries = ::ClangBackEnd::SourceEntries> +class ModifiedTimeChecker final : public ModifiedTimeCheckerInterface<SourceEntries> { + using SourceEntry = typename SourceEntries::value_type; + public: using GetModifiedTime = std::function<ClangBackEnd::TimeStamp(ClangBackEnd::FilePathView filePath)>; ModifiedTimeChecker(GetModifiedTime &getModifiedTime, FilePathCachingInterface &filePathCache) @@ -67,7 +68,7 @@ public: std::back_inserter(timeStampsToUpdate)); for (SourceTimeStamp &sourceTimeStamp : timeStampsToUpdate) { - sourceTimeStamp.lastModified = m_getModifiedTime( + sourceTimeStamp.timeStamp = m_getModifiedTime( m_filePathCache.filePath(sourceTimeStamp.sourceId)); } } @@ -78,21 +79,22 @@ private: class CompareSourceId { public: - bool operator()(SourceTimeStamp first, SourceTimeStamp second) { + bool operator()(SourceTimeStamp first, SourceTimeStamp second) + { return first.sourceId < second.sourceId; } - bool operator()(SourceEntry first, SourceEntry second) + bool operator()(::ClangBackEnd::SourceEntry first, ::ClangBackEnd::SourceEntry second) { return first.sourceId < second.sourceId; } - bool operator()(SourceTimeStamp first, SourceEntry second) + bool operator()(SourceTimeStamp first, ::ClangBackEnd::SourceEntry second) { return first.sourceId < second.sourceId; } - bool operator()(SourceEntry first, SourceTimeStamp second) + bool operator()(::ClangBackEnd::SourceEntry first, SourceTimeStamp second) { return first.sourceId < second.sourceId; } @@ -112,23 +114,22 @@ private: public: bool operator()(SourceTimeStamp first, SourceTimeStamp second) { - return first.lastModified <= second.lastModified; + return first.timeStamp <= second.timeStamp; } - bool operator()(SourceEntry first, SourceEntry second) + bool operator()(::ClangBackEnd::SourceEntry first, ::ClangBackEnd::SourceEntry second) { - return first.pchCreationTimeStamp <= - second.pchCreationTimeStamp; + return first.timeStamp <= second.timeStamp; } - bool operator()(SourceTimeStamp first, SourceEntry second) + bool operator()(SourceTimeStamp first, ::ClangBackEnd::SourceEntry second) { - return first.lastModified <= second.pchCreationTimeStamp; + return first.timeStamp <= second.timeStamp; } - bool operator()(SourceEntry first, SourceTimeStamp second) + bool operator()(::ClangBackEnd::SourceEntry first, SourceTimeStamp second) { - return first.pchCreationTimeStamp <= second.lastModified; + return first.timeStamp <= second.timeStamp; } }; @@ -144,7 +145,7 @@ private: SourceTimeStamps sourceTimeStamps = newSourceTimeStamps(sourceEntries); for (SourceTimeStamp &newSourceTimeStamp : sourceTimeStamps) { - newSourceTimeStamp.lastModified = m_getModifiedTime( + newSourceTimeStamp.timeStamp = m_getModifiedTime( m_filePathCache.filePath(newSourceTimeStamp.sourceId)); } @@ -169,17 +170,17 @@ private: return first.sourceId < second.sourceId; } - bool operator()(SourceEntry first, SourceEntry second) + bool operator()(::ClangBackEnd::SourceEntry first, ::ClangBackEnd::SourceEntry second) { return first.sourceId < second.sourceId; } - bool operator()(SourceTimeStamp first, SourceEntry second) + bool operator()(SourceTimeStamp first, ::ClangBackEnd::SourceEntry second) { return first.sourceId < second.sourceId; } - bool operator()(SourceEntry first, SourceTimeStamp second) + bool operator()(::ClangBackEnd::SourceEntry first, SourceTimeStamp second) { return first.sourceId < second.sourceId; } diff --git a/src/tools/clangpchmanagerbackend/source/modifiedtimecheckerinterface.h b/src/libs/clangsupport/modifiedtimecheckerinterface.h index c0cae1cf4b..a0e79b0701 100644 --- a/src/tools/clangpchmanagerbackend/source/modifiedtimecheckerinterface.h +++ b/src/libs/clangsupport/modifiedtimecheckerinterface.h @@ -29,6 +29,7 @@ namespace ClangBackEnd { +template<typename SourceEntries = ::ClangBackEnd::SourceEntries> class ModifiedTimeCheckerInterface { public: diff --git a/src/libs/clangsupport/projectpartsstorage.h b/src/libs/clangsupport/projectpartsstorage.h index 4e29cee03e..e54a7a61d1 100644 --- a/src/libs/clangsupport/projectpartsstorage.h +++ b/src/libs/clangsupport/projectpartsstorage.h @@ -245,6 +245,22 @@ public: return statement.template value<ProjectPartArtefact, 8>(projectPartId.projectPathId); } + void resetIndexingTimeStamps(const ProjectPartContainers &projectsParts) override + { + try { + Sqlite::ImmediateTransaction transaction{database}; + + for (const ProjectPartContainer &projectPart : projectsParts) { + for (FilePathId sourcePathId : projectPart.sourcePathIds) + resetDependentIndexingTimeStampsStatement.write(sourcePathId.filePathId); + } + + transaction.commit(); + } catch (const Sqlite::StatementIsBusy &) { + resetIndexingTimeStamps(projectsParts); + } + } + Sqlite::TransactionInterface &transactionBackend() override { return database; } static Utils::SmallString toJson(const Utils::SmallStringVector &strings) @@ -343,5 +359,11 @@ public: "SELECT sourceId FROM projectPartsSources WHERE projectPartId = ?", database}; mutable ReadStatement fetchProjectPrecompiledHeaderPathStatement{ "SELECT projectPchPath FROM precompiledHeaders WHERE projectPartId = ?", database}; + WriteStatement resetDependentIndexingTimeStampsStatement{ + "WITH RECURSIVE collectedDependencies(sourceId) AS (VALUES(?) UNION SELECT " + "dependencySourceId FROM sourceDependencies, collectedDependencies WHERE " + "sourceDependencies.sourceId == collectedDependencies.sourceId) UPDATE fileStatuses SET " + "indexingTimeStamp = NULL WHERE sourceId IN (SELECT sourceId FROM collectedDependencies)", + database}; }; } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/projectpartsstorageinterface.h b/src/libs/clangsupport/projectpartsstorageinterface.h index 1127f70b1f..2d0675e657 100644 --- a/src/libs/clangsupport/projectpartsstorageinterface.h +++ b/src/libs/clangsupport/projectpartsstorageinterface.h @@ -63,6 +63,7 @@ public: virtual Utils::optional<ProjectPartArtefact> fetchProjectPartArtefact(FilePathId sourceId) const = 0; virtual Utils::optional<ProjectPartArtefact> fetchProjectPartArtefact( ProjectPartId projectPartId) const = 0; + virtual void resetIndexingTimeStamps(const ProjectPartContainers &projectsParts) = 0; virtual Sqlite::TransactionInterface &transactionBackend() = 0; diff --git a/src/libs/clangsupport/refactoringdatabaseinitializer.h b/src/libs/clangsupport/refactoringdatabaseinitializer.h index efca925413..a7058d3204 100644 --- a/src/libs/clangsupport/refactoringdatabaseinitializer.h +++ b/src/libs/clangsupport/refactoringdatabaseinitializer.h @@ -177,6 +177,7 @@ public: Sqlite::Contraint::PrimaryKey); table.addColumn("size", Sqlite::ColumnType::Integer); table.addColumn("lastModified", Sqlite::ColumnType::Integer); + table.addColumn("indexingTimeStamp", Sqlite::ColumnType::Integer); table.initialize(database); } @@ -188,6 +189,7 @@ public: const Sqlite::Column &sourceIdColumn = table.addColumn("sourceId", Sqlite::ColumnType::Integer); const Sqlite::Column &dependencySourceIdColumn = table.addColumn("dependencySourceId", Sqlite::ColumnType::Integer); table.addIndex({sourceIdColumn, dependencySourceIdColumn}); + table.addIndex({dependencySourceIdColumn, sourceIdColumn}); table.initialize(database); } diff --git a/src/tools/clangpchmanagerbackend/source/sourceentry.h b/src/libs/clangsupport/sourceentry.h index 5c45db6bde..c593f1fcd3 100644 --- a/src/tools/clangpchmanagerbackend/source/sourceentry.h +++ b/src/libs/clangsupport/sourceentry.h @@ -64,12 +64,12 @@ class SourceTimeStamp using int64 = long long; public: SourceTimeStamp(int sourceId, int64 lastModified) - : lastModified(lastModified) + : timeStamp(lastModified) , sourceId(sourceId) {} SourceTimeStamp(FilePathId sourceId, TimeStamp lastModified) - : lastModified(lastModified) + : timeStamp(lastModified) , sourceId(sourceId) {} @@ -90,7 +90,7 @@ public: friend bool operator==(SourceTimeStamp first, SourceTimeStamp second) { - return first.sourceId == second.sourceId && first.lastModified == second.lastModified; + return first.sourceId == second.sourceId && first.timeStamp == second.timeStamp; } friend bool operator!=(SourceTimeStamp first, SourceTimeStamp second) @@ -99,7 +99,7 @@ public: } public: - TimeStamp lastModified; + TimeStamp timeStamp; FilePathId sourceId; }; @@ -110,21 +110,22 @@ class SourceEntry using int64 = long long; public: - SourceEntry(int sourceId, - int64 pchCreationTimeStamp, - int sourceType, - int hasMissingIncludes) - : pchCreationTimeStamp(pchCreationTimeStamp), sourceId(sourceId), - sourceType(static_cast<SourceType>(sourceType)), - hasMissingIncludes( - static_cast<HasMissingIncludes>(hasMissingIncludes)) {} + SourceEntry(int sourceId, int64 timeStamp, int sourceType, int hasMissingIncludes) + : timeStamp(timeStamp) + , sourceId(sourceId) + , sourceType(static_cast<SourceType>(sourceType)) + , hasMissingIncludes(static_cast<HasMissingIncludes>(hasMissingIncludes)) + {} SourceEntry(FilePathId sourceId, SourceType sourceType, - TimeStamp pchCreationTimeStamp, + TimeStamp timeStamp, HasMissingIncludes hasMissingIncludes = HasMissingIncludes::No) - : pchCreationTimeStamp(pchCreationTimeStamp), sourceId(sourceId), - sourceType(sourceType), hasMissingIncludes(hasMissingIncludes) {} + : timeStamp(timeStamp) + , sourceId(sourceId) + , sourceType(sourceType) + , hasMissingIncludes(hasMissingIncludes) + {} friend bool operator<(SourceEntry first, SourceEntry second) { return first.sourceId < second.sourceId; @@ -133,13 +134,13 @@ public: friend bool operator==(SourceEntry first, SourceEntry second) { return first.sourceId == second.sourceId && first.sourceType == second.sourceType - && first.pchCreationTimeStamp == second.pchCreationTimeStamp; + && first.timeStamp == second.timeStamp; } friend bool operator!=(SourceEntry first, SourceEntry second) { return !(first == second); } public: - TimeStamp pchCreationTimeStamp; + TimeStamp timeStamp; FilePathId sourceId; SourceType sourceType = SourceType::UserInclude; HasMissingIncludes hasMissingIncludes = HasMissingIncludes::No; diff --git a/src/plugins/clangcodemodel/clangcompletionchunkstotextconverter.cpp b/src/plugins/clangcodemodel/clangcompletionchunkstotextconverter.cpp index 5af48386bd..ad5ee50476 100644 --- a/src/plugins/clangcodemodel/clangcompletionchunkstotextconverter.cpp +++ b/src/plugins/clangcodemodel/clangcompletionchunkstotextconverter.cpp @@ -47,7 +47,7 @@ void CompletionChunksToTextConverter::parseChunks( m_codeCompletionChunks.cend(), [this] (const ClangBackEnd::CodeCompletionChunk &chunk) { - parseDependendOnTheOptionalState(chunk); + parseDependentOnTheOptionalState(chunk); m_previousCodeCompletionChunk = chunk; }); } @@ -200,7 +200,7 @@ void CompletionChunksToTextConverter::parse( } } -void CompletionChunksToTextConverter::parseDependendOnTheOptionalState( +void CompletionChunksToTextConverter::parseDependentOnTheOptionalState( const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk) { wrapInCursiveTagIfOptional(codeCompletionChunk); diff --git a/src/plugins/clangcodemodel/clangcompletionchunkstotextconverter.h b/src/plugins/clangcodemodel/clangcompletionchunkstotextconverter.h index 88654ed3fd..09ad15e30f 100644 --- a/src/plugins/clangcodemodel/clangcompletionchunkstotextconverter.h +++ b/src/plugins/clangcodemodel/clangcompletionchunkstotextconverter.h @@ -75,7 +75,7 @@ public: private: void parse(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk); - void parseDependendOnTheOptionalState(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk); + void parseDependentOnTheOptionalState(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk); void parseResultType(const Utf8String &text); void parseText(const Utf8String &text); void wrapInCursiveTagIfOptional(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk); diff --git a/src/plugins/cpptools/compileroptionsbuilder.cpp b/src/plugins/cpptools/compileroptionsbuilder.cpp index 8dfe0c7977..a1e01911d6 100644 --- a/src/plugins/cpptools/compileroptionsbuilder.cpp +++ b/src/plugins/cpptools/compileroptionsbuilder.cpp @@ -455,6 +455,8 @@ void CompilerOptionsBuilder::addLanguageVersionAndExtensions() case LanguageVersion::CXX2a: option = (gnuExtensions ? QLatin1String("-std=gnu++2a") : QLatin1String("-std=c++2a")); break; + case LanguageVersion::None: + break; } add(option, /*gccOnlyOption=*/true); diff --git a/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp b/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp index dc1c7085e7..a7ca916f15 100644 --- a/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp +++ b/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp @@ -215,7 +215,8 @@ struct Data // because we have a cycle dependency [&](ClangBackEnd::FilePathView path) -> TimeStamp { return QFileInfo(QString(path)).lastModified().toSecsSinceEpoch(); }}; - ClangBackEnd::ModifiedTimeChecker modifiedTimeChecker{getModifiedTime, filePathCache}; + ClangBackEnd::ModifiedTimeChecker<ClangBackEnd::SourceEntries> modifiedTimeChecker{getModifiedTime, + filePathCache}; ClangBackEnd::BuildDependenciesProvider buildDependencyProvider{buildDependencyStorage, modifiedTimeChecker, buildDependencyCollector, diff --git a/src/tools/clangpchmanagerbackend/source/builddependenciesprovider.h b/src/tools/clangpchmanagerbackend/source/builddependenciesprovider.h index 9700b83aa3..a006b10a06 100644 --- a/src/tools/clangpchmanagerbackend/source/builddependenciesprovider.h +++ b/src/tools/clangpchmanagerbackend/source/builddependenciesprovider.h @@ -27,6 +27,8 @@ #include "builddependenciesproviderinterface.h" +#include <modifiedtimecheckerinterface.h> + namespace Sqlite { class TransactionInterface; } @@ -34,14 +36,13 @@ class TransactionInterface; namespace ClangBackEnd { class BuildDependenciesStorageInterface; -class ModifiedTimeCheckerInterface; class BuildDependencyGeneratorInterface; class BuildDependenciesProvider : public BuildDependenciesProviderInterface { public: BuildDependenciesProvider(BuildDependenciesStorageInterface &buildDependenciesStorage, - ModifiedTimeCheckerInterface &modifiedTimeChecker, + ModifiedTimeCheckerInterface<> &modifiedTimeChecker, BuildDependencyGeneratorInterface &buildDependenciesGenerator, Sqlite::TransactionInterface &transactionBackend) : m_storage(buildDependenciesStorage) @@ -61,7 +62,7 @@ private: private: BuildDependenciesStorageInterface &m_storage; - ModifiedTimeCheckerInterface &m_modifiedTimeChecker; + ModifiedTimeCheckerInterface<> &m_modifiedTimeChecker; BuildDependencyGeneratorInterface &m_generator; Sqlite::TransactionInterface &m_transactionBackend; }; diff --git a/src/tools/clangpchmanagerbackend/source/clangpchmanagerbackend-source.pri b/src/tools/clangpchmanagerbackend/source/clangpchmanagerbackend-source.pri index 61bcedc262..bbf3bf16f4 100644 --- a/src/tools/clangpchmanagerbackend/source/clangpchmanagerbackend-source.pri +++ b/src/tools/clangpchmanagerbackend/source/clangpchmanagerbackend-source.pri @@ -29,8 +29,6 @@ HEADERS += \ $$PWD/builddependenciesprovider.h \ $$PWD/builddependenciesstorageinterface.h \ $$PWD/builddependency.h \ - $$PWD/modifiedtimecheckerinterface.h \ - $$PWD/sourceentry.h \ $$PWD/builddependenciesstorage.h \ $$PWD/builddependencygeneratorinterface.h \ $$PWD/usedmacrofilter.h \ @@ -40,8 +38,7 @@ HEADERS += \ $$PWD/pchtaskqueue.h \ $$PWD/generatepchactionfactory.h \ $$PWD/pchtaskgeneratorinterface.h \ - $$PWD/toolchainargumentscache.h \ - $$PWD/modifiedtimechecker.h + $$PWD/toolchainargumentscache.h !isEmpty(LIBTOOLING_LIBS) { SOURCES += \ diff --git a/src/tools/clangpchmanagerbackend/source/collectbuilddependencypreprocessorcallbacks.h b/src/tools/clangpchmanagerbackend/source/collectbuilddependencypreprocessorcallbacks.h index f3b365d8d5..9db095bd75 100644 --- a/src/tools/clangpchmanagerbackend/source/collectbuilddependencypreprocessorcallbacks.h +++ b/src/tools/clangpchmanagerbackend/source/collectbuilddependencypreprocessorcallbacks.h @@ -293,7 +293,7 @@ public: entry.get().hasMissingIncludes = HasMissingIncludes::Yes; } - SourceDependencies sourceDependenciesSortedByDependendFilePathId() const + SourceDependencies sourceDependenciesSortedByDependentFilePathId() const { auto sourceDependencies = m_buildDependency.sourceDependencies; std::sort(sourceDependencies.begin(), sourceDependencies.end(), [](auto first, auto second) { @@ -309,7 +309,7 @@ public: sortAndMakeUnique(m_containsMissingIncludes); collectSourceWithMissingIncludes(m_containsMissingIncludes, - sourceDependenciesSortedByDependendFilePathId()); + sourceDependenciesSortedByDependentFilePathId()); removeSourceWithMissingIncludesFromSources(); } diff --git a/src/tools/clangpchmanagerbackend/source/projectpartsmanager.cpp b/src/tools/clangpchmanagerbackend/source/projectpartsmanager.cpp index 584d06ded2..cc4ca53d7d 100644 --- a/src/tools/clangpchmanagerbackend/source/projectpartsmanager.cpp +++ b/src/tools/clangpchmanagerbackend/source/projectpartsmanager.cpp @@ -65,7 +65,7 @@ ProjectPartContainers ProjectPartsManager::update(ProjectPartContainers &&projec } m_projectPartsStorage.updateProjectParts(updatedProjectParts); - + m_projectPartsStorage.resetIndexingTimeStamps(updatedProjectParts); m_precompiledHeaderStorage.deleteProjectPrecompiledHeaders( toProjectPartIds(updatedProjectParts)); diff --git a/src/tools/clangrefactoringbackend/source/symbolindexer.cpp b/src/tools/clangrefactoringbackend/source/symbolindexer.cpp index e21268322a..6d0978e54a 100644 --- a/src/tools/clangrefactoringbackend/source/symbolindexer.cpp +++ b/src/tools/clangrefactoringbackend/source/symbolindexer.cpp @@ -66,7 +66,8 @@ SymbolIndexer::SymbolIndexer(SymbolIndexerTaskQueueInterface &symbolIndexerTaskQ FilePathCachingInterface &filePathCache, FileStatusCache &fileStatusCache, Sqlite::TransactionInterface &transactionInterface, - ProjectPartsStorageInterface &projectPartsStorage) + ProjectPartsStorageInterface &projectPartsStorage, + ModifiedTimeCheckerInterface<SourceTimeStamps> &modifiedTimeChecker) : m_symbolIndexerTaskQueue(symbolIndexerTaskQueue) , m_symbolStorage(symbolStorage) , m_buildDependencyStorage(buildDependenciesStorage) @@ -76,6 +77,7 @@ SymbolIndexer::SymbolIndexer(SymbolIndexerTaskQueueInterface &symbolIndexerTaskQ , m_fileStatusCache(fileStatusCache) , m_transactionInterface(transactionInterface) , m_projectPartsStorage(projectPartsStorage) + , m_modifiedTimeChecker(modifiedTimeChecker) { pathWatcher.setNotifier(this); } @@ -109,23 +111,27 @@ void SymbolIndexer::updateProjectPart(ProjectPartContainer &&projectPart) std::vector<SymbolIndexerTask> symbolIndexerTask; symbolIndexerTask.reserve(projectPart.sourcePathIds.size()); for (FilePathId sourcePathId : projectPart.sourcePathIds) { - auto indexing = [arguments = commandLineBuilder.commandLine, - sourcePathId, - this](SymbolsCollectorInterface &symbolsCollector) { - symbolsCollector.setFile(sourcePathId, arguments); - - bool success = symbolsCollector.collectSymbols(); - - if (success) { - Sqlite::ImmediateTransaction transaction{m_transactionInterface}; - - m_symbolStorage.addSymbolsAndSourceLocations(symbolsCollector.symbols(), - symbolsCollector.sourceLocations()); - transaction.commit(); - } - }; - - symbolIndexerTask.emplace_back(sourcePathId, projectPartId, std::move(indexing)); + SourceTimeStamps dependentTimeStamps = m_symbolStorage.fetchIncludedIndexingTimeStamps( + sourcePathId); + + if (!m_modifiedTimeChecker.isUpToDate(dependentTimeStamps)) { + auto indexing = [arguments = commandLineBuilder.commandLine, sourcePathId, this]( + SymbolsCollectorInterface &symbolsCollector) { + symbolsCollector.setFile(sourcePathId, arguments); + + bool success = symbolsCollector.collectSymbols(); + + if (success) { + Sqlite::ImmediateTransaction transaction{m_transactionInterface}; + m_symbolStorage.insertOrUpdateIndexingTimeStamps(symbolsCollector.fileStatuses()); + m_symbolStorage.addSymbolsAndSourceLocations(symbolsCollector.symbols(), + symbolsCollector.sourceLocations()); + transaction.commit(); + } + }; + + symbolIndexerTask.emplace_back(sourcePathId, projectPartId, std::move(indexing)); + } } m_symbolIndexerTaskQueue.addOrUpdateTasks(std::move(symbolIndexerTask)); @@ -136,11 +142,13 @@ void SymbolIndexer::pathsWithIdsChanged(const ProjectPartIds &) {} void SymbolIndexer::pathsChanged(const FilePathIds &filePathIds) { + FilePathIds dependentSourcePathIds = m_symbolStorage.fetchDependentSourceIds(filePathIds); + std::vector<SymbolIndexerTask> symbolIndexerTask; - symbolIndexerTask.reserve(filePathIds.size()); + symbolIndexerTask.reserve(dependentSourcePathIds.size()); - for (FilePathId filePathId : filePathIds) - updateChangedPath(filePathId, symbolIndexerTask); + for (FilePathId dependentSourcePathId : dependentSourcePathIds) + updateChangedPath(dependentSourcePathId, symbolIndexerTask); m_symbolIndexerTaskQueue.addOrUpdateTasks(std::move(symbolIndexerTask)); m_symbolIndexerTaskQueue.processEntries(); @@ -161,6 +169,8 @@ void SymbolIndexer::updateChangedPath(FilePathId filePathId, = m_precompiledHeaderStorage.fetchPrecompiledHeader(optionalArtefact->projectPartId); transaction.commit(); + SourceTimeStamps dependentTimeStamps = m_symbolStorage.fetchIncludedIndexingTimeStamps(filePathId); + const ProjectPartArtefact &artefact = *optionalArtefact; auto pchPath = optionalProjectPartPch ? optionalProjectPartPch->pchPath : FilePath{}; @@ -176,10 +186,9 @@ void SymbolIndexer::updateChangedPath(FilePathId filePathId, if (success) { Sqlite::ImmediateTransaction transaction{m_transactionInterface}; - + m_symbolStorage.insertOrUpdateIndexingTimeStamps(symbolsCollector.fileStatuses()); m_symbolStorage.addSymbolsAndSourceLocations(symbolsCollector.symbols(), symbolsCollector.sourceLocations()); - transaction.commit(); } }; diff --git a/src/tools/clangrefactoringbackend/source/symbolindexer.h b/src/tools/clangrefactoringbackend/source/symbolindexer.h index c398c0f2bf..d574d0e01d 100644 --- a/src/tools/clangrefactoringbackend/source/symbolindexer.h +++ b/src/tools/clangrefactoringbackend/source/symbolindexer.h @@ -32,6 +32,7 @@ #include "clangpathwatcher.h" #include <filecontainerv2.h> +#include <modifiedtimecheckerinterface.h> #include <precompiledheaderstorageinterface.h> #include <projectpartcontainer.h> #include <projectpartsstorageinterface.h> @@ -51,7 +52,8 @@ public: FilePathCachingInterface &filePathCache, FileStatusCache &fileStatusCache, Sqlite::TransactionInterface &transactionInterface, - ProjectPartsStorageInterface &projectPartsStorage); + ProjectPartsStorageInterface &projectPartsStorage, + ModifiedTimeCheckerInterface<SourceTimeStamps> &modifiedTimeChecker); void updateProjectParts(ProjectPartContainers &&projectParts); void updateProjectPart(ProjectPartContainer &&projectPart); @@ -81,6 +83,7 @@ private: FileStatusCache &m_fileStatusCache; Sqlite::TransactionInterface &m_transactionInterface; ProjectPartsStorageInterface &m_projectPartsStorage; + ModifiedTimeCheckerInterface<SourceTimeStamps> &m_modifiedTimeChecker; }; } // namespace ClangBackEnd diff --git a/src/tools/clangrefactoringbackend/source/symbolindexing.h b/src/tools/clangrefactoringbackend/source/symbolindexing.h index c942059bad..10df4e397b 100644 --- a/src/tools/clangrefactoringbackend/source/symbolindexing.h +++ b/src/tools/clangrefactoringbackend/source/symbolindexing.h @@ -38,13 +38,16 @@ #include <precompiledheaderstorage.h> #include <projectpartsstorage.h> -#include <refactoringdatabaseinitializer.h> #include <filepathcachingfwd.h> +#include <modifiedtimechecker.h> +#include <refactoringdatabaseinitializer.h> #include <sqlitedatabase.h> #include <sqlitereadstatement.h> #include <sqlitewritestatement.h> +#include <QDateTime> +#include <QFileInfo> #include <QFileSystemWatcher> #include <thread> @@ -130,6 +133,12 @@ private: FileStatusCache m_fileStatusCache{m_filePathCache}; SymbolsCollectorManager m_collectorManger; ProgressCounter m_progressCounter; + std::function<TimeStamp(FilePathView filePath)> getModifiedTime{ + [&](ClangBackEnd::FilePathView path) -> TimeStamp { + return QFileInfo(QString(path)).lastModified().toSecsSinceEpoch(); + }}; + ModifiedTimeChecker<ClangBackEnd::SourceTimeStamps> m_modifiedTimeChecker{getModifiedTime, + m_filePathCache}; SymbolIndexer m_indexer{m_indexerQueue, m_symbolStorage, m_buildDependencyStorage, @@ -138,7 +147,8 @@ private: m_filePathCache, m_fileStatusCache, m_symbolStorage.database, - m_projectPartsStorage}; + m_projectPartsStorage, + m_modifiedTimeChecker}; SymbolIndexerTaskQueue m_indexerQueue{m_indexerScheduler, m_progressCounter}; SymbolIndexerTaskScheduler m_indexerScheduler; }; diff --git a/src/tools/clangrefactoringbackend/source/symbolstorage.h b/src/tools/clangrefactoringbackend/source/symbolstorage.h index 9dffe48c96..f21b0412b8 100644 --- a/src/tools/clangrefactoringbackend/source/symbolstorage.h +++ b/src/tools/clangrefactoringbackend/source/symbolstorage.h @@ -41,7 +41,7 @@ namespace ClangBackEnd { -template <typename DatabaseType> +template<typename DatabaseType = Sqlite::Database> class SymbolStorage final : public SymbolStorageInterface { using Database = DatabaseType; @@ -70,6 +70,93 @@ public: deleteNewLocationsTable(); } + void insertOrUpdateIndexingTimeStamps(const FilePathIds &filePathIds, TimeStamp indexingTimeStamp) + { + try { + Sqlite::ImmediateTransaction transaction{database}; + + for (FilePathId filePathId : filePathIds) { + inserOrUpdateIndexingTimesStampStatement.write(filePathId.filePathId, + indexingTimeStamp.value); + } + + transaction.commit(); + } catch (const Sqlite::StatementIsBusy &) { + insertOrUpdateIndexingTimeStamps(filePathIds, indexingTimeStamp); + } + } + + void insertOrUpdateIndexingTimeStamps(const FileStatuses &fileStatuses) override + { + for (FileStatus fileStatus : fileStatuses) { + inserOrUpdateIndexingTimesStampStatement.write(fileStatus.filePathId.filePathId, + fileStatus.lastModified); + } + } + + SourceTimeStamps fetchIndexingTimeStamps() const + { + try { + Sqlite::DeferredTransaction transaction{database}; + + auto timeStamps = fetchIndexingTimeStampsStatement.template values<SourceTimeStamp, 2>( + 1024); + + transaction.commit(); + + return timeStamps; + } catch (const Sqlite::StatementIsBusy &) { + return fetchIndexingTimeStamps(); + } + } + + SourceTimeStamps fetchIncludedIndexingTimeStamps(FilePathId sourcePathId) const + { + try { + Sqlite::DeferredTransaction transaction{database}; + + auto timeStamps = fetchIncludedIndexingTimeStampsStatement + .template values<SourceTimeStamp, 2>(1024, sourcePathId.filePathId); + + transaction.commit(); + + return timeStamps; + } catch (const Sqlite::StatementIsBusy &) { + return fetchIncludedIndexingTimeStamps(sourcePathId); + } + } + + FilePathIds fetchDependentSourceIds(const FilePathIds &sourcePathIds) const override + { + try { + FilePathIds dependentSourceIds; + + Sqlite::DeferredTransaction transaction{database}; + + for (FilePathId sourcePathId : sourcePathIds) { + FilePathIds newDependentSourceIds; + newDependentSourceIds.reserve(dependentSourceIds.size() + 1024); + + auto newIds = fetchDependentSourceIdsStatement + .template values<FilePathId>(1024, sourcePathId.filePathId); + + std::set_union(dependentSourceIds.begin(), + dependentSourceIds.end(), + newIds.begin(), + newIds.end(), + std::back_inserter(newDependentSourceIds)); + + dependentSourceIds = std::move(newDependentSourceIds); + } + + transaction.commit(); + + return dependentSourceIds; + } catch (const Sqlite::StatementIsBusy &) { + return fetchDependentSourceIds(sourcePathIds); + } + } + void fillTemporarySymbolsTable(const SymbolEntries &symbolEntries) { WriteStatement &statement = insertSymbolsToNewSymbolsStatement; @@ -191,6 +278,25 @@ public: database}; WriteStatement deleteNewSymbolsTableStatement{"DELETE FROM newSymbols", database}; WriteStatement deleteNewLocationsTableStatement{"DELETE FROM newLocations", database}; + WriteStatement inserOrUpdateIndexingTimesStampStatement{ + "INSERT INTO fileStatuses(sourceId, indexingTimeStamp) VALUES (?001, ?002) ON " + "CONFLICT(sourceId) DO UPDATE SET indexingTimeStamp = ?002", + database}; + mutable ReadStatement fetchIncludedIndexingTimeStampsStatement{ + "WITH RECURSIVE collectedDependencies(sourceId) AS (VALUES(?) UNION SELECT " + "dependencySourceId FROM sourceDependencies, collectedDependencies WHERE " + "sourceDependencies.sourceId == collectedDependencies.sourceId) SELECT DISTINCT sourceId, " + "indexingTimeStamp FROM collectedDependencies NATURAL JOIN fileStatuses ORDER BY sourceId", + database}; + mutable ReadStatement fetchIndexingTimeStampsStatement{ + "SELECT sourceId, indexingTimeStamp FROM fileStatuses", database}; + mutable ReadStatement fetchDependentSourceIdsStatement{ + "WITH RECURSIVE collectedDependencies(sourceId) AS (VALUES(?) UNION SELECT " + "sourceDependencies.sourceId FROM sourceDependencies, collectedDependencies WHERE " + "sourceDependencies.dependencySourceId == collectedDependencies.sourceId) SELECT sourceId " + "FROM collectedDependencies WHERE sourceId NOT IN (SELECT dependencySourceId FROM " + "sourceDependencies) ORDER BY sourceId", + database}; }; } // namespace ClangBackEnd diff --git a/src/tools/clangrefactoringbackend/source/symbolstorageinterface.h b/src/tools/clangrefactoringbackend/source/symbolstorageinterface.h index 1d04c4d168..d3c8035e8c 100644 --- a/src/tools/clangrefactoringbackend/source/symbolstorageinterface.h +++ b/src/tools/clangrefactoringbackend/source/symbolstorageinterface.h @@ -28,6 +28,9 @@ #include "sourcelocationentry.h" #include "symbolentry.h" +#include <filestatus.h> +#include <sourceentry.h> + #include <compilermacro.h> #include <sqlitetransaction.h> @@ -45,6 +48,11 @@ public: virtual void addSymbolsAndSourceLocations(const SymbolEntries &symbolEntries, const SourceLocationEntries &sourceLocations) = 0; + virtual void insertOrUpdateIndexingTimeStamps(const FilePathIds &filePathIds, TimeStamp indexingTimeStamp) = 0; + virtual void insertOrUpdateIndexingTimeStamps(const FileStatuses &fileStatuses) = 0; + virtual SourceTimeStamps fetchIndexingTimeStamps() const = 0; + virtual SourceTimeStamps fetchIncludedIndexingTimeStamps(FilePathId sourcePathId) const = 0; + virtual FilePathIds fetchDependentSourceIds(const FilePathIds &sourcePathIds) const = 0; protected: ~SymbolStorageInterface() = default; diff --git a/tests/unit/unittest/builddependenciesprovider-test.cpp b/tests/unit/unittest/builddependenciesprovider-test.cpp index 772a435a58..c9794c8cc0 100644 --- a/tests/unit/unittest/builddependenciesprovider-test.cpp +++ b/tests/unit/unittest/builddependenciesprovider-test.cpp @@ -58,7 +58,7 @@ class BuildDependenciesProvider : public testing::Test protected: NiceMock<MockSqliteTransactionBackend> mockSqliteTransactionBackend; NiceMock<MockBuildDependenciesStorage> mockBuildDependenciesStorage; - NiceMock<MockModifiedTimeChecker> mockModifiedTimeChecker; + NiceMock<MockSourceEntriesModifiedTimeChecker> mockModifiedTimeChecker; NiceMock<MockBuildDependencyGenerator> mockBuildDependenciesGenerator; ClangBackEnd::BuildDependenciesProvider provider{mockBuildDependenciesStorage, mockModifiedTimeChecker, diff --git a/tests/unit/unittest/clangdocument-test.cpp b/tests/unit/unittest/clangdocument-test.cpp index 26a4ab1bfd..4978f41fd7 100644 --- a/tests/unit/unittest/clangdocument-test.cpp +++ b/tests/unit/unittest/clangdocument-test.cpp @@ -201,7 +201,7 @@ TEST_F(DocumentSlowTest, NeedsReparseAfterChangeOfMainFile) ASSERT_TRUE(document.isDirty()); } -TEST_F(DocumentSlowTest, NoNeedForReparsingForIndependendFile) +TEST_F(DocumentSlowTest, NoNeedForReparsingForIndependentFile) { document.parse(); @@ -210,7 +210,7 @@ TEST_F(DocumentSlowTest, NoNeedForReparsingForIndependendFile) ASSERT_FALSE(document.isDirty()); } -TEST_F(DocumentSlowTest, NeedsReparsingForDependendFile) +TEST_F(DocumentSlowTest, NeedsReparsingForDependentFile) { document.parse(); diff --git a/tests/unit/unittest/clangupdateannotationsjob-test.cpp b/tests/unit/unittest/clangupdateannotationsjob-test.cpp index 0bcd5a67c0..79623619c1 100644 --- a/tests/unit/unittest/clangupdateannotationsjob-test.cpp +++ b/tests/unit/unittest/clangupdateannotationsjob-test.cpp @@ -97,16 +97,16 @@ TEST_F(UpdateAnnotationsJobSlowTest, DontSendAnnotationsIfDocumentRevisionChange ASSERT_TRUE(waitUntilJobFinished(job)); } -TEST_F(UpdateAnnotationsJobSlowTest, UpdatesDependendFilePaths) +TEST_F(UpdateAnnotationsJobSlowTest, UpdatesDependentFilePaths) { - const QSet<Utf8String> dependendOnFilesBefore = document.dependedFilePaths(); + const QSet<Utf8String> dependentOnFilesBefore = document.dependedFilePaths(); job.setContext(jobContext); job.prepareAsyncRun(); job.runAsync(); ASSERT_TRUE(waitUntilJobFinished(job)); - ASSERT_THAT(dependendOnFilesBefore, Not(document.dependedFilePaths())); + ASSERT_THAT(dependentOnFilesBefore, Not(document.dependedFilePaths())); } TEST_F(UpdateAnnotationsJobSlowTest, UpdatesUnresolvedFilePaths) diff --git a/tests/unit/unittest/gtest-creator-printing.cpp b/tests/unit/unittest/gtest-creator-printing.cpp index 8644edea4d..84add6b4a1 100644 --- a/tests/unit/unittest/gtest-creator-printing.cpp +++ b/tests/unit/unittest/gtest-creator-printing.cpp @@ -1230,6 +1230,16 @@ std::ostream &operator<<(std::ostream &out, const SourceEntry &entry) << typeToString(entry.hasMissingIncludes) << ")"; } +std::ostream &operator<<(std::ostream &out, const SourceTimeStamp &sourceTimeStamp) +{ + return out << "(" << sourceTimeStamp.sourceId << ", " << sourceTimeStamp.timeStamp << ")"; +} + +std::ostream &operator<<(std::ostream &out, const TimeStamp &timeStamp) +{ + return out << timeStamp.value; +} + const char *typeToString(IncludeSearchPathType type) { switch (type) { diff --git a/tests/unit/unittest/gtest-creator-printing.h b/tests/unit/unittest/gtest-creator-printing.h index 9e2c5632f5..2d83535d14 100644 --- a/tests/unit/unittest/gtest-creator-printing.h +++ b/tests/unit/unittest/gtest-creator-printing.h @@ -165,7 +165,7 @@ class UpdateProjectPartsMessage; class DocumentsChangedMessage; class DocumentVisibilityChangedMessage; class FilePath; -template <char WindowsSlash> +template<char WindowsSlash> class AbstractFilePathView; using FilePathView = AbstractFilePathView<'/'>; using NativeFilePathView = AbstractFilePathView<'\\'>; @@ -190,6 +190,8 @@ class PchTask; class PchTaskSet; class BuildDependency; class SourceEntry; +class SourceTimeStamp; +class TimeStamp; class FilePathCaching; struct SlotUsage; class IncludeSearchPath; @@ -281,6 +283,8 @@ std::ostream &operator<<(std::ostream &out, const PchTask &task); std::ostream &operator<<(std::ostream &out, const PchTaskSet &taskSet); std::ostream &operator<<(std::ostream &out, const BuildDependency &dependency); std::ostream &operator<<(std::ostream &out, const SourceEntry &entry); +std::ostream &operator<<(std::ostream &out, const SourceTimeStamp &sourceTimeStamp); +std::ostream &operator<<(std::ostream &out, const TimeStamp &timeStamp); std::ostream &operator<<(std::ostream &out, const SlotUsage &slotUsage); std::ostream &operator<<(std::ostream &out, const IncludeSearchPathType &pathType); std::ostream &operator<<(std::ostream &out, const IncludeSearchPath &path); diff --git a/tests/unit/unittest/mockmodifiedtimechecker.h b/tests/unit/unittest/mockmodifiedtimechecker.h index 5f0559f682..bf101988b1 100644 --- a/tests/unit/unittest/mockmodifiedtimechecker.h +++ b/tests/unit/unittest/mockmodifiedtimechecker.h @@ -29,9 +29,17 @@ #include <modifiedtimecheckerinterface.h> -class MockModifiedTimeChecker : public ClangBackEnd::ModifiedTimeCheckerInterface +class MockSourceEntriesModifiedTimeChecker + : public ClangBackEnd::ModifiedTimeCheckerInterface<ClangBackEnd::SourceEntries> { public: MOCK_CONST_METHOD1(isUpToDate, bool (const ClangBackEnd::SourceEntries &sourceEntries)); }; + +class MockSourceTimeStampsModifiedTimeChecker + : public ClangBackEnd::ModifiedTimeCheckerInterface<ClangBackEnd::SourceTimeStamps> +{ +public: + MOCK_CONST_METHOD1(isUpToDate, bool(const ClangBackEnd::SourceTimeStamps &sourceTimeStamps)); +}; diff --git a/tests/unit/unittest/mockprojectpartsstorage.h b/tests/unit/unittest/mockprojectpartsstorage.h index 114a7fab0e..e8dcbc94b2 100644 --- a/tests/unit/unittest/mockprojectpartsstorage.h +++ b/tests/unit/unittest/mockprojectpartsstorage.h @@ -56,5 +56,7 @@ public: MOCK_CONST_METHOD1(fetchProjectPartArtefact, Utils::optional<ClangBackEnd::ProjectPartArtefact>( ClangBackEnd::ProjectPartId projectPartId)); + MOCK_METHOD1(resetIndexingTimeStamps, + void(const ClangBackEnd::ProjectPartContainers &projectsParts)); MOCK_METHOD0(transactionBackend, Sqlite::TransactionInterface &()); }; diff --git a/tests/unit/unittest/mocksqlitereadstatement.cpp b/tests/unit/unittest/mocksqlitereadstatement.cpp index c12d40b9ea..2e9d74acf6 100644 --- a/tests/unit/unittest/mocksqlitereadstatement.cpp +++ b/tests/unit/unittest/mocksqlitereadstatement.cpp @@ -222,6 +222,19 @@ SourceEntries MockSqliteReadStatement::values<SourceEntry, 4>(std::size_t reserv return valuesReturnSourceEntries(reserveSize, filePathId, projectPartId); } +template<> +SourceTimeStamps MockSqliteReadStatement::values<SourceTimeStamp, 2>(std::size_t reserveSize) +{ + return valuesReturnSourceTimeStamps(reserveSize); +} + +template<> +SourceTimeStamps MockSqliteReadStatement::values<SourceTimeStamp, 2>(std::size_t reserveSize, + const int &sourcePathId) +{ + return valuesReturnSourceTimeStamps(reserveSize, sourcePathId); +} + template <> Utils::optional<Sources::SourceNameAndDirectoryId> MockSqliteReadStatement::value<Sources::SourceNameAndDirectoryId, 2>(const int &id) diff --git a/tests/unit/unittest/mocksqlitereadstatement.h b/tests/unit/unittest/mocksqlitereadstatement.h index e3ab740b6e..135a6d1851 100644 --- a/tests/unit/unittest/mocksqlitereadstatement.h +++ b/tests/unit/unittest/mocksqlitereadstatement.h @@ -50,6 +50,8 @@ using ClangBackEnd::FilePathIds; using ClangBackEnd::SourceEntries; using ClangBackEnd::SourceEntry; +using ClangBackEnd::SourceTimeStamp; +using ClangBackEnd::SourceTimeStamps; using ClangRefactoring::SourceLocation; using ClangRefactoring::SourceLocations; using std::int64_t; @@ -136,6 +138,9 @@ public: MOCK_METHOD1(valueReturnProjectPartId, Utils::optional<ClangBackEnd::ProjectPartId>(Utils::SmallStringView)); + MOCK_METHOD1(valuesReturnSourceTimeStamps, SourceTimeStamps(std::size_t)); + MOCK_METHOD2(valuesReturnSourceTimeStamps, SourceTimeStamps(std::size_t, int sourcePathId)); + template <typename ResultType, int ResultTypeCount = 1, typename... QueryType> @@ -283,6 +288,13 @@ SourceEntries MockSqliteReadStatement::values<SourceEntry, 4>(std::size_t reserv const int &, const int &); +template<> +SourceTimeStamps MockSqliteReadStatement::values<SourceTimeStamp, 2>(std::size_t reserveSize); + +template<> +SourceTimeStamps MockSqliteReadStatement::values<SourceTimeStamp, 2>(std::size_t reserveSize, + const int &sourcePathId); + template <> Utils::optional<Sources::SourceNameAndDirectoryId> MockSqliteReadStatement::value<Sources::SourceNameAndDirectoryId, 2>(const int&); diff --git a/tests/unit/unittest/mocksymbolstorage.h b/tests/unit/unittest/mocksymbolstorage.h index a174389735..7b77868e8a 100644 --- a/tests/unit/unittest/mocksymbolstorage.h +++ b/tests/unit/unittest/mocksymbolstorage.h @@ -37,4 +37,11 @@ public: MOCK_METHOD2(addSymbolsAndSourceLocations, void(const ClangBackEnd::SymbolEntries &symbolEentries, const ClangBackEnd::SourceLocationEntries &sourceLocations)); + MOCK_METHOD2(insertOrUpdateIndexingTimeStamps, + void(const FilePathIds &filePathIds, ClangBackEnd::TimeStamp indexingTimeStamp)); + MOCK_METHOD1(insertOrUpdateIndexingTimeStamps, void(const ClangBackEnd::FileStatuses &)); + MOCK_CONST_METHOD0(fetchIndexingTimeStamps, ClangBackEnd::SourceTimeStamps()); + MOCK_CONST_METHOD1(fetchIncludedIndexingTimeStamps, + ClangBackEnd::SourceTimeStamps(ClangBackEnd::FilePathId sourcePathId)); + MOCK_CONST_METHOD1(fetchDependentSourceIds, FilePathIds(const FilePathIds &sourcePathIds)); }; diff --git a/tests/unit/unittest/modifiedtimechecker-test.cpp b/tests/unit/unittest/modifiedtimechecker-test.cpp index 8e0c881b38..ff0c3c66d8 100644 --- a/tests/unit/unittest/modifiedtimechecker-test.cpp +++ b/tests/unit/unittest/modifiedtimechecker-test.cpp @@ -57,7 +57,7 @@ protected: ClangBackEnd::FilePathCaching filePathCache{database}; decltype(getModifiedTimeCallback.AsStdFunction()) callback = getModifiedTimeCallback .AsStdFunction(); - ClangBackEnd::ModifiedTimeChecker checker{callback, filePathCache}; + ClangBackEnd::ModifiedTimeChecker<> checker{callback, filePathCache}; SourceEntries upToDateEntries = {{id("/path1"), SourceType::UserInclude, 100}, {id("/path2"), SourceType::SystemInclude, 30}}; SourceEntries notUpToDateEntries = {{id("/path1"), SourceType::UserInclude, 50}, diff --git a/tests/unit/unittest/projectpartsmanager-test.cpp b/tests/unit/unittest/projectpartsmanager-test.cpp index 235a7553ba..b12cbb6b2e 100644 --- a/tests/unit/unittest/projectpartsmanager-test.cpp +++ b/tests/unit/unittest/projectpartsmanager-test.cpp @@ -302,6 +302,7 @@ TEST_F(ProjectPartsManager, UpdateCallsIfNewProjectPartIsAdded) EXPECT_CALL(mockProjectPartsStorage, updateProjectParts(ElementsAre(projectPartContainer1))); EXPECT_CALL(mockPrecompiledHeaderStorage, deleteProjectPrecompiledHeaders(ElementsAre(projectPartContainer1.projectPartId))); + EXPECT_CALL(mockProjectPartsStorage, resetIndexingTimeStamps(ElementsAre(projectPartContainer1))); manager.update({projectPartContainer1}); } @@ -337,6 +338,16 @@ TEST_F(ProjectPartsManager, UpdateCallsNotDeleteProjectPrecompiledHeadersIfNoNew manager.update({projectPartContainer1}); } +TEST_F(ProjectPartsManager, UpdateCallsNotResetIndexingTimeStampsIfNoNewerProjectPartsExists) +{ + manager.update({projectPartContainer1}); + + EXPECT_CALL(mockProjectPartsStorage, resetIndexingTimeStamps(ElementsAre(projectPartContainer1))) + .Times(0); + + manager.update({projectPartContainer1}); +} + TEST_F(ProjectPartsManager, UpdateCallsIfOldProjectPartIsAdded) { EXPECT_CALL(mockProjectPartsStorage, @@ -346,6 +357,8 @@ TEST_F(ProjectPartsManager, UpdateCallsIfOldProjectPartIsAdded) EXPECT_CALL(mockPrecompiledHeaderStorage, deleteProjectPrecompiledHeaders(ElementsAre(projectPartContainer1.projectPartId))) .Times(0); + EXPECT_CALL(mockProjectPartsStorage, resetIndexingTimeStamps(ElementsAre(projectPartContainer1))) + .Times(0); manager.update({projectPartContainer1}); } @@ -361,6 +374,8 @@ TEST_F(ProjectPartsManager, UpdateCallsIfUpdatedProjectPartIsAdded) updateProjectParts(ElementsAre(updatedProjectPartContainer1))); EXPECT_CALL(mockPrecompiledHeaderStorage, deleteProjectPrecompiledHeaders(ElementsAre(projectPartContainer1.projectPartId))); + EXPECT_CALL(mockProjectPartsStorage, + resetIndexingTimeStamps(ElementsAre(updatedProjectPartContainer1))); manager.update({updatedProjectPartContainer1}); } diff --git a/tests/unit/unittest/projectpartsstorage-test.cpp b/tests/unit/unittest/projectpartsstorage-test.cpp index 963248cd29..3c13f33ad6 100644 --- a/tests/unit/unittest/projectpartsstorage-test.cpp +++ b/tests/unit/unittest/projectpartsstorage-test.cpp @@ -27,12 +27,13 @@ #include "mocksqlitedatabase.h" +#include <builddependenciesstorage.h> #include <projectpartsstorage.h> #include <refactoringdatabaseinitializer.h> #include <sqlitedatabase.h> #include <sqlitereadstatement.h> #include <sqlitewritestatement.h> - +#include <symbolstorage.h> namespace { using ClangBackEnd::FilePathId; @@ -104,6 +105,7 @@ protected: MockSqliteReadStatement &fetchProjectPartsHeadersByIdStatement = storage.fetchProjectPartsHeadersByIdStatement; MockSqliteReadStatement &fetchProjectPartsSourcesByIdStatement = storage.fetchProjectPartsSourcesByIdStatement; MockSqliteReadStatement &fetchProjectPrecompiledHeaderPathStatement = storage.fetchProjectPrecompiledHeaderPathStatement; + MockSqliteWriteStatement &resetDependentIndexingTimeStampsStatement = storage.resetDependentIndexingTimeStampsStatement; IncludeSearchPaths systemIncludeSearchPaths{{"/includes", 1, IncludeSearchPathType::BuiltIn}, {"/other/includes", 2, IncludeSearchPathType::System}}; IncludeSearchPaths projectIncludeSearchPaths{{"/project/includes", 1, IncludeSearchPathType::User}, @@ -424,6 +426,35 @@ TEST_F(ProjectPartsStorage, FetchProjectPartArtefactByProjectPartIdReturnArtefac ASSERT_THAT(result, Eq(artefact)); } +TEST_F(ProjectPartsStorage, ResetDependentIndexingTimeStamps) +{ + InSequence s; + + EXPECT_CALL(mockDatabase, immediateBegin()); + EXPECT_CALL(resetDependentIndexingTimeStampsStatement, write(TypedEq<int>(3))); + EXPECT_CALL(resetDependentIndexingTimeStampsStatement, write(TypedEq<int>(4))); + EXPECT_CALL(resetDependentIndexingTimeStampsStatement, write(TypedEq<int>(7))); + EXPECT_CALL(resetDependentIndexingTimeStampsStatement, write(TypedEq<int>(8))); + EXPECT_CALL(mockDatabase, commit()); + + storage.resetIndexingTimeStamps({projectPart1, projectPart2}); +} + +TEST_F(ProjectPartsStorage, ResetDependentIndexingTimeStampsIsBusy) +{ + InSequence s; + + EXPECT_CALL(mockDatabase, immediateBegin()).WillOnce(Throw(Sqlite::StatementIsBusy{""})); + EXPECT_CALL(mockDatabase, immediateBegin()); + EXPECT_CALL(resetDependentIndexingTimeStampsStatement, write(TypedEq<int>(3))); + EXPECT_CALL(resetDependentIndexingTimeStampsStatement, write(TypedEq<int>(4))); + EXPECT_CALL(resetDependentIndexingTimeStampsStatement, write(TypedEq<int>(7))); + EXPECT_CALL(resetDependentIndexingTimeStampsStatement, write(TypedEq<int>(8))); + EXPECT_CALL(mockDatabase, commit()); + + storage.resetIndexingTimeStamps({projectPart1, projectPart2}); +} + class ProjectPartsStorageSlow : public testing::Test, public Data { using Storage = ClangBackEnd::ProjectPartsStorage<Sqlite::Database>; @@ -432,6 +463,8 @@ protected: Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; ClangBackEnd::RefactoringDatabaseInitializer<Sqlite::Database> databaseInitializer{database}; Storage storage{database}; + ClangBackEnd::SymbolStorage<> symbolStorage{database}; + ClangBackEnd::BuildDependenciesStorage<> buildDependenciesStorage{database}; }; TEST_F(ProjectPartsStorageSlow, FetchProjectPartName) @@ -469,4 +502,26 @@ TEST_F(ProjectPartsStorageSlow, FetchProjectParts) ASSERT_THAT(projectParts, ElementsAre(projectPart1, projectPart2)); } + +TEST_F(ProjectPartsStorageSlow, ResetDependentIndexingTimeStamps) +{ + symbolStorage.insertOrUpdateIndexingTimeStamps({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 34); + buildDependenciesStorage.insertOrUpdateSourceDependencies( + {{3, 1}, {4, 1}, {1, 2}, {7, 5}, {8, 6}, {6, 5}, {9, 10}}); + + storage.resetIndexingTimeStamps({projectPart1, projectPart2}); + + ASSERT_THAT(symbolStorage.fetchIndexingTimeStamps(), + ElementsAre(SourceTimeStamp{1, 0}, + SourceTimeStamp{2, 0}, + SourceTimeStamp{3, 0}, + SourceTimeStamp{4, 0}, + SourceTimeStamp{5, 0}, + SourceTimeStamp{6, 0}, + SourceTimeStamp{7, 0}, + SourceTimeStamp{8, 0}, + SourceTimeStamp{9, 34}, + SourceTimeStamp{10, 34})); +} + } // namespace diff --git a/tests/unit/unittest/refactoringdatabaseinitializer-test.cpp b/tests/unit/unittest/refactoringdatabaseinitializer-test.cpp index 4865e2ebff..7177060311 100644 --- a/tests/unit/unittest/refactoringdatabaseinitializer-test.cpp +++ b/tests/unit/unittest/refactoringdatabaseinitializer-test.cpp @@ -131,7 +131,7 @@ TEST_F(RefactoringDatabaseInitializer, AddFileStatusesTable) mockDatabase, execute(Eq( "CREATE TABLE IF NOT EXISTS fileStatuses(sourceId INTEGER PRIMARY KEY, size INTEGER, " - "lastModified INTEGER)"))); + "lastModified INTEGER, indexingTimeStamp INTEGER)"))); initializer.createFileStatusesTable(); } @@ -140,8 +140,19 @@ TEST_F(RefactoringDatabaseInitializer, AddSourceDependenciesTable) { InSequence s; - EXPECT_CALL(mockDatabase, execute(Eq("CREATE TABLE IF NOT EXISTS sourceDependencies(sourceId INTEGER, dependencySourceId INTEGER)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE INDEX IF NOT EXISTS index_sourceDependencies_sourceId_dependencySourceId ON sourceDependencies(sourceId, dependencySourceId)"))); + EXPECT_CALL(mockDatabase, + execute(Eq("CREATE TABLE IF NOT EXISTS sourceDependencies(sourceId INTEGER, " + "dependencySourceId INTEGER)"))); + EXPECT_CALL( + mockDatabase, + execute( + Eq("CREATE INDEX IF NOT EXISTS index_sourceDependencies_sourceId_dependencySourceId ON " + "sourceDependencies(sourceId, dependencySourceId)"))); + EXPECT_CALL( + mockDatabase, + execute( + Eq("CREATE INDEX IF NOT EXISTS index_sourceDependencies_dependencySourceId_sourceId ON " + "sourceDependencies(dependencySourceId, sourceId)"))); initializer.createSourceDependenciesTable(); } @@ -189,40 +200,88 @@ TEST_F(RefactoringDatabaseInitializer, CreateInTheContructor) EXPECT_CALL(mockDatabase, isInitialized()).WillOnce(Return(false)); EXPECT_CALL(mockDatabase, exclusiveBegin()); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE TABLE IF NOT EXISTS symbols(symbolId INTEGER PRIMARY KEY, usr TEXT, symbolName TEXT, symbolKind INTEGER, signature TEXT)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE INDEX IF NOT EXISTS index_symbols_usr ON symbols(usr)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE INDEX IF NOT EXISTS index_symbols_symbolKind_symbolName ON symbols(symbolKind, symbolName)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE TABLE IF NOT EXISTS locations(symbolId INTEGER, line INTEGER, column INTEGER, sourceId INTEGER, locationKind INTEGER)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_locations_sourceId_line_column ON locations(sourceId, line, column)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE INDEX IF NOT EXISTS index_locations_sourceId_locationKind ON locations(sourceId, locationKind)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE TABLE IF NOT EXISTS sources(sourceId INTEGER PRIMARY KEY, directoryId INTEGER, sourceName TEXT)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_sources_directoryId_sourceName ON sources(directoryId, sourceName)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE TABLE IF NOT EXISTS directories(directoryId INTEGER PRIMARY KEY, directoryPath TEXT)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_directories_directoryPath ON directories(directoryPath)"))); + EXPECT_CALL(mockDatabase, + execute(Eq("CREATE TABLE IF NOT EXISTS symbols(symbolId INTEGER PRIMARY KEY, usr " + "TEXT, symbolName TEXT, symbolKind INTEGER, signature TEXT)"))); + EXPECT_CALL(mockDatabase, + execute(Eq("CREATE INDEX IF NOT EXISTS index_symbols_usr ON symbols(usr)"))); + EXPECT_CALL(mockDatabase, + execute(Eq("CREATE INDEX IF NOT EXISTS index_symbols_symbolKind_symbolName ON " + "symbols(symbolKind, symbolName)"))); + EXPECT_CALL(mockDatabase, + execute(Eq("CREATE TABLE IF NOT EXISTS locations(symbolId INTEGER, line INTEGER, " + "column INTEGER, sourceId INTEGER, locationKind INTEGER)"))); + EXPECT_CALL(mockDatabase, + execute(Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_locations_sourceId_line_column " + "ON locations(sourceId, line, column)"))); + EXPECT_CALL(mockDatabase, + execute(Eq("CREATE INDEX IF NOT EXISTS index_locations_sourceId_locationKind ON " + "locations(sourceId, locationKind)"))); + EXPECT_CALL(mockDatabase, + execute(Eq("CREATE TABLE IF NOT EXISTS sources(sourceId INTEGER PRIMARY KEY, " + "directoryId INTEGER, sourceName TEXT)"))); + EXPECT_CALL(mockDatabase, + execute(Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_sources_directoryId_sourceName " + "ON sources(directoryId, sourceName)"))); + EXPECT_CALL(mockDatabase, + execute(Eq("CREATE TABLE IF NOT EXISTS directories(directoryId INTEGER PRIMARY " + "KEY, directoryPath TEXT)"))); + EXPECT_CALL(mockDatabase, + execute(Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_directories_directoryPath ON " + "directories(directoryPath)"))); EXPECT_CALL(mockDatabase, execute( Eq("CREATE TABLE IF NOT EXISTS projectParts(projectPartId INTEGER PRIMARY " "KEY, projectPartName TEXT, toolChainArguments TEXT, compilerMacros " "TEXT, systemIncludeSearchPaths TEXT, projectIncludeSearchPaths TEXT, " "language INTEGER, languageVersion INTEGER, languageExtension INTEGER)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_projectParts_projectPartName ON projectParts(projectPartName)"))); + EXPECT_CALL(mockDatabase, + execute(Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_projectParts_projectPartName " + "ON projectParts(projectPartName)"))); EXPECT_CALL(mockDatabase, execute(Eq("CREATE TABLE IF NOT EXISTS projectPartsFiles(projectPartId INTEGER, " "sourceId INTEGER, sourceType INTEGER, pchCreationTimeStamp INTEGER, " "hasMissingIncludes INTEGER)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_projectPartsFiles_sourceId_projectPartId ON projectPartsFiles(sourceId, projectPartId)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE INDEX IF NOT EXISTS index_projectPartsFiles_projectPartId ON projectPartsFiles(projectPartId)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE TABLE IF NOT EXISTS usedMacros(usedMacroId INTEGER PRIMARY KEY, sourceId INTEGER, macroName TEXT)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE INDEX IF NOT EXISTS index_usedMacros_sourceId_macroName ON usedMacros(sourceId, macroName)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE INDEX IF NOT EXISTS index_usedMacros_macroName ON usedMacros(macroName)"))); + EXPECT_CALL( + mockDatabase, + execute( + Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_projectPartsFiles_sourceId_projectPartId " + "ON projectPartsFiles(sourceId, projectPartId)"))); + EXPECT_CALL(mockDatabase, + execute(Eq("CREATE INDEX IF NOT EXISTS index_projectPartsFiles_projectPartId ON " + "projectPartsFiles(projectPartId)"))); + EXPECT_CALL(mockDatabase, + execute(Eq("CREATE TABLE IF NOT EXISTS usedMacros(usedMacroId INTEGER PRIMARY KEY, " + "sourceId INTEGER, macroName TEXT)"))); + EXPECT_CALL(mockDatabase, + execute(Eq("CREATE INDEX IF NOT EXISTS index_usedMacros_sourceId_macroName ON " + "usedMacros(sourceId, macroName)"))); + EXPECT_CALL( + mockDatabase, + execute( + Eq("CREATE INDEX IF NOT EXISTS index_usedMacros_macroName ON usedMacros(macroName)"))); EXPECT_CALL( mockDatabase, execute(Eq( "CREATE TABLE IF NOT EXISTS fileStatuses(sourceId INTEGER PRIMARY KEY, size INTEGER, " - "lastModified INTEGER)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE TABLE IF NOT EXISTS sourceDependencies(sourceId INTEGER, dependencySourceId INTEGER)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE INDEX IF NOT EXISTS index_sourceDependencies_sourceId_dependencySourceId ON sourceDependencies(sourceId, dependencySourceId)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE TABLE IF NOT EXISTS precompiledHeaders(projectPartId INTEGER PRIMARY KEY, projectPchPath TEXT, projectPchBuildTime INTEGER, systemPchPath TEXT, systemPchBuildTime INTEGER)"))); + "lastModified INTEGER, indexingTimeStamp INTEGER)"))); + EXPECT_CALL(mockDatabase, + execute(Eq("CREATE TABLE IF NOT EXISTS sourceDependencies(sourceId INTEGER, " + "dependencySourceId INTEGER)"))); + EXPECT_CALL( + mockDatabase, + execute( + Eq("CREATE INDEX IF NOT EXISTS index_sourceDependencies_sourceId_dependencySourceId ON " + "sourceDependencies(sourceId, dependencySourceId)"))); + EXPECT_CALL( + mockDatabase, + execute( + Eq("CREATE INDEX IF NOT EXISTS index_sourceDependencies_dependencySourceId_sourceId ON " + "sourceDependencies(dependencySourceId, sourceId)"))); + EXPECT_CALL(mockDatabase, + execute(Eq("CREATE TABLE IF NOT EXISTS precompiledHeaders(projectPartId INTEGER " + "PRIMARY KEY, projectPchPath TEXT, projectPchBuildTime INTEGER, " + "systemPchPath TEXT, systemPchBuildTime INTEGER)"))); EXPECT_CALL(mockDatabase, execute(Eq("CREATE TABLE IF NOT EXISTS projectPartsHeaders(projectPartId INTEGER, " "sourceId INTEGER)"))); @@ -271,7 +330,7 @@ TEST_F(RefactoringDatabaseInitializer, DontCreateIfAlreadyInitialized) EXPECT_CALL(mockDatabase, execute(Eq("CREATE INDEX IF NOT EXISTS index_usedMacros_macroName ON usedMacros(macroName)"))).Times(0); EXPECT_CALL(mockDatabase, execute(Eq("CREATE TABLE IF NOT EXISTS fileStatuses(sourceId INTEGER PRIMARY KEY, " - "size INTEGER, lastModified INTEGER, isInPrecompiledHeader INTEGER)"))) + "size INTEGER, lastModified INTEGER, indexingTimeStamp INTEGER)"))) .Times(0); EXPECT_CALL(mockDatabase, execute(Eq("CREATE TABLE IF NOT EXISTS sourceDependencies(sourceId INTEGER, dependencySourceId INTEGER)"))).Times(0); EXPECT_CALL(mockDatabase, execute(Eq("CREATE INDEX IF NOT EXISTS index_sourceDependencies_sourceId_dependencySourceId ON sourceDependencies(sourceId, dependencySourceId)"))).Times(0); diff --git a/tests/unit/unittest/symbolindexer-test.cpp b/tests/unit/unittest/symbolindexer-test.cpp index edfcb286ad..0be2d3b0e5 100644 --- a/tests/unit/unittest/symbolindexer-test.cpp +++ b/tests/unit/unittest/symbolindexer-test.cpp @@ -28,6 +28,7 @@ #include "mockbuilddependenciesstorage.h" #include "mockclangpathwatcher.h" #include "mockfilepathcaching.h" +#include "mockmodifiedtimechecker.h" #include "mockprecompiledheaderstorage.h" #include "mockprojectpartsstorage.h" #include "mocksqlitetransactionbackend.h" @@ -113,7 +114,12 @@ protected: .WillByDefault(Return(artefact)); ON_CALL(mockBuildDependenciesStorage, fetchLowestLastModifiedTime(A<FilePathId>())).WillByDefault(Return(-1)); ON_CALL(mockCollector, collectSymbols()).WillByDefault(Return(true)); - + ON_CALL(mockSymbolStorage, fetchDependentSourceIds(sourceFileIds)) + .WillByDefault(Return(sourceFileIds)); + ON_CALL(mockSymbolStorage, fetchDependentSourceIds(ElementsAre(sourceFileIds[0]))) + .WillByDefault(Return(FilePathIds{sourceFileIds[0]})); + ON_CALL(mockSymbolStorage, fetchDependentSourceIds(ElementsAre(main1PathId))) + .WillByDefault(Return(FilePathIds{main1PathId})); mockCollector.setIsUsed(false); generatedFiles.update(unsaved); @@ -230,6 +236,10 @@ protected: Utils::Language::Cxx, Utils::LanguageVersion::CXX14, Utils::LanguageExtension::None}; + ClangBackEnd::SourceTimeStamps dependentSourceTimeStamps1{{1, 32}}; + ClangBackEnd::SourceTimeStamps dependentSourceTimeStamps2{{2, 35}}; + ClangBackEnd::FileStatuses fileStatuses1{{1, 0, 32}}; + ClangBackEnd::FileStatuses fileStatuses2{{2, 0, 35}}; Utils::optional<ClangBackEnd::ProjectPartArtefact > nullArtefact; ClangBackEnd::ProjectPartPch projectPartPch{74, "/path/to/pch", 4}; NiceMock<MockSqliteTransactionBackend> mockSqliteTransactionBackend; @@ -243,6 +253,7 @@ protected: Manager collectorManger{generatedFiles}; NiceMock<MockFunction<void(int, int)>> mockSetProgressCallback; ClangBackEnd::ProgressCounter progressCounter{mockSetProgressCallback.AsStdFunction()}; + NiceMock<MockSourceTimeStampsModifiedTimeChecker> mockModifiedTimeChecker; ClangBackEnd::SymbolIndexer indexer{indexerQueue, mockSymbolStorage, mockBuildDependenciesStorage, @@ -251,7 +262,8 @@ protected: filePathCache, fileStatusCache, mockSqliteTransactionBackend, - mockProjectPartsStorage}; + mockProjectPartsStorage, + mockModifiedTimeChecker}; SymbolIndexerTaskQueue indexerQueue{indexerScheduler, progressCounter}; Scheduler indexerScheduler{collectorManger, indexerQueue, @@ -493,6 +505,58 @@ TEST_F(SymbolIndexer, UpdateProjectPartsCallsInOrderButGetsAnErrorForCollectingS indexer.updateProjectParts({projectPart1}); } +TEST_F(SymbolIndexer, UpdateProjectPartsFetchIncludedIndexingTimeStamps) +{ + InSequence s; + ProjectPartContainer projectPart{1, + {"-Wno-pragma-once-outside-header"}, + {{"BAR", "1", 1}, {"FOO", "1", 2}}, + Utils::clone(systemIncludeSearchPaths), + Utils::clone(projectIncludeSearchPaths), + {header1PathId}, + {main1PathId, main2PathId}, + Utils::Language::Cxx, + Utils::LanguageVersion::CXX14, + Utils::LanguageExtension::None}; + + EXPECT_CALL(mockSymbolStorage, fetchIncludedIndexingTimeStamps(Eq(main1PathId))) + .WillOnce(Return(dependentSourceTimeStamps1)); + EXPECT_CALL(mockModifiedTimeChecker, isUpToDate(dependentSourceTimeStamps1)); + EXPECT_CALL(mockSymbolStorage, fetchIncludedIndexingTimeStamps(Eq(main2PathId))) + .WillOnce(Return(dependentSourceTimeStamps2)); + EXPECT_CALL(mockModifiedTimeChecker, isUpToDate(dependentSourceTimeStamps2)); + EXPECT_CALL(mockCollector, fileStatuses()).WillRepeatedly(ReturnRef(fileStatuses1)); + EXPECT_CALL(mockSymbolStorage, insertOrUpdateIndexingTimeStamps(fileStatuses1)); + EXPECT_CALL(mockCollector, fileStatuses()).WillRepeatedly(ReturnRef(fileStatuses2)); + EXPECT_CALL(mockSymbolStorage, insertOrUpdateIndexingTimeStamps(fileStatuses2)); + + indexer.updateProjectParts({projectPart}); +} + +TEST_F(SymbolIndexer, DependentSourceAreNotUpToDate) +{ + InSequence s; + + EXPECT_CALL(mockModifiedTimeChecker, isUpToDate(_)).WillOnce(Return(false)); + EXPECT_CALL(mockCollector, setFile(main1PathId, _)); + EXPECT_CALL(mockCollector, collectSymbols()).WillOnce(Return(true)); + EXPECT_CALL(mockSymbolStorage, addSymbolsAndSourceLocations(symbolEntries, sourceLocations)); + + indexer.updateProjectParts({projectPart1}); +} + +TEST_F(SymbolIndexer, DependentSourceAreUpToDate) +{ + InSequence s; + + EXPECT_CALL(mockModifiedTimeChecker, isUpToDate(_)).WillOnce(Return(true)); + EXPECT_CALL(mockCollector, setFile(main1PathId, _)).Times(0); + EXPECT_CALL(mockCollector, collectSymbols()).Times(0); + EXPECT_CALL(mockSymbolStorage, addSymbolsAndSourceLocations(symbolEntries, sourceLocations)).Times(0); + + indexer.updateProjectParts({projectPart1}); +} + TEST_F(SymbolIndexer, CallSetNotifier) { EXPECT_CALL(mockPathWatcher, setNotifier(_)); @@ -505,7 +569,8 @@ TEST_F(SymbolIndexer, CallSetNotifier) filePathCache, fileStatusCache, mockSqliteTransactionBackend, - mockProjectPartsStorage}; + mockProjectPartsStorage, + mockModifiedTimeChecker}; } TEST_F(SymbolIndexer, PathChangedCallsFetchProjectPartArtefactInStorage) @@ -516,6 +581,54 @@ TEST_F(SymbolIndexer, PathChangedCallsFetchProjectPartArtefactInStorage) indexer.pathsChanged(sourceFileIds); } +TEST_F(SymbolIndexer, PathChangedCallsFetchSourcePathIds) +{ + InSequence s; + + EXPECT_CALL(mockSymbolStorage, fetchDependentSourceIds(sourceFileIds)) + .WillOnce(Return(FilePathIds{2, 6, 5})); + EXPECT_CALL(mockProjectPartsStorage, fetchProjectPartArtefact(FilePathId{2})); + EXPECT_CALL(mockProjectPartsStorage, fetchProjectPartArtefact(FilePathId{6})); + EXPECT_CALL(mockProjectPartsStorage, fetchProjectPartArtefact(FilePathId{5})); + + indexer.pathsChanged(sourceFileIds); +} + +TEST_F(SymbolIndexer, PathChangedFetchIncludedIndexingTimeStamps) +{ + InSequence s; + ProjectPartContainer projectPart{1, + {"-Wno-pragma-once-outside-header"}, + {{"BAR", "1", 1}, {"FOO", "1", 2}}, + Utils::clone(systemIncludeSearchPaths), + Utils::clone(projectIncludeSearchPaths), + {header1PathId}, + {main1PathId, main2PathId}, + Utils::Language::Cxx, + Utils::LanguageVersion::CXX14, + Utils::LanguageExtension::None}; + + EXPECT_CALL(mockSymbolStorage, fetchDependentSourceIds(_)).WillOnce(Return(FilePathIds{1, 2})); + EXPECT_CALL(mockSymbolStorage, fetchIncludedIndexingTimeStamps(Eq(1))) + .WillOnce(Return(dependentSourceTimeStamps1)); + EXPECT_CALL(mockSymbolStorage, fetchIncludedIndexingTimeStamps(Eq(2))) + .WillOnce(Return(dependentSourceTimeStamps2)); + EXPECT_CALL(mockCollector, fileStatuses()).WillOnce(ReturnRef(fileStatuses1)); + EXPECT_CALL(mockSymbolStorage, insertOrUpdateIndexingTimeStamps(fileStatuses1)); + EXPECT_CALL(mockCollector, fileStatuses()).WillOnce(ReturnRef(fileStatuses2)); + EXPECT_CALL(mockSymbolStorage, insertOrUpdateIndexingTimeStamps(fileStatuses2)); + + indexer.pathsChanged({1, 3}); +} + +TEST_F(SymbolIndexer, PathChangedFetchesDependentSourceIdsFromStorage) +{ + EXPECT_CALL(mockProjectPartsStorage, fetchProjectPartArtefact(sourceFileIds[0])); + EXPECT_CALL(mockProjectPartsStorage, fetchProjectPartArtefact(sourceFileIds[1])); + + indexer.pathsChanged(sourceFileIds); +} + TEST_F(SymbolIndexer, UpdateChangedPathCallsInOrder) { InSequence s; @@ -797,6 +910,7 @@ TEST_F(SymbolIndexer, PathsChangedUpdatesFileStatusCache) auto sourceId = filePathId(TESTDATA_DIR "/symbolindexer_pathChanged.cpp"); auto oldLastModified = fileStatusCache.lastModifiedTime(sourceId); touchFile(sourceId); + ON_CALL(mockSymbolStorage, fetchDependentSourceIds(_)).WillByDefault(Return(FilePathIds{sourceId})); indexer.pathsChanged({sourceId}); diff --git a/tests/unit/unittest/symbolstorage-test.cpp b/tests/unit/unittest/symbolstorage-test.cpp index 059b1a3c0f..a29b7f92aa 100644 --- a/tests/unit/unittest/symbolstorage-test.cpp +++ b/tests/unit/unittest/symbolstorage-test.cpp @@ -28,17 +28,22 @@ #include "mockfilepathcaching.h" #include "mocksqlitedatabase.h" -#include <symbolstorage.h> +#include <builddependenciesstorage.h> +#include <refactoringdatabaseinitializer.h> #include <sqlitedatabase.h> +#include <sqlitereadstatement.h> +#include <sqlitewritestatement.h> +#include <symbolstorage.h> #include <utils/optional.h> namespace { - using ClangBackEnd::FilePathCachingInterface; +using ClangBackEnd::FilePathId; using ClangBackEnd::SourceLocationEntries; using ClangBackEnd::SourceLocationEntry; using ClangBackEnd::SourceLocationKind; +using ClangBackEnd::SourceTimeStamp; using ClangBackEnd::SymbolEntries; using ClangBackEnd::SymbolEntry; using ClangBackEnd::SymbolIndex; @@ -64,6 +69,10 @@ protected: MockSqliteWriteStatement &insertNewLocationsInLocationsStatement = storage.insertNewLocationsInLocationsStatement; MockSqliteWriteStatement &deleteNewSymbolsTableStatement = storage.deleteNewSymbolsTableStatement; MockSqliteWriteStatement &deleteNewLocationsTableStatement = storage.deleteNewLocationsTableStatement; + MockSqliteWriteStatement &inserOrUpdateIndexingTimesStampStatement = storage.inserOrUpdateIndexingTimesStampStatement; + MockSqliteReadStatement &fetchIndexingTimeStampsStatement = storage.fetchIndexingTimeStampsStatement; + MockSqliteReadStatement &fetchIncludedIndexingTimeStampsStatement = storage.fetchIncludedIndexingTimeStampsStatement; + MockSqliteReadStatement &fetchDependentSourceIdsStatement = storage.fetchDependentSourceIdsStatement; SymbolEntries symbolEntries{{1, {"functionUSR", "function", SymbolKind::Function}}, {2, {"function2USR", "function2", SymbolKind::Function}}}; SourceLocationEntries sourceLocations{{1, 3, {42, 23}, SourceLocationKind::Declaration}, @@ -183,5 +192,145 @@ TEST_F(SymbolStorage, AddTablesInConstructor) Storage storage{mockDatabase}; } +TEST_F(SymbolStorage, FetchIndexingTimeStampsIsBusy) +{ + InSequence s; + + EXPECT_CALL(mockDatabase, deferredBegin()); + EXPECT_CALL(fetchIndexingTimeStampsStatement, valuesReturnSourceTimeStamps(1024)) + .WillOnce(Throw(Sqlite::StatementIsBusy{""})); + EXPECT_CALL(mockDatabase, rollback()); + EXPECT_CALL(mockDatabase, deferredBegin()); + EXPECT_CALL(fetchIndexingTimeStampsStatement, valuesReturnSourceTimeStamps(1024)); + EXPECT_CALL(mockDatabase, commit()); + + storage.fetchIndexingTimeStamps(); +} + +TEST_F(SymbolStorage, InsertIndexingTimeStamp) +{ + ClangBackEnd::FileStatuses fileStatuses{{1, 0, 34}, {2, 0, 37}}; + + EXPECT_CALL(inserOrUpdateIndexingTimesStampStatement, write(TypedEq<int>(1), TypedEq<int>(34))); + EXPECT_CALL(inserOrUpdateIndexingTimesStampStatement, write(TypedEq<int>(2), TypedEq<int>(37))); + + storage.insertOrUpdateIndexingTimeStamps(fileStatuses); +} + +TEST_F(SymbolStorage, InsertIndexingTimeStampsIsBusy) +{ + InSequence s; + + EXPECT_CALL(mockDatabase, immediateBegin()).WillOnce(Throw(Sqlite::StatementIsBusy{""})); + EXPECT_CALL(mockDatabase, immediateBegin()); + EXPECT_CALL(inserOrUpdateIndexingTimesStampStatement, write(TypedEq<int>(1), TypedEq<int>(34))); + EXPECT_CALL(inserOrUpdateIndexingTimesStampStatement, write(TypedEq<int>(2), TypedEq<int>(34))); + EXPECT_CALL(mockDatabase, commit()); + + storage.insertOrUpdateIndexingTimeStamps({1, 2}, 34); +} + +TEST_F(SymbolStorage, FetchIncludedIndexingTimeStampsIsBusy) +{ + InSequence s; + + EXPECT_CALL(mockDatabase, deferredBegin()); + EXPECT_CALL(fetchIncludedIndexingTimeStampsStatement, + valuesReturnSourceTimeStamps(1024, TypedEq<int>(1))) + .WillOnce(Throw(Sqlite::StatementIsBusy{""})); + EXPECT_CALL(mockDatabase, rollback()); + EXPECT_CALL(mockDatabase, deferredBegin()); + EXPECT_CALL(fetchIncludedIndexingTimeStampsStatement, + valuesReturnSourceTimeStamps(1024, TypedEq<int>(1))); + EXPECT_CALL(mockDatabase, commit()); + + storage.fetchIncludedIndexingTimeStamps(1); +} + +TEST_F(SymbolStorage, FetchDependentSourceIdsIsBusy) +{ + InSequence s; + + EXPECT_CALL(mockDatabase, deferredBegin()); + EXPECT_CALL(fetchDependentSourceIdsStatement, valuesReturnFilePathIds(1024, TypedEq<int>(3))); + EXPECT_CALL(fetchDependentSourceIdsStatement, valuesReturnFilePathIds(1024, TypedEq<int>(2))) + .WillOnce(Throw(Sqlite::StatementIsBusy{""})); + EXPECT_CALL(mockDatabase, rollback()); + EXPECT_CALL(mockDatabase, deferredBegin()); + EXPECT_CALL(fetchDependentSourceIdsStatement, valuesReturnFilePathIds(1024, TypedEq<int>(3))); + EXPECT_CALL(fetchDependentSourceIdsStatement, valuesReturnFilePathIds(1024, TypedEq<int>(2))); + EXPECT_CALL(fetchDependentSourceIdsStatement, valuesReturnFilePathIds(1024, TypedEq<int>(7))); + EXPECT_CALL(mockDatabase, commit()); + + storage.fetchDependentSourceIds({3, 2, 7}); +} + +class SymbolStorageSlow : public testing::Test +{ +protected: + Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; + ClangBackEnd::RefactoringDatabaseInitializer<Sqlite::Database> databaseInitializer{database}; + ClangBackEnd::SymbolStorage<> storage{database}; + ClangBackEnd::BuildDependenciesStorage<> buildDependenciesStorage{database}; +}; + +TEST_F(SymbolStorageSlow, InsertIndexingTimeStamps) +{ + storage.insertOrUpdateIndexingTimeStamps({1, 2}, 34); + + ASSERT_THAT(storage.fetchIndexingTimeStamps(), + ElementsAre(SourceTimeStamp{1, 34}, SourceTimeStamp{2, 34})); +} + +TEST_F(SymbolStorageSlow, UpdateIndexingTimeStamps) +{ + storage.insertOrUpdateIndexingTimeStamps({1, 2}, 34); + + storage.insertOrUpdateIndexingTimeStamps({1}, 37); + + ASSERT_THAT(storage.fetchIndexingTimeStamps(), + ElementsAre(SourceTimeStamp{1, 37}, SourceTimeStamp{2, 34})); } +TEST_F(SymbolStorageSlow, InsertIndexingTimeStamp) +{ + storage.insertOrUpdateIndexingTimeStamps({{1, 0, 34}, {2, 0, 37}}); + + ASSERT_THAT(storage.fetchIndexingTimeStamps(), + ElementsAre(SourceTimeStamp{1, 34}, SourceTimeStamp{2, 37})); +} + +TEST_F(SymbolStorageSlow, UpdateIndexingTimeStamp) +{ + storage.insertOrUpdateIndexingTimeStamps({{1, 0, 34}, {2, 0, 34}}); + + storage.insertOrUpdateIndexingTimeStamps({{2, 0, 37}}); + + ASSERT_THAT(storage.fetchIndexingTimeStamps(), + ElementsAre(SourceTimeStamp{1, 34}, SourceTimeStamp{2, 37})); +} + +TEST_F(SymbolStorageSlow, FetchIncludedIndexingTimeStamps) +{ + storage.insertOrUpdateIndexingTimeStamps({1, 2, 3, 4, 5}, 34); + buildDependenciesStorage.insertOrUpdateSourceDependencies({{1, 2}, {1, 3}, {2, 3}, {3, 4}, {5, 3}}); + + auto timeStamps = storage.fetchIncludedIndexingTimeStamps(1); + + ASSERT_THAT(timeStamps, + ElementsAre(SourceTimeStamp{1, 34}, + SourceTimeStamp{2, 34}, + SourceTimeStamp{3, 34}, + SourceTimeStamp{4, 34})); +} + +TEST_F(SymbolStorageSlow, FetchDependentSourceIds) +{ + buildDependenciesStorage.insertOrUpdateSourceDependencies( + {{1, 2}, {1, 3}, {2, 3}, {4, 2}, {5, 6}, {7, 6}}); + + auto sourceIds = storage.fetchDependentSourceIds({3, 2, 7}); + + ASSERT_THAT(sourceIds, ElementsAre(FilePathId{1}, FilePathId{4}, FilePathId{7})); +} +} // namespace diff --git a/tests/unit/unittest/translationunitupdater-test.cpp b/tests/unit/unittest/translationunitupdater-test.cpp index b491018956..eecc06b8a7 100644 --- a/tests/unit/unittest/translationunitupdater-test.cpp +++ b/tests/unit/unittest/translationunitupdater-test.cpp @@ -113,7 +113,7 @@ TEST_F(TranslationUnitUpdaterSlowTest, NotUpdatingParseTimePointForReparseOnly) ASSERT_FALSE(result.hasParsed()); } -TEST_F(TranslationUnitUpdaterSlowTest, UpdatesDependendOnFilesOnParse) +TEST_F(TranslationUnitUpdaterSlowTest, UpdatesDependentOnFilesOnParse) { ::TranslationUnitUpdater updater = createUpdater(createInput()); |