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