import Testing
@testable import PureXML
/// The output-method behaviors of XSLT 1.0 section 16 added in the xalan
/// burn-down (#130): disable-output-escaping (including its 16.4 fragment
/// rule and the marker-safety gate), CDATA "]]>" splitting, the simplified
/// stylesheet syntax, the html default method with META injection, key()
/// inside document() trees, and the attribute-start following/preceding axes.
@Suite("XSLT output methods and runtime corners")
struct XSLTOutputMethodTests {
private let xsl = "xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\""
@Test("disable-output-escaping writes text through unescaped")
func test_disableOutputEscaping() throws {
let style = """
<P> </P>
"""
#expect(try PureXML.XSLT.transform(stylesheet: style, source: "") == "
")
}
@Test("disable-output-escaping does not survive a fragment round-trip (16.4)")
func test_rawTextFragmentRoundTrip() throws {
let style = """
a <b> c
"""
#expect(try PureXML.XSLT.transform(stylesheet: style, source: "") == "a <b> c")
}
@Test("disable-output-escaping is ignored for an attribute value (16.4)")
func test_disableOutputEscapingIgnoredOffTextNode() throws {
// 16.4: disabling escaping for a value used as an attribute (not a text
// node) is an error; the recovery ignores it, so the attribute escapes
// `<`/`>` while a sibling text node keeps it raw (Apache Xalan output75).
let body = ""
+ "<x>"
+ "<t>"
let style = """
\(body)
"""
#expect(try PureXML.XSLT.transform(stylesheet: style, source: "") == "")
}
@Test("Source text holding the private-use marker characters is untouched")
func test_markerCharactersInSource() throws {
let style = """
"""
let source = "a\u{E000}<kept>\u{E001}b"
#expect(try PureXML.XSLT.transform(stylesheet: style, source: source) == "a\u{E000}<kept>\u{E001}b")
}
@Test("cdata-section-elements split ]]> across section boundaries")
func test_cdataTerminatorSplitting() throws {
let style = """
]]>
"""
#expect(try PureXML.XSLT.transform(stylesheet: style, source: "") == "]]>")
}
@Test("A literal result element with xsl:version is a one-template stylesheet (2.3)")
func test_simplifiedSyntax() throws {
let style = """
value:
"""
let result = try PureXML.XSLT.transform(stylesheet: style, source: "7")
#expect(result.hasSuffix("value: 7"))
}
@Test("A null-namespace html root selects the html method and gains META (16.1/16.2)")
func test_htmlDefaultMethod() throws {
let style = """
t
"""
let result = try PureXML.XSLT.transform(stylesheet: style, source: "")
#expect(result == "
t
")
}
@Test("key() indexes the current node's own document, document() trees included")
func test_keyPerDocument() throws {
let style = """
"""
let result = try PureXML.XSLT.transform(
stylesheet: style,
source: "- local
",
documentLoader: { $0 == "other.xml" ? "- external
" : nil },
)
#expect(result == "external")
}
@Test("following and preceding work from attribute starts")
func test_axesFromAttributes() throws {
let style = """
"""
let source = ""
// Document order places the attribute after b and before b's children,
// so following includes inner and c; preceding holds a only (r is an
// ancestor).
#expect(try PureXML.XSLT.transform(stylesheet: style, source: source) == "inner c a
")
}
@Test("html boolean attributes minimize when the value repeats the name")
func test_htmlBooleanAttributes() throws {
let style = """
"""
#expect(try PureXML.XSLT.transform(stylesheet: style, source: "")
== "")
}
@Test("Stylesheet and source entities resolve through the document loader")
func test_loaderEntityResolution() throws {
let style = """
"""
let result = try PureXML.XSLT.transform(
stylesheet: style,
source: "",
documentLoader: { $0 == "ents.dtd" ? "" : nil },
)
#expect(result == "a\u{E6}b")
}
@Test("A comment ending in a hyphen is well-formed in html output too")
func test_htmlCommentHyphenSafety() throws {
// The html output method must also apply the section-16 comment recovery,
// so a trailing hyphen does not produce a malformed ``.
let style = """
x-
"""
#expect(try PureXML.XSLT.transform(stylesheet: style, source: "") == "")
}
}