hoxsl/test/transform/apply-gen.xspec

496 lines
17 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!--
Tests dynamic function application boilerplate generation
Copyright (C) 2014 LoVullo Associates, Inc.
This file is part of hoxsl.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<description xmlns="http://www.jenitennison.com/xslt/xspec"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://mikegerwitz.com/hoxsl/apply"
xmlns:foo="http://mikegerwitz.com/_junk"
stylesheet="apply-gen-test.xsl">
<variable name="args">
<foo:args>
<foo:arg1 />
<foo:arg2 />
<foo:arg3 />
<foo:arg4 />
<foo:arg5 />
<foo:arg6 />
<foo:arg7 />
<foo:arg8 />
</foo:args>
</variable>
<!-- while functions are only supported in version >=2, this could be run
on future versions, and we want to make sure that we don't break
anything -->
<scenario label="stylesheet version">
<scenario label="when provided">
<context>
<xsl:stylesheet version="7.05" />
</context>
<expect label="is copied to output"
test="xsl:stylesheet/@version = '7.05'" />
</scenario>
<scenario label="when empty">
<context>
<xsl:stylesheet />
</context>
<expect label="defaults to 2.0"
test="xsl:stylesheet/@version = '2.0'" />
</scenario>
</scenario>
<!-- source namespaces must be copied -->
<scenario label="source namespaces">
<context>
<xsl:stylesheet xmlns:foobar="baz">
<xsl:function name="foobar:baz">
<xsl:param name="foo" />
</xsl:function>
</xsl:stylesheet>
</context>
<expect label="are copied to destination"
test="exists( xsl:stylesheet/namespace::foobar )" />
</scenario>
<!-- basic case -->
<scenario label="given a unary function">
<context>
<xsl:stylesheet>
<xsl:function name="foo:bar">
<xsl:param name="foo" />
</xsl:function>
</xsl:stylesheet>
</context>
<expect label="generates a function of the same name"
test="xsl:stylesheet/xsl:function/@name = 'foo:bar'" />
<expect label="generates function of type `element()'"
test="xsl:stylesheet/xsl:function/@as = 'element()'" />
<!-- this function is used to generate an element for later
application -->
<expect label="generated function is nullary"
test="count( xsl:stylesheet/xsl:function/xsl:param ) = 0" />
<!-- the generated node is simply the same name and namespace as
the function -->
<expect label="generated function produces function reference
node"
test="exists( xsl:stylesheet/xsl:function/f:ref/foo:bar )" />
<!-- more detailed tests below -->
<expect label="generates application template"
test="xsl:stylesheet/xsl:template/@match
= 'f:ref[ foo:bar ]'" />
<expect label="generated template has mode f:apply"
test="xsl:stylesheet/xsl:template/@mode = 'f:apply'" />
</scenario>
<!-- we'll test the application directly; we won't parse the output,
since that is error-prone, fragile, and misses the poin -->
<scenario label="application templates">
<scenario label="of N-ary functions">
<context>
<xsl:stylesheet>
<xsl:function name="foo:bar">
<xsl:param name="a" />
<xsl:param name="b" as="xs:decimal" />
<xsl:param name="c" />
</xsl:function>
</xsl:stylesheet>
</context>
<expect label="yields N template parameters"
test="count( xsl:stylesheet/xsl:template/xsl:param )
= 3" />
<!-- important for application! -->
<expect label="template parameter names are sequential"
test="xsl:stylesheet/xsl:template/xsl:param
/@name = concat( 'arg', position() )" />
<expect label="mirrors params without @as"
test="not( xsl:stylesheet/xsl:template
/xsl:param[ 1 ]/@as )" />
<expect label="mirrors params with @as"
test="xsl:stylesheet/xsl:template
/xsl:param[ 2 ]/@as = 'xs:decimal'" />
</scenario>
</scenario>
<!-- if a function is nullary, then it is either a thunk or used for
its side-effects; we expect the user to take appropriate
caution -->
<scenario label="given a nullary function">
<context>
<xsl:stylesheet>
<xsl:function name="foo:bar">
<foo:thunk />
</xsl:function>
</xsl:stylesheet>
</context>
<!-- generating one would conflict, of couse -->
<expect label="does not generate a function"
test="not( xsl:stylesheet/xsl:function )" />
</scenario>
<!-- see SUT for rationale -->
<scenario label="given an arity-overloaded function">
<context>
<xsl:stylesheet>
<xsl:function name="foo:overloaded">
<xsl:param name="a" />
</xsl:function>
<xsl:function name="foo:overloaded">
<xsl:param name="a" />
<xsl:param name="b" />
</xsl:function>
</xsl:stylesheet>
</context>
<expect label="yields no functions of that name"
test="not( xsl:stylesheet/xsl:function[
@name='foo:overloaded' ] )" />
</scenario>
<scenario label="given a stylesheet">
<scenario label="with no function elements">
<context>
<xsl:stylesheet>
<xsl:template name="foo" />
<xsl:template name="bar" />
</xsl:stylesheet>
</context>
<expect label="produces an empty stylesheet"
context="xsl:stylesheet">
<xsl:stylesheet version="2.0" />
</expect>
</scenario>
</scenario>
<!-- xsl:transform is an alternative to xsl:stylesheet -->
<scenario label="given a xsl:template root note">
<context>
<xsl:transform />
</context>
<expect label="properly outputs stylesheet"
context="xsl:stylesheet">
<xsl:stylesheet version="2.0" />
</expect>
</scenario>
<!-- This actually tests the generated stylesheet to ensure proper
execution. We test by invoking the application template with
the result of the generated nullary. We expect that the
application template will apply the arguments to the proper
function and return the result of the expected type.
You can't see it here (check the imported stylesheet), but
`foo:apply-add-two' defines a return type, so we also ensure
that such type data are retained.
Exciting! -->
<scenario label="given a generated document">
<scenario label="with a processed function">
<!-- calling the generated nullary -->
<call function="foo:apply-add-two">
<param name="x" select="2" />
<param name="y" select="3" />
</call>
<expect label="applying template to nullary yields proper result"
test="$x:result = 5" />
</scenario>
<!-- ensure that multiple functions are processed -->
<scenario label="with multiple functions">
<call function="foo:apply-sub-two">
<param name="x" select="2" />
<param name="y" select="3" />
</call>
<expect label="applying template to nullary yields proper result"
test="$x:result = -1" />
</scenario>
<!-- note that this _does not_ test f:arity itself: it is intended
to test that the arity datum is properly generated -->
<scenario label="constructed dynamic function reference">
<variable name="two-result"
select="foo:sub-two()" />
<scenario label="is recognized as">
<call function="f:is-ref">
<param name="fnref" select="$two-result" />
</call>
<expect label="a dynamic function reference"
select="true()" />
</scenario>
<scenario label="target QName">
<call function="f:QName">
<param name="fnref" select="$two-result" />
</call>
<expect label="is the QName of the target function"
select="QName( 'http://mikegerwitz.com/_junk',
'sub-two' )" />
</scenario>
<scenario label="arity">
<call function="f:arity">
<param name="fnref" select="$two-result" />
</call>
<expect label="is the arity of target function"
select="2" />
</scenario>
</scenario>
<!-- more duplication...we support up to eight arguments, so we'll
have to test the generation of 1..7 (nullary is already
tested and 8 results in a full application) -->
<scenario label="with partial functions of 8-ary target, given">
<variable name="qname"
select="QName( 'http://mikegerwitz.com/_junk',
'foo:eight' )" />
<scenario label="one argument">
<call function="foo:eight">
<param name="arg1" select="$args/foo:arg1" />
</call>
<expect label="returns partially applied function"
test="f:is-partial( $x:result )" />
<expect label="references the target function"
test="f:QName( $x:result ) = $qname" />
<expect label="partially applies one argument"
test="f:arity( $x:result ) = 7" />
<variable name="result-args" as="item()+"
select="f:args( $x:result )" />
<expect label="argument is partially applied by reference"
test="$result-args[ 1 ] is $args/foo:arg1" />
</scenario>
<scenario label="two arguments">
<call function="foo:eight">
<param name="arg1" select="$args/foo:arg1" />
<param name="arg2" select="$args/foo:arg2" />
</call>
<expect label="returns partially applied function"
test="f:is-partial( $x:result )" />
<expect label="references the target function"
test="f:QName( $x:result ) = $qname" />
<expect label="partially applies one argument"
test="f:arity( $x:result ) = 6" />
<variable name="result-args" as="item()+"
select="f:args( $x:result )" />
<expect label="argument is partially applied by reference"
test="$result-args[ 1 ] is $args/foo:arg1
and $result-args[ 2 ] is $args/foo:arg2" />
</scenario>
<scenario label="three arguments">
<call function="foo:eight">
<param name="arg1" select="$args/foo:arg1" />
<param name="arg2" select="$args/foo:arg2" />
<param name="arg3" select="$args/foo:arg3" />
</call>
<expect label="returns partially applied function"
test="f:is-partial( $x:result )" />
<expect label="references the target function"
test="f:QName( $x:result ) = $qname" />
<expect label="partially applies one argument"
test="f:arity( $x:result ) = 5" />
<variable name="result-args" as="item()+"
select="f:args( $x:result )" />
<expect label="argument is partially applied by reference"
test="$result-args[ 1 ] is $args/foo:arg1
and $result-args[ 2 ] is $args/foo:arg2
and $result-args[ 3 ] is $args/foo:arg3" />
</scenario>
<scenario label="four arguments">
<call function="foo:eight">
<param name="arg1" select="$args/foo:arg1" />
<param name="arg2" select="$args/foo:arg2" />
<param name="arg3" select="$args/foo:arg3" />
<param name="arg4" select="$args/foo:arg4" />
</call>
<expect label="returns partially applied function"
test="f:is-partial( $x:result )" />
<expect label="references the target function"
test="f:QName( $x:result ) = $qname" />
<expect label="partially applies one argument"
test="f:arity( $x:result ) = 4" />
<variable name="result-args" as="item()+"
select="f:args( $x:result )" />
<expect label="argument is partially applied by reference"
test="$result-args[ 1 ] is $args/foo:arg1
and $result-args[ 2 ] is $args/foo:arg2
and $result-args[ 3 ] is $args/foo:arg3
and $result-args[ 4 ] is $args/foo:arg4" />
</scenario>
<scenario label="five arguments">
<call function="foo:eight">
<param name="arg1" select="$args/foo:arg1" />
<param name="arg2" select="$args/foo:arg2" />
<param name="arg3" select="$args/foo:arg3" />
<param name="arg4" select="$args/foo:arg4" />
<param name="arg5" select="$args/foo:arg5" />
</call>
<expect label="returns partially applied function"
test="f:is-partial( $x:result )" />
<expect label="references the target function"
test="f:QName( $x:result ) = $qname" />
<expect label="partially applies one argument"
test="f:arity( $x:result ) = 3" />
<variable name="result-args" as="item()+"
select="f:args( $x:result )" />
<expect label="argument is partially applied by reference"
test="$result-args[ 1 ] is $args/foo:arg1
and $result-args[ 2 ] is $args/foo:arg2
and $result-args[ 3 ] is $args/foo:arg3
and $result-args[ 4 ] is $args/foo:arg4
and $result-args[ 5 ] is $args/foo:arg5" />
</scenario>
<scenario label="six arguments">
<call function="foo:eight">
<param name="arg1" select="$args/foo:arg1" />
<param name="arg2" select="$args/foo:arg2" />
<param name="arg3" select="$args/foo:arg3" />
<param name="arg4" select="$args/foo:arg4" />
<param name="arg5" select="$args/foo:arg5" />
<param name="arg6" select="$args/foo:arg6" />
</call>
<expect label="returns partially applied function"
test="f:is-partial( $x:result )" />
<expect label="references the target function"
test="f:QName( $x:result ) = $qname" />
<expect label="partially applies one argument"
test="f:arity( $x:result ) = 2" />
<variable name="result-args" as="item()+"
select="f:args( $x:result )" />
<expect label="argument is partially applied by reference"
test="$result-args[ 1 ] is $args/foo:arg1
and $result-args[ 2 ] is $args/foo:arg2
and $result-args[ 3 ] is $args/foo:arg3
and $result-args[ 4 ] is $args/foo:arg4
and $result-args[ 5 ] is $args/foo:arg5
and $result-args[ 6 ] is $args/foo:arg6" />
</scenario>
<scenario label="seven arguments">
<call function="foo:eight">
<param name="arg1" select="$args/foo:arg1" />
<param name="arg2" select="$args/foo:arg2" />
<param name="arg3" select="$args/foo:arg3" />
<param name="arg4" select="$args/foo:arg4" />
<param name="arg5" select="$args/foo:arg5" />
<param name="arg6" select="$args/foo:arg6" />
<param name="arg7" select="$args/foo:arg7" />
</call>
<expect label="returns partially applied function"
test="f:is-partial( $x:result )" />
<expect label="references the target function"
test="f:QName( $x:result ) = $qname" />
<expect label="partially applies one argument"
test="f:arity( $x:result ) = 1" />
<variable name="result-args" as="item()+"
select="f:args( $x:result )" />
<expect label="argument is partially applied by reference"
test="$result-args[ 1 ] is $args/foo:arg1
and $result-args[ 2 ] is $args/foo:arg2
and $result-args[ 3 ] is $args/foo:arg3
and $result-args[ 4 ] is $args/foo:arg4
and $result-args[ 5 ] is $args/foo:arg5
and $result-args[ 6 ] is $args/foo:arg6
and $result-args[ 7 ] is $args/foo:arg7" />
</scenario>
</scenario>
</scenario>
</description>