1
2 // Copyright 2006-2009 Daniel James.
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 #include "./containers.hpp"
6
7 #include "../helpers/helpers.hpp"
8 #include "../helpers/invariants.hpp"
9 #include "../helpers/random_values.hpp"
10 #include "../helpers/strong.hpp"
11 #include "../helpers/tracker.hpp"
12 #include <cmath>
13 #include <string>
14
15 test::seed_t initialize_seed(747373);
16
17 // Fill in a container so that it's about to rehash
rehash_prep(T & x)18 template <typename T> void rehash_prep(T& x)
19 {
20 using namespace std;
21 typedef typename T::size_type size_type;
22
23 x.max_load_factor(0.25);
24 size_type bucket_count = x.bucket_count();
25 size_type initial_elements = static_cast<size_type>(
26 ceil((double)bucket_count * (double)x.max_load_factor()) - 1);
27 test::random_values<T> v(initial_elements);
28 x.insert(v.begin(), v.end());
29 BOOST_TEST(bucket_count == x.bucket_count());
30 }
31
32 // Overload to generate inserters that need type information.
33
34 template <typename Inserter, typename T>
generate(Inserter inserter,T &)35 Inserter generate(Inserter inserter, T&)
36 {
37 return inserter;
38 }
39
40 // Get the iterator returned from an insert/emplace.
41
get_iterator(T const & x)42 template <typename T> T get_iterator(T const& x) { return x; }
43
get_iterator(std::pair<T,bool> const & x)44 template <typename T> T get_iterator(std::pair<T, bool> const& x)
45 {
46 return x.first;
47 }
48
49 // Generic insert exception test for typical single element inserts..
50
51 template <typename T, typename Inserter, typename Values>
insert_exception_test_impl(T x,Inserter insert,Values const & v)52 void insert_exception_test_impl(T x, Inserter insert, Values const& v)
53 {
54 test::strong<T> strong;
55
56 test::ordered<T> tracker;
57 tracker.insert(x.begin(), x.end());
58
59 try {
60 ENABLE_EXCEPTIONS;
61
62 for (typename Values::const_iterator it = v.begin(); it != v.end(); ++it) {
63 strong.store(x, test::detail::tracker.count_allocations);
64 insert(x, it);
65 }
66 } catch (...) {
67 test::check_equivalent_keys(x);
68 insert.exception_check(x, strong);
69 throw;
70 }
71
72 test::check_equivalent_keys(x);
73 insert.track(tracker, v.begin(), v.end());
74 tracker.compare(x);
75 }
76
77 // Simple insert exception test
78
79 template <typename T, typename Inserter>
insert_exception_test(T *,Inserter insert,test::random_generator gen)80 void insert_exception_test(T*, Inserter insert, test::random_generator gen)
81 {
82 for (int i = 0; i < 5; ++i) {
83 test::random_values<T> v(10, gen);
84 T x;
85
86 EXCEPTION_LOOP(insert_exception_test_impl(x, generate(insert, x), v));
87 }
88 }
89
90 // Insert into a container which is about to hit its max load, so that it
91 // rehashes.
92
93 template <typename T, typename Inserter>
insert_rehash_exception_test(T *,Inserter insert,test::random_generator gen)94 void insert_rehash_exception_test(
95 T*, Inserter insert, test::random_generator gen)
96 {
97 for (int i = 0; i < 5; ++i) {
98 T x;
99 rehash_prep(x);
100
101 test::random_values<T> v2(5, gen);
102 EXCEPTION_LOOP(insert_exception_test_impl(x, generate(insert, x), v2));
103 }
104 }
105
106 // Various methods for inserting a single element
107
108 struct inserter_base
109 {
exception_checkinserter_base110 template <typename T> void exception_check(T& x, test::strong<T>& strong)
111 {
112 std::string scope(test::scope);
113
114 if (scope.find("hash::operator()") == std::string::npos)
115 strong.test(x, test::detail::tracker.count_allocations);
116 }
117
118 template <typename T, typename Iterator>
trackinserter_base119 void track(T& tracker, Iterator begin, Iterator end)
120 {
121 tracker.insert(begin, end);
122 }
123 };
124
125 struct insert_lvalue_type : inserter_base
126 {
operator ()insert_lvalue_type127 template <typename T, typename Iterator> void operator()(T& x, Iterator it)
128 {
129 x.insert(*it);
130 }
131 } insert_lvalue;
132
133 struct insert_lvalue_begin_type : inserter_base
134 {
operator ()insert_lvalue_begin_type135 template <typename T, typename Iterator> void operator()(T& x, Iterator it)
136 {
137 x.insert(x.begin(), *it);
138 }
139 } insert_lvalue_begin;
140
141 struct insert_lvalue_end_type : inserter_base
142 {
operator ()insert_lvalue_end_type143 template <typename T, typename Iterator> void operator()(T& x, Iterator it)
144 {
145 x.insert(x.end(), *it);
146 }
147 } insert_lvalue_end;
148
149 template <typename T> struct insert_lvalue_pos_type_impl : inserter_base
150 {
151 typename T::iterator pos;
152
insert_lvalue_pos_type_implinsert_lvalue_pos_type_impl153 insert_lvalue_pos_type_impl(T& x) : pos(x.begin()) {}
154
operator ()insert_lvalue_pos_type_impl155 template <typename Iterator> void operator()(T& x, Iterator it)
156 {
157 pos = get_iterator(x.insert(pos, *it));
158 }
159 };
160
161 struct insert_lvalue_pos_type
162 {
163 template <typename T>
generate(insert_lvalue_pos_type,T & x)164 friend insert_lvalue_pos_type_impl<T> generate(insert_lvalue_pos_type, T& x)
165 {
166 return insert_lvalue_pos_type_impl<T>(x);
167 }
168 } insert_lvalue_pos;
169
170 struct insert_single_item_range_type : inserter_base
171 {
operator ()insert_single_item_range_type172 template <typename T, typename Iterator> void operator()(T& x, Iterator it)
173 {
174 x.insert(it, test::next(it));
175 }
176 } insert_single_item_range;
177
178 struct emplace_lvalue_type : inserter_base
179 {
operator ()emplace_lvalue_type180 template <typename T, typename Iterator> void operator()(T& x, Iterator it)
181 {
182 x.emplace(*it);
183 }
184 } emplace_lvalue;
185
186 struct emplace_lvalue_begin_type : inserter_base
187 {
operator ()emplace_lvalue_begin_type188 template <typename T, typename Iterator> void operator()(T& x, Iterator it)
189 {
190 x.emplace_hint(x.begin(), *it);
191 }
192 } emplace_lvalue_begin;
193
194 struct emplace_lvalue_end_type : inserter_base
195 {
operator ()emplace_lvalue_end_type196 template <typename T, typename Iterator> void operator()(T& x, Iterator it)
197 {
198 x.emplace_hint(x.end(), *it);
199 }
200 } emplace_lvalue_end;
201
202 template <typename T> struct emplace_lvalue_pos_type_impl : inserter_base
203 {
204 typename T::iterator pos;
205
emplace_lvalue_pos_type_implemplace_lvalue_pos_type_impl206 emplace_lvalue_pos_type_impl(T& x) : pos(x.begin()) {}
207
operator ()emplace_lvalue_pos_type_impl208 template <typename Iterator> void operator()(T& x, Iterator it)
209 {
210 pos = get_iterator(x.emplace_hint(pos, *it));
211 }
212 };
213
214 struct emplace_lvalue_pos_type
215 {
216 template <typename T>
generate(emplace_lvalue_pos_type,T & x)217 friend emplace_lvalue_pos_type_impl<T> generate(emplace_lvalue_pos_type, T& x)
218 {
219 return emplace_lvalue_pos_type_impl<T>(x);
220 }
221 } emplace_lvalue_pos;
222
223 // Run the exception tests in various combinations.
224
225 test_set* test_set_;
226 test_multiset* test_multiset_;
227 test_map* test_map_;
228 test_multimap* test_multimap_;
229
230 using test::default_generator;
231 using test::limited_range;
232 using test::generate_collisions;
233
234 // clang-format off
235 UNORDERED_TEST(insert_exception_test,
236 ((test_set_)(test_multiset_)(test_map_)(test_multimap_))
237 ((insert_lvalue)(insert_lvalue_begin)(insert_lvalue_end)
238 (insert_lvalue_pos)(insert_single_item_range)
239 (emplace_lvalue)(emplace_lvalue_begin)(emplace_lvalue_end)
240 (emplace_lvalue_pos)
241 )
242 ((default_generator)(limited_range)(generate_collisions))
243 )
244
245 UNORDERED_TEST(insert_rehash_exception_test,
246 ((test_set_)(test_multiset_)(test_map_)(test_multimap_))
247 ((insert_lvalue)(insert_lvalue_begin)(insert_lvalue_end)
248 (insert_lvalue_pos)(insert_single_item_range)
249 (emplace_lvalue)(emplace_lvalue_begin)(emplace_lvalue_end)
250 (emplace_lvalue_pos)
251 )
252 ((default_generator)(limited_range)(generate_collisions))
253 )
254 // clang-format on
255
256 // Repeat insert tests with pairs
257
258 struct pair_emplace_type : inserter_base
259 {
operator ()pair_emplace_type260 template <typename T, typename Iterator> void operator()(T& x, Iterator it)
261 {
262 x.emplace(boost::unordered::piecewise_construct,
263 boost::make_tuple(it->first), boost::make_tuple(it->second));
264 }
265 } pair_emplace;
266
267 struct pair_emplace2_type : inserter_base
268 {
operator ()pair_emplace2_type269 template <typename T, typename Iterator> void operator()(T& x, Iterator it)
270 {
271 x.emplace_hint(x.begin(), boost::unordered::piecewise_construct,
272 boost::make_tuple(it->first),
273 boost::make_tuple(it->second.tag1_, it->second.tag2_));
274 }
275 } pair_emplace2;
276
277 test_pair_set* test_pair_set_;
278 test_pair_multiset* test_pair_multiset_;
279
280 // clang-format off
281 UNORDERED_TEST(insert_exception_test,
282 ((test_pair_set_)(test_pair_multiset_)(test_map_)(test_multimap_))
283 ((pair_emplace)(pair_emplace2))
284 ((default_generator)(limited_range)(generate_collisions))
285 )
286 UNORDERED_TEST(insert_rehash_exception_test,
287 ((test_pair_set_)(test_pair_multiset_)(test_map_)(test_multimap_))
288 ((pair_emplace)(pair_emplace2))
289 ((default_generator)(limited_range)(generate_collisions))
290 )
291 // clang-format on
292
293 // Test inserting using operator[]
294
295 struct try_emplace_type : inserter_base
296 {
operator ()try_emplace_type297 template <typename T, typename Iterator> void operator()(T& x, Iterator it)
298 {
299 x.try_emplace(it->first, it->second);
300 }
301 } try_emplace;
302
303 struct try_emplace2_type : inserter_base
304 {
operator ()try_emplace2_type305 template <typename T, typename Iterator> void operator()(T& x, Iterator it)
306 {
307 x.try_emplace(it->first, it->second.tag1_, it->second.tag2_);
308 }
309 } try_emplace2;
310
311 struct map_inserter_base
312 {
exception_checkmap_inserter_base313 template <typename T> void exception_check(T& x, test::strong<T>& strong)
314 {
315 std::string scope(test::scope);
316
317 if (scope.find("hash::operator()") == std::string::npos &&
318 scope.find("::operator=") == std::string::npos)
319 strong.test(x, test::detail::tracker.count_allocations);
320 }
321
322 template <typename T, typename Iterator>
trackmap_inserter_base323 void track(T& tracker, Iterator begin, Iterator end)
324 {
325 for (; begin != end; ++begin) {
326 tracker[begin->first] = begin->second;
327 }
328 }
329 };
330
331 struct map_insert_operator_type : map_inserter_base
332 {
operator ()map_insert_operator_type333 template <typename T, typename Iterator> void operator()(T& x, Iterator it)
334 {
335 x[it->first] = it->second;
336 }
337 } map_insert_operator;
338
339 struct map_insert_or_assign_type : map_inserter_base
340 {
operator ()map_insert_or_assign_type341 template <typename T, typename Iterator> void operator()(T& x, Iterator it)
342 {
343 x.insert_or_assign(it->first, it->second);
344 }
345 } map_insert_or_assign;
346
347 // clang-format off
348 UNORDERED_TEST(insert_exception_test,
349 ((test_map_))
350 ((try_emplace)(try_emplace2)(map_insert_operator)(map_insert_or_assign))
351 ((default_generator)(limited_range)(generate_collisions))
352 )
353 UNORDERED_TEST(insert_rehash_exception_test,
354 ((test_map_))
355 ((try_emplace)(try_emplace2)(map_insert_operator)(map_insert_or_assign))
356 ((default_generator)(limited_range)(generate_collisions))
357 )
358 // clang-format on
359
360 // Range insert tests
361
362 template <typename T, typename Values>
insert_range_exception_test_impl(T x,Values const & v)363 void insert_range_exception_test_impl(T x, Values const& v)
364 {
365 test::ordered<T> tracker;
366 tracker.insert(x.begin(), x.end());
367
368 try {
369 ENABLE_EXCEPTIONS;
370 x.insert(v.begin(), v.end());
371 } catch (...) {
372 test::check_equivalent_keys(x);
373 throw;
374 }
375
376 test::check_equivalent_keys(x);
377 tracker.insert(v.begin(), v.end());
378 tracker.compare(x);
379 }
380
381 template <typename T>
insert_range_exception_test(T *,test::random_generator gen)382 void insert_range_exception_test(T*, test::random_generator gen)
383 {
384 for (int i = 0; i < 5; ++i) {
385 test::random_values<T> v(10, gen);
386 T x;
387
388 EXCEPTION_LOOP(insert_range_exception_test_impl(x, v));
389 }
390 }
391
392 template <typename T>
insert_range_rehash_exception_test(T *,test::random_generator gen)393 void insert_range_rehash_exception_test(T*, test::random_generator gen)
394 {
395 for (int i = 0; i < 5; ++i) {
396 T x;
397 rehash_prep(x);
398
399 test::random_values<T> v2(5, gen);
400 EXCEPTION_LOOP(insert_range_exception_test_impl(x, v2));
401 }
402 }
403
404 // clang-format off
405 UNORDERED_TEST(insert_range_exception_test,
406 ((test_set_)(test_multiset_)(test_map_)(test_multimap_))
407 ((default_generator)(limited_range)(generate_collisions))
408 )
409
410 UNORDERED_TEST(insert_range_rehash_exception_test,
411 ((test_set_)(test_multiset_)(test_map_)(test_multimap_))
412 ((default_generator)(limited_range)(generate_collisions))
413 )
414 // clang-format on
415
416 RUN_TESTS()
417