You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
570 lines
15 KiB
570 lines
15 KiB
/* |
|
* Copyright (C) 2022 Riyyi |
|
* |
|
* SPDX-License-Identifier: MIT |
|
*/ |
|
|
|
#include <cstddef> // nullptr_t |
|
#include <cstdint> // uint32_t |
|
#include <functional> // function |
|
#include <map> |
|
#include <string> |
|
#include <unordered_map> |
|
#include <vector> |
|
|
|
#include "macro.h" |
|
#include "ruc/json/array.h" |
|
#include "ruc/json/job.h" |
|
#include "ruc/json/json.h" |
|
#include "ruc/json/lexer.h" |
|
#include "ruc/json/parser.h" |
|
#include "ruc/json/serializer.h" |
|
#include "testcase.h" |
|
#include "testsuite.h" |
|
|
|
#define DONT_PRINT_PARSER_ERRORS |
|
|
|
#ifndef DONT_PRINT_PARSER_ERRORS |
|
#define EXEC(x) x |
|
#else |
|
#define EXEC(x) \ |
|
stderr = test::TestSuite::the().outputNull(); \ |
|
x; \ |
|
stderr = test::TestSuite::the().outputErr(); |
|
#endif |
|
|
|
std::vector<ruc::json::Token> lex(const std::string& input) |
|
{ |
|
EXEC( |
|
ruc::json::Job job(input); |
|
ruc::json::Lexer lexer(&job); |
|
lexer.analyze();); |
|
return *job.tokens(); |
|
} |
|
|
|
ruc::Json parse(const std::string& input) |
|
{ |
|
EXEC( |
|
ruc::json::Job job(input); |
|
ruc::json::Lexer lexer(&job); |
|
lexer.analyze();); |
|
|
|
if (!job.success()) { |
|
return nullptr; |
|
} |
|
|
|
EXEC( |
|
ruc::json::Parser parser(&job); |
|
ruc::Json json = parser.parse();); |
|
|
|
if (!job.success()) { |
|
return nullptr; |
|
} |
|
|
|
return json; |
|
} |
|
|
|
std::string serialize(const std::string& input, uint32_t indent = 0) |
|
{ |
|
EXEC( |
|
auto json = ruc::Json::parse(input);); |
|
return json.dump(indent); |
|
} |
|
|
|
// ----------------------------------------- |
|
|
|
TEST_CASE(JsonLexer) |
|
{ |
|
std::vector<ruc::json::Token> tokens; |
|
|
|
// Literal |
|
|
|
tokens = lex("true"); |
|
EXPECT_EQ(tokens.size(), 1); |
|
EXPECT_EQ(tokens[0].symbol, "true"); |
|
EXPECT(tokens[0].type == ruc::json::Token::Type::Literal); |
|
|
|
tokens = lex("false"); |
|
EXPECT_EQ(tokens.size(), 1); |
|
EXPECT_EQ(tokens[0].symbol, "false"); |
|
EXPECT(tokens[0].type == ruc::json::Token::Type::Literal); |
|
|
|
tokens = lex("null"); |
|
EXPECT_EQ(tokens.size(), 1); |
|
EXPECT_EQ(tokens[0].symbol, "null"); |
|
EXPECT(tokens[0].type == ruc::json::Token::Type::Literal); |
|
|
|
// Number |
|
|
|
tokens = lex("3.14"); |
|
EXPECT_EQ(tokens.size(), 1); |
|
EXPECT_EQ(tokens[0].symbol, "3.14"); |
|
EXPECT(tokens[0].type == ruc::json::Token::Type::Number); |
|
|
|
tokens = lex("-3.14e+2"); |
|
EXPECT_EQ(tokens.size(), 1); |
|
EXPECT_EQ(tokens[0].symbol, "-3.14e+2"); |
|
EXPECT(tokens[0].type == ruc::json::Token::Type::Number); |
|
|
|
tokens = lex("+3.14"); |
|
EXPECT_EQ(tokens.size(), 1); |
|
EXPECT_EQ(tokens[0].symbol, "+"); |
|
EXPECT(tokens[0].type == ruc::json::Token::Type::None); |
|
|
|
// String |
|
|
|
tokens = lex(R"("a string")"); |
|
EXPECT_EQ(tokens.size(), 1); |
|
EXPECT_EQ(tokens[0].symbol, "a string"); |
|
EXPECT(tokens[0].type == ruc::json::Token::Type::String); |
|
|
|
tokens = lex(R"("a string""another string")"); |
|
EXPECT_EQ(tokens.size(), 2); |
|
EXPECT_EQ(tokens[0].symbol, "a string"); |
|
EXPECT_EQ(tokens[1].symbol, "another string"); |
|
EXPECT(tokens[0].type == ruc::json::Token::Type::String); |
|
|
|
tokens = lex("\"a string\nwill break on the newline symbol\""); |
|
EXPECT_EQ(tokens.size(), 1); |
|
EXPECT_EQ(tokens[0].symbol, "a string"); |
|
EXPECT(tokens[0].type == ruc::json::Token::Type::String); |
|
|
|
// Array |
|
|
|
tokens = lex("[]"); |
|
EXPECT_EQ(tokens.size(), 2); |
|
EXPECT_EQ(tokens[0].symbol, "["); |
|
EXPECT_EQ(tokens[1].symbol, "]"); |
|
|
|
tokens = lex("[\n\n\n]"); |
|
EXPECT_EQ(tokens.size(), 2); |
|
EXPECT_EQ(tokens[0].symbol, "["); |
|
EXPECT_EQ(tokens[1].symbol, "]"); |
|
|
|
// Object |
|
|
|
tokens = lex("{}"); |
|
EXPECT_EQ(tokens.size(), 2); |
|
EXPECT_EQ(tokens[0].symbol, "{"); |
|
EXPECT_EQ(tokens[1].symbol, "}"); |
|
|
|
tokens = lex("{\n\n\n}"); |
|
EXPECT_EQ(tokens.size(), 2); |
|
EXPECT_EQ(tokens[0].symbol, "{"); |
|
EXPECT_EQ(tokens[1].symbol, "}"); |
|
} |
|
|
|
TEST_CASE(JsonParser) |
|
{ |
|
ruc::Json json; |
|
|
|
json = parse("null"); |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = parse("true"); |
|
EXPECT_EQ(json.size(), 1); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Bool); |
|
|
|
json = parse("false"); |
|
EXPECT_EQ(json.size(), 1); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Bool); |
|
|
|
json = parse("3.14"); |
|
EXPECT_EQ(json.size(), 1); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Number); |
|
|
|
json = parse(R"("a string")"); |
|
EXPECT_EQ(json.size(), 1); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::String); |
|
|
|
// Array |
|
|
|
json = parse("["); |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = parse("[ 123"); |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = parse("[ 123,"); |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = parse("[ 123, ]"); |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = parse("[ 123 456 ]"); |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = parse("[]"); |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Array); |
|
|
|
json = parse(R"([ "element", 3.14 ])"); |
|
EXPECT_EQ(json.size(), 2); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Array); |
|
|
|
// Object |
|
|
|
json = parse("{"); |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = parse(R"({ "name")"); |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = parse(R"({ "name":)"); |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = parse(R"({ "name":,)"); |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = parse(R"({ "name":"value")"); |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = parse(R"({ "name":"value",)"); |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = parse(R"({ "name":"value", })"); |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = parse(R"({ "name" "value" })"); |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = parse(R"({ 123 })"); |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = parse("{}"); |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Object); |
|
|
|
json = parse(R"({ "name": "value", "name2": 3.14 })"); |
|
EXPECT_EQ(json.size(), 2); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Object); |
|
|
|
// Multiple root elements |
|
|
|
json = parse("54 false"); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = parse("3.14, 666"); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = parse("true\nfalse"); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
} |
|
|
|
TEST_CASE(JsonToJsonValue) |
|
{ |
|
ruc::Json json; |
|
|
|
json = {}; |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = nullptr; |
|
EXPECT_EQ(json.size(), 0); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Null); |
|
|
|
json = true; |
|
EXPECT_EQ(json.size(), 1); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Bool); |
|
|
|
json = false; |
|
EXPECT_EQ(json.size(), 1); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Bool); |
|
|
|
json = 666; |
|
EXPECT_EQ(json.size(), 1); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Number); |
|
|
|
json = 3.14; |
|
EXPECT_EQ(json.size(), 1); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Number); |
|
|
|
const char* characters = "my string"; |
|
json = characters; |
|
EXPECT_EQ(json.size(), 1); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::String); |
|
|
|
std::string string = "my string"; |
|
json = string; |
|
EXPECT_EQ(json.size(), 1); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::String); |
|
|
|
// Nested Array with multiple types |
|
json = { "element", 3.14, true, nullptr, { "nested element", { "more nesting", { 1, 2, 3, "yes" } } } }; |
|
EXPECT_EQ(json.size(), 5); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Array); |
|
|
|
// Nested Object with multiple types |
|
json = { { "name", "value" }, { "name2", 3.14 }, { "name3", true }, { "name4", nullptr }, { "name5", { { "nested name", "value" } } } }; |
|
EXPECT_EQ(json.size(), 5); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Object); |
|
|
|
// Array with singular type |
|
std::vector<std::string> vector = { "element", "element2", "element3" }; |
|
json = vector; |
|
EXPECT_EQ(json.size(), 3); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Array); |
|
|
|
// Object with singular type |
|
std::map<std::string, std::string> map = { { "name", "value" }, { "name2", "value2" } }; |
|
json = map; |
|
EXPECT_EQ(json.size(), 2); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Object); |
|
|
|
// Object with singular type |
|
std::unordered_map<std::string, std::string> unorderedMap = { { "name", "value" }, { "name2", "value2" } }; |
|
json = unorderedMap; |
|
EXPECT_EQ(json.size(), 2); |
|
EXPECT_EQ(json.type(), ruc::Json::Type::Object); |
|
} |
|
|
|
TEST_CASE(JsonFromJsonValue) |
|
{ |
|
ruc::Json json; |
|
|
|
json = nullptr; |
|
EXPECT_EQ(json.get<std::nullptr_t>(), nullptr); |
|
|
|
json = true; |
|
EXPECT_EQ(json.get<bool>(), true); |
|
|
|
json = false; |
|
EXPECT_EQ(json.get<bool>(), false); |
|
|
|
json = 666; |
|
EXPECT_EQ(json.get<int>(), 666); |
|
|
|
json = 3.14; |
|
EXPECT_EQ(json.get<double>(), 3.14); |
|
|
|
std::string string; |
|
json = "my string"; |
|
json.getTo(string); |
|
EXPECT_EQ(string, "my string"); |
|
EXPECT_EQ(json.get<std::string>(), "my string"); |
|
|
|
// Array with singular type |
|
json = { "element", "element2" }; |
|
EXPECT_EQ(json[0].get<std::string>(), "element"); |
|
EXPECT_EQ(json.at(1).get<std::string>(), "element2"); |
|
auto array = json.get<std::vector<std::string>>(); |
|
EXPECT_EQ(array.size(), 2); |
|
EXPECT_EQ(array[0], "element"); |
|
EXPECT_EQ(array[1], "element2"); |
|
|
|
// Array with multiple types |
|
json = { "string", 3.14, true, nullptr }; |
|
EXPECT_EQ(json[0].get<std::string>(), "string"); |
|
EXPECT_EQ(json.at(1).get<double>(), 3.14); |
|
EXPECT_EQ(json[2].get<bool>(), true); |
|
EXPECT_EQ(json[3].get<std::nullptr_t>(), nullptr); |
|
auto valueArray = json.get<std::vector<ruc::Json>>(); |
|
EXPECT_EQ(valueArray.size(), 4); |
|
EXPECT_EQ(valueArray[0].get<std::string>(), "string"); |
|
EXPECT_EQ(valueArray[1].get<double>(), 3.14); |
|
EXPECT_EQ(valueArray[2].get<bool>(), true); |
|
EXPECT_EQ(valueArray[3].get<std::nullptr_t>(), nullptr); |
|
|
|
// Nested Array with multiple types |
|
json = { |
|
"value", |
|
{ |
|
"thing", |
|
666, |
|
}, |
|
{ |
|
{ |
|
3.14, |
|
}, |
|
} |
|
}; |
|
EXPECT_EQ(json[0].get<std::string>(), "value"); |
|
EXPECT_EQ(json.at(1)[0].get<std::string>(), "thing"); |
|
EXPECT_EQ(json[1].at(1).get<int>(), 666); |
|
EXPECT_EQ(json[2][0][0].get<double>(), 3.14); |
|
|
|
// Object with singular type |
|
json = { { "name", "value" }, { "name2", "value2" } }; |
|
EXPECT_EQ(json["name"].get<std::string>(), "value"); |
|
EXPECT_EQ(json.at("name2").get<std::string>(), "value2"); |
|
auto object = json.get<std::map<std::string, std::string>>(); |
|
EXPECT_EQ(object.size(), 2); |
|
EXPECT_EQ(object["name"], "value"); |
|
EXPECT_EQ(object["name2"], "value2"); |
|
auto unorderedObject = json.get<std::unordered_map<std::string, std::string>>(); |
|
EXPECT_EQ(unorderedObject.size(), 2); |
|
EXPECT_EQ(unorderedObject["name"], "value"); |
|
EXPECT_EQ(unorderedObject["name2"], "value2"); |
|
|
|
// Object with multiple types |
|
json = { { "name", "value" }, { "name2", 3.14 }, { "name3", true }, { "name4", nullptr } }; |
|
EXPECT_EQ(json["name"].get<std::string>(), "value"); |
|
EXPECT_EQ(json.at("name2").get<double>(), 3.14); |
|
EXPECT_EQ(json["name3"].get<bool>(), true); |
|
EXPECT_EQ(json["name4"].get<std::nullptr_t>(), nullptr); |
|
auto valueObject = json.get<std::map<std::string, ruc::Json>>(); |
|
EXPECT_EQ(valueObject.size(), 4); |
|
EXPECT_EQ(valueObject["name"].get<std::string>(), "value"); |
|
EXPECT_EQ(valueObject["name2"].get<double>(), 3.14); |
|
EXPECT_EQ(valueObject["name3"].get<bool>(), true); |
|
EXPECT_EQ(valueObject["name4"].get<std::nullptr_t>(), nullptr); |
|
|
|
// Nested Object with multiple types |
|
json = { |
|
{ |
|
"name", |
|
"value", |
|
}, |
|
{ |
|
"nest 1-deep", |
|
{ { |
|
"number", |
|
1, |
|
} }, |
|
}, |
|
{ |
|
"nest 2-deep", |
|
{ { |
|
"nest 1-deep", |
|
{ { |
|
"bool", |
|
true, |
|
} }, |
|
} }, |
|
}, |
|
}; |
|
EXPECT_EQ(json["name"].get<std::string>(), "value"); |
|
EXPECT_EQ(json["nest 1-deep"]["number"].get<int>(), 1); |
|
EXPECT_EQ(json["nest 2-deep"]["nest 1-deep"]["bool"].get<bool>(), true); |
|
} |
|
|
|
TEST_CASE(JsonImplicitConversion) |
|
{ |
|
ruc::Json array; |
|
array[0]; |
|
EXPECT_EQ(array.type(), ruc::Json::Type::Array); |
|
|
|
ruc::Json arrayEmplace; |
|
arrayEmplace.emplace_back("element"); |
|
arrayEmplace.emplace_back({ "nested element" }); |
|
EXPECT_EQ(arrayEmplace.type(), ruc::Json::Type::Array); |
|
EXPECT_EQ(arrayEmplace[1].type(), ruc::Json::Type::Array); |
|
|
|
ruc::Json object; |
|
object[""]; |
|
EXPECT_EQ(object.type(), ruc::Json::Type::Object); |
|
|
|
ruc::Json objectEmplace; |
|
objectEmplace.emplace("name", "value"); |
|
objectEmplace.emplace("name2", { { "nested name", "value" } }); |
|
EXPECT_EQ(objectEmplace.type(), ruc::Json::Type::Object); |
|
EXPECT_EQ(objectEmplace["name2"].type(), ruc::Json::Type::Object); |
|
} |
|
|
|
TEST_CASE(JsonSerializer) |
|
{ |
|
EXPECT_EQ(serialize(""), "null"); |
|
EXPECT_EQ(serialize("null"), "null"); |
|
EXPECT_EQ(serialize("true"), "true"); |
|
EXPECT_EQ(serialize("false"), "false"); |
|
EXPECT_EQ(serialize("3.14"), "3.14"); |
|
EXPECT_EQ(serialize(R"("string")"), R"("string")"); |
|
|
|
EXPECT_EQ(serialize("\n\n\n"), "null"); |
|
EXPECT_EQ(serialize("null\n"), "null"); |
|
EXPECT_EQ(serialize("true\n"), "true"); |
|
EXPECT_EQ(serialize("false\n"), "false"); |
|
EXPECT_EQ(serialize("3.14\n"), "3.14"); |
|
// clang-format off |
|
EXPECT_EQ(serialize(R"("string")" "\n"), R"("string")"); |
|
// clang-format on |
|
|
|
EXPECT_EQ(serialize("[\n\n\n]"), "[]"); |
|
EXPECT_EQ(serialize("[null]"), "[null]"); |
|
EXPECT_EQ(serialize("[true]"), "[true]"); |
|
EXPECT_EQ(serialize("[false]"), "[false]"); |
|
EXPECT_EQ(serialize("[3.14]"), "[3.14]"); |
|
EXPECT_EQ(serialize(R"(["string"])"), R"(["string"])"); |
|
|
|
EXPECT_EQ(serialize("[\n\n\n]", 4), "[\n]"); |
|
EXPECT_EQ(serialize("[null]", 4), "[\n null\n]"); |
|
EXPECT_EQ(serialize("[true]", 4), "[\n true\n]"); |
|
EXPECT_EQ(serialize("[false]", 4), "[\n false\n]"); |
|
EXPECT_EQ(serialize("[3.14]", 4), "[\n 3.14\n]"); |
|
// clang-format off |
|
EXPECT_EQ(serialize(R"(["string"])", 4), "[\n " R"("string")" "\n]"); |
|
// clang-format on |
|
|
|
// Check for trailing comma on last array element |
|
EXPECT_EQ(serialize(R"([1])"), R"([1])"); |
|
EXPECT_EQ(serialize(R"([1,2])"), R"([1,2])"); |
|
EXPECT_EQ(serialize(R"([1,2,3])"), R"([1,2,3])"); |
|
|
|
// Check for trailing comma on last object member |
|
EXPECT_EQ(serialize(R"({"n1":"v1"})"), R"({"n1":"v1"})"); |
|
EXPECT_EQ(serialize(R"({"n1":"v1", "n2":"v2"})"), R"({"n1":"v1","n2":"v2"})"); |
|
EXPECT_EQ(serialize(R"({"n1":"v1", "n2":"v2", "n3":"v3"})"), R"({"n1":"v1","n2":"v2","n3":"v3"})"); |
|
|
|
// clang-format off |
|
EXPECT_EQ(serialize(R"({ |
|
"object member one": [ |
|
"array element one" |
|
], |
|
"object member two": [ |
|
"array element one", |
|
"array element two" |
|
], |
|
"object member three": [ |
|
"array element one", |
|
2, |
|
3.0, |
|
4.56, |
|
true, |
|
false, |
|
null |
|
], |
|
"object member four": 3.14, |
|
"object member five": "value five", |
|
"object member six": null, |
|
"object member seven": { "no": 0 } |
|
})", 4), R"({ |
|
"object member five": "value five", |
|
"object member four": 3.14, |
|
"object member one": [ |
|
"array element one" |
|
], |
|
"object member seven": { |
|
"no": 0 |
|
}, |
|
"object member six": null, |
|
"object member three": [ |
|
"array element one", |
|
2, |
|
3, |
|
4.56, |
|
true, |
|
false, |
|
null |
|
], |
|
"object member two": [ |
|
"array element one", |
|
"array element two" |
|
] |
|
})"); |
|
// clang-format on |
|
}
|
|
|