xref: /aosp_15_r20/external/cronet/base/json/json_writer_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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