From 31953e1b7eb799d687861eee21dcb352eaaf0c7d Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 21 Oct 2024 18:25:17 +0200 Subject: [PATCH] QmlDesigner: Add view management tests Cannot really test the reweiter because it expects a rewriter view. So no mock can be used. Task-number: QDS-13406 Change-Id: I0191e3681ed288322dbd339d76288fa22bf0766a Reviewed-by: Thomas Hartmann --- .../libs/designercore/model/model.cpp | 14 +- .../designercore/rewriter/rewriterview.cpp | 8 +- tests/unit/tests/mocks/abstractviewmock.h | 15 +- .../unit/tests/unittests/model/model-test.cpp | 142 ++++++++++++++++-- 4 files changed, 160 insertions(+), 19 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designercore/model/model.cpp b/src/plugins/qmldesigner/libs/designercore/model/model.cpp index b100cd678ab..8bd53b8b201 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/model.cpp @@ -928,13 +928,16 @@ void ModelPrivate::attachView(AbstractView *view) if (!view->isEnabled()) return; - if (m_viewList.contains(view)) - return; + if (view->isAttached()) { + if (view->model() == m_model) + return; + else + view->model()->detachView(view); + } m_viewList.append(view); - if (!view->isAttached()) - view->modelAttached(m_model); + view->modelAttached(m_model); } void ModelPrivate::detachView(AbstractView *view, bool notifyView) @@ -1641,6 +1644,9 @@ void ModelPrivate::setNodeInstanceView(AbstractView *nodeInstanceView) if (nodeInstanceView && nodeInstanceView->kind() != AbstractView::Kind::NodeInstance) return; + if (nodeInstanceView && nodeInstanceView->isAttached()) + nodeInstanceView->model()->setNodeInstanceView(nullptr); + if (m_nodeInstanceView) m_nodeInstanceView->modelAboutToBeDetached(m_model); diff --git a/src/plugins/qmldesigner/libs/designercore/rewriter/rewriterview.cpp b/src/plugins/qmldesigner/libs/designercore/rewriter/rewriterview.cpp index f7d4b5bc243..a1ba0848713 100644 --- a/src/plugins/qmldesigner/libs/designercore/rewriter/rewriterview.cpp +++ b/src/plugins/qmldesigner/libs/designercore/rewriter/rewriterview.cpp @@ -105,11 +105,13 @@ Internal::TextToModelMerger *RewriterView::textToModelMerger() const void RewriterView::modelAttached(Model *model) { - QTC_ASSERT(m_textModifier, return); - m_modelAttachPending = false; - AbstractView::modelAttached(model); + if (!m_textModifier) + return; + + m_modelAttachPending = false; + ModelAmender differenceHandler(m_textToModelMerger.get()); const QString qmlSource = m_textModifier->text(); if (m_textToModelMerger->load(qmlSource, differenceHandler)) diff --git a/tests/unit/tests/mocks/abstractviewmock.h b/tests/unit/tests/mocks/abstractviewmock.h index d7eab46acce..862ec56a8ce 100644 --- a/tests/unit/tests/mocks/abstractviewmock.h +++ b/tests/unit/tests/mocks/abstractviewmock.h @@ -14,7 +14,15 @@ class AbstractViewMock : public QmlDesigner::AbstractView public: AbstractViewMock(QmlDesigner::ExternalDependenciesInterface *externalDependencies = nullptr) : QmlDesigner::AbstractView{*externalDependencies} - {} + { + ON_CALL(*this, modelAttached).WillByDefault([this](QmlDesigner::Model *model) { + this->QmlDesigner::AbstractView::modelAttached(model); + }); + + ON_CALL(*this, modelAboutToBeDetached).WillByDefault([this](QmlDesigner::Model *model) { + this->QmlDesigner::AbstractView::modelAboutToBeDetached(model); + }); + } MOCK_METHOD(void, nodeOrderChanged, (const QmlDesigner::NodeListProperty &listProperty), (override)); MOCK_METHOD(void, variantPropertiesChanged, @@ -57,4 +65,9 @@ public: (override)); MOCK_METHOD(void, nodeAboutToBeRemoved, (const QmlDesigner::ModelNode &removedNode), (override)); MOCK_METHOD(void, refreshMetaInfos, (const QmlDesigner::TypeIds &), (override)); + + MOCK_METHOD(void, modelAttached, (QmlDesigner::Model *), (override)); + MOCK_METHOD(void, modelAboutToBeDetached, (QmlDesigner::Model *), (override)); + + using AbstractView::setKind; }; diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp index a374eb321c7..2a6fa2fb543 100644 --- a/tests/unit/tests/unittests/model/model-test.cpp +++ b/tests/unit/tests/unittests/model/model-test.cpp @@ -5,24 +5,27 @@ #include #include +#include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace { using QmlDesigner::AbstractProperty; +using QmlDesigner::AbstractView; using QmlDesigner::ModelNode; using QmlDesigner::ModelNodes; using QmlDesigner::ModelResourceSet; @@ -119,13 +122,13 @@ protected: NiceMock projectStorageMock{pathCacheMock.sourceId, "/path"}; NiceMock resourceManagementMock; QmlDesigner::Imports imports = {QmlDesigner::Import::createLibraryImport("QtQuick")}; + NiceMock viewMock; QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, "Item", imports, QUrl::fromLocalFile(pathCacheMock.path.toQString()), std::make_unique( resourceManagementMock)}; - NiceMock viewMock; QmlDesigner::SourceId filePathId = pathCacheMock.sourceId; QmlDesigner::ModuleId qtQuickModuleId = projectStorageMock.moduleId("QtQuick", ModuleKind::QmlLibrary); @@ -1201,4 +1204,121 @@ TEST_F(Model_TypeAnnotation, item_library_entries) ElementsAre(u"/extra/file/path")))); } +class Model_ViewManagement : public Model +{ +protected: + NiceMock viewMock; +}; + +TEST_F(Model_ViewManagement, set_rewriter) +{ + NiceMock externalDependenciesMock; + QmlDesigner::RewriterView rewriter{externalDependenciesMock}; + + model.setRewriterView(&rewriter); + + ASSERT_THAT(model.rewriterView(), Eq(&rewriter)); +} + +TEST_F(Model_ViewManagement, attach_rewriter) +{ + NiceMock externalDependenciesMock; + QmlDesigner::RewriterView rewriter{externalDependenciesMock}; + + model.attachView(&rewriter); + + ASSERT_THAT(model.rewriterView(), Eq(&rewriter)); +} + +TEST_F(Model_ViewManagement, set_node_instance_view) +{ + viewMock.setKind(AbstractView::Kind::NodeInstance); + + model.setNodeInstanceView(&viewMock); + + ASSERT_THAT(model.nodeInstanceView(), Eq(&viewMock)); +} + +TEST_F(Model_ViewManagement, call_modelAttached_if_node_instance_view_is_set) +{ + viewMock.setKind(AbstractView::Kind::NodeInstance); + + EXPECT_CALL(viewMock, modelAttached(&model)); + + model.setNodeInstanceView(&viewMock); +} + +TEST_F(Model_ViewManagement, dont_call_modelAttached_if_node_instance_view_is_already_set) +{ + viewMock.setKind(AbstractView::Kind::NodeInstance); + model.setNodeInstanceView(&viewMock); + + EXPECT_CALL(viewMock, modelAttached(&model)).Times(0); + + model.setNodeInstanceView(&viewMock); +} + +TEST_F(Model_ViewManagement, detach_node_instance_view_from_other_model_before_attach_to_new_model) +{ + InSequence s; + QmlDesigner::Model otherModel{{projectStorageMock, pathCacheMock}, + "Item", + imports, + QUrl::fromLocalFile(pathCacheMock.path.toQString()), + std::make_unique( + resourceManagementMock)}; + viewMock.setKind(AbstractView::Kind::NodeInstance); + otherModel.setNodeInstanceView(&viewMock); + + EXPECT_CALL(viewMock, modelAboutToBeDetached(&otherModel)); + EXPECT_CALL(viewMock, modelAttached(&model)); + + model.setNodeInstanceView(&viewMock); +} + +TEST_F(Model_ViewManagement, call_modelAboutToBeDetached_for_already_set_node_instance_view) +{ + NiceMock otherViewMock; + otherViewMock.setKind(AbstractView::Kind::NodeInstance); + viewMock.setKind(AbstractView::Kind::NodeInstance); + model.setNodeInstanceView(&otherViewMock); + + EXPECT_CALL(otherViewMock, modelAboutToBeDetached(&model)); + + model.setNodeInstanceView(&viewMock); +} + +TEST_F(Model_ViewManagement, attach_view_is_calling_modelAttached) +{ + EXPECT_CALL(viewMock, modelAttached(&model)); + + model.attachView(&viewMock); +} + +TEST_F(Model_ViewManagement, attach_view_is_not_calling_modelAttached_if_it_is_already_attached) +{ + model.attachView(&viewMock); + + EXPECT_CALL(viewMock, modelAttached(&model)).Times(0); + + model.attachView(&viewMock); +} + +TEST_F(Model_ViewManagement, view_is_detached_before_it_is_attached_ot_new_model) +{ + InSequence s; + QmlDesigner::Model otherModel{{projectStorageMock, pathCacheMock}, + "Item", + imports, + QUrl::fromLocalFile(pathCacheMock.path.toQString()), + std::make_unique( + resourceManagementMock)}; + otherModel.attachView(&viewMock); + + EXPECT_CALL(viewMock, modelAboutToBeDetached(&otherModel)); + EXPECT_CALL(viewMock, modelAttached(&model)); + + model.attachView(&viewMock); +} + } // namespace