260 lines
8.5 KiB
Rust
260 lines
8.5 KiB
Rust
import std::option;
|
|
import std::getopts;
|
|
import std::test;
|
|
import std::fs;
|
|
import std::str;
|
|
import std::ivec;
|
|
import std::task;
|
|
import std::task::task_id;
|
|
|
|
import std::comm;
|
|
import std::comm::_port;
|
|
import std::comm::_chan;
|
|
import std::comm::send;
|
|
import std::comm::mk_port;
|
|
|
|
import common::cx;
|
|
import common::config;
|
|
import common::mode_run_pass;
|
|
import common::mode_run_fail;
|
|
import common::mode_compile_fail;
|
|
import common::mode_pretty;
|
|
import common::mode;
|
|
import util::logv;
|
|
|
|
fn main(args: vec[str]) {
|
|
|
|
let ivec_args = ivec::from_vec(args);
|
|
|
|
let config = parse_config(ivec_args);
|
|
log_config(config);
|
|
run_tests(config);
|
|
}
|
|
|
|
fn parse_config(args: &[str]) -> config {
|
|
let opts =
|
|
~[getopts::reqopt("compile-lib-path"),
|
|
getopts::reqopt("run-lib-path"), getopts::reqopt("rustc-path"),
|
|
getopts::reqopt("src-base"), getopts::reqopt("build-base"),
|
|
getopts::reqopt("stage-id"), getopts::reqopt("mode"),
|
|
getopts::optflag("ignored"), getopts::optopt("runtool"),
|
|
getopts::optopt("rustcflags"), getopts::optflag("verbose")];
|
|
|
|
check (ivec::is_not_empty(args));
|
|
let args_ = ivec::tail(args);
|
|
let match =
|
|
alt getopts::getopts(args_, opts) {
|
|
getopts::success(m) { m }
|
|
getopts::failure(f) { fail getopts::fail_str(f) }
|
|
};
|
|
|
|
ret {compile_lib_path: getopts::opt_str(match, "compile-lib-path"),
|
|
run_lib_path: getopts::opt_str(match, "run-lib-path"),
|
|
rustc_path: getopts::opt_str(match, "rustc-path"),
|
|
src_base: getopts::opt_str(match, "src-base"),
|
|
build_base: getopts::opt_str(match, "build-base"),
|
|
stage_id: getopts::opt_str(match, "stage-id"),
|
|
mode: str_mode(getopts::opt_str(match, "mode")),
|
|
run_ignored: getopts::opt_present(match, "ignored"),
|
|
filter:
|
|
if ivec::len(match.free) > 0u {
|
|
option::some(match.free.(0))
|
|
} else { option::none },
|
|
runtool: getopts::opt_maybe_str(match, "runtool"),
|
|
rustcflags: getopts::opt_maybe_str(match, "rustcflags"),
|
|
verbose: getopts::opt_present(match, "verbose")};
|
|
}
|
|
|
|
fn log_config(config: &config) {
|
|
let c = config;
|
|
logv(c, #fmt("configuration:"));
|
|
logv(c, #fmt("compile_lib_path: %s", config.compile_lib_path));
|
|
logv(c, #fmt("run_lib_path: %s", config.run_lib_path));
|
|
logv(c, #fmt("rustc_path: %s", config.rustc_path));
|
|
logv(c, #fmt("src_base: %s", config.src_base));
|
|
logv(c, #fmt("build_base: %s", config.build_base));
|
|
logv(c, #fmt("stage_id: %s", config.stage_id));
|
|
logv(c, #fmt("mode: %s", mode_str(config.mode)));
|
|
logv(c, #fmt("run_ignored: %b", config.run_ignored));
|
|
logv(c, #fmt("filter: %s", opt_str(config.filter)));
|
|
logv(c, #fmt("runtool: %s", opt_str(config.runtool)));
|
|
logv(c, #fmt("rustcflags: %s", opt_str(config.rustcflags)));
|
|
logv(c, #fmt("verbose: %b", config.verbose));
|
|
logv(c, #fmt("\n"));
|
|
}
|
|
|
|
fn opt_str(maybestr: option::t[str]) -> str {
|
|
alt maybestr { option::some(s) { s } option::none. { "(none)" } }
|
|
}
|
|
|
|
fn str_opt(maybestr: str) -> option::t[str] {
|
|
if maybestr != "(none)" { option::some(maybestr) } else { option::none }
|
|
}
|
|
|
|
fn str_mode(s: str) -> mode {
|
|
alt s {
|
|
"compile-fail" { mode_compile_fail }
|
|
"run-fail" { mode_run_fail }
|
|
"run-pass" { mode_run_pass }
|
|
"pretty" { mode_pretty }
|
|
_ { fail "invalid mode" }
|
|
}
|
|
}
|
|
|
|
fn mode_str(mode: mode) -> str {
|
|
alt mode {
|
|
mode_compile_fail. { "compile-fail" }
|
|
mode_run_fail. { "run-fail" }
|
|
mode_run_pass. { "run-pass" }
|
|
mode_pretty. { "pretty" }
|
|
}
|
|
}
|
|
|
|
fn run_tests(config: &config) {
|
|
let opts = test_opts(config);
|
|
let cx = {config: config, procsrv: procsrv::mk()};
|
|
let tests = make_tests(cx);
|
|
test::run_tests_console_(opts, tests.tests, tests.to_task);
|
|
procsrv::close(cx.procsrv);
|
|
}
|
|
|
|
fn test_opts(config: &config) -> test::test_opts {
|
|
{filter: config.filter, run_ignored: config.run_ignored}
|
|
}
|
|
|
|
type tests_and_conv_fn =
|
|
{tests: [test::test_desc], to_task: fn(&fn() ) -> task_id };
|
|
|
|
fn make_tests(cx: &cx) -> tests_and_conv_fn {
|
|
log #fmt("making tests from %s", cx.config.src_base);
|
|
let configport = mk_port[[u8]]();
|
|
let tests = ~[];
|
|
for file: str in fs::list_dir(cx.config.src_base) {
|
|
log #fmt("inspecting file %s", file);
|
|
if is_test(cx.config, file) {
|
|
tests += ~[make_test(cx, file, configport)];
|
|
}
|
|
}
|
|
ret {tests: tests, to_task: bind closure_to_task(cx, configport, _)};
|
|
}
|
|
|
|
fn is_test(config: &config, testfile: &str) -> bool {
|
|
// Pretty-printer does not work with .rc files yet
|
|
let valid_extensions = alt config.mode {
|
|
mode_pretty. { ~[".rs"] }
|
|
_ { ~[".rc", ".rs"] }
|
|
};
|
|
let invalid_prefixes = ~[".", "#", "~"];
|
|
let name = fs::basename(testfile);
|
|
|
|
let valid = false;
|
|
|
|
for ext in valid_extensions {
|
|
if str::ends_with(name, ext) { valid = true }
|
|
}
|
|
|
|
for pre in invalid_prefixes {
|
|
if str::starts_with(name, pre) { valid = false }
|
|
}
|
|
|
|
ret valid;
|
|
}
|
|
|
|
fn make_test(cx: &cx, testfile: &str, configport: &_port[[u8]]) ->
|
|
test::test_desc {
|
|
{name: make_test_name(cx.config, testfile),
|
|
fn: make_test_closure(testfile, configport.mk_chan()),
|
|
ignore: header::is_test_ignored(cx.config, testfile)}
|
|
}
|
|
|
|
fn make_test_name(config: &config, testfile: &str) -> str {
|
|
#fmt("[%s] %s", mode_str(config.mode), testfile)
|
|
}
|
|
|
|
/*
|
|
So this is kind of crappy:
|
|
|
|
A test is just defined as a function, as you might expect, but tests have to
|
|
run in their own tasks. Unfortunately, if your test needs dynamic data then it
|
|
needs to be a closure, and transferring closures across tasks without
|
|
committing a host of memory management transgressions is just impossible.
|
|
|
|
To get around this, the standard test runner allows you the opportunity do
|
|
your own conversion from a test function to a task. It gives you your function
|
|
and you give it back a task.
|
|
|
|
So that's what we're going to do. Here's where it gets stupid. To get the
|
|
the data out of the test function we are going to run the test function,
|
|
which will do nothing but send the data for that test to a port we've set
|
|
up. Then we'll spawn that data into another task and return the task.
|
|
Really convoluted. Need to think up of a better definition for tests.
|
|
*/
|
|
|
|
fn make_test_closure(testfile: &str, configchan: _chan[[u8]]) -> test::test_fn
|
|
{
|
|
bind send_config(testfile, configchan)
|
|
}
|
|
|
|
fn send_config(testfile: str, configchan: _chan[[u8]]) {
|
|
send(configchan, str::bytes(testfile));
|
|
}
|
|
|
|
/*
|
|
FIXME: Good god forgive me.
|
|
|
|
So actually shuttling structural data across tasks isn't possible at this
|
|
time, but we can send strings! Sadly, I need the whole config record, in the
|
|
test task so, instead of fixing the mechanism in the compiler I'm going to
|
|
break up the config record and pass everything individually to the spawned
|
|
function.
|
|
*/
|
|
|
|
fn closure_to_task(cx: cx, configport: _port[[u8]], testfn: &fn() ) -> task_id
|
|
{
|
|
testfn();
|
|
let testfile = configport.recv();
|
|
ret task::_spawn(bind run_test_task(cx.config.compile_lib_path,
|
|
cx.config.run_lib_path, cx.config.rustc_path,
|
|
cx.config.src_base, cx.config.build_base,
|
|
cx.config.stage_id, mode_str(cx.config.mode),
|
|
cx.config.run_ignored, opt_str(cx.config.filter),
|
|
opt_str(cx.config.runtool),
|
|
opt_str(cx.config.rustcflags), cx.config.verbose,
|
|
cx.procsrv.chan, testfile));
|
|
}
|
|
|
|
fn run_test_task(compile_lib_path: str, run_lib_path: str, rustc_path: str,
|
|
src_base: str, build_base: str, stage_id: str, mode: str,
|
|
run_ignored: bool, opt_filter: str, opt_runtool: str,
|
|
opt_rustcflags: str, verbose: bool,
|
|
procsrv_chan: procsrv::reqchan, testfile: -[u8]) {
|
|
|
|
let config =
|
|
{compile_lib_path: compile_lib_path,
|
|
run_lib_path: run_lib_path,
|
|
rustc_path: rustc_path,
|
|
src_base: src_base,
|
|
build_base: build_base,
|
|
stage_id: stage_id,
|
|
mode: str_mode(mode),
|
|
run_ignored: run_ignored,
|
|
filter: str_opt(opt_filter),
|
|
runtool: str_opt(opt_runtool),
|
|
rustcflags: str_opt(opt_rustcflags),
|
|
verbose: verbose};
|
|
|
|
let procsrv = procsrv::from_chan(procsrv_chan);
|
|
|
|
let cx = {config: config, procsrv: procsrv};
|
|
|
|
runtest::run(cx, testfile);
|
|
}
|
|
|
|
// Local Variables:
|
|
// fill-column: 78;
|
|
// indent-tabs-mode: nil
|
|
// c-basic-offset: 4
|
|
// buffer-file-coding-system: utf-8-unix
|
|
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
|
// End:
|