Browse Source

* Patch from Graeme Geldenhuys to improve layout of generated report

git-svn-id: trunk@12328 -
michael 16 years ago
parent
commit
8be887e018

+ 8 - 0
.gitattributes

@@ -1347,7 +1347,15 @@ packages/fcl-fpcunit/src/example_output/results.pdf -text
 packages/fcl-fpcunit/src/example_output/results.txt svneol=native#text/plain
 packages/fcl-fpcunit/src/example_xsl/fpcunit.css svneol=native#text/plain
 packages/fcl-fpcunit/src/example_xsl/fpcunit.xsl svneol=native#text/plain
+packages/fcl-fpcunit/src/example_xsl/images/lastlink.gif -text
+packages/fcl-fpcunit/src/example_xsl/images/link.gif -text
+packages/fcl-fpcunit/src/example_xsl/images/minus.gif -text
+packages/fcl-fpcunit/src/example_xsl/images/plus.gif -text
+packages/fcl-fpcunit/src/example_xsl/images/testcase.gif -text
+packages/fcl-fpcunit/src/example_xsl/images/testsuite.gif -text
 packages/fcl-fpcunit/src/example_xsl/readme.txt svneol=native#text/plain
+packages/fcl-fpcunit/src/example_xsl/scripts/fpcunit.css svneol=native#text/plain
+packages/fcl-fpcunit/src/example_xsl/scripts/treeview.js svneol=native#text/plain
 packages/fcl-fpcunit/src/exampletests/Makefile svneol=native#text/plain
 packages/fcl-fpcunit/src/exampletests/Makefile.fpc svneol=native#text/plain
 packages/fcl-fpcunit/src/exampletests/fpcunittests.pp svneol=native#text/plain

+ 394 - 206
packages/fcl-fpcunit/src/example_xsl/fpcunit.xsl

@@ -1,82 +1,63 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
-<xsl:output method="html" encoding="UTF-8"/>
+
+  <!-- Change the encoding here if you need it, i.e. UTF-8 -->
+  <xsl:output method="html" encoding="UTF-8" indent="yes"
+    doctype-public="-//W3C//DTD HTML 4.0 Transitional//EN"
+  />
+
+  <!-- ************************************ Parameters ************************************ -->
+  <!-- expand-treeview, boolean - true if you want to expand the tree-view at the first print -->
+  <xsl:param name="param-expand-treeview" select="'false'"/>
+  <!-- preferred browser Netscape/Mozilla or Internet Explorer. Thanks to Bill, 90% of the sheeps use IE, but I don't so NS is the default here -->
+  <xsl:param name="param-is-netscape" select="'true'"/>
+  <!-- horizontal distance in pixels between a folder and its leaves -->
+  <xsl:param name="param-shift-width" select="15"/>
+  <!-- image source directory-->
+  <xsl:param name="param-img-directory" select="'images/'"/>
+  <!-- scripts and stylesheet source directory-->
+  <xsl:param name="param-scripts-directory" select="'scripts/'"/>
+  <!-- expand-errors-and-failures, boolean - true if you want to expand the testcase nodes (errors & failures) at the first print -->
+  <xsl:param name="param-expand-errors-and-failures" select="'false'"/>
+  <!-- if expand-treeview is false we can auto expand first few nodes. if 0 then expand until error or failure testcases. -->
+  <xsl:param name="param-expand-depth" select="3"/>
+
+  <!-- ************************************ Variables ************************************ -->
+  <xsl:variable name="var-simple-quote">'</xsl:variable>
+  <xsl:variable name="var-slash-quote">\'</xsl:variable>
+
 
 <xsl:template match="/">
   <html>
   <head>
-    <title>fpcUnit Results</title>
-  	  <style type="text/css" title="fpcUnit" media="screen">
-		    @import "fpcunit.css";
-	    </style>
-    <script>
-window.onload = function () {
-	var x = document.getElementsByTagName('div');
-	for (var i=0;i&lt;x.length;i++)
-	{
-		if (x[i].className == 'testsuitelabel')
-			x[i].onclick = clickSuite;
-		if (x[i].className == 'testsuiteshowall')
-			x[i].onclick = openSuites;
-		if (x[i].className == 'testsuitehideall')
-			x[i].onclick = closeSuites;
-	}
-
-	closeSuites();
-}
-
-function closeSuites()
-{
-	var x = document.getElementsByTagName('div');
-	for (var i=0;i&lt;x.length;i++)
-	{
-		if (x[i].className == 'testcontent')
-			x[i].style.display = 'none';
-	}
-}
-
-function openSuites()
-{
-	var x = document.getElementsByTagName('div');
-	for (var i=0;i&lt;x.length;i++)
-	{
-		if (x[i].className == 'testcontent')
-			x[i].style.display = 'block';
-	}
-}
-
-function clickSuite(e)
-{
-	if (!e) var e = window.event;
-	if (e.target) var tg = e.target;
-	else if (e.srcElement) var tg = e.srcElement;
-	while (tg.nodeName != 'DIV') // Safari GRRRRRRRRRR
-		tg = tg.parentNode;
-	var nextSib = tg.nextSibling;
-	while (nextSib.nodeType != 1)
-		nextSib = nextSib.nextSibling;
-	var nextSibStatus = (nextSib.style.display == 'none') ? 'block' : 'none';
-	nextSib.style.display = nextSibStatus;
-}
-    </script>
+    <title>FPCUnit Results</title>
+      <link href="{$param-scripts-directory}fpcunit.css" rel="stylesheet" type="text/css"/>
+      <script src="{$param-scripts-directory}treeview.js" language="javascript" type="text/javascript"/>
   </head>
   <body>
 
   <a name="Summary"/>
-  <h2>fpcUnit Results</h2>
+  <h2>FPCUnit Results</h2>
   <xsl:apply-templates/>
 
