[ 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
156namespace vigra {
157
158class test_suite;
159
160namespace detail {
161
162typedef std::pair<std::string, int> CheckpointType;
163
164struct 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
172inline 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.
179inline 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
189enum {
190 unexpected_exception = -1,
191 os_exception = -2,
192 memory_access_violation = -3,
193 destructor_failure = -4
194};
195
196inline bool critical_error(int i)
197{ return i <= memory_access_violation; }
198
199inline bool unexpected_error(int i)
200{ return i < 0; }
201
202#ifndef VIGRA_CANT_CATCH_SIGNALS
203
204#ifdef _MSC_VER
205
206inline 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
218template< class Generator > // Generator is function object returning int
219int 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
251inline jmp_buf & unit_test_jump_buffer()
252{
253 static jmp_buf unit_test_jump_buffer_;
254 return unit_test_jump_buffer_;
255}
256
257static void unit_test_signal_handler(int sig)
258{
259 longjmp(unit_test_jump_buffer(), sig);
260}
261
262template< class Generator > // Generator is function object returning int
263int 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
346template< class Generator > // Generator is function object returning int
347int catch_signals( Generator function_object, detail::errstream & , int)
348{
349 return function_object();
350}
351
352#endif /* VIGRA_CANT_CATCH_SIGNALS */
353
354} // namespace detail
355
356template< class Generator > // Generator is function object returning int
357int 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
429template< class Generator > // Generator is function object returning int
430inline
431int catch_exceptions( Generator function_object, detail::errstream & err)
432{
433 return catch_exceptions(function_object, err, 0);
434}
435
436namespace detail {
437
438struct 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
457inline void
458checkpoint_impl(const char * file, int line)
459{
460 exception_checkpoint().first = file;
461 exception_checkpoint().second = line;
462}
463
464inline void
465should_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
476inline void
477should_impl(bool predicate, std::string const & message, const char * file, int line)
478{
479 should_impl(predicate, message.c_str(), file, line);
480}
481
482template <class Iter1, class Iter2>
483void
484sequence_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*/
503struct ScalarType {};
504struct VectorType {};
505
506template<class T>
507struct FloatTraits
508{
509 typedef VectorType ScalarOrVector;
510};
511
512template<>
513struct 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
522template<>
523struct 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
532template<>
533struct 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
542template<class FPT>
543inline
544FPT fpt_abs( FPT arg )
545{
546 return arg < 0 ? -arg : arg;
547}
548
549
550/***********************************************************************/
551
552// both f1 and f2 are unsigned here
553template<class FPT>
554inline
555FPT 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
578template<class FPT>
579class close_at_tolerance {
580public:
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
607private:
608 bool m_strong_test;
609 FPT m_tolerance;
610};
611
612/*****************end of float comparison***********************************/
613
614template <class T1, class T2, class T3>
615void
616tolerance_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
631template <class T1, class T2, class T3>
632void
633tolerance_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
657template <class T1, class T2, class T3>
658void
659tolerance_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
665template <class Iter1, class Iter2, class T>
666void
667sequence_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
675template <class Left, class Right>
676void
677equal_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
688template <class Left, class Right>
689void
690equal_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
701inline void
702equal_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
707inline void
708equal_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
713inline void
714equal_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
719inline void
720equal_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
725class 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
762std::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
770class 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
859namespace detail {
860
861struct 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
885struct 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
909struct 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
933template <class TESTCASE>
934class 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
1045class 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
1085template <class FCT>
1086struct 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
1095template <class FCT>
1096class 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
1138template <class TESTCASE>
1139inline
1140detail::test_case *
1141create_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
1147inline
1148detail::test_case *
1149create_test_case(void (*fct)(), char const * name)
1150{
1151 if(*name == '&') ++name;
1152 return new detail::function_test_case(fct, name);
1153}
1154
1155template <class FCT>
1156inline
1157detail::test_case *
1158create_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";
1171template <class E, class T, class V>
1172inline
1173std::basic_ostream<E,T> & operator,(std::basic_ostream<E,T> & o, V const & t)
1174{
1175 return (o << ' ' << t);
1176}
1177
1178template <class E, class T>
1179inline
1180std::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
1188template <class V>
1189inline
1190std::ostream & operator,(std::ostream & o, V const & t)
1191{
1192 return (o << ' ' << t);
1193}
1194
1195inline
1196std::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 */
size_type size() const
Definition tinyvector.hxx:913
R arg(const FFTWComplex< R > &a)
phase
Definition fftw3.hxx:1009
void add(FixedPoint< IntBits1, FracBits1 > l, FixedPoint< IntBits2, FracBits2 > r, FixedPoint< IntBits3, FracBits3 > &result)
addition with enforced result type.
Definition fixedpoint.hxx:561

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