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<class T> -struct my_allocator : std::allocator<T> +struct bad_allocator : std::allocator<T> { template<class... Args> 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<std::map, + using bad_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, double, - my_allocator>; + 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<class T> +struct my_allocator : std::allocator<T> +{ + template<class... Args> + void construct(T* p, Args&& ... args) + { + if (next_allocation_fails) + { + throw std::bad_alloc(); + } + else + { + ::new(reinterpret_cast<void*>(p)) T(std::forward<Args>(args)...); + } + } +}; + +TEST_CASE("controlled bad_alloc") +{ + // create JSON type using the throwing allocator + using my_json = nlohmann::basic_json<std::map, + std::vector, + std::string, + bool, + std::int64_t, + std::uint64_t, + double, + my_allocator>; + + 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<std::string, std::string> 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<std::string> 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; + } } }