[M5+] add resolver::nixpkgs_git_resolve fallback

This commit is contained in:
2026-05-10 12:19:25 +00:00
parent df2c25b559
commit cb82e918d8
7 changed files with 622 additions and 0 deletions

View File

@@ -0,0 +1,115 @@
// Unit + fixture-based test for resolver::nixpkgs_git_resolve. Avoids
// the multi-GB nixos/nixpkgs clone by building a tiny throwaway repo
// that mirrors the relevant slice of the nixpkgs layout.
#include <catch2/catch_test_macros.hpp>
import cargoxx.resolver;
import cargoxx.exec;
import cargoxx.util;
import std;
using cargoxx::resolver::nixpkgs_git_resolve;
using cargoxx::resolver::pick_youngest_commit;
namespace {
auto run_git(const std::filesystem::path& cwd, std::vector<std::string> args)
-> int {
auto r = cargoxx::exec::run(
"git", args,
cargoxx::exec::ExecOptions{
.cwd = cwd,
.env_overrides = {{"GIT_COMMITTER_NAME", "t"},
{"GIT_COMMITTER_EMAIL", "t@t"},
{"GIT_AUTHOR_NAME", "t"},
{"GIT_AUTHOR_EMAIL", "t@t"},
{"GIT_COMMITTER_DATE", "1700000000 +0000"},
{"GIT_AUTHOR_DATE", "1700000000 +0000"}},
.timeout = std::chrono::seconds{30},
.inherit_stdio = false,
});
REQUIRE(r.has_value());
return r->exit_code;
}
auto write_text(const std::filesystem::path& p, std::string_view content) {
std::filesystem::create_directories(p.parent_path());
std::ofstream{p} << content;
}
auto fixture_repo(std::string_view pkg, std::string_view v1,
std::string_view v2) -> std::filesystem::path {
auto root = std::filesystem::temp_directory_path() /
std::format("cargoxx-git-fixture-{}", std::random_device{}());
std::filesystem::create_directories(root);
REQUIRE(run_git(root, {"init", "-q", "-b", "main"}) == 0);
auto nix_path =
root / "pkgs" / "development" / "libraries" / std::string{pkg} / "default.nix";
write_text(nix_path, std::format("{{ stdenv }}: stdenv.mkDerivation {{ "
"version = \"{}\"; }}\n",
v1));
REQUIRE(run_git(root, {"add", "."}) == 0);
REQUIRE(run_git(root, {"commit", "-q", "-m", std::format("init {}", v1)}) == 0);
write_text(nix_path, std::format("{{ stdenv }}: stdenv.mkDerivation {{ "
"version = \"{}\"; }}\n",
v2));
REQUIRE(run_git(root, {"add", "."}) == 0);
REQUIRE(run_git(root, {"commit", "-q", "-m", std::format("bump {}", v2)}) == 0);
return root;
}
} // namespace
TEST_CASE("pick_youngest_commit picks the highest-timestamp SHA",
"[resolver][git]") {
constexpr std::string_view input =
"abc111 1700000000\n"
"def222 1800000000\n"
"ghi333 1750000000\n";
auto r = pick_youngest_commit(input);
REQUIRE(r == std::string{"def222"});
}
TEST_CASE("pick_youngest_commit returns nullopt on empty input",
"[resolver][git]") {
REQUIRE_FALSE(pick_youngest_commit("").has_value());
REQUIRE_FALSE(pick_youngest_commit("\n\n").has_value());
}
TEST_CASE("pick_youngest_commit skips malformed lines", "[resolver][git]") {
constexpr std::string_view input =
"garbage\n"
"abc111 1700000000\n"
"no-timestamp\n";
auto r = pick_youngest_commit(input);
REQUIRE(r == std::string{"abc111"});
}
TEST_CASE("nixpkgs_git_resolve finds the introducing commit",
"[resolver][git]") {
auto repo = fixture_repo("fmt", "10.2.0", "10.3.0");
auto r = nixpkgs_git_resolve("fmt", "10.2.0", repo);
REQUIRE(r.has_value());
REQUIRE(r->size() == 40); // git sha
std::error_code ec;
std::filesystem::remove_all(repo, ec);
}
TEST_CASE("nixpkgs_git_resolve returns ResolutionVersionNotFound for an absent version",
"[resolver][git]") {
auto repo = fixture_repo("fmt", "10.2.0", "10.3.0");
auto r = nixpkgs_git_resolve("fmt", "99.99.99", repo);
REQUIRE_FALSE(r.has_value());
REQUIRE(r.error().code ==
cargoxx::util::ErrorCode::ResolutionVersionNotFound);
std::error_code ec;
std::filesystem::remove_all(repo, ec);
}