use super::super::info::{NoNamespaceSchemaLocationHint, SchemaLocationHint};
use super::super::validator::SchemaValidator;
use super::*;
use crate::namespace::context::NamespaceContextSnapshot;
use crate::pipeline::load_and_process_schema;
struct TestSink {
errors: Vec<ValidationError>,
warnings: Vec<ValidationWarning>,
}
impl TestSink {
fn new() -> Self {
TestSink {
errors: Vec::new(),
warnings: Vec::new(),
}
}
}
impl ValidationSink for TestSink {
fn on_error(&mut self, error: ValidationError) {
self.errors.push(error);
}
fn on_warning(&mut self, warning: ValidationWarning) {
self.warnings.push(warning);
}
}
fn empty_ns_context() -> NamespaceContextSnapshot {
NamespaceContextSnapshot {
default_ns: None,
bindings: Vec::new(),
}
}
fn load_schema(xsd: &str) -> SchemaSet {
let mut schema_set = SchemaSet::new();
load_and_process_schema(xsd.as_bytes(), "test.xsd", &mut schema_set, None)
.expect("failed to load schema");
schema_set
}
#[cfg(feature = "xsd11")]
fn load_schema_xsd11(xsd: &str) -> SchemaSet {
let mut schema_set = SchemaSet::xsd11();
load_and_process_schema(xsd.as_bytes(), "test.xsd", &mut schema_set, None)
.expect("failed to load schema");
schema_set
}
#[test]
fn test_simple_element_valid() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let info = v.validate_element("root", "", None, None, &ns);
assert_eq!(info.validity, SchemaValidity::Valid);
assert!(info.element_decl.is_some());
assert!(info.schema_type.is_some());
v.validate_end_of_attributes();
v.validate_text("hello world");
let end_info = v.validate_end_element();
assert_eq!(end_info.validity, SchemaValidity::Valid);
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_unknown_element_strict() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let info = v.validate_element("unknown", "", None, None, &ns);
assert_eq!(info.validity, SchemaValidity::Invalid);
assert!(v.sink.errors.iter().any(|e| e.constraint == "cvc-elt.1"));
v.validate_end_of_attributes();
v.validate_end_element();
assert!(v.end_validation().is_ok());
}
#[test]
fn test_sequence_content_model() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="a" type="xs:string"/>
<xs:element name="b" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("a", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_element("b", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("world");
v.validate_end_element();
v.validate_end_element();
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_sequence_wrong_order() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="a" type="xs:string"/>
<xs:element name="b" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("b", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("a", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-complex-type.2.4"),
"errors: {:?}",
v.sink.errors
);
}
#[test]
fn test_required_attribute_missing() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="id" type="xs:string" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-complex-type.4"),
"expected required attribute error, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_duplicate_attribute() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="id" type="xs:string"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("id", "", "val1");
v.validate_attribute("id", "", "val2"); v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-complex-type.3"),
"expected duplicate attribute error, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_text_in_empty_content() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType/>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("not allowed");
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-complex-type.2.1"),
"expected empty content error, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_state_machine_attribute_before_element() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let info = v.validate_attribute("id", "", "val");
assert_eq!(info.validity, SchemaValidity::Invalid);
assert!(!v.sink.errors.is_empty());
}
#[test]
fn test_xsi_type_override() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:anyType"/>
<xs:complexType name="myType">
<xs:sequence>
<xs:element name="child" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let info = v.validate_element("root", "", Some("myType"), None, &ns);
assert_eq!(info.validity, SchemaValidity::Valid);
assert!(info.schema_type.is_some());
v.validate_end_of_attributes();
v.validate_element("child", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_xsi_nil_on_nillable_element() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="child" type="xs:string" nillable="true"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
let info = v.validate_element("child", "", None, Some("true"), &ns);
assert!(info.is_nil);
assert_eq!(info.validity, SchemaValidity::Valid);
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_end_validation_with_unclosed_elements() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
let result = v.end_validation();
assert!(result.is_err());
}
#[test]
fn test_local_element_with_complex_type() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="addressType">
<xs:sequence>
<xs:element name="street" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="address" type="addressType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
let info = v.validate_element("address", "", None, None, &ns);
assert_eq!(info.validity, SchemaValidity::Valid);
assert!(
info.schema_type.is_some(),
"local element should have resolved type"
);
assert!(
matches!(info.content_type, Some(ContentType::ElementOnly)),
"addressType has element-only content, got {:?}",
info.content_type,
);
v.validate_end_of_attributes();
v.validate_element("street", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("123 Main St");
v.validate_end_element();
v.validate_element("city", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("Springfield");
v.validate_end_element();
v.validate_end_element(); v.validate_end_element(); assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_local_element_with_simple_type_resolved() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="count" type="xs:integer"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
let info = v.validate_element("count", "", None, None, &ns);
assert_eq!(info.validity, SchemaValidity::Valid);
assert!(
info.schema_type.is_some(),
"local element should have resolved type for xs:integer"
);
assert_eq!(info.content_type, Some(ContentType::TextOnly));
v.validate_end_of_attributes();
v.validate_text("42");
v.validate_end_element();
v.validate_end_element();
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_local_element_complex_type_rejects_wrong_children() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="myType">
<xs:sequence>
<xs:element name="expected" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" type="myType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("wrong", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element(); v.validate_end_element(); v.end_validation().ok();
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-complex-type.2.4"),
"expected content model error for wrong child, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_local_element_with_inline_type() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="code">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="10"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let root_name = schema_set
.name_table
.get("root")
.expect("name 'root' not interned");
let root_key = schema_set
.lookup_element(None, root_name)
.expect("root element not found");
let root_type = schema_set.arenas.elements[root_key]
.resolved_type
.expect("root element has no resolved_type");
let ct_key = match root_type {
crate::ids::TypeKey::Complex(k) => k,
_ => panic!("root type is not complex"),
};
let ct = &schema_set.arenas.complex_types[ct_key];
assert!(
!ct.resolved_content_particle_types.is_empty(),
"resolved_content_particle_types is empty"
);
assert!(
ct.resolved_content_particle_types[0].is_some(),
"resolved_content_particle_types[0] is None"
);
{
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
let info = v.validate_element("code", "", None, None, &ns);
assert_eq!(info.validity, SchemaValidity::Valid);
assert!(info.schema_type.is_some(), "inline type not resolved");
assert_eq!(info.content_type, Some(ContentType::TextOnly));
v.validate_end_of_attributes();
v.validate_text("ABC");
v.validate_end_element();
v.validate_end_element();
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
{
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("code", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("this text exceeds maxLength of 10");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
!v.sink.errors.is_empty(),
"expected facet error for text exceeding maxLength=10"
);
}
}
#[test]
fn test_xsi_type_on_local_element() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="baseType">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="derivedType">
<xs:complexContent>
<xs:extension base="baseType">
<xs:sequence>
<xs:element name="extra" type="xs:string"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" type="baseType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
let info = v.validate_element("item", "", Some("derivedType"), None, &ns);
assert_eq!(info.validity, SchemaValidity::Valid);
assert!(
info.schema_type.is_some(),
"schema_type should reflect overridden type"
);
v.validate_end_of_attributes();
v.validate_element("name", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("test");
v.validate_end_element();
v.validate_element("extra", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("additional");
v.validate_end_element();
v.validate_end_element(); v.validate_end_element(); assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_group_ref_with_nillable_fixed_default() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:group name="fields">
<xs:sequence>
<xs:element name="nillableField" type="xs:string" nillable="true"/>
<xs:element name="fixedField" type="xs:string" fixed="LOCKED"/>
<xs:element name="defaultField" type="xs:string" default="fallback"/>
</xs:sequence>
</xs:group>
<xs:element name="root">
<xs:complexType>
<xs:group ref="fields"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
let info = v.validate_element("nillableField", "", None, Some("true"), &ns);
assert!(info.is_nil, "nillableField should report is_nil=true");
assert_eq!(info.validity, SchemaValidity::Valid);
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("fixedField", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("WRONG");
let end_info = v.validate_end_element();
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-elt.5.2.2"),
"expected cvc-elt.5.2.2 for fixed value mismatch, errors: {:?}",
v.sink.errors
);
assert_eq!(end_info.validity, SchemaValidity::Invalid);
v.validate_element("defaultField", "", None, None, &ns);
v.validate_end_of_attributes();
let end_info = v.validate_end_element();
assert!(
end_info.is_default,
"defaultField with no text should report is_default=true"
);
v.validate_end_element(); assert!(v.end_validation().is_ok());
assert_eq!(
v.sink.errors.len(),
1,
"expected exactly 1 error (cvc-elt.5.2.2), got: {:?}",
v.sink.errors
);
}
#[test]
fn test_attribute_group_basic() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:attributeGroup name="myAttrs">
<xs:attribute name="color" type="xs:string"/>
<xs:attribute name="size" type="xs:integer"/>
</xs:attributeGroup>
<xs:element name="root">
<xs:complexType>
<xs:attributeGroup ref="myAttrs"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let info = v.validate_attribute("color", "", "red");
assert_eq!(info.validity, SchemaValidity::Valid);
let info = v.validate_attribute("size", "", "42");
assert_eq!(info.validity, SchemaValidity::Valid);
v.validate_end_of_attributes();
v.validate_end_element();
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_attribute_group_nested() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:attributeGroup name="inner">
<xs:attribute name="depth" type="xs:integer"/>
</xs:attributeGroup>
<xs:attributeGroup name="outer">
<xs:attribute name="width" type="xs:string"/>
<xs:attributeGroup ref="inner"/>
</xs:attributeGroup>
<xs:element name="root">
<xs:complexType>
<xs:attributeGroup ref="outer"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let info = v.validate_attribute("width", "", "100px");
assert_eq!(info.validity, SchemaValidity::Valid);
let info = v.validate_attribute("depth", "", "5");
assert_eq!(info.validity, SchemaValidity::Valid);
v.validate_end_of_attributes();
v.validate_end_element();
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_attribute_group_required_missing() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:attributeGroup name="myAttrs">
<xs:attribute name="id" type="xs:string" use="required"/>
</xs:attributeGroup>
<xs:element name="root">
<xs:complexType>
<xs:attributeGroup ref="myAttrs"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_end_element();
assert!(v.end_validation().is_ok());
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-complex-type.4"),
"expected cvc-complex-type.4 for missing required attribute from group, errors: {:?}",
v.sink.errors
);
}
#[test]
fn test_wildcard_namespace_other_rejects_same_ns() {
let schema_set = load_schema(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.com/ns"
xmlns:tns="http://example.com/ns">
<xs:element name="root">
<xs:complexType>
<xs:anyAttribute namespace="##other" processContents="skip"/>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let tns_id = schema_set.name_table.add("http://example.com/ns");
let tns_prefix = schema_set.name_table.add("tns");
let ns = NamespaceContextSnapshot {
default_ns: Some(tns_id),
bindings: vec![(tns_prefix, tns_id)],
};
v.validate_element("root", "http://example.com/ns", None, None, &ns);
let info = v.validate_attribute("foreign", "http://other.com/ns", "val");
assert_ne!(info.validity, SchemaValidity::Invalid);
let info = v.validate_attribute("local", "http://example.com/ns", "val");
assert_eq!(info.validity, SchemaValidity::Invalid);
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-complex-type.3.2.2"),
"expected cvc-complex-type.3.2.2, errors: {:?}",
v.sink.errors
);
v.validate_end_of_attributes();
v.validate_end_element();
assert!(v.end_validation().is_ok());
}
#[test]
fn test_wildcard_process_contents_strict() {
let schema_set = load_schema(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:attribute name="globalAttr" type="xs:integer"/>
<xs:element name="root">
<xs:complexType>
<xs:anyAttribute namespace="##any" processContents="strict"/>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let info = v.validate_attribute("globalAttr", "", "42");
assert_eq!(info.validity, SchemaValidity::Valid);
assert!(info.attribute_decl.is_some());
v.validate_end_of_attributes();
v.validate_end_element();
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_wildcard_process_contents_strict_unknown() {
let schema_set = load_schema(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:anyAttribute namespace="##any" processContents="strict"/>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let info = v.validate_attribute("unknownAttr", "", "anything");
assert_eq!(info.validity, SchemaValidity::Invalid);
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-assess-attr.1.2"),
"expected cvc-assess-attr.1.2 for strict wildcard with unknown attr, errors: {:?}",
v.sink.errors
);
v.validate_end_of_attributes();
v.validate_end_element();
assert!(v.end_validation().is_ok());
}
#[test]
fn test_wildcard_process_contents_lax() {
let schema_set = load_schema(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:anyAttribute namespace="##any" processContents="lax"/>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let info = v.validate_attribute("whatever", "", "anything");
assert_ne!(info.validity, SchemaValidity::Invalid);
v.validate_end_of_attributes();
v.validate_end_element();
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_wildcard_process_contents_skip() {
let schema_set = load_schema(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:attribute name="globalAttr" type="xs:integer"/>
<xs:element name="root">
<xs:complexType>
<xs:anyAttribute namespace="##any" processContents="skip"/>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let info = v.validate_attribute("globalAttr", "", "not_an_integer");
assert_ne!(info.validity, SchemaValidity::Invalid);
let info = v.validate_attribute("madeUp", "", "anything");
assert_ne!(info.validity, SchemaValidity::Invalid);
v.validate_end_of_attributes();
v.validate_end_element();
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_attribute_ref_basic() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:attribute name="globalAttr" type="xs:integer"/>
<xs:element name="root">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute ref="globalAttr"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let info = v.validate_attribute("globalAttr", "", "42");
assert_eq!(
info.validity,
SchemaValidity::Valid,
"attribute ref should match by resolved name; errors: {:?}",
v.sink.errors
);
assert!(
info.attribute_decl.is_some(),
"should resolve attribute decl key"
);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_attribute_ref_required_missing() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:attribute name="reqAttr" type="xs:string"/>
<xs:element name="root">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute ref="reqAttr" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-complex-type.4"),
"expected cvc-complex-type.4 for missing required ref attribute, errors: {:?}",
v.sink.errors
);
}
#[test]
fn test_prohibited_attribute_wildcard_rescue() {
let schema_a = load_schema(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:attribute name="blocked" use="prohibited"/>
<xs:anyAttribute namespace="##any" processContents="skip"/>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
let validator = SchemaValidator::new(&schema_a, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let _info = v.validate_attribute("blocked", "", "value");
assert!(
!v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-complex-type.3.2.2"),
"wildcard should rescue prohibited attribute; errors: {:?}",
v.sink.errors
);
v.validate_end_of_attributes();
v.validate_end_element();
assert!(v.end_validation().is_ok());
let schema_b = load_schema(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:attribute name="blocked" use="prohibited"/>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
let validator = SchemaValidator::new(&schema_b, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let info = v.validate_attribute("blocked", "", "value");
assert_eq!(
info.validity,
SchemaValidity::Invalid,
"prohibited without wildcard must be rejected"
);
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-complex-type.3.2.2" && e.message.contains("prohibited")),
"expected 'prohibited' error, errors: {:?}",
v.sink.errors
);
}
#[test]
fn test_group_wildcard_honored() {
let schema_set = load_schema(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:attributeGroup name="flexAttrs">
<xs:attribute name="known" type="xs:string"/>
<xs:anyAttribute namespace="##any" processContents="skip"/>
</xs:attributeGroup>
<xs:element name="root">
<xs:complexType>
<xs:attributeGroup ref="flexAttrs"/>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let info = v.validate_attribute("known", "", "hello");
assert_eq!(info.validity, SchemaValidity::Valid);
let info = v.validate_attribute("extra", "", "anything");
assert_ne!(
info.validity,
SchemaValidity::Invalid,
"group wildcard should accept unknown attributes; errors: {:?}",
v.sink.errors
);
v.validate_end_of_attributes();
v.validate_end_element();
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_default_from_global_declaration() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:attribute name="lang" type="xs:string" default="en"/>
<xs:element name="root">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute ref="lang"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
let defaults = v.get_default_attributes();
assert!(
defaults.iter().any(|d| {
let name = schema_set.name_table.resolve(d.local_name);
name == "lang" && d.value == "en"
}),
"expected default attribute lang='en', got: {:?}",
defaults
.iter()
.map(|d| (schema_set.name_table.resolve(d.local_name), &d.value))
.collect::<Vec<_>>()
);
v.validate_text("hello");
v.validate_end_element();
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_default_from_global_declaration_in_group() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:attribute name="lang" type="xs:string" default="en"/>
<xs:attributeGroup name="grp">
<xs:attribute ref="lang"/>
</xs:attributeGroup>
<xs:element name="root">
<xs:complexType>
<xs:attributeGroup ref="grp"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
let defaults = v.get_default_attributes();
assert!(
defaults.iter().any(|d| {
let name = schema_set.name_table.resolve(d.local_name);
name == "lang" && d.value == "en"
}),
"expected default attribute lang='en' from group, got: {:?}",
defaults
.iter()
.map(|d| (schema_set.name_table.resolve(d.local_name), &d.value))
.collect::<Vec<_>>()
);
v.validate_end_element();
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_mixed_content_text_allowed() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType mixed="true">
<xs:sequence>
<xs:element name="a" type="xs:string"/>
<xs:element name="b" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let info = v.validate_element("root", "", None, None, &ns);
assert_eq!(info.content_type, Some(ContentType::Mixed));
v.validate_end_of_attributes();
v.validate_text("hello ");
v.validate_element("a", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("val_a");
v.validate_end_element();
v.validate_text(" middle ");
v.validate_element("b", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("val_b");
v.validate_end_element();
v.validate_text(" world");
v.validate_end_element();
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_mixed_content_text_only_incomplete_model() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType mixed="true">
<xs:sequence>
<xs:element name="a" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("just text");
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-complex-type.2.4"),
"expected content model incomplete error, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_mixed_content_whitespace_accumulated() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType mixed="true">
<xs:sequence>
<xs:element name="a" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let info = v.validate_element("root", "", None, None, &ns);
assert_eq!(info.content_type, Some(ContentType::Mixed));
v.validate_end_of_attributes();
v.validate_whitespace(" ");
v.validate_element("a", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("val");
v.validate_end_element();
v.validate_whitespace(" \n ");
v.validate_end_element();
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_element_only_rejects_non_whitespace_text() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="a" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let info = v.validate_element("root", "", None, None, &ns);
assert_eq!(info.content_type, Some(ContentType::ElementOnly));
v.validate_end_of_attributes();
v.validate_text("not allowed here");
v.validate_element("a", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("val");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-complex-type.2.3"),
"expected element-only text error, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_mixed_content_wrong_child_order() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType mixed="true">
<xs:sequence>
<xs:element name="a" type="xs:string"/>
<xs:element name="b" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("some text ");
v.validate_element("b", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_text(" more text ");
v.validate_element("a", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-complex-type.2.4"),
"expected content model error for wrong child order, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_mixed_content_model_complete() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType mixed="true">
<xs:sequence>
<xs:element name="a" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let info = v.validate_element("root", "", None, None, &ns);
assert_eq!(info.content_type, Some(ContentType::Mixed));
v.validate_end_of_attributes();
v.validate_text("prefix ");
v.validate_element("a", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("child value");
v.validate_end_element();
v.validate_text(" suffix");
let end_info = v.validate_end_element();
assert_eq!(end_info.validity, SchemaValidity::Valid);
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_minoccurs_zero_element_in_sequence() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="a" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
let end_info = v.validate_end_element();
assert_eq!(end_info.validity, SchemaValidity::Valid);
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_maxoccurs_unbounded_element_in_sequence() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="a" type="xs:string" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
for _ in 0..3 {
v.validate_element("a", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("value");
v.validate_end_element();
}
let end_info = v.validate_end_element();
assert_eq!(end_info.validity, SchemaValidity::Valid);
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_mixed_content_optional_children_text_only() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType mixed="true">
<xs:sequence>
<xs:element name="a" type="xs:string" minOccurs="0"/>
<xs:element name="b" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("just text content");
let end_info = v.validate_end_element();
assert_eq!(end_info.validity, SchemaValidity::Valid);
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_nil_element_rejects_child_elements() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="parent" nillable="true">
<xs:complexType>
<xs:sequence>
<xs:element name="child" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
let info = v.validate_element("parent", "", None, Some("true"), &ns);
assert!(info.is_nil);
v.validate_end_of_attributes();
v.validate_element("child", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element(); v.validate_end_element(); v.end_validation().ok();
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-elt.3.2.1"),
"expected cvc-elt.3.2.1 error for child element in nilled parent, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_nil_element_allows_attributes_only() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" nillable="true">
<xs:complexType>
<xs:sequence>
<xs:element name="child" type="xs:string"/>
</xs:sequence>
<xs:attribute name="id" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
let info = v.validate_element("item", "", None, Some("true"), &ns);
assert!(info.is_nil);
v.validate_attribute("id", "", "123");
v.validate_end_of_attributes();
v.validate_end_element(); v.validate_end_element(); v.end_validation().ok();
assert!(
v.sink.errors.is_empty(),
"nilled element with attributes only should be valid, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_ic_key_duplicate() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="id" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:key name="itemKey">
<xs:selector xpath="./item"/>
<xs:field xpath="@id"/>
</xs:key>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "A");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "A");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element(); v.end_validation().ok();
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-identity-constraint.4.2.2"),
"Expected duplicate key error, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_ic_unique_incomplete_ok_duplicate_rejected() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="id" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:unique name="itemUnique">
<xs:selector xpath="./item"/>
<xs:field xpath="@id"/>
</xs:unique>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "X");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "X");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element(); v.end_validation().ok();
let dup_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-identity-constraint.4.2.2")
.collect();
assert_eq!(
dup_errors.len(),
1,
"Expected exactly 1 duplicate error, got: {:?}",
dup_errors
);
}
#[test]
fn test_ic_keyref_matching_and_missing() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="id" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="ref" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="ref" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:key name="itemKey">
<xs:selector xpath="./item"/>
<xs:field xpath="@id"/>
</xs:key>
<xs:keyref name="itemRef" refer="itemKey">
<xs:selector xpath="./ref"/>
<xs:field xpath="@ref"/>
</xs:keyref>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "A");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("ref", "", None, None, &ns);
v.validate_attribute("ref", "", "A");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("ref", "", None, None, &ns);
v.validate_attribute("ref", "", "B");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element(); v.end_validation().ok();
let keyref_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-identity-constraint.4.3")
.collect();
assert_eq!(
keyref_errors.len(),
1,
"Expected 1 keyref error for missing 'B', got: {:?}",
keyref_errors
);
}
#[test]
fn test_ic_element_field_value() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="code" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:key name="codeKey">
<xs:selector xpath="./item"/>
<xs:field xpath="code"/>
</xs:key>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("code", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("X");
v.validate_end_element(); v.validate_end_element();
v.validate_element("item", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("code", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("X");
v.validate_end_element(); v.validate_end_element();
v.validate_end_element(); v.end_validation().ok();
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-identity-constraint.4.2.2"),
"Expected duplicate key error for element field, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_ic_attribute_field_value() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="val" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:unique name="valUnique">
<xs:selector xpath="./item"/>
<xs:field xpath="@val"/>
</xs:unique>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("val", "", "alpha");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("val", "", "beta");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink.errors.is_empty(),
"Expected no errors for unique values, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_id_duplicate() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="id" type="xs:ID" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "a1");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "a1");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink.errors.iter().any(|e| e.constraint == "cvc-id.2"),
"Expected duplicate ID error, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_idref_valid_and_missing() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="id" type="xs:ID" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="link" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="ref" type="xs:IDREF" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "x1");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("link", "", None, None, &ns);
v.validate_attribute("ref", "", "x1");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("link", "", None, None, &ns);
v.validate_attribute("ref", "", "missing");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let idref_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.1")
.collect();
assert_eq!(
idref_errors.len(),
1,
"Expected 1 IDREF error for 'missing', got: {:?}",
idref_errors
);
}
#[test]
fn test_ic_nested_selector() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="item" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="id" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="id" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:unique name="allItems">
<xs:selector xpath=".//item"/>
<xs:field xpath="@id"/>
</xs:unique>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "1");
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "2");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.validate_end_element(); v.end_validation().ok();
assert!(
v.sink.errors.is_empty(),
"Expected no errors for unique nested items, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_ic_keyref_key_same_scope() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="dept" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="id" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="emp" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="dept" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:key name="deptKey">
<xs:selector xpath="./dept"/>
<xs:field xpath="@id"/>
</xs:key>
<xs:keyref name="empDeptRef" refer="deptKey">
<xs:selector xpath="./emp"/>
<xs:field xpath="@dept"/>
</xs:keyref>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("dept", "", None, None, &ns);
v.validate_attribute("id", "", "sales");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("dept", "", None, None, &ns);
v.validate_attribute("id", "", "eng");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("emp", "", None, None, &ns);
v.validate_attribute("dept", "", "sales");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("emp", "", None, None, &ns);
v.validate_attribute("dept", "", "hr");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element(); v.end_validation().ok();
let keyref_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-identity-constraint.4.3")
.collect();
assert_eq!(
keyref_errors.len(),
1,
"Expected 1 keyref error for 'hr', got: {:?}",
keyref_errors
);
}
#[test]
fn test_ic_key_no_duplicates_valid() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="id" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:key name="pk">
<xs:selector xpath="./item"/>
<xs:field xpath="@id"/>
</xs:key>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "A");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "B");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink.errors.is_empty(),
"Expected no errors for unique keys, got: {:?}",
v.sink.errors
);
}
#[cfg(feature = "xsd11")]
mod assertion_runtime_tests {
use super::*;
#[test]
fn test_disabled_mode_no_overhead() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
assert_eq!(v.assertion_source, AssertionSource::Disabled);
let ns = empty_ns_context();
let info = v.validate_element("root", "", None, None, &ns);
assert_eq!(info.validity, SchemaValidity::Valid);
v.validate_end_of_attributes();
v.validate_text("hello");
let end_info = v.validate_end_element();
assert_eq!(end_info.validity, SchemaValidity::Valid);
v.end_validation().ok();
assert!(
v.sink.errors.is_empty(),
"Expected no errors in Disabled mode, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_new_strips_process_assertions_flag() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let flags = ValidationFlags::default() | ValidationFlags::PROCESS_ASSERTIONS;
let validator = SchemaValidator::new(&schema_set, flags);
assert!(!validator
.flags
.contains(ValidationFlags::PROCESS_ASSERTIONS));
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let info = v.validate_element("root", "", None, None, &ns);
assert_eq!(info.validity, SchemaValidity::Valid);
}
#[test]
fn test_main_document_full_roundtrip() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let mut validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
validator.set_assertion_source(AssertionSource::MainDocument);
let mut v = validator.start_run(TestSink::new());
assert_eq!(v.assertion_source, AssertionSource::MainDocument);
let ns = empty_ns_context();
let info = v.validate_element("root", "", None, None, &ns);
assert_eq!(info.validity, SchemaValidity::Valid);
v.validate_end_of_attributes();
v.validate_text("hello");
let end_info = v.validate_end_element();
assert_eq!(end_info.validity, SchemaValidity::Valid);
v.end_validation().ok();
assert!(
v.sink.errors.is_empty(),
"Expected no errors in MainDocument mode, got: {:?}",
v.sink.errors
);
}
fn validate_with_fragment_buffer(
xsd: &str,
element: &str,
attrs: &[(&str, &str)],
text: Option<&str>,
) -> Vec<ValidationError> {
let schema_set = load_schema_xsd11(xsd);
let validator =
SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element(element, "", None, None, &ns);
for &(name, value) in attrs {
v.validate_attribute(name, "", value);
}
v.validate_end_of_attributes();
if let Some(t) = text {
v.validate_text(t);
}
v.validate_end_element();
v.end_validation().ok();
v.sink.errors
}
#[test]
fn test_assertion_pass() {
let errors = validate_with_fragment_buffer(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="item">
<xs:complexType>
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val >= 0"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
"item",
&[("val", "25")],
None,
);
assert!(
errors.is_empty(),
"Expected no assertion errors, got: {:?}",
errors
);
}
#[test]
fn test_assertion_fail() {
let errors = validate_with_fragment_buffer(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="item">
<xs:complexType>
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val >= 0"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
"item",
&[("val", "-5")],
None,
);
let has_assertion_error = errors.iter().any(|e| e.constraint == "cvc-assertion");
assert!(
has_assertion_error,
"Expected cvc-assertion error for negative @val, got: {:?}",
errors
);
}
#[test]
fn test_assertion_multiple_one_fails() {
let errors = validate_with_fragment_buffer(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="item">
<xs:complexType>
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val >= 0"/>
<xs:assert test="@val < 100"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
"item",
&[("val", "150")],
None,
);
let assertion_errors: Vec<_> = errors
.iter()
.filter(|e| e.constraint == "cvc-assertion")
.collect();
assert_eq!(
assertion_errors.len(),
1,
"Expected exactly 1 assertion failure, got: {:?}",
assertion_errors
);
}
#[test]
fn test_no_assertion_type_no_buffering_overhead() {
let errors = validate_with_fragment_buffer(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="plain" type="xs:string"/>
</xs:schema>"#,
"plain",
&[],
Some("hello"),
);
assert!(
errors.is_empty(),
"No assertion type should produce no errors, got: {:?}",
errors
);
}
#[test]
fn test_assertion_attribute_check() {
let errors = validate_with_fragment_buffer(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="item">
<xs:complexType>
<xs:attribute name="code" type="xs:string" use="required"/>
<xs:assert test="string-length(@code) > 0"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
"item",
&[("code", "ABC")],
None,
);
assert!(
errors.is_empty(),
"Assertion on non-empty @code should pass, got: {:?}",
errors
);
}
#[test]
fn test_assertion_on_element_content() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="order">
<xs:complexType>
<xs:sequence>
<xs:element name="qty" type="xs:integer"/>
</xs:sequence>
<xs:assert test="qty > 0"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator =
SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("order", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("qty", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("5");
v.validate_end_element(); v.validate_end_element(); v.end_validation().ok();
let assertion_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-assertion")
.collect();
assert!(
assertion_errors.is_empty(),
"qty=5 should pass qty > 0 assertion, got: {:?}",
assertion_errors
);
}
#[test]
fn test_assertion_on_element_content_fail() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="order">
<xs:complexType>
<xs:sequence>
<xs:element name="qty" type="xs:integer"/>
</xs:sequence>
<xs:assert test="qty > 0"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator =
SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("order", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("qty", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("0");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let assertion_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-assertion")
.collect();
assert_eq!(
assertion_errors.len(),
1,
"qty=0 should fail qty > 0, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_inherited_assertion_pass() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="baseType">
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val >= 0"/>
</xs:complexType>
<xs:complexType name="derivedType">
<xs:complexContent>
<xs:restriction base="baseType">
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val < 100"/>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
<xs:element name="item" type="derivedType"/>
</xs:schema>"#,
);
let validator =
SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("val", "", "50");
v.validate_end_of_attributes();
v.validate_end_element();
v.end_validation().ok();
let assertion_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-assertion")
.collect();
assert!(
assertion_errors.is_empty(),
"val=50 should satisfy both base and derived assertions, got: {:?}",
assertion_errors
);
}
#[test]
fn test_inherited_assertion_base_fails() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="baseType">
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val >= 0"/>
</xs:complexType>
<xs:complexType name="derivedType">
<xs:complexContent>
<xs:restriction base="baseType">
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val < 100"/>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
<xs:element name="item" type="derivedType"/>
</xs:schema>"#,
);
let validator =
SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("val", "", "-5");
v.validate_end_of_attributes();
v.validate_end_element();
v.end_validation().ok();
let assertion_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-assertion")
.collect();
assert!(
!assertion_errors.is_empty(),
"val=-5 should fail inherited @val >= 0 assertion"
);
}
#[test]
fn test_inherited_assertion_derived_fails() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="baseType">
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val >= 0"/>
</xs:complexType>
<xs:complexType name="derivedType">
<xs:complexContent>
<xs:restriction base="baseType">
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val < 100"/>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
<xs:element name="item" type="derivedType"/>
</xs:schema>"#,
);
let validator =
SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("val", "", "200");
v.validate_end_of_attributes();
v.validate_end_element();
v.end_validation().ok();
let assertion_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-assertion")
.collect();
assert_eq!(
assertion_errors.len(),
1,
"val=200 should fail only derived @val < 100, got: {:?}",
assertion_errors
);
}
#[test]
fn test_inherited_assertion_both_fail() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="baseType">
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val >= 0"/>
</xs:complexType>
<xs:complexType name="derivedType">
<xs:complexContent>
<xs:restriction base="baseType">
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val > 10"/>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
<xs:element name="item" type="derivedType"/>
</xs:schema>"#,
);
let validator =
SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("val", "", "-5");
v.validate_end_of_attributes();
v.validate_end_element();
v.end_validation().ok();
let assertion_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-assertion")
.collect();
assert_eq!(
assertion_errors.len(),
2,
"val=-5 should fail both inherited assertions, got: {:?}",
assertion_errors
);
}
#[test]
fn test_nested_element_assertions() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="parent">
<xs:complexType>
<xs:sequence>
<xs:element name="child">
<xs:complexType>
<xs:attribute name="x" type="xs:integer"/>
<xs:assert test="@x > 0"/>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="total" type="xs:integer"/>
<xs:assert test="@total >= 0"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator =
SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("parent", "", None, None, &ns);
v.validate_attribute("total", "", "10");
v.validate_end_of_attributes();
v.validate_element("child", "", None, None, &ns);
v.validate_attribute("x", "", "5");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element(); v.end_validation().ok();
let assertion_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-assertion")
.collect();
assert!(
assertion_errors.is_empty(),
"Both assertions should pass, got: {:?}",
assertion_errors
);
}
#[test]
fn test_nested_element_child_assertion_fails() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="parent">
<xs:complexType>
<xs:sequence>
<xs:element name="child">
<xs:complexType>
<xs:attribute name="x" type="xs:integer"/>
<xs:assert test="@x > 0"/>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="total" type="xs:integer"/>
<xs:assert test="@total >= 0"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator =
SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("parent", "", None, None, &ns);
v.validate_attribute("total", "", "10");
v.validate_end_of_attributes();
v.validate_element("child", "", None, None, &ns);
v.validate_attribute("x", "", "-1");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element(); v.end_validation().ok();
let assertion_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-assertion")
.collect();
assert_eq!(
assertion_errors.len(),
1,
"Only child assertion should fail, got: {:?}",
assertion_errors
);
}
#[test]
fn test_named_type_assertion_pass() {
let errors = validate_with_fragment_buffer(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="positiveType">
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val > 0"/>
</xs:complexType>
<xs:element name="item" type="positiveType"/>
</xs:schema>"#,
"item",
&[("val", "42")],
None,
);
assert!(
errors.is_empty(),
"Named type assertion should pass for val=42, got: {:?}",
errors
);
}
#[test]
fn test_named_type_assertion_fail() {
let errors = validate_with_fragment_buffer(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="positiveType">
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val > 0"/>
</xs:complexType>
<xs:element name="item" type="positiveType"/>
</xs:schema>"#,
"item",
&[("val", "-1")],
None,
);
let has_assertion_error = errors.iter().any(|e| e.constraint == "cvc-assertion");
assert!(
has_assertion_error,
"Named type assertion should fail for val=-1, got: {:?}",
errors
);
}
#[test]
fn test_named_type_child_element_assertion() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="orderType">
<xs:sequence>
<xs:element name="qty" type="xs:integer"/>
<xs:element name="price" type="xs:decimal"/>
</xs:sequence>
<xs:assert test="qty > 0 and price > 0"/>
</xs:complexType>
<xs:element name="order" type="orderType"/>
</xs:schema>"#,
);
let validator =
SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("order", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("qty", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("3");
v.validate_end_element();
v.validate_element("price", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("9.99");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let assertion_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-assertion")
.collect();
assert!(
assertion_errors.is_empty(),
"qty=3, price=9.99 should pass assertion, got: {:?}",
assertion_errors
);
}
#[test]
fn test_named_type_child_element_assertion_fail() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="orderType">
<xs:sequence>
<xs:element name="qty" type="xs:integer"/>
<xs:element name="price" type="xs:decimal"/>
</xs:sequence>
<xs:assert test="qty > 0 and price > 0"/>
</xs:complexType>
<xs:element name="order" type="orderType"/>
</xs:schema>"#,
);
let validator =
SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("order", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("qty", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("0");
v.validate_end_element();
v.validate_element("price", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("9.99");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let assertion_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-assertion")
.collect();
assert_eq!(
assertion_errors.len(),
1,
"qty=0 should fail 'qty > 0 and price > 0', got: {:?}",
assertion_errors
);
}
#[test]
fn test_assertion_xpath_default_namespace() {
let schema_set = load_schema_xsd11(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.com/ns"
xmlns:tns="http://example.com/ns"
elementFormDefault="qualified">
<xs:element name="order">
<xs:complexType>
<xs:sequence>
<xs:element name="qty" type="xs:integer"/>
</xs:sequence>
<xs:assert test="qty > 0"
xpathDefaultNamespace="##targetNamespace"/>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
let validator =
SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let tns = "http://example.com/ns";
v.validate_element("order", tns, None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("qty", tns, None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("5");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let assertion_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-assertion")
.collect();
assert!(
assertion_errors.is_empty(),
"xpathDefaultNamespace=##targetNamespace should allow unqualified 'qty' to match, got: {:?}",
assertion_errors
);
}
#[test]
fn test_extension_inherits_base_assertion() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="baseType">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
<xs:assert test="string-length(name) > 0"/>
</xs:complexType>
<xs:complexType name="extType">
<xs:complexContent>
<xs:extension base="baseType">
<xs:sequence>
<xs:element name="extra" type="xs:string"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="item" type="extType"/>
</xs:schema>"#,
);
let validator =
SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("item", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("name", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_element("extra", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("world");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let assertion_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-assertion")
.collect();
assert!(
assertion_errors.is_empty(),
"Extension type should inherit and pass base assertion, got: {:?}",
assertion_errors
);
}
}
#[cfg(feature = "xsd11")]
mod fragment_arena_tests {
use super::*;
#[test]
fn fragment_arena_lifecycle() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
assert!(v.fragment_arena().is_none());
let _arena = v.fragment_arena_mut();
assert!(v.fragment_arena().is_some());
}
#[test]
fn fragment_arena_allocate_and_drop() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let arena = v.fragment_arena_mut();
let _s = arena.alloc_str("hello fragment");
drop(v);
}
}
#[test]
fn test_global_element_with_named_complex_type_ref() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="itemType">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="value" type="xs:integer"/>
</xs:sequence>
</xs:complexType>
<xs:element name="item" type="itemType"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let info = v.validate_element("item", "", None, None, &ns);
assert_eq!(info.validity, SchemaValidity::Valid, "item should be valid");
assert!(info.schema_type.is_some(), "item should have a schema type");
v.validate_end_of_attributes();
let name_info = v.validate_element("name", "", None, None, &ns);
assert_eq!(
name_info.validity,
SchemaValidity::Valid,
"name should be valid"
);
v.validate_end_of_attributes();
v.validate_text("Widget");
v.validate_end_element();
let value_info = v.validate_element("value", "", None, None, &ns);
assert_eq!(
value_info.validity,
SchemaValidity::Valid,
"value should be valid"
);
v.validate_end_of_attributes();
v.validate_text("42");
v.validate_end_element();
v.validate_end_element();
assert!(v.end_validation().is_ok());
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[cfg(feature = "xsd11")]
mod type_alternatives_tests {
use super::*;
fn validate_errors(
schema_set: &SchemaSet,
run: impl FnOnce(&mut ValidationRuntime<'_, TestSink>),
) -> Vec<ValidationError> {
let validator = SchemaValidator::new(schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
run(&mut v);
v.end_validation().ok();
v.sink.errors
}
const ALTERNATIVES_SCHEMA: &str = r#"
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="intContent">
<xs:sequence>
<xs:element name="val" type="xs:integer"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
</xs:complexType>
<xs:complexType name="strContent">
<xs:sequence>
<xs:element name="val" type="xs:string"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
</xs:complexType>
<xs:element name="data">
<xs:complexType>
<xs:sequence>
<xs:element name="val" type="xs:string"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
</xs:complexType>
<xs:alternative test="@kind='int'" type="intContent"/>
<xs:alternative test="@kind='str'" type="strContent"/>
</xs:element>
</xs:schema>"#;
#[test]
fn test_alternative_selects_int_type() {
let schema_set = load_schema_xsd11(ALTERNATIVES_SCHEMA);
let ns = empty_ns_context();
let errors = validate_errors(&schema_set, |v| {
v.validate_element("data", "", None, None, &ns);
v.validate_attribute("kind", "", "int");
v.validate_end_of_attributes();
v.validate_element("val", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("42");
v.validate_end_element();
v.validate_end_element();
});
assert!(errors.is_empty(), "Expected no errors, got: {:?}", errors);
}
#[test]
fn test_alternative_selects_str_type() {
let schema_set = load_schema_xsd11(ALTERNATIVES_SCHEMA);
let ns = empty_ns_context();
let errors = validate_errors(&schema_set, |v| {
v.validate_element("data", "", None, None, &ns);
v.validate_attribute("kind", "", "str");
v.validate_end_of_attributes();
v.validate_element("val", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
});
assert!(errors.is_empty(), "Expected no errors, got: {:?}", errors);
}
#[test]
fn test_alternative_int_rejects_non_integer() {
let schema_set = load_schema_xsd11(ALTERNATIVES_SCHEMA);
let ns = empty_ns_context();
let errors = validate_errors(&schema_set, |v| {
v.validate_element("data", "", None, None, &ns);
v.validate_attribute("kind", "", "int");
v.validate_end_of_attributes();
v.validate_element("val", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
});
assert!(
!errors.is_empty(),
"Expected validation error for non-integer value"
);
}
#[test]
fn test_no_matching_alternative_uses_declared_type() {
let schema_set = load_schema_xsd11(ALTERNATIVES_SCHEMA);
let ns = empty_ns_context();
let errors = validate_errors(&schema_set, |v| {
v.validate_element("data", "", None, None, &ns);
v.validate_attribute("kind", "", "other");
v.validate_end_of_attributes();
v.validate_element("val", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("anything");
v.validate_end_element();
v.validate_end_element();
});
assert!(
errors.is_empty(),
"Expected no errors with declared type, got: {:?}",
errors
);
}
#[test]
fn test_alternative_with_default_fallback() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="specialType">
<xs:sequence>
<xs:element name="s" type="xs:integer"/>
</xs:sequence>
<xs:attribute name="mode" type="xs:string"/>
</xs:complexType>
<xs:complexType name="defaultType">
<xs:sequence>
<xs:element name="d" type="xs:string"/>
</xs:sequence>
<xs:attribute name="mode" type="xs:string"/>
</xs:complexType>
<xs:element name="item">
<xs:complexType>
<xs:sequence>
<xs:element name="x" type="xs:string"/>
</xs:sequence>
<xs:attribute name="mode" type="xs:string"/>
</xs:complexType>
<xs:alternative test="@mode='special'" type="specialType"/>
<xs:alternative type="defaultType"/>
</xs:element>
</xs:schema>"#,
);
let ns = empty_ns_context();
let errors_special = validate_errors(&schema_set, |v| {
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("mode", "", "special");
v.validate_end_of_attributes();
v.validate_element("s", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("42");
v.validate_end_element();
v.validate_end_element();
});
assert!(
errors_special.is_empty(),
"Expected no errors for special mode, got: {:?}",
errors_special
);
let errors_default = validate_errors(&schema_set, |v| {
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("mode", "", "other");
v.validate_end_of_attributes();
v.validate_element("d", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
});
assert!(
errors_default.is_empty(),
"Expected no errors for default mode, got: {:?}",
errors_default
);
}
#[test]
fn test_alternative_wrong_child_for_selected_type() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="typeA">
<xs:sequence>
<xs:element name="a" type="xs:string"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
</xs:complexType>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="x" type="xs:string"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
</xs:complexType>
<xs:alternative test="@kind='A'" type="typeA"/>
</xs:element>
</xs:schema>"#,
);
let ns = empty_ns_context();
let errors = validate_errors(&schema_set, |v| {
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("kind", "", "A");
v.validate_end_of_attributes();
v.validate_element("x", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
});
assert!(
!errors.is_empty(),
"Expected content model error for wrong child element"
);
}
#[test]
fn test_alternative_no_attribute_no_match() {
let schema_set = load_schema_xsd11(ALTERNATIVES_SCHEMA);
let ns = empty_ns_context();
let errors = validate_errors(&schema_set, |v| {
v.validate_element("data", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("val", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("anything");
v.validate_end_element();
v.validate_end_element();
});
assert!(errors.is_empty(), "Expected no errors, got: {:?}", errors);
}
#[test]
fn test_alternative_schema_info_reflects_selected_type() {
let schema_set = load_schema_xsd11(ALTERNATIVES_SCHEMA);
let ns = empty_ns_context();
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
v.validate_element("data", "", None, None, &ns);
v.validate_attribute("kind", "", "int");
let eoa_info = v.validate_end_of_attributes();
assert!(
eoa_info.schema_type.is_some(),
"validate_end_of_attributes() should return updated type after CTA switch"
);
v.validate_element("val", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("123");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_deferred_attr_validation_rejects_prohibited_attr() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="strict">
<xs:sequence>
<xs:element name="v" type="xs:string"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
</xs:complexType>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="v" type="xs:string"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
<xs:attribute name="extra" type="xs:string"/>
</xs:complexType>
<xs:alternative test="@kind='strict'" type="strict"/>
</xs:element>
</xs:schema>"#,
);
let ns = empty_ns_context();
let errors = validate_errors(&schema_set, |v| {
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("kind", "", "strict");
v.validate_attribute("extra", "", "foo");
v.validate_end_of_attributes();
v.validate_element("v", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
});
assert!(
errors.iter().any(|e| e.message.contains("extra")),
"Expected error for undeclared 'extra' attribute in selected type, got: {:?}",
errors
);
}
#[test]
fn test_deferred_attr_validation_checks_fixed_value() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="fixed">
<xs:sequence>
<xs:element name="v" type="xs:string"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
<xs:attribute name="code" type="xs:string" fixed="ABC"/>
</xs:complexType>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="v" type="xs:string"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
<xs:attribute name="code" type="xs:string"/>
</xs:complexType>
<xs:alternative test="@kind='fixed'" type="fixed"/>
</xs:element>
</xs:schema>"#,
);
let ns = empty_ns_context();
let errors = validate_errors(&schema_set, |v| {
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("kind", "", "fixed");
v.validate_attribute("code", "", "XYZ");
v.validate_end_of_attributes();
v.validate_element("v", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
});
assert!(
errors.iter().any(|e| e.constraint == "cvc-attribute.4"),
"Expected cvc-attribute.4 error for fixed value mismatch, got: {:?}",
errors
);
let errors_ok = validate_errors(&schema_set, |v| {
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("kind", "", "fixed");
v.validate_attribute("code", "", "ABC");
v.validate_end_of_attributes();
v.validate_element("v", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
});
assert!(
errors_ok.is_empty(),
"Expected no errors, got: {:?}",
errors_ok
);
}
#[test]
fn test_deferred_attr_validates_type_against_selected() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="numType">
<xs:sequence>
<xs:element name="v" type="xs:string"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
<xs:attribute name="val" type="xs:integer"/>
</xs:complexType>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="v" type="xs:string"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
<xs:attribute name="val" type="xs:string"/>
</xs:complexType>
<xs:alternative test="@kind='num'" type="numType"/>
</xs:element>
</xs:schema>"#,
);
let ns = empty_ns_context();
let errors = validate_errors(&schema_set, |v| {
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("kind", "", "num");
v.validate_attribute("val", "", "abc");
v.validate_end_of_attributes();
v.validate_element("v", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
});
assert!(
!errors.is_empty(),
"Expected type error for 'abc' against xs:integer in selected type"
);
let errors_ok = validate_errors(&schema_set, |v| {
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("kind", "", "num");
v.validate_attribute("val", "", "42");
v.validate_end_of_attributes();
v.validate_element("v", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
});
assert!(
errors_ok.is_empty(),
"Expected no errors, got: {:?}",
errors_ok
);
}
#[test]
fn test_cta_no_switch_still_validates_attributes() {
let schema_set = load_schema_xsd11(ALTERNATIVES_SCHEMA);
let ns = empty_ns_context();
let errors = validate_errors(&schema_set, |v| {
v.validate_element("data", "", None, None, &ns);
v.validate_attribute("kind", "", "other"); v.validate_attribute("unknown", "", "val"); v.validate_end_of_attributes();
v.validate_end_element();
});
assert!(
errors
.iter()
.any(|e| e.constraint == "cvc-complex-type.3.2.2"),
"Undeclared attribute 'unknown' should be reported even when CTA \
doesn't switch type, got: {:?}",
errors
);
}
#[test]
fn test_no_cta_returns_empty_schema_info() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let eoa_info = v.validate_end_of_attributes();
assert!(
eoa_info.schema_type.is_none(),
"No CTA switch should return empty SchemaInfo, got type: {:?}",
eoa_info.schema_type
);
}
fn validate_errors_with_assertions(
schema_set: &SchemaSet,
run: impl FnOnce(&mut ValidationRuntime<'_, TestSink>),
) -> Vec<ValidationError> {
let validator =
SchemaValidator::new_fragment_buffer(schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
run(&mut v);
v.end_validation().ok();
v.sink.errors
}
#[test]
fn test_cta_non_asserted_to_asserted() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="assertedType">
<xs:sequence>
<xs:element name="v" type="xs:string"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val > 0"/>
</xs:complexType>
<xs:element name="item">
<xs:complexType>
<xs:sequence>
<xs:element name="v" type="xs:string"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
<xs:attribute name="val" type="xs:integer"/>
</xs:complexType>
<xs:alternative test="@kind='checked'" type="assertedType"/>
</xs:element>
</xs:schema>"#,
);
let ns = empty_ns_context();
let errors = validate_errors_with_assertions(&schema_set, |v| {
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("kind", "", "checked");
v.validate_attribute("val", "", "-1");
v.validate_end_of_attributes();
v.validate_element("v", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
});
assert!(
errors.iter().any(|e| e.constraint == "cvc-assertion"),
"Expected assertion error for @val > 0 with val=-1, got: {:?}",
errors
);
let errors_ok = validate_errors_with_assertions(&schema_set, |v| {
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("kind", "", "checked");
v.validate_attribute("val", "", "5");
v.validate_end_of_attributes();
v.validate_element("v", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
});
assert!(
errors_ok.iter().all(|e| e.constraint != "cvc-assertion"),
"Expected no assertion errors for @val > 0 with val=5, got: {:?}",
errors_ok
);
}
#[test]
fn test_cta_asserted_to_non_asserted() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="plainType">
<xs:sequence>
<xs:element name="v" type="xs:string"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
<xs:attribute name="val" type="xs:integer"/>
</xs:complexType>
<xs:element name="item">
<xs:complexType>
<xs:sequence>
<xs:element name="v" type="xs:string"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val > 100"/>
</xs:complexType>
<xs:alternative test="@kind='plain'" type="plainType"/>
</xs:element>
</xs:schema>"#,
);
let ns = empty_ns_context();
let errors = validate_errors_with_assertions(&schema_set, |v| {
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("kind", "", "plain");
v.validate_attribute("val", "", "1");
v.validate_end_of_attributes();
v.validate_element("v", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
});
assert!(
errors.iter().all(|e| e.constraint != "cvc-assertion"),
"Expected NO assertion errors (CTA selected non-asserted type), got: {:?}",
errors
);
}
#[test]
fn test_cta_asserted_to_asserted() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="strictType">
<xs:sequence>
<xs:element name="v" type="xs:string"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val > 10"/>
</xs:complexType>
<xs:element name="item">
<xs:complexType>
<xs:sequence>
<xs:element name="v" type="xs:string"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val > 0"/>
</xs:complexType>
<xs:alternative test="@kind='strict'" type="strictType"/>
</xs:element>
</xs:schema>"#,
);
let ns = empty_ns_context();
let errors = validate_errors_with_assertions(&schema_set, |v| {
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("kind", "", "strict");
v.validate_attribute("val", "", "5");
v.validate_end_of_attributes();
v.validate_element("v", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
});
assert!(
errors.iter().any(|e| e.constraint == "cvc-assertion"),
"Expected assertion error from strict @val > 10 with val=5, got: {:?}",
errors
);
let errors_ok = validate_errors_with_assertions(&schema_set, |v| {
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("kind", "", "strict");
v.validate_attribute("val", "", "20");
v.validate_end_of_attributes();
v.validate_element("v", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
});
assert!(
errors_ok.iter().all(|e| e.constraint != "cvc-assertion"),
"Expected no assertion errors for @val > 10 with val=20, got: {:?}",
errors_ok
);
}
}
#[test]
#[cfg(feature = "xsd11")]
fn test_default_attributes_applied() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
defaultAttributes="commonAttrs">
<xs:attributeGroup name="commonAttrs">
<xs:attribute name="lang" type="xs:string"/>
</xs:attributeGroup>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="a" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("lang", "", "en");
v.validate_end_of_attributes();
v.validate_element("a", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink.errors.is_empty(),
"Default attribute group attribute 'lang' should be accepted, got: {:?}",
v.sink.errors
);
}
#[test]
#[cfg(feature = "xsd11")]
fn test_default_attributes_opt_out() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
defaultAttributes="commonAttrs">
<xs:attributeGroup name="commonAttrs">
<xs:attribute name="lang" type="xs:string"/>
</xs:attributeGroup>
<xs:element name="root">
<xs:complexType defaultAttributesApply="false">
<xs:sequence>
<xs:element name="a" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("lang", "", "en");
v.validate_end_of_attributes();
v.validate_element("a", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint.starts_with("cvc-complex-type.3")),
"Attribute 'lang' should be rejected when defaultAttributesApply=false, got: {:?}",
v.sink.errors
);
}
#[test]
#[cfg(feature = "xsd11")]
fn test_default_attributes_contributes_defaults() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
defaultAttributes="commonAttrs">
<xs:attribute name="lang" type="xs:string" default="en"/>
<xs:attributeGroup name="commonAttrs">
<xs:attribute ref="lang"/>
</xs:attributeGroup>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="a" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
let defaults = v.get_default_attributes();
assert!(
defaults.iter().any(|d| {
let name = schema_set.name_table.resolve(d.local_name);
name == "lang" && d.value == "en"
}),
"Default attributes should include 'lang' with value 'en', got: {:?}",
defaults
.iter()
.map(|d| (schema_set.name_table.resolve(d.local_name), &d.value))
.collect::<Vec<_>>()
);
v.validate_element("a", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
}
#[test]
#[cfg(feature = "xsd11")]
fn test_default_attributes_required() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
defaultAttributes="commonAttrs">
<xs:attributeGroup name="commonAttrs">
<xs:attribute name="lang" type="xs:string" use="required"/>
</xs:attributeGroup>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="a" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("a", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-complex-type.4"),
"Required attribute from default group should cause cvc-complex-type.4 error, got: {:?}",
v.sink.errors
);
}
#[test]
#[cfg(feature = "xsd11")]
fn test_default_attributes_any_attribute() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
defaultAttributes="commonAttrs">
<xs:attributeGroup name="commonAttrs">
<xs:anyAttribute processContents="lax"/>
</xs:attributeGroup>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="a" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("unknown", "", "value");
v.validate_end_of_attributes();
v.validate_element("a", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink.errors.is_empty(),
"anyAttribute in default group should allow unknown attributes, got: {:?}",
v.sink.errors
);
}
fn tns_ns_context(schema_set: &SchemaSet) -> NamespaceContextSnapshot {
let tns_id = schema_set.name_table.add("http://example.com/ns");
let tns_prefix = schema_set.name_table.add("tns");
NamespaceContextSnapshot {
default_ns: Some(tns_id),
bindings: vec![(tns_prefix, tns_id)],
}
}
fn assert_attribute_form(
schema_set: &SchemaSet,
accept_ns: &str,
reject_ns: &str,
accept_msg: &str,
reject_msg: &str,
) {
let validator = SchemaValidator::new(schema_set, ValidationFlags::default());
let ns = tns_ns_context(schema_set);
let mut v = validator.start_run(TestSink::new());
v.validate_element("root", "http://example.com/ns", None, None, &ns);
let info = v.validate_attribute("id", accept_ns, "val");
assert_ne!(
info.validity,
SchemaValidity::Invalid,
"{accept_msg}, errors: {:?}",
v.sink.errors
);
v.validate_end_of_attributes();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink.errors.is_empty(),
"expected no errors, got: {:?}",
v.sink.errors
);
let mut v2 = validator.start_run(TestSink::new());
v2.validate_element("root", "http://example.com/ns", None, None, &ns);
let info = v2.validate_attribute("id", reject_ns, "val");
assert_eq!(info.validity, SchemaValidity::Invalid, "{reject_msg}");
v2.validate_end_of_attributes();
v2.validate_end_element();
v2.end_validation().ok();
assert!(
v2.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-complex-type.3.2.2"),
"expected cvc-complex-type.3.2.2, got: {:?}",
v2.sink.errors
);
}
const TNS: &str = "http://example.com/ns";
#[test]
fn test_attribute_form_default_qualified() {
let schema_set = load_schema(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.com/ns"
attributeFormDefault="qualified"
xmlns:tns="http://example.com/ns">
<xs:element name="root">
<xs:complexType>
<xs:attribute name="id" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
assert_attribute_form(
&schema_set,
TNS,
"",
"qualified attribute should be valid",
"unqualified attribute should be rejected when attributeFormDefault=qualified",
);
}
#[test]
fn test_attribute_form_qualified_explicit() {
let schema_set = load_schema(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.com/ns"
xmlns:tns="http://example.com/ns">
<xs:element name="root">
<xs:complexType>
<xs:attribute name="id" type="xs:string" form="qualified"/>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
assert_attribute_form(
&schema_set,
TNS,
"",
"form=qualified attribute should be valid",
"unqualified attribute should be rejected when form=qualified",
);
}
#[test]
fn test_attribute_form_unqualified_explicit() {
let schema_set = load_schema(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.com/ns"
attributeFormDefault="qualified"
xmlns:tns="http://example.com/ns">
<xs:element name="root">
<xs:complexType>
<xs:attribute name="id" type="xs:string" form="unqualified"/>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
assert_attribute_form(
&schema_set,
"",
TNS,
"form=unqualified attribute should be valid",
"qualified attribute should be rejected when form=unqualified",
);
}
#[test]
fn test_attribute_form_default_unqualified() {
let schema_set = load_schema(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.com/ns"
xmlns:tns="http://example.com/ns">
<xs:element name="root">
<xs:complexType>
<xs:attribute name="id" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
assert_attribute_form(
&schema_set,
"",
TNS,
"default unqualified attribute should be valid",
"qualified attribute should be rejected when default is unqualified",
);
}
#[test]
fn test_attribute_group_form_qualified() {
let schema_set = load_schema(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.com/ns"
attributeFormDefault="qualified"
xmlns:tns="http://example.com/ns">
<xs:attributeGroup name="myAttrs">
<xs:attribute name="id" type="xs:string"/>
</xs:attributeGroup>
<xs:element name="root">
<xs:complexType>
<xs:attributeGroup ref="tns:myAttrs"/>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
assert_attribute_form(
&schema_set,
TNS,
"",
"qualified attribute from group should be valid",
"unqualified attribute should be rejected for qualified group attribute",
);
}
#[cfg(feature = "xsd11")]
#[test]
fn test_attribute_explicit_target_namespace() {
let schema_set = load_schema_xsd11(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.com/ns"
xmlns:tns="http://example.com/ns"
xmlns:other="http://other.com/ns">
<xs:element name="root">
<xs:complexType>
<xs:attribute name="id" type="xs:string"
targetNamespace="http://other.com/ns"/>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
assert_attribute_form(
&schema_set,
"http://other.com/ns",
TNS,
"explicit targetNamespace attribute should be valid",
"attribute with wrong namespace should be rejected",
);
}
fn id_idref_attr_schema() -> crate::schema::SchemaSet {
load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="id" type="xs:ID" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="link" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="ref" type="xs:IDREF" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="multi" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="refs" type="xs:IDREFS" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
)
}
#[test]
fn test_idref_forward_reference() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:choice maxOccurs="unbounded">
<xs:element name="item">
<xs:complexType>
<xs:attribute name="id" type="xs:ID" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="link">
<xs:complexType>
<xs:attribute name="ref" type="xs:IDREF" use="required"/>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("link", "", None, None, &ns);
v.validate_attribute("ref", "", "future");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "future");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink.errors.is_empty(),
"Forward IDREF reference should be valid, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_idrefs_all_valid() {
let schema_set = id_idref_attr_schema();
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "a1");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "a2");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("multi", "", None, None, &ns);
v.validate_attribute("refs", "", "a1 a2");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink.errors.is_empty(),
"IDREFS with all valid tokens should succeed, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_idrefs_one_missing_one_valid() {
let schema_set = id_idref_attr_schema();
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "exists");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("multi", "", None, None, &ns);
v.validate_attribute("refs", "", "exists ghost");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let idref_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.1")
.collect();
assert_eq!(
idref_errors.len(),
1,
"Expected 1 IDREF error for 'ghost', got: {:?}",
idref_errors
);
}
#[test]
fn test_idrefs_multiple_missing() {
let schema_set = id_idref_attr_schema();
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "only");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("multi", "", None, None, &ns);
v.validate_attribute("refs", "", "nope1 nope2 nope3");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let idref_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.1")
.collect();
assert_eq!(
idref_errors.len(),
3,
"Expected 3 IDREF errors for nope1/nope2/nope3, got: {:?}",
idref_errors
);
}
#[test]
fn test_idrefs_empty_after_collapse() {
let schema_set = id_idref_attr_schema();
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("multi", "", None, None, &ns);
v.validate_attribute("refs", "", " ");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
!v.sink.errors.is_empty(),
"IDREFS with only whitespace should produce an error"
);
let idref_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.1")
.collect();
assert!(
idref_errors.is_empty(),
"Empty IDREFS should not produce cvc-id.1 errors (lexical rejection), got: {:?}",
idref_errors
);
}
#[test]
fn test_id_invalid_ncname() {
let schema_set = id_idref_attr_schema();
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "1bad");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
!v.sink.errors.is_empty(),
"Invalid NCName for ID should produce an error"
);
let id_dup_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.2")
.collect();
assert!(
id_dup_errors.is_empty(),
"Invalid NCName should not produce cvc-id.2, got: {:?}",
id_dup_errors
);
}
#[test]
fn test_idref_invalid_ncname() {
let schema_set = id_idref_attr_schema();
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("link", "", None, None, &ns);
v.validate_attribute("ref", "", "bad:name");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
!v.sink.errors.is_empty(),
"Invalid NCName for IDREF should produce an error"
);
let idref_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.1")
.collect();
assert!(
idref_errors.is_empty(),
"Invalid IDREF should not produce cvc-id.1 (no runtime tracking), got: {:?}",
idref_errors
);
}
#[test]
fn test_idrefs_one_invalid_ncname_token() {
let schema_set = id_idref_attr_schema();
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("multi", "", None, None, &ns);
v.validate_attribute("refs", "", "good 2bad");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
!v.sink.errors.is_empty(),
"IDREFS with one invalid token should produce an error"
);
let idref_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.1")
.collect();
assert!(
idref_errors.is_empty(),
"Invalid IDREFS should not produce cvc-id.1 errors, got: {:?}",
idref_errors
);
}
#[test]
fn test_element_text_id_duplicate() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="id" type="xs:ID" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("id", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("alpha");
v.validate_end_element();
v.validate_element("id", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("alpha"); v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink.errors.iter().any(|e| e.constraint == "cvc-id.2"),
"Duplicate ID in element text should raise cvc-id.2, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_element_text_idref_resolution() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="id" type="xs:ID" maxOccurs="unbounded"/>
<xs:element name="ref" type="xs:IDREF" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("id", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("x1");
v.validate_end_element();
v.validate_element("ref", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("x1");
v.validate_end_element();
v.validate_element("ref", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("missing");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let idref_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.1")
.collect();
assert_eq!(
idref_errors.len(),
1,
"Expected 1 cvc-id.1 error for element-text IDREF 'missing', got: {:?}",
idref_errors
);
}
#[test]
fn test_derived_id_duplicate_detection() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="myID">
<xs:restriction base="xs:ID">
<xs:maxLength value="20"/>
</xs:restriction>
</xs:simpleType>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="id" type="myID" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "dup");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "dup"); v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink.errors.iter().any(|e| e.constraint == "cvc-id.2"),
"Derived xs:ID should still detect duplicates, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_derived_idref_tracking() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="myIDREF">
<xs:restriction base="xs:IDREF">
<xs:maxLength value="20"/>
</xs:restriction>
</xs:simpleType>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="id" type="xs:ID" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="link" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="ref" type="myIDREF" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "ok");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("link", "", None, None, &ns);
v.validate_attribute("ref", "", "ok");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("link", "", None, None, &ns);
v.validate_attribute("ref", "", "nope");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let idref_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.1")
.collect();
assert_eq!(
idref_errors.len(),
1,
"Derived xs:IDREF should track references, got: {:?}",
idref_errors
);
}
#[test]
fn test_derived_idrefs_tracking() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="myIDREFS">
<xs:restriction base="xs:IDREFS">
<xs:maxLength value="5"/>
</xs:restriction>
</xs:simpleType>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="id" type="xs:ID" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="multi" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="refs" type="myIDREFS" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "x");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("multi", "", None, None, &ns);
v.validate_attribute("refs", "", "x y");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let idref_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.1")
.collect();
assert_eq!(
idref_errors.len(),
1,
"Derived xs:IDREFS should track each token, got: {:?}",
idref_errors
);
}
#[test]
fn test_repeated_idref_no_false_duplicate() {
let schema_set = id_idref_attr_schema();
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "target");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("link", "", None, None, &ns);
v.validate_attribute("ref", "", "target");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("link", "", None, None, &ns);
v.validate_attribute("ref", "", "target");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("multi", "", None, None, &ns);
v.validate_attribute("refs", "", "target target");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink.errors.is_empty(),
"Repeated IDREF to same ID should not error, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_invalid_lexical_does_not_poison_tracking() {
let schema_set = id_idref_attr_schema();
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "123bad");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "good");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("link", "", None, None, &ns);
v.validate_attribute("ref", "", "123bad");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("link", "", None, None, &ns);
v.validate_attribute("ref", "", "good");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let dup_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.2")
.collect();
assert!(
dup_errors.is_empty(),
"Invalid lexical values should not cause cvc-id.2, got: {:?}",
dup_errors
);
let idref_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.1")
.collect();
assert!(
idref_errors.is_empty(),
"Invalid IDREF '123bad' should fail lexically, not produce cvc-id.1, got: {:?}",
idref_errors
);
}
#[test]
fn test_custom_idref_list_tracking() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="myRefList">
<xs:list itemType="xs:IDREF"/>
</xs:simpleType>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="id" type="xs:ID" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="refs" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="targets" type="myRefList" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "a1");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("refs", "", None, None, &ns);
v.validate_attribute("targets", "", "a1 missing1 missing2");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let idref_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.1")
.collect();
assert_eq!(
idref_errors.len(), 2,
"Custom IDREF-list should track each token; expected 2 cvc-id.1 errors for missing1/missing2, got: {:?}",
idref_errors
);
}
#[test]
fn test_whitespace_normalization_id_idref_match() {
let schema_set = id_idref_attr_schema();
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", " foo ");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("link", "", None, None, &ns);
v.validate_attribute("ref", "", "foo");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("link", "", None, None, &ns);
v.validate_attribute("ref", "", " foo ");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("multi", "", None, None, &ns);
v.validate_attribute("refs", "", " foo ");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let idref_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.1")
.collect();
assert!(
idref_errors.is_empty(),
"Whitespace-padded ID/IDREF/IDREFS should all resolve after collapse, got: {:?}",
idref_errors
);
let dup_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.2")
.collect();
assert!(
dup_errors.is_empty(),
"Single whitespace-padded ID should not produce duplicates, got: {:?}",
dup_errors
);
}
#[test]
fn test_whitespace_normalization_element_text() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="id" type="xs:ID" maxOccurs="unbounded"/>
<xs:element name="ref" type="xs:IDREF" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("id", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text(" bar ");
v.validate_end_element();
v.validate_element("ref", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("bar");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let idref_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.1")
.collect();
assert!(
idref_errors.is_empty(),
"Element-text ID ' bar ' collapsed to 'bar' should match IDREF 'bar', got: {:?}",
idref_errors
);
}
#[test]
fn test_xsi_type_unresolved_on_global_element() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let info = v.validate_element("root", "", Some("noSuchType"), None, &ns);
assert_eq!(info.validity, SchemaValidity::Invalid);
assert!(info.schema_type.is_some());
assert!(
v.sink.errors.iter().any(|e| e.constraint == "cvc-elt.4.1"),
"Expected cvc-elt.4.1 error, got: {:?}",
v.sink.errors
);
v.validate_end_of_attributes();
v.validate_text("hello");
let end_info = v.validate_end_element();
let type_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint != "cvc-elt.4.1")
.collect();
assert!(
type_errors.is_empty(),
"Expected only cvc-elt.4.1 error, but got additional: {:?}",
type_errors
);
assert_eq!(end_info.validity, SchemaValidity::Invalid);
v.end_validation().ok();
}
#[test]
fn test_xsi_type_invalid_derivation_on_global_element() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
<xs:complexType name="unrelatedType">
<xs:sequence>
<xs:element name="child" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let info = v.validate_element("root", "", Some("unrelatedType"), None, &ns);
assert_eq!(info.validity, SchemaValidity::Invalid);
assert!(info.schema_type.is_some());
assert!(
v.sink.errors.iter().any(|e| e.constraint == "cvc-elt.4.2"),
"Expected cvc-elt.4.2 error, got: {:?}",
v.sink.errors
);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.end_validation().ok();
let other_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint != "cvc-elt.4.2")
.collect();
assert!(
other_errors.is_empty(),
"Expected only cvc-elt.4.2 error, but got additional: {:?}",
other_errors
);
}
#[test]
fn test_xsi_type_unresolved_on_local_element_with_type() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
let info = v.validate_element("item", "", Some("noSuchType"), None, &ns);
assert_eq!(info.validity, SchemaValidity::Invalid);
assert!(info.schema_type.is_some());
assert!(
v.sink.errors.iter().any(|e| e.constraint == "cvc-elt.4.1"),
"Expected cvc-elt.4.1 error, got: {:?}",
v.sink.errors
);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
}
#[test]
fn test_xsi_type_unresolved_lax_assessment() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:any processContents="lax"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
let info = v.validate_element("unknown", "", Some("noSuchType"), None, &ns);
assert!(info.schema_type.is_none());
v.validate_end_of_attributes();
v.validate_element("nested", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element(); v.validate_end_element(); v.end_validation().ok();
assert!(
v.sink.errors.iter().any(|e| e.constraint == "cvc-elt.4.1"),
"Expected cvc-elt.4.1 error, got: {:?}",
v.sink.errors
);
let content_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-complex-type.2.4")
.collect();
assert!(
content_errors.is_empty(),
"Lax assessment should not produce content model errors, got: {:?}",
content_errors
);
}
#[test]
fn test_undeclared_element_lax_allows_children() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:any processContents="lax"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
let info = v.validate_element("unknown", "", None, None, &ns);
assert!(info.schema_type.is_none());
v.validate_end_of_attributes();
v.validate_element("child1", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("text1");
v.validate_end_element();
v.validate_element("child2", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element(); v.validate_end_element(); v.end_validation().ok();
assert!(
v.sink.errors.is_empty(),
"Lax undeclared element should accept children without errors, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_undeclared_element_skip_no_assessment() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:any processContents="skip"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("anything", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("nested1", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("nested2", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("deep");
v.validate_end_element(); v.validate_end_element();
v.validate_end_element(); v.validate_end_element(); v.end_validation().ok();
assert!(
v.sink.errors.is_empty(),
"Skip wildcard should accept all nested content without errors, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_strict_undeclared_same_assessment_as_lax() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://test">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:any namespace="http://other" processContents="strict"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "http://test", None, None, &ns);
v.validate_end_of_attributes();
let info = v.validate_element("unknown", "http://other", None, None, &ns);
assert_eq!(info.validity, SchemaValidity::Invalid);
assert!(
v.sink.errors.iter().any(|e| e.constraint == "cvc-elt.1"),
"Expected cvc-elt.1 error, got: {:?}",
v.sink.errors
);
v.validate_end_of_attributes();
v.validate_element("child", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element(); v.validate_end_element(); v.end_validation().ok();
let content_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-complex-type.2.4")
.collect();
assert!(
content_errors.is_empty(),
"Strict undeclared element should use lax assessment for children, got: {:?}",
content_errors
);
}
#[cfg(feature = "xsd11")]
#[test]
fn test_cta_preserves_xsi_type_invalidity() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="typeA">
<xs:sequence>
<xs:element name="a" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="typeB">
<xs:complexContent>
<xs:extension base="typeA">
<xs:sequence>
<xs:element name="b" type="xs:string"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="root" type="typeA">
<xs:alternative test="@kind = 'B'" type="typeB"/>
</xs:element>
</xs:schema>"#,
);
let flags = ValidationFlags::default() | ValidationFlags::PROCESS_ASSERTIONS;
let validator = SchemaValidator::new(&schema_set, flags);
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let info = v.validate_element("root", "", Some("noSuchType"), None, &ns);
assert_eq!(info.validity, SchemaValidity::Invalid);
v.validate_attribute("kind", "", "B");
let eoa_info = v.validate_end_of_attributes();
assert_eq!(
eoa_info.validity,
SchemaValidity::Invalid,
"CTA switch should preserve prior invalidity from bad xsi:type"
);
v.validate_element("b", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element(); v.end_validation().ok();
assert!(
v.sink.errors.iter().any(|e| e.constraint == "cvc-elt.4.1"),
"Expected cvc-elt.4.1 for bad xsi:type, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_lax_assessment_validates_attributes_against_any_type() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:any processContents="lax"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
let info = v.validate_element("unknown", "", None, None, &ns);
assert!(info.schema_type.is_none());
let attr_info = v.validate_attribute("myattr", "", "some-value");
assert_ne!(
attr_info.validity,
SchemaValidity::Invalid,
"Lax assessment should accept attributes via xs:anyType's anyAttribute wildcard"
);
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let attr_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint.contains("cvc-complex-type"))
.collect();
assert!(
attr_errors.is_empty(),
"Lax assessment should not produce attribute errors, got: {:?}",
attr_errors
);
}
#[test]
fn test_skip_descendant_globally_declared_not_validated() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:any processContents="skip"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="known" type="xs:integer"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("wrapper", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("known", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("not-an-integer");
v.validate_end_element();
v.validate_end_element(); v.validate_end_element(); v.end_validation().ok();
assert!(
v.sink.errors.is_empty(),
"Globally declared element inside skip subtree should not be validated, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_strict_wildcard_xsi_type_supplies_governing_type() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://test">
<xs:complexType name="myType">
<xs:sequence>
<xs:element name="child" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:any namespace="http://other" processContents="strict"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let tns_prefix = schema_set.name_table.add("tns");
let tns_uri = schema_set.name_table.add("http://test");
let ns = NamespaceContextSnapshot {
default_ns: None,
bindings: vec![(tns_prefix, tns_uri)],
};
v.validate_element("root", "http://test", None, None, &ns);
v.validate_end_of_attributes();
let info = v.validate_element("foo", "http://other", Some("tns:myType"), None, &ns);
assert!(
info.schema_type.is_some(),
"xsi:type should supply governing type even without global declaration"
);
let elt1_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-elt.1")
.collect();
assert!(
elt1_errors.is_empty(),
"Strict wildcard should not report cvc-elt.1 when xsi:type supplies a type, got: {:?}",
elt1_errors
);
v.validate_end_of_attributes();
v.validate_element("child", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element(); v.validate_end_element(); v.end_validation().ok();
assert!(
v.sink.errors.is_empty(),
"No errors expected when xsi:type supplies valid governing type, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_type_source_declaration() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let info = v.validate_element("root", "", None, None, &ns);
assert_eq!(info.type_source, Some(TypeSource::Declaration));
v.validate_end_of_attributes();
v.validate_text("hello");
let end_info = v.validate_end_element();
assert_eq!(end_info.type_source, Some(TypeSource::Declaration));
v.end_validation().ok();
}
#[test]
fn test_type_source_xsi_type() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:anyType"/>
<xs:complexType name="myType">
<xs:sequence>
<xs:element name="child" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let info = v.validate_element("root", "", Some("myType"), None, &ns);
assert_eq!(info.type_source, Some(TypeSource::XsiType));
v.validate_end_of_attributes();
v.validate_element("child", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
let end_info = v.validate_end_element();
assert_eq!(end_info.type_source, Some(TypeSource::XsiType));
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[cfg(feature = "xsd11")]
#[test]
fn test_type_source_cta() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="intContent">
<xs:sequence>
<xs:element name="val" type="xs:integer"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
</xs:complexType>
<xs:element name="data">
<xs:complexType>
<xs:sequence>
<xs:element name="val" type="xs:string"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
</xs:complexType>
<xs:alternative test="@kind='int'" type="intContent"/>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let info = v.validate_element("data", "", None, None, &ns);
assert_eq!(info.type_source, Some(TypeSource::Declaration));
v.validate_attribute("kind", "", "int");
let eoa_info = v.validate_end_of_attributes();
assert_eq!(eoa_info.type_source, Some(TypeSource::TypeAlternative));
assert!(eoa_info.cta_selected);
v.validate_element("val", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("42");
v.validate_end_element();
let end_info = v.validate_end_element();
assert_eq!(end_info.type_source, Some(TypeSource::TypeAlternative));
assert!(end_info.cta_selected);
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[cfg(feature = "xsd11")]
#[test]
fn test_cta_selected_same_type() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="baseType">
<xs:sequence>
<xs:element name="val" type="xs:string"/>
</xs:sequence>
<xs:attribute name="kind" type="xs:string"/>
</xs:complexType>
<xs:element name="data" type="baseType">
<xs:alternative test="@kind='same'" type="baseType"/>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("data", "", None, None, &ns);
v.validate_attribute("kind", "", "same");
let eoa_info = v.validate_end_of_attributes();
assert!(
eoa_info.cta_selected,
"cta_selected should be true even when type is unchanged"
);
assert_eq!(eoa_info.type_source, Some(TypeSource::TypeAlternative));
v.validate_element("val", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_type_source_end_element() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:integer"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let start_info = v.validate_element("root", "", None, None, &ns);
assert_eq!(start_info.type_source, Some(TypeSource::Declaration));
v.validate_end_of_attributes();
v.validate_text("42");
let end_info = v.validate_end_element();
assert_eq!(end_info.type_source, Some(TypeSource::Declaration));
assert_eq!(end_info.validity, SchemaValidity::Valid);
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[cfg(feature = "xsd11")]
#[test]
fn test_assertion_outcome_passed() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val >= 0"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("val", "", "42");
v.validate_end_of_attributes();
let end_info = v.validate_end_element();
assert_eq!(
end_info.assertion_outcome,
Some(AssertionOutcome::Passed),
"Passing assertion should yield Passed"
);
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[cfg(feature = "xsd11")]
#[test]
fn test_assertion_outcome_failed() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val >= 0"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("val", "", "-5"); v.validate_end_of_attributes();
let end_info = v.validate_end_element();
assert_eq!(
end_info.assertion_outcome,
Some(AssertionOutcome::Failed),
"Failing assertion should yield Failed"
);
v.end_validation().ok();
assert!(!v.sink.errors.is_empty(), "Should have assertion error");
}
#[cfg(feature = "xsd11")]
#[test]
fn test_assertion_outcome_not_evaluated() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:attribute name="val" type="xs:integer"/>
<xs:assert test="@val >= 0"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("val", "", "42");
v.validate_end_of_attributes();
let end_info = v.validate_end_element();
assert_eq!(
end_info.assertion_outcome,
Some(AssertionOutcome::NotEvaluated),
"Assertions exist but PROCESS_ASSERTIONS not set → NotEvaluated"
);
v.end_validation().ok();
}
#[cfg(feature = "xsd11")]
#[test]
fn test_no_assertions_outcome_none() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new_fragment_buffer(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
let end_info = v.validate_end_element();
assert_eq!(
end_info.assertion_outcome, None,
"No assertions on type → None"
);
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
#[cfg(feature = "xsd11")]
fn test_inheritable_basic() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="child" type="xs:string"/>
</xs:sequence>
<xs:attribute name="lang" type="xs:string" inheritable="true"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("lang", "", "en");
v.validate_end_of_attributes();
v.validate_element("child", "", None, None, &ns);
v.validate_end_of_attributes();
let inherited = v.get_inherited_attributes();
assert_eq!(inherited.len(), 1, "child should inherit 'lang'");
let lang = &inherited[0];
assert_eq!(v.schema_set.name_table.resolve(lang.local_name), "lang");
assert_eq!(lang.value, "en");
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
#[cfg(feature = "xsd11")]
fn test_inheritable_override() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="mid">
<xs:complexType>
<xs:sequence>
<xs:element name="leaf" type="xs:string"/>
</xs:sequence>
<xs:attribute name="lang" type="xs:string" inheritable="true"/>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="lang" type="xs:string" inheritable="true"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("lang", "", "en");
v.validate_end_of_attributes();
v.validate_element("mid", "", None, None, &ns);
let mid_inherited = v.get_inherited_attributes();
assert_eq!(mid_inherited.len(), 1);
assert_eq!(
mid_inherited[0].value, "en",
"overriding element itself should still see ancestor value from incoming_inherited"
);
v.validate_attribute("lang", "", "fr"); v.validate_end_of_attributes();
let mid_inherited_after = v.get_inherited_attributes();
assert_eq!(
mid_inherited_after[0].value, "en",
"PSVI [inherited attributes] is the incoming snapshot, not affected by own attrs"
);
v.validate_element("leaf", "", None, None, &ns);
v.validate_end_of_attributes();
let inherited = v.get_inherited_attributes();
assert_eq!(inherited.len(), 1);
assert_eq!(
inherited[0].value, "fr",
"grandchild should see overridden value"
);
v.validate_text("text");
v.validate_end_element();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
#[cfg(feature = "xsd11")]
fn test_inheritable_multi_level() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="mid">
<xs:complexType>
<xs:sequence>
<xs:element name="leaf" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="lang" type="xs:string" inheritable="true"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("lang", "", "en");
v.validate_end_of_attributes();
v.validate_element("mid", "", None, None, &ns);
v.validate_end_of_attributes();
let mid_inherited = v.get_inherited_attributes();
assert_eq!(mid_inherited.len(), 1, "mid should inherit 'lang'");
assert_eq!(mid_inherited[0].value, "en");
v.validate_element("leaf", "", None, None, &ns);
v.validate_end_of_attributes();
let leaf_inherited = v.get_inherited_attributes();
assert_eq!(leaf_inherited.len(), 1, "leaf should inherit 'lang'");
assert_eq!(leaf_inherited[0].value, "en");
v.validate_text("text");
v.validate_end_element();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
#[cfg(feature = "xsd11")]
fn test_inheritable_not_set() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="child" type="xs:string"/>
</xs:sequence>
<xs:attribute name="lang" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("lang", "", "en");
v.validate_end_of_attributes();
v.validate_element("child", "", None, None, &ns);
v.validate_end_of_attributes();
let inherited = v.get_inherited_attributes();
assert!(
inherited.is_empty(),
"non-inheritable attr should NOT be inherited"
);
v.validate_text("text");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
#[cfg(feature = "xsd11")]
fn test_inheritable_no_child_type_filter() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="child">
<xs:complexType>
<xs:sequence>
<xs:element name="x" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="lang" type="xs:string" inheritable="true"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("lang", "", "en");
v.validate_end_of_attributes();
v.validate_element("child", "", None, None, &ns);
v.validate_end_of_attributes();
let inherited = v.get_inherited_attributes();
assert_eq!(
inherited.len(),
1,
"inherited attrs should be present even when child type doesn't declare it"
);
assert_eq!(inherited[0].value, "en");
v.validate_element("x", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("text");
v.validate_end_element();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
#[cfg(feature = "xsd11")]
fn test_inheritable_cta() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="enType">
<xs:sequence>
<xs:element name="val" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:element name="item">
<xs:complexType>
<xs:sequence>
<xs:element name="other" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:alternative test="@lang='en'" type="enType"/>
</xs:element>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element ref="item"/>
</xs:sequence>
<xs:attribute name="lang" type="xs:string" inheritable="true"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("lang", "", "en");
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
let eoa_info = v.validate_end_of_attributes();
assert!(
eoa_info.cta_selected,
"CTA should have selected a type using inherited lang"
);
v.validate_element("val", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink.errors.is_empty(),
"CTA with inherited attr should produce no errors, got: {:?}",
v.sink.errors
);
}
#[test]
#[cfg(feature = "xsd11")]
fn test_inheritable_wildcard() {
let schema_set = load_schema_xsd11(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:attribute name="lang" type="xs:string" inheritable="true"/>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="child" type="xs:string"/>
</xs:sequence>
<xs:anyAttribute namespace="##any" processContents="lax"/>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("lang", "", "en");
v.validate_end_of_attributes();
v.validate_element("child", "", None, None, &ns);
v.validate_end_of_attributes();
let inherited = v.get_inherited_attributes();
assert_eq!(
inherited.len(),
1,
"wildcard-backed inheritable attr should be inherited"
);
assert_eq!(inherited[0].value, "en");
v.validate_text("text");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
#[cfg(feature = "xsd11")]
fn test_inheritable_default_shadows_ancestor() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="mid">
<xs:complexType>
<xs:sequence>
<xs:element name="leaf" type="xs:string"/>
</xs:sequence>
<xs:attribute name="lang" type="xs:string"
inheritable="true" default="fr"/>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="lang" type="xs:string" inheritable="true"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("lang", "", "en");
v.validate_end_of_attributes();
v.validate_element("mid", "", None, None, &ns);
v.validate_end_of_attributes();
let mid_inherited = v.get_inherited_attributes();
assert_eq!(mid_inherited.len(), 1);
assert_eq!(
mid_inherited[0].value, "en",
"mid's incoming inherited should be ancestor's en"
);
v.validate_element("leaf", "", None, None, &ns);
v.validate_end_of_attributes();
let leaf_inherited = v.get_inherited_attributes();
assert_eq!(leaf_inherited.len(), 1);
assert_eq!(
leaf_inherited[0].value, "fr",
"leaf should see defaulted value fr, not ancestor en"
);
v.validate_text("text");
v.validate_end_element();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_psvi_normalized_value_token() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:token"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text(" hello world ");
let end_info = v.validate_end_element();
assert_eq!(end_info.validity, SchemaValidity::Valid);
assert_eq!(
end_info.normalized_value.as_deref(),
Some("hello world"),
"xs:token should collapse whitespace"
);
v.end_validation().ok();
}
#[test]
fn test_psvi_normalized_value_string() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text(" hello world ");
let end_info = v.validate_end_element();
assert_eq!(end_info.validity, SchemaValidity::Valid);
assert_eq!(
end_info.normalized_value.as_deref(),
Some(" hello world "),
"xs:string should preserve whitespace"
);
v.end_validation().ok();
}
#[test]
fn test_psvi_normalized_value_attribute() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:attribute name="val" type="xs:token"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let attr_info = v.validate_attribute("val", "", " a b ");
assert_eq!(
attr_info.normalized_value.as_deref(),
Some("a b"),
"xs:token attribute should collapse whitespace"
);
v.validate_end_of_attributes();
v.validate_end_element();
v.end_validation().ok();
}
#[test]
fn test_psvi_schema_error_codes_invalid_type() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:integer"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("not-a-number");
let end_info = v.validate_end_element();
assert_eq!(end_info.validity, SchemaValidity::Invalid);
assert!(
!end_info.schema_error_codes.is_empty(),
"invalid element should have error codes"
);
v.end_validation().ok();
}
#[test]
fn test_psvi_schema_error_codes_valid_element() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
let end_info = v.validate_end_element();
assert_eq!(end_info.validity, SchemaValidity::Valid);
assert!(
end_info.schema_error_codes.is_empty(),
"valid element should have no error codes"
);
v.end_validation().ok();
}
#[test]
fn test_psvi_schema_error_codes_duplicate_attribute() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:attribute name="a" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("a", "", "first");
let dup_info = v.validate_attribute("a", "", "second");
assert_eq!(dup_info.validity, SchemaValidity::Invalid);
assert!(
dup_info.schema_error_codes.contains(&"cvc-complex-type.3"),
"duplicate attribute should have cvc-complex-type.3 error code, got {:?}",
dup_info.schema_error_codes
);
v.validate_end_of_attributes();
v.validate_end_element();
v.end_validation().ok();
}
#[test]
fn test_psvi_validation_attempted_full() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
let end_info = v.validate_end_element();
assert_eq!(
end_info.validation_attempted,
crate::validation::ValidationAttempted::Full,
"fully validated element should report Full"
);
v.end_validation().ok();
}
#[test]
fn test_psvi_validation_attempted_attribute_full() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:attribute name="a" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let attr_info = v.validate_attribute("a", "", "val");
assert_eq!(
attr_info.validation_attempted,
crate::validation::ValidationAttempted::Full,
"declared attribute should report Full"
);
v.validate_end_of_attributes();
v.validate_end_element();
v.end_validation().ok();
}
#[test]
fn test_psvi_is_simple_complex_type() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="simple" type="xs:string"/>
<xs:element name="complex">
<xs:complexType>
<xs:sequence/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let info = v.validate_element("simple", "", None, None, &ns);
assert!(
info.is_simple_type(),
"xs:string element should be simple type"
);
assert!(!info.is_complex_type());
v.validate_end_of_attributes();
v.validate_text("hi");
v.validate_end_element();
let info = v.validate_element("complex", "", None, None, &ns);
assert!(
info.is_complex_type(),
"complexType element should be complex type"
);
assert!(!info.is_simple_type());
v.validate_end_of_attributes();
v.validate_end_element();
v.end_validation().ok();
}
#[test]
fn test_psvi_identity_constraint_tables() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="id" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:unique name="itemId">
<xs:selector xpath="item"/>
<xs:field xpath="@id"/>
</xs:unique>
</xs:element>
</xs:schema>"#,
);
let flags = ValidationFlags::default() | ValidationFlags::PROCESS_IDENTITY_CONSTRAINTS;
let validator = SchemaValidator::new(&schema_set, flags);
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "a");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "b");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
let tables = v.identity_constraint_tables();
assert!(
tables.is_some(),
"IC tables should be available after validation"
);
let tables = tables.unwrap();
assert!(!tables.is_empty(), "should have at least one IC table");
let table = tables.values().next().unwrap();
assert_eq!(
table.sequences.len(),
2,
"unique constraint should have 2 key sequences"
);
}
#[test]
fn test_psvi_schema_error_codes_root_xsi_type() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
let _start = v.validate_element("root", "", Some("xs:nonExistentType"), None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
let end_info = v.validate_end_element();
assert!(
end_info.schema_error_codes.contains(&"cvc-elt.4.1"),
"root xsi:type error should be in element schema_error_codes, got {:?}",
end_info.schema_error_codes
);
v.end_validation().ok();
}
#[test]
fn test_psvi_schema_error_codes_wildcard_xsi_type() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:any processContents="lax"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let xsi_ns = "http://www.w3.org/2001/XMLSchema-instance";
let xs_ns = "http://www.w3.org/2001/XMLSchema";
let ns = NamespaceContextSnapshot {
default_ns: None,
bindings: vec![
(
v.schema_set.name_table.add("xsi"),
v.schema_set.name_table.add(xsi_ns),
),
(
v.schema_set.name_table.add("xs"),
v.schema_set.name_table.add(xs_ns),
),
],
};
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("child", "", Some("xs:nonExistentType"), None, &ns);
v.validate_end_of_attributes();
let end_info = v.validate_end_element();
assert!(
end_info.schema_error_codes.contains(&"cvc-elt.4.1"),
"wildcard child xsi:type error should be in child schema_error_codes, got {:?}",
end_info.schema_error_codes
);
let parent_end = v.validate_end_element();
assert!(
!parent_end.schema_error_codes.contains(&"cvc-elt.4.1"),
"parent should not carry child's xsi:type error code, got {:?}",
parent_end.schema_error_codes
);
v.end_validation().ok();
}
const XSI_NS: &str = "http://www.w3.org/2001/XMLSchema-instance";
#[test]
fn test_xsi_no_namespace_schema_location_valid() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let info = v.validate_attribute("noNamespaceSchemaLocation", XSI_NS, "schema.xsd");
assert_eq!(info.validity, SchemaValidity::Valid);
assert_eq!(info.validation_attempted, ValidationAttempted::Full);
assert_eq!(
info.schema_type,
Some(TypeKey::Simple(schema_set.builtin_types().any_uri))
);
assert!(info.typed_value.is_some());
assert!(info.attribute_decl.is_some());
assert_eq!(
v.no_namespace_schema_location_hints(),
&[NoNamespaceSchemaLocationHint {
location: "schema.xsd".to_string(),
base_uri: String::new(),
}]
);
assert!(v.schema_location_hints().is_empty());
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_xsi_schema_location_valid() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let info = v.validate_attribute(
"schemaLocation",
XSI_NS,
"http://example.com schema.xsd http://other.com other.xsd",
);
assert_eq!(info.validity, SchemaValidity::Valid);
assert_eq!(info.validation_attempted, ValidationAttempted::Full);
assert_eq!(
info.schema_type,
Some(TypeKey::Simple(
schema_set.builtin_types().xsi_schema_location_type
))
);
assert_eq!(
v.schema_location_hints(),
&[
SchemaLocationHint {
namespace: "http://example.com".to_string(),
location: "schema.xsd".to_string(),
base_uri: String::new(),
},
SchemaLocationHint {
namespace: "http://other.com".to_string(),
location: "other.xsd".to_string(),
base_uri: String::new(),
},
]
);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_xsi_schema_location_odd_token_count() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let info = v.validate_attribute(
"schemaLocation",
XSI_NS,
"http://example.com schema.xsd extra",
);
assert_eq!(info.validity, SchemaValidity::Invalid);
assert_eq!(info.validation_attempted, ValidationAttempted::Full);
assert_eq!(
v.schema_location_hints(),
&[SchemaLocationHint {
namespace: "http://example.com".to_string(),
location: "schema.xsd".to_string(),
base_uri: String::new(),
}]
);
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-schema-location"),
"expected cvc-schema-location error, got {:?}",
v.sink.errors
);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.end_validation().ok();
}
#[test]
fn test_xsi_type_attribute_schema_info() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let info = v.validate_attribute("type", XSI_NS, "xs:string");
assert_eq!(info.validation_attempted, ValidationAttempted::Full);
assert_eq!(
info.schema_type,
Some(TypeKey::Simple(schema_set.builtin_types().qname))
);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.end_validation().ok();
}
#[test]
fn test_xsi_nil_attribute_schema_info() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string" nillable="true"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, Some("true"), &ns);
let info = v.validate_attribute("nil", XSI_NS, "true");
assert_eq!(info.validity, SchemaValidity::Valid);
assert_eq!(info.validation_attempted, ValidationAttempted::Full);
assert_eq!(
info.schema_type,
Some(TypeKey::Simple(schema_set.builtin_types().boolean))
);
v.validate_end_of_attributes();
v.validate_end_element();
v.end_validation().ok();
assert!(v.sink.errors.is_empty(), "errors: {:?}", v.sink.errors);
}
#[test]
fn test_xsi_nil_invalid_value() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string" nillable="true"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, Some("maybe"), &ns);
let info = v.validate_attribute("nil", XSI_NS, "maybe");
assert_eq!(info.validity, SchemaValidity::Invalid);
assert_eq!(info.validation_attempted, ValidationAttempted::Full);
assert_eq!(
info.schema_type,
Some(TypeKey::Simple(schema_set.builtin_types().boolean))
);
v.validate_end_of_attributes();
v.validate_end_element();
v.end_validation().ok();
}
#[test]
fn test_xsi_attrs_per_attribute_schema_info_returned() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let info_type = v.validate_attribute("type", XSI_NS, "xs:string");
assert!(info_type.schema_type.is_some());
let info_nil = v.validate_attribute("nil", XSI_NS, "false");
assert!(info_nil.schema_type.is_some());
let info_sl = v.validate_attribute("schemaLocation", XSI_NS, "http://ex.com a.xsd");
assert!(info_sl.schema_type.is_some());
let info_nnsl = v.validate_attribute("noNamespaceSchemaLocation", XSI_NS, "local.xsd");
assert!(info_nnsl.schema_type.is_some());
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.end_validation().ok();
}
#[test]
fn test_xsi_attrs_update_parent_validation_attempted() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let attr_info = v.validate_attribute("noNamespaceSchemaLocation", XSI_NS, "schema.xsd");
assert_eq!(attr_info.validation_attempted, ValidationAttempted::Full);
v.validate_end_of_attributes();
v.validate_text("hello");
let end_info = v.validate_end_element();
assert_eq!(end_info.validation_attempted, ValidationAttempted::Full);
v.end_validation().ok();
}
#[test]
fn test_unknown_xsi_attribute_goes_through_wildcard() {
let schema_set = load_schema(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:anyAttribute namespace="##other" processContents="lax"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let info = v.validate_attribute("unknownAttr", XSI_NS, "value");
assert_eq!(info.validation_attempted, ValidationAttempted::None);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.end_validation().ok();
}
#[test]
fn test_xsi_attribute_decl_returned_for_all_builtins() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
let info_type = v.validate_attribute("type", XSI_NS, "xs:string");
assert!(
info_type.attribute_decl.is_some(),
"xsi:type should have attribute_decl"
);
let info_nil = v.validate_attribute("nil", XSI_NS, "false");
assert!(
info_nil.attribute_decl.is_some(),
"xsi:nil should have attribute_decl"
);
let info_sl = v.validate_attribute("schemaLocation", XSI_NS, "http://ex.com a.xsd");
assert!(
info_sl.attribute_decl.is_some(),
"xsi:schemaLocation should have attribute_decl"
);
let info_nnsl = v.validate_attribute("noNamespaceSchemaLocation", XSI_NS, "a.xsd");
assert!(
info_nnsl.attribute_decl.is_some(),
"xsi:noNamespaceSchemaLocation should have attribute_decl"
);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.end_validation().ok();
}
#[test]
fn test_xsi_declarations_visible_through_namespace_lookup() {
use crate::namespace::table::well_known;
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let type_key =
schema_set.lookup_attribute(Some(well_known::XSI_NAMESPACE), well_known::XSI_TYPE);
assert!(type_key.is_some(), "xsi:type should be in namespace table");
let nil_key = schema_set.lookup_attribute(Some(well_known::XSI_NAMESPACE), well_known::XSI_NIL);
assert!(nil_key.is_some(), "xsi:nil should be in namespace table");
let sl_key = schema_set.lookup_attribute(
Some(well_known::XSI_NAMESPACE),
well_known::XSI_SCHEMA_LOCATION,
);
assert!(
sl_key.is_some(),
"xsi:schemaLocation should be in namespace table"
);
let nnsl_key = schema_set.lookup_attribute(
Some(well_known::XSI_NAMESPACE),
well_known::XSI_NO_NAMESPACE_SCHEMA_LOCATION,
);
assert!(
nnsl_key.is_some(),
"xsi:noNamespaceSchemaLocation should be in namespace table"
);
}
#[test]
fn test_xsi_schema_location_inherits_per_element_base_uri() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="child" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.set_instance_base_uri("file:///docs/instance.xml");
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("noNamespaceSchemaLocation", XSI_NS, "root.xsd");
v.validate_end_of_attributes();
v.validate_element("child", "", None, None, &ns);
v.validate_attribute(
"base",
"http://www.w3.org/XML/1998/namespace",
"http://other.com/schemas/",
);
v.validate_attribute("noNamespaceSchemaLocation", XSI_NS, "child.xsd");
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let hints = v.no_namespace_schema_location_hints();
assert_eq!(hints.len(), 2);
assert_eq!(hints[0].base_uri, "file:///docs/instance.xml");
assert_eq!(hints[1].base_uri, "http://other.com/schemas/");
}
#[test]
fn test_xsi_relative_xml_base_resolved_against_parent() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="child" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.set_instance_base_uri("http://example.com/docs/instance.xml");
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("base", "http://www.w3.org/XML/1998/namespace", "subdir/");
v.validate_attribute("noNamespaceSchemaLocation", XSI_NS, "sub.xsd");
v.validate_end_of_attributes();
v.validate_element("child", "", None, None, &ns);
v.validate_attribute("base", "http://www.w3.org/XML/1998/namespace", "deeper/");
v.validate_attribute("noNamespaceSchemaLocation", XSI_NS, "deep.xsd");
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let hints = v.no_namespace_schema_location_hints();
assert_eq!(hints.len(), 2);
assert_eq!(hints[0].base_uri, "http://example.com/docs/subdir/");
assert_eq!(hints[1].base_uri, "http://example.com/docs/subdir/deeper/");
}
#[test]
fn test_resolve_base_uri_windows_backslash() {
let result = super::resolve_base_uri("sub.xsd", r"C:\docs\instance.xml");
assert_eq!(result, r"C:\docs\sub.xsd");
}
#[test]
fn test_resolve_base_uri_absolute_replaces() {
let result = super::resolve_base_uri("http://new.com/", "http://old.com/docs/");
assert_eq!(result, "http://new.com/");
}
#[test]
fn test_resolve_base_uri_empty_inherits() {
let result = super::resolve_base_uri("", "http://example.com/base/");
assert_eq!(result, "http://example.com/base/");
}
#[test]
fn test_duplicate_xml_base_does_not_overwrite() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.set_instance_base_uri("http://example.com/docs/instance.xml");
v.validate_element("root", "", None, None, &ns);
v.validate_attribute(
"base",
"http://www.w3.org/XML/1998/namespace",
"http://first.com/",
);
v.validate_attribute(
"base",
"http://www.w3.org/XML/1998/namespace",
"http://second.com/",
);
v.validate_attribute("noNamespaceSchemaLocation", XSI_NS, "schema.xsd");
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.end_validation().ok();
let hints = v.no_namespace_schema_location_hints();
assert_eq!(hints.len(), 1);
assert_eq!(
hints[0].base_uri, "http://first.com/",
"duplicate xml:base should not overwrite the first"
);
}
#[test]
fn test_xml_base_after_xsi_hint_rebases_current_element_hints() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.set_instance_base_uri("http://example.com/docs/instance.xml");
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("noNamespaceSchemaLocation", XSI_NS, "schema.xsd");
v.validate_attribute("base", "http://www.w3.org/XML/1998/namespace", "subdir/");
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.end_validation().ok();
let hints = v.no_namespace_schema_location_hints();
assert_eq!(hints.len(), 1);
assert_eq!(
hints[0].base_uri, "http://example.com/docs/subdir/",
"xml:base should rebase earlier hints on the same element"
);
}
#[test]
fn test_substitution_group_abstract_head_no_cvc_elt_2() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element ref="head"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="head" abstract="true" type="xs:string"/>
<xs:element name="member" substitutionGroup="head" type="xs:string"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
v.validate_element("root", "", None, None, &empty_ns_context());
v.validate_end_of_attributes();
v.validate_element("member", "", None, None, &empty_ns_context());
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let cvc_elt_2_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-elt.2")
.collect();
assert!(
cvc_elt_2_errors.is_empty(),
"substitution group member should not trigger cvc-elt.2, got: {:?}",
cvc_elt_2_errors
);
assert!(
v.sink.errors.is_empty(),
"expected no validation errors, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_wildcard_target_ns_from_schema_type() {
let xsd = concat!(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema""#,
r#" targetNamespace="http://example.com/ns""#,
r#" xmlns:tns="http://example.com/ns">"#,
r#"<xs:element name="doc"><xs:complexType><xs:sequence>"#,
r#"<xs:element name="wrapper"><xs:complexType><xs:sequence>"#,
r#"<xs:any namespace='##targetNamespace'/>"#,
r#"</xs:sequence></xs:complexType></xs:element>"#,
r#"</xs:sequence></xs:complexType></xs:element>"#,
r#"<xs:element name="child" type="xs:string"/>"#,
r#"</xs:schema>"#,
);
let schema_set = load_schema(xsd);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let mut ns = empty_ns_context();
let tns_id = schema_set.name_table.add("http://example.com/ns");
ns.default_ns = Some(tns_id);
v.validate_element("doc", "http://example.com/ns", None, None, &ns);
v.validate_end_of_attributes();
let plain_ns = empty_ns_context();
v.validate_element("wrapper", "", None, None, &plain_ns);
v.validate_end_of_attributes();
v.validate_element("child", "http://example.com/ns", None, None, &ns);
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element(); v.validate_end_element(); let _ = v.end_validation();
assert!(
v.sink.errors.is_empty(),
"##targetNamespace wildcard should accept element in schema target ns, errors: {:?}",
v.sink.errors
);
}
#[test]
fn test_inherited_attribute_from_base_type() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="Base">
<xs:attribute name="inherited" type="xs:string"/>
</xs:complexType>
<xs:complexType name="Derived">
<xs:complexContent>
<xs:restriction base="Base"/>
</xs:complexContent>
</xs:complexType>
<xs:element name="root" type="Derived"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("inherited", "", "hello");
v.validate_end_of_attributes();
v.validate_end_element();
let _ = v.end_validation();
assert!(
v.sink.errors.is_empty(),
"attribute 'inherited' from base type should be accepted, errors: {:?}",
v.sink.errors
);
}
#[test]
fn test_inherited_attribute_through_extension_chain() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="A">
<xs:attribute name="fromA" type="xs:string" use="required"/>
</xs:complexType>
<xs:complexType name="B">
<xs:complexContent>
<xs:extension base="A">
<xs:attribute name="fromB" type="xs:int"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="C">
<xs:complexContent>
<xs:extension base="B">
<xs:attribute name="fromC" type="xs:boolean"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="root" type="C"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("fromA", "", "base");
v.validate_attribute("fromB", "", "42");
v.validate_attribute("fromC", "", "true");
v.validate_end_of_attributes();
v.validate_end_element();
let _ = v.end_validation();
assert!(
v.sink.errors.is_empty(),
"all inherited attributes should be accepted, errors: {:?}",
v.sink.errors
);
}
#[test]
fn test_prohibited_attribute_overrides_base() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="Base">
<xs:attribute name="forbidden" type="xs:string"/>
<xs:attribute name="allowed" type="xs:string"/>
</xs:complexType>
<xs:complexType name="Derived">
<xs:complexContent>
<xs:restriction base="Base">
<xs:attribute name="forbidden" use="prohibited"/>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
<xs:element name="root" type="Derived"/>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("allowed", "", "ok");
v.validate_end_of_attributes();
v.validate_end_element();
let _ = v.end_validation();
assert!(
v.sink.errors.is_empty(),
"inherited 'allowed' should be accepted"
);
let mut v2 = validator.start_run(TestSink::new());
v2.validate_element("root", "", None, None, &ns);
v2.validate_attribute("forbidden", "", "nope");
v2.validate_end_of_attributes();
v2.validate_end_element();
let _ = v2.end_validation();
assert!(
!v2.sink.errors.is_empty(),
"prohibited 'forbidden' should be rejected"
);
}
#[test]
fn test_substitution_group_union_member_type() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="myUnion">
<xs:union memberTypes="xs:integer xs:boolean"/>
</xs:simpleType>
<xs:element name="head" type="myUnion"/>
<xs:element name="member" substitutionGroup="head" type="xs:integer"/>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element ref="head"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("member", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("42");
v.validate_end_element();
v.validate_end_element();
let _ = v.end_validation();
assert!(
v.sink.errors.is_empty(),
"substitution group member with union-member type should be accepted, errors: {:?}",
v.sink.errors
);
}
#[test]
fn test_xsi_type_blocked_by_element_block_restriction() {
let schema_set = load_schema(
r###"<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://test" xmlns:t="http://test"
elementFormDefault="qualified">
<xsd:complexType name="base">
<xsd:choice>
<xsd:element name="e1" block="restriction"/>
<xsd:element name="e2" block="restriction"/>
</xsd:choice>
</xsd:complexType>
<xsd:complexType name="testing">
<xsd:complexContent>
<xsd:restriction base="t:base">
<xsd:choice>
<xsd:element name="e1" block="restriction"/>
<xsd:element name="e2" block="restriction"/>
</xsd:choice>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
<xsd:element name="doc" type="t:testing"/>
<xsd:complexType name="ext">
<xsd:complexContent>
<xsd:extension base="t:base"/>
</xsd:complexContent>
</xsd:complexType>
</xsd:schema>"###,
);
let tns = "http://test";
let tns_id = schema_set.name_table.get(tns).unwrap();
let a_prefix = schema_set.name_table.add("a");
let ns = NamespaceContextSnapshot {
default_ns: Some(tns_id),
bindings: vec![(a_prefix, tns_id)],
};
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
v.validate_element("doc", tns, None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("e2", tns, Some("a:ext"), None, &ns);
v.validate_end_of_attributes();
v.validate_element("e1", tns, None, None, &ns);
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.validate_end_element();
let _ = v.end_validation();
assert!(
v.sink.errors.iter().any(|e| e.constraint == "cvc-elt.4.3"),
"xsi:type should be rejected by element block='restriction': errors={:?}",
v.sink.errors
);
}
#[test]
fn test_xsi_type_allowed_when_no_block() {
let schema_set = load_schema(
r###"<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://test" xmlns:t="http://test"
elementFormDefault="qualified">
<xsd:complexType name="base">
<xsd:choice>
<xsd:element name="e1"/>
<xsd:element name="e2"/>
</xsd:choice>
</xsd:complexType>
<xsd:complexType name="testing">
<xsd:complexContent>
<xsd:restriction base="t:base">
<xsd:choice>
<xsd:element name="e1"/>
<xsd:element name="e2"/>
</xsd:choice>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
<xsd:element name="doc" type="t:testing"/>
<xsd:complexType name="ext">
<xsd:complexContent>
<xsd:extension base="t:base"/>
</xsd:complexContent>
</xsd:complexType>
</xsd:schema>"###,
);
let tns = "http://test";
let tns_id = schema_set.name_table.get(tns).unwrap();
let a_prefix = schema_set.name_table.add("a");
let ns = NamespaceContextSnapshot {
default_ns: Some(tns_id),
bindings: vec![(a_prefix, tns_id)],
};
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
v.validate_element("doc", tns, None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("e2", tns, Some("a:ext"), None, &ns);
v.validate_end_of_attributes();
v.validate_element("e1", tns, None, None, &ns);
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.validate_end_element();
let _ = v.end_validation();
assert!(
!v.sink.errors.iter().any(|e| e.constraint == "cvc-elt.4.3"),
"xsi:type should be accepted without block: errors={:?}",
v.sink.errors
);
}
#[test]
fn test_xsi_type_blocked_when_declared_type_is_anytype() {
let schema_set = load_schema(
r###"<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://test" xmlns:t="http://test"
elementFormDefault="qualified">
<xsd:complexType name="myType">
<xsd:sequence>
<xsd:element name="child"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="doc">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="item" block="restriction"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>"###,
);
let tns = "http://test";
let tns_id = schema_set.name_table.get(tns).unwrap();
let t_prefix = schema_set.name_table.add("t");
let ns = NamespaceContextSnapshot {
default_ns: Some(tns_id),
bindings: vec![(t_prefix, tns_id)],
};
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
v.validate_element("doc", tns, None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", tns, Some("t:myType"), None, &ns);
v.validate_end_of_attributes();
v.validate_element("child", tns, None, None, &ns);
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.validate_end_element();
let _ = v.end_validation();
assert!(
v.sink.errors.iter().any(|e| e.constraint == "cvc-elt.4.3"),
"xsi:type with implicit anyType + block='restriction' should fail: errors={:?}",
v.sink.errors
);
}
#[test]
fn test_other_wildcard_rejects_unqualified_child_xsd10() {
let schema_set = load_schema(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://test"
xmlns:t="http://test"
elementFormDefault="qualified">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:any namespace="##other" processContents="lax"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let tns = "http://test";
let ns = NamespaceContextSnapshot {
default_ns: Some(schema_set.name_table.get(tns).unwrap()),
bindings: Vec::new(),
};
v.validate_element("root", tns, None, None, &ns);
v.validate_end_of_attributes();
let empty_ns = empty_ns_context();
v.validate_element("a", "", None, None, &empty_ns);
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
let _ = v.end_validation();
assert!(
!v.sink.errors.is_empty(),
"##other in XSD 1.0 should reject unqualified (absent-namespace) child, errors={:?}",
v.sink.errors,
);
}
#[test]
fn test_other_wildcard_accepts_other_namespace_child_xsd10() {
let schema_set = load_schema(
r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://test"
xmlns:t="http://test"
elementFormDefault="qualified">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:any namespace="##other" processContents="lax"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"###,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let tns = "http://test";
let other = "http://other";
let tns_id = schema_set.name_table.get(tns).unwrap();
let ns = NamespaceContextSnapshot {
default_ns: Some(tns_id),
bindings: Vec::new(),
};
v.validate_element("root", tns, None, None, &ns);
v.validate_end_of_attributes();
let empty_ns = empty_ns_context();
v.validate_element("a", other, None, None, &empty_ns);
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
let _ = v.end_validation();
assert!(
v.sink.errors.is_empty(),
"##other should accept child from a different namespace, errors={:?}",
v.sink.errors,
);
}
#[test]
fn test_empty_element_fixed_value_synthesis() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="e" type="xs:string" fixed="hello"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("e", "", None, None, &ns);
v.validate_end_of_attributes();
let end_info = v.validate_end_element();
v.validate_end_element();
assert!(v.end_validation().is_ok());
assert_eq!(
end_info.validity,
SchemaValidity::Valid,
"empty element with fixed='hello' should be valid, errors={:?}",
v.sink.errors,
);
assert!(
v.sink.errors.is_empty(),
"no cvc-elt.5.2.2 for empty element with fixed value, errors={:?}",
v.sink.errors,
);
}
#[test]
fn test_attribute_ref_inherits_type_for_id_tracking() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:attribute name="gid" type="xs:ID"/>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute ref="gid" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("gid", "", "id1");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("gid", "", "id1");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink.errors.iter().any(|e| e.constraint == "cvc-id.2"),
"attribute ref should inherit xs:ID type for duplicate detection, errors={:?}",
v.sink.errors,
);
}
#[cfg(feature = "xsd11")]
#[test]
fn test_ic_singleton_list_equals_atomic() {
use crate::types::value::{XmlAtomicValue, XmlValue, XmlValueKind};
use crate::types::XmlTypeCode;
use crate::validation::identity::KeyFieldValue;
let atomic = KeyFieldValue {
string_value: "alpha".to_string(),
typed_value: Some(XmlValue::new(
XmlTypeCode::Name,
XmlValueKind::Atomic(XmlAtomicValue::String("alpha".to_string())),
)),
};
let singleton_list = KeyFieldValue {
string_value: "alpha".to_string(),
typed_value: Some(XmlValue::new(
XmlTypeCode::Name,
XmlValueKind::List {
item_type: XmlTypeCode::Name,
items: vec![XmlAtomicValue::String("alpha".to_string())],
},
)),
};
assert_eq!(
atomic, singleton_list,
"atomic xs:Name must equal singleton list-of-xs:Name for IC"
);
assert_eq!(singleton_list, atomic, "equality must be symmetric");
}
#[cfg(feature = "xsd11")]
#[test]
fn test_xsd11_same_id_on_same_element() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="id1" type="xs:ID"/>
<xs:attribute name="id2" type="xs:ID"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id1", "", "eee");
v.validate_attribute("id2", "", "eee");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let dup_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.2")
.collect();
assert!(
dup_errors.is_empty(),
"XSD 1.1: same ID on same element must not be a duplicate, errors={:?}",
dup_errors,
);
}
#[cfg(feature = "xsd11")]
#[test]
fn test_xsd11_same_id_on_different_elements() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="id" type="xs:ID"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "aaa");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("id", "", "aaa");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink.errors.iter().any(|e| e.constraint == "cvc-id.2"),
"XSD 1.1: same ID on different elements must be duplicate, errors={:?}",
v.sink.errors,
);
}
#[cfg(feature = "xsd11")]
#[test]
fn test_xsd11_child_element_id_binds_to_parent() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="node" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="id" type="xs:ID" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="a" type="xs:ID"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("node", "", None, None, &ns);
v.validate_attribute("a", "", "aaa");
v.validate_end_of_attributes();
v.validate_element("id", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("zzz");
v.validate_end_element();
v.validate_element("id", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("zzz");
v.validate_end_element();
v.validate_element("id", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("aaa");
v.validate_end_element();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let dup_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.2")
.collect();
assert!(
dup_errors.is_empty(),
"child ID elements bind to parent; same owner means no duplicate, errors={:?}",
dup_errors,
);
}
#[cfg(feature = "xsd11")]
#[test]
fn test_xsd11_list_of_id_per_item_tracking() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="idList">
<xs:list itemType="xs:ID"/>
</xs:simpleType>
<xs:simpleType name="idrefList">
<xs:list itemType="xs:IDREF"/>
</xs:simpleType>
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="ids" type="idList"/>
<xs:attribute name="refs" type="idrefList"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("ids", "", "aaa bbb ccc");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("refs", "", "aaa bbb ccc");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
let idref_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-id.1")
.collect();
assert!(
idref_errors.is_empty(),
"list-of-ID items must be tracked individually; all IDREFs should resolve, errors={:?}",
idref_errors,
);
}
#[cfg(feature = "xsd11")]
#[test]
fn test_xsd11_ic_ref_resolves_global() {
let schema_set = load_schema_xsd11(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="section" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="code" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:unique ref="unique-code"/>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:unique name="unique-code">
<xs:selector xpath="item"/>
<xs:field xpath="@code"/>
</xs:unique>
</xs:element>
</xs:schema>"#,
);
let flags = ValidationFlags::default() | ValidationFlags::PROCESS_IDENTITY_CONSTRAINTS;
let validator = SchemaValidator::new(&schema_set, flags);
let mut v = validator.start_run(TestSink::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("section", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("code", "", "A");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_element("item", "", None, None, &ns);
v.validate_attribute("code", "", "A");
v.validate_end_of_attributes();
v.validate_end_element();
v.validate_end_element();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-identity-constraint.4.2.2"),
"IC @ref must resolve and enforce the unique constraint, errors={:?}",
v.sink.errors,
);
}
#[test]
fn test_entity_undeclared_reports_error() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:attribute name="pic" type="xs:ENTITY" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
v.set_unparsed_entities(std::collections::HashSet::new());
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("pic", "", "mylogo");
v.validate_end_of_attributes();
v.validate_end_element();
v.end_validation().ok();
assert!(
v.sink
.errors
.iter()
.any(|e| e.constraint == "cvc-datatype-valid.1.2.1"),
"undeclared ENTITY must report error, errors={:?}",
v.sink.errors,
);
}
#[test]
fn test_entity_declared_passes() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:attribute name="pic" type="xs:ENTITY" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let mut entities = std::collections::HashSet::new();
entities.insert("mylogo".to_string());
v.set_unparsed_entities(entities);
let ns = empty_ns_context();
v.validate_element("root", "", None, None, &ns);
v.validate_attribute("pic", "", "mylogo");
v.validate_end_of_attributes();
v.validate_end_element();
v.end_validation().ok();
let entity_errors: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-datatype-valid.1.2.1")
.collect();
assert!(
entity_errors.is_empty(),
"declared ENTITY should pass, errors={:?}",
entity_errors,
);
}
#[test]
fn test_root_xsi_type_simple_type_governs_undeclared_root() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:t" xmlns="urn:t">
<xs:simpleType name="Test">
<xs:restriction base="xs:string">
<xs:pattern value="1|2|3"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let tns_prefix = schema_set.name_table.add("t");
let tns_uri = schema_set.name_table.add("urn:t");
let ns = NamespaceContextSnapshot {
default_ns: Some(tns_uri),
bindings: vec![(tns_prefix, tns_uri)],
};
let info = v.validate_element("test", "urn:t", Some("t:Test"), None, &ns);
assert!(
info.schema_type.is_some(),
"xsi:type should supply governing type at root with no element decl"
);
assert_eq!(info.type_source, Some(TypeSource::XsiType));
v.validate_end_of_attributes();
v.validate_text("2");
v.validate_end_element();
v.end_validation().ok();
let elt1: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-elt.1")
.collect();
assert!(
elt1.is_empty(),
"no cvc-elt.1 expected when root xsi:type resolves, got: {:?}",
elt1
);
assert!(
v.sink.errors.is_empty(),
"instance value '2' matches pattern '1|2|3' — expected zero errors, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_root_xsi_type_complex_type_governs_undeclared_root() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:t" xmlns="urn:t">
<xs:complexType name="Test">
<xs:sequence>
<xs:element name="abc" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let tns_prefix = schema_set.name_table.add("t");
let tns_uri = schema_set.name_table.add("urn:t");
let ns = NamespaceContextSnapshot {
default_ns: Some(tns_uri),
bindings: vec![(tns_prefix, tns_uri)],
};
let info = v.validate_element("test", "urn:t", Some("t:Test"), None, &ns);
assert!(
info.schema_type.is_some(),
"xsi:type should supply governing complex type at root"
);
assert_eq!(info.type_source, Some(TypeSource::XsiType));
v.validate_end_of_attributes();
v.validate_element("abc", "", None, None, &ns);
v.validate_end_of_attributes();
v.validate_text("hello");
v.validate_end_element();
let end_info = v.validate_end_element();
assert_eq!(
end_info.validity,
SchemaValidity::Valid,
"errors: {:?}",
v.sink.errors
);
v.end_validation().ok();
let elt1: Vec<_> = v
.sink
.errors
.iter()
.filter(|e| e.constraint == "cvc-elt.1")
.collect();
assert!(
elt1.is_empty(),
"no cvc-elt.1 expected when root xsi:type resolves, got: {:?}",
elt1
);
assert!(
v.sink.errors.is_empty(),
"no errors expected, got: {:?}",
v.sink.errors
);
}
#[test]
fn test_root_xsi_type_unresolved_emits_cvc_elt_1() {
let schema_set = load_schema(
r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:t" xmlns="urn:t">
<xs:simpleType name="Real">
<xs:restriction base="xs:string"/>
</xs:simpleType>
</xs:schema>"#,
);
let validator = SchemaValidator::new(&schema_set, ValidationFlags::default());
let mut v = validator.start_run(TestSink::new());
let tns_prefix = schema_set.name_table.add("t");
let tns_uri = schema_set.name_table.add("urn:t");
let ns = NamespaceContextSnapshot {
default_ns: Some(tns_uri),
bindings: vec![(tns_prefix, tns_uri)],
};
let info = v.validate_element("test", "urn:t", Some("t:Imaginary"), None, &ns);
assert_eq!(info.validity, SchemaValidity::Invalid);
assert!(
info.schema_type.is_none(),
"unresolved xsi:type must not supply a governing type"
);
v.validate_end_of_attributes();
v.validate_end_element();
v.end_validation().ok();
let codes: std::collections::HashSet<&str> =
v.sink.errors.iter().map(|e| e.constraint).collect();
assert!(
codes.contains("cvc-elt.1"),
"expected cvc-elt.1 when root xsi:type fails to resolve, got: {:?}",
codes
);
assert!(
codes.contains("cvc-elt.4.1"),
"expected cvc-elt.4.1 (xsi:type unresolved) alongside cvc-elt.1, got: {:?}",
codes
);
}