Line data Source code
1 : // Copyright (c) 2011-2023 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/as2js
4 : // contact@m2osw.com
5 : //
6 : // This program is free software: you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation, either version 3 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program. If not, see <https://www.gnu.org/licenses/>.
18 :
19 : // as2js
20 : //
21 : #include <as2js/json.h>
22 :
23 : #include <as2js/exception.h>
24 : #include <as2js/message.h>
25 :
26 :
27 : // self
28 : //
29 : #include "catch_main.h"
30 :
31 :
32 : // libutf8
33 : //
34 : #include <libutf8/libutf8.h>
35 :
36 :
37 : // ICU
38 : //
39 : // See http://icu-project.org/apiref/icu4c/index.html
40 : #include <unicode/uchar.h>
41 : //#include <unicode/cuchar> // once available in Linux...
42 :
43 :
44 : // C++
45 : //
46 : #include <limits>
47 : #include <cstring>
48 : #include <algorithm>
49 : #include <iomanip>
50 :
51 :
52 : // C
53 : //
54 : #include <unistd.h>
55 :
56 :
57 : // last include
58 : //
59 : #include <snapdev/poison.h>
60 :
61 :
62 :
63 : namespace
64 : {
65 :
66 :
67 937 : std::int32_t generate_string(std::string & str, std::string & stringified)
68 : {
69 937 : stringified += '"';
70 937 : char32_t c(U'\0');
71 937 : int32_t used(0);
72 937 : int ctrl(rand() % 7);
73 937 : int const max_chars(rand() % 25 + 5);
74 16883 : for(int j(0); j < max_chars; ++j)
75 : {
76 : do
77 : {
78 25059 : c = rand() & 0x1FFFFF;
79 25059 : if(ctrl == 0)
80 : {
81 6027 : ctrl = rand() % 7;
82 6027 : if((ctrl & 3) == 1)
83 : {
84 1798 : c = c & 1 ? '"' : '\'';
85 : }
86 : else
87 : {
88 4229 : c &= 0x1F;
89 : }
90 : }
91 : else
92 : {
93 19032 : --ctrl;
94 : }
95 : }
96 : while(c >= 0x110000
97 16111 : || (c >= 0xD800 && c <= 0xDFFF)
98 16092 : || ((c & 0xFFFE) == 0xFFFE)
99 16092 : || c == '\\' // this can cause problems (i.e. if followed by say an 'f' then it becomes the '\f' character, not '\' then an 'f'
100 41151 : || c == '\0');
101 15946 : str += libutf8::to_u8string(c);
102 15946 : switch(c)
103 : {
104 117 : case U'\b':
105 117 : stringified += '\\';
106 117 : stringified += 'b';
107 117 : used |= 0x01;
108 117 : break;
109 :
110 134 : case U'\f':
111 134 : stringified += '\\';
112 134 : stringified += 'f';
113 134 : used |= 0x02;
114 134 : break;
115 :
116 121 : case U'\n':
117 121 : stringified += '\\';
118 121 : stringified += 'n';
119 121 : used |= 0x04;
120 121 : break;
121 :
122 117 : case U'\r':
123 117 : stringified += '\\';
124 117 : stringified += 'r';
125 117 : used |= 0x08;
126 117 : break;
127 :
128 135 : case U'\t':
129 135 : stringified += '\\';
130 135 : stringified += 't';
131 135 : used |= 0x10;
132 135 : break;
133 :
134 887 : case U'"':
135 887 : stringified += '\\';
136 887 : stringified += '"';
137 887 : used |= 0x20;
138 887 : break;
139 :
140 911 : case U'\'':
141 : // JSON does not expect the apostrophe (') to be escaped
142 : //stringified += '\\';
143 911 : stringified += '\'';
144 911 : used |= 0x40;
145 911 : break;
146 :
147 0 : case U'\\':
148 0 : stringified += "\\\\";
149 : //used |= 0x100; -- this is very unlikely to happen
150 0 : break;
151 :
152 13524 : default:
153 13524 : if(c < 0x0020 || c == 0x007F)
154 : {
155 : // other controls must be escaped using Unicode
156 3460 : std::stringstream ss;
157 3460 : ss << std::hex << "\\u" << std::setfill('0') << std::setw(4) << static_cast<int>(c);
158 3460 : stringified += ss.str();
159 3460 : used |= 0x80;
160 3460 : }
161 : else
162 : {
163 10064 : stringified += libutf8::to_u8string(c);
164 : }
165 13524 : break;
166 :
167 : }
168 : }
169 937 : stringified += '"';
170 :
171 937 : return used;
172 : }
173 :
174 :
175 285 : void stringify_string(std::string const & str, std::string & stringified)
176 : {
177 285 : stringified += '"';
178 285 : size_t const max_chars(str.length());
179 14199 : for(size_t j(0); j < max_chars; ++j)
180 : {
181 : // we essentially ignore UTF-8 in this case so we can just use the
182 : // bytes as is
183 : //
184 13914 : char c(str[j]);
185 13914 : switch(c)
186 : {
187 18 : case '\b':
188 18 : stringified += '\\';
189 18 : stringified += 'b';
190 18 : break;
191 :
192 54 : case '\f':
193 54 : stringified += '\\';
194 54 : stringified += 'f';
195 54 : break;
196 :
197 63 : case '\n':
198 63 : stringified += '\\';
199 63 : stringified += 'n';
200 63 : break;
201 :
202 33 : case '\r':
203 33 : stringified += '\\';
204 33 : stringified += 'r';
205 33 : break;
206 :
207 39 : case '\t':
208 39 : stringified += '\\';
209 39 : stringified += 't';
210 39 : break;
211 :
212 0 : case '\\':
213 0 : stringified += "\\\\";
214 0 : break;
215 :
216 264 : case '"':
217 264 : stringified += '\\';
218 264 : stringified += '"';
219 264 : break;
220 :
221 261 : case '\'':
222 : // JSON does not escape apostrophes (')
223 : //stringified += '\\';
224 261 : stringified += '\'';
225 261 : break;
226 :
227 13182 : default:
228 13182 : if(static_cast<std::uint8_t>(c) < 0x0020 || c == 0x007F)
229 : {
230 : // other controls must be escaped using Unicode
231 1047 : std::stringstream ss;
232 1047 : ss << std::hex << "\\u" << std::setfill('0') << std::setw(4) << static_cast<int>(static_cast<std::uint8_t>(c));
233 1047 : stringified += ss.str();
234 1047 : }
235 : else
236 : {
237 12135 : stringified += c;
238 : }
239 13182 : break;
240 :
241 : }
242 : }
243 285 : stringified += '"';
244 285 : }
245 :
246 :
247 : struct test_data_t
248 : {
249 : as2js::position f_pos = as2js::position();
250 : as2js::json::json_value::pointer_t f_value = as2js::json::json_value::pointer_t();
251 : std::uint32_t f_count = 0;
252 : };
253 :
254 :
255 : int const TYPE_NULL = 0x00000001;
256 : int const TYPE_INTEGER = 0x00000002;
257 : int const TYPE_FLOATING_POINT = 0x00000004;
258 : int const TYPE_NAN = 0x00000008;
259 : int const TYPE_PINFINITY = 0x00000010;
260 : int const TYPE_MINFINITY = 0x00000020;
261 : int const TYPE_TRUE = 0x00000040;
262 : int const TYPE_FALSE = 0x00000080;
263 : int const TYPE_STRING = 0x00000100;
264 : int const TYPE_ARRAY = 0x00000200;
265 : int const TYPE_OBJECT = 0x00000400;
266 :
267 : int const TYPE_ALL = 0x000007FF;
268 :
269 : int g_type_used = 0;
270 :
271 :
272 292 : std::string float_to_string(double f)
273 : {
274 292 : std::string s(std::to_string(f));
275 329 : while(s.back() == '0')
276 : {
277 37 : s.pop_back();
278 : }
279 292 : if(s.back() == '.')
280 : {
281 0 : s.pop_back();
282 : }
283 292 : return s;
284 : }
285 :
286 :
287 36 : void create_item(
288 : test_data_t & data
289 : , as2js::json::json_value::pointer_t parent
290 : , int depth)
291 : {
292 36 : std::size_t const max_items(rand() % 8 + 2);
293 219 : for(std::size_t j(0); j < max_items; ++j)
294 : {
295 183 : ++data.f_count;
296 183 : as2js::json::json_value::pointer_t item;
297 183 : int const select(rand() % 8);
298 183 : switch(select)
299 : {
300 23 : case 0: // NULL
301 23 : g_type_used |= TYPE_NULL;
302 23 : item = std::make_shared<as2js::json::json_value>(data.f_pos);
303 23 : break;
304 :
305 28 : case 1: // INTEGER
306 28 : g_type_used |= TYPE_INTEGER;
307 : {
308 28 : as2js::integer::value_type int_value((rand() << 13) ^ rand());
309 28 : as2js::integer integer(int_value);
310 28 : item = std::make_shared<as2js::json::json_value>(data.f_pos, integer);
311 : }
312 : break;
313 :
314 15 : case 2: // FLOATING_POINT
315 15 : switch(rand() % 10)
316 : {
317 3 : case 0:
318 3 : g_type_used |= TYPE_NAN;
319 : {
320 3 : as2js::floating_point flt;
321 3 : flt.set_nan();
322 3 : item = std::make_shared<as2js::json::json_value>(data.f_pos, flt);
323 : }
324 : break;
325 :
326 2 : case 1:
327 2 : g_type_used |= TYPE_PINFINITY;
328 : {
329 2 : as2js::floating_point flt;
330 2 : flt.set_infinity();
331 2 : item = std::make_shared<as2js::json::json_value>(data.f_pos, flt);
332 : }
333 : break;
334 :
335 2 : case 2:
336 2 : g_type_used |= TYPE_MINFINITY;
337 : {
338 2 : as2js::floating_point::value_type flt_value(-std::numeric_limits<as2js::floating_point::value_type>::infinity());
339 2 : as2js::floating_point flt(flt_value);
340 2 : item = std::make_shared<as2js::json::json_value>(data.f_pos, flt);
341 : }
342 : break;
343 :
344 8 : default:
345 8 : g_type_used |= TYPE_FLOATING_POINT;
346 : {
347 8 : as2js::floating_point::value_type flt_value(static_cast<as2js::floating_point::value_type>((rand() << 16) | rand()) / static_cast<as2js::floating_point::value_type>((rand() << 16) | rand()));
348 8 : as2js::floating_point flt(flt_value);
349 8 : item = std::make_shared<as2js::json::json_value>(data.f_pos, flt);
350 : }
351 : break;
352 :
353 : }
354 15 : break;
355 :
356 21 : case 3: // TRUE
357 21 : g_type_used |= TYPE_TRUE;
358 21 : item = std::make_shared<as2js::json::json_value>(data.f_pos, true);
359 21 : break;
360 :
361 21 : case 4: // FALSE
362 21 : g_type_used |= TYPE_FALSE;
363 21 : item = std::make_shared<as2js::json::json_value>(data.f_pos, false);
364 21 : break;
365 :
366 28 : case 5: // STRING
367 28 : g_type_used |= TYPE_STRING;
368 : {
369 28 : std::string str;
370 28 : std::string stringified;
371 28 : generate_string(str, stringified);
372 28 : item = std::make_shared<as2js::json::json_value>(data.f_pos, str);
373 28 : }
374 : break;
375 :
376 32 : case 6: // empty ARRAY
377 32 : g_type_used |= TYPE_ARRAY;
378 : {
379 32 : as2js::json::json_value::array_t empty_array;
380 32 : item = std::make_shared<as2js::json::json_value>(data.f_pos, empty_array);
381 32 : if(depth < 5 && (rand() & 1) != 0)
382 : {
383 20 : create_item(data, item, depth + 1);
384 : }
385 32 : }
386 : break;
387 :
388 15 : case 7: // empty OBJECT
389 15 : g_type_used |= TYPE_OBJECT;
390 : {
391 15 : as2js::json::json_value::object_t empty_object;
392 15 : item = std::make_shared<as2js::json::json_value>(data.f_pos, empty_object);
393 15 : if(depth < 5 && (rand() & 1) != 0)
394 : {
395 6 : create_item(data, item, depth + 1);
396 : }
397 15 : }
398 : break;
399 :
400 : // more?
401 0 : default:
402 0 : throw std::logic_error("test generated an invalid # to generate an object item");
403 :
404 : }
405 183 : if(parent->get_type() == as2js::json::json_value::type_t::JSON_TYPE_ARRAY)
406 : {
407 116 : parent->set_item(parent->get_array().size(), item);
408 : }
409 : else
410 : {
411 67 : std::string field_name;
412 67 : std::string stringified_value;
413 67 : generate_string(field_name, stringified_value);
414 67 : parent->set_member(field_name, item);
415 67 : }
416 183 : }
417 36 : }
418 :
419 :
420 3 : void create_array(test_data_t & data)
421 : {
422 3 : as2js::json::json_value::array_t array;
423 3 : data.f_value = std::make_shared<as2js::json::json_value>(data.f_pos, array);
424 3 : create_item(data, data.f_value, 0);
425 6 : }
426 :
427 :
428 7 : void create_object(test_data_t & data)
429 : {
430 7 : as2js::json::json_value::object_t object;
431 7 : data.f_value = std::make_shared<as2js::json::json_value>(data.f_pos, object);
432 7 : create_item(data, data.f_value, 0);
433 14 : }
434 :
435 :
436 579 : void data_to_string(as2js::json::json_value::pointer_t value, std::string & expected)
437 : {
438 579 : switch(value->get_type())
439 : {
440 69 : case as2js::json::json_value::type_t::JSON_TYPE_NULL:
441 69 : expected += "null";
442 69 : break;
443 :
444 63 : case as2js::json::json_value::type_t::JSON_TYPE_TRUE:
445 63 : expected += "true";
446 63 : break;
447 :
448 63 : case as2js::json::json_value::type_t::JSON_TYPE_FALSE:
449 63 : expected += "false";
450 63 : break;
451 :
452 84 : case as2js::json::json_value::type_t::JSON_TYPE_INTEGER:
453 84 : expected += std::to_string(value->get_integer().get());
454 84 : break;
455 :
456 45 : case as2js::json::json_value::type_t::JSON_TYPE_FLOATING_POINT:
457 45 : if(value->get_floating_point().is_nan())
458 : {
459 9 : expected += "NaN";
460 : }
461 36 : else if(value->get_floating_point().is_positive_infinity())
462 : {
463 6 : expected += "Infinity";
464 : }
465 30 : else if(value->get_floating_point().is_negative_infinity())
466 : {
467 6 : expected += "-Infinity";
468 : }
469 : else
470 : {
471 24 : expected += float_to_string(value->get_floating_point().get());
472 : }
473 45 : break;
474 :
475 84 : case as2js::json::json_value::type_t::JSON_TYPE_STRING:
476 84 : stringify_string(value->get_string(), expected);
477 84 : break;
478 :
479 105 : case as2js::json::json_value::type_t::JSON_TYPE_ARRAY:
480 105 : expected += '[';
481 : {
482 105 : bool first(true);
483 453 : for(auto it : value->get_array())
484 : {
485 348 : if(first)
486 : {
487 69 : first = false;
488 : }
489 : else
490 : {
491 279 : expected += ',';
492 : }
493 348 : data_to_string(it, expected); // recursive
494 348 : }
495 : }
496 105 : expected += ']';
497 105 : break;
498 :
499 66 : case as2js::json::json_value::type_t::JSON_TYPE_OBJECT:
500 66 : expected += '{';
501 : {
502 66 : bool first(true);
503 267 : for(auto it : value->get_object())
504 : {
505 201 : if(first)
506 : {
507 39 : first = false;
508 : }
509 : else
510 : {
511 162 : expected += ',';
512 : }
513 201 : stringify_string(it.first, expected);
514 201 : expected += ':';
515 201 : data_to_string(it.second, expected); // recursive
516 201 : }
517 : }
518 66 : expected += '}';
519 66 : break;
520 :
521 : // more?
522 0 : default:
523 0 : throw std::logic_error("test found an invalid JSONValue::type_t to stringify a value item");
524 :
525 : }
526 579 : }
527 :
528 :
529 : class test_callback
530 : : public as2js::message_callback
531 : {
532 : public:
533 135066 : test_callback()
534 135066 : {
535 135066 : as2js::set_message_callback(this);
536 135066 : g_warning_count = as2js::warning_count();
537 135066 : g_error_count = as2js::error_count();
538 135066 : }
539 :
540 135066 : ~test_callback()
541 135066 : {
542 : // make sure the pointer gets reset!
543 135066 : as2js::set_message_callback(nullptr);
544 135066 : }
545 :
546 : // implementation of the output
547 270129 : virtual void output(
548 : as2js::message_level_t message_level
549 : , as2js::err_code_t error_code
550 : , as2js::position const & pos
551 : , std::string const & message)
552 : {
553 270129 : CATCH_REQUIRE_FALSE(f_expected.empty());
554 :
555 : //std::cerr << "filename = " << pos.get_filename() << " / " << f_expected[0].f_pos.get_filename() << "\n";
556 : //std::cerr << "msg = " << message << " / " << f_expected[0].f_message << "\n";
557 : //std::cerr << "page = " << pos.get_page() << " / " << f_expected[0].f_pos.get_page() << "\n";
558 : //std::cerr << "error_code = " << static_cast<int>(error_code) << " / " << static_cast<int>(f_expected[0].f_error_code) << "\n";
559 :
560 270129 : CATCH_REQUIRE(f_expected[0].f_call);
561 270129 : CATCH_REQUIRE(message_level == f_expected[0].f_message_level);
562 270129 : CATCH_REQUIRE(error_code == f_expected[0].f_error_code);
563 270129 : CATCH_REQUIRE(pos.get_filename() == f_expected[0].f_pos.get_filename());
564 270129 : CATCH_REQUIRE(pos.get_function() == f_expected[0].f_pos.get_function());
565 270129 : CATCH_REQUIRE(pos.get_page() == f_expected[0].f_pos.get_page());
566 270129 : CATCH_REQUIRE(pos.get_page_line() == f_expected[0].f_pos.get_page_line());
567 270129 : CATCH_REQUIRE(pos.get_paragraph() == f_expected[0].f_pos.get_paragraph());
568 270129 : CATCH_REQUIRE(pos.get_line() == f_expected[0].f_pos.get_line());
569 270129 : CATCH_REQUIRE(message == f_expected[0].f_message);
570 :
571 270129 : if(message_level == as2js::message_level_t::MESSAGE_LEVEL_WARNING)
572 : {
573 0 : ++g_warning_count;
574 0 : CATCH_REQUIRE(g_warning_count == as2js::warning_count());
575 : }
576 :
577 270129 : if(message_level == as2js::message_level_t::MESSAGE_LEVEL_FATAL
578 135065 : || message_level == as2js::message_level_t::MESSAGE_LEVEL_ERROR)
579 : {
580 270129 : ++g_error_count;
581 : //std::cerr << "error: " << g_error_count << " / " << as2js::error_count() << "\n";
582 270129 : CATCH_REQUIRE(g_error_count == as2js::error_count());
583 : }
584 :
585 270129 : f_expected.erase(f_expected.begin());
586 270129 : }
587 :
588 135066 : void got_called()
589 : {
590 135066 : if(!f_expected.empty())
591 : {
592 0 : std::cerr << "\n*** STILL EXPECTED: ***\n";
593 0 : std::cerr << "filename = " << f_expected[0].f_pos.get_filename() << "\n";
594 0 : std::cerr << "msg = " << f_expected[0].f_message << "\n";
595 0 : std::cerr << "page = " << f_expected[0].f_pos.get_page() << "\n";
596 0 : std::cerr << "error_code = " << static_cast<int>(f_expected[0].f_error_code) << "\n";
597 : }
598 135066 : CATCH_REQUIRE(f_expected.empty());
599 135066 : }
600 :
601 : struct expected_t
602 : {
603 : bool f_call = true;
604 : as2js::message_level_t f_message_level = as2js::message_level_t::MESSAGE_LEVEL_OFF;
605 : as2js::err_code_t f_error_code = as2js::err_code_t::AS_ERR_NONE;
606 : as2js::position f_pos = as2js::position();
607 : std::string f_message = std::string(); // UTF-8 string
608 : };
609 :
610 : std::vector<expected_t> f_expected = std::vector<expected_t>();
611 :
612 : static int32_t g_warning_count;
613 : static int32_t g_error_count;
614 : };
615 :
616 : int32_t test_callback::g_warning_count = 0;
617 : int32_t test_callback::g_error_count = 0;
618 :
619 :
620 1112010 : bool is_identifier_char(std::int32_t const c)
621 : {
622 : // special cases in JavaScript identifiers
623 1112010 : if(c == 0x200C // ZWNJ
624 1112009 : || c == 0x200D) // ZWJ
625 : {
626 2 : return true;
627 : }
628 :
629 1112008 : switch(u_charType(static_cast<UChar32>(c)))
630 : {
631 135047 : case U_UPPERCASE_LETTER:
632 : case U_LOWERCASE_LETTER:
633 : case U_TITLECASE_LETTER:
634 : case U_MODIFIER_LETTER:
635 : case U_OTHER_LETTER:
636 : case U_LETTER_NUMBER:
637 : case U_NON_SPACING_MARK:
638 : case U_COMBINING_SPACING_MARK:
639 : case U_DECIMAL_DIGIT_NUMBER:
640 : case U_CONNECTOR_PUNCTUATION:
641 135047 : return true;
642 :
643 976961 : default:
644 976961 : return false;
645 :
646 : }
647 : }
648 :
649 :
650 : }
651 : // no name namespace
652 :
653 :
654 :
655 :
656 7 : CATCH_TEST_CASE("json_basic_values", "[json][basic]")
657 : {
658 : // a null pointer value...
659 7 : as2js::json::json_value::pointer_t const nullptr_value;
660 :
661 : // NULL value
662 7 : CATCH_START_SECTION("json: NULL value")
663 : {
664 1 : as2js::position pos;
665 1 : pos.reset_counters(33);
666 1 : pos.set_filename("data.json");
667 1 : pos.set_function("save_objects");
668 1 : as2js::json::json_value::pointer_t value(new as2js::json::json_value(pos));
669 1 : CATCH_REQUIRE(value->get_type() == as2js::json::json_value::type_t::JSON_TYPE_NULL);
670 :
671 1 : CATCH_REQUIRE_THROWS_MATCHES(
672 : value->get_integer().get()
673 : , as2js::internal_error
674 : , Catch::Matchers::ExceptionMessage(
675 : "internal_error: get_integer() called with a non-integer value type."));
676 :
677 1 : CATCH_REQUIRE_THROWS_MATCHES(
678 : value->get_floating_point().get()
679 : , as2js::internal_error
680 : , Catch::Matchers::ExceptionMessage(
681 : "internal_error: get_floating_point() called with a non-floating point value type."));
682 :
683 1 : CATCH_REQUIRE_THROWS_MATCHES(
684 : value->get_string()
685 : , as2js::internal_error
686 : , Catch::Matchers::ExceptionMessage(
687 : "internal_error: get_string() called with a non-string value type."));
688 :
689 1 : CATCH_REQUIRE_THROWS_MATCHES(
690 : value->get_array()
691 : , as2js::internal_error
692 : , Catch::Matchers::ExceptionMessage(
693 : "internal_error: get_array() called with a non-array value type."));
694 :
695 3 : CATCH_REQUIRE_THROWS_MATCHES(
696 : value->set_item(rand(), nullptr_value)
697 : , as2js::internal_error
698 : , Catch::Matchers::ExceptionMessage(
699 : "internal_error: set_item() called with a non-array value type."));
700 :
701 1 : CATCH_REQUIRE_THROWS_MATCHES(
702 : value->get_object()
703 : , as2js::internal_error
704 : , Catch::Matchers::ExceptionMessage(
705 : "internal_error: get_object() called with a non-object value type."));
706 :
707 7 : CATCH_REQUIRE_THROWS_MATCHES(
708 : value->set_member("name", nullptr_value)
709 : , as2js::internal_error
710 : , Catch::Matchers::ExceptionMessage(
711 : "internal_error: set_member() called with a non-object value type."));
712 :
713 1 : as2js::position const& p(value->get_position());
714 1 : CATCH_REQUIRE(p.get_filename() == pos.get_filename());
715 1 : CATCH_REQUIRE(p.get_function() == pos.get_function());
716 1 : CATCH_REQUIRE(p.get_line() == 33);
717 1 : CATCH_REQUIRE(value->to_string() == "null");
718 : // copy operator
719 1 : as2js::json::json_value copy(*value);
720 1 : CATCH_REQUIRE(copy.get_type() == as2js::json::json_value::type_t::JSON_TYPE_NULL);
721 :
722 1 : CATCH_REQUIRE_THROWS_MATCHES(
723 : copy.get_integer().get()
724 : , as2js::internal_error
725 : , Catch::Matchers::ExceptionMessage(
726 : "internal_error: get_integer() called with a non-integer value type."));
727 :
728 1 : CATCH_REQUIRE_THROWS_MATCHES(
729 : copy.get_floating_point().get()
730 : , as2js::internal_error
731 : , Catch::Matchers::ExceptionMessage(
732 : "internal_error: get_floating_point() called with a non-floating point value type."));
733 :
734 1 : CATCH_REQUIRE_THROWS_MATCHES(
735 : copy.get_string()
736 : , as2js::internal_error
737 : , Catch::Matchers::ExceptionMessage(
738 : "internal_error: get_string() called with a non-string value type."));
739 :
740 1 : CATCH_REQUIRE_THROWS_MATCHES(
741 : copy.get_array()
742 : , as2js::internal_error
743 : , Catch::Matchers::ExceptionMessage(
744 : "internal_error: get_array() called with a non-array value type."));
745 :
746 3 : CATCH_REQUIRE_THROWS_MATCHES(
747 : copy.set_item(rand(), nullptr_value)
748 : , as2js::internal_error
749 : , Catch::Matchers::ExceptionMessage(
750 : "internal_error: set_item() called with a non-array value type."));
751 :
752 1 : CATCH_REQUIRE_THROWS_MATCHES(
753 : copy.get_object()
754 : , as2js::internal_error
755 : , Catch::Matchers::ExceptionMessage(
756 : "internal_error: get_object() called with a non-object value type."));
757 :
758 7 : CATCH_REQUIRE_THROWS_MATCHES(
759 : copy.set_member("name", nullptr_value)
760 : , as2js::internal_error
761 : , Catch::Matchers::ExceptionMessage(
762 : "internal_error: set_member() called with a non-object value type."));
763 :
764 1 : as2js::position const & q(copy.get_position());
765 1 : CATCH_REQUIRE(q.get_filename() == pos.get_filename());
766 1 : CATCH_REQUIRE(q.get_function() == pos.get_function());
767 1 : CATCH_REQUIRE(q.get_line() == 33);
768 1 : CATCH_REQUIRE(copy.to_string() == "null");
769 1 : }
770 7 : CATCH_END_SECTION()
771 :
772 : // TRUE value
773 7 : CATCH_START_SECTION("json: TRUE value")
774 : {
775 1 : as2js::position pos;
776 1 : pos.reset_counters(35);
777 1 : pos.set_filename("data.json");
778 1 : pos.set_function("save_objects");
779 1 : as2js::json::json_value::pointer_t value(std::make_shared<as2js::json::json_value>(pos, true));
780 : // modify out pos object to make sure that the one in value is not a reference
781 1 : pos.set_filename("verify.json");
782 1 : pos.set_function("bad_objects");
783 1 : pos.new_line();
784 1 : CATCH_REQUIRE(value->get_type() == as2js::json::json_value::type_t::JSON_TYPE_TRUE);
785 :
786 1 : CATCH_REQUIRE_THROWS_MATCHES(
787 : value->get_integer().get()
788 : , as2js::internal_error
789 : , Catch::Matchers::ExceptionMessage(
790 : "internal_error: get_integer() called with a non-integer value type."));
791 :
792 1 : CATCH_REQUIRE_THROWS_MATCHES(
793 : value->get_floating_point().get()
794 : , as2js::internal_error
795 : , Catch::Matchers::ExceptionMessage(
796 : "internal_error: get_floating_point() called with a non-floating point value type."));
797 :
798 1 : CATCH_REQUIRE_THROWS_MATCHES(
799 : value->get_string()
800 : , as2js::internal_error
801 : , Catch::Matchers::ExceptionMessage(
802 : "internal_error: get_string() called with a non-string value type."));
803 :
804 1 : CATCH_REQUIRE_THROWS_MATCHES(
805 : value->get_array()
806 : , as2js::internal_error
807 : , Catch::Matchers::ExceptionMessage(
808 : "internal_error: get_array() called with a non-array value type."));
809 :
810 3 : CATCH_REQUIRE_THROWS_MATCHES(
811 : value->set_item(rand(), nullptr_value)
812 : , as2js::internal_error
813 : , Catch::Matchers::ExceptionMessage(
814 : "internal_error: set_item() called with a non-array value type."));
815 :
816 1 : CATCH_REQUIRE_THROWS_MATCHES(
817 : value->get_object()
818 : , as2js::internal_error
819 : , Catch::Matchers::ExceptionMessage(
820 : "internal_error: get_object() called with a non-object value type."));
821 :
822 7 : CATCH_REQUIRE_THROWS_MATCHES(
823 : value->set_member("name", nullptr_value)
824 : , as2js::internal_error
825 : , Catch::Matchers::ExceptionMessage(
826 : "internal_error: set_member() called with a non-object value type."));
827 :
828 1 : as2js::position const & p(value->get_position());
829 1 : CATCH_REQUIRE(p.get_filename() == "data.json");
830 1 : CATCH_REQUIRE(p.get_function() == "save_objects");
831 1 : CATCH_REQUIRE(p.get_line() == 35);
832 1 : CATCH_REQUIRE(value->to_string() == "true");
833 : // copy operator
834 1 : as2js::json::json_value copy(*value);
835 1 : CATCH_REQUIRE(copy.get_type() == as2js::json::json_value::type_t::JSON_TYPE_TRUE);
836 :
837 1 : CATCH_REQUIRE_THROWS_MATCHES(
838 : copy.get_integer().get()
839 : , as2js::internal_error
840 : , Catch::Matchers::ExceptionMessage(
841 : "internal_error: get_integer() called with a non-integer value type."));
842 :
843 1 : CATCH_REQUIRE_THROWS_MATCHES(
844 : copy.get_floating_point().get()
845 : , as2js::internal_error
846 : , Catch::Matchers::ExceptionMessage(
847 : "internal_error: get_floating_point() called with a non-floating point value type."));
848 :
849 1 : CATCH_REQUIRE_THROWS_MATCHES(
850 : copy.get_string()
851 : , as2js::internal_error
852 : , Catch::Matchers::ExceptionMessage(
853 : "internal_error: get_string() called with a non-string value type."));
854 :
855 1 : CATCH_REQUIRE_THROWS_MATCHES(
856 : copy.get_array()
857 : , as2js::internal_error
858 : , Catch::Matchers::ExceptionMessage(
859 : "internal_error: get_array() called with a non-array value type."));
860 :
861 3 : CATCH_REQUIRE_THROWS_MATCHES(
862 : copy.set_item(rand(), nullptr_value)
863 : , as2js::internal_error
864 : , Catch::Matchers::ExceptionMessage(
865 : "internal_error: set_item() called with a non-array value type."));
866 :
867 1 : CATCH_REQUIRE_THROWS_MATCHES(
868 : copy.get_object()
869 : , as2js::internal_error
870 : , Catch::Matchers::ExceptionMessage(
871 : "internal_error: get_object() called with a non-object value type."));
872 :
873 7 : CATCH_REQUIRE_THROWS_MATCHES(
874 : copy.set_member("name", nullptr_value)
875 : , as2js::internal_error
876 : , Catch::Matchers::ExceptionMessage(
877 : "internal_error: set_member() called with a non-object value type."));
878 :
879 1 : as2js::position const & q(copy.get_position());
880 1 : CATCH_REQUIRE(q.get_filename() == "data.json");
881 1 : CATCH_REQUIRE(q.get_function() == "save_objects");
882 1 : CATCH_REQUIRE(q.get_line() == 35);
883 1 : CATCH_REQUIRE(copy.to_string() == "true");
884 1 : }
885 7 : CATCH_END_SECTION()
886 :
887 : // FALSE value
888 7 : CATCH_START_SECTION("json: FALSE value")
889 : {
890 1 : as2js::position pos;
891 1 : pos.reset_counters(53);
892 1 : pos.set_filename("data.json");
893 1 : pos.set_function("save_objects");
894 1 : as2js::json::json_value::pointer_t value(std::make_shared<as2js::json::json_value>(pos, false));
895 1 : CATCH_REQUIRE(value->get_type() == as2js::json::json_value::type_t::JSON_TYPE_FALSE);
896 :
897 1 : CATCH_REQUIRE_THROWS_MATCHES(
898 : value->get_integer().get()
899 : , as2js::internal_error
900 : , Catch::Matchers::ExceptionMessage(
901 : "internal_error: get_integer() called with a non-integer value type."));
902 :
903 1 : CATCH_REQUIRE_THROWS_MATCHES(
904 : value->get_floating_point().get()
905 : , as2js::internal_error
906 : , Catch::Matchers::ExceptionMessage(
907 : "internal_error: get_floating_point() called with a non-floating point value type."));
908 :
909 1 : CATCH_REQUIRE_THROWS_MATCHES(
910 : value->get_string()
911 : , as2js::internal_error
912 : , Catch::Matchers::ExceptionMessage(
913 : "internal_error: get_string() called with a non-string value type."));
914 :
915 1 : CATCH_REQUIRE_THROWS_MATCHES(
916 : value->get_array()
917 : , as2js::internal_error
918 : , Catch::Matchers::ExceptionMessage(
919 : "internal_error: get_array() called with a non-array value type."));
920 :
921 3 : CATCH_REQUIRE_THROWS_MATCHES(
922 : value->set_item(rand(), nullptr_value)
923 : , as2js::internal_error
924 : , Catch::Matchers::ExceptionMessage(
925 : "internal_error: set_item() called with a non-array value type."));
926 :
927 1 : CATCH_REQUIRE_THROWS_MATCHES(
928 : value->get_object()
929 : , as2js::internal_error
930 : , Catch::Matchers::ExceptionMessage(
931 : "internal_error: get_object() called with a non-object value type."));
932 :
933 7 : CATCH_REQUIRE_THROWS_MATCHES(
934 : value->set_member("name", nullptr_value)
935 : , as2js::internal_error
936 : , Catch::Matchers::ExceptionMessage(
937 : "internal_error: set_member() called with a non-object value type."));
938 :
939 1 : as2js::position const & p(value->get_position());
940 1 : CATCH_REQUIRE(p.get_filename() == pos.get_filename());
941 1 : CATCH_REQUIRE(p.get_function() == pos.get_function());
942 1 : CATCH_REQUIRE(p.get_line() == 53);
943 1 : CATCH_REQUIRE(value->to_string() == "false");
944 : // copy operator
945 1 : as2js::json::json_value copy(*value);
946 1 : CATCH_REQUIRE(copy.get_type() == as2js::json::json_value::type_t::JSON_TYPE_FALSE);
947 :
948 1 : CATCH_REQUIRE_THROWS_MATCHES(
949 : copy.get_integer().get()
950 : , as2js::internal_error
951 : , Catch::Matchers::ExceptionMessage(
952 : "internal_error: get_integer() called with a non-integer value type."));
953 :
954 1 : CATCH_REQUIRE_THROWS_MATCHES(
955 : copy.get_floating_point().get()
956 : , as2js::internal_error
957 : , Catch::Matchers::ExceptionMessage(
958 : "internal_error: get_floating_point() called with a non-floating point value type."));
959 :
960 1 : CATCH_REQUIRE_THROWS_MATCHES(
961 : copy.get_string()
962 : , as2js::internal_error
963 : , Catch::Matchers::ExceptionMessage(
964 : "internal_error: get_string() called with a non-string value type."));
965 :
966 1 : CATCH_REQUIRE_THROWS_MATCHES(
967 : copy.get_array()
968 : , as2js::internal_error
969 : , Catch::Matchers::ExceptionMessage(
970 : "internal_error: get_array() called with a non-array value type."));
971 :
972 3 : CATCH_REQUIRE_THROWS_MATCHES(
973 : copy.set_item(rand(), nullptr_value)
974 : , as2js::internal_error
975 : , Catch::Matchers::ExceptionMessage(
976 : "internal_error: set_item() called with a non-array value type."));
977 :
978 1 : CATCH_REQUIRE_THROWS_MATCHES(
979 : copy.get_object()
980 : , as2js::internal_error
981 : , Catch::Matchers::ExceptionMessage(
982 : "internal_error: get_object() called with a non-object value type."));
983 :
984 7 : CATCH_REQUIRE_THROWS_MATCHES(
985 : copy.set_member("name", nullptr_value)
986 : , as2js::internal_error
987 : , Catch::Matchers::ExceptionMessage(
988 : "internal_error: set_member() called with a non-object value type."));
989 :
990 1 : as2js::position const & q(copy.get_position());
991 1 : CATCH_REQUIRE(q.get_filename() == pos.get_filename());
992 1 : CATCH_REQUIRE(q.get_function() == pos.get_function());
993 1 : CATCH_REQUIRE(q.get_line() == 53);
994 1 : CATCH_REQUIRE(copy.to_string() == "false");
995 1 : }
996 7 : CATCH_END_SECTION()
997 :
998 : // INTEGER value
999 7 : CATCH_START_SECTION("json: INTEGER value")
1000 : {
1001 101 : for(int idx(0); idx < 100; ++idx)
1002 : {
1003 100 : as2js::position pos;
1004 100 : pos.reset_counters(103);
1005 100 : pos.set_filename("data.json");
1006 100 : pos.set_function("save_objects");
1007 100 : as2js::integer::value_type int_value((rand() << 14) ^ rand());
1008 100 : as2js::integer integer(int_value);
1009 100 : as2js::json::json_value::pointer_t value(std::make_shared<as2js::json::json_value>(pos, integer));
1010 100 : CATCH_REQUIRE(value->get_type() == as2js::json::json_value::type_t::JSON_TYPE_INTEGER);
1011 100 : CATCH_REQUIRE(value->get_integer().get() == int_value);
1012 :
1013 100 : CATCH_REQUIRE_THROWS_MATCHES(
1014 : value->get_floating_point().get()
1015 : , as2js::internal_error
1016 : , Catch::Matchers::ExceptionMessage(
1017 : "internal_error: get_floating_point() called with a non-floating point value type."));
1018 :
1019 100 : CATCH_REQUIRE_THROWS_MATCHES(
1020 : value->get_string()
1021 : , as2js::internal_error
1022 : , Catch::Matchers::ExceptionMessage(
1023 : "internal_error: get_string() called with a non-string value type."));
1024 :
1025 100 : CATCH_REQUIRE_THROWS_MATCHES(
1026 : value->get_array()
1027 : , as2js::internal_error
1028 : , Catch::Matchers::ExceptionMessage(
1029 : "internal_error: get_array() called with a non-array value type."));
1030 :
1031 300 : CATCH_REQUIRE_THROWS_MATCHES(
1032 : value->set_item(rand(), nullptr_value)
1033 : , as2js::internal_error
1034 : , Catch::Matchers::ExceptionMessage(
1035 : "internal_error: set_item() called with a non-array value type."));
1036 :
1037 100 : CATCH_REQUIRE_THROWS_MATCHES(
1038 : value->get_object()
1039 : , as2js::internal_error
1040 : , Catch::Matchers::ExceptionMessage(
1041 : "internal_error: get_object() called with a non-object value type."));
1042 :
1043 700 : CATCH_REQUIRE_THROWS_MATCHES(
1044 : value->set_member("name", nullptr_value)
1045 : , as2js::internal_error
1046 : , Catch::Matchers::ExceptionMessage(
1047 : "internal_error: set_member() called with a non-object value type."));
1048 :
1049 100 : as2js::position const & p(value->get_position());
1050 100 : CATCH_REQUIRE(p.get_filename() == pos.get_filename());
1051 100 : CATCH_REQUIRE(p.get_function() == pos.get_function());
1052 100 : CATCH_REQUIRE(p.get_line() == 103);
1053 100 : std::stringstream ss;
1054 100 : ss << integer.get();
1055 100 : std::string cmp(ss.str());
1056 100 : CATCH_REQUIRE(value->to_string() == cmp);
1057 : // copy operator
1058 100 : as2js::json::json_value copy(*value);
1059 100 : CATCH_REQUIRE(copy.get_type() == as2js::json::json_value::type_t::JSON_TYPE_INTEGER);
1060 100 : CATCH_REQUIRE(copy.get_integer().get() == int_value);
1061 :
1062 100 : CATCH_REQUIRE_THROWS_MATCHES(
1063 : copy.get_floating_point().get()
1064 : , as2js::internal_error
1065 : , Catch::Matchers::ExceptionMessage(
1066 : "internal_error: get_floating_point() called with a non-floating point value type."));
1067 :
1068 100 : CATCH_REQUIRE_THROWS_MATCHES(
1069 : copy.get_string()
1070 : , as2js::internal_error
1071 : , Catch::Matchers::ExceptionMessage(
1072 : "internal_error: get_string() called with a non-string value type."));
1073 :
1074 100 : CATCH_REQUIRE_THROWS_MATCHES(
1075 : copy.get_array()
1076 : , as2js::internal_error
1077 : , Catch::Matchers::ExceptionMessage(
1078 : "internal_error: get_array() called with a non-array value type."));
1079 :
1080 300 : CATCH_REQUIRE_THROWS_MATCHES(
1081 : copy.set_item(rand(), nullptr_value)
1082 : , as2js::internal_error
1083 : , Catch::Matchers::ExceptionMessage(
1084 : "internal_error: set_item() called with a non-array value type."));
1085 :
1086 100 : CATCH_REQUIRE_THROWS_MATCHES(
1087 : copy.get_object()
1088 : , as2js::internal_error
1089 : , Catch::Matchers::ExceptionMessage(
1090 : "internal_error: get_object() called with a non-object value type."));
1091 :
1092 700 : CATCH_REQUIRE_THROWS_MATCHES(
1093 : copy.set_member("name", nullptr_value)
1094 : , as2js::internal_error
1095 : , Catch::Matchers::ExceptionMessage(
1096 : "internal_error: set_member() called with a non-object value type."));
1097 :
1098 100 : as2js::position const & q(copy.get_position());
1099 100 : CATCH_REQUIRE(q.get_filename() == pos.get_filename());
1100 100 : CATCH_REQUIRE(q.get_function() == pos.get_function());
1101 100 : CATCH_REQUIRE(q.get_line() == 103);
1102 100 : CATCH_REQUIRE(copy.to_string() == cmp);
1103 100 : }
1104 : }
1105 7 : CATCH_END_SECTION()
1106 :
1107 : // FLOATING_POINT value
1108 7 : CATCH_START_SECTION("json: FLOATING_POINT NaN value")
1109 : {
1110 1 : as2js::position pos;
1111 1 : pos.reset_counters(144);
1112 1 : pos.set_filename("data.json");
1113 1 : pos.set_function("save_objects");
1114 1 : as2js::floating_point::value_type flt_value(std::numeric_limits<as2js::floating_point::value_type>::quiet_NaN());
1115 1 : as2js::floating_point flt(flt_value);
1116 1 : as2js::json::json_value::pointer_t value(new as2js::json::json_value(pos, flt));
1117 1 : CATCH_REQUIRE(value->get_type() == as2js::json::json_value::type_t::JSON_TYPE_FLOATING_POINT);
1118 :
1119 1 : CATCH_REQUIRE_THROWS_MATCHES(
1120 : value->get_integer().get()
1121 : , as2js::internal_error
1122 : , Catch::Matchers::ExceptionMessage(
1123 : "internal_error: get_integer() called with a non-integer value type."));
1124 :
1125 : #pragma GCC diagnostic push
1126 : #pragma GCC diagnostic ignored "-Wfloat-equal"
1127 : // NaN's do not compare equal
1128 1 : bool const unequal_nan(value->get_floating_point().get() != flt_value);
1129 : #pragma GCC diagnostic pop
1130 1 : CATCH_REQUIRE(unequal_nan);
1131 :
1132 1 : CATCH_REQUIRE_THROWS_MATCHES(
1133 : value->get_string()
1134 : , as2js::internal_error
1135 : , Catch::Matchers::ExceptionMessage(
1136 : "internal_error: get_string() called with a non-string value type."));
1137 :
1138 1 : CATCH_REQUIRE_THROWS_MATCHES(
1139 : value->get_array()
1140 : , as2js::internal_error
1141 : , Catch::Matchers::ExceptionMessage(
1142 : "internal_error: get_array() called with a non-array value type."));
1143 :
1144 3 : CATCH_REQUIRE_THROWS_MATCHES(
1145 : value->set_item(rand(), nullptr_value)
1146 : , as2js::internal_error
1147 : , Catch::Matchers::ExceptionMessage(
1148 : "internal_error: set_item() called with a non-array value type."));
1149 :
1150 1 : CATCH_REQUIRE_THROWS_MATCHES(
1151 : value->get_object()
1152 : , as2js::internal_error
1153 : , Catch::Matchers::ExceptionMessage(
1154 : "internal_error: get_object() called with a non-object value type."));
1155 :
1156 7 : CATCH_REQUIRE_THROWS_MATCHES(
1157 : value->set_member("name", nullptr_value)
1158 : , as2js::internal_error
1159 : , Catch::Matchers::ExceptionMessage(
1160 : "internal_error: set_member() called with a non-object value type."));
1161 :
1162 1 : as2js::position const & p(value->get_position());
1163 1 : CATCH_REQUIRE(p.get_filename() == pos.get_filename());
1164 1 : CATCH_REQUIRE(p.get_function() == pos.get_function());
1165 1 : CATCH_REQUIRE(p.get_line() == 144);
1166 : //std::cerr << "compare " << value->to_string() << " with " << cmp << "\n";
1167 1 : CATCH_REQUIRE(value->to_string() == "NaN");
1168 : // copy operator
1169 1 : as2js::json::json_value copy(*value);
1170 1 : CATCH_REQUIRE(copy.get_type() == as2js::json::json_value::type_t::JSON_TYPE_FLOATING_POINT);
1171 :
1172 1 : CATCH_REQUIRE_THROWS_MATCHES(
1173 : copy.get_integer().get()
1174 : , as2js::internal_error
1175 : , Catch::Matchers::ExceptionMessage(
1176 : "internal_error: get_integer() called with a non-integer value type."));
1177 :
1178 : #pragma GCC diagnostic push
1179 : #pragma GCC diagnostic ignored "-Wfloat-equal"
1180 : // NaN's do not compare equal
1181 1 : bool const copy_unequal_nan(copy.get_floating_point().get() != flt_value);
1182 : #pragma GCC diagnostic pop
1183 1 : CATCH_REQUIRE(copy_unequal_nan);
1184 :
1185 1 : CATCH_REQUIRE_THROWS_MATCHES(
1186 : copy.get_string()
1187 : , as2js::internal_error
1188 : , Catch::Matchers::ExceptionMessage(
1189 : "internal_error: get_string() called with a non-string value type."));
1190 :
1191 1 : CATCH_REQUIRE_THROWS_MATCHES(
1192 : copy.get_array()
1193 : , as2js::internal_error
1194 : , Catch::Matchers::ExceptionMessage(
1195 : "internal_error: get_array() called with a non-array value type."));
1196 :
1197 3 : CATCH_REQUIRE_THROWS_MATCHES(
1198 : copy.set_item(rand(), nullptr_value)
1199 : , as2js::internal_error
1200 : , Catch::Matchers::ExceptionMessage(
1201 : "internal_error: set_item() called with a non-array value type."));
1202 :
1203 1 : CATCH_REQUIRE_THROWS_MATCHES(
1204 : copy.get_object()
1205 : , as2js::internal_error
1206 : , Catch::Matchers::ExceptionMessage(
1207 : "internal_error: get_object() called with a non-object value type."));
1208 :
1209 7 : CATCH_REQUIRE_THROWS_MATCHES(
1210 : copy.set_member("name", nullptr_value)
1211 : , as2js::internal_error
1212 : , Catch::Matchers::ExceptionMessage(
1213 : "internal_error: set_member() called with a non-object value type."));
1214 :
1215 1 : as2js::position const & q(copy.get_position());
1216 1 : CATCH_REQUIRE(q.get_filename() == pos.get_filename());
1217 1 : CATCH_REQUIRE(q.get_function() == pos.get_function());
1218 1 : CATCH_REQUIRE(q.get_line() == 144);
1219 1 : CATCH_REQUIRE(copy.to_string() == "NaN");
1220 1 : }
1221 7 : CATCH_END_SECTION()
1222 :
1223 7 : CATCH_START_SECTION("json: FLOATING_POINT value")
1224 : {
1225 101 : for(int idx(0); idx < 100; ++idx)
1226 : {
1227 100 : as2js::position pos;
1228 100 : pos.reset_counters(44);
1229 100 : pos.set_filename("data.json");
1230 100 : pos.set_function("save_objects");
1231 100 : as2js::floating_point::value_type flt_value(static_cast<as2js::floating_point::value_type>(rand()) / static_cast<as2js::floating_point::value_type>(rand()));
1232 100 : as2js::floating_point flt(flt_value);
1233 100 : as2js::json::json_value::pointer_t value(std::make_shared<as2js::json::json_value>(pos, flt));
1234 100 : CATCH_REQUIRE(value->get_type() == as2js::json::json_value::type_t::JSON_TYPE_FLOATING_POINT);
1235 :
1236 100 : CATCH_REQUIRE_THROWS_MATCHES(
1237 : value->get_integer().get()
1238 : , as2js::internal_error
1239 : , Catch::Matchers::ExceptionMessage(
1240 : "internal_error: get_integer() called with a non-integer value type."));
1241 :
1242 100 : CATCH_REQUIRE_FLOATING_POINT(value->get_floating_point().get(), flt_value);
1243 :
1244 100 : CATCH_REQUIRE_THROWS_MATCHES(
1245 : value->get_string()
1246 : , as2js::internal_error
1247 : , Catch::Matchers::ExceptionMessage(
1248 : "internal_error: get_string() called with a non-string value type."));
1249 :
1250 100 : CATCH_REQUIRE_THROWS_MATCHES(
1251 : value->get_array()
1252 : , as2js::internal_error
1253 : , Catch::Matchers::ExceptionMessage(
1254 : "internal_error: get_array() called with a non-array value type."));
1255 :
1256 300 : CATCH_REQUIRE_THROWS_MATCHES(
1257 : value->set_item(rand(), nullptr_value)
1258 : , as2js::internal_error
1259 : , Catch::Matchers::ExceptionMessage(
1260 : "internal_error: set_item() called with a non-array value type."));
1261 :
1262 100 : CATCH_REQUIRE_THROWS_MATCHES(
1263 : value->get_object()
1264 : , as2js::internal_error
1265 : , Catch::Matchers::ExceptionMessage(
1266 : "internal_error: get_object() called with a non-object value type."));
1267 :
1268 700 : CATCH_REQUIRE_THROWS_MATCHES(
1269 : value->set_member("name", nullptr_value)
1270 : , as2js::internal_error
1271 : , Catch::Matchers::ExceptionMessage(
1272 : "internal_error: set_member() called with a non-object value type."));
1273 :
1274 100 : as2js::position const & p(value->get_position());
1275 100 : CATCH_REQUIRE(p.get_filename() == pos.get_filename());
1276 100 : CATCH_REQUIRE(p.get_function() == pos.get_function());
1277 100 : CATCH_REQUIRE(p.get_line() == 44);
1278 100 : std::string const cmp(float_to_string(flt_value));
1279 : //std::cerr << "compare " << value->to_string() << " with " << cmp << "\n";
1280 100 : CATCH_REQUIRE(value->to_string() == cmp);
1281 : // copy operator
1282 100 : as2js::json::json_value copy(*value);
1283 100 : CATCH_REQUIRE(copy.get_type() == as2js::json::json_value::type_t::JSON_TYPE_FLOATING_POINT);
1284 :
1285 100 : CATCH_REQUIRE_THROWS_MATCHES(
1286 : copy.get_integer().get()
1287 : , as2js::internal_error
1288 : , Catch::Matchers::ExceptionMessage(
1289 : "internal_error: get_integer() called with a non-integer value type."));
1290 :
1291 100 : CATCH_REQUIRE_FLOATING_POINT(copy.get_floating_point().get(), flt_value);
1292 :
1293 100 : CATCH_REQUIRE_THROWS_MATCHES(
1294 : copy.get_string()
1295 : , as2js::internal_error
1296 : , Catch::Matchers::ExceptionMessage(
1297 : "internal_error: get_string() called with a non-string value type."));
1298 :
1299 100 : CATCH_REQUIRE_THROWS_MATCHES(
1300 : copy.get_array()
1301 : , as2js::internal_error
1302 : , Catch::Matchers::ExceptionMessage(
1303 : "internal_error: get_array() called with a non-array value type."));
1304 :
1305 300 : CATCH_REQUIRE_THROWS_MATCHES(
1306 : copy.set_item(rand(), nullptr_value)
1307 : , as2js::internal_error
1308 : , Catch::Matchers::ExceptionMessage(
1309 : "internal_error: set_item() called with a non-array value type."));
1310 :
1311 100 : CATCH_REQUIRE_THROWS_MATCHES(
1312 : copy.get_object()
1313 : , as2js::internal_error
1314 : , Catch::Matchers::ExceptionMessage(
1315 : "internal_error: get_object() called with a non-object value type."));
1316 :
1317 700 : CATCH_REQUIRE_THROWS_MATCHES(
1318 : copy.set_member("name", nullptr_value)
1319 : , as2js::internal_error
1320 : , Catch::Matchers::ExceptionMessage(
1321 : "internal_error: set_member() called with a non-object value type."));
1322 :
1323 100 : as2js::position const & q(copy.get_position());
1324 100 : CATCH_REQUIRE(q.get_filename() == pos.get_filename());
1325 100 : CATCH_REQUIRE(q.get_function() == pos.get_function());
1326 100 : CATCH_REQUIRE(q.get_line() == 44);
1327 100 : CATCH_REQUIRE(copy.to_string() == cmp);
1328 100 : }
1329 : }
1330 7 : CATCH_END_SECTION()
1331 :
1332 : // STRING value
1333 7 : CATCH_START_SECTION("json: STRING value")
1334 : {
1335 101 : for(size_t idx(0), used(0); idx < 100 || used != 0xFF; ++idx)
1336 : {
1337 100 : as2js::position pos;
1338 100 : pos.reset_counters(89);
1339 100 : pos.set_filename("data.json");
1340 100 : pos.set_function("save_objects");
1341 100 : std::string str;
1342 100 : std::string stringified;
1343 100 : used |= generate_string(str, stringified);
1344 100 : as2js::json::json_value::pointer_t value(std::make_shared<as2js::json::json_value>(pos, str));
1345 100 : CATCH_REQUIRE(value->get_type() == as2js::json::json_value::type_t::JSON_TYPE_STRING);
1346 :
1347 100 : CATCH_REQUIRE_THROWS_MATCHES(
1348 : value->get_integer().get()
1349 : , as2js::internal_error
1350 : , Catch::Matchers::ExceptionMessage(
1351 : "internal_error: get_integer() called with a non-integer value type."));
1352 :
1353 100 : CATCH_REQUIRE_THROWS_MATCHES(
1354 : value->get_floating_point().get()
1355 : , as2js::internal_error
1356 : , Catch::Matchers::ExceptionMessage(
1357 : "internal_error: get_floating_point() called with a non-floating point value type."));
1358 :
1359 100 : CATCH_REQUIRE(value->get_string() == str);
1360 :
1361 100 : CATCH_REQUIRE_THROWS_MATCHES(
1362 : value->get_array()
1363 : , as2js::internal_error
1364 : , Catch::Matchers::ExceptionMessage(
1365 : "internal_error: get_array() called with a non-array value type."));
1366 :
1367 300 : CATCH_REQUIRE_THROWS_MATCHES(
1368 : value->set_item(rand(), nullptr_value)
1369 : , as2js::internal_error
1370 : , Catch::Matchers::ExceptionMessage(
1371 : "internal_error: set_item() called with a non-array value type."));
1372 :
1373 100 : CATCH_REQUIRE_THROWS_MATCHES(
1374 : value->get_object()
1375 : , as2js::internal_error
1376 : , Catch::Matchers::ExceptionMessage(
1377 : "internal_error: get_object() called with a non-object value type."));
1378 :
1379 700 : CATCH_REQUIRE_THROWS_MATCHES(
1380 : value->set_member("name", nullptr_value)
1381 : , as2js::internal_error
1382 : , Catch::Matchers::ExceptionMessage(
1383 : "internal_error: set_member() called with a non-object value type."));
1384 :
1385 100 : as2js::position const & p(value->get_position());
1386 100 : CATCH_REQUIRE(p.get_filename() == pos.get_filename());
1387 100 : CATCH_REQUIRE(p.get_function() == pos.get_function());
1388 100 : CATCH_REQUIRE(p.get_line() == 89);
1389 : #if 0
1390 : std::string r(value->to_string());
1391 : std::cerr << std::hex << " lengths " << r.length() << " / " << stringified.length() << "\n";
1392 : size_t max_chrs(std::min(r.length(), stringified.length()));
1393 : for(size_t g(0); g < max_chrs; ++g)
1394 : {
1395 : if(static_cast<int>(r[g]) != static_cast<int>(stringified[g]))
1396 : {
1397 : std::cerr << " --- " << static_cast<int>(static_cast<std::uint8_t>(r[g])) << " / " << static_cast<int>(static_cast<std::uint8_t>(stringified[g])) << "\n";
1398 : }
1399 : else
1400 : {
1401 : std::cerr << " " << static_cast<int>(static_cast<std::uint8_t>(r[g])) << " / " << static_cast<int>(static_cast<std::uint8_t>(stringified[g])) << "\n";
1402 : }
1403 : }
1404 : if(r.length() > stringified.length())
1405 : {
1406 : for(size_t g(stringified.length()); g < r.length(); ++g)
1407 : {
1408 : std::cerr << " *** " << static_cast<int>(static_cast<std::uint8_t>(r[g])) << "\n";
1409 : }
1410 : }
1411 : else
1412 : {
1413 : for(size_t g(r.length()); g < stringified.length(); ++g)
1414 : {
1415 : std::cerr << " +++ " << static_cast<int>(static_cast<std::uint8_t>(stringified[g])) << "\n";
1416 : }
1417 : }
1418 : std::cerr << std::dec;
1419 : #endif
1420 100 : CATCH_REQUIRE(value->to_string() == stringified);
1421 : // copy operator
1422 100 : as2js::json::json_value copy(*value);
1423 100 : CATCH_REQUIRE(copy.get_type() == as2js::json::json_value::type_t::JSON_TYPE_STRING);
1424 :
1425 100 : CATCH_REQUIRE_THROWS_MATCHES(
1426 : copy.get_integer().get()
1427 : , as2js::internal_error
1428 : , Catch::Matchers::ExceptionMessage(
1429 : "internal_error: get_integer() called with a non-integer value type."));
1430 :
1431 100 : CATCH_REQUIRE_THROWS_MATCHES(
1432 : copy.get_floating_point().get()
1433 : , as2js::internal_error
1434 : , Catch::Matchers::ExceptionMessage(
1435 : "internal_error: get_floating_point() called with a non-floating point value type."));
1436 :
1437 100 : CATCH_REQUIRE(copy.get_string() == str);
1438 :
1439 100 : CATCH_REQUIRE_THROWS_MATCHES(
1440 : copy.get_array()
1441 : , as2js::internal_error
1442 : , Catch::Matchers::ExceptionMessage(
1443 : "internal_error: get_array() called with a non-array value type."));
1444 :
1445 300 : CATCH_REQUIRE_THROWS_MATCHES(
1446 : copy.set_item(rand(), nullptr_value)
1447 : , as2js::internal_error
1448 : , Catch::Matchers::ExceptionMessage(
1449 : "internal_error: set_item() called with a non-array value type."));
1450 :
1451 100 : CATCH_REQUIRE_THROWS_MATCHES(
1452 : copy.get_object()
1453 : , as2js::internal_error
1454 : , Catch::Matchers::ExceptionMessage(
1455 : "internal_error: get_object() called with a non-object value type."));
1456 :
1457 700 : CATCH_REQUIRE_THROWS_MATCHES(
1458 : copy.set_member("name", nullptr_value)
1459 : , as2js::internal_error
1460 : , Catch::Matchers::ExceptionMessage(
1461 : "internal_error: set_member() called with a non-object value type."));
1462 :
1463 100 : as2js::position const & q(copy.get_position());
1464 100 : CATCH_REQUIRE(q.get_filename() == pos.get_filename());
1465 100 : CATCH_REQUIRE(q.get_function() == pos.get_function());
1466 100 : CATCH_REQUIRE(q.get_line() == 89);
1467 100 : CATCH_REQUIRE(copy.to_string() == stringified);
1468 100 : }
1469 : }
1470 7 : CATCH_END_SECTION()
1471 14 : }
1472 :
1473 :
1474 2 : CATCH_TEST_CASE("json_array", "[json][array]")
1475 : {
1476 : // a null pointer value...
1477 2 : as2js::json::json_value::pointer_t const nullptr_value;
1478 :
1479 : // test with an empty array
1480 2 : CATCH_START_SECTION("json: empty array")
1481 : {
1482 1 : as2js::position pos;
1483 1 : pos.reset_counters(109);
1484 1 : pos.set_filename("array.json");
1485 1 : pos.set_function("save_array");
1486 1 : as2js::json::json_value::array_t initial;
1487 1 : as2js::json::json_value::pointer_t value(std::make_shared<as2js::json::json_value>(pos, initial));
1488 1 : CATCH_REQUIRE(value->get_type() == as2js::json::json_value::type_t::JSON_TYPE_ARRAY);
1489 :
1490 1 : CATCH_REQUIRE_THROWS_MATCHES(
1491 : value->get_integer().get()
1492 : , as2js::internal_error
1493 : , Catch::Matchers::ExceptionMessage(
1494 : "internal_error: get_integer() called with a non-integer value type."));
1495 :
1496 1 : CATCH_REQUIRE_THROWS_MATCHES(
1497 : value->get_floating_point().get()
1498 : , as2js::internal_error
1499 : , Catch::Matchers::ExceptionMessage(
1500 : "internal_error: get_floating_point() called with a non-floating point value type."));
1501 :
1502 1 : CATCH_REQUIRE_THROWS_MATCHES(
1503 : value->get_string()
1504 : , as2js::internal_error
1505 : , Catch::Matchers::ExceptionMessage(
1506 : "internal_error: get_string() called with a non-string value type."));
1507 :
1508 1 : as2js::json::json_value::array_t const & array(value->get_array());
1509 1 : CATCH_REQUIRE(array.empty());
1510 22 : for(int idx(-10); idx <= 10; ++idx)
1511 : {
1512 21 : if(idx == 0)
1513 : {
1514 : // nullptr is not valid for data
1515 3 : CATCH_REQUIRE_THROWS_MATCHES(
1516 : value->set_item(idx, nullptr_value)
1517 : , as2js::invalid_data
1518 : , Catch::Matchers::ExceptionMessage(
1519 : "as2js_exception: json::json_value::set_item() called with a null pointer as the value."));
1520 : }
1521 : else
1522 : {
1523 : // index is invalid
1524 60 : CATCH_REQUIRE_THROWS_MATCHES(
1525 : value->set_item(idx, nullptr_value)
1526 : , as2js::out_of_range
1527 : , Catch::Matchers::ExceptionMessage(
1528 : "out_of_range: json::json_value::set_item() called with an index out of range."));
1529 : }
1530 : }
1531 :
1532 1 : CATCH_REQUIRE_THROWS_MATCHES(
1533 : value->get_object()
1534 : , as2js::internal_error
1535 : , Catch::Matchers::ExceptionMessage(
1536 : "internal_error: get_object() called with a non-object value type."));
1537 :
1538 7 : CATCH_REQUIRE_THROWS_MATCHES(
1539 : value->set_member("name", nullptr_value)
1540 : , as2js::internal_error
1541 : , Catch::Matchers::ExceptionMessage(
1542 : "internal_error: set_member() called with a non-object value type."));
1543 :
1544 1 : as2js::position const & p(value->get_position());
1545 1 : CATCH_REQUIRE(p.get_filename() == pos.get_filename());
1546 1 : CATCH_REQUIRE(p.get_function() == pos.get_function());
1547 1 : CATCH_REQUIRE(p.get_line() == 109);
1548 : //std::cerr << "compare " << value->to_string() << " with " << cmp << "\n";
1549 1 : CATCH_REQUIRE(value->to_string() == "[]");
1550 : // copy operator
1551 1 : as2js::json::json_value copy(*value);
1552 1 : CATCH_REQUIRE(copy.get_type() == as2js::json::json_value::type_t::JSON_TYPE_ARRAY);
1553 :
1554 1 : CATCH_REQUIRE_THROWS_MATCHES(
1555 : copy.get_integer().get()
1556 : , as2js::internal_error
1557 : , Catch::Matchers::ExceptionMessage(
1558 : "internal_error: get_integer() called with a non-integer value type."));
1559 :
1560 1 : CATCH_REQUIRE_THROWS_MATCHES(
1561 : copy.get_floating_point().get()
1562 : , as2js::internal_error
1563 : , Catch::Matchers::ExceptionMessage(
1564 : "internal_error: get_floating_point() called with a non-floating point value type."));
1565 :
1566 1 : CATCH_REQUIRE_THROWS_MATCHES(
1567 : copy.get_string()
1568 : , as2js::internal_error
1569 : , Catch::Matchers::ExceptionMessage(
1570 : "internal_error: get_string() called with a non-string value type."));
1571 :
1572 1 : as2js::json::json_value::array_t const & array_copy(copy.get_array());
1573 1 : CATCH_REQUIRE(array_copy.empty());
1574 22 : for(int idx(-10); idx <= 10; ++idx)
1575 : {
1576 21 : if(idx == 0)
1577 : {
1578 : // nullptr is not valid for data
1579 : //CPPUNIT_ASSERT_THROW(copy.set_item(idx, nullptr_value), as2js::invalid_data);
1580 :
1581 3 : CATCH_REQUIRE_THROWS_MATCHES(
1582 : copy.set_item(idx, nullptr_value)
1583 : , as2js::invalid_data
1584 : , Catch::Matchers::ExceptionMessage(
1585 : "as2js_exception: json::json_value::set_item() called with a null pointer as the value."));
1586 : }
1587 : else
1588 : {
1589 : // index is invalid
1590 : //CPPUNIT_ASSERT_THROW(copy.set_item(idx, nullptr_value), as2js::out_of_range);
1591 :
1592 60 : CATCH_REQUIRE_THROWS_MATCHES(
1593 : copy.set_item(idx, nullptr_value)
1594 : , as2js::out_of_range
1595 : , Catch::Matchers::ExceptionMessage(
1596 : "out_of_range: json::json_value::set_item() called with an index out of range."));
1597 : }
1598 : }
1599 :
1600 1 : CATCH_REQUIRE_THROWS_MATCHES(
1601 : copy.get_object()
1602 : , as2js::internal_error
1603 : , Catch::Matchers::ExceptionMessage(
1604 : "internal_error: get_object() called with a non-object value type."));
1605 :
1606 7 : CATCH_REQUIRE_THROWS_MATCHES(
1607 : copy.set_member("name", nullptr_value)
1608 : , as2js::internal_error
1609 : , Catch::Matchers::ExceptionMessage(
1610 : "internal_error: set_member() called with a non-object value type."));
1611 :
1612 1 : as2js::position const& q(copy.get_position());
1613 1 : CATCH_REQUIRE(q.get_filename() == pos.get_filename());
1614 1 : CATCH_REQUIRE(q.get_function() == pos.get_function());
1615 1 : CATCH_REQUIRE(q.get_line() == 109);
1616 1 : CATCH_REQUIRE(copy.to_string() == "[]");
1617 1 : }
1618 2 : CATCH_END_SECTION()
1619 :
1620 : // test with a few random arrays
1621 2 : CATCH_START_SECTION("json: random array value")
1622 : {
1623 11 : for(int idx(0); idx < 10; ++idx)
1624 : {
1625 10 : as2js::position pos;
1626 10 : pos.reset_counters(109);
1627 10 : pos.set_filename("array.json");
1628 10 : pos.set_function("save_array");
1629 10 : as2js::json::json_value::array_t initial;
1630 :
1631 20 : std::string result("[");
1632 10 : size_t const max_items(rand() % 100 + 20);
1633 777 : for(size_t j(0); j < max_items; ++j)
1634 : {
1635 767 : if(j != 0)
1636 : {
1637 757 : result += ",";
1638 : }
1639 767 : as2js::json::json_value::pointer_t item;
1640 767 : int const select(rand() % 8);
1641 767 : switch(select)
1642 : {
1643 91 : case 0: // NULL
1644 91 : item.reset(new as2js::json::json_value(pos));
1645 91 : result += "null";
1646 91 : break;
1647 :
1648 84 : case 1: // INTEGER
1649 : {
1650 84 : as2js::integer::value_type int_value((rand() << 13) ^ rand());
1651 84 : as2js::integer integer(int_value);
1652 84 : item = std::make_shared<as2js::json::json_value>(pos, integer);
1653 84 : result += std::to_string(int_value);
1654 : }
1655 : break;
1656 :
1657 91 : case 2: // FLOATING_POINT
1658 : {
1659 91 : as2js::floating_point::value_type flt_value(static_cast<as2js::floating_point::value_type>((rand() << 16) | rand()) / static_cast<as2js::floating_point::value_type>((rand() << 16) | rand()));
1660 91 : as2js::floating_point flt(flt_value);
1661 91 : item = std::make_shared<as2js::json::json_value>(pos, flt);
1662 91 : result += float_to_string(flt_value);
1663 : }
1664 : break;
1665 :
1666 107 : case 3: // TRUE
1667 107 : item.reset(new as2js::json::json_value(pos, true));
1668 107 : result += "true";
1669 107 : break;
1670 :
1671 101 : case 4: // FALSE
1672 101 : item.reset(new as2js::json::json_value(pos, false));
1673 101 : result += "false";
1674 101 : break;
1675 :
1676 94 : case 5: // STRING
1677 : {
1678 94 : std::string str;
1679 94 : std::string stringified;
1680 94 : generate_string(str, stringified);
1681 94 : item.reset(new as2js::json::json_value(pos, str));
1682 94 : result += stringified;
1683 94 : }
1684 : break;
1685 :
1686 99 : case 6: // empty ARRAY
1687 : {
1688 99 : as2js::json::json_value::array_t empty_array;
1689 99 : item.reset(new as2js::json::json_value(pos, empty_array));
1690 99 : result += "[]";
1691 99 : }
1692 : break;
1693 :
1694 100 : case 7: // empty OBJECT
1695 : {
1696 100 : as2js::json::json_value::object_t empty_object;
1697 100 : item.reset(new as2js::json::json_value(pos, empty_object));
1698 100 : result += "{}";
1699 100 : }
1700 : break;
1701 :
1702 : // more?
1703 0 : default:
1704 0 : throw std::logic_error("test generated an invalid # to generate an array item");
1705 :
1706 : }
1707 767 : initial.push_back(item);
1708 767 : }
1709 10 : result += "]";
1710 :
1711 20 : as2js::json::json_value::pointer_t value(std::make_shared<as2js::json::json_value>(pos, initial));
1712 10 : CATCH_REQUIRE(value->get_type() == as2js::json::json_value::type_t::JSON_TYPE_ARRAY);
1713 :
1714 10 : CATCH_REQUIRE_THROWS_MATCHES(
1715 : value->get_integer().get()
1716 : , as2js::internal_error
1717 : , Catch::Matchers::ExceptionMessage(
1718 : "internal_error: get_integer() called with a non-integer value type."));
1719 :
1720 10 : CATCH_REQUIRE_THROWS_MATCHES(
1721 : value->get_floating_point().get()
1722 : , as2js::internal_error
1723 : , Catch::Matchers::ExceptionMessage(
1724 : "internal_error: get_floating_point() called with a non-floating point value type."));
1725 :
1726 10 : CATCH_REQUIRE_THROWS_MATCHES(
1727 : value->get_string()
1728 : , as2js::internal_error
1729 : , Catch::Matchers::ExceptionMessage(
1730 : "internal_error: get_string() called with a non-string value type."));
1731 :
1732 10 : as2js::json::json_value::array_t const& array(value->get_array());
1733 10 : CATCH_REQUIRE(array.size() == max_items);
1734 : //for(int idx(-10); idx <= 10; ++idx)
1735 : //{
1736 : // if(idx == 0)
1737 : // {
1738 : // // nullptr is not valid for data
1739 : // CPPUNIT_ASSERT_THROW(value->set_item(idx, nullptr_value), as2js::invalid_data);
1740 : // }
1741 : // else
1742 : // {
1743 : // // index is invalid
1744 : // CPPUNIT_ASSERT_THROW(value->set_item(idx, nullptr_value), as2js::out_of_range);
1745 : // }
1746 : //}
1747 :
1748 10 : CATCH_REQUIRE_THROWS_MATCHES(
1749 : value->get_object()
1750 : , as2js::internal_error
1751 : , Catch::Matchers::ExceptionMessage(
1752 : "internal_error: get_object() called with a non-object value type."));
1753 :
1754 : // now setting member to nullptr deletes it from the object
1755 : //CATCH_REQUIRE_THROWS_MATCHES(
1756 : // value->set_member("name", nullptr_value)
1757 : // , as2js::internal_error
1758 : // , Catch::Matchers::ExceptionMessage(
1759 : // "internal_error: set_member() called with a non-object value type"));
1760 :
1761 : //CPPUNIT_ASSERT_THROW(value->get_object(), as2js::internal_error);
1762 : //CPPUNIT_ASSERT_THROW(value->set_member("name", nullptr_value), as2js::internal_error);
1763 10 : as2js::position const& p(value->get_position());
1764 10 : CATCH_REQUIRE(p.get_filename() == pos.get_filename());
1765 10 : CATCH_REQUIRE(p.get_function() == pos.get_function());
1766 10 : CATCH_REQUIRE(p.get_line() == 109);
1767 : #if 0
1768 : std::string r(value->to_string());
1769 : std::cerr << std::hex << " lengths " << r.length() << " / " << result.length() << "\n";
1770 : size_t max_chrs(std::min(r.length(), result.length()));
1771 : for(size_t g(0); g < max_chrs; ++g)
1772 : {
1773 : if(static_cast<int>(r[g]) != static_cast<int>(result[g]))
1774 : {
1775 : std::cerr << " --- " << static_cast<int>(static_cast<std::uint8_t>(r[g])) << " / " << static_cast<int>(static_cast<std::uint8_t>(result[g])) << "\n";
1776 : }
1777 : else
1778 : {
1779 : std::cerr << " " << static_cast<int>(static_cast<std::uint8_t>(r[g])) << " / " << static_cast<int>(static_cast<std::uint8_t>(result[g])) << "\n";
1780 : }
1781 : }
1782 : if(r.length() > result.length())
1783 : {
1784 : }
1785 : else
1786 : {
1787 : for(size_t g(r.length()); g < result.length(); ++g)
1788 : {
1789 : std::cerr << " +++ " << static_cast<int>(result[g]) << "\n";
1790 : }
1791 : }
1792 : std::cerr << std::dec;
1793 : #endif
1794 10 : CATCH_REQUIRE(value->to_string() == result);
1795 : // copy operator
1796 20 : as2js::json::json_value copy(*value);
1797 10 : CATCH_REQUIRE(copy.get_type() == as2js::json::json_value::type_t::JSON_TYPE_ARRAY);
1798 :
1799 10 : CATCH_REQUIRE_THROWS_MATCHES(
1800 : copy.get_integer().get()
1801 : , as2js::internal_error
1802 : , Catch::Matchers::ExceptionMessage(
1803 : "internal_error: get_integer() called with a non-integer value type."));
1804 :
1805 10 : CATCH_REQUIRE_THROWS_MATCHES(
1806 : copy.get_floating_point().get()
1807 : , as2js::internal_error
1808 : , Catch::Matchers::ExceptionMessage(
1809 : "internal_error: get_floating_point() called with a non-floating point value type."));
1810 :
1811 10 : CATCH_REQUIRE_THROWS_MATCHES(
1812 : copy.get_string()
1813 : , as2js::internal_error
1814 : , Catch::Matchers::ExceptionMessage(
1815 : "internal_error: get_string() called with a non-string value type."));
1816 :
1817 10 : as2js::json::json_value::array_t const& array_copy(copy.get_array());
1818 10 : CATCH_REQUIRE(array_copy.size() == max_items);
1819 : //for(int idx(-10); idx <= 10; ++idx)
1820 : //{
1821 : // if(idx == 0)
1822 : // {
1823 : // // nullptr is not valid for data
1824 : // CPPUNIT_ASSERT_THROW(copy.set_item(idx, nullptr_value), as2js::invalid_data);
1825 : // }
1826 : // else
1827 : // {
1828 : // // index is invalid
1829 : // CATCH_REQUIRE_THROWS_MATCHES(copy.set_item(idx, nullptr_value), as2js::out_of_range);
1830 : // }
1831 : //}
1832 :
1833 10 : CATCH_REQUIRE_THROWS_MATCHES(
1834 : copy.get_object()
1835 : , as2js::internal_error
1836 : , Catch::Matchers::ExceptionMessage(
1837 : "internal_error: get_object() called with a non-object value type."));
1838 :
1839 : // this now works as "delete that element in that object"
1840 : //CATCH_REQUIRE_THROWS_MATCHES(
1841 : // copy.set_member("name", nullptr_value)
1842 : // , as2js::internal_error
1843 : // , Catch::Matchers::ExceptionMessage(
1844 : // "set_member() called with a non-object value type."));
1845 :
1846 : //CPPUNIT_ASSERT_THROW(copy.get_object(), as2js::internal_error);
1847 : //CPPUNIT_ASSERT_THROW(copy.set_member("name", nullptr_value), as2js::internal_error);
1848 10 : as2js::position const& q(copy.get_position());
1849 10 : CATCH_REQUIRE(q.get_filename() == pos.get_filename());
1850 10 : CATCH_REQUIRE(q.get_function() == pos.get_function());
1851 10 : CATCH_REQUIRE(q.get_line() == 109);
1852 10 : CATCH_REQUIRE(copy.to_string() == result);
1853 : // the cyclic flag should have been reset, make sure of that:
1854 10 : CATCH_REQUIRE(copy.to_string() == result);
1855 :
1856 : // test that we catch a direct 'array[x] = array;'
1857 10 : value->set_item(max_items, value);
1858 : // copy is not affected...
1859 10 : CATCH_REQUIRE(copy.to_string() == result);
1860 : // value to string fails because it is cyclic
1861 : //CPPUNIT_ASSERT_THROW(value->to_string() == result, as2js::cyclical_structure);
1862 :
1863 10 : CATCH_REQUIRE_THROWS_MATCHES(
1864 : value->to_string()
1865 : , as2js::cyclical_structure
1866 : , Catch::Matchers::ExceptionMessage(
1867 : "as2js_exception: JSON cannot stringify a set of objects and arrays which are cyclical."));
1868 :
1869 10 : as2js::json::json_value::array_t const& cyclic_array(value->get_array());
1870 10 : CATCH_REQUIRE(cyclic_array.size() == max_items + 1);
1871 :
1872 : {
1873 10 : std::string str;
1874 10 : std::string stringified;
1875 10 : generate_string(str, stringified);
1876 10 : as2js::json::json_value::pointer_t item;
1877 10 : item.reset(new as2js::json::json_value(pos, str));
1878 : // remove the existing ']' first
1879 10 : result.erase(result.end() - 1);
1880 10 : result += ',';
1881 10 : result += stringified;
1882 10 : result += ']';
1883 10 : value->set_item(max_items, item);
1884 : //std::string r(value->to_string());
1885 : //std::cerr << std::hex << " lengths " << r.length() << " / " << result.length() << "\n";
1886 : //size_t max_chrs(std::min(r.length(), result.length()));
1887 : //for(size_t g(0); g < max_chrs; ++g)
1888 : //{
1889 : // if(static_cast<int>(r[g]) != static_cast<int>(result[g]))
1890 : // {
1891 : // std::cerr << " --- " << static_cast<int>(r[g]) << " / " << static_cast<int>(result[g]) << "\n";
1892 : // }
1893 : // else
1894 : // {
1895 : // std::cerr << " " << static_cast<int>(r[g]) << " / " << static_cast<int>(result[g]) << "\n";
1896 : // }
1897 : //}
1898 : //if(r.length() > result.length())
1899 : //{
1900 : //}
1901 : //else
1902 : //{
1903 : // for(size_t g(r.length()); g < result.length(); ++g)
1904 : // {
1905 : // std::cerr << " +++ " << static_cast<int>(result[g]) << "\n";
1906 : // }
1907 : //}
1908 : //std::cerr << std::dec;
1909 10 : CATCH_REQUIRE(value->to_string() == result);
1910 10 : }
1911 10 : }
1912 : }
1913 2 : CATCH_END_SECTION()
1914 4 : }
1915 :
1916 :
1917 2 : CATCH_TEST_CASE("json_object", "[json][object]")
1918 : {
1919 : // a null pointer value...
1920 2 : as2js::json::json_value::pointer_t const nullptr_value;
1921 :
1922 : // test with an empty object
1923 2 : CATCH_START_SECTION("json: empty object")
1924 : {
1925 1 : as2js::position pos;
1926 1 : pos.reset_counters(109);
1927 1 : pos.set_filename("object.json");
1928 1 : pos.set_function("save_object");
1929 1 : as2js::json::json_value::object_t initial;
1930 1 : as2js::json::json_value::pointer_t value(new as2js::json::json_value(pos, initial));
1931 1 : CATCH_REQUIRE(value->get_type() == as2js::json::json_value::type_t::JSON_TYPE_OBJECT);
1932 :
1933 1 : CATCH_REQUIRE_THROWS_MATCHES(
1934 : value->get_integer().get()
1935 : , as2js::internal_error
1936 : , Catch::Matchers::ExceptionMessage(
1937 : "internal_error: get_integer() called with a non-integer value type."));
1938 :
1939 1 : CATCH_REQUIRE_THROWS_MATCHES(
1940 : value->get_floating_point().get()
1941 : , as2js::internal_error
1942 : , Catch::Matchers::ExceptionMessage(
1943 : "internal_error: get_floating_point() called with a non-floating point value type."));
1944 :
1945 1 : CATCH_REQUIRE_THROWS_MATCHES(
1946 : value->get_string()
1947 : , as2js::internal_error
1948 : , Catch::Matchers::ExceptionMessage(
1949 : "internal_error: get_string() called with a non-string value type."));
1950 :
1951 1 : CATCH_REQUIRE_THROWS_MATCHES(
1952 : value->get_array()
1953 : , as2js::internal_error
1954 : , Catch::Matchers::ExceptionMessage(
1955 : "internal_error: get_array() called with a non-array value type."));
1956 :
1957 3 : CATCH_REQUIRE_THROWS_MATCHES(
1958 : value->set_item(rand(), nullptr_value)
1959 : , as2js::internal_error
1960 : , Catch::Matchers::ExceptionMessage(
1961 : "internal_error: set_item() called with a non-array value type."));
1962 :
1963 1 : as2js::json::json_value::object_t const& object(value->get_object());
1964 1 : CATCH_REQUIRE(object.empty());
1965 : // name is invalid
1966 : //CPPUNIT_ASSERT_THROW(value->set_member("", nullptr_value), as2js::invalid_index);
1967 :
1968 7 : CATCH_REQUIRE_THROWS_MATCHES(
1969 : value->set_member("", nullptr_value)
1970 : , as2js::invalid_index
1971 : , Catch::Matchers::ExceptionMessage(
1972 : "as2js_exception: json::json_value::set_member() called with an empty string as the member name."));
1973 :
1974 : // nullptr is not valid for data
1975 : //CPPUNIT_ASSERT_THROW(value->set_member("ignore", nullptr_value), as2js::invalid_data);
1976 :
1977 : // in the new version, setting to a nullptr means remove that member
1978 : //CATCH_REQUIRE_THROWS_MATCHES(
1979 : // value->set_member("ignore", nullptr_value)
1980 : // , as2js::invalid_data
1981 : // , Catch::Matchers::ExceptionMessage(
1982 : // "set_member() called with a non-member value type."));
1983 :
1984 1 : as2js::position const& p(value->get_position());
1985 1 : CATCH_REQUIRE(p.get_filename() == pos.get_filename());
1986 1 : CATCH_REQUIRE(p.get_function() == pos.get_function());
1987 1 : CATCH_REQUIRE(p.get_line() == 109);
1988 : //std::cerr << "compare " << value->to_string() << " with " << cmp << "\n";
1989 1 : CATCH_REQUIRE(value->to_string() == "{}");
1990 : // copy operator
1991 1 : as2js::json::json_value copy(*value);
1992 1 : CATCH_REQUIRE(copy.get_type() == as2js::json::json_value::type_t::JSON_TYPE_OBJECT);
1993 :
1994 1 : CATCH_REQUIRE_THROWS_MATCHES(
1995 : copy.get_integer().get()
1996 : , as2js::internal_error
1997 : , Catch::Matchers::ExceptionMessage(
1998 : "internal_error: get_integer() called with a non-integer value type."));
1999 :
2000 1 : CATCH_REQUIRE_THROWS_MATCHES(
2001 : copy.get_floating_point().get()
2002 : , as2js::internal_error
2003 : , Catch::Matchers::ExceptionMessage(
2004 : "internal_error: get_floating_point() called with a non-floating point value type."));
2005 :
2006 1 : CATCH_REQUIRE_THROWS_MATCHES(
2007 : copy.get_string()
2008 : , as2js::internal_error
2009 : , Catch::Matchers::ExceptionMessage(
2010 : "internal_error: get_string() called with a non-string value type."));
2011 :
2012 1 : CATCH_REQUIRE_THROWS_MATCHES(
2013 : copy.get_array()
2014 : , as2js::internal_error
2015 : , Catch::Matchers::ExceptionMessage(
2016 : "internal_error: get_array() called with a non-array value type."));
2017 :
2018 3 : CATCH_REQUIRE_THROWS_MATCHES(
2019 : copy.set_item(rand(), nullptr_value)
2020 : , as2js::internal_error
2021 : , Catch::Matchers::ExceptionMessage(
2022 : "internal_error: set_item() called with a non-array value type."));
2023 :
2024 1 : as2js::json::json_value::object_t const& object_copy(copy.get_object());
2025 1 : CATCH_REQUIRE(object_copy.empty());
2026 : // name is invalid
2027 : //CPPUNIT_ASSERT_THROW(copy.set_member("", nullptr_value), as2js::invalid_index);
2028 :
2029 7 : CATCH_REQUIRE_THROWS_MATCHES(
2030 : copy.set_member("", nullptr_value)
2031 : , as2js::invalid_index
2032 : , Catch::Matchers::ExceptionMessage(
2033 : "as2js_exception: json::json_value::set_member() called with an empty string as the member name."));
2034 :
2035 : // nullptr is not valid for data
2036 : //CPPUNIT_ASSERT_THROW(copy.set_member("ignore", nullptr_value), as2js::invalid_data);
2037 :
2038 : // setting a member to nullptr is now equivalent to deleting it
2039 : //CATCH_REQUIRE_THROWS_MATCHES(
2040 : // copy.set_member("ignore", nullptr_value)
2041 : // , as2js::invalid_data
2042 : // , Catch::Matchers::ExceptionMessage(
2043 : // "as2js: wrong."));
2044 :
2045 1 : as2js::position const& q(copy.get_position());
2046 1 : CATCH_REQUIRE(q.get_filename() == pos.get_filename());
2047 1 : CATCH_REQUIRE(q.get_function() == pos.get_function());
2048 1 : CATCH_REQUIRE(q.get_line() == 109);
2049 1 : CATCH_REQUIRE(copy.to_string() == "{}");
2050 1 : }
2051 2 : CATCH_END_SECTION()
2052 :
2053 : // test with a few random objects
2054 2 : CATCH_START_SECTION("json: random objects")
2055 : {
2056 : typedef std::map<std::string, std::string> sort_t;
2057 11 : for(int idx(0); idx < 10; ++idx)
2058 : {
2059 10 : as2js::position pos;
2060 10 : pos.reset_counters(199);
2061 10 : pos.set_filename("object.json");
2062 10 : pos.set_function("save_object");
2063 10 : as2js::json::json_value::object_t initial;
2064 10 : sort_t sorted;
2065 :
2066 10 : size_t const max_items(rand() % 100 + 20);
2067 570 : for(size_t j(0); j < max_items; ++j)
2068 : {
2069 560 : std::string field_name;
2070 560 : std::string stringified_value;
2071 560 : generate_string(field_name, stringified_value);
2072 560 : stringified_value += ':';
2073 560 : as2js::json::json_value::pointer_t item;
2074 560 : int const select(rand() % 8);
2075 560 : switch(select)
2076 : {
2077 65 : case 0: // NULL
2078 65 : item.reset(new as2js::json::json_value(pos));
2079 65 : stringified_value += "null";
2080 65 : break;
2081 :
2082 57 : case 1: // INTEGER
2083 : {
2084 57 : as2js::integer::value_type int_value((rand() << 13) ^ rand());
2085 57 : as2js::integer integer(int_value);
2086 57 : item = std::make_shared<as2js::json::json_value>(pos, integer);
2087 57 : stringified_value += std::to_string(int_value);
2088 : }
2089 : break;
2090 :
2091 77 : case 2: // FLOATING_POINT
2092 : {
2093 77 : as2js::floating_point::value_type flt_value(static_cast<as2js::floating_point::value_type>((rand() << 16) | rand()) / static_cast<as2js::floating_point::value_type>((rand() << 16) | rand()));
2094 77 : as2js::floating_point flt(flt_value);
2095 77 : item.reset(new as2js::json::json_value(pos, flt));
2096 77 : stringified_value += float_to_string(flt_value);
2097 : }
2098 : break;
2099 :
2100 66 : case 3: // TRUE
2101 66 : item.reset(new as2js::json::json_value(pos, true));
2102 66 : stringified_value += "true";
2103 66 : break;
2104 :
2105 76 : case 4: // FALSE
2106 76 : item.reset(new as2js::json::json_value(pos, false));
2107 76 : stringified_value += "false";
2108 76 : break;
2109 :
2110 68 : case 5: // STRING
2111 : {
2112 68 : std::string str;
2113 68 : std::string stringified;
2114 68 : generate_string(str, stringified);
2115 68 : item.reset(new as2js::json::json_value(pos, str));
2116 68 : stringified_value += stringified;
2117 68 : }
2118 : break;
2119 :
2120 69 : case 6: // empty ARRAY
2121 : {
2122 69 : as2js::json::json_value::array_t empty_array;
2123 69 : item.reset(new as2js::json::json_value(pos, empty_array));
2124 69 : stringified_value += "[]";
2125 69 : }
2126 : break;
2127 :
2128 82 : case 7: // empty OBJECT
2129 : {
2130 82 : as2js::json::json_value::object_t empty_object;
2131 82 : item.reset(new as2js::json::json_value(pos, empty_object));
2132 82 : stringified_value += "{}";
2133 82 : }
2134 : break;
2135 :
2136 : // more?
2137 0 : default:
2138 0 : throw std::logic_error("test generated an invalid # to generate an object item");
2139 :
2140 : }
2141 560 : initial[field_name] = item;
2142 560 : sorted[field_name] = stringified_value;
2143 560 : }
2144 30 : std::string result("{");
2145 10 : bool first(true);
2146 570 : for(auto it : sorted)
2147 : {
2148 560 : if(!first)
2149 : {
2150 550 : result += ',';
2151 : }
2152 : else
2153 : {
2154 10 : first = false;
2155 : }
2156 560 : result += it.second;
2157 560 : }
2158 10 : result += "}";
2159 :
2160 20 : as2js::json::json_value::pointer_t value(new as2js::json::json_value(pos, initial));
2161 10 : CATCH_REQUIRE(value->get_type() == as2js::json::json_value::type_t::JSON_TYPE_OBJECT);
2162 :
2163 10 : CATCH_REQUIRE_THROWS_MATCHES(
2164 : value->get_integer().get()
2165 : , as2js::internal_error
2166 : , Catch::Matchers::ExceptionMessage(
2167 : "internal_error: get_integer() called with a non-integer value type."));
2168 :
2169 10 : CATCH_REQUIRE_THROWS_MATCHES(
2170 : value->get_floating_point().get()
2171 : , as2js::internal_error
2172 : , Catch::Matchers::ExceptionMessage(
2173 : "internal_error: get_floating_point() called with a non-floating point value type."));
2174 :
2175 10 : CATCH_REQUIRE_THROWS_MATCHES(
2176 : value->get_string()
2177 : , as2js::internal_error
2178 : , Catch::Matchers::ExceptionMessage(
2179 : "internal_error: get_string() called with a non-string value type."));
2180 :
2181 10 : CATCH_REQUIRE_THROWS_MATCHES(
2182 : value->get_array()
2183 : , as2js::internal_error
2184 : , Catch::Matchers::ExceptionMessage(
2185 : "internal_error: get_array() called with a non-array value type."));
2186 :
2187 30 : CATCH_REQUIRE_THROWS_MATCHES(
2188 : value->set_item(rand(), nullptr_value)
2189 : , as2js::internal_error
2190 : , Catch::Matchers::ExceptionMessage(
2191 : "internal_error: set_item() called with a non-array value type."));
2192 :
2193 10 : as2js::json::json_value::object_t const& object(value->get_object());
2194 10 : CATCH_REQUIRE(object.size() == max_items);
2195 : //for(int idx(-10); idx <= 10; ++idx)
2196 : //{
2197 : // if(idx == 0)
2198 : // {
2199 : // // nullptr is not valid for data
2200 : // CPPUNIT_ASSERT_THROW(value->set_item(idx, nullptr_value), as2js::invalid_data);
2201 : // }
2202 : // else
2203 : // {
2204 : // // index is invalid
2205 : // CPPUNIT_ASSERT_THROW(value->set_item(idx, nullptr_value), as2js::out_of_range);
2206 : // }
2207 : //}
2208 10 : as2js::position const& p(value->get_position());
2209 10 : CATCH_REQUIRE(p.get_filename() == pos.get_filename());
2210 10 : CATCH_REQUIRE(p.get_function() == pos.get_function());
2211 10 : CATCH_REQUIRE(p.get_line() == 199);
2212 : //std::string r(value->to_string());
2213 : //std::cerr << std::hex << " lengths " << r.length() << " / " << result.length() << "\n";
2214 : //size_t max_chrs(std::min(r.length(), result.length()));
2215 : //for(size_t g(0); g < max_chrs; ++g)
2216 : //{
2217 : // if(static_cast<int>(r[g]) != static_cast<int>(result[g]))
2218 : // {
2219 : // std::cerr << " --- " << static_cast<int>(r[g]) << " / " << static_cast<int>(result[g]) << "\n";
2220 : // }
2221 : // else
2222 : // {
2223 : // std::cerr << " " << static_cast<int>(r[g]) << " / " << static_cast<int>(result[g]) << "\n";
2224 : // }
2225 : //}
2226 : //if(r.length() > result.length())
2227 : //{
2228 : // for(size_t g(result.length()); g < r.length(); ++g)
2229 : // {
2230 : // std::cerr << " *** " << static_cast<int>(r[g]) << "\n";
2231 : // }
2232 : //}
2233 : //else
2234 : //{
2235 : // for(size_t g(r.length()); g < result.length(); ++g)
2236 : // {
2237 : // std::cerr << " +++ " << static_cast<int>(result[g]) << "\n";
2238 : // }
2239 : //}
2240 : //std::cerr << std::dec;
2241 10 : CATCH_REQUIRE(value->to_string() == result);
2242 : // copy operator
2243 20 : as2js::json::json_value copy(*value);
2244 10 : CATCH_REQUIRE(copy.get_type() == as2js::json::json_value::type_t::JSON_TYPE_OBJECT);
2245 :
2246 10 : CATCH_REQUIRE_THROWS_MATCHES(
2247 : copy.get_integer().get()
2248 : , as2js::internal_error
2249 : , Catch::Matchers::ExceptionMessage(
2250 : "internal_error: get_integer() called with a non-integer value type."));
2251 :
2252 10 : CATCH_REQUIRE_THROWS_MATCHES(
2253 : copy.get_floating_point().get()
2254 : , as2js::internal_error
2255 : , Catch::Matchers::ExceptionMessage(
2256 : "internal_error: get_floating_point() called with a non-floating point value type."));
2257 :
2258 10 : CATCH_REQUIRE_THROWS_MATCHES(
2259 : copy.get_string()
2260 : , as2js::internal_error
2261 : , Catch::Matchers::ExceptionMessage(
2262 : "internal_error: get_string() called with a non-string value type."));
2263 :
2264 10 : CATCH_REQUIRE_THROWS_MATCHES(
2265 : copy.get_array()
2266 : , as2js::internal_error
2267 : , Catch::Matchers::ExceptionMessage(
2268 : "internal_error: get_array() called with a non-array value type."));
2269 :
2270 30 : CATCH_REQUIRE_THROWS_MATCHES(
2271 : copy.set_item(rand(), nullptr_value)
2272 : , as2js::internal_error
2273 : , Catch::Matchers::ExceptionMessage(
2274 : "internal_error: set_item() called with a non-array value type."));
2275 :
2276 10 : as2js::json::json_value::object_t const& object_copy(copy.get_object());
2277 10 : CATCH_REQUIRE(object_copy.size() == max_items);
2278 : //for(int idx(-10); idx <= 10; ++idx)
2279 : //{
2280 : // if(idx == 0)
2281 : // {
2282 : // // nullptr is not valid for data
2283 : // CPPUNIT_ASSERT_THROW(copy.set_member("", nullptr_value), as2js::invalid_data);
2284 : // }
2285 : // else
2286 : // {
2287 : // // index is invalid
2288 : // CPPUNIT_ASSERT_THROW(copy.set_member("ingore", nullptr_value), as2js::out_of_range);
2289 : // }
2290 : //}
2291 10 : as2js::position const & q(copy.get_position());
2292 10 : CATCH_REQUIRE(q.get_filename() == pos.get_filename());
2293 10 : CATCH_REQUIRE(q.get_function() == pos.get_function());
2294 10 : CATCH_REQUIRE(q.get_line() == 199);
2295 10 : CATCH_REQUIRE(copy.to_string() == result);
2296 : // the cyclic flag should have been reset, make sure of that:
2297 10 : CATCH_REQUIRE(copy.to_string() == result);
2298 :
2299 : // test that we catch a direct 'object[x] = object;'
2300 10 : value->set_member("random", value);
2301 : // copy is not affected...
2302 10 : CATCH_REQUIRE(copy.to_string() == result);
2303 : // value to string fails because it is cyclic
2304 : //CPPUNIT_ASSERT_THROW(value->to_string() == result, as2js::cyclical_structure);
2305 :
2306 10 : CATCH_REQUIRE_THROWS_MATCHES(
2307 : value->to_string()
2308 : , as2js::cyclical_structure
2309 : , Catch::Matchers::ExceptionMessage(
2310 : "as2js_exception: JSON cannot stringify a set of objects and arrays which are cyclical."));
2311 :
2312 10 : as2js::json::json_value::object_t const& cyclic_object(value->get_object());
2313 10 : CATCH_REQUIRE(cyclic_object.size() == max_items + 1);
2314 :
2315 : {
2316 10 : std::string str;
2317 20 : std::string stringified("\"random\":");
2318 10 : generate_string(str, stringified);
2319 10 : as2js::json::json_value::pointer_t item;
2320 10 : item = std::make_shared<as2js::json::json_value>(pos, str);
2321 10 : sorted["random"] = stringified;
2322 : // with objects the entire result needs to be rebuilt
2323 10 : result = "{";
2324 10 : first = true;
2325 580 : for(auto it : sorted)
2326 : {
2327 570 : if(!first)
2328 : {
2329 560 : result += ',';
2330 : }
2331 : else
2332 : {
2333 10 : first = false;
2334 : }
2335 570 : result += it.second;
2336 570 : }
2337 10 : result += "}";
2338 10 : value->set_member("random", item);
2339 : //std::string r(value->to_string());
2340 : //std::cerr << std::hex << " lengths " << r.length() << " / " << result.length() << "\n";
2341 : //size_t max_chrs(std::min(r.length(), result.length()));
2342 : //for(size_t g(0); g < max_chrs; ++g)
2343 : //{
2344 : // if(static_cast<int>(r[g]) != static_cast<int>(result[g]))
2345 : // {
2346 : // std::cerr << " --- " << static_cast<int>(r[g]) << " / " << static_cast<int>(result[g]) << "\n";
2347 : // }
2348 : // else
2349 : // {
2350 : // std::cerr << " " << static_cast<int>(r[g]) << " / " << static_cast<int>(result[g]) << "\n";
2351 : // }
2352 : //}
2353 : //if(r.length() > result.length())
2354 : //{
2355 : // for(size_t g(result.length()); g < r.length(); ++g)
2356 : // {
2357 : // std::cerr << " *** " << static_cast<int>(r[g]) << "\n";
2358 : // }
2359 : //}
2360 : //else
2361 : //{
2362 : // for(size_t g(r.length()); g < result.length(); ++g)
2363 : // {
2364 : // std::cerr << " +++ " << static_cast<int>(result[g]) << "\n";
2365 : // }
2366 : //}
2367 : //std::cerr << std::dec;
2368 10 : CATCH_REQUIRE(value->to_string() == result);
2369 10 : }
2370 10 : }
2371 : }
2372 2 : CATCH_END_SECTION()
2373 4 : }
2374 :
2375 :
2376 1 : CATCH_TEST_CASE("json_random_object", "[json][object]")
2377 : {
2378 1 : CATCH_START_SECTION("json: random objects and arrays")
2379 : {
2380 : // test with a few random objects
2381 1 : g_type_used = 0;
2382 : typedef std::map<std::string, std::string> sort_t;
2383 11 : for(int idx(0); idx < 10 || g_type_used != TYPE_ALL; ++idx)
2384 : {
2385 20 : std::string const header(rand() & 1 ? "// we can have a C++ comment\n/* or even a C like comment in the header\n(not the rest because we do not have access...) */\n" : "");
2386 :
2387 10 : test_data_t data;
2388 10 : data.f_pos.reset_counters(199);
2389 10 : data.f_pos.set_filename("full.json");
2390 10 : data.f_pos.set_function("save_full");
2391 :
2392 10 : if(rand() & 1)
2393 : {
2394 7 : create_object(data);
2395 : }
2396 : else
2397 : {
2398 3 : create_array(data);
2399 : }
2400 10 : std::string expected;
2401 : //expected += 0xFEFF; // BOM
2402 10 : expected += header;
2403 10 : if(!header.empty())
2404 : {
2405 8 : expected += '\n';
2406 : }
2407 10 : data_to_string(data.f_value, expected);
2408 : //std::cerr << "created " << data.f_count << " items.\n";
2409 :
2410 10 : as2js::json::pointer_t json(std::make_shared<as2js::json>());
2411 10 : json->set_value(data.f_value);
2412 :
2413 10 : as2js::output_stream<std::stringstream>::pointer_t out(std::make_shared<as2js::output_stream<std::stringstream>>());
2414 10 : json->output(out, header);
2415 10 : std::string const& result(out->str());
2416 : #if 0
2417 : {
2418 : std::cerr << std::hex << " lengths " << expected.length() << " / " << result.length() << "\n";
2419 : size_t max_chrs(std::min(expected.length(), result.length()));
2420 : for(size_t g(0); g < max_chrs; ++g)
2421 : {
2422 : if(static_cast<int>(expected[g]) != static_cast<int>(result[g]))
2423 : {
2424 : std::cerr << " --- " << static_cast<int>(expected[g]) << " / " << static_cast<int>(result[g]) << "\n";
2425 : }
2426 : else
2427 : {
2428 : std::cerr << " " << static_cast<int>(expected[g]) << " / " << static_cast<int>(result[g]) << "\n";
2429 : }
2430 : }
2431 : if(expected.length() > result.length())
2432 : {
2433 : for(size_t g(result.length()); g < expected.length(); ++g)
2434 : {
2435 : std::cerr << " *** " << static_cast<int>(expected[g]) << "\n";
2436 : }
2437 : }
2438 : else
2439 : {
2440 : for(size_t g(expected.length()); g < result.length(); ++g)
2441 : {
2442 : std::cerr << " +++ " << static_cast<int>(result[g]) << "\n";
2443 : }
2444 : }
2445 : std::cerr << std::dec;
2446 : }
2447 : #endif
2448 10 : CATCH_REQUIRE(result == expected);
2449 :
2450 10 : CATCH_REQUIRE(json->get_value() == data.f_value);
2451 : // make sure the tree is also correct:
2452 10 : std::string expected_tree;
2453 : //expected_tree += 0xFEFF; // BOM
2454 10 : expected_tree += header;
2455 10 : if(!header.empty())
2456 : {
2457 8 : expected_tree += '\n';
2458 : }
2459 10 : data_to_string(json->get_value(), expected_tree);
2460 10 : CATCH_REQUIRE(expected_tree == expected);
2461 :
2462 : // copy operator
2463 10 : as2js::json copy(*json);
2464 :
2465 : // the copy gets the exact same value pointer...
2466 10 : CATCH_REQUIRE(copy.get_value() == data.f_value);
2467 : // make sure the tree is also correct:
2468 10 : std::string expected_copy;
2469 : //expected_copy += 0xFEFF; // BOM
2470 10 : expected_copy += header;
2471 10 : if(!header.empty())
2472 : {
2473 8 : expected_copy += '\n';
2474 : }
2475 10 : data_to_string(copy.get_value(), expected_copy);
2476 10 : CATCH_REQUIRE(expected_copy == expected);
2477 :
2478 : // create an unsafe temporary file and save that JSON in there...
2479 10 : int number(rand() % 1000000);
2480 10 : std::stringstream ss;
2481 10 : ss << SNAP_CATCH2_NAMESPACE::g_tmp_dir() << "/json_test" << std::setfill('0') << std::setw(6) << number << ".js";
2482 : //std::cerr << "filename [" << ss.str() << "]\n";
2483 10 : std::string const filename(ss.str());
2484 10 : json->save(filename, header);
2485 :
2486 10 : as2js::json::pointer_t load_json(std::make_shared<as2js::json>());
2487 10 : as2js::json::json_value::pointer_t loaded_value(load_json->load(filename));
2488 10 : CATCH_REQUIRE(loaded_value == load_json->get_value());
2489 :
2490 10 : as2js::output_stream<std::stringstream>::pointer_t lout(new as2js::output_stream<std::stringstream>());
2491 10 : load_json->output(lout, header);
2492 10 : std::string const & lresult(lout->str());
2493 : {
2494 10 : std::ofstream co;
2495 10 : co.open(filename + "2");
2496 10 : CATCH_REQUIRE(co.is_open());
2497 10 : co << lresult;
2498 10 : }
2499 :
2500 10 : CATCH_REQUIRE(lresult == expected);
2501 :
2502 10 : unlink(filename.c_str());
2503 10 : }
2504 : }
2505 1 : CATCH_END_SECTION()
2506 1 : }
2507 :
2508 :
2509 1 : CATCH_TEST_CASE("json_positive_numbers", "[json][number]")
2510 : {
2511 1 : CATCH_START_SECTION("json: positive numbers")
2512 : {
2513 1 : std::string const content(
2514 : "// we can have a C++ comment\n"
2515 : "/* or even a C like comment in the header\n"
2516 : "(not the rest because we do not have access...) */\n"
2517 : "[\n"
2518 : "\t+111,\n"
2519 : "\t+1.113,\n"
2520 : "\t+Infinity,\n"
2521 : "\t+NaN\n"
2522 : "]\n"
2523 2 : );
2524 :
2525 1 : test_data_t data;
2526 1 : data.f_pos.reset_counters(201);
2527 1 : data.f_pos.set_filename("full.json");
2528 1 : data.f_pos.set_function("save_full");
2529 :
2530 1 : as2js::input_stream<std::stringstream>::pointer_t in(std::make_shared<as2js::input_stream<std::stringstream>>());
2531 1 : *in << content;
2532 :
2533 1 : as2js::json::pointer_t load_json(std::make_shared<as2js::json>());
2534 2 : as2js::json::json_value::pointer_t loaded_value(load_json->parse(in));
2535 1 : CATCH_REQUIRE(loaded_value == load_json->get_value());
2536 :
2537 1 : as2js::json::json_value::pointer_t value(load_json->get_value());
2538 1 : CATCH_REQUIRE(value->get_type() == as2js::json::json_value::type_t::JSON_TYPE_ARRAY);
2539 1 : as2js::json::json_value::array_t array(value->get_array());
2540 1 : CATCH_REQUIRE(array.size() == 4);
2541 :
2542 1 : CATCH_REQUIRE(array[0]->get_type() == as2js::json::json_value::type_t::JSON_TYPE_INTEGER);
2543 1 : as2js::integer integer(array[0]->get_integer());
2544 1 : CATCH_REQUIRE(integer.get() == 111);
2545 :
2546 1 : CATCH_REQUIRE(array[1]->get_type() == as2js::json::json_value::type_t::JSON_TYPE_FLOATING_POINT);
2547 1 : as2js::floating_point floating_point(array[1]->get_floating_point());
2548 1 : CATCH_REQUIRE_FLOATING_POINT(floating_point.get(), 1.113);
2549 :
2550 1 : CATCH_REQUIRE(array[2]->get_type() == as2js::json::json_value::type_t::JSON_TYPE_FLOATING_POINT);
2551 1 : floating_point = array[2]->get_floating_point();
2552 1 : CATCH_REQUIRE(floating_point.is_positive_infinity());
2553 :
2554 1 : CATCH_REQUIRE(array[3]->get_type() == as2js::json::json_value::type_t::JSON_TYPE_FLOATING_POINT);
2555 1 : floating_point = array[3]->get_floating_point();
2556 1 : CATCH_REQUIRE(floating_point.is_nan());
2557 1 : }
2558 1 : CATCH_END_SECTION()
2559 1 : }
2560 :
2561 :
2562 19 : CATCH_TEST_CASE("json_errors", "[json][errors]")
2563 : {
2564 19 : CATCH_START_SECTION("json: cannot open input")
2565 : {
2566 1 : test_callback::expected_t expected;
2567 1 : expected.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_ERROR;
2568 1 : expected.f_error_code = as2js::err_code_t::AS_ERR_NOT_FOUND;
2569 1 : expected.f_pos.set_filename("/this/file/definitively/does/not/exist/so/we'll/get/an/error/immediately");
2570 1 : expected.f_pos.set_function("unknown-func");
2571 1 : expected.f_message = "cannot open JSON file \"/this/file/definitively/does/not/exist/so/we'll/get/an/error/immediately\".";
2572 :
2573 1 : test_callback tc;
2574 1 : tc.f_expected.push_back(expected);
2575 :
2576 1 : as2js::json::pointer_t load_json(new as2js::json);
2577 1 : CATCH_REQUIRE(load_json->load("/this/file/definitively/does/not/exist/so/we'll/get/an/error/immediately") == as2js::json::json_value::pointer_t());
2578 1 : tc.got_called();
2579 1 : }
2580 19 : CATCH_END_SECTION()
2581 :
2582 19 : CATCH_START_SECTION("json: cannot open output")
2583 : {
2584 1 : test_callback::expected_t expected;
2585 1 : expected.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_FATAL;
2586 1 : expected.f_error_code = as2js::err_code_t::AS_ERR_CANNOT_COMPILE;
2587 1 : expected.f_pos.set_filename("unknown-file");
2588 1 : expected.f_pos.set_function("unknown-func");
2589 1 : expected.f_message = "could not open output file \"/this/file/definitively/does/not/exist/so/we'll/get/an/error/immediately\".";
2590 :
2591 1 : test_callback tc;
2592 1 : tc.f_expected.push_back(expected);
2593 :
2594 1 : as2js::json::pointer_t save_json(new as2js::json);
2595 1 : CATCH_REQUIRE(save_json->save("/this/file/definitively/does/not/exist/so/we'll/get/an/error/immediately", "// unused\n") == false);
2596 1 : tc.got_called();
2597 1 : }
2598 19 : CATCH_END_SECTION()
2599 :
2600 19 : CATCH_START_SECTION("json: invalid data")
2601 : {
2602 1 : as2js::json::pointer_t json(new as2js::json);
2603 1 : as2js::output_stream<std::stringstream>::pointer_t lout(new as2js::output_stream<std::stringstream>);
2604 2 : std::string const header("// unused\n");
2605 : //CPPUNIT_ASSERT_THROW(json->output(lout, header), as2js::invalid_data);
2606 :
2607 3 : CATCH_REQUIRE_THROWS_MATCHES(
2608 : json->output(lout, header)
2609 : , as2js::invalid_data
2610 : , Catch::Matchers::ExceptionMessage(
2611 : "as2js_exception: this JSON has no value to output."));
2612 1 : }
2613 19 : CATCH_END_SECTION()
2614 :
2615 19 : CATCH_START_SECTION("json: EOF error")
2616 : {
2617 : // use an unsafe temporary file...
2618 1 : int number(rand() % 1000000);
2619 1 : std::stringstream ss;
2620 1 : ss << SNAP_CATCH2_NAMESPACE::g_tmp_dir() << "/json_test" << std::setfill('0') << std::setw(6) << number << ".js";
2621 1 : std::string filename(ss.str());
2622 : // create an empty file
2623 1 : FILE *f(fopen(filename.c_str(), "w"));
2624 : //std::cerr << "--- opened [" << filename << "] result: " << reinterpret_cast<void*>(f) << "\n";
2625 1 : CATCH_REQUIRE(f != nullptr);
2626 1 : fclose(f);
2627 :
2628 1 : test_callback tc;
2629 :
2630 1 : test_callback::expected_t expected1;
2631 1 : expected1.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_ERROR;
2632 1 : expected1.f_error_code = as2js::err_code_t::AS_ERR_UNEXPECTED_EOF;
2633 1 : expected1.f_pos.set_filename(filename);
2634 1 : expected1.f_pos.set_function("unknown-func");
2635 1 : expected1.f_message = "the end of the file was reached while reading JSON data.";
2636 1 : tc.f_expected.push_back(expected1);
2637 :
2638 1 : test_callback::expected_t expected2;
2639 1 : expected2.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_FATAL;
2640 1 : expected2.f_error_code = as2js::err_code_t::AS_ERR_CANNOT_COMPILE;
2641 1 : expected2.f_pos.set_filename(filename);
2642 1 : expected2.f_pos.set_function("unknown-func");
2643 1 : expected2.f_message = "could not interpret this JSON input \"" + filename + "\".";
2644 1 : tc.f_expected.push_back(expected2);
2645 :
2646 : //std::cerr << "filename [" << ss.str() << "]\n";
2647 1 : as2js::json::pointer_t json(std::make_shared<as2js::json>());
2648 1 : CATCH_REQUIRE(json->load(filename) == as2js::json::json_value::pointer_t());
2649 1 : tc.got_called();
2650 1 : }
2651 19 : CATCH_END_SECTION()
2652 :
2653 19 : CATCH_START_SECTION("json: string name missing")
2654 : {
2655 1 : std::string str(
2656 : "{'valid':123,,'valid too':123}"
2657 2 : );
2658 1 : as2js::input_stream<std::stringstream>::pointer_t in(new as2js::input_stream<std::stringstream>());
2659 1 : *in << str;
2660 :
2661 1 : test_callback tc;
2662 :
2663 1 : test_callback::expected_t expected1;
2664 1 : expected1.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_ERROR;
2665 1 : expected1.f_error_code = as2js::err_code_t::AS_ERR_STRING_EXPECTED;
2666 1 : expected1.f_pos.set_filename("unknown-file");
2667 1 : expected1.f_pos.set_function("unknown-func");
2668 1 : expected1.f_message = "expected a string as the JSON object member name.";
2669 1 : tc.f_expected.push_back(expected1);
2670 :
2671 1 : test_callback::expected_t expected2;
2672 1 : expected2.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_FATAL;
2673 1 : expected2.f_error_code = as2js::err_code_t::AS_ERR_CANNOT_COMPILE;
2674 1 : expected2.f_pos.set_filename("unknown-file");
2675 1 : expected2.f_pos.set_function("unknown-func");
2676 1 : expected2.f_message = "could not interpret this JSON input \"\".";
2677 1 : tc.f_expected.push_back(expected2);
2678 :
2679 1 : as2js::json::pointer_t json(new as2js::json);
2680 1 : CATCH_REQUIRE(json->parse(in) == as2js::json::json_value::pointer_t());
2681 1 : tc.got_called();
2682 1 : }
2683 19 : CATCH_END_SECTION()
2684 :
2685 19 : CATCH_START_SECTION("json: unquoted string")
2686 : {
2687 1 : std::string str(
2688 : "{'valid':123,invalid:123}"
2689 2 : );
2690 1 : as2js::input_stream<std::stringstream>::pointer_t in(new as2js::input_stream<std::stringstream>());
2691 1 : *in << str;
2692 :
2693 1 : test_callback tc;
2694 :
2695 1 : test_callback::expected_t expected1;
2696 1 : expected1.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_ERROR;
2697 1 : expected1.f_error_code = as2js::err_code_t::AS_ERR_STRING_EXPECTED;
2698 1 : expected1.f_pos.set_filename("unknown-file");
2699 1 : expected1.f_pos.set_function("unknown-func");
2700 1 : expected1.f_message = "expected a string as the JSON object member name.";
2701 1 : tc.f_expected.push_back(expected1);
2702 :
2703 1 : test_callback::expected_t expected2;
2704 1 : expected2.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_FATAL;
2705 1 : expected2.f_error_code = as2js::err_code_t::AS_ERR_CANNOT_COMPILE;
2706 1 : expected2.f_pos.set_filename("unknown-file");
2707 1 : expected2.f_pos.set_function("unknown-func");
2708 1 : expected2.f_message = "could not interpret this JSON input \"\".";
2709 1 : tc.f_expected.push_back(expected2);
2710 :
2711 1 : as2js::json::pointer_t json(new as2js::json);
2712 1 : CATCH_REQUIRE(json->parse(in) == as2js::json::json_value::pointer_t());
2713 1 : tc.got_called();
2714 1 : }
2715 19 : CATCH_END_SECTION()
2716 :
2717 19 : CATCH_START_SECTION("json: number instead of string for name")
2718 : {
2719 1 : std::string str(
2720 : "{'valid':123,123:'invalid'}"
2721 2 : );
2722 1 : as2js::input_stream<std::stringstream>::pointer_t in(new as2js::input_stream<std::stringstream>());
2723 1 : *in << str;
2724 :
2725 1 : test_callback tc;
2726 :
2727 1 : test_callback::expected_t expected1;
2728 1 : expected1.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_ERROR;
2729 1 : expected1.f_error_code = as2js::err_code_t::AS_ERR_STRING_EXPECTED;
2730 1 : expected1.f_pos.set_filename("unknown-file");
2731 1 : expected1.f_pos.set_function("unknown-func");
2732 1 : expected1.f_message = "expected a string as the JSON object member name.";
2733 1 : tc.f_expected.push_back(expected1);
2734 :
2735 1 : test_callback::expected_t expected2;
2736 1 : expected2.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_FATAL;
2737 1 : expected2.f_error_code = as2js::err_code_t::AS_ERR_CANNOT_COMPILE;
2738 1 : expected2.f_pos.set_filename("unknown-file");
2739 1 : expected2.f_pos.set_function("unknown-func");
2740 1 : expected2.f_message = "could not interpret this JSON input \"\".";
2741 1 : tc.f_expected.push_back(expected2);
2742 :
2743 1 : as2js::json::pointer_t json(new as2js::json);
2744 1 : CATCH_REQUIRE(json->parse(in) == as2js::json::json_value::pointer_t());
2745 1 : tc.got_called();
2746 1 : }
2747 19 : CATCH_END_SECTION()
2748 :
2749 19 : CATCH_START_SECTION("json: array instead of name")
2750 : {
2751 1 : std::string str(
2752 : "{'valid':123,['invalid']}"
2753 2 : );
2754 1 : as2js::input_stream<std::stringstream>::pointer_t in(new as2js::input_stream<std::stringstream>());
2755 1 : *in << str;
2756 :
2757 1 : test_callback tc;
2758 :
2759 1 : test_callback::expected_t expected1;
2760 1 : expected1.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_ERROR;
2761 1 : expected1.f_error_code = as2js::err_code_t::AS_ERR_STRING_EXPECTED;
2762 1 : expected1.f_pos.set_filename("unknown-file");
2763 1 : expected1.f_pos.set_function("unknown-func");
2764 1 : expected1.f_message = "expected a string as the JSON object member name.";
2765 1 : tc.f_expected.push_back(expected1);
2766 :
2767 1 : test_callback::expected_t expected2;
2768 1 : expected2.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_FATAL;
2769 1 : expected2.f_error_code = as2js::err_code_t::AS_ERR_CANNOT_COMPILE;
2770 1 : expected2.f_pos.set_filename("unknown-file");
2771 1 : expected2.f_pos.set_function("unknown-func");
2772 1 : expected2.f_message = "could not interpret this JSON input \"\".";
2773 1 : tc.f_expected.push_back(expected2);
2774 :
2775 1 : as2js::json::pointer_t json(new as2js::json);
2776 1 : CATCH_REQUIRE(json->parse(in) == as2js::json::json_value::pointer_t());
2777 1 : tc.got_called();
2778 1 : }
2779 19 : CATCH_END_SECTION()
2780 :
2781 19 : CATCH_START_SECTION("json: object instead of name")
2782 : {
2783 1 : std::string str(
2784 : "{'valid':123,{'invalid':123}}"
2785 2 : );
2786 1 : as2js::input_stream<std::stringstream>::pointer_t in(new as2js::input_stream<std::stringstream>());
2787 1 : *in << str;
2788 :
2789 1 : test_callback tc;
2790 :
2791 1 : test_callback::expected_t expected1;
2792 1 : expected1.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_ERROR;
2793 1 : expected1.f_error_code = as2js::err_code_t::AS_ERR_STRING_EXPECTED;
2794 1 : expected1.f_pos.set_filename("unknown-file");
2795 1 : expected1.f_pos.set_function("unknown-func");
2796 1 : expected1.f_message = "expected a string as the JSON object member name.";
2797 1 : tc.f_expected.push_back(expected1);
2798 :
2799 1 : test_callback::expected_t expected2;
2800 1 : expected2.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_FATAL;
2801 1 : expected2.f_error_code = as2js::err_code_t::AS_ERR_CANNOT_COMPILE;
2802 1 : expected2.f_pos.set_filename("unknown-file");
2803 1 : expected2.f_pos.set_function("unknown-func");
2804 1 : expected2.f_message = "could not interpret this JSON input \"\".";
2805 1 : tc.f_expected.push_back(expected2);
2806 :
2807 1 : as2js::json::pointer_t json(new as2js::json);
2808 1 : CATCH_REQUIRE(json->parse(in) == as2js::json::json_value::pointer_t());
2809 1 : tc.got_called();
2810 1 : }
2811 19 : CATCH_END_SECTION()
2812 :
2813 19 : CATCH_START_SECTION("json: colon missing")
2814 : {
2815 1 : std::string str(
2816 : "{'valid':123,'colon missing'123}"
2817 2 : );
2818 1 : as2js::input_stream<std::stringstream>::pointer_t in(new as2js::input_stream<std::stringstream>());
2819 1 : *in << str;
2820 :
2821 1 : test_callback tc;
2822 :
2823 1 : test_callback::expected_t expected1;
2824 1 : expected1.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_ERROR;
2825 1 : expected1.f_error_code = as2js::err_code_t::AS_ERR_COLON_EXPECTED;
2826 1 : expected1.f_pos.set_filename("unknown-file");
2827 1 : expected1.f_pos.set_function("unknown-func");
2828 1 : expected1.f_message = "expected a colon (:) as the JSON object member name (colon missing) and member value separator (invalid type is INTEGER)";
2829 1 : tc.f_expected.push_back(expected1);
2830 :
2831 1 : test_callback::expected_t expected2;
2832 1 : expected2.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_FATAL;
2833 1 : expected2.f_error_code = as2js::err_code_t::AS_ERR_CANNOT_COMPILE;
2834 1 : expected2.f_pos.set_filename("unknown-file");
2835 1 : expected2.f_pos.set_function("unknown-func");
2836 1 : expected2.f_message = "could not interpret this JSON input \"\".";
2837 1 : tc.f_expected.push_back(expected2);
2838 :
2839 1 : as2js::json::pointer_t json(new as2js::json);
2840 1 : CATCH_REQUIRE(json->parse(in) == as2js::json::json_value::pointer_t());
2841 1 : tc.got_called();
2842 1 : }
2843 19 : CATCH_END_SECTION()
2844 :
2845 19 : CATCH_START_SECTION("json: sub-list missing colon")
2846 : {
2847 1 : std::string str(
2848 : // we use 'valid' twice but one is in a sub-object to test
2849 : // that does not generate a problem
2850 : "{'valid':123,'sub-member':{'valid':123,'sub-sub-member':{'sub-sub-invalid'123},'ignore':'this'}}"
2851 2 : );
2852 1 : as2js::input_stream<std::stringstream>::pointer_t in(new as2js::input_stream<std::stringstream>());
2853 1 : *in << str;
2854 :
2855 1 : test_callback tc;
2856 :
2857 1 : test_callback::expected_t expected1;
2858 1 : expected1.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_ERROR;
2859 1 : expected1.f_error_code = as2js::err_code_t::AS_ERR_COLON_EXPECTED;
2860 1 : expected1.f_pos.set_filename("unknown-file");
2861 1 : expected1.f_pos.set_function("unknown-func");
2862 1 : expected1.f_message = "expected a colon (:) as the JSON object member name (sub-sub-invalid) and member value separator (invalid type is INTEGER)";
2863 1 : tc.f_expected.push_back(expected1);
2864 :
2865 1 : test_callback::expected_t expected2;
2866 1 : expected2.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_FATAL;
2867 1 : expected2.f_error_code = as2js::err_code_t::AS_ERR_CANNOT_COMPILE;
2868 1 : expected2.f_pos.set_filename("unknown-file");
2869 1 : expected2.f_pos.set_function("unknown-func");
2870 1 : expected2.f_message = "could not interpret this JSON input \"\".";
2871 1 : tc.f_expected.push_back(expected2);
2872 :
2873 1 : as2js::json::pointer_t json(new as2js::json);
2874 1 : CATCH_REQUIRE(json->parse(in) == as2js::json::json_value::pointer_t());
2875 1 : tc.got_called();
2876 1 : }
2877 19 : CATCH_END_SECTION()
2878 :
2879 19 : CATCH_START_SECTION("json: field repeated")
2880 : {
2881 1 : std::string str(
2882 : "{'valid':123,'re-valid':{'sub-valid':123,'sub-sub-member':{'sub-sub-valid':123},'more-valid':'this'},'valid':'again'}"
2883 2 : );
2884 1 : as2js::input_stream<std::stringstream>::pointer_t in(new as2js::input_stream<std::stringstream>());
2885 1 : *in << str;
2886 :
2887 1 : test_callback tc;
2888 :
2889 1 : test_callback::expected_t expected1;
2890 1 : expected1.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_ERROR;
2891 1 : expected1.f_error_code = as2js::err_code_t::AS_ERR_OBJECT_MEMBER_DEFINED_TWICE;
2892 1 : expected1.f_pos.set_filename("unknown-file");
2893 1 : expected1.f_pos.set_function("unknown-func");
2894 1 : expected1.f_message = "the same object member \"valid\" was defined twice, which is not allowed in JSON.";
2895 1 : tc.f_expected.push_back(expected1);
2896 :
2897 1 : as2js::json::pointer_t json(new as2js::json);
2898 : // defined twice does not mean we get a null pointer...
2899 : // (we should enhance this test to verify the result which is
2900 : // that we keep the first entry with a given name.)
2901 1 : CATCH_REQUIRE(json->parse(in) != as2js::json::json_value::pointer_t());
2902 1 : tc.got_called();
2903 1 : }
2904 19 : CATCH_END_SECTION()
2905 :
2906 19 : CATCH_START_SECTION("json: comma missing")
2907 : {
2908 1 : std::string str(
2909 : "{'valid':123 'next-member':456}"
2910 2 : );
2911 1 : as2js::input_stream<std::stringstream>::pointer_t in(new as2js::input_stream<std::stringstream>());
2912 1 : *in << str;
2913 :
2914 1 : test_callback tc;
2915 :
2916 1 : test_callback::expected_t expected1;
2917 1 : expected1.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_ERROR;
2918 1 : expected1.f_error_code = as2js::err_code_t::AS_ERR_COMMA_EXPECTED;
2919 1 : expected1.f_pos.set_filename("unknown-file");
2920 1 : expected1.f_pos.set_function("unknown-func");
2921 1 : expected1.f_message = "expected a comma (,) to separate two JSON object members.";
2922 1 : tc.f_expected.push_back(expected1);
2923 :
2924 1 : test_callback::expected_t expected2;
2925 1 : expected2.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_FATAL;
2926 1 : expected2.f_error_code = as2js::err_code_t::AS_ERR_CANNOT_COMPILE;
2927 1 : expected2.f_pos.set_filename("unknown-file");
2928 1 : expected2.f_pos.set_function("unknown-func");
2929 1 : expected2.f_message = "could not interpret this JSON input \"\".";
2930 1 : tc.f_expected.push_back(expected2);
2931 :
2932 1 : as2js::json::pointer_t json(new as2js::json);
2933 1 : CATCH_REQUIRE(json->parse(in) == as2js::json::json_value::pointer_t());
2934 1 : tc.got_called();
2935 1 : }
2936 19 : CATCH_END_SECTION()
2937 :
2938 19 : CATCH_START_SECTION("json: double comma")
2939 : {
2940 1 : std::string str(
2941 : "['valid',-123,,'next-item',456]"
2942 2 : );
2943 1 : as2js::input_stream<std::stringstream>::pointer_t in(new as2js::input_stream<std::stringstream>());
2944 1 : *in << str;
2945 :
2946 1 : test_callback tc;
2947 :
2948 1 : test_callback::expected_t expected1;
2949 1 : expected1.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_ERROR;
2950 1 : expected1.f_error_code = as2js::err_code_t::AS_ERR_UNEXPECTED_TOKEN;
2951 1 : expected1.f_pos.set_filename("unknown-file");
2952 1 : expected1.f_pos.set_function("unknown-func");
2953 1 : expected1.f_message = "unexpected token (COMMA) found in a JSON input stream.";
2954 1 : tc.f_expected.push_back(expected1);
2955 :
2956 1 : test_callback::expected_t expected2;
2957 1 : expected2.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_FATAL;
2958 1 : expected2.f_error_code = as2js::err_code_t::AS_ERR_CANNOT_COMPILE;
2959 1 : expected2.f_pos.set_filename("unknown-file");
2960 1 : expected2.f_pos.set_function("unknown-func");
2961 1 : expected2.f_message = "could not interpret this JSON input \"\".";
2962 1 : tc.f_expected.push_back(expected2);
2963 :
2964 1 : as2js::json::pointer_t json(new as2js::json);
2965 1 : CATCH_REQUIRE(json->parse(in) == as2js::json::json_value::pointer_t());
2966 1 : tc.got_called();
2967 1 : }
2968 19 : CATCH_END_SECTION()
2969 :
2970 19 : CATCH_START_SECTION("json: negative string")
2971 : {
2972 1 : std::string str(
2973 : "['valid',-555,'bad-neg',-'123']"
2974 2 : );
2975 1 : as2js::input_stream<std::stringstream>::pointer_t in(new as2js::input_stream<std::stringstream>());
2976 1 : *in << str;
2977 :
2978 1 : test_callback tc;
2979 :
2980 1 : test_callback::expected_t expected1;
2981 1 : expected1.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_ERROR;
2982 1 : expected1.f_error_code = as2js::err_code_t::AS_ERR_UNEXPECTED_TOKEN;
2983 1 : expected1.f_pos.set_filename("unknown-file");
2984 1 : expected1.f_pos.set_function("unknown-func");
2985 1 : expected1.f_message = "unexpected token (STRING) found after a \"-\" sign, a number was expected.";
2986 1 : tc.f_expected.push_back(expected1);
2987 :
2988 1 : test_callback::expected_t expected2;
2989 1 : expected2.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_FATAL;
2990 1 : expected2.f_error_code = as2js::err_code_t::AS_ERR_CANNOT_COMPILE;
2991 1 : expected2.f_pos.set_filename("unknown-file");
2992 1 : expected2.f_pos.set_function("unknown-func");
2993 1 : expected2.f_message = "could not interpret this JSON input \"\".";
2994 1 : tc.f_expected.push_back(expected2);
2995 :
2996 1 : as2js::json::pointer_t json(new as2js::json);
2997 1 : CATCH_REQUIRE(json->parse(in) == as2js::json::json_value::pointer_t());
2998 1 : tc.got_called();
2999 1 : }
3000 19 : CATCH_END_SECTION()
3001 :
3002 19 : CATCH_START_SECTION("json: positive string")
3003 : {
3004 1 : std::string str(
3005 : "['valid',+555,'bad-pos',+'123']"
3006 2 : );
3007 1 : as2js::input_stream<std::stringstream>::pointer_t in(new as2js::input_stream<std::stringstream>());
3008 1 : *in << str;
3009 :
3010 1 : test_callback tc;
3011 :
3012 1 : test_callback::expected_t expected1;
3013 1 : expected1.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_ERROR;
3014 1 : expected1.f_error_code = as2js::err_code_t::AS_ERR_UNEXPECTED_TOKEN;
3015 1 : expected1.f_pos.set_filename("unknown-file");
3016 1 : expected1.f_pos.set_function("unknown-func");
3017 1 : expected1.f_message = "unexpected token (STRING) found after a \"+\" sign, a number was expected.";
3018 1 : tc.f_expected.push_back(expected1);
3019 :
3020 1 : test_callback::expected_t expected2;
3021 1 : expected2.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_FATAL;
3022 1 : expected2.f_error_code = as2js::err_code_t::AS_ERR_CANNOT_COMPILE;
3023 1 : expected2.f_pos.set_filename("unknown-file");
3024 1 : expected2.f_pos.set_function("unknown-func");
3025 1 : expected2.f_message = "could not interpret this JSON input \"\".";
3026 1 : tc.f_expected.push_back(expected2);
3027 :
3028 1 : as2js::json::pointer_t json(new as2js::json);
3029 1 : CATCH_REQUIRE(json->parse(in) == as2js::json::json_value::pointer_t());
3030 1 : tc.got_called();
3031 1 : }
3032 19 : CATCH_END_SECTION()
3033 :
3034 19 : CATCH_START_SECTION("json: missing comma")
3035 : {
3036 1 : std::string str(
3037 : "['valid',123 'next-item',456]"
3038 2 : );
3039 1 : as2js::input_stream<std::stringstream>::pointer_t in(new as2js::input_stream<std::stringstream>());
3040 1 : *in << str;
3041 :
3042 1 : test_callback tc;
3043 :
3044 1 : test_callback::expected_t expected1;
3045 1 : expected1.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_ERROR;
3046 1 : expected1.f_error_code = as2js::err_code_t::AS_ERR_COMMA_EXPECTED;
3047 1 : expected1.f_pos.set_filename("unknown-file");
3048 1 : expected1.f_pos.set_function("unknown-func");
3049 1 : expected1.f_message = "expected a comma (,) to separate two JSON array items.";
3050 1 : tc.f_expected.push_back(expected1);
3051 :
3052 1 : test_callback::expected_t expected2;
3053 1 : expected2.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_FATAL;
3054 1 : expected2.f_error_code = as2js::err_code_t::AS_ERR_CANNOT_COMPILE;
3055 1 : expected2.f_pos.set_filename("unknown-file");
3056 1 : expected2.f_pos.set_function("unknown-func");
3057 1 : expected2.f_message = "could not interpret this JSON input \"\".";
3058 1 : tc.f_expected.push_back(expected2);
3059 :
3060 1 : as2js::json::pointer_t json(new as2js::json);
3061 1 : CATCH_REQUIRE(json->parse(in) == as2js::json::json_value::pointer_t());
3062 1 : tc.got_called();
3063 1 : }
3064 19 : CATCH_END_SECTION()
3065 :
3066 19 : CATCH_START_SECTION("json: missing comma in sub-array")
3067 : {
3068 1 : std::string str(
3069 : "['valid',[123 'next-item'],456]"
3070 2 : );
3071 1 : as2js::input_stream<std::stringstream>::pointer_t in(new as2js::input_stream<std::stringstream>());
3072 1 : *in << str;
3073 :
3074 1 : test_callback tc;
3075 :
3076 1 : test_callback::expected_t expected1;
3077 1 : expected1.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_ERROR;
3078 1 : expected1.f_error_code = as2js::err_code_t::AS_ERR_COMMA_EXPECTED;
3079 1 : expected1.f_pos.set_filename("unknown-file");
3080 1 : expected1.f_pos.set_function("unknown-func");
3081 1 : expected1.f_message = "expected a comma (,) to separate two JSON array items.";
3082 1 : tc.f_expected.push_back(expected1);
3083 :
3084 1 : test_callback::expected_t expected2;
3085 1 : expected2.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_FATAL;
3086 1 : expected2.f_error_code = as2js::err_code_t::AS_ERR_CANNOT_COMPILE;
3087 1 : expected2.f_pos.set_filename("unknown-file");
3088 1 : expected2.f_pos.set_function("unknown-func");
3089 1 : expected2.f_message = "could not interpret this JSON input \"\".";
3090 1 : tc.f_expected.push_back(expected2);
3091 :
3092 1 : as2js::json::pointer_t json(new as2js::json);
3093 1 : CATCH_REQUIRE(json->parse(in) == as2js::json::json_value::pointer_t());
3094 1 : tc.got_called();
3095 1 : }
3096 19 : CATCH_END_SECTION()
3097 :
3098 19 : CATCH_START_SECTION("json: unexpected token")
3099 : {
3100 : // skip controls to avoid problems with the lexer itself...
3101 1114081 : for(char32_t c(0x20); c < 0x110000; ++c)
3102 : {
3103 1114080 : switch(c)
3104 : {
3105 : //case '\n':
3106 : //case '\r':
3107 : //case '\t':
3108 22 : case ' ':
3109 : case '{':
3110 : case '[':
3111 : case '\'':
3112 : case '"':
3113 : case '#':
3114 : case '-':
3115 : case '@':
3116 : case '\\':
3117 : case '`':
3118 : case 0x7F:
3119 : //case ',': -- that would generate errors because it would be in the wrong place
3120 : case '.':
3121 : case '0':
3122 : case '1':
3123 : case '2':
3124 : case '3':
3125 : case '4':
3126 : case '5':
3127 : case '6':
3128 : case '7':
3129 : case '8':
3130 : case '9':
3131 : // that looks like valid entries as is... so ignore
3132 22 : continue;
3133 :
3134 1114058 : default:
3135 1114058 : if(c >= 0xD800 && c <= 0xDFFF)
3136 : {
3137 : // skip surrogate, no need to test those
3138 2048 : continue;
3139 : }
3140 1112010 : if(!is_identifier_char(c))
3141 : {
3142 : // skip "punctuation" for now...
3143 976961 : continue;
3144 : }
3145 135049 : break;
3146 :
3147 : }
3148 270098 : std::string str;
3149 135049 : str += libutf8::to_u8string(c);
3150 :
3151 270098 : as2js::node::pointer_t node;
3152 : {
3153 135049 : as2js::options::pointer_t options(std::make_shared<as2js::options>());
3154 135049 : options->set_option(as2js::option_t::OPTION_JSON, 1);
3155 135049 : as2js::input_stream<std::stringstream>::pointer_t input(std::make_shared<as2js::input_stream<std::stringstream>>());
3156 135049 : *input << str;
3157 135049 : as2js::lexer::pointer_t lexer(std::make_shared<as2js::lexer>(input, options));
3158 135049 : CATCH_REQUIRE(lexer->get_input() == input);
3159 135049 : node = lexer->get_next_token(false);
3160 135049 : CATCH_REQUIRE(node != nullptr);
3161 135049 : }
3162 :
3163 270098 : as2js::input_stream<std::stringstream>::pointer_t in(std::make_shared<as2js::input_stream<std::stringstream>>());
3164 135049 : *in << str;
3165 :
3166 270098 : test_callback tc;
3167 :
3168 270098 : test_callback::expected_t expected1;
3169 135049 : expected1.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_ERROR;
3170 135049 : expected1.f_error_code = as2js::err_code_t::AS_ERR_UNEXPECTED_TOKEN;
3171 135049 : expected1.f_pos.set_filename("unknown-file");
3172 135049 : expected1.f_pos.set_function("unknown-func");
3173 135049 : expected1.f_message = "unexpected token (";
3174 135049 : expected1.f_message += node->get_type_name();
3175 135049 : expected1.f_message += ") found in a JSON input stream.";
3176 135049 : tc.f_expected.push_back(expected1);
3177 :
3178 270098 : test_callback::expected_t expected2;
3179 135049 : expected2.f_message_level = as2js::message_level_t::MESSAGE_LEVEL_FATAL;
3180 135049 : expected2.f_error_code = as2js::err_code_t::AS_ERR_CANNOT_COMPILE;
3181 135049 : expected2.f_pos.set_filename("unknown-file");
3182 135049 : expected2.f_pos.set_function("unknown-func");
3183 135049 : expected2.f_message = "could not interpret this JSON input \"\".";
3184 135049 : tc.f_expected.push_back(expected2);
3185 :
3186 270098 : as2js::json::pointer_t json(std::make_shared<as2js::json>());
3187 405147 : as2js::json::json_value::pointer_t result(json->parse(in));
3188 135049 : CATCH_REQUIRE(result == as2js::json::json_value::pointer_t());
3189 135049 : tc.got_called();
3190 : }
3191 : }
3192 19 : CATCH_END_SECTION()
3193 19 : }
3194 :
3195 :
3196 1 : CATCH_TEST_CASE("json_canonicalization", "[json][canonical]")
3197 : {
3198 1 : CATCH_START_SECTION("json: canonicalize")
3199 : {
3200 1 : char const * const json_to_canonicalize[] =
3201 : {
3202 : "{}",
3203 : "{}",
3204 :
3205 : "{\"we-accept\": 'some funny things'}",
3206 : "{\"we-accept\":\"some funny things\"}",
3207 :
3208 : "{'single_field': 11.3040}",
3209 : "{\"single_field\":11.304}",
3210 :
3211 : "{'no_decimal': 34.00}",
3212 : "{\"no_decimal\":34}",
3213 : };
3214 1 : std::size_t const max(std::size(json_to_canonicalize));
3215 5 : for(std::size_t idx(0); idx < max; idx += 2)
3216 : {
3217 12 : std::string const result(as2js::json_canonicalize(json_to_canonicalize[idx]));
3218 4 : CATCH_REQUIRE(result == json_to_canonicalize[idx + 1]);
3219 4 : }
3220 : }
3221 1 : CATCH_END_SECTION()
3222 1 : }
3223 :
3224 :
3225 :
3226 : // vim: ts=4 sw=4 et
|