1 /*
2 Copyright (c) 2005-2022 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
check_outputCheck(int nUsed,int maxCnt)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
reset_outputCheck(int nUsed,int maxCnt)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:
test_class()64 test_class() { my_val = 0; }
test_class(int i)65 test_class(int i) { my_val = i; }
operator int()66 operator int() { return my_val; }
67 private:
68 int my_val;
69 };
70
71 template<typename T>
72 class name_of {
73 public:
name()74 static const char* name() { return "Unknown"; }
75 };
76 template<>
77 class name_of<int> {
78 public:
name()79 static const char* name() { return "int"; }
80 };
81 template<>
82 class name_of<float> {
83 public:
name()84 static const char* name() { return "float"; }
85 };
86 template<>
87 class name_of<double> {
88 public:
name()89 static const char* name() { return "double"; }
90 };
91 template<>
92 class name_of<long> {
93 public:
name()94 static const char* name() { return "long"; }
95 };
96 template<>
97 class name_of<short> {
98 public:
name()99 static const char* name() { return "short"; }
100 };
101 template<>
102 class name_of<test_class> {
103 public:
name()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:
my_input_body(TT multiplier,int init_val,int addto)120 my_input_body(TT multiplier, int init_val, int addto) : my_mult(multiplier), my_count(init_val), addend(addto) { }
operator ()(tbb::flow_control & fc)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:
create()139 static IType *create() {
140 IType *temp = new IType();
141 return temp;
142 }
destroy(IType * p)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
get_integer_valgetval_helper152 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;
print_remark()169 static void print_remark() {
170 input_node_helper<ELEM-1,INT>::print_remark();
171 INFO(", " << name_of<IT>::name());
172 }
add_input_nodes(indexer_node_type & my_indexer,tbb::flow::graph & g,int nInputs)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 }
check_value(TT & v)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
remove_input_nodes(indexer_node_type & my_indexer,int nInputs)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:
print_remark()216 static void print_remark() {
217 INFO("Parallel test of indexer_node< " << name_of<IT>::name());
218 }
add_input_nodes(indexer_node_type & my_indexer,tbb::flow::graph & g,int nInputs)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 }
check_value(TT & v)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 }
remove_input_nodes(indexer_node_type & my_indexer,int nInputs)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;
test()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;
print_remark()306 static void print_remark() {
307 serial_queue_helper<ELEM-1,IType>::print_remark();
308 INFO("," << name_of<IT>::name());
309 }
fill_one_queue(int maxVal,IType & my_indexer)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 }
put_one_queue_val(int myVal,IType & my_indexer)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 }
check_queue_value(OT & v)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;
print_remark()341 static void print_remark() {
342 INFO("Serial test of indexer_node< " << name_of<IT>::name());
343 }
fill_one_queue(int maxVal,IType & my_indexer)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 }
put_one_queue_val(int myVal,IType & my_indexer)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 }
check_queue_value(OT & v)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>
test_one_serial(IType & my_indexer,tbb::flow::graph & g)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>
test_input_ports_return_ref(NodeType & mip_node)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:
test()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;
do_test()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;
do_test()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;
do_test()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;
do_test()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;
do_test()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;
do_test()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;
do_test()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;
do_test()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;
do_test()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;
do_test()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>
check_edge(tbb::flow::graph & g,tbb::flow::broadcast_node<input_t> & start,tbb::flow::buffer_node<tagged_msg_t> & buf,input_t input_value)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>
sink(T...)575 void sink(T...) {}
576
577 template <typename indexer_output_t, typename Type, typename BN, std::size_t... Seq>
check_edge(tbb::flow::graph & g,BN & bn,tbb::flow::buffer_node<indexer_output_t> & buf,Type,tbb::detail::index_sequence<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>
test_follows_impl(std::tuple<Args...> t,tbb::detail::index_sequence<Seq...> 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>
test_follows()599 void test_follows() {
600 test_follows_impl(std::tuple<Args...>(), tbb::detail::make_index_sequence<sizeof...(Args)>());
601 }
602
test_precedes()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
test_follows_and_precedes_api()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
test_deduction_guides()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