Merge branch 'master' of git://git.pipapo.org/cinelerra3/ichthyo

This commit is contained in:
Christian Thaeter 2007-08-30 01:42:06 +02:00
commit 5832cfeae2
34 changed files with 1864 additions and 386 deletions

2
.gitignore vendored
View file

@ -2,10 +2,12 @@
*~
*.tar.*
.[^.]*
*.os
Buildhelper.pyc
optcache
Makefile.in
build/*
bin/*
autom4te.cache/*
scripts/*
configure

View file

@ -31,7 +31,7 @@ from Buildhelper import *
OPTIONSCACHEFILE = 'optcache'
CUSTOPTIONSFILE = 'custom-options'
SRCDIR = 'src'
BINDIR = 'src/bin'
BINDIR = 'bin'
TESTDIR = 'tests'
VERSION = '3+alpha.01'
#-----------------------------------Configuration
@ -61,7 +61,7 @@ def setupBasicEnvironment():
, SRCDIR=SRCDIR
, BINDIR=BINDIR
, CPPPATH=["#"+SRCDIR] # used to find includes, "#" means always absolute to build-root
, CPPDEFINES=['-DCINELERRA_VERSION=\\"%s\\"' % VERSION ] # note: make it a list to append further defines
, CPPDEFINES=['-DCINELERRA_VERSION='+VERSION ] # note: it's a list to append further defines
, CCFLAGS='-Wall'
)
@ -137,6 +137,7 @@ Special Targets:
build : just compile and link
testcode: additionally compile the Testsuite
check : build and run the Testsuite
doc : generate documetation (Doxygen)
install : install created artifacts at PREFIX
src.tar : create source tarball
doc.tar : create developer doc tarball
@ -174,6 +175,7 @@ def configurePlatform(env):
print 'Did not find the pthread lib or pthread.h, exiting.'
else:
conf.env.Append(CPPFLAGS = ' -DHAVE_PTHREAD_H')
conf.env.Append(CCFLAGS = ' -pthread')
if conf.CheckCHeader('execinfo.h'):
conf.env.Append(CPPFLAGS = ' -DHAS_EXECINFO_H')
@ -183,15 +185,22 @@ def configurePlatform(env):
if not conf.CheckCXXHeader('tr1/memory'):
print 'We rely on the std::tr1 proposed standard extension for shared_ptr.'
Exit(1)
Exit(1)
if not conf.CheckCXXHeader('boost/config.hpp'):
print 'We need the C++ boost-lib.'
Exit(1)
if not conf.CheckCXXHeader('boost/shared_ptr.hpp'):
print 'We need boost::shared_ptr (shared_ptr.hpp).'
Exit(1)
else:
if not conf.CheckCXXHeader('boost/shared_ptr.hpp'):
print 'We need boost::shared_ptr (shared_ptr.hpp).'
Exit(1)
if not conf.CheckLibWithHeader('boost_program_options-mt','boost/program_options.hpp','C++'):
print 'We need boost::program_options (including binary lib for linking).'
Exit(1)
if not conf.CheckLibWithHeader('boost_regex-mt','boost/regex.hpp','C++'):
print 'We need the boost regular expression lib (incl. binary lib for linking).'
Exit(1)
# create new env containing the finished configuration
return conf.Finish()
@ -235,22 +244,41 @@ def defineBuildTargets(env, artifacts):
SConscript(dirs=[TESTDIR], exports='env artifacts corelib')
def defineInstallTargets(env, artifacts):
""" define install locations and cleanup after the build.
def definePostBuildTargets(env, artifacts):
""" define further actions after the core build (e.g. Documentaion).
define alias targets to trigger the installing.
"""
ib = env.Alias('install-bin', '$DESTDIR/bin')
il = env.Alias('install-lib', '$DESTDIR/lib')
env.Alias('install', [ib, il])
build = env.Alias('build', '$BINDIR')
allbu = env.Alias('allbuild', build+artifacts['testsuite'])
env.Default('build')
# additional files to be cleaned when cleaning 'build'
env.Clean ('build', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log'])
# Doxygen documentation
# Note: at the moment we only depend on Doxyfile
# obviousely, we should depend on all sourcefiles
# real Doxygen builder for scons is under developement for 0.97
# so for the moment I prefere not to bother
doxyfile = File('doc/devel/Doxyfile')
env.NoClean(doxyfile)
doxydoc = artifacts['doxydoc'] = [ Dir('doc/devel/html'), Dir('doc/devel/latex') ]
env.Command(doxydoc, doxyfile, "doxygen Doxyfile 2>&1 |tee ,doxylog", chdir='doc/devel')
env.Clean ('doc/devel', doxydoc + ['doc/devel/,doxylog'])
def defineInstallTargets(env, artifacts):
""" define some artifacts to be installed into target locations.
"""
env.Install(dir = '$DESTDIR/bin', source=artifacts['cinelerra'])
env.Install(dir = '$DESTDIR/lib', source=artifacts['plugins'])
env.Install(dir = '$DESTDIR/bin', source=artifacts['tools'])
ib = env.Alias('install-bin', '$DESTDIR/bin')
il = env.Alias('install-lib', '$DESTDIR/lib')
env.Alias('install', [ib, il])
env.Alias('build', '$BINDIR')
env.Default('build')
# additional files to be cleaned when cleaning 'build'
env.Clean ('build', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log'])
env.Install(dir = '$DESTDIR/share/doc/cinelerra$VERSION/devel', source=artifacts['doxydoc'])
#####################################################################
@ -278,5 +306,6 @@ artifacts = {}
definePackagingTargets(env, artifacts)
defineBuildTargets(env, artifacts)
definePostBuildTargets(env, artifacts)
defineInstallTargets(env, artifacts)

View file

@ -46,16 +46,17 @@ def isHelpRequest():
def srcSubtree(env,tree,isShared=False, **args):
def srcSubtree(env,tree,isShared=False,builder=None, **args):
""" convienience wrapper: scans the given subtree, which is
relative to the current SConscript, find all source files and
declare them as Static or SharedObjects for compilation
"""
root = env.subst(tree) # expand Construction Vars
if isShared:
builder = lambda f: env.SharedObject(f, **args)
else:
builder = lambda f: env.Object(f, **args)
if not builder:
if isShared:
builder = lambda f: env.SharedObject(f, **args)
else:
builder = lambda f: env.Object(f, **args)
return [builder(f) for f in scanSrcSubtree(root)]

View file

@ -33,7 +33,7 @@ MULTILINE_CPP_IS_BRIEF = NO
DETAILS_AT_TOP = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 8
TAB_SIZE = 4
ALIASES =
OPTIMIZE_OUTPUT_FOR_C = NO
OPTIMIZE_OUTPUT_JAVA = NO
@ -52,7 +52,7 @@ HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
INTERNAL_DOCS = YES
CASE_SENSE_NAMES = YES
HIDE_SCOPE_NAMES = NO
SHOW_INCLUDE_FILES = YES
@ -74,7 +74,7 @@ FILE_VERSION_FILTER =
#---------------------------------------------------------------------------
QUIET = NO
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_UNDOCUMENTED = NO
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = YES
WARN_FORMAT = "$file:$line: $text"
@ -82,7 +82,8 @@ WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = ../../src/
INPUT = ../../src/ \
../../tests/
FILE_PATTERNS = *.c \
*.cc \
*.cxx \

View file

@ -29,6 +29,8 @@
#include "nobugcfg.h"
#undef NOBUG_INIT_DEFS_
#include <exception>
using util::isnil;
@ -41,10 +43,10 @@ namespace cinelerra
* Appconfig::instance() probably already has been called
* by another compilation unit. This is ugliy, but preferable
* to beeing dependant on inclusion order of headers. */
Appconfig* Appconfig::theApp_ ;
// scoped_ptr<Appconfig> Appconfig::theApp_;
#ifndef CINELERRA_VERSION
#define CINELERRA_VERSION "3++devel"
#define CINELERRA_VERSION 3++devel
#endif
@ -55,38 +57,40 @@ namespace cinelerra
*/
Appconfig::Appconfig()
: configParam_ (new Configmap)
{
//////////
NOBUG_INIT;
//////////
INFO(config, "Basic application configuration triggered.");
(*configParam_)["version"] = CINELERRA_VERSION;
}
{
//////////
NOBUG_INIT;
//////////
INFO(config, "Basic application configuration triggered.");
// install our own handler for undeclared exceptions
std::set_unexpected (cinelerra::error::cinelerra_unexpectedException);
(*configParam_)["version"] = STRINGIFY (CINELERRA_VERSION);
}
/** access the configuation value for a given key.
* @return empty string for unknown keys, else the corresponding configuration value
*/
const string &
Appconfig::get (const string & key) throw()
{
try
{
const string& val = (*instance().configParam_)[key];
WARN_IF( isnil(val), config, "undefined config parameter \"%s\" requested.", key.c_str());
return val;
}
catch (...)
{
ERROR(config, "error while accessing configuration parameter \"%s\".", key.c_str());
}
}
{
try
{
const string& val = (*instance().configParam_)[key];
WARN_IF( isnil(val), config, "undefined config parameter \"%s\" requested.", key.c_str());
return val;
}
catch (...)
{
ERROR(config, "error while accessing configuration parameter \"%s\".", key.c_str());
throw cinelerra::error::Fatal ();
} }

