LCOV - code coverage report
Current view: top level - tests - catch_json_tokens.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 547 547 100.0 %
Date: 2023-01-26 17:17:53 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13