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