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 }