View file

@ -38,48 +38,50 @@
#include <map>
#include <string>
#include <memory>
#include <boost/scoped_ptr.hpp>
#include "nobugcfg.h"
using std::string;
using std::auto_ptr;
namespace cinelerra
{
using std::string;
using boost::scoped_ptr;
/**
* Singleton to hold inevitable global flags and constants
* and for performing early (static) global initialization tasks.
* Appconfig services are available already from static
* initialsation code.
* @warning don't use Appconfig in destuctors.
*/
class Appconfig
{
private:
/** holds the single instance and triggers initialization */
static Appconfig* theApp_;
/** perform initialization on first access.
* A call is placed in static initialization code
* included via cinelerra.h (see below),
* thus it will happen rather early.
*/
Appconfig () ;
* @see #instance() for Lifecycle */
Appconfig ();
Appconfig (const Appconfig&); ///< copy prohibited, not implemented
~Appconfig () throw() {}; ///< deletion prohibited
friend void boost::checked_delete<Appconfig>(Appconfig*);
public:
/** get the (single) Appconfig instance.
* Implemented as Meyers singleton.
* @warning don't use it in destruction code!
*/
static Appconfig& instance()
{
if (!theApp_) theApp_ = new Appconfig ();
return *theApp_;
}
{
static scoped_ptr<Appconfig> theApp_ (0);
if (!theApp_) theApp_.reset (new Appconfig ());
return *theApp_;
}
/** access the configuation value for a given key.
* @return empty string for unknown keys, config value else
* @todo do we need such a facility?
@ -89,10 +91,11 @@ namespace cinelerra
private:
typedef std::map<string,string> Configmap;
typedef auto_ptr<Configmap> PConfig;
typedef std::auto_ptr<Configmap> PConfig;
/** @TODO <b>the following is just placeholder code!</b>
* Appconfig <i>could</i> do such things if necessary.
/** @todo <b>the following is just placeholder code!</b>
* Appconfig <i>could</i> do such things if necessary,
* or provide similar "allways available" services.
*/
PConfig configParam_;
@ -100,12 +103,5 @@ namespace cinelerra
namespace
{
/** "magic code" to cause early static initialization */
Appconfig& init (Appconfig::instance ());
}
} // namespace cinelerra
#endif

85
src/common/cmdline.cpp Normal file
View file

@ -0,0 +1,85 @@
/*
Cmdline - abstraction of the usual commandline, a sequence of strings
Copyright (C) CinelerraCV
2007, Christian Thaeter <ct@pipapo.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
#include "common/cmdline.hpp"
#include "common/util.hpp"
#include "nobugcfg.h"
#include <boost/regex.hpp>
#include <boost/algorithm/string.hpp>
using boost::algorithm::split;
using boost::algorithm::join;
using boost::algorithm::is_any_of;
using boost::algorithm::token_compress_on;
#include <iostream>
namespace util
{
/** create as a tokenized <i>copy</i> of the current commandline.
* Note that argv[0] is allways ignored. */
Cmdline::Cmdline (int argc, char* argv[])
: vector<string> (noneg(argc-1))
{
for (int i=1; i<argc; ++i)
{
ASSERT (argv[i]);
(*this)[i-1] = argv[i];
}
}
/** create by tokenizing a string
* (e.g. "fake" cmdline, separated by whitespace)
*/
Cmdline::Cmdline (const string cmdline)
{
boost::regex tokendef("[^ \r\n\t]+");
boost::smatch match;
string::const_iterator it = cmdline.begin();
string::const_iterator end = cmdline.end();
while (boost::regex_search(it, end, match, tokendef))
{
string ss(match[0]);
this->push_back(ss);
it = match[0].second;
}
}
/** conversion to string by joining the tokens */
Cmdline::operator string () const
{
return join(*this," ");
}
} // namespace cinelerra

70
src/common/cmdline.hpp Normal file
View file

@ -0,0 +1,70 @@
/*
CMDLINE.hpp - abstraction of the usual commandline, a sequence of strings
Copyright (C) CinelerraCV
2007, Christian Thaeter <ct@pipapo.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef UTIL_CMDLINE_H
#define UTIL_CMDLINE_H
#include <vector>
#include <string>
#include <iostream>
namespace util
{
using std::string;
using std::vector;
using std::ostream;
typedef vector<string> VectS;
/**
* Abstraction of the usual "int argc, int** argv"-Commandline,
* to be able to treat it as a vector of strings. Inherits from
* vector<std::string>, but provides convienient conversions to
* string (joining delimited by space)...
*/
class Cmdline : public VectS
{
public:
Cmdline (int argc, char* argv[]);
explicit Cmdline (const string cmdline);
operator string () const;
VectS& operator= (const VectS& source) { return VectS::operator= (source); }
// inherited ctors
template <class In>
Cmdline (In first, In last) : VectS (first,last) {}
Cmdline () : VectS () {}
};
/** for outputting Cmdline objects */
inline ostream& operator<< (ostream& os, const Cmdline& cmdL) { return os << (string (cmdL)); }
} // namespace util
#endif

View file

@ -21,33 +21,174 @@
* *****************************************************/
#include "common/error.hpp"
#include "common/util.hpp"
#include <exception>
#include <typeinfo>
#include <iostream>
using util::isnil;
using std::exception;
namespace cinelerra
{
namespace error
{
/** the message shown to the user per default
* if an exception reaches one of the top-level
* catch clauses.
* @todo to be localized
*/
inline const string default_usermsg (Error* exception_obj) throw()
{
return string("Sorry, Cinelerra encountered an internal error. (")
+ typeid(*exception_obj).name() + ")";
}
/* constants to be used as error IDs */
CINELERRA_ERROR_DEFINE (LOGIC , "internal logic broken");
CINELERRA_ERROR_DEFINE (FATAL , "floundered");
CINELERRA_ERROR_DEFINE (CONFIG , "misconfiguration");
CINELERRA_ERROR_DEFINE (STATE , "unforseen state");
CINELERRA_ERROR_DEFINE (INVALID , "invalid input or parameters");
CINELERRA_ERROR_DEFINE (EXTERNAL , "failure in external service");
CINELERRA_ERROR_DEFINE (ASSERTION, "assertion failure");
} // namespace error
CINELERRA_ERROR_DEFINE (EXCEPTION, "generic cinelerra exception");
/** @note we set the C-style errorstate as a side effect */
Error::Error (string description, const char* id) throw()
: std::exception (),
id_ (id),
msg_ (error::default_usermsg (this)),
desc_ (description),
cause_ ("")
{
cinelerra_error_set (this->id_);
}
Error::Error (std::exception& cause,
string description, const char* id) throw()
: std::exception (),
id_ (id),
msg_ (error::default_usermsg (this)),
desc_ (description),
cause_ (extractCauseMsg(cause))
{
cinelerra_error_set (this->id_);
}
/** @note copy ctor behaves like chaining, i.e setting the cause_. */
Error::Error (const Error& ref) throw()
: std::exception (),
id_ (ref.id_),
msg_ (ref.msg_),
desc_ (ref.desc_),
cause_ (extractCauseMsg(ref))
{ }
/** Description of the problem, including the internal char constant
* in accordance to cinelerras error identification scheme.
* If a ::rootCause() can be obtained, this will be included in the
* in accordance to cinelerra's error identification scheme.
* If a root cause can be obtained, this will be included in the
* generated output as well.
*/
const char*
Error::what () const throw()
{
}
Error::what() const throw()
{
if (isnil (this->what_))
{
what_ = string(id_);
if (!isnil (desc_)) what_ += " ("+desc_+").";
if (!isnil (cause_)) what_ += string(" -- caused by: ") + cause_;
}
return what_.c_str();
}
/** If this exception was caused by a chain of further exceptions,
* return the first one registered in this throw sequence.
* This works only, if every exceptions thrown as a consequence
* of another exception is propperly constructed by passing
* the original exception to the constructor
/** @internal get at the description message of the
* first exception encountered in a chain of exceptions
*/
std::exception
Error::rootCause() const throw()
const string
Error::extractCauseMsg (const exception& cause) throw()
{
const Error* err=dynamic_cast<const Error*> (&cause);
if (err)
if (isnil (err->cause_))
return cause.what(); // cause is root cause
else
return err->cause_; // cause was caused by another exception
// unknown other exception type
return cause.what ();
}
/* -- originally, I wanted to chain the exception objects themselfs.
but this doesn't work; we'd need to clone the "cause" error object,
because it can be destroyed when leaving the original
handler by throwing a new exception.
Anyways, not needed at the moment; maybe later? 8/2007
const exception&
Error::rootCause () const throw()
{
const exception * root(this);
if (this->cause)
if (Error* err = dynamic_cast<Error*> (this->cause))
root = &err->rootCause ();
else
root = this->cause;
ENSURE (root);
ENSURE (root!=this || !cause);
return *root;
}
*/
namespace error
{
void cinelerra_unexpectedException () throw()
{
const char* is_halted
= "### Cinelerra halted due to an unexpected Error ###";
std::cerr << "\n" << is_halted << "\n\n";
ERROR (NOBUG_ON, "%s", is_halted);
if (const char * errorstate = cinelerra_error ())
ERROR (NOBUG_ON, "last registered error was....\n%s", errorstate);
std::terminate();
}
void assertion_terminate (const string& location)
{
throw Fatal (location, CINELERRA_ERROR_ASSERTION)
.setUsermsg("Program terminated because of violating "
"an internal consistency check.");
}
} // namespace error
} // namespace cinelerra

