diff --git a/src/tools/filetest/mod.rs b/src/tools/filetest/mod.rs index 9126efbfc0..dec7ff6016 100644 --- a/src/tools/filetest/mod.rs +++ b/src/tools/filetest/mod.rs @@ -4,6 +4,7 @@ //! available test commands. use std::path::Path; +use std::time; use cton_reader::TestCommand; use CommandResult; use cat; @@ -12,9 +13,13 @@ use filetest::runner::TestRunner; pub mod subtest; mod runner; +mod runone; mod domtree; mod verifier; +/// The result of running the test in a file. +pub type TestResult = Result; + /// Main entry point for `cton-util test`. /// /// Take a list of filenames which can be either `.cton` files or directories. diff --git a/src/tools/filetest/runner.rs b/src/tools/filetest/runner.rs index 8c9ea87810..7de2829296 100644 --- a/src/tools/filetest/runner.rs +++ b/src/tools/filetest/runner.rs @@ -3,22 +3,13 @@ //! This module implements the `TestRunner` struct which manages executing tests as well as //! scanning directories for tests. -use std::ffi::OsStr; -use std::path::{Path, PathBuf}; use std::error::Error; +use std::ffi::OsStr; use std::mem; -use std::borrow::{Borrow, Cow}; use std::panic::catch_unwind; -use std::time; +use std::path::{Path, PathBuf}; +use filetest::{TestResult, runone}; use CommandResult; -use utils::read_to_string; -use cton_reader::parse_test; -use cretonne::ir::Function; -use cretonne::verify_function; -use filetest::new_subtest; -use filetest::subtest::{self, SubTest, Context}; - -type TestResult = Result; #[derive(PartialEq, Eq, Debug)] enum QueueEntry { @@ -204,66 +195,9 @@ impl Job { } pub fn run(&self) -> TestResult { - match catch_unwind(|| self.run_or_panic()) { + match catch_unwind(|| runone::run(self.path.as_path())) { Err(msg) => Err(format!("panic: {:?}", msg)), Ok(result) => result, } } - - fn run_or_panic(&self) -> TestResult { - let started = time::Instant::now(); - let buffer = try!(read_to_string(&self.path).map_err(|e| e.to_string())); - let testfile = try!(parse_test(&buffer).map_err(|e| e.to_string())); - if testfile.functions.is_empty() { - return Err("no functions found".to_string()); - } - // Parse the test commands. - let mut tests = - try!(testfile.commands.iter().map(new_subtest).collect::>>()); - - // Sort the tests so the mutators are at the end, and those that - // don't need the verifier are at the front - tests.sort_by_key(|st| (st.is_mutating(), st.needs_verifier())); - - // Isolate the last test in the hope that this is the only mutating test. - // If so, we can completely avoid cloning functions. - let last_test = match tests.pop() { - None => return Err("no test commands found".to_string()), - Some(t) => t, - }; - - for (func, details) in testfile.functions { - let mut context = subtest::Context { - details: details, - verified: false, - }; - - for test in &tests { - try!(self.run_one_test(test.borrow(), Cow::Borrowed(&func), &mut context)); - } - // Run the last test with an owned function which means it won't need to clone it - // before mutating. - try!(self.run_one_test(last_test.borrow(), Cow::Owned(func), &mut context)); - } - - - // TODO: Actually run the tests. - Ok(started.elapsed()) - } - - fn run_one_test(&self, - test: &SubTest, - func: Cow, - context: &mut Context) - -> subtest::Result<()> { - let name = format!("{}({})", test.name(), func.name); - - // Should we run the verifier before this test? - if !context.verified && test.needs_verifier() { - try!(verify_function(&func).map_err(|e| e.to_string())); - context.verified = true; - } - - test.run(func, context).map_err(|e| format!("{}: {}", name, e)) - } } diff --git a/src/tools/filetest/runone.rs b/src/tools/filetest/runone.rs new file mode 100644 index 0000000000..e0d1e9d17b --- /dev/null +++ b/src/tools/filetest/runone.rs @@ -0,0 +1,66 @@ +//! Run the tests in a single test file. + +use std::borrow::{Borrow, Cow}; +use std::path::Path; +use std::time; +use cretonne::ir::Function; +use cretonne::verify_function; +use cton_reader::parse_test; +use utils::read_to_string; +use filetest::{TestResult, new_subtest}; +use filetest::subtest::{SubTest, Context, Result}; + +/// Load `path` and run the test in it. +/// +/// If running this test causes a panic, it will propagate as normal. +pub fn run(path: &Path) -> TestResult { + let started = time::Instant::now(); + let buffer = try!(read_to_string(path).map_err(|e| e.to_string())); + let testfile = try!(parse_test(&buffer).map_err(|e| e.to_string())); + if testfile.functions.is_empty() { + return Err("no functions found".to_string()); + } + // Parse the test commands. + let mut tests = try!(testfile.commands.iter().map(new_subtest).collect::>>()); + + // Sort the tests so the mutators are at the end, and those that don't need the verifier are at + // the front. + tests.sort_by_key(|st| (st.is_mutating(), st.needs_verifier())); + + // Isolate the last test in the hope that this is the only mutating test. + // If so, we can completely avoid cloning functions. + let last_test = match tests.pop() { + None => return Err("no test commands found".to_string()), + Some(t) => t, + }; + + for (func, details) in testfile.functions { + let mut context = Context { + details: details, + verified: false, + }; + + for test in &tests { + try!(run_one_test(test.borrow(), Cow::Borrowed(&func), &mut context)); + } + // Run the last test with an owned function which means it won't need to clone it before + // mutating. + try!(run_one_test(last_test.borrow(), Cow::Owned(func), &mut context)); + } + + + // TODO: Actually run the tests. + Ok(started.elapsed()) +} + +fn run_one_test(test: &SubTest, func: Cow, context: &mut Context) -> Result<()> { + let name = format!("{}({})", test.name(), func.name); + + // Should we run the verifier before this test? + if !context.verified && test.needs_verifier() { + try!(verify_function(&func).map_err(|e| e.to_string())); + context.verified = true; + } + + test.run(func, context).map_err(|e| format!("{}: {}", name, e)) +}