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