View file

@ -21,44 +21,75 @@
*/
#ifndef CINELERRA_ERROR_H
#define CINELERRA_ERROR_H
#ifndef CINELERRA_ERROR_HPP_
#define CINELERRA_ERROR_HPP_
#include <exception>
#include <string>
#include "nobugcfg.h"
#include "lib/error.h"
namespace cinelerra
{
using std::string;
/** error-ID for unspecified exceptions */
CINELERRA_ERROR_DECLARE(EXCEPTION);
/**
* Interface and Baseclass of all Exceptions thrown
* from within cinelerra (C++) code. Common operations
* for getting an diagnostic message and for obtaining
* the root cause, i.e. the frist exception encountered
* the root cause, i.e. the first exception encountered
* in a chain of exceptions.
*/
class Error : public std::exception
{
public:
Error (string description="", const char* id=CINELERRA_ERROR_EXCEPTION) throw();
Error (std::exception& cause,
string description="", const char* id=CINELERRA_ERROR_EXCEPTION) throw();
Error (const Error&) throw();
virtual ~Error () throw() {};
/** yield a diagnostig message characterizing the problem */
/** yield a diagnostic message characterizing the problem */
virtual const char* what () const throw();
/** the internal cinelerra-error-ID (was set as C-errorstate in ctor) */
const char* getID () const throw() { return this->id_; }
/** extract the message to be displayed for the user */
const string& getUsermsg () const throw();
/** If this exception was caused by a chain of further exceptions,
* return the first one registered in this throw sequence.
* This works only, if every exceptions thrown as a consequence
* of another exception is propperly constructed by passing
* the original exception to the constructor
* return the description of the first one registered in this throw sequence.
* This works only if every exceptions thrown as a consequence of another exception
* is propperly constructed by passing the original exception to the constructor
* @return the description string, maybe empty (if there is no known root cause)
*/
std::exception rootCause () const throw();
const string& rootCause () const throw() { return this->cause_; }
/** replace the previous or default friendly message for the user. To be localized. */
Error& setUsermsg (const string& newMsg) throw() { this->msg_ = newMsg; return *this; }
/** give additional developer info. Typically used at intermediate handlers to add context. */
Error& prependInfo (const string& text) throw() { this->desc_.insert (0,text); return *this; }
private:
/** a copy of the first exception encountered in this exception chain */
std::exception cause;
const char* id_; ///< an CINELERRA_ERROR id, which is set as errorstate on construction
string msg_; ///< friendly message intended for users (to be localized)
string desc_; ///< detailed description of the error situation for the developers
mutable string what_; ///< buffer for generating the detailed description on demand
const string cause_; ///< descriptoin of first exception encountered in the chain
static const string extractCauseMsg (const std::exception&) throw();
};
@ -68,35 +99,74 @@ namespace cinelerra
namespace error
{
class Logic : public Error
{
/** global function for handling unknown exceptions
* encountered at functions declaring not to throw
* this kind of exception. Basically, any such event
* can be considered a severe design flaw; we can just
* add some diagnostics prior to halting.
*/
void cinelerra_unexpectedException () throw();
/** throw an error::Fatal indicating "assertion failure" */
void assertion_terminate (const string& location);
/* constants to be used as error IDs */
CINELERRA_ERROR_DECLARE (LOGIC ); ///< contradiction to internal logic assumptions detected
CINELERRA_ERROR_DECLARE (FATAL ); ///< unable to cope with, internal logic floundered
CINELERRA_ERROR_DECLARE (CONFIG ); ///< execution aborted due to misconfiguration
CINELERRA_ERROR_DECLARE (STATE ); ///< unforeseen internal state
CINELERRA_ERROR_DECLARE (INVALID ); ///< invalid input or parameters encountered
CINELERRA_ERROR_DECLARE (EXTERNAL ); ///< failure in external service the application relies on
CINELERRA_ERROR_DECLARE (ASSERTION); ///< assertion failure
/** Macro for creating derived exception classes properly
* integrated into cinelerra's exception hierarchy. Using
* this macro asures that the new class will get the full
* set of constructors and behaviour common to all exception
* classes, so it should be used when creating an derived
* exception type for more then stricly local purposes
*/
#define CINELERRA_EXCEPTION_DECLARE(CLASS, PARENT, _ID_) \
class CLASS : public PARENT \
{ \
public: \
CLASS (std::string description="", \
const char* id=_ID_) throw() \
: PARENT (description, id) {} \
\
CLASS (std::exception& cause, \
std::string description="", \
const char* id=_ID_) throw() \
: PARENT (cause, description, id) {} \
};
class Config : public Error
{
};
class State : public Error
{
};
class Invalid : public Error
{
};
class External : public Error
{
};
//---------------------------CLASS-----PARENT--ID----------------------
CINELERRA_EXCEPTION_DECLARE (Logic, Error, CINELERRA_ERROR_LOGIC);
CINELERRA_EXCEPTION_DECLARE (Fatal, Logic, CINELERRA_ERROR_FATAL);
CINELERRA_EXCEPTION_DECLARE (Config, Error, CINELERRA_ERROR_CONFIG);
CINELERRA_EXCEPTION_DECLARE (State, Error, CINELERRA_ERROR_STATE);
CINELERRA_EXCEPTION_DECLARE (Invalid, Error, CINELERRA_ERROR_INVALID);
CINELERRA_EXCEPTION_DECLARE (External, Error, CINELERRA_ERROR_EXTERNAL);
} // namespace error
} // namespace cinelerra
/******************************************************
* if NoBug is used, redefine some macros
* to rather throw Cinelerra Errors instead of aborting
*/
#ifdef NOBUG_ABORT
#undef NOBUG_ABORT
#define CIN_NOBUG_LOCATION \
std::string (NOBUG_BASENAME(__FILE__)) +":"+ NOBUG_STRINGIZE(__LINE__) + ", function " + __func__
#define NOBUG_ABORT \
cinelerra::error::assertion_terminate (CIN_NOBUG_LOCATION);
#endif
#endif // CINELERRA_ERROR_HPP_

View file

@ -60,6 +60,8 @@ namespace cinelerra
* Note: non-virtual.
*/
SMP<T> operator() (){ return SMP<T> (new T ); };
typedef SMP<T> ptype;
private:
void operator= (const Factory&); // copy prohibited
@ -102,7 +104,7 @@ namespace cinelerra
*/
static void destroy (T* victim) { delete victim; };
public:
public:
shared_ptr<T> operator() () { return shared_ptr<T> (new T, &destroy ); }
};

View file

@ -28,7 +28,8 @@
#include <vector>
#include <string>
#include "helper/suite.hpp"
#include "common/test/suite.hpp"
#include "common/util.hpp"
namespace test
@ -37,7 +38,7 @@ namespace test
using std::string;
using std::auto_ptr;
typedef std::vector<string> * Arg;
typedef std::vector<string> & Arg;
@ -89,4 +90,10 @@ using ::test::Arg;
using ::test::Test;
using ::test::Launch;
// and provide shortcut for registration
#define LAUNCHER(_TEST_CLASS_, _GROUPS_) \
/** Register _TEST_CLASS_ to be invoked in some test suites (groups) _GROUPS_ */ \
Launch<_TEST_CLASS_> run_##_TEST_CLASS_##_(STRINGIFY(_TEST_CLASS_), _GROUPS_);
#endif

213
src/common/test/suite.cpp Normal file
View file

