[Libvir] Adding test suite for sexpr<->xml conversions

Daniel P. Berrange berrange at redhat.com
Thu Aug 24 14:42:07 UTC 2006


On Thu, Aug 24, 2006 at 02:07:51PM +0100, Daniel P. Berrange wrote:
> Quite a critical bit of the libvirt code is that which converts between
> SEXPR and XML, and vica-verca. I've broken this code several times when
> making changes, so it is way overdue to get some unit test coverage in
> this area.

Having added some unit test coverage, I was wondering just how *much* 
coverage...  So I've attached another patch which adds a new flag to
configure - --enable-test-coverage, which causes everything to be
compiled & linked with -fprofile-arcs -ftest-coverage

There is then a 'make cov' target in the top level directory which 
genrates an HTML report showing code coverage:

http://people.redhat.com/berrange/libvirt-coverage/

Obviously it is mostly red - my goal is not to get 100% coverage - clearly
thats impossible in unit tests, since most of the XenD interaction can
only be tested through integration tests. It is however useful to be able
to view how much of the XML<->SEXPR routines are covered, since they're
easily unit testable. Also coverage on 'virsh' is useful since we can 
run it with the test backend instead of xen.

The reports are generated using Perl & xsltproc - yes, this adds Perl as
a dependancy, but I don't think its a worry, because this is totally a
no-op unless you explicitly pass --enable-test-coverage to configure,
so won't impact normal 'configure; make ; make install' cycle done by
most developers.

Regards,
Dan.
-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 
-------------- next part --------------
diff -ruN libvirt-new/configure.in libvirt/configure.in
--- libvirt-new/configure.in	2006-08-24 10:26:46.000000000 -0400
+++ libvirt/configure.in	2006-08-24 10:21:47.000000000 -0400
@@ -246,6 +246,34 @@
 AC_SUBST(PYTHON_INCLUDES)
 AC_SUBST(PYTHON_SITE_PACKAGES)
 
+AC_ARG_ENABLE(test-coverage,
+[  --enable-test-coverage   turn on code coverage instrumentation],
+[case "${enableval}" in
+   yes|no) ;;
+   *)      AC_MSG_ERROR([bad value ${enableval} for test-coverage option]) ;;
+ esac],
+              [enableval=no])
+
+if test "${enableval}" = yes; then
+   COV_CFLAGS="-fprofile-arcs -ftest-coverage"
+   COV_LDFLAGS="-Wc,-fprofile-arcs -Wc,-ftest-coverage"
+   AC_MSG_CHECKING(whether compiler accepts $COV_CFLAGS)
+   ac_save_CFLAGS="$CFLAGS"
+   ac_save_LDFLAGS="$LDFLAGS"
+   CFLAGS="$CFLAGS $COV_CFLAGS"
+   LDFLAGS="$LDFLAGS $COV_LDFLAGS"
+   AC_TRY_COMPILE(,
+    [int x;],
+    AC_MSG_RESULT(yes),
+    COV_CFLAGS=""
+    COV_LDFLAGS=""
+    AC_MSG_RESULT(no))
+  CFLAGS="$ac_save_CFLAGS"
+  LDFLAGS="$ac_save_LDFLAGS"
+  AC_SUBST([COVERAGE_CFLAGS], [$COV_CFLAGS])
+  AC_SUBST([COVERAGE_LDFLAGS], [$COV_LDFLAGS])
+fi
+
 # very annoying
 rm -f COPYING
 cp COPYING.LIB COPYING
@@ -256,4 +284,5 @@
           libvirt.pc libvirt.spec \
 	  include/libvirt/Makefile include/libvirt/libvirt.h \
 	  python/Makefile python/tests/Makefile \
-          tests/Makefile proxy/Makefile)
+          tests/Makefile proxy/Makefile \
+          utils/Makefile)
diff -ruN libvirt-new/Makefile.am libvirt/Makefile.am
--- libvirt-new/Makefile.am	2006-07-07 08:30:27.000000000 -0400
+++ libvirt/Makefile.am	2006-08-24 10:21:10.000000000 -0400
@@ -1,6 +1,6 @@
 ## Process this file with automake to produce Makefile.in
 
-SUBDIRS = src include docs @PYTHON_SUBDIR@ tests proxy
+SUBDIRS = src include docs @PYTHON_SUBDIR@ tests proxy utils
 
 EXTRA_DIST = libvirt.spec.in libvirt.spec COPYING.LIB \
              libvirt.pc.in libvirt.pc TODO AUTHORS ChangeLog \
@@ -11,6 +11,25 @@
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = libvirt.pc
 
