| /* |
| * 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 |