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