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 "oneapi/tbb/concurrent_unordered_map.h"
22 #include <common/test.h>
23 #include <common/utils.h>
24 #include <common/concurrent_unordered_common.h>
25 #include <memory>
26 #include <type_traits>
27 
28 //! \file conformance_concurrent_unordered_map.cpp
29 //! \brief Test for [containers.concurrent_unordered_map containers.concurrent_unordered_multimap] specifications
30 
31 template <typename... Args>
32 struct AllowMultimapping<oneapi::tbb::concurrent_unordered_multimap<Args...>> : std::true_type {};
33 
34 template <typename Key, typename Mapped>
35 using Allocator = LocalCountingAllocator<std::allocator<std::pair<const Key, Mapped>>>;
36 
37 using map_type = oneapi::tbb::concurrent_unordered_map<int, int, std::hash<int>, std::equal_to<int>, Allocator<int, int>>;
38 using multimap_type = oneapi::tbb::concurrent_unordered_multimap<int, int, std::hash<int>, std::equal_to<int>, Allocator<int, int>>;
39 
40 template <>
41 struct SpecialTests<map_type> {
42     static void Test() {
43         SpecialMapTests<map_type>();
44     }
45 };
46 
47 template <>
48 struct SpecialTests<multimap_type> {
49     static void Test() {
50         SpecialMultiMapTests<multimap_type>();
51     }
52 };
53 
54 template <template <typename... > class ContainerType>
55 void test_member_types() {
56     using default_container_type = ContainerType<int, int>;
57     static_assert(std::is_same<typename default_container_type::hasher, std::hash<int>>::value,
58                   "Incorrect default template hasher");
59     static_assert(std::is_same<typename default_container_type::key_equal, std::equal_to<int>>::value,
60                   "Incorrect default template key equality");
61     static_assert(std::is_same<typename default_container_type::allocator_type,
62                                oneapi::tbb::tbb_allocator<std::pair<const int, int>>>::value,
63                   "Incorrect default template allocator");
64 
65     auto test_hasher = [](const int&)->std::size_t { return 0; };
66     auto test_equality = [](const int&, const int&)->bool { return true; };
67     using test_allocator_type = std::allocator<std::pair<const int, int>>;
68 
69     using container_type = ContainerType<int, int, decltype(test_hasher),
70                                          decltype(test_equality), test_allocator_type>;
71 
72     static_assert(std::is_same<typename container_type::key_type, int>::value,
73                   "Incorrect container key_type member type");
74     static_assert(std::is_same<typename container_type::mapped_type, int>::value,
75                   "Incorrect container mapped_type member type");
76     static_assert(std::is_same<typename container_type::value_type, std::pair<const int, int>>::value,
77                   "Incorrect container value_type member type");
78 
79     static_assert(std::is_unsigned<typename container_type::size_type>::value,
80                   "Incorrect container size_type member type");
81     static_assert(std::is_signed<typename container_type::difference_type>::value,
82                   "Incorrect container difference_type member type");
83 
84     static_assert(std::is_same<typename container_type::hasher, decltype(test_hasher)>::value,
85                   "Incorrect container hasher member type");
86     static_assert(std::is_same<typename container_type::key_equal, decltype(test_equality)>::value,
87                   "Incorrect container key_equal member type");
88 
89     using transparent_container_type = ContainerType<int, int, hasher_with_transparent_key_equal,
90                                                      std::equal_to<int>, test_allocator_type>;
91 
92     static_assert(std::is_same<typename transparent_container_type::key_equal, transparent_key_equality>::value,
93                   "Incorrect container key_equal member type");
94     static_assert(std::is_same<typename container_type::allocator_type, test_allocator_type>::value,
95                   "Incorrect container allocator_type member type");
96 
97     using value_type = typename container_type::value_type;
98     static_assert(std::is_same<typename container_type::reference, value_type&>::value,
99                   "Incorrect container reference member type");
100     static_assert(std::is_same<typename container_type::const_reference, const value_type&>::value,
101                   "Incorrect container const_reference member type");
102     using allocator_type = typename container_type::allocator_type;
103     static_assert(std::is_same<typename container_type::pointer, typename std::allocator_traits<allocator_type>::pointer>::value,
104                   "Incorrect container pointer member type");
105     static_assert(std::is_same<typename container_type::const_pointer, typename std::allocator_traits<allocator_type>::const_pointer>::value,
106                   "Incorrect container const_pointer member type");
107 
108     static_assert(utils::is_forward_iterator<typename container_type::iterator>::value,
109                   "Incorrect container iterator member type");
110     static_assert(!std::is_const<typename container_type::iterator::value_type>::value,
111                   "Incorrect container iterator member type");
112     static_assert(utils::is_forward_iterator<typename container_type::const_iterator>::value,
113                   "Incorrect container const_iterator member type");
114     static_assert(std::is_const<typename container_type::const_iterator::value_type>::value,
115                   "Incorrect container iterator member type");
116     static_assert(utils::is_forward_iterator<typename container_type::local_iterator>::value,
117                   "Incorrect container local_iterator member type");
118     static_assert(!std::is_const<typename container_type::local_iterator::value_type>::value,
119                   "Incorrect container local_iterator member type");
120     static_assert(utils::is_forward_iterator<typename container_type::const_local_iterator>::value,
121                   "Incorrect container const_local_iterator member type");
122     static_assert(std::is_const<typename container_type::const_local_iterator::value_type>::value,
123                   "Incorrect container const_local_iterator member type");
124 }
125 
126 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
127 template <template <typename...> typename TMap>
128 void test_deduction_guides() {
129     using ComplexType = std::pair<int, std::string>;
130     using ComplexTypeConst = std::pair<const int, std::string>;
131     std::vector<ComplexType> v;
132     auto l = { ComplexTypeConst(1, "one"), ComplexTypeConst(2, "two")};
133     using custom_allocator_type = std::allocator<ComplexTypeConst>;
134 
135     // check TMap(InputIterator, InputIterator)
136     TMap m0(v.begin(), v.end());
137     static_assert(std::is_same<decltype(m0), TMap<int, std::string>>::value);
138 
139     // check TMap(InputIterator, InputIterator, size_t)
140     TMap m1(v.begin(), v.end(), 1);
141     static_assert(std::is_same<decltype(m1), TMap<int, std::string>>::value);
142 
143     // check TMap(InputIterator, InputIterator, size_t, Hasher)
144     TMap m2(v.begin(), v.end(), 4, degenerate_hash<int>());
145     static_assert(std::is_same<decltype(m2), TMap<int, std::string, degenerate_hash<int>>>::value);
146 
147     // check TMap(InputIterator, InputIterator, size_t, Hasher, Equality)
148     TMap m3(v.begin(), v.end(), 4, degenerate_hash<int>(), std::less<int>());
149     static_assert(std::is_same<decltype(m3), TMap<int, std::string, degenerate_hash<int>, std::less<int>>>::value);
150 
151     // check TMap(InputIterator, InputIterator, size_t, Hasher, Equality, Allocator)
152     TMap m4(v.begin(), v.end(), 4, degenerate_hash<int>(), std::less<int>(), custom_allocator_type{});
153     static_assert(std::is_same<decltype(m4), TMap<int, std::string, degenerate_hash<int>,
154         std::less<int>, custom_allocator_type>>::value);
155 
156     // check TMap(InputIterator, InputIterator, size_t, Allocator)
157     TMap m5(v.begin(), v.end(), 5, custom_allocator_type{});
158     static_assert(std::is_same<decltype(m5), TMap<int, std::string, std::hash<int>,
159         std::equal_to<int>, custom_allocator_type>>::value);
160 
161     // check TMap(InputIterator, InputIterator, size_t, Hasher, Allocator)
162     TMap m6(v.begin(), v.end(), 4, degenerate_hash<int>(), custom_allocator_type{});
163     static_assert(std::is_same<decltype(m6), TMap<int, std::string, degenerate_hash<int>,
164         std::equal_to<int>, custom_allocator_type>>::value);
165 
166     // check TMap(std::initializer_list)
167     TMap m7(l);
168     static_assert(std::is_same<decltype(m7), TMap<int, std::string>>::value);
169 
170     // check TMap(std::initializer_list, size_t)
171     TMap m8(l, 1);
172     static_assert(std::is_same<decltype(m8), TMap<int, std::string>>::value);
173 
174     // check TMap(std::initializer_list, size_t, Hasher)
175     TMap m9(l, 4, degenerate_hash<int>());
176     static_assert(std::is_same<decltype(m9), TMap<int, std::string, degenerate_hash<int>>>::value);
177 
178     // check TMap(std::initializer_list, size_t, Hasher, Equality)
179     TMap m10(l, 4, degenerate_hash<int>(), std::less<int>());
180     static_assert(std::is_same<decltype(m10), TMap<int, std::string, degenerate_hash<int>, std::less<int>>>::value);
181 
182     // check TMap(std::initializer_list, size_t, Hasher, Equality, Allocator)
183     TMap m11(l, 4, degenerate_hash<int>(), std::less<int>(), custom_allocator_type{});
184     static_assert(std::is_same<decltype(m11), TMap<int, std::string, degenerate_hash<int>,
185         std::less<int>, custom_allocator_type>>::value);
186 
187     // check TMap(std::initializer_list, size_t, Allocator)
188     TMap m12(l, 4, custom_allocator_type{});
189     static_assert(std::is_same<decltype(m12), TMap<int, std::string, std::hash<int>,
190         std::equal_to<int>, custom_allocator_type>>::value);
191 
192     // check TMap(std::initializer_list, size_t, Hasher, Allocator)
193     TMap m13(l, 4, degenerate_hash<int>(), custom_allocator_type{});
194     static_assert(std::is_same<decltype(m13), TMap<int, std::string, degenerate_hash<int>,
195         std::equal_to<int>, custom_allocator_type>>::value);
196 
197     // check TMap(TMap &)
198     TMap m14(m1);
199     static_assert(std::is_same<decltype(m14), decltype(m1)>::value);
200 
201     // check TMap(TMap &, Allocator)
202     // TODO: investigate why no implicit deduction guides generated for this ctor
203     TMap m15(m5, custom_allocator_type{});
204     static_assert(std::is_same<decltype(m15), decltype(m5)>::value);
205 
206     // check TMap(TMap &&)
207     TMap m16(std::move(m1));
208     static_assert(std::is_same<decltype(m16), decltype(m1)>::value);
209 
210     // check TMap(TMap &&, Allocator)
211     // TODO: investigate why no implicit deduction guides generated for this ctor
212     TMap m17(std::move(m5), custom_allocator_type{});
213     static_assert(std::is_same<decltype(m17), decltype(m5)>::value);
214 }
215 #endif
216 
217 void test_heterogeneous_functions() {
218     check_heterogeneous_functions_key_int<oneapi::tbb::concurrent_unordered_map, int, int>();
219     check_heterogeneous_functions_key_int<oneapi::tbb::concurrent_unordered_multimap, int, int>();
220     check_heterogeneous_functions_key_string<oneapi::tbb::concurrent_unordered_map, std::string, std::string>();
221     check_heterogeneous_functions_key_string<oneapi::tbb::concurrent_unordered_multimap, std::string, std::string>();
222 }
223 
224 struct CumapTraits : UnorderedMoveTraitsBase {
225     template <typename T, typename Allocator>
226     using container_type = oneapi::tbb::concurrent_unordered_map<T, T, std::hash<T>, std::equal_to<T>, Allocator>;
227 
228     template <typename T>
229     using container_value_type = std::pair<const T, T>;
230 
231     using init_iterator_type = move_support_tests::FooPairIterator;
232 }; // struct CumapTraits
233 
234 struct CumultimapTraits : UnorderedMoveTraitsBase {
235     template <typename T, typename Allocator>
236     using container_type = oneapi::tbb::concurrent_unordered_multimap<T, T, std::hash<T>, std::equal_to<T>, Allocator>;
237 
238     template <typename T>
239     using container_value_type = std::pair<const T, T>;
240 
241     using init_iterator_type = move_support_tests::FooPairIterator;
242 }; // struct CumultimapTraits
243 
244 //! Testing concurrent_unordered_map member types
245 //! \brief \ref interface \ref requirement
246 TEST_CASE("concurrent_unordered_map member types") {
247     test_member_types<oneapi::tbb::concurrent_unordered_map>();
248 }
249 
250 //! Testing requirements of concurrent_unordered_map
251 //! \brief \ref interface \ref requirement
252 TEST_CASE("concurrent_unordered_map requirements") {
253     test_basic<map_type>();
254 }
255 
256 //! Testing multithreading support in concurrent_unordered_map
257 //! \brief \ref requirement
258 TEST_CASE("concurrent_unordered_map multithreading support") {
259     test_concurrent<map_type>();
260 }
261 
262 //! Testing move constructors and assignment operator in concurrent_unordered_map
263 //! \brief \ref interface \ref requirement
264 TEST_CASE("concurrent_unordered_map move semantics support") {
265     test_rvalue_ref_support<CumapTraits>();
266 }
267 
268 //! Testing std::initializer_list constructors and modifiers in concurrent_unordered_map
269 //! \brief \ref interface \ref requirement
270 TEST_CASE("std::initializer_list support in concurrent_unordered_map") {
271     test_initializer_list_support<map_type>({{1, 1}, {2, 2}, {3, 3}, {4, 4}});
272 }
273 
274 //! Testing node handling in concurrent_unordered_map
275 //! \brief \ref interface \ref requirement
276 TEST_CASE("node handling support in concurrent_unordered_map") {
277     node_handling_tests::test_node_handling_support<map_type>();
278 }
279 
280 //! Testing std::allocator_traits support in concurrent_unordered_map
281 //! \brief \ref interface \ref requirement
282 TEST_CASE("std::allocator_traits support in concurrent_unordered_map") {
283     test_allocator_traits_support<CumapTraits>();
284 }
285 
286 //! Testing heterogeneous overloads in concurrent_unordered_map
287 //! \brief \ref interface \ref requirement
288 TEST_CASE("heterogeneous overloads in concurrent_unordered_map") {
289     check_heterogeneous_functions_key_int<oneapi::tbb::concurrent_unordered_map, int, int>();
290     check_heterogeneous_functions_key_string<oneapi::tbb::concurrent_unordered_map, std::string, std::string>();
291 }
292 
293 //! Testing insert overloads with generic pair in concurrent_unordered_map
294 //! \brief \ref interface \ref requirement
295 TEST_CASE("insertion by generic pair in concurrent_unordered_map") {
296     test_insert_by_generic_pair<oneapi::tbb::concurrent_unordered_map>();
297 }
298 
299 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
300 //! Testing Class Template Argument Deduction in concurrent_unordered_map
301 //! \brief \ref interface \ref requirement
302 TEST_CASE("CTAD support in concurrent_unordered_map") {
303     test_deduction_guides<oneapi::tbb::concurrent_unordered_map>();
304 }
305 #endif
306 
307 //! Testing comparisons in concurrent_unordered_map
308 //! \brief \ref interface \ref requirement
309 TEST_CASE("concurrent_unordered_map comparisons") {
310     test_map_comparisons<oneapi::tbb::concurrent_unordered_map>();
311 }
312 
313 //! Testing concurrent_unordered_multimap member types
314 //! \brief \ref interface \ref requirement
315 TEST_CASE("concurrent_unordered_multimap member types") {
316     test_member_types<oneapi::tbb::concurrent_unordered_multimap>();
317 }
318 
319 //! Testing requirements of concurrent_unordered_multimap
320 //! \brief \ref interface \ref requirement
321 TEST_CASE("concurrent_unordered_multimap requirements") {
322     test_basic<multimap_type>();
323 }
324 
325 //! Testing multithreading support in concurrent_unordered_multimap
326 //! \brief \ref requirement
327 TEST_CASE("concurrent_unordered_multimap multithreading support") {
328     test_concurrent<multimap_type>();
329 }
330 
331 //! Testing move constructors and assignment operator in concurrent_unordered_multimap
332 //! \brief \ref interface \ref requirement
333 TEST_CASE("concurrent_unordered_multimap move semantics support") {
334     test_rvalue_ref_support<CumultimapTraits>();
335 }
336 
337 //! Testing std::initializer_list constructors and modifiers in concurrent_unordered_multimap
338 //! \brief \ref interface \ref requirement
339 TEST_CASE("std::initializer_list support in concurrent_unordered_multimap") {
340     test_initializer_list_support<multimap_type>({{1, 1}, {2, 2}, {3, 3}, {4, 4}, {4, 40}});
341 }
342 
343 //! Testing node handling support in concurrent_unordered_multimap
344 //! \brief \ref interface \ref requirement
345 TEST_CASE("node handling support in concurrent_unordered_multimap") {
346     node_handling_tests::test_node_handling_support<multimap_type>();
347 }
348 
349 //! Testing std::allocator_traits support in concurrent_unordered_multimap
350 //! \brief \ref interface \ref requirement
351 TEST_CASE("std::allocator_traits support in concurrent_unordered_multimap") {
352     test_allocator_traits_support<CumultimapTraits>();
353 }
354 
355 //! Testing heterogeneous overloads in concurrent_unordered_multimap
356 //! \brief \ref interface \ref requirement
357 TEST_CASE("heterogeneous overloads in concurrent_unordered_multimap") {
358     check_heterogeneous_functions_key_int<oneapi::tbb::concurrent_unordered_multimap, int, int>();
359     check_heterogeneous_functions_key_string<oneapi::tbb::concurrent_unordered_multimap, std::string, std::string>();
360 }
361 
362 //! Testing insert overloads with generic pair in concurrent_unordered_multimap
363 //! \brief \ref interface \ref requirement
364 TEST_CASE("insertion by generic pair in concurrent_unordered_multimap") {
365     test_insert_by_generic_pair<oneapi::tbb::concurrent_unordered_multimap>();
366 }
367 
368 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
369 //! Testing Class Template Argument Deduction in concurrent_unordered_multimap
370 //! \brief \ref interface \ref requirement
371 TEST_CASE("CTAD support in concurrent_unordered_multimap") {
372     test_deduction_guides<oneapi::tbb::concurrent_unordered_multimap>();
373 }
374 #endif
375 
376 //! Testing comparisons in concurrent_unordered_multimap
377 //! \brief \ref interface \ref requirement
378 TEST_CASE("concurrent_unordered_multimap comparisons") {
379     test_map_comparisons<oneapi::tbb::concurrent_unordered_multimap>();
380 }
381 
382 //! Testing of merge operations in concurrent_unordered_map and concurrent_unordered_multimap
383 //! \brief \ref interface \ref requirement
384 TEST_CASE("merge operations") {
385     node_handling_tests::test_merge<map_type, multimap_type>(1000);
386 }
387