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