Group @testable imports at the bottom, per public/rules-swift's swiftformat. A behavior-preserving import-ordering reformat across 252 files (mostly tests). --trailingclosures was deliberately not adopted (cosmetic, and swiftformat flags it as caution-worthy).
226 lines
9.3 KiB
Swift
226 lines
9.3 KiB
Swift
import Testing
|
|
@testable import PureXML
|
|
|
|
/// Component-name uniqueness (XSD 1.0 symbol-space constraints): within a schema,
|
|
/// global type names (simpleType and complexType share one space), global element
|
|
/// names, global attribute names, named model-group names, and named
|
|
/// attribute-group names must each be unique, and identity-constraint names must
|
|
/// be unique across the document. A duplicate was previously accepted, with the
|
|
/// later definition silently overwriting the earlier one.
|
|
@Suite("Schema component-name uniqueness")
|
|
struct SchemaNameUniquenessTests {
|
|
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("distinct global names compile")
|
|
func test_valid() throws {
|
|
try compile(#"""
|
|
<xs:simpleType name="t"><xs:restriction base="xs:string"/></xs:simpleType>
|
|
<xs:complexType name="c"><xs:sequence><xs:element name="a" type="xs:string"/></xs:sequence></xs:complexType>
|
|
<xs:element name="e" type="t"/>
|
|
"""#)
|
|
// A name shared across different symbol spaces (a type and an element) is
|
|
// allowed.
|
|
try compile(#"""
|
|
<xs:element name="x" type="xs:string"/>
|
|
<xs:complexType name="x"><xs:sequence/></xs:complexType>
|
|
"""#)
|
|
}
|
|
|
|
@Test("a duplicate global type name is rejected (one symbol space)")
|
|
func test_duplicateType() {
|
|
#expect(rejects(#"<xs:simpleType name="t"><xs:restriction base="xs:string"/></xs:simpleType><xs:simpleType name="t"><xs:restriction base="xs:int"/></xs:simpleType>"#))
|
|
// simpleType and complexType share the type symbol space.
|
|
#expect(rejects(#"<xs:simpleType name="t"><xs:restriction base="xs:string"/></xs:simpleType><xs:complexType name="t"><xs:sequence/></xs:complexType>"#))
|
|
}
|
|
|
|
@Test("a duplicate global element or attribute name is rejected")
|
|
func test_duplicateElementAttribute() {
|
|
#expect(rejects(#"<xs:element name="e" type="xs:string"/><xs:element name="e" type="xs:int"/>"#))
|
|
#expect(rejects(#"<xs:attribute name="a" type="xs:string"/><xs:attribute name="a" type="xs:int"/>"#))
|
|
}
|
|
|
|
@Test("a keyref refer must name a key or unique")
|
|
func test_keyrefRefer() throws {
|
|
// refer to a defined key resolves.
|
|
try compile(#"""
|
|
<xs:element name="root">
|
|
<xs:complexType><xs:sequence><xs:element ref="a"/></xs:sequence></xs:complexType>
|
|
<xs:key name="k"><xs:selector xpath="a"/><xs:field xpath="@id"/></xs:key>
|
|
<xs:keyref name="r" refer="k"><xs:selector xpath="a"/><xs:field xpath="@ref"/></xs:keyref>
|
|
</xs:element>
|
|
<xs:element name="a"><xs:complexType><xs:attribute name="id" type="xs:string"/><xs:attribute name="ref" type="xs:string"/></xs:complexType></xs:element>
|
|
"""#)
|
|
// refer to a name that is not a key/unique is rejected.
|
|
#expect(rejects(#"""
|
|
<xs:element name="root">
|
|
<xs:complexType><xs:sequence><xs:element ref="a"/></xs:sequence></xs:complexType>
|
|
<xs:keyref name="r" refer="ghost"><xs:selector xpath="a"/><xs:field xpath="@ref"/></xs:keyref>
|
|
</xs:element>
|
|
<xs:element name="a"><xs:complexType><xs:attribute name="ref" type="xs:string"/></xs:complexType></xs:element>
|
|
"""#))
|
|
}
|
|
|
|
@Test("a keyref arity must match its referenced key or unique")
|
|
func test_keyrefArityMismatch() {
|
|
// keyref and key have different arities (1 vs 2): must be rejected.
|
|
#expect(rejects(#"""
|
|
<xs:element name="root">
|
|
<xs:complexType><xs:sequence><xs:element ref="a"/></xs:sequence></xs:complexType>
|
|
<xs:key name="k">
|
|
<xs:selector xpath="a"/>
|
|
<xs:field xpath="@id1"/>
|
|
<xs:field xpath="@id2"/>
|
|
</xs:key>
|
|
<xs:keyref name="r" refer="k">
|
|
<xs:selector xpath="a"/>
|
|
<xs:field xpath="@ref"/>
|
|
</xs:keyref>
|
|
</xs:element>
|
|
<xs:element name="a">
|
|
<xs:complexType>
|
|
<xs:attribute name="id1" type="xs:string"/>
|
|
<xs:attribute name="id2" type="xs:string"/>
|
|
<xs:attribute name="ref" type="xs:string"/>
|
|
</xs:complexType>
|
|
</xs:element>
|
|
"""#))
|
|
}
|
|
|
|
@Test("a duplicate identity-constraint name is rejected")
|
|
func test_duplicateIdentityConstraint() {
|
|
#expect(rejects(#"""
|
|
<xs:element name="root">
|
|
<xs:complexType><xs:sequence><xs:element ref="a"/></xs:sequence></xs:complexType>
|
|
<xs:unique name="dup"><xs:selector xpath="a"/><xs:field xpath="@id"/></xs:unique>
|
|
<xs:unique name="dup"><xs:selector xpath="a"/><xs:field xpath="@k"/></xs:unique>
|
|
</xs:element>
|
|
<xs:element name="a"><xs:complexType><xs:attribute name="id" type="xs:string"/><xs:attribute name="k" type="xs:string"/></xs:complexType></xs:element>
|
|
"""#))
|
|
}
|
|
|
|
@Test("duplicate identity-constraint names across included files are rejected")
|
|
func test_duplicateIdentityConstraintAcrossIncluded() {
|
|
let imported = """
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
<xs:element name="roota">
|
|
<xs:complexType><xs:sequence/></xs:complexType>
|
|
<xs:key name="KEY">
|
|
<xs:selector xpath="person"/>
|
|
<xs:field xpath="."/>
|
|
</xs:key>
|
|
</xs:element>
|
|
</xs:schema>
|
|
"""
|
|
let main = """
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
<xs:include schemaLocation="sub.xsd"/>
|
|
<xs:element name="root">
|
|
<xs:complexType><xs:sequence/></xs:complexType>
|
|
<xs:key name="KEY">
|
|
<xs:selector xpath="person"/>
|
|
<xs:field xpath="."/>
|
|
</xs:key>
|
|
</xs:element>
|
|
</xs:schema>
|
|
"""
|
|
#expect((try? PureXML.Schema.Document(main, schemaLoader: { $0 == "sub.xsd" ? imported : nil })) == nil)
|
|
}
|
|
|
|
@Test("duplicate global element names across included files are rejected")
|
|
func test_duplicateGlobalElementAcrossIncluded() {
|
|
let imported = """
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
<xs:element name="dup" type="xs:string"/>
|
|
</xs:schema>
|
|
"""
|
|
let main = """
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
<xs:include schemaLocation="sub.xsd"/>
|
|
<xs:element name="dup" type="xs:int"/>
|
|
</xs:schema>
|
|
"""
|
|
#expect((try? PureXML.Schema.Document(main, schemaLoader: { $0 == "sub.xsd" ? imported : nil })) == nil)
|
|
}
|
|
|
|
@Test("debug name00101m2")
|
|
func test_debugName() {
|
|
let path = "/private/tmp/xsts/xmlschema2006-11-06/sunData/IdConstrDefs/name/name00101m/name00101m2.xsd"
|
|
guard let source = try? String(contentsOfFile: path) else {
|
|
return
|
|
}
|
|
do {
|
|
_ = try PureXML.Schema.Document(source)
|
|
} catch {
|
|
// Expected
|
|
}
|
|
}
|
|
|
|
@Test("duplicate global names in different imported files are rejected even if files are structurally identical")
|
|
func test_duplicateAcrossSeparateFilesIdenticalContent() {
|
|
let imported = """
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://imported">
|
|
<xs:attributeGroup name="car">
|
|
<xs:attribute name="model"/>
|
|
</xs:attributeGroup>
|
|
</xs:schema>
|
|
"""
|
|
let main = """
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://main" xmlns:imp="http://imported">
|
|
<xs:import namespace="http://imported" schemaLocation="sub1.xsd"/>
|
|
<xs:import namespace="http://imported" schemaLocation="sub2.xsd"/>
|
|
</xs:schema>
|
|
"""
|
|
#expect((try? PureXML.Schema.Document(main, schemaLoader: { location in
|
|
if location == "sub1.xsd" || location == "sub2.xsd" {
|
|
return imported
|
|
}
|
|
return nil
|
|
})) == nil)
|
|
}
|
|
|
|
@Test("circular import of the same schema does not trigger duplicate global name error")
|
|
func test_circularImportSelfDeduplication() throws {
|
|
let imported = """
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://imported" xmlns:main="http://main">
|
|
<xs:import namespace="http://main" schemaLocation="main.xsd"/>
|
|
<xs:complexType name="A">
|
|
<xs:sequence>
|
|
<xs:element name="x" type="main:B"/>
|
|
</xs:sequence>
|
|
</xs:complexType>
|
|
</xs:schema>
|
|
"""
|
|
let main = """
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://main" xmlns:imp="http://imported">
|
|
<xs:import namespace="http://imported" schemaLocation="sub.xsd"/>
|
|
<xs:complexType name="B">
|
|
<xs:sequence>
|
|
<xs:element name="y" type="xs:string"/>
|
|
</xs:sequence>
|
|
</xs:complexType>
|
|
</xs:schema>
|
|
"""
|
|
_ = try PureXML.Schema.Document(main, schemaLoader: { location in
|
|
if location == "sub.xsd" {
|
|
return imported
|
|
}
|
|
if location == "main.xsd" {
|
|
return main
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
}
|