1*6777b538SAndroid Build Coastguard Worker // Copyright 2012 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker #include "base/json/json_writer.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker #include <optional>
8*6777b538SAndroid Build Coastguard Worker
9*6777b538SAndroid Build Coastguard Worker #include "base/containers/span.h"
10*6777b538SAndroid Build Coastguard Worker #include "base/json/json_reader.h"
11*6777b538SAndroid Build Coastguard Worker #include "base/strings/stringprintf.h"
12*6777b538SAndroid Build Coastguard Worker #include "base/test/gmock_expected_support.h"
13*6777b538SAndroid Build Coastguard Worker #include "base/values.h"
14*6777b538SAndroid Build Coastguard Worker #include "build/build_config.h"
15*6777b538SAndroid Build Coastguard Worker #include "testing/gtest/include/gtest/gtest.h"
16*6777b538SAndroid Build Coastguard Worker
17*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
18*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_util.h"
19*6777b538SAndroid Build Coastguard Worker #endif
20*6777b538SAndroid Build Coastguard Worker
21*6777b538SAndroid Build Coastguard Worker namespace base {
22*6777b538SAndroid Build Coastguard Worker
23*6777b538SAndroid Build Coastguard Worker namespace {
24*6777b538SAndroid Build Coastguard Worker
FixNewlines(const std::string & json)25*6777b538SAndroid Build Coastguard Worker std::string FixNewlines(const std::string& json) {
26*6777b538SAndroid Build Coastguard Worker // The pretty-printer uses a different newline style on Windows than on
27*6777b538SAndroid Build Coastguard Worker // other platforms.
28*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
29*6777b538SAndroid Build Coastguard Worker std::string result;
30*6777b538SAndroid Build Coastguard Worker ReplaceChars(json, "\n", "\r\n", &result);
31*6777b538SAndroid Build Coastguard Worker return result;
32*6777b538SAndroid Build Coastguard Worker #else
33*6777b538SAndroid Build Coastguard Worker return json;
34*6777b538SAndroid Build Coastguard Worker #endif
35*6777b538SAndroid Build Coastguard Worker }
36*6777b538SAndroid Build Coastguard Worker
37*6777b538SAndroid Build Coastguard Worker } // namespace
38*6777b538SAndroid Build Coastguard Worker
TEST(JsonWriterTest,BasicTypes)39*6777b538SAndroid Build Coastguard Worker TEST(JsonWriterTest, BasicTypes) {
40*6777b538SAndroid Build Coastguard Worker // Test null.
41*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(WriteJson(Value()), "null");
42*6777b538SAndroid Build Coastguard Worker
43*6777b538SAndroid Build Coastguard Worker // Test empty dict.
44*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(WriteJson(Value(Value::Type::DICT)), "{}");
45*6777b538SAndroid Build Coastguard Worker
46*6777b538SAndroid Build Coastguard Worker // Test empty list.
47*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(WriteJson(Value(Value::Type::LIST)), "[]");
48*6777b538SAndroid Build Coastguard Worker
49*6777b538SAndroid Build Coastguard Worker // Test integer values.
50*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(WriteJson(Value(42)), "42");
51*6777b538SAndroid Build Coastguard Worker
52*6777b538SAndroid Build Coastguard Worker // Test boolean values.
53*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(WriteJson(Value(true)), "true");
54*6777b538SAndroid Build Coastguard Worker
55*6777b538SAndroid Build Coastguard Worker // Test Real values should always have a decimal or an 'e'.
56*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(WriteJson(Value(1.0)), "1.0");
57*6777b538SAndroid Build Coastguard Worker
58*6777b538SAndroid Build Coastguard Worker // Test Real values in the range (-1, 1) must have leading zeros
59*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(WriteJson(Value(0.2)), "0.2");
60*6777b538SAndroid Build Coastguard Worker
61*6777b538SAndroid Build Coastguard Worker // Test Real values in the range (-1, 1) must have leading zeros
62*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(WriteJson(Value(-0.8)), "-0.8");
63*6777b538SAndroid Build Coastguard Worker
64*6777b538SAndroid Build Coastguard Worker // Test String values.
65*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(WriteJson(Value("foo")), "\"foo\"");
66*6777b538SAndroid Build Coastguard Worker }
67*6777b538SAndroid Build Coastguard Worker
TEST(JsonWriterTest,NestedTypes)68*6777b538SAndroid Build Coastguard Worker TEST(JsonWriterTest, NestedTypes) {
69*6777b538SAndroid Build Coastguard Worker // Writer unittests like empty list/dict nesting,
70*6777b538SAndroid Build Coastguard Worker // list list nesting, etc.
71*6777b538SAndroid Build Coastguard Worker auto dict =
72*6777b538SAndroid Build Coastguard Worker Value::Dict().Set("list", Value::List()
73*6777b538SAndroid Build Coastguard Worker .Append(Value::Dict().Set("inner int", 10))
74*6777b538SAndroid Build Coastguard Worker .Append(Value::Dict())
75*6777b538SAndroid Build Coastguard Worker .Append(Value::List())
76*6777b538SAndroid Build Coastguard Worker .Append(true));
77*6777b538SAndroid Build Coastguard Worker
78*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(WriteJson(dict), "{\"list\":[{\"inner int\":10},{},[],true]}");
79*6777b538SAndroid Build Coastguard Worker
80*6777b538SAndroid Build Coastguard Worker // Test the pretty-printer.
81*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(WriteJsonWithOptions(dict, JSONWriter::OPTIONS_PRETTY_PRINT),
82*6777b538SAndroid Build Coastguard Worker FixNewlines(R"({
83*6777b538SAndroid Build Coastguard Worker "list": [ {
84*6777b538SAndroid Build Coastguard Worker "inner int": 10
85*6777b538SAndroid Build Coastguard Worker }, {
86*6777b538SAndroid Build Coastguard Worker }, [ ], true ]
87*6777b538SAndroid Build Coastguard Worker }
88*6777b538SAndroid Build Coastguard Worker )"));
89*6777b538SAndroid Build Coastguard Worker }
90*6777b538SAndroid Build Coastguard Worker
TEST(JsonWriterTest,KeysWithPeriods)91*6777b538SAndroid Build Coastguard Worker TEST(JsonWriterTest, KeysWithPeriods) {
92*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(WriteJson(Value::Dict() //
93*6777b538SAndroid Build Coastguard Worker .Set("a.b", 3)
94*6777b538SAndroid Build Coastguard Worker .Set("c", 2)
95*6777b538SAndroid Build Coastguard Worker .Set("d.e.f", Value::Dict().Set("g.h.i.j", 1))),
96*6777b538SAndroid Build Coastguard Worker R"({"a.b":3,"c":2,"d.e.f":{"g.h.i.j":1}})");
97*6777b538SAndroid Build Coastguard Worker
98*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(WriteJson(Value::Dict() //
99*6777b538SAndroid Build Coastguard Worker .SetByDottedPath("a.b", 2)
100*6777b538SAndroid Build Coastguard Worker .Set("a.b", 1)),
101*6777b538SAndroid Build Coastguard Worker R"({"a":{"b":2},"a.b":1})");
102*6777b538SAndroid Build Coastguard Worker }
103*6777b538SAndroid Build Coastguard Worker
TEST(JsonWriterTest,BinaryValues)104*6777b538SAndroid Build Coastguard Worker TEST(JsonWriterTest, BinaryValues) {
105*6777b538SAndroid Build Coastguard Worker const auto kBinaryData = base::as_bytes(base::make_span("asdf", 4u));
106*6777b538SAndroid Build Coastguard Worker
107*6777b538SAndroid Build Coastguard Worker // Binary values should return errors unless suppressed via the
108*6777b538SAndroid Build Coastguard Worker // `OPTIONS_OMIT_BINARY_VALUES` flag.
109*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(WriteJson(Value(kBinaryData)), std::nullopt);
110*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(WriteJsonWithOptions(Value(kBinaryData),
111*6777b538SAndroid Build Coastguard Worker JsonOptions::OPTIONS_OMIT_BINARY_VALUES),
112*6777b538SAndroid Build Coastguard Worker "");
113*6777b538SAndroid Build Coastguard Worker
114*6777b538SAndroid Build Coastguard Worker auto binary_list = Value::List()
115*6777b538SAndroid Build Coastguard Worker .Append(Value(kBinaryData))
116*6777b538SAndroid Build Coastguard Worker .Append(5)
117*6777b538SAndroid Build Coastguard Worker .Append(Value(kBinaryData))
118*6777b538SAndroid Build Coastguard Worker .Append(2)
119*6777b538SAndroid Build Coastguard Worker .Append(Value(kBinaryData));
120*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(WriteJson(binary_list), std::nullopt);
121*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(
122*6777b538SAndroid Build Coastguard Worker WriteJsonWithOptions(binary_list, JSONWriter::OPTIONS_OMIT_BINARY_VALUES),
123*6777b538SAndroid Build Coastguard Worker "[5,2]");
124*6777b538SAndroid Build Coastguard Worker
125*6777b538SAndroid Build Coastguard Worker auto binary_dict = Value::Dict()
126*6777b538SAndroid Build Coastguard Worker .Set("a", Value(kBinaryData))
127*6777b538SAndroid Build Coastguard Worker .Set("b", 5)
128*6777b538SAndroid Build Coastguard Worker .Set("c", Value(kBinaryData))
129*6777b538SAndroid Build Coastguard Worker .Set("d", 2)
130*6777b538SAndroid Build Coastguard Worker .Set("e", Value(kBinaryData));
131*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(WriteJson(binary_dict), std::nullopt);
132*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(
133*6777b538SAndroid Build Coastguard Worker WriteJsonWithOptions(binary_dict, JSONWriter::OPTIONS_OMIT_BINARY_VALUES),
134*6777b538SAndroid Build Coastguard Worker R"({"b":5,"d":2})");
135*6777b538SAndroid Build Coastguard Worker }
136*6777b538SAndroid Build Coastguard Worker
TEST(JsonWriterTest,DoublesAsInts)137*6777b538SAndroid Build Coastguard Worker TEST(JsonWriterTest, DoublesAsInts) {
138*6777b538SAndroid Build Coastguard Worker // Test allowing a double with no fractional part to be written as an integer.
139*6777b538SAndroid Build Coastguard Worker Value double_value(1e10);
140*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(
141*6777b538SAndroid Build Coastguard Worker WriteJsonWithOptions(double_value,
142*6777b538SAndroid Build Coastguard Worker JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION),
143*6777b538SAndroid Build Coastguard Worker "10000000000");
144*6777b538SAndroid Build Coastguard Worker }
145*6777b538SAndroid Build Coastguard Worker
TEST(JsonWriterTest,StackOverflow)146*6777b538SAndroid Build Coastguard Worker TEST(JsonWriterTest, StackOverflow) {
147*6777b538SAndroid Build Coastguard Worker Value::List deep_list;
148*6777b538SAndroid Build Coastguard Worker const size_t max_depth = 100000;
149*6777b538SAndroid Build Coastguard Worker
150*6777b538SAndroid Build Coastguard Worker for (size_t i = 0; i < max_depth; ++i) {
151*6777b538SAndroid Build Coastguard Worker Value::List new_top_list;
152*6777b538SAndroid Build Coastguard Worker new_top_list.Append(std::move(deep_list));
153*6777b538SAndroid Build Coastguard Worker deep_list = std::move(new_top_list);
154*6777b538SAndroid Build Coastguard Worker }
155*6777b538SAndroid Build Coastguard Worker
156*6777b538SAndroid Build Coastguard Worker Value deep_list_value(std::move(deep_list));
157*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(WriteJson(deep_list_value), std::nullopt);
158*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(
159*6777b538SAndroid Build Coastguard Worker WriteJsonWithOptions(deep_list_value, JSONWriter::OPTIONS_PRETTY_PRINT),
160*6777b538SAndroid Build Coastguard Worker std::nullopt);
161*6777b538SAndroid Build Coastguard Worker
162*6777b538SAndroid Build Coastguard Worker // We cannot just let `deep_list` tear down since it
163*6777b538SAndroid Build Coastguard Worker // would cause a stack overflow. Therefore, we tear
164*6777b538SAndroid Build Coastguard Worker // down the deep list manually.
165*6777b538SAndroid Build Coastguard Worker deep_list = std::move(deep_list_value).TakeList();
166*6777b538SAndroid Build Coastguard Worker while (!deep_list.empty()) {
167*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(deep_list.size(), 1u);
168*6777b538SAndroid Build Coastguard Worker Value::List inner_list = std::move(deep_list[0]).TakeList();
169*6777b538SAndroid Build Coastguard Worker deep_list = std::move(inner_list);
170*6777b538SAndroid Build Coastguard Worker }
171*6777b538SAndroid Build Coastguard Worker }
172*6777b538SAndroid Build Coastguard Worker
TEST(JsonWriterTest,TestMaxDepthWithValidNodes)173*6777b538SAndroid Build Coastguard Worker TEST(JsonWriterTest, TestMaxDepthWithValidNodes) {
174*6777b538SAndroid Build Coastguard Worker // Create JSON to the max depth - 1. Nodes at that depth are still valid
175*6777b538SAndroid Build Coastguard Worker // for writing which matches the JSONParser logic.
176*6777b538SAndroid Build Coastguard Worker std::string nested_json;
177*6777b538SAndroid Build Coastguard Worker for (int i = 0; i < 199; ++i) {
178*6777b538SAndroid Build Coastguard Worker std::string node = "[";
179*6777b538SAndroid Build Coastguard Worker for (int j = 0; j < 5; j++) {
180*6777b538SAndroid Build Coastguard Worker node.append(StringPrintf("%d,", j));
181*6777b538SAndroid Build Coastguard Worker }
182*6777b538SAndroid Build Coastguard Worker nested_json.insert(0, node);
183*6777b538SAndroid Build Coastguard Worker nested_json.append("]");
184*6777b538SAndroid Build Coastguard Worker }
185*6777b538SAndroid Build Coastguard Worker
186*6777b538SAndroid Build Coastguard Worker // Ensure we can read and write the JSON
187*6777b538SAndroid Build Coastguard Worker ASSERT_OK_AND_ASSIGN(Value value,
188*6777b538SAndroid Build Coastguard Worker JSONReader::ReadAndReturnValueWithError(
189*6777b538SAndroid Build Coastguard Worker nested_json, JSON_ALLOW_TRAILING_COMMAS));
190*6777b538SAndroid Build Coastguard Worker EXPECT_TRUE(WriteJson(std::move(value)).has_value());
191*6777b538SAndroid Build Coastguard Worker }
192*6777b538SAndroid Build Coastguard Worker
193*6777b538SAndroid Build Coastguard Worker // Test that the JSONWriter::Write method still works.
TEST(JsonWriterTest,JSONWriterWriteSuccess)194*6777b538SAndroid Build Coastguard Worker TEST(JsonWriterTest, JSONWriterWriteSuccess) {
195*6777b538SAndroid Build Coastguard Worker std::string output_js;
196*6777b538SAndroid Build Coastguard Worker
197*6777b538SAndroid Build Coastguard Worker EXPECT_TRUE(
198*6777b538SAndroid Build Coastguard Worker JSONWriter::Write(base::Value::Dict().Set("key", "value"), &output_js));
199*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(output_js, R"({"key":"value"})");
200*6777b538SAndroid Build Coastguard Worker }
201*6777b538SAndroid Build Coastguard Worker
202*6777b538SAndroid Build Coastguard Worker // Test that the JSONWriter::Write method still works.
TEST(JsonWriterTest,JSONWriterWriteFailure)203*6777b538SAndroid Build Coastguard Worker TEST(JsonWriterTest, JSONWriterWriteFailure) {
204*6777b538SAndroid Build Coastguard Worker std::string output_js;
205*6777b538SAndroid Build Coastguard Worker
206*6777b538SAndroid Build Coastguard Worker EXPECT_FALSE(JSONWriter::Write(
207*6777b538SAndroid Build Coastguard Worker base::Value::Dict() //
208*6777b538SAndroid Build Coastguard Worker .Set("key",
209*6777b538SAndroid Build Coastguard Worker base::Value::Dict().Set("nested-key", base::Value::Dict())),
210*6777b538SAndroid Build Coastguard Worker &output_js, /*max_depth=*/1));
211*6777b538SAndroid Build Coastguard Worker }
212*6777b538SAndroid Build Coastguard Worker
213*6777b538SAndroid Build Coastguard Worker // Test that the JSONWriter::WriteWithOptions method still works.
TEST(JsonWriterTest,JSONWriterWriteWithOptionsSuccess)214*6777b538SAndroid Build Coastguard Worker TEST(JsonWriterTest, JSONWriterWriteWithOptionsSuccess) {
215*6777b538SAndroid Build Coastguard Worker std::string output_js;
216*6777b538SAndroid Build Coastguard Worker EXPECT_TRUE(JSONWriter::WriteWithOptions(
217*6777b538SAndroid Build Coastguard Worker base::Value::Dict().Set("key", "value"), JSONWriter::OPTIONS_PRETTY_PRINT,
218*6777b538SAndroid Build Coastguard Worker &output_js));
219*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(output_js, FixNewlines(R"({
220*6777b538SAndroid Build Coastguard Worker "key": "value"
221*6777b538SAndroid Build Coastguard Worker }
222*6777b538SAndroid Build Coastguard Worker )"));
223*6777b538SAndroid Build Coastguard Worker }
224*6777b538SAndroid Build Coastguard Worker
225*6777b538SAndroid Build Coastguard Worker // Test that the JSONWriter::WriteWithOptions method still works.
TEST(JsonWriterTest,JSONWriterWriteWithOptionsFailure)226*6777b538SAndroid Build Coastguard Worker TEST(JsonWriterTest, JSONWriterWriteWithOptionsFailure) {
227*6777b538SAndroid Build Coastguard Worker std::string output_js;
228*6777b538SAndroid Build Coastguard Worker
229*6777b538SAndroid Build Coastguard Worker EXPECT_FALSE(JSONWriter::WriteWithOptions(
230*6777b538SAndroid Build Coastguard Worker base::Value::Dict().Set(
231*6777b538SAndroid Build Coastguard Worker "key", base::Value::Dict().Set("nested-key", base::Value::Dict())),
232*6777b538SAndroid Build Coastguard Worker JSONWriter::OPTIONS_PRETTY_PRINT, &output_js, /*max_depth=*/1));
233*6777b538SAndroid Build Coastguard Worker }
234*6777b538SAndroid Build Coastguard Worker
235*6777b538SAndroid Build Coastguard Worker } // namespace base
236