import Testing
@testable import PureXML
@Suite("RELAX NG streaming validation")
struct RelaxNGStreamingTests {
private let grammar = """
start = element library { book+ }
book = element book { attribute id { text }, element title { text }, author+ }
author = element author { text }
"""
private let mixedGrammar = "start = element note { mixed { element b { text }* } }"
/// Asserts streaming validation agrees with the tree engine, and matches the
/// expected verdict. Streaming agreeing with the trusted tree walk on every
/// document is the correctness proof for the derivative stepper.
private func check(_ rnc: String, _ xml: String, expected: Bool) throws {
let schema = try PureXML.Schema.RelaxNG(compact: rnc)
let tree = try schema.validate(xml)
let streamed = try schema.validate(streaming: xml)
#expect(tree == streamed, "streaming disagreed with tree on: \(xml)")
#expect(streamed == expected, "wrong verdict for: \(xml)")
}
@Test("Valid documents stream as valid and agree with the tree engine")
func test_valid() throws {
try check(grammar, "TA", expected: true)
// Multiple books and authors (oneOrMore).
try check(
grammar,
"TAB"
+ "UC",
expected: true,
)
// Ignorable whitespace between elements is allowed.
try check(grammar, "\n T\n A\n \n", expected: true)
}
@Test("Invalid documents stream as invalid and agree with the tree engine")
func test_invalid() throws {
// Missing required title.
try check(grammar, "A", expected: false)
// Missing required attribute.
try check(grammar, "TA", expected: false)
// Wrong order (group is ordered: title then author).
try check(grammar, "AT", expected: false)
// Empty library (book+ requires at least one).
try check(grammar, "", expected: false)
// A stray element.
try check(grammar, "TA", expected: false)
// Character data where only elements are allowed.
try check(grammar, "textTA", expected: false)
}
@Test("Mixed content streams correctly (text interleaved with elements)")
func test_mixed() throws {
try check(mixedGrammar, "hello world there", expected: true)
try check(mixedGrammar, "just text", expected: true)
try check(mixedGrammar, "xy", expected: true)
// A disallowed child element fails even in mixed content.
try check(mixedGrammar, "text no", expected: false)
}
@Test("Streaming errors fall back to located tree diagnostics when invalid")
func test_streamingErrors() throws {
let schema = try PureXML.Schema.RelaxNG(compact: grammar)
let bad = "A"
#expect(try schema.validate(streaming: bad) == false)
let errors = try schema.errors(streaming: bad)
#expect(!errors.isEmpty)
#expect(try schema.errors(streaming: "TA").isEmpty)
}
}