1//     __ _____ _____ _____
2//  __|  |   __|     |   | |  JSON for Modern C++ (supporting code)
3// |  |  |__   |  |  | | | |  version 3.11.2
4// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
5//
6// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
7// SPDX-License-Identifier: MIT
8
9#include "doctest_compatibility.h"
10
11#include <nlohmann/json.hpp>
12using nlohmann::json;
13
14#include <sstream>
15#include <iomanip>
16
17TEST_CASE("serialization")
18{
19    SECTION("operator<<")
20    {
21        SECTION("no given width")
22        {
23            std::stringstream ss;
24            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            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            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            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            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            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            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            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            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)
122            {
123                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)
155        {
156            using std::to_string;
157            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
168TEST_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        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        json j = maximum;
181        CHECK(j.dump() == std::to_string(maximum));
182    }
183}
184
185TEST_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    json object = {{"key", binary}};
193    json object_empty = {{"key", binary_empty}};
194    json object_with_subtype = {{"key", binary_with_subtype}};
195    json object_empty_with_subtype = {{"key", binary_empty_with_subtype}};
196
197    json array = {"value", 1, binary};
198    json array_empty = {"value", 1, binary_empty};
199    json array_with_subtype = {"value", 1, binary_with_subtype};
200    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