Move the filetest harness into its own crate.
This allows us to run the tests via a library call rather than just as a command execution. And, it's a step toward a broader goal, which is to keep the code in the top-level src directory minimal, with important functionality exposed as crates.
This commit is contained in:
82
lib/filetests/src/test_verifier.rs
Normal file
82
lib/filetests/src/test_verifier.rs
Normal file
@@ -0,0 +1,82 @@
|
||||
//! Test command for checking the IL verifier.
|
||||
//!
|
||||
//! The `test verifier` test command looks for annotations on instructions like this:
|
||||
//!
|
||||
//! ```cton
|
||||
//! jump ebb3 ; error: jump to non-existent EBB
|
||||
//! ```
|
||||
//!
|
||||
//! This annotation means that the verifier is expected to given an error for the jump instruction
|
||||
//! containing the substring "jump to non-existent EBB".
|
||||
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use cretonne::verify_function;
|
||||
use cretonne::ir::Function;
|
||||
use cton_reader::TestCommand;
|
||||
use subtest::{SubTest, Context, Result};
|
||||
use match_directive::match_directive;
|
||||
|
||||
struct TestVerifier;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> Result<Box<SubTest>> {
|
||||
assert_eq!(parsed.command, "verifier");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestVerifier))
|
||||
}
|
||||
}
|
||||
|
||||
impl SubTest for TestVerifier {
|
||||
fn name(&self) -> Cow<str> {
|
||||
Cow::from("verifier")
|
||||
}
|
||||
|
||||
fn needs_verifier(&self) -> bool {
|
||||
// Running the verifier before this test would defeat its purpose.
|
||||
false
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> Result<()> {
|
||||
let func = func.borrow();
|
||||
|
||||
// Scan source annotations for "error:" directives.
|
||||
let mut expected = None;
|
||||
for comment in &context.details.comments {
|
||||
if let Some(tail) = match_directive(comment.text, "error:") {
|
||||
// Currently, the verifier can only report one problem at a time.
|
||||
// Reject more than one `error:` directives.
|
||||
if expected.is_some() {
|
||||
return Err("cannot handle multiple error: directives".to_string());
|
||||
}
|
||||
expected = Some((comment.entity, tail));
|
||||
}
|
||||
}
|
||||
|
||||
match verify_function(func, context.flags_or_isa()) {
|
||||
Ok(_) => {
|
||||
match expected {
|
||||
None => Ok(()),
|
||||
Some((_, msg)) => Err(format!("passed, expected error: {}", msg)),
|
||||
}
|
||||
}
|
||||
Err(got) => {
|
||||
match expected {
|
||||
None => Err(format!("verifier pass, got {}", got)),
|
||||
Some((want_loc, want_msg)) if got.message.contains(want_msg) => {
|
||||
if want_loc == got.location {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!(
|
||||
"correct error reported on {}, but wanted {}",
|
||||
got.location,
|
||||
want_loc
|
||||
))
|
||||
}
|
||||
}
|
||||
Some(_) => Err(format!("mismatching error: {}", got)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user