This commit is contained in:
2026-09-13 13:30:21 +08:00
commit a6bbd520cf
2744 changed files with 1598795 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
load("@rules_cc//cc:defs.bzl", "cc_test")
cc_test(
name = "test",
srcs = glob([
"*.cpp",
"*.h",
]),
deps = [
"//:yaml-cpp",
"//:yaml-cpp_internal",
"//test:mock_event_handler",
"//test:specexamples",
"@googletest//:gtest_main",
],
)
@@ -0,0 +1,84 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <yaml-cpp/yaml.h>
struct ComparableMark {
int pos;
int line, column;
ComparableMark(int pos, int line, int column)
: pos(pos), line(line), column(column) {}
};
bool operator==(const YAML::Mark& a, const ComparableMark& b) {
return a.pos == b.pos && a.line == b.line && a.column == b.column;
}
template <typename Mark>
void PrintMark(const Mark& mark, std::ostream* os) {
*os << mark.line << ':' << mark.column << " (pos " << mark.pos << ')';
}
void PrintTo(const ComparableMark& mark, std::ostream* os) {
PrintMark(mark, os);
}
namespace YAML {
void PrintTo(const Mark& mark, std::ostream* os) { PrintMark(mark, os); }
} // namespace YAML
TEST(CloneNodeTest, PreserveMark) {
std::string yaml_str = R"(
scalar: value
sequence: [1, 2, 3]
"null": null
[1, 2, 3]: value # check non-scalar keys
)";
auto checkMarks = [](const YAML::Node& root_node) {
EXPECT_EQ(root_node.Mark(), ComparableMark(1, 1, 0));
const YAML::Node& scalar = root_node["scalar"];
EXPECT_EQ(scalar.Mark(), ComparableMark(9, 1, 8));
const YAML::Node& sequence = root_node["sequence"];
EXPECT_EQ(sequence.Mark(), ComparableMark(25, 2, 10));
EXPECT_EQ(sequence[0].Mark(), ComparableMark(26, 2, 11));
EXPECT_EQ(sequence[1].Mark(), ComparableMark(29, 2, 14));
EXPECT_EQ(sequence[2].Mark(), ComparableMark(32, 2, 17));
const YAML::Node& null = root_node["null"];
EXPECT_EQ(null.Mark(), ComparableMark(43, 3, 8));
YAML::Node sequence_key;
std::vector<YAML::Mark> key_marks;
for (auto it = root_node.begin(); it != root_node.end(); ++it) {
// Not assuming any key order
key_marks.emplace_back(it->first.Mark());
if (it->first.IsSequence()) {
sequence_key.reset(it->first);
}
}
EXPECT_THAT(key_marks,
testing::UnorderedElementsAre(
ComparableMark(1, 1, 0), ComparableMark(15, 2, 0),
ComparableMark(35, 3, 0), ComparableMark(48, 4, 0)));
ASSERT_TRUE(sequence_key);
EXPECT_EQ(sequence_key[0].Mark(), ComparableMark(49, 4, 1));
EXPECT_EQ(sequence_key[1].Mark(), ComparableMark(52, 4, 4));
EXPECT_EQ(sequence_key[2].Mark(), ComparableMark(55, 4, 7));
};
YAML::Node root_node = YAML::Load(yaml_str);
{
SCOPED_TRACE("original node");
checkMarks(root_node);
}
YAML::Node cloned_node = YAML::Clone(root_node);
{
SCOPED_TRACE("cloned node");
checkMarks(cloned_node);
}
}
@@ -0,0 +1,92 @@
#include "yaml-cpp/emitterstyle.h"
#include "yaml-cpp/eventhandler.h"
#include "yaml-cpp/yaml.h" // IWYU pragma: keep
#include "gtest/gtest.h"
// Base class
class A {
public:
A() = default;
A(int a) : a{a} {}
// virtual load/emit methods
virtual void load(const YAML::Node &node) { a = node["a"].as<int>(); }
virtual YAML::Node emit() const {
YAML::Node node;
node["a"] = a;
return node;
}
int a{};
};
// Derived class
class B : public A {
public:
B() = default;
B(int a, int b) : A{a}, b{b} {}
// override virtual load/emit methods
virtual void load(const YAML::Node &node) override {
A::load(node);
b = node["b"].as<int>();
}
virtual YAML::Node emit() const override {
YAML::Node node = A::emit();
node["b"] = b;
return node;
}
int b{};
};
// Implementation of convert::{encode,decode} for all classes derived from or
// being A
namespace YAML {
template <typename T>
struct convert<T, typename std::enable_if<std::is_base_of<A, T>::value>::type> {
static Node encode(const T &rhs) {
Node node = rhs.emit();
return node;
}
static bool decode(const Node &node, T &rhs) {
rhs.load(node);
return true;
}
};
namespace {
TEST(ConvertPartialSpecializationTest, EncodeBaseClass) {
Node n(Load("{a: 1}"));
A a = n.as<A>();
EXPECT_EQ(a.a, 1);
}
TEST(ConvertPartialSpecializationTest, EncodeDerivedClass) {
Node n(Load("{a: 1, b: 2}"));
B b = n.as<B>();
EXPECT_EQ(b.a, 1);
EXPECT_EQ(b.b, 2);
}
TEST(ConvertPartialSpecializationTest, DecodeBaseClass) {
A a(1);
Node n;
n = a;
EXPECT_EQ(a.a, n["a"].as<int>());
}
TEST(ConvertPartialSpecializationTest, DecodeDerivedClass) {
B b(1, 2);
Node n;
n = b;
EXPECT_EQ(b.a, n["a"].as<int>());
EXPECT_EQ(b.b, n["b"].as<int>());
}
} // namespace
} // namespace YAML
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,182 @@
#include <sstream>
#include "handler_test.h"
#include "yaml-cpp/yaml.h" // IWYU pragma: keep
#include "gtest/gtest.h"
using ::testing::_;
using ::testing::InSequence;
namespace YAML {
namespace {
typedef void (*EncodingFn)(std::ostream&, int);
inline char Byte(int ch) {
return static_cast<char>(
static_cast<unsigned char>(static_cast<unsigned int>(ch)));
}
void EncodeToUtf8(std::ostream& stream, int ch) {
if (ch <= 0x7F) {
stream << Byte(ch);
} else if (ch <= 0x7FF) {
stream << Byte(0xC0 | (ch >> 6));
stream << Byte(0x80 | (ch & 0x3F));
} else if (ch <= 0xFFFF) {
stream << Byte(0xE0 | (ch >> 12));
stream << Byte(0x80 | ((ch >> 6) & 0x3F));
stream << Byte(0x80 | (ch & 0x3F));
} else if (ch <= 0x1FFFFF) {
stream << Byte(0xF0 | (ch >> 18));
stream << Byte(0x80 | ((ch >> 12) & 0x3F));
stream << Byte(0x80 | ((ch >> 6) & 0x3F));
stream << Byte(0x80 | (ch & 0x3F));
}
}
bool SplitUtf16HighChar(std::ostream& stream, EncodingFn encoding, int ch) {
int biasedValue = ch - 0x10000;
if (biasedValue < 0) {
return false;
}
int high = 0xD800 | (biasedValue >> 10);
int low = 0xDC00 | (biasedValue & 0x3FF);
encoding(stream, high);
encoding(stream, low);
return true;
}
void EncodeToUtf16LE(std::ostream& stream, int ch) {
if (!SplitUtf16HighChar(stream, &EncodeToUtf16LE, ch)) {
stream << Byte(ch & 0xFF) << Byte(ch >> 8);
}
}
void EncodeToUtf16BE(std::ostream& stream, int ch) {
if (!SplitUtf16HighChar(stream, &EncodeToUtf16BE, ch)) {
stream << Byte(ch >> 8) << Byte(ch & 0xFF);
}
}
void EncodeToUtf32LE(std::ostream& stream, int ch) {
stream << Byte(ch & 0xFF) << Byte((ch >> 8) & 0xFF) << Byte((ch >> 16) & 0xFF)
<< Byte((ch >> 24) & 0xFF);
}
void EncodeToUtf32BE(std::ostream& stream, int ch) {
stream << Byte((ch >> 24) & 0xFF) << Byte((ch >> 16) & 0xFF)
<< Byte((ch >> 8) & 0xFF) << Byte(ch & 0xFF);
}
class EncodingTest : public HandlerTest {
protected:
void SetUpEncoding(EncodingFn encoding, bool declareEncoding) {
if (declareEncoding) {
encoding(m_yaml, 0xFEFF);
}
AddEntry(encoding, 0x0021, 0x007E); // Basic Latin
AddEntry(encoding, 0x00A1, 0x00FF); // Latin-1 Supplement
AddEntry(encoding, 0x0660, 0x06FF); // Arabic (largest contiguous block)
// CJK unified ideographs (multiple lines)
AddEntry(encoding, 0x4E00, 0x4EFF);
AddEntry(encoding, 0x4F00, 0x4FFF);
AddEntry(encoding, 0x5000, 0x51FF); // 512 character line
AddEntry(encoding, 0x5200, 0x54FF); // 768 character line
AddEntry(encoding, 0x5500, 0x58FF); // 1024 character line
AddEntry(encoding, 0x103A0, 0x103C3); // Old Persian
m_yaml.seekg(0, std::ios::beg);
}
void Run() {
InSequence sequence;
EXPECT_CALL(handler, OnDocumentStart(_));
EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block));
for (std::size_t i = 0; i < m_entries.size(); i++) {
EXPECT_CALL(handler, OnScalar(_, "!", 0, m_entries[i]));
}
EXPECT_CALL(handler, OnSequenceEnd());
EXPECT_CALL(handler, OnDocumentEnd());
Parse(m_yaml.str());
}
private:
std::stringstream m_yaml;
std::vector<std::string> m_entries;
void AddEntry(EncodingFn encoding, int startCh, int endCh) {
encoding(m_yaml, '-');
encoding(m_yaml, ' ');
encoding(m_yaml, '|');
encoding(m_yaml, '\n');
encoding(m_yaml, ' ');
encoding(m_yaml, ' ');
std::stringstream entry;
for (int ch = startCh; ch <= endCh; ++ch) {
encoding(m_yaml, ch);
EncodeToUtf8(entry, ch);
}
encoding(m_yaml, '\n');
EncodeToUtf8(entry, '\n');
m_entries.push_back(entry.str());
}
};
TEST_F(EncodingTest, UTF8_noBOM) {
SetUpEncoding(&EncodeToUtf8, false);
Run();
}
TEST_F(EncodingTest, UTF8_BOM) {
SetUpEncoding(&EncodeToUtf8, true);
Run();
}
TEST_F(EncodingTest, UTF16LE_noBOM) {
SetUpEncoding(&EncodeToUtf16LE, false);
Run();
}
TEST_F(EncodingTest, UTF16LE_BOM) {
SetUpEncoding(&EncodeToUtf16LE, true);
Run();
}
TEST_F(EncodingTest, UTF16BE_noBOM) {
SetUpEncoding(&EncodeToUtf16BE, false);
Run();
}
TEST_F(EncodingTest, UTF16BE_BOM) {
SetUpEncoding(&EncodeToUtf16BE, true);
Run();
}
TEST_F(EncodingTest, UTF32LE_noBOM) {
SetUpEncoding(&EncodeToUtf32LE, false);
Run();
}
TEST_F(EncodingTest, UTF32LE_BOM) {
SetUpEncoding(&EncodeToUtf32LE, true);
Run();
}
TEST_F(EncodingTest, UTF32BE_noBOM) {
SetUpEncoding(&EncodeToUtf32BE, false);
Run();
}
TEST_F(EncodingTest, UTF32BE_BOM) {
SetUpEncoding(&EncodeToUtf32BE, true);
Run();
}
}
}
@@ -0,0 +1,61 @@
#include "yaml-cpp/yaml.h" // IWYU pragma: keep
#include "gtest/gtest.h"
#define EXPECT_THROW_EXCEPTION(exception_type, statement, message) \
ASSERT_THROW(statement, exception_type); \
try { \
statement; \
} catch (const exception_type& e) { \
EXPECT_EQ(e.msg, message); \
}
namespace YAML {
namespace {
TEST(ErrorMessageTest, BadSubscriptErrorMessage) {
const char *example_yaml = "first:\n"
" second: 1\n"
" third: 2\n";
Node doc = Load(example_yaml);
// Test that printable key is part of error message
EXPECT_THROW_EXCEPTION(YAML::BadSubscript, doc["first"]["second"]["fourth"],
"operator[] call on a scalar (key: \"fourth\")");
EXPECT_THROW_EXCEPTION(YAML::BadSubscript, doc["first"]["second"][37],
"operator[] call on a scalar (key: \"37\")");
// Non-printable key is not included in error message
EXPECT_THROW_EXCEPTION(YAML::BadSubscript,
doc["first"]["second"][std::vector<int>()],
"operator[] call on a scalar");
EXPECT_THROW_EXCEPTION(YAML::BadSubscript, doc["first"]["second"][Node()],
"operator[] call on a scalar");
}
TEST(ErrorMessageTest, Ex9_1_InvalidNodeErrorMessage) {
const char *example_yaml = "first:\n"
" second: 1\n"
" third: 2\n";
const Node doc = Load(example_yaml);
// Test that printable key is part of error message
EXPECT_THROW_EXCEPTION(YAML::InvalidNode, doc["first"]["fourth"].as<int>(),
"invalid node; first invalid key: \"fourth\"");
EXPECT_THROW_EXCEPTION(YAML::InvalidNode, doc["first"][37].as<int>(),
"invalid node; first invalid key: \"37\"");
// Non-printable key is not included in error message
EXPECT_THROW_EXCEPTION(YAML::InvalidNode,
doc["first"][std::vector<int>()].as<int>(),
"invalid node; this may result from using a map "
"iterator as a sequence iterator, or vice-versa");
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,131 @@
#include "handler_test.h"
#include "specexamples.h" // IWYU pragma: keep
#include "yaml-cpp/yaml.h" // IWYU pragma: keep
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using ::testing::_;
#define EXPECT_THROW_PARSER_EXCEPTION(statement, message) \
ASSERT_THROW(statement, ParserException); \
try { \
statement; \
} catch (const ParserException& e) { \
EXPECT_EQ(e.msg, message); \
}
namespace YAML {
namespace {
TEST_F(HandlerTest, NoEndOfMapFlow) {
EXPECT_THROW_PARSER_EXCEPTION(IgnoreParse("---{header: {id: 1"),
ErrorMsg::END_OF_MAP_FLOW);
}
TEST_F(HandlerTest, PlainScalarStartingWithQuestionMark) {
EXPECT_CALL(handler, OnDocumentStart(_));
EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block));
EXPECT_CALL(handler, OnScalar(_, "?", 0, "foo"));
EXPECT_CALL(handler, OnScalar(_, "?", 0, "?bar"));
EXPECT_CALL(handler, OnMapEnd());
EXPECT_CALL(handler, OnDocumentEnd());
Parse("foo: ?bar");
}
TEST_F(HandlerTest, NullStringScalar) {
EXPECT_CALL(handler, OnDocumentStart(_));
EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block));
EXPECT_CALL(handler, OnScalar(_, "?", 0, "foo"));
EXPECT_CALL(handler, OnNull(_, 0));
EXPECT_CALL(handler, OnMapEnd());
EXPECT_CALL(handler, OnDocumentEnd());
Parse("foo: null");
}
TEST_F(HandlerTest, CommentOnNewlineOfMapValueWithNoSpaces) {
EXPECT_CALL(handler, OnDocumentStart(_));
EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block));
EXPECT_CALL(handler, OnScalar(_, "?", 0, "key"));
EXPECT_CALL(handler, OnScalar(_, "?", 0, "value"));
EXPECT_CALL(handler, OnMapEnd());
EXPECT_CALL(handler, OnDocumentEnd());
Parse("key: value\n# comment");
}
TEST_F(HandlerTest, CommentOnNewlineOfMapValueWithOneSpace) {
EXPECT_CALL(handler, OnDocumentStart(_));
EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block));
EXPECT_CALL(handler, OnScalar(_, "?", 0, "key"));
EXPECT_CALL(handler, OnScalar(_, "?", 0, "value"));
EXPECT_CALL(handler, OnMapEnd());
EXPECT_CALL(handler, OnDocumentEnd());
Parse("key: value\n # comment");
}
TEST_F(HandlerTest, CommentOnNewlineOfMapValueWithManySpace) {
EXPECT_CALL(handler, OnDocumentStart(_));
EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block));
EXPECT_CALL(handler, OnScalar(_, "?", 0, "key"));
EXPECT_CALL(handler, OnScalar(_, "?", 0, "value"));
EXPECT_CALL(handler, OnMapEnd());
EXPECT_CALL(handler, OnDocumentEnd());
Parse("key: value\n # comment");
}
// example from issue #1475: a stale simple key (from a plain scalar spanning
// a line break) must not pop an unrelated indent when parsing a block scalar
TEST_F(HandlerTest, LiteralScalarWithMultiLineUnverifiedPotentialSimpleKey) {
EXPECT_THROW_PARSER_EXCEPTION(IgnoreParse("!\n: |\nb\n>\n|\n !\n>"),
ErrorMsg::END_OF_MAP);
}
// examples from issue #1163
TEST_F(HandlerTest, LiteralScalarWithTagAndLargeIndentation) {
EXPECT_CALL(handler, OnDocumentStart(_));
EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block));
EXPECT_CALL(handler, OnScalar(_, "?", 0, "key"));
EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block));
EXPECT_CALL(handler, OnScalar(_, "tag:yaml.org,2002:str", 0, "multiple\nwords"));
EXPECT_CALL(handler, OnSequenceEnd());
EXPECT_CALL(handler, OnMapEnd());
EXPECT_CALL(handler, OnDocumentEnd());
Parse("key:\n- !!str |\n multiple\n words");
}
TEST_F(HandlerTest, LiteralScalarWithTagAndSmallIndentation_NotBlockSequence) {
EXPECT_CALL(handler, OnDocumentStart(_));
EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block));
EXPECT_CALL(handler, OnScalar(_, "?", 0, "key"));
EXPECT_CALL(handler, OnScalar(_, "!t", 0, "multiple\nwords"));
EXPECT_CALL(handler, OnMapEnd());
EXPECT_CALL(handler, OnDocumentEnd());
Parse("key: !t |\n multiple\n words");
}
TEST_F(HandlerTest, LiteralScalarWithTagAndSmallIndentation_ApplicationTag) {
EXPECT_CALL(handler, OnDocumentStart(_));
EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block));
EXPECT_CALL(handler, OnScalar(_, "?", 0, "key"));
EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block));
EXPECT_CALL(handler, OnScalar(_, "!t", 0, "multiple\nwords"));
EXPECT_CALL(handler, OnSequenceEnd());
EXPECT_CALL(handler, OnMapEnd());
EXPECT_CALL(handler, OnDocumentEnd());
Parse("key:\n- !t |\n multiple\n words");
}
TEST_F(HandlerTest, LiteralScalarWithTagAndSmallIndentation_StandardTag) {
EXPECT_CALL(handler, OnDocumentStart(_));
EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block));
EXPECT_CALL(handler, OnScalar(_, "?", 0, "key"));
EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block));
EXPECT_CALL(handler, OnScalar(_, "tag:yaml.org,2002:str", 0, "multiple\nwords"));
EXPECT_CALL(handler, OnSequenceEnd());
EXPECT_CALL(handler, OnMapEnd());
EXPECT_CALL(handler, OnDocumentEnd());
Parse("key:\n- !!str |\n multiple\n words");
}
} // namespace
} // namespace YAML
@@ -0,0 +1,32 @@
#include "mock_event_handler.h"
#include "yaml-cpp/yaml.h" // IWYU pragma: keep
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using ::testing::InSequence;
using ::testing::NiceMock;
using ::testing::StrictMock;
namespace YAML {
class HandlerTest : public ::testing::Test {
protected:
void Parse(const std::string& example) {
std::stringstream stream(example);
Parser parser(stream);
while (parser.HandleNextDocument(handler)) {
}
}
void IgnoreParse(const std::string& example) {
std::stringstream stream(example);
Parser parser(stream);
while (parser.HandleNextDocument(nice_handler)) {
}
}
InSequence sequence;
StrictMock<MockEventHandler> handler;
NiceMock<MockEventHandler> nice_handler;
};
}
@@ -0,0 +1,544 @@
#include "yaml-cpp/yaml.h" // IWYU pragma: keep
#include <sstream>
#include <string>
#include "gtest/gtest.h"
namespace YAML {
namespace {
class FailingStreamBuf : public std::stringbuf {
public:
explicit FailingStreamBuf(const std::string& input)
: std::stringbuf(input), first_read_(true) {}
protected:
std::streamsize xsgetn(char* output, std::streamsize count) override {
if (first_read_) {
first_read_ = false;
return std::stringbuf::xsgetn(output, count > 32 ? 32 : count);
}
throw std::ios_base::failure("simulated read failure");
}
private:
bool first_read_;
};
// Fails on the very first read, which the buffer above cannot do: it serves 32
// bytes before throwing, so Stream's constructor completes and the failure is
// raised later, during scanning. Stream's constructor ends in ReadAheadTo(0),
// so a buffer that throws immediately makes the failure escape the constructor
// itself - a different path, and the one that used to leak the prefetch buffer
// because ~Stream cannot run for an object that was never constructed.
class ImmediatelyFailingStreamBuf : public std::stringbuf {
public:
ImmediatelyFailingStreamBuf() : std::stringbuf(std::string()) {}
protected:
std::streamsize xsgetn(char*, std::streamsize) override {
throw std::ios_base::failure("simulated read failure");
}
};
TEST(LoadNodeTest, Reassign) {
Node node = Load("foo");
node = Node();
EXPECT_TRUE(node.IsNull());
}
TEST(LoadNodeTest, RejectsFailedInputStream) {
std::istringstream stream("key: value");
stream.setstate(std::ios_base::failbit);
EXPECT_THROW(Load(stream), BadStream);
}
TEST(LoadNodeTest, RejectsBadInputStream) {
std::istringstream stream("key: value");
stream.setstate(std::ios_base::badbit);
EXPECT_THROW(Load(stream), BadStream);
}
TEST(LoadNodeTest, LoadAllRejectsFailedInputStream) {
std::istringstream stream("---\nfirst\n---\nsecond\n");
stream.setstate(std::ios_base::failbit);
EXPECT_THROW(LoadAll(stream), BadStream);
}
TEST(LoadNodeTest, RejectsInputStreamFailureWhileReading) {
FailingStreamBuf buffer("value: " + std::string(128, 'a'));
std::istream stream(&buffer);
EXPECT_THROW(Load(stream), BadStream);
}
TEST(LoadNodeTest, RejectsInputStreamFailureOnFirstRead) {
ImmediatelyFailingStreamBuf buffer;
std::istream stream(&buffer);
EXPECT_THROW(Load(stream), BadStream);
}
TEST(LoadNodeTest, EmptyInputStreamRemainsNull) {
std::istringstream stream;
EXPECT_TRUE(Load(stream).IsNull());
}
TEST(LoadNodeTest, EofInputStreamRemainsNull) {
std::istringstream stream;
stream.setstate(std::ios_base::eofbit);
EXPECT_TRUE(Load(stream).IsNull());
}
TEST(LoadNodeTest, FallbackValues) {
Node node = Load("foo: bar\nx: 2");
EXPECT_EQ("bar", node["foo"].as<std::string>());
EXPECT_EQ("bar", node["foo"].as<std::string>("hello"));
EXPECT_EQ("hello", node["baz"].as<std::string>("hello"));
EXPECT_EQ(2, node["x"].as<int>());
EXPECT_EQ(2, node["x"].as<int>(5));
EXPECT_EQ(5, node["y"].as<int>(5));
}
TEST(LoadNodeTest, NumericConversion) {
EXPECT_EQ(1.5f, Load("1.5").as<float>());
EXPECT_EQ(1.5, Load("1.5").as<double>());
EXPECT_THROW(Load("1.5").as<int>(), TypedBadConversion<int>);
EXPECT_EQ(1, Load("1").as<int>());
EXPECT_EQ(1.0f, Load("1").as<float>());
EXPECT_NE(Load(".nan").as<float>(), Load(".nan").as<float>());
EXPECT_EQ(std::numeric_limits<float>::infinity(), Load(".inf").as<float>());
EXPECT_EQ(-std::numeric_limits<float>::infinity(), Load("-.inf").as<float>());
EXPECT_EQ(21, Load("0x15").as<int>());
EXPECT_EQ(13, Load("015").as<int>());
EXPECT_EQ(-128, +Load("-128").as<int8_t>());
EXPECT_EQ(127, +Load("127").as<int8_t>());
EXPECT_THROW(Load("128").as<int8_t>(), TypedBadConversion<signed char>);
EXPECT_EQ(255, +Load("255").as<uint8_t>());
EXPECT_THROW(Load("256").as<uint8_t>(), TypedBadConversion<unsigned char>);
// test as<char>/as<uint8_t> with ‘a’,"ab",'1',"127"
EXPECT_EQ('a', Load("a").as<char>());
EXPECT_THROW(Load("ab").as<char>(), TypedBadConversion<char>);
EXPECT_EQ('1', Load("1").as<char>());
EXPECT_THROW(Load("127").as<char>(), TypedBadConversion<char>);
EXPECT_THROW(Load("a").as<uint8_t>(), TypedBadConversion<unsigned char>);
EXPECT_THROW(Load("ab").as<uint8_t>(), TypedBadConversion<unsigned char>);
EXPECT_EQ(1, +Load("1").as<uint8_t>());
// Throw exception: convert a negative number to an unsigned number.
EXPECT_THROW(Load("-128").as<unsigned>(), TypedBadConversion<unsigned int>);
EXPECT_THROW(Load("-128").as<unsigned short>(), TypedBadConversion<unsigned short>);
EXPECT_THROW(Load("-128").as<unsigned long>(), TypedBadConversion<unsigned long>);
EXPECT_THROW(Load("-128").as<unsigned long long>(), TypedBadConversion<unsigned long long>);
EXPECT_THROW(Load("-128").as<uint8_t>(), TypedBadConversion<unsigned char>);
}
TEST(LoadNodeTest, Binary) {
Node node = Load(
"[!!binary \"SGVsbG8sIFdvcmxkIQ==\", !!binary "
"\"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieS"
"B0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIG"
"x1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbi"
"B0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZG"
"dlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS"
"4K\"]");
EXPECT_EQ(Binary(reinterpret_cast<const unsigned char*>("Hello, World!"), 13),
node[0].as<Binary>());
EXPECT_EQ(Binary(reinterpret_cast<const unsigned char*>(
"Man is distinguished, not only by his reason, "
"but by this singular passion from other "
"animals, which is a lust of the mind, that by "
"a perseverance of delight in the continued and "
"indefatigable generation of knowledge, exceeds "
"the short vehemence of any carnal pleasure.\n"),
270),
node[1].as<Binary>());
}
TEST(LoadNodeTest, BinaryWithWhitespaces) {
Node node = Load(
"binaryText: !binary |-\n"
" TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieS\n"
" B0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIG\n"
" x1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbi\n"
" B0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZG\n"
" dlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS\n"
" 4K");
EXPECT_EQ(Binary(reinterpret_cast<const unsigned char*>(
"Man is distinguished, not only by his reason, "
"but by this singular passion from other "
"animals, which is a lust of the mind, that by "
"a perseverance of delight in the continued and "
"indefatigable generation of knowledge, exceeds "
"the short vehemence of any carnal pleasure.\n"),
270),
node["binaryText"].as<Binary>());
}
TEST(LoadNodeTest, IterateSequence) {
Node node = Load("[1, 3, 5, 7]");
int seq[] = {1, 3, 5, 7};
int i = 0;
for (const_iterator it = node.begin(); it != node.end(); ++it) {
EXPECT_TRUE(i < 4);
int x = seq[i++];
EXPECT_EQ(x, it->as<int>());
}
EXPECT_EQ(4, i);
}
TEST(LoadNodeTest, IterateMap) {
Node node = Load("{a: A, b: B, c: C}");
int i = 0;
for (const_iterator it = node.begin(); it != node.end(); ++it) {
EXPECT_TRUE(i < 3);
i++;
EXPECT_EQ(it->second.as<char>(), it->first.as<char>() + 'A' - 'a');
}
EXPECT_EQ(3, i);
}
#ifdef BOOST_FOREACH
TEST(LoadNodeTest, ForEach) {
Node node = Load("[1, 3, 5, 7]");
int seq[] = {1, 3, 5, 7};
int i = 0;
BOOST_FOREACH (const Node& item, node) {
int x = seq[i++];
EXPECT_EQ(x, item.as<int>());
}
}
TEST(LoadNodeTest, ForEachMap) {
Node node = Load("{a: A, b: B, c: C}");
BOOST_FOREACH (const const_iterator::value_type& p, node) {
EXPECT_EQ(p.second.as<char>(), p.first.as<char>() + 'A' - 'a');
}
}
#endif
TEST(LoadNodeTest, CloneScalar) {
Node node = Load("!foo monkey");
Node clone = Clone(node);
EXPECT_FALSE(clone == node);
EXPECT_EQ(clone.as<std::string>(), node.as<std::string>());
EXPECT_EQ(clone.Tag(), node.Tag());
}
TEST(LoadNodeTest, CloneSeq) {
Node node = Load("[1, 3, 5, 7]");
Node clone = Clone(node);
EXPECT_FALSE(clone == node);
EXPECT_EQ(NodeType::Sequence, clone.Type());
EXPECT_EQ(clone.size(), node.size());
for (std::size_t i = 0; i < node.size(); i++) {
EXPECT_EQ(clone[i].as<int>(), node[i].as<int>());
}
}
TEST(LoadNodeTest, CloneMap) {
Node node = Load("{foo: bar}");
Node clone = Clone(node);
EXPECT_FALSE(clone == node);
EXPECT_EQ(NodeType::Map, clone.Type());
EXPECT_EQ(clone.size(), node.size());
EXPECT_EQ(clone["foo"].as<std::string>(), node["foo"].as<std::string>());
}
TEST(LoadNodeTest, CloneAlias) {
Node node = Load("&foo [*foo]");
Node clone = Clone(node);
EXPECT_FALSE(clone == node);
EXPECT_EQ(NodeType::Sequence, clone.Type());
EXPECT_EQ(clone.size(), node.size());
EXPECT_EQ(clone[0], clone);
}
TEST(LoadNodeTest, ForceInsertIntoMap) {
Node node;
node["a"] = "b";
node.force_insert("x", "y");
node.force_insert("a", 5);
EXPECT_EQ(3, node.size());
EXPECT_EQ(NodeType::Map, node.Type());
bool ab = false;
bool a5 = false;
bool xy = false;
for (const_iterator it = node.begin(); it != node.end(); ++it) {
if (it->first.as<std::string>() == "a") {
if (it->second.as<std::string>() == "b")
ab = true;
else if (it->second.as<std::string>() == "5")
a5 = true;
} else if (it->first.as<std::string>() == "x" &&
it->second.as<std::string>() == "y")
xy = true;
}
EXPECT_TRUE(ab);
EXPECT_TRUE(a5);
EXPECT_TRUE(xy);
}
TEST(LoadNodeTest, ResetNode) {
Node node = Load("[1, 2, 3]");
EXPECT_TRUE(!node.IsNull());
Node other = node;
node.reset();
EXPECT_TRUE(node.IsNull());
EXPECT_TRUE(!other.IsNull());
node.reset(other);
EXPECT_TRUE(!node.IsNull());
EXPECT_EQ(node, other);
}
TEST(LoadNodeTest, EmptyString) {
Node node = Load("\"\"");
EXPECT_TRUE(!node.IsNull());
}
TEST(LoadNodeTest, DereferenceIteratorError) {
Node node = Load("[{a: b}, 1, 2]");
EXPECT_THROW(node.begin()->first.as<int>(), InvalidNode);
EXPECT_EQ(true, (*node.begin()).IsMap());
EXPECT_EQ(true, node.begin()->IsMap());
EXPECT_THROW((*node.begin()->begin()).Type(), InvalidNode);
EXPECT_THROW(node.begin()->begin()->Type(), InvalidNode);
}
TEST(NodeTest, ParseNodeStyle) {
EXPECT_EQ(EmitterStyle::Flow, Load("[1, 2, 3]").Style());
EXPECT_EQ(EmitterStyle::Flow, Load("{foo: bar}").Style());
EXPECT_EQ(EmitterStyle::Block, Load("- foo\n- bar").Style());
EXPECT_EQ(EmitterStyle::Block, Load("foo: bar").Style());
}
struct ParserExceptionTestCase {
std::string name;
std::string input;
std::string expected_exception;
};
TEST(NodeTest, IncompleteJson) {
std::vector<ParserExceptionTestCase> tests = {
{"JSON map without value", "{\"access\"", ErrorMsg::END_OF_MAP_FLOW},
{"JSON map with colon but no value", "{\"access\":",
ErrorMsg::END_OF_MAP_FLOW},
{"JSON map with unclosed value quote", "{\"access\":\"",
ErrorMsg::END_OF_MAP_FLOW},
{"JSON map without end brace", "{\"access\":\"abc\"",
ErrorMsg::END_OF_MAP_FLOW},
};
for (const ParserExceptionTestCase& test : tests) {
try {
Load(test.input);
FAIL() << "Expected exception " << test.expected_exception << " for "
<< test.name << ", input: " << test.input;
} catch (const ParserException& e) {
EXPECT_EQ(test.expected_exception, e.msg);
}
}
}
struct SingleNodeTestCase {
std::string input;
NodeType::value nodeType;
int nodeSize;
std::string expected_content;
};
TEST(NodeTest, SpecialFlow) {
std::vector<SingleNodeTestCase> tests = {
{"[:]", NodeType::Sequence, 1, "[{~: ~}]"},
{"[a:]", NodeType::Sequence, 1, "[{a: ~}]"},
{"[:a]", NodeType::Sequence, 1, "[:a]"},
{"[,]", NodeType::Sequence, 1, "[~]"},
{"[a:,]", NodeType::Sequence, 1, "[{a: ~}]"},
{"{:}", NodeType::Map, 1, "{~: ~}"},
{"{a:}", NodeType::Map, 1, "{a: ~}"},
{"{:a}", NodeType::Map, 1, "{:a: ~}"},
{"{,}", NodeType::Map, 1, "{~: ~}"},
{"{a:,}", NodeType::Map, 1, "{a: ~}"},
//testcase for the trailing TAB of scalar
{"key\t: value\t", NodeType::Map, 1, "key: value"},
{"key\t: value\t #comment", NodeType::Map, 1, "key: value"},
{"{key\t: value\t}", NodeType::Map, 1, "{key: value}"},
{"{key\t: value\t #comment\n}", NodeType::Map, 1, "{key: value}"},
};
for (const SingleNodeTestCase& test : tests) {
Node node = Load(test.input);
Emitter emitter;
emitter << node;
EXPECT_EQ(test.nodeType, node.Type());
EXPECT_EQ(test.nodeSize, node.size());
EXPECT_EQ(test.expected_content, std::string(emitter.c_str()));
}
}
TEST(NodeTest, IncorrectFlow) {
std::vector<ParserExceptionTestCase> tests = {
{"Incorrect yaml: \"{:]\"", "{:]", ErrorMsg::FLOW_END},
{"Incorrect yaml: \"[:}\"", "[:}", ErrorMsg::FLOW_END},
};
for (const ParserExceptionTestCase test : tests) {
try {
Load(test.input);
FAIL() << "Expected exception " << test.expected_exception << " for "
<< test.name << ", input: " << test.input;
} catch (const ParserException& e) {
EXPECT_EQ(test.expected_exception, e.msg);
}
}
}
TEST(NodeTest, LoadTildeAsNull) {
Node node = Load("~");
ASSERT_TRUE(node.IsNull());
EXPECT_EQ(node.as<std::string>(), "null");
EXPECT_EQ(node.as<std::string>("~"), "null");
}
TEST(NodeTest, LoadNullWithStrTag) {
Node node = Load("!!str null");
EXPECT_EQ(node.Tag(), "tag:yaml.org,2002:str");
EXPECT_EQ(node.as<std::string>(), "null");
}
TEST(NodeTest, LoadQuotedNull) {
Node node = Load("\"null\"");
EXPECT_EQ(node.as<std::string>(), "null");
}
TEST(NodeTest, LoadNonClosedQuotedString) {
EXPECT_THROW(Load(R"("foo)"), ParserException);
}
TEST(NodeTest, LoadWrongQuotedString) {
EXPECT_THROW(Load(R"("foo" [)"), ParserException);
EXPECT_THROW(Load(R"("foo", [)"), ParserException);
}
TEST(NodeTest, LoadUnquotedQuotedStrings) {
Node node = Load(R"(foo,"bar")");
EXPECT_EQ(node.as<std::string>(), "foo,\"bar\"");
node = Load(R"(foo,bar)");
EXPECT_EQ(node.as<std::string>(), "foo,bar");
node = Load(R"(foo,)");
EXPECT_EQ(node.as<std::string>(), "foo,");
node = Load(R"(foo "bar")");
EXPECT_EQ(node.as<std::string>(), "foo \"bar\"");
}
TEST(NodeTest, LoadCommaSeparatedStrings) {
EXPECT_THROW(Load(R"("foo","bar")"), ParserException);
EXPECT_THROW(Load(R"("foo",bar)"), ParserException);
EXPECT_THROW(Load(R"(,)"), ParserException);
EXPECT_THROW(Load(R"("foo",)"), ParserException);
EXPECT_THROW(Load(R"("foo","")"), ParserException);
EXPECT_THROW(Load(R"("foo",)"), ParserException);
EXPECT_THROW(Load(R"(,"foo")"), ParserException);
EXPECT_THROW(Load(R"(,foo)"), ParserException);
}
TEST(NodeTest, InfiniteLoopNodes) {
// Until yaml-cpp <= 0.8.0 this caused an infinite loop;
// After, it triggers an exception (but LoadAll is smart enough to avoid
// the infinite loop in any case).
EXPECT_THROW(LoadAll(R"(,)"), ParserException);
}
TEST(NodeTest, MultipleDocumentsBeginning) {
std::vector<Node> docs = LoadAll("\n---\n---\nA\n");
EXPECT_EQ(docs.size(), 2);
}
TEST(NodeTest, MultipleDocumentsEnds) {
std::vector<Node> docs = LoadAll("\n...\nA\n...\n");
EXPECT_EQ(docs.size(), 2);
}
TEST(NodeTest, MultipleDocumentsEndsWithEmptyDocs) {
std::vector<Node> docs = LoadAll("\n...\nA\n...\n...\nB\n...");
EXPECT_EQ(docs.size(), 4);
}
struct NewLineStringsTestCase {
std::string input;
std::string expected_content;
bool should_throw;
};
TEST(NodeTest, LoadNewLineStrings) {
std::vector<NewLineStringsTestCase> tests = {
{"foo\n, bar", "foo , bar", false},
{"foo\n, \"bar\"", "foo , \"bar\"", false},
{"\"foo\"\n, \"bar\"", "", true},
{"\"foo\"\n, bar", "", true},
};
for (const NewLineStringsTestCase& test : tests) {
if (test.should_throw) {
EXPECT_THROW(Load(test.input), ParserException);
} else {
Node node = Load(test.input);
Emitter emitter;
emitter << node;
EXPECT_EQ(NodeType::Scalar, node.Type());
EXPECT_EQ(test.expected_content, std::string(emitter.c_str()));
}
}
}
TEST(NodeTest, LoadSameLineStrings) {
EXPECT_THROW(Load(R"("foo" "bar")"), ParserException);
EXPECT_THROW(Load(R"("foo" bar)"), ParserException);
}
TEST(NodeTest, LoadTagWithParenthesis) {
Node node = Load("!Complex(Tag) foo");
EXPECT_EQ(node.Tag(), "!Complex(Tag)");
EXPECT_EQ(node.as<std::string>(), "foo");
}
TEST(NodeTest, LoadTagWithNullScalar) {
Node node = Load("!2");
EXPECT_TRUE(node.IsNull());
}
TEST(LoadNodeTest, BlockCRNLEncoded) {
Node node = Load(
"blockText: |\r\n"
" some arbitrary text \r\n"
" spanning some \r\n"
" lines, that are split \r\n"
" by CR and NL\r\n"
"followup: 1");
EXPECT_EQ(
"some arbitrary text \nspanning some \nlines, that are split \nby CR and "
"NL\n",
node["blockText"].as<std::string>());
EXPECT_EQ(1, node["followup"].as<int>());
}
TEST(LoadNodeTest, BlockCREncoded) {
Node node = Load(
"blockText: |\r"
" some arbitrary text \r"
" spanning some \r"
" lines, that are split \r"
" by CR and NL\r"
"followup: 1");
EXPECT_EQ(
"some arbitrary text \nspanning some \nlines, that are split \nby CR and "
"NL\n",
node["blockText"].as<std::string>());
EXPECT_EQ(1, node["followup"].as<int>());
}
TEST(LoadNodeTest, IncorrectSeqEnd) {
EXPECT_THROW(Load("[foo]_bar"), ParserException);
}
TEST(LoadNodeTest, NonUniqueMapKey) {
EXPECT_THROW(Load("{a: A, b: B, a: A}"), NonUniqueMapKey);
}
} // namespace
} // namespace YAML
File diff suppressed because it is too large Load Diff