<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
		xmlns:ut="http://www.o-xml.org/namespace/unit-test/"
		xmlns:o="http://www.o-xml.org/lang/">
  <xsl:param name="file" select="''"/><!-- name of the file that we're processing -->

  <xsl:output method="xml" indent="yes"/>
  
  <xsl:template match="/">
    <o:program xmlns:o="http://www.o-xml.org/lang/"
	       xmlns:ut="http://www.o-xml.org/namespace/unit-test/">

      <xsl:if test="$file"><o:import href="{$file}"/></xsl:if>
      <o:import href="unit-test.oml"/>
      <o:import href="lib/time.oml"/>

      <!-- named test definitions -->
      <xsl:apply-templates select="//ut:definition[@name]"/>

      <!-- define test types -->
      <xsl:apply-templates select="//ut:test"/>

      <o:type name="ut:TestSuite">
        <o:parent name="TestSuite"/>
        <o:variable name="tests" type="List" select="List()"/>

        <o:function name="ut:TestSuite">
          <o:do>
            <xsl:apply-templates select="//ut:dataset"/>
            <xsl:apply-templates select="//ut:test" mode="instantiate"/>
          </o:do>
        </o:function>

        <o:function name="run">
          <o:do>
            <o:for-each name="test" select="$tests">
              <o:do select="$this.test($test)"/>
            </o:for-each>
          </o:do>
        </o:function>

      </o:type>

      <o:variable name="suite" select="ut:TestSuite()"/>

      <o:do select="$suite.run()"/>

      <o:set now="time:Date()" xmlns:time="http://www.o-xml.org/lib/time/"/>
      <report>
        <!-- report info -->
        <created>
          <o:eval select="$now.format('dd/MM yyyy hh:mm:ss')"/>
        </created>
        <file><xsl:value-of select="$file"/></file>
        <!-- test report -->
        <tests>
          <o:eval select="$suite.report()"/>
        </tests>
        <!-- coverage report -->
        <coverage>
          <types>
            <total><xsl:value-of select="count(//o:type)"/></total>
            <tested>
              <xsl:value-of select="count(//o:type[.//ut:test])"/>
            </tested>
            <xsl:apply-templates select="//o:type" mode="coverage"/>
          </types>
          <functions>
            <total><xsl:value-of select="count(//o:function)"/></total>
            <tested>
              <xsl:value-of select="count(//o:function[.//ut:test])"/>
            </tested>
            <xsl:apply-templates select="//o:function" mode="coverage"/>
          </functions>
          <procedures>
            <total><xsl:value-of select="count(//o:procedure)"/></total>
            <tested>
              <xsl:value-of select="count(//o:procedure[.//ut:test])"/>
            </tested>
            <xsl:apply-templates select="//o:procedure" mode="coverage"/>
          </procedures>
        </coverage>
      </report>
    </o:program>
  </xsl:template>

  <xsl:template match="o:function[@name != parent::o:type/@name]" mode="name">
    <!-- type function (not constructor) -->
    <xsl:value-of select="parent::o:type/@name"/>
    <xsl:text>.</xsl:text>
    <xsl:value-of select="@name"/>
    <xsl:text>(</xsl:text>
    <xsl:apply-templates select="o:param" mode="name"/>
    <xsl:text>)</xsl:text>
  </xsl:template>

  <xsl:template match="o:procedure" mode="name">
    <xsl:value-of select="@name"/>
    <xsl:text>(</xsl:text>
    <xsl:apply-templates select="o:param" mode="name"/>
    <xsl:text>)</xsl:text>
  </xsl:template>

  <xsl:template match="o:param" mode="name">
    <xsl:value-of select="@type"/>
    <xsl:if test="not(@type)">Node</xsl:if>
    <xsl:text> </xsl:text>
    <xsl:value-of select="@name"/>
    <xsl:if test="following-sibling::o:param">, </xsl:if>
  </xsl:template>

  <xsl:template match="o:function" mode="name">
    <xsl:value-of select="@name"/>
    <xsl:text>(</xsl:text>
    <xsl:apply-templates select="o:param" mode="name"/>
    <xsl:text>)</xsl:text>
  </xsl:template>

  <xsl:template match="o:type" mode="coverage">
    <type>
      <name><xsl:value-of select="@name"/></name>
      <tests><xsl:value-of select="count(.//ut:test)"/></tests>
    </type>    
  </xsl:template>

  <xsl:template match="o:function" mode="coverage">
    <function>
      <name>
        <xsl:apply-templates select="." mode="name"/>
      </name>
      <tests><xsl:value-of select="count(.//ut:test)"/></tests>
    </function>
  </xsl:template>

  <xsl:template match="o:procedure" mode="coverage">
    <procedure>
      <name>
        <xsl:apply-templates select="." mode="name"/>
      </name>
      <tests><xsl:value-of select="count(.//ut:test)"/></tests>
    </procedure>
  </xsl:template>

  <xsl:template match="text()"/>

  <xsl:template match="ut:dataset">
    <!-- create the variable that represents the dataset -->
    <o:variable name="ut:{@name}">
      <xsl:copy-of select="node()"/>
    </o:variable>
  </xsl:template>

  <xsl:template match="ut:definition[@name]">
    <!-- create the function -->
    <o:function name="ut:{@name}">
      <o:param name="input"/>
      <o:do>
        <xsl:copy-of select="node()"/>
      </o:do>
    </o:function>
  </xsl:template>

  <xsl:template match="ut:test">
    <xsl:variable name="name">
      <xsl:value-of select="@name"/>
      <xsl:if test="not(@name)">Test<xsl:value-of select="position()"/></xsl:if>
    </xsl:variable>
    <o:type name="ut:{$name}">
      <o:parent name="Test"/>

      <o:function name="test">
        <o:param name="input"/>
        <o:do>
          <xsl:apply-templates select="ut:definition"/>
        </o:do>
      </o:function>

      <o:function name="ut:{$name}">
        <!-- constructor, takes input and expected results dataset -->
        <o:param name="input"/>
        <o:param name="result"/>
        <o:parent name="Test" select="Test('{$name}', $input, $result)"/>
        <o:do/>
      </o:function>
    </o:type>
  </xsl:template>

  <xsl:template match="ut:definition">
    <!-- anonymous test definition -->
    <xsl:copy-of select="node()"/>
  </xsl:template>

  <xsl:template match="ut:definition[@ref]">
    <!-- call the function with this name -->
    <o:return select="ut:{@ref}($input)"/>
  </xsl:template>

  <xsl:template match="ut:test" mode="instantiate">
    <!-- call the generated constructor with input/result -->
    <xsl:variable name="name">
      <xsl:value-of select="@name"/>
      <xsl:if test="not(@name)">Test<xsl:value-of select="position()"/></xsl:if>
    </xsl:variable>
    <o:variable name="input"/>
    <o:variable name="result"/>
    <xsl:apply-templates select="ut:input|ut:result"/>
    <!-- instantiate this Test -->
    <o:set test="ut:{$name}($input, $result)"/>
    <!-- set the available contextual information -->
    <xsl:if test="parent::o:function">
      <xsl:variable name="name">
        <xsl:apply-templates select="parent::o:function" mode="name"/>
      </xsl:variable>
      <o:do select="$test.function('{$name}')"/>
      <xsl:if test="parent::o:function/parent::o:type">
        <o:do select="$test.type('{parent::o:function/parent::o:type/@name}')"/>
      </xsl:if>
    </xsl:if>
    <xsl:if test="parent::o:type">
      <o:do select="$test.type('{parent::o:type/@name}')"/>
    </xsl:if>
    <xsl:if test="parent::o:procedure">
      <xsl:variable name="name">
        <xsl:apply-templates select="parent::o:procedure" mode="name"/>
      </xsl:variable>
      <o:do select="$test.procedure('{$name}')"/>
    </xsl:if>
    <o:do select="$tests.add($test)"/>
  </xsl:template>

  <xsl:template match="ut:input">
    <!-- anonymous input dataset -->
    <o:variable name="input">
      <xsl:copy-of select="node()"/>
    </o:variable>
  </xsl:template>

  <xsl:template match="ut:input[@ref]">
    <!-- dereference the variable with this name -->
    <o:variable name="input" select="$ut:{@ref}.copy()"/>
  </xsl:template>

  <xsl:template match="ut:result">
    <!-- anonymous result dataset -->
    <o:variable name="result">
      <xsl:copy-of select="node()"/>
    </o:variable>
  </xsl:template>

  <xsl:template match="ut:result[@ref]">
    <!-- dereference the variable with this name -->
    <o:variable name="result" select="$ut:{@ref}.copy()"/>
  </xsl:template>
  
</xsl:stylesheet>
