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

0001 // ct_lvtclp_tool.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_lvtclp_cpp_tool.h>
0021 
0022 #include <ct_lvtclp_testutil.h>
0023 
0024 #include <ct_lvtmdb_componentobject.h>
0025 #include <ct_lvtmdb_fileobject.h>
0026 #include <ct_lvtmdb_objectstore.h>
0027 #include <ct_lvtmdb_packageobject.h>
0028 #include <ct_lvtmdb_typeobject.h>
0029 
0030 #include <fstream>
0031 #include <initializer_list>
0032 
0033 #include <catch2-local-includes.h>
0034 
0035 #include <autogen-test-variables.h>
0036 
0037 using namespace Codethink::lvtclp;
0038 using namespace Codethink::lvtmdb;
0039 
0040 const PyDefaultGilReleasedContext defaultGilContextForTesting;
0041 
0042 static void createTop(const std::filesystem::path& topLevel)
0043 {
0044     bool created = std::filesystem::create_directories(topLevel / "groups/one/onetop");
0045     REQUIRE(created);
0046 
0047     created = Test_Util::createFile(topLevel / "groups/one/onetop/onetop_top.h", R"(
0048 // onetop_top.h
0049 
0050 namespace onetop {
0051 
0052 class Base
0053 {
0054 };
0055 
0056 class Top : public Base {
0057   public:
0058     int method();
0059 };
0060 
0061 })");
0062     REQUIRE(created);
0063 
0064     created = Test_Util::createFile(topLevel / "groups/one/onetop/onetop_top.cpp", R"(
0065 // onetop_top.cpp
0066 
0067 #include <onetop_top.h>
0068 
0069 namespace onetop {
0070 
0071 int Top::method()
0072 {
0073     return 2;
0074 }
0075 
0076 })");
0077 
0078     REQUIRE(created);
0079 }
0080 
0081 static void createDep(const std::filesystem::path& topLevel)
0082 {
0083     assert(std::filesystem::create_directories(topLevel / "groups/one/onedep"));
0084 
0085     Test_Util::createFile(topLevel / "groups/one/onedep/onedep_dep.h", R"(
0086 // onedep_dep.h
0087 
0088 #include <onetop_top.h>
0089 
0090 namespace onedep {
0091 
0092 class Dep {
0093     onetop::Top top;
0094 
0095     int method();
0096 };
0097 
0098 })");
0099     Test_Util::createFile(topLevel / "groups/one/onedep/onedep_dep.cpp", R"(
0100 // onedep_dep.cpp
0101 
0102 #include <onedep_dep.h>
0103 
0104 namespace onedep {
0105 
0106 int Dep::method()
0107 {
0108     return top.method();
0109 }
0110 
0111 })");
0112 }
0113 
0114 static void createTestEnv(const std::filesystem::path& topLevel)
0115 {
0116     if (std::filesystem::exists(topLevel / "groups")) {
0117         REQUIRE(std::filesystem::remove_all(topLevel / "groups"));
0118     }
0119 
0120     createTop(topLevel);
0121     createDep(topLevel);
0122 }
0123 
0124 static void checkDatabaseOriginal(ObjectStore& session)
0125 // check the database before any files are removed
0126 {
0127     const std::initializer_list<ModelUtil::SourceFileModel> files = {{"groups/one/onedep/onedep_dep.cpp",
0128                                                                       false,
0129                                                                       "groups/one/onedep",
0130                                                                       "groups/one/onedep/onedep_dep",
0131                                                                       {"onedep"},
0132                                                                       {},
0133                                                                       {"groups/one/onedep/onedep_dep.h"}},
0134                                                                      {"groups/one/onedep/onedep_dep.h",
0135                                                                       true,
0136                                                                       "groups/one/onedep",
0137                                                                       "groups/one/onedep/onedep_dep",
0138                                                                       {"onedep"},
0139                                                                       {"onedep::Dep"},
0140                                                                       {"groups/one/onetop/onetop_top.h"}},
0141                                                                      {"groups/one/onetop/onetop_top.cpp",
0142                                                                       false,
0143                                                                       "groups/one/onetop",
0144                                                                       "groups/one/onetop/onetop_top",
0145                                                                       {"onetop"},
0146                                                                       {},
0147                                                                       {"groups/one/onetop/onetop_top.h"}},
0148                                                                      {"groups/one/onetop/onetop_top.h",
0149                                                                       true,
0150                                                                       "groups/one/onetop",
0151                                                                       "groups/one/onetop/onetop_top",
0152                                                                       {"onetop"},
0153                                                                       {"onetop::Base", "onetop::Top"},
0154                                                                       {}}};
0155 
0156     const std::initializer_list<ModelUtil::ComponentModel> comps = {
0157         {"groups/one/onedep/onedep_dep",
0158          "onedep_dep",
0159          {"groups/one/onedep/onedep_dep.h", "groups/one/onedep/onedep_dep.cpp"},
0160          {"groups/one/onetop/onetop_top"}},
0161         {"groups/one/onetop/onetop_top",
0162          "onetop_top",
0163          {"groups/one/onetop/onetop_top.h", "groups/one/onetop/onetop_top.cpp"},
0164          {}},
0165     };
0166 
0167     const std::initializer_list<ModelUtil::PackageModel> pkgs = {
0168         {"groups/one/onedep", "groups/one", {"onedep::Dep"}, {"groups/one/onedep/onedep_dep"}, {"groups/one/onetop"}},
0169         {"groups/one/onetop", "groups/one", {"onetop::Base", "onetop::Top"}, {"groups/one/onetop/onetop_top"}, {}}};
0170 
0171     const std::initializer_list<ModelUtil::UDTModel> udts = {
0172         {
0173             "onedep::Dep",
0174             "onedep",
0175             "groups/one/onedep",
0176             {"onetop::Top"},
0177             {},
0178             {},
0179             {"onedep::Dep::method"},
0180             {"onedep::Dep::top"},
0181         },
0182         {
0183             "onetop::Base",
0184             "onetop",
0185             "groups/one/onetop",
0186             {},
0187             {},
0188             {},
0189             {},
0190             {},
0191         },
0192         {
0193             "onetop::Top",
0194             "onetop",
0195             "groups/one/onetop",
0196             {},
0197             {},
0198             {"onetop::Base"},
0199             {"onetop::Top::method"},
0200             {},
0201         },
0202     };
0203 
0204     ModelUtil::checkSourceFiles(session, files);
0205     ModelUtil::checkComponents(session, comps);
0206     ModelUtil::checkPackages(session, pkgs);
0207     ModelUtil::checkUDTs(session, udts);
0208 }
0209 
0210 static void checkDatabaseModifiedFile(ObjectStore& session, bool printToConsole = false)
0211 {
0212     const std::initializer_list<ModelUtil::SourceFileModel> files = {
0213         {"groups/one/onedep/onedep_dep.cpp",
0214          false,
0215          "groups/one/onedep",
0216          "groups/one/onedep/onedep_dep",
0217          {"onedep"},
0218          {},
0219          {"groups/one/onedep/onedep_dep.h"}},
0220         {"groups/one/onedep/onedep_dep.h",
0221          true,
0222          "groups/one/onedep",
0223          "groups/one/onedep/onedep_dep",
0224          {"onedep"},
0225          {"onedep::Dep"},
0226          {"groups/one/onetop/onetop_top.h"}},
0227         {"groups/one/onetop/onetop_top.cpp",
0228          false,
0229          "groups/one/onetop",
0230          "groups/one/onetop/onetop_top",
0231          {"onetop"},
0232          {},
0233          {"groups/one/onetop/onetop_top.h"}},
0234         // NewClass added:
0235         {"groups/one/onetop/onetop_top.h",
0236          true,
0237          "groups/one/onetop",
0238          "groups/one/onetop/onetop_top",
0239          {"onetop"},
0240          {"onetop::Base", "onetop::Top", "onetop::NewClass"},
0241          {}}};
0242 
0243     const std::initializer_list<ModelUtil::ComponentModel> comps = {
0244         {"groups/one/onedep/onedep_dep",
0245          "onedep_dep",
0246          {"groups/one/onedep/onedep_dep.h", "groups/one/onedep/onedep_dep.cpp"},
0247          {"groups/one/onetop/onetop_top"}},
0248         {"groups/one/onetop/onetop_top",
0249          "onetop_top",
0250          {"groups/one/onetop/onetop_top.h", "groups/one/onetop/onetop_top.cpp"},
0251          {}},
0252     };
0253 
0254     const std::initializer_list<ModelUtil::PackageModel> pkgs = {
0255         {"groups/one/onedep", "groups/one", {"onedep::Dep"}, {"groups/one/onedep/onedep_dep"}, {"groups/one/onetop"}},
0256         {"groups/one/onetop",
0257          "groups/one",
0258          {"onetop::Base", "onetop::Top", "onetop::NewClass"},
0259          {"groups/one/onetop/onetop_top"},
0260          {}}};
0261 
0262     const std::initializer_list<ModelUtil::UDTModel> udts = {
0263         {
0264             "onedep::Dep",
0265             "onedep",
0266             "groups/one/onedep",
0267             {"onetop::Top"},
0268             {},
0269             {},
0270             {"onedep::Dep::method"},
0271             {"onedep::Dep::top"},
0272         },
0273         {
0274             "onetop::Base",
0275             "onetop",
0276             "groups/one/onetop",
0277             {},
0278             {},
0279             {},
0280             {},
0281             {},
0282         },
0283         {
0284             "onetop::Top",
0285             "onetop",
0286             "groups/one/onetop",
0287             {},
0288             {},
0289             {"onetop::Base"},
0290             {"onetop::Top::method"},
0291             {},
0292         },
0293         {
0294             "onetop::NewClass",
0295             "onetop",
0296             "groups/one/onetop",
0297             {},
0298             {},
0299             {},
0300             {},
0301             {},
0302         },
0303     };
0304 
0305     ModelUtil::checkSourceFiles(session, files);
0306     ModelUtil::checkComponents(session, comps);
0307     ModelUtil::checkPackages(session, pkgs);
0308     ModelUtil::checkUDTs(session, udts);
0309 }
0310 
0311 static void checkDatabaseNewFiles(ObjectStore& session)
0312 // check the database after onedep_newfile has been added
0313 {
0314     const std::initializer_list<ModelUtil::SourceFileModel> files = {{"groups/one/onedep/onedep_dep.cpp",
0315                                                                       false,
0316                                                                       "groups/one/onedep",
0317                                                                       "groups/one/onedep/onedep_dep",
0318                                                                       {"onedep"},
0319                                                                       {},
0320                                                                       {"groups/one/onedep/onedep_dep.h"}},
0321                                                                      {"groups/one/onedep/onedep_dep.h",
0322                                                                       true,
0323                                                                       "groups/one/onedep",
0324                                                                       "groups/one/onedep/onedep_dep",
0325                                                                       {"onedep"},
0326                                                                       {"onedep::Dep"},
0327                                                                       {"groups/one/onetop/onetop_top.h"}},
0328                                                                      {"groups/one/onedep/onedep_newfile.cpp",
0329                                                                       false,
0330                                                                       "groups/one/onedep",
0331                                                                       "groups/one/onedep/onedep_newfile",
0332                                                                       {},
0333                                                                       {},
0334                                                                       {"groups/one/onedep/onedep_newfile.h"}},
0335                                                                      {"groups/one/onedep/onedep_newfile.h",
0336                                                                       true,
0337                                                                       "groups/one/onedep",
0338                                                                       "groups/one/onedep/onedep_newfile",
0339                                                                       {"onedep"},
0340                                                                       {"onedep::NewFile"},
0341                                                                       {}},
0342                                                                      {"groups/one/onetop/onetop_top.cpp",
0343                                                                       false,
0344                                                                       "groups/one/onetop",
0345                                                                       "groups/one/onetop/onetop_top",
0346                                                                       {"onetop"},
0347                                                                       {},
0348                                                                       {"groups/one/onetop/onetop_top.h"}},
0349                                                                      {"groups/one/onetop/onetop_top.h",
0350                                                                       true,
0351                                                                       "groups/one/onetop",
0352                                                                       "groups/one/onetop/onetop_top",
0353                                                                       {"onetop"},
0354                                                                       {"onetop::Base", "onetop::Top"},
0355                                                                       {}}};
0356 
0357     const std::initializer_list<ModelUtil::ComponentModel> comps = {
0358         {"groups/one/onedep/onedep_dep",
0359          "onedep_dep",
0360          {"groups/one/onedep/onedep_dep.h", "groups/one/onedep/onedep_dep.cpp"},
0361          {"groups/one/onetop/onetop_top"}},
0362         {"groups/one/onedep/onedep_newfile",
0363          "onedep_newfile",
0364          {"groups/one/onedep/onedep_newfile.h", "groups/one/onedep/onedep_newfile.cpp"},
0365          {}},
0366         {"groups/one/onetop/onetop_top",
0367          "onetop_top",
0368          {"groups/one/onetop/onetop_top.h", "groups/one/onetop/onetop_top.cpp"},
0369          {}},
0370     };
0371 
0372     const std::initializer_list<ModelUtil::PackageModel> pkgs = {
0373         {"groups/one/onedep",
0374          "groups/one",
0375          {"onedep::Dep", "onedep::NewFile"},
0376          {"groups/one/onedep/onedep_dep", "groups/one/onedep/onedep_newfile"},
0377          {"groups/one/onetop"}},
0378         {"groups/one/onetop", "groups/one", {"onetop::Base", "onetop::Top"}, {"groups/one/onetop/onetop_top"}, {}}};
0379 
0380     const std::initializer_list<ModelUtil::UDTModel> udts = {
0381         {
0382             "onedep::Dep",
0383             "onedep",
0384             "groups/one/onedep",
0385             {"onetop::Top"},
0386             {},
0387             {},
0388             {"onedep::Dep::method"},
0389             {"onedep::Dep::top"},
0390         },
0391         {
0392             "onedep::NewFile",
0393             "onedep",
0394             "groups/one/onedep",
0395             {},
0396             {},
0397             {},
0398             {},
0399             {},
0400         },
0401         {
0402             "onetop::Base",
0403             "onetop",
0404             "groups/one/onetop",
0405             {},
0406             {},
0407             {},
0408             {},
0409             {},
0410         },
0411         {
0412             "onetop::Top",
0413             "onetop",
0414             "groups/one/onetop",
0415             {},
0416             {},
0417             {"onetop::Base"},
0418             {"onetop::Top::method"},
0419             {},
0420         },
0421     };
0422 
0423     ModelUtil::checkSourceFiles(session, files);
0424     ModelUtil::checkComponents(session, comps);
0425     ModelUtil::checkPackages(session, pkgs);
0426     ModelUtil::checkUDTs(session, udts);
0427 }
0428 
0429 static void runTool(CppTool& tool, const bool madeChanges, const unsigned numRuns = 1, bool verbose = false)
0430 {
0431     tool.setPrintToConsole(verbose);
0432     for (unsigned i = 0; i < numRuns; i++) {
0433         REQUIRE(tool.runFull());
0434         REQUIRE(tool.lastRunMadeChanges() == madeChanges);
0435     }
0436 }
0437 
0438 struct CppToolTestFixture {
0439     CppToolTestFixture(): topLevel(std::filesystem::temp_directory_path() / "ct_lvtclp_tool_test")
0440     {
0441         createTestEnv(topLevel);
0442     }
0443 
0444     ~CppToolTestFixture()
0445     {
0446         REQUIRE(std::filesystem::remove_all(topLevel));
0447     }
0448 
0449     std::filesystem::path topLevel;
0450 };
0451 
0452 TEST_CASE_METHOD(CppToolTestFixture, "Tool")
0453 {
0454     constexpr unsigned NUM_RERUNS = 2;
0455 
0456     {
0457         StaticCompilationDatabase cmds({{"groups/one/onetop/onetop_top.cpp", "build/onetop_top.o"},
0458                                         {"groups/one/onedep/onedep_dep.cpp", "build/onedep_dep.o"}},
0459                                        "placeholder",
0460                                        {"-Igroups/one/onetop", "-Igroups/one/onedep"},
0461                                        topLevel);
0462 
0463         CppTool tool(topLevel, cmds, topLevel / "database");
0464         ObjectStore& session = tool.getObjectStore();
0465 
0466         // initial parse
0467         runTool(tool, true);
0468         checkDatabaseOriginal(session);
0469 
0470         // re-run incrementally
0471         runTool(tool, false, NUM_RERUNS);
0472         checkDatabaseOriginal(session);
0473 
0474         // modify a file, adding a class
0475         std::filesystem::path fileToChange = topLevel / "groups/one/onetop/onetop_top.h";
0476         {
0477             // open for appending
0478             std::ofstream of(fileToChange.string(), std::ios_base::out | std::ios_base::app);
0479             REQUIRE(!of.fail());
0480             of << "namespace onetop { class NewClass {}; }" << std::endl;
0481         }
0482         runTool(tool, true);
0483         checkDatabaseModifiedFile(session);
0484 
0485         // re-run incrementally
0486         runTool(tool, false, NUM_RERUNS);
0487         checkDatabaseModifiedFile(session);
0488 
0489         // change modified file back again, run again, and re-run
0490         createTestEnv(topLevel);
0491         runTool(tool, true);
0492         checkDatabaseOriginal(session);
0493         runTool(tool, false, NUM_RERUNS);
0494         checkDatabaseOriginal(session);
0495     }
0496 
0497     // add a new component (already in the above compilation database)
0498     {
0499         StaticCompilationDatabase cmdsPlus(
0500             // cmds plus the new component
0501             {{"groups/one/onetop/onetop_top.cpp", "build/onetop_top.o"},
0502              {"groups/one/onedep/onedep_dep.cpp", "build/onedep_dep.o"},
0503              {"groups/one/onedep/onedep_newfile.cpp", "build/onedep_newfile.o"}},
0504             "placeholder",
0505             {"-Igroups/one/onetop", "-Igroups/one/onedep"},
0506             topLevel.string());
0507 
0508         CppTool toolPlus(topLevel, cmdsPlus, topLevel / "database");
0509 
0510         Test_Util::createFile(topLevel / "groups/one/onedep/onedep_newfile.h", R"(
0511     // onedep_newfile.h
0512     namespace onedep {
0513     class NewFile {};
0514     }
0515     )");
0516         Test_Util::createFile(topLevel / "groups/one/onedep/onedep_newfile.cpp", R"(
0517     // onedep_newfile.cpp
0518 #include <onedep_newfile.h>
0519     )");
0520         ObjectStore& session = toolPlus.getObjectStore();
0521         runTool(toolPlus, true);
0522         checkDatabaseNewFiles(session);
0523         runTool(toolPlus, false, NUM_RERUNS);
0524         checkDatabaseNewFiles(session);
0525 
0526         // delete the new component and re-run
0527         createTestEnv(topLevel);
0528         runTool(toolPlus, true);
0529         checkDatabaseOriginal(session);
0530         runTool(toolPlus, false, NUM_RERUNS);
0531         checkDatabaseOriginal(session);
0532     }
0533 }
0534 
0535 TEST_CASE("Run Tool on example project")
0536 {
0537     StaticCompilationDatabase cmds({{"hello.m.cpp", "hello.m.o"}}, "placeholder", {}, PREFIX + "/hello_world/");
0538     CppTool tool(PREFIX + "/hello_world/", cmds, PREFIX + "/hello_world/database");
0539     ObjectStore& session = tool.getObjectStore();
0540 
0541     REQUIRE(tool.runFull());
0542 
0543     FileObject *file = nullptr;
0544     session.withROLock([&] {
0545         file = session.getFile("hello.m.cpp");
0546     });
0547     REQUIRE(file);
0548 }
0549 
0550 TEST_CASE("Run Tool on example project incrementally")
0551 {
0552     auto sourcePath = PREFIX + "/package_circular_dependency/";
0553 
0554     auto files = std::initializer_list<std::pair<std::string, std::string>>{
0555         {"groups/one/onepkg/ct_onepkg_circle.cpp", "ct_onepkg_circle.o"},
0556         {"groups/one/onepkg/ct_onepkg_thing.cpp", "ct_onepkg_thing.o"}};
0557 
0558     StaticCompilationDatabase cmds(
0559         files,
0560         "placeholder",
0561         {"-I" + sourcePath + "/groups/one/onepkg/", "-I" + sourcePath + "/groups/two/twodmo/"},
0562         sourcePath);
0563     CppTool tool(sourcePath, cmds, sourcePath + "database");
0564     ObjectStore& session = tool.getObjectStore();
0565 
0566     REQUIRE(tool.runPhysical());
0567     session.withROLock([&] {
0568         REQUIRE(session.getAllFiles().size() == 5);
0569         REQUIRE(session.getAllPackages().size() == 2);
0570         auto *e = session.getPackage("groups/one/onepkg");
0571         e->withROLock([&]() {
0572             REQUIRE(e->components().size() == 2);
0573             for (auto&& c : e->components()) {
0574                 c->withROLock([&]() {
0575                     if (c->qualifiedName() == "groups/one/onepkg/ct_onepkg_circle") {
0576                         REQUIRE(c->forwardDependencies().size() == 2);
0577                     } else if (c->qualifiedName() == "groups/one/onepkg/ct_onepkg_thing") {
0578                         REQUIRE(c->forwardDependencies().empty());
0579                     } else {
0580                         FAIL("Unexpected component: " + c->qualifiedName());
0581                     }
0582                 });
0583             }
0584         });
0585     });
0586 
0587     // There was a bug where a logical parse after a only-physical parse would remove physical connections from
0588     // components. This test has been added to avoid regression.
0589     REQUIRE(tool.runFull(true));
0590     session.withROLock([&] {
0591         REQUIRE(session.getAllFiles().size() == 5);
0592         REQUIRE(session.getAllPackages().size() == 2);
0593         auto *e = session.getPackage("groups/one/onepkg");
0594         e->withROLock([&]() {
0595             REQUIRE(e->components().size() == 2);
0596             for (auto&& c : e->components()) {
0597                 c->withROLock([&]() {
0598                     if (c->qualifiedName() == "groups/one/onepkg/ct_onepkg_circle") {
0599                         REQUIRE(c->forwardDependencies().size() == 2);
0600                     } else if (c->qualifiedName() == "groups/one/onepkg/ct_onepkg_thing") {
0601                         REQUIRE(c->forwardDependencies().empty());
0602                     } else {
0603                         FAIL("Unexpected component: " + c->qualifiedName());
0604                     }
0605                 });
0606             }
0607         });
0608     });
0609 }
0610 
0611 TEST_CASE("Run Tool on project including other lakosian project")
0612 {
0613     auto const prjAPath = PREFIX + "/project_with_includes_outside_src/prjA";
0614     auto const prjBPath = PREFIX + "/project_with_includes_outside_src/prjB";
0615     StaticCompilationDatabase cmds({{"groups/one/oneaaa/oneaaa_comp.cpp", "oneaaa_comp.o"}},
0616                                    "placeholder",
0617                                    {"-I" + prjAPath + "/groups/one/oneaaa/", "-I" + prjBPath + "/groups/two/twoaaa/"},
0618                                    prjAPath);
0619     CppTool tool(prjAPath, cmds, prjAPath + "/database");
0620     ObjectStore& session = tool.getObjectStore();
0621 
0622     REQUIRE(tool.runFull());
0623     session.withROLock([&] {
0624         REQUIRE(session.getFile("groups/one/oneaaa/oneaaa_comp.cpp"));
0625         REQUIRE(session.getFile("groups/one/oneaaa/oneaaa_comp.h"));
0626         REQUIRE(session.getComponent("groups/one/oneaaa/oneaaa_comp"));
0627         REQUIRE(session.getComponent("groups/two/twoaaa/twoaaa_comp"));
0628     });
0629 }
0630 
0631 struct HashFunc {
0632     size_t operator()(const std::tuple<std::string, unsigned>& data) const
0633     {
0634         return std::hash<std::string>{}(std::get<0>(data)) ^ std::hash<unsigned>{}(std::get<1>(data));
0635     }
0636 };
0637 
0638 TEST_CASE("Run Tool store test-only dependencies")
0639 {
0640     auto const prjAPath = PREFIX + "/test_only_dependencies/prjA";
0641     StaticCompilationDatabase cmds(
0642         {
0643             {"groups/one/oneaaa/oneaaa_comp.cpp", "oneaaa_comp.o"},
0644             {"groups/one/oneaaa/oneaaa_comp.t.cpp", "oneaaa_comp.t.o"},
0645             {"groups/one/oneaaa/oneaaa_othercomp.cpp", "oneaaa_othercomp.o"},
0646             {"groups/one/oneaaa/oneaaa_othercomp2.cpp", "oneaaa_othercomp2.o"},
0647         },
0648         "placeholder",
0649         {"-I" + prjAPath + "/groups/one/oneaaa/", "-fparse-all-comments"},
0650         prjAPath);
0651     CppTool tool(prjAPath, cmds, prjAPath + "/database");
0652 
0653     using Filename = std::string;
0654     using LineNo = unsigned;
0655     using FileAndLine = std::tuple<Filename, LineNo>;
0656     auto includesInFile = std::unordered_map<Filename, std::unordered_set<FileAndLine, HashFunc>>{};
0657     tool.setHeaderLocationCallback(
0658         [&includesInFile](Filename const& sourceFile, Filename const& includedFile, LineNo lineNo) {
0659             if (includesInFile.find(sourceFile) == includesInFile.end()) {
0660                 includesInFile[sourceFile] = std::unordered_set<FileAndLine, HashFunc>{};
0661             }
0662             includesInFile[sourceFile].insert(std::make_tuple(includedFile, lineNo));
0663         });
0664 
0665     auto testOnlyDepComments = std::unordered_map<Filename, std::unordered_set<LineNo>>{};
0666     tool.setHandleCppCommentsCallback([&testOnlyDepComments](Filename const& filename,
0667                                                              std::string const& briefText,
0668                                                              LineNo startLine,
0669                                                              LineNo endLine) {
0670         if (briefText != "Test only dependency") {
0671             return;
0672         }
0673 
0674         if (testOnlyDepComments.find(filename) == testOnlyDepComments.end()) {
0675             testOnlyDepComments[filename] = std::unordered_set<LineNo>{};
0676         }
0677         testOnlyDepComments[filename].insert(startLine);
0678     });
0679 
0680     ObjectStore& session = tool.getObjectStore();
0681 
0682     REQUIRE(tool.runFull());
0683     session.withROLock([&] {
0684         REQUIRE(session.getFile("groups/one/oneaaa/oneaaa_comp.cpp"));
0685         REQUIRE(session.getFile("groups/one/oneaaa/oneaaa_comp.h"));
0686         REQUIRE(session.getFile("groups/one/oneaaa/oneaaa_othercomp.cpp"));
0687         REQUIRE(session.getFile("groups/one/oneaaa/oneaaa_othercomp.h"));
0688         REQUIRE(session.getFile("groups/one/oneaaa/oneaaa_othercomp2.cpp"));
0689         REQUIRE(session.getFile("groups/one/oneaaa/oneaaa_othercomp2.h"));
0690 
0691         REQUIRE(session.getComponent("groups/one/oneaaa/oneaaa_comp"));
0692         REQUIRE(session.getComponent("groups/one/oneaaa/oneaaa_comp.t"));
0693         REQUIRE(session.getComponent("groups/one/oneaaa/oneaaa_othercomp"));
0694         REQUIRE(session.getComponent("groups/one/oneaaa/oneaaa_othercomp2"));
0695     });
0696 
0697     auto fileHasIncludeAtLine = [&includesInFile](Filename const& file, Filename const& included_file, LineNo line) {
0698         auto& set = includesInFile.at(file);
0699         return set.find(std::make_tuple(included_file, line)) != set.end();
0700     };
0701     REQUIRE(fileHasIncludeAtLine("oneaaa_othercomp2.cpp", "oneaaa_othercomp2.h", 1));
0702     REQUIRE(fileHasIncludeAtLine("oneaaa_othercomp.cpp", "oneaaa_othercomp.h", 1));
0703     REQUIRE(fileHasIncludeAtLine("oneaaa_comp.t.cpp", "oneaaa_comp.h", 1));
0704     REQUIRE(fileHasIncludeAtLine("oneaaa_comp.t.cpp", "oneaaa_othercomp.h", 2));
0705     REQUIRE(fileHasIncludeAtLine("oneaaa_comp.t.cpp", "oneaaa_othercomp2.h", 3));
0706     REQUIRE(fileHasIncludeAtLine("oneaaa_comp.cpp", "oneaaa_comp.h", 1));
0707     REQUIRE(fileHasIncludeAtLine("oneaaa_comp.cpp", "oneaaa_othercomp2.h", 2));
0708 
0709     auto fileHasTestOnlyCommentAtLine = [&testOnlyDepComments](Filename const& file, LineNo line) {
0710         auto& set = testOnlyDepComments.at(file);
0711         return set.find(line) != set.end();
0712     };
0713     REQUIRE(fileHasTestOnlyCommentAtLine("oneaaa_comp.cpp", 2));
0714 }