Merge branch 'master' of git://git.pipapo.org/cinelerra3/ichthyo
This commit is contained in:
commit
5832cfeae2
34 changed files with 1864 additions and 386 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -2,10 +2,12 @@
|
|||
*~
|
||||
*.tar.*
|
||||
.[^.]*
|
||||
*.os
|
||||
Buildhelper.pyc
|
||||
optcache
|
||||
Makefile.in
|
||||
build/*
|
||||
bin/*
|
||||
autom4te.cache/*
|
||||
scripts/*
|
||||
configure
|
||||
|
|
|
|||
63
SConstruct
63
SConstruct
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
} }
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
85
src/common/cmdline.cpp
Normal 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
70
src/common/cmdline.hpp
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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_
|
||||
|
|
|
|||
|
|
@ -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 ); }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
213
src/common/test/suite.cpp
Normal 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
|
||||
|
|
@ -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
|
||||
126
src/common/test/testoption.cpp
Normal file
126
src/common/test/testoption.cpp
Normal 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
|
||||
75
src/common/test/testoption.hpp
Normal file
75
src/common/test/testoption.hpp
Normal 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
|
||||
|
|
@ -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_*/
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
12
src/main.cpp
12
src/main.cpp
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
1
tests/.gitignore
vendored
|
|
@ -1,4 +1,5 @@
|
|||
,*
|
||||
test-*
|
||||
mainsuite
|
||||
errortest
|
||||
plugin-example
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'])
|
||||
|
|
|
|||
64
tests/components/common/appconfigtest.cpp
Normal file
64
tests/components/common/appconfigtest.cpp
Normal 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
|
||||
|
||||
239
tests/components/common/exceptionerrortest.cpp
Normal file
239
tests/components/common/exceptionerrortest.cpp
Normal 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
|
||||
|
||||
|
|
@ -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");
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
100
tests/components/common/test/cmdlinewrappertest.cpp
Normal file
100
tests/components/common/test/cmdlinewrappertest.cpp
Normal 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
|
||||
|
||||
83
tests/components/common/test/testoptiontest.cpp
Normal file
83
tests/components/common/test/testoptiontest.cpp
Normal 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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 (&rarr; glibc)
|
||||
* std::tr1 &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 <iostream>
|
||||
#include "common/test/run.hpp"
|
||||
|
||||
class HelloWorld_test : public Test
|
||||
{
|
||||
virtual void run(Arg arg)
|
||||
{
|
||||
greeting();
|
||||
}
|
||||
|
||||
void greeting()
|
||||
{
|
||||
std::cout << "This is how the world ends...\n";
|
||||
}
|
||||
};
|
||||
|
||||
/** Register this test class to be invoked in some test groups (suites) */
|
||||
LAUNCHER (HelloWorld_test, "unit function common");
|
||||
}}}
|
||||
Notes:
|
||||
* type Arg is {{{typedef std::vector<string> & 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 "LAUNCHER" expands to {{{Launch<HelloWorld_test> run_HelloWorld_test("HelloWorld_test","unit function common");}}}
|
||||
* 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:
|
||||
|
||||
|>|!{{{./test-components --group <groupID> [testID [arguments...]]}}}|
|
||||
|{{{--help}}}| options summary|
|
||||
|{{{--group|-g <groupID>}}}| 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
|
||||
|
|
|
|||
|
|
@ -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 "exceptionerrortest.cpp"
|
||||
* 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">
|
||||
|
|
|
|||
Loading…
Reference in a new issue