Update to 2.0.0 tree from current Fremantle build
[opencv] / tests / cxts / cxts.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 "_cxts.h"
43 #include <ctype.h>
44 #include <stdarg.h>
45 #include <fcntl.h>
46 #include <time.h>
47 #if defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64
48 #include <io.h>
49 #else
50 #include <unistd.h>
51 #endif
52
53 CvTest* CvTest::first = 0;
54 CvTest* CvTest::last = 0;
55 int CvTest::test_count = 0;
56
57 /*****************************************************************************************\
58 *                                Exception and memory handlers                            *
59 \*****************************************************************************************/
60
61 // a few platform-dependent declarations
62
63 #define CV_TS_NORMAL 0
64 #define CV_TS_BLUE   1
65 #define CV_TS_GREEN  2
66 #define CV_TS_RED    4
67
68 #if defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64
69 #include <windows.h>
70
71 #ifdef _MSC_VER
72 #include <eh.h>
73 #endif
74
75 #ifdef _MSC_VER
76 static void cv_seh_translator( unsigned int /*u*/, EXCEPTION_POINTERS* pExp )
77 {
78     int code = CvTS::FAIL_EXCEPTION;
79     switch( pExp->ExceptionRecord->ExceptionCode )
80     {
81     case EXCEPTION_ACCESS_VIOLATION:
82     case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
83     case EXCEPTION_DATATYPE_MISALIGNMENT:
84     case EXCEPTION_FLT_STACK_CHECK:
85     case EXCEPTION_STACK_OVERFLOW:
86     case EXCEPTION_IN_PAGE_ERROR:
87         code = CvTS::FAIL_MEMORY_EXCEPTION;
88         break;
89     case EXCEPTION_FLT_DENORMAL_OPERAND:
90     case EXCEPTION_FLT_DIVIDE_BY_ZERO:
91     case EXCEPTION_FLT_INEXACT_RESULT:
92     case EXCEPTION_FLT_INVALID_OPERATION:
93     case EXCEPTION_FLT_OVERFLOW:
94     case EXCEPTION_FLT_UNDERFLOW:
95     case EXCEPTION_INT_DIVIDE_BY_ZERO:
96     case EXCEPTION_INT_OVERFLOW:
97         code = CvTS::FAIL_ARITHM_EXCEPTION;
98         break;
99     case EXCEPTION_BREAKPOINT:
100     case EXCEPTION_ILLEGAL_INSTRUCTION:
101     case EXCEPTION_INVALID_DISPOSITION:
102     case EXCEPTION_NONCONTINUABLE_EXCEPTION:
103     case EXCEPTION_PRIV_INSTRUCTION:
104     case EXCEPTION_SINGLE_STEP:
105         code = CvTS::FAIL_EXCEPTION;
106     }
107     throw code;
108 }
109 #endif
110
111 #define CV_TS_TRY_BLOCK_BEGIN                   \
112     try {
113
114 #define CV_TS_TRY_BLOCK_END                     \
115     } catch( int _code ) {                      \
116         ts->set_failed_test_info( _code );      \
117     }
118
119 static void change_color( int color )
120 {
121     static int normal_attributes = -1;
122     HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
123     fflush(stdout);
124
125     if( normal_attributes < 0 )
126     {
127         CONSOLE_SCREEN_BUFFER_INFO info;
128         GetConsoleScreenBufferInfo( hstdout, &info );
129         normal_attributes = info.wAttributes;
130     }
131
132     SetConsoleTextAttribute( hstdout,
133         (WORD)(color == CV_TS_NORMAL ? normal_attributes :
134         ((color & CV_TS_BLUE ? FOREGROUND_BLUE : 0)|
135         (color & CV_TS_GREEN ? FOREGROUND_GREEN : 0)|
136         (color & CV_TS_RED ? FOREGROUND_RED : 0)|FOREGROUND_INTENSITY)) );
137 }
138
139 #else
140
141 #include <signal.h>
142
143 static const int cv_ts_sig_id[] = { SIGSEGV, SIGBUS, SIGFPE, SIGILL, -1 };
144
145 static jmp_buf cv_ts_jmp_mark;
146
147 void cv_signal_handler( int sig_code )
148 {
149     int code = CvTS::FAIL_EXCEPTION;
150     switch( sig_code )
151     {
152     case SIGFPE:
153         code = CvTS::FAIL_ARITHM_EXCEPTION;
154         break;
155     case SIGSEGV:
156     case SIGBUS:
157         code = CvTS::FAIL_ARITHM_EXCEPTION;
158         break;
159     case SIGILL:
160         code = CvTS::FAIL_EXCEPTION;
161     }
162
163     longjmp( cv_ts_jmp_mark, code );
164 }
165
166 #define CV_TS_TRY_BLOCK_BEGIN                   \
167     {                                           \
168         int _code = setjmp( cv_ts_jmp_mark );   \
169         if( !_code ) {
170
171 #define CV_TS_TRY_BLOCK_END                     \
172         }                                       \
173         else  {                                 \
174             ts->set_failed_test_info( _code );  \
175         }                                       \
176     }
177
178 static void change_color( int color )
179 {
180     static const uchar ansi_tab[] = { 30, 34, 32, 36, 31, 35, 33, 37 };
181     char buf[16];
182     int code = 0;
183     fflush( stdout );
184     if( color != CV_TS_NORMAL )
185         code = ansi_tab[color & (CV_TS_BLUE|CV_TS_GREEN|CV_TS_RED)];
186     sprintf( buf, "\x1b[%dm", code );
187     fputs( buf, stdout );
188 }
189
190 #endif
191
192
193 /***************************** memory manager *****************************/
194
195 typedef struct CvTestAllocBlock
196 {
197     struct CvTestAllocBlock* prev;
198     struct CvTestAllocBlock* next;
199     char* origin;
200     char* data;
201     size_t size;
202     int index;
203 }
204 CvTestAllocBlock;
205
206
207 class CvTestMemoryManager
208 {
209 public:
210     CvTestMemoryManager( CvTS* ts );
211     virtual ~CvTestMemoryManager();
212
213     virtual void clear_and_check( int min_index = -1 );
214     virtual void start_tracking( int index_to_stop_at=-1 );
215     virtual void stop_tracking_and_check();
216     int get_alloc_index() { return index; }
217
218     static void* alloc_proxy( size_t size, void* userdata );
219     static int free_proxy( void* ptr, void* userdata );
220
221 protected:
222     virtual void* alloc( size_t size );
223     virtual int free( void* ptr );
224     virtual int free_block( CvTestAllocBlock* block );
225
226     int index;
227     int track_blocks;
228     int show_msg_box;
229     int index_to_stop_at;
230     const char* guard_pattern;
231     int guard_size;
232     int block_align;
233     enum { MAX_MARKS = 1024 };
234     int marks[MAX_MARKS];
235     int marks_top;
236     CvTS* ts;
237     CvTestAllocBlock* first;
238     CvTestAllocBlock* last;
239 };
240
241
242 void* CvTestMemoryManager::alloc_proxy( size_t size, void* userdata )
243 {
244     return ((CvTestMemoryManager*)userdata)->alloc( size );
245 }
246
247
248 int CvTestMemoryManager::free_proxy( void* ptr, void* userdata )
249 {
250     return ((CvTestMemoryManager*)userdata)->free( ptr );
251 }
252
253
254 CvTestMemoryManager::CvTestMemoryManager( CvTS* _test_system )
255 {
256     ts = _test_system;
257     guard_pattern = "THIS IS A GUARD PATTERN!";
258     guard_size = (int)strlen(guard_pattern);
259     block_align = CV_MALLOC_ALIGN;
260     track_blocks = 0;
261     marks_top = 0;
262     first = last = 0;
263     index = 0;
264     index_to_stop_at = -1;
265     show_msg_box = 1;
266 }
267
268
269 CvTestMemoryManager::~CvTestMemoryManager()
270 {
271     clear_and_check();
272 }
273
274
275 void CvTestMemoryManager::clear_and_check( int min_index )
276 {
277     int alloc_index = -1;
278     CvTestAllocBlock* block;
279     int leak_size = 0, leak_block_count = 0, mem_size = 0;
280     void* mem_addr = 0;
281
282     while( marks_top > 0 && marks[marks_top - 1] >= min_index )
283         marks_top--;
284
285     for( block = last; block != 0; )
286     {
287         CvTestAllocBlock* prev = block->prev;
288         if( block->index < min_index )
289             break;
290         leak_size += (int)block->size;
291         leak_block_count++;
292         alloc_index = block->index;
293         mem_addr = block->data;
294         mem_size = (int)block->size;
295         free_block( block );
296         block = prev;
297     }
298     track_blocks--;
299     if( leak_block_count > 0 )
300     {
301         ts->set_failed_test_info( CvTS::FAIL_MEMORY_LEAK, alloc_index );
302         ts->printf( CvTS::LOG, "Memory leaks: %u blocks, %u bytes total\n"
303                     "%s leaked block: %p, %u bytes\n",
304                     leak_block_count, leak_size, leak_block_count > 1 ? "The first" : "The",
305                     mem_addr, mem_size );
306     }
307
308     index = block ? block->index + 1 : 0;
309 }
310
311
312 void CvTestMemoryManager::start_tracking( int _index_to_stop_at )
313 {
314     track_blocks--;
315     marks[marks_top++] = index;
316     assert( marks_top <= MAX_MARKS );
317     track_blocks+=2;
318     index_to_stop_at = _index_to_stop_at >= index ? _index_to_stop_at : -1;
319 }
320
321
322 void CvTestMemoryManager::stop_tracking_and_check()
323 {
324     if( marks_top > 0 )
325     {
326         int min_index = marks[--marks_top];
327         clear_and_check( min_index );
328     }
329 }
330
331
332 int CvTestMemoryManager::free_block( CvTestAllocBlock* block )
333 {
334     int code = 0;
335     char* data = block->data;
336
337     if( block->origin == 0 || ((size_t)block->origin & (sizeof(double)-1)) != 0 )
338         code = CvTS::FAIL_MEMORY_CORRUPTION_BEGIN;
339
340     if( memcmp( data - guard_size, guard_pattern, guard_size ) != 0 )
341         code = CvTS::FAIL_MEMORY_CORRUPTION_BEGIN;
342     else if( memcmp( data + block->size, guard_pattern, guard_size ) != 0 )
343         code = CvTS::FAIL_MEMORY_CORRUPTION_END;
344
345     if( code >= 0 )
346     {
347         if( block->prev )
348             block->prev->next = block->next;
349         else if( first == block )
350             first = block->next;
351
352         if( block->next )
353             block->next->prev = block->prev;
354         else if( last == block )
355             last = block->prev;
356
357         free( block->origin );
358     }
359     else
360     {
361         ts->set_failed_test_info( code, block->index );
362         ts->printf( CvTS::LOG, "Corrupted block (%s): %p, %u bytes\n",
363                     code == CvTS::FAIL_MEMORY_CORRUPTION_BEGIN ? "beginning" : "end",
364                     block->data, block->size );
365     }
366
367     return code;
368 }
369
370
371 void* CvTestMemoryManager::alloc( size_t size )
372 {
373     char* data;
374     CvTestAllocBlock* block;
375     size_t new_size = sizeof(*block) + size + guard_size*2 + block_align + sizeof(size_t)*2;
376     char* ptr = (char*)malloc( new_size );
377
378     if( !ptr )
379         return 0;
380
381     data = (char*)cvAlignPtr( ptr + sizeof(size_t) + sizeof(*block) + guard_size, block_align );
382     block = (CvTestAllocBlock*)cvAlignPtr( data - guard_size -
383             sizeof(size_t) - sizeof(*block), sizeof(size_t) );
384     block->origin = ptr;
385     block->data = data;
386     block->size = 0;
387     block->index = -1;
388     block->next = block->prev = 0;
389     memcpy( data - guard_size, guard_pattern, guard_size );
390     memcpy( data + size, guard_pattern, guard_size );
391
392     if( track_blocks > 0 )
393     {
394         track_blocks--;
395         block->size = size;
396
397         if( index == index_to_stop_at )
398         {
399             if( show_msg_box )
400             {
401         #if defined WIN32 || defined _WIN32
402                 MessageBox( NULL, "The block that is corrupted and/or not deallocated has been just allocated\n"
403                             "Press Ok to start debugging", "Memory Manager", MB_ICONERROR|MB_OK|MB_SYSTEMMODAL );
404         #endif
405             }
406             CV_DBG_BREAK();
407         }
408
409         block->index = index++;
410
411         block->prev = last;
412         block->next = 0;
413         if( last )
414             last = last->next = block;
415         else
416             first = last = block;
417
418         track_blocks++;
419     }
420
421     return data;
422 }
423
424
425 int CvTestMemoryManager::free( void* ptr )
426 {
427     char* data = (char*)ptr;
428     CvTestAllocBlock* block = (CvTestAllocBlock*)
429         cvAlignPtr( data - guard_size - sizeof(size_t) - sizeof(*block), sizeof(size_t) );
430
431     int code = free_block( block );
432     if( code < 0 && ts->is_debug_mode() )
433         CV_DBG_BREAK();
434     return 0;
435 }
436
437
438 /***************************** error handler *****************************/
439
440 #if 0
441 static int cvTestErrorCallback( int status, const char* func_name, const char* err_msg,
442                          const char* file_name, int line, void* userdata )
443 {
444     if( status < 0 && status != CV_StsBackTrace && status != CV_StsAutoTrace )
445         ((CvTS*)userdata)->set_failed_test_info( CvTS::FAIL_ERROR_IN_CALLED_FUNC );
446
447     // print error message
448     return cvStdErrReport( status, func_name, err_msg, file_name, line, 0 );
449 }
450 #endif
451
452 /*****************************************************************************************\
453 *                                    Base Class for Tests                                 *
454 \*****************************************************************************************/
455
456 CvTest::CvTest( const char* _test_name, const char* _test_funcs, const char* _test_descr ) :
457     name(_test_name ? _test_name : ""), tested_functions(_test_funcs ? _test_funcs : ""),
458     description(_test_descr ? _test_descr : ""), ts(0)
459 {
460     if( last )
461         last->next = this;
462     else
463         first = this;
464     last = this;
465     test_count++;
466     ts = 0;
467     hdr_state = 0;
468
469     timing_param_names = 0;
470     timing_param_current = 0;
471     timing_param_seqs = 0;
472     timing_param_idxs = 0;
473     timing_param_count = -1;
474
475     test_case_count = -1;
476     support_testing_modes = CvTS::CORRECTNESS_CHECK_MODE;
477 }
478
479 CvTest::~CvTest()
480 {
481     clear();
482 }
483
484 CvTest* CvTest::get_first_test()
485 {
486     return first;
487 }
488
489 void CvTest::clear()
490 {
491     if( timing_param_current )
492         free( timing_param_current );
493     if( timing_param_seqs )
494         free( timing_param_seqs );
495     if( timing_param_idxs )
496         free( timing_param_idxs );
497
498     timing_param_current = 0;
499     timing_param_seqs = 0;
500     timing_param_idxs = 0;
501     timing_param_count = -1;
502 }
503
504
505 int CvTest::init( CvTS* _test_system )
506 {
507     clear();
508     ts = _test_system;
509     return read_params( ts->get_file_storage() );
510 }
511
512
513 const char* CvTest::get_parent_name( const char* name, char* buffer )
514 {
515     const char* dash_pos = strrchr( name ? name : "", '-' );
516     if( !dash_pos )
517         return 0;
518
519     if( name != (const char*)buffer )
520         strncpy( buffer, name, dash_pos - name );
521     buffer[dash_pos - name] = '\0';
522     return buffer;
523 }
524
525
526 const CvFileNode* CvTest::find_param( CvFileStorage* fs, const char* param_name )
527 {
528     char buffer[256];
529     const char* name = get_name();
530     CvFileNode* node = 0;
531
532     for(;;)
533     {
534         if( !name )
535             break;
536         node = cvGetFileNodeByName( fs, 0, name );
537         if( node )
538         {
539             node = cvGetFileNodeByName( fs, node, param_name );
540             if( node )
541                 break;
542         }
543         name = get_parent_name( name, buffer );
544     }
545
546     return node;
547 }
548
549
550 void CvTest::start_write_param( CvFileStorage* fs )
551 {
552     if( hdr_state == 0 )
553     {
554         cvStartWriteStruct( fs, get_name(), CV_NODE_MAP );
555         hdr_state = 1;
556     }
557 }
558
559
560 void CvTest::write_param( CvFileStorage* fs, const char* paramname, int val )
561 {
562     if( !ts->find_written_param( this, paramname, CV_NODE_INT, &val) )
563     {
564         start_write_param( fs );
565         cvWriteInt( fs, paramname, val );
566     }
567 }
568
569
570 void CvTest::write_param( CvFileStorage* fs, const char* paramname, double val )
571 {
572     if( !ts->find_written_param( this, paramname, CV_NODE_REAL, &val) )
573     {
574         start_write_param( fs );
575         cvWriteReal( fs, paramname, val );
576     }
577 }
578
579
580 void CvTest::write_param( CvFileStorage* fs, const char* paramname, const char* val )
581 {
582     if( !ts->find_written_param( this, paramname, CV_NODE_STRING, &val) )
583     {
584         start_write_param( fs );
585         cvWriteString( fs, paramname, val );
586     }
587 }
588
589
590 void CvTest::write_string_list( CvFileStorage* fs, const char* paramname, const char** val, int count )
591 {
592     if( val )
593     {
594         start_write_param( fs );
595         int i;
596         if( count < 0 )
597             count = INT_MAX;
598
599         cvStartWriteStruct( fs, paramname, CV_NODE_SEQ + CV_NODE_FLOW );
600         for( i = 0; i < count && val[i] != 0; i++ )
601             cvWriteString( fs, 0, val[i] );
602         cvEndWriteStruct( fs );
603     }
604 }
605
606
607 void CvTest::write_int_list( CvFileStorage* fs, const char* paramname,
608                              const int* val, int count, int stop_value )
609 {
610     if( val )
611     {
612         start_write_param( fs );
613         int i;
614         if( count < 0 )
615             count = INT_MAX;
616
617         cvStartWriteStruct( fs, paramname, CV_NODE_SEQ + CV_NODE_FLOW );
618         for( i = 0; i < count && val[i] != stop_value; i++ )
619             cvWriteInt( fs, 0, val[i] );
620         cvEndWriteStruct( fs );
621     }
622 }
623
624
625 void CvTest::write_real_list( CvFileStorage* fs, const char* paramname,
626                               const double* val, int count, double stop_value )
627 {
628     if( val )
629     {
630         start_write_param( fs );
631         int i;
632         if( count < 0 )
633             count = INT_MAX;
634
635         cvStartWriteStruct( fs, paramname, CV_NODE_SEQ + CV_NODE_FLOW );
636         for( i = 0; i < count && val[i] != stop_value; i++ )
637             cvWriteReal( fs, 0, val[i] );
638         cvEndWriteStruct( fs );
639     }
640 }
641
642
643 int CvTest::read_params( CvFileStorage* fs )
644 {
645     int code = 0;
646
647     if( ts->get_testing_mode() == CvTS::TIMING_MODE )
648     {
649         timing_param_names = find_param( fs, "timing_params" );
650         if( CV_NODE_IS_SEQ(timing_param_names->tag) )
651         {
652             CvSeq* seq = timing_param_names->data.seq;
653             CvSeqReader reader;
654             cvStartReadSeq( seq, &reader );
655             int i;
656
657             timing_param_count = seq->total;
658             timing_param_seqs = (const CvFileNode**)malloc( timing_param_count*sizeof(timing_param_seqs[0]));
659             timing_param_idxs = (int*)malloc( timing_param_count*sizeof(timing_param_idxs[0]));
660             timing_param_current = (const CvFileNode**)malloc( timing_param_count*sizeof(timing_param_current[0]));
661             test_case_count = 1;
662
663             for( i = 0; i < timing_param_count; i++ )
664             {
665                 CvFileNode* param_name = (CvFileNode*)(reader.ptr);
666
667                 if( !CV_NODE_IS_STRING(param_name->tag) )
668                 {
669                     ts->printf( CvTS::LOG, "ERROR: name of timing parameter #%d is not a string\n", i );
670                     code = -1;
671                     break;
672                 }
673
674                 timing_param_idxs[i] = 0;
675                 timing_param_current[i] = 0;
676                 timing_param_seqs[i] = find_param( fs, param_name->data.str.ptr );
677                 if( !timing_param_seqs[i] )
678                 {
679                     ts->printf( CvTS::LOG, "ERROR: timing parameter %s is not found\n", param_name->data.str.ptr );
680                     code = -1;
681                     break;
682                 }
683
684                 if( CV_NODE_IS_SEQ(timing_param_seqs[i]->tag) )
685                     test_case_count *= timing_param_seqs[i]->data.seq->total;
686
687                 CV_NEXT_SEQ_ELEM( seq->elem_size, reader );
688             }
689
690             if( i < timing_param_count )
691                 timing_param_count = 0;
692         }
693         else
694         {
695             ts->printf( CvTS::LOG, "ERROR: \"timing_params\" is not found" );
696             code = -1;
697         }
698     }
699
700     return code;
701 }
702
703
704 int CvTest::get_next_timing_param_tuple()
705 {
706     bool increment;
707     int i;
708
709     if( timing_param_count <= 0 || !timing_param_names || !timing_param_seqs )
710         return -1;
711
712     increment = timing_param_current[0] != 0; // if already have some valid test tuple, move to the next
713     for( i = 0; i < timing_param_count; i++ )
714     {
715         const CvFileNode* node = timing_param_seqs[i];
716         int total = CV_NODE_IS_SEQ(node->tag) ? node->data.seq->total : 1;
717         int new_idx = timing_param_idxs[i];
718
719         if( !timing_param_current[i] )
720             timing_param_idxs[i] = new_idx = 0;
721         else if( increment )
722         {
723             new_idx++;
724             if( new_idx >= total )
725                 new_idx = 0;
726             else if( total > 1 )
727                 increment = false;
728         }
729
730         if( !timing_param_current[i] || new_idx != timing_param_idxs[i] )
731         {
732             if( CV_NODE_IS_SEQ(node->tag) )
733                 timing_param_current[i] = (CvFileNode*)cvGetSeqElem( node->data.seq, new_idx );
734             else
735                 timing_param_current[i] = node;
736             timing_param_idxs[i] = new_idx;
737         }
738     }
739
740     return !increment; // return 0 in case of overflow (i.e. if there is no more test cases)
741 }
742
743
744 const CvFileNode* CvTest::find_timing_param( const char* paramname )
745 {
746     if( timing_param_names )
747     {
748         int i;
749         CvSeqReader reader;
750         cvStartReadSeq( timing_param_names->data.seq, &reader, 0 );
751
752         for( i = 0; i < timing_param_count; i++ )
753         {
754             const char* ptr = ((const CvFileNode*)(reader.ptr))->data.str.ptr;
755             if( ptr[0] == paramname[0] && strcmp(ptr, paramname) == 0 )
756                 return timing_param_current[i];
757             CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
758         }
759     }
760     return 0;
761 }
762
763
764 int CvTest::write_defaults(CvTS* _ts)
765 {
766     ts = _ts;
767     hdr_state = 0;
768     write_default_params( ts->get_file_storage() );
769     if( hdr_state )
770         cvEndWriteStruct( ts->get_file_storage() );
771     return 0;
772 }
773
774
775 int CvTest::write_default_params( CvFileStorage* fs )
776 {
777     if( ts->get_testing_mode() == CvTS::TIMING_MODE )
778         write_string_list( fs, "timing_params", default_timing_param_names, timing_param_count );
779     return 0;
780 }
781
782
783 bool CvTest::can_do_fast_forward()
784 {
785     return true;
786 }
787
788
789 int CvTest::get_support_testing_modes()
790 {
791     return support_testing_modes;
792 }
793
794 void CvTest::safe_run( int start_from )
795 {
796     CV_TS_TRY_BLOCK_BEGIN;
797
798     run( start_from );
799
800     CV_TS_TRY_BLOCK_END;
801 }
802
803
804 void CvTest::run( int start_from )
805 {
806     int i, test_case_idx, count = get_test_case_count();
807     int64 t_start = cvGetTickCount();
808     double freq = cvGetTickFrequency();
809     bool ff = can_do_fast_forward();
810     int progress = 0, code;
811
812     for( test_case_idx = ff && start_from >= 0 ? start_from : 0;
813          count < 0 || test_case_idx < count; test_case_idx++ )
814     {
815         ts->update_context( this, test_case_idx, ff );
816         int64 t00 = 0, t0, t1 = 0;
817         double t_acc = 0;
818
819         if( ts->get_testing_mode() == CvTS::TIMING_MODE )
820         {
821             const int iterations = 15;
822             code = prepare_test_case( test_case_idx );
823
824             if( code < 0 || ts->get_err_code() < 0 )
825                 return;
826
827             if( code == 0 )
828                 continue;
829
830             for( i = 0; i < iterations; i++ )
831             {
832                 t0 = cvGetTickCount();
833                 run_func();
834                 t1 = cvGetTickCount();
835                 if( ts->get_err_code() < 0 )
836                     return;
837
838                 if( i == 0 )
839                 {
840                     t_acc = (double)(t1 - t0);
841                     t00 = t0;
842                 }
843                 else
844                 {
845                     t0 = t1 - t0;
846
847                     if( ts->get_timing_mode() == CvTS::MIN_TIME )
848                     {
849                         if( (double)t0 < t_acc )
850                             t_acc = (double)t0;
851                     }
852                     else
853                     {
854                         assert( ts->get_timing_mode() == CvTS::AVG_TIME );
855                         t_acc += (double)t0;
856                     }
857
858                     if( t1 - t00 > freq*2000000 )
859                         break;
860                 }
861             }
862
863             if( ts->get_timing_mode() == CvTS::AVG_TIME )
864                 t_acc /= i;
865             print_time( test_case_idx, t_acc );
866         }
867         else
868         {
869             code = prepare_test_case( test_case_idx );
870             if( code < 0 || ts->get_err_code() < 0 )
871                 return;
872
873             if( code == 0 )
874                 continue;
875
876             run_func();
877             if( ts->get_err_code() < 0 )
878                 return;
879
880             if( validate_test_results( test_case_idx ) < 0 || ts->get_err_code() < 0 )
881                 return;
882         }
883
884         progress = update_progress( progress, test_case_idx, count, (double)(t1 - t_start)/(freq*1000) );
885     }
886 }
887
888
889 void CvTest::run_func()
890 {
891     assert(0);
892 }
893
894
895 int CvTest::get_test_case_count()
896 {
897     return test_case_count;
898 }
899
900
901 int CvTest::prepare_test_case( int )
902 {
903     return 0;
904 }
905
906
907 int CvTest::validate_test_results( int )
908 {
909     return 0;
910 }
911
912
913 void CvTest::print_time( int /*test_case_idx*/, double /*time_usecs*/ )
914 {
915 }
916
917
918 int CvTest::update_progress( int progress, int test_case_idx, int count, double dt )
919 {
920     int width = 60 - (int)strlen(get_name());
921     if( count > 0 )
922     {
923         int t = cvRound( ((double)test_case_idx * width)/count );
924         if( t > progress )
925         {
926             ts->printf( CvTS::CONSOLE, "." );
927             progress = t;
928         }
929     }
930     else if( cvRound(dt) > progress )
931     {
932         ts->printf( CvTS::CONSOLE, "." );
933         progress = cvRound(dt);
934     }
935
936     return progress;
937 }
938
939 /*****************************************************************************************\
940 *                                 Base Class for Test System                              *
941 \*****************************************************************************************/
942
943 /******************************** Constructors/Destructors ******************************/
944
945 CvTS::CvTS()
946 {
947     start_time = 0;
948     version = CV_TS_VERSION;
949     memory_manager = 0;
950     /*
951     memory_manager = new CvTestMemoryManager(this);
952     cvSetMemoryManager( CvTestMemoryManager::alloc_proxy,
953                         CvTestMemoryManager::free_proxy,
954                         memory_manager );*/
955     ostrm_suffixes[SUMMARY_IDX] = ".sum";
956     ostrm_suffixes[LOG_IDX] = ".log";
957     ostrm_suffixes[CSV_IDX] = ".csv";
958     ostrm_suffixes[CONSOLE_IDX] = 0;
959     ostrm_base_name = 0;
960     memset( output_streams, 0, sizeof(output_streams) );
961     memset( &params, 0, sizeof(params) );
962     selected_tests = new CvTestPtrVec();
963     failed_tests = new CvTestInfoVec();
964     written_params = new CvTestPtrVec();
965
966     clear();
967 }
968
969
970 void CvTS::clear()
971 {
972     int i;
973     CvTest* test;
974
975     for( test = get_first_test(); test != 0; test = test->get_next() )
976         test->clear();
977
978     for( i = 0; i <= CONSOLE_IDX; i++ )
979     {
980         if( i == LOG_IDX )
981             fflush( stderr );
982         else if( i == CONSOLE_IDX )
983             fflush( stdout );
984
985         if( i < CONSOLE_IDX && output_streams[i].f )
986         {
987             fclose( output_streams[i].f );
988             output_streams[i].f = 0;
989         }
990
991         if( i == LOG_IDX && output_streams[i].default_handle > 0 )
992         {
993             dup2( output_streams[i].default_handle, 2 );
994             output_streams[i].default_handle = 0;
995         }
996         output_streams[i].enable = 1;
997     }
998     cvReleaseFileStorage( &fs );
999     selected_tests->clear();
1000     failed_tests->clear();
1001     if( ostrm_base_name )
1002     {
1003         free( ostrm_base_name );
1004         ostrm_base_name = 0;
1005     }
1006     params.rng_seed = (uint64)-1;
1007     params.debug_mode = 1;
1008     params.print_only_failed = 0;
1009     params.skip_header = 0;
1010     params.test_mode = CORRECTNESS_CHECK_MODE;
1011     params.timing_mode = MIN_TIME;
1012     params.use_optimized = -1;
1013     params.color_terminal = 1;
1014
1015     if( memory_manager )
1016         memory_manager->clear_and_check();
1017 }
1018
1019
1020 CvTS::~CvTS()
1021 {
1022     clear();
1023     set_data_path(0);
1024
1025     if( written_params )
1026     {
1027         for( int i = 0; i < written_params->size(); i++ )
1028             free( written_params->at(i) );
1029         delete written_params;
1030     }
1031
1032     delete selected_tests;
1033     delete failed_tests;
1034 }
1035
1036
1037 const char* CvTS::str_from_code( int code )
1038 {
1039     switch( code )
1040     {
1041     case OK: return "Ok";
1042     case FAIL_GENERIC: return "Generic/Unknown";
1043     case FAIL_MISSING_TEST_DATA: return "No test data";
1044     case FAIL_INVALID_TEST_DATA: return "Invalid test data";
1045     case FAIL_ERROR_IN_CALLED_FUNC: return "cvError invoked";
1046     case FAIL_EXCEPTION: return "Hardware/OS exception";
1047     case FAIL_MEMORY_EXCEPTION: return "Invalid memory access";
1048     case FAIL_ARITHM_EXCEPTION: return "Arithmetic exception";
1049     case FAIL_MEMORY_CORRUPTION_BEGIN: return "Corrupted memblock (beginning)";
1050     case FAIL_MEMORY_CORRUPTION_END: return "Corrupted memblock (end)";
1051     case FAIL_MEMORY_LEAK: return "Memory leak";
1052     case FAIL_INVALID_OUTPUT: return "Invalid function output";
1053     case FAIL_MISMATCH: return "Unexpected output";
1054     case FAIL_BAD_ACCURACY: return "Bad accuracy";
1055     case FAIL_HANG: return "Infinite loop(?)";
1056     case FAIL_BAD_ARG_CHECK: return "Incorrect handling of bad arguments";
1057     default: return "Generic/Unknown";
1058     }
1059 }
1060
1061 /************************************** Running tests **********************************/
1062
1063 void CvTS::make_output_stream_base_name( const char* config_name )
1064 {
1065     int k, len = (int)strlen( config_name );
1066
1067     if( ostrm_base_name )
1068         free( ostrm_base_name );
1069
1070     for( k = len-1; k >= 0; k-- )
1071     {
1072         char c = config_name[k];
1073         if( c == '.' || c == '/' || c == '\\' || c == ':' )
1074             break;
1075     }
1076
1077     if( k > 0 && config_name[k] == '.' )
1078         len = k;
1079
1080     ostrm_base_name = (char*)malloc( len + 1 );
1081     memcpy( ostrm_base_name, config_name, len );
1082     ostrm_base_name[len] = '\0';
1083 }
1084
1085
1086 void CvTS::set_handlers( bool on )
1087 {
1088     if( on )
1089     {
1090         cvSetErrMode( CV_ErrModeParent );
1091         cvRedirectError( cvStdErrReport );
1092     #if defined WIN32 || defined _WIN32
1093         #ifdef _MSC_VER
1094         _set_se_translator( cv_seh_translator );
1095         #endif
1096     #else
1097         for( int i = 0; cv_ts_sig_id[i] >= 0; i++ )
1098             signal( cv_ts_sig_id[i], cv_signal_handler );
1099     #endif
1100     }
1101     else
1102     {
1103         cvSetErrMode( CV_ErrModeLeaf );
1104         cvRedirectError( cvGuiBoxReport );
1105     #if defined WIN32 || defined _WIN32
1106         #ifdef _MSC_VER
1107         _set_se_translator( 0 );
1108         #endif
1109     #else
1110         for( int i = 0; cv_ts_sig_id[i] >= 0; i++ )
1111             signal( cv_ts_sig_id[i], SIG_DFL );
1112     #endif
1113     }
1114 }
1115
1116
1117 void CvTS::set_data_path( const char* data_path )
1118 {
1119     if( data_path == params.data_path )
1120         return;
1121
1122     if( params.data_path )
1123         delete[] params.data_path;
1124     if( data_path )
1125     {
1126         int size = (int)strlen(data_path)+1;
1127         bool append_slash = data_path[size-1] != '/' && data_path[size-1] != '\\';
1128         params.data_path = new char[size+1];
1129         memcpy( params.data_path, data_path, size );
1130         if( append_slash )
1131             strcat( params.data_path, "/" );
1132     }
1133 }
1134
1135
1136 typedef struct CvTsParamVal
1137 {
1138     const char* fullname;
1139     const void* val;
1140 }
1141 CvTsParamVal;
1142
1143 int CvTS::find_written_param( CvTest* test, const char* paramname, int valtype, const void* val )
1144 {
1145     const char* testname = test->get_name();
1146     bool add_to_list = test->get_func_list()[0] == '\0';
1147     char buffer[256];
1148     int paramname_len = (int)strlen(paramname);
1149     int paramval_len = valtype == CV_NODE_INT ? (int)sizeof(int) :
1150         valtype == CV_NODE_REAL ? (int)sizeof(double) : -1;
1151     const char* name = CvTest::get_parent_name( testname, buffer );
1152
1153     if( !fs )
1154         return -1;
1155
1156     if( paramval_len < 0 )
1157     {
1158         assert(0); // unsupported parameter type
1159         return -1;
1160     }
1161
1162     while( name )
1163     {
1164         int i, len = (int)strlen(buffer);
1165         buffer[len] = '.';
1166         memcpy( buffer + len + 1, paramname, paramname_len + 1 );
1167         for( i = 0; i < written_params->size(); i++ )
1168         {
1169             CvTsParamVal* param = (CvTsParamVal*)written_params->at(i);
1170             if( strcmp( param->fullname, buffer ) == 0 )
1171             {
1172                 if( (paramval_len > 0 && memcmp( param->val, val, paramval_len ) == 0) ||
1173                     (paramval_len < 0 && strcmp( (const char*)param->val, (const char*)val ) == 0) )
1174                     return 1;
1175                 break;
1176             }
1177         }
1178         if( i < written_params->size() )
1179             break;
1180         buffer[len] = '\0';
1181         name = CvTest::get_parent_name( buffer, buffer );
1182     }
1183
1184     if( add_to_list )
1185     {
1186         int bufsize, fullname_len = (int)strlen(testname) + paramname_len + 2;
1187         CvTsParamVal* param;
1188         if( paramval_len < 0 )
1189             paramval_len = (int)strlen((const char*)val) + 1;
1190         bufsize = sizeof(*param) + fullname_len + paramval_len;
1191         param = (CvTsParamVal*)malloc(bufsize);
1192         param->fullname = (const char*)(param + 1);
1193         param->val = param->fullname + fullname_len;
1194         sprintf( (char*)param->fullname, "%s.%s", testname, paramname );
1195         memcpy( (void*)param->val, val, paramval_len );
1196         written_params->push( param );
1197     }
1198
1199     return 0;
1200 }
1201
1202
1203 #ifndef MAX_PATH
1204 #define MAX_PATH 1024
1205 #endif
1206
1207 static int CV_CDECL cmp_test_names( const void* a, const void* b )
1208 {
1209     return strcmp( (*(const CvTest**)a)->get_name(), (*(const CvTest**)b)->get_name() );
1210 }
1211
1212 int CvTS::run( int argc, char** argv )
1213 {
1214     time( &start_time );
1215
1216     int i, write_params = 0;
1217     int list_tests = 0;
1218     CvTestPtrVec all_tests;
1219     CvTest* test;
1220
1221     // 0. reset all the parameters, reorder tests
1222     clear();
1223
1224     for( test = get_first_test(), i = 0; test != 0; test = test->get_next(), i++ )
1225         all_tests.push(test);
1226
1227     if( all_tests.size() > 0 && all_tests.data() )
1228         qsort( all_tests.data(), all_tests.size(), sizeof(CvTest*), cmp_test_names );
1229
1230     // 1. parse command line options
1231     for( i = 1; i < argc; i++ )
1232     {
1233         if( strcmp( argv[i], "-h" ) == 0 || strcmp( argv[i], "--help" ) == 0 )
1234         {
1235             print_help();
1236             return 0;
1237         }
1238         else if( strcmp( argv[i], "-f" ) == 0 )
1239             config_name = argv[++i];
1240         else if( strcmp( argv[i], "-w" ) == 0 )
1241             write_params = 1;
1242         else if( strcmp( argv[i], "-t" ) == 0 )
1243             params.test_mode = TIMING_MODE;
1244         else if( strcmp( argv[i], "-O0" ) == 0 || strcmp( argv[i], "-O1" ) == 0 )
1245             params.use_optimized = argv[i][2] - '0';
1246         else if( strcmp( argv[i], "-l" ) == 0 )
1247             list_tests = 1;
1248         else if( strcmp( argv[i], "-d" ) == 0 )
1249             set_data_path(argv[++i]);
1250         else if( strcmp( argv[i], "-nc" ) == 0 )
1251             params.color_terminal = 0;
1252     }
1253
1254 #if 0
1255 //#if !defined WIN32 && !defined _WIN32
1256     if (! config_name )
1257     {    
1258       char * confname = getenv("configname");
1259       if (confname)
1260         config_name = confname;
1261     }
1262     
1263     if( !params.data_path || !params.data_path[0] )
1264     {
1265         char* datapath = getenv("datapath");
1266         if( datapath )
1267             set_data_path(datapath);
1268     }
1269     
1270     // this is the fallback for the current OpenCV autotools setup
1271     if( !params.data_path || !params.data_path[0] )
1272     {
1273         char* srcdir = getenv("srcdir");
1274         char buf[1024];
1275         if( srcdir )
1276         {
1277             sprintf( buf, "%s/../../opencv_extra/testdata/", srcdir );
1278             set_data_path(buf);
1279         }
1280     }
1281 #endif
1282
1283     if( write_params )
1284     {
1285         if( !config_name )
1286         {
1287             printf( LOG, "ERROR: output config name is not specified\n" );
1288             return -1;
1289         }
1290         fs = cvOpenFileStorage( config_name, 0, CV_STORAGE_WRITE );
1291         if( !fs )
1292         {
1293             printf( LOG, "ERROR: could not open config file %s\n", config_name );
1294             return -1;
1295         }
1296         cvWriteComment( fs, CV_TS_VERSION " config file", 0 );
1297         cvStartWriteStruct( fs, "common", CV_NODE_MAP );
1298         write_default_params( fs );
1299         cvEndWriteStruct( fs );
1300
1301         for( i = 0; i < all_tests.size(); i++ )
1302         {
1303             test = (CvTest*)all_tests[i];
1304             if( !(test->get_support_testing_modes() & get_testing_mode()) )
1305                 continue;
1306             test->write_defaults( this );
1307             test->clear();
1308         }
1309         cvReleaseFileStorage( &fs );
1310         return 0;
1311     }
1312
1313     if( !config_name )
1314         printf( LOG, "WARNING: config name is not specified, using default parameters\n" );
1315     else
1316     {
1317         // 2. read common parameters of test system
1318         fs = cvOpenFileStorage( config_name, 0, CV_STORAGE_READ );
1319         if( !fs )
1320         {
1321             printf( LOG, "ERROR: could not open config file %s", config_name );
1322             return -1;
1323         }
1324     }
1325
1326     if( read_params(fs) < 0 )
1327         return -1;
1328
1329     if( !ostrm_base_name )
1330         make_output_stream_base_name( config_name ? config_name : argv[0] );
1331
1332     ostream_testname_mask = -1; // disable printing test names at initial stage
1333
1334     // 3. open file streams
1335     for( i = 0; i < CONSOLE_IDX; i++ )
1336     {
1337         char filename[MAX_PATH];
1338         sprintf( filename, "%s%s", ostrm_base_name, ostrm_suffixes[i] );
1339         output_streams[i].f = fopen( filename, "wt" );
1340         if( !output_streams[i].f )
1341         {
1342             printf( LOG, "ERROR: could not open %s\n", filename );
1343             return -1;
1344         }
1345
1346         if( i == LOG_IDX )
1347         {
1348             // redirect stderr to log file
1349             fflush( stderr );
1350             output_streams[i].default_handle = dup(2);
1351             dup2( fileno(output_streams[i].f), 2 );
1352         }
1353     }
1354
1355     // 4. traverse through the list of all registered tests.
1356     // Initialize the selected tests and put them into the separate sequence
1357     for( i = 0; i < all_tests.size(); i++ )
1358     {
1359         test = (CvTest*)all_tests[i];
1360         if( !(test->get_support_testing_modes() & get_testing_mode()) )
1361             continue;
1362
1363         if( strcmp( test->get_func_list(), "" ) != 0 && filter(test) )
1364         {
1365             if( test->init(this) >= 0 )
1366             {
1367                 selected_tests->push( test );
1368                 if( list_tests )
1369                     ::printf( "%s\n", test->get_name() );
1370             }
1371             else
1372                 printf( LOG, "WARNING: an error occured during test %s initialization\n", test->get_name() );
1373         }
1374     }
1375
1376     if( list_tests )
1377     {
1378         clear();
1379         return 0;
1380     }
1381
1382     // 5. setup all the neccessary handlers and print header
1383     set_handlers( !params.debug_mode );
1384
1385     if( params.use_optimized == 0 )
1386         cvUseOptimized(0);
1387
1388     if( !params.skip_header )
1389         print_summary_header( SUMMARY + LOG + CONSOLE + CSV );
1390     rng = params.rng_seed;
1391     update_context( 0, -1, true );
1392
1393     // 6. run all the tests
1394     for( i = 0; i < selected_tests->size(); i++ )
1395     {
1396         CvTest* test = (CvTest*)selected_tests->at(i);
1397         int code;
1398         CvTestInfo temp;
1399
1400         if( memory_manager )
1401             memory_manager->start_tracking();
1402         update_context( test, -1, true );
1403         ostream_testname_mask = 0; // reset "test name was printed" flags
1404         if( output_streams[LOG_IDX].f )
1405             fflush( output_streams[LOG_IDX].f );
1406
1407         temp = current_test_info;
1408         test->safe_run(0);
1409         if( get_err_code() >= 0 )
1410         {
1411             update_context( test, -1, false );
1412             current_test_info.rng_seed = temp.rng_seed;
1413             current_test_info.base_alloc_index = temp.base_alloc_index;
1414         }
1415         test->clear();
1416         if( memory_manager )
1417             memory_manager->stop_tracking_and_check();
1418
1419         code = get_err_code();
1420         if( code >= 0 )
1421         {
1422             if( !params.print_only_failed )
1423             {
1424                 printf( SUMMARY + CONSOLE, "\t" );
1425                 set_color( CV_TS_GREEN );
1426                 printf( SUMMARY + CONSOLE, "Ok\n" );
1427                 set_color( CV_TS_NORMAL );
1428             }
1429         }
1430         else
1431         {
1432             printf( SUMMARY + CONSOLE, "\t" );
1433             set_color( CV_TS_RED );
1434             printf( SUMMARY + CONSOLE, "FAIL(%s)\n", str_from_code(code) );
1435             set_color( CV_TS_NORMAL );
1436             printf( LOG, "context: test case = %d, seed = %08x%08x\n",
1437                     current_test_info.test_case_idx,
1438                     (unsigned)(current_test_info.rng_seed>>32),
1439                     (unsigned)(current_test_info.rng_seed));
1440             failed_tests->push(current_test_info);
1441             if( params.rerun_immediately )
1442                 break;
1443         }
1444     }
1445
1446     ostream_testname_mask = -1;
1447     print_summary_tailer( SUMMARY + CONSOLE + LOG );
1448
1449     if( !params.debug_mode && (params.rerun_failed || params.rerun_immediately) )
1450     {
1451         set_handlers(0);
1452         update_context( 0, -1, true );
1453         for( i = 0; i < failed_tests->size(); i++ )
1454         {
1455             CvTestInfo info = failed_tests->at(i);
1456             if( (info.code == FAIL_MEMORY_CORRUPTION_BEGIN ||
1457                 info.code == FAIL_MEMORY_CORRUPTION_END ||
1458                 info.code == FAIL_MEMORY_LEAK) && memory_manager )
1459                 memory_manager->start_tracking( info.alloc_index - info.base_alloc_index
1460                                                 + memory_manager->get_alloc_index() );
1461             rng = info.rng_seed;
1462             test->safe_run( info.test_case_idx );
1463         }
1464     }
1465     int nfailed = failed_tests ? (int)failed_tests->size() : 0;
1466     clear();
1467
1468     return nfailed;
1469 }
1470
1471
1472 void CvTS::print_help()
1473 {
1474     ::printf(
1475         "Usage: <test_executable> [{-h|--help}][-l] [-w] [-t] [-f <config_name>] [-d <data_path>] [-O{0|1}]\n\n"
1476         "-d - specify the test data path\n\n"
1477         "-f - use parameters from the provided config XML/YAML file instead of the default parameters\n\n"
1478         "-h or --help - print this help information\n\n"
1479         "-l - list all the registered tests or subset of the tests, selected in the config file, and exit\n\n"
1480         "-nc - do not use colors in the console output\n\n"     
1481         "-O{0|1} - disable/enable on-fly detection of IPP and other supported optimized libs. It's enabled by default\n\n"
1482         "-t - switch to the performance testing mode instead of the default algorithmic/correctness testing mode\n\n"
1483         "-w - write default parameters of the algorithmic or performance (when -t is passed) tests to the specifed config file (see -f) and exit\n\n"
1484         //"Test data path and config file can also be specified by the environment variables 'config' and 'datapath'.\n\n"
1485         );
1486 }
1487
1488
1489 #if defined WIN32 || defined _WIN32
1490 const char* default_data_path = "../tests/cv/testdata/";
1491 #else
1492 const char* default_data_path = "../../../../tests/cv/testdata/";
1493 #endif
1494
1495
1496 int CvTS::read_params( CvFileStorage* fs )
1497 {
1498     CvFileNode* node = fs ? cvGetFileNodeByName( fs, 0, "common" ) : 0;
1499     params.debug_mode = cvReadIntByName( fs, node, "debug_mode", 1 ) != 0;
1500     params.skip_header = cvReadIntByName( fs, node, "skip_header", 0 ) != 0;
1501     params.print_only_failed = cvReadIntByName( fs, node, "print_only_failed", 0 ) != 0;
1502     params.rerun_failed = cvReadIntByName( fs, node, "rerun_failed", 0 ) != 0;
1503     params.rerun_immediately = cvReadIntByName( fs, node, "rerun_immediately", 0 ) != 0;
1504     const char* str = cvReadStringByName( fs, node, "filter_mode", "tests" );
1505     params.test_filter_mode = strcmp( str, "functions" ) == 0 ? CHOOSE_FUNCTIONS : CHOOSE_TESTS;
1506     str = cvReadStringByName( fs, node, "test_mode", params.test_mode == TIMING_MODE ? "timing" : "correctness" );
1507     params.test_mode = strcmp( str, "timing" ) == 0 || strcmp( str, "performance" ) == 0 ?
1508                         TIMING_MODE : CORRECTNESS_CHECK_MODE;
1509     str = cvReadStringByName( fs, node, "timing_mode", params.timing_mode == AVG_TIME ? "avg" : "min" );
1510     params.timing_mode = strcmp( str, "average" ) == 0 || strcmp( str, "avg" ) == 0 ? AVG_TIME : MIN_TIME;
1511     params.test_filter_pattern = cvReadStringByName( fs, node, params.test_filter_mode == CHOOSE_FUNCTIONS ?
1512                                                      "functions" : "tests", "" );
1513     params.resource_path = cvReadStringByName( fs, node, "." );
1514     if( params.use_optimized < 0 )
1515         params.use_optimized = cvReadIntByName( fs, node, "use_optimized", -1 );
1516     if( !params.data_path || !params.data_path[0] )
1517     {
1518         const char* data_path =
1519             cvReadStringByName( fs, node, "data_path", default_data_path );
1520         set_data_path(data_path);
1521     }
1522     params.test_case_count_scale = cvReadRealByName( fs, node, "test_case_count_scale", 1. );
1523     if( params.test_case_count_scale <= 0 )
1524         params.test_case_count_scale = 1.;
1525     str = cvReadStringByName( fs, node, "seed", 0 );
1526     params.rng_seed = 0;
1527     if( str && strlen(str) == 16 )
1528     {
1529         params.rng_seed = 0;
1530         for( int i = 0; i < 16; i++ )
1531         {
1532             int c = tolower(str[i]);
1533             if( !isxdigit(c) )
1534             {
1535                 params.rng_seed = 0;
1536                 break;
1537             }
1538             params.rng_seed = params.rng_seed * 16 +
1539                 (str[i] < 'a' ? str[i] - '0' : str[i] - 'a' + 10);
1540         }
1541     }
1542
1543     if( params.rng_seed == 0 )
1544         params.rng_seed = cvGetTickCount();
1545
1546     str = cvReadStringByName( fs, node, "output_file_base_name", 0 );
1547     if( str )
1548         make_output_stream_base_name( str );
1549
1550     return 0;
1551 }
1552
1553
1554 void CvTS::write_default_params( CvFileStorage* fs )
1555 {
1556     read_params(0); // fill parameters with default values
1557
1558     cvWriteInt( fs, "debug_mode", params.debug_mode );
1559     cvWriteInt( fs, "skip_header", params.skip_header );
1560     cvWriteInt( fs, "print_only_failed", params.print_only_failed );
1561     cvWriteInt( fs, "rerun_failed", params.rerun_failed );
1562     cvWriteInt( fs, "rerun_immediately", params.rerun_immediately );
1563     cvWriteString( fs, "filter_mode", params.test_filter_mode == CHOOSE_FUNCTIONS ? "functions" : "tests" );
1564     cvWriteString( fs, "test_mode", params.test_mode == TIMING_MODE ? "timing" : "correctness" );
1565     cvWriteString( fs, "data_path", params.data_path ? params.data_path : default_data_path, 1 );
1566     if( params.test_mode == TIMING_MODE )
1567         cvWriteString( fs, "timing_mode", params.timing_mode == AVG_TIME ? "avg" : "min" );
1568     // test_filter, seed & output_file_base_name are not written
1569 }
1570
1571
1572 void CvTS::enable_output_streams( int stream_mask, int value )
1573 {
1574     for( int i = 0; i < MAX_IDX; i++ )
1575         if( stream_mask & (1 << i) )
1576             output_streams[i].enable = value != 0;
1577 }
1578
1579
1580 void CvTS::update_context( CvTest* test, int test_case_idx, bool update_ts_context )
1581 {
1582     current_test_info.test = test;
1583     current_test_info.test_case_idx = test_case_idx;
1584     current_test_info.alloc_index = 0;
1585     current_test_info.code = 0;
1586     cvSetErrStatus( CV_StsOk );
1587     if( update_ts_context )
1588     {
1589         current_test_info.rng_seed = rng;
1590         current_test_info.base_alloc_index = memory_manager ?
1591             memory_manager->get_alloc_index() : 0;
1592     }
1593 }
1594
1595
1596 void CvTS::set_failed_test_info( int fail_code, int alloc_index )
1597 {
1598     if( fail_code == FAIL_MEMORY_CORRUPTION_BEGIN ||
1599         fail_code == FAIL_MEMORY_CORRUPTION_END ||
1600         current_test_info.code >= 0 )
1601     {
1602         current_test_info.code = fail_code;
1603         current_test_info.alloc_index = alloc_index;
1604     }
1605 }
1606
1607
1608 const char* CvTS::get_libs_info( const char** addon_modules )
1609 {
1610     const char* all_info = 0;
1611     cvGetModuleInfo( 0, &all_info, addon_modules );
1612     return all_info;
1613 }
1614
1615
1616 void CvTS::print_summary_header( int streams )
1617 {
1618     char csv_header[256], *ptr = csv_header;
1619     int i;
1620
1621     printf( streams, "Engine: %s\n", version );
1622     time_t t1;
1623     time( &t1 );
1624     struct tm *t2 = localtime( &t1 );
1625     char buf[1024];
1626     strftime( buf, sizeof(buf)-1, "%c", t2 );
1627     printf( streams, "Execution Date & Time: %s\n", buf );
1628     printf( streams, "Config File: %s\n", config_name );
1629     const char* plugins = 0;
1630     const char* lib_verinfo = get_libs_info( &plugins );
1631     printf( streams, "Tested Libraries: %s\n", lib_verinfo );
1632     printf( streams, "Optimized Low-level Plugin\'s: %s\n", plugins );
1633     printf( streams, "=================================================\n");
1634
1635     sprintf( ptr, "funcName,dataType,channels,size," );
1636     ptr += strlen(ptr);
1637
1638     for( i = 0; i < CvTest::TIMING_EXTRA_PARAMS; i++ )
1639     {
1640         sprintf( ptr, "param%d,", i );
1641         ptr += strlen(ptr);
1642     }
1643
1644     sprintf( ptr, "CPE,Time(uSecs)" );
1645     printf( CSV, "%s\n", csv_header );
1646 }
1647
1648
1649 void CvTS::print_summary_tailer( int streams )
1650 {
1651     printf( streams, "=================================================\n");
1652     if( selected_tests && failed_tests )
1653     {
1654         time_t end_time;
1655         time( &end_time );
1656         double total_time = difftime( end_time, start_time );
1657         printf( streams, "Summary: %d out of %d tests failed\n",
1658             failed_tests->size(), selected_tests->size() );
1659         int minutes = cvFloor(total_time/60.);
1660         int seconds = cvRound(total_time - minutes*60);
1661         int hours = minutes / 60;
1662         minutes %= 60;
1663         printf( streams, "Running time: %02d:%02d:%02d\n", hours, minutes, seconds );
1664     }
1665 }
1666
1667
1668 void CvTS::vprintf( int streams, const char* fmt, va_list l )
1669 {
1670     if( streams )
1671     {
1672         char str[1 << 14];
1673         vsprintf( str, fmt, l );
1674
1675         for( int i = 0; i < MAX_IDX; i++ )
1676         {
1677             if( (streams & (1 << i)) && output_streams[i].enable )
1678             {
1679                 FILE* f = i == CONSOLE_IDX ? stdout :
1680                           i == LOG_IDX ? stderr : output_streams[i].f;
1681                 if( f )
1682                 {
1683                     if( i != CSV_IDX && !(ostream_testname_mask & (1 << i)) && current_test_info.test )
1684                     {
1685                         fprintf( f, "-------------------------------------------------\n" );
1686                         fprintf( f, "%s: ", current_test_info.test->get_name() );
1687                         fflush( f );
1688                         ostream_testname_mask |= 1 << i;
1689                     }
1690                     fputs( str, f );
1691                     if( i == CONSOLE_IDX )
1692                         fflush(f);
1693                 }
1694             }
1695         }
1696     }
1697 }
1698
1699
1700 void CvTS::printf( int streams, const char* fmt, ... )
1701 {
1702     if( streams )
1703     {
1704         va_list l;
1705         va_start( l, fmt );
1706         vprintf( streams, fmt, l );
1707         va_end( l );
1708     }
1709 }
1710
1711 void CvTS::set_color(int color)
1712 {
1713     if( params.color_terminal )
1714         change_color(color);
1715 }
1716
1717 static char* cv_strnstr( const char* str, int len,
1718                          const char* pattern,
1719                          int pattern_len = -1,
1720                          int whole_word = 1 )
1721 {
1722     int i;
1723
1724     if( len < 0 && pattern_len < 0 )
1725         return (char*)strstr( str, pattern );
1726
1727     if( len < 0 )
1728         len = (int)strlen( str );
1729
1730     if( pattern_len < 0 )
1731         pattern_len = (int)strlen( pattern );
1732
1733     for( i = 0; i < len - pattern_len + 1; i++ )
1734     {
1735         int j = i + pattern_len;
1736         if( str[i] == pattern[0] &&
1737             memcmp( str + i, pattern, pattern_len ) == 0 &&
1738             (!whole_word ||
1739             ((i == 0 || (!isalnum(str[i-1]) && str[i-1] != '_')) &&
1740              (j == len || (!isalnum(str[j]) && str[j] != '_')))))
1741             return (char*)(str + i);
1742     }
1743
1744     return 0;
1745 }
1746
1747
1748 int CvTS::filter( CvTest* test )
1749 {
1750     const char* pattern = params.test_filter_pattern;
1751     int inverse = 0;
1752
1753     if( pattern && pattern[0] == '!' )
1754     {
1755         inverse = 1;
1756         pattern++;
1757     }
1758
1759     if( !pattern || strcmp( pattern, "" ) == 0 || strcmp( pattern, "*" ) == 0 )
1760         return 1 ^ inverse;
1761
1762     if( params.test_filter_mode == CHOOSE_TESTS )
1763     {
1764         int found = 0;
1765
1766         while( pattern && *pattern )
1767         {
1768             char *ptr, *endptr = (char*)strchr( pattern, ',' );
1769             int len, have_wildcard;
1770             int t_name_len;
1771
1772             if( endptr )
1773                 *endptr = '\0';
1774
1775             ptr = (char*)strchr( pattern, '*' );
1776             if( ptr )
1777             {
1778                 len = (int)(ptr - pattern);
1779                 have_wildcard = 1;
1780             }
1781             else
1782             {
1783                 len = (int)strlen( pattern );
1784                 have_wildcard = 0;
1785             }
1786
1787             t_name_len = (int)strlen( test->get_name() );
1788             found = (t_name_len == len || (have_wildcard && t_name_len > len)) &&
1789                     (len == 0 || memcmp( test->get_name(), pattern, len ) == 0);
1790             if( endptr )
1791             {
1792                 *endptr = ',';
1793                 pattern = endptr + 1;
1794                 while( isspace(*pattern) )
1795                     pattern++;
1796             }
1797
1798             if( found || !endptr )
1799                 break;
1800         }
1801
1802         return found ^ inverse;
1803     }
1804     else
1805     {
1806         assert( params.test_filter_mode == CHOOSE_FUNCTIONS );
1807         int glob_len = (int)strlen( pattern );
1808         const char* ptr = test->get_func_list();
1809         const char *tmp_ptr;
1810
1811         while( ptr && *ptr )
1812         {
1813             const char* endptr = ptr - 1;
1814             const char* name_ptr;
1815             const char* name_first_match;
1816             int name_len;
1817             char c;
1818
1819             do c = *++endptr;
1820             while( isspace(c) );
1821
1822             if( !c )
1823                 break;
1824
1825             assert( isalpha(c) );
1826             name_ptr = endptr;
1827
1828             do c = *++endptr;
1829             while( isalnum(c) || c == '_' );
1830
1831             if( c == ':' ) // class
1832             {
1833                 assert( endptr[1] == ':' );
1834                 endptr = endptr + 2;
1835                 name_len = (int)(endptr - name_ptr);
1836
1837                 // find the first occurence of the class name
1838                 // in pattern
1839                 name_first_match = cv_strnstr( pattern,
1840                                       glob_len, name_ptr, name_len, 1 );
1841
1842                 if( *endptr == '*' )
1843                 {
1844                     if( name_first_match )
1845                         return 1 ^ inverse;
1846                 }
1847                 else
1848                 {
1849                     assert( *endptr == '{' ); // a list of methods
1850
1851                     if( !name_first_match )
1852                     {
1853                         // skip all the methods, if there is no such a class name
1854                         // in pattern
1855                         endptr = strchr( endptr, '}' );
1856                         assert( endptr != 0 );
1857                         endptr--;
1858                     }
1859
1860                     for( ;; )
1861                     {
1862                         const char* method_name_ptr;
1863                         int method_name_len;
1864
1865                         do c = *++endptr;
1866                         while( isspace(c) );
1867
1868                         if( c == '}' )
1869                             break;
1870                         assert( isalpha(c) );
1871
1872                         method_name_ptr = endptr;
1873
1874                         do c = *++endptr;
1875                         while( isalnum(c) || c == '_' );
1876
1877                         method_name_len = (int)(endptr - method_name_ptr);
1878
1879                         // search for class_name::* or
1880                         // class_name::{...method_name...}
1881                         tmp_ptr = name_first_match;
1882                         do
1883                         {
1884                             const char* tmp_ptr2;
1885                             tmp_ptr += name_len;
1886                             if( *tmp_ptr == '*' )
1887                                 return 1;
1888                             assert( *tmp_ptr == '{' );
1889                             tmp_ptr2 = strchr( tmp_ptr, '}' );
1890                             assert( tmp_ptr2 );
1891
1892                             if( cv_strnstr( tmp_ptr, (int)(tmp_ptr2 - tmp_ptr) + 1,
1893                                              method_name_ptr, method_name_len, 1 ))
1894                                 return 1 ^ inverse;
1895
1896                             tmp_ptr = cv_strnstr( tmp_ptr2, glob_len -
1897                                                    (int)(tmp_ptr2 - pattern),
1898                                                    name_ptr, name_len, 1 );
1899                         }
1900                         while( tmp_ptr );
1901
1902                         endptr--;
1903                         do c = *++endptr;
1904                         while( isspace(c) );
1905
1906                         if( c != ',' )
1907                             endptr--;
1908                     }
1909                 }
1910             }
1911             else
1912             {
1913                 assert( !c || isspace(c) || c == ',' );
1914                 name_len = (int)(endptr - name_ptr);
1915                 tmp_ptr = pattern;
1916
1917                 for(;;)
1918                 {
1919                     const char *tmp_ptr2, *tmp_ptr3;
1920
1921                     tmp_ptr = cv_strnstr( tmp_ptr, glob_len -
1922                         (int)(tmp_ptr - pattern), name_ptr, name_len, 1 );
1923
1924                     if( !tmp_ptr )
1925                         break;
1926
1927                     // make sure it is not a method
1928                     tmp_ptr2 = strchr( tmp_ptr, '}' );
1929                     if( !tmp_ptr2 )
1930                         return 1 ^ inverse;
1931
1932                     tmp_ptr3 = strchr( tmp_ptr, '{' );
1933                     if( tmp_ptr3 < tmp_ptr2 )
1934                         return 1 ^ inverse;
1935
1936                     tmp_ptr = tmp_ptr2 + 1;
1937                 }
1938
1939                 endptr--;
1940             }
1941
1942             do c = *++endptr;
1943             while( isspace(c) );
1944
1945             if( c == ',' )
1946                 endptr++;
1947             ptr = endptr;
1948         }
1949
1950         return 0 ^ inverse;
1951     }
1952 }
1953
1954 /* End of file. */