2023-07-14 01:39:17 +03:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2023, 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);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static void run_patch(Vector<char const*>&& arguments, StringView standard_input, 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());
|
|
|
|
if (status != Core::Command::ProcessResult::DoneWithZeroExitCode) {
|
|
|
|
FAIL(MUST(String::formatted("patch didn't exit cleanly: status: {}, stdout:{}, stderr: {}", static_cast<int>(status), StringView { stdout.bytes() }, StringView { stderr.bytes() })));
|
|
|
|
}
|
|
|
|
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(MUST(String::formatted("{}/a", s_test_dir)), Core::File::OpenMode::Write));
|
|
|
|
MUST(input->write_until_depleted(file.bytes()));
|
|
|
|
|
|
|
|
run_patch({}, patch, "patching file a\n"sv);
|
|
|
|
|
|
|
|
EXPECT_FILE_EQ(MUST(String::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(MUST(String::formatted("{}/a", s_test_dir)), Core::File::OpenMode::Write));
|
|
|
|
MUST(input->write_until_depleted(file.bytes()));
|
|
|
|
|
|
|
|
run_patch({}, patch, "patching file a\n"sv);
|
|
|
|
|
|
|
|
EXPECT_FILE_EQ(MUST(String::formatted("{}/a", s_test_dir)), "1\n2\n3\n");
|
|
|
|
}
|
2023-07-14 03:07:00 +03:00
|
|
|
|
|
|
|
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({}, 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({ "-p6" }, patch, "patching file to/basename\n"sv);
|
|
|
|
|
|
|
|
EXPECT_FILE_EQ(MUST(String::formatted("{}/to/basename", s_test_dir)), "Hello, friends!\n");
|
|
|
|
}
|
2023-07-10 09:48:32 +03:00
|
|
|
|
|
|
|
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({}, patch, "patching file file_to_add\n"sv);
|
|
|
|
|
|
|
|
EXPECT_FILE_EQ(MUST(String::formatted("{}/file_to_add", s_test_dir)), "Hello, friends!\n");
|
|
|
|
}
|
2023-07-12 01:37:26 +03:00
|
|
|
|
|
|
|
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({}, patch, "patching file first_file_to_add\n"
|
|
|
|
"patching file second_file_to_add\n"sv);
|
|
|
|
|
|
|
|
EXPECT_FILE_EQ(MUST(String::formatted("{}/first_file_to_add", s_test_dir)), "Hello, friends!\n");
|
|
|
|
EXPECT_FILE_EQ(MUST(String::formatted("{}/second_file_to_add", s_test_dir)), "Hello, friends!\n");
|
|
|
|
}
|