File indexing completed on 2024-05-19 05:42:10
0001 // ct_lvtmdb_merge_multiple_db.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_namespaceobject.h> 0022 #include <ct_lvtmdb_objectstore.h> 0023 #include <ct_lvtmdb_packageobject.h> 0024 0025 #include <ct_lvtmdb_soci_helper.h> 0026 #include <ct_lvtmdb_soci_reader.h> 0027 #include <ct_lvtmdb_soci_writer.h> 0028 0029 #include <filesystem> 0030 #include <iostream> 0031 #include <unordered_set> 0032 0033 #include <catch2-local-includes.h> 0034 0035 namespace fs = std::filesystem; 0036 0037 using namespace Codethink::lvtmdb; 0038 0039 namespace { 0040 const fs::path projectPath = fs::temp_directory_path(); 0041 const auto tmpDBPathA = (projectPath / "database_1.db").string(); 0042 const auto tmpDbPathB = (projectPath / "database_2.db").string(); 0043 const auto tmpDbPathC = (projectPath / "database_3.db").string(); 0044 const auto tmpDbPathD = (projectPath / "database_4.db").string(); 0045 const auto resultingDb = (projectPath / "resulting.db").string(); 0046 } // namespace 0047 0048 void remove_temporary_files() 0049 { 0050 for (const auto& file : {tmpDBPathA, tmpDbPathB, tmpDbPathC, tmpDbPathD, resultingDb}) { 0051 if (std::filesystem::exists(file)) { 0052 std::filesystem::remove(file); 0053 } 0054 } 0055 } 0056 0057 void create_temporary_dataases() 0058 { 0059 // idea: use two different stores, add a bunch of random data at it 0060 // then save 4 distinct versions of it, two for db1, 2 for db2 0061 // then merge them and check if the results are sane. 0062 ObjectStore store; 0063 auto mtx = store.rwLock(); 0064 auto repo = store.getOrAddRepository("test_repository", "/home/ble/projects/repo"); 0065 auto pkg_a = store.getOrAddPackage("root_pkg_a", "root_a", "/home/ble/projects/repo/root_a", nullptr, repo); 0066 auto pkg_b = store.getOrAddPackage("root_pkg_b", "root_b", "/home/ble/projects/repo/root_b", nullptr, repo); 0067 auto pkg_a_pkga = 0068 store.getOrAddPackage("root_pkg_a/pkga", "pkga", "/home/ble/projects/repo/root_a/pkga", pkg_a, repo); 0069 auto pkg_a_pkgb = 0070 store.getOrAddPackage("root_pkg_a/pkgb", "pkgb", "/home/ble/projects/repo/root_a/pkgb", pkg_a, repo); 0071 auto pkg_b_pkga = 0072 store.getOrAddPackage("root_pkg_b/pkga", "pkga", "/home/ble/projects/repo/root_b/pkga", pkg_b, repo); 0073 auto pkg_b_pkgb = 0074 store.getOrAddPackage("root_pkg_b/pkgb", "pkgb", "/home/ble/projects/repo/root_b/pkgb", pkg_b, repo); 0075 0076 auto pkg_a_pkga_comp_a = store.getOrAddComponent("root_pkg_a/pkga/comp_a", "comp_a", pkg_a_pkga); 0077 auto pkg_a_pkga_comp_b = store.getOrAddComponent("root_pkg_a/pkga/comp_b", "comp_b", pkg_a_pkga); 0078 auto pkg_a_pkgb_comp_a = store.getOrAddComponent("root_pkg_a/pkgb/comp_a", "comp_a", pkg_a_pkgb); 0079 auto pkg_a_pkgb_comp_b = store.getOrAddComponent("root_pkg_a/pkgb/comp_b", "comp_b", pkg_a_pkgb); 0080 auto pkg_b_pkga_comp_a = store.getOrAddComponent("root_pkg_b/pkga/comp_a", "comp_a", pkg_b_pkga); 0081 auto pkg_b_pkga_comp_b = store.getOrAddComponent("root_pkg_b/pkga/comp_b", "comp_b", pkg_b_pkga); 0082 auto pkg_b_pkgb_comp_a = store.getOrAddComponent("root_pkg_b/pkgb/comp_a", "comp_a", pkg_b_pkgb); 0083 auto pkg_b_pkgb_comp_b = store.getOrAddComponent("root_pkg_b/pkgb/comp_b", "comp_b", pkg_b_pkgb); 0084 0085 // Files are just created and we don't need to keep track of them (hopefully)' 0086 store.getOrAddFile("/home/ble/projects/repo/root_a/pkga/comp_a.cpp", 0087 "compa.cpp", 0088 false, 0089 {}, 0090 pkg_a_pkga, 0091 pkg_a_pkga_comp_a); 0092 store.getOrAddFile("/home/ble/projects/repo/root_a/pkga/comp_b.cpp", 0093 "compb.cpp", 0094 false, 0095 {}, 0096 pkg_a_pkga, 0097 pkg_a_pkga_comp_b); 0098 store.getOrAddFile("/home/ble/projects/repo/root_a/pkgb/comp_a.cpp", 0099 "compa.cpp", 0100 false, 0101 {}, 0102 pkg_a_pkgb, 0103 pkg_a_pkgb_comp_a); 0104 store.getOrAddFile("/home/ble/projects/repo/root_a/pkgb/comp_b.cpp", 0105 "compa.cpp", 0106 false, 0107 {}, 0108 pkg_a_pkgb, 0109 pkg_a_pkgb_comp_b); 0110 store.getOrAddFile("/home/ble/projects/repo/root_b/pkga/comp_a.cpp", 0111 "compa.cpp", 0112 false, 0113 {}, 0114 pkg_a_pkga, 0115 pkg_b_pkga_comp_a); 0116 store.getOrAddFile("/home/ble/projects/repo/root_b/pkga/comp_b.cpp", 0117 "compa.cpp", 0118 false, 0119 {}, 0120 pkg_a_pkga, 0121 pkg_b_pkga_comp_b); 0122 store.getOrAddFile("/home/ble/projects/repo/root_b/pkgb/comp_a.cpp", 0123 "compa.cpp", 0124 false, 0125 {}, 0126 pkg_a_pkga, 0127 pkg_b_pkgb_comp_a); 0128 store.getOrAddFile("/home/ble/projects/repo/root_b/pkgb/comp_b.cpp", 0129 "compa.cpp", 0130 false, 0131 {}, 0132 pkg_a_pkga, 0133 pkg_b_pkgb_comp_b); 0134 0135 auto external_dep = 0136 store.getOrAddPackage("external_dep", "external_dep", "/home/ble/projects/external_dep", nullptr, nullptr); 0137 0138 auto external_dep_comp_common = store.getOrAddComponent("external_dep/comp_common", "comp_common", external_dep); 0139 ComponentObject::addDependency(pkg_a_pkga_comp_a, external_dep_comp_common); 0140 0141 { // Save one db. 0142 SociWriter writer; 0143 writer.createOrOpen(tmpDBPathA); 0144 store.writeToDatabase(writer); 0145 } 0146 0147 auto external_dep_comp_common_private = 0148 store.getOrAddComponent("external_dep/comp_common_private", "comp_common_private", external_dep); 0149 ComponentObject::addDependency(external_dep_comp_common, external_dep_comp_common_private); 0150 0151 // Add more data on the ObjectStore, save it somewhere else. 0152 auto repo2 = store.getOrAddRepository("test_another_repo", "/home/ble/projects/repo2"); 0153 auto pkg_repo2 = 0154 store.getOrAddPackage("repo2_pkg", "repo2_pkg", "/home/ble/projects/repo/repo2pkg", nullptr, repo2); 0155 auto pkg_repo2_pkga = store.getOrAddPackage("repo2_pkg/pkgabla", 0156 "pkgabla", 0157 "/home/ble/projects/repo/repo2pkg/pkgbla", 0158 pkg_repo2, 0159 repo2); 0160 auto pkg2_a_pkga_comp_a = store.getOrAddComponent("/repo2_pkg/repo2_pkg/pkgabla/comp_a", "comp_a", pkg_repo2_pkga); 0161 store.getOrAddFile("/home/ble/projects//repo2_pkg/repo2_pkg/pkgabla/compa.cpp", 0162 "compa.cpp", 0163 false, 0164 {}, 0165 pkg_repo2_pkga, 0166 pkg2_a_pkga_comp_a); 0167 0168 { // Save second db. 0169 SociWriter writer; 0170 writer.createOrOpen(tmpDbPathB); 0171 store.writeToDatabase(writer); 0172 } 0173 0174 ObjectStore 0175 store2; // smaller store, with a few of the same values - same packages as the first db but has a new component. 0176 0177 auto lock2 = store2.rwLock(); 0178 auto repo_3 = store2.getOrAddRepository("test_repository", "/home/ble/projects/repo"); 0179 auto pkg3_a = store2.getOrAddPackage("root_pkg_a", "root_a", "/home/ble/projects/repo/root_a", nullptr, repo_3); 0180 auto pkg3_a_pkga = 0181 store2.getOrAddPackage("root_pkg_a/pkga", "pkga", "/home/ble/projects/repo/root_a/pkga", pkg3_a, repo_3); 0182 store2.getOrAddComponent("root_pkg_a/pkga/comp_a", "comp_a", pkg3_a_pkga); 0183 store2.getOrAddComponent("root_pkg_a/pkga/comp_diff", "comp_diff", pkg3_a_pkga); 0184 0185 { // Save third db. 0186 SociWriter writer; 0187 writer.createOrOpen(tmpDbPathC); 0188 store.writeToDatabase(writer); 0189 } 0190 0191 // Add more data on the ObjectStore, save it somewhere else. 0192 auto pkg4_repo = 0193 store2.getOrAddPackage("repo4_pkg", "repo3_pkg", "/home/ble/projects/repo/repo2pkg", nullptr, repo_3); 0194 auto pkg4_repo2_pkga = 0195 store2.getOrAddPackage("repo4_pkg", "pkgabla", "/home/ble/projects/repo/repo2pkg", pkg4_repo, repo_3); 0196 auto pkg4_a_pkga_comp_a = store2.getOrAddComponent("root_pkg_a/pkga/comp_a", "comp_a", pkg4_repo2_pkga); 0197 store2.getOrAddFile("/home/ble/projects/repo/root_a/pkga/compa.cpp", 0198 "compa.cpp", 0199 false, 0200 {}, 0201 pkg4_repo2_pkga, 0202 pkg4_a_pkga_comp_a); 0203 0204 { // Save fourth db. 0205 SociWriter writer; 0206 writer.createOrOpen(tmpDbPathD); 0207 store2.writeToDatabase(writer); 0208 } 0209 } 0210 0211 void merge_in_disk_dbs() 0212 { 0213 // Now, iterate over all databases, *not* keeping all of them in memory at the same time, 0214 // Note that I didn't just dumped everything on disk before because this is to make sure 0215 // merging different in - disk databases will work.' 0216 for (const auto& db_file : {tmpDBPathA, tmpDbPathB, tmpDbPathC, tmpDbPathD}) { 0217 SociReader reader; 0218 ObjectStore readerStore; 0219 auto res = readerStore.readFromDatabase(reader, db_file); 0220 if (res.has_error()) { 0221 std::cout << res.error().what << "\n"; 0222 } 0223 SociWriter writer; 0224 writer.createOrOpen(resultingDb); 0225 readerStore.writeToDatabase(writer); 0226 } 0227 } 0228 0229 void validate_in_disk_db() 0230 { 0231 SociReader reader; 0232 ObjectStore readerStore; 0233 auto res = readerStore.readFromDatabase(reader, resultingDb); 0234 0235 if (res.has_error()) { 0236 std::cout << res.error().what << "\n"; 0237 } 0238 0239 auto lock = readerStore.readOnlyLock(); 0240 REQUIRE(readerStore.getRepository("test_repository")); 0241 REQUIRE(readerStore.getRepository("test_another_repo")); 0242 0243 auto packages = readerStore.getAllPackages(); 0244 auto files = readerStore.getAllFiles(); 0245 0246 auto comp_common = readerStore.getComponent("external_dep/comp_common"); 0247 auto comp_common_private = readerStore.getComponent("external_dep/comp_common_private"); 0248 auto comp_a = readerStore.getComponent("root_pkg_a/pkga/comp_a"); 0249 comp_a->withROLock([comp_a, comp_common] { 0250 REQUIRE(comp_a->forwardDependencies().size() == 1); 0251 REQUIRE(comp_a->forwardDependencies()[0] == comp_common); 0252 }); 0253 0254 comp_common->withROLock([comp_common, comp_common_private] { 0255 REQUIRE(comp_common->forwardDependencies().size() == 1); 0256 REQUIRE(comp_common->forwardDependencies()[0] == comp_common_private); 0257 }); 0258 0259 REQUIRE(packages.size() == 10); 0260 REQUIRE(files.size() == 10); 0261 } 0262 0263 TEST_CASE("Merge Multiple In-Disk Databases") 0264 { 0265 remove_temporary_files(); 0266 create_temporary_dataases(); 0267 merge_in_disk_dbs(); 0268 validate_in_disk_db(); 0269 }