use std::fs;
use std::path::{Path, PathBuf};
use schematron::Schema;
fn corpus(case: &str) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests/corpus")
.join(case)
}
fn portability_of(case: &str) -> Vec<schematron::Lint> {
let source = fs::read_to_string(corpus(case).join("schema.sch"))
.unwrap_or_else(|e| panic!("corpus case {case:?} should have a schema: {e}"));
let schema = Schema::from_str(&source)
.unwrap_or_else(|e| panic!("corpus case {case:?} should compile: {e}"));
schema.portability()
}
#[test]
fn the_cases_the_reference_cannot_compile_are_reported() {
for case in ["let-shadowing", "let-phase-scope"] {
let found = portability_of(case);
assert!(
found
.iter()
.any(|lint| lint.kind == schematron::LintKind::VariableShadowsAnOuterScope),
"{case} should report a shadowed variable, but reported {found:?}"
);
}
}
#[test]
fn the_documented_divergences_are_reported() {
for (case, kind) in [
("node-kinds", schematron::LintKind::ContextSelectsANonElementKind),
("subject", schematron::LintKind::SubjectMovesTheLocation),
("rich-metadata", schematron::LintKind::FlagOrRoleOnTheRule),
] {
let found = portability_of(case);
assert!(
found.iter().any(|lint| lint.kind == kind),
"{case} should report {kind:?}, but reported {found:?}"
);
}
}
#[test]
fn the_remaining_detectable_divergences_are_reported() {
for (case, kind) in [
(
"namespaced-attribute-context",
schematron::LintKind::CollidingAttributeContexts,
),
(
"following-axis-from-attribute",
schematron::LintKind::FollowingFromAnAttribute,
),
(
"message-inline-whitespace",
schematron::LintKind::SpaceBetweenInlineElements,
),
] {
let found = portability_of(case);
assert!(
found.iter().any(|lint| lint.kind == kind),
"{case} should report {kind:?}, but reported {found:?}"
);
}
}
#[test]
fn a_schema_that_avoids_all_of_them_is_quiet() {
let schema = Schema::from_str(
r#"<schema xmlns="http://purl.oclc.org/dsdl/schematron">
<let name="base" value="10"/>
<pattern>
<let name="limit" value="$base * 2"/>
<rule context="a">
<let name="count" value="count(b)"/>
<assert test="$count <= $limit" flag="error">too many</assert>
</rule>
</pattern>
</schema>"#,
)
.unwrap();
assert_eq!(schema.portability(), Vec::new());
}
#[test]
fn sibling_scopes_binding_one_name_are_portable() {
let schema = Schema::from_str(
r#"<schema xmlns="http://purl.oclc.org/dsdl/schematron">
<pattern>
<rule context="a"><let name="n" value="1"/><assert test="$n">a</assert></rule>
<rule context="b"><let name="n" value="2"/><assert test="$n">b</assert></rule>
</pattern>
</schema>"#,
)
.unwrap();
assert_eq!(schema.portability(), Vec::new());
}
#[test]
fn portability_findings_are_kept_out_of_lint() {
let schema = Schema::from_str(
r#"<schema xmlns="http://purl.oclc.org/dsdl/schematron">
<pattern>
<rule context="a" flag="warning" subject="b">
<assert test="b">needs a b</assert>
</rule>
</pattern>
</schema>"#,
)
.unwrap();
assert!(schema.lint().is_empty(), "{:?}", schema.lint());
assert_eq!(schema.portability().len(), 2);
}
#[test]
fn no_message_or_help_carries_a_run_of_spaces() {
let schema = Schema::from_str(
r#"<schema xmlns="http://purl.oclc.org/dsdl/schematron">
<let name="dup" value="1"/>
<pattern id="p">
<let name="dup" value="2"/>
<rule context="comment()" flag="warning" subject="x">
<assert test="a">m</assert>
</rule>
</pattern>
</schema>"#,
)
.unwrap();
let reported = schema
.portability()
.into_iter()
.chain(schema.lint())
.collect::<Vec<_>>();
assert!(!reported.is_empty(), "the probe schema should report something");
for lint in reported {
assert!(
!lint.message.contains(" "),
"{:?} message has a run of spaces: {:?}",
lint.kind,
lint.message
);
if let Some(help) = &lint.help {
assert!(
!help.contains(" "),
"{:?} help has a run of spaces: {:?}",
lint.kind,
help
);
}
}
}