implemented C++ error handling system

This commit is contained in:
Fischlurch 2007-08-26 19:14:39 +02:00
parent fee63d8f06
commit d54b600382
12 changed files with 651 additions and 223 deletions

View file

@ -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 ();
} }

View file

@ -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.

View file

@ -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

View file

@ -21,44 +21,75 @@
*/
#ifndef CINELERRA_ERROR_H
#define CINELERRA_ERROR_H
#ifndef CINELERRA_ERROR_HPP_
#define CINELERRA_ERROR_HPP_
#include <exception>
#include <string>
#include "nobugcfg.h"
#include "lib/error.h"
namespace cinelerra
{
using std::string;
/** error-ID for unspecified exceptions */
CINELERRA_ERROR_DECLARE(EXCEPTION);
/**
* Interface and Baseclass of all Exceptions thrown
* from within cinelerra (C++) code. Common operations
* for getting an diagnostic message and for obtaining
* the root cause, i.e. the frist exception encountered
* the root cause, i.e. the first exception encountered
* in a chain of exceptions.
*/
class Error : public std::exception
{
public:
Error (string description="", const char* id=CINELERRA_ERROR_EXCEPTION) throw();
Error (std::exception& cause,
string description="", const char* id=CINELERRA_ERROR_EXCEPTION) throw();
Error (const Error&) throw();
virtual ~Error () throw() {};
/** yield a diagnostig message characterizing the problem */
/** yield a diagnostic message characterizing the problem */
virtual const char* what () const throw();
/** the internal cinelerra-error-ID (was set as C-errorstate in ctor) */
const char* getID () const throw() { return this->id_; }
/** extract the message to be displayed for the user */
const string& getUsermsg () const throw();
/** If this exception was caused by a chain of further exceptions,
* return the first one registered in this throw sequence.
* This works only, if every exceptions thrown as a consequence
* of another exception is propperly constructed by passing
* the original exception to the constructor
* return the description of the first one registered in this throw sequence.
* This works only if every exceptions thrown as a consequence of another exception
* is propperly constructed by passing the original exception to the constructor
* @return the description string, maybe empty (if there is no known root cause)
*/
std::exception rootCause () const throw();
const string& rootCause () const throw() { return this->cause_; }
/** replace the previous or default friendly message for the user. To be localized. */
Error& setUsermsg (const string& newMsg) throw() { this->msg_ = newMsg; return *this; }
/** give additional developer info. Typically used at intermediate handlers to add context. */
Error& prependInfo (const string& text) throw() { this->desc_.insert (0,text); return *this; }
private:
/** a copy of the first exception encountered in this exception chain */
std::exception cause;
const char* id_; ///< an CINELERRA_ERROR id, which is set as errorstate on construction
string msg_; ///< friendly message intended for users (to be localized)
string desc_; ///< detailed description of the error situation for the developers
mutable string what_; ///< buffer for generating the detailed description on demand
const string cause_; ///< descriptoin of first exception encountered in the chain
static const string extractCauseMsg (const std::exception&) throw();
};
@ -68,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_

View file

@ -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";
}
}

View file

@ -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

View file

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

View file

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

View file

@ -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");

View file

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

View file

@ -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";
}
};

View file

@ -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;
}