feat: initial test module implementation
This commit is contained in:
		
							parent
							
								
									1247b1ac69
								
							
						
					
					
						commit
						bddef4238d
					
				
					 7 changed files with 274 additions and 0 deletions
				
			
		|  | @ -21,3 +21,4 @@ add_subdirectory(./app) | |||
| # apps  | ||||
| add_subdirectory(./mirror) | ||||
| 
 | ||||
| add_subdirectory(test) | ||||
|  |  | |||
							
								
								
									
										4
									
								
								modules/test/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								modules/test/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | |||
| add_library_module(test test.cpp entrypoint.cpp) | ||||
| 
 | ||||
| add_executable(test_tests ${CMAKE_CURRENT_SOURCE_DIR}/tests/test.cpp) | ||||
| target_link_libraries(test_tests PRIVATE test) | ||||
							
								
								
									
										128
									
								
								modules/test/include/test/expects.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								modules/test/include/test/expects.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,128 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <concepts> | ||||
| #include <format> | ||||
| #include <source_location> | ||||
| 
 | ||||
| namespace lt::test { | ||||
| 
 | ||||
| template<typename T> | ||||
| concept Printable = requires(std::ostream &os, T t) { | ||||
| 	{ os << t } -> std::same_as<std::ostream &>; | ||||
| }; | ||||
| 
 | ||||
| template<typename T> | ||||
| concept Testable = Printable<T> && std::equality_comparable<T>; | ||||
| 
 | ||||
| constexpr void expect_eq( | ||||
|     Testable auto lhs, | ||||
|     Testable auto rhs, | ||||
|     std::source_location source_location = std::source_location::current() | ||||
| ) | ||||
| { | ||||
| 	if (lhs != rhs) | ||||
| 	{ | ||||
| 		throw std::runtime_error { | ||||
| 			std::format( | ||||
| 			    "Failed equality expectation:\n" | ||||
| 			    "\tactual: {}\n" | ||||
| 			    "\texpected: {}\n" | ||||
| 			    "\tlocation: {}:{}", | ||||
| 			    lhs, | ||||
| 			    rhs, | ||||
| 			    source_location.file_name(), | ||||
| 			    source_location.line() | ||||
| 			), | ||||
| 		}; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| constexpr void expect_ne( | ||||
|     Testable auto lhs, | ||||
|     Testable auto rhs, | ||||
|     std::source_location source_location = std::source_location::current() | ||||
| ) | ||||
| { | ||||
| 	if (lhs == rhs) | ||||
| 	{ | ||||
| 		throw std::runtime_error { | ||||
| 			std::format( | ||||
| 			    "Failed un-equality expectation:\n" | ||||
| 			    "\tactual: {}\n" | ||||
| 			    "\texpected: {}\n" | ||||
| 			    "\tlocation: {}:{}", | ||||
| 			    lhs, | ||||
| 			    rhs, | ||||
| 			    source_location.file_name(), | ||||
| 			    source_location.line() | ||||
| 			), | ||||
| 		}; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| constexpr void expect_true( | ||||
|     bool expression, | ||||
|     std::source_location source_location = std::source_location::current() | ||||
| ) | ||||
| { | ||||
| 	if (!expression) | ||||
| 	{ | ||||
| 		throw std::runtime_error { | ||||
| 			std::format( | ||||
| 			    "Failed true expectation:\n" | ||||
| 			    "\tactual: {}\n" | ||||
| 			    "\texpected: true\n" | ||||
| 			    "\tlocation: {}:{}", | ||||
| 			    expression, | ||||
| 			    source_location.file_name(), | ||||
| 			    source_location.line() | ||||
| 			), | ||||
| 		}; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| constexpr void expect_false( | ||||
|     bool expression, | ||||
|     std::source_location source_location = std::source_location::current() | ||||
| ) | ||||
| { | ||||
| 	if (expression) | ||||
| 	{ | ||||
| 		throw std::runtime_error { | ||||
| 			std::format( | ||||
| 			    "Failed false expectation:\n" | ||||
| 			    "\tactual: {}\n" | ||||
| 			    "\texpected: true\n" | ||||
| 			    "\tlocation: {}:{}", | ||||
| 			    expression, | ||||
| 			    source_location.file_name(), | ||||
| 			    source_location.line() | ||||
| 			), | ||||
| 		}; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| constexpr void expect_le( | ||||
|     Testable auto lhs, | ||||
|     Testable auto rhs, | ||||
|     std::source_location source_location = std::source_location::current() | ||||
| ) | ||||
| { | ||||
| 	if (lhs > rhs) | ||||
| 	{ | ||||
| 		throw std::runtime_error { | ||||
| 			std::format( | ||||
| 			    "Failed false expectation:\n" | ||||
| 			    "\tactual: {}\n" | ||||
| 			    "\texpected: >= {}\n" | ||||
| 			    "\tlocation: {}:{}", | ||||
| 			    lhs, | ||||
| 			    rhs, | ||||
| 			    source_location.file_name(), | ||||
| 			    source_location.line() | ||||
| 			), | ||||
| 		}; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| } // namespace lt::test
 | ||||
							
								
								
									
										101
									
								
								modules/test/include/test/test.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								modules/test/include/test/test.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,101 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <concepts> | ||||
| #include <test/expects.hpp> | ||||
| 
 | ||||
| namespace lt::test { | ||||
| 
 | ||||
| namespace concepts { | ||||
| 
 | ||||
| template<typename T> | ||||
| concept printable = requires(std::ostream &os, T t) { | ||||
| 	{ os << t } -> std::same_as<std::ostream &>; | ||||
| }; | ||||
| 
 | ||||
| template< | ||||
|     class T, | ||||
|     auto expr = | ||||
|         [] { | ||||
|         }> | ||||
| concept test = requires(T test) { | ||||
| 	{ test.name } -> printable; | ||||
| 
 | ||||
| 	{ test = expr } -> std::same_as<void>; | ||||
| }; | ||||
| 
 | ||||
| } // namespace concepts
 | ||||
| 
 | ||||
| 
 | ||||
| struct Case | ||||
| { | ||||
| 	auto operator=(std::invocable auto test) -> void // NOLINT
 | ||||
| 	{ | ||||
| 		std::cout << "Running... " << name; | ||||
| 
 | ||||
| 		try | ||||
| 		{ | ||||
| 			test(); | ||||
| 		} | ||||
| 		catch (const std::exception &exp) | ||||
| 		{ | ||||
| 			std::cout << " --> FAIL !" << '\n'; | ||||
| 			std::cout << exp.what() << "\n\n"; | ||||
| 			return; // TODO(Light): Should we run the remaining tests after a failure?
 | ||||
| 		} | ||||
| 
 | ||||
| 		std::cout << " --> SUCCESS :D" << "\n"; | ||||
| 	} | ||||
| 
 | ||||
| 	std::string_view name; | ||||
| }; | ||||
| 
 | ||||
| namespace details { | ||||
| 
 | ||||
| 
 | ||||
| class Registry | ||||
| { | ||||
| public: | ||||
| 	using Suite = void (*)(); | ||||
| 
 | ||||
| 	static void register_suite(Suite suite) | ||||
| 	{ | ||||
| 		instance().m_suites.emplace_back(suite); | ||||
| 	} | ||||
| 
 | ||||
| 	static void run_all() | ||||
| 	{ | ||||
| 		for (auto &test : instance().m_suites) | ||||
| 		{ | ||||
| 			test(); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| private: | ||||
| 	Registry() = default; | ||||
| 
 | ||||
| 	[[nodiscard]] static auto instance() -> Registry & | ||||
| 	{ | ||||
| 		static auto registry = Registry {}; | ||||
| 		return registry; | ||||
| 	} | ||||
| 
 | ||||
| 	std::vector<void (*)()> m_suites; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| } // namespace details
 | ||||
| 
 | ||||
| struct TestSuite | ||||
| { | ||||
| 	template<class TSuite> | ||||
| 	constexpr TestSuite(TSuite suite) | ||||
| 	{ | ||||
| #ifndef LIGHT_SKIP_TESTS | ||||
| 		details::Registry::register_suite(+suite); | ||||
| #endif | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| using Suite = const TestSuite; | ||||
| 
 | ||||
| } // namespace lt::test
 | ||||
							
								
								
									
										15
									
								
								modules/test/src/entrypoint.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								modules/test/src/entrypoint.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | |||
| #include <test/test.hpp> | ||||
| 
 | ||||
| auto main() -> int32_t | ||||
| try | ||||
| { | ||||
| 	using namespace ::lt::test; | ||||
| 	using namespace ::lt::test::details; | ||||
| 
 | ||||
| 	Registry::run_all(); | ||||
| } | ||||
| catch (const std::exception &exp) | ||||
| { | ||||
| 	std::cout << "Terminated after uncaught exception:\n"; // NOLINT
 | ||||
| 	std::cout << "exception.what: " << exp.what(); | ||||
| } | ||||
							
								
								
									
										0
									
								
								modules/test/src/test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								modules/test/src/test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										25
									
								
								modules/test/tests/test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								modules/test/tests/test.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | |||
| #include <test/test.hpp> | ||||
| 
 | ||||
| lt::test::Suite meta = []() { | ||||
| 	using lt::test::expect_eq; | ||||
| 
 | ||||
| 	lt::test::Case { "test_1" } = [] { | ||||
| 		expect_eq(5, 5); | ||||
| 	}; | ||||
| 
 | ||||
| 	lt::test::Case { "test_2" } = [] { | ||||
| 		expect_eq(20.0, 20.0); | ||||
| 	}; | ||||
| 
 | ||||
| 	lt::test::Case { "test_3" } = [] { | ||||
| 		expect_eq(true, false); | ||||
| 	}; | ||||
| 
 | ||||
| 	lt::test::Case { "test_4" } = [] { | ||||
| 		expect_eq(true, 1); | ||||
| 	}; | ||||
| 
 | ||||
| 	lt::test::Case { "test_5" } = [] { | ||||
| 		throw std::runtime_error("Uncaught std exception!"); | ||||
| 	}; | ||||
| }; | ||||
		Loading…
	
	Add table
		
		Reference in a new issue