#include import cargoxx.manifest; import cargoxx.util; import std; using cargoxx::manifest::BuildSettings; using cargoxx::manifest::Dependency; using cargoxx::manifest::Edition; using cargoxx::manifest::Manifest; using cargoxx::manifest::Package; using cargoxx::manifest::parse; using cargoxx::manifest::write; namespace { auto tmp_path() -> std::filesystem::path { auto dir = std::filesystem::temp_directory_path() / std::format("cargoxx-write-test-{}", std::random_device{}()); std::filesystem::create_directories(dir); return dir / "Cargoxx.toml"; } auto pkg(std::string name, std::string version, Edition ed = Edition::Cpp23, std::vector authors = {}, std::optional license = std::nullopt) -> Package { return Package{ .name = std::move(name), .version = std::move(version), .edition = ed, .authors = std::move(authors), .license = std::move(license), }; } auto dep(std::string name, std::string version, std::vector components = {}) -> Dependency { return Dependency{ .name = std::move(name), .version_spec = std::move(version), .components = std::move(components), }; } auto round_trip(const Manifest& m) -> Manifest { auto path = tmp_path(); REQUIRE(write(m, path).has_value()); auto parsed = parse(path); REQUIRE(parsed.has_value()); return *parsed; } } // namespace TEST_CASE("write round-trips a minimal manifest", "[manifest][write]") { Manifest m{pkg("foo", "0.1.0"), {}, {}}; REQUIRE(round_trip(m) == m); } TEST_CASE("write round-trips authors and license", "[manifest][write]") { Manifest m{pkg("foo", "0.1.0", Edition::Cpp20, {"Ada", "Grace"}, "MIT"), {}, {}}; REQUIRE(round_trip(m) == m); } TEST_CASE("write round-trips a string-form dependency", "[manifest][write]") { Manifest m{pkg("foo", "0.1.0"), {dep("fmt", "10.2")}, {}}; REQUIRE(round_trip(m) == m); } TEST_CASE("write round-trips a table-form dependency with components", "[manifest][write]") { Manifest m{pkg("foo", "0.1.0"), {dep("boost", "1.84", {"filesystem", "system"})}, {}}; REQUIRE(round_trip(m) == m); } TEST_CASE("write sorts dependencies alphabetically (matches Cargo)", "[manifest][write]") { Manifest m{ pkg("foo", "0.1.0"), { dep("fmt", "10.2"), dep("spdlog", "1.13"), dep("boost", "1.84", {"filesystem"}), }, {}, }; auto rt = round_trip(m); REQUIRE(rt.dependencies.size() == 3); REQUIRE(rt.dependencies[0].name == "boost"); REQUIRE(rt.dependencies[1].name == "fmt"); REQUIRE(rt.dependencies[2].name == "spdlog"); } TEST_CASE("write round-trips build settings", "[manifest][write]") { Manifest m{ pkg("foo", "0.1.0"), {}, BuildSettings{.warnings_as_errors = true, .sanitizers = {"address", "undefined"}}, }; REQUIRE(round_trip(m) == m); } TEST_CASE("write omits empty optional sections", "[manifest][write]") { Manifest m{pkg("foo", "0.1.0"), {}, {}}; auto path = tmp_path(); REQUIRE(write(m, path).has_value()); std::ifstream in{path}; std::string content{std::istreambuf_iterator(in), {}}; REQUIRE(content.find("[dependencies]") == std::string::npos); REQUIRE(content.find("[build]") == std::string::npos); } TEST_CASE("write overwrites an existing file", "[manifest][write]") { auto path = tmp_path(); std::ofstream{path} << "# stale content\n[package]\nname = \"old\"\n"; Manifest m{pkg("new", "1.0.0"), {}, {}}; REQUIRE(write(m, path).has_value()); auto parsed = parse(path); REQUIRE(parsed.has_value()); REQUIRE(parsed->package.name == "new"); } TEST_CASE("write fails when the target directory does not exist", "[manifest][write]") { Manifest m{pkg("foo", "0.1.0"), {}, {}}; auto r = write(m, "/nonexistent/dir/Cargoxx.toml"); REQUIRE_FALSE(r.has_value()); }