Chapter 15
Testing and Debugging
1) Using xsl:message Effectively
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   
<!-- ... -->
   
<xsl:template match="someElement[someChild = 'someValue']">
  <xsl:message>Matched someElement[someChild = 'someValue']</xsl:message>
   
  <!-- ... -->
   
</xsl:template>
   
</xsl:stylesheet>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   
<xsl:template match="someElement[someChild = 'someValue']">
  <xsl:param name="myParam"/>
  <!-- This is not an effective debugging technique. If you run a test and see
       nothing it might be because the template was never matched or it might be
       because it was matched with $myParam empty -->
  <xsl:message><xsl:value-of select="$myParam"/></xsl:message>
</xsl:template>
   
<xsl:template match="someElement[someChild = 'someOtherValue']">
  <xsl:param name="myParam"/>
  <!-- This is better -->
  <xsl:message>Matched someElement[someChild = 'someOtherValue']</xsl:message>
  <xsl:message>$myParam=[<xsl:value-of select="$myParam"/>]</xsl:message>
</xsl:template>
   
</xsl:stylesheet>
 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                         xmlns:dbg="http:www.ora.com/
XSLTCookbook/ns/debug">
   
<xsl:param name="dbg:debugOn" select="false(  )"/>
   
<xsl:template match="someElement[someChild = 'someValue']">
  <xsl:param name="myParam"/>
  <xsl:if test="$dbg:debugOn">
    <xsl:message>Matched someElement[someChild = 'someValue']</xsl:message>
    <xsl:message>$myParam=[<xsl:value-of select="$myParam"/>]</xsl:message>
  </xsl:if>
</xsl:template>
   
</xsl:stylesheet>
XSLT 2.0
<!-- Output the value of $foo and terminate if it is negative -->
<xsl:message select=" 'foo=', $foo " terminate="{ if ($foo lt 0) then 'yes' else 
'no'}/>
<xsl:message use-when="system-property('debug_on') = 'yes' ">
	<xsl:text>Debug mode is on!</xsl:text>
</xsl:message>
Discussion
XSLT 1.0
<xsl:if test="debugOn>
  <xsl:if test="insert some invariant test">
    <xsl:message terminate="yes">
       Message describing the violation or failure.
    </xsl:message>
  </xsl:if>
</xsl:if>
<xsl:template match="*">
     <xsl:comment>Generated by the wild card match</xsl:comment> 
 ...
</xsl:template>
   
...
   
<xsl:template match="*" mode="foo">
     <xsl:comment>Generated by the mode=foo wild card match</xsl:comment> 
 ...
</xsl:template>
2) Tracing the Flow of Your Stylesheet 
Through Its Input Document
XSLT 1.0
<!-- xtrace.xslt -->
   
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                              xmlns:dbg="http://www.ora.com/XSLTCookbook/ns/debug">
   
<xsl:param name="debugOn" select="false(  )"/>
   
<xsl:template match="node(  )" mode="dbg:trace" name="dbg:xtrace">
<xsl:param name="tag" select=" 'xtrace' "/>
<xsl:if test="$debugOn">
  <xsl:message>
       <xsl:value-of select="$tag"/>: <xsl:call-template name="dbg:expand-path"/> 
  </xsl:message>
</xsl:if>
</xsl:template> 
   
<!--Expand the xpath to the current node -->
<xsl:template name="dbg:expand-path">
  <xsl:apply-templates select="." mode="dbg:expand-path"/>
</xsl:template>
   
<!-- Root -->
<xsl:template match="/" mode="dbg:expand-path">
  <xsl:text>/</xsl:text>
</xsl:template> 
   
<!--Top level node -->
<xsl:template match="/*" mode="dbg:expand-path">
  <xsl:text>/</xsl:text><xsl:value-of select="name(  )"/>
</xsl:template> 
   
<!--Nodes with node parents -->
<xsl:template match="*/*" mode="dbg:expand-path">
  <xsl:apply-templates select=".." mode="dbg:expand-path"/>/<xsl:value-of 
select="name(  )"/>[<xsl:number/>]<xsl:text/>
</xsl:template> 
   
