use libxml::schemas::SchemaParserContext;
use libxml::schemas::SchemaValidationContext;
use libxml::parser::Parser;
static NOTE_SCHEMA: &str = r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="note">
<xs:complexType>
<xs:sequence>
<xs:element name="to" type="xs:string"/>
<xs:element name="from" type="xs:string"/>
<xs:element name="heading" type="xs:string"/>
<xs:element name="body" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
"#;
static STOCK_SCHEMA: &str = r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="stock">
<xs:complexType>
<xs:sequence maxOccurs="unbounded">
<xs:element name="sample">
<xs:complexType>
<xs:all>
<xs:element name="date" type="xs:date"/>
<xs:element name="price" type="xs:float"/>
</xs:all>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="ticker" type="xs:string" use="required"/>
<xs:attribute name="exchange" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>
"#;
static VALID_NOTE_XML: &str = r#"<?xml version="1.0"?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
"#;
static INVALID_NOTE_XML: &str = r#"<?xml version="1.0"?>
<note>
<bad>Tove</bad>
<another>Jani</another>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
"#;
static INVALID_STOCK_XML: &str = r#"<?xml version="1.0"?>
<stock junkAttribute="foo">
<sample>
<date>2014-01-01</date>
<price>NOT A NUMBER</price>
</sample>
<sample>
<date>2014-01-02</date>
<price>540.98</price>
</sample>
<sample>
<date>NOT A DATE</date>
<price>543.93</price>
</sample>
</stock
"#;
#[test]
fn schema_from_string() {
let xml = Parser::default()
.parse_string(VALID_NOTE_XML)
.expect("Expected to be able to parse XML Document from string");
let mut xsdparser = SchemaParserContext::from_buffer(NOTE_SCHEMA);
let xsd = SchemaValidationContext::from_parser(&mut xsdparser);
if let Err(errors) = xsd {
for err in &errors {
eprintln!("{}", err.message.as_ref().unwrap());
}
panic!("Failed to parse schema with {} errors", errors.len());
}
let mut xsdvalidator = xsd.unwrap();
for _ in 0..5 {
if let Err(errors) = xsdvalidator.validate_document(&xml) {
for err in &errors {
eprintln!("{}", err.message.as_ref().unwrap());
}
panic!("Invalid XML accoding to XSD schema");
}
}
}
#[test]
fn schema_from_string_generates_errors() {
let xml = Parser::default()
.parse_string(INVALID_NOTE_XML)
.expect("Expected to be able to parse XML Document from string");
let mut xsdparser = SchemaParserContext::from_buffer(NOTE_SCHEMA);
let xsd = SchemaValidationContext::from_parser(&mut xsdparser);
if let Err(errors) = xsd {
for err in &errors {
eprintln!("{}", err.message.as_ref().unwrap());
}
panic!("Failed to parse schema with {} errors", errors.len());
}
let mut xsdvalidator = xsd.unwrap();
for _ in 0..5 {
if let Err(errors) = xsdvalidator.validate_document(&xml) {
for err in &errors {
assert_eq!(
"Element 'bad': This element is not expected. Expected is ( to ).\n",
err.message.as_ref().unwrap()
);
}
}
}
}
#[test]
fn schema_from_string_reports_unique_errors() {
let xml = Parser::default()
.parse_string(INVALID_STOCK_XML)
.expect("Expected to be able to parse XML Document from string");
let mut xsdparser = SchemaParserContext::from_buffer(STOCK_SCHEMA);
let xsd = SchemaValidationContext::from_parser(&mut xsdparser);
if let Err(errors) = xsd {
for err in &errors {
eprintln!("{}", err.message.as_ref().unwrap());
}
panic!("Failed to parse schema with {} errors", errors.len());
}
let mut xsdvalidator = xsd.unwrap();
for _ in 0..5 {
if let Err(errors) = xsdvalidator.validate_document(&xml) {
assert_eq!(errors.len(), 5);
let expected_errors = vec![
"Element 'stock', attribute 'junkAttribute': The attribute 'junkAttribute' is not allowed.\n",
"Element 'stock': The attribute 'ticker' is required but missing.\n",
"Element 'stock': The attribute 'exchange' is required but missing.\n",
"Element 'price': 'NOT A NUMBER' is not a valid value of the atomic type 'xs:float'.\n",
"Element 'date': 'NOT A DATE' is not a valid value of the atomic type 'xs:date'.\n"
];
for err_msg in expected_errors {
assert!(errors.iter().any(|err| err.message.as_ref().unwrap() == err_msg), "Expected error message {} was not found", err_msg);
}
}
}
}