You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
yaml-cpp/test/integration/load_node_test.cpp

452 lines
15 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "yaml-cpp/yaml.h" // IWYU pragma: keep
#include "gtest/gtest.h"
namespace YAML {
namespace {
TEST(LoadNodeTest, Reassign) {
Node node = Load("foo");
node = Node();
EXPECT_TRUE(node.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, EmitEmptyNode) {
Node node;
Emitter emitter;
emitter << node;
EXPECT_EQ("", std::string(emitter.c_str()));
}
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(NodeSpecTest, 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);
}
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>());
}
} // namespace
} // namespace YAML