Skip to content

Commit

Permalink
Initial commit into Google Code; working except for coverage report
Browse files Browse the repository at this point in the history
  • Loading branch information
jeni.tennison committed Sep 5, 2008
0 parents commit 54f6e15
Show file tree
Hide file tree
Showing 12 changed files with 2,712 additions and 0 deletions.
Binary file not shown.
103 changes: 103 additions & 0 deletions com/jenitennison/xslt/tests/XSLTCoverageTraceListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.jenitennison.xslt.tests;

import net.sf.saxon.trace.TraceListener;
import net.sf.saxon.trace.InstructionInfo;
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.om.Item;
import java.io.PrintStream;

/**
* A Simple trace listener for XSLT that writes messages (by default) to System.err
*/

public class XSLTCoverageTraceListener implements TraceListener {

private PrintStream out = System.err;

/**
* Method called at the start of execution, that is, when the run-time transformation starts
*/

public void open() {
out.println("<trace>");
}

/**
* Method called at the end of execution, that is, when the run-time execution ends
*/

public void close() {
out.println("</trace>");
}

/**
* Method that is called when an instruction in the stylesheet gets processed.
* @param instruction gives information about the instruction being
* executed, and about the context in which it is executed. This object is mutable,
* so if information from the InstructionInfo is to be retained, it must be copied.
*/

public void enter(InstructionInfo info, XPathContext context) {
out.println("<hit line=\"" + info.getLineNumber() + "\" module=\"" + info.getSystemId() + "\" />");
}

/**
* Method that is called after processing an instruction of the stylesheet,
* that is, after any child instructions have been processed.
* @param instruction gives the same information that was supplied to the
* enter method, though it is not necessarily the same object. Note that the
* line number of the instruction is that of the start tag in the source stylesheet,
* not the line number of the end tag.
*/

public void leave(InstructionInfo instruction) {
// Do nothing
}

/**
* Method that is called by an instruction that changes the current item
* in the source document: that is, xsl:for-each, xsl:apply-templates, xsl:for-each-group.
* The method is called after the enter method for the relevant instruction, and is called
* once for each item processed.
* @param currentItem the new current item. Item objects are not mutable; it is safe to retain
* a reference to the Item for later use.
*/

public void startCurrentItem(Item currentItem) {
// Do nothing
}

/**
* Method that is called when an instruction has finished processing a new current item
* and is ready to select a new current item or revert to the previous current item.
* The method will be called before the leave() method for the instruction that made this
* item current.
* @param currentItem the item that was current, whose processing is now complete. This will represent
* the same underlying item as the corresponding startCurrentItem() call, though it will
* not necessarily be the same actual object.
*/

public void endCurrentItem(Item currentItem) {
// Do nothing
}

}

//
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the
// License at http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations under the License.
//
// The Original Code is: all this file.
//
// The Initial Developer of the Original Code is Edwin Glaser (edwin@pannenleiter.de)
//
// Portions created by Jeni Tennison are Copyright (C) Jeni Tennison. All Rights Reserved.
//
// Contributor(s): Heavily modified by Michael Kay
// Methods implemented by Jeni Tennison
//
203 changes: 203 additions & 0 deletions coverage-report.xsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:test="http://www.jenitennison.com/xslt/unit-test"
xmlns:saxon="http://saxon.sf.net/"
xmlns="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="xs saxon">

<xsl:import href="format-utils.xsl" />

<xsl:param name="tests" as="xs:string" required="yes" />

