#include import cargoxx.linkdb; import cargoxx.util; import std; using cargoxx::linkdb::Database; using cargoxx::linkdb::Recipe; using cargoxx::linkdb::expand_targets; using cargoxx::linkdb::substitute_components; using cargoxx::util::ErrorCode; namespace { auto fresh_overlay() -> std::filesystem::path { auto d = std::filesystem::temp_directory_path() / std::format("cargoxx-linkdb-test-{}", std::random_device{}()); std::filesystem::create_directories(d); return d / "overlay.sqlite"; } auto open_db() -> Database { auto r = Database::open(fresh_overlay()); REQUIRE(r.has_value()); return std::move(*r); } } // namespace TEST_CASE("Database::open loads the curated linkdb", "[linkdb]") { auto db = open_db(); (void)db; } TEST_CASE("resolve returns the curated recipe for fmt 10", "[linkdb]") { auto db = open_db(); auto rec = db.resolve("fmt", "10.2.0"); REQUIRE(rec.has_value()); REQUIRE(rec->nixpkgs_attr == "fmt_10"); REQUIRE(rec->find_package == "fmt CONFIG REQUIRED"); REQUIRE(rec->targets == std::vector{"fmt::fmt"}); REQUIRE(rec->source == "curated"); } TEST_CASE("resolve returns the older fmt recipe for fmt 8", "[linkdb]") { auto db = open_db(); auto rec = db.resolve("fmt", "8.1.0"); REQUIRE(rec.has_value()); REQUIRE(rec->nixpkgs_attr == "fmt_8"); } TEST_CASE("resolve fails for an unknown package", "[linkdb]") { auto db = open_db(); auto rec = db.resolve("obscurelib", "0.0.1"); REQUIRE_FALSE(rec.has_value()); REQUIRE(rec.error().code == ErrorCode::LinkdbUnknownPackage); } TEST_CASE("resolve substitutes boost components", "[linkdb]") { auto db = open_db(); auto rec = db.resolve("boost", "1.84.0", {"filesystem", "system"}); REQUIRE(rec.has_value()); REQUIRE(rec->find_package == "Boost REQUIRED COMPONENTS filesystem system"); REQUIRE(rec->targets == std::vector{"Boost::filesystem", "Boost::system"}); } TEST_CASE("resolve fails when a componentized package gets no components", "[linkdb]") { auto db = open_db(); auto rec = db.resolve("boost", "1.84.0"); REQUIRE_FALSE(rec.has_value()); REQUIRE(rec.error().code == ErrorCode::LinkdbComponentNotSupported); } TEST_CASE("resolve fails when components are passed to a non-componentized package", "[linkdb]") { auto db = open_db(); auto rec = db.resolve("fmt", "10.2.0", {"core"}); REQUIRE_FALSE(rec.has_value()); REQUIRE(rec.error().code == ErrorCode::LinkdbComponentNotSupported); } TEST_CASE("resolve handles wildcard versions", "[linkdb]") { auto db = open_db(); auto rec = db.resolve("openssl", "3.2.0"); REQUIRE(rec.has_value()); REQUIRE(rec->find_package == "OpenSSL REQUIRED"); REQUIRE(rec->targets == std::vector{"OpenSSL::SSL", "OpenSSL::Crypto"}); } TEST_CASE("resolve covers all 25 curated packages", "[linkdb]") { auto db = open_db(); struct Sample { std::string name; std::string version; std::vector components; }; const std::vector samples = { {"fmt", "10.2.0", {}}, {"spdlog", "1.13.0", {}}, {"nlohmann_json", "3.11.0", {}}, {"boost", "1.84.0", {"system"}}, {"openssl", "3.2.0", {}}, {"zlib", "1.3.0", {}}, {"sqlite3", "3.45.0", {}}, {"curl", "8.5.0", {}}, {"protobuf", "25.0.0", {}}, {"grpc", "1.60.0", {}}, {"abseil-cpp", "20240116.0", {"strings"}}, {"gtest", "1.14.0", {}}, {"catch2", "3.5.0", {}}, {"eigen", "3.4.0", {}}, {"tbb", "2021.10.0", {}}, {"libpng", "1.6.40", {}}, {"libjpeg", "3.0.1", {}}, {"freetype", "2.13.2", {}}, {"glfw", "3.3.9", {}}, {"glm", "0.9.9.8", {}}, {"sdl2", "2.28.5", {}}, {"cli11", "2.4.1", {}}, {"cxxopts", "3.2.0", {}}, {"range-v3", "0.12.0", {}}, {"magic_enum", "0.9.5", {}}, }; for (const auto& s : samples) { auto rec = db.resolve(s.name, s.version, s.components); INFO("resolving " << s.name); REQUIRE(rec.has_value()); REQUIRE_FALSE(rec->nixpkgs_attr.empty()); REQUIRE_FALSE(rec->find_package.empty()); REQUIRE_FALSE(rec->targets.empty()); } } TEST_CASE("substitute_components is a no-op when marker is absent", "[linkdb][substitute]") { REQUIRE(substitute_components("foo bar", {"a", "b"}) == "foo bar"); } TEST_CASE("substitute_components joins components with spaces", "[linkdb][substitute]") { REQUIRE(substitute_components("X {{components}} Y", {"a", "b", "c"}) == "X a b c Y"); } TEST_CASE("expand_targets fans out per-component templates", "[linkdb][substitute]") { REQUIRE(expand_targets({"Boost::{{component}}"}, {"filesystem", "system"}) == std::vector{"Boost::filesystem", "Boost::system"}); } TEST_CASE("expand_targets keeps non-templated targets verbatim", "[linkdb][substitute]") { REQUIRE(expand_targets({"OpenSSL::SSL", "OpenSSL::Crypto"}, {}) == std::vector{"OpenSSL::SSL", "OpenSSL::Crypto"}); }