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 #define CONFORMANCE_INPUT_NODE
22 
23 #include "conformance_flowgraph.h"
24 
25 //! \file conformance_input_node.cpp
26 //! \brief Test for [flow_graph.input_node] specification
27 
28 using output_msg = conformance::message<true, true, true>;
29 
30 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
31 int input_body_f(tbb::flow_control&) { return 42; }
32 
33 void test_deduction_guides() {
34     using namespace tbb::flow;
35     graph g;
36 
37     auto lambda = [](tbb::flow_control&) { return 42; };
38     auto non_const_lambda = [](tbb::flow_control&) mutable { return 42; };
39 
40     // Tests for input_node(graph&, Body)
41     input_node s1(g, lambda);
42     static_assert(std::is_same_v<decltype(s1), input_node<int>>);
43 
44     input_node s2(g, non_const_lambda);
45     static_assert(std::is_same_v<decltype(s2), input_node<int>>);
46 
47     input_node s3(g, input_body_f);
48     static_assert(std::is_same_v<decltype(s3), input_node<int>>);
49 
50     input_node s4(s3);
51     static_assert(std::is_same_v<decltype(s4), input_node<int>>);
52 
53 #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
54     broadcast_node<int> bc(g);
55 
56     // Tests for input_node(const node_set<Args...>&, Body)
57     input_node s5(precedes(bc), lambda);
58     static_assert(std::is_same_v<decltype(s5), input_node<int>>);
59 
60     input_node s6(precedes(bc), non_const_lambda);
61     static_assert(std::is_same_v<decltype(s6), input_node<int>>);
62 
63     input_node s7(precedes(bc), input_body_f);
64     static_assert(std::is_same_v<decltype(s7), input_node<int>>);
65 #endif
66     g.wait_for_all();
67 }
68 
69 #endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
70 
71 template<typename O>
72 void test_inheritance(){
73     using namespace oneapi::tbb::flow;
74 
75     CHECK_MESSAGE((std::is_base_of<graph_node, input_node<O>>::value), "input_node should be derived from graph_node");
76     CHECK_MESSAGE((std::is_base_of<sender<O>, input_node<O>>::value), "input_node should be derived from sender<Output>");
77     CHECK_MESSAGE((!std::is_base_of<receiver<O>, input_node<O>>::value), "input_node cannot have predecessors");
78 }
79 
80 //! Test the body object passed to a node is copied
81 //! \brief \ref interface
82 TEST_CASE("input_node and body copying"){
83     conformance::test_copy_body_function<oneapi::tbb::flow::input_node<int>, conformance::copy_counting_object<int>>();
84 }
85 
86 //! The node that is constructed has a reference to the same graph object as src,
87 //! has a copy of the initial body used by src.
88 //! The successors of src are not copied.
89 //! \brief \ref requirement
90 TEST_CASE("input_node copy constructor"){
91     using namespace oneapi::tbb::flow;
92     graph g;
93 
94     conformance::copy_counting_object<output_msg> fun2;
95 
96     input_node<output_msg> node1(g, fun2);
97     conformance::test_push_receiver<output_msg> node2(g);
98     conformance::test_push_receiver<output_msg> node3(g);
99 
100     oneapi::tbb::flow::make_edge(node1, node2);
101 
102     input_node<output_msg> node_copy(node1);
103 
104     conformance::copy_counting_object<output_msg> b2 = copy_body<conformance::copy_counting_object<output_msg>, input_node<output_msg>>(node_copy);
105 
106     CHECK_MESSAGE((fun2.copy_count + 1 < b2.copy_count), "constructor should copy bodies");
107 
108     oneapi::tbb::flow::make_edge(node_copy, node3);
109 
110     node_copy.activate();
111     g.wait_for_all();
112 
113     CHECK_MESSAGE((conformance::get_values(node2).size() == 0 && conformance::get_values(node3).size() == 1), "Copied node doesn`t copy successor");
114 
115     node1.activate();
116     g.wait_for_all();
117 
118     CHECK_MESSAGE((conformance::get_values(node2).size() == 1 && conformance::get_values(node3).size() == 0), "Copied node doesn`t copy successor");
119 }
120 
121 //! Test inheritance relations
122 //! \brief \ref interface
123 TEST_CASE("input_node superclasses"){
124     test_inheritance<int>();
125     test_inheritance<void*>();
126     test_inheritance<output_msg>();
127 }
128 
129 //! Test input_node forwarding
130 //! \brief \ref requirement
131 TEST_CASE("input_node forwarding"){
132     conformance::counting_functor<output_msg> fun(conformance::expected);
133     conformance::test_forwarding<oneapi::tbb::flow::input_node<output_msg>, void, output_msg>(5, fun);
134 }
135 
136 //! Test input_node buffering
137 //! \brief \ref requirement
138 TEST_CASE("input_node buffering"){
139     conformance::dummy_functor<int> fun;
140     conformance::test_buffering<oneapi::tbb::flow::input_node<int>, int>(fun);
141 }
142 
143 //! Test calling input_node body
144 //! \brief \ref interface \ref requirement
145 TEST_CASE("input_node body") {
146     oneapi::tbb::flow::graph g;
147     constexpr std::size_t counting_threshold = 10;
148     conformance::counting_functor<output_msg> fun(counting_threshold);
149 
150     oneapi::tbb::flow::input_node<output_msg> node1(g, fun);
151     conformance::test_push_receiver<output_msg> node2(g);
152 
153     oneapi::tbb::flow::make_edge(node1, node2);
154 
155     node1.activate();
156     g.wait_for_all();
157 
158     CHECK_MESSAGE((conformance::get_values(node2).size() == counting_threshold), "Descendant of the node needs to be receive N messages");
159     CHECK_MESSAGE((fun.execute_count == counting_threshold + 1), "Body of the node needs to be executed N + 1 times");
160 }
161 
162 //! Test deduction guides
163 //! \brief \ref interface \ref requirement
164 TEST_CASE("Deduction guides"){
165 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
166     test_deduction_guides();
167 #endif
168 }
169 
170 //! Test that measured concurrency respects set limits
171 //! \brief \ref requirement
172 TEST_CASE("concurrency follows set limits"){
173     oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism,
174                                   oneapi::tbb::this_task_arena::max_concurrency());
175 
176 
177     utils::ConcurrencyTracker::Reset();
178     oneapi::tbb::flow::graph g;
179     conformance::concurrency_peak_checker_body counter(1);
180     oneapi::tbb::flow::input_node<int> testing_node(g, counter);
181 
182     conformance::test_push_receiver<int> sink(g);
183 
184     make_edge(testing_node, sink);
185     testing_node.activate();
186 
187     g.wait_for_all();
188 }
189 
190 //! Test node Output class meet the CopyConstructible requirements.
191 //! \brief \ref interface \ref requirement
192 TEST_CASE("Test input_node Output class") {
193     conformance::test_output_class<oneapi::tbb::flow::input_node<conformance::copy_counting_object<int>>>();
194 }
195 
196 struct input_node_counter{
197     static int count;
198     int N;
199     input_node_counter(int n) : N(n){};
200 
201     int operator()( oneapi::tbb::flow_control & fc ) {
202        ++count;
203        if(count > N){
204            fc.stop();
205            return N;
206        }
207        return N;
208     }
209 };
210 
211 struct function_node_counter{
212     static int count;
213 
214     int operator()( int ) {
215         ++count;
216         utils::doDummyWork(1000000);
217         CHECK_MESSAGE((input_node_counter::count <= function_node_counter::count + 1), "input_node `try_get()' call testing: a call to body is made only when the internal buffer is empty");
218         return 1;
219     }
220 };
221 
222 int input_node_counter::count = 0;
223 int function_node_counter::count = 0;
224 
225 //! Test input_node `try_get()' call testing: a call to body is made only when the internal buffer is empty.
226 //! \brief \ref requirement
227 TEST_CASE("input_node `try_get()' call testing: a call to body is made only when the internal buffer is empty.") {
228     oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, 1);
229     oneapi::tbb::flow::graph g;
230     input_node_counter fun1(500);
231     function_node_counter fun2;
232 
233     oneapi::tbb::flow::function_node <int, int, oneapi::tbb::flow::rejecting> fnode(g, oneapi::tbb::flow::serial, fun2);
234     oneapi::tbb::flow::input_node<int> testing_node(g, fun1);
235 
236     make_edge(testing_node, fnode);
237     testing_node.activate();
238 
239     g.wait_for_all();
240 }
241