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