<!--Attribute nodes -->
<xsl:template match="@*" mode="dbg:expand-path">
  <xsl:apply-templates select=".." mode="dbg:expand-path"/>/@<xsl:value-of 
select="name(  )"/>
</xsl:template> 
   
<!-- Text nodes (normalized for clarity) -->
<xsl:template match="text(  )" mode="dbg:expand-path">normalized-text(<xsl:value-of 
select="normalize-space(.)"/>)</xsl:template> 
   
</xsl:stylesheet>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                                              xmlns:dbg="http://www.ora.com/
XSLTCookbook/ns/debug">
   
<xsl:include href="xtrace.xslt"/>
   
<xsl:template match="/ | node(  ) | @* | comment(  ) | processing-instruction(  )">
  <xsl:call-template name="dbg:trace"/>
  <xsl:copy>
    <xsl:apply-templates select="@* | node(  )"/>
  </xsl:copy>
</xsl:template>
   
</xsl:stylesheet>
<test foo="1">
  <someElement n="1"/>
  <someElement n="2">
    <someChild>someValue</someChild>
  </someElement>
  <someElement n="3">
    <someChild>someOtherValue</someChild>
  </someElement>
  <someElement n="4">
    <someChild>someValue</someChild>
  </someElement>
</test>
xtrace: /
xtrace: /test
xtrace: /test/@foo
xtrace: normalized-text(  )
xtrace: /test/someElement[1]
xtrace: /test/someElement[1]/@n
xtrace: normalized-text(  )
xtrace: /test/someElement[2]
xtrace: /test/someElement[2]/@n
xtrace: normalized-text(  )
xtrace: /test/someElement[2]/someChild[1]
xtrace: normalized-text(someValue)
xtrace: normalized-text(  )
xtrace: normalized-text(  )
xtrace: /test/someElement[3]
xtrace: /test/someElement[3]/@n
xtrace: normalized-text(  )
xtrace: /test/someElement[3]/someChild[1]
xtrace: normalized-text(someOtherValue)
xtrace: normalized-text(  )
xtrace: normalized-text(  )
xtrace: /test/someElement[4]
xtrace: /test/someElement[4]/@n
xtrace: normalized-text(  )
xtrace: /test/someElement[4]/someChild[1]
xtrace: normalized-text(someValue)
xtrace: normalized-text(  )
xtrace: normalized-text(  )
XSLT 2.0
Discussion
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:dbg="http://www.ora.com/XSLTCookbook/ns/debug">
   
  <xsl:include href="xtrace.xslt"/>
   
  <xsl:output method="text"/>
   
  <xsl:strip-space elements="*"/>
   
  <xsl:template match="/employee" priority="10">
    <xsl:apply-templates/>
    <xsl:call-template name="dbg:trace"/>
    <xsl:value-of select="@name"/>
    <xsl:text> is the head of the company. </xsl:text>
    <xsl:call-template name="reportsTo"/>
    <xsl:call-template name="HimHer"/>
    <xsl:text>. </xsl:text>
    <xsl:text>&#xa;&#xa;</xsl:text>
  </xsl:template>
   
  <xsl:template match="employee[employee]">
    <xsl:apply-templates/>
    <xsl:call-template name="dbg:trace"/>
    <xsl:value-of select="@name"/>
    <xsl:text> is a manager. </xsl:text>
    <xsl:call-template name="reportsTo"/>
    <xsl:call-template name="HimHer"/>
    <xsl:text>. </xsl:text>
    <xsl:text>&#xa;&#xa;</xsl:text>
  </xsl:template>
   
  <xsl:template match="employee">
    <xsl:call-template name="dbg:trace"/>
    <xsl:text>Nobody reports to </xsl:text>
    <xsl:value-of select="@name"/>
    <xsl:text>. &#xa;</xsl:text>
  </xsl:template>
   
<!-- Remainder elided ... -->
   
