[libvirt] Switch to using lcov for test coverage reports

Daniel P. Berrange berrange at redhat.com
Thu May 22 21:30:14 UTC 2008


I got fed up trying to figure out how to fix the coverage reports to merge
data from multiple runs correctly, so this patch rips out all our test
coverage support. In its place is a makefile rule which just calls out to
the lcov program.  It requires lcov >= 1.6  since earlier versions were
too dumb to deal with .libs/ directories properly.  This version has just
been submitted to Fedora updates repos.

 Makefile.am                        |   26 +---
 a/scripts/.cvsignore               |    2 
 a/scripts/Makefile.am              |    4 
 a/scripts/README                   |    3 
 a/scripts/coverage-report-entry.pl |   77 -------------
 a/scripts/coverage-report.pl       |  167 ----------------------------
 a/scripts/coverage-report.xsl      |  217 -------------------------------------
 configure.in                       |    2 
 src/Makefile.am                    |   20 ---
 9 files changed, 12 insertions(+), 506 deletions(-)


Dan.

diff -r ddd05d94cc2d Makefile.am
--- a/Makefile.am	Thu May 22 17:13:05 2008 -0400
+++ b/Makefile.am	Thu May 22 17:27:30 2008 -0400
@@ -1,7 +1,10 @@
 ## Process this file with automake to produce Makefile.in
 
+LCOV = lcov
+GENHTML = genhtml
+
 SUBDIRS = gnulib/lib include src qemud proxy docs gnulib/tests \
-  python tests po scripts
+  python tests po
 
 ACLOCAL_AMFLAGS = -I m4 -I gnulib/m4
 
@@ -28,24 +31,15 @@
 	@(if [ "$(pythondir)" != "" ] ; then cd python ; \
 	  $(MAKE) MAKEFLAGS+=--silent tests ; fi)
 
-cov: cov-recursive cov-am
+cov: clean-cov
+	mkdir $(top_builddir)/coverage
+	$(LCOV) -c -o $(top_builddir)/coverage/libvirt.info.tmp -d $(top_srcdir)/src  -d $(top_srcdir)/qemud -d $(top_srcdir)/tests
+	$(LCOV) -r $(top_builddir)/coverage/libvirt.info.tmp -o $(top_builddir)/coverage/libvirt.info *usr*
+	rm $(top_builddir)/coverage/libvirt.info.tmp
+	$(GENHTML) -s -t "libvirt" -o $(top_builddir)/coverage --legend $(top_builddir)/coverage/libvirt.info
 
 clean-cov:
 	rm -rf $(top_builddir)/coverage
