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