import Testing @testable import PureXML /// The IBM-section burn-down behaviors: a document needs a root element /// (production 1), entity references in literals must be lexical Names (P66), /// WFC: No Recursion is checked at declaration (P69), an undeclared /// parameter-entity reference is a WFC or a VC depending on standalone and /// external declarations (production 68), and a content-model group split /// across parameter entities is a validity finding (VC: Proper Group/PE /// Nesting, P49). @Suite("Entity declared and recursion constraints") struct DTDEntityDeclaredTests { private func limits() -> PureXML.Parsing.Limits { PureXML.Parsing.Limits(allowDoctype: true) } @Test("A document without a root element is rejected") func test_missingRoot() { #expect(throws: PureXML.Parsing.ParseError.self) { _ = try PureXML.parse("\n]>\n", limits: limits()) } #expect(throws: PureXML.Parsing.ParseError.self) { _ = try PureXML.parse("") } } @Test("An entity reference in a literal must be a Name or character reference") func test_referenceNameInLiteral() { let xml = "]>\n" #expect(throws: PureXML.Parsing.ParseError.self) { _ = try PureXML.parse(xml, limits: limits()) } let legal = "]>\n" #expect((try? PureXML.parse(legal, limits: limits())) != nil) } @Test("Recursive entity declarations are rejected without being referenced") func test_declarationRecursion() { let direct = """ ]> """ #expect(throws: PureXML.Parsing.ParseError.self) { _ = try PureXML.parse(direct, limits: limits()) } let indirect = """ ]> """ #expect(throws: PureXML.Parsing.ParseError.self) { _ = try PureXML.parse(indirect, limits: limits()) } } @Test("An undeclared PE reference is a WFC when standalone, a VC otherwise") func test_undeclaredParameterEntity() throws { let standalone = """ %paaa; "> ]> """ #expect(throws: PureXML.Parsing.ParseError.self) { _ = try PureXML.parse(standalone, limits: limits()) } // With an external PE in play the same reference is a validity finding. let loose = """ %outside; %undeclared; ]> """ let resolver = PureXML.Parsing.EntityResolver( resolveEntity: { _, _ in nil }, resolveExternalSubset: { _ in "" }, ) let errors = try PureXML.validateAgainstInternalDTD(loose, resolver: resolver) #expect(errors.contains { $0.reason.contains("'%undeclared;' is referenced but not declared") }) } @Test("A group split across parameter entities is a validity finding") func test_properGroupNesting() throws { let dtd = """ """ let xml = "]>\n" let resolver = PureXML.Parsing.EntityResolver( resolveEntity: { _, _ in nil }, resolveExternalSubset: { _ in dtd }, ) let errors = try PureXML.validateAgainstInternalDTD(xml, resolver: resolver) #expect(errors.contains { $0.reason.contains("improper group/PE nesting") }) } }