summaryrefslogtreecommitdiff
path: root/json4cpp/tests/src/unit-allocator.cpp
diff options
context:
space:
mode:
authorMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-02 18:42:50 +0300
committerMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-02 18:42:50 +0300
commit5fad10f89c485cfdc7b99011f07609f8871160d4 (patch)
tree1860b39753b652dfe54d3cbbc80c875f40198d1f /json4cpp/tests/src/unit-allocator.cpp
parent292baed7ac0cf84263263966ed32ed113cae857f (diff)
parent9a737481aed085fd289f82dff1fa8c3c66627a7e (diff)
downloadProject-Tick-5fad10f89c485cfdc7b99011f07609f8871160d4.tar.gz
Project-Tick-5fad10f89c485cfdc7b99011f07609f8871160d4.zip
Add 'json4cpp/' from commit '9a737481aed085fd289f82dff1fa8c3c66627a7e'
git-subtree-dir: json4cpp git-subtree-mainline: 292baed7ac0cf84263263966ed32ed113cae857f git-subtree-split: 9a737481aed085fd289f82dff1fa8c3c66627a7e
Diffstat (limited to 'json4cpp/tests/src/unit-allocator.cpp')
-rw-r--r--json4cpp/tests/src/unit-allocator.cpp263
1 files changed, 263 insertions, 0 deletions
diff --git a/json4cpp/tests/src/unit-allocator.cpp b/json4cpp/tests/src/unit-allocator.cpp
new file mode 100644
index 0000000000..6f2c70148c
--- /dev/null
+++ b/json4cpp/tests/src/unit-allocator.cpp
@@ -0,0 +1,263 @@
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++ (supporting code)
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#include "doctest_compatibility.h"
+
+#define JSON_TESTS_PRIVATE
+#include <nlohmann/json.hpp>
+using nlohmann::json;
+
+namespace
+{
+// special test case to check if memory is leaked if constructor throws
+template<class T>
+struct bad_allocator : std::allocator<T>
+{
+ using std::allocator<T>::allocator;
+
+ bad_allocator() = default;
+ template<class U> bad_allocator(const bad_allocator<U>& /*unused*/) { }
+
+ template<class... Args>
+ void construct(T* /*unused*/, Args&& ... /*unused*/) // NOLINT(cppcoreguidelines-missing-std-forward)
+ {
+ throw std::bad_alloc();
+ }
+
+ template <class U>
+ struct rebind
+ {
+ using other = bad_allocator<U>;
+ };
+};
+} // namespace
+
+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();
+ }
+
+ ::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();
+ }
+
+ std::allocator<T>::deallocate(p, n);
+ }
+
+ void destroy(T* p)
+ {
+ if (next_destroy_fails)
+ {
+ next_destroy_fails = false;
+ throw std::bad_alloc();
+ }
+
+ static_cast<void>(p); // fix MSVC's C4100 warning
+ 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);
+}
+} // namespace
+
+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;
+ const 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;
+ const 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;
+ const 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;
+ const 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() = default;
+ template <class U>
+ allocator_no_forward(allocator_no_forward<U> /*unused*/) {}
+
+ 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...);
+ }
+};
+} // namespace
+
+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");
+ }
+}