From a3c4a48ef7e9299b198ad403a9a78ae56adc144c Mon Sep 17 00:00:00 2001 From: Deomid Ryabkov Date: Sat, 10 Feb 2018 10:53:33 +0000 Subject: [PATCH] Make sure test assertions are evaluated once E.g. if `ASSERT_EQ(myfunc(), 123)` fails, `myfunc()` would be invoked again to print the actual value. This can cause confusion sometimes. To avoid this, we cast arguments to double, which is wide enough for most cases and we provide a variant for 64 bit value types for when it's not. We also perform a check and fail if argument to the check results in loss of precision. CL: none PUBLISHED_FROM=a14551289d92e8a5dead21d16471ebddebe938a8 --- src/common/test_util.h | 128 +++++++++++++++++++++++++++++------------ test/unit_test.c | 6 +- 2 files changed, 95 insertions(+), 39 deletions(-) diff --git a/src/common/test_util.h b/src/common/test_util.h index fbc0eae3..60618f10 100644 --- a/src/common/test_util.h +++ b/src/common/test_util.h @@ -77,51 +77,103 @@ void _strfail(const char *a, const char *e, int len); if (msg) return msg; \ } while (0) -/* VC6 doesn't know how to cast an unsigned 64-bit int to double */ +/* VC6 doesn't have long long. */ #if (defined(_MSC_VER) && _MSC_VER <= 1200) -#define AS_DOUBLE(d) (double)(int64_t)(d) +#define LONG_TYPE int64_t #else -#define AS_DOUBLE(d) (double)(d) +#define LONG_TYPE long long #endif +#define AS_DOUBLE(d) (double)(d) + +#define RANGE_CHECK(x) (fabs(x) > 4503599627370496.0 /* 2 << 51 */) /* - * Numeric equality assertion. Comparison is made in native types but for - * printing both are convetrted to double. + * Numeric equality assertion. + * Args are cast to double to ensure aruemnts are only evaluated once. + * This will work fine for all integer types except large 64-bit values. */ -#define ASSERT_EQ(actual, expected) \ - do { \ - g_num_checks++; \ - if (!((actual) == (expected))) { \ - printf("%f != %f\n", AS_DOUBLE(actual), AS_DOUBLE(expected)); \ - FAIL(#actual " == " #expected, __LINE__); \ - } \ +#define ASSERT_EQ(actual, expected) \ + do { \ + const double ad = AS_DOUBLE(actual); \ + const double ed = AS_DOUBLE(expected); \ + if (RANGE_CHECK(ad) || RANGE_CHECK(ed)) { \ + FAIL("loss of precision, use ASSERT_EQ64", __LINE__); \ + } \ + g_num_checks++; \ + if (!(ad == ed)) { \ + printf("%lf != %lf\n", ad, ed); \ + FAIL(#actual " != " #expected, __LINE__); \ + } \ + } while (0) + +#define ASSERT_EQ64(actual, expected) \ + do { \ + const LONG_TYPE a64 = (LONG_TYPE)(actual); \ + const LONG_TYPE e64 = (LONG_TYPE)(expected); \ + g_num_checks++; \ + if (!(a64 == e64)) { \ + printf("%lld != %lld\n", a64, e64); \ + FAIL(#actual " != " #expected, __LINE__); \ + } \ + } while (0) + +#define ASSERT_NE(actual, expected) \ + do { \ + const double ad = AS_DOUBLE(actual); \ + const double ed = AS_DOUBLE(expected); \ + g_num_checks++; \ + if (RANGE_CHECK(ad) || RANGE_CHECK(ed)) { \ + FAIL("loss of precision, use ASSERT_NE64", __LINE__); \ + } \ + if (!(ad != ed)) { \ + printf("%lf == %lf\n", ad, ed); \ + FAIL(#actual " == " #expected, __LINE__); \ + } \ + } while (0) + +#define ASSERT_NE64(actual, expected) \ + do { \ + const LONG_TYPE a64 = (LONG_TYPE)(actual); \ + const LONG_TYPE e64 = (LONG_TYPE)(expected); \ + g_num_checks++; \ + if (!(a64 != e64)) { \ + printf("%lld == %lld\n", a64, e64); \ + FAIL(#actual " == " #expected, __LINE__); \ + } \ } while (0) /* "Less than" assertion. */ -#define ASSERT_LT(a, b) \ - do { \ - g_num_checks++; \ - if (!((a) < (b))) { \ - printf("%f >= %f\n", AS_DOUBLE(a), AS_DOUBLE(b)); \ - FAIL(#a " < " #b, __LINE__); \ - } \ +#define ASSERT_LT(a, b) \ + do { \ + const double ad = AS_DOUBLE(a); \ + const double bd = AS_DOUBLE(b); \ + g_num_checks++; \ + if (RANGE_CHECK(ad) || RANGE_CHECK(bd)) { \ + FAIL("loss of precision in comparison", __LINE__); \ + } \ + if (!(ad < bd)) { \ + printf("%lf >= %lf\n", ad, bd); \ + FAIL(#a " < " #b, __LINE__); \ + } \ } while (0) /* "Greater than" assertion. */ -#define ASSERT_GT(a, b) \ - do { \ - g_num_checks++; \ - if (!((a) > (b))) { \ - printf("%f <= %f\n", AS_DOUBLE(a), AS_DOUBLE(b)); \ - FAIL(#a " > " #b, __LINE__); \ - } \ +#define ASSERT_GT(a, b) \ + do { \ + const double ad = AS_DOUBLE(a); \ + const double bd = AS_DOUBLE(b); \ + g_num_checks++; \ + if (!(ad > bd)) { \ + printf("%lf <= %lf\n", ad, bd); \ + FAIL(#a " > " #b, __LINE__); \ + } \ } while (0) /* Assert that actual == expected, where both are NUL-terminated. */ #define ASSERT_STREQ(actual, expected) \ do { \ g_num_checks++; \ - if (!_assert_streq(actual, expected)) { \ + if (!_assert_streq((actual), (expected))) { \ FAIL("ASSERT_STREQ(" #actual ", " #expected ")", __LINE__); \ } \ } while (0) @@ -129,21 +181,25 @@ void _strfail(const char *a, const char *e, int len); /* Assert that actual == expected, where both are pointers */ #define ASSERT_PTREQ(actual, expected) \ do { \ + const void *ap = (const void *) (actual); \ + const void *ep = (const void *) (expected); \ g_num_checks++; \ - if (actual != expected) { \ - printf("%p != %p\n", actual, expected); \ + if (!(ap == ep)) { \ + printf("%p != %p\n", ap, ep); \ FAIL("ASSERT_PTREQ(" #actual ", " #expected ")", __LINE__); \ } \ } while (0) /* Assert that actual != expected, where both are pointers */ -#define ASSERT_PTRNEQ(actual, expected) \ - do { \ - g_num_checks++; \ - if (actual == expected) { \ - printf("%p == %p\n", actual, expected); \ - FAIL("ASSERT_PTRNEQ(" #actual ", " #expected ")", __LINE__); \ - } \ +#define ASSERT_PTRNE(actual, expected) \ + do { \ + const void *ap = (const void *) (actual); \ + const void *ep = (const void *) (expected); \ + g_num_checks++; \ + if (!(ap != ep)) { \ + printf("%p == %p\n", ap, ep); \ + FAIL("ASSERT_PTRNE(" #actual ", " #expected ")", __LINE__); \ + } \ } while (0) /* Same as STREQ, but only expected is NUL-terminated. */ diff --git a/test/unit_test.c b/test/unit_test.c index ff080c58..d9cee8a3 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -1533,7 +1533,7 @@ static const char *test_parse_http_message(void) { ASSERT_EQ(mg_vcmp(&req.query_string, "a=b&c=d"), 0); ASSERT_EQ(mg_parse_http(f, strlen(f), &req, 1), (int) strlen(f)); - ASSERT_EQ(req.body.len, (size_t) ~0); + ASSERT_EQ64(req.body.len, (size_t) ~0); ASSERT_EQ(mg_parse_http(g, strlen(g), &req, 1), (int) strlen(g)); ASSERT_EQ(req.body.len, 0); @@ -1542,7 +1542,7 @@ static const char *test_parse_http_message(void) { ASSERT_EQ(mg_vcmp(&req.proto, "HTTP/1.0"), 0); ASSERT_EQ(req.resp_code, 200); ASSERT_EQ(mg_vcmp(&req.resp_status_msg, "OK"), 0); - ASSERT_EQ(req.body.len, (size_t) ~0); + ASSERT_EQ64(req.body.len, (size_t) ~0); ASSERT_EQ(mg_parse_http(i, strlen(i), &req, 0), -1); @@ -3921,7 +3921,7 @@ static const char *test_http_chunk2(void) { strcat(buf, "3\r\n...\r\na\r\n0123456789\r\n0\r"); ASSERT_EQ(mg_handle_chunked(&nc, &hm, buf, strlen(buf)), 13); ASSERT_STREQ(buf, "...01234567890\r"); - ASSERT_EQ(hm.message.len, (size_t) ~0); + ASSERT_EQ64(hm.message.len, (size_t) ~0); strcat(buf, "\n\r\n"); ASSERT_EQ(mg_handle_chunked(&nc, &hm, buf, strlen(buf)), 13);