</xsl:stylesheet>
xtrace: /employee/employee[1]/employee[1]
xtrace: /employee/employee[1]/employee[2]/employee[1]
xtrace: /employee/employee[1]/employee[2]
xtrace: /employee/employee[1]
xtrace: /employee/employee[2]/employee[1]
xtrace: /employee/employee[2]/employee[2]/employee[1]
xtrace: /employee/employee[2]/employee[2]/employee[2]
xtrace: /employee/employee[2]/employee[2]/employee[3]
xtrace: /employee/employee[2]/employee[2]
xtrace: /employee/employee[2]
xtrace: /employee/employee[3]/employee[1]/employee[1]
xtrace: /employee/employee[3]/employee[1]/employee[2]
xtrace: /employee/employee[3]/employee[1]/employee[3]
xtrace: /employee/employee[3]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[1]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[2]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[2]/employee[2]
xtrace: /employee/employee[3]/employee[2]/employee[2]
xtrace: /employee/employee[3]/employee[2]/employee[3]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[3]
xtrace: /employee/employee[3]/employee[2]
xtrace: /employee/employee[3]
xtrace: /employee
xtrace: /employee
xtrace: /employee/employee[1]
xtrace: /employee/employee[1]/employee[1]
xtrace: /employee/employee[1]/employee[2]
xtrace: /employee/employee[1]/employee[2]/employee[1]
xtrace: /employee/employee[1]/employee[2]
xtrace: /employee/employee[1]
xtrace: /employee/employee[2]
xtrace: /employee/employee[2]/employee[1]
xtrace: /employee/employee[2]/employee[2]
xtrace: /employee/employee[2]/employee[2]/employee[1]
xtrace: /employee/employee[2]/employee[2]/employee[2]
xtrace: /employee/employee[2]/employee[2]/employee[3]
xtrace: /employee/employee[2]/employee[2]
xtrace: /employee/employee[2]
xtrace: /employee/employee[3]
xtrace: /employee/employee[3]/employee[1]
xtrace: /employee/employee[3]/employee[1]/employee[1]
xtrace: /employee/employee[3]/employee[1]/employee[2]
xtrace: /employee/employee[3]/employee[1]/employee[3]
xtrace: /employee/employee[3]/employee[1]
xtrace: /employee/employee[3]/employee[2]
xtrace: /employee/employee[3]/employee[2]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[1]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[2]
xtrace: /employee/employee[3]/employee[2]/employee[2]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[2]/employee[2]
xtrace: /employee/employee[3]/employee[2]/employee[2]
xtrace: /employee/employee[3]/employee[2]/employee[3]
xtrace: /employee/employee[3]/employee[2]/employee[3]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[3]
xtrace: /employee/employee[3]/employee[2]
xtrace: /employee/employee[3]
3) Automating the Insertion of Debug Output
<!--
   Trace utility, modifies a stylesheet to produce trace messages
   Version 0.2
   GPL (c) Oliver Becker, 2002-02-13
   obecker@informatik.hu-berlin.de
-->
   
