import Testing
@testable import PureXML
@Suite("DTD attribute defaulting and tokenized types")
struct DTDDefaultingTests {
private func validate(_ xml: String) throws -> [PureXML.Validation.ValidationError] {
try PureXML.validateAgainstInternalDTD(xml)
}
private func attribute(_ element: PureXML.Model.Element?, _ name: String) -> String? {
element?.attributes.first { $0.name.description == name }?.value
}
private func root(_ node: PureXML.Model.Node) -> PureXML.Model.Element? {
guard case let .document(children) = node else { return nil }
return children.compactMap(\.element).first
}
// MARK: Defaulting
@Test("A default attribute value is injected into an element that omits it")
func test_defaultInjected() throws {
let xml = "]>"
let node = try PureXML.parseApplyingInternalDTDDefaults(xml)
#expect(attribute(root(node), "kind") == "box")
}
@Test("A present attribute is not overwritten by its default")
func test_presentNotOverwritten() throws {
let xml = "]>"
let node = try PureXML.parseApplyingInternalDTDDefaults(xml)
#expect(attribute(root(node), "kind") == "crate")
}
@Test("A #FIXED value is injected when absent")
func test_fixedInjected() throws {
let xml = "]>"
let node = try PureXML.parseApplyingInternalDTDDefaults(xml)
#expect(attribute(root(node), "v") == "1")
}
@Test("#REQUIRED and #IMPLIED inject nothing")
func test_requiredImpliedInjectNothing() throws {
let xml = "]>"
let node = try PureXML.parseApplyingInternalDTDDefaults(xml)
#expect(attribute(root(node), "b") == nil)
}
@Test("A non-CDATA default value is whitespace-normalized")
func test_defaultNormalized() throws {
let xml = "]>"
let node = try PureXML.parseApplyingInternalDTDDefaults(xml)
// Tabs become spaces and the value is collapsed and trimmed.
#expect(attribute(root(node), "t") == "a b")
}
@Test("A CDATA default keeps internal content but replaces tabs with spaces")
func test_cdataDefaultReplaceOnly() throws {
let xml = "]>"
let node = try PureXML.parseApplyingInternalDTDDefaults(xml)
#expect(attribute(root(node), "t") == "a b")
}
@Test("A character reference in a default is expanded")
func test_defaultCharacterReference() throws {
let xml = "]>"
let node = try PureXML.parseApplyingInternalDTDDefaults(xml)
#expect(attribute(root(node), "t") == "AB")
}
// MARK: Tokenized type validation
@Test("A valid NMTOKEN passes and an invalid one fails")
func test_nmtoken() throws {
let valid = "]>"
#expect(try validate(valid).isEmpty)
let bad = "]>"
#expect(try !validate(bad).isEmpty)
}
@Test("NMTOKENS requires every whitespace-separated token to be a name token")
func test_nmtokens() throws {
let valid = "]>"
#expect(try validate(valid).isEmpty)
let bad = "]>"
#expect(try !validate(bad).isEmpty)
}
@Test("ENTITY must name a declared unparsed entity")
func test_entity() throws {
let dtd = ""
#expect(try validate("").isEmpty)
// A syntactically valid name that is not a declared unparsed entity fails.
#expect(try !validate("").isEmpty)
}
@Test("ENTITIES requires every token to name a declared unparsed entity")
func test_entities() throws {
let dtd = ""
#expect(try validate("").isEmpty)
#expect(try !validate("").isEmpty)
}
}