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 })) } }