1 /* 2 Copyright (c) 2005-2021 Intel Corporation 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 #if __INTEL_COMPILER && _MSC_VER 18 #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated 19 #endif 20 21 #include <common/concurrent_priority_queue_common.h> 22 #include <common/initializer_list_support.h> 23 #include <common/container_move_support.h> 24 #include <common/containers_common.h> 25 #include <common/test_comparisons.h> 26 #include <scoped_allocator> 27 28 //! \file conformance_concurrent_priority_queue.cpp 29 //! \brief Test for [containers.concurrent_priority_queue] specification 30 31 void test_to_vector() { 32 using equality_comparison_helpers::toVector; 33 int array[] = {1, 5, 6, 8, 4, 7}; 34 oneapi::tbb::blocked_range<int*> range = utils::make_blocked_range(array); 35 std::vector<int> source(range.begin(), range.end()); 36 37 oneapi::tbb::concurrent_priority_queue<int> q(source.begin(), source.end()); 38 std::vector<int> from_cpq = toVector(q); 39 40 std::sort(source.begin(), source.end()); 41 REQUIRE_MESSAGE(source == from_cpq, "equality_comparison_helpers::toVector incorrectly copied items from CPQ"); 42 } 43 44 void test_basic() { 45 const int NUMBER = 10; 46 utils::FastRandom<> rnd(1234); 47 48 std::vector<int> arrInt; 49 for (int i = 0; i < NUMBER; ++i) 50 arrInt.emplace_back(rnd.get()); 51 52 type_tester(arrInt); // Test integers 53 } 54 55 void test_initializer_list() { 56 using namespace initializer_list_support_tests; 57 test_initializer_list_support<oneapi::tbb::concurrent_priority_queue<char>>({1, 2, 3, 4, 5}); 58 test_initializer_list_support<oneapi::tbb::concurrent_priority_queue<int>>({}); 59 } 60 61 struct SpecialMemberCalls { 62 std::size_t copy_ctor_called_times; 63 std::size_t move_ctor_called_times; 64 std::size_t copy_assign_called_times; 65 std::size_t move_assign_called_times; 66 }; // struct SpecialMemberCalls 67 68 bool operator==( const SpecialMemberCalls& lhs, const SpecialMemberCalls& rhs ) { 69 return lhs.copy_ctor_called_times == rhs.copy_ctor_called_times && 70 lhs.move_ctor_called_times == rhs.move_ctor_called_times && 71 lhs.copy_assign_called_times == rhs.copy_assign_called_times && 72 lhs.move_assign_called_times == rhs.move_assign_called_times; 73 } 74 75 template <typename CounterType> 76 struct MoveOperationTrackerBase { 77 static CounterType copy_ctor_called_times; 78 static CounterType move_ctor_called_times; 79 static CounterType copy_assign_called_times; 80 static CounterType move_assign_called_times; 81 82 static SpecialMemberCalls special_member_calls() { 83 return SpecialMemberCalls{copy_ctor_called_times, move_ctor_called_times, copy_assign_called_times, move_assign_called_times}; 84 } 85 static CounterType value_counter; 86 std::size_t value; 87 88 MoveOperationTrackerBase() : value(++value_counter) {} 89 explicit MoveOperationTrackerBase( const std::size_t val ) : value(val) {} 90 ~MoveOperationTrackerBase() { value = 0; } 91 92 MoveOperationTrackerBase( const MoveOperationTrackerBase& other ) : value(other.value) { 93 REQUIRE_MESSAGE(other.value, "The object has been moved or destroyed"); 94 ++copy_ctor_called_times; 95 } 96 97 MoveOperationTrackerBase( MoveOperationTrackerBase&& other ) noexcept : value(other.value) { 98 REQUIRE_MESSAGE(other.value, "The object has been moved or destroyed"); 99 other.value = 0; 100 ++move_ctor_called_times; 101 } 102 103 MoveOperationTrackerBase& operator=( const MoveOperationTrackerBase& other ) { 104 REQUIRE_MESSAGE(other.value, "The object has been moved or destroyed"); 105 value = other.value; 106 ++copy_assign_called_times; 107 return *this; 108 } 109 110 MoveOperationTrackerBase& operator=( MoveOperationTrackerBase&& other ) noexcept { 111 REQUIRE_MESSAGE(other.value, "The object has been moved or destroyed"); 112 value = other.value; 113 other.value = 0; 114 ++move_assign_called_times; 115 return *this; 116 } 117 118 bool operator<( const MoveOperationTrackerBase& other ) const { 119 REQUIRE_MESSAGE(value, "The object has been moved or destroyed"); 120 REQUIRE_MESSAGE(other.value, "The object has been moved or destroyed"); 121 return value < other.value; 122 } 123 }; // struct MoveOperationTrackerBase 124 125 template<typename CounterType> 126 bool operator==( const MoveOperationTrackerBase<CounterType>& lhs, const MoveOperationTrackerBase<CounterType>& rhs ) { 127 return !(lhs < rhs) && !(rhs < lhs); 128 } 129 130 using MoveOperationTracker = MoveOperationTrackerBase<std::size_t>; 131 using MoveOperationTrackerConc = MoveOperationTrackerBase<std::atomic<std::size_t>>; 132 133 template <typename CounterType> CounterType MoveOperationTrackerBase<CounterType>::copy_ctor_called_times(0); 134 template <typename CounterType> CounterType MoveOperationTrackerBase<CounterType>::move_ctor_called_times(0); 135 template <typename CounterType> CounterType MoveOperationTrackerBase<CounterType>::copy_assign_called_times(0); 136 template <typename CounterType> CounterType MoveOperationTrackerBase<CounterType>::move_assign_called_times(0); 137 template <typename CounterType> CounterType MoveOperationTrackerBase<CounterType>::value_counter(0); 138 139 template <typename Allocator = std::allocator<MoveOperationTracker>> 140 struct CPQSrcFixture { 141 CPQSrcFixture& operator=( const CPQSrcFixture& ) = delete; 142 143 enum {default_container_size = 100}; 144 using cpq_compare_type = std::less<MoveOperationTracker>; 145 using cpq_allocator_type = typename std::allocator_traits<Allocator>::template rebind_alloc<MoveOperationTracker>; 146 using cpq_type = oneapi::tbb::concurrent_priority_queue<MoveOperationTracker, cpq_compare_type, cpq_allocator_type>; 147 148 cpq_type cpq_src; 149 const std::size_t container_size; 150 151 void init() { 152 std::size_t& mcct = MoveOperationTracker::move_ctor_called_times; 153 std::size_t& ccct = MoveOperationTracker::copy_ctor_called_times; 154 std::size_t& cact = MoveOperationTracker::copy_assign_called_times; 155 std::size_t& mact = MoveOperationTracker::move_assign_called_times; 156 mcct = ccct = cact = mact = 0; 157 158 for (std::size_t i = 1; i <= container_size; ++i) { 159 cpq_src.push(MoveOperationTracker(i)); 160 } 161 REQUIRE_MESSAGE(cpq_src.size() == container_size, "Error in test setup"); 162 } 163 164 CPQSrcFixture( std::size_t size = default_container_size ) 165 : CPQSrcFixture(typename cpq_type::allocator_type(), size) {} 166 167 CPQSrcFixture( const typename cpq_type::allocator_type& a, std::size_t size = default_container_size ) 168 : cpq_src(a), container_size(size) 169 { 170 init(); 171 } 172 }; // struct CPQSrcFixture 173 174 void test_steal_move_ctor() { 175 using fixture_type = CPQSrcFixture<>; 176 using container_type = typename fixture_type::cpq_type; 177 fixture_type fixture; 178 container_type src_copy{fixture.cpq_src}; 179 180 SpecialMemberCalls previous = MoveOperationTracker::special_member_calls(); 181 container_type dst{std::move(fixture.cpq_src)}; 182 REQUIRE_MESSAGE(previous == MoveOperationTracker::special_member_calls(), "Steal move ctor should not create any new elements"); 183 REQUIRE_MESSAGE(dst == src_copy, "cpq content changed during steal move"); 184 REQUIRE_MESSAGE(!(dst != src_copy), "cpq content changed during steal move"); 185 } 186 187 void test_steal_move_ctor_with_allocator() { 188 using arena_fixture_type = move_support_tests::TwoMemoryArenasFixture<MoveOperationTracker>; 189 using fixture_type = CPQSrcFixture<arena_fixture_type::allocator_type>; 190 191 arena_fixture_type arena_fixture(8 * fixture_type::default_container_size); 192 fixture_type fixture(arena_fixture.source_allocator); 193 fixture_type::cpq_type src_copy(fixture.cpq_src); 194 195 SpecialMemberCalls previous = MoveOperationTracker::special_member_calls(); 196 fixture_type::cpq_type dst(std::move(fixture.cpq_src), arena_fixture.source_allocator); 197 REQUIRE_MESSAGE(previous == MoveOperationTracker::special_member_calls(), "Steal move ctor should not create any new elements"); 198 REQUIRE_MESSAGE(dst == src_copy, "cpq content changed during steal move"); 199 REQUIRE_MESSAGE(!(dst != src_copy), "cpq content changed during steal move"); 200 } 201 202 void test_per_element_move_ctor_with_allocator() { 203 using arena_fixture_type = move_support_tests::TwoMemoryArenasFixture<MoveOperationTracker>; 204 using fixture_type = CPQSrcFixture<arena_fixture_type::allocator_type>; 205 206 arena_fixture_type arena_fixture(8 * fixture_type::default_container_size); 207 fixture_type fixture(arena_fixture.source_allocator); 208 fixture_type::cpq_type src_copy(fixture.cpq_src); 209 210 SpecialMemberCalls move_ctor_called_cpq_size_times = MoveOperationTracker::special_member_calls(); 211 move_ctor_called_cpq_size_times.move_ctor_called_times += fixture.container_size; 212 213 fixture_type::cpq_type dst(std::move(fixture.cpq_src), arena_fixture.dst_allocator); 214 REQUIRE_MESSAGE(move_ctor_called_cpq_size_times == MoveOperationTracker::special_member_calls(), 215 "Per element move ctor should move initialize all new elements"); 216 REQUIRE_MESSAGE(dst == src_copy, "cpq content changed during move"); 217 REQUIRE_MESSAGE(!(dst != src_copy), "cpq content changed during move"); 218 } 219 220 void test_steal_move_assign_operator() { 221 using fixture_type = CPQSrcFixture<>; 222 223 fixture_type fixture; 224 fixture_type::cpq_type src_copy(fixture.cpq_src); 225 226 fixture_type::cpq_type dst; 227 SpecialMemberCalls previous = MoveOperationTracker::special_member_calls(); 228 dst = std::move(fixture.cpq_src); 229 230 REQUIRE_MESSAGE(previous == MoveOperationTracker::special_member_calls(), "Steal move assign operator should not create any new elements"); 231 REQUIRE_MESSAGE(dst == src_copy, "cpq content changed during steal move assignment"); 232 REQUIRE_MESSAGE(!(dst != src_copy), "cpq content changed during steal move assignment"); 233 } 234 235 void test_steal_move_assign_operator_with_stateful_allocator() { 236 // Use stateful allocator which is propagated on move assignment 237 using arena_fixture_type = move_support_tests::TwoMemoryArenasFixture<MoveOperationTracker, /*POCMA = */std::true_type>; 238 using fixture_type = CPQSrcFixture<arena_fixture_type::allocator_type>; 239 240 arena_fixture_type arena_fixture(8 * fixture_type::default_container_size); 241 fixture_type fixture(arena_fixture.source_allocator); 242 fixture_type::cpq_type src_copy(fixture.cpq_src); 243 fixture_type::cpq_type dst(arena_fixture.dst_allocator); 244 245 SpecialMemberCalls previous = MoveOperationTracker::special_member_calls(); 246 dst = std::move(fixture.cpq_src); 247 REQUIRE_MESSAGE(previous == MoveOperationTracker::special_member_calls(), "Steal move assign operator should not create any new elements"); 248 REQUIRE_MESSAGE(dst == src_copy, "cpq content changed during steal move assignment"); 249 REQUIRE_MESSAGE(!(dst != src_copy), "cpq content changed during steal move assignment"); 250 } 251 252 void test_per_element_move_assign_operator() { 253 // Use stateful allocator which is not prepagated on move assignment 254 using arena_fixture_type = move_support_tests::TwoMemoryArenasFixture<MoveOperationTracker, /*POCMA = */std::false_type>; 255 using fixture_type = CPQSrcFixture<arena_fixture_type::allocator_type>; 256 257 arena_fixture_type arena_fixture(8 * fixture_type::default_container_size); 258 fixture_type fixture(arena_fixture.source_allocator); 259 fixture_type::cpq_type src_copy(fixture.cpq_src); 260 fixture_type::cpq_type dst(arena_fixture.dst_allocator); 261 262 SpecialMemberCalls move_ctor_called_cpq_size_times = MoveOperationTracker::special_member_calls(); 263 move_ctor_called_cpq_size_times.move_ctor_called_times += fixture.container_size; 264 dst = std::move(fixture.cpq_src); 265 REQUIRE_MESSAGE(move_ctor_called_cpq_size_times == MoveOperationTracker::special_member_calls(), 266 "Per element move assignment should move initialize all new elements"); 267 REQUIRE_MESSAGE(dst == src_copy, "cpq content changed during per element move assignment"); 268 REQUIRE_MESSAGE(!(dst != src_copy), "cpq content changed during per element move assignment"); 269 } 270 271 void test_cpq_move_constructor() { 272 test_steal_move_ctor(); 273 test_steal_move_ctor_with_allocator(); 274 test_per_element_move_ctor_with_allocator(); 275 } 276 277 void test_cpq_move_assignment() { 278 test_steal_move_assign_operator(); 279 test_steal_move_assign_operator_with_stateful_allocator(); 280 test_per_element_move_assign_operator(); 281 } 282 283 284 struct NoDefaultCtorType { 285 NoDefaultCtorType() = delete; 286 287 NoDefaultCtorType( std::size_t val1, std::size_t val2 ) : value1(val1), value2(val2) {} 288 bool operator<(const NoDefaultCtorType& other) const { 289 return value1 + value2 < other.value1 + other.value2; 290 } 291 292 std::size_t value1, value2; 293 }; // struct NoDefaultCtorType 294 295 struct ForwardInEmplaceTester { 296 int a; 297 static bool move_ctor_called; 298 static bool move_assign_called; 299 300 ForwardInEmplaceTester( int val ) : a(val) {} 301 ForwardInEmplaceTester( const ForwardInEmplaceTester& ) = default; 302 ForwardInEmplaceTester( ForwardInEmplaceTester&& ) = default; 303 304 ForwardInEmplaceTester( ForwardInEmplaceTester&& obj, int val ) : a(obj.a) { 305 move_ctor_called = true; 306 obj.a = val; 307 } 308 309 ForwardInEmplaceTester& operator=( const ForwardInEmplaceTester& ) = default; 310 311 ForwardInEmplaceTester& operator=( ForwardInEmplaceTester&& obj ) { 312 a = obj.a; 313 move_assign_called = true; 314 return *this; 315 } 316 317 bool operator<( const ForwardInEmplaceTester& ) const { return true; } 318 }; // struct ForwardInEmplaceTester 319 320 bool ForwardInEmplaceTester::move_ctor_called = false; 321 bool ForwardInEmplaceTester::move_assign_called = false; 322 323 void test_move_support_in_push_pop() { 324 std::size_t& mcct = MoveOperationTracker::move_ctor_called_times; 325 std::size_t& ccct = MoveOperationTracker::copy_ctor_called_times; 326 std::size_t& cact = MoveOperationTracker::copy_assign_called_times; 327 std::size_t& mact = MoveOperationTracker::move_assign_called_times; 328 mcct = ccct = cact = mact = 0; 329 330 oneapi::tbb::concurrent_priority_queue<MoveOperationTracker> q1; 331 332 REQUIRE_MESSAGE(mcct == 0, "Value must be zero-initialized"); 333 REQUIRE_MESSAGE(ccct == 0, "Value must be zero-initialized"); 334 335 q1.push(MoveOperationTracker{}); 336 REQUIRE_MESSAGE(mcct > 0, "Not working push(T&&)"); 337 REQUIRE_MESSAGE(ccct == 0, "Copying of arg occurred during push(T&&)"); 338 339 MoveOperationTracker ob; 340 const std::size_t prev_mcct = mcct; 341 q1.push(std::move(ob)); 342 REQUIRE_MESSAGE(mcct > prev_mcct, "Not working push(T&&)"); 343 REQUIRE_MESSAGE(ccct == 0, "Copying of arg occurred during push(T&&)"); 344 345 REQUIRE_MESSAGE(cact == 0, "Copy assignment called during push(T&&)"); 346 const std::size_t prev_mact = mact; 347 q1.try_pop(ob); 348 REQUIRE_MESSAGE(cact == 0, "Copy assignment called during try_pop(T&)"); 349 REQUIRE_MESSAGE(mact > prev_mact, "Move assignment was not called during try_pop(T&)"); 350 351 oneapi::tbb::concurrent_priority_queue<NoDefaultCtorType> q2; 352 q2.emplace(15, 3); 353 q2.emplace(2, 35); 354 q2.emplace(8, 8); 355 356 NoDefaultCtorType o(0, 0); 357 q2.try_pop(o); 358 REQUIRE_MESSAGE((o.value1 == 2 && o.value2 == 35), "Unexpected data popped; possible emplace() failure"); 359 q2.try_pop(o); 360 REQUIRE_MESSAGE((o.value1 == 15 && o.value2 == 3), "Unexpected data popped; possible emplace() failure"); 361 q2.try_pop(o); 362 REQUIRE_MESSAGE((o.value1 == 8 && o.value2 == 8), "Unexpected data popped; possible emplace() failure"); 363 REQUIRE_MESSAGE(!q2.try_pop(o), "The queue should be empty"); 364 365 oneapi::tbb::concurrent_priority_queue<ForwardInEmplaceTester> q3; 366 REQUIRE(ForwardInEmplaceTester::move_ctor_called == false); 367 q3.emplace(ForwardInEmplaceTester{5}, 2); 368 REQUIRE_MESSAGE(ForwardInEmplaceTester::move_ctor_called == true, "Not used std::forward in emplace()"); 369 ForwardInEmplaceTester obj(0); 370 q3.try_pop(obj); 371 372 REQUIRE_MESSAGE(ForwardInEmplaceTester::move_assign_called == true, "Not used move assignment in try_pop"); 373 REQUIRE_MESSAGE(obj.a == 5, "Not used std::forward in emplace"); 374 REQUIRE_MESSAGE(!q3.try_pop(obj), "The queue should be empty"); 375 } 376 377 // Comparator with assert in default ctor 378 template <typename T> 379 class LessA : public std::less<T> { 380 public: 381 explicit LessA( bool no_assert = false ) { 382 REQUIRE_MESSAGE(no_assert, "Default ctor should not be called"); 383 } 384 }; // class LessA 385 386 // TODO: consider use of TEST_SUITE for these tests 387 // TODO: combine with the constructors test from the common part 388 void test_ctors_dtor_accessors() { 389 std::vector<int> v; 390 std::allocator<int> a; 391 392 using cpq_type = oneapi::tbb::concurrent_priority_queue<int, std::less<int>>; 393 using cpq_with_compare_type = oneapi::tbb::concurrent_priority_queue<int, LessA<int>>; 394 using cpq_with_compare_and_allocator_type = oneapi::tbb::concurrent_priority_queue<int, LessA<int>, std::allocator<int>>; 395 396 LessA<int> l(true); 397 398 // Test default ctor 399 cpq_type cpq1; 400 REQUIRE_MESSAGE(cpq1.size() == 0, "Failed size test for default ctor"); 401 REQUIRE_MESSAGE(cpq1.empty(), "Failed empty test for default ctor"); 402 403 // Test capacity ctor 404 cpq_type cpq2(42); 405 REQUIRE_MESSAGE(cpq2.size() == 0, "Failed size test for capacity ctor"); 406 REQUIRE_MESSAGE(cpq2.empty(), "Failed empty test for capacity ctor"); 407 408 // Test compare ctor 409 cpq_with_compare_type cpq3(l); 410 REQUIRE_MESSAGE(cpq3.size() == 0, "Failed size test for compare ctor"); 411 REQUIRE_MESSAGE(cpq3.empty(), "Failed empty test for compare ctor"); 412 413 // Test compare+allocator ctor 414 cpq_with_compare_and_allocator_type cpq4(l, a); 415 REQUIRE_MESSAGE(cpq4.size() == 0, "Failed size test for compare+allocator ctor"); 416 REQUIRE_MESSAGE(cpq4.empty(), "Failed empty test for compare+allocator ctor"); 417 418 // Test capacity+compare ctor 419 cpq_with_compare_type cpq5(42, l); 420 REQUIRE_MESSAGE(cpq5.size() == 0, "Failed size test for capacity+compare ctor"); 421 REQUIRE_MESSAGE(cpq5.empty(), "Failed empty test for capacity+compare ctor"); 422 423 // Test capacity+compare+allocator ctor 424 cpq_with_compare_and_allocator_type cpq6(42, l, a); 425 REQUIRE_MESSAGE(cpq6.size() == 0, "Failed size test for capacity+compare+allocator ctor"); 426 REQUIRE_MESSAGE(cpq6.empty(), "Failed empty test for capacity+compare+allocator ctor"); 427 428 // Test half-open range ctor 429 for (int i = 0; i < 42; ++i) { 430 v.emplace_back(i); 431 } 432 using equality_comparison_helpers::toVector; 433 cpq_type cpq7(v.begin(), v.end()); 434 REQUIRE_MESSAGE(cpq7.size() == 42, "Failed size test for half-open range ctor"); 435 REQUIRE_MESSAGE(!cpq7.empty(), "Failed empty test for half-open range test"); 436 REQUIRE_MESSAGE(v == toVector(cpq7), "Failed equality test for half-open range ctor"); 437 438 // Test half-open range + compare ctor 439 cpq_with_compare_type cpq8(v.begin(), v.end(), l); 440 REQUIRE_MESSAGE(cpq8.size() == 42, "Failed size test for half-open range+compare ctor"); 441 REQUIRE_MESSAGE(!cpq8.empty(), "Failed empty test for half-open range+compare ctor"); 442 REQUIRE_MESSAGE(v == toVector(cpq8), "Failed equality test for half-open range+compare ctor"); 443 444 // Test copy ctor 445 cpq_type cpq9(cpq7); 446 REQUIRE_MESSAGE(cpq9.size() == cpq7.size(), "Failed size test for copy ctor"); 447 REQUIRE_MESSAGE(!cpq9.empty(), "Failed empty test for copy ctor"); 448 REQUIRE_MESSAGE(cpq9 == cpq7, "Failed equality test for copy ctor"); 449 } 450 451 void test_assignment_clear_swap() { 452 using equality_comparison_helpers::toVector; 453 using cpq_type = oneapi::tbb::concurrent_priority_queue<int, std::less<int>>; 454 std::vector<int> v; 455 int e; 456 457 for( int i = 0; i < 42; ++i ) 458 v.emplace_back(i); 459 460 cpq_type q(v.begin(), v.end()); 461 cpq_type qo; 462 463 // Test assignment 464 qo = q; 465 REQUIRE_MESSAGE(qo.size() == 42, "Failed assignment size test"); 466 REQUIRE_MESSAGE(!qo.empty(), "Failed assignment empty test"); 467 REQUIRE_MESSAGE(v == toVector(qo), "Failed assignment equality test"); 468 REQUIRE_MESSAGE(qo == q, "Failed assignment equality test"); 469 REQUIRE_MESSAGE(!(qo != q), "Failed assignment inequality test"); 470 471 cpq_type assigned_q; 472 // Testing assign member function 473 assigned_q.assign(v.begin(), v.end()); 474 REQUIRE_MESSAGE(assigned_q.size() == 42, "Failed assign size test"); 475 REQUIRE_MESSAGE(!assigned_q.empty(), "Failed assign empty test"); 476 REQUIRE_MESSAGE(v == toVector(assigned_q), "Failed assign equality test"); 477 478 // Testing clear() 479 q.clear(); 480 REQUIRE_MESSAGE(q.size() == 0, "Failed clear size test"); 481 REQUIRE_MESSAGE(q.empty(), "Failed clear empty test"); 482 483 // Test assignment again 484 for (std::size_t i = 0; i < 5; ++i) 485 qo.try_pop(e); 486 487 q = qo; 488 REQUIRE_MESSAGE(q.size() == 37, "Failed assignment size test"); 489 REQUIRE_MESSAGE(!q.empty(), "Failed assignment empty test"); 490 491 for (std::size_t i = 0; i < 5; ++i) 492 qo.try_pop(e); 493 494 q.swap(qo); 495 496 REQUIRE_MESSAGE(q.size() == 32, "Failed swap size test"); 497 REQUIRE_MESSAGE(!q.empty(), "Failed swap empty test"); 498 REQUIRE_MESSAGE(qo.size() == 37, "Failed swap size test"); 499 REQUIRE_MESSAGE(!qo.empty(), "Failed swap empty test"); 500 } 501 502 void test_serial_push_pop() { 503 oneapi::tbb::concurrent_priority_queue<int, std::less<int>> q(MAX_ITER); 504 int e = 42; 505 int prev = INT_MAX; 506 std::size_t count = 0; 507 508 // Test serial push 509 for (std::size_t i = 0; i < MAX_ITER; ++i) { 510 push_selector(q, e, i); 511 e = e*-1 + int(i); 512 } 513 514 REQUIRE_MESSAGE(q.size() == MAX_ITER, "Failed push size test"); 515 REQUIRE_MESSAGE(!q.empty(), "Failed push empty test"); 516 517 // Test serial pop 518 while(!q.empty()) { 519 REQUIRE_MESSAGE(q.try_pop(e), "Failed pop test"); 520 REQUIRE_MESSAGE(prev >= e, "Failed pop priority test"); 521 prev = e; 522 ++count; 523 524 REQUIRE_MESSAGE(q.size() == MAX_ITER - count, "Failed pop size test"); 525 REQUIRE_MESSAGE((!q.empty() || count == MAX_ITER), "Failed pop empty test"); 526 } 527 REQUIRE_MESSAGE(!q.try_pop(e), "Failed: successful pop from the empty queue"); 528 } 529 530 void test_concurrent(std::size_t n) { 531 test_parallel_push_pop<std::less<int>>(n, INT_MAX, INT_MIN); 532 test_parallel_push_pop<std::less<int>>(n, (unsigned char)CHAR_MAX, (unsigned char)CHAR_MIN); 533 534 test_flogger<std::less<int>, int>(n); 535 test_flogger<std::less<int>, unsigned char>(n); 536 537 MoveOperationTrackerConc::copy_assign_called_times = 0; 538 test_flogger<std::less<MoveOperationTrackerConc>, MoveOperationTrackerConc>(n); 539 REQUIRE_MESSAGE(MoveOperationTrackerConc::copy_assign_called_times == 0, "Copy assignment called during try_pop"); 540 } 541 542 void test_multithreading() { 543 for (std::size_t n = utils::MinThread; n != utils::MaxThread; ++n) { 544 test_concurrent(n); 545 } 546 } 547 548 struct CPQTraits { 549 template <typename T> 550 using container_value_type = T; 551 552 template <typename T, typename Allocator> 553 using container_type = oneapi::tbb::concurrent_priority_queue<T, std::less<T>, Allocator>; 554 }; // struct CPQTraits 555 556 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT 557 template <template <typename...>typename TQueue> 558 void TestDeductionGuides() { 559 using ComplexType = const std::string*; 560 std::string s("s"); 561 std::vector<ComplexType> v; 562 auto l = {ComplexType(&s), ComplexType(&s) }; 563 564 // check TQueue(InputIterator, InputIterator) 565 TQueue qv(v.begin(), v.end()); 566 static_assert(std::is_same<decltype(qv), TQueue<ComplexType> >::value); 567 568 // check TQueue(InputIterator, InputIterator, Allocator) 569 TQueue qva(v.begin(), v.end(), std::allocator<ComplexType>()); 570 static_assert(std::is_same<decltype(qva), TQueue<ComplexType, std::less<ComplexType>, 571 std::allocator<ComplexType>>>::value); 572 573 // check TQueue(InputIterator, InputIterator, Compare) 574 TQueue qvc(v.begin(), v.end(), LessA<ComplexType>(true)); 575 static_assert(std::is_same<decltype(qvc), TQueue<ComplexType, LessA<ComplexType>>>::value); 576 577 // check TQueue(InputIterator, InputIterator, Compare, Allocator) 578 TQueue qvca(v.begin(), v.end(), LessA<ComplexType>(true), std::allocator<ComplexType>()); 579 static_assert(std::is_same<decltype(qvca), TQueue<ComplexType, LessA<ComplexType>, 580 std::allocator<ComplexType>>>::value); 581 582 // check TQueue(std::initializer_list) 583 TQueue ql(l); 584 static_assert(std::is_same<decltype(ql), TQueue<ComplexType>>::value); 585 586 // check TQueue(std::initializer_list, Allocator) 587 TQueue qla(l, std::allocator<ComplexType>()); 588 static_assert(std::is_same<decltype(qla), TQueue<ComplexType, std::less<ComplexType>, 589 std::allocator<ComplexType>>>::value); 590 591 // check TQueue(std::initializer_list, Compare) 592 TQueue qlc(l, LessA<ComplexType>(true)); 593 static_assert(std::is_same<decltype(qlc), TQueue<ComplexType, LessA<ComplexType>>>::value); 594 595 // check TQueue(std::initializer_list, Compare, Allocator) 596 TQueue qlca(l, LessA<ComplexType>(true), std::allocator<ComplexType>()); 597 static_assert(std::is_same<decltype(qlca), TQueue<ComplexType, LessA<ComplexType>, 598 std::allocator<ComplexType>>>::value); 599 600 // check TQueue(TQueue &) 601 TQueue qc(qv); 602 static_assert(std::is_same<decltype(qv), decltype(qv)>::value); 603 604 // check TQueue(TQueue &, Allocator) 605 TQueue qca(qva, std::allocator<ComplexType>()); 606 static_assert(std::is_same<decltype(qca), decltype(qva)>::value); 607 608 // check TQueue(TQueue &&) 609 TQueue qm(std::move(qv)); 610 static_assert(std::is_same<decltype(qm), decltype(qv)>::value); 611 612 // check TQueue(TQueue &&, Allocator) 613 TQueue qma(std::move(qva), std::allocator<ComplexType>()); 614 static_assert(std::is_same<decltype(qma), decltype(qva)>::value); 615 } 616 #endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT 617 618 template <typename CPQType> 619 void TestComparisonsBasic() { 620 using comparisons_testing::testEqualityComparisons; 621 CPQType c1, c2; 622 testEqualityComparisons</*ExpectEqual = */true>(c1, c2); 623 624 c1.emplace(1); 625 testEqualityComparisons</*ExpectEqual = */false>(c1, c2); 626 627 c2.emplace(1); 628 testEqualityComparisons</*ExpectEqual = */true>(c1, c2); 629 } 630 631 template <typename TwoWayComparableCPQType> 632 void TestTwoWayComparableCPQ() { 633 TwoWayComparableCPQType c1, c2; 634 c1.emplace(1); 635 c2.emplace(1); 636 comparisons_testing::TwoWayComparable::reset(); 637 REQUIRE_MESSAGE(c1 == c2, "Incorrect operator == result"); 638 comparisons_testing::check_equality_comparison(); 639 REQUIRE_MESSAGE(!(c1 != c2), "Incorrect operator != result"); 640 comparisons_testing::check_equality_comparison(); 641 } 642 643 void TestCPQComparisons() { 644 using integral_container = oneapi::tbb::concurrent_priority_queue<int>; 645 using two_way_comparable_container = oneapi::tbb::concurrent_priority_queue<comparisons_testing::TwoWayComparable>; 646 647 TestComparisonsBasic<integral_container>(); 648 TestComparisonsBasic<two_way_comparable_container>(); 649 TestTwoWayComparableCPQ<two_way_comparable_container>(); 650 } 651 652 // Testing basic operations with concurrent_priority_queue with integral value type 653 //! \brief \ref interface \ref requirement 654 TEST_CASE("basic test for concurrent_priority_queue") { 655 test_to_vector(); // Test concurrent_priority_queue helper 656 test_basic(); 657 } 658 659 // Testing std::initializer_list interfaces in concurrent_priority_queue 660 //! \brief \ref interface \ref requirement 661 TEST_CASE("std::initializer_list support in concurrent_priority_queue") { 662 test_initializer_list(); 663 } 664 665 //! Testing concurrent_priority_queue moving constructors 666 //! \brief \ref interface \ref requirement 667 TEST_CASE("concurrent_priority_queue move constructor") { 668 test_cpq_move_constructor(); 669 } 670 671 //! Testing concurrent_priority_queue move assignment operator with different allocator types 672 //! \brief \ref interface \ref requirement 673 TEST_CASE("concurrent_priority_queue move assignment operator") { 674 test_cpq_move_assignment(); 675 } 676 677 //! Testing move semantics on basic push-pop operations 678 //! \brief \ref requirement 679 TEST_CASE("move semantics support on push-pop operations") { 680 test_move_support_in_push_pop(); 681 } 682 683 //! \brief \ref interface \ref requirement 684 TEST_CASE("constructors, destructor and accessors") { 685 test_ctors_dtor_accessors(); 686 } 687 688 //! \brief \ref interface \ref requirement 689 TEST_CASE("assignment, clear and swap operations") { 690 test_assignment_clear_swap(); 691 } 692 693 //! Testing push-pop operations in concurrent_priority_queue 694 //! \brief \ref requirement 695 TEST_CASE("serial push-pop") { 696 test_serial_push_pop(); 697 } 698 699 //! Testing push-pop operations in concurrent_priority_queue with multithreading 700 //! \brief \ref requirement 701 TEST_CASE("multithreading support in concurrent_priority_queue") { 702 test_multithreading(); 703 } 704 705 #if !_MSC_VER || _MSC_VER > 1900 706 // MSVC 2015 does not provide required level of allocator support for standard containers 707 708 //! \brief \ref requirement 709 TEST_CASE("std::allocator_traits support in concurrent_priority_queue") { 710 test_allocator_traits_support<CPQTraits>(); 711 } 712 #endif 713 714 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT 715 //! Testing Class Template Argument Deduction in concurrent_priority_queue 716 //! \brief \ref interface \ref requirement 717 TEST_CASE("CTAD support in concurrent_priority_queue") { 718 TestDeductionGuides<oneapi::tbb::concurrent_priority_queue>(); 719 } 720 #endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT 721 722 //! \brief \ref interface \ref requirement 723 TEST_CASE("concurrent_priority_queue iterator comparisons") { 724 TestCPQComparisons(); 725 } 726