LCOV - code coverage report
Current view: top level - tests - catch_json.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1432 1454 98.5 %
Date: 2023-07-29 22:00:24 Functions: 19 20 95.0 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.14