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 }