-
Notifications
You must be signed in to change notification settings - Fork 211
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
Conversation
Jenkins: ok to test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally I like it. There needs to be a trade-off for how much is printed, as it cannot be boundless.
This change will need some unit tests in order to cover the new API. Can you also provide these unit tests along with your change?
/* The x and y parameter swap in memcmp() is needed to handle >, >=, <, <= operators */ | ||
/* Output is limited to 64 bytes (or 128 hex digits) */ | ||
#define _ck_assert_mem(X, OP, Y, L) do { \ | ||
const char* _ck_x = (const char*)(void*)(X); \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is the (void*) cast necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to be explicit, in case there is a platform where char!=8bits, should this be uint8_t?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to uint8_t and removed void* cast.
_ck_x_str[_ck_i * 2] = 0; \ | ||
_ck_y_str[_ck_i * 2] = 0; \ | ||
ck_assert_msg(0 OP memcmp(_ck_y, _ck_x, _ck_l), \ | ||
"Assertion '"#X#OP#Y"' failed: "#X"==\"%s\", "#Y"==\"%s\"", _ck_x_str, _ck_y_str); \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am torn between the message containing "#X#OP#Y" or something else which mentions the size of the strings. Should the message mention memcmp, as the documentation uses? For example, "0#OPmemcmp(#X,#Y,#L)"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I copied this from ck_assert_str usage, where strcmp is not mentioned in the output (but it is in the documentation). I would prefer not adding it to the output. However I am fine with adding length somewhere.
const char* _ck_x = (const char*)(void*)(X); \ | ||
const char* _ck_y = (const char*)(void*)(Y); \ | ||
size_t _ck_l = (L); \ | ||
char _ck_x_str[129]; \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can the magic numbers used in the macro, e.g. "129", "64", be defined? For example CK_MAX_ASSERT_MEM_PRINT_SIZE=64 which is then used everywhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additionally, perhaps define it as such:
#ifndef CK_MAX_ASSERT_MEM_PRINT_SIZE
#define CK_MAX_ASSERT_MEM_PRINT_SIZE
#endif
That way if a user wishes to override it they may. Another option it to make it a configure option
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea.
size_t _ck_l = (L); \ | ||
char _ck_x_str[129]; \ | ||
char _ck_y_str[129]; \ | ||
static char _ck_hexdigits[] = "0123456789abcdef"; \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this array be made const?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes!
char _ck_y_str[129]; \ | ||
static char _ck_hexdigits[] = "0123456789abcdef"; \ | ||
size_t _ck_i; \ | ||
for (_ck_i = 0; _ck_i < ((_ck_l > 64) ? 64 : _ck_l); _ck_i++) { \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can the computation for the string size be done before the loop, for readability?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
Pushed a new commit fixing the issues from above. |
Also I am adding the following for discussion. The way I usually use these macros is like this:
while the function
Do you think it makes sense to integrate it further in the check library, so we could have something like this?
It is much more convenient (especially for longer values, eg. 64 bytes) than using:
|
I can appreciate the use case you mention regarding the hex. What gives me pause is it would be another place where a maximum size would be used (for the static buffer). The first place using a max size, the strings which reports buffer contents on a failure, does not result in a problem if more data is used than the buffer size. In that case the differences in the two buffers will simply not be printed, but it will not affect the success or failure of the comparison. However, in the case of the hex based comparison call because it may not be obvious to a caller that the hex string provided has a maximum possible length, passing too many characters may affect the expected result. In addition, if an odd number of characters were passed the last character would be ignored. From projects where I've compared binary data typically the expected data is in a file, and before the comparison the file's contents are loaded into a buffer. However, those typically are for comparisons that are larger than one would want to type for a unit test. My opinion is to provide the ck_assert_mem_* calls for now, and if there is a way to quell the concerns listed above maybe a ck_assert_hex call can be added as a separate commit. |
Ok, let's focus on |
The change will need to have some unit tests accompany it. You can add some test cases under the test_ck_assert_ptr_ne test here: https://github.com/libcheck/check/blob/master/tests/check_check_sub.c#L555 You will also need to add the test cases around here: https://github.com/libcheck/check/blob/master/tests/check_check_sub.c#L1098 After that, you will need to determine the expected result of each of the test cases (there should be some tests that pass the check and some that fail) and put the expected result at the end of the pointer-based tests in the master_tests table: https://github.com/libcheck/check/blob/master/tests/check_check_master.c#L117 Just keep the order of adding the tests to the simple test case and the order in the master_tests table consistent to avoid issues. Look around check_check_sub.c for examples of other tests for the ck_assert* macros for inspiration. After adding those tests the change should be good to go. Let me know if you have any questions or run into something when adding the unit tests. |
Added unit tests as requested in f9676cf ... These are basically ck_assert_str_* tests, but using binary data instead of strings. |
@@ -553,6 +553,78 @@ START_TEST(test_ck_assert_ptr_ne) | |||
} | |||
END_TEST | |||
|
|||
START_TEST(test_ck_assert_mem_eq) |
There was a problem hiding this comment.
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?
It would appear that one of the unit tests is not passing. This example shows the following message in the test run output:
I believe that means there was no failure expected but a failure did occur. I am not sure the reason for this. If you would like to reproduce this locally, there is a way to disable the unit tests which take a long time to run. Give the --disable-timeout-tests argument to configure and then the unit tests should run fairly quickly. You could also execute the check_check unit test program directly and check for a non-zero return code. |
Ah, there was an error in Also added two new tests: |
} | ||
END_TEST | ||
|
||
START_TEST(test_ck_assert_mem_expr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does this test for which the other test do not already test for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
const char *t[] = { "\x00\x00\x00\x00\x01", "\x00\x00\x00\x00\x02" }; | ||
int i = -1; | ||
ck_assert_str_eq(s, t[++i]); | ||
ck_assert_str_eq(s, t[i]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are not both of these identical? Is not t[1] never used?
START_TEST(test_ck_assert_mem_eq_long) | ||
{ | ||
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"; |
There was a problem hiding this comment.
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};
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
{ "Simple Tests", CK_FAILURE, "Assertion 's >= t' failed: s == \"0000000001\", t == \"0000000002\"" }, | ||
{ "Simple Tests", CK_PASS, "Passed" }, | ||
{ "Simple Tests", CK_PASS, "Passed" }, | ||
{ "Simple Tests", CK_FAILURE, "Assertion 't == s' failed: t == \"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\", s == \"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"" }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is a question about the output of an assertion when the data is too long. In this example we see that both the first and second argument look the same though the data is different. When the data is too long and it is truncated should a "..." be added to the end of the two arguments, so it is obvious to the developer that the difference may not be in the portion of the string shown?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added in 8b1cd61
@@ -772,6 +772,12 @@ CK_DLL_EXP void CK_EXPORT _ck_assert_failed(const char *file, int line, | |||
} \ | |||
_ck_x_str[_ck_i * 2] = 0; \ | |||
_ck_y_str[_ck_i * 2] = 0; \ | |||
if (_ck_i != _ck_l) { \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it not be more direct to compare _ck_l against _ck_maxl? It may be more intuitive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK.
@@ -121,7 +121,7 @@ static master_test_t master_tests[] = { | |||
{ "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 == \"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\", s == \"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"" }, | |||
{ "Simple Tests", CK_FAILURE, "Assertion 't == s' failed: t == \"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000..\", s == \"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000..\"" }, |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added.
I think that this change is good to go, thanks for the contribution. Would you mind adding documentation for this change here, perhaps in a separate commit? Maybe add it after the description of ck_assert_ptr_eq and ck_assert_ptr_ne. I'll look into the unit test which is failing. It appears to be failing before this change, so I believe it to be unrelated. |
Added documentation in a separate commit. |
Thanks for the contribution! |
Added in #68 |
As discussed here: https://sourceforge.net/p/check/feature-requests/30/