-	<address>
-		<a href="http://opensoft.homeip.net">fpcUnit Report</a> 0.3.1 © 2006 by 
-		<a href="mailto:[email protected]?subject=Comments about fpcUnit Report">Graeme Geldenhuys</a>.<br/>
-		Licensed under the <a href="http://www.gnu.org/copyleft/gpl.html">GNU General Public License</a>.<br/>
-	</address>
+  
+  <address>
+    <a href="http://opensoft.homeip.net">FPCUnit Report</a> 0.4.0 [beta3] &#169; 2006-2008 by 
+    <a href="mailto:[email protected]?subject=Comments about FPCUnit Report">Graeme Geldenhuys</a>.<br/>
+    Licensed under the <a href="http://www.gnu.org/copyleft/gpl.html">GNU General Public License</a>.<br/>
+  </address>
+
+  <p align="right">
+    <a href="http://validator.w3.org/check?uri=referer"><img
+        src="http://www.w3.org/Icons/valid-html40" border="0"
+        alt="Valid HTML 4.0 Transitional" height="31" width="88"/></a>
+  </p>
 </body>
 </html>
 </xsl:template>
 
 
-<xsl:template match="TestResults">
+<xsl:template name="summary" match="TestResults">
   <xsl:variable name="runCount" select="NumberOfRunTests"/>
   <xsl:variable name="failureCount" select="NumberOfFailures"/>
   <xsl:variable name="errorCount" select="NumberOfErrors"/>
@@ -85,38 +66,24 @@ function clickSuite(e)
 
   <h3>Summary</h3>
   <!--  Summary Table -->
-	<table border="0" rules="none" width="100%">
-		<tr align="left" class="title">
-			<th width="45%" align="left">Name</th>
-			<th width="7%" align="left">Tests</th>
-			<th width="8%" align="left">Failures</th>
-			<th width="8%" align="left">Errors</th>
-			<th width="11%" align="left">Elapsed Time</th>
-			<th width="14%" align="left">Run Date</th>
-		</tr>
-  <xsl:choose>
-   <xsl:when test="$errorCount &gt; 0">
-    <tr class="error">
-      <td>Summary</td>
-      <td><xsl:value-of select="$runCount"/></td>
-      <td><xsl:value-of select="$failureCount"/></td>
-      <td><xsl:value-of select="$errorCount"/></td>
-      <td><xsl:value-of select="$elapsedTime"/></td>
-      <td><xsl:value-of select="$dateRan"/></td>
+  <table border="0" rules="none" width="100%">
+    <tr align="left" class="title">
+      <th width="45%" align="left">Name</th>
+      <th width="7%" align="left">Tests</th>
+      <th width="8%" align="left">Failures</th>
+      <th width="8%" align="left">Errors</th>
+      <th width="11%" align="left">Elapsed Time</th>
+      <th width="14%" align="left">Run Date</th>
     </tr>
-   </xsl:when>
-   <xsl:when test="$failureCount &gt; 0">
-    <tr class="failure">
-      <td>Summary</td>
-      <td><xsl:value-of select="$runCount"/></td>
-      <td><xsl:value-of select="$failureCount"/></td>
-      <td><xsl:value-of select="$errorCount"/></td>
-      <td><xsl:value-of select="$elapsedTime"/></td>
-      <td><xsl:value-of select="$dateRan"/></td>
-    </tr>
-   </xsl:when>
-   <xsl:otherwise>
     <tr class="success">
+      <!-- Set class attribute based on test results -->
+      <xsl:if test="$failureCount &gt; 0">
+        <xsl:attribute name="class">failure</xsl:attribute>
+      </xsl:if>
+      <xsl:if test="$errorCount &gt; 0">
+        <xsl:attribute name="class">error</xsl:attribute>
+      </xsl:if>
+
       <td>Summary</td>
       <td><xsl:value-of select="$runCount"/></td>
       <td><xsl:value-of select="$failureCount"/></td>
@@ -124,136 +91,357 @@ function clickSuite(e)
       <td><xsl:value-of select="$elapsedTime"/></td>
       <td><xsl:value-of select="$dateRan"/></td>
     </tr>
-   </xsl:otherwise>
-  </xsl:choose>
-	</table>
+  </table>
 
   <p>Note: <i>Failures</i> are anticipated and checked for with assertions. <i>Errors</i> are 
 unexpected results.</p>
-	<hr/>
+  <hr/>
+
+  <xsl:call-template name="test_listing"></xsl:call-template>
 
-  <xsl:call-template name="test_listing"/>
-  <xsl:call-template name="test_failures"/>
-  <xsl:call-template name="test_errors"/>
 </xsl:template>
 
 
-<xsl:template name="test_listing">
-  <div id="testlisting">
-  <a name="Test_Listing"/>
-	<h3>Test Listing</h3>
-	<p>
-		[<a href="#Summary">Summary</a>]
-		[<a href="#Test_Listing">Test Listing</a>]
-		[<a href="#Failures">Failures</a>]
-		[<a href="#Errors">Errors</a>]
-	</p>
-  <!--  Test Listing Table -->
-	<table border="0" rules="none" width="100%">
-		<tr align="left" class="title">
-			<td align="left">Name<br/><div class="testsuiteshowall">[show all]</div><div class="testsuitehideall">[hide all]</div></td>
-			<td width="150" align="right">Elapsed Time<br/>(hh:mm:ss.zzz)</td>
-		</tr>
-  </table>
-	<xsl:for-each select="TestListing/TestSuite">
-    <div class="testsuitelabel"><xsl:value-of select="@Name"/></div>
-    <div class="testcontent">
-      <table border="0" cellspacing="1" width="100%">
-        <xsl:for-each select="./Test">
-        <tr class="success">
-          <td><xsl:value-of select="@Name"/></td>
-          <td width="150" align="right"><xsl:value-of select="ElapsedTime"/></td>
+<!--
+**********************************************************************
+  Represents the test suites as a treeview
+**********************************************************************  
+-->
+  <xsl:template name="test_listing">
+    <div id="testlisting">
+      <a name="Test_Listing"/>
+      <h3>Test Listing</h3>
+      <!-- Treeview Start -->
+      <table border="0" cellspacing="0" cellpadding="0">
+        <tr>
+          <td>
+            <!-- Apply the template TestSuite starting with a depth in the tree of 1-->
+            <xsl:call-template name="test_suite">
+              <xsl:with-param name="depth" select="1"/>
+            </xsl:call-template>
+          </td>
         </tr>
