#include import cargoxx.cli; import cargoxx.manifest; import cargoxx.util; import std; using cargoxx::cli::cmd_new; using cargoxx::util::ErrorCode; namespace manifest = cargoxx::manifest; namespace { auto fresh_dir() -> std::filesystem::path { auto d = std::filesystem::temp_directory_path() / std::format("cargoxx-cmd_new-test-{}", std::random_device{}()); std::filesystem::create_directories(d); return d; } auto read_file(const std::filesystem::path& p) -> std::string { std::ifstream in{p}; return std::string{std::istreambuf_iterator(in), {}}; } } // namespace TEST_CASE("cmd_new --bin scaffolds a binary project", "[cli][new]") { auto parent = fresh_dir(); REQUIRE(cmd_new("hello", false, parent).has_value()); auto root = parent / "hello"; REQUIRE(std::filesystem::exists(root / "Cargoxx.toml")); REQUIRE(std::filesystem::exists(root / "src" / "main.cpp")); REQUIRE_FALSE(std::filesystem::exists(root / "src" / "lib.cppm")); REQUIRE(std::filesystem::exists(root / ".gitignore")); auto m = manifest::parse(root / "Cargoxx.toml"); REQUIRE(m.has_value()); REQUIRE(m->package.name == "hello"); REQUIRE(m->package.version == "0.1.0"); REQUIRE(m->package.edition == manifest::Edition::Cpp23); auto main_src = read_file(root / "src" / "main.cpp"); REQUIRE(main_src.find("import std;") != std::string::npos); REQUIRE(main_src.find("\"hello\"") != std::string::npos); } TEST_CASE("cmd_new --lib scaffolds a library project", "[cli][new]") { auto parent = fresh_dir(); REQUIRE(cmd_new("widget", true, parent).has_value()); auto root = parent / "widget"; REQUIRE(std::filesystem::exists(root / "src" / "lib.cppm")); REQUIRE_FALSE(std::filesystem::exists(root / "src" / "main.cpp")); auto lib_src = read_file(root / "src" / "lib.cppm"); REQUIRE(lib_src.find("export module widget;") != std::string::npos); REQUIRE(lib_src.find("export namespace widget") != std::string::npos); REQUIRE(lib_src.find("Hello from widget!") != std::string::npos); } TEST_CASE("cmd_new replaces hyphens with underscores in module name", "[cli][new]") { auto parent = fresh_dir(); REQUIRE(cmd_new("my-project", true, parent).has_value()); auto m = manifest::parse(parent / "my-project" / "Cargoxx.toml"); REQUIRE(m->package.name == "my-project"); auto lib_src = read_file(parent / "my-project" / "src" / "lib.cppm"); REQUIRE(lib_src.find("export module my_project;") != std::string::npos); REQUIRE(lib_src.find("Hello from my-project!") != std::string::npos); } TEST_CASE("cmd_new fails when target directory exists", "[cli][new]") { auto parent = fresh_dir(); std::filesystem::create_directories(parent / "exists"); auto r = cmd_new("exists", false, parent); REQUIRE_FALSE(r.has_value()); REQUIRE(r.error().code == ErrorCode::Internal); } TEST_CASE("cmd_new rejects an empty name", "[cli][new]") { auto parent = fresh_dir(); auto r = cmd_new("", false, parent); REQUIRE_FALSE(r.has_value()); REQUIRE(r.error().code == ErrorCode::LayoutInvalidName); } TEST_CASE("cmd_new rejects a name starting with a digit", "[cli][new]") { auto parent = fresh_dir(); auto r = cmd_new("1bad", false, parent); REQUIRE_FALSE(r.has_value()); REQUIRE(r.error().code == ErrorCode::LayoutInvalidName); } TEST_CASE("cmd_new rejects a name with a space", "[cli][new]") { auto parent = fresh_dir(); auto r = cmd_new("foo bar", false, parent); REQUIRE_FALSE(r.has_value()); REQUIRE(r.error().code == ErrorCode::LayoutInvalidName); } TEST_CASE("cmd_new writes a minimal .gitignore", "[cli][new]") { auto parent = fresh_dir(); REQUIRE(cmd_new("foo", false, parent).has_value()); auto content = read_file(parent / "foo" / ".gitignore"); REQUIRE(content.find("/build/") != std::string::npos); }