Skip to content

Instantly share code, notes, and snippets.

@wilkie
Created May 3, 2013 02:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wilkie/5506860 to your computer and use it in GitHub Desktop.
Save wilkie/5506860 to your computer and use it in GitHub Desktop.
WIP Rust testing framework
/*
Compile: rustc my_spec.rs
Run: ./my_spec
Output:
math
addition
should add numbers together - Pass
should add with a negative number - Pass
should add two negative numbers - Pass
multiplication
should multiply numbers together - Pass
should multiply with a negative number - Pass
should multiply two negative numbers - Pass
floating point division
should divide roughly well - Pass
7 tests 8 assertions 0 failures
*/
use tester::*;
mod tester;
describe!("math", {
test!("addition", {
should!("add numbers together", {
must!(2 + 2 eq 4)
})
should!("add with a negative number", {
must!(2 + -4 eq -2)
})
should!("add two negative numbers", {
must!(-2 + -4 eq -6)
})
})
test!("multiplication", {
should!("multiply numbers together", {
must!(2 * 2 eq 4)
})
should!("multiply with a negative number", {
must!(2 * -4 eq -8)
})
should!("multiply two negative numbers", {
must!(-2 * -4 eq 8)
})
})
test!("floating point division", {
should!("divide roughly well", {
let x = 2.3;
let y = 5.6;
must!(y / x near 2.43478)
must!(y / x near 2.43478)
})
})
})
#[macro_escape];
pub fn report_failure(expected: ~str, actual: ~str) {
io::print(fmt!("\n\x1b[31;1mFail\x1b[39;0m - %s vs %s", actual, expected));
}
trait TestInput {
fn compare(&self, b: &Self) -> bool;
fn compare_near(&self, b: &Self) -> bool;
fn fail(&self, b: Self);
}
macro_rules! impl_test_input(
($t:ty) => {
impl TestInput for $t {
pub fn compare(&self, b: &$t) -> bool {
*self == *b
}
pub fn compare_near(&self, b: &$t) -> bool {
*self == *b
}
pub fn fail(&self, b: $t) {
report_failure(self.to_str(), b.to_str());
}
}
}
)
macro_rules! impl_test_float(
($t:ty) => {
impl TestInput for $t {
pub fn compare(&self, b: &$t) -> bool {
*self == *b
}
pub fn compare_near(&self, b: &$t) -> bool {
(*self > b - 0.00001) && (*self < b + 0.00001)
}
pub fn fail(&self, b: $t) {
report_failure(self.to_str(), b.to_str());
}
}
}
)
macro_rules! impl_test_char(
($t:ty) => {
impl TestInput for $t {
pub fn compare(&self, b: &$t) -> bool {
*self == *b
}
pub fn compare_near(&self, b: &$t) -> bool {
*self == *b
}
pub fn fail(&self, b: $t) {
report_failure(fmt!("%c", *self), fmt!("%c", b))
}
}
}
)
impl_test_input!(~str)
impl_test_input!(u8)
impl_test_input!(u16)
impl_test_input!(u32)
impl_test_input!(u64)
impl_test_input!(uint)
impl_test_input!(i8)
impl_test_input!(i16)
impl_test_input!(i32)
impl_test_input!(i64)
impl_test_input!(int)
impl_test_input!(bool)
impl_test_char!(char)
impl_test_float!(float)
impl_test_float!(f32)
impl_test_float!(f64)
pub fn perform_test<T:TestInput>(a:T, b:T, negate: bool) -> bool {
if (a.compare(&b) ^ negate) {
true
}
else {
a.fail(b);
false
}
}
pub fn perform_near<T:TestInput>(a:T, b:T, negate: bool) -> bool {
if (a.compare_near(&b) ^ negate) {
true
}
else {
a.fail(b);
false
}
}
pub fn describe(prompt: &str, function: &fn()) {
io::println(prompt);
function();
}
pub fn test(prompt: &str, function: &fn()) {
io::print(" ");
io::println(prompt);
function();
}
pub fn should(prompt: &str, function: &fn()) {
io::print(" should ");
io::print(prompt);
io::print(" - ");
function();
}
macro_rules! describe(
($prompt:expr, $func:expr) => (
fn main() {
let module_name = $prompt;
let mut _current_test = "<invalid>";
let mut _indent = 0;
let mut _tests:uint = 0;
let mut _fails:uint = 0;
let mut _successes:uint = 0;
io::println(module_name);
$func;
let assertions = _successes + _fails;
io::println(fmt!("%u tests %u assertions %u failures", _tests, assertions, _fails));
}
)
)
macro_rules! test(
($prompt:expr, $func:expr) => ({
_current_test = $prompt;
_indent += 1;
for uint::range(0, _indent) |_| {
io::print(" ");
}
io::println(_current_test);
$func;
_indent -= 1;
})
)
macro_rules! must(
($a:expr eq $b:expr) => (
if (perform_test($a, $b, false)) { _successes += 1; } else { _failure = true; _fails += 1; }
);
($a:expr near $b:expr) => (
if (perform_near($a, $b, false)) { _successes += 1; } else { _failure = true; _fails += 1; }
);
)
macro_rules! wont(
($a:expr eq $b:expr) => (
if (perform_test($a, $b, true)) { _successes += 1; } else { _failure = true; _fails += 1; }
);
($a:expr near $b:expr) => (
if (perform_near($a, $b, true)) { _successes += 1; } else { _failure = true; _fails += 1; }
);
)
macro_rules! should(
($prompt:expr, $func:expr) => ({
let mut _failure = false;
_tests += 1;
_indent += 1;
for uint::range(0, _indent) |_| {
io::print(" ");
}
io::print("should ");
io::print($prompt);
$func;
if (!_failure) {
io::print(" - ");
io::println("\x1b[32;1mPass\x1b[39;0m");
}
else {
io::println("");
}
_indent -= 1;
})
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment