/** * The Attractor Unit Test library * * This file is distributed under the MIT License. See LICENSE for details. * * Usage: * * #include * * int var_to_test = 1; * int expected_value = 1; * * ATT_ASSERT(var_to_test, expected_value, "one == one") */ #ifndef ATT_TEST_H #define ATT_TEST_H #include #ifdef __cplusplus #include #include #include #include // In C++, _Bool doesn't exist, so map it to bool #ifndef _Bool #define _Bool bool #endif extern "C" { #endif #ifndef ATT_API #define ATT_API #endif #ifndef ATT_VERBOSE #define ATT_VERBOSE 1 #endif #ifndef ATT_SHOW_ERROR #define ATT_SHOW_ERROR 1 #endif // Tolerance for float, double and long double comparisons. Defaults to 0 (exact // equality). Define a small value (e.g. 1e-6) to compare within an epsilon. #ifndef ATT_FLOAT_EPSILON #define ATT_FLOAT_EPSILON 0 #endif #ifndef ATT_STRING_AS_POINTERS #define ATT_STRING_AS_POINTERS 0 #endif #ifndef ATT_CUSTOM_TYPES #define ATT_CUSTOM_TYPES #endif // clang-format off #ifndef __cplusplus #define ATT_ASSERT(VALUE, EXPECTED, MESSAGE) \ (att_set_assert_context(#VALUE, __FILE__, __LINE__), _Generic(VALUE, \ ATT_CUSTOM_TYPES \ char: att_assert_c, \ unsigned char: att_assert_u_c, \ char *: att_assert_p_c, \ const char *: att_assert_cp_c, \ short: att_assert_hd, \ unsigned short: att_assert_u_hu, \ int: att_assert_d, \ unsigned int: att_assert_u_u, \ long: att_assert_ld, \ unsigned long: att_assert_u_lu, \ long long: att_assert_lld, \ unsigned long long: att_assert_u_llu, \ float: att_assert_f, \ double: att_assert_lf, \ long double: att_assert_Lf, \ void *: att_assert_p_p, \ _Bool: att_assert_b, \ default: att_assert_unknown \ )(VALUE, EXPECTED, MESSAGE, __FILE__, __LINE__)); #else #define ATT_ASSERT(VALUE, EXPECTED, MESSAGE) \ (att_set_assert_context(#VALUE, __FILE__, __LINE__), \ att_assert_cpp(VALUE, EXPECTED, MESSAGE, __FILE__, __LINE__)); #endif ATT_API unsigned int att_assert_c(char result, char expected, const char *description, const char *file, unsigned int line); ATT_API unsigned int att_assert_u_c(unsigned char result, unsigned char expected, const char *description, const char *file, unsigned int line); ATT_API unsigned int att_assert_p_c(char *result, char *expected, const char *description, const char *file, unsigned int line); ATT_API unsigned int att_assert_cp_c(const char *result, const char *expected, const char *description, const char *file, unsigned int line); ATT_API unsigned int att_assert_hd(short result, short expected, const char *description, const char *file, unsigned int line); ATT_API unsigned int att_assert_u_hu(unsigned short result, unsigned short expected, const char *description, const char *file, unsigned int line); ATT_API unsigned int att_assert_d(int result, int expected, const char *description, const char *file, unsigned int line); ATT_API unsigned int att_assert_u_u(unsigned int result, unsigned int expected, const char *description, const char *file, unsigned int line); ATT_API unsigned int att_assert_ld(long result, long expected, const char *description, const char *file, unsigned int line); ATT_API unsigned int att_assert_u_lu(unsigned long result, unsigned long expected, const char *description, const char *file, unsigned int line); ATT_API unsigned int att_assert_lld(long long result, long long expected, const char *description, const char *file, unsigned int line); ATT_API unsigned int att_assert_u_llu(unsigned long long result, unsigned long long expected, const char *description, const char *file, unsigned int line); ATT_API unsigned int att_assert_f(float result, float expected, const char *description, const char *file, unsigned int line); ATT_API unsigned int att_assert_lf(double result, double expected, const char *description, const char *file, unsigned int line); ATT_API unsigned int att_assert_Lf(long double result, long double expected, const char *description, const char *file, unsigned int line); ATT_API unsigned int att_assert_p_p(void *result, void *expected, const char *description, const char *file, unsigned int line); ATT_API unsigned int att_assert_b(_Bool result, _Bool expected, const char *description, const char *file, unsigned int line); ATT_API unsigned int att_assert_unknown(void * result, void * expected, const char *description, const char *file, unsigned int line); unsigned int att_get_valid_tests(void); unsigned int att_get_total_tests(void); void att_set_verbose(unsigned int verbose); void att_set_show_error(unsigned int show_error); // Tolerance used for float, double and long double comparisons. Initialized to // ATT_FLOAT_EPSILON (0 by default, i.e. exact equality) and overridable at runtime. long double att_get_float_epsilon(void); void att_set_float_epsilon(long double epsilon); // A callback to be used when the default comparison fails. typedef int (*att_generic_callback)(void *result, void *expected, const char *description); void att_set_generic_callback(att_generic_callback callback); // A callback to be used when an test occurs. typedef int (*att_test_callback)(int test, const char *description, const char *expression, const char *file, unsigned int line); void att_set_test_callback(att_test_callback callback); // Stores the expression for the next assertion. Used internally by ATT_ASSERT. void att_set_assert_context(const char *expression, const char *file, unsigned int line); #ifdef __cplusplus } #endif // clang-format on #ifdef __cplusplus // Helper to check if common_type exists template struct has_common_type : std::false_type {}; template struct has_common_type::type>> : std::true_type {}; // C++ template function for att_assert to handle type deduction properly template inline unsigned int att_assert_cpp(T1 result, T2 expected, const char *description, const char *file, unsigned int line) { // Check if we can find a common type if constexpr(has_common_type::value) { // Convert both to a common type for comparison using common_type = std::common_type_t; common_type converted_result = static_cast(result); common_type converted_expected = static_cast(expected); // Handle different common types if constexpr(std::is_same_v) { return att_assert_c(converted_result, converted_expected, description, file, line); } else if constexpr(std::is_same_v) { return att_assert_u_c(converted_result, converted_expected, description, file, line); } else if constexpr(std::is_same_v) { return att_assert_hd(converted_result, converted_expected, description, file, line); } else if constexpr(std::is_same_v) { return att_assert_u_hu(converted_result, converted_expected, description, file, line); } else if constexpr(std::is_same_v) { return att_assert_d(converted_result, converted_expected, description, file, line); } else if constexpr(std::is_same_v) { return att_assert_u_u(converted_result, converted_expected, description, file, line); } else if constexpr(std::is_same_v) { return att_assert_ld(converted_result, converted_expected, description, file, line); } else if constexpr(std::is_same_v) { return att_assert_u_lu(converted_result, converted_expected, description, file, line); } else if constexpr(std::is_same_v) { return att_assert_lld(converted_result, converted_expected, description, file, line); } else if constexpr(std::is_same_v) { return att_assert_u_llu(converted_result, converted_expected, description, file, line); } else if constexpr(std::is_same_v) { return att_assert_f(converted_result, converted_expected, description, file, line); } else if constexpr(std::is_same_v) { return att_assert_lf(converted_result, converted_expected, description, file, line); } else if constexpr(std::is_same_v) { return att_assert_Lf(converted_result, converted_expected, description, file, line); } else if constexpr(std::is_same_v) { return att_assert_b(converted_result, converted_expected, description, file, line); } else if constexpr(std::is_same_v) { return att_assert_cp_c(result.c_str(), expected.c_str(), description, file, line); } else { // Unsupported type with common_type - compile error static_assert(sizeof(T1) == 0, "ATT_ASSERT: Unsupported type. Supported types are: char, short, int, long, long " "long (signed/unsigned), float, double, long double, bool, std::string, and " "pointer types."); return 0; } } else { // No common type - compile error static_assert(sizeof(T1) == 0, "ATT_ASSERT: Cannot compare incompatible types with no common type."); return 0; } } // Overloads for pointer types (can't use template easily for these) inline unsigned int att_assert_cpp(char *result, char *expected, const char *description, const char *file, unsigned int line) { return att_assert_p_c(result, expected, description, file, line); } inline unsigned int att_assert_cpp(const char *result, const char *expected, const char *description, const char *file, unsigned int line) { return att_assert_cp_c(result, expected, description, file, line); } inline unsigned int att_assert_cpp(void *result, void *expected, const char *description, const char *file, unsigned int line) { return att_assert_p_p(result, expected, description, file, line); } // Convert the int form of NULL through a pointer-sized integer. On LLP64 platforms (64-bit // Windows), long is narrower than a pointer. template inline T *att_pointer_from_long(long value) { return value == 0 ? nullptr : reinterpret_cast(static_cast(value)); } // Special overloads for legacy NULL pointer comparisons inline unsigned int att_assert_cpp(const char *result, long expected, const char *description, const char *file, unsigned int line) { return att_assert_cp_c(result, att_pointer_from_long(expected), description, file, line); } inline unsigned int att_assert_cpp(char *result, long expected, const char *description, const char *file, unsigned int line) { return att_assert_p_c(result, att_pointer_from_long(expected), description, file, line); } inline unsigned int att_assert_cpp(void *result, long expected, const char *description, const char *file, unsigned int line) { return att_assert_p_p(result, att_pointer_from_long(expected), description, file, line); } // Modern C++ null pointer comparisons inline unsigned int att_assert_cpp(const char *result, std::nullptr_t, const char *description, const char *file, unsigned int line) { return att_assert_cp_c(result, nullptr, description, file, line); } inline unsigned int att_assert_cpp(char *result, std::nullptr_t, const char *description, const char *file, unsigned int line) { return att_assert_p_c(result, nullptr, description, file, line); } inline unsigned int att_assert_cpp(void *result, std::nullptr_t, const char *description, const char *file, unsigned int line) { return att_assert_p_p(result, nullptr, description, file, line); } // Mixed char pointer types (char* vs const char*) inline unsigned int att_assert_cpp(char *result, const char *expected, const char *description, const char *file, unsigned int line) { return att_assert_cp_c(result, expected, description, file, line); } inline unsigned int att_assert_cpp(const char *result, char *expected, const char *description, const char *file, unsigned int line) { return att_assert_cp_c(result, expected, description, file, line); } // Generic pointer type catch-all (for any pointer types not covered by specific overloads) template inline unsigned int att_assert_cpp(T *result, T *expected, const char *description, const char *file, unsigned int line) { return att_assert_unknown((void *)result, (void *)expected, description, file, line); } #endif #endif /* ATT_TEST_H */