Browse Source
This test runs the verifier on each function and matches the resulting verifier error against the "error:" annotation. Move the existing verifier test into filetests/verifier/ and use the new syntex.pull/3/head
Jakob Stoklund Olesen
8 years ago
7 changed files with 88 additions and 69 deletions
@ -1,6 +1,8 @@ |
|||
function test(i32) { ; Err(terminator) |
|||
test verifier |
|||
|
|||
function test(i32) { |
|||
ebb0(v0: i32): |
|||
jump ebb1 |
|||
jump ebb1 ; error: terminator |
|||
return |
|||
ebb1: |
|||
jump ebb2 |
@ -0,0 +1,78 @@ |
|||
//! Test command for checking the IL verifier.
|
|||
//!
|
|||
//! The `test verifier` test command looks for annotations on instructions like this:
|
|||
//!
|
|||
//! 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 filetest::subtest::{SubTest, Context, Result}; |
|||
use utils::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) { |
|||
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)), |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
@ -1,66 +0,0 @@ |
|||
extern crate cretonne; |
|||
extern crate cton_reader; |
|||
extern crate glob; |
|||
extern crate regex; |
|||
|
|||
use std::env; |
|||
use glob::glob; |
|||
use regex::Regex; |
|||
use std::fs::File; |
|||
use std::io::Read; |
|||
use self::cton_reader::parse_functions; |
|||
use self::cretonne::verifier::Verifier; |
|||
|
|||
/// Compile a function and run verifier tests based on specially formatted
|
|||
/// comments in the [function's] source.
|
|||
fn verifier_tests_from_source(function_source: &str) { |
|||
let func_re = Regex::new("^[ \t]*function.*").unwrap(); |
|||
let err_re = Regex::new(";[ \t]*Err\\((.*)+\\)").unwrap(); |
|||
|
|||
// Each entry corresponds to an optional regular expression, where
|
|||
// the index corresponds to the function offset in our source code.
|
|||
// If no error is expected for a given function its entry will be
|
|||
// set to None.
|
|||
let mut verifier_results = Vec::new(); |
|||
for line in function_source.lines() { |
|||
if func_re.is_match(line) { |
|||
match err_re.captures(line) { |
|||
Some(caps) => { |
|||
verifier_results.push(Some(Regex::new(caps.at(1).unwrap()).unwrap())); |
|||
}, |
|||
None => { |
|||
verifier_results.push(None); |
|||
}, |
|||
}; |
|||
} |
|||
} |
|||
|
|||
// Run the verifier against each function and compare the output
|
|||
// with the expected result (as determined above).
|
|||
for (i, func) in parse_functions(function_source).unwrap().into_iter().enumerate() { |
|||
let result = Verifier::new(&func).run(); |
|||
match verifier_results[i] { |
|||
Some(ref re) => { |
|||
assert_eq!(re.is_match(&result.err().unwrap().message), true); |
|||
}, |
|||
None => { |
|||
assert_eq!(result, Ok(())); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
#[test] |
|||
fn test_all() { |
|||
let testdir = format!("{}/tests/verifier_testdata/*.cton", |
|||
env::current_dir().unwrap().display()); |
|||
|
|||
for entry in glob(&testdir).unwrap() { |
|||
let path = entry.unwrap(); |
|||
println!("Testing {:?}", path); |
|||
let mut file = File::open(&path).unwrap(); |
|||
let mut buffer = String::new(); |
|||
file.read_to_string(&mut buffer).unwrap(); |
|||
verifier_tests_from_source(&buffer); |
|||
} |
|||
} |
Loading…
Reference in new issue