import Testing
@testable import PureXML
@Suite("XSD include composition (#161)")
struct SchemaIncludeTests {
@Test("chameleon include: no-namespace library merges into a target-namespace main schema")
func test_chameleonIncludeWithTargetNamespace() throws {
let library = """
"""
let main = """
"""
let doc = try PureXML.Schema.Document(main, schemaLoader: { $0 == "lib.xsd" ? library : nil })
#expect(try doc.validate("ABC").isEmpty)
#expect(try !doc.validate("abc").isEmpty)
}
/// src-redefine.5: a type inside xs:redefine must restrict/extend the type it
/// redefines, which lives in the redefining schema's own target namespace. A base
/// bound to a foreign namespace with the same local name (an imported type) is not
/// self and is rejected; an unprefixed/own-namespace self-reference is valid.
@Test("a redefined type must derive from itself, not a same-named foreign type")
func test_redefineBaseMustBeSelfNamespace() {
let redefined = """
"""
let imported = """
"""
let loader: (String) -> String? = { $0 == "a.xsd" ? redefined : ($0 == "foo.xsd" ? imported : nil) }
// Redefine of T whose extension base is foo:T (a foreign same-local-name type).
let foreign = """
"""
#expect((try? PureXML.Schema.Document(foreign, schemaLoader: loader)) == nil)
// A proper self-redefine (base resolves to the redefining target namespace).
let selfRedefine = """
"""
#expect((try? PureXML.Schema.Document(selfRedefine, schemaLoader: loader)) != nil)
}
/// src-redefine.6.1.2: a group inside xs:redefine may reference itself, but that
/// self-reference must occur exactly once (minOccurs = maxOccurs = 1). A
/// self-reference with minOccurs="0" or maxOccurs="unbounded" is invalid; a unit
/// self-reference (or absent occurrence, defaulting to 1) is valid.
@Test("a redefined group's self-reference must occur exactly once")
func test_redefineGroupSelfReferenceOccurrence() {
let original = """
"""
let loader: (String) -> String? = { $0 == "a.xsd" ? original : nil }
func redefine(_ selfRef: String) -> String {
"""
\(selfRef)
"""
}
// A non-unit self-reference is invalid.
#expect((try? PureXML.Schema.Document(redefine(#""#), schemaLoader: loader)) == nil)
#expect((try? PureXML.Schema.Document(redefine(#""#), schemaLoader: loader)) == nil)
// A unit self-reference is valid.
#expect((try? PureXML.Schema.Document(redefine(#""#), schemaLoader: loader)) != nil)
}
/// A redefined schema's targetNamespace must match the redefining schema's (or be
/// chameleon, no targetNamespace), exactly as for include; a redefine of a
/// different-namespace schema is invalid.
@Test("a redefine's schema must match the redefiner's targetNamespace")
func test_redefineTargetNamespaceMatch() {
let main = """
"""
func redefined(_ namespace: String) -> String {
""
}
// A different targetNamespace is invalid; the same one is valid.
#expect((try? PureXML.Schema.Document(main, schemaLoader: { $0 == "r.xsd" ? redefined(" targetNamespace=\"urn:other\"") : nil })) == nil)
#expect((try? PureXML.Schema.Document(main, schemaLoader: { $0 == "r.xsd" ? redefined(" targetNamespace=\"urn:a\"") : nil })) != nil)
}
/// src-redefine.7.2.1: a redefined attributeGroup may contain at most one
/// self-reference; two references to the group being redefined are invalid.
@Test("a redefined attributeGroup may have at most one self-reference")
func test_redefineAttributeGroupSelfReferenceCount() {
let original = """
"""
let loader: (String) -> String? = { $0 == "a.xsd" ? original : nil }
func redefine(_ refs: String) -> String {
"""
\(refs)
"""
}
// Two self-references are invalid; one is valid.
#expect((try? PureXML.Schema.Document(redefine(#""#), schemaLoader: loader)) == nil)
#expect((try? PureXML.Schema.Document(redefine(#""#), schemaLoader: loader)) != nil)
}
/// src-resolve: when an include/import/redefine schemaLocation IS resolved (the
/// loader returns content), that content must be a well-formed schema. Not-well-
/// formed XML, or well-formed XML that is not an xs:schema, is rejected. A
/// location the loader does not resolve (returns nil) is not an error.
@Test("a resolved schemaLocation that is not a valid schema is rejected")
func test_resolvedSchemaLocationMustBeSchema() {
let main = """
"""
// Resolved to not-well-formed XML.
#expect(throws: Error.self) {
_ = try PureXML.Schema.Document(main, schemaLoader: { $0 == "bad.xsd" ? "" : nil })
}
// An UNRESOLVED location (loader returns nil) is not an error.
#expect((try? PureXML.Schema.Document(main, schemaLoader: { _ in nil })) != nil)
}
/// src-import.3.1/3.2: an import's namespace attribute must equal the imported
/// schema's targetNamespace (both equal, or both absent). Checked only when the
/// schemaLocation resolves to a loaded schema.
@Test("an import's namespace must match the imported targetNamespace")
func test_importNamespaceMustMatchImportedTarget() {
let mainNS = """
"""
// Imported doc's targetNamespace does NOT match the declared import namespace.
let mismatched = ""
#expect((try? PureXML.Schema.Document(mainNS, schemaLoader: { $0 == "b.xsd" ? mismatched : nil })) == nil)
// Imported doc has NO targetNamespace but the import declares one.
let noTarget = ""
#expect((try? PureXML.Schema.Document(mainNS, schemaLoader: { $0 == "b.xsd" ? noTarget : nil })) == nil)
// Matching targetNamespace compiles.
let matched = ""
#expect((try? PureXML.Schema.Document(mainNS, schemaLoader: { $0 == "b.xsd" ? matched : nil })) != nil)
// An unresolved import (loader returns nil) is not an error.
#expect((try? PureXML.Schema.Document(mainNS, schemaLoader: { _ in nil })) != nil)
}
@Test("xs:include with a mismatched targetNamespace is rejected")
func test_invalidIncludeDifferentTargetNamespace() {
let included = """
"""
let main = """
"""
#expect(throws: Error.self) {
_ = try PureXML.Schema.Document(main, schemaLoader: { $0 == "other.xsd" ? included : nil })
}
}
@Test("xs:include with matching targetNamespace is accepted")
func test_includeMatchingTargetNamespace() throws {
let included = """
"""
let main = """
"""
let doc = try PureXML.Schema.Document(main, schemaLoader: { $0 == "lib.xsd" ? included : nil })
#expect(try doc.validate("ABC").isEmpty)
}
@Test("chameleon include: attribute refs inside the included schema resolve after merge")
func test_chameleonIncludeAttributeGroupRef() throws {
let library = """
"""
let main = """
"""
let doc = try PureXML.Schema.Document(main, schemaLoader: { $0 == "lib.xsd" ? library : nil })
#expect(try doc.validate("").isEmpty)
}
@Test("undeclared refs inside an included chameleon schema are rejected at compile time")
func test_invalidReferenceInsideIncludedSchema() {
let library = """
"""
let main = """
"""
#expect(throws: Error.self) {
_ = try PureXML.Schema.Document(main, schemaLoader: { $0 == "lib.xsd" ? library : nil })
}
}
}