Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add memory location comparison macros (ck_assert_mem_*) #64

Merged
merged 3 commits into from
Nov 3, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions doc/check.texi
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,20 @@ used for comparison is different for each function and is indicated by
the last two letters of the function name. The abbreviations @code{eq} and
@code{ne} correspond to @code{==} and @code{!=} respectively.

@item ck_assert_mem_eq
@itemx ck_assert_mem_ne
@itemx ck_assert_mem_lt
@itemx ck_assert_mem_le
@itemx ck_assert_mem_gt
@itemx ck_assert_mem_ge

Compares contents of two memory locations of the given length, using the
@code{memcmp()} function internally, and displays predefined message
with condition and input parameter values on failure. The comparison
operator is again indicated by last two letters of the function name.
@code{ck_assert_mem_lt(a, b)} will pass if the unsigned numerical value
of memory location @code{a} is less than that of @code{b}.

@item fail
(Deprecated) Unconditionally fails test with user supplied message.

Expand Down
113 changes: 113 additions & 0 deletions src/check.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,119 @@ CK_DLL_EXP void CK_EXPORT _ck_assert_failed(const char *file, int line,
*/
#define ck_assert_str_ge(X, Y) _ck_assert_str(X, >=, Y)

/* Memory location comparison macros with improved output compared to ck_assert() */
/* OP might be any operator that can be used in '0 OP memcmp(X,Y,L)' comparison */
/* The x and y parameter swap in memcmp() is needed to handle >, >=, <, <= operators */
/* Output is limited to CK_MAX_ASSERT_MEM_PRINT_SIZE bytes */
#ifndef CK_MAX_ASSERT_MEM_PRINT_SIZE
#define CK_MAX_ASSERT_MEM_PRINT_SIZE 64
#endif

#define _ck_assert_mem(X, OP, Y, L) do { \
const uint8_t* _ck_x = (const uint8_t*)(X); \
const uint8_t* _ck_y = (const uint8_t*)(Y); \
size_t _ck_l = (L); \
char _ck_x_str[CK_MAX_ASSERT_MEM_PRINT_SIZE * 2 + 1]; \
char _ck_y_str[CK_MAX_ASSERT_MEM_PRINT_SIZE * 2 + 1]; \
static const char _ck_hexdigits[] = "0123456789abcdef"; \
size_t _ck_i; \
size_t _ck_maxl = (_ck_l > CK_MAX_ASSERT_MEM_PRINT_SIZE) ? CK_MAX_ASSERT_MEM_PRINT_SIZE : _ck_l; \
for (_ck_i = 0; _ck_i < _ck_maxl; _ck_i++) { \
_ck_x_str[_ck_i * 2 ] = _ck_hexdigits[(_ck_x[_ck_i] >> 4) & 0xF]; \
_ck_y_str[_ck_i * 2 ] = _ck_hexdigits[(_ck_y[_ck_i] >> 4) & 0xF]; \
_ck_x_str[_ck_i * 2 + 1] = _ck_hexdigits[_ck_x[_ck_i] & 0xF]; \
_ck_y_str[_ck_i * 2 + 1] = _ck_hexdigits[_ck_y[_ck_i] & 0xF]; \
} \
_ck_x_str[_ck_i * 2] = 0; \
_ck_y_str[_ck_i * 2] = 0; \
if (_ck_maxl != _ck_l) { \
_ck_x_str[_ck_i * 2 - 2] = '.'; \
_ck_y_str[_ck_i * 2 - 2] = '.'; \
_ck_x_str[_ck_i * 2 - 1] = '.'; \
_ck_y_str[_ck_i * 2 - 1] = '.'; \
} \
ck_assert_msg(0 OP memcmp(_ck_y, _ck_x, _ck_l), \
"Assertion '%s' failed: %s == \"%s\", %s == \"%s\"", #X" "#OP" "#Y, #X, _ck_x_str, #Y, _ck_y_str); \
} while (0)
/**
* Check two memory locations to determine if 0==memcmp(X,Y,L)
*
* If not 0==memcmp(X,Y,L), the test fails.
*
* @param X memory location
* @param Y memory location to compare against X
*
* @note If the check fails, the remaining of the test is aborted
*
* @since 0.10.1
*/
#define ck_assert_mem_eq(X, Y, L) _ck_assert_mem(X, ==, Y, L)
/**
* Check two memory locations to determine if 0!=memcmp(X,Y,L)
*
* If not 0!=memcmp(X,Y,L), the test fails.
*
* @param X memory location
* @param Y memory location to compare against X
*
* @note If the check fails, the remaining of the test is aborted
*
* @since 0.10.1
*/
#define ck_assert_mem_ne(X, Y, L) _ck_assert_mem(X, !=, Y, L)
/**
* Check two memory locations to determine if 0<memcmp(X,Y,L), (e.g. memcmp(X,Y,L)>0)
*
* If not 0<memcmp(X,Y,L), the test fails.
*
* @param X memory location
* @param Y memory location to compare against X
*
* @note If the check fails, the remaining of the test is aborted
*
* @since 0.10.1
*/
#define ck_assert_mem_lt(X, Y, L) _ck_assert_mem(X, <, Y, L)
/**
* Check two memory locations to determine if 0<=memcmp(X,Y,L) (e.g. memcmp(X,Y,L)>=0)
*
* If not 0<=memcmp(X,Y,L), the test fails.
*
* @param X memory location
* @param Y memory location to compare against X
*
* @note If the check fails, the remaining of the test is aborted
*
* @since 0.10.1
*/
#define ck_assert_mem_le(X, Y, L) _ck_assert_mem(X, <=, Y, L)
/**
* Check two memory locations to determine if 0<memcmp(X,Y,L) (e.g. memcmp(X,Y,L)>0)
*
* If not 0<memcmp(X,Y,L), the test fails.
*
* @param X memory location
* @param Y memory location to compare against X
*
* @note If the check fails, the remaining of the test is aborted
*
* @since 0.10.1
*/
#define ck_assert_mem_gt(X, Y, L) _ck_assert_mem(X, >, Y, L)
/**
* Check two memory locations to determine if 0>=memcmp(X,Y,L) (e.g. memcmp(X,Y,L)<=0)
*
* If not 0>=memcmp(X,Y,L), the test fails.
*
* @param X memory location
* @param Y memory location to compare against X
*
* @note If the check fails, the remaining of the test is aborted
*
* @since 0.10.1
*/
#define ck_assert_mem_ge(X, Y, L) _ck_assert_mem(X, >=, Y, L)

/* Pointer comparison macros with improved output compared to ck_assert(). */
/* OP may only be == or != */
#define _ck_assert_ptr(X, OP, Y) do { \
Expand Down
9 changes: 9 additions & 0 deletions tests/check_check_master.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,15 @@ static master_test_t master_tests[] = {
{ "Simple Tests", CK_PASS, "Passed" },
{ "Simple Tests", CK_FAILURE, "Assertion 'x == y' failed: x == 0x1, y == 0x2" },
{ "Simple Tests", CK_FAILURE, "Assertion 'x != z' failed: x == 0x1, z == 0x1" },
{ "Simple Tests", CK_FAILURE, "Assertion '\"\\x00\\x00\\x00\\x00\\x01\" == s' failed: \"\\x00\\x00\\x00\\x00\\x01\" == \"0000000001\", s == \"0000000002\"" },
{ "Simple Tests", CK_FAILURE, "Assertion 't != s' failed: t == \"0000000002\", s == \"0000000002\"" },
{ "Simple Tests", CK_FAILURE, "Assertion 's < s' failed: s == \"0000000001\", s == \"0000000001\"" },
{ "Simple Tests", CK_FAILURE, "Assertion 't <= s' failed: t == \"0000000002\", s == \"0000000001\"" },
{ "Simple Tests", CK_FAILURE, "Assertion 't > t' failed: t == \"0000000002\", t == \"0000000002\"" },
{ "Simple Tests", CK_FAILURE, "Assertion 's >= t' failed: s == \"0000000001\", t == \"0000000002\"" },
{ "Simple Tests", CK_PASS, "Passed" },
{ "Simple Tests", CK_FAILURE, "Assertion 't == s' failed: t == \"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001\", s == \"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002\"" },
{ "Simple Tests", CK_FAILURE, "Assertion 't == s' failed: t == \"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000..\", s == \"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000..\"" },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As there would be a difference between one check against CK_MAX_ASSERT_MEM_PRINT_SIZE characters and one with CK_MAX_ASSERT_MEM_PRINT_SIZE+1 characters, can there be a test using exactly CK_MAX_ASSERT_MEM_PRINT_SIZE characters to show that there is no .. at the end?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.


#if defined(HAVE_FORK) && HAVE_FORK==1
{ "Signal Tests", CK_ERROR, signal_11_str },
Expand Down
97 changes: 97 additions & 0 deletions tests/check_check_sub.c
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,94 @@ START_TEST(test_ck_assert_ptr_ne)
}
END_TEST

START_TEST(test_ck_assert_mem_eq)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because there is a limit on the number of characters printed in the assert message, there will need to be a test that check the boundary conditions of the message. Namely, a test comparing 0 bytes (should never produce a message), a test where all the bytes are printed (covered already), a test which is the max number of bytes allowed, and a test with too many bytes. Can you add tests for these cases?

{
const char *s = "\x00\x00\x00\x00\x02";
ck_assert_mem_eq("\x00\x00\x00\x00\x02", s, 5);
record_failure_line_num(__LINE__);
ck_assert_mem_eq("\x00\x00\x00\x00\x01", s, 5);
}
END_TEST

START_TEST(test_ck_assert_mem_ne)
{
const char *s = "\x00\x00\x00\x00\x02";
const char *t = "\x00\x00\x00\x00\x01";
ck_assert_mem_ne(t, s, 5);
t = "\x00\x00\x00\x00\x02";
record_failure_line_num(__LINE__);
ck_assert_mem_ne(t, s, 5);
}
END_TEST

START_TEST(test_ck_assert_mem_lt)
{
const char *s = "\x00\x00\x00\x00\x01";
const char *t = "\x00\x00\x00\x00\x02";
ck_assert_mem_lt(s, t, 5);
record_failure_line_num(__LINE__);
ck_assert_mem_lt(s, s, 5);
}
END_TEST

START_TEST(test_ck_assert_mem_le)
{
const char *s = "\x00\x00\x00\x00\x01";
const char *t = "\x00\x00\x00\x00\x02";
ck_assert_mem_le(s, t, 5);
ck_assert_mem_le(s, s, 5);
record_failure_line_num(__LINE__);
ck_assert_mem_le(t, s, 5);
}
END_TEST

START_TEST(test_ck_assert_mem_gt)
{
const char *s = "\x00\x00\x00\x00\x01";
const char *t = "\x00\x00\x00\x00\x02";
ck_assert_mem_gt(t, s, 5);
record_failure_line_num(__LINE__);
ck_assert_mem_gt(t, t, 5);
}
END_TEST

START_TEST(test_ck_assert_mem_ge)
{
const char *s = "\x00\x00\x00\x00\x01";
const char *t = "\x00\x00\x00\x00\x02";
ck_assert_mem_ge(t, s, 5);
ck_assert_mem_ge(t, t, 5);
record_failure_line_num(__LINE__);
ck_assert_mem_ge(s, t, 5);
}
END_TEST

START_TEST(test_ck_assert_mem_zerolen)
{
const char *s = "\x00\x00\x00\x00\x02";
const char *t = "\x00\x00\x00\x00\x01";
ck_assert_mem_eq(t, s, 0);
}
END_TEST

START_TEST(test_ck_assert_mem_eq_exact)
{
const char *s = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02";
const char *t = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01";
record_failure_line_num(__LINE__);
ck_assert_mem_eq(t, s, 64);
}
END_TEST

START_TEST(test_ck_assert_mem_eq_longer)
{
const char *s = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02";
const char *t = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To easily check that the two arrays are both at least 65 characters long, might it be easier to:

const char s[65] = {0};
const char t[65] = {1};

Copy link
Contributor Author

@prusnak prusnak Oct 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but I need a different character at the end of the array, not at the beginning.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is fine, it is OK as is.

record_failure_line_num(__LINE__);
ck_assert_mem_eq(t, s, 65);
}
END_TEST

#if defined(HAVE_FORK) && HAVE_FORK == 1
START_TEST(test_segv_pass)
{
Expand Down Expand Up @@ -1095,6 +1183,15 @@ Suite *make_sub_suite(void)
tcase_add_test (tc_simple, test_ck_assert_str_expr);
tcase_add_test (tc_simple, test_ck_assert_ptr_eq);
tcase_add_test (tc_simple, test_ck_assert_ptr_ne);
tcase_add_test (tc_simple, test_ck_assert_mem_eq);
tcase_add_test (tc_simple, test_ck_assert_mem_ne);
tcase_add_test (tc_simple, test_ck_assert_mem_lt);
tcase_add_test (tc_simple, test_ck_assert_mem_le);
tcase_add_test (tc_simple, test_ck_assert_mem_gt);
tcase_add_test (tc_simple, test_ck_assert_mem_ge);
tcase_add_test (tc_simple, test_ck_assert_mem_zerolen);
tcase_add_test (tc_simple, test_ck_assert_mem_eq_exact);
tcase_add_test (tc_simple, test_ck_assert_mem_eq_longer);

#if defined(HAVE_FORK) && HAVE_FORK==1
tcase_add_test (tc_signal, test_segv);
Expand Down