1 /*
2 __ _____ _____ _____
3 __| | __| | | | JSON for Modern C++ (test suite)
4 | | |__ | | | | | | version 3.9.1
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 private public
33 #include <nlohmann/json.hpp>
34 using nlohmann::json;
35 #undef private
36
37 namespace
38 {
39 // special test case to check if memory is leaked if constructor throws
40 template<class T>
41 struct bad_allocator : std::allocator<T>
42 {
43 template<class... Args>
construct__anon861915100111::bad_allocator44 void construct(T*, Args&& ...)
45 {
46 throw std::bad_alloc();
47 }
48 };
49 }
50
51 TEST_CASE("bad_alloc")
52 {
53 SECTION("bad_alloc")
54 {
55 // create JSON type using the throwing allocator
56 using bad_json = nlohmann::basic_json<std::map,
57 std::vector,
58 std::string,
59 bool,
60 std::int64_t,
61 std::uint64_t,
62 double,
63 bad_allocator>;
64
65 // creating an object should throw
66 CHECK_THROWS_AS(bad_json(bad_json::value_t::object), std::bad_alloc&);
67 }
68 }
69
70 namespace
71 {
72 bool next_construct_fails = false;
73 bool next_destroy_fails = false;
74 bool next_deallocate_fails = false;
75
76 template<class T>
77 struct my_allocator : std::allocator<T>
78 {
79 using std::allocator<T>::allocator;
80
81 template<class... Args>
construct__anon861915100211::my_allocator82 void construct(T* p, Args&& ... args)
83 {
84 if (next_construct_fails)
85 {
86 next_construct_fails = false;
87 throw std::bad_alloc();
88 }
89 else
90 {
91 ::new (reinterpret_cast<void*>(p)) T(std::forward<Args>(args)...);
92 }
93 }
94
deallocate__anon861915100211::my_allocator95 void deallocate(T* p, std::size_t n)
96 {
97 if (next_deallocate_fails)
98 {
99 next_deallocate_fails = false;
100 throw std::bad_alloc();
101 }
102 else
103 {
104 std::allocator<T>::deallocate(p, n);
105 }
106 }
107
destroy__anon861915100211::my_allocator108 void destroy(T* p)
109 {
110 if (next_destroy_fails)
111 {
112 next_destroy_fails = false;
113 throw std::bad_alloc();
114 }
115 else
116 {
117 p->~T();
118 }
119 }
120
121 template <class U>
122 struct rebind
123 {
124 using other = my_allocator<U>;
125 };
126 };
127
128 // allows deletion of raw pointer, usually hold by json_value
129 template<class T>
my_allocator_clean_up(T * p)130 void my_allocator_clean_up(T* p)
131 {
132 assert(p != nullptr);
133 my_allocator<T> alloc;
134 alloc.destroy(p);
135 alloc.deallocate(p, 1);
136 }
137 }
138
139 TEST_CASE("controlled bad_alloc")
140 {
141 // create JSON type using the throwing allocator
142 using my_json = nlohmann::basic_json<std::map,
143 std::vector,
144 std::string,
145 bool,
146 std::int64_t,
147 std::uint64_t,
148 double,
149 my_allocator>;
150
151 SECTION("class json_value")
152 {
153 SECTION("json_value(value_t)")
154 {
155 SECTION("object")
156 {
157 next_construct_fails = false;
158 auto t = my_json::value_t::object;
159 CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).object));
160 next_construct_fails = true;
161 CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
162 next_construct_fails = false;
163 }
164 SECTION("array")
165 {
166 next_construct_fails = false;
167 auto t = my_json::value_t::array;
168 CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).array));
169 next_construct_fails = true;
170 CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
171 next_construct_fails = false;
172 }
173 SECTION("string")
174 {
175 next_construct_fails = false;
176 auto t = my_json::value_t::string;
177 CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).string));
178 next_construct_fails = true;
179 CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
180 next_construct_fails = false;
181 }
182 }
183
184 SECTION("json_value(const string_t&)")
185 {
186 next_construct_fails = false;
187 my_json::string_t v("foo");
188 CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(v).string));
189 next_construct_fails = true;
190 CHECK_THROWS_AS(my_json::json_value(v), std::bad_alloc&);
191 next_construct_fails = false;
192 }
193 }
194
195 SECTION("class basic_json")
196 {
197 SECTION("basic_json(const CompatibleObjectType&)")
198 {
199 next_construct_fails = false;
200 std::map<std::string, std::string> v {{"foo", "bar"}};
201 CHECK_NOTHROW(my_json(v));
202 next_construct_fails = true;
203 CHECK_THROWS_AS(my_json(v), std::bad_alloc&);
204 next_construct_fails = false;
205 }
206
207 SECTION("basic_json(const CompatibleArrayType&)")
208 {
209 next_construct_fails = false;
210 std::vector<std::string> v {"foo", "bar", "baz"};
211 CHECK_NOTHROW(my_json(v));
212 next_construct_fails = true;
213 CHECK_THROWS_AS(my_json(v), std::bad_alloc&);
214 next_construct_fails = false;
215 }
216
217 SECTION("basic_json(const typename string_t::value_type*)")
218 {
219 next_construct_fails = false;
220 CHECK_NOTHROW(my_json("foo"));
221 next_construct_fails = true;
222 CHECK_THROWS_AS(my_json("foo"), std::bad_alloc&);
223 next_construct_fails = false;
224 }
225
226 SECTION("basic_json(const typename string_t::value_type*)")
227 {
228 next_construct_fails = false;
229 std::string s("foo");
230 CHECK_NOTHROW(my_json(s));
231 next_construct_fails = true;
232 CHECK_THROWS_AS(my_json(s), std::bad_alloc&);
233 next_construct_fails = false;
234 }
235 }
236 }
237
238 namespace
239 {
240 template<class T>
241 struct allocator_no_forward : std::allocator<T>
242 {
allocator_no_forward__anon861915100311::allocator_no_forward243 allocator_no_forward() {}
244 template <class U>
allocator_no_forward__anon861915100311::allocator_no_forward245 allocator_no_forward(allocator_no_forward<U>) {}
246
247 template <class U>
248 struct rebind
249 {
250 using other = allocator_no_forward<U>;
251 };
252
253 template <class... Args>
construct__anon861915100311::allocator_no_forward254 void construct(T* p, const Args& ... args) noexcept(noexcept(::new (static_cast<void*>(p)) T(args...)))
255 {
256 // force copy even if move is available
257 ::new (static_cast<void*>(p)) T(args...);
258 }
259 };
260 }
261
262 TEST_CASE("bad my_allocator::construct")
263 {
264 SECTION("my_allocator::construct doesn't forward")
265 {
266 using bad_alloc_json = nlohmann::basic_json<std::map,
267 std::vector,
268 std::string,
269 bool,
270 std::int64_t,
271 std::uint64_t,
272 double,
273 allocator_no_forward>;
274
275 bad_alloc_json j;
276 j["test"] = bad_alloc_json::array_t();
277 j["test"].push_back("should not leak");
278 }
279 }
280