-    	  </xsl:for-each>  <!-- Test -->
       </table>
-    </div>
-  </xsl:for-each> <!-- TestSuite -->
+      <!-- Treeview End -->
+    </div>  <!-- testlisting -->
+  </xsl:template>
 
-</div>  <!-- testlisting -->
-</xsl:template>
 
+<!--
+**********************************************************************
+  Creates a image and text representing a test suite.
+**********************************************************************  
+-->
+  <xsl:template name="test_suite" match="TestSuite">
+    <xsl:param name="depth"/>
+      <table border="0" cellspacing="0" cellpadding="0">
+        <tr>
+          <!-- If first level of depth, do not shift of $param-shift-width-->
+          <xsl:if test="$depth>1">
+            <!-- highlight the test results -->
+            <xsl:choose>
+              <xsl:when test="@NumberOfErrors &gt; 0">
+                <td width="{$param-shift-width}" class="error_">&#160;</td>
+              </xsl:when>
+              <xsl:when test="@NumberOfFailures &gt; 0">
+                <td width="{$param-shift-width}" class="failure_">&#160;</td>
+              </xsl:when>
+              <xsl:otherwise>
+                <td width="{$param-shift-width}">&#160;</td>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:if>
+          <td>
+            <a class="folder">
+              <xsl:attribute name="onclick">toggle(this)</xsl:attribute>
+              <!-- If the treeview is unfold, the image minus (-) is displayed-->
+              <xsl:if test="@expanded">
+                <xsl:if test="@expanded='true'">
+                  <img src="{$param-img-directory}minus.gif" alt=""/>
+                </xsl:if>
+                <xsl:if test="@expanded='false'">
+                  <img src="{$param-img-directory}plus.gif" alt=""/>
+                </xsl:if>
+              </xsl:if>
 
+              <xsl:if test="not(@expanded)">
+                <xsl:if test="$param-expand-treeview = 'true'">
+                  <img src="{$param-img-directory}minus.gif" alt=""/>
+                </xsl:if>
+                <xsl:if test="$param-expand-treeview = 'false' or not(@expanded)">
+                  <img src="{$param-img-directory}plus.gif" alt=""/>
+                </xsl:if>
+              </xsl:if>
+              <img src="{$param-img-directory}testsuite.gif" alt="">
+                <!-- if the attribut Name is present-->
+                <xsl:if test="@Name">
+                  <!-- if Netscape / Mozilla -->
+                  <xsl:if test="$param-is-netscape='true'">
+                    <xsl:attribute name="title"><xsl:value-of select="@Name"/></xsl:attribute>
+                  </xsl:if>
+                  <!-- if Internet Explorer -->
+                  <xsl:if test="$param-is-netscape='false'">
+                    <xsl:attribute name="alt"><xsl:value-of select="@Name"/></xsl:attribute>
+                  </xsl:if>
+                </xsl:if>
+              </img>
+              <!-- Tree node text can be decorated based on the test results -->
+              <xsl:choose>
+                <xsl:when test="@NumberOfErrors &gt; 0">
+                  <span class="node_error"><xsl:value-of select="@Name"/></span>
+                </xsl:when>
+                <xsl:when test="@NumberOfFailures &gt; 0">
+                  <span class="node_failure"><xsl:value-of select="@Name"/></span>
+                </xsl:when>
+                <xsl:otherwise>
+                  <span class="node_success"><xsl:value-of select="@Name"/></span>
+                </xsl:otherwise>
+              </xsl:choose>
+            </a>
 
-<xsl:template name="test_failures">
-  <div id="failures">
-  <a name="Failures"/>
-  <h3>Failures:</h3>
-	<p>
-		[<a href="#Summary">Summary</a>]
-		[<a href="#Test_Listing">Test Listing</a>]
-		[<a href="#Failures">Failures</a>]
-		[<a href="#Errors">Errors</a>]
-	</p>
-<xsl:for-each select="ListOfFailures/Failure">
-	<p class="backToTop">
-		[<a href="#Failures">Back to top</a>]
-	</p>
-  <table>
-    <!--  Error Table Body  -->
-  <TR>
-     <TD valign="top" class="title" width="300">Message:</TD>
-     <TD valign="top" class="resultmessage"><xsl:value-of select="Message"/></TD>
-  </TR>
-  <TR>
-    <TD valign="top" class="title">Exception Class:</TD>
-    <TD valign="top" class="resultmessage"><xsl:value-of select="ExceptionClass"/></TD>  
-  </TR>
-  <TR>
-     <TD valign="top" class="title">Exception Message:</TD>
-     <TD valign="top" class="resultmessage"><xsl:value-of select="ExceptionMessage"/></TD>
-  </TR>
-  </table>
-</xsl:for-each>
-</div>  <!-- failures -->
-</xsl:template>
+            <!-- Shall we expand all the leaves of the treeview? No by default -->
+            <div>
+              <xsl:if test="@expanded">
+                <xsl:if test="@expanded='true'">
+                  <xsl:attribute name="style">display:block;</xsl:attribute>
+                </xsl:if>
+                <!-- plus (+) otherwise-->
+                <xsl:if test="@expanded='false'">
+                  <xsl:attribute name="style">display:none;</xsl:attribute>
+                </xsl:if>
+              </xsl:if>
 
