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>
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