test(conformance): add an XPath axes/predicates corpus
Grow the Tier-2 conformance corpus with 13 XPath navigation cases (descendant and all-element counts, positional and last() predicates, ancestor and ancestor-or-self axes, following/preceding-sibling, the attribute axis, an attribute predicate, union, the parent abbreviation, and position()) over a fixed tree, checked through the validation-driven harness. Own suite. Part of #105 (Tier 2)
This commit is contained in:
parent
f8e9938f4a
commit
0cf697c4ba
@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Added
|
||||
|
||||
- Conformance corpus harness (#105, Tier 2): the start of conformance-suite checking, expressed through the validation framework (the OpenAPIKit idiom). A `PureXML.Validation.ConformanceCase` is a `Validatable` named expectation (actual vs spec-authoritative expected output), and `Conformance.matchesExpected` is a `Validation<ConformanceCase, Void>` rule that emits one located `ValidationError` per diverging case, so a whole suite reports every failure at once with the case name as its coding path. Seeded with a Canonical XML 1.0 corpus (attribute ordering, empty-element expansion, comment removal, CDATA-to-escaped-text, processing instructions, namespace rendering) an XPath 1.0 core-function corpus, and an XSD datatype/facet corpus (length, range, pattern, enumeration, lexical space), a RELAX NG pattern corpus (text, optional, repetition, choice, ordered group, attribute, interleave, empty), an XSLT 1.0 transformation corpus (value-of, for-each, if, choose, sort, variable, format-number, apply-templates, call-template), an HTML5 tree-construction corpus (void elements, implied tag closing, case normalization, the adoption agency, entities), a Schematron corpus (assert, report, rule context), and a C14N exclusive/2.0 corpus (unused-namespace dropping, render-at-use, whitespace trimming). The HTML corpus surfaced its first divergence (the fragment parser does not reconstruct active formatting elements like the document parser, tracked as #109). Growing the corpora against the official W3C/OASIS suites is the ongoing Tier-2 work.
|
||||
- Conformance corpus harness (#105, Tier 2): the start of conformance-suite checking, expressed through the validation framework (the OpenAPIKit idiom). A `PureXML.Validation.ConformanceCase` is a `Validatable` named expectation (actual vs spec-authoritative expected output), and `Conformance.matchesExpected` is a `Validation<ConformanceCase, Void>` rule that emits one located `ValidationError` per diverging case, so a whole suite reports every failure at once with the case name as its coding path. Seeded with a Canonical XML 1.0 corpus (attribute ordering, empty-element expansion, comment removal, CDATA-to-escaped-text, processing instructions, namespace rendering) an XPath 1.0 core-function corpus, and an XSD datatype/facet corpus (length, range, pattern, enumeration, lexical space), a RELAX NG pattern corpus (text, optional, repetition, choice, ordered group, attribute, interleave, empty), an XSLT 1.0 transformation corpus (value-of, for-each, if, choose, sort, variable, format-number, apply-templates, call-template), an HTML5 tree-construction corpus (void elements, implied tag closing, case normalization, the adoption agency, entities), a Schematron corpus (assert, report, rule context), a C14N exclusive/2.0 corpus (unused-namespace dropping, render-at-use, whitespace trimming), and an XPath axes/predicates corpus (descendant, ancestor, sibling axes, positional and attribute predicates, union). The HTML corpus surfaced its first divergence (the fragment parser does not reconstruct active formatting elements like the document parser, tracked as #109). Growing the corpora against the official W3C/OASIS suites is the ongoing Tier-2 work.
|
||||
- EXSLT extension functions (#105, Tier 3): the `common`, `math`, and `sets` EXSLT modules, dispatched by resolving a function's prefix to its namespace (so any prefix bound to an EXSLT namespace works). `math:min`, `math:max`, `math:highest`, `math:lowest`, `math:abs`, `math:sqrt` (with the EXSLT NaN rule for non-numeric nodes); `set:distinct`, `set:difference`, `set:intersection`, `set:has-same-node`, `set:leading`, `set:trailing` (by node identity and document order); `exsl:object-type`. `math:power` and the other transcendentals are omitted because they need a C math library, which the pure-Swift, Foundation-free target excludes; `exsl:node-set` awaits result-tree-fragment support.
|
||||
- Canonical XML 1.1 (#105, Tier 3): a `Canonical.Options.canonical11` preset and a `mergeInheritedBase` option. When canonicalizing a document subset, the `xml:base` values of the apex's omitted ancestors are merged into the apex by RFC 3986 reference resolution (vs. 1.0's nearest-ancestor rule), the apex's own relative `xml:base` is resolved against that chain, and `xml:id` is no longer inherited; `xml:lang` and `xml:space` still inherit the nearest. A self-contained RFC 3986 resolver (scheme/authority/path parsing, path merging, dot-segment removal) backs the merge.
|
||||
- Single-byte legacy encodings (#97): the byte decoder gains a table-based single-byte path and the full set of single-byte legacy encodings, selected by the XML declaration's `encoding` name for both the whole-buffer and streaming decoders: the complete ISO-8859 family (parts 2 through 16), Windows-1250/1251/1253/1254/1255/1256/1257/1258, and KOI8-R/U. Most are vendored verbatim from the authoritative `unicode.org` mapping files; ISO-8859-5/9/15 are derived exactly; Windows-1254 composes from Windows-1252. Multi-byte CJK encodings (#99) are tracked separately.
|
||||
|
||||
@ -99,7 +99,7 @@ flowchart TB
|
||||
|
||||
PureXML is a working, dependency-free XML library today: parse, emit, validate,
|
||||
query, and stream documents on macOS, Linux, Windows, and WASM. The test suite
|
||||
currently runs **818 tests in 127 suites** (`swift test`).
|
||||
currently runs **819 tests in 128 suites** (`swift test`).
|
||||
|
||||
### Shipped (libxml2-aligned surface)
|
||||
|
||||
|
||||
44
Tests/ConformanceXPathAxisTests.swift
Normal file
44
Tests/ConformanceXPathAxisTests.swift
Normal file
@ -0,0 +1,44 @@
|
||||
@testable import PureXML
|
||||
import Testing
|
||||
|
||||
/// XPath 1.0 axis and predicate conformance, driven through the validation
|
||||
/// framework (its own suite to keep the main corpus under the cap).
|
||||
@Suite("Conformance corpus: XPath axes and predicates")
|
||||
struct ConformanceXPathAxisTests {
|
||||
private struct AxisSpec {
|
||||
let name: String
|
||||
let expression: String
|
||||
let expected: String
|
||||
}
|
||||
|
||||
/// Axis navigation and positional predicates over a fixed tree, checked
|
||||
/// against the node-set the XPath 1.0 spec selects (coerced to a string).
|
||||
private func corpus() throws -> [PureXML.Validation.ConformanceCase] {
|
||||
let document = try PureXML.parse("<root><a id=\"1\"><b>x</b><b>y</b></a><a id=\"2\"><c>z</c></a></root>")
|
||||
let specs = [
|
||||
AxisSpec(name: "descendant-count", expression: "count(//b)", expected: "2"),
|
||||
AxisSpec(name: "all-elements-count", expression: "count(//*)", expected: "6"),
|
||||
AxisSpec(name: "positional-predicate", expression: "//b[2]", expected: "y"),
|
||||
AxisSpec(name: "last-predicate", expression: "//a[last()]/c", expected: "z"),
|
||||
AxisSpec(name: "ancestor-axis", expression: "count(//c/ancestor::a)", expected: "1"),
|
||||
AxisSpec(name: "ancestor-or-self", expression: "count(//c/ancestor-or-self::*)", expected: "3"),
|
||||
AxisSpec(name: "following-sibling", expression: "count(//a[1]/following-sibling::a)", expected: "1"),
|
||||
AxisSpec(name: "preceding-sibling", expression: "count(//a[2]/preceding-sibling::a)", expected: "1"),
|
||||
AxisSpec(name: "attribute-axis", expression: "//a[2]/@id", expected: "2"),
|
||||
AxisSpec(name: "attribute-predicate", expression: "count(//a[@id='1'])", expected: "1"),
|
||||
AxisSpec(name: "union", expression: "count(//b | //c)", expected: "3"),
|
||||
AxisSpec(name: "parent-abbreviation", expression: "//c/../@id", expected: "2"),
|
||||
AxisSpec(name: "position-function", expression: "count(//a[position()=1])", expected: "1"),
|
||||
]
|
||||
return try specs.map { spec in
|
||||
let actual = try PureXML.XPath.Query(spec.expression).string(over: document)
|
||||
return PureXML.Validation.ConformanceCase(name: spec.name, actual: actual, expected: spec.expected)
|
||||
}
|
||||
}
|
||||
|
||||
@Test("The XPath axes/predicates conformance corpus passes with no located failures")
|
||||
func test_axisCorpusConforms() throws {
|
||||
let failures = try PureXML.Validation.Conformance.failures(in: corpus())
|
||||
#expect(failures.isEmpty, "\(failures.map(\.reason))")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user