import Testing
@testable import PureXML
/// `document()` resolves a relative URI reference against a base URI (XSLT 1.0
/// 12.1): the second argument's first node supplies the base for the two-argument
/// form, and a node's own document supplies it for the node-set form. A document
/// loaded from `dir/sub/inner.xml` lends its `dir/sub/` directory to references
/// taken relative to it.
@Suite("XSLT document() base URI")
struct XSLTDocumentBaseURITests {
private let xsl = "xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\""
/// A loader over a fixed in-memory directory tree, exact path to content.
private static let files = [
"dir/sub/inner.xml": "[sibling.xml]",
"dir/sub/target.xml": "TWO_ARG",
"dir/sub/sibling.xml": "NODE_SET",
]
private func run(_ select: String) throws -> String {
try PureXML.XSLT.transform(
stylesheet: """
""",
source: "",
documentLoader: { Self.files[$0] },
)
}
@Test("two-argument form resolves against the second argument's base URI")
func test_twoArgument() throws {
// `target.xml` resolves against `dir/sub/inner.xml`, reaching dir/sub/target.xml.
let out = try run("document('target.xml', document('dir/sub/inner.xml'))/hit")
#expect(out == "TWO_ARG")
}
@Test("node-set form resolves against the referencing node's own base URI")
func test_nodeSet() throws {
// The [ node lives in dir/sub/inner.xml, so its `sibling.xml` value
// resolves to dir/sub/sibling.xml.
let out = try run("document(document('dir/sub/inner.xml')/inner/ref)/sib")
#expect(out == "NODE_SET")
}
@Test("a fragment-selected node still carries its document's base URI")
func test_fragmentBase() throws {
// The fragment selection is a detached subtree, yet it must still resolve
// `target.xml` against dir/sub/inner.xml's directory.
let out = try run("document('target.xml', document('dir/sub/inner.xml#xpointer(/inner)'))/hit")
#expect(out == "TWO_ARG")
}
/// A string-form `document()` reference resolves against the base URI of the
/// stylesheet element holding it, which for a declaration in an included or
/// imported file is that file's URI, not the top stylesheet's (XSLT 1.0 12.1;
/// the semantics behind Apache Xalan impincl08).
@Test("a global document() in an included file resolves against the included file's base")
func test_includedGlobalBase() throws {
let files = [
"lib/inc.xsl": """
""",
"lib/data.xml": "INCLUDED",
]
let out = try PureXML.XSLT.transform(
stylesheet: """
""",
source: "",
documentLoader: { files[$0] },
)
// `data.xml` resolves against lib/inc.xsl, reaching lib/data.xml; without
// base provenance it would resolve against the top stylesheet and miss.
#expect(out == "INCLUDED")
}
/// The base URI must reach a `document()` call inside an `xsl:for-each` body,
/// which runs in a context derived from (not equal to) the enclosing
/// template's, when that template comes from an included file.
@Test("document() inside a for-each body in an included file keeps the file's base")
func test_forEachBodyBase() throws {
let files = [
"lib/inc.xsl": """
""",
"lib/data.xml": "INCLUDED",
]
let out = try PureXML.XSLT.transform(
stylesheet: """
""",
source: "] ",
documentLoader: { files[$0] },
)
#expect(out == "INCLUDED")
}
/// `document('')` in the top stylesheet is the stylesheet's own document and
/// must not start depending on the loader to serve the stylesheet's URL once
/// a non-empty base URI is supplied (a regression the base threading could
/// introduce, since the empty reference resolves to that base).
@Test("document('') uses the self document even with a non-empty base URI")
func test_emptyReferenceWithBaseURI() throws {
let out = try PureXML.XSLT.transform(
stylesheet: """
""",
source: "",
documentLoader: { _ in nil },
baseURI: "http://example.org/style.xsl",
)
#expect(out == "1")
}
}