TestPatch.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /*
  2. * Copyright (c) 2023-2024, Shannon Booth <shannon@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/StringView.h>
  7. #include <LibCore/Command.h>
  8. #include <LibCore/File.h>
  9. #include <LibCore/System.h>
  10. #include <LibFileSystem/FileSystem.h>
  11. #include <LibTest/Macros.h>
  12. #include <LibTest/TestCase.h>
  13. static constexpr char const* s_test_dir = "/tmp/patch-test";
  14. #define EXPECT_FILE_EQ(file_path, expected_content) \
  15. do { \
  16. auto output = MUST(Core::File::open(file_path, Core::File::OpenMode::Read)); \
  17. auto content = MUST(output->read_until_eof()); \
  18. EXPECT_EQ(StringView { content }, expected_content); \
  19. } while (false)
  20. class PatchSetup {
  21. public:
  22. PatchSetup()
  23. {
  24. clean_up(); // Just in case something was left behind from beforehand.
  25. MUST(Core::System::mkdir(StringView { s_test_dir, strlen(s_test_dir) }, 0755));
  26. }
  27. ~PatchSetup()
  28. {
  29. clean_up();
  30. }
  31. private:
  32. static void clean_up()
  33. {
  34. auto result = FileSystem::remove(StringView { s_test_dir, strlen(s_test_dir) }, FileSystem::RecursionMode::Allowed);
  35. if (result.is_error())
  36. VERIFY(result.error().is_errno() && result.error().code() == ENOENT);
  37. }
  38. };
  39. enum class ExpectSuccess {
  40. Yes,
  41. No,
  42. };
  43. static void run_patch(ExpectSuccess success, Vector<char const*>&& arguments, StringView standard_input, Optional<StringView> expected_stdout = {})
  44. {
  45. // Ask patch to run the test in a temporary directory so we don't leave any files around.
  46. Vector<char const*> args_with_chdir = { "patch", "-d", s_test_dir };
  47. args_with_chdir.extend(arguments);
  48. args_with_chdir.append(nullptr);
  49. auto patch = MUST(Core::Command::create("patch"sv, args_with_chdir.data()));
  50. MUST(patch->write(standard_input));
  51. auto [stdout, stderr] = MUST(patch->read_all());
  52. auto status = MUST(patch->status());
  53. StringView stdout_view { stdout.bytes() };
  54. StringView stderr_view { stderr.bytes() };
  55. if (success == ExpectSuccess::Yes && status != Core::Command::ProcessResult::DoneWithZeroExitCode) {
  56. FAIL(MUST(String::formatted("patch did not return success: status: {}, stdout: {}, stderr: {}", static_cast<int>(status), stdout_view, stderr_view)));
  57. } else if (success == ExpectSuccess::No && status != Core::Command::ProcessResult::Failed) {
  58. FAIL(MUST(String::formatted("patch did not return error: status: {}, stdout: {}, stderr: {}", static_cast<int>(status), stdout_view, stderr_view)));
  59. }
  60. if (expected_stdout.has_value())
  61. EXPECT_EQ(StringView { expected_stdout->bytes() }, StringView { stdout.bytes() });
  62. }
  63. TEST_CASE(basic_change_patch)
  64. {
  65. PatchSetup setup;
  66. auto patch = R"(
  67. --- a
  68. +++ b
  69. @@ -1,3 +1,3 @@
  70. 1
  71. -2
  72. +b
  73. 3
  74. )"sv;
  75. auto file = "1\n2\n3\n"sv;
  76. auto input = MUST(Core::File::open(ByteString::formatted("{}/a", s_test_dir), Core::File::OpenMode::Write));
  77. MUST(input->write_until_depleted(file.bytes()));
  78. run_patch(ExpectSuccess::Yes, {}, patch, "patching file a\n"sv);
  79. EXPECT_FILE_EQ(ByteString::formatted("{}/a", s_test_dir), "1\nb\n3\n");
  80. }
  81. TEST_CASE(basic_addition_patch_from_empty_file)
  82. {
  83. PatchSetup setup;
  84. auto patch = R"(
  85. --- /dev/null
  86. +++ a
  87. @@ -0,0 +1,3 @@
  88. +1
  89. +2
  90. +3
  91. )"sv;
  92. auto file = ""sv;
  93. auto input = MUST(Core::File::open(ByteString::formatted("{}/a", s_test_dir), Core::File::OpenMode::Write));
  94. MUST(input->write_until_depleted(file.bytes()));
  95. run_patch(ExpectSuccess::Yes, {}, patch, "patching file a\n"sv);
  96. EXPECT_FILE_EQ(ByteString::formatted("{}/a", s_test_dir), "1\n2\n3\n");
  97. }
  98. TEST_CASE(strip_path_to_basename)
  99. {
  100. PatchSetup setup;
  101. auto patch = R"(
  102. --- /dev/null
  103. +++ a/bunch/of/../folders/stripped/to/basename
  104. @@ -0,0 +1 @@
  105. +Hello, friends!
  106. )"sv;
  107. auto file = ""sv;
  108. auto input = MUST(Core::File::open(MUST(String::formatted("{}/basename", s_test_dir)), Core::File::OpenMode::Write));
  109. MUST(input->write_until_depleted(file.bytes()));
  110. run_patch(ExpectSuccess::Yes, {}, patch, "patching file basename\n"sv);
  111. EXPECT_FILE_EQ(MUST(String::formatted("{}/basename", s_test_dir)), "Hello, friends!\n");
  112. }
  113. TEST_CASE(strip_path_partially)
  114. {
  115. PatchSetup setup;
  116. auto patch = R"(
  117. --- /dev/null
  118. +++ a/bunch/of/../folders/stripped/to/basename
  119. @@ -0,0 +1 @@
  120. +Hello, friends!
  121. )"sv;
  122. MUST(Core::System::mkdir(MUST(String::formatted("{}/to", s_test_dir)), 0755));
  123. auto file = ""sv;
  124. auto input = MUST(Core::File::open(MUST(String::formatted("{}/to/basename", s_test_dir)), Core::File::OpenMode::Write));
  125. MUST(input->write_until_depleted(file.bytes()));
  126. run_patch(ExpectSuccess::Yes, { "-p6" }, patch, "patching file to/basename\n"sv);
  127. EXPECT_FILE_EQ(MUST(String::formatted("{}/to/basename", s_test_dir)), "Hello, friends!\n");
  128. }
  129. TEST_CASE(add_file_from_scratch)
  130. {
  131. PatchSetup setup;
  132. auto patch = R"(
  133. --- /dev/null
  134. +++ a/file_to_add
  135. @@ -0,0 +1 @@
  136. +Hello, friends!
  137. )"sv;
  138. run_patch(ExpectSuccess::Yes, {}, patch, "patching file file_to_add\n"sv);
  139. EXPECT_FILE_EQ(ByteString::formatted("{}/file_to_add", s_test_dir), "Hello, friends!\n");
  140. }
  141. TEST_CASE(two_patches_in_single_patch_file)
  142. {
  143. PatchSetup setup;
  144. auto patch = R"(
  145. --- /dev/null
  146. +++ a/first_file_to_add
  147. @@ -0,0 +1 @@
  148. +Hello, friends!
  149. --- /dev/null
  150. +++ a/second_file_to_add
  151. @@ -0,0 +1 @@
  152. +Hello, friends!
  153. )"sv;
  154. run_patch(ExpectSuccess::Yes, {}, patch, "patching file first_file_to_add\n"
  155. "patching file second_file_to_add\n"sv);
  156. EXPECT_FILE_EQ(ByteString::formatted("{}/first_file_to_add", s_test_dir), "Hello, friends!\n");
  157. EXPECT_FILE_EQ(ByteString::formatted("{}/second_file_to_add", s_test_dir), "Hello, friends!\n");
  158. }
  159. TEST_CASE(patch_adding_file_to_existing_file)
  160. {
  161. PatchSetup setup;
  162. auto patch = R"(
  163. --- /dev/null
  164. +++ a
  165. @@ -0,0 +1 @@
  166. +1
  167. )"sv;
  168. auto file = "a\n"sv;
  169. auto input = MUST(Core::File::open(ByteString::formatted("{}/a", s_test_dir), Core::File::OpenMode::Write));
  170. MUST(input->write_until_depleted(file.bytes()));
  171. run_patch(ExpectSuccess::No, {}, patch);
  172. EXPECT_FILE_EQ(ByteString::formatted("{}/a", s_test_dir), "a\n"sv);
  173. }