| /* |
| __ _____ _____ _____ |
| __| | __| | | | JSON for Modern C++ (test suite) |
| | | |__ | | | | | | version 3.9.0 |
| |_____|_____|_____|_|___| https://github.com/nlohmann/json |
| |
| Licensed under the MIT License <http://opensource.org/licenses/MIT>. |
| SPDX-License-Identifier: MIT |
| Copyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>. |
| |
| Permission is hereby granted, free of charge, to any person obtaining a copy |
| of this software and associated documentation files (the "Software"), to deal |
| in the Software without restriction, including without limitation the rights |
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| copies of the Software, and to permit persons to whom the Software is |
| furnished to do so, subject to the following conditions: |
| |
| The above copyright notice and this permission notice shall be included in all |
| copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| SOFTWARE. |
| */ |
| |
| #include "doctest_compatibility.h" |
| |
| #define private public |
| #include <nlohmann/json.hpp> |
| using nlohmann::json; |
| #undef private |
| |
| #include <valarray> |
| |
| namespace |
| { |
| class SaxEventLogger |
| { |
| public: |
| bool null() |
| { |
| events.push_back("null()"); |
| return true; |
| } |
| |
| bool boolean(bool val) |
| { |
| events.push_back(val ? "boolean(true)" : "boolean(false)"); |
| return true; |
| } |
| |
| bool number_integer(json::number_integer_t val) |
| { |
| events.push_back("number_integer(" + std::to_string(val) + ")"); |
| return true; |
| } |
| |
| bool number_unsigned(json::number_unsigned_t val) |
| { |
| events.push_back("number_unsigned(" + std::to_string(val) + ")"); |
| return true; |
| } |
| |
| bool number_float(json::number_float_t, const std::string& s) |
| { |
| events.push_back("number_float(" + s + ")"); |
| return true; |
| } |
| |
| bool string(std::string& val) |
| { |
| events.push_back("string(" + val + ")"); |
| return true; |
| } |
| |
| bool binary(json::binary_t& val) |
| { |
| std::string binary_contents = "binary("; |
| std::string comma_space = ""; |
| for (auto b : val) |
| { |
| binary_contents.append(comma_space); |
| binary_contents.append(std::to_string(static_cast<int>(b))); |
| comma_space = ", "; |
| } |
| binary_contents.append(")"); |
| events.push_back(binary_contents); |
| return true; |
| } |
| |
| bool start_object(std::size_t elements) |
| { |
| if (elements == std::size_t(-1)) |
| { |
| events.push_back("start_object()"); |
| } |
| else |
| { |
| events.push_back("start_object(" + std::to_string(elements) + ")"); |
| } |
| return true; |
| } |
| |
| bool key(std::string& val) |
| { |
| events.push_back("key(" + val + ")"); |
| return true; |
| } |
| |
| bool end_object() |
| { |
| events.push_back("end_object()"); |
| return true; |
| } |
| |
| bool start_array(std::size_t elements) |
| { |
| if (elements == std::size_t(-1)) |
| { |
| events.push_back("start_array()"); |
| } |
| else |
| { |
| events.push_back("start_array(" + std::to_string(elements) + ")"); |
| } |
| return true; |
| } |
| |
| bool end_array() |
| { |
| events.push_back("end_array()"); |
| return true; |
| } |
| |
| bool parse_error(std::size_t position, const std::string&, const json::exception&) |
| { |
| errored = true; |
| events.push_back("parse_error(" + std::to_string(position) + ")"); |
| return false; |
| } |
| |
| std::vector<std::string> events {}; |
| bool errored = false; |
| }; |
| |
| class SaxCountdown : public nlohmann::json::json_sax_t |
| { |
| public: |
| explicit SaxCountdown(const int count) : events_left(count) |
| {} |
| |
| bool null() override |
| { |
| return events_left-- > 0; |
| } |
| |
| bool boolean(bool) override |
| { |
| return events_left-- > 0; |
| } |
| |
| bool number_integer(json::number_integer_t) override |
| { |
| return events_left-- > 0; |
| } |
| |
| bool number_unsigned(json::number_unsigned_t) override |
| { |
| return events_left-- > 0; |
| } |
| |
| bool number_float(json::number_float_t, const std::string&) override |
| { |
| return events_left-- > 0; |
| } |
| |
| bool string(std::string&) override |
| { |
| return events_left-- > 0; |
| } |
| |
| bool binary(json::binary_t&) override |
| { |
| return events_left-- > 0; |
| } |
| |
| bool start_object(std::size_t) override |
| { |
| return events_left-- > 0; |
| } |
| |
| bool key(std::string&) override |
| { |
| return events_left-- > 0; |
| } |
| |
| bool end_object() override |
| { |
| return events_left-- > 0; |
| } |
| |
| bool start_array(std::size_t) override |
| { |
| return events_left-- > 0; |
| } |
| |
| bool end_array() override |
| { |
| return events_left-- > 0; |
| } |
| |
| bool parse_error(std::size_t, const std::string&, const json::exception&) override |
| { |
| return false; |
| } |
| |
| private: |
| int events_left = 0; |
| }; |
| |
| json parser_helper(const std::string& s); |
| bool accept_helper(const std::string& s); |
| void comments_helper(const std::string& s); |
| |
| json parser_helper(const std::string& s) |
| { |
| json j; |
| json::parser(nlohmann::detail::input_adapter(s)).parse(true, j); |
| |
| // if this line was reached, no exception occurred |
| // -> check if result is the same without exceptions |
| json j_nothrow; |
| CHECK_NOTHROW(json::parser(nlohmann::detail::input_adapter(s), nullptr, false).parse(true, j_nothrow)); |
| CHECK(j_nothrow == j); |
| |
| json j_sax; |
| nlohmann::detail::json_sax_dom_parser<json> sdp(j_sax); |
| json::sax_parse(s, &sdp); |
| CHECK(j_sax == j); |
| |
| comments_helper(s); |
| |
| return j; |
| } |
| |
| bool accept_helper(const std::string& s) |
| { |
| CAPTURE(s) |
| |
| // 1. parse s without exceptions |
| json j; |
| CHECK_NOTHROW(json::parser(nlohmann::detail::input_adapter(s), nullptr, false).parse(true, j)); |
| const bool ok_noexcept = !j.is_discarded(); |
| |
| // 2. accept s |
| const bool ok_accept = json::parser(nlohmann::detail::input_adapter(s)).accept(true); |
| |
| // 3. check if both approaches come to the same result |
| CHECK(ok_noexcept == ok_accept); |
| |
| // 4. parse with SAX (compare with relaxed accept result) |
| SaxEventLogger el; |
| CHECK_NOTHROW(json::sax_parse(s, &el, json::input_format_t::json, false)); |
| CHECK(json::parser(nlohmann::detail::input_adapter(s)).accept(false) == !el.errored); |
| |
| // 5. parse with simple callback |
| json::parser_callback_t cb = [](int, json::parse_event_t, json&) |
| { |
| return true; |
| }; |
| json j_cb = json::parse(s, cb, false); |
| const bool ok_noexcept_cb = !j_cb.is_discarded(); |
| |
| // 6. check if this approach came to the same result |
| CHECK(ok_noexcept == ok_noexcept_cb); |
| |
| // 7. check if comments are properly ignored |
| if (ok_accept) |
| { |
| comments_helper(s); |
| } |
| |
| // 8. return result |
| return ok_accept; |
| } |
| |
| void comments_helper(const std::string& s) |
| { |
| json _; |
| |
| // parse/accept with default parser |
| CHECK_NOTHROW(_ = json::parse(s)); |
| CHECK(json::accept(s)); |
| |
| // parse/accept while skipping comments |
| CHECK_NOTHROW(_ = json::parse(s, nullptr, false, true)); |
| CHECK(json::accept(s, true)); |
| |
| std::vector<std::string> json_with_comments; |
| |
| // start with a comment |
| json_with_comments.push_back(std::string("// this is a comment\n") + s); |
| json_with_comments.push_back(std::string("/* this is a comment */") + s); |
| // end with a comment |
| json_with_comments.push_back(s + "// this is a comment"); |
| json_with_comments.push_back(s + "/* this is a comment */"); |
| |
| // check all strings |
| for (const auto& json_with_comment : json_with_comments) |
| { |
| CAPTURE(json_with_comment) |
| CHECK_THROWS_AS(_ = json::parse(json_with_comment), json::parse_error); |
| CHECK(!json::accept(json_with_comment)); |
| |
| CHECK_NOTHROW(_ = json::parse(json_with_comment, nullptr, true, true)); |
| CHECK(json::accept(json_with_comment, true)); |
| } |
| } |
| |
| } // namespace |
| |
| TEST_CASE("parser class") |
| { |
| SECTION("parse") |
| { |
| SECTION("null") |
| { |
| CHECK(parser_helper("null") == json(nullptr)); |
| } |
| |
| SECTION("true") |
| { |
| CHECK(parser_helper("true") == json(true)); |
| } |
| |
| SECTION("false") |
| { |
| CHECK(parser_helper("false") == json(false)); |
| } |
| |
| SECTION("array") |
| { |
| SECTION("empty array") |
| { |
| CHECK(parser_helper("[]") == json(json::value_t::array)); |
| CHECK(parser_helper("[ ]") == json(json::value_t::array)); |
| } |
| |
| SECTION("nonempty array") |
| { |
| CHECK(parser_helper("[true, false, null]") == json({true, false, nullptr})); |
| } |
| } |
| |
| SECTION("object") |
| { |
| SECTION("empty object") |
| { |
| CHECK(parser_helper("{}") == json(json::value_t::object)); |
| CHECK(parser_helper("{ }") == json(json::value_t::object)); |
| } |
| |
| SECTION("nonempty object") |
| { |
| CHECK(parser_helper("{\"\": true, \"one\": 1, \"two\": null}") == json({{"", true}, {"one", 1}, {"two", nullptr}})); |
| } |
| } |
| |
| SECTION("string") |
| { |
| // empty string |
| CHECK(parser_helper("\"\"") == json(json::value_t::string)); |
| |
| SECTION("errors") |
| { |
| // error: tab in string |
| CHECK_THROWS_AS(parser_helper("\"\t\""), json::parse_error&); |
| CHECK_THROWS_WITH(parser_helper("\"\t\""), |
| "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t; last read: '\"<U+0009>'"); |
| // error: newline in string |
| CHECK_THROWS_AS(parser_helper("\"\n\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\r\""), json::parse_error&); |
| CHECK_THROWS_WITH(parser_helper("\"\n\""), |
| "[json.exception.parse_error.101] parse error at line 2, column 0: syntax error while parsing value - invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n; last read: '\"<U+000A>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\r\""), |
| "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r; last read: '\"<U+000D>'"); |
| // error: backspace in string |
| CHECK_THROWS_AS(parser_helper("\"\b\""), json::parse_error&); |
| CHECK_THROWS_WITH(parser_helper("\"\b\""), |
| "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b; last read: '\"<U+0008>'"); |
| // improve code coverage |
| CHECK_THROWS_AS(parser_helper("\uFF01"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("[-4:1,]"), json::parse_error&); |
| // unescaped control characters |
| CHECK_THROWS_AS(parser_helper("\"\x00\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x01\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x02\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x03\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x04\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x05\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x06\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x07\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x08\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x09\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x0a\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x0b\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x0c\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x0d\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x0e\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x0f\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x10\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x11\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x12\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x13\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x14\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x15\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x16\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x17\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x18\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x19\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x1a\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x1b\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x1c\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x1d\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x1e\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\x1f\""), json::parse_error&); |
| CHECK_THROWS_WITH(parser_helper("\"\x00\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: missing closing quote; last read: '\"'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x01\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0001 (SOH) must be escaped to \\u0001; last read: '\"<U+0001>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x02\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0002 (STX) must be escaped to \\u0002; last read: '\"<U+0002>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x03\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0003 (ETX) must be escaped to \\u0003; last read: '\"<U+0003>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x04\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0004 (EOT) must be escaped to \\u0004; last read: '\"<U+0004>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x05\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0005 (ENQ) must be escaped to \\u0005; last read: '\"<U+0005>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x06\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0006 (ACK) must be escaped to \\u0006; last read: '\"<U+0006>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x07\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0007 (BEL) must be escaped to \\u0007; last read: '\"<U+0007>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x08\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b; last read: '\"<U+0008>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x09\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t; last read: '\"<U+0009>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x0a\""), "[json.exception.parse_error.101] parse error at line 2, column 0: syntax error while parsing value - invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n; last read: '\"<U+000A>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x0b\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000B (VT) must be escaped to \\u000B; last read: '\"<U+000B>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x0c\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f; last read: '\"<U+000C>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x0d\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r; last read: '\"<U+000D>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x0e\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000E (SO) must be escaped to \\u000E; last read: '\"<U+000E>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x0f\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000F (SI) must be escaped to \\u000F; last read: '\"<U+000F>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x10\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0010 (DLE) must be escaped to \\u0010; last read: '\"<U+0010>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x11\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0011 (DC1) must be escaped to \\u0011; last read: '\"<U+0011>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x12\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0012 (DC2) must be escaped to \\u0012; last read: '\"<U+0012>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x13\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0013 (DC3) must be escaped to \\u0013; last read: '\"<U+0013>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x14\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0014 (DC4) must be escaped to \\u0014; last read: '\"<U+0014>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x15\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0015 (NAK) must be escaped to \\u0015; last read: '\"<U+0015>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x16\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0016 (SYN) must be escaped to \\u0016; last read: '\"<U+0016>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x17\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0017 (ETB) must be escaped to \\u0017; last read: '\"<U+0017>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x18\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0018 (CAN) must be escaped to \\u0018; last read: '\"<U+0018>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x19\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0019 (EM) must be escaped to \\u0019; last read: '\"<U+0019>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x1a\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001A (SUB) must be escaped to \\u001A; last read: '\"<U+001A>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x1b\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001B (ESC) must be escaped to \\u001B; last read: '\"<U+001B>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x1c\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001C (FS) must be escaped to \\u001C; last read: '\"<U+001C>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x1d\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001D (GS) must be escaped to \\u001D; last read: '\"<U+001D>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x1e\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001E (RS) must be escaped to \\u001E; last read: '\"<U+001E>'"); |
| CHECK_THROWS_WITH(parser_helper("\"\x1f\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001F (US) must be escaped to \\u001F; last read: '\"<U+001F>'"); |
| |
| SECTION("additional test for null byte") |
| { |
| // The test above for the null byte is wrong, because passing |
| // a string to the parser only reads int until it encounters |
| // a null byte. This test inserts the null byte later on and |
| // uses an iterator range. |
| std::string s = "\"1\""; |
| s[1] = '\0'; |
| json _; |
| CHECK_THROWS_AS(_ = json::parse(s.begin(), s.end()), json::parse_error&); |
| CHECK_THROWS_WITH(_ = json::parse(s.begin(), s.end()), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0000 (NUL) must be escaped to \\u0000; last read: '\"<U+0000>'"); |
| } |
| } |
| |
| SECTION("escaped") |
| { |
| // quotation mark "\"" |
| auto r1 = R"("\"")"_json; |
| CHECK(parser_helper("\"\\\"\"") == r1); |
| // reverse solidus "\\" |
| auto r2 = R"("\\")"_json; |
| CHECK(parser_helper("\"\\\\\"") == r2); |
| // solidus |
| CHECK(parser_helper("\"\\/\"") == R"("/")"_json); |
| // backspace |
| CHECK(parser_helper("\"\\b\"") == json("\b")); |
| // formfeed |
| CHECK(parser_helper("\"\\f\"") == json("\f")); |
| // newline |
| CHECK(parser_helper("\"\\n\"") == json("\n")); |
| // carriage return |
| CHECK(parser_helper("\"\\r\"") == json("\r")); |
| // horizontal tab |
| CHECK(parser_helper("\"\\t\"") == json("\t")); |
| |
| CHECK(parser_helper("\"\\u0001\"").get<json::string_t>() == "\x01"); |
| CHECK(parser_helper("\"\\u000a\"").get<json::string_t>() == "\n"); |
| CHECK(parser_helper("\"\\u00b0\"").get<json::string_t>() == "°"); |
| CHECK(parser_helper("\"\\u0c00\"").get<json::string_t>() == "à°€"); |
| CHECK(parser_helper("\"\\ud000\"").get<json::string_t>() == "퀀"); |
| CHECK(parser_helper("\"\\u000E\"").get<json::string_t>() == "\x0E"); |
| CHECK(parser_helper("\"\\u00F0\"").get<json::string_t>() == "ð"); |
| CHECK(parser_helper("\"\\u0100\"").get<json::string_t>() == "Ä€"); |
| CHECK(parser_helper("\"\\u2000\"").get<json::string_t>() == " "); |
| CHECK(parser_helper("\"\\uFFFF\"").get<json::string_t>() == "ï¿¿"); |
| CHECK(parser_helper("\"\\u20AC\"").get<json::string_t>() == "€"); |
| CHECK(parser_helper("\"€\"").get<json::string_t>() == "€"); |
| CHECK(parser_helper("\"🎈\"").get<json::string_t>() == "🎈"); |
| |
| CHECK(parser_helper("\"\\ud80c\\udc60\"").get<json::string_t>() == u8"\U00013060"); |
| CHECK(parser_helper("\"\\ud83c\\udf1e\"").get<json::string_t>() == "🌞"); |
| } |
| } |
| |
| SECTION("number") |
| { |
| SECTION("integers") |
| { |
| SECTION("without exponent") |
| { |
| CHECK(parser_helper("-128") == json(-128)); |
| CHECK(parser_helper("-0") == json(-0)); |
| CHECK(parser_helper("0") == json(0)); |
| CHECK(parser_helper("128") == json(128)); |
| } |
| |
| SECTION("with exponent") |
| { |
| CHECK(parser_helper("0e1") == json(0e1)); |
| CHECK(parser_helper("0E1") == json(0e1)); |
| |
| CHECK(parser_helper("10000E-4") == json(10000e-4)); |
| CHECK(parser_helper("10000E-3") == json(10000e-3)); |
| CHECK(parser_helper("10000E-2") == json(10000e-2)); |
| CHECK(parser_helper("10000E-1") == json(10000e-1)); |
| CHECK(parser_helper("10000E0") == json(10000e0)); |
| CHECK(parser_helper("10000E1") == json(10000e1)); |
| CHECK(parser_helper("10000E2") == json(10000e2)); |
| CHECK(parser_helper("10000E3") == json(10000e3)); |
| CHECK(parser_helper("10000E4") == json(10000e4)); |
| |
| CHECK(parser_helper("10000e-4") == json(10000e-4)); |
| CHECK(parser_helper("10000e-3") == json(10000e-3)); |
| CHECK(parser_helper("10000e-2") == json(10000e-2)); |
| CHECK(parser_helper("10000e-1") == json(10000e-1)); |
| CHECK(parser_helper("10000e0") == json(10000e0)); |
| CHECK(parser_helper("10000e1") == json(10000e1)); |
| CHECK(parser_helper("10000e2") == json(10000e2)); |
| CHECK(parser_helper("10000e3") == json(10000e3)); |
| CHECK(parser_helper("10000e4") == json(10000e4)); |
| |
| CHECK(parser_helper("-0e1") == json(-0e1)); |
| CHECK(parser_helper("-0E1") == json(-0e1)); |
| CHECK(parser_helper("-0E123") == json(-0e123)); |
| |
| // numbers after exponent |
| CHECK(parser_helper("10E0") == json(10e0)); |
| CHECK(parser_helper("10E1") == json(10e1)); |
| CHECK(parser_helper("10E2") == json(10e2)); |
| CHECK(parser_helper("10E3") == json(10e3)); |
| CHECK(parser_helper("10E4") == json(10e4)); |
| CHECK(parser_helper("10E5") == json(10e5)); |
| CHECK(parser_helper("10E6") == json(10e6)); |
| CHECK(parser_helper("10E7") == json(10e7)); |
| CHECK(parser_helper("10E8") == json(10e8)); |
| CHECK(parser_helper("10E9") == json(10e9)); |
| CHECK(parser_helper("10E+0") == json(10e0)); |
| CHECK(parser_helper("10E+1") == json(10e1)); |
| CHECK(parser_helper("10E+2") == json(10e2)); |
| CHECK(parser_helper("10E+3") == json(10e3)); |
| CHECK(parser_helper("10E+4") == json(10e4)); |
| CHECK(parser_helper("10E+5") == json(10e5)); |
| CHECK(parser_helper("10E+6") == json(10e6)); |
| CHECK(parser_helper("10E+7") == json(10e7)); |
| CHECK(parser_helper("10E+8") == json(10e8)); |
| CHECK(parser_helper("10E+9") == json(10e9)); |
| CHECK(parser_helper("10E-1") == json(10e-1)); |
| CHECK(parser_helper("10E-2") == json(10e-2)); |
| CHECK(parser_helper("10E-3") == json(10e-3)); |
| CHECK(parser_helper("10E-4") == json(10e-4)); |
| CHECK(parser_helper("10E-5") == json(10e-5)); |
| CHECK(parser_helper("10E-6") == json(10e-6)); |
| CHECK(parser_helper("10E-7") == json(10e-7)); |
| CHECK(parser_helper("10E-8") == json(10e-8)); |
| CHECK(parser_helper("10E-9") == json(10e-9)); |
| } |
| |
| 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(parser_helper("-9007199254740991").get<int64_t>() == -9007199254740991); |
| // (2**53)-1 |
| CHECK(parser_helper("9007199254740991").get<int64_t>() == 9007199254740991); |
| } |
| |
| 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(parser_helper("-9223372036854775808").get<int64_t>() == -9223372036854775807 - 1); |
| // (2**63)-1 |
| CHECK(parser_helper("9223372036854775807").get<int64_t>() == 9223372036854775807); |
| // (2**64)-1 |
| CHECK(parser_helper("18446744073709551615").get<uint64_t>() == 18446744073709551615u); |
| } |
| } |
| |
| SECTION("floating-point") |
| { |
| SECTION("without exponent") |
| { |
| CHECK(parser_helper("-128.5") == json(-128.5)); |
| CHECK(parser_helper("0.999") == json(0.999)); |
| CHECK(parser_helper("128.5") == json(128.5)); |
| CHECK(parser_helper("-0.0") == json(-0.0)); |
| } |
| |
| SECTION("with exponent") |
| { |
| CHECK(parser_helper("-128.5E3") == json(-128.5E3)); |
| CHECK(parser_helper("-128.5E-3") == json(-128.5E-3)); |
| CHECK(parser_helper("-0.0e1") == json(-0.0e1)); |
| CHECK(parser_helper("-0.0E1") == json(-0.0e1)); |
| } |
| } |
| |
| SECTION("overflow") |
| { |
| // overflows during parsing yield an exception |
| CHECK_THROWS_AS(parser_helper("1.18973e+4932") == json(), json::out_of_range&); |
| CHECK_THROWS_WITH(parser_helper("1.18973e+4932") == json(), |
| "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'"); |
| } |
| |
| SECTION("invalid numbers") |
| { |
| CHECK_THROWS_AS(parser_helper("01"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("--1"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("1."), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("1E"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("1E-"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("1.E1"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("-1E"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("-0E#"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("-0E-#"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("-0#"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("-0.0:"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("-0.0Z"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("-0E123:"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("-0e0-:"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("-0e-:"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("-0f"), json::parse_error&); |
| |
| // numbers must not begin with "+" |
| CHECK_THROWS_AS(parser_helper("+1"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("+0"), json::parse_error&); |
| |
| CHECK_THROWS_WITH(parser_helper("01"), |
| "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - unexpected number literal; expected end of input"); |
| CHECK_THROWS_WITH(parser_helper("-01"), |
| "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - unexpected number literal; expected end of input"); |
| CHECK_THROWS_WITH(parser_helper("--1"), |
| "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '--'"); |
| CHECK_THROWS_WITH(parser_helper("1."), |
| "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected digit after '.'; last read: '1.'"); |
| CHECK_THROWS_WITH(parser_helper("1E"), |
| "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1E'"); |
| CHECK_THROWS_WITH(parser_helper("1E-"), |
| "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid number; expected digit after exponent sign; last read: '1E-'"); |
| CHECK_THROWS_WITH(parser_helper("1.E1"), |
| "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected digit after '.'; last read: '1.E'"); |
| CHECK_THROWS_WITH(parser_helper("-1E"), |
| "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '-1E'"); |
| CHECK_THROWS_WITH(parser_helper("-0E#"), |
| "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '-0E#'"); |
| CHECK_THROWS_WITH(parser_helper("-0E-#"), |
| "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid number; expected digit after exponent sign; last read: '-0E-#'"); |
| CHECK_THROWS_WITH(parser_helper("-0#"), |
| "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid literal; last read: '-0#'; expected end of input"); |
| CHECK_THROWS_WITH(parser_helper("-0.0:"), |
| "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - unexpected ':'; expected end of input"); |
| CHECK_THROWS_WITH(parser_helper("-0.0Z"), |
| "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid literal; last read: '-0.0Z'; expected end of input"); |
| CHECK_THROWS_WITH(parser_helper("-0E123:"), |
| "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - unexpected ':'; expected end of input"); |
| CHECK_THROWS_WITH(parser_helper("-0e0-:"), |
| "[json.exception.parse_error.101] parse error at line 1, column 6: syntax error while parsing value - invalid number; expected digit after '-'; last read: '-:'; expected end of input"); |
| CHECK_THROWS_WITH(parser_helper("-0e-:"), |
| "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid number; expected digit after exponent sign; last read: '-0e-:'"); |
| CHECK_THROWS_WITH(parser_helper("-0f"), |
| "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: '-0f'; expected end of input"); |
| } |
| } |
| } |
| |
| SECTION("accept") |
| { |
| SECTION("null") |
| { |
| CHECK(accept_helper("null")); |
| } |
| |
| SECTION("true") |
| { |
| CHECK(accept_helper("true")); |
| } |
| |
| SECTION("false") |
| { |
| CHECK(accept_helper("false")); |
| } |
| |
| SECTION("array") |
| { |
| SECTION("empty array") |
| { |
| CHECK(accept_helper("[]")); |
| CHECK(accept_helper("[ ]")); |
| } |
| |
| SECTION("nonempty array") |
| { |
| CHECK(accept_helper("[true, false, null]")); |
| } |
| } |
| |
| SECTION("object") |
| { |
| SECTION("empty object") |
| { |
| CHECK(accept_helper("{}")); |
| CHECK(accept_helper("{ }")); |
| } |
| |
| SECTION("nonempty object") |
| { |
| CHECK(accept_helper("{\"\": true, \"one\": 1, \"two\": null}")); |
| } |
| } |
| |
| SECTION("string") |
| { |
| // empty string |
| CHECK(accept_helper("\"\"")); |
| |
| SECTION("errors") |
| { |
| // error: tab in string |
| CHECK(accept_helper("\"\t\"") == false); |
| // error: newline in string |
| CHECK(accept_helper("\"\n\"") == false); |
| CHECK(accept_helper("\"\r\"") == false); |
| // error: backspace in string |
| CHECK(accept_helper("\"\b\"") == false); |
| // improve code coverage |
| CHECK(accept_helper("\uFF01") == false); |
| CHECK(accept_helper("[-4:1,]") == false); |
| // unescaped control characters |
| CHECK(accept_helper("\"\x00\"") == false); |
| CHECK(accept_helper("\"\x01\"") == false); |
| CHECK(accept_helper("\"\x02\"") == false); |
| CHECK(accept_helper("\"\x03\"") == false); |
| CHECK(accept_helper("\"\x04\"") == false); |
| CHECK(accept_helper("\"\x05\"") == false); |
| CHECK(accept_helper("\"\x06\"") == false); |
| CHECK(accept_helper("\"\x07\"") == false); |
| CHECK(accept_helper("\"\x08\"") == false); |
| CHECK(accept_helper("\"\x09\"") == false); |
| CHECK(accept_helper("\"\x0a\"") == false); |
| CHECK(accept_helper("\"\x0b\"") == false); |
| CHECK(accept_helper("\"\x0c\"") == false); |
| CHECK(accept_helper("\"\x0d\"") == false); |
| CHECK(accept_helper("\"\x0e\"") == false); |
| CHECK(accept_helper("\"\x0f\"") == false); |
| CHECK(accept_helper("\"\x10\"") == false); |
| CHECK(accept_helper("\"\x11\"") == false); |
| CHECK(accept_helper("\"\x12\"") == false); |
| CHECK(accept_helper("\"\x13\"") == false); |
| CHECK(accept_helper("\"\x14\"") == false); |
| CHECK(accept_helper("\"\x15\"") == false); |
| CHECK(accept_helper("\"\x16\"") == false); |
| CHECK(accept_helper("\"\x17\"") == false); |
| CHECK(accept_helper("\"\x18\"") == false); |
| CHECK(accept_helper("\"\x19\"") == false); |
| CHECK(accept_helper("\"\x1a\"") == false); |
| CHECK(accept_helper("\"\x1b\"") == false); |
| CHECK(accept_helper("\"\x1c\"") == false); |
| CHECK(accept_helper("\"\x1d\"") == false); |
| CHECK(accept_helper("\"\x1e\"") == false); |
| CHECK(accept_helper("\"\x1f\"") == false); |
| } |
| |
| SECTION("escaped") |
| { |
| // quotation mark "\"" |
| auto r1 = R"("\"")"_json; |
| CHECK(accept_helper("\"\\\"\"")); |
| // reverse solidus "\\" |
| auto r2 = R"("\\")"_json; |
| CHECK(accept_helper("\"\\\\\"")); |
| // solidus |
| CHECK(accept_helper("\"\\/\"")); |
| // backspace |
| CHECK(accept_helper("\"\\b\"")); |
| // formfeed |
| CHECK(accept_helper("\"\\f\"")); |
| // newline |
| CHECK(accept_helper("\"\\n\"")); |
| // carriage return |
| CHECK(accept_helper("\"\\r\"")); |
| // horizontal tab |
| CHECK(accept_helper("\"\\t\"")); |
| |
| CHECK(accept_helper("\"\\u0001\"")); |
| CHECK(accept_helper("\"\\u000a\"")); |
| CHECK(accept_helper("\"\\u00b0\"")); |
| CHECK(accept_helper("\"\\u0c00\"")); |
| CHECK(accept_helper("\"\\ud000\"")); |
| CHECK(accept_helper("\"\\u000E\"")); |
| CHECK(accept_helper("\"\\u00F0\"")); |
| CHECK(accept_helper("\"\\u0100\"")); |
| CHECK(accept_helper("\"\\u2000\"")); |
| CHECK(accept_helper("\"\\uFFFF\"")); |
| CHECK(accept_helper("\"\\u20AC\"")); |
| CHECK(accept_helper("\"€\"")); |
| CHECK(accept_helper("\"🎈\"")); |
| |
| CHECK(accept_helper("\"\\ud80c\\udc60\"")); |
| CHECK(accept_helper("\"\\ud83c\\udf1e\"")); |
| } |
| } |
| |
| SECTION("number") |
| { |
| SECTION("integers") |
| { |
| SECTION("without exponent") |
| { |
| CHECK(accept_helper("-128")); |
| CHECK(accept_helper("-0")); |
| CHECK(accept_helper("0")); |
| CHECK(accept_helper("128")); |
| } |
| |
| SECTION("with exponent") |
| { |
| CHECK(accept_helper("0e1")); |
| CHECK(accept_helper("0E1")); |
| |
| CHECK(accept_helper("10000E-4")); |
| CHECK(accept_helper("10000E-3")); |
| CHECK(accept_helper("10000E-2")); |
| CHECK(accept_helper("10000E-1")); |
| CHECK(accept_helper("10000E0")); |
| CHECK(accept_helper("10000E1")); |
| CHECK(accept_helper("10000E2")); |
| CHECK(accept_helper("10000E3")); |
| CHECK(accept_helper("10000E4")); |
| |
| CHECK(accept_helper("10000e-4")); |
| CHECK(accept_helper("10000e-3")); |
| CHECK(accept_helper("10000e-2")); |
| CHECK(accept_helper("10000e-1")); |
| CHECK(accept_helper("10000e0")); |
| CHECK(accept_helper("10000e1")); |
| CHECK(accept_helper("10000e2")); |
| CHECK(accept_helper("10000e3")); |
| CHECK(accept_helper("10000e4")); |
| |
| CHECK(accept_helper("-0e1")); |
| CHECK(accept_helper("-0E1")); |
| CHECK(accept_helper("-0E123")); |
| } |
| |
| 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(accept_helper("-9007199254740991")); |
| // (2**53)-1 |
| CHECK(accept_helper("9007199254740991")); |
| } |
| |
| 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(accept_helper("-9223372036854775808")); |
| // (2**63)-1 |
| CHECK(accept_helper("9223372036854775807")); |
| // (2**64)-1 |
| CHECK(accept_helper("18446744073709551615")); |
| } |
| } |
| |
| SECTION("floating-point") |
| { |
| SECTION("without exponent") |
| { |
| CHECK(accept_helper("-128.5")); |
| CHECK(accept_helper("0.999")); |
| CHECK(accept_helper("128.5")); |
| CHECK(accept_helper("-0.0")); |
| } |
| |
| SECTION("with exponent") |
| { |
| CHECK(accept_helper("-128.5E3")); |
| CHECK(accept_helper("-128.5E-3")); |
| CHECK(accept_helper("-0.0e1")); |
| CHECK(accept_helper("-0.0E1")); |
| } |
| } |
| |
| SECTION("overflow") |
| { |
| // overflows during parsing |
| CHECK(!accept_helper("1.18973e+4932")); |
| } |
| |
| SECTION("invalid numbers") |
| { |
| CHECK(accept_helper("01") == false); |
| CHECK(accept_helper("--1") == false); |
| CHECK(accept_helper("1.") == false); |
| CHECK(accept_helper("1E") == false); |
| CHECK(accept_helper("1E-") == false); |
| CHECK(accept_helper("1.E1") == false); |
| CHECK(accept_helper("-1E") == false); |
| CHECK(accept_helper("-0E#") == false); |
| CHECK(accept_helper("-0E-#") == false); |
| CHECK(accept_helper("-0#") == false); |
| CHECK(accept_helper("-0.0:") == false); |
| CHECK(accept_helper("-0.0Z") == false); |
| CHECK(accept_helper("-0E123:") == false); |
| CHECK(accept_helper("-0e0-:") == false); |
| CHECK(accept_helper("-0e-:") == false); |
| CHECK(accept_helper("-0f") == false); |
| |
| // numbers must not begin with "+" |
| CHECK(accept_helper("+1") == false); |
| CHECK(accept_helper("+0") == false); |
| } |
| } |
| } |
| |
| SECTION("parse errors") |
| { |
| // unexpected end of number |
| CHECK_THROWS_AS(parser_helper("0."), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("-"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("--"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("-0."), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("-."), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("-:"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("0.:"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("e."), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("1e."), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("1e/"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("1e:"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("1E."), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("1E/"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("1E:"), json::parse_error&); |
| CHECK_THROWS_WITH(parser_helper("0."), |
| "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected digit after '.'; last read: '0.'"); |
| CHECK_THROWS_WITH(parser_helper("-"), |
| "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '-'"); |
| CHECK_THROWS_WITH(parser_helper("--"), |
| "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '--'"); |
| CHECK_THROWS_WITH(parser_helper("-0."), |
| "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid number; expected digit after '.'; last read: '-0.'"); |
| CHECK_THROWS_WITH(parser_helper("-."), |
| "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '-.'"); |
| CHECK_THROWS_WITH(parser_helper("-:"), |
| "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '-:'"); |
| CHECK_THROWS_WITH(parser_helper("0.:"), |
| "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected digit after '.'; last read: '0.:'"); |
| CHECK_THROWS_WITH(parser_helper("e."), |
| "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - invalid literal; last read: 'e'"); |
| CHECK_THROWS_WITH(parser_helper("1e."), |
| "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1e.'"); |
| CHECK_THROWS_WITH(parser_helper("1e/"), |
| "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1e/'"); |
| CHECK_THROWS_WITH(parser_helper("1e:"), |
| "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1e:'"); |
| CHECK_THROWS_WITH(parser_helper("1E."), |
| "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1E.'"); |
| CHECK_THROWS_WITH(parser_helper("1E/"), |
| "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1E/'"); |
| CHECK_THROWS_WITH(parser_helper("1E:"), |
| "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1E:'"); |
| |
| // unexpected end of null |
| CHECK_THROWS_AS(parser_helper("n"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("nu"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("nul"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("nulk"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("nulm"), json::parse_error&); |
| CHECK_THROWS_WITH(parser_helper("n"), |
| "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid literal; last read: 'n'"); |
| CHECK_THROWS_WITH(parser_helper("nu"), |
| "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid literal; last read: 'nu'"); |
| CHECK_THROWS_WITH(parser_helper("nul"), |
| "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'nul'"); |
| CHECK_THROWS_WITH(parser_helper("nulk"), |
| "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'nulk'"); |
| CHECK_THROWS_WITH(parser_helper("nulm"), |
| "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'nulm'"); |
| |
| // unexpected end of true |
| CHECK_THROWS_AS(parser_helper("t"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("tr"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("tru"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("trud"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("truf"), json::parse_error&); |
| CHECK_THROWS_WITH(parser_helper("t"), |
| "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid literal; last read: 't'"); |
| CHECK_THROWS_WITH(parser_helper("tr"), |
| "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid literal; last read: 'tr'"); |
| CHECK_THROWS_WITH(parser_helper("tru"), |
| "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'tru'"); |
| CHECK_THROWS_WITH(parser_helper("trud"), |
| "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'trud'"); |
| CHECK_THROWS_WITH(parser_helper("truf"), |
| "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'truf'"); |
| |
| // unexpected end of false |
| CHECK_THROWS_AS(parser_helper("f"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("fa"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("fal"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("fals"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("falsd"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("falsf"), json::parse_error&); |
| CHECK_THROWS_WITH(parser_helper("f"), |
| "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid literal; last read: 'f'"); |
| CHECK_THROWS_WITH(parser_helper("fa"), |
| "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid literal; last read: 'fa'"); |
| CHECK_THROWS_WITH(parser_helper("fal"), |
| "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'fal'"); |
| CHECK_THROWS_WITH(parser_helper("fals"), |
| "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid literal; last read: 'fals'"); |
| CHECK_THROWS_WITH(parser_helper("falsd"), |
| "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid literal; last read: 'falsd'"); |
| CHECK_THROWS_WITH(parser_helper("falsf"), |
| "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid literal; last read: 'falsf'"); |
| |
| // missing/unexpected end of array |
| CHECK_THROWS_AS(parser_helper("["), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("[1"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("[1,"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("[1,]"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("]"), json::parse_error&); |
| CHECK_THROWS_WITH(parser_helper("["), |
| "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"); |
| CHECK_THROWS_WITH(parser_helper("[1"), |
| "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing array - unexpected end of input; expected ']'"); |
| CHECK_THROWS_WITH(parser_helper("[1,"), |
| "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"); |
| CHECK_THROWS_WITH(parser_helper("[1,]"), |
| "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal"); |
| CHECK_THROWS_WITH(parser_helper("]"), |
| "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal"); |
| |
| // missing/unexpected end of object |
| CHECK_THROWS_AS(parser_helper("{"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("{\"foo\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("{\"foo\":"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("{\"foo\":}"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("{\"foo\":1,}"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("}"), json::parse_error&); |
| CHECK_THROWS_WITH(parser_helper("{"), |
| "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing object key - unexpected end of input; expected string literal"); |
| CHECK_THROWS_WITH(parser_helper("{\"foo\""), |
| "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing object separator - unexpected end of input; expected ':'"); |
| CHECK_THROWS_WITH(parser_helper("{\"foo\":"), |
| "[json.exception.parse_error.101] parse error at line 1, column 8: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"); |
| CHECK_THROWS_WITH(parser_helper("{\"foo\":}"), |
| "[json.exception.parse_error.101] parse error at line 1, column 8: syntax error while parsing value - unexpected '}'; expected '[', '{', or a literal"); |
| CHECK_THROWS_WITH(parser_helper("{\"foo\":1,}"), |
| "[json.exception.parse_error.101] parse error at line 1, column 10: syntax error while parsing object key - unexpected '}'; expected string literal"); |
| CHECK_THROWS_WITH(parser_helper("}"), |
| "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected '}'; expected '[', '{', or a literal"); |
| |
| // missing/unexpected end of string |
| CHECK_THROWS_AS(parser_helper("\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\\\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\\u\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\\u0\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\\u01\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\\u012\""), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\\u"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\\u0"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\\u01"), json::parse_error&); |
| CHECK_THROWS_AS(parser_helper("\"\\u012"), json::parse_error&); |
| CHECK_THROWS_WITH(parser_helper("\""), |
| "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: missing closing quote; last read: '\"'"); |
| CHECK_THROWS_WITH(parser_helper("\"\\\""), |
| "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid string: missing closing quote; last read: '\"\\\"'"); |
| CHECK_THROWS_WITH(parser_helper("\"\\u\""), |
| "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u\"'"); |
| CHECK_THROWS_WITH(parser_helper("\"\\u0\""), |
| "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u0\"'"); |
| CHECK_THROWS_WITH(parser_helper("\"\\u01\""), |
| "[json.exception.parse_error.101] parse error at line 1, column 6: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u01\"'"); |
| CHECK_THROWS_WITH(parser_helper("\"\\u012\""), |
| "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u012\"'"); |
| CHECK_THROWS_WITH(parser_helper("\"\\u"), |
| "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u'"); |
| CHECK_THROWS_WITH(parser_helper("\"\\u0"), |
| "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u0'"); |
| CHECK_THROWS_WITH(parser_helper("\"\\u01"), |
| "[json.exception.parse_error.101] parse error at line 1, column 6: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u01'"); |
| CHECK_THROWS_WITH(parser_helper("\"\\u012"), |
| "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u012'"); |
| |
| // 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_NOTHROW(parser_helper(s.c_str())); |
| 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_THROWS_AS(parser_helper(s.c_str()), json::parse_error&); |
| // only check error message if c is not a control character |
| if (c > 0x1f) |
| { |
| CHECK_THROWS_WITH_STD_STR(parser_helper(s.c_str()), |
| "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid string: forbidden character after backslash; last read: '\"\\" + std::string(1, static_cast<char>(c)) + "'"); |
| } |
| 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_NOTHROW(parser_helper(s1.c_str())); |
| CAPTURE(s2) |
| CHECK_NOTHROW(parser_helper(s2.c_str())); |
| CAPTURE(s3) |
| CHECK_NOTHROW(parser_helper(s3.c_str())); |
| CAPTURE(s4) |
| CHECK_NOTHROW(parser_helper(s4.c_str())); |
| } |
| else |
| { |
| CAPTURE(s1) |
| CHECK_THROWS_AS(parser_helper(s1.c_str()), json::parse_error&); |
| // only check error message if c is not a control character |
| if (c > 0x1f) |
| { |
| CHECK_THROWS_WITH_STD_STR(parser_helper(s1.c_str()), |
| "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '" + s1.substr(0, 7) + "'"); |
| } |
| |
| CAPTURE(s2) |
| CHECK_THROWS_AS(parser_helper(s2.c_str()), json::parse_error&); |
| // only check error message if c is not a control character |
| if (c > 0x1f) |
| { |
| CHECK_THROWS_WITH_STD_STR(parser_helper(s2.c_str()), |
| "[json.exception.parse_error.101] parse error at line 1, column 6: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '" + s2.substr(0, 6) + "'"); |
| } |
| |
| CAPTURE(s3) |
| CHECK_THROWS_AS(parser_helper(s3.c_str()), json::parse_error&); |
| // only check error message if c is not a control character |
| if (c > 0x1f) |
| { |
| CHECK_THROWS_WITH_STD_STR(parser_helper(s3.c_str()), |
| "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '" + s3.substr(0, 5) + "'"); |
| } |
| |
| CAPTURE(s4) |
| CHECK_THROWS_AS(parser_helper(s4.c_str()), json::parse_error&); |
| // only check error message if c is not a control character |
| if (c > 0x1f) |
| { |
| CHECK_THROWS_WITH_STD_STR(parser_helper(s4.c_str()), |
| "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '" + s4.substr(0, 4) + "'"); |
| } |
| } |
| } |
| } |
| |
| json _; |
| |
| // 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.101] parse error at line 1, column 8: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF 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.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD80C\\uD80C'"); |
| CHECK_THROWS_WITH(_ = json::parse("\"\\uD80C\\u0000\""), |
| "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD80C\\u0000'"); |
| CHECK_THROWS_WITH(_ = json::parse("\"\\uD80C\\uFFFF\""), |
| "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD80C\\uFFFF'"); |
| } |
| |
| SECTION("parse errors (accept)") |
| { |
| // unexpected end of number |
| CHECK(accept_helper("0.") == false); |
| CHECK(accept_helper("-") == false); |
| CHECK(accept_helper("--") == false); |
| CHECK(accept_helper("-0.") == false); |
| CHECK(accept_helper("-.") == false); |
| CHECK(accept_helper("-:") == false); |
| CHECK(accept_helper("0.:") == false); |
| CHECK(accept_helper("e.") == false); |
| CHECK(accept_helper("1e.") == false); |
| CHECK(accept_helper("1e/") == false); |
| CHECK(accept_helper("1e:") == false); |
| CHECK(accept_helper("1E.") == false); |
| CHECK(accept_helper("1E/") == false); |
| CHECK(accept_helper("1E:") == false); |
| |
| // unexpected end of null |
| CHECK(accept_helper("n") == false); |
| CHECK(accept_helper("nu") == false); |
| CHECK(accept_helper("nul") == false); |
| |
| // unexpected end of true |
| CHECK(accept_helper("t") == false); |
| CHECK(accept_helper("tr") == false); |
| CHECK(accept_helper("tru") == false); |
| |
| // unexpected end of false |
| CHECK(accept_helper("f") == false); |
| CHECK(accept_helper("fa") == false); |
| CHECK(accept_helper("fal") == false); |
| CHECK(accept_helper("fals") == false); |
| |
| // missing/unexpected end of array |
| CHECK(accept_helper("[") == false); |
| CHECK(accept_helper("[1") == false); |
| CHECK(accept_helper("[1,") == false); |
| CHECK(accept_helper("[1,]") == false); |
| CHECK(accept_helper("]") == false); |
| |
| // missing/unexpected end of object |
| CHECK(accept_helper("{") == false); |
| CHECK(accept_helper("{\"foo\"") == false); |
| CHECK(accept_helper("{\"foo\":") == false); |
| CHECK(accept_helper("{\"foo\":}") == false); |
| CHECK(accept_helper("{\"foo\":1,}") == false); |
| CHECK(accept_helper("}") == false); |
| |
| // missing/unexpected end of string |
| CHECK(accept_helper("\"") == false); |
| CHECK(accept_helper("\"\\\"") == false); |
| CHECK(accept_helper("\"\\u\"") == false); |
| CHECK(accept_helper("\"\\u0\"") == false); |
| CHECK(accept_helper("\"\\u01\"") == false); |
| CHECK(accept_helper("\"\\u012\"") == false); |
| CHECK(accept_helper("\"\\u") == false); |
| CHECK(accept_helper("\"\\u0") == false); |
| CHECK(accept_helper("\"\\u01") == false); |
| CHECK(accept_helper("\"\\u012") == false); |
| |
| // unget of newline |
| CHECK(parser_helper("\n123\n") == 123); |
| |
| // 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(json::parser(nlohmann::detail::input_adapter(std::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(json::parser(nlohmann::detail::input_adapter(std::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(json::parser(nlohmann::detail::input_adapter(std::string(s1.c_str()))).accept()); |
| CAPTURE(s2) |
| CHECK(json::parser(nlohmann::detail::input_adapter(std::string(s2.c_str()))).accept()); |
| CAPTURE(s3) |
| CHECK(json::parser(nlohmann::detail::input_adapter(std::string(s3.c_str()))).accept()); |
| CAPTURE(s4) |
| CHECK(json::parser(nlohmann::detail::input_adapter(std::string(s4.c_str()))).accept()); |
| } |
| else |
| { |
| CAPTURE(s1) |
| CHECK(json::parser(nlohmann::detail::input_adapter(std::string(s1.c_str()))).accept() == false); |
| |
| CAPTURE(s2) |
| CHECK(json::parser(nlohmann::detail::input_adapter(std::string(s2.c_str()))).accept() == false); |
| |
| CAPTURE(s3) |
| CHECK(json::parser(nlohmann::detail::input_adapter(std::string(s3.c_str()))).accept() == false); |
| |
| CAPTURE(s4) |
| CHECK(json::parser(nlohmann::detail::input_adapter(std::string(s4.c_str()))).accept() == false); |
| } |
| } |
| } |
| |
| // missing part of a surrogate pair |
| CHECK(accept_helper("\"\\uD80C\"") == false); |
| // invalid surrogate pair |
| CHECK(accept_helper("\"\\uD80C\\uD80C\"") == false); |
| CHECK(accept_helper("\"\\uD80C\\u0000\"") == false); |
| CHECK(accept_helper("\"\\uD80C\\uFFFF\"") == false); |
| } |
| |
| SECTION("tests found by mutate++") |
| { |
| // test case to make sure no comma precedes the first key |
| CHECK_THROWS_AS(parser_helper("{,\"key\": false}"), json::parse_error&); |
| CHECK_THROWS_WITH(parser_helper("{,\"key\": false}"), |
| "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing object key - unexpected ','; expected string literal"); |
| // test case to make sure an object is properly closed |
| CHECK_THROWS_AS(parser_helper("[{\"key\": false true]"), json::parse_error&); |
| CHECK_THROWS_WITH(parser_helper("[{\"key\": false true]"), |
| "[json.exception.parse_error.101] parse error at line 1, column 19: syntax error while parsing object - unexpected true literal; expected '}'"); |
| |
| // test case to make sure the callback is properly evaluated after reading a key |
| { |
| json::parser_callback_t cb = [](int, json::parse_event_t event, json&) |
| { |
| if (event == json::parse_event_t::key) |
| { |
| return false; |
| } |
| else |
| { |
| return true; |
| } |
| }; |
| |
| json x = json::parse("{\"key\": false}", cb); |
| CHECK(x == json::object()); |
| } |
| } |
| |
| SECTION("callback function") |
| { |
| auto s_object = R"( |
| { |
| "foo": 2, |
| "bar": { |
| "baz": 1 |
| } |
| } |
| )"; |
| |
| auto s_array = R"( |
| [1,2,[3,4,5],4,5] |
| )"; |
| |
| auto structured_array = R"( |
| [ |
| 1, |
| { |
| "foo": "bar" |
| }, |
| { |
| "qux": "baz" |
| } |
| ] |
| )"; |
| |
| SECTION("filter nothing") |
| { |
| json j_object = json::parse(s_object, [](int, json::parse_event_t, const json&) |
| { |
| return true; |
| }); |
| |
| CHECK (j_object == json({{"foo", 2}, {"bar", {{"baz", 1}}}})); |
| |
| json j_array = json::parse(s_array, [](int, json::parse_event_t, const json&) |
| { |
| return true; |
| }); |
| |
| CHECK (j_array == json({1, 2, {3, 4, 5}, 4, 5})); |
| } |
| |
| SECTION("filter everything") |
| { |
| json j_object = json::parse(s_object, [](int, json::parse_event_t, const json&) |
| { |
| return false; |
| }); |
| |
| // the top-level object will be discarded, leaving a null |
| CHECK (j_object.is_null()); |
| |
| json j_array = json::parse(s_array, [](int, json::parse_event_t, const json&) |
| { |
| return false; |
| }); |
| |
| // the top-level array will be discarded, leaving a null |
| CHECK (j_array.is_null()); |
| } |
| |
| SECTION("filter specific element") |
| { |
| json j_object = json::parse(s_object, [](int, json::parse_event_t, const json & j) |
| { |
| // filter all number(2) elements |
| if (j == json(2)) |
| { |
| return false; |
| } |
| else |
| { |
| return true; |
| } |
| }); |
| |
| CHECK (j_object == json({{"bar", {{"baz", 1}}}})); |
| |
| json j_array = json::parse(s_array, [](int, json::parse_event_t, const json & j) |
| { |
| if (j == json(2)) |
| { |
| return false; |
| } |
| else |
| { |
| return true; |
| } |
| }); |
| |
| CHECK (j_array == json({1, {3, 4, 5}, 4, 5})); |
| } |
| |
| SECTION("filter object in array") |
| { |
| json j_filtered1 = json::parse(structured_array, [](int, json::parse_event_t e, const json & parsed) |
| { |
| if (e == json::parse_event_t::object_end && parsed.contains("foo")) |
| { |
| return false; |
| } |
| else |
| { |
| return true; |
| } |
| }); |
| |
| // the specified object will be discarded, and removed. |
| CHECK (j_filtered1.size() == 2); |
| CHECK (j_filtered1 == json({1, {{"qux", "baz"}}})); |
| |
| json j_filtered2 = json::parse(structured_array, [](int, json::parse_event_t e, const json& /*parsed*/) |
| { |
| if (e == json::parse_event_t::object_end) |
| { |
| return false; |
| } |
| else |
| { |
| return true; |
| } |
| }); |
| |
| // removed all objects in array. |
| CHECK (j_filtered2.size() == 1); |
| CHECK (j_filtered2 == json({1})); |
| } |
| |
| SECTION("filter specific events") |
| { |
| SECTION("first closing event") |
| { |
| { |
| json j_object = json::parse(s_object, [](int, json::parse_event_t e, const json&) |
| { |
| static bool first = true; |
| if (e == json::parse_event_t::object_end && first) |
| { |
| first = false; |
| return false; |
| } |
| else |
| { |
| return true; |
| } |
| }); |
| |
| // the first completed object will be discarded |
| CHECK (j_object == json({{"foo", 2}})); |
| } |
| |
| { |
| json j_array = json::parse(s_array, [](int, json::parse_event_t e, const json&) |
| { |
| static bool first = true; |
| if (e == json::parse_event_t::array_end && first) |
| { |
| first = false; |
| return false; |
| } |
| else |
| { |
| return true; |
| } |
| }); |
| |
| // the first completed array will be discarded |
| CHECK (j_array == json({1, 2, 4, 5})); |
| } |
| } |
| } |
| |
| SECTION("special cases") |
| { |
| // the following test cases cover the situation in which an empty |
| // object and array is discarded only after the closing character |
| // has been read |
| |
| json j_empty_object = json::parse("{}", [](int, json::parse_event_t e, const json&) |
| { |
| if (e == json::parse_event_t::object_end) |
| { |
| return false; |
| } |
| else |
| { |
| return true; |
| } |
| }); |
| CHECK(j_empty_object == json()); |
| |
| json j_empty_array = json::parse("[]", [](int, json::parse_event_t e, const json&) |
| { |
| if (e == json::parse_event_t::array_end) |
| { |
| return false; |
| } |
| else |
| { |
| return true; |
| } |
| }); |
| CHECK(j_empty_array == json()); |
| } |
| } |
| |
| SECTION("constructing from contiguous containers") |
| { |
| SECTION("from std::vector") |
| { |
| std::vector<uint8_t> v = {'t', 'r', 'u', 'e'}; |
| json j; |
| json::parser(nlohmann::detail::input_adapter(std::begin(v), std::end(v))).parse(true, j); |
| CHECK(j == json(true)); |
| } |
| |
| SECTION("from std::array") |
| { |
| std::array<uint8_t, 5> v { {'t', 'r', 'u', 'e'} }; |
| json j; |
| json::parser(nlohmann::detail::input_adapter(std::begin(v), std::end(v))).parse(true, j); |
| CHECK(j == json(true)); |
| } |
| |
| SECTION("from array") |
| { |
| uint8_t v[] = {'t', 'r', 'u', 'e'}; |
| json j; |
| json::parser(nlohmann::detail::input_adapter(std::begin(v), std::end(v))).parse(true, j); |
| CHECK(j == json(true)); |
| } |
| |
| SECTION("from char literal") |
| { |
| CHECK(parser_helper("true") == json(true)); |
| } |
| |
| SECTION("from std::string") |
| { |
| std::string v = {'t', 'r', 'u', 'e'}; |
| json j; |
| json::parser(nlohmann::detail::input_adapter(std::begin(v), std::end(v))).parse(true, j); |
| CHECK(j == json(true)); |
| } |
| |
| SECTION("from std::initializer_list") |
| { |
| std::initializer_list<uint8_t> v = {'t', 'r', 'u', 'e'}; |
| json j; |
| json::parser(nlohmann::detail::input_adapter(std::begin(v), std::end(v))).parse(true, j); |
| CHECK(j == json(true)); |
| } |
| |
| SECTION("from std::valarray") |
| { |
| std::valarray<uint8_t> v = {'t', 'r', 'u', 'e'}; |
| json j; |
| json::parser(nlohmann::detail::input_adapter(std::begin(v), std::end(v))).parse(true, j); |
| CHECK(j == json(true)); |
| } |
| } |
| |
| SECTION("improve test coverage") |
| { |
| SECTION("parser with callback") |
| { |
| json::parser_callback_t cb = [](int, json::parse_event_t, json&) |
| { |
| return true; |
| }; |
| |
| CHECK(json::parse("{\"foo\": true:", cb, false).is_discarded()); |
| |
| json _; |
| CHECK_THROWS_AS(_ = json::parse("{\"foo\": true:", cb), json::parse_error&); |
| CHECK_THROWS_WITH(_ = json::parse("{\"foo\": true:", cb), |
| "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing object - unexpected ':'; expected '}'"); |
| |
| CHECK_THROWS_AS(_ = json::parse("1.18973e+4932", cb), json::out_of_range&); |
| CHECK_THROWS_WITH(_ = json::parse("1.18973e+4932", cb), |
| "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'"); |
| } |
| |
| SECTION("SAX parser") |
| { |
| SECTION("} without value") |
| { |
| SaxCountdown s(1); |
| CHECK(json::sax_parse("{}", &s) == false); |
| } |
| |
| SECTION("} with value") |
| { |
| SaxCountdown s(3); |
| CHECK(json::sax_parse("{\"k1\": true}", &s) == false); |
| } |
| |
| SECTION("second key") |
| { |
| SaxCountdown s(3); |
| CHECK(json::sax_parse("{\"k1\": true, \"k2\": false}", &s) == false); |
| } |
| |
| SECTION("] without value") |
| { |
| SaxCountdown s(1); |
| CHECK(json::sax_parse("[]", &s) == false); |
| } |
| |
| SECTION("] with value") |
| { |
| SaxCountdown s(2); |
| CHECK(json::sax_parse("[1]", &s) == false); |
| } |
| |
| SECTION("float") |
| { |
| SaxCountdown s(0); |
| CHECK(json::sax_parse("3.14", &s) == false); |
| } |
| |
| SECTION("false") |
| { |
| SaxCountdown s(0); |
| CHECK(json::sax_parse("false", &s) == false); |
| } |
| |
| SECTION("null") |
| { |
| SaxCountdown s(0); |
| CHECK(json::sax_parse("null", &s) == false); |
| } |
| |
| SECTION("true") |
| { |
| SaxCountdown s(0); |
| CHECK(json::sax_parse("true", &s) == false); |
| } |
| |
| SECTION("unsigned") |
| { |
| SaxCountdown s(0); |
| CHECK(json::sax_parse("12", &s) == false); |
| } |
| |
| SECTION("integer") |
| { |
| SaxCountdown s(0); |
| CHECK(json::sax_parse("-12", &s) == false); |
| } |
| |
| SECTION("string") |
| { |
| SaxCountdown s(0); |
| CHECK(json::sax_parse("\"foo\"", &s) == false); |
| } |
| } |
| } |
| |
| SECTION("error messages for comments") |
| { |
| CHECK_THROWS_WITH_AS(json::parse("/a", nullptr, true, true), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid comment; expecting '/' or '*' after '/'; last read: '/a'", json::parse_error); |
| CHECK_THROWS_WITH_AS(json::parse("/*", nullptr, true, true), "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid comment; missing closing '*/'; last read: '/*<U+0000>'", json::parse_error); |
| } |
| } |