vigra - vigra/unittest.hxx Source File

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