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/@static or //x:scenario/x:param/@static)
  • expect x:context to 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:stylesheet or xsl:transform). If the test uses x:call/@function with functions that don't declare any visibility, the test will issue an error like XTDE0041: 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:stylesheet or xsl:transform).
      • temporarily change their visibility using static parameters, if they are in an explicit package.
  • 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-options of fn: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:context is a single node, the root node of the x:context tree becomes the global context item.
  • If x:context is 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:call is preceded by x:context, the template (@template) or the function (@function) is invoked once for each item in x:context. Each item in x:context is the global context item of each invocation.
  • If x:call is not preceded by x: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:

  • On Saxon 9.9.1.6 or earlier, URI in $x:saxon-config may need to be absolute due to a Saxon bug.
  • When you use $x:saxon-config, the tested stylesheet can't call a function passed as a parameter. (example)

Code Coverage

Code Coverage does not support xsl:use-package. Stylesheets specified by xsl:use-package do not appear in the coverage report.

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.