mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:20:13 +01:00
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
This commit is contained in:
+5
-5
@@ -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",
|
||||
],
|
||||
|
||||
@@ -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 <gmock/gmock.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
@@ -19,27 +21,24 @@
|
||||
|
||||
namespace Carbon::Testing {
|
||||
|
||||
// Matcher that compares the elements of two containers and produces a unified
|
||||
// diff on failure.
|
||||
template <typename Container>
|
||||
class UnifiedDiffMatcher {
|
||||
// Compares the elements of two containers and prints a unified diff when
|
||||
// streamed to an ostream.
|
||||
template <typename ExpectedContainer, typename ActualContainer>
|
||||
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 <typename ActualContainer>
|
||||
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 <typename ActualContainer>
|
||||
auto IsEqual(const ActualContainer& actual,
|
||||
Table<MatchResult>& match_results) const -> bool;
|
||||
// element in `actual_`. Stores comparisons in `match_results`.
|
||||
auto IsEqual(Table<MatchResult>& 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 <typename ActualContainer>
|
||||
auto GetLongestCommonSubsequences(const ActualContainer& actual,
|
||||
Table<MatchResult>& match_results,
|
||||
auto GetLongestCommonSubsequences(Table<MatchResult>& match_results,
|
||||
Table<int>& subsequences) const -> void;
|
||||
|
||||
// Prints the unified diff.
|
||||
template <typename ActualContainer>
|
||||
auto PrintDiff(const ActualContainer& actual,
|
||||
Table<MatchResult>& match_results,
|
||||
const Table<int>& subsequences,
|
||||
testing::MatchResultListener* listener) const -> void;
|
||||
auto PrintDiff(Table<MatchResult>& match_results,
|
||||
const Table<int>& 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 <typename Container>
|
||||
auto ElementsAreArrayWithUnifiedDiff(Container expected) {
|
||||
return testing::MakePolymorphicMatcher(
|
||||
UnifiedDiffMatcher<Container>(std::move(expected)));
|
||||
}
|
||||
template <typename ExpectedContainer, typename ActualContainer>
|
||||
UnifiedDiff(const ExpectedContainer&, const ActualContainer&)
|
||||
-> UnifiedDiff<ExpectedContainer, ActualContainer>;
|
||||
template <typename ExpectedContainer, typename ActualContainer>
|
||||
UnifiedDiff(const ExpectedContainer&, const ActualContainer&, bool)
|
||||
-> UnifiedDiff<ExpectedContainer, ActualContainer>;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Internal implementation details follow.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
template <typename Container>
|
||||
template <typename ExpectedContainer, typename ActualContainer>
|
||||
template <typename T>
|
||||
class UnifiedDiffMatcher<Container>::Table {
|
||||
class UnifiedDiff<ExpectedContainer, ActualContainer>::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<Container>::Table {
|
||||
llvm::SmallVector<T> data_;
|
||||
};
|
||||
|
||||
template <typename Container>
|
||||
template <typename ActualContainer>
|
||||
auto UnifiedDiffMatcher<Container>::MatchAndExplain(
|
||||
const ActualContainer& actual, testing::MatchResultListener* listener) const
|
||||
-> bool {
|
||||
Table<MatchResult> match_results(expected_.size(), std::size(actual),
|
||||
template <typename ExpectedContainer, typename ActualContainer>
|
||||
auto UnifiedDiff<ExpectedContainer, ActualContainer>::Print(
|
||||
std::ostream* os) const -> void {
|
||||
Table<MatchResult> 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<int> subsequences(expected_.size() + 1, std::size(actual) + 1, 0);
|
||||
GetLongestCommonSubsequences(actual, match_results, subsequences);
|
||||
PrintDiff(actual, match_results, subsequences, listener);
|
||||
}
|
||||
return false;
|
||||
Table<int> subsequences(expected_.size() + 1, std::size(actual_) + 1, 0);
|
||||
GetLongestCommonSubsequences(match_results, subsequences);
|
||||
PrintDiff(match_results, subsequences, os);
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
template <typename ActualContainer>
|
||||
auto UnifiedDiffMatcher<Container>::IsEqual(
|
||||
const ActualContainer& actual, Table<MatchResult>& match_results) const
|
||||
-> bool {
|
||||
if (expected_.size() != std::size(actual)) {
|
||||
template <typename ExpectedContainer, typename ActualContainer>
|
||||
auto UnifiedDiff<ExpectedContainer, ActualContainer>::IsEqual(
|
||||
Table<MatchResult>& 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<Container>::IsEqual(
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
template <typename ActualContainer>
|
||||
auto UnifiedDiffMatcher<Container>::GetLongestCommonSubsequences(
|
||||
const ActualContainer& actual, Table<MatchResult>& match_results,
|
||||
Table<int>& subsequences) const -> void {
|
||||
template <typename ExpectedContainer, typename ActualContainer>
|
||||
auto UnifiedDiff<ExpectedContainer, ActualContainer>::
|
||||
GetLongestCommonSubsequences(Table<MatchResult>& match_results,
|
||||
Table<int>& 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<Container>::GetLongestCommonSubsequences(
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
template <typename ActualContainer>
|
||||
auto UnifiedDiffMatcher<Container>::PrintDiff(
|
||||
const ActualContainer& actual, Table<MatchResult>& match_results,
|
||||
const Table<int>& subsequences,
|
||||
testing::MatchResultListener* listener) const -> void {
|
||||
template <typename ExpectedContainer, typename ActualContainer>
|
||||
auto UnifiedDiff<ExpectedContainer, ActualContainer>::PrintDiff(
|
||||
Table<MatchResult>& match_results, const Table<int>& 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<Container>::PrintDiff(
|
||||
|
||||
llvm::SmallVector<DiffLine> 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<Container>::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_
|
||||
@@ -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 <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#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<std::string>& actual,
|
||||
const llvm::SmallVector<Matcher<std::string>>& 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<std::string> actual = {"A", "B", "C"};
|
||||
llvm::SmallVector<Matcher<std::string>> 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<Matcher<std::string>> expected = {
|
||||
StrEq("X"), StrEq("1"), StrEq("2"), StrEq("3"), StrEq("4"),
|
||||
StrEq("5"), StrEq("6"), StrEq("7"), StrEq("8"), StrEq("9")};
|
||||
llvm::SmallVector<std::string> 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
|
||||
@@ -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",
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
Reference in New Issue
Block a user