lumiera_/tests/library/exception-error-test.cpp
Ichthyostega 9c21164ae6 Doxygen Fixes (#1062)
This changeset fixes a huge pile of problems, as indicated in the
error log of the Doxygen run after merging all the recent Doxygen improvements

unfortunately, auto-linking does still not work at various places.
There is no clear indication what might be the problem.
Possibly the rather unstable Sqlite support in this Doxygen version
is the cause. Anyway, needs to be investigated further.
2017-04-02 04:22:51 +02:00

260 lines
9.3 KiB
C++

/*
ExceptionError(Test) - throwing and catching our exception type
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>
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.
* *****************************************************/
/** @file exception-error-test.cpp
** unit test \ref ExceptionError_test
*/
#include "lib/error.h"
#include "lib/error.hpp"
#include "lib/test/run.hpp"
#include "lib/util.hpp"
#include <string>
#include <iostream>
#include <exception>
#include <stdexcept>
using std::runtime_error;
using std::exception;
using std::string;
using std::cout;
namespace lumiera {
namespace test {
/** local specific error-constant for use in the
* constructor of the nested SpecificError class.
*/
LUMIERA_ERROR_DEFINE(LIFE_AND_UNIVERSE, "and everything?");
LUMIERA_ERROR_DEFINE(DERIVED, "convoluted exception");
/** declare a specific Error class with parent class error::external */
LUMIERA_EXCEPTION_DECLARE (DerivedError, error::External, LUMIERA_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();
checkErrorFlagPropagation();
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(); }
/** @test catching, repackaging and rethrowing of errors.
* This feature is important for passing exceptions transparently
* 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 lumiera::Error object,
* the corresponding lumiera_error state is set automatically
*/
void checkErrorIntegration()
{
lumiera_error ();
CHECK (!lumiera_error ());
Error err1;
Error err2("boo",LUMIERA_ERROR_DERIVED);
CHECK (err1.getID () == lumiera_error ()); // (we didn't clear the first one!)
Error err3("boooo",LUMIERA_ERROR_DERIVED);
CHECK (err3.getID () == lumiera_error ());
SpecificError err4;
CHECK (err4.getID () == LUMIERA_ERROR_LIFE_AND_UNIVERSE);
CHECK (err4.getID () == lumiera_error ());
CHECK (!lumiera_error ());
}
void detectErrorflag (string) { throwOnError(); }
void detectErrorflagChained (string msg) { maybeThrow<error::Logic>(msg); }
/** @test verify throwing of Exceptions
* based on a non-cleared C error flag
*/
void checkErrorFlagPropagation()
{
lumiera_error_set(LUMIERA_ERROR_LIFE_AND_UNIVERSE, "what is the answer?");
CHECK (lumiera_error_peek());
catcher (&test::detectErrorflag, "");
CHECK (LUMIERA_ERROR_LIFE_AND_UNIVERSE == lumiera_error_peek());
catcher (&test::detectErrorflagChained, "the big bang");
CHECK (LUMIERA_ERROR_LIFE_AND_UNIVERSE == lumiera_error());
}
/** @test the chaining of lumiera::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);
CHECK (err2.rootCause() == err1.what());
CHECK (err3.rootCause() == err1.what());
CHECK (err4.rootCause() == err1.what());
CHECK (err5.rootCause() == rerr.what());
CHECK (err6.rootCause() == rerr.what());
}
/** @test terminate the Application by throwing an undeclared exception.
* this should result in the global unknown() handler to be called,
* so usually it will terminate the test run.
* @note inside error.hpp, an initialisation hook has been installed into
* AppState, causing our own unknown() handler to be installed and
* invoked, which gives additional diagnostics.*/
void terminateUnknown () throw()
{
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",LUMIERA_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 lumiera::Error: " << e.what() << "\n"; }
catch (runtime_error& e) { cout << "caught std::runtime_error: " << e.what() << "\n"; }
catch (exception& e) { cout << "caught std::exception. (unspecific)" << "\n"; }
catch (...) { cout << "caught an unknown exception\n"; }
}
};
/** register this test class... */
LAUNCHER (ExceptionError_test, "function common");
} // namespace test
} // namespace util