use regexr::Regex;
mod corpus;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Support {
Yes,
No,
}
pub struct Probe {
pub pattern: &'static str,
pub matches: Option<&'static str>,
pub rejects: Option<&'static str>,
pub expect: Option<&'static str>,
pub icase: bool,
pub xmode: bool,
}
pub struct Feature {
pub syntax: &'static str,
pub desc: &'static str,
pub support: Support,
pub covered_by: Option<&'static str>,
pub probes: &'static [Probe],
}
pub struct Group {
pub name: &'static str,
pub features: &'static [Feature],
}
impl Probe {
fn pattern(&self) -> String {
let mut flags = String::new();
if self.icase {
flags.push('i');
}
if self.xmode {
flags.push('x');
}
if flags.is_empty() {
self.pattern.to_string()
} else {
format!("(?{flags}){}", self.pattern)
}
}
fn run(&self) -> Result<(), String> {
let pattern = self.pattern();
let re = match Regex::new(&pattern) {
Ok(re) => re,
Err(e) => return Err(format!("`{pattern}` failed to compile: {e}")),
};
if let Some(text) = self.matches {
match re.find(text) {
None => return Err(format!("`{pattern}` did not match {text:?}")),
Some(m) => {
if let Some(expect) = self.expect {
if m.as_str() != expect {
return Err(format!(
"`{pattern}` matched {:?} in {text:?}, expected {expect:?}",
m.as_str()
));
}
}
}
}
}
if let Some(text) = self.rejects {
if re.is_match(text) {
return Err(format!(
"`{pattern}` matched {text:?}, which it should reject"
));
}
}
Ok(())
}
}
fn evaluate(feature: &Feature) -> Result<(), String> {
for probe in feature.probes {
probe.run()?;
}
Ok(())
}
#[test]
fn supported_features_hold() {
let mut broken = Vec::new();
for group in corpus::GROUPS {
for feature in group.features {
if feature.support != Support::Yes || feature.covered_by.is_some() {
continue;
}
if let Err(reason) = evaluate(feature) {
broken.push(format!(
" {} / {} ({}): {reason}",
group.name, feature.syntax, feature.desc
));
}
}
}
assert!(
broken.is_empty(),
"{} feature-matrix rows claim support but no longer work:\n{}",
broken.len(),
broken.join("\n")
);
}
#[test]
fn unsupported_features_stay_declared() {
let mut implemented = Vec::new();
for group in corpus::GROUPS {
for feature in group.features {
if feature.support != Support::No {
continue;
}
if evaluate(feature).is_ok() {
implemented.push(format!(
" {} / {} ({})",
group.name, feature.syntax, feature.desc
));
}
}
}
assert!(
implemented.is_empty(),
"{} feature-matrix rows are declared unsupported but now pass; \
set them to `Support::Yes`:\n{}",
implemented.len(),
implemented.join("\n")
);
}
#[test]
fn corpus_is_well_formed() {
for group in corpus::GROUPS {
assert!(
!group.features.is_empty(),
"group `{}` has no rows",
group.name
);
for feature in group.features {
match feature.covered_by {
None => assert!(
!feature.probes.is_empty(),
"`{}` in `{}` has neither probes nor a `covered_by` pointer",
feature.syntax,
group.name
),
Some(module) => {
assert!(
feature.probes.is_empty(),
"`{}` in `{}` delegates to `{module}` but also carries probes; \
one of the two is a duplicate",
feature.syntax,
group.name
);
assert_eq!(
feature.support,
Support::Yes,
"`{}` in `{}` delegates to `{module}`, which only makes sense \
for a row we claim to support",
feature.syntax,
group.name
);
}
}
for probe in feature.probes {
assert!(
probe.matches.is_some() || probe.rejects.is_some(),
"probe `{}` in `{}` asserts nothing",
probe.pattern,
feature.syntax
);
assert!(
probe.expect.is_none() || probe.matches.is_some(),
"probe `{}` in `{}` pins a match span but has no matching text",
probe.pattern,
feature.syntax
);
}
}
}
}