1 /*
2 Copyright (c) 2019-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 #ifndef __TBB_test_common_node_handling_support_H
18 #define __TBB_test_common_node_handling_support_H
19
20 #include "common/test.h"
21 #include <utility>
22
23 template <typename T>
24 struct AllowMultimapping;
25
26 template <typename Container>
27 struct Value;
28
29 namespace node_handling_tests {
30
31 template <typename Handle>
compare_handle_getters(const Handle & node,const std::pair<typename Handle::key_type,typename Handle::mapped_type> & value)32 bool compare_handle_getters( const Handle& node, const std::pair<typename Handle::key_type, typename Handle::mapped_type>& value ) {
33 return node.key() == value.first && node.mapped() == value.second;
34 }
35
36 template <typename Handle>
compare_handle_getters(const Handle & node,const typename Handle::value_type & value)37 bool compare_handle_getters( const Handle& node, const typename Handle::value_type& value ) {
38 return node.value() == value;
39 }
40
41 template <typename Handle>
set_node_handle_value(Handle & node,const std::pair<typename Handle::key_type,typename Handle::mapped_type> & value)42 void set_node_handle_value( Handle& node, const std::pair<typename Handle::key_type, typename Handle::mapped_type>& value ) {
43 node.key() = value.first;
44 node.mapped() = value.second;
45 }
46
47 template <typename Handle>
set_node_handle_value(Handle & node,const typename Handle::value_type & value)48 void set_node_handle_value( Handle& node, const typename Handle::value_type& value ) {
49 node.value() = value;
50 }
51
52 template <typename NodeType>
test_node_handle_traits()53 void test_node_handle_traits() {
54 REQUIRE_MESSAGE(!std::is_copy_constructible<NodeType>::value,
55 "Node handle: handle should not be copy constructible");
56 REQUIRE_MESSAGE(!std::is_copy_assignable<NodeType>::value,
57 "Node handle: handle should not be copy assignable");
58 REQUIRE_MESSAGE(std::is_move_constructible<NodeType>::value,
59 "Node handle: handle should be move constructible");
60 REQUIRE_MESSAGE(std::is_move_assignable<NodeType>::value,
61 "Node handle: handle should be move assignable");
62 REQUIRE_MESSAGE(std::is_default_constructible<NodeType>::value,
63 "Node handle: handle should be default constructible");
64 REQUIRE_MESSAGE(std::is_destructible<NodeType>::value,
65 "Node handle: handle should be destructible");
66 }
67
68 template <typename Container>
test_node_handle(Container test_table)69 void test_node_handle( Container test_table ) {
70 REQUIRE_MESSAGE(test_table.size() > 1, "Node handle: Container must contain 2 or more elements");
71 // Initialization
72 using node_type = typename Container::node_type;
73
74 test_node_handle_traits<node_type>();
75
76 // Default ctor and empty initialization
77 node_type nh;
78 REQUIRE_MESSAGE(nh.empty(), "Node handle: node_type object is not empty after default ctor");
79
80 // Move assignment
81 // key/mapped/value function
82 auto expected_value = *test_table.begin();
83
84 nh = test_table.unsafe_extract(test_table.begin());
85 REQUIRE_MESSAGE(!nh.empty(), "Node handle: node_type object is empty after valid move assignment");
86 REQUIRE_MESSAGE(nh.get_allocator() == test_table.get_allocator(), "Node handle: node_type object allocator is incorrect");
87 REQUIRE_MESSAGE(compare_handle_getters(nh, expected_value),
88 "Node handle: node_type object does not contains expected value after valid move assignment");
89
90 // Move ctor
91 node_type nh2(std::move(nh));
92 REQUIRE_MESSAGE(nh.empty(), "Node handle: moved-from node_type object is not empty");
93 REQUIRE_MESSAGE(!nh2.empty(), "Node handle: node_type object is empty after valid move construction");
94 REQUIRE_MESSAGE(compare_handle_getters(nh2, expected_value),
95 "Node handle: node_type object does not contains expected value after valid move ctor");
96
97 // bool conversion
98 REQUIRE_MESSAGE(nh2, "Node handle: Wrong node handle bool conversion");
99
100 auto expected_value2 = *test_table.begin();
101 set_node_handle_value(nh2, expected_value2);
102 REQUIRE_MESSAGE(compare_handle_getters(nh2, expected_value2),
103 "Node handle: Wrond node handle key/mapped/value change behaviour");
104
105 // Member and non-member swap check
106 node_type empty_node;
107 // Extract an element for nh2 and nh3 difference
108 test_table.unsafe_extract(test_table.begin());
109 auto expected_value3 = *test_table.begin();
110 node_type nh3(test_table.unsafe_extract(test_table.begin()));
111
112 // Both of node handles are not empty
113 nh3.swap(nh2);
114 REQUIRE_MESSAGE(!nh2.empty(), "Node handle: Wrong node handle swap behavior");
115 REQUIRE_MESSAGE(!nh3.empty(), "Node handle: Wrong node handle swap behavior");
116 REQUIRE_MESSAGE(compare_handle_getters(nh3, expected_value2),
117 "Node handle: Wrong node handle swap behavior");
118 REQUIRE_MESSAGE(compare_handle_getters(nh2, expected_value3),
119 "Node handle: Wrong node handle swap behavior");
120
121 using std::swap;
122 swap(nh2, nh3);
123 REQUIRE_MESSAGE(!nh2.empty(), "Node handle: Wrong node handle swap behavior");
124 REQUIRE_MESSAGE(!nh3.empty(), "Node handle: Wrong node handle swap behavior");
125 REQUIRE_MESSAGE(compare_handle_getters(nh3, expected_value3),
126 "Node handle: Wrong node handle swap behavior");
127 REQUIRE_MESSAGE(compare_handle_getters(nh2, expected_value2),
128 "Node handle: Wrong node handle swap behavior");
129
130 // One of the handles is empty
131 nh3.swap(empty_node);
132 REQUIRE_MESSAGE(nh3.empty(), "Node handle: Wrong node handle swap behavior");
133 REQUIRE_MESSAGE(compare_handle_getters(empty_node, expected_value3),
134 "Node handle: Wrong node handle swap behavior");
135
136 swap(empty_node, nh3);
137 REQUIRE_MESSAGE(empty_node.empty(), "Node handle: Wrong node handle swap behavior");
138 REQUIRE_MESSAGE(compare_handle_getters(nh3, expected_value3),
139 "Node handle: Wrong node handle swap behavior");
140
141 empty_node.swap(nh3);
142 REQUIRE_MESSAGE(nh3.empty(), "Node handle: Wrong node handle swap behavior");
143 REQUIRE_MESSAGE(compare_handle_getters(empty_node, expected_value3),
144 "Node handle: Wrong node handle swap behavior");
145 }
146
147 template <typename Container>
generate_node_handle(const typename Container::value_type & value)148 typename Container::node_type generate_node_handle( const typename Container::value_type& value ) {
149 Container table;
150 table.insert(value);
151 return table.unsafe_extract(table.begin());
152 }
153
154 template <typename Container>
155 void check_insert( const Container& table,
156 typename Container::iterator result,
157 const typename Container::value_type* node_value = nullptr )
158 {
159 if (node_value == nullptr) {
160 REQUIRE_MESSAGE(result == table.end(),
161 "Insert: Result iterator does not point to the end after empty node insertion");
162 } else {
163 if (AllowMultimapping<Container>::value) {
164 REQUIRE_MESSAGE(*result == *node_value,
165 "Insert: Result iterator points to the wrong element after successful insertion");
166
167 for (auto it = table.begin(); it != table.end(); ++it) {
168 if (it == result) return;
169 }
170 REQUIRE_MESSAGE(false, "Insert: iterator does not point to the element in the container");
171 } else {
172 REQUIRE_MESSAGE((result == table.find(Value<Container>::key(*node_value)) &&
173 result != table.end()),
174 "Insert: Iterator does not point to the equal element in the container");
175 }
176 }
177 }
178
179 // Overload for sets
180 template <typename Container>
181 void check_insert( const Container& table,
182 typename Container::iterator result,
183 bool,
184 const typename Container::value_type* node_value = nullptr )
185 {
186 check_insert(table, result, node_value);
187 }
188
189 // Overload for maps
190 template <typename Container>
191 void check_insert( const Container& table,
192 const std::pair<typename Container::iterator, bool>& result,
193 bool successful,
194 const typename Container::value_type* node_value = nullptr )
195 {
196 check_insert(table, result.first, node_value);
197 REQUIRE_MESSAGE((result.second == successful || AllowMultimapping<Container>::value),
198 "Insert: Wrong bool returned after node insertion");
199 }
200
201 // Can't delete reference from table_to_insert argument because hint must point to the element in the table
202 template <typename Container, typename... Hint>
test_insert_overloads(Container & table_to_insert,const typename Container::value_type & value,const Hint &...hint)203 void test_insert_overloads(Container& table_to_insert, const typename Container::value_type& value, const Hint&... hint) {
204 // Insert an empty element
205 typename Container::node_type nh;
206
207 auto table_size = table_to_insert.size();
208 auto result = table_to_insert.insert(hint..., std::move(nh));
209 check_insert(table_to_insert, result, /*successful = */false);
210
211 REQUIRE_MESSAGE(table_to_insert.size() == table_size,
212 "Insert: Container size changed after the insertion of the empty node handle");
213
214 // Standard insertion
215 nh = generate_node_handle<Container>(value);
216
217 result = table_to_insert.insert(hint..., std::move(nh));
218 REQUIRE_MESSAGE(nh.empty(), "Insert: Not empty node handle after successful insertion");
219 check_insert(table_to_insert, result, /*successful = */true, &value);
220
221 // Insert existing node
222 nh = generate_node_handle<Container>(value);
223 result = table_to_insert.insert(hint..., std::move(nh));
224
225 check_insert(table_to_insert, result, /*successful = */false, &value);
226
227 if (AllowMultimapping<Container>::value) {
228 REQUIRE_MESSAGE(nh.empty(), "Insert: Failed insertion to multitable");
229 } else {
230 REQUIRE_MESSAGE(!nh.empty(), "Insert: Empty node handle after failed insertion");
231 REQUIRE_MESSAGE(compare_handle_getters(nh, value),
232 "Insert: Existing data does not equal to the one being inserted");
233 }
234 }
235
236 template <typename Container>
test_insert(Container table,const typename Container::value_type & value)237 void test_insert( Container table, const typename Container::value_type& value ) {
238 REQUIRE_MESSAGE(!table.empty(), "Insert: container should contains 1 or more elements");
239 Container table_backup(table);
240 test_insert_overloads(table, value); // test insert
241 test_insert_overloads(table_backup, value, table_backup.begin()); // test insert with the hint
242 }
243
244 template <typename Container>
test_extract(Container table_for_extract,typename Container::key_type new_key)245 void test_extract( Container table_for_extract, typename Container::key_type new_key ) {
246 REQUIRE_MESSAGE(table_for_extract.size() > 1, "Extract: container must contain 2 or more elements");
247 REQUIRE_MESSAGE(!table_for_extract.contains(new_key), "Extract: container must not contain new element");
248
249 // Extract new_element
250 auto nh = table_for_extract.unsafe_extract(new_key);
251 REQUIRE_MESSAGE(nh.empty(), "Extract: node handle is not empty after extraction of key which is not is the container");
252
253 // Valid key extraction
254 auto expected_value = *table_for_extract.begin();
255 auto key = Value<Container>::key(expected_value);
256 auto count = table_for_extract.count(key);
257
258 nh = table_for_extract.unsafe_extract(key);
259 REQUIRE_MESSAGE(!nh.empty(), "Extract: node handle is empty after successful extraction");
260 REQUIRE_MESSAGE(compare_handle_getters(nh, expected_value), "Extract: node handle contains wrong node after successful extraction");
261 REQUIRE_MESSAGE(table_for_extract.count(key) == count -1, "Extract: more than one elements were extracted");
262
263 // Valid iterator extraction
264 auto expected_value2 = *table_for_extract.begin();
265 auto key2 = Value<Container>::key(expected_value2);
266 auto count2 = table_for_extract.count(key2);
267
268 nh = table_for_extract.unsafe_extract(table_for_extract.cbegin());
269 REQUIRE_MESSAGE(!nh.empty(), "Extract: node handle is empty after successful extraction");
270 REQUIRE_MESSAGE(compare_handle_getters(nh, expected_value2), "Extract: node handle contains wrong node after successful extraction");
271 REQUIRE_MESSAGE(table_for_extract.count(key2) == count2 -1, "Extract: more than one elements were extracted");
272 }
273
274 template <typename Container>
test_node_handling(const Container & container,const typename Container::value_type & new_value)275 void test_node_handling( const Container& container, const typename Container::value_type& new_value ) {
276 test_node_handle(container);
277 test_insert(container, new_value);
278 test_extract(container, Value<Container>::key(new_value));
279 }
280
281 template <typename Container>
test_node_handling_support()282 void test_node_handling_support() {
283 Container cont;
284
285 for (int i = 1; i < 5; ++i) {
286 cont.insert(Value<Container>::make(i));
287 }
288
289 if (AllowMultimapping<Container>::value) {
290 cont.insert(Value<Container>::make(4));
291 }
292
293 test_node_handling(cont, Value<Container>::make(5));
294 }
295
296 template <typename Container1, typename Container2>
test_merge_basic(Container1 table1,Container2 && table2)297 void test_merge_basic( Container1 table1, Container2&& table2 ) {
298 using container2_pure_type = typename std::decay<Container2>::type;
299
300 // Initialization
301 Container1 table1_backup = table1;
302 container2_pure_type table2_backup = table2;
303
304 table1.merge(std::forward<Container2>(table2));
305 for (auto it : table2) {
306 REQUIRE_MESSAGE(table1.contains(Value<container2_pure_type>::key(it)),
307 "Merge: Some key was not merged");
308 }
309
310 // After the following step table1 will contains only merged elements from table2
311 for (auto it : table1_backup) {
312 table1.unsafe_extract(Value<Container1>::key(it));
313 }
314 // After the following step table2_backup will contains only merged elements from table2
315 for (auto it : table2) {
316 table2_backup.unsafe_extract(Value<container2_pure_type>::key(it));
317 }
318
319 REQUIRE_MESSAGE(table1.size() == table2_backup.size(), "Merge: Sizes of tables are not equal");
320 for (auto it : table2_backup) {
321 REQUIRE_MESSAGE(table1.contains(Value<container2_pure_type>::key(it)),
322 "Merge: Wrong merge behavior");
323 }
324 }
325
326 template <typename Container1, typename Container2>
test_merge_overloads(const Container1 & table1,Container2 table2)327 void test_merge_overloads( const Container1& table1, Container2 table2 ) {
328 Container2 table_backup(table2);
329 test_merge_basic(table1, table2);
330 test_merge_basic(table1, std::move(table_backup));
331 }
332
333 template <typename UniqueContainer, typename MultiContainer>
test_merge_transposition(UniqueContainer table1,UniqueContainer table2,MultiContainer multitable1,MultiContainer multitable2)334 void test_merge_transposition( UniqueContainer table1, UniqueContainer table2,
335 MultiContainer multitable1, MultiContainer multitable2 ) {
336 UniqueContainer empty_table;
337 MultiContainer empty_multitable;
338
339 // Unique table transpositions
340 test_merge_overloads(table1, table2);
341 test_merge_overloads(table1, empty_table);
342 test_merge_overloads(empty_table, table2);
343
344 // Multi table transpositions
345 test_merge_overloads(multitable1, multitable2);
346 test_merge_overloads(multitable1, empty_multitable);
347 test_merge_overloads(empty_multitable, multitable2);
348
349 // Unique/Multi tables transpositions
350 test_merge_overloads(table1, multitable1);
351 test_merge_overloads(multitable2, table2);
352 }
353
354 template <typename SrcTableType, typename DstTableType>
check_concurrent_merge(SrcTableType & start_data,DstTableType & dst_table,std::vector<SrcTableType> & src_tables,std::true_type)355 void check_concurrent_merge( SrcTableType& start_data, DstTableType& dst_table,
356 std::vector<SrcTableType>& src_tables, std::true_type )
357 {
358 REQUIRE_MESSAGE(dst_table.size() == start_data.size() * src_tables.size(),
359 "Merge: Incorrect merge for some elements");
360
361 for (auto it : start_data) {
362 REQUIRE_MESSAGE(dst_table.count(Value<DstTableType>::key(it)) ==
363 start_data.count(Value<SrcTableType>::key(it)) * src_tables.size(),
364 "Merge: Incorrect merge for some elements");
365 }
366
367 for (size_t i = 0; i < src_tables.size(); ++i) {
368 REQUIRE_MESSAGE(src_tables[i].empty(), "Merge: Some elements were not merged");
369 }
370 }
371
372 template <typename SrcTableType, typename DstTableType>
check_concurrent_merge(SrcTableType & start_data,DstTableType & dst_table,std::vector<SrcTableType> & src_tables,std::false_type)373 void check_concurrent_merge( SrcTableType& start_data, DstTableType& dst_table,
374 std::vector<SrcTableType>& src_tables, std::false_type )
375 {
376 SrcTableType expected_result;
377 for (auto table : src_tables) {
378 for (auto it : start_data) {
379 // If we cannot find some element in some table, then it has been moved
380 if (!table.contains(Value<SrcTableType>::key(it))) {
381 bool result = expected_result.insert(it).second;
382 REQUIRE_MESSAGE(result,
383 "Merge: Some element was merged twice or was not returned to his owner after unsuccessful merge");
384 }
385 }
386 }
387
388 REQUIRE_MESSAGE((expected_result.size() == dst_table.size() && start_data.size() == dst_table.size()),
389 "Merge: wrong size of result table");
390
391 for (auto it : expected_result) {
392 if (dst_table.contains(Value<SrcTableType>::key(it)) &&
393 start_data.contains(Value<DstTableType>::key(it)))
394 {
395 dst_table.unsafe_extract(Value<SrcTableType>::key(it));
396 start_data.unsafe_extract(Value<DstTableType>::key(it));
397 } else {
398 REQUIRE_MESSAGE(false, "Merge: Incorrect merge for some element");
399 }
400 }
401
402 REQUIRE_MESSAGE((dst_table.empty() && start_data.empty()), "Merge: Some elements were not merged");
403 }
404
405 template <typename SrcTableType, typename DstTableType>
test_concurrent_merge(SrcTableType table_data)406 void test_concurrent_merge( SrcTableType table_data ) {
407 for (std::size_t num_threads = utils::MinThread; num_threads <= utils::MaxThread; ++num_threads) {
408 std::vector<SrcTableType> src_tables;
409 DstTableType dst_table;
410
411 for (std::size_t j = 0; j < num_threads; ++j) {
412 src_tables.push_back(table_data);
413 }
414
415 utils::NativeParallelFor(num_threads, [&](std::size_t index) { dst_table.merge(src_tables[index]); } );
416
417 check_concurrent_merge(table_data, dst_table, src_tables,
418 AllowMultimapping<DstTableType>{});
419 }
420 }
421
422 template <typename Container1, typename Container2>
test_merge(int size)423 void test_merge( int size ) {
424 Container1 table1_1;
425 Container1 table1_2;
426 int i = 1;
427
428 for (; i < 5; ++i) {
429 table1_1.insert(Value<Container1>::make(i));
430 table1_2.insert(Value<Container1>::make(i * i));
431 }
432 if (AllowMultimapping<Container1>::value) {
433 table1_1.insert(Value<Container1>::make(i));
434 table1_2.insert(Value<Container1>::make(i * i));
435 }
436
437 Container2 table2_1;
438 Container2 table2_2;
439
440 for (i = 3; i < 7; ++i) {
441 table2_1.insert(Value<Container2>::make(i));
442 table2_2.insert(Value<Container2>::make(i * i));
443 }
444 if (AllowMultimapping<Container2>::value) {
445 table2_1.insert(Value<Container2>::make(i));
446 table2_2.insert(Value<Container2>::make(i * i));
447 }
448
449 test_merge_transposition(table1_1, table1_2, table2_1, table2_2);
450
451 Container1 table1_3;
452 Container2 table2_3;
453 for (i = 0; i < size; ++i) {
454 table1_3.insert(Value<Container1>::make(i));
455 table2_3.insert(Value<Container2>::make(i));
456 }
457
458 test_concurrent_merge<Container1, Container2>(table1_3);
459 test_concurrent_merge<Container2, Container1>(table2_3);
460 }
461
462 } // namespace node_handling tests
463
464 #endif // __TBB_test_common_node_handling_support_H
465