use xmlschema::{BuiltIn, Facets, Occurs};
#[test]
fn the_default_cardinality_is_exactly_once() {
let d = Occurs::default();
assert_eq!(d.min, 1);
assert_eq!(d.max, Some(1));
assert!(d.permits(1));
assert!(!d.permits(0));
assert!(!d.permits(2));
}
#[test]
fn permits_respects_both_bounds() {
let between = Occurs {
min: 2,
max: Some(4),
};
assert!(!between.permits(1));
assert!(between.permits(2));
assert!(between.permits(4));
assert!(!between.permits(5));
let unbounded = Occurs { min: 1, max: None };
assert!(!unbounded.permits(0));
assert!(unbounded.permits(1));
assert!(unbounded.permits(10_000));
}
#[test]
fn describe_covers_every_shape_of_cardinality() {
let cases = [
(
Occurs {
min: 1,
max: Some(1),
},
"exactly once",
),
(
Occurs {
min: 0,
max: Some(1),
},
"at most once",
),
(Occurs { min: 0, max: None }, "any number of times"),
(Occurs { min: 2, max: None }, "at least 2 times"),
(
Occurs {
min: 3,
max: Some(3),
},
"exactly 3 times",
),
(
Occurs {
min: 1,
max: Some(4),
},
"between 1 and 4 times",
),
];
for (occurs, expected) in cases {
assert_eq!(occurs.describe(), expected, "for {occurs:?}");
}
}
#[test]
fn built_in_names_resolve_ignoring_the_prefix() {
assert_eq!(BuiltIn::from_name("xs:string"), Some(BuiltIn::String));
assert_eq!(BuiltIn::from_name("xsd:string"), Some(BuiltIn::String));
assert_eq!(BuiltIn::from_name("string"), Some(BuiltIn::String));
}
#[test]
fn every_built_in_is_its_own_type_with_its_own_rule() {
let cases: &[(&str, &str, bool)] = &[
("byte", "127", true),
("byte", "128", false),
("byte", "-128", true),
("byte", "-129", false),
("short", "32767", true),
("short", "32768", false),
("int", "2147483647", true),
("int", "2147483648", false),
("long", "9223372036854775807", true),
("long", "9223372036854775808", false),
("unsignedByte", "255", true),
("unsignedByte", "256", false),
("unsignedByte", "-1", false),
("unsignedShort", "65535", true),
("unsignedShort", "65536", false),
("unsignedInt", "4294967295", true),
("unsignedInt", "4294967296", false),
("positiveInteger", "1", true),
("positiveInteger", "0", false),
("nonNegativeInteger", "0", true),
("nonNegativeInteger", "-1", false),
("negativeInteger", "-1", true),
("negativeInteger", "0", false),
("nonPositiveInteger", "0", true),
("nonPositiveInteger", "1", false),
("integer", "123456789012345678901234567890", true),
("integer", "1.0", false),
("decimal", "1.0", true),
("decimal", "1.0e3", false),
("double", "1.0e3", true),
("double", "NaN", true),
("double", "INF", true),
("NCName", "abc", true),
("NCName", "a:b", false),
("NCName", "1abc", false),
("Name", "a:b", true),
("Name", "-abc", false),
("NMTOKEN", "-abc", true),
("NMTOKEN", "a b", false),
("NMTOKENS", "a b", true),
("language", "en-GB", true),
("language", "e0", false),
("QName", "p:local", true),
("QName", "p:l:x", false),
("integer", " 5 ", true),
("boolean", "\ttrue\n", true),
("date", "2001-02-28", true),
("date", "2001-02-29", false),
("date", "2004-02-29", true),
("date", "2001-13-01", false),
("time", "23:59:59", true),
("time", "24:00:00", true),
("time", "24:00:01", false),
("dateTime", "2001-01-01T00:00:00Z", true),
("dateTime", "2001-01-01", false),
("duration", "P1Y2M3DT4H5M6S", true),
("duration", "P", false),
("duration", "P1S", false),
("gYear", "2001", true),
("gMonth", "--02", true),
("gDay", "---15", true),
("gMonthDay", "--02-29", true),
("gYearMonth", "2001-02", true),
("hexBinary", "0FB7", true),
("hexBinary", "0FB", false),
("base64Binary", "QUJD", true),
("base64Binary", "QUJ", false),
];
for (name, value, want) in cases {
let ty = BuiltIn::from_name(name)
.unwrap_or_else(|| panic!("`{name}` must resolve"));
assert_eq!(
ty.accepts(value),
*want,
"{name} should {} {value:?}",
if *want { "accept" } else { "reject" }
);
}
}
#[test]
fn the_integer_lattice_is_not_one_type() {
let names = [
"integer",
"long",
"int",
"short",
"byte",
"nonNegativeInteger",
"unsignedLong",
"unsignedInt",
"unsignedShort",
"unsignedByte",
"positiveInteger",
"negativeInteger",
"nonPositiveInteger",
];
let mut seen = Vec::new();
for n in names {
let ty = BuiltIn::from_name(n).expect("resolves");
assert!(!seen.contains(&ty), "`{n}` collapsed into another type");
seen.push(ty);
}
assert_eq!(seen.len(), names.len());
}
#[test]
fn an_unknown_type_name_is_none_rather_than_a_guess() {
for n in ["", "notAType", "xs:", "xs:madeUp", "String", "INTEGER"] {
assert_eq!(BuiltIn::from_name(n), None, "{n} should not resolve");
}
}
#[test]
fn facets_are_empty_until_one_is_set() {
assert!(Facets::default().is_empty());
let with_enum = Facets {
enumeration: vec!["a".to_owned()],
..Default::default()
};
assert!(!with_enum.is_empty());
let with_min = Facets {
min_length: Some(1),
..Default::default()
};
assert!(!with_min.is_empty());
}