File indexing completed on 2024-05-19 05:42:08
0001 // ct_lvtldr_nodestorage.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 <catch2-local-includes.h> 0021 0022 #include <ct_lvtldr_lakosiannode.h> 0023 #include <ct_lvtldr_nodestorage.h> 0024 #include <ct_lvtldr_packagenode.h> 0025 #include <ct_lvtldr_typenode.h> 0026 0027 #include <ct_lvtldr_nodestoragetestutils.h> 0028 #include <ct_lvttst_tmpdir.h> 0029 0030 using Codethink::lvtshr::DiagramType; 0031 using Codethink::lvtshr::LakosRelationType; 0032 using Codethink::lvtshr::UDTKind; 0033 using Codethink::lvtshr::UniqueId; 0034 0035 using namespace Codethink::lvtldr; 0036 0037 #include <ct_lvtmdb_objectstore.h> 0038 #include <ct_lvtmdb_packageobject.h> 0039 #include <ct_lvtmdb_soci_writer.h> 0040 #include <ct_lvtprj_projectfile.h> 0041 0042 TEST_CASE("Add entities to nodeStorage") 0043 { 0044 auto tmpDir = TmpDir{"add_entities_to_ns_test"}; 0045 auto dbPath = tmpDir.path() / "codedb.db"; 0046 0047 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0048 0049 int callcount = 0; 0050 QObject::connect(&nodeStorage, &NodeStorage::nodeAdded, [&callcount](LakosianNode *_, std::any) { // NOLINT 0051 (void) _; 0052 callcount += 1; 0053 }); 0054 0055 // Make sure we don't crash while querying an item that does not exist, returning a nullptr is ok. 0056 REQUIRE(nodeStorage.findById(UniqueId(DiagramType::PackageType, 15)) == nullptr); 0057 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::PackageType, "non-existing-name") == nullptr); 0058 0059 // Add a package on the nodeStorage. 0060 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::PackageType, "tmz") == nullptr); 0061 REQUIRE(callcount == 0); 0062 auto *tmz = nodeStorage.addPackage("tmz", "tmz", nullptr, std::any()).value(); 0063 REQUIRE(callcount == 1); 0064 REQUIRE(tmz != nullptr); 0065 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::PackageType, "tmz") == tmz); 0066 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::ComponentType, "tmz") == nullptr); 0067 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::ClassType, "tmz") == nullptr); 0068 REQUIRE(tmz->name() == "tmz"); 0069 REQUIRE(tmz->type() == DiagramType::PackageType); 0070 REQUIRE(tmz->qualifiedName() == "tmz"); 0071 REQUIRE(tmz->children().empty()); 0072 0073 // Make sure that getting from nodeStorage in different ways will return the same thing 0074 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::PackageType, "tmz") == tmz); 0075 REQUIRE(nodeStorage.findById(tmz->uid()) == tmz); 0076 0077 // Add a component on the nodeStorage. 0078 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::ComponentType, "tmz/tmzcmp") == nullptr); 0079 REQUIRE(callcount == 1); 0080 auto *tmzcmp = nodeStorage.addComponent("tmzcmp", "tmz/tmzcmp", tmz).value(); 0081 REQUIRE(callcount == 2); 0082 REQUIRE(tmzcmp != nullptr); 0083 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::PackageType, "tmz/tmzcmp") == nullptr); 0084 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::ComponentType, "tmz/tmzcmp") == tmzcmp); 0085 { 0086 // Make sure that even in a node storage with empty cache, the entity can be found (will fetch from DB) 0087 NodeStorage nodeStorage2; 0088 nodeStorage2.setDatabaseSourcePath(dbPath.string()); 0089 REQUIRE(nodeStorage2.findByQualifiedName(DiagramType::ComponentType, "tmz/tmzcmp") != nullptr); 0090 } 0091 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::ClassType, "tmz/tmzcmp") == nullptr); 0092 REQUIRE(tmzcmp->name() == "tmzcmp"); 0093 REQUIRE(tmzcmp->type() == DiagramType::ComponentType); 0094 REQUIRE(tmzcmp->qualifiedName() == "tmz/tmzcmp"); 0095 REQUIRE(tmzcmp->children().empty()); 0096 REQUIRE(tmz->children().size() == 1); 0097 REQUIRE(tmz->children()[0] == tmzcmp); 0098 0099 // Add a component on the nodeStorage. 0100 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::ComponentType, "tmzcmp::tmzcmpa") == nullptr); 0101 REQUIRE(callcount == 2); 0102 auto *tmzcmpa = [&]() { 0103 auto result = nodeStorage.addLogicalEntity("tmzcmpa", "tmzcmp::tmzcmpa", tmzcmp, UDTKind::Class); 0104 REQUIRE_FALSE(result.has_error()); 0105 return result.value(); 0106 }(); 0107 REQUIRE(callcount == 3); 0108 REQUIRE(tmzcmpa != nullptr); 0109 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::PackageType, "tmzcmp::tmzcmpa") == nullptr); 0110 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::ComponentType, "tmzcmp::tmzcmpa") == nullptr); 0111 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::ClassType, "tmzcmp::tmzcmpa") == tmzcmpa); 0112 { 0113 // Make sure that even in a node storage with empty cache, the entity can be found (will fetch from DB) 0114 NodeStorage nodeStorage2; 0115 nodeStorage2.setDatabaseSourcePath(dbPath.string()); 0116 REQUIRE(nodeStorage2.findByQualifiedName(DiagramType::ClassType, "tmzcmp::tmzcmpa") != nullptr); 0117 } 0118 REQUIRE(tmzcmpa->name() == "tmzcmpa"); 0119 REQUIRE(tmzcmpa->type() == DiagramType::ClassType); 0120 REQUIRE(tmzcmpa->qualifiedName() == "tmzcmp::tmzcmpa"); 0121 REQUIRE(tmzcmpa->children().empty()); 0122 REQUIRE(tmzcmp->children().size() == 1); 0123 REQUIRE(tmzcmp->children()[0] == tmzcmpa); 0124 0125 auto parentHierarchy = tmzcmpa->parentHierarchy(); 0126 REQUIRE(parentHierarchy.size() == 3); 0127 REQUIRE(parentHierarchy[0] == tmz); 0128 REQUIRE(parentHierarchy[1] == tmzcmp); 0129 REQUIRE(parentHierarchy[2] == tmzcmpa); 0130 0131 { 0132 // Cannot add a logical entity in a package 0133 auto result = nodeStorage.addLogicalEntity("somepkg", "somepkg", tmz, UDTKind::Class); 0134 REQUIRE(result.has_error()); 0135 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::ClassType, "somepkg") == nullptr); 0136 } 0137 0138 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::ClassType, "tmzcmp::tmzcmpa") == tmzcmpa); 0139 REQUIRE(nodeStorage.findById(tmzcmpa->uid()) == tmzcmpa); 0140 auto tmzcmpaUid = tmzcmpa->uid(); 0141 REQUIRE_FALSE(nodeStorage.removeLogicalEntity(tmzcmpa).has_error()); 0142 // Can't use tmzcmpa object from this point, since it's deleted 0143 tmzcmpa = nullptr; 0144 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::ClassType, "tmzcmp::tmzcmpa") == nullptr); 0145 REQUIRE(nodeStorage.findById(tmzcmpaUid) == nullptr); 0146 0147 auto *tmzcmp_class = 0148 nodeStorage.addLogicalEntity("tmzcmp_class", "tmzcmp::tmzcmp_class", tmzcmp, UDTKind::Class).value(); 0149 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::ClassType, "tmzcmp::tmzcmp_class") == tmzcmp_class); 0150 REQUIRE(dynamic_cast<TypeNode *>(tmzcmp_class)->kind() == UDTKind::Class); 0151 0152 auto *tmzcmp_struct = 0153 nodeStorage.addLogicalEntity("tmzcmp_struct", "tmzcmp::tmzcmp_struct", tmzcmp, UDTKind::Struct).value(); 0154 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::ClassType, "tmzcmp::tmzcmp_struct") == tmzcmp_struct); 0155 REQUIRE(dynamic_cast<TypeNode *>(tmzcmp_struct)->kind() == UDTKind::Struct); 0156 0157 auto *tmzcmp_enum = 0158 nodeStorage.addLogicalEntity("tmzcmp_enum", "tmzcmp::tmzcmp_enum", tmzcmp, UDTKind::Enum).value(); 0159 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::ClassType, "tmzcmp::tmzcmp_enum") == tmzcmp_enum); 0160 REQUIRE(dynamic_cast<TypeNode *>(tmzcmp_enum)->kind() == UDTKind::Enum); 0161 0162 // It is ok for a class to be child of another class 0163 auto *tmzcmp_class2 = 0164 nodeStorage 0165 .addLogicalEntity("tmzcmp_class2", "tmzcmp::tmzcmp_class::tmzcmp_class2", tmzcmp_class, UDTKind::Class) 0166 .value(); 0167 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::ClassType, "tmzcmp::tmzcmp_class::tmzcmp_class2") 0168 == tmzcmp_class2); 0169 REQUIRE(dynamic_cast<TypeNode *>(tmzcmp_class2)->kind() == UDTKind::Class); 0170 0171 // It is *NOT* ok for a class to be child of an enum 0172 REQUIRE(nodeStorage.addLogicalEntity("some_klass", "tmzcmp::tmzcmp_enum::some_klass", tmzcmp_enum, UDTKind::Class) 0173 .has_error()); 0174 0175 // Cannot add a component in a package group 0176 { 0177 auto *grp = nodeStorage.addPackage("grp", "grp", nullptr).value(); 0178 REQUIRE_FALSE(grp->isPackageGroup()); 0179 (void) nodeStorage.addPackage("pkg", "grp/pkg", grp); 0180 REQUIRE(grp->isPackageGroup()); 0181 auto result = nodeStorage.addComponent("component", "grp::component", grp); 0182 REQUIRE(result.has_error()); 0183 REQUIRE(result.error().kind == ErrorAddComponent::Kind::CannotAddComponentToPkgGroup); 0184 } 0185 0186 // Cannot add a package in a group that already contains a component 0187 { 0188 auto *pkg = nodeStorage.addPackage("pkg2", "pkg2", nullptr).value(); 0189 (void) nodeStorage.addComponent("component", "pkg2/component", pkg); 0190 auto result = nodeStorage.addPackage("inner", "pkg2/inner", pkg); 0191 REQUIRE(result.has_error()); 0192 REQUIRE(result.error().kind == ErrorAddPackage::Kind::CannotAddPackageToStandalonePackage); 0193 } 0194 } 0195 0196 TEST_CASE("Package groups and standalone packages interaction") 0197 { 0198 auto tmpDir = TmpDir{"pkgs_std_pkg_interact_test"}; 0199 auto dbPath = tmpDir.path() / "codedb.db"; 0200 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0201 0202 auto *grp = nodeStorage.addPackage("grp", "grp", nullptr).value(); 0203 auto *pkg = nodeStorage.addPackage("pkg", "grp/pkg", grp).value(); 0204 auto *a = nodeStorage.addComponent("a", "grp/pkg/a", pkg).value(); 0205 auto *std = nodeStorage.addPackage("std", "std", nullptr).value(); 0206 auto *b = nodeStorage.addComponent("b", "std/b", std).value(); 0207 0208 // Rule: A pkg group is allowed to depend on a standalone pkg. 0209 { 0210 auto result = nodeStorage.addPhysicalDependency(grp, std); 0211 REQUIRE_FALSE(result.has_error()); 0212 REQUIRE(grp->hasProvider(std)); 0213 } 0214 0215 // Rule: A pkg group that depends on a standalone pkg can consume any component from the standalone pkg. 0216 { 0217 auto result = nodeStorage.addPhysicalDependency(a, b); 0218 REQUIRE_FALSE(result.has_error()); 0219 } 0220 } 0221 0222 TEST_CASE("Remove Packages") 0223 { 0224 using ErrorKind = ErrorRemoveEntity::Kind; 0225 0226 { 0227 auto tmpDir = TmpDir{"remove_packages_basic"}; 0228 auto dbPath = tmpDir.path() / "codedb.db"; 0229 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0230 0231 auto *pkg1 = nodeStorage.addPackage("pkg1", "pkg1", nullptr, std::any()).value(); 0232 auto *pkg2 = nodeStorage.addPackage("pkg2", "pkg2", nullptr, std::any()).value(); 0233 0234 REQUIRE(nodeStorage.getTopLevelPackages().size() == 2); 0235 auto ret = nodeStorage.removePackage(pkg1); 0236 REQUIRE_FALSE(ret.has_error()); 0237 REQUIRE(nodeStorage.getTopLevelPackages().size() == 1); 0238 0239 ret = nodeStorage.removePackage(pkg2); 0240 REQUIRE_FALSE(ret.has_error()); 0241 REQUIRE(nodeStorage.getTopLevelPackages().empty()); 0242 } 0243 0244 // we currently don't allow removing a package that has children 0245 { 0246 auto tmpDir = TmpDir{"remove_packages_with_childs"}; 0247 auto dbPath = tmpDir.path() / "codedb.db"; 0248 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0249 0250 auto *pkg1 = nodeStorage.addPackage("pkg1", "pkg1", nullptr, std::any()).value(); 0251 auto *pkg2 = nodeStorage.addPackage("pkg2", "pkg2", pkg1, std::any()).value(); 0252 0253 // pkg1 contains pkg2, first we need to remove pkg2, to be able to remove pkg1. 0254 REQUIRE(pkg1->children().size() == 1); 0255 REQUIRE(nodeStorage.getTopLevelPackages().size() == 1); 0256 auto ret = nodeStorage.removePackage(pkg1); 0257 REQUIRE(ret.has_error()); 0258 REQUIRE(ret.error().kind == ErrorKind::CannotRemoveWithChildren); 0259 REQUIRE(nodeStorage.getTopLevelPackages().size() == 1); 0260 0261 ret = nodeStorage.removePackage(pkg2); 0262 REQUIRE_FALSE(ret.has_error()); 0263 REQUIRE(pkg1->children().empty()); 0264 0265 ret = nodeStorage.removePackage(pkg1); 0266 REQUIRE_FALSE(ret.has_error()); 0267 REQUIRE(nodeStorage.getTopLevelPackages().empty()); 0268 } 0269 0270 // we currently don't allow removing packages with dependencies 0271 { 0272 auto tmpDir = TmpDir{"remove_packages_with_deps"}; 0273 auto dbPath = tmpDir.path() / "codedb.db"; 0274 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0275 0276 auto *pkg1 = nodeStorage.addPackage("pkg1", "pkg1", nullptr).value(); 0277 auto *pkg2 = nodeStorage.addPackage("pkg2", "pkg2", nullptr).value(); 0278 nodeStorage.addPhysicalDependency(pkg1, pkg2).expect(""); 0279 0280 // pkg1 contains pkg2, first we need to remove pkg2, to be able to remove pkg1. 0281 REQUIRE(nodeStorage.getTopLevelPackages().size() == 2); 0282 auto ret1 = nodeStorage.removePackage(pkg1); 0283 REQUIRE(ret1.has_error()); 0284 REQUIRE(ret1.error().kind == ErrorKind::CannotRemoveWithProviders); 0285 REQUIRE(nodeStorage.getTopLevelPackages().size() == 2); 0286 auto ret2 = nodeStorage.removePackage(pkg2); 0287 REQUIRE(ret2.has_error()); 0288 REQUIRE(ret2.error().kind == ErrorKind::CannotRemoveWithClients); 0289 REQUIRE(nodeStorage.getTopLevelPackages().size() == 2); 0290 } 0291 } 0292 0293 TEST_CASE("Cyclic dependencies aren't allowed") 0294 { 0295 { 0296 auto tmpDir = TmpDir{"cyclic_deps_not_allowed_test_1"}; 0297 auto dbPath = tmpDir.path() / "codedb.db"; 0298 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0299 0300 auto *pkg1 = nodeStorage.addPackage("pkg1", "pkg1", nullptr, std::any()).value(); 0301 auto *pkg2 = nodeStorage.addPackage("pkg2", "pkg2", nullptr, std::any()).value(); 0302 0303 REQUIRE(pkg1->children().empty()); 0304 auto ret = pkg1->addChild(pkg2); 0305 REQUIRE(!ret.has_error()); 0306 REQUIRE(pkg1->children().size() == 1); 0307 0308 // TODO: Shouldn't allow cyclic dependency 0309 REQUIRE(pkg2->children().empty()); 0310 auto ret2 = pkg2->addChild(pkg1); 0311 REQUIRE(ret2.has_error()); 0312 REQUIRE(pkg2->children().empty()); 0313 } 0314 0315 { 0316 auto tmpDir = TmpDir{"cyclic_deps_not_allowed_test_2"}; 0317 auto dbPath = tmpDir.path() / "codedb.db"; 0318 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0319 0320 auto *pkg1 = nodeStorage.addPackage("pkg1", "pkg1", nullptr, std::any()).value(); 0321 auto *pkg2 = nodeStorage.addPackage("pkg2", "pkg2", nullptr, std::any()).value(); 0322 auto *pkg3 = nodeStorage.addPackage("pkg3", "pkg3", nullptr, std::any()).value(); 0323 0324 REQUIRE(pkg1->children().empty()); 0325 auto ret = pkg1->addChild(pkg2); 0326 REQUIRE(!ret.has_error()); 0327 REQUIRE(pkg1->children().size() == 1); 0328 0329 REQUIRE(pkg2->children().empty()); 0330 auto ret2 = pkg2->addChild(pkg3); 0331 REQUIRE(!ret2.has_error()); 0332 REQUIRE(pkg2->children().size() == 1); 0333 0334 // TODO [xxx]: Shouldn't allow cyclic dependency 0335 REQUIRE(pkg3->children().empty()); 0336 (void) pkg3->addChild(pkg1); 0337 // REQUIRE(ret3.has_error()); 0338 REQUIRE(pkg3->children().empty()); 0339 } 0340 } 0341 0342 TEST_CASE("Add dependency relationship") 0343 { 0344 using Kind = ErrorAddPhysicalDependency::Kind; 0345 0346 auto tmpDir = TmpDir{"add_dep_relation_test"}; 0347 auto dbPath = tmpDir.path() / "codedb.db"; 0348 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0349 0350 auto expectAddComponent = [&nodeStorage](const std::string& name, const std::string& qualifiedName, auto *parent) { 0351 auto result = nodeStorage.addComponent(name, qualifiedName, parent); 0352 REQUIRE_FALSE(result.has_error()); 0353 return result.value(); 0354 }; 0355 0356 auto *a = nodeStorage.addPackage("a", "a", nullptr, std::any()).value(); 0357 auto *b = nodeStorage.addPackage("b", "b", nullptr, std::any()).value(); 0358 auto *aa = nodeStorage.addPackage("aa", "aa", a, std::any()).value(); 0359 auto *ab = nodeStorage.addPackage("ab", "ab", a, std::any()).value(); 0360 auto *ba = nodeStorage.addPackage("ba", "ba", b, std::any()).value(); 0361 auto *sln = nodeStorage.addPackage("sln", "standalones/sln", nullptr, std::any()).value(); 0362 auto *aaa = expectAddComponent("aaa", "aaa", aa); 0363 auto *aab = expectAddComponent("aab", "aab", aa); 0364 auto *aba = expectAddComponent("aba", "aba", ab); 0365 auto *baa = expectAddComponent("baa", "baa", ba); 0366 0367 // Its ok to components from the same package to depend on each other 0368 REQUIRE_FALSE(aaa->hasProvider(aab)); 0369 REQUIRE_FALSE(aab->hasProvider(aaa)); 0370 auto r = nodeStorage.addPhysicalDependency(aaa, aab); 0371 REQUIRE_FALSE(r.has_error()); 0372 REQUIRE(aaa->hasProvider(aab)); 0373 REQUIRE_FALSE(aab->hasProvider(aaa)); 0374 0375 // Self-dependency is not allowed 0376 REQUIRE_FALSE(aaa->hasProvider(aaa)); 0377 r = nodeStorage.addPhysicalDependency(aaa, aaa); 0378 REQUIRE((r.has_error() && r.error().kind == Kind::SelfRelation)); 0379 REQUIRE_FALSE(aaa->hasProvider(aaa)); 0380 0381 // Component from different packages cannot depend on each other if there's no dependency between the packages... 0382 REQUIRE_FALSE(aba->hasProvider(aaa)); 0383 r = nodeStorage.addPhysicalDependency(aba, aaa); 0384 REQUIRE((r.has_error() && r.error().kind == Kind::MissingParentDependency)); 0385 REQUIRE_FALSE(aba->hasProvider(aaa)); 0386 0387 // ... but if the parents have a dependency, then it is allowed 0388 r = nodeStorage.addPhysicalDependency(ab, aa); 0389 REQUIRE_FALSE(r.has_error()); 0390 r = nodeStorage.addPhysicalDependency(aba, aaa); 0391 REQUIRE_FALSE(r.has_error()); 0392 REQUIRE(aba->hasProvider(aaa)); 0393 0394 // It is not possible for a component to depend on a package 0395 REQUIRE_FALSE(aaa->hasProvider(ab)); 0396 r = nodeStorage.addPhysicalDependency(aaa, ab); 0397 REQUIRE((r.has_error() && r.error().kind == Kind::HierarchyLevelMismatch)); 0398 REQUIRE_FALSE(aaa->hasProvider(ab)); 0399 0400 // It is not possible for a package to depend on a component 0401 REQUIRE_FALSE(ab->hasProvider(aaa)); 0402 r = nodeStorage.addPhysicalDependency(ab, aaa); 0403 REQUIRE((r.has_error() && r.error().kind == Kind::HierarchyLevelMismatch)); 0404 REQUIRE_FALSE(ab->hasProvider(aaa)); 0405 0406 // In order for two components to depend on each other, their parents must have a dependency 0407 REQUIRE_FALSE(b->hasProvider(a)); 0408 REQUIRE_FALSE(ba->hasProvider(aa)); 0409 REQUIRE_FALSE(baa->hasProvider(aaa)); 0410 0411 // Not possible because `ba` doesn't depend on `aa` 0412 r = nodeStorage.addPhysicalDependency(baa, aaa); 0413 REQUIRE((r.has_error() && r.error().kind == Kind::MissingParentDependency)); 0414 REQUIRE_FALSE(baa->hasProvider(aaa)); 0415 0416 // Not possible because `b` doesn't depend on `a` 0417 r = nodeStorage.addPhysicalDependency(ba, aa); 0418 REQUIRE((r.has_error() && r.error().kind == Kind::MissingParentDependency)); 0419 r = nodeStorage.addPhysicalDependency(baa, aaa); 0420 REQUIRE((r.has_error() && r.error().kind == Kind::MissingParentDependency)); 0421 REQUIRE_FALSE(baa->hasProvider(aaa)); 0422 0423 r = nodeStorage.addPhysicalDependency(b, a); 0424 REQUIRE_FALSE(r.has_error()); 0425 // Not possible because `ba` doesnt depend on `aa` 0426 r = nodeStorage.addPhysicalDependency(baa, aaa); 0427 REQUIRE((r.has_error() && r.error().kind == Kind::MissingParentDependency)); 0428 REQUIRE(b->hasProvider(a)); 0429 REQUIRE_FALSE(baa->hasProvider(aaa)); 0430 0431 r = nodeStorage.addPhysicalDependency(ba, aa); 0432 REQUIRE_FALSE(r.has_error()); 0433 r = nodeStorage.addPhysicalDependency(baa, aaa); 0434 REQUIRE_FALSE(r.has_error()); 0435 REQUIRE(ba->hasProvider(aa)); 0436 REQUIRE(baa->hasProvider(aaa)); 0437 0438 // Check that removing the relation is working properly 0439 REQUIRE(baa->hasProvider(aaa)); 0440 nodeStorage.removePhysicalDependency(baa, aaa).expect(""); 0441 REQUIRE_FALSE(baa->hasProvider(aaa)); 0442 r = nodeStorage.addPhysicalDependency(baa, aaa); 0443 REQUIRE(baa->hasProvider(aaa)); 0444 0445 // It must not be possible to add a physical dependency relation between logical entities 0446 auto *x = nodeStorage.addLogicalEntity("x", "x", aaa, UDTKind::Class).value(); 0447 auto *y = nodeStorage.addLogicalEntity("y", "y", aaa, UDTKind::Class).value(); 0448 r = nodeStorage.addPhysicalDependency(x, y); 0449 REQUIRE((r.has_error() && r.error().kind == Kind::InvalidType)); 0450 REQUIRE_FALSE(x->hasProvider(y)); 0451 0452 // It is ok for a package group to depend on a standalone package 0453 REQUIRE_FALSE(a->hasProvider(sln)); 0454 nodeStorage.addPhysicalDependency(a, sln).expect(""); 0455 REQUIRE(a->hasProvider(sln)); 0456 nodeStorage.removePhysicalDependency(a, sln).expect(""); 0457 REQUIRE_FALSE(a->hasProvider(sln)); 0458 0459 // It is ok for a standalone package to depend on package groups 0460 REQUIRE_FALSE(sln->hasProvider(a)); 0461 nodeStorage.addPhysicalDependency(sln, a).expect(""); 0462 REQUIRE(sln->hasProvider(a)); 0463 nodeStorage.removePhysicalDependency(sln, a).expect(""); 0464 REQUIRE_FALSE(sln->hasProvider(a)); 0465 } 0466 0467 TEST_CASE("Renamed entity") 0468 { 0469 auto tmpDir = TmpDir{"rename_entity_test"}; 0470 auto dbPath = tmpDir.path() / "codedb.db"; 0471 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0472 0473 auto *somePkg = nodeStorage.addPackage("a", "a", nullptr, std::any()).value(); 0474 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::PackageType, "a")); 0475 somePkg->setName("b"); 0476 REQUIRE_FALSE(nodeStorage.findByQualifiedName(DiagramType::PackageType, "a")); 0477 REQUIRE(nodeStorage.findByQualifiedName(DiagramType::PackageType, "b")); 0478 } 0479 0480 TEST_CASE("Connect renamed entities") 0481 { 0482 // There was a bug where renamed entities couldn't be connected (physical dependency). 0483 // This test has been added to avoid regression. 0484 auto tmpDir = TmpDir{"connect_renamed_ent_test"}; 0485 auto dbPath = tmpDir.path() / "codedb.db"; 0486 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0487 0488 auto *a = nodeStorage.addPackage("a", "a", nullptr, std::any()).value(); 0489 auto *b = nodeStorage.addPackage("b", "b", nullptr, std::any()).value(); 0490 REQUIRE_FALSE(a->hasProvider(b)); 0491 a->setName("c"); 0492 auto r = nodeStorage.addPhysicalDependency(a, b); 0493 REQUIRE_FALSE(r.has_error()); 0494 REQUIRE(a->hasProvider(b)); 0495 } 0496 0497 TEST_CASE("Wrong entity parenting") 0498 { 0499 // There was a bug where renamed entities was wrongly creating ghost children packages 0500 // This test has been added to avoid regression. 0501 auto tmpDir = TmpDir{"wrong_ent_parent_test"}; 0502 auto dbPath = tmpDir.path() / "codedb.db"; 0503 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0504 0505 auto *a = nodeStorage.addPackage("a", "a", nullptr, std::any()).value(); 0506 a->setName("c"); 0507 auto *aa = nodeStorage.addPackage("aa", "aa", a, std::any()).value(); 0508 (void) aa; 0509 0510 REQUIRE(nodeStorage.getTopLevelPackages().size() == 1); 0511 } 0512 0513 TEST_CASE("Dependency relationship crash") 0514 { 0515 // There was a bug where relationship between components raised a crash in this specific situation 0516 // This test has been added to avoid regression. 0517 auto tmpDir = TmpDir{"dep_rel_test"}; 0518 auto dbPath = tmpDir.path() / "codedb.db"; 0519 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0520 0521 auto *pkg = nodeStorage.addPackage("pkg", "pkg", nullptr, std::any()).value(); 0522 auto *a = nodeStorage.addComponent("a", "a", pkg).value(); 0523 auto *b = nodeStorage.addComponent("b", "b", pkg).value(); 0524 0525 { 0526 REQUIRE_FALSE(a->hasProvider(b)); 0527 auto r = nodeStorage.addPhysicalDependency(a, b); 0528 REQUIRE_FALSE(r.has_error()); 0529 REQUIRE(a->hasProvider(b)); 0530 } 0531 0532 { 0533 REQUIRE(a->hasProvider(b)); 0534 nodeStorage.removePhysicalDependency(a, b).expect(""); 0535 REQUIRE_FALSE(a->hasProvider(b)); 0536 } 0537 0538 { 0539 REQUIRE_FALSE(a->hasProvider(b)); 0540 auto r = nodeStorage.addPhysicalDependency(a, b); 0541 REQUIRE_FALSE(r.has_error()); 0542 REQUIRE(a->hasProvider(b)); 0543 } 0544 } 0545 0546 TEST_CASE("User defined types") 0547 { 0548 auto tmpDir = TmpDir{"udt_test"}; 0549 auto dbPath = tmpDir.path() / "codedb.db"; 0550 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0551 0552 auto *zoo = nodeStorage.addPackage("zoo", "zoo", nullptr).value(); 0553 0554 auto *animal = nodeStorage.addComponent("animal", "animal", zoo).value(); 0555 auto *domesticAnimalInterface = dynamic_cast<TypeNode *>( 0556 nodeStorage.addLogicalEntity("IDomesticAnimal", "cmp::IDomesticAnimal", animal, UDTKind::Class).value()); 0557 auto *catClass = 0558 dynamic_cast<TypeNode *>(nodeStorage.addLogicalEntity("Cat", "animal::Cat", animal, UDTKind::Class).value()); 0559 auto *dogClass = 0560 dynamic_cast<TypeNode *>(nodeStorage.addLogicalEntity("Dog", "animal::Dog", animal, UDTKind::Class).value()); 0561 0562 REQUIRE_FALSE(catClass->hasProvider(domesticAnimalInterface)); 0563 REQUIRE_FALSE(dogClass->hasProvider(domesticAnimalInterface)); 0564 nodeStorage.addLogicalRelation(catClass, domesticAnimalInterface, LakosRelationType::IsA) 0565 .expect("Unexpected error"); 0566 nodeStorage.addLogicalRelation(dogClass, domesticAnimalInterface, LakosRelationType::IsA) 0567 .expect("Unexpected error"); 0568 REQUIRE(catClass->hasProvider(domesticAnimalInterface)); 0569 REQUIRE(dogClass->hasProvider(domesticAnimalInterface)); 0570 0571 auto *animalBuilder = nodeStorage.addComponent("animal_builder", "animal_builder", zoo).value(); 0572 auto *animalBuilderClass = dynamic_cast<TypeNode *>( 0573 nodeStorage.addLogicalEntity("AnimalBuilder", "animal_builder::AnimalBuilder", animalBuilder, UDTKind::Class) 0574 .value()); 0575 { 0576 // Cannot add dependency between classes on different components if the components aren't depending on each 0577 // other. 0578 REQUIRE_FALSE(animalBuilderClass->hasProvider(catClass)); 0579 auto r = 0580 nodeStorage.addLogicalRelation(animalBuilderClass, catClass, LakosRelationType::UsesInTheImplementation); 0581 REQUIRE(r.has_error()); 0582 REQUIRE(r.error().kind == ErrorAddLogicalRelation::Kind::ComponentDependencyRequired); 0583 REQUIRE_FALSE(animalBuilderClass->hasProvider(catClass)); 0584 } 0585 nodeStorage.addPhysicalDependency(animalBuilder, animal).expect("Unexpected error"); 0586 REQUIRE_FALSE(animalBuilderClass->hasProvider(catClass)); 0587 REQUIRE_FALSE(animalBuilderClass->hasProvider(dogClass)); 0588 nodeStorage.addLogicalRelation(animalBuilderClass, catClass, LakosRelationType::UsesInTheImplementation) 0589 .expect("Unexpected error"); 0590 nodeStorage.addLogicalRelation(animalBuilderClass, dogClass, LakosRelationType::UsesInTheImplementation) 0591 .expect("Unexpected error"); 0592 REQUIRE(animalBuilderClass->hasProvider(catClass)); 0593 REQUIRE(animalBuilderClass->hasProvider(dogClass)); 0594 { 0595 // Cannot add a dependency twice 0596 auto r = 0597 nodeStorage.addLogicalRelation(animalBuilderClass, catClass, LakosRelationType::UsesInTheImplementation); 0598 REQUIRE(r.has_error()); 0599 REQUIRE(r.error().kind == ErrorAddLogicalRelation::Kind::AlreadyHaveDependency); 0600 } 0601 0602 { 0603 // Cannot add a dependency from a UDT to itself 0604 auto r = nodeStorage.addLogicalRelation(animalBuilderClass, animalBuilderClass, LakosRelationType::IsA); 0605 REQUIRE(r.has_error()); 0606 REQUIRE(r.error().kind == ErrorAddLogicalRelation::Kind::SelfRelation); 0607 REQUIRE_FALSE(animalBuilderClass->hasProvider(animalBuilderClass)); 0608 } 0609 { 0610 // Invalid relationship types must generate an error 0611 auto r = nodeStorage.addLogicalRelation(dogClass, animalBuilderClass, LakosRelationType::None); 0612 REQUIRE(r.has_error()); 0613 REQUIRE(r.error().kind == ErrorAddLogicalRelation::Kind::InvalidLakosRelationType); 0614 REQUIRE_FALSE(dogClass->hasProvider(animalBuilderClass)); 0615 } 0616 0617 REQUIRE(catClass->hasProvider(domesticAnimalInterface)); 0618 nodeStorage.removeLogicalRelation(catClass, domesticAnimalInterface, LakosRelationType::IsA) 0619 .expect("Unexpected error"); 0620 REQUIRE_FALSE(catClass->hasProvider(domesticAnimalInterface)); 0621 0622 { 0623 // Cannot remove a relation that doesn't exist (There is an "Is A" relation between dog and domesticAnimal, but 0624 // there is no "UsesInTheImplementation" dependency between dog and domesticAnimal) 0625 REQUIRE(dogClass->hasProvider(domesticAnimalInterface)); 0626 auto r = nodeStorage.removeLogicalRelation(dogClass, 0627 domesticAnimalInterface, 0628 LakosRelationType::UsesInTheImplementation); 0629 REQUIRE(r.has_error()); 0630 REQUIRE(r.error().kind == ErrorRemoveLogicalRelation::Kind::InexistentRelation); 0631 REQUIRE(dogClass->hasProvider(domesticAnimalInterface)); 0632 } 0633 } 0634 0635 TEST_CASE("Reparent entity on NodeStorage") 0636 { 0637 auto tmpDir = TmpDir{"reparent_entity_test"}; 0638 auto dbPath = tmpDir.path() / "codedb.db"; 0639 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0640 0641 auto *a = nodeStorage.addPackage("a", "a").value(); 0642 auto *b = nodeStorage.addPackage("b", "b").value(); 0643 auto *aa = nodeStorage.addComponent("aa", "aa", a).value(); 0644 (void) nodeStorage.addLogicalEntity("some_class", "some_class", aa, Codethink::lvtshr::UDTKind::Class).value(); 0645 0646 REQUIRE(aa->parent() == a); 0647 REQUIRE(a->children().size() == 1); 0648 REQUIRE(a->children()[0] == aa); 0649 REQUIRE(b->children().empty()); 0650 0651 nodeStorage.reparentEntity(aa, b).expect("."); 0652 0653 REQUIRE(aa->parent() == b); 0654 REQUIRE(b->children().size() == 1); 0655 REQUIRE(b->children()[0] == aa); 0656 REQUIRE(a->children().empty()); 0657 } 0658 0659 TEST_CASE("Check Lakosian nodes") 0660 { 0661 auto tmpDir = TmpDir{"ns_lakosian_node_test"}; 0662 auto dbPath = tmpDir.path() / "codedb.db"; 0663 0664 { 0665 auto nodeStorage = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0666 0667 // High level package 0668 auto *aaa = nodeStorage.addPackage("aaa", "aaa").value(); 0669 REQUIRE(aaa->isLakosian() == LakosianNode::IsLakosianResult::IsLakosian); 0670 auto *b = nodeStorage.addPackage("b", "b").value(); 0671 REQUIRE_FALSE(b->isLakosian() == LakosianNode::IsLakosianResult::IsLakosian); 0672 0673 // Package 0674 auto *aaaxxx = nodeStorage.addPackage("aaaxxx", "aaa/aaaxxx", aaa).value(); 0675 REQUIRE(aaaxxx->isLakosian() == LakosianNode::IsLakosianResult::IsLakosian); 0676 auto *aaalele = nodeStorage.addPackage("aaalele", "aaa/aaalele", aaa).value(); 0677 REQUIRE_FALSE(aaalele->isLakosian() == LakosianNode::IsLakosianResult::IsLakosian); 0678 auto *aaa_with_std = nodeStorage.addPackage("aaa+std", "aaa_std", aaa).value(); 0679 REQUIRE(aaa_with_std->isLakosian() == LakosianNode::IsLakosianResult::IsLakosian); 0680 0681 // Component 0682 auto *aaaxxx_bla = nodeStorage.addComponent("aaaxxx_bla", "aaaxxx/aaaxxx_bla", aaaxxx).value(); 0683 REQUIRE(aaaxxx_bla->isLakosian() == LakosianNode::IsLakosianResult::IsLakosian); 0684 auto *aaaxxxnonlak = nodeStorage.addComponent("aaaxxxnonlak", "aaaxxx/aaaxxxnonlak", aaaxxx).value(); 0685 REQUIRE_FALSE(aaaxxxnonlak->isLakosian() == LakosianNode::IsLakosianResult::IsLakosian); 0686 0687 // UDT 0688 auto *klass = 0689 nodeStorage.addLogicalEntity("Klass", "aaaxxx::aaaxxx_bla::Klass", aaaxxx_bla, UDTKind::Class).value(); 0690 REQUIRE(klass->isLakosian() == LakosianNode::IsLakosianResult::IsLakosian); 0691 } 0692 0693 { 0694 NodeStorage nodeStorage; 0695 nodeStorage.setDatabaseSourcePath(dbPath.string()); 0696 0697 // Requesting an invalid component must not crash 0698 auto *invalid = nodeStorage.findByQualifiedName(DiagramType::ComponentType, "invalid"); 0699 REQUIRE(invalid == nullptr); 0700 0701 auto *aaaxxx_bla = nodeStorage.findByQualifiedName(DiagramType::ComponentType, "aaaxxx/aaaxxx_bla"); 0702 REQUIRE(aaaxxx_bla); 0703 REQUIRE(aaaxxx_bla->qualifiedName() == "aaaxxx/aaaxxx_bla"); 0704 REQUIRE(aaaxxx_bla->name() == "aaaxxx_bla"); 0705 } 0706 } 0707 0708 TEST_CASE("Test Project Database Loads Successfully") 0709 { 0710 Codethink::lvtprj::ProjectFile project; 0711 0712 auto err = project.createEmpty(); 0713 REQUIRE_FALSE(err.has_error()); 0714 0715 Codethink::lvtmdb::ObjectStore store; 0716 store.withRWLock([&] { 0717 auto *pkg = store.getOrAddPackage("ble", "ble", "ble", nullptr, nullptr); 0718 REQUIRE(pkg); 0719 }); 0720 0721 const auto code_file = project.codeDatabasePath(); 0722 Codethink::lvtmdb::SociWriter writer; 0723 writer.createOrOpen(code_file.string(), "codebase_db.sql"); 0724 store.writeToDatabase(writer); 0725 0726 auto res = project.resetCadDatabaseFromCodeDatabase(); 0727 REQUIRE_FALSE(res.has_error()); 0728 0729 auto dbPath = project.cadDatabasePath(); 0730 0731 NodeStorage storage; 0732 storage.setDatabaseSourcePath(project.cadDatabasePath().string()); 0733 REQUIRE_FALSE(storage.getTopLevelPackages().empty()); 0734 0735 // Test - setNotes used to crash when a database for cad 0736 // is created based on a code database. 0737 auto node = storage.getTopLevelPackages()[0]; 0738 node->setNotes("ABC"); 0739 }