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
|
// This is an example step library.
// Our step function uses the subplotlib Runcmd context
// in order that it can access the stdout of the recently
// executed command.
use subplotlib::steplibrary::runcmd::Runcmd;
// All step functions are annotated with the #[step] attribute
// which is implemented in the subplotlib-derive crate.
// This marks the function as a step function and automatically
// creates the step builders and other code needed to be able to
// use this step in a scenario.
#[step]
// The first argument to a step function is always the context with
// which the step is run. If the step is simple (needs only one
// context) then this will be a borrow (or mut borrow) of the context
// type. If the step uses multiple kinds of context then instead there
// will be some number of #[context(TypeName)] attributes, and the step
// function will take a ScenarioContext as its context argument.
// The rest of the arguments to the step function are the captures from the
// binding for the step.
fn count_lines_in_stdout(context: &Runcmd, count: usize) {
let stdout_count = context.stdout_as_string().lines().count();
if stdout_count != count {
// Step functions can use the throw!() macro to raise a
// step error. This will be reported as the reason the
// scenario fails.
throw!(format!(
"Incorrect number of lines, got {} expected {}",
stdout_count, count
));
}
}
// While this step function could just use Runcmd again, we use ScenarioContext
// in order to demonstrate how to access other contexts from a generic step
// function in Rust
#[step]
#[context(Runcmd)]
fn stderr_contains_two_things(context: &ScenarioContext, what: &str, other: &str) {
// To keep lifetimes sane, you access other contexts by using closures which
// take the context as an argument.
let stderr_has_both = context.with(
|runcmd: &Runcmd| {
// In here, `runcmd` is the context we need
let stderr = runcmd.stderr_as_string();
// Since we could fail in here, we're actually returning a Result object
Ok(stderr.contains(what) && stderr.contains(other))
},
// This `false` means that we will not run the closure if the scenario is
// currently failing. This is more useful in cleanup functions since it
// allows you to decide if you want to ignore the failure mode in order to
// clean up more effectively.
false,
)?;
if !stderr_has_both {
throw!(format!(
"Stderr does not contain both of {:?} and {:?}",
what, other
))
}
}
|