-	cd src && $(MAKE) $(AM_MAKEFLAGS) clean-cov
-
-cov-recursive:
-	cd src && $(MAKE) $(AM_MAKEFLAGS) cov
-
-cov-am:
-	rm -rf $(top_builddir)/coverage
-	mkdir $(top_builddir)/coverage
-	perl $(srcdir)/scripts/coverage-report.pl src/*.cov > $(top_builddir)/coverage/index.xml
-	xsltproc $(srcdir)/scripts/coverage-report.xsl \
-	  $(top_builddir)/coverage/index.xml \
-	  > $(top_builddir)/coverage/index.html
-	for i in $(top_builddir)/src/*.gcov ; do o=`echo $$i | sed -e 's,$(top_builddir)/src,coverage,'` ; \
-	  perl $(srcdir)/scripts/coverage-report-entry.pl $$i > $$o.html ; done
 
 # disable this check
 distuninstallcheck:
diff -r ddd05d94cc2d configure.in
--- a/configure.in	Thu May 22 17:13:05 2008 -0400
+++ b/configure.in	Thu May 22 17:27:30 2008 -0400
@@ -1001,7 +1001,7 @@
 	  gnulib/lib/Makefile \
 	  gnulib/tests/Makefile \
           libvirt.pc libvirt.spec \
-          po/Makefile.in scripts/Makefile \
+          po/Makefile.in \
 	  include/libvirt/Makefile include/libvirt/libvirt.h \
 	  python/Makefile python/tests/Makefile \
           qemud/Makefile \
diff -r ddd05d94cc2d scripts/.cvsignore
--- a/scripts/.cvsignore	Thu May 22 17:13:05 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-Makefile
-Makefile.in
diff -r ddd05d94cc2d scripts/Makefile.am
--- a/scripts/Makefile.am	Thu May 22 17:13:05 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-
-EXTRA_DIST = coverage-report.pl \
-             coverage-report-entry.pl \
-             coverage-report.xsl
diff -r ddd05d94cc2d scripts/README
--- a/scripts/README	Thu May 22 17:13:05 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-This directory provides a collection of tools used in the
-build / test process. They are not installed / used after
-deployment.
diff -r ddd05d94cc2d scripts/coverage-report-entry.pl
--- a/scripts/coverage-report-entry.pl	Thu May 22 17:13:05 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-#!/usr/bin/perl
-#
-# Copyright (C) 2006-2007 Daniel P. Berrange
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
-#
-# Author: Daniel P. Berrange <berrange at redhat.com>
-#
-# coverage-report-entry.pl: convert gcov annotated source into HTML
-#
-# This script takes a gcov annotated source code files on STDIN
-# converts it to HTML, coloured according to coverage, and sends
-# it to STDOUT
-
-print <<EOF;
-<html>
-<head>
-<title>Coverage report for $ARGV[0]</title>
-<style type="text/css">
-          span.perfect {
-            background: rgb(0,255,0);
-          }
-          span.terrible {
-            background: rgb(255,0,0);
-          }
-</style>
-</head>
-<body>
-<h1>Coverage report for $ARGV[0]</h1>
-
-<pre>
-EOF
-
-
-while (<>) {
-    s/&/&/g;
-    s/</</g;
-    s/>/>/g;
-
-    if (/^\s*function (\S+) called (\d+) returned \d+% blocks executed \d+%/) {
-	my $class = $2 > 0 ? "perfect" : "terrible";
-	$_ = "<span class=\"$class\" id=\"" . $1 . "\">$_</span>";
-    } elsif (/^\s*branch\s+\d+\s+taken\s+(\d+)%\s+.*$/) {
-	my $class = $1 > 0 ? "perfect" : "terrible";
-	$_ = "<span class=\"$class\">$_</span>";
-    } elsif (/^\s*branch\s+\d+\s+never executed.*$/) {
-	my $class = "terrible";
-	$_ = "<span class=\"$class\">$_</span>";
-    } elsif (/^\s*call\s+\d+\s+never executed.*$/) {
-	my $class = "terrible";
-	$_ = "<span class=\"$class\">$_</span>";
-    } elsif (/^\s*call\s+\d+\s+returned\s+(\d+)%.*$/) {
-	my $class = $1 > 0 ? "perfect" : "terrible";
-	$_ = "<span class=\"$class\">$_</span>";
-    }
-
-
-    print;
-}
-
-print <<EOF;
-</pre>
-</body>
-</html>
-EOF
diff -r ddd05d94cc2d scripts/coverage-report.pl
--- a/scripts/coverage-report.pl	Thu May 22 17:13:05 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,167 +0,0 @@
-#!/usr/bin/perl
-#
-# Copyright (C) 2006-2007 Daniel P. Berrange
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
-#
-# Author: Daniel P. Berrange <berrange at redhat.com>
-#
-# coverage-report.pl: generate XML coverage summary report
-#
-# This script takes a listof gcov .cov files as args, and generates
-# an XML document summarizing the coverage per function and per
-# source file.
-
-use warnings;
-use strict;
-
-my %coverage = ( function => {}, file => {} );
-
-my @functionBlackList = (
-			 "__memcpy",
-			 "__memmove",
-			 "__memset",
-			 "__strcat",
-			 "__strcpy",
-			 "__strncpy",
-			 "__strsep",
-			 "__strtok"
-		      );
-
-my %filemap;
-
-my $type;
-my $name;
-
-my @functions;
-
-while (<>) {
-    if (/^Function '(.*)'\s*$/) {
-	$type = "function";
-	$name = $1;
-	$coverage{$type}->{$name} = {};
-	push @functions, $name;
-    } elsif (/^File '(.*?)'\s*$/) {
-	$type = "file";
-	$name = $1;
-	$coverage{$type}->{$name} = {};
-
-	foreach my $func (@functions) {
-	    $coverage{"function"}->{$func}->{file} = $name;
-	}
-	@functions = ();
-    } elsif (/^Lines executed:(.*)%\s*of\s*(\d+)\s*$/) {
-	$coverage{$type}->{$name}->{lines} = $2;
-	$coverage{$type}->{$name}->{linesCoverage} = $1;
-    } elsif (/^No executable lines\s*$/) {
-	$coverage{$type}->{$name}->{lines} = 0;
-	$coverage{$type}->{$name}->{linesCoverage} = "100.00";
-    } elsif (/^Branches executed:(.*)%\s*of\s*(\d+)\s*$/) {
-	$coverage{$type}->{$name}->{branches} = $2;
-	$coverage{$type}->{$name}->{branchesCoverage} = $1;
-    } elsif (/^Taken at least once:(.*)%\s*of\s*(\d+)\s*$/) {
-	$coverage{$type}->{$name}->{conds} = $2;
-	$coverage{$type}->{$name}->{condsCoverage} = $1;
-    } elsif (/^Calls executed:(.*)%\s*of\s*(\d+)\s*$/) {
-	$coverage{$type}->{$name}->{calls} = $2;
-	$coverage{$type}->{$name}->{callsCoverage} = $1;
-    } elsif (/^No branches$/) {
-	$coverage{$type}->{$name}->{branches} = 0;
-	$coverage{$type}->{$name}->{branchesCoverage} = "100.00";
-	$coverage{$type}->{$name}->{conds} = 0;
-	$coverage{$type}->{$name}->{condsCoverage} = "100.00";
-    } elsif (/^No calls$/) {
-	$coverage{$type}->{$name}->{calls} = 0;
-	$coverage{$type}->{$name}->{callsCoverage} = "100.00";
-    } elsif (/^\s*(.*):creating '(.*)'\s*$/) {
-	$filemap{$1} = $2;
-    } elsif (/^\s*$/) {
-	# nada
-    } else {
-	warn "unexpected input [$_]\n";
-    }
-}
-
-my %summary;
-foreach my $type ("function", "file") {
-    $summary{$type} = {};
-    foreach my $m ("lines", "branches", "conds", "calls") {
-	my $totalGot = 0;
-	my $totalMiss = 0;
-	my $count = 0;
-	foreach my $func (keys %{$coverage{function}}) {
-	    my $blacklisted = 0;
-	    foreach my $blackName (@functionBlackList) {
-		$blacklisted = 1 if $func =~ /^$blackName/;
-	    }
-	    next if $blacklisted;
-
-	    $count++;
-	    my $got = $coverage{function}->{$func}->{$m};
-	    $totalGot += $got;
-	    my $miss = $got * $coverage{function}->{$func}->{$m ."Coverage"} / 100;
-	    $totalMiss += $miss;
-	}
-	$summary{$type}->{$m} = sprintf("%d", $totalGot);
-	if ($totalGot == 0) {
-	    $summary{$type}->{$m . "Coverage"} = "100.00";
-	} else {
-	    $summary{$type}->{$m . "Coverage"} = sprintf("%.2f", $totalMiss / $totalGot * 100);
-	}
-    }
-}
-
-
-
-print "<coverage>\n";
-
-foreach my $type ("function", "file") {
-    printf "<%ss>\n", $type;
-    foreach my $name (sort { $a cmp $b } keys %{$coverage{$type}}) {
-	if ($type eq "file") {
-	    next if $name =~ m,^/usr,;
-	} else {
-	    my $blacklisted = 0;
-	    foreach my $blackName (@functionBlackList) {
-		$blacklisted = 1 if $name =~ /^$blackName/;
-	    }
-	    next if $blacklisted;
-	}
-
-	my $rec = $coverage{$type}->{$name};
-	printf "  <entry name=\"%s\" details=\"%s\">\n", $name, ($type eq "file" ? $filemap{$name} : $filemap{$rec->{file}});
-	printf "    <lines count=\"%s\" coverage=\"%s\"/>\n", $rec->{lines}, $rec->{linesCoverage};
-	if (exists $rec->{branches}) {
-	    printf "    <branches count=\"%s\" coverage=\"%s\"/>\n", $rec->{branches}, $rec->{branchesCoverage};
-	}
-	if (exists $rec->{conds}) {
-	    printf "    <conditions count=\"%s\" coverage=\"%s\"/>\n", $rec->{conds}, $rec->{condsCoverage};
-	}
-	if (exists $rec->{calls}) {
-	    printf "    <calls count=\"%s\" coverage=\"%s\"/>\n", $rec->{calls}, $rec->{callsCoverage};
-	}
-	print  "  </entry>\n";
-    }
-
-    printf "  <summary>\n";
-    printf "    <lines count=\"%s\" coverage=\"%s\"/>\n", $summary{$type}->{lines}, $summary{$type}->{linesCoverage};
-    printf "    <branches count=\"%s\" coverage=\"%s\"/>\n", $summary{$type}->{branches}, $summary{$type}->{branchesCoverage};
-    printf "    <conditions count=\"%s\" coverage=\"%s\"/>\n", $summary{$type}->{conds}, $summary{$type}->{condsCoverage};
-    printf "    <calls count=\"%s\" coverage=\"%s\"/>\n", $summary{$type}->{calls}, $summary{$type}->{callsCoverage};
-    printf  "  </summary>\n";
-    printf "</%ss>\n", $type;
-}
-
-print "</coverage>\n";
diff -r ddd05d94cc2d scripts/coverage-report.xsl
--- a/scripts/coverage-report.xsl	Thu May 22 17:13:05 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,217 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
-                version="1.0">
-
-  <xsl:output method="html"/>
-
-  <xsl:template match="coverage">
-    <html>
-      <head>
-        <title>Coverage report</title>
-        <style type="text/css">
-          tbody tr.odd td.label {
-            border-top: 1px solid rgb(128,128,128);
-            border-bottom: 1px solid rgb(128,128,128);
-          }
-          tbody tr.odd td.label {
-            background: rgb(200,200,200);
-          }
-          
-          thead, tfoot {
-            background: rgb(60,60,60);
-            color: white;
-            font-weight: bold;
-          }
-
-          tr td.perfect {
-            background: rgb(0,255,0);
-            color: black;
-          }
-          tr td.excellant {
-            background: rgb(140,255,140);
-            color: black;
-          }
-          tr td.good {
-            background: rgb(160,255,0);
-            color: black;
-          }
-          tr td.poor {
-            background: rgb(255,160,0);
-            color: black;
-          }
-          tr td.bad {
-            background: rgb(255,140,140);
-            color: black;
-          }
-          tr td.terrible {
-            background: rgb(255,0,0);
-            color: black;
-          }
-        </style>
-      </head>
-      <body>
-        <h1>Coverage report</h1>
-        <xsl:apply-templates/>
-      </body>
-    </html>
-  </xsl:template>
-
-  <xsl:template match="functions">
-    <h2>Function coverage</h2>
-    <xsl:call-template name="content">
-      <xsl:with-param name="type" select="'function'"/>
-    </xsl:call-template>
-  </xsl:template>
-  
-
-  <xsl:template match="files">
-    <h2>File coverage</h2>
-    <xsl:call-template name="content">
-      <xsl:with-param name="type" select="'file'"/>
-    </xsl:call-template>
-  </xsl:template>
-
-  <xsl:template name="content">
-    <xsl:param name="type"/>
-    <table>
-      <thead>
-        <tr>
-          <th>Name</th>
-          <th>Lines</th>
-          <th>Branches</th>
-          <th>Conditions</th>
-          <th>Calls</th>
-        </tr>
-      </thead>
-      <tbody>
-        <xsl:for-each select="entry">
-          <xsl:call-template name="entry">
-            <xsl:with-param name="type" select="$type"/>
-            <xsl:with-param name="class">
-              <xsl:choose>
-                <xsl:when test="position() mod 2">
-                  <xsl:text>odd</xsl:text>
-                </xsl:when>
-                <xsl:otherwise>
-                  <xsl:text>even</xsl:text>
-                </xsl:otherwise>
-              </xsl:choose>
-            </xsl:with-param>
-          </xsl:call-template>
-        </xsl:for-each>
-      </tbody>
-      <tfoot>
-        <xsl:for-each select="summary">
-          <xsl:call-template name="entry">
-            <xsl:with-param name="type" select="'summary'"/>
-            <xsl:with-param name="class">
-              <xsl:choose>
-                <xsl:when test="position() mod 2">
-                  <xsl:text>odd</xsl:text>
-                </xsl:when>
-                <xsl:otherwise>
-                  <xsl:text>even</xsl:text>
-                </xsl:otherwise>
-              </xsl:choose>
-            </xsl:with-param>
-          </xsl:call-template>
-        </xsl:for-each>
-      </tfoot>
-    </table>
-  </xsl:template>
-  
-  <xsl:template name="entry">
-    <xsl:param name="type"/>
-    <xsl:param name="class"/>
-    <tr class="{$class}">
-      <xsl:choose>
-        <xsl:when test="$type = 'function'">
-          <td class="label"><a href="{@details}.html#{@name}"><xsl:value-of select="@name"/></a></td>
-        </xsl:when>
-        <xsl:when test="$type = 'file'">
-          <td class="label"><a href="{@details}.html"><xsl:value-of select="@name"/></a></td>
-        </xsl:when>
-        <xsl:otherwise>
-          <td class="label">Summary</td>
-        </xsl:otherwise>
-      </xsl:choose>
-
-      <xsl:if test="count(lines)">
-        <xsl:apply-templates select="lines"/>
-      </xsl:if>
-      <xsl:if test="not(count(lines))">
-        <xsl:call-template name="missing"/>
-      </xsl:if>
-
-      <xsl:if test="count(branches)">
-        <xsl:apply-templates select="branches"/>
-      </xsl:if>
-      <xsl:if test="not(count(branches))">
-        <xsl:call-template name="missing"/>
-      </xsl:if>
-
-      <xsl:if test="count(conditions)">
-        <xsl:apply-templates select="conditions"/>
-      </xsl:if>
-      <xsl:if test="not(count(conditions))">
-        <xsl:call-template name="missing"/>
-      </xsl:if>
-
-      <xsl:if test="count(calls)">
-        <xsl:apply-templates select="calls"/>
-      </xsl:if>
-      <xsl:if test="not(count(calls))">
-        <xsl:call-template name="missing"/>
-      </xsl:if>
-
-    </tr>
-  </xsl:template>
-  
-  <xsl:template match="lines">
-    <xsl:call-template name="row"/>
-  </xsl:template>
-
-  <xsl:template match="branches">
-    <xsl:call-template name="row"/>
-  </xsl:template>
-
-  <xsl:template match="conditions">
-    <xsl:call-template name="row"/>
-  </xsl:template>
-
-  <xsl:template match="calls">
-    <xsl:call-template name="row"/>
-  </xsl:template>
-
-  <xsl:template name="missing">
-    <td></td>
-  </xsl:template>
-
-  <xsl:template name="row">
-    <xsl:variable name="quality">
-      <xsl:choose>
-        <xsl:when test="@coverage = 100">
-          <xsl:text>perfect</xsl:text>
-        </xsl:when>
-        <xsl:when test="@coverage >= 80.0">
-          <xsl:text>excellant</xsl:text>
-        </xsl:when>
-        <xsl:when test="@coverage >= 60.0">
-          <xsl:text>good</xsl:text>
-        </xsl:when>
-        <xsl:when test="@coverage >= 40.0">
-          <xsl:text>poor</xsl:text>
-        </xsl:when>
-        <xsl:when test="@coverage >= 20.0">
-          <xsl:text>bad</xsl:text>
-        </xsl:when>
-        <xsl:otherwise>
-          <xsl:text>terrible</xsl:text>
-        </xsl:otherwise>
-      </xsl:choose>
-    </xsl:variable>
-    
-    <td class="{$quality}"><xsl:value-of select="@coverage"/>% of <xsl:value-of select="@count"/></td>
-  </xsl:template>
-
-</xsl:stylesheet>
diff -r ddd05d94cc2d src/Makefile.am
--- a/src/Makefile.am	Thu May 22 17:13:05 2008 -0400
+++ b/src/Makefile.am	Thu May 22 17:27:30 2008 -0400
@@ -149,22 +149,4 @@
 EXTRA_DIST += parthelper.c
 endif
 
-cov: clean-cov
-	for i in $(CLIENT_SOURCES); do			        \
-	  case $$i in *.c) ;; *) continue;; esac;	        \
-	  b=$$(basename $$i .c);			        \
-	  o_files=;					        \
-	  for i in '' _test; do				        \
-	    g="$(LV_LIBTOOL_OBJDIR)/libvirt$${i}_la-$$b.gcda";	\
-	    o="$(LV_LIBTOOL_OBJDIR)/libvirt$${i}_la-$$b.o";	\
-	    test -f "$$o" -a -f "$$g"				\
-	      && o_files="$$o_files $$o";		        \
-	  done;						        \
-	  test -n "$$o_files"                                   \
-              && gcov -o $(LV_LIBTOOL_OBJDIR) -b -f $$o_files > $$b.cov;	\
-	done
-
-clean-cov:
-	rm -f *.cov *.gcov
-
-CLEANFILES = *.cov *.gcov .libs/*.gcda .libs/*.gcno *.gcno *.gcda
+CLEANFILES = *.gcov .libs/*.gcda .libs/*.gcno *.gcno *.gcda

-- 
|: Red Hat, Engineering, Boston   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|




More information about the libvir-list mailing list