1*51c0b2f7Stbbdev /*
2*51c0b2f7Stbbdev     Copyright (c) 2020 Intel Corporation
3*51c0b2f7Stbbdev 
4*51c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
5*51c0b2f7Stbbdev     you may not use this file except in compliance with the License.
6*51c0b2f7Stbbdev     You may obtain a copy of the License at
7*51c0b2f7Stbbdev 
8*51c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
9*51c0b2f7Stbbdev 
10*51c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
11*51c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
12*51c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*51c0b2f7Stbbdev     See the License for the specific language governing permissions and
14*51c0b2f7Stbbdev     limitations under the License.
15*51c0b2f7Stbbdev */
16*51c0b2f7Stbbdev 
17*51c0b2f7Stbbdev 
18*51c0b2f7Stbbdev #include "common/test.h"
19*51c0b2f7Stbbdev 
20*51c0b2f7Stbbdev #include "common/utils.h"
21*51c0b2f7Stbbdev #include "common/graph_utils.h"
22*51c0b2f7Stbbdev 
23*51c0b2f7Stbbdev #include "tbb/flow_graph.h"
24*51c0b2f7Stbbdev #include "tbb/task_arena.h"
25*51c0b2f7Stbbdev #include "tbb/global_control.h"
26*51c0b2f7Stbbdev 
27*51c0b2f7Stbbdev #include "conformance_flowgraph.h"
28*51c0b2f7Stbbdev 
29*51c0b2f7Stbbdev //! \file conformance_join_node.cpp
30*51c0b2f7Stbbdev //! \brief Test for [flow_graph.join_node] specification
31*51c0b2f7Stbbdev 
32*51c0b2f7Stbbdev /*
33*51c0b2f7Stbbdev TODO: implement missing conformance tests for join_node:
34*51c0b2f7Stbbdev   - [ ] Check that `OutputTuple' is an instantiation of a tuple.
35*51c0b2f7Stbbdev   - [ ] The copy constructor and copy assignment are called for each type within the `OutputTuple'.
36*51c0b2f7Stbbdev   - [ ] Check all possible policies of the node: `reserving', `key_matching', `queueing',
37*51c0b2f7Stbbdev     `tag_matching'. Check the semantics the node has with each policy separately.
38*51c0b2f7Stbbdev   - [ ] Check that corresponding methods are invoked in specified `KHash' type.
39*51c0b2f7Stbbdev   - [ ] Improve test for constructors, including their availability based on used Policy for the
40*51c0b2f7Stbbdev     node.
41*51c0b2f7Stbbdev   - [ ] Unify code style in the test by extracting the implementation from the `TEST_CASE' scope
42*51c0b2f7Stbbdev     into separate functions.
43*51c0b2f7Stbbdev   - [ ] Check that corresponding methods mentioned in the requirements are called for `Bi' types.
44*51c0b2f7Stbbdev   - [ ] Explicitly check that `input_ports_type' is defined, accessible and is a tuple of
45*51c0b2f7Stbbdev     corresponding to `OutputTuple' receivers.
46*51c0b2f7Stbbdev   - [ ] Explicitly check the method `join_node::input_ports()' exists, is accessible and it returns
47*51c0b2f7Stbbdev     a reference to the `input_ports_type' type.
48*51c0b2f7Stbbdev   - [ ] Implement `test_buffering' (for node policy).
49*51c0b2f7Stbbdev   - [ ] Check `try_get()' copies the generated tuple into passed argument and returns `true'. If
50*51c0b2f7Stbbdev     node is empty returns `false'.
51*51c0b2f7Stbbdev   - [ ] Check `tag_value' is defined and has properties specified.
52*51c0b2f7Stbbdev   - [ ] Add test for CTAD.
53*51c0b2f7Stbbdev */
54*51c0b2f7Stbbdev 
55*51c0b2f7Stbbdev using namespace tbb::flow;
56*51c0b2f7Stbbdev using namespace std;
57*51c0b2f7Stbbdev 
58*51c0b2f7Stbbdev template<typename T>
59*51c0b2f7Stbbdev void test_inheritance(){
60*51c0b2f7Stbbdev     CHECK_MESSAGE( (std::is_base_of<graph_node, join_node<std::tuple<T, T>>>::value), "join_node should be derived from graph_node");
61*51c0b2f7Stbbdev     CHECK_MESSAGE( (std::is_base_of<sender<std::tuple<T, T>>, join_node<std::tuple<T, T>>>::value), "join_node should be derived from graph_node");
62*51c0b2f7Stbbdev }
63*51c0b2f7Stbbdev 
64*51c0b2f7Stbbdev void test_copies(){
65*51c0b2f7Stbbdev     using namespace tbb::flow;
66*51c0b2f7Stbbdev 
67*51c0b2f7Stbbdev     graph g;
68*51c0b2f7Stbbdev     join_node<std::tuple<int, int>> n(g);
69*51c0b2f7Stbbdev     join_node<std::tuple<int, int>> n2(n);
70*51c0b2f7Stbbdev 
71*51c0b2f7Stbbdev     join_node <std::tuple<int, int, tbb::flow::reserving>> nr(g);
72*51c0b2f7Stbbdev     join_node <std::tuple<int, int, tbb::flow::reserving>> nr2(nr);
73*51c0b2f7Stbbdev }
74*51c0b2f7Stbbdev 
75*51c0b2f7Stbbdev void test_forwarding(){
76*51c0b2f7Stbbdev     tbb::flow::graph g;
77*51c0b2f7Stbbdev 
78*51c0b2f7Stbbdev     join_node<std::tuple<int, int>> node1(g);
79*51c0b2f7Stbbdev 
80*51c0b2f7Stbbdev     using output_t = join_node<std::tuple<int, int>>::output_type;
81*51c0b2f7Stbbdev 
82*51c0b2f7Stbbdev     test_push_receiver<output_t> node2(g);
83*51c0b2f7Stbbdev     test_push_receiver<output_t> node3(g);
84*51c0b2f7Stbbdev 
85*51c0b2f7Stbbdev     tbb::flow::make_edge(node1, node2);
86*51c0b2f7Stbbdev     tbb::flow::make_edge(node1, node3);
87*51c0b2f7Stbbdev 
88*51c0b2f7Stbbdev     input_port<0>(node1).try_put(1);
89*51c0b2f7Stbbdev     input_port<1>(node1).try_put(1);
90*51c0b2f7Stbbdev 
91*51c0b2f7Stbbdev     g.wait_for_all();
92*51c0b2f7Stbbdev 
93*51c0b2f7Stbbdev     CHECK_MESSAGE( (get_count(node2) == 1), "Descendant of the node needs to be receive N messages");
94*51c0b2f7Stbbdev     CHECK_MESSAGE( (get_count(node3) == 1), "Descendant of the node must receive one message.");
95*51c0b2f7Stbbdev }
96*51c0b2f7Stbbdev 
97*51c0b2f7Stbbdev //! Test broadcast
98*51c0b2f7Stbbdev //! \brief \ref interface
99*51c0b2f7Stbbdev TEST_CASE("join_node broadcast") {
100*51c0b2f7Stbbdev     test_forwarding();
101*51c0b2f7Stbbdev }
102*51c0b2f7Stbbdev 
103*51c0b2f7Stbbdev 
104*51c0b2f7Stbbdev //! Test copy constructor
105*51c0b2f7Stbbdev //! \brief \ref interface
106*51c0b2f7Stbbdev TEST_CASE("join_node copy constructor") {
107*51c0b2f7Stbbdev     test_copies();
108*51c0b2f7Stbbdev }
109*51c0b2f7Stbbdev 
110*51c0b2f7Stbbdev //! Test inheritance relations
111*51c0b2f7Stbbdev //! \brief \ref interface
112*51c0b2f7Stbbdev TEST_CASE("join_node inheritance"){
113*51c0b2f7Stbbdev     test_inheritance<int>();
114*51c0b2f7Stbbdev }
115*51c0b2f7Stbbdev 
116*51c0b2f7Stbbdev //! Test join_node behavior
117*51c0b2f7Stbbdev //! \brief \ref requirement
118*51c0b2f7Stbbdev TEST_CASE("join_node") {
119*51c0b2f7Stbbdev     graph g;
120*51c0b2f7Stbbdev     function_node<int,int>
121*51c0b2f7Stbbdev         f1( g, unlimited, [](const int &i) { return 2*i; } );
122*51c0b2f7Stbbdev     function_node<float,float>
123*51c0b2f7Stbbdev         f2( g, unlimited, [](const float &f) { return f/2; } );
124*51c0b2f7Stbbdev 
125*51c0b2f7Stbbdev     join_node< std::tuple<int,float> > j(g);
126*51c0b2f7Stbbdev 
127*51c0b2f7Stbbdev     function_node< std::tuple<int,float> >
128*51c0b2f7Stbbdev         f3( g, unlimited,
129*51c0b2f7Stbbdev             []( const std::tuple<int,float> &t ) {
130*51c0b2f7Stbbdev                 CHECK_MESSAGE( (std::get<0>(t) == 6), "Expected to receive 6" );
131*51c0b2f7Stbbdev                 CHECK_MESSAGE( (std::get<1>(t) == 1.5), "Expected to receive 1.5" );
132*51c0b2f7Stbbdev             } );
133*51c0b2f7Stbbdev 
134*51c0b2f7Stbbdev     make_edge( f1, input_port<0>( j ) );
135*51c0b2f7Stbbdev     make_edge( f2, input_port<1>( j ) );
136*51c0b2f7Stbbdev     make_edge( j, f3 );
137*51c0b2f7Stbbdev 
138*51c0b2f7Stbbdev     f1.try_put( 3 );
139*51c0b2f7Stbbdev     f2.try_put( 3 );
140*51c0b2f7Stbbdev     g.wait_for_all( );
141*51c0b2f7Stbbdev }
142*51c0b2f7Stbbdev 
143*51c0b2f7Stbbdev //! Test join_node key matching behavior
144*51c0b2f7Stbbdev //! \brief \ref requirement
145*51c0b2f7Stbbdev TEST_CASE("remove edge to join_node"){
146*51c0b2f7Stbbdev     graph g;
147*51c0b2f7Stbbdev     continue_node<int> c(g, [](const continue_msg&){ return 1; });
148*51c0b2f7Stbbdev     join_node<tuple<int> > jn(g);
149*51c0b2f7Stbbdev     queue_node<tuple<int> > q(g);
150*51c0b2f7Stbbdev 
151*51c0b2f7Stbbdev     make_edge(jn, q);
152*51c0b2f7Stbbdev 
153*51c0b2f7Stbbdev     make_edge(c, jn);
154*51c0b2f7Stbbdev 
155*51c0b2f7Stbbdev     c.try_put(continue_msg());
156*51c0b2f7Stbbdev     g.wait_for_all();
157*51c0b2f7Stbbdev 
158*51c0b2f7Stbbdev     tuple<int> tmp = tuple<int>(0);
159*51c0b2f7Stbbdev     CHECK_MESSAGE( (q.try_get(tmp)== true), "Message should pass when edge exists");
160*51c0b2f7Stbbdev     CHECK_MESSAGE( (tmp == tuple<int>(1) ), "Message should pass when edge exists");
161*51c0b2f7Stbbdev     CHECK_MESSAGE( (q.try_get(tmp)== false), "Message should not pass after item is consumed");
162*51c0b2f7Stbbdev 
163*51c0b2f7Stbbdev     remove_edge(c, jn);
164*51c0b2f7Stbbdev 
165*51c0b2f7Stbbdev     c.try_put(continue_msg());
166*51c0b2f7Stbbdev     g.wait_for_all();
167*51c0b2f7Stbbdev 
168*51c0b2f7Stbbdev     tmp = tuple<int>(0);
169*51c0b2f7Stbbdev     CHECK_MESSAGE( (q.try_get(tmp)== false), "Message should not pass when edge doesn't exist");
170*51c0b2f7Stbbdev     CHECK_MESSAGE( (tmp == tuple<int>(0)), "Value should not be altered");
171*51c0b2f7Stbbdev }
172*51c0b2f7Stbbdev 
173*51c0b2f7Stbbdev //! Test join_node key matching behavior
174*51c0b2f7Stbbdev //! \brief \ref requirement
175*51c0b2f7Stbbdev TEST_CASE("join_node key_matching"){
176*51c0b2f7Stbbdev     graph g;
177*51c0b2f7Stbbdev     auto body1 = [](const continue_msg &) -> int { return 1; };
178*51c0b2f7Stbbdev     auto body2 = [](const double &val) -> int { return int(val); };
179*51c0b2f7Stbbdev 
180*51c0b2f7Stbbdev     join_node<std::tuple<continue_msg, double>, key_matching<int>> jn(g, body1, body2);
181*51c0b2f7Stbbdev 
182*51c0b2f7Stbbdev     input_port<0>(jn).try_put(continue_msg());
183*51c0b2f7Stbbdev     input_port<1>(jn).try_put(1.3);
184*51c0b2f7Stbbdev 
185*51c0b2f7Stbbdev     g.wait_for_all( );
186*51c0b2f7Stbbdev 
187*51c0b2f7Stbbdev     tuple<continue_msg, double> tmp;
188*51c0b2f7Stbbdev     CHECK_MESSAGE( (jn.try_get(tmp) == true), "Mapped keys should match");
189*51c0b2f7Stbbdev }
190