PureXML/Tests/LineEndingTests.swift
Mihaela Mihaljevic 5351a5c777 feat(parsing): line-ending normalization and XML 1.1 line terminators
Normalize line endings in the Reader as the spec requires, which was not
done before:

- CR and CRLF fold to a single line feed (XML 1.0), so every downstream
  scan sees line feeds only.
- When the declaration names version 1.1, NEL (U+0085) and LINE SEPARATOR
  (U+2028), and a CR followed by NEL, also fold to a line feed; in 1.0
  those characters are preserved as data.

The XML 1.1 name-character productions are identical to XML 1.0 Fifth
Edition, which the character classifier already implements, so names need
no change. The 1.1 character range (C0 controls permitted only as
references) is not yet enforced.

Closes #96
2026-06-08 15:07:21 +02:00

51 lines
1.7 KiB
Swift

@testable import PureXML
import Testing
@Suite("Line-ending normalization and XML 1.1")
struct LineEndingTests {
private func rootText(_ xml: String) throws -> String {
let node = try PureXML.parse(xml)
guard case let .document(children) = node, let root = children.compactMap(\.element).first else {
return ""
}
return root.children.reduce(into: "") { result, child in
if case let .text(text) = child { result += text }
}
}
@Test("CRLF is normalized to a single line feed")
func test_crlf() throws {
#expect(try rootText("<a>x\r\ny</a>") == "x\ny")
}
@Test("A lone carriage return is normalized to a line feed")
func test_loneCR() throws {
#expect(try rootText("<a>x\ry</a>") == "x\ny")
}
@Test("In XML 1.0, NEL is preserved as data")
func test_nel10() throws {
#expect(try rootText("<a>x\u{85}y</a>") == "x\u{85}y")
}
@Test("In XML 1.1, NEL is normalized to a line feed")
func test_nel11() throws {
#expect(try rootText("<?xml version=\"1.1\"?><a>x\u{85}y</a>") == "x\ny")
}
@Test("In XML 1.1, LINE SEPARATOR is normalized to a line feed")
func test_lineSeparator11() throws {
#expect(try rootText("<?xml version=\"1.1\"?><a>x\u{2028}y</a>") == "x\ny")
}
@Test("In XML 1.1, CR followed by NEL collapses to one line feed")
func test_crNel11() throws {
#expect(try rootText("<?xml version=\"1.1\"?><a>x\r\u{85}y</a>") == "x\ny")
}
@Test("In XML 1.0, LINE SEPARATOR is preserved as data")
func test_lineSeparator10() throws {
#expect(try rootText("<a>x\u{2028}y</a>") == "x\u{2028}y")
}
}