@@ -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
new file mode 100644
@@ -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;
+}
@@ -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 {
new file mode 100644
@@ -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;
+}
@@ -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"
@@ -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
new file mode 100644
@@ -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"