From 4db48ef0003d360d617712ca53733984e6fbbfc3 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
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 <ggc_root_tab *> (
+		       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 <ggc_root_tab *> (
+		       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

