blob: 9c619716c2eaaef323c64e8f7db0757745e07bcc [file] [edit]
// Copyright 2025 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "json_parser.h"
#include "json.h"
#include "test.h"
namespace {
const char* const kWhitespacePrefixes[] = {
"", " ", "\t\t", "\n", "\r\n\t ",
};
} // namespace
TEST(JsonParserTest, PeekToken) {
const struct {
const char* input;
JsonParser::TokenType expected_type;
StringPiece expected_token;
} kTestCases[] = {
{ "", JsonParser::kEndOfInput, "" },
{ "true", JsonParser::kTrue, "true" },
{ "true", JsonParser::kTrue, "true" },
{ "true", JsonParser::kTrue, "true" },
{ "false", JsonParser::kFalse, "false" },
{ "null", JsonParser::kNull, "null" },
{ "{", JsonParser::kObjectStart, "{" },
{ "{...", JsonParser::kObjectStart, "{" },
{ "}...", JsonParser::kObjectEnd, "}" },
{ "[...", JsonParser::kArrayStart, "[" },
{ "]...", JsonParser::kArrayEnd, "]" },
{ ",]", JsonParser::kComma, "," },
{ ":", JsonParser::kColon, ":" },
{ "\"foo\"", JsonParser::kString, "foo" },
{ "\"foo\\u0020bar\"", JsonParser::kString, "foo\\u0020bar" },
{ "!", JsonParser::kParsingError, "" },
};
for (const char* whitespace_prefix : kWhitespacePrefixes) {
for (const auto& test_case : kTestCases) {
std::string input = std::string(whitespace_prefix) + test_case.input;
JsonParser parser(input);
std::string err_message = "For input [" + input + "]";
EXPECT_EQ(parser.PeekTokenType(), test_case.expected_type) << err_message;
EXPECT_EQ(parser.PeekToken(), test_case.expected_token) << err_message;
}
}
}
TEST(JsonParserTest, Array) {
static const char kInput[] =
" [ \"some\\n\", \"newline\\n\", true, { \"foo\": [ \"bar\" ] } ]";
JsonParser parser(kInput);
ASSERT_TRUE(parser.ExpectArrayStart());
StringPiece str_value;
ASSERT_TRUE(parser.ExpectEncodedString(&str_value));
EXPECT_EQ(str_value, "some\\n");
ASSERT_TRUE(parser.ExpectComma());
std::string str;
ASSERT_TRUE(parser.ExpectString(&str));
EXPECT_EQ(str, "newline\n");
ASSERT_TRUE(parser.ExpectComma());
bool bool_value = false;
ASSERT_TRUE(parser.ExpectBoolean(&bool_value));
EXPECT_TRUE(bool_value);
ASSERT_TRUE(parser.ExpectComma());
ASSERT_TRUE(parser.ExpectObjectStart());
ASSERT_TRUE(parser.ExpectEncodedString(&str_value));
EXPECT_EQ(str_value, "foo");
ASSERT_TRUE(parser.ExpectColon());
ASSERT_TRUE(parser.ExpectArrayStart());
ASSERT_TRUE(parser.ExpectEncodedString(&str_value));
EXPECT_EQ(str_value, "bar");
ASSERT_TRUE(parser.ExpectArrayEnd());
ASSERT_TRUE(parser.ExpectObjectEnd());
ASSERT_TRUE(parser.ExpectArrayEnd());
}
TEST(JsonParserTest, SkipJsonValue) {
static const char kInput[] =
" [ \"some\", \"array\", true, { \"foo\": [ \"bar\" ] } ]";
JsonParser parser(kInput);
ASSERT_TRUE(parser.ExpectArrayStart());
ASSERT_EQ(parser.PeekTokenType(), parser.kString);
ASSERT_TRUE(parser.SkipJsonValue());
ASSERT_TRUE(parser.ExpectComma());
ASSERT_EQ(parser.PeekTokenType(), parser.kString);
ASSERT_TRUE(parser.SkipJsonValue());
ASSERT_TRUE(parser.ExpectComma());
ASSERT_EQ(parser.PeekTokenType(), parser.kTrue);
ASSERT_TRUE(parser.SkipJsonValue());
ASSERT_TRUE(parser.ExpectComma());
ASSERT_EQ(parser.PeekTokenType(), parser.kObjectStart);
ASSERT_TRUE(parser.SkipJsonValue());
ASSERT_TRUE(parser.ExpectArrayEnd());
}
TEST(JsonParserTest, ParseJsonValue) {
static const char kInput[] =
" [ \"some\", \"array\", true, { \"foo\": [ \"bar\" ] } ]";
JsonParser parser(kInput);
JsonParser::ValueInfo vi;
ASSERT_TRUE(parser.ExpectArrayStart());
ASSERT_TRUE(parser.ParseJsonValue(&vi));
EXPECT_EQ(vi.type, JsonParser::kString);
EXPECT_EQ(vi.value, "some");
ASSERT_TRUE(parser.ExpectComma());
ASSERT_TRUE(parser.ParseJsonValue(&vi));
EXPECT_EQ(vi.type, JsonParser::kString);
EXPECT_EQ(vi.value, "array");
ASSERT_TRUE(parser.ExpectComma());
ASSERT_TRUE(parser.ParseJsonValue(&vi));
EXPECT_EQ(vi.type, JsonParser::kTrue);
EXPECT_EQ(vi.value, "true");
ASSERT_TRUE(parser.ExpectComma());
ASSERT_TRUE(parser.ParseJsonValue(&vi));
EXPECT_EQ(vi.type, JsonParser::kObjectStart);
EXPECT_EQ(vi.value, "{ \"foo\": [ \"bar\" ] }");
ASSERT_TRUE(parser.ExpectArrayEnd());
}
TEST(JsonParserTest, EmptyArrayIterator) {
JsonParser parser("[]");
JsonParser::ArrayIterator it(parser);
ASSERT_EQ(parser.PeekTokenType(), parser.kArrayEnd);
ASSERT_FALSE(parser.HasError());
ASSERT_FALSE(it.HasItem());
ASSERT_FALSE(parser.HasError());
ASSERT_EQ(parser.PeekTokenType(), parser.kEndOfInput);
}
TEST(JsonParserTest, ExpectStringList) {
// Test with a valid string list.
{
JsonParser parser(R"([ "a", "b", "c" ])");
std::vector<std::string> list;
ASSERT_TRUE(parser.ExpectStringList(&list));
ASSERT_EQ(list.size(), 3u);
EXPECT_EQ(list[0], "a");
EXPECT_EQ(list[1], "b");
EXPECT_EQ(list[2], "c");
}
// Test with an empty list.
{
JsonParser parser("[]");
std::vector<std::string> list;
ASSERT_TRUE(parser.ExpectStringList(&list));
EXPECT_TRUE(list.empty());
}
// Test with a list containing non-string elements.
{
JsonParser parser(R"([ "a", 1, "c" ])");
std::vector<std::string> list;
ASSERT_FALSE(parser.ExpectStringList(&list));
EXPECT_TRUE(parser.HasError());
}
// Test with malformed JSON.
{
JsonParser parser("[");
std::vector<std::string> list;
ASSERT_FALSE(parser.ExpectStringList(&list));
EXPECT_TRUE(parser.HasError());
}
}
TEST(JsonParserTest, ArrayIterator) {
static const char kInput[] =
" [ \"some\", \"array\", true, { \"foo\": [ \"bar\" ] } ]";
JsonParser parser(kInput);
JsonParser::ArrayIterator it(parser);
ASSERT_TRUE(it.HasItem());
StringPiece str_value;
ASSERT_TRUE(parser.ExpectEncodedString(&str_value));
EXPECT_EQ(str_value, "some");
ASSERT_TRUE(it.HasItem());
ASSERT_TRUE(parser.ExpectEncodedString(&str_value));
EXPECT_EQ(str_value, "array");
ASSERT_TRUE(it.HasItem());
bool bool_value = false;
ASSERT_TRUE(parser.ExpectBoolean(&bool_value));
EXPECT_TRUE(bool_value);
ASSERT_TRUE(it.HasItem());
ASSERT_EQ(parser.PeekTokenType(), parser.kObjectStart);
ASSERT_TRUE(parser.SkipJsonValue());
ASSERT_FALSE(it.HasItem());
ASSERT_FALSE(parser.HasError());
ASSERT_EQ(parser.PeekTokenType(), parser.kEndOfInput);
}
TEST(JsonParserTest, EmptyObjecttIterator) {
JsonParser parser("{}");
JsonParser::ObjectIterator it(parser);
ASSERT_FALSE(parser.HasError());
ASSERT_EQ(parser.PeekTokenType(), parser.kObjectEnd);
ASSERT_FALSE(it.HasKeyValue());
ASSERT_EQ(parser.PeekTokenType(), parser.kEndOfInput);
}
TEST(JsonParserTest, ObjectIterator) {
static const char kInput[] =
"{ \"a\" : \"AA\",\n \"b\" : [ 1, 2, 3 ], \"c\": { }, \"d\": false }";
JsonParser parser(kInput);
JsonParser::ObjectIterator it(parser);
ASSERT_TRUE(it.HasKeyValue());
ASSERT_EQ(it.key(), "a");
StringPiece str_value;
ASSERT_TRUE(parser.ExpectEncodedString(&str_value));
EXPECT_EQ(str_value, "AA");
ASSERT_TRUE(it.HasKeyValue());
ASSERT_EQ(it.key(), "b");
ASSERT_EQ(parser.PeekTokenType(), parser.kArrayStart);
ASSERT_TRUE(parser.SkipJsonValue());
ASSERT_TRUE(it.HasKeyValue());
ASSERT_EQ(it.key(), "c");
ASSERT_EQ(parser.PeekTokenType(), parser.kObjectStart);
ASSERT_TRUE(parser.SkipJsonValue());
ASSERT_TRUE(it.HasKeyValue());
ASSERT_EQ(it.key(), "d");
bool bool_value = true;
ASSERT_TRUE(parser.ExpectBoolean(&bool_value));
EXPECT_FALSE(bool_value);
ASSERT_FALSE(it.HasKeyValue());
ASSERT_FALSE(parser.HasError());
}
TEST(JsonParserTest, ParseObject) {
// Input value is not an object is an error.
{
JsonParser parser("[]");
JsonParser::KeyValueMap kv;
ASSERT_FALSE(parser.ParseObject(&kv));
EXPECT_EQ(parser.error(), "expecting object start");
}
// An empty object is not an error.
{
JsonParser parser("{}");
JsonParser::KeyValueMap kv;
ASSERT_TRUE(parser.ParseObject(&kv));
EXPECT_TRUE(kv.empty());
}
// A single key value pair.
{
JsonParser parser(" { \"foo\": [ \"bar\" ] } ");
JsonParser::KeyValueMap kv;
ASSERT_TRUE(parser.ParseObject(&kv));
EXPECT_EQ(kv.size(), 1u);
auto it = kv.begin();
EXPECT_EQ(it->first, "foo");
EXPECT_EQ(it->second.type, JsonParser::kArrayStart);
EXPECT_EQ(it->second.value, "[ \"bar\" ]");
}
// Several key value pairs
{
JsonParser parser(" { \"foo\": [ \"bar\" ], \"zoo\": { \"a\": 42 } }");
JsonParser::KeyValueMap kv;
ASSERT_TRUE(parser.ParseObject(&kv));
EXPECT_EQ(kv.size(), 2u);
JsonParser::ValueInfo vi;
ASSERT_TRUE(kv.Get("foo", &vi));
EXPECT_EQ(vi.type, JsonParser::kArrayStart);
EXPECT_EQ(vi.value, "[ \"bar\" ]");
ASSERT_TRUE(kv.Get("zoo", &vi));
EXPECT_EQ(vi.type, JsonParser::kObjectStart);
EXPECT_EQ(vi.value, "{ \"a\": 42 }");
EXPECT_FALSE(kv.Get("bar", &vi));
}
// Malformed objects.
const char* kMalformedInputs[] = {
"{",
"}",
"{ \"a\" }",
"{ \"a\": }",
"{ : 1 }",
"{ \"a\": 1, }",
"{ \"a\": 1, , \"b\": 2 }",
"{ \"a\" 1 }",
};
for (const char* input : kMalformedInputs) {
JsonParser parser(input);
JsonParser::KeyValueMap kv;
ASSERT_FALSE(parser.ParseObject(&kv)) << "input: " << input;
EXPECT_TRUE(parser.HasError()) << "input: " << input;
}
// Duplicate keys. Last one wins.
{
JsonParser parser("{ \"a\": 1, \"b\": 2, \"a\": 3 }");
JsonParser::KeyValueMap kv;
ASSERT_TRUE(parser.ParseObject(&kv));
EXPECT_EQ(kv.size(), 2u);
JsonParser::ValueInfo vi;
ASSERT_TRUE(kv.Get("a", &vi));
EXPECT_EQ(vi.type, JsonParser::kNumber);
EXPECT_EQ(vi.value, "3");
ASSERT_TRUE(kv.Get("b", &vi));
EXPECT_EQ(vi.type, JsonParser::kNumber);
EXPECT_EQ(vi.value, "2");
}
}
TEST(JsonParserTest, KeyValueMap) {
const char kInput[] = R"({
"s": "string value",
"i": 123,
"b": true,
"n": null,
"a": [ "foo", "bar" ],
"o": { "x": 1 }
})";
JsonParser parser(kInput);
JsonParser::KeyValueMap kv;
ASSERT_TRUE(parser.ParseObject(&kv));
ASSERT_FALSE(parser.HasError());
JsonParser::ValueInfo vi;
EXPECT_TRUE(kv.Get("s", &vi));
EXPECT_EQ(vi.type, JsonParser::kString);
EXPECT_FALSE(kv.Get("missing", &vi));
EXPECT_TRUE(kv.GetRequired("s", &vi, parser));
EXPECT_FALSE(kv.GetRequired("missing", &vi, parser));
EXPECT_EQ(parser.error(), "Missing required key 'missing'");
parser.Reset(kInput);
EXPECT_TRUE(kv.GetRequiredExpected("s", JsonParser::kString, &vi, parser));
EXPECT_FALSE(kv.GetRequiredExpected("s", JsonParser::kNumber, &vi, parser));
EXPECT_EQ(parser.error(), "Invalid type for key 's', expected NUMBER");
parser.Reset(kInput);
int64_t int_val;
EXPECT_TRUE(kv.GetRequiredInteger("i", &int_val, parser));
EXPECT_EQ(int_val, 123);
EXPECT_FALSE(kv.GetRequiredInteger("s", &int_val, parser));
EXPECT_EQ(parser.error(), "Invalid type for key 's', expected NUMBER");
parser.Reset(kInput);
std::string str_val;
EXPECT_TRUE(kv.GetRequiredString("s", &str_val, parser));
EXPECT_EQ(str_val, "string value");
EXPECT_FALSE(kv.GetRequiredString("i", &str_val, parser));
EXPECT_EQ(parser.error(), "Invalid type for key 'i', expected STRING");
parser.Reset(kInput);
std::vector<std::string> list_val;
EXPECT_TRUE(kv.GetRequiredStringList("a", &list_val, parser));
EXPECT_EQ(list_val.size(), 2u);
EXPECT_EQ(list_val[0], "foo");
EXPECT_EQ(list_val[1], "bar");
EXPECT_FALSE(kv.GetRequiredStringList("s", &list_val, parser));
EXPECT_EQ(parser.error(), "Invalid type for key 's', expected ARRAY_START");
parser.Reset(kInput);
}
TEST(JsonParserTest, JsonInputStringInfo) {
{
StringPiece input("missing starting quote\"");
auto info = JsonParser::ParseJsonInputString(input.begin(), input.end());
EXPECT_EQ(info.start, input.begin());
EXPECT_EQ(info.end, input.end());
EXPECT_TRUE(info.error);
}
{
StringPiece input("\"missing closing quote");
auto info = JsonParser::ParseJsonInputString(input.begin(), input.end());
EXPECT_EQ(info.start, input.begin() + 1);
EXPECT_EQ(info.end, input.end());
EXPECT_TRUE(info.error);
}
{
StringPiece input("\"simple string without escapes\" extra");
auto info = JsonParser::ParseJsonInputString(input.begin(), input.end());
EXPECT_EQ(info.start, input.begin() + 1);
EXPECT_EQ(info.end, input.end() - 7u);
EXPECT_FALSE(info.error);
}
{
StringPiece input("\"string with escaped quote \\\" here\" extra");
auto info = JsonParser::ParseJsonInputString(input.begin(), input.end());
EXPECT_EQ(info.start, input.begin() + 1);
EXPECT_EQ(info.end, input.end() - 7u);
EXPECT_FALSE(info.error);
}
{
StringPiece input("\"string with escaped backslash \\\\\" extra");
auto info = JsonParser::ParseJsonInputString(input.begin(), input.end());
EXPECT_EQ(info.start, input.begin() + 1);
EXPECT_EQ(info.end, input.end() - 7u);
EXPECT_FALSE(info.error);
}
{
StringPiece input("\"string with unicode escape \\u0022\" extra");
auto info = JsonParser::ParseJsonInputString(input.begin(), input.end());
EXPECT_EQ(info.start, input.begin() + 1);
EXPECT_EQ(info.end, input.end() - 7u);
EXPECT_FALSE(info.error);
}
{
StringPiece input(
"\"string with unicode escape (surrogate pair) \\uD800\\uDC00\" extra");
auto info = JsonParser::ParseJsonInputString(input.begin(), input.end());
EXPECT_EQ(info.start, input.begin() + 1);
EXPECT_EQ(info.end, input.end() - 7u);
EXPECT_FALSE(info.error);
}
{
StringPiece input(
"\"string with misc escapes \\b\\f\\r\\n\\t\\\\\\\"\\u0022\" extra");
auto info = JsonParser::ParseJsonInputString(input.begin(), input.end());
EXPECT_EQ(info.start, input.begin() + 1);
EXPECT_EQ(info.end, input.end() - 7u);
EXPECT_FALSE(info.error);
}
{
StringPiece input("\"broken unicode escape 1 \\u022 \"");
auto info = JsonParser::ParseJsonInputString(input.begin(), input.end());
EXPECT_EQ(info.start, input.begin() + 1);
EXPECT_EQ(info.end, input.end() - 7u);
EXPECT_TRUE(info.error);
}
{
StringPiece input("\"broken unicode escape 2 \\u22 \"");
auto info = JsonParser::ParseJsonInputString(input.begin(), input.end());
EXPECT_EQ(info.start, input.begin() + 1);
EXPECT_EQ(info.end, input.end() - 6u);
EXPECT_TRUE(info.error);
}
{
StringPiece input("\"broken unicode escape 3 \\u \"");
auto info = JsonParser::ParseJsonInputString(input.begin(), input.end());
EXPECT_EQ(info.start, input.begin() + 1);
EXPECT_EQ(info.end, input.end() - 4u);
EXPECT_TRUE(info.error);
}
{
StringPiece input("\"broken unicode escape 4 \\uX022\"");
auto info = JsonParser::ParseJsonInputString(input.begin(), input.end());
EXPECT_EQ(info.start, input.begin() + 1);
EXPECT_EQ(info.end, input.end() - 7u);
EXPECT_TRUE(info.error);
}
{
StringPiece input(
"\"broken unicode escape 5 (surrogate pair start without following "
"end) "
"\\uDC00\"");
auto info = JsonParser::ParseJsonInputString(input.begin(), input.end());
EXPECT_EQ(info.start, input.begin() + 1);
EXPECT_EQ(info.end, input.end() - 7u);
EXPECT_TRUE(info.error);
}
{
StringPiece input(
"\"broken unicode escape 6 (surrogate pair end doubled) "
"\\uDC00\\uDC00\"");
auto info = JsonParser::ParseJsonInputString(input.begin(), input.end());
EXPECT_EQ(info.start, input.begin() + 1);
EXPECT_EQ(info.end, input.end() - 13u);
EXPECT_TRUE(info.error);
}
}
TEST(JsonParserTest, DecodeString) {
static const struct {
const char* input;
const char* expected;
} kTestCases[] = {
{ "", "" },
{ "foo bar", "foo bar" },
{ "\\\"hello\\\"", "\"hello\"" },
{ "hello\\nworld\\r\\n", "hello\nworld\r\n" },
{ "single\\bchar\\fescapes\\t", "single\bchar\fescapes\t" },
{ "\u0022hello\u0020world\u002f\u0022", "\"hello world/\"" },
{ "a \\ud800\\udc00 b", "a \xf4\x80\x80\x80 b" },
};
for (const auto& test_case : kTestCases) {
std::string actual = test_case.input;
std::string got = JsonParser::DecodeString(actual);
EXPECT_EQ(got, test_case.expected) << "For input " << actual;
}
}