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/graph_utils.h" 28 29 #include <tuple> 30 #include <cmath> 31 #include <vector> 32 33 34 //! \file test_composite_node.cpp 35 //! \brief Test for [flow_graph.composite_node] specification 36 37 38 struct passthru_body { 39 int operator()( int i ) { 40 return i; 41 } 42 }; 43 44 class my_input_body{ 45 int start; 46 int finish; 47 int step; 48 public: 49 my_input_body(int f, int s) : start(1), finish(f), step(s) {} 50 int operator()(tbb::flow_control& fc) { 51 int a = start; 52 if (start <= finish) { 53 a = start; 54 start+=step; 55 return a; 56 } 57 else { 58 fc.stop(); 59 return int(); 60 }; 61 } 62 }; 63 64 struct m_fxn_body{ 65 void operator()(int, tbb::flow::multifunction_node<int, std::tuple<int,int> >::output_ports_type ) {} 66 }; 67 68 struct ct_body { 69 ct_body(){} 70 void operator()(tbb::flow::continue_msg){} 71 }; 72 73 struct seq_body { 74 std::size_t operator()(int i) { return i; } 75 }; 76 77 template<int N, typename T1, typename T2> 78 struct compare { 79 static void compare_refs(T1 tuple1, T2 tuple2) { 80 CHECK_MESSAGE( ( &std::get<N>(tuple1) == &std::get<N>(tuple2)), "ports not set correctly"); 81 compare<N-1, T1, T2>::compare_refs(tuple1, tuple2); 82 } 83 }; 84 85 template<typename T1, typename T2> 86 struct compare<1, T1, T2> { 87 static void compare_refs(T1 tuple1, T2 tuple2) { 88 CHECK_MESSAGE( (&std::get<0>(tuple1) == &std::get<0>(tuple2)), "port 0 not correctly set"); 89 } 90 }; 91 92 struct tiny_node : public tbb::flow::composite_node< std::tuple< int >, std::tuple< int > > { 93 tbb::flow::function_node< int, int > f1; 94 tbb::flow::function_node< int, int > f2; 95 typedef tbb::flow::composite_node< std::tuple< int >, std::tuple< int > > base_type; 96 97 public: 98 tiny_node(tbb::flow::graph &g, bool hidden = false) : base_type(g), f1(g, tbb::flow::unlimited, passthru_body() ), f2(g, tbb::flow::unlimited, passthru_body() ) { 99 tbb::flow::make_edge(f1, f2); 100 101 std::tuple<tbb::flow::function_node< int, int >& > input_tuple(f1); 102 std::tuple<tbb::flow::function_node< int, int >& > output_tuple(f2); 103 base_type::set_external_ports(input_tuple, output_tuple); 104 105 if(hidden) 106 base_type::add_nodes(f1, f2); 107 else 108 base_type::add_visible_nodes(f1, f2); 109 110 } 111 }; 112 113 int test_tiny(bool hidden = false) { 114 tbb::flow::graph g; 115 tbb::flow::function_node< int, int > f0( g, tbb::flow::unlimited, passthru_body() ); 116 tiny_node t(g, hidden); 117 CHECK_MESSAGE( (&tbb::flow::input_port<0>(t) == &t.f1), "f1 not bound to input port 0 in composite_node t"); 118 CHECK_MESSAGE( (&tbb::flow::output_port<0>(t) == &t.f2), "f2 not bound to output port 0 in composite_node t"); 119 120 tiny_node t1(g, hidden); 121 CHECK_MESSAGE( (&std::get<0>(t1.input_ports()) == &t1.f1), "f1 not bound to input port 0 in composite_node t1"); 122 CHECK_MESSAGE( (&std::get<0>(t1.output_ports()) == &t1.f2), "f2 not bound to output port 0 in composite_node t1"); 123 124 test_input_ports_return_ref(t1); 125 test_output_ports_return_ref(t1); 126 127 tiny_node t2(g, hidden); 128 CHECK_MESSAGE( (&tbb::flow::input_port<0>(t2) == &t2.f1), "f1 not bound to input port 0 in composite_node t2"); 129 CHECK_MESSAGE( (&tbb::flow::output_port<0>(t2) == &t2.f2), "f2 not bound to output port 0 in composite_node t2"); 130 131 tbb::flow::function_node< int, int > f3( g, tbb::flow::unlimited, passthru_body() ); 132 tbb::flow::make_edge( f0, t ); 133 tbb::flow::make_edge( t, t1 ); 134 tbb::flow::make_edge( t1, t2 ); 135 tbb::flow::make_edge( t2 , f3 ); 136 tbb::flow::queue_node<int> q(g); 137 tbb::flow::make_edge(f3, q); 138 f0.try_put(1); 139 g.wait_for_all(); 140 141 int i, j =0; 142 q.try_get(i); 143 CHECK_MESSAGE( ( i == 1), "item did not go through graph"); 144 q.try_get(j); 145 CHECK_MESSAGE( ( !j), "unexpected item in graph"); 146 g.wait_for_all(); 147 148 tbb::flow::remove_edge(f3, q); 149 tbb::flow::remove_edge(t2, f3); 150 tbb::flow::remove_edge(t1, t2); 151 152 tbb::flow::make_edge( t1 , f3 ); 153 tbb::flow::make_edge(f3, q); 154 155 f0.try_put(2); 156 g.wait_for_all(); 157 158 q.try_get(i); 159 CHECK_MESSAGE( ( i == 2), "item did not go through graph after removal of edge"); 160 q.try_get(j); 161 CHECK_MESSAGE( ( !j), "unexpected item in graph after removal of edge"); 162 163 return 0; 164 } 165 166 class adder_node : public tbb::flow::composite_node< std::tuple< int, int >, std::tuple< int > > { 167 public: 168 tbb::flow::join_node< std::tuple< int, int >, tbb::flow::queueing > j; 169 tbb::flow::function_node< std::tuple< int, int >, int > f; 170 private: 171 typedef tbb::flow::composite_node< std::tuple< int, int >, std::tuple< int > > base_type; 172 173 struct f_body { 174 int operator()( const std::tuple< int, int > &t ) { 175 return std::get<0>(t) + std::get<1>(t); 176 } 177 }; 178 179 public: 180 adder_node(tbb::flow::graph &g, bool hidden = false) : base_type(g), j(g), f(g, tbb::flow::unlimited, f_body() ) { 181 tbb::flow::make_edge( j, f ); 182 183 base_type::set_external_ports(base_type::input_ports_type(tbb::flow::input_port<0>(j), tbb::flow::input_port<1>(j)), base_type::output_ports_type(f)); 184 185 if (hidden) 186 base_type::add_nodes(j, f); 187 else 188 base_type::add_visible_nodes(j, f); 189 190 } 191 }; 192 193 struct square_body { int operator()(int v) { return v*v; } }; 194 struct cube_body { int operator()(int v) { return v*v*v; } }; 195 int adder_sum(int i) { 196 return (int)(pow(3*pow(i,3) + pow(i, 2),2)); 197 } 198 int test_adder(bool hidden = false) { 199 tbb::flow::graph g; 200 tbb::flow::function_node<int,int> s(g, tbb::flow::unlimited, square_body()); 201 tbb::flow::function_node<int,int> c(g, tbb::flow::unlimited, cube_body()); 202 tbb::flow::function_node<int,int> p(g, tbb::flow::unlimited, passthru_body()); 203 204 adder_node a0(g, hidden); 205 CHECK_MESSAGE( (&tbb::flow::input_port<0>(a0) == &tbb::flow::input_port<0>(a0.j)), "input_port 0 of j not bound to input port 0 in composite_node a0"); 206 CHECK_MESSAGE( (&tbb::flow::input_port<1>(a0) == &tbb::flow::input_port<1>(a0.j)), "input_port 1 of j not bound to input port 1 in composite_node a0"); 207 CHECK_MESSAGE( (&tbb::flow::output_port<0>(a0) == &a0.f), "f not bound to output port 0 in composite_node a0"); 208 209 adder_node a1(g, hidden); 210 CHECK_MESSAGE( (&std::get<0>(a0.input_ports()) == &tbb::flow::input_port<0>(a0.j)), "input_port 0 of j not bound to input port 0 in composite_node a1"); 211 CHECK_MESSAGE( (&std::get<1>(a0.input_ports()) == &tbb::flow::input_port<1>(a0.j)), "input_port1 of j not bound to input port 1 in composite_node a1"); 212 CHECK_MESSAGE( (&std::get<0>(a0.output_ports()) == &a0.f), "f not bound to output port 0 in composite_node a1"); 213 214 adder_node a2(g, hidden); 215 CHECK_MESSAGE( (&tbb::flow::input_port<0>(a2) == &tbb::flow::input_port<0>(a2.j)), "input_port 0 of j not bound to input port 0 in composite_node a2"); 216 CHECK_MESSAGE( (&tbb::flow::input_port<1>(a2) == &tbb::flow::input_port<1>(a2.j)), "input_port 1 of j not bound to input port 1 in composite_node a2"); 217 CHECK_MESSAGE( (&tbb::flow::output_port<0>(a2) == &a2.f), "f not bound to output port 0 in composite_node a2"); 218 219 adder_node a3(g, hidden); 220 CHECK_MESSAGE( (&std::get<0>(a3.input_ports()) == &tbb::flow::input_port<0>(a3.j)), "input_port 0 of j not bound to input port 0 in composite_node a3"); 221 CHECK_MESSAGE( (&std::get<1>(a3.input_ports()) == &tbb::flow::input_port<1>(a3.j)), "input_port1 of j not bound to input port 1 in composite_node a3"); 222 CHECK_MESSAGE( (&std::get<0>(a3.output_ports()) == &a3.f), "f not bound to output port 0 in composite_node a3"); 223 224 tbb::flow::function_node<int,int> s2(g, tbb::flow::unlimited, square_body()); 225 tbb::flow::queue_node<int> q(g); 226 227 tbb::flow::make_edge( s, tbb::flow::input_port<0>(a0) ); 228 tbb::flow::make_edge( c, tbb::flow::input_port<1>(a0) ); 229 230 tbb::flow::make_edge( c, tbb::flow::input_port<0>(a1) ); 231 tbb::flow::make_edge( c, tbb::flow::input_port<1>(a1) ); 232 233 tbb::flow::make_edge( tbb::flow::output_port<0>(a0), tbb::flow::input_port<0>(a2) ); 234 tbb::flow::make_edge( tbb::flow::output_port<0>(a1), tbb::flow::input_port<1>(a2) ); 235 236 tbb::flow::make_edge( tbb::flow::output_port<0>(a2), s2 ); 237 tbb::flow::make_edge( s2, q ); 238 239 int sum_total=0; 240 int result=0; 241 for ( int i = 1; i < 4; ++i ) { 242 s.try_put(i); 243 c.try_put(i); 244 sum_total += adder_sum(i); 245 g.wait_for_all(); 246 } 247 248 int j; 249 for ( int i = 1; i < 4; ++i ) { 250 q.try_get(j); 251 result += j; 252 } 253 g.wait_for_all(); 254 CHECK_MESSAGE( (result == sum_total), "the sum from the graph does not match the calculated value"); 255 256 tbb::flow::remove_edge(s2, q); 257 tbb::flow::remove_edge( a2, s2 ); 258 tbb::flow::make_edge( a0, a3 ); 259 tbb::flow::make_edge( a1, tbb::flow::input_port<1>(a3) ); 260 tbb::flow::make_edge( a3, s2 ); 261 tbb::flow::make_edge( s2, q ); 262 263 sum_total=0; 264 result=0; 265 for ( int i = 10; i < 20; ++i ) { 266 s.try_put(i); 267 c.try_put(i); 268 sum_total += adder_sum(i); 269 g.wait_for_all(); 270 } 271 272 for ( int i = 10; i < 20; ++i ) { 273 q.try_get(j); 274 result += j; 275 } 276 g.wait_for_all(); 277 CHECK_MESSAGE( (result == sum_total), "the new sum after the replacement of the nodes does not match the calculated value"); 278 279 return 0; 280 } 281 282 /* 283 outer composite node (outer_node) 284 |-------------------------------------------------------------------| 285 | | 286 | |------------------| |------------------| |------------------| | 287 |---------------------| |--| inner composite | /| inner composite | /| inner composite | | |-------------------| 288 |broadcast node(input)|/| | node |/ | node |/ | node |-+-| queue node(output)| 289 |---------------------|\| |(inner_node1) |\ | (inner_node2) |\ | (inner_node3) | | |-------------------| 290 |--| | \| | \| | | 291 | |------------------| |------------------| |------------------| | 292 | | 293 |-------------------------------------------------------------------| 294 295 */ 296 int test_nested_adder(bool hidden=false) { 297 tbb::flow::graph g; 298 tbb::flow::composite_node<std::tuple<int, int>, std::tuple<int> > outer_node(g); 299 typedef tbb::flow::composite_node<std::tuple<int, int>, std::tuple<int> > base_type; 300 tbb::flow::broadcast_node<int> input(g); 301 tbb::flow::queue_node<int> output(g); 302 303 adder_node inner_node1(g, hidden); 304 adder_node inner_node2(g, hidden); 305 adder_node inner_node3(g, hidden); 306 307 outer_node.set_external_ports(base_type::input_ports_type(tbb::flow::input_port<0>(inner_node1), tbb::flow::input_port<1>(inner_node1)), base_type::output_ports_type(tbb::flow::output_port<0>(inner_node3))); 308 309 CHECK_MESSAGE( (&tbb::flow::input_port<0>(outer_node) == &tbb::flow::input_port<0>(inner_node1)), "input port 0 of inner_node1 not bound to input port 0 in outer_node"); 310 CHECK_MESSAGE( (&tbb::flow::input_port<1>(outer_node) == &tbb::flow::input_port<1>(inner_node1)), "input port 1 of inner_node1 not bound to input port 1 in outer_node"); 311 CHECK_MESSAGE( (&tbb::flow::output_port<0>(outer_node) == &tbb::flow::output_port<0>(inner_node3)), "output port 0 of inner_node3 not bound to output port 0 in outer_node"); 312 313 tbb::flow::make_edge(input, tbb::flow::input_port<0>(outer_node)/*inner_node1*/); 314 tbb::flow::make_edge(input, tbb::flow::input_port<1>(outer_node)/*inner_node1*/); 315 316 tbb::flow::make_edge(inner_node1, tbb::flow::input_port<0>(inner_node2)); 317 tbb::flow::make_edge(inner_node1, tbb::flow::input_port<1>(inner_node2)); 318 319 tbb::flow::make_edge(inner_node2, tbb::flow::input_port<0>(inner_node3)); 320 tbb::flow::make_edge(inner_node2, tbb::flow::input_port<1>(inner_node3)); 321 322 tbb::flow::make_edge(outer_node/*inner_node3*/, output); 323 324 if(hidden) 325 outer_node.add_nodes(inner_node1, inner_node2, inner_node3); 326 else 327 outer_node.add_visible_nodes(inner_node1, inner_node2, inner_node3); 328 329 int out; 330 for (int i = 1; i < 200000; ++i) { 331 input.try_put(i); 332 g.wait_for_all(); 333 output.try_get(out); 334 CHECK_MESSAGE( (tbb::flow::output_port<0>(outer_node).try_get(out) == output.try_get(out)), "output from outer_node does not match output from graph"); 335 CHECK_MESSAGE( (out == 8*i), "output from outer_node not correct"); 336 } 337 g.wait_for_all(); 338 339 return 0; 340 } 341 342 template< typename T > 343 class prefix_node : public tbb::flow::composite_node< std::tuple< T, T, T, T, T >, std::tuple< T, T, T, T, T > > { 344 typedef std::tuple< T, T, T, T, T > my_tuple_t; 345 public: 346 tbb::flow::join_node< my_tuple_t, tbb::flow::queueing > j; 347 tbb::flow::split_node< my_tuple_t > s; 348 private: 349 tbb::flow::function_node< my_tuple_t, my_tuple_t > f; 350 typedef tbb::flow::composite_node< my_tuple_t, my_tuple_t > base_type; 351 352 struct f_body { 353 my_tuple_t operator()( const my_tuple_t &t ) { 354 return my_tuple_t( std::get<0>(t), 355 std::get<0>(t) + std::get<1>(t), 356 std::get<0>(t) + std::get<1>(t) + std::get<2>(t), 357 std::get<0>(t) + std::get<1>(t) + std::get<2>(t) + std::get<3>(t), 358 std::get<0>(t) + std::get<1>(t) + std::get<2>(t) + std::get<3>(t) + std::get<4>(t) ); 359 } 360 }; 361 362 public: 363 prefix_node(tbb::flow::graph &g, bool hidden = false ) : base_type(g), j(g), s(g), f(g, tbb::flow::serial, f_body() ) { 364 tbb::flow::make_edge( j, f ); 365 tbb::flow::make_edge( f, s ); 366 367 typename base_type::input_ports_type input_tuple(tbb::flow::input_port<0>(j), tbb::flow::input_port<1>(j), tbb::flow::input_port<2>(j), tbb::flow::input_port<3>(j), tbb::flow::input_port<4>(j)); 368 369 typename base_type::output_ports_type output_tuple(tbb::flow::output_port<0>(s), tbb::flow::output_port<1>(s), tbb::flow::output_port<2>(s), tbb::flow::output_port<3>(s), tbb::flow::output_port<4>(s)); 370 371 base_type::set_external_ports(input_tuple, output_tuple); 372 373 if(hidden) 374 base_type::add_nodes(j,s,f); 375 else 376 base_type::add_visible_nodes(j,s,f); 377 378 } 379 }; 380 381 int test_prefix(bool hidden = false) { 382 tbb::flow::graph g; 383 prefix_node<double> p(g, hidden); 384 385 CHECK_MESSAGE( (&std::get<0>(p.input_ports()) == &tbb::flow::input_port<0>(p.j)), "input port 0 of j is not bound to input port 0 of composite node p"); 386 CHECK_MESSAGE( (&tbb::flow::input_port<1>(p.j) == &tbb::flow::input_port<1>(p.j)), "input port 1 of j is not bound to input port 1 of composite node p"); 387 CHECK_MESSAGE( (&std::get<2>(p.input_ports()) == &tbb::flow::input_port<2>(p.j)), "input port 2 of j is not bound to input port 2 of composite node p"); 388 CHECK_MESSAGE( (&tbb::flow::input_port<3>(p.j) == &tbb::flow::input_port<3>(p.j)), "input port 3 of j is not bound to input port 3 of composite node p"); 389 CHECK_MESSAGE( (&std::get<4>(p.input_ports()) == &tbb::flow::input_port<4>(p.j)), "input port 4 of j is not bound to input port 4 of composite node p"); 390 391 392 CHECK_MESSAGE( (&std::get<0>(p.output_ports()) == &tbb::flow::output_port<0>(p.s)), "output port 0 of s is not bound to output port 0 of composite node p"); 393 CHECK_MESSAGE( (&tbb::flow::output_port<1>(p.s) == &tbb::flow::output_port<1>(p.s)), "output port 1 of s is not bound to output port 1 of composite node p"); 394 CHECK_MESSAGE( (&std::get<2>(p.output_ports()) == &tbb::flow::output_port<2>(p.s)), "output port 2 of s is not bound to output port 2 of composite node p"); 395 CHECK_MESSAGE( (&tbb::flow::output_port<3>(p.s) == &tbb::flow::output_port<3>(p.s)), "output port 3 of s is not bound to output port 3 of composite node p"); 396 CHECK_MESSAGE( (&std::get<4>(p.output_ports()) == &tbb::flow::output_port<4>(p.s)), "output port 4 of s is not bound to output port 4 of composite node p"); 397 398 std::vector< tbb::flow::queue_node<double> > v( 5, tbb::flow::queue_node<double>(g) ); 399 tbb::flow::make_edge( tbb::flow::output_port<0>(p), v[0] ); 400 tbb::flow::make_edge( tbb::flow::output_port<1>(p), v[1] ); 401 tbb::flow::make_edge( tbb::flow::output_port<2>(p), v[2] ); 402 tbb::flow::make_edge( tbb::flow::output_port<3>(p), v[3] ); 403 tbb::flow::make_edge( tbb::flow::output_port<4>(p), v[4] ); 404 405 for( double offset = 1; offset < 10000; offset *= 10 ) { 406 tbb::flow::input_port<0>(p).try_put( offset ); 407 tbb::flow::input_port<1>(p).try_put( offset + 1 ); 408 tbb::flow::input_port<2>(p).try_put( offset + 2 ); 409 tbb::flow::input_port<3>(p).try_put( offset + 3 ); 410 tbb::flow::input_port<4>(p).try_put( offset + 4 ); 411 } 412 g.wait_for_all(); 413 414 double x; 415 while ( v[0].try_get(x) ) { 416 g.wait_for_all(); 417 for ( int i = 1; i < 5; ++i ) { 418 v[i].try_get(x); 419 g.wait_for_all(); 420 } 421 } 422 return 0; 423 } 424 425 struct input_only_output_only_seq { 426 std::size_t operator()(int i) { 427 CHECK(i > 0); 428 return std::size_t((i + 3) / 4 - 1); 429 } 430 }; 431 432 void input_only_output_only_composite(bool hidden) { 433 tbb::flow::graph g; 434 435 tbb::flow::composite_node<std::tuple<int>, std::tuple<int> > input_output(g); 436 437 typedef tbb::flow::composite_node<std::tuple<int>, std::tuple<> > input_only_composite; 438 typedef tbb::flow::composite_node<std::tuple<>, std::tuple<int> > output_only_composite; 439 440 typedef tbb::flow::input_node<int> src_type; 441 typedef tbb::flow::queue_node<int> q_type; 442 typedef tbb::flow::function_node<int, int> f_type; 443 typedef tbb::flow::sequencer_node<int> sequencer_type; 444 445 int num = 0; 446 int finish=1000; 447 int step = 4; 448 449 input_only_composite a_in(g); 450 output_only_composite a_out(g); 451 452 src_type src(g, my_input_body(finish, step)); 453 q_type que(g); 454 f_type f(g, 1, passthru_body()); 455 456 // Sequencer_node is needed, because serial function_node guarantees only serial body execution, 457 // not a sequential order of messages dispatch 458 sequencer_type seq(g, input_only_output_only_seq()); 459 460 std::tuple<f_type& > input_tuple(f); 461 a_in.set_external_ports(input_tuple); 462 CHECK_MESSAGE( (&std::get<0>(a_in.input_ports()) == &f), "f not bound to input port 0 in composite_node a_in"); 463 464 std::tuple<src_type&> output_tuple(src); 465 a_out.set_external_ports(output_tuple); 466 CHECK_MESSAGE( (&std::get<0>(a_out.output_ports()) == &src), "src not bound to output port 0 in composite_node a_out"); 467 468 if(hidden) { 469 a_in.add_nodes(f, seq, que); 470 a_out.add_nodes(src); 471 } else { 472 a_in.add_visible_nodes(f, seq, que); 473 a_out.add_visible_nodes(src); 474 } 475 476 tbb::flow::make_edge(a_out, a_in); 477 tbb::flow::make_edge(f, seq); 478 tbb::flow::make_edge(seq, que); 479 src.activate(); 480 g.wait_for_all(); 481 482 for(int i = 1; i<finish/step; ++i) { 483 que.try_get(num); 484 CHECK_MESSAGE( (num == 4*i - 3), "number does not match position in sequence"); 485 } 486 g.wait_for_all(); 487 } 488 489 //! Test single node inside composite nodes 490 //! \brief \ref error_guessing 491 TEST_CASE("Tiny tests"){ 492 test_tiny(false); 493 test_tiny(true); 494 } 495 496 //! Test basic adders in composite node 497 //! \brief \ref error_guessing 498 TEST_CASE("Adder tests"){ 499 test_adder(false); 500 test_adder(true); 501 } 502 503 //! Test nested adders in composite node 504 //! \brief \ref error_guessing 505 TEST_CASE("Nested adder tests"){ 506 test_nested_adder(true); 507 test_nested_adder(false); 508 } 509 510 //! Test returning a subset of inputs 511 //! \brief \ref error_guessing 512 TEST_CASE("Prefix test"){ 513 test_prefix(false); 514 test_prefix(true); 515 } 516 517 //! Test input-only composite node 518 //! \brief \ref error_guessing \ref boundary 519 TEST_CASE("Input-only composite"){ 520 input_only_output_only_composite(true); 521 input_only_output_only_composite(false); 522 } 523 524