// 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("xy") let query = try PureXML.XPath.Query("count(//a)") #expect(try query.number(over: document) == 2) #expect(PureXML.serialize(document).contains("x")) } @Test("The streaming reader pulls events publicly, bounded memory") func test_streamingPull() throws { var reader = PureXML.events("x") 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 = ["", "hello", ""].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) } @Test("An evaluation budget throws instead of evaluating unbounded") func test_evaluationBudget() throws { let tree = try PureXML.parseTree("") let query = try PureXML.XPath.Query("//a") // Unbounded evaluates; a budget below the result size throws. #expect(try query.value(at: tree).nodes?.count == 5) #expect(throws: PureXML.XPath.QueryError.budgetExceeded(3)) { _ = try query.value(at: tree, budget: .init(maxNodeSetLength: 3)) } // The libxml2-compatible cap admits ordinary documents. #expect(try query.value(at: tree, budget: .libxml2Compatible).nodes?.count == 5) } }