1
0
forked from Alepha/Alepha

Unified testing output.

This should eliminate the duplicate "PASSED" or "FAILED" lines.

I think I can type-erase some of this sufficiently to make the
output rendering be in its own TU... then tweaks to test output
format will mostly require a relink, not a rebuild.
This commit is contained in:
2023-11-11 10:13:19 -05:00
parent be150783f0
commit df72d745e6
2 changed files with 100 additions and 62 deletions

View File

@ -8,6 +8,7 @@ static_assert( __cplusplus > 2020'99 );
#include <tuple>
#include <string>
#include <variant>
#include <iostream>
#include <algorithm>
#include <typeinfo>
@ -68,41 +69,47 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
using Invoker= std::function< return_type () >;
std::function< TestResult ( Invoker, const std::string & ) > impl;
std::function< std::tuple< TestResult, std::optional< std::string > > ( Invoker, const std::string & ) > impl;
BasicUniversalHandler( const return_type expected ) : impl
{
[expected]( Invoker invoker, const std::string &comment )
[expected]( Invoker invoker, const std::string &comment ) -> std::tuple< TestResult, std::optional< std::string > >
{
const auto witness= Utility::evaluate <=[&]() -> std::optional< return_type >
struct ErrorMessage { std::string mesg; };
const auto witness= Utility::evaluate <=[&]() -> std::variant< return_type, ErrorMessage >
{
try
{
return invoker();
}
catch( const std::exception &ex )
{
return ErrorMessage{ typeid( ex ).name() };
}
catch( ... )
{
return std::nullopt;
return ErrorMessage{ "Unknown Exception Type." };
}
};
const auto result= witness == expected ? TestResult::Passed : TestResult::Failed;
const auto result= ( std::holds_alternative< return_type >( witness ) and std::get< return_type >( witness ) == expected )
? TestResult::Passed
: TestResult::Failed;
std::ostringstream oss;
if( result == TestResult::Failed )
{
if( witness.has_value() )
if( std::holds_alternative< return_type >( witness ) )
{
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
printDebugging< outputMode >( witness.value(), expected );
streamDebugging< outputMode >( oss, std::get< return_type >( witness ), expected );
}
else std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": Unexpected exception in \"" << comment << '"' << std::endl;
else oss << "Unexpected exception of type: " << std::get< ErrorMessage >( witness ).mesg;
}
else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
return result;
return { result, oss.str().empty() ? std::optional< std::string >{} : std::move( oss ).str() };
}
}
{}
TestResult
std::tuple< TestResult, std::optional< std::string > >
operator() ( Invoker invoker, const std::string &comment ) const
{
if( impl != nullptr ) return impl( invoker, comment );
@ -111,30 +118,36 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
const return_type *const expected_p= this;
const auto expected= *expected_p;
breakpoint();
const auto witness= Utility::evaluate <=[&]() -> std::optional< return_type >
struct ErrorMessage { std::string mesg; };
const auto witness= Utility::evaluate <=[&]() -> std::variant< return_type, ErrorMessage >
{
try
{
return invoker();
}
catch( const std::exception &ex )
{
return ErrorMessage{ typeid( ex ).name() };
}
catch( ... )
{
return std::nullopt;
return ErrorMessage{ "Unknown Exception Type." };
}
};
const auto result= witness == expected ? TestResult::Passed : TestResult::Failed;
const auto result= ( std::holds_alternative< return_type >( witness ) and std::get< return_type >( witness ) == expected )
? TestResult::Passed
: TestResult::Failed;
std::ostringstream oss;
if( result == TestResult::Failed )
{
if( witness.has_value() )
if( std::holds_alternative< return_type >( witness ) )
{
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
printDebugging< outputMode >( witness.value(), expected );
streamDebugging< outputMode >( oss, std::get< return_type >( witness ), expected );
}
else std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": Unexpected exception in \"" << comment << '"' << std::endl;
else oss << "Unexpected exception of type: " << std::get< ErrorMessage >( witness ).mesg;
}
else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
return result;
return { result, oss.str().empty() ? std::optional< std::string >{} : std::move( oss ).str() };
}
else throw std::logic_error( "Somehow we didn't setup impl, and it's not an adapted case!" );
}
@ -143,19 +156,17 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
requires( not SameAs< T, void > )
BasicUniversalHandler( std::type_identity< T > ) : impl
{
[]( Invoker invoker, const std::string &comment )
[]( Invoker invoker, const std::string &comment ) -> std::tuple< TestResult, std::optional< std::string > >
{
try
{
std::ignore= invoker();
breakpoint();
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
return TestResult::Failed;
return { TestResult::Failed, std::nullopt };
}
catch( const T & )
{
std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
return TestResult::Passed;
return { TestResult::Passed, std::nullopt };
}
}
}
@ -165,18 +176,16 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
requires( SameAs< T, std::type_identity< void > > or SameAs< T, std::nothrow_t > )
BasicUniversalHandler( T ) : impl
{
[]( Invoker invoker, const std::string &comment )
[]( Invoker invoker, const std::string &comment ) -> std::tuple< TestResult, std::optional< std::string > >
{
try
{
std::ignore= invoker();
std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
return TestResult::Passed;
return { TestResult::Passed, std::nullopt };
}
catch( ... )
{
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
return TestResult::Failed;
return { TestResult::Failed, std::nullopt };
}
}
}
@ -185,28 +194,25 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
template< DerivedFrom< std::exception > T >
BasicUniversalHandler( const T exemplar ) : impl
{
[expected= std::string{ exemplar.what() }]( Invoker invoker, const std::string &comment )
[expected= std::string{ exemplar.what() }]( Invoker invoker, const std::string &comment ) -> std::tuple< TestResult, std::optional< std::string > >
{
try
{
invoker();
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
std::cout << " " << C::testInfo << "NOTE" << resetStyle << ": expected exception `"
<< typeid( T ).name()
<< "` wasn't thrown." << std::endl;
return TestResult::Failed;
return { TestResult::Failed,
IOStreams::String{} << "expected exception `" << typeid( T ).name() << "` wasn't thrown." };
}
catch( const T &ex )
{
const std::string witness= ex.what();
const TestResult rv= witness == expected ? TestResult::Passed : TestResult::Failed;
std::ostringstream oss;
if( rv == TestResult::Failed )
{
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
std::cout << " " << C::testInfo << "NOTE" << resetStyle << ": expected message did not match." << std::endl;
printDebugging< outputMode >( witness, expected );
oss << "expected message did not match." << std::endl;
streamDebugging< outputMode >( oss, witness, expected );
}
return rv;
return { rv, oss.str().empty() ? std::optional< std::string >{} : std::move( oss ).str() };
}
}
}
@ -294,13 +300,24 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
breakpoint();
return std::apply( function, params );
};
const auto result= checker( invoker, comment );
const auto [result, supplement]= checker( invoker, comment );
if( result == TestResult::Failed )
{
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
++failureCount;
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
if( supplement.has_value() )
{
std::cout << " " << C::testWarn << "DETAILS" << resetStyle << ": " << supplement.value() << std::endl;
}
}
else
{
std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
if( supplement.has_value() )
{
std::cout << " " << C::testWarn << "INFO: " << resetStyle << ": " << supplement.value() << std::endl;
}
}
else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
breakpoint();
}