diff options
author | Oliver Reiche <oliver.reiche@huawei.com> | 2022-06-13 13:30:13 +0200 |
---|---|---|
committer | Oliver Reiche <oliver.reiche@huawei.com> | 2022-06-13 15:55:41 +0200 |
commit | 47bb77c39199ae4842f79347de076a4534f7d310 (patch) | |
tree | e8341770359bf656ce014cef1aa18554adbbbd63 /test | |
parent | be6b7b24e74e605533c0a1c4d3f32cd56f662ca7 (diff) | |
download | justbuild-47bb77c39199ae4842f79347de076a4534f7d310.tar.gz |
RepoConfig: Add tests for key computation
Diffstat (limited to 'test')
-rw-r--r-- | test/buildtool/common/TARGETS | 19 | ||||
-rw-r--r-- | test/buildtool/common/repository_config.test.cpp | 232 |
2 files changed, 250 insertions, 1 deletions
diff --git a/test/buildtool/common/TARGETS b/test/buildtool/common/TARGETS index 1487b1b3..8890b8b8 100644 --- a/test/buildtool/common/TARGETS +++ b/test/buildtool/common/TARGETS @@ -33,9 +33,26 @@ ] , "stage": ["test", "buildtool", "common"] } +, "repository_config": + { "type": ["@", "rules", "CC/test", "test"] + , "name": ["repository_config"] + , "srcs": ["repository_config.test.cpp"] + , "deps": + [ ["@", "catch2", "", "catch2"] + , ["test", "catch-main"] + , ["src/buildtool/common", "config"] + , ["test/utils", "local_hermeticity"] + ] + , "stage": ["test", "buildtool", "common"] + } , "TESTS": { "type": "install" , "tainted": ["test"] - , "deps": ["action_description", "artifact_description", "artifact_factory"] + , "deps": + [ "action_description" + , "artifact_description" + , "artifact_factory" + , "repository_config" + ] } } diff --git a/test/buildtool/common/repository_config.test.cpp b/test/buildtool/common/repository_config.test.cpp new file mode 100644 index 00000000..b62dbd68 --- /dev/null +++ b/test/buildtool/common/repository_config.test.cpp @@ -0,0 +1,232 @@ +#include "catch2/catch.hpp" +#include "src/buildtool/common/repository_config.hpp" +#include "test/utils/hermeticity/local.hpp" + +namespace { + +[[nodiscard]] auto GetTestDir() -> std::filesystem::path { + auto* tmp_dir = std::getenv("TEST_TMPDIR"); + if (tmp_dir != nullptr) { + return tmp_dir; + } + return FileSystemManager::GetCurrentDirectory() / "test/buildtool/common"; +} + +[[nodiscard]] auto GetGitRoot() -> FileRoot { + static std::atomic<int> counter{}; + auto repo_path = + GetTestDir() / "test_repo" / + std::filesystem::path{std::to_string(counter++)}.filename(); + REQUIRE(FileSystemManager::CreateDirectory(repo_path)); + auto anchor = FileSystemManager::ChangeDirectory(repo_path); + if (std::system("git init") == 0 and + std::system("git -c user.name='nobody' -c user.email='' " + "commit --allow-empty -m'init'") == 0) { + auto constexpr kEmptyTreeId = + "4b825dc642cb6eb9a060e54bf8d69288fbee4904"; + if (auto root = FileRoot::FromGit(repo_path, kEmptyTreeId)) { + return std::move(*root); + } + } + return FileRoot{"missing"}; +} + +[[nodiscard]] auto CreateFixedRepoInfo( + std::map<std::string, std::string> const& bindings = {}, + std::string const& tfn = "TARGETS", + std::string const& rfn = "RULES", + std::string const& efn = "EXPRESSIONS") { + static auto const kGitRoot = GetGitRoot(); + return RepositoryConfig::RepositoryInfo{ + kGitRoot, kGitRoot, kGitRoot, kGitRoot, bindings, tfn, rfn, efn}; +} + +[[nodiscard]] auto CreateFileRepoInfo( + std::map<std::string, std::string> const& bindings = {}, + std::string const& tfn = "TARGETS", + std::string const& rfn = "RULES", + std::string const& efn = "EXPRESSIONS") { + static auto const kFileRoot = FileRoot{"file path"}; + return RepositoryConfig::RepositoryInfo{ + kFileRoot, kFileRoot, kFileRoot, kFileRoot, bindings, tfn, rfn, efn}; +} + +template <class T> +[[nodiscard]] auto Copy(T const& x) -> T { + return x; +} + +// Read graph from CAS +[[nodiscard]] auto ReadGraph(std::string const& hash) -> nlohmann::json { + auto& cas = LocalCAS<ObjectType::File>::Instance(); + auto blob = cas.BlobPath(ArtifactDigest{hash, /*does not matter*/ 0}); + REQUIRE(blob); + auto content = FileSystemManager::ReadFile(*blob); + REQUIRE(content); + return nlohmann::json::parse(*content); +} + +// From [info0, info1, ...] and [bindings0, bindings1, ...] +// build graph: {"0": (info0 + bindings0), "1": (info1 + bindings1), ...} +[[nodiscard]] auto BuildGraph( + std::vector<RepositoryConfig::RepositoryInfo const*> const& infos, + std::vector<std::unordered_map<std::string, int>> const& bindings) + -> nlohmann::json { + auto graph = nlohmann::json::object(); + for (std::size_t i{}; i < infos.size(); ++i) { + auto entry = infos[i]->BaseContentDescription(); + REQUIRE(entry); + (*entry)["bindings"] = bindings[i]; + graph[std::to_string(i)] = std::move(*entry); + } + return graph; +} + +} // namespace + +TEST_CASE_METHOD(HermeticLocalTestFixture, + "Test missing repository", + "[repository_config]") { + auto& config = RepositoryConfig::Instance(); + config.Reset(); + + CHECK(config.Info("missing") == nullptr); + CHECK_FALSE(config.RepositoryKey("missing")); +} + +TEST_CASE_METHOD(HermeticLocalTestFixture, + "Compute key of fixed repository", + "[repository_config]") { + auto& config = RepositoryConfig::Instance(); + config.Reset(); + + SECTION("for single fixed repository") { + config.SetInfo("foo", CreateFixedRepoInfo()); + auto key = config.RepositoryKey("foo"); + REQUIRE(key); + + // verify created graph from CAS + CHECK(ReadGraph(*key) == BuildGraph({config.Info("foo")}, {{}})); + } + + SECTION("for fixed repositories with same missing dependency") { + config.SetInfo("foo", CreateFixedRepoInfo({{"dep", "baz"}})); + config.SetInfo("bar", CreateFixedRepoInfo({{"dep", "baz"}})); + CHECK_FALSE(config.RepositoryKey("foo")); + CHECK_FALSE(config.RepositoryKey("bar")); + } + + SECTION("for fixed repositories with different missing dependency") { + config.SetInfo("foo", CreateFixedRepoInfo({{"dep", "baz0"}})); + config.SetInfo("bar", CreateFixedRepoInfo({{"dep", "baz1"}})); + CHECK_FALSE(config.RepositoryKey("foo")); + CHECK_FALSE(config.RepositoryKey("bar")); + } +} + +TEST_CASE_METHOD(HermeticLocalTestFixture, + "Compute key of file repository", + "[repository_config]") { + auto& config = RepositoryConfig::Instance(); + config.Reset(); + + SECTION("for single file repository") { + config.SetInfo("foo", CreateFileRepoInfo()); + CHECK_FALSE(config.RepositoryKey("foo")); + } + + SECTION("for graph with leaf dependency as file") { + config.SetInfo("foo", CreateFixedRepoInfo({{"bar", "bar"}})); + config.SetInfo("bar", CreateFixedRepoInfo({{"baz", "baz"}})); + config.SetInfo("baz", CreateFileRepoInfo()); + CHECK_FALSE(config.RepositoryKey("foo")); + } +} + +TEST_CASE_METHOD(HermeticLocalTestFixture, + "Compare key of two repos with same content", + "[repository_config]") { + auto& config = RepositoryConfig::Instance(); + config.Reset(); + + // create two different repo infos with same content (baz should be same) + config.SetInfo("foo", CreateFixedRepoInfo({{"dep", "baz0"}})); + config.SetInfo("bar", CreateFixedRepoInfo({{"dep", "baz1"}})); + + SECTION("with leaf dependency") { + // create duplicate leaf repo info with global name 'baz0' and 'baz1' + auto baz = CreateFixedRepoInfo(); + config.SetInfo("baz0", Copy(baz)); + config.SetInfo("baz1", Copy(baz)); + + // check if computed key is same + auto foo_key = config.RepositoryKey("foo"); + auto bar_key = config.RepositoryKey("bar"); + REQUIRE(foo_key); + REQUIRE(bar_key); + CHECK(*foo_key == *bar_key); + + // verify created graph from CAS + CHECK(ReadGraph(*foo_key) == + BuildGraph({config.Info("foo"), config.Info("baz0")}, + {{{"dep", 1}}, {}})); + } + + SECTION("with cyclic dependency") { + // create duplicate cyclic repo info with global name 'baz0' and 'baz1' + auto baz = CreateFixedRepoInfo({{"foo", "foo"}, {"bar", "bar"}}); + config.SetInfo("baz0", Copy(baz)); + config.SetInfo("baz1", Copy(baz)); + + // check if computed key is same + auto foo_key = config.RepositoryKey("foo"); + auto bar_key = config.RepositoryKey("bar"); + REQUIRE(foo_key); + REQUIRE(bar_key); + CHECK(*foo_key == *bar_key); + + // verify created graph from CAS + CHECK(ReadGraph(*foo_key) == + BuildGraph({config.Info("foo"), config.Info("baz0")}, + {{{"dep", 1}}, {{"foo", 0}, {"bar", 0}}})); + } + + SECTION("with two separate cyclic graphs") { + // create two cyclic repo infos producing two separate graphs + config.SetInfo("baz0", CreateFixedRepoInfo({{"dep", "foo"}})); + config.SetInfo("baz1", CreateFixedRepoInfo({{"dep", "bar"}})); + + // check if computed key is same + auto foo_key = config.RepositoryKey("foo"); + auto bar_key = config.RepositoryKey("bar"); + REQUIRE(foo_key); + REQUIRE(bar_key); + CHECK(*foo_key == *bar_key); + + // verify created graph from CAS + CHECK(ReadGraph(*foo_key) == + BuildGraph({config.Info("foo")}, {{{"dep", 0}}})); + } + + SECTION("for graph with leaf repos refering to themselfs") { + config.SetInfo("baz0", CreateFixedRepoInfo({{"dep", "baz0"}})); + config.SetInfo("baz1", CreateFixedRepoInfo({{"dep", "baz1"}})); + + // check if computed key is same + auto foo_key = config.RepositoryKey("foo"); + auto bar_key = config.RepositoryKey("bar"); + auto baz0_key = config.RepositoryKey("baz0"); + auto baz1_key = config.RepositoryKey("baz1"); + CHECK(foo_key); + CHECK(bar_key); + CHECK(baz0_key); + CHECK(baz1_key); + CHECK(*foo_key == *bar_key); + CHECK(*bar_key == *baz0_key); + CHECK(*baz0_key == *baz1_key); + + // verify created graph from CAS + CHECK(ReadGraph(*foo_key) == + BuildGraph({config.Info("foo")}, {{{"dep", 0}}})); + } +} |