@ -0,0 +1,213 @@
/*
Suite - helper class for running collections of tests
Copyright (C) CinelerraCV
2007, Christian Thaeter <ct@pipapo.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
#include <map>
#include <vector>
#include <memory>
#include <tr1/memory>
#include <iostream>
#include <sstream>
#include <boost/algorithm/string.hpp>
#include "nobugcfg.h"
#include "common/cmdline.hpp"
#include "common/test/suite.hpp"
#include "common/test/run.hpp"
#include "common/error.hpp"
#include "common/util.hpp"
namespace test
{
using std::map;
using std::vector;
using std::auto_ptr;
using std::tr1::shared_ptr;
using boost::algorithm::trim;
using util::isnil;
using util::contains;
typedef map<string, Launcher*> TestMap;
typedef shared_ptr<TestMap> PTestMap;
typedef map<string,PTestMap> GroupMap;
/** helper to collect and manage the test cases.
* Every testcase class should create a Launch instance
* which causes a call to Suite::enroll(), so we can add a
* pointer to this Launcher into a map indexed by the
* provided testIDs and groupIDs.
* This enables us to build a Suite instance for any
* requested group and then instantiiate and invoke
* individual testcases accordingly.
*/
class Registry
{
auto_ptr<GroupMap> groups;
public:
Registry() : groups(new GroupMap ) {};
PTestMap& getGroup (string grpID) { return (*groups)[grpID]; };
void add2group (Launcher* test, string testID, string groupID);
};
void
Registry::add2group (Launcher* test, string testID, string groupID)
{
REQUIRE( test );
REQUIRE( !isnil(testID) );
REQUIRE( !isnil(groupID) );
PTestMap& group = getGroup(groupID);
if (!group)
group.reset( new TestMap );
(*group)[testID] = test;
}
Registry testcases;
/** register the given test-launcher, so it can be later accessed
* either as a member of one of the specified groups, or direcly
* by its testID. Any test is automatically added to the groupID
* #ALLGROUP
* @param test the Launcher object used to run this test
* @param testID unique ID to refere to this test (will be used as std::map key)
* @param groups List of group-IDs selected by whitespace
*
*/
void
Suite::enroll (Launcher* test, string testID, string groups)
{
REQUIRE( test );
REQUIRE( !isnil(testID) );
std::istringstream ss(groups);
string group;
while (ss >> group )
testcases.add2group(test, testID, group);
// Magic: allways add any testcas to groupID="ALL"
testcases.add2group(test,testID, ALLGROUP);
}
/** "magic" groupID containing all registered testcases */
const string Suite::ALLGROUP = "ALL";
/** create a suite comprised of all the testcases
* previously @link #enroll() registered @endlink with this
* this group.
* @see #run() running tests in a Suite
*/
Suite::Suite(string groupID)
: groupID_(groupID)
{
REQUIRE( !isnil(groupID) );
TRACE(test, "Test-Suite( groupID=%s )\n", groupID.c_str () );
if (!testcases.getGroup(groupID))
throw cinelerra::error::Invalid ();
//throw "empty testsuite"; /////////// TODO Errorhandling!
}
#define VALID(test,testID) \
ASSERT ((test), "NULL testcase laucher for test '%s' found in testsuite '%s'", groupID_.c_str(),testID.c_str());
/** run all testcases contained in this Suite.
* The first argument in the commandline, if present,
* will select one single testcase with a matching ID.
* In case of invoking a single testcase, the given cmdline
* will be forwarded to the testcase, after removind the
* testcaseID from cmdline[0]. Otherwise, every testcase
* in this suite is invoked with a empty cmdline vector.
* @param cmdline ref to the vector of commandline tokens
*/
void
Suite::run (Arg cmdline)
{
PTestMap tests = testcases.getGroup(groupID_);
if (!tests)
throw cinelerra::error::Invalid (); ///////// TODO: pass error description
if (0 < cmdline.size())
{
string& testID (cmdline[0]);
trim(testID);
if ( contains (*tests, testID))
{
// first cmdline argument denotes a valid testcase registered in
// this group: invoke just this test with the remaining cmdline
Launcher* test = (*tests)[testID];
cmdline.erase (cmdline.begin());
VALID (test,testID);
(*test)()->run(cmdline);
return;
} }
// no test-ID was specified.
// instantiiate all tests cases and execute them.
for ( TestMap::iterator i=tests->begin(); i!=tests->end(); ++i )
{
std::cout << "\n ----------"<< i->first<< "----------\n";
Launcher* test = (i->second);
VALID (test, i->first);
(*test)()->run(cmdline); // actually no cmdline arguments
}
}
/** print to stdout an ennumeration of all testcases in this suite,
* in a format suitable for use with Cehteh's ./test.sh
*/
void
Suite::describe ()
{
util::Cmdline noCmdline("");
PTestMap tests = testcases.getGroup(groupID_);
ASSERT (tests);
std::cout << "TESTING \"Component Test Suite: " << groupID_ << "\" ./test-components\n\n";
for ( TestMap::iterator i=tests->begin(); i!=tests->end(); ++i )
{
string key (i->first);
std::cout << "\n\n";
std::cout << "TEST \""<<key<<"\" "<<key<<" <<END\n";
Launcher* test = (i->second);
VALID (test, i->first);
(*test)()->run(noCmdline); // run it to insert test generated output
std::cout << "END\n";
}
}
} // namespace test

View file

@ -24,6 +24,7 @@
#ifndef TESTHELPER_SUITE_H
#define TESTHELPER_SUITE_H
#include <vector>
#include <string>
@ -35,12 +36,16 @@ namespace test
// Forward decls needed for run.hpp
class Test;
class Launcher;
typedef std::vector<string> & Arg;
/**
* Helper class for running a collection of tests.
*
* Enables running a collection of tests.
* An internal registration service #enroll() is provided
* for the individual Test - inscances to be recognized as
* testcases. The groupID passed to the constructor selects
* all testcases declared as belonging to this Group.
*/
class Suite
{
@ -48,13 +53,13 @@ namespace test
public:
Suite (string groupID);
void run (int argc, char* argv[]);
void run (Arg cmdline);
void describe ();
static void enroll (Launcher *test, string testID, string groups);
static const string ALLGROUP;
};
} // namespace test
#endif

View file

@ -0,0 +1,126 @@
/*
Suite - helper class for running collections of tests
Copyright (C) CinelerraCV
2007, Christian Thaeter <ct@pipapo.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
#include "common/test/testoption.hpp"
#include "common/test/suite.hpp"
#include "nobugcfg.h"
#include "common/error.hpp"
typedef boost::program_options::options_description Syntax;
typedef boost::program_options::variables_map VarMap;
namespace op = boost::program_options;
using util::VectS;
namespace test
{
/** set up an options parser to use the current commandline.
* reconizes the following options
* \code
* --help
* --group <groupID>
* \endcode
*/
TestOption::TestOption (util::Cmdline& cmdline)
: syntax("Run a collection of test cases. Supported parameters"),
parameters()
{
syntax.add_options()
("help,h", "produce help message")
("group,g", op::value<string>()->default_value(Suite::ALLGROUP),
"the group (selection) of testcases to execute")
("describe", op::bool_switch(),
"ennumerate all testcases in this Suite in a format usable with ./test.sh.")
("id", op::value<VectS>(),
"an individual testcase to be called.\nIf not specified, run all.")
;
// the testcase-ID is really an positional parameter
op::positional_options_description posopt;
posopt.add("id", -1);
op::parsed_options parsed =
op::command_line_parser (cmdline)
.options (syntax)
.positional(posopt)
.allow_unregistered()
.run();
op::store (parsed, parameters);
op::notify(parameters);
// remove all recognized options from original cmdline vector
cmdline = op::collect_unrecognized(parsed.options, op::include_positional);
if (parameters.count("help"))
std::cerr << *this;
}
/** @return the Tests-Group as given on cmdline, or Suite::ALLGROUP as default
*/
const string
TestOption::getTestgroup ()
{
ASSERT (parameters.count ("group"));
return parameters["group"].as<string>();
}
/** @return ID of a single test to run, empty string if not specified
*/
const string
TestOption::getTestID ()
{
if (parameters.count ("id") &&
parameters["id"].as<VectS>().size() > 0)
return parameters["id"].as<VectS>()[0];
else
return string ();
}
/** @return \c true if --describe switch was given */
const bool
TestOption::getDescribe ()
{
return parameters["describe"].as<bool>();
}
ostream&
operator<< (ostream& os, const TestOption& to)
{
return os << to.syntax;
}
} // namespace test

View file

@ -0,0 +1,75 @@
/*
TESTOPTION.hpp - handle cmdline for invoking Testsuite
Copyright (C) CinelerraCV
2007, Christian Thaeter <ct@pipapo.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef TESTHELPER_TESTOPTION_H
#define TESTHELPER_TESTOPTION_H
#include "common/cmdline.hpp"
#include <string>
#include <iostream>
#include <boost/program_options.hpp>
#include <boost/utility.hpp>
namespace test
{
using std::string;
using std::ostream;
/**
* Support for selecting and configuring testcases
* via commandline arguments. A preconfigured wrapper
* around boost::program_options, with the ability
* to tolerate unknown options. The commandline
* to be parsed is taken wrapped into a Cmdline
* instance; after parsing this commandline
* vector will contain only the remaining
* unrecognized parts.
*/
class TestOption : private boost::noncopyable
{
public:
TestOption (util::Cmdline& cmdline);
const string getTestgroup ();
const string getTestID ();
const bool getDescribe ();
private:
boost::program_options::options_description syntax;
boost::program_options::variables_map parameters;
friend ostream& operator<< (ostream&, const TestOption&);
};
/** for outputting the help messages. Forward accummulated
* help messages from all contained option defintions */
ostream& operator<< (ostream& os, const TestOption& to);
} // namespace test
#endif

View file

