[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