isUndeclaredReferenceType matched a no-namespace type/base/itemType/memberTypes
reference by local name, ignoring that an unprefixed QName with no default
xmlns resolves to the absent namespace: type="t" is {}t, not {urn:x}t where the
type is declared, so the reference does not resolve (XSD 1.0 §3.15.3). It now
mirrors isUndeclaredReferenceRef's nil-namespace branch: a no-namespace
reference is undeclared when the schema has a non-empty target namespace; a
chameleon-included global and an imported no-namespace type (via the foreign
pools, checked first) remain accepted.
This corrects an asymmetry where the ref side already rejected the pattern but
the type side did not. The existing SchemaXSIAttributeTests case that asserted
this very pattern compiles used an invalid schema (the same shape as XSTS
addB009/test66059); its type reference is now qualified (tns:attRef), which
preserves the test's xsi:Not-Allowed intent while being valid.
Verified against the full W3C XSTS 2006-11-06 suite: invalid-schemas-accepted
27 -> 25 (addB009/test66059 and one more), valid-schemas-rejected held at 0,
no other bucket moved. Adds a reference test for the type-side rule.
158 lines
7.5 KiB
Swift
158 lines
7.5 KiB
Swift
import Testing
|
|
@testable import PureXML
|
|
|
|
/// Every QName a schema names (a `type`/`base`/`itemType`/`memberTypes` type, an
|
|
/// element/attribute/group/attributeGroup `ref`, an element `substitutionGroup`)
|
|
/// must resolve to a declared component or a built-in. An undeclared reference
|
|
/// was previously accepted at compile time and only surfaced (if at all) lazily
|
|
/// during instance validation. The check is skipped when the document pulls in
|
|
/// external definitions through `import`/`include`/`redefine`, which the default
|
|
/// compile does not load.
|
|
@Suite("Schema reference resolution")
|
|
struct SchemaReferenceTests {
|
|
private func compile(_ body: String) throws {
|
|
_ = try PureXML.Schema.Document("""
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
\(body)
|
|
</xs:schema>
|
|
""")
|
|
}
|
|
|
|
private func rejects(_ body: String) -> Bool {
|
|
do { try compile(body)
|
|
return false
|
|
} catch { return true }
|
|
}
|
|
|
|
@Test("a reference to a declared component or a built-in resolves")
|
|
func test_resolves() throws {
|
|
try compile(#"<xs:element name="a" type="xs:string"/>"#)
|
|
try compile(#"<xs:simpleType name="t"><xs:restriction base="xs:integer"/></xs:simpleType><xs:element name="a" type="t"/>"#)
|
|
try compile(#"<xs:element name="g" type="xs:string"/><xs:complexType name="c"><xs:sequence><xs:element ref="g"/></xs:sequence></xs:complexType>"#)
|
|
}
|
|
|
|
@Test("an undeclared reference is rejected")
|
|
func test_undeclared() {
|
|
#expect(rejects(#"<xs:element name="a" type="DoesNotExist"/>"#))
|
|
#expect(rejects(#"<xs:simpleType name="t"><xs:restriction base="Missing"/></xs:simpleType>"#))
|
|
#expect(rejects(#"<xs:complexType name="c"><xs:sequence><xs:element ref="ghost"/></xs:sequence></xs:complexType>"#))
|
|
#expect(rejects(#"<xs:complexType name="c"><xs:group ref="noGroup"/></xs:complexType>"#))
|
|
}
|
|
|
|
@Test("a reference value tolerates surrounding whitespace (whiteSpace collapse)")
|
|
func test_whitespacePadded() throws {
|
|
try compile("<xs:simpleType name=\"t\"><xs:restriction base=\" xs:string \"/></xs:simpleType>")
|
|
}
|
|
|
|
@Test("a ref resolving to the XSD namespace is undeclared")
|
|
func test_refResolvingToXSDNamespaceIsUndeclared() throws {
|
|
// The default xmlns is the XSD namespace, so an unprefixed `ref="root"`
|
|
// resolves to {XSD}root, not {foo}root, and the XSD namespace declares no
|
|
// referenceable user element: undeclared (XSTS xsd013/xsd014).
|
|
let refRoot = """
|
|
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:foo="foo" targetNamespace="foo">
|
|
<element name="root"><complexType><sequence><element ref="root"/></sequence></complexType></element>
|
|
</schema>
|
|
"""
|
|
#expect(throws: (any Error).self) { _ = try PureXML.Schema.Document(refRoot) }
|
|
let refUndef = """
|
|
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:foo="foo" targetNamespace="foo">
|
|
<element name="root"><complexType><sequence><element ref="undef"/></sequence></complexType></element>
|
|
</schema>
|
|
"""
|
|
#expect(throws: (any Error).self) { _ = try PureXML.Schema.Document(refUndef) }
|
|
// Guard: the same shape with the ref qualified to the target resolves, so the
|
|
// XSD-namespace rule does not reject a correctly-namespaced reference.
|
|
let qualified = """
|
|
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:foo="foo" targetNamespace="foo">
|
|
<element name="root"><complexType><sequence><element ref="foo:root"/></sequence></complexType></element>
|
|
</schema>
|
|
"""
|
|
_ = try PureXML.Schema.Document(qualified)
|
|
}
|
|
|
|
@Test("an unprefixed type reference to a target-namespace type is undeclared")
|
|
func test_unprefixedTypeRefToTargetNamespace() throws {
|
|
// No default xmlns, so `type="t"` resolves to {}t, not {urn:x}t where the
|
|
// type is declared: undeclared (XSTS addB009 / test66059).
|
|
let unprefixed = """
|
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:x">
|
|
<xsd:complexType name="t"><xsd:sequence/></xsd:complexType>
|
|
<xsd:element name="e" type="t"/>
|
|
</xsd:schema>
|
|
"""
|
|
#expect(throws: (any Error).self) { _ = try PureXML.Schema.Document(unprefixed) }
|
|
// Guard: qualifying the reference to the target namespace resolves.
|
|
let qualified = """
|
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:x="urn:x" targetNamespace="urn:x">
|
|
<xsd:complexType name="t"><xsd:sequence/></xsd:complexType>
|
|
<xsd:element name="e" type="x:t"/>
|
|
</xsd:schema>
|
|
"""
|
|
_ = try PureXML.Schema.Document(qualified)
|
|
}
|
|
|
|
@Test("references are not checked when an import may supply them")
|
|
func test_importSkipsCheck() throws {
|
|
// A dangling reference into an imported namespace is tolerated: the default
|
|
// compile does not load the import, so the reference cannot be verified.
|
|
try compile(#"""
|
|
<xs:import namespace="urn:other"/>
|
|
<xs:element name="a" type="other:Foo" xmlns:other="urn:other"/>
|
|
"""#)
|
|
}
|
|
|
|
@Test("references are checked strictly against the targetNamespace")
|
|
func test_referenceNamespaceChecking() throws {
|
|
// Without targetNamespace, unprefixed ref resolves to no namespace and is accepted
|
|
_ = try PureXML.Schema.Document("""
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
<xs:attribute name="number" type="xs:integer"/>
|
|
<xs:element name="elementWithAttr">
|
|
<xs:complexType>
|
|
<xs:attribute ref="number" use="required"/>
|
|
</xs:complexType>
|
|
</xs:element>
|
|
</xs:schema>
|
|
""")
|
|
|
|
// With targetNamespace, unprefixed ref without default namespace resolves to no namespace but declaration is in targetNamespace -> rejected
|
|
#expect(throws: Error.self) {
|
|
_ = try PureXML.Schema.Document("""
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="AttrUse/attrDecl" xmlns:tn="AttrUse/attrDecl">
|
|
<xs:attribute name="number" type="xs:integer"/>
|
|
<xs:element name="elementWithAttr">
|
|
<xs:complexType>
|
|
<xs:attribute ref="number" use="required"/>
|
|
</xs:complexType>
|
|
</xs:element>
|
|
</xs:schema>
|
|
""")
|
|
}
|
|
|
|
// With targetNamespace, prefix matching targetNamespace is accepted
|
|
_ = try PureXML.Schema.Document("""
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="AttrUse/attrDecl" xmlns:tn="AttrUse/attrDecl">
|
|
<xs:attribute name="number" type="xs:integer"/>
|
|
<xs:element name="elementWithAttr">
|
|
<xs:complexType>
|
|
<xs:attribute ref="tn:number" use="required"/>
|
|
</xs:complexType>
|
|
</xs:element>
|
|
</xs:schema>
|
|
""")
|
|
|
|
// With targetNamespace, default namespace matching targetNamespace is accepted
|
|
_ = try PureXML.Schema.Document("""
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="AttrUse/attrDecl" xmlns="AttrUse/attrDecl">
|
|
<xs:attribute name="number" type="xs:integer"/>
|
|
<xs:element name="elementWithAttr">
|
|
<xs:complexType>
|
|
<xs:attribute ref="number" use="required"/>
|
|
</xs:complexType>
|
|
</xs:element>
|
|
</xs:schema>
|
|
""")
|
|
}
|
|
}
|