use ptr::P;
use util::small_vector::SmallVector;
+enum ShouldFail {
+ No,
+ Yes(Option<InternedString>),
+}
+
struct Test {
span: Span,
path: Vec<ast::Ident> ,
bench: bool,
ignore: bool,
- should_fail: bool
+ should_fail: ShouldFail
}
struct TestCtxt<'a> {
i.attrs.iter().any(|attr| attr.check_name("ignore"))
}
-fn should_fail(i: &ast::Item) -> bool {
- attr::contains_name(i.attrs.as_slice(), "should_fail")
+fn should_fail(i: &ast::Item) -> ShouldFail {
+ match i.attrs.iter().find(|attr| attr.check_name("should_fail")) {
+ Some(attr) => {
+ let msg = attr.meta_item_list()
+ .and_then(|list| list.iter().find(|mi| mi.check_name("message")))
+ .and_then(|mi| mi.value_str());
+ ShouldFail::Yes(msg)
+ }
+ None => ShouldFail::No,
+ }
}
/*
vec![name_expr]);
let ignore_expr = ecx.expr_bool(span, test.ignore);
- let fail_expr = ecx.expr_bool(span, test.should_fail);
+ let should_fail_path = |name| {
+ ecx.path(span, vec![self_id, test_id, ecx.ident_of("ShouldFail"), ecx.ident_of(name)])
+ };
+ let fail_expr = match test.should_fail {
+ ShouldFail::No => ecx.expr_path(should_fail_path("No")),
+ ShouldFail::Yes(ref msg) => {
+ let path = should_fail_path("Yes");
+ let arg = match *msg {
+ Some(ref msg) => ecx.expr_some(span, ecx.expr_str(span, msg.clone())),
+ None => ecx.expr_none(span),
+ };
+ ecx.expr_call(span, ecx.expr_path(path), vec![arg])
+ }
+ };
// self::test::TestDesc { ... }
let desc_expr = ecx.expr_struct(
use self::NamePadding::*;
use self::OutputLocation::*;
+use std::any::{Any, AnyRefExt};
use std::collections::TreeMap;
use stats::Stats;
use getopts::{OptGroup, optflag, optopt};
MetricChange, Improvement, Regression, LikelyNoise,
StaticTestFn, StaticTestName, DynTestName, DynTestFn,
run_test, test_main, test_main_static, filter_tests,
- parse_opts, StaticBenchFn};
+ parse_opts, StaticBenchFn, ShouldFail};
}
pub mod stats;
pub bytes: u64,
}
+#[deriving(Clone, Show, PartialEq, Eq, Hash)]
+pub enum ShouldFail {
+ No,
+ Yes(Option<&'static str>)
+}
+
// The definition of a single test. A test runner will run a list of
// these.
#[deriving(Clone, Show, PartialEq, Eq, Hash)]
pub struct TestDesc {
pub name: TestName,
pub ignore: bool,
- pub should_fail: bool,
+ pub should_fail: ShouldFail,
}
#[deriving(Show)]
fn usage(binary: &str) {
let message = format!("Usage: {} [OPTIONS] [FILTER]", binary);
- println!(r"{usage}
+ println!(r#"{usage}
The FILTER regex is tested against the name of all tests to run, and
only those tests that match are run.
function takes one argument (test::Bencher).
#[should_fail] - This function (also labeled with #[test]) will only pass if
the code causes a failure (an assertion failure or panic!)
+ A message may be provided, which the failure string must
+ contain: #[should_fail(message = "foo")].
#[ignore] - When applied to a function which is already attributed as a
test, then the test runner will ignore these tests during
normal test runs. Running with --ignored will run these
- tests.",
+ tests."#,
usage = getopts::usage(message.as_slice(),
optgroups().as_slice()));
}
let test_a = TestDesc {
name: StaticTestName("a"),
ignore: false,
- should_fail: false
+ should_fail: ShouldFail::No
};
let test_b = TestDesc {
name: StaticTestName("b"),
ignore: false,
- should_fail: false
+ should_fail: ShouldFail::No
};
let mut st = ConsoleTestState {
let stdout = reader.read_to_end().unwrap().into_iter().collect();
let task_result = result_future.into_inner();
- let test_result = calc_result(&desc, task_result.is_ok());
+ let test_result = calc_result(&desc, task_result);
monitor_ch.send((desc.clone(), test_result, stdout));
})
}
}
}
-fn calc_result(desc: &TestDesc, task_succeeded: bool) -> TestResult {
- if task_succeeded {
- if desc.should_fail { TrFailed }
- else { TrOk }
- } else {
- if desc.should_fail { TrOk }
- else { TrFailed }
+fn calc_result(desc: &TestDesc, task_result: Result<(), Box<Any+Send>>) -> TestResult {
+ match (&desc.should_fail, task_result) {
+ (&ShouldFail::No, Ok(())) |
+ (&ShouldFail::Yes(None), Err(_)) => TrOk,
+ (&ShouldFail::Yes(Some(msg)), Err(ref err))
+ if err.downcast_ref::<String>()
+ .map(|e| &**e)
+ .or_else(|| err.downcast_ref::<&'static str>().map(|e| *e))
+ .map(|e| e.contains(msg))
+ .unwrap_or(false) => TrOk,
+ _ => TrFailed,
}
}
TestDesc, TestDescAndFn, TestOpts, run_test,
Metric, MetricMap, MetricAdded, MetricRemoved,
Improvement, Regression, LikelyNoise,
- StaticTestName, DynTestName, DynTestFn};
+ StaticTestName, DynTestName, DynTestFn, ShouldFail};
use std::io::TempDir;
#[test]
desc: TestDesc {
name: StaticTestName("whatever"),
ignore: true,
- should_fail: false
+ should_fail: ShouldFail::No,
},
testfn: DynTestFn(proc() f()),
};
desc: TestDesc {
name: StaticTestName("whatever"),
ignore: true,
- should_fail: false
+ should_fail: ShouldFail::No,
},
testfn: DynTestFn(proc() f()),
};
desc: TestDesc {
name: StaticTestName("whatever"),
ignore: false,
- should_fail: true
+ should_fail: ShouldFail::Yes(None)
+ },
+ testfn: DynTestFn(proc() f()),
+ };
+ let (tx, rx) = channel();
+ run_test(&TestOpts::new(), false, desc, tx);
+ let (_, res, _) = rx.recv();
+ assert!(res == TrOk);
+ }
+
+ #[test]
+ fn test_should_fail_good_message() {
+ fn f() { panic!("an error message"); }
+ let desc = TestDescAndFn {
+ desc: TestDesc {
+ name: StaticTestName("whatever"),
+ ignore: false,
+ should_fail: ShouldFail::Yes(Some("error message"))
},
testfn: DynTestFn(proc() f()),
};
assert!(res == TrOk);
}
+ #[test]
+ fn test_should_fail_bad_message() {
+ fn f() { panic!("an error message"); }
+ let desc = TestDescAndFn {
+ desc: TestDesc {
+ name: StaticTestName("whatever"),
+ ignore: false,
+ should_fail: ShouldFail::Yes(Some("foobar"))
+ },
+ testfn: DynTestFn(proc() f()),
+ };
+ let (tx, rx) = channel();
+ run_test(&TestOpts::new(), false, desc, tx);
+ let (_, res, _) = rx.recv();
+ assert!(res == TrFailed);
+ }
+
#[test]
fn test_should_fail_but_succeeds() {
fn f() { }
desc: TestDesc {
name: StaticTestName("whatever"),
ignore: false,
- should_fail: true
+ should_fail: ShouldFail::Yes(None)
},
testfn: DynTestFn(proc() f()),
};
desc: TestDesc {
name: StaticTestName("1"),
ignore: true,
- should_fail: false,
+ should_fail: ShouldFail::No,
},
testfn: DynTestFn(proc() {}),
},
desc: TestDesc {
name: StaticTestName("2"),
ignore: false,
- should_fail: false
+ should_fail: ShouldFail::No,
},
testfn: DynTestFn(proc() {}),
});
desc: TestDesc {
name: DynTestName((*name).clone()),
ignore: false,
- should_fail: false
+ should_fail: ShouldFail::No,
},
testfn: DynTestFn(testfn),
};
desc: TestDesc {
name: DynTestName(name.to_string()),
ignore: false,
- should_fail: false
+ should_fail: ShouldFail::No,
},
testfn: DynTestFn(test_fn)
}