151c0b2f7Stbbdev /*
2*a088cfa0SKonstantin Boyarinov Copyright (c) 2020-2023 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
20b15aabb3Stbbdev
21de0109beSIlya Mishin #define CONFORMANCE_MULTIFUNCTION_NODE
2251c0b2f7Stbbdev
2351c0b2f7Stbbdev #include "conformance_flowgraph.h"
24*a088cfa0SKonstantin Boyarinov #include "common/test_invoke.h"
2551c0b2f7Stbbdev
2651c0b2f7Stbbdev //! \file conformance_multifunction_node.cpp
2751c0b2f7Stbbdev //! \brief Test for [flow_graph.function_node] specification
2851c0b2f7Stbbdev
29de0109beSIlya Mishin using input_msg = conformance::message</*default_ctor*/true, /*copy_ctor*/true, /*copy_assign*/false>;
30de0109beSIlya Mishin using output_msg = conformance::message</*default_ctor*/false, /*copy_ctor*/false, /*copy_assign*/false>;
31de0109beSIlya Mishin
3251c0b2f7Stbbdev /*
33de0109beSIlya Mishin Test function_node is a graph_node, receiver<Input>, and sender<Output>
3451c0b2f7Stbbdev */
3551c0b2f7Stbbdev template<typename I, typename O>
test_inheritance()3651c0b2f7Stbbdev void test_inheritance(){
3749e08aacStbbdev using namespace oneapi::tbb::flow;
3851c0b2f7Stbbdev
3951c0b2f7Stbbdev CHECK_MESSAGE((std::is_base_of<graph_node, multifunction_node<I, O>>::value), "multifunction_node should be derived from graph_node");
4051c0b2f7Stbbdev CHECK_MESSAGE((std::is_base_of<receiver<I>, multifunction_node<I, O>>::value), "multifunction_node should be derived from receiver<Input>");
4151c0b2f7Stbbdev }
4251c0b2f7Stbbdev
43de0109beSIlya Mishin //! Test node reject the incoming message if the concurrency limit achieved.
4451c0b2f7Stbbdev //! \brief \ref interface
4551c0b2f7Stbbdev TEST_CASE("multifunction_node with rejecting policy"){
46de0109beSIlya Mishin conformance::test_rejecting<oneapi::tbb::flow::multifunction_node<int, std::tuple<int>, oneapi::tbb::flow::rejecting>>();
4751c0b2f7Stbbdev }
4851c0b2f7Stbbdev
49de0109beSIlya Mishin //! Test nodes for execution with priority in single-threaded configuration
5051c0b2f7Stbbdev //! \brief \ref interface
5151c0b2f7Stbbdev TEST_CASE("multifunction_node priority"){
52de0109beSIlya Mishin conformance::test_priority<oneapi::tbb::flow::multifunction_node<input_msg, std::tuple<int>>, input_msg>(oneapi::tbb::flow::unlimited);
5351c0b2f7Stbbdev }
5451c0b2f7Stbbdev
55de0109beSIlya Mishin //! Test function_node has a user-settable concurrency limit. It can be set to one of predefined values.
56de0109beSIlya Mishin //! The user can also provide a value of type std::size_t to limit concurrency.
57de0109beSIlya Mishin //! Test that not more than limited threads works in parallel.
5851c0b2f7Stbbdev //! \brief \ref interface
5951c0b2f7Stbbdev TEST_CASE("multifunction_node concurrency"){
60de0109beSIlya Mishin conformance::test_concurrency<oneapi::tbb::flow::multifunction_node<int, std::tuple<int>>>();
6151c0b2f7Stbbdev }
6251c0b2f7Stbbdev
63de0109beSIlya Mishin //! Test all node constructors
6451c0b2f7Stbbdev //! \brief \ref interface
6551c0b2f7Stbbdev TEST_CASE("multifunction_node constructors"){
66de0109beSIlya Mishin using namespace oneapi::tbb::flow;
67de0109beSIlya Mishin graph g;
68de0109beSIlya Mishin
69de0109beSIlya Mishin conformance::counting_functor<int> fun;
70de0109beSIlya Mishin
71de0109beSIlya Mishin multifunction_node<int, std::tuple<int>> fn1(g, unlimited, fun);
72de0109beSIlya Mishin multifunction_node<int, std::tuple<int>> fn2(g, unlimited, fun, oneapi::tbb::flow::node_priority_t(1));
73de0109beSIlya Mishin
74de0109beSIlya Mishin multifunction_node<int, std::tuple<int>, lightweight> lw_node1(g, serial, fun, lightweight());
75de0109beSIlya Mishin multifunction_node<int, std::tuple<int>, lightweight> lw_node2(g, serial, fun, lightweight(), oneapi::tbb::flow::node_priority_t(1));
7651c0b2f7Stbbdev }
7751c0b2f7Stbbdev
78de0109beSIlya Mishin //! 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.
79de0109beSIlya Mishin //! The predecessors and successors of src are not copied.
80de0109beSIlya Mishin //! \brief \ref interface
81de0109beSIlya Mishin TEST_CASE("multifunction_node copy constructor"){
82de0109beSIlya Mishin conformance::test_copy_ctor<oneapi::tbb::flow::multifunction_node<int, std::tuple<int>>>();
83de0109beSIlya Mishin }
84de0109beSIlya Mishin
85de0109beSIlya Mishin //! Test node not buffered unsuccessful message, and try_get after rejection should not succeed.
8651c0b2f7Stbbdev //! \brief \ref requirement
8751c0b2f7Stbbdev TEST_CASE("multifunction_node buffering"){
88de0109beSIlya Mishin conformance::dummy_functor<int> fun;
89de0109beSIlya Mishin conformance::test_buffering<oneapi::tbb::flow::multifunction_node<input_msg, std::tuple<int>,
90de0109beSIlya Mishin oneapi::tbb::flow::rejecting>, input_msg>(oneapi::tbb::flow::unlimited, fun);
9151c0b2f7Stbbdev }
9251c0b2f7Stbbdev
93de0109beSIlya Mishin //! Test multifunction_node broadcasting
9451c0b2f7Stbbdev //! \brief \ref requirement
9551c0b2f7Stbbdev TEST_CASE("multifunction_node broadcast"){
96de0109beSIlya Mishin conformance::counting_functor<int> fun(conformance::expected);
97de0109beSIlya Mishin conformance::test_forwarding<oneapi::tbb::flow::multifunction_node<input_msg, std::tuple<int>>, input_msg, int>(1, oneapi::tbb::flow::unlimited, fun);
9851c0b2f7Stbbdev }
9951c0b2f7Stbbdev
100de0109beSIlya Mishin //! Test the body object passed to a node is copied
10151c0b2f7Stbbdev //! \brief \ref interface
102de0109beSIlya Mishin TEST_CASE("multifunction_node copy body"){
103de0109beSIlya Mishin conformance::test_copy_body_function<oneapi::tbb::flow::multifunction_node<int, std::tuple<int>>, conformance::copy_counting_object<int>>(oneapi::tbb::flow::unlimited);
10451c0b2f7Stbbdev }
10551c0b2f7Stbbdev
106de0109beSIlya Mishin //! Test execution of node body
107de0109beSIlya Mishin //! Test node can do try_put call
10851c0b2f7Stbbdev //! \brief \ref interface \ref requirement
10951c0b2f7Stbbdev TEST_CASE("multifunction_node body") {
110de0109beSIlya Mishin 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);
111de0109beSIlya Mishin }
112de0109beSIlya Mishin
113de0109beSIlya Mishin //! Test multifunction_node output_ports() returns a tuple of output ports.
114de0109beSIlya Mishin //! \brief \ref interface \ref requirement
115de0109beSIlya Mishin TEST_CASE("multifunction_node output_ports") {
116de0109beSIlya Mishin using namespace oneapi::tbb::flow;
117de0109beSIlya Mishin graph g;
118de0109beSIlya Mishin conformance::dummy_functor<int> fun;
119de0109beSIlya Mishin oneapi::tbb::flow::multifunction_node <int, std::tuple<int>> node(g, unlimited, fun);
120de0109beSIlya Mishin
121de0109beSIlya Mishin CHECK_MESSAGE((std::is_same<oneapi::tbb::flow::multifunction_node<int, std::tuple<int>>::output_ports_type&,
122de0109beSIlya Mishin decltype(node.output_ports())>::value), "multifunction_node output_ports should returns a tuple of output ports");
12351c0b2f7Stbbdev }
12451c0b2f7Stbbdev
12551c0b2f7Stbbdev //! Test inheritance relations
12651c0b2f7Stbbdev //! \brief \ref interface
12751c0b2f7Stbbdev TEST_CASE("multifunction_node superclasses"){
12851c0b2f7Stbbdev test_inheritance<int, std::tuple<int>>();
12951c0b2f7Stbbdev test_inheritance<void*, std::tuple<float>>();
130de0109beSIlya Mishin test_inheritance<input_msg, std::tuple<output_msg>>();
131de0109beSIlya Mishin }
132de0109beSIlya Mishin
133de0109beSIlya Mishin //! Test node Input class meet the DefaultConstructible and CopyConstructible requirements and Output class meet the CopyConstructible requirements.
134de0109beSIlya Mishin //! \brief \ref interface \ref requirement
135de0109beSIlya Mishin TEST_CASE("Test function_node Output and Input class") {
136de0109beSIlya Mishin using Body = conformance::copy_counting_object<int>;
137de0109beSIlya Mishin conformance::test_output_input_class<oneapi::tbb::flow::multifunction_node<Body, std::tuple<Body>>, Body>();
13851c0b2f7Stbbdev }
139*a088cfa0SKonstantin Boyarinov
140*a088cfa0SKonstantin Boyarinov #if __TBB_CPP17_INVOKE_PRESENT
141*a088cfa0SKonstantin Boyarinov //! Test that multifunction_node uses std::invoke to execute the body
142*a088cfa0SKonstantin Boyarinov //! \brief \ref interface \ref requirement
143*a088cfa0SKonstantin Boyarinov TEST_CASE("Test multifunction_node and std::invoke") {
144*a088cfa0SKonstantin Boyarinov using namespace oneapi::tbb::flow;
145*a088cfa0SKonstantin Boyarinov
146*a088cfa0SKonstantin Boyarinov using output_type1 = test_invoke::SmartID<std::size_t>;
147*a088cfa0SKonstantin Boyarinov using input_type = test_invoke::SmartID<output_type1>;
148*a088cfa0SKonstantin Boyarinov
149*a088cfa0SKonstantin Boyarinov using output_tuple1 = std::tuple<output_type1, output_type1>;
150*a088cfa0SKonstantin Boyarinov using output_tuple2 = std::tuple<std::size_t>;
151*a088cfa0SKonstantin Boyarinov
152*a088cfa0SKonstantin Boyarinov using first_mf_node_type = multifunction_node<input_type, output_tuple1>;
153*a088cfa0SKonstantin Boyarinov using second_mf_node_type = multifunction_node<output_type1, output_tuple2>;
154*a088cfa0SKonstantin Boyarinov
155*a088cfa0SKonstantin Boyarinov using first_ports_type = typename first_mf_node_type::output_ports_type;
156*a088cfa0SKonstantin Boyarinov using second_ports_type = typename second_mf_node_type::output_ports_type;
157*a088cfa0SKonstantin Boyarinov
158*a088cfa0SKonstantin Boyarinov graph g;
159*a088cfa0SKonstantin Boyarinov
160*a088cfa0SKonstantin Boyarinov auto first_body = &input_type::template send_id<first_ports_type>;
161*a088cfa0SKonstantin Boyarinov auto second_body = &output_type1::template send_id<second_ports_type>;
162*a088cfa0SKonstantin Boyarinov
163*a088cfa0SKonstantin Boyarinov first_mf_node_type mf1(g, unlimited, first_body);
164*a088cfa0SKonstantin Boyarinov second_mf_node_type mf21(g, unlimited, second_body);
165*a088cfa0SKonstantin Boyarinov second_mf_node_type mf22(g, unlimited, second_body);
166*a088cfa0SKonstantin Boyarinov
167*a088cfa0SKonstantin Boyarinov buffer_node<std::size_t> buf(g);
168*a088cfa0SKonstantin Boyarinov
169*a088cfa0SKonstantin Boyarinov make_edge(output_port<0>(mf1), mf21);
170*a088cfa0SKonstantin Boyarinov make_edge(output_port<1>(mf1), mf22);
171*a088cfa0SKonstantin Boyarinov
172*a088cfa0SKonstantin Boyarinov make_edge(output_port<0>(mf21), buf);
173*a088cfa0SKonstantin Boyarinov make_edge(output_port<0>(mf22), buf);
174*a088cfa0SKonstantin Boyarinov
175*a088cfa0SKonstantin Boyarinov mf1.try_put(input_type{output_type1{1}});
176*a088cfa0SKonstantin Boyarinov
177*a088cfa0SKonstantin Boyarinov g.wait_for_all();
178*a088cfa0SKonstantin Boyarinov
179*a088cfa0SKonstantin Boyarinov std::size_t buf_size = 0;
180*a088cfa0SKonstantin Boyarinov std::size_t tmp = 0;
181*a088cfa0SKonstantin Boyarinov while(buf.try_get(tmp)) {
182*a088cfa0SKonstantin Boyarinov ++buf_size;
183*a088cfa0SKonstantin Boyarinov CHECK(tmp == 1);
184*a088cfa0SKonstantin Boyarinov }
185*a088cfa0SKonstantin Boyarinov
186*a088cfa0SKonstantin Boyarinov CHECK(buf_size == 2);
187*a088cfa0SKonstantin Boyarinov }
188*a088cfa0SKonstantin Boyarinov #endif
189