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

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