<xsl:transform version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:trace="http://www.obqo.de/XSL/Trace"
  xmlns:alias="http://www.w3.org/TransformAlias"
  exclude-result-prefixes="alias">
  
  <xsl:namespace-alias stylesheet-prefix="alias" result-prefix="xsl" />
   
  <!-- <xsl:output indent="yes" /> -->
   
  <!-- XSLT root element -->
  <xsl:template match="xsl:stylesheet | xsl:transform">
    <xsl:copy>
      <!-- We need the trace namespace for names and modes -->
      <xsl:copy-of select="document('')/*/namespace::trace" />
      <!-- dito: perhaps a namespace was used only as attribute value -->
      <xsl:copy-of select="namespace::*|@*" />
      <xsl:apply-templates />
      <!-- append utility templates -->
      <xsl:copy-of 
           select="document('')/*/xsl:template
                                  [@mode='trace:getCurrent' or 
                                   @name='trace:getPath']" />
      <!-- compute the lowest priority and add a default template with 
           a lower priority for element nodes -->
      <xsl:variable name="priority" 
                    select="xsl:template/@priority
                            [not(. &gt; current(  )/xsl:template/@priority)]" />
      <xsl:variable name="newpri">
        <xsl:choose>
          <xsl:when test="$priority &lt; -1">
            <xsl:value-of select="$priority - 1" />
          </xsl:when>
          <!-- in case there's only a greater or no priority at all -->
          <xsl:otherwise>-2</xsl:otherwise> 
        </xsl:choose>
      </xsl:variable>
      <!-- copy the contents only -->
      <alias:template match="*" priority="{$newpri}">
        <xsl:copy-of select="document('')/*/xsl:template
                             [@name='trace:defaultRule']/node(  )" />
      </alias:template>
    </xsl:copy>
  </xsl:template>
   
  <!-- XSLT templates -->
  <xsl:template match="xsl:template">
    <xsl:copy>
      <xsl:copy-of select="@*" />
      <!-- first: copy parameters -->
      <xsl:apply-templates select="xsl:param" />
      <alias:param name="trace:callstack" />
      <xsl:choose>
        <xsl:when test="@name">
          <alias:variable name="trace:current"
                          select="concat($trace:callstack,'/{@name}')" />
        </xsl:when>
        <xsl:otherwise>
          <alias:variable name="trace:current"
                 select="concat($trace:callstack,
                         '/{count(preceding-sibling::xsl:template)+1}')" />
        </xsl:otherwise>
      </xsl:choose>
   
      <!-- emit a message -->
      <alias:message>
        <alias:call-template name="trace:getPath" />
        <alias:text>&#xA;   stack: </alias:text>
        <alias:value-of select="$trace:current" />
        <xsl:if test="@match or @mode">
          <alias:text> (</alias:text>
          <xsl:if test="@match">
            <alias:text>match="<xsl:value-of select="@match" />"</alias:text>
            <xsl:if test="@mode">
              <alias:text><xsl:text> </xsl:text></alias:text>
            </xsl:if>
          </xsl:if>
          <xsl:if test="@mode">
            <alias:text>mode="<xsl:value-of select="@mode" />"</alias:text>
          </xsl:if>
          <alias:text>)</alias:text>
        </xsl:if>
        <xsl:apply-templates select="xsl:param" mode="traceParams" />
      </alias:message>
   
      <!-- process children except parameters -->
      <xsl:apply-templates select="node(  )[not(self::xsl:param)]" />
    </xsl:copy>
  </xsl:template>
   
  <!-- add the callstack parameter for apply-templates and call-template -->
  <xsl:template match="xsl:apply-templates | xsl:call-template">
    <xsl:copy>
      <xsl:copy-of select="@*" />
      <alias:with-param name="trace:callstack" select="$trace:current" />
      <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>
   
  <!-- output parameter values -->
  <xsl:template match="xsl:param" mode="traceParams">
    <alias:text>&#xA;   param: name="<xsl:value-of select="@name" />" 
                        value="</alias:text>
    <alias:value-of select="${@name}" />" <alias:text />
    <!--
    <alias:copy-of select="${@name}" />" <alias:text />
    -->
  </xsl:template>
   
  <!-- output variable values -->
  <xsl:template match="xsl:variable">
    <xsl:copy>
      <xsl:copy-of select="@*" />
      <xsl:apply-templates />
    </xsl:copy>
    <xsl:if test="ancestor::xsl:template">
      <alias:message>   variable: name="<xsl:value-of select="@name" />" 
                        value="<alias:text />
      <alias:value-of select="${@name}" />" </alias:message>
    </xsl:if>
  </xsl:template>
   
  <!-- copy every unprocessed node -->
  <xsl:template match="*|@*">
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>
   
  <!-- *************************************************************** -->
  <!-- The following templates will be copied into the modified        -->
  <!-- stylesheet                                                      -->   
  <!-- *************************************************************** -->
   
  <!-- 
   | trace:getPath
   | compute the absolute path of the context node 
   +-->
  <xsl:template name="trace:getPath">
    <xsl:text>node: </xsl:text>
    <xsl:for-each select="ancestor::*">
      <xsl:value-of 
           select="concat('/', name(  ), '[', 
           count(preceding-sibling::*[name(  )=name(current(  ))])+1, ']')" />
    </xsl:for-each>      
    <xsl:apply-templates select="." mode="trace:getCurrent" />
  </xsl:template>
   
  <!-- 
   | trace:getCurrent
   | compute the last step of the location path, depending on the
   | node type
   +-->
  <xsl:template match="*" mode="trace:getCurrent">
    <xsl:value-of 
         select="concat('/', name(  ), '[', 
         count(preceding-sibling::*[name(  )=name(current(  ))])+1, ']')" />
  </xsl:template>
   
  <xsl:template match="@*" mode="trace:getCurrent">
    <xsl:value-of select="concat('/@', name(  ))" />
  </xsl:template>
   
  <xsl:template match="text(  )" mode="trace:getCurrent">
    <xsl:value-of 
         select="concat('/text(  )[', count(preceding-sibling::text(  ))+1,
                                                                   ']')" />
  </xsl:template>
   
  <xsl:template match="comment(  )" mode="trace:getCurrent">
    <xsl:value-of 
         select="concat('/comment(  )[', 
                        count(preceding-sibling::comment(  ))+1, ']')" />
  </xsl:template>
   
  <xsl:template match="processing-instruction(  )" mode="trace:getCurrent">
    <xsl:value-of 
         select="concat('/processing-instruction(  )[', 
         count(preceding-sibling::processing-instruction(  ))+1, ']')" />
  </xsl:template>
   
  <!-- 
   | trace:defaultRule
   | default rule with parameter passing 
   +-->
  <xsl:template name="trace:defaultRule">
    <xsl:param name="trace:callstack" />
    <xsl:message>
      <xsl:call-template name="trace:getPath" />
      <xsl:text>&#xA;   default rule applied</xsl:text>
    </xsl:message>
    <xsl:apply-templates>
      <xsl:with-param name="trace:callstack" select="$trace:callstack" />
    </xsl:apply-templates>
  </xsl:template>
   
