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