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