From 8862233fb8e6cd46453b928722205f03fe28026f Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 10 Feb 2025 18:10:04 +0100 Subject: [PATCH] QmlDesigner: Project property sheet path collection The property sheet paths are always collected if the designer directory, module directory, or the qmldir file is changed. We could minimize the updates, but then we must add an extra linking step in the project storage. That extra linking step would be needed because there could be property sheets for non-existing types. Task-number: QDS-14618 Change-Id: Idc26e41e3b4298fb118452045f3d907ad9f5e619 Reviewed-by: Thomas Hartmann --- .../designercore/projectstorage/filestatus.h | 7 +- .../projectstorage/filesystem.cpp | 4 +- .../designercore/projectstorage/filesystem.h | 2 +- .../projectstorage/filesysteminterface.h | 2 +- .../projectstorage/projectstorage.cpp | 5 +- .../projectstorage/projectstoragetypes.h | 2 +- .../projectstorage/projectstorageupdater.cpp | 252 ++++++++-- .../projectstorage/projectstorageupdater.h | 26 +- tests/unit/tests/mocks/filesystemmock.h | 5 +- .../tests/printers/gtest-creator-printing.cpp | 7 +- .../projectstorage/projectstorage-test.cpp | 9 +- .../projectstorageupdater-test.cpp | 430 +++++++++++++++--- .../tests/utils/google-using-declarations.h | 1 + 13 files changed, 608 insertions(+), 144 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/filestatus.h b/src/plugins/qmldesigner/libs/designercore/projectstorage/filestatus.h index 48b3ba2700c..289f8fd0394 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/filestatus.h +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/filestatus.h @@ -22,8 +22,7 @@ public: friend bool operator==(const FileStatus &first, const FileStatus &second) { return first.sourceId == second.sourceId && first.size == second.size - && first.lastModified == second.lastModified && first.size >= 0 - && first.lastModified >= 0; + && first.lastModified == second.lastModified; } friend bool operator!=(const FileStatus &first, const FileStatus &second) @@ -46,9 +45,9 @@ public: return first.sourceId < second; } - bool isValid() const { return sourceId && size >= 0 && lastModified >= 0; } + bool isExisting() const { return sourceId && size >= 0 && lastModified >= 0; } - explicit operator bool() const { return isValid(); } + explicit operator bool() const { return bool(sourceId); } template friend void convertToString(String &string, const FileStatus &fileStatus) diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/filesystem.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/filesystem.cpp index 856d8ddd617..a06c2d527ff 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/filesystem.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/filesystem.cpp @@ -31,9 +31,9 @@ SourceIds FileSystem::directoryEntries(const QString &directoryPath) const return sourceIds; } -QStringList FileSystem::qmlFileNames(const QString &directoryPath) const +QStringList FileSystem::fileNames(const QString &directoryPath, const QStringList &nameFilters) const { - return QDir{directoryPath}.entryList({"*.qml"}, QDir::Files); + return QDir{directoryPath}.entryList(nameFilters, QDir::Files); } long long FileSystem::lastModified(SourceId sourceId) const diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/filesystem.h b/src/plugins/qmldesigner/libs/designercore/projectstorage/filesystem.h index ae58b229d82..e7ae3dad53d 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/filesystem.h +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/filesystem.h @@ -25,7 +25,7 @@ public: {} SourceIds directoryEntries(const QString &directoryPath) const override; - QStringList qmlFileNames(const QString &directoryPath) const override; + QStringList fileNames(const QString &directoryPath, const QStringList &nameFilters) const override; long long lastModified(SourceId sourceId) const override; FileStatus fileStatus(SourceId sourceId) const override; QString contentAsQString(const QString &filePath) const override; diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/filesysteminterface.h b/src/plugins/qmldesigner/libs/designercore/projectstorage/filesysteminterface.h index ff7608c9a3f..9e96638546f 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/filesysteminterface.h +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/filesysteminterface.h @@ -15,7 +15,7 @@ class FileSystemInterface { public: virtual SourceIds directoryEntries(const QString &directoryPath) const = 0; - virtual QStringList qmlFileNames(const QString &directoryPath) const = 0; + virtual QStringList fileNames(const QString &directoryPath, const QStringList &nameFilters) const = 0; virtual long long lastModified(SourceId sourceId) const = 0; virtual FileStatus fileStatus(SourceId sourceId) const = 0; virtual void remove(const SourceIds &sourceIds) = 0; diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp index 9e12164c6f6..326f374aeea 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp @@ -1335,7 +1335,7 @@ void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackag package.updatedSourceIds); synchronizeTypeAnnotations(package.typeAnnotations, package.updatedTypeAnnotationSourceIds); synchronizePropertyEditorQmlPaths(package.propertyEditorQmlPaths, - package.updatedPropertyEditorQmlPathSourceContextIds); + package.updatedPropertyEditorQmlPathDirectoryIds); deleteNotUpdatedTypes(updatedTypeIds, package.updatedSourceIds, @@ -1413,6 +1413,9 @@ ModuleId ProjectStorage::moduleId(Utils::SmallStringView moduleName, Storage::Mo projectStorageCategory(), keyValue("module name", moduleName)}; + if (moduleName.empty()) + return ModuleId{}; + auto moduleId = moduleCache.id({moduleName, kind}); tracer.end(keyValue("module id", moduleId)); diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstoragetypes.h index 71279f57412..a8f3d1d6e95 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstoragetypes.h @@ -1331,7 +1331,7 @@ public: ModuleExportedImports moduleExportedImports; ModuleIds updatedModuleIds; PropertyEditorQmlPaths propertyEditorQmlPaths; - SourceContextIds updatedPropertyEditorQmlPathSourceContextIds; + SourceContextIds updatedPropertyEditorQmlPathDirectoryIds; TypeAnnotations typeAnnotations; SourceIds updatedTypeAnnotationSourceIds; }; diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp index d89ef04b246..e535f323176 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp @@ -37,12 +37,21 @@ void convertToString(String &string, const ProjectStorageUpdater::FileState &sta case ProjectStorageUpdater::FileState::Changed: convertToString(string, "Changed"); break; - case ProjectStorageUpdater::FileState::NotChanged: - convertToString(string, "NotChanged"); + case ProjectStorageUpdater::FileState::Unchanged: + convertToString(string, "Unchanged"); break; case ProjectStorageUpdater::FileState::NotExists: convertToString(string, "NotExists"); break; + case ProjectStorageUpdater::FileState::NotExistsUnchanged: + convertToString(string, "NotExistsUnchanged"); + break; + case ProjectStorageUpdater::FileState::Added: + convertToString(string, "Added"); + break; + case ProjectStorageUpdater::FileState::Removed: + convertToString(string, "Removed"); + break; } } @@ -92,7 +101,7 @@ ProjectStorageUpdater::Components createComponents( { ProjectStorageUpdater::Components components; - auto qmlFileNames = fileSystem.qmlFileNames(directory); + auto qmlFileNames = fileSystem.fileNames(directory, {"*.qml"}); components.reserve(static_cast(qmlDirParserComponents.size() + qmlFileNames.size())); @@ -289,35 +298,87 @@ void ProjectStorageUpdater::update(Update update) } namespace { + +bool isChanged(ProjectStorageUpdater::FileState state) +{ + using FileState = ProjectStorageUpdater::FileState; + switch (state) { + case FileState::Changed: + case FileState::Added: + case FileState::Removed: + return true; + case FileState::NotExists: + case FileState::NotExistsUnchanged: + case FileState::Unchanged: + break; + } + return false; +} + +bool isUnchanged(ProjectStorageUpdater::FileState state) +{ + return !isChanged(state); +} + template ProjectStorageUpdater::FileState combineState(FileStates... fileStates) { - if (((fileStates == ProjectStorageUpdater::FileState::Changed) || ...)) + if (((fileStates == ProjectStorageUpdater::FileState::Removed) && ...)) + return ProjectStorageUpdater::FileState::Removed; + + if (((fileStates == ProjectStorageUpdater::FileState::Added) && ...)) + return ProjectStorageUpdater::FileState::Added; + + if ((isChanged(fileStates) || ...)) return ProjectStorageUpdater::FileState::Changed; - if (((fileStates == ProjectStorageUpdater::FileState::NotChanged) || ...)) - return ProjectStorageUpdater::FileState::NotChanged; + if (((fileStates == ProjectStorageUpdater::FileState::Unchanged) || ...)) + return ProjectStorageUpdater::FileState::Unchanged; return ProjectStorageUpdater::FileState::NotExists; } +bool isExisting(ProjectStorageUpdater::FileState state) +{ + using FileState = ProjectStorageUpdater::FileState; + switch (state) { + case FileState::Changed: + case FileState::Added: + case FileState::Unchanged: + return true; + case FileState::NotExistsUnchanged: + case FileState::NotExists: + case FileState::Removed: + break; + } + return false; +} + +bool isNotExisting(ProjectStorageUpdater::FileState state) +{ + return !isExisting(state); +} + } // namespace void ProjectStorageUpdater::updateDirectoryChanged(Utils::SmallStringView directoryPath, + Utils::SmallStringView annotationDirectoryPath, FileState qmldirState, + FileState annotationDirectoryState, SourcePath qmldirSourcePath, SourceId qmldirSourceId, SourceContextId directoryId, + SourceContextId annotationDirectoryId, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds, Tracer &tracer) { QmlDirParser parser; - if (qmldirState != FileState::NotExists) + if (isExisting(qmldirState)) parser.parse(m_fileSystem.contentAsQString(QString{qmldirSourcePath})); - if (qmldirState != FileState::NotChanged) { + if (isChanged(qmldirState)) { tracer.tick("append updated source id", keyValue("module id", qmldirSourceId)); package.updatedSourceIds.push_back(qmldirSourceId); } @@ -374,6 +435,17 @@ void ProjectStorageUpdater::updateDirectoryChanged(Utils::SmallStringView direct qmldirSourceId); tracer.tick("append updated project source id", keyValue("module id", moduleId)); package.updatedDirectoryInfoDirectoryIds.push_back(directoryId); + + if (isExisting(annotationDirectoryState)) { + annotationDirectoryChanged(annotationDirectoryPath, + directoryId, + annotationDirectoryId, + moduleId, + package, + notUpdatedSourceIds); + } else if (annotationDirectoryState == FileState::Removed) { + package.updatedPropertyEditorQmlPathDirectoryIds.push_back(annotationDirectoryId); + } } void ProjectStorageUpdater::updateDirectories(const QStringList &directories, @@ -417,11 +489,12 @@ void ProjectStorageUpdater::updateSubdirectories(const Utils::PathString &direct auto exisitingSubdirectoryPaths = m_fileSystem.subdirectories(directoryPath.toQString()); Directories existingSubdirecories; - for (const QString &subdirectory : exisitingSubdirectoryPaths) { - if (subdirectory.endsWith("/designer") || subdirectory.endsWith("/QtQuick/Scene2D") - || subdirectory.endsWith("/QtQuick/Scene3D")) + + for (Utils::PathString subdirectoryPath : exisitingSubdirectoryPaths) { + if (subdirectoryPath.endsWith("designer") || subdirectoryPath.endsWith("/QtQuick/Scene2D") + || subdirectoryPath.endsWith("/QtQuick/Scene3D")) continue; - Utils::PathString subdirectoryPath = subdirectory; + SourceContextId sourceContextId = m_pathCache.sourceContextId(subdirectoryPath); subdirectories.emplace_back(subdirectoryPath, sourceContextId); existingSubdirecories.emplace_back(subdirectoryPath, sourceContextId); @@ -454,6 +527,64 @@ void ProjectStorageUpdater::updateSubdirectories(const Utils::PathString &direct } } +void ProjectStorageUpdater::annotationDirectoryChanged( + Utils::SmallStringView directoryPath, + SourceContextId directoryId, + SourceContextId annotationDirectoryId, + ModuleId moduleId, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds) +{ + NanotraceHR::Tracer tracer{"annotation directory changed", + category(), + keyValue("directory path", directoryPath), + keyValue("directory id", directoryId)}; + + package.updatedPropertyEditorQmlPathDirectoryIds.push_back(annotationDirectoryId); + + updatePropertyEditorFiles(directoryPath, annotationDirectoryId, moduleId, package, notUpdatedSourceIds); +} + +void ProjectStorageUpdater::updatePropertyEditorFiles( + Utils::SmallStringView directoryPath, + SourceContextId directoryId, + ModuleId moduleId, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds) +{ + NanotraceHR::Tracer tracer{"update property editor files", + category(), + keyValue("directory path", directoryPath), + keyValue("directory id", directoryId)}; + + const auto fileNames = m_fileSystem.fileNames(QString{directoryPath}, + {"*Pane.qml", + "*Specifics.qml", + "*SpecificsDynamic.qml"}); + for (const QString &fileName : fileNames) + updatePropertyEditorFile(fileName, directoryId, moduleId, package, notUpdatedSourceIds); +} + +void ProjectStorageUpdater::updatePropertyEditorFile( + const QString &fileName, + SourceContextId directoryId, + ModuleId moduleId, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds) +{ + Utils::PathString propertyEditorFileName{fileName}; + auto propertyEditorSourceId = m_pathCache.sourceId(directoryId, propertyEditorFileName); + + fileState(propertyEditorSourceId, package, notUpdatedSourceIds); + + QRegularExpression regex{R"xo((\w+)(Specifics|Pane|SpecificsDynamic).qml)xo"}; + auto match = regex.match(fileName); + + Storage::TypeNameString typeName{match.capturedView(1)}; + + package.propertyEditorQmlPaths.emplace_back(moduleId, typeName, propertyEditorSourceId, directoryId); +} + void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPath, const SourceContextIds &subdirectoriesToIgnore, Storage::Synchronization::SynchronizationPackage &package, @@ -462,32 +593,39 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa { NanotraceHR::Tracer tracer{"update directory", category(), keyValue("directory", directoryPath)}; - SourcePath qmldirSourcePath{directoryPath + "/qmldir"}; - auto [directoryId, qmldirSourceId] = m_pathCache.sourceContextAndSourceId(qmldirSourcePath); + SourcePath qmldirPath{directoryPath + "/qmldir"}; + auto [directoryId, qmldirSourceId] = m_pathCache.sourceContextAndSourceId(qmldirPath); auto directoryState = fileState(directoryId, package, notUpdatedSourceIds); - if (directoryState != FileState::NotExists) + if (isExisting(directoryState)) watchedSourceIdsIds.directoryIds.push_back(SourceId::create(SourceNameId{}, directoryId)); auto qmldirState = fileState(qmldirSourceId, package, notUpdatedSourceIds); - if (qmldirState != FileState::NotExists) + if (isExisting(qmldirState)) watchedSourceIdsIds.qmldirSourceIds.push_back(qmldirSourceId); - switch (combineState(directoryState, qmldirState)) { - case FileState::Changed: { + SourcePath annotationDirectoryPath{directoryPath + "/designer"}; + auto annotationDirectoryId = m_pathCache.sourceContextId(annotationDirectoryPath); + auto annotationDirectoryState = fileState(annotationDirectoryId, package, notUpdatedSourceIds); + + switch (combineState(directoryState, qmldirState, annotationDirectoryState)) { + case FileState::Changed: + case FileState::Added: tracer.tick("update directory changed"); updateDirectoryChanged(directoryPath, + annotationDirectoryPath, qmldirState, - qmldirSourcePath, + annotationDirectoryState, + qmldirPath, qmldirSourceId, directoryId, + annotationDirectoryId, package, notUpdatedSourceIds, watchedSourceIdsIds, tracer); break; - } - case FileState::NotChanged: { + case FileState::Unchanged: tracer.tick("update directory not changed"); parseDirectoryInfos(m_projectStorage.fetchDirectoryInfos(directoryId), @@ -495,13 +633,9 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa notUpdatedSourceIds, watchedSourceIdsIds); break; - } - case FileState::NotExists: { + case FileState::Removed: { tracer.tick("update directory don't exits"); - auto directorySourceId = SourceId::create(SourceNameId{}, directoryId); - package.updatedFileStatusSourceIds.push_back(directorySourceId); - package.updatedFileStatusSourceIds.push_back(qmldirSourceId); package.updatedDirectoryInfoDirectoryIds.push_back(directoryId); package.updatedSourceIds.push_back(qmldirSourceId); auto qmlDirectoryInfos = m_projectStorage.fetchDirectoryInfos(directoryId); @@ -512,9 +646,11 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa keyValue("source id", directoryInfo.sourceId)); package.updatedFileStatusSourceIds.push_back(directoryInfo.sourceId); } - break; } + case FileState::NotExists: + case FileState::NotExistsUnchanged: + break; } updateSubdirectories(directoryPath, @@ -525,7 +661,7 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa notUpdatedSourceIds, watchedSourceIdsIds); - tracer.end(keyValue("qmldir source path", qmldirSourcePath), + tracer.end(keyValue("qmldir source path", qmldirPath), keyValue("directory id", directoryId), keyValue("qmldir source id", qmldirSourceId), keyValue("qmldir state", qmldirState), @@ -630,7 +766,7 @@ void ProjectStorageUpdater::updateTypeAnnotations( if (state == FileState::Changed) updateTypeAnnotation(directoryPath, fileInfo.filePath(), sourceId, directoryId, package); - if (state != FileState::NotChanged) + if (state != FileState::Unchanged) updatedSourceIdsDictonary[directoryId].push_back(sourceId); } } @@ -643,7 +779,7 @@ void ProjectStorageUpdater::updateTypeAnnotationDirectories( for (auto &[directoryId, updatedSourceIds] : updatedSourceIdsDictonary) { auto directoryState = fileState(directoryId, package, notUpdatedSourceIds); - if (directoryState != FileState::NotChanged) { + if (directoryState != FileState::Unchanged) { auto existingTypeAnnotationSourceIds = m_projectStorage.typeAnnotationSourceIds( directoryId); @@ -708,7 +844,7 @@ void ProjectStorageUpdater::updatePropertyEditorPath( tracer.tick("append updated property editor qml path source id", keyValue("source id", directoryId)); - package.updatedPropertyEditorQmlPathSourceContextIds.push_back(directoryId); + package.updatedPropertyEditorQmlPathDirectoryIds.push_back(directoryId); auto dir = QDir{directoryPath}; const auto fileInfos = dir.entryInfoList({"*Pane.qml", "*Specifics.qml"}, QDir::Files); for (const auto &fileInfo : fileInfos) @@ -950,6 +1086,7 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Direct auto state = fileState(directoryInfo.sourceId, package, notUpdatedSourceIds); switch (state) { + case FileState::Added: case FileState::Changed: { tracer.tick("append updated source ids", keyValue("source id", directoryInfo.sourceId)); package.updatedSourceIds.push_back(directoryInfo.sourceId); @@ -958,12 +1095,14 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Direct m_qmlTypesParser.parse(content, package.imports, package.types, directoryInfo); break; } - case FileState::NotChanged: { + case FileState::Unchanged: { tracer.tick("append not updated source ids", keyValue("source id", directoryInfo.sourceId)); notUpdatedSourceIds.sourceIds.push_back(directoryInfo.sourceId); break; } case FileState::NotExists: + case FileState::NotExistsUnchanged: + case FileState::Removed: throw CannotParseQmlTypesFile{}; } @@ -1003,8 +1142,8 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil watchedSourceIds.qmlSourceIds.push_back(sourceId); switch (state) { - case FileState::NotChanged: - if (qmldirState == FileState::NotExists) { + case FileState::Unchanged: + if (isNotExisting(qmldirState)) { tracer.tick("append not updated source id", keyValue("source id", sourceId)); notUpdatedSourceIds.sourceIds.emplace_back(sourceId); @@ -1017,6 +1156,7 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil type.changeLevel = Storage::Synchronization::ChangeLevel::Minimal; break; case FileState::NotExists: + case FileState::Removed: tracer.tick("file does not exits", keyValue("source id", sourceId)); for (const auto &exportedType : exportedTypes) m_errorNotifier.qmlDocumentDoesNotExistsForQmldirEntry(exportedType.name, @@ -1024,6 +1164,9 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil sourceId, qmldirSourceId); break; + case FileState::NotExistsUnchanged: + break; + case FileState::Added: case FileState::Changed: tracer.tick("update from qml document", keyValue("source id", sourceId)); const auto content = m_fileSystem.contentAsQString(QString{qmlFilePath}); @@ -1033,7 +1176,7 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil const auto &directoryInfo = package.directoryInfos.emplace_back( directoryId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument); - tracer.tick("append project data", keyValue("project data", directoryInfo)); + tracer.tick("append directory info", keyValue("project data", directoryInfo)); tracer.tick("append updated source id", keyValue("source id", sourceId)); package.updatedSourceIds.push_back(sourceId); @@ -1054,13 +1197,13 @@ void ProjectStorageUpdater::parseQmlComponent(SourceId sourceId, NanotraceHR::Tracer tracer{"parse qml component", category(), keyValue("source id", sourceId)}; auto state = fileState(sourceId, package, notUpdatedSourceIds); - if (state == FileState::NotChanged) + if (isUnchanged(state)) return; tracer.tick("append updated source id", keyValue("source id", sourceId)); package.updatedSourceIds.push_back(sourceId); - if (state == FileState::NotExists) + if (isNotExisting(state)) return; SourcePath sourcePath = m_pathCache.sourcePath(sourceId); @@ -1186,33 +1329,42 @@ ProjectStorageUpdater::FileState ProjectStorageUpdater::fileState( keyValue("source id", sourceId)}; auto currentFileStatus = m_fileStatusCache.find(sourceId); + auto projectStorageFileStatus = m_projectStorage.fetchFileStatus(sourceId); - if (!currentFileStatus.isValid()) { - tracer.tick("append updated file status source id", keyValue("source id", sourceId)); + if (!currentFileStatus.isExisting()) { + if (projectStorageFileStatus.isExisting()) { + package.updatedFileStatusSourceIds.push_back(sourceId); + return FileState::Removed; + } + + if (projectStorageFileStatus) { + notUpdatedSourceIds.fileStatusSourceIds.push_back(sourceId); + return FileState::NotExistsUnchanged; + } + + package.fileStatuses.push_back(currentFileStatus); package.updatedFileStatusSourceIds.push_back(sourceId); - tracer.end(keyValue("state", FileState::NotExists)); return FileState::NotExists; } - auto projectStorageFileStatus = m_projectStorage.fetchFileStatus(sourceId); - - if (!projectStorageFileStatus.isValid() || projectStorageFileStatus != currentFileStatus) { - tracer.tick("append file status", keyValue("file status", sourceId)); + if (!projectStorageFileStatus) { + package.fileStatuses.push_back(currentFileStatus); + package.updatedFileStatusSourceIds.push_back(sourceId); + + return FileState::Added; + } + + if (projectStorageFileStatus != currentFileStatus) { package.fileStatuses.push_back(currentFileStatus); - - tracer.tick("append updated file status source id", keyValue("source id", sourceId)); package.updatedFileStatusSourceIds.push_back(sourceId); - tracer.end(keyValue("state", FileState::Changed)); return FileState::Changed; } - tracer.tick("append not updated file status source id", keyValue("source id", sourceId)); notUpdatedSourceIds.fileStatusSourceIds.push_back(sourceId); - tracer.end(keyValue("state", FileState::NotChanged)); - return FileState::NotChanged; + return FileState::Unchanged; } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.h index de042968930..f6cf60c736c 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.h @@ -104,11 +104,7 @@ public: const_iterator m_end; }; - enum class FileState { - NotChanged, - Changed, - NotExists, - }; + enum class FileState { Unchanged, Changed, NotExists, NotExistsUnchanged, Added, Removed }; struct WatchedSourceIdsIds { @@ -157,15 +153,33 @@ private: NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds); void updateDirectoryChanged(Utils::SmallStringView directoryPath, + Utils::SmallStringView annotationDirectoryPath, FileState qmldirState, + FileState annotationDirectoryState, SourcePath qmldirSourcePath, SourceId qmldirSourceId, SourceContextId directoryId, + SourceContextId annotationDirectoryId, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds, ProjectStorageTracing::Category::TracerType &tracer); - + void annotationDirectoryChanged(Utils::SmallStringView directoryPath, + SourceContextId directoryId, + SourceContextId annotationDirectoryId, + ModuleId moduleId, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds); + void updatePropertyEditorFiles(Utils::SmallStringView directyPath, + SourceContextId directoryId, + ModuleId moduleId, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds); + void updatePropertyEditorFile(const QString &fileName, + SourceContextId directoryId, + ModuleId moduleId, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds); void updatePropertyEditorPaths(const QString &propertyEditorResourcesPath, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds); diff --git a/tests/unit/tests/mocks/filesystemmock.h b/tests/unit/tests/mocks/filesystemmock.h index f8544e509f6..8346dca9439 100644 --- a/tests/unit/tests/mocks/filesystemmock.h +++ b/tests/unit/tests/mocks/filesystemmock.h @@ -15,7 +15,10 @@ public: directoryEntries, (const QString &directoryPath), (const, override)); - MOCK_METHOD(QStringList, qmlFileNames, (const QString &directoryPath), (const, override)); + MOCK_METHOD(QStringList, + fileNames, + (const QString &directoryPath, const QStringList &nameFilters), + (const, override)); MOCK_METHOD(long long, lastModified, (QmlDesigner::SourceId sourceId), (const, override)); MOCK_METHOD(QmlDesigner::FileStatus, fileStatus, (QmlDesigner::SourceId sourceId), (const, override)); MOCK_METHOD(void, remove, (const QmlDesigner::SourceIds &sourceIds), (override)); diff --git a/tests/unit/tests/printers/gtest-creator-printing.cpp b/tests/unit/tests/printers/gtest-creator-printing.cpp index defef8918c2..73909bbf66e 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.cpp +++ b/tests/unit/tests/printers/gtest-creator-printing.cpp @@ -853,11 +853,11 @@ std::ostream &operator<<(std::ostream &out, const SynchronizationPackage &packag << ", updatedSourceIds: " << package.updatedSourceIds << ", fileStatuses: " << package.fileStatuses << ", updatedFileStatusSourceIds: " << package.updatedFileStatusSourceIds - << ", updatedDirectoryInfoDirectoryIds: " << package.updatedDirectoryInfoDirectoryIds << ", directoryInfos: " << package.directoryInfos + << ", updatedDirectoryInfoDirectoryIds: " << package.updatedDirectoryInfoDirectoryIds << ", propertyEditorQmlPaths: " << package.propertyEditorQmlPaths << ", updatedPropertyEditorQmlPathSourceIds: " - << package.updatedPropertyEditorQmlPathSourceContextIds + << package.updatedPropertyEditorQmlPathDirectoryIds << ", typeAnnotations: " << package.typeAnnotations << ", updatedTypeAnnotationSourceIds: " << package.updatedTypeAnnotationSourceIds << ")"; @@ -963,7 +963,8 @@ std::ostream &operator<<(std::ostream &out, const ModuleExportedImport &import) std::ostream &operator<<(std::ostream &out, const PropertyEditorQmlPath &path) { - return out << "(" << path.moduleId << ", " << path.typeName << ", " << path.pathId << ")"; + return out << "(" << path.moduleId << ", " << path.typeName << ", " << path.pathId << ", " + << path.directoryId << ", " << path.typeId << ")"; } std::ostream &operator<<(std::ostream &out, const TypeAnnotation &annotation) diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index 94b7ac5b9a5..96aa0a6ede8 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -1079,7 +1079,7 @@ protected: "Item3D", sourceId3, sourceContextIdPath6); - package.updatedPropertyEditorQmlPathSourceContextIds.emplace_back(sourceContextIdPath6); + package.updatedPropertyEditorQmlPathDirectoryIds.emplace_back(sourceContextIdPath6); return package; } @@ -5774,6 +5774,13 @@ TEST_F(ProjectStorage, get_module_id) ASSERT_TRUE(id); } +TEST_F(ProjectStorage, get_invalid_module_id_for_empty_name) +{ + auto id = storage.moduleId("", ModuleKind::QmlLibrary); + + ASSERT_FALSE(id); +} + TEST_F(ProjectStorage, get_same_module_id_again) { auto initialId = storage.moduleId("Qml", ModuleKind::QmlLibrary); diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp index 0136fa7c343..2d7df558c9b 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -133,7 +133,7 @@ MATCHER(PackageIsEmpty, std::string(negation ? "isn't empty" : "is empty")) && package.updatedModuleDependencySourceIds.empty() && package.moduleExportedImports.empty() && package.updatedModuleIds.empty() && package.propertyEditorQmlPaths.empty() - && package.updatedPropertyEditorQmlPathSourceContextIds.empty() + && package.updatedPropertyEditorQmlPathDirectoryIds.empty() && package.typeAnnotations.empty() && package.updatedTypeAnnotationSourceIds.empty(); } @@ -188,6 +188,11 @@ public: qmltypes1SourceId, qmltypes2SourceId, itemLibraryPathSourceId}); + setFilesDontExistsUnchanged({createDirectorySourceId("/path/designer"), + createDirectorySourceId("/root/designer"), + createDirectorySourceId("/path/one/designer"), + createDirectorySourceId("/path/two/designer"), + createDirectorySourceId("/path/three/designer")}); setFilesAdded({qmldir1SourceId, qmldir2SourceId, qmldir3SourceId}); @@ -279,7 +284,8 @@ public: void setFilesRemoved(const QmlDesigner::SourceIds &sourceIds) { for (auto sourceId : sourceIds) { - ON_CALL(fileSystemMock, fileStatus(Eq(sourceId))).WillByDefault(Return(FileStatus{})); + ON_CALL(fileSystemMock, fileStatus(Eq(sourceId))) + .WillByDefault(Return(FileStatus{sourceId, -1, -1})); ON_CALL(projectStorageMock, fetchFileStatus(Eq(sourceId))) .WillByDefault(Return(FileStatus{sourceId, 1, 21})); } @@ -288,15 +294,34 @@ public: void setFilesDontExists(const QmlDesigner::SourceIds &sourceIds) { for (auto sourceId : sourceIds) { - ON_CALL(fileSystemMock, fileStatus(Eq(sourceId))).WillByDefault(Return(FileStatus{})); + ON_CALL(fileSystemMock, fileStatus(Eq(sourceId))) + .WillByDefault(Return(FileStatus{sourceId, -1, -1})); ON_CALL(projectStorageMock, fetchFileStatus(Eq(sourceId))) - .WillByDefault(Return(FileStatus{})); + .WillByDefault(Return(FileStatus{SourceId{}, -1, -1})); } } + void setFilesDontExistsUnchanged(const QmlDesigner::SourceIds &sourceIds) + { + for (auto sourceId : sourceIds) { + ON_CALL(fileSystemMock, fileStatus(Eq(sourceId))) + .WillByDefault(Return(FileStatus{sourceId, -1, -1})); + ON_CALL(projectStorageMock, fetchFileStatus(Eq(sourceId))) + .WillByDefault(Return(FileStatus{sourceId, -1, -1})); + } + } + + void setFileNames(QStringView directoryPath, + const QStringList &fileNames, + const QStringList &nameFilters) + { + ON_CALL(fileSystemMock, fileNames(Eq(directoryPath), UnorderedElementsAreArray(nameFilters))) + .WillByDefault(Return(fileNames)); + } + void setQmlFileNames(QStringView directoryPath, const QStringList &qmlFileNames) { - ON_CALL(fileSystemMock, qmlFileNames(Eq(directoryPath))).WillByDefault(Return(qmlFileNames)); + setFileNames(directoryPath, qmlFileNames, {"*.qml"}); } void setDirectoryInfos(SourceContextId directorySourceId, const DirectoryInfos &directoryInfos) @@ -376,6 +401,7 @@ protected: SourceId qmlDirPathSourceId = sourcePathCache.sourceId("/path/qmldir"); SourceContextId directoryPathId = qmlDirPathSourceId.contextId(); SourceId directoryPathSourceId = SourceId::create(QmlDesigner::SourceNameId{}, directoryPathId); + SourceId annotationDirectorySourceId = createDirectorySourceId("/path/designer"); SourceId qmlDocumentSourceId1 = sourcePathCache.sourceId("/path/First.qml"); SourceId qmlDocumentSourceId2 = sourcePathCache.sourceId("/path/First2.qml"); SourceId qmlDocumentSourceId3 = sourcePathCache.sourceId("/path/Second.qml"); @@ -2277,10 +2303,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir) UnorderedElementsAre(IsExportedType(pathModuleId, "Second", -1, -1)))))), Field("SynchronizationPackage::updatedSourceIds", &SynchronizationPackage::updatedSourceIds, - UnorderedElementsAre(qmlDirPathSourceId, - qmlDocumentSourceId1, - qmlDocumentSourceId2, - qmlDocumentSourceId3)), + UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2, qmlDocumentSourceId3)), Field("SynchronizationPackage::updatedFileStatusSourceIds", &SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(directoryPathSourceId, @@ -2291,6 +2314,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir) Field("SynchronizationPackage::fileStatuses", &SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21), + IsFileStatus(qmlDirPathSourceId, -1, -1), IsFileStatus(qmlDocumentSourceId1, 1, 21), IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(qmlDocumentSourceId3, 1, 21))), @@ -2331,7 +2355,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_parsed_type_if_qml_document_does_not_exists) { setFilesDontExists({qmlDocumentSourceId1}); - setFilesAdded({directoryPathSourceId}); + setFilesAdded({directoryPathSourceId, qmlDirPathSourceId, qmlDocumentSourceId2}); QString qmldir{R"(module Example FirstType 1.0 First.qml FirstType 2.2 First2.qml)"}; @@ -2379,6 +2403,7 @@ TEST_F(ProjectStorageUpdater, &SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21), IsFileStatus(qmlDirPathSourceId, 1, 21), + IsFileStatus(qmlDocumentSourceId1, -1, -1), IsFileStatus(qmlDocumentSourceId2, 1, 21))), Field("SynchronizationPackage::updatedDirectoryInfoDirectoryIds", &SynchronizationPackage::updatedDirectoryInfoDirectoryIds, @@ -2397,9 +2422,14 @@ TEST_F(ProjectStorageUpdater, updater.update({.directories = directories}); } -TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if_directory_does_not_exists) +TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_if_directory_is_removed) { - setFilesDontExists({qmlDirPathSourceId, directoryPathSourceId}); + setFilesRemoved({qmlDirPathSourceId, + directoryPathSourceId, + annotationDirectorySourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3}); setDirectoryInfos(directoryPathId, {{directoryPathId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, {directoryPathId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, @@ -2419,6 +2449,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if Field("SynchronizationPackage::updatedFileStatusSourceIds", &SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(directoryPathSourceId, + annotationDirectorySourceId, qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2, @@ -2438,7 +2469,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_add_qml_document) { - setFilesDontExists({qmlDirPathSourceId}); + setFilesDontExistsUnchanged({qmlDirPathSourceId}); setFilesChanged({directoryPathSourceId}); setFilesAdded({qmlDocumentSourceId3}); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); @@ -2446,43 +2477,50 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_add_qml_d {{directoryPathId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, {directoryPathId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); - EXPECT_CALL( - projectStorageMock, - synchronize(AllOf( - Field("SynchronizationPackage::imports", - &SynchronizationPackage::imports, - UnorderedElementsAre(import3)), - Field("SynchronizationPackage::types", - &SynchronizationPackage::types, - UnorderedElementsAre(AllOf( - IsStorageType("Second.qml", - ImportedType{"Object3"}, - TypeTraitsKind::Reference, - qmlDocumentSourceId3, - ChangeLevel::Full), - Field("Type::exportedTypes", - &Type::exportedTypes, - UnorderedElementsAre(IsExportedType(pathModuleId, "Second", -1, -1)))))), - Field("SynchronizationPackage::updatedSourceIds", - &SynchronizationPackage::updatedSourceIds, - UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId3)), - Field("SynchronizationPackage::updatedFileStatusSourceIds", - &SynchronizationPackage::updatedFileStatusSourceIds, - UnorderedElementsAre(directoryPathSourceId, qmlDirPathSourceId, qmlDocumentSourceId3)), - Field("SynchronizationPackage::fileStatuses", - &SynchronizationPackage::fileStatuses, - UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21), - IsFileStatus(qmlDocumentSourceId3, 1, 21))), - Field("SynchronizationPackage::updatedDirectoryInfoDirectoryIds", - &SynchronizationPackage::updatedDirectoryInfoDirectoryIds, - UnorderedElementsAre(directoryPathId)), - Field("SynchronizationPackage::directoryInfos", - &SynchronizationPackage::directoryInfos, - UnorderedElementsAre( - IsDirectoryInfo(directoryPathId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument), - IsDirectoryInfo(directoryPathId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument), - IsDirectoryInfo( - directoryPathId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument)))))); + EXPECT_CALL(projectStorageMock, + synchronize( + AllOf(Field("SynchronizationPackage::imports", + &SynchronizationPackage::imports, + UnorderedElementsAre(import3)), + Field("SynchronizationPackage::types", + &SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("Second.qml", + ImportedType{"Object3"}, + TypeTraitsKind::Reference, + qmlDocumentSourceId3, + ChangeLevel::Full), + Field("Type::exportedTypes", + &Type::exportedTypes, + UnorderedElementsAre( + IsExportedType(pathModuleId, "Second", -1, -1)))))), + Field("SynchronizationPackage::updatedSourceIds", + &SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDocumentSourceId3)), + Field("SynchronizationPackage::updatedFileStatusSourceIds", + &SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(directoryPathSourceId, qmlDocumentSourceId3)), + Field("SynchronizationPackage::fileStatuses", + &SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21), + IsFileStatus(qmlDocumentSourceId3, 1, 21))), + Field("SynchronizationPackage::updatedDirectoryInfoDirectoryIds", + &SynchronizationPackage::updatedDirectoryInfoDirectoryIds, + UnorderedElementsAre(directoryPathId)), + Field("SynchronizationPackage::directoryInfos", + &SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.update({.directories = directories}); } @@ -2506,13 +2544,14 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_removes_q Field("SynchronizationPackage::types", &SynchronizationPackage::types, IsEmpty()), Field("SynchronizationPackage::updatedSourceIds", &SynchronizationPackage::updatedSourceIds, - UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId3)), + UnorderedElementsAre(qmlDocumentSourceId3)), Field("SynchronizationPackage::updatedFileStatusSourceIds", &SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(directoryPathSourceId, qmlDirPathSourceId, qmlDocumentSourceId3)), Field("SynchronizationPackage::fileStatuses", &SynchronizationPackage::fileStatuses, - UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21))), + UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21), + IsFileStatus(qmlDirPathSourceId, -1, -1))), Field("SynchronizationPackage::updatedDirectoryInfoDirectoryIds", &SynchronizationPackage::updatedDirectoryInfoDirectoryIds, UnorderedElementsAre(directoryPathId)), @@ -2672,7 +2711,9 @@ TEST_F(ProjectStorageUpdater, watcher_updates_subdirectories) TEST_F(ProjectStorageUpdater, watcher_updates_removed_directory) { + auto annotationDirectorySourceId = createDirectorySourceId("/path/designer"); setFilesRemoved({directoryPathSourceId, + annotationDirectorySourceId, qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2, @@ -2696,13 +2737,14 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_directory) Field("SynchronizationPackage::updatedFileStatusSourceIds", &SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(directoryPathSourceId, + annotationDirectorySourceId, qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2, qmlDocumentSourceId3)), Field("SynchronizationPackage::fileStatuses", &SynchronizationPackage::fileStatuses, - UnorderedElementsAre()), + IsEmpty()), Field("SynchronizationPackage::updatedDirectoryInfoDirectoryIds", &SynchronizationPackage::updatedDirectoryInfoDirectoryIds, UnorderedElementsAre(directoryPathId)), @@ -3439,7 +3481,9 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qml_documents) TEST_F(ProjectStorageUpdater, watcher_updates_removed_qml_documents) { + setFilesDontExistsUnchanged({annotationDirectorySourceId, qmlDirPathSourceId}); setFilesRemoved({qmlDocumentSourceId2}); + setFilesChanged({qmlDocumentSourceId1}); EXPECT_CALL(projectStorageMock, synchronize( @@ -3450,11 +3494,13 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_qml_documents) &SynchronizationPackage::types, UnorderedElementsAre(AllOf( IsStorageType("First.qml", - ImportedType{"Object"}, + Storage::Synchronization::ImportedType{"Object"}, TypeTraitsKind::Reference, qmlDocumentSourceId1, - ChangeLevel::ExcludeExportedTypes), - Field("Type::exportedTypes", &Type::exportedTypes, IsEmpty())))), + Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), + Field("exportedTypes", + &Storage::Synchronization::Type::exportedTypes, + IsEmpty())))), Field("SynchronizationPackage::updatedSourceIds", &SynchronizationPackage::updatedSourceIds, UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)), @@ -4162,21 +4208,21 @@ TEST_F(ProjectStorageUpdater, update_property_editor_panes) setFilesChanged({directorySourceId}); auto qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary); - EXPECT_CALL( - projectStorageMock, - synchronize(AllOf( - Field("SynchronizationPackage::fileStatuses", - &SynchronizationPackage::fileStatuses, - UnorderedElementsAre(IsFileStatus(directorySourceId, 1, 21))), - Field("SynchronizationPackage::updatedFileStatusSourceIds", - &SynchronizationPackage::updatedFileStatusSourceIds, - UnorderedElementsAre(directorySourceId)), - Field("SynchronizationPackage::propertyEditorQmlPaths", - &SynchronizationPackage::propertyEditorQmlPaths, - Contains(IsPropertyEditorQmlPath(qmlModuleId, "QtObject", sourceId, directoryId))), - Field("SynchronizationPackage::updatedPropertyEditorQmlPathSourceContextIds", - &SynchronizationPackage::updatedPropertyEditorQmlPathSourceContextIds, - ElementsAre(directoryId))))); + EXPECT_CALL(projectStorageMock, + synchronize( + AllOf(Field("SynchronizationPackage::fileStatuses", + &SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(directorySourceId, 1, 21))), + Field("SynchronizationPackage::updatedFileStatusSourceIds", + &SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(directorySourceId)), + Field("SynchronizationPackage::propertyEditorQmlPaths", + &SynchronizationPackage::propertyEditorQmlPaths, + Contains(IsPropertyEditorQmlPath( + qmlModuleId, "QtObject", sourceId, directoryId))), + Field("SynchronizationPackage::updatedPropertyEditorQmlPathDirectoryIds", + &SynchronizationPackage::updatedPropertyEditorQmlPathDirectoryIds, + ElementsAre(directoryId))))); updater.update({.propertyEditorResourcesPath = propertyEditorQmlPath}); } @@ -4213,8 +4259,8 @@ TEST_F(ProjectStorageUpdater, update_property_editor_specifics) {IsPropertyEditorQmlPath(qtQuickModuleId, "Text", textSourceId, qtQuickDirectoryId), IsPropertyEditorQmlPath( controlsModuleId, "Button", buttonSourceId, controlsDirectoryId)})), - Field("SynchronizationPackage::updatedPropertyEditorQmlPathSourceContextIds", - &SynchronizationPackage::updatedPropertyEditorQmlPathSourceContextIds, + Field("SynchronizationPackage::updatedPropertyEditorQmlPathDirectoryIds", + &SynchronizationPackage::updatedPropertyEditorQmlPathDirectoryIds, ElementsAre(qtQuickDirectoryId, controlsDirectoryId))))); updater.update({.propertyEditorResourcesPath = propertyEditorQmlPath}); @@ -4373,4 +4419,242 @@ TEST_F(ProjectStorageUpdater, update_type_annotations_removed_directory) updater.update({.typeAnnotationPaths = {itemLibraryPath}}); } +TEST_F(ProjectStorageUpdater, synchronize_property_editor_qml_paths_directory) +{ + QStringList directories = {"/path/one"}; + setSubdirectoryPaths(u"/path/one", {"/path/one/designer"}); + SourceContextId designer1DirectoryId = sourcePathCache.sourceContextId("/path/one/designer"); + SourceId designer1SourceId = SourceId::create(QmlDesigner::SourceNameId{}, designer1DirectoryId); + setFilesChanged({path1SourceId, designer1SourceId}); + + EXPECT_CALL(projectStorageMock, + synchronize( + AllOf(Field("SynchronizationPackage::fileStatuses", + &SynchronizationPackage::fileStatuses, + IsSupersetOf({IsFileStatus(designer1SourceId, 1, 21)})), + Field("SynchronizationPackage::updatedFileStatusSourceIds", + &SynchronizationPackage::updatedFileStatusSourceIds, + IsSupersetOf({designer1SourceId})), + Field("SynchronizationPackage::propertyEditorQmlPaths", + &SynchronizationPackage::propertyEditorQmlPaths, + Each(Field("PropertyEditorQmlPath::directoryId", + &PropertyEditorQmlPath::directoryId, + designer1DirectoryId))), + Field("SynchronizationPackage::updatedPropertyEditorQmlPathDirectoryIds", + &SynchronizationPackage::updatedPropertyEditorQmlPathDirectoryIds, + UnorderedElementsAre(designer1DirectoryId)), + Field("SynchronizationPackage::directoryInfos", + &SynchronizationPackage::directoryInfos, + IsEmpty()), + Field("SynchronizationPackage::updatedDirectoryInfoDirectoryIds", + &SynchronizationPackage::updatedDirectoryInfoDirectoryIds, + UnorderedElementsAre(path1SourceContextId))))); + + updater.update({.directories = directories}); +} + +TEST_F(ProjectStorageUpdater, dont_synchronize_empty_property_editor_qml_paths_directory) +{ + QStringList directories = {"/path/one", "/path/two"}; + setSubdirectoryPaths(u"/path/one", {"/path/one/designer"}); + setSubdirectoryPaths(u"/path/two", {}); + SourceContextId designer1DirectoryId = sourcePathCache.sourceContextId("/path/one/designer"); + SourceId designer1SourceId = SourceId::create(QmlDesigner::SourceNameId{}, designer1DirectoryId); + SourceContextId designer2DirectoryId = sourcePathCache.sourceContextId("/path/two/designer"); + SourceId designer2SourceId = SourceId::create(QmlDesigner::SourceNameId{}, designer2DirectoryId); + setFilesChanged({path1SourceId, path2SourceId, designer1SourceId}); + setFilesDontExists({designer2SourceId}); + + EXPECT_CALL(projectStorageMock, + synchronize( + AllOf(Field("SynchronizationPackage::fileStatuses", + &SynchronizationPackage::fileStatuses, + IsSupersetOf({IsFileStatus(designer1SourceId, 1, 21)})), + Field("SynchronizationPackage::updatedFileStatusSourceIds", + &SynchronizationPackage::updatedFileStatusSourceIds, + IsSupersetOf({designer1SourceId, designer2SourceId})), + Field("SynchronizationPackage::propertyEditorQmlPaths", + &SynchronizationPackage::propertyEditorQmlPaths, + Each(Field("PropertyEditorQmlPath::directoryId", + &PropertyEditorQmlPath::directoryId, + designer1DirectoryId))), + Field("SynchronizationPackage::updatedPropertyEditorQmlPathDirectoryIds", + &SynchronizationPackage::updatedPropertyEditorQmlPathDirectoryIds, + UnorderedElementsAre(designer1DirectoryId)), + Field("SynchronizationPackage::directoryInfos", + &SynchronizationPackage::directoryInfos, + IsEmpty()), + Field("SynchronizationPackage::updatedDirectoryInfoDirectoryIds", + &SynchronizationPackage::updatedDirectoryInfoDirectoryIds, + UnorderedElementsAre(path1SourceContextId, path2SourceContextId))))); + + updater.update({.directories = directories}); +} + +TEST_F(ProjectStorageUpdater, remove_property_editor_qml_paths_if_designer_directory_is_removed) +{ + QStringList directories = {"/path/one"}; + SourceContextId designer1DirectoryId = sourcePathCache.sourceContextId("/path/one/designer"); + SourceId designer1SourceId = SourceId::create(QmlDesigner::SourceNameId{}, designer1DirectoryId); + setFilesDontChanged({path1SourceId, qmldir1SourceId}); + setFilesRemoved({designer1SourceId}); + + EXPECT_CALL(projectStorageMock, + synchronize( + AllOf(Field("SynchronizationPackage::fileStatuses", + &SynchronizationPackage::fileStatuses, + IsEmpty()), + Field("SynchronizationPackage::updatedFileStatusSourceIds", + &SynchronizationPackage::updatedFileStatusSourceIds, + IsSupersetOf({designer1SourceId})), + Field("SynchronizationPackage::propertyEditorQmlPaths", + &SynchronizationPackage::propertyEditorQmlPaths, + IsEmpty()), + Field("SynchronizationPackage::updatedPropertyEditorQmlPathDirectoryIds", + &SynchronizationPackage::updatedPropertyEditorQmlPathDirectoryIds, + UnorderedElementsAre(designer1DirectoryId)), + Field("SynchronizationPackage::directoryInfos", + &SynchronizationPackage::directoryInfos, + IsEmpty()), + Field("SynchronizationPackage::updatedDirectoryInfoDirectoryIds", + &SynchronizationPackage::updatedDirectoryInfoDirectoryIds, + UnorderedElementsAre(path1SourceContextId))))); + + updater.update({.directories = directories}); +} + +TEST_F(ProjectStorageUpdater, + synchronize_annotation_property_editor_qml_paths_directory_if_designer_directory_is_changed) +{ + QStringList directories = {"/path/one"}; + setSubdirectoryPaths(u"/path/one", {"/path/one/designer"}); + QString qmldir{R"(module Bar)"}; + setContent(u"/path/one/qmldir", qmldir); + SourceContextId designer1DirectoryId = sourcePathCache.sourceContextId("/path/one/designer"); + SourceId propertyEditorSpecificSourceId = sourcePathCache.sourceId( + "/path/one/designer/FooSpecifics.qml"); + SourceId propertyEditorSpecificDynamicSourceId = sourcePathCache.sourceId( + "/path/one/designer/HuoSpecificsDynamic.qml"); + SourceId propertyEditorPaneSourceId = sourcePathCache.sourceId( + "/path/one/designer/CaoPane.qml"); + SourceId designer1SourceId = SourceId::create(QmlDesigner::SourceNameId{}, designer1DirectoryId); + setFilesChanged({designer1SourceId}); + setFilesDontChanged({path1SourceId, qmldir1SourceId}); + auto barModuleId = storage.moduleId("Bar", ModuleKind::QmlLibrary); + setFileNames(u"/path/one/designer", + {"FooSpecifics.qml", "HuoSpecificsDynamic.qml", "CaoPane.qml"}, + {"*Pane.qml", "*Specifics.qml", "*SpecificsDynamic.qml"}); + + EXPECT_CALL(projectStorageMock, + synchronize( + AllOf(Field("SynchronizationPackage::propertyEditorQmlPaths", + &SynchronizationPackage::propertyEditorQmlPaths, + IsSupersetOf({IsPropertyEditorQmlPath(barModuleId, + Eq("Foo"), + propertyEditorSpecificSourceId, + designer1DirectoryId), + IsPropertyEditorQmlPath(barModuleId, + Eq("Huo"), + propertyEditorSpecificDynamicSourceId, + designer1DirectoryId), + IsPropertyEditorQmlPath(barModuleId, + Eq("Cao"), + propertyEditorPaneSourceId, + designer1DirectoryId)})), + Field("SynchronizationPackage::updatedPropertyEditorQmlPathDirectoryIds", + &SynchronizationPackage::updatedPropertyEditorQmlPathDirectoryIds, + Contains(designer1DirectoryId))))); + + updater.update({.directories = directories}); +} + +TEST_F(ProjectStorageUpdater, + synchronize_property_editor_qml_paths_directory_if_qml_directory_is_changed) +{ + QStringList directories = {"/path/one"}; + setSubdirectoryPaths(u"/path/one", {"/path/one/designer"}); + QString qmldir{R"(module Bar)"}; + setContent(u"/path/one/qmldir", qmldir); + SourceContextId designer1DirectoryId = sourcePathCache.sourceContextId("/path/one/designer"); + SourceId propertyEditorSpecificSourceId = sourcePathCache.sourceId( + "/path/one/designer/FooSpecifics.qml"); + SourceId propertyEditorSpecificDynamicSourceId = sourcePathCache.sourceId( + "/path/one/designer/HuoSpecificsDynamic.qml"); + SourceId propertyEditorPaneSourceId = sourcePathCache.sourceId( + "/path/one/designer/CaoPane.qml"); + SourceId designer1SourceId = SourceId::create(QmlDesigner::SourceNameId{}, designer1DirectoryId); + setFilesChanged({path1SourceId}); + setFilesDontChanged({qmldir1SourceId, designer1SourceId}); + auto barModuleId = storage.moduleId("Bar", ModuleKind::QmlLibrary); + setFileNames(u"/path/one/designer", + {"FooSpecifics.qml", "HuoSpecificsDynamic.qml", "CaoPane.qml"}, + {"*Pane.qml", "*Specifics.qml", "*SpecificsDynamic.qml"}); + + EXPECT_CALL(projectStorageMock, + synchronize( + AllOf(Field("SynchronizationPackage::propertyEditorQmlPaths", + &SynchronizationPackage::propertyEditorQmlPaths, + IsSupersetOf({IsPropertyEditorQmlPath(barModuleId, + Eq("Foo"), + propertyEditorSpecificSourceId, + designer1DirectoryId), + IsPropertyEditorQmlPath(barModuleId, + Eq("Huo"), + propertyEditorSpecificDynamicSourceId, + designer1DirectoryId), + IsPropertyEditorQmlPath(barModuleId, + Eq("Cao"), + propertyEditorPaneSourceId, + designer1DirectoryId)})), + Field("SynchronizationPackage::updatedPropertyEditorQmlPathDirectoryIds", + &SynchronizationPackage::updatedPropertyEditorQmlPathDirectoryIds, + Contains(designer1DirectoryId))))); + + updater.update({.directories = directories}); +} + +TEST_F(ProjectStorageUpdater, synchronize_property_editor_qml_paths_directory_if_qmldir_is_changed) +{ + QStringList directories = {"/path/one"}; + setSubdirectoryPaths(u"/path/one", {"/path/one/designer"}); + QString qmldir{R"(module Bar)"}; + setContent(u"/path/one/qmldir", qmldir); + SourceContextId designer1DirectoryId = sourcePathCache.sourceContextId("/path/one/designer"); + SourceId propertyEditorSpecificSourceId = sourcePathCache.sourceId( + "/path/one/designer/FooSpecifics.qml"); + SourceId propertyEditorSpecificDynamicSourceId = sourcePathCache.sourceId( + "/path/one/designer/HuoSpecificsDynamic.qml"); + SourceId propertyEditorPaneSourceId = sourcePathCache.sourceId( + "/path/one/designer/CaoPane.qml"); + SourceId designer1SourceId = SourceId::create(QmlDesigner::SourceNameId{}, designer1DirectoryId); + setFilesChanged({qmldir1SourceId}); + setFilesDontChanged({path1SourceId, designer1SourceId}); + auto barModuleId = storage.moduleId("Bar", ModuleKind::QmlLibrary); + setFileNames(u"/path/one/designer", + {"FooSpecifics.qml", "HuoSpecificsDynamic.qml", "CaoPane.qml"}, + {"*Pane.qml", "*Specifics.qml", "*SpecificsDynamic.qml"}); + + EXPECT_CALL(projectStorageMock, + synchronize( + AllOf(Field("SynchronizationPackage::propertyEditorQmlPaths", + &SynchronizationPackage::propertyEditorQmlPaths, + IsSupersetOf({IsPropertyEditorQmlPath(barModuleId, + Eq("Foo"), + propertyEditorSpecificSourceId, + designer1DirectoryId), + IsPropertyEditorQmlPath(barModuleId, + Eq("Huo"), + propertyEditorSpecificDynamicSourceId, + designer1DirectoryId), + IsPropertyEditorQmlPath(barModuleId, + Eq("Cao"), + propertyEditorPaneSourceId, + designer1DirectoryId)})), + Field("SynchronizationPackage::updatedPropertyEditorQmlPathDirectoryIds", + &SynchronizationPackage::updatedPropertyEditorQmlPathDirectoryIds, + Contains(designer1DirectoryId))))); + + updater.update({.directories = directories}); +} + } // namespace diff --git a/tests/unit/tests/utils/google-using-declarations.h b/tests/unit/tests/utils/google-using-declarations.h index 2d5fb1119af..213608916fb 100644 --- a/tests/unit/tests/utils/google-using-declarations.h +++ b/tests/unit/tests/utils/google-using-declarations.h @@ -19,6 +19,7 @@ using testing::ByMove; using testing::ByRef; using testing::ContainerEq; using testing::Contains; +using testing::Each; using testing::ElementsAre; using testing::Eq; using testing::Exactly;