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 // for some reason including this after the json header leads to linker errors with VS 2017...
12 #include <locale>
13
14 #include <nlohmann/json.hpp>
15 using nlohmann::json;
16
17 #include <fstream>
18 #include <sstream>
19 #include <iostream>
20 #include <iomanip>
21 #include "make_test_data_available.hpp"
22
23 // this test suite uses static variables with non-trivial destructors
24 DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
25 DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
26
27 namespace
28 {
29 extern size_t calls;
30 size_t calls = 0;
31
32 void check_utf8dump(bool success_expected, int byte1, int byte2, int byte3, int byte4);
33
check_utf8dump(bool success_expected,int byte1,int byte2=-1,int byte3=-1,int byte4=-1)34 void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
35 {
36 static std::string json_string;
37 json_string.clear();
38
39 CAPTURE(byte1)
40 CAPTURE(byte2)
41 CAPTURE(byte3)
42 CAPTURE(byte4)
43
44 json_string += std::string(1, static_cast<char>(byte1));
45
46 if (byte2 != -1)
47 {
48 json_string += std::string(1, static_cast<char>(byte2));
49 }
50
51 if (byte3 != -1)
52 {
53 json_string += std::string(1, static_cast<char>(byte3));
54 }
55
56 if (byte4 != -1)
57 {
58 json_string += std::string(1, static_cast<char>(byte4));
59 }
60
61 CAPTURE(json_string)
62
63 // store the string in a JSON value
64 static json j;
65 static json j2;
66 j = json_string;
67 j2 = "abc" + json_string + "xyz";
68
69 static std::string s_ignored;
70 static std::string s_ignored2;
71 static std::string s_ignored_ascii;
72 static std::string s_ignored2_ascii;
73 static std::string s_replaced;
74 static std::string s_replaced2;
75 static std::string s_replaced_ascii;
76 static std::string s_replaced2_ascii;
77
78 // dumping with ignore/replace must not throw in any case
79 s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
80 s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
81 s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
82 s_ignored2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::ignore);
83 s_replaced = j.dump(-1, ' ', false, json::error_handler_t::replace);
84 s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
85 s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
86 s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
87
88 if (success_expected)
89 {
90 static std::string s_strict;
91 // strict mode must not throw if success is expected
92 s_strict = j.dump();
93 // all dumps should agree on the string
94 CHECK(s_strict == s_ignored);
95 CHECK(s_strict == s_replaced);
96 }
97 else
98 {
99 // strict mode must throw if success is not expected
100 CHECK_THROWS_AS(j.dump(), json::type_error&);
101 // ignore and replace must create different dumps
102 CHECK(s_ignored != s_replaced);
103
104 // check that replace string contains a replacement character
105 CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
106 }
107
108 // check that prefix and suffix are preserved
109 CHECK(s_ignored2.substr(1, 3) == "abc");
110 CHECK(s_ignored2.substr(s_ignored2.size() - 4, 3) == "xyz");
111 CHECK(s_ignored2_ascii.substr(1, 3) == "abc");
112 CHECK(s_ignored2_ascii.substr(s_ignored2_ascii.size() - 4, 3) == "xyz");
113 CHECK(s_replaced2.substr(1, 3) == "abc");
114 CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
115 CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
116 CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
117 }
118
119 void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
120
121 // 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)122 void check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
123 {
124 if (++calls % 100000 == 0)
125 {
126 std::cout << calls << " of 455355 UTF-8 strings checked" << std::endl;
127 }
128
129 static std::string json_string;
130 json_string = "\"";
131
132 CAPTURE(byte1)
133 json_string += std::string(1, static_cast<char>(byte1));
134
135 if (byte2 != -1)
136 {
137 CAPTURE(byte2)
138 json_string += std::string(1, static_cast<char>(byte2));
139 }
140
141 if (byte3 != -1)
142 {
143 CAPTURE(byte3)
144 json_string += std::string(1, static_cast<char>(byte3));
145 }
146
147 if (byte4 != -1)
148 {
149 CAPTURE(byte4)
150 json_string += std::string(1, static_cast<char>(byte4));
151 }
152
153 json_string += "\"";
154
155 CAPTURE(json_string)
156
157 json _;
158 if (success_expected)
159 {
160 CHECK_NOTHROW(_ = json::parse(json_string));
161 }
162 else
163 {
164 CHECK_THROWS_AS(_ = json::parse(json_string), json::parse_error&);
165 }
166 }
167 } // namespace
168
skip()169 TEST_CASE("Unicode (2/5)" * doctest::skip())
170 {
171 SECTION("RFC 3629")
172 {
173 /*
174 RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as
175 follows:
176
177 A UTF-8 string is a sequence of octets representing a sequence of UCS
178 characters. An octet sequence is valid UTF-8 only if it matches the
179 following syntax, which is derived from the rules for encoding UTF-8
180 and is expressed in the ABNF of [RFC2234].
181
182 UTF8-octets = *( UTF8-char )
183 UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
184 UTF8-1 = %x00-7F
185 UTF8-2 = %xC2-DF UTF8-tail
186 UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
187 %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
188 UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
189 %xF4 %x80-8F 2( UTF8-tail )
190 UTF8-tail = %x80-BF
191 */
192
193 SECTION("ill-formed first byte")
194 {
195 for (int byte1 = 0x80; byte1 <= 0xC1; ++byte1)
196 {
197 check_utf8string(false, byte1);
198 check_utf8dump(false, byte1);
199 }
200
201 for (int byte1 = 0xF5; byte1 <= 0xFF; ++byte1)
202 {
203 check_utf8string(false, byte1);
204 check_utf8dump(false, byte1);
205 }
206 }
207
208 SECTION("UTF8-1 (x00-x7F)")
209 {
210 SECTION("well-formed")
211 {
212 for (int byte1 = 0x00; byte1 <= 0x7F; ++byte1)
213 {
214 // unescaped control characters are parse errors in JSON
215 if (0x00 <= byte1 && byte1 <= 0x1F)
216 {
217 check_utf8string(false, byte1);
218 continue;
219 }
220
221 // a single quote is a parse error in JSON
222 if (byte1 == 0x22)
223 {
224 check_utf8string(false, byte1);
225 continue;
226 }
227
228 // a single backslash is a parse error in JSON
229 if (byte1 == 0x5C)
230 {
231 check_utf8string(false, byte1);
232 continue;
233 }
234
235 // all other characters are OK
236 check_utf8string(true, byte1);
237 check_utf8dump(true, byte1);
238 }
239 }
240 }
241
242 SECTION("UTF8-2 (xC2-xDF UTF8-tail)")
243 {
244 SECTION("well-formed")
245 {
246 for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
247 {
248 for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
249 {
250 check_utf8string(true, byte1, byte2);
251 check_utf8dump(true, byte1, byte2);
252 }
253 }
254 }
255
256 SECTION("ill-formed: missing second byte")
257 {
258 for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
259 {
260 check_utf8string(false, byte1);
261 check_utf8dump(false, byte1);
262 }
263 }
264
265 SECTION("ill-formed: wrong second byte")
266 {
267 for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
268 {
269 for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
270 {
271 // skip correct second byte
272 if (0x80 <= byte2 && byte2 <= 0xBF)
273 {
274 continue;
275 }
276
277 check_utf8string(false, byte1, byte2);
278 check_utf8dump(false, byte1, byte2);
279 }
280 }
281 }
282 }
283
284 SECTION("UTF8-3 (xE0 xA0-BF UTF8-tail)")
285 {
286 SECTION("well-formed")
287 {
288 for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
289 {
290 for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
291 {
292 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
293 {
294 check_utf8string(true, byte1, byte2, byte3);
295 check_utf8dump(true, byte1, byte2, byte3);
296 }
297 }
298 }
299 }
300
301 SECTION("ill-formed: missing second byte")
302 {
303 for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
304 {
305 check_utf8string(false, byte1);
306 check_utf8dump(false, byte1);
307 }
308 }
309
310 SECTION("ill-formed: missing third byte")
311 {
312 for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
313 {
314 for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
315 {
316 check_utf8string(false, byte1, byte2);
317 check_utf8dump(false, byte1, byte2);
318 }
319 }
320 }
321
322 SECTION("ill-formed: wrong second byte")
323 {
324 for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
325 {
326 for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
327 {
328 // skip correct second byte
329 if (0xA0 <= byte2 && byte2 <= 0xBF)
330 {
331 continue;
332 }
333
334 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
335 {
336 check_utf8string(false, byte1, byte2, byte3);
337 check_utf8dump(false, byte1, byte2, byte3);
338 }
339 }
340 }
341 }
342
343 SECTION("ill-formed: wrong third byte")
344 {
345 for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
346 {
347 for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
348 {
349 for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
350 {
351 // skip correct third byte
352 if (0x80 <= byte3 && byte3 <= 0xBF)
353 {
354 continue;
355 }
356
357 check_utf8string(false, byte1, byte2, byte3);
358 check_utf8dump(false, byte1, byte2, byte3);
359 }
360 }
361 }
362 }
363 }
364
365 SECTION("UTF8-3 (xE1-xEC UTF8-tail UTF8-tail)")
366 {
367 SECTION("well-formed")
368 {
369 for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
370 {
371 for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
372 {
373 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
374 {
375 check_utf8string(true, byte1, byte2, byte3);
376 check_utf8dump(true, byte1, byte2, byte3);
377 }
378 }
379 }
380 }
381
382 SECTION("ill-formed: missing second byte")
383 {
384 for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
385 {
386 check_utf8string(false, byte1);
387 check_utf8dump(false, byte1);
388 }
389 }
390
391 SECTION("ill-formed: missing third byte")
392 {
393 for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
394 {
395 for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
396 {
397 check_utf8string(false, byte1, byte2);
398 check_utf8dump(false, byte1, byte2);
399 }
400 }
401 }
402
403 SECTION("ill-formed: wrong second byte")
404 {
405 for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
406 {
407 for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
408 {
409 // skip correct second byte
410 if (0x80 <= byte2 && byte2 <= 0xBF)
411 {
412 continue;
413 }
414
415 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
416 {
417 check_utf8string(false, byte1, byte2, byte3);
418 check_utf8dump(false, byte1, byte2, byte3);
419 }
420 }
421 }
422 }
423
424 SECTION("ill-formed: wrong third byte")
425 {
426 for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
427 {
428 for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
429 {
430 for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
431 {
432 // skip correct third byte
433 if (0x80 <= byte3 && byte3 <= 0xBF)
434 {
435 continue;
436 }
437
438 check_utf8string(false, byte1, byte2, byte3);
439 check_utf8dump(false, byte1, byte2, byte3);
440 }
441 }
442 }
443 }
444 }
445
446 SECTION("UTF8-3 (xED x80-9F UTF8-tail)")
447 {
448 SECTION("well-formed")
449 {
450 for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
451 {
452 for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
453 {
454 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
455 {
456 check_utf8string(true, byte1, byte2, byte3);
457 check_utf8dump(true, byte1, byte2, byte3);
458 }
459 }
460 }
461 }
462
463 SECTION("ill-formed: missing second byte")
464 {
465 for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
466 {
467 check_utf8string(false, byte1);
468 check_utf8dump(false, byte1);
469 }
470 }
471
472 SECTION("ill-formed: missing third byte")
473 {
474 for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
475 {
476 for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
477 {
478 check_utf8string(false, byte1, byte2);
479 check_utf8dump(false, byte1, byte2);
480 }
481 }
482 }
483
484 SECTION("ill-formed: wrong second byte")
485 {
486 for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
487 {
488 for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
489 {
490 // skip correct second byte
491 if (0x80 <= byte2 && byte2 <= 0x9F)
492 {
493 continue;
494 }
495
496 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
497 {
498 check_utf8string(false, byte1, byte2, byte3);
499 check_utf8dump(false, byte1, byte2, byte3);
500 }
501 }
502 }
503 }
504
505 SECTION("ill-formed: wrong third byte")
506 {
507 for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
508 {
509 for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
510 {
511 for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
512 {
513 // skip correct third byte
514 if (0x80 <= byte3 && byte3 <= 0xBF)
515 {
516 continue;
517 }
518
519 check_utf8string(false, byte1, byte2, byte3);
520 check_utf8dump(false, byte1, byte2, byte3);
521 }
522 }
523 }
524 }
525 }
526
527 SECTION("UTF8-3 (xEE-xEF UTF8-tail UTF8-tail)")
528 {
529 SECTION("well-formed")
530 {
531 for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
532 {
533 for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
534 {
535 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
536 {
537 check_utf8string(true, byte1, byte2, byte3);
538 check_utf8dump(true, byte1, byte2, byte3);
539 }
540 }
541 }
542 }
543
544 SECTION("ill-formed: missing second byte")
545 {
546 for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
547 {
548 check_utf8string(false, byte1);
549 check_utf8dump(false, byte1);
550 }
551 }
552
553 SECTION("ill-formed: missing third byte")
554 {
555 for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
556 {
557 for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
558 {
559 check_utf8string(false, byte1, byte2);
560 check_utf8dump(false, byte1, byte2);
561 }
562 }
563 }
564
565 SECTION("ill-formed: wrong second byte")
566 {
567 for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
568 {
569 for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
570 {
571 // skip correct second byte
572 if (0x80 <= byte2 && byte2 <= 0xBF)
573 {
574 continue;
575 }
576
577 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
578 {
579 check_utf8string(false, byte1, byte2, byte3);
580 check_utf8dump(false, byte1, byte2, byte3);
581 }
582 }
583 }
584 }
585
586 SECTION("ill-formed: wrong third byte")
587 {
588 for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
589 {
590 for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
591 {
592 for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
593 {
594 // skip correct third byte
595 if (0x80 <= byte3 && byte3 <= 0xBF)
596 {
597 continue;
598 }
599
600 check_utf8string(false, byte1, byte2, byte3);
601 check_utf8dump(false, byte1, byte2, byte3);
602 }
603 }
604 }
605 }
606 }
607 }
608 }
609
610 DOCTEST_CLANG_SUPPRESS_WARNING_POP
611