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