1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! Routines for running tests.

use argparse;

use argparse::ArgumentParser;
use std::borrow::Borrow;

use {Context, Config, print};
use test::TestResultKind;

/// Runs all tests according to a given config.
///
/// Return `Ok` if all tests pass, and `Err` otherwise.
///
/// # Parameters
///
/// * `config_fn` is a function which sets up the test config.
pub fn tests<F>(config_fn: F) -> Result<(), ()>
    where F: Fn(&mut Config) {
    let mut config = Config::default();
    config_fn(&mut config);

    let mut paths: Vec<String> = config.test_paths.iter().map(|p| p.display().to_string()).collect();

    {
        let mut tmp = false;
        let mut ap = ArgumentParser::new();
        ap.set_description("Runs tests");

        ap.refer(&mut paths)
            .add_argument("paths", argparse::List,
                          r#"Paths to test"#);
        // Required in order to use 'cargo test --nocapture'.
        ap.refer(&mut tmp)
            .add_option(&["--nocapture"], argparse::StoreTrue,
            "ignore this");
        ap.parse_args_or_exit();
    }

    if paths.is_empty() {
        util::abort("no filenames given")
    }

    let paths = paths.iter()
                     .map(|s| s.borrow());

    let test_paths = match ::find::in_paths(paths, &config) {
        Ok(paths) => paths,
        Err(e) => util::abort(format!("could not find files: {}", e)),
    };

    if test_paths.is_empty() {
        print::warning("could not find any tests");
        return Err(());
    }

    let mut context = test_paths.into_iter().fold(Context::new(), |c,file| {
        let test = util::parse_test(&file).unwrap();
        c.test(test)
    });

    match util::crate_dir() {
        Some(dir) => context.add_search_dir(dir),
        None => print::warning("could not find tool directory"),
    }

    let results = context.run(&config);

    for result in results.iter() {
        print::result(result)
    }

    let has_failure = results.iter().any(|r| {
        if let TestResultKind::Fail(..) = r.kind { true } else { false }
    });
    if !has_failure { Ok(()) } else { Err(()) }
}

mod util
{
    use Test;
    use print;

    use std::error::Error;
    use std::io::Read;
    use std;

    pub fn crate_dir() -> Option<String> {
        let current_exec = match std::env::current_exe() {
            Ok(e) => e,
            Err(e) => abort(
                format!("failed to get current executable path: {}", e)),
        };

        current_exec.parent().map(|p| p.to_str().unwrap().to_owned())
    }

    pub fn parse_test(file_name: &str) -> Result<Test,String> {
        let mut text = String::new();
        open_file(file_name).read_to_string(&mut text).unwrap();
        Test::parse(file_name, text.chars())
    }

    fn open_file(path: &str) -> std::fs::File {
        match std::fs::File::open(path) {
            Ok(f) => f,
            Err(e) => abort(format!("could not open {}: {}",
                                    path, e.description())),
        }
    }
    pub fn abort<S>(msg: S) -> !
        where S: Into<String> {
        print::failure(format!("error: {}", msg.into()));

        std::process::exit(1);
    }
}