File indexing completed on 2024-05-19 05:42:07
0001 // ct_lvtldr_lakosiannode.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_lvtldr_componentnode.h> 0021 #include <ct_lvtldr_lakosiannode.h> 0022 #include <ct_lvtldr_nodestorage.h> 0023 0024 #include <ct_lvtmdb_soci_writer.h> 0025 0026 #include <ct_lvtclp_cpp_tool.h> 0027 #include <ct_lvtclp_testutil.h> 0028 0029 #include <ct_lvtshr_graphenums.h> 0030 #include <ct_lvtshr_uniqueid.h> 0031 0032 #include <ct_lvttst_tmpdir.h> 0033 0034 #include <filesystem> 0035 #include <iostream> 0036 0037 #include <catch2-local-includes.h> 0038 0039 const PyDefaultGilReleasedContext defaultGilContextForTesting; 0040 0041 namespace { 0042 0043 using namespace Codethink; 0044 using namespace Codethink::lvtclp; 0045 using namespace Codethink::lvtldr; 0046 using namespace Codethink::lvtshr; 0047 0048 void createTop(const std::filesystem::path& topLevel) 0049 { 0050 std::filesystem::create_directories(topLevel / "groups/one/onetop"); 0051 0052 Test_Util::createFile(topLevel / "groups/one/onetop/onetop_top.h", R"( 0053 // onetop_top.h 0054 0055 namespace onetop { 0056 0057 struct Top { 0058 using NestedType = int; 0059 0060 static NestedType method(); 0061 }; 0062 0063 })"); 0064 Test_Util::createFile(topLevel / "groups/one/onetop/onetop_top.cpp", R"( 0065 // onetop_top.cpp 0066 0067 #include <onetop_top.h> 0068 0069 namespace onetop { 0070 0071 Top::NestedType Top::method() 0072 { 0073 return 2; 0074 } 0075 0076 })"); 0077 } 0078 0079 void createDep(const std::filesystem::path& topLevel) 0080 { 0081 std::filesystem::create_directories(topLevel / "groups/two/twodep"); 0082 0083 Test_Util::createFile(topLevel / "groups/two/twodep/twodep_dep.h", R"( 0084 // twodep_dep.h 0085 0086 #include <onetop_top.h> 0087 0088 namespace twodep { 0089 0090 class Dep { 0091 public: 0092 onetop::Top::NestedType method(); 0093 }; 0094 0095 class IsADep : public Dep { 0096 }; 0097 0098 })"); 0099 Test_Util::createFile(topLevel / "groups/two/twodep/twodep_dep.cpp", R"( 0100 // twodep_dep.cpp 0101 0102 #include <twodep_dep.h> 0103 0104 namespace twodep { 0105 0106 int Dep::method() 0107 { 0108 return onetop::Top::method(); 0109 } 0110 0111 })"); 0112 } 0113 0114 void createTestEnv(const std::filesystem::path& topLevel) 0115 { 0116 if (std::filesystem::exists(topLevel)) { 0117 REQUIRE(std::filesystem::remove_all(topLevel)); 0118 } 0119 0120 createTop(topLevel); 0121 createDep(topLevel); 0122 } 0123 0124 void checkPackageGroups(NodeStorage& store) 0125 { 0126 LakosianNode *oneNode = store.findByQualifiedName(DiagramType::PackageType, "groups/one"); 0127 REQUIRE(oneNode); 0128 LakosianNode *twoNode = store.findByQualifiedName(DiagramType::PackageType, "groups/two"); 0129 REQUIRE(twoNode); 0130 0131 // package group qualified name 0132 REQUIRE(oneNode->qualifiedName() == "groups/one"); 0133 REQUIRE(twoNode->qualifiedName() == "groups/two"); 0134 0135 // package group type 0136 REQUIRE(oneNode->type() == lvtshr::DiagramType::PackageType); 0137 REQUIRE(twoNode->type() == lvtshr::DiagramType::PackageType); 0138 0139 // package group forward dependencies: two -> one 0140 const std::vector<LakosianEdge> expectedProviders{ 0141 LakosianEdge(lvtshr::PackageDependency, oneNode), 0142 }; 0143 REQUIRE(oneNode->providers().empty()); 0144 REQUIRE(twoNode->providers() == expectedProviders); 0145 0146 // package group reverse dependencies: one <- two 0147 const std::vector<LakosianEdge> expectedClients{ 0148 LakosianEdge(lvtshr::PackageDependency, twoNode), 0149 }; 0150 REQUIRE(oneNode->clients() == expectedClients); 0151 REQUIRE(twoNode->clients().empty()); 0152 0153 // package group parents should be null 0154 REQUIRE(!oneNode->parent()); 0155 REQUIRE(!twoNode->parent()); 0156 0157 // package group children 0158 LakosianNode *onetopNode = store.findByQualifiedName(DiagramType::PackageType, "groups/one/onetop"); 0159 REQUIRE(onetopNode); 0160 LakosianNode *twodepNode = store.findByQualifiedName(DiagramType::PackageType, "groups/two/twodep"); 0161 REQUIRE(twodepNode); 0162 0163 const std::vector<LakosianNode *> oneChildren{ 0164 onetopNode, 0165 }; 0166 REQUIRE(oneNode->children() == oneChildren); 0167 0168 const std::vector<LakosianNode *> twoChildren{ 0169 twodepNode, 0170 }; 0171 REQUIRE(twoNode->children() == twoChildren); 0172 } 0173 0174 void checkPackages(NodeStorage& store) 0175 { 0176 LakosianNode *onetopNode = store.findByQualifiedName(DiagramType::PackageType, "groups/one/onetop"); 0177 REQUIRE(onetopNode); 0178 LakosianNode *twodepNode = store.findByQualifiedName(DiagramType::PackageType, "groups/two/twodep"); 0179 REQUIRE(twodepNode); 0180 0181 // pacakge qualified name 0182 REQUIRE(onetopNode->qualifiedName() == "groups/one/onetop"); 0183 REQUIRE(twodepNode->qualifiedName() == "groups/two/twodep"); 0184 0185 // package type 0186 REQUIRE(onetopNode->type() == lvtshr::DiagramType::PackageType); 0187 REQUIRE(twodepNode->type() == lvtshr::DiagramType::PackageType); 0188 0189 // package forward dependencies: twodep -> onetop 0190 const std::vector<LakosianEdge> expectedproviders{ 0191 LakosianEdge(lvtshr::PackageDependency, onetopNode), 0192 }; 0193 REQUIRE(onetopNode->providers().empty()); 0194 REQUIRE(twodepNode->providers() == expectedproviders); 0195 0196 // package reverse dependencies: onetop <- twodep 0197 const std::vector<LakosianEdge> expectedclients{ 0198 LakosianEdge(lvtshr::PackageDependency, twodepNode), 0199 }; 0200 REQUIRE(onetopNode->clients() == expectedclients); 0201 REQUIRE(twodepNode->clients().empty()); 0202 0203 // package parents 0204 LakosianNode *oneNode = store.findByQualifiedName(DiagramType::PackageType, "groups/one"); 0205 REQUIRE(oneNode); 0206 LakosianNode *twoNode = store.findByQualifiedName(DiagramType::PackageType, "groups/two"); 0207 REQUIRE(twoNode); 0208 0209 REQUIRE(onetopNode->parent() == oneNode); 0210 REQUIRE(twodepNode->parent() == twoNode); 0211 0212 // package children 0213 LakosianNode *onetopCompNode = 0214 store.findByQualifiedName(DiagramType::ComponentType, "groups/one/onetop/onetop_top"); 0215 REQUIRE(onetopCompNode); 0216 LakosianNode *twodepCompNode = 0217 store.findByQualifiedName(DiagramType::ComponentType, "groups/two/twodep/twodep_dep"); 0218 REQUIRE(twodepCompNode); 0219 0220 const std::vector<LakosianNode *> onetopChildren{ 0221 onetopCompNode, 0222 }; 0223 REQUIRE(onetopNode->children() == onetopChildren); 0224 0225 const std::vector<LakosianNode *> twodepChildren{ 0226 twodepCompNode, 0227 }; 0228 REQUIRE(twodepNode->children() == twodepChildren); 0229 } 0230 0231 void checkComponents(NodeStorage& store) 0232 { 0233 LakosianNode *onetopCompNode = 0234 store.findByQualifiedName(DiagramType::ComponentType, "groups/one/onetop/onetop_top"); 0235 REQUIRE(onetopCompNode); 0236 LakosianNode *twodepCompNode = 0237 store.findByQualifiedName(DiagramType::ComponentType, "groups/two/twodep/twodep_dep"); 0238 REQUIRE(twodepCompNode); 0239 0240 // component qualified name 0241 REQUIRE(onetopCompNode->qualifiedName() == "groups/one/onetop/onetop_top"); 0242 REQUIRE(twodepCompNode->qualifiedName() == "groups/two/twodep/twodep_dep"); 0243 0244 // component type 0245 REQUIRE(onetopCompNode->type() == lvtshr::DiagramType::ComponentType); 0246 REQUIRE(twodepCompNode->type() == lvtshr::DiagramType::ComponentType); 0247 0248 // component forward dependencies twodep_dep -> onetop_top 0249 const std::vector<LakosianEdge> expectedproviders{ 0250 LakosianEdge(lvtshr::PackageDependency, onetopCompNode), 0251 }; 0252 REQUIRE(onetopCompNode->providers().empty()); 0253 REQUIRE(twodepCompNode->providers() == expectedproviders); 0254 0255 // component reverse dependencies onetop_top <- twodep_dep 0256 const std::vector<LakosianEdge> expectedclients{ 0257 LakosianEdge(lvtshr::PackageDependency, twodepCompNode), 0258 }; 0259 REQUIRE(onetopCompNode->clients() == expectedclients); 0260 REQUIRE(twodepCompNode->clients().empty()); 0261 0262 // component parents 0263 LakosianNode *onetopNode = store.findByQualifiedName(DiagramType::PackageType, "groups/one/onetop"); 0264 REQUIRE(onetopNode); 0265 LakosianNode *twodepNode = store.findByQualifiedName(DiagramType::PackageType, "groups/two/twodep"); 0266 REQUIRE(twodepNode); 0267 0268 REQUIRE(onetopCompNode->parent() == onetopNode); 0269 REQUIRE(twodepCompNode->parent() == twodepNode); 0270 0271 // component children 0272 LakosianNode *topNode = store.findByQualifiedName(DiagramType::ClassType, "onetop::Top"); 0273 REQUIRE(topNode); 0274 LakosianNode *depNode = store.findByQualifiedName(DiagramType::ClassType, "twodep::Dep"); 0275 REQUIRE(depNode); 0276 LakosianNode *isADepNode = store.findByQualifiedName(DiagramType::ClassType, "twodep::IsADep"); 0277 REQUIRE(isADepNode); 0278 0279 const std::vector<LakosianNode *> onetopChildren{ 0280 topNode, 0281 }; 0282 REQUIRE(onetopCompNode->children() == onetopChildren); 0283 0284 const std::vector<LakosianNode *> twodepChildren{ 0285 isADepNode, 0286 depNode, 0287 }; 0288 REQUIRE(twodepCompNode->children() == twodepChildren); 0289 } 0290 0291 void checkUDTs(NodeStorage& store) 0292 { 0293 LakosianNode *topNode = store.findByQualifiedName(DiagramType::ClassType, "onetop::Top"); 0294 REQUIRE(topNode); 0295 LakosianNode *nestedNode = store.findByQualifiedName(DiagramType::ClassType, "onetop::Top::NestedType"); 0296 REQUIRE(nestedNode); 0297 LakosianNode *depNode = store.findByQualifiedName(DiagramType::ClassType, "twodep::Dep"); 0298 REQUIRE(depNode); 0299 LakosianNode *isADepNode = store.findByQualifiedName(DiagramType::ClassType, "twodep::IsADep"); 0300 REQUIRE(isADepNode); 0301 0302 // type qualifiedName 0303 REQUIRE(topNode->qualifiedName() == "onetop::Top"); 0304 REQUIRE(nestedNode->qualifiedName() == "onetop::Top::NestedType"); 0305 REQUIRE(depNode->qualifiedName() == "twodep::Dep"); 0306 REQUIRE(isADepNode->qualifiedName() == "twodep::IsADep"); 0307 0308 // udt type 0309 REQUIRE(topNode->type() == lvtshr::DiagramType::ClassType); 0310 REQUIRE(nestedNode->type() == lvtshr::DiagramType::ClassType); 0311 REQUIRE(depNode->type() == lvtshr::DiagramType::ClassType); 0312 REQUIRE(isADepNode->type() == lvtshr::DiagramType::ClassType); 0313 0314 // udt fwd deps: twodep::Dep uses-in-impl onetop::Top 0315 // TODO: test isA and usesInTheInterface 0316 const std::vector<LakosianEdge> expectedproviders{ 0317 LakosianEdge(lvtshr::UsesInTheInterface, nestedNode), 0318 LakosianEdge(lvtshr::UsesInTheImplementation, topNode), 0319 }; 0320 const std::vector<LakosianEdge> isAproviders{ 0321 LakosianEdge(lvtshr::IsA, depNode), 0322 }; 0323 0324 for (auto&& p : topNode->providers()) { 0325 std::cout << p.type() << "\n"; 0326 std::cout << p.other()->qualifiedName() << "\n"; 0327 } 0328 0329 REQUIRE(topNode->providers().empty()); 0330 REQUIRE(nestedNode->providers().empty()); 0331 REQUIRE(depNode->providers() == expectedproviders); 0332 REQUIRE(isADepNode->providers() == isAproviders); 0333 0334 // udt rev deps: twodep::Dbo uses-in-impl onetop::Top 0335 const std::vector<LakosianEdge> expectedclients{ 0336 LakosianEdge(lvtshr::UsesInTheImplementation, depNode), 0337 }; 0338 const std::vector<LakosianEdge> nestedclients{ 0339 LakosianEdge(lvtshr::UsesInTheInterface, depNode), 0340 }; 0341 const std::vector<LakosianEdge> isAclients{ 0342 LakosianEdge(lvtshr::IsA, isADepNode), 0343 }; 0344 REQUIRE(topNode->clients() == expectedclients); 0345 REQUIRE(nestedNode->clients() == nestedclients); 0346 REQUIRE(depNode->clients() == isAclients); 0347 REQUIRE(isADepNode->clients().empty()); 0348 // udt parents 0349 LakosianNode *onetopCompNode = 0350 store.findByQualifiedName(DiagramType::ComponentType, "groups/one/onetop/onetop_top"); 0351 REQUIRE(onetopCompNode); 0352 LakosianNode *twodepCompNode = 0353 store.findByQualifiedName(DiagramType::ComponentType, "groups/two/twodep/twodep_dep"); 0354 REQUIRE(twodepCompNode); 0355 0356 REQUIRE(topNode->parent() == onetopCompNode); 0357 REQUIRE(nestedNode->parent() == topNode); 0358 REQUIRE(depNode->parent() == twodepCompNode); 0359 REQUIRE(isADepNode->parent() == twodepCompNode); 0360 0361 // udt children 0362 const std::vector<LakosianNode *> topChildren{ 0363 nestedNode, 0364 }; 0365 REQUIRE(topNode->children() == topChildren); 0366 REQUIRE(nestedNode->children().empty()); 0367 REQUIRE(depNode->children().empty()); 0368 REQUIRE(isADepNode->children().empty()); 0369 } 0370 0371 struct LakosianNodeTestFixture { 0372 LakosianNodeTestFixture(): topLevel(std::filesystem::temp_directory_path() / "ct_lvtldr_lakosiannode_test") 0373 { 0374 createTestEnv(topLevel); 0375 } 0376 0377 ~LakosianNodeTestFixture() 0378 { 0379 REQUIRE(std::filesystem::remove_all(topLevel)); 0380 } 0381 0382 std::filesystem::path topLevel; 0383 }; 0384 } // namespace 0385 0386 TEST_CASE_METHOD(LakosianNodeTestFixture, "Lakosian nodes test") 0387 { 0388 StaticCompilationDatabase cmds({{"groups/one/onetop/onetop_top.cpp", "build/onetop_top.o"}, 0389 {"groups/two/twodep/twodep_dep.cpp", "build/onedep_dep.o"}}, 0390 "placeholder", 0391 {"-Igroups/one/onetop", "-Igroups/two/twodep"}, 0392 topLevel); 0393 0394 auto tmpDir = TmpDir{"lakosian_nodes_test"}; 0395 auto dbPath = tmpDir.path() / "codedb.db"; 0396 CppTool tool(topLevel, cmds, dbPath); 0397 REQUIRE(tool.runFull()); 0398 0399 lvtmdb::SociWriter writer; 0400 writer.createOrOpen(dbPath.string(), "cad_db.sql"); 0401 tool.getObjectStore().writeToDatabase(writer); 0402 0403 NodeStorage store; 0404 store.setDatabaseSourcePath(dbPath.string()); 0405 0406 checkPackageGroups(store); 0407 checkPackages(store); 0408 checkComponents(store); 0409 checkUDTs(store); 0410 } 0411 0412 TEST_CASE_METHOD(LakosianNodeTestFixture, "find entities") 0413 { 0414 StaticCompilationDatabase cmds({{"groups/one/onetop/onetop_top.cpp", "build/onetop_top.o"}, 0415 {"groups/two/twodep/twodep_dep.cpp", "build/onedep_dep.o"}}, 0416 "placeholder", 0417 {"-Igroups/one/onetop", "-Igroups/two/twodep"}, 0418 topLevel); 0419 0420 auto tmpDir = TmpDir{"find_entities_test"}; 0421 auto dbPath = tmpDir.path() / "codedb.db"; 0422 CppTool tool(topLevel, cmds, dbPath); 0423 REQUIRE(tool.runFull()); 0424 0425 lvtmdb::SociWriter writer; 0426 writer.createOrOpen(dbPath.string(), "cad_db.sql"); 0427 tool.getObjectStore().writeToDatabase(writer); 0428 0429 NodeStorage store; 0430 store.setDatabaseSourcePath(dbPath.string()); 0431 0432 auto *some_class = store.findById(UniqueId{DiagramType::ClassType, 1}); 0433 auto *some_component = store.findById(UniqueId{DiagramType::ComponentType, 1}); 0434 auto *some_package = store.findById(UniqueId{DiagramType::PackageType, 1}); 0435 0436 REQUIRE(some_class != nullptr); 0437 REQUIRE(some_component != nullptr); 0438 REQUIRE(some_package != nullptr); 0439 0440 // Even though they have the same record number, different diagram types will yield return data 0441 REQUIRE(some_class != some_component); 0442 REQUIRE(some_class != some_package); 0443 0444 // Getting the same object twice will return the same pointer to it 0445 REQUIRE(some_class == store.findById(UniqueId{DiagramType::ClassType, 1})); 0446 REQUIRE(some_component == store.findById(UniqueId{DiagramType::ComponentType, 1})); 0447 REQUIRE(some_package == store.findById(UniqueId{DiagramType::PackageType, 1})); 0448 0449 // Getting them from findByQualifiedName will return the same pointer 0450 REQUIRE(some_class == store.findByQualifiedName(some_class->type(), some_class->qualifiedName())); 0451 REQUIRE(some_component == store.findByQualifiedName(some_component->type(), some_component->qualifiedName())); 0452 REQUIRE(some_package == store.findByQualifiedName(some_package->type(), some_package->qualifiedName())); 0453 } 0454 0455 TEST_CASE_METHOD(LakosianNodeTestFixture, "retrieve child structures") 0456 { 0457 StaticCompilationDatabase cmds({{"groups/one/onetop/onetop_top.cpp", "build/onetop_top.o"}, 0458 {"groups/two/twodep/twodep_dep.cpp", "build/onedep_dep.o"}}, 0459 "placeholder", 0460 {"-Igroups/one/onetop", "-Igroups/two/twodep"}, 0461 topLevel); 0462 auto tmpDir = TmpDir{"find_entities_test"}; 0463 auto dbPath = tmpDir.path() / "codedb.db"; 0464 CppTool tool(topLevel, cmds, dbPath); 0465 REQUIRE(tool.runFull()); 0466 0467 lvtmdb::SociWriter writer; 0468 writer.createOrOpen(dbPath.string(), "cad_db.sql"); 0469 tool.getObjectStore().writeToDatabase(writer); 0470 0471 NodeStorage store; 0472 store.setDatabaseSourcePath(dbPath.string()); 0473 0474 { 0475 auto pkgGroups = store.getTopLevelPackages(); 0476 REQUIRE(pkgGroups.size() == 2); 0477 // Retrieving from `findById` returns the _same pointer_ as the one in the package group 0478 REQUIRE(pkgGroups[0] == store.findById({DiagramType::PackageType, pkgGroups[0]->id()})); 0479 } 0480 0481 { 0482 auto pkgGroups = store.getTopLevelPackages(); 0483 auto packages = pkgGroups[0]->children(); 0484 auto components = packages[0]->children(); 0485 auto *someComponent = dynamic_cast<ComponentNode *>(components[0]); 0486 REQUIRE(someComponent != nullptr); 0487 } 0488 } 0489 0490 TEST_CASE_METHOD(LakosianNodeTestFixture, "parent hierarchy") 0491 { 0492 StaticCompilationDatabase cmds({{"groups/one/onetop/onetop_top.cpp", "build/onetop_top.o"}, 0493 {"groups/two/twodep/twodep_dep.cpp", "build/onedep_dep.o"}}, 0494 "placeholder", 0495 {"-Igroups/one/onetop", "-Igroups/two/twodep"}, 0496 topLevel); 0497 0498 auto tmpDir = TmpDir{"parent_hierarchy_test"}; 0499 auto dbPath = tmpDir.path() / "codedb.db"; 0500 CppTool tool(topLevel, cmds, dbPath); 0501 REQUIRE(tool.runFull()); 0502 0503 lvtmdb::SociWriter writer; 0504 writer.createOrOpen(dbPath.string(), "cad_db.sql"); 0505 tool.getObjectStore().writeToDatabase(writer); 0506 0507 NodeStorage ns; 0508 ns.setDatabaseSourcePath(dbPath.string()); 0509 0510 auto rootPackages = ns.getTopLevelPackages(); 0511 REQUIRE(rootPackages.size() == 2); 0512 0513 auto *rootPkgGrp = [&]() -> LakosianNode * { 0514 for (auto&& r : rootPackages) { 0515 if (r->qualifiedName() == "groups/one") { 0516 return r; 0517 } 0518 } 0519 return nullptr; 0520 }(); 0521 auto childPackages = rootPkgGrp->children(); 0522 REQUIRE(childPackages.size() == 1); 0523 0524 auto packageQualifiedName = childPackages[0]->qualifiedName(); 0525 REQUIRE(packageQualifiedName == "groups/one/onetop"); 0526 auto components = childPackages[0]->children(); 0527 REQUIRE(components.size() == 1); 0528 0529 auto *childComponent = components[0]; 0530 auto componentQualifiedName = childComponent->qualifiedName(); 0531 REQUIRE(componentQualifiedName == "groups/one/onetop/onetop_top"); 0532 0533 auto hierarchy = childComponent->parentHierarchy(); 0534 REQUIRE(hierarchy[0]->qualifiedName() == "groups/one"); 0535 REQUIRE(hierarchy[1]->qualifiedName() == "groups/one/onetop"); 0536 REQUIRE(hierarchy[2]->qualifiedName() == "groups/one/onetop/onetop_top"); 0537 } 0538 0539 TEST_CASE_METHOD(LakosianNodeTestFixture, "changing node storage") 0540 { 0541 StaticCompilationDatabase cmds({{"groups/one/onetop/onetop_top.cpp", "build/onetop_top.o"}, 0542 {"groups/two/twodep/twodep_dep.cpp", "build/onedep_dep.o"}}, 0543 "placeholder", 0544 {"-Igroups/one/onetop", "-Igroups/two/twodep"}, 0545 topLevel); 0546 0547 auto tmpDir = TmpDir{"changing_node_storage_test"}; 0548 auto dbPath = tmpDir.path() / "codedb.db"; 0549 CppTool tool(topLevel, cmds, dbPath); 0550 REQUIRE(tool.runFull()); 0551 0552 lvtmdb::SociWriter writer; 0553 writer.createOrOpen(dbPath.string(), "cad_db.sql"); 0554 tool.getObjectStore().writeToDatabase(writer); 0555 0556 NodeStorage ns; 0557 ns.setDatabaseSourcePath(dbPath.string()); 0558 0559 int n_changes = 0; 0560 QObject::connect(&ns, &NodeStorage::nodeNameChanged, [&n_changes](const LakosianNode *node) { 0561 REQUIRE(node != nullptr); 0562 n_changes += 1; 0563 }); 0564 0565 auto rootPackages = ns.getTopLevelPackages(); 0566 REQUIRE(rootPackages.size() == 2); 0567 0568 auto *rootPkgGrp = [&]() -> LakosianNode * { 0569 for (auto&& r : rootPackages) { 0570 if (r->qualifiedName() == "groups/one") { 0571 return r; 0572 } 0573 } 0574 return nullptr; 0575 }(); 0576 auto childPackages = rootPkgGrp->children(); 0577 REQUIRE(childPackages.size() == 1); 0578 0579 auto packageQualifiedName = childPackages[0]->qualifiedName(); 0580 REQUIRE(packageQualifiedName == "groups/one/onetop"); 0581 auto components = childPackages[0]->children(); 0582 REQUIRE(components.size() == 1); 0583 0584 auto componentQualifiedName = components[0]->qualifiedName(); 0585 REQUIRE(componentQualifiedName == "groups/one/onetop/onetop_top"); 0586 0587 auto changeNode = [&](auto *node) { 0588 auto oldName = node->name(); 0589 auto oldQualifiedName = node->qualifiedName(); 0590 0591 node->setName("othername"); 0592 0593 REQUIRE(node->name() != oldName); 0594 REQUIRE(node->qualifiedName() != oldQualifiedName); 0595 }; 0596 0597 REQUIRE(n_changes == 0); 0598 changeNode(rootPackages[0]); 0599 REQUIRE(n_changes == 1); 0600 changeNode(childPackages[0]); 0601 REQUIRE(n_changes == 2); 0602 changeNode(components[0]); 0603 REQUIRE(n_changes == 3); 0604 } 0605 0606 TEST_CASE("qualified name building") 0607 { 0608 { 0609 auto prefixParts = NamingUtils::buildQualifiedNamePrefixParts("xxx::yyyy::zz", "::"); 0610 REQUIRE(prefixParts == std::vector<std::string>{"xxx", "yyyy"}); 0611 REQUIRE(NamingUtils::buildQualifiedName(prefixParts, "other", "::") == "xxx::yyyy::other"); 0612 REQUIRE(NamingUtils::buildQualifiedName(prefixParts, "other", "/") == "xxx/yyyy/other"); 0613 } 0614 0615 { 0616 auto prefixParts = NamingUtils::buildQualifiedNamePrefixParts("pkg/comp/udt", "/"); 0617 REQUIRE(prefixParts == std::vector<std::string>{"pkg", "comp"}); 0618 REQUIRE(NamingUtils::buildQualifiedName(prefixParts, "klass", "::") == "pkg::comp::klass"); 0619 REQUIRE(NamingUtils::buildQualifiedName(prefixParts, "klass", "/") == "pkg/comp/klass"); 0620 } 0621 0622 // Edge case: There's no prefix 0623 { 0624 auto prefixParts = NamingUtils::buildQualifiedNamePrefixParts("myKlass", "::"); 0625 REQUIRE(prefixParts.empty()); 0626 REQUIRE(NamingUtils::buildQualifiedName(prefixParts, "otherKlass", "::") == "otherKlass"); 0627 } 0628 0629 // Edge case: Empty string prefix 0630 { 0631 auto prefixParts = NamingUtils::buildQualifiedNamePrefixParts("::myKlass", "::"); 0632 REQUIRE(prefixParts == std::vector<std::string>{""}); 0633 REQUIRE(NamingUtils::buildQualifiedName(prefixParts, "otherKlass", "::") == "::otherKlass"); 0634 } 0635 0636 // Edge case: There's no string at all! 0637 { 0638 auto prefixParts = NamingUtils::buildQualifiedNamePrefixParts("", "::"); 0639 REQUIRE(prefixParts.empty()); 0640 REQUIRE(NamingUtils::buildQualifiedName(prefixParts, "bla", "::") == "bla"); 0641 } 0642 }