File indexing completed on 2024-05-19 05:42:18
0001 // ct_lvtqtc_graphicsview.t.cpp -*-C++-*- 0002 0003 /* 0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk> 0005 // SPDX-License-Identifier: Apache-2.0 0006 // 0007 // Licensed under the Apache License, Version 2.0 (the "License"); 0008 // you may not use this file except in compliance with the License. 0009 // You may obtain a copy of the License at 0010 // 0011 // http://www.apache.org/licenses/LICENSE-2.0 0012 // 0013 // Unless required by applicable law or agreed to in writing, software 0014 // distributed under the License is distributed on an "AS IS" BASIS, 0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 0016 // See the License for the specific language governing permissions and 0017 // limitations under the License. 0018 */ 0019 0020 #include <ct_lvtqtc_graphicsview.h> 0021 0022 #include <ct_lvtclr_colormanagement.h> 0023 #include <ct_lvtqtc_graphicsscene.h> 0024 #include <ct_lvtqtc_lakosentity.h> 0025 #include <ct_lvtqtc_testing_utils.h> 0026 #include <ct_lvttst_fixture_qt.h> 0027 0028 #include <ct_lvtldr_nodestorage.h> 0029 #include <ct_lvtldr_typenode.h> 0030 #include <ct_lvtshr_graphenums.h> 0031 0032 #include <QApplication> 0033 0034 #include <catch2-local-includes.h> 0035 #include <ct_lvtldr_nodestoragetestutils.h> 0036 #include <ct_lvttst_tmpdir.h> 0037 0038 using namespace Codethink::lvtqtc; 0039 using namespace Codethink::lvtclr; 0040 using namespace Codethink::lvtprj; 0041 using namespace Codethink::lvtldr; 0042 0043 using UDTKind = Codethink::lvtshr::UDTKind; 0044 0045 static auto const projectFileForTesting = ProjectFileForTesting{}; 0046 0047 TEST_CASE_METHOD(QTApplicationFixture, "Basic vertex addition test") 0048 { 0049 auto tmpDir = TmpDir{"basic_vertex_test"}; 0050 auto dbPath = tmpDir.path() / "codedb.db"; 0051 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0052 0053 auto *packageNode = nodeStorage.addPackage("some package", "some package qn").value(); 0054 auto *componentNode = nodeStorage.addComponent("some component", "some component qn", packageNode).value(); 0055 auto *classNode = 0056 nodeStorage.addLogicalEntity("some class", "some class qn", componentNode, UDTKind::Class).value(); 0057 0058 GraphicsView view{nodeStorage, projectFileForTesting}; 0059 auto c = std::make_shared<ColorManagement>(); 0060 view.setColorManagement(c); 0061 view.resize(400, 300); 0062 auto *scene = dynamic_cast<GraphicsScene *>(view.scene()); 0063 0064 // We have the Text saying "Drop Elements" now. 0065 auto previous_size = view.items().size(); 0066 REQUIRE(previous_size == 1); 0067 0068 auto *some_class = scene->addUdtVertex(classNode, false, nullptr, Codethink::lvtshr::LoaderInfo{}); 0069 scene->addItem(some_class); 0070 REQUIRE(view.items().size() > previous_size); 0071 0072 previous_size = view.items().size(); 0073 auto *some_component = scene->addCompVertex(componentNode, false, nullptr, Codethink::lvtshr::LoaderInfo{}); 0074 scene->addItem(some_component); 0075 REQUIRE(view.items().size() > previous_size); 0076 0077 previous_size = view.items().size(); 0078 auto *some_package = scene->addPkgVertex(packageNode, false, nullptr, Codethink::lvtshr::LoaderInfo{}); 0079 scene->addItem(some_package); 0080 REQUIRE(view.items().size() > previous_size); 0081 0082 // Select one entity as "being viewed" 0083 // scene->setMainEntity(some_class); 0084 0085 // All items are initially positioned on origin ... 0086 REQUIRE(some_class->pos() == QPointF{0, 0}); 0087 REQUIRE(some_component->pos() == QPointF{0, 0}); 0088 REQUIRE(some_package->pos() == QPointF{0, 0}); 0089 // REQUIRE(some_manual_cluster->pos() == QPointF{0, 0}); 0090 0091 some_class->setPos(QPointF{1.2, 3.4}); 0092 some_component->setPos(QPointF{2.2, 5.4}); 0093 some_package->setPos(QPointF{3.2, 7.4}); 0094 auto class_pos_prev = some_class->pos(); 0095 auto component_pos_prev = some_component->pos(); 0096 auto package_pos_prev = some_package->pos(); 0097 scene->runLayoutAlgorithm(); 0098 view.show(); 0099 processEvents(); 0100 0101 // ... but after running the layout, positions must change 0102 REQUIRE(some_class->pos() != class_pos_prev); 0103 REQUIRE(some_component->pos() != component_pos_prev); 0104 REQUIRE(some_package->pos() != package_pos_prev); 0105 // REQUIRE(some_manual_cluster->pos() != package_pos_prev); 0106 } 0107 0108 TEST_CASE_METHOD(QTApplicationFixture, "Vertex and edges test") 0109 { 0110 auto tmpDir = TmpDir{"vertex_edges_test"}; 0111 auto dbPath = tmpDir.path() / "codedb.db"; 0112 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0113 0114 GraphicsView view{nodeStorage, projectFileForTesting}; 0115 auto c = std::make_shared<ColorManagement>(); 0116 view.setColorManagement(c); 0117 view.resize(400, 300); 0118 auto *scene = dynamic_cast<GraphicsScene *>(view.scene()); 0119 0120 auto *packageNode = nodeStorage.addPackage("some package", "some package qn").value(); 0121 auto *componentNode = nodeStorage.addComponent("some component", "some component qn", packageNode).value(); 0122 auto create_class = [&](std::string const& name) { 0123 auto *udtNode = nodeStorage.addLogicalEntity(name, "_" + name, componentNode, UDTKind::Class).value(); 0124 auto *item = scene->addUdtVertex(udtNode, false, nullptr, Codethink::lvtshr::LoaderInfo{}); 0125 scene->addItem(item); 0126 return item; 0127 }; 0128 0129 auto create_edge = [&](auto *a, auto *b) { 0130 auto item = scene->addIsARelation(a, b); 0131 scene->addItem(item); 0132 return item; 0133 }; 0134 0135 auto *class_a = create_class("A"); 0136 auto *class_b = create_class("B"); 0137 auto *class_c = create_class("C"); 0138 0139 auto *connect_a_b = create_edge(class_a, class_b); 0140 auto *connect_b_c = create_edge(class_b, class_c); 0141 0142 // Select one entity as "being viewed" 0143 // scene->setMainEntity(class_a); 0144 0145 REQUIRE(connect_a_b->from() == class_a); 0146 REQUIRE(connect_a_b->to() == class_b); 0147 REQUIRE(connect_b_c->from() == class_b); 0148 REQUIRE(connect_b_c->to() == class_c); 0149 } 0150 0151 TEST_CASE_METHOD(QTApplicationFixture, "Clear graphics view when nodeStorage is wiped out") 0152 { 0153 auto tmpDir = TmpDir{"clear_gv_on_ns_empty"}; 0154 auto dbPath = tmpDir.path() / "codedb.db"; 0155 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0156 0157 GraphicsView view{nodeStorage, projectFileForTesting}; 0158 GraphicsView view_second{nodeStorage, projectFileForTesting}; 0159 0160 view.show(); 0161 view_second.show(); 0162 0163 auto c = std::make_shared<ColorManagement>(); 0164 view.setColorManagement(c); 0165 view_second.setColorManagement(c); 0166 0167 // Empty views have the text "Drop items to view" 0168 REQUIRE(view.items().size() == 1); 0169 REQUIRE(view_second.items().size() == 1); 0170 REQUIRE(view.scene() != view_second.scene()); 0171 0172 auto *scn = qobject_cast<Codethink::lvtqtc::GraphicsScene *>(view.scene()); 0173 auto result = nodeStorage.addPackage("somepkg", "somepkg", nullptr, scn); 0174 0175 REQUIRE(view_second.items().size() == 1); 0176 REQUIRE_FALSE(result.has_error()); 0177 REQUIRE(!view.items().empty()); 0178 nodeStorage.clear(); 0179 REQUIRE(view.items().size() == 1); 0180 } 0181 0182 TEST_CASE_METHOD(QTApplicationFixture, "Component on renamed package") 0183 { 0184 // There was a bug in which renaming a package would cause it's components to be placed outside of it. This test 0185 // has been created to avoid regression. 0186 auto tmpDir = TmpDir{"component_on_renamed_pkg_test"}; 0187 auto dbPath = tmpDir.path() / "codedb.db"; 0188 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0189 GraphicsView view{nodeStorage, projectFileForTesting}; 0190 auto c = std::make_shared<ColorManagement>(); 0191 view.setColorManagement(c); 0192 auto *scene = dynamic_cast<GraphicsScene *>(view.scene()); 0193 0194 auto *pkg = nodeStorage.addPackage("pkg", "pkg", nullptr, view.scene()).value(); 0195 auto *pkgView = scene->findLakosEntityFromUid(pkg->uid()); 0196 REQUIRE(pkgView); 0197 REQUIRE(pkgView->lakosEntities().empty()); 0198 pkg->setName("abc"); 0199 auto *component = nodeStorage.addComponent("component", "abc/component", pkg).value(); 0200 auto *componentView = scene->findLakosEntityFromUid(component->uid()); 0201 REQUIRE(componentView); 0202 0203 // Before the issue was solved, the following assert was failing: 0204 REQUIRE(pkgView->lakosEntities().size() == 1); 0205 } 0206 0207 TEST_CASE_METHOD(QTApplicationFixture, "Multiple views") 0208 { 0209 auto tmpDir = TmpDir{"multiple_views_test"}; 0210 auto dbPath = tmpDir.path() / "codedb.db"; 0211 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0212 0213 auto c = std::make_shared<ColorManagement>(); 0214 GraphicsView view1{nodeStorage, projectFileForTesting}; 0215 view1.setColorManagement(c); 0216 auto *scene1 = dynamic_cast<GraphicsScene *>(view1.scene()); 0217 GraphicsView view2{nodeStorage, projectFileForTesting}; 0218 view2.setColorManagement(c); 0219 auto *scene2 = dynamic_cast<GraphicsScene *>(view2.scene()); 0220 0221 auto *pkg1 = nodeStorage.addPackage("pkg1", "pkg1", nullptr, scene1).value(); 0222 auto *pkg2 = nodeStorage.addPackage("pkg2", "pkg2", nullptr, scene1).value(); 0223 0224 REQUIRE(scene1->findLakosEntityFromUid(pkg2->uid())); 0225 REQUIRE_FALSE(scene2->findLakosEntityFromUid(pkg2->uid())); 0226 scene2->loadEntityByQualifiedName(QString::fromStdString(pkg2->qualifiedName()), {100, 100}); 0227 REQUIRE(scene1->findLakosEntityFromUid(pkg2->uid())); 0228 REQUIRE(scene2->findLakosEntityFromUid(pkg2->uid())); 0229 0230 // Adding and removing dependencies shouldn't crash when dealing with multiple views 0231 REQUIRE_FALSE(nodeStorage.addPhysicalDependency(pkg1, pkg2).has_error()); 0232 nodeStorage.removePhysicalDependency(pkg1, pkg2).expect(""); 0233 } 0234 0235 TEST_CASE_METHOD(QTApplicationFixture, "Load Class Test") 0236 { 0237 auto tmpDir = TmpDir{"load_class_test"}; 0238 auto dbPath = tmpDir.path() / "codedb.db"; 0239 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0240 0241 auto *mainPackage = nodeStorage.addPackage("somepkg", "somepkg").value(); 0242 auto *mainComponent = nodeStorage.addComponent("somecomp", "somecomp", mainPackage).value(); 0243 auto *class1 = nodeStorage.addLogicalEntity("class1", "class1", mainComponent, UDTKind::Class).value(); 0244 auto *class2 = nodeStorage.addLogicalEntity("class2", "class2", mainComponent, UDTKind::Class).value(); 0245 auto *innerClass1 = nodeStorage.addLogicalEntity("innerclass1", "innerclass1", class1, UDTKind::Class).value(); 0246 REQUIRE(innerClass1); 0247 0248 auto relation = nodeStorage.addLogicalRelation(dynamic_cast<Codethink::lvtldr::TypeNode *>(class1), 0249 dynamic_cast<Codethink::lvtldr::TypeNode *>(class2), 0250 Codethink::lvtshr::LakosRelationType::IsA); 0251 0252 REQUIRE(relation); 0253 0254 GraphicsView view{nodeStorage, projectFileForTesting}; 0255 auto c = std::make_shared<ColorManagement>(); 0256 view.setColorManagement(c); 0257 view.show(); 0258 0259 auto *scene = dynamic_cast<GraphicsScene *>(view.scene()); 0260 0261 scene->loadEntityByQualifiedName(QString::fromStdString(innerClass1->qualifiedName()), QPoint(10, 10)); 0262 0263 REQUIRE(scene->entityByQualifiedName("class1")); 0264 REQUIRE_FALSE(scene->entityByQualifiedName("class2")); 0265 0266 REQUIRE(scene->entityByQualifiedName("somepkg")); 0267 REQUIRE(scene->entityByQualifiedName("somecomp")); 0268 REQUIRE(scene->entityByQualifiedName("innerclass1")); 0269 }