ebf7a7d3e2182387b7d2d51bc3c67f6ed6ab548a
[platform/upstream/dldt.git] / inference-engine / thirdparty / fluid / modules / gapi / test / gapi_async_test.cpp
1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html.
4 //
5 // Copyright (C) 2019 Intel Corporation
6
7
8 #include "test_precomp.hpp"
9 #include "opencv2/gapi/gcomputation_async.hpp"
10 #include "opencv2/gapi/gcompiled_async.hpp"
11
12 #include <condition_variable>
13 #include <stdexcept>
14
15 namespace opencv_test
16 {
17 //Main idea behind these tests is to have the same test script that is parameterized in order to test all setups (GCompiled vs apply, callback vs future).
18 //So these differences are factored into devoted helper classes (mixins) which are then used by the common test script by help of CRTP.
19 //Actual GAPI Computation with parameters to run on is mixed into test via CRTP as well.
20
21 struct SumOfSum2x2 {
22     cv::GComputation sum_of_sum;
23     SumOfSum2x2() : sum_of_sum([]{
24         cv::GMat in;
25         cv::GScalar out = cv::gapi::sum(in + in);
26         return GComputation{in, out};
27     })
28     {}
29
30     const cv::Size sz{2, 2};
31     cv::Mat in_mat{sz, CV_8U, cv::Scalar(1)};
32     cv::Scalar out_sc;
33
34     cv::GCompiled compile(){
35         return sum_of_sum.compile(descr_of(in_mat));
36     }
37
38     cv::GComputation& computation(){
39         return sum_of_sum;
40     }
41
42     cv::GCompileArgs compile_args(){
43         return {};
44     }
45
46     cv::GRunArgs in_args(){
47         return cv::gin(in_mat);
48     }
49
50     cv::GRunArgsP out_args(){
51         return cv::gout(out_sc);
52     }
53
54     void verify(){
55         EXPECT_EQ(8, out_sc[0]);
56     }
57 };
58
59 namespace {
60     G_TYPED_KERNEL(GThrow, <GMat(GMat)>, "org.opencv.test.throw")
61     {
62         static GMatDesc outMeta(GMatDesc in) { return in;  }
63
64     };
65
66     struct gthrow_exception : std::runtime_error {
67         using std::runtime_error::runtime_error;
68     };
69
70     GAPI_OCV_KERNEL(GThrowImpl, GThrow)
71     {
72         static void run(const cv::Mat& in, cv::Mat&)
73         {
74             //this condition is needed to avoid "Unreachable code" warning on windows inside OCVCallHelper
75             if (!in.empty())
76             {
77                 throw gthrow_exception{"test"};
78             }
79         }
80     };
81 }
82
83 struct ExceptionOnExecution {
84     cv::GComputation throwing_gcomp;
85     ExceptionOnExecution() : throwing_gcomp([]{
86         cv::GMat in;
87         auto gout = GThrow::on(in);
88         return GComputation{in, gout};
89     })
90     {}
91
92
93     const cv::Size sz{2, 2};
94     cv::Mat in_mat{sz, CV_8U, cv::Scalar(1)};
95     cv::Mat out;
96
97     cv::GCompiled compile(){
98         return throwing_gcomp.compile(descr_of(in_mat), compile_args());
99     }
100
101     cv::GComputation& computation(){
102         return throwing_gcomp;
103     }
104
105     cv::GRunArgs in_args(){
106         return cv::gin(in_mat);
107     }
108
109     cv::GRunArgsP out_args(){
110         return cv::gout(out);
111     }
112
113     cv::GCompileArgs compile_args(){
114         auto pkg = cv::gapi::kernels<GThrowImpl>();
115         return cv::compile_args(pkg);
116     }
117
118 };
119
120 template<typename crtp_final_t>
121 struct crtp_cast {
122     template<typename crtp_base_t>
123     static crtp_final_t* crtp_cast_(crtp_base_t* this_)
124     {
125         return  static_cast<crtp_final_t*>(this_);
126     }
127 };
128
129 //Test Mixin, hiding details of callback based notification
130 template<typename crtp_final_t>
131 struct CallBack: crtp_cast<crtp_final_t> {
132     std::atomic<bool> callback_called = {false};
133     std::mutex mtx;
134     std::exception_ptr ep;
135
136     std::condition_variable cv;
137
138     std::function<void(std::exception_ptr)> callback(){
139         return [&](std::exception_ptr ep_){
140             ep = ep_;
141             callback_called = true;
142             mtx.lock();
143             mtx.unlock();
144             cv.notify_one();
145         };
146     };
147
148     template<typename... Args >
149     void start_async(Args&&... args){
150         this->crtp_cast_(this)->async(callback(), std::forward<Args>(args)...);
151     }
152
153     void wait_for_result()
154     {
155         std::unique_lock<std::mutex> lck{mtx};
156         cv.wait(lck,[&]{return callback_called == true;});
157         if (ep)
158         {
159             std::rethrow_exception(ep);
160         }
161     }
162 };
163
164 //Test Mixin, hiding details of future based notification
165 template<typename crtp_final_t>
166 struct Future: crtp_cast<crtp_final_t> {
167     std::future<void> f;
168
169     template<typename... Args >
170     void start_async(Args&&... args){
171         f = this->crtp_cast_(this)->async(std::forward<Args>(args)...);
172     }
173
174     void wait_for_result()
175     {
176         f.get();
177     }
178 };
179
180 //Test Mixin, hiding details of using compiled GAPI object
181 template<typename crtp_final_t>
182 struct AsyncCompiled  : crtp_cast<crtp_final_t>{
183
184     template<typename... Args>
185     auto async(Args&&... args) -> decltype(cv::gapi::wip::async(std::declval<cv::GCompiled&>(), std::forward<Args>(args)...)){
186         auto gcmpld = this->crtp_cast_(this)->compile();
187         return cv::gapi::wip::async(gcmpld, std::forward<Args>(args)...);
188     }
189 };
190
191 //Test Mixin, hiding details of calling apply (async_apply) on GAPI Computation object
192 template<typename crtp_final_t>
193 struct AsyncApply : crtp_cast<crtp_final_t> {
194
195     template<typename... Args>
196     auto async(Args&&... args) ->decltype(cv::gapi::wip::async_apply(std::declval<cv::GComputation&>(), std::forward<Args>(args)...)) {
197         return cv::gapi::wip::async_apply(this->crtp_cast_(this)->computation(), std::forward<Args>(args)..., this->crtp_cast_(this)->compile_args());
198     }
199 };
200
201
202 template<typename case_t>
203 struct normal: ::testing::Test, case_t{};
204
205 TYPED_TEST_CASE_P(normal);
206
207 TYPED_TEST_P(normal, basic){
208     //Normal scenario:  start function asynchronously and wait for the result, and verify it
209     this->start_async(this->in_args(), this->out_args());
210     this->wait_for_result();
211
212     this->verify();
213 }
214
215 REGISTER_TYPED_TEST_CASE_P(normal,
216         basic
217 );
218
219 template<typename case_t>
220 struct exception: ::testing::Test, case_t{};
221 TYPED_TEST_CASE_P(exception);
222
223 TYPED_TEST_P(exception, basic){
224     //Exceptional scenario:  start function asynchronously and make sure exception is passed to the user
225     this->start_async(this->in_args(), this->out_args());
226     EXPECT_THROW(this->wait_for_result(), gthrow_exception);
227 }
228
229 REGISTER_TYPED_TEST_CASE_P(exception,
230         basic
231 );
232
233 template<typename case_t>
234 struct stress : ::testing::Test{};
235 TYPED_TEST_CASE_P(stress);
236
237 TYPED_TEST_P(stress, test){
238     //Some stress testing: use a number of threads to start a bunch of async requests
239     const std::size_t request_per_thread = 10;
240     const std::size_t number_of_threads  = 4;
241
242     auto thread_body = [&](){
243         std::vector<TypeParam> requests{request_per_thread};
244         for (auto&& r : requests){
245             r.start_async(r.in_args(), r.out_args());
246         }
247
248         for (auto&& r : requests){
249             r.wait_for_result();
250             r.verify();
251         }
252     };
253
254     std::vector<std::thread> pool {number_of_threads};
255     for (auto&& t : pool){
256         t = std::thread{thread_body};
257     }
258
259     for (auto&& t : pool){
260         t.join();
261     }
262 }
263 REGISTER_TYPED_TEST_CASE_P(stress, test);
264
265 //little helpers to match up all combinations of setups
266 template<typename compute_fixture_t,template <typename> class callback_or_future_t, template <typename> class compiled_or_apply_t>
267 struct Case
268         : compute_fixture_t,
269           callback_or_future_t<Case<compute_fixture_t,callback_or_future_t,compiled_or_apply_t>>,
270           compiled_or_apply_t <Case<compute_fixture_t,callback_or_future_t,compiled_or_apply_t>>
271 {};
272
273 template<typename computation_t>
274 using cases = ::testing::Types<
275             Case<computation_t, CallBack, AsyncCompiled>,
276             Case<computation_t, CallBack, AsyncApply>,
277             Case<computation_t, Future,   AsyncCompiled>,
278             Case<computation_t, Future,   AsyncApply>
279             >;
280 INSTANTIATE_TYPED_TEST_CASE_P(AsyncAPINormalFlow_,        normal,     cases<SumOfSum2x2>);
281 INSTANTIATE_TYPED_TEST_CASE_P(AsyncAPIExceptionHandling_, exception,  cases<ExceptionOnExecution>);
282
283 INSTANTIATE_TYPED_TEST_CASE_P(AsyncAPIStress,             stress,     cases<SumOfSum2x2>);
284
285 TEST(AsyncAPI, Sample){
286     cv::GComputation self_mul([]{
287         cv::GMat in;
288         cv::GMat out = cv::gapi::mul(in, in);
289         return GComputation{in, out};
290     });
291
292     const cv::Size sz{2, 2};
293     cv::Mat in_mat{sz, CV_8U, cv::Scalar(1)};
294     cv::Mat out;
295
296     auto f = cv::gapi::wip::async_apply(self_mul,cv::gin(in_mat), cv::gout(out));
297     f.wait();
298 }
299 } // namespace opencv_test