EventReader.next(), both initializers, and the Event alias are public (an internal typealias in a split file had been shadowing the public Parsing.Event); a new PureXMLPublicAPITests target compiles without testable import so facade gaps fail CI. The large-corpus benchmarks exposed document-order sorting as quadratic on flat fan-outs; the fix's first attempt was itself worse (an eager sibling table built per predicate evaluation), caught by sampling the live run, and replaced with a lazy table: single-node sets never touch order keys and a parent's table builds on its second lookup. XPath at 42 MB drops 27.1 s to 1.57 s (190x to 11.7x); at 210 MB PureXML completes in 9.4 s the query libxml2 refuses at its 10M node-set cap (#143 tracks our configurable budget). Benchmark harness: size series to 210 MB, CORPUS mode for real files (NASA, SwissProt measured), SAX stage, refusal-aware table, NULL-safe C driver. All conformance suites, Linux, and WASM verified. Closes #142.
52 lines
2.0 KiB
Swift
52 lines
2.0 KiB
Swift
// Deliberately NOT @testable: these tests compile against the public
|
|
// surface exactly as an external package would, so a public facade over
|
|
// internal members (the #142 class of gap: a public EventReader whose
|
|
// next() was internal) fails CI instead of hiding behind testability.
|
|
import PureXML
|
|
import Testing
|
|
|
|
@Suite("Public API surface")
|
|
struct PublicSurfaceTests {
|
|
@Test("Parse, query, and serialize work through the public surface")
|
|
func test_parseQuerySerialize() throws {
|
|
let document = try PureXML.parse("<r><a k=\"v\">x</a><a>y</a></r>")
|
|
let query = try PureXML.XPath.Query("count(//a)")
|
|
#expect(try query.number(over: document) == 2)
|
|
#expect(PureXML.serialize(document).contains("<a k=\"v\">x</a>"))
|
|
}
|
|
|
|
@Test("The streaming reader pulls events publicly, bounded memory")
|
|
func test_streamingPull() throws {
|
|
var reader = PureXML.events("<r><a>x</a><!--c--></r>")
|
|
var names: [String] = []
|
|
var texts: [String] = []
|
|
var comments = 0
|
|
while let event = try reader.next() {
|
|
switch event {
|
|
case let .startElement(name, _): names.append(name.description)
|
|
case let .characters(text): texts.append(text)
|
|
case .comment: comments += 1
|
|
default: break
|
|
}
|
|
}
|
|
#expect(names == ["r", "a"])
|
|
#expect(texts == ["x"])
|
|
#expect(comments == 1)
|
|
}
|
|
|
|
@Test("The streaming reader accepts a chunked pull source")
|
|
func test_streamingFromChunks() throws {
|
|
var chunks = ["<r><a>", "hello", "</a></r>"].makeIterator()
|
|
var characters: [Character] = []
|
|
var reader = PureXML.Parsing.EventReader(pulling: {
|
|
if characters.isEmpty, let chunk = chunks.next() { characters = Array(chunk) }
|
|
return characters.isEmpty ? nil : characters.removeFirst()
|
|
})
|
|
var events = 0
|
|
while try reader.next() != nil {
|
|
events += 1
|
|
}
|
|
#expect(events == 5)
|
|
}
|
|
}
|