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