Selaa lähdekoodia

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.
Chandler Carruth 3 kuukautta sitten
vanhempi
sitoutus
b2ab53e49c

+ 1 - 0
common/BUILD

@@ -199,6 +199,7 @@ cc_library(
     hdrs = ["error_test_helpers.h"],
     deps = [
         ":error",
+        ":ostream",
         "@googletest//:gtest",
     ],
 )

+ 5 - 0
common/error_test_helpers.h

@@ -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 - 0
toolchain/testing/BUILD

@@ -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
toolchain/testing/yaml_test_helpers.cpp

@@ -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
toolchain/testing/yaml_test_helpers.h

@@ -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>;

+ 18 - 0
toolchain/testing/yaml_test_helpers_test.cpp

@@ -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