151c0b2f7Stbbdev /*
2*a088cfa0SKonstantin Boyarinov     Copyright (c) 2020-2023 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
17b15aabb3Stbbdev #if __INTEL_COMPILER && _MSC_VER
18b15aabb3Stbbdev #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
19b15aabb3Stbbdev #endif
2051c0b2f7Stbbdev 
2151c0b2f7Stbbdev #include "conformance_flowgraph.h"
22*a088cfa0SKonstantin Boyarinov #include "common/test_invoke.h"
2351c0b2f7Stbbdev 
2451c0b2f7Stbbdev //! \file conformance_join_node.cpp
2551c0b2f7Stbbdev //! \brief Test for [flow_graph.join_node] specification
2651c0b2f7Stbbdev 
27de0109beSIlya Mishin using input_msg = conformance::message</*default_ctor*/true, /*copy_ctor*/true, /*copy_assign*/true>;
28de0109beSIlya Mishin using my_input_tuple = std::tuple<int, float, input_msg>;
2951c0b2f7Stbbdev 
get_values(conformance::test_push_receiver<my_input_tuple> & rr)30de0109beSIlya Mishin std::vector<my_input_tuple> get_values( conformance::test_push_receiver<my_input_tuple>& rr ) {
31de0109beSIlya Mishin     std::vector<my_input_tuple> messages;
32274f68e5SIlya Isaev     my_input_tuple tmp(0, 0.f, input_msg(0));
33274f68e5SIlya Isaev     while(rr.try_get(tmp)) {
34de0109beSIlya Mishin         messages.push_back(tmp);
35de0109beSIlya Mishin     }
36de0109beSIlya Mishin     return messages;
3751c0b2f7Stbbdev }
3851c0b2f7Stbbdev 
39de0109beSIlya Mishin #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
test_deduction_guides()40de0109beSIlya Mishin void test_deduction_guides() {
41de0109beSIlya Mishin     using namespace tbb::flow;
4251c0b2f7Stbbdev 
4351c0b2f7Stbbdev     graph g;
44de0109beSIlya Mishin     using tuple_type = std::tuple<int, int, int>;
45de0109beSIlya Mishin     broadcast_node<int> b1(g), b2(g), b3(g);
46de0109beSIlya Mishin     broadcast_node<tuple_type> b4(g);
47de0109beSIlya Mishin     join_node<tuple_type> j0(g);
4851c0b2f7Stbbdev 
49de0109beSIlya Mishin #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
50de0109beSIlya Mishin     join_node j1(follows(b1, b2, b3));
51de0109beSIlya Mishin     static_assert(std::is_same_v<decltype(j1), join_node<tuple_type>>);
52de0109beSIlya Mishin 
53de0109beSIlya Mishin     join_node j2(follows(b1, b2, b3), reserving());
54de0109beSIlya Mishin     static_assert(std::is_same_v<decltype(j2), join_node<tuple_type, reserving>>);
55de0109beSIlya Mishin 
56de0109beSIlya Mishin     join_node j3(precedes(b4));
57de0109beSIlya Mishin     static_assert(std::is_same_v<decltype(j3), join_node<tuple_type>>);
58de0109beSIlya Mishin 
59de0109beSIlya Mishin     join_node j4(precedes(b4), reserving());
60de0109beSIlya Mishin     static_assert(std::is_same_v<decltype(j4), join_node<tuple_type, reserving>>);
61de0109beSIlya Mishin #endif
62de0109beSIlya Mishin 
63de0109beSIlya Mishin     join_node j5(j0);
64de0109beSIlya Mishin     static_assert(std::is_same_v<decltype(j5), join_node<tuple_type>>);
6551c0b2f7Stbbdev }
6651c0b2f7Stbbdev 
67de0109beSIlya Mishin #endif
6851c0b2f7Stbbdev 
69de0109beSIlya Mishin //! The node that is constructed has a reference to the same graph object as src.
70de0109beSIlya Mishin //! The list of predecessors, messages in the input ports, and successors are not copied.
7151c0b2f7Stbbdev //! \brief \ref interface
7251c0b2f7Stbbdev TEST_CASE("join_node copy constructor"){
73de0109beSIlya Mishin     oneapi::tbb::flow::graph g;
74de0109beSIlya Mishin     oneapi::tbb::flow::continue_node<int> node0( g,
__anon798ebb9d0102(oneapi::tbb::flow::continue_msg) 75de0109beSIlya Mishin                                 [](oneapi::tbb::flow::continue_msg) { return 1; } );
76de0109beSIlya Mishin 
77de0109beSIlya Mishin     oneapi::tbb::flow::join_node<std::tuple<int>> node1(g);
78de0109beSIlya Mishin     conformance::test_push_receiver<std::tuple<int>> node2(g);
79de0109beSIlya Mishin     conformance::test_push_receiver<std::tuple<int>> node3(g);
80de0109beSIlya Mishin 
81de0109beSIlya Mishin     oneapi::tbb::flow::make_edge(node0, oneapi::tbb::flow::input_port<0>(node1));
82de0109beSIlya Mishin     oneapi::tbb::flow::make_edge(node1, node2);
83de0109beSIlya Mishin     oneapi::tbb::flow::join_node<std::tuple<int>> node_copy(node1);
84de0109beSIlya Mishin 
85de0109beSIlya Mishin     oneapi::tbb::flow::make_edge(node_copy, node3);
86de0109beSIlya Mishin 
87de0109beSIlya Mishin     oneapi::tbb::flow::input_port<0>(node_copy).try_put(1);
88de0109beSIlya Mishin     g.wait_for_all();
89de0109beSIlya Mishin 
90de0109beSIlya Mishin     auto values = conformance::get_values(node3);
91de0109beSIlya Mishin     CHECK_MESSAGE((conformance::get_values(node2).size() == 0 && values.size() == 1), "Copied node doesn`t copy successor");
92de0109beSIlya Mishin 
93de0109beSIlya Mishin     node0.try_put(oneapi::tbb::flow::continue_msg());
94de0109beSIlya Mishin     g.wait_for_all();
95de0109beSIlya Mishin 
96de0109beSIlya Mishin     CHECK_MESSAGE((conformance::get_values(node2).size() == 1 && conformance::get_values(node3).size() == 0), "Copied node doesn`t copy predecessor");
97de0109beSIlya Mishin 
98de0109beSIlya Mishin     oneapi::tbb::flow::remove_edge(node1, node2);
99de0109beSIlya Mishin     oneapi::tbb::flow::input_port<0>(node1).try_put(1);
100de0109beSIlya Mishin     g.wait_for_all();
101de0109beSIlya Mishin     oneapi::tbb::flow::join_node<std::tuple<int>> node_copy2(node1);
102de0109beSIlya Mishin     oneapi::tbb::flow::make_edge(node_copy2, node3);
103de0109beSIlya Mishin     oneapi::tbb::flow::input_port<0>(node_copy2).try_put(2);
104de0109beSIlya Mishin     g.wait_for_all();
105de0109beSIlya Mishin     CHECK_MESSAGE((std::get<0>(conformance::get_values(node3)[0]) == 2), "Copied node doesn`t copy messages in the input ports");
10651c0b2f7Stbbdev }
10751c0b2f7Stbbdev 
10851c0b2f7Stbbdev //! Test inheritance relations
10951c0b2f7Stbbdev //! \brief \ref interface
11051c0b2f7Stbbdev TEST_CASE("join_node inheritance"){
111de0109beSIlya Mishin     CHECK_MESSAGE((std::is_base_of<oneapi::tbb::flow::graph_node,
112de0109beSIlya Mishin                    oneapi::tbb::flow::join_node<my_input_tuple>>::value),
113de0109beSIlya Mishin                    "join_node should be derived from graph_node");
114de0109beSIlya Mishin     CHECK_MESSAGE((std::is_base_of<oneapi::tbb::flow::sender<my_input_tuple>,
115de0109beSIlya Mishin                    oneapi::tbb::flow::join_node<my_input_tuple>>::value),
116de0109beSIlya Mishin                    "join_node should be derived from sender<input_tuple>");
11751c0b2f7Stbbdev }
11851c0b2f7Stbbdev 
119de0109beSIlya Mishin //! Test join_node<queueing> behavior and broadcast property
12051c0b2f7Stbbdev //! \brief \ref requirement
121de0109beSIlya Mishin TEST_CASE("join_node queueing policy and broadcast property") {
122de0109beSIlya Mishin     oneapi::tbb::flow::graph g;
123de0109beSIlya Mishin     oneapi::tbb::flow::function_node<int, int>
__anon798ebb9d0202(const int &i) 124de0109beSIlya Mishin         f1( g, oneapi::tbb::flow::unlimited, [](const int &i) { return i; } );
125de0109beSIlya Mishin     oneapi::tbb::flow::function_node<float, float>
__anon798ebb9d0302(const float &f) 126de0109beSIlya Mishin         f2( g, oneapi::tbb::flow::unlimited, [](const float &f) { return f; } );
127de0109beSIlya Mishin     oneapi::tbb::flow::continue_node<input_msg> c1( g,
__anon798ebb9d0402(oneapi::tbb::flow::continue_msg) 128de0109beSIlya Mishin                             [](oneapi::tbb::flow::continue_msg) { return input_msg(1); } );
12951c0b2f7Stbbdev 
130de0109beSIlya Mishin     oneapi::tbb::flow::join_node<my_input_tuple, oneapi::tbb::flow::queueing> testing_node(g);
13151c0b2f7Stbbdev 
132de0109beSIlya Mishin     conformance::test_push_receiver<my_input_tuple> q_node(g);
133de0109beSIlya Mishin 
134de0109beSIlya Mishin     std::atomic<int> number{1};
135de0109beSIlya Mishin     oneapi::tbb::flow::function_node<my_input_tuple, my_input_tuple>
136de0109beSIlya Mishin         f3( g, oneapi::tbb::flow::unlimited,
__anon798ebb9d0502( const my_input_tuple &t ) 137de0109beSIlya Mishin             [&]( const my_input_tuple &t ) {
138de0109beSIlya Mishin                 CHECK_MESSAGE((std::get<0>(t) == number), "Messages must be in first-in first-out order" );
139de0109beSIlya Mishin                 CHECK_MESSAGE((std::get<1>(t) == static_cast<float>(number) + 0.5f), "Messages must be in first-in first-out order" );
140de0109beSIlya Mishin                 CHECK_MESSAGE((std::get<2>(t) == 1), "Messages must be in first-in first-out order" );
141de0109beSIlya Mishin                 ++number;
142de0109beSIlya Mishin                 return t;
14351c0b2f7Stbbdev             } );
14451c0b2f7Stbbdev 
145de0109beSIlya Mishin     oneapi::tbb::flow::make_edge(f1, oneapi::tbb::flow::input_port<0>(testing_node));
146de0109beSIlya Mishin     oneapi::tbb::flow::make_edge(f2, oneapi::tbb::flow::input_port<1>(testing_node));
147de0109beSIlya Mishin     oneapi::tbb::flow::make_edge(c1, oneapi::tbb::flow::input_port<2>(testing_node));
148de0109beSIlya Mishin     make_edge(testing_node, f3);
149de0109beSIlya Mishin     make_edge(f3, q_node);
15051c0b2f7Stbbdev 
151de0109beSIlya Mishin     f1.try_put(1);
152de0109beSIlya Mishin     g.wait_for_all();
153de0109beSIlya Mishin     CHECK_MESSAGE((get_values(q_node).size() == 0),
154de0109beSIlya Mishin         "join_node must broadcast when there is at least one message at each input port");
155de0109beSIlya Mishin     f1.try_put(2);
156de0109beSIlya Mishin     f2.try_put(1.5f);
157de0109beSIlya Mishin     g.wait_for_all();
158de0109beSIlya Mishin     CHECK_MESSAGE((get_values(q_node).size() == 0),
159de0109beSIlya Mishin         "join_node must broadcast when there is at least one message at each input port");
16051c0b2f7Stbbdev     f1.try_put(3);
161de0109beSIlya Mishin     f2.try_put(2.5f);
162de0109beSIlya Mishin     c1.try_put(oneapi::tbb::flow::continue_msg());
16351c0b2f7Stbbdev     g.wait_for_all();
164de0109beSIlya Mishin     CHECK_MESSAGE((get_values(q_node).size() == 1),
165de0109beSIlya Mishin         "join_node must broadcast when there is at least one message at each input port");
166de0109beSIlya Mishin     f2.try_put(3.5f);
167de0109beSIlya Mishin     c1.try_put(oneapi::tbb::flow::continue_msg());
168de0109beSIlya Mishin     g.wait_for_all();
169de0109beSIlya Mishin     CHECK_MESSAGE((get_values(q_node).size() == 1),
170de0109beSIlya Mishin         "If at least one successor accepts the tuple, the head of each input port’s queue is removed");
171de0109beSIlya Mishin     c1.try_put(oneapi::tbb::flow::continue_msg());
172de0109beSIlya Mishin     g.wait_for_all();
173de0109beSIlya Mishin     CHECK_MESSAGE((get_values(q_node).size() == 1),
174de0109beSIlya Mishin         "If at least one successor accepts the tuple, the head of each input port’s queue is removed");
175de0109beSIlya Mishin     c1.try_put(oneapi::tbb::flow::continue_msg());
176de0109beSIlya Mishin     g.wait_for_all();
177de0109beSIlya Mishin     CHECK_MESSAGE((get_values(q_node).size() == 0),
178de0109beSIlya Mishin         "join_node must broadcast when there is at least one message at each input port");
179de0109beSIlya Mishin 
180de0109beSIlya Mishin     oneapi::tbb::flow::remove_edge(testing_node, f3);
181de0109beSIlya Mishin 
182de0109beSIlya Mishin     f1.try_put(1);
183de0109beSIlya Mishin     f2.try_put(1);
184de0109beSIlya Mishin     c1.try_put(oneapi::tbb::flow::continue_msg());
185de0109beSIlya Mishin     g.wait_for_all();
186de0109beSIlya Mishin 
187de0109beSIlya Mishin     my_input_tuple tmp(0, 0.f, input_msg(0));
188de0109beSIlya Mishin     CHECK_MESSAGE((testing_node.try_get(tmp)), "If no one successor accepts the tuple the messages\
189de0109beSIlya Mishin         must remain in their respective input port queues");
190de0109beSIlya Mishin     CHECK_MESSAGE((tmp == my_input_tuple(1, 1.f, input_msg(1))), "If no one successor accepts the tuple\
191de0109beSIlya Mishin         the messages must remain in their respective input port queues");
19251c0b2f7Stbbdev }
19351c0b2f7Stbbdev 
194de0109beSIlya Mishin //! Test join_node<reserving> behavior
19551c0b2f7Stbbdev //! \brief \ref requirement
196de0109beSIlya Mishin TEST_CASE("join_node reserving policy") {
197de0109beSIlya Mishin     conformance::test_with_reserving_join_node_class<oneapi::tbb::flow::write_once_node<int>>();
19851c0b2f7Stbbdev }
19951c0b2f7Stbbdev 
200de0109beSIlya Mishin template<typename KeyType>
201de0109beSIlya Mishin struct MyHash{
hashMyHash202de0109beSIlya Mishin     std::size_t hash(const KeyType &k) const {
203de0109beSIlya Mishin         return k * 2000 + 3;
204de0109beSIlya Mishin     }
205de0109beSIlya Mishin 
equalMyHash206de0109beSIlya Mishin     bool equal(const KeyType &k1, const KeyType &k2) const{
207de0109beSIlya Mishin         return hash(k1) == hash(k2);
208de0109beSIlya Mishin     }
209de0109beSIlya Mishin };
210de0109beSIlya Mishin 
211de0109beSIlya Mishin //! Test join_node<key_matching> behavior
21251c0b2f7Stbbdev //! \brief \ref requirement
213de0109beSIlya Mishin TEST_CASE("join_node key_matching policy"){
214de0109beSIlya Mishin     oneapi::tbb::flow::graph g;
__anon798ebb9d0602(const oneapi::tbb::flow::continue_msg &) 215de0109beSIlya Mishin     auto body1 = [](const oneapi::tbb::flow::continue_msg &) -> int { return 1; };
__anon798ebb9d0702(const float &val) 216de0109beSIlya Mishin     auto body2 = [](const float &val) -> int { return static_cast<int>(val); };
21751c0b2f7Stbbdev 
218de0109beSIlya Mishin     oneapi::tbb::flow::join_node<std::tuple<oneapi::tbb::flow::continue_msg, float>,
219de0109beSIlya Mishin         oneapi::tbb::flow::key_matching<int, MyHash<int>>> testing_node(g, body1, body2);
22051c0b2f7Stbbdev 
221de0109beSIlya Mishin     oneapi::tbb::flow::input_port<0>(testing_node).try_put(oneapi::tbb::flow::continue_msg());
222de0109beSIlya Mishin     oneapi::tbb::flow::input_port<1>(testing_node).try_put(1.3f);
22351c0b2f7Stbbdev 
22451c0b2f7Stbbdev     g.wait_for_all();
22551c0b2f7Stbbdev 
226de0109beSIlya Mishin     std::tuple<oneapi::tbb::flow::continue_msg, float> tmp;
227de0109beSIlya Mishin     CHECK_MESSAGE((testing_node.try_get(tmp)), "Mapped keys should match.\
228de0109beSIlya Mishin         If no successor accepts the tuple, it is must been saved and will be forwarded on a subsequent try_get");
229de0109beSIlya Mishin     CHECK_MESSAGE((!testing_node.try_get(tmp)), "Message should not exist after item is consumed");
230de0109beSIlya Mishin }
231de0109beSIlya Mishin 
232de0109beSIlya Mishin //! Test join_node<tag_matching> behavior
233de0109beSIlya Mishin //! \brief \ref requirement
234de0109beSIlya Mishin TEST_CASE("join_node tag_matching policy"){
235de0109beSIlya Mishin     oneapi::tbb::flow::graph g;
__anon798ebb9d0802(const oneapi::tbb::flow::continue_msg &) 236de0109beSIlya Mishin     auto body1 = [](const oneapi::tbb::flow::continue_msg &) -> oneapi::tbb::flow::tag_value { return 1; };
__anon798ebb9d0902(const float &val) 237de0109beSIlya Mishin     auto body2 = [](const float &val) -> oneapi::tbb::flow::tag_value { return static_cast<oneapi::tbb::flow::tag_value>(val); };
238de0109beSIlya Mishin 
239de0109beSIlya Mishin     oneapi::tbb::flow::join_node<std::tuple<oneapi::tbb::flow::continue_msg, float>,
240de0109beSIlya Mishin         oneapi::tbb::flow::tag_matching> testing_node(g, body1, body2);
241de0109beSIlya Mishin 
242de0109beSIlya Mishin     oneapi::tbb::flow::input_port<0>(testing_node).try_put(oneapi::tbb::flow::continue_msg());
243de0109beSIlya Mishin     oneapi::tbb::flow::input_port<1>(testing_node).try_put(1.3f);
244de0109beSIlya Mishin 
245de0109beSIlya Mishin     g.wait_for_all();
246de0109beSIlya Mishin 
247de0109beSIlya Mishin     std::tuple<oneapi::tbb::flow::continue_msg, float> tmp;
248de0109beSIlya Mishin     CHECK_MESSAGE((testing_node.try_get(tmp) == true), "Mapped keys should match");
249de0109beSIlya Mishin }
250de0109beSIlya Mishin 
251de0109beSIlya Mishin #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
252de0109beSIlya Mishin //! Test deduction guides
253de0109beSIlya Mishin //! \brief \ref requirement
254de0109beSIlya Mishin TEST_CASE("Deduction guides test"){
255de0109beSIlya Mishin     test_deduction_guides();
256de0109beSIlya Mishin }
257de0109beSIlya Mishin #endif
258de0109beSIlya Mishin 
259de0109beSIlya Mishin //! Test join_node input_ports() returns a tuple of input ports.
260de0109beSIlya Mishin //! \brief \ref interface \ref requirement
261de0109beSIlya Mishin TEST_CASE("join_node output_ports") {
262de0109beSIlya Mishin     oneapi::tbb::flow::graph g;
263de0109beSIlya Mishin     oneapi::tbb::flow::join_node<std::tuple<int>> node(g);
264de0109beSIlya Mishin 
265de0109beSIlya Mishin     CHECK_MESSAGE((std::is_same<oneapi::tbb::flow::join_node<std::tuple<int>>::input_ports_type&,
266de0109beSIlya Mishin         decltype(node.input_ports())>::value), "join_node input_ports should returns a tuple of input ports");
26751c0b2f7Stbbdev }
268*a088cfa0SKonstantin Boyarinov 
269*a088cfa0SKonstantin Boyarinov #if __TBB_CPP17_INVOKE_PRESENT
270*a088cfa0SKonstantin Boyarinov 
271*a088cfa0SKonstantin Boyarinov template <typename K, typename Body1, typename Body2>
test_invoke_basic(Body1 body1,Body2 body2)272*a088cfa0SKonstantin Boyarinov void test_invoke_basic(Body1 body1, Body2 body2) {
273*a088cfa0SKonstantin Boyarinov     static_assert(std::is_same_v<std::decay_t<K>, std::size_t>, "incorrect test setup");
274*a088cfa0SKonstantin Boyarinov     using namespace oneapi::tbb::flow;
275*a088cfa0SKonstantin Boyarinov     auto generator = [](std::size_t n) { return test_invoke::SmartID<std::size_t>(n); };
276*a088cfa0SKonstantin Boyarinov     graph g;
277*a088cfa0SKonstantin Boyarinov 
278*a088cfa0SKonstantin Boyarinov     function_node<std::size_t, test_invoke::SmartID<std::size_t>> f1(g, unlimited, generator);
279*a088cfa0SKonstantin Boyarinov     function_node<std::size_t, test_invoke::SmartID<std::size_t>> f2(g, unlimited, generator);
280*a088cfa0SKonstantin Boyarinov 
281*a088cfa0SKonstantin Boyarinov     using tuple_type = std::tuple<test_invoke::SmartID<std::size_t>, test_invoke::SmartID<std::size_t>>;
282*a088cfa0SKonstantin Boyarinov     using join_type = join_node<tuple_type, key_matching<K>>;
283*a088cfa0SKonstantin Boyarinov 
284*a088cfa0SKonstantin Boyarinov 
285*a088cfa0SKonstantin Boyarinov     join_type j(g, body1, body2);
286*a088cfa0SKonstantin Boyarinov 
287*a088cfa0SKonstantin Boyarinov     buffer_node<tuple_type> buf(g);
288*a088cfa0SKonstantin Boyarinov 
289*a088cfa0SKonstantin Boyarinov     make_edge(f1, input_port<0>(j));
290*a088cfa0SKonstantin Boyarinov     make_edge(f2, input_port<1>(j));
291*a088cfa0SKonstantin Boyarinov     make_edge(j, buf);
292*a088cfa0SKonstantin Boyarinov 
293*a088cfa0SKonstantin Boyarinov     std::size_t objects_count = 100;
294*a088cfa0SKonstantin Boyarinov     for (std::size_t i = 0; i < objects_count; ++i) {
295*a088cfa0SKonstantin Boyarinov         f1.try_put(i);
296*a088cfa0SKonstantin Boyarinov         f2.try_put(objects_count - i - 1);
297*a088cfa0SKonstantin Boyarinov     }
298*a088cfa0SKonstantin Boyarinov 
299*a088cfa0SKonstantin Boyarinov     g.wait_for_all();
300*a088cfa0SKonstantin Boyarinov 
301*a088cfa0SKonstantin Boyarinov     std::size_t buf_size = 0;
302*a088cfa0SKonstantin Boyarinov     tuple_type tpl;
303*a088cfa0SKonstantin Boyarinov 
304*a088cfa0SKonstantin Boyarinov     while(buf.try_get(tpl)) {
305*a088cfa0SKonstantin Boyarinov         ++buf_size;
306*a088cfa0SKonstantin Boyarinov         CHECK(std::get<0>(tpl).id == std::get<1>(tpl).id);
307*a088cfa0SKonstantin Boyarinov     }
308*a088cfa0SKonstantin Boyarinov     CHECK(buf_size == objects_count);
309*a088cfa0SKonstantin Boyarinov }
310*a088cfa0SKonstantin Boyarinov 
311*a088cfa0SKonstantin Boyarinov //! Test that key_matching join_node uses std::invoke to run the body
312*a088cfa0SKonstantin Boyarinov //! \brief \ref requirement
313*a088cfa0SKonstantin Boyarinov TEST_CASE("key_matching join_node invoke semantics") {
314*a088cfa0SKonstantin Boyarinov     test_invoke_basic</*K = */std::size_t>(&test_invoke::SmartID<std::size_t>::get_id, &test_invoke::SmartID<std::size_t>::id);
315*a088cfa0SKonstantin Boyarinov     test_invoke_basic</*K = */const std::size_t&>(&test_invoke::SmartID<std::size_t>::get_id_ref, &test_invoke::SmartID<std::size_t>::get_id_ref);
316*a088cfa0SKonstantin Boyarinov }
317*a088cfa0SKonstantin Boyarinov #endif // __TBB_CPP17_INVOKE_PRESENT
318