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

© 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.11.0