summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2019-01-15 21:48:20 +0000
committerDaniel Silverstone <dsilvers@digital-scurf.org>2019-01-15 21:48:20 +0000
commitf14c562e3f007212883ce287cec4d29218ebbe05 (patch)
tree4c9e0599d5c8703d39251fb0a591764ab826fa88
parent43b405634a27ac296ff8d2b5db94540025436777 (diff)
downloadrsyarn-f14c562e3f007212883ce287cec4d29218ebbe05.tar.bz2
cargo fmt
-rw-r--r--src/cli.rs364
-rw-r--r--src/err.rs2
-rw-r--r--src/main.rs164
-rw-r--r--src/runner.rs173
-rw-r--r--src/tui.rs9
-rw-r--r--src/yarn.rs126
6 files changed, 478 insertions, 360 deletions
diff --git a/src/cli.rs b/src/cli.rs
index dc8c389..37ca30f 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -1,5 +1,5 @@
-use std::path::Path;
use std::ffi::{OsStr, OsString};
+use std::path::Path;
use clap::{App, AppSettings, Arg, Shell};
@@ -77,161 +77,209 @@ pub fn get_app() -> App<'static, 'static> {
.global_setting(AppSettings::AllowInvalidUtf8)
.max_term_width(100)
.usage("rsyarn [options] <YARN>...")
- .arg(Arg::with_name("pretend")
- .short("n")
- .long("dry-run")
- .visible_aliases(&["pretend", "no-act"])
- .help("do not actually run any tests, merely print what would be run")
- .required(false))
- .arg(Arg::with_name("quiet")
- .short("q")
- .long("quiet")
- .help("be quiet, avoid progress reporting, only show errors")
- .conflicts_with("verbose")
- .required(false))
- .arg(Arg::with_name("verbose")
- .short("v")
- .long("verbose")
- .help("make progress reporting be more verbose (\"wall of text\") \
- instead of a one-line status info; this is turned on automatically \
- if there is no controlling terminal and --quiet is not set")
- .conflicts_with("quiet")
- .required(false))
- .arg(Arg::with_name("prologue")
- .short("p")
- .long("prologue")
- .help("include a prologue for the IMPLEMENTS sections to use")
- .visible_aliases(&["shell-library", "prelude", "prefix", "prolog"])
- .required(false)
- .takes_value(true)
- .multiple(true)
- .number_of_values(1)
- .empty_values(false)
- .validator_os(file_exists)
- .value_name("FILE"))
- .arg(Arg::with_name("epilogue")
- .short("e")
- .long("epilogue")
- .help("include an epilogue for the IMPLEMENTS sections to use")
- .visible_aliases(&["postlude", "suffix", "epilog"])
- .required(false)
- .takes_value(true)
- .multiple(true)
- .number_of_values(1)
- .empty_values(false)
- .validator_os(file_exists)
- .value_name("FILE"))
- .arg(Arg::with_name("run")
- .short("r")
- .long("run")
- .help("run only SCENARIO (this option can be repeated)")
- .required(false)
- .takes_value(true)
- .multiple(true)
- .number_of_values(1)
- .empty_values(false)
- .value_name("SCENARIO"))
- .arg(Arg::with_name("tempdir")
- .long("tempdir")
- .help("use DIR as the temporary directory for tests; it should be empty or not exist")
- .required(false)
- .takes_value(true)
- .number_of_values(1)
- .empty_values(false)
- .validator_os(dir_empty_or_not_exists)
- .value_name("DIR"))
- .arg(Arg::with_name("env")
- .long("env")
- .help("add KEY=VALUE to the environment when tests are run")
- .required(false)
- .multiple(true)
- .takes_value(true)
- .number_of_values(1)
- .empty_values(false)
- .validator(looks_envish)
- .value_name("KEY=VALUE"))
- .arg(Arg::with_name("snapshot")
- .long("snapshot")
- .help("make snapshots of test working directory after each scenario \
- step; requires the use of --tempdir")
- .required(false)
- .requires("tempdir")
- .takes_value(false))
- .arg(Arg::with_name("timings")
- .long("timings")
- .help("report wall clock time for each scenario and step")
- .required(false)
- .takes_value(false))
- .arg(Arg::with_name("allow-missing-steps")
- .short("k")
- .long("allow-missing-steps")
- .help("allow scenarios to reference steps that do not exist, \
- by warning about them but otherwise ignoring the scenarios")
- .required(false)
- .takes_value(false))
- .arg(Arg::with_name("require-assumptions")
- .long("require-assumptions")
- .help("require ASSUMING to always pass")
- .required(false)
- .takes_value(false))
- .arg(Arg::with_name("interpreter")
- .short("i")
- .long("interpreter")
- .visible_alias("shell")
- .help("run IMPLEMENTS using INTERP")
- .required(false)
- .takes_value(true)
- .number_of_values(1)
- .empty_values(false)
- .value_name("INTERP")
- .validator_os(exists_executable)
- .default_value("/bin/sh"))
- .arg(Arg::with_name("interpreter-argument")
- .long("arg")
- .visible_alias("shell-arg")
- .help("pass ARG when running interpreter (default ignored if \
- INTERP is changed from /bin/sh)")
- .required(false)
- .takes_value(true)
- .number_of_values(1)
- .empty_values(false)
- .value_name("ARG")
- .default_value("-xeu"))
- .arg(Arg::with_name("cd-datadir")
- .long("cd-datadir")
- .help("change to DATADIR when running commands")
- .required(false)
- .takes_value(false))
- .arg(Arg::with_name("stop-on-first-fail")
- .long("stop-on-first-fail")
- .help("stop if any scenario step fails, don't run more scenarios")
- .required(false)
- .conflicts_with("workers")
- .takes_value(false))
- .arg(Arg::with_name("completions")
- .long("gen-completions")
- .help("generate shell completions (disables normal behaviour)")
- .required(false)
- .takes_value(true)
- .number_of_values(1)
- .empty_values(false)
- .possible_values(&Shell::variants()))
- .arg(Arg::with_name("workers")
- .long("workers")
- .short("j")
- .help("number of worker threads to use to run suite")
- .required(false)
- .takes_value(true)
- .number_of_values(1)
- .empty_values(false)
- .validator(is_number)
- .conflicts_with("stop-on-first-fail")
- .value_name("WORKERS"))
- // Finally we have the collector for the yarn file names
- .arg(Arg::with_name("yarns")
- .multiple(true)
- .help("markdown files with Yarn scenarios at the top level")
- .required_unless("completions")
- .validator_os(file_exists)
- .value_name("YARN"))
+ .arg(
+ Arg::with_name("pretend")
+ .short("n")
+ .long("dry-run")
+ .visible_aliases(&["pretend", "no-act"])
+ .help("do not actually run any tests, merely print what would be run")
+ .required(false),
+ )
+ .arg(
+ Arg::with_name("quiet")
+ .short("q")
+ .long("quiet")
+ .help("be quiet, avoid progress reporting, only show errors")
+ .conflicts_with("verbose")
+ .required(false),
+ )
+ .arg(
+ Arg::with_name("verbose")
+ .short("v")
+ .long("verbose")
+ .help(
+ "make progress reporting be more verbose (\"wall of text\") \
+ instead of a one-line status info; this is turned on automatically \
+ if there is no controlling terminal and --quiet is not set",
+ )
+ .conflicts_with("quiet")
+ .required(false),
+ )
+ .arg(
+ Arg::with_name("prologue")
+ .short("p")
+ .long("prologue")
+ .help("include a prologue for the IMPLEMENTS sections to use")
+ .visible_aliases(&["shell-library", "prelude", "prefix", "prolog"])
+ .required(false)
+ .takes_value(true)
+ .multiple(true)
+ .number_of_values(1)
+ .empty_values(false)
+ .validator_os(file_exists)
+ .value_name("FILE"),
+ )
+ .arg(
+ Arg::with_name("epilogue")
+ .short("e")
+ .long("epilogue")
+ .help("include an epilogue for the IMPLEMENTS sections to use")
+ .visible_aliases(&["postlude", "suffix", "epilog"])
+ .required(false)
+ .takes_value(true)
+ .multiple(true)
+ .number_of_values(1)
+ .empty_values(false)
+ .validator_os(file_exists)
+ .value_name("FILE"),
+ )
+ .arg(
+ Arg::with_name("run")
+ .short("r")
+ .long("run")
+ .help("run only SCENARIO (this option can be repeated)")
+ .required(false)
+ .takes_value(true)
+ .multiple(true)
+ .number_of_values(1)
+ .empty_values(false)
+ .value_name("SCENARIO"),
+ )
+ .arg(
+ Arg::with_name("tempdir")
+ .long("tempdir")
+ .help(
+ "use DIR as the temporary directory for tests; it should be empty or not exist",
+ )
+ .required(false)
+ .takes_value(true)
+ .number_of_values(1)
+ .empty_values(false)
+ .validator_os(dir_empty_or_not_exists)
+ .value_name("DIR"),
+ )
+ .arg(
+ Arg::with_name("env")
+ .long("env")
+ .help("add KEY=VALUE to the environment when tests are run")
+ .required(false)
+ .multiple(true)
+ .takes_value(true)
+ .number_of_values(1)
+ .empty_values(false)
+ .validator(looks_envish)
+ .value_name("KEY=VALUE"),
+ )
+ .arg(
+ Arg::with_name("snapshot")
+ .long("snapshot")
+ .help(
+ "make snapshots of test working directory after each scenario \
+ step; requires the use of --tempdir",
+ )
+ .required(false)
+ .requires("tempdir")
+ .takes_value(false),
+ )
+ .arg(
+ Arg::with_name("timings")
+ .long("timings")
+ .help("report wall clock time for each scenario and step")
+ .required(false)
+ .takes_value(false),
+ )
+ .arg(
+ Arg::with_name("allow-missing-steps")
+ .short("k")
+ .long("allow-missing-steps")
+ .help(
+ "allow scenarios to reference steps that do not exist, \
+ by warning about them but otherwise ignoring the scenarios",
+ )
+ .required(false)
+ .takes_value(false),
+ )
+ .arg(
+ Arg::with_name("require-assumptions")
+ .long("require-assumptions")
+ .help("require ASSUMING to always pass")
+ .required(false)
+ .takes_value(false),
+ )
+ .arg(
+ Arg::with_name("interpreter")
+ .short("i")
+ .long("interpreter")
+ .visible_alias("shell")
+ .help("run IMPLEMENTS using INTERP")
+ .required(false)
+ .takes_value(true)
+ .number_of_values(1)
+ .empty_values(false)
+ .value_name("INTERP")
+ .validator_os(exists_executable)
+ .default_value("/bin/sh"),
+ )
+ .arg(
+ Arg::with_name("interpreter-argument")
+ .long("arg")
+ .visible_alias("shell-arg")
+ .help(
+ "pass ARG when running interpreter (default ignored if \
+ INTERP is changed from /bin/sh)",
+ )
+ .required(false)
+ .takes_value(true)
+ .number_of_values(1)
+ .empty_values(false)
+ .value_name("ARG")
+ .default_value("-xeu"),
+ )
+ .arg(
+ Arg::with_name("cd-datadir")
+ .long("cd-datadir")
+ .help("change to DATADIR when running commands")
+ .required(false)
+ .takes_value(false),
+ )
+ .arg(
+ Arg::with_name("stop-on-first-fail")
+ .long("stop-on-first-fail")
+ .help("stop if any scenario step fails, don't run more scenarios")
+ .required(false)
+ .conflicts_with("workers")
+ .takes_value(false),
+ )
+ .arg(
+ Arg::with_name("completions")
+ .long("gen-completions")
+ .help("generate shell completions (disables normal behaviour)")
+ .required(false)
+ .takes_value(true)
+ .number_of_values(1)
+ .empty_values(false)
+ .possible_values(&Shell::variants()),
+ )
+ .arg(
+ Arg::with_name("workers")
+ .long("workers")
+ .short("j")
+ .help("number of worker threads to use to run suite")
+ .required(false)
+ .takes_value(true)
+ .number_of_values(1)
+ .empty_values(false)
+ .validator(is_number)
+ .conflicts_with("stop-on-first-fail")
+ .value_name("WORKERS"),
+ )
+ // Finally we have the collector for the yarn file names
+ .arg(
+ Arg::with_name("yarns")
+ .multiple(true)
+ .help("markdown files with Yarn scenarios at the top level")
+ .required_unless("completions")
+ .validator_os(file_exists)
+ .value_name("YARN"),
+ )
}
diff --git a/src/err.rs b/src/err.rs
index e5c3c35..0ff94cf 100644
--- a/src/err.rs
+++ b/src/err.rs
@@ -1,8 +1,8 @@
// Our generic error handling
use regex;
-use std::io;
use std::env;
+use std::io;
quick_error! {
#[derive(Debug)]
diff --git a/src/main.rs b/src/main.rs
index b55ca18..314709d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,8 +7,8 @@ extern crate quick_error;
extern crate pbr;
-extern crate regex;
extern crate pulldown_cmark;
+extern crate regex;
extern crate mktemp;
@@ -16,10 +16,10 @@ extern crate termion;
extern crate crossbeam;
+use std::fs;
use std::io;
use std::io::Write;
use std::process::exit;
-use std::fs;
use std::time::Instant;
use std::sync::mpsc::channel;
@@ -28,11 +28,11 @@ use std::sync::{Arc, Mutex};
use regex::Regex;
mod cli;
-mod opt;
mod err;
-mod yarn;
+mod opt;
mod runner;
mod tui;
+mod yarn;
#[derive(Debug, PartialEq, Eq)]
enum RunResult {
@@ -41,14 +41,15 @@ enum RunResult {
Failed,
}
-fn run_scenario<'a>(args: &opt::YarnArgs,
- yf: &'a yarn::YarnFiles,
- scenario: &'a yarn::YarnScenario,
- yarnfile: &'a yarn::YarnFile,
- joiner: &tui::TextUi,
- row: usize,
- timings: &mut Vec<String>)
- -> RunResult {
+fn run_scenario<'a>(
+ args: &opt::YarnArgs,
+ yf: &'a yarn::YarnFiles,
+ scenario: &'a yarn::YarnScenario,
+ yarnfile: &'a yarn::YarnFile,
+ joiner: &tui::TextUi,
+ row: usize,
+ timings: &mut Vec<String>,
+) -> RunResult {
joiner.reset_bar(row, scenario.lines.len() as u64);
let scenstart = Instant::now();
let mut runner = runner::YarnRunner::new(&args);
@@ -61,21 +62,32 @@ fn run_scenario<'a>(args: &opt::YarnArgs,
let steps: Vec<&yarn::YarnScenarioLine> = runner.run(&scenario, &yarnfile, &yf).clone();
let mut idx: usize = 0;
for step in steps {
- joiner.bar_message(row,
- format!("[{:30.30}] ",
- format!("{} {}", step.cmd.as_str(), step.text)));
+ joiner.bar_message(
+ row,
+ format!(
+ "[{:30.30}] ",
+ format!("{} {}", step.cmd.as_str(), step.text)
+ ),
+ );
if args.verbose {
- joiner.msg(&format!("Running step '{} {}' for {}",
- step.cmd.as_str(),
- step.text,
- scenario.name));
+ joiner.msg(&format!(
+ "Running step '{} {}' for {}",
+ step.cmd.as_str(),
+ step.text,
+ scenario.name
+ ));
}
let stepstart = Instant::now();
runner.run_step(idx, step, &joiner);
let stepend = stepstart.elapsed();
- let runtime: f64 = (stepend.as_secs() as f64) +
- ((stepend.subsec_nanos() as f64) / 1000000000.0);
- timings.push(format!(" {:2.3} {} {}", runtime, step.cmd.as_str(), step.text));
+ let runtime: f64 =
+ (stepend.as_secs() as f64) + ((stepend.subsec_nanos() as f64) / 1000000000.0);
+ timings.push(format!(
+ " {:2.3} {} {}",
+ runtime,
+ step.cmd.as_str(),
+ step.text
+ ));
joiner.bar_inc(row);
joiner.scen_inc();
idx += 1;
@@ -97,8 +109,8 @@ fn run_scenario<'a>(args: &opt::YarnArgs,
joiner.msg(&format!("Finished Scenario {}", scenario.name));
}
let scenend = scenstart.elapsed();
- let runtime: f64 = (scenend.as_secs() as f64) +
- ((scenend.subsec_nanos() as f64) / 1000000000.0);
+ let runtime: f64 =
+ (scenend.as_secs() as f64) + ((scenend.subsec_nanos() as f64) / 1000000000.0);
timings.insert(0, format!("{:2.3} {}", runtime, scenario.name));
ret
}
@@ -126,9 +138,10 @@ fn run_yarn(args: &opt::YarnArgs) -> err::YarnResult<()> {
}
if !args.quiet {
- println!("Parsed and linked {} scenario(s) totalling {} step(s)",
- scens,
- steps);
+ println!(
+ "Parsed and linked {} scenario(s) totalling {} step(s)",
+ scens, steps
+ );
}
let mut tui = tui::TextUiBuilder::new(steps as u64);
@@ -145,7 +158,8 @@ fn run_yarn(args: &opt::YarnArgs) -> err::YarnResult<()> {
let (jobtx, jobrx) = channel::<Option<YarnJob>>();
for &(ref scenario, ref yarnfile) in &yf.scenarios() {
- jobtx.send(Some(YarnJob {
+ jobtx
+ .send(Some(YarnJob {
scenario: scenario,
yarnfile: &yarnfile,
}))
@@ -166,35 +180,33 @@ fn run_yarn(args: &opt::YarnArgs) -> err::YarnResult<()> {
let joiner = joiner.clone();
let failing = failing.clone();
jobtx.send(None).unwrap();
- scope.spawn(move || {
- loop {
- let job = {
- let lock = workerjobrx.lock().unwrap();
- lock.recv()
- };
- if let Some(job) = job.unwrap() {
- let alreadyfailing: bool = {
- *failing.lock().unwrap()
- };
- if !(alreadyfailing && args.stop_on_fail) {
- let mut timings: Vec<String> = Vec::new();
- let rr = run_scenario(args,
- &yf,
- job.scenario,
- job.yarnfile,
- &joiner,
- worker,
- &mut timings);
- if rr == RunResult::Failed {
- *failing.lock().unwrap() = true;
- }
- workerrestx.send((job, rr, timings)).unwrap();
- } else {
- break;
+ scope.spawn(move || loop {
+ let job = {
+ let lock = workerjobrx.lock().unwrap();
+ lock.recv()
+ };
+ if let Some(job) = job.unwrap() {
+ let alreadyfailing: bool = { *failing.lock().unwrap() };
+ if !(alreadyfailing && args.stop_on_fail) {
+ let mut timings: Vec<String> = Vec::new();
+ let rr = run_scenario(
+ args,
+ &yf,
+ job.scenario,
+ job.yarnfile,
+ &joiner,
+ worker,
+ &mut timings,
+ );
+ if rr == RunResult::Failed {
+ *failing.lock().unwrap() = true;
}
+ workerrestx.send((job, rr, timings)).unwrap();
} else {
break;
}
+ } else {
+ break;
}
});
}
@@ -222,31 +234,45 @@ fn run_yarn(args: &opt::YarnArgs) -> err::YarnResult<()> {
if !args.quiet {
let runend = runstart.elapsed();
- let runtime: f64 = (runend.as_secs() as f64) +
- ((runend.subsec_nanos() as f64) / 1000000000.0);
- println!("Summary: {} failed, {} skipped, {} passed, in {:2.3} seconds",
- results.iter().filter(|&g| g.2 == RunResult::Failed).count(),
- results.iter().filter(|&g| g.2 == RunResult::Skipped).count(),
- results.iter().filter(|&g| g.2 == RunResult::Success).count(),
- runtime);
+ let runtime: f64 =
+ (runend.as_secs() as f64) + ((runend.subsec_nanos() as f64) / 1000000000.0);
+ println!(
+ "Summary: {} failed, {} skipped, {} passed, in {:2.3} seconds",
+ results.iter().filter(|&g| g.2 == RunResult::Failed).count(),
+ results
+ .iter()
+ .filter(|&g| g.2 == RunResult::Skipped)
+ .count(),
+ results
+ .iter()
+ .filter(|&g| g.2 == RunResult::Success)
+ .count(),
+ runtime
+ );
for &(ref scen, ref file, ref res) in &results {
match *res {
RunResult::Success => {
if args.verbose {
- println!(" PASS {} from {}",
- scen.name,
- file.source.format_location(scen.loc));
+ println!(
+ " PASS {} from {}",
+ scen.name,
+ file.source.format_location(scen.loc)
+ );
}
}
RunResult::Skipped => {
- println!(" SKIP {} from {}",
- scen.name,
- file.source.format_location(scen.loc));
+ println!(
+ " SKIP {} from {}",
+ scen.name,
+ file.source.format_location(scen.loc)
+ );
}
RunResult::Failed => {
- println!(" FAIL {} from {}",
- scen.name,
- file.source.format_location(scen.loc));
+ println!(
+ " FAIL {} from {}",
+ scen.name,
+ file.source.format_location(scen.loc)
+ );
}
};
}
diff --git a/src/runner.rs b/src/runner.rs
index 6e34517..20e6afa 100644
--- a/src/runner.rs
+++ b/src/runner.rs
@@ -1,12 +1,11 @@
-
+use crate::err::*;
use crate::opt::YarnArgs;
use crate::yarn::*;
-use crate::err::*;
-use std::path::PathBuf;
+use std::env;
use std::fs;
use std::fs::File;
use std::io::Write;
-use std::env;
+use std::path::PathBuf;
use std::process::Command;
@@ -25,8 +24,10 @@ pub struct YarnRunner<'a> {
fn normalise_path_name(instr: &str) -> String {
let mut ret: String = String::new();
for ch in instr.chars() {
- if ((ch >= 'A') && (ch <= 'Z')) || ((ch >= 'a') && (ch <= 'z')) ||
- ((ch >= '0') && (ch <= '9')) {
+ if ((ch >= 'A') && (ch <= 'Z'))
+ || ((ch >= 'a') && (ch <= 'z'))
+ || ((ch >= '0') && (ch <= '9'))
+ {
ret.push(ch);
} else if ch == '_' {
ret.push_str("__");
@@ -40,7 +41,6 @@ fn normalise_path_name(instr: &str) -> String {
ret
}
-
impl<'a> YarnRunner<'a> {
pub fn new(args: &'a YarnArgs) -> YarnRunner<'a> {
YarnRunner {
@@ -54,24 +54,29 @@ impl<'a> YarnRunner<'a> {
}
}
- pub fn run(&mut self,
- scen: &'a YarnScenario,
- file: &'a YarnFile,
- files: &'a YarnFiles)
- -> &Vec<(&'a YarnScenarioLine)> {
+ pub fn run(
+ &mut self,
+ scen: &'a YarnScenario,
+ file: &'a YarnFile,
+ files: &'a YarnFiles,
+ ) -> &Vec<(&'a YarnScenarioLine)> {
self.scen = Some(scen);
self.file = Some(file);
self.files = Some(files);
self.lines.truncate(0);
// Now prepare the ordered set of lines
// First the ASSUMINGs
- self.lines.extend(scen.lines.iter().filter(|s| s.cmd == YarnCommand::Assuming));
+ self.lines
+ .extend(scen.lines.iter().filter(|s| s.cmd == YarnCommand::Assuming));
// Now anything not ASSUMING or FINALLY
- self.lines.extend(scen.lines
- .iter()
- .filter(|s| s.cmd != YarnCommand::Assuming && s.cmd != YarnCommand::Finally));
+ self.lines.extend(
+ scen.lines
+ .iter()
+ .filter(|s| s.cmd != YarnCommand::Assuming && s.cmd != YarnCommand::Finally),
+ );
// Now the FINALLYs
- self.lines.extend(scen.lines.iter().filter(|s| s.cmd == YarnCommand::Finally));
+ self.lines
+ .extend(scen.lines.iter().filter(|s| s.cmd == YarnCommand::Finally));
// And return it
&self.lines
}
@@ -96,13 +101,18 @@ impl<'a> YarnRunner<'a> {
fn dir_for_line(&self, idx: usize, line: &YarnScenarioLine) -> PathBuf {
let mut ret: PathBuf = self.dir_for_scen();
// Extend with normalised scenario line name
- ret.push(normalise_path_name(&format!("{:04} {} {}", idx, line.cmd.as_str(), line.text)));
+ ret.push(normalise_path_name(&format!(
+ "{:04} {} {}",
+ idx,
+ line.cmd.as_str(),
+ line.text
+ )));
ret
}
pub fn prepare(&mut self) {
self.failing = false; /* let's not get ahead of ourselves */
- self.running = true; /* We're not skipping this (yet) */
+ self.running = true; /* We're not skipping this (yet) */
}
pub fn cleanup(&mut self) {
@@ -133,11 +143,12 @@ impl<'a> YarnRunner<'a> {
}
}
- fn run_step_(&mut self,
- idx: usize,
- line: &YarnScenarioLine,
- tui: &super::tui::TextUi)
- -> YarnResult<bool> {
+ fn run_step_(
+ &mut self,
+ idx: usize,
+ line: &YarnScenarioLine,
+ tui: &super::tui::TextUi,
+ ) -> YarnResult<bool> {
// We actually want to run this step...
// If we're not meant to be running, we clearly succeeded
if !self.running {
@@ -148,17 +159,20 @@ impl<'a> YarnRunner<'a> {
return Ok(true);
}
// Now grab the implementation...
- let (ref imp, ref impfile) =
- match self.files.unwrap().find_implementation(line, &self.file.unwrap().source)? {
- None => {
- if self.args.allow_missing {
- self.running = false;
- return Ok(true);
- }
- return Ok(false);
+ let (ref imp, ref impfile) = match self
+ .files
+ .unwrap()
+ .find_implementation(line, &self.file.unwrap().source)?
+ {
+ None => {
+ if self.args.allow_missing {
+ self.running = false;
+ return Ok(true);
}
- Some(imp) => imp,
- };
+ return Ok(false);
+ }
+ Some(imp) => imp,
+ };
let caps = match imp.matcher.captures(&line.text) {
None => panic!("Somehow the chosen implementation didn't match"),
@@ -229,54 +243,65 @@ impl<'a> YarnRunner<'a> {
.output()
.expect("Unable to launch snapshot process");
if !cpcmd.status.success() {
- tui.msg(&format!("Unable to snapshot, sorry:\n{}\n{}\n{}",
- format!("Exit code: {}",
- match cpcmd.status.code() {
- None => -1,
- Some(n) => n,
- }),
- format!("Standard output: {}",
- String::from_utf8_lossy(&cpcmd.stdout)),
- format!("Standard error: {}",
- String::from_utf8_lossy(&cpcmd.stderr))));
+ tui.msg(&format!(
+ "Unable to snapshot, sorry:\n{}\n{}\n{}",
+ format!(
+ "Exit code: {}",
+ match cpcmd.status.code() {
+ None => -1,
+ Some(n) => n,
+ }
+ ),
+ format!(
+ "Standard output: {}",
+ String::from_utf8_lossy(&cpcmd.stdout)
+ ),
+ format!("Standard error: {}", String::from_utf8_lossy(&cpcmd.stderr))
+ ));
}
-
}
if !out.status.success() {
// Something went wrong, report it...
if line.cmd != YarnCommand::Assuming || self.args.require_assuming {
- tui.msg(&format!("ERROR: Something went wrong running scenario \
- step:\n{}\n{}\n{}\n{}\n{}",
- format!("Step {} '{} {}' in scenario '{}' at {} failed.",
- idx,
- line.cmd.as_str(),
- line.text,
- self.scen.unwrap().name,
- self.file.unwrap().source.format_location(line.loc)),
- format!("Implementation found at {}",
- impfile.format_location(imp.loc)),
- format!("Exit code: {}",
- match out.status.code() {
- None => -1,
- Some(n) => n,
- }),
- format!("Standard output: {}",
- String::from_utf8_lossy(&out.stdout)),
- format!("Standard error: {}",
- String::from_utf8_lossy(&out.stderr)),
- ));
+ tui.msg(&format!(
+ "ERROR: Something went wrong running scenario \
+ step:\n{}\n{}\n{}\n{}\n{}",
+ format!(
+ "Step {} '{} {}' in scenario '{}' at {} failed.",
+ idx,
+ line.cmd.as_str(),
+ line.text,
+ self.scen.unwrap().name,
+ self.file.unwrap().source.format_location(line.loc)
+ ),
+ format!(
+ "Implementation found at {}",
+ impfile.format_location(imp.loc)
+ ),
+ format!(
+ "Exit code: {}",
+ match out.status.code() {
+ None => -1,
+ Some(n) => n,
+ }
+ ),
+ format!("Standard output: {}", String::from_utf8_lossy(&out.stdout)),
+ format!("Standard error: {}", String::from_utf8_lossy(&out.stderr)),
+ ));
} else if line.cmd == YarnCommand::Assuming {
if !self.args.quiet {
- tui.msg(&format!("SKIP: Skipping scenario '{}' from {}, because '{} {}' \
- failed",
- self.scen.unwrap().name,
- self.file
- .unwrap()
- .source
- .format_location(self.scen.unwrap().loc),
- line.cmd.as_str(),
- line.text));
+ tui.msg(&format!(
+ "SKIP: Skipping scenario '{}' from {}, because '{} {}' \
+ failed",
+ self.scen.unwrap().name,
+ self.file
+ .unwrap()
+ .source
+ .format_location(self.scen.unwrap().loc),
+ line.cmd.as_str(),
+ line.text
+ ));
}
}
}
diff --git a/src/tui.rs b/src/tui.rs
index 3272bad..49c6d95 100644
--- a/src/tui.rs
+++ b/src/tui.rs
@@ -1,10 +1,9 @@
-
+use pbr::{MultiBar, Pipe, ProgressBar};
use std::io::{Result, Write};
-use pbr::{ProgressBar, MultiBar, Pipe};
use std::thread::{spawn, JoinHandle};
-use std::sync::mpsc::{channel, Sender, Receiver};
+use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::{Arc, Mutex};
use termion;
@@ -19,7 +18,9 @@ pub struct TextUiBuilder {
impl TextUiBuilder {
pub fn new(scens: u64) -> TextUiBuilder {
let chan = channel();
- let mut mb = MultiBar::on(TuiPipe { chan: chan.0.clone() });
+ let mut mb = MultiBar::on(TuiPipe {
+ chan: chan.0.clone(),
+ });
let sb = mb.create_bar(scens);
TextUiBuilder {
mb: mb,
diff --git a/src/yarn.rs b/src/yarn.rs
index 5c239d2..c73707c 100644
--- a/src/yarn.rs
+++ b/src/yarn.rs
@@ -1,31 +1,30 @@
-
use crate::err::YarnResult;
+use std::collections::HashSet;
use std::fs::File;
+use std::hash::{Hash, Hasher};
use std::io::Read;
use std::str::Lines;
-use std::hash::{Hash, Hasher};
-use std::collections::HashSet;
use pulldown_cmark::Parser;
-use pulldown_cmark::{Options, OPTION_ENABLE_TABLES, OPTION_ENABLE_FOOTNOTES};
use pulldown_cmark::{Event, Tag};
+use pulldown_cmark::{Options, OPTION_ENABLE_FOOTNOTES, OPTION_ENABLE_TABLES};
use regex::Regex;
use crate::opt::YarnArgs;
lazy_static! {
- static ref SCENARIO_RE: Regex = Regex::new("^SCENARIO +(.+)$").unwrap();
- static ref ASSUMING_RE: Regex = Regex::new("^ASSUMING +(.+)$").unwrap();
- static ref GIVEN_RE: Regex = Regex::new("^GIVEN +(.+)$").unwrap();
- static ref WHEN_RE: Regex = Regex::new("^WHEN +(.+)$").unwrap();
- static ref THEN_RE: Regex = Regex::new("^THEN +(.+)$").unwrap();
- static ref FINALLY_RE: Regex = Regex::new("^FINALLY +(.+)$").unwrap();
- static ref AND_RE: Regex = Regex::new("^AND +(.+)$").unwrap();
- static ref CONT_RE: Regex = Regex::new("^\\.\\.\\. +(.+)$").unwrap();
+ static ref SCENARIO_RE: Regex = Regex::new("^SCENARIO +(.+)$").unwrap();
+ static ref ASSUMING_RE: Regex = Regex::new("^ASSUMING +(.+)$").unwrap();
+ static ref GIVEN_RE: Regex = Regex::new("^GIVEN +(.+)$").unwrap();
+ static ref WHEN_RE: Regex = Regex::new("^WHEN +(.+)$").unwrap();
+ static ref THEN_RE: Regex = Regex::new("^THEN +(.+)$").unwrap();
+ static ref FINALLY_RE: Regex = Regex::new("^FINALLY +(.+)$").unwrap();
+ static ref AND_RE: Regex = Regex::new("^AND +(.+)$").unwrap();
+ static ref CONT_RE: Regex = Regex::new("^\\.\\.\\. +(.+)$").unwrap();
static ref IMPLEMENTS_RE: Regex = Regex::new("^IMPLEMENTS +([^ ]+) +(.+)$").unwrap();
- static ref EXAMPLE_RE: Regex = Regex::new("^EXAMPLE").unwrap();
+ static ref EXAMPLE_RE: Regex = Regex::new("^EXAMPLE").unwrap();
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
@@ -335,10 +334,12 @@ impl YarnFile {
let cmd = YarnCommand::new(kw_);
match cmd {
YarnCommand::Unknown => {
- return Err(format!("Unknown IMPLEMENTS keyword '{}' at {}",
- kw_,
- source.format_location(pos))
- .into())
+ return Err(format!(
+ "Unknown IMPLEMENTS keyword '{}' at {}",
+ kw_,
+ source.format_location(pos)
+ )
+ .into())
}
YarnCommand::Assuming => {}
YarnCommand::Given => {}
@@ -346,20 +347,24 @@ impl YarnFile {
YarnCommand::Then => {}
YarnCommand::Finally => {}
_ => {
- return Err(format!("Unexpected IMPLEMENTS keyword '{}' at {}",
- kw_,
- source.format_location(pos))
- .into())
+ return Err(format!(
+ "Unexpected IMPLEMENTS keyword '{}' at {}",
+ kw_,
+ source.format_location(pos)
+ )
+ .into())
}
}
// If we get this far, it's worth constructing the IMPLEMENTS
match YarnImplements::new(cmd, format!("^(?i){}$", rest_), pos) {
Ok(yi) => implements.push(yi),
Err(e) => {
- return Err(format!("{} at {}",
- e.to_string(),
- source.format_location(pos))
- .into())
+ return Err(format!(
+ "{} at {}",
+ e.to_string(),
+ source.format_location(pos)
+ )
+ .into())
}
}
in_implements = true;
@@ -396,10 +401,12 @@ impl YarnFile {
// skip the entire block unless it's labelled yarn in which
// case it's an error...
if (linenr != 0) || (kind == "yarn") {
- return Err(format!("Unexpected content '{}' at {}",
- line,
- source.format_location(pos))
- .into());
+ return Err(format!(
+ "Unexpected content '{}' at {}",
+ line,
+ source.format_location(pos)
+ )
+ .into());
}
break;
}
@@ -434,8 +441,9 @@ impl YarnFile {
}
if let Some(cap) = AND_RE.captures(&tidyline) {
if last_cmd == YarnCommand::Unknown {
- return Err(format!("Unexpected AND at {}", source.format_location(pos))
- .into());
+ return Err(
+ format!("Unexpected AND at {}", source.format_location(pos)).into()
+ );
}
scen.push_line(last_cmd, cap[1].to_string(), pos);
continue;
@@ -456,10 +464,12 @@ impl YarnFile {
}
// If we reach this point without having processed the line
// then something went very wrong.
- return Err(format!("Unprocessed line '{}' at {}",
- tidyline,
- source.format_location(pos))
- .into());
+ return Err(format!(
+ "Unprocessed line '{}' at {}",
+ tidyline,
+ source.format_location(pos)
+ )
+ .into());
}
}
@@ -489,10 +499,11 @@ impl<'a> YarnFiles<'a> {
Ok(ret)
}
- pub fn find_implementation(&'a self,
- sline: &YarnScenarioLine,
- ssrc: &YarnSource)
- -> YarnResult<Option<(&'a YarnImplements, &'a YarnSource)>> {
+ pub fn find_implementation(
+ &'a self,
+ sline: &YarnScenarioLine,
+ ssrc: &YarnSource,
+ ) -> YarnResult<Option<(&'a YarnImplements, &'a YarnSource)>> {
let mut imps: Vec<(&YarnImplements, &YarnSource)> = Vec::new();
for f in &self.yarnfiles {
for imp in &f.implements {
@@ -504,22 +515,28 @@ impl<'a> YarnFiles<'a> {
if imps.len() == 0 && self.args.allow_missing {
Ok(None)
} else if imps.len() == 0 {
- Err(format!("'{}' at {} matches no implementation",
- ssrc.line(ssrc.linenum(sline.loc))?.trim(),
- ssrc.format_location(sline.loc))
- .into())
+ Err(format!(
+ "'{}' at {} matches no implementation",
+ ssrc.line(ssrc.linenum(sline.loc))?.trim(),
+ ssrc.format_location(sline.loc)
+ )
+ .into())
} else if imps.len() > 1 {
- let mut msg = format!("'{}' at {} matches implementations:",
- ssrc.line(ssrc.linenum(sline.loc))?,
- ssrc.format_location(sline.loc));
+ let mut msg = format!(
+ "'{}' at {} matches implementations:",
+ ssrc.line(ssrc.linenum(sline.loc))?,
+ ssrc.format_location(sline.loc)
+ );
let mut comma = false;
for &(imp, src) in &imps {
if comma {
msg.push(',');
}
- msg.push_str(&format!(" '{}' at {}",
- src.line(src.linenum(imp.loc))?.trim(),
- src.format_location(imp.loc)));
+ msg.push_str(&format!(
+ " '{}' at {}",
+ src.line(src.linenum(imp.loc))?.trim(),
+ src.format_location(imp.loc)
+ ));
comma = true;
}
Err(msg.into())
@@ -544,15 +561,16 @@ impl<'a> YarnFiles<'a> {
let mut badimpls = 0;
for maybeimpl in &f.implements {
if !implset.contains(maybeimpl) {
- msg.push_str(
- &format!("\n '{}' at {}",
- f.source.line(f.source.linenum(maybeimpl.loc))?.trim(),
- f.source.format_location(maybeimpl.loc)));
+ msg.push_str(&format!(
+ "\n '{}' at {}",
+ f.source.line(f.source.linenum(maybeimpl.loc))?.trim(),
+ f.source.format_location(maybeimpl.loc)
+ ));
badimpls += 1;
}
}
if badimpls > 0 {
- return Ok(Some(msg))
+ return Ok(Some(msg));
}
}
Ok(None)