123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- /*
- __ _____ _____ _____
- __| | __| | | | JSON for Modern C++ (test suite)
- | | |__ | | | | | | version 3.7.3
- |_____|_____|_____|_|___| 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
- namespace
- {
- // special test case to check if memory is leaked if constructor throws
- template<class T>
- struct bad_allocator : std::allocator<T>
- {
- template<class... Args>
- void construct(T*, Args&& ...)
- {
- throw std::bad_alloc();
- }
- };
- }
- TEST_CASE("bad_alloc")
- {
- SECTION("bad_alloc")
- {
- // create JSON type using the throwing allocator
- using bad_json = nlohmann::basic_json<std::map,
- std::vector,
- std::string,
- bool,
- std::int64_t,
- std::uint64_t,
- double,
- bad_allocator>;
- // creating an object should throw
- CHECK_THROWS_AS(bad_json(bad_json::value_t::object), std::bad_alloc&);
- }
- }
- namespace
- {
- bool next_construct_fails = false;
- bool next_destroy_fails = false;
- bool next_deallocate_fails = false;
- template<class T>
- struct my_allocator : std::allocator<T>
- {
- using std::allocator<T>::allocator;
- template<class... Args>
- void construct(T* p, Args&& ... args)
- {
- if (next_construct_fails)
- {
- next_construct_fails = false;
- throw std::bad_alloc();
- }
- else
- {
- ::new (reinterpret_cast<void*>(p)) T(std::forward<Args>(args)...);
- }
- }
- void deallocate(T* p, std::size_t n)
- {
- if (next_deallocate_fails)
- {
- next_deallocate_fails = false;
- throw std::bad_alloc();
- }
- else
- {
- std::allocator<T>::deallocate(p, n);
- }
- }
- void destroy(T* p)
- {
- if (next_destroy_fails)
- {
- next_destroy_fails = false;
- throw std::bad_alloc();
- }
- else
- {
- p->~T();
- }
- }
- template <class U>
- struct rebind
- {
- using other = my_allocator<U>;
- };
- };
- // allows deletion of raw pointer, usually hold by json_value
- template<class T>
- void my_allocator_clean_up(T* p)
- {
- assert(p != nullptr);
- my_allocator<T> alloc;
- alloc.destroy(p);
- alloc.deallocate(p, 1);
- }
- }
- 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_construct_fails = false;
- auto t = my_json::value_t::object;
- CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).object));
- next_construct_fails = true;
- CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
- next_construct_fails = false;
- }
- SECTION("array")
- {
- next_construct_fails = false;
- auto t = my_json::value_t::array;
- CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).array));
- next_construct_fails = true;
- CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
- next_construct_fails = false;
- }
- SECTION("string")
- {
- next_construct_fails = false;
- auto t = my_json::value_t::string;
- CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).string));
- next_construct_fails = true;
- CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
- next_construct_fails = false;
- }
- }
- SECTION("json_value(const string_t&)")
- {
- next_construct_fails = false;
- my_json::string_t v("foo");
- CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(v).string));
- next_construct_fails = true;
- CHECK_THROWS_AS(my_json::json_value(v), std::bad_alloc&);
- next_construct_fails = false;
- }
- }
- SECTION("class basic_json")
- {
- SECTION("basic_json(const CompatibleObjectType&)")
- {
- next_construct_fails = false;
- std::map<std::string, std::string> v {{"foo", "bar"}};
- CHECK_NOTHROW(my_json(v));
- next_construct_fails = true;
- CHECK_THROWS_AS(my_json(v), std::bad_alloc&);
- next_construct_fails = false;
- }
- SECTION("basic_json(const CompatibleArrayType&)")
- {
- next_construct_fails = false;
- std::vector<std::string> v {"foo", "bar", "baz"};
- CHECK_NOTHROW(my_json(v));
- next_construct_fails = true;
- CHECK_THROWS_AS(my_json(v), std::bad_alloc&);
- next_construct_fails = false;
- }
- SECTION("basic_json(const typename string_t::value_type*)")
- {
- next_construct_fails = false;
- CHECK_NOTHROW(my_json("foo"));
- next_construct_fails = true;
- CHECK_THROWS_AS(my_json("foo"), std::bad_alloc&);
- next_construct_fails = false;
- }
- SECTION("basic_json(const typename string_t::value_type*)")
- {
- next_construct_fails = false;
- std::string s("foo");
- CHECK_NOTHROW(my_json(s));
- next_construct_fails = true;
- CHECK_THROWS_AS(my_json(s), std::bad_alloc&);
- next_construct_fails = false;
- }
- }
- }
- namespace
- {
- template<class T>
- struct allocator_no_forward : std::allocator<T>
- {
- allocator_no_forward() {}
- template <class U>
- allocator_no_forward(allocator_no_forward<U>) {}
- template <class U>
- struct rebind
- {
- using other = allocator_no_forward<U>;
- };
- template <class... Args>
- void construct(T* p, const Args& ... args) noexcept(noexcept(::new (static_cast<void*>(p)) T(args...)))
- {
- // force copy even if move is available
- ::new (static_cast<void*>(p)) T(args...);
- }
- };
- }
- TEST_CASE("bad my_allocator::construct")
- {
- SECTION("my_allocator::construct doesn't forward")
- {
- using bad_alloc_json = nlohmann::basic_json<std::map,
- std::vector,
- std::string,
- bool,
- std::int64_t,
- std::uint64_t,
- double,
- allocator_no_forward>;
- bad_alloc_json j;
- j["test"] = bad_alloc_json::array_t();
- j["test"].push_back("should not leak");
- }
- }
|