import Testing @testable import PureXML /// The XSLT evaluator is iterative, so template recursion is bounded by heap, not /// the native stack (#356). Deep recursion that would once have overflowed the /// stack now succeeds, even here where swift-testing runs on a small Task stack; /// only a runaway/infinite recursion (whose result tree would grow without bound) /// fails gracefully at the configurable `maxTemplateDepth`. @Suite("XSLT template recursion") struct XSLTRecursionTests { private let xsl = "xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"" /// A stylesheet whose named template recurses `count` times, driving the /// recursion from the passed parameter (a stand-in for `count(//x)`). private func recursiveStylesheet(_ count: Int) -> String { "" + "" + "" + "" + "" + "" + "" } @Test("deep recursion succeeds on a small stack instead of overflowing") func test_deepRecursionSucceeds() throws { // 5000 levels far exceeds what the recursive engine survived on a Task // stack (a few hundred); the iterative engine builds it on the heap. let out = try PureXML.XSLT.transform(stylesheet: recursiveStylesheet(5000), source: "") #expect(out.components(separatedBy: "").count - 1 == 4999) } @Test("a runaway recursion fails gracefully at the configured limit") func test_runawayFailsGracefully() { // A low limit stands in for an unbounded recursion: it trips the guard // cheaply and throws rather than building without bound. #expect(throws: PureXML.XSLT.XSLTError.recursionLimitExceeded(100)) { _ = try PureXML.XSLT.transform(stylesheet: recursiveStylesheet(5000), source: "", maxTemplateDepth: 100) } } @Test("recursion within the limit transforms normally") func test_withinLimitSucceeds() throws { let out = try PureXML.XSLT.transform(stylesheet: recursiveStylesheet(100), source: "") #expect(out.components(separatedBy: "").count - 1 == 99) } @Test("an identity transform of the deepest permitted source runs on a small stack") func test_identityOfMaxDepthSource() throws { let depth = 254 // the parser permits up to maxDepth (256) nesting. let src = String(repeating: "", count: depth) + "leaf" + String(repeating: "", count: depth) let sheet = "" + "" + "" let out = try PureXML.XSLT.transform(stylesheet: sheet, source: src) #expect(out.contains("leaf")) #expect(out.contains(String(repeating: "", count: depth))) } }