A couple of recent bugs have made it very clear that a test suite for
gcov function summaries is necessary.  This is about as simple as I
could make it; simply look for a summary header in the test file,
record it in a dict, and match it against the function summaries in
gcov stdout.  I figured that if we do need fancier matching of
expected output it's better to work backwards from actual needs than
trying to anticipate it now.

Function summaries is an old feature, but it hasn't had a test before
now. A few recent bugs of mine have shown that some test support would
be nice:

commit a566f9efe1652b294e424e21e9910de2a011f4f1
    Fix function summaries with suppressed blocks

commit 9c09713871ad6d05e1bd2bd65cb3c78c823cb42e
    Compare right field when printing summary

--

Bootstrapped and tested on x86_64-linux-gnu. While I considered pushing
as obvious, as it only adds tests and in a way cannot introduce a
regression, a review is quite appropriate, particularly when the size is
taken into consideration.

--

gcc/testsuite/ChangeLog:

        * lib/gcov.exp: Add function summary test function.
        * gcc.misc-tests/gcov-43.c: New test.
---
 gcc/testsuite/gcc.misc-tests/gcov-43.c |  99 +++++++++++++++++
 gcc/testsuite/lib/gcov.exp             | 141 ++++++++++++++++++++++++-
 2 files changed, 238 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-43.c

diff --git a/gcc/testsuite/gcc.misc-tests/gcov-43.c 
b/gcc/testsuite/gcc.misc-tests/gcov-43.c
new file mode 100644
index 00000000000..0497aea0ed3
--- /dev/null
+++ b/gcc/testsuite/gcc.misc-tests/gcov-43.c
@@ -0,0 +1,99 @@
+/* { dg-options "-fcondition-coverage -fpath-coverage --coverage" } */
+/* { dg-do run } */
+
+int do_something (int x) { return x; }
+
+/*
+  Summaries for the same function, with and without suppression.
+
+  Function 'fn1'
+  Lines executed:88.89% of 9
+  Branches executed:100.00% of 4
+  Taken at least once:50.00% of 4
+  Calls executed:50.00% of 2
+  Condition outcomes covered:50.00% of 4
+  Prime paths covered:33.33% of 3
+
+  Function 'fn2'
+  Lines executed:83.33% of 6 (3 of 9 suppressed)
+  Branches executed:100.00% of 2 (2 of 4 suppressed)
+  Taken at least once:50.00% of 2
+  Calls executed:0.00% of 1 (1 of 2 suppressed)
+  Condition outcomes covered:50.00% of 4
+  Prime paths covered:0.00% of 1 (2 of 3 suppressed)
+*/
+
+int
+fn1 (int argc)
+{
+  int b = argc + 1;
+
+  int c;
+
+  int a = argc;
+
+  if (a)
+    if (b)
+      {
+      c = do_something (4);
+      }
+    else
+      c = do_something (1024);
+
+  int d = a + c - 1;
+}
+
+int
+fn2 (int argc)
+{
+#pragma GCC suppress_coverage begin
+  int b = argc + 1;
+#pragma GCC suppress_coverage end
+
+#pragma GCC suppress_coverage begin
+  int c;
+#pragma GCC suppress_coverage end
+
+  int a = argc;
+
+  if (a)
+    if (b)
+      {
+#pragma GCC suppress_coverage begin
+      c = do_something (4);
+#pragma GCC suppress_coverage end
+      }
+    else
+      c = do_something (1024);
+
+#pragma GCC suppress_coverage begin
+  int d = a + c - 1;
+#pragma GCC suppress_coverage end
+}
+
+/*
+  This function has neither conditions nor branches, which means quite
+  different summary output.
+
+  Function 'no_branches'
+  Lines executed:100.00% of 3
+  No branches
+  No calls
+  No conditions
+  Prime paths covered:100.00% of 1
+*/
+int
+no_branches (int a)
+{
+  int b = a * 2;
+  return b;
+}
+
+int main ()
+{
+    fn1 (1);
+    fn2 (2);
+    no_branches (3);
+}
+
+/* { dg-final { run-gcov function-summaries "-fge gcov-43.c" } } */
diff --git a/gcc/testsuite/lib/gcov.exp b/gcc/testsuite/lib/gcov.exp
index 94a734817b1..6d6c6abb9b5 100644
--- a/gcc/testsuite/lib/gcov.exp
+++ b/gcc/testsuite/lib/gcov.exp
@@ -660,6 +660,134 @@ proc verify-prime-paths { testname testcase file } {
     return $failed
 }
 
