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 }