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