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
pub use af_core_macros::test_main as main;
use crate::test::prelude::*;
use crate::util::defer;
use console::style;
use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle};
use std::io;
#[derive(Debug, Error)]
pub enum Error {
#[error("{}.", fmt::count(*.0, "test failure", "test failures"))]
Failures(usize),
#[error("IO error. {0}")]
Io(#[from] io::Error),
}
pub struct Output {
pub elapsed: Duration,
pub failures: usize,
pub tests: usize,
}
pub type Result<T = Output, E = Error> = std::result::Result<T, E>;
pub async fn run(build: impl FnOnce(&mut test::Context)) -> Result {
let style_initial = ProgressStyle::default_bar()
.template("[{elapsed_precise}] {bar:40} {pos:>7}/{len:7} {msg}")
.progress_chars("##-");
let style_ok = style_initial
.clone()
.template("[{elapsed_precise}] {bar:40.green.bright/.green} {pos:>7}/{len:7} {msg}");
let style_err = style_ok
.clone()
.template("[{elapsed_precise}] {bar:40.red.bright/.red} {pos:>7}/{len:7} {msg}");
let mut term = console::Term::buffered_stdout();
let pb = ProgressBar::with_draw_target(0, ProgressDrawTarget::to_term(term.clone(), None));
pb.set_message("Starting…");
pb.set_style(style_initial);
let mut ctx = test::Context::new();
build(&mut ctx);
let panic_hook = panic::take_hook();
let _guard = defer(|| panic::set_hook(panic_hook));
panic::set_hook(Box::new(|_| ()));
let started_at = Time::now();
let mut output = ctx.start();
let tests = output.len();
let mut failures = 0;
pb.set_length(tests as u64);
pb.set_message("Running…");
pb.set_style(style_ok);
while let Some(test::Output { path, result }) = output.next().await {
if let Err(err) = result {
failures += 1;
term.clear_last_lines(1)?;
pb.set_style(style_err.clone());
writeln!(
term,
"{} {} — {:#}\n",
path,
style("failed").bright().red(),
fmt::indent("", " ", err)
)?;
}
pb.set_position((tests - output.len()) as u64);
}
pb.finish_and_clear();
let elapsed = started_at.elapsed();
let (count, status) = match failures {
0 => (fmt::count(tests, "test", "tests"), style("passed").bright().green()),
n => (fmt::count(n, "test", "tests"), style("failed").bright().red()),
};
if failures > 0 {
writeln!(term)?;
}
writeln!(term, "{} {} in {}.", count, status, style(elapsed).bright().white())?;
term.flush()?;
Ok(Output { elapsed, failures, tests })
}