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
use super::{Output, OutputStream, Path};
use crate::channel;
use crate::prelude::*;
use crate::string::SharedString;
use crate::task::{self, Task};
use std::collections::BTreeMap;
#[derive(Default)]
pub struct Context {
children: BTreeMap<SharedString, Child>,
len: usize,
path: Path,
}
enum Child {
Context(Context),
Test(Box<dyn FnOnce() -> Task<Result<(), fail::Error>> + Send>),
}
impl Context {
pub fn new() -> Self {
default()
}
pub fn scope(&mut self, name: impl Into<SharedString>, build: impl FnOnce(&mut Context)) {
let name = name.into();
assert!(!name.is_empty(), "A test context cannot be named \"\".");
assert!(!self.children.contains_key(&name), "Duplicate name {:?}.", name);
let mut ctx = Self::new();
build(&mut ctx);
ctx.path = self.path.clone();
ctx.path.components.push_back(name.clone());
self.len += ctx.len;
self.children.insert(name, Child::Context(ctx));
}
pub fn test(
&mut self,
name: impl Into<SharedString>,
test: impl task::Start<Result> + Send + 'static,
) {
let name = name.into();
assert!(!name.is_empty(), "A test cannot be named \"\".");
assert!(!self.children.contains_key(&name), "Duplicate name {:?}.", name);
let start = Box::new(move || task::start(test));
self.len += 1;
self.children.insert(name, Child::Test(start));
}
pub fn start(self) -> OutputStream {
let remaining = self.len;
let (tx, rx) = channel::unbounded();
let _task = task::start(self.run(default(), tx));
OutputStream { remaining, rx, _task }
}
#[future::boxed]
async fn run(self, path: Path, output: channel::Sender<Output>) {
let mut tasks = task::Join::new();
for (name, child) in self.children {
let output = output.clone();
let mut path = path.clone();
path.components.push_back(name);
match child {
Child::Context(ctx) => tasks.add(ctx.run(path, output)),
Child::Test(start) => tasks.add(async move {
let result = start().try_join().await;
output.send(Output { path, result }).await.unwrap();
}),
};
}
while let Some(task) = tasks.next().await {
if task.result.is_err() {
error!("Internal test runner panic.");
}
}
}
}