xref: /oneTBB/test/tbb/test_indexer_node.cpp (revision cd6a5f9f)
1 /*
2     Copyright (c) 2005-2021 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 #if __INTEL_COMPILER && _MSC_VER
18 #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
19 #endif
20 
21 #include "common/config.h"
22 
23 #include "tbb/flow_graph.h"
24 
25 #include "common/test.h"
26 #include "common/utils.h"
27 #include "common/utils_assert.h"
28 #include "common/test_follows_and_precedes_api.h"
29 
30 
31 //! \file test_indexer_node.cpp
32 //! \brief Test for [flow_graph.indexer_node] specification
33 
34 
35 #if defined(_MSC_VER) && _MSC_VER < 1600
36     #pragma warning (disable : 4503) //disabling the "decorated name length exceeded" warning for VS2008 and earlier
37 #endif
38 
39 const int Count = 150;
40 const int MaxPorts = 10;
41 const int MaxNInputs = 5; // max # of input_nodes to register for each indexer_node input in parallel test
42 bool outputCheck[MaxPorts][Count];  // for checking output
43 
44 void
45 check_outputCheck( int nUsed, int maxCnt) {
46     for(int i=0; i < nUsed; ++i) {
47         for( int j = 0; j < maxCnt; ++j) {
48             CHECK_MESSAGE(outputCheck[i][j], "");
49         }
50     }
51 }
52 
53 void
54 reset_outputCheck( int nUsed, int maxCnt) {
55     for(int i=0; i < nUsed; ++i) {
56         for( int j = 0; j < maxCnt; ++j) {
57             outputCheck[i][j] = false;
58         }
59     }
60 }
61 
62 class test_class {
63     public:
64         test_class() { my_val = 0; }
65         test_class(int i) { my_val = i; }
66         operator int() { return my_val; }
67     private:
68         int my_val;
69 };
70 
71 template<typename T>
72 class name_of {
73 public:
74     static const char* name() { return  "Unknown"; }
75 };
76 template<>
77 class name_of<int> {
78 public:
79     static const char* name() { return  "int"; }
80 };
81 template<>
82 class name_of<float> {
83 public:
84     static const char* name() { return  "float"; }
85 };
86 template<>
87 class name_of<double> {
88 public:
89     static const char* name() { return  "double"; }
90 };
91 template<>
92 class name_of<long> {
93 public:
94     static const char* name() { return  "long"; }
95 };
96 template<>
97 class name_of<short> {
98 public:
99     static const char* name() { return  "short"; }
100 };
101 template<>
102 class name_of<test_class> {
103 public:
104     static const char* name() { return  "test_class"; }
105 };
106 
107 // TT must be arithmetic, and shouldn't wrap around for reasonable sizes of Count (which is now 150, and maxPorts is 10,
108 // so the max number generated right now is 1500 or so.)  Input will generate a series of TT with value
109 // (init_val + (i-1)*addend) * my_mult, where i is the i-th invocation of the body.  We are attaching addend
110 // input nodes to a indexer_port, and each will generate part of the numerical series the port is expecting
111 // to receive.  If there is only one input node, the series order will be maintained; if more than one,
112 // this is not guaranteed.
113 // The manual specifies bodies can be assigned, so we can't hide the operator=.
114 template<typename TT>
115 class my_input_body {
116     TT my_mult;
117     int my_count;
118     int addend;
119 public:
120     my_input_body(TT multiplier, int init_val, int addto) : my_mult(multiplier), my_count(init_val), addend(addto) { }
121     TT operator()( tbb::flow_control& fc) {
122         int lc = my_count;
123         TT ret = my_mult * (TT)my_count;
124         my_count += addend;
125         if ( lc < Count){
126             return ret;
127         }else{
128             fc.stop();
129             return TT();
130         }
131     }
132 };
133 
134 // allocator for indexer_node.
135 
136 template<typename IType>
137 class makeIndexer {
138 public:
139     static IType *create() {
140         IType *temp = new IType();
141         return temp;
142     }
143     static void destroy(IType *p) { delete p; }
144 };
145 
146 template<int ELEM, typename INT>
147 struct getval_helper {
148 
149     typedef typename INT::output_type OT;
150     typedef typename std::tuple_element<ELEM-1, typename INT::tuple_types>::type stored_type;
151 
152     static int get_integer_val(OT const &o) {
153         stored_type res = tbb::flow::cast_to<stored_type>(o);
154         return (int)res;
155     }
156 };
157 
158 // holder for input_node pointers for eventual deletion
159 
160 static void* all_input_nodes[MaxPorts][MaxNInputs];
161 
162 template<int ELEM, typename INT>
163 class input_node_helper {
164 public:
165     typedef INT indexer_node_type;
166     typedef typename indexer_node_type::output_type TT;
167     typedef typename std::tuple_element<ELEM-1,typename INT::tuple_types>::type IT;
168     typedef typename tbb::flow::input_node<IT> my_input_node_type;
169     static void print_remark() {
170         input_node_helper<ELEM-1,INT>::print_remark();
171         INFO(", " << name_of<IT>::name());
172     }
173     static void add_input_nodes(indexer_node_type &my_indexer, tbb::flow::graph &g, int nInputs) {
174         for(int i=0; i < nInputs; ++i) {
175             my_input_node_type *new_node = new my_input_node_type(g, my_input_body<IT>((IT)(ELEM+1), i, nInputs));
176             tbb::flow::make_edge(*new_node, tbb::flow::input_port<ELEM-1>(my_indexer));
177 
178             all_input_nodes[ELEM-1][i] = (void *)new_node;
179             new_node->activate();
180         }
181 
182         // add the next input_node
183         input_node_helper<ELEM-1, INT>::add_input_nodes(my_indexer, g, nInputs);
184     }
185     static void check_value(TT &v) {
186         if(v.tag() == ELEM-1) {
187             int ival = getval_helper<ELEM,INT>::get_integer_val(v);
188             CHECK_MESSAGE(!(ival%(ELEM+1)), "");
189             ival /= (ELEM+1);
190             CHECK_MESSAGE(!outputCheck[ELEM-1][ival], "");
191             outputCheck[ELEM-1][ival] = true;
192         }
193         else {
194             input_node_helper<ELEM-1,INT>::check_value(v);
195         }
196     }
197 
198     static void remove_input_nodes(indexer_node_type& my_indexer, int nInputs) {
199         for(int i=0; i< nInputs; ++i) {
200             my_input_node_type *dp = reinterpret_cast<my_input_node_type *>(all_input_nodes[ELEM-1][i]);
201             tbb::flow::remove_edge(*dp, tbb::flow::input_port<ELEM-1>(my_indexer));
202             delete dp;
203         }
204         input_node_helper<ELEM-1, INT>::remove_input_nodes(my_indexer, nInputs);
205     }
206 };
207 
208 template<typename INT>
209 class input_node_helper<1, INT> {
210     typedef INT indexer_node_type;
211     typedef typename indexer_node_type::output_type TT;
212 
213     typedef typename std::tuple_element<0, typename INT::tuple_types>::type IT;
214     typedef typename tbb::flow::input_node<IT> my_input_node_type;
215 public:
216     static void print_remark() {
217         INFO("Parallel test of indexer_node< " << name_of<IT>::name());
218     }
219     static void add_input_nodes(indexer_node_type &my_indexer, tbb::flow::graph &g, int nInputs) {
220         for(int i=0; i < nInputs; ++i) {
221             my_input_node_type *new_node = new my_input_node_type(g, my_input_body<IT>((IT)2, i, nInputs));
222             tbb::flow::make_edge(*new_node, tbb::flow::input_port<0>(my_indexer));
223             all_input_nodes[0][i] = (void *)new_node;
224             new_node->activate();
225         }
226     }
227     static void check_value(TT &v) {
228         int ival = getval_helper<1,INT>::get_integer_val(v);
229         CHECK_MESSAGE(!(ival%2), "");
230         ival /= 2;
231         CHECK_MESSAGE(!outputCheck[0][ival], "");
232         outputCheck[0][ival] = true;
233     }
234     static void remove_input_nodes(indexer_node_type& my_indexer, int nInputs) {
235         for(int i=0; i < nInputs; ++i) {
236             my_input_node_type *dp = reinterpret_cast<my_input_node_type *>(all_input_nodes[0][i]);
237             tbb::flow::remove_edge(*dp, tbb::flow::input_port<0>(my_indexer));
238             delete dp;
239         }
240     }
241 };
242 
243 template<typename IType>
244 class parallel_test {
245 public:
246     typedef typename IType::output_type TType;
247     typedef typename IType::tuple_types union_types;
248     static const int SIZE = std::tuple_size<union_types>::value;
249     static void test() {
250         TType v;
251         input_node_helper<SIZE,IType>::print_remark();
252         INFO(" >\n");
253         for(int i=0; i < MaxPorts; ++i) {
254             for(int j=0; j < MaxNInputs; ++j) {
255                 all_input_nodes[i][j] = nullptr;
256             }
257         }
258         for(int nInputs = 1; nInputs <= MaxNInputs; ++nInputs) {
259             tbb::flow::graph g;
260             IType* my_indexer_ptr = new IType(g); //makeIndexer<IType>::create();
261             IType my_indexer = *my_indexer_ptr;
262             tbb::flow::queue_node<TType> outq1(g);
263             tbb::flow::queue_node<TType> outq2(g);
264 
265             tbb::flow::make_edge(my_indexer, outq1);
266             tbb::flow::make_edge(my_indexer, outq2);
267 
268             input_node_helper<SIZE, IType>::add_input_nodes(my_indexer, g, nInputs);
269 
270             g.wait_for_all();
271             makeIndexer<IType>::destroy(my_indexer_ptr);
272 
273             reset_outputCheck(SIZE, Count);
274             for(int i=0; i < Count*SIZE; ++i) {
275                 CHECK_MESSAGE(outq1.try_get(v), "");
276                 input_node_helper<SIZE, IType>::check_value(v);
277             }
278 
279             check_outputCheck(SIZE, Count);
280             reset_outputCheck(SIZE, Count);
281 
282             for(int i=0; i < Count*SIZE; i++) {
283                 CHECK_MESSAGE(outq2.try_get(v), "");;
284                 input_node_helper<SIZE, IType>::check_value(v);
285             }
286             check_outputCheck(SIZE, Count);
287 
288             CHECK_MESSAGE(!outq1.try_get(v), "");
289             CHECK_MESSAGE(!outq2.try_get(v), "");
290 
291             input_node_helper<SIZE, IType>::remove_input_nodes(my_indexer, nInputs);
292             tbb::flow::remove_edge(my_indexer, outq1);
293             tbb::flow::remove_edge(my_indexer, outq2);
294         }
295     }
296 };
297 
298 std::vector<int> last_index_seen;
299 
300 template<int ELEM, typename IType>
301 class serial_queue_helper {
302 public:
303     typedef typename IType::output_type OT;
304     typedef typename IType::tuple_types TT;
305     typedef typename std::tuple_element<ELEM-1,TT>::type IT;
306     static void print_remark() {
307         serial_queue_helper<ELEM-1,IType>::print_remark();
308         INFO("," << name_of<IT>::name());
309     }
310     static void fill_one_queue(int maxVal, IType &my_indexer) {
311         // fill queue to "left" of me
312         serial_queue_helper<ELEM-1,IType>::fill_one_queue(maxVal,my_indexer);
313         for(int i = 0; i < maxVal; ++i) {
314             CHECK_MESSAGE(tbb::flow::input_port<ELEM-1>(my_indexer).try_put((IT)(i*(ELEM+1))), "");
315         }
316     }
317     static void put_one_queue_val(int myVal, IType &my_indexer) {
318         // put this val to my "left".
319         serial_queue_helper<ELEM-1,IType>::put_one_queue_val(myVal, my_indexer);
320         CHECK_MESSAGE(tbb::flow::input_port<ELEM-1>(my_indexer).try_put((IT)(myVal*(ELEM+1))), "");
321     }
322     static void check_queue_value(OT &v) {
323         if(ELEM - 1 == v.tag()) {
324             // this assumes each or node input is queueing.
325             int rval = getval_helper<ELEM,IType>::get_integer_val(v);
326             CHECK_MESSAGE( rval == (last_index_seen[ELEM-1]+1)*(ELEM+1), "");
327             last_index_seen[ELEM-1] = rval / (ELEM+1);
328         }
329         else {
330             serial_queue_helper<ELEM-1,IType>::check_queue_value(v);
331         }
332     }
333 };
334 
335 template<typename IType>
336 class serial_queue_helper<1, IType> {
337 public:
338     typedef typename IType::output_type OT;
339     typedef typename IType::tuple_types TT;
340     typedef typename std::tuple_element<0,TT>::type IT;
341     static void print_remark() {
342         INFO("Serial test of indexer_node< " << name_of<IT>::name());
343     }
344     static void fill_one_queue(int maxVal, IType &my_indexer) {
345         for(int i = 0; i < maxVal; ++i) {
346             CHECK_MESSAGE(tbb::flow::input_port<0>(my_indexer).try_put((IT)(i*2)), "");
347         }
348     }
349     static void put_one_queue_val(int myVal, IType &my_indexer) {
350         CHECK_MESSAGE(tbb::flow::input_port<0>(my_indexer).try_put((IT)(myVal*2)), "");
351     }
352     static void check_queue_value(OT &v) {
353         CHECK_MESSAGE(v.tag() == 0, "");  // won't get here unless true
354         int rval = getval_helper<1,IType>::get_integer_val(v);
355         CHECK_MESSAGE( rval == (last_index_seen[0]+1)*2, "");
356         last_index_seen[0] = rval / 2;
357     }
358 };
359 
360 template<typename IType, typename TType, int SIZE>
361 void test_one_serial( IType &my_indexer, tbb::flow::graph &g) {
362     last_index_seen.clear();
363     for(int ii=0; ii < SIZE; ++ii) last_index_seen.push_back(-1);
364 
365     typedef TType q3_input_type;
366     tbb::flow::queue_node< q3_input_type >  q3(g);
367     q3_input_type v;
368 
369     tbb::flow::make_edge(my_indexer, q3);
370 
371     // fill each queue with its value one-at-a-time
372     for (int i = 0; i < Count; ++i ) {
373         serial_queue_helper<SIZE,IType>::put_one_queue_val(i,my_indexer);
374     }
375 
376     g.wait_for_all();
377     for (int i = 0; i < Count * SIZE; ++i ) {
378         g.wait_for_all();
379         CHECK_MESSAGE( (q3.try_get( v )), "Error in try_get()");
380         {
381             serial_queue_helper<SIZE,IType>::check_queue_value(v);
382         }
383     }
384     CHECK_MESSAGE( (!q3.try_get( v )), "extra values in output queue");
385     for(int ii=0; ii < SIZE; ++ii) last_index_seen[ii] = -1;
386 
387     // fill each queue completely before filling the next.
388     serial_queue_helper<SIZE, IType>::fill_one_queue(Count,my_indexer);
389 
390     g.wait_for_all();
391     for (int i = 0; i < Count*SIZE; ++i ) {
392         g.wait_for_all();
393         CHECK_MESSAGE( (q3.try_get( v )), "Error in try_get()");
394         {
395             serial_queue_helper<SIZE,IType>::check_queue_value(v);
396         }
397     }
398     CHECK_MESSAGE( (!q3.try_get( v )), "extra values in output queue");
399 }
400 
401 //
402 template<typename NodeType>
403 void test_input_ports_return_ref(NodeType& mip_node) {
404     typename NodeType::input_ports_type& input_ports1 = mip_node.input_ports();
405     typename NodeType::input_ports_type& input_ports2 = mip_node.input_ports();
406     CHECK_MESSAGE( (&input_ports1 == &input_ports2), "input_ports() should return reference");
407 }
408 
409 // Single predecessor at each port, single accepting successor
410 //   * put to buffer before port0, then put to buffer before port1, ...
411 //   * fill buffer before port0 then fill buffer before port1, ...
412 
413 template<typename IType>
414 class serial_test {
415     typedef typename IType::output_type TType;  // this is the union
416     typedef typename IType::tuple_types union_types;
417     static const int SIZE = std::tuple_size<union_types>::value;
418 public:
419 static void test() {
420     tbb::flow::graph g;
421     static const int ELEMS = 3;
422     IType* my_indexer = new IType(g); //makeIndexer<IType>::create(g);
423 
424     test_input_ports_return_ref(*my_indexer);
425 
426     serial_queue_helper<SIZE, IType>::print_remark(); INFO(" >\n");
427 
428     test_one_serial<IType,TType,SIZE>(*my_indexer, g);
429 
430     std::vector<IType> indexer_vector(ELEMS,*my_indexer);
431 
432     makeIndexer<IType>::destroy(my_indexer);
433 
434     for(int e = 0; e < ELEMS; ++e) {
435         test_one_serial<IType,TType,SIZE>(indexer_vector[e], g);
436     }
437 }
438 
439 }; // serial_test
440 
441 template<
442       template<typename> class TestType,  // serial_test or parallel_test
443       typename T0, typename T1=void, typename T2=void, typename T3=void, typename T4=void,
444       typename T5=void, typename T6=void, typename T7=void, typename T8=void, typename T9=void> // type of the inputs to the indexer_node
445 class generate_test {
446 public:
447     typedef tbb::flow::indexer_node<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>  indexer_node_type;
448     static void do_test() {
449         TestType<indexer_node_type>::test();
450     }
451 };
452 
453 //specializations for indexer node inputs
454 template<
455       template<typename> class TestType,
456       typename T0, typename T1, typename T2, typename T3, typename T4,
457       typename T5, typename T6, typename T7, typename T8>
458 class generate_test<TestType, T0, T1, T2, T3, T4, T5, T6, T7, T8> {
459 public:
460     typedef tbb::flow::indexer_node<T0, T1, T2, T3, T4, T5, T6, T7, T8>  indexer_node_type;
461     static void do_test() {
462         TestType<indexer_node_type>::test();
463     }
464 };
465 
466 template<
467       template<typename> class TestType,
468       typename T0, typename T1, typename T2, typename T3, typename T4,
469       typename T5, typename T6, typename T7>
470 class generate_test<TestType, T0, T1, T2, T3, T4, T5, T6, T7> {
471 public:
472     typedef tbb::flow::indexer_node<T0, T1, T2, T3, T4, T5, T6, T7>  indexer_node_type;
473     static void do_test() {
474         TestType<indexer_node_type>::test();
475     }
476 };
477 
478 template<
479       template<typename> class TestType,
480       typename T0, typename T1, typename T2, typename T3, typename T4,
481       typename T5, typename T6>
482 class generate_test<TestType, T0, T1, T2, T3, T4, T5, T6> {
483 public:
484     typedef tbb::flow::indexer_node<T0, T1, T2, T3, T4, T5, T6>  indexer_node_type;
485     static void do_test() {
486         TestType<indexer_node_type>::test();
487     }
488 };
489 
490 template<
491       template<typename> class TestType,
492       typename T0, typename T1, typename T2, typename T3, typename T4,
493       typename T5>
494 class generate_test<TestType, T0, T1, T2, T3, T4, T5>  {
495 public:
496     typedef tbb::flow::indexer_node<T0, T1, T2, T3, T4, T5>  indexer_node_type;
497     static void do_test() {
498         TestType<indexer_node_type>::test();
499     }
500 };
501 
502 template<
503       template<typename> class TestType,
504       typename T0, typename T1, typename T2, typename T3, typename T4>
505 class generate_test<TestType, T0, T1, T2, T3, T4>  {
506 public:
507     typedef tbb::flow::indexer_node<T0, T1, T2, T3, T4>  indexer_node_type;
508     static void do_test() {
509         TestType<indexer_node_type>::test();
510     }
511 };
512 
513 template<
514       template<typename> class TestType,
515       typename T0, typename T1, typename T2, typename T3>
516 class generate_test<TestType, T0, T1, T2, T3> {
517 public:
518     typedef tbb::flow::indexer_node<T0, T1, T2, T3>  indexer_node_type;
519     static void do_test() {
520         TestType<indexer_node_type>::test();
521     }
522 };
523 
524 template<
525       template<typename> class TestType,
526       typename T0, typename T1, typename T2>
527 class generate_test<TestType, T0, T1, T2> {
528 public:
529     typedef tbb::flow::indexer_node<T0, T1, T2>  indexer_node_type;
530     static void do_test() {
531         TestType<indexer_node_type>::test();
532     }
533 };
534 
535 template<
536       template<typename> class TestType,
537       typename T0, typename T1>
538 class generate_test<TestType, T0, T1> {
539 public:
540     typedef tbb::flow::indexer_node<T0, T1>  indexer_node_type;
541     static void do_test() {
542         TestType<indexer_node_type>::test();
543     }
544 };
545 
546 template<
547       template<typename> class TestType,
548       typename T0>
549 class generate_test<TestType, T0> {
550 public:
551     typedef tbb::flow::indexer_node<T0>  indexer_node_type;
552     static void do_test() {
553         TestType<indexer_node_type>::test();
554     }
555 };
556 
557 #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
558 template<typename tagged_msg_t, typename input_t>
559 bool check_edge(tbb::flow::graph& g,
560                 tbb::flow::broadcast_node<input_t>& start,
561                 tbb::flow::buffer_node<tagged_msg_t>& buf,
562                 input_t input_value) {
563     start.try_put(input_value);
564     g.wait_for_all();
565 
566     tagged_msg_t msg;
567     bool is_get_succeeded = buf.try_get(msg);
568 
569     CHECK_MESSAGE( ((is_get_succeeded)), "There is no item in the buffer");
570     CHECK_MESSAGE( ((tbb::flow::cast_to<input_t>(msg) == input_value)), "Wrong item value");
571     return true;
572 }
573 
574 template <typename... T>
575 void sink(T...) {}
576 
577 template <typename indexer_output_t, typename Type, typename BN, std::size_t... Seq>
578 void check_edge(tbb::flow::graph& g, BN& bn, tbb::flow::buffer_node<indexer_output_t>& buf, Type, tbb::detail::index_sequence<Seq...>) {
579     sink(check_edge<indexer_output_t>(g, std::get<Seq>(bn), buf, typename std::tuple_element<Seq, Type>::type(Seq))...);
580 }
581 
582 template <typename... Args, std::size_t... Seq>
583 void test_follows_impl(std::tuple<Args...> t, tbb::detail::index_sequence<Seq...> seq) {
584     using namespace tbb::flow;
585     using indexer_output_t = typename indexer_node<Args...>::output_type;
586 
587     graph g;
588     auto bn = std::make_tuple(broadcast_node<Args>(g)...);
589 
590     indexer_node<Args...> my_indexer(follows(std::get<Seq>(bn)...));
591 
592     buffer_node<indexer_output_t> buf(g);
593     make_edge(my_indexer, buf);
594 
595     check_edge<indexer_output_t>(g, bn, buf, t, seq);
596 }
597 
598 template <typename... Args>
599 void test_follows() {
600     test_follows_impl(std::tuple<Args...>(), tbb::detail::make_index_sequence<sizeof...(Args)>());
601 }
602 
603 void test_precedes() {
604     using namespace tbb::flow;
605 
606     using indexer_output_t = indexer_node<int, float, double>::output_type;
607 
608     graph g;
609 
610     broadcast_node<int> start1(g);
611     broadcast_node<float> start2(g);
612     broadcast_node<double> start3(g);
613 
614     buffer_node<indexer_output_t> buf1(g);
615     buffer_node<indexer_output_t> buf2(g);
616     buffer_node<indexer_output_t> buf3(g);
617 
618     indexer_node<int, float, double> node(precedes(buf1, buf2, buf3));
619 
620     make_edge(start1, input_port<0>(node));
621     make_edge(start2, input_port<1>(node));
622     make_edge(start3, input_port<2>(node));
623 
624     check_edge<indexer_output_t, int>(g, start1, buf1, 1);
625     check_edge<indexer_output_t, float>(g, start2, buf2, 2.2f);
626     check_edge<indexer_output_t, double>(g, start3, buf3, 3.3);
627 }
628 
629 void test_follows_and_precedes_api() {
630     test_follows<double>();
631     test_follows<int, double>();
632     test_follows<int, float, double>();
633     test_follows<float, double, int, double>();
634     test_follows<float, double, int, double, double>();
635     test_follows<float, double, int, double, double, float>();
636     test_follows<float, double, int, double, double, float, long>();
637     test_follows<float, double, int, double, double, float, long, int>();
638     test_follows<float, double, int, double, double, float, long, int, long>();
639     test_follows<float, double, int, double, double, float, long, int, float, long>();
640     test_precedes();
641 }
642 #endif // __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
643 
644 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
645 void test_deduction_guides() {
646     using namespace tbb::flow;
647     graph g;
648 
649     broadcast_node<int> b1(g);
650     broadcast_node<double> b2(g);
651     indexer_node<int, double> i0(g);
652 
653 #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
654     indexer_node i1(follows(b1, b2));
655     static_assert(std::is_same_v<decltype(i1), indexer_node<int, double>>);
656 #endif
657 
658     indexer_node i2(i0);
659     static_assert(std::is_same_v<decltype(i2), indexer_node<int, double>>);
660 }
661 
662 #endif
663 
664 //! Serial and parallel test on various tuple sizes
665 //! \brief \ref error_guessing
666 TEST_CASE("Serial and parallel test") {
667     INFO("Testing indexer_node, ");
668 
669    for (int p = 0; p < 2; ++p) {
670        generate_test<serial_test, float>::do_test();
671 #if MAX_TUPLE_TEST_SIZE >= 4
672        generate_test<serial_test, float, double, int, short>::do_test();
673 #endif
674 #if MAX_TUPLE_TEST_SIZE >= 6
675        generate_test<serial_test, double, double, int, long, int, short>::do_test();
676 #endif
677 #if MAX_TUPLE_TEST_SIZE >= 8
678        generate_test<serial_test, float, double, double, double, float, int, float, long>::do_test();
679 #endif
680 #if MAX_TUPLE_TEST_SIZE >= 10
681        generate_test<serial_test, float, double, int, double, double, float, long, int, float, long>::do_test();
682 #endif
683        generate_test<parallel_test, float, double>::do_test();
684 #if MAX_TUPLE_TEST_SIZE >= 3
685        generate_test<parallel_test, float, int, long>::do_test();
686 #endif
687 #if MAX_TUPLE_TEST_SIZE >= 5
688        generate_test<parallel_test, double, double, int, int, short>::do_test();
689 #endif
690 #if MAX_TUPLE_TEST_SIZE >= 7
691        generate_test<parallel_test, float, int, double, float, long, float, long>::do_test();
692 #endif
693 #if MAX_TUPLE_TEST_SIZE >= 9
694        generate_test<parallel_test, float, double, int, double, double, long, int, float, long>::do_test();
695 #endif
696    }
697 }
698 
699 #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
700 //! Test follows and precedes API
701 //! \brief \ref error_guessing
702 TEST_CASE("Follows and precedes API") {
703     test_follows_and_precedes_api();
704 }
705 #endif
706 
707 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
708 //! Test deduction guides
709 //! \brief \ref requirement
710 TEST_CASE("Deduction guides") {
711     test_deduction_guides();
712 }
713 #endif
714 
715