1 /*
2     Copyright (c) 2019-2021 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 #ifndef __TBB_test_common_test_follows_and_precedes_api_H_
18 #define __TBB_test_common_test_follows_and_precedes_api_H_
19 
20 #include "config.h"
21 #include "oneapi/tbb/flow_graph.h"
22 
23 #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
24 #include <array>
25 #include <vector>
26 #include <type_traits>
27 
28 namespace follows_and_precedes_testing{
29 
30 template <typename NodeType>
31 struct testing_method_follows: std::integral_constant<int, 0> {};
32 
33 template <typename... Args>
34 struct testing_method_follows<tbb::flow::join_node<Args...>> : std::integral_constant<int, 1> {};
35 template <typename... Args>
36 struct testing_method_follows<tbb::flow::continue_node<Args...>> : std::integral_constant<int, 1> {};
37 
38 template <typename... Args>
39 struct testing_method_follows<tbb::flow::overwrite_node<Args...>> : std::integral_constant<int, 2> {};
40 template <typename... Args>
41 struct testing_method_follows<tbb::flow::write_once_node<Args...>> : std::integral_constant<int, 2> {};
42 
43 template <typename... Args>
44 struct testing_method_follows<tbb::flow::multifunction_node<Args...>> : std::integral_constant<int, 3> {};
45 
46 template <typename MessageType, typename NodeType, typename PredecessorType>
47 class edge_checker_follows {
48 public:
49     static void check(tbb::flow::graph& g,
50                       NodeType& node,
51                       std::array<PredecessorType, 3>& preds,
52                       std::array<MessageType, 3>& messages) {
53         check_impl(g, node, preds, messages, typename testing_method_follows<NodeType>::type());
54     }
55 private:
56     // Test is applicable for: function_node, buffer_node, queue_node, priority_queue_node, limiter_node,
57     //                         broadcast_node, sequencer_node
58     static void check_impl(tbb::flow::graph& g,
59                            NodeType& node,
60                            std::array<PredecessorType, 3>& preds,
61                            std::array<MessageType, 3>& messages,
62                            std::integral_constant<int, 0>) {
63         tbb::flow::buffer_node<typename NodeType::output_type> buffer(g);
64         tbb::flow::make_edge(node, buffer);
65 
66         for(size_t i = 0; i < 3; ++i) {
67             preds[i].try_put(messages[i]);
68             g.wait_for_all();
69 
70             typename NodeType::output_type storage;
71             CHECK_MESSAGE((buffer.try_get(storage) && !buffer.try_get(storage)), "Not exact edge quantity was made");
72         }
73     }
74     // Test is applicable for: continue_node, join_node
75     static void check_impl(tbb::flow::graph& g,
76                            NodeType& node,
77                            std::array<PredecessorType, 3>& preds,
78                            std::array<MessageType, 3>& messages,
79                            std::integral_constant<int, 1>) {
80         tbb::flow::buffer_node<typename NodeType::output_type> buffer(g);
81         tbb::flow::make_edge(node, buffer);
82 
83         for(size_t i = 0; i < 3; ++i) {
84             preds[i].try_put(messages[i]);
85             g.wait_for_all();
86         }
87 
88         typename NodeType::output_type storage;
89         CHECK_MESSAGE((buffer.try_get(storage) && !buffer.try_get(storage)), "Not exact edge quantity was made");
90     }
91     // Test is applicable for: overwrite_node, write_once_node
92     static void check_impl(tbb::flow::graph& g,
93                            NodeType& node,
94                            std::array<PredecessorType, 3>& preds,
95                            std::array<MessageType, 3>& messages,
96                            std::integral_constant<int, 2>) {
97         for(size_t i = 0; i < 3; ++i) {
98             node.clear();
99             preds[i].try_put(messages[i]);
100             g.wait_for_all();
101 
102             typename NodeType::output_type storage;
103             CHECK_MESSAGE((node.try_get(storage)), "Not exact edge quantity was made");
104         }
105    }
106     // Test is applicable for: multifunction_node
107     static void check_impl(tbb::flow::graph& g,
108                            NodeType& node,
109                            std::array<PredecessorType, 3>& preds,
110                            std::array<MessageType, 3>& messages,
111                            std::integral_constant<int, 3>) {
112         typedef typename std::remove_reference<decltype(tbb::flow::output_port<0>(node))>::type multioutput;
113         tbb::flow::buffer_node<typename multioutput::output_type> buf(tbb::flow::follows(tbb::flow::output_port<0>(node)));
114 
115         for(size_t i = 0; i < 3; ++i) {
116             preds[i].try_put(messages[i]);
117             g.wait_for_all();
118 
119             typename multioutput::output_type storage;
120             CHECK_MESSAGE((buf.try_get(storage) && ! buf.try_get(storage)), "Not exact edge quantity was made");
121         }
122    }
123 };
124 
125 template <typename NodeType>
126 struct testing_method_precedes: std::integral_constant<int, 0> {};
127 
128 template <typename... Args>
129 struct testing_method_precedes<tbb::flow::buffer_node<Args...>> : std::integral_constant<int, 1> {};
130 template <typename... Args>
131 struct testing_method_precedes<tbb::flow::queue_node<Args...>> : std::integral_constant<int, 1> {};
132 template <typename... Args>
133 struct testing_method_precedes<tbb::flow::priority_queue_node<Args...>> : std::integral_constant<int, 1> {};
134 template <typename... Args>
135 struct testing_method_precedes<tbb::flow::sequencer_node<Args...>> : std::integral_constant<int, 1> {};
136 
137 template <typename... Args>
138 struct testing_method_precedes<tbb::flow::join_node<Args...>> : std::integral_constant<int, 2> {};
139 
140 template <typename MessageType, typename NodeType>
141 class edge_checker_precedes {
142     using NodeOutputType = typename NodeType::output_type;
143     using SuccessorOutputType = typename tbb::flow::buffer_node<NodeOutputType>::output_type;
144 public:
145     static void check(tbb::flow::graph& g,
146                       NodeType& node,
147                       std::array<tbb::flow::buffer_node<NodeOutputType>, 3>& successors,
148                       std::vector<MessageType>& messages) {
149         check_impl(g, node, successors, messages, typename testing_method_precedes<NodeType>::type());
150     }
151 private:
152     // Testing is applicable for: continue_node, function_node, overwrite_node, buffer_node,
153     //                            broadcast_node, write_once_node, limiter_node
154     static void check_impl(tbb::flow::graph& g,
155                            NodeType& node,
156                            std::array<tbb::flow::buffer_node<NodeOutputType>, 3>& successors,
157                            std::vector<MessageType>& messages,
158                            std::integral_constant<int, 0>) {
159         CHECK_MESSAGE((messages.size() == 1),
160                "messages.size() has to be 1 to test nodes what broadcast message to all the successors");
161 
162         node.try_put(messages[0]);
163         g.wait_for_all();
164 
165         SuccessorOutputType storage;
166         for(auto& successor: successors) {
167             CHECK_MESSAGE((successor.try_get(storage) && !successor.try_get(storage)),
168                     "Not exact edge quantity was made");
169         }
170     }
171     // Testing is applicable for: buffer_node, queue_node, priority_queue_node, sequencer_node
172     static void check_impl(tbb::flow::graph& g,
173                            NodeType& node,
174                            std::array<tbb::flow::buffer_node<typename NodeType::output_type>, 3>& successors,
175                            std::vector<MessageType>& messages,
176                            std::integral_constant<int, 1>) {
177         CHECK_MESSAGE((messages.size() == 3),
178                "messages.size() has to be 3 to test nodes what send a message to the first available successor");
179 
180         tbb::flow::write_once_node<SuccessorOutputType>
181             buffer(tbb::flow::follows(successors[0], successors[1], successors[2]));
182 
183         for(size_t i = 0; i < 3; ++i) {
184             node.try_put(messages[i]);
185             g.wait_for_all();
186 
187             SuccessorOutputType storage;
188             CHECK_MESSAGE((buffer.try_get(storage)), "Not exact edge quantity was made");
189 
190             buffer.clear();
191         }
192     }
193     // Testing is applicable for: join_node
194     static void check_impl(tbb::flow::graph& g,
195                            NodeType& node,
196                            std::array<tbb::flow::buffer_node<typename NodeType::output_type>, 3>& successors,
197                            std::vector<MessageType>& messages,
198                            std::integral_constant<int, 2>) {
199         CHECK_MESSAGE((messages.size() == 3),
200                "messages.size() has to be 3 to test nodes what send a message to the first available successor");
201         std::array<tbb::flow::buffer_node<MessageType>, 3> preds = {
202 	  {
203 	    tbb::flow::buffer_node<MessageType>(g),
204 	    tbb::flow::buffer_node<MessageType>(g),
205 	    tbb::flow::buffer_node<MessageType>(g)
206 	  }
207 	};
208 
209         make_edge(preds[0], tbb::flow::input_port<0>(node));
210         make_edge(preds[1], tbb::flow::input_port<1>(node));
211         make_edge(preds[2], tbb::flow::input_port<2>(node));
212 
213         for(size_t i = 0; i < 3; ++i) {
214             preds[i].try_put(messages[i]);
215             g.wait_for_all();
216         }
217 
218         typename NodeType::output_type storage;
219         for(auto& successor: successors) {
220             CHECK_MESSAGE((successor.try_get(storage) && !successor.try_get(storage)),
221             "Not exact edge quantity was made");
222         }
223     }
224 };
225 
226 template<typename MessageType,
227          typename NodeType,
228          typename PredecessorType=tbb::flow::broadcast_node<MessageType>,
229          typename... ConstructorArgs>
230 void test_follows(std::array<MessageType, 3>& messages, ConstructorArgs&&... args) {
231     using namespace tbb::flow;
232 
233     graph g;
234     std::array<PredecessorType, 3> preds = {
235         {
236             PredecessorType(g),
237             PredecessorType(g),
238             PredecessorType(g)
239         }
240     };
241 
242     NodeType node(follows(preds[0], preds[1], preds[2]), std::forward<ConstructorArgs>(args)...);
243 
244     edge_checker_follows<MessageType, NodeType, PredecessorType>::check(g, node, preds, messages);
245 }
246 
247 template<typename MessageType,
248          typename NodeType,
249          typename... ConstructorArgs>
250 void test_precedes(std::vector<MessageType>& messages, ConstructorArgs&&... args) {
251     using namespace tbb::flow;
252 
253     using SuccessorType = buffer_node<typename NodeType::output_type>;
254 
255     graph g;
256 
257     std::array<SuccessorType, 3> successors = { {SuccessorType(g), SuccessorType(g), SuccessorType(g)} };
258 
259     NodeType node(precedes(successors[0], successors[1], successors[2]), std::forward<ConstructorArgs>(args)...);
260 
261     edge_checker_precedes<MessageType, NodeType>::check(g, node, successors, messages);
262 }
263 
264 } // follows_and_precedes_testing
265 #endif // __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
266 #endif // __TBB_test_common_test_follows_and_precedes_api_H_
267