blob: 5c5413054ac6d9c8b5f56817a4a3e55bd36c80f7 [file]
/*
* Copyright (C) 2024 The Android Open Source Project
*
* 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 "src/trace_processor/util/json_parser.h"
#include <string>
#include <string_view>
#include "perfetto/base/status.h"
#include "test/gtest_and_gmock.h"
namespace perfetto::trace_processor::json {
namespace {
using ::testing::ElementsAre;
using ::testing::IsEmpty;
class JsonParserTest : public ::testing::Test {
protected:
void Parse(std::string_view str,
JsonValue& value,
std::string& unescaped_str,
bool expect_ok = true) {
const char* begin = str.data();
const char* end = begin + str.size();
const char* cur = begin;
base::Status status;
if (!internal::SkipWhitespace(cur, end)) {
return;
}
auto res = ParseValue(cur, end, value, unescaped_str, status);
if (expect_ok) {
EXPECT_EQ(res, ReturnCode::kOk);
EXPECT_TRUE(status.ok()) << status.message();
} else {
EXPECT_NE(res, ReturnCode::kOk);
EXPECT_FALSE(status.ok());
}
}
};
TEST_F(JsonParserTest, ParseNull) {
constexpr std::string_view kJson = "null";
JsonValue value;
std::string str;
Parse(kJson, value, str);
EXPECT_TRUE(std::holds_alternative<Null>(value));
}
TEST_F(JsonParserTest, ParseTrue) {
constexpr std::string_view kJson = "true";
JsonValue value;
std::string str;
Parse(kJson, value, str);
ASSERT_TRUE(std::holds_alternative<bool>(value));
EXPECT_TRUE(std::get<bool>(value));
}
TEST_F(JsonParserTest, ParseFalse) {
constexpr std::string_view kJson = "false";
JsonValue value;
std::string str;
Parse(kJson, value, str);
ASSERT_TRUE(std::holds_alternative<bool>(value));
EXPECT_FALSE(std::get<bool>(value));
}
TEST_F(JsonParserTest, ParseInteger) {
constexpr std::string_view kJson = "12345,";
JsonValue value;
std::string str;
Parse(kJson, value, str);
ASSERT_TRUE(std::holds_alternative<int64_t>(value));
EXPECT_EQ(std::get<int64_t>(value), 12345);
}
TEST_F(JsonParserTest, ParseNegativeInteger) {
constexpr std::string_view kJson = "-12345,";
JsonValue value;
std::string str;
Parse(kJson, value, str);
ASSERT_TRUE(std::holds_alternative<int64_t>(value));
EXPECT_EQ(std::get<int64_t>(value), -12345);
}
TEST_F(JsonParserTest, ParseDouble) {
constexpr std::string_view kJson = "123.45,";
JsonValue value;
std::string str;
Parse(kJson, value, str);
ASSERT_TRUE(std::holds_alternative<double>(value));
EXPECT_DOUBLE_EQ(std::get<double>(value), 123.45);
}
TEST_F(JsonParserTest, ParseLargeDouble) {
constexpr std::string_view kJson = "1750244461563845.0,";
JsonValue value;
std::string str;
Parse(kJson, value, str);
ASSERT_TRUE(std::holds_alternative<double>(value));
EXPECT_DOUBLE_EQ(std::get<double>(value), 1750244461563845.0);
}
TEST_F(JsonParserTest, ParseString) {
constexpr std::string_view kJson = "\"hello world\"";
JsonValue value;
std::string str;
Parse(kJson, value, str);
ASSERT_TRUE(std::holds_alternative<std::string_view>(value));
EXPECT_EQ(std::get<std::string_view>(value), "hello world");
}
TEST_F(JsonParserTest, ParseStringWithEscapes) {
constexpr std::string_view kJson = "\"hello \\\"world\\\"\"";
JsonValue value;
std::string str;
Parse(kJson, value, str);
ASSERT_TRUE(std::holds_alternative<std::string_view>(value));
EXPECT_EQ(std::get<std::string_view>(value), "hello \"world\"");
}
TEST_F(JsonParserTest, ParseStringEndingWithBackslash) {
constexpr std::string_view kJson = "\"value\\\\\"";
JsonValue value;
std::string str;
Parse(kJson, value, str);
ASSERT_TRUE(std::holds_alternative<std::string_view>(value));
EXPECT_EQ(std::get<std::string_view>(value), "value\\");
}
TEST_F(JsonParserTest, ParseStringWithEscapesInMiddle) {
constexpr std::string_view kJson = "\"hello\\nworld\"";
JsonValue value;
std::string str;
Parse(kJson, value, str);
ASSERT_TRUE(std::holds_alternative<std::string_view>(value));
EXPECT_EQ(std::get<std::string_view>(value), "hello\nworld");
}
TEST_F(JsonParserTest, ParseEmptyString) {
constexpr std::string_view kJson = "\"\"";
JsonValue value;
std::string str;
Parse(kJson, value, str);
ASSERT_TRUE(std::holds_alternative<std::string_view>(value));
EXPECT_EQ(std::get<std::string_view>(value), "");
}
TEST_F(JsonParserTest, ParseObject) {
constexpr std::string_view kJson = "{\"key\": \"value\"}";
JsonValue value;
std::string str;
Parse(kJson, value, str);
ASSERT_TRUE(std::holds_alternative<Object>(value));
EXPECT_EQ(std::get<Object>(value).contents, "{\"key\": \"value\"}");
}
TEST_F(JsonParserTest, ParseArray) {
constexpr std::string_view kJson = "[1, 2, 3]";
JsonValue value;
std::string str;
Parse(kJson, value, str);
ASSERT_TRUE(std::holds_alternative<Array>(value));
EXPECT_EQ(std::get<Array>(value).contents, "[1, 2, 3]");
}
TEST_F(JsonParserTest, InvalidToken) {
constexpr std::string_view kJson = "invalid";
JsonValue value;
std::string str;
Parse(kJson, value, str, false);
}
TEST(JsonParserIteratorTest, EmptyObject) {
constexpr std::string_view kJson = "{}";
Iterator it;
it.Reset(kJson.data(), kJson.data() + kJson.size());
ASSERT_TRUE(it.ParseStart());
ASSERT_EQ(it.ParseObjectFieldWithoutRecursing(), ReturnCode::kEndOfScope);
ASSERT_TRUE(it.eof());
}
TEST(JsonParserIteratorTest, SimpleObject) {
constexpr std::string_view kJson = R"({"key": "value", "key2": 123})";
Iterator it;
it.Reset(kJson.data(), kJson.data() + kJson.size());
ASSERT_TRUE(it.ParseStart());
ASSERT_EQ(it.ParseObjectFieldWithoutRecursing(), ReturnCode::kOk);
EXPECT_EQ(it.key(), "key");
ASSERT_TRUE(std::holds_alternative<std::string_view>(it.value()));
EXPECT_EQ(std::get<std::string_view>(it.value()), "value");
ASSERT_EQ(it.ParseObjectFieldWithoutRecursing(), ReturnCode::kOk);
EXPECT_EQ(it.key(), "key2");
ASSERT_TRUE(std::holds_alternative<int64_t>(it.value()));
EXPECT_EQ(std::get<int64_t>(it.value()), 123);
ASSERT_EQ(it.ParseObjectFieldWithoutRecursing(), ReturnCode::kEndOfScope);
ASSERT_TRUE(it.eof());
}
TEST(JsonParserIteratorTest, NestedObject) {
constexpr std::string_view kJson =
R"({"key": {"nested_key": "nested_value"}})";
Iterator it;
it.Reset(kJson.data(), kJson.data() + kJson.size());
ASSERT_TRUE(it.ParseStart());
ASSERT_EQ(it.ParseAndRecurse(), ReturnCode::kOk);
EXPECT_EQ(it.key(), "key");
ASSERT_TRUE(std::holds_alternative<Object>(it.value()));
ASSERT_EQ(it.ParseAndRecurse(), ReturnCode::kOk);
EXPECT_EQ(it.key(), "nested_key");
ASSERT_TRUE(std::holds_alternative<std::string_view>(it.value()));
EXPECT_EQ(std::get<std::string_view>(it.value()), "nested_value");
ASSERT_EQ(it.ParseAndRecurse(), ReturnCode::kEndOfScope);
ASSERT_EQ(it.ParseAndRecurse(), ReturnCode::kEndOfScope);
ASSERT_TRUE(it.eof());
}
TEST(JsonParserIteratorTest, SimpleArray) {
constexpr std::string_view kJson = R"(["value", 123, true, null])";
Iterator it;
it.Reset(kJson.data(), kJson.data() + kJson.size());
ASSERT_TRUE(it.ParseStart());
ASSERT_EQ(it.ParseAndRecurse(), ReturnCode::kOk);
ASSERT_TRUE(std::holds_alternative<std::string_view>(it.value()));
EXPECT_EQ(std::get<std::string_view>(it.value()), "value");
ASSERT_EQ(it.ParseAndRecurse(), ReturnCode::kOk);
ASSERT_TRUE(std::holds_alternative<int64_t>(it.value()));
EXPECT_EQ(std::get<int64_t>(it.value()), 123);
ASSERT_EQ(it.ParseAndRecurse(), ReturnCode::kOk);
ASSERT_TRUE(std::holds_alternative<bool>(it.value()));
EXPECT_TRUE(std::get<bool>(it.value()));
ASSERT_EQ(it.ParseAndRecurse(), ReturnCode::kOk);
ASSERT_TRUE(std::holds_alternative<Null>(it.value()));
ASSERT_EQ(it.ParseAndRecurse(), ReturnCode::kEndOfScope);
ASSERT_TRUE(it.eof());
}
TEST(JsonParserUnescapeTest, Unescape) {
std::string res;
base::Status status;
constexpr std::string_view kEscaped = R"(\"\\\/\b\f\n\r\t\u1234)";
auto ret = internal::UnescapeString(
kEscaped.data(), kEscaped.data() + kEscaped.size(), res, status);
ASSERT_EQ(ret, internal::ReturnCode::kOk);
ASSERT_TRUE(status.ok());
EXPECT_EQ(res, "\"\\/\b\f\n\r\t\xe1\x88\xb4");
}
} // namespace
} // namespace perfetto::trace_processor::json