1
2// Copyright 2006-2009 Daniel James.
3// Copyright 2022 Christian Mazakas.
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6
7#define BOOST_ENABLE_ASSERT_HANDLER
8#include <boost/assert.hpp>
9
10#include "./containers.hpp"
11
12#include "../helpers/invariants.hpp"
13#include "../helpers/random_values.hpp"
14#include "../helpers/tracker.hpp"
15#include "../objects/test.hpp"
16
17#include <sstream>
18
19namespace boost {
20 void assertion_failed(
21 char const* expr, char const* function, char const* file, long line)
22 {
23 std::stringstream ss;
24 ss << expr << "\nin " << function << " failed at : " << file << ", line "
25 << line;
26
27 throw std::runtime_error(ss.str());
28 }
29
30 void assertion_failed_msg(char const* expr, char const* msg,
31 char const* function, char const* file, long line)
32 {
33 std::stringstream ss;
34 ss << expr << "\nin " << function << " failed at : " << file << ", line "
35 << line << "\n"
36 << msg;
37
38 throw std::runtime_error(ss.str());
39 }
40} // namespace boost
41
42#if defined(BOOST_MSVC)
43#pragma warning(disable : 4512) // assignment operator could not be generated
44#endif
45
46test::seed_t initialize_seed(9387);
47
48template <class T> struct self_swap_base : public test::exception_base
49{
50 test::random_values<T> values;
51 self_swap_base(std::size_t count = 0) : values(count, test::limited_range) {}
52
53 typedef T data_type;
54 T init() const { return T(values.begin(), values.end()); }
55
56 void run(T& x) const
57 {
58 x.swap(x);
59
60 DISABLE_EXCEPTIONS;
61 test::check_container(x, this->values);
62 test::check_equivalent_keys(x);
63 }
64
65 void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x) const
66 {
67 (void)x;
68
69 BOOST_ERROR("An exception leaked when it should not have. Allocator "
70 "equality assertion must precede all other ops");
71 }
72};
73
74template <class T> struct self_swap_test1 : self_swap_base<T>
75{
76};
77
78template <class T> struct self_swap_test2 : self_swap_base<T>
79{
80 self_swap_test2() : self_swap_base<T>(100) {}
81};
82
83template <class T> struct swap_base : public test::exception_base
84{
85 const test::random_values<T> x_values, y_values;
86 const T initial_x, initial_y;
87
88 typedef typename T::hasher hasher;
89 typedef typename T::key_equal key_equal;
90 typedef typename T::allocator_type allocator_type;
91
92 swap_base(unsigned int count1, unsigned int count2, int tag1, int tag2)
93 : x_values(count1, test::limited_range),
94 y_values(count2, test::limited_range),
95 initial_x(x_values.begin(), x_values.end(), 0, hasher(tag1),
96 key_equal(tag1), allocator_type(tag1)),
97 initial_y(y_values.begin(), y_values.end(), 0, hasher(tag2),
98 key_equal(tag2),
99 allocator_type(T::allocator_type::propagate_on_container_swap::value
100 ? tag2
101 : tag1))
102 {
103 }
104
105 struct data_type
106 {
107 data_type(T const& x_, T const& y_) : x(x_), y(y_) {}
108
109 T x, y;
110 };
111
112 data_type init() const { return data_type(initial_x, initial_y); }
113
114 void run(data_type& d) const
115 {
116 try {
117 d.x.swap(d.y);
118 } catch (std::runtime_error&) {
119 }
120
121 DISABLE_EXCEPTIONS;
122 test::check_container(d.x, this->y_values);
123 test::check_equivalent_keys(d.x);
124 test::check_container(d.y, this->x_values);
125 test::check_equivalent_keys(d.y);
126 }
127
128 void check BOOST_PREVENT_MACRO_SUBSTITUTION(data_type const& d) const
129 {
130 std::string scope(test::scope);
131
132 // TODO: In C++11 exceptions are only allowed in the swap function.
133 BOOST_TEST(scope == "hash::hash(hash)" ||
134 scope == "hash::operator=(hash)" ||
135 scope == "equal_to::equal_to(equal_to)" ||
136 scope == "equal_to::operator=(equal_to)");
137
138 test::check_equivalent_keys(d.x);
139 test::check_equivalent_keys(d.y);
140 }
141};
142
143template <class T> struct swap_test1 : swap_base<T>
144{
145 swap_test1() : swap_base<T>(0, 0, 0, 0) {}
146};
147
148template <class T> struct swap_test2 : swap_base<T>
149{
150 swap_test2() : swap_base<T>(60, 0, 0, 0) {}
151};
152
153template <class T> struct swap_test3 : swap_base<T>
154{
155 swap_test3() : swap_base<T>(0, 60, 0, 0) {}
156};
157
158template <class T> struct swap_test4 : swap_base<T>
159{
160 swap_test4() : swap_base<T>(10, 10, 1, 2) {}
161};
162
163template <class T> struct unequal_alloc_swap_base : public test::exception_base
164{
165 const test::random_values<T> x_values, y_values;
166 const T initial_x, initial_y;
167
168 typedef typename T::hasher hasher;
169 typedef typename T::key_equal key_equal;
170 typedef typename T::allocator_type allocator_type;
171
172 unequal_alloc_swap_base(unsigned int count1, unsigned int count2)
173 : x_values(count1, test::limited_range),
174 y_values(count2, test::limited_range),
175 initial_x(x_values.begin(), x_values.end(), 0, allocator_type(1337)),
176 initial_y(y_values.begin(), y_values.end(), 0, allocator_type(7331))
177 {
178 }
179
180 struct data_type
181 {
182 data_type(T const& x_, T const& y_) : x(x_), y(y_) {}
183
184 T x, y;
185 };
186
187 data_type init() const { return data_type(initial_x, initial_y); }
188
189 void run(data_type& d) const
190 {
191 bool assert_threw = false;
192
193 BOOST_TEST(d.x.get_allocator() != d.y.get_allocator());
194
195 try {
196 d.x.swap(d.y);
197 } catch (std::runtime_error&) {
198 assert_threw = true;
199 }
200
201 DISABLE_EXCEPTIONS;
202 BOOST_TEST(assert_threw);
203 test::check_container(d.x, this->x_values);
204 test::check_equivalent_keys(d.x);
205 test::check_container(d.y, this->y_values);
206 test::check_equivalent_keys(d.y);
207 }
208
209 void check BOOST_PREVENT_MACRO_SUBSTITUTION(data_type const& d) const
210 {
211 std::string scope(test::scope);
212
213 // TODO: In C++11 exceptions are only allowed in the swap function.
214 BOOST_TEST(scope == "hash::hash(hash)" ||
215 scope == "hash::operator=(hash)" ||
216 scope == "equal_to::equal_to(equal_to)" ||
217 scope == "equal_to::operator=(equal_to)");
218
219 test::check_equivalent_keys(d.x);
220 test::check_equivalent_keys(d.y);
221 }
222};
223
224template <class T> struct unequal_alloc_swap_test1 : unequal_alloc_swap_base<T>
225{
226 unequal_alloc_swap_test1() : unequal_alloc_swap_base<T>(0, 0) {}
227};
228
229template <class T> struct unequal_alloc_swap_test2 : unequal_alloc_swap_base<T>
230{
231 unequal_alloc_swap_test2() : unequal_alloc_swap_base<T>(0, 10) {}
232};
233
234template <class T> struct unequal_alloc_swap_test3 : unequal_alloc_swap_base<T>
235{
236 unequal_alloc_swap_test3() : unequal_alloc_swap_base<T>(10, 0) {}
237};
238
239template <class T> struct unequal_alloc_swap_test4 : unequal_alloc_swap_base<T>
240{
241 unequal_alloc_swap_test4() : unequal_alloc_swap_base<T>(10, 10) {}
242};
243
244#if defined(BOOST_UNORDERED_FOA_TESTS)
245
246using unordered_flat_set = boost::unordered_flat_set<int, boost::hash<int>,
247 std::equal_to<int>, test::allocator1<int> >;
248using unordered_flat_map = boost::unordered_flat_map<int, int, boost::hash<int>,
249 std::equal_to<int>, test::allocator1<std::pair<int const, int> > >;
250using unordered_node_set = boost::unordered_node_set<int, boost::hash<int>,
251 std::equal_to<int>, test::allocator1<int> >;
252using unordered_node_map = boost::unordered_node_map<int, int, boost::hash<int>,
253 std::equal_to<int>, test::allocator1<std::pair<int const, int> > >;
254
255#define SWAP_CONTAINER_SEQ \
256 (unordered_flat_set)(unordered_flat_map) \
257 (unordered_node_set)(unordered_node_map)
258
259#else
260
261typedef boost::unordered_set<int, boost::hash<int>, std::equal_to<int>,
262 test::allocator1<int> >
263 unordered_set;
264typedef boost::unordered_map<int, int, boost::hash<int>, std::equal_to<int>,
265 test::allocator1<std::pair<int const, int> > >
266 unordered_map;
267typedef boost::unordered_multiset<int, boost::hash<int>, std::equal_to<int>,
268 test::allocator1<int> >
269 unordered_multiset;
270typedef boost::unordered_multimap<int, int, boost::hash<int>,
271 std::equal_to<int>, test::allocator1<std::pair<int const, int> > >
272 unordered_multimap;
273
274#define SWAP_CONTAINER_SEQ \
275 (unordered_set)(unordered_map)(unordered_multiset)(unordered_multimap)
276#endif
277
278// FOA containers deliberately choose to not offer the strong exception
279// guarantee so we can't reliably test what happens if swapping one of the data
280// members throws
281//
282// clang-format off
283#if !defined(BOOST_UNORDERED_FOA_TESTS)
284EXCEPTION_TESTS(
285 (self_swap_test1)(self_swap_test2)
286 (swap_test1)(swap_test2)(swap_test3)(swap_test4),
287 CONTAINER_SEQ)
288#endif
289
290// want to prove that when assertions are defined as throwing operations that we
291// uphold invariants
292EXCEPTION_TESTS(
293 (unequal_alloc_swap_test1)(unequal_alloc_swap_test2)
294 (unequal_alloc_swap_test3)(unequal_alloc_swap_test4),
295 SWAP_CONTAINER_SEQ)
296// clang-format on
297
298RUN_TESTS()
299

source code of boost/libs/unordered/test/exception/swap_exception_tests.cpp