| // Copyright 2017-2020 The Verible Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "common/strings/diff.h" | |
| #include <initializer_list> | |
| #include <sstream> | |
| #include "absl/strings/string_view.h" | |
| #include "gmock/gmock.h" | |
| #include "gtest/gtest.h" | |
| namespace diff { | |
| // Print functions copied from external_libs/editscript_test.cc | |
| std::ostream& operator<<(std::ostream& out, Operation operation) { | |
| switch (operation) { | |
| case Operation::EQUALS: | |
| return (out << "EQUALS"); | |
| case Operation::DELETE: | |
| return (out << "DELETE"); | |
| case Operation::INSERT: | |
| return (out << "INSERT"); | |
| } | |
| return out; | |
| } | |
| std::ostream& operator<<(std::ostream& out, const diff::Edit& edit) { | |
| out << "{" << edit.operation << ",[" << edit.start << "," << edit.end << ")}"; | |
| return out; | |
| } | |
| std::ostream& operator<<(std::ostream& out, const Edits& edits) { | |
| out << "Edits{"; | |
| std::string outer_delim = ""; | |
| for (auto& edit : edits) { | |
| out << outer_delim << edit; | |
| outer_delim = ","; | |
| } | |
| out << "};"; | |
| return out; | |
| } | |
| } // namespace diff | |
| namespace verible { | |
| namespace { | |
| using diff::Edits; | |
| using diff::Operation; | |
| using ::testing::ElementsAreArray; | |
| struct DiffTestCase { | |
| absl::string_view before; | |
| absl::string_view after; | |
| absl::string_view expected; | |
| }; | |
| TEST(LineDiffsTest, Various) { | |
| constexpr DiffTestCase kTestCases[] = { | |
| {"", "", ""}, | |
| {"", " ", "+ \n"}, | |
| {" ", "", "- \n"}, | |
| {" ", " ", " \n"}, | |
| {"", "\n", "+\n"}, | |
| {"\n", "", "-\n"}, | |
| {"\n", "\n", " \n"}, | |
| {"\n\n", "\n", " \n-\n"}, | |
| {"\n", "\n\n", " \n+\n"}, | |
| {"foo\nbar", "foo\nBar", // missing end \n | |
| " foo\n" | |
| "-bar\n" | |
| "+Bar\n"}, | |
| {"foo\nbar\n", "foo\nBar\n", // with end \n | |
| " foo\n" | |
| "-bar\n" | |
| "+Bar\n"}, | |
| {"foo\nbar\n", "Foo\nbar\n", // with end \n | |
| "-foo\n" | |
| "+Foo\n" | |
| " bar\n"}, | |
| {"foo\nbar\n", "Foo\nBar\n", // both lines changed | |
| "-foo\n" | |
| "-bar\n" | |
| "+Foo\n" | |
| "+Bar\n"}, | |
| {"foo\nbar", "foo\nbar\n", // end \n added | |
| " foo\n" | |
| "-bar\n" | |
| "+bar\n"}, | |
| {"frodo\nsam\nmerry\npippin\n", // | |
| "frodo\nmerry\npippin\n", // | |
| " frodo\n" | |
| "-sam\n" | |
| " merry\n" | |
| " pippin\n"}, | |
| {"frodo\nsam\nmerry\npippin\n", // | |
| "frodo\nmerry\ngandalf\npippin\n", // | |
| " frodo\n" | |
| "-sam\n" | |
| " merry\n" | |
| "+gandalf\n" | |
| " pippin\n"}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| const LineDiffs line_diffs(test.before, test.after); | |
| std::ostringstream stream; | |
| stream << line_diffs; | |
| EXPECT_EQ(stream.str(), test.expected) << "\bbefore:\n" | |
| << test.before << "\nafter:\n" | |
| << test.after; | |
| } | |
| } | |
| struct AddedLineNumbersTestCase { | |
| Edits edits; | |
| LineNumberSet expected_line_numbers; | |
| }; | |
| TEST(DiffEditsToAddedLineNumbersTest, Various) { | |
| const AddedLineNumbersTestCase kTestCases[] = { | |
| {{}, // | |
| {}}, | |
| {{{Operation::DELETE, 0, 3}}, // | |
| {}}, | |
| {{{Operation::EQUALS, 1, 4}}, // | |
| {}}, | |
| {{{Operation::INSERT, 2, 5}}, // | |
| {{3, 6}}}, | |
| {{{Operation::EQUALS, 0, 2}, // | |
| {Operation::DELETE, 2, 7}, // | |
| {Operation::INSERT, 2, 4}, // | |
| {Operation::EQUALS, 7, 9}}, // | |
| {{3, 5}}}, | |
| {{{Operation::EQUALS, 0, 2}, // | |
| {Operation::DELETE, 2, 7}, // | |
| {Operation::INSERT, 2, 4}, // | |
| {Operation::EQUALS, 7, 9}, // | |
| {Operation::INSERT, 6, 11}}, // | |
| {{3, 5}, {7, 12}}}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| EXPECT_EQ(DiffEditsToAddedLineNumbers(test.edits), | |
| test.expected_line_numbers); | |
| } | |
| } | |
| // Represents an Edit operation over a number of elements. | |
| struct RelativeEdit { | |
| Operation operation; | |
| int64_t size; | |
| }; | |
| // Construct a well-formed sequence of Edits with consistent and contiguous | |
| // start/end ranges given a sequence of RelativeEdits. | |
| // Rationale: it is much easier to reason about relative-sized edit ranges | |
| // and absolute indices when hand-crafting test cases. | |
| // | |
| // Example: | |
| // RelativeEdits: | |
| // {Operation::EQUALS, 2}, | |
| // {Operation::DELETE, 3}, | |
| // {Operation::INSERT, 4}, | |
| // {Operation::EQUALS, 5}, | |
| // | |
| // starting at indices 0 for both sequences, | |
| // translates into diff::Edit's (absolute indices): | |
| // {Operation::EQUALS, 0, 2}, // both files start at 0 for 2 lines | |
| // {Operation::DELETE, 2, 5}, // 3 lines [2,5) of old sequence deleted | |
| // {Operation::INSERT, 2, 6}, // 4 lines [2,6) of new sequence added | |
| // {Operation::EQUALS, 5, 10}, // both files advance 5 lines in common | |
| // | |
| // See MakeDiffEditsTest below for examples. | |
| // | |
| // (This is currently well-suited for a test-only library, but | |
| // could eventually become a crucial piece of future diff-to-patch-library.) | |
| diff::Edits MakeDiffEdits(const std::vector<RelativeEdit>& relative_edits, | |
| int64_t old_index = 0, int64_t new_index = 0) { | |
| diff::Edits edits; | |
| for (const RelativeEdit& edit : relative_edits) { | |
| if (!edits.empty() && edits.back().operation == edit.operation) { | |
| // same type as previous operation, just combine them. | |
| edits.back().end += edit.size; | |
| continue; | |
| } | |
| switch (edit.operation) { | |
| case Operation::EQUALS: { | |
| const int64_t old_end = old_index + edit.size; | |
| edits.push_back(diff::Edit{edit.operation, old_index, old_end}); | |
| old_index = old_end; | |
| new_index += edit.size; | |
| break; | |
| } | |
| case Operation::INSERT: { | |
| const int64_t new_end = new_index + edit.size; | |
| edits.push_back(diff::Edit{edit.operation, new_index, new_end}); | |
| new_index = new_end; | |
| break; | |
| } | |
| case Operation::DELETE: { | |
| const int64_t old_end = old_index + edit.size; | |
| edits.push_back(diff::Edit{edit.operation, old_index, old_end}); | |
| old_index = old_end; | |
| break; | |
| } | |
| } | |
| } | |
| return edits; | |
| } | |
| struct MakeDiffEditsTestCase { | |
| std::vector<RelativeEdit> rel_edits; | |
| diff::Edits expected_edits; | |
| }; | |
| TEST(MakeDiffEditsTest, Various) { | |
| const MakeDiffEditsTestCase kTestCases[] = { | |
| {{}, {}}, | |
| // Single edit operations: | |
| {{ | |
| {Operation::EQUALS, 10}, | |
| }, | |
| { | |
| {Operation::EQUALS, 0, 10}, | |
| }}, | |
| {{ | |
| {Operation::DELETE, 8}, | |
| }, | |
| { | |
| {Operation::DELETE, 0, 8}, | |
| }}, | |
| {{ | |
| {Operation::INSERT, 7}, | |
| }, | |
| { | |
| {Operation::INSERT, 0, 7}, | |
| }}, | |
| // Repeated edit operations: | |
| {{ | |
| {Operation::EQUALS, 4}, | |
| {Operation::EQUALS, 6}, | |
| }, | |
| { | |
| {Operation::EQUALS, 0, 10}, | |
| }}, | |
| {{ | |
| {Operation::DELETE, 5}, | |
| {Operation::DELETE, 3}, | |
| }, | |
| { | |
| {Operation::DELETE, 0, 8}, | |
| }}, | |
| {{ | |
| {Operation::INSERT, 2}, | |
| {Operation::INSERT, 5}, | |
| }, | |
| { | |
| {Operation::INSERT, 0, 7}, | |
| }}, | |
| // Cover each edit transition: | |
| {{ | |
| {Operation::EQUALS, 2}, | |
| {Operation::DELETE, 3}, | |
| }, | |
| { | |
| {Operation::EQUALS, 0, 2}, | |
| {Operation::DELETE, 2, 5}, | |
| }}, | |
| {{ | |
| {Operation::EQUALS, 4}, | |
| {Operation::INSERT, 5}, | |
| }, | |
| { | |
| {Operation::EQUALS, 0, 4}, | |
| {Operation::INSERT, 4, 9}, | |
| }}, | |
| {{ | |
| {Operation::DELETE, 3}, | |
| {Operation::EQUALS, 2}, | |
| }, | |
| { | |
| {Operation::DELETE, 0, 3}, | |
| {Operation::EQUALS, 3, 5}, | |
| }}, | |
| {{ | |
| {Operation::DELETE, 3}, | |
| {Operation::INSERT, 6}, | |
| }, | |
| { | |
| {Operation::DELETE, 0, 3}, | |
| {Operation::INSERT, 0, 6}, | |
| }}, | |
| {{ | |
| {Operation::INSERT, 7}, | |
| {Operation::EQUALS, 4}, | |
| }, | |
| { | |
| {Operation::INSERT, 0, 7}, | |
| {Operation::EQUALS, 0, 4}, | |
| }}, | |
| {{ | |
| {Operation::INSERT, 7}, | |
| {Operation::DELETE, 3}, | |
| }, | |
| { | |
| {Operation::INSERT, 0, 7}, | |
| {Operation::DELETE, 0, 3}, | |
| }}, | |
| {{ | |
| // covers one of each transition | |
| {Operation::EQUALS, 2}, | |
| {Operation::DELETE, 3}, | |
| {Operation::INSERT, 4}, | |
| {Operation::EQUALS, 5}, | |
| {Operation::INSERT, 6}, | |
| {Operation::DELETE, 7}, | |
| {Operation::EQUALS, 8}, | |
| }, | |
| { | |
| {Operation::EQUALS, 0, 2}, | |
| {Operation::DELETE, 2, 5}, | |
| {Operation::INSERT, 2, 6}, | |
| {Operation::EQUALS, 5, 10}, | |
| {Operation::INSERT, 11, 17}, | |
| {Operation::DELETE, 10, 17}, | |
| {Operation::EQUALS, 17, 25}, | |
| }}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| EXPECT_THAT(MakeDiffEdits(test.rel_edits), | |
| ElementsAreArray(test.expected_edits)); | |
| } | |
| } | |
| struct DiffEditsToPatchHunksTestCase { | |
| diff::Edits whole_edits; | |
| int common_context; | |
| std::vector<diff::Edits> expected_hunks; | |
| }; | |
| TEST(DiffEditsToPatchHunksTest, Various) { | |
| typedef std::initializer_list<RelativeEdit> RelEdits; | |
| const DiffEditsToPatchHunksTestCase kTestCases[] = { | |
| { | |
| .whole_edits = MakeDiffEdits(RelEdits{{Operation::EQUALS, 2}}), | |
| .common_context = 1, | |
| .expected_hunks = {} // empty because no-change hunk was removed | |
| }, | |
| { | |
| .whole_edits = MakeDiffEdits(RelEdits{{Operation::EQUALS, 200}}), | |
| .common_context = 1, | |
| .expected_hunks = {} // empty because no-change hunk was removed | |
| }, | |
| {.whole_edits = MakeDiffEdits(RelEdits{{Operation::INSERT, 3}}), | |
| .common_context = 1, | |
| .expected_hunks = | |
| { | |
| MakeDiffEdits(RelEdits{{Operation::INSERT, 3}}), | |
| }}, | |
| {.whole_edits = MakeDiffEdits(RelEdits{{Operation::DELETE, 4}}), | |
| .common_context = 1, | |
| .expected_hunks = | |
| { | |
| MakeDiffEdits(RelEdits{{Operation::DELETE, 4}}), | |
| }}, | |
| {.whole_edits = MakeDiffEdits(RelEdits{ | |
| {Operation::EQUALS, 3}, | |
| {Operation::DELETE, 1}, | |
| }), | |
| .common_context = 2, // first hunk should start at line[3-2] | |
| .expected_hunks = | |
| { | |
| MakeDiffEdits( | |
| RelEdits{ | |
| {Operation::EQUALS, 2}, | |
| {Operation::DELETE, 1}, | |
| }, | |
| 1, 1), | |
| }}, | |
| {.whole_edits = MakeDiffEdits(RelEdits{ | |
| {Operation::DELETE, 1}, | |
| {Operation::EQUALS, 3}, | |
| }), | |
| .common_context = 2, // last EQUALS edit should be no larger than this | |
| .expected_hunks = | |
| { | |
| MakeDiffEdits( | |
| RelEdits{ | |
| {Operation::DELETE, 1}, | |
| {Operation::EQUALS, 2}, | |
| }, | |
| 0, 0), | |
| }}, | |
| {.whole_edits = MakeDiffEdits(RelEdits{ | |
| {Operation::EQUALS, 3}, | |
| {Operation::DELETE, 1}, | |
| {Operation::EQUALS, 3}, | |
| }), | |
| .common_context = 2, // first hunk should start at line[3-2] | |
| .expected_hunks = | |
| { | |
| MakeDiffEdits( | |
| RelEdits{ | |
| {Operation::EQUALS, 2}, | |
| {Operation::DELETE, 1}, | |
| {Operation::EQUALS, 2}, | |
| }, | |
| 1, 1), | |
| }}, | |
| {.whole_edits = MakeDiffEdits(RelEdits{ | |
| {Operation::EQUALS, 3}, | |
| {Operation::INSERT, 1}, | |
| }), | |
| .common_context = 2, // first hunk should start at line[3-2] | |
| .expected_hunks = | |
| { | |
| MakeDiffEdits( | |
| RelEdits{ | |
| {Operation::EQUALS, 2}, | |
| {Operation::INSERT, 1}, | |
| }, | |
| 1, 1), | |
| }}, | |
| {.whole_edits = MakeDiffEdits(RelEdits{ | |
| {Operation::INSERT, 1}, | |
| {Operation::EQUALS, 3}, | |
| }), | |
| .common_context = 2, // last EQUALS edit should be no larger than this | |
| .expected_hunks = | |
| { | |
| MakeDiffEdits( | |
| RelEdits{ | |
| {Operation::INSERT, 1}, | |
| {Operation::EQUALS, 2}, | |
| }, | |
| 0, 0), | |
| }}, | |
| {.whole_edits = MakeDiffEdits(RelEdits{ | |
| {Operation::EQUALS, 3}, | |
| {Operation::INSERT, 1}, | |
| {Operation::EQUALS, 3}, | |
| }), | |
| .common_context = 2, // first hunk should start at line[3-2] | |
| .expected_hunks = | |
| { | |
| MakeDiffEdits( | |
| RelEdits{ | |
| {Operation::EQUALS, 2}, | |
| {Operation::INSERT, 1}, | |
| {Operation::EQUALS, 2}, | |
| }, | |
| 1, 1), | |
| }}, | |
| {.whole_edits = MakeDiffEdits(RelEdits{ | |
| {Operation::DELETE, 2}, | |
| {Operation::INSERT, 1}, | |
| {Operation::EQUALS, 4}, // expect to remain in one piece | |
| {Operation::DELETE, 1}, | |
| {Operation::INSERT, 2}, | |
| }), | |
| .common_context = 2, | |
| .expected_hunks = | |
| { | |
| MakeDiffEdits( | |
| RelEdits{ | |
| {Operation::DELETE, 2}, | |
| {Operation::INSERT, 1}, | |
| {Operation::EQUALS, 4}, // remain in one piece | |
| {Operation::DELETE, 1}, | |
| {Operation::INSERT, 2}, | |
| }, | |
| 0, 0), | |
| }}, | |
| {.whole_edits = MakeDiffEdits(RelEdits{ | |
| {Operation::DELETE, 2}, | |
| {Operation::INSERT, 1}, | |
| {Operation::EQUALS, 5}, // expect to split here | |
| {Operation::DELETE, 1}, | |
| {Operation::INSERT, 2}, | |
| }), | |
| .common_context = 2, | |
| .expected_hunks = | |
| { | |
| // expect two hunks | |
| MakeDiffEdits( | |
| RelEdits{ | |
| {Operation::DELETE, 2}, | |
| {Operation::INSERT, 1}, | |
| {Operation::EQUALS, 2}, | |
| }, | |
| 0, 0), | |
| // one line of EQUALS in the new gap | |
| MakeDiffEdits( | |
| RelEdits{ | |
| {Operation::EQUALS, 2}, | |
| {Operation::DELETE, 1}, | |
| {Operation::INSERT, 2}, | |
| }, | |
| 5, 4), | |
| }}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| EXPECT_THAT(DiffEditsToPatchHunks(test.whole_edits, test.common_context), | |
| ElementsAreArray(test.expected_hunks)); | |
| } | |
| } | |
| struct LineDiffsToUnifiedDiffTestCase { | |
| absl::string_view before_text; | |
| absl::string_view after_text; | |
| absl::string_view file_a; | |
| absl::string_view file_b; | |
| int common_context; | |
| absl::string_view expected_diff_text; | |
| }; | |
| TEST(LineDiffsToUnifiedDiffTest, Various) { | |
| const LineDiffsToUnifiedDiffTestCase kTestCases[] = { | |
| // No changes | |
| { | |
| "a\nb\nc\n", | |
| "a\nb\nc\n", | |
| {}, | |
| {}, | |
| 1, | |
| "", | |
| }, | |
| { | |
| "a\nb\nc\n", | |
| "a\nb\nc\n", | |
| "file.txt", | |
| {}, | |
| 1, | |
| "", | |
| }, | |
| { | |
| "a\nb\nc\n", | |
| "a\nb\nc\n", | |
| "old_file.txt", | |
| "new_file.txt", | |
| 1, | |
| "", | |
| }, | |
| // Single change | |
| { | |
| "a\nb\nc\n", | |
| "a\nb\nC\n", | |
| {}, | |
| {}, | |
| 1, | |
| "@@ -2,2 +2,2 @@\n" | |
| " b\n" | |
| "-c\n" | |
| "+C\n", | |
| }, | |
| { | |
| "a\nb\nc\n", | |
| "a\nb\nC\n", | |
| "file.txt", | |
| {}, | |
| 1, | |
| "--- a/file.txt\n" | |
| "+++ b/file.txt\n" | |
| "@@ -2,2 +2,2 @@\n" | |
| " b\n" | |
| "-c\n" | |
| "+C\n", | |
| }, | |
| { | |
| "a\nb\nc\n", | |
| "a\nb\nC\n", | |
| "old_file.txt", | |
| "new_file.txt", | |
| 1, | |
| "--- old_file.txt\n" | |
| "+++ new_file.txt\n" | |
| "@@ -2,2 +2,2 @@\n" | |
| " b\n" | |
| "-c\n" | |
| "+C\n", | |
| }, | |
| // Multiple chunks | |
| { | |
| "a\nb\nc\nd\ne\nf\nh\n", | |
| "A\nb\nc\nd\ne\nf\ng\nh\n", | |
| {}, | |
| {}, | |
| 1, | |
| "@@ -1,2 +1,2 @@\n" | |
| "-a\n" | |
| "+A\n" | |
| " b\n" | |
| "@@ -6,2 +6,3 @@\n" | |
| " f\n" | |
| "+g\n" | |
| " h\n", | |
| }, | |
| { | |
| "a\nb\nc\nd\ne\nf\nh\n", | |
| "A\nb\nc\nd\ne\nf\ng\nh\n", | |
| "file.txt", | |
| {}, | |
| 1, | |
| "--- a/file.txt\n" | |
| "+++ b/file.txt\n" | |
| "@@ -1,2 +1,2 @@\n" | |
| "-a\n" | |
| "+A\n" | |
| " b\n" | |
| "@@ -6,2 +6,3 @@\n" | |
| " f\n" | |
| "+g\n" | |
| " h\n", | |
| }, | |
| { | |
| "a\nb\nc\nd\ne\nf\nh\n", | |
| "A\nb\nc\nd\ne\nf\ng\nh\n", | |
| "old_file.txt", | |
| "new_file.txt", | |
| 1, | |
| "--- old_file.txt\n" | |
| "+++ new_file.txt\n" | |
| "@@ -1,2 +1,2 @@\n" | |
| "-a\n" | |
| "+A\n" | |
| " b\n" | |
| "@@ -6,2 +6,3 @@\n" | |
| " f\n" | |
| "+g\n" | |
| " h\n", | |
| }, | |
| // Large context | |
| { | |
| "a\nb\nc\nd\ne\nf\nh\n", | |
| "A\nb\nc\nd\ne\nf\ng\nh\n", | |
| "file.txt", | |
| {}, | |
| 99, | |
| "--- a/file.txt\n" | |
| "+++ b/file.txt\n" | |
| "@@ -1,7 +1,8 @@\n" | |
| "-a\n" | |
| "+A\n" | |
| " b\n" | |
| " c\n" | |
| " d\n" | |
| " e\n" | |
| " f\n" | |
| "+g\n" | |
| " h\n", | |
| }, | |
| // No context | |
| { | |
| "a\nb\nc\nd\ne\nf\nh\n", | |
| "A\nb\nc\nd\ne\nf\ng\nh\n", | |
| "file.txt", | |
| {}, | |
| 0, | |
| "--- a/file.txt\n" | |
| "+++ b/file.txt\n" | |
| "@@ -1 +1 @@\n" | |
| "-a\n" | |
| "+A\n" | |
| "@@ -7 +7 @@\n" | |
| "+g\n", | |
| }, | |
| // Multiple inserts and deletions | |
| { | |
| "a\nb\nc\nh\ni\nj\nk\nm\nn\no\np\nq\nx\ny\nz\nr\ns\n", | |
| "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\np\nq\nr\ns\n", | |
| "file.txt", | |
| {}, | |
| 1, | |
| "--- a/file.txt\n" | |
| "+++ b/file.txt\n" | |
| "@@ -3,2 +3,6 @@\n" | |
| " c\n" | |
| "+d\n" | |
| "+e\n" | |
| "+f\n" | |
| "+g\n" | |
| " h\n" | |
| "@@ -7,2 +11,3 @@\n" | |
| " k\n" | |
| "+l\n" | |
| " m\n" | |
| "@@ -12,5 +17,2 @@\n" | |
| " q\n" | |
| "-x\n" | |
| "-y\n" | |
| "-z\n" | |
| " r\n", | |
| }, | |
| // Missing \n in the last line of "before" text | |
| { | |
| "a\nb\nc\nd\ne\nf\nh", | |
| "A\nb\nc\nd\ne\nf\ng\nh\n", | |
| "file.txt", | |
| {}, | |
| 1, | |
| "--- a/file.txt\n" | |
| "+++ b/file.txt\n" | |
| "@@ -1,2 +1,2 @@\n" | |
| "-a\n" | |
| "+A\n" | |
| " b\n" | |
| "@@ -6,2 +6,3 @@\n" | |
| " f\n" | |
| "+g\n" | |
| "-h\n" | |
| "\\ No newline at end of file\n" | |
| "+h\n", | |
| }, | |
| // Missing \n in the last line of "after" text | |
| { | |
| "a\nb\nc\nd\ne\nf\nh\n", | |
| "A\nb\nc\nd\ne\nf\ng\nh", | |
| "file.txt", | |
| {}, | |
| 1, | |
| "--- a/file.txt\n" | |
| "+++ b/file.txt\n" | |
| "@@ -1,2 +1,2 @@\n" | |
| "-a\n" | |
| "+A\n" | |
| " b\n" | |
| "@@ -6,2 +6,3 @@\n" | |
| " f\n" | |
| "+g\n" | |
| "-h\n" | |
| "+h\n" | |
| "\\ No newline at end of file\n", | |
| }, | |
| // Missing \n in the last lines of both texts | |
| { | |
| "a\nb\nc\nd\ne\nf\nh", | |
| "A\nb\nc\nd\ne\nf\ng\nh", | |
| "file.txt", | |
| {}, | |
| 1, | |
| "--- a/file.txt\n" | |
| "+++ b/file.txt\n" | |
| "@@ -1,2 +1,2 @@\n" | |
| "-a\n" | |
| "+A\n" | |
| " b\n" | |
| "@@ -6,2 +6,3 @@\n" | |
| " f\n" | |
| "+g\n" | |
| " h\n" | |
| "\\ No newline at end of file\n", | |
| }, | |
| // File created | |
| { | |
| "", | |
| "A\nb\nc\nd\ne\nf\ng\nh\n", | |
| "/dev/null", | |
| "file.txt", | |
| 1, | |
| "--- /dev/null\n" | |
| "+++ file.txt\n" | |
| "@@ -1 +1,8 @@\n" | |
| "+A\n" | |
| "+b\n" | |
| "+c\n" | |
| "+d\n" | |
| "+e\n" | |
| "+f\n" | |
| "+g\n" | |
| "+h\n", | |
| }, | |
| // File removed | |
| { | |
| "a\nb\nc\nd\ne\nf\nh\n", | |
| "", | |
| "file.txt", | |
| "/dev/null", | |
| 1, | |
| "--- file.txt\n" | |
| "+++ /dev/null\n" | |
| "@@ -1,7 +1 @@\n" | |
| "-a\n" | |
| "-b\n" | |
| "-c\n" | |
| "-d\n" | |
| "-e\n" | |
| "-f\n" | |
| "-h\n", | |
| }, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| LineDiffs linediffs(test.before_text, test.after_text); | |
| std::ostringstream stream; | |
| LineDiffsToUnifiedDiff(stream, linediffs, test.common_context, test.file_a, | |
| test.file_b); | |
| EXPECT_EQ(stream.str(), test.expected_diff_text); | |
| } | |
| } | |
| } // namespace | |
| } // namespace verible |