PureXML/Tests/SchematronAbstractPatternTests.swift
Mihaela Mihaljevic 83e6e27d12 style(format): adopt --importgrouping testable-bottom from canonical rules-swift
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).
2026-06-18 20:33:07 +02:00

101 lines
3.9 KiB
Swift

import Testing
@testable import PureXML
@Suite("Schematron abstract patterns (is-a / param)")
struct SchematronAbstractPatternTests {
private func validate(_ schema: String, _ xml: String) throws -> [PureXML.Validation.ValidationError] {
try PureXML.Validation.Schematron(schema: schema).validate(xml)
}
private func reasons(_ schema: String, _ xml: String) throws -> [String] {
try validate(schema, xml).map(\.reason)
}
@Test("An is-a pattern substitutes $params into context and test")
func test_paramSubstitution() throws {
let schema = """
<schema xmlns="http://purl.oclc.org/dsdl/schematron">
<pattern abstract="true" id="required">
<rule context="$element">
<assert test="$attribute">missing</assert>
</rule>
</pattern>
<pattern is-a="required" id="car-needs-wheels">
<param name="element" value="car"/>
<param name="attribute" value="@wheels"/>
</pattern>
</schema>
"""
#expect(try validate(schema, "<car wheels='4'/>").isEmpty)
let failed = try reasons(schema, "<car/>")
#expect(failed.count == 1)
#expect(failed.first?.contains("missing") == true)
}
@Test("The abstract template itself is not evaluated")
func test_abstractTemplateInert() throws {
// $ctx as a raw context would select nothing useful if evaluated; the
// template must contribute no rules of its own.
let schema = """
<schema xmlns="http://purl.oclc.org/dsdl/schematron">
<pattern abstract="true" id="t">
<rule context="$ctx"><assert test="$cond">x</assert></rule>
</pattern>
</schema>
"""
#expect(try validate(schema, "<doc/>").isEmpty)
}
@Test("One template instantiates for two different concrete patterns")
func test_reuseTemplate() throws {
let schema = """
<schema xmlns="http://purl.oclc.org/dsdl/schematron">
<pattern abstract="true" id="present">
<rule context="$el"><assert test="count(.) = 1">absent</assert></rule>
</pattern>
<pattern is-a="present" id="a"><param name="el" value="alpha"/></pattern>
<pattern is-a="present" id="b"><param name="el" value="beta"/></pattern>
</schema>
"""
#expect(try validate(schema, "<root><alpha/><beta/></root>").isEmpty)
}
@Test("A longer param name is not captured by a shorter one")
func test_noPrefixCapture() throws {
let schema = """
<schema xmlns="http://purl.oclc.org/dsdl/schematron">
<pattern abstract="true" id="t">
<rule context="$n"><assert test="@$nm = 'ok'">bad</assert></rule>
</pattern>
<pattern is-a="t" id="c">
<param name="n" value="item"/>
<param name="nm" value="status"/>
</pattern>
</schema>
"""
// $nm must map to "status", not be eaten by $n -> "item" + "m".
#expect(try validate(schema, "<item status='ok'/>").isEmpty)
#expect(try !validate(schema, "<item status='no'/>").isEmpty)
}
@Test("A $name that is not a param (an ordinary let variable) is preserved")
func test_letVariablePreserved() throws {
let schema = """
<schema xmlns="http://purl.oclc.org/dsdl/schematron">
<pattern abstract="true" id="t">
<rule context="$el">
<let name="actual" value="@count"/>
<assert test="$actual = $expected">wrong count</assert>
</rule>
</pattern>
<pattern is-a="t" id="c">
<param name="el" value="bag"/>
<param name="expected" value="3"/>
</pattern>
</schema>
"""
#expect(try validate(schema, "<bag count='3'/>").isEmpty)
#expect(try !validate(schema, "<bag count='2'/>").isEmpty)
}
}