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