/*
Ousía
Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include
#include
#include
namespace ousia {
static TerminalLogger logger{std::cerr, true};
// static Logger logger;
TEST(VariantReader, readString)
{
// Simple, double quoted string
{
CharReader reader("\"hello world\"");
auto res = VariantReader::parseString(reader, logger);
ASSERT_TRUE(res.first);
ASSERT_EQ("hello world", res.second);
}
// Simple, double quoted string with whitespace
{
CharReader reader(" \"hello world\" ");
auto res = VariantReader::parseString(reader, logger);
ASSERT_TRUE(res.first);
ASSERT_EQ("hello world", res.second);
}
// Simple, single quoted string
{
CharReader reader("'hello world'");
auto res = VariantReader::parseString(reader, logger);
ASSERT_TRUE(res.first);
ASSERT_EQ("hello world", res.second);
}
// Escape characters
{
CharReader reader("'\\'\\\"\\b\\f\\n\\r\\t\\v'");
auto res = VariantReader::parseString(reader, logger);
ASSERT_TRUE(res.first);
ASSERT_EQ("'\"\b\f\n\r\t\v", res.second);
}
// Hex Unicode character
{
CharReader reader("'linebreak\\u000A in unicode'");
auto res = VariantReader::parseString(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_EQ("linebreak\n in unicode", res.second);
}
}
TEST(VariantReader, readStringUnicode)
{
// Hex Unicode character
{
CharReader reader("'linebreak \\u000A in unicode'");
auto res = VariantReader::parseString(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_EQ("linebreak \n in unicode", res.second);
}
// Hex Unicode character
{
CharReader reader("'hammer and sickle \\u262D in unicode'");
auto res = VariantReader::parseString(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_EQ("hammer and sickle \342\230\255 in unicode", res.second);
}
// Octal Latin-1 character
{
CharReader reader("'copyright symbol \\251 in Unicode'");
auto res = VariantReader::parseString(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_EQ("copyright symbol \302\251 in Unicode", res.second);
}
// Hexadecimal Latin-1 character
{
CharReader reader("'copyright symbol \\xA9 in Unicode'");
auto res = VariantReader::parseString(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_EQ("copyright symbol \302\251 in Unicode", res.second);
}
// Errornous unicode escape sequence
{
CharReader reader("'\\uBLUB'");
auto res = VariantReader::parseString(reader, logger, {';'});
ASSERT_FALSE(res.first);
}
// Errornous octal escape sequence
{
CharReader reader("'\\400'");
auto res = VariantReader::parseString(reader, logger, {';'});
ASSERT_FALSE(res.first);
}
// Errornous hexadecimal latin1 escape sequence
{
CharReader reader("'\\xa'");
auto res = VariantReader::parseString(reader, logger, {';'});
ASSERT_FALSE(res.first);
}
}
TEST(VariantReader, parseToken)
{
// Simple case
{
CharReader reader("hello world;");
{
auto res = VariantReader::parseToken(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_EQ("hello", res.second);
}
{
auto res = VariantReader::parseToken(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_EQ("world", res.second);
}
}
// Simple case with whitespace
{
CharReader reader(" hello world ; ");
{
auto res = VariantReader::parseToken(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_EQ("hello", res.second);
}
{
auto res = VariantReader::parseToken(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_EQ("world", res.second);
}
}
// Linebreaks
{
CharReader reader(" hello\nworld ; ");
{
auto res = VariantReader::parseToken(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_EQ("hello", res.second);
}
{
auto res = VariantReader::parseToken(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_EQ("world", res.second);
}
}
// End of stream
{
CharReader reader(" hello world");
{
auto res = VariantReader::parseToken(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_EQ("hello", res.second);
}
{
auto res = VariantReader::parseToken(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_EQ("world", res.second);
}
}
}
TEST(VariantReader, parseUnescapedString)
{
// Simple case
{
CharReader reader("hello world;");
auto res = VariantReader::parseUnescapedString(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_EQ("hello world", res.second);
}
// Simple case with whitespace
{
CharReader reader(" hello world ; aha");
auto res = VariantReader::parseUnescapedString(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_EQ("hello world", res.second);
}
// Linebreaks
{
CharReader reader(" hello\nworld ; ");
auto res = VariantReader::parseUnescapedString(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_EQ("hello\nworld", res.second);
}
// End of stream
{
CharReader reader(" hello world");
auto res = VariantReader::parseUnescapedString(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_EQ("hello world", res.second);
}
}
TEST(VariantReader, parseBool)
{
// Valid bools
{
CharReader reader(" true ");
auto res = VariantReader::parseBool(reader, logger);
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second);
}
{
CharReader reader(" false ");
auto res = VariantReader::parseBool(reader, logger);
ASSERT_TRUE(res.first);
ASSERT_FALSE(res.second);
}
{
CharReader reader(" true bla");
auto res = VariantReader::parseBool(reader, logger);
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second);
reader.consumeWhitespace();
char c;
ASSERT_TRUE(reader.read(c));
ASSERT_EQ('b', c);
ASSERT_TRUE(reader.read(c));
ASSERT_EQ('l', c);
ASSERT_TRUE(reader.read(c));
ASSERT_EQ('a', c);
ASSERT_FALSE(reader.read(c));
}
// invalid bools.
{
CharReader reader(" bla ");
auto res = VariantReader::parseBool(reader, logger);
ASSERT_FALSE(res.first);
}
{
CharReader reader(" TRUE ");
auto res = VariantReader::parseBool(reader, logger);
ASSERT_FALSE(res.first);
}
{
CharReader reader(" tr ue ");
auto res = VariantReader::parseBool(reader, logger);
ASSERT_FALSE(res.first);
}
}
static const std::unordered_set noDelim;
TEST(VariantReader, parseInteger)
{
// Valid integers
{
CharReader reader("0 ");
auto res = VariantReader::parseInteger(reader, logger, noDelim);
ASSERT_TRUE(res.first);
ASSERT_EQ(0, res.second);
}
{
CharReader reader("42 ");
auto res = VariantReader::parseInteger(reader, logger, noDelim);
ASSERT_TRUE(res.first);
ASSERT_EQ(42, res.second);
}
{
CharReader reader("-42");
auto res = VariantReader::parseInteger(reader, logger, noDelim);
ASSERT_TRUE(res.first);
ASSERT_EQ(-42, res.second);
}
{
CharReader reader(" -0x4A2 ");
auto res = VariantReader::parseInteger(reader, logger, noDelim);
ASSERT_TRUE(res.first);
ASSERT_EQ(-0x4A2, res.second);
}
{
CharReader reader(" 0Xaffe");
auto res = VariantReader::parseInteger(reader, logger, noDelim);
ASSERT_TRUE(res.first);
ASSERT_EQ(0xAFFE, res.second);
}
{
CharReader reader("0x7FFFFFFFFFFFFFFF");
auto res = VariantReader::parseInteger(reader, logger, noDelim);
ASSERT_TRUE(res.first);
ASSERT_EQ(0x7FFFFFFFFFFFFFFFL, res.second);
}
{
CharReader reader("-0x7FFFFFFFFFFFFFFF");
auto res = VariantReader::parseInteger(reader, logger, noDelim);
ASSERT_TRUE(res.first);
ASSERT_EQ(-0x7FFFFFFFFFFFFFFFL, res.second);
}
// Invalid integers
{
CharReader reader("-");
auto res = VariantReader::parseInteger(reader, logger, noDelim);
ASSERT_FALSE(res.first);
}
{
CharReader reader("0a");
auto res = VariantReader::parseInteger(reader, logger, noDelim);
ASSERT_FALSE(res.first);
}
{
CharReader reader("-0xag");
auto res = VariantReader::parseInteger(reader, logger, noDelim);
ASSERT_FALSE(res.first);
}
{
CharReader reader("0x8000000000000000");
auto res = VariantReader::parseInteger(reader, logger, noDelim);
ASSERT_FALSE(res.first);
}
}
TEST(VariantReader, parseDouble)
{
// Valid doubles
{
CharReader reader("1.25");
auto res = VariantReader::parseDouble(reader, logger, noDelim);
ASSERT_TRUE(res.first);
ASSERT_EQ(1.25, res.second);
}
{
CharReader reader(".25");
auto res = VariantReader::parseDouble(reader, logger, noDelim);
ASSERT_TRUE(res.first);
ASSERT_EQ(.25, res.second);
}
{
CharReader reader(".25e1");
auto res = VariantReader::parseDouble(reader, logger, noDelim);
ASSERT_TRUE(res.first);
ASSERT_EQ(2.5, res.second);
}
{
CharReader reader("-2.5e-1");
auto res = VariantReader::parseDouble(reader, logger, noDelim);
ASSERT_TRUE(res.first);
ASSERT_EQ(-0.25, res.second);
}
{
CharReader reader("-50e-2");
auto res = VariantReader::parseDouble(reader, logger, noDelim);
ASSERT_TRUE(res.first);
ASSERT_EQ(-0.5, res.second);
}
{
CharReader reader("-1.");
auto res = VariantReader::parseDouble(reader, logger, noDelim);
ASSERT_TRUE(res.first);
ASSERT_EQ(-1., res.second);
}
{
CharReader reader("-50.e-2");
auto res = VariantReader::parseDouble(reader, logger, {'.'});
ASSERT_TRUE(res.first);
ASSERT_EQ(-50, res.second);
}
// Invalid doubles
{
CharReader reader(".e1");
auto res = VariantReader::parseDouble(reader, logger, noDelim);
ASSERT_FALSE(res.first);
}
{
CharReader reader("0e100000");
auto res = VariantReader::parseDouble(reader, logger, noDelim);
ASSERT_FALSE(res.first);
}
}
TEST(VariantReader, parseArray)
{
// Simple case (only primitive data types)
{
CharReader reader(
"[\"Hello, World\", unescaped\n string ,\n"
"1234, 0.56, true, false, null]");
auto res = VariantReader::parseArray(reader, logger);
ASSERT_TRUE(res.first);
// Make sure array has the correct size
ASSERT_EQ(7U, res.second.size());
// Check the types
ASSERT_TRUE(res.second[0].isString());
ASSERT_TRUE(res.second[1].isString());
ASSERT_TRUE(res.second[2].isInt());
ASSERT_TRUE(res.second[3].isDouble());
ASSERT_TRUE(res.second[4].isBool());
ASSERT_TRUE(res.second[5].isBool());
ASSERT_TRUE(res.second[6].isNull());
// Check the values
ASSERT_EQ("Hello, World", res.second[0].asString());
ASSERT_EQ("unescaped\n string", res.second[1].asString());
ASSERT_EQ(1234, res.second[2].asInt());
ASSERT_EQ(0.56, res.second[3].asDouble());
ASSERT_TRUE(res.second[4].asBool());
ASSERT_FALSE(res.second[5].asBool());
}
// Ending with comma
{
CharReader reader("[ 'test' ,]");
auto res = VariantReader::parseArray(reader, logger);
ASSERT_TRUE(res.first);
// Make sure the array has the correct size
ASSERT_EQ(1U, res.second.size());
// Check the types
ASSERT_TRUE(res.second[0].isString());
// Check the values
ASSERT_EQ("test", res.second[0].asString());
}
// Recovery from invalid values
{
CharReader reader("[ 0invalidNumber, str, 1invalid]");
auto res = VariantReader::parseArray(reader, logger);
ASSERT_TRUE(res.first);
// Make sure the array has the correct size
ASSERT_EQ(3U, res.second.size());
// Check the types (all must be strings since the numbers are invalid)
ASSERT_TRUE(res.second[0].isString());
ASSERT_TRUE(res.second[1].isString());
ASSERT_TRUE(res.second[2].isString());
// Check the values
ASSERT_EQ("0invalidNumber", res.second[0].asString());
ASSERT_EQ("str", res.second[1].asString());
ASSERT_EQ("1invalid", res.second[2].asString());
}
}
TEST(VariantReader, parseObject)
{
// Array as object
{
CharReader reader(
"[\"Hello, World\", unescaped\n string ,\n"
"1234, 0.56, true, false, null]");
auto res = VariantReader::parseObject(reader, logger);
ASSERT_TRUE(res.first);
// Make sure the object has the correct size
ASSERT_EQ(7U, res.second.size());
// Check the types
ASSERT_TRUE(res.second["#0"].isString());
ASSERT_TRUE(res.second["#1"].isString());
ASSERT_TRUE(res.second["#2"].isInt());
ASSERT_TRUE(res.second["#3"].isDouble());
ASSERT_TRUE(res.second["#4"].isBool());
ASSERT_TRUE(res.second["#5"].isBool());
ASSERT_TRUE(res.second["#6"].isNull());
// Check the values
ASSERT_EQ("Hello, World", res.second["#0"].asString());
ASSERT_EQ("unescaped\n string", res.second["#1"].asString());
ASSERT_EQ(1234, res.second["#2"].asInt());
ASSERT_EQ(0.56, res.second["#3"].asDouble());
ASSERT_TRUE(res.second["#4"].asBool());
ASSERT_FALSE(res.second["#5"].asBool());
}
// Simple object
{
CharReader reader("[key1=foo, key2=bar]");
auto res = VariantReader::parseObject(reader, logger);
ASSERT_TRUE(res.first);
// Make sure the object has the correct size
ASSERT_EQ(2U, res.second.size());
// Check the types
ASSERT_TRUE(res.second["key1"].isString());
ASSERT_TRUE(res.second["key2"].isString());
// Check the values
ASSERT_EQ("foo", res.second["key1"].asString());
ASSERT_EQ("bar", res.second["key2"].asString());
}
// Simple array/object interleaved
{
CharReader reader("[foo1, key1=foo, foo2, key2=bar, foo3]");
auto res = VariantReader::parseObject(reader, logger);
ASSERT_TRUE(res.first);
// Make sure the object has the correct size
ASSERT_EQ(5U, res.second.size());
// Check the types
ASSERT_TRUE(res.second["key1"].isString());
ASSERT_TRUE(res.second["key2"].isString());
ASSERT_TRUE(res.second["#0"].isString());
ASSERT_TRUE(res.second["#2"].isString());
ASSERT_TRUE(res.second["#4"].isString());
// Check the values
ASSERT_EQ("foo", res.second["key1"].asString());
ASSERT_EQ("bar", res.second["key2"].asString());
ASSERT_EQ("foo1", res.second["#0"].asString());
ASSERT_EQ("foo2", res.second["#2"].asString());
ASSERT_EQ("foo3", res.second["#4"].asString());
}
// More complex array/object
{
CharReader reader("[key1=true, foo, key2=3.5]");
auto res = VariantReader::parseObject(reader, logger);
ASSERT_TRUE(res.first);
// Make sure the object has the correct size
ASSERT_EQ(3U, res.second.size());
// Check the types
ASSERT_TRUE(res.second["key1"].isBool());
ASSERT_TRUE(res.second["key2"].isDouble());
ASSERT_TRUE(res.second["#1"].isString());
// Check the values
ASSERT_TRUE(res.second["key1"].asBool());
ASSERT_EQ(3.5, res.second["key2"].asDouble());
ASSERT_EQ("foo", res.second["#1"].asString());
}
// Even More complex array/object
{
CharReader reader(
"[\"key1\" = [4, 5, true, e=[1, 2, 3]], \"key2\"=[]]");
auto res = VariantReader::parseObject(reader, logger);
ASSERT_TRUE(res.first);
// Make sure the object has the correct size
ASSERT_EQ(2U, res.second.size());
// Check the types
ASSERT_TRUE(res.second["key1"].isMap());
ASSERT_TRUE(res.second["key2"].isArray());
// Check the values
auto m = res.second["key1"].asMap();
ASSERT_EQ(4U, m.size());
ASSERT_TRUE(m["#0"].isInt());
ASSERT_TRUE(m["#1"].isInt());
ASSERT_TRUE(m["#2"].isBool());
ASSERT_TRUE(m["e"].isArray());
ASSERT_EQ(4, m["#0"].asInt());
ASSERT_EQ(5, m["#1"].asInt());
ASSERT_TRUE(m["#2"].asBool());
ASSERT_EQ(3U, m["e"].asArray().size());
ASSERT_EQ(1, m["e"].asArray()[0].asInt());
ASSERT_EQ(2, m["e"].asArray()[1].asInt());
ASSERT_EQ(3, m["e"].asArray()[2].asInt());
auto a = res.second["key2"].asArray();
ASSERT_EQ(0U, a.size());
}
// Invalid array/object
{
CharReader reader("[invalid key = bla]");
auto res = VariantReader::parseObject(reader, logger);
ASSERT_FALSE(res.first);
}
// Mutliple commas
{
CharReader reader("[r=50,,t=70, b=70,g=60]");
auto res = VariantReader::parseObject(reader, logger);
ASSERT_FALSE(res.first);
}
}
TEST(VariantReader, parseCardinality)
{
Logger logger;
// Primitive cardinality.
{
CharReader reader(" { 5 } ");
auto res = VariantReader::parseCardinality(reader, logger);
ASSERT_TRUE(res.first);
Variant::cardinalityType card{};
card.merge({5});
ASSERT_EQ(card, res.second);
}
// Range cardinality
{
CharReader reader(" { 5-10 } ");
auto res = VariantReader::parseCardinality(reader, logger);
ASSERT_TRUE(res.first);
Variant::cardinalityType card{};
card.merge({5, 10});
ASSERT_EQ(card, res.second);
}
// Larger than
{
CharReader reader(" { >9 } ");
auto res = VariantReader::parseCardinality(reader, logger);
ASSERT_TRUE(res.first);
Variant::cardinalityType card{};
card.merge(Variant::rangeType::typeRangeFrom(10));
ASSERT_EQ(card, res.second);
}
// Smaller than
{
CharReader reader(" { <9 } ");
auto res = VariantReader::parseCardinality(reader, logger);
ASSERT_TRUE(res.first);
Variant::cardinalityType card{};
card.merge(Variant::rangeType::typeRangeUntil(8));
ASSERT_EQ(card, res.second);
}
// Kleene-Star
{
CharReader reader(" { * } ");
auto res = VariantReader::parseCardinality(reader, logger);
ASSERT_TRUE(res.first);
Variant::cardinalityType card{};
card.merge(Variant::rangeType::typeRange());
ASSERT_EQ(card, res.second);
}
// More complex parse
{
CharReader reader(" { 1 , 4- 6 ,>8 } some other text");
auto res = VariantReader::parseCardinality(reader, logger);
ASSERT_TRUE(res.first);
Variant::cardinalityType card{};
card.merge({1});
card.merge({4, 6});
card.merge(Variant::rangeType::typeRangeFrom(9));
ASSERT_EQ(card, res.second);
}
// More complex parses that are equivalent.
{
Variant::cardinalityType card{};
card.merge(Variant::rangeType::typeRange());
{
CharReader reader(" { * } ");
auto res = VariantReader::parseCardinality(reader, logger);
ASSERT_TRUE(res.first);
ASSERT_EQ(card, res.second);
}
{
CharReader reader = CharReader(" { 1-4, 8, 9-12, 10, * } ");
auto res = VariantReader::parseCardinality(reader, logger);
ASSERT_TRUE(res.first);
ASSERT_EQ(card, res.second);
}
{
CharReader reader = CharReader(" { 0, >0 } ");
auto res = VariantReader::parseCardinality(reader, logger);
ASSERT_TRUE(res.first);
ASSERT_EQ(card, res.second);
}
{
CharReader reader = CharReader(" { <10, 10, >10 } ");
auto res = VariantReader::parseCardinality(reader, logger);
ASSERT_TRUE(res.first);
ASSERT_EQ(card, res.second);
}
{
CharReader reader = CharReader(" { 0,1-2, 3-4, >4 } ");
auto res = VariantReader::parseCardinality(reader, logger);
ASSERT_TRUE(res.first);
ASSERT_EQ(card, res.second);
}
}
// Invalid cardinalities.
{
CharReader reader(" 5 } ");
ASSERT_FALSE(VariantReader::parseCardinality(reader, logger).first);
}
{
CharReader reader(" { 5 , } ");
ASSERT_FALSE(VariantReader::parseCardinality(reader, logger).first);
}
{
CharReader reader(" { 5- } ");
ASSERT_FALSE(VariantReader::parseCardinality(reader, logger).first);
}
{
CharReader reader(" { -3 } ");
ASSERT_FALSE(VariantReader::parseCardinality(reader, logger).first);
}
{
CharReader reader(" { 5-3 } ");
ASSERT_FALSE(VariantReader::parseCardinality(reader, logger).first);
}
{
CharReader reader(" { 3-3 } ");
ASSERT_FALSE(VariantReader::parseCardinality(reader, logger).first);
}
{
CharReader reader(" { > } ");
ASSERT_FALSE(VariantReader::parseCardinality(reader, logger).first);
}
{
CharReader reader(" { < } ");
ASSERT_FALSE(VariantReader::parseCardinality(reader, logger).first);
}
{
CharReader reader(" { , } ");
ASSERT_FALSE(VariantReader::parseCardinality(reader, logger).first);
}
{
CharReader reader(" { 4 ");
ASSERT_FALSE(VariantReader::parseCardinality(reader, logger).first);
}
{
CharReader reader(" { m } ");
ASSERT_FALSE(VariantReader::parseCardinality(reader, logger).first);
}
}
TEST(VariantReader, parseGenericToken)
{
// Simple case, unescaped string
{
CharReader reader("hello world");
// 01234567890
// 0 1
auto res =
VariantReader::parseGenericToken(reader, logger, {';'}, true);
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isString());
ASSERT_FALSE(res.second.isMagic());
ASSERT_EQ("hello world", res.second.asString());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(0U, loc.getStart());
ASSERT_EQ(11U, loc.getEnd());
}
// Simple case, double quoted string
{
CharReader reader(" \"hello world\" ");
// 0 123456789012 34567
// 0 1
auto res =
VariantReader::parseGenericToken(reader, logger, {';'}, true);
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isString());
ASSERT_FALSE(res.second.isMagic());
ASSERT_EQ("hello world", res.second.asString());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(1U, loc.getStart());
ASSERT_EQ(14U, loc.getEnd());
}
// Simple case, single quoted string
{
CharReader reader(" 'hello world' ");
// 012345678901234567
// 0 1
auto res =
VariantReader::parseGenericToken(reader, logger, {';'}, true);
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isString());
ASSERT_FALSE(res.second.isMagic());
ASSERT_EQ("hello world", res.second.asString());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(1U, loc.getStart());
ASSERT_EQ(14U, loc.getEnd());
}
// String with whitespaces at the beginning.
{
CharReader reader(" ' test'");
// 0123456789
auto res =
VariantReader::parseGenericToken(reader, logger, {';'}, true);
ASSERT_EQ(" test", res.second);
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(3U, loc.getStart());
ASSERT_EQ(10U, loc.getEnd());
}
// Integer
{
CharReader reader("1234");
// 0123
auto res =
VariantReader::parseGenericToken(reader, logger, {';'}, true);
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isInt());
ASSERT_EQ(1234, res.second.asInt());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(0U, loc.getStart());
ASSERT_EQ(4U, loc.getEnd());
}
// Double
{
CharReader reader("1234.5");
// 012345
auto res =
VariantReader::parseGenericToken(reader, logger, {';'}, true);
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isDouble());
ASSERT_EQ(1234.5, res.second.asDouble());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(0U, loc.getStart());
ASSERT_EQ(6U, loc.getEnd());
}
// Boolean (true)
{
CharReader reader("true");
// 0123
auto res =
VariantReader::parseGenericToken(reader, logger, {';'}, true);
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isBool());
ASSERT_TRUE(res.second.asBool());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(0U, loc.getStart());
ASSERT_EQ(4U, loc.getEnd());
}
// Boolean (false)
{
CharReader reader("false");
// 01234
auto res =
VariantReader::parseGenericToken(reader, logger, {';'}, true);
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isBool());
ASSERT_FALSE(res.second.asBool());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(0U, loc.getStart());
ASSERT_EQ(5U, loc.getEnd());
}
// Nullptr
{
CharReader reader("null");
// 0123
auto res =
VariantReader::parseGenericToken(reader, logger, {';'}, true);
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isNull());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(0U, loc.getStart());
ASSERT_EQ(4U, loc.getEnd());
}
// Simple case, unescaped string
{
CharReader reader("hello world");
// 01234567890
// 0 1
{
auto res =
VariantReader::parseGenericToken(reader, logger, {';'}, false);
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isString());
ASSERT_TRUE(res.second.isMagic());
ASSERT_EQ("hello", res.second.asString());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(0U, loc.getStart());
ASSERT_EQ(5U, loc.getEnd());
}
{
auto res =
VariantReader::parseGenericToken(reader, logger, {';'}, false);
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isString());
ASSERT_TRUE(res.second.isMagic());
ASSERT_EQ("world", res.second.asString());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(6U, loc.getStart());
ASSERT_EQ(11U, loc.getEnd());
}
}
// Simple case, double quoted string
{
CharReader reader(" \"hello world\" ");
// 0 123456789012 34567
// 0 1
auto res =
VariantReader::parseGenericToken(reader, logger, {';'}, false);
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isString());
ASSERT_FALSE(res.second.isMagic());
ASSERT_EQ("hello world", res.second.asString());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(1U, loc.getStart());
ASSERT_EQ(14U, loc.getEnd());
}
// Simple case, single quoted string
{
CharReader reader(" 'hello world' ");
// 012345678901234567
// 0 1
auto res =
VariantReader::parseGenericToken(reader, logger, {';'}, false);
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isString());
ASSERT_FALSE(res.second.isMagic());
ASSERT_EQ("hello world", res.second.asString());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(1U, loc.getStart());
ASSERT_EQ(14U, loc.getEnd());
}
}
TEST(VariantReader, parseGeneric)
{
// Simple case, int.
{
CharReader reader("0");
// 0
auto res = VariantReader::parseGeneric(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isInt());
ASSERT_EQ(0, res.second.asInt());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(0U, loc.getStart());
ASSERT_EQ(1U, loc.getEnd());
}
// Simple case, unescaped string
{
CharReader reader("hello");
// 01234
auto res = VariantReader::parseGeneric(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isMagic());
ASSERT_EQ("hello", res.second.asMagic());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(0U, loc.getStart());
ASSERT_EQ(5U, loc.getEnd());
}
// Simple case, unescaped string with multiple array entries
{
CharReader reader("hello world");
// 01234567890
// 0 1
auto res = VariantReader::parseGeneric(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isArray());
auto arr = res.second.asArray();
ASSERT_EQ(2U, arr.size());
ASSERT_TRUE(arr[0].isMagic());
ASSERT_TRUE(arr[1].isMagic());
ASSERT_EQ("hello", arr[0].asMagic());
ASSERT_EQ("world", arr[1].asMagic());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(0U, loc.getStart());
ASSERT_EQ(11U, loc.getEnd());
}
// Delimiter test
{
CharReader reader("hello; world");
// 012345678901
// 0 1
auto res = VariantReader::parseGeneric(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isMagic());
ASSERT_EQ("hello", res.second.asMagic());
char c;
ASSERT_TRUE(reader.peek(c));
ASSERT_EQ(';', c);
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(0U, loc.getStart());
ASSERT_EQ(5U, loc.getEnd());
}
// More complex CSS-like case
{
CharReader reader("1px solid blue");
// 01234567890123
// 0 1
auto res = VariantReader::parseGeneric(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isArray());
auto arr = res.second.asArray();
ASSERT_EQ(3U, arr.size());
ASSERT_TRUE(arr[0].isString());
ASSERT_TRUE(arr[1].isMagic());
ASSERT_TRUE(arr[2].isMagic());
ASSERT_EQ("1px", arr[0].asString());
ASSERT_EQ("solid", arr[1].asMagic());
ASSERT_EQ("blue", arr[2].asMagic());
{
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(0U, loc.getStart());
ASSERT_EQ(14U, loc.getEnd());
}
{
SourceLocation loc = arr[0].getLocation();
ASSERT_EQ(0U, loc.getStart());
ASSERT_EQ(3U, loc.getEnd());
}
{
SourceLocation loc = arr[1].getLocation();
ASSERT_EQ(4U, loc.getStart());
ASSERT_EQ(9U, loc.getEnd());
}
{
SourceLocation loc = arr[2].getLocation();
ASSERT_EQ(10U, loc.getStart());
ASSERT_EQ(14U, loc.getEnd());
}
}
}
TEST(VariantReader, parseGenericString)
{
// Simple case, unescaped string
{
auto res = VariantReader::parseGenericString("foo", logger);
// 012
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isMagic());
ASSERT_EQ("foo", res.second.asMagic());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(0U, loc.getStart());
ASSERT_EQ(3U, loc.getEnd());
}
// Simple case, unescaped string with space
{
auto res = VariantReader::parseGenericString("foo bar", logger);
// 0123456
ASSERT_TRUE(res.first);
ASSERT_FALSE(res.second.isMagic());
ASSERT_TRUE(res.second.isString());
ASSERT_EQ("foo bar", res.second.asString());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(0U, loc.getStart());
ASSERT_EQ(7U, loc.getEnd());
}
// Parse double
{
auto res = VariantReader::parseGenericString("12.3", logger);
// 0123
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isDouble());
ASSERT_EQ(12.3, res.second.asDouble());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(0U, loc.getStart());
ASSERT_EQ(4U, loc.getEnd());
}
// Parse string
{
auto res = VariantReader::parseGenericString("6 times 7 is 42", logger);
// 012345678901234
// 0 1
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isString());
ASSERT_EQ("6 times 7 is 42", res.second.asString());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(0U, loc.getStart());
ASSERT_EQ(15U, loc.getEnd());
}
// Parse empty string
{
auto res = VariantReader::parseGenericString("", logger);
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isString());
ASSERT_EQ("", res.second.asString());
SourceLocation loc = res.second.getLocation();
ASSERT_EQ(0U, loc.getStart());
ASSERT_EQ(0U, loc.getEnd());
}
}
TEST(VariantReader, parseGenericComplex)
{
CharReader reader("10 true [1, 2] [] [foo=bar,h]; []");
auto res = VariantReader::parseGeneric(reader, logger, {';'});
ASSERT_TRUE(res.first);
ASSERT_TRUE(res.second.isArray());
auto arr = res.second.asArray();
ASSERT_EQ(5U, arr.size());
ASSERT_TRUE(arr[0].isInt());
ASSERT_TRUE(arr[1].isBool());
ASSERT_TRUE(arr[2].isArray());
ASSERT_TRUE(arr[3].isArray());
ASSERT_TRUE(arr[4].isMap());
ASSERT_EQ(10, arr[0].asInt());
ASSERT_TRUE(arr[1].asBool());
ASSERT_EQ(2U, arr[2].asArray().size());
ASSERT_EQ(1, arr[2].asArray()[0].asInt());
ASSERT_EQ(2, arr[2].asArray()[1].asInt());
ASSERT_EQ(0U, arr[3].asArray().size());
ASSERT_EQ(2U, arr[4].asMap().size());
ASSERT_TRUE(arr[4].asMap().count("foo"));
ASSERT_TRUE(arr[4].asMap().count("#1"));
ASSERT_TRUE(arr[4].asMap().find("foo")->second.isMagic());
ASSERT_EQ("bar", arr[4].asMap().find("foo")->second.asMagic());
char c;
ASSERT_TRUE(reader.peek(c));
ASSERT_EQ(';', c);
}
TEST(VariantReader, parseTyped)
{
{
auto res = VariantReader::parseTyped(VariantType::BOOL, "true", logger);
ASSERT_TRUE(res.first);
ASSERT_EQ(VariantType::BOOL, res.second.getType());
ASSERT_TRUE(res.second.asBool());
}
{
auto res =
VariantReader::parseTyped(VariantType::INT, " 1254", logger);
ASSERT_TRUE(res.first);
ASSERT_EQ(VariantType::INT, res.second.getType());
ASSERT_EQ(1254, res.second.asInt());
}
{
auto res =
VariantReader::parseTyped(VariantType::DOUBLE, " 3.14", logger);
ASSERT_TRUE(res.first);
ASSERT_EQ(VariantType::DOUBLE, res.second.getType());
ASSERT_EQ(3.14, res.second.asDouble());
}
{
auto res = VariantReader::parseTyped(VariantType::STRING,
"\'my string\'", logger);
ASSERT_TRUE(res.first);
ASSERT_EQ(VariantType::STRING, res.second.getType());
ASSERT_EQ("my string", res.second.asString());
}
{
auto res =
VariantReader::parseTyped(VariantType::STRING, "my string", logger);
ASSERT_FALSE(res.first);
}
{
auto res =
VariantReader::parseTyped(VariantType::ARRAY, "[1, 4, 5]", logger);
ASSERT_TRUE(res.first);
ASSERT_EQ(VariantType::ARRAY, res.second.getType());
Variant::arrayType actual = res.second.asArray();
Variant::arrayType expected{{1}, {4}, {5}};
ASSERT_EQ(expected, actual);
}
{
auto res = VariantReader::parseTyped(
VariantType::MAP, "[a=\"str\", b=true, i=4]", logger);
ASSERT_TRUE(res.first);
ASSERT_EQ(VariantType::MAP, res.second.getType());
Variant::mapType actual = res.second.asMap();
Variant::mapType expected{{"a", {"str"}}, {"b", {true}}, {"i", {4}}};
ASSERT_EQ(expected, actual);
}
{
auto res = VariantReader::parseTyped(VariantType::CARDINALITY,
"{1-2, >18}", logger);
ASSERT_TRUE(res.first);
ASSERT_EQ(VariantType::CARDINALITY, res.second.getType());
Variant::cardinalityType actual = res.second.asCardinality();
Variant::cardinalityType expected;
expected.merge({1, 2});
expected.merge(Variant::rangeType::typeRangeFrom(19));
ASSERT_EQ(expected, actual);
}
}
}