1 /*M///////////////////////////////////////////////////////////////////////////////////////
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
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.
10 // Intel License Agreement
11 // For Open Source Computer Vision Library
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
19 // * Redistribution's of source code must retain the above copyright notice,
20 // this list of conditions and the following disclaimer.
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.
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.
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.
53 CvTest* CvTest::first = 0;
54 CvTest* CvTest::last = 0;
55 int CvTest::test_count = 0;
57 /*****************************************************************************************\
58 * Exception and memory handlers *
59 \*****************************************************************************************/
61 // a few platform-dependent declarations
63 #define CV_TS_NORMAL 0
75 static void cv_seh_translator( unsigned int /*u*/, EXCEPTION_POINTERS* pExp )
77 int code = CvTS::FAIL_EXCEPTION;
78 switch( pExp->ExceptionRecord->ExceptionCode )
80 case EXCEPTION_ACCESS_VIOLATION:
81 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
82 case EXCEPTION_DATATYPE_MISALIGNMENT:
83 case EXCEPTION_FLT_STACK_CHECK:
84 case EXCEPTION_STACK_OVERFLOW:
85 case EXCEPTION_IN_PAGE_ERROR:
86 code = CvTS::FAIL_MEMORY_EXCEPTION;
88 case EXCEPTION_FLT_DENORMAL_OPERAND:
89 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
90 case EXCEPTION_FLT_INEXACT_RESULT:
91 case EXCEPTION_FLT_INVALID_OPERATION:
92 case EXCEPTION_FLT_OVERFLOW:
93 case EXCEPTION_FLT_UNDERFLOW:
94 case EXCEPTION_INT_DIVIDE_BY_ZERO:
95 case EXCEPTION_INT_OVERFLOW:
96 code = CvTS::FAIL_ARITHM_EXCEPTION;
98 case EXCEPTION_BREAKPOINT:
99 case EXCEPTION_ILLEGAL_INSTRUCTION:
100 case EXCEPTION_INVALID_DISPOSITION:
101 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
102 case EXCEPTION_PRIV_INSTRUCTION:
103 case EXCEPTION_SINGLE_STEP:
104 code = CvTS::FAIL_EXCEPTION;
110 #define CV_TS_TRY_BLOCK_BEGIN \
113 #define CV_TS_TRY_BLOCK_END \
114 } catch( int _code ) { \
115 ts->set_failed_test_info( _code ); \
118 static void change_color( int color )
120 static int normal_attributes = -1;
121 HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
124 if( normal_attributes < 0 )
126 CONSOLE_SCREEN_BUFFER_INFO info;
127 GetConsoleScreenBufferInfo( hstdout, &info );
128 normal_attributes = info.wAttributes;
131 SetConsoleTextAttribute( hstdout,
132 (WORD)(color == CV_TS_NORMAL ? normal_attributes :
133 ((color & CV_TS_BLUE ? FOREGROUND_BLUE : 0)|
134 (color & CV_TS_GREEN ? FOREGROUND_GREEN : 0)|
135 (color & CV_TS_RED ? FOREGROUND_RED : 0)|FOREGROUND_INTENSITY)) );
142 static const int cv_ts_sig_id[] = { SIGSEGV, SIGBUS, SIGFPE, SIGILL, -1 };
144 static jmp_buf cv_ts_jmp_mark;
146 void cv_signal_handler( int sig_code )
148 int code = CvTS::FAIL_EXCEPTION;
152 code = CvTS::FAIL_ARITHM_EXCEPTION;
156 code = CvTS::FAIL_ARITHM_EXCEPTION;
159 code = CvTS::FAIL_EXCEPTION;
162 longjmp( cv_ts_jmp_mark, code );
165 #define CV_TS_TRY_BLOCK_BEGIN \
167 int _code = setjmp( cv_ts_jmp_mark ); \
170 #define CV_TS_TRY_BLOCK_END \
173 ts->set_failed_test_info( _code ); \
177 static void change_color( int color )
179 static const uchar ansi_tab[] = { 30, 34, 32, 36, 31, 35, 33, 37 };
183 if( color != CV_TS_NORMAL )
184 code = ansi_tab[color & (CV_TS_BLUE|CV_TS_GREEN|CV_TS_RED)];
185 sprintf( buf, "\x1b[%dm", code );
192 /***************************** memory manager *****************************/
194 typedef struct CvTestAllocBlock
196 struct CvTestAllocBlock* prev;
197 struct CvTestAllocBlock* next;
206 class CvTestMemoryManager
209 CvTestMemoryManager( CvTS* ts );
210 virtual ~CvTestMemoryManager();
212 virtual void clear_and_check( int min_index = -1 );
213 virtual void start_tracking( int index_to_stop_at=-1 );
214 virtual void stop_tracking_and_check();
215 int get_alloc_index() { return index; }
217 static void* alloc_proxy( size_t size, void* userdata );
218 static int free_proxy( void* ptr, void* userdata );
221 virtual void* alloc( size_t size );
222 virtual int free( void* ptr );
223 virtual int free_block( CvTestAllocBlock* block );
228 int index_to_stop_at;
229 const char* guard_pattern;
232 enum { MAX_MARKS = 1024 };
233 int marks[MAX_MARKS];
236 CvTestAllocBlock* first;
237 CvTestAllocBlock* last;
241 void* CvTestMemoryManager::alloc_proxy( size_t size, void* userdata )
243 return ((CvTestMemoryManager*)userdata)->alloc( size );
247 int CvTestMemoryManager::free_proxy( void* ptr, void* userdata )
249 return ((CvTestMemoryManager*)userdata)->free( ptr );
253 CvTestMemoryManager::CvTestMemoryManager( CvTS* _test_system )
256 guard_pattern = "THIS IS A GUARD PATTERN!";
257 guard_size = strlen(guard_pattern);
258 block_align = CV_MALLOC_ALIGN;
263 index_to_stop_at = -1;
268 CvTestMemoryManager::~CvTestMemoryManager()
274 void CvTestMemoryManager::clear_and_check( int min_index )
276 int alloc_index = -1;
277 CvTestAllocBlock* block;
278 int leak_size = 0, leak_block_count = 0, mem_size = 0;
281 while( marks_top > 0 && marks[marks_top - 1] >= min_index )
284 for( block = last; block != 0; )
286 CvTestAllocBlock* prev = block->prev;
287 if( block->index < min_index )
289 leak_size += block->size;
291 alloc_index = block->index;
292 mem_addr = block->data;
293 mem_size = block->size;
298 if( leak_block_count > 0 )
300 ts->set_failed_test_info( CvTS::FAIL_MEMORY_LEAK, alloc_index );
301 ts->printf( CvTS::LOG, "Memory leaks: %u blocks, %u bytes total\n"
302 "%s leaked block: %p, %u bytes\n",
303 leak_block_count, leak_size, leak_block_count > 1 ? "The first" : "The",
304 mem_addr, mem_size );
307 index = block ? block->index + 1 : 0;
311 void CvTestMemoryManager::start_tracking( int _index_to_stop_at )
314 marks[marks_top++] = index;
315 assert( marks_top <= MAX_MARKS );
317 index_to_stop_at = _index_to_stop_at >= index ? _index_to_stop_at : -1;
321 void CvTestMemoryManager::stop_tracking_and_check()
325 int min_index = marks[--marks_top];
326 clear_and_check( min_index );
331 int CvTestMemoryManager::free_block( CvTestAllocBlock* block )
334 char* data = block->data;
336 if( block->origin == 0 || ((size_t)block->origin & (sizeof(double)-1)) != 0 )
337 code = CvTS::FAIL_MEMORY_CORRUPTION_BEGIN;
339 if( memcmp( data - guard_size, guard_pattern, guard_size ) != 0 )
340 code = CvTS::FAIL_MEMORY_CORRUPTION_BEGIN;
341 else if( memcmp( data + block->size, guard_pattern, guard_size ) != 0 )
342 code = CvTS::FAIL_MEMORY_CORRUPTION_END;
347 block->prev->next = block->next;
348 else if( first == block )
352 block->next->prev = block->prev;
353 else if( last == block )
356 free( block->origin );
360 ts->set_failed_test_info( code, block->index );
361 ts->printf( CvTS::LOG, "Corrupted block (%s): %p, %u bytes\n",
362 code == CvTS::FAIL_MEMORY_CORRUPTION_BEGIN ? "beginning" : "end",
363 block->data, block->size );
370 void* CvTestMemoryManager::alloc( size_t size )
373 CvTestAllocBlock* block;
374 size_t new_size = sizeof(*block) + size + guard_size*2 + block_align + sizeof(size_t)*2;
375 char* ptr = (char*)malloc( new_size );
380 data = (char*)cvAlignPtr( ptr + sizeof(size_t) + sizeof(*block) + guard_size, block_align );
381 block = (CvTestAllocBlock*)cvAlignPtr( data - guard_size -
382 sizeof(size_t) - sizeof(*block), sizeof(size_t) );
387 block->next = block->prev = 0;
388 memcpy( data - guard_size, guard_pattern, guard_size );
389 memcpy( data + size, guard_pattern, guard_size );
391 if( track_blocks > 0 )
396 if( index == index_to_stop_at )
401 MessageBox( NULL, "The block that is corrupted and/or not deallocated has been just allocated\n"
402 "Press Ok to start debugging", "Memory Manager", MB_ICONERROR|MB_OK|MB_SYSTEMMODAL );
408 block->index = index++;
413 last = last->next = block;
415 first = last = block;
424 int CvTestMemoryManager::free( void* ptr )
426 char* data = (char*)ptr;
427 CvTestAllocBlock* block = (CvTestAllocBlock*)
428 cvAlignPtr( data - guard_size - sizeof(size_t) - sizeof(*block), sizeof(size_t) );
430 int code = free_block( block );
431 if( code < 0 && ts->is_debug_mode() )
437 /***************************** error handler *****************************/
440 static int cvTestErrorCallback( int status, const char* func_name, const char* err_msg,
441 const char* file_name, int line, void* userdata )
443 if( status < 0 && status != CV_StsBackTrace && status != CV_StsAutoTrace )
444 ((CvTS*)userdata)->set_failed_test_info( CvTS::FAIL_ERROR_IN_CALLED_FUNC );
446 // print error message
447 return cvStdErrReport( status, func_name, err_msg, file_name, line, 0 );
451 /*****************************************************************************************\
452 * Base Class for Tests *
453 \*****************************************************************************************/
455 CvTest::CvTest( const char* _test_name, const char* _test_funcs, const char* _test_descr ) :
456 name(_test_name ? _test_name : ""), tested_functions(_test_funcs ? _test_funcs : ""),
457 description(_test_descr ? _test_descr : ""), ts(0)
468 timing_param_names = 0;
469 timing_param_current = 0;
470 timing_param_seqs = 0;
471 timing_param_idxs = 0;
472 timing_param_count = 0;
474 test_case_count = -1;
485 if( timing_param_current )
486 free( timing_param_current );
487 if( timing_param_seqs )
488 free( timing_param_seqs );
489 if( timing_param_idxs )
490 free( timing_param_idxs );
492 timing_param_current = 0;
493 timing_param_seqs = 0;
494 timing_param_idxs = 0;
495 timing_param_count = 0;
499 int CvTest::init( CvTS* _test_system )
503 return read_params( ts->get_file_storage() );
507 const char* CvTest::get_parent_name( const char* name, char* buffer )
509 const char* dash_pos = strrchr( name ? name : "", '-' );
513 if( name != (const char*)buffer )
514 strncpy( buffer, name, dash_pos - name );
515 buffer[dash_pos - name] = '\0';
520 const CvFileNode* CvTest::find_param( CvFileStorage* fs, const char* param_name )
523 const char* name = get_name();
524 CvFileNode* node = 0;
530 node = cvGetFileNodeByName( fs, 0, name );
533 node = cvGetFileNodeByName( fs, node, param_name );
537 name = get_parent_name( name, buffer );
544 void CvTest::start_write_param( CvFileStorage* fs )
548 cvStartWriteStruct( fs, get_name(), CV_NODE_MAP );
554 void CvTest::write_param( CvFileStorage* fs, const char* paramname, int val )
556 if( !ts->find_written_param( this, paramname, CV_NODE_INT, &val) )
558 start_write_param( fs );
559 cvWriteInt( fs, paramname, val );
564 void CvTest::write_param( CvFileStorage* fs, const char* paramname, double val )
566 if( !ts->find_written_param( this, paramname, CV_NODE_REAL, &val) )
568 start_write_param( fs );
569 cvWriteReal( fs, paramname, val );
574 void CvTest::write_param( CvFileStorage* fs, const char* paramname, const char* val )
576 if( !ts->find_written_param( this, paramname, CV_NODE_STRING, &val) )
578 start_write_param( fs );
579 cvWriteString( fs, paramname, val );
584 void CvTest::write_string_list( CvFileStorage* fs, const char* paramname, const char** val, int count )
588 start_write_param( fs );
593 cvStartWriteStruct( fs, paramname, CV_NODE_SEQ + CV_NODE_FLOW );
594 for( i = 0; i < count && val[i] != 0; i++ )
595 cvWriteString( fs, 0, val[i] );
596 cvEndWriteStruct( fs );
601 void CvTest::write_int_list( CvFileStorage* fs, const char* paramname,
602 const int* val, int count, int stop_value )
606 start_write_param( fs );
611 cvStartWriteStruct( fs, paramname, CV_NODE_SEQ + CV_NODE_FLOW );
612 for( i = 0; i < count && val[i] != stop_value; i++ )
613 cvWriteInt( fs, 0, val[i] );
614 cvEndWriteStruct( fs );
619 int CvTest::read_params( CvFileStorage* fs )
623 if( ts->get_testing_mode() == CvTS::TIMING_MODE )
625 timing_param_names = find_param( fs, "timing_params" );
626 if( CV_NODE_IS_SEQ(timing_param_names->tag) )
628 CvSeq* seq = timing_param_names->data.seq;
630 cvStartReadSeq( seq, &reader );
633 timing_param_count = seq->total;
634 timing_param_seqs = (const CvFileNode**)malloc( timing_param_count*sizeof(timing_param_seqs[0]));
635 timing_param_idxs = (int*)malloc( timing_param_count*sizeof(timing_param_idxs[0]));
636 timing_param_current = (const CvFileNode**)malloc( timing_param_count*sizeof(timing_param_current[0]));
639 for( i = 0; i < timing_param_count; i++ )
641 CvFileNode* param_name = (CvFileNode*)(reader.ptr);
643 if( !CV_NODE_IS_STRING(param_name->tag) )
645 ts->printf( CvTS::LOG, "ERROR: name of timing parameter #%d is not a string\n", i );
650 timing_param_idxs[i] = 0;
651 timing_param_current[i] = 0;
652 timing_param_seqs[i] = find_param( fs, param_name->data.str.ptr );
653 if( !timing_param_seqs[i] )
655 ts->printf( CvTS::LOG, "ERROR: timing parameter %s is not found\n", param_name->data.str.ptr );
660 if( CV_NODE_IS_SEQ(timing_param_seqs[i]->tag) )
661 test_case_count *= timing_param_seqs[i]->data.seq->total;
663 CV_NEXT_SEQ_ELEM( seq->elem_size, reader );
666 if( i < timing_param_count )
667 timing_param_count = 0;
671 ts->printf( CvTS::LOG, "ERROR: \"timing_params\" is not found" );
680 int CvTest::get_next_timing_param_tuple()
685 if( timing_param_count <= 0 || !timing_param_names || !timing_param_seqs )
688 increment = timing_param_current[0] != 0; // if already have some valid test tuple, move to the next
689 for( i = 0; i < timing_param_count; i++ )
691 const CvFileNode* node = timing_param_seqs[i];
692 int total = CV_NODE_IS_SEQ(node->tag) ? node->data.seq->total : 1;
693 int new_idx = timing_param_idxs[i];
695 if( !timing_param_current[i] )
696 timing_param_idxs[i] = new_idx = 0;
700 if( new_idx >= total )
706 if( !timing_param_current[i] || new_idx != timing_param_idxs[i] )
708 if( CV_NODE_IS_SEQ(node->tag) )
709 timing_param_current[i] = (CvFileNode*)cvGetSeqElem( node->data.seq, new_idx );
711 timing_param_current[i] = node;
712 timing_param_idxs[i] = new_idx;
716 return !increment; // return 0 in case of overflow (i.e. if there is no more test cases)
720 const CvFileNode* CvTest::find_timing_param( const char* paramname )
722 if( timing_param_names )
726 cvStartReadSeq( timing_param_names->data.seq, &reader, 0 );
728 for( i = 0; i < timing_param_count; i++ )
730 const char* ptr = ((const CvFileNode*)(reader.ptr))->data.str.ptr;
731 if( ptr[0] == paramname[0] && strcmp(ptr, paramname) == 0 )
732 return timing_param_current[i];
733 CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
740 int CvTest::write_defaults(CvTS* _ts)
744 write_default_params( ts->get_file_storage() );
746 cvEndWriteStruct( ts->get_file_storage() );
751 int CvTest::write_default_params( CvFileStorage* fs )
753 if( ts->get_testing_mode() == CvTS::TIMING_MODE )
754 write_string_list( fs, "timing_params", default_timing_param_names, timing_param_count );
759 bool CvTest::can_do_fast_forward()
765 int CvTest::support_testing_modes()
767 return CvTS::CORRECTNESS_CHECK_MODE;
770 void CvTest::safe_run( int start_from )
772 CV_TS_TRY_BLOCK_BEGIN;
780 void CvTest::run( int start_from )
782 int i, test_case_idx, count = get_test_case_count();
783 int64 t_start = cvGetTickCount();
784 double freq = cvGetTickFrequency();
785 bool ff = can_do_fast_forward();
786 int progress = 0, code;
788 for( test_case_idx = ff && start_from >= 0 ? start_from : 0;
789 count < 0 || test_case_idx < count; test_case_idx++ )
791 ts->update_context( this, test_case_idx, ff );
792 int64 t00 = 0, t0, t1 = 0;
795 if( ts->get_testing_mode() == CvTS::TIMING_MODE )
797 const int iterations = 10;
798 code = prepare_test_case( test_case_idx );
800 if( code < 0 || ts->get_err_code() < 0 )
806 for( i = 0; i < iterations; i++ )
808 t0 = cvGetTickCount();
810 t1 = cvGetTickCount();
811 if( ts->get_err_code() < 0 )
816 t_acc = (double)(t1 - t0);
823 if( ts->get_timing_mode() == CvTS::MIN_TIME )
825 if( (double)t0 < t_acc )
830 assert( ts->get_timing_mode() == CvTS::AVG_TIME );
834 if( t1 - t00 > freq*2000000 )
839 if( ts->get_timing_mode() == CvTS::AVG_TIME )
841 print_time( test_case_idx, t_acc );
845 code = prepare_test_case( test_case_idx );
846 if( code < 0 || ts->get_err_code() < 0 )
853 if( ts->get_err_code() < 0 )
856 if( validate_test_results( test_case_idx ) < 0 || ts->get_err_code() < 0 )
860 progress = update_progress( progress, test_case_idx, count, (double)(t1 - t_start)/(freq*1000) );
865 void CvTest::run_func()
871 int CvTest::get_test_case_count()
873 return test_case_count;
877 int CvTest::prepare_test_case( int )
883 int CvTest::validate_test_results( int )
889 void CvTest::print_time( int /*test_case_idx*/, double /*time_usecs*/ )
894 int CvTest::update_progress( int progress, int test_case_idx, int count, double dt )
896 int width = 60 - strlen(get_name());
899 int t = cvRound( ((double)test_case_idx * width)/count );
902 ts->printf( CvTS::CONSOLE, "." );
906 else if( cvRound(dt*0.001) > progress )
908 ts->printf( CvTS::CONSOLE, "." );
909 progress = cvRound(dt*0.001);
915 /*****************************************************************************************\
916 * Base Class for Test System *
917 \*****************************************************************************************/
919 /******************************** Constructors/Destructors ******************************/
924 version = CV_TS_VERSION;
927 memory_manager = new CvTestMemoryManager(this);
928 cvSetMemoryManager( CvTestMemoryManager::alloc_proxy,
929 CvTestMemoryManager::free_proxy,
931 ostrm_suffixes[SUMMARY_IDX] = ".sum";
932 ostrm_suffixes[LOG_IDX] = ".log";
933 ostrm_suffixes[CSV_IDX] = ".csv";
934 ostrm_suffixes[CONSOLE_IDX] = 0;
936 memset( output_streams, 0, sizeof(output_streams) );
937 memset( ¶ms, 0, sizeof(params) );
938 selected_tests = new CvTestPtrVec();
939 failed_tests = new CvTestInfoVec();
940 written_params = new CvTestPtrVec();
951 for( test = get_first_test(); test != 0; test = test->get_next() )
954 for( i = 0; i <= CONSOLE_IDX; i++ )
958 else if( i == CONSOLE_IDX )
961 if( i < CONSOLE_IDX && output_streams[i].f )
963 fclose( output_streams[i].f );
964 output_streams[i].f = 0;
967 if( i == LOG_IDX && output_streams[i].default_handle > 0 )
969 dup2( output_streams[i].default_handle, 2 );
970 output_streams[i].default_handle = 0;
972 output_streams[i].enable = 1;
974 cvReleaseFileStorage( &fs );
975 selected_tests->clear();
976 failed_tests->clear();
977 if( ostrm_base_name )
979 free( ostrm_base_name );
982 params.rng_seed = (uint64)-1;
983 params.debug_mode = 1;
984 params.print_only_failed = 0;
985 params.skip_header = 0;
986 params.test_mode = CORRECTNESS_CHECK_MODE;
987 params.timing_mode = MIN_TIME;
988 params.use_optimized = -1;
991 memory_manager->clear_and_check();
1001 for( int i = 0; i < written_params->size(); i++ )
1002 free( written_params->at(i) );
1003 delete written_params;
1006 delete selected_tests;
1007 delete failed_tests;
1008 cvSetMemoryManager( 0, 0 );
1012 const char* CvTS::str_from_code( int code )
1016 case OK: return "Ok";
1017 case FAIL_GENERIC: return "Generic/Unknown";
1018 case FAIL_MISSING_TEST_DATA: return "No test data";
1019 case FAIL_ERROR_IN_CALLED_FUNC: return "cvError invoked";
1020 case FAIL_EXCEPTION: return "Hardware/OS exception";
1021 case FAIL_MEMORY_EXCEPTION: return "Invalid memory access";
1022 case FAIL_ARITHM_EXCEPTION: return "Arithmetic exception";
1023 case FAIL_MEMORY_CORRUPTION_BEGIN: return "Corrupted memblock (beginning)";
1024 case FAIL_MEMORY_CORRUPTION_END: return "Corrupted memblock (end)";
1025 case FAIL_MEMORY_LEAK: return "Memory leak";
1026 case FAIL_INVALID_OUTPUT: return "Invalid function output";
1027 case FAIL_MISMATCH: return "Unexpected output";
1028 case FAIL_BAD_ACCURACY: return "Bad accuracy";
1029 case FAIL_HANG: return "Infinite loop(?)";
1030 case FAIL_BAD_ARG_CHECK: return "Incorrect handling of bad arguments";
1031 default: return "Generic/Unknown";
1035 /************************************** Running tests **********************************/
1037 void CvTS::make_output_stream_base_name( const char* config_name )
1039 int k, len = strlen( config_name );
1041 if( ostrm_base_name )
1042 free( ostrm_base_name );
1044 for( k = len-1; k >= 0; k-- )
1046 char c = config_name[k];
1047 if( c == '.' || c == '/' || c == '\\' || c == ':' )
1051 if( k > 0 && config_name[k] == '.' )
1054 ostrm_base_name = (char*)malloc( len + 1 );
1055 memcpy( ostrm_base_name, config_name, len );
1056 ostrm_base_name[len] = '\0';
1060 void CvTS::set_handlers( bool on )
1064 cvSetErrMode( CV_ErrModeParent );
1065 cvRedirectError( cvStdErrReport );
1068 _set_se_translator( cv_seh_translator );
1071 for( int i = 0; cv_ts_sig_id[i] >= 0; i++ )
1072 signal( cv_ts_sig_id[i], cv_signal_handler );
1077 cvSetErrMode( CV_ErrModeLeaf );
1078 cvRedirectError( cvGuiBoxReport );
1081 _set_se_translator( 0 );
1084 for( int i = 0; cv_ts_sig_id[i] >= 0; i++ )
1085 signal( cv_ts_sig_id[i], SIG_DFL );
1091 typedef struct CvTsParamVal
1093 const char* fullname;
1098 int CvTS::find_written_param( CvTest* test, const char* paramname, int valtype, const void* val )
1100 const char* testname = test->get_name();
1101 bool add_to_list = test->get_func_list()[0] == '\0';
1103 int paramname_len = strlen(paramname);
1104 int paramval_len = valtype == CV_NODE_INT ? sizeof(int) :
1105 valtype == CV_NODE_REAL ? sizeof(double) : -1;
1106 const char* name = CvTest::get_parent_name( testname, buffer );
1111 if( paramval_len < 0 )
1113 assert(0); // unsupported parameter type
1119 int i, len = strlen(buffer);
1121 memcpy( buffer + len + 1, paramname, paramname_len + 1 );
1122 for( i = 0; i < written_params->size(); i++ )
1124 CvTsParamVal* param = (CvTsParamVal*)written_params->at(i);
1125 if( strcmp( param->fullname, buffer ) == 0 )
1127 if( paramval_len > 0 && memcmp( param->val, val, paramval_len ) == 0 ||
1128 paramval_len < 0 && strcmp( (const char*)param->val, (const char*)val ) == 0 )
1133 if( i < written_params->size() )
1136 name = CvTest::get_parent_name( buffer, buffer );
1141 int bufsize, fullname_len = strlen(testname) + paramname_len + 2;
1142 CvTsParamVal* param;
1143 if( paramval_len < 0 )
1144 paramval_len = strlen((const char*)val) + 1;
1145 bufsize = sizeof(*param) + fullname_len + paramval_len;
1146 param = (CvTsParamVal*)malloc(bufsize);
1147 param->fullname = (const char*)(param + 1);
1148 param->val = param->fullname + fullname_len;
1149 sprintf( (char*)param->fullname, "%s.%s", testname, paramname );
1150 memcpy( (void*)param->val, val, paramval_len );
1151 written_params->push( param );
1159 #define MAX_PATH 1024
1162 static int CV_CDECL cmp_test_names( const void* a, const void* b )
1164 return strcmp( (*(const CvTest**)a)->get_name(), (*(const CvTest**)b)->get_name() );
1167 int CvTS::run( int argc, char** argv )
1169 time( &start_time );
1171 int i, write_params = 0;
1172 CvTestPtrVec all_tests;
1175 // 0. reset all the parameters, reorder tests
1178 for( test = get_first_test(), i = 0; test != 0; test = test->get_next(), i++ )
1179 all_tests.push(test);
1181 if( all_tests.size() > 0 && all_tests.data() )
1182 qsort( all_tests.data(), all_tests.size(), sizeof(CvTest*), cmp_test_names );
1184 // 1. parse command line options
1185 for( i = 1; i < argc; i++ )
1187 if( argv[i] && argv[i][0] != '-' )
1189 config_name = argv[i];
1194 if( strcmp( argv[i], "-w" ) == 0 )
1196 else if( strcmp( argv[i], "-t" ) == 0 )
1197 params.test_mode = TIMING_MODE;
1205 printf( LOG, "ERROR: output config name is not specified\n" );
1208 fs = cvOpenFileStorage( config_name, 0, CV_STORAGE_WRITE );
1211 printf( LOG, "ERROR: could not open config file %s", config_name );
1214 cvWriteComment( fs, CV_TS_VERSION " config file", 0 );
1215 cvStartWriteStruct( fs, "common", CV_NODE_MAP );
1216 write_default_params( fs );
1217 cvEndWriteStruct( fs );
1219 for( i = 0; i < all_tests.size(); i++ )
1221 test = (CvTest*)all_tests[i];
1222 if( !(test->support_testing_modes() & get_testing_mode()) )
1224 test->write_defaults( this );
1227 cvReleaseFileStorage( &fs );
1232 printf( LOG, "WARNING: config name is not specified, using default parameters\n" );
1235 // 2. read common parameters of test system
1236 fs = cvOpenFileStorage( config_name, 0, CV_STORAGE_READ );
1239 printf( LOG, "ERROR: could not open config file %s", config_name );
1244 if( read_params(fs) < 0 )
1247 if( !ostrm_base_name )
1248 make_output_stream_base_name( config_name ? config_name : argv[0] );
1250 ostream_testname_mask = -1; // disable printing test names at initial stage
1252 // 3. open file streams
1253 for( i = 0; i < CONSOLE_IDX; i++ )
1255 char filename[MAX_PATH];
1256 sprintf( filename, "%s%s", ostrm_base_name, ostrm_suffixes[i] );
1257 output_streams[i].f = fopen( filename, "wt" );
1258 if( !output_streams[i].f )
1260 printf( LOG, "ERROR: could not open %s\n", filename );
1266 // redirect stderr to log file
1268 output_streams[i].default_handle = dup(2);
1269 dup2( fileno(output_streams[i].f), 2 );
1273 // 4. traverse through the list of all registered tests.
1274 // Initialize the selected tests and put them into the separate sequence
1275 for( i = 0; i < all_tests.size(); i++ )
1277 test = (CvTest*)all_tests[i];
1278 if( !(test->support_testing_modes() & get_testing_mode()) )
1281 if( strcmp( test->get_func_list(), "" ) != 0 && filter(test) )
1283 if( test->init(this) >= 0 )
1284 selected_tests->push( test );
1286 printf( LOG, "WARNING: an error occured during test %s initialization\n", test->get_name() );
1290 // 5. setup all the neccessary handlers and print header
1291 set_handlers( !params.debug_mode );
1293 if( params.use_optimized >= 0 )
1295 printf( LOG, params.use_optimized ? "Loading optimized plugins..." : "Unloading optimized plugins..." );
1296 if( params.use_optimized == 0 )
1299 cvUseOptimized(1); // this is done anyway, so we comment it off
1303 if( !params.skip_header )
1304 print_summary_header( SUMMARY + LOG + CONSOLE + CSV );
1305 rng = params.rng_seed;
1306 update_context( 0, -1, true );
1308 // 6. run all the tests
1309 for( i = 0; i < selected_tests->size(); i++ )
1311 CvTest* test = (CvTest*)selected_tests->at(i);
1315 if( memory_manager )
1316 memory_manager->start_tracking();
1317 update_context( test, -1, true );
1318 ostream_testname_mask = 0; // reset "test name was printed" flags
1319 if( output_streams[LOG_IDX].f )
1320 fflush( output_streams[LOG_IDX].f );
1322 temp = current_test_info;
1324 if( get_err_code() >= 0 )
1326 update_context( test, -1, false );
1327 current_test_info.rng_seed = temp.rng_seed;
1328 current_test_info.base_alloc_index = temp.base_alloc_index;
1331 if( memory_manager )
1332 memory_manager->stop_tracking_and_check();
1334 code = get_err_code();
1337 if( !params.print_only_failed )
1339 printf( SUMMARY + CONSOLE, "\t" );
1340 change_color( CV_TS_GREEN );
1341 printf( SUMMARY + CONSOLE, "Ok\n" );
1342 change_color( CV_TS_NORMAL );
1347 printf( SUMMARY + CONSOLE, "\t" );
1348 change_color( CV_TS_RED );
1349 printf( SUMMARY + CONSOLE, "FAIL(%s)\n", str_from_code(code) );
1350 change_color( CV_TS_NORMAL );
1351 printf( LOG, "context: test case = %d, seed = %08x%08x\n",
1352 current_test_info.test_case_idx,
1353 (unsigned)(current_test_info.rng_seed>>32),
1354 (unsigned)(current_test_info.rng_seed));
1355 failed_tests->push(current_test_info);
1356 if( params.rerun_immediately )
1361 ostream_testname_mask = -1;
1362 print_summary_tailer( SUMMARY + CONSOLE + LOG );
1364 if( !params.debug_mode && (params.rerun_failed || params.rerun_immediately) )
1367 update_context( 0, -1, true );
1368 for( i = 0; i < failed_tests->size(); i++ )
1370 CvTestInfo info = failed_tests->at(i);
1371 if( (info.code == FAIL_MEMORY_CORRUPTION_BEGIN ||
1372 info.code == FAIL_MEMORY_CORRUPTION_END ||
1373 info.code == FAIL_MEMORY_LEAK) && memory_manager )
1374 memory_manager->start_tracking( info.alloc_index - info.base_alloc_index
1375 + memory_manager->get_alloc_index() );
1376 rng = info.rng_seed;
1377 test->safe_run( info.test_case_idx );
1387 int CvTS::read_params( CvFileStorage* fs )
1389 CvFileNode* node = fs ? cvGetFileNodeByName( fs, 0, "common" ) : 0;
1390 params.debug_mode = cvReadIntByName( fs, node, "debug_mode", 1 ) != 0;
1391 params.skip_header = cvReadIntByName( fs, node, "skip_header", 0 ) != 0;
1392 params.print_only_failed = cvReadIntByName( fs, node, "print_only_failed", 0 ) != 0;
1393 params.rerun_failed = cvReadIntByName( fs, node, "rerun_failed", 0 ) != 0;
1394 params.rerun_immediately = cvReadIntByName( fs, node, "rerun_immediately", 0 ) != 0;
1395 const char* str = cvReadStringByName( fs, node, "filter_mode", "tests" );
1396 params.test_filter_mode = strcmp( str, "functions" ) == 0 ? CHOOSE_FUNCTIONS : CHOOSE_TESTS;
1397 str = cvReadStringByName( fs, node, "test_mode", params.test_mode == TIMING_MODE ? "timing" : "correctness" );
1398 params.test_mode = strcmp( str, "timing" ) == 0 || strcmp( str, "performance" ) == 0 ?
1399 TIMING_MODE : CORRECTNESS_CHECK_MODE;
1400 str = cvReadStringByName( fs, node, "timing_mode", params.timing_mode == AVG_TIME ? "avg" : "min" );
1401 params.timing_mode = strcmp( str, "average" ) == 0 || strcmp( str, "avg" ) == 0 ? AVG_TIME : MIN_TIME;
1402 params.test_filter_pattern = cvReadStringByName( fs, node, params.test_filter_mode == CHOOSE_FUNCTIONS ?
1403 "functions" : "tests", "" );
1404 params.resource_path = cvReadStringByName( fs, node, "." );
1405 params.use_optimized = cvReadIntByName( fs, node, "use_optimized", -1 );
1406 str = cvReadStringByName( fs, node, "seed", 0 );
1407 params.rng_seed = 0;
1408 if( str && strlen(str) == 16 )
1410 params.rng_seed = 0;
1411 for( int i = 0; i < 16; i++ )
1413 int c = tolower(str[i]);
1416 params.rng_seed = 0;
1419 params.rng_seed = params.rng_seed * 16 +
1420 (str[i] < 'a' ? str[i] - '0' : str[i] - 'a' + 10);
1424 if( params.rng_seed == 0 )
1425 params.rng_seed = cvGetTickCount();
1427 str = cvReadStringByName( fs, node, "output_file_base_name", 0 );
1429 make_output_stream_base_name( str );
1435 void CvTS::write_default_params( CvFileStorage* fs )
1437 read_params(0); // fill parameters with default values
1439 cvWriteInt( fs, "debug_mode", params.debug_mode );
1440 cvWriteInt( fs, "skip_header", params.skip_header );
1441 cvWriteInt( fs, "print_only_failed", params.print_only_failed );
1442 cvWriteInt( fs, "rerun_failed", params.rerun_failed );
1443 cvWriteInt( fs, "rerun_immediately", params.rerun_immediately );
1444 cvWriteString( fs, "filter_mode", params.test_filter_mode == CHOOSE_FUNCTIONS ? "functions" : "tests" );
1445 cvWriteString( fs, "test_mode", params.test_mode == TIMING_MODE ? "timing" : "correctness" );
1446 if( params.test_mode == TIMING_MODE )
1447 cvWriteString( fs, "timing_mode", params.timing_mode == AVG_TIME ? "avg" : "min" );
1448 // test_filter, seed & output_file_base_name are not written
1452 void CvTS::enable_output_streams( int stream_mask, int value )
1454 for( int i = 0; i < MAX_IDX; i++ )
1455 if( stream_mask & (1 << i) )
1456 output_streams[i].enable = value != 0;
1460 void CvTS::update_context( CvTest* test, int test_case_idx, bool update_ts_context )
1462 current_test_info.test = test;
1463 current_test_info.test_case_idx = test_case_idx;
1464 current_test_info.alloc_index = 0;
1465 current_test_info.code = 0;
1466 cvSetErrStatus( CV_StsOk );
1467 if( update_ts_context )
1469 current_test_info.rng_seed = rng;
1470 current_test_info.base_alloc_index = memory_manager ?
1471 memory_manager->get_alloc_index() : 0;
1476 void CvTS::set_failed_test_info( int fail_code, int alloc_index )
1478 if( fail_code == FAIL_MEMORY_CORRUPTION_BEGIN ||
1479 fail_code == FAIL_MEMORY_CORRUPTION_END ||
1480 current_test_info.code >= 0 )
1482 current_test_info.code = fail_code;
1483 current_test_info.alloc_index = alloc_index;
1488 const char* CvTS::get_libs_info( const char** addon_modules )
1490 const char* all_info = 0;
1491 cvGetModuleInfo( 0, &all_info, addon_modules );
1496 void CvTS::print_summary_header( int streams )
1498 char csv_header[256], *ptr = csv_header;
1501 printf( streams, "Engine: %s\n", version );
1504 struct tm *t2 = localtime( &t1 );
1506 strftime( buf, sizeof(buf)-1, "%c", t2 );
1507 printf( streams, "Execution Date & Time: %s\n", buf );
1508 printf( streams, "Config File: %s\n", config_name );
1509 const char* plugins = 0;
1510 const char* lib_verinfo = get_libs_info( &plugins );
1511 printf( streams, "Tested Libraries: %s\n", lib_verinfo );
1512 printf( streams, "Optimized Low-level Plugin\'s: %s\n", plugins );
1513 printf( streams, "=================================================\n");
1516 sprintf( ptr, "funcName,dataType,channels,size,%n", &len );
1519 for( i = 0; i < CvTest::TIMING_EXTRA_PARAMS; i++ )
1522 sprintf( ptr, "param%d,%n", i, &len );
1526 sprintf( ptr, "CPE,Time(uSecs)" );
1527 printf( CSV, "%s\n", csv_header );
1531 void CvTS::print_summary_tailer( int streams )
1533 printf( streams, "=================================================\n");
1534 if( selected_tests && failed_tests )
1538 double total_time = difftime( end_time, start_time );
1539 printf( streams, "Summary: %d out of %d tests failed\n",
1540 failed_tests->size(), selected_tests->size() );
1541 int minutes = cvFloor(total_time/60.);
1542 int seconds = cvRound(total_time - minutes*60);
1543 int hours = minutes / 60;
1545 printf( streams, "Running time: %02d:%02d:%02d\n", hours, minutes, seconds );
1550 void CvTS::vprintf( int streams, const char* fmt, va_list l )
1555 vsprintf( str, fmt, l );
1557 for( int i = 0; i < MAX_IDX; i++ )
1559 if( (streams & (1 << i)) && output_streams[i].enable )
1561 FILE* f = i == CONSOLE_IDX ? stdout :
1562 i == LOG_IDX ? stderr : output_streams[i].f;
1565 if( i != CSV_IDX && !(ostream_testname_mask & (1 << i)) && current_test_info.test )
1567 fprintf( f, "-------------------------------------------------\n" );
1568 fprintf( f, "%s: ", current_test_info.test->get_name() );
1570 ostream_testname_mask |= 1 << i;
1573 if( i == CONSOLE_IDX )
1582 void CvTS::printf( int streams, const char* fmt, ... )
1588 vprintf( streams, fmt, l );
1594 static char* cv_strnstr( const char* str, int len,
1595 const char* pattern,
1596 int pattern_len = -1,
1597 int whole_word = 1 )
1601 if( len < 0 && pattern_len < 0 )
1602 return (char*)strstr( str, pattern );
1605 len = strlen( str );
1607 if( pattern_len < 0 )
1608 pattern_len = strlen( pattern );
1610 for( i = 0; i < len - pattern_len + 1; i++ )
1612 int j = i + pattern_len;
1613 if( str[i] == pattern[0] &&
1614 memcmp( str + i, pattern, pattern_len ) == 0 &&
1616 ((i == 0 || !isalnum(str[i-1]) && str[i-1] != '_') &&
1617 (j == len || !isalnum(str[j]) && str[j] != '_'))))
1618 return (char*)(str + i);
1625 int CvTS::filter( CvTest* test )
1627 const char* pattern = params.test_filter_pattern;
1629 if( !pattern || strcmp( pattern, "" ) == 0 || strcmp( pattern, "*" ) == 0 )
1632 if( params.test_filter_mode == CHOOSE_TESTS )
1636 while( pattern && *pattern )
1638 char *ptr, *endptr = (char*)strchr( pattern, ',' );
1639 int len, have_wildcard;
1645 ptr = (char*)strchr( pattern, '*' );
1648 len = ptr - pattern;
1653 len = strlen( pattern );
1657 t_name_len = strlen( test->get_name() );
1658 found = (t_name_len == len || have_wildcard && t_name_len > len) &&
1659 (len == 0 || memcmp( test->get_name(), pattern, len ) == 0);
1663 pattern = endptr + 1;
1664 while( isspace(*pattern) )
1668 if( found || !endptr )
1676 assert( params.test_filter_mode == CHOOSE_FUNCTIONS );
1677 int glob_len = strlen( pattern );
1678 const char* ptr = test->get_func_list();
1679 const char *tmp_ptr;
1681 while( ptr && *ptr )
1683 const char* endptr = ptr - 1;
1684 const char* name_ptr;
1685 const char* name_first_match;
1690 while( isspace(c) );
1695 assert( isalpha(c) );
1699 while( isalnum(c) || c == '_' );
1701 if( c == ':' ) // class
1703 assert( endptr[1] == ':' );
1704 endptr = endptr + 2;
1705 name_len = endptr - name_ptr;
1707 // find the first occurence of the class name
1709 name_first_match = cv_strnstr( pattern,
1710 glob_len, name_ptr, name_len, 1 );
1712 if( *endptr == '*' )
1714 if( name_first_match )
1719 assert( *endptr == '{' ); // a list of methods
1721 if( !name_first_match )
1723 // skip all the methods, if there is no such a class name
1725 endptr = strchr( endptr, '}' );
1726 assert( endptr != 0 );
1732 const char* method_name_ptr;
1733 int method_name_len;
1736 while( isspace(c) );
1740 assert( isalpha(c) );
1742 method_name_ptr = endptr;
1745 while( isalnum(c) || c == '_' );
1747 method_name_len = endptr - method_name_ptr;
1749 // search for class_name::* or
1750 // class_name::{...method_name...}
1751 tmp_ptr = name_first_match;
1754 const char* tmp_ptr2;
1755 tmp_ptr += name_len;
1756 if( *tmp_ptr == '*' )
1758 assert( *tmp_ptr == '{' );
1759 tmp_ptr2 = strchr( tmp_ptr, '}' );
1762 if( cv_strnstr( tmp_ptr, tmp_ptr2 - tmp_ptr + 1,
1763 method_name_ptr, method_name_len, 1 ))
1766 tmp_ptr = cv_strnstr( tmp_ptr2, glob_len -
1767 (tmp_ptr2 - pattern),
1768 name_ptr, name_len, 1 );
1774 while( isspace(c) );
1783 assert( !c || isspace(c) || c == ',' );
1784 name_len = endptr - name_ptr;
1789 const char *tmp_ptr2, *tmp_ptr3;
1791 tmp_ptr = cv_strnstr( tmp_ptr, glob_len -
1792 (tmp_ptr - pattern), name_ptr, name_len, 1 );
1797 // make sure it is not a method
1798 tmp_ptr2 = strchr( tmp_ptr, '}' );
1802 tmp_ptr3 = strchr( tmp_ptr, '{' );
1803 if( tmp_ptr3 < tmp_ptr2 )
1806 tmp_ptr = tmp_ptr2 + 1;
1813 while( isspace(c) );