From 606a25195fcb863a7fae057dd2262bdc9f8ac877 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 17 Mar 2018 19:15:59 +0100 Subject: [PATCH] :white_check_mark: improved test coverage --- include/nlohmann/detail/input/json_sax.hpp | 6 +- single_include/nlohmann/json.hpp | 36 +++-- test/src/unit-class_parser.cpp | 171 +++++++++++++++++++++ 3 files changed, 194 insertions(+), 19 deletions(-) diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index 28dfe6a4..a0ffdc66 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -241,13 +241,13 @@ class json_sax_dom_parser : public json_sax case 1: JSON_THROW(*reinterpret_cast(&ex)); case 2: - JSON_THROW(*reinterpret_cast(&ex)); + JSON_THROW(*reinterpret_cast(&ex)); // LCOV_EXCL_LINE case 3: - JSON_THROW(*reinterpret_cast(&ex)); + JSON_THROW(*reinterpret_cast(&ex)); // LCOV_EXCL_LINE case 4: JSON_THROW(*reinterpret_cast(&ex)); case 5: - JSON_THROW(*reinterpret_cast(&ex)); + JSON_THROW(*reinterpret_cast(&ex)); // LCOV_EXCL_LINE default: assert(false); // LCOV_EXCL_LINE } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index a588e4f0..639d6400 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3377,13 +3377,13 @@ class json_sax_dom_parser : public json_sax case 1: JSON_THROW(*reinterpret_cast(&ex)); case 2: - JSON_THROW(*reinterpret_cast(&ex)); + JSON_THROW(*reinterpret_cast(&ex)); // LCOV_EXCL_LINE case 3: - JSON_THROW(*reinterpret_cast(&ex)); + JSON_THROW(*reinterpret_cast(&ex)); // LCOV_EXCL_LINE case 4: JSON_THROW(*reinterpret_cast(&ex)); case 5: - JSON_THROW(*reinterpret_cast(&ex)); + JSON_THROW(*reinterpret_cast(&ex)); // LCOV_EXCL_LINE default: assert(false); // LCOV_EXCL_LINE } @@ -3946,11 +3946,11 @@ class parser // stack to remember the hieararchy of structured values we are parsing std::vector states; // value to avoid a goto (see comment where set to true) - bool skip_to_tail = false; + bool skip_to_state_evaluation = false; while (true) { - if (not skip_to_tail) + if (not skip_to_state_evaluation) { // invariant: get_token() was called before each iteration switch (last_token) @@ -3999,9 +3999,11 @@ class parser parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator))); } + // remember we are now inside an object + states.push_back(parse_state_t::object_value); + // parse values get_token(); - states.push_back(parse_state_t::object_value); continue; } @@ -4025,8 +4027,10 @@ class parser break; } - // parse values (no need to call get_token) + // remember we are now inside an array states.push_back(parse_state_t::array_value); + + // parse values (no need to call get_token) continue; } @@ -4122,7 +4126,7 @@ class parser } else { - skip_to_tail = false; + skip_to_state_evaluation = false; } // we reached this line after we successfully parsed a value @@ -4156,12 +4160,12 @@ class parser // We are done with this array. Before we can parse // a new value, we need to evaluate the new state - // first. By setting skip_to_tail to false, we are - // effectively jumping to the beginning of this - // switch. + // first. By setting skip_to_state_evaluation to + // false, we are effectively jumping to the + // beginning of this switch. assert(not states.empty()); states.pop_back(); - skip_to_tail = true; + skip_to_state_evaluation = true; continue; } else @@ -4218,12 +4222,12 @@ class parser // We are done with this object. Before we can // parse a new value, we need to evaluate the new - // state first. By setting skip_to_tail to false, - // we are effectively jumping to the beginning of - // this switch. + // state first. By setting skip_to_state_evaluation + // to false, we are effectively jumping to the + // beginning of this switch. assert(not states.empty()); states.pop_back(); - skip_to_tail = true; + skip_to_state_evaluation = true; continue; } else diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index e37f398a..c425207a 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -134,6 +134,81 @@ class SaxEventLogger : public nlohmann::json::json_sax_t 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 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 binary(const std::vector&) 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); @@ -1595,4 +1670,100 @@ TEST_CASE("parser class") 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()); + + 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 13: syntax error - 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); + } + } + } }