From 4db48ef0003d360d617712ca53733984e6fbbfc3 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Wed, 10 Jun 2015 06:05:16 -0400 Subject: [PATCH 01/16] Add unittest infrastructure gcc/testsuite/ChangeLog: * g++.dg/plugin/plugin.exp (plugin_test_list): Add the unittests plugin. * g++.dg/plugin/unittests.C: New file. * gcc.dg/plugin/plugin.exp (plugin_test_list): Add the unittests plugin. * gcc.dg/plugin/unittests.c: New file. * lib/plugin-support.exp (dg-plugin-run-gengtype): New function. (plugin-get-options): Handle dg-plugin-run-gengtype. * lib/prune.exp (prune_gcc_output): Process any DejaGnu output from the test, and convert into results at this level, prefixing with the testcase. * unittests/unittests-plugin.c: New file. --- gcc/testsuite/g++.dg/plugin/plugin.exp | 4 +- gcc/testsuite/g++.dg/plugin/unittests.C | 9 ++ gcc/testsuite/gcc.dg/plugin/plugin.exp | 1 + gcc/testsuite/gcc.dg/plugin/unittests.c | 9 ++ gcc/testsuite/lib/plugin-support.exp | 29 +++- gcc/testsuite/lib/prune.exp | 43 ++++++ gcc/testsuite/unittests/unittests-plugin.c | 213 +++++++++++++++++++++++++++++ 7 files changed, 306 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/g++.dg/plugin/unittests.C create mode 100644 gcc/testsuite/gcc.dg/plugin/unittests.c create mode 100644 gcc/testsuite/unittests/unittests-plugin.c diff --git a/gcc/testsuite/g++.dg/plugin/plugin.exp b/gcc/testsuite/g++.dg/plugin/plugin.exp index 3ed1397..3e6e7f1 100644 --- a/gcc/testsuite/g++.dg/plugin/plugin.exp +++ b/gcc/testsuite/g++.dg/plugin/plugin.exp @@ -62,7 +62,9 @@ set plugin_test_list [list \ { dumb_plugin.c dumb-plugin-test-1.C } \ { header_plugin.c header-plugin-test.C } \ { decl_plugin.c decl-plugin-test.C } \ - { def_plugin.c def-plugin-test.C } ] + { def_plugin.c def-plugin-test.C } \ + { ../../unittests/unittests-plugin.c unittests.C } \ +] foreach plugin_test $plugin_test_list { # Replace each source file with its full-path name diff --git a/gcc/testsuite/g++.dg/plugin/unittests.C b/gcc/testsuite/g++.dg/plugin/unittests.C new file mode 100644 index 0000000..42b4e93 --- /dev/null +++ b/gcc/testsuite/g++.dg/plugin/unittests.C @@ -0,0 +1,9 @@ +/* Placeholder C++ source file for running the unittests plugin. */ +/* { dg-do compile } */ +/* { dg-options "-O" } */ + +int +main (int argc, char **argv) +{ + return 0; +} diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp index 39fab6e..5956fe3 100644 --- a/gcc/testsuite/gcc.dg/plugin/plugin.exp +++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp @@ -63,6 +63,7 @@ set plugin_test_list [list \ { start_unit_plugin.c start_unit-test-1.c } \ { finish_unit_plugin.c finish_unit-test-1.c } \ { wide-int_plugin.c wide-int-test-1.c } \ + { ../../unittests/unittests-plugin.c unittests.c } \ ] foreach plugin_test $plugin_test_list { diff --git a/gcc/testsuite/gcc.dg/plugin/unittests.c b/gcc/testsuite/gcc.dg/plugin/unittests.c new file mode 100644 index 0000000..610e91d --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/unittests.c @@ -0,0 +1,9 @@ +/* Placeholder C source file for running the unittests plugin. */ +/* { dg-do compile } */ +/* { dg-options "-O" } */ + +int +main (int argc, char **argv) +{ + return 0; +} diff --git a/gcc/testsuite/lib/plugin-support.exp b/gcc/testsuite/lib/plugin-support.exp index a9343cb..ffa8404 100644 --- a/gcc/testsuite/lib/plugin-support.exp +++ b/gcc/testsuite/lib/plugin-support.exp @@ -20,6 +20,32 @@ load_lib dg.exp load_lib gcc.exp +# Special-case directive for use by unittests/unittests-plugin.c +# for invoking gengtype on test-ggc.c, to generate a +# gt-unittests-test-ggc.h file. + +proc dg-plugin-run-gengtype { args } { + global srcdir objdir + verbose "dg-plugin-run-gengtype: args: $args" 3 + verbose "srcdir: $srcdir" 3 + verbose "objdir: $objdir" 3 + set gcc_objdir "$objdir/../.." + set gengtype_cmd \ + "$gcc_objdir/gengtype \ + --plugin gt-unittests-test-ggc.h \ + --read-state $gcc_objdir/gtype.state \ + $srcdir/unittests/test-ggc.c" + verbose "gengtype_cmd: $gengtype_cmd" 3 + + set status [remote_exec build $gengtype_cmd] + set status [lindex $status 0] + if { $status != 0 } then { + fail "gengtype" + } else { + pass "gengtype" + } +} + # # plugin-get-options -- process test directives # @@ -35,7 +61,8 @@ proc plugin-get-options { src } { set tmp [dg-get-options $src] foreach op $tmp { set cmd [lindex $op 0] - if { ![string compare "dg-options" $cmd] } { + if { ![string compare "dg-options" $cmd] + || ![string compare "dg-plugin-run-gengtype" $cmd] } { set status [catch "$op" errmsg] if { $status != 0 } { perror "src: $errmsg for \"$op\"\n" diff --git a/gcc/testsuite/lib/prune.exp b/gcc/testsuite/lib/prune.exp index fa10043..40a5a05 100644 --- a/gcc/testsuite/lib/prune.exp +++ b/gcc/testsuite/lib/prune.exp @@ -73,6 +73,49 @@ proc prune_gcc_output { text } { # Call into multiline.exp to handle any multiline output directives. set text [handle-multiline-outputs $text] + # Process any DejaGnu output from the test, and convert into results at + # this level, prefixing with the testcase. + # For example, given the line + # PASS: vec_test.quick_push + # from the test, we want to call "pass", giving a line like this in + # our .log/.sum, depending on [testname-for-summary]: + # PASS: gcc.dg/plugin/unittests.c -fplugin=./unittests-plugin.so vec_test.quick_push + verbose "Would handle: $text\n" + # Currently this only covers the possible outputs from + # unittests-plugin.c (PASS/FAIL/NOTE). + foreach status_pair { {"PASS" pass} {"FAIL" fail} {"NOTE" note} } { + set kind [lindex $status_pair 0] + set handler [lindex $status_pair 1] + #verbose "kind: $kind handler: $handler" + set pattern "^$kind: (.*?)$" + #verbose "pattern: $pattern" + set matches [regexp -all -inline -lineanchor $pattern $text] + #verbose "matches: $matches" + foreach {matched_line detail} $matches { + #verbose "matched_line: $matched_line" + #verbose "detail: $detail" + set testname [testname-for-summary] + + # Call the handler ("pass"/"fail" etc) to convert to a message + # at this level, prefixed with [testname-for-summary]: + $handler "$testname $detail" + + # Prune $matched_line from $text (or, at least, the first + # instance of it). + # Is there a less clunky way to do this? (regsub would require + # escaping any regex special characters within $matched_line). + # Locate first instance of matched_line: + set idx [string first $matched_line $text] + # Get length, adding one for the trailing newline char: + set linelen [expr [string length $matched_line] + 1] + # Cut it out from the middle of $text: + set textlen [string length $text] + set before [string range $text 0 [expr $idx - 1] ] + set after_ [string range $text [expr $idx + $linelen] $textlen] + set text $before$after_ + } + } + #send_user "After:$text\n" return $text diff --git a/gcc/testsuite/unittests/unittests-plugin.c b/gcc/testsuite/unittests/unittests-plugin.c new file mode 100644 index 0000000..e0fdf76 --- /dev/null +++ b/gcc/testsuite/unittests/unittests-plugin.c @@ -0,0 +1,213 @@ +/* Unittests plugin. */ + +/* The following options are needed by gtest. + + { dg-options "-lpthread -fPIC -Wno-missing-field-initializers -Wno-conversion-null -Wno-suggest-attribute=format -Wno-sign-compare" } */ + +/* Directive to invoke gengtype on test-ggc.c + (handled by plugin-support.exp). + + { dg-plugin-run-gengtype "" } */ + +#include "config.h" +#include "gtest/gtest.h" +#include "gcc-plugin.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "tm.h" +#include "toplev.h" +#include "hash-table.h" +#include "vec.h" +#include "ggc.h" +#include "basic-block.h" +#include "tree-ssa-alias.h" +#include "internal-fn.h" +#include "gimple-fold.h" +#include "tree-eh.h" +#include "gimple-expr.h" +#include "is-a.h" +#include "gimple.h" +#include "tree-pass.h" +#include "intl.h" +#include "context.h" +#include "diagnostic.h" +#include "bitmap.h" + +int plugin_is_GPL_compatible; + +/* Print test output in DejaGnu form. */ +class deja_gnu_printer : public ::testing::EmptyTestEventListener +{ + public: + deja_gnu_printer (FILE *outfile, int verbose) + : m_outfile (outfile), + m_verbose (verbose) + {} + + private: + virtual void + OnTestCaseStart(const ::testing::TestCase& test_case) + { + if (m_verbose) + fprintf (m_outfile, "NOTE: %s: case starting\n", + test_case.name ()); + } + + /* Vfunc called before a test starts. */ + virtual void + OnTestStart (const ::testing::TestInfo& test_info) + { + if (m_verbose) + fprintf (m_outfile, + "NOTE: %s.%s: test starting\n", + test_info.test_case_name (), test_info.name ()); + m_per_test_fails = 0; + } + + /* Vfunc called after a failed assertion or a SUCCEED() invocation. */ + virtual void + OnTestPartResult (const ::testing::TestPartResult& test_part_result) + { + fprintf (m_outfile, + "%s: %s:%d: %s\n", + test_part_result.failed () ? "FAIL" : "PASS", + test_part_result.file_name (), + test_part_result.line_number (), + test_part_result.summary ()); + if (test_part_result.failed ()) + m_per_test_fails++; + } + + /* Vfunc called after a test ends. */ + virtual void + OnTestEnd (const ::testing::TestInfo& test_info) + { + if (m_verbose) + fprintf (m_outfile, + "NOTE: %s.%s: test ending: %i failure(s)\n", + test_info.test_case_name (), test_info.name (), + m_per_test_fails); + fprintf (m_outfile, + "%s: %s.%s\n", + m_per_test_fails > 0 ? "FAIL" : "PASS", + test_info.test_case_name (), test_info.name ()); + } + + virtual void + OnTestCaseEnd (const ::testing::TestCase& test_case) + { + if (m_verbose) + fprintf (m_outfile, "NOTE: %s: case ending\n", + test_case.name ()); + } + + private: + FILE *m_outfile; + int m_verbose; + int m_per_test_fails; +}; + +/* Get rid of the default gtest result printer, and instead use LISTENER, + taking ownership of the ptr. */ + +static void +replace_default_gtest_result_printer (::testing::TestEventListener *listener) +{ + ::testing::TestEventListeners& listeners = + ::testing::UnitTest::GetInstance()->listeners(); + delete listeners.Release(listeners.default_result_printer()); + listeners.Append(listener); +} + +/* Callback handler for the PLUGIN_FINISH event. + At this point, all GCC subsystems should be initialized and + "warmed up"; this is where we run our unit tests. */ + +static void +on_finish (void */*gcc_data*/, void */*user_data*/) +{ + /* Initialize gtest with empty arguments. In theory we could locate + the arguments passed to the plugin and pass them on to gtest. */ + int argc = 0; + char **argv = NULL; + ::testing::InitGoogleTest (&argc, argv); + + /* Use our custom result-printer. */ + replace_default_gtest_result_printer (new deja_gnu_printer (stderr, 0)); + + /* Reset some state. */ + input_location = UNKNOWN_LOCATION; + bitmap_obstack_initialize (NULL); + + /* Run the tests. */ + int result = RUN_ALL_TESTS(); + + /* Ensure that a test failure leads to the process exiting with + a non-zero exit code. */ + if (result) + error ("at least one test failure occurred"); + + /* Cleanup. */ + bitmap_obstack_release (NULL); +} + +/* Defined in "gt-unittests-test-ggc.h", as generated by + dg-plugin-run-gengtype. */ +extern const struct ggc_root_tab gt_ggc_r_gt_unittests_test_ggc_h[]; +#if 0 +extern const struct ggc_root_tab gt_ggc_rd_gt_unittests_test_ggc_h[]; +#endif + +int +plugin_init (struct plugin_name_args *plugin_info, + struct plugin_gcc_version */*version*/) +{ + if (0) + fprintf (stderr, "got here\n"); + + register_callback (plugin_info->base_name, + PLUGIN_REGISTER_GGC_ROOTS, + NULL, + const_cast ( + gt_ggc_r_gt_unittests_test_ggc_h)); + + /* FIXME: we'd use this for test-ggc.c's test_of_deletable. + However we'd need to register it as a different kind of roottab; + doing it with PLUGIN_REGISTER_GGC_ROOTS leads to a segfault on + collection due to the NULL cb. + It looks like we need another hook; gt_ggc_deletable_rtab is + currently not expandable. */ +#if 0 + register_callback (plugin_info->base_name, + PLUGIN_REGISTER_GGC_ROOTS, + NULL, + const_cast ( + gt_ggc_rd_gt_unittests_test_ggc_h)); +#endif + register_callback (plugin_info->base_name, + PLUGIN_FINISH, + on_finish, + NULL); /* void *user_data */ + + return 0; +} + +/* Include gtest's source. */ +#include "gtest-all.c" + +/* Now include the test cases. */ +#include "test-bitmap.c" +#include "test-cfg.c" +#include "test-et-forest.c" +#include "test-folding.c" +#include "test-functions.c" +#include "test-ggc.c" +#include "test-gimple.c" +#include "test-hash-map.c" +#include "test-hash-set.c" +#include "test-locations.c" +#include "test-rtl.c" +#include "test-tree.c" +#include "test-vec.c" +#include "test-wide-int.c" -- 1.8.5.3