import Testing
@testable import PureXML
@Suite("DTD validation")
struct DTDValidationTests {
private func issues(_ xml: String, strict: Bool = false) throws -> [PureXML.Validation.ValidationError] {
try PureXML.validateAgainstInternalDTD(xml, strict: strict)
}
@Test("EMPTY element accepts no content and rejects content")
func test_empty() throws {
try #expect(issues("]>").isEmpty)
try #expect(!issues("]>x").isEmpty)
try #expect(!issues("]>").isEmpty)
}
@Test("(#PCDATA) accepts text and rejects child elements")
func test_pcdata() throws {
try #expect(issues("]>hello").isEmpty)
try #expect(!issues("]>").isEmpty)
}
@Test("Sequence content model enforces order and presence")
func test_sequence() throws {
let dtd = "]>"
try #expect(issues("\(dtd)").isEmpty)
try #expect(!issues("\(dtd)").isEmpty)
try #expect(!issues("\(dtd)").isEmpty)
}
@Test("Choice content model accepts exactly one alternative")
func test_choice() throws {
let dtd = "]>"
try #expect(issues("\(dtd)").isEmpty)
try #expect(issues("\(dtd)").isEmpty)
try #expect(!issues("\(dtd)").isEmpty)
}
@Test("Occurrence indicators bound repetition")
func test_occurrence() throws {
let star = "]>"
try #expect(issues("\(star)").isEmpty)
try #expect(issues("\(star)").isEmpty)
let plus = "]>"
try #expect(!issues("\(plus)").isEmpty)
let optional = "]>"
try #expect(issues("\(optional)").isEmpty)
}
@Test("Mixed content allows only the declared elements")
func test_mixed() throws {
let dtd = "]>"
try #expect(issues("\(dtd)textmore").isEmpty)
try #expect(!issues("\(dtd)").isEmpty)
}
@Test("Element content rejects character data")
func test_elementContentRejectsText() throws {
let dtd = "]>"
try #expect(!issues("\(dtd)text").isEmpty)
}
@Test("Strict mode rejects undeclared elements")
func test_strictUndeclared() throws {
let dtd = "]>"
try #expect(issues("\(dtd)", strict: false).isEmpty)
try #expect(!issues("\(dtd)", strict: true).isEmpty)
}
@Test("A document without a DTD reports no content-model issues")
func test_noDTD() throws {
try #expect(issues("").isEmpty)
}
@Test("A content-model violation is located by coding path")
func test_violationCarriesPath() throws {
let dtd = "]>"
let found = try issues("\(dtd)oops")
#expect(found.count == 1)
#expect(String(describing: found[0]) == "element is declared EMPTY but has content at path: r/a")
}
}