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") } }