From 7ceef7e1b106e0b2b856ffa4054eb639c45cd80a Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Thu, 1 Jul 2021 01:42:11 -0400 Subject: [PATCH] Starting out Alepha from scratch, as a C++17 effort. This one is going to be prepped for GitHub private, from the get go. This initial commit has some evaluation helpers and the unit testing infrastructure. `TableTest` will come next. --- Alepha.h | 8 +++ Makefile | 1 + Testing/Makefile | 3 + Testing/test.cc | 28 ++++++++ Testing/test.h | 165 +++++++++++++++++++++++++++++++++++++++++++ Utility/evaluation.h | 48 +++++++++++++ 6 files changed, 253 insertions(+) create mode 100644 Alepha.h create mode 100644 Makefile create mode 100644 Testing/Makefile create mode 100644 Testing/test.cc create mode 100644 Testing/test.h create mode 100644 Utility/evaluation.h diff --git a/Alepha.h b/Alepha.h new file mode 100644 index 0000000..2d17219 --- /dev/null +++ b/Alepha.h @@ -0,0 +1,8 @@ +static_assert( __cplusplus > 201700, "C++17 Required" ); + +#pragma once + +namespace Alepha +{ + inline namespace Hydrogen {} +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2e93091 --- /dev/null +++ b/Makefile @@ -0,0 +1 @@ +CXXFLAGS+= -std=c++17 diff --git a/Testing/Makefile b/Testing/Makefile new file mode 100644 index 0000000..3ec6ce7 --- /dev/null +++ b/Testing/Makefile @@ -0,0 +1,3 @@ +CXXFLAGS+= -std=c++17 -I ../ + +all: test diff --git a/Testing/test.cc b/Testing/test.cc new file mode 100644 index 0000000..716f172 --- /dev/null +++ b/Testing/test.cc @@ -0,0 +1,28 @@ +#include +#include + +namespace +{ + namespace UnitTest= Alepha::Testing::exports::testing; +} + +int +main( const int argcnt, const char *const *const argvec ) +{ + return UnitTest::runAllTests( argcnt, argvec ); +} + +namespace +{ + using namespace UnitTest::literals; + using namespace Alepha::Utility::exports::evaluation; + + auto registration= enroll <=[] + { + "enroll.basic.success"_test <=[]{}; + -"enroll.basic.failure"_test <=[]{ throw 0; }; + }; + + auto named1= "named.basic.success"_test <= []{}; + auto named2= -"named.basic.failure"_test <=[]{ return 1; }; +} diff --git a/Testing/test.h b/Testing/test.h new file mode 100644 index 0000000..b701bed --- /dev/null +++ b/Testing/test.h @@ -0,0 +1,165 @@ +static_assert( __cplusplus > 201700, "C++17 Required" ); + +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace Alepha::Hydrogen::Testing +{ + inline namespace exports { inline namespace testing {} } + + namespace detail::testing + { + inline namespace exports {} + + using namespace std::literals::string_literals; + + namespace C + { + const std::string csi= "\e["; + const std::string green= C::csi + "32m"; + const std::string red= C::csi + "31m"; + const std::string normal= C::csi + "0m"; + } + + struct TestName + { + std::string name; + bool disabled= false; + + [[nodiscard]] TestName + operator -() const + { + auto rv= *this; + + rv.disabled= true; + + return rv; + } + }; + + namespace exports + { + struct TestFailureException; + + inline namespace literals + { + [[nodiscard]] inline auto + operator""_test( const char name[], std::size_t amount ) + { + return TestName{ std::string{ name, name + amount } }; + } + } + } + + inline auto & + registry() + { + static std::vector< std::tuple< std::string, bool, std::function< void() > > > registry; + return registry; + } + + // It is okay to discard this, if making tests in an enroll block. + inline auto + operator <= ( TestName name, std::function< void () > test ) + { + struct TestRegistration {} rv; + + registry().emplace_back( name.name, name.disabled, test ); + assert( not registry().empty() ); + assert( std::get< 1 >( registry().back() ) == name.disabled ); + + return rv; + }; + + struct exports::TestFailureException + { + int failureCount= -1; + }; + + inline auto + operator <= ( TestName name, std::function< int () > test ) + { + auto wrapper= [test] + { + const int failures= test(); + if( failures > 0 ) throw TestFailureException{ failures }; + }; + + return name <= wrapper; + } + + template< typename TestFunc > + inline auto + operator <= ( TestName name, TestFunc test ) + { + return name <= std::function{ test }; + } + + namespace exports + { + [[nodiscard]] inline int + runAllTests( const std::vector< std::string > selections= {} ) + { + bool failed= false; + const auto selected= [ selections ]( const std::string test ) + { + for( const auto &selection: selections ) + { + if( test.find( selection ) != std::string::npos ) return true; + } + return empty( selections ); + }; + + const auto explicitlyNamed= [ selections ]( const std::string s ) + { + return std::find( begin( selections ), end( selections ), s ) != end( selections ); + }; + + for( const auto &[ name, disabled, test ]: registry() ) + try + { + if( explicitlyNamed( name ) or not disabled and selected( name ) ) + { + test(); + std::cout << C::green << "SUCCESS" << C::normal << ": " << name << std::endl; + } + } + catch( ... ) + { + try + { + failed= true; + std::cout << C::red << "FAILURE" << C::normal << ": " << name; + throw; + } + catch( const TestFailureException &fail ) { std::cout << " -- " << fail.failureCount << " failures."; } + catch( ... ) { std::cout << " -- unknown failure count"; } + std::cout << std::endl; + } + + return failed ? EXIT_FAILURE : EXIT_SUCCESS; + } + + [[nodiscard]] inline int + runAllTests( const unsigned argcnt, const char *const *const argvec ) + { + return runAllTests( { argvec + 1, argvec + argcnt } ); + } + } + } + + namespace exports::testing + { + using namespace detail::testing::exports; + } +} diff --git a/Utility/evaluation.h b/Utility/evaluation.h new file mode 100644 index 0000000..c412fc1 --- /dev/null +++ b/Utility/evaluation.h @@ -0,0 +1,48 @@ +static_assert( __cplusplus > 201700, "C++17 Required" ); + +#pragma once + +#include + +#include +#include + +namespace Alepha::Hydrogen::Utility +{ + inline namespace exports { inline namespace evaluation {} } + + namespace detail::evaluation + { + struct evaluate_t {}; + struct enroll_t {}; + + inline namespace exports + { + inline constexpr evaluate_t evaluate; + inline constexpr enroll_t enroll; + } + + template< typename Function > + decltype( auto ) + operator <=( evaluate_t, Function &&init ) + { + return std::forward< Function >( init )(); + } + + template< typename Init > + auto + operator <=( enroll_t, Init init ) + { + struct {} registration; + + (void) ( evaluate <=init ); + + return registration; + } + } + + namespace exports::evaluation + { + using namespace detail::evaluation::exports; + } +}