isUndeclaredReferenceRef fell through to a lenient accept when a ref or substitutionGroup resolved to the XSD namespace but the schema did not target it. The XSD namespace declares no referenceable user component (element, group, attribute, attribute group), so such a reference is undeclared, the asymmetry isUndeclaredReferenceType already handled for type/base. This is reached when an unprefixed reference picks up a default xmlns of the XSD namespace, so it is not in the target namespace where the schema's own components live: xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="foo" with <element ref="root"/> resolves to {XSD}root, not {foo}root (XSTS xsd013/xsd014). A schema that itself targets the XSD namespace is the inTargetNamespace case, and a genuinely loaded XSD-namespace document is the foreignPools case before this branch, so both are unaffected. Verified against the full W3C XSTS 2006-11-06 suite: invalid-schemas-accepted 29 -> 27, valid-schemas-rejected held at 0, no other bucket moved. Adds reference tests for both reject cases and a qualified-ref guard.
137 lines
6.5 KiB
Swift
137 lines
6.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("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>
|
|
""")
|
|
}
|
|
}
|