+              <xsl:if test="not(@expanded)">
+                <xsl:if test="$param-expand-treeview = 'true'">
+                  <xsl:attribute name="style">display:block;</xsl:attribute>
+                </xsl:if>
+                <xsl:if test="$param-expand-treeview = 'false'">
+                  <xsl:attribute name="style">display:none;</xsl:attribute>
+                </xsl:if>
+                <!-- Auto expand any nodes containing failures or errors -->  
+                <xsl:if test="($param-expand-treeview = 'false') and ($param-expand-depth = 0)">
+                  <xsl:if test="@NumberOfErrors &gt; 0">
+                    <xsl:attribute name="style">display:block;</xsl:attribute>
+                  </xsl:if>
+                  <xsl:if test="@NumberOfFailures &gt; 0">
+                    <xsl:attribute name="style">display:block;</xsl:attribute>
+                  </xsl:if>
+                </xsl:if>
+                <!-- Auto expand the first few nodes as defined by param-expand-depth -->  
+                <xsl:if test="($param-expand-treeview = 'false') and ($param-expand-depth &gt; 0)">
+                  <xsl:if test="($depth &lt; $param-expand-depth)">
+                    <xsl:attribute name="style">display:block;</xsl:attribute>
+                  </xsl:if>
+                </xsl:if>
+              </xsl:if>
+              <!-- Thanks to the magic of recursive calls, all the descendants of 
+                   the current folder are going to be built -->
+              <xsl:apply-templates name="test_suite">
+                <xsl:with-param name="depth" select="$depth+1"/>
+              </xsl:apply-templates>
+              <!-- print all the leaves of this folder-->
+              <xsl:apply-templates select="/Test"></xsl:apply-templates>
+            </div>
+          </td>
+        </tr>
+      </table>
+  </xsl:template>
 
 
-<xsl:template name="test_errors">
-<div id="errors">
-  <a name="Errors"/>
-  <h3>Errors</h3>
-	<p>
-		[<a href="#Summary">Summary</a>]
-		[<a href="#Test_Listing">Test Listing</a>]
-		[<a href="#Failures">Failures</a>]
-		[<a href="#Errors">Errors</a>]
-	</p>
-<xsl:for-each select="ListOfErrors/Error">
-	<p class="backToTop">
-		[<a href="#Errors">Back to top</a>]
-	</p>
-<table>
-  <!--  Error Table Body  -->
-<TR>
-   <TD valign="top" class="title" width="300">Message:</TD>
-   <TD valign="top" class="resultmessage"><xsl:value-of select="Message"/></TD>
-</TR>
-<TR>
-   <TD valign="top" class="title">Exception Class:</TD>
-   <TD valign="top" class="resultmessage"><xsl:value-of select="ExceptionClass"/></TD>
-</TR>
-<TR>
-   <TD valign="top" class="title">Exception Message:</TD>
-   <TD valign="top" class="resultmessage"><xsl:value-of select="ExceptionMessage"/></TD>
-</TR>
-<TR>
-   <TD valign="top" class="title">UnitName:</TD>
-   <TD valign="top" class="resultmessage"><xsl:value-of select="SourceUnitName"/></TD>
-</TR>
-<TR>
-   <TD valign="top" class="title">LineNumber:</TD>
-   <TD valign="top" class="resultmessage"><xsl:value-of select="LineNumber"/></TD>
-</TR>
-<TR>
-   <TD valign="top" class="title">Method Name:</TD>
-   <TD valign="top" class="resultmessage"><xsl:value-of select="FailedMethodName"/></TD>
-</TR>
-</table>
-</xsl:for-each>
-</div>  <!-- errors -->
-</xsl:template>
+<!--
+**********************************************************************
+  Represents the actual test.
+**********************************************************************  
+-->
+  <xsl:template match="Test">
+    <table border="0" cellspacing="1" cellpadding="0">
+      <tr>
+        <!-- highlight the test result -->
+        <xsl:choose>
+          <xsl:when test="@Result = 'Error'">
+            <td width="{$param-shift-width}" class="error_">&#160;</td>
+          </xsl:when>
+          <xsl:when test="@Result = 'Failed'">
+            <td width="{$param-shift-width}" class="failure_">&#160;</td>
+          </xsl:when>
+          <xsl:otherwise>
+            <td width="{$param-shift-width}">&#160;</td>
+          </xsl:otherwise>
+        </xsl:choose>
+        <td class="success">
+          <!-- Set class attribute based on test result -->
+          <xsl:if test="@Result = 'Error'">
+            <xsl:attribute name="class">error</xsl:attribute>
+          </xsl:if>
+          <xsl:if test="@Result = 'Failed'">
+            <xsl:attribute name="class">failure</xsl:attribute>
+          </xsl:if>
+          <a class="leaf">
+            <xsl:attribute name="onclick">toggle(this)</xsl:attribute>
+            <!-- if it is the last leaf, print a different image for the link to the folder-->
+            <xsl:choose>
+              <xsl:when test="($param-expand-errors-and-failures='false') and ((@Result = 'Error') or (@Result = 'Failed'))">
+                <img src="{$param-img-directory}plus.gif" alt=""/>
+              </xsl:when>
+              <xsl:when test="($param-expand-errors-and-failures='true') and ((@Result = 'Error') or (@Result = 'Failed'))">
+                <img src="{$param-img-directory}minus.gif" alt=""/>
+              </xsl:when>
+              <xsl:when test="position()=last()">
+                <img src="{$param-img-directory}lastlink.gif" alt=""/>
+              </xsl:when>
+              <xsl:otherwise>
+                <img src="{$param-img-directory}link.gif" alt=""/>
+              </xsl:otherwise>
+            </xsl:choose>
+
+            <img src="{$param-img-directory}testcase.gif" alt="">
+              <!-- if the attribut alt is present-->
+              <xsl:if test="@ElapsedTime">
+                <!-- if Netscape / Mozilla -->
+                <xsl:if test="$param-is-netscape='true'">
+                  <xsl:attribute name="title"><xsl:value-of select="@ElapsedTime"/></xsl:attribute>
+                </xsl:if>
+                <!-- if Internet Explorer -->
+                <xsl:if test="$param-is-netscape='false'">
+                  <xsl:attribute name="alt"><xsl:value-of select="@ElapsedTime"/></xsl:attribute>
+                </xsl:if>
+              </xsl:if>
+            </img>
+
+            <!-- Text background can be highlighted based on the test results -->
+<!--            <xsl:choose>
+              <xsl:when test="@Result = 'Error'">
+                <span class="node_error"><xsl:value-of select="@Name"/></span>
+              </xsl:when>
+              <xsl:when test="@Result = 'Failed'">
+                <span class="node_failure"><xsl:value-of select="@Name"/></span>
+              </xsl:when>
+              <xsl:otherwise>
+                <span class="node_success"><xsl:value-of select="@Name"/></span>
+              </xsl:otherwise>
+            </xsl:choose>
+-->
+            <span class="node_success"><xsl:value-of select="@Name"/></span>
+          </a>
+          <!-- Show test result if they exist -->
+          <xsl:choose>
+            <xsl:when test="@Result = 'Error'">
+              <xsl:call-template name="error_results"></xsl:call-template>
+            </xsl:when>
+            <xsl:when test="@Result = 'Failed'">
+              <xsl:call-template name="failed_results"></xsl:call-template>
+            </xsl:when>
+          </xsl:choose>
+
+        </td>
+        <td width="150" align="right" class="success">
+          <!-- Set class attribute based on test result -->
+          <xsl:if test="@Result = 'Error'">
+            <xsl:attribute name="class">error</xsl:attribute>
+          </xsl:if>
+          <xsl:if test="@Result = 'Failed'">
+            <xsl:attribute name="class">failure</xsl:attribute>
+          </xsl:if>
+
+          <xsl:value-of select="@ElapsedTime"/>
+        </td>
+      </tr>
+    </table>
+  </xsl:template>
+
+
+  <xsl:template name="error_results">
+    <div style="display:none">
+      <xsl:if test="$param-expand-errors-and-failures='true'">
+        <xsl:attribute name="style">display:block;</xsl:attribute>
+      </xsl:if>
+
+      <table border="0">
+        <!--  Error Table Body  -->
+        <TR>
+          <td width="{$param-shift-width}">&#160;</td>
+          <TD width="200" valign="top" class="title">Message:</TD>
+          <TD valign="top" class="resultmessage"><xsl:value-of select="Message"/></TD>
+        </TR>
+        <TR>
+          <td width="{$param-shift-width}">&#160;</td>
+          <TD valign="top" class="title">Exception Class:</TD>
+          <TD valign="top" class="resultmessage"><xsl:value-of select="ExceptionClass"/></TD>
+        </TR>
+        <TR>
+          <td width="{$param-shift-width}">&#160;</td>
+          <TD valign="top" class="title">Exception Message:</TD>
+          <TD valign="top" class="resultmessage"><xsl:value-of select="ExceptionMessage"/></TD>
+        </TR>
+        <TR>
+          <td width="{$param-shift-width}">&#160;</td>
+          <TD valign="top" class="title">UnitName:</TD>
+          <TD valign="top" class="resultmessage"><xsl:value-of select="SourceUnitName"/></TD>
+        </TR>
+        <TR>
+          <td width="{$param-shift-width}">&#160;</td>
+          <TD valign="top" class="title">LineNumber:</TD>
+          <TD valign="top" class="resultmessage"><xsl:value-of select="LineNumber"/></TD>
+        </TR>
+        <TR>
+          <td width="{$param-shift-width}">&#160;</td>
+          <TD valign="top" class="title">Method Name:</TD>
+          <TD valign="top" class="resultmessage"><xsl:value-of select="FailedMethodName"/></TD>
+        </TR>
+      </table>
+    </div>
+  </xsl:template>
+
+
+  <xsl:template name="failed_results">
+    <div style="display:none">
+      <xsl:if test="$param-expand-errors-and-failures='true'">
+        <xsl:attribute name="style">display:block;</xsl:attribute>
+      </xsl:if>
+
+      <table border="0">
+        <!--  Error Table Body  -->
+        <tr>
+          <td width="{$param-shift-width}">&#160;</td>
+          <td width="200" valign="top" class="title">Message:</td>
+          <td valign="top" class="resultmessage"><xsl:value-of select="Message"/></td>
+        </tr>
+        <tr>
+          <td width="{$param-shift-width}">&#160;</td>
+          <td valign="top" class="title">Exception Class:</td>
+          <td valign="top" class="resultmessage"><xsl:value-of select="ExceptionClass"/></td>
+        </tr>
+        <tr>
+          <td width="{$param-shift-width}">&#160;</td>
+          <td valign="top" class="title">Exception Message:</td>
+          <td valign="top" class="resultmessage"><xsl:value-of select="ExceptionMessage"/></td>
+        </tr>
+      </table>
+    </div>
+  </xsl:template>
 
 
+  <xsl:template name="replace-string">
+    <xsl:param name="text"/>
+    <xsl:param name="from"/>
+    <xsl:param name="to"/>
+    <xsl:choose>
+      <xsl:when test="contains($text, $from)">
+        <xsl:variable name="before" select="substring-before($text, $from)"/>
+        <xsl:variable name="after" select="substring-after($text, $from)"/>
+        <xsl:variable name="prefix" select="concat($before, $to)"/>
+        <xsl:value-of select="$before"/>
+        <xsl:value-of select="$to"/>
+        <xsl:call-template name="replace-string">
+          <xsl:with-param name="text" select="$after"/>
+          <xsl:with-param name="from" select="$from"/>
+          <xsl:with-param name="to" select="$to"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$text"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
 
 </xsl:stylesheet>

