import Testing
@testable import PureXML
@Suite("XSD circular type derivation")
struct SchemaCircularDerivationTests {
private let header = ""
private func compiles(_ body: String, loader: @escaping (String) -> String? = { _ in nil }) -> Bool {
(try? PureXML.Schema.Document(header + body + "", schemaLoader: loader)) != nil
}
@Test("A type derived from itself is rejected (ct-props-correct.3 / st-props-correct.2)")
func test_selfDerivationRejected() {
// A complex type extending itself (addB101).
#expect(!compiles(
"",
))
// A complex type restricting itself.
#expect(!compiles(
"",
))
// A mutual derivation cycle A -> B -> A.
#expect(!compiles(
""
+ "",
))
// A simpleType restriction cycle s0 -> s1 -> s0.
#expect(!compiles(
""
+ "",
))
}
@Test("Recursive element content and normal derivation are not cycles")
func test_recursionAndNormalDerivationAccepted() {
// A type containing an element of its own type is a recursive data structure,
// not a derivation cycle: only the base chain is a cycle, element content is
// not. (Must compile.)
#expect(compiles(
""
+ "",
))
// Deriving from a ur-type terminates the chain (the schema-for-schemas case).
#expect(compiles(
"",
))
// A normal restriction chain A -> base -> xs:string.
#expect(compiles(
""
+ "",
))
}
@Test("A type whose base is a same-local-name type in another namespace is not a cycle")
func test_crossNamespaceSameLocalNameAccepted() {
// local:X extends the entirely different foreign:X. Resolving by local name
// would falsely report a self-cycle; the namespace-aware walk follows only
// a base in this schema's own target namespace, so the foreign base ends the
// chain. (Must compile.)
let schema = ""
+ ""
+ ""
+ ""
#expect((try? PureXML.Schema.Document(schema)) != nil)
}
@Test("Circular model-group, attribute-group, and substitution-group references are rejected")
func test_referenceCyclesRejected() {
// A model group that directly contains itself (mg-props-correct.2).
#expect(!compiles(
""
+ "",
))
// An attribute-group reference cycle a -> b -> a (ag-props-correct.3).
#expect(!compiles(
""
+ "",
))
// A substitution-group affiliation loop x -> y -> x (e-props-correct.6).
#expect(!compiles(
"",
))
}
@Test("A group recursing through an element, and a substitution chain, are allowed")
func test_groupRecursionAndChainAccepted() {
// particlesZ010: a group referenced inside an element's content is a recursive
// data structure, not a group containing itself. (Must compile.)
#expect(compiles(
""
+ ""
+ ""
+ "",
))
// A non-cyclic substitution chain leaf -> mid -> head.
#expect(compiles(
""
+ "",
))
}
@Test("A type redefining itself is allowed (the base names its former self)")
func test_redefineSelfDerivationAccepted() {
let base = ""
+ ""
+ ""
let body = ""
+ ""
+ ""
+ ""
+ ""
#expect(compiles(body, loader: { $0 == "base.xsd" ? base : nil }))
}
}