<xsl:variable name="tests-uri" as="xs:anyURI"
select="if (starts-with($tests, '/'))
then xs:anyURI($tests)
else xs:anyURI(concat('file:/', translate($tests, '\', '/')))" />

<xsl:variable name="stylesheet-uri" as="xs:anyURI"
select="if (doc($tests-uri)/*/@stylesheet)
then resolve-uri(doc($tests-uri)/*/@stylesheet, $tests-uri)
else $tests-uri" />

<xsl:variable name="trace" as="document-node()" select="/" />

<xsl:variable name="stylesheet-trees" as="document-node()+"
select="test:collect-stylesheets(doc($stylesheet-uri))" />

<xsl:function name="test:collect-stylesheets" as="document-node()+">
<xsl:param name="stylesheets" as="document-node()+" />
<xsl:variable name="imports" as="document-node()*"
select="document($stylesheets/xsl:import/@href)" />
<xsl:variable name="new-stylesheets" as="document-node()*"
select="$stylesheets | $imports" />
<xsl:choose>
<xsl:when test="$imports except $stylesheets">
<xsl:sequence select="test:collect-stylesheets($stylesheets | $imports)" />
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$stylesheets" />
</xsl:otherwise>
</xsl:choose>
</xsl:function>

<xsl:key name="tree" match="node()" use="saxon:line-number(.)" />
<xsl:key name="coverage" match="hit" use="concat(@module, ':', @line)" />

<xsl:template match="/">
<xsl:apply-templates select="." mode="test:coverage-report" />
</xsl:template>

<xsl:template match="/" mode="test:coverage-report">
<html>
<head>
<title>Test Coverage Report for <xsl:value-of select="test:format-URI($stylesheet-uri)" /></title>
<link rel="stylesheet" type="text/css"
href="{resolve-uri('test-report.css', static-base-uri())}" />
</head>
<body>
<h1>Test Coverage Report</h1>
<p>Stylesheet: <a href="{$stylesheet-uri}"><xsl:value-of select="test:format-URI($stylesheet-uri)" /></a></p>
<xsl:apply-templates select="$stylesheet-trees/xsl:*" mode="test:coverage-report" />
</body>
</html>
</xsl:template>

<xsl:template match="xsl:stylesheet | xsl:transform" mode="test:coverage-report">
<xsl:variable name="stylesheet-uri" as="xs:anyURI"
select="base-uri(.)" />
<xsl:variable name="stylesheet-tree" as="document-node()"
select=".." />
<xsl:variable name="stylesheet-string" as="xs:string"
select="unparsed-text($stylesheet-uri)" />
<xsl:variable name="stylesheet-lines" as="xs:string+"
select="tokenize($stylesheet-string, '\n')" />
<xsl:variable name="number-of-lines" as="xs:integer"
select="count($stylesheet-lines)" />
<xsl:variable name="number-width" as="xs:integer"
select="string-length(xs:string($number-of-lines))" />
<xsl:variable name="number-format" as="xs:string"
select="string-join(for $i in 1 to $number-width return '0', '')" />
<h2>
module: <xsl:value-of select="$stylesheet-uri" />;
<xsl:value-of select="$number-of-lines" /> lines
</h2>
<pre>
<xsl:apply-templates select="." mode="test:line">
<xsl:with-param name="number-format" select="$number-format" tunnel="yes" />
<xsl:with-param name="lines" select="$stylesheet-lines" tunnel="yes" />
<xsl:with-param name="last-line" select="0" tunnel="yes" />
</xsl:apply-templates>
</pre>
</xsl:template>

<xsl:template match="text()[not(normalize-space())]" mode="test:line">
<xsl:apply-templates select="following::node()[1]" mode="test:line" />
</xsl:template>

<xsl:template match="node()" mode="test:line">
<xsl:param name="number-format" as="xs:string" required="yes" tunnel="yes" />
<xsl:param name="lines" as="xs:string+" required="yes" tunnel="yes" />
<xsl:param name="last-line" as="xs:integer" required="yes" tunnel="yes" />
<xsl:variable name="line-number" as="xs:integer" select="saxon:line-number(.)" />
<xsl:variable name="coverage" as="xs:string" select="test:coverage(.)" />
<xsl:call-template name="test:output-lines">
<xsl:with-param name="line-number" select="$last-line + 1" />
<xsl:with-param name="lines"
select="$lines[position() &lt;= $line-number]" />
<xsl:with-param name="node-start" as="xs:string">
<xsl:choose>
<xsl:when test="self::element()">
<xsl:value-of select="concat('&lt;', name(.))" />
</xsl:when>
<xsl:when test="self::text()">
<xsl:value-of select="tokenize(., '&#xA;')[last()]" />
</xsl:when>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
<xsl:apply-templates select="(node() | following::node())[1]" mode="test:line">
<xsl:with-param name="last-line" select="$line-number" tunnel="yes" />
</xsl:apply-templates>
</xsl:template>

<xsl:template name="test:output-lines">
<xsl:param name="line-number" as="xs:integer" required="yes" />
<xsl:param name="lines" as="xs:string*" required="yes" />
<xsl:param name="node-start" as="xs:string" required="yes" />
</xsl:template>

<xsl:function name="test:coverage" as="xs:string">
<xsl:param name="node" as="node()" />
<xsl:choose>
<!-- A hit on these nodes doesn't really count; you have to hit
their contents to hit them -->
<xsl:when test="$node/self::xsl:otherwise or
$node/self::xsl:when or
$node/self::xsl:matching-substring or
$node/self::xsl:non-matching-substring or
$node/self::xsl:for-each">
<xsl:variable name="hits-on-content" as="element(hit)*"
select="test:hit-on-nodes($node/node())" />
<xsl:choose>
<xsl:when test="exists($hits-on-content)">hit</xsl:when>
<xsl:otherwise>missed</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="hit" as="element(hit)*"
select="test:hit-on-nodes($node)" />
<xsl:choose>
<xsl:when test="exists($hit)">hit</xsl:when>
<xsl:when test="$node/self::xsl:variable">
<xsl:sequence select="test:coverage($node/following-sibling::*[not(self::xsl:variable)][1])" />
</xsl:when>
<xsl:when test="$node/ancestor::xsl:variable">
<xsl:sequence select="test:coverage($node/ancestor::xsl:variable)" />
</xsl:when>
<xsl:when test="$node/self::xsl:stylesheet or
$node/self::xsl:transform">ignored</xsl:when>
<xsl:when test="$node/self::xsl:function or
$node/self::xsl:template">missed</xsl:when>
<!-- A node within an top-level non-XSLT element -->
<xsl:when test="not($node/ancestor::xsl:*/parent::xsl:stylesheet)">ignored</xsl:when>
<xsl:when test="$node/self::xsl:param">
<xsl:sequence select="test:coverage($node/parent::*)" />
</xsl:when>
<xsl:otherwise>missed</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:function>

<xsl:function name="test:hit-on-nodes" as="element(hit)*">
<xsl:param name="nodes" as="node()*" />
<xsl:for-each select="$nodes[not(self::text()[not(normalize-space())])]">
<xsl:variable name="stylesheet-path" as="xs:string"
select="if (starts-with(base-uri(.), '/'))
then concat('file:', base-uri(.))
else base-uri(.)" />
<xsl:sequence select="test:hit-on-lines(saxon:line-number(.), $stylesheet-path)" />
</xsl:for-each>
</xsl:function>

<xsl:function name="test:hit-on-lines" as="element(hit)*">
<xsl:param name="line-numbers" as="xs:integer*" />
<xsl:variable name="stylesheet-path" as="xs:string"
select="if (starts-with($stylesheet-uri, '/'))
then concat('file:', $stylesheet-uri)
else $stylesheet-uri" />
<xsl:sequence select="test:hit-on-lines($line-numbers, $stylesheet-path)" />
</xsl:function>

<xsl:function name="test:hit-on-lines" as="element(hit)*">
<xsl:param name="line-numbers" as="xs:integer*" />
<xsl:param name="stylesheet-path" as="xs:string" />
<xsl:variable name="keys" as="xs:string*"
select="for $l in $line-numbers
return concat($stylesheet-path, ':', $l)" />
<xsl:sequence select="key('coverage', $keys, $trace)" />
</xsl:function>

</xsl:stylesheet>
Loading

0 comments on commit 54f6e15

Please sign in to comment.