From 09765e3a0a8be2f7bca694a1171ecf050f5400d1 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Mon, 13 Jun 2016 21:04:07 +0000 Subject: [PATCH] selftests: improve reported failure locations This patch introduce a selftest::location struct to wrap up __FILE__ and __LINE__ information (and __FUNCTION__) throughout the selftests, allowing location information to be passed around. It updates the helper functions in pretty-print.c to pass through the precise location of each test, so that if a failure occurs, the correct line number is printed, rather than a line within a helper function. gcc/ChangeLog: * input.c (test_reading_source_line): Use SELFTEST_LOCATION. * pretty-print.c (assert_pp_format_va): Add location param and use it with ASSERT_STREQ_AT. (assert_pp_format): Add location param and pass it to assert_pp_format_va. (assert_pp_format_colored): Likewise. (ASSERT_PP_FORMAT_1): New. (ASSERT_PP_FORMAT_2): New. (ASSERT_PP_FORMAT_3): New. (test_pp_format): Provide SELFTEST_LOCATION throughout, either explicitly, or implicitly via the above macros. * selftest.c (selftest::pass): Use a selftest::location rather than file and line. (selftest::fail): Likewise. Print the function name. (selftest::fail_formatted): Likewise. (selftest::assert_streq): Use a selftest::location rather than file and line. * selftest.h (selftest::location): New struct. (SELFTEST_LOCATION): New macro. (selftest::pass): Accept a const location & rather than file and line. (selftest::fail): Likewise. (selftest::fail_formatted): Likewise. (selftest::assert_streq): Likewise. (ASSERT_TRUE): Update for above changes, using SELFTEST_LOCATION. (ASSERT_FALSE): Likewise. (ASSERT_EQ): Likewise. (ASSERT_NE): Likewise. (ASSERT_STREQ): Likewise. (ASSERT_PRED1): Likewise. (ASSERT_STREQ_AT): New macro. From-SVN: r237410 --- gcc/ChangeLog | 34 ++++++++++++++ gcc/input.c | 4 +- gcc/pretty-print.c | 128 ++++++++++++++++++++++++++++++++--------------------- gcc/selftest.c | 20 ++++----- gcc/selftest.h | 60 ++++++++++++++++++------- 5 files changed, 168 insertions(+), 78 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index dc16da7..ccb5ccf 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,39 @@ 2016-06-13 David Malcolm + * input.c (test_reading_source_line): Use SELFTEST_LOCATION. + * pretty-print.c (assert_pp_format_va): Add location param and use + it with ASSERT_STREQ_AT. + (assert_pp_format): Add location param and pass it to + assert_pp_format_va. + (assert_pp_format_colored): Likewise. + (ASSERT_PP_FORMAT_1): New. + (ASSERT_PP_FORMAT_2): New. + (ASSERT_PP_FORMAT_3): New. + (test_pp_format): Provide SELFTEST_LOCATION throughout, either + explicitly, or implicitly via the above macros. + * selftest.c (selftest::pass): Use a selftest::location rather + than file and line. + (selftest::fail): Likewise. Print the function name. + (selftest::fail_formatted): Likewise. + (selftest::assert_streq): Use a selftest::location rather than + file and line. + * selftest.h (selftest::location): New struct. + (SELFTEST_LOCATION): New macro. + (selftest::pass): Accept a const location & rather than file + and line. + (selftest::fail): Likewise. + (selftest::fail_formatted): Likewise. + (selftest::assert_streq): Likewise. + (ASSERT_TRUE): Update for above changes, using SELFTEST_LOCATION. + (ASSERT_FALSE): Likewise. + (ASSERT_EQ): Likewise. + (ASSERT_NE): Likewise. + (ASSERT_STREQ): Likewise. + (ASSERT_PRED1): Likewise. + (ASSERT_STREQ_AT): New macro. + +2016-06-13 David Malcolm + * selftest.c (selftest::fail_formatted): New function. (selftest::assert_streq): New function. * selftest.h (selftests::fail_formatted): New decl. diff --git a/gcc/input.c b/gcc/input.c index e872cf0..1d004f8 100644 --- a/gcc/input.c +++ b/gcc/input.c @@ -1231,10 +1231,10 @@ test_reading_source_line () ASSERT_EQ (53, line_size); if (!strncmp (" The quick brown fox jumps over the lazy dog. */", source_line, line_size)) - ::selftest::pass (__FILE__, __LINE__, + ::selftest::pass (SELFTEST_LOCATION, "source_line matched expected value"); else - ::selftest::fail (__FILE__, __LINE__, + ::selftest::fail (SELFTEST_LOCATION, "source_line did not match expected value"); } diff --git a/gcc/pretty-print.c b/gcc/pretty-print.c index 8febda0..86ae3a5 100644 --- a/gcc/pretty-print.c +++ b/gcc/pretty-print.c @@ -1227,8 +1227,8 @@ test_basic_printing () prints EXPECTED, assuming that pp_show_color is SHOW_COLOR. */ static void -assert_pp_format_va (const char *expected, bool show_color, const char *fmt, - va_list *ap) +assert_pp_format_va (const location &loc, const char *expected, + bool show_color, const char *fmt, va_list *ap) { pretty_printer pp; text_info ti; @@ -1243,34 +1243,59 @@ assert_pp_format_va (const char *expected, bool show_color, const char *fmt, pp_show_color (&pp) = show_color; pp_format (&pp, &ti); pp_output_formatted_text (&pp); - ASSERT_STREQ (expected, pp_formatted_text (&pp)); + ASSERT_STREQ_AT (loc, expected, pp_formatted_text (&pp)); } /* Verify that pp_format (FMT, ...) followed by pp_output_formatted_text prints EXPECTED, with show_color disabled. */ static void -assert_pp_format (const char *expected, const char *fmt, ...) +assert_pp_format (const location &loc, const char *expected, + const char *fmt, ...) { va_list ap; va_start (ap, fmt); - assert_pp_format_va (expected, false, fmt, &ap); + assert_pp_format_va (loc, expected, false, fmt, &ap); va_end (ap); } /* As above, but with colorization enabled. */ static void -assert_pp_format_colored (const char *expected, const char *fmt, ...) +assert_pp_format_colored (const location &loc, const char *expected, + const char *fmt, ...) { va_list ap; va_start (ap, fmt); - assert_pp_format_va (expected, true, fmt, &ap); + assert_pp_format_va (loc, expected, true, fmt, &ap); va_end (ap); } +/* Helper function for calling testing pp_format, + by calling assert_pp_format with various numbers of arguments. + These exist mostly to avoid having to write SELFTEST_LOCATION + throughout test_pp_format. */ + +#define ASSERT_PP_FORMAT_1(EXPECTED, FMT, ARG1) \ + SELFTEST_BEGIN_STMT \ + assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \ + (ARG1)); \ + SELFTEST_END_STMT + +#define ASSERT_PP_FORMAT_2(EXPECTED, FMT, ARG1, ARG2) \ + SELFTEST_BEGIN_STMT \ + assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \ + (ARG1), (ARG2)); \ + SELFTEST_END_STMT + +#define ASSERT_PP_FORMAT_3(EXPECTED, FMT, ARG1, ARG2, ARG3) \ + SELFTEST_BEGIN_STMT \ + assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \ + (ARG1), (ARG2), (ARG3)); \ + SELFTEST_END_STMT + /* Verify that pp_format works, for various format codes. */ static void @@ -1284,68 +1309,71 @@ test_pp_format () close_quote = "'"; /* Verify that plain text is passed through unchanged. */ - assert_pp_format ("unformatted", "unformatted"); + assert_pp_format (SELFTEST_LOCATION, "unformatted", "unformatted"); /* Verify various individual format codes, in the order listed in the comment for pp_format above. For each code, we append a second argument with a known bit pattern (0x12345678), to ensure that we are consuming arguments correctly. */ - assert_pp_format ("-27 12345678", "%d %x", -27, 0x12345678); - assert_pp_format ("-5 12345678", "%i %x", -5, 0x12345678); - assert_pp_format ("10 12345678", "%u %x", 10, 0x12345678); - assert_pp_format ("17 12345678", "%o %x", 15, 0x12345678); - assert_pp_format ("cafebabe 12345678", "%x %x", 0xcafebabe, 0x12345678); - assert_pp_format ("-27 12345678", "%ld %x", (long)-27, 0x12345678); - assert_pp_format ("-5 12345678", "%li %x", (long)-5, 0x12345678); - assert_pp_format ("10 12345678", "%lu %x", (long)10, 0x12345678); - assert_pp_format ("17 12345678", "%lo %x", (long)15, 0x12345678); - assert_pp_format ("cafebabe 12345678", "%lx %x", (long)0xcafebabe, - 0x12345678); - assert_pp_format ("-27 12345678", "%lld %x", (long long)-27, 0x12345678); - assert_pp_format ("-5 12345678", "%lli %x", (long long)-5, 0x12345678); - assert_pp_format ("10 12345678", "%llu %x", (long long)10, 0x12345678); - assert_pp_format ("17 12345678", "%llo %x", (long long)15, 0x12345678); - assert_pp_format ("cafebabe 12345678", "%llx %x", (long long)0xcafebabe, - 0x12345678); - assert_pp_format ("-27 12345678", "%wd %x", (HOST_WIDE_INT)-27, 0x12345678); - assert_pp_format ("-5 12345678", "%wi %x", (HOST_WIDE_INT)-5, 0x12345678); - assert_pp_format ("10 12345678", "%wu %x", (unsigned HOST_WIDE_INT)10, - 0x12345678); - assert_pp_format ("17 12345678", "%wo %x", (HOST_WIDE_INT)15, 0x12345678); - assert_pp_format ("0xcafebabe 12345678", "%wx %x", (HOST_WIDE_INT)0xcafebabe, - 0x12345678); - assert_pp_format ("A 12345678", "%c %x", 'A', 0x12345678); - assert_pp_format ("hello world 12345678", "%s %x", "hello world", - 0x12345678); + ASSERT_PP_FORMAT_2 ("-27 12345678", "%d %x", -27, 0x12345678); + ASSERT_PP_FORMAT_2 ("-5 12345678", "%i %x", -5, 0x12345678); + ASSERT_PP_FORMAT_2 ("10 12345678", "%u %x", 10, 0x12345678); + ASSERT_PP_FORMAT_2 ("17 12345678", "%o %x", 15, 0x12345678); + ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%x %x", 0xcafebabe, 0x12345678); + ASSERT_PP_FORMAT_2 ("-27 12345678", "%ld %x", (long)-27, 0x12345678); + ASSERT_PP_FORMAT_2 ("-5 12345678", "%li %x", (long)-5, 0x12345678); + ASSERT_PP_FORMAT_2 ("10 12345678", "%lu %x", (long)10, 0x12345678); + ASSERT_PP_FORMAT_2 ("17 12345678", "%lo %x", (long)15, 0x12345678); + ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%lx %x", (long)0xcafebabe, + 0x12345678); + ASSERT_PP_FORMAT_2 ("-27 12345678", "%lld %x", (long long)-27, 0x12345678); + ASSERT_PP_FORMAT_2 ("-5 12345678", "%lli %x", (long long)-5, 0x12345678); + ASSERT_PP_FORMAT_2 ("10 12345678", "%llu %x", (long long)10, 0x12345678); + ASSERT_PP_FORMAT_2 ("17 12345678", "%llo %x", (long long)15, 0x12345678); + ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%llx %x", (long long)0xcafebabe, + 0x12345678); + ASSERT_PP_FORMAT_2 ("-27 12345678", "%wd %x", (HOST_WIDE_INT)-27, 0x12345678); + ASSERT_PP_FORMAT_2 ("-5 12345678", "%wi %x", (HOST_WIDE_INT)-5, 0x12345678); + ASSERT_PP_FORMAT_2 ("10 12345678", "%wu %x", (unsigned HOST_WIDE_INT)10, + 0x12345678); + ASSERT_PP_FORMAT_2 ("17 12345678", "%wo %x", (HOST_WIDE_INT)15, 0x12345678); + ASSERT_PP_FORMAT_2 ("0xcafebabe 12345678", "%wx %x", (HOST_WIDE_INT)0xcafebabe, + 0x12345678); + ASSERT_PP_FORMAT_2 ("A 12345678", "%c %x", 'A', 0x12345678); + ASSERT_PP_FORMAT_2 ("hello world 12345678", "%s %x", "hello world", + 0x12345678); /* We can't test for %p; the pointer is printed in an implementation-defined manner. */ - assert_pp_format ("normal colored normal 12345678", - "normal %rcolored%R normal %x", - "error", 0x12345678); + ASSERT_PP_FORMAT_2 ("normal colored normal 12345678", + "normal %rcolored%R normal %x", + "error", 0x12345678); /* The following assumes an empty value for GCC_COLORS. */ assert_pp_format_colored - ("normal \33[01;31m\33[Kcolored\33[m\33[K normal 12345678", + (SELFTEST_LOCATION, + "normal \33[01;31m\33[Kcolored\33[m\33[K normal 12345678", "normal %rcolored%R normal %x", "error", 0x12345678); /* TODO: %m: strerror(text->err_no) - does not consume a value from args_ptr. */ - assert_pp_format ("% 12345678", "%% %x", 0x12345678); - assert_pp_format ("` 12345678", "%< %x", 0x12345678); - assert_pp_format ("' 12345678", "%> %x", 0x12345678); - assert_pp_format ("' 12345678", "%' %x", 0x12345678); - assert_pp_format ("abc 12345678", "%.*s %x", 3, "abcdef", 0x12345678); - assert_pp_format ("abc 12345678", "%.3s %x", "abcdef", 0x12345678); + ASSERT_PP_FORMAT_1 ("% 12345678", "%% %x", 0x12345678); + ASSERT_PP_FORMAT_1 ("` 12345678", "%< %x", 0x12345678); + ASSERT_PP_FORMAT_1 ("' 12345678", "%> %x", 0x12345678); + ASSERT_PP_FORMAT_1 ("' 12345678", "%' %x", 0x12345678); + ASSERT_PP_FORMAT_3 ("abc 12345678", "%.*s %x", 3, "abcdef", 0x12345678); + ASSERT_PP_FORMAT_2 ("abc 12345678", "%.3s %x", "abcdef", 0x12345678); /* Verify flag 'q'. */ - assert_pp_format ("`foo' 12345678", "%qs %x", "foo", 0x12345678); - assert_pp_format_colored ("`\33[01m\33[Kfoo\33[m\33[K' 12345678", "%qs %x", + ASSERT_PP_FORMAT_2 ("`foo' 12345678", "%qs %x", "foo", 0x12345678); + assert_pp_format_colored (SELFTEST_LOCATION, + "`\33[01m\33[Kfoo\33[m\33[K' 12345678", "%qs %x", "foo", 0x12345678); /* Verify that combinations work, along with unformatted text. */ - assert_pp_format ("the quick brown fox jumps over the lazy dog", + assert_pp_format (SELFTEST_LOCATION, + "the quick brown fox jumps over the lazy dog", "the %s %s %s jumps over the %s %s", "quick", "brown", "fox", "lazy", "dog"); - assert_pp_format ("item 3 of 7", "item %i of %i", 3, 7); - assert_pp_format ("problem with `bar' at line 10", + assert_pp_format (SELFTEST_LOCATION, "item 3 of 7", "item %i of %i", 3, 7); + assert_pp_format (SELFTEST_LOCATION, "problem with `bar' at line 10", "problem with %qs at line %i", "bar", 10); /* Restore old values of open_quote and close_quote. */ diff --git a/gcc/selftest.c b/gcc/selftest.c index e5332db..ed6e517 100644 --- a/gcc/selftest.c +++ b/gcc/selftest.c @@ -29,7 +29,7 @@ int selftest::num_passes; /* Record the successful outcome of some aspect of a test. */ void -selftest::pass (const char */*file*/, int /*line*/, const char */*msg*/) +selftest::pass (const location &/*loc*/, const char */*msg*/) { num_passes++; } @@ -37,22 +37,22 @@ selftest::pass (const char */*file*/, int /*line*/, const char */*msg*/) /* Report the failed outcome of some aspect of a test and abort. */ void -selftest::fail (const char *file, int line, const char *msg) +selftest::fail (const location &loc, const char *msg) { - fprintf (stderr,"%s:%i: FAIL: %s\n", file, line, msg); - /* TODO: add calling function name as well? */ + fprintf (stderr,"%s:%i: %s: FAIL: %s\n", loc.m_file, loc.m_line, + loc.m_function, msg); abort (); } /* As "fail", but using printf-style formatted output. */ void -selftest::fail_formatted (const char *file, int line, const char *fmt, ...) +selftest::fail_formatted (const location &loc, const char *fmt, ...) { va_list ap; - fprintf (stderr, "%s:%i: FAIL: ", file, line); - /* TODO: add calling function name as well? */ + fprintf (stderr, "%s:%i: %s: FAIL: ", loc.m_file, loc.m_line, + loc.m_function); va_start (ap, fmt); vfprintf (stderr, fmt, ap); va_end (ap); @@ -63,15 +63,15 @@ selftest::fail_formatted (const char *file, int line, const char *fmt, ...) /* Implementation detail of ASSERT_STREQ. */ void -selftest::assert_streq (const char *file, int line, +selftest::assert_streq (const location &loc, const char *desc_expected, const char *desc_actual, const char *val_expected, const char *val_actual) { if (0 == strcmp (val_expected, val_actual)) - ::selftest::pass (file, line, "ASSERT_STREQ"); + ::selftest::pass (loc, "ASSERT_STREQ"); else ::selftest::fail_formatted - (file, line, "ASSERT_STREQ (%s, %s) expected=\"%s\" actual=\"%s\"", + (loc, "ASSERT_STREQ (%s, %s) expected=\"%s\" actual=\"%s\"", desc_expected, desc_actual, val_expected, val_actual); } diff --git a/gcc/selftest.h b/gcc/selftest.h index 6759734..e719f5f 100644 --- a/gcc/selftest.h +++ b/gcc/selftest.h @@ -27,26 +27,45 @@ along with GCC; see the file COPYING3. If not see namespace selftest { +/* A struct describing the source-location of a selftest, to make it + easier to track down failing tests. */ + +struct location +{ + location (const char *file, int line, const char *function) + : m_file (file), m_line (line), m_function (function) {} + + const char *m_file; + int m_line; + const char *m_function; +}; + +/* A macro for use in selftests and by the ASSERT_ macros below, + constructing a selftest::location for the current source location. */ + +#define SELFTEST_LOCATION \ + (::selftest::location (__FILE__, __LINE__, __FUNCTION__)) + /* The entrypoint for running all tests. */ extern void run_tests (); /* Record the successful outcome of some aspect of the test. */ -extern void pass (const char *file, int line, const char *msg); +extern void pass (const location &loc, const char *msg); /* Report the failed outcome of some aspect of the test and abort. */ -extern void fail (const char *file, int line, const char *msg); +extern void fail (const location &loc, const char *msg); /* As "fail", but using printf-style formatted output. */ -extern void fail_formatted (const char *file, int line, const char *fmt, ...) - ATTRIBUTE_PRINTF_3; +extern void fail_formatted (const location &loc, const char *fmt, ...) + ATTRIBUTE_PRINTF_2; /* Implementation detail of ASSERT_STREQ. */ -extern void assert_streq (const char *file, int line, +extern void assert_streq (const location &loc, const char *desc_expected, const char *desc_actual, const char *val_expected, const char *val_actual); @@ -85,9 +104,9 @@ extern int num_passes; const char *desc = "ASSERT_TRUE (" #EXPR ")"; \ bool actual = (EXPR); \ if (actual) \ - ::selftest::pass (__FILE__, __LINE__, desc); \ + ::selftest::pass (SELFTEST_LOCATION, desc); \ else \ - ::selftest::fail (__FILE__, __LINE__, desc); \ + ::selftest::fail (SELFTEST_LOCATION, desc); \ SELFTEST_END_STMT /* Evaluate EXPR and coerce to bool, calling @@ -99,9 +118,9 @@ extern int num_passes; const char *desc = "ASSERT_FALSE (" #EXPR ")"; \ bool actual = (EXPR); \ if (actual) \ - ::selftest::fail (__FILE__, __LINE__, desc); \ + ::selftest::fail (SELFTEST_LOCATION, desc); \ else \ - ::selftest::pass (__FILE__, __LINE__, desc); \ + ::selftest::pass (SELFTEST_LOCATION, desc); \ SELFTEST_END_STMT /* Evaluate EXPECTED and ACTUAL and compare them with ==, calling @@ -112,9 +131,9 @@ extern int num_passes; SELFTEST_BEGIN_STMT \ const char *desc = "ASSERT_EQ (" #EXPECTED ", " #ACTUAL ")"; \ if ((EXPECTED) == (ACTUAL)) \ - ::selftest::pass (__FILE__, __LINE__, desc); \ + ::selftest::pass (SELFTEST_LOCATION, desc); \ else \ - ::selftest::fail (__FILE__, __LINE__, desc); \ + ::selftest::fail (SELFTEST_LOCATION, desc); \ SELFTEST_END_STMT /* Evaluate EXPECTED and ACTUAL and compare them with !=, calling @@ -125,9 +144,9 @@ extern int num_passes; SELFTEST_BEGIN_STMT \ const char *desc = "ASSERT_NE (" #EXPECTED ", " #ACTUAL ")"; \ if ((EXPECTED) != (ACTUAL)) \ - ::selftest::pass (__FILE__, __LINE__, desc); \ + ::selftest::pass (SELFTEST_LOCATION, desc); \ else \ - ::selftest::fail (__FILE__, __LINE__, desc); \ + ::selftest::fail (SELFTEST_LOCATION, desc); \ SELFTEST_END_STMT /* Evaluate EXPECTED and ACTUAL and compare them with strcmp, calling @@ -136,7 +155,16 @@ extern int num_passes; #define ASSERT_STREQ(EXPECTED, ACTUAL) \ SELFTEST_BEGIN_STMT \ - ::selftest::assert_streq (__FILE__, __LINE__, #EXPECTED, #ACTUAL, \ + ::selftest::assert_streq (SELFTEST_LOCATION, #EXPECTED, #ACTUAL, \ + (EXPECTED), (ACTUAL)); \ + SELFTEST_END_STMT + +/* Like ASSERT_STREQ_AT, but treat LOC as the effective location of the + selftest. */ + +#define ASSERT_STREQ_AT(LOC, EXPECTED, ACTUAL) \ + SELFTEST_BEGIN_STMT \ + ::selftest::assert_streq ((LOC), #EXPECTED, #ACTUAL, \ (EXPECTED), (ACTUAL)); \ SELFTEST_END_STMT @@ -148,9 +176,9 @@ extern int num_passes; const char *desc = "ASSERT_PRED1 (" #PRED1 ", " #VAL1 ")"; \ bool actual = (PRED1) (VAL1); \ if (actual) \ - ::selftest::pass (__FILE__, __LINE__, desc); \ + ::selftest::pass (SELFTEST_LOCATION, desc); \ else \ - ::selftest::fail (__FILE__, __LINE__, desc); \ + ::selftest::fail (SELFTEST_LOCATION, desc); \ SELFTEST_END_STMT #define SELFTEST_BEGIN_STMT do { -- 2.7.4