@ -1,5 +1,5 @@
/*
TIME.hpp - unified representation of a time point, including conversion functions
UTIL.hpp - collection of small helper functions used "everywhere"
Copyright (C) CinelerraCV
2007, Christian Thaeter <ct@pipapo.org>
@ -33,21 +33,60 @@ namespace util
using std::string;
/** a family of util functions providing a "no value whatsoever" test */
inline bool isnil(const string& val)
{
return 0 == val.length();
}
inline bool isnil(const string* pval)
{
return !pval || 0 == pval->length();
}
inline bool isnil(const char* pval)
{
return !pval || 0 == std::strlen(pval);
}
/** a family of util functions providing a "no value whatsoever" test.
Works on strings and all STL containers, includes NULL test for pointers */
template <class CONT>
inline bool isnil(const CONT& container)
{
return container.empty();
}
template <class CONT>
inline bool isnil(const CONT* pContainer)
{
return !pContainer || pContainer->empty();
}
template <>
inline bool isnil(const char* pCStr)
{
return !pCStr || 0 == std::strlen(pCStr);
}
/** cut a numeric value to be >=0 */
template <typename NUM>
inline NUM noneg (NUM val)
{
return (0<val? val : 0);
}
/** shortcut for containment test on a map */
template <typename MAP>
inline bool contains (MAP& map, typename MAP::key_type& key)
{
return map.find(key) != map.end();
}
/** shortcut for operating on all elements of a container.
* Isn't this already defined somewhere? It's so obvious..
*/
template <typename Container, typename Oper>
inline Oper
for_each (Container& c, Oper& doIt)
{
return std::for_each (c.begin(),c.end(), doIt);
}
} // namespace util
/* some common macro definitions */
/** this macro wraps its parameter into a cstring literal */
#define STRINGIFY(TOKEN) __STRNGFY(TOKEN)
#define __STRNGFY(TOKEN) #TOKEN
#endif /*UTIL_HPP_*/

View file

@ -21,6 +21,12 @@
#ifndef CINELERRA_ERROR_H
#define CINELERRA_ERROR_H
#ifdef __cplusplus
extern "C" {
#elif 0
} /*eek, fixes emacs indenting for now*/
#endif
#include <nobug.h>
#include <stdlib.h>
@ -78,5 +84,7 @@ cinelerra_error_set (const char * err);
const char*
cinelerra_error ();
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* CINELERRA_ERROR_H */

View file

@ -31,9 +31,9 @@ using cinelerra::Appconfig;
int main (int argc, char* argv[])
{
cout << "*** Cinelerra NLE for Linux ***" << endl
<< " Version: " << Appconfig::get("version") << endl;
assert(true);
return 0;
}
{
cout << "*** Cinelerra NLE for Linux ***" << endl
<< " Version: " << Appconfig::get("version") << endl;
assert(true);
return 0;
}

View file

@ -75,7 +75,8 @@
NOBUG_CPP_DEFINE_FLAG(config);
NOBUG_CPP_DEFINE_FLAG(test);
#include "common/error.hpp"
#endif /* ===================== (End) C++-Part ============= */

1
tests/.gitignore vendored
View file

@ -1,4 +1,5 @@
,*
test-*
mainsuite
errortest
plugin-example

View file

@ -1,12 +1,107 @@
TESTING "Component Test Suite: ALL" ./test-components
TESTING "Component Test Suite" ./test-components
TEST "Fac test" Factory_test <<END
return: 0
END
TEST "Hello test" HelloWorld_test <<END
TEST "Hello test" HelloWorld_test 3 <<END
out: This is how the world ends...
out: This is how the world ends...
out: This is how the world ends...
return: 0
END
TEST "Appconfig_test" Appconfig_test <<END
return: 0
END
TEST "CmdlineWrapper_test" CmdlineWrapper_test <<END
out: wrapping cmdline:...
out: -->
out: wrapping cmdline:
out: ...
out: -->
out: wrapping cmdline:spam...
out: 0|spam|
out: -->spam
out: wrapping cmdline:
out: spam...
out: 0|spam|
out: -->spam
out: wrapping cmdline:eat more spam...
out: 0|eat|
out: 1|more|
out: 2|spam|
out: -->eat more spam
out: wrapping cmdline: oo _O()O_ ä + €...
out: 0|oo|
out: 1|_O()O_|
out: 2|ä|
out: 3|+|
out: 4|€|
out: -->oo _O()O_ ä + €
out: wrapping cmdline:...
out: -->
out: Standard Cmdlineformat:one two
END
TEST "ExceptionError_test" ExceptionError_test <<END
out: caught: CINELERRA_ERROR_LIFE_AND_UNIVERSE:and everything? (don't panic)...the answer is: 42
out: caught cinelerra::Error: CINELERRA_ERROR_DERIVED:convoluted exception
out: caught error::Logic: CINELERRA_ERROR_FATAL:floundered (test-2).
out: caught error::Invalid: CINELERRA_ERROR_INVALID:invalid input or parameters (test-3).
out: caught cinelerra::Error: CINELERRA_ERROR_EXTERNAL:failure in external service (test-4).
out: caught std::runtime_error: test-5
out: caught std::exception: St9exception
out: intermediate handler caught: CINELERRA_ERROR_EXTERNAL:failure in external service (test-7).....will rethrow as error::State
out: caught cinelerra::Error: CINELERRA_ERROR_STATE:unforseen state -- caused by: CINELERRA_ERROR_EXTERNAL:failure in external service (test-7).
out: intermediate handler caught: CINELERRA_ERROR_EXTERNAL:failure in external service (test-8).....will rethrow as error::State
out: 2nd intermediate handler caught: CINELERRA_ERROR_STATE:unforseen state -- caused by: CINELERRA_ERROR_EXTERNAL:failure in external service (test-8).....will rethrow as error::Config
out: caught cinelerra::Error: CINELERRA_ERROR_CONFIG:misconfiguration -- caused by: CINELERRA_ERROR_EXTERNAL:failure in external service (test-8).
END
TEST "Factory_test" Factory_test 5 <<END
out: ctor TargetObj(5) successfull
out: now the smart-ptr has refcount=3
out: .....TargetObj(5): data="*****", array[5]={0,1,2,3,4,}
out: dtor ~TargetObj(5) successfull
END
TEST "TestOption_test" TestOption_test <<END
out: Testing invocation with cmdline: ...
out: --> Testgroup=ALL
out: --> Test-ID =--missing--
out: --> remaining=
out: Testing invocation with cmdline: --help...
out: --> Testgroup=ALL
out: --> Test-ID =--missing--
out: --> remaining=
out: Testing invocation with cmdline: --group TestGroupID...
out: --> Testgroup=TestGroupID
out: --> Test-ID =--missing--
out: --> remaining=
out: Testing invocation with cmdline: SingleTestID...
out: --> Testgroup=ALL
out: --> Test-ID =SingleTestID
out: --> remaining=SingleTestID
out: Testing invocation with cmdline: SingleTestID --group TestGroupID...
out: --> Testgroup=TestGroupID
out: --> Test-ID =SingleTestID
out: --> remaining=SingleTestID
out: Testing invocation with cmdline: --group TestGroupID SingleTestID ...
out: --> Testgroup=TestGroupID
out: --> Test-ID =SingleTestID
out: --> remaining=SingleTestID
out: Testing invocation with cmdline: --group TestGroupID SingleTestID spam eggs...
out: --> Testgroup=TestGroupID
out: --> Test-ID =SingleTestID
out: --> remaining=SingleTestID spam eggs
out: Testing invocation with cmdline: SingleTestID spam --group TestGroupID --eggs...
out: --> Testgroup=TestGroupID
out: --> Test-ID =SingleTestID
out: --> remaining=SingleTestID spam --eggs
END

View file

@ -26,8 +26,13 @@ def treatPluginTestcase(env):
""" Special case: the test-plugin executable
"""
env = env.Clone()
env.Append(CPPPATH='plugin')
testplugin = env.SharedLibrary('hello_1', 'plugin/example_plugin.c', SHLIBPREFIX='')
env.Append(CPPPATH='plugin')
prfx = 'plugin/example_plugin'
oC = env.SharedObject(prfx, prfx+'.c')
oCPP = env.SharedObject(prfx+'_cpp', prfx+'.cpp')
testplugin = ( env.SharedLibrary('.libs/example_plugin', oC, SHLIBPREFIX='')
+ env.SharedLibrary('.libs/example_plugin_cpp', oCPP, SHLIBPREFIX='')
)
testExe = env.Program('test-plugin', ['plugin/plugin_main.c'] + corelib)
env.Depends(testExe, testplugin)
return testExe
@ -46,7 +51,6 @@ moduledirs = globRootdirs('*')
isnt_plugin = lambda dir : dir!='plugin'
moduledirs = filter(isnt_plugin, moduledirs)
pluginExe = treatPluginTestcase(env)
print 'moduledirs: %s' %moduledirs
artifacts['testsuite'] = ts = [ SingleTestExecutableSubdir(env, dir) for dir in moduledirs] + pluginExe
@ -62,8 +66,7 @@ artifacts['testsuite'] = ts = [ SingleTestExecutableSubdir(env, dir) for dir in
# - it depends on all artifacts defined as "ts" above
#
runTs = env.Command(',testlog', ts, "./test.sh", chdir=1)
env.Clean (runTs, [ ',*']) # declare tempfiles of test.sh as cleanable
#
@ -74,3 +77,5 @@ env.Clean (runTs, [ ',*']) # declare tempfiles of test.sh as cleanable
env.Alias('testcode', ts )
env.Alias('check', runTs )
# declare tempfiles of test.sh as cleanable
env.Clean ('check', [',testlog.pre',',expect_stdout',',stdout',',stderr',',testtmp','.libs'])

View file

@ -0,0 +1,64 @@
/*
Appconfig(Test) - accessing the allwasy-available Appconfig singleton
Copyright (C) CinelerraCV
2007, Christian Thaeter <ct@pipapo.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
#include "nobugcfg.h"
#include "common/appconfig.hpp"
#include "common/test/run.hpp"
#include "common/util.hpp"
#include <iostream>
using std::cout;
namespace cinelerra
{
namespace test
{
class Appconfig_test : public Test
{
virtual void run (Arg arg)
{
testAccess("version");
}
/** @test accessing a value from cinelerra::Appconfig */
void testAccess (const string& key)
{
string ver = cinelerra::Appconfig::get(key);
ASSERT ( !util::isnil(ver));
}
};
LAUNCHER (Appconfig_test, "function common");
} // namespace test
} // namespace util

