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 }