From baa90cfcaa2fe9772e7a23aa900793bdf645897b Mon Sep 17 00:00:00 2001 From: Mihaela Mihaljevic Date: Tue, 16 Jun 2026 01:27:10 +0200 Subject: [PATCH] chore(release): prepare 0.2.0 Cut the second pre-release: schema-validity burn-down to XSTS baselines 1/266/171/155, component child-content shape checks, and synced conformance docs. CHANGELOG updated for v0.2.0. --- CHANGELOG.md | 6 ++ README.md | 4 +- Sources/Schema/SchemaStructure.swift | 8 ++ Sources/Schema/SchemaStructureHelpers.swift | 59 +++++++++++++ Tests/SchemaStructureTests.swift | 96 +++++++++++++++++++++ docs/xsts-deviations.md | 6 +- 6 files changed, 174 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19826b7..f849896 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.2.0] - 2026-06-16 + +Second pre-release. Continues the W3C XSTS schema-validity campaign: **invalid-schemas-accepted 394 → 266** (settled expectations), **valid-instances-rejected 180 → 171**, **invalid-instances-accepted 158 → 155**, **valid-schemas-rejected held at 1** (`particlesZ001`, spec-ambiguous). Not yet 1.0: see `docs/production-readiness.md`. + ### Fixed +- Enforce element, attribute, attributeGroup, and group child-content shape (schema-for-schemas cardinality and order): at most one inline type per element/attribute, inline type before identity constraints, at most one compositor per group, at most one `anyAttribute` per attributeGroup with attribute references preceding it (`SchemaStructureHelpers.swift`). + - Enforce facet applicability per base type, fractionDigits constraints on integer types, ordered duration range bounds, and built-in list non-emptiness constraints (XSD 1.0 Datatypes §4.3). Ensure that built-in list types (xs:IDREFS, xs:ENTITIES, xs:NMTOKENS) are validated as non-empty unless restricted with an explicit minLength. Resolve type references namespace-aware to avoid prefix conflation with user types of the same name. Ignore foreign elements matching facet names inside xs:restriction. XSTS: invalid schemas accepted 296 to 266, no other bucket raised. - Enforce schema element order (imports, includes, and redefines must precede other global declarations) and validate import constraints (namespace attribute must not match the schema's targetNamespace, and if missing, the schema must have a targetNamespace) (XSD 1.0 Structures §3.10.6, §3.15.2). XSTS: invalid schemas accepted 302 to 296, no other bucket raised. diff --git a/README.md b/README.md index 25ee94a..bc270f6 100644 --- a/README.md +++ b/README.md @@ -99,8 +99,8 @@ flowchart TB EncodingMismatch["#137 encoding-declaration mismatches: complete"]:::done BaseURI["#138 per-entity base URIs: complete"]:::done Epic122["#122 official spec suites"]:::epic - XSTS["#129 W3C XSD test suite (XSTS): first measurement 2026-06-13 baselined (deviations 75/2467/604/582); occurrence-explosion and reversed-range crashes fixed, suite green in 8s"]:::review - XSLTTS["#130 XSLT 1.0 suite (xalan-test): runner + baselines in, burning down (125)"]:::review + XSTS["#129 W3C XSD test suite (XSTS): runner shipped; settled deviations 1/266/171/155 (#145–#148)"]:::review + XSLTTS["#130 XSLT 1.0 suite (xalan-test): runner + baselines in, burning down (~126)"]:::review Parity100["Behavioral parity closed: #141 top-level params, #142 public streaming, #143 XPath budget, #144 WASI runtime proof, pl/ru sort collation"]:::done Epic139["#139 faster than libxml2, measured + streaming (benchmarks in docs/benchmarks.md; quadratics killed; unsafe authorized)"]:::epic Audit140["#140 XSLT validation-framework audit"]:::todo diff --git a/Sources/Schema/SchemaStructure.swift b/Sources/Schema/SchemaStructure.swift index 9a1d256..ddcad3b 100644 --- a/Sources/Schema/SchemaStructure.swift +++ b/Sources/Schema/SchemaStructure.swift @@ -133,6 +133,14 @@ extension PureXML.Schema.XSDParser { errors += schemaChildrenOrderErrors(names) case "import": errors += importErrors(node) + case "element": + errors += elementChildrenErrors(node, names: names) + case "attribute": + errors += attributeChildrenErrors(node, names: names) + case "attributeGroup": + errors += attributeGroupChildrenErrors(node, names: names) + case "group": + errors += groupChildrenErrors(node, names: names) default: break } diff --git a/Sources/Schema/SchemaStructureHelpers.swift b/Sources/Schema/SchemaStructureHelpers.swift index 70b1692..75458b1 100644 --- a/Sources/Schema/SchemaStructureHelpers.swift +++ b/Sources/Schema/SchemaStructureHelpers.swift @@ -138,4 +138,63 @@ extension PureXML.Schema.XSDParser { } return errors } + + static func elementChildrenErrors(_: XSDTree, names: [String]) -> [String] { + var errors: [String] = [] + let simpleTypes = names.count { $0 == "simpleType" } + let complexTypes = names.count { $0 == "complexType" } + if (simpleTypes + complexTypes) > 1 { + errors.append("element may have at most one inline type definition (simpleType or complexType)") + } + var seenIdentityConstraint = false + for name in names { + if name == "annotation" { continue } + if name == "simpleType" || name == "complexType" { + if seenIdentityConstraint { + errors.append("inline type definition must appear before identity constraints in element") + } + } else if ["unique", "key", "keyref"].contains(name) { + seenIdentityConstraint = true + } + } + return errors + } + + static func attributeChildrenErrors(_: XSDTree, names: [String]) -> [String] { + var errors: [String] = [] + let simpleTypes = names.count { $0 == "simpleType" } + if simpleTypes > 1 { + errors.append("attribute may have at most one inline simpleType") + } + return errors + } + + static func attributeGroupChildrenErrors(_: XSDTree, names: [String]) -> [String] { + var errors: [String] = [] + let anyAttributes = names.count { $0 == "anyAttribute" } + if anyAttributes > 1 { + errors.append("attributeGroup may have at most one anyAttribute") + } + var seenAnyAttribute = false + for name in names { + if name == "annotation" { continue } + if name == "attribute" || name == "attributeGroup" { + if seenAnyAttribute { + errors.append("attribute and attributeGroup references must appear before anyAttribute in attributeGroup") + } + } else if name == "anyAttribute" { + seenAnyAttribute = true + } + } + return errors + } + + static func groupChildrenErrors(_: XSDTree, names: [String]) -> [String] { + var errors: [String] = [] + let compositors = names.count { $0 == "all" || $0 == "choice" || $0 == "sequence" } + if compositors > 1 { + errors.append("group may have at most one compositor (all, choice, or sequence)") + } + return errors + } } diff --git a/Tests/SchemaStructureTests.swift b/Tests/SchemaStructureTests.swift index a78a8d3..7458729 100644 --- a/Tests/SchemaStructureTests.swift +++ b/Tests/SchemaStructureTests.swift @@ -276,3 +276,99 @@ struct SchemaStructureTests { } } } + +@Suite("Schema structural order and cardinality validity") +struct SchemaStructureOrderTests { + private func compile(_ body: String) throws { + _ = try PureXML.Schema.Document(""" + + \(body) + + """) + } + + private func rejects(_ body: String) -> Bool { + do { + try compile(body) + return false + } catch { + return true + } + } + + @Test("element child order and type exclusivity constraints") + func test_elementChildOrderAndExclusivity() throws { + // More than one type definition is rejected + #expect(rejects(#""" + + + + + """#)) + + // Type definition after identity constraints is rejected + #expect(rejects(#""" + + + + + """#)) + + // Correct order compiles successfully + try compile(#""" + + + + + """#) + } + + @Test("attribute child cardinality constraints") + func test_attributeChildCardinality() { + // More than one simpleType is rejected + #expect(rejects(#""" + + + + + """#)) + } + + @Test("attributeGroup child order and cardinality constraints") + func test_attributeGroupChildOrderAndCardinality() throws { + // More than one anyAttribute is rejected + #expect(rejects(#""" + + + + + """#)) + + // Attribute reference after anyAttribute is rejected + #expect(rejects(#""" + + + + + """#)) + + // Correct order compiles successfully + try compile(#""" + + + + + """#) + } + + @Test("group compositor cardinality constraints") + func test_groupCompositorCardinality() { + // More than one compositor is rejected + #expect(rejects(#""" + + + + + """#)) + } +} diff --git a/docs/xsts-deviations.md b/docs/xsts-deviations.md index 3f4ec99..8c05c1c 100644 --- a/docs/xsts-deviations.md +++ b/docs/xsts-deviations.md @@ -29,12 +29,12 @@ which is an astral math capital); agreeing or disagreeing with a contested expectation is not meaningful, so the runner skips them. Excluding the queried entries removed about 70 apparent deviations that were never bugs. -## Baseline (settled expectations; after twelve fixes) +## Baseline (settled expectations; synced 2026-06-16) | Kind | First measurement | Now | |---|---|---| | valid schema rejected | 75 | 1 | -| invalid schema accepted | 2467 | 296 | +| invalid schema accepted | 2467 | 266 | | valid instance rejected | 604 | 171 | | invalid instance accepted | 582 | 155 | @@ -65,7 +65,7 @@ validity (`min`/`maxInclusive`, `min`/`maxExclusive`, `enumeration` values valid in the base space; inclusive/exclusive exclusions; bound ordering) · `gMonth` `--MM--` lexical form · schema `id` attribute validity (`xs:ID`: NCName and unique within the document). The schema-validity work brought invalid schemas -accepted 2461 to 296 (schema-document structural validity: content model + complexType content shape, attribute values, name/reference + pattern-regex lexical validity, and reference resolution, against the schema-for-schemas, plus simpleType variety constraints on list itemType and union memberTypes, plus targetNamespace scope matching on local schema references, plus substitution group exclusions based on element final and schema finalDefault attributes, duplicate schema container validation across separate files with identical content, schema element order, and import constraints validation); see `schema-validity-burndown.md`. +accepted 2461 to 266 (schema-document structural validity: content model + complexType content shape, attribute values, name/reference + pattern-regex lexical validity, and reference resolution, against the schema-for-schemas, plus simpleType variety constraints on list itemType and union memberTypes, plus targetNamespace scope matching on local schema references, plus substitution group exclusions based on element final and schema finalDefault attributes, duplicate schema container validation across separate files with identical content, schema element order, and import constraints validation); see `schema-validity-burndown.md`. Plus the measurement fix (exclude W3C-disputed `status="queried"` entries). ### Remaining, root-caused (the hard tail)