From d0a8de5694cc867637cfd06760843d819e4c80eb Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 24 Oct 2024 18:28:34 +0200 Subject: [PATCH] QmlDesigner: Remove duplicate exports in qmldir Fixes: QDS-13909 Change-Id: I9f369024d832d210c2efaed6410c462ef8af8bd7 Reviewed-by: Thomas Hartmann --- .../projectstorage/projectstorageupdater.cpp | 33 +++++++- .../projectstorageupdater-test.cpp | 76 +++++++++++++++++++ 2 files changed, 106 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp index 542af30805c..17072441777 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp @@ -1167,6 +1167,32 @@ void rangeForTheSameFileName(const ProjectStorageUpdater::Components &components } } +namespace { + +void removeDuplicates(Storage::Synchronization::ExportedTypes &exportedTypes) +{ + using Storage::Synchronization::ExportedType; + + auto factory = [](auto... projections) { + return [=](auto compare) { + return [=](const auto &first, const auto &second) { + return compare(std::forward_as_tuple(std::invoke(projections, first)...), + std::forward_as_tuple(std::invoke(projections, second)...)); + }; + }; + }; + + auto compare = factory(&ExportedType::name, &ExportedType::version); + auto less = compare(std::ranges::less{}); + auto equal = compare(std::ranges::equal_to{}); + + std::ranges::sort(exportedTypes, less); + + auto duplicateExportedTypes = std::ranges::unique(exportedTypes, equal); + exportedTypes.erase(duplicateExportedTypes.begin(), duplicateExportedTypes.end()); +} +} // namespace + Storage::Synchronization::ExportedTypes createExportedTypes(ProjectStorageUpdater::ComponentRange components) { Storage::Synchronization::ExportedTypes exportedTypes; @@ -1178,6 +1204,8 @@ Storage::Synchronization::ExportedTypes createExportedTypes(ProjectStorageUpdate Storage::Version{component.majorVersion, component.minorVersion}); } + removeDuplicates(exportedTypes); + return exportedTypes; } @@ -1197,9 +1225,8 @@ void ProjectStorageUpdater::parseQmlComponents(Components components, keyValue("directory id", directoryId), keyValue("qmldir state", qmldirState)}; - std::sort(components.begin(), components.end(), [](auto &&first, auto &&second) { - return first.fileName < second.fileName; - }); + std::ranges::sort(components, + [](auto &&first, auto &&second) { return first.fileName < second.fileName; }); auto directoryPath = m_pathCache.sourceContextPath(directoryId); diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp index 60d617635da..3a51b997eba 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -830,6 +830,82 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents) updater.update({.directories = directories}); } +TEST_F(ProjectStorageUpdater, skip_duplicate_qmldir_entries) +{ + QString qmldir{R"(module Example + FirstType 1.0 First.qml + FirstType 1.0 First.qml + FirstType 2.0 First.qml + FirstType 2.2 First2.qml + SecondType 2.2 Second.qml)"}; + setContent(u"/path/qmldir", qmldir); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import2, import3)), + Field( + &SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraitsKind::Reference, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::Full), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0), + IsExportedType(exampleModuleId, "FirstType", 2, 0), + IsExportedType(pathModuleId, "First", -1, -1)))), + AllOf(IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{"Object2"}, + TypeTraitsKind::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::Full), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2), + IsExportedType(pathModuleId, "First2", -1, -1)))), + AllOf(IsStorageType("Second.qml", + Storage::Synchronization::ImportedType{"Object3"}, + TypeTraitsKind::Reference, + qmlDocumentSourceId3, + Storage::Synchronization::ChangeLevel::Full), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(exampleModuleId, "SecondType", 2, 2), + IsExportedType(pathModuleId, "Second", -1, -1)))))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21), + IsFileStatus(qmlDocumentSourceId1, 1, 21), + IsFileStatus(qmlDocumentSourceId2, 1, 21), + IsFileStatus(qmlDocumentSourceId3, 1, 21))), + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, + UnorderedElementsAre(directoryPathSourceId)), + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); + + updater.update({.directories = directories}); +} + TEST_F(ProjectStorageUpdater, synchronize_add_only_qml_document_in_directory) { QString qmldir{R"(module Example