forked from Alepha/Alepha
Basic support for exception verification in tests
This probably needs to be expanded upon. The basic functionality added is to permit a test expectation clause to be a function which takes some kind of exception type. That function can then perform any arbitrary checks and analyses it needs to confirm that the exception which was caught passes muster for that test case.
This commit is contained in:
@ -69,6 +69,18 @@ namespace Alepha::Hydrogen::Testing ::detail:: TableTest_m
|
||||
else return ex.message();
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
concept ExceptionLike= DerivedFrom< T, std::exception >
|
||||
or DerivedFrom< T, Alepha::Exception >;
|
||||
|
||||
template< typename T, typename Param >
|
||||
concept FunctionOver= Concepts::UnaryFunction< T >
|
||||
and Concepts::SameAs< get_args_t< T >, std::tuple< Param > >;
|
||||
|
||||
template< typename T >
|
||||
concept FunctionTakingThrowable= UnaryFunction< T >
|
||||
and ExceptionLike< get_arg_t< T, 0 > >;
|
||||
|
||||
template< typename return_type, OutputMode outputMode >
|
||||
struct BasicUniversalHandler
|
||||
: compute_base_t< return_type >
|
||||
@ -80,6 +92,41 @@ namespace Alepha::Hydrogen::Testing ::detail:: TableTest_m
|
||||
|
||||
std::function< std::tuple< TestResult, std::optional< std::string > > ( Invoker, const std::string & ) > impl;
|
||||
|
||||
template< FunctionTakingThrowable Verifier >
|
||||
BasicUniversalHandler( Verifier verifier ) : impl
|
||||
{
|
||||
[verifier] ( Invoker invoker, const std::string &comment ) -> std::tuple< TestResult, std::optional< std::string > >
|
||||
{
|
||||
try
|
||||
{
|
||||
invoker();
|
||||
return { TestResult::Failed, "Expected an exception but none was thrown." };
|
||||
}
|
||||
catch( const get_arg_t< Verifier, 0 > &ex )
|
||||
{
|
||||
if( verifier( ex ) ) return { TestResult::Passed, std::nullopt };
|
||||
return { TestResult::Failed, "Verification of exception contents failed." };
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
try { throw; }
|
||||
catch( const std::exception &ex )
|
||||
{
|
||||
return { TestResult::Failed, "Unexpected Exception Type: " + Utility::fancyTypeName( typeid( ex ) ) };
|
||||
}
|
||||
catch( const Alepha::Exception &ex )
|
||||
{
|
||||
return { TestResult::Failed, "Unexpected Exception Type: " + Utility::fancyTypeName( typeid( ex ) ) };
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
return { TestResult::Failed, "Unknown Exception Type." };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
||||
BasicUniversalHandler( const return_type expected ) : impl
|
||||
{
|
||||
[expected]( Invoker invoker, const std::string &comment ) -> std::tuple< TestResult, std::optional< std::string > >
|
||||
@ -220,7 +267,15 @@ namespace Alepha::Hydrogen::Testing ::detail:: TableTest_m
|
||||
{}
|
||||
|
||||
template< typename T >
|
||||
requires( DerivedFrom< T, std::exception > or DerivedFrom< T, Alepha::Exception > )
|
||||
requires
|
||||
(
|
||||
(
|
||||
false
|
||||
or DerivedFrom< T, std::exception >
|
||||
or DerivedFrom< T, Alepha::Exception >
|
||||
)
|
||||
and not std::is_same_v< return_type, T >
|
||||
)
|
||||
BasicUniversalHandler( const T exemplar ) : impl
|
||||
{
|
||||
[expected= getMessageFromException( exemplar )]( Invoker invoker, const std::string &comment ) -> std::tuple< TestResult, std::optional< std::string > >
|
||||
|
@ -53,6 +53,12 @@ namespace
|
||||
struct DerivedError : std::runtime_error
|
||||
{
|
||||
using std::runtime_error::runtime_error;
|
||||
|
||||
std::optional< int > value_;
|
||||
|
||||
explicit DerivedError( const std::string &s, int value_ ) : std::runtime_error{ s }, value_( value_ ) {}
|
||||
|
||||
int value() const { return value_.value(); }
|
||||
};
|
||||
|
||||
"Can we use Aggregates with universal cases, correctly?"_test <=
|
||||
@ -60,7 +66,7 @@ namespace
|
||||
<
|
||||
[]( const int x )
|
||||
{
|
||||
if( x < 0 ) throw DerivedError{ "Cannot be negative." };
|
||||
if( x < 0 ) throw DerivedError{ "Cannot be negative.", x };
|
||||
return Aggregate{ x, x, x };
|
||||
}
|
||||
>
|
||||
@ -73,6 +79,11 @@ namespace
|
||||
{ "Expect exception type exception", { -42 }, std::type_identity< std::exception >{} },
|
||||
{ "Expect exception value specific", { -42 }, DerivedError{ "Cannot be negative." } },
|
||||
{ "Expect exception value specific (loose)", { -42 }, std::runtime_error{ "Cannot be negative." } },
|
||||
{ "Expect exception value specific (loose)", { -42 },
|
||||
[]( const DerivedError &e )
|
||||
{
|
||||
return e.value() == -42;
|
||||
} },
|
||||
|
||||
/* These cases should fail, but we don't want to fail them in normal builds. */
|
||||
/* A few different ways of disabling these tests are shown below. */
|
||||
|
Reference in New Issue
Block a user