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: 2022-04-20 16:57:29 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             : // unit test
      21             : //
      22             : #include    "catch_main.h"
      23             : 
      24             : 
      25             : // libutf8 lib
      26             : //
      27             : #include    "libutf8/json_tokens.h"
      28             : #include    "libutf8/libutf8.h"
      29             : 
      30             : 
      31             : // last include
      32             : //
      33             : #include    <snapdev/file_contents.h>
      34             : 
      35             : 
      36             : // C++ lib
      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           6 : CATCH_TEST_CASE("json_tokens", "[json][iterator]")
      52             : {
      53           8 :     CATCH_START_SECTION("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           2 :         );
      67             : 
      68             : #if 0
      69             :         snap::file_contents f("test.json");
      70             :         f.contents(valid_json);
      71             :         f.write_all();
      72             : #endif
      73             : 
      74           2 :         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             :     }
     157             :     CATCH_END_SECTION()
     158             : 
     159           8 :     CATCH_START_SECTION("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           2 :         );
     171             : 
     172           2 :         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             :     }
     196             :     CATCH_END_SECTION()
     197             : 
     198           8 :     CATCH_START_SECTION("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           2 :         );
     212             : 
     213           2 :         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             :     }
     267             :     CATCH_END_SECTION()
     268             : 
     269           8 :     CATCH_START_SECTION("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     2224126 :             std::stringstream ss;
     279     2224126 :             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     2224126 :             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     2224126 :             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             :         }
     307             :     }
     308             :     CATCH_END_SECTION()
     309           4 : }
     310             : #pragma GCC diagnostic pop
     311             : 
     312             : 
     313          22 : CATCH_TEST_CASE("json_tokens_invalid", "[json][iterator][invalid]")
     314             : {
     315          40 :     CATCH_START_SECTION("invalid JSON negative number")
     316             :     {
     317           1 :         std::string valid_json(
     318             :             "-a"
     319           2 :         );
     320             : 
     321           2 :         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             :     }
     329             :     CATCH_END_SECTION()
     330             : 
     331          40 :     CATCH_START_SECTION("invalid JSON number with fraction")
     332             :     {
     333           1 :         std::string valid_json(
     334             :             "-3., 2."
     335           2 :         );
     336             : 
     337           2 :         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             :     }
     345             :     CATCH_END_SECTION()
     346             : 
     347          40 :     CATCH_START_SECTION("invalid JSON number exponent")
     348             :     {
     349           1 :         std::string valid_json(
     350             :             "-3.0e+a, 2.1"
     351           2 :         );
     352             : 
     353           2 :         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             :     }
     361             :     CATCH_END_SECTION()
     362             : 
     363          40 :     CATCH_START_SECTION("invalid JSON number with fraction")
     364             :     {
     365           1 :         std::string valid_json(
     366             :             "\"back\\slash\""
     367           2 :         );
     368             : 
     369           2 :         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             :     }
     377             :     CATCH_END_SECTION()
     378             : 
     379          40 :     CATCH_START_SECTION("unsupported JSON backslash character")
     380             :     {
     381             :         {
     382           1 :             std::string valid_json(
     383             :                 "\"back\\slash\""
     384           2 :             );
     385             : 
     386           2 :             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             :         }
     394             : 
     395             :     }
     396             :     CATCH_END_SECTION()
     397             : 
     398          40 :     CATCH_START_SECTION("invalid JSON unicode: too short")
     399             :     {
     400             :         {
     401           1 :             std::string valid_json(
     402             :                 "\"\\u0"
     403           2 :             );
     404             : 
     405           2 :             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             :         }
     413             : 
     414             :         {
     415           1 :             std::string valid_json(
     416             :                 "\"\\u20"
     417           2 :             );
     418             : 
     419           2 :             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             :         }
     427             : 
     428             :         {
     429           1 :             std::string valid_json(
     430             :                 "\"\\u301"
     431           2 :             );
     432             : 
     433           2 :             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             :         }
     441             : 
     442             :     }
     443             :     CATCH_END_SECTION()
     444             : 
     445          40 :     CATCH_START_SECTION("invalid JSON unicode: low surrogate missing backslash")
     446             :     {
     447           1 :         std::string valid_json(
     448             :             "\"\\uD91Fmissing\""
     449           2 :         );
     450             : 
     451           2 :         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             :     }
     459             :     CATCH_END_SECTION()
     460             : 
     461          40 :     CATCH_START_SECTION("invalid JSON unicode: low surrogate missing 'u'")
     462             :     {
     463           1 :         std::string valid_json(
     464             :             "\"\\uD91F\\missing\""
     465           2 :         );
     466             : 
     467           2 :         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             :     }
     475             :     CATCH_END_SECTION()
     476             : 
     477          40 :     CATCH_START_SECTION("invalid JSON unicode: low surrogate expected")
     478             :     {
     479             :         {
     480           1 :             std::string valid_json(
     481             :                 "\"\\uD91F\\u0010\""
     482           2 :             );
     483             : 
     484           2 :             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             :         }
     492             : 
     493             :         {
     494           1 :             std::string valid_json(
     495             :                 "\"\\uD91F\\uDBFF\""
     496           2 :             );
     497             : 
     498           2 :             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             :         }
     506             : 
     507             :         {
     508           1 :             std::string valid_json(
     509             :                 "\"\\uD91F\\uE030"
     510           2 :             );
     511             : 
     512           2 :             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             :         }
     520             :     }
     521             :     CATCH_END_SECTION()
     522             : 
     523          40 :     CATCH_START_SECTION("invalid JSON unicode: low surrogate too short")
     524             :     {
     525             :         {
     526           1 :             std::string valid_json(
     527             :                 "\"\\uD91F\\u0"
     528           2 :             );
     529             : 
     530           2 :             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             :         }
     538             : 
     539             :         {
     540           1 :             std::string valid_json(
     541             :                 "\"\\uD91F\\u0f"
     542           2 :             );
     543             : 
     544           2 :             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             :         }
     552             : 
     553             :         {
     554           1 :             std::string valid_json(
     555             :                 "\"\\uD91F\\u0fa"
     556           2 :             );
     557             : 
     558           2 :             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             :         }
     566             :     }
     567             :     CATCH_END_SECTION()
     568             : 
     569          40 :     CATCH_START_SECTION("invalid JSON unicode: invalid hexadecimal digit (low surrogate)")
     570             :     {
     571             :         {
     572           1 :             std::string valid_json(
     573             :                 "\"\\udb31\\u0t\""
     574           2 :             );
     575             : 
     576           2 :             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             :         }
     584             : 
     585             :         {
     586           1 :             std::string valid_json(
     587             :                 "\"\\udb31\\u3eg\""
     588           2 :             );
     589             : 
     590           2 :             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             :         }
     598             : 
     599             :         {
     600           1 :             std::string valid_json(
     601             :                 "\"\\udb31\\ua3e!\""
     602           2 :             );
     603             : 
     604           2 :             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             :         }
     612             :     }
     613             :     CATCH_END_SECTION()
     614             : 
     615          40 :     CATCH_START_SECTION("invalid JSON unicode: low surrogate first")
     616             :     {
     617        1025 :         for(char32_t c(0xDC00); c <= 0xDFFF; ++c)
     618             :         {
     619        2048 :             std::stringstream ss;
     620        1024 :             ss << "\\u" << std::hex << std::setw(4)
     621        1024 :                 << std::setfill('0') << static_cast<int>(c);
     622             : 
     623        2048 :             libutf8::json_tokens jt("\"" + ss.str() + "\"");
     624        1024 :             CATCH_REQUIRE(jt.line() == 0);
     625        1024 :             CATCH_REQUIRE(jt.column() == 0);
     626        1024 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     627        1024 :             CATCH_REQUIRE(jt.line() == 1);
     628        1024 :             CATCH_REQUIRE(jt.column() == 1);
     629        3072 :             std::string const msg("low surrogate " + ss.str()
     630        3072 :                                     + " found before a high surrogate");
     631        1024 :             CATCH_REQUIRE(jt.error() == msg);
     632             :         }
     633             : 
     634             :     }
     635             :     CATCH_END_SECTION()
     636             : 
     637          40 :     CATCH_START_SECTION("invalid JSON unicode: invalid hexadecimal digit")
     638             :     {
     639             :         {
     640           1 :             std::string valid_json(
     641             :                 "\"\\u5z"
     642           2 :             );
     643             : 
     644           2 :             libutf8::json_tokens jt(valid_json);
     645           1 :             CATCH_REQUIRE(jt.line() == 0);
     646           1 :             CATCH_REQUIRE(jt.column() == 0);
     647           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     648           1 :             CATCH_REQUIRE(jt.line() == 1);
     649           1 :             CATCH_REQUIRE(jt.column() == 1);
     650           1 :             CATCH_REQUIRE(jt.error() == "invalid unicode character: 'z'");
     651             :         }
     652             : 
     653             :         {
     654           1 :             std::string valid_json(
     655             :                 "\"\\uaa$"
     656           2 :             );
     657             : 
     658           2 :             libutf8::json_tokens jt(valid_json);
     659           1 :             CATCH_REQUIRE(jt.line() == 0);
     660           1 :             CATCH_REQUIRE(jt.column() == 0);
     661           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     662           1 :             CATCH_REQUIRE(jt.line() == 1);
     663           1 :             CATCH_REQUIRE(jt.column() == 1);
     664           1 :             CATCH_REQUIRE(jt.error() == "invalid unicode character: '$'");
     665             :         }
     666             : 
     667             :         {
     668           1 :             std::string valid_json(
     669             :                 "\"\\ua9a\001"
     670           2 :             );
     671             : 
     672           2 :             libutf8::json_tokens jt(valid_json);
     673           1 :             CATCH_REQUIRE(jt.line() == 0);
     674           1 :             CATCH_REQUIRE(jt.column() == 0);
     675           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     676           1 :             CATCH_REQUIRE(jt.line() == 1);
     677           1 :             CATCH_REQUIRE(jt.column() == 1);
     678           1 :             CATCH_REQUIRE(jt.error() == "invalid unicode character: '^A'");
     679             :         }
     680             :     }
     681             :     CATCH_END_SECTION()
     682             : 
     683          40 :     CATCH_START_SECTION("unterminated JSON string")
     684             :     {
     685           2 :         libutf8::json_tokens jt("\"unterminated");
     686           1 :         CATCH_REQUIRE(jt.line() == 0);
     687           1 :         CATCH_REQUIRE(jt.column() == 0);
     688           1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     689           1 :         CATCH_REQUIRE(jt.line() == 1);
     690           1 :         CATCH_REQUIRE(jt.column() == 1);
     691           1 :         CATCH_REQUIRE(jt.error() == "unclosed string");
     692             : 
     693             :     }
     694             :     CATCH_END_SECTION()
     695             : 
     696          40 :     CATCH_START_SECTION("JSON true with missing characters")
     697             :     {
     698             :         {
     699           2 :             libutf8::json_tokens jt("{\"bad-true\":tru}");
     700           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     701           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     702           1 :             CATCH_REQUIRE(jt.string() == "bad-true");
     703           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     704           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     705           1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 't'");
     706           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     707           1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'r'");
     708             :         }
     709             : 
     710             :         {
     711           2 :             libutf8::json_tokens jt("{\"bad-true\":tr}");
     712           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     713           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     714           1 :             CATCH_REQUIRE(jt.string() == "bad-true");
     715           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     716           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     717           1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 't'");
     718           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     719           1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'r'");
     720             :         }
     721             : 
     722             :         {
     723           2 :             libutf8::json_tokens jt("{\"bad-true\":t}");
     724           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     725           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     726           1 :             CATCH_REQUIRE(jt.string() == "bad-true");
     727           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     728           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     729           1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 't'");
     730           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_CLOSE_OBJECT);
     731             :         }
     732             :     }
     733             :     CATCH_END_SECTION()
     734             : 
     735          40 :     CATCH_START_SECTION("JSON false with missing characters")
     736             :     {
     737             :         {
     738           2 :             libutf8::json_tokens jt("{\"bad-false\":fals}");
     739           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     740           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     741           1 :             CATCH_REQUIRE(jt.string() == "bad-false");
     742           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     743           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     744           1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'f'");
     745           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     746           1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'a'");
     747             :         }
     748             : 
     749             :         {
     750           2 :             libutf8::json_tokens jt("{\"bad-false\":fal}");
     751           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     752           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     753           1 :             CATCH_REQUIRE(jt.string() == "bad-false");
     754           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     755           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     756           1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'f'");
     757           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     758           1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'a'");
     759             :         }
     760             : 
     761             :         {
     762           2 :             libutf8::json_tokens jt("{\"bad-false\":fa}");
     763           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     764           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     765           1 :             CATCH_REQUIRE(jt.string() == "bad-false");
     766           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     767           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     768           1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'f'");
     769           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     770           1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'a'");
     771             :         }
     772             : 
     773             :         {
     774           2 :             libutf8::json_tokens jt("{\"bad-false\":f}");
     775           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     776           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     777           1 :             CATCH_REQUIRE(jt.string() == "bad-false");
     778           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     779           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     780           1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'f'");
     781           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_CLOSE_OBJECT);
     782             :         }
     783             :     }
     784             :     CATCH_END_SECTION()
     785             : 
     786          40 :     CATCH_START_SECTION("JSON null with missing characters")
     787             :     {
     788             :         {
     789           2 :             libutf8::json_tokens jt("{\"bad-null\":nul}");
     790           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     791           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     792           1 :             CATCH_REQUIRE(jt.string() == "bad-null");
     793           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     794           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     795           1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'n'");
     796           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     797           1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'u'");
     798             :         }
     799             : 
     800             :         {
     801           2 :             libutf8::json_tokens jt("{\"bad-null\":nu}");
     802           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     803           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     804           1 :             CATCH_REQUIRE(jt.string() == "bad-null");
     805           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     806           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     807           1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'n'");
     808           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     809           1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'u'");
     810             :         }
     811             : 
     812             :         {
     813           2 :             libutf8::json_tokens jt("{\"bad-null\":n}");
     814           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_OPEN_OBJECT);
     815           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_STRING);
     816           1 :             CATCH_REQUIRE(jt.string() == "bad-null");
     817           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_COLON);
     818           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     819           1 :             CATCH_REQUIRE(jt.error() == "found unexpected character: 'n'");
     820           1 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_CLOSE_OBJECT);
     821             :         }
     822             :     }
     823             :     CATCH_END_SECTION()
     824             : 
     825          40 :     CATCH_START_SECTION("unexpected JSON characters")
     826             :     {
     827     1114112 :         for(char32_t c(1); c < 0x110000; ++c)
     828             :         {
     829     1114111 :             if(c >= 0xD800 && c <= 0xDFFF)
     830             :             {
     831        2048 :                 continue;
     832             :             }
     833     1112087 :             switch(c)
     834             :             {
     835          24 :             case '"':
     836             :             case '{':
     837             :             case '}':
     838             :             case '[':
     839             :             case ']':
     840             :             case '0':
     841             :             case '1':
     842             :             case '2':
     843             :             case '3':
     844             :             case '4':
     845             :             case '5':
     846             :             case '6':
     847             :             case '7':
     848             :             case '8':
     849             :             case '9':
     850             :             case ' ':
     851             :             case '\t':
     852             :             case '\r':
     853             :             case '\n':
     854             :             case 'n':
     855             :             case 't':
     856             :             case 'f':
     857             :             case ',':
     858             :             case ':':
     859          24 :                 continue;
     860             : 
     861             :             }
     862             : 
     863     2224078 :             std::string invalid_json(libutf8::to_u8string(c));
     864     2224078 :             libutf8::json_tokens jt(invalid_json);
     865             : 
     866     1112039 :             CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     867     1112039 :             if(c < 0x20)
     868             :             {
     869          28 :                 CATCH_REQUIRE(jt.error() == "found unexpected character: '^" + libutf8::to_u8string(static_cast<char32_t>(c + 0x40)) + "'");
     870             :             }
     871     1112011 :             else if(c >= 0x80 && c < 0xA0)
     872             :             {
     873          32 :                 CATCH_REQUIRE(jt.error() == "found unexpected character: '@" + libutf8::to_u8string(static_cast<char32_t>(c - 0x40)) + "'");
     874             :             }
     875             :             else
     876             :             {
     877     1111979 :                 CATCH_REQUIRE(jt.error() == "found unexpected character: '" + libutf8::to_u8string(c) + "'");
     878             :             }
     879             :         }
     880             :     }
     881             :     CATCH_END_SECTION()
     882             : 
     883          40 :     CATCH_START_SECTION("unexpected '\\0' in JSON")
     884             :     {
     885           2 :         std::string invalid_json;
     886           1 :         invalid_json += '\0';
     887           2 :         libutf8::json_tokens jt(invalid_json);
     888             : 
     889           1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     890           1 :         CATCH_REQUIRE(jt.error() == "found unexpected NULL character");
     891             :     }
     892             :     CATCH_END_SECTION()
     893             : 
     894          40 :     CATCH_START_SECTION("unexpected '\\0' in JSON string")
     895             :     {
     896           2 :         std::string invalid_json("\"string");
     897           1 :         invalid_json += '\0';
     898           1 :         invalid_json += "with null\"";
     899           2 :         libutf8::json_tokens jt(invalid_json);
     900             : 
     901           1 :         CATCH_REQUIRE(jt.next_token() == libutf8::token_t::TOKEN_ERROR);
     902           1 :         CATCH_REQUIRE(jt.error() == "unexpected NULL character in string");
     903             :     }
     904             :     CATCH_END_SECTION()
     905          26 : }
     906             : 
     907             : 
     908             : 
     909             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13