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