xref: /oneTBB/test/tbb/test_split_node.cpp (revision b15aabb3)
151c0b2f7Stbbdev /*
2*b15aabb3Stbbdev     Copyright (c) 2005-2021 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 
1751c0b2f7Stbbdev #include "common/config.h"
1851c0b2f7Stbbdev 
1951c0b2f7Stbbdev // TODO revamp: move parts dependent on __TBB_EXTRA_DEBUG into separate test(s) since having these
2051c0b2f7Stbbdev // parts in all of tests might make testing of the product, which is different from what is actually
2151c0b2f7Stbbdev // released.
2251c0b2f7Stbbdev #define __TBB_EXTRA_DEBUG 1
2351c0b2f7Stbbdev #include "tbb/flow_graph.h"
2451c0b2f7Stbbdev 
2551c0b2f7Stbbdev #include "common/test.h"
2651c0b2f7Stbbdev #include "common/utils.h"
2751c0b2f7Stbbdev #include "common/utils_assert.h"
2851c0b2f7Stbbdev #include "common/graph_utils.h"
2951c0b2f7Stbbdev 
3051c0b2f7Stbbdev 
3151c0b2f7Stbbdev //! \file test_split_node.cpp
3251c0b2f7Stbbdev //! \brief Test for [flow_graph.split_node] specification
3351c0b2f7Stbbdev 
3451c0b2f7Stbbdev 
3551c0b2f7Stbbdev #if defined(_MSC_VER) && _MSC_VER < 1600
3651c0b2f7Stbbdev     #pragma warning (disable : 4503) //disabling the "decorated name length exceeded" warning for VS2008 and earlier
3751c0b2f7Stbbdev #endif
3851c0b2f7Stbbdev 
3951c0b2f7Stbbdev //
4051c0b2f7Stbbdev // Tests
4151c0b2f7Stbbdev //
4251c0b2f7Stbbdev 
4351c0b2f7Stbbdev const int Count = 300;
4451c0b2f7Stbbdev const int MaxPorts = 10;
4551c0b2f7Stbbdev const int MaxNInputs = 5; // max # of input_nodes to register for each split_node input in parallel test
4651c0b2f7Stbbdev 
4751c0b2f7Stbbdev std::vector<bool> flags;   // for checking output
4851c0b2f7Stbbdev 
4951c0b2f7Stbbdev template<typename T>
5051c0b2f7Stbbdev class name_of {
5151c0b2f7Stbbdev public:
5251c0b2f7Stbbdev     static const char* name() { return  "Unknown"; }
5351c0b2f7Stbbdev };
5451c0b2f7Stbbdev template<>
5551c0b2f7Stbbdev class name_of<int> {
5651c0b2f7Stbbdev public:
5751c0b2f7Stbbdev     static const char* name() { return  "int"; }
5851c0b2f7Stbbdev };
5951c0b2f7Stbbdev template<>
6051c0b2f7Stbbdev class name_of<float> {
6151c0b2f7Stbbdev public:
6251c0b2f7Stbbdev     static const char* name() { return  "float"; }
6351c0b2f7Stbbdev };
6451c0b2f7Stbbdev template<>
6551c0b2f7Stbbdev class name_of<double> {
6651c0b2f7Stbbdev public:
6751c0b2f7Stbbdev     static const char* name() { return  "double"; }
6851c0b2f7Stbbdev };
6951c0b2f7Stbbdev template<>
7051c0b2f7Stbbdev class name_of<long> {
7151c0b2f7Stbbdev public:
7251c0b2f7Stbbdev     static const char* name() { return  "long"; }
7351c0b2f7Stbbdev };
7451c0b2f7Stbbdev template<>
7551c0b2f7Stbbdev class name_of<short> {
7651c0b2f7Stbbdev public:
7751c0b2f7Stbbdev     static const char* name() { return  "short"; }
7851c0b2f7Stbbdev };
7951c0b2f7Stbbdev 
8051c0b2f7Stbbdev // T must be arithmetic, and shouldn't wrap around for reasonable sizes of Count (which is now 150, and maxPorts is 10,
8151c0b2f7Stbbdev // so the max number generated right now is 1500 or so.)  Input will generate a series of TT with value
8251c0b2f7Stbbdev // (init_val + (i-1)*addend) * my_mult, where i is the i-th invocation of the body.  We are attaching addend
8351c0b2f7Stbbdev // input nodes to a join_port, and each will generate part of the numerical series the port is expecting
8451c0b2f7Stbbdev // to receive.  If there is only one input node, the series order will be maintained; if more than one,
8551c0b2f7Stbbdev // this is not guaranteed.
8651c0b2f7Stbbdev 
8751c0b2f7Stbbdev template<int N>
8851c0b2f7Stbbdev struct tuple_helper {
8951c0b2f7Stbbdev     template<typename TupleType>
9051c0b2f7Stbbdev     static void set_element( TupleType &t, int i) {
9151c0b2f7Stbbdev         std::get<N-1>(t) = (typename std::tuple_element<N-1,TupleType>::type)(i * (N+1));
9251c0b2f7Stbbdev         tuple_helper<N-1>::set_element(t, i);
9351c0b2f7Stbbdev     }
9451c0b2f7Stbbdev };
9551c0b2f7Stbbdev 
9651c0b2f7Stbbdev template<>
9751c0b2f7Stbbdev struct tuple_helper<1> {
9851c0b2f7Stbbdev     template<typename TupleType>
9951c0b2f7Stbbdev     static void set_element(TupleType &t, int i) {
10051c0b2f7Stbbdev         std::get<0>(t) = (typename std::tuple_element<0,TupleType>::type)(i * 2);
10151c0b2f7Stbbdev     }
10251c0b2f7Stbbdev };
10351c0b2f7Stbbdev 
10451c0b2f7Stbbdev // if we start N input_bodys they will all have the addend N, and my_count should be initialized to 0 .. N-1.
10551c0b2f7Stbbdev // the output tuples should have all the sequence, but the order will in general vary.
10651c0b2f7Stbbdev template<typename TupleType>
10751c0b2f7Stbbdev class my_input_body {
10851c0b2f7Stbbdev     typedef TupleType TT;
10951c0b2f7Stbbdev     static const int N = std::tuple_size<TT>::value;
11051c0b2f7Stbbdev     int my_count;
11151c0b2f7Stbbdev     int addend;
11251c0b2f7Stbbdev public:
11351c0b2f7Stbbdev     my_input_body(int init_val, int addto) : my_count(init_val), addend(addto) { }
11451c0b2f7Stbbdev     TT operator()( tbb::flow_control &fc) {
11551c0b2f7Stbbdev         if(my_count >= Count){
11651c0b2f7Stbbdev             fc.stop();
11751c0b2f7Stbbdev             return TT();
11851c0b2f7Stbbdev         }
11951c0b2f7Stbbdev         TT v;
12051c0b2f7Stbbdev         tuple_helper<N>::set_element(v, my_count);
12151c0b2f7Stbbdev         my_count += addend;
12251c0b2f7Stbbdev         return v;
12351c0b2f7Stbbdev     }
12451c0b2f7Stbbdev };
12551c0b2f7Stbbdev 
12651c0b2f7Stbbdev // allocator for split_node.
12751c0b2f7Stbbdev 
12851c0b2f7Stbbdev template<int N, typename SType>
12951c0b2f7Stbbdev class makeSplit {
13051c0b2f7Stbbdev public:
13151c0b2f7Stbbdev     static SType *create(tbb::flow::graph& g) {
13251c0b2f7Stbbdev         SType *temp = new SType(g);
13351c0b2f7Stbbdev         return temp;
13451c0b2f7Stbbdev     }
13551c0b2f7Stbbdev     static void destroy(SType *p) { delete p; }
13651c0b2f7Stbbdev };
13751c0b2f7Stbbdev 
13851c0b2f7Stbbdev // holder for sink_node pointers for eventual deletion
13951c0b2f7Stbbdev 
14051c0b2f7Stbbdev static void* all_sink_nodes[MaxPorts];
14151c0b2f7Stbbdev 
14251c0b2f7Stbbdev 
14351c0b2f7Stbbdev template<int ELEM, typename SType>
14451c0b2f7Stbbdev class sink_node_helper {
14551c0b2f7Stbbdev public:
14651c0b2f7Stbbdev     typedef typename SType::input_type TT;
14751c0b2f7Stbbdev     typedef typename std::tuple_element<ELEM-1,TT>::type IT;
14851c0b2f7Stbbdev     typedef typename tbb::flow::queue_node<IT> my_sink_node_type;
14951c0b2f7Stbbdev     static void print_parallel_remark() {
15051c0b2f7Stbbdev         sink_node_helper<ELEM-1,SType>::print_parallel_remark();
15151c0b2f7Stbbdev         INFO(", " << name_of<IT>::name());
15251c0b2f7Stbbdev     }
15351c0b2f7Stbbdev     static void print_serial_remark() {
15451c0b2f7Stbbdev         sink_node_helper<ELEM-1,SType>::print_serial_remark();
15551c0b2f7Stbbdev         INFO(", " << name_of<IT>::name());
15651c0b2f7Stbbdev     }
15751c0b2f7Stbbdev     static void add_sink_nodes(SType &my_split, tbb::flow::graph &g) {
15851c0b2f7Stbbdev         my_sink_node_type *new_node = new my_sink_node_type(g);
15951c0b2f7Stbbdev         tbb::flow::make_edge( tbb::flow::output_port<ELEM-1>(my_split) , *new_node);
16051c0b2f7Stbbdev         all_sink_nodes[ELEM-1] = (void *)new_node;
16151c0b2f7Stbbdev         sink_node_helper<ELEM-1, SType>::add_sink_nodes(my_split, g);
16251c0b2f7Stbbdev     }
16351c0b2f7Stbbdev 
16451c0b2f7Stbbdev     static void check_sink_values() {
16551c0b2f7Stbbdev         my_sink_node_type *dp = reinterpret_cast<my_sink_node_type *>(all_sink_nodes[ELEM-1]);
16651c0b2f7Stbbdev         for(int i = 0; i < Count; ++i) {
16751c0b2f7Stbbdev             IT v{};
16851c0b2f7Stbbdev             CHECK_MESSAGE(dp->try_get(v), "");
16951c0b2f7Stbbdev             flags[((int)v) / (ELEM+1)] = true;
17051c0b2f7Stbbdev         }
17151c0b2f7Stbbdev         for(int i = 0; i < Count; ++i) {
17251c0b2f7Stbbdev             CHECK_MESSAGE(flags[i], "");
17351c0b2f7Stbbdev             flags[i] = false;  // reset for next test
17451c0b2f7Stbbdev         }
17551c0b2f7Stbbdev         sink_node_helper<ELEM-1,SType>::check_sink_values();
17651c0b2f7Stbbdev     }
17751c0b2f7Stbbdev     static void remove_sink_nodes(SType& my_split) {
17851c0b2f7Stbbdev         my_sink_node_type *dp = reinterpret_cast<my_sink_node_type *>(all_sink_nodes[ELEM-1]);
17951c0b2f7Stbbdev         tbb::flow::remove_edge( tbb::flow::output_port<ELEM-1>(my_split) , *dp);
18051c0b2f7Stbbdev         delete dp;
18151c0b2f7Stbbdev         sink_node_helper<ELEM-1, SType>::remove_sink_nodes(my_split);
18251c0b2f7Stbbdev     }
18351c0b2f7Stbbdev };
18451c0b2f7Stbbdev 
18551c0b2f7Stbbdev template<typename SType>
18651c0b2f7Stbbdev class sink_node_helper<1, SType> {
18751c0b2f7Stbbdev     typedef typename SType::input_type TT;
18851c0b2f7Stbbdev     typedef typename std::tuple_element<0,TT>::type IT;
18951c0b2f7Stbbdev     typedef typename tbb::flow::queue_node<IT> my_sink_node_type;
19051c0b2f7Stbbdev public:
19151c0b2f7Stbbdev     static void print_parallel_remark() {
19251c0b2f7Stbbdev         INFO("Parallel test of split_node< " << name_of<IT>::name());
19351c0b2f7Stbbdev     }
19451c0b2f7Stbbdev     static void print_serial_remark() {
19551c0b2f7Stbbdev         INFO("Serial test of split_node< " << name_of<IT>::name());
19651c0b2f7Stbbdev     }
19751c0b2f7Stbbdev     static void add_sink_nodes(SType &my_split, tbb::flow::graph &g) {
19851c0b2f7Stbbdev         my_sink_node_type *new_node = new my_sink_node_type(g);
19951c0b2f7Stbbdev         tbb::flow::make_edge( tbb::flow::output_port<0>(my_split) , *new_node);
20051c0b2f7Stbbdev         all_sink_nodes[0] = (void *)new_node;
20151c0b2f7Stbbdev     }
20251c0b2f7Stbbdev     static void check_sink_values() {
20351c0b2f7Stbbdev         my_sink_node_type *dp = reinterpret_cast<my_sink_node_type *>(all_sink_nodes[0]);
20451c0b2f7Stbbdev         for(int i = 0; i < Count; ++i) {
20551c0b2f7Stbbdev             IT v{};
20651c0b2f7Stbbdev             CHECK_MESSAGE(dp->try_get(v), "");
20751c0b2f7Stbbdev             flags[((int)v) / 2] = true;
20851c0b2f7Stbbdev         }
20951c0b2f7Stbbdev         for(int i = 0; i < Count; ++i) {
21051c0b2f7Stbbdev             CHECK_MESSAGE(flags[i], "");
21151c0b2f7Stbbdev             flags[i] = false;  // reset for next test
21251c0b2f7Stbbdev         }
21351c0b2f7Stbbdev     }
21451c0b2f7Stbbdev     static void remove_sink_nodes(SType& my_split) {
21551c0b2f7Stbbdev         my_sink_node_type *dp = reinterpret_cast<my_sink_node_type *>(all_sink_nodes[0]);
21651c0b2f7Stbbdev         tbb::flow::remove_edge( tbb::flow::output_port<0>(my_split) , *dp);
21751c0b2f7Stbbdev         delete dp;
21851c0b2f7Stbbdev     }
21951c0b2f7Stbbdev };
22051c0b2f7Stbbdev 
22151c0b2f7Stbbdev // parallel_test: create input_nodes that feed tuples into the split node
22251c0b2f7Stbbdev //    and queue_nodes that receive the output.
22351c0b2f7Stbbdev template<typename SType>
22451c0b2f7Stbbdev class parallel_test {
22551c0b2f7Stbbdev public:
22651c0b2f7Stbbdev     typedef typename SType::input_type TType;
22751c0b2f7Stbbdev     typedef tbb::flow::input_node<TType> input_type;
22851c0b2f7Stbbdev     static const int N = std::tuple_size<TType>::value;
22951c0b2f7Stbbdev 
23051c0b2f7Stbbdev     static void test() {
23151c0b2f7Stbbdev         input_type* all_input_nodes[MaxNInputs];
23251c0b2f7Stbbdev         sink_node_helper<N,SType>::print_parallel_remark();
23351c0b2f7Stbbdev         INFO(" >\n");
23451c0b2f7Stbbdev         for(int i=0; i < MaxPorts; ++i) {
23551c0b2f7Stbbdev             all_sink_nodes[i] = NULL;
23651c0b2f7Stbbdev         }
23751c0b2f7Stbbdev         // try test for # inputs 1 .. MaxNInputs
23851c0b2f7Stbbdev         for(int nInputs = 1; nInputs <= MaxNInputs; ++nInputs) {
23951c0b2f7Stbbdev             tbb::flow::graph g;
24051c0b2f7Stbbdev             SType* my_split = makeSplit<N,SType>::create(g);
24151c0b2f7Stbbdev 
24251c0b2f7Stbbdev             // add sinks first so when inputs start spitting out values they are there to catch them
24351c0b2f7Stbbdev             sink_node_helper<N, SType>::add_sink_nodes((*my_split), g);
24451c0b2f7Stbbdev 
24551c0b2f7Stbbdev             // now create nInputs input_nodes, each spitting out i, i+nInputs, i+2*nInputs ...
24651c0b2f7Stbbdev             // each element of the tuple is i*(n+1), where n is the tuple element index (1-N)
24751c0b2f7Stbbdev             for(int i = 0; i < nInputs; ++i) {
24851c0b2f7Stbbdev                 // create input node
24951c0b2f7Stbbdev                 input_type *s = new input_type(g, my_input_body<TType>(i, nInputs) );
25051c0b2f7Stbbdev                 tbb::flow::make_edge(*s, *my_split);
25151c0b2f7Stbbdev                 all_input_nodes[i] = s;
25251c0b2f7Stbbdev                 s->activate();
25351c0b2f7Stbbdev             }
25451c0b2f7Stbbdev 
25551c0b2f7Stbbdev             g.wait_for_all();
25651c0b2f7Stbbdev 
25751c0b2f7Stbbdev             // check that we got Count values in each output queue, and all the index values
25851c0b2f7Stbbdev             // are there.
25951c0b2f7Stbbdev             sink_node_helper<N, SType>::check_sink_values();
26051c0b2f7Stbbdev 
26151c0b2f7Stbbdev             sink_node_helper<N, SType>::remove_sink_nodes(*my_split);
26251c0b2f7Stbbdev             for(int i = 0; i < nInputs; ++i) {
26351c0b2f7Stbbdev                 delete all_input_nodes[i];
26451c0b2f7Stbbdev             }
26551c0b2f7Stbbdev             makeSplit<N,SType>::destroy(my_split);
26651c0b2f7Stbbdev         }
26751c0b2f7Stbbdev     }
26851c0b2f7Stbbdev };
26951c0b2f7Stbbdev 
27051c0b2f7Stbbdev //
27151c0b2f7Stbbdev // Single predecessor, single accepting successor at each port
27251c0b2f7Stbbdev 
27351c0b2f7Stbbdev template<typename SType>
27451c0b2f7Stbbdev void test_one_serial( SType &my_split, tbb::flow::graph &g) {
27551c0b2f7Stbbdev     typedef typename SType::input_type TType;
27651c0b2f7Stbbdev     static const int TUPLE_SIZE = std::tuple_size<TType>::value;
27751c0b2f7Stbbdev     sink_node_helper<TUPLE_SIZE, SType>::add_sink_nodes(my_split,g);
27851c0b2f7Stbbdev     typedef TType q3_input_type;
27951c0b2f7Stbbdev     tbb::flow::queue_node< q3_input_type >  q3(g);
28051c0b2f7Stbbdev 
28151c0b2f7Stbbdev     tbb::flow::make_edge( q3, my_split );
28251c0b2f7Stbbdev 
28351c0b2f7Stbbdev     // fill the  queue with its value one-at-a-time
28451c0b2f7Stbbdev     flags.clear();
28551c0b2f7Stbbdev     for (int i = 0; i < Count; ++i ) {
28651c0b2f7Stbbdev         TType v;
28751c0b2f7Stbbdev         tuple_helper<TUPLE_SIZE>::set_element(v, i);
28851c0b2f7Stbbdev         CHECK_MESSAGE(my_split.try_put(v), "");
28951c0b2f7Stbbdev         flags.push_back(false);
29051c0b2f7Stbbdev     }
29151c0b2f7Stbbdev 
29251c0b2f7Stbbdev     g.wait_for_all();
29351c0b2f7Stbbdev 
29451c0b2f7Stbbdev     sink_node_helper<TUPLE_SIZE,SType>::check_sink_values();
29551c0b2f7Stbbdev 
29651c0b2f7Stbbdev     sink_node_helper<TUPLE_SIZE, SType>::remove_sink_nodes(my_split);
29751c0b2f7Stbbdev 
29851c0b2f7Stbbdev }
29951c0b2f7Stbbdev 
30051c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
30151c0b2f7Stbbdev void test_follows_and_precedes_api() {
30251c0b2f7Stbbdev     using namespace tbb::flow;
30351c0b2f7Stbbdev     using msg_t = std::tuple<int, float, double>;
30451c0b2f7Stbbdev 
30551c0b2f7Stbbdev     graph g;
30651c0b2f7Stbbdev 
30751c0b2f7Stbbdev     function_node<msg_t, msg_t> f1(g, unlimited, [](msg_t msg) { return msg; } );
30851c0b2f7Stbbdev     auto f2(f1);
30951c0b2f7Stbbdev     auto f3(f1);
31051c0b2f7Stbbdev 
31151c0b2f7Stbbdev     std::atomic<int> body_calls;
31251c0b2f7Stbbdev     body_calls = 0;
31351c0b2f7Stbbdev 
31451c0b2f7Stbbdev     function_node<int, int> f4(g, unlimited, [&](int val) { ++body_calls; return val; } );
31551c0b2f7Stbbdev     function_node<float, float> f5(g, unlimited, [&](float val) { ++body_calls; return val; } );
31651c0b2f7Stbbdev     function_node<double, double> f6(g, unlimited, [&](double val) { ++body_calls; return val; } );
31751c0b2f7Stbbdev 
31851c0b2f7Stbbdev     split_node<msg_t> following_node(follows(f1, f2, f3));
31951c0b2f7Stbbdev     make_edge(output_port<0>(following_node), f4);
32051c0b2f7Stbbdev     make_edge(output_port<1>(following_node), f5);
32151c0b2f7Stbbdev     make_edge(output_port<2>(following_node), f6);
32251c0b2f7Stbbdev 
32351c0b2f7Stbbdev     split_node<msg_t> preceding_node(precedes(f4, f5, f6));
32451c0b2f7Stbbdev     make_edge(f1, preceding_node);
32551c0b2f7Stbbdev     make_edge(f2, preceding_node);
32651c0b2f7Stbbdev     make_edge(f3, preceding_node);
32751c0b2f7Stbbdev 
32851c0b2f7Stbbdev     msg_t msg(1, 2.2f, 3.3);
32951c0b2f7Stbbdev     f1.try_put(msg);
33051c0b2f7Stbbdev     f2.try_put(msg);
33151c0b2f7Stbbdev     f3.try_put(msg);
33251c0b2f7Stbbdev 
33351c0b2f7Stbbdev     g.wait_for_all();
33451c0b2f7Stbbdev 
33551c0b2f7Stbbdev     // <number of try puts> * <number of splits by a input node> * <number of input nodes>
33651c0b2f7Stbbdev     CHECK_MESSAGE( ((body_calls == 3*3*2)), "Not exact edge quantity was made");
33751c0b2f7Stbbdev }
33851c0b2f7Stbbdev #endif // __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
33951c0b2f7Stbbdev 
34051c0b2f7Stbbdev template<typename SType>
34151c0b2f7Stbbdev class serial_test {
34251c0b2f7Stbbdev     typedef typename SType::input_type TType;
34351c0b2f7Stbbdev     static const int TUPLE_SIZE = std::tuple_size<TType>::value;
34451c0b2f7Stbbdev     static const int ELEMS = 3;
34551c0b2f7Stbbdev public:
34651c0b2f7Stbbdev static void test() {
34751c0b2f7Stbbdev     tbb::flow::graph g;
34851c0b2f7Stbbdev     flags.reserve(Count);
34951c0b2f7Stbbdev     SType* my_split = makeSplit<TUPLE_SIZE,SType>::create(g);
35051c0b2f7Stbbdev     sink_node_helper<TUPLE_SIZE, SType>::print_serial_remark(); INFO(" >\n");
35151c0b2f7Stbbdev 
35251c0b2f7Stbbdev     test_output_ports_return_ref(*my_split);
35351c0b2f7Stbbdev 
35451c0b2f7Stbbdev     test_one_serial<SType>(*my_split, g);
35551c0b2f7Stbbdev     // build the vector with copy construction from the used split node.
35651c0b2f7Stbbdev     std::vector<SType>split_vector(ELEMS, *my_split);
35751c0b2f7Stbbdev     // destroy the tired old split_node in case we're accidentally reusing pieces of it.
35851c0b2f7Stbbdev     makeSplit<TUPLE_SIZE,SType>::destroy(my_split);
35951c0b2f7Stbbdev 
36051c0b2f7Stbbdev 
36151c0b2f7Stbbdev     for(int e = 0; e < ELEMS; ++e) {  // exercise each of the vector elements
36251c0b2f7Stbbdev         test_one_serial<SType>(split_vector[e], g);
36351c0b2f7Stbbdev     }
36451c0b2f7Stbbdev }
36551c0b2f7Stbbdev 
36651c0b2f7Stbbdev }; // serial_test
36751c0b2f7Stbbdev 
36851c0b2f7Stbbdev template<
36951c0b2f7Stbbdev       template<typename> class TestType,  // serial_test or parallel_test
37051c0b2f7Stbbdev       typename TupleType >                               // type of the input of the split
37151c0b2f7Stbbdev struct generate_test {
37251c0b2f7Stbbdev     typedef tbb::flow::split_node<TupleType> split_node_type;
37351c0b2f7Stbbdev     static void do_test() {
37451c0b2f7Stbbdev         TestType<split_node_type>::test();
37551c0b2f7Stbbdev     }
37651c0b2f7Stbbdev }; // generate_test
37751c0b2f7Stbbdev 
37851c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
37951c0b2f7Stbbdev 
38051c0b2f7Stbbdev void test_deduction_guides() {
38151c0b2f7Stbbdev     using namespace tbb::flow;
38251c0b2f7Stbbdev     using tuple_type = std::tuple<int, int>;
38351c0b2f7Stbbdev 
38451c0b2f7Stbbdev     graph g;
38551c0b2f7Stbbdev     split_node<tuple_type> s0(g);
38651c0b2f7Stbbdev 
38751c0b2f7Stbbdev     split_node s1(s0);
38851c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(s1), split_node<tuple_type>>);
38951c0b2f7Stbbdev 
39051c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
39151c0b2f7Stbbdev     broadcast_node<tuple_type> b1(g), b2(g);
39251c0b2f7Stbbdev     broadcast_node<int> b3(g), b4(g);
39351c0b2f7Stbbdev 
39451c0b2f7Stbbdev     split_node s2(follows(b1, b2));
39551c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(s2), split_node<tuple_type>>);
39651c0b2f7Stbbdev 
39751c0b2f7Stbbdev     split_node s3(precedes(b3, b4));
39851c0b2f7Stbbdev     static_assert(std::is_same_v<decltype(s3), split_node<tuple_type>>);
39951c0b2f7Stbbdev #endif
40051c0b2f7Stbbdev }
40151c0b2f7Stbbdev 
40251c0b2f7Stbbdev #endif
40351c0b2f7Stbbdev 
40451c0b2f7Stbbdev //! Test output ports and message passing with different input tuples
40551c0b2f7Stbbdev //! \brief \ref requirement \ref error_guessing
40651c0b2f7Stbbdev TEST_CASE("Tuple tests"){
40751c0b2f7Stbbdev     for (int p = 0; p < 2; ++p) {
40851c0b2f7Stbbdev         generate_test<serial_test, std::tuple<float, double> >::do_test();
40951c0b2f7Stbbdev #if MAX_TUPLE_TEST_SIZE >= 4
41051c0b2f7Stbbdev         generate_test<serial_test, std::tuple<float, double, int, long> >::do_test();
41151c0b2f7Stbbdev #endif
41251c0b2f7Stbbdev #if MAX_TUPLE_TEST_SIZE >= 6
41351c0b2f7Stbbdev         generate_test<serial_test, std::tuple<double, double, int, long, int, short> >::do_test();
41451c0b2f7Stbbdev #endif
41551c0b2f7Stbbdev #if MAX_TUPLE_TEST_SIZE >= 8
41651c0b2f7Stbbdev         generate_test<serial_test, std::tuple<float, double, double, double, float, int, float, long> >::do_test();
41751c0b2f7Stbbdev #endif
41851c0b2f7Stbbdev #if MAX_TUPLE_TEST_SIZE >= 10
41951c0b2f7Stbbdev         generate_test<serial_test, std::tuple<float, double, int, double, double, float, long, int, float, long> >::do_test();
42051c0b2f7Stbbdev #endif
42151c0b2f7Stbbdev         generate_test<parallel_test, std::tuple<float, double> >::do_test();
42251c0b2f7Stbbdev #if MAX_TUPLE_TEST_SIZE >= 3
42351c0b2f7Stbbdev         generate_test<parallel_test, std::tuple<float, int, long> >::do_test();
42451c0b2f7Stbbdev #endif
42551c0b2f7Stbbdev #if MAX_TUPLE_TEST_SIZE >= 5
42651c0b2f7Stbbdev         generate_test<parallel_test, std::tuple<double, double, int, int, short> >::do_test();
42751c0b2f7Stbbdev #endif
42851c0b2f7Stbbdev #if MAX_TUPLE_TEST_SIZE >= 7
42951c0b2f7Stbbdev         generate_test<parallel_test, std::tuple<float, int, double, float, long, float, long> >::do_test();
43051c0b2f7Stbbdev #endif
43151c0b2f7Stbbdev #if MAX_TUPLE_TEST_SIZE >= 9
43251c0b2f7Stbbdev         generate_test<parallel_test, std::tuple<float, double, int, double, double, long, int, float, long> >::do_test();
43351c0b2f7Stbbdev #endif
43451c0b2f7Stbbdev     }
43551c0b2f7Stbbdev }
43651c0b2f7Stbbdev 
43751c0b2f7Stbbdev #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
43851c0b2f7Stbbdev //! Test decution guides
43951c0b2f7Stbbdev //! \brief \ref requirement
44051c0b2f7Stbbdev TEST_CASE("Test follows and precedes API"){
44151c0b2f7Stbbdev     test_follows_and_precedes_api();
44251c0b2f7Stbbdev }
44351c0b2f7Stbbdev #endif
44451c0b2f7Stbbdev 
44551c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
44651c0b2f7Stbbdev //! Test decution guides
44751c0b2f7Stbbdev //! \brief \ref requirement
44851c0b2f7Stbbdev TEST_CASE("Deduction guides"){
44951c0b2f7Stbbdev     test_deduction_guides();
45051c0b2f7Stbbdev }
45151c0b2f7Stbbdev #endif
45251c0b2f7Stbbdev 
453