1 /*
2 __ _____ _____ _____
3 __| | __| | | | JSON for Modern C++ (test suite)
4 | | |__ | | | | | | version 3.10.0
5 |_____|_____|_____|_|___| https://github.com/nlohmann/json
6
7 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
8 SPDX-License-Identifier: MIT
9 Copyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.
10
11 Permission is hereby granted, free of charge, to any person obtaining a copy
12 of this software and associated documentation files (the "Software"), to deal
13 in the Software without restriction, including without limitation the rights
14 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 copies of the Software, and to permit persons to whom the Software is
16 furnished to do so, subject to the following conditions:
17
18 The above copyright notice and this permission notice shall be included in all
19 copies or substantial portions of the Software.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 SOFTWARE.
28 */
29
30 #include "doctest_compatibility.h"
31
32 // for some reason including this after the json header leads to linker errors with VS 2017...
33 #include <locale>
34
35 #include <nlohmann/json.hpp>
36 using nlohmann::json;
37
38 #include <fstream>
39 #include <sstream>
40 #include <iostream>
41 #include <iomanip>
42 #include <test_data.hpp>
43
44 // this test suite uses static variables with non-trivial destructors
45 DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
46 DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
47
48 namespace
49 {
50 extern size_t calls;
51 size_t calls = 0;
52
53 void check_utf8dump(bool success_expected, int byte1, int byte2, int byte3, int byte4);
54
check_utf8dump(bool success_expected,int byte1,int byte2=-1,int byte3=-1,int byte4=-1)55 void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
56 {
57 static std::string json_string;
58 json_string.clear();
59
60 CAPTURE(byte1)
61 CAPTURE(byte2)
62 CAPTURE(byte3)
63 CAPTURE(byte4)
64
65 json_string += std::string(1, static_cast<char>(byte1));
66
67 if (byte2 != -1)
68 {
69 json_string += std::string(1, static_cast<char>(byte2));
70 }
71
72 if (byte3 != -1)
73 {
74 json_string += std::string(1, static_cast<char>(byte3));
75 }
76
77 if (byte4 != -1)
78 {
79 json_string += std::string(1, static_cast<char>(byte4));
80 }
81
82 CAPTURE(json_string)
83
84 // store the string in a JSON value
85 static json j;
86 static json j2;
87 j = json_string;
88 j2 = "abc" + json_string + "xyz";
89
90 static std::string s_ignored;
91 static std::string s_ignored2;
92 static std::string s_ignored_ascii;
93 static std::string s_ignored2_ascii;
94 static std::string s_replaced;
95 static std::string s_replaced2;
96 static std::string s_replaced_ascii;
97 static std::string s_replaced2_ascii;
98
99 // dumping with ignore/replace must not throw in any case
100 s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
101 s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
102 s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
103 s_ignored2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::ignore);
104 s_replaced = j.dump(-1, ' ', false, json::error_handler_t::replace);
105 s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
106 s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
107 s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
108
109 if (success_expected)
110 {
111 static std::string s_strict;
112 // strict mode must not throw if success is expected
113 s_strict = j.dump();
114 // all dumps should agree on the string
115 CHECK(s_strict == s_ignored);
116 CHECK(s_strict == s_replaced);
117 }
118 else
119 {
120 // strict mode must throw if success is not expected
121 CHECK_THROWS_AS(j.dump(), json::type_error&);
122 // ignore and replace must create different dumps
123 CHECK(s_ignored != s_replaced);
124
125 // check that replace string contains a replacement character
126 CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
127 }
128
129 // check that prefix and suffix are preserved
130 CHECK(s_ignored2.substr(1, 3) == "abc");
131 CHECK(s_ignored2.substr(s_ignored2.size() - 4, 3) == "xyz");
132 CHECK(s_ignored2_ascii.substr(1, 3) == "abc");
133 CHECK(s_ignored2_ascii.substr(s_ignored2_ascii.size() - 4, 3) == "xyz");
134 CHECK(s_replaced2.substr(1, 3) == "abc");
135 CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
136 CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
137 CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
138 }
139
140 void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
141
142 // create and check a JSON string with up to four UTF-8 bytes
check_utf8string(bool success_expected,int byte1,int byte2=-1,int byte3=-1,int byte4=-1)143 void check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
144 {
145 if (++calls % 100000 == 0)
146 {
147 std::cout << calls << " of 1246225 UTF-8 strings checked" << std::endl;
148 }
149
150 static std::string json_string;
151 json_string = "\"";
152
153 CAPTURE(byte1)
154 json_string += std::string(1, static_cast<char>(byte1));
155
156 if (byte2 != -1)
157 {
158 CAPTURE(byte2)
159 json_string += std::string(1, static_cast<char>(byte2));
160 }
161
162 if (byte3 != -1)
163 {
164 CAPTURE(byte3)
165 json_string += std::string(1, static_cast<char>(byte3));
166 }
167
168 if (byte4 != -1)
169 {
170 CAPTURE(byte4)
171 json_string += std::string(1, static_cast<char>(byte4));
172 }
173
174 json_string += "\"";
175
176 CAPTURE(json_string)
177
178 json _;
179 if (success_expected)
180 {
181 CHECK_NOTHROW(_ = json::parse(json_string));
182 }
183 else
184 {
185 CHECK_THROWS_AS(_ = json::parse(json_string), json::parse_error&);
186 }
187 }
188 } // namespace
189
skip()190 TEST_CASE("Unicode (5/5)" * doctest::skip())
191 {
192 SECTION("RFC 3629")
193 {
194 /*
195 RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as
196 follows:
197
198 A UTF-8 string is a sequence of octets representing a sequence of UCS
199 characters. An octet sequence is valid UTF-8 only if it matches the
200 following syntax, which is derived from the rules for encoding UTF-8
201 and is expressed in the ABNF of [RFC2234].
202
203 UTF8-octets = *( UTF8-char )
204 UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
205 UTF8-1 = %x00-7F
206 UTF8-2 = %xC2-DF UTF8-tail
207 UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
208 %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
209 UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
210 %xF4 %x80-8F 2( UTF8-tail )
211 UTF8-tail = %x80-BF
212 */
213
214 SECTION("UTF8-4 (xF4 x80-8F UTF8-tail UTF8-tail)")
215 {
216 SECTION("well-formed")
217 {
218 for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
219 {
220 for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
221 {
222 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
223 {
224 for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
225 {
226 check_utf8string(true, byte1, byte2, byte3, byte4);
227 check_utf8dump(true, byte1, byte2, byte3, byte4);
228 }
229 }
230 }
231 }
232 }
233
234 SECTION("ill-formed: missing second byte")
235 {
236 for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
237 {
238 check_utf8string(false, byte1);
239 check_utf8dump(false, byte1);
240 }
241 }
242
243 SECTION("ill-formed: missing third byte")
244 {
245 for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
246 {
247 for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
248 {
249 check_utf8string(false, byte1, byte2);
250 check_utf8dump(false, byte1, byte2);
251 }
252 }
253 }
254
255 SECTION("ill-formed: missing fourth byte")
256 {
257 for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
258 {
259 for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
260 {
261 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
262 {
263 check_utf8string(false, byte1, byte2, byte3);
264 check_utf8dump(false, byte1, byte2, byte3);
265 }
266 }
267 }
268 }
269
270 SECTION("ill-formed: wrong second byte")
271 {
272 for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
273 {
274 for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
275 {
276 // skip correct second byte
277 if (0x80 <= byte2 && byte2 <= 0x8F)
278 {
279 continue;
280 }
281
282 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
283 {
284 for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
285 {
286 check_utf8string(false, byte1, byte2, byte3, byte4);
287 check_utf8dump(false, byte1, byte2, byte3, byte4);
288 }
289 }
290 }
291 }
292 }
293
294 SECTION("ill-formed: wrong third byte")
295 {
296 for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
297 {
298 for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
299 {
300 for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
301 {
302 // skip correct third byte
303 if (0x80 <= byte3 && byte3 <= 0xBF)
304 {
305 continue;
306 }
307
308 for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
309 {
310 check_utf8string(false, byte1, byte2, byte3, byte4);
311 check_utf8dump(false, byte1, byte2, byte3, byte4);
312 }
313 }
314 }
315 }
316 }
317
318 SECTION("ill-formed: wrong fourth byte")
319 {
320 for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
321 {
322 for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
323 {
324 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
325 {
326 for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
327 {
328 // skip correct fourth byte
329 if (0x80 <= byte3 && byte3 <= 0xBF)
330 {
331 continue;
332 }
333
334 check_utf8string(false, byte1, byte2, byte3, byte4);
335 check_utf8dump(false, byte1, byte2, byte3, byte4);
336 }
337 }
338 }
339 }
340 }
341 }
342 }
343 }
344
345 DOCTEST_CLANG_SUPPRESS_WARNING_POP
346