</xsl:transform>
Discussion
node: /employee[1]
   stack: /1 (match="/employee")
node: /employee[1]/employee[1]
   stack: /1/2 (match="employee[employee]")
node: /employee[1]/employee[1]/employee[1]
   stack: /1/2/3 (match="employee")
node: /employee[1]/employee[1]/employee[2]
   stack: /1/2/2 (match="employee[employee]")
node: /employee[1]/employee[1]/employee[2]/employee[1]
   stack: /1/2/2/3 (match="employee")
node: /employee[1]/employee[1]/employee[2]
   stack: /1/2/2/reportsTo
node: /employee[1]/employee[1]/employee[2]
   stack: /1/2/2/HimHer
node: /employee[1]/employee[1]
   stack: /1/2/reportsTo
node: /employee[1]/employee[1]
   stack: /1/2/HimHer
node: /employee[1]/employee[2]
   stack: /1/2 (match="employee[employee]")
node: /employee[1]/employee[2]/employee[1]
   stack: /1/2/3 (match="employee")
node: /employee[1]/employee[2]/employee[2]
   stack: /1/2/2 (match="employee[employee]")
node: /employee[1]/employee[2]/employee[2]/employee[1]
   stack: /1/2/2/3 (match="employee")
node: /employee[1]/employee[2]/employee[2]/employee[2]
   stack: /1/2/2/3 (match="employee")
node: /employee[1]/employee[2]/employee[2]/employee[3]
   stack: /1/2/2/3 (match="employee")
node: /employee[1]/employee[2]/employee[2]
   stack: /1/2/2/reportsTo
node: /employee[1]/employee[2]/employee[2]
   stack: /1/2/2/HimHer
node: /employee[1]/employee[2]
   stack: /1/2/reportsTo
node: /employee[1]/employee[2]
   stack: /1/2/HimHer
node: /employee[1]/employee[3]
   stack: /1/2 (match="employee[employee]")
node: /employee[1]/employee[3]/employee[1]
   stack: /1/2/2 (match="employee[employee]")
node: /employee[1]/employee[3]/employee[1]/employee[1]
   stack: /1/2/2/3 (match="employee")
node: /employee[1]/employee[3]/employee[1]/employee[2]
   stack: /1/2/2/3 (match="employee")
node: /employee[1]/employee[3]/employee[1]/employee[3]
   stack: /1/2/2/3 (match="employee")
node: /employee[1]/employee[3]/employee[1]
   stack: /1/2/2/reportsTo
node: /employee[1]/employee[3]/employee[1]
   stack: /1/2/2/HimHer
node: /employee[1]/employee[3]/employee[2]
   stack: /1/2/2 (match="employee[employee]")
node: /employee[1]/employee[3]/employee[2]/employee[1]
   stack: /1/2/2/2 (match="employee[employee]")
