xref: /aosp_15_r20/external/pigweed/pw_json/builder_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2024 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #include "pw_json/builder.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <array>
18*61c4878aSAndroid Build Coastguard Worker #include <cstring>
19*61c4878aSAndroid Build Coastguard Worker #include <string_view>
20*61c4878aSAndroid Build Coastguard Worker 
21*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
22*61c4878aSAndroid Build Coastguard Worker #include "pw_compilation_testing/negative_compilation.h"
23*61c4878aSAndroid Build Coastguard Worker #include "pw_unit_test/framework.h"
24*61c4878aSAndroid Build Coastguard Worker 
25*61c4878aSAndroid Build Coastguard Worker namespace {
26*61c4878aSAndroid Build Coastguard Worker 
27*61c4878aSAndroid Build Coastguard Worker using namespace std::string_view_literals;
28*61c4878aSAndroid Build Coastguard Worker 
29*61c4878aSAndroid Build Coastguard Worker // First example for the docs.
__anon1fb128a40202null30*61c4878aSAndroid Build Coastguard Worker static_assert([] {
31*61c4878aSAndroid Build Coastguard Worker   bool is_simple = true;
32*61c4878aSAndroid Build Coastguard Worker   int safety_percentage = 100;
33*61c4878aSAndroid Build Coastguard Worker 
34*61c4878aSAndroid Build Coastguard Worker   std::string_view features[] = {"values", "arrays", "objects", "nesting!"};
35*61c4878aSAndroid Build Coastguard Worker 
36*61c4878aSAndroid Build Coastguard Worker   // DOCTSAG: [pw-json-builder-example-1]
37*61c4878aSAndroid Build Coastguard Worker   pw::JsonBuffer<256> json_buffer;
38*61c4878aSAndroid Build Coastguard Worker   pw::JsonObject& object = json_buffer.StartObject();
39*61c4878aSAndroid Build Coastguard Worker   object.Add("tagline", "Easy, efficient JSON serialization!")
40*61c4878aSAndroid Build Coastguard Worker       .Add("simple", is_simple)
41*61c4878aSAndroid Build Coastguard Worker       .Add("safe", safety_percentage)
42*61c4878aSAndroid Build Coastguard Worker       .Add("dynamic allocation", false);
43*61c4878aSAndroid Build Coastguard Worker 
44*61c4878aSAndroid Build Coastguard Worker   pw::NestedJsonArray nested_array = object.AddNestedArray("features");
45*61c4878aSAndroid Build Coastguard Worker   for (const std::string_view feature : features) {
46*61c4878aSAndroid Build Coastguard Worker     nested_array.Append(feature);
47*61c4878aSAndroid Build Coastguard Worker   }
48*61c4878aSAndroid Build Coastguard Worker   // DOCTSAG: [pw-json-builder-example-1]
49*61c4878aSAndroid Build Coastguard Worker   return json_buffer;
50*61c4878aSAndroid Build Coastguard Worker }() == R"({"tagline": "Easy, efficient JSON serialization!", "simple": true,)"
51*61c4878aSAndroid Build Coastguard Worker        R"( "safe": 100, "dynamic allocation": false, "features":)"
52*61c4878aSAndroid Build Coastguard Worker        R"( ["values", "arrays", "objects", "nesting!"]})"sv);
53*61c4878aSAndroid Build Coastguard Worker 
54*61c4878aSAndroid Build Coastguard Worker // Second example for the docs.
__anon1fb128a40302null55*61c4878aSAndroid Build Coastguard Worker static_assert([] {
56*61c4878aSAndroid Build Coastguard Worker   constexpr char empty[128] = {};
57*61c4878aSAndroid Build Coastguard Worker   std::string_view huge_string_that_wont_fit(empty, sizeof(empty));
58*61c4878aSAndroid Build Coastguard Worker 
59*61c4878aSAndroid Build Coastguard Worker   // DOCTSAG: [pw-json-builder-example-2]
60*61c4878aSAndroid Build Coastguard Worker   // Declare a JsonBuffer (JsonBuilder with included buffer) and start a JSON
61*61c4878aSAndroid Build Coastguard Worker   // object in it.
62*61c4878aSAndroid Build Coastguard Worker   pw::JsonBuffer<128> json_buffer;
63*61c4878aSAndroid Build Coastguard Worker   pw::JsonObject& json = json_buffer.StartObject();
64*61c4878aSAndroid Build Coastguard Worker 
65*61c4878aSAndroid Build Coastguard Worker   const char* name = "Crag";
66*61c4878aSAndroid Build Coastguard Worker   constexpr const char* kOccupationKey = "job";
67*61c4878aSAndroid Build Coastguard Worker 
68*61c4878aSAndroid Build Coastguard Worker   // Add key-value pairs to a JSON object.
69*61c4878aSAndroid Build Coastguard Worker   json.Add("name", name).Add(kOccupationKey, "hacker");
70*61c4878aSAndroid Build Coastguard Worker 
71*61c4878aSAndroid Build Coastguard Worker   // Add an array as the value in a key-value pair.
72*61c4878aSAndroid Build Coastguard Worker   pw::NestedJsonArray nested_array = json.AddNestedArray("skills");
73*61c4878aSAndroid Build Coastguard Worker 
74*61c4878aSAndroid Build Coastguard Worker   // Append items to an array.
75*61c4878aSAndroid Build Coastguard Worker   nested_array.Append(20).Append(1).Append(1).Append(1);
76*61c4878aSAndroid Build Coastguard Worker 
77*61c4878aSAndroid Build Coastguard Worker   // Check that everything fit in the JSON buffer.
78*61c4878aSAndroid Build Coastguard Worker   PW_ASSERT(json.ok());
79*61c4878aSAndroid Build Coastguard Worker 
80*61c4878aSAndroid Build Coastguard Worker   // Compare the contents of the JSON to a std::string_view.
81*61c4878aSAndroid Build Coastguard Worker   PW_ASSERT(std::string_view(json) ==
82*61c4878aSAndroid Build Coastguard Worker             R"({"name": "Crag", "job": "hacker", "skills": [20, 1, 1, 1]})");
83*61c4878aSAndroid Build Coastguard Worker 
84*61c4878aSAndroid Build Coastguard Worker   // Add an object as the value in a key-value pair.
85*61c4878aSAndroid Build Coastguard Worker   pw::NestedJsonObject nested_object = json.AddNestedObject("items");
86*61c4878aSAndroid Build Coastguard Worker 
87*61c4878aSAndroid Build Coastguard Worker   // Declare another JsonArray, and add it as nested value.
88*61c4878aSAndroid Build Coastguard Worker   pw::JsonBuffer<10> inner_buffer;
89*61c4878aSAndroid Build Coastguard Worker   inner_buffer.StartArray().Append(nullptr);
90*61c4878aSAndroid Build Coastguard Worker   nested_object.Add("misc", inner_buffer);
91*61c4878aSAndroid Build Coastguard Worker 
92*61c4878aSAndroid Build Coastguard Worker   // Add a value that is too large for the JsonBuffer.
93*61c4878aSAndroid Build Coastguard Worker   json.Add("way too big!", huge_string_that_wont_fit);
94*61c4878aSAndroid Build Coastguard Worker 
95*61c4878aSAndroid Build Coastguard Worker   // Adding the last entry failed, but the JSON is still valid.
96*61c4878aSAndroid Build Coastguard Worker   PW_ASSERT(json.status().IsResourceExhausted());
97*61c4878aSAndroid Build Coastguard Worker 
98*61c4878aSAndroid Build Coastguard Worker   PW_ASSERT(std::string_view(json) ==
99*61c4878aSAndroid Build Coastguard Worker             R"({"name": "Crag", "job": "hacker", "skills": [20, 1, 1, 1],)"
100*61c4878aSAndroid Build Coastguard Worker             R"( "items": {"misc": [null]}})");
101*61c4878aSAndroid Build Coastguard Worker   // DOCTSAG: [pw-json-builder-example-2]
102*61c4878aSAndroid Build Coastguard Worker   return json_buffer;
103*61c4878aSAndroid Build Coastguard Worker }() == R"({"name": "Crag", "job": "hacker", "skills": [20, 1, 1, 1],)"
104*61c4878aSAndroid Build Coastguard Worker        R"( "items": {"misc": [null]}})"sv);
105*61c4878aSAndroid Build Coastguard Worker 
106*61c4878aSAndroid Build Coastguard Worker }  // namespace
107*61c4878aSAndroid Build Coastguard Worker 
108*61c4878aSAndroid Build Coastguard Worker namespace pw {
109*61c4878aSAndroid Build Coastguard Worker namespace {
110*61c4878aSAndroid Build Coastguard Worker 
111*61c4878aSAndroid Build Coastguard Worker class JsonOverflowTest : public ::testing::Test {
112*61c4878aSAndroid Build Coastguard Worker  public:
~JsonOverflowTest()113*61c4878aSAndroid Build Coastguard Worker   ~JsonOverflowTest() override {
114*61c4878aSAndroid Build Coastguard Worker     EXPECT_STREQ(&buffer_[end_], kTag) << "Overflow occurred!";
115*61c4878aSAndroid Build Coastguard Worker   }
116*61c4878aSAndroid Build Coastguard Worker 
117*61c4878aSAndroid Build Coastguard Worker  protected:
MarkBufferEnd(const JsonBuilder & json)118*61c4878aSAndroid Build Coastguard Worker   void MarkBufferEnd(const JsonBuilder& json) {
119*61c4878aSAndroid Build Coastguard Worker     end_ = json.max_size() + 1;
120*61c4878aSAndroid Build Coastguard Worker     ASSERT_LT(end_, sizeof(buffer_) - sizeof(kTag));
121*61c4878aSAndroid Build Coastguard Worker     std::memcpy(&buffer_[end_], kTag, sizeof(kTag));
122*61c4878aSAndroid Build Coastguard Worker   }
123*61c4878aSAndroid Build Coastguard Worker 
124*61c4878aSAndroid Build Coastguard Worker   char buffer_[512];
125*61c4878aSAndroid Build Coastguard Worker 
126*61c4878aSAndroid Build Coastguard Worker  private:
127*61c4878aSAndroid Build Coastguard Worker   static constexpr const char kTag[] = "Hi! Your buffer is safe.";
128*61c4878aSAndroid Build Coastguard Worker 
129*61c4878aSAndroid Build Coastguard Worker   size_t end_ = 0;
130*61c4878aSAndroid Build Coastguard Worker };
131*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonObject,BasicJson)132*61c4878aSAndroid Build Coastguard Worker TEST(JsonObject, BasicJson) {
133*61c4878aSAndroid Build Coastguard Worker   char buffer[128];
134*61c4878aSAndroid Build Coastguard Worker   std::memset(buffer, '?', sizeof(buffer));
135*61c4878aSAndroid Build Coastguard Worker   JsonBuilder json_buffer(buffer);
136*61c4878aSAndroid Build Coastguard Worker   JsonObject& json = json_buffer.StartObject();
137*61c4878aSAndroid Build Coastguard Worker 
138*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(buffer, json.data());
139*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("{}", json.data());
140*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(2u, json.size());
141*61c4878aSAndroid Build Coastguard Worker 
142*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.Add("foo", "bar").status());
143*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"foo": "bar"})", json.data());
144*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(std::strlen(json.data()), json.size());
145*61c4878aSAndroid Build Coastguard Worker 
146*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.Add("bar", 0).status());
147*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"foo": "bar", "bar": 0})", json.data());
148*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(std::strlen(json.data()), json.size());
149*61c4878aSAndroid Build Coastguard Worker 
150*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.Add("baz", nullptr).status());
151*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"foo": "bar", "bar": 0, "baz": null})", json.data());
152*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(std::strlen(json.data()), json.size());
153*61c4878aSAndroid Build Coastguard Worker 
154*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.Add("EMPTY STR!", "").status());
155*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"foo": "bar", "bar": 0, "baz": null, "EMPTY STR!": ""})",
156*61c4878aSAndroid Build Coastguard Worker                json.data());
157*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(std::strlen(json.data()), json.size());
158*61c4878aSAndroid Build Coastguard Worker }
159*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonObject,OverflowAtKey)160*61c4878aSAndroid Build Coastguard Worker TEST(JsonObject, OverflowAtKey) {
161*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<19> json_buffer;
162*61c4878aSAndroid Build Coastguard Worker   JsonObject& json = json_buffer.StartObject();
163*61c4878aSAndroid Build Coastguard Worker 
164*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.Add("a", 5l).Add("b", "!").status());
165*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"a": 5, "b": "!"})", json.data());  // 18 chars + \0
166*61c4878aSAndroid Build Coastguard Worker 
167*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), json.Add("b", "!").status());
168*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"a": 5, "b": "!"})", json.data());
169*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), json.Add("b", "").status());
170*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"a": 5, "b": "!"})", json.data());
171*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), json.status());
172*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), json.last_status());
173*61c4878aSAndroid Build Coastguard Worker   json.clear_status();
174*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"a": 5, "b": "!"})", json.data());
175*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.status());
176*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.last_status());
177*61c4878aSAndroid Build Coastguard Worker }
178*61c4878aSAndroid Build Coastguard Worker 
TEST_F(JsonOverflowTest,ObjectOverflowAtFirstEntry)179*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonOverflowTest, ObjectOverflowAtFirstEntry) {
180*61c4878aSAndroid Build Coastguard Worker   JsonBuilder json_builder(buffer_, 5);
181*61c4878aSAndroid Build Coastguard Worker   MarkBufferEnd(json_builder);
182*61c4878aSAndroid Build Coastguard Worker 
183*61c4878aSAndroid Build Coastguard Worker   JsonObject& json = json_builder.StartObject();
184*61c4878aSAndroid Build Coastguard Worker   ASSERT_STREQ("{}", json.data());
185*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), json.Add("some_key", "").status());
186*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("{}", json.data());
187*61c4878aSAndroid Build Coastguard Worker }
188*61c4878aSAndroid Build Coastguard Worker 
TEST_F(JsonOverflowTest,ObjectOverflowAtStringValue)189*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonOverflowTest, ObjectOverflowAtStringValue) {
190*61c4878aSAndroid Build Coastguard Worker   JsonBuilder json_builder(buffer_, 32);
191*61c4878aSAndroid Build Coastguard Worker   MarkBufferEnd(json_builder);
192*61c4878aSAndroid Build Coastguard Worker 
193*61c4878aSAndroid Build Coastguard Worker   JsonObject& json = json_builder.StartObject();
194*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.Add("a", 5l).status());
195*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"a": 5})", json.data());
196*61c4878aSAndroid Build Coastguard Worker 
197*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(
198*61c4878aSAndroid Build Coastguard Worker       Status::ResourceExhausted(),
199*61c4878aSAndroid Build Coastguard Worker       json.Add("b", "This string is so long that it won't fit!!!!").status());
200*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"a": 5})", json.data());
201*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), json.status());
202*61c4878aSAndroid Build Coastguard Worker 
203*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.Add("b", "This will!").last_status());
204*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"a": 5, "b": "This will!"})", json.data());
205*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), json.status());
206*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.last_status());
207*61c4878aSAndroid Build Coastguard Worker }
208*61c4878aSAndroid Build Coastguard Worker 
TEST_F(JsonOverflowTest,OverflowAtUnicodeCharacter)209*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonOverflowTest, OverflowAtUnicodeCharacter) {
210*61c4878aSAndroid Build Coastguard Worker   JsonBuilder json_builder(buffer_, 10);
211*61c4878aSAndroid Build Coastguard Worker   MarkBufferEnd(json_builder);
212*61c4878aSAndroid Build Coastguard Worker 
213*61c4878aSAndroid Build Coastguard Worker   JsonValue& overflow_at_unicode = json_builder.StartValue();
214*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), overflow_at_unicode.Set("234\x01"));
215*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("null", overflow_at_unicode.data());
216*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), overflow_at_unicode.Set("2345\x01"));
217*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("null", overflow_at_unicode.data());
218*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(),
219*61c4878aSAndroid Build Coastguard Worker             overflow_at_unicode.Set("23456789\x01"));
220*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("null", overflow_at_unicode.data());
221*61c4878aSAndroid Build Coastguard Worker }
222*61c4878aSAndroid Build Coastguard Worker 
TEST_F(JsonOverflowTest,ObjectOverflowAtNumber)223*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonOverflowTest, ObjectOverflowAtNumber) {
224*61c4878aSAndroid Build Coastguard Worker   JsonBuilder json_builder(buffer_, 14);
225*61c4878aSAndroid Build Coastguard Worker   MarkBufferEnd(json_builder);
226*61c4878aSAndroid Build Coastguard Worker 
227*61c4878aSAndroid Build Coastguard Worker   JsonObject& json = json_builder.StartObject();
228*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.Add("a", 123456).status());
229*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"a": 123456})", json.data());
230*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(json.max_size(), json.size());
231*61c4878aSAndroid Build Coastguard Worker   json.clear();
232*61c4878aSAndroid Build Coastguard Worker 
233*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), json.Add("a", 1234567).status());
234*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({})", json.data());
235*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(2u, json.size());
236*61c4878aSAndroid Build Coastguard Worker   json.clear();
237*61c4878aSAndroid Build Coastguard Worker 
238*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), json.Add("a", 12345678).status());
239*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({})", json.data());
240*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(2u, json.size());
241*61c4878aSAndroid Build Coastguard Worker }
242*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonObject,StringValueFillsAllSpace)243*61c4878aSAndroid Build Coastguard Worker TEST(JsonObject, StringValueFillsAllSpace) {
244*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<15> json_buffer;
245*61c4878aSAndroid Build Coastguard Worker 
246*61c4878aSAndroid Build Coastguard Worker   JsonObject& json = json_buffer.StartObject();
247*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.Add("key", "12\\").status());
248*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"key": "12\\"})", json.data());
249*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(15u, json.size());
250*61c4878aSAndroid Build Coastguard Worker 
251*61c4878aSAndroid Build Coastguard Worker   json.clear();
252*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), json.Add("key", "123\\").status());
253*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({})", json.data());
254*61c4878aSAndroid Build Coastguard Worker }
255*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonObject,NestedJson)256*61c4878aSAndroid Build Coastguard Worker TEST(JsonObject, NestedJson) {
257*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<64> outside_builder;
258*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<32> inside_builder;
259*61c4878aSAndroid Build Coastguard Worker 
260*61c4878aSAndroid Build Coastguard Worker   JsonObject& outside = outside_builder.StartObject();
261*61c4878aSAndroid Build Coastguard Worker   JsonObject& inside = inside_builder.StartObject();
262*61c4878aSAndroid Build Coastguard Worker 
263*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), inside.Add("inside", 123).status());
264*61c4878aSAndroid Build Coastguard Worker   ASSERT_STREQ(R"({"inside": 123})", inside.data());
265*61c4878aSAndroid Build Coastguard Worker 
266*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), outside.Add("some_value", inside).status());
267*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"some_value": {"inside": 123}})", outside.data());
268*61c4878aSAndroid Build Coastguard Worker 
269*61c4878aSAndroid Build Coastguard Worker   inside.clear();
270*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), outside.Add("MT", inside).status());
271*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"some_value": {"inside": 123}, "MT": {}})", outside.data());
272*61c4878aSAndroid Build Coastguard Worker 
273*61c4878aSAndroid Build Coastguard Worker   outside.AddNestedArray("key").Append(99).Append(1);
274*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(outside,
275*61c4878aSAndroid Build Coastguard Worker             R"({"some_value": {"inside": 123}, "MT": {}, "key": [99, 1]})"sv);
276*61c4878aSAndroid Build Coastguard Worker }
277*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonObject,NestedArrayOverflowWhenNesting)278*61c4878aSAndroid Build Coastguard Worker TEST(JsonObject, NestedArrayOverflowWhenNesting) {
279*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<5> buffer;
280*61c4878aSAndroid Build Coastguard Worker   JsonArray& array = buffer.StartArray();
281*61c4878aSAndroid Build Coastguard Worker   array.Append(123);
282*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(array, "[123]"sv);
283*61c4878aSAndroid Build Coastguard Worker 
284*61c4878aSAndroid Build Coastguard Worker   NestedJsonArray nested_array = array.AppendNestedArray();
285*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), array.status());
286*61c4878aSAndroid Build Coastguard Worker   nested_array.Append(1);
287*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(array, "[123]"sv);
288*61c4878aSAndroid Build Coastguard Worker }
289*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonObject,NestedArrayOverflowAppend)290*61c4878aSAndroid Build Coastguard Worker TEST(JsonObject, NestedArrayOverflowAppend) {
291*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<5> buffer;
292*61c4878aSAndroid Build Coastguard Worker   JsonArray& array = buffer.StartArray();
293*61c4878aSAndroid Build Coastguard Worker   NestedJsonArray nested_array = array.AppendNestedArray();
294*61c4878aSAndroid Build Coastguard Worker 
295*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), array.status());
296*61c4878aSAndroid Build Coastguard Worker   nested_array.Append(10);
297*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), array.status());
298*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(array, "[[]]"sv);
299*61c4878aSAndroid Build Coastguard Worker }
300*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonObject,NestedArrayOverflowSecondAppend)301*61c4878aSAndroid Build Coastguard Worker TEST(JsonObject, NestedArrayOverflowSecondAppend) {
302*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<7> buffer;
303*61c4878aSAndroid Build Coastguard Worker   JsonArray& array = buffer.StartArray();
304*61c4878aSAndroid Build Coastguard Worker   NestedJsonArray nested_array = array.AppendNestedArray();
305*61c4878aSAndroid Build Coastguard Worker 
306*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), array.status());
307*61c4878aSAndroid Build Coastguard Worker   nested_array.Append(1);
308*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(array, "[[1]]"sv);
309*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), array.status());
310*61c4878aSAndroid Build Coastguard Worker 
311*61c4878aSAndroid Build Coastguard Worker   nested_array.Append(2);
312*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(array, "[[1]]"sv);
313*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), array.status());
314*61c4878aSAndroid Build Coastguard Worker }
315*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonObject,NestedObjectOverflowWhenNesting)316*61c4878aSAndroid Build Coastguard Worker TEST(JsonObject, NestedObjectOverflowWhenNesting) {
317*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<5> buffer;
318*61c4878aSAndroid Build Coastguard Worker   JsonArray& array = buffer.StartArray();
319*61c4878aSAndroid Build Coastguard Worker   array.Append(123);
320*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(array, "[123]"sv);
321*61c4878aSAndroid Build Coastguard Worker 
322*61c4878aSAndroid Build Coastguard Worker   std::ignore = array.AppendNestedObject();
323*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), array.status());
324*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(array, "[123]"sv);
325*61c4878aSAndroid Build Coastguard Worker }
326*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonObject,NestedObjectOverflowAppend)327*61c4878aSAndroid Build Coastguard Worker TEST(JsonObject, NestedObjectOverflowAppend) {
328*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<5> buffer;
329*61c4878aSAndroid Build Coastguard Worker   JsonArray& array = buffer.StartArray();
330*61c4878aSAndroid Build Coastguard Worker   NestedJsonObject nested_object = array.AppendNestedObject();
331*61c4878aSAndroid Build Coastguard Worker 
332*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), array.status());
333*61c4878aSAndroid Build Coastguard Worker   nested_object.Add("k", 10);
334*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), array.status());
335*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(array, "[{}]"sv);
336*61c4878aSAndroid Build Coastguard Worker }
337*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonObject,NestedObjectOverflowSecondAppend)338*61c4878aSAndroid Build Coastguard Worker TEST(JsonObject, NestedObjectOverflowSecondAppend) {
339*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<14> buffer;
340*61c4878aSAndroid Build Coastguard Worker   JsonArray& array = buffer.StartArray();
341*61c4878aSAndroid Build Coastguard Worker   NestedJsonObject nested_object = array.AppendNestedObject();
342*61c4878aSAndroid Build Coastguard Worker 
343*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), array.status());
344*61c4878aSAndroid Build Coastguard Worker   nested_object.Add("k", 1);
345*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(array, R"([{"k": 1}])"sv);
346*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), array.status());
347*61c4878aSAndroid Build Coastguard Worker 
348*61c4878aSAndroid Build Coastguard Worker   nested_object.Add("K", 2);
349*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(array, R"([{"k": 1}])"sv);
350*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), array.status());
351*61c4878aSAndroid Build Coastguard Worker }
352*61c4878aSAndroid Build Coastguard Worker 
TEST_F(JsonOverflowTest,ObjectNestedJsonOverflow)353*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonOverflowTest, ObjectNestedJsonOverflow) {
354*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<32> inside_buffer;
355*61c4878aSAndroid Build Coastguard Worker   JsonObject& inside = inside_buffer.StartObject();
356*61c4878aSAndroid Build Coastguard Worker 
357*61c4878aSAndroid Build Coastguard Worker   JsonBuilder outside_builder(buffer_, 20);
358*61c4878aSAndroid Build Coastguard Worker   MarkBufferEnd(outside_builder);
359*61c4878aSAndroid Build Coastguard Worker   JsonObject& outside = outside_builder.StartObject();
360*61c4878aSAndroid Build Coastguard Worker 
361*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), inside.Add("k", 78).status());
362*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(9u, inside.size());  // 9 bytes, will fit
363*61c4878aSAndroid Build Coastguard Worker 
364*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), outside.Add("data", inside).status());
365*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"data": {"k": 78}})", outside.data());  // 20 bytes total
366*61c4878aSAndroid Build Coastguard Worker 
367*61c4878aSAndroid Build Coastguard Worker   inside.clear();
368*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), inside.Add("k", 789).status());
369*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(10u, inside.size());  // 10 bytes, won't fit
370*61c4878aSAndroid Build Coastguard Worker 
371*61c4878aSAndroid Build Coastguard Worker   outside.clear();
372*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), outside.Add("data", inside).status());
373*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), outside.last_status());
374*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), outside.status());
375*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({})", outside.data());
376*61c4878aSAndroid Build Coastguard Worker 
377*61c4878aSAndroid Build Coastguard Worker   inside.clear();
378*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), outside.Add("data", inside).last_status());
379*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), outside.last_status());
380*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), outside.status());
381*61c4878aSAndroid Build Coastguard Worker }
382*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonValue,BasicValues)383*61c4878aSAndroid Build Coastguard Worker TEST(JsonValue, BasicValues) {
384*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<13> json;
385*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.SetValue(-15));
386*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("-15", json.data());
387*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(3u, json.size());
388*61c4878aSAndroid Build Coastguard Worker 
389*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.SetValue(0));
390*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("0", json.data());
391*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(1u, json.size());
392*61c4878aSAndroid Build Coastguard Worker 
393*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.SetValue(static_cast<char>(35)));
394*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("35", json.data());
395*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(2u, json.size());
396*61c4878aSAndroid Build Coastguard Worker 
397*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.SetValue(nullptr));
398*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("null", json.data());
399*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(4u, json.size());
400*61c4878aSAndroid Build Coastguard Worker 
401*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.SetValue(static_cast<const char*>(nullptr)));
402*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("null", json.data());
403*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(4u, json.size());
404*61c4878aSAndroid Build Coastguard Worker 
405*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.SetValue(""));
406*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"("")", json.data());
407*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(2u, json.size());
408*61c4878aSAndroid Build Coastguard Worker 
409*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.SetValue("Hey\n!"));
410*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"("Hey\n!")", json.data());
411*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(8u, json.size());
412*61c4878aSAndroid Build Coastguard Worker 
413*61c4878aSAndroid Build Coastguard Worker   JsonValue& json_value = json.StartValue();
414*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("null", json_value.data());
415*61c4878aSAndroid Build Coastguard Worker 
416*61c4878aSAndroid Build Coastguard Worker   char str[] = R"(Qu"o"tes)";
417*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json_value.Set(str));
418*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"("Qu\"o\"tes")", json_value.data());
419*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(12u, json_value.size());
420*61c4878aSAndroid Build Coastguard Worker 
421*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json_value.Set(true));
422*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("true", json_value.data());
423*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(4u, json_value.size());
424*61c4878aSAndroid Build Coastguard Worker 
425*61c4878aSAndroid Build Coastguard Worker   bool false_value = false;
426*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.SetValue(false_value));
427*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("false", json.data());
428*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(5u, json.size());
429*61c4878aSAndroid Build Coastguard Worker 
430*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json_value.Set(static_cast<double>(1)));
431*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(json_value, "1"sv);
432*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json_value.Set(-1.0f));
433*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(json_value, "-1"sv);
434*61c4878aSAndroid Build Coastguard Worker }
435*61c4878aSAndroid Build Coastguard Worker 
436*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonOverflowTest, ValueOverflowUnquoted) {
437*61c4878aSAndroid Build Coastguard Worker   JsonBuilder json(buffer_, 5);
438*61c4878aSAndroid Build Coastguard Worker   MarkBufferEnd(json);
439*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(4u, json.max_size());
440*61c4878aSAndroid Build Coastguard Worker 
441*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), json.SetValue(12345));
442*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("null", json.data());
443*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(4u, json.size());
444*61c4878aSAndroid Build Coastguard Worker 
445*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.SetValue(1234));
446*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("1234", json.data());
447*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(4u, json.size());
448*61c4878aSAndroid Build Coastguard Worker 
449*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), json.SetValue(false));
450*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("null", json.data());
451*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(4u, json.size());
452*61c4878aSAndroid Build Coastguard Worker 
453*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.SetValue(true));
454*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("true", json.data());
455*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(4u, json.size());
456*61c4878aSAndroid Build Coastguard Worker }
457*61c4878aSAndroid Build Coastguard Worker 
458*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonOverflowTest, ValueOverflowQuoted) {
459*61c4878aSAndroid Build Coastguard Worker   JsonBuilder json(buffer_, 8);
460*61c4878aSAndroid Build Coastguard Worker   MarkBufferEnd(json);
461*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(7u, json.max_size());
462*61c4878aSAndroid Build Coastguard Worker 
463*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.SetValue("34567"));
464*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"("34567")", json.data());
465*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(7u, json.size());
466*61c4878aSAndroid Build Coastguard Worker 
467*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), json.SetValue("345678"));
468*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("null", json.data());
469*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(4u, json.size());
470*61c4878aSAndroid Build Coastguard Worker 
471*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.SetValue("567\n"));
472*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"("567\n")", json.data());
473*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(7u, json.size());
474*61c4878aSAndroid Build Coastguard Worker 
475*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), json.SetValue("5678\n"));
476*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("null", json.data());
477*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(4u, json.size());
478*61c4878aSAndroid Build Coastguard Worker 
479*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), json.SetValue("\x05"));
480*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("null", json.data());
481*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(4u, json.size());
482*61c4878aSAndroid Build Coastguard Worker 
483*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<9> bigger_json;
484*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), bigger_json.SetValue("\x05"));
485*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"("\u0005")", bigger_json.data());
486*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(8u, bigger_json.size());
487*61c4878aSAndroid Build Coastguard Worker }
488*61c4878aSAndroid Build Coastguard Worker 
489*61c4878aSAndroid Build Coastguard Worker TEST(JsonValue, NestedJson) {
490*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<11> json;
491*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<12> object_buffer;
492*61c4878aSAndroid Build Coastguard Worker   JsonObject& object = object_buffer.StartObject();
493*61c4878aSAndroid Build Coastguard Worker 
494*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), object.Add("3", 7890).status());
495*61c4878aSAndroid Build Coastguard Worker   ASSERT_STREQ(R"({"3": 7890})", object.data());
496*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(json.max_size(), object.size());
497*61c4878aSAndroid Build Coastguard Worker 
498*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.SetValue(object));
499*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"3": 7890})", json.data());
500*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(11u, json.size());
501*61c4878aSAndroid Build Coastguard Worker 
502*61c4878aSAndroid Build Coastguard Worker   object.clear();
503*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), object.Add("3", 78901).status());
504*61c4878aSAndroid Build Coastguard Worker   ASSERT_STREQ(R"({"3": 78901})", object.data());
505*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(object.max_size(), object.size());
506*61c4878aSAndroid Build Coastguard Worker   ASSERT_GT(object.size(), json.size());
507*61c4878aSAndroid Build Coastguard Worker 
508*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), json.SetValue(object));
509*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("null", json.data());
510*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(4u, json.size());
511*61c4878aSAndroid Build Coastguard Worker 
512*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<12> value;
513*61c4878aSAndroid Build Coastguard Worker   const char* something = nullptr;
514*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), value.SetValue(something));
515*61c4878aSAndroid Build Coastguard Worker 
516*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), json.SetValue(value));
517*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("null", json.data());
518*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(4u, json.size());
519*61c4878aSAndroid Build Coastguard Worker }
520*61c4878aSAndroid Build Coastguard Worker 
521*61c4878aSAndroid Build Coastguard Worker TEST(JsonValue, SetFromOtherJsonValue) {
522*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<32> first = JsonBuffer<32>::Value("$$02$ok$$C");
523*61c4878aSAndroid Build Coastguard Worker   constexpr const char kExpected[] = R"("$$02$ok$$C")";
524*61c4878aSAndroid Build Coastguard Worker   ASSERT_STREQ(kExpected, first.data());
525*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(sizeof(kExpected) - 1, first.size());
526*61c4878aSAndroid Build Coastguard Worker 
527*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<24> second;
528*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), second.SetValue(first));
529*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(kExpected, second.data());
530*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(sizeof(kExpected) - 1, second.size());
531*61c4878aSAndroid Build Coastguard Worker }
532*61c4878aSAndroid Build Coastguard Worker 
533*61c4878aSAndroid Build Coastguard Worker TEST(JsonValue, ToJsonValue) {
534*61c4878aSAndroid Build Coastguard Worker   static constexpr auto value = JsonBuffer<4>::Value(1234);
535*61c4878aSAndroid Build Coastguard Worker 
536*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("1234", value.data());
537*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("\"1234\"", JsonBuffer<6>::Value("1234").data());
538*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("null", JsonBuffer<4>::Value(nullptr).data());
539*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("false", JsonBuffer<5>::Value(false).data());
540*61c4878aSAndroid Build Coastguard Worker 
541*61c4878aSAndroid Build Coastguard Worker #if PW_NC_TEST(ValueDoesNotFit)
542*61c4878aSAndroid Build Coastguard Worker   PW_NC_EXPECT("PW_ASSERT\(json.SetValue\(initial_value\).ok\(\)\)");
543*61c4878aSAndroid Build Coastguard Worker   [[maybe_unused]] static constexpr auto fail = JsonBuffer<4>::Value(12345);
544*61c4878aSAndroid Build Coastguard Worker #endif  // PW_NC_TEST
545*61c4878aSAndroid Build Coastguard Worker }
546*61c4878aSAndroid Build Coastguard Worker 
__anon1fb128a40502null547*61c4878aSAndroid Build Coastguard Worker static_assert([] {
548*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<32> buffer;
549*61c4878aSAndroid Build Coastguard Worker   buffer.StartObject().Add("hello", "world").Add("ptr", nullptr);
550*61c4878aSAndroid Build Coastguard Worker   return buffer;
551*61c4878aSAndroid Build Coastguard Worker }() == std::string_view(R"({"hello": "world", "ptr": null})"));
552*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonArray,BasicUse)553*61c4878aSAndroid Build Coastguard Worker TEST(JsonArray, BasicUse) {
554*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<48> list_buffer;
555*61c4878aSAndroid Build Coastguard Worker   JsonArray& list = list_buffer.StartArray();
556*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), list.Append(nullptr).last_status());
557*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), list.Append("what").status());
558*61c4878aSAndroid Build Coastguard Worker 
559*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<96> big_list_buffer;
560*61c4878aSAndroid Build Coastguard Worker   JsonArray& big_list = big_list_buffer.StartArray();
561*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), big_list.Append(list).status());
562*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), big_list.Append(123).status());
563*61c4878aSAndroid Build Coastguard Worker 
564*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<48> object_buffer;
565*61c4878aSAndroid Build Coastguard Worker   JsonObject& object = object_buffer.StartObject();
566*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), object.Add("foo", "bar").status());
567*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), object.Add("bar", list).status());
568*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), big_list.Append(object).status());
569*61c4878aSAndroid Build Coastguard Worker 
570*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), big_list.Append('\0').status());
571*61c4878aSAndroid Build Coastguard Worker 
572*61c4878aSAndroid Build Coastguard Worker   std::array<bool, 2> bools{true, false};
573*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), big_list.Extend(bools).status());
574*61c4878aSAndroid Build Coastguard Worker 
575*61c4878aSAndroid Build Coastguard Worker   const char kExpected[] =
576*61c4878aSAndroid Build Coastguard Worker       R"([[null, "what"], 123, {"foo": "bar", "bar": [null, "what"]}, )"
577*61c4878aSAndroid Build Coastguard Worker       R"(0, true, false])";
578*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(kExpected, big_list.data());
579*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(sizeof(kExpected) - 1, big_list.size());
580*61c4878aSAndroid Build Coastguard Worker }
581*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonArray,FromArray)582*61c4878aSAndroid Build Coastguard Worker TEST(JsonArray, FromArray) {
583*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<31> array_buffer;
584*61c4878aSAndroid Build Coastguard Worker   JsonArray& array = array_buffer.StartArray();
585*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), array.Extend({1, 2, 3, 4, 5}).status());
586*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("[1, 2, 3, 4, 5]", array.data());
587*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(15u, array.size());
588*61c4878aSAndroid Build Coastguard Worker 
589*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), array.Extend({6, 7, 8, 9, 0}).status());
590*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]", array.data());
591*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(30u, array.size());
592*61c4878aSAndroid Build Coastguard Worker }
593*61c4878aSAndroid Build Coastguard Worker 
TEST_F(JsonOverflowTest,FromArrayOverflow)594*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonOverflowTest, FromArrayOverflow) {
595*61c4878aSAndroid Build Coastguard Worker   JsonBuilder array_buffer(buffer_, 31);
596*61c4878aSAndroid Build Coastguard Worker   MarkBufferEnd(array_buffer);
597*61c4878aSAndroid Build Coastguard Worker   JsonArray& array = array_buffer.StartArray();
598*61c4878aSAndroid Build Coastguard Worker 
599*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), array.Extend({1, 2, 3, 4, 5}).status());
600*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("[1, 2, 3, 4, 5]", array.data());
601*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(15u, array.size());
602*61c4878aSAndroid Build Coastguard Worker 
603*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(),
604*61c4878aSAndroid Build Coastguard Worker             array.Extend({6, 7, 8, 9, 0, 1, 2, 3}).status());
605*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("[1, 2, 3, 4, 5]", array.data());
606*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(15u, array.size());
607*61c4878aSAndroid Build Coastguard Worker 
608*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(),
609*61c4878aSAndroid Build Coastguard Worker             array.Extend({6, 7, 8}).Extend({9, 0}).status());
610*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]", array.data());
611*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(30u, array.size());
612*61c4878aSAndroid Build Coastguard Worker 
613*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), array.Extend({5}).status());
614*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]", array.data());
615*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(30u, array.size());
616*61c4878aSAndroid Build Coastguard Worker }
617*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonArray,AppendIndividualExtendContainer)618*61c4878aSAndroid Build Coastguard Worker TEST(JsonArray, AppendIndividualExtendContainer) {
619*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<64> array_buffer;
620*61c4878aSAndroid Build Coastguard Worker   JsonArray& array = array_buffer.StartArray();
621*61c4878aSAndroid Build Coastguard Worker   constexpr int kInts[] = {1, 2, 3};
622*61c4878aSAndroid Build Coastguard Worker #if PW_NC_TEST(CannotAppendArrays)
623*61c4878aSAndroid Build Coastguard Worker   PW_NC_EXPECT("JSON values may only be numbers, strings, JSON");
624*61c4878aSAndroid Build Coastguard Worker   array.Append(kInts);
625*61c4878aSAndroid Build Coastguard Worker #endif  // PW_NC_TEST
626*61c4878aSAndroid Build Coastguard Worker 
627*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), array.Extend(kInts).status());
628*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("[1, 2, 3]", array.data());
629*61c4878aSAndroid Build Coastguard Worker }
630*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonArray,NestingArray)631*61c4878aSAndroid Build Coastguard Worker TEST(JsonArray, NestingArray) {
632*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<64> array_buffer;
633*61c4878aSAndroid Build Coastguard Worker   JsonArray& array = array_buffer.StartArray();
634*61c4878aSAndroid Build Coastguard Worker   std::ignore = array.AppendNestedArray();
635*61c4878aSAndroid Build Coastguard Worker 
636*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(array.data(), "[[]]");
637*61c4878aSAndroid Build Coastguard Worker 
638*61c4878aSAndroid Build Coastguard Worker   NestedJsonArray nested = array.AppendNestedArray();
639*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), array.last_status());
640*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(array.data(), "[[], []]");
641*61c4878aSAndroid Build Coastguard Worker 
642*61c4878aSAndroid Build Coastguard Worker   nested.Append(123);
643*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(array.size(), sizeof("[[], [123]]") - 1);
644*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(array.data(), "[[], [123]]");
645*61c4878aSAndroid Build Coastguard Worker 
646*61c4878aSAndroid Build Coastguard Worker   nested.Append("");
647*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(array.data(), "[[], [123, \"\"]]");
648*61c4878aSAndroid Build Coastguard Worker }
649*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonArray,NestingObject)650*61c4878aSAndroid Build Coastguard Worker TEST(JsonArray, NestingObject) {
651*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<64> array_buffer;
652*61c4878aSAndroid Build Coastguard Worker   JsonArray& array = array_buffer.StartArray();
653*61c4878aSAndroid Build Coastguard Worker   NestedJsonObject object = array.AppendNestedObject();
654*61c4878aSAndroid Build Coastguard Worker 
655*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(array.data(), "[{}]");
656*61c4878aSAndroid Build Coastguard Worker 
657*61c4878aSAndroid Build Coastguard Worker   object.Add("key", 123);
658*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(array, R"([{"key": 123}])"sv);
659*61c4878aSAndroid Build Coastguard Worker 
660*61c4878aSAndroid Build Coastguard Worker   object.Add("k", true);
661*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(array, R"([{"key": 123, "k": true}])"sv);
662*61c4878aSAndroid Build Coastguard Worker 
663*61c4878aSAndroid Build Coastguard Worker   array.AppendNestedArray().Append("done").Append("!");
664*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(array, R"([{"key": 123, "k": true}, ["done", "!"]])"sv);
665*61c4878aSAndroid Build Coastguard Worker }
666*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonBuilder,DeepNesting)667*61c4878aSAndroid Build Coastguard Worker TEST(JsonBuilder, DeepNesting) {
668*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<64> buffer;
669*61c4878aSAndroid Build Coastguard Worker   JsonArray& arr1 = buffer.StartArray();
670*61c4878aSAndroid Build Coastguard Worker   std::ignore = arr1.AppendNestedObject();
671*61c4878aSAndroid Build Coastguard Worker 
672*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(buffer, "[{}]"sv);
673*61c4878aSAndroid Build Coastguard Worker 
674*61c4878aSAndroid Build Coastguard Worker   auto arr2 = arr1.AppendNestedObject().Add("a", 1).AddNestedArray("b");
675*61c4878aSAndroid Build Coastguard Worker   arr2.Append(0).Append(1).AppendNestedObject().Add("yes", "no");
676*61c4878aSAndroid Build Coastguard Worker   arr2.Append(2);
677*61c4878aSAndroid Build Coastguard Worker 
678*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(buffer, R"([{}, {"a": 1, "b": [0, 1, {"yes": "no"}, 2]}])"sv);
679*61c4878aSAndroid Build Coastguard Worker 
680*61c4878aSAndroid Build Coastguard Worker   arr1.Append(true);
681*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(buffer, R"([{}, {"a": 1, "b": [0, 1, {"yes": "no"}, 2]}, true])"sv);
682*61c4878aSAndroid Build Coastguard Worker }
683*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonBuilder,ConvertBetween)684*61c4878aSAndroid Build Coastguard Worker TEST(JsonBuilder, ConvertBetween) {
685*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<64> buffer;
686*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("null", buffer.data());
687*61c4878aSAndroid Build Coastguard Worker   EXPECT_TRUE(buffer.IsValue());
688*61c4878aSAndroid Build Coastguard Worker   EXPECT_FALSE(buffer.IsObject());
689*61c4878aSAndroid Build Coastguard Worker   EXPECT_FALSE(buffer.IsArray());
690*61c4878aSAndroid Build Coastguard Worker 
691*61c4878aSAndroid Build Coastguard Worker   JsonObject& object = buffer.StartObject();
692*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("{}", buffer.data());
693*61c4878aSAndroid Build Coastguard Worker   EXPECT_FALSE(object.IsValue());
694*61c4878aSAndroid Build Coastguard Worker   EXPECT_FALSE(object.IsArray());
695*61c4878aSAndroid Build Coastguard Worker   EXPECT_TRUE(object.IsObject());
696*61c4878aSAndroid Build Coastguard Worker   object.Add("123", true);
697*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"123": true})", buffer.data());
698*61c4878aSAndroid Build Coastguard Worker 
699*61c4878aSAndroid Build Coastguard Worker   JsonArray& array = buffer.StartArray();
700*61c4878aSAndroid Build Coastguard Worker 
701*61c4878aSAndroid Build Coastguard Worker   EXPECT_FALSE(object.IsObject()) << "No longer an object";
702*61c4878aSAndroid Build Coastguard Worker   EXPECT_TRUE(object.ok()) << "Still OK, just not an object";
703*61c4878aSAndroid Build Coastguard Worker   EXPECT_FALSE(array.IsValue());
704*61c4878aSAndroid Build Coastguard Worker   EXPECT_TRUE(array.IsArray());
705*61c4878aSAndroid Build Coastguard Worker   EXPECT_FALSE(array.IsObject());
706*61c4878aSAndroid Build Coastguard Worker 
707*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("[]", buffer.data());
708*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), array.Extend({1, 2, 3}).status());
709*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("[1, 2, 3]", buffer.data());
710*61c4878aSAndroid Build Coastguard Worker 
711*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), array.Append(false).Append(-1).status());
712*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("[1, 2, 3, false, -1]", buffer.data());
713*61c4878aSAndroid Build Coastguard Worker 
714*61c4878aSAndroid Build Coastguard Worker   object.clear();
715*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), object.Add("yes", nullptr).status());
716*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(R"({"yes": null})", buffer.data());
717*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), buffer.status());
718*61c4878aSAndroid Build Coastguard Worker }
719*61c4878aSAndroid Build Coastguard Worker 
__anon1fb128a40602null720*61c4878aSAndroid Build Coastguard Worker static_assert([] {
721*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<64> buffer;
722*61c4878aSAndroid Build Coastguard Worker   auto& object = buffer.StartObject();
723*61c4878aSAndroid Build Coastguard Worker   auto nested_array = object.AddNestedArray("array");
724*61c4878aSAndroid Build Coastguard Worker   nested_array.Append(1);
725*61c4878aSAndroid Build Coastguard Worker   object.Add("key", "value");
726*61c4878aSAndroid Build Coastguard Worker   if (buffer != R"({"array": [1], "key": "value"})"sv) {
727*61c4878aSAndroid Build Coastguard Worker     return false;
728*61c4878aSAndroid Build Coastguard Worker   }
729*61c4878aSAndroid Build Coastguard Worker 
730*61c4878aSAndroid Build Coastguard Worker #if PW_NC_TEST(NestedJsonAttemptsToDetectWrongType)
731*61c4878aSAndroid Build Coastguard Worker   PW_NC_EXPECT("PW_ASSERT\(.*// Nested structure must match the expected type");
732*61c4878aSAndroid Build Coastguard Worker   nested_array.Append(2);
733*61c4878aSAndroid Build Coastguard Worker #elif PW_NC_TEST(NestedJsonDetectsClearedJson)
734*61c4878aSAndroid Build Coastguard Worker   PW_NC_EXPECT("PW_ASSERT\(.*// JSON must not have been cleared since nesting");
735*61c4878aSAndroid Build Coastguard Worker   object.clear();
736*61c4878aSAndroid Build Coastguard Worker   nested_array.Append(2);
737*61c4878aSAndroid Build Coastguard Worker #endif  // PW_NC_TEST
738*61c4878aSAndroid Build Coastguard Worker   return true;
739*61c4878aSAndroid Build Coastguard Worker }());
740*61c4878aSAndroid Build Coastguard Worker 
__anon1fb128a40702null741*61c4878aSAndroid Build Coastguard Worker static_assert([] {
742*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<64> buffer;
743*61c4878aSAndroid Build Coastguard Worker   auto& array = buffer.StartArray();
744*61c4878aSAndroid Build Coastguard Worker 
745*61c4878aSAndroid Build Coastguard Worker   NestedJsonArray nested = array.AppendNestedArray();
746*61c4878aSAndroid Build Coastguard Worker   for (int i = 1; i < 16; ++i) {
747*61c4878aSAndroid Build Coastguard Worker     nested = nested.AppendNestedArray();
748*61c4878aSAndroid Build Coastguard Worker   }
749*61c4878aSAndroid Build Coastguard Worker   // 17 arrays total (1 outer array, 16 levels of nesting inside it).
750*61c4878aSAndroid Build Coastguard Worker   //              1234567890123456712345678901234567
751*61c4878aSAndroid Build Coastguard Worker   if (array != R"([[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]])"sv) {
752*61c4878aSAndroid Build Coastguard Worker     return JsonBuffer<64>{};
753*61c4878aSAndroid Build Coastguard Worker   }
754*61c4878aSAndroid Build Coastguard Worker   nested.Append("-_-");
755*61c4878aSAndroid Build Coastguard Worker 
756*61c4878aSAndroid Build Coastguard Worker #if PW_NC_TEST(NestingLimit)
757*61c4878aSAndroid Build Coastguard Worker   PW_NC_EXPECT(
758*61c4878aSAndroid Build Coastguard Worker       "PW_ASSERT\(.*// Arrays or objects may be nested at most 17 times");
759*61c4878aSAndroid Build Coastguard Worker   std::ignore = nested.AppendNestedArray();
760*61c4878aSAndroid Build Coastguard Worker #endif  // PW_NC_TEST
761*61c4878aSAndroid Build Coastguard Worker   return buffer;
762*61c4878aSAndroid Build Coastguard Worker }() == R"([[[[[[[[[[[[[[[[["-_-"]]]]]]]]]]]]]]]]])"sv);
763*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonBuffer,SetClear)764*61c4878aSAndroid Build Coastguard Worker TEST(JsonBuffer, SetClear) {
765*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<4> buffer;
766*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(buffer, "null"sv);
767*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), buffer.SetValue(""));
768*61c4878aSAndroid Build Coastguard Worker   EXPECT_TRUE(buffer.ok());
769*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(buffer, "\"\""sv);
770*61c4878aSAndroid Build Coastguard Worker 
771*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(Status::ResourceExhausted(), buffer.SetValue("234"));
772*61c4878aSAndroid Build Coastguard Worker   EXPECT_FALSE(buffer.ok());
773*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(buffer, "null"sv);
774*61c4878aSAndroid Build Coastguard Worker 
775*61c4878aSAndroid Build Coastguard Worker   buffer.clear();
776*61c4878aSAndroid Build Coastguard Worker   EXPECT_TRUE(buffer.ok());
777*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(buffer, "null"sv);
778*61c4878aSAndroid Build Coastguard Worker }
779*61c4878aSAndroid Build Coastguard Worker 
TEST(JsonBuffer,Copy)780*61c4878aSAndroid Build Coastguard Worker TEST(JsonBuffer, Copy) {
781*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<64> foo;
782*61c4878aSAndroid Build Coastguard Worker   ASSERT_EQ(OkStatus(), foo.SetValue("yes"));
783*61c4878aSAndroid Build Coastguard Worker 
784*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<48> bar;
785*61c4878aSAndroid Build Coastguard Worker   auto& object = bar.StartObject().Add("no", true);
786*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(object, R"({"no": true})"sv);
787*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), bar.StartArray().Append(1).Append(2).status());
788*61c4878aSAndroid Build Coastguard Worker 
789*61c4878aSAndroid Build Coastguard Worker   foo = bar;
790*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("[1, 2]", foo.data());
791*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(6u, foo.size());
792*61c4878aSAndroid Build Coastguard Worker 
793*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<128> baz(foo);
794*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(foo.data(), baz.data());
795*61c4878aSAndroid Build Coastguard Worker }
796*61c4878aSAndroid Build Coastguard Worker 
797*61c4878aSAndroid Build Coastguard Worker // Tests character escaping using a table generated with the following Python:
798*61c4878aSAndroid Build Coastguard Worker //
799*61c4878aSAndroid Build Coastguard Worker // import json
800*61c4878aSAndroid Build Coastguard Worker // print(', '.join('R"_({})_"'.format(json.dumps(chr(i))) for i in range(128)))
TEST(JsonBuilder,TestEscape)801*61c4878aSAndroid Build Coastguard Worker TEST(JsonBuilder, TestEscape) {
802*61c4878aSAndroid Build Coastguard Worker   static constexpr std::array<const char*, 128> kEscapedCharacters = {
803*61c4878aSAndroid Build Coastguard Worker       R"_("\u0000")_", R"_("\u0001")_", R"_("\u0002")_", R"_("\u0003")_",
804*61c4878aSAndroid Build Coastguard Worker       R"_("\u0004")_", R"_("\u0005")_", R"_("\u0006")_", R"_("\u0007")_",
805*61c4878aSAndroid Build Coastguard Worker       R"_("\b")_",     R"_("\t")_",     R"_("\n")_",     R"_("\u000b")_",
806*61c4878aSAndroid Build Coastguard Worker       R"_("\f")_",     R"_("\r")_",     R"_("\u000e")_", R"_("\u000f")_",
807*61c4878aSAndroid Build Coastguard Worker       R"_("\u0010")_", R"_("\u0011")_", R"_("\u0012")_", R"_("\u0013")_",
808*61c4878aSAndroid Build Coastguard Worker       R"_("\u0014")_", R"_("\u0015")_", R"_("\u0016")_", R"_("\u0017")_",
809*61c4878aSAndroid Build Coastguard Worker       R"_("\u0018")_", R"_("\u0019")_", R"_("\u001a")_", R"_("\u001b")_",
810*61c4878aSAndroid Build Coastguard Worker       R"_("\u001c")_", R"_("\u001d")_", R"_("\u001e")_", R"_("\u001f")_",
811*61c4878aSAndroid Build Coastguard Worker       R"_(" ")_",      R"_("!")_",      R"_("\"")_",     R"_("#")_",
812*61c4878aSAndroid Build Coastguard Worker       R"_("$")_",      R"_("%")_",      R"_("&")_",      R"_("'")_",
813*61c4878aSAndroid Build Coastguard Worker       R"_("(")_",      R"_(")")_",      R"_("*")_",      R"_("+")_",
814*61c4878aSAndroid Build Coastguard Worker       R"_(",")_",      R"_("-")_",      R"_(".")_",      R"_("/")_",
815*61c4878aSAndroid Build Coastguard Worker       R"_("0")_",      R"_("1")_",      R"_("2")_",      R"_("3")_",
816*61c4878aSAndroid Build Coastguard Worker       R"_("4")_",      R"_("5")_",      R"_("6")_",      R"_("7")_",
817*61c4878aSAndroid Build Coastguard Worker       R"_("8")_",      R"_("9")_",      R"_(":")_",      R"_(";")_",
818*61c4878aSAndroid Build Coastguard Worker       R"_("<")_",      R"_("=")_",      R"_(">")_",      R"_("?")_",
819*61c4878aSAndroid Build Coastguard Worker       R"_("@")_",      R"_("A")_",      R"_("B")_",      R"_("C")_",
820*61c4878aSAndroid Build Coastguard Worker       R"_("D")_",      R"_("E")_",      R"_("F")_",      R"_("G")_",
821*61c4878aSAndroid Build Coastguard Worker       R"_("H")_",      R"_("I")_",      R"_("J")_",      R"_("K")_",
822*61c4878aSAndroid Build Coastguard Worker       R"_("L")_",      R"_("M")_",      R"_("N")_",      R"_("O")_",
823*61c4878aSAndroid Build Coastguard Worker       R"_("P")_",      R"_("Q")_",      R"_("R")_",      R"_("S")_",
824*61c4878aSAndroid Build Coastguard Worker       R"_("T")_",      R"_("U")_",      R"_("V")_",      R"_("W")_",
825*61c4878aSAndroid Build Coastguard Worker       R"_("X")_",      R"_("Y")_",      R"_("Z")_",      R"_("[")_",
826*61c4878aSAndroid Build Coastguard Worker       R"_("\\")_",     R"_("]")_",      R"_("^")_",      R"_("_")_",
827*61c4878aSAndroid Build Coastguard Worker       R"_("`")_",      R"_("a")_",      R"_("b")_",      R"_("c")_",
828*61c4878aSAndroid Build Coastguard Worker       R"_("d")_",      R"_("e")_",      R"_("f")_",      R"_("g")_",
829*61c4878aSAndroid Build Coastguard Worker       R"_("h")_",      R"_("i")_",      R"_("j")_",      R"_("k")_",
830*61c4878aSAndroid Build Coastguard Worker       R"_("l")_",      R"_("m")_",      R"_("n")_",      R"_("o")_",
831*61c4878aSAndroid Build Coastguard Worker       R"_("p")_",      R"_("q")_",      R"_("r")_",      R"_("s")_",
832*61c4878aSAndroid Build Coastguard Worker       R"_("t")_",      R"_("u")_",      R"_("v")_",      R"_("w")_",
833*61c4878aSAndroid Build Coastguard Worker       R"_("x")_",      R"_("y")_",      R"_("z")_",      R"_("{")_",
834*61c4878aSAndroid Build Coastguard Worker       R"_("|")_",      R"_("}")_",      R"_("~")_",      R"_("\u007f")_"};
835*61c4878aSAndroid Build Coastguard Worker 
836*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<9> buffer;
837*61c4878aSAndroid Build Coastguard Worker 
838*61c4878aSAndroid Build Coastguard Worker   for (size_t i = 0; i < kEscapedCharacters.size(); ++i) {
839*61c4878aSAndroid Build Coastguard Worker     const char character = static_cast<char>(i);
840*61c4878aSAndroid Build Coastguard Worker     ASSERT_EQ(OkStatus(), buffer.SetValue(std::string_view(&character, 1)));
841*61c4878aSAndroid Build Coastguard Worker     ASSERT_STREQ(kEscapedCharacters[i], buffer.data());
842*61c4878aSAndroid Build Coastguard Worker   }
843*61c4878aSAndroid Build Coastguard Worker }
844*61c4878aSAndroid Build Coastguard Worker 
845*61c4878aSAndroid Build Coastguard Worker class JsonObjectTest : public ::testing::Test {
846*61c4878aSAndroid Build Coastguard Worker  protected:
847*61c4878aSAndroid Build Coastguard Worker   static constexpr size_t kMaxSize = 127;
848*61c4878aSAndroid Build Coastguard Worker   static constexpr size_t kBufferSize = kMaxSize + 1;
849*61c4878aSAndroid Build Coastguard Worker 
850*61c4878aSAndroid Build Coastguard Worker   JsonObjectTest() : object_(json_buffer_.StartObject()) {}
851*61c4878aSAndroid Build Coastguard Worker 
852*61c4878aSAndroid Build Coastguard Worker   JsonBuffer<kMaxSize> json_buffer_;
853*61c4878aSAndroid Build Coastguard Worker   JsonObject& object_;
854*61c4878aSAndroid Build Coastguard Worker };
855*61c4878aSAndroid Build Coastguard Worker 
856*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonObjectTest, TestSingleStringValue) {
857*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), object_.Add("key", "value").status());
858*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("{\"key\": \"value\"}", object_.data());
859*61c4878aSAndroid Build Coastguard Worker }
860*61c4878aSAndroid Build Coastguard Worker 
861*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonObjectTest, TestEscapedQuoteString) {
862*61c4878aSAndroid Build Coastguard Worker   const char* buf = "{\"key\": \"\\\"value\\\"\"}";
863*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(buf, object_.Add("key", "\"value\"").data());
864*61c4878aSAndroid Build Coastguard Worker }
865*61c4878aSAndroid Build Coastguard Worker 
866*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonObjectTest, TestEscapedSlashString) {
867*61c4878aSAndroid Build Coastguard Worker   const char* buf = "{\"key\": \"\\\\\"}";
868*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(buf, object_.Add("key", "\\").data());
869*61c4878aSAndroid Build Coastguard Worker }
870*61c4878aSAndroid Build Coastguard Worker 
871*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonObjectTest, TestEscapedCharactersString) {
872*61c4878aSAndroid Build Coastguard Worker   const char* buf = "{\"key\": \"\\r\\n\\t\"}";
873*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ(buf, object_.Add("key", "\r\n\t").data());
874*61c4878aSAndroid Build Coastguard Worker }
875*61c4878aSAndroid Build Coastguard Worker 
876*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonObjectTest, TestEscapedControlCharacterString) {
877*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("{\"key\": \"\\u001f\"}", object_.Add("key", "\x1F").data());
878*61c4878aSAndroid Build Coastguard Worker   object_.clear();
879*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("{\"key\": \"\\u0080\"}", object_.Add("key", "\x80").data());
880*61c4878aSAndroid Build Coastguard Worker }
881*61c4878aSAndroid Build Coastguard Worker 
882*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonObjectTest, TestNullptrString) {
883*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("{\"key\": null}",
884*61c4878aSAndroid Build Coastguard Worker                object_.Add("key", static_cast<const char*>(nullptr)).data());
885*61c4878aSAndroid Build Coastguard Worker }
886*61c4878aSAndroid Build Coastguard Worker 
887*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonObjectTest, TestCharValue) {
888*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("{\"key\": 88}",
889*61c4878aSAndroid Build Coastguard Worker                object_.Add("key", static_cast<unsigned char>('X')).data());
890*61c4878aSAndroid Build Coastguard Worker   object_.clear();
891*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("{\"key\": 88}", object_.Add("key", 'X').data());
892*61c4878aSAndroid Build Coastguard Worker }
893*61c4878aSAndroid Build Coastguard Worker 
894*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonObjectTest, TestShortValue) {
895*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("{\"key\": 88}",
896*61c4878aSAndroid Build Coastguard Worker                object_.Add("key", static_cast<unsigned short>(88)).data());
897*61c4878aSAndroid Build Coastguard Worker   object_.clear();
898*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("{\"key\": -88}",
899*61c4878aSAndroid Build Coastguard Worker                object_.Add("key", static_cast<short>(-88)).data());
900*61c4878aSAndroid Build Coastguard Worker }
901*61c4878aSAndroid Build Coastguard Worker 
902*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonObjectTest, TestIntValue) {
903*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("{\"key\": 88}",
904*61c4878aSAndroid Build Coastguard Worker                object_.Add("key", static_cast<unsigned int>(88)).data());
905*61c4878aSAndroid Build Coastguard Worker   object_.clear();
906*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("{\"key\": -88}", object_.Add("key", -88).data());
907*61c4878aSAndroid Build Coastguard Worker }
908*61c4878aSAndroid Build Coastguard Worker 
909*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonObjectTest, TestLongValue) {
910*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("{\"key\": 88}", object_.Add("key", 88UL).data());
911*61c4878aSAndroid Build Coastguard Worker   object_.clear();
912*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("{\"key\": -88}", object_.Add("key", -88L).data());
913*61c4878aSAndroid Build Coastguard Worker }
914*61c4878aSAndroid Build Coastguard Worker 
915*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonObjectTest, TestMultipleValues) {
916*61c4878aSAndroid Build Coastguard Worker   char buf[16] = "nonconst";
917*61c4878aSAndroid Build Coastguard Worker   EXPECT_STREQ("{\"one\": \"nonconst\", \"two\": null, \"three\": -3}",
918*61c4878aSAndroid Build Coastguard Worker                object_.Add("one", buf)
919*61c4878aSAndroid Build Coastguard Worker                    .Add("two", static_cast<char*>(nullptr))
920*61c4878aSAndroid Build Coastguard Worker                    .Add("three", -3)
921*61c4878aSAndroid Build Coastguard Worker                    .data());
922*61c4878aSAndroid Build Coastguard Worker }
923*61c4878aSAndroid Build Coastguard Worker 
924*61c4878aSAndroid Build Coastguard Worker TEST_F(JsonObjectTest, TestOverflow) {
925*61c4878aSAndroid Build Coastguard Worker   // Create a buffer that is just large enough to overflow with "key".
926*61c4878aSAndroid Build Coastguard Worker   std::array<char, kBufferSize + 1 /* NUL */ - (sizeof("{\"key\": \"\"}") - 1)>
927*61c4878aSAndroid Build Coastguard Worker       buf;
928*61c4878aSAndroid Build Coastguard Worker   std::memset(buf.data(), 'z', sizeof(buf));
929*61c4878aSAndroid Build Coastguard Worker   buf[sizeof(buf) - 1] = '\0';
930*61c4878aSAndroid Build Coastguard Worker 
931*61c4878aSAndroid Build Coastguard Worker   // Make sure the overflow happens at exactly the right character.
932*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(),
933*61c4878aSAndroid Build Coastguard Worker             object_.Add("key", buf.data()).status());
934*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(Status::ResourceExhausted(), object_.status());
935*61c4878aSAndroid Build Coastguard Worker 
936*61c4878aSAndroid Build Coastguard Worker   object_.clear();
937*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), object_.Add("ke", buf.data()).status());
938*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(OkStatus(), object_.status());
939*61c4878aSAndroid Build Coastguard Worker 
940*61c4878aSAndroid Build Coastguard Worker   // Ensure the internal buffer is NUL-terminated still, even on overflow.
941*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(object_.data()[object_.size()], '\0');
942*61c4878aSAndroid Build Coastguard Worker }
943*61c4878aSAndroid Build Coastguard Worker 
944*61c4878aSAndroid Build Coastguard Worker }  // namespace
945*61c4878aSAndroid Build Coastguard Worker }  // namespace pw
946