123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- /*
- * Copyright (c) 2023-2024, Shannon Booth <shannon@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #include <AK/StringView.h>
- #include <LibCore/Command.h>
- #include <LibCore/File.h>
- #include <LibCore/System.h>
- #include <LibFileSystem/FileSystem.h>
- #include <LibTest/Macros.h>
- #include <LibTest/TestCase.h>
- static constexpr char const* s_test_dir = "/tmp/patch-test";
- #define EXPECT_FILE_EQ(file_path, expected_content) \
- do { \
- auto output = MUST(Core::File::open(file_path, Core::File::OpenMode::Read)); \
- auto content = MUST(output->read_until_eof()); \
- EXPECT_EQ(StringView { content }, expected_content); \
- } while (false)
- class PatchSetup {
- public:
- PatchSetup()
- {
- clean_up(); // Just in case something was left behind from beforehand.
- MUST(Core::System::mkdir(StringView { s_test_dir, strlen(s_test_dir) }, 0755));
- }
- ~PatchSetup()
- {
- clean_up();
- }
- private:
- static void clean_up()
- {
- auto result = FileSystem::remove(StringView { s_test_dir, strlen(s_test_dir) }, FileSystem::RecursionMode::Allowed);
- if (result.is_error())
- VERIFY(result.error().is_errno() && result.error().code() == ENOENT);
- }
- };
- enum class ExpectSuccess {
- Yes,
- No,
- };
- static void run_patch(ExpectSuccess success, Vector<char const*>&& arguments, StringView standard_input, Optional<StringView> expected_stdout = {})
- {
- // Ask patch to run the test in a temporary directory so we don't leave any files around.
- Vector<char const*> args_with_chdir = { "patch", "-d", s_test_dir };
- args_with_chdir.extend(arguments);
- args_with_chdir.append(nullptr);
- auto patch = MUST(Core::Command::create("patch"sv, args_with_chdir.data()));
- MUST(patch->write(standard_input));
- auto [stdout, stderr] = MUST(patch->read_all());
- auto status = MUST(patch->status());
- StringView stdout_view { stdout.bytes() };
- StringView stderr_view { stderr.bytes() };
- if (success == ExpectSuccess::Yes && status != Core::Command::ProcessResult::DoneWithZeroExitCode) {
- FAIL(MUST(String::formatted("patch did not return success: status: {}, stdout: {}, stderr: {}", static_cast<int>(status), stdout_view, stderr_view)));
- } else if (success == ExpectSuccess::No && status != Core::Command::ProcessResult::Failed) {
- FAIL(MUST(String::formatted("patch did not return error: status: {}, stdout: {}, stderr: {}", static_cast<int>(status), stdout_view, stderr_view)));
- }
- if (expected_stdout.has_value())
- EXPECT_EQ(StringView { expected_stdout->bytes() }, StringView { stdout.bytes() });
- }
- TEST_CASE(basic_change_patch)
- {
- PatchSetup setup;
- auto patch = R"(
- --- a
- +++ b
- @@ -1,3 +1,3 @@
- 1
- -2
- +b
- 3
- )"sv;
- auto file = "1\n2\n3\n"sv;
- auto input = MUST(Core::File::open(ByteString::formatted("{}/a", s_test_dir), Core::File::OpenMode::Write));
- MUST(input->write_until_depleted(file.bytes()));
- run_patch(ExpectSuccess::Yes, {}, patch, "patching file a\n"sv);
- EXPECT_FILE_EQ(ByteString::formatted("{}/a", s_test_dir), "1\nb\n3\n");
- }
- TEST_CASE(basic_addition_patch_from_empty_file)
- {
- PatchSetup setup;
- auto patch = R"(
- --- /dev/null
- +++ a
- @@ -0,0 +1,3 @@
- +1
- +2
- +3
- )"sv;
- auto file = ""sv;
- auto input = MUST(Core::File::open(ByteString::formatted("{}/a", s_test_dir), Core::File::OpenMode::Write));
- MUST(input->write_until_depleted(file.bytes()));
- run_patch(ExpectSuccess::Yes, {}, patch, "patching file a\n"sv);
- EXPECT_FILE_EQ(ByteString::formatted("{}/a", s_test_dir), "1\n2\n3\n");
- }
- TEST_CASE(strip_path_to_basename)
- {
- PatchSetup setup;
- auto patch = R"(
- --- /dev/null
- +++ a/bunch/of/../folders/stripped/to/basename
- @@ -0,0 +1 @@
- +Hello, friends!
- )"sv;
- auto file = ""sv;
- auto input = MUST(Core::File::open(MUST(String::formatted("{}/basename", s_test_dir)), Core::File::OpenMode::Write));
- MUST(input->write_until_depleted(file.bytes()));
- run_patch(ExpectSuccess::Yes, {}, patch, "patching file basename\n"sv);
- EXPECT_FILE_EQ(MUST(String::formatted("{}/basename", s_test_dir)), "Hello, friends!\n");
- }
- TEST_CASE(strip_path_partially)
- {
- PatchSetup setup;
- auto patch = R"(
- --- /dev/null
- +++ a/bunch/of/../folders/stripped/to/basename
- @@ -0,0 +1 @@
- +Hello, friends!
- )"sv;
- MUST(Core::System::mkdir(MUST(String::formatted("{}/to", s_test_dir)), 0755));
- auto file = ""sv;
- auto input = MUST(Core::File::open(MUST(String::formatted("{}/to/basename", s_test_dir)), Core::File::OpenMode::Write));
- MUST(input->write_until_depleted(file.bytes()));
- run_patch(ExpectSuccess::Yes, { "-p6" }, patch, "patching file to/basename\n"sv);
- EXPECT_FILE_EQ(MUST(String::formatted("{}/to/basename", s_test_dir)), "Hello, friends!\n");
- }
- TEST_CASE(add_file_from_scratch)
- {
- PatchSetup setup;
- auto patch = R"(
- --- /dev/null
- +++ a/file_to_add
- @@ -0,0 +1 @@
- +Hello, friends!
- )"sv;
- run_patch(ExpectSuccess::Yes, {}, patch, "patching file file_to_add\n"sv);
- EXPECT_FILE_EQ(ByteString::formatted("{}/file_to_add", s_test_dir), "Hello, friends!\n");
- }
- TEST_CASE(two_patches_in_single_patch_file)
- {
- PatchSetup setup;
- auto patch = R"(
- --- /dev/null
- +++ a/first_file_to_add
- @@ -0,0 +1 @@
- +Hello, friends!
- --- /dev/null
- +++ a/second_file_to_add
- @@ -0,0 +1 @@
- +Hello, friends!
- )"sv;
- run_patch(ExpectSuccess::Yes, {}, patch, "patching file first_file_to_add\n"
- "patching file second_file_to_add\n"sv);
- EXPECT_FILE_EQ(ByteString::formatted("{}/first_file_to_add", s_test_dir), "Hello, friends!\n");
- EXPECT_FILE_EQ(ByteString::formatted("{}/second_file_to_add", s_test_dir), "Hello, friends!\n");
- }
- TEST_CASE(patch_adding_file_to_existing_file)
- {
- PatchSetup setup;
- auto patch = R"(
- --- /dev/null
- +++ a
- @@ -0,0 +1 @@
- +1
- )"sv;
- auto file = "a\n"sv;
- auto input = MUST(Core::File::open(ByteString::formatted("{}/a", s_test_dir), Core::File::OpenMode::Write));
- MUST(input->write_until_depleted(file.bytes()));
- run_patch(ExpectSuccess::No, {}, patch);
- EXPECT_FILE_EQ(ByteString::formatted("{}/a", s_test_dir), "a\n"sv);
- }
|