BIN
packages/fcl-fpcunit/src/example_xsl/images/lastlink.gif


BIN
packages/fcl-fpcunit/src/example_xsl/images/link.gif


BIN
packages/fcl-fpcunit/src/example_xsl/images/minus.gif


BIN
packages/fcl-fpcunit/src/example_xsl/images/plus.gif


BIN
packages/fcl-fpcunit/src/example_xsl/images/testcase.gif


BIN
packages/fcl-fpcunit/src/example_xsl/images/testsuite.gif


+ 11 - 2
packages/fcl-fpcunit/src/example_xsl/readme.txt

@@ -2,14 +2,23 @@
    Sample XSLT processing
    ----------------------
 
-The fpcunit.xsl and fpcunit.css demonstrates how I create a Results HTML
+The fpcunit.xsl demonstrates how I create a Results HTML
 page from the XML generated by my unit tests.
 
 I use the following command line application under Linux to process the XML
-file.
+file. This is much faster that letting the web browser do it on the fly.
 
   xsltproc -o index.html fpcunit.xsl results.xml
 
+For an example of the generated HTML results have a look at:
+  http://opensoft.homeip.net/tiopf/fpcunit/index.html
+  
+  
+Configuration Options:
+----------------------
+The new fpcunit.xsl file has many configuration option. Have a look at
+the beginning of the fpcunit.xsl file. All options are listed and
+has associated comments.
 
 Graeme Geldenhuys.
 [email protected]

