Skip to content
David Maus M. A.

Running XSpec with Ant in a pre-push hook

David Maus, 8. Jun 2022

I recently joined the Text Encoding Initiative's TEI Stylesheets Task Force that is tasked with writing a processor of the TEI's schema language. I somehow started this workgroup with a snarky off-hand comment about the current implementation.

We decided to use Ant for automation and I was tasked to create an Ant task that executes all our XSpec tests. After some research and trial & error I came up with the following solution.

<project>
<fileset id="xspec-files" dir="Tests/xspec" includes="*.xspec"/>

<target name="test.xspec" description="Run all XSpec tests">
<pathconvert refid="xspec-files" property="xspec.files" pathsep=" "/>
<xslt style="Tests/xspec-runner.xslt" in="build.xml" out="Tests/xspec-runner.xml">
<param name="files" expression="${xspec.files}"/>
</xslt>
<subant antfile="xspec-runner.xml" buildpath="Tests" target="xspec-runner"/>
</target>

</project>

See, the problem is that XSpec provides an Ant build file with a target that executes a single XSpec tests but we want to execute all of our tests without listing them explicitely in the Ant build file. To achive this I use the pathconvert to convert the fileset into a list of the contained files separated by space and store it in a property. I pass this property as parameter to a transformation. This won't work when files or directories contain space characters but we can live with this limitation.

<xsl:stylesheet version="3.0" exclude-result-prefixes="xs xspec"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xspec="http://www.jenitennison.com/xslt/xspec">

<xsl:param name="files" as="xs:string" required="true"/>

<xsl:template match="/" as="element(project)">
<project>
<include file="../Lib/xspec/build.xml"/>
<target name="xspec-runner">
<xsl:for-each select="tokenize($files)">
<antcall target="xspec.xspec" inheritall="false">
<param name="xspec.xml" location="{.}"/>
<param name="test.type" value="{if (doc(.)/xspec:description/@stylesheet) then 't' else 's'}"/>
<param name="clean.output.dir" value="true"/>
</antcall>
</xsl:for-each>
</target>
</project>
</xsl:template>

</xsl:stylesheet>

This transformation creates an Ant build file that executes the XSpec target for each of our tests. Our main build file executes an Ant subprocess to run the tests.

Now we can run all XSpec tests from the commandline and add it in a Git pre-push hook.

#!/bin/sh

ant test.xspec