diff --git a/Tests/Utilities/CMakeLists.txt b/Tests/Utilities/CMakeLists.txt index be7562c15f6..34efc9ea620 100644 --- a/Tests/Utilities/CMakeLists.txt +++ b/Tests/Utilities/CMakeLists.txt @@ -1,6 +1,7 @@ set(TEST_SOURCES TestPatch.cpp TestSed.cpp + TestTest.cpp TestUniq.cpp ) diff --git a/Tests/Utilities/TestTest.cpp b/Tests/Utilities/TestTest.cpp new file mode 100644 index 00000000000..058b8f80b46 --- /dev/null +++ b/Tests/Utilities/TestTest.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2026, Lucas Chollet + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +static void run_test(Vector&& arguments, int expected_exit_status) +{ + auto test = TRY_OR_FAIL(Core::Process::spawn( + Core::ProcessSpawnOptions { .executable = "test"sv, + .search_for_executable_in_path = true, + .arguments = arguments })); + auto exited_with_code_0 = TRY_OR_FAIL(test.wait_for_termination()); + EXPECT_EQ(expected_exit_status, exited_with_code_0 ? 0 : 1); +} + +TEST_CASE(option_s) +{ + run_test({ "-s", "file_that_do_not_exist" }, 1); + + // empty file + auto temp_file = TRY_OR_FAIL(FileSystem::TempFile::create_temp_file()); + run_test({ "-s", MUST(ByteString::from_utf8(temp_file->path())) }, 1); + + auto file = TRY_OR_FAIL(Core::File::open(temp_file->path(), Core::File::OpenMode::Write)); + TRY_OR_FAIL(file->write_until_depleted("file content\n"sv)); + run_test({ "-s", MUST(ByteString::from_utf8(temp_file->path())) }, 0); +} diff --git a/Userland/Utilities/test.cpp b/Userland/Utilities/test.cpp index 68b4f594faf..072c80bee87 100644 --- a/Userland/Utilities/test.cpp +++ b/Userland/Utilities/test.cpp @@ -265,6 +265,28 @@ private: Owner m_kind { EffectiveGID }; }; +class FileExists : public Condition { +public: + FileExists(StringView path) + : m_path(path) + { + } + +private: + virtual bool check() const override + { + // "True if pathname resolves to an existing directory entry for a file that + // has a size greater than zero. False if pathname cannot be resolved, or if + // pathname resolves to an existing directory entry for a file that does not + // have a size greater than zero." + + auto result = Core::System::stat(m_path); + return !result.is_error() && result.value().st_size > 0; + } + + ByteString m_path; +}; + class StringCompare : public Condition { public: enum Mode { @@ -478,8 +500,9 @@ static OwnPtr parse_simple_expression(char* argv[]) return make(value, FileIsOwnedBy::EffectiveGID); case 'O': return make(value, FileIsOwnedBy::EffectiveUID); - case 'N': case 's': + return make(value); + case 'N': // 'optind' has been incremented to refer to the argument after the // operator, while we want to print the operator itself. fatal_error("Unsupported operator \033[1m%s", argv[optind - 1]);