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