View file

@ -0,0 +1,239 @@
/*
Exceptionhandlin(Test) - throwing and catching our exception type
Copyright (C) CinelerraCV
2007, Christian Thaeter <ct@pipapo.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
#include "lib/error.h"
#include "common/error.hpp"
#include "common/appconfig.hpp"
#include "common/test/run.hpp"
#include "common/util.hpp"
#include <string>
#include <iostream>
#include <exception>
#include <stdexcept>
using std::runtime_error;
using std::exception;
using std::string;
using std::cout;
namespace cinelerra
{
namespace test
{
/** local specific error-constant for use in the
* construcor of the nested SpecificError class.
*/
CINELERRA_ERROR_DEFINE(LIFE_AND_UNIVERSE, "and everything?");
CINELERRA_ERROR_DEFINE(DERIVED, "convoluted exception");
/** declare a specific Error class with parent class error::external */
CINELERRA_EXCEPTION_DECLARE (DerivedError, error::External, CINELERRA_ERROR_DERIVED);
/**********************************************************
* Some aspects of C++ exception handling.
* Not to be confused with the basic C-style error value
* mechanism used by the low-level parts of the backend.
* Both approaches are laregely orthogonal, but the
* C++ exception handling uses the C-style error constants.
*
*/
class ExceptionError_test : public Test
{
typedef ExceptionError_test test;
virtual void run (Arg arg)
{
if (0 < arg.size() && arg[1]=="terminate")
terminateUnknown ();
catcher (&test::throwSpecial, "");
catcher (&test::throwDerived, "test-1");
catcher (&test::throwFatal, "test-2");
catcher (&test::throwInvalid, "test-3");
catcher (&test::throwExternal, "test-4");
catcher (&test::throwRuntime, "test-5");
catcher (&test::throwExceptn, "test-6");
catcher (&test::nestedThrower, "test-7");
catcher (&test::doubleNestedTh,"test-8");
checkErrorIntegration();
checkRootCauseChaining();
}
/** @test simply throw some exception and pass context info */
void throwSpecial (string _) { throw SpecificError(); }
void throwDerived (string _) { throw DerivedError(); }
void throwFatal (string _) { throw error::Fatal(_); }
void throwInvalid (string _) { throw error::Invalid(_); }
void throwExternal(string _) { throw error::External(_); }
void throwRuntime (string _) { throw std::runtime_error(_); }
void throwExceptn (string _) { throw std::exception(error::State(_)); }
/** @test catching, repackaging and rethrowing of errors.
* This feature is important for passing exceptions transparentely
* over several layers. The nested operation will throw an error::External,
* which we are able to catch because it is derived from std::exception.
* We don't need to know the exact type, but we can classify the error situation
* as a "state error" and throw an error::State, passing on the root cause.
* Some levels up, this error get caught and the root cause can be
* extracted successfully.
*/
void nestedThrower (string msg) throw(Error)
{
try { throwExternal(msg); }
catch (std::exception& e)
{
cout << "intermediate handler caught: " << e.what()
<< "....will rethrow as error::State\n";
throw error::State (e);
}
}
/** @test repeated repackaging and rethrowing */
void doubleNestedTh (string msg) throw(error::Config)
{
try { nestedThrower (msg); }
catch (Error& e)
{
cout << "2nd intermediate handler caught: " << e.what()
<< "....will rethrow as error::Config\n";
throw error::Config (e);
}
}
/** @test by constructing an cinelerra::Error object,
* the corresponding cinelerra_error state is set automatically
*/
void checkErrorIntegration()
{
cinelerra_error ();
ASSERT (!cinelerra_error ());
Error err1;
Error err2("boo",CINELERRA_ERROR_DERIVED);
ASSERT (err1.getID () == cinelerra_error ()); // (we didn't clear the first one!)
Error err3("boooo",CINELERRA_ERROR_DERIVED);
ASSERT (err3.getID () == cinelerra_error ());
SpecificError err4;
ASSERT (err4.getID () == CINELERRA_ERROR_LIFE_AND_UNIVERSE);
ASSERT (err4.getID () == cinelerra_error ());
ASSERT (!cinelerra_error ());
}
/** @test the chaining of cinelerra::Exception objects
* and the retrieval of the original root cause.
*/
void checkRootCauseChaining()
{
error::Logic err1;
error::Config err2(err1);
error::Config err3(err2); //note: using copy ctor behaves like chaining
Error err4(err1); // note: copy ctor
std::runtime_error rerr("what a shame");
error::External err5(rerr);
Error err6(err5);
ASSERT (err2.rootCause() == err1.what());
ASSERT (err3.rootCause() == err1.what());
ASSERT (err4.rootCause() == err1.what());
ASSERT (err5.rootCause() == rerr.what());
ASSERT (err6.rootCause() == rerr.what());
}
/** @test terminate the Application by throwing an undclared exception.
* this should result in the global unknown() handler to be called,
* so usually it will terminate the testrun.
* @note because we call Appconfig::instance(), our own unknown() handler
* gets installed and invoked, which gives additional diagnostics.*/
void terminateUnknown () throw()
{
cinelerra::Appconfig::instance();
// will cause initialisation of Appconfig,
throw Error("You'll never get me, won't you?");
}
/** a very specific Exception class
* local to this scope and with
* additional behaviour.
*/
class SpecificError : public error::Invalid
{
int value;
public:
SpecificError () : Invalid("don't panic",CINELERRA_ERROR_LIFE_AND_UNIVERSE), value(42) {}
int revealIt () { return value; }
};
/** helper: provides a bunch of catch-clauses and
* runs the given member functions within
*/
void catcher (void (ExceptionError_test::*funky)(string), string context)
{
try
{
(this->*funky) (context);
}
catch (SpecificError& e) { cout << "caught: " << e.what() << "..the answer is: " << e.revealIt() << "\n"; }
catch (error::Logic& e) { cout << "caught error::Logic: " << e.what() << "\n"; }
catch (error::Invalid&e) { cout << "caught error::Invalid: " << e.what() << "\n"; }
catch (Error& e) { cout << "caught cinelerra::Error: " << e.what() << "\n"; }
catch (runtime_error& e) { cout << "caught std::runtime_error: " << e.what() << "\n"; }
catch (exception& e) { cout << "caught std::exception: " << e.what() << "\n"; }
catch (...) { cout << "caught an unknown exception\n"; }
}
};
/** register this test class... */
LAUNCHER (ExceptionError_test, "function common");
} // namespace test
} // namespace util

View file

