1 /*
2     Copyright (c) 2020-2023 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_MULTIFUNCTION_NODE
22 
23 #include "conformance_flowgraph.h"
24 #include "common/test_invoke.h"
25 
26 //! \file conformance_multifunction_node.cpp
27 //! \brief Test for [flow_graph.function_node] specification
28 
29 using input_msg = conformance::message</*default_ctor*/true, /*copy_ctor*/true, /*copy_assign*/false>;
30 using output_msg = conformance::message</*default_ctor*/false, /*copy_ctor*/false, /*copy_assign*/false>;
31 
32 /*
33     Test function_node is a graph_node, receiver<Input>, and sender<Output>
34 */
35 template<typename I, typename O>
test_inheritance()36 void test_inheritance(){
37     using namespace oneapi::tbb::flow;
38 
39     CHECK_MESSAGE((std::is_base_of<graph_node, multifunction_node<I, O>>::value), "multifunction_node should be derived from graph_node");
40     CHECK_MESSAGE((std::is_base_of<receiver<I>, multifunction_node<I, O>>::value), "multifunction_node should be derived from receiver<Input>");
41 }
42 
43 //! Test node reject the incoming message if the concurrency limit achieved.
44 //! \brief \ref interface
45 TEST_CASE("multifunction_node with rejecting policy"){
46     conformance::test_rejecting<oneapi::tbb::flow::multifunction_node<int, std::tuple<int>, oneapi::tbb::flow::rejecting>>();
47 }
48 
49 //! Test nodes for execution with priority in single-threaded configuration
50 //! \brief \ref interface
51 TEST_CASE("multifunction_node priority"){
52     conformance::test_priority<oneapi::tbb::flow::multifunction_node<input_msg, std::tuple<int>>, input_msg>(oneapi::tbb::flow::unlimited);
53 }
54 
55 //! Test function_node has a user-settable concurrency limit. It can be set to one of predefined values.
56 //! The user can also provide a value of type std::size_t to limit concurrency.
57 //! Test that not more than limited threads works in parallel.
58 //! \brief \ref interface
59 TEST_CASE("multifunction_node concurrency"){
60     conformance::test_concurrency<oneapi::tbb::flow::multifunction_node<int, std::tuple<int>>>();
61 }
62 
63 //! Test all node constructors
64 //! \brief \ref interface
65 TEST_CASE("multifunction_node constructors"){
66     using namespace oneapi::tbb::flow;
67     graph g;
68 
69     conformance::counting_functor<int> fun;
70 
71     multifunction_node<int, std::tuple<int>> fn1(g, unlimited, fun);
72     multifunction_node<int, std::tuple<int>> fn2(g, unlimited, fun, oneapi::tbb::flow::node_priority_t(1));
73 
74     multifunction_node<int, std::tuple<int>, lightweight> lw_node1(g, serial, fun, lightweight());
75     multifunction_node<int, std::tuple<int>, lightweight> lw_node2(g, serial, fun, lightweight(), oneapi::tbb::flow::node_priority_t(1));
76 }
77 
78 //! The node that is constructed has a reference to the same graph object as src, has a copy of the initial body used by src, and has the same concurrency threshold as src.
79 //! The predecessors and successors of src are not copied.
80 //! \brief \ref interface
81 TEST_CASE("multifunction_node copy constructor"){
82     conformance::test_copy_ctor<oneapi::tbb::flow::multifunction_node<int, std::tuple<int>>>();
83 }
84 
85 //! Test node not buffered unsuccessful message, and try_get after rejection should not succeed.
86 //! \brief \ref requirement
87 TEST_CASE("multifunction_node buffering"){
88     conformance::dummy_functor<int> fun;
89     conformance::test_buffering<oneapi::tbb::flow::multifunction_node<input_msg, std::tuple<int>,
90     oneapi::tbb::flow::rejecting>, input_msg>(oneapi::tbb::flow::unlimited, fun);
91 }
92 
93 //! Test multifunction_node broadcasting
94 //! \brief \ref requirement
95 TEST_CASE("multifunction_node broadcast"){
96     conformance::counting_functor<int> fun(conformance::expected);
97     conformance::test_forwarding<oneapi::tbb::flow::multifunction_node<input_msg, std::tuple<int>>, input_msg, int>(1, oneapi::tbb::flow::unlimited, fun);
98 }
99 
100 //! Test the body object passed to a node is copied
101 //! \brief \ref interface
102 TEST_CASE("multifunction_node copy body"){
103     conformance::test_copy_body_function<oneapi::tbb::flow::multifunction_node<int, std::tuple<int>>, conformance::copy_counting_object<int>>(oneapi::tbb::flow::unlimited);
104 }
105 
106 //! Test execution of node body
107 //! Test node can do try_put call
108 //! \brief \ref interface \ref requirement
109 TEST_CASE("multifunction_node body") {
110     conformance::test_body_exec<oneapi::tbb::flow::multifunction_node<input_msg, std::tuple<output_msg>, oneapi::tbb::flow::rejecting>, input_msg, output_msg>(oneapi::tbb::flow::unlimited);
111 }
112 
113 //! Test multifunction_node output_ports() returns a tuple of output ports.
114 //! \brief \ref interface \ref requirement
115 TEST_CASE("multifunction_node output_ports") {
116     using namespace oneapi::tbb::flow;
117     graph g;
118     conformance::dummy_functor<int> fun;
119     oneapi::tbb::flow::multifunction_node <int, std::tuple<int>> node(g, unlimited, fun);
120 
121     CHECK_MESSAGE((std::is_same<oneapi::tbb::flow::multifunction_node<int, std::tuple<int>>::output_ports_type&,
122         decltype(node.output_ports())>::value), "multifunction_node output_ports should returns a tuple of output ports");
123 }
124 
125 //! Test inheritance relations
126 //! \brief \ref interface
127 TEST_CASE("multifunction_node superclasses"){
128     test_inheritance<int, std::tuple<int>>();
129     test_inheritance<void*, std::tuple<float>>();
130     test_inheritance<input_msg, std::tuple<output_msg>>();
131 }
132 
133 //! Test node Input class meet the DefaultConstructible and CopyConstructible requirements and Output class meet the CopyConstructible requirements.
134 //! \brief \ref interface \ref requirement
135 TEST_CASE("Test function_node Output and Input class") {
136     using Body = conformance::copy_counting_object<int>;
137     conformance::test_output_input_class<oneapi::tbb::flow::multifunction_node<Body, std::tuple<Body>>, Body>();
138 }
139 
140 #if __TBB_CPP17_INVOKE_PRESENT
141 //! Test that multifunction_node uses std::invoke to execute the body
142 //! \brief \ref interface \ref requirement
143 TEST_CASE("Test multifunction_node and std::invoke") {
144     using namespace oneapi::tbb::flow;
145 
146     using output_type1 = test_invoke::SmartID<std::size_t>;
147     using input_type = test_invoke::SmartID<output_type1>;
148 
149     using output_tuple1 = std::tuple<output_type1, output_type1>;
150     using output_tuple2 = std::tuple<std::size_t>;
151 
152     using first_mf_node_type = multifunction_node<input_type, output_tuple1>;
153     using second_mf_node_type = multifunction_node<output_type1, output_tuple2>;
154 
155     using first_ports_type = typename first_mf_node_type::output_ports_type;
156     using second_ports_type = typename second_mf_node_type::output_ports_type;
157 
158     graph g;
159 
160     auto first_body = &input_type::template send_id<first_ports_type>;
161     auto second_body = &output_type1::template send_id<second_ports_type>;
162 
163     first_mf_node_type mf1(g, unlimited, first_body);
164     second_mf_node_type mf21(g, unlimited, second_body);
165     second_mf_node_type mf22(g, unlimited, second_body);
166 
167     buffer_node<std::size_t> buf(g);
168 
169     make_edge(output_port<0>(mf1), mf21);
170     make_edge(output_port<1>(mf1), mf22);
171 
172     make_edge(output_port<0>(mf21), buf);
173     make_edge(output_port<0>(mf22), buf);
174 
175     mf1.try_put(input_type{output_type1{1}});
176 
177     g.wait_for_all();
178 
179     std::size_t buf_size = 0;
180     std::size_t tmp = 0;
181     while(buf.try_get(tmp)) {
182         ++buf_size;
183         CHECK(tmp == 1);
184     }
185 
186     CHECK(buf_size == 2);
187 }
188 #endif
189