PureXML/Tests/XMLDeclarationTests.swift
Mihaela Mihaljevic 9168f990d6 feat(parsing): surface the XML declaration (version, encoding, standalone)
Parse the XML declaration's pseudo-attributes instead of skipping the
<?xml?> processing instruction:

- version, encoding, and standalone are surfaced on a new XMLDeclaration,
  returned from parseWithDocumentType and via PureXML.xmlDeclaration(_:).
- The pseudo-attributes are validated: fixed order (version, encoding,
  standalone), known names only, and standalone must be yes or no. A
  malformed declaration is rejected.
- The declared version is now readable, a prerequisite for XML 1.1.

parseWithDocumentType returns a ParsedDocument struct (node,
documentType, declaration) rather than a 2-tuple; the member names are
unchanged so existing call sites are unaffected.

Closes #95
2026-06-08 14:53:04 +02:00

65 lines
2.3 KiB
Swift

@testable import PureXML
import Testing
@Suite("XML declaration")
struct XMLDeclarationTests {
@Test("version, encoding, and standalone are all surfaced")
func test_full() {
let declaration = PureXML.xmlDeclaration("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><r/>")
#expect(declaration?.version == "1.0")
#expect(declaration?.encoding == "UTF-8")
#expect(declaration?.standalone == true)
}
@Test("standalone=no is parsed as false")
func test_standaloneNo() {
let declaration = PureXML.xmlDeclaration("<?xml version=\"1.1\" standalone=\"no\"?><r/>")
#expect(declaration?.version == "1.1")
#expect(declaration?.standalone == false)
}
@Test("A version-only declaration leaves encoding and standalone nil")
func test_versionOnly() {
let declaration = PureXML.xmlDeclaration("<?xml version=\"1.0\"?><r/>")
#expect(declaration?.version == "1.0")
#expect(declaration?.encoding == nil)
#expect(declaration?.standalone == nil)
}
@Test("A document without a declaration has none")
func test_none() {
#expect(PureXML.xmlDeclaration("<r/>") == nil)
}
@Test("Pseudo-attributes out of order are rejected")
func test_outOfOrder() {
#expect(throws: PureXML.Parsing.ParseError.self) {
_ = try PureXML.parse("<?xml encoding=\"UTF-8\" version=\"1.0\"?><r/>")
}
}
@Test("An illegal standalone value is rejected")
func test_badStandalone() {
#expect(throws: PureXML.Parsing.ParseError.self) {
_ = try PureXML.parse("<?xml version=\"1.0\" standalone=\"maybe\"?><r/>")
}
}
@Test("An unknown pseudo-attribute is rejected")
func test_unknownPseudoAttribute() {
#expect(throws: PureXML.Parsing.ParseError.self) {
_ = try PureXML.parse("<?xml version=\"1.0\" mode=\"strict\"?><r/>")
}
}
@Test("A valid declaration still parses the document tree")
func test_treeStillParses() throws {
let node = try PureXML.parse("<?xml version=\"1.0\"?><r>hi</r>")
guard case let .document(children) = node, let root = children.compactMap(\.element).first else {
Issue.record("no root")
return
}
#expect(root.name.localName == "r")
}
}