@ -21,7 +21,21 @@
* *****************************************************/
#include "helper/run.hpp"
#include "common/test/run.hpp"
#include "common/factory.hpp"
#include "common/util.hpp"
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>
#include <iostream>
using boost::algorithm::join;
using boost::lexical_cast;
using boost::format;
using util::isnil;
using std::string;
using std::cout;
namespace cinelerra
@ -29,23 +43,122 @@ namespace cinelerra
namespace test
{
class ObjFactory;
/**
* Target object to be created by the Test-Factory
* Target object to be created by the Test-Factory.
* Allocates a variable amount of additional heap memory
* and prints diagnostic messages.
*/
class TargetObj
{
uint cnt_;
string* heapData_;
string* heapArray_;
TargetObj(uint num)
: cnt_ (num),
heapData_ (new string(num,'*')),
heapArray_ (new string[num])
{
for (uint i=0; i<cnt_; ++i)
heapArray_[i] = lexical_cast<string>(i);
cout << format("ctor TargetObj(%i) successfull\n") % cnt_;
}
~TargetObj() throw()
{
delete heapData_;
delete[] heapArray_;
cout << format("dtor ~TargetObj(%i) successfull\n") % cnt_;
}
friend class ObjFactory;
public:
static ObjFactory create;
operator string () const
{
string array_contents = "{";
for (uint i=0; i<cnt_; ++i)
array_contents += heapArray_[i]+",";
array_contents+="}";
return str (format(".....TargetObj(%1%): data=\"%2%\", array[%1%]=%3%")
% cnt_
% *heapData_
% array_contents
);
}
};
/** Test-Factory specialized to create TargetObj instances
* using the 1-argument constructor TargetObj::TargetObj(int).
* It will create boost::shared_ptr instances, because
* factory::RefcountPtr was parametrized with this smart pointer type.
* @note ObjFactory can use the private constructor because it's a friend.
*/
class ObjFactory : public factory::RefcountPtr<TargetObj>
{
static void destroy (TargetObj* victim) { delete victim; };
public:
/** specialized Factory method for creating TargetObj instances.
* Here, we invoke a special constructor, but basically we could
* do everything we want, creating instances of sub classes,
* registering objects etc. Further, we could have used a
* custom allocator or a special deleter function.
*/
ptype operator() (uint param){ return ptype (new TargetObj (param), &destroy); };
};
/** shorthand for the created smart-pointer class,
* here it's a (refcounting) boost::shared_ptr
*/
typedef ObjFactory::ptype pTarget;
ObjFactory TargetObj::create;
/*******************************************************************
* @test the basic object creation Factory behaviour: We declared
* a static field TargetObj::create to be a ObjFactory. So,
* by invoking this functor, we get a boost::shared_ptr
* wrapping a new TargetObj instance. From this we copy
* further shared-ptrs, invoke a member function and
* finally, when leaving the scope, our TargetObj
* will be destroyed automatically.
* @see cinelerra::Factory
*/
class Factory_test : public Test
{
virtual void run(Arg arg)
{
uint num= isnil(arg)? 1 : lexical_cast<uint>(arg[1]);
pTarget p1 (TargetObj::create (num));
pTarget p2 (p1);
pTarget p3 = p2;
cout << "now the smart-ptr has refcount=" << p1.use_count() << "\n"
<< string (*p3) << "\n";
}
};
/** Register this test class to be invoked in some test groups (suites) */
Launch<Factory_test> run_Factory_test("Factory_test","unit common");
/** Register this test class... */
LAUNCHER (Factory_test, "unit common");

View file

@ -0,0 +1,100 @@
/*
Cmdlinewrapper(Test) - build vector of tokens from cmdline, various conversions
Copyright (C) CinelerraCV
2007, Christian Thaeter <ct@pipapo.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
#include "nobugcfg.h"
#include "common/test/run.hpp"
#include "common/cmdline.hpp"
#include "common/util.hpp"
#include <iostream>
#include <sstream>
#include <boost/lambda/lambda.hpp>
using namespace boost::lambda;
using std::cout;
namespace util
{
namespace test
{
/** @test for util::Cmdline, wrapping various example cmdlines */
class CmdlineWrapper_test : public Test
{
virtual void run (Arg arg)
{
testLine("");
testLine("\n\t ");
testLine("spam");
testLine("\nspam");
testLine("eat more spam");
testLine(" oo _O()O_ ä + €");
testLine("\0\too\0\to\0o\t\0oo");
testStandardCmdlineformat();
}
void testLine (const string cmdline)
{
cout << "wrapping cmdline:" << cmdline << "..." << "\n";
int i=0;
Cmdline theCmdline (cmdline);
for_each(theCmdline, (cout << var(i)++ << "|" << _1 << "|\n"));
cout << "-->" << theCmdline << "\n";
// consistency checks
std::ostringstream output;
output << theCmdline;
ENSURE (output.str() == string(theCmdline));
i=0;
string token;
std::istringstream input(theCmdline);
while (input >> token)
ENSURE (token == theCmdline[i++]);
}
/** @test wrapping a (albeit faked) standard commandline
* given as (argc, argv[])
*/
void testStandardCmdlineformat()
{
char* fakeArg[3] = {"CMD", "one ", "two"};
Cmdline theCmdline(3, fakeArg);
cout << "Standard Cmdlineformat:" << theCmdline << "\n";
}
};
LAUNCHER (CmdlineWrapper_test, "unit common");
} // namespace test
} // namespace util

View file

@ -0,0 +1,83 @@
/*
TestOption(Test) - parsing of cmd line options for running Testcases
Copyright (C) CinelerraCV
2007, Christian Thaeter <ct@pipapo.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
#include <iostream>
#include "common/test/run.hpp"
#include "common/test/testoption.hpp"
#include "common/util.hpp"
using util::Cmdline;
using util::isnil;
using std::endl;
namespace test
{
/****************************************************************
* invokes the TestOption parser for various example commandlines
* @test for test::TestOption, parsing of commandline options
* @see test::Suite
* @see util::Cmdline
*/
class TestOption_test : public Test
{
virtual void run(Arg arg)
{
noOptions();
help();
groupID();
singleTest();
groupFilter1();
groupFilter2();
additionalCmd();
additionalCmd2();
}
/** @test performs the actual test for the option parser test::TestOption */
void doIt (const string cmdline)
{
std::cout << "Testing invocation with cmdline: " << cmdline << "..." << endl;
Cmdline args(cmdline);
TestOption optparser (args);
const string testID = optparser.getTestID();
std::cout << "--> Testgroup=" << optparser.getTestgroup() << endl;
std::cout << "--> Test-ID =" << (isnil(testID)? "--missing--" : testID ) << endl;
std::cout << "--> remaining=" << args << endl;
}
void noOptions() { doIt (""); }
void help() { doIt ("--help"); }
void groupID() { doIt ("--group TestGroupID"); }
void singleTest() { doIt (" SingleTestID"); }
void groupFilter1() { doIt (" SingleTestID --group TestGroupID"); }
void groupFilter2() { doIt (" --group TestGroupID SingleTestID "); }
void additionalCmd() { doIt (" --group TestGroupID SingleTestID spam eggs"); }
void additionalCmd2() { doIt ("\t\tSingleTestID spam --group TestGroupID \t --eggs"); }
};
LAUNCHER (TestOption_test, "function common");
} // namespace test

View file

@ -22,7 +22,13 @@
#include <iostream>
#include "helper/run.hpp"
#include "common/test/run.hpp"
#include "common/util.hpp"
using util::isnil;
#include <boost/lexical_cast.hpp>
using boost::lexical_cast;
namespace cinelerra
@ -30,18 +36,24 @@ namespace cinelerra
namespace test
{
/******************************************
* Helooooooo to the world of TDD
* @test demo of using the test framework
*/
class HelloWorld_test : public Test
{
virtual void run(Arg arg)
{
{
int num= isnil(arg)? 1 : lexical_cast<int> (arg[1]);
for ( ; 0 < num-- ; )
greeting();
}
}
void greeting()
{
std::cout << "This is how the world ends...\n";
}
{
std::cout << "This is how the world ends...\n";
}
};
@ -49,6 +61,7 @@ namespace cinelerra
/** Register this test class to be invoked in some test groups (suites) */
Launch<HelloWorld_test> run_HelloWorld_test("HelloWorld_test","unit common");
// NOTE: you may use the Macro "LAUNCHER" in run.hpp to simplify this Registration
} // namespace test

View file

@ -1,182 +0,0 @@
/*
Suite - helper class for running collections of tests
Copyright (C) CinelerraCV
2007, Christian Thaeter <ct@pipapo.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
#include <map>
#include <vector>
#include <memory>
#include <tr1/memory>
#include <iostream>
#include <sstream>
#include "helper/suite.hpp"
#include "helper/run.hpp"
#include "common/error.hpp"
#include "common/util.hpp"
#include "nobugcfg.h"
namespace test
{
using std::map;
using std::vector;
using std::auto_ptr;
using std::tr1::shared_ptr;
using util::isnil;
typedef map<string, Launcher*> TestMap;
typedef shared_ptr<TestMap> PTestMap;
typedef map<string,PTestMap> GroupMap;
/** helper to collect and manage the test cases.
* Every testcase class should create a Launch instance
* which causes a call to Suite::enroll(), so we can add a
* pointer to this Launcher into a map indexed by the
* provided testIDs and groupIDs.
* This enables us to build a Suite instance for any
* requested group and then instantiiate and invoke
* individual testcases accordingly.
*/
class Registry
{
auto_ptr<GroupMap> groups;
public:
Registry() : groups(new GroupMap ) {};
PTestMap& getGroup (string grpID) { return (*groups)[grpID]; };
void add2group (Launcher* test, string testID, string groupID);
};
void
Registry::add2group (Launcher* test, string testID, string groupID)
{
REQUIRE( test );
REQUIRE( !isnil(testID) );
REQUIRE( !isnil(groupID) );
PTestMap& group = getGroup(groupID);
if (!group)
group.reset( new TestMap );
(*group)[testID] = test;
}
Registry testcases;
/** register the given test-launcher, so it can be later accessed
* either as a member of one of the specified groups, or direcly
* by its testID. Any test is automatically added to the groupID
* #ALLGROUP
* @param groups List of group-IDs selected by whitespace
*/
void
Suite::enroll (Launcher* test, string testID, string groups)
{
REQUIRE( test );
REQUIRE( !isnil(testID) );
std::istringstream ss(groups);
string group;
while (ss >> group )
testcases.add2group(test, testID, group);
// Magic: allways add any testcas to groupID="ALL"
testcases.add2group(test,testID, ALLGROUP);
}
/** "magic" groupID containing all registered testcases */
const string Suite::ALLGROUP = "ALL";
/** create a suite comprised of all the testcases
* previously @link #enroll() registered @endlink with this
* this group.
* @see #run() running tests in a Suite
*/
Suite::Suite(string groupID)
: groupID_(groupID)
{
REQUIRE( !isnil(groupID) );
TRACE(test, "Test-Suite( groupID=%s )\n", groupID.c_str () );
if (!testcases.getGroup(groupID))
throw cinelerra::error::Invalid ();
//throw "empty testsuite"; /////////// TODO Errorhandling!
}
/** run all testcases contained in this Suite.
* The first argument in the commandline, if present, will select
* one single testcase with a matching ID.
*/
void
Suite::run (int argc, char* argv[])
{
PTestMap tests = testcases.getGroup(groupID_);
if (!tests)
throw cinelerra::error::Invalid ();
if (argc >= 2)
{
if (Launcher* test = (*tests)[argv[1]])
{
// first cmdline argument denotes a valid
// testcase registered in this group:
// go ahead and invoke just this test.
if (argc > 2)
{ // pass additional cmdline as vector
vector<string> arglist(argc-2);
for ( int i=2; i<argc; ++i )
arglist.push_back(string(argv[i]));
// run single Testcase with provided arguments
(*test)()->run(&arglist);
}
else
(*test)()->run(0); // without additional argumens
return;
}
}
// no test-ID was specified.
// instantiiate all tests cases and execute them.
for ( TestMap::iterator i=tests->begin(); i!=tests->end(); ++i )
if (i->second)
{
std::cout << " ----------"<< i->first<< "----------\n";
Launcher& test = *(i->second);
test()->run(0); // without cmdline arguments
}
}
} // namespace test

