#pragma once #include <iostream> // inspired by boost/core/lightweight_test.hpp - lightweight test library #define AMDIS_TEST(expr) \ ::AMDiS::Impl::_test(expr, #expr, __FILE__, __LINE__) #define AMDIS_TEST_EQ(expr, value) \ ::AMDiS::Impl::_test_eq(expr, value, #expr, #value, __FILE__, __LINE__) #define AMDIS_TEST_NE(expr, value) \ ::AMDiS::Impl::_test_ne(expr, value, #expr, #value, __FILE__, __LINE__) #define AMDIS_TEST_TOL 1.e-10 #define AMDIS_TEST_APPROX(expr, value) \ ::AMDiS::Impl::_test_approx(expr, value, #expr, #value, __FILE__, __LINE__) #ifdef DEC_NO_THROW #define AMDIS_TEST_THROWS(expr) #else #define AMDIS_TEST_THROWS(expr) \ try { \ expr; \ ::AMDiS::Impl::_test_throws(#expr, __FILE__, __LINE__); \ } catch(...) {} #endif namespace AMDiS { namespace Impl { /// global counter for the number of detected errors inline int& num_errors() { static int num = 0; return num; } } // end namespace Impl /// Returns 0 if no errors are detected, otherwise returns 1. Must be called at the end /// of main(). inline int report_errors() { int errors = Impl::num_errors(); if (errors == 0) { std::cout << "No errors detected.\n"; return 0; } else { std::cerr << errors << " error(s) detected.\n"; return 1; } } namespace Impl { /// Tests whether an expression evaluates to true inline void _test(bool success, char const* expr, char const* file, size_t line) { if (!success) { std::cerr << file << ":" << line << " TEST( " << expr << " ) failed\n"; num_errors()++; } } /// Tests whether the value of an expression is equal to an expected value template <class T1, class T2> inline void _test_eq(T1 const& expr_value, T2 const& value, char const* expr_str, char const* value_str, char const* file, size_t line) { using T = std::common_type_t<T1,T2>; if (T(expr_value) != T(value)) { std::cerr << file << ":" << line << " TEST( " << expr_str << " == " << value_str << " ) failed: " << expr_value << " != " << value << "\n"; num_errors()++; } } /// Tests whether the value of an expression is not equal to an expected value template <class T1, class T2> inline void _test_ne(T1 const& expr_value, T2 const& value, char const* expr_str, char const* value_str, char const* file, size_t line) { using T = std::common_type_t<T1,T2>; if (T(expr_value) == T(value)) { std::cerr << file << ":" << line << " TEST( " << expr_str << " != " << value_str << " ) failed: " << expr_value << " == " << value << "\n"; num_errors()++; } } template <class T> constexpr T _abs(T const& x) { return x < T(0) ? -x : x; } /// Tests whether the value of an expression is approximately equal to an expected value // implementation for scalars template <class T1, class T2, std::enable_if_t<std::is_arithmetic<T1>::value && std::is_arithmetic<T2>::value, int>* = nullptr> inline void _test_approx(T1 const& expr_value, T2 const& value, char const* expr_str, char const* value_str, char const* file, size_t line) { if (_abs(expr_value - value) > AMDIS_TEST_TOL) { std::cerr << file << ":" << line << " TEST( " << expr_str << " ~= " << value_str << " ) failed: " << expr_value << " != " << value << "\n"; num_errors()++; } } // implementation for pair template <class T11, class T12, class T21, class T22> inline void _test_approx(std::pair<T11,T12> const& expr_value, std::pair<T21,T22> const& value, char const* expr_str, char const* value_str, char const* file, size_t line) { if (_abs(expr_value.first - value.first) > AMDIS_TEST_TOL || _abs(expr_value.second - value.second) > AMDIS_TEST_TOL) { std::cerr << file << ":" << line << " TEST( " << expr_str << " ~= " << value_str << " ) failed: (" << expr_value.first << "," << expr_value.second << ") != (" << value.first << "," << value.second << ")\n"; num_errors()++; } } // implementation for ranges template <class T1, class T2, class = decltype(((void)std::declval<T1>().cbegin(), (void)std::declval<T2>().cbegin())) > inline void _test_approx(T1 const& expr_range, T2 const& range, char const* expr_str, char const* value_str, char const* file, size_t line) { auto it1 = expr_range.cbegin(); auto it2 = range.cbegin(); for(; it1 != expr_range.cend(); ++it1, ++it2) _test_approx(*it1, *it2, expr_str, value_str, file, line); } /// Tests whether an expression throws an exception template <class T1, class T2> inline void _test_throws(char const* expr, char const* file, size_t line) { std::cerr << file << ":" << line << " EXPR( " << expr << " ) should throw\n"; num_errors()++; } } // end namspace Impl } // end namespace Dec