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