From 4fdf1522ff210f80277a5bc5c3dd914710fd2923 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 31 Mar 2025 18:06:23 +0200 Subject: [PATCH] QmlDesigner: Add exported type name notification If an exported type name will be updated, the notifier will put it into removed and added. That can anyway happen if the module is moved with a time difference. Task-number: QDS-14669 Change-Id: I5ede7ce8edc8c02056bea35f27d266379bae044c Reviewed-by: Thomas Hartmann --- .../libs/designercore/include/abstractview.h | 3 + .../libs/designercore/model/abstractview.cpp | 2 + .../libs/designercore/model/model.cpp | 7 ++ .../libs/designercore/model/model_p.h | 2 + .../projectstorage/projectstorage.cpp | 74 +++++++++++++----- .../projectstorage/projectstorage.h | 10 ++- .../projectstorage/projectstorageinfotypes.h | 43 ++++------- .../projectstorage/projectstorageobserver.h | 4 + .../projectstorage/projectstoragetypes.h | 2 + .../matchers/info_exportedtypenames-matcher.h | 52 ++++++++----- tests/unit/tests/mocks/abstractviewmock.h | 4 + tests/unit/tests/mocks/projectstoragemock.cpp | 4 +- .../tests/mocks/projectstorageobservermock.h | 4 + .../tests/printers/gtest-creator-printing.cpp | 3 +- .../unittests/metainfo/nodemetainfo-test.cpp | 28 +++---- .../unit/tests/unittests/model/model-test.cpp | 33 +++++++- .../projectstorage/projectstorage-test.cpp | 76 +++++++++++++++++-- 17 files changed, 255 insertions(+), 96 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designercore/include/abstractview.h b/src/plugins/qmldesigner/libs/designercore/include/abstractview.h index c6f25e7bad1..79f6267162a 100644 --- a/src/plugins/qmldesigner/libs/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/libs/designercore/include/abstractview.h @@ -155,6 +155,9 @@ public: virtual void modelAboutToBeDetached(Model *model); virtual void refreshMetaInfos(const TypeIds &deletedTypeIds); + using ExportedTypeNames = Storage::Info::ExportedTypeNames; + virtual void exportedTypeNamesChanged(const ExportedTypeNames &added, + const ExportedTypeNames &removed); virtual void nodeCreated(const ModelNode &createdNode); virtual void nodeAboutToBeRemoved(const ModelNode &removedNode); diff --git a/src/plugins/qmldesigner/libs/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/libs/designercore/model/abstractview.cpp index acc8be0f9b0..c19ff88219e 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/abstractview.cpp @@ -177,6 +177,8 @@ void AbstractView::modelAboutToBeDetached(Model *) void AbstractView::refreshMetaInfos(const TypeIds &) {} +void AbstractView::exportedTypeNamesChanged(const ExportedTypeNames &, const ExportedTypeNames &) {} + /*! \enum QmlDesigner::AbstractView::PropertyChangeFlag diff --git a/src/plugins/qmldesigner/libs/designercore/model/model.cpp b/src/plugins/qmldesigner/libs/designercore/model/model.cpp index dc609493f98..b0636a9f0ca 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/model.cpp @@ -474,6 +474,13 @@ void ModelPrivate::exportedTypesChanged() } } +void ModelPrivate::exportedTypeNamesChanged(const ExportedTypeNames &added, + const ExportedTypeNames &removed) +{ + notifyNodeInstanceViewLast( + [&](AbstractView *view) { view->exportedTypeNamesChanged(added, removed); }); +} + void ModelPrivate::removeAllSubNodes(const InternalNodePointer &node) { for (const InternalNodePointer &subNode : node->allSubNodes()) diff --git a/src/plugins/qmldesigner/libs/designercore/model/model_p.h b/src/plugins/qmldesigner/libs/designercore/model/model_p.h index 38384a416c5..d9591ccbf9e 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/libs/designercore/model/model_p.h @@ -326,6 +326,8 @@ public: protected: void removedTypeIds(const TypeIds &removedTypeIds) override; void exportedTypesChanged() override; + void exportedTypeNamesChanged(const ExportedTypeNames &added, + const ExportedTypeNames &removed) override; void removeNode(const InternalNodePointer &node); private: diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp index 79c3749fe6e..f7dfa4bcaac 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp @@ -107,14 +107,20 @@ struct ProjectStorage::Statements "defaultPropertyId=propertyDeclarationId " "WHERE t.typeId=?", database}; - mutable Sqlite::ReadStatement<4, 1> selectExportedTypesByTypeIdStatement{ - "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1) FROM " - "exportedTypeNames WHERE typeId=?", + mutable Sqlite::ReadStatement<5, 1> selectExportedTypesByTypeIdStatement{ + "SELECT moduleId, typeId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1) " + "FROM exportedTypeNames " + "WHERE typeId=?", database}; - mutable Sqlite::ReadStatement<4, 2> selectExportedTypesByTypeIdAndSourceIdStatement{ - "SELECT etn.moduleId, name, ifnull(etn.majorVersion, -1), ifnull(etn.minorVersion, -1) " - "FROM exportedTypeNames AS etn JOIN documentImports USING(moduleId) WHERE typeId=?1 AND " - "sourceId=?2", + mutable Sqlite::ReadStatement<5, 2> selectExportedTypesByTypeIdAndSourceIdStatement{ + "SELECT etn.moduleId, " + " typeId, " + " name, " + " ifnull(etn.majorVersion, -1), " + " ifnull(etn.minorVersion, -1) " + "FROM exportedTypeNames AS etn " + "JOIN documentImports USING(moduleId) " + "WHERE typeId=?1 AND sourceId=?2", database}; mutable Sqlite::ReadStatement<8> selectTypesStatement{ "SELECT sourceId, t.name, t.typeId, prototypeId, extensionId, traits, annotationTraits, " @@ -1298,6 +1304,8 @@ void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackag NanotraceHR::Tracer tracer{"synchronize", projectStorageCategory()}; TypeIds deletedTypeIds; + Storage::Info::ExportedTypeNames removedExportedTypeNames; + Storage::Info::ExportedTypeNames addedExportedTypeNames; ExportedTypesChanged exportedTypesChanged = ExportedTypesChanged::No; Sqlite::withImmediateTransaction(database, [&] { @@ -1332,6 +1340,8 @@ void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackag relinkablePrototypes, relinkableExtensions, exportedTypesChanged, + removedExportedTypeNames, + addedExportedTypeNames, package.updatedSourceIds); synchronizeTypeAnnotations(package.typeAnnotations, package.updatedTypeAnnotationSourceIds); synchronizePropertyEditorQmlPaths(package.propertyEditorQmlPaths, @@ -1361,7 +1371,10 @@ void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackag commonTypeCache_.resetTypeIds(); }); - callRefreshMetaInfoCallback(deletedTypeIds, exportedTypesChanged); + callRefreshMetaInfoCallback(deletedTypeIds, + exportedTypesChanged, + removedExportedTypeNames, + addedExportedTypeNames); } void ProjectStorage::synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) @@ -2348,8 +2361,11 @@ ProjectStorage::ModuleCacheEntries ProjectStorage::fetchAllModules() const return s->selectAllModulesStatement.valuesWithTransaction(); } -void ProjectStorage::callRefreshMetaInfoCallback(TypeIds &deletedTypeIds, - ExportedTypesChanged &exportedTypesChanged) +void ProjectStorage::callRefreshMetaInfoCallback( + TypeIds &deletedTypeIds, + ExportedTypesChanged exportedTypesChanged, + const Storage::Info::ExportedTypeNames &removedExportedTypeNames, + const Storage::Info::ExportedTypeNames &addedExportedTypeNames) { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"call refresh meta info callback", @@ -2364,8 +2380,10 @@ void ProjectStorage::callRefreshMetaInfoCallback(TypeIds &deletedTypeIds, } if (exportedTypesChanged == ExportedTypesChanged::Yes) { - for (ProjectStorageObserver *observer : observers) + for (ProjectStorageObserver *observer : observers) { observer->exportedTypesChanged(); + observer->exportedTypeNamesChanged(addedExportedTypeNames, removedExportedTypeNames); + } } } @@ -2428,7 +2446,6 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn { NanotraceHR::Tracer tracer{"synchronize type annotations", projectStorageCategory()}; - updateTypeIdInTypeAnnotations(typeAnnotations); auto compareKey = [](auto &&first, auto &&second) { return first.typeId <=> second.typeId; }; @@ -2522,6 +2539,8 @@ void ProjectStorage::synchronizeTypes(Storage::Synchronization::Types &types, Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions, ExportedTypesChanged &exportedTypesChanged, + Storage::Info::ExportedTypeNames &removedExportedTypeNames, + Storage::Info::ExportedTypeNames &addedExportedTypeNames, const SourceIds &updatedSourceIds) { NanotraceHR::Tracer tracer{"synchronize types", projectStorageCategory()}; @@ -2564,7 +2583,9 @@ void ProjectStorage::synchronizeTypes(Storage::Synchronization::Types &types, relinkablePropertyDeclarations, relinkablePrototypes, relinkableExtensions, - exportedTypesChanged); + exportedTypesChanged, + removedExportedTypeNames, + addedExportedTypeNames); syncPrototypesAndExtensions(types, relinkablePrototypes, relinkableExtensions); resetDefaultPropertiesIfChanged(types); @@ -3299,17 +3320,23 @@ void ProjectStorage::repairBrokenAliasPropertyDeclarations() linkAliases(brokenAliasPropertyDeclarations, RaiseError::No); } -void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, - Storage::Synchronization::ExportedTypes &exportedTypes, - AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, - PropertyDeclarations &relinkablePropertyDeclarations, - Prototypes &relinkablePrototypes, - Prototypes &relinkableExtensions, - ExportedTypesChanged &exportedTypesChanged) +void ProjectStorage::synchronizeExportedTypes( + const TypeIds &updatedTypeIds, + Storage::Synchronization::ExportedTypes &exportedTypes, + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions, + ExportedTypesChanged &exportedTypesChanged, + Storage::Info::ExportedTypeNames &removedExportedTypeNames, + Storage::Info::ExportedTypeNames &addedExportedTypeNames) { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"synchronize exported types", projectStorageCategory()}; + removedExportedTypeNames.reserve(exportedTypes.size()); + addedExportedTypeNames.reserve(exportedTypes.size()); + std::ranges::sort(exportedTypes, [](auto &&first, auto &&second) { if (first.moduleId < second.moduleId) return true; @@ -3376,6 +3403,8 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, handlePrototypesWithExportedTypeNameAndTypeId(type.name, unresolvedTypeId, relinkablePrototypes); handleExtensionsWithExportedTypeNameAndTypeId(type.name, unresolvedTypeId, relinkableExtensions); + addedExportedTypeNames.emplace_back(type.moduleId, type.typeId, type.name, type.version); + exportedTypesChanged = ExportedTypesChanged::Yes; }; @@ -3397,6 +3426,9 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, s->updateExportedTypeNameTypeIdStatement.write(view.exportedTypeNameId, type.typeId); exportedTypesChanged = ExportedTypesChanged::Yes; + addedExportedTypeNames.emplace_back(type.moduleId, type.typeId, type.name, type.version); + removedExportedTypeNames.emplace_back(view.moduleId, view.typeId, view.name, view.version); + return Sqlite::UpdateChange::Update; } return Sqlite::UpdateChange::No; @@ -3417,6 +3449,8 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, s->deleteExportedTypeNameStatement.write(view.exportedTypeNameId); + removedExportedTypeNames.emplace_back(view.moduleId, view.typeId, view.name, view.version); + exportedTypesChanged = ExportedTypesChanged::Yes; }; diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.h index f11921330f3..1e6d287abe2 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.h @@ -325,7 +325,9 @@ private: enum class ExportedTypesChanged { No, Yes }; void callRefreshMetaInfoCallback(TypeIds &deletedTypeIds, - ExportedTypesChanged &exportedTypesChanged); + ExportedTypesChanged exportedTypesChanged, + const Storage::Info::ExportedTypeNames &removedExportedTypeNames, + const Storage::Info::ExportedTypeNames &addedExportedTypeNames); class AliasPropertyDeclaration { @@ -565,6 +567,8 @@ private: Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions, ExportedTypesChanged &exportedTypesChanged, + Storage::Info::ExportedTypeNames &removedExportedTypeNames, + Storage::Info::ExportedTypeNames &addedExportedTypeNames, const SourceIds &updatedSourceIds); void synchronizeDirectoryInfos(Storage::Synchronization::DirectoryInfos &directoryInfos, @@ -666,7 +670,9 @@ private: PropertyDeclarations &relinkablePropertyDeclarations, Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions, - ExportedTypesChanged &exportedTypesChanged); + ExportedTypesChanged &exportedTypesChanged, + Storage::Info::ExportedTypeNames &removedExportedTypeNames, + Storage::Info::ExportedTypeNames &addedExportedTypeNames); void synchronizePropertyDeclarationsInsertAlias( AliasPropertyDeclarations &insertedAliasPropertyDeclarations, diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageinfotypes.h index a23e2bd0212..025dc9de466 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageinfotypes.h @@ -257,20 +257,7 @@ public: explicit operator bool() const { return value >= 0; } - friend bool operator==(VersionNumber first, VersionNumber second) noexcept - { - return first.value == second.value; - } - - friend bool operator!=(VersionNumber first, VersionNumber second) noexcept - { - return !(first == second); - } - - friend bool operator<(VersionNumber first, VersionNumber second) noexcept - { - return first.value < second.value; - } + auto operator<=>(const VersionNumber &first) const noexcept = default; public: int value = -1; @@ -294,15 +281,7 @@ public: : major{major} {} - friend bool operator==(Version first, Version second) noexcept - { - return first.major == second.major && first.minor == second.minor; - } - - friend bool operator<(Version first, Version second) noexcept - { - return std::tie(first.major, first.minor) < std::tie(second.major, second.minor); - } + auto operator<=>(const Version &first) const noexcept = default; explicit operator bool() const { return major && minor; } @@ -470,32 +449,36 @@ public: ExportedTypeName() = default; ExportedTypeName(ModuleId moduleId, + TypeId typeId, ::Utils::SmallStringView name, Storage::Version version = Storage::Version{}) : name{name} , version{version} , moduleId{moduleId} + , typeId{typeId} {} ExportedTypeName(ModuleId moduleId, + TypeId typeId, ::Utils::SmallStringView name, int majorVersion, int minorVersion) : name{name} , version{majorVersion, minorVersion} , moduleId{moduleId} + , typeId{typeId} {} friend bool operator==(const ExportedTypeName &first, const ExportedTypeName &second) { - return first.moduleId == second.moduleId && first.version == second.version - && first.name == second.name; + return first.moduleId == second.moduleId && first.typeId == second.typeId + && first.version == second.version && first.name == second.name; } - friend bool operator<(const ExportedTypeName &first, const ExportedTypeName &second) + friend auto operator<=>(const ExportedTypeName &first, const ExportedTypeName &second) { - return std::tie(first.moduleId, first.name, first.version) - < std::tie(second.moduleId, second.name, second.version); + return std::tie(first.moduleId, first.name, first.version, second.typeId) + <=> std::tie(second.moduleId, second.name, second.version, second.typeId); } template @@ -505,7 +488,8 @@ public: using NanotraceHR::keyValue; auto dict = dictonary(keyValue("name", exportedTypeName.name), keyValue("version", exportedTypeName.version), - keyValue("module id", exportedTypeName.moduleId)); + keyValue("module id", exportedTypeName.moduleId), + keyValue("type id", exportedTypeName.typeId)); convertToString(string, dict); } @@ -514,6 +498,7 @@ public: ::Utils::SmallString name; Storage::Version version; ModuleId moduleId; + TypeId typeId; }; using ExportedTypeNames = std::vector; diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageobserver.h b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageobserver.h index 5beadc0472d..4fec677cb0a 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageobserver.h +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageobserver.h @@ -4,13 +4,17 @@ #pragma once #include "projectstorageids.h" +#include "projectstorageinfotypes.h" namespace QmlDesigner { class ProjectStorageObserver { public: + using ExportedTypeNames = Storage::Info::ExportedTypeNames; + virtual void removedTypeIds(const TypeIds &removedTypeIds) = 0; virtual void exportedTypesChanged() = 0; + virtual void exportedTypeNamesChanged(const ExportedTypeNames &added, const ExportedTypeNames &removed) = 0; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstoragetypes.h index a8f3d1d6e95..0331418568a 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstoragetypes.h @@ -393,11 +393,13 @@ public: {} explicit ExportedType(ModuleId moduleId, + TypeId typeId, ::Utils::SmallStringView name, int majorVersion, int minorVersion) : name{name} , version{majorVersion, minorVersion} + , typeId{typeId} , moduleId{moduleId} {} diff --git a/tests/unit/tests/matchers/info_exportedtypenames-matcher.h b/tests/unit/tests/matchers/info_exportedtypenames-matcher.h index 8ef86e53dec..f28008c7849 100644 --- a/tests/unit/tests/matchers/info_exportedtypenames-matcher.h +++ b/tests/unit/tests/matchers/info_exportedtypenames-matcher.h @@ -7,27 +7,43 @@ #include -template -auto IsInfoExportTypeNames(const ModuleIdMatcher &moduleIdMatcher, - const NameMatcher &nameMatcher, - const MajorVersionMatcher &majorVersionMatcher, - const MinorVersionMatcher &minorVersionMatcher) +template +auto IsInfoExportTypeName(const ModuleIdMatcher &moduleIdMatcher, + const TypeIdMatcher &typeIdMatcher, + const NameMatcher &nameMatcher, + const MajorVersionMatcher &majorVersionMatcher, + const MinorVersionMatcher &minorVersionMatcher) { - return AllOf(Field("QmlDesigner::Storage::Info::ExportedTypeName::moduleId", &QmlDesigner::Storage::Info::ExportedTypeName::moduleId, moduleIdMatcher), - Field("QmlDesigner::Storage::Info::ExportedTypeName::name", &QmlDesigner::Storage::Info::ExportedTypeName::name, nameMatcher), - Field("QmlDesigner::Storage::Info::ExportedTypeName::version", &QmlDesigner::Storage::Info::ExportedTypeName::version, + return AllOf(Field("QmlDesigner::Storage::Info::ExportedTypeName::moduleId", + &QmlDesigner::Storage::Info::ExportedTypeName::moduleId, + moduleIdMatcher), + Field("QmlDesigner::Storage::Info::ExportedTypeName::typeId", + &QmlDesigner::Storage::Info::ExportedTypeName::typeId, + typeIdMatcher), + Field("QmlDesigner::Storage::Info::ExportedTypeName::name", + &QmlDesigner::Storage::Info::ExportedTypeName::name, + nameMatcher), + Field("QmlDesigner::Storage::Info::ExportedTypeName::version", + &QmlDesigner::Storage::Info::ExportedTypeName::version, IsVersion(majorVersionMatcher, minorVersionMatcher))); } -template -auto IsInfoExportTypeNames(const ModuleIdMatcher &moduleIdMatcher, - const NameMatcher &nameMatcher, - const VersionMatcher &versionMatcher) +template +auto IsInfoExportTypeName(const ModuleIdMatcher &moduleIdMatcher, + const TypeIdMatcher &typeIdMatcher, + const NameMatcher &nameMatcher, + const VersionMatcher &versionMatcher) { - return AllOf(Field("QmlDesigner::Storage::Info::ExportedTypeName::moduleId", &QmlDesigner::Storage::Info::ExportedTypeName::moduleId, moduleIdMatcher), - Field("QmlDesigner::Storage::Info::ExportedTypeName::name", &QmlDesigner::Storage::Info::ExportedTypeName::name, nameMatcher), - Field("QmlDesigner::Storage::Info::ExportedTypeName::version", &QmlDesigner::Storage::Info::ExportedTypeName::version, versionMatcher)); + return AllOf(Field("QmlDesigner::Storage::Info::ExportedTypeName::moduleId", + &QmlDesigner::Storage::Info::ExportedTypeName::moduleId, + moduleIdMatcher), + Field("QmlDesigner::Storage::Info::ExportedTypeName::typeId", + &QmlDesigner::Storage::Info::ExportedTypeName::typeId, + typeIdMatcher), + Field("QmlDesigner::Storage::Info::ExportedTypeName::name", + &QmlDesigner::Storage::Info::ExportedTypeName::name, + nameMatcher), + Field("QmlDesigner::Storage::Info::ExportedTypeName::version", + &QmlDesigner::Storage::Info::ExportedTypeName::version, + versionMatcher)); } diff --git a/tests/unit/tests/mocks/abstractviewmock.h b/tests/unit/tests/mocks/abstractviewmock.h index 7a2ea1b48c2..941f6c2cb3d 100644 --- a/tests/unit/tests/mocks/abstractviewmock.h +++ b/tests/unit/tests/mocks/abstractviewmock.h @@ -65,6 +65,10 @@ public: (override)); MOCK_METHOD(void, nodeAboutToBeRemoved, (const QmlDesigner::ModelNode &removedNode), (override)); MOCK_METHOD(void, refreshMetaInfos, (const QmlDesigner::TypeIds &), (override)); + MOCK_METHOD(void, + exportedTypeNamesChanged, + (const ExportedTypeNames &added, const ExportedTypeNames &removed), + (override)); MOCK_METHOD(void, modelAttached, (QmlDesigner::Model *), (override)); MOCK_METHOD(void, modelAboutToBeDetached, (QmlDesigner::Model *), (override)); diff --git a/tests/unit/tests/mocks/projectstoragemock.cpp b/tests/unit/tests/mocks/projectstoragemock.cpp index 1d3a5f58cd2..150930db1ab 100644 --- a/tests/unit/tests/mocks/projectstoragemock.cpp +++ b/tests/unit/tests/mocks/projectstoragemock.cpp @@ -128,7 +128,7 @@ void ProjectStorageMock::addExportedTypeName(QmlDesigner::TypeId typeId, ON_CALL(*this, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId)); ON_CALL(*this, fetchTypeIdByModuleIdAndExportedName(Eq(moduleId), Eq(typeName))) .WillByDefault(Return(typeId)); - exportedTypeName[typeId].emplace_back(moduleId, typeName); + exportedTypeName[typeId].emplace_back(moduleId, typeId, typeName); } void ProjectStorageMock::addExportedTypeNameBySourceId(QmlDesigner::TypeId typeId, @@ -136,7 +136,7 @@ void ProjectStorageMock::addExportedTypeNameBySourceId(QmlDesigner::TypeId typeI Utils::SmallStringView typeName, QmlDesigner::SourceId sourceId) { - exportedTypeNameBySourceId[{typeId, sourceId}].emplace_back(moduleId, typeName); + exportedTypeNameBySourceId[{typeId, sourceId}].emplace_back(moduleId, typeId, typeName); } void ProjectStorageMock::removeExportedTypeName(QmlDesigner::TypeId typeId, diff --git a/tests/unit/tests/mocks/projectstorageobservermock.h b/tests/unit/tests/mocks/projectstorageobservermock.h index 1f98ecae3bb..b1b6717026a 100644 --- a/tests/unit/tests/mocks/projectstorageobservermock.h +++ b/tests/unit/tests/mocks/projectstorageobservermock.h @@ -12,4 +12,8 @@ class ProjectStorageObserverMock : public QmlDesigner::ProjectStorageObserver public: MOCK_METHOD(void, removedTypeIds, (const QmlDesigner::TypeIds &), (override)); MOCK_METHOD(void, exportedTypesChanged, (), (override)); + MOCK_METHOD(void, + exportedTypeNamesChanged, + (const ExportedTypeNames &added, const ExportedTypeNames &removed), + (override)); }; diff --git a/tests/unit/tests/printers/gtest-creator-printing.cpp b/tests/unit/tests/printers/gtest-creator-printing.cpp index 40cc868edea..6d82128ab18 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.cpp +++ b/tests/unit/tests/printers/gtest-creator-printing.cpp @@ -751,7 +751,8 @@ std::ostream &operator<<(std::ostream &out, const Type &type) std::ostream &operator<<(std::ostream &out, const ExportedTypeName &name) { - return out << "(\"" << name.name << "\", " << name.moduleId << ", " << name.version << ")"; + return out << "(\"" << name.name << "\", " << name.moduleId << ", " << name.version << ", " + << name.typeId << ")"; } std::ostream &operator<<(std::ostream &out, const TypeHint &hint) diff --git a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp index ed28cb28544..41ad91ee6e1 100644 --- a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp +++ b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp @@ -2624,23 +2624,23 @@ TEST_F(NodeMetaInfo, default_is_not_enumeration) TEST_F(NodeMetaInfo, all_external_type_names) { - QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, "Object", 2, -1}, - {qmlModuleId, "Obj", 2, 1}}; auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "Foo"); + QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, metaInfo.id(), "Object", 2, -1}, + {qmlModuleId, metaInfo.id(), "Obj", 2, 1}}; ON_CALL(projectStorageMock, exportedTypeNames(metaInfo.id())).WillByDefault(Return(names)); auto exportedTypeNames = metaInfo.allExportedTypeNames(); ASSERT_THAT(exportedTypeNames, - UnorderedElementsAre(IsInfoExportTypeNames(qmlModuleId, "Object", 2, -1), - IsInfoExportTypeNames(qmlModuleId, "Obj", 2, 1))); + UnorderedElementsAre(IsInfoExportTypeName(qmlModuleId, metaInfo.id(), "Object", 2, -1), + IsInfoExportTypeName(qmlModuleId, metaInfo.id(), "Obj", 2, 1))); } TEST_F(NodeMetaInfo, default_has_no_external_type_names) { - QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, "Object", 2, -1}, - {qmlModuleId, "Obj", 2, 1}}; QmlDesigner::NodeMetaInfo metaInfo; + QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, metaInfo.id(), "Object", 2, -1}, + {qmlModuleId, metaInfo.id(), "Obj", 2, 1}}; ON_CALL(projectStorageMock, exportedTypeNames(_)).WillByDefault(Return(names)); auto exportedTypeNames = metaInfo.allExportedTypeNames(); @@ -2650,24 +2650,24 @@ TEST_F(NodeMetaInfo, default_has_no_external_type_names) TEST_F(NodeMetaInfo, external_type_names_for_source_id) { - QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, "Object", 2, -1}, - {qmlModuleId, "Obj", 2, 1}}; auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "Foo"); + QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, metaInfo.id(), "Object", 2, -1}, + {qmlModuleId, metaInfo.id(), "Obj", 2, 1}}; ON_CALL(projectStorageMock, exportedTypeNames(metaInfo.id(), model.fileUrlSourceId())) .WillByDefault(Return(names)); auto exportedTypeNames = metaInfo.exportedTypeNamesForSourceId(model.fileUrlSourceId()); ASSERT_THAT(exportedTypeNames, - UnorderedElementsAre(IsInfoExportTypeNames(qmlModuleId, "Object", 2, -1), - IsInfoExportTypeNames(qmlModuleId, "Obj", 2, 1))); + UnorderedElementsAre(IsInfoExportTypeName(qmlModuleId, metaInfo.id(), "Object", 2, -1), + IsInfoExportTypeName(qmlModuleId, metaInfo.id(), "Obj", 2, 1))); } TEST_F(NodeMetaInfo, default_has_no_external_type_names_for_source_id) { - QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, "Object", 2, -1}, - {qmlModuleId, "Obj", 2, 1}}; QmlDesigner::NodeMetaInfo metaInfo; + QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, metaInfo.id(), "Object", 2, -1}, + {qmlModuleId, metaInfo.id(), "Obj", 2, 1}}; ON_CALL(projectStorageMock, exportedTypeNames(metaInfo.id(), model.fileUrlSourceId())) .WillByDefault(Return(names)); @@ -2678,9 +2678,9 @@ TEST_F(NodeMetaInfo, default_has_no_external_type_names_for_source_id) TEST_F(NodeMetaInfo, invalid_source_id_has_no_external_type_names_for_source_id) { - QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, "Object", 2, -1}, - {qmlModuleId, "Obj", 2, 1}}; auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "Foo"); + QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, metaInfo.id(), "Object", 2, -1}, + {qmlModuleId, metaInfo.id(), "Obj", 2, 1}}; ON_CALL(projectStorageMock, exportedTypeNames(metaInfo.id(), model.fileUrlSourceId())) .WillByDefault(Return(names)); QmlDesigner::SourceId sourceId; diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp index 88502e1d9ec..31ce1a30d8e 100644 --- a/tests/unit/tests/unittests/model/model-test.cpp +++ b/tests/unit/tests/unittests/model/model-test.cpp @@ -1141,14 +1141,13 @@ TEST_F(Model_MetaInfo, remove_project_storage_observer_from_project_storage) QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, "Item", -1, -1, nullptr, {}}; } -TEST_F(Model_MetaInfo, refresh_callback_is_calling_abstract_view) +TEST_F(Model_MetaInfo, refresh_meta_infos_callback_is_calling_abstract_view) { const QmlDesigner::TypeIds typeIds = {QmlDesigner::TypeId::create(3), QmlDesigner::TypeId::create(1)}; ProjectStorageObserverMock observerMock; QmlDesigner::ProjectStorageObserver *observer = nullptr; ON_CALL(projectStorageMock, addObserver(_)).WillByDefault([&](auto *o) { observer = o; }); - QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, "Item", -1, -1, nullptr, {}}; model.attachView(&viewMock); @@ -1157,6 +1156,36 @@ TEST_F(Model_MetaInfo, refresh_callback_is_calling_abstract_view) observer->removedTypeIds(typeIds); } +TEST_F(Model_MetaInfo, added_exported_type_names_are_changed_callback_is_calling_abstract_view) +{ + using QmlDesigner::Storage::Info::ExportedTypeNames; + ExportedTypeNames added = {{qtQuickModuleId, itemTypeId, "Foo", 1, 1}}; + ProjectStorageObserverMock observerMock; + QmlDesigner::ProjectStorageObserver *observer = nullptr; + ON_CALL(projectStorageMock, addObserver(_)).WillByDefault([&](auto *o) { observer = o; }); + QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, "Item", -1, -1, nullptr, {}}; + model.attachView(&viewMock); + + EXPECT_CALL(viewMock, exportedTypeNamesChanged(added, IsEmpty())); + + observer->exportedTypeNamesChanged(added, {}); +} + +TEST_F(Model_MetaInfo, removed_exported_type_names_are_changed_callback_is_calling_abstract_view) +{ + using QmlDesigner::Storage::Info::ExportedTypeNames; + ExportedTypeNames removed = {{qtQuickModuleId, itemTypeId, "Foo", 1, 1}}; + ProjectStorageObserverMock observerMock; + QmlDesigner::ProjectStorageObserver *observer = nullptr; + ON_CALL(projectStorageMock, addObserver(_)).WillByDefault([&](auto *o) { observer = o; }); + QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, "Item", -1, -1, nullptr, {}}; + model.attachView(&viewMock); + + EXPECT_CALL(viewMock, exportedTypeNamesChanged(IsEmpty(), removed)); + + observer->exportedTypeNamesChanged({}, removed); +} + TEST_F(Model_MetaInfo, meta_infos_for_mdoule) { projectStorageMock.createModule("Foo", ModuleKind::QmlLibrary); diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index 96aa0a6ede8..b948fb91ce1 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -8189,9 +8189,9 @@ TEST_F(ProjectStorage, get_exported_type_names) auto exportedTypeNames = storage.exportedTypeNames(typeId); ASSERT_THAT(exportedTypeNames, - UnorderedElementsAre(IsInfoExportTypeNames(qmlModuleId, "Object", 2, -1), - IsInfoExportTypeNames(qmlModuleId, "Obj", 2, -1), - IsInfoExportTypeNames(qmlNativeModuleId, "QObject", -1, -1))); + UnorderedElementsAre(IsInfoExportTypeName(qmlModuleId, typeId, "Object", 2, -1), + IsInfoExportTypeName(qmlModuleId, typeId, "Obj", 2, -1), + IsInfoExportTypeName(qmlNativeModuleId, typeId, "QObject", -1, -1))); } TEST_F(ProjectStorage, get_no_exported_type_names_if_type_id_is_invalid) @@ -8215,8 +8215,8 @@ TEST_F(ProjectStorage, get_exported_type_names_for_source_id) auto exportedTypeNames = storage.exportedTypeNames(typeId, sourceId3); ASSERT_THAT(exportedTypeNames, - UnorderedElementsAre(IsInfoExportTypeNames(qmlModuleId, "Object", 2, -1), - IsInfoExportTypeNames(qmlModuleId, "Obj", 2, -1))); + UnorderedElementsAre(IsInfoExportTypeName(qmlModuleId, typeId, "Object", 2, -1), + IsInfoExportTypeName(qmlModuleId, typeId, "Obj", 2, -1))); } TEST_F(ProjectStorage, get_no_exported_type_names_for_source_id_for_invalid_type_id) @@ -9075,7 +9075,7 @@ TEST_F(ProjectStorage, added_document_import_fixes_unresolved_extension) ASSERT_THAT(fetchType(sourceId1, "QQuickItem"), HasExtensionId(fetchTypeId(sourceId2, "QObject"))); } -TEST_F(ProjectStorage, added_export_is_notifing_changed_exported_types) +TEST_F(ProjectStorage, added_export_is_notifying_changed_exported_types) { auto package{createSimpleSynchronizationPackage()}; storage.synchronize(package); @@ -9088,7 +9088,7 @@ TEST_F(ProjectStorage, added_export_is_notifing_changed_exported_types) storage.synchronize(std::move(package)); } -TEST_F(ProjectStorage, removed_export_is_notifing_changed_exported_types) +TEST_F(ProjectStorage, removed_export_is_notifying_changed_exported_types) { auto package{createSimpleSynchronizationPackage()}; storage.synchronize(package); @@ -9101,7 +9101,7 @@ TEST_F(ProjectStorage, removed_export_is_notifing_changed_exported_types) storage.synchronize(std::move(package)); } -TEST_F(ProjectStorage, changed_export_is_notifing_changed_exported_types) +TEST_F(ProjectStorage, changed_export_is_notifying_changed_exported_types) { auto package{createSimpleSynchronizationPackage()}; storage.synchronize(package); @@ -9114,6 +9114,66 @@ TEST_F(ProjectStorage, changed_export_is_notifing_changed_exported_types) storage.synchronize(std::move(package)); } +TEST_F(ProjectStorage, added_export_is_notifying_changed_exported_type_names) +{ + auto package{createSimpleSynchronizationPackage()}; + storage.synchronize(package); + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + NiceMock observerMock; + storage.addObserver(&observerMock); + + EXPECT_CALL(observerMock, + exportedTypeNamesChanged(ElementsAre( + IsInfoExportTypeName(qmlNativeModuleId, + fetchTypeId(sourceId2, "QObject"), + Eq("Objec"), + A())), + IsEmpty())); + + storage.synchronize(std::move(package)); +} + +TEST_F(ProjectStorage, removed_export_is_notifying_changed_exported_type_names) +{ + auto package{createSimpleSynchronizationPackage()}; + storage.synchronize(package); + package.types[1].exportedTypes.pop_back(); + NiceMock observerMock; + storage.addObserver(&observerMock); + + EXPECT_CALL(observerMock, + exportedTypeNamesChanged(IsEmpty(), + ElementsAre( + IsInfoExportTypeName(qmlNativeModuleId, + fetchTypeId(sourceId2, "QObject"), + Eq("QObject"), + A())))); + + storage.synchronize(std::move(package)); +} + +TEST_F(ProjectStorage, changed_export_is_notifying_changed_exported_type_names) +{ + auto package{createSimpleSynchronizationPackage()}; + storage.synchronize(package); + package.types[1].exportedTypes[1].name = "Obj2"; + NiceMock observerMock; + storage.addObserver(&observerMock); + + EXPECT_CALL(observerMock, + exportedTypeNamesChanged( + ElementsAre(IsInfoExportTypeName(qmlModuleId, + fetchTypeId(sourceId2, "QObject"), + Eq("Obj2"), + A())), + ElementsAre(IsInfoExportTypeName(qmlModuleId, + fetchTypeId(sourceId2, "QObject"), + Eq("Obj"), + A())))); + + storage.synchronize(std::move(package)); +} + TEST_F(ProjectStorage, get_unqiue_singleton_type_ids) { auto package{createSimpleSynchronizationPackage()};