From 585a39a2357dc54ae10afb6f948755dcf6395e6b Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 21 Aug 2016 14:39:54 +0200 Subject: [PATCH 1/4] improved branch coverage --- test/src/unit-allocator.cpp | 150 +++++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 4 deletions(-) diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp index dcf8aa35..948446b9 100644 --- a/test/src/unit-allocator.cpp +++ b/test/src/unit-allocator.cpp @@ -28,13 +28,14 @@ SOFTWARE. #include "catch.hpp" +#define private public #include "json.hpp" using nlohmann::json; // special test case to check if memory is leaked if constructor throws template -struct my_allocator : std::allocator +struct bad_allocator : std::allocator { template void construct(T*, Args&& ...) @@ -48,16 +49,157 @@ TEST_CASE("bad_alloc") SECTION("bad_alloc") { // create JSON type using the throwing allocator - using my_json = nlohmann::basic_json; + bad_allocator>; // creating an object should throw - CHECK_THROWS_AS(my_json j(my_json::value_t::object), std::bad_alloc); + CHECK_THROWS_AS(bad_json j(bad_json::value_t::object), std::bad_alloc); + } +} + +bool next_allocation_fails = false; + +template +struct my_allocator : std::allocator +{ + template + void construct(T* p, Args&& ... args) + { + if (next_allocation_fails) + { + throw std::bad_alloc(); + } + else + { + ::new(reinterpret_cast(p)) T(std::forward(args)...); + } + } +}; + +TEST_CASE("controlled bad_alloc") +{ + // create JSON type using the throwing allocator + using my_json = nlohmann::basic_json; + + SECTION("class json_value") + { + SECTION("json_value(value_t)") + { + SECTION("object") + { + next_allocation_fails = false; + auto t = my_json::value_t::object; + CHECK_NOTHROW(my_json::json_value j(t)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); + next_allocation_fails = false; + } + SECTION("array") + { + next_allocation_fails = false; + auto t = my_json::value_t::array; + CHECK_NOTHROW(my_json::json_value j(t)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); + next_allocation_fails = false; + } + SECTION("string") + { + next_allocation_fails = false; + auto t = my_json::value_t::string; + CHECK_NOTHROW(my_json::json_value j(t)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); + next_allocation_fails = false; + } + } + + SECTION("json_value(const string_t&)") + { + next_allocation_fails = false; + my_json::string_t v("foo"); + CHECK_NOTHROW(my_json::json_value j(v)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); + next_allocation_fails = false; + } + + /* + SECTION("json_value(const object_t&)") + { + next_allocation_fails = false; + my_json::object_t v {{"foo", "bar"}}; + CHECK_NOTHROW(my_json::json_value j(v)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); + next_allocation_fails = false; + } + */ + /* + SECTION("json_value(const array_t&)") + { + next_allocation_fails = false; + my_json::array_t v = {"foo", "bar", "baz"}; + CHECK_NOTHROW(my_json::json_value j(v)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); + next_allocation_fails = false; + } + */ + } + + SECTION("class basic_json") + { + SECTION("basic_json(const CompatibleObjectType&)") + { + next_allocation_fails = false; + std::map v {{"foo", "bar"}}; + CHECK_NOTHROW(my_json j(v)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json j(v), std::bad_alloc); + next_allocation_fails = false; + } + + SECTION("basic_json(const CompatibleArrayType&)") + { + next_allocation_fails = false; + std::vector v {"foo", "bar", "baz"}; + CHECK_NOTHROW(my_json j(v)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json j(v), std::bad_alloc); + next_allocation_fails = false; + } + + SECTION("basic_json(const typename string_t::value_type*)") + { + next_allocation_fails = false; + CHECK_NOTHROW(my_json v("foo")); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json v("foo"), std::bad_alloc); + next_allocation_fails = false; + } + + SECTION("basic_json(const typename string_t::value_type*)") + { + next_allocation_fails = false; + std::string s("foo"); + CHECK_NOTHROW(my_json v(s)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json v(s), std::bad_alloc); + next_allocation_fails = false; + } } } From aa7f5ad8b123177067fb0b943866425d22e20013 Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 21 Aug 2016 21:48:15 +0200 Subject: [PATCH 2/4] minor changes --- test/src/unit-allocator.cpp | 93 ++++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 32 deletions(-) diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp index 948446b9..9ad162c1 100644 --- a/test/src/unit-allocator.cpp +++ b/test/src/unit-allocator.cpp @@ -63,7 +63,9 @@ TEST_CASE("bad_alloc") } } -bool next_allocation_fails = false; +bool next_construct_fails = false; +bool next_destroy_fails = false; +bool next_deallocate_fails = false; template struct my_allocator : std::allocator @@ -71,8 +73,9 @@ struct my_allocator : std::allocator template void construct(T* p, Args&& ... args) { - if (next_allocation_fails) + if (next_construct_fails) { + next_construct_fails = false; throw std::bad_alloc(); } else @@ -80,6 +83,32 @@ struct my_allocator : std::allocator ::new(reinterpret_cast(p)) T(std::forward(args)...); } } + + void deallocate(T* p, std::size_t n) + { + if (next_deallocate_fails) + { + next_deallocate_fails = false; + throw std::bad_alloc(); + } + else + { + std::allocator::deallocate(p, n); + } + } + + void destroy(T* p) + { + if (next_destroy_fails) + { + next_destroy_fails = false; + throw std::bad_alloc(); + } + else + { + p->~T(); + } + } }; TEST_CASE("controlled bad_alloc") @@ -100,63 +129,63 @@ TEST_CASE("controlled bad_alloc") { SECTION("object") { - next_allocation_fails = false; + next_construct_fails = false; auto t = my_json::value_t::object; CHECK_NOTHROW(my_json::json_value j(t)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } SECTION("array") { - next_allocation_fails = false; + next_construct_fails = false; auto t = my_json::value_t::array; CHECK_NOTHROW(my_json::json_value j(t)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } SECTION("string") { - next_allocation_fails = false; + next_construct_fails = false; auto t = my_json::value_t::string; CHECK_NOTHROW(my_json::json_value j(t)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } } SECTION("json_value(const string_t&)") { - next_allocation_fails = false; + next_construct_fails = false; my_json::string_t v("foo"); CHECK_NOTHROW(my_json::json_value j(v)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } /* SECTION("json_value(const object_t&)") { - next_allocation_fails = false; + next_construct_fails = false; my_json::object_t v {{"foo", "bar"}}; CHECK_NOTHROW(my_json::json_value j(v)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } */ /* SECTION("json_value(const array_t&)") { - next_allocation_fails = false; + next_construct_fails = false; my_json::array_t v = {"foo", "bar", "baz"}; CHECK_NOTHROW(my_json::json_value j(v)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } */ } @@ -165,41 +194,41 @@ TEST_CASE("controlled bad_alloc") { SECTION("basic_json(const CompatibleObjectType&)") { - next_allocation_fails = false; + next_construct_fails = false; std::map v {{"foo", "bar"}}; CHECK_NOTHROW(my_json j(v)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json j(v), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } SECTION("basic_json(const CompatibleArrayType&)") { - next_allocation_fails = false; + next_construct_fails = false; std::vector v {"foo", "bar", "baz"}; CHECK_NOTHROW(my_json j(v)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json j(v), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } SECTION("basic_json(const typename string_t::value_type*)") { - next_allocation_fails = false; + next_construct_fails = false; CHECK_NOTHROW(my_json v("foo")); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json v("foo"), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } SECTION("basic_json(const typename string_t::value_type*)") { - next_allocation_fails = false; + next_construct_fails = false; std::string s("foo"); CHECK_NOTHROW(my_json v(s)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json v(s), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } } } From 94331a355d5fc2032810af7160504addcf86f783 Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 21 Aug 2016 21:50:13 +0200 Subject: [PATCH 3/4] removed LCOV_EXCL_LINE --- src/json.hpp | 22 +++++++++++----------- src/json.hpp.re2c | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index a8289a49..b5e24206 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -7734,7 +7734,7 @@ class basic_json }; if ((m_limit - m_cursor) < 5) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yybm[0 + yych] & 32) @@ -7868,7 +7868,7 @@ basic_json_parser_6: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yybm[0 + yych] & 32) @@ -7938,7 +7938,7 @@ basic_json_parser_15: m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yybm[0 + yych] & 64) @@ -8031,7 +8031,7 @@ basic_json_parser_31: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; basic_json_parser_32: @@ -8068,7 +8068,7 @@ basic_json_parser_36: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= 'e') @@ -8212,7 +8212,7 @@ basic_json_parser_43: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= '@') @@ -8248,7 +8248,7 @@ basic_json_parser_44: m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= 'D') @@ -8289,7 +8289,7 @@ basic_json_parser_47: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= '/') @@ -8331,7 +8331,7 @@ basic_json_parser_54: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= '@') @@ -8385,7 +8385,7 @@ basic_json_parser_60: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= '@') @@ -8426,7 +8426,7 @@ basic_json_parser_63: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= '@') diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index ffa20f39..fdc00638 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -7698,7 +7698,7 @@ class basic_json re2c:define:YYCURSOR = m_cursor; re2c:define:YYLIMIT = m_limit; re2c:define:YYMARKER = m_marker; - re2c:define:YYFILL = "yyfill(); // LCOV_EXCL_LINE"; + re2c:define:YYFILL = "yyfill()"; re2c:yyfill:parameter = 0; re2c:indent:string = " "; re2c:indent:top = 1; From 1e896eb91ef940cda23a4bb49a302ed227c442d7 Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 21 Aug 2016 22:38:56 +0200 Subject: [PATCH 4/4] improved code coverage --- test/src/unit-class_parser.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index fe005503..259daf84 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -445,6 +445,10 @@ TEST_CASE("parser class") CHECK_THROWS_AS(json::parser("\"\\u0\"").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("\"\\u01\"").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("\"\\u012\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\\u").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\\u0").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\\u01").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\\u012").parse(), std::invalid_argument); CHECK_THROWS_WITH(json::parser("\"").parse(), "parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\\"").parse(), @@ -457,6 +461,14 @@ TEST_CASE("parser class") "parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\u012\"").parse(), "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\\u").parse(), + "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\\u0").parse(), + "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\\u01").parse(), + "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\\u012").parse(), + "parse error - unexpected '\"'"); // invalid escapes for (int c = 1; c < 128; ++c)