unit-allocator.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. /*
  2. __ _____ _____ _____
  3. __| | __| | | | JSON for Modern C++ (test suite)
  4. | | |__ | | | | | | version 3.7.3
  5. |_____|_____|_____|_|___| https://github.com/nlohmann/json
  6. Licensed under the MIT License <http://opensource.org/licenses/MIT>.
  7. SPDX-License-Identifier: MIT
  8. Copyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.
  9. Permission is hereby granted, free of charge, to any person obtaining a copy
  10. of this software and associated documentation files (the "Software"), to deal
  11. in the Software without restriction, including without limitation the rights
  12. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. copies of the Software, and to permit persons to whom the Software is
  14. furnished to do so, subject to the following conditions:
  15. The above copyright notice and this permission notice shall be included in all
  16. copies or substantial portions of the Software.
  17. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  23. SOFTWARE.
  24. */
  25. #include "doctest_compatibility.h"
  26. #define private public
  27. #include <nlohmann/json.hpp>
  28. using nlohmann::json;
  29. #undef private
  30. namespace
  31. {
  32. // special test case to check if memory is leaked if constructor throws
  33. template<class T>
  34. struct bad_allocator : std::allocator<T>
  35. {
  36. template<class... Args>
  37. void construct(T*, Args&& ...)
  38. {
  39. throw std::bad_alloc();
  40. }
  41. };
  42. }
  43. TEST_CASE("bad_alloc")
  44. {
  45. SECTION("bad_alloc")
  46. {
  47. // create JSON type using the throwing allocator
  48. using bad_json = nlohmann::basic_json<std::map,
  49. std::vector,
  50. std::string,
  51. bool,
  52. std::int64_t,
  53. std::uint64_t,
  54. double,
  55. bad_allocator>;
  56. // creating an object should throw
  57. CHECK_THROWS_AS(bad_json(bad_json::value_t::object), std::bad_alloc&);
  58. }
  59. }
  60. namespace
  61. {
  62. bool next_construct_fails = false;
  63. bool next_destroy_fails = false;
  64. bool next_deallocate_fails = false;
  65. template<class T>
  66. struct my_allocator : std::allocator<T>
  67. {
  68. using std::allocator<T>::allocator;
  69. template<class... Args>
  70. void construct(T* p, Args&& ... args)
  71. {
  72. if (next_construct_fails)
  73. {
  74. next_construct_fails = false;
  75. throw std::bad_alloc();
  76. }
  77. else
  78. {
  79. ::new (reinterpret_cast<void*>(p)) T(std::forward<Args>(args)...);
  80. }
  81. }
  82. void deallocate(T* p, std::size_t n)
  83. {
  84. if (next_deallocate_fails)
  85. {
  86. next_deallocate_fails = false;
  87. throw std::bad_alloc();
  88. }
  89. else
  90. {
  91. std::allocator<T>::deallocate(p, n);
  92. }
  93. }
  94. void destroy(T* p)
  95. {
  96. if (next_destroy_fails)
  97. {
  98. next_destroy_fails = false;
  99. throw std::bad_alloc();
  100. }
  101. else
  102. {
  103. p->~T();
  104. }
  105. }
  106. template <class U>
  107. struct rebind
  108. {
  109. using other = my_allocator<U>;
  110. };
  111. };
  112. // allows deletion of raw pointer, usually hold by json_value
  113. template<class T>
  114. void my_allocator_clean_up(T* p)
  115. {
  116. assert(p != nullptr);
  117. my_allocator<T> alloc;
  118. alloc.destroy(p);
  119. alloc.deallocate(p, 1);
  120. }
  121. }
  122. TEST_CASE("controlled bad_alloc")
  123. {
  124. // create JSON type using the throwing allocator
  125. using my_json = nlohmann::basic_json<std::map,
  126. std::vector,
  127. std::string,
  128. bool,
  129. std::int64_t,
  130. std::uint64_t,
  131. double,
  132. my_allocator>;
  133. SECTION("class json_value")
  134. {
  135. SECTION("json_value(value_t)")
  136. {
  137. SECTION("object")
  138. {
  139. next_construct_fails = false;
  140. auto t = my_json::value_t::object;
  141. CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).object));
  142. next_construct_fails = true;
  143. CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
  144. next_construct_fails = false;
  145. }
  146. SECTION("array")
  147. {
  148. next_construct_fails = false;
  149. auto t = my_json::value_t::array;
  150. CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).array));
  151. next_construct_fails = true;
  152. CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
  153. next_construct_fails = false;
  154. }
  155. SECTION("string")
  156. {
  157. next_construct_fails = false;
  158. auto t = my_json::value_t::string;
  159. CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).string));
  160. next_construct_fails = true;
  161. CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
  162. next_construct_fails = false;
  163. }
  164. }
  165. SECTION("json_value(const string_t&)")
  166. {
  167. next_construct_fails = false;
  168. my_json::string_t v("foo");
  169. CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(v).string));
  170. next_construct_fails = true;
  171. CHECK_THROWS_AS(my_json::json_value(v), std::bad_alloc&);
  172. next_construct_fails = false;
  173. }
  174. }
  175. SECTION("class basic_json")
  176. {
  177. SECTION("basic_json(const CompatibleObjectType&)")
  178. {
  179. next_construct_fails = false;
  180. std::map<std::string, std::string> v {{"foo", "bar"}};
  181. CHECK_NOTHROW(my_json(v));
  182. next_construct_fails = true;
  183. CHECK_THROWS_AS(my_json(v), std::bad_alloc&);
  184. next_construct_fails = false;
  185. }
  186. SECTION("basic_json(const CompatibleArrayType&)")
  187. {
  188. next_construct_fails = false;
  189. std::vector<std::string> v {"foo", "bar", "baz"};
  190. CHECK_NOTHROW(my_json(v));
  191. next_construct_fails = true;
  192. CHECK_THROWS_AS(my_json(v), std::bad_alloc&);
  193. next_construct_fails = false;
  194. }
  195. SECTION("basic_json(const typename string_t::value_type*)")
  196. {
  197. next_construct_fails = false;
  198. CHECK_NOTHROW(my_json("foo"));
  199. next_construct_fails = true;
  200. CHECK_THROWS_AS(my_json("foo"), std::bad_alloc&);
  201. next_construct_fails = false;
  202. }
  203. SECTION("basic_json(const typename string_t::value_type*)")
  204. {
  205. next_construct_fails = false;
  206. std::string s("foo");
  207. CHECK_NOTHROW(my_json(s));
  208. next_construct_fails = true;
  209. CHECK_THROWS_AS(my_json(s), std::bad_alloc&);
  210. next_construct_fails = false;
  211. }
  212. }
  213. }
  214. namespace
  215. {
  216. template<class T>
  217. struct allocator_no_forward : std::allocator<T>
  218. {
  219. allocator_no_forward() {}
  220. template <class U>
  221. allocator_no_forward(allocator_no_forward<U>) {}
  222. template <class U>
  223. struct rebind
  224. {
  225. using other = allocator_no_forward<U>;
  226. };
  227. template <class... Args>
  228. void construct(T* p, const Args& ... args) noexcept(noexcept(::new (static_cast<void*>(p)) T(args...)))
  229. {
  230. // force copy even if move is available
  231. ::new (static_cast<void*>(p)) T(args...);
  232. }
  233. };
  234. }
  235. TEST_CASE("bad my_allocator::construct")
  236. {
  237. SECTION("my_allocator::construct doesn't forward")
  238. {
  239. using bad_alloc_json = nlohmann::basic_json<std::map,
  240. std::vector,
  241. std::string,
  242. bool,
  243. std::int64_t,
  244. std::uint64_t,
  245. double,
  246. allocator_no_forward>;
  247. bad_alloc_json j;
  248. j["test"] = bad_alloc_json::array_t();
  249. j["test"].push_back("should not leak");
  250. }
  251. }