I have adapted the NCover xslt originally worked up by Yves Lorphelin to function correctly within the 0.9.2 and 1.0.RC Web Dashboards of Thoughtworks Cruise Control .Net Continuous Integration Server.
These stylesheets will allow multiple NCover reports to be parsed and to be shown in full detail or summary detail only. (The full detail can take minutes to transform when large code coverage reports are being parsed).
Simply drop the xslt code below into a file called NCover.xsl and replace the \webdashboard\xsl\NCover.xsl included with the default install of CCNet.
To configure NCover reporting in CCNet see Thoughtworks Cruise Control .Net Documentation.
Summary Detail Xslt:
<
xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<!-- created by Yves Lorphelin, largely inspired by the nunitsumary.xsl (see nantcontrib.sourceforge.net)-->
<!-- modified by Richard Schneider, short document names with a hyperlink -->
<!-- modified by Robert Neuman, enabled for webdashboard and NUnit report style -->
<!-- modified by Adrian Spear, enabled processing of multiple 'coverage' aggregated reports -->
<xsl:output method="html" />
<xsl:template match="/">
<style>BODY { font: small verdana, arial, helvetica; color:#000000; } P { line-height:1.5em; margin-top:0.5em; margin-bottom:1.0em; } H1 { MARGIN: 0px 0px 5px; FONT: bold larger arial, verdana, helvetica; } H2 { MARGIN-TOP: 1em; MARGIN-BOTTOM: 0.5em; FONT: larger verdana,arial,helvetica } H3 { MARGIN-BOTTOM: 0.5em; FONT: bold 13px verdana,arial,helvetica } H4 { MARGIN-BOTTOM: 0.5em; FONT: bold 100% verdana,arial,helvetica } H5 { MARGIN-BOTTOM: 0.5em; FONT: bold 100% verdana,arial,helvetica } H6 { MARGIN-BOTTOM: 0.5em; FONT: bold 100% verdana,arial,helvetica } .notVisited {background:red; } .visited { background: #90ee90; } .title { font-size: large; font-weight: bold; } .assembly { font-size: normal; font-weight: bold;} .class {font-size:normal; cursor: hand; color: #777;} .module { color: navy; font-size: small; } .method {cursor: hand; color: ; font-size: normal; font-weight: bold; } .subtitle { color: black; font-size: large; font-weight: bold; } .hdrcell {font-size:smaller; background-color: #DDEEFF; } .datacell {font-size:smaller; background-color: #FFFFEE; text-align: right; } .textcell {font-size:smaller; background-color: #FFFFEE; text-align: left; } .hldatacell {font-size:smaller; background-color: #FFCCCC; text-align: right; } .detailProcent { font-size: small; font-weight: bold; padding-top: 1px; padding-bottom: 1px; padding-left: 3px; padding-right: 3px;}</style>
<div id="report">
<script>
<![CDATA[
function toggleDiv
( imgId, divId )
{
eDiv = document.getElementById
( divId );
eImg = document.getElementById
( imgId );
if
( eDiv.style.display == "none" )
{
eDiv.style.display =
"block";
eImg.src =
"images/arrow_minus_small.gif";
}
else
{
eDiv.style.display =
"none";
eImg.src =
"images/arrow_plus_small.gif";
}
}
function SwitchAll
(how)
{
var len = document.all.length-1;
for
(i=0;i!=len;i++)
{
var block = document.all[i];
if (block != null && block.id != '')
{
if (block.id.substring(0,3) == 'img')
{
if(how=='none'){block.src = 'images/arrow_plus_small.gif';}
else {block.src = 'images/arrow_minus_small.gif';}
}
if (block.id.substring(0,10) == 'divDetails')
{
block.style.display=how;}
}
}
}
function ExpandAll() {SwitchAll('block');}
function CollapseAll() {SwitchAll('none');}
]]>
</script>
<div id="summary">
<xsl:call-template name="header" />
</div>
</div>
<xsl:apply-templates select="//coverage[count(module) != 0]" />
</xsl:template>
<xsl:template name="coverage" match="coverage">
<div id="details">
<xsl:apply-templates select="module" />
</div>
</xsl:template>
<xsl:template name="module" match="module">
<xsl:variable name="module" select="./@assembly" />
<xsl:variable name="total" select="count(./method/seqpnt)" />
<xsl:variable name="notVisited" select="count( ./method/seqpnt[ @visitcount='0'] )" />
<xsl:variable name="visited" select="count(./method/seqpnt[not(@visitcount='0')] )" />
<table cellpadding="2" cellspacing="0" border="0" width="98%">
<tr>
<td class="yellow-sectionheader" colspan="6" valign="top">
<div class="assembly">
<table cellspacing="0" border="0" width="98%">
<tr>
<td width="30%">
<a name="#{generate-id($module)}"><xsl:value-of select="$module" />.dll</a>
</td>
<td width="70%">
<xsl:call-template name="detailProcent">
<xsl:with-param name="visited" select="$visited" />
<xsl:with-param name="notVisited" select="$notVisited" />
<xsl:with-param name="total" select="$total" />
</xsl:call-template>
</td>
</tr>
</table>
</div>
</td>
</tr>
</table>
<xsl:variable name="totalMod" select="count(./method/seqpnt)" />
<xsl:variable name="notvisitedMod" select="count( ./method/seqpnt[ @visitcount='0'] ) div $totalMod * 100 " />
<xsl:variable name="visitedMod" select="count(./method/seqpnt[not(@visitcount='0')] ) div $totalMod * 100" />
</xsl:template>
<!-- only output the file name/ext of the path -->
<xsl:template name="output-filename">
<xsl:param name="path" />
<xsl:variable name="s" select="substring-after(translate($path, '\', '/'), '/')" />
<xsl:if test="not($s)">
<xsl:value-of select="$path" />
</xsl:if>
<xsl:if test="$s">
<xsl:call-template name="output-filename">
<xsl:with-param name="path" select="$s" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<!-- General Header -->
<xsl:template name="header">
<h1>Code Coverage Report</h1>
<hr size="1" />
</xsl:template>
<xsl:template name="footer">
<hr size="1" />
<a href="#{top}">Top</a>
</xsl:template>
<!-- draw % table-->
<xsl:template name="detailProcent">
<xsl:param name="visited" />
<xsl:param name="notVisited" />
<xsl:param name="total" />
<table width="100%" class="detailProcent">
<tr>
<xsl:if test="not($notVisited=0)">
<td class="notVisited">
<xsl:attribute name="width">
<xsl:value-of select="concat($notVisited div $total * 100,'%')" />
</xsl:attribute>
<xsl:value-of select="concat (format-number($notVisited div $total * 100,'#.##'),'%')" />
</td>
</xsl:if>
<xsl:if test="not ($visited=0)">
<td class="visited">
<xsl:attribute name="width">
<xsl:value-of select="concat($visited div $total * 100,'%')" />
</xsl:attribute>
<xsl:value-of select="concat (format-number($visited div $total * 100,'#.##'), '%')" />
</td>
</xsl:if>
</tr>
</table>
</xsl:template>
</
xsl:stylesheet>
Full Detail Xslt:
<
xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<!-- created by Yves Lorphelin, largely inspired by the nunitsumary.xsl (see nantcontrib.sourceforge.net)-->
<!-- modified by Richard Schneider, short document names with a hyperlink -->
<!-- modified by Robert Neuman, enabled for webdashboard and NUnit report style -->
<!-- modified by Adrian Spear, enabled processing of multiple 'coverage' aggregated reports -->
<xsl:output method="html" />
<xsl:template match="/">
<style>BODY { font: small verdana, arial, helvetica; color:#000000; } P { line-height:1.5em; margin-top:0.5em; margin-bottom:1.0em; } H1 { MARGIN: 0px 0px 5px; FONT: bold larger arial, verdana, helvetica; } H2 { MARGIN-TOP: 1em; MARGIN-BOTTOM: 0.5em; FONT: larger verdana,arial,helvetica } H3 { MARGIN-BOTTOM: 0.5em; FONT: bold 13px verdana,arial,helvetica } H4 { MARGIN-BOTTOM: 0.5em; FONT: bold 100% verdana,arial,helvetica } H5 { MARGIN-BOTTOM: 0.5em; FONT: bold 100% verdana,arial,helvetica } H6 { MARGIN-BOTTOM: 0.5em; FONT: bold 100% verdana,arial,helvetica } .notVisited {background:red; } .visited { background: #90ee90; } .title { font-size: large; font-weight: bold; } .assembly { font-size: normal; font-weight: bold;} .class {font-size:normal; cursor: hand; color: #777;} .module { color: navy; font-size: small; } .method {cursor: hand; color: ; font-size: normal; font-weight: bold; } .subtitle { color: black; font-size: large; font-weight: bold; } .hdrcell {font-size:smaller; background-color: #DDEEFF; } .datacell {font-size:smaller; background-color: #FFFFEE; text-align: right; } .textcell {font-size:smaller; background-color: #FFFFEE; text-align: left; } .hldatacell {font-size:smaller; background-color: #FFCCCC; text-align: right; } .detailProcent { font-size: small; font-weight: bold; padding-top: 1px; padding-bottom: 1px; padding-left: 3px; padding-right: 3px;}</style>
<div id="report">
<script>
<![CDATA[
function toggleDiv
( imgId, divId )
{
eDiv = document.getElementById
( divId );
eImg = document.getElementById
( imgId );
if
( eDiv.style.display == "none" )
{
eDiv.style.display =
"block";
eImg.src =
"images/arrow_minus_small.gif";
}
else
{
eDiv.style.display =
"none";
eImg.src =
"images/arrow_plus_small.gif";
}
}
function SwitchAll
(how)
{
var len = document.all.length-1;
for
(i=0;i!=len;i++)
{
var block = document.all[i];
if (block != null && block.id != '')
{
if (block.id.substring(0,3) == 'img')
{
if(how=='none'){block.src = 'images/arrow_plus_small.gif';}
else {block.src = 'images/arrow_minus_small.gif';}
}
if (block.id.substring(0,10) == 'divDetails')
{
block.style.display=how;}
}
}
}
function ExpandAll() {SwitchAll('block');}
function CollapseAll() {SwitchAll('none');}
]]>
</script>
<div id="summary">
<xsl:call-template name="header" />
</div>
</div>
<xsl:apply-templates select="//coverage[count(module) != 0]" />
</xsl:template>
<xsl:template name="coverage" match="coverage">
<div id="details">
<xsl:apply-templates select="module" />
</div>
</xsl:template>
<xsl:template name="module" match="module">
<xsl:variable name="module" select="./@assembly" />
<xsl:variable name="total" select="count(./method/seqpnt)" />
<xsl:variable name="notVisited" select="count( ./method/seqpnt[ @visitcount='0'] )" />
<xsl:variable name="visited" select="count(./method/seqpnt[not(@visitcount='0')] )" />
<table cellpadding="2" cellspacing="0" border="0" width="98%">
<tr>
<td class="yellow-sectionheader" colspan="6" valign="top">
<div class="assembly">
<table cellspacing="0" border="0" width="98%">
<tr>
<xsl:attribute name="onclick">BLOCKED SCRIPTtoggleDiv('img<xsl:value-of select="$module" />', 'divDetails<xsl:value-of select="$module" />');</xsl:attribute>
<td width="30%">
<input type="image" src="images/arrow_plus_small.gif">
<xsl:attribute name="id">img<xsl:value-of select="$module" /></xsl:attribute>
</input> 
<a name="#{generate-id($module)}"><xsl:value-of select="$module" />.dll</a>
</td>
<td width="70%">
<xsl:call-template name="detailProcent">
<xsl:with-param name="visited" select="$visited" />
<xsl:with-param name="notVisited" select="$notVisited" />
<xsl:with-param name="total" select="$total" />
</xsl:call-template>
</td>
</tr>
</table>
</div>
<div style="display:none">
<xsl:attribute name="id">divDetails<xsl:value-of select="$module" /></xsl:attribute>
<xsl:for-each select="./method[not(./@class = preceding-sibling::method/@class)]">
<xsl:sort select="@class" />
<xsl:sort select="@name" />
<xsl:call-template name="ClassSummary">
<xsl:with-param name="module" select="$module" />
<xsl:with-param name="class" select="./@class" />
</xsl:call-template>
</xsl:for-each>
</div>
</td>
</tr>
</table>
<xsl:variable name="totalMod" select="count(./method/seqpnt)" />
<xsl:variable name="notvisitedMod" select="count( ./method/seqpnt[ @visitcount='0'] ) div $totalMod * 100 " />
<xsl:variable name="visitedMod" select="count(./method/seqpnt[not(@visitcount='0')] ) div $totalMod * 100" />
</xsl:template>
<xsl:template name="Methods">
<xsl:param name="module" />
<xsl:param name="class" />
<xsl:for-each select="//method[(@class = $class) and (parent::module/@assembly=$module)]">
<xsl:sort select="count(./seqpnt[@visitcount='0'] )" order="descending" />
<xsl:sort select="@name" /> <!--notVisited-->
<xsl:variable name="total" select="count(./seqpnt)" />
<xsl:variable name="notvisited" select="count(./seqpnt[@visitcount='0'] ) " />
<xsl:variable name="visited" select="count(./seqpnt[not(@visitcount='0')])" />
<xsl:variable name="methid" select="generate-id(.)" />
<table cellpadding="3" cellspacing="0" width="100%">
<tr>
<td width="45%" class='method'>
<xsl:attribute name="onclick">BLOCKED SCRIPTtoggleDiv('img<xsl:value-of select="$methid" />', 'divDetails<xsl:value-of select="$methid" />');</xsl:attribute>
        
<input type="image" src="images/arrow_plus_small.gif">
<xsl:attribute name="id">img<xsl:value-of select="$methid" /></xsl:attribute>
</input> 
<xsl:value-of select="@name" />
</td>
<td width="55%">
<xsl:attribute name="onclick">BLOCKED SCRIPTtoggleDiv('img<xsl:value-of select="$methid" />', 'divDetails<xsl:value-of select="$methid" />');</xsl:attribute>
<xsl:call-template name="detailProcent">
<xsl:with-param name="visited" select="$visited" />
<xsl:with-param name="notVisited" select="$notvisited" />
<xsl:with-param name="total" select="$total" />
</xsl:call-template>
</td>
</tr>
</table>
<div style="display:none">
<xsl:attribute name="id">divDetails<xsl:value-of select="$methid" /></xsl:attribute>
<xsl:call-template name="seqpnt">
<xsl:with-param name="module" select="$module" />
<xsl:with-param name="class" select="$class" />
<xsl:with-param name="id" select="$methid" />
</xsl:call-template>
</div>
</xsl:for-each>
</xsl:template>
<xsl:template name="seqpnt">
<xsl:param name="module" />
<xsl:param name="class" />
<xsl:param name="id" />
<table cellpadding="3" cellspacing="0" border='1' width="100%" bordercolor="black" style="display: block;">
<tr>
<td class="hdrcell">Visits</td>
<td class="hdrcell">Line</td>
<td class="hdrcell">End</td>
<td class="hdrcell">Column</td>
<td class="hdrcell">End</td>
<td class="hdrcell">Document</td>
</tr>
<xsl:for-each select="./seqpnt">
<xsl:sort select="@line" />
<tr>
<td class="datacell">
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="@visitcount = 0">hldatacell</xsl:when>
<xsl:otherwise>datacell</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:value-of select="@visitcount" />
</td>
<td class="datacell">
<xsl:value-of select="@line" />
</td>
<td class="datacell">
<xsl:value-of select="@endline" />
</td>
<td class="datacell">
<xsl:value-of select="@column" />
</td>
<td class="datacell">
<xsl:value-of select="@endcolumn" />
</td>
<td class="textcell">
<a href="{@document}">
<xsl:call-template name="output-filename">
<xsl:with-param name="path" select="@document" />
</xsl:call-template>
</a>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<!-- only output the file name/ext of the path -->
<xsl:template name="output-filename">
<xsl:param name="path" />
<xsl:variable name="s" select="substring-after(translate($path, '\', '/'), '/')" />
<xsl:if test="not($s)">
<xsl:value-of select="$path" />
</xsl:if>
<xsl:if test="$s">
<xsl:call-template name="output-filename">
<xsl:with-param name="path" select="$s" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<!-- Class Summary -->
<xsl:template name="ClassSummary">
<xsl:param name="module" />
<xsl:param name="class" />
<xsl:variable name="total" select="count(//seqpnt[(parent::method/parent::module/@assembly=$module) and (parent::method/@class=$class) ])" />
<xsl:variable name="notvisited" select="count(//seqpnt[(parent::method/parent::module/@assembly=$module)and (parent::method/@class=$class) and (@visitcount='0')] )" />
<xsl:variable name="visited" select="count(//seqpnt[(parent::method/parent::module/@assembly=$module) and (parent::method/@class=$class) and (not(@visitcount='0'))] )" />
<xsl:variable name="newid" select="concat (generate-id(), 'class')" />
<div>
<table width='100%'>
<tr>
<td width="40%" class="class">
<xsl:attribute name="onclick">BLOCKED SCRIPTtoggleDiv('img<xsl:value-of select="$newid" />', 'divDetails<xsl:value-of select="$newid" />');</xsl:attribute>
    
<input type="image" src="images/arrow_plus_small.gif">
<xsl:attribute name="id">img<xsl:value-of select="$newid" /></xsl:attribute>
</input> 
<xsl:value-of select="$class" />
</td>
<td width="60%">
<xsl:attribute name="onclick">BLOCKED SCRIPTtoggleDiv('img<xsl:value-of select="$newid" />', 'divDetails<xsl:value-of select="$newid" />');</xsl:attribute>
<xsl:call-template name="detailProcent">
<xsl:with-param name="visited" select="$visited" />
<xsl:with-param name="notVisited" select="$notvisited" />
<xsl:with-param name="total" select="$total" />
</xsl:call-template>
</td>
</tr>
</table>
<div style="display:none">
<xsl:attribute name="id">divDetails<xsl:value-of select="$newid" /></xsl:attribute>
<table width='99%'>
<tr>
<td>
<xsl:call-template name="Methods">
<xsl:with-param name="module" select="$module" />
<xsl:with-param name="class" select="$class" />
</xsl:call-template>
</td>
</tr>
</table>
</div>
</div>
<hr size="1" width='90%' align='left' style=" border-bottom: 1px dotted #999;" />
</xsl:template>
<xsl:template name="ClassSummaryDetail">
<xsl:param name="module" />
<xsl:variable name="total" select="count(./method/seqpnt)" />
<xsl:variable name="notVisited" select="count( ./method/seqpnt[ @visitcount='0'] )" />
<xsl:variable name="visited" select="count(./method/seqpnt[not(@visitcount='0')] )" />
<td width="35%">
<div class="assembly">
<a href="#{generate-id($module)}">
<xsl:value-of select="$module" />
</a>
</div>
</td>
<td width="65%">
<xsl:call-template name="detailProcent">
<xsl:with-param name="visited" select="$visited" />
<xsl:with-param name="notVisited" select="$notVisited" />
<xsl:with-param name="total" select="$total" />
</xsl:call-template>
</td>
</xsl:template>
<!-- Modules Summary -->
<xsl:template name="ModuleSummary">
<H2>Modules summary</H2>
<xsl:for-each select="//module">
<xsl:sort select="@assembly" />
<table width='90%'>
<tr>
<xsl:call-template name="ModuleSummaryDetail">
<xsl:with-param name="module" select="./@assembly" />
</xsl:call-template>
</tr>
</table>
</xsl:for-each>
<hr size="1" />
</xsl:template>
<xsl:template name="ModuleSummaryDetail">
<xsl:param name="module" />
<xsl:variable name="total" select="count(./method/seqpnt)" />
<xsl:variable name="notVisited" select="count( ./method/seqpnt[ @visitcount='0'] )" />
<xsl:variable name="visited" select="count(./method/seqpnt[not(@visitcount='0')] )" />
<td width="30%">
<div class="assembly">
<a href="#{generate-id($module)}">
<xsl:value-of select="$module" />
</a>
</div>
</td>
<td width="70%">
<xsl:call-template name="detailProcent">
<xsl:with-param name="visited" select="$visited" />
<xsl:with-param name="notVisited" select="$notVisited" />
<xsl:with-param name="total" select="$total" />
</xsl:call-template>
</td>
</xsl:template>
<!-- General Header -->
<xsl:template name="header">
<h1