diff --git a/Reflection/tuplizeAggregate.h b/Reflection/tuplizeAggregate.h index e631743..f959801 100644 --- a/Reflection/tuplizeAggregate.h +++ b/Reflection/tuplizeAggregate.h @@ -6,6 +6,8 @@ static_assert( __cplusplus > 2020'00 ); #include +#include + #include #include @@ -192,6 +194,9 @@ namespace Alepha::Hydrogen::Reflection { return tuplizeAggregate< compute_salient_members_count_v< std::decay_t< Aggregate > > >( std::forward< Aggregate >( agg ) ); } + + template< typename Aggregate > + using aggregate_tuple_t= decay_tuple_t< std::decay_t< decltype( tuplizeAggregate( std::declval< const Aggregate & >() ) ) > >; } } diff --git a/Testing/TableTest.h b/Testing/TableTest.h index 45d2021..d8f9348 100644 --- a/Testing/TableTest.h +++ b/Testing/TableTest.h @@ -33,6 +33,8 @@ static_assert( __cplusplus > 2020'00 ); #include +#include + #include #include @@ -45,6 +47,8 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test enum class OutputMode { All, Relaxed }; } + inline void breakpoint() {} + namespace C { inline namespace Colors @@ -53,6 +57,506 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test } } + template< OutputMode outputMode, typename T > + void printDebugging( const T &witness, const T &expected ); + + template< Aggregate Agg, TypeListType > + struct TupleSneak; + + template< Aggregate Agg > + struct TupleSneak< Agg, Nil > + : Agg + { + TupleSneak() { std::cerr << "The inherited default ctor was called." << std::endl; } + + protected: + void set( Agg agg ) { static_cast< Agg & >( *this )= agg; } + }; + + template< Aggregate Agg, typename ... Args > + struct TupleSneak< Agg, TypeList< Args... > > + : TupleSneak< Agg, cdr_t< TypeList< Args... > > > + { + using Parent= TupleSneak< Agg, cdr_t< TypeList< Args... > > >; + using Parent::Parent; + + TupleSneak( Args ... args ) + { + std::cerr << "I was the ctor called, with " << sizeof...( Args ) << " arguments." << std::endl; + tuple_for_each( std::tuple{ args... } ) <= + []( const auto element ) + { + std::cerr << "Element: " << element << std::endl; + }; + this->set( { args... } ); + } + }; + + enum class TestResult { Passed, Failed }; + + struct BlankBase {}; + + template< typename T > + static consteval auto + compute_base_f() noexcept + { + if constexpr ( Aggregate< T > ) return std::type_identity< TupleSneak< T, list_from_tuple_t< Reflection::aggregate_tuple_t< T > > > >{}; + else if constexpr( std::is_class_v< T > ) return std::type_identity< T >{}; + else return std::type_identity< BlankBase >{}; + } + + template< typename T > + using compute_base_t= typename decltype( compute_base_f< std::decay_t< T > >() )::type; + + template< typename return_type, OutputMode outputMode > + struct BasicUniversalHandler; + + template< Primitive return_type, OutputMode outputMode > + struct BasicUniversalHandler< return_type, outputMode > + { + using Invoker= std::function< return_type () >; + std::function< TestResult ( Invoker, const std::string & ) > impl; + + TestResult + operator() ( Invoker invoker, const std::string &comment ) const + { + return impl( invoker, comment ); + //if constexpr( std::is_base_of_v< std::decay_t< return_type >, ComputedBase > ) + } + +#if 1 + BasicUniversalHandler( const return_type expected ) + : impl + { + [expected]( Invoker invoker, const std::string &comment ) + { + const auto witness= Utility::evaluate <=[&]() -> std::optional< return_type > + { + try + { + return invoker(); + } + catch( ... ) + { + return std::nullopt; + } + }; + const auto result= witness == expected ? TestResult::Passed : TestResult::Failed; + + if( result == TestResult::Failed ) + { + if( witness.has_value() ) + { + std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl; + printDebugging< outputMode >( witness.value(), expected ); + } + else std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": Unexpected exception in \"" << comment << '"' << std::endl; + } + else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl; + return result; + } + } + {} +#endif + +#if 0 + template< typename ... Args > + requires ConstructibleFrom< return_type, std::decay_t< Args >... > + BasicUniversalHandler( Args &&... expected_init ) + : BasicUniversalHandler( return_type{ std::forward< Args >( expected_init )... } ) + {} +#endif + +#if 1 + template< typename T > + requires( not SameAs< T, void > ) + BasicUniversalHandler( std::type_identity< T > ) : impl + { + []( Invoker invoker, const std::string &comment ) + { + try + { + std::ignore= invoker(); + std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl; + return TestResult::Failed; + } + catch( const T & ) + { + std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl; + return TestResult::Passed; + } + } + } + {} + + template< typename T > + requires( SameAs< T, std::type_identity< void > > or SameAs< T, std::nothrow_t > ) + BasicUniversalHandler( T ) : impl + { + []( Invoker invoker, const std::string &comment ) + { + try + { + std::ignore= invoker(); + std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl; + return TestResult::Passed; + } + catch( ... ) + { + std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl; + return TestResult::Failed; + } + } + } + {} + + template< DerivedFrom< std::exception > T > + BasicUniversalHandler( const T exemplar ) : impl + { + [expected= std::string{ exemplar.what() }]( Invoker invoker, const std::string &comment ) + { + 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; + } + catch( const T &ex ) + { + const std::string witness= ex.what(); + const TestResult rv= witness == expected ? TestResult::Passed : TestResult::Failed; + 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 ); + } + return rv; + } + } + } + {} +#endif + }; + + template< Aggregate return_type, OutputMode outputMode > + struct BasicUniversalHandler< return_type, outputMode > + : compute_base_t< return_type > + { + using ComputedBase= compute_base_t< return_type >; + using ComputedBase::ComputedBase; + + using Invoker= std::function< return_type () >; + + std::function< TestResult ( Invoker, const std::string & ) > impl; + + TestResult + operator() ( Invoker invoker, const std::string &comment ) const + { + if( impl != nullptr ) return impl( invoker, comment ); + //if constexpr( std::is_base_of_v< std::decay_t< return_type >, ComputedBase > ) + if constexpr( true ) + { + const return_type *const expected_p= this; + const auto expected= *expected_p; + breakpoint(); + const auto witness= Utility::evaluate <=[&]() -> std::optional< return_type > + { + try + { + return invoker(); + } + catch( ... ) + { + return std::nullopt; + } + }; + const auto result= witness == expected ? TestResult::Passed : TestResult::Failed; + + if( result == TestResult::Failed ) + { + if( witness.has_value() ) + { + std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl; + printDebugging< outputMode >( witness.value(), expected ); + } + else std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": Unexpected exception in \"" << comment << '"' << std::endl; + } + else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl; + return result; + } + else throw std::logic_error( "Somehow we didn't setup impl, and it's not an adapted case!" ); + } + +#if 0 + template< typename T_= return_type, typename= std::enable_if_t< not std::is_class_v< std::decay_t< T_ > > > > + BasicUniversalHandler( const T_ expected ) + : impl + { + [expected]( Invoker invoker, const std::string &comment ) + { + static_assert( not Aggregate< T_ > ); + static_assert( not std::is_class_v< T_ > ); + const return_type witness= invoker(); + const auto result= witness == expected ? TestResult::Passed : TestResult::Failed; + + if( result == TestResult::Failed ) + { + std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl; + printDebugging< outputMode >( witness, expected ); + } + else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl; + return result; + } + } + {} +#endif + +#if 0 + template< typename ... Args > + requires ConstructibleFrom< return_type, std::decay_t< Args >... > + BasicUniversalHandler( Args &&... expected_init ) + : BasicUniversalHandler( return_type{ std::forward< Args >( expected_init )... } ) + {} +#endif + +#if 1 + template< typename T > + requires( not SameAs< T, void > ) + BasicUniversalHandler( std::type_identity< T > ) : impl + { + []( Invoker invoker, const std::string &comment ) + { + try + { + std::ignore= invoker(); + breakpoint(); + std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl; + return TestResult::Failed; + } + catch( const T & ) + { + std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl; + return TestResult::Passed; + } + } + } + {} + + template< typename T > + requires( SameAs< T, std::type_identity< void > > or SameAs< T, std::nothrow_t > ) + BasicUniversalHandler( T ) : impl + { + []( Invoker invoker, const std::string &comment ) + { + try + { + std::ignore= invoker(); + std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl; + return TestResult::Passed; + } + catch( ... ) + { + std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl; + return TestResult::Failed; + } + } + } + {} + + template< DerivedFrom< std::exception > T > + BasicUniversalHandler( const T exemplar ) : impl + { + [expected= std::string{ exemplar.what() }]( Invoker invoker, const std::string &comment ) + { + 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; + } + catch( const T &ex ) + { + const std::string witness= ex.what(); + const TestResult rv= witness == expected ? TestResult::Passed : TestResult::Failed; + 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 ); + } + return rv; + } + } + } + {} +#endif + }; + + template< typename return_type, OutputMode outputMode > + struct BasicUniversalHandler + : return_type + { + using return_type::return_type; + + BasicUniversalHandler( return_type rt ) : return_type( rt ) {} + + using Invoker= std::function< return_type () >; + + std::function< TestResult ( Invoker, const std::string & ) > impl; + + TestResult + operator() ( Invoker invoker, const std::string &comment ) const + { + if( impl != nullptr ) return impl( invoker, comment ); + //if constexpr( std::is_base_of_v< std::decay_t< return_type >, ComputedBase > ) + if constexpr( true ) + { + const return_type *const expected_p= this; + const auto expected= *expected_p; + const auto witness= Utility::evaluate <=[&]() -> std::optional< return_type > + { + try + { + return invoker(); + } + catch( ... ) + { + return std::nullopt; + } + }; + const auto result= witness == expected ? TestResult::Passed : TestResult::Failed; + + if( result == TestResult::Failed ) + { + if( witness.has_value() ) + { + std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl; + printDebugging< outputMode >( witness.value(), expected ); + } + else std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": Unexpected exception in \"" << comment << '"' << std::endl; + } + else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl; + return result; + } + else throw std::logic_error( "Somehow we didn't setup impl, and it's not an adapted case!" ); + } + +#if 0 + template< typename T_= return_type, typename= std::enable_if_t< not std::is_class_v< std::decay_t< T_ > > > > + BasicUniversalHandler( const T_ expected ) + : impl + { + [expected]( Invoker invoker, const std::string &comment ) + { + static_assert( not Aggregate< T_ > ); + static_assert( not std::is_class_v< T_ > ); + const return_type witness= invoker(); + const auto result= witness == expected ? TestResult::Passed : TestResult::Failed; + + if( result == TestResult::Failed ) + { + std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl; + printDebugging< outputMode >( witness, expected ); + } + else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl; + return result; + } + } + {} +#endif + +#if 0 + template< typename ... Args > + requires ConstructibleFrom< return_type, std::decay_t< Args >... > + BasicUniversalHandler( Args &&... expected_init ) + : BasicUniversalHandler( return_type{ std::forward< Args >( expected_init )... } ) + {} +#endif + +#if 1 + template< typename T > + requires( not SameAs< T, void > ) + BasicUniversalHandler( std::type_identity< T > ) : impl + { + []( Invoker invoker, const std::string &comment ) + { + try + { + std::ignore= invoker(); + std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl; + return TestResult::Failed; + } + catch( const T & ) + { + std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl; + return TestResult::Passed; + } + } + } + {} + + template< typename T > + requires( SameAs< T, std::type_identity< void > > or SameAs< T, std::nothrow_t > ) + BasicUniversalHandler( T ) : impl + { + []( Invoker invoker, const std::string &comment ) + { + try + { + std::ignore= invoker(); + std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl; + return TestResult::Passed; + } + catch( ... ) + { + std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl; + return TestResult::Failed; + } + } + } + {} + + template< DerivedFrom< std::exception > T > + BasicUniversalHandler( const T exemplar ) : impl + { + [expected= std::string{ exemplar.what() }]( Invoker invoker, const std::string &comment ) + { + 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; + } + catch( const T &ex ) + { + const std::string witness= ex.what(); + const TestResult rv= witness == expected ? TestResult::Passed : TestResult::Failed; + 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 ); + } + return rv; + } + } + } + {} +#endif + }; + + + template< typename F > concept FunctionVariable= requires( const F &f ) @@ -66,8 +570,6 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test template< FunctionVariable auto, OutputMode outputMode= OutputMode::All > struct TableTest; } - inline void breakpoint() {} - namespace C { const bool debug= false; @@ -288,7 +790,7 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test } }; - struct ExceptionCases + struct ExceptionCases_real { using Invoker= std::function< void () >; struct ExceptionHandler @@ -312,6 +814,23 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test {} template< typename T > + requires( SameAs< T, std::type_identity< void > > or SameAs< T, std::nothrow_t > ) + ExceptionHandler( T ) : impl + { + []( Invoker invoker ) + { + try + { + invoker(); + return true; + } + catch( ... ) { return false; } + } + } + {} + + template< typename T > + requires( not SameAs< T, void > ) ExceptionHandler( std::type_identity< T > ) : impl { []( Invoker invoker ) @@ -376,7 +895,7 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test explicit - ExceptionCases( std::initializer_list< TestDescription > initList ) + ExceptionCases_real( std::initializer_list< TestDescription > initList ) : tests( initList ) {} int @@ -400,66 +919,66 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test return failureCount; } }; + + using ComputedBase= compute_base_t< return_type >; - class UniversalCases + struct UniversalCases { using RunDescription= std::tuple< std::string, args_type, return_type >; using Invoker= std::function< return_type () >; - enum class TestResult { Passed, Failed }; - - struct UniversalHandler - { - std::function< TestResult ( Invoker, const std::string & ) > impl; - - bool operator() ( Invoker invoker ) const { return impl( invoker ); } - - UniversalHandler( const return_type expected ) : impl - { - [expected]( Invoker invoker, const std::string &comment ) - { - const auto witness= invoker(); - const auto result= witness == expected ? TestResult::Passed : TestResult::Failed; - - if( result == TestResult::Failed ) - { - std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl; - printDebugging< outputMode >( witness, expected ); - } - else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl; - return result; - } - } - {} - - template< typename T > - UniversalHandler( std::type_identity< T > ) : impl - { - []( Invoker invoker ) - { - try { std::ignore= invoker(); } - catch( const T & ) { return TestResult::Passed; } - return TestResult::Failed; - } - } - {} - - UniversalHandler( const DerivedFrom< std::exception > auto exemplar ) : impl - { - [expected= std::string{ exemplar.what() }]( Invoker invoker ) - { - throw "Unimpl"; - } - } - {} - }; + using UniversalHandler= BasicUniversalHandler< return_type, outputMode >; using TestDescription= std::tuple< std::string, args_type, UniversalHandler >; + std::vector< TestDescription > tests; + + UniversalCases( std::initializer_list< TestDescription > initList ) + { + for( const auto &desc: initList ) + { + if constexpr( Aggregate< return_type > ) + { + std::cerr << "Case: " << std::get< 0 >( desc ); + const return_type &v= std::get< 2 >( desc ); + std::cerr << " (" << v << ")" << std::endl; + tests.push_back( desc ); + } + } + } + + int + operator() () const + { + int failureCount= 0; + for( const auto &[ comment, params, checker ]: tests ) + { + if( C::debugCaseTypes ) std::cerr << boost::core::demangle( typeid( params ).name() ) << std::endl; + auto invoker= [&] + { + breakpoint(); + return std::apply( function, params ); + }; + const TestResult result= checker( invoker, comment ); + if( result == TestResult::Failed ) + { + std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl; + ++failureCount; + } + else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl; + breakpoint(); + } + + return failureCount; + } }; // When the `UniversalCases` impl is ready to go, then this alias shim can be redirected to that form. Then I can // retire the `ExceptionCases` and `ExecutionCases` forms and replace them with an alias to `UniversalCases`. - using Cases= ExecutionCases; + //using Cases= ExecutionCases; + using Cases= UniversalCases; + + //using ExceptionCases= ExceptionCases_real; + using ExceptionCases= UniversalCases; }; #ifdef DISABLED diff --git a/Testing/TableTest.test/test2.cc b/Testing/TableTest.test/test2.cc index 7b412ea..ecad23a 100644 --- a/Testing/TableTest.test/test2.cc +++ b/Testing/TableTest.test/test2.cc @@ -1,9 +1,15 @@ static_assert( __cplusplus > 2020'00 ); #include + #include #include +#include +#include + +#include + namespace { @@ -28,11 +34,49 @@ namespace { "Righthand identity", { 25, 0 }, 25 }, }; + template< typename= Alepha::Capabilities< Alepha::IOStreams::OStreamable, Alepha::comparable > > + struct Aggregate_core + { + int x, y, z; + + friend bool operator == ( Aggregate_core, Aggregate_core ) noexcept= default; + }; + using Aggregate= Aggregate_core<>; + auto alltests= enroll <=[] { "addition.two.local"_test <=TableTest< add >::Cases { { "Negative case", { -10, -20 }, -30 }, }; + + "Can we use Aggregates with universal cases, correctly?"_test <= + TableTest + < + []( const int x ) + { + if( x < 0 ) throw std::runtime_error{ "Cannot be negative." }; + return Aggregate{ x, x, x }; + } + > + ::UniversalCases + { + { "Basic value case", { 42 }, { 42, 42, 42 } }, + { "Ignore exceptions case (`std::nothrow`)", { 42 }, std::nothrow }, + { "Ignore exceptions case (`std::type_identity< void >`)", { 42 }, std::type_identity< void >{} }, + { "Expect exception type runtime_error", { -42 }, std::type_identity< std::runtime_error >{} }, + { "Expect exception type exception", { -42 }, std::type_identity< std::exception >{} }, + { "Expect exception value specific", { -42 }, std::runtime_error{ "Cannot be negative." } }, + + /* These cases should fail, but we don't want to fail them in normal builds. */ + #if 0 + { "Failing: Basic value case", { -42 }, { 42, 42, 42 } }, + { "Failing: Ignore exceptions case (`std::nothrow`)", { -42 }, std::nothrow }, + { "Failing: Ignore exceptions case (`std::type_identity< void >`)", { -42 }, std::type_identity< void >{} }, + { "Failing: Expect exception type runtime_error", { 42 }, std::type_identity< std::runtime_error >{} }, + { "Failing: Expect exception type exception", { 42 }, std::type_identity< std::exception >{} }, + { "Failing: Expect exception value specific", { 42 }, std::runtime_error{ "Cannot be negative." } }, + #endif + }; }; } diff --git a/Testing/test.cc b/Testing/test.cc index 059f195..d940fbf 100644 --- a/Testing/test.cc +++ b/Testing/test.cc @@ -61,6 +61,7 @@ namespace Alepha::Hydrogen::Testing::detail::testing throw; } catch( const TestFailure &fail ) { std::cout << " -- " << fail.failureCount << " failures."; } + catch( const std::exception &ex ) { std::cout << " -- unknown failure count (mesg: " << ex.what() << ")"; } catch( ... ) { std::cout << " -- unknown failure count"; } std::cout << std::endl; } diff --git a/string_algorithms.test/0.cc b/string_algorithms.test/0.cc index 93ec0f4..3c3887e 100644 --- a/string_algorithms.test/0.cc +++ b/string_algorithms.test/0.cc @@ -42,7 +42,11 @@ static auto init= enroll <=[] { { "Complete var", { "$H$ $W$", { { "H", lambaste<="Hello" }, { "W", lambaste<="World" } }, '$' }, - {} + std::nothrow + }, + { "Complete var", + { "$H$ $W$", { { "H", lambaste<="Hello" }, { "W", lambaste<="World" } }, '$' }, + std::type_identity< void >{} }, { "Incomplete var", { "$H$ $W", { { "H", lambaste<="Hello" }, { "W", lambaste<="World" } }, '$' }, diff --git a/word_wrap.test/0.cc b/word_wrap.test/0.cc index 86d89fd..10bbaa1 100644 --- a/word_wrap.test/0.cc +++ b/word_wrap.test/0.cc @@ -39,7 +39,7 @@ static auto init= Alepha::Utility::enroll <=[] { "Trailing spaces should be kept", { helloWorld + " ", 100, 0 }, helloWorld + " " }, { "Trailing spaces should be dropped", { helloWorld + " ", helloWorld.size(), 0 }, helloWorld }, { "All but 2 trailing spaces dropped", { helloWorld + " ", helloWorld.size() + 2, 0 }, - helloWorld + " "}, + helloWorld + " " }, { "Split line", { helloWorld, 8, 0 }, "Hello \nWorld" }, // TODO: Should we swallow trailing spaces? };