1 /* 2 Copyright (c) 2020 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 #include "common/test.h" 18 19 #include "common/utils.h" 20 #include "common/graph_utils.h" 21 22 #include "tbb/flow_graph.h" 23 #include "tbb/task_arena.h" 24 25 #include "tbb/global_control.h" 26 #include "conformance_flowgraph.h" 27 28 //! \file conformance_continue_node.cpp 29 //! \brief Test for [flow_graph.continue_node] specification 30 31 /* 32 TODO: implement missing conformance tests for continue_node: 33 - [ ] For `test_forwarding' check that the value passed is the actual one received. 34 - [ ] The `copy_body' function copies altered body (e.g. after its successful invocation). 35 - [ ] Improve CTAD test. 36 - [ ] Improve constructors test, including addition of calls to constructors with 37 `number_of_predecessors' parameter. 38 - [ ] Explicit test for copy constructor of the node. 39 - [ ] Rewrite test_priority. 40 - [ ] Check `Output' type indeed copy-constructed and copy-assigned while working with the node. 41 - [ ] Explicit test for correct working of `number_of_predecessors' constructor parameter, 42 including taking it into account when making and removing edges. 43 - [ ] Add testing of `try_put' statement. In particular that it does not wait for the execution of 44 the body to complete. 45 */ 46 47 void test_cont_body(){ 48 tbb::flow::graph g; 49 inc_functor<int> cf; 50 cf.execute_count = 0; 51 52 tbb::flow::continue_node<int> node1(g, cf); 53 54 const size_t n = 10; 55 for(size_t i = 0; i < n; ++i) { 56 CHECK_MESSAGE((node1.try_put(tbb::flow::continue_msg()) == true), 57 "continue_node::try_put() should never reject a message."); 58 } 59 g.wait_for_all(); 60 61 CHECK_MESSAGE( (cf.execute_count == n), "Body of the first node needs to be executed N times"); 62 } 63 64 template<typename O> 65 void test_inheritance(){ 66 using namespace tbb::flow; 67 68 CHECK_MESSAGE( (std::is_base_of<graph_node, continue_node<O>>::value), "continue_node should be derived from graph_node"); 69 CHECK_MESSAGE( (std::is_base_of<receiver<continue_msg>, continue_node<O>>::value), "continue_node should be derived from receiver<Input>"); 70 CHECK_MESSAGE( (std::is_base_of<sender<O>, continue_node<O>>::value), "continue_node should be derived from sender<Output>"); 71 } 72 73 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT 74 void test_deduction_guides(){ 75 tbb::flow::graph g; 76 inc_functor<int> fun; 77 tbb::flow::continue_node node1(g, fun); 78 } 79 #endif 80 81 void test_forwarding(){ 82 tbb::flow::graph g; 83 inc_functor<int> fun; 84 fun.execute_count = 0; 85 86 tbb::flow::continue_node<int> node1(g, fun); 87 test_push_receiver<int> node2(g); 88 test_push_receiver<int> node3(g); 89 90 tbb::flow::make_edge(node1, node2); 91 tbb::flow::make_edge(node1, node3); 92 93 node1.try_put(tbb::flow::continue_msg()); 94 g.wait_for_all(); 95 96 CHECK_MESSAGE( (get_count(node2) == 1), "Descendant of the node must receive one message."); 97 CHECK_MESSAGE( (get_count(node3) == 1), "Descendant of the node must receive one message."); 98 } 99 100 void test_buffering(){ 101 tbb::flow::graph g; 102 inc_functor<int> fun; 103 104 tbb::flow::continue_node<int> node(g, fun); 105 tbb::flow::limiter_node<int> rejecter(g, 0); 106 107 tbb::flow::make_edge(node, rejecter); 108 node.try_put(tbb::flow::continue_msg()); 109 110 int tmp = -1; 111 CHECK_MESSAGE( (node.try_get(tmp) == false), "try_get after rejection should not succeed"); 112 CHECK_MESSAGE( (tmp == -1), "try_get after rejection should not alter passed value"); 113 g.wait_for_all(); 114 } 115 116 void test_policy_ctors(){ 117 using namespace tbb::flow; 118 graph g; 119 120 inc_functor<int> fun; 121 122 continue_node<int, lightweight> lw_node(g, fun); 123 } 124 125 void test_ctors(){ 126 using namespace tbb::flow; 127 graph g; 128 129 inc_functor<int> fun; 130 131 continue_node<int> proto1(g, 2, fun, tbb::flow::node_priority_t(1)); 132 } 133 134 template<typename O> 135 struct CopyCounterBody{ 136 size_t copy_count; 137 138 CopyCounterBody(): 139 copy_count(0) {} 140 141 CopyCounterBody(const CopyCounterBody<O>& other): 142 copy_count(other.copy_count + 1) {} 143 144 CopyCounterBody& operator=(const CopyCounterBody<O>& other){ 145 copy_count = other.copy_count + 1; 146 return *this; 147 } 148 149 O operator()(tbb::flow::continue_msg){ 150 return 1; 151 } 152 }; 153 154 void test_copies(){ 155 using namespace tbb::flow; 156 157 CopyCounterBody<int> b; 158 159 graph g; 160 continue_node<int> fn(g, b); 161 162 CopyCounterBody<int> b2 = copy_body<CopyCounterBody<int>, 163 continue_node<int>>(fn); 164 165 CHECK_MESSAGE( (b.copy_count + 2 <= b2.copy_count), "copy_body and constructor should copy bodies"); 166 } 167 168 169 void test_priority(){ 170 size_t concurrency_limit = 1; 171 tbb::global_control control(tbb::global_control::max_allowed_parallelism, concurrency_limit); 172 173 tbb::flow::graph g; 174 175 tbb::flow::continue_node<tbb::flow::continue_msg> source(g, 176 [](tbb::flow::continue_msg){ return tbb::flow::continue_msg();}); 177 source.try_put(tbb::flow::continue_msg()); 178 179 first_functor<int>::first_id = -1; 180 first_functor<int> low_functor(1); 181 first_functor<int> high_functor(2); 182 183 tbb::flow::continue_node<int, int> high(g, high_functor, tbb::flow::node_priority_t(1)); 184 tbb::flow::continue_node<int, int> low(g, low_functor); 185 186 make_edge(source, low); 187 make_edge(source, high); 188 189 g.wait_for_all(); 190 191 CHECK_MESSAGE( (first_functor<int>::first_id == 2), "High priority node should execute first"); 192 } 193 194 //! Test node costructors 195 //! \brief \ref requirement 196 TEST_CASE("continue_node constructors"){ 197 test_ctors(); 198 } 199 200 //! Test priorities work in single-threaded configuration 201 //! \brief \ref requirement 202 TEST_CASE("continue_node priority support"){ 203 test_priority(); 204 } 205 206 //! Test body copying and copy_body logic 207 //! \brief \ref interface 208 TEST_CASE("continue_node and body copying"){ 209 test_copies(); 210 } 211 212 //! Test constructors 213 //! \brief \ref interface 214 TEST_CASE("continue_node constructors"){ 215 test_policy_ctors(); 216 } 217 218 //! Test continue_node buffering 219 //! \brief \ref requirement 220 TEST_CASE("continue_node buffering"){ 221 test_buffering(); 222 } 223 224 //! Test function_node broadcasting 225 //! \brief \ref requirement 226 TEST_CASE("continue_node broadcast"){ 227 test_forwarding(); 228 } 229 230 //! Test deduction guides 231 //! \brief \ref interface \ref requirement 232 TEST_CASE("Deduction guides"){ 233 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT 234 test_deduction_guides(); 235 #endif 236 } 237 238 //! Test inheritance relations 239 //! \brief \ref interface 240 TEST_CASE("continue_node superclasses"){ 241 test_inheritance<int>(); 242 test_inheritance<void*>(); 243 } 244 245 //! Test body execution 246 //! \brief \ref interface \ref requirement 247 TEST_CASE("continue body") { 248 test_cont_body(); 249 } 250