+# Verify function summaries (--function-summaries/-f).  The function
+# summaries are printed to stdout, so we take it (or rather, the
+# [lindex result 1] from remote_exec in addition to the .gcov file.
+# We scan the gcov file for a comment with the expected rows of the
+# function summaries output, which should look like this:
+#
+#  Function 'fn1'
+#  Lines executed:88.89% of 9
+#  Branches executed:100.00% of 4
+#  Taken at least once:50.00% of 4
+#  Calls executed:50.00% of 2
+#  Condition outcomes covered:50.00% of 4
+#  Prime paths covered:33.33% of 3
+#
+# The order of fields is important and should match what gcov prints.
+proc verify-function-summaries { testname testcase file stdout } {
+    set failed 0
+    set fd [open $file r]
+    set expected [dict create]
+    set funcname ""
+
+    while { [gets $fd line] >= 0 } {
+       if {[regexp "Function '(.+)'" $line all fname]} {
+           set funcname $fname
+       }
+       if {[regexp "(Lines executed:.*)" $line _ ln]} {
+           dict set expected $funcname "lines" $ln
+       }
+       if {[regexp "(Branches executed:.*)" $line _ ln]} {
+           dict set expected $funcname "branches" $ln
+       }
+       if {[regexp "(No branches)" $line _ ln]} {
+           dict set expected $funcname "branches" $ln
+       }
+       if {[regexp "(Taken at least once:.*)" $line _ ln]} {
+           dict set expected $funcname "taken" $ln
+       }
+       if {[regexp "(Calls executed:.*)" $line _ ln]} {
+           dict set expected $funcname "calls" $ln
+       }
+       if {[regexp "(No calls)" $line _ ln]} {
+           dict set expected $funcname "calls" $ln
+       }
+       if {[regexp "(Condition outcomes covered:.*)" $line _ ln]} {
+           dict set expected $funcname "conditions" $ln
+       }
+       if {[regexp "(No conditions)" $line _ ln]} {
+           dict set expected $funcname "conditions" $ln
+       }
+       if {[regexp "(Prime paths covered:.*)" $line _ ln]} {
+           dict set expected $funcname "paths" $ln
+       }
+    }
+
+    set records [split $stdout "\n"]
+    for {set i 0} {$i < [llength $records]} {incr i} {
+       set line [string trimright [lindex $records $i]]
+
+       if {[regexp "Function '(.+)'" $line _ fname]} {
+           # Skip if we haven't recorded expected summaries for this function
+           if {![dict exists $expected $fname]} {
+               continue
+           }
+
+           set rows [dict get $expected $fname]
+           dict with rows {
+               if {[info exists lines]} {
+                   incr i
+                   set line [string trimright [lindex $records $i]]
+                   if {![string equal $lines $line]} {
+                       incr failed
+                       fail "$fname: Expected '$lines',  was '$line'"
+                   }
+               }
+               if {[info exists branches]} {
+                   incr i
+                   set line [string trimright [lindex $records $i]]
+                   if {![string equal $branches $line]} {
+                       incr failed
+                       fail "$fname: Expected '$branches',  was '$line'"
+                   }
+               }
+               if {[info exists taken]} {
+                   incr i
+                   set line [string trimright [lindex $records $i]]
+                   if {![string equal $taken $line]} {
+                       incr failed
+                       fail "$fname: Expected '$taken',  was '$line'"
+                   }
+               }
+               if {[info exists calls]} {
+                   incr i
+                   set line [string trimright [lindex $records $i]]
+                   if {![string equal $calls $line]} {
+                       incr failed
+                       fail "$fname: Expected '$calls',  was '$line'"
+                   }
+               }
+               if {[info exists conditions]} {
+                   incr i
+                   set line [string trimright [lindex $records $i]]
+                   if {![string equal $conditions $line]} {
+                       incr failed
+                       fail "$fname: Expected '$conditions',  was '$line'"
+                   }
+               }
+               if {[info exists paths]} {
+                   incr i
+                   set line [string trimright [lindex $records $i]]
+                   if {![string equal $paths $line]} {
+                       incr failed
+                       fail "$fname: Expected '$paths',  was '$line'"
+                   }
+               }
+           }
+           dict unset expected $fname
+       }
+    }
+
+    if {[dict size $expected] > 0} {
+       incr failed
+       fail "Did not see summary for all functions, missing [dict keys 
$expected]"
+    }
+
+    close $fd
+    return $failed
+}
+
 proc gcov-pytest-format-line { args } {
     global subdir
 
@@ -741,6 +869,7 @@ proc run-gcov { args } {
     set gcov_verify_lines 1
     set gcov_verify_intermediate 0
     set gcov_verify_filters 0
+    set gcov_verify_function_summaries 0
     set gcov_remove_gcda 0
     set xfailed 0
 
@@ -757,6 +886,8 @@ proc run-gcov { args } {
          set verify_filters_unexpected [lindex $a 2]
        } elseif { $a == "prime-paths" } {
          set gcov_verify_prime_paths 1
+       } elseif { $a == "function-summaries" } {
+           set gcov_verify_function_summaries 1
        } elseif { $a == "intermediate" } {
          set gcov_verify_intermediate 1
          set gcov_verify_calls 0
@@ -764,6 +895,7 @@ proc run-gcov { args } {
          set gcov_verify_conditions 0
          set gcov_verify_lines 0
          set gcov_verify_filters 0
+         set gcov_verify_function_summaries 0
        } elseif { $a == "remove-gcda" } {
          set gcov_remove_gcda 1
        } elseif { $gcov_args == "" } {
@@ -866,15 +998,20 @@ proc run-gcov { args } {
     } else {
        set ffailed 0
     }
+    if { $gcov_verify_function_summaries } {
+       set fsfailed [verify-function-summaries $testname $testcase 
$testcase.gcov [lindex $result 1]]
+    } else {
+       set fsfailed 0
+    }
 
     # Report whether the gcov test passed or failed.  If there were
     # multiple failures then the message is a summary.
-    set tfailed [expr $lfailed + $bfailed + $cdfailed + $ppfailed + $cfailed + 
$ifailed + $ffailed]
+    set tfailed [expr $lfailed + $bfailed + $cdfailed + $ppfailed + $cfailed + 
$ifailed + $ffailed + $fsfailed]
     if { $xfailed } {
        setup_xfail "*-*-*"
     }
     if { $tfailed > 0 } {
-       fail "$testname gcov: $lfailed failures in line counts, $bfailed in 
branch percentages, $cdfailed in condition/decision, $ppfailed in prime-paths, 
$cfailed in return percentages, $ifailed in intermediate format, $ffailed 
failed in filters"
+       fail "$testname gcov: $lfailed failures in line counts, $bfailed in 
branch percentages, $cdfailed in condition/decision, $ppfailed in prime-paths, 
$cfailed in return percentages, $ifailed in intermediate format, $ffailed 
failed in filters, $fsfailed in summaries"
        if { $xfailed } {
            clean-gcov $testcase
        }
-- 
2.47.3

Reply via email to