node: /employee[1]/employee[3]/employee[2]/employee[1]/employee[1]
   stack: /1/2/2/2/3 (match="employee")
node: /employee[1]/employee[3]/employee[2]/employee[1]
   stack: /1/2/2/2/reportsTo
node: /employee[1]/employee[3]/employee[2]/employee[1]
   stack: /1/2/2/2/HimHer
node: /employee[1]/employee[3]/employee[2]/employee[2]
   stack: /1/2/2/2 (match="employee[employee]")
node: /employee[1]/employee[3]/employee[2]/employee[2]/employee[1]
   stack: /1/2/2/2/3 (match="employee")
node: /employee[1]/employee[3]/employee[2]/employee[2]/employee[2]
   stack: /1/2/2/2/3 (match="employee")
node: /employee[1]/employee[3]/employee[2]/employee[2]
   stack: /1/2/2/2/reportsTo
node: /employee[1]/employee[3]/employee[2]/employee[2]
   stack: /1/2/2/2/HimHer
node: /employee[1]/employee[3]/employee[2]/employee[3]
   stack: /1/2/2/2 (match="employee[employee]")
node: /employee[1]/employee[3]/employee[2]/employee[3]/employee[1]
   stack: /1/2/2/2/3 (match="employee")
node: /employee[1]/employee[3]/employee[2]/employee[3]
   stack: /1/2/2/2/reportsTo
node: /employee[1]/employee[3]/employee[2]/employee[3]
   stack: /1/2/2/2/HimHer
node: /employee[1]/employee[3]/employee[2]
   stack: /1/2/2/reportsTo
node: /employee[1]/employee[3]/employee[2]
   stack: /1/2/2/HimHer
node: /employee[1]/employee[3]
   stack: /1/2/reportsTo
node: /employee[1]/employee[3]
   stack: /1/2/HimHer
node: /employee[1]
   stack: /1/reportsTo
node: /employee[1]
   stack: /1/HimHer
node: [XPath to this node]
   stack: [call stack of the templates invoked]
   param: name="[parameter name]" value="[parameter value]"
   more parameters ...
   variable: name="[variable name]" value="[variable value]"
   more variables ...
4) Including Embedded Unit Test Data in 
Utility Stylesheets
<!-- math.max.xslt -->
   
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:math="http://www.exslt.org/math" exclude-result-prefixes="math"
xmlns:test="http://www.ora.com/XSLTCookbook/test" id="math:math.max">
   
<xsl:template name="math:max">
     <xsl:param name="nodes" select="/.."/>
     <xsl:param name="max"/>
  <xsl:variable name="count" select="count($nodes)"/>
  <xsl:variable name="aNode" select="$nodes[ceiling($count div 2)]"/>
  <xsl:choose>
    <xsl:when test="not($count)">
      <xsl:value-of select="number($max)"/>
    </xsl:when>
    <xsl:when test="number($aNode) != number($aNode)">
      <xsl:value-of select="number($aNode)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="math:max">
        <xsl:with-param name="nodes" select="$nodes[not(. &lt;= number($aNode))]"/>
        <xsl:with-param name="max">
          <xsl:choose>
            <xsl:when test="not($max) or $aNode > $max">
              <xsl:value-of select="$aNode"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="$max"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
   
