From f29deec723ece07915086b91d5aab23eadd5ec2f Mon Sep 17 00:00:00 2001 From: Mihaela Mihaljevic Date: Tue, 9 Jun 2026 18:50:54 +0200 Subject: [PATCH] test(dtd): verify parameter-entity and external-subset depth Add depth tests confirming the existing DTD machinery handles parameter entities within element-model and attribute-list declarations, deep (5-level) PE composition, and an external subset that itself loads a further external parameter entity through the resolver. The Tier-3 DTD PE / external-subset depth item was already implemented; these lock it to full depth. (The XML 1.0 PE-reference spacing rule, leading/trailing space on DTD-content inclusion, remains a known minor non-conformance.) Part of #105 (Tier 3) --- README.md | 4 +- Tests/DTDParameterEntityDepthTests.swift | 61 ++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 Tests/DTDParameterEntityDepthTests.swift diff --git a/README.md b/README.md index 704fa4a..bab0674 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ flowchart TB OutEnc["#106 output encodings: complete"]:::done StreamVal["#107 streaming validation: DTD, RELAX NG, XSD"]:::done MoreEnc["#108 encodings: ISO-2022-JP, EUC-TW"]:::done - Depth["Tier 2 conformance corpora; Tier 3 DTD PE depth (C14N 1.1, EXSLT done); frontier #104"]:::todo + Depth["Tier 3 done (C14N 1.1, EXSLT, DTD PE depth); Tier 2 conformance corpora; frontier #104"]:::todo Epic105 --> OutEnc OutEnc --> StreamVal StreamVal --> MoreEnc @@ -99,7 +99,7 @@ flowchart TB PureXML is a working, dependency-free XML library today: parse, emit, validate, query, and stream documents on macOS, Linux, Windows, and WASM. The test suite -currently runs **801 tests in 123 suites** (`swift test`). +currently runs **805 tests in 124 suites** (`swift test`). ### Shipped (libxml2-aligned surface) diff --git a/Tests/DTDParameterEntityDepthTests.swift b/Tests/DTDParameterEntityDepthTests.swift new file mode 100644 index 0000000..0c92067 --- /dev/null +++ b/Tests/DTDParameterEntityDepthTests.swift @@ -0,0 +1,61 @@ +@testable import PureXML +import Testing + +@Suite("DTD parameter-entity and external-subset depth") +struct DTDParameterEntityDepthTests { + private let dtdAllowed = PureXML.Parsing.Limits(allowDoctype: true) + + private func documentType(_ xml: String, resolver: PureXML.Parsing.EntityResolver = .refusing) throws -> PureXML.Parsing.DocumentType { + try PureXML.Parsing.Parser().parseWithDocumentType(xml, limits: dtdAllowed, resolver: resolver).documentType + } + + private func rootText(_ node: PureXML.Model.Node) -> String { + guard case let .document(children) = node else { return "" } + for case let .element(element) in children { + return element.text + } + return "" + } + + @Test("A parameter entity expands within an element-model declaration") + func test_parameterEntityInElementModel() throws { + let dtd = try documentType("]>") + #expect(dtd.elementModels["r"] == "(a|b)") + } + + @Test("A parameter entity expands within an attribute-list declaration") + func test_parameterEntityInAttlist() throws { + let xml = "]>" + // The attribute is declared via the PE, so a missing required code is caught. + let errors = try PureXML.validateAgainstInternalDTD(xml) + #expect(errors.contains { $0.reason.contains("code") }) + } + + @Test("Deeply nested parameter entities compose fully") + func test_deepNesting() throws { + let xml = """ + + + + + + ]>&e; + """ + try #expect(rootText(PureXML.parse(xml, limits: dtdAllowed)) == "12345") + } + + @Test("An external subset can itself load a further external parameter entity") + func test_recursiveExternalLoading() throws { + let resolver = PureXML.Parsing.EntityResolver(resolveExternalSubset: { id in + switch id.systemID { + case "a.dtd": "%inner;" + case "b.dtd": "" + default: nil + } + }) + let xml = "%ext;]>&deep;" + let node = try PureXML.parse(xml, limits: dtdAllowed, resolver: resolver) + #expect(rootText(node) == "found") + } +}