[geos-commits] r2343 - in trunk: . tests tests/unit tests/unit/tut
svn_geos at osgeo.org
svn_geos at osgeo.org
Thu Apr 9 15:39:44 EDT 2009
Author: mloskot
Date: 2009-04-09 15:39:44 -0400 (Thu, 09 Apr 2009)
New Revision: 2343
Added:
trunk/tests/unit/tut/
trunk/tests/unit/tut/Makefile.am
trunk/tests/unit/tut/tut.h
trunk/tests/unit/tut/tut_reporter.h
trunk/tests/unit/tut/tut_restartable.h
Removed:
trunk/tests/tut/
Modified:
trunk/configure.in
trunk/tests/Makefile.am
trunk/tests/unit/Makefile.am
Log:
Moved tests/tut to tests/unit/tut. Preparing for update to latest version of C++ TUT Framework.
Modified: trunk/configure.in
===================================================================
--- trunk/configure.in 2009-04-09 16:22:26 UTC (rev 2342)
+++ trunk/configure.in 2009-04-09 19:39:44 UTC (rev 2343)
@@ -343,7 +343,7 @@
tests/Makefile
tests/bigtest/Makefile
tests/unit/Makefile
- tests/tut/Makefile
+ tests/unit/tut/Makefile
tests/xmltester/Makefile
tools/Makefile
tools/geos-config
Modified: trunk/tests/Makefile.am
===================================================================
--- trunk/tests/Makefile.am 2009-04-09 16:22:26 UTC (rev 2342)
+++ trunk/tests/Makefile.am 2009-04-09 19:39:44 UTC (rev 2343)
@@ -1,6 +1,5 @@
SUBDIRS = \
bigtest \
xmltester \
- tut \
unit
Modified: trunk/tests/unit/Makefile.am
===================================================================
--- trunk/tests/unit/Makefile.am 2009-04-09 16:22:26 UTC (rev 2342)
+++ trunk/tests/unit/Makefile.am 2009-04-09 19:39:44 UTC (rev 2343)
@@ -1,5 +1,6 @@
+SUBDIRS = tut
-INCLUDES = -I$(top_srcdir)/source/headers -I$(top_srcdir)/tests/tut -I$(top_builddir)/capi
+INCLUDES = -I$(top_srcdir)/source/headers -I$(top_srcdir)/tests/unit/tut -I$(top_builddir)/capi
TESTS = geos_unit
Added: trunk/tests/unit/tut/Makefile.am
===================================================================
--- trunk/tests/unit/tut/Makefile.am (rev 0)
+++ trunk/tests/unit/tut/Makefile.am 2009-04-09 19:39:44 UTC (rev 2343)
@@ -0,0 +1,4 @@
+EXTRA_DIST = \
+ tut.h \
+ tut_reporter.h \
+ tut_restartable.h
Added: trunk/tests/unit/tut/tut.h
===================================================================
--- trunk/tests/unit/tut/tut.h (rev 0)
+++ trunk/tests/unit/tut/tut.h 2009-04-09 19:39:44 UTC (rev 2343)
@@ -0,0 +1,1059 @@
+// $Id: tut.h 1820 2006-09-06 16:54:23Z mloskot $
+//
+// TUT: C++ Unit Test Framework
+// http://tut-framework.sourceforge.net/
+//
+// NOTE: This file is included to GEOS source tree and may include some
+// changes comparing to the official version of TUT.
+//
+// The TUT License
+//
+// Copyright 2002-2006 Vladimir Dyuzhev.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+#ifndef TUT_H_GUARD
+#define TUT_H_GUARD
+
+#include <iostream>
+#include <iomanip>
+#include <map>
+#include <vector>
+#include <string>
+#include <sstream>
+#include <stdexcept>
+#include <typeinfo>
+#include <limits>
+#include <cmath>
+
+#if defined(TUT_USE_SEH)
+#include <windows.h>
+#include <winbase.h>
+#endif
+
+/**
+ * Template Unit Tests Framework for C++.
+ * http://tut.dozen.ru
+ *
+ * @author dozen, tut at dozen.ru
+ */
+namespace tut
+{
+ /**
+ * Exception to be throwed when attempted to execute
+ * missed test by number.
+ */
+ struct no_such_test : public std::logic_error
+ {
+ no_such_test() : std::logic_error("no such test") {}
+ };
+
+ /**
+ * No such test and passed test number is higher than
+ * any test number in current group. Used in one-by-one
+ * test running when upper bound is not known.
+ */
+ struct beyond_last_test : public no_such_test
+ {
+ beyond_last_test() {}
+ };
+
+ /**
+ * Group not found exception.
+ */
+ struct no_such_group : public std::logic_error
+ {
+ no_such_group(const std::string& grp)
+ : std::logic_error(grp)
+ {}
+ };
+
+ /**
+ * Internal exception to be throwed when
+ * no more tests left in group or journal.
+ */
+ struct no_more_tests
+ {
+ no_more_tests(){}
+ };
+
+ /**
+ * Internal exception to be throwed when
+ * test constructor has failed.
+ */
+ struct bad_ctor : public std::logic_error
+ {
+ bad_ctor(const std::string& msg)
+ : std::logic_error(msg)
+ {}
+ };
+
+ /**
+ * Exception to be throwed when ensure() fails or fail() called.
+ */
+ class failure : public std::logic_error
+ {
+ public:
+ failure(const std::string& msg)
+ : std::logic_error(msg)
+ {}
+ };
+
+ /**
+ * Exception to be throwed when test desctructor throwed an exception.
+ */
+ class warning : public std::logic_error
+ {
+ public:
+ warning(const std::string& msg)
+ : std::logic_error(msg)
+ {}
+ };
+
+ /**
+ * Exception to be throwed when test is going to send non-failure
+ * information message.
+ */
+ class information : public std::logic_error
+ {
+ public:
+ information(const std::string& msg)
+ : std::logic_error(msg)
+ {}
+ };
+
+ /**
+ * Exception to be throwed when test issued SEH (Win32)
+ */
+ class seh : public std::logic_error
+ {
+ public:
+ seh(const std::string& msg)
+ : std::logic_error(msg)
+ {}
+ };
+
+ /**
+ * Return type of runned test/test group.
+ *
+ * For test: contains result of test and, possible, message
+ * for failure or exception.
+ */
+ struct test_result
+ {
+ /**
+ * Test group name.
+ */
+ std::string group;
+
+ /**
+ * Test number in group.
+ */
+ int test;
+
+ /**
+ * ok - test finished successfully
+ * fail - test failed with ensure() or fail() methods
+ * ex - test throwed an exceptions
+ * warn - test finished successfully, but test destructor throwed
+ * term - test forced test application to terminate abnormally
+ */
+ typedef enum { ok, fail, ex, warn, term, ex_ctor, inform } result_type;
+ result_type result;
+
+ /**
+ * Exception message for failed test.
+ */
+ std::string message;
+ std::string exception_typeid;
+
+ /**
+ * Default constructor.
+ */
+ test_result()
+ : test(0), result(ok)
+ {}
+
+ /**
+ * Constructor.
+ */
+ test_result( const std::string& grp,int pos,result_type res)
+ : group(grp), test(pos), result(res)
+ {}
+
+ /**
+ * Constructor with exception.
+ */
+ test_result( const std::string& grp,int pos,
+ result_type res,
+ const std::exception& ex)
+ : group(grp), test(pos), result(res),
+ message(ex.what()), exception_typeid(typeid(ex).name())
+ {}
+
+ };
+
+ /**
+ * Interface.
+ * Test group operations.
+ */
+ struct group_base
+ {
+ virtual ~group_base(){};
+
+ // execute tests iteratively
+ virtual void rewind() = 0;
+ virtual test_result run_next() = 0;
+
+ // execute one test
+ virtual test_result run_test(int n) = 0;
+ };
+
+ /**
+ * Test runner callback interface.
+ * Can be implemented by caller to update
+ * tests results in real-time. User can implement
+ * any of callback methods, and leave unused
+ * in default implementation.
+ */
+ struct callback
+ {
+ /**
+ * Virtual destructor is a must for subclassed types.
+ */
+ virtual ~callback(){};
+
+ /**
+ * Called when new test run started.
+ */
+ virtual void run_started(){};
+
+ /**
+ * Called when a group started
+ * @param name Name of the group
+ */
+ virtual void group_started(const std::string& /*name*/){};
+
+ /**
+ * Called when a test finished.
+ * @param tr Test results.
+ */
+ virtual void test_completed(const test_result& /*tr*/){};
+
+ /**
+ * Called when a group is completed
+ * @param name Name of the group
+ */
+ virtual void group_completed(const std::string& /*name*/){};
+
+ /**
+ * Called when all tests in run completed.
+ */
+ virtual void run_completed(){};
+ };
+
+ /**
+ * Typedef for runner::list_groups()
+ */
+ typedef std::vector<std::string> groupnames;
+
+ /**
+ * Test runner.
+ */
+ class test_runner
+ {
+ protected:
+ typedef std::map<std::string,group_base*> groups;
+ typedef groups::iterator iterator;
+ typedef groups::const_iterator const_iterator;
+ groups groups_;
+
+ callback default_callback_;
+ callback* callback_;
+
+ public:
+ /**
+ * Constructor
+ */
+ test_runner() : callback_(&default_callback_)
+ {
+ }
+
+ /**
+ * Stores another group for getting by name.
+ */
+ void register_group(const std::string& name,group_base* gr)
+ {
+ if( gr == 0 )
+ {
+ throw std::invalid_argument("group shall be non-null");
+ }
+
+ groups::iterator found = groups_.find(name);
+ if( found != groups_.end() )
+ {
+ std::string msg("attempt to add already existent group "+name);
+ // this exception terminates application so we use cerr also
+ std::cerr << msg << std::endl;
+ throw std::logic_error(msg);
+ }
+
+ groups_[name] = gr;
+ }
+
+ /**
+ * Stores callback object.
+ */
+ void set_callback(callback* cb)
+ {
+ callback_ = cb==0? &default_callback_:cb;
+ }
+
+ /**
+ * Returns callback object.
+ */
+ callback& get_callback() const
+ {
+ return *callback_;
+ }
+
+ /**
+ * Returns list of known test groups.
+ */
+ const groupnames list_groups() const
+ {
+ groupnames ret;
+ const_iterator i = groups_.begin();
+ const_iterator e = groups_.end();
+ while( i != e )
+ {
+ ret.push_back(i->first);
+ ++i;
+ }
+ return ret;
+ }
+
+ /**
+ * Runs all tests in all groups.
+ * @param callback Callback object if exists; null otherwise
+ */
+ void run_tests() const
+ {
+ callback_->run_started();
+
+ const_iterator i = groups_.begin();
+ const_iterator e = groups_.end();
+ while( i != e )
+ {
+ callback_->group_started(i->first);
+ try
+ {
+ run_all_tests_in_group_(i);
+ }
+ catch( const no_more_tests& )
+ {
+ callback_->group_completed(i->first);
+ }
+
+ ++i;
+ }
+
+ callback_->run_completed();
+ }
+
+ /**
+ * Runs all tests in specified group.
+ */
+ void run_tests(const std::string& group_name) const
+ {
+ callback_->run_started();
+
+ const_iterator i = groups_.find(group_name);
+ if( i == groups_.end() )
+ {
+ callback_->run_completed();
+ throw no_such_group(group_name);
+ }
+
+ callback_->group_started(group_name);
+ try
+ {
+ run_all_tests_in_group_(i);
+ }
+ catch( const no_more_tests& )
+ {
+ // ok
+ }
+
+ callback_->group_completed(group_name);
+ callback_->run_completed();
+ }
+
+ /**
+ * Runs one test in specified group.
+ */
+ test_result run_test(const std::string& group_name,int n) const
+ {
+ callback_->run_started();
+
+ const_iterator i = groups_.find(group_name);
+ if( i == groups_.end() )
+ {
+ callback_->run_completed();
+ throw no_such_group(group_name);
+ }
+
+ callback_->group_started(group_name);
+ try
+ {
+ test_result tr = i->second->run_test(n);
+ callback_->test_completed(tr);
+ callback_->group_completed(group_name);
+ callback_->run_completed();
+ return tr;
+ }
+ catch( const beyond_last_test& )
+ {
+ callback_->group_completed(group_name);
+ callback_->run_completed();
+ throw;
+ }
+ catch( const no_such_test& )
+ {
+ callback_->group_completed(group_name);
+ callback_->run_completed();
+ throw;
+ }
+ }
+
+ private:
+ void run_all_tests_in_group_(const_iterator i) const
+ {
+ i->second->rewind();
+ for( ;; )
+ {
+ test_result tr = i->second->run_next();
+ callback_->test_completed(tr);
+
+ if( tr.result == test_result::ex_ctor )
+ {
+ throw no_more_tests();
+ }
+ }
+ }
+ };
+
+ /**
+ * Singleton for test_runner implementation.
+ * Instance with name runner_singleton shall be implemented
+ * by user.
+ */
+ class test_runner_singleton
+ {
+ public:
+ static test_runner& get()
+ {
+ static test_runner tr;
+ return tr;
+ }
+ };
+ extern test_runner_singleton runner;
+
+ /**
+ * Test object. Contains data test run upon and default test method
+ * implementation. Inherited from Data to allow tests to
+ * access test data as members.
+ */
+ template <class Data>
+ class test_object : public Data
+ {
+ public:
+ /**
+ * Default constructor
+ */
+ test_object(){};
+
+ /**
+ * The flag is set to true by default (dummy) test.
+ * Used to detect usused test numbers and avoid unnecessary
+ * test object creation which may be time-consuming depending
+ * on operations described in Data::Data() and Data::~Data().
+ * TODO: replace with throwing special exception from default test.
+ */
+ bool called_method_was_a_dummy_test_;
+
+ /**
+ * Default do-nothing test.
+ */
+ template <int n>
+ void test()
+ {
+ called_method_was_a_dummy_test_ = true;
+ }
+ };
+
+ namespace
+ {
+ /**
+ * Tests provided condition.
+ * Throws if false.
+ */
+ void ensure(bool cond)
+ {
+ if( !cond ) throw failure("");
+ }
+
+ /**
+ * Tests provided condition.
+ * Throws if false.
+ */
+ template<typename T>
+ void ensure(const T msg,bool cond)
+ {
+ if( !cond ) throw failure(msg);
+ }
+
+ /**
+ * Tests two objects for being equal.
+ * Throws if false.
+ *
+ * NB: both T and Q must have operator << defined somewhere, or
+ * client code will not compile at all!
+ */
+ template <class T,class Q>
+ void ensure_equals(const char* msg,const Q& actual,const T& expected)
+ {
+ if( !(expected == actual) )
+ {
+ std::stringstream ss;
+ ss << (msg?msg:"") << (msg?": ":"")
+ << "expected " << expected
+ << " actual " << actual;
+ throw failure(ss.str().c_str());
+ }
+ }
+
+ // ensure_equals specialization for double
+ template <>
+ void ensure_equals<double, double>(const char* msg, const double& actual,const double& expected)
+ {
+ const double epsilon = std::numeric_limits<double>::epsilon();
+ const double diff = actual - expected;
+
+ if ( !((diff <= epsilon) && (diff >= -epsilon )) )
+ {
+ std::stringstream ss;
+ ss << (msg?msg:"") << (msg?": ":"")
+ << std::scientific
+ << std::showpoint
+ << std::setprecision(16)
+ << "expected " << expected
+ << " actual " << actual
+ << " with precision " << epsilon;
+ throw failure(ss.str().c_str());
+ }
+ }
+
+ template <class T,class Q>
+ void ensure_equals(const Q& actual,const T& expected)
+ {
+ ensure_equals<>(0,actual,expected);
+ }
+
+ /**
+ * Tests two objects for being equal.
+ * Throws if false.
+ *
+ * NB: both T and Q must have operator << defined somewhere, or
+ * client code will not compile at all!
+ */
+ template <class T,class Q>
+ void ensure_not_equals(const char* msg,const Q& actual,const T& expected)
+ {
+ if( !(expected != actual) )
+ {
+ std::stringstream ss;
+ ss << (msg?msg:"") << (msg?": ":"")
+ << "expected " << expected
+ << " actual " << actual;
+ throw failure(ss.str().c_str());
+ }
+ }
+
+ template <class T,class Q>
+ void ensure_not_equals(const Q& actual,const T& expected)
+ {
+ ensure_not_equals<>(0,actual,expected);
+ }
+
+ /**
+ * Tests two objects for being at most in given distance one from another.
+ * Borders are excluded.
+ * Throws if false.
+ *
+ * NB: T must have operator << defined somewhere, or
+ * client code will not compile at all! Also, T shall have
+ * operators + and -, and be comparable.
+ */
+ template <class T>
+ void ensure_distance(const char* msg,const T& actual,
+ const T& expected,const T& distance)
+ {
+ if( expected-distance >= actual || expected+distance <= actual )
+ {
+ std::stringstream ss;
+ ss << (msg?msg:"") << (msg?": ":"")
+ << "expected [" << expected-distance
+ << ";" << expected+distance
+ << "] actual " << actual;
+ throw failure(ss.str().c_str());
+ }
+ }
+
+ template <class T>
+ void ensure_distance(const T& actual,const T& expected,const T& distance)
+ {
+ ensure_distance<>(0,actual,expected,distance);
+ }
+
+ /**
+ * Non-conditional information message.
+ */
+ void inform(const char* msg="")
+ {
+ throw information(msg);
+ }
+
+ /**
+ * Non-conditonal warning message.
+ */
+ void warn(const char* msg="")
+ {
+ throw warning(msg);
+ }
+
+ /**
+ * Unconditionally fails with message.
+ */
+ void fail(const char* msg="")
+ {
+ throw failure(msg);
+ }
+ }
+
+ /**
+ * Walks through test tree and stores address of each
+ * test method in group. Instantiation stops at 0.
+ */
+ template <class Test,class Group,int n>
+ struct tests_registerer
+ {
+ static void reg(Group& group)
+ {
+ group.reg(n,&Test::template test<n>);
+ tests_registerer<Test,Group,n-1>::reg(group);
+ }
+ };
+
+ template<class Test,class Group>
+ struct tests_registerer<Test,Group,0>
+ {
+ static void reg(Group&){};
+ };
+
+ /**
+ * Test group; used to recreate test object instance for
+ * each new test since we have to have reinitialized
+ * Data base class.
+ */
+ template <class Data,int MaxTestsInGroup = 50>
+ class test_group : public group_base
+ {
+ const char* name_;
+
+ typedef void (test_object<Data>::*testmethod)();
+ typedef std::map<int,testmethod> tests;
+ typedef typename tests::iterator tests_iterator;
+ typedef typename tests::const_iterator tests_const_iterator;
+ typedef typename tests::const_reverse_iterator
+ tests_const_reverse_iterator;
+ typedef typename tests::size_type size_type;
+
+ tests tests_;
+ tests_iterator current_test_;
+
+ /**
+ * Exception-in-destructor-safe smart-pointer class.
+ */
+ template <class T>
+ class safe_holder
+ {
+ T* p_;
+ bool permit_throw_in_dtor;
+
+ safe_holder(const safe_holder&);
+ safe_holder& operator = (const safe_holder&);
+
+ public:
+ safe_holder() : p_(0),permit_throw_in_dtor(false)
+ {
+ }
+
+ ~safe_holder()
+ {
+ release();
+ }
+
+ T* operator -> () const { return p_; };
+ T* get() const { return p_; };
+
+ /**
+ * Tell ptr it can throw from destructor. Right way is to
+ * use std::uncaught_exception(), but some compilers lack
+ * correct implementation of the function.
+ */
+ void permit_throw(){ permit_throw_in_dtor = true; }
+
+ /**
+ * Specially treats exceptions in test object destructor;
+ * if test itself failed, exceptions in destructor
+ * are ignored; if test was successful and destructor failed,
+ * warning exception throwed.
+ */
+ void release()
+ {
+ try
+ {
+ if( delete_obj() == false )
+ {
+ throw warning("destructor of test object raised an SEH exception");
+ }
+ }
+ catch( const std::exception& ex )
+ {
+ if( permit_throw_in_dtor )
+ {
+ std::string msg = "destructor of test object raised exception: ";
+ msg += ex.what();
+ throw warning(msg);
+ }
+ }
+ catch( ... )
+ {
+ if( permit_throw_in_dtor )
+ {
+ throw warning("destructor of test object raised an exception");
+ }
+ }
+ }
+
+ /**
+ * Re-init holder to get brand new object.
+ */
+ void reset()
+ {
+ release();
+ permit_throw_in_dtor = false;
+ p_ = new T();
+ }
+
+ bool delete_obj()
+ {
+#if defined(TUT_USE_SEH)
+ __try
+ {
+#endif
+ T* p = p_;
+ p_ = 0;
+ delete p;
+#if defined(TUT_USE_SEH)
+ }
+ __except(handle_seh_(::GetExceptionCode()))
+ {
+ if( permit_throw_in_dtor )
+ {
+ return false;
+ }
+ }
+#endif
+ return true;
+ }
+ };
+
+ public:
+ typedef test_object<Data> object;
+
+ /**
+ * Creates and registers test group with specified name.
+ */
+ test_group(const char* name)
+ : name_(name)
+ {
+ // register itself
+ runner.get().register_group(name_,this);
+
+ // register all tests
+ tests_registerer<object,test_group,MaxTestsInGroup>::reg(*this);
+ };
+
+ /**
+ * This constructor is used in self-test run only.
+ */
+ test_group(const char* name,test_runner& another_runner)
+ : name_(name)
+ {
+ // register itself
+ another_runner.register_group(name_,this);
+
+ // register all tests
+ tests_registerer<test_object<Data>,
+ test_group,MaxTestsInGroup>::reg(*this);
+ };
+
+ /**
+ * Registers test method under given number.
+ */
+ void reg(int n,testmethod tm)
+ {
+ tests_[n] = tm;
+ }
+
+ /**
+ * Reset test position before first test.
+ */
+ void rewind()
+ {
+ current_test_ = tests_.begin();
+ }
+
+ /**
+ * Runs next test.
+ */
+ test_result run_next()
+ {
+ if( current_test_ == tests_.end() )
+ {
+ throw no_more_tests();
+ }
+
+ // find next user-specialized test
+ safe_holder<object> obj;
+ while( current_test_ != tests_.end() )
+ {
+ try
+ {
+ return run_test_(current_test_++,obj);
+ }
+ catch( const no_such_test& )
+ {
+ continue;
+ }
+ }
+
+ throw no_more_tests();
+ }
+
+ /**
+ * Runs one test by position.
+ */
+ test_result run_test(int n)
+ {
+ // beyond tests is special case to discover upper limit
+ if( tests_.rbegin() == tests_.rend() ) throw beyond_last_test();
+ if( tests_.rbegin()->first < n ) throw beyond_last_test();
+
+ // withing scope; check if given test exists
+ tests_iterator ti = tests_.find(n);
+ if( ti == tests_.end() ) throw no_such_test();
+
+ safe_holder<object> obj;
+ return run_test_(ti,obj);
+ }
+
+ private:
+ /**
+ * VC allows only one exception handling type per function,
+ * so I have to split the method
+ */
+ test_result run_test_(const tests_iterator& ti,safe_holder<object>& obj)
+ {
+ try
+ {
+ if( run_test_seh_(ti->second,obj) == false )
+ throw seh("seh");
+ }
+ catch(const no_such_test&)
+ {
+ throw;
+ }
+ catch(const warning& ex)
+ {
+ // test ok, but destructor failed
+ test_result tr(name_,ti->first,test_result::warn,ex);
+ return tr;
+ }
+ catch(const failure& ex)
+ {
+ // test failed because of ensure() or similar method
+ test_result tr(name_,ti->first,test_result::fail,ex);
+ return tr;
+ }
+ catch(const information& ex)
+ {
+ // test not failed and not succeeded, just wants to inform about something
+ test_result tr(name_,ti->first,test_result::inform,ex);
+ return tr;
+ }
+ catch(const seh& ex)
+ {
+ // test failed with sigsegv, divide by zero, etc
+ test_result tr(name_,ti->first,test_result::term,ex);
+ return tr;
+ }
+ catch(const bad_ctor& ex)
+ {
+ // test failed because test ctor failed; stop the whole group
+ test_result tr(name_,ti->first,test_result::ex_ctor,ex);
+ return tr;
+ }
+ catch(const std::exception& ex)
+ {
+ // test failed with std::exception
+ test_result tr(name_,ti->first,test_result::ex,ex);
+ return tr;
+ }
+ catch(...)
+ {
+ // test failed with unknown exception
+ test_result tr(name_,ti->first,test_result::ex);
+ return tr;
+ }
+
+ // test passed
+ test_result tr(name_,ti->first,test_result::ok);
+ return tr;
+ }
+
+ /**
+ * Runs one under SEH if platform supports it.
+ */
+ bool run_test_seh_(testmethod tm,safe_holder<object>& obj)
+ {
+#if defined(TUT_USE_SEH)
+ __try
+ {
+#endif
+ if( obj.get() == 0 )
+ {
+ reset_holder_(obj);
+ }
+ obj->called_method_was_a_dummy_test_ = false;
+
+#if defined(TUT_USE_SEH)
+ __try
+ {
+#endif
+ (obj.get()->*tm)();
+#if defined(TUT_USE_SEH)
+ }
+ __except(handle_seh_(::GetExceptionCode()))
+ {
+ // throw seh("SEH");
+ return false;
+ }
+#endif
+
+ if( obj->called_method_was_a_dummy_test_ )
+ {
+ // do not call obj.release(); reuse object
+ throw no_such_test();
+ }
+
+ obj.permit_throw();
+ obj.release();
+#if defined(TUT_USE_SEH)
+ }
+ __except(handle_seh_(::GetExceptionCode()))
+ {
+ return false;
+ }
+#endif
+ return true;
+ }
+
+ void reset_holder_(safe_holder<object>& obj)
+ {
+ try
+ {
+ obj.reset();
+ }
+ catch(const std::exception& ex)
+ {
+ throw bad_ctor(ex.what());
+ }
+ catch(...)
+ {
+ throw bad_ctor("test constructor has generated an exception; \
+ group execution is terminated");
+ }
+ }
+ };
+
+#if defined(TUT_USE_SEH)
+ /**
+ * Decides should we execute handler or ignore SE.
+ */
+ inline int handle_seh_(DWORD excode)
+ {
+ switch(excode)
+ {
+ case EXCEPTION_ACCESS_VIOLATION:
+ case EXCEPTION_DATATYPE_MISALIGNMENT:
+ case EXCEPTION_BREAKPOINT:
+ case EXCEPTION_SINGLE_STEP:
+ case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+ case EXCEPTION_FLT_DENORMAL_OPERAND:
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ case EXCEPTION_FLT_INEXACT_RESULT:
+ case EXCEPTION_FLT_INVALID_OPERATION:
+ case EXCEPTION_FLT_OVERFLOW:
+ case EXCEPTION_FLT_STACK_CHECK:
+ case EXCEPTION_FLT_UNDERFLOW:
+ case EXCEPTION_INT_DIVIDE_BY_ZERO:
+ case EXCEPTION_INT_OVERFLOW:
+ case EXCEPTION_PRIV_INSTRUCTION:
+ case EXCEPTION_IN_PAGE_ERROR:
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ case EXCEPTION_NONCONTINUABLE_EXCEPTION:
+ case EXCEPTION_STACK_OVERFLOW:
+ case EXCEPTION_INVALID_DISPOSITION:
+ case EXCEPTION_GUARD_PAGE:
+ case EXCEPTION_INVALID_HANDLE:
+ return EXCEPTION_EXECUTE_HANDLER;
+ };
+
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+#endif // #if defined(TUT_USE_SEH)
+
+} // namespace tut
+
+#endif // #ifndef TUT_H_GUARD
+
Added: trunk/tests/unit/tut/tut_reporter.h
===================================================================
--- trunk/tests/unit/tut/tut_reporter.h (rev 0)
+++ trunk/tests/unit/tut/tut_reporter.h 2009-04-09 19:39:44 UTC (rev 2343)
@@ -0,0 +1,252 @@
+// $Id: tut_reporter.h 1820 2006-09-06 16:54:23Z mloskot $
+//
+// TUT: C++ Unit Test Framework
+// http://tut-framework.sourceforge.net/
+//
+// NOTE: This file is a customized version of the official tut_reporter.h.
+// The TUT's version of tut_reporter.h file is only a sample of
+// how to write TUT visualizer.
+//
+#ifndef TUT_REPORTER
+#define TUT_REPORTER
+
+#include <tut.h>
+
+/**
+* Template Unit Tests Framework for C++.
+* http://tut.dozen.ru
+*
+* @author dozen, tut at dozen.ru
+*/
+namespace
+{
+ std::ostream& operator << (std::ostream& os,const tut::test_result& tr)
+ {
+ switch(tr.result)
+ {
+ case tut::test_result::ok:
+ os << '.';
+ break;
+
+ case tut::test_result::fail:
+ os << '[' << tr.test << "=F]";
+ break;
+
+ case tut::test_result::ex_ctor:
+ os << '[' << tr.test << "=C]";
+ break;
+
+ case tut::test_result::ex:
+ os << '[' << tr.test << "=X]";
+ break;
+
+ case tut::test_result::warn:
+ os << '[' << tr.test << "=W]";
+ break;
+
+ case tut::test_result::term:
+ os << '[' << tr.test << "=T]";
+ break;
+ case tut::test_result::inform:
+ break; // do nothing
+ }
+
+ return os;
+ }
+} // namespace
+
+namespace tut
+{
+ /**
+ * Default TUT callback handler.
+ */
+ class reporter : public tut::callback
+ {
+ std::string current_group;
+ typedef std::vector<tut::test_result> not_passed_list;
+ not_passed_list not_passed;
+ std::ostream& os;
+
+ public:
+ int ok_count;
+ int ok_group_count;
+ int exceptions_count;
+ int failures_count;
+ int terminations_count;
+ int warnings_count;
+ int informs_count;
+
+ reporter() : os(std::cout)
+ {
+ init();
+ }
+
+ reporter(std::ostream& out) : os(out)
+ {
+ init();
+ }
+
+ void run_started()
+ {
+ init();
+ }
+
+ void test_completed(const tut::test_result& tr)
+ {
+ if( tr.group != current_group )
+ {
+ os << std::endl << tr.group << ": " << std::flush;
+ current_group = tr.group;
+ }
+
+ os << tr << std::flush;
+
+ // inform result is neutral: not failure naither success
+ if (tr.result == tut::test_result::inform)
+ {
+ informs_count++;
+ }
+
+ // Treat inform test results as passed
+ if( tr.result == tut::test_result::ok
+ || tr.result == tut::test_result::inform )
+ {
+ ok_group_count++;
+ ok_count++;
+ }
+ else if( tr.result == tut::test_result::ex )
+ exceptions_count++;
+ else if( tr.result == tut::test_result::ex_ctor )
+ exceptions_count++;
+ else if( tr.result == tut::test_result::fail )
+ failures_count++;
+ else if( tr.result == tut::test_result::warn )
+ warnings_count++;
+ else
+ terminations_count++;
+
+ if( tr.result != tut::test_result::ok )
+ {
+ not_passed.push_back(tr);
+ }
+ }
+
+ void group_started(const std::string& name)
+ {
+ ok_group_count = 0;
+ }
+
+ void group_completed(const std::string& name)
+ {
+ os << " " << ok_group_count << std::endl;
+ }
+
+ void run_completed()
+ {
+ os << std::endl;
+
+ if( not_passed.size() > 0 )
+ {
+ not_passed_list::const_iterator i = not_passed.begin();
+ while( i != not_passed.end() )
+ {
+ tut::test_result tr = *i;
+
+ os << std::endl;
+ os << "---> " << "group: " << tr.group
+ << ", test: test<" << tr.test
+ << ">" << std::endl;
+ os << " problem: ";
+
+ switch(tr.result)
+ {
+ case test_result::fail:
+ os << "assertion failed" << std::endl;
+ break;
+ case test_result::ex:
+ case test_result::ex_ctor:
+ os << "unexpected exception" << std::endl;
+ if( tr.exception_typeid != "" )
+ {
+ os << " exception typeid: "
+ << tr.exception_typeid << std::endl;
+ }
+ break;
+ case test_result::term:
+ os << "would be terminated" << std::endl;
+ break;
+ case test_result::warn:
+ os << "test passed, but cleanup code (destructor) \
+ raised an exception" << std::endl;
+ break;
+ case test_result::inform:
+ os << "test sends neutral information message" << std::endl;
+ break;
+ default:
+ break;
+ }
+
+ if( tr.message != "" )
+ {
+ if( tr.result == test_result::fail )
+ {
+ os << " failed assertion: \""
+ << tr.message << "\"" << std::endl;
+ }
+ else
+ {
+ os << " message: \""
+ << tr.message << "\"" << std::endl;
+ }
+ }
+
+ ++i;
+ }
+ }
+
+ os << std::endl;
+ os << "Tests summary:" << std::endl;
+ if( terminations_count > 0 )
+ os << " - terminations:" << terminations_count << std::endl;
+ if( exceptions_count > 0 )
+ os << " - exceptions:" << exceptions_count << std::endl;
+ if( failures_count > 0 )
+ os << " - failures:" << failures_count << std::endl;
+ if( warnings_count > 0 )
+ os << " - warnings:" << warnings_count << std::endl;
+ if( informs_count > 0 )
+ os << " - informing:" << informs_count << std::endl;
+
+ os << " - passed: " << ok_count;
+ os << std::endl;
+ }
+
+ bool all_ok() const
+ {
+ return not_passed.size() == 0;
+ }
+
+ int get_failures_count() const
+ {
+ return failures_count;
+ }
+
+ private:
+ void init()
+ {
+ ok_count = 0;
+ ok_group_count = 0;
+ exceptions_count = 0;
+ failures_count = 0;
+ terminations_count = 0;
+ warnings_count = 0;
+ informs_count = 0;
+
+ not_passed.clear();
+ }
+ };
+
+} // namespace tut
+
+#endif // #ifndef TUT_REPORTER
+
Added: trunk/tests/unit/tut/tut_restartable.h
===================================================================
--- trunk/tests/unit/tut/tut_restartable.h (rev 0)
+++ trunk/tests/unit/tut/tut_restartable.h 2009-04-09 19:39:44 UTC (rev 2343)
@@ -0,0 +1,372 @@
+// $Id: tut_restartable.h 1820 2006-09-06 16:54:23Z mloskot $
+//
+// TUT: C++ Unit Test Framework
+// http://tut-framework.sourceforge.net/
+//
+// NOTE: This file is included to GEOS source tree and may include some
+// changes comparing to the official version of TUT.
+//
+// The TUT License
+//
+// Copyright 2002-2006 Vladimir Dyuzhev.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+#ifndef TUT_RESTARTABLE_H_GUARD
+#define TUT_RESTARTABLE_H_GUARD
+
+#include "tut.h"
+#include <fstream>
+#include <iostream>
+
+/**
+ * Template Unit Tests Framework for C++.
+ * http://tut.dozen.ru
+ *
+ * Optional restartable wrapper for test_runner.
+ * Allows to restart test runs finished due to
+ * abnormal test application termination (such as
+ * segmentation fault or math error).
+ *
+ * @author dozen, tut at dozen.ru
+ */
+namespace tut
+{
+ namespace util
+ {
+ /**
+ * Escapes non-alphabetical characters in string.
+ */
+ std::string escape(const std::string& orig)
+ {
+ std::string rc;
+ std::string::const_iterator i,e;
+ i = orig.begin();
+ e = orig.end();
+
+ while( i != e )
+ {
+ if( (*i >= 'a' && *i <= 'z') ||
+ (*i >= 'A' && *i <= 'Z') ||
+ (*i >= '0' && *i <= '9') )
+ {
+ rc += *i;
+ }
+ else
+ {
+ rc += '\\';
+ rc += ('a'+(((unsigned int)*i)>>4));
+ rc += ('a'+(((unsigned int)*i)&0xF));
+ }
+
+ ++i;
+ }
+ return rc;
+ }
+
+ /**
+ * Un-escapes string.
+ */
+ std::string unescape(const std::string& orig)
+ {
+ std::string rc;
+ std::string::const_iterator i,e;
+ i = orig.begin();
+ e = orig.end();
+
+ while( i != e )
+ {
+ if( *i != '\\' )
+ {
+ rc += *i;
+ }
+ else
+ {
+ ++i; if( i == e ) throw std::invalid_argument("unexpected end of string");
+ unsigned int c1 = *i;
+ ++i; if( i == e ) throw std::invalid_argument("unexpected end of string");
+ unsigned int c2 = *i;
+ rc += (((c1-'a')<<4) + (c2-'a'));
+ }
+
+ ++i;
+ }
+ return rc;
+ }
+
+ /**
+ * Serialize test_result avoiding interfering with operator <<.
+ */
+ void serialize(std::ostream& os,const tut::test_result& tr)
+ {
+ os << escape(tr.group) << std::endl;
+ os << tr.test << ' ';
+ switch(tr.result)
+ {
+ case test_result::ok: os << 0; break;
+ case test_result::fail: os << 1; break;
+ case test_result::ex: os << 2; break;
+ case test_result::warn: os << 3; break;
+ case test_result::term: os << 4; break;
+ default: throw std::logic_error("operator << : bad result_type");
+ }
+ os << ' ' << escape(tr.message) << std::endl;
+ }
+
+ /**
+ * deserialization for test_result
+ */
+ void deserialize(std::istream& is,tut::test_result& tr)
+ {
+ std::getline(is,tr.group);
+ if( is.eof() ) throw tut::no_more_tests();
+ tr.group = unescape(tr.group);
+
+ tr.test = -1;
+ is >> tr.test;
+ if( tr.test < 0 ) throw std::logic_error("operator >> : bad test number");
+
+ int n = -1; is >> n;
+ switch(n)
+ {
+ case 0: tr.result = test_result::ok; break;
+ case 1: tr.result = test_result::fail; break;
+ case 2: tr.result = test_result::ex; break;
+ case 3: tr.result = test_result::warn; break;
+ case 4: tr.result = test_result::term; break;
+ default: throw std::logic_error("operator >> : bad result_type");
+ }
+
+ is.ignore(1); // space
+ std::getline(is,tr.message);
+ tr.message = unescape(tr.message);
+ if( !is.good() ) throw std::logic_error("malformed test result");
+ }
+ };
+
+ /**
+ * Restartable test runner wrapper.
+ */
+ class restartable_wrapper
+ {
+ test_runner& runner_;
+ callback* callback_;
+
+ std::string dir_;
+ std::string log_; // log file: last test being executed
+ std::string jrn_; // journal file: results of all executed tests
+
+ public:
+ /**
+ * Default constructor.
+ * @param dir Directory where to search/put log and journal files
+ */
+ restartable_wrapper(const std::string& dir = ".")
+ : runner_(runner.get()), callback_(0), dir_(dir)
+ {
+ // dozen: it works, but it would be better to use system path separator
+ jrn_ = dir_+'/'+"journal.tut";
+ log_ = dir_+'/'+"log.tut";
+ }
+
+ /**
+ * Stores another group for getting by name.
+ */
+ void register_group(const std::string& name,group_base* gr)
+ {
+ runner_.register_group(name,gr);
+ }
+
+ /**
+ * Stores callback object.
+ */
+ void set_callback(callback* cb)
+ {
+ callback_ = cb;
+ }
+
+ /**
+ * Returns callback object.
+ */
+ callback& get_callback() const
+ {
+ return runner_.get_callback();
+ }
+
+ /**
+ * Returns list of known test groups.
+ */
+ groupnames list_groups() const
+ {
+ return runner_.list_groups();
+ }
+
+ /**
+ * Runs all tests in all groups.
+ */
+ void run_tests() const
+ {
+ // where last run was failed
+ std::string fail_group;
+ int fail_test;
+ read_log_(fail_group,fail_test);
+ bool fail_group_reached = (fail_group == "");
+
+ // iterate over groups
+ tut::groupnames gn = list_groups();
+ tut::groupnames::const_iterator gni,gne;
+ gni = gn.begin();
+ gne = gn.end();
+ while( gni != gne )
+ {
+ // skip all groups before one that failed
+ if( !fail_group_reached )
+ {
+ if( *gni != fail_group )
+ {
+ ++gni;
+ continue;
+ }
+ fail_group_reached = true;
+ }
+
+ // first or restarted run
+ int test = (*gni == fail_group && fail_test>=0)? fail_test+1:1;
+ while(true)
+ {
+ // last executed test pos
+ register_execution_(*gni,test);
+
+ try
+ {
+ tut::test_result tr = runner_.run_test(*gni,test);
+ register_test_(tr);
+ }
+ catch( const tut::beyond_last_test& ex )
+ {
+ break;
+ }
+ catch( const tut::no_such_test& ex )
+ {
+ // it's ok
+ }
+
+ ++test;
+ }
+
+ ++gni;
+ }
+
+ // show final results to user
+ invoke_callback_();
+
+ // truncate files as mark of successful finish
+ truncate_();
+ }
+
+ private:
+ /**
+ * Shows results from journal file.
+ */
+ void invoke_callback_() const
+ {
+ runner_.set_callback(callback_);
+ runner_.get_callback().run_started();
+
+ std::string current_group;
+ std::ifstream ijournal(jrn_.c_str());
+ while( ijournal.good() )
+ {
+ // read next test result
+ try
+ {
+ tut::test_result tr;
+ util::deserialize(ijournal,tr);
+ runner_.get_callback().test_completed(tr);
+ }
+ catch( const no_more_tests& )
+ {
+ break;
+ }
+ }
+
+ runner_.get_callback().run_completed();
+ }
+
+ /**
+ * Register test into journal.
+ */
+ void register_test_(const test_result& tr) const
+ {
+ std::ofstream ojournal(jrn_.c_str(),std::ios::app);
+ util::serialize(ojournal,tr);
+ ojournal << std::flush;
+ if( !ojournal.good() )
+ throw std::runtime_error("unable to register test result in file "+jrn_);
+ }
+
+ /**
+ * Mark the fact test going to be executed
+ */
+ void register_execution_(const std::string& grp,int test) const
+ {
+ // last executed test pos
+ std::ofstream olog(log_.c_str());
+ olog << util::escape(grp) << std::endl << test << std::endl << std::flush;
+ if( !olog.good() )
+ throw std::runtime_error("unable to register execution in file "+log_);
+ }
+
+ /**
+ * Truncate tests.
+ */
+ void truncate_() const
+ {
+ std::ofstream olog(log_.c_str());
+ std::ofstream ojournal(jrn_.c_str());
+ }
+
+ /**
+ * Read log file
+ */
+ void read_log_(std::string& fail_group,int& fail_test) const
+ {
+ // read failure point, if any
+ std::ifstream ilog(log_.c_str());
+ std::getline(ilog,fail_group);
+ fail_group = util::unescape(fail_group);
+ ilog >> fail_test;
+ if( !ilog.good() )
+ {
+ fail_group = ""; fail_test = -1;
+ truncate_();
+ }
+ else
+ {
+ // test was terminated...
+ tut::test_result tr(fail_group,fail_test,tut::test_result::term);
+ register_test_(tr);
+ }
+ }
+ };
+}
+
+#endif
+
More information about the geos-commits
mailing list