151c0b2f7Stbbdev /*
2b15aabb3Stbbdev Copyright (c) 2020-2021 Intel Corporation
351c0b2f7Stbbdev
451c0b2f7Stbbdev Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev you may not use this file except in compliance with the License.
651c0b2f7Stbbdev You may obtain a copy of the License at
751c0b2f7Stbbdev
851c0b2f7Stbbdev http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev
1051c0b2f7Stbbdev Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev See the License for the specific language governing permissions and
1451c0b2f7Stbbdev limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev
17b15aabb3Stbbdev #if __INTEL_COMPILER && _MSC_VER
18b15aabb3Stbbdev #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
19b15aabb3Stbbdev #endif
2051c0b2f7Stbbdev
21*de0109beSIlya Mishin #define CONFORMANCE_INPUT_NODE
2251c0b2f7Stbbdev
2351c0b2f7Stbbdev #include "conformance_flowgraph.h"
2451c0b2f7Stbbdev
2551c0b2f7Stbbdev //! \file conformance_input_node.cpp
2651c0b2f7Stbbdev //! \brief Test for [flow_graph.input_node] specification
2751c0b2f7Stbbdev
28*de0109beSIlya Mishin using output_msg = conformance::message<true, true, true>;
2951c0b2f7Stbbdev
3051c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
input_body_f(tbb::flow_control &)31*de0109beSIlya Mishin int input_body_f(tbb::flow_control&) { return 42; }
32*de0109beSIlya Mishin
test_deduction_guides()3351c0b2f7Stbbdev void test_deduction_guides() {
34*de0109beSIlya Mishin using namespace tbb::flow;
35*de0109beSIlya Mishin graph g;
36*de0109beSIlya Mishin
37*de0109beSIlya Mishin auto lambda = [](tbb::flow_control&) { return 42; };
38*de0109beSIlya Mishin auto non_const_lambda = [](tbb::flow_control&) mutable { return 42; };
39*de0109beSIlya Mishin
40*de0109beSIlya Mishin // Tests for input_node(graph&, Body)
41*de0109beSIlya Mishin input_node s1(g, lambda);
42*de0109beSIlya Mishin static_assert(std::is_same_v<decltype(s1), input_node<int>>);
43*de0109beSIlya Mishin
44*de0109beSIlya Mishin input_node s2(g, non_const_lambda);
45*de0109beSIlya Mishin static_assert(std::is_same_v<decltype(s2), input_node<int>>);
46*de0109beSIlya Mishin
47*de0109beSIlya Mishin input_node s3(g, input_body_f);
48*de0109beSIlya Mishin static_assert(std::is_same_v<decltype(s3), input_node<int>>);
49*de0109beSIlya Mishin
50*de0109beSIlya Mishin input_node s4(s3);
51*de0109beSIlya Mishin static_assert(std::is_same_v<decltype(s4), input_node<int>>);
52*de0109beSIlya Mishin
53*de0109beSIlya Mishin #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
54*de0109beSIlya Mishin broadcast_node<int> bc(g);
55*de0109beSIlya Mishin
56*de0109beSIlya Mishin // Tests for input_node(const node_set<Args...>&, Body)
57*de0109beSIlya Mishin input_node s5(precedes(bc), lambda);
58*de0109beSIlya Mishin static_assert(std::is_same_v<decltype(s5), input_node<int>>);
59*de0109beSIlya Mishin
60*de0109beSIlya Mishin input_node s6(precedes(bc), non_const_lambda);
61*de0109beSIlya Mishin static_assert(std::is_same_v<decltype(s6), input_node<int>>);
62*de0109beSIlya Mishin
63*de0109beSIlya Mishin input_node s7(precedes(bc), input_body_f);
64*de0109beSIlya Mishin static_assert(std::is_same_v<decltype(s7), input_node<int>>);
6551c0b2f7Stbbdev #endif
6651c0b2f7Stbbdev g.wait_for_all();
6751c0b2f7Stbbdev }
6851c0b2f7Stbbdev
69*de0109beSIlya Mishin #endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
7051c0b2f7Stbbdev
7151c0b2f7Stbbdev template<typename O>
test_inheritance()7251c0b2f7Stbbdev void test_inheritance(){
7349e08aacStbbdev using namespace oneapi::tbb::flow;
7451c0b2f7Stbbdev
7551c0b2f7Stbbdev CHECK_MESSAGE((std::is_base_of<graph_node, input_node<O>>::value), "input_node should be derived from graph_node");
7651c0b2f7Stbbdev CHECK_MESSAGE((std::is_base_of<sender<O>, input_node<O>>::value), "input_node should be derived from sender<Output>");
77*de0109beSIlya Mishin CHECK_MESSAGE((!std::is_base_of<receiver<O>, input_node<O>>::value), "input_node cannot have predecessors");
7851c0b2f7Stbbdev }
7951c0b2f7Stbbdev
80*de0109beSIlya Mishin //! Test the body object passed to a node is copied
8151c0b2f7Stbbdev //! \brief \ref interface
8251c0b2f7Stbbdev TEST_CASE("input_node and body copying"){
83*de0109beSIlya Mishin conformance::test_copy_body_function<oneapi::tbb::flow::input_node<int>, conformance::copy_counting_object<int>>();
84*de0109beSIlya Mishin }
85*de0109beSIlya Mishin
86*de0109beSIlya Mishin //! The node that is constructed has a reference to the same graph object as src,
87*de0109beSIlya Mishin //! has a copy of the initial body used by src.
88*de0109beSIlya Mishin //! The successors of src are not copied.
89*de0109beSIlya Mishin //! \brief \ref requirement
90*de0109beSIlya Mishin TEST_CASE("input_node copy constructor"){
91*de0109beSIlya Mishin using namespace oneapi::tbb::flow;
92*de0109beSIlya Mishin graph g;
93*de0109beSIlya Mishin
94*de0109beSIlya Mishin conformance::copy_counting_object<output_msg> fun2;
95*de0109beSIlya Mishin
96*de0109beSIlya Mishin input_node<output_msg> node1(g, fun2);
97*de0109beSIlya Mishin conformance::test_push_receiver<output_msg> node2(g);
98*de0109beSIlya Mishin conformance::test_push_receiver<output_msg> node3(g);
99*de0109beSIlya Mishin
100*de0109beSIlya Mishin oneapi::tbb::flow::make_edge(node1, node2);
101*de0109beSIlya Mishin
102*de0109beSIlya Mishin input_node<output_msg> node_copy(node1);
103*de0109beSIlya Mishin
104*de0109beSIlya Mishin conformance::copy_counting_object<output_msg> b2 = copy_body<conformance::copy_counting_object<output_msg>, input_node<output_msg>>(node_copy);
105*de0109beSIlya Mishin
106*de0109beSIlya Mishin CHECK_MESSAGE((fun2.copy_count + 1 < b2.copy_count), "constructor should copy bodies");
107*de0109beSIlya Mishin
108*de0109beSIlya Mishin oneapi::tbb::flow::make_edge(node_copy, node3);
109*de0109beSIlya Mishin
110*de0109beSIlya Mishin node_copy.activate();
111*de0109beSIlya Mishin g.wait_for_all();
112*de0109beSIlya Mishin
113*de0109beSIlya Mishin CHECK_MESSAGE((conformance::get_values(node2).size() == 0 && conformance::get_values(node3).size() == 1), "Copied node doesn`t copy successor");
114*de0109beSIlya Mishin
115*de0109beSIlya Mishin node1.activate();
116*de0109beSIlya Mishin g.wait_for_all();
117*de0109beSIlya Mishin
118*de0109beSIlya Mishin CHECK_MESSAGE((conformance::get_values(node2).size() == 1 && conformance::get_values(node3).size() == 0), "Copied node doesn`t copy successor");
11951c0b2f7Stbbdev }
12051c0b2f7Stbbdev
12151c0b2f7Stbbdev //! Test inheritance relations
12251c0b2f7Stbbdev //! \brief \ref interface
12351c0b2f7Stbbdev TEST_CASE("input_node superclasses"){
12451c0b2f7Stbbdev test_inheritance<int>();
12551c0b2f7Stbbdev test_inheritance<void*>();
126*de0109beSIlya Mishin test_inheritance<output_msg>();
12751c0b2f7Stbbdev }
12851c0b2f7Stbbdev
12951c0b2f7Stbbdev //! Test input_node forwarding
13051c0b2f7Stbbdev //! \brief \ref requirement
13151c0b2f7Stbbdev TEST_CASE("input_node forwarding"){
132*de0109beSIlya Mishin conformance::counting_functor<output_msg> fun(conformance::expected);
133*de0109beSIlya Mishin conformance::test_forwarding<oneapi::tbb::flow::input_node<output_msg>, void, output_msg>(5, fun);
13451c0b2f7Stbbdev }
13551c0b2f7Stbbdev
13651c0b2f7Stbbdev //! Test input_node buffering
13751c0b2f7Stbbdev //! \brief \ref requirement
13851c0b2f7Stbbdev TEST_CASE("input_node buffering"){
139*de0109beSIlya Mishin conformance::dummy_functor<int> fun;
140*de0109beSIlya Mishin conformance::test_buffering<oneapi::tbb::flow::input_node<int>, int>(fun);
14151c0b2f7Stbbdev }
14251c0b2f7Stbbdev
14351c0b2f7Stbbdev //! Test calling input_node body
14451c0b2f7Stbbdev //! \brief \ref interface \ref requirement
14551c0b2f7Stbbdev TEST_CASE("input_node body") {
146*de0109beSIlya Mishin oneapi::tbb::flow::graph g;
147*de0109beSIlya Mishin constexpr std::size_t counting_threshold = 10;
148*de0109beSIlya Mishin conformance::counting_functor<output_msg> fun(counting_threshold);
149*de0109beSIlya Mishin
150*de0109beSIlya Mishin oneapi::tbb::flow::input_node<output_msg> node1(g, fun);
151*de0109beSIlya Mishin conformance::test_push_receiver<output_msg> node2(g);
152*de0109beSIlya Mishin
153*de0109beSIlya Mishin oneapi::tbb::flow::make_edge(node1, node2);
154*de0109beSIlya Mishin
155*de0109beSIlya Mishin node1.activate();
156*de0109beSIlya Mishin g.wait_for_all();
157*de0109beSIlya Mishin
158*de0109beSIlya Mishin CHECK_MESSAGE((conformance::get_values(node2).size() == counting_threshold), "Descendant of the node needs to be receive N messages");
159*de0109beSIlya Mishin CHECK_MESSAGE((fun.execute_count == counting_threshold + 1), "Body of the node needs to be executed N + 1 times");
16051c0b2f7Stbbdev }
16151c0b2f7Stbbdev
16251c0b2f7Stbbdev //! Test deduction guides
16351c0b2f7Stbbdev //! \brief \ref interface \ref requirement
16451c0b2f7Stbbdev TEST_CASE("Deduction guides"){
16551c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
16651c0b2f7Stbbdev test_deduction_guides();
16751c0b2f7Stbbdev #endif
16851c0b2f7Stbbdev }
169*de0109beSIlya Mishin
170*de0109beSIlya Mishin //! Test that measured concurrency respects set limits
171*de0109beSIlya Mishin //! \brief \ref requirement
172*de0109beSIlya Mishin TEST_CASE("concurrency follows set limits"){
173*de0109beSIlya Mishin oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism,
174*de0109beSIlya Mishin oneapi::tbb::this_task_arena::max_concurrency());
175*de0109beSIlya Mishin
176*de0109beSIlya Mishin
177*de0109beSIlya Mishin utils::ConcurrencyTracker::Reset();
178*de0109beSIlya Mishin oneapi::tbb::flow::graph g;
179*de0109beSIlya Mishin conformance::concurrency_peak_checker_body counter(1);
180*de0109beSIlya Mishin oneapi::tbb::flow::input_node<int> testing_node(g, counter);
181*de0109beSIlya Mishin
182*de0109beSIlya Mishin conformance::test_push_receiver<int> sink(g);
183*de0109beSIlya Mishin
184*de0109beSIlya Mishin make_edge(testing_node, sink);
185*de0109beSIlya Mishin testing_node.activate();
186*de0109beSIlya Mishin
187*de0109beSIlya Mishin g.wait_for_all();
188*de0109beSIlya Mishin }
189*de0109beSIlya Mishin
190*de0109beSIlya Mishin //! Test node Output class meet the CopyConstructible requirements.
191*de0109beSIlya Mishin //! \brief \ref interface \ref requirement
192*de0109beSIlya Mishin TEST_CASE("Test input_node Output class") {
193*de0109beSIlya Mishin conformance::test_output_class<oneapi::tbb::flow::input_node<conformance::copy_counting_object<int>>>();
194*de0109beSIlya Mishin }
195*de0109beSIlya Mishin
196*de0109beSIlya Mishin struct input_node_counter{
197*de0109beSIlya Mishin static int count;
198*de0109beSIlya Mishin int N;
input_node_counterinput_node_counter199*de0109beSIlya Mishin input_node_counter(int n) : N(n){};
200*de0109beSIlya Mishin
operator ()input_node_counter201*de0109beSIlya Mishin int operator()( oneapi::tbb::flow_control & fc ) {
202*de0109beSIlya Mishin ++count;
203*de0109beSIlya Mishin if(count > N){
204*de0109beSIlya Mishin fc.stop();
205*de0109beSIlya Mishin return N;
206*de0109beSIlya Mishin }
207*de0109beSIlya Mishin return N;
208*de0109beSIlya Mishin }
209*de0109beSIlya Mishin };
210*de0109beSIlya Mishin
211*de0109beSIlya Mishin struct function_node_counter{
212*de0109beSIlya Mishin static int count;
213*de0109beSIlya Mishin
operator ()function_node_counter214*de0109beSIlya Mishin int operator()( int ) {
215*de0109beSIlya Mishin ++count;
216*de0109beSIlya Mishin utils::doDummyWork(1000000);
217*de0109beSIlya Mishin 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*de0109beSIlya Mishin return 1;
219*de0109beSIlya Mishin }
220*de0109beSIlya Mishin };
221*de0109beSIlya Mishin
222*de0109beSIlya Mishin int input_node_counter::count = 0;
223*de0109beSIlya Mishin int function_node_counter::count = 0;
224*de0109beSIlya Mishin
225*de0109beSIlya Mishin //! Test input_node `try_get()' call testing: a call to body is made only when the internal buffer is empty.
226*de0109beSIlya Mishin //! \brief \ref requirement
227*de0109beSIlya Mishin TEST_CASE("input_node `try_get()' call testing: a call to body is made only when the internal buffer is empty.") {
228*de0109beSIlya Mishin oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, 1);
229*de0109beSIlya Mishin oneapi::tbb::flow::graph g;
230*de0109beSIlya Mishin input_node_counter fun1(500);
231*de0109beSIlya Mishin function_node_counter fun2;
232*de0109beSIlya Mishin
233*de0109beSIlya Mishin oneapi::tbb::flow::function_node <int, int, oneapi::tbb::flow::rejecting> fnode(g, oneapi::tbb::flow::serial, fun2);
234*de0109beSIlya Mishin oneapi::tbb::flow::input_node<int> testing_node(g, fun1);
235*de0109beSIlya Mishin
236*de0109beSIlya Mishin make_edge(testing_node, fnode);
237*de0109beSIlya Mishin testing_node.activate();
238*de0109beSIlya Mishin
239*de0109beSIlya Mishin g.wait_for_all();
240*de0109beSIlya Mishin }
241