From f776cf07444510c034bd5e6f4902a5354c08a9cd Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 4 Aug 2026 14:23:36 -0700 Subject: [PATCH] Improve readability of test failures. (#7609) Reduce use of gmock matcher infrastructure for diagnosing mismatches, and instead manually stream an explanation of the difference. The gmock-style "EXPECT_THAT" approach adds an unsuppressable "Actual: ..." line into the output that only contains unreadable and redundant noise. We're getting zero value from using a matcher diagnostic here, so don't. Before: ``` Value of: SplitOutput(test_file.actual_stdout) Expected: matches elements with unified diff Actual: { "--- else.carbon", "", "constants {", " %F.type: type = fn_type @F [concrete]", " %empty_tuple.type: type = tuple_type () [concrete]", " %F: %F.type = struct_value () [concrete]", " %H.type: type = fn_type @H [concrete]", " %H: %H.type = struct_value () [concrete]", " %pattern_type: type = pattern_type bool [concrete]", " %b.param_patt: %pattern_type = value_param_pattern [concrete]", " %b.patt: %pattern_type = at_binding_pattern b, %b.param_patt [concrete]", " %If.type: type = fn_type @If [concrete]", " %If: %If.type = struct_value () [concrete]", "}", "", "file {", " %If.decl: %If.type = fn_decl @If [concrete = constants.%If] {", " %b.param_patt: %pattern_type = value_param_pattern [concrete = constants.%b.param_patt]", " %b.patt: %pattern_type = at_binding_pattern b, %b.param_patt [concrete = constants.%b.patt]", " } {", " %b.param: bool = value_param call_param0", " %.loc8: type = type_literal bool [concrete = bool]", " %b: bool = wrapper_binding b, %b.param", " }", "}", "", "fn @If(%b.param: bool) {", "!entry:", " %b.ref: bool = name_ref b, %b", " if %b.ref br !if.then else br !if.else", "", "!if.then:", ... }, unified diff (- expected, + actual): === diff in expected elements 4 to 11 (1-based index): %F.type: type = fn_type @F [concrete] %empty_tuple.type: type = tuple_type () [concrete] %F: %F.type = struct_value () [concrete] - is equal to " %G.type: type = fn_type @G [concrete]" - is equal to " %G: %G.type = struct_value () [concrete]" %H.type: type = fn_type @H [concrete] %H: %H.type = struct_value () [concrete] %pattern_type: type = pattern_type bool [concrete] === diff in expected elements 37 to 44 (1-based index): br !if.done !if.else: - is equal to " %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G]" - is equal to " %G.call: init %empty_tuple.type = call %G.ref()" br !if.done !if.done: === diff end ``` After: ``` Value of: testing::Value(SplitOutput(test_file.actual_stdout), testing::ElementsAreArray(test_file.expected_stdout)) Actual: false Expected: true unified diff (- expected, + actual): === diff in expected elements 4 to 11 (1-based index): %F.type: type = fn_type @F [concrete] %empty_tuple.type: type = tuple_type () [concrete] %F: %F.type = struct_value () [concrete] - %G.type: type = fn_type @G [concrete] - %G: %G.type = struct_value () [concrete] %H.type: type = fn_type @H [concrete] %H: %H.type = struct_value () [concrete] %pattern_type: type = pattern_type bool [concrete] === diff in expected elements 37 to 44 (1-based index): br !if.done !if.else: - %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G] - %G.call: init %empty_tuple.type = call %G.ref() br !if.done !if.done: === diff end ``` Assisted-by: Gemini via Antigravity --- testing/base/BUILD | 10 +- ...{unified_diff_matcher.h => unified_diff.h} | 195 ++++++++------- ...matcher_test.cpp => unified_diff_test.cpp} | 236 ++++++++++-------- testing/file_test/BUILD | 2 +- testing/file_test/file_test_base.cpp | 36 ++- 5 files changed, 272 insertions(+), 207 deletions(-) rename testing/base/{unified_diff_matcher.h => unified_diff.h} (60%) rename testing/base/{unified_diff_matcher_test.cpp => unified_diff_test.cpp} (68%) diff --git a/testing/base/BUILD b/testing/base/BUILD index 54c2c363769d..4fa135002a56 100644 --- a/testing/base/BUILD +++ b/testing/base/BUILD @@ -92,9 +92,9 @@ cc_test( ) cc_library( - name = "unified_diff_matcher", + name = "unified_diff", testonly = 1, - hdrs = ["unified_diff_matcher.h"], + hdrs = ["unified_diff.h"], deps = [ "//common:check", "@googletest//:gtest", @@ -103,12 +103,12 @@ cc_library( ) cc_test( - name = "unified_diff_matcher_test", + name = "unified_diff_test", size = "small", - srcs = ["unified_diff_matcher_test.cpp"], + srcs = ["unified_diff_test.cpp"], deps = [ ":gtest_main", - ":unified_diff_matcher", + ":unified_diff", "@googletest//:gtest", "@llvm-project//llvm:Support", ], diff --git a/testing/base/unified_diff_matcher.h b/testing/base/unified_diff.h similarity index 60% rename from testing/base/unified_diff_matcher.h rename to testing/base/unified_diff.h index 6d399435b19e..03e418ad6282 100644 --- a/testing/base/unified_diff_matcher.h +++ b/testing/base/unified_diff.h @@ -2,13 +2,15 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#ifndef CARBON_TESTING_BASE_UNIFIED_DIFF_MATCHER_H_ -#define CARBON_TESTING_BASE_UNIFIED_DIFF_MATCHER_H_ +#ifndef CARBON_TESTING_BASE_UNIFIED_DIFF_H_ +#define CARBON_TESTING_BASE_UNIFIED_DIFF_H_ #include #include #include +#include +#include #include #include @@ -19,27 +21,24 @@ namespace Carbon::Testing { -// Matcher that compares the elements of two containers and produces a unified -// diff on failure. -template -class UnifiedDiffMatcher { +// Compares the elements of two containers and prints a unified diff when +// streamed to an ostream. +template +class UnifiedDiff { public: - explicit UnifiedDiffMatcher(Container expected) - : expected_(std::move(expected)) {} + explicit UnifiedDiff(const ExpectedContainer& expected, + const ActualContainer& actual, bool check_subset = false) + : expected_(expected), actual_(actual), check_subset_(check_subset) {} - // Matches `actual` against `expected_`. Returns true on a match; returns - // false and prints a unified diff to `listener` on a mismatch. - template - auto MatchAndExplain(const ActualContainer& actual, - testing::MatchResultListener* listener) const -> bool; - - auto DescribeTo(std::ostream* os) const -> void { - *os << "matches elements with unified diff"; + friend auto operator<<(std::ostream& os, const UnifiedDiff& diff) + -> std::ostream& { + diff.Print(&os); + return os; } - auto DescribeNegationTo(std::ostream* os) const -> void { - *os << "does not match elements with unified diff"; - } + // Prints the unified diff to `os`, or prints nothing if `expected_` and + // `actual_` match. + auto Print(std::ostream* os) const -> void; private: // A 2D array, stored contiguously. Rows correspond to `expected_`'s elements, @@ -71,45 +70,41 @@ class UnifiedDiffMatcher { } // Returns true if every element in `expected_` matches the corresponding - // element in `actual`. Stores comparisons in `match_results`. - template - auto IsEqual(const ActualContainer& actual, - Table& match_results) const -> bool; + // element in `actual_`. Stores comparisons in `match_results`. + auto IsEqual(Table& match_results) const -> bool; // Populates `subsequences` with the longest common matching subsequences - // found when comparing `actual` and `expected_`. Stores comparisons in + // found when comparing `actual_` and `expected_`. Stores comparisons in // `match_results`. - template - auto GetLongestCommonSubsequences(const ActualContainer& actual, - Table& match_results, + auto GetLongestCommonSubsequences(Table& match_results, Table& subsequences) const -> void; // Prints the unified diff. - template - auto PrintDiff(const ActualContainer& actual, - Table& match_results, - const Table& subsequences, - testing::MatchResultListener* listener) const -> void; + auto PrintDiff(Table& match_results, + const Table& subsequences, std::ostream* os) const + -> void; - // The expected elements. - Container expected_; + // The expected and actual elements. + const ExpectedContainer& expected_; + const ActualContainer& actual_; + // Whether we are checking that `expected_` is a subset of `actual_`. + bool check_subset_; }; -// Returns a polymorphic matcher that acts similarly to -// ElementsAreArray but produces a unified diff on failure. -template -auto ElementsAreArrayWithUnifiedDiff(Container expected) { - return testing::MakePolymorphicMatcher( - UnifiedDiffMatcher(std::move(expected))); -} +template +UnifiedDiff(const ExpectedContainer&, const ActualContainer&) + -> UnifiedDiff; +template +UnifiedDiff(const ExpectedContainer&, const ActualContainer&, bool) + -> UnifiedDiff; // ----------------------------------------------------------------------------- // Internal implementation details follow. // ----------------------------------------------------------------------------- -template +template template -class UnifiedDiffMatcher::Table { +class UnifiedDiff::Table { public: // Constructs a table with dimensions of expected_size and actual_size, // corresponding to the containers being compared. @@ -134,36 +129,29 @@ class UnifiedDiffMatcher::Table { llvm::SmallVector data_; }; -template -template -auto UnifiedDiffMatcher::MatchAndExplain( - const ActualContainer& actual, testing::MatchResultListener* listener) const - -> bool { - Table match_results(expected_.size(), std::size(actual), +template +auto UnifiedDiff::Print( + std::ostream* os) const -> void { + Table match_results(expected_.size(), std::size(actual_), MatchResult::Unknown); - if (IsEqual(actual, match_results)) { - return true; + if (IsEqual(match_results)) { + return; } - if (listener->IsInterested()) { - Table subsequences(expected_.size() + 1, std::size(actual) + 1, 0); - GetLongestCommonSubsequences(actual, match_results, subsequences); - PrintDiff(actual, match_results, subsequences, listener); - } - return false; + Table subsequences(expected_.size() + 1, std::size(actual_) + 1, 0); + GetLongestCommonSubsequences(match_results, subsequences); + PrintDiff(match_results, subsequences, os); } -template -template -auto UnifiedDiffMatcher::IsEqual( - const ActualContainer& actual, Table& match_results) const - -> bool { - if (expected_.size() != std::size(actual)) { +template +auto UnifiedDiff::IsEqual( + Table& match_results) const -> bool { + if (expected_.size() != std::size(actual_)) { return false; } - for (auto [i, actual_element] : llvm::enumerate(actual)) { + for (auto [i, actual_element] : llvm::enumerate(actual_)) { if (!IsElementMatch(i, i, actual_element, match_results)) { return false; } @@ -171,13 +159,12 @@ auto UnifiedDiffMatcher::IsEqual( return true; } -template -template -auto UnifiedDiffMatcher::GetLongestCommonSubsequences( - const ActualContainer& actual, Table& match_results, - Table& subsequences) const -> void { +template +auto UnifiedDiff:: + GetLongestCommonSubsequences(Table& match_results, + Table& subsequences) const -> void { for (auto expected_index : llvm::seq(expected_.size())) { - for (auto [actual_index, actual_element] : llvm::enumerate(actual)) { + for (auto [actual_index, actual_element] : llvm::enumerate(actual_)) { int subsequence_value; if (IsElementMatch(expected_index, actual_index, actual_element, match_results)) { @@ -196,12 +183,10 @@ auto UnifiedDiffMatcher::GetLongestCommonSubsequences( } } -template -template -auto UnifiedDiffMatcher::PrintDiff( - const ActualContainer& actual, Table& match_results, - const Table& subsequences, - testing::MatchResultListener* listener) const -> void { +template +auto UnifiedDiff::PrintDiff( + Table& match_results, const Table& subsequences, + std::ostream* os) const -> void { // A line in the diff output. struct DiffLine { enum class Kind { Match, ActualOnly, ExpectedOnly }; @@ -213,12 +198,12 @@ auto UnifiedDiffMatcher::PrintDiff( llvm::SmallVector diff; // Reserve a quick upper bound of the size. - diff.reserve(expected_.size() + std::size(actual)); + diff.reserve(expected_.size() + std::size(actual_)); // Reconstruct the diff by backtracking from the end of the table. int expected_index = expected_.size() - 1; - int actual_index = std::size(actual) - 1; - auto actual_it = std::end(actual) - 1; + int actual_index = std::size(actual_) - 1; + auto actual_it = std::end(actual_) - 1; while (expected_index >= 0 || actual_index >= 0) { auto match_result = (expected_index >= 0 && actual_index >= 0) ? match_results.Get(expected_index, actual_index) @@ -274,27 +259,57 @@ auto UnifiedDiffMatcher::PrintDiff( } } - *listener << "unified diff (- expected, + actual):\n"; + *os << "unified diff (- expected, + actual)"; + if (check_subset_) { + *os << " [+ lines are normal]"; + } + *os << ":\n"; for (const auto& range : print_ranges) { - *listener << "=== diff in expected elements " - << diff[range.end].expected_index + 1 << " to " - << diff[range.begin].expected_index + 1 << " (1-based index):\n"; + if (check_subset_) { + // In check_subset mode, only print diff ranges that contain unmatched + // expected lines. + bool has_expected_only = false; + for (auto i : llvm::seq_inclusive(range.begin, range.end)) { + if (diff[i].kind == DiffLine::Kind::ExpectedOnly) { + has_expected_only = true; + break; + } + } + if (!has_expected_only) { + continue; + } + } + *os << "=== diff in expected elements " + << diff[range.end].expected_index + 1 << " to " + << diff[range.begin].expected_index + 1 << " (1-based index):\n"; for (auto i : llvm::reverse(llvm::seq_inclusive(range.begin, range.end))) { const auto& line = diff[i]; if (line.kind == DiffLine::Kind::Match) { - *listener << " " << *line.actual_value << "\n"; + *os << " " << *line.actual_value << "\n"; } else if (line.kind == DiffLine::Kind::ActualOnly) { - *listener << "+ " << *line.actual_value << "\n"; + *os << "+" << *line.actual_value << "\n"; } else { - *listener << "- "; - expected_[line.expected_index].DescribeTo(listener->stream()); - *listener << "\n"; + *os << "-"; + // Strip off the extra decoration that `StrEq` adds. + // TODO: Also tidy up the `MatchesRegex` description. Maybe we shouldn't + // be building a list of matchers at all. + std::stringstream ss; + expected_[line.expected_index].DescribeTo(&ss); + std::string desc = ss.str(); + llvm::StringRef desc_ref = desc; + if (desc_ref.consume_front("is equal to \"") && + desc_ref.consume_back("\"")) { + *os << desc_ref.str(); + } else { + *os << desc; + } + *os << "\n"; } } } - *listener << "=== diff end\n"; + *os << "=== diff end\n"; } } // namespace Carbon::Testing -#endif // CARBON_TESTING_BASE_UNIFIED_DIFF_MATCHER_H_ +#endif // CARBON_TESTING_BASE_UNIFIED_DIFF_H_ diff --git a/testing/base/unified_diff_matcher_test.cpp b/testing/base/unified_diff_test.cpp similarity index 68% rename from testing/base/unified_diff_matcher_test.cpp rename to testing/base/unified_diff_test.cpp index 797aa16548ca..8f4fd43a4aec 100644 --- a/testing/base/unified_diff_matcher_test.cpp +++ b/testing/base/unified_diff_test.cpp @@ -2,11 +2,12 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#include "testing/base/unified_diff_matcher.h" +#include "testing/base/unified_diff.h" #include #include +#include #include #include "llvm/ADT/SmallVector.h" @@ -14,7 +15,9 @@ namespace Carbon::Testing { namespace { +using ::testing::ElementsAreArray; using ::testing::Matcher; +using ::testing::MatchesRegex; using ::testing::StrEq; // Asserts that when expected does not match actual, the string @@ -22,63 +25,67 @@ using ::testing::StrEq; auto ExpectUnifiedDiff(const llvm::SmallVector& actual, const llvm::SmallVector>& expected, const std::string& expected_diff) -> void { - testing::StringMatchResultListener listener; - EXPECT_FALSE(testing::ExplainMatchResult( - ElementsAreArrayWithUnifiedDiff(expected), actual, &listener)); - EXPECT_THAT(listener.str(), testing::Eq(expected_diff)); + std::stringstream ss; + ss << UnifiedDiff(expected, actual); + EXPECT_THAT(ss.str(), testing::Eq(expected_diff)); } -TEST(UnifiedDiffMatcherTest, Matches) { +TEST(UnifiedDiffTest, Matches) { llvm::SmallVector actual = {"A", "B", "C"}; llvm::SmallVector> expected = {StrEq("A"), StrEq("B"), StrEq("C")}; - EXPECT_THAT(actual, ElementsAreArrayWithUnifiedDiff(expected)); + EXPECT_TRUE(testing::Value(actual, ElementsAreArray(expected))) + << UnifiedDiff(expected, actual); + + std::stringstream ss; + ss << UnifiedDiff(expected, actual); + EXPECT_THAT(ss.str(), testing::Eq("")); } -TEST(UnifiedDiffMatcherTest, MismatchMissing) { +TEST(UnifiedDiffTest, MismatchMissing) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 3 (1-based index): - A -- is equal to "B" - C + A +-B + C === diff end )"; ExpectUnifiedDiff({"A", "C"}, {StrEq("A"), StrEq("B"), StrEq("C")}, ExpectedDiff); } -TEST(UnifiedDiffMatcherTest, MismatchExtra) { +TEST(UnifiedDiffTest, MismatchExtra) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 2 (1-based index): - A -+ B - C + A ++B + C === diff end )"; ExpectUnifiedDiff({"A", "B", "C"}, {StrEq("A"), StrEq("C")}, ExpectedDiff); } -TEST(UnifiedDiffMatcherTest, MismatchBoth) { +TEST(UnifiedDiffTest, MismatchBoth) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 2 (1-based index): - A -- is equal to "C" -+ B + A +-C ++B === diff end )"; ExpectUnifiedDiff({"A", "B"}, {StrEq("A"), StrEq("C")}, ExpectedDiff); } -TEST(UnifiedDiffMatcherTest, MismatchMultiple) { +TEST(UnifiedDiffTest, MismatchMultiple) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 5 (1-based index): - A -- is equal to "B" -+ X - C -- is equal to "D" -+ Y - E + A +-B ++X + C +-D ++Y + E === diff end )"; ExpectUnifiedDiff( @@ -87,17 +94,17 @@ TEST(UnifiedDiffMatcherTest, MismatchMultiple) { ExpectedDiff); } -TEST(UnifiedDiffMatcherTest, MismatchLongContext) { +TEST(UnifiedDiffTest, MismatchLongContext) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 2 to 8 (1-based index): - 1 - 2 - 3 -- is equal to "X" -+ 4 - 5 - 6 - 7 + 1 + 2 + 3 +-X ++4 + 5 + 6 + 7 === diff end )"; ExpectUnifiedDiff({"0", "1", "2", "3", "4", "5", "6", "7", "8"}, @@ -106,18 +113,18 @@ TEST(UnifiedDiffMatcherTest, MismatchLongContext) { ExpectedDiff); } -TEST(UnifiedDiffMatcherTest, Mismatch5LineContext) { +TEST(UnifiedDiffTest, Mismatch5LineContext) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 7 (1-based index): -- is equal to "X" -+ 0 - 1 - 2 - 3 - 4 - 5 -- is equal to "Y" -+ 6 +-X ++0 + 1 + 2 + 3 + 4 + 5 +-Y ++6 === diff end )"; ExpectUnifiedDiff({"0", "1", "2", "3", "4", "5", "6"}, @@ -126,19 +133,19 @@ TEST(UnifiedDiffMatcherTest, Mismatch5LineContext) { ExpectedDiff); } -TEST(UnifiedDiffMatcherTest, Mismatch6LineContext) { +TEST(UnifiedDiffTest, Mismatch6LineContext) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 8 (1-based index): -- is equal to "X" -+ 0 - 1 - 2 - 3 - 4 - 5 - 6 -- is equal to "Y" -+ 7 +-X ++0 + 1 + 2 + 3 + 4 + 5 + 6 +-Y ++7 === diff end )"; ExpectUnifiedDiff({"0", "1", "2", "3", "4", "5", "6", "7"}, @@ -147,20 +154,20 @@ TEST(UnifiedDiffMatcherTest, Mismatch6LineContext) { ExpectedDiff); } -TEST(UnifiedDiffMatcherTest, Mismatch7LineContext) { +TEST(UnifiedDiffTest, Mismatch7LineContext) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 4 (1-based index): -- is equal to "X" -+ 0 - 1 - 2 - 3 +-X ++0 + 1 + 2 + 3 === diff in expected elements 6 to 9 (1-based index): - 5 - 6 - 7 -- is equal to "Y" -+ 8 + 5 + 6 + 7 +-Y ++8 === diff end )"; ExpectUnifiedDiff({"0", "1", "2", "3", "4", "5", "6", "7", "8"}, @@ -169,34 +176,34 @@ TEST(UnifiedDiffMatcherTest, Mismatch7LineContext) { ExpectedDiff); } -TEST(UnifiedDiffMatcherTest, MismatchEmptyExpected) { +TEST(UnifiedDiffTest, MismatchEmptyExpected) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 1 (1-based index): -+ A ++A === diff end )"; ExpectUnifiedDiff({"A"}, {}, ExpectedDiff); } -TEST(UnifiedDiffMatcherTest, MismatchEmptyActual) { +TEST(UnifiedDiffTest, MismatchEmptyActual) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 1 (1-based index): -- is equal to "A" +-A === diff end )"; ExpectUnifiedDiff({}, {StrEq("A")}, ExpectedDiff); } -TEST(UnifiedDiffMatcherTest, MismatchLongDifference) { +TEST(UnifiedDiffTest, MismatchLongDifference) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 4 (1-based index): - 1 -- is equal to "2" -- is equal to "3" -+ X -+ Y -+ Z - 4 + 1 +-2 +-3 ++X ++Y ++Z + 4 === diff end )"; ExpectUnifiedDiff({"1", "X", "Y", "Z", "4"}, @@ -204,17 +211,17 @@ TEST(UnifiedDiffMatcherTest, MismatchLongDifference) { ExpectedDiff); } -TEST(UnifiedDiffMatcherTest, MismatchGreedyResyncActualMissing) { +TEST(UnifiedDiffTest, MismatchGreedyResyncActualMissing) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 6 (1-based index): - 1 - 2 -- is equal to "3" -+ X -+ 7 - 4 - 5 - 6 + 1 + 2 +-3 ++X ++7 + 4 + 5 + 6 === diff end )"; ExpectUnifiedDiff({"1", "2", "X", "7", "4", "5", "6", "7", "8", "9"}, @@ -223,17 +230,17 @@ TEST(UnifiedDiffMatcherTest, MismatchGreedyResyncActualMissing) { ExpectedDiff); } -TEST(UnifiedDiffMatcherTest, MismatchGreedyResyncExpectedMissing) { +TEST(UnifiedDiffTest, MismatchGreedyResyncExpectedMissing) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 7 (1-based index): - 1 - 2 -- is equal to "X" -- is equal to "7" -+ 3 - 4 - 5 - 6 + 1 + 2 +-X +-7 ++3 + 4 + 5 + 6 === diff end )"; ExpectUnifiedDiff( @@ -243,5 +250,38 @@ TEST(UnifiedDiffMatcherTest, MismatchGreedyResyncExpectedMissing) { ExpectedDiff); } +TEST(UnifiedDiffTest, MismatchRegexMatcher) { + constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): +=== diff in expected elements 1 to 3 (1-based index): + A +-matches regular expression ".*B.*" + C +=== diff end +)"; + ExpectUnifiedDiff({"A", "C"}, {StrEq("A"), MatchesRegex(".*B.*"), StrEq("C")}, + ExpectedDiff); +} + +TEST(UnifiedDiffTest, CheckSubset) { + constexpr char ExpectedDiff[] = + R"(unified diff (- expected, + actual) [+ lines are normal]: +=== diff in expected elements 1 to 4 (1-based index): +-X ++0 + 1 + 2 + 3 +=== diff end +)"; + std::stringstream ss; + llvm::SmallVector> expected = { + StrEq("X"), StrEq("1"), StrEq("2"), StrEq("3"), StrEq("4"), + StrEq("5"), StrEq("6"), StrEq("7"), StrEq("8"), StrEq("9")}; + llvm::SmallVector actual = {"0", "1", "2", "3", "4", "5", "6", + "7", "8", "9", "10", "11", "12"}; + ss << UnifiedDiff(expected, actual, /*check_subset=*/true); + EXPECT_THAT(ss.str(), testing::Eq(ExpectedDiff)); +} + } // namespace } // namespace Carbon::Testing diff --git a/testing/file_test/BUILD b/testing/file_test/BUILD index 3ca35b141408..043cf9fd275f 100644 --- a/testing/file_test/BUILD +++ b/testing/file_test/BUILD @@ -54,7 +54,7 @@ cc_library( "//common:raw_string_ostream", "//common:set", "//testing/base:file_helpers", - "//testing/base:unified_diff_matcher", + "//testing/base:unified_diff", "@abseil-cpp//absl/flags:flag", "@abseil-cpp//absl/flags:parse", "@abseil-cpp//absl/strings", diff --git a/testing/file_test/file_test_base.cpp b/testing/file_test/file_test_base.cpp index a316d6d35f21..f095e501fd60 100644 --- a/testing/file_test/file_test_base.cpp +++ b/testing/file_test/file_test_base.cpp @@ -50,7 +50,7 @@ #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Process.h" #include "llvm/Support/ThreadPool.h" -#include "testing/base/unified_diff_matcher.h" +#include "testing/base/unified_diff.h" #include "testing/file_test/autoupdate.h" #include "testing/file_test/run_test.h" #include "testing/file_test/test_file.h" @@ -248,20 +248,30 @@ auto FileTestCase::TestBody() -> void { "updates."); } if (test_file.check_subset) { - EXPECT_THAT(SplitOutput(test_file.actual_stdout), - IsSupersetOf(test_file.expected_stdout)) - << "Actual text:\n" - << test_file.actual_stdout; - EXPECT_THAT(SplitOutput(test_file.actual_stderr), - IsSupersetOf(test_file.expected_stderr)) - << "Actual text:\n" - << test_file.actual_stderr; + EXPECT_TRUE( + testing::Value(SplitOutput(test_file.actual_stdout), + testing::IsSupersetOf(test_file.expected_stdout))) + << UnifiedDiff(test_file.expected_stdout, + SplitOutput(test_file.actual_stdout), + /*check_subset=*/true); + EXPECT_TRUE( + testing::Value(SplitOutput(test_file.actual_stderr), + testing::IsSupersetOf(test_file.expected_stderr))) + << UnifiedDiff(test_file.expected_stderr, + SplitOutput(test_file.actual_stderr), + /*check_subset=*/true); } else { - EXPECT_THAT(SplitOutput(test_file.actual_stdout), - ElementsAreArrayWithUnifiedDiff(test_file.expected_stdout)); - EXPECT_THAT(SplitOutput(test_file.actual_stderr), - ElementsAreArrayWithUnifiedDiff(test_file.expected_stderr)); + EXPECT_TRUE( + testing::Value(SplitOutput(test_file.actual_stdout), + testing::ElementsAreArray(test_file.expected_stdout))) + << UnifiedDiff(test_file.expected_stdout, + SplitOutput(test_file.actual_stdout)); + EXPECT_TRUE( + testing::Value(SplitOutput(test_file.actual_stderr), + testing::ElementsAreArray(test_file.expected_stderr))) + << UnifiedDiff(test_file.expected_stderr, + SplitOutput(test_file.actual_stderr)); } if (HasFailure()) {