• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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