improved assertion message for memory assertions

This commit is contained in:
Gwendolyn 2022-01-03 17:26:10 +01:00
parent 19ebe3d7b6
commit e15b1af7e0
2 changed files with 40 additions and 11 deletions

44
xtest.c
View file

@ -24,7 +24,7 @@ struct xtest_assert_info assertion_info;
#define XTEST_INDENT 2 #define XTEST_INDENT 2
#endif #endif
const char * group_nesting[XTEST_MAX_GROUP_NESTING]; const char *group_nesting[XTEST_MAX_GROUP_NESTING];
int group_nesting_pos = 0; int group_nesting_pos = 0;
jmp_buf xtest_jmp; jmp_buf xtest_jmp;
@ -38,8 +38,6 @@ const char *skip_reason;
int xtest_indent = 0; int xtest_indent = 0;
int expecting_assertion = 0; int expecting_assertion = 0;
int num_tests = 0; int num_tests = 0;
int successful_tests = 0; int successful_tests = 0;
@ -55,7 +53,7 @@ int xtest_complete() {
return failed_tests != 0 ? 1 : 0; return failed_tests != 0 ? 1 : 0;
} }
void subunit_message(const char * name, const char * message, const char * details) { void subunit_message(const char *name, const char *message, const char *details) {
printf("%s: %s", message, name); printf("%s: %s", message, name);
if (details == NULL) { if (details == NULL) {
printf("\n"); printf("\n");
@ -212,12 +210,12 @@ void xtest_internal_assert(const char *file, int line, const char *func, const c
#define PRINT_ASSERT_MESSAGE(fmt, type) snprintf(assert_message, XTEST_ASSERT_MESSAGE_MAX_LEN, FAIL_ASSERT_FORMAT(fmt), \ #define PRINT_ASSERT_MESSAGE(fmt, type) snprintf(assert_message, XTEST_ASSERT_MESSAGE_MAX_LEN, FAIL_ASSERT_FORMAT(fmt), \
expression, invert ? "not " : "", CAST_PTR(type, expected), CAST_PTR(type, actual), file, line) expression, invert ? "not " : "", CAST_PTR(type, expected), CAST_PTR(type, actual), file, line)
void xtest_fail_assert(const char *expression, const char *file, int line, void *expected, void xtest_fail_assert(const char *expression, const char *file, int line, void *expected,
void *actual, int invert, enum xtest_type type) { void *actual, int invert, enum xtest_type type) {
switch (type) { switch (type) {
case xtest_type_bool: case xtest_type_bool:
snprintf(assert_message, XTEST_ASSERT_MESSAGE_MAX_LEN, FAIL_ASSERT_FORMAT("%s"), expression, invert ? "not " : "", snprintf(assert_message, XTEST_ASSERT_MESSAGE_MAX_LEN, FAIL_ASSERT_FORMAT("%s"), expression,
invert ? "not " : "",
CAST_PTR(_Bool, expected) ? "true" : "false", CAST_PTR(_Bool, actual) ? "true" : "false", file, CAST_PTR(_Bool, expected) ? "true" : "false", CAST_PTR(_Bool, actual) ? "true" : "false", file,
line); line);
break; break;
@ -265,7 +263,8 @@ void xtest_fail_assert(const char *expression, const char *file, int line, void
break; break;
case xtest_type_other: case xtest_type_other:
default: default:
snprintf(assert_message, XTEST_ASSERT_MESSAGE_MAX_LEN, FAIL_ASSERT_FORMAT("%s"), expression, invert ? "not " : "", snprintf(assert_message, XTEST_ASSERT_MESSAGE_MAX_LEN, FAIL_ASSERT_FORMAT("%s"), expression,
invert ? "not " : "",
"<value of unknown type>", "<value of unknown type>", file, line); "<value of unknown type>", "<value of unknown type>", file, line);
break; break;
} }
@ -283,11 +282,38 @@ xtest_assert_float(double expected, double actual, int precision, int invert, co
int equals = diff < epsilon; int equals = diff < epsilon;
int failed = invert == equals; // invert = true: fail if equals, invert = false: fail if not equals int failed = invert == equals; // invert = true: fail if equals, invert = false: fail if not equals
if (!failed) return; if (!failed) return;
snprintf(assert_message, 1024, "assertion failed: %s, expected %s%g but got %g (difference %g, precision %d) (%s:%d)", snprintf(assert_message, 1024,
"assertion failed: %s, expected %s%g but got %g (difference %g, precision %d) (%s:%d)",
expression, invert ? "not " : "", expected, actual, diff, precision, file, line); expression, invert ? "not " : "", expected, actual, diff, precision, file, line);
longjmp(xtest_jmp, 1); longjmp(xtest_jmp, 1);
} }
size_t find_diff_offset(const char *a, const char *b, size_t length) {
size_t offset = 0;
while (offset < length && *(a + offset) == *(b + offset)) offset++;
return offset;
}
void xtest_assert_mem(const char *expected, const char *actual, size_t length, int invert, const char *expression,
const char *file,
int line) {
size_t offset = find_diff_offset(expected, actual, length);
int equals = offset == length;
int failed = invert == equals; // invert = true: fail if equals, invert = false: fail if not equals
if (!failed) return;
if (invert) {
snprintf(assert_message, 1024,
"assertion failed: %s, expected memory to not match but did (%s:%d)",
expression, file, line);
} else {
snprintf(assert_message, 1024,
"assertion failed: %s, expected memory to match but did not (first different byte at offset %zu, expected 0x%2x but got 0x%2x) (%s:%d)",
expression, offset, (unsigned int) expected[offset], (unsigned int) actual[offset], file, line);
}
longjmp(xtest_jmp, 1);
}
void xtest_skip(const char *reason) { void xtest_skip(const char *reason) {
skip_reason = reason; skip_reason = reason;
@ -311,4 +337,4 @@ void xtest_internal_end_group() {
void xtest_expect_assertion_failure() { void xtest_expect_assertion_failure() {
expecting_assertion = 1; expecting_assertion = 1;
} }

View file

@ -49,6 +49,9 @@ void
xtest_assert_float(double expected, double actual, int precision, int invert, const char *expression, const char *file, xtest_assert_float(double expected, double actual, int precision, int invert, const char *expression, const char *file,
int line); int line);
void xtest_assert_mem(const char *expected, const char *actual, size_t length, int invert, const char *expression, const char *file,
int line);
void xtest_internal_run(xtest_test_fn fn, const char *name, xtest_param *params, xtest_setup_fn setup, void xtest_internal_run(xtest_test_fn fn, const char *name, xtest_param *params, xtest_setup_fn setup,
xtest_teardown_fn teardown); xtest_teardown_fn teardown);
@ -121,8 +124,8 @@ int xtest_complete();
#define xtest_assert_float_is_not(actual, expected, precision) xtest_assert_float(expected, actual, precision, 1, #actual " is not " #expected, __FILE__, __LINE__) #define xtest_assert_float_is_not(actual, expected, precision) xtest_assert_float(expected, actual, precision, 1, #actual " is not " #expected, __FILE__, __LINE__)
#define xtest_assert_str_is(actual, expected) xtest_assert_(strcmp(actual, expected) == 0, #actual " is " #expected, (const char*)(actual), (const char*)(expected), 0) #define xtest_assert_str_is(actual, expected) xtest_assert_(strcmp(actual, expected) == 0, #actual " is " #expected, (const char*)(actual), (const char*)(expected), 0)
#define xtest_assert_str_is_not(actual, expected) xtest_assert_(strcmp(actual, expected) != 0, #actual " is not " #expected, (const char*)(actual), (const char*)(expected), 1) #define xtest_assert_str_is_not(actual, expected) xtest_assert_(strcmp(actual, expected) != 0, #actual " is not " #expected, (const char*)(actual), (const char*)(expected), 1)
#define xtest_assert_mem_is(actual, expected, len) xtest_assert_(memcmp(actual, expected, len) == 0, #actual " is " #expected " (" #len " bytes)", NULL, NULL, 0) #define xtest_assert_mem_is(actual, expected, len) xtest_assert_mem(expected, actual, len, 0, #actual " is " #expected " (" #len " bytes)", __FILE__, __LINE__)
#define xtest_assert_mem_is_not(actual, expected, len) xtest_assert_(memcmp(actual, expected, len) != 0, #actual " is not " #expected " (" #len " bytes)", NULL, NULL, 1) #define xtest_assert_mem_is_not(actual, expected, len) xtest_assert_mem(expected, actual, len, 1, #actual " is not " #expected " (" #len " bytes)", __FILE__, __LINE__)
#define XTEST_RUN_MAIN(fn) int main(int argc, char ** argv) { \ #define XTEST_RUN_MAIN(fn) int main(int argc, char ** argv) { \