Skip to content

Commit

Permalink
Avoid dependency on Rust nightly for capturing test output
Browse files Browse the repository at this point in the history
Instead of using unstable API available only on Rust nightly, this uses
the gag crate to capture stdout and stderr of the test scenarios. This
has one downside though, that crate currently works only on Unix-like
systems. But it is strictly more general as it redirects file
descriptors instead of just change the Rust-specific stdout and stderr
locked readers and writers.
  • Loading branch information
Adam Reichold committed May 30, 2019
1 parent 1df5b19 commit aa4bba3
Show file tree
Hide file tree
Showing 5 changed files with 22 additions and 53 deletions.
9 changes: 2 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ repository = "https://github.com/bbqsrc/cucumber-rust"
documentation = "https://docs.rs/cucumber_rust"
homepage = "https://github.com/bbqsrc/cucumber-rust"
edition = "2018"
build = "build.rs"

[badges]
travis-ci = { repository = "bbqsrc/cucumber-rust" }
Expand All @@ -26,9 +25,5 @@ pathdiff = "^0.1.0"
textwrap = { version = "0.11", features = ["term_size"] }
clap = "^2.32.0"
globwalk = "0.7"

[build-dependencies]
rustc_version = "0.2"

[features]
nightly = []
gag = "0.1"
tempfile = "3.0"
7 changes: 0 additions & 7 deletions build.rs

This file was deleted.

2 changes: 1 addition & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub fn make_app() -> Result<CliOptions, CliError> {
let feature = matches.value_of("feature").map(|v| v.to_string());
let tag = matches.value_of("tag").map(|v| v.to_string());

let suppress_output = cfg!(feature = "nightly") && !matches.is_present("nocapture");
let suppress_output = !matches.is_present("nocapture");

Ok(CliOptions {
feature,
Expand Down
2 changes: 0 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg_attr(feature = "nightly", feature(set_stdio))]

pub extern crate gherkin_rust as gherkin;
pub extern crate globwalk;
pub extern crate regex;
Expand Down
55 changes: 19 additions & 36 deletions src/panic_trap.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use std::io::{self, Write};
use std::io::{Read, Seek, SeekFrom};
use std::ops::Deref;
use std::panic;
use std::sync::{Arc, Mutex};

use gag::Redirect;
use tempfile::tempfile;

#[derive(Clone)]
pub struct PanicDetails {
pub payload: String,
Expand All @@ -29,19 +32,6 @@ impl PanicDetails {
}
}

#[derive(Default)]
struct Sink(Arc<Mutex<Vec<u8>>>);

impl Write for Sink {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
Write::write(&mut *self.0.lock().unwrap(), data)
}

fn flush(&mut self) -> io::Result<()> {
self.0.lock().unwrap().flush()
}
}

pub struct PanicTrap<T> {
pub stdout: Vec<u8>,
pub result: Result<T, PanicDetails>,
Expand All @@ -56,35 +46,28 @@ impl<T> PanicTrap<T> {
}
}

#[cfg(feature = "nightly")]
fn run_quietly<F: FnOnce() -> T>(f: F) -> PanicTrap<T> {
let stdout_sink = Arc::new(Mutex::new(vec![]));
let stdout_sink_hook = stdout_sink.clone();
let old_io = (
io::set_print(Some(Box::new(Sink(stdout_sink.clone())))),
io::set_panic(Some(Box::new(Sink(stdout_sink)))),
);

let loud_panic_trap = PanicTrap::run_loudly(f);

io::set_print(old_io.0);
io::set_panic(old_io.1);

let stdout = stdout_sink_hook
.lock()
.expect("Stdout mutex poisoned")
.clone();
let mut tmp = tempfile().unwrap();

let loud_panic_trap = {
let _stdout =
Redirect::stdout(tmp.try_clone().unwrap()).expect("Failed to capture stdout");
let _stderr =
Redirect::stderr(tmp.try_clone().unwrap()).expect("Failed to capture stderr");

PanicTrap::run_loudly(f)
};

let mut stdout = Vec::new();
tmp.seek(SeekFrom::Start(0)).unwrap();
tmp.read_to_end(&mut stdout).unwrap();

PanicTrap {
stdout,
result: loud_panic_trap.result,
}
}

#[cfg(not(feature = "nightly"))]
fn run_quietly<F: FnOnce() -> T>(_f: F) -> PanicTrap<T> {
panic!("PanicTrap cannot run quietly without the 'nightly' feature");
}

fn run_loudly<F: FnOnce() -> T>(f: F) -> PanicTrap<T> {
let last_panic = Arc::new(Mutex::new(None));

Expand Down

0 comments on commit aa4bba3

Please sign in to comment.