View file

@ -21,16 +21,22 @@
*/
#include "helper/suite.hpp"
#include "common/test/suite.hpp"
#include "common/test/testoption.hpp"
/** run all tests or any single test specified in the first
* cmd line argument.
* Note: to ease debugging, we don't catch any exceptions.
*/
int main (int argc, char* argv[])
{
test::Suite suite (test::Suite::ALLGROUP);
suite.run(argc,argv);
return 0;
}
{
util::Cmdline args (argc,argv);
test::TestOption optparser (args);
test::Suite suite (optparser.getTestgroup());
if (optparser.getDescribe())
suite.describe();
else
suite.run (args);
return 0;
}

View file

@ -747,14 +747,19 @@ config.macros.timeline.handler = function(place,macroName,params,wikifier,paramS
}
//}}}</pre>
</div>
<div title="BuildDependenceis" modifier="Ichthyostega" modified="200708140618" created="200708120103" tags="organization buildsys" changecount="5">
<div title="BuildDependenceis" modifier="Ichthyostega" modified="200708210341" created="200708120103" tags="organization buildsys" changecount="7">
<pre>for __Building__
* gcc (4.1), glibc6 (2.3), libstdc++6 (4.1)
* [[build system|BuildSystem]] dependencies: SCons (0.96.90), Python (2.3)
* NoBug for Logging, Tracing, Asserting (can be obtained from [[Pipapo.org|http://www.pipapo.org/pipawiki/NoBug]])
* ~NoBug needs [[valgrind|Valgrind]] (3.2), execinfo.h and libpthread (&amp;rarr; glibc)
* std::tr1 &amp;mdash; esp. for the former BOOST::shared_ptr (which is now proposed standard)
* BOOST
* BOOST ~~(below are the DEBIAN package names)~~
** libboost-dev (=1.34.1-2)
** libboost-program-options-dev (=1.34.1-2)
** libboost-program-options1.34.1 (=1.34.1-2) ''NOTE: binary dependency''
** libboost-regex-dev (=1.34.1-2)
** libboost-regex1.34.1 (=1.34.1-2) ''binary..''
//usually, newer versions are OK//
for __Running__</pre>
@ -3549,7 +3554,7 @@ Here is the order suggested:
</pre>
</div>
<div title="TestSuite" modifier="Ichthyostega" modified="200708180218" created="200708120254" tags="buildsys organization testsuite" changecount="11">
<div title="TestSuite" modifier="Ichthyostega" modified="200708231706" created="200708120254" tags="buildsys organization testsuite" changecount="16">
<pre>For running the automatic Tests, we use Cehteh's simple [[test.sh|TestSh]].
This page is a proposal (by Ichthyo) how the various tests could be organized.
@ -3561,6 +3566,49 @@ This page is a proposal (by Ichthyo) how the various tests could be organized.
* the Testsuite executable provides some command line magic to select individual tests.
* Top-level Testsuites or ''~Test-Collections'' for [[test.sh|TestSh]] contain calls to the different (sub)-Suites, together with the expected results/output
!internal Testsuite runner
The class {{{test::Suite}}} (common/test/suite.hpp) helps building an executable which will run all //registered// test case objects, or some group of such testcases. Each test case implements a simple interface and thus provides a {{{run (args)}}} function, moreover, it registers itself immediately alongside with his definition; this works by the usual trick of defining a static class object and calling some registration function from the constructor of this static var. See the following __hello-world-Example__:
{{{
#include &lt;iostream&gt;
#include &quot;common/test/run.hpp&quot;
class HelloWorld_test : public Test
{
virtual void run(Arg arg)
{
greeting();
}
void greeting()
{
std::cout &lt;&lt; &quot;This is how the world ends...\n&quot;;
}
};
/** Register this test class to be invoked in some test groups (suites) */
LAUNCHER (HelloWorld_test, &quot;unit function common&quot;);
}}}
Notes:
* type Arg is {{{typedef std::vector&lt;string&gt; &amp; Arg;}}}
* this vector may be {{{size()==0}}}, which means no comandline args available.
* otherwise arg[0] is always the ID (normally the classname) of the test
* the following args may contain further arguments passed from system commandline.
* the test can/should produce output that can be checked with Cehteh's [[./test.sh|TestSh]].
* the macro &quot;LAUNCHER&quot; expands to {{{Launch&lt;HelloWorld_test&gt; run_HelloWorld_test(&quot;HelloWorld_test&quot;,&quot;unit function common&quot;);}}}
* note the second parameter to the macro (or the Laucher-ctor) is a space-delimited list of group names
* thus any test can declare itself as belonging to some groups, and we can create a {{{test::Suite}}} for each group if we want.
!!!invoking the testrunner
The class {{{test::TestOption}}} predefines a boost-commandlineparser to support the following optons:
|&gt;|!{{{./test-components --group &lt;groupID&gt; [testID [arguments...]]}}}|
|{{{--help}}}| options summary|
|{{{--group|-g &lt;groupID&gt;}}}| build a Testsuite out of all tests from this group. If missing, ALL tests will be included |
|{{{[testID]}}}| (optional) one single testcase. If missing, all testcases of the group will be invoked |
|{{{--describe}}}| print all registered tests to stdout in a format suited for use with test.sh |
Further commandline arguments are deliverd to a single testcase only if you specify a {{{testID}}}. Otherwise, all commandline arguments remaining after options parsing will be discarded and all tests of the suite will be run with an commandline vector of size()==0
!conventions for the Buildsystem
to help with automating the build, ichthyo would appreciate to have the following conventions.
* in the {{{tests}}} directory are

View file

@ -707,7 +707,7 @@ The next point is allocation failures. These are possible by C/C++ standard but
</pre>
</div>
<div title="ErrorHandling-Cpp" modifier="Ichthyostega" created="200708140640" changecount="1">
<div title="ErrorHandling-Cpp" modifier="Ichthyostega" modified="200708290301" created="200708140640" changecount="3">
<pre>Basically, the C++ error handling techniques are layered on top of the [[C solution|ErrorHandling-C]].
!Proposal:
We use a common base class for all our application specific exceptions. These exceptions can be thought of as a classification of error situations, thus the hierarchical approach. The purpose of throwing such a //classified exception// is
@ -716,8 +716,26 @@ We use a common base class for all our application specific exceptions. These ex
!!Requirements for the Exception Interface
* a means for capturing and transporting detail informations
* the possibility to link to the ''root cause'' of an exception, even after having passed several subsystem barriers.
* the possibility to get at the ''root cause'' of an exception, even after having passed several subsystem barriers.
* getting standardized error messages automatically
!!provided features
The C++ errorhandling Classes and functions can be found in {{{common/error.hpp}}} (not to be confused with the elementary C errorhandling of the cinelerra support lib {{{lib/error.h}}}. See also the &quot;exceptionerrortest.cpp&quot;
* the constructor of the Exception base class will set the C-style error flag as well. Obviously, when an exception gets caught and handled, this error-flag should be reset (and this is the responsibility of the handler).
* we add a __unknown()__ handler which will print additional diagnostics.
* the Exception class has a diagnostic message intended for developers and a friendly message for the user. It is encouraged to write a detailed description of each error situation right into a string constant passed to the exception object ctor. This will serve the purpose of documenting the error situation in the source code and at the same time help diagnosis.
* there is a variant of the constructor taking a reference to an std::exception. This is intended for //chained exceptions//. Whenever an handler catches an exception, but then decides to rethrow it with different classification, the original exception object should be passed on by using this constructor, so it's {{{what()}}} message can be preserved and will be included in the final log entry. While this may look like overkill in a small example, it is a very helpful facility in a larger layered application, where it is often difficult to spot the original cause of an exception encountered.
* for each mayor category of Exception subclasses, we define a C-style error constant. The client code is free to define further detailed error constants and Exception subclasses.
* to help defining Exception subclasses, a macro {{{CINELERRA_EXCEPTION_DECLARE}}} is provided.
!!!basic Exception categories
|!category|!description|
|error::Logic| contradiction to internal logic assumptions detected|
|error::Fatal| special subclass of Logic: situation can't be handled, internal logic floundered |
|error::Config| execution aborted due to misconfiguration |
|error::State| unforeseen internal state |
|error::Invalid| invalid input or parameters encountered |
|error::External| failure in external service the application relies on |
</pre>
</div>
<div title="FrameAndTime" modifier="CehTeh" modified="200708282024" created="200708261938" changecount="2">