File indexing completed on 2024-05-19 05:41:57
0001 // ct_lvtcgn_app_adapter.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_lvtcgn_app_adapter.h> 0021 0022 #include <catch2-local-includes.h> 0023 #include <ct_lvtldr_nodestoragetestutils.h> 0024 #include <ct_lvttst_tmpdir.h> 0025 0026 #pragma push_macro("slots") 0027 #undef slots 0028 #include <pybind11/embed.h> 0029 #include <pybind11/pybind11.h> 0030 #pragma pop_macro("slots") 0031 0032 #include <ctime> 0033 #include <test-project-paths.h> 0034 0035 using namespace Codethink::lvtcgn::app; 0036 using namespace Codethink::lvtcgn::mdl; 0037 using namespace Codethink::lvtldr; 0038 0039 namespace py = pybind11; 0040 struct PyDefaultGilReleasedContext { 0041 py::scoped_interpreter pyInterp; 0042 py::gil_scoped_release pyGilDefaultReleased; 0043 }; 0044 0045 TEST_CASE("Code generation adapter") 0046 { 0047 PyDefaultGilReleasedContext _pyDefaultGilReleasedContext; 0048 0049 auto tmpDir = TmpDir{"codegen_adapter"}; 0050 auto dbPath = tmpDir.path() / "codedb.db"; 0051 auto ns = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0052 0053 auto *pkgA = ns.addPackage("pkgA", "pkgA").value(); 0054 (void) ns.addComponent("componentA", "pkgA::componentA", pkgA); 0055 auto *pkgB = ns.addPackage("pkgB", "pkgB").value(); 0056 (void) ns.addPackage("pkgBa", "pkgB/pkgBa", pkgB); 0057 (void) ns.addPackage("non-lakosian group", "non-lakosian group"); 0058 0059 auto dataProvider = NodeStorageDataProvider{ns}; 0060 REQUIRE(dataProvider.numberOfPhysicalEntities() == 5); 0061 0062 auto pkgs = dataProvider.topLevelEntities(); 0063 REQUIRE(pkgs.size() == 3); 0064 0065 auto getTopLvlEntity = [&pkgs](auto const& name) -> IPhysicalEntityInfo& { 0066 return std::find_if(pkgs.begin(), 0067 pkgs.end(), 0068 [&name](auto&& p) { 0069 return p.get().name() == name; 0070 }) 0071 ->get(); 0072 }; 0073 0074 auto& pkg0 = getTopLvlEntity("non-lakosian group"); 0075 REQUIRE(pkg0.name() == "non-lakosian group"); 0076 // In this particular case, the non-lakosian group is simply a package because it is empty, and falls into 0077 // this specific rule. 0078 REQUIRE(pkg0.type() == "Package"); 0079 REQUIRE_FALSE(pkg0.parent().has_value()); 0080 REQUIRE(pkg0.children().empty()); 0081 // non-lakosian groups are expected to be unselected for code generation 0082 REQUIRE(pkg0.selectedForCodeGeneration() == false); 0083 0084 auto& pkg1 = getTopLvlEntity("pkgA"); 0085 REQUIRE(pkg1.name() == "pkgA"); 0086 REQUIRE(pkg1.type() == "Package"); 0087 REQUIRE_FALSE(pkg1.parent().has_value()); 0088 REQUIRE(pkg1.selectedForCodeGeneration() == true); 0089 REQUIRE(pkg1.children().size() == 1); 0090 0091 auto& componentA = pkg1.children()[0].get(); 0092 REQUIRE(componentA.name() == "componentA"); 0093 REQUIRE(componentA.type() == "Component"); 0094 REQUIRE(componentA.parent().value().get().name() == "pkgA"); 0095 REQUIRE(componentA.children().empty()); 0096 REQUIRE(componentA.fwdDependencies().empty()); 0097 0098 auto& pkg2 = getTopLvlEntity("pkgB"); 0099 REQUIRE(pkg2.name() == "pkgB"); 0100 REQUIRE(pkg2.type() == "PackageGroup"); 0101 REQUIRE_FALSE(pkg2.parent().has_value()); 0102 REQUIRE(pkg2.children().size() == 1); 0103 REQUIRE(pkg2.selectedForCodeGeneration() == true); 0104 0105 auto& pkgBa = pkg2.children()[0].get(); 0106 REQUIRE(pkgBa.name() == "pkgBa"); 0107 REQUIRE(pkgBa.type() == "Package"); 0108 REQUIRE(pkgBa.parent().value().get().name() == "pkgB"); 0109 REQUIRE(pkgBa.children().empty()); 0110 REQUIRE(pkgBa.selectedForCodeGeneration() == true); 0111 } 0112 0113 TEST_CASE("CMake code generation script") 0114 { 0115 auto cmakeGeneratorPath = std::string(LAKOSDIAGRAM_PYSCRIPTS_PATH) + "/cmake/codegenerator.py"; 0116 0117 SECTION("Basic package project without package groups") 0118 { 0119 PyDefaultGilReleasedContext _pyDefaultGilReleasedContext; 0120 0121 auto tmpDir = TmpDir{"basic_pkg_no_grp"}; 0122 auto dbPath = tmpDir.path() / "codedb.db"; 0123 auto ns = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0124 0125 auto *pkgA = ns.addPackage("pkgA", "pkgA").value(); 0126 auto *componentA = ns.addComponent("componentA", "pkgA/componentA", pkgA).value(); 0127 auto *pkgB = ns.addPackage("pkgB", "pkgB").value(); 0128 auto *componentB = ns.addComponent("componentB", "pkgB/componentB", pkgB).value(); 0129 ns.addPhysicalDependency(pkgA, pkgB).expect("Unexpected error on relationship pkgA->pkgB"); 0130 ns.addPhysicalDependency(componentA, componentB) 0131 .expect("Unexpected error on relationship componentA->componentB"); 0132 0133 auto outputDir = TmpDir{"cmake_out_dir"}; 0134 auto dataProvider = NodeStorageDataProvider{ns}; 0135 auto result = 0136 CodeGeneration::generateCodeFromScript(cmakeGeneratorPath, outputDir.path().string(), dataProvider); 0137 if (result.has_error()) { 0138 FAIL("ERROR MESSAGE: " + result.error().message); 0139 } 0140 0141 REQUIRE(std::filesystem::exists(outputDir.path() / "CMakeLists.txt")); 0142 REQUIRE(std::filesystem::exists(outputDir.path() / "pkgA" / "CMakeLists.txt")); 0143 REQUIRE(std::filesystem::exists(outputDir.path() / "pkgA" / "componentA.cpp")); 0144 REQUIRE(std::filesystem::exists(outputDir.path() / "pkgA" / "componentA.h")); 0145 REQUIRE(std::filesystem::exists(outputDir.path() / "pkgB" / "CMakeLists.txt")); 0146 REQUIRE(std::filesystem::exists(outputDir.path() / "pkgB" / "componentB.cpp")); 0147 REQUIRE(std::filesystem::exists(outputDir.path() / "pkgB" / "componentB.h")); 0148 } 0149 0150 SECTION("Generate code with package groups") 0151 { 0152 PyDefaultGilReleasedContext _pyDefaultGilReleasedContext; 0153 0154 auto tmpDir = TmpDir{"codegen_with_grps"}; 0155 auto dbPath = tmpDir.path() / "codedb.db"; 0156 auto ns = NodeStorageTestUtils::createEmptyNodeStorage(dbPath); 0157 0158 auto *pkgGrpA = ns.addPackage("pkgGrpA", "pkgGrpA").value(); 0159 auto *pkgA = ns.addPackage("pkgA", "pkgGrpA/pkgA", pkgGrpA).value(); 0160 auto *componentA = ns.addComponent("componentA", "pkgGrpA/pkgA/componentA", pkgA).value(); 0161 auto *pkgGrpB = ns.addPackage("pkgGrpB", "pkgGrpB").value(); 0162 auto *pkgB = ns.addPackage("pkgB", "pkgGrpB/pkgB", pkgGrpB).value(); 0163 auto *componentB = ns.addComponent("componentB", "pkgGrpB/pkgB/componentB", pkgB).value(); 0164 ns.addPhysicalDependency(pkgGrpA, pkgGrpB).expect("Unexpected error on relationship pkgGrpA->pkgGrpB"); 0165 ns.addPhysicalDependency(pkgA, pkgB).expect("Unexpected error on relationship pkgA->pkgB"); 0166 ns.addPhysicalDependency(componentA, componentB) 0167 .expect("Unexpected error on relationship componentA->componentB"); 0168 0169 auto outputDir = TmpDir{"cmake_out_dir"}; 0170 auto dataProvider = NodeStorageDataProvider{ns}; 0171 auto result = 0172 CodeGeneration::generateCodeFromScript(cmakeGeneratorPath, outputDir.path().string(), dataProvider); 0173 if (result.has_error()) { 0174 FAIL("ERROR MESSAGE: " + result.error().message); 0175 } 0176 0177 REQUIRE(std::filesystem::exists(outputDir.path() / "CMakeLists.txt")); 0178 REQUIRE(std::filesystem::exists(outputDir.path() / "pkgGrpA" / "CMakeLists.txt")); 0179 REQUIRE(std::filesystem::exists(outputDir.path() / "pkgGrpA" / "pkgA" / "CMakeLists.txt")); 0180 REQUIRE(std::filesystem::exists(outputDir.path() / "pkgGrpA" / "pkgA" / "componentA.cpp")); 0181 REQUIRE(std::filesystem::exists(outputDir.path() / "pkgGrpA" / "pkgA" / "componentA.h")); 0182 REQUIRE(std::filesystem::exists(outputDir.path() / "pkgGrpB" / "CMakeLists.txt")); 0183 REQUIRE(std::filesystem::exists(outputDir.path() / "pkgGrpB" / "pkgB" / "CMakeLists.txt")); 0184 REQUIRE(std::filesystem::exists(outputDir.path() / "pkgGrpB" / "pkgB" / "componentB.cpp")); 0185 REQUIRE(std::filesystem::exists(outputDir.path() / "pkgGrpB" / "pkgB" / "componentB.h")); 0186 } 0187 }