// Copyright Epic Games, Inc. All Rights Reserved. #include #if ZEN_WITH_TESTS # include "zen-test.h" # include # include # include # include # include namespace zen::tests { TEST_SUITE_BEGIN("zen.utility-cmd"); TEST_CASE("print.cbo_to_json") { ScopedTemporaryDirectory TempDir; // Build a small compact binary object covering a few primitive field types. CbObjectWriter Writer; Writer << "Name" << "ZenPrintTest"; Writer << "Count" << int32_t(42); Writer << "Enabled" << true; IoBuffer Payload = Writer.Save().GetBuffer().AsIoBuffer(); REQUIRE(Payload.GetSize() > 0); const std::filesystem::path CboPath = TempDir.Path() / "object.cbo"; WriteFile(CboPath, Payload); const ZenCommandResult Result = RunZen(fmt::format(R"(print --source "{}")", CboPath.string())); REQUIRE_MESSAGE(Result.ExitCode == 0, fmt::format("zen print exited with code {}", Result.ExitCode)); // CompactBinaryToJson emits a JSON object; field names are quoted and values appear verbatim. CHECK(Result.Output.find("\"Name\"") != std::string::npos); CHECK(Result.Output.find("\"ZenPrintTest\"") != std::string::npos); CHECK(Result.Output.find("\"Count\"") != std::string::npos); CHECK(Result.Output.find("42") != std::string::npos); CHECK(Result.Output.find("\"Enabled\"") != std::string::npos); CHECK(Result.Output.find("true") != std::string::npos); } TEST_CASE("print.show_type_info_annotates_output") { ScopedTemporaryDirectory TempDir; CbObjectWriter Writer; Writer << "Count" << int32_t(7); IoBuffer Payload = Writer.Save().GetBuffer().AsIoBuffer(); const std::filesystem::path CboPath = TempDir.Path() / "object.cbo"; WriteFile(CboPath, Payload); const ZenCommandResult Plain = RunZen(fmt::format(R"(print --source "{}")", CboPath.string())); const ZenCommandResult Typed = RunZen(fmt::format(R"(print --show-type-info --source "{}")", CboPath.string())); REQUIRE(Plain.ExitCode == 0); REQUIRE(Typed.ExitCode == 0); // --show-type-info prefixes values with type annotations like [IntegerPositive] that the plain variant omits. CHECK(Typed.Output.size() > Plain.Output.size()); CHECK(Typed.Output.find("[IntegerPositive]") != std::string::npos); CHECK(Plain.Output.find("[IntegerPositive]") == std::string::npos); } TEST_CASE("print.missing_source_fails") { const ZenCommandResult Result = RunZen(R"(print)"); CHECK(Result.ExitCode != 0); } TEST_CASE("print.non_cbo_file_fails") { ScopedTemporaryDirectory TempDir; const std::filesystem::path GarbagePath = TempDir.Path() / "garbage.cbo"; const std::string_view Garbage = "this is not compact binary data"; WriteFile(GarbagePath, IoBuffer(IoBuffer::Wrap, Garbage.data(), Garbage.size())); const ZenCommandResult Result = RunZen(fmt::format(R"(print --source "{}")", GarbagePath.string())); CHECK(Result.ExitCode != 0); } //////////////////////////////////////////////////////////////////////////////// // wipe namespace { // Drop a small fixture tree of files and nested directories inside Root so wipe has // something non-trivial to recurse over. void PopulateWipeFixture(const std::filesystem::path& Root) { std::filesystem::create_directories(Root / "nested" / "deep"); const std::string_view Contents = "hello"; const IoBuffer Payload(IoBuffer::Wrap, Contents.data(), Contents.size()); WriteFile(Root / "top.txt", Payload); WriteFile(Root / "nested" / "mid.txt", Payload); WriteFile(Root / "nested" / "deep" / "leaf.txt", Payload); } bool DirectoryIsEmpty(const std::filesystem::path& Path) { std::error_code Ec; return std::filesystem::is_empty(Path, Ec) && !Ec; } } // namespace TEST_CASE("wipe.removes_directory_contents") { ScopedTemporaryDirectory TempDir; const std::filesystem::path Target = TempDir.Path() / "to-wipe"; PopulateWipeFixture(Target); REQUIRE(std::filesystem::exists(Target / "top.txt")); REQUIRE(std::filesystem::exists(Target / "nested" / "deep" / "leaf.txt")); const ZenCommandResult Result = RunZen(fmt::format(R"(wipe --yes --quiet --directory "{}")", Target.string())); REQUIRE_MESSAGE(Result.ExitCode == 0, fmt::format("zen wipe exited with code {}", Result.ExitCode)); CHECK_FALSE(std::filesystem::exists(Target / "top.txt")); CHECK_FALSE(std::filesystem::exists(Target / "nested")); } TEST_CASE("wipe.dryrun_keeps_files") { ScopedTemporaryDirectory TempDir; const std::filesystem::path Target = TempDir.Path() / "to-wipe"; PopulateWipeFixture(Target); const ZenCommandResult Result = RunZen(fmt::format(R"(wipe --yes --quiet --dryrun --directory "{}")", Target.string())); REQUIRE_MESSAGE(Result.ExitCode == 0, fmt::format("zen wipe --dryrun exited with code {}", Result.ExitCode)); // --dryrun must leave the tree untouched. CHECK(std::filesystem::exists(Target / "top.txt")); CHECK(std::filesystem::exists(Target / "nested" / "mid.txt")); CHECK(std::filesystem::exists(Target / "nested" / "deep" / "leaf.txt")); } TEST_CASE("wipe.nonexistent_directory_is_noop") { ScopedTemporaryDirectory TempDir; const std::filesystem::path Missing = TempDir.Path() / "does-not-exist"; REQUIRE_FALSE(std::filesystem::exists(Missing)); const ZenCommandResult Result = RunZen(fmt::format(R"(wipe --yes --quiet --directory "{}")", Missing.string())); // A missing directory should be silently skipped without failing. CHECK(Result.ExitCode == 0); CHECK_FALSE(std::filesystem::exists(Missing)); } TEST_CASE("wipe.empty_directory") { ScopedTemporaryDirectory TempDir; const std::filesystem::path Target = TempDir.Path() / "empty"; std::filesystem::create_directories(Target); REQUIRE(DirectoryIsEmpty(Target)); const ZenCommandResult Result = RunZen(fmt::format(R"(wipe --yes --quiet --directory "{}")", Target.string())); CHECK(Result.ExitCode == 0); } TEST_SUITE_END(); } // namespace zen::tests #endif