An element whose children break its content model now names each stray child outside the model's alphabet individually, and reports a pure order/count violation once with the allowed elements as a hint, instead of a single opaque "do not match its content model". Adds an alphabet extractor to the content-model matcher. This brings DTD to the same located standard as the XSD, RELAX NG, and Schematron validators, so all four schema languages give editor-grade diagnostics.
39 lines
1.7 KiB
Swift
39 lines
1.7 KiB
Swift
@testable import PureXML
|
|
import Testing
|
|
|
|
@Suite("DTD located content-model errors")
|
|
struct DTDLocatedContentTests {
|
|
private func reasons(_ xml: String) throws -> [String] {
|
|
try PureXML.validateAgainstInternalDTD(xml).map(\.reason)
|
|
}
|
|
|
|
@Test("A stray child outside the model is named individually")
|
|
func test_strayChild() throws {
|
|
let dtd = "<!DOCTYPE r [<!ELEMENT r (a, b)><!ELEMENT a EMPTY><!ELEMENT b EMPTY><!ELEMENT x EMPTY>]>"
|
|
let found = try reasons("\(dtd)<r><a/><x/></r>")
|
|
#expect(found.contains { $0.contains("<x>") && $0.contains("not allowed in <r>") })
|
|
}
|
|
|
|
@Test("Two distinct stray children are each reported (recovery)")
|
|
func test_multipleStray() throws {
|
|
let dtd = "<!DOCTYPE r [<!ELEMENT r (a)><!ELEMENT a EMPTY><!ELEMENT x EMPTY><!ELEMENT y EMPTY>]>"
|
|
let found = try reasons("\(dtd)<r><x/><y/></r>")
|
|
#expect(found.contains { $0.contains("<x>") })
|
|
#expect(found.contains { $0.contains("<y>") })
|
|
}
|
|
|
|
@Test("An ordering-only violation lists the allowed elements as a hint")
|
|
func test_orderingHint() throws {
|
|
// Both children are in the alphabet, but reversed against (a, b).
|
|
let dtd = "<!DOCTYPE r [<!ELEMENT r (a, b)><!ELEMENT a EMPTY><!ELEMENT b EMPTY>]>"
|
|
let found = try reasons("\(dtd)<r><b/><a/></r>")
|
|
#expect(found.contains { $0.contains("do not match its content model") && $0.contains("<a>") && $0.contains("<b>") })
|
|
}
|
|
|
|
@Test("A conforming document has no content errors")
|
|
func test_valid() throws {
|
|
let dtd = "<!DOCTYPE r [<!ELEMENT r (a, b)><!ELEMENT a EMPTY><!ELEMENT b EMPTY>]>"
|
|
#expect(try reasons("\(dtd)<r><a/><b/></r>").isEmpty)
|
|
}
|
|
}
|