+cov: cov-recursive cov-am
+
+clean-cov:
+	rm -rf coverage
+	cd src && $(MAKE) $(AM_MAKEFLAGS) clean-cov
+
+cov-recursive:
+	cd src && $(MAKE) $(AM_MAKEFLAGS) cov
+
+cov-am:
+	rm -rf coverage
+	mkdir coverage
+	perl $(srcdir)/utils/coverage-report.pl src/*.cov > coverage/index.xml
+	xsltproc $(srcdir)/utils/coverage-report.xsl coverage/index.xml \
+	  > coverage/index.html
+	for i in src/*.gcov ; do o=`echo $$i | sed -e 's/src/coverage/'` ; \
+	  perl $(srcdir)/utils/coverage-report-entry.pl $$i > $$o.html ; done
+
 rpm: clean
 	@(unset CDPATH ; $(MAKE) dist && rpmbuild -ta $(distdir).tar.gz)
 
diff -ruN libvirt-new/src/.cvsignore libvirt/src/.cvsignore
--- libvirt-new/src/.cvsignore	2006-07-07 08:30:32.000000000 -0400
+++ libvirt/src/.cvsignore	2006-08-24 10:19:50.000000000 -0400
@@ -5,3 +5,7 @@
 *.lo
 *.la
 virsh
+*.gcov
+*.cov
+*.gcno
+*.gcda
diff -ruN libvirt-new/src/Makefile.am libvirt/src/Makefile.am
--- libvirt-new/src/Makefile.am	2006-07-07 08:30:32.000000000 -0400
+++ libvirt/src/Makefile.am	2006-08-24 09:55:24.000000000 -0400
@@ -9,10 +9,11 @@
 EXTRA_DIST = libvirt_sym.version
 
 lib_LTLIBRARIES = libvirt.la
+libvirt_la_CFLAGS = $(COVERAGE_CFLAGS)
 libvirt_la_LIBADD = @LIBXML_LIBS@
-
 libvirt_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libvirt_sym.version \
-                    -version-info @LIBVIRT_VERSION_INFO@
+                    -version-info @LIBVIRT_VERSION_INFO@ \
+                     $(COVERAGE_LDFLAGS)
 
 libvirt_la_SOURCES =						\
 		libvirt.c internal.h				\
@@ -30,7 +31,22 @@
 bin_PROGRAMS = virsh
 
 virsh_SOURCES = virsh.c
-virsh_LDFLAGS =
+virsh_CFLAGS =  $(COVERAGE_CFLAGS)
+virsh_LDFLAGS = $(COVERAGE_CFLAGS)
 virsh_DEPENDENCIES = $(DEPS)
 virsh_LDADD = $(LDADDS) $(VIRSH_LIBS)
 
+COVERAGE_FILES = $(libvirt_la_SOURCES:%.c=libvirt_la-%.cov) $(virsh_SOURCES:%.c=virsh-%.cov)
+
+cov: clean-cov $(COVERAGE_FILES)
+
+clean-cov:
+	rm -f *.cov *.gcov
+
+libvirt_la-%.cov: libvirt_la-%.o
+	gcov -b -f -o . $< > $@
+
+virsh-%.cov: virsh-%.o
+	gcov -b -f -o . $< > $@
+
+CLEANFILES = *.cov *.gcov .libs/*.gcda
diff -ruN libvirt-new/tests/Makefile.am libvirt/tests/Makefile.am
--- libvirt-new/tests/Makefile.am	2006-08-24 10:27:02.000000000 -0400
+++ libvirt/tests/Makefile.am	2006-08-24 10:10:39.000000000 -0400
@@ -27,19 +27,19 @@
 	$(top_builddir)/src/xmlrpc.c \
 	$(top_builddir)/src/xmlrpc.h
 
-xmlrpctest_LDFLAGS =
+xmlrpctest_LDFLAGS = $(COVERAGE_CFLAGS)
 xmlrpctest_LDADD = $(LDADDS)
 
 xml2sexprtest_SOURCES = \
 	xml2sexprtest.c \
 	testutils.c testutils.h
-xml2sexprtest_LDFLAGS =
+xml2sexprtest_LDFLAGS = $(COVERAGE_CFLAGS)
 xml2sexprtest_LDADD = $(LDADDS)
 
 sexpr2xmltest_SOURCES = \
 	sexpr2xmltest.c \
 	testutils.c testutils.h
-sexpr2xmltest_LDFLAGS =
+sexpr2xmltest_LDFLAGS = $(COVERAGE_CFLAGS)
 sexpr2xmltest_LDADD = $(LDADDS)
 
 $(LIBVIRT):
diff -ruN libvirt-new/utils/coverage-report-entry.pl libvirt/utils/coverage-report-entry.pl
--- libvirt-new/utils/coverage-report-entry.pl	1969-12-31 19:00:00.000000000 -0500
+++ libvirt/utils/coverage-report-entry.pl	2006-08-24 10:25:21.000000000 -0400
@@ -0,0 +1,57 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2006 Daniel P. Berrange
+#
+# See COPYING.LIB for the License of this software
+
+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 -ruN libvirt-new/utils/coverage-report.pl libvirt/utils/coverage-report.pl
--- libvirt-new/utils/coverage-report.pl	1969-12-31 19:00:00.000000000 -0500
+++ libvirt/utils/coverage-report.pl	2006-08-24 10:25:35.000000000 -0400
@@ -0,0 +1,113 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2006 Daniel P. Berrange
+#
+# See COPYING.LIB for the License of this software
+
+use warnings;
+use strict;
+
+my %coverage = ( functions => {}, files => {} );
+
+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 (/^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 "Shit [$_]\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}}) {
+	    $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);
+	$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}}) {
+	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 -ruN libvirt-new/utils/coverage-report.xsl libvirt/utils/coverage-report.xsl
--- libvirt-new/utils/coverage-report.xsl	1969-12-31 19:00:00.000000000 -0500
+++ libvirt/utils/coverage-report.xsl	2006-08-24 09:10:46.000000000 -0400
@@ -0,0 +1,217 @@
+<?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 -ruN libvirt-new/utils/Makefile.am libvirt/utils/Makefile.am
--- libvirt-new/utils/Makefile.am	1969-12-31 19:00:00.000000000 -0500
+++ libvirt/utils/Makefile.am	2006-08-24 10:22:52.000000000 -0400
@@ -0,0 +1,3 @@
+
+EXTRA_DIST = $(wildcard *.xsl) $(wildcard *.pl)
+ 


More information about the libvir-list mailing list