| 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 | |
| 19 | namespace 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 | |
| 46 | test::seed_t initialize_seed(9387); |
| 47 | |
| 48 | template <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 | |
| 74 | template <class T> struct self_swap_test1 : self_swap_base<T> |
| 75 | { |
| 76 | }; |
| 77 | |
| 78 | template <class T> struct self_swap_test2 : self_swap_base<T> |
| 79 | { |
| 80 | self_swap_test2() : self_swap_base<T>(100) {} |
| 81 | }; |
| 82 | |
| 83 | template <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 | |
| 143 | template <class T> struct swap_test1 : swap_base<T> |
| 144 | { |
| 145 | swap_test1() : swap_base<T>(0, 0, 0, 0) {} |
| 146 | }; |
| 147 | |
| 148 | template <class T> struct swap_test2 : swap_base<T> |
| 149 | { |
| 150 | swap_test2() : swap_base<T>(60, 0, 0, 0) {} |
| 151 | }; |
| 152 | |
| 153 | template <class T> struct swap_test3 : swap_base<T> |
| 154 | { |
| 155 | swap_test3() : swap_base<T>(0, 60, 0, 0) {} |
| 156 | }; |
| 157 | |
| 158 | template <class T> struct swap_test4 : swap_base<T> |
| 159 | { |
| 160 | swap_test4() : swap_base<T>(10, 10, 1, 2) {} |
| 161 | }; |
| 162 | |
| 163 | template <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 | |
| 224 | template <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 | |
| 229 | template <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 | |
| 234 | template <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 | |
| 239 | template <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 | |
| 246 | using unordered_flat_set = boost::unordered_flat_set<int, boost::hash<int>, |
| 247 | std::equal_to<int>, test::allocator1<int> >; |
| 248 | using unordered_flat_map = boost::unordered_flat_map<int, int, boost::hash<int>, |
| 249 | std::equal_to<int>, test::allocator1<std::pair<int const, int> > >; |
| 250 | using unordered_node_set = boost::unordered_node_set<int, boost::hash<int>, |
| 251 | std::equal_to<int>, test::allocator1<int> >; |
| 252 | using 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 | |
| 261 | typedef boost::unordered_set<int, boost::hash<int>, std::equal_to<int>, |
| 262 | test::allocator1<int> > |
| 263 | unordered_set; |
| 264 | typedef boost::unordered_map<int, int, boost::hash<int>, std::equal_to<int>, |
| 265 | test::allocator1<std::pair<int const, int> > > |
| 266 | unordered_map; |
| 267 | typedef boost::unordered_multiset<int, boost::hash<int>, std::equal_to<int>, |
| 268 | test::allocator1<int> > |
| 269 | unordered_multiset; |
| 270 | typedef 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) |
| 284 | EXCEPTION_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 |
| 292 | EXCEPTION_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 | |
| 298 | RUN_TESTS() |
| 299 | |