diff --git a/Testing/TableTest.h b/Testing/TableTest.h index 81ae076..26a3e4f 100644 --- a/Testing/TableTest.h +++ b/Testing/TableTest.h @@ -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 > > diff --git a/Testing/TableTest.test/test2.cc b/Testing/TableTest.test/test2.cc index 80b90a7..c6afefd 100644 --- a/Testing/TableTest.test/test2.cc +++ b/Testing/TableTest.test/test2.cc @@ -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. */