xref: /oneTBB/test/tbb/test_split_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 // TODO revamp: move parts dependent on __TBB_EXTRA_DEBUG into separate test(s) since having these
20*51c0b2f7Stbbdev // parts in all of tests might make testing of the product, which is different from what is actually
21*51c0b2f7Stbbdev // released.
22*51c0b2f7Stbbdev #define __TBB_EXTRA_DEBUG 1
23*51c0b2f7Stbbdev #include "tbb/flow_graph.h"
24*51c0b2f7Stbbdev 
25*51c0b2f7Stbbdev #include "common/test.h"
26*51c0b2f7Stbbdev #include "common/utils.h"
27*51c0b2f7Stbbdev #include "common/utils_assert.h"
28*51c0b2f7Stbbdev #include "common/graph_utils.h"
29*51c0b2f7Stbbdev 
30*51c0b2f7Stbbdev 
31*51c0b2f7Stbbdev //! \file test_split_node.cpp
32*51c0b2f7Stbbdev //! \brief Test for [flow_graph.split_node] specification
33*51c0b2f7Stbbdev 
34*51c0b2f7Stbbdev 
35*51c0b2f7Stbbdev #if defined(_MSC_VER) && _MSC_VER < 1600
36*51c0b2f7Stbbdev     #pragma warning (disable : 4503) //disabling the "decorated name length exceeded" warning for VS2008 and earlier
37*51c0b2f7Stbbdev #endif
38*51c0b2f7Stbbdev 
39*51c0b2f7Stbbdev //
40*51c0b2f7Stbbdev // Tests
41*51c0b2f7Stbbdev //
42*51c0b2f7Stbbdev 
43*51c0b2f7Stbbdev const int Count = 300;
44*51c0b2f7Stbbdev const int MaxPorts = 10;
45*51c0b2f7Stbbdev const int MaxNInputs = 5; // max # of input_nodes to register for each split_node input in parallel test
46*51c0b2f7Stbbdev 
47*51c0b2f7Stbbdev std::vector<bool> flags;   // for checking output
48*51c0b2f7Stbbdev 
49*51c0b2f7Stbbdev template<typename T>
50*51c0b2f7Stbbdev class name_of {
51*51c0b2f7Stbbdev public:
52*51c0b2f7Stbbdev     static const char* name() { return  "Unknown"; }
53*51c0b2f7Stbbdev };
54*51c0b2f7Stbbdev template<>
55*51c0b2f7Stbbdev class name_of<int> {
56*51c0b2f7Stbbdev public:
57*51c0b2f7Stbbdev     static const char* name() { return  "int"; }
58*51c0b2f7Stbbdev };
59*51c0b2f7Stbbdev template<>
60*51c0b2f7Stbbdev class name_of<float> {
61*51c0b2f7Stbbdev public:
62*51c0b2f7Stbbdev     static const char* name() { return  "float"; }
63*51c0b2f7Stbbdev };
64*51c0b2f7Stbbdev template<>
65*51c0b2f7Stbbdev class name_of<double> {
66*51c0b2f7Stbbdev public:
67*51c0b2f7Stbbdev     static const char* name() { return  "double"; }
68*51c0b2f7Stbbdev };
69*51c0b2f7Stbbdev template<>
70*51c0b2f7Stbbdev class name_of<long> {
71*51c0b2f7Stbbdev public:
72*51c0b2f7Stbbdev     static const char* name() { return  "long"; }
73*51c0b2f7Stbbdev };
74*51c0b2f7Stbbdev template<>
75*51c0b2f7Stbbdev class name_of<short> {
76*51c0b2f7Stbbdev public:
77*51c0b2f7Stbbdev     static const char* name() { return  "short"; }
78*51c0b2f7Stbbdev };
79*51c0b2f7Stbbdev 
80*51c0b2f7Stbbdev // T must be arithmetic, and shouldn't wrap around for reasonable sizes of Count (which is now 150, and maxPorts is 10,
81*51c0b2f7Stbbdev // so the max number generated right now is 1500 or so.)  Input will generate a series of TT with value
82*51c0b2f7Stbbdev // (init_val + (i-1)*addend) * my_mult, where i is the i-th invocation of the body.  We are attaching addend
83*51c0b2f7Stbbdev // input nodes to a join_port, and each will generate part of the numerical series the port is expecting
84*51c0b2f7Stbbdev // to receive.  If there is only one input node, the series order will be maintained; if more than one,
85*51c0b2f7Stbbdev // this is not guaranteed.
86*51c0b2f7Stbbdev 
87*51c0b2f7Stbbdev template<int N>
88*51c0b2f7Stbbdev struct tuple_helper {
89*51c0b2f7Stbbdev     template<typename TupleType>
90*51c0b2f7Stbbdev     static void set_element( TupleType &t, int i) {
91*51c0b2f7Stbbdev         std::get<N-1>(t) = (typename std::tuple_element<N-1,TupleType>::type)(i * (N+1));
92*51c0b2f7Stbbdev         tuple_helper<N-1>::set_element(t, i);
93*51c0b2f7Stbbdev     }
94*51c0b2f7Stbbdev };
95*51c0b2f7Stbbdev 
96*51c0b2f7Stbbdev template<>
97*51c0b2f7Stbbdev struct tuple_helper<1> {
98*51c0b2f7Stbbdev     template<typename TupleType>
99*51c0b2f7Stbbdev     static void set_element(TupleType &t, int i) {
100*51c0b2f7Stbbdev         std::get<0>(t) = (typename std::tuple_element<0,TupleType>::type)(i * 2);
101*51c0b2f7Stbbdev     }
102*51c0b2f7Stbbdev };
103*51c0b2f7Stbbdev 
104*51c0b2f7Stbbdev // if we start N input_bodys they will all have the addend N, and my_count should be initialized to 0 .. N-1.
105*51c0b2f7Stbbdev // the output tuples should have all the sequence, but the order will in general vary.
106*51c0b2f7Stbbdev template<typename TupleType>
107*51c0b2f7Stbbdev class my_input_body {
108*51c0b2f7Stbbdev     typedef TupleType TT;
109*51c0b2f7Stbbdev     static const int N = std::tuple_size<TT>::value;
110*51c0b2f7Stbbdev     int my_count;
111*51c0b2f7Stbbdev     int addend;
112*51c0b2f7Stbbdev public:
113*51c0b2f7Stbbdev     my_input_body(int init_val, int addto) : my_count(init_val), addend(addto) { }
114*51c0b2f7Stbbdev     TT operator()( tbb::flow_control &fc) {
115*51c0b2f7Stbbdev         if(my_count >= Count){
116*51c0b2f7Stbbdev             fc.stop();
117*51c0b2f7Stbbdev             return TT();
118*51c0b2f7Stbbdev         }
119*51c0b2f7Stbbdev         TT v;
120*51c0b2f7Stbbdev         tuple_helper<N>::set_element(v, my_count);
121*51c0b2f7Stbbdev         my_count += addend;
122*51c0b2f7Stbbdev         return v;
123*51c0b2f7Stbbdev     }
124*51c0b2f7Stbbdev };
125*51c0b2f7Stbbdev 
126*51c0b2f7Stbbdev // allocator for split_node.
127*51c0b2f7Stbbdev 
128*51c0b2f7Stbbdev template<int N, typename SType>
129*51c0b2f7Stbbdev class makeSplit {
130*51c0b2f7Stbbdev public:
131*51c0b2f7Stbbdev     static SType *create(tbb::flow::graph& g) {
132*51c0b2f7Stbbdev         SType *temp = new SType(g);
133*51c0b2f7Stbbdev         return temp;
134*51c0b2f7Stbbdev     }
135*51c0b2f7Stbbdev     static void destroy(SType *p) { delete p; }
136*51c0b2f7Stbbdev };
137*51c0b2f7Stbbdev 
138*51c0b2f7Stbbdev // holder for sink_node pointers for eventual deletion
139*51c0b2f7Stbbdev 
140*51c0b2f7Stbbdev static void* all_sink_nodes[MaxPorts];
141*51c0b2f7Stbbdev 
142*51c0b2f7Stbbdev 
143*51c0b2f7Stbbdev template<int ELEM, typename SType>
144*51c0b2f7Stbbdev class sink_node_helper {
145*51c0b2f7Stbbdev public:
146*51c0b2f7Stbbdev     typedef typename SType::input_type TT;
147*51c0b2f7Stbbdev     typedef typename std::tuple_element<ELEM-1,TT>::type IT;
148*51c0b2f7Stbbdev     typedef typename tbb::flow::queue_node<IT> my_sink_node_type;
149*51c0b2f7Stbbdev     static void print_parallel_remark() {
150*51c0b2f7Stbbdev         sink_node_helper<ELEM-1,SType>::print_parallel_remark();
151*51c0b2f7Stbbdev         INFO(", " << name_of<IT>::name());
152*51c0b2f7Stbbdev     }
153*51c0b2f7Stbbdev     static void print_serial_remark() {
154*51c0b2f7Stbbdev         sink_node_helper<ELEM-1,SType>::print_serial_remark();
155*51c0b2f7Stbbdev         INFO(", " << name_of<IT>::name());
156*51c0b2f7Stbbdev     }
157*51c0b2f7Stbbdev     static void add_sink_nodes(SType &my_split, tbb::flow::graph &g) {
158*51c0b2f7Stbbdev         my_sink_node_type *new_node = new my_sink_node_type(g);
159*51c0b2f7Stbbdev         tbb::flow::make_edge( tbb::flow::output_port<ELEM-1>(my_split) , *new_node);
160*51c0b2f7Stbbdev         all_sink_nodes[ELEM-1] = (void *)new_node;
161*51c0b2f7Stbbdev         sink_node_helper<ELEM-1, SType>::add_sink_nodes(my_split, g);
162*51c0b2f7Stbbdev     }
163*51c0b2f7Stbbdev 
164*51c0b2f7Stbbdev     static void check_sink_values() {
165*51c0b2f7Stbbdev         my_sink_node_type *dp = reinterpret_cast<my_sink_node_type *>(all_sink_nodes[ELEM-1]);
166*51c0b2f7Stbbdev         for(int i = 0; i < Count; ++i) {
167*51c0b2f7Stbbdev             IT v{};
168*51c0b2f7Stbbdev             CHECK_MESSAGE(dp->try_get(v), "");
169*51c0b2f7Stbbdev             flags[((int)v) / (ELEM+1)] = true;
170*51c0b2f7Stbbdev         }
171*51c0b2f7Stbbdev         for(int i = 0; i < Count; ++i) {
172*51c0b2f7Stbbdev             CHECK_MESSAGE(flags[i], "");
173*51c0b2f7Stbbdev             flags[i] = false;  // reset for next test
174*51c0b2f7Stbbdev         }
175*51c0b2f7Stbbdev         sink_node_helper<ELEM-1,SType>::check_sink_values();
176*51c0b2f7Stbbdev     }
177*51c0b2f7Stbbdev     static void remove_sink_nodes(SType& my_split) {
178*51c0b2f7Stbbdev         my_sink_node_type *dp = reinterpret_cast<my_sink_node_type *>(all_sink_nodes[ELEM-1]);
179*51c0b2f7Stbbdev         tbb::flow::remove_edge( tbb::flow::output_port<ELEM-1>(my_split) , *dp);
180*51c0b2f7Stbbdev         delete dp;
181*51c0b2f7Stbbdev         sink_node_helper<ELEM-1, SType>::remove_sink_nodes(my_split);
182*51c0b2f7Stbbdev     }
183*51c0b2f7Stbbdev };
184*51c0b2f7Stbbdev 
185*51c0b2f7Stbbdev template<typename SType>
186*51c0b2f7Stbbdev class sink_node_helper<1, SType> {
187*51c0b2f7Stbbdev     typedef typename SType::input_type TT;
188*51c0b2f7Stbbdev     typedef typename std::tuple_element<0,TT>::type IT;
189*51c0b2f7Stbbdev     typedef typename tbb::flow::queue_node<IT> my_sink_node_type;
190*51c0b2f7Stbbdev public:
191*51c0b2f7Stbbdev     static void print_parallel_remark() {
192*51c0b2f7Stbbdev         INFO("Parallel test of split_node< " << name_of<IT>::name());
193*51c0b2f7Stbbdev     }
194*51c0b2f7Stbbdev     static void print_serial_remark() {
195*51c0b2f7Stbbdev         INFO("Serial test of split_node< " << name_of<IT>::name());
196*51c0b2f7Stbbdev     }
197*51c0b2f7Stbbdev     static void add_sink_nodes(SType &my_split, tbb::flow::graph &g) {
198*51c0b2f7Stbbdev         my_sink_node_type *new_node = new my_sink_node_type(g);
199*51c0b2f7Stbbdev         tbb::flow::make_edge( tbb::flow::output_port<0>(my_split) , *new_node);
200*51c0b2f7Stbbdev         all_sink_nodes[0] = (void *)new_node;
201*51c0b2f7Stbbdev     }
202*51c0b2f7Stbbdev     static void check_sink_values() {
203*51c0b2f7Stbbdev         my_sink_node_type *dp = reinterpret_cast<my_sink_node_type *>(all_sink_nodes[0]);
204*51c0b2f7Stbbdev         for(int i = 0; i < Count; ++i) {
205*51c0b2f7Stbbdev             IT v{};
206*51c0b2f7Stbbdev             CHECK_MESSAGE(dp->try_get(v), "");
207*51c0b2f7Stbbdev             flags[((int)v) / 2] = true;
208*51c0b2f7Stbbdev         }
209*51c0b2f7Stbbdev         for(int i = 0; i < Count; ++i) {
210*51c0b2f7Stbbdev             CHECK_MESSAGE(flags[i], "");
211*51c0b2f7Stbbdev             flags[i] = false;  // reset for next test
212*51c0b2f7Stbbdev         }
213*51c0b2f7Stbbdev     }
214*51c0b2f7Stbbdev     static void remove_sink_nodes(SType& my_split) {
215*51c0b2f7Stbbdev         my_sink_node_type *dp = reinterpret_cast<my_sink_node_type *>(all_sink_nodes[0]);
216*51c0b2f7Stbbdev         tbb::flow::remove_edge( tbb::flow::output_port<0>(my_split) , *dp);
217*51c0b2f7Stbbdev         delete dp;
218*51c0b2f7Stbbdev     }
219*51c0b2f7Stbbdev };
220*51c0b2f7Stbbdev 
221*51c0b2f7Stbbdev // parallel_test: create input_nodes that feed tuples into the split node
222*51c0b2f7Stbbdev //    and queue_nodes that receive the output.
223*51c0b2f7Stbbdev template<typename SType>
224*51c0b2f7Stbbdev class parallel_test {
225*51c0b2f7Stbbdev public:
226*51c0b2f7Stbbdev     typedef typename SType::input_type TType;
227*51c0b2f7Stbbdev     typedef tbb::flow::input_node<TType> input_type;
228*51c0b2f7Stbbdev     static const int N = std::tuple_size<TType>::value;
229*51c0b2f7Stbbdev 
230*51c0b2f7Stbbdev     static void test() {
231*51c0b2f7Stbbdev         input_type* all_input_nodes[MaxNInputs];
232*51c0b2f7Stbbdev         sink_node_helper<N,SType>::print_parallel_remark();
233*51c0b2f7Stbbdev         INFO(" >\n");
234*51c0b2f7Stbbdev         for(int i=0; i < MaxPorts; ++i) {
235*51c0b2f7Stbbdev             all_sink_nodes[i] = NULL;
236*51c0b2f7Stbbdev         }
237*51c0b2f7Stbbdev         // try test for # inputs 1 .. MaxNInputs
238*51c0b2f7Stbbdev         for(int nInputs = 1; nInputs <= MaxNInputs; ++nInputs) {
239*51c0b2f7Stbbdev             tbb::flow::graph g;
240*51c0b2f7Stbbdev             SType* my_split = makeSplit<N,SType>::create(g);
241*51c0b2f7Stbbdev 
242*51c0b2f7Stbbdev             // add sinks first so when inputs start spitting out values they are there to catch them
243*51c0b2f7Stbbdev             sink_node_helper<N, SType>::add_sink_nodes((*my_split), g);
244*51c0b2f7Stbbdev 
245*51c0b2f7Stbbdev             // now create nInputs input_nodes, each spitting out i, i+nInputs, i+2*nInputs ...
246*51c0b2f7Stbbdev             // each element of the tuple is i*(n+1), where n is the tuple element index (1-N)
247*51c0b2f7Stbbdev             for(int i = 0; i < nInputs; ++i) {
248*51c0b2f7Stbbdev                 // create input node
249*51c0b2f7Stbbdev                 input_type *s = new input_type(g, my_input_body<TType>(i, nInputs) );
250*51c0b2f7Stbbdev                 tbb::flow::make_edge(*s, *my_split);
251*51c0b2f7Stbbdev                 all_input_nodes[i] = s;
252*51c0b2f7Stbbdev                 s->activate();
253*51c0b2f7Stbbdev             }
254*51c0b2f7Stbbdev 
255*51c0b2f7Stbbdev             g.wait_for_all();
256*51c0b2f7Stbbdev 
257*51c0b2f7Stbbdev             // check that we got Count values in each output queue, and all the index values
258*51c0b2f7Stbbdev             // are there.
259*51c0b2f7Stbbdev             sink_node_helper<N, SType>::check_sink_values();
260*51c0b2f7Stbbdev 
261*51c0b2f7Stbbdev             sink_node_helper<N, SType>::remove_sink_nodes(*my_split);
262*51c0b2f7Stbbdev             for(int i = 0; i < nInputs; ++i) {
263*51c0b2f7Stbbdev                 delete all_input_nodes[i];
264*51c0b2f7Stbbdev             }
265*51c0b2f7Stbbdev             makeSplit<N,SType>::destroy(my_split);
266*51c0b2f7Stbbdev         }
267*51c0b2f7Stbbdev     }
268*51c0b2f7Stbbdev };
269*51c0b2f7Stbbdev 
270*51c0b2f7Stbbdev //
271*51c0b2f7Stbbdev // Single predecessor, single accepting successor at each port
272*51c0b2f7Stbbdev 
273*51c0b2f7Stbbdev template<typename SType>
274*51c0b2f7Stbbdev void test_one_serial( SType &my_split, tbb::flow::graph &g) {
275*51c0b2f7Stbbdev     typedef typename SType::input_type TType;
276*51c0b2f7Stbbdev     static const int TUPLE_SIZE = std::tuple_size<TType>::value;
277*51c0b2f7Stbbdev     sink_node_helper<TUPLE_SIZE, SType>::add_sink_nodes(my_split,g);
278*51c0b2f7Stbbdev     typedef TType q3_input_type;
279*51c0b2f7Stbbdev     tbb::flow::queue_node< q3_input_type >  q3(g);
280*51c0b2f7Stbbdev 
281*51c0b2f7Stbbdev     tbb::flow::make_edge( q3, my_split );
282*51c0b2f7Stbbdev 
283*51c0b2f7Stbbdev     // fill the  queue with its value one-at-a-time
284*51c0b2f7Stbbdev     flags.clear();
285*51c0b2f7Stbbdev     for (int i = 0; i < Count; ++i ) {
286*51c0b2f7Stbbdev         TType v;
287*51c0b2f7Stbbdev         tuple_helper<TUPLE_SIZE>::set_element(v, i);
288*51c0b2f7Stbbdev         CHECK_MESSAGE(my_split.try_put(v), "");
289*51c0b2f7Stbbdev         flags.push_back(false);
290*51c0b2f7Stbbdev     }
291*51c0b2f7Stbbdev 
292*51c0b2f7Stbbdev     g.wait_for_all();
293*51c0b2f7Stbbdev 
294*51c0b2f7Stbbdev     sink_node_helper<TUPLE_SIZE,SType>::check_sink_values();
295*51c0b2f7Stbbdev 
296*51c0b2f7Stbbdev     sink_node_helper<TUPLE_SIZE, SType>::remove_sink_nodes(my_split);
297*51c0b2f7Stbbdev 
298*51c0b2f7Stbbdev }
299*51c0b2f7Stbbdev 
300*51c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
301*51c0b2f7Stbbdev void test_follows_and_precedes_api() {
302*51c0b2f7Stbbdev     using namespace tbb::flow;
303*51c0b2f7Stbbdev     using msg_t = std::tuple<int, float, double>;
304*51c0b2f7Stbbdev 
305*51c0b2f7Stbbdev     graph g;
306*51c0b2f7Stbbdev 
307*51c0b2f7Stbbdev     function_node<msg_t, msg_t> f1(g, unlimited, [](msg_t msg) { return msg; } );
308*51c0b2f7Stbbdev     auto f2(f1);
309*51c0b2f7Stbbdev     auto f3(f1);
310*51c0b2f7Stbbdev 
311*51c0b2f7Stbbdev     std::atomic<int> body_calls;
312*51c0b2f7Stbbdev     body_calls = 0;
313*51c0b2f7Stbbdev 
314*51c0b2f7Stbbdev     function_node<int, int> f4(g, unlimited, [&](int val) { ++body_calls; return val; } );
315*51c0b2f7Stbbdev     function_node<float, float> f5(g, unlimited, [&](float val) { ++body_calls; return val; } );
316*51c0b2f7Stbbdev     function_node<double, double> f6(g, unlimited, [&](double val) { ++body_calls; return val; } );
317*51c0b2f7Stbbdev 
318*51c0b2f7Stbbdev     split_node<msg_t> following_node(follows(f1, f2, f3));
319*51c0b2f7Stbbdev     make_edge(output_port<0>(following_node), f4);
320*51c0b2f7Stbbdev     make_edge(output_port<1>(following_node), f5);
321*51c0b2f7Stbbdev     make_edge(output_port<2>(following_node), f6);
322*51c0b2f7Stbbdev 
323*51c0b2f7Stbbdev     split_node<msg_t> preceding_node(precedes(f4, f5, f6));
324*51c0b2f7Stbbdev     make_edge(f1, preceding_node);
325*51c0b2f7Stbbdev     make_edge(f2, preceding_node);
326*51c0b2f7Stbbdev     make_edge(f3, preceding_node);
327*51c0b2f7Stbbdev 
328*51c0b2f7Stbbdev     msg_t msg(1, 2.2f, 3.3);
329*51c0b2f7Stbbdev     f1.try_put(msg);
330*51c0b2f7Stbbdev     f2.try_put(msg);
331*51c0b2f7Stbbdev     f3.try_put(msg);
332*51c0b2f7Stbbdev 
333*51c0b2f7Stbbdev     g.wait_for_all();
334*51c0b2f7Stbbdev 
335*51c0b2f7Stbbdev     // <number of try puts> * <number of splits by a input node> * <number of input nodes>
336*51c0b2f7Stbbdev     CHECK_MESSAGE( ((body_calls == 3*3*2)), "Not exact edge quantity was made");
337*51c0b2f7Stbbdev }
338*51c0b2f7Stbbdev #endif // __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
339*51c0b2f7Stbbdev 
340*51c0b2f7Stbbdev template<typename SType>
341*51c0b2f7Stbbdev class serial_test {
342*51c0b2f7Stbbdev     typedef typename SType::input_type TType;
343*51c0b2f7Stbbdev     static const int TUPLE_SIZE = std::tuple_size<TType>::value;
344*51c0b2f7Stbbdev     static const int ELEMS = 3;
345*51c0b2f7Stbbdev public:
346*51c0b2f7Stbbdev static void test() {
347*51c0b2f7Stbbdev     tbb::flow::graph g;
348*51c0b2f7Stbbdev     flags.reserve(Count);
349*51c0b2f7Stbbdev     SType* my_split = makeSplit<TUPLE_SIZE,SType>::create(g);
350*51c0b2f7Stbbdev     sink_node_helper<TUPLE_SIZE, SType>::print_serial_remark(); INFO(" >\n");
351*51c0b2f7Stbbdev 
352*51c0b2f7Stbbdev     test_output_ports_return_ref(*my_split);
353*51c0b2f7Stbbdev 
354*51c0b2f7Stbbdev     test_one_serial<SType>(*my_split, g);
355*51c0b2f7Stbbdev     // build the vector with copy construction from the used split node.
356*51c0b2f7Stbbdev     std::vector<SType>split_vector(ELEMS, *my_split);
357*51c0b2f7Stbbdev     // destroy the tired old split_node in case we're accidentally reusing pieces of it.
358*51c0b2f7Stbbdev     makeSplit<TUPLE_SIZE,SType>::destroy(my_split);
359*51c0b2f7Stbbdev 
360*51c0b2f7Stbbdev 
361*51c0b2f7Stbbdev     for(int e = 0; e < ELEMS; ++e) {  // exercise each of the vector elements
362*51c0b2f7Stbbdev         test_one_serial<SType>(split_vector[e], g);
363*51c0b2f7Stbbdev     }
364*51c0b2f7Stbbdev }
365*51c0b2f7Stbbdev 
366*51c0b2f7Stbbdev }; // serial_test
367*51c0b2f7Stbbdev 
368*51c0b2f7Stbbdev template<
369*51c0b2f7Stbbdev       template<typename> class TestType,  // serial_test or parallel_test
370*51c0b2f7Stbbdev       typename TupleType >                               // type of the input of the split
371*51c0b2f7Stbbdev struct generate_test {
372*51c0b2f7Stbbdev     typedef tbb::flow::split_node<TupleType> split_node_type;
373*51c0b2f7Stbbdev     static void do_test() {
374*51c0b2f7Stbbdev         TestType<split_node_type>::test();
375*51c0b2f7Stbbdev     }
376*51c0b2f7Stbbdev }; // generate_test
377*51c0b2f7Stbbdev 
378*51c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
379*51c0b2f7Stbbdev 
380*51c0b2f7Stbbdev void test_deduction_guides() {
381*51c0b2f7Stbbdev     using namespace tbb::flow;
382*51c0b2f7Stbbdev     using tuple_type = std::tuple<int, int>;
383*51c0b2f7Stbbdev 
384*51c0b2f7Stbbdev     graph g;
385*51c0b2f7Stbbdev     split_node<tuple_type> s0(g);
386*51c0b2f7Stbbdev 
387*51c0b2f7Stbbdev     split_node s1(s0);
388*51c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(s1), split_node<tuple_type>>);
389*51c0b2f7Stbbdev 
390*51c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
391*51c0b2f7Stbbdev     broadcast_node<tuple_type> b1(g), b2(g);
392*51c0b2f7Stbbdev     broadcast_node<int> b3(g), b4(g);
393*51c0b2f7Stbbdev 
394*51c0b2f7Stbbdev     split_node s2(follows(b1, b2));
395*51c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(s2), split_node<tuple_type>>);
396*51c0b2f7Stbbdev 
397*51c0b2f7Stbbdev     split_node s3(precedes(b3, b4));
398*51c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(s3), split_node<tuple_type>>);
399*51c0b2f7Stbbdev #endif
400*51c0b2f7Stbbdev }
401*51c0b2f7Stbbdev 
402*51c0b2f7Stbbdev #endif
403*51c0b2f7Stbbdev 
404*51c0b2f7Stbbdev //! Test output ports and message passing with different input tuples
405*51c0b2f7Stbbdev //! \brief \ref requirement \ref error_guessing
406*51c0b2f7Stbbdev TEST_CASE("Tuple tests"){
407*51c0b2f7Stbbdev     for (int p = 0; p < 2; ++p) {
408*51c0b2f7Stbbdev         generate_test<serial_test, std::tuple<float, double> >::do_test();
409*51c0b2f7Stbbdev #if MAX_TUPLE_TEST_SIZE >= 4
410*51c0b2f7Stbbdev         generate_test<serial_test, std::tuple<float, double, int, long> >::do_test();
411*51c0b2f7Stbbdev #endif
412*51c0b2f7Stbbdev #if MAX_TUPLE_TEST_SIZE >= 6
413*51c0b2f7Stbbdev         generate_test<serial_test, std::tuple<double, double, int, long, int, short> >::do_test();
414*51c0b2f7Stbbdev #endif
415*51c0b2f7Stbbdev #if MAX_TUPLE_TEST_SIZE >= 8
416*51c0b2f7Stbbdev         generate_test<serial_test, std::tuple<float, double, double, double, float, int, float, long> >::do_test();
417*51c0b2f7Stbbdev #endif
418*51c0b2f7Stbbdev #if MAX_TUPLE_TEST_SIZE >= 10
419*51c0b2f7Stbbdev         generate_test<serial_test, std::tuple<float, double, int, double, double, float, long, int, float, long> >::do_test();
420*51c0b2f7Stbbdev #endif
421*51c0b2f7Stbbdev         generate_test<parallel_test, std::tuple<float, double> >::do_test();
422*51c0b2f7Stbbdev #if MAX_TUPLE_TEST_SIZE >= 3
423*51c0b2f7Stbbdev         generate_test<parallel_test, std::tuple<float, int, long> >::do_test();
424*51c0b2f7Stbbdev #endif
425*51c0b2f7Stbbdev #if MAX_TUPLE_TEST_SIZE >= 5
426*51c0b2f7Stbbdev         generate_test<parallel_test, std::tuple<double, double, int, int, short> >::do_test();
427*51c0b2f7Stbbdev #endif
428*51c0b2f7Stbbdev #if MAX_TUPLE_TEST_SIZE >= 7
429*51c0b2f7Stbbdev         generate_test<parallel_test, std::tuple<float, int, double, float, long, float, long> >::do_test();
430*51c0b2f7Stbbdev #endif
431*51c0b2f7Stbbdev #if MAX_TUPLE_TEST_SIZE >= 9
432*51c0b2f7Stbbdev         generate_test<parallel_test, std::tuple<float, double, int, double, double, long, int, float, long> >::do_test();
433*51c0b2f7Stbbdev #endif
434*51c0b2f7Stbbdev     }
435*51c0b2f7Stbbdev }
436*51c0b2f7Stbbdev 
437*51c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
438*51c0b2f7Stbbdev //! Test decution guides
439*51c0b2f7Stbbdev //! \brief \ref requirement
440*51c0b2f7Stbbdev TEST_CASE("Test follows and precedes API"){
441*51c0b2f7Stbbdev     test_follows_and_precedes_api();
442*51c0b2f7Stbbdev }
443*51c0b2f7Stbbdev #endif
444*51c0b2f7Stbbdev 
445*51c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
446*51c0b2f7Stbbdev //! Test decution guides
447*51c0b2f7Stbbdev //! \brief \ref requirement
448*51c0b2f7Stbbdev TEST_CASE("Deduction guides"){
449*51c0b2f7Stbbdev     test_deduction_guides();
450*51c0b2f7Stbbdev }
451*51c0b2f7Stbbdev #endif
452*51c0b2f7Stbbdev 
453