• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //     __ _____ _____ _____
2 //  __|  |   __|     |   | |  JSON for Modern C++ (supporting code)
3 // |  |  |__   |  |  | | | |  version 3.11.3
4 // |_____|_____|_____|_|___|  https://github.com/nlohmann/json
5 //
6 // SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
7 // SPDX-License-Identifier: MIT
8 
9 #include "doctest_compatibility.h"
10 
11 #include <nlohmann/json.hpp>
12 using nlohmann::json;
13 
14 #include <sstream>
15 #include <iomanip>
16 
17 TEST_CASE("serialization")
18 {
19     SECTION("operator<<")
20     {
21         SECTION("no given width")
22         {
23             std::stringstream ss;
24             const json j = {"foo", 1, 2, 3, false, {{"one", 1}}};
25             ss << j;
26             CHECK(ss.str() == "[\"foo\",1,2,3,false,{\"one\":1}]");
27         }
28 
29         SECTION("given width")
30         {
31             std::stringstream ss;
32             const json j = {"foo", 1, 2, 3, false, {{"one", 1}}};
33             ss << std::setw(4) << j;
34             CHECK(ss.str() ==
35                   "[\n    \"foo\",\n    1,\n    2,\n    3,\n    false,\n    {\n        \"one\": 1\n    }\n]");
36         }
37 
38         SECTION("given fill")
39         {
40             std::stringstream ss;
41             const json j = {"foo", 1, 2, 3, false, {{"one", 1}}};
42             ss << std::setw(1) << std::setfill('\t') << j;
43             CHECK(ss.str() ==
44                   "[\n\t\"foo\",\n\t1,\n\t2,\n\t3,\n\tfalse,\n\t{\n\t\t\"one\": 1\n\t}\n]");
45         }
46     }
47 
48     SECTION("operator>>")
49     {
50         SECTION("no given width")
51         {
52             std::stringstream ss;
53             const json j = {"foo", 1, 2, 3, false, {{"one", 1}}};
54             j >> ss;
55             CHECK(ss.str() == "[\"foo\",1,2,3,false,{\"one\":1}]");
56         }
57 
58         SECTION("given width")
59         {
60             std::stringstream ss;
61             const json j = {"foo", 1, 2, 3, false, {{"one", 1}}};
62             ss.width(4);
63             j >> ss;
64             CHECK(ss.str() ==
65                   "[\n    \"foo\",\n    1,\n    2,\n    3,\n    false,\n    {\n        \"one\": 1\n    }\n]");
66         }
67 
68         SECTION("given fill")
69         {
70             std::stringstream ss;
71             const json j = {"foo", 1, 2, 3, false, {{"one", 1}}};
72             ss.width(1);
73             ss.fill('\t');
74             j >> ss;
75             CHECK(ss.str() ==
76                   "[\n\t\"foo\",\n\t1,\n\t2,\n\t3,\n\tfalse,\n\t{\n\t\t\"one\": 1\n\t}\n]");
77         }
78     }
79 
80     SECTION("dump")
81     {
82         SECTION("invalid character")
83         {
84             const json j = "ä\xA9ü";
85 
86             CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&);
87             CHECK_THROWS_WITH_AS(j.dump(1, ' ', false, json::error_handler_t::strict), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&);
88             CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"äü\"");
89             CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"ä\xEF\xBF\xBDü\"");
90             CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"\\u00e4\\ufffd\\u00fc\"");
91         }
92 
93         SECTION("ending with incomplete character")
94         {
95             const json j = "123\xC2";
96 
97             CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2", json::type_error&);
98             CHECK_THROWS_AS(j.dump(1, ' ', false, json::error_handler_t::strict), json::type_error&);
99             CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123\"");
100             CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\"");
101             CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"123\\ufffd\"");
102         }
103 
104         SECTION("unexpected character")
105         {
106             const json j = "123\xF1\xB0\x34\x35\x36";
107 
108             CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 5: 0x34", json::type_error&);
109             CHECK_THROWS_AS(j.dump(1, ' ', false, json::error_handler_t::strict), json::type_error&);
110             CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123456\"");
111             CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\x34\x35\x36\"");
112             CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"123\\ufffd456\"");
113         }
114 
115         SECTION("U+FFFD Substitution of Maximal Subparts")
116         {
117             // Some tests (mostly) from
118             // https://www.unicode.org/versions/Unicode11.0.0/ch03.pdf
119             // Section 3.9 -- U+FFFD Substitution of Maximal Subparts
120 
121             auto test = [&](std::string const & input, std::string const & expected)
__anon045d28220102(std::string const & input, std::string const & expected) 122             {
123                 const json j = input;
124                 CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"" + expected + "\"");
125             };
126 
127             test("\xC2", "\\ufffd");
128             test("\xC2\x41\x42", "\\ufffd" "\x41" "\x42");
129             test("\xC2\xF4", "\\ufffd" "\\ufffd");
130 
131             test("\xF0\x80\x80\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
132             test("\xF1\x80\x80\x41", "\\ufffd" "\x41");
133             test("\xF2\x80\x80\x41", "\\ufffd" "\x41");
134             test("\xF3\x80\x80\x41", "\\ufffd" "\x41");
135             test("\xF4\x80\x80\x41", "\\ufffd" "\x41");
136             test("\xF5\x80\x80\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
137 
138             test("\xF0\x90\x80\x41", "\\ufffd" "\x41");
139             test("\xF1\x90\x80\x41", "\\ufffd" "\x41");
140             test("\xF2\x90\x80\x41", "\\ufffd" "\x41");
141             test("\xF3\x90\x80\x41", "\\ufffd" "\x41");
142             test("\xF4\x90\x80\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
143             test("\xF5\x90\x80\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
144 
145             test("\xC0\xAF\xE0\x80\xBF\xF0\x81\x82\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
146             test("\xED\xA0\x80\xED\xBF\xBF\xED\xAF\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
147             test("\xF4\x91\x92\x93\xFF\x41\x80\xBF\x42", "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\x41" "\\ufffd""\\ufffd" "\x42");
148             test("\xE1\x80\xE2\xF0\x91\x92\xF1\xBF\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
149         }
150     }
151 
152     SECTION("to_string")
153     {
154         auto test = [&](std::string const & input, std::string const & expected)
__anon045d28220202(std::string const & input, std::string const & expected) 155         {
156             using std::to_string;
157             const json j = input;
158             CHECK(to_string(j) == "\"" + expected + "\"");
159         };
160 
161         test(R"({"x":5,"y":6})", R"({\"x\":5,\"y\":6})");
162         test("{\"x\":[10,null,null,null]}", R"({\"x\":[10,null,null,null]})");
163         test("test", "test");
164         test("[3,\"false\",false]", R"([3,\"false\",false])");
165     }
166 }
167 
168 TEST_CASE_TEMPLATE("serialization for extreme integer values", T, int32_t, uint32_t, int64_t, uint64_t)
169 {
170     SECTION("minimum")
171     {
172         constexpr auto minimum = (std::numeric_limits<T>::min)();
173         const json j = minimum;
174         CHECK(j.dump() == std::to_string(minimum));
175     }
176 
177     SECTION("maximum")
178     {
179         constexpr auto maximum = (std::numeric_limits<T>::max)();
180         const json j = maximum;
181         CHECK(j.dump() == std::to_string(maximum));
182     }
183 }
184 
185 TEST_CASE("dump with binary values")
186 {
187     auto binary = json::binary({1, 2, 3, 4});
188     auto binary_empty = json::binary({});
189     auto binary_with_subtype = json::binary({1, 2, 3, 4}, 128);
190     auto binary_empty_with_subtype = json::binary({}, 128);
191 
192     const json object = {{"key", binary}};
193     const json object_empty = {{"key", binary_empty}};
194     const json object_with_subtype = {{"key", binary_with_subtype}};
195     const json object_empty_with_subtype = {{"key", binary_empty_with_subtype}};
196 
197     const json array = {"value", 1, binary};
198     const json array_empty = {"value", 1, binary_empty};
199     const json array_with_subtype = {"value", 1, binary_with_subtype};
200     const json array_empty_with_subtype = {"value", 1, binary_empty_with_subtype};
201 
202     SECTION("normal")
203     {
204         CHECK(binary.dump() == "{\"bytes\":[1,2,3,4],\"subtype\":null}");
205         CHECK(binary_empty.dump() == "{\"bytes\":[],\"subtype\":null}");
206         CHECK(binary_with_subtype.dump() == "{\"bytes\":[1,2,3,4],\"subtype\":128}");
207         CHECK(binary_empty_with_subtype.dump() == "{\"bytes\":[],\"subtype\":128}");
208 
209         CHECK(object.dump() == "{\"key\":{\"bytes\":[1,2,3,4],\"subtype\":null}}");
210         CHECK(object_empty.dump() == "{\"key\":{\"bytes\":[],\"subtype\":null}}");
211         CHECK(object_with_subtype.dump() == "{\"key\":{\"bytes\":[1,2,3,4],\"subtype\":128}}");
212         CHECK(object_empty_with_subtype.dump() == "{\"key\":{\"bytes\":[],\"subtype\":128}}");
213 
214         CHECK(array.dump() == "[\"value\",1,{\"bytes\":[1,2,3,4],\"subtype\":null}]");
215         CHECK(array_empty.dump() == "[\"value\",1,{\"bytes\":[],\"subtype\":null}]");
216         CHECK(array_with_subtype.dump() == "[\"value\",1,{\"bytes\":[1,2,3,4],\"subtype\":128}]");
217         CHECK(array_empty_with_subtype.dump() == "[\"value\",1,{\"bytes\":[],\"subtype\":128}]");
218     }
219 
220     SECTION("pretty-printed")
221     {
222         CHECK(binary.dump(4) == "{\n"
223               "    \"bytes\": [1, 2, 3, 4],\n"
224               "    \"subtype\": null\n"
225               "}");
226         CHECK(binary_empty.dump(4) == "{\n"
227               "    \"bytes\": [],\n"
228               "    \"subtype\": null\n"
229               "}");
230         CHECK(binary_with_subtype.dump(4) == "{\n"
231               "    \"bytes\": [1, 2, 3, 4],\n"
232               "    \"subtype\": 128\n"
233               "}");
234         CHECK(binary_empty_with_subtype.dump(4) == "{\n"
235               "    \"bytes\": [],\n"
236               "    \"subtype\": 128\n"
237               "}");
238 
239         CHECK(object.dump(4) == "{\n"
240               "    \"key\": {\n"
241               "        \"bytes\": [1, 2, 3, 4],\n"
242               "        \"subtype\": null\n"
243               "    }\n"
244               "}");
245         CHECK(object_empty.dump(4) == "{\n"
246               "    \"key\": {\n"
247               "        \"bytes\": [],\n"
248               "        \"subtype\": null\n"
249               "    }\n"
250               "}");
251         CHECK(object_with_subtype.dump(4) == "{\n"
252               "    \"key\": {\n"
253               "        \"bytes\": [1, 2, 3, 4],\n"
254               "        \"subtype\": 128\n"
255               "    }\n"
256               "}");
257         CHECK(object_empty_with_subtype.dump(4) == "{\n"
258               "    \"key\": {\n"
259               "        \"bytes\": [],\n"
260               "        \"subtype\": 128\n"
261               "    }\n"
262               "}");
263 
264         CHECK(array.dump(4) == "[\n"
265               "    \"value\",\n"
266               "    1,\n"
267               "    {\n"
268               "        \"bytes\": [1, 2, 3, 4],\n"
269               "        \"subtype\": null\n"
270               "    }\n"
271               "]");
272         CHECK(array_empty.dump(4) == "[\n"
273               "    \"value\",\n"
274               "    1,\n"
275               "    {\n"
276               "        \"bytes\": [],\n"
277               "        \"subtype\": null\n"
278               "    }\n"
279               "]");
280         CHECK(array_with_subtype.dump(4) == "[\n"
281               "    \"value\",\n"
282               "    1,\n"
283               "    {\n"
284               "        \"bytes\": [1, 2, 3, 4],\n"
285               "        \"subtype\": 128\n"
286               "    }\n"
287               "]");
288         CHECK(array_empty_with_subtype.dump(4) == "[\n"
289               "    \"value\",\n"
290               "    1,\n"
291               "    {\n"
292               "        \"bytes\": [],\n"
293               "        \"subtype\": 128\n"
294               "    }\n"
295               "]");
296     }
297 }
298