<!-- TEST CODE: DO NOT REMOVE! -->
<xsl:template match="/xsl:stylesheet[@id='math:math.max'] | 
xsl:include[@href='math.
max.xslt'] " priority="-1000">
<xsl:message>
TESTING math.max
</xsl:message>
   
<xsl:for-each select="document('')/*/test:test">
     <xsl:variable name="ans">
          <xsl:call-template name="math:max">
               <xsl:with-param name="nodes" select="test:data"/>
          </xsl:call-template>
     </xsl:variable>
     <xsl:if test="$ans != @ans">
          <xsl:message>
               math:max TEST <xsl:value-of select="@num"/> FAILED [<xsl:value-of 
select="$ans"/>]
          </xsl:message>
     </xsl:if>
</xsl:for-each>
   
<!-- Test with Infinity -->
<xsl:variable name="ans1">
     <xsl:call-template name="math:max">
          <xsl:with-param name="nodes" 
select="document('')/*/test:test[@num=1]/test:
data"/>
          <xsl:with-param name="max" select="1 div 0"/>
     </xsl:call-template>
</xsl:variable>
<xsl:if test="$ans1 != Infinity">
     <xsl:message>
          math:max Infinity Test FAILED [<xsl:value-of select="$ans1"/>]
     </xsl:message>
</xsl:if>
   
<!-- Test with -Infinity -->
<xsl:variable name="ans2">
     <xsl:call-template name="math:max">
          <xsl:with-param name="nodes" 
select="document('')/*/test:test[@num=1]/test:
data"/>
          <xsl:with-param name="max" select="-1 div 0"/>
     </xsl:call-template>
</xsl:variable>
<xsl:if test="$ans2 != document('')/*/test:test[@num=1]/@ans">
     <xsl:message>
          math:max -Infinity Test FAILED [<xsl:value-of select="$ans2"/>]
     </xsl:message>
</xsl:if>
   
</xsl:template>
   
<test:test num="1" ans="9" xmlns="http://www.ora.com/XSLTCookbook/test">
     <data>9</data>
     <data>8</data>
     <data>7</data>
     <data>6</data>
     <data>5</data>
     <data>4</data>
     <data>3</data>
     <data>2</data>
     <data>1</data>
</test:test>
   
<test:test num="2" ans="1" xmlns="http://www.ora.com/XSLTCookbook/test">
     <data>1</data>
</test:test>
   
<test:test num="3" ans="1" xmlns="http://www.ora.com/XSLTCookbook/test">
     <data>-1</data>
     <data>1</data>
</test:test>
   
<test:test num="4" ans="0" xmlns="http://www.ora.com/XSLTCookbook/test">
     <data>0</data>
     <data>0</data>
</test:test>
   
<test:test num="5" ans="NaN" xmlns="http://www.ora.com/XSLTCookbook/test">
     <data>foo</data>
     <data>1</data>
</test:test>
   
<test:test num="6" ans="NaN" xmlns="http://www.ora.com/XSLTCookbook/test">
     <data>1</data>
     <data>foo</data>
</test:test>
   
<test:test num="7" ans="NaN" xmlns="http://www.ora.com/XSLTCookbook/test">
</test:test>
   
</xsl:stylesheet>
Discussion
<xsl:template match="/xsl:stylesheet[@id='math:math.max'] | 
    xsl:include[@href='math.max.xslt']">
<!-- math.xslt -->
   
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:math="http://exslt.org/math" 
     extension-element-prefixes="math" id="math:math">
   
<xsl:include href="math.abs.xslt"/>
<xsl:include href="math.constant.xslt"/>
<xsl:include href="math.exp.xslt"/>
<xsl:include href="math.highest.xslt"/>
<xsl:include href="math.log.xslt"/>
<xsl:include href="math.lowest.xslt"/>
<xsl:include href="math.max.xslt"/>
<xsl:include href="math.min.xslt"/>
<xsl:include href="math.power.xslt"/>
<xsl:include href="math.sqrt.xslt"/>
   
<!--TEST CODE -->
<xsl:template match="xsl:stylesheet[@id='math:math'] | xsl:include[@href='math.
xslt']">
   
<xsl:message>
TESTING math
</xsl:message>
   
     <xsl:for-each select="document('')/*/xsl:include">
          <xsl:apply-templates select="."/>
     </xsl:for-each>
</xsl:template>
   
<xsl:template match="xsl:include" priority="-10">
     <xsl:message>
     WARNING: <xsl:value-of select="@href"/> has no test code.
     </xsl:message>
</xsl:template>
   
</xsl:stylesheet>
<!-- math.max.test.xslt-->
   
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:math="http://www.exslt.org/math" exclude-result-prefixes="math"
xmlns:test="http://www.ora.com/XSLTCookbook/test">
   
<xsl:include href="../math/math.max.xslt"/>
   
<!-- TEST CODE: DO NOT REMOVE! -->
<xsl:template match="/ | xsl:include[@href='math.max.test.xslt']">
<xsl:message>
TESTING math.max
</xsl:message>
   
<xsl:for-each select="document('')/*/test:test">
     <xsl:variable name="ans">
          <xsl:call-template name="math:max">
               <xsl:with-param name="nodes" select="test:data"/>
          </xsl:call-template>
     </xsl:variable>
     <xsl:if test="$ans != @ans">
          <xsl:message>
               math:max TEST <xsl:value-of select="@num"/> FAILED [<xsl:value-of 
select="$ans"/>]
          </xsl:message>
     </xsl:if>
</xsl:for-each>
   
<!-- ... Same as math.max.xslt above ... -->
   
</xsl:stylesheet>
<!-- math.test.xslt -->
   
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:math="http://exslt.org/math" 
     extension-element-prefixes="math">
   
<xsl:include href="math.max.test.xslt"/>
<xsl:include href="math.min.test.xslt"/>
   
<!-- ... Same as math.xslt, above ... -->
   
</xsl:stylesheet>
5) Structuring Unit Tests
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:
math="http://www.exslt.org/math" exclude-result-prefixes="math test" xmlns:
test="http://www.ora.com/XSLTCookbook/test" id="math:math.lowest">
   
