External Transformation - xspec/xspec GitHub Wiki
XSpec v2.0 introduced a new, experimental feature that invokes the tested templates and functions indirectly via fn:transform() instead of by importing them directly. This feature is controlled by /x:description/@run-as. This table compares the options for this attribute:
@run-as="import" or Attribute Omitted |
@run-as="external" |
|
|---|---|---|
XSpec Imports Tested Stylesheet with xsl:import? |
Yes | No |
| How XSpec Invokes Tested Templates and Functions | Directly | Indirectly, via fn:transform() |
@run-as="external" is currently an experimental feature and subject to change. Any feedback is welcome.
@run-as="external" is only for XSLT and Schematron. The @run-as attribute is ignored in tests for XQuery.
Use Cases
@run-as="external" will help when you need to
- test an XSLT package
- test different scenarios with different global XSLT parameters (
//x:scenario/x:param) - test with a static parameter (
/x:description/x:param/@staticor//x:scenario/x:param/@static) - expect
x:contextto be the global context item - test a stylesheet containing
xsl:result-document(requires a Saxon bug fix) - specify a Saxon configuration per scenario (
$x:saxon-config) - test with
@default-mode
How to Use This Feature
Set /x:description/@run-as = external.
Examples can be found in test/external_*.xspec.
Caveats and Limitations
Do not think that @run-as="external" is universally better just because it's newer. You might find that some of your existing tests cannot use this option.
- You can't test components in a package unless they are exposed or are made accessible via another component.
- Note, in particular, that functions are not exposed by default even if they are in a classic stylesheet (
xsl:stylesheetorxsl:transform). If the test usesx:call/@functionwith functions that don't declare any visibility, the test will issue an error likeXTDE0041: Cannot invoke function foo#1 externally, because it is not public. - To test private components, you may want to
- wrap them in an explicit package and expose them using
xsl:expose, if they are in a classic stylesheet (xsl:stylesheetorxsl:transform). - temporarily change their visibility using static parameters, if they are in an explicit package.
- wrap them in an explicit package and expose them using
- Note, in particular, that functions are not exposed by default even if they are in a classic stylesheet (
- In XPath expressions in XSpec files (such as
x:expect/@select), you can't access variables, functions, keys, or accumulators defined in the tested stylesheet. - You can't override global variables defined in the tested stylesheet.
- Saxon 9.8.0.8 or later is required, because of a breaking change in
vendor-optionsoffn:transform().
Global Context Item
The global context item is a context item where global xsl:param and xsl:variable are evaluated.
When @run-as does not exist or it is @run-as="import", the global context item is always absent.
When @run-as="external", every XSpec scenario has a different global context item depending on how the tested component is invoked:
apply-templates invocation (x:context without x:call)
- If
x:contextis a single node, the root node of thex:contexttree becomes the global context item. - If
x:contextis not a single node, the global context item is absent.
call-template invocation (x:call[@template]) and call-function invocation (x:call[@function])
- If
x:callis preceded byx:context, the template (@template) or the function (@function) is invoked once for each item inx:context. Each item inx:contextis the global context item of each invocation. - If
x:callis not preceded byx:context, the global context item is absent.
XSpec Global Parameters, Scenario-level Parameters and Global Variables
XSpec global parameters (/x:description/x:param) and scenario-level parameters (//x:scenario/x:param)
They take effect in the tested stylesheet, overriding a global xsl:param element declared there. XSpec tests that use external transformations cannot override global xsl:variable elements in the tested stylesheet.
XSpec global variables (/x:description/x:variable)
They do not take effect in the tested stylesheet. They can be used only in XPath expressions in XSpec files (such as x:expect/@select).
Saxon Configuration
If you need to configure Saxon options for the external transform, you can use x:variable in your XSpec file to create a variable named $x:saxon-config. When the test invokes fn:transform, the input map that defines transformation options includes a vendor-options map entry whose key is QName('http://saxon.sf.net/', 'configuration') and whose value is your value of $x:saxon-config.
Note:
- When you use
$x:saxon-config, the tested stylesheet can't call a function passed as a parameter. (example) - Code Coverage reporting does not support the use of
$x:saxon-config. For more information, see "Code Coverage", next. - On Saxon 9.9.1.6 or earlier, URI in
$x:saxon-configmay need to be absolute due to a Saxon bug.
Code Coverage
Code Coverage does not support xsl:use-package. Stylesheets specified by xsl:use-package do not appear in the coverage report.
Code Coverage does not support test suites that define the Saxon configuration variable mentioned above, $x:saxon-config. If your desired Saxon configuration is the same for the entire test suite, either set the SAXON_CUSTOM_OPTIONS environment variable before invoking XSpec with xspec.bat or xspec.sh, or provide a value for the saxon.custom.options Ant property when invoking XSpec with Ant. You can use the -config option to point to a configuration file (potentially expressing multiple settings) or specify command-line options individually. For syntax, see the XSLT from the command line page in the Saxon documentation.
Effect of Default XSLT Mode
The @run-as attribute determines whether an XSLT stylesheet's @default-mode attribute takes effect during testing of template rules.
When @run-as does not exist or it is @run-as="import", x:context elements without a @mode attribute use the unnamed mode, even if the XSLT stylesheet specifies a default mode.
When @run-as="external", x:context elements without a @mode attribute use the mode specified in the stylesheet's @default-mode attribute.