LCOV - code coverage report
Current view: top level - tests - catch_json_tokens.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 615 615
Test Date: 2025-08-03 08:53:08 Functions: 100.0 % 2 2
Legend: Lines: hit not hit

            Line data    Source code
       1              : // Copyright (c) 2021-2025  Made to Order Software Corp.  All Rights Reserved
       2              : //
       3              : // https://snapwebsites.org/project/libutf8
       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              : // libutf8
      20              : //
      21              : #include    <libutf8/json_tokens.h>
      22              : 
      23              : #include    <libutf8/libutf8.h>
      24              : 
      25              : 
      26              : // unit test
      27              : //
      28              : #include    "catch_main.h"
      29              : 
      30              : 
      31              : // snapdev
      32              : //
      33              : #include    <snapdev/file_contents.h>
      34              : 
      35              : 
      36              : // C++
      37              : //
      38              : #include    <cctype>
      39              : #include    <iomanip>
      40              : #include    <iostream>
      41              : 
      42              : 
      43              : // last include
      44              : //
      45              : #include    <snapdev/poison.h>
      46              : 
      47              : 
      48              : 
      49              : #pragma GCC diagnostic push
      50              : #pragma GCC diagnostic ignored "-Wfloat-equal"
      51            4 : CATCH_TEST_CASE("json_tokens", "[json][iterator]")
      52              : {
      53            4 :     CATCH_START_SECTION("json_tokens: valid JSON tokens")
      54              :     {
      55            1 :         std::string valid_json(
      56              :             "{\n"
      57              :                 "\"array-of-numbers\": [\n"
      58              :                     "\t1,\r\n"
      59              :                     "\t1.0,\r"
      60              :                     "\t-0.1\r\n"
      61              :                 "]   ,  \n"
      62              :                 "\"color\"      :true  ,\n"
      63              :                 "\"temporary\"  :false,   \r"
      64              :                 "\"flowers\":null\r\n"
      65              :             "}"
      66            3 :         );
      67              : 
      68              : #if 0
      69              :         snap::file_contents f("test.json");
      70              :         f.contents(valid_json);
      71              :         f.write_all();
      72              : #endif
      73              : 
      74            1 :         libutf8::json_tokens jt(valid_json);
      75            1 :         CATCH_REQUIRE(jt.line() == 0);
      76            1 :         CATCH_REQUIRE(jt.column() == 0);
      77            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
      78            1 :         CATCH_REQUIRE(jt.line() == 1);
      79            1 :         CATCH_REQUIRE(jt.column() == 1);
      80            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
      81            1 :         CATCH_REQUIRE(jt.string() == "array-of-numbers");
      82            1 :         CATCH_REQUIRE(jt.line() == 2);
      83            1 :         CATCH_REQUIRE(jt.column() == 1);
      84            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
      85            1 :         CATCH_REQUIRE(jt.line() == 2);
      86            1 :         CATCH_REQUIRE(jt.column() == 19);
      87            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_ARRAY);
      88            1 :         CATCH_REQUIRE(jt.line() == 2);
      89            1 :         CATCH_REQUIRE(jt.column() == 21);
      90            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_NUMBER);
      91            1 :         CATCH_REQUIRE(jt.number() == 1.0_a);
      92            1 :         CATCH_REQUIRE(jt.line() == 3);
      93            1 :         CATCH_REQUIRE(jt.column() == 2);
      94            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COMMA);
      95            1 :         CATCH_REQUIRE(jt.line() == 3);
      96            1 :         CATCH_REQUIRE(jt.column() == 3 + 1);    // error due to the unget
      97            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_NUMBER);
      98            1 :         CATCH_REQUIRE(jt.number() == 1.0_a);
      99            1 :         CATCH_REQUIRE(jt.line() == 4);
     100            1 :         CATCH_REQUIRE(jt.column() == 2);
     101            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COMMA);
     102            1 :         CATCH_REQUIRE(jt.line() == 4);
     103            1 :         CATCH_REQUIRE(jt.column() == 5 + 1);    // error due to the unget
     104            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_NUMBER);
     105            1 :         CATCH_REQUIRE(jt.number() == -0.1_a);
     106            1 :         CATCH_REQUIRE(jt.line() == 5);
     107            1 :         CATCH_REQUIRE(jt.column() == 2);
     108            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_CLOSE_ARRAY);
     109            1 :         CATCH_REQUIRE(jt.line() == 6);
     110            1 :         CATCH_REQUIRE(jt.column() == 1);
     111            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COMMA);
     112            1 :         CATCH_REQUIRE(jt.line() == 6);
     113            1 :         CATCH_REQUIRE(jt.column() == 5);
     114            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     115            1 :         CATCH_REQUIRE(jt.string() == "color");
     116            1 :         CATCH_REQUIRE(jt.line() == 7);
     117            1 :         CATCH_REQUIRE(jt.column() == 1);
     118            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     119            1 :         CATCH_REQUIRE(jt.line() == 7);
     120            1 :         CATCH_REQUIRE(jt.column() == 14);
     121            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_TRUE);
     122            1 :         CATCH_REQUIRE(jt.line() == 7);
     123            1 :         CATCH_REQUIRE(jt.column() == 15);
     124            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COMMA);
     125            1 :         CATCH_REQUIRE(jt.line() == 7);
     126            1 :         CATCH_REQUIRE(jt.column() == 21);
     127            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     128            1 :         CATCH_REQUIRE(jt.string() == "temporary");
     129            1 :         CATCH_REQUIRE(jt.line() == 8);
     130            1 :         CATCH_REQUIRE(jt.column() == 1);
     131            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     132            1 :         CATCH_REQUIRE(jt.line() == 8);
     133            1 :         CATCH_REQUIRE(jt.column() == 14);
     134            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_FALSE);
     135            1 :         CATCH_REQUIRE(jt.line() == 8);
     136            1 :         CATCH_REQUIRE(jt.column() == 15);
     137            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COMMA);
     138            1 :         CATCH_REQUIRE(jt.line() == 8);
     139            1 :         CATCH_REQUIRE(jt.column() == 20);
     140            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     141            1 :         CATCH_REQUIRE(jt.string() == "flowers");
     142            1 :         CATCH_REQUIRE(jt.line() == 9);
     143            1 :         CATCH_REQUIRE(jt.column() == 1);
     144            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     145            1 :         CATCH_REQUIRE(jt.line() == 9);
     146            1 :         CATCH_REQUIRE(jt.column() == 10);
     147            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_NULL);
     148            1 :         CATCH_REQUIRE(jt.line() == 9);
     149            1 :         CATCH_REQUIRE(jt.column() == 11);
     150            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_CLOSE_OBJECT);
     151            1 :         CATCH_REQUIRE(jt.line() == 10);
     152            1 :         CATCH_REQUIRE(jt.column() == 1);
     153            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_END);
     154            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_END);
     155            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_END);
     156            1 :     }
     157            4 :     CATCH_END_SECTION()
     158              : 
     159            4 :     CATCH_START_SECTION("json_tokens: valid JSON numbers")
     160              :     {
     161            1 :         std::string valid_json(
     162              :             "["
     163              :                 "733,"
     164              :                 "-1892,"
     165              :                 "-1.892,"
     166              :                 "-9.892e33,"
     167              :                 "101.302e+3,"
     168              :                 "5031.70232e-13"
     169              :             "]"
     170            3 :         );
     171              : 
     172            1 :         libutf8::json_tokens jt(valid_json);
     173            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_ARRAY);
     174            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_NUMBER);
     175            1 :         CATCH_REQUIRE_FLOATING_POINT(jt.number(), 733.0);
     176            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COMMA);
     177            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_NUMBER);
     178            1 :         CATCH_REQUIRE_FLOATING_POINT(jt.number(), -1892.0);
     179            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COMMA);
     180            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_NUMBER);
     181            1 :         CATCH_REQUIRE_FLOATING_POINT(jt.number(), -1.892);
     182            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COMMA);
     183            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_NUMBER);
     184            1 :         CATCH_REQUIRE_FLOATING_POINT(jt.number(), -9.892e33);
     185            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COMMA);
     186            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_NUMBER);
     187            1 :         CATCH_REQUIRE_FLOATING_POINT(jt.number(), 101.302e3);
     188            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COMMA);
     189            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_NUMBER);
     190            1 :         CATCH_REQUIRE_FLOATING_POINT(jt.number(), 5031.70232e-13);
     191            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_CLOSE_ARRAY);
     192            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_END);
     193            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_END);
     194            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_END);
     195            1 :     }
     196            4 :     CATCH_END_SECTION()
     197              : 
     198            4 :     CATCH_START_SECTION("json_tokens: valid JSON special escaped characters")
     199              :     {
     200            1 :         std::string valid_json(
     201              :             "{"
     202              :                 "\"backslash\":\"\\\\\","
     203              :                 "\"quote\":\"\\\"\","
     204              :                 "\"slash\":\"\\/\","
     205              :                 "\"backspace\":\"\\b\","
     206              :                 "\"formfeed\":\"\\f\","
     207              :                 "\"newline\":\"\\n\","
     208              :                 "\"carriage-return\":\"\\r\","
     209              :                 "\"tab\":\"\\t\""
     210              :             "}"
     211            3 :         );
     212              : 
     213            1 :         libutf8::json_tokens jt(valid_json);
     214            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     215            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     216            1 :         CATCH_REQUIRE(jt.string() == "backslash");
     217            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     218            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     219            1 :         CATCH_REQUIRE(jt.string() == "\\");
     220            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COMMA);
     221            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     222            1 :         CATCH_REQUIRE(jt.string() == "quote");
     223            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     224            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     225            1 :         CATCH_REQUIRE(jt.string() == "\"");
     226            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COMMA);
     227            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     228            1 :         CATCH_REQUIRE(jt.string() == "slash");
     229            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     230            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     231            1 :         CATCH_REQUIRE(jt.string() == "/");
     232            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COMMA);
     233            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     234            1 :         CATCH_REQUIRE(jt.string() == "backspace");
     235            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     236            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     237            1 :         CATCH_REQUIRE(jt.string() == "\b");
     238            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COMMA);
     239            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     240            1 :         CATCH_REQUIRE(jt.string() == "formfeed");
     241            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     242            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     243            1 :         CATCH_REQUIRE(jt.string() == "\f");
     244            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COMMA);
     245            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     246            1 :         CATCH_REQUIRE(jt.string() == "newline");
     247            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     248            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     249            1 :         CATCH_REQUIRE(jt.string() == "\n");
     250            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COMMA);
     251            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     252            1 :         CATCH_REQUIRE(jt.string() == "carriage-return");
     253            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     254            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     255            1 :         CATCH_REQUIRE(jt.string() == "\r");
     256            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COMMA);
     257            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     258            1 :         CATCH_REQUIRE(jt.string() == "tab");
     259            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     260            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     261            1 :         CATCH_REQUIRE(jt.string() == "\t");
     262            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_CLOSE_OBJECT);
     263            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_END);
     264            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_END);
     265            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_END);
     266            1 :     }
     267            4 :     CATCH_END_SECTION()
     268              : 
     269            4 :     CATCH_START_SECTION("json_tokens: valid JSON unicode escaped characters")
     270              :     {
     271      1114112 :         for(char32_t c(1); c < 0x110000; ++c)
     272              :         {
     273      1114111 :             if(c >= 0xD800 && c <= 0xDFFF)
     274              :             {
     275         2048 :                 continue;
     276              :             }
     277              : 
     278      1112063 :             std::stringstream ss;
     279      1112063 :             std::string valid_json;
     280      1112063 :             valid_json += "{\"unicode\":\"\\u";
     281      1112063 :             if(c > 0xFFFF)
     282              :             {
     283      1048576 :                 int s(c - 0x10000);
     284      1048576 :                 ss << std::hex << std::setw(4) << std::setfill('0') << ((s >> 10) | 0xD800)
     285      1048576 :                    << "\\u" << std::setw(4) << std::setfill('0') << ((s & 0x3FF) | 0xDC00);
     286              :             }
     287              :             else
     288              :             {
     289        63487 :                 ss << std::hex << std::setw(4) << std::setfill('0') << static_cast<int>(c);
     290              :             }
     291      1112063 :             valid_json += ss.str();
     292      1112063 :             valid_json += "\"}";
     293              : 
     294      1112063 :             libutf8::json_tokens jt(valid_json);
     295      1112063 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     296      1112063 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     297      1112063 :             CATCH_REQUIRE(jt.string() == "unicode");
     298      1112063 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     299      1112063 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     300      1112063 :             std::string expected(libutf8::to_u8string(c));
     301      1112063 :             CATCH_REQUIRE_LONG_STRING(jt.string(), expected);
     302      1112063 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_CLOSE_OBJECT);
     303      1112063 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_END);
     304      1112063 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_END);
     305      1112063 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_END);
     306      1112063 :         }
     307              :     }
     308            4 :     CATCH_END_SECTION()
     309            4 : }
     310              : #pragma GCC diagnostic pop
     311              : 
     312              : 
     313           20 : CATCH_TEST_CASE("json_tokens_invalid", "[json][iterator][invalid]")
     314              : {
     315           20 :     CATCH_START_SECTION("json_tokens_invalid: invalid JSON negative number")
     316              :     {
     317            1 :         std::string valid_json(
     318              :             "-a"
     319            3 :         );
     320              : 
     321            1 :         libutf8::json_tokens jt(valid_json);
     322            1 :         CATCH_REQUIRE(jt.line() == 0);
     323            1 :         CATCH_REQUIRE(jt.column() == 0);
     324            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     325            1 :         CATCH_REQUIRE(jt.line() == 1);
     326            1 :         CATCH_REQUIRE(jt.column() == 1);
     327            1 :         CATCH_REQUIRE(jt.error() == "found unexpected character: '-'");
     328            1 :     }
     329           20 :     CATCH_END_SECTION()
     330              : 
     331           20 :     CATCH_START_SECTION("json_tokens_invalid: invalid JSON number with fraction")
     332              :     {
     333            1 :         std::string valid_json(
     334              :             "-3., 2."
     335            3 :         );
     336              : 
     337            1 :         libutf8::json_tokens jt(valid_json);
     338            1 :         CATCH_REQUIRE(jt.line() == 0);
     339            1 :         CATCH_REQUIRE(jt.column() == 0);
     340            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     341            1 :         CATCH_REQUIRE(jt.line() == 1);
     342            1 :         CATCH_REQUIRE(jt.column() == 1);
     343            1 :         CATCH_REQUIRE(jt.error() == "number cannot end with a period (\"1.\" is not valid JSON)");
     344            1 :     }
     345           20 :     CATCH_END_SECTION()
     346              : 
     347           20 :     CATCH_START_SECTION("json_tokens_invalid: invalid JSON number exponent")
     348              :     {
     349            1 :         std::string valid_json(
     350              :             "-3.0e+a, 2.1"
     351            3 :         );
     352              : 
     353            1 :         libutf8::json_tokens jt(valid_json);
     354            1 :         CATCH_REQUIRE(jt.line() == 0);
     355            1 :         CATCH_REQUIRE(jt.column() == 0);
     356            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     357            1 :         CATCH_REQUIRE(jt.line() == 1);
     358            1 :         CATCH_REQUIRE(jt.column() == 1);
     359            1 :         CATCH_REQUIRE(jt.error() == "number exponent must include at least one digit");
     360            1 :     }
     361           20 :     CATCH_END_SECTION()
     362              : 
     363           20 :     CATCH_START_SECTION("json_tokens_invalid: invalid JSON number with fraction")
     364              :     {
     365            1 :         std::string valid_json(
     366              :             "\"back\\slash\""
     367            3 :         );
     368              : 
     369            1 :         libutf8::json_tokens jt(valid_json);
     370            1 :         CATCH_REQUIRE(jt.line() == 0);
     371            1 :         CATCH_REQUIRE(jt.column() == 0);
     372            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     373            1 :         CATCH_REQUIRE(jt.line() == 1);
     374            1 :         CATCH_REQUIRE(jt.column() == 1);
     375            1 :         CATCH_REQUIRE(jt.error() == "unexpected escape character: 's'");
     376            1 :     }
     377           20 :     CATCH_END_SECTION()
     378              : 
     379           20 :     CATCH_START_SECTION("json_tokens_invalid: unsupported JSON backslash character")
     380              :     {
     381              :         {
     382            1 :             std::string valid_json(
     383              :                 "\"back\\slash\""
     384            3 :             );
     385              : 
     386            1 :             libutf8::json_tokens jt(valid_json);
     387            1 :             CATCH_REQUIRE(jt.line() == 0);
     388            1 :             CATCH_REQUIRE(jt.column() == 0);
     389            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     390            1 :             CATCH_REQUIRE(jt.line() == 1);
     391            1 :             CATCH_REQUIRE(jt.column() == 1);
     392            1 :             CATCH_REQUIRE(jt.error() == "unexpected escape character: 's'");
     393            1 :         }
     394              : 
     395              :     }
     396           20 :     CATCH_END_SECTION()
     397              : 
     398           20 :     CATCH_START_SECTION("json_tokens_invalid: invalid JSON unicode: too short")
     399              :     {
     400              :         {
     401            1 :             std::string valid_json(
     402              :                 "\"\\u0"
     403            3 :             );
     404              : 
     405            1 :             libutf8::json_tokens jt(valid_json);
     406            1 :             CATCH_REQUIRE(jt.line() == 0);
     407            1 :             CATCH_REQUIRE(jt.column() == 0);
     408            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     409            1 :             CATCH_REQUIRE(jt.line() == 1);
     410            1 :             CATCH_REQUIRE(jt.column() == 1);
     411            1 :             CATCH_REQUIRE(jt.error() == "invalid unicode character: 'EOS'");
     412            1 :         }
     413              : 
     414              :         {
     415            1 :             std::string valid_json(
     416              :                 "\"\\u20"
     417            3 :             );
     418              : 
     419            1 :             libutf8::json_tokens jt(valid_json);
     420            1 :             CATCH_REQUIRE(jt.line() == 0);
     421            1 :             CATCH_REQUIRE(jt.column() == 0);
     422            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     423            1 :             CATCH_REQUIRE(jt.line() == 1);
     424            1 :             CATCH_REQUIRE(jt.column() == 1);
     425            1 :             CATCH_REQUIRE(jt.error() == "invalid unicode character: 'EOS'");
     426            1 :         }
     427              : 
     428              :         {
     429            1 :             std::string valid_json(
     430              :                 "\"\\u301"
     431            3 :             );
     432              : 
     433            1 :             libutf8::json_tokens jt(valid_json);
     434            1 :             CATCH_REQUIRE(jt.line() == 0);
     435            1 :             CATCH_REQUIRE(jt.column() == 0);
     436            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     437            1 :             CATCH_REQUIRE(jt.line() == 1);
     438            1 :             CATCH_REQUIRE(jt.column() == 1);
     439            1 :             CATCH_REQUIRE(jt.error() == "invalid unicode character: 'EOS'");
     440            1 :         }
     441              : 
     442              :     }
     443           20 :     CATCH_END_SECTION()
     444              : 
     445           20 :     CATCH_START_SECTION("json_tokens_invalid: invalid JSON unicode: low surrogate missing backslash")
     446              :     {
     447            1 :         std::string valid_json(
     448              :             "\"\\uD91Fmissing\""
     449            3 :         );
     450              : 
     451            1 :         libutf8::json_tokens jt(valid_json);
     452            1 :         CATCH_REQUIRE(jt.line() == 0);
     453            1 :         CATCH_REQUIRE(jt.column() == 0);
     454            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     455            1 :         CATCH_REQUIRE(jt.line() == 1);
     456            1 :         CATCH_REQUIRE(jt.column() == 1);
     457            1 :         CATCH_REQUIRE(jt.error() == "expected a low surrogate right after a high surrogate, backslash (\\) mising");
     458            1 :     }
     459           20 :     CATCH_END_SECTION()
     460              : 
     461           20 :     CATCH_START_SECTION("json_tokens_invalid: invalid JSON unicode: low surrogate missing 'u'")
     462              :     {
     463            1 :         std::string valid_json(
     464              :             "\"\\uD91F\\missing\""
     465            3 :         );
     466              : 
     467            1 :         libutf8::json_tokens jt(valid_json);
     468            1 :         CATCH_REQUIRE(jt.line() == 0);
     469            1 :         CATCH_REQUIRE(jt.column() == 0);
     470            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     471            1 :         CATCH_REQUIRE(jt.line() == 1);
     472            1 :         CATCH_REQUIRE(jt.column() == 1);
     473            1 :         CATCH_REQUIRE(jt.error() == "expected a low surrogate right after a high surrogate, 'u' missing");
     474            1 :     }
     475           20 :     CATCH_END_SECTION()
     476              : 
     477           20 :     CATCH_START_SECTION("json_tokens_invalid: invalid JSON unicode: low surrogate expected")
     478              :     {
     479              :         {
     480            1 :             std::string valid_json(
     481              :                 "\"\\uD91F\\u0010\""
     482            3 :             );
     483              : 
     484            1 :             libutf8::json_tokens jt(valid_json);
     485            1 :             CATCH_REQUIRE(jt.line() == 0);
     486            1 :             CATCH_REQUIRE(jt.column() == 0);
     487            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     488            1 :             CATCH_REQUIRE(jt.line() == 1);
     489            1 :             CATCH_REQUIRE(jt.column() == 1);
     490            1 :             CATCH_REQUIRE(jt.error() == "expected a low surrogate right after a high surrogate");
     491            1 :         }
     492              : 
     493              :         {
     494            1 :             std::string valid_json(
     495              :                 "\"\\uD91F\\uDBFF\""
     496            3 :             );
     497              : 
     498            1 :             libutf8::json_tokens jt(valid_json);
     499            1 :             CATCH_REQUIRE(jt.line() == 0);
     500            1 :             CATCH_REQUIRE(jt.column() == 0);
     501            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     502            1 :             CATCH_REQUIRE(jt.line() == 1);
     503            1 :             CATCH_REQUIRE(jt.column() == 1);
     504            1 :             CATCH_REQUIRE(jt.error() == "expected a low surrogate right after a high surrogate");
     505            1 :         }
     506              : 
     507              :         {
     508            1 :             std::string valid_json(
     509              :                 "\"\\uD91F\\uE030"
     510            3 :             );
     511              : 
     512            1 :             libutf8::json_tokens jt(valid_json);
     513            1 :             CATCH_REQUIRE(jt.line() == 0);
     514            1 :             CATCH_REQUIRE(jt.column() == 0);
     515            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     516            1 :             CATCH_REQUIRE(jt.line() == 1);
     517            1 :             CATCH_REQUIRE(jt.column() == 1);
     518            1 :             CATCH_REQUIRE(jt.error() == "expected a low surrogate right after a high surrogate");
     519            1 :         }
     520              :     }
     521           20 :     CATCH_END_SECTION()
     522              : 
     523           20 :     CATCH_START_SECTION("json_tokens_invalid: invalid JSON unicode: low surrogate too short")
     524              :     {
     525              :         {
     526            1 :             std::string valid_json(
     527              :                 "\"\\uD91F\\u0"
     528            3 :             );
     529              : 
     530            1 :             libutf8::json_tokens jt(valid_json);
     531            1 :             CATCH_REQUIRE(jt.line() == 0);
     532            1 :             CATCH_REQUIRE(jt.column() == 0);
     533            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     534            1 :             CATCH_REQUIRE(jt.line() == 1);
     535            1 :             CATCH_REQUIRE(jt.column() == 1);
     536            1 :             CATCH_REQUIRE(jt.error() == "invalid unicode character: 'EOS'");
     537            1 :         }
     538              : 
     539              :         {
     540            1 :             std::string valid_json(
     541              :                 "\"\\uD91F\\u0f"
     542            3 :             );
     543              : 
     544            1 :             libutf8::json_tokens jt(valid_json);
     545            1 :             CATCH_REQUIRE(jt.line() == 0);
     546            1 :             CATCH_REQUIRE(jt.column() == 0);
     547            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     548            1 :             CATCH_REQUIRE(jt.line() == 1);
     549            1 :             CATCH_REQUIRE(jt.column() == 1);
     550            1 :             CATCH_REQUIRE(jt.error() == "invalid unicode character: 'EOS'");
     551            1 :         }
     552              : 
     553              :         {
     554            1 :             std::string valid_json(
     555              :                 "\"\\uD91F\\u0fa"
     556            3 :             );
     557              : 
     558            1 :             libutf8::json_tokens jt(valid_json);
     559            1 :             CATCH_REQUIRE(jt.line() == 0);
     560            1 :             CATCH_REQUIRE(jt.column() == 0);
     561            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     562            1 :             CATCH_REQUIRE(jt.line() == 1);
     563            1 :             CATCH_REQUIRE(jt.column() == 1);
     564            1 :             CATCH_REQUIRE(jt.error() == "invalid unicode character: 'EOS'");
     565            1 :         }
     566              :     }
     567           20 :     CATCH_END_SECTION()
     568              : 
     569           20 :     CATCH_START_SECTION("json_tokens_invalid: invalid JSON unicode: invalid hexadecimal digit (low surrogate)")
     570              :     {
     571              :         {
     572            1 :             std::string valid_json(
     573              :                 "\"\\udb31\\u0t\""
     574            3 :             );
     575              : 
     576            1 :             libutf8::json_tokens jt(valid_json);
     577            1 :             CATCH_REQUIRE(jt.line() == 0);
     578            1 :             CATCH_REQUIRE(jt.column() == 0);
     579            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     580            1 :             CATCH_REQUIRE(jt.line() == 1);
     581            1 :             CATCH_REQUIRE(jt.column() == 1);
     582            1 :             CATCH_REQUIRE(jt.error() == "invalid unicode character: 't'");
     583            1 :         }
     584              : 
     585              :         {
     586            1 :             std::string valid_json(
     587              :                 "\"\\udb31\\u3eg\""
     588            3 :             );
     589              : 
     590            1 :             libutf8::json_tokens jt(valid_json);
     591            1 :             CATCH_REQUIRE(jt.line() == 0);
     592            1 :             CATCH_REQUIRE(jt.column() == 0);
     593            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     594            1 :             CATCH_REQUIRE(jt.line() == 1);
     595            1 :             CATCH_REQUIRE(jt.column() == 1);
     596            1 :             CATCH_REQUIRE(jt.error() == "invalid unicode character: 'g'");
     597            1 :         }
     598              : 
     599              :         {
     600            1 :             std::string valid_json(
     601              :                 "\"\\udb31\\ua3e!\""
     602            3 :             );
     603              : 
     604            1 :             libutf8::json_tokens jt(valid_json);
     605            1 :             CATCH_REQUIRE(jt.line() == 0);
     606            1 :             CATCH_REQUIRE(jt.column() == 0);
     607            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     608            1 :             CATCH_REQUIRE(jt.line() == 1);
     609            1 :             CATCH_REQUIRE(jt.column() == 1);
     610            1 :             CATCH_REQUIRE(jt.error() == "invalid unicode character: '!'");
     611            1 :         }
     612              :     }
     613           20 :     CATCH_END_SECTION()
     614              : 
     615           20 :     CATCH_START_SECTION("json_tokens_invalid: invalid JSON unicode: low surrogate first")
     616              :     {
     617         1025 :         for(char32_t c(0xDC00); c <= 0xDFFF; ++c)
     618              :         {
     619         1024 :             std::stringstream ss;
     620         1024 :             ss << "\\u" << std::hex << std::setw(4)
     621         1024 :                 << std::setfill('0') << static_cast<int>(c);
     622              : 
     623              :             // breaking up line so it compiles on lunar
     624              :             //
     625              :             //libutf8::json_tokens jt("\"" + ss.str() + "\"");
     626         3072 :             std::string str("\"");
     627         1024 :             str += ss.str();
     628         1024 :             str += "\"";
     629         1024 :             libutf8::json_tokens jt(str);
     630              : 
     631         1024 :             CATCH_REQUIRE(jt.line() == 0);
     632         1024 :             CATCH_REQUIRE(jt.column() == 0);
     633         1024 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     634         1024 :             CATCH_REQUIRE(jt.line() == 1);
     635         1024 :             CATCH_REQUIRE(jt.column() == 1);
     636         3072 :             std::string const msg("low surrogate " + ss.str()
     637         2048 :                                     + " found before a high surrogate");
     638         1024 :             CATCH_REQUIRE(jt.error() == msg);
     639         1024 :         }
     640              : 
     641              :     }
     642           20 :     CATCH_END_SECTION()
     643              : 
     644           20 :     CATCH_START_SECTION("json_tokens_invalid: invalid JSON unicode: invalid hexadecimal digit")
     645              :     {
     646              :         {
     647            1 :             std::string valid_json(
     648              :                 "\"\\u5z"
     649            3 :             );
     650              : 
     651            1 :             libutf8::json_tokens jt(valid_json);
     652            1 :             CATCH_REQUIRE(jt.line() == 0);
     653            1 :             CATCH_REQUIRE(jt.column() == 0);
     654            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     655            1 :             CATCH_REQUIRE(jt.line() == 1);
     656            1 :             CATCH_REQUIRE(jt.column() == 1);
     657            1 :             CATCH_REQUIRE(jt.error() == "invalid unicode character: 'z'");
     658            1 :         }
     659              : 
     660              :         {
     661            1 :             std::string valid_json(
     662              :                 "\"\\uaa$"
     663            3 :             );
     664              : 
     665            1 :             libutf8::json_tokens jt(valid_json);
     666            1 :             CATCH_REQUIRE(jt.line() == 0);
     667            1 :             CATCH_REQUIRE(jt.column() == 0);
     668            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     669            1 :             CATCH_REQUIRE(jt.line() == 1);
     670            1 :             CATCH_REQUIRE(jt.column() == 1);
     671            1 :             CATCH_REQUIRE(jt.error() == "invalid unicode character: '$'");
     672            1 :         }
     673              : 
     674              :         {
     675            1 :             std::string valid_json(
     676              :                 "\"\\ua9a\001"
     677            3 :             );
     678              : 
     679            1 :             libutf8::json_tokens jt(valid_json);
     680            1 :             CATCH_REQUIRE(jt.line() == 0);
     681            1 :             CATCH_REQUIRE(jt.column() == 0);
     682            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     683            1 :             CATCH_REQUIRE(jt.line() == 1);
     684            1 :             CATCH_REQUIRE(jt.column() == 1);
     685            1 :             CATCH_REQUIRE(jt.error() == "invalid unicode character: '^A'");
     686            1 :         }
     687              :     }
     688           20 :     CATCH_END_SECTION()
     689              : 
     690           20 :     CATCH_START_SECTION("json_tokens_invalid: unterminated JSON string")
     691              :     {
     692            3 :         libutf8::json_tokens jt("\"unterminated");
     693            1 :         CATCH_REQUIRE(jt.line() == 0);
     694            1 :         CATCH_REQUIRE(jt.column() == 0);
     695            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     696            1 :         CATCH_REQUIRE(jt.line() == 1);
     697            1 :         CATCH_REQUIRE(jt.column() == 1);
     698            1 :         CATCH_REQUIRE(jt.error() == "unclosed string");
     699              : 
     700            1 :     }
     701           20 :     CATCH_END_SECTION()
     702              : 
     703           20 :     CATCH_START_SECTION("json_tokens_invalid: JSON true with missing characters")
     704              :     {
     705              :         {
     706            3 :             libutf8::json_tokens jt("{\"bad-true\":tru}");
     707            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     708            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     709            1 :             CATCH_REQUIRE(jt.string() == "bad-true");
     710            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     711            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     712            1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 't'");
     713            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     714            1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'r'");
     715            1 :         }
     716              : 
     717              :         {
     718            3 :             libutf8::json_tokens jt("{\"bad-true\":tr}");
     719            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     720            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     721            1 :             CATCH_REQUIRE(jt.string() == "bad-true");
     722            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     723            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     724            1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 't'");
     725            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     726            1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'r'");
     727            1 :         }
     728              : 
     729              :         {
     730            3 :             libutf8::json_tokens jt("{\"bad-true\":t}");
     731            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     732            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     733            1 :             CATCH_REQUIRE(jt.string() == "bad-true");
     734            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     735            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     736            1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 't'");
     737            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_CLOSE_OBJECT);
     738            1 :         }
     739              :     }
     740           20 :     CATCH_END_SECTION()
     741              : 
     742           20 :     CATCH_START_SECTION("json_tokens_invalid: JSON false with missing characters")
     743              :     {
     744              :         {
     745            3 :             libutf8::json_tokens jt("{\"bad-false\":fals}");
     746            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     747            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     748            1 :             CATCH_REQUIRE(jt.string() == "bad-false");
     749            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     750            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     751            1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'f'");
     752            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     753            1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'a'");
     754            1 :         }
     755              : 
     756              :         {
     757            3 :             libutf8::json_tokens jt("{\"bad-false\":fal}");
     758            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     759            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     760            1 :             CATCH_REQUIRE(jt.string() == "bad-false");
     761            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     762            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     763            1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'f'");
     764            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     765            1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'a'");
     766            1 :         }
     767              : 
     768              :         {
     769            3 :             libutf8::json_tokens jt("{\"bad-false\":fa}");
     770            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     771            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     772            1 :             CATCH_REQUIRE(jt.string() == "bad-false");
     773            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     774            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     775            1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'f'");
     776            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     777            1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'a'");
     778            1 :         }
     779              : 
     780              :         {
     781            3 :             libutf8::json_tokens jt("{\"bad-false\":f}");
     782            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     783            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     784            1 :             CATCH_REQUIRE(jt.string() == "bad-false");
     785            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     786            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     787            1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'f'");
     788            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_CLOSE_OBJECT);
     789            1 :         }
     790              :     }
     791           20 :     CATCH_END_SECTION()
     792              : 
     793           20 :     CATCH_START_SECTION("json_tokens_invalid: JSON null with missing characters")
     794              :     {
     795              :         {
     796            3 :             libutf8::json_tokens jt("{\"bad-null\":nul}");
     797            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     798            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     799            1 :             CATCH_REQUIRE(jt.string() == "bad-null");
     800            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     801            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     802            1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'n'");
     803            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     804            1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'u'");
     805            1 :         }
     806              : 
     807              :         {
     808            3 :             libutf8::json_tokens jt("{\"bad-null\":nu}");
     809            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     810            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     811            1 :             CATCH_REQUIRE(jt.string() == "bad-null");
     812            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     813            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     814            1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'n'");
     815            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     816            1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'u'");
     817            1 :         }
     818              : 
     819              :         {
     820            3 :             libutf8::json_tokens jt("{\"bad-null\":n}");
     821            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     822            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     823            1 :             CATCH_REQUIRE(jt.string() == "bad-null");
     824            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     825            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     826            1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'n'");
     827            1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_CLOSE_OBJECT);
     828            1 :         }
     829              :     }
     830           20 :     CATCH_END_SECTION()
     831              : 
     832           20 :     CATCH_START_SECTION("json_tokens_invalid: unexpected JSON characters")
     833              :     {
     834      1114112 :         for(char32_t c(1); c < 0x110000; ++c)
     835              :         {
     836      1114111 :             if(c >= 0xD800 && c <= 0xDFFF)
     837              :             {
     838         2048 :                 continue;
     839              :             }
     840      1112063 :             switch(c)
     841              :             {
     842           24 :             case '"':
     843              :             case '{':
     844              :             case '}':
     845              :             case '[':
     846              :             case ']':
     847              :             case '0':
     848              :             case '1':
     849              :             case '2':
     850              :             case '3':
     851              :             case '4':
     852              :             case '5':
     853              :             case '6':
     854              :             case '7':
     855              :             case '8':
     856              :             case '9':
     857              :             case ' ':
     858              :             case '\t':
     859              :             case '\r':
     860              :             case '\n':
     861              :             case 'n':
     862              :             case 't':
     863              :             case 'f':
     864              :             case ',':
     865              :             case ':':
     866           24 :                 continue;
     867              : 
     868              :             }
     869              : 
     870      2224078 :             std::string invalid_json(libutf8::to_u8string(c));
     871      2224078 :             libutf8::json_tokens jt(invalid_json);
     872              : 
     873      1112039 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     874      1112039 :             if(c < 0x20)
     875              :             {
     876           28 :                 CATCH_REQUIRE(jt.error() == "found unexpected character: '^" + libutf8::to_u8string(static_cast<char32_t>(c + 0x40)) + "'");
     877              :             }
     878      1112011 :             else if(c >= 0x80 && c < 0xA0)
     879              :             {
     880           32 :                 CATCH_REQUIRE(jt.error() == "found unexpected character: '@" + libutf8::to_u8string(static_cast<char32_t>(c - 0x40)) + "'");
     881           32 :             }
     882              :             else
     883              :             {
     884      1111979 :                 CATCH_REQUIRE(jt.error() == "found unexpected character: '" + libutf8::to_u8string(c) + "'");
     885              :             }
     886              :         }
     887              :     }
     888           20 :     CATCH_END_SECTION()
     889              : 
     890           20 :     CATCH_START_SECTION("json_tokens_invalid: unexpected '\\0' in JSON")
     891              :     {
     892            1 :         std::string invalid_json;
     893            1 :         invalid_json += '\0';
     894            1 :         libutf8::json_tokens jt(invalid_json);
     895              : 
     896            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     897            1 :         CATCH_REQUIRE(jt.error() == "found unexpected NULL character");
     898            1 :     }
     899           20 :     CATCH_END_SECTION()
     900              : 
     901           20 :     CATCH_START_SECTION("json_tokens_invalid: unexpected '\\0' in JSON string")
     902              :     {
     903            3 :         std::string invalid_json("\"string");
     904            1 :         invalid_json += '\0';
     905            1 :         invalid_json += "with null\"";
     906            1 :         libutf8::json_tokens jt(invalid_json);
     907              : 
     908            1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     909            1 :         CATCH_REQUIRE(jt.error() == "unexpected NULL character in string");
     910            1 :     }
     911           20 :     CATCH_END_SECTION()
     912           20 : }
     913              : 
     914              : 
     915              : 
     916              : // vim: ts=4 sw=4 et
        

Generated by: LCOV version 2.0-1

Snap C++ | List of projects | List of versions