<xsl:import href="math.min.xslt"/>
 
<xsl:template name="math:lowest">
     <xsl:param name="nodes" select="/.."/>
   
     <xsl:variable name="min">
          <xsl:call-template name="math:min">
               <xsl:with-param name="nodes" select="$nodes"/>
          </xsl:call-template>
     </xsl:variable> 
     <xsl:choose>
          <xsl:when test="number($min) = $min">
               <xsl:copy-of select="$nodes[. = $min]"/>
          </xsl:when>
          <xsl:otherwise/>
     </xsl:choose>
</xsl:template>
 
  <!-- TEST CODE: DO NOT REMOVE! -->
  <xsl:template match="xsl:stylesheet[@id='math:math.lowest'] | 
      xsl:include[@href='math.lowest.xslt'] " xmlns:exsl="http://exslt.org/common">
    <xsl:message>
TESTING math.lowest
</xsl:message>
    <xsl:choose>
      <xsl:when test="function-available('exsl:node-set')">
        <xsl:for-each select="document('')/*/test:test">
          <xsl:variable name="ans">
            <xsl:call-template name="math:lowest">
              <xsl:with-param name="nodes" select="test:data"/>
            </xsl:call-template>
          </xsl:variable>
           <xsl:variable name="$ans-ns" select=" exsl:node-set($ans)"/>
          <xsl:if test="not($ans-ns/* != test:data[. = current(  )/@ans]) and 
               count($ans-ns/*) != count(test:data[. = current(  )/@ans])">
            <xsl:message>
                math:lowest TEST <xsl:value-of select="@num"/> FAILED 
                [<xsl:copy-of select="$ans-ns"/>] 
                [<xsl:copy-of select="test:data[. = current(  )/@ans]"/>]
          </xsl:message>
          </xsl:if>
        </xsl:for-each>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message>
          WARNING math.lowest test code requires exsl:node-set
          THIS VERSION=[<xsl:value-of select="system-property('xsl:version')"/>]
          VENDOR=[<xsl:value-of select="system-property('xsl:vendor')"/>]
          </xsl:message>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
   
  <test:test num="1" ans="1" xmlns="http://www.ora.com/XSLTCookbook/test">
    <data>9</data>
    <data>8</data>
    <data>7</data>
    <data>6</data>
    <data>5</data>
    <data>4</data>
    <data>3</data>
    <data>2</data>
    <data>1</data>
  </test:test>
   
 <!-- more tests here ... >
   
</xsl:stylesheet>
6) Testing Boundary and Error Conditions
*	Parameters receive values of the wrong type
<xsl:param name="param1">
   <xsl:message terminate="yes">
     $param1 has not been set.
   </xsl:message>
</xsl:param>
<xsl:param name="param1" select="1 div 0" />
   
  <xsl:if test="$param1 = 1 div 0">
    <xsl:message terminate="yes">
      $param1 has not been set, or has been set to Infinity, which is
      invalid.
    </xsl:message>
  </xsl:if>
 
 
 
 
16		Testing and Debugging
6) Testing Boundary and Error Conditions		17 of 17
16
	17
DRAFT	O'Reilly & Associates	1/17/2006
