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