• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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