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 
26*51c0b2f7Stbbdev #include "conformance_flowgraph.h"
27*51c0b2f7Stbbdev 
28*51c0b2f7Stbbdev //! \file conformance_input_node.cpp
29*51c0b2f7Stbbdev //! \brief Test for [flow_graph.input_node] specification
30*51c0b2f7Stbbdev 
31*51c0b2f7Stbbdev /*
32*51c0b2f7Stbbdev TODO: implement missing conformance tests for input_node:
33*51c0b2f7Stbbdev   - [ ] The `copy_body' function copies altered body (e.g. after its successful invocation).
34*51c0b2f7Stbbdev   - [ ] Check that in `test_forwarding' the value passed is the actual one received.
35*51c0b2f7Stbbdev   - [ ] Improve CTAD test to assert result node type.
36*51c0b2f7Stbbdev   - [ ] Explicit test for copy constructor of the node.
37*51c0b2f7Stbbdev   - [ ] Check `Output' type indeed copy-constructed and copy-assigned while working with the node.
38*51c0b2f7Stbbdev   - [ ] Check node cannot have predecessors (Will ADL be of any help here?)
39*51c0b2f7Stbbdev   - [ ] Check the node is serial and its body never invoked concurrently.
40*51c0b2f7Stbbdev   - [ ] `try_get()' call testing: a call to body is made only when the internal buffer is empty.
41*51c0b2f7Stbbdev */
42*51c0b2f7Stbbdev 
43*51c0b2f7Stbbdev std::atomic<size_t> global_execute_count;
44*51c0b2f7Stbbdev 
45*51c0b2f7Stbbdev template<typename OutputType>
46*51c0b2f7Stbbdev struct input_functor {
47*51c0b2f7Stbbdev     const size_t n;
48*51c0b2f7Stbbdev 
49*51c0b2f7Stbbdev     input_functor( ) : n(10) { }
50*51c0b2f7Stbbdev     input_functor( const input_functor &f ) : n(f.n) {  }
51*51c0b2f7Stbbdev     void operator=(const input_functor &f) { n = f.n; }
52*51c0b2f7Stbbdev 
53*51c0b2f7Stbbdev     OutputType operator()( tbb::flow_control & fc ) {
54*51c0b2f7Stbbdev        ++global_execute_count;
55*51c0b2f7Stbbdev        if(global_execute_count > n){
56*51c0b2f7Stbbdev            fc.stop();
57*51c0b2f7Stbbdev            return OutputType();
58*51c0b2f7Stbbdev        }
59*51c0b2f7Stbbdev        return OutputType(global_execute_count.load());
60*51c0b2f7Stbbdev     }
61*51c0b2f7Stbbdev 
62*51c0b2f7Stbbdev };
63*51c0b2f7Stbbdev 
64*51c0b2f7Stbbdev template<typename O>
65*51c0b2f7Stbbdev struct CopyCounterBody{
66*51c0b2f7Stbbdev     size_t copy_count;
67*51c0b2f7Stbbdev 
68*51c0b2f7Stbbdev     CopyCounterBody():
69*51c0b2f7Stbbdev         copy_count(0) {}
70*51c0b2f7Stbbdev 
71*51c0b2f7Stbbdev     CopyCounterBody(const CopyCounterBody<O>& other):
72*51c0b2f7Stbbdev         copy_count(other.copy_count + 1) {}
73*51c0b2f7Stbbdev 
74*51c0b2f7Stbbdev     CopyCounterBody& operator=(const CopyCounterBody<O>& other) {
75*51c0b2f7Stbbdev         copy_count = other.copy_count + 1; return *this;
76*51c0b2f7Stbbdev     }
77*51c0b2f7Stbbdev 
78*51c0b2f7Stbbdev     O operator()(tbb::flow_control & fc){
79*51c0b2f7Stbbdev         fc.stop();
80*51c0b2f7Stbbdev         return O();
81*51c0b2f7Stbbdev     }
82*51c0b2f7Stbbdev };
83*51c0b2f7Stbbdev 
84*51c0b2f7Stbbdev 
85*51c0b2f7Stbbdev void test_input_body(){
86*51c0b2f7Stbbdev     tbb::flow::graph g;
87*51c0b2f7Stbbdev     input_functor<int> fun;
88*51c0b2f7Stbbdev 
89*51c0b2f7Stbbdev     global_execute_count = 0;
90*51c0b2f7Stbbdev     tbb::flow::input_node<int> node1(g, fun);
91*51c0b2f7Stbbdev     test_push_receiver<int> node2(g);
92*51c0b2f7Stbbdev 
93*51c0b2f7Stbbdev     tbb::flow::make_edge(node1, node2);
94*51c0b2f7Stbbdev 
95*51c0b2f7Stbbdev     node1.activate();
96*51c0b2f7Stbbdev     g.wait_for_all();
97*51c0b2f7Stbbdev 
98*51c0b2f7Stbbdev     CHECK_MESSAGE( (get_count(node2) == 10), "Descendant of the node needs to be receive N messages");
99*51c0b2f7Stbbdev     CHECK_MESSAGE( (global_execute_count == 10 + 1), "Body of the node needs to be executed N + 1 times");
100*51c0b2f7Stbbdev }
101*51c0b2f7Stbbdev 
102*51c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
103*51c0b2f7Stbbdev void test_deduction_guides(){
104*51c0b2f7Stbbdev     tbb::flow::graph g;
105*51c0b2f7Stbbdev     input_functor<int> fun;
106*51c0b2f7Stbbdev     tbb::flow::input_node node1(g, fun);
107*51c0b2f7Stbbdev }
108*51c0b2f7Stbbdev #endif
109*51c0b2f7Stbbdev 
110*51c0b2f7Stbbdev void test_buffering(){
111*51c0b2f7Stbbdev     tbb::flow::graph g;
112*51c0b2f7Stbbdev     input_functor<int> fun;
113*51c0b2f7Stbbdev     global_execute_count = 0;
114*51c0b2f7Stbbdev 
115*51c0b2f7Stbbdev     tbb::flow::input_node<int> source(g, fun);
116*51c0b2f7Stbbdev     tbb::flow::limiter_node<int> rejecter(g, 0);
117*51c0b2f7Stbbdev 
118*51c0b2f7Stbbdev     tbb::flow::make_edge(source, rejecter);
119*51c0b2f7Stbbdev     source.activate();
120*51c0b2f7Stbbdev     g.wait_for_all();
121*51c0b2f7Stbbdev 
122*51c0b2f7Stbbdev     int tmp = -1;
123*51c0b2f7Stbbdev     CHECK_MESSAGE( (source.try_get(tmp) == true), "try_get after rejection should succeed");
124*51c0b2f7Stbbdev     CHECK_MESSAGE( (tmp == 1), "try_get should return correct value");
125*51c0b2f7Stbbdev }
126*51c0b2f7Stbbdev 
127*51c0b2f7Stbbdev void test_forwarding(){
128*51c0b2f7Stbbdev     tbb::flow::graph g;
129*51c0b2f7Stbbdev     input_functor<int> fun;
130*51c0b2f7Stbbdev 
131*51c0b2f7Stbbdev     global_execute_count = 0;
132*51c0b2f7Stbbdev     tbb::flow::input_node<int> node1(g, fun);
133*51c0b2f7Stbbdev     test_push_receiver<int> node2(g);
134*51c0b2f7Stbbdev     test_push_receiver<int> node3(g);
135*51c0b2f7Stbbdev 
136*51c0b2f7Stbbdev     tbb::flow::make_edge(node1, node2);
137*51c0b2f7Stbbdev     tbb::flow::make_edge(node1, node3);
138*51c0b2f7Stbbdev 
139*51c0b2f7Stbbdev     node1.activate();
140*51c0b2f7Stbbdev     g.wait_for_all();
141*51c0b2f7Stbbdev 
142*51c0b2f7Stbbdev     CHECK_MESSAGE( (get_count(node2) == 10), "Descendant of the node needs to be receive N messages");
143*51c0b2f7Stbbdev     CHECK_MESSAGE( (get_count(node3) == 10), "Descendant of the node needs to be receive N messages");
144*51c0b2f7Stbbdev }
145*51c0b2f7Stbbdev 
146*51c0b2f7Stbbdev template<typename O>
147*51c0b2f7Stbbdev void test_inheritance(){
148*51c0b2f7Stbbdev     using namespace tbb::flow;
149*51c0b2f7Stbbdev 
150*51c0b2f7Stbbdev     CHECK_MESSAGE( (std::is_base_of<graph_node, input_node<O>>::value), "input_node should be derived from graph_node");
151*51c0b2f7Stbbdev     CHECK_MESSAGE( (std::is_base_of<sender<O>, input_node<O>>::value), "input_node should be derived from sender<Output>");
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     input_node<int> fn(g, b);
161*51c0b2f7Stbbdev 
162*51c0b2f7Stbbdev     CopyCounterBody<int> b2 = copy_body<CopyCounterBody<int>, input_node<int>>(fn);
163*51c0b2f7Stbbdev 
164*51c0b2f7Stbbdev     CHECK_MESSAGE( (b.copy_count + 2 <= b2.copy_count), "copy_body and constructor should copy bodies");
165*51c0b2f7Stbbdev }
166*51c0b2f7Stbbdev 
167*51c0b2f7Stbbdev //! Test body copying and copy_body logic
168*51c0b2f7Stbbdev //! \brief \ref interface
169*51c0b2f7Stbbdev TEST_CASE("input_node and body copying"){
170*51c0b2f7Stbbdev     test_copies();
171*51c0b2f7Stbbdev }
172*51c0b2f7Stbbdev 
173*51c0b2f7Stbbdev //! Test inheritance relations
174*51c0b2f7Stbbdev //! \brief \ref interface
175*51c0b2f7Stbbdev TEST_CASE("input_node superclasses"){
176*51c0b2f7Stbbdev     test_inheritance<int>();
177*51c0b2f7Stbbdev     test_inheritance<void*>();
178*51c0b2f7Stbbdev }
179*51c0b2f7Stbbdev 
180*51c0b2f7Stbbdev //! Test input_node forwarding
181*51c0b2f7Stbbdev //! \brief \ref requirement
182*51c0b2f7Stbbdev TEST_CASE("input_node forwarding"){
183*51c0b2f7Stbbdev     test_forwarding();
184*51c0b2f7Stbbdev }
185*51c0b2f7Stbbdev 
186*51c0b2f7Stbbdev //! Test input_node buffering
187*51c0b2f7Stbbdev //! \brief \ref requirement
188*51c0b2f7Stbbdev TEST_CASE("input_node buffering"){
189*51c0b2f7Stbbdev     test_buffering();
190*51c0b2f7Stbbdev }
191*51c0b2f7Stbbdev 
192*51c0b2f7Stbbdev //! Test calling input_node body
193*51c0b2f7Stbbdev //! \brief \ref interface \ref requirement
194*51c0b2f7Stbbdev TEST_CASE("input_node body") {
195*51c0b2f7Stbbdev     test_input_body();
196*51c0b2f7Stbbdev }
197*51c0b2f7Stbbdev 
198*51c0b2f7Stbbdev //! Test deduction guides
199*51c0b2f7Stbbdev //! \brief \ref interface \ref requirement
200*51c0b2f7Stbbdev TEST_CASE("Deduction guides"){
201*51c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
202*51c0b2f7Stbbdev     test_deduction_guides();
203*51c0b2f7Stbbdev #endif
204*51c0b2f7Stbbdev }
205