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 
test_to_vector()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 
test_basic()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 
test_initializer_list()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 
operator ==(const SpecialMemberCalls & lhs,const SpecialMemberCalls & rhs)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 
special_member_callsMoveOperationTrackerBase82     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 
MoveOperationTrackerBaseMoveOperationTrackerBase88     MoveOperationTrackerBase() : value(++value_counter) {}
MoveOperationTrackerBaseMoveOperationTrackerBase89     explicit MoveOperationTrackerBase( const std::size_t val ) : value(val) {}
~MoveOperationTrackerBaseMoveOperationTrackerBase90     ~MoveOperationTrackerBase() { value = 0; }
91 
MoveOperationTrackerBaseMoveOperationTrackerBase92     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 
MoveOperationTrackerBaseMoveOperationTrackerBase97     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 
operator =MoveOperationTrackerBase103     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 
operator =MoveOperationTrackerBase110     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 
operator <MoveOperationTrackerBase118     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>
operator ==(const MoveOperationTrackerBase<CounterType> & lhs,const MoveOperationTrackerBase<CounterType> & rhs)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 
initCPQSrcFixture151     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 
CPQSrcFixtureCPQSrcFixture164     CPQSrcFixture( std::size_t size = default_container_size )
165         : CPQSrcFixture(typename cpq_type::allocator_type(), size) {}
166 
CPQSrcFixtureCPQSrcFixture167     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 
test_steal_move_ctor()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 
test_steal_move_ctor_with_allocator()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 
test_per_element_move_ctor_with_allocator()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 
test_steal_move_assign_operator()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 
test_steal_move_assign_operator_with_stateful_allocator()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 
test_per_element_move_assign_operator()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 
test_cpq_move_constructor()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 
test_cpq_move_assignment()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 
NoDefaultCtorTypeNoDefaultCtorType287     NoDefaultCtorType( std::size_t val1, std::size_t val2 ) : value1(val1), value2(val2) {}
operator <NoDefaultCtorType288     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 
ForwardInEmplaceTesterForwardInEmplaceTester300     ForwardInEmplaceTester( int val ) : a(val) {}
301     ForwardInEmplaceTester( const ForwardInEmplaceTester& ) = default;
302     ForwardInEmplaceTester( ForwardInEmplaceTester&& ) = default;
303 
ForwardInEmplaceTesterForwardInEmplaceTester304     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 
operator =ForwardInEmplaceTester311     ForwardInEmplaceTester& operator=( ForwardInEmplaceTester&& obj ) {
312         a = obj.a;
313         move_assign_called = true;
314         return *this;
315     }
316 
operator <ForwardInEmplaceTester317     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 
test_move_support_in_push_pop()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:
LessA(bool no_assert=false)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
test_ctors_dtor_accessors()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 
test_assignment_clear_swap()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 
test_serial_push_pop()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 
test_concurrent(std::size_t n)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 
test_multithreading()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>
TestDeductionGuides()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>
TestComparisonsBasic()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>
TestTwoWayComparableCPQ()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 
TestCPQComparisons()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