Merge remote-tracking branch 'upstream/3.4' into merge-3.4
[platform/upstream/opencv.git] / modules / ts / src / ts.cpp
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                        Intel License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 //   * Redistribution's of source code must retain the above copyright notice,
20 //     this list of conditions and the following disclaimer.
21 //
22 //   * Redistribution's in binary form must reproduce the above copyright notice,
23 //     this list of conditions and the following disclaimer in the documentation
24 //     and/or other materials provided with the distribution.
25 //
26 //   * The name of Intel Corporation may not be used to endorse or promote products
27 //     derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/
41
42 #include "precomp.hpp"
43 #include <opencv2/core/utils/configuration.private.hpp>
44
45 #include "opencv2/core/core_c.h"
46
47 #include <ctype.h>
48 #include <stdarg.h>
49 #include <stdlib.h>
50 #include <fcntl.h>
51 #include <time.h>
52 #if defined _WIN32
53 #include <io.h>
54
55 #include <windows.h>
56 #undef small
57 #undef min
58 #undef max
59 #undef abs
60
61 #ifdef _MSC_VER
62 #include <eh.h>
63 #endif
64
65 #else
66 #include <unistd.h>
67 #include <signal.h>
68 #include <setjmp.h>
69 #endif
70
71 // isDirectory
72 #if defined _WIN32 || defined WINCE
73 # include <windows.h>
74 #else
75 # include <dirent.h>
76 # include <sys/stat.h>
77 #endif
78
79 #ifdef HAVE_OPENCL
80
81 #define DUMP_CONFIG_PROPERTY(propertyName, propertyValue) \
82     do { \
83         std::stringstream ssName, ssValue;\
84         ssName << propertyName;\
85         ssValue << (propertyValue); \
86         ::testing::Test::RecordProperty(ssName.str(), ssValue.str()); \
87     } while (false)
88
89 #define DUMP_MESSAGE_STDOUT(msg) \
90     do { \
91         std::cout << msg << std::endl; \
92     } while (false)
93
94 #include "opencv2/core/opencl/opencl_info.hpp"
95
96 #include "opencv2/core/utils/allocator_stats.hpp"
97 namespace cv { namespace ocl {
98 cv::utils::AllocatorStatisticsInterface& getOpenCLAllocatorStatistics();
99 }}
100 #endif // HAVE_OPENCL
101
102 #include "opencv2/core/utils/allocator_stats.hpp"
103 namespace cv {
104 CV_EXPORTS cv::utils::AllocatorStatisticsInterface& getAllocatorStatistics();
105 }
106
107 #include "opencv_tests_config.hpp"
108
109 #include "ts_tags.hpp"
110
111 #if defined(__GNUC__) && defined(__linux__)
112 extern "C" {
113 size_t malloc_peak(void) __attribute__((weak));
114 void malloc_reset_peak(void) __attribute__((weak));
115 } // extern "C"
116 #else // stubs
117 static size_t (*malloc_peak)(void) = 0;
118 static void (*malloc_reset_peak)(void) = 0;
119 #endif
120
121 namespace opencv_test {
122 bool required_opencv_test_namespace = false;  // compilation check for non-refactored tests
123 }
124
125 namespace cvtest
126 {
127
128 uint64 param_seed = 0x12345678; // real value is passed via parseCustomOptions function
129
130 static std::string path_join(const std::string& prefix, const std::string& subpath)
131 {
132     CV_Assert(subpath.empty() || subpath[0] != '/');
133     if (prefix.empty())
134         return subpath;
135     bool skipSlash = prefix.size() > 0 ? (prefix[prefix.size()-1] == '/' || prefix[prefix.size()-1] == '\\') : false;
136     std::string path = prefix + (skipSlash ? "" : "/") + subpath;
137     return path;
138 }
139
140
141
142 /*****************************************************************************************\
143 *                                Exception and memory handlers                            *
144 \*****************************************************************************************/
145
146 // a few platform-dependent declarations
147
148 #if defined _WIN32
149 #ifdef _MSC_VER
150 static void SEHTranslator( unsigned int /*u*/, EXCEPTION_POINTERS* pExp )
151 {
152     TS::FailureCode code = TS::FAIL_EXCEPTION;
153     switch( pExp->ExceptionRecord->ExceptionCode )
154     {
155     case EXCEPTION_ACCESS_VIOLATION:
156     case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
157     case EXCEPTION_DATATYPE_MISALIGNMENT:
158     case EXCEPTION_FLT_STACK_CHECK:
159     case EXCEPTION_STACK_OVERFLOW:
160     case EXCEPTION_IN_PAGE_ERROR:
161         code = TS::FAIL_MEMORY_EXCEPTION;
162         break;
163     case EXCEPTION_FLT_DENORMAL_OPERAND:
164     case EXCEPTION_FLT_DIVIDE_BY_ZERO:
165     case EXCEPTION_FLT_INEXACT_RESULT:
166     case EXCEPTION_FLT_INVALID_OPERATION:
167     case EXCEPTION_FLT_OVERFLOW:
168     case EXCEPTION_FLT_UNDERFLOW:
169     case EXCEPTION_INT_DIVIDE_BY_ZERO:
170     case EXCEPTION_INT_OVERFLOW:
171         code = TS::FAIL_ARITHM_EXCEPTION;
172         break;
173     case EXCEPTION_BREAKPOINT:
174     case EXCEPTION_ILLEGAL_INSTRUCTION:
175     case EXCEPTION_INVALID_DISPOSITION:
176     case EXCEPTION_NONCONTINUABLE_EXCEPTION:
177     case EXCEPTION_PRIV_INSTRUCTION:
178     case EXCEPTION_SINGLE_STEP:
179         code = TS::FAIL_EXCEPTION;
180     }
181     throw code;
182 }
183 #endif
184
185 #else
186
187 static const int tsSigId[] = { SIGSEGV, SIGBUS, SIGFPE, SIGILL, SIGABRT, -1 };
188
189 static jmp_buf tsJmpMark;
190
191 static void signalHandler( int sig_code )
192 {
193     TS::FailureCode code = TS::FAIL_EXCEPTION;
194     switch( sig_code )
195     {
196     case SIGFPE:
197         code = TS::FAIL_ARITHM_EXCEPTION;
198         break;
199     case SIGSEGV:
200     case SIGBUS:
201         code = TS::FAIL_ARITHM_EXCEPTION;
202         break;
203     case SIGILL:
204         code = TS::FAIL_EXCEPTION;
205     }
206
207     longjmp( tsJmpMark, (int)code );
208 }
209
210 #endif
211
212
213 // reads 16-digit hexadecimal number (i.e. 64-bit integer)
214 int64 readSeed( const char* str )
215 {
216     int64 val = 0;
217     if( str && strlen(str) == 16 )
218     {
219         for( int i = 0; str[i]; i++ )
220         {
221             int c = tolower(str[i]);
222             if( !isxdigit(c) )
223                 return 0;
224             val = val * 16 +
225             (str[i] < 'a' ? str[i] - '0' : str[i] - 'a' + 10);
226         }
227     }
228     return val;
229 }
230
231
232 /*****************************************************************************************\
233 *                                    Base Class for Tests                                 *
234 \*****************************************************************************************/
235
236 BaseTest::BaseTest()
237 {
238     ts = TS::ptr();
239     test_case_count = -1;
240 }
241
242 BaseTest::~BaseTest()
243 {
244     clear();
245 }
246
247 void BaseTest::clear()
248 {
249 }
250
251
252 cv::FileNode BaseTest::find_param( const cv::FileStorage& fs, const char* param_name )
253 {
254     cv::FileNode node = fs[get_name()];
255     return node[param_name];
256 }
257
258
259 int BaseTest::read_params( const cv::FileStorage& )
260 {
261     return 0;
262 }
263
264
265 bool BaseTest::can_do_fast_forward()
266 {
267     return true;
268 }
269
270
271 void BaseTest::safe_run( int start_from )
272 {
273     CV_TRACE_FUNCTION();
274     ts->update_context( 0, -1, true );
275     ts->update_context( this, -1, true );
276
277     if( !::testing::GTEST_FLAG(catch_exceptions) )
278         run( start_from );
279     else
280     {
281         try
282         {
283         #if !defined _WIN32
284         int _code = setjmp( tsJmpMark );
285         if( !_code )
286             run( start_from );
287         else
288             throw TS::FailureCode(_code);
289         #else
290             run( start_from );
291         #endif
292         }
293         catch (const cv::Exception& exc)
294         {
295             const char* errorStr = cvErrorStr(exc.code);
296             char buf[1 << 16];
297
298             const char* delim = exc.err.find('\n') == cv::String::npos ? "" : "\n";
299             sprintf( buf, "OpenCV Error:\n\t%s (%s%s) in %s, file %s, line %d",
300                     errorStr, delim, exc.err.c_str(), exc.func.size() > 0 ?
301                     exc.func.c_str() : "unknown function", exc.file.c_str(), exc.line );
302             ts->printf(TS::LOG, "%s\n", buf);
303
304             ts->set_failed_test_info( TS::FAIL_ERROR_IN_CALLED_FUNC );
305         }
306         catch (const TS::FailureCode& fc)
307         {
308             std::string errorStr = TS::str_from_code(fc);
309             ts->printf(TS::LOG, "General failure:\n\t%s (%d)\n", errorStr.c_str(), fc);
310
311             ts->set_failed_test_info( fc );
312         }
313         catch (...)
314         {
315             ts->printf(TS::LOG, "Unknown failure\n");
316
317             ts->set_failed_test_info( TS::FAIL_EXCEPTION );
318         }
319     }
320
321     ts->set_gtest_status();
322 }
323
324
325 void BaseTest::run( int start_from )
326 {
327     int test_case_idx, count = get_test_case_count();
328     int64 t_start = cvGetTickCount();
329     double freq = cv::getTickFrequency();
330     bool ff = can_do_fast_forward();
331     int progress = 0, code;
332     int64 t1 = t_start;
333
334     for( test_case_idx = ff && start_from >= 0 ? start_from : 0;
335          count < 0 || test_case_idx < count; test_case_idx++ )
336     {
337         ts->update_context( this, test_case_idx, ff );
338         progress = update_progress( progress, test_case_idx, count, (double)(t1 - t_start)/(freq*1000) );
339
340         code = prepare_test_case( test_case_idx );
341         if( code < 0 || ts->get_err_code() < 0 )
342             return;
343
344         if( code == 0 )
345             continue;
346
347         run_func();
348
349         if( ts->get_err_code() < 0 )
350             return;
351
352         if( validate_test_results( test_case_idx ) < 0 || ts->get_err_code() < 0 )
353             return;
354     }
355 }
356
357
358 void BaseTest::run_func(void)
359 {
360     assert(0);
361 }
362
363
364 int BaseTest::get_test_case_count(void)
365 {
366     return test_case_count;
367 }
368
369
370 int BaseTest::prepare_test_case( int )
371 {
372     return 0;
373 }
374
375
376 int BaseTest::validate_test_results( int )
377 {
378     return 0;
379 }
380
381
382 int BaseTest::update_progress( int progress, int test_case_idx, int count, double dt )
383 {
384     int width = 60 - (int)get_name().size();
385     if( count > 0 )
386     {
387         int t = cvRound( ((double)test_case_idx * width)/count );
388         if( t > progress )
389         {
390             ts->printf( TS::CONSOLE, "." );
391             progress = t;
392         }
393     }
394     else if( cvRound(dt) > progress )
395     {
396         ts->printf( TS::CONSOLE, "." );
397         progress = cvRound(dt);
398     }
399
400     return progress;
401 }
402
403
404 BadArgTest::BadArgTest()
405 {
406     test_case_idx   = -1;
407     // oldErrorCbk     = 0;
408     // oldErrorCbkData = 0;
409 }
410
411 BadArgTest::~BadArgTest(void)
412 {
413 }
414
415 int BadArgTest::run_test_case( int expected_code, const string& _descr )
416 {
417     int errcount = 0;
418     bool thrown = false;
419     const char* descr = _descr.c_str() ? _descr.c_str() : "";
420
421     try
422     {
423         run_func();
424     }
425     catch(const cv::Exception& e)
426     {
427         thrown = true;
428         if (e.code != expected_code &&
429             e.code != cv::Error::StsError && e.code != cv::Error::StsAssert  // Exact error codes support will be dropped. Checks should provide proper text messages intead.
430         )
431         {
432             ts->printf(TS::LOG, "%s (test case #%d): the error code %d is different from the expected %d\n",
433                        descr, test_case_idx, e.code, expected_code);
434             errcount = 1;
435         }
436     }
437     catch(...)
438     {
439         thrown = true;
440         ts->printf(TS::LOG, "%s  (test case #%d): unknown exception was thrown (the function has likely crashed)\n",
441                    descr, test_case_idx);
442         errcount = 1;
443     }
444
445     if(!thrown)
446     {
447         ts->printf(TS::LOG, "%s  (test case #%d): no expected exception was thrown\n",
448                    descr, test_case_idx);
449         errcount = 1;
450     }
451     test_case_idx++;
452
453     return errcount;
454 }
455
456 /*****************************************************************************************\
457 *                                 Base Class for Test System                              *
458 \*****************************************************************************************/
459
460 /******************************** Constructors/Destructors ******************************/
461
462 TSParams::TSParams()
463 {
464     rng_seed = (uint64)-1;
465     use_optimized = true;
466     test_case_count_scale = 1;
467 }
468
469
470 TestInfo::TestInfo()
471 {
472     test = 0;
473     code = 0;
474     rng_seed = rng_seed0 = 0;
475     test_case_idx = -1;
476 }
477
478
479 TS::TS()
480 {
481 } // ctor
482
483
484 TS::~TS()
485 {
486 } // dtor
487
488
489 string TS::str_from_code( const TS::FailureCode code )
490 {
491     switch( code )
492     {
493     case OK:                           return "Ok";
494     case FAIL_GENERIC:                 return "Generic/Unknown";
495     case FAIL_MISSING_TEST_DATA:       return "No test data";
496     case FAIL_INVALID_TEST_DATA:       return "Invalid test data";
497     case FAIL_ERROR_IN_CALLED_FUNC:    return "cvError invoked";
498     case FAIL_EXCEPTION:               return "Hardware/OS exception";
499     case FAIL_MEMORY_EXCEPTION:        return "Invalid memory access";
500     case FAIL_ARITHM_EXCEPTION:        return "Arithmetic exception";
501     case FAIL_MEMORY_CORRUPTION_BEGIN: return "Corrupted memblock (beginning)";
502     case FAIL_MEMORY_CORRUPTION_END:   return "Corrupted memblock (end)";
503     case FAIL_MEMORY_LEAK:             return "Memory leak";
504     case FAIL_INVALID_OUTPUT:          return "Invalid function output";
505     case FAIL_MISMATCH:                return "Unexpected output";
506     case FAIL_BAD_ACCURACY:            return "Bad accuracy";
507     case FAIL_HANG:                    return "Infinite loop(?)";
508     case FAIL_BAD_ARG_CHECK:           return "Incorrect handling of bad arguments";
509     default:
510             ;
511     }
512     return "Generic/Unknown";
513 }
514
515 static int tsErrorCallback( int status, const char* func_name, const char* err_msg, const char* file_name, int line, TS* ts )
516 {
517     const char* delim = std::string(err_msg).find('\n') == std::string::npos ? "" : "\n";
518     ts->printf(TS::LOG, "OpenCV Error:\n\t%s (%s%s) in %s, file %s, line %d\n", cvErrorStr(status), delim, err_msg, func_name[0] != 0 ? func_name : "unknown function", file_name, line);
519     return 0;
520 }
521
522 /************************************** Running tests **********************************/
523
524 void TS::init( const string& modulename )
525 {
526     data_search_subdir.push_back(modulename);
527 #ifndef WINRT
528     char* datapath_dir = getenv("OPENCV_TEST_DATA_PATH");
529 #else
530     char* datapath_dir = OPENCV_TEST_DATA_PATH;
531 #endif
532
533     if( datapath_dir )
534     {
535         data_path = path_join(path_join(datapath_dir, modulename), "");
536     }
537
538     cv::redirectError((cv::ErrorCallback)tsErrorCallback, this);
539
540     if( ::testing::GTEST_FLAG(catch_exceptions) )
541     {
542 #if defined _WIN32
543 #ifdef _MSC_VER
544         _set_se_translator( SEHTranslator );
545 #endif
546 #else
547         for( int i = 0; tsSigId[i] >= 0; i++ )
548             signal( tsSigId[i], signalHandler );
549 #endif
550     }
551     else
552     {
553 #if defined _WIN32
554 #ifdef _MSC_VER
555         _set_se_translator( 0 );
556 #endif
557 #else
558         for( int i = 0; tsSigId[i] >= 0; i++ )
559             signal( tsSigId[i], SIG_DFL );
560 #endif
561     }
562
563     if( params.use_optimized == 0 )
564         cv::setUseOptimized(false);
565
566     rng = RNG(params.rng_seed);
567 }
568
569
570 void TS::set_gtest_status()
571 {
572     TS::FailureCode code = get_err_code();
573     if( code >= 0 )
574         return SUCCEED();
575
576     char seedstr[32];
577     sprintf(seedstr, "%08x%08x", (unsigned)(current_test_info.rng_seed>>32),
578                                 (unsigned)(current_test_info.rng_seed));
579
580     string logs = "";
581     if( !output_buf[SUMMARY_IDX].empty() )
582         logs += "\n-----------------------------------\n\tSUM: " + output_buf[SUMMARY_IDX];
583     if( !output_buf[LOG_IDX].empty() )
584         logs += "\n-----------------------------------\n\tLOG:\n" + output_buf[LOG_IDX];
585     if( !output_buf[CONSOLE_IDX].empty() )
586         logs += "\n-----------------------------------\n\tCONSOLE: " + output_buf[CONSOLE_IDX];
587     logs += "\n-----------------------------------\n";
588
589     FAIL() << "\n\tfailure reason: " << str_from_code(code) <<
590         "\n\ttest case #" << current_test_info.test_case_idx <<
591         "\n\tseed: " << seedstr << logs;
592 }
593
594
595 void TS::update_context( BaseTest* test, int test_case_idx, bool update_ts_context )
596 {
597     if( current_test_info.test != test )
598     {
599         for( int i = 0; i <= CONSOLE_IDX; i++ )
600             output_buf[i] = string();
601         rng = RNG(params.rng_seed);
602         current_test_info.rng_seed0 = current_test_info.rng_seed = rng.state;
603     }
604
605     current_test_info.test = test;
606     current_test_info.test_case_idx = test_case_idx;
607     current_test_info.code = 0;
608     cvSetErrStatus( CV_StsOk );
609     if( update_ts_context )
610         current_test_info.rng_seed = rng.state;
611 }
612
613
614 void TS::set_failed_test_info( int fail_code )
615 {
616     if( current_test_info.code >= 0 )
617         current_test_info.code = TS::FailureCode(fail_code);
618 }
619
620 #if defined _MSC_VER && _MSC_VER < 1400
621 #undef vsnprintf
622 #define vsnprintf _vsnprintf
623 #endif
624
625 void TS::vprintf( int streams, const char* fmt, va_list l )
626 {
627     char str[1 << 14];
628     vsnprintf( str, sizeof(str)-1, fmt, l );
629
630     for( int i = 0; i < MAX_IDX; i++ )
631         if( (streams & (1 << i)) )
632         {
633             output_buf[i] += std::string(str);
634             // in the new GTest-based framework we do not use
635             // any output files (except for the automatically generated xml report).
636             // if a test fails, all the buffers are printed, so we do not want to duplicate the information and
637             // thus only add the new information to a single buffer and return from the function.
638             break;
639         }
640 }
641
642
643 void TS::printf( int streams, const char* fmt, ... )
644 {
645     if( streams )
646     {
647         va_list l;
648         va_start( l, fmt );
649         vprintf( streams, fmt, l );
650         va_end( l );
651     }
652 }
653
654
655 TS* TS::ptr()
656 {
657     static TS ts;
658     return &ts;
659 }
660
661 void fillGradient(Mat& img, int delta)
662 {
663     const int ch = img.channels();
664     CV_Assert(!img.empty() && img.depth() == CV_8U && ch <= 4);
665
666     int n = 255 / delta;
667     int r, c, i;
668     for(r=0; r<img.rows; r++)
669     {
670         int kR = r % (2*n);
671         int valR = (kR<=n) ? delta*kR : delta*(2*n-kR);
672         for(c=0; c<img.cols; c++)
673         {
674             int kC = c % (2*n);
675             int valC = (kC<=n) ? delta*kC : delta*(2*n-kC);
676             uchar vals[] = {uchar(valR), uchar(valC), uchar(200*r/img.rows), uchar(255)};
677             uchar *p = img.ptr(r, c);
678             for(i=0; i<ch; i++) p[i] = vals[i];
679         }
680     }
681 }
682
683 void smoothBorder(Mat& img, const Scalar& color, int delta)
684 {
685     const int ch = img.channels();
686     CV_Assert(!img.empty() && img.depth() == CV_8U && ch <= 4);
687
688     Scalar s;
689     uchar *p = NULL;
690     int n = 100/delta;
691     int nR = std::min(n, (img.rows+1)/2), nC = std::min(n, (img.cols+1)/2);
692
693     int r, c, i;
694     for(r=0; r<nR; r++)
695     {
696         double k1 = r*delta/100., k2 = 1-k1;
697         for(c=0; c<img.cols; c++)
698         {
699             p = img.ptr(r, c);
700             for(i=0; i<ch; i++) s[i] = p[i];
701             s = s * k1 + color * k2;
702             for(i=0; i<ch; i++) p[i] = uchar(s[i]);
703         }
704         for(c=0; c<img.cols; c++)
705         {
706             p = img.ptr(img.rows-r-1, c);
707             for(i=0; i<ch; i++) s[i] = p[i];
708             s = s * k1 + color * k2;
709             for(i=0; i<ch; i++) p[i] = uchar(s[i]);
710         }
711     }
712
713     for(r=0; r<img.rows; r++)
714     {
715         for(c=0; c<nC; c++)
716         {
717             double k1 = c*delta/100., k2 = 1-k1;
718             p = img.ptr(r, c);
719             for(i=0; i<ch; i++) s[i] = p[i];
720             s = s * k1 + color * k2;
721             for(i=0; i<ch; i++) p[i] = uchar(s[i]);
722         }
723         for(c=0; c<n; c++)
724         {
725             double k1 = c*delta/100., k2 = 1-k1;
726             p = img.ptr(r, img.cols-c-1);
727             for(i=0; i<ch; i++) s[i] = p[i];
728             s = s * k1 + color * k2;
729             for(i=0; i<ch; i++) p[i] = uchar(s[i]);
730         }
731     }
732 }
733
734
735 bool test_ipp_check = false;
736
737 void checkIppStatus()
738 {
739     if (test_ipp_check)
740     {
741         int status = cv::ipp::getIppStatus();
742         EXPECT_LE(0, status) << cv::ipp::getIppErrorLocation().c_str();
743     }
744 }
745
746 static bool checkTestData = cv::utils::getConfigurationParameterBool("OPENCV_TEST_REQUIRE_DATA", false);
747 bool skipUnstableTests = false;
748 bool runBigDataTests = false;
749 int testThreads = 0;
750
751
752 static size_t memory_usage_base = 0;
753 static uint64_t memory_usage_base_opencv = 0;
754 #ifdef HAVE_OPENCL
755 static uint64_t memory_usage_base_opencl = 0;
756 #endif
757
758 void testSetUp()
759 {
760     fflush(stdout); fflush(stderr);
761     cv::ipp::setIppStatus(0);
762     cv::theRNG().state = cvtest::param_seed;
763     cv::setNumThreads(cvtest::testThreads);
764     if (malloc_peak)  // if memory profiler is available
765     {
766         malloc_reset_peak();
767         memory_usage_base = malloc_peak(); // equal to malloc_current()
768     }
769     {
770         cv::utils::AllocatorStatisticsInterface& ocv_stats = cv::getAllocatorStatistics();
771         ocv_stats.resetPeakUsage();
772         memory_usage_base_opencv = ocv_stats.getCurrentUsage();
773     }
774 #ifdef HAVE_OPENCL
775     {
776         cv::utils::AllocatorStatisticsInterface& ocl_stats = cv::ocl::getOpenCLAllocatorStatistics();
777         ocl_stats.resetPeakUsage();
778         memory_usage_base_opencl = ocl_stats.getCurrentUsage();
779     }
780 #endif
781     checkTestTags();
782 }
783
784 void testTearDown()
785 {
786     ::cvtest::checkIppStatus();
787     uint64_t memory_usage = 0;
788     uint64_t ocv_memory_usage = 0, ocv_peak = 0;
789     if (malloc_peak)  // if memory profiler is available
790     {
791         size_t peak = malloc_peak();
792         memory_usage = peak - memory_usage_base;
793         if (peak > 0)
794         {
795             CV_LOG_INFO(NULL, "Memory_usage (malloc): " << memory_usage << " (base=" << memory_usage_base << ")");
796         }
797     }
798     {
799         // core/src/alloc.cpp: #define OPENCV_ALLOC_ENABLE_STATISTICS
800         // handle large buffers via fastAlloc()
801         // (not always accurate on heavy 3rdparty usage, like protobuf)
802         cv::utils::AllocatorStatisticsInterface& ocv_stats = cv::getAllocatorStatistics();
803         ocv_peak = ocv_stats.getPeakUsage();
804         ocv_memory_usage = ocv_peak - memory_usage_base_opencv;
805         if (ocv_peak)
806         {
807             CV_LOG_INFO(NULL, "Memory_usage (OpenCV): " << ocv_memory_usage << " (base=" << memory_usage_base_opencv << "  current=" << ocv_stats.getCurrentUsage() << ")");
808         }
809         if (memory_usage == 0)  // external profiler has higher priority (and accuracy)
810             memory_usage = ocv_memory_usage;
811     }
812 #ifdef HAVE_OPENCL
813     uint64_t ocl_memory_usage = 0, ocl_peak = 0;
814     {
815         cv::utils::AllocatorStatisticsInterface& ocl_stats = cv::ocl::getOpenCLAllocatorStatistics();
816         ocl_peak = ocl_stats.getPeakUsage();
817         ocl_memory_usage = ocl_peak - memory_usage_base_opencl;
818         if (ocl_memory_usage > 0)
819         {
820             CV_LOG_INFO(NULL, "Memory_usage (OpenCL): " << ocl_memory_usage << " (base=" << memory_usage_base_opencl << "  current=" << ocl_stats.getCurrentUsage() << ")");
821         }
822         ::testing::Test::RecordProperty("ocl_memory_usage",
823                 cv::format("%llu", (unsigned long long)ocl_memory_usage));
824     }
825 #else
826     uint64_t ocl_memory_usage = 0;
827 #endif
828     if (malloc_peak      // external memory profiler is available
829         || ocv_peak > 0  // or enabled OpenCV builtin allocation statistics
830     )
831     {
832         CV_LOG_INFO(NULL, "Memory usage total: " << (memory_usage + ocl_memory_usage));
833         ::testing::Test::RecordProperty("memory_usage",
834                 cv::format("%llu", (unsigned long long)memory_usage));
835         ::testing::Test::RecordProperty("total_memory_usage",
836                 cv::format("%llu", (unsigned long long)(memory_usage + ocl_memory_usage)));
837     }
838 }
839
840 void parseCustomOptions(int argc, char **argv)
841 {
842     const string command_line_keys = string(
843         "{ ipp test_ipp_check |false    |check whether IPP works without failures }"
844         "{ test_seed          |809564   |seed for random numbers generator }"
845         "{ test_threads       |-1       |the number of worker threads, if parallel execution is enabled}"
846         "{ skip_unstable      |false    |skip unstable tests }"
847         "{ test_bigdata       |false    |run BigData tests (>=2Gb) }"
848         "{ test_require_data  |") + (checkTestData ? "true" : "false") + string("|fail on missing non-required test data instead of skip (env:OPENCV_TEST_REQUIRE_DATA)}"
849         CV_TEST_TAGS_PARAMS
850         "{ h   help           |false    |print help info                          }"
851     );
852
853     cv::CommandLineParser parser(argc, argv, command_line_keys);
854     if (parser.get<bool>("help"))
855     {
856         std::cout << "\nAvailable options besides google test option: \n";
857         parser.printMessage();
858     }
859
860     test_ipp_check = parser.get<bool>("test_ipp_check");
861     if (!test_ipp_check)
862 #ifndef WINRT
863         test_ipp_check = getenv("OPENCV_IPP_CHECK") != NULL;
864 #else
865         test_ipp_check = false;
866 #endif
867
868     param_seed = parser.get<unsigned int>("test_seed");
869
870     testThreads = parser.get<int>("test_threads");
871
872     skipUnstableTests = parser.get<bool>("skip_unstable");
873     runBigDataTests = parser.get<bool>("test_bigdata");
874     if (parser.has("test_require_data"))
875         checkTestData = parser.get<bool>("test_require_data");
876
877     activateTestTags(parser);
878 }
879
880 static bool isDirectory(const std::string& path)
881 {
882 #if defined _WIN32 || defined WINCE
883     WIN32_FILE_ATTRIBUTE_DATA all_attrs;
884 #ifdef WINRT
885     wchar_t wpath[MAX_PATH];
886     size_t copied = mbstowcs(wpath, path.c_str(), MAX_PATH);
887     CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
888     BOOL status = ::GetFileAttributesExW(wpath, GetFileExInfoStandard, &all_attrs);
889 #else
890     BOOL status = ::GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &all_attrs);
891 #endif
892     DWORD attributes = all_attrs.dwFileAttributes;
893     return status && ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
894 #else
895     struct stat s;
896     if (0 != stat(path.c_str(), &s))
897         return false;
898     return S_ISDIR(s.st_mode);
899 #endif
900 }
901
902 void addDataSearchPath(const std::string& path)
903 {
904     if (isDirectory(path))
905         TS::ptr()->data_search_path.push_back(path);
906 }
907 void addDataSearchSubDirectory(const std::string& subdir)
908 {
909     TS::ptr()->data_search_subdir.push_back(subdir);
910 }
911
912 static std::string findData(const std::string& relative_path, bool required, bool findDirectory)
913 {
914 #define TEST_TRY_FILE_WITH_PREFIX(prefix) \
915 { \
916     std::string path = path_join(prefix, relative_path); \
917     /*printf("Trying %s\n", path.c_str());*/ \
918     if (findDirectory) \
919     { \
920         if (isDirectory(path)) \
921             return path; \
922     } \
923     else \
924     { \
925         FILE* f = fopen(path.c_str(), "rb"); \
926         if(f) { \
927             fclose(f); \
928             return path; \
929         } \
930     } \
931 }
932
933     const std::vector<std::string>& search_path = TS::ptr()->data_search_path;
934     for(size_t i = search_path.size(); i > 0; i--)
935     {
936         const std::string& prefix = search_path[i - 1];
937         TEST_TRY_FILE_WITH_PREFIX(prefix);
938     }
939
940     const std::vector<std::string>& search_subdir = TS::ptr()->data_search_subdir;
941
942 #ifndef WINRT
943     char* datapath_dir = getenv("OPENCV_TEST_DATA_PATH");
944 #else
945     char* datapath_dir = OPENCV_TEST_DATA_PATH;
946 #endif
947
948     std::string datapath;
949     if (datapath_dir)
950     {
951         datapath = datapath_dir;
952         //CV_Assert(isDirectory(datapath) && "OPENCV_TEST_DATA_PATH is specified but it doesn't exist");
953         if (isDirectory(datapath))
954         {
955             for(size_t i = search_subdir.size(); i > 0; i--)
956             {
957                 const std::string& subdir = search_subdir[i - 1];
958                 std::string prefix = path_join(datapath, subdir);
959                 TEST_TRY_FILE_WITH_PREFIX(prefix);
960             }
961         }
962     }
963 #ifdef OPENCV_TEST_DATA_INSTALL_PATH
964     datapath = path_join("./", OPENCV_TEST_DATA_INSTALL_PATH);
965     if (isDirectory(datapath))
966     {
967         for(size_t i = search_subdir.size(); i > 0; i--)
968         {
969             const std::string& subdir = search_subdir[i - 1];
970             std::string prefix = path_join(datapath, subdir);
971             TEST_TRY_FILE_WITH_PREFIX(prefix);
972         }
973     }
974 #ifdef OPENCV_INSTALL_PREFIX
975     else
976     {
977         datapath = path_join(OPENCV_INSTALL_PREFIX, OPENCV_TEST_DATA_INSTALL_PATH);
978         if (isDirectory(datapath))
979         {
980             for(size_t i = search_subdir.size(); i > 0; i--)
981             {
982                 const std::string& subdir = search_subdir[i - 1];
983                 std::string prefix = path_join(datapath, subdir);
984                 TEST_TRY_FILE_WITH_PREFIX(prefix);
985             }
986         }
987     }
988 #endif
989 #endif
990     const char* type = findDirectory ? "directory" : "data file";
991     if (required || checkTestData)
992         CV_Error(cv::Error::StsError, cv::format("OpenCV tests: Can't find required %s: %s", type, relative_path.c_str()));
993     throw SkipTestException(cv::format("OpenCV tests: Can't find %s: %s", type, relative_path.c_str()));
994 }
995
996 std::string findDataFile(const std::string& relative_path, bool required)
997 {
998     return findData(relative_path, required, false);
999 }
1000
1001 std::string findDataDirectory(const std::string& relative_path, bool required)
1002 {
1003     return findData(relative_path, required, true);
1004 }
1005
1006 inline static std::string getSnippetFromConfig(const std::string & start, const std::string & end)
1007 {
1008     const std::string buildInfo = cv::getBuildInformation();
1009     size_t pos1 = buildInfo.find(start);
1010     if (pos1 != std::string::npos)
1011     {
1012         pos1 += start.length();
1013         pos1 = buildInfo.find_first_not_of(" \t\n\r", pos1);
1014     }
1015     size_t pos2 = buildInfo.find(end, pos1);
1016     if (pos2 != std::string::npos)
1017     {
1018         pos2 = buildInfo.find_last_not_of(" \t\n\r", pos2);
1019     }
1020     if (pos1 != std::string::npos && pos2 != std::string::npos && pos1 < pos2)
1021     {
1022         return buildInfo.substr(pos1, pos2 - pos1 + 1);
1023     }
1024     return std::string();
1025 }
1026
1027 inline static void recordPropertyVerbose(const std::string & property,
1028                                          const std::string & msg,
1029                                          const std::string & value,
1030                                          const std::string & build_value = std::string())
1031 {
1032     ::testing::Test::RecordProperty(property, value);
1033     std::cout << msg << ": " << (value.empty() ? std::string("N/A") : value) << std::endl;
1034     if (!build_value.empty())
1035     {
1036         ::testing::Test::RecordProperty(property + "_build", build_value);
1037         if (build_value != value)
1038             std::cout << "WARNING: build value differs from runtime: " << build_value << endl;
1039     }
1040 }
1041
1042 inline static void recordPropertyVerbose(const std::string& property, const std::string& msg,
1043                                          const char* value, const char* build_value = NULL)
1044 {
1045     return recordPropertyVerbose(property, msg,
1046         value ? std::string(value) : std::string(),
1047         build_value ? std::string(build_value) : std::string());
1048 }
1049
1050 #ifdef _DEBUG
1051 #define CV_TEST_BUILD_CONFIG "Debug"
1052 #else
1053 #define CV_TEST_BUILD_CONFIG "Release"
1054 #endif
1055
1056 void SystemInfoCollector::OnTestProgramStart(const testing::UnitTest&)
1057 {
1058     std::cout << "CTEST_FULL_OUTPUT" << std::endl; // Tell CTest not to discard any output
1059     recordPropertyVerbose("cv_version", "OpenCV version", cv::getVersionString(), CV_VERSION);
1060     recordPropertyVerbose("cv_vcs_version", "OpenCV VCS version", getSnippetFromConfig("Version control:", "\n"));
1061     recordPropertyVerbose("cv_build_type", "Build type", getSnippetFromConfig("Configuration:", "\n"), CV_TEST_BUILD_CONFIG);
1062     recordPropertyVerbose("cv_compiler", "Compiler", getSnippetFromConfig("C++ Compiler:", "\n"));
1063     recordPropertyVerbose("cv_parallel_framework", "Parallel framework", cv::currentParallelFramework());
1064     recordPropertyVerbose("cv_cpu_features", "CPU features", cv::getCPUFeaturesLine());
1065 #ifdef HAVE_IPP
1066     recordPropertyVerbose("cv_ipp_version", "Intel(R) IPP version", cv::ipp::useIPP() ? cv::ipp::getIppVersion() :  "disabled");
1067 #endif
1068 #ifdef HAVE_OPENCL
1069     cv::dumpOpenCLInformation();
1070 #endif
1071 }
1072
1073 } //namespace cvtest
1074
1075 /* End of file. */