diff options
Diffstat (limited to 'subplotlib/subplot-rust-support.rs')
-rw-r--r-- | subplotlib/subplot-rust-support.rs | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/subplotlib/subplot-rust-support.rs b/subplotlib/subplot-rust-support.rs new file mode 100644 index 0000000..19a4ff7 --- /dev/null +++ b/subplotlib/subplot-rust-support.rs @@ -0,0 +1,219 @@ +// Rust support for running subplot-rust.md + +use subplotlib::steplibrary::datadir::Datadir; +use subplotlib::steplibrary::runcmd::{self, Runcmd}; + +use tempfile::TempDir; + +use std::io::{Read, Seek, SeekFrom}; + +#[derive(Default)] +struct SubplotContext { + bin_dir: Option<TempDir>, +} + +impl ContextElement for SubplotContext {} + +#[step] +fn do_nothing(context: &ScenarioContext) { + // Nothing to do here +} + +#[step] +#[context(SubplotContext)] +#[context(Runcmd)] +fn install_subplot(context: &ScenarioContext) { + if let Some(bindir) = std::env::var_os("SUBPLOT_DIR") { + println!("Found SUBPLOT_DIR environment variable, using that"); + context.with_mut( + |rc: &mut Runcmd| { + rc.prepend_to_path(bindir); + Ok(()) + }, + false, + )?; + } else { + let bin_dir = TempDir::new()?; + println!("Creating temporary rundir at {}", bin_dir.path().display()); + + // Since we don't get CARGO_BIN_EXE_subplot when building a subcrate + // we retrieve the path to `subplot` via the assumption that integration + // tests are always located one dir down from the outer crate binaries. + let target_path = std::fs::canonicalize( + std::env::current_exe() + .expect("Cannot determine test exe path") + .parent() + .unwrap() + .join(".."), + ) + .expect("Cannot canonicalise path to binaries"); + + let src_dir = env!("CARGO_MANIFEST_DIR"); + for bin_name in &["subplot"] { + let file_path = bin_dir.path().join(bin_name); + std::fs::write( + &file_path, + format!( + r#" +#!/bin/sh +set -eu +exec '{target_path}/{bin_name}' --resources '{src_dir}/share' "$@" +"#, + target_path = target_path.display(), + bin_name = bin_name, + src_dir = src_dir, + ), + )?; + { + let mut perms = std::fs::metadata(&file_path)?.permissions(); + use std::os::unix::fs::PermissionsExt; + perms.set_mode(perms.mode() | 0o111); // Set executable bit + std::fs::set_permissions(&file_path, perms)?; + } + } + + context.with_mut( + |context: &mut Runcmd| { + context.prepend_to_path(bin_dir.path()); + context.prepend_to_path(target_path); + Ok(()) + }, + false, + )?; + } +} + +#[step] +fn uninstall_subplot(context: &mut SubplotContext) { + context.bin_dir.take(); +} + +#[step] +#[context(Runcmd)] +fn scenario_was_run(context: &ScenarioContext, name: &str) { + let text = format!("\nscenario: {}\n", name); + runcmd::stdout_contains::call(context, &text)?; +} + +#[step] +#[context(Runcmd)] +fn scenario_was_not_run(context: &ScenarioContext, name: &str) { + let text = format!("\nscenario: {}\n", name); + runcmd::stdout_doesnt_contain::call(context, &text)?; +} + +#[step] +#[context(Runcmd)] +fn step_was_run(context: &ScenarioContext, keyword: &str, name: &str) { + let text = format!("\n step: {} {}\n", keyword, name); + runcmd::stdout_contains::call(context, &text)?; +} + +#[step] +#[context(Runcmd)] +fn step_was_run_and_then( + context: &ScenarioContext, + keyword1: &str, + name1: &str, + keyword2: &str, + name2: &str, +) { + let text = format!( + "\n step: {} {}\n step: {} {}", + keyword1, name1, keyword2, name2 + ); + runcmd::stdout_contains::call(context, &text)?; +} + +#[step] +#[context(Runcmd)] +fn cleanup_was_run( + context: &ScenarioContext, + keyword1: &str, + name1: &str, + keyword2: &str, + name2: &str, +) { + let text = format!( + "\n cleanup: {} {}\n cleanup: {} {}\n", + keyword1, name1, keyword2, name2 + ); + runcmd::stdout_contains::call(context, &text)?; +} + +#[step] +#[context(Runcmd)] +fn cleanup_was_not_run(context: &ScenarioContext, keyword: &str, name: &str) { + let text = format!("\n cleanup: {} {}\n", keyword, name); + runcmd::stdout_doesnt_contain::call(context, &text)?; +} + +#[throws(StepError)] +fn end_of_file(context: &Datadir, filename: &str, nbytes: usize) -> Vec<u8> { + let mut fh = context.open_read(filename)?; + fh.seek(SeekFrom::End(-(nbytes as i64)))?; + let mut b = vec![0; nbytes]; + fh.read_exact(&mut b[0..nbytes])?; + b +} + +#[step] +fn file_ends_in_zero_newlines(context: &Datadir, filename: &str) { + let b = end_of_file(context, filename, 1)?; + if b[0] == b'\n' { + throw!(format!("File {} ends in unexpected newline", filename)); + } +} + +#[step] +fn file_ends_in_one_newline(context: &Datadir, filename: &str) { + let b = end_of_file(context, filename, 2)?; + if !(b[0] != b'\n' && b[1] == b'\n') { + throw!(format!( + "File {} does not end in exactly one newline", + filename + )); + } +} + +#[step] +fn file_ends_in_two_newlines(context: &Datadir, filename: &str) { + let b = end_of_file(context, filename, 2)?; + if b[0] != b'\n' || b[1] != b'\n' { + throw!(format!( + "File {} does not end in exactly two newlines", + filename + )); + } +} + +#[step] +fn sleep_seconds(context: &Datadir, delay: u64) { + std::thread::sleep(std::time::Duration::from_secs(delay)); +} + +#[step] +#[context(Datadir)] +#[context(Runcmd)] +fn json_output_matches_file(context: &ScenarioContext, filename: &str) { + let output = context.with(|rc: &Runcmd| Ok(rc.stdout_as_string()), false)?; + let fcontent = context.with( + |dd: &Datadir| { + Ok(std::fs::read_to_string( + dd.canonicalise_filename(filename)?, + )?) + }, + false, + )?; + let output: serde_json::Value = serde_json::from_str(&output)?; + let fcontent: serde_json::Value = serde_json::from_str(&fcontent)?; + println!("########"); + println!("Output:\n{:#}", output); + println!("File:\n{:#}", fcontent); + println!("########"); + assert_eq!( + output, fcontent, + "Command output does not match the content of {}", + filename + ); +} |