• 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 #define JSON_TESTS_PRIVATE
33 #include <nlohmann/json.hpp>
34 using nlohmann::json;
35 
36 namespace
37 {
38 // shortcut to scan a string literal
39 json::lexer::token_type scan_string(const char* s, bool ignore_comments = false);
scan_string(const char * s,const bool ignore_comments)40 json::lexer::token_type scan_string(const char* s, const bool ignore_comments)
41 {
42     auto ia = nlohmann::detail::input_adapter(s);
43     return nlohmann::detail::lexer<json, decltype(ia)>(std::move(ia), ignore_comments).scan(); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)
44 }
45 } // namespace
46 
47 std::string get_error_message(const char* s, bool ignore_comments = false);
get_error_message(const char * s,const bool ignore_comments)48 std::string get_error_message(const char* s, const bool ignore_comments)
49 {
50     auto ia = nlohmann::detail::input_adapter(s);
51     auto lexer = nlohmann::detail::lexer<json, decltype(ia)>(std::move(ia), ignore_comments); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)
52     lexer.scan();
53     return lexer.get_error_message();
54 }
55 
56 TEST_CASE("lexer class")
57 {
58     SECTION("scan")
59     {
60         SECTION("structural characters")
61         {
62             CHECK((scan_string("[") == json::lexer::token_type::begin_array));
63             CHECK((scan_string("]") == json::lexer::token_type::end_array));
64             CHECK((scan_string("{") == json::lexer::token_type::begin_object));
65             CHECK((scan_string("}") == json::lexer::token_type::end_object));
66             CHECK((scan_string(",") == json::lexer::token_type::value_separator));
67             CHECK((scan_string(":") == json::lexer::token_type::name_separator));
68         }
69 
70         SECTION("literal names")
71         {
72             CHECK((scan_string("null") == json::lexer::token_type::literal_null));
73             CHECK((scan_string("true") == json::lexer::token_type::literal_true));
74             CHECK((scan_string("false") == json::lexer::token_type::literal_false));
75         }
76 
77         SECTION("numbers")
78         {
79             CHECK((scan_string("0") == json::lexer::token_type::value_unsigned));
80             CHECK((scan_string("1") == json::lexer::token_type::value_unsigned));
81             CHECK((scan_string("2") == json::lexer::token_type::value_unsigned));
82             CHECK((scan_string("3") == json::lexer::token_type::value_unsigned));
83             CHECK((scan_string("4") == json::lexer::token_type::value_unsigned));
84             CHECK((scan_string("5") == json::lexer::token_type::value_unsigned));
85             CHECK((scan_string("6") == json::lexer::token_type::value_unsigned));
86             CHECK((scan_string("7") == json::lexer::token_type::value_unsigned));
87             CHECK((scan_string("8") == json::lexer::token_type::value_unsigned));
88             CHECK((scan_string("9") == json::lexer::token_type::value_unsigned));
89 
90             CHECK((scan_string("-0") == json::lexer::token_type::value_integer));
91             CHECK((scan_string("-1") == json::lexer::token_type::value_integer));
92 
93             CHECK((scan_string("1.1") == json::lexer::token_type::value_float));
94             CHECK((scan_string("-1.1") == json::lexer::token_type::value_float));
95             CHECK((scan_string("1E10") == json::lexer::token_type::value_float));
96         }
97 
98         SECTION("whitespace")
99         {
100             // result is end_of_input, because not token is following
101             CHECK((scan_string(" ") == json::lexer::token_type::end_of_input));
102             CHECK((scan_string("\t") == json::lexer::token_type::end_of_input));
103             CHECK((scan_string("\n") == json::lexer::token_type::end_of_input));
104             CHECK((scan_string("\r") == json::lexer::token_type::end_of_input));
105             CHECK((scan_string(" \t\n\r\n\t ") == json::lexer::token_type::end_of_input));
106         }
107     }
108 
109     SECTION("token_type_name")
110     {
111         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::uninitialized)) == "<uninitialized>"));
112         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::literal_true)) == "true literal"));
113         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::literal_false)) == "false literal"));
114         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::literal_null)) == "null literal"));
115         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_string)) == "string literal"));
116         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_unsigned)) == "number literal"));
117         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_integer)) == "number literal"));
118         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_float)) == "number literal"));
119         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::begin_array)) == "'['"));
120         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::begin_object)) == "'{'"));
121         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::end_array)) == "']'"));
122         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::end_object)) == "'}'"));
123         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::name_separator)) == "':'"));
124         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_separator)) == "','"));
125         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::parse_error)) == "<parse error>"));
126         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::end_of_input)) == "end of input"));
127     }
128 
129     SECTION("parse errors on first character")
130     {
131         for (int c = 1; c < 128; ++c)
132         {
133             // create string from the ASCII code
134             const auto s = std::string(1, static_cast<char>(c));
135             // store scan() result
136             const auto res = scan_string(s.c_str());
137 
138             CAPTURE(s)
139 
140             switch (c)
141             {
142                 // single characters that are valid tokens
143                 case ('['):
144                 case (']'):
145                 case ('{'):
146                 case ('}'):
147                 case (','):
148                 case (':'):
149                 case ('0'):
150                 case ('1'):
151                 case ('2'):
152                 case ('3'):
153                 case ('4'):
154                 case ('5'):
155                 case ('6'):
156                 case ('7'):
157                 case ('8'):
158                 case ('9'):
159                 {
160                     CHECK((res != json::lexer::token_type::parse_error));
161                     break;
162                 }
163 
164                 // whitespace
165                 case (' '):
166                 case ('\t'):
167                 case ('\n'):
168                 case ('\r'):
169                 {
170                     CHECK((res == json::lexer::token_type::end_of_input));
171                     break;
172                 }
173 
174                 // anything else is not expected
175                 default:
176                 {
177                     CHECK((res == json::lexer::token_type::parse_error));
178                     break;
179                 }
180             }
181         }
182     }
183 
184     SECTION("very large string")
185     {
186         // strings larger than 1024 bytes yield a resize of the lexer's yytext buffer
187         std::string s("\"");
188         s += std::string(2048, 'x');
189         s += "\"";
190         CHECK((scan_string(s.c_str()) == json::lexer::token_type::value_string));
191     }
192 
193     SECTION("fail on comments")
194     {
195         CHECK((scan_string("/", false) == json::lexer::token_type::parse_error));
196         CHECK(get_error_message("/", false) == "invalid literal");
197 
198         CHECK((scan_string("/!", false) == json::lexer::token_type::parse_error));
199         CHECK(get_error_message("/!", false) == "invalid literal");
200         CHECK((scan_string("/*", false) == json::lexer::token_type::parse_error));
201         CHECK(get_error_message("/*", false) == "invalid literal");
202         CHECK((scan_string("/**", false) == json::lexer::token_type::parse_error));
203         CHECK(get_error_message("/**", false) == "invalid literal");
204 
205         CHECK((scan_string("//", false) == json::lexer::token_type::parse_error));
206         CHECK(get_error_message("//", false) == "invalid literal");
207         CHECK((scan_string("/**/", false) == json::lexer::token_type::parse_error));
208         CHECK(get_error_message("/**/", false) == "invalid literal");
209         CHECK((scan_string("/** /", false) == json::lexer::token_type::parse_error));
210         CHECK(get_error_message("/** /", false) == "invalid literal");
211 
212         CHECK((scan_string("/***/", false) == json::lexer::token_type::parse_error));
213         CHECK(get_error_message("/***/", false) == "invalid literal");
214         CHECK((scan_string("/* true */", false) == json::lexer::token_type::parse_error));
215         CHECK(get_error_message("/* true */", false) == "invalid literal");
216         CHECK((scan_string("/*/**/", false) == json::lexer::token_type::parse_error));
217         CHECK(get_error_message("/*/**/", false) == "invalid literal");
218         CHECK((scan_string("/*/* */", false) == json::lexer::token_type::parse_error));
219         CHECK(get_error_message("/*/* */", false) == "invalid literal");
220     }
221 
222     SECTION("ignore comments")
223     {
224         CHECK((scan_string("/", true) == json::lexer::token_type::parse_error));
225         CHECK(get_error_message("/", true) == "invalid comment; expecting '/' or '*' after '/'");
226 
227         CHECK((scan_string("/!", true) == json::lexer::token_type::parse_error));
228         CHECK(get_error_message("/!", true) == "invalid comment; expecting '/' or '*' after '/'");
229         CHECK((scan_string("/*", true) == json::lexer::token_type::parse_error));
230         CHECK(get_error_message("/*", true) == "invalid comment; missing closing '*/'");
231         CHECK((scan_string("/**", true) == json::lexer::token_type::parse_error));
232         CHECK(get_error_message("/**", true) == "invalid comment; missing closing '*/'");
233 
234         CHECK((scan_string("//", true) == json::lexer::token_type::end_of_input));
235         CHECK((scan_string("/**/", true) == json::lexer::token_type::end_of_input));
236         CHECK((scan_string("/** /", true) == json::lexer::token_type::parse_error));
237         CHECK(get_error_message("/** /", true) == "invalid comment; missing closing '*/'");
238 
239         CHECK((scan_string("/***/", true) == json::lexer::token_type::end_of_input));
240         CHECK((scan_string("/* true */", true) == json::lexer::token_type::end_of_input));
241         CHECK((scan_string("/*/**/", true) == json::lexer::token_type::end_of_input));
242         CHECK((scan_string("/*/* */", true) == json::lexer::token_type::end_of_input));
243 
244         CHECK((scan_string("//\n//\n", true) == json::lexer::token_type::end_of_input));
245         CHECK((scan_string("/**//**//**/", true) == json::lexer::token_type::end_of_input));
246     }
247 }
248