xref: /oneTBB/test/tbb/test_join_node.cpp (revision 51c0b2f7)
1*51c0b2f7Stbbdev /*
2*51c0b2f7Stbbdev     Copyright (c) 2005-2020 Intel Corporation
3*51c0b2f7Stbbdev 
4*51c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
5*51c0b2f7Stbbdev     you may not use this file except in compliance with the License.
6*51c0b2f7Stbbdev     You may obtain a copy of the License at
7*51c0b2f7Stbbdev 
8*51c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
9*51c0b2f7Stbbdev 
10*51c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
11*51c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
12*51c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*51c0b2f7Stbbdev     See the License for the specific language governing permissions and
14*51c0b2f7Stbbdev     limitations under the License.
15*51c0b2f7Stbbdev */
16*51c0b2f7Stbbdev 
17*51c0b2f7Stbbdev #include "common/config.h"
18*51c0b2f7Stbbdev 
19*51c0b2f7Stbbdev #include "test_join_node.h"
20*51c0b2f7Stbbdev 
21*51c0b2f7Stbbdev 
22*51c0b2f7Stbbdev //! \file test_join_node.cpp
23*51c0b2f7Stbbdev //! \brief Test for [flow_graph.join_node] specification
24*51c0b2f7Stbbdev 
25*51c0b2f7Stbbdev 
26*51c0b2f7Stbbdev static std::atomic<int> output_count;
27*51c0b2f7Stbbdev 
28*51c0b2f7Stbbdev // get the tag from the output tuple and emit it.
29*51c0b2f7Stbbdev // the first tuple component is tag * 2 cast to the type
30*51c0b2f7Stbbdev template<typename OutputTupleType>
31*51c0b2f7Stbbdev class recirc_output_func_body {
32*51c0b2f7Stbbdev public:
33*51c0b2f7Stbbdev     // we only need this to use input_node_helper
34*51c0b2f7Stbbdev     typedef typename tbb::flow::join_node<OutputTupleType, tbb::flow::tag_matching> join_node_type;
35*51c0b2f7Stbbdev     static const int N = std::tuple_size<OutputTupleType>::value;
36*51c0b2f7Stbbdev     int operator()(const OutputTupleType &v) {
37*51c0b2f7Stbbdev         int out = int(std::get<0>(v))/2;
38*51c0b2f7Stbbdev         input_node_helper<N, join_node_type>::only_check_value(out, v);
39*51c0b2f7Stbbdev         ++output_count;
40*51c0b2f7Stbbdev         return out;
41*51c0b2f7Stbbdev     }
42*51c0b2f7Stbbdev };
43*51c0b2f7Stbbdev 
44*51c0b2f7Stbbdev template<typename JType>
45*51c0b2f7Stbbdev class tag_recirculation_test {
46*51c0b2f7Stbbdev public:
47*51c0b2f7Stbbdev     typedef typename JType::output_type TType;
48*51c0b2f7Stbbdev     typedef typename std::tuple<int, tbb::flow::continue_msg> input_tuple_type;
49*51c0b2f7Stbbdev     typedef tbb::flow::join_node<input_tuple_type, tbb::flow::reserving> input_join_type;
50*51c0b2f7Stbbdev     static const int N = std::tuple_size<TType>::value;
51*51c0b2f7Stbbdev     static void test() {
52*51c0b2f7Stbbdev         input_node_helper<N, JType>::print_remark("Recirculation test of tag-matching join");
53*51c0b2f7Stbbdev         INFO(" >\n");
54*51c0b2f7Stbbdev         for(int maxTag = 1; maxTag <10; maxTag *= 3) {
55*51c0b2f7Stbbdev             for(int i = 0; i < N; ++i) all_input_nodes[i][0] = NULL;
56*51c0b2f7Stbbdev 
57*51c0b2f7Stbbdev             tbb::flow::graph g;
58*51c0b2f7Stbbdev             // this is the tag-matching join we're testing
59*51c0b2f7Stbbdev             JType * my_join = makeJoin<N, JType, tbb::flow::tag_matching>::create(g);
60*51c0b2f7Stbbdev             // input_node for continue messages
61*51c0b2f7Stbbdev             tbb::flow::input_node<tbb::flow::continue_msg> snode(g, recirc_input_node_body());
62*51c0b2f7Stbbdev             // reserving join that matches recirculating tags with continue messages.
63*51c0b2f7Stbbdev             input_join_type * my_input_join = makeJoin<2, input_join_type, tbb::flow::reserving>::create(g);
64*51c0b2f7Stbbdev             // tbb::flow::make_edge(snode, tbb::flow::input_port<1>(*my_input_join));
65*51c0b2f7Stbbdev             tbb::flow::make_edge(snode, std::get<1>(my_input_join->input_ports()));
66*51c0b2f7Stbbdev             // queue to hold the tags
67*51c0b2f7Stbbdev             tbb::flow::queue_node<int> tag_queue(g);
68*51c0b2f7Stbbdev             tbb::flow::make_edge(tag_queue, tbb::flow::input_port<0>(*my_input_join));
69*51c0b2f7Stbbdev             // add all the function_nodes that are inputs to the tag-matching join
70*51c0b2f7Stbbdev             input_node_helper<N, JType>::add_recirc_func_nodes(*my_join, *my_input_join, g);
71*51c0b2f7Stbbdev             // add the function_node that accepts the output of the join and emits the int tag it was based on
72*51c0b2f7Stbbdev             tbb::flow::function_node<TType, int> recreate_tag(g, tbb::flow::unlimited, recirc_output_func_body<TType>());
73*51c0b2f7Stbbdev             tbb::flow::make_edge(*my_join, recreate_tag);
74*51c0b2f7Stbbdev             // now the recirculating part (output back to the queue)
75*51c0b2f7Stbbdev             tbb::flow::make_edge(recreate_tag, tag_queue);
76*51c0b2f7Stbbdev 
77*51c0b2f7Stbbdev             // put the tags into the queue
78*51c0b2f7Stbbdev             for(int t = 1; t<=maxTag; ++t) tag_queue.try_put(t);
79*51c0b2f7Stbbdev 
80*51c0b2f7Stbbdev             input_count = Recirc_count;
81*51c0b2f7Stbbdev             output_count = 0;
82*51c0b2f7Stbbdev 
83*51c0b2f7Stbbdev             // start up the source node to get things going
84*51c0b2f7Stbbdev             snode.activate();
85*51c0b2f7Stbbdev 
86*51c0b2f7Stbbdev             // wait for everything to stop
87*51c0b2f7Stbbdev             g.wait_for_all();
88*51c0b2f7Stbbdev 
89*51c0b2f7Stbbdev             CHECK_MESSAGE( (output_count==Recirc_count), "not all instances were received");
90*51c0b2f7Stbbdev 
91*51c0b2f7Stbbdev             int j{};
92*51c0b2f7Stbbdev             // grab the tags from the queue, record them
93*51c0b2f7Stbbdev             std::vector<bool> out_tally(maxTag, false);
94*51c0b2f7Stbbdev             for(int i = 0; i < maxTag; ++i) {
95*51c0b2f7Stbbdev                 CHECK_MESSAGE( (tag_queue.try_get(j)), "not enough tags in queue");
96*51c0b2f7Stbbdev                 CHECK_MESSAGE( (!out_tally.at(j-1)), "duplicate tag from queue");
97*51c0b2f7Stbbdev                 out_tally[j-1] = true;
98*51c0b2f7Stbbdev             }
99*51c0b2f7Stbbdev             CHECK_MESSAGE( (!tag_queue.try_get(j)), "Extra tags in recirculation queue");
100*51c0b2f7Stbbdev 
101*51c0b2f7Stbbdev             // deconstruct graph
102*51c0b2f7Stbbdev             input_node_helper<N, JType>::remove_recirc_func_nodes(*my_join, *my_input_join);
103*51c0b2f7Stbbdev             tbb::flow::remove_edge(*my_join, recreate_tag);
104*51c0b2f7Stbbdev             makeJoin<N, JType, tbb::flow::tag_matching>::destroy(my_join);
105*51c0b2f7Stbbdev             tbb::flow::remove_edge(tag_queue, tbb::flow::input_port<0>(*my_input_join));
106*51c0b2f7Stbbdev             tbb::flow::remove_edge(snode, tbb::flow::input_port<1>(*my_input_join));
107*51c0b2f7Stbbdev             makeJoin<2, input_join_type, tbb::flow::reserving>::destroy(my_input_join);
108*51c0b2f7Stbbdev         }
109*51c0b2f7Stbbdev     }
110*51c0b2f7Stbbdev };
111*51c0b2f7Stbbdev 
112*51c0b2f7Stbbdev template<typename JType>
113*51c0b2f7Stbbdev class generate_recirc_test {
114*51c0b2f7Stbbdev public:
115*51c0b2f7Stbbdev     typedef tbb::flow::join_node<JType, tbb::flow::tag_matching> join_node_type;
116*51c0b2f7Stbbdev     static void do_test() {
117*51c0b2f7Stbbdev         tag_recirculation_test<join_node_type>::test();
118*51c0b2f7Stbbdev     }
119*51c0b2f7Stbbdev };
120*51c0b2f7Stbbdev 
121*51c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
122*51c0b2f7Stbbdev #include <array>
123*51c0b2f7Stbbdev #include <vector>
124*51c0b2f7Stbbdev void test_follows_and_precedes_api() {
125*51c0b2f7Stbbdev     using msg_t = tbb::flow::continue_msg;
126*51c0b2f7Stbbdev     using JoinOutputType = std::tuple<msg_t, msg_t, msg_t>;
127*51c0b2f7Stbbdev 
128*51c0b2f7Stbbdev     std::array<msg_t, 3> messages_for_follows = { {msg_t(), msg_t(), msg_t()} };
129*51c0b2f7Stbbdev     std::vector<msg_t> messages_for_precedes = {msg_t(), msg_t(), msg_t()};
130*51c0b2f7Stbbdev 
131*51c0b2f7Stbbdev     follows_and_precedes_testing::test_follows
132*51c0b2f7Stbbdev         <msg_t, tbb::flow::join_node<JoinOutputType>, tbb::flow::buffer_node<msg_t>>(messages_for_follows);
133*51c0b2f7Stbbdev     follows_and_precedes_testing::test_follows
134*51c0b2f7Stbbdev         <msg_t, tbb::flow::join_node<JoinOutputType, tbb::flow::queueing>>(messages_for_follows);
135*51c0b2f7Stbbdev     follows_and_precedes_testing::test_follows
136*51c0b2f7Stbbdev         <msg_t, tbb::flow::join_node<JoinOutputType, tbb::flow::reserving>, tbb::flow::buffer_node<msg_t>>(messages_for_follows);
137*51c0b2f7Stbbdev     auto b = [](msg_t) { return msg_t(); };
138*51c0b2f7Stbbdev     class hash_compare {
139*51c0b2f7Stbbdev     public:
140*51c0b2f7Stbbdev         std::size_t hash(msg_t) const { return 0; }
141*51c0b2f7Stbbdev         bool equal(msg_t, msg_t) const { return true; }
142*51c0b2f7Stbbdev     };
143*51c0b2f7Stbbdev     follows_and_precedes_testing::test_follows
144*51c0b2f7Stbbdev         <msg_t, tbb::flow::join_node<JoinOutputType, tbb::flow::key_matching<msg_t, hash_compare>>, tbb::flow::buffer_node<msg_t>>
145*51c0b2f7Stbbdev         (messages_for_follows, b, b, b);
146*51c0b2f7Stbbdev 
147*51c0b2f7Stbbdev     follows_and_precedes_testing::test_precedes
148*51c0b2f7Stbbdev         <msg_t, tbb::flow::join_node<JoinOutputType>>(messages_for_precedes);
149*51c0b2f7Stbbdev     follows_and_precedes_testing::test_precedes
150*51c0b2f7Stbbdev         <msg_t, tbb::flow::join_node<JoinOutputType, tbb::flow::queueing>>(messages_for_precedes);
151*51c0b2f7Stbbdev     follows_and_precedes_testing::test_precedes
152*51c0b2f7Stbbdev         <msg_t, tbb::flow::join_node<JoinOutputType, tbb::flow::reserving>>(messages_for_precedes);
153*51c0b2f7Stbbdev     follows_and_precedes_testing::test_precedes
154*51c0b2f7Stbbdev         <msg_t, tbb::flow::join_node<JoinOutputType, tbb::flow::key_matching<msg_t, hash_compare>>>
155*51c0b2f7Stbbdev         (messages_for_precedes, b, b, b);
156*51c0b2f7Stbbdev }
157*51c0b2f7Stbbdev #endif
158*51c0b2f7Stbbdev 
159*51c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
160*51c0b2f7Stbbdev void test_deduction_guides() {
161*51c0b2f7Stbbdev     using namespace tbb::flow;
162*51c0b2f7Stbbdev 
163*51c0b2f7Stbbdev     graph g;
164*51c0b2f7Stbbdev     using tuple_type = std::tuple<int, int, int>;
165*51c0b2f7Stbbdev     broadcast_node<int> b1(g), b2(g), b3(g);
166*51c0b2f7Stbbdev     broadcast_node<tuple_type> b4(g);
167*51c0b2f7Stbbdev     join_node<tuple_type> j0(g);
168*51c0b2f7Stbbdev 
169*51c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
170*51c0b2f7Stbbdev     join_node j1(follows(b1, b2, b3));
171*51c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(j1), join_node<tuple_type>>);
172*51c0b2f7Stbbdev 
173*51c0b2f7Stbbdev     join_node j2(follows(b1, b2, b3), reserving());
174*51c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(j2), join_node<tuple_type, reserving>>);
175*51c0b2f7Stbbdev 
176*51c0b2f7Stbbdev     join_node j3(precedes(b4));
177*51c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(j3), join_node<tuple_type>>);
178*51c0b2f7Stbbdev 
179*51c0b2f7Stbbdev     join_node j4(precedes(b4), reserving());
180*51c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(j4), join_node<tuple_type, reserving>>);
181*51c0b2f7Stbbdev #endif
182*51c0b2f7Stbbdev 
183*51c0b2f7Stbbdev     join_node j5(j0);
184*51c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(j5), join_node<tuple_type>>);
185*51c0b2f7Stbbdev }
186*51c0b2f7Stbbdev 
187*51c0b2f7Stbbdev #endif
188*51c0b2f7Stbbdev 
189*51c0b2f7Stbbdev namespace multiple_predecessors {
190*51c0b2f7Stbbdev 
191*51c0b2f7Stbbdev using namespace tbb::flow;
192*51c0b2f7Stbbdev 
193*51c0b2f7Stbbdev using join_node_t = join_node<std::tuple<continue_msg, continue_msg, continue_msg>, reserving>;
194*51c0b2f7Stbbdev using queue_node_t = queue_node<std::tuple<continue_msg, continue_msg, continue_msg>>;
195*51c0b2f7Stbbdev 
196*51c0b2f7Stbbdev void twist_join_connections(
197*51c0b2f7Stbbdev     buffer_node<continue_msg>& bn1, buffer_node<continue_msg>& bn2, buffer_node<continue_msg>& bn3,
198*51c0b2f7Stbbdev     join_node_t& jn)
199*51c0b2f7Stbbdev {
200*51c0b2f7Stbbdev     // order, in which edges are created/destroyed, is important
201*51c0b2f7Stbbdev     make_edge(bn1, input_port<0>(jn));
202*51c0b2f7Stbbdev     make_edge(bn2, input_port<0>(jn));
203*51c0b2f7Stbbdev     make_edge(bn3, input_port<0>(jn));
204*51c0b2f7Stbbdev 
205*51c0b2f7Stbbdev     remove_edge(bn3, input_port<0>(jn));
206*51c0b2f7Stbbdev     make_edge  (bn3, input_port<2>(jn));
207*51c0b2f7Stbbdev 
208*51c0b2f7Stbbdev     remove_edge(bn2, input_port<0>(jn));
209*51c0b2f7Stbbdev     make_edge  (bn2, input_port<1>(jn));
210*51c0b2f7Stbbdev }
211*51c0b2f7Stbbdev 
212*51c0b2f7Stbbdev std::unique_ptr<join_node_t> connect_join_via_make_edge(
213*51c0b2f7Stbbdev     graph& g, buffer_node<continue_msg>& bn1, buffer_node<continue_msg>& bn2,
214*51c0b2f7Stbbdev     buffer_node<continue_msg>& bn3, queue_node_t& qn)
215*51c0b2f7Stbbdev {
216*51c0b2f7Stbbdev     std::unique_ptr<join_node_t> jn( new join_node_t(g) );
217*51c0b2f7Stbbdev     twist_join_connections( bn1, bn2, bn3, *jn );
218*51c0b2f7Stbbdev     make_edge(*jn, qn);
219*51c0b2f7Stbbdev     return jn;
220*51c0b2f7Stbbdev }
221*51c0b2f7Stbbdev 
222*51c0b2f7Stbbdev #if TBB_PREVIEW_FLOW_GRAPH_FEATURES
223*51c0b2f7Stbbdev std::unique_ptr<join_node_t> connect_join_via_follows(
224*51c0b2f7Stbbdev     graph&, buffer_node<continue_msg>& bn1, buffer_node<continue_msg>& bn2,
225*51c0b2f7Stbbdev     buffer_node<continue_msg>& bn3, queue_node_t& qn)
226*51c0b2f7Stbbdev {
227*51c0b2f7Stbbdev     auto bn_set = make_node_set(bn1, bn2, bn3);
228*51c0b2f7Stbbdev     std::unique_ptr<join_node_t> jn( new join_node_t(follows(bn_set)) );
229*51c0b2f7Stbbdev     make_edge(*jn, qn);
230*51c0b2f7Stbbdev     return jn;
231*51c0b2f7Stbbdev }
232*51c0b2f7Stbbdev 
233*51c0b2f7Stbbdev std::unique_ptr<join_node_t> connect_join_via_precedes(
234*51c0b2f7Stbbdev     graph&, buffer_node<continue_msg>& bn1, buffer_node<continue_msg>& bn2,
235*51c0b2f7Stbbdev     buffer_node<continue_msg>& bn3, queue_node_t& qn)
236*51c0b2f7Stbbdev {
237*51c0b2f7Stbbdev     auto qn_set = make_node_set(qn);
238*51c0b2f7Stbbdev     auto qn_copy_set = qn_set;
239*51c0b2f7Stbbdev     std::unique_ptr<join_node_t> jn( new join_node_t(precedes(qn_copy_set)) );
240*51c0b2f7Stbbdev     twist_join_connections( bn1, bn2, bn3, *jn );
241*51c0b2f7Stbbdev     return jn;
242*51c0b2f7Stbbdev }
243*51c0b2f7Stbbdev #endif // TBB_PREVIEW_FLOW_GRAPH_FEATURES
244*51c0b2f7Stbbdev 
245*51c0b2f7Stbbdev void run_and_check(
246*51c0b2f7Stbbdev     graph& g, buffer_node<continue_msg>& bn1, buffer_node<continue_msg>& bn2,
247*51c0b2f7Stbbdev     buffer_node<continue_msg>& bn3, queue_node_t& qn, bool expected)
248*51c0b2f7Stbbdev {
249*51c0b2f7Stbbdev     std::tuple<continue_msg, continue_msg, continue_msg> msg;
250*51c0b2f7Stbbdev 
251*51c0b2f7Stbbdev     bn1.try_put(continue_msg());
252*51c0b2f7Stbbdev     bn2.try_put(continue_msg());
253*51c0b2f7Stbbdev     bn3.try_put(continue_msg());
254*51c0b2f7Stbbdev     g.wait_for_all();
255*51c0b2f7Stbbdev 
256*51c0b2f7Stbbdev     CHECK_MESSAGE(
257*51c0b2f7Stbbdev         (qn.try_get(msg) == expected),
258*51c0b2f7Stbbdev         "Unexpected message absence/existence at the end of the graph."
259*51c0b2f7Stbbdev     );
260*51c0b2f7Stbbdev }
261*51c0b2f7Stbbdev 
262*51c0b2f7Stbbdev template<typename ConnectJoinNodeFunc>
263*51c0b2f7Stbbdev void test(ConnectJoinNodeFunc&& connect_join_node) {
264*51c0b2f7Stbbdev     graph g;
265*51c0b2f7Stbbdev     buffer_node<continue_msg> bn1(g);
266*51c0b2f7Stbbdev     buffer_node<continue_msg> bn2(g);
267*51c0b2f7Stbbdev     buffer_node<continue_msg> bn3(g);
268*51c0b2f7Stbbdev     queue_node_t qn(g);
269*51c0b2f7Stbbdev 
270*51c0b2f7Stbbdev     auto jn = connect_join_node(g, bn1, bn2, bn3, qn);
271*51c0b2f7Stbbdev 
272*51c0b2f7Stbbdev     run_and_check(g, bn1, bn2, bn3, qn, /*expected=*/true);
273*51c0b2f7Stbbdev 
274*51c0b2f7Stbbdev     remove_edge(bn3, input_port<2>(*jn));
275*51c0b2f7Stbbdev     remove_edge(bn2, input_port<1>(*jn));
276*51c0b2f7Stbbdev     remove_edge(bn1, input_port<0>(*jn));
277*51c0b2f7Stbbdev     remove_edge(*jn, qn);
278*51c0b2f7Stbbdev 
279*51c0b2f7Stbbdev     run_and_check(g, bn1, bn2, bn3, qn, /*expected=*/false);
280*51c0b2f7Stbbdev }
281*51c0b2f7Stbbdev } // namespace multiple_predecessors
282*51c0b2f7Stbbdev 
283*51c0b2f7Stbbdev 
284*51c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
285*51c0b2f7Stbbdev //! Test follows and precedes API
286*51c0b2f7Stbbdev //! \brief \ref error_guessing
287*51c0b2f7Stbbdev TEST_CASE("Test follows and preceedes API"){
288*51c0b2f7Stbbdev     test_follows_and_precedes_api();
289*51c0b2f7Stbbdev }
290*51c0b2f7Stbbdev #endif
291*51c0b2f7Stbbdev 
292*51c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
293*51c0b2f7Stbbdev //! Test deduction guides
294*51c0b2f7Stbbdev //! \brief \ref requirement
295*51c0b2f7Stbbdev TEST_CASE("Deduction guides test"){
296*51c0b2f7Stbbdev     test_deduction_guides();
297*51c0b2f7Stbbdev }
298*51c0b2f7Stbbdev #endif
299*51c0b2f7Stbbdev 
300*51c0b2f7Stbbdev //! Test hash buffers behavior
301*51c0b2f7Stbbdev //! \brief \ref error_guessing
302*51c0b2f7Stbbdev TEST_CASE("Tagged buffers test"){
303*51c0b2f7Stbbdev     TestTaggedBuffers();
304*51c0b2f7Stbbdev }
305*51c0b2f7Stbbdev 
306*51c0b2f7Stbbdev //! Test with various policies and tuple sizes
307*51c0b2f7Stbbdev //! \brief \ref error_guessing
308*51c0b2f7Stbbdev TEST_CASE("Main test"){
309*51c0b2f7Stbbdev     test_main<tbb::flow::queueing>();
310*51c0b2f7Stbbdev     test_main<tbb::flow::reserving>();
311*51c0b2f7Stbbdev     test_main<tbb::flow::tag_matching>();
312*51c0b2f7Stbbdev }
313*51c0b2f7Stbbdev 
314*51c0b2f7Stbbdev //! Test with recirculating tags
315*51c0b2f7Stbbdev //! \brief \ref error_guessing
316*51c0b2f7Stbbdev TEST_CASE("Recirculation test"){
317*51c0b2f7Stbbdev     generate_recirc_test<std::tuple<int,float> >::do_test();
318*51c0b2f7Stbbdev }
319*51c0b2f7Stbbdev 
320*51c0b2f7Stbbdev //! Test maintaining correct count of ports without input
321*51c0b2f7Stbbdev //! \brief \ref error_guessing
322*51c0b2f7Stbbdev TEST_CASE("Test removal of the predecessor while having none") {
323*51c0b2f7Stbbdev     using namespace multiple_predecessors;
324*51c0b2f7Stbbdev 
325*51c0b2f7Stbbdev     test(connect_join_via_make_edge);
326*51c0b2f7Stbbdev #if TBB_PREVIEW_FLOW_GRAPH_FEATURES
327*51c0b2f7Stbbdev     test(connect_join_via_follows);
328*51c0b2f7Stbbdev     test(connect_join_via_precedes);
329*51c0b2f7Stbbdev #endif
330*51c0b2f7Stbbdev }
331