[ VIGRA Homepage | Function Index | Class Index | Namespaces | File List | Main Page ]

unittest.hxx |

1 /************************************************************************/
2 /* */
3 /* a simple unit test framework, similar to Kent Beck's JUnit */
4 /* */
5 /* Copyright 2002-2004 by Ullrich Koethe */
6 /* */
7 /* This file is part of the VIGRA computer vision library. */
8 /* The VIGRA Website is */
9 /* http://hci.iwr.uni-heidelberg.de/vigra/ */
10 /* Please direct questions, bug reports, and contributions to */
11 /* ullrich.koethe@iwr.uni-heidelberg.de or */
12 /* vigra@informatik.uni-hamburg.de */
13 /* */
14 /* Permission is hereby granted, free of charge, to any person */
15 /* obtaining a copy of this software and associated documentation */
16 /* files (the "Software"), to deal in the Software without */
17 /* restriction, including without limitation the rights to use, */
18 /* copy, modify, merge, publish, distribute, sublicense, and/or */
19 /* sell copies of the Software, and to permit persons to whom the */
20 /* Software is furnished to do so, subject to the following */
21 /* conditions: */
22 /* */
23 /* The above copyright notice and this permission notice shall be */
24 /* included in all copies or substantial portions of the */
25 /* Software. */
26 /* */
27 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
28 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
29 /* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
30 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
31 /* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
32 /* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
33 /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
34 /* OTHER DEALINGS IN THE SOFTWARE. */
35 /* */
36 /************************************************************************/
37
38 #ifndef VIGRA_UNIT_TEST_HPP
39 #define VIGRA_UNIT_TEST_HPP
40
41 #include <vector>
42 #include <string>
43 #include <new> // for bad_alloc
44 #include <typeinfo> // for bad_cast, bad_typeid
45 #include <exception> // for exception, bad_exception
46 #include <stdexcept>
47 #include <iostream>
48 #include <limits>
49 #include <cfloat>
50 #include <cmath>
51 #include "vigra/config.hxx"
52 #include "vigra/error.hxx"
53
54 #ifdef VIGRA_NO_WORKING_STRINGSTREAM
55 #include <strstream>
56 #define VIGRA_SSTREAM std::strstream
57 #define VIGRA_SSTREAM_STR(s) ((s << char()), std::string(s.str()))
58 #else
59 #include <sstream>
60 #define VIGRA_SSTREAM std::basic_stringstream<char>
61 #define VIGRA_SSTREAM_STR(s) s.str()
62 #endif
63
64
65 #ifdef _MSC_VER
66
67 #include <wtypes.h>
68 #include <winbase.h>
69 #include <excpt.h>
70
71 #ifdef min
72 #undef min
73 #endif
74 #ifdef max
75 #undef max
76 #endif
77 #ifdef DIFFERENCE
78 #undef DIFFERENCE
79 #endif
80 #ifdef RGB
81 #undef RGB
82 #endif
83
84 #elif defined(__CYGWIN__)
85
86 #define VIGRA_CANT_CATCH_SIGNALS
87
88 #elif defined(__unix) || defined(unix)
89
90 #include <unistd.h>
91 #include <signal.h>
92 #include <sys/signal.h>
93 #include <setjmp.h>
94
95 #else
96
97 #define VIGRA_CANT_CATCH_SIGNALS
98
99 #endif
100
101 #define VIGRA_TEST_CASE(function) vigra::create_test_case(function, #function "()")
102
103 #define testCase VIGRA_TEST_CASE
104
105 #define VIGRA_TEST_SUITE(testsuite) ( new testsuite )
106
107 #define VIGRA_CHECKPOINT(message) \
108 vigra::detail::checkpoint_impl(message, __FILE__, __LINE__)
109
110 #define VIGRA_ASSERT(predicate) \
111 vigra::detail::should_impl((predicate), #predicate, __FILE__, __LINE__)
112
113 #define should VIGRA_ASSERT
114
115 #define VIGRA_ASSERT_MESSAGE(predicate, message) \
116 vigra::detail::should_impl((predicate), message, __FILE__, __LINE__)
117
118 #define shouldMsg VIGRA_ASSERT_MESSAGE
119
120 #define shouldEqual(left, right) \
121 vigra::detail::equal_impl(left, right, #left " == " #right, __FILE__, __LINE__)
122
123 #define shouldEqualMessage(left, right, message) \
124 vigra::detail::equal_impl(left, right, message "\n" #left " == " #right, __FILE__, __LINE__)
125
126 #define shouldEqualTolerance(left, right, eps) \
127 vigra::detail::tolerance_equal_impl(left, right, eps, #left " == " #right, __FILE__, __LINE__)
128
129 #define shouldEqualToleranceMessage(left, right, eps, message) \
130 vigra::detail::tolerance_equal_impl(left, right, eps, message "\n" #left " == " #right, __FILE__, __LINE__)
131
132 #define shouldEqualSequence(begin1, end1, begin2) \
133 vigra::detail::sequence_equal_impl(begin1, end1, begin2, __FILE__, __LINE__)
134
135 #define shouldEqualSequenceTolerance(begin1, end1, begin2, eps) \
136 vigra::detail::sequence_equal_tolerance_impl(begin1, end1, begin2, eps, __FILE__, __LINE__)
137
138 #define VIGRA_ERROR(message) \
139 vigra::detail::should_impl(false, message, __FILE__, __LINE__)
140
141 #define failTest VIGRA_ERROR
142
143 namespace vigra {
144
145 class test_suite;
146
147 namespace detail {
148
149 struct errstream
150 {
151 VIGRA_SSTREAM buf;
152 std::string str() { return VIGRA_SSTREAM_STR(buf); }
153 template <class T>
154 errstream & operator<<(T t) { buf << t; return *this; }
155 };
156
157 inline std::string & exception_checkpoint()
158 {
159 static std::string test_checkpoint_;
160 return test_checkpoint_;
161 }
162
163 // A separate reporting function was requested during formal review.
164 inline void report_exception( detail::errstream & os,
165 const char * name, const char * info )
166 {
167 os << "Unexpected " << name << " " << info << "\n";
168 if(exception_checkpoint().size() > 0)
169 {
170 os << "Last checkpoint: " << exception_checkpoint() << "\n";
171 }
172 }
173
174 enum {
175 unexpected_exception = -1,
176 os_exception = -2,
177 memory_access_violation = -3,
178 destructor_failure = -4
179 };
180
181 inline bool critical_error(int i)
182 { return i <= memory_access_violation; }
183
184 inline bool unexpected_error(int i)
185 { return i < 0; }
186
187 #ifndef VIGRA_CANT_CATCH_SIGNALS
188
189 #ifdef _MSC_VER
190
191 inline long handle_signal_here(long code)
192 {
193 switch (code)
194 {
195 case EXCEPTION_ACCESS_VIOLATION:
196 case EXCEPTION_INT_DIVIDE_BY_ZERO:
197 return EXCEPTION_EXECUTE_HANDLER;
198 default:
199 return EXCEPTION_CONTINUE_SEARCH;
200 }
201 }
202
203 template< class Generator > // Generator is function object returning int
204 int catch_signals( Generator function_object, detail::errstream & err, int timeout )
205 {
206 int result = 0;
207 int code;
208 __try
209 {
210 result = function_object();
211 }
212 __except (handle_signal_here(code = GetExceptionCode()))
213 {
214 switch (code)
215 {
216 case EXCEPTION_ACCESS_VIOLATION:
217 report_exception(err, "operating system exception:", "memory access violation");
218 result = memory_access_violation;
219 break;
220 case EXCEPTION_INT_DIVIDE_BY_ZERO:
221 report_exception(err, "operating system exception:", "integer divide by zero");
222 result = os_exception;
223 break;
224 default:
225 report_exception(err, "operating system exception:", "unrecognized exception or signal");
226 result = os_exception;
227 }
228 }
229 return result;
230
231 }
232
233
234 #elif defined(__unix)
235
236 extern "C" {
237
238 inline jmp_buf & unit_test_jump_buffer()
239 {
240 static jmp_buf unit_test_jump_buffer_;
241 return unit_test_jump_buffer_;
242 }
243
244 static void unit_test_signal_handler(int sig)
245 {
246 longjmp(unit_test_jump_buffer(), sig);
247 }
248
249 } // extern "C"
250
251 template< class Generator > // Generator is function object returning int
252 int catch_signals( Generator function_object, detail::errstream & err, int timeout)
253 {
254 volatile int sigtype;
255 int result;
256
257 #if defined(linux) || defined(__linux)
258 signal(SIGFPE, &unit_test_signal_handler);
259 signal(SIGTRAP, &unit_test_signal_handler);
260 signal(SIGSEGV, &unit_test_signal_handler);
261 signal(SIGBUS, &unit_test_signal_handler);
262 #else
263 sigset(SIGFPE, &unit_test_signal_handler);
264 sigset(SIGTRAP, &unit_test_signal_handler);
265 sigset(SIGSEGV, &unit_test_signal_handler);
266 sigset(SIGBUS, &unit_test_signal_handler);
267 #endif
268
269 if(timeout)
270 {
271 #if defined(linux) || defined(__linux)
272 signal(SIGALRM, &unit_test_signal_handler);
273 #else
274 sigset(SIGALRM, &unit_test_signal_handler);
275 #endif
276 alarm(timeout);
277 }
278
279 sigtype = setjmp(unit_test_jump_buffer());
280 if(sigtype == 0)
281 {
282 result = function_object();
283 }
284 else
285 {
286 switch(sigtype)
287 {
288 case SIGALRM:
289 report_exception(err, "signal:", "SIGALRM (timeout while executing function)");
290 result = os_exception;
291 break;
292 case SIGTRAP:
293 report_exception(err, "signal:", "SIGTRAP (perhaps integer divide by zero)");
294 result = os_exception;
295 break;
296 case SIGFPE:
297 report_exception(err, "signal:", "SIGFPE (arithmetic exception)");
298 result = os_exception;
299 break;
300 case SIGSEGV:
301 case SIGBUS:
302 report_exception(err, "signal:", "memory access violation");
303 result = memory_access_violation;
304 break;
305 default:
306 report_exception(err, "signal:", "unrecognized signal");
307 result = os_exception;
308 }
309 }
310
311 if(timeout)
312 {
313 alarm(0);
314 #if defined(linux) || defined(__linux)
315 #else
316 sigrelse(SIGALRM);
317 #endif
318 }
319
320 #if defined(linux) || defined(__linux)
321 #else
322 sigrelse(SIGFPE);
323 sigrelse(SIGTRAP);
324 sigrelse(SIGSEGV);
325 sigrelse(SIGBUS);
326 #endif
327
328 return result;
329 }
330
331 #endif /* _MSC_VER || __unix */
332
333 #else /* VIGRA_CANT_CATCH_SIGNALS */
334
335 template< class Generator > // Generator is function object returning int
336 int catch_signals( Generator function_object, detail::errstream & err , int)
337 {
338 return function_object();
339 }
340
341 #endif /* VIGRA_CANT_CATCH_SIGNALS */
342
343 } // namespace detail
344
345 template< class Generator > // Generator is function object returning int
346 int catch_exceptions( Generator function_object, detail::errstream & err, int timeout )
347 {
348 int result = detail::unexpected_exception;
349
350 try
351 {
352 result = detail::catch_signals(function_object, err, timeout);
353 }
354
355 // As a result of hard experience with strangely interleaved output
356 // under some compilers, there is a lot of use of endl in the code below
357 // where a simple '\n' might appear to do.
358
359 // The rules for catch & arguments are a bit different from function
360 // arguments (ISO 15.3 paragraphs 18 & 19). Apparently const isn't
361 // required, but it doesn't hurt and some programmers ask for it.
362
363 catch ( vigra::ContractViolation & ex )
364 { detail::report_exception( err, "Contract exception: ", ex.what() ); }
365 catch ( const char * ex )
366 { detail::report_exception( err, "string exception: ", ex ); }
367 catch ( const std::string & ex )
368 { detail::report_exception( err, "string exception: ", ex.c_str() ); }
369
370 // std:: exceptions
371 catch ( const std::bad_alloc & ex )
372 { detail::report_exception( err, "exception: std::bad_alloc:", ex.what() ); }
373
374 # if !defined(__BORLANDC__) || __BORLANDC__ > 0x0551
375 catch ( const std::bad_cast & ex )
376 { detail::report_exception( err, "exception: std::bad_cast:", ex.what() ); }
377 catch ( const std::bad_typeid & ex )
378 { detail::report_exception( err, "exception: std::bad_typeid:", ex.what() ); }
379 # else
380 catch ( const std::bad_cast & ex )
381 { detail::report_exception( err, "exception: std::bad_cast", "" ); }
382 catch ( const std::bad_typeid & ex )
383 { detail::report_exception( err, "exception: std::bad_typeid", "" ); }
384 # endif
385
386 catch ( const std::bad_exception & ex )
387 { detail::report_exception( err, "exception: std::bad_exception:", ex.what() ); }
388 catch ( const std::domain_error & ex )
389 { detail::report_exception( err, "exception: std::domain_error:", ex.what() ); }
390 catch ( const std::invalid_argument & ex )
391 { detail::report_exception( err, "exception: std::invalid_argument:", ex.what() ); }
392 catch ( const std::length_error & ex )
393 { detail::report_exception( err, "exception: std::length_error:", ex.what() ); }
394 catch ( const std::out_of_range & ex )
395 { detail::report_exception( err, "exception: std::out_of_range:", ex.what() ); }
396 catch ( const std::range_error & ex )
397 { detail::report_exception( err, "exception: std::range_error:", ex.what() ); }
398 catch ( const std::overflow_error & ex )
399 { detail::report_exception( err, "exception: std::overflow_error:", ex.what() ); }
400 catch ( const std::underflow_error & ex )
401 { detail::report_exception( err, "exception: std::underflow_error:", ex.what() ); }
402 catch ( const std::logic_error & ex )
403 { detail::report_exception( err, "exception: std::logic_error:", ex.what() ); }
404 catch ( const std::runtime_error & ex )
405 { detail::report_exception( err, "exception: std::runtime_error:", ex.what() ); }
406 catch ( const std::exception & ex )
407 { detail::report_exception( err, "exception: std::exception:", ex.what() ); }
408
409 catch ( ... )
410 {
411 detail::report_exception( err, "unknown exception", "" );
412 throw;
413 }
414
415 return result;
416 } // catch_exceptions
417
418 template< class Generator > // Generator is function object returning int
419 inline
420 int catch_exceptions( Generator function_object, detail::errstream & err)
421 {
422 return catch_exceptions(function_object, err, 0);
423 }
424
425 namespace detail {
426
427 struct unit_test_failed
428 : public std::exception
429 {
430 unit_test_failed(std::string const & message)
431 : what_(message)
432 {}
433
434 virtual ~unit_test_failed() throw()
435 {
436 }
437
438 virtual const char * what() const throw()
439 {
440 return what_.c_str();
441 }
442
443 std::string what_;
444 };
445
446 inline void
447 checkpoint_impl(const char * message, const char * file, int line)
448 {
449 detail::errstream buf;
450 buf << message << " (" << file <<":" << line << ")";
451 exception_checkpoint() = buf.str();
452 }
453
454 inline void
455 should_impl(bool predicate, const char * message, const char * file, int line)
456 {
457 checkpoint_impl(message, file, line);
458 if(!predicate)
459 {
460 detail::errstream buf;
461 buf << message << " (" << file <<":" << line << ")";
462 throw unit_test_failed(buf.str());
463 }
464 }
465
466 template <class Iter1, class Iter2>
467 void
468 sequence_equal_impl(Iter1 i1, Iter1 end1, Iter2 i2, const char * file, int line)
469 {
470 for(int counter = 0; i1 != end1; ++i1, ++i2, ++counter)
471 {
472 if(*i1 != *i2)
473 {
474 detail::errstream buf;
475 buf << "Sequence items differ at index " << counter <<
476 " ["<< *i1 << " != " << *i2 << "]";
477 should_impl(false, buf.str().c_str(), file, line);
478 }
479 }
480 }
481
482 /******************Floating point comparison********************************/
483 /**
484 * See Knuth "The art of computer programming" (Vol II, Ch.4.2)
485 */
486 struct ScalarType {};
487 struct VectorType {};
488
489 template<class T>
490 struct FloatTraits
491 {
492 typedef VectorType ScalarOrVector;
493 };
494
495 template<>
496 struct FloatTraits<float>
497 {
498 typedef ScalarType ScalarOrVector;
499 static float epsilon() { return FLT_EPSILON; }
500 static float smallestPositive() { return FLT_MIN; }
501 static float min() { return -FLT_MAX; }
502 static float max() { return FLT_MAX; }
503 };
504
505 template<>
506 struct FloatTraits<double>
507 {
508 typedef ScalarType ScalarOrVector;
509 static double epsilon() { return DBL_EPSILON; }
510 static double smallestPositive() { return DBL_MIN; }
511 static double min() { return -DBL_MAX; }
512 static double max() { return DBL_MAX; }
513 };
514
515 template<>
516 struct FloatTraits<long double>
517 {
518 typedef ScalarType ScalarOrVector;
519 static long double epsilon() { return LDBL_EPSILON; }
520 static long double smallestPositive() { return LDBL_MIN; }
521 static long double min() { return -LDBL_MAX; }
522 static long double max() { return LDBL_MAX; }
523 };
524
525 template<class FPT>
526 inline
527 FPT fpt_abs( FPT arg )
528 {
529 return arg < 0 ? -arg : arg;
530 }
531
532
533 /***********************************************************************/
534
535 // both f1 and f2 are unsigned here
536 template<class FPT>
537 inline
538 FPT safe_fpt_division( FPT f1, FPT f2 )
539 {
540 /* ist f1 das absolute minimum (in diesem Fall einfach nur sehr kleine Zahl)
541 * aber nicht null (1.65242e-28) und f2 = 0,
542 * dann tritt die erste Bedingung in Kraft 0<1 && 1.65242e-28 > 0*1.79769e+308 (max)
543 * deshalb schlaegt es fehl sogar wenn min closed at tolarance zu 0 ist ???
544 * Der Vergleich aller Zahlen closed at tolarance zu 0 wuerden fehlschlagen;
545 * Sie umzudrehen bringt nichts, denn diese Funktion wird symetrisch fuer beide
546 * angewendet wird.
547 * 0 mit 0 zu Vergleichen bereitet keine Probleme.
548 * Ausweg: evl. eine extra Behandlung der F = 0 ???
549 */
550 return ((f2 < 1) && (f1 > (f2 * FloatTraits<FPT>::max()))) ?
551 FloatTraits<FPT>::max() :
552 ((((f2 > 1) && (f1 < (f2 * FloatTraits<FPT>::smallestPositive())))
553 || (f1 == 0)) ? 0 : f1/f2 );
554 /* Die Multiplikation mit max in 1.ten Bedingung und mit min in der 2.ten ist eine Absicherung gegen
555 * die Owerflow bzw Underflow ???
556 */
557 }
558
559 /***********************************************************************/
560
561 template<class FPT>
562 class close_at_tolerance {
563 public:
564 explicit close_at_tolerance( FPT tolerance, bool strong_test = true )
565 : m_strong_test( strong_test ),
566 m_tolerance( tolerance ) {}
567
568 explicit close_at_tolerance( int number_of_rounding_errors, bool strong_test = true )
569 : m_strong_test( strong_test ),
570 m_tolerance( FloatTraits<FPT>::epsilon() * number_of_rounding_errors / 2.0 ) {}
571
572 bool operator()( FPT left, FPT right ) const
573 {
574 if (left == 0 && right != 0)
575 {
576 return (fpt_abs(right) <= m_tolerance);
577 }
578 if (right == 0 && left != 0)
579 {
580 return (fpt_abs(left) <= m_tolerance);
581 }
582 FPT diff = fpt_abs( left - right );
583 FPT d1 = safe_fpt_division( diff, fpt_abs( right ) );
584 FPT d2 = safe_fpt_division( diff, fpt_abs( left ) );
585
586 return m_strong_test ? (d1 <= m_tolerance && d2 <= m_tolerance)
587 : (d1 <= m_tolerance || d2 <= m_tolerance);
588 }
589
590 private:
591 bool m_strong_test;
592 FPT m_tolerance;
593 };
594
595 /*****************end of float comparison***********************************/
596
597 template <class T1, class T2, class T3>
598 void
599 tolerance_equal_impl(T1 left, T2 right, T3 epsilon,
600 const char * message, const char * file, int line, ScalarType)
601 {
602 detail::errstream buf;
603 buf << message << " [" << left << " != " << right << "]";
604
605 close_at_tolerance<T3> fcomparator( epsilon );
606 bool compare = fcomparator ( (T3)left , (T3)right );
607 should_impl(compare, buf.str().c_str(), file, line);
608
609 }
610
611 template <class T1, class T2, class T3>
612 void
613 tolerance_equal_impl(T1 left, T2 right, T3 epsilon,
614 const char * message, const char * file, int line, VectorType)
615 {
616 detail::errstream buf;
617 buf << message << " [" << left << " != " << right << "]";
618
619 bool compare = true;
620 for(unsigned int i=0; i<epsilon.size(); ++i)
621 {
622 close_at_tolerance<typename T3::value_type> fcomparator( epsilon[i] );
623 compare = compare && fcomparator ( left[i] , right[i] );
624 }
625 should_impl(compare, buf.str().c_str(), file, line);
626 }
627
628 template <class T1, class T2, class T3>
629 void
630 tolerance_equal_impl(T1 left, T2 right, T3 epsilon, const char * message, const char * file, int line)
631 {
632 tolerance_equal_impl(left, right, epsilon,
633 message, file, line, typename FloatTraits<T3>::ScalarOrVector());
634 }
635
636 template <class Iter1, class Iter2, class T>
637 void
638 sequence_equal_tolerance_impl(Iter1 i1, Iter1 end1, Iter2 i2, T epsilon, const char * file, int line)
639 {
640 for(int counter = 0; i1 != end1; ++i1, ++i2, ++counter)
641 {
642 detail::errstream buf;
643 buf << "Sequence items differ at index " << counter;
644 tolerance_equal_impl(*i1, *i2, epsilon, buf.str().c_str(), file, line, typename FloatTraits<T>::ScalarOrVector());
645 }
646 }
647
648 template <class Left, class Right>
649 void
650 equal_impl(Left left, Right right, const char * message, const char * file, int line)
651 {
652 detail::errstream buf;
653 buf << message << " [" << left << " != " << right << "]";
654 should_impl(left == right, buf.str().c_str(), file, line);
655 }
656
657 template <class Left, class Right>
658 void
659 equal_impl(Left * left, Right * right, const char * message, const char * file, int line)
660 {
661 detail::errstream buf;
662 buf << message << " [" << (void*)left << " != " << (void*)right << "]";
663 should_impl(left == right, buf.str().c_str(), file, line);
664 }
665
666 inline void
667 equal_impl(double left, double right, const char * message, const char * file, int line)
668 {
669 tolerance_equal_impl(left, right, 1.0e-16, message, file, line);
670 }
671
672 inline void
673 equal_impl(float left, float right, const char * message, const char * file, int line)
674 {
675 tolerance_equal_impl(left, right, 1.0e-6f, message, file, line);
676 }
677
678 inline void
679 equal_impl(float left, double right, const char * message, const char * file, int line)
680 {
681 tolerance_equal_impl(left, right, 1.0e-6f, message, file, line);
682 }
683
684 inline void
685 equal_impl(double left, float right, const char * message, const char * file, int line)
686 {
687 tolerance_equal_impl(left, right, 1.0e-6f, message, file, line);
688 }
689
690 class test_case
691 {
692 public:
693
694 test_case(char const * name = "Unnamed")
695 : name_(name), timeout(0)
696 {}
697
698 virtual ~test_case() {}
699
700 virtual int run() { return run(std::vector<std::string>()); }
701 virtual int run(std::vector<std::string> const & testsToBeRun) = 0;
702 virtual void do_init() {}
703 virtual void do_run() {}
704 virtual void do_destroy() {}
705
706 virtual char const * name() { return name_.c_str(); }
707 virtual int size() const { return 1; }
708
709 virtual int numberOfTestsToRun(std::vector<std::string> const & testsToBeRun) const
710 {
711 if(testsToBeRun.empty()) // empty list => run all tests
712 return 1;
713 for(unsigned int k=0; k<testsToBeRun.size(); ++k)
714 if(this->name_.find(testsToBeRun[k]) != std::string::npos)
715 return 1;
716 return 0;
717 }
718
719 std::string name_;
720 std::string report_;
721 int timeout;
722 };
723
724
725 } // namespace detail
726
727 std::vector<std::string> testsToBeExecuted(int argc, char ** argv)
728 {
729 std::vector<std::string> res;
730 for(int i=1; i < argc; ++i)
731 res.push_back(std::string(argv[i]));
732 return res;
733 }
734
735 class test_suite
736 : public detail::test_case
737 {
738 public:
739 using detail::test_case::run;
740
741 test_suite(char const * name = "TopLevel")
742 : detail::test_case(name),
743 size_(0)
744 {}
745
746 virtual ~test_suite()
747 {
748 for(unsigned int i=0; i != testcases_.size(); ++i)
749 delete testcases_[i];
750 }
751
752 virtual void add(detail::test_case * t, int timeout = 0)
753 {
754 t->timeout = timeout;
755 testcases_.push_back(t);
756 size_ += t->size();
757 }
758
759 virtual int run(std::vector<std::string> const & testsToBeRun)
760 {
761 int size = numberOfTestsToRun(testsToBeRun);
762
763 std::vector<std::string> testsToBeRunRecursive =
764 size < this->size()
765 ? testsToBeRun // run selectively
766 : std::vector<std::string>(); // run all
767
768 int failed = 0;
769 report_ = std::string("Entering test suite ") + name() + "\n";
770
771 for(unsigned int i=0; i != testcases_.size(); ++i)
772 {
773 int result = testcases_[i]->run(testsToBeRunRecursive);
774 report_ += testcases_[i]->report_;
775
776 if(detail::critical_error(result))
777 {
778 report_ += std::string("\nFatal error - aborting test suite ") + name() + ".\n";
779 return result;
780 }
781 else if(detail::unexpected_error(result))
782 failed++;
783 else
784 failed += result;
785 }
786
787 if(failed)
788 {
789 detail::errstream buf;
790 buf << "\n" << failed << " of " << size <<
791 " tests failed in test suite " << name() << "\n";
792 report_ += buf.str();
793 }
794 else
795 {
796 detail::errstream buf;
797 buf << "All (" << size <<
798 ") tests passed in test suite " << name() << "\n";
799 report_ += buf.str();
800 }
801
802 report_ += std::string("Leaving test suite ") + name() + "\n";
803
804 return failed;
805 }
806
807 virtual int numberOfTestsToRun(std::vector<std::string> const & testsToBeRun) const
808 {
809 if(detail::test_case::numberOfTestsToRun(testsToBeRun) > 0)
810 return this->size();
811 int size = 0;
812 for(unsigned int i=0; i != testcases_.size(); ++i)
813 size += testcases_[i]->numberOfTestsToRun(testsToBeRun);
814 return size;
815 }
816
817 virtual int size() const { return size_; }
818 virtual std::string report() { return report_; }
819
820 std::vector<detail::test_case *> testcases_;
821 int size_;
822 };
823
824 namespace detail {
825
826 struct test_case_init_functor
827 {
828 detail::errstream & buf_;
829 test_case * test_case_;
830
831 test_case_init_functor(detail::errstream & b, test_case * tc)
832 : buf_(b), test_case_(tc)
833 {}
834
835 int operator()()
836 {
837 try
838 {
839 test_case_->do_init();
840 return 0;
841 }
842 catch(unit_test_failed & e)
843 {
844 buf_ << "Assertion failed: " << e.what() << "\n";
845 return 1;
846 }
847 }
848 };
849
850 struct test_case_run_functor
851 {
852 detail::errstream & buf_;
853 test_case * test_case_;
854
855 test_case_run_functor(detail::errstream & b, test_case * tc)
856 : buf_(b), test_case_(tc)
857 {}
858
859 int operator()()
860 {
861 try
862 {
863 test_case_->do_run();
864 return 0;
865 }
866 catch(unit_test_failed & e)
867 {
868 buf_ << "Assertion failed: " << e.what() << "\n";
869 return 1;
870 }
871 }
872 };
873
874 struct test_case_destroy_functor
875 {
876 detail::errstream & buf_;
877 test_case * test_case_;
878
879 test_case_destroy_functor(detail::errstream & b, test_case * tc)
880 : buf_(b), test_case_(tc)
881 {}
882
883 int operator()()
884 {
885 try
886 {
887 test_case_->do_destroy();
888 return 0;
889 }
890 catch(unit_test_failed & e)
891 {
892 buf_ << "Assertion failed: " << e.what() << "\n";
893 return 1;
894 }
895 }
896 };
897
898 template <class TESTCASE>
899 class class_test_case
900 : public test_case
901 {
902 public:
903 using test_case::run;
904
905 class_test_case(void (TESTCASE::*fct)(), char const * name)
906 : test_case(name),
907 fct_(fct),
908 testcase_(0)
909 {}
910
911 virtual ~class_test_case()
912 {
913 delete testcase_;
914 }
915
916 virtual void do_init()
917 {
918 testcase_ = new TESTCASE;
919 }
920
921 int init()
922 {
923 exception_checkpoint() = "";
924 report_ = "";
925 int failed = 0;
926
927 detail::errstream buf;
928 buf << "\nFailure in initialization of " << name() << "\n";
929 if(testcase_ != 0)
930 {
931 buf << "Test case failed to clean up after previous run.\n";
932 failed = 1;
933 }
934 else
935 {
936 failed = catch_exceptions(
937 detail::test_case_init_functor(buf, this), buf, timeout);
938 }
939
940 if(failed)
941 {
942 report_ += buf.str();
943 }
944
945 return failed;
946 }
947
948 virtual void do_run()
949 {
950 if(testcase_ != 0)
951 (testcase_->*fct_)();
952 }
953
954 virtual int run(std::vector<std::string> const & testsToBeRun)
955 {
956 if(numberOfTestsToRun(testsToBeRun) == 0)
957 return 0;
958
959 int failed = init();
960
961 if(failed)
962 return failed;
963
964 detail::errstream buf;
965 buf << "\nFailure in " << name() << "\n";
966
967 failed = catch_exceptions(
968 detail::test_case_run_functor(buf, this), buf, timeout);
969 if(failed)
970 report_ += buf.str();
971
972 if(critical_error(failed))
973 return failed;
974
975 int destruction_failed = destroy();
976
977 return destruction_failed ?
978 destruction_failed :
979 failed;
980 }
981
982 virtual void do_destroy()
983 {
984 delete testcase_;
985 testcase_ = 0;
986 }
987
988 int destroy()
989 {
990 detail::errstream buf;
991 buf << "\nFailure in destruction of " << "\n";
992
993 int failed = catch_exceptions(
994 detail::test_case_destroy_functor(buf, this), buf, timeout);
995 if(failed)
996 {
997 report_ += buf.str();
998 return destructor_failure;
999 }
1000 else
1001 {
1002 return 0;
1003 }
1004 }
1005
1006 void (TESTCASE::*fct_)();
1007 TESTCASE * testcase_;
1008 };
1009
1010 class function_test_case
1011 : public test_case
1012 {
1013 public:
1014 using test_case::run;
1015
1016 function_test_case(void (*fct)(), char const * name)
1017 : test_case(name),
1018 fct_(fct)
1019 {}
1020
1021 virtual void do_run()
1022 {
1023 (*fct_)();
1024 }
1025
1026 virtual int run(std::vector<std::string> const & testsToBeRun)
1027 {
1028 if(numberOfTestsToRun(testsToBeRun) == 0)
1029 return 0;
1030
1031 report_ = "";
1032 exception_checkpoint() = "";
1033
1034 detail::errstream buf;
1035 buf << "\nFailure in " << name() << "\n";
1036
1037 int failed = catch_exceptions(
1038 detail::test_case_run_functor(buf, this), buf, timeout);
1039 if(failed)
1040 {
1041 report_ += buf.str();
1042 }
1043
1044 return failed;
1045 }
1046
1047 void (*fct_)();
1048 };
1049
1050 template <class FCT>
1051 struct test_functor
1052 {
1053 virtual ~test_functor() {}
1054 virtual void operator()() = 0;
1055
1056 FCT clone() const
1057 { return FCT(static_cast<FCT const &>(*this)); }
1058 };
1059
1060 template <class FCT>
1061 class functor_test_case
1062 : public test_case
1063 {
1064 public:
1065 using test_case::run;
1066
1067 functor_test_case(FCT const & fct, char const * name)
1068 : test_case(name),
1069 fct_(fct)
1070 {}
1071
1072 virtual void do_run()
1073 {
1074 fct_();
1075 }
1076
1077 virtual int run(std::vector<std::string> const & testsToBeRun)
1078 {
1079 if(numberOfTestsToRun(testsToBeRun) == 0)
1080 return 0;
1081
1082 report_ = "";
1083 exception_checkpoint() = "";
1084
1085 detail::errstream buf;
1086 buf << "\nFailure in " << name() << "\n";
1087
1088 int failed = catch_exceptions(
1089 detail::test_case_run_functor(buf, this), buf, timeout);
1090 if(failed)
1091 {
1092 report_ += buf.str();
1093 }
1094
1095 return failed;
1096 }
1097
1098 FCT fct_;
1099 };
1100
1101 } // namespace detail
1102
1103 template <class TESTCASE>
1104 inline
1105 detail::test_case *
1106 create_test_case(void (TESTCASE::*fct)(), char const * name)
1107 {
1108 if(*name == '&') ++name;
1109 return new detail::class_test_case<TESTCASE>(fct, name);
1110 }
1111
1112 inline
1113 detail::test_case *
1114 create_test_case(void (*fct)(), char const * name)
1115 {
1116 if(*name == '&') ++name;
1117 return new detail::function_test_case(fct, name);
1118 }
1119
1120 template <class FCT>
1121 inline
1122 detail::test_case *
1123 create_test_case(detail::test_functor<FCT> const & fct, char const * name)
1124 {
1125 if(*name == '&') ++name;
1126 return new detail::functor_test_case<FCT>(fct.clone(), name);
1127 }
1128
1129 } // namespace vigra
1130
1131
1132 #if !defined(__GNUC__) || __GNUC__ >= 3
1133
1134 // provide more convenient output functions, used like:
1135 // std::cerr << 1, 2, 3, 4, "\n";
1136 template <class E, class T, class V>
1137 inline
1138 std::basic_ostream<E,T> & operator,(std::basic_ostream<E,T> & o, V const & t)
1139 {
1140 return (o << ' ' << t);
1141 }
1142
1143 template <class E, class T>
1144 inline
1145 std::basic_ostream<E,T> & operator,(std::basic_ostream<E,T> & o,
1146 std::basic_ostream<E,T> & (*t)(std::basic_ostream<E,T> &))
1147 {
1148 return (o << t);
1149 }
1150
1151 #else
1152
1153 template <class V>
1154 inline
1155 std::ostream & operator,(std::ostream & o, V const & t)
1156 {
1157 return (o << ' ' << t);
1158 }
1159
1160 inline
1161 std::ostream & operator,(std::ostream & o,
1162 std::ostream & (*t)(std::ostream &))
1163 {
1164 return (o << t);
1165 }
1166
1167 #endif
1168
1169
1170 #endif /* VIGRA_UNIT_TEST_HPP */

© Ullrich Köthe (ullrich.koethe@iwr.uni-heidelberg.de) Heidelberg Collaboratory for Image Processing, University of Heidelberg, Germany | html generated using doxygen and Python vigra 1.10.0 (Mon Nov 18 2013)