Unverified Commit 56ac7908 by Niels Lohmann

Merge branch 'feature/manual_lexer' into develop

parents ecf895f2 dbcb032f
......@@ -30,3 +30,6 @@ test/parse_afl_fuzzer
test/parse_cbor_fuzzer
test/parse_msgpack_fuzzer
minibench
.PHONY: pretty clean ChangeLog.md
# used programs
RE2C := $(shell command -v re2c 2> /dev/null)
SED = sed
# main target
all:
$(MAKE) -C test
......@@ -51,7 +47,8 @@ doctest:
# -Wno-keyword-macro: unit-tests use "#define private public"
# -Wno-deprecated-declarations: the library deprecated some functions
# -Wno-weak-vtables: exception class is defined inline, but has virtual method
# -Wno-range-loop-analysis: iterator_wrapper tests tests "for(const auto i...)"
# -Wno-range-loop-analysis: iterator_wrapper tests "for(const auto i...)"
# -Wno-float-equal: not all comparisons in the tests can be replaced by Approx
pedantic_clang:
$(MAKE) json_unit CXXFLAGS="\
-std=c++11 \
......@@ -62,7 +59,8 @@ pedantic_clang:
-Wno-keyword-macro \
-Wno-deprecated-declarations \
-Wno-weak-vtables \
-Wno-range-loop-analysis"
-Wno-range-loop-analysis \
-Wno-float-equal"
# calling GCC with most warnings
pedantic_gcc:
......@@ -186,13 +184,6 @@ clang_sanitize: clean
# maintainer targets
##########################################################################
# create scanner with re2c
re2c: src/json.hpp.re2c
ifndef RE2C
$(error "re2c is not available, please install re2c")
endif
$(RE2C) -W --utf-8 --encoding-policy fail --bit-vectors --nested-ifs --no-debug-info $< | $(SED) '1d' > src/json.hpp
# pretty printer
pretty:
astyle --style=allman --indent=spaces=4 --indent-modifiers \
......@@ -200,7 +191,7 @@ pretty:
--indent-col1-comments --pad-oper --pad-header --align-pointer=type \
--align-reference=type --add-brackets --convert-tabs --close-templates \
--lineend=linux --preserve-date --suffix=none --formatted \
src/json.hpp src/json.hpp.re2c test/src/*.cpp \
src/json.hpp test/src/*.cpp \
benchmarks/benchmarks.cpp doc/examples/*.cpp
......
......@@ -899,7 +899,7 @@ $ make json_unit -Ctest
$ ./test/json_unit "*"
===============================================================================
All tests passed (11203022 assertions in 48 test cases)
All tests passed (13391115 assertions in 49 test cases)
```
Alternatively, you can use [CMake](https://cmake.org) and run
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -28,7 +28,6 @@ SOFTWARE.
#include "catch.hpp"
#define private public
#include "json.hpp"
using nlohmann::json;
......@@ -728,14 +727,9 @@ TEST_CASE("CBOR")
const auto result = json::to_cbor(j);
CHECK(result == expected);
// restore value (reverse array for endianess)
double restored;
std::reverse(expected.begin(), expected.end());
memcpy(&restored, expected.data(), sizeof(double));
CHECK(restored == v);
// roundtrip
CHECK(json::from_cbor(result) == j);
CHECK(json::from_cbor(result) == v);
}
}
......@@ -1166,35 +1160,35 @@ TEST_CASE("CBOR")
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error);
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x18})),
"[json.exception.parse_error.110] parse error at 2: cannot read 1 bytes from vector");
"[json.exception.parse_error.110] parse error at 2: unexpected end of input");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x19})),
"[json.exception.parse_error.110] parse error at 2: cannot read 2 bytes from vector");
"[json.exception.parse_error.110] parse error at 2: unexpected end of input");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x19, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 2 bytes from vector");
"[json.exception.parse_error.110] parse error at 3: unexpected end of input");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1a})),
"[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector");
"[json.exception.parse_error.110] parse error at 2: unexpected end of input");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1a, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector");
"[json.exception.parse_error.110] parse error at 3: unexpected end of input");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1a, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector");
"[json.exception.parse_error.110] parse error at 4: unexpected end of input");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1a, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector");
"[json.exception.parse_error.110] parse error at 5: unexpected end of input");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1b})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
"[json.exception.parse_error.110] parse error at 2: unexpected end of input");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
"[json.exception.parse_error.110] parse error at 3: unexpected end of input");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
"[json.exception.parse_error.110] parse error at 4: unexpected end of input");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
"[json.exception.parse_error.110] parse error at 5: unexpected end of input");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
"[json.exception.parse_error.110] parse error at 6: unexpected end of input");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
"[json.exception.parse_error.110] parse error at 7: unexpected end of input");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
"[json.exception.parse_error.110] parse error at 8: unexpected end of input");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
"[json.exception.parse_error.110] parse error at 9: unexpected end of input");
}
SECTION("unsupported bytes")
......@@ -1357,12 +1351,6 @@ TEST_CASE("CBOR regressions", "[!throws]")
}
}
}
SECTION("improve code coverage")
{
// exotic edge case
CHECK_THROWS_AS(json::check_length(0xffffffffffffffffull, 0xfffffffffffffff0ull, 0xff), json::parse_error);
}
}
TEST_CASE("CBOR roundtrips", "[hide]")
......@@ -1756,7 +1744,7 @@ TEST_CASE("examples from RFC 7049 Appendix A")
CHECK(json::parse("\"\\ud800\\udd51\"") == json::from_cbor(std::vector<uint8_t>({0x64, 0xf0, 0x90, 0x85, 0x91})));
// indefinite length strings
CHECK(json::parse("\"streaming\"") == json::from_cbor(std::vector<uint8_t>({0x7f, 0x65, 0x73, 0x74, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x67, 0xff})));
CHECK(json::parse("\"streaming\"") == json::from_cbor(std::vector<uint8_t>({0x7f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0xff})));
}
SECTION("arrays")
......
......@@ -32,106 +32,84 @@ SOFTWARE.
#include "json.hpp"
using nlohmann::json;
// shortcut to scan a string literal
json::lexer::token_type scan_string(const char* s);
json::lexer::token_type scan_string(const char* s)
{
return json::lexer(json::input_adapter::create(s)).scan();
}
TEST_CASE("lexer class")
{
SECTION("scan")
{
SECTION("structural characters")
{
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("["),
1).scan() == json::lexer::token_type::begin_array));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("]"),
1).scan() == json::lexer::token_type::end_array));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("{"),
1).scan() == json::lexer::token_type::begin_object));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("}"),
1).scan() == json::lexer::token_type::end_object));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(","),
1).scan() == json::lexer::token_type::value_separator));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(":"),
1).scan() == json::lexer::token_type::name_separator));
CHECK((scan_string("[") == json::lexer::token_type::begin_array));
CHECK((scan_string("]") == json::lexer::token_type::end_array));
CHECK((scan_string("{") == json::lexer::token_type::begin_object));
CHECK((scan_string("}") == json::lexer::token_type::end_object));
CHECK((scan_string(",") == json::lexer::token_type::value_separator));
CHECK((scan_string(":") == json::lexer::token_type::name_separator));
}
SECTION("literal names")
{
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("null"),
4).scan() == json::lexer::token_type::literal_null));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("true"),
4).scan() == json::lexer::token_type::literal_true));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("false"),
5).scan() == json::lexer::token_type::literal_false));
CHECK((scan_string("null") == json::lexer::token_type::literal_null));
CHECK((scan_string("true") == json::lexer::token_type::literal_true));
CHECK((scan_string("false") == json::lexer::token_type::literal_false));
}
SECTION("numbers")
{
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("0"),
1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("1"),
1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("2"),
1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("3"),
1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("4"),
1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("5"),
1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("6"),
1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("7"),
1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("8"),
1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("9"),
1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("-0"),
2).scan() == json::lexer::token_type::value_integer));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("-1"),
2).scan() == json::lexer::token_type::value_integer));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("1.1"),
3).scan() == json::lexer::token_type::value_float));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("-1.1"),
4).scan() == json::lexer::token_type::value_float));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("1E10"),
4).scan() == json::lexer::token_type::value_float));
CHECK((scan_string("0") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("1") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("2") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("3") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("4") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("5") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("6") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("7") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("8") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("9") == json::lexer::token_type::value_unsigned));
CHECK((scan_string("-0") == json::lexer::token_type::value_integer));
CHECK((scan_string("-1") == json::lexer::token_type::value_integer));
CHECK((scan_string("1.1") == json::lexer::token_type::value_float));
CHECK((scan_string("-1.1") == json::lexer::token_type::value_float));
CHECK((scan_string("1E10") == json::lexer::token_type::value_float));
}
SECTION("whitespace")
{
// result is end_of_input, because not token is following
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(" "),
1).scan() == json::lexer::token_type::end_of_input));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\t"),
1).scan() == json::lexer::token_type::end_of_input));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\n"),
1).scan() == json::lexer::token_type::end_of_input));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\r"),
1).scan() == json::lexer::token_type::end_of_input));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(" \t\n\r\n\t "),
7).scan() == json::lexer::token_type::end_of_input));
CHECK((scan_string(" ") == json::lexer::token_type::end_of_input));
CHECK((scan_string("\t") == json::lexer::token_type::end_of_input));
CHECK((scan_string("\n") == json::lexer::token_type::end_of_input));
CHECK((scan_string("\r") == json::lexer::token_type::end_of_input));
CHECK((scan_string(" \t\n\r\n\t ") == json::lexer::token_type::end_of_input));
}
}
SECTION("token_type_name")
{
CHECK((json::lexer::token_type_name(json::lexer::token_type::uninitialized) == "<uninitialized>"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::literal_true) == "true literal"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::literal_false) == "false literal"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::literal_null) == "null literal"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::value_string) == "string literal"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::value_unsigned) == "number literal"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::value_integer) == "number literal"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::value_float) == "number literal"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::begin_array) == "'['"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::begin_object) == "'{'"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::end_array) == "']'"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::end_object) == "'}'"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::name_separator) == "':'"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::value_separator) == "','"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::parse_error) == "<parse error>"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::end_of_input) == "end of input"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::uninitialized)) == "<uninitialized>"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::literal_true)) == "true literal"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::literal_false)) == "false literal"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::literal_null)) == "null literal"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_string)) == "string literal"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_unsigned)) == "number literal"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_integer)) == "number literal"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_float)) == "number literal"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::begin_array)) == "'['"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::begin_object)) == "'{'"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::end_array)) == "']'"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::end_object)) == "'}'"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::name_separator)) == "':'"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_separator)) == "','"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::parse_error)) == "<parse error>"));
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::end_of_input)) == "end of input"));
}
SECTION("parse errors on first character")
......@@ -141,8 +119,7 @@ TEST_CASE("lexer class")
// create string from the ASCII code
const auto s = std::string(1, static_cast<char>(c));
// store scan() result
const auto res = json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(s.c_str()),
1).scan();
const auto res = scan_string(s.c_str());
switch (c)
{
......@@ -188,12 +165,23 @@ TEST_CASE("lexer class")
}
}
SECTION("very large string")
{
// strings larger than 1024 bytes yield a resize of the lexer's yytext buffer
std::string s("\"");
s += std::string(2048, 'x');
s += "\"";
CHECK((scan_string(s.c_str()) == json::lexer::token_type::value_string));
}
/* NOTE: to_unicode function has been removed
SECTION("to_unicode")
{
// lexer to call to_unicode on
json::lexer dummy_lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(""), 0);
json::lexer dummy_lexer("", 0);
CHECK(dummy_lexer.to_unicode(0x1F4A9) == "💩");
CHECK_THROWS_AS(dummy_lexer.to_unicode(0x200000), json::parse_error);
CHECK_THROWS_WITH(dummy_lexer.to_unicode(0x200000), "[json.exception.parse_error.103] parse error: code points above 0x10FFFF are invalid");
}
*/
}
......@@ -34,36 +34,43 @@ using nlohmann::json;
#include <valarray>
// shortcut to parse a string literal
json::parser parse_string(const char* s);
json::parser parse_string(const char* s)
{
return json::parser(json::input_adapter::create(s));
}
TEST_CASE("parser class")
{
SECTION("parse")
{
SECTION("null")
{
CHECK(json::parser("null").parse() == json(nullptr));
CHECK(parse_string("null").parse() == json(nullptr));
}
SECTION("true")
{
CHECK(json::parser("true").parse() == json(true));
CHECK(parse_string("true").parse() == json(true));
}
SECTION("false")
{
CHECK(json::parser("false").parse() == json(false));
CHECK(parse_string("false").parse() == json(false));
}
SECTION("array")
{
SECTION("empty array")
{
CHECK(json::parser("[]").parse() == json(json::value_t::array));
CHECK(json::parser("[ ]").parse() == json(json::value_t::array));
CHECK(parse_string("[]").parse() == json(json::value_t::array));
CHECK(parse_string("[ ]").parse() == json(json::value_t::array));
}
SECTION("nonempty array")
{
CHECK(json::parser("[true, false, null]").parse() == json({true, false, nullptr}));
CHECK(parse_string("[true, false, null]").parse() == json({true, false, nullptr}));
}
}
......@@ -71,113 +78,113 @@ TEST_CASE("parser class")
{
SECTION("empty object")
{
CHECK(json::parser("{}").parse() == json(json::value_t::object));
CHECK(json::parser("{ }").parse() == json(json::value_t::object));
CHECK(parse_string("{}").parse() == json(json::value_t::object));
CHECK(parse_string("{ }").parse() == json(json::value_t::object));
}
SECTION("nonempty object")
{
CHECK(json::parser("{\"\": true, \"one\": 1, \"two\": null}").parse() == json({{"", true}, {"one", 1}, {"two", nullptr}}));
CHECK(parse_string("{\"\": true, \"one\": 1, \"two\": null}").parse() == json({{"", true}, {"one", 1}, {"two", nullptr}}));
}
}
SECTION("string")
{
// empty string
CHECK(json::parser("\"\"").parse() == json(json::value_t::string));
CHECK(parse_string("\"\"").parse() == json(json::value_t::string));
SECTION("errors")
{
// error: tab in string
CHECK_THROWS_AS(json::parser("\"\t\"").parse(), json::parse_error);
CHECK_THROWS_WITH(json::parser("\"\t\"").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CHECK_THROWS_AS(parse_string("\"\t\"").parse(), json::parse_error);
CHECK_THROWS_WITH(parse_string("\"\t\"").parse(),
"[json.exception.parse_error.101] parse error at 2: syntax error - invalid string: control character U+0009 must be escaped; last read '\"<U+0009>'");
// error: newline in string
CHECK_THROWS_AS(json::parser("\"\n\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\r\"").parse(), json::parse_error);
CHECK_THROWS_WITH(json::parser("\"\n\"").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser("\"\r\"").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CHECK_THROWS_AS(parse_string("\"\n\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\r\"").parse(), json::parse_error);
CHECK_THROWS_WITH(parse_string("\"\n\"").parse(),
"[json.exception.parse_error.101] parse error at 2: syntax error - invalid string: control character U+000A must be escaped; last read '\"<U+000A>'");
CHECK_THROWS_WITH(parse_string("\"\r\"").parse(),
"[json.exception.parse_error.101] parse error at 2: syntax error - invalid string: control character U+000D must be escaped; last read '\"<U+000D>'");
// error: backspace in string
CHECK_THROWS_AS(json::parser("\"\b\"").parse(), json::parse_error);
CHECK_THROWS_WITH(json::parser("\"\b\"").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CHECK_THROWS_AS(parse_string("\"\b\"").parse(), json::parse_error);
CHECK_THROWS_WITH(parse_string("\"\b\"").parse(),
"[json.exception.parse_error.101] parse error at 2: syntax error - invalid string: control character U+0008 must be escaped; last read '\"<U+0008>'");
// improve code coverage
CHECK_THROWS_AS(json::parser("\uFF01").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("[-4:1,]").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\uFF01").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("[-4:1,]").parse(), json::parse_error);
// unescaped control characters
CHECK_THROWS_AS(json::parser("\"\x00\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x01\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x02\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x03\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x04\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x05\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x06\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x07\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x08\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x09\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x0a\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x0b\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x0c\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x0d\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x0e\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x0f\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x10\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x11\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x12\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x13\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x14\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x15\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x16\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x17\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x18\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x19\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x1a\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x1b\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x1c\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x1d\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x1e\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\x1f\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x00\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x01\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x02\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x03\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x04\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x05\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x06\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x07\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x08\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x09\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x0a\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x0b\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x0c\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x0d\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x0e\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x0f\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x10\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x11\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x12\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x13\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x14\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x15\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x16\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x17\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x18\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x19\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x1a\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x1b\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x1c\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x1d\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x1e\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\x1f\"").parse(), json::parse_error);
}
SECTION("escaped")
{
// quotation mark "\""
auto r1 = R"("\"")"_json;
CHECK(json::parser("\"\\\"\"").parse() == r1);
CHECK(parse_string("\"\\\"\"").parse() == r1);
// reverse solidus "\\"
auto r2 = R"("\\")"_json;
CHECK(json::parser("\"\\\\\"").parse() == r2);
CHECK(parse_string("\"\\\\\"").parse() == r2);
// solidus
CHECK(json::parser("\"\\/\"").parse() == R"("/")"_json);
CHECK(parse_string("\"\\/\"").parse() == R"("/")"_json);
// backspace
CHECK(json::parser("\"\\b\"").parse() == json("\b"));
CHECK(parse_string("\"\\b\"").parse() == json("\b"));
// formfeed
CHECK(json::parser("\"\\f\"").parse() == json("\f"));
CHECK(parse_string("\"\\f\"").parse() == json("\f"));
// newline
CHECK(json::parser("\"\\n\"").parse() == json("\n"));
CHECK(parse_string("\"\\n\"").parse() == json("\n"));
// carriage return
CHECK(json::parser("\"\\r\"").parse() == json("\r"));
CHECK(parse_string("\"\\r\"").parse() == json("\r"));
// horizontal tab
CHECK(json::parser("\"\\t\"").parse() == json("\t"));
CHECK(json::parser("\"\\u0001\"").parse().get<json::string_t>() == "\x01");
CHECK(json::parser("\"\\u000a\"").parse().get<json::string_t>() == "\n");
CHECK(json::parser("\"\\u00b0\"").parse().get<json::string_t>() == "°");
CHECK(json::parser("\"\\u0c00\"").parse().get<json::string_t>() == "ఀ");
CHECK(json::parser("\"\\ud000\"").parse().get<json::string_t>() == "퀀");
CHECK(json::parser("\"\\u000E\"").parse().get<json::string_t>() == "\x0E");
CHECK(json::parser("\"\\u00F0\"").parse().get<json::string_t>() == "ð");
CHECK(json::parser("\"\\u0100\"").parse().get<json::string_t>() == "Ā");
CHECK(json::parser("\"\\u2000\"").parse().get<json::string_t>() == " ");
CHECK(json::parser("\"\\uFFFF\"").parse().get<json::string_t>() == "￿");
CHECK(json::parser("\"\\u20AC\"").parse().get<json::string_t>() == "€");
CHECK(json::parser("\"\"").parse().get<json::string_t>() == "€");
CHECK(json::parser("\"🎈\"").parse().get<json::string_t>() == "🎈");
CHECK(json::parse("\"\\ud80c\\udc60\"").get<json::string_t>() == u8"\U00013060");
CHECK(json::parse("\"\\ud83c\\udf1e\"").get<json::string_t>() == "🌞");
CHECK(parse_string("\"\\t\"").parse() == json("\t"));
CHECK(parse_string("\"\\u0001\"").parse().get<json::string_t>() == "\x01");
CHECK(parse_string("\"\\u000a\"").parse().get<json::string_t>() == "\n");
CHECK(parse_string("\"\\u00b0\"").parse().get<json::string_t>() == "°");
CHECK(parse_string("\"\\u0c00\"").parse().get<json::string_t>() == "ఀ");
CHECK(parse_string("\"\\ud000\"").parse().get<json::string_t>() == "퀀");
CHECK(parse_string("\"\\u000E\"").parse().get<json::string_t>() == "\x0E");
CHECK(parse_string("\"\\u00F0\"").parse().get<json::string_t>() == "ð");
CHECK(parse_string("\"\\u0100\"").parse().get<json::string_t>() == "Ā");
CHECK(parse_string("\"\\u2000\"").parse().get<json::string_t>() == " ");
CHECK(parse_string("\"\\uFFFF\"").parse().get<json::string_t>() == "￿");
CHECK(parse_string("\"\\u20AC\"").parse().get<json::string_t>() == "€");
CHECK(parse_string("\"\"").parse().get<json::string_t>() == "€");
CHECK(parse_string("\"🎈\"").parse().get<json::string_t>() == "🎈");
CHECK(parse_string("\"\\ud80c\\udc60\"").parse().get<json::string_t>() == u8"\U00013060");
CHECK(parse_string("\"\\ud83c\\udf1e\"").parse().get<json::string_t>() == "🌞");
}
}
......@@ -187,40 +194,40 @@ TEST_CASE("parser class")
{
SECTION("without exponent")
{
CHECK(json::parser("-128").parse() == json(-128));
CHECK(json::parser("-0").parse() == json(-0));
CHECK(json::parser("0").parse() == json(0));
CHECK(json::parser("128").parse() == json(128));
CHECK(parse_string("-128").parse() == json(-128));
CHECK(parse_string("-0").parse() == json(-0));
CHECK(parse_string("0").parse() == json(0));
CHECK(parse_string("128").parse() == json(128));
}
SECTION("with exponent")
{
CHECK(json::parser("0e1").parse() == json(0e1));
CHECK(json::parser("0E1").parse() == json(0e1));
CHECK(json::parser("10000E-4").parse() == json(10000e-4));
CHECK(json::parser("10000E-3").parse() == json(10000e-3));
CHECK(json::parser("10000E-2").parse() == json(10000e-2));
CHECK(json::parser("10000E-1").parse() == json(10000e-1));
CHECK(json::parser("10000E0").parse() == json(10000e0));
CHECK(json::parser("10000E1").parse() == json(10000e1));
CHECK(json::parser("10000E2").parse() == json(10000e2));
CHECK(json::parser("10000E3").parse() == json(10000e3));
CHECK(json::parser("10000E4").parse() == json(10000e4));
CHECK(json::parser("10000e-4").parse() == json(10000e-4));
CHECK(json::parser("10000e-3").parse() == json(10000e-3));
CHECK(json::parser("10000e-2").parse() == json(10000e-2));
CHECK(json::parser("10000e-1").parse() == json(10000e-1));
CHECK(json::parser("10000e0").parse() == json(10000e0));
CHECK(json::parser("10000e1").parse() == json(10000e1));
CHECK(json::parser("10000e2").parse() == json(10000e2));
CHECK(json::parser("10000e3").parse() == json(10000e3));
CHECK(json::parser("10000e4").parse() == json(10000e4));
CHECK(json::parser("-0e1").parse() == json(-0e1));
CHECK(json::parser("-0E1").parse() == json(-0e1));
CHECK(json::parser("-0E123").parse() == json(-0e123));
CHECK(parse_string("0e1").parse() == json(0e1));
CHECK(parse_string("0E1").parse() == json(0e1));
CHECK(parse_string("10000E-4").parse() == json(10000e-4));
CHECK(parse_string("10000E-3").parse() == json(10000e-3));
CHECK(parse_string("10000E-2").parse() == json(10000e-2));
CHECK(parse_string("10000E-1").parse() == json(10000e-1));
CHECK(parse_string("10000E0").parse() == json(10000e0));
CHECK(parse_string("10000E1").parse() == json(10000e1));
CHECK(parse_string("10000E2").parse() == json(10000e2));
CHECK(parse_string("10000E3").parse() == json(10000e3));
CHECK(parse_string("10000E4").parse() == json(10000e4));
CHECK(parse_string("10000e-4").parse() == json(10000e-4));
CHECK(parse_string("10000e-3").parse() == json(10000e-3));
CHECK(parse_string("10000e-2").parse() == json(10000e-2));
CHECK(parse_string("10000e-1").parse() == json(10000e-1));
CHECK(parse_string("10000e0").parse() == json(10000e0));
CHECK(parse_string("10000e1").parse() == json(10000e1));
CHECK(parse_string("10000e2").parse() == json(10000e2));
CHECK(parse_string("10000e3").parse() == json(10000e3));
CHECK(parse_string("10000e4").parse() == json(10000e4));
CHECK(parse_string("-0e1").parse() == json(-0e1));
CHECK(parse_string("-0E1").parse() == json(-0e1));
CHECK(parse_string("-0E123").parse() == json(-0e123));
}
SECTION("edge cases")
......@@ -232,9 +239,9 @@ TEST_CASE("parser class")
// agree exactly on their numeric values.
// -(2**53)+1
CHECK(json::parser("-9007199254740991").parse().get<int64_t>() == -9007199254740991);
CHECK(parse_string("-9007199254740991").parse().get<int64_t>() == -9007199254740991);
// (2**53)-1
CHECK(json::parser("9007199254740991").parse().get<int64_t>() == 9007199254740991);
CHECK(parse_string("9007199254740991").parse().get<int64_t>() == 9007199254740991);
}
SECTION("over the edge cases") // issue #178 - Integer conversion to unsigned (incorrect handling of 64 bit integers)
......@@ -247,11 +254,11 @@ TEST_CASE("parser class")
// i.e. -(2**63) -> (2**64)-1.
// -(2**63) ** Note: compilers see negative literals as negated positive numbers (hence the -1))
CHECK(json::parser("-9223372036854775808").parse().get<int64_t>() == -9223372036854775807 - 1);
CHECK(parse_string("-9223372036854775808").parse().get<int64_t>() == -9223372036854775807 - 1);
// (2**63)-1
CHECK(json::parser("9223372036854775807").parse().get<int64_t>() == 9223372036854775807);
CHECK(parse_string("9223372036854775807").parse().get<int64_t>() == 9223372036854775807);
// (2**64)-1
CHECK(json::parser("18446744073709551615").parse().get<uint64_t>() == 18446744073709551615u);
CHECK(parse_string("18446744073709551615").parse().get<uint64_t>() == 18446744073709551615u);
}
}
......@@ -259,86 +266,348 @@ TEST_CASE("parser class")
{
SECTION("without exponent")
{
CHECK(json::parser("-128.5").parse() == json(-128.5));
CHECK(json::parser("0.999").parse() == json(0.999));
CHECK(json::parser("128.5").parse() == json(128.5));
CHECK(json::parser("-0.0").parse() == json(-0.0));
CHECK(parse_string("-128.5").parse() == json(-128.5));
CHECK(parse_string("0.999").parse() == json(0.999));
CHECK(parse_string("128.5").parse() == json(128.5));
CHECK(parse_string("-0.0").parse() == json(-0.0));
}
SECTION("with exponent")
{
CHECK(json::parser("-128.5E3").parse() == json(-128.5E3));
CHECK(json::parser("-128.5E-3").parse() == json(-128.5E-3));
CHECK(json::parser("-0.0e1").parse() == json(-0.0e1));
CHECK(json::parser("-0.0E1").parse() == json(-0.0e1));
CHECK(parse_string("-128.5E3").parse() == json(-128.5E3));
CHECK(parse_string("-128.5E-3").parse() == json(-128.5E-3));
CHECK(parse_string("-0.0e1").parse() == json(-0.0e1));
CHECK(parse_string("-0.0E1").parse() == json(-0.0e1));
}
}
SECTION("overflow")
{
// overflows during parsing yield an exception
CHECK_THROWS_AS(json::parser("1.18973e+4932").parse() == json(), json::out_of_range);
CHECK_THROWS_WITH(json::parser("1.18973e+4932").parse() == json(),
CHECK_THROWS_AS(parse_string("1.18973e+4932").parse() == json(), json::out_of_range);
CHECK_THROWS_WITH(parse_string("1.18973e+4932").parse() == json(),
"[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'");
}
SECTION("invalid numbers")
{
CHECK_THROWS_AS(json::parser("01").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("--1").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("1.").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("1E").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("1E-").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("1.E1").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("-1E").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("-0E#").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("-0E-#").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("-0#").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("-0.0:").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("-0.0Z").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("-0E123:").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("-0e0-:").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("-0e-:").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("-0f").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("01").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("--1").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("1.").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("1E").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("1E-").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("1.E1").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("-1E").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("-0E#").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("-0E-#").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("-0#").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("-0.0:").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("-0.0Z").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("-0E123:").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("-0e0-:").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("-0e-:").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("-0f").parse(), json::parse_error);
// numbers must not begin with "+"
CHECK_THROWS_AS(parse_string("+1").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("+0").parse(), json::parse_error);
CHECK_THROWS_WITH(parse_string("01").parse(),
"[json.exception.parse_error.101] parse error at 2: syntax error - unexpected number literal; expected end of input");
CHECK_THROWS_WITH(parse_string("-01").parse(),
"[json.exception.parse_error.101] parse error at 3: syntax error - unexpected number literal; expected end of input");
CHECK_THROWS_WITH(parse_string("--1").parse(),
"[json.exception.parse_error.101] parse error at 2: syntax error - invalid number; expected digit after '-'; last read '--'");
CHECK_THROWS_WITH(parse_string("1.").parse(),
"[json.exception.parse_error.101] parse error at 3: syntax error - invalid number; expected digit after '.'; last read '1.'");
CHECK_THROWS_WITH(parse_string("1E").parse(),
"[json.exception.parse_error.101] parse error at 3: syntax error - invalid number; expected '+', '-', or digit after exponent; last read '1E'");
CHECK_THROWS_WITH(parse_string("1E-").parse(),
"[json.exception.parse_error.101] parse error at 4: syntax error - invalid number; expected digit after exponent sign; last read '1E-'");
CHECK_THROWS_WITH(parse_string("1.E1").parse(),
"[json.exception.parse_error.101] parse error at 3: syntax error - invalid number; expected digit after '.'; last read '1.E'");
CHECK_THROWS_WITH(parse_string("-1E").parse(),
"[json.exception.parse_error.101] parse error at 4: syntax error - invalid number; expected '+', '-', or digit after exponent; last read '-1E'");
CHECK_THROWS_WITH(parse_string("-0E#").parse(),
"[json.exception.parse_error.101] parse error at 4: syntax error - invalid number; expected '+', '-', or digit after exponent; last read '-0E#'");
CHECK_THROWS_WITH(parse_string("-0E-#").parse(),
"[json.exception.parse_error.101] parse error at 5: syntax error - invalid number; expected digit after exponent sign; last read '-0E-#'");
CHECK_THROWS_WITH(parse_string("-0#").parse(),
"[json.exception.parse_error.101] parse error at 3: syntax error - invalid literal; last read: '-0#'; expected end of input");
CHECK_THROWS_WITH(parse_string("-0.0:").parse(),
"[json.exception.parse_error.101] parse error at 5: syntax error - unexpected ':'; expected end of input");
CHECK_THROWS_WITH(parse_string("-0.0Z").parse(),
"[json.exception.parse_error.101] parse error at 5: syntax error - invalid literal; last read: '-0.0Z'; expected end of input");
CHECK_THROWS_WITH(parse_string("-0E123:").parse(),
"[json.exception.parse_error.101] parse error at 7: syntax error - unexpected ':'; expected end of input");
CHECK_THROWS_WITH(parse_string("-0e0-:").parse(),
"[json.exception.parse_error.101] parse error at 6: syntax error - invalid number; expected digit after '-'; last read: '-:'; expected end of input");
CHECK_THROWS_WITH(parse_string("-0e-:").parse(),
"[json.exception.parse_error.101] parse error at 5: syntax error - invalid number; expected digit after exponent sign; last read '-0e-:'");
CHECK_THROWS_WITH(parse_string("-0f").parse(),
"[json.exception.parse_error.101] parse error at 4: syntax error - invalid literal; expected 'false'; last read: '-0f'; expected end of input");
}
}
}
SECTION("accept")
{
SECTION("null")
{
CHECK(parse_string("null").accept());
}
SECTION("true")
{
CHECK(parse_string("true").accept());
}
SECTION("false")
{
CHECK(parse_string("false").accept());
}
SECTION("array")
{
SECTION("empty array")
{
CHECK(parse_string("[]").accept());
CHECK(parse_string("[ ]").accept());
}
SECTION("nonempty array")
{
CHECK(parse_string("[true, false, null]").accept());
}
}
SECTION("object")
{
SECTION("empty object")
{
CHECK(parse_string("{}").accept());
CHECK(parse_string("{ }").accept());
}
SECTION("nonempty object")
{
CHECK(parse_string("{\"\": true, \"one\": 1, \"two\": null}").accept());
}
}
SECTION("string")
{
// empty string
CHECK(parse_string("\"\"").accept());
SECTION("errors")
{
// error: tab in string
CHECK(parse_string("\"\t\"").accept() == false);
// error: newline in string
CHECK(parse_string("\"\n\"").accept() == false);
CHECK(parse_string("\"\r\"").accept() == false);
// error: backspace in string
CHECK(parse_string("\"\b\"").accept() == false);
// improve code coverage
CHECK(parse_string("\uFF01").accept() == false);
CHECK(parse_string("[-4:1,]").accept() == false);
// unescaped control characters
CHECK(parse_string("\"\x00\"").accept() == false);
CHECK(parse_string("\"\x01\"").accept() == false);
CHECK(parse_string("\"\x02\"").accept() == false);
CHECK(parse_string("\"\x03\"").accept() == false);
CHECK(parse_string("\"\x04\"").accept() == false);
CHECK(parse_string("\"\x05\"").accept() == false);
CHECK(parse_string("\"\x06\"").accept() == false);
CHECK(parse_string("\"\x07\"").accept() == false);
CHECK(parse_string("\"\x08\"").accept() == false);
CHECK(parse_string("\"\x09\"").accept() == false);
CHECK(parse_string("\"\x0a\"").accept() == false);
CHECK(parse_string("\"\x0b\"").accept() == false);
CHECK(parse_string("\"\x0c\"").accept() == false);
CHECK(parse_string("\"\x0d\"").accept() == false);
CHECK(parse_string("\"\x0e\"").accept() == false);
CHECK(parse_string("\"\x0f\"").accept() == false);
CHECK(parse_string("\"\x10\"").accept() == false);
CHECK(parse_string("\"\x11\"").accept() == false);
CHECK(parse_string("\"\x12\"").accept() == false);
CHECK(parse_string("\"\x13\"").accept() == false);
CHECK(parse_string("\"\x14\"").accept() == false);
CHECK(parse_string("\"\x15\"").accept() == false);
CHECK(parse_string("\"\x16\"").accept() == false);
CHECK(parse_string("\"\x17\"").accept() == false);
CHECK(parse_string("\"\x18\"").accept() == false);
CHECK(parse_string("\"\x19\"").accept() == false);
CHECK(parse_string("\"\x1a\"").accept() == false);
CHECK(parse_string("\"\x1b\"").accept() == false);
CHECK(parse_string("\"\x1c\"").accept() == false);
CHECK(parse_string("\"\x1d\"").accept() == false);
CHECK(parse_string("\"\x1e\"").accept() == false);
CHECK(parse_string("\"\x1f\"").accept() == false);
}
SECTION("escaped")
{
// quotation mark "\""
auto r1 = R"("\"")"_json;
CHECK(parse_string("\"\\\"\"").accept());
// reverse solidus "\\"
auto r2 = R"("\\")"_json;
CHECK(parse_string("\"\\\\\"").accept());
// solidus
CHECK(parse_string("\"\\/\"").accept());
// backspace
CHECK(parse_string("\"\\b\"").accept());
// formfeed
CHECK(parse_string("\"\\f\"").accept());
// newline
CHECK(parse_string("\"\\n\"").accept());
// carriage return
CHECK(parse_string("\"\\r\"").accept());
// horizontal tab
CHECK(parse_string("\"\\t\"").accept());
CHECK(parse_string("\"\\u0001\"").accept());
CHECK(parse_string("\"\\u000a\"").accept());
CHECK(parse_string("\"\\u00b0\"").accept());
CHECK(parse_string("\"\\u0c00\"").accept());
CHECK(parse_string("\"\\ud000\"").accept());
CHECK(parse_string("\"\\u000E\"").accept());
CHECK(parse_string("\"\\u00F0\"").accept());
CHECK(parse_string("\"\\u0100\"").accept());
CHECK(parse_string("\"\\u2000\"").accept());
CHECK(parse_string("\"\\uFFFF\"").accept());
CHECK(parse_string("\"\\u20AC\"").accept());
CHECK(parse_string("\"\"").accept());
CHECK(parse_string("\"🎈\"").accept());
CHECK(parse_string("\"\\ud80c\\udc60\"").accept());
CHECK(parse_string("\"\\ud83c\\udf1e\"").accept());
}
}
SECTION("number")
{
SECTION("integers")
{
SECTION("without exponent")
{
CHECK(parse_string("-128").accept());
CHECK(parse_string("-0").accept());
CHECK(parse_string("0").accept());
CHECK(parse_string("128").accept());
}
SECTION("with exponent")
{
CHECK(parse_string("0e1").accept());
CHECK(parse_string("0E1").accept());
CHECK(parse_string("10000E-4").accept());
CHECK(parse_string("10000E-3").accept());
CHECK(parse_string("10000E-2").accept());
CHECK(parse_string("10000E-1").accept());
CHECK(parse_string("10000E0").accept());
CHECK(parse_string("10000E1").accept());
CHECK(parse_string("10000E2").accept());
CHECK(parse_string("10000E3").accept());
CHECK(parse_string("10000E4").accept());
CHECK(parse_string("10000e-4").accept());
CHECK(parse_string("10000e-3").accept());
CHECK(parse_string("10000e-2").accept());
CHECK(parse_string("10000e-1").accept());
CHECK(parse_string("10000e0").accept());
CHECK(parse_string("10000e1").accept());
CHECK(parse_string("10000e2").accept());
CHECK(parse_string("10000e3").accept());
CHECK(parse_string("10000e4").accept());
CHECK(parse_string("-0e1").accept());
CHECK(parse_string("-0E1").accept());
CHECK(parse_string("-0E123").accept());
}
SECTION("edge cases")
{
// From RFC7159, Section 6:
// Note that when such software is used, numbers that are
// integers and are in the range [-(2**53)+1, (2**53)-1]
// are interoperable in the sense that implementations will
// agree exactly on their numeric values.
// -(2**53)+1
CHECK(parse_string("-9007199254740991").accept());
// (2**53)-1
CHECK(parse_string("9007199254740991").accept());
}
SECTION("over the edge cases") // issue #178 - Integer conversion to unsigned (incorrect handling of 64 bit integers)
{
// While RFC7159, Section 6 specifies a preference for support
// for ranges in range of IEEE 754-2008 binary64 (double precision)
// this does not accommodate 64 bit integers without loss of accuracy.
// As 64 bit integers are now widely used in software, it is desirable
// to expand support to to the full 64 bit (signed and unsigned) range
// i.e. -(2**63) -> (2**64)-1.
// -(2**63) ** Note: compilers see negative literals as negated positive numbers (hence the -1))
CHECK(parse_string("-9223372036854775808").accept());
// (2**63)-1
CHECK(parse_string("9223372036854775807").accept());
// (2**64)-1
CHECK(parse_string("18446744073709551615").accept());
}
}
SECTION("floating-point")
{
SECTION("without exponent")
{
CHECK(parse_string("-128.5").accept());
CHECK(parse_string("0.999").accept());
CHECK(parse_string("128.5").accept());
CHECK(parse_string("-0.0").accept());
}
SECTION("with exponent")
{
CHECK(parse_string("-128.5E3").accept());
CHECK(parse_string("-128.5E-3").accept());
CHECK(parse_string("-0.0e1").accept());
CHECK(parse_string("-0.0E1").accept());
}
}
SECTION("overflow")
{
// overflows during parsing yield an exception, but is accepted anyway
CHECK(parse_string("1.18973e+4932").accept());
}
SECTION("invalid numbers")
{
CHECK(parse_string("01").accept() == false);
CHECK(parse_string("--1").accept() == false);
CHECK(parse_string("1.").accept() == false);
CHECK(parse_string("1E").accept() == false);
CHECK(parse_string("1E-").accept() == false);
CHECK(parse_string("1.E1").accept() == false);
CHECK(parse_string("-1E").accept() == false);
CHECK(parse_string("-0E#").accept() == false);
CHECK(parse_string("-0E-#").accept() == false);
CHECK(parse_string("-0#").accept() == false);
CHECK(parse_string("-0.0:").accept() == false);
CHECK(parse_string("-0.0Z").accept() == false);
CHECK(parse_string("-0E123:").accept() == false);
CHECK(parse_string("-0e0-:").accept() == false);
CHECK(parse_string("-0e-:").accept() == false);
CHECK(parse_string("-0f").accept() == false);
// numbers must not begin with "+"
CHECK_THROWS_AS(json::parser("+1").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("+0").parse(), json::parse_error);
CHECK_THROWS_WITH(json::parser("01").parse(),
"[json.exception.parse_error.101] parse error at 2: parse error - unexpected '01'");
CHECK_THROWS_WITH(json::parser("-01").parse(),
"[json.exception.parse_error.101] parse error at 3: parse error - unexpected '-01'");
CHECK_THROWS_WITH(json::parser("--1").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '-'");
CHECK_THROWS_WITH(json::parser("1.").parse(),
"[json.exception.parse_error.101] parse error at 2: parse error - unexpected '.'; expected end of input");
CHECK_THROWS_WITH(json::parser("1E").parse(),
"[json.exception.parse_error.101] parse error at 2: parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_WITH(json::parser("1E-").parse(),
"[json.exception.parse_error.101] parse error at 2: parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_WITH(json::parser("1.E1").parse(),
"[json.exception.parse_error.101] parse error at 2: parse error - unexpected '.'; expected end of input");
CHECK_THROWS_WITH(json::parser("-1E").parse(),
"[json.exception.parse_error.101] parse error at 3: parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0E#").parse(),
"[json.exception.parse_error.101] parse error at 3: parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0E-#").parse(),
"[json.exception.parse_error.101] parse error at 3: parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0#").parse(),
"[json.exception.parse_error.101] parse error at 3: parse error - unexpected '#'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0.0:").parse(),
"[json.exception.parse_error.101] parse error at 5: parse error - unexpected ':'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0.0Z").parse(),
"[json.exception.parse_error.101] parse error at 5: parse error - unexpected 'Z'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0E123:").parse(),
"[json.exception.parse_error.101] parse error at 7: parse error - unexpected ':'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0e0-:").parse(),
"[json.exception.parse_error.101] parse error at 5: parse error - unexpected '-'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0e-:").parse(),
"[json.exception.parse_error.101] parse error at 3: parse error - unexpected 'e'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0f").parse(),
"[json.exception.parse_error.101] parse error at 3: parse error - unexpected 'f'; expected end of input");
CHECK(parse_string("+1").accept() == false);
CHECK(parse_string("+0").accept() == false);
}
}
}
......@@ -346,150 +615,153 @@ TEST_CASE("parser class")
SECTION("parse errors")
{
// unexpected end of number
CHECK_THROWS_AS(json::parser("0.").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("-").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("--").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("-0.").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("-.").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("-:").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("0.:").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("e.").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("1e.").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("1e/").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("1e:").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("1E.").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("1E/").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("1E:").parse(), json::parse_error);
CHECK_THROWS_WITH(json::parser("0.").parse(),
"[json.exception.parse_error.101] parse error at 2: parse error - unexpected '.'; expected end of input");
CHECK_THROWS_WITH(json::parser("-").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '-'");
CHECK_THROWS_WITH(json::parser("--").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '-'");
CHECK_THROWS_WITH(json::parser("-0.").parse(),
"[json.exception.parse_error.101] parse error at 3: parse error - unexpected '.'; expected end of input");
CHECK_THROWS_WITH(json::parser("-.").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '-'");
CHECK_THROWS_WITH(json::parser("-:").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '-'");
CHECK_THROWS_WITH(json::parser("0.:").parse(),
"[json.exception.parse_error.101] parse error at 2: parse error - unexpected '.'; expected end of input");
CHECK_THROWS_WITH(json::parser("e.").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected 'e'");
CHECK_THROWS_WITH(json::parser("1e.").parse(),
"[json.exception.parse_error.101] parse error at 2: parse error - unexpected 'e'; expected end of input");
CHECK_THROWS_WITH(json::parser("1e/").parse(),
"[json.exception.parse_error.101] parse error at 2: parse error - unexpected 'e'; expected end of input");
CHECK_THROWS_WITH(json::parser("1e:").parse(),
"[json.exception.parse_error.101] parse error at 2: parse error - unexpected 'e'; expected end of input");
CHECK_THROWS_WITH(json::parser("1E.").parse(),
"[json.exception.parse_error.101] parse error at 2: parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_WITH(json::parser("1E/").parse(),
"[json.exception.parse_error.101] parse error at 2: parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_WITH(json::parser("1E:").parse(),
"[json.exception.parse_error.101] parse error at 2: parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_AS(parse_string("0.").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("-").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("--").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("-0.").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("-.").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("-:").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("0.:").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("e.").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("1e.").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("1e/").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("1e:").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("1E.").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("1E/").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("1E:").parse(), json::parse_error);
CHECK_THROWS_WITH(parse_string("0.").parse(),
"[json.exception.parse_error.101] parse error at 3: syntax error - invalid number; expected digit after '.'; last read '0.'");
CHECK_THROWS_WITH(parse_string("-").parse(),
"[json.exception.parse_error.101] parse error at 2: syntax error - invalid number; expected digit after '-'; last read '-'");
CHECK_THROWS_WITH(parse_string("--").parse(),
"[json.exception.parse_error.101] parse error at 2: syntax error - invalid number; expected digit after '-'; last read '--'");
CHECK_THROWS_WITH(parse_string("-0.").parse(),
"[json.exception.parse_error.101] parse error at 4: syntax error - invalid number; expected digit after '.'; last read '-0.'");
CHECK_THROWS_WITH(parse_string("-.").parse(),
"[json.exception.parse_error.101] parse error at 2: syntax error - invalid number; expected digit after '-'; last read '-.'");
CHECK_THROWS_WITH(parse_string("-:").parse(),
"[json.exception.parse_error.101] parse error at 2: syntax error - invalid number; expected digit after '-'; last read '-:'");
CHECK_THROWS_WITH(parse_string("0.:").parse(),
"[json.exception.parse_error.101] parse error at 3: syntax error - invalid number; expected digit after '.'; last read '0.:'");
CHECK_THROWS_WITH(parse_string("e.").parse(),
"[json.exception.parse_error.101] parse error at 1: syntax error - invalid literal; last read 'e'");
CHECK_THROWS_WITH(parse_string("1e.").parse(),
"[json.exception.parse_error.101] parse error at 3: syntax error - invalid number; expected '+', '-', or digit after exponent; last read '1e.'");
CHECK_THROWS_WITH(parse_string("1e/").parse(),
"[json.exception.parse_error.101] parse error at 3: syntax error - invalid number; expected '+', '-', or digit after exponent; last read '1e/'");
CHECK_THROWS_WITH(parse_string("1e:").parse(),
"[json.exception.parse_error.101] parse error at 3: syntax error - invalid number; expected '+', '-', or digit after exponent; last read '1e:'");
CHECK_THROWS_WITH(parse_string("1E.").parse(),
"[json.exception.parse_error.101] parse error at 3: syntax error - invalid number; expected '+', '-', or digit after exponent; last read '1E.'");
CHECK_THROWS_WITH(parse_string("1E/").parse(),
"[json.exception.parse_error.101] parse error at 3: syntax error - invalid number; expected '+', '-', or digit after exponent; last read '1E/'");
CHECK_THROWS_WITH(parse_string("1E:").parse(),
"[json.exception.parse_error.101] parse error at 3: syntax error - invalid number; expected '+', '-', or digit after exponent; last read '1E:'");
// unexpected end of null
CHECK_THROWS_AS(json::parser("n").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("nu").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("nul").parse(), json::parse_error);
CHECK_THROWS_WITH(json::parser("n").parse(), "[json.exception.parse_error.101] parse error at 1: parse error - unexpected 'n'");
CHECK_THROWS_WITH(json::parser("nu").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected 'n'");
CHECK_THROWS_WITH(json::parser("nul").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected 'n'");
CHECK_THROWS_AS(parse_string("n").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("nu").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("nul").parse(), json::parse_error);
CHECK_THROWS_WITH(parse_string("n").parse(),
"[json.exception.parse_error.101] parse error at 2: syntax error - invalid literal; expected 'null'; last read 'n'");
CHECK_THROWS_WITH(parse_string("nu").parse(),
"[json.exception.parse_error.101] parse error at 3: syntax error - invalid literal; expected 'null'; last read 'nu'");
CHECK_THROWS_WITH(parse_string("nul").parse(),
"[json.exception.parse_error.101] parse error at 4: syntax error - invalid literal; expected 'null'; last read 'nul'");
// unexpected end of true
CHECK_THROWS_AS(json::parser("t").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("tr").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("tru").parse(), json::parse_error);
CHECK_THROWS_WITH(json::parser("t").parse(), "[json.exception.parse_error.101] parse error at 1: parse error - unexpected 't'");
CHECK_THROWS_WITH(json::parser("tr").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected 't'");
CHECK_THROWS_WITH(json::parser("tru").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected 't'");
CHECK_THROWS_AS(parse_string("t").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("tr").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("tru").parse(), json::parse_error);
CHECK_THROWS_WITH(parse_string("t").parse(),
"[json.exception.parse_error.101] parse error at 2: syntax error - invalid literal; expected 'true'; last read 't'");
CHECK_THROWS_WITH(parse_string("tr").parse(),
"[json.exception.parse_error.101] parse error at 3: syntax error - invalid literal; expected 'true'; last read 'tr'");
CHECK_THROWS_WITH(parse_string("tru").parse(),
"[json.exception.parse_error.101] parse error at 4: syntax error - invalid literal; expected 'true'; last read 'tru'");
// unexpected end of false
CHECK_THROWS_AS(json::parser("f").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("fa").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("fal").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("fals").parse(), json::parse_error);
CHECK_THROWS_WITH(json::parser("f").parse(), "[json.exception.parse_error.101] parse error at 1: parse error - unexpected 'f'");
CHECK_THROWS_WITH(json::parser("fa").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected 'f'");
CHECK_THROWS_WITH(json::parser("fal").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected 'f'");
CHECK_THROWS_WITH(json::parser("fals").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected 'f'");
CHECK_THROWS_AS(parse_string("f").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("fa").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("fal").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("fals").parse(), json::parse_error);
CHECK_THROWS_WITH(parse_string("f").parse(),
"[json.exception.parse_error.101] parse error at 2: syntax error - invalid literal; expected 'false'; last read 'f'");
CHECK_THROWS_WITH(parse_string("fa").parse(),
"[json.exception.parse_error.101] parse error at 3: syntax error - invalid literal; expected 'false'; last read 'fa'");
CHECK_THROWS_WITH(parse_string("fal").parse(),
"[json.exception.parse_error.101] parse error at 4: syntax error - invalid literal; expected 'false'; last read 'fal'");
CHECK_THROWS_WITH(parse_string("fals").parse(),
"[json.exception.parse_error.101] parse error at 5: syntax error - invalid literal; expected 'false'; last read 'fals'");
// missing/unexpected end of array
CHECK_THROWS_AS(json::parser("[").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("[1").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("[1,").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("[1,]").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("]").parse(), json::parse_error);
CHECK_THROWS_WITH(json::parser("[").parse(),
"[json.exception.parse_error.101] parse error at 2: parse error - unexpected end of input");
CHECK_THROWS_WITH(json::parser("[1").parse(),
"[json.exception.parse_error.101] parse error at 3: parse error - unexpected end of input; expected ']'");
CHECK_THROWS_WITH(json::parser("[1,").parse(),
"[json.exception.parse_error.101] parse error at 4: parse error - unexpected end of input");
CHECK_THROWS_WITH(json::parser("[1,]").parse(),
"[json.exception.parse_error.101] parse error at 4: parse error - unexpected ']'");
CHECK_THROWS_WITH(json::parser("]").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected ']'");
CHECK_THROWS_AS(parse_string("[").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("[1").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("[1,").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("[1,]").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("]").parse(), json::parse_error);
CHECK_THROWS_WITH(parse_string("[").parse(),
"[json.exception.parse_error.101] parse error at 2: syntax error - unexpected end of input");
CHECK_THROWS_WITH(parse_string("[1").parse(),
"[json.exception.parse_error.101] parse error at 3: syntax error - unexpected end of input; expected ']'");
CHECK_THROWS_WITH(parse_string("[1,").parse(),
"[json.exception.parse_error.101] parse error at 4: syntax error - unexpected end of input");
CHECK_THROWS_WITH(parse_string("[1,]").parse(),
"[json.exception.parse_error.101] parse error at 4: syntax error - unexpected ']'");
CHECK_THROWS_WITH(parse_string("]").parse(),
"[json.exception.parse_error.101] parse error at 1: syntax error - unexpected ']'");
// missing/unexpected end of object
CHECK_THROWS_AS(json::parser("{").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("{\"foo\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("{\"foo\":").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("{\"foo\":}").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("{\"foo\":1,}").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("}").parse(), json::parse_error);
CHECK_THROWS_WITH(json::parser("{").parse(),
"[json.exception.parse_error.101] parse error at 2: parse error - unexpected end of input; expected string literal");
CHECK_THROWS_WITH(json::parser("{\"foo\"").parse(),
"[json.exception.parse_error.101] parse error at 7: parse error - unexpected end of input; expected ':'");
CHECK_THROWS_WITH(json::parser("{\"foo\":").parse(),
"[json.exception.parse_error.101] parse error at 8: parse error - unexpected end of input");
CHECK_THROWS_WITH(json::parser("{\"foo\":}").parse(),
"[json.exception.parse_error.101] parse error at 8: parse error - unexpected '}'");
CHECK_THROWS_WITH(json::parser("{\"foo\":1,}").parse(),
"[json.exception.parse_error.101] parse error at 10: parse error - unexpected '}'; expected string literal");
CHECK_THROWS_WITH(json::parser("}").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '}'");
CHECK_THROWS_AS(parse_string("{").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("{\"foo\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("{\"foo\":").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("{\"foo\":}").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("{\"foo\":1,}").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("}").parse(), json::parse_error);
CHECK_THROWS_WITH(parse_string("{").parse(),
"[json.exception.parse_error.101] parse error at 2: syntax error - unexpected end of input; expected string literal");
CHECK_THROWS_WITH(parse_string("{\"foo\"").parse(),
"[json.exception.parse_error.101] parse error at 7: syntax error - unexpected end of input; expected ':'");
CHECK_THROWS_WITH(parse_string("{\"foo\":").parse(),
"[json.exception.parse_error.101] parse error at 8: syntax error - unexpected end of input");
CHECK_THROWS_WITH(parse_string("{\"foo\":}").parse(),
"[json.exception.parse_error.101] parse error at 8: syntax error - unexpected '}'");
CHECK_THROWS_WITH(parse_string("{\"foo\":1,}").parse(),
"[json.exception.parse_error.101] parse error at 10: syntax error - unexpected '}'; expected string literal");
CHECK_THROWS_WITH(parse_string("}").parse(),
"[json.exception.parse_error.101] parse error at 1: syntax error - unexpected '}'");
// missing/unexpected end of string
CHECK_THROWS_AS(json::parser("\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\\\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\\u\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\\u0\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\\u01\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\\u012\"").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\\u").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\\u0").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\\u01").parse(), json::parse_error);
CHECK_THROWS_AS(json::parser("\"\\u012").parse(), json::parse_error);
CHECK_THROWS_WITH(json::parser("\"").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser("\"\\\"").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser("\"\\u\"").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser("\"\\u0\"").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser("\"\\u01\"").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser("\"\\u012\"").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser("\"\\u").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser("\"\\u0").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser("\"\\u01").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser("\"\\u012").parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CHECK_THROWS_AS(parse_string("\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\\\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\\u\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\\u0\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\\u01\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\\u012\"").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\\u").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\\u0").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\\u01").parse(), json::parse_error);
CHECK_THROWS_AS(parse_string("\"\\u012").parse(), json::parse_error);
CHECK_THROWS_WITH(parse_string("\"").parse(),
"[json.exception.parse_error.101] parse error at 2: syntax error - invalid string: missing closing quote; last read '\"'");
CHECK_THROWS_WITH(parse_string("\"\\\"").parse(),
"[json.exception.parse_error.101] parse error at 4: syntax error - invalid string: missing closing quote; last read '\"\\\"'");
CHECK_THROWS_WITH(parse_string("\"\\u\"").parse(),
"[json.exception.parse_error.101] parse error at 4: syntax error - invalid string: '\\u' must be followed by 4 hex digits; last read '\"\\u\"'");
CHECK_THROWS_WITH(parse_string("\"\\u0\"").parse(),
"[json.exception.parse_error.101] parse error at 5: syntax error - invalid string: '\\u' must be followed by 4 hex digits; last read '\"\\u0\"'");
CHECK_THROWS_WITH(parse_string("\"\\u01\"").parse(),
"[json.exception.parse_error.101] parse error at 6: syntax error - invalid string: '\\u' must be followed by 4 hex digits; last read '\"\\u01\"'");
CHECK_THROWS_WITH(parse_string("\"\\u012\"").parse(),
"[json.exception.parse_error.101] parse error at 7: syntax error - invalid string: '\\u' must be followed by 4 hex digits; last read '\"\\u012\"'");
CHECK_THROWS_WITH(parse_string("\"\\u").parse(),
"[json.exception.parse_error.101] parse error at 4: syntax error - invalid string: '\\u' must be followed by 4 hex digits; last read '\"\\u'");
CHECK_THROWS_WITH(parse_string("\"\\u0").parse(),
"[json.exception.parse_error.101] parse error at 5: syntax error - invalid string: '\\u' must be followed by 4 hex digits; last read '\"\\u0'");
CHECK_THROWS_WITH(parse_string("\"\\u01").parse(),
"[json.exception.parse_error.101] parse error at 6: syntax error - invalid string: '\\u' must be followed by 4 hex digits; last read '\"\\u01'");
CHECK_THROWS_WITH(parse_string("\"\\u012").parse(),
"[json.exception.parse_error.101] parse error at 7: syntax error - invalid string: '\\u' must be followed by 4 hex digits; last read '\"\\u012'");
// invalid escapes
for (int c = 1; c < 128; ++c)
......@@ -508,7 +780,7 @@ TEST_CASE("parser class")
case ('r'):
case ('t'):
{
CHECK_NOTHROW(json::parser(s.c_str()).parse());
CHECK_NOTHROW(parse_string(s.c_str()).parse());
break;
}
......@@ -521,9 +793,13 @@ TEST_CASE("parser class")
// any other combination of backslash and character is invalid
default:
{
CHECK_THROWS_AS(json::parser(s.c_str()).parse(), json::parse_error);
CHECK_THROWS_WITH(json::parser(s.c_str()).parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CHECK_THROWS_AS(parse_string(s.c_str()).parse(), json::parse_error);
// only check error message if c is not a control character
if (c > 0x1f)
{
CHECK_THROWS_WITH(parse_string(s.c_str()).parse(),
"[json.exception.parse_error.101] parse error at 3: syntax error - invalid string: forbidden character after backspace; last read '\"\\" + std::string(1, static_cast<char>(c)) + "'");
}
break;
}
}
......@@ -581,26 +857,52 @@ TEST_CASE("parser class")
if (valid(c))
{
CHECK_NOTHROW(json::parser(s1.c_str()).parse());
CHECK_NOTHROW(json::parser(s2.c_str()).parse());
CHECK_NOTHROW(json::parser(s3.c_str()).parse());
CHECK_NOTHROW(json::parser(s4.c_str()).parse());
CAPTURE(s1);
CHECK_NOTHROW(parse_string(s1.c_str()).parse());
CAPTURE(s2);
CHECK_NOTHROW(parse_string(s2.c_str()).parse());
CAPTURE(s3);
CHECK_NOTHROW(parse_string(s3.c_str()).parse());
CAPTURE(s4);
CHECK_NOTHROW(parse_string(s4.c_str()).parse());
}
else
{
CHECK_THROWS_AS(json::parser(s1.c_str()).parse(), json::parse_error);
CHECK_THROWS_AS(json::parser(s2.c_str()).parse(), json::parse_error);
CHECK_THROWS_AS(json::parser(s3.c_str()).parse(), json::parse_error);
CHECK_THROWS_AS(json::parser(s4.c_str()).parse(), json::parse_error);
CAPTURE(s1);
CHECK_THROWS_AS(parse_string(s1.c_str()).parse(), json::parse_error);
// only check error message if c is not a control character
if (c > 0x1f)
{
CHECK_THROWS_WITH(parse_string(s1.c_str()).parse(),
"[json.exception.parse_error.101] parse error at 7: syntax error - invalid string: '\\u' must be followed by 4 hex digits; last read '" + s1.substr(0, 7) + "'");
}
CAPTURE(s2);
CHECK_THROWS_AS(parse_string(s2.c_str()).parse(), json::parse_error);
// only check error message if c is not a control character
if (c > 0x1f)
{
CHECK_THROWS_WITH(parse_string(s2.c_str()).parse(),
"[json.exception.parse_error.101] parse error at 6: syntax error - invalid string: '\\u' must be followed by 4 hex digits; last read '" + s2.substr(0, 6) + "'");
}
CAPTURE(s3);
CHECK_THROWS_AS(parse_string(s3.c_str()).parse(), json::parse_error);
// only check error message if c is not a control character
if (c > 0x1f)
{
CHECK_THROWS_WITH(parse_string(s3.c_str()).parse(),
"[json.exception.parse_error.101] parse error at 5: syntax error - invalid string: '\\u' must be followed by 4 hex digits; last read '" + s3.substr(0, 5) + "'");
}
CHECK_THROWS_WITH(json::parser(s1.c_str()).parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser(s2.c_str()).parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser(s3.c_str()).parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser(s4.c_str()).parse(),
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'");
CAPTURE(s4);
CHECK_THROWS_AS(parse_string(s4.c_str()).parse(), json::parse_error);
// only check error message if c is not a control character
if (c > 0x1f)
{
CHECK_THROWS_WITH(parse_string(s4.c_str()).parse(),
"[json.exception.parse_error.101] parse error at 4: syntax error - invalid string: '\\u' must be followed by 4 hex digits; last read '" + s4.substr(0, 4) + "'");
}
}
}
}
......@@ -608,29 +910,212 @@ TEST_CASE("parser class")
// missing part of a surrogate pair
CHECK_THROWS_AS(json::parse("\"\\uD80C\""), json::parse_error);
CHECK_THROWS_WITH(json::parse("\"\\uD80C\""),
"[json.exception.parse_error.102] parse error at 8: missing low surrogate");
"[json.exception.parse_error.101] parse error at 8: syntax error - invalid string: surrogate U+D80C must be followed by U+DC00..U+DFFF; last read '\"\\uD80C\"'");
// invalid surrogate pair
CHECK_THROWS_AS(json::parse("\"\\uD80C\\uD80C\""), json::parse_error);
CHECK_THROWS_AS(json::parse("\"\\uD80C\\u0000\""), json::parse_error);
CHECK_THROWS_AS(json::parse("\"\\uD80C\\uFFFF\""), json::parse_error);
CHECK_THROWS_WITH(json::parse("\"\\uD80C\\uD80C\""),
"[json.exception.parse_error.102] parse error at 14: missing or wrong low surrogate");
"[json.exception.parse_error.101] parse error at 13: syntax error - invalid string: surrogate U+D80C must be followed by U+DC00..U+DFFF instead of U+D80C; last read '\"\\uD80C\\uD80C'");
CHECK_THROWS_WITH(json::parse("\"\\uD80C\\u0000\""),
"[json.exception.parse_error.102] parse error at 14: missing or wrong low surrogate");
"[json.exception.parse_error.101] parse error at 13: syntax error - invalid string: surrogate U+D80C must be followed by U+DC00..U+DFFF instead of U+0000; last read '\"\\uD80C\\u0000'");
CHECK_THROWS_WITH(json::parse("\"\\uD80C\\uFFFF\""),
"[json.exception.parse_error.102] parse error at 14: missing or wrong low surrogate");
"[json.exception.parse_error.101] parse error at 13: syntax error - invalid string: surrogate U+D80C must be followed by U+DC00..U+DFFF instead of U+FFFF; last read '\"\\uD80C\\uFFFF'");
}
SECTION("parse errors (accept)")
{
// unexpected end of number
CHECK(parse_string("0.").accept() == false);
CHECK(parse_string("-").accept() == false);
CHECK(parse_string("--").accept() == false);
CHECK(parse_string("-0.").accept() == false);
CHECK(parse_string("-.").accept() == false);
CHECK(parse_string("-:").accept() == false);
CHECK(parse_string("0.:").accept() == false);
CHECK(parse_string("e.").accept() == false);
CHECK(parse_string("1e.").accept() == false);
CHECK(parse_string("1e/").accept() == false);
CHECK(parse_string("1e:").accept() == false);
CHECK(parse_string("1E.").accept() == false);
CHECK(parse_string("1E/").accept() == false);
CHECK(parse_string("1E:").accept() == false);
// unexpected end of null
CHECK(parse_string("n").accept() == false);
CHECK(parse_string("nu").accept() == false);
CHECK(parse_string("nul").accept() == false);
// unexpected end of true
CHECK(parse_string("t").accept() == false);
CHECK(parse_string("tr").accept() == false);
CHECK(parse_string("tru").accept() == false);
// unexpected end of false
CHECK(parse_string("f").accept() == false);
CHECK(parse_string("fa").accept() == false);
CHECK(parse_string("fal").accept() == false);
CHECK(parse_string("fals").accept() == false);
// missing/unexpected end of array
CHECK(parse_string("[").accept() == false);
CHECK(parse_string("[1").accept() == false);
CHECK(parse_string("[1,").accept() == false);
CHECK(parse_string("[1,]").accept() == false);
CHECK(parse_string("]").accept() == false);
// missing/unexpected end of object
CHECK(parse_string("{").accept() == false);
CHECK(parse_string("{\"foo\"").accept() == false);
CHECK(parse_string("{\"foo\":").accept() == false);
CHECK(parse_string("{\"foo\":}").accept() == false);
CHECK(parse_string("{\"foo\":1,}").accept() == false);
CHECK(parse_string("}").accept() == false);
// missing/unexpected end of string
CHECK(parse_string("\"").accept() == false);
CHECK(parse_string("\"\\\"").accept() == false);
CHECK(parse_string("\"\\u\"").accept() == false);
CHECK(parse_string("\"\\u0\"").accept() == false);
CHECK(parse_string("\"\\u01\"").accept() == false);
CHECK(parse_string("\"\\u012\"").accept() == false);
CHECK(parse_string("\"\\u").accept() == false);
CHECK(parse_string("\"\\u0").accept() == false);
CHECK(parse_string("\"\\u01").accept() == false);
CHECK(parse_string("\"\\u012").accept() == false);
// invalid escapes
for (int c = 1; c < 128; ++c)
{
auto s = std::string("\"\\") + std::string(1, static_cast<char>(c)) + "\"";
switch (c)
{
// valid escapes
case ('"'):
case ('\\'):
case ('/'):
case ('b'):
case ('f'):
case ('n'):
case ('r'):
case ('t'):
{
CHECK(parse_string(s.c_str()).accept());
break;
}
// \u must be followed with four numbers, so we skip it here
case ('u'):
{
break;
}
// any other combination of backslash and character is invalid
default:
{
CHECK(parse_string(s.c_str()).accept() == false);
break;
}
}
}
// invalid \uxxxx escapes
{
// check whether character is a valid hex character
const auto valid = [](int c)
{
switch (c)
{
case ('0'):
case ('1'):
case ('2'):
case ('3'):
case ('4'):
case ('5'):
case ('6'):
case ('7'):
case ('8'):
case ('9'):
case ('a'):
case ('b'):
case ('c'):
case ('d'):
case ('e'):
case ('f'):
case ('A'):
case ('B'):
case ('C'):
case ('D'):
case ('E'):
case ('F'):
{
return true;
}
default:
{
return false;
}
}
};
for (int c = 1; c < 128; ++c)
{
std::string s = "\"\\u";
// create a string with the iterated character at each position
auto s1 = s + "000" + std::string(1, static_cast<char>(c)) + "\"";
auto s2 = s + "00" + std::string(1, static_cast<char>(c)) + "0\"";
auto s3 = s + "0" + std::string(1, static_cast<char>(c)) + "00\"";
auto s4 = s + std::string(1, static_cast<char>(c)) + "000\"";
if (valid(c))
{
CAPTURE(s1);
CHECK(parse_string(s1.c_str()).accept());
CAPTURE(s2);
CHECK(parse_string(s2.c_str()).accept());
CAPTURE(s3);
CHECK(parse_string(s3.c_str()).accept());
CAPTURE(s4);
CHECK(parse_string(s4.c_str()).accept());
}
else
{
CAPTURE(s1);
CHECK(parse_string(s1.c_str()).accept() == false);
CAPTURE(s2);
CHECK(parse_string(s2.c_str()).accept() == false);
CAPTURE(s3);
CHECK(parse_string(s3.c_str()).accept() == false);
CAPTURE(s4);
CHECK(parse_string(s4.c_str()).accept() == false);
}
}
}
// missing part of a surrogate pair
CHECK(parse_string("\"\\uD80C\"").accept() == false);
// invalid surrogate pair
CHECK(parse_string("\"\\uD80C\\uD80C\"").accept() == false);
CHECK(parse_string("\"\\uD80C\\u0000\"").accept() == false);
CHECK(parse_string("\"\\uD80C\\uFFFF\"").accept() == false);
}
SECTION("tests found by mutate++")
{
// test case to make sure no comma preceeds the first key
CHECK_THROWS_AS(json::parser("{,\"key\": false}").parse(), json::parse_error);
CHECK_THROWS_WITH(json::parser("{,\"key\": false}").parse(),
"[json.exception.parse_error.101] parse error at 2: parse error - unexpected ','");
CHECK_THROWS_AS(parse_string("{,\"key\": false}").parse(), json::parse_error);
CHECK_THROWS_WITH(parse_string("{,\"key\": false}").parse(),
"[json.exception.parse_error.101] parse error at 2: syntax error - unexpected ','; expected string literal");
// test case to make sure an object is properly closed
CHECK_THROWS_AS(json::parser("[{\"key\": false true]").parse(), json::parse_error);
CHECK_THROWS_WITH(json::parser("[{\"key\": false true]").parse(),
"[json.exception.parse_error.101] parse error at 19: parse error - unexpected true literal; expected '}'");
CHECK_THROWS_AS(parse_string("[{\"key\": false true]").parse(), json::parse_error);
CHECK_THROWS_WITH(parse_string("[{\"key\": false true]").parse(),
"[json.exception.parse_error.101] parse error at 19: syntax error - unexpected true literal; expected '}'");
// test case to make sure the callback is properly evaluated after reading a key
{
......@@ -817,42 +1302,42 @@ TEST_CASE("parser class")
SECTION("from std::vector")
{
std::vector<uint8_t> v = {'t', 'r', 'u', 'e'};
CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
CHECK(json::parser(json::input_adapter::create(std::begin(v), std::end(v))).parse() == json(true));
}
SECTION("from std::array")
{
std::array<uint8_t, 5> v { {'t', 'r', 'u', 'e'} };
CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
CHECK(json::parser(json::input_adapter::create(std::begin(v), std::end(v))).parse() == json(true));
}
SECTION("from array")
{
uint8_t v[] = {'t', 'r', 'u', 'e'};
CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
CHECK(json::parser(json::input_adapter::create(std::begin(v), std::end(v))).parse() == json(true));
}
SECTION("from char literal")
{
CHECK(json::parser("true").parse() == json(true));
CHECK(parse_string("true").parse() == json(true));
}
SECTION("from std::string")
{
std::string v = {'t', 'r', 'u', 'e'};
CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
CHECK(json::parser(json::input_adapter::create(std::begin(v), std::end(v))).parse() == json(true));
}
SECTION("from std::initializer_list")
{
std::initializer_list<uint8_t> v = {'t', 'r', 'u', 'e'};
CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
CHECK(json::parser(json::input_adapter::create(std::begin(v), std::end(v))).parse() == json(true));
}
SECTION("from std::valarray")
{
std::valarray<uint8_t> v = {'t', 'r', 'u', 'e'};
CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
CHECK(json::parser(json::input_adapter::create(std::begin(v), std::end(v))).parse() == json(true));
}
}
}
......@@ -53,7 +53,7 @@ TEST_CASE("convenience functions")
const char* escaped)
{
std::stringstream ss;
json::serializer s(ss);
json::serializer s(json::output_adapter<char>::create(ss));
s.dump_escaped(original);
CHECK(ss.str() == escaped);
};
......
......@@ -92,7 +92,7 @@ TEST_CASE("deserialization")
ss2 << "[\"foo\",1,2,3,false,{\"one\":1}";
CHECK_THROWS_AS(json::parse(ss1), json::parse_error);
CHECK_THROWS_WITH(json::parse(ss2),
"[json.exception.parse_error.101] parse error at 30: parse error - unexpected end of input; expected ']'");
"[json.exception.parse_error.101] parse error at 29: syntax error - unexpected end of input; expected ']'");
}
SECTION("string")
......@@ -100,7 +100,7 @@ TEST_CASE("deserialization")
json::string_t s = "[\"foo\",1,2,3,false,{\"one\":1}";
CHECK_THROWS_AS(json::parse(s), json::parse_error);
CHECK_THROWS_WITH(json::parse(s),
"[json.exception.parse_error.101] parse error at 29: parse error - unexpected end of input; expected ']'");
"[json.exception.parse_error.101] parse error at 29: syntax error - unexpected end of input; expected ']'");
}
SECTION("operator<<")
......@@ -111,7 +111,7 @@ TEST_CASE("deserialization")
json j;
CHECK_THROWS_AS(j << ss1, json::parse_error);
CHECK_THROWS_WITH(j << ss2,
"[json.exception.parse_error.101] parse error at 30: parse error - unexpected end of input; expected ']'");
"[json.exception.parse_error.101] parse error at 29: syntax error - unexpected end of input; expected ']'");
}
SECTION("operator>>")
......@@ -122,14 +122,14 @@ TEST_CASE("deserialization")
json j;
CHECK_THROWS_AS(ss1 >> j, json::parse_error);
CHECK_THROWS_WITH(ss2 >> j,
"[json.exception.parse_error.101] parse error at 30: parse error - unexpected end of input; expected ']'");
"[json.exception.parse_error.101] parse error at 29: syntax error - unexpected end of input; expected ']'");
}
SECTION("user-defined string literal")
{
CHECK_THROWS_AS("[\"foo\",1,2,3,false,{\"one\":1}"_json, json::parse_error);
CHECK_THROWS_WITH("[\"foo\",1,2,3,false,{\"one\":1}"_json,
"[json.exception.parse_error.101] parse error at 29: parse error - unexpected end of input; expected ']'");
"[json.exception.parse_error.101] parse error at 29: syntax error - unexpected end of input; expected ']'");
}
}
......
......@@ -676,14 +676,9 @@ TEST_CASE("MessagePack")
const auto result = json::to_msgpack(j);
CHECK(result == expected);
// restore value (reverse array for endianess)
double restored;
std::reverse(expected.begin(), expected.end());
memcpy(&restored, expected.data(), sizeof(double));
CHECK(restored == v);
// roundtrip
CHECK(json::from_msgpack(result) == j);
CHECK(json::from_msgpack(result) == v);
}
}
}
......@@ -1038,35 +1033,35 @@ TEST_CASE("MessagePack")
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error);
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcc})),
"[json.exception.parse_error.110] parse error at 2: cannot read 1 bytes from vector");
"[json.exception.parse_error.110] parse error at 2: unexpected end of input");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcd})),
"[json.exception.parse_error.110] parse error at 2: cannot read 2 bytes from vector");
"[json.exception.parse_error.110] parse error at 2: unexpected end of input");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcd, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 2 bytes from vector");
"[json.exception.parse_error.110] parse error at 3: unexpected end of input");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xce})),
"[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector");
"[json.exception.parse_error.110] parse error at 2: unexpected end of input");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xce, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector");
"[json.exception.parse_error.110] parse error at 3: unexpected end of input");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xce, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector");
"[json.exception.parse_error.110] parse error at 4: unexpected end of input");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xce, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector");
"[json.exception.parse_error.110] parse error at 5: unexpected end of input");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcf})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
"[json.exception.parse_error.110] parse error at 2: unexpected end of input");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
"[json.exception.parse_error.110] parse error at 3: unexpected end of input");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
"[json.exception.parse_error.110] parse error at 4: unexpected end of input");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
"[json.exception.parse_error.110] parse error at 5: unexpected end of input");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
"[json.exception.parse_error.110] parse error at 6: unexpected end of input");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
"[json.exception.parse_error.110] parse error at 7: unexpected end of input");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
"[json.exception.parse_error.110] parse error at 8: unexpected end of input");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
"[json.exception.parse_error.110] parse error at 9: unexpected end of input");
}
SECTION("unsupported bytes")
......
......@@ -596,7 +596,7 @@ TEST_CASE("regression tests")
// a parse error because of the EOF.
CHECK_THROWS_AS(ss >> j, json::parse_error);
CHECK_THROWS_WITH(ss >> j,
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected end of input");
"[json.exception.parse_error.101] parse error at 1: syntax error - unexpected end of input");
}
SECTION("issue #389 - Integer-overflow (OSS-Fuzz issue 267)")
......@@ -629,7 +629,7 @@ TEST_CASE("regression tests")
std::vector<uint8_t> vec {0x65, 0xf5, 0x0a, 0x48, 0x21};
CHECK_THROWS_AS(json::from_cbor(vec), json::parse_error);
CHECK_THROWS_WITH(json::from_cbor(vec),
"[json.exception.parse_error.110] parse error at 2: cannot read 5 bytes from vector");
"[json.exception.parse_error.110] parse error at 6: unexpected end of input");
}
SECTION("issue #407 - Heap-buffer-overflow (OSS-Fuzz issue 343)")
......@@ -638,31 +638,31 @@ TEST_CASE("regression tests")
std::vector<uint8_t> vec1 {0xcb, 0x8f, 0x0a};
CHECK_THROWS_AS(json::from_msgpack(vec1), json::parse_error);
CHECK_THROWS_WITH(json::from_msgpack(vec1),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
"[json.exception.parse_error.110] parse error at 4: unexpected end of input");
// related test case: incomplete float32
std::vector<uint8_t> vec2 {0xca, 0x8f, 0x0a};
CHECK_THROWS_AS(json::from_msgpack(vec2), json::parse_error);
CHECK_THROWS_WITH(json::from_msgpack(vec2),
"[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector");
"[json.exception.parse_error.110] parse error at 4: unexpected end of input");
// related test case: incomplete Half-Precision Float (CBOR)
std::vector<uint8_t> vec3 {0xf9, 0x8f};
CHECK_THROWS_AS(json::from_cbor(vec3), json::parse_error);
CHECK_THROWS_WITH(json::from_cbor(vec3),
"[json.exception.parse_error.110] parse error at 2: cannot read 2 bytes from vector");
"[json.exception.parse_error.110] parse error at 3: unexpected end of input");
// related test case: incomplete Single-Precision Float (CBOR)
std::vector<uint8_t> vec4 {0xfa, 0x8f, 0x0a};
CHECK_THROWS_AS(json::from_cbor(vec4), json::parse_error);
CHECK_THROWS_WITH(json::from_cbor(vec4),
"[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector");
"[json.exception.parse_error.110] parse error at 4: unexpected end of input");
// related test case: incomplete Double-Precision Float (CBOR)
std::vector<uint8_t> vec5 {0xfb, 0x8f, 0x0a};
CHECK_THROWS_AS(json::from_cbor(vec5), json::parse_error);
CHECK_THROWS_WITH(json::from_cbor(vec5),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
"[json.exception.parse_error.110] parse error at 4: unexpected end of input");
}
SECTION("issue #408 - Heap-buffer-overflow (OSS-Fuzz issue 344)")
......@@ -671,7 +671,7 @@ TEST_CASE("regression tests")
std::vector<uint8_t> vec1 {0x87};
CHECK_THROWS_AS(json::from_msgpack(vec1), json::parse_error);
CHECK_THROWS_WITH(json::from_msgpack(vec1),
"[json.exception.parse_error.110] parse error at 2: cannot read 1 bytes from vector");
"[json.exception.parse_error.110] parse error at 2: unexpected end of input");
// more test cases for MessagePack
for (auto b :
......@@ -705,10 +705,10 @@ TEST_CASE("regression tests")
std::vector<uint8_t> vec2;
CHECK_THROWS_AS(json::from_cbor(vec2), json::parse_error);
CHECK_THROWS_WITH(json::from_cbor(vec2),
"[json.exception.parse_error.110] parse error at 1: cannot read 1 bytes from vector");
"[json.exception.parse_error.110] parse error at 1: unexpected end of input");
CHECK_THROWS_AS(json::from_msgpack(vec2), json::parse_error);
CHECK_THROWS_WITH(json::from_msgpack(vec2),
"[json.exception.parse_error.110] parse error at 1: cannot read 1 bytes from vector");
"[json.exception.parse_error.110] parse error at 1: unexpected end of input");
}
SECTION("issue #411 - Heap-buffer-overflow (OSS-Fuzz issue 366)")
......@@ -717,19 +717,19 @@ TEST_CASE("regression tests")
std::vector<uint8_t> vec1 {0x7f};
CHECK_THROWS_AS(json::from_cbor(vec1), json::parse_error);
CHECK_THROWS_WITH(json::from_cbor(vec1),
"[json.exception.parse_error.110] parse error at 2: cannot read 1 bytes from vector");
"[json.exception.parse_error.110] parse error at 2: unexpected end of input");
// related test case: empty array (indefinite length)
std::vector<uint8_t> vec2 {0x9f};
CHECK_THROWS_AS(json::from_cbor(vec2), json::parse_error);
CHECK_THROWS_WITH(json::from_cbor(vec2),
"[json.exception.parse_error.110] parse error at 2: cannot read 1 bytes from vector");
"[json.exception.parse_error.110] parse error at 2: unexpected end of input");
// related test case: empty map (indefinite length)
std::vector<uint8_t> vec3 {0xbf};
CHECK_THROWS_AS(json::from_cbor(vec3), json::parse_error);
CHECK_THROWS_WITH(json::from_cbor(vec3),
"[json.exception.parse_error.110] parse error at 2: cannot read 1 bytes from vector");
"[json.exception.parse_error.110] parse error at 2: unexpected end of input");
}
SECTION("issue #412 - Heap-buffer-overflow (OSS-Fuzz issue 367)")
......@@ -763,19 +763,19 @@ TEST_CASE("regression tests")
std::vector<uint8_t> vec1 {0x7f, 0x61, 0x61};
CHECK_THROWS_AS(json::from_cbor(vec1), json::parse_error);
CHECK_THROWS_WITH(json::from_cbor(vec1),
"[json.exception.parse_error.110] parse error at 4: cannot read 1 bytes from vector");
"[json.exception.parse_error.110] parse error at 4: unexpected end of input");
// related test case: nonempty array (indefinite length)
std::vector<uint8_t> vec2 {0x9f, 0x01};
CHECK_THROWS_AS(json::from_cbor(vec2), json::parse_error);
CHECK_THROWS_WITH(json::from_cbor(vec2),
"[json.exception.parse_error.110] parse error at 3: cannot read 1 bytes from vector");
"[json.exception.parse_error.110] parse error at 3: unexpected end of input");
// related test case: nonempty map (indefinite length)
std::vector<uint8_t> vec3 {0xbf, 0x61, 0x61, 0x01};
CHECK_THROWS_AS(json::from_cbor(vec3), json::parse_error);
CHECK_THROWS_WITH(json::from_cbor(vec3),
"[json.exception.parse_error.110] parse error at 5: cannot read 1 bytes from vector");
"[json.exception.parse_error.110] parse error at 5: unexpected end of input");
}
SECTION("issue #414 - compare with literal 0)")
......@@ -921,6 +921,7 @@ TEST_CASE("regression tests")
CHECK(j["bool_vector"].dump() == "[false,true,false,false]");
}
/* NOTE: m_line_buffer is not used any more
SECTION("issue #495 - fill_line_buffer incorrectly tests m_stream for eof but not fail or bad bits")
{
SECTION("setting failbit")
......@@ -953,6 +954,7 @@ TEST_CASE("regression tests")
CHECK_THROWS_WITH(l.fill_line_buffer(), "[json.exception.parse_error.111] parse error: bad input stream");
}
}
*/
SECTION("issue #504 - assertion error (OSS-Fuzz 856)")
{
......
......@@ -77,8 +77,8 @@ TEST_CASE("compliance tests from json.org")
})
{
CAPTURE(filename);
json j;
std::ifstream f(filename);
json j;
CHECK_THROWS_AS(f >> j, json::parse_error);
}
}
......@@ -93,8 +93,8 @@ TEST_CASE("compliance tests from json.org")
})
{
CAPTURE(filename);
json j;
std::ifstream f(filename);
json j;
CHECK_NOTHROW(f >> j);
}
}
......@@ -305,6 +305,7 @@ TEST_CASE("compliance tests from nativejson-benchmark")
std::string json_string( (std::istreambuf_iterator<char>(f) ),
(std::istreambuf_iterator<char>()) );
CAPTURE(json_string);
json j = json::parse(json_string);
CHECK(j.dump() == json_string);
}
......
......@@ -34,17 +34,832 @@ using nlohmann::json;
#include <fstream>
// create and check a JSON string with up to four UTF-8 bytes
void check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
{
std::string json_string = "\"";
CAPTURE(byte1);
json_string += std::string(1, static_cast<char>(byte1));
if (byte2 != -1)
{
CAPTURE(byte2);
json_string += std::string(1, static_cast<char>(byte2));
}
if (byte3 != -1)
{
CAPTURE(byte3);
json_string += std::string(1, static_cast<char>(byte3));
}
if (byte4 != -1)
{
CAPTURE(byte4);
json_string += std::string(1, static_cast<char>(byte4));
}
json_string += "\"";
CAPTURE(json_string);
if (success_expected)
{
CHECK_NOTHROW(json::parse(json_string));
}
else
{
CHECK_THROWS_AS(json::parse(json_string), json::parse_error);
}
}
TEST_CASE("Unicode", "[hide]")
{
SECTION("full enumeration of Unicode code points")
SECTION("RFC 3629")
{
// lexer to call to_unicode on
json::lexer dummy_lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(""), 0);
/*
RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as
follows:
A UTF-8 string is a sequence of octets representing a sequence of UCS
characters. An octet sequence is valid UTF-8 only if it matches the
following syntax, which is derived from the rules for encoding UTF-8
and is expressed in the ABNF of [RFC2234].
UTF8-octets = *( UTF8-char )
UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
UTF8-1 = %x00-7F
UTF8-2 = %xC2-DF UTF8-tail
UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
%xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
%xF4 %x80-8F 2( UTF8-tail )
UTF8-tail = %x80-BF
*/
SECTION("ill-formed first byte")
{
for (int byte1 = 0x80; byte1 <= 0xC1; ++byte1)
{
check_utf8string(false, byte1);
}
for (int byte1 = 0xF5; byte1 <= 0xFF; ++byte1)
{
check_utf8string(false, byte1);
}
}
SECTION("UTF8-1 (x00-x7F)")
{
SECTION("well-formed")
{
for (int byte1 = 0x00; byte1 <= 0x7F; ++byte1)
{
// unescaped control characters are parse errors in JSON
if (0x00 <= byte1 and byte1 <= 0x1F)
{
check_utf8string(false, byte1);
continue;
}
// a single quote is a parse error in JSON
if (byte1 == 0x22)
{
check_utf8string(false, byte1);
continue;
}
// a single backslash is a parse error in JSON
if (byte1 == 0x5C)
{
check_utf8string(false, byte1);
continue;
}
// all other characters are OK
check_utf8string(true, byte1);
}
}
}
SECTION("UTF8-2 (xC2-xDF UTF8-tail)")
{
SECTION("well-formed")
{
for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
check_utf8string(true, byte1, byte2);
}
}
}
SECTION("ill-formed: missing second byte")
{
for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
{
check_utf8string(false, byte1);
}
}
SECTION("ill-formed: wrong second byte")
{
for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
{
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
{
// skip correct second byte
if (0x80 <= byte2 and byte2 <= 0xBF)
{
continue;
}
check_utf8string(false, byte1, byte2);
}
}
}
}
SECTION("UTF8-3 (xE0 xA0-BF UTF8-tail)")
{
SECTION("well-formed")
{
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
{
for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(true, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: missing second byte")
{
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
{
check_utf8string(false, byte1);
}
}
SECTION("ill-formed: missing third byte")
{
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
{
for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
{
check_utf8string(false, byte1, byte2);
}
}
}
SECTION("ill-formed: wrong second byte")
{
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
{
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
{
// skip correct second byte
if (0xA0 <= byte2 and byte2 <= 0xBF)
{
continue;
}
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(false, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: wrong third byte")
{
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
{
for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
{
// skip correct third byte
if (0x80 <= byte3 and byte3 <= 0xBF)
{
continue;
}
check_utf8string(false, byte1, byte2, byte3);
}
}
}
}
}
SECTION("UTF8-3 (xE1-xEC UTF8-tail UTF8-tail)")
{
SECTION("well-formed")
{
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(true, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: missing second byte")
{
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
{
check_utf8string(false, byte1);
}
}
SECTION("ill-formed: missing third byte")
{
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
check_utf8string(false, byte1, byte2);
}
}
}
SECTION("ill-formed: wrong second byte")
{
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
{
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
{
// skip correct second byte
if (0x80 <= byte2 and byte2 <= 0xBF)
{
continue;
}
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(false, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: wrong third byte")
{
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
{
// skip correct third byte
if (0x80 <= byte3 and byte3 <= 0xBF)
{
continue;
}
check_utf8string(false, byte1, byte2, byte3);
}
}
}
}
}
SECTION("UTF8-3 (xED x80-9F UTF8-tail)")
{
SECTION("well-formed")
{
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(true, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: missing second byte")
{
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
{
check_utf8string(false, byte1);
}
}
SECTION("ill-formed: missing third byte")
{
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
{
check_utf8string(false, byte1, byte2);
}
}
}
SECTION("ill-formed: wrong second byte")
{
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
{
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
{
// skip correct second byte
if (0x80 <= byte2 and byte2 <= 0x9F)
{
continue;
}
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(false, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: wrong third byte")
{
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
{
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
{
// skip correct third byte
if (0x80 <= byte3 and byte3 <= 0xBF)
{
continue;
}
check_utf8string(false, byte1, byte2, byte3);
}
}
}
}
}
SECTION("UTF8-3 (xEE-xEF UTF8-tail UTF8-tail)")
{
SECTION("well-formed")
{
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(true, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: missing second byte")
{
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
{
check_utf8string(false, byte1);
}
}
SECTION("ill-formed: missing third byte")
{
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
check_utf8string(false, byte1, byte2);
}
}
}
SECTION("ill-formed: wrong second byte")
{
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
{
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
{
// skip correct second byte
if (0x80 <= byte2 and byte2 <= 0xBF)
{
continue;
}
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(false, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: wrong third byte")
{
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
{
// skip correct third byte
if (0x80 <= byte3 and byte3 <= 0xBF)
{
continue;
}
check_utf8string(false, byte1, byte2, byte3);
}
}
}
}
}
SECTION("UTF8-4 (xF0 x90-BF UTF8-tail UTF8-tail)")
{
SECTION("well-formed")
{
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
{
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
{
check_utf8string(true, byte1, byte2, byte3, byte4);
}
}
}
}
}
SECTION("ill-formed: missing second byte")
{
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
{
check_utf8string(false, byte1);
}
}
SECTION("ill-formed: missing third byte")
{
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
{
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
{
check_utf8string(false, byte1, byte2);
}
}
}
SECTION("ill-formed: missing fourth byte")
{
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
{
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(false, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: wrong second byte")
{
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
{
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
{
// skip correct second byte
if (0x90 <= byte2 and byte2 <= 0xBF)
{
continue;
}
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
{
check_utf8string(false, byte1, byte2, byte3, byte4);
}
}
}
}
}
SECTION("ill-formed: wrong third byte")
{
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
{
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
{
// skip correct third byte
if (0x80 <= byte3 and byte3 <= 0xBF)
{
continue;
}
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
{
check_utf8string(false, byte1, byte2, byte3, byte4);
}
}
}
}
}
SECTION("ill-formed: wrong fourth byte")
{
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
{
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
{
// skip fourth second byte
if (0x80 <= byte3 and byte3 <= 0xBF)
{
continue;
}
check_utf8string(false, byte1, byte2, byte3, byte4);
}
}
}
}
}
}
SECTION("UTF8-4 (xF1-F3 UTF8-tail UTF8-tail UTF8-tail)")
{
SECTION("well-formed")
{
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
{
check_utf8string(true, byte1, byte2, byte3, byte4);
}
}
}
}
}
SECTION("ill-formed: missing second byte")
{
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
{
check_utf8string(false, byte1);
}
}
SECTION("ill-formed: missing third byte")
{
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
check_utf8string(false, byte1, byte2);
}
}
}
SECTION("ill-formed: missing fourth byte")
{
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(false, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: wrong second byte")
{
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
{
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
{
// skip correct second byte
if (0x80 <= byte2 and byte2 <= 0xBF)
{
continue;
}
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
{
check_utf8string(false, byte1, byte2, byte3, byte4);
}
}
}
}
}
SECTION("ill-formed: wrong third byte")
{
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
{
// skip correct third byte
if (0x80 <= byte3 and byte3 <= 0xBF)
{
continue;
}
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
{
check_utf8string(false, byte1, byte2, byte3, byte4);
}
}
}
}
}
SECTION("ill-formed: wrong fourth byte")
{
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
{
// skip correct fourth byte
if (0x80 <= byte3 and byte3 <= 0xBF)
{
continue;
}
check_utf8string(false, byte1, byte2, byte3, byte4);
}
}
}
}
}
}
SECTION("UTF8-4 (xF4 x80-8F UTF8-tail UTF8-tail)")
{
SECTION("well-formed")
{
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
{
check_utf8string(true, byte1, byte2, byte3, byte4);
}
}
}
}
}
SECTION("ill-formed: missing second byte")
{
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
{
check_utf8string(false, byte1);
}
}
SECTION("ill-formed: missing third byte")
{
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
{
check_utf8string(false, byte1, byte2);
}
}
}
SECTION("ill-formed: missing fourth byte")
{
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
check_utf8string(false, byte1, byte2, byte3);
}
}
}
}
SECTION("ill-formed: wrong second byte")
{
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
{
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
{
// skip correct second byte
if (0x80 <= byte2 and byte2 <= 0x8F)
{
continue;
}
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
{
check_utf8string(false, byte1, byte2, byte3, byte4);
}
}
}
}
}
SECTION("ill-formed: wrong third byte")
{
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
{
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
{
// skip correct third byte
if (0x80 <= byte3 and byte3 <= 0xBF)
{
continue;
}
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
{
check_utf8string(false, byte1, byte2, byte3, byte4);
}
}
}
}
}
SECTION("ill-formed: wrong fourth byte")
{
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
{
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
{
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
{
for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
{
// skip correct fourth byte
if (0x80 <= byte3 and byte3 <= 0xBF)
{
continue;
}
check_utf8string(false, byte1, byte2, byte3, byte4);
}
}
}
}
}
}
}
SECTION("\\uxxxx sequences")
{
// create an escaped string from a code point
const auto codepoint_to_unicode = [](std::size_t cp)
{
// copd points are represented as a six-character sequence: a
// code points are represented as a six-character sequence: a
// reverse solidus, followed by the lowercase letter u, followed
// by four hexadecimal digits that encode the character's code
// point
......@@ -53,70 +868,100 @@ TEST_CASE("Unicode", "[hide]")
return ss.str();
};
// generate all UTF-8 code points; in total, 1112064 code points are
// generated: 0x1FFFFF code points - 2048 invalid values between
// 0xD800 and 0xDFFF.
for (std::size_t cp = 0; cp <= 0x10FFFFu; ++cp)
SECTION("correct sequences")
{
// The Unicode standard permanently reserves these code point
// values for UTF-16 encoding of the high and low surrogates, and
// they will never be assigned a character, so there should be no
// reason to encode them. The official Unicode standard says that
// no UTF forms, including UTF-16, can encode these code points.
if (cp >= 0xD800u and cp <= 0xDFFFu)
// generate all UTF-8 code points; in total, 1112064 code points are
// generated: 0x1FFFFF code points - 2048 invalid values between
// 0xD800 and 0xDFFF.
for (std::size_t cp = 0; cp <= 0x10FFFFu; ++cp)
{
// if we would not skip these code points, we would get a
// "missing low surrogate" exception
continue;
}
// string to store the code point as in \uxxxx format
std::string json_text = "\"";
// string to store the code point as in \uxxxx format
std::string escaped_string;
// string to store the code point as unescaped character sequence
std::string unescaped_string;
if (cp < 0x10000u)
{
// code points in the Basic Multilingual Plane can be
// represented with one \\uxxxx sequence
escaped_string = codepoint_to_unicode(cp);
// decide whether to use one or two \uxxxx sequences
if (cp < 0x10000u)
{
// The Unicode standard permanently reserves these code point
// values for UTF-16 encoding of the high and low surrogates, and
// they will never be assigned a character, so there should be no
// reason to encode them. The official Unicode standard says that
// no UTF forms, including UTF-16, can encode these code points.
if (cp >= 0xD800u and cp <= 0xDFFFu)
{
// if we would not skip these code points, we would get a
// "missing low surrogate" exception
continue;
}
// All Unicode characters may be placed within the quotation
// marks, except for the characters that must be escaped:
// quotation mark, reverse solidus, and the control characters
// (U+0000 through U+001F); we ignore these code points as
// they are checked with codepoint_to_unicode.
if (cp > 0x1f and cp != 0x22 and cp != 0x5c)
// code points in the Basic Multilingual Plane can be
// represented with one \uxxxx sequence
json_text += codepoint_to_unicode(cp);
}
else
{
unescaped_string = dummy_lexer.to_unicode(cp);
// To escape an extended character that is not in the Basic
// Multilingual Plane, the character is represented as a
// 12-character sequence, encoding the UTF-16 surrogate pair
const auto codepoint1 = 0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu);
const auto codepoint2 = 0xdc00u + ((cp - 0x10000u) & 0x3ffu);
json_text += codepoint_to_unicode(codepoint1) + codepoint_to_unicode(codepoint2);
}
json_text += "\"";
CAPTURE(json_text);
CHECK_NOTHROW(json::parse(json_text));
}
else
}
#if 0
SECTION("incorrect sequences")
{
SECTION("high surrogate without low surrogate")
{
// To escape an extended character that is not in the Basic
// Multilingual Plane, the character is represented as a
// 12-character sequence, encoding the UTF-16 surrogate pair
const auto codepoint1 = 0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu);
const auto codepoint2 = 0xdc00u + ((cp - 0x10000u) & 0x3ffu);
escaped_string = codepoint_to_unicode(codepoint1);
escaped_string += codepoint_to_unicode(codepoint2);
unescaped_string += dummy_lexer.to_unicode(codepoint1, codepoint2);
// D800..DBFF are high surrogates and must be followed by low
// surrogates DC00..DFFF; here, nothing follows
for (std::size_t cp = 0xD800u; cp <= 0xDBFFu; ++cp)
{
std::string json_text = "\"" + codepoint_to_unicode(cp) + "\"";
CAPTURE(json_text);
CHECK_THROWS_AS(json::parse(json_text), json::parse_error);
}
}
// all other code points are valid and must not yield parse errors
CAPTURE(cp);
CAPTURE(escaped_string);
CAPTURE(unescaped_string);
SECTION("high surrogate with wrong low surrogate")
{
// D800..DBFF are high surrogates and must be followed by low
// surrogates DC00..DFFF; here a different sequence follows
for (std::size_t cp1 = 0xD800u; cp1 <= 0xDBFFu; ++cp1)
{
for (std::size_t cp2 = 0x0000u; cp2 <= 0xFFFFu; ++cp2)
{
if (0xDC00u <= cp2 and cp2 <= 0xDFFFu)
{
continue;
}
std::string json_text = "\"" + codepoint_to_unicode(cp1) + codepoint_to_unicode(cp2) + "\"";
CAPTURE(json_text);
CHECK_THROWS_AS(json::parse(json_text), json::parse_error);
}
}
}
json j1, j2, j3, j4;
CHECK_NOTHROW(j1 = json::parse("\"" + escaped_string + "\""));
CHECK_NOTHROW(j2 = json::parse(j1.dump()));
CHECK(j1 == j2);
SECTION("low surrogate without high surrogate")
{
// low surrogates DC00..DFFF must follow high surrogates; here,
// they occur alone
for (std::size_t cp = 0xDC00u; cp <= 0xDFFFu; ++cp)
{
std::string json_text = "\"" + codepoint_to_unicode(cp) + "\"";
CAPTURE(json_text);
CHECK_THROWS_AS(json::parse(json_text), json::parse_error);
}
}
CHECK_NOTHROW(j3 = json::parse("\"" + unescaped_string + "\""));
CHECK_NOTHROW(j4 = json::parse(j3.dump()));
CHECK(j3 == j4);
}
#endif
}
SECTION("read all unicode characters")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment