PureXML/Tests/XSDRestrictionContentFreeOrderTests.swift
Mihaela Mihaljevic 83e6e27d12 style(format): adopt --importgrouping testable-bottom from canonical rules-swift
Group @testable imports at the bottom, per public/rules-swift's swiftformat. A
behavior-preserving import-ordering reformat across 252 files (mostly tests).
--trailingclosures was deliberately not adopted (cosmetic, and swiftformat flags
it as caution-worthy).
2026-06-18 20:33:07 +02:00

100 lines
6.5 KiB
Swift

import Testing
@testable import PureXML
@Suite("XSD restriction: content-free and substitution-group order")
struct XSDRestrictionContentFreeAndOrderTests {
private func compiles(_ source: String) -> Bool {
(try? PureXML.Schema.Document(source)) != nil
}
@Test("A never-occurring (maxOccurs=0) group restricts an emptiable base, but not a required one")
func test_contentFreeRestriction() {
// particlesW006: a sequence with maxOccurs=0 accepts only the empty sequence,
// whatever its members' own occurrences, so it validly restricts a base that
// also never occurs (or is otherwise emptiable).
let base = "<xs:complexType name=\"B\"><xs:sequence minOccurs=\"0\" maxOccurs=\"0\">"
+ "<xs:element name=\"e1\" maxOccurs=\"3\"/><xs:element name=\"e2\" maxOccurs=\"3\"/></xs:sequence></xs:complexType>"
let restriction = "<xs:complexType name=\"R\"><xs:complexContent><xs:restriction base=\"B\">"
+ "<xs:sequence minOccurs=\"0\" maxOccurs=\"0\">"
+ "<xs:element name=\"e1\" minOccurs=\"4\" maxOccurs=\"5\"/><xs:element name=\"e2\" minOccurs=\"3\" maxOccurs=\"3\"/></xs:sequence>"
+ "</xs:restriction></xs:complexContent></xs:complexType>"
#expect(compiles("<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\(base)\(restriction)</xs:schema>"))
// But a content-free restriction of a REQUIRED base is invalid: the base
// requires content the empty restriction cannot supply.
let requiredBase = "<xs:complexType name=\"B\"><xs:sequence><xs:element name=\"e1\"/></xs:sequence></xs:complexType>"
let emptyRestriction = "<xs:complexType name=\"R\"><xs:complexContent><xs:restriction base=\"B\">"
+ "<xs:sequence minOccurs=\"0\" maxOccurs=\"0\"><xs:element name=\"e1\"/></xs:sequence>"
+ "</xs:restriction></xs:complexContent></xs:complexType>"
#expect(!compiles("<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\(requiredBase)\(emptyRestriction)</xs:schema>"))
}
@Test("A choice of substitution-group members restricts a reference to the head, in declaration order")
func test_substitutionGroupChoiceOrder() {
// elemZ027a: a ref to a head expands to choice(head, m1, m2) in declaration
// order; a derived choice(m1, m2) maps onto it in order (RecurseLax).
let decls = "<xs:element name=\"head\"/><xs:element name=\"m1\" substitutionGroup=\"head\"/>"
+ "<xs:element name=\"m2\" substitutionGroup=\"head\"/>"
let base = "<xs:complexType name=\"base\"><xs:sequence><xs:element ref=\"head\"/></xs:sequence></xs:complexType>"
func derived(_ first: String, _ second: String) -> String {
"<xs:complexType name=\"derived\"><xs:complexContent><xs:restriction base=\"base\"><xs:sequence>"
+ "<xs:choice><xs:element ref=\"\(first)\"/><xs:element ref=\"\(second)\"/></xs:choice>"
+ "</xs:sequence></xs:restriction></xs:complexContent></xs:complexType>"
}
let head = "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\(decls)\(base)"
#expect(compiles(head + derived("m1", "m2") + "</xs:schema>"))
}
@Test("Reordering a base choice's branches is not a valid restriction (RecurseLax is order-preserving)")
func test_choiceReorderRejected() {
// particlesT002: choice(c2, c1) is NOT a valid restriction of choice(c1, c2).
let base = "<xs:complexType name=\"B\"><xs:sequence><xs:choice>"
+ "<xs:element name=\"c1\"/><xs:element name=\"c2\"/></xs:choice><xs:element name=\"foo\"/></xs:sequence></xs:complexType>"
let reordered = "<xs:complexType name=\"R\"><xs:complexContent><xs:restriction base=\"B\"><xs:sequence><xs:choice>"
+ "<xs:element name=\"c2\"/><xs:element name=\"c1\"/></xs:choice><xs:element name=\"foo\"/></xs:sequence>"
+ "</xs:restriction></xs:complexContent></xs:complexType>"
#expect(!compiles("<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\(base)\(reordered)</xs:schema>"))
}
}
@Suite("XSD restriction: MapAndSum (sequence restricting a choice)")
struct XSDRestrictionMapAndSumTests {
private func wrap(_ base: String, _ restriction: String) -> String {
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
+ "<xs:complexType name=\"B\">\(base)</xs:complexType>"
+ "<xs:complexType name=\"R\"><xs:complexContent><xs:restriction base=\"B\">\(restriction)"
+ "</xs:restriction></xs:complexContent></xs:complexType></xs:schema>"
}
private func compiles(_ base: String, _ restriction: String) -> Bool {
(try? PureXML.Schema.Document(wrap(base, restriction))) != nil
}
@Test("A sequence restricts a choice when each member fits a branch and the count-product is in range")
func test_mapAndSum() {
// particlesV003: derived sequence{2,4}(e1,e2) emits 4..8 elements, within the
// base choice{3,9}'s range, each an e1/e2 the choice admits. Valid (MapAndSum).
#expect(compiles(
"<xs:choice minOccurs=\"3\" maxOccurs=\"9\"><xs:element name=\"e1\"/><xs:element name=\"e2\"/></xs:choice>",
"<xs:sequence minOccurs=\"2\" maxOccurs=\"4\"><xs:element name=\"e1\"/><xs:element name=\"e2\"/></xs:sequence>",
))
// Count-product out of range: derived sequence{2,4}(e1,e2) emits up to 8, the
// base choice{3,7} allows at most 7. Invalid.
#expect(!compiles(
"<xs:choice minOccurs=\"3\" maxOccurs=\"7\"><xs:element name=\"e1\"/><xs:element name=\"e2\"/></xs:choice>",
"<xs:sequence minOccurs=\"2\" maxOccurs=\"4\"><xs:element name=\"e1\"/><xs:element name=\"e2\"/></xs:sequence>",
))
// A multi-member sequence cannot restrict a single-occurrence choice: the base
// picks exactly one, the derived requires both (count 2 > max 1). Invalid
// (this is the over-acceptance the count-product check closes).
#expect(!compiles(
"<xs:choice><xs:element name=\"a\"/><xs:element name=\"b\"/></xs:choice>",
"<xs:sequence><xs:element name=\"a\"/><xs:element name=\"b\"/></xs:sequence>",
))
// A member that fits no branch is rejected regardless of count.
#expect(!compiles(
"<xs:choice minOccurs=\"2\" maxOccurs=\"4\"><xs:element name=\"e1\"/><xs:element name=\"e2\"/></xs:choice>",
"<xs:sequence minOccurs=\"2\" maxOccurs=\"2\"><xs:element name=\"e1\"/><xs:element name=\"z\"/></xs:sequence>",
))
}
}