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