+ 174 - 0
packages/fcl-fpcunit/src/example_xsl/scripts/fpcunit.css

@@ -0,0 +1,174 @@
+body 
+{
+	font-family: verdana, arial, helvetica, Sans-Serif;
+	font-size: x-small;
+  background: #FFFFFF;
+}
+
+a 
+{
+  text-decoration: none;
+  background-color: Transparent
+}
+
+a:link 
+{	
+	color: #0033ff;
+}		
+
+a:visited 
+{	
+	color:	#003399;
+}			
+
+a:active, a:hover
+{	
+	color:#69c;
+}		
+	
+h2 
+{
+  padding: 4px 4px 4px 6px;
+  font-size: large;
+  border: 1px solid black;
+  font-weight: bold;
+  color: white;
+  background-color: #006699;
+}
+
+h3 
+{
+  padding: 4px 4px 4px 6px;
+  border: 1px solid #003399;
+  color: #FFFFFF;
+  background-color: #0099CC;
+  font-weight: normal;
+  font-size: medium;
+}
+
+table 
+{
+	padding:0px;
+	width: 100%;
+	margin-left: -2px;
+	margin-right: -2px;
+}
+
+th, td 
+{
+  padding: 2px 4px 2px 4px;
+  vertical-align: top;
+  font-size: x-small;
+}
+
+address 
+{
+	font-family: verdana, arial, helvetica, Sans-Serif;
+	font-size: 8pt;
+  font-style: normal; 
+  text-align: right;
+}
+
+.title 
+{
+	background-color: #bbb; 
+  color: white;	
+}
+
+.resultmessage
+{
+	background-color: #D3E4FF; 
+}
+
+.success 
+{
+	background-color: lightgreen;
+	color: black;
+}
+
+.node_success
+{
+  cursor:pointer; cursor:hand;   /* cross browser hack */
+}
+
+.failure 
+{
+	background-color: #C100C1;
+	color: black;
+}
+
+.node_failure
+{
+  color: #C100C1;
+  cursor:pointer; cursor:hand;   /* cross browser hack */
+}
+
+.notrun 
+{
+	background-color: yellow;
+	color: black;
+}
+
+.error 
+{
+	background-color: red;
+	color: black;
+	font-size: 9pt;
+}
+
+.node_error
+{
+  color: red;
+  cursor:pointer; cursor:hand;   /* cross browser hack */
+}
+
+.right
+{
+	font-size: 8pt;
+	text-align: right;
+}
+
+.backToTop
+{
+  text-align: right;
+}
+
+.testsuitelabel
+{
+	background-color: cadetblue;
+	color: black;
+  cursor:pointer; cursor:hand;   /* cross browser hack */
+	padding-top: 3px;
+	border: 1px solid #F5F5F5;
+}
+
+.testsuiteshowall
+{
+  display:inline;
+  cursor:pointer; cursor:hand;   /* cross browser hack */
+}
+
+.testsuitehideall
+{
+  display:inline;
+  cursor:pointer; cursor:hand;   /* cross browser hack */
+}
+
+div.testcontent {
+	margin-left: 8px;
+}
+
+/* Customize the "folder" style here */
+a.folder {  font-family: arial;
+            font-size: 9pt;
+            padding:0px;
+            font-family : cursive;
+}
+
+/* Customize the "leaf" style here */
+a.leaf {
+	font-family: cursive;
+	padding:0px;
+	cursor:pointer; cursor:hand;   /* cross browser hack */
+	font-size : 9pt;                             
+}

+ 379 - 0
packages/fcl-fpcunit/src/example_xsl/scripts/treeview.js

