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 #include "common/test.h"
18*51c0b2f7Stbbdev 
19*51c0b2f7Stbbdev #include "common/utils.h"
20*51c0b2f7Stbbdev #include "common/graph_utils.h"
21*51c0b2f7Stbbdev 
22*51c0b2f7Stbbdev #include "tbb/flow_graph.h"
23*51c0b2f7Stbbdev #include "tbb/task_arena.h"
24*51c0b2f7Stbbdev 
25*51c0b2f7Stbbdev #include "tbb/global_control.h"
26*51c0b2f7Stbbdev #include "conformance_flowgraph.h"
27*51c0b2f7Stbbdev 
28*51c0b2f7Stbbdev //! \file conformance_continue_node.cpp
29*51c0b2f7Stbbdev //! \brief Test for [flow_graph.continue_node] specification
30*51c0b2f7Stbbdev 
31*51c0b2f7Stbbdev /*
32*51c0b2f7Stbbdev TODO: implement missing conformance tests for continue_node:
33*51c0b2f7Stbbdev   - [ ] For `test_forwarding' check that the value passed is the actual one received.
34*51c0b2f7Stbbdev   - [ ] The `copy_body' function copies altered body (e.g. after its successful invocation).
35*51c0b2f7Stbbdev   - [ ] Improve CTAD test.
36*51c0b2f7Stbbdev   - [ ] Improve constructors test, including addition of calls to constructors with
37*51c0b2f7Stbbdev     `number_of_predecessors' parameter.
38*51c0b2f7Stbbdev   - [ ] Explicit test for copy constructor of the node.
39*51c0b2f7Stbbdev   - [ ] Rewrite test_priority.
40*51c0b2f7Stbbdev   - [ ] Check `Output' type indeed copy-constructed and copy-assigned while working with the node.
41*51c0b2f7Stbbdev   - [ ] Explicit test for correct working of `number_of_predecessors' constructor parameter,
42*51c0b2f7Stbbdev     including taking it into account when making and removing edges.
43*51c0b2f7Stbbdev   - [ ] Add testing of `try_put' statement. In particular that it does not wait for the execution of
44*51c0b2f7Stbbdev     the body to complete.
45*51c0b2f7Stbbdev */
46*51c0b2f7Stbbdev 
47*51c0b2f7Stbbdev void test_cont_body(){
48*51c0b2f7Stbbdev     tbb::flow::graph g;
49*51c0b2f7Stbbdev     inc_functor<int> cf;
50*51c0b2f7Stbbdev     cf.execute_count = 0;
51*51c0b2f7Stbbdev 
52*51c0b2f7Stbbdev     tbb::flow::continue_node<int> node1(g, cf);
53*51c0b2f7Stbbdev 
54*51c0b2f7Stbbdev     const size_t n = 10;
55*51c0b2f7Stbbdev     for(size_t i = 0; i < n; ++i) {
56*51c0b2f7Stbbdev         CHECK_MESSAGE((node1.try_put(tbb::flow::continue_msg()) == true),
57*51c0b2f7Stbbdev                       "continue_node::try_put() should never reject a message.");
58*51c0b2f7Stbbdev     }
59*51c0b2f7Stbbdev     g.wait_for_all();
60*51c0b2f7Stbbdev 
61*51c0b2f7Stbbdev     CHECK_MESSAGE( (cf.execute_count == n), "Body of the first node needs to be executed N times");
62*51c0b2f7Stbbdev }
63*51c0b2f7Stbbdev 
64*51c0b2f7Stbbdev template<typename O>
65*51c0b2f7Stbbdev void test_inheritance(){
66*51c0b2f7Stbbdev     using namespace tbb::flow;
67*51c0b2f7Stbbdev 
68*51c0b2f7Stbbdev     CHECK_MESSAGE( (std::is_base_of<graph_node, continue_node<O>>::value), "continue_node should be derived from graph_node");
69*51c0b2f7Stbbdev     CHECK_MESSAGE( (std::is_base_of<receiver<continue_msg>, continue_node<O>>::value), "continue_node should be derived from receiver<Input>");
70*51c0b2f7Stbbdev     CHECK_MESSAGE( (std::is_base_of<sender<O>, continue_node<O>>::value), "continue_node should be derived from sender<Output>");
71*51c0b2f7Stbbdev }
72*51c0b2f7Stbbdev 
73*51c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
74*51c0b2f7Stbbdev void test_deduction_guides(){
75*51c0b2f7Stbbdev     tbb::flow::graph g;
76*51c0b2f7Stbbdev     inc_functor<int> fun;
77*51c0b2f7Stbbdev     tbb::flow::continue_node node1(g, fun);
78*51c0b2f7Stbbdev }
79*51c0b2f7Stbbdev #endif
80*51c0b2f7Stbbdev 
81*51c0b2f7Stbbdev void test_forwarding(){
82*51c0b2f7Stbbdev     tbb::flow::graph g;
83*51c0b2f7Stbbdev     inc_functor<int> fun;
84*51c0b2f7Stbbdev     fun.execute_count = 0;
85*51c0b2f7Stbbdev 
86*51c0b2f7Stbbdev     tbb::flow::continue_node<int> node1(g, fun);
87*51c0b2f7Stbbdev     test_push_receiver<int> node2(g);
88*51c0b2f7Stbbdev     test_push_receiver<int> node3(g);
89*51c0b2f7Stbbdev 
90*51c0b2f7Stbbdev     tbb::flow::make_edge(node1, node2);
91*51c0b2f7Stbbdev     tbb::flow::make_edge(node1, node3);
92*51c0b2f7Stbbdev 
93*51c0b2f7Stbbdev     node1.try_put(tbb::flow::continue_msg());
94*51c0b2f7Stbbdev     g.wait_for_all();
95*51c0b2f7Stbbdev 
96*51c0b2f7Stbbdev     CHECK_MESSAGE( (get_count(node2) == 1), "Descendant of the node must receive one message.");
97*51c0b2f7Stbbdev     CHECK_MESSAGE( (get_count(node3) == 1), "Descendant of the node must receive one message.");
98*51c0b2f7Stbbdev }
99*51c0b2f7Stbbdev 
100*51c0b2f7Stbbdev void test_buffering(){
101*51c0b2f7Stbbdev     tbb::flow::graph g;
102*51c0b2f7Stbbdev     inc_functor<int> fun;
103*51c0b2f7Stbbdev 
104*51c0b2f7Stbbdev     tbb::flow::continue_node<int> node(g, fun);
105*51c0b2f7Stbbdev     tbb::flow::limiter_node<int> rejecter(g, 0);
106*51c0b2f7Stbbdev 
107*51c0b2f7Stbbdev     tbb::flow::make_edge(node, rejecter);
108*51c0b2f7Stbbdev     node.try_put(tbb::flow::continue_msg());
109*51c0b2f7Stbbdev 
110*51c0b2f7Stbbdev     int tmp = -1;
111*51c0b2f7Stbbdev     CHECK_MESSAGE( (node.try_get(tmp) == false), "try_get after rejection should not succeed");
112*51c0b2f7Stbbdev     CHECK_MESSAGE( (tmp == -1), "try_get after rejection should not alter passed value");
113*51c0b2f7Stbbdev     g.wait_for_all();
114*51c0b2f7Stbbdev }
115*51c0b2f7Stbbdev 
116*51c0b2f7Stbbdev void test_policy_ctors(){
117*51c0b2f7Stbbdev     using namespace tbb::flow;
118*51c0b2f7Stbbdev     graph g;
119*51c0b2f7Stbbdev 
120*51c0b2f7Stbbdev     inc_functor<int> fun;
121*51c0b2f7Stbbdev 
122*51c0b2f7Stbbdev     continue_node<int, lightweight> lw_node(g, fun);
123*51c0b2f7Stbbdev }
124*51c0b2f7Stbbdev 
125*51c0b2f7Stbbdev void test_ctors(){
126*51c0b2f7Stbbdev     using namespace tbb::flow;
127*51c0b2f7Stbbdev     graph g;
128*51c0b2f7Stbbdev 
129*51c0b2f7Stbbdev     inc_functor<int> fun;
130*51c0b2f7Stbbdev 
131*51c0b2f7Stbbdev     continue_node<int> proto1(g, 2, fun, tbb::flow::node_priority_t(1));
132*51c0b2f7Stbbdev }
133*51c0b2f7Stbbdev 
134*51c0b2f7Stbbdev template<typename O>
135*51c0b2f7Stbbdev struct CopyCounterBody{
136*51c0b2f7Stbbdev     size_t copy_count;
137*51c0b2f7Stbbdev 
138*51c0b2f7Stbbdev     CopyCounterBody():
139*51c0b2f7Stbbdev         copy_count(0) {}
140*51c0b2f7Stbbdev 
141*51c0b2f7Stbbdev     CopyCounterBody(const CopyCounterBody<O>& other):
142*51c0b2f7Stbbdev         copy_count(other.copy_count + 1) {}
143*51c0b2f7Stbbdev 
144*51c0b2f7Stbbdev     CopyCounterBody& operator=(const CopyCounterBody<O>& other){
145*51c0b2f7Stbbdev         copy_count = other.copy_count + 1;
146*51c0b2f7Stbbdev         return *this;
147*51c0b2f7Stbbdev     }
148*51c0b2f7Stbbdev 
149*51c0b2f7Stbbdev     O operator()(tbb::flow::continue_msg){
150*51c0b2f7Stbbdev         return 1;
151*51c0b2f7Stbbdev     }
152*51c0b2f7Stbbdev };
153*51c0b2f7Stbbdev 
154*51c0b2f7Stbbdev void test_copies(){
155*51c0b2f7Stbbdev     using namespace tbb::flow;
156*51c0b2f7Stbbdev 
157*51c0b2f7Stbbdev     CopyCounterBody<int> b;
158*51c0b2f7Stbbdev 
159*51c0b2f7Stbbdev     graph g;
160*51c0b2f7Stbbdev     continue_node<int> fn(g, b);
161*51c0b2f7Stbbdev 
162*51c0b2f7Stbbdev     CopyCounterBody<int> b2 = copy_body<CopyCounterBody<int>,
163*51c0b2f7Stbbdev                                              continue_node<int>>(fn);
164*51c0b2f7Stbbdev 
165*51c0b2f7Stbbdev     CHECK_MESSAGE( (b.copy_count + 2 <= b2.copy_count), "copy_body and constructor should copy bodies");
166*51c0b2f7Stbbdev }
167*51c0b2f7Stbbdev 
168*51c0b2f7Stbbdev 
169*51c0b2f7Stbbdev void test_priority(){
170*51c0b2f7Stbbdev     size_t concurrency_limit = 1;
171*51c0b2f7Stbbdev     tbb::global_control control(tbb::global_control::max_allowed_parallelism, concurrency_limit);
172*51c0b2f7Stbbdev 
173*51c0b2f7Stbbdev     tbb::flow::graph g;
174*51c0b2f7Stbbdev 
175*51c0b2f7Stbbdev     tbb::flow::continue_node<tbb::flow::continue_msg> source(g,
176*51c0b2f7Stbbdev                                                              [](tbb::flow::continue_msg){ return tbb::flow::continue_msg();});
177*51c0b2f7Stbbdev     source.try_put(tbb::flow::continue_msg());
178*51c0b2f7Stbbdev 
179*51c0b2f7Stbbdev     first_functor<int>::first_id = -1;
180*51c0b2f7Stbbdev     first_functor<int> low_functor(1);
181*51c0b2f7Stbbdev     first_functor<int> high_functor(2);
182*51c0b2f7Stbbdev 
183*51c0b2f7Stbbdev     tbb::flow::continue_node<int, int> high(g, high_functor, tbb::flow::node_priority_t(1));
184*51c0b2f7Stbbdev     tbb::flow::continue_node<int, int> low(g, low_functor);
185*51c0b2f7Stbbdev 
186*51c0b2f7Stbbdev     make_edge(source, low);
187*51c0b2f7Stbbdev     make_edge(source, high);
188*51c0b2f7Stbbdev 
189*51c0b2f7Stbbdev     g.wait_for_all();
190*51c0b2f7Stbbdev 
191*51c0b2f7Stbbdev     CHECK_MESSAGE( (first_functor<int>::first_id == 2), "High priority node should execute first");
192*51c0b2f7Stbbdev }
193*51c0b2f7Stbbdev 
194*51c0b2f7Stbbdev //! Test node costructors
195*51c0b2f7Stbbdev //! \brief \ref requirement
196*51c0b2f7Stbbdev TEST_CASE("continue_node constructors"){
197*51c0b2f7Stbbdev     test_ctors();
198*51c0b2f7Stbbdev }
199*51c0b2f7Stbbdev 
200*51c0b2f7Stbbdev //! Test priorities work in single-threaded configuration
201*51c0b2f7Stbbdev //! \brief \ref requirement
202*51c0b2f7Stbbdev TEST_CASE("continue_node priority support"){
203*51c0b2f7Stbbdev     test_priority();
204*51c0b2f7Stbbdev }
205*51c0b2f7Stbbdev 
206*51c0b2f7Stbbdev //! Test body copying and copy_body logic
207*51c0b2f7Stbbdev //! \brief \ref interface
208*51c0b2f7Stbbdev TEST_CASE("continue_node and body copying"){
209*51c0b2f7Stbbdev     test_copies();
210*51c0b2f7Stbbdev }
211*51c0b2f7Stbbdev 
212*51c0b2f7Stbbdev //! Test constructors
213*51c0b2f7Stbbdev //! \brief \ref interface
214*51c0b2f7Stbbdev TEST_CASE("continue_node constructors"){
215*51c0b2f7Stbbdev     test_policy_ctors();
216*51c0b2f7Stbbdev }
217*51c0b2f7Stbbdev 
218*51c0b2f7Stbbdev //! Test continue_node buffering
219*51c0b2f7Stbbdev //! \brief \ref requirement
220*51c0b2f7Stbbdev TEST_CASE("continue_node buffering"){
221*51c0b2f7Stbbdev     test_buffering();
222*51c0b2f7Stbbdev }
223*51c0b2f7Stbbdev 
224*51c0b2f7Stbbdev //! Test function_node broadcasting
225*51c0b2f7Stbbdev //! \brief \ref requirement
226*51c0b2f7Stbbdev TEST_CASE("continue_node broadcast"){
227*51c0b2f7Stbbdev     test_forwarding();
228*51c0b2f7Stbbdev }
229*51c0b2f7Stbbdev 
230*51c0b2f7Stbbdev //! Test deduction guides
231*51c0b2f7Stbbdev //! \brief \ref interface \ref requirement
232*51c0b2f7Stbbdev TEST_CASE("Deduction guides"){
233*51c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
234*51c0b2f7Stbbdev     test_deduction_guides();
235*51c0b2f7Stbbdev #endif
236*51c0b2f7Stbbdev }
237*51c0b2f7Stbbdev 
238*51c0b2f7Stbbdev //! Test inheritance relations
239*51c0b2f7Stbbdev //! \brief \ref interface
240*51c0b2f7Stbbdev TEST_CASE("continue_node superclasses"){
241*51c0b2f7Stbbdev     test_inheritance<int>();
242*51c0b2f7Stbbdev     test_inheritance<void*>();
243*51c0b2f7Stbbdev }
244*51c0b2f7Stbbdev 
245*51c0b2f7Stbbdev //! Test body execution
246*51c0b2f7Stbbdev //! \brief \ref interface \ref requirement
247*51c0b2f7Stbbdev TEST_CASE("continue body") {
248*51c0b2f7Stbbdev     test_cont_body();
249*51c0b2f7Stbbdev }
250