test(conformance): add a Schematron corpus
Grow the Tier-2 conformance corpus with 7 Schematron cases (assert satisfied/ violated, an inert non-matching context, numeric comparison, and report fired/ not-fired) checked through the validation-driven harness. Moved to its own suite (ConformanceSchematronTests) so the main corpus stays under the type-body cap. Part of #105 (Tier 2)
This commit is contained in:
parent
4c93555d1f
commit
1f2dcc64f2
@ -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), and an HTML5 tree-construction corpus (void elements, implied tag closing, case normalization, the adoption agency, entities). 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), and a Schematron corpus (assert, report, rule context). 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 **816 tests in 125 suites** (`swift test`).
|
||||
currently runs **817 tests in 126 suites** (`swift test`).
|
||||
|
||||
### Shipped (libxml2-aligned surface)
|
||||
|
||||
|
||||
46
Tests/ConformanceSchematronTests.swift
Normal file
46
Tests/ConformanceSchematronTests.swift
Normal file
@ -0,0 +1,46 @@
|
||||
@testable import PureXML
|
||||
import Testing
|
||||
|
||||
/// Schematron conformance, driven through the validation framework (kept in its
|
||||
/// own suite so the main corpus stays under the type-body cap).
|
||||
@Suite("Conformance corpus: Schematron")
|
||||
struct ConformanceSchematronTests {
|
||||
private struct SchematronSpec {
|
||||
let name: String
|
||||
let rule: String
|
||||
let xml: String
|
||||
let valid: Bool
|
||||
}
|
||||
|
||||
/// An `assert` fires when its test is false, a `report` when its test is true,
|
||||
/// and a rule whose context matches nothing is inert.
|
||||
private func schematronCorpus() throws -> [PureXML.Validation.ConformanceCase] {
|
||||
let assertRule = "<rule context=\"a\"><assert test=\"b\">missing b</assert></rule>"
|
||||
let ageRule = "<rule context=\"age\"><assert test=\"number(.) < 100\">too high</assert></rule>"
|
||||
let reportRule = "<rule context=\"a\"><report test=\"@flag\">flagged</report></rule>"
|
||||
let specs = [
|
||||
SchematronSpec(name: "assert-satisfied", rule: assertRule, xml: "<a><b/></a>", valid: true),
|
||||
SchematronSpec(name: "assert-violated", rule: assertRule, xml: "<a/>", valid: false),
|
||||
SchematronSpec(name: "context-no-match-inert", rule: "<rule context=\"z\"><assert test=\"false()\">never</assert></rule>", xml: "<a/>", valid: true),
|
||||
SchematronSpec(name: "number-comparison-ok", rule: ageRule, xml: "<age>50</age>", valid: true),
|
||||
SchematronSpec(name: "number-comparison-fail", rule: ageRule, xml: "<age>150</age>", valid: false),
|
||||
SchematronSpec(name: "report-not-fired", rule: reportRule, xml: "<a/>", valid: true),
|
||||
SchematronSpec(name: "report-fired", rule: reportRule, xml: "<a flag=\"1\"/>", valid: false),
|
||||
]
|
||||
return try specs.map { spec in
|
||||
let schema = "<schema xmlns=\"http://purl.oclc.org/dsdl/schematron\"><pattern>\(spec.rule)</pattern></schema>"
|
||||
let errors = try PureXML.Validation.Schematron(schema: schema).validate(spec.xml)
|
||||
return PureXML.Validation.ConformanceCase(
|
||||
name: spec.name,
|
||||
actual: errors.isEmpty ? "valid" : "invalid",
|
||||
expected: spec.valid ? "valid" : "invalid",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test("The Schematron conformance corpus passes with no located failures")
|
||||
func test_schematronCorpusConforms() throws {
|
||||
let failures = try PureXML.Validation.Conformance.failures(in: schematronCorpus())
|
||||
#expect(failures.isEmpty, "\(failures.map(\.reason))")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user