@@ -0,0 +1,379 @@
+// ---------------------------------------------------------------------------
+// --- Name:    Easy DHTML Treeview                                         --
+// --- Original idea by : D.D. de Kerf                  --
+// --- Updated by Jean-Michel Garnier, [email protected]                   --
+// ---------------------------------------------------------------------------
+
+/*****************************************************************************
+Name : toggle
+Parameters :  node , DOM element (<a> tag)
+Description :     Description, collapse or unfold a branch
+Author : Jean-Michel Garnier /  D.D. de Kerf
+*****************************************************************************/
+
+function toggle(node) {
+    // Get the next tag (read the HTML source)
+	var nextDIV = node.nextSibling;
+
+	// find the next DIV
+	while(nextDIV.nodeName != "DIV") {
+		nextDIV = nextDIV.nextSibling;
+	}
+
+	// Unfold the branch if it isn't visible
+	if (nextDIV.style.display == 'none') {
+
+		// Change the image (if there is an image)
+		if (node.childNodes.length > 0) {
+
+			if (node.childNodes.item(0).nodeName == "IMG") {
+				node.childNodes.item(0).src = getImgDirectory(node.childNodes.item(0).src) + "minus.gif";
+			}
+		}
+
+		nextDIV.style.display = 'block';
+	}
+	// Collapse the branch if it IS visible
+	else {
+
+		// Change the image (if there is an image)
+		if (node.childNodes.length > 0) {
+			if (node.childNodes.item(0).nodeName == "IMG") {
+  				node.childNodes.item(0).src = getImgDirectory(node.childNodes.item(0).src) + "plus.gif";
+			}
+		}
+		nextDIV.style.display = 'none';
+	}
+}
+
+/*****************************************************************************
+Name : toggle2
+Parameters :  node DOM element (<a> tag), folderCode String
+Description :    if you use the "code" attribute in a folder element, toggle2 is called
+instead of toggle. The consequence is that you MUST implement a selectFolder function in your page.
+Author : Jean-Michel Garnier
+*****************************************************************************/
+function toggle2(node, folderCode) {
+    toggle(node);
+    selectFolder(folderCode);
+}
+
+/*****************************************************************************
+Name : getImgDirectory
+Parameters : Image source path
+Return : Image source Directory
+Author : Jean-Michel Garnier
+*****************************************************************************/
+
+function getImgDirectory(source) {
+    return source.substring(0, source.lastIndexOf('/') + 1);
+}
+
+/************************************
+************* IMPORTANT *************
+*************************************
+
+The functions above are NOT used by the DHTML treeview. Netherless, have a look bc some be useful if you
+need to make XSLT on the client (since IE 5.5 and soon Mozilla !)
+
+*/
+
+
+/*****************************************************************************
+Name : stringExtract
+Parameters :
+- st String input string, contains n separators
+- position int, from 0 to n, position of the token wanted
+- separator char, separator between token
+
+Return : the token at the position wanted if it exists
+
+Description : Equivalent to class java.util.StringTokenizer
+Example -> stringExtract("A; B; C", 0, ";") = "A"
+
+Author : Jean-Michel Garnier
+*****************************************************************************/
+
+function stringExtract( st, position, separator ) {
+	var array;
+	var result = new String('');
+	var s = new String(st);
+	if (s != '' ) {
+		array = s.split( separator);
+		// @TODO, add a control on position value ...
+		result = array[position];
+	}
+	return result;
+}
+
+/*****************************************************************************
+Name : jsTrim
+Parameters : value, String
+Return : the same String, with space characters removed
+Description : equivalent to trim function
+Author : Jean-Michel Garnier
+*****************************************************************************/
+
+function jsTrim(value) {
+    var result = "";
+    for (i=0; i < value.length; i++) {
+        if (value.charAt(i) != ' ') {
+            result += value.charAt(i);
+        }
+    }
+    return result;
+}
+
+/*****************************************************************************
+Name : findObj
+Parameters :
+- n String object's name
+- d Document document
+Return : a reference on the object if it exists
+Description : Search an object in a document from its name.
+Author : Macromedia
+*****************************************************************************/
+
+function findObj(n, d) {
+  var p, i, x;
+  if (!d)
+    d = document;
+  if ( (p=n.indexOf("?") )>0 && parent.frames.length ) {
+		d = parent.frames[n.substring(p+1)].document;
+		n = n.substring(0,p);
+  }
+  if (!(x=d[n])&& d.all )
+	x = d.all[n];
+  for (i=0; !x && i < d.forms.length; i++)
+	x = d.forms[i][n];
+  for (i=0; !x && d.layers && i<d.layers.length; i++)
+	x = findObj(n, d.layers[i].document);
+
+  return x;
+}
+
+/*****************************************************************************
+Name : isInSelectInput
+Parameters :
+- v String Option value
+- select_input input SELECT
+Return : true if the SELECT already value
+Author : Jean-Michel Garnier
+*****************************************************************************/
+
+function isInSelectInput(v, select_input) {
+	for(var i=0; i<select_input.options.length; i++) {
+		if (select_input.options[i].value == v) {
+			return true;
+		}
+	}
+	return false;
+}
+
+/*****************************************************************************
+Name : selectOption
+Parameters :
+- v_value String Option value
+- select_input SELECT
+Description : Select all options whose value
+Author : Jean-Michel Garnier
+*****************************************************************************/
+
+function selectOption(v_value, select_input) {
+	var i, nb_item;
+	nb_item = select_input.options.length;
+	for (i = 0; i < nb_item ; i++) {
+		if ( select_input.options[i].value == v_value )
+			select_input.options[i].selected = true;
+	}
+}
+
+/*****************************************************************************
+Name : selectRemoveSelectedOption
+Parameters : select_input SELECT
+Description : removes all the selected options
+Author : Jean-Michel Garnier
+*****************************************************************************/
+
+function selectRemoveSelectedOption(select_input) {
+    for(var i=0; i<select_input.options.length; i++) {
+        if ( select_input.options[i].selected ) {
+  			select_input.options[i] = null;
+   		}
+	}
+	select_input.selectedIndex = -1;
+}
+
+/*****************************************************************************
+Name : selectRemoveAll
+Parameters : select_input
+Description : This Function removes all options
+Author : Jean-Michel Garnier
+*****************************************************************************/
+
+function selectRemoveAll(select_input) {
+
+    var linesNumber = select_input.options.length;
+    for(i=0; i < linesNumber; i++) {
+		select_input.options[0] = null;
+    }
+	select_input.selectedIndex = -1;
+}
+
+/*****************************************************************************
+Name : buildXMLSource
+Parameters : xmlSource_name, String, can be a file name (.xml or .xslt) or
+a String containing the xml 
+!!!BE SURE xml and xlt are lowercase
+Return : a reference on a ActiveX Msxml2.FreeThreadedDOMDocument with the xml loaded
+Author : Jean-Michel Garnier
+*****************************************************************************/
+
+function buildXMLSource(xmlSource_name) {
+
+    var obj, file_extension;
+    obj = new ActiveXObject("Msxml2.FreeThreadedDOMDocument");
+    obj.async = false;
+    obj.resolveExternals = false;
+
+    file_extension = stringExtract(xmlSource_name, 1, ".");
+    // if there is a file extension, then load the file
+    if (file_extension == "xml" || file_extension == "xslt" ) {
+        obj.load(xmlSource_name);
+    }
+    else {
+        // else load the XML String
+        obj.loadXML(xmlSource_name);
+    }
+
+    return obj;
+}
+
+/*****************************************************************************
+Name : transform
+Parameters :
+- xmlSource Msxml2.FreeThreadedDOMDocument ActiveX XML
+- xsltSource Msxml2.FreeThreadedDOMDocument ActiveX XSLT
+Return : String with the result of the transformation (not an ActiveX object !)
+Description :
+
+Author : Jean-Michel Garnier
+*****************************************************************************/
+
+function transform(xmlSource, xsltSource) {
+
+    var xslt;
+    var xslProc, paramName, paramValue;
+
+    // Create XLST
+    xslt = new ActiveXObject("Msxml2.XSLTemplate");
+    xslt.stylesheet = xsltSource;
+
+    // Add parameters
+    xslProc = xslt.createProcessor();
+
+    xslProc.input = xmlSource;
+
+    // add parameters if present
+    if (arguments.length >2 && arguments.length % 2 == 0){
+        for (var i=0; i < Math.floor((arguments.length)/2)-1; i++){
+            paramName = arguments[2*i+2];
+            paramValue = arguments[2*i+3];
+            xslProc.addParameter(paramName, paramValue);
+        }
+    }
+
+    xslProc.transform();
+    return xslProc.output;
+}
+
+function BrowserDetectLite() {
+	var ua = navigator.userAgent.toLowerCase();
+
+	// browser name
+	this.isGecko     = (ua.indexOf('gecko') != -1);
+	this.isMozilla   = (this.isGecko && ua.indexOf("gecko/") + 14 == ua.length);
+	this.isNS        = ( (this.isGecko) ? (ua.indexOf('netscape') != -1) : ( (ua.indexOf('mozilla') != -1) && (ua.indexOf('spoofer') == -1) && (ua.indexOf('compatible') == -1) && (ua.indexOf('opera') == -1) && (ua.indexOf('webtv') == -1) && (ua.indexOf('hotjava') == -1) ) );
+	this.isIE        = ( (ua.indexOf("msie") != -1) && (ua.indexOf("opera") == -1) && (ua.indexOf("webtv") == -1) );
+	this.isOpera     = (ua.indexOf("opera") != -1);
+	this.isKonqueror = (ua.indexOf("konqueror") != -1);
+	this.isIcab      = (ua.indexOf("icab") != -1);
+	this.isAol       = (ua.indexOf("aol") != -1);
+	this.isWebtv     = (ua.indexOf("webtv") != -1);
+
+	// spoofing and compatible browsers
+	this.isIECompatible = ( (ua.indexOf("msie") != -1) && !this.isIE);
+	this.isNSCompatible = ( (ua.indexOf("mozilla") != -1) && !this.isNS && !this.isMozilla);
+
+	// browser version
+	this.versionMinor = parseFloat(navigator.appVersion);
+
+	// correct version number
+	if (this.isNS && this.isGecko) {
+		this.versionMinor = parseFloat( ua.substring( ua.lastIndexOf('/') + 1 ) );
+	}
+	else if (this.isIE && this.versionMinor >= 4) {
+		this.versionMinor = parseFloat( ua.substring( ua.indexOf('msie ') + 5 ) );
+	}
+	else if (this.isMozilla) {
+      this.versionMinor = parseFloat( ua.substring( ua.indexOf('rv:') + 3 ) );
+   }
+   else if (this.isOpera) {
+		if (ua.indexOf('opera/') != -1) {
+			this.versionMinor = parseFloat( ua.substring( ua.indexOf('opera/') + 6 ) );
+		}
+		else {
+			this.versionMinor = parseFloat( ua.substring( ua.indexOf('opera ') + 6 ) );
+		}
+	}
+	else if (this.isKonqueror) {
+		this.versionMinor = parseFloat( ua.substring( ua.indexOf('konqueror/') + 10 ) );
+	}
+	else if (this.isIcab) {
+		if (ua.indexOf('icab/') != -1) {
+			this.versionMinor = parseFloat( ua.substring( ua.indexOf('icab/') + 6 ) );
+		}
+		else {
+			this.versionMinor = parseFloat( ua.substring( ua.indexOf('icab ') + 6 ) );
+		}
+	}
+	else if (this.isWebtv) {
+		this.versionMinor = parseFloat( ua.substring( ua.indexOf('webtv/') + 6 ) );
+	}
+
+	this.versionMajor = parseInt(this.versionMinor);
+	this.geckoVersion = ( (this.isGecko) ? ua.substring( (ua.lastIndexOf('gecko/') + 6), (ua.lastIndexOf('gecko/') + 14) ) : -1 );
+
+	// dom support
+   this.isDOM1 = (document.getElementById);
+	this.isDOM2Event = (document.addEventListener && document.removeEventListener);
+
+   // css compatibility mode
+   this.mode = document.compatMode ? document.compatMode : 'BackCompat';
+
+	// platform
+	this.isWin   = (ua.indexOf('win') != -1);
+	this.isWin32 = (this.isWin && ( ua.indexOf('95') != -1 || ua.indexOf('98') != -1 || ua.indexOf('nt') != -1 || ua.indexOf('win32') != -1 || ua.indexOf('32bit') != -1 || ua.indexOf('xp') != -1) );
+	this.isMac   = (ua.indexOf('mac') != -1);
+	this.isUnix  = (ua.indexOf('unix') != -1 || ua.indexOf('linux') != -1 || ua.indexOf('sunos') != -1 || ua.indexOf('bsd') != -1 || ua.indexOf('x11') != -1)
+
+	// specific browser shortcuts
+	this.isNS4x = (this.isNS && this.versionMajor == 4);
+	this.isNS40x = (this.isNS4x && this.versionMinor < 4.5);
+	this.isNS47x = (this.isNS4x && this.versionMinor >= 4.7);
+	this.isNS4up = (this.isNS && this.versionMinor >= 4);
+	this.isNS6x = (this.isNS && this.versionMajor == 6);
+	this.isNS6up = (this.isNS && this.versionMajor >= 6);
+	this.isNS7x = (this.isNS && this.versionMajor == 7);
+	this.isNS7up = (this.isNS && this.versionMajor >= 7);
+
+	this.isIE4x = (this.isIE && this.versionMajor == 4);
+	this.isIE4up = (this.isIE && this.versionMajor >= 4);
+	this.isIE5x = (this.isIE && this.versionMajor == 5);
+	this.isIE55 = (this.isIE && this.versionMinor == 5.5);
+	this.isIE5up = (this.isIE && this.versionMajor >= 5);
+	this.isIE6x = (this.isIE && this.versionMajor == 6);
+	this.isIE6up = (this.isIE && this.versionMajor >= 6);
+
+	this.isIE4xMac = (this.isIE4x && this.isMac);
+}