Fix an incompatiblitiy between our YAML and ErrorOr test helpers (#6636)

The YAML test helpers didn't use the `Printable` abstraction in one
place and instead directly used `<<` with a `std::ostream`. This matches
the `require`s expression in the `error_test_helpers.h` printing logic
for `ErrorOr`, but fails to provide the necessary implementation for
`llvm::formatv` to succeed with the `Yaml::Value` type.

The main fix is to use `Printable` and to define the `Print` method in
terms of `llvm::raw_ostream`. We already have all the mapping hooks in
place to also support `std::ostream` when needed based on that
definition.

This also adds some constraints to the printing in
`error_test_helpers.h` so it is a bit less under-constrained and more
understandable when it is correctly being used. These are just tidying
though, they aren't what makes these headers work together.

I've added a test to try and make sure these test helpers compose as
well.
This commit is contained in:
Chandler Carruth
2026-01-21 17:41:46 +00:00
committed by GitHub
parent 114d892ac1
commit b2ab53e49c
6 changed files with 38 additions and 7 deletions
+1
View File
@@ -199,6 +199,7 @@ cc_library(
hdrs = ["error_test_helpers.h"],
deps = [
":error",
":ostream",
"@googletest//:gtest",
],
)
+5
View File
@@ -7,7 +7,10 @@
#include <gmock/gmock.h>
#include <concepts>
#include "common/error.h"
#include "common/ostream.h"
namespace Carbon::Testing {
@@ -122,6 +125,8 @@ namespace Carbon {
// Supports printing `ErrorOr<T>` to `std::ostream` in tests.
template <typename T, typename ErrorT>
requires(std::same_as<ErrorT, Error> ||
std::derived_from<ErrorT, ErrorBase<ErrorT>>)
auto operator<<(std::ostream& out, const ErrorOr<T, ErrorT>& error_or)
-> std::ostream& {
if (error_or.ok()) {
+1
View File
@@ -98,6 +98,7 @@ cc_test(
srcs = ["yaml_test_helpers_test.cpp"],
deps = [
":yaml_test_helpers",
"//common:error_test_helpers",
"//testing/base:gtest_main",
"@googletest//:gtest",
],
+9 -5
View File
@@ -88,13 +88,17 @@ auto Value::FromText(llvm::StringRef text) -> ErrorOr<SequenceValue> {
return result;
}
auto operator<<(std::ostream& os, const Value& v) -> std::ostream& {
auto Value::Print(llvm::raw_ostream& os) const -> void {
// Variant visitor that prints the value in the form of code to recreate the
// value.
struct Printer {
auto operator()(NullValue /*v*/) -> void { out << "Yaml::NullValue()"; }
auto operator()(AliasValue /*v*/) -> void { out << "Yaml::AliasValue()"; }
auto operator()(const ScalarValue& v) -> void { out << std::quoted(v); }
auto operator()(const ScalarValue& v) -> void {
out << "\"";
out.write_escaped(v);
out << "\"";
}
auto operator()(const MappingValue& v) -> void {
out << "Yaml::MappingValue{";
bool first = true;
@@ -122,10 +126,10 @@ auto operator<<(std::ostream& os, const Value& v) -> std::ostream& {
out << "}";
}
std::ostream& out;
llvm::raw_ostream& out;
};
std::visit(Printer{.out = os}, v);
return os;
std::visit(Printer{.out = os}, *this);
}
} // namespace Carbon::Testing::Yaml
+4 -2
View File
@@ -55,6 +55,7 @@
#include "absl/strings/str_replace.h"
#include "common/error.h"
#include "common/ostream.h"
#include "llvm/ADT/StringRef.h"
namespace Carbon::Testing::Yaml {
@@ -81,12 +82,13 @@ struct AliasValue : EmptyComparable {};
// A thin wrapper around a variant of possible YAML value types. This type
// intentionally provides no additional encapsulation or invariants beyond
// those of the variant.
struct Value : std::variant<NullValue, ScalarValue, MappingValue, SequenceValue,
struct Value : Carbon::Printable<Value>,
std::variant<NullValue, ScalarValue, MappingValue, SequenceValue,
AliasValue> {
using variant::variant;
// Prints the Value in the form of code to recreate the value.
friend auto operator<<(std::ostream& os, const Value& v) -> std::ostream&;
auto Print(llvm::raw_ostream& os) const -> void;
// Parses a sequence of YAML documents from the given YAML text.
static auto FromText(llvm::StringRef text) -> ErrorOr<SequenceValue>;
@@ -7,6 +7,8 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "common/error_test_helpers.h"
namespace Carbon::Testing {
namespace {
@@ -28,5 +30,21 @@ TEST(YamlTestHelpersTest, InvalidYaml) {
EXPECT_THAT(result, Not(Yaml::IsYaml(_)));
}
TEST(YamlTestHelpersTest, ComposeWithErrorOr) {
auto helper = []() -> ErrorOr<Yaml::Value> {
auto result = Yaml::Value::FromText("[foo, bar]");
if (!result.ok()) {
return std::move(result).error();
}
return {*std::move(result)};
};
// Make sure this works correctly with the generic `ErrorOr` test helper as
// well. Note that `FromText` always produces a sequence of its own, so there
// are two layers of nested sequence here.
EXPECT_THAT(helper(), IsSuccess(Yaml::Sequence(ElementsAre(
Yaml::Sequence(ElementsAre("foo", "bar"))))));
}
} // namespace
} // namespace Carbon::Testing