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

unittest.hxx VIGRA

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)