mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-03 19:15:52 +01:00
[toolchain] Add simpler mechanism for matching YAML output in tests. (#583)
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
# Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
# Exceptions. See /LICENSE for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
cc_library(
|
||||
name = "yaml_test_helpers",
|
||||
testonly = 1,
|
||||
srcs = ["yaml_test_helpers.cpp"],
|
||||
hdrs = ["yaml_test_helpers.h"],
|
||||
deps = [
|
||||
"@llvm-project//llvm:gmock",
|
||||
"@llvm-project//llvm:gtest",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,110 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/common/yaml_test_helpers.h"
|
||||
|
||||
#include "llvm/Support/YAMLParser.h"
|
||||
|
||||
namespace Carbon::Testing::Yaml {
|
||||
|
||||
static auto Parse(llvm::yaml::Node* node) -> Value {
|
||||
if (!node) {
|
||||
return Value{ErrorValue()};
|
||||
}
|
||||
|
||||
switch (node->getType()) {
|
||||
case llvm::yaml::Node::NK_Null:
|
||||
return Value{NullValue()};
|
||||
|
||||
case llvm::yaml::Node::NK_Scalar: {
|
||||
llvm::SmallString<128> storage;
|
||||
return Value{
|
||||
llvm::cast<llvm::yaml::ScalarNode>(*node).getValue(storage).str()};
|
||||
}
|
||||
|
||||
case llvm::yaml::Node::NK_BlockScalar:
|
||||
return Value{
|
||||
llvm::cast<llvm::yaml::BlockScalarNode>(*node).getValue().str()};
|
||||
|
||||
case llvm::yaml::Node::NK_Mapping: {
|
||||
MappingValue v;
|
||||
for (llvm::yaml::KeyValueNode& kv :
|
||||
llvm::cast<llvm::yaml::MappingNode>(*node)) {
|
||||
Value key = Parse(kv.getKey());
|
||||
Value value = Parse(kv.getValue());
|
||||
v.emplace_back(std::move(key), std::move(value));
|
||||
}
|
||||
return Value{std::move(v)};
|
||||
}
|
||||
|
||||
case llvm::yaml::Node::NK_Sequence: {
|
||||
SequenceValue v;
|
||||
for (llvm::yaml::Node& n : llvm::cast<llvm::yaml::SequenceNode>(*node)) {
|
||||
v.push_back(Parse(&n));
|
||||
}
|
||||
return Value{std::move(v)};
|
||||
}
|
||||
|
||||
case llvm::yaml::Node::NK_Alias:
|
||||
return Value{AliasValue()};
|
||||
|
||||
case llvm::yaml::Node::NK_KeyValue:
|
||||
llvm_unreachable("should only exist as child of mapping");
|
||||
}
|
||||
|
||||
llvm_unreachable("unknown yaml node kind");
|
||||
}
|
||||
|
||||
auto Value::FromText(llvm::StringRef text) -> SequenceValue {
|
||||
llvm::SourceMgr sm;
|
||||
llvm::yaml::Stream yaml_stream(text, sm);
|
||||
|
||||
SequenceValue result;
|
||||
for (llvm::yaml::Document& document : yaml_stream) {
|
||||
result.push_back(Parse(document.getRoot()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto operator<<(std::ostream& os, const Value& v) -> std::ostream& {
|
||||
// Variant visitor that prints the value in the form of code to recreate the
|
||||
// value.
|
||||
struct Printer {
|
||||
std::ostream& out;
|
||||
auto operator()(NullValue) -> void { out << "Yaml::NullValue()"; }
|
||||
auto operator()(AliasValue) -> void { out << "Yaml::AliasValue()"; }
|
||||
auto operator()(ErrorValue) -> void { out << "Yaml::ErrorValue()"; }
|
||||
auto operator()(const ScalarValue& v) -> void { out << std::quoted(v); }
|
||||
auto operator()(const MappingValue& v) -> void {
|
||||
out << "Yaml::MappingValue{";
|
||||
bool first = true;
|
||||
for (auto& [key, value] : v) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
out << ", ";
|
||||
}
|
||||
out << "{" << key << ", " << value << "}";
|
||||
}
|
||||
out << "}";
|
||||
}
|
||||
auto operator()(const SequenceValue& v) -> void {
|
||||
out << "Yaml::SequenceValue{";
|
||||
bool first = true;
|
||||
for (auto& value : v) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
out << ", ";
|
||||
}
|
||||
out << value;
|
||||
}
|
||||
out << "}";
|
||||
}
|
||||
};
|
||||
std::visit(Printer{os}, v);
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace Carbon::Testing::Yaml
|
||||
@@ -0,0 +1,149 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// This file provides gmock matchers to support testing YAML output.
|
||||
//
|
||||
// A YAML document can be converted into a matchable value using
|
||||
// Yaml::Value::FromText, and then matched with Yaml::Mapping, Yaml::Sequence,
|
||||
// or Yaml::Scalar. Scalar values can also be matched directly against strings.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// namespace Yaml = Carbon::Testing::Yaml;
|
||||
// using ::testing::ElementsAre;
|
||||
// using ::testing::Pair;
|
||||
// Yaml::Value yaml = Yaml::Value::FromText(R"yaml(
|
||||
// ---
|
||||
// fruits:
|
||||
// - apple
|
||||
// - orange
|
||||
// - pear
|
||||
// ...
|
||||
// ---
|
||||
// - [foo: "bar"]: "baz"
|
||||
// )yaml"),
|
||||
//
|
||||
// // Exact values can be matched by constructing the desired value.
|
||||
// EXPECT_THAT(
|
||||
// yaml,
|
||||
// ElementsAre(
|
||||
// Yaml::MappingValue{
|
||||
// {"fruits", Yaml::SequenceValue{"apple", "orange", "pear"}}},
|
||||
// Yaml::SequenceValue{Yaml::MappingValue{
|
||||
// {Yaml::SequenceValue{Yaml::MappingValue{{"foo", "bar"}}},
|
||||
// "baz"}}}));
|
||||
//
|
||||
// // Properties can be checked using Yaml::Mapping or Yaml::Sequence to
|
||||
// // adapt regular gmock container matchers.
|
||||
// EXPECT_THAT(
|
||||
// yaml,
|
||||
// Contains(Yaml::Mapping(
|
||||
// Contains(Pair("fruits", Yaml::Sequence(Contains("orange")))))));
|
||||
//
|
||||
// On match failure, Yaml::Values are printed as C++ code that can be used to
|
||||
// recreate the value, for easy copy-pasting into test expectations.
|
||||
|
||||
#ifndef TOOLCHAIN_COMMON_YAML_TEST_HELPERS_H_
|
||||
#define TOOLCHAIN_COMMON_YAML_TEST_HELPERS_H_
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <variant>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace Carbon {
|
||||
namespace Testing {
|
||||
namespace Yaml {
|
||||
|
||||
struct EmptyComparable {
|
||||
friend auto operator==(EmptyComparable, EmptyComparable) -> bool {
|
||||
return true;
|
||||
}
|
||||
friend auto operator!=(EmptyComparable, EmptyComparable) -> bool {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct Value;
|
||||
struct NullValue : EmptyComparable {};
|
||||
using ScalarValue = std::string;
|
||||
using MappingValue = std::vector<std::pair<Value, Value>>;
|
||||
using SequenceValue = std::vector<Value>;
|
||||
struct AliasValue : EmptyComparable {};
|
||||
struct ErrorValue : 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,
|
||||
AliasValue, ErrorValue> {
|
||||
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&;
|
||||
|
||||
// Parses a sequence of YAML documents from the given YAML text.
|
||||
static auto FromText(llvm::StringRef text) -> SequenceValue;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
auto DescribeMatcher(::testing::Matcher<T> matcher) -> std::string {
|
||||
std::ostringstream out;
|
||||
matcher.DescribeTo(&out);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
// Match a Value that is a MappingValue.
|
||||
// Same as testing::VariantWith<MappingValue>(contents).
|
||||
// NOLINTNEXTLINE: Expands from GoogleTest.
|
||||
MATCHER_P(Mapping, contents,
|
||||
"is mapping that " + DescribeMatcher<MappingValue>(contents)) {
|
||||
::testing::Matcher<MappingValue> contents_matcher = contents;
|
||||
|
||||
if (auto* map = std::get_if<MappingValue>(&arg)) {
|
||||
return contents_matcher.MatchAndExplain(*map, result_listener);
|
||||
}
|
||||
|
||||
*result_listener << "which is not a mapping";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Match a Value that is a SequenceValue.
|
||||
// Same as testing::VariantWith<SequenceValue>(contents).
|
||||
// NOLINTNEXTLINE: Expands from GoogleTest.
|
||||
MATCHER_P(Sequence, contents,
|
||||
"is mapping that " + DescribeMatcher<SequenceValue>(contents)) {
|
||||
::testing::Matcher<SequenceValue> contents_matcher = contents;
|
||||
|
||||
if (auto* map = std::get_if<SequenceValue>(&arg)) {
|
||||
return contents_matcher.MatchAndExplain(*map, result_listener);
|
||||
}
|
||||
|
||||
*result_listener << "which is not a sequence";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Match a Value that is a ScalarValue.
|
||||
// Same as testing::VariantWith<ScalarValue>(contents).
|
||||
// NOLINTNEXTLINE: Expands from GoogleTest.
|
||||
MATCHER_P(Scalar, value,
|
||||
"has scalar value " + ::testing::PrintToString(value)) {
|
||||
::testing::Matcher<ScalarValue> value_matcher = value;
|
||||
|
||||
if (auto* map = std::get_if<ScalarValue>(&arg)) {
|
||||
return value_matcher.MatchAndExplain(*map, result_listener);
|
||||
}
|
||||
|
||||
*result_listener << "which is not a scalar";
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Yaml
|
||||
} // namespace Testing
|
||||
} // namespace Carbon
|
||||
|
||||
#endif // TOOLCHAIN_COMMON_YAML_TEST_HELPERS_H_
|
||||
@@ -25,6 +25,7 @@ cc_test(
|
||||
srcs = ["driver_test.cpp"],
|
||||
deps = [
|
||||
":driver",
|
||||
"//toolchain/common:yaml_test_helpers",
|
||||
"//toolchain/lexer:tokenized_buffer_test_helpers",
|
||||
"@llvm-project//llvm:Support",
|
||||
"@llvm-project//llvm:gmock",
|
||||
|
||||
@@ -9,17 +9,17 @@
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/SourceMgr.h"
|
||||
#include "llvm/Support/YAMLParser.h"
|
||||
#include "toolchain/lexer/tokenized_buffer_test_helpers.h"
|
||||
#include "toolchain/common/yaml_test_helpers.h"
|
||||
|
||||
namespace Carbon {
|
||||
namespace {
|
||||
|
||||
using Carbon::Testing::IsKeyValueScalars;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::Eq;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::StrEq;
|
||||
namespace Yaml = Carbon::Testing::Yaml;
|
||||
|
||||
/// A raw_ostream that makes it easy to repeatedly check streamed output.
|
||||
class RawTestOstream : public llvm::raw_ostream {
|
||||
@@ -130,96 +130,30 @@ TEST(DriverTest, DumpTokens) {
|
||||
EXPECT_THAT(test_error_stream.TakeStr(), StrEq(""));
|
||||
auto tokenized_text = test_output_stream.TakeStr();
|
||||
|
||||
// Parse the output into a YAML stream. This will print errors to stderr and
|
||||
// is the most stable view of the textual dumping API.
|
||||
llvm::SourceMgr sm;
|
||||
llvm::yaml::Stream yaml_stream(tokenized_text, sm);
|
||||
auto yaml_it = yaml_stream.begin();
|
||||
auto* root_node = llvm::dyn_cast<llvm::yaml::MappingNode>(yaml_it->getRoot());
|
||||
ASSERT_THAT(root_node, NotNull());
|
||||
|
||||
// Walk the top-level mapping of tokens, dig out the sub-mapping of data for
|
||||
// each taken, and then verify those entries.
|
||||
auto mapping_it = llvm::cast<llvm::yaml::MappingNode>(root_node)->begin();
|
||||
auto* token_node = llvm::dyn_cast<llvm::yaml::KeyValueNode>(&*mapping_it);
|
||||
ASSERT_THAT(token_node, NotNull());
|
||||
auto* token_key_node =
|
||||
llvm::dyn_cast<llvm::yaml::ScalarNode>(token_node->getKey());
|
||||
ASSERT_THAT(token_key_node, NotNull());
|
||||
EXPECT_THAT(token_key_node->getRawValue(), StrEq("token"));
|
||||
auto* token_value_node =
|
||||
llvm::dyn_cast<llvm::yaml::MappingNode>(token_node->getValue());
|
||||
ASSERT_THAT(token_value_node, NotNull());
|
||||
auto token_it = token_value_node->begin();
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("index", "0"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("kind", "Identifier"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("line", "1"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("column", "1"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("indent", "1"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("spelling", "Hello"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("identifier", "0"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("has_trailing_space", "true"));
|
||||
EXPECT_THAT(++token_it, Eq(token_value_node->end()));
|
||||
|
||||
++mapping_it;
|
||||
token_node = llvm::dyn_cast<llvm::yaml::KeyValueNode>(&*mapping_it);
|
||||
ASSERT_THAT(token_node, NotNull());
|
||||
token_key_node = llvm::dyn_cast<llvm::yaml::ScalarNode>(token_node->getKey());
|
||||
ASSERT_THAT(token_key_node, NotNull());
|
||||
EXPECT_THAT(token_key_node->getRawValue(), StrEq("token"));
|
||||
token_value_node =
|
||||
llvm::dyn_cast<llvm::yaml::MappingNode>(token_node->getValue());
|
||||
ASSERT_THAT(token_value_node, NotNull());
|
||||
token_it = token_value_node->begin();
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("index", "1"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("kind", "Identifier"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("line", "1"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("column", "7"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("indent", "1"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("spelling", "World"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("identifier", "1"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("has_trailing_space", "true"));
|
||||
EXPECT_THAT(++token_it, Eq(token_value_node->end()));
|
||||
|
||||
++mapping_it;
|
||||
token_node = llvm::dyn_cast<llvm::yaml::KeyValueNode>(&*mapping_it);
|
||||
ASSERT_THAT(token_node, NotNull());
|
||||
token_key_node = llvm::dyn_cast<llvm::yaml::ScalarNode>(token_node->getKey());
|
||||
ASSERT_THAT(token_key_node, NotNull());
|
||||
EXPECT_THAT(token_key_node->getRawValue(), StrEq("token"));
|
||||
token_value_node =
|
||||
llvm::dyn_cast<llvm::yaml::MappingNode>(token_node->getValue());
|
||||
ASSERT_THAT(token_value_node, NotNull());
|
||||
token_it = token_value_node->begin();
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("index", "2"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("kind", "EndOfFile"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("line", "1"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("column", "12"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("indent", "1"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("spelling", ""));
|
||||
EXPECT_THAT(++token_it, Eq(token_value_node->end()));
|
||||
|
||||
ASSERT_THAT(++mapping_it, Eq(root_node->end()));
|
||||
ASSERT_THAT(++yaml_it, Eq(yaml_stream.end()));
|
||||
EXPECT_THAT(Yaml::Value::FromText(tokenized_text),
|
||||
ElementsAre(Yaml::MappingValue{
|
||||
{"token", Yaml::MappingValue{{"index", "0"},
|
||||
{"kind", "Identifier"},
|
||||
{"line", "1"},
|
||||
{"column", "1"},
|
||||
{"indent", "1"},
|
||||
{"spelling", "Hello"},
|
||||
{"identifier", "0"},
|
||||
{"has_trailing_space", "true"}}},
|
||||
{"token", Yaml::MappingValue{{"index", "1"},
|
||||
{"kind", "Identifier"},
|
||||
{"line", "1"},
|
||||
{"column", "7"},
|
||||
{"indent", "1"},
|
||||
{"spelling", "World"},
|
||||
{"identifier", "1"},
|
||||
{"has_trailing_space", "true"}}},
|
||||
{"token", Yaml::MappingValue{{"index", "2"},
|
||||
{"kind", "EndOfFile"},
|
||||
{"line", "1"},
|
||||
{"column", "12"},
|
||||
{"indent", "1"},
|
||||
{"spelling", ""}}}}));
|
||||
|
||||
// Check that the subcommand dispatch works.
|
||||
EXPECT_TRUE(driver.RunFullCommand({"dump-tokens", test_file_path}));
|
||||
|
||||
@@ -148,6 +148,7 @@ cc_test(
|
||||
deps = [
|
||||
":tokenized_buffer",
|
||||
":tokenized_buffer_test_helpers",
|
||||
"//toolchain/common:yaml_test_helpers",
|
||||
"//toolchain/diagnostics:diagnostic_emitter",
|
||||
"//toolchain/diagnostics:mocks",
|
||||
"@llvm-project//llvm:Support",
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/SourceMgr.h"
|
||||
#include "llvm/Support/YAMLParser.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "toolchain/common/yaml_test_helpers.h"
|
||||
#include "toolchain/diagnostics/diagnostic_emitter.h"
|
||||
#include "toolchain/diagnostics/mocks.h"
|
||||
#include "toolchain/lexer/tokenized_buffer_test_helpers.h"
|
||||
@@ -27,11 +27,12 @@ using ::Carbon::Testing::DiagnosticAt;
|
||||
using ::Carbon::Testing::DiagnosticMessage;
|
||||
using ::Carbon::Testing::ExpectedToken;
|
||||
using ::Carbon::Testing::HasTokens;
|
||||
using ::Carbon::Testing::IsKeyValueScalars;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::Eq;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::StrEq;
|
||||
namespace Yaml = Carbon::Testing::Yaml;
|
||||
|
||||
struct LexerTest : ::testing::Test {
|
||||
llvm::SmallVector<SourceBuffer, 16> source_storage;
|
||||
@@ -962,116 +963,35 @@ TEST_F(LexerTest, PrintingAsYaml) {
|
||||
buffer.Print(print_stream);
|
||||
print_stream.flush();
|
||||
|
||||
// Parse the output into a YAML stream. This will print errors to stderr.
|
||||
llvm::SourceMgr source_manager;
|
||||
llvm::yaml::Stream yaml_stream(print_output, source_manager);
|
||||
auto yaml_it = yaml_stream.begin();
|
||||
auto* root_node = llvm::dyn_cast<llvm::yaml::MappingNode>(yaml_it->getRoot());
|
||||
ASSERT_THAT(root_node, NotNull());
|
||||
|
||||
// Walk the top-level mapping of tokens, dig out the sub-mapping of data for
|
||||
// each taken, and then verify those entries.
|
||||
auto mapping_it = llvm::cast<llvm::yaml::MappingNode>(root_node)->begin();
|
||||
auto* token_node = llvm::dyn_cast<llvm::yaml::KeyValueNode>(&*mapping_it);
|
||||
ASSERT_THAT(token_node, NotNull());
|
||||
auto* token_key_node =
|
||||
llvm::dyn_cast<llvm::yaml::ScalarNode>(token_node->getKey());
|
||||
ASSERT_THAT(token_key_node, NotNull());
|
||||
EXPECT_THAT(token_key_node->getRawValue(), StrEq("token"));
|
||||
auto* token_value_node =
|
||||
llvm::dyn_cast<llvm::yaml::MappingNode>(token_node->getValue());
|
||||
ASSERT_THAT(token_value_node, NotNull());
|
||||
auto token_it = token_value_node->begin();
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("index", "0"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("kind", "Semi"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("line", "2"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("column", "2"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("indent", "2"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("spelling", ";"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("has_trailing_space", "true"));
|
||||
EXPECT_THAT(++token_it, Eq(token_value_node->end()));
|
||||
|
||||
++mapping_it;
|
||||
token_node = llvm::dyn_cast<llvm::yaml::KeyValueNode>(&*mapping_it);
|
||||
ASSERT_THAT(token_node, NotNull());
|
||||
token_key_node = llvm::dyn_cast<llvm::yaml::ScalarNode>(token_node->getKey());
|
||||
ASSERT_THAT(token_key_node, NotNull());
|
||||
EXPECT_THAT(token_key_node->getRawValue(), StrEq("token"));
|
||||
token_value_node =
|
||||
llvm::dyn_cast<llvm::yaml::MappingNode>(token_node->getValue());
|
||||
ASSERT_THAT(token_value_node, NotNull());
|
||||
token_it = token_value_node->begin();
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("index", "1"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("kind", "Semi"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("line", "5"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("column", "1"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("indent", "1"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("spelling", ";"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("has_trailing_space", "true"));
|
||||
EXPECT_THAT(++token_it, Eq(token_value_node->end()));
|
||||
|
||||
++mapping_it;
|
||||
token_node = llvm::dyn_cast<llvm::yaml::KeyValueNode>(&*mapping_it);
|
||||
ASSERT_THAT(token_node, NotNull());
|
||||
token_key_node = llvm::dyn_cast<llvm::yaml::ScalarNode>(token_node->getKey());
|
||||
ASSERT_THAT(token_key_node, NotNull());
|
||||
EXPECT_THAT(token_key_node->getRawValue(), StrEq("token"));
|
||||
token_value_node =
|
||||
llvm::dyn_cast<llvm::yaml::MappingNode>(token_node->getValue());
|
||||
ASSERT_THAT(token_value_node, NotNull());
|
||||
token_it = token_value_node->begin();
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("index", "2"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("kind", "Semi"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("line", "5"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("column", "3"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("indent", "1"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("spelling", ";"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("has_trailing_space", "true"));
|
||||
EXPECT_THAT(++token_it, Eq(token_value_node->end()));
|
||||
|
||||
++mapping_it;
|
||||
token_node = llvm::dyn_cast<llvm::yaml::KeyValueNode>(&*mapping_it);
|
||||
ASSERT_THAT(token_node, NotNull());
|
||||
token_key_node = llvm::dyn_cast<llvm::yaml::ScalarNode>(token_node->getKey());
|
||||
ASSERT_THAT(token_key_node, NotNull());
|
||||
EXPECT_THAT(token_key_node->getRawValue(), StrEq("token"));
|
||||
token_value_node =
|
||||
llvm::dyn_cast<llvm::yaml::MappingNode>(token_node->getValue());
|
||||
ASSERT_THAT(token_value_node, NotNull());
|
||||
token_it = token_value_node->begin();
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("index", "3"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("kind", "EndOfFile"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("line", "15"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("column", "1"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("indent", "1"));
|
||||
++token_it;
|
||||
EXPECT_THAT(&*token_it, IsKeyValueScalars("spelling", ""));
|
||||
EXPECT_THAT(++token_it, Eq(token_value_node->end()));
|
||||
|
||||
ASSERT_THAT(++mapping_it, Eq(root_node->end()));
|
||||
ASSERT_THAT(++yaml_it, Eq(yaml_stream.end()));
|
||||
EXPECT_THAT(Yaml::Value::FromText(print_output),
|
||||
ElementsAre(Yaml::MappingValue{
|
||||
{"token", Yaml::MappingValue{{"index", "0"},
|
||||
{"kind", "Semi"},
|
||||
{"line", "2"},
|
||||
{"column", "2"},
|
||||
{"indent", "2"},
|
||||
{"spelling", ";"},
|
||||
{"has_trailing_space", "true"}}},
|
||||
{"token", Yaml::MappingValue{{"index", "1"},
|
||||
{"kind", "Semi"},
|
||||
{"line", "5"},
|
||||
{"column", "1"},
|
||||
{"indent", "1"},
|
||||
{"spelling", ";"},
|
||||
{"has_trailing_space", "true"}}},
|
||||
{"token", Yaml::MappingValue{{"index", "2"},
|
||||
{"kind", "Semi"},
|
||||
{"line", "5"},
|
||||
{"column", "3"},
|
||||
{"indent", "1"},
|
||||
{"spelling", ";"},
|
||||
{"has_trailing_space", "true"}}},
|
||||
{"token", Yaml::MappingValue{{"index", "3"},
|
||||
{"kind", "EndOfFile"},
|
||||
{"line", "15"},
|
||||
{"column", "1"},
|
||||
{"indent", "1"},
|
||||
{"spelling", ""}}}}));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -151,45 +151,6 @@ MATCHER_P(HasTokens, raw_all_expected, "") {
|
||||
return matches;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE: Expands from GoogleTest.
|
||||
MATCHER_P2(IsKeyValueScalars, key, value, "") {
|
||||
auto* kv_node = llvm::dyn_cast<llvm::yaml::KeyValueNode>(arg);
|
||||
if (!kv_node) {
|
||||
*result_listener << "this is a `" << arg->getType()
|
||||
<< "` node, not a `KeyValueNode`.";
|
||||
return false;
|
||||
}
|
||||
|
||||
llvm::SmallString<128> storage;
|
||||
|
||||
auto* key_node = llvm::dyn_cast<llvm::yaml::ScalarNode>(kv_node->getKey());
|
||||
if (!key_node) {
|
||||
*result_listener << "the key is a `" << arg->getType()
|
||||
<< "` node, not a `ScalarNode`.";
|
||||
return false;
|
||||
}
|
||||
if (key != key_node->getValue(storage)) {
|
||||
*result_listener << "the key is `" << key_node->getValue(storage).str()
|
||||
<< "`, expected `" << key << "`.";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* value_node =
|
||||
llvm::dyn_cast<llvm::yaml::ScalarNode>(kv_node->getValue());
|
||||
if (!value_node) {
|
||||
*result_listener << "the value is a `" << arg->getType()
|
||||
<< "` node, not a `ScalarNode`.";
|
||||
return false;
|
||||
}
|
||||
if (value != value_node->getValue(storage)) {
|
||||
*result_listener << "the value is `" << value_node->getValue(storage).str()
|
||||
<< "`, expected `" << value << "`.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Testing
|
||||
} // namespace Carbon
|
||||
|
||||
|
||||
@@ -64,9 +64,9 @@ cc_test(
|
||||
":parse_node_kind",
|
||||
":parse_test_helpers",
|
||||
":parse_tree",
|
||||
"//toolchain/common:yaml_test_helpers",
|
||||
"//toolchain/diagnostics:diagnostic_emitter",
|
||||
"//toolchain/lexer:tokenized_buffer",
|
||||
"//toolchain/lexer:tokenized_buffer_test_helpers",
|
||||
"@llvm-project//llvm:Support",
|
||||
"@llvm-project//llvm:gmock",
|
||||
"@llvm-project//llvm:gtest",
|
||||
|
||||
@@ -10,10 +10,9 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "llvm/ADT/Sequence.h"
|
||||
#include "llvm/Support/SourceMgr.h"
|
||||
#include "llvm/Support/YAMLParser.h"
|
||||
#include "toolchain/common/yaml_test_helpers.h"
|
||||
#include "toolchain/diagnostics/diagnostic_emitter.h"
|
||||
#include "toolchain/lexer/tokenized_buffer.h"
|
||||
#include "toolchain/lexer/tokenized_buffer_test_helpers.h"
|
||||
#include "toolchain/parser/parse_node_kind.h"
|
||||
#include "toolchain/parser/parse_test_helpers.h"
|
||||
|
||||
@@ -21,13 +20,14 @@ namespace Carbon {
|
||||
namespace {
|
||||
|
||||
using Carbon::Testing::ExpectedNode;
|
||||
using Carbon::Testing::IsKeyValueScalars;
|
||||
using Carbon::Testing::MatchParseTreeNodes;
|
||||
using namespace Carbon::Testing::NodeMatchers;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::Eq;
|
||||
using ::testing::Ne;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::StrEq;
|
||||
namespace Yaml = Carbon::Testing::Yaml;
|
||||
|
||||
struct ParseTreeTest : ::testing::Test {
|
||||
std::forward_list<SourceBuffer> source_storage;
|
||||
@@ -884,129 +884,34 @@ TEST_F(ParseTreeTest, PrintingAsYAML) {
|
||||
tree.Print(print_stream);
|
||||
print_stream.flush();
|
||||
|
||||
// Parse the output into a YAML stream. This will print errors to stderr.
|
||||
llvm::SourceMgr source_manager;
|
||||
llvm::yaml::Stream yaml_stream(print_output, source_manager);
|
||||
auto di = yaml_stream.begin();
|
||||
auto* root_node = llvm::dyn_cast<llvm::yaml::SequenceNode>(di->getRoot());
|
||||
ASSERT_THAT(root_node, NotNull());
|
||||
|
||||
// The root node is just an array of top-level parse nodes.
|
||||
auto ni = root_node->begin();
|
||||
auto ne = root_node->end();
|
||||
auto* node = llvm::dyn_cast<llvm::yaml::MappingNode>(&*ni);
|
||||
ASSERT_THAT(node, NotNull());
|
||||
auto nkvi = node->begin();
|
||||
auto nkve = node->end();
|
||||
EXPECT_THAT(&*nkvi, IsKeyValueScalars("node_index", "4"));
|
||||
++nkvi;
|
||||
EXPECT_THAT(&*nkvi, IsKeyValueScalars("kind", "FunctionDeclaration"));
|
||||
++nkvi;
|
||||
EXPECT_THAT(&*nkvi, IsKeyValueScalars("text", "fn"));
|
||||
++nkvi;
|
||||
EXPECT_THAT(&*nkvi, IsKeyValueScalars("subtree_size", "5"));
|
||||
++nkvi;
|
||||
auto* children_node = llvm::dyn_cast<llvm::yaml::KeyValueNode>(&*nkvi);
|
||||
ASSERT_THAT(children_node, NotNull());
|
||||
auto* children_key_node =
|
||||
llvm::dyn_cast<llvm::yaml::ScalarNode>(children_node->getKey());
|
||||
ASSERT_THAT(children_key_node, NotNull());
|
||||
EXPECT_THAT(children_key_node->getRawValue(), StrEq("children"));
|
||||
auto* children_value_node =
|
||||
llvm::dyn_cast<llvm::yaml::SequenceNode>(children_node->getValue());
|
||||
ASSERT_THAT(children_value_node, NotNull());
|
||||
|
||||
auto ci = children_value_node->begin();
|
||||
auto ce = children_value_node->end();
|
||||
ASSERT_THAT(ci, Ne(ce));
|
||||
node = llvm::dyn_cast<llvm::yaml::MappingNode>(&*ci);
|
||||
ASSERT_THAT(node, NotNull());
|
||||
auto ckvi = node->begin();
|
||||
EXPECT_THAT(&*ckvi, IsKeyValueScalars("node_index", "0"));
|
||||
++ckvi;
|
||||
EXPECT_THAT(&*ckvi, IsKeyValueScalars("kind", "DeclaredName"));
|
||||
++ckvi;
|
||||
EXPECT_THAT(&*ckvi, IsKeyValueScalars("text", "F"));
|
||||
++ckvi;
|
||||
EXPECT_THAT(ckvi, Eq(node->end()));
|
||||
|
||||
++ci;
|
||||
ASSERT_THAT(ci, Ne(ce));
|
||||
node = llvm::dyn_cast<llvm::yaml::MappingNode>(&*ci);
|
||||
ASSERT_THAT(node, NotNull());
|
||||
ckvi = node->begin();
|
||||
auto ckve = node->end();
|
||||
EXPECT_THAT(&*ckvi, IsKeyValueScalars("node_index", "2"));
|
||||
++ckvi;
|
||||
EXPECT_THAT(&*ckvi, IsKeyValueScalars("kind", "ParameterList"));
|
||||
++ckvi;
|
||||
EXPECT_THAT(&*ckvi, IsKeyValueScalars("text", "("));
|
||||
++ckvi;
|
||||
EXPECT_THAT(&*ckvi, IsKeyValueScalars("subtree_size", "2"));
|
||||
++ckvi;
|
||||
children_node = llvm::dyn_cast<llvm::yaml::KeyValueNode>(&*ckvi);
|
||||
ASSERT_THAT(children_node, NotNull());
|
||||
children_key_node =
|
||||
llvm::dyn_cast<llvm::yaml::ScalarNode>(children_node->getKey());
|
||||
ASSERT_THAT(children_key_node, NotNull());
|
||||
EXPECT_THAT(children_key_node->getRawValue(), StrEq("children"));
|
||||
children_value_node =
|
||||
llvm::dyn_cast<llvm::yaml::SequenceNode>(children_node->getValue());
|
||||
ASSERT_THAT(children_value_node, NotNull());
|
||||
|
||||
auto c2_i = children_value_node->begin();
|
||||
auto c2_e = children_value_node->end();
|
||||
ASSERT_THAT(c2_i, Ne(c2_e));
|
||||
node = llvm::dyn_cast<llvm::yaml::MappingNode>(&*c2_i);
|
||||
ASSERT_THAT(node, NotNull());
|
||||
auto c2_kvi = node->begin();
|
||||
EXPECT_THAT(&*c2_kvi, IsKeyValueScalars("node_index", "1"));
|
||||
++c2_kvi;
|
||||
EXPECT_THAT(&*c2_kvi, IsKeyValueScalars("kind", "ParameterListEnd"));
|
||||
++c2_kvi;
|
||||
EXPECT_THAT(&*c2_kvi, IsKeyValueScalars("text", ")"));
|
||||
++c2_kvi;
|
||||
EXPECT_THAT(c2_kvi, Eq(node->end()));
|
||||
++c2_i;
|
||||
EXPECT_THAT(c2_i, Eq(c2_e));
|
||||
++ckvi;
|
||||
EXPECT_THAT(ckvi, Eq(ckve));
|
||||
|
||||
++ci;
|
||||
ASSERT_THAT(ci, Ne(ce));
|
||||
node = llvm::dyn_cast<llvm::yaml::MappingNode>(&*ci);
|
||||
ASSERT_THAT(node, NotNull());
|
||||
ckvi = node->begin();
|
||||
EXPECT_THAT(&*ckvi, IsKeyValueScalars("node_index", "3"));
|
||||
++ckvi;
|
||||
EXPECT_THAT(&*ckvi, IsKeyValueScalars("kind", "DeclarationEnd"));
|
||||
++ckvi;
|
||||
EXPECT_THAT(&*ckvi, IsKeyValueScalars("text", ";"));
|
||||
++ckvi;
|
||||
EXPECT_THAT(ckvi, Eq(node->end()));
|
||||
++ci;
|
||||
EXPECT_THAT(ci, Eq(ce));
|
||||
|
||||
++nkvi;
|
||||
EXPECT_THAT(nkvi, Eq(nkve));
|
||||
|
||||
++ni;
|
||||
ASSERT_THAT(ni, Ne(ne));
|
||||
node = llvm::dyn_cast<llvm::yaml::MappingNode>(&*ni);
|
||||
ASSERT_THAT(node, NotNull());
|
||||
nkvi = node->begin();
|
||||
EXPECT_THAT(&*nkvi, IsKeyValueScalars("node_index", "5"));
|
||||
++nkvi;
|
||||
EXPECT_THAT(&*nkvi, IsKeyValueScalars("kind", "FileEnd"));
|
||||
++nkvi;
|
||||
EXPECT_THAT(&*nkvi, IsKeyValueScalars("text", ""));
|
||||
++nkvi;
|
||||
EXPECT_THAT(nkvi, Eq(node->end()));
|
||||
|
||||
++ni;
|
||||
EXPECT_THAT(ni, Eq(ne));
|
||||
++di;
|
||||
EXPECT_THAT(di, Eq(yaml_stream.end()));
|
||||
EXPECT_THAT(
|
||||
Yaml::Value::FromText(print_output),
|
||||
ElementsAre(Yaml::SequenceValue{
|
||||
Yaml::MappingValue{
|
||||
{"node_index", "4"},
|
||||
{"kind", "FunctionDeclaration"},
|
||||
{"text", "fn"},
|
||||
{"subtree_size", "5"},
|
||||
{"children",
|
||||
Yaml::SequenceValue{
|
||||
Yaml::MappingValue{{"node_index", "0"},
|
||||
{"kind", "DeclaredName"},
|
||||
{"text", "F"}},
|
||||
Yaml::MappingValue{{"node_index", "2"},
|
||||
{"kind", "ParameterList"},
|
||||
{"text", "("},
|
||||
{"subtree_size", "2"},
|
||||
{"children", //
|
||||
Yaml::SequenceValue{Yaml::MappingValue{
|
||||
{"node_index", "1"},
|
||||
{"kind", "ParameterListEnd"},
|
||||
{"text", ")"}}}}},
|
||||
Yaml::MappingValue{{"node_index", "3"},
|
||||
{"kind", "DeclarationEnd"},
|
||||
{"text", ";"}}}}},
|
||||
Yaml::MappingValue{{"node_index", "5"}, //
|
||||
{"kind", "FileEnd"},
|
||||
{"text", ""}}}));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Reference in New Issue
Block a user