implemented C++ error handling system
This commit is contained in:
parent
fee63d8f06
commit
d54b600382
12 changed files with 651 additions and 223 deletions
|
|
@ -29,6 +29,7 @@
|
|||
#include "nobugcfg.h"
|
||||
#undef NOBUG_INIT_DEFS_
|
||||
|
||||
#include <exception>
|
||||
|
||||
|
||||
using util::isnil;
|
||||
|
|
@ -56,14 +57,18 @@ namespace cinelerra
|
|||
*/
|
||||
Appconfig::Appconfig()
|
||||
: configParam_ (new Configmap)
|
||||
{
|
||||
//////////
|
||||
NOBUG_INIT;
|
||||
//////////
|
||||
|
||||
INFO(config, "Basic application configuration triggered.");
|
||||
(*configParam_)["version"] = STRINGIFY (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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -74,18 +79,18 @@ namespace cinelerra
|
|||
*/
|
||||
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());
|
||||
throw cinelerra::error::Fatal ();
|
||||
} }
|
||||
{
|
||||
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 ();
|
||||
} }
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -75,11 +75,11 @@ namespace cinelerra
|
|||
* @warning don't use it in destruction code!
|
||||
*/
|
||||
static Appconfig& instance()
|
||||
{
|
||||
static scoped_ptr<Appconfig> theApp_ (0);
|
||||
if (!theApp_) theApp_.reset (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.
|
||||
|
|
|
|||
|
|
@ -21,48 +21,174 @@
|
|||
* *****************************************************/
|
||||
|
||||
|
||||
#include "common/error.hpp"
|
||||
#include "nobugcfg.h"
|
||||
|
||||
///////////////////////////////////TODO
|
||||
#include "common/error.hpp"
|
||||
#include "common/util.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <typeinfo>
|
||||
#include <iostream>
|
||||
|
||||
extern void booo()
|
||||
{
|
||||
std::cerr << "Booooo!!!" << std::endl;
|
||||
std::cerr.flush();
|
||||
}
|
||||
///////////////////////////////////TODO
|
||||
using util::isnil;
|
||||
using std::exception;
|
||||
|
||||
|
||||
namespace cinelerra
|
||||
{
|
||||
char* killme ="cinelerra Errrror. TODO real description needed";
|
||||
|
||||
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()
|
||||
{
|
||||
TODO("really implement cinelerra::Error description");
|
||||
return killme;
|
||||
}
|
||||
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()
|
||||
{
|
||||
UNIMPLEMENTED("storing and managing root causes");
|
||||
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,45 +99,62 @@ 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 ); ///< unforseen 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 Fatal : public Logic
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
#include <iostream>
|
||||
|
||||
extern void booo();
|
||||
|
||||
/******************************************************
|
||||
* if NoBug is used, redefine some macros
|
||||
|
|
@ -114,8 +162,11 @@ extern void booo();
|
|||
*/
|
||||
#ifdef NOBUG_ABORT
|
||||
#undef NOBUG_ABORT
|
||||
#define NOBUG_ABORT throw cinelerra::error::Fatal(); ////////////////TODO
|
||||
#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
|
||||
#endif // CINELERRA_ERROR_HPP_
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
* *****************************************************/
|
||||
|
||||
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
|
@ -29,13 +30,13 @@
|
|||
#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"
|
||||
|
||||
#include "nobugcfg.h"
|
||||
|
||||
|
||||
namespace test
|
||||
|
|
@ -75,16 +76,16 @@ namespace test
|
|||
|
||||
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;
|
||||
}
|
||||
{
|
||||
REQUIRE( test );
|
||||
REQUIRE( !isnil(testID) );
|
||||
REQUIRE( !isnil(groupID) );
|
||||
|
||||
PTestMap& group = getGroup(groupID);
|
||||
if (!group)
|
||||
group.reset( new TestMap );
|
||||
(*group)[testID] = test;
|
||||
}
|
||||
|
||||
Registry testcases;
|
||||
|
||||
|
|
@ -102,18 +103,18 @@ namespace test
|
|||
*/
|
||||
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);
|
||||
}
|
||||
{
|
||||
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";
|
||||
|
|
@ -127,14 +128,14 @@ namespace test
|
|||
*/
|
||||
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!
|
||||
}
|
||||
{
|
||||
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());
|
||||
|
|
@ -151,36 +152,36 @@ namespace test
|
|||
*/
|
||||
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
|
||||
}
|
||||
}
|
||||
{
|
||||
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,
|
||||
|
|
@ -188,27 +189,24 @@ namespace test
|
|||
*/
|
||||
void
|
||||
Suite::describe ()
|
||||
{
|
||||
util::Cmdline noCmdline("");
|
||||
PTestMap tests = testcases.getGroup(groupID_);
|
||||
ASSERT (tests);
|
||||
|
||||
std::cout << "TESTING \"Component Test Suite: " << groupID_ << "\" ./test-components\n\n";
|
||||
{
|
||||
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";
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -35,34 +35,35 @@ namespace util
|
|||
|
||||
/** a family of util functions providing a "no value whatsoever" test */
|
||||
inline bool isnil(const string& val)
|
||||
{
|
||||
return 0 == val.length();
|
||||
}
|
||||
{
|
||||
return 0 == val.length();
|
||||
}
|
||||
|
||||
inline bool isnil(const string* pval)
|
||||
{
|
||||
return !pval || 0 == pval->length();
|
||||
}
|
||||
{
|
||||
return !pval || 0 == pval->length();
|
||||
}
|
||||
|
||||
inline bool isnil(const char* pval)
|
||||
{
|
||||
return !pval || 0 == std::strlen(pval);
|
||||
}
|
||||
{
|
||||
return !pval || 0 == std::strlen(pval);
|
||||
}
|
||||
|
||||
|
||||
/** cut a numeric value to be >=0 */
|
||||
template <typename NUM>
|
||||
inline NUM noneg (NUM val)
|
||||
{
|
||||
return (0<val? val : 0);
|
||||
}
|
||||
{
|
||||
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();
|
||||
}
|
||||
{
|
||||
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..
|
||||
|
|
@ -70,9 +71,9 @@ namespace util
|
|||
template <typename Container, typename Oper>
|
||||
inline Oper
|
||||
for_each (Container& c, Oper& doIt)
|
||||
{
|
||||
return std::for_each (c.begin(),c.end(), doIt);
|
||||
}
|
||||
{
|
||||
return std::for_each (c.begin(),c.end(), doIt);
|
||||
}
|
||||
|
||||
|
||||
} // namespace util
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
@ -42,5 +48,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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,10 +49,10 @@ namespace cinelerra
|
|||
|
||||
/** @test accessing a value from cinelerra::Appconfig */
|
||||
void testAccess (const string& key)
|
||||
{
|
||||
string ver = cinelerra::Appconfig::get(key);
|
||||
ASSERT ( !util::isnil(ver));
|
||||
}
|
||||
{
|
||||
string ver = cinelerra::Appconfig::get(key);
|
||||
ASSERT ( !util::isnil(ver));
|
||||
}
|
||||
};
|
||||
|
||||
LAUNCHER (Appconfig_test, "function common");
|
||||
|
|
|
|||
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
|
||||
|
||||
|
|
@ -37,14 +37,14 @@ namespace cinelerra
|
|||
class HelloWorld_test : public Test
|
||||
{
|
||||
virtual void run(Arg arg)
|
||||
{
|
||||
greeting();
|
||||
}
|
||||
{
|
||||
greeting();
|
||||
}
|
||||
|
||||
void greeting()
|
||||
{
|
||||
std::cout << "This is how the world ends...\n";
|
||||
}
|
||||
{
|
||||
std::cout << "This is how the world ends...\n";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -29,14 +29,14 @@
|
|||
* Note: to ease debugging, we don't catch any exceptions.
|
||||
*/
|
||||
int main (int argc, char* argv[])
|
||||
{
|
||||
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;
|
||||
}
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue