File indexing completed on 2024-05-19 05:42:04

0001 // ct_lvtclp_testphysicalandtemplates.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_lvtmdb_componentobject.h>
0021 #include <ct_lvtmdb_fileobject.h>
0022 #include <ct_lvtmdb_objectstore.h>
0023 #include <ct_lvtmdb_packageobject.h>
0024 #include <ct_lvtmdb_typeobject.h>
0025 
0026 #include <ct_lvtclp_cpp_tool.h>
0027 #include <ct_lvtclp_testutil.h>
0028 
0029 #include <filesystem>
0030 
0031 #include <catch2-local-includes.h>
0032 #include <clang/Basic/Version.h>
0033 
0034 using namespace Codethink::lvtmdb;
0035 using namespace Codethink::lvtclp;
0036 
0037 const PyDefaultGilReleasedContext defaultGilContextForTesting;
0038 
0039 void createTestEnv(const std::filesystem::path& topLevel)
0040 {
0041     if (std::filesystem::exists(topLevel)) {
0042         REQUIRE(std::filesystem::remove_all(topLevel));
0043     }
0044 
0045     REQUIRE(std::filesystem::create_directories(topLevel / "groups/bsl/bslma"));
0046 
0047     REQUIRE(Test_Util::createFile(topLevel / "groups/bsl/bslma/bslma_allocatortraits.h", R"(
0048 // bslma_allocatortraits.h
0049 
0050 namespace bslma {
0051 
0052 template <class ALLOCATOR_TYPE>
0053 struct AllocatorTraits_SomeTrait {
0054     static const bool value = false;
0055 };
0056 
0057 })"));
0058     REQUIRE(Test_Util::createFile(topLevel / "groups/bsl/bslma/bslma_allocatortraits.cpp", R"(
0059 // bslma_allocatortraits.cpp
0060 #include <bslma_allocatortraits.h>)"));
0061 
0062     REQUIRE(std::filesystem::create_directories(topLevel / "groups/foo/foobar"));
0063 
0064     REQUIRE(Test_Util::createFile(topLevel / "groups/foo/foobar/foobar_someclass.h", R"(
0065 // foobar_someclass.h
0066 
0067 namespace foobar {
0068 
0069 class SomeClass {
0070 };
0071 
0072 })"));
0073     REQUIRE(Test_Util::createFile(topLevel / "groups/foo/foobar/foobar_someclass.cpp", R"(
0074 // foobar_someclass.cpp
0075 #include <foobar_someclass.h>)"));
0076 
0077     REQUIRE(Test_Util::createFile(topLevel / "groups/foo/foobar/foobar_vector.h", R"(
0078 // foobar_vector.h
0079 
0080 namespace foobar {
0081 
0082 template <class TYPE>
0083 class Vector {
0084     // ...
0085 };
0086 
0087 }
0088 )"));
0089     REQUIRE(Test_Util::createFile(topLevel / "groups/foo/foobar/foobar_vector.cpp", R"(
0090 // foobar_vector.cpp
0091 #include <foobar_vector.h>)"));
0092 
0093     REQUIRE(Test_Util::createFile(topLevel / "groups/foo/foobar/foobar_somecomponent.h", R"(
0094 // foobar_somecomponent.h
0095 
0096 #include <bslma_allocatortraits.h>
0097 
0098 namespace foobar {
0099 
0100 class C {
0101     void method();
0102 };
0103 
0104 }
0105 
0106 namespace bslma {
0107 
0108 template<>
0109 struct AllocatorTraits_SomeTrait<foobar::C> {
0110     static const bool value = true;
0111 };
0112 
0113 })"));
0114     REQUIRE(Test_Util::createFile(topLevel / "groups/foo/foobar/foobar_somecomponent.cpp", R"(
0115 // foobar_somecomponent.cpp
0116 #include <foobar_somecomponent.h>
0117 
0118 #include <foobar_someclass.h>
0119 #include <foobar_vector.h>
0120 
0121 namespace foobar {
0122 
0123 void C::method()
0124 {
0125     Vector<SomeClass> v;
0126 }
0127 
0128 })"));
0129 }
0130 
0131 void testFilesExist(ObjectStore& session)
0132 {
0133     auto files = {"groups/bsl/bslma/bslma_allocatortraits.h",
0134                   "groups/bsl/bslma/bslma_allocatortraits.cpp",
0135                   "groups/foo/foobar/foobar_someclass.h",
0136                   "groups/foo/foobar/foobar_someclass.cpp",
0137                   "groups/foo/foobar/foobar_vector.h",
0138                   "groups/foo/foobar/foobar_vector.cpp",
0139                   "groups/foo/foobar/foobar_somecomponent.h",
0140                   "groups/foo/foobar/foobar_somecomponent.cpp"};
0141 
0142     session.withROLock([&] {
0143         for (auto const& file : files) {
0144             REQUIRE(session.getFile(file));
0145         }
0146     });
0147 }
0148 
0149 void testPackagesExist(ObjectStore& session)
0150 {
0151     auto packages = {"groups/bsl", "groups/bsl/bslma", "groups/foo", "groups/foo/foobar"};
0152     session.withROLock([&] {
0153         for (auto const& pkg : packages) {
0154             REQUIRE(session.getPackage(pkg));
0155         }
0156     });
0157 }
0158 
0159 bool pkgGrpHasOneChild(PackageObject *grp, PackageObject *const child)
0160 {
0161     bool ret = false;
0162     grp->withROLock([&] {
0163         const auto& children = grp->children();
0164         if (children.size() != 1) {
0165             ret = false;
0166         } else if (children.front() != child) {
0167             ret = false;
0168         } else {
0169             ret = true;
0170         }
0171     });
0172     return ret;
0173 }
0174 
0175 struct ModelComponent {
0176     std::string name;
0177     std::string qualifiedName;
0178     std::string parent;
0179     std::string fileBase;
0180     std::vector<std::string> classes;
0181     std::vector<std::string> dependencies;
0182 };
0183 
0184 void checkComponent(ComponentObject *real, const ModelComponent& model)
0185 {
0186     std::vector<TypeObject *> childClasses;
0187     std::vector<ComponentObject *> deps;
0188     std::vector<FileObject *> files;
0189 
0190     real->withROLock([&] {
0191         childClasses = real->types();
0192         deps = real->forwardDependencies();
0193         files = real->files();
0194 
0195         CHECK(real->name() == model.name);
0196         CHECK(real->qualifiedName() == model.qualifiedName);
0197         CHECK(real->types().size() == model.classes.size());
0198         auto lock = real->package()->readOnlyLock();
0199         CHECK(real->package()->qualifiedName() == model.parent);
0200     });
0201 
0202     // we should have a file called model.fileBase + ".h" and another called
0203     // model.fileBase + ".cpp" and no others
0204     CHECK(files.size() == 2);
0205     auto it = std::find_if(files.begin(), files.end(), [&](FileObject *file) {
0206         auto lock = file->readOnlyLock();
0207         return file->qualifiedName() == (model.fileBase + ".h");
0208     });
0209     CHECK(it != files.end());
0210     it = std::find_if(files.begin(), files.end(), [&](FileObject *file) {
0211         auto lock = file->readOnlyLock();
0212         return file->qualifiedName() == (model.fileBase + ".cpp");
0213     });
0214     CHECK(it != files.end());
0215 
0216     CHECK(std::equal(childClasses.begin(),
0217                      childClasses.end(),
0218                      model.classes.begin(),
0219                      [](TypeObject *ptr, const std::string& qname) {
0220                          auto lock = ptr->readOnlyLock();
0221                          return ptr->qualifiedName() == qname;
0222                      }));
0223 
0224     // test forward dependencies
0225     CHECK(deps.size() == model.dependencies.size());
0226     CHECK(std::equal(deps.begin(),
0227                      deps.end(),
0228                      model.dependencies.begin(),
0229                      [](ComponentObject *rel, const std::string& qname) {
0230                          auto lock = rel->readOnlyLock();
0231                          return rel->qualifiedName() == qname;
0232                      }));
0233 }
0234 
0235 void checkPkgComponents(PackageObject *pkg, std::initializer_list<ModelComponent> expected)
0236 {
0237     std::vector<ComponentObject *> children;
0238     pkg->withROLock([&] {
0239         children = pkg->components();
0240     });
0241     REQUIRE(children.size() == expected.size());
0242 
0243     for (const ModelComponent& model : expected) {
0244         // find matching component by qualifiedName
0245         auto it = std::find_if(children.begin(), children.end(), [&model](ComponentObject *c) {
0246             auto lock = c->readOnlyLock();
0247             return model.qualifiedName == c->qualifiedName();
0248         });
0249         CHECK(it != children.end());
0250         checkComponent(*it, model);
0251     }
0252 }
0253 
0254 void checkUdtInPkg(const std::string& udtName, PackageObject *pkg, ObjectStore& session)
0255 {
0256     TypeObject *udt = nullptr;
0257     session.withROLock([&] {
0258         udt = session.getType(udtName);
0259     });
0260 
0261     REQUIRE(udt);
0262     udt->withROLock([&] {
0263         REQUIRE(udt->package() == pkg);
0264     });
0265 
0266     pkg->withROLock([&] {
0267         std::vector<TypeObject *> udts = pkg->types();
0268         auto it = std::find(udts.begin(), udts.end(), udt);
0269         REQUIRE(it != udts.end());
0270     });
0271 }
0272 
0273 struct PhysicalAndTemplatesFixture {
0274     PhysicalAndTemplatesFixture():
0275         topLevel(std::filesystem::temp_directory_path() / "ct_lvtclp_testphysicalandtemplates_test")
0276     {
0277         createTestEnv(topLevel);
0278     }
0279 
0280     ~PhysicalAndTemplatesFixture()
0281     {
0282         REQUIRE(std::filesystem::remove_all(topLevel));
0283     }
0284 
0285     std::filesystem::path topLevel;
0286 };
0287 
0288 TEST_CASE_METHOD(PhysicalAndTemplatesFixture, "Physical and Templates")
0289 {
0290     StaticCompilationDatabase cmds({{"groups/bsl/bslma/bslma_allocatortraits.cpp", "build/bslma_allocatortriats.o"},
0291                                     {"groups/foo/foobar/foobar_someclass.cpp", "build/foobar_someclass.o"},
0292                                     {"groups/foo/foobar/foobar_vector.cpp", "build/foobar_vector.o"},
0293                                     {"groups/foo/foobar/foobar_somecomponent.cpp", "build/foobar_somecomponent.o"}},
0294                                    "placeholder-g++",
0295                                    {"-Igroups/bsl/bslma", "-Igroups/foo/foobar"},
0296                                    topLevel);
0297 
0298     CppTool tool(topLevel, cmds, ":memory:");
0299     REQUIRE(tool.runFull());
0300     ObjectStore& session = tool.getObjectStore();
0301     testFilesExist(session);
0302     testPackagesExist(session);
0303 
0304     // we test that files have things in them accross the other unit tests
0305     // lets focus on components and packages here
0306 
0307     PackageObject *bsl = nullptr;
0308     PackageObject *bslma = nullptr;
0309     PackageObject *foo = nullptr;
0310     PackageObject *foobar = nullptr;
0311 
0312     session.withROLock([&] {
0313         bsl = session.getPackage("groups/bsl");
0314         bslma = session.getPackage("groups/bsl/bslma");
0315         foo = session.getPackage("groups/foo");
0316         foobar = session.getPackage("groups/foo/foobar");
0317     });
0318 
0319     REQUIRE(bsl);
0320     REQUIRE(bslma);
0321     REQUIRE(foo);
0322     REQUIRE(foobar);
0323 
0324     // bslma is in bsl
0325     CHECK(pkgGrpHasOneChild(bsl, bslma));
0326     // foobar is in foo
0327     CHECK(pkgGrpHasOneChild(foo, foobar));
0328     // bslma has no deps
0329     bslma->withROLock([&] {
0330         CHECK(bslma->forwardDependencies().empty());
0331 
0332         // bslma has reverse dep on foobar
0333         const auto bslmaRevDeps = bslma->reverseDependencies();
0334         REQUIRE(bslmaRevDeps.size() == 1);
0335         CHECK(bslmaRevDeps.front() == foobar);
0336     });
0337 
0338     foobar->withROLock([&] {
0339         // foobar depends on bslma
0340         const auto foobarDeps = foobar->forwardDependencies();
0341         REQUIRE(foobarDeps.size() == 1);
0342         CHECK(foobarDeps.front() == bslma);
0343         // foobar has no reverse dependencies
0344         CHECK(foobar->reverseDependencies().empty());
0345     });
0346 
0347     // check the packages have classes inside of them
0348     checkUdtInPkg("bslma::AllocatorTraits_SomeTrait", bslma, session);
0349     checkUdtInPkg("foobar::SomeClass", foobar, session);
0350     checkUdtInPkg("foobar::Vector", foobar, session);
0351     checkUdtInPkg("foobar::C", foobar, session);
0352 
0353     // check bslma compnoents
0354     checkPkgComponents(bslma,
0355                        {{"bslma_allocatortraits",
0356                          "groups/bsl/bslma/bslma_allocatortraits",
0357                          "groups/bsl/bslma",
0358                          "groups/bsl/bslma/bslma_allocatortraits",
0359                          {"bslma::AllocatorTraits_SomeTrait"},
0360                          {}}});
0361 
0362     // check foobar components
0363     checkPkgComponents(foobar,
0364                        {{"foobar_someclass",
0365                          "groups/foo/foobar/foobar_someclass",
0366                          "groups/foo/foobar",
0367                          "groups/foo/foobar/foobar_someclass",
0368                          {"foobar::SomeClass"},
0369                          {}},
0370                         {"foobar_vector",
0371                          "groups/foo/foobar/foobar_vector",
0372                          "groups/foo/foobar",
0373                          "groups/foo/foobar/foobar_vector",
0374                          {"foobar::Vector"},
0375                          {}},
0376                         {
0377                             "foobar_somecomponent",
0378                             "groups/foo/foobar/foobar_somecomponent",
0379                             "groups/foo/foobar",
0380                             "groups/foo/foobar/foobar_somecomponent",
0381                             // TODO - this is not supposed to add - bslma::AllocatorTraits_SomeTrait<foobar::C>, so we
0382                             // need to check logicaldepvisitor. Perhaps we need to add a new rule that inner namespaces
0383                             // that have names that matches packages are out of scope.
0384                             {"foobar::C", "bslma::AllocatorTraits_SomeTrait"},
0385                             {"groups/bsl/bslma/bslma_allocatortraits",
0386                              "groups/foo/foobar/foobar_someclass",
0387                              "groups/foo/foobar/foobar_vector"},
0388                         }});
0389 
0390     // C usesInImpl on Vector and SomeClass,
0391     // no relationship between AllocatorTraits_SomeTrait and C
0392     REQUIRE(Test_Util::usesInTheImplementationExists("foobar::C", "foobar::Vector", session));
0393     REQUIRE(Test_Util::usesInTheImplementationExists("foobar::C", "foobar::SomeClass", session));
0394     REQUIRE(!Test_Util::usesInTheImplementationExists("foobar::C", "bslma::AllocatorTraits_SomeTrait", session));
0395     REQUIRE(!Test_Util::usesInTheImplementationExists("bslma::AllocatorTraits_SomeTrait", "foobar::C", session));
0396     REQUIRE(!Test_Util::usesInTheInterfaceExists("foobar::C", "bslma::AllocatorTraits_SomeTrait", session));
0397     REQUIRE(!Test_Util::usesInTheInterfaceExists("bslma::AllocatorTraits_SomeTrait", "foobar::C", session));
0398 }
0399 
0400 class NonLakosianFixture {
0401   public:
0402     // public data for Catch2 magic
0403     std::filesystem::path d_topLevel;
0404     std::filesystem::path d_pcre2;
0405     std::filesystem::path d_pcre2Source;
0406     std::filesystem::path d_pcre2Header;
0407     std::filesystem::path d_sljit;
0408     std::filesystem::path d_sljitSource;
0409     std::filesystem::path d_sljitHeader;
0410     std::filesystem::path d_bdlpcre;
0411     std::filesystem::path d_bdlpcreRegex;
0412 
0413     void createTestEnv() const
0414     {
0415         if (std::filesystem::exists(d_topLevel)) {
0416             REQUIRE(std::filesystem::remove_all(d_topLevel));
0417         }
0418 
0419         REQUIRE(std::filesystem::create_directories(d_sljit));
0420         REQUIRE(Test_Util::createFile(d_sljitSource));
0421         REQUIRE(Test_Util::createFile(d_sljitHeader));
0422         REQUIRE(Test_Util::createFile(d_pcre2Source, "#include \"sljit/sljit.h\""));
0423         REQUIRE(Test_Util::createFile(d_pcre2Header));
0424 
0425         REQUIRE(std::filesystem::create_directories(d_bdlpcre));
0426         REQUIRE(Test_Util::createFile(d_bdlpcreRegex, "#include <pcre2/pcre2.h>"));
0427     }
0428 
0429     NonLakosianFixture():
0430         d_topLevel(std::filesystem::temp_directory_path() / "ct_lvtclp_testphysicalandtemplates_nonlakosian"),
0431         d_pcre2(d_topLevel / "thirdparty" / "pcre2"),
0432         d_pcre2Source(d_pcre2 / "pcre2.c"),
0433         d_pcre2Header(d_pcre2 / "pcre2.h"),
0434         d_sljit(d_pcre2 / "sljit"),
0435         d_sljitSource(d_sljit / "sljit.c"),
0436         d_sljitHeader(d_sljit / "sljit.h"),
0437         d_bdlpcre(d_topLevel / "groups" / "bdl" / "bdlpcre"),
0438         d_bdlpcreRegex(d_bdlpcre / "bdlpcre_regex.cpp")
0439     {
0440         createTestEnv();
0441     }
0442 
0443     ~NonLakosianFixture()
0444     {
0445         REQUIRE(std::filesystem::remove_all(d_topLevel));
0446     }
0447 };
0448 
0449 TEST_CASE_METHOD(NonLakosianFixture, "Non-lakosian extra levels of hierarchy")
0450 {
0451     StaticCompilationDatabase cmds(
0452         {{d_sljitSource.string(), ""}, {d_pcre2Source.string(), ""}, {d_bdlpcreRegex.string(), ""}},
0453         "placeholder-g++",
0454         {"-Ithirdparty"},
0455         d_topLevel);
0456 
0457     CppTool tool(d_topLevel, cmds, ":memory:");
0458     REQUIRE(tool.runFull());
0459     ObjectStore& session = tool.getObjectStore();
0460 
0461     FileObject *sljitSource = nullptr;
0462     PackageObject *sljit = nullptr;
0463     PackageObject *pcre2 = nullptr;
0464     PackageObject *bdlpcre = nullptr;
0465     PackageObject *bdl = nullptr;
0466     PackageObject *nonlakosiangroup = nullptr;
0467 
0468     session.withROLock([&] {
0469         sljitSource = session.getFile("thirdparty/pcre2/sljit/sljit.c");
0470         sljit = session.getPackage("thirdparty/pcre2/sljit");
0471         pcre2 = session.getPackage("thirdparty/pcre2");
0472         bdlpcre = session.getPackage("groups/bdl/bdlpcre");
0473         bdl = session.getPackage("groups/bdl");
0474         nonlakosiangroup = session.getPackage("non-lakosian group");
0475     });
0476 
0477     REQUIRE(sljitSource);
0478     REQUIRE(sljit);
0479     REQUIRE(pcre2);
0480     REQUIRE(bdlpcre);
0481     REQUIRE(bdl);
0482     REQUIRE(nonlakosiangroup);
0483 
0484     sljitSource->withROLock([&] {
0485         CHECK(sljitSource->package() == sljit); // file/component
0486     });
0487 
0488     // TODO: Check if we need to verify the package grouop dependencies here.
0489     // I believe this is only needed on lvtldr.
0490     sljit->withROLock([&] {
0491         CHECK(sljit->parent() == nonlakosiangroup); // package
0492 
0493         //        CHECK(sljit->packageGroupDependencies().empty());
0494         //        CHECK(sljit->packageGroupRevDependencies().size() == 1);
0495     });
0496 
0497     bdlpcre->withROLock([&] {
0498         CHECK(bdlpcre->parent() == bdl);
0499         //        CHECK(bdlpcre->packageGroupDependencies().size() == 1);
0500         //        CHECK(bdlpcre->packageGroupRevDependencies().empty());
0501     });
0502     bdl->withROLock([&] {
0503         CHECK(!bdl->parent());
0504         //        CHECK(bdl->packageGroupDependencies().size() == 1);
0505         //        CHECK(bdl->packageGroupRevDependencies().empty());
0506     });
0507 
0508     pcre2->withROLock([&] {
0509         //        CHECK(pcre2->packageGroupDependencies().size() == 1);
0510         //        CHECK(pcre2->packageGroupRevDependencies().size() == 1);
0511     });
0512 
0513     // check that all packageGroupDependencies() are correct
0514     // this is a regression test for an old assertion error
0515 }