From a0df28bed44def6cddb342d0331313a298bdf00c Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Mon, 27 Dec 2021 14:09:52 +0000 Subject: codegen: Refuse to generate code if the specified template is not present Signed-off-by: Daniel Silverstone --- src/doc.rs | 3 +++ src/error.rs | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/src/doc.rs b/src/doc.rs index 9989bc4..5c81e27 100644 --- a/src/doc.rs +++ b/src/doc.rs @@ -559,6 +559,9 @@ pub fn codegen(filename: &Path, output: &Path, template: Option<&str>) -> Result .unwrap_or_else(|| doc.template())? .to_string(); event!(Level::TRACE, ?template); + if !doc.meta().templates().any(|t| t == template) { + return Err(SubplotError::TemplateSupportNotPresent); + } if !doc.check_named_files_exist(&template)? || !doc.check_matched_steps_have_impl(&template) || !doc.check_embedded_files_are_used(&template)? diff --git a/src/error.rs b/src/error.rs index ec88733..bcee4ff 100644 --- a/src/error.rs +++ b/src/error.rs @@ -125,6 +125,15 @@ pub enum SubplotError { #[error("document has more than one template possibility")] AmbiguousTemplate, + /// Document does not support the requested template + /// + /// The document YAML metadata does not specify support for the + /// stated template. + /// + /// To fix, specify a template which is provided for in the document. + #[error("document lacks specified template support")] + TemplateSupportNotPresent, + /// Pandoc AST is not JSON /// /// Subplot acts as a Pandoc filter, and as part of that Pandoc -- cgit v1.2.1 From dcc24d0c74a5af88253129e6e22023e8050cc72e Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sun, 30 May 2021 21:35:56 +0100 Subject: rust/template: Permit non_snake_case for test functions Signed-off-by: Daniel Silverstone --- share/rust/template/template.rs.tera | 1 + 1 file changed, 1 insertion(+) diff --git a/share/rust/template/template.rs.tera b/share/rust/template/template.rs.tera index c94d63c..65fb755 100644 --- a/share/rust/template/template.rs.tera +++ b/share/rust/template/template.rs.tera @@ -28,6 +28,7 @@ lazy_static! { // {{ scenario.title | commentsafe }} #[test] +#[allow(non_snake_case)] fn {{ scenario.title | nameslug }}() { let mut scenario = Scenario::new(&base64_decode("{{scenario.title | base64}}")); {% for step in scenario.steps %} -- cgit v1.2.1 From 689eb6665eca5bf2a86ddb3c40e25e438dd72d0a Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sun, 30 May 2021 21:36:26 +0100 Subject: datadir: Add open_read() method to Datadir context Signed-off-by: Daniel Silverstone --- subplotlib/src/steplibrary/datadir.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/subplotlib/src/steplibrary/datadir.rs b/subplotlib/src/steplibrary/datadir.rs index 8aa6f00..88e6375 100644 --- a/subplotlib/src/steplibrary/datadir.rs +++ b/subplotlib/src/steplibrary/datadir.rs @@ -91,6 +91,17 @@ impl Datadir { .open(full_path)? } + /// Open a file for reading + #[throws(StepError)] + pub fn open_read>(&self, subpath: S) -> File { + let full_path = self.canonicalise_filename(subpath)?; + OpenOptions::new() + .create(false) + .write(false) + .read(true) + .open(full_path)? + } + #[throws(StepError)] pub fn create_dir_all>(&self, subpath: S) { let full_path = self.canonicalise_filename(subpath)?; -- cgit v1.2.1 From 32bc1e9aaa2b8a6a08c27d8b8fda838d741766b9 Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sun, 30 May 2021 21:39:20 +0100 Subject: runcmd: Add stdout/stderr accessors Signed-off-by: Daniel Silverstone --- subplotlib/src/steplibrary/runcmd.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/subplotlib/src/steplibrary/runcmd.rs b/subplotlib/src/steplibrary/runcmd.rs index f42df6e..5fc12eb 100644 --- a/subplotlib/src/steplibrary/runcmd.rs +++ b/subplotlib/src/steplibrary/runcmd.rs @@ -67,9 +67,24 @@ impl ContextElement for Runcmd { } impl Runcmd { + /// Prepend the given location to the run path pub fn prepend_to_path>(&mut self, element: S) { self.paths.push(element.into()); } + + /// Retrieve the last run command's stdout as a string. + /// + /// This does a lossy conversion from utf8 so should always succeed. + pub fn stdout_as_string(&self) -> String { + String::from_utf8_lossy(&self.stdout).into_owned() + } + + /// Retrieve the last run command's stderr as a string. + /// + /// This does a lossy conversion from utf8 so should always succeed. + pub fn stderr_as_string(&self) -> String { + String::from_utf8_lossy(&self.stderr).into_owned() + } } #[step] -- cgit v1.2.1 From d1f336e141c3533c5e4dda049f03dfe168f99470 Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sun, 30 May 2021 21:39:58 +0100 Subject: subplotlib: Add some scenario running output to make it easier to work out what broke Signed-off-by: Daniel Silverstone --- subplotlib/src/scenario.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/subplotlib/src/scenario.rs b/subplotlib/src/scenario.rs index b239883..c0f3e87 100644 --- a/subplotlib/src/scenario.rs +++ b/subplotlib/src/scenario.rs @@ -284,6 +284,7 @@ impl Scenario { // Firstly, we start all the contexts let mut ret = Ok(()); let mut highest_start = None; + println!("Scenario Start: {}", self.contexts.title()); for (i, hook) in self.contexts.hooks.borrow().iter().enumerate() { let res = hook.scenario_starts(&self.contexts); if res.is_err() { @@ -292,10 +293,14 @@ impl Scenario { } highest_start = Some(i); } - + println!( + "*** Context hooks returned {}", + if ret.is_ok() { "OK" } else { "Failure" } + ); if ret.is_ok() { let mut highest = None; for (i, step) in self.steps.iter().map(|(step, _)| step).enumerate() { + println!(" !!! Step {}", step.name()); let mut highest_prep = None; for (i, prep) in self.contexts.hooks.borrow().iter().enumerate() { let res = prep.step_starts(&self.contexts, step.name()); @@ -305,8 +310,17 @@ impl Scenario { } highest_prep = Some(i); } + println!( + " *** Context hooks returned {}", + if ret.is_ok() { "OK" } else { "Failure" } + ); if ret.is_ok() { + println!(" >>> Run step function"); let res = step.call(&self.contexts, false); + println!( + " Step returned {}", + if res.is_ok() { "OK" } else { "Failure" } + ); if res.is_err() { ret = res; break; @@ -314,6 +328,7 @@ impl Scenario { highest = Some(i); } if let Some(n) = highest_prep { + println!(" *** Unwinding step contexts"); for hookn in (0..=n).rev() { let res = self.contexts.hooks.borrow()[hookn].step_stops(&self.contexts); ret = ret.and(res) @@ -321,9 +336,15 @@ impl Scenario { } } if let Some(n) = highest { + println!(" *** Running cleanup functions"); for stepn in (0..=n).rev() { if let (_, Some(cleanup)) = &self.steps[stepn] { + println!(" >>> Cleanup {}", cleanup.name()); let res = cleanup.call(&self.contexts, true); + println!( + " Cleanup returned {}", + if res.is_ok() { "OK" } else { "Failure" } + ); ret = ret.and(res); } } @@ -331,11 +352,16 @@ impl Scenario { } if let Some(n) = highest_start { + println!("*** Running scenario closedown"); for hookn in (0..=n).rev() { let res = self.contexts.hooks.borrow()[hookn].scenario_stops(&self.contexts); ret = ret.and(res); } } + println!( + "<<< Scenario returns {}", + if ret.is_ok() { "OK" } else { "Failure" } + ); ret } } -- cgit v1.2.1 From 0fd065044076888285f42e0105f07a0c924b2819 Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Mon, 27 Dec 2021 14:29:28 +0000 Subject: subplotlib: Run subplot.md in Rust too Signed-off-by: Daniel Silverstone --- Cargo.lock | 1 + subplot.md | 2 + subplot.yaml | 31 ++++++ subplotlib/Cargo.toml | 7 +- subplotlib/build.rs | 3 +- subplotlib/subplot-rust-support.rs | 219 +++++++++++++++++++++++++++++++++++++ subplotlib/tests/subplot.rs | 1 + 7 files changed, 261 insertions(+), 3 deletions(-) create mode 100644 subplotlib/subplot-rust-support.rs create mode 100644 subplotlib/tests/subplot.rs diff --git a/Cargo.lock b/Cargo.lock index 494d6a9..79aeabe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1159,6 +1159,7 @@ dependencies = [ "lazy_static", "regex", "remove_dir_all 0.7.0", + "serde_json", "shell-words", "state", "subplot-build", diff --git a/subplot.md b/subplot.md index b1c500b..d2ec877 100644 --- a/subplot.md +++ b/subplot.md @@ -10,6 +10,8 @@ impls: - subplot.py - lib/files.py - lib/runcmd.py + rust: + - subplotlib/subplot-rust-support.rs classes: - json ... diff --git a/subplot.yaml b/subplot.yaml index b865d83..12a9fae 100644 --- a/subplot.yaml +++ b/subplot.yaml @@ -3,60 +3,83 @@ python: function: install_subplot cleanup: uninstall_subplot + rust: + function: install_subplot + cleanup: uninstall_subplot - then: scenario "{name:text}" was run impl: python: function: scenario_was_run + rust: + function: scenario_was_run - then: scenario "{name:text}" was not run impl: python: function: scenario_was_not_run + rust: + function: scenario_was_not_run - then: step "(?Pgiven|when|then) (?P.+)" was run impl: python: function: step_was_run + rust: + function: step_was_run regex: true - then: step "(?Pgiven|when|then) (?P.+)" was run, and then step "(?Pgiven|when|then) (?P.+)" impl: python: function: step_was_run_and_then + rust: + function: step_was_run_and_then regex: true - then: cleanup for "(?Pgiven|when|then) (?P.+)" was run, and then for "(?Pgiven|when|then) (?P.+)" impl: python: function: cleanup_was_run + rust: + function: cleanup_was_run regex: true - then: cleanup for "(?Pgiven|when|then) (?P.+)" was not run impl: python: function: cleanup_was_not_run + rust: + function: cleanup_was_not_run regex: true - then: JSON output matches {filename} impl: python: function: json_output_matches_file + rust: + function: json_output_matches_file - then: "{filename} does not end in a newline" impl: python: function: file_ends_in_zero_newlines + rust: + function: file_ends_in_zero_newlines - then: "{filename} ends in one newline" impl: python: function: file_ends_in_one_newline + rust: + function: file_ends_in_one_newline - then: "{filename} ends in two newlines" impl: python: function: file_ends_in_two_newlines + rust: + function: file_ends_in_two_newlines # In order to cope with low granularity filesystems, sometimes we need to wait # for things to happen @@ -64,6 +87,8 @@ impl: python: function: sleep_seconds + rust: + function: sleep_seconds regex: true types: delay: uint @@ -74,13 +99,19 @@ impl: python: function: do_nothing + rust: + function: do_nothing - when: I do the required actions impl: python: function: do_nothing + rust: + function: do_nothing - then: the desired outcome is achieved impl: python: function: do_nothing + rust: + function: do_nothing diff --git a/subplotlib/Cargo.toml b/subplotlib/Cargo.toml index 781a017..ed3fb8a 100644 --- a/subplotlib/Cargo.toml +++ b/subplotlib/Cargo.toml @@ -16,7 +16,7 @@ repository = "https://gitlab.com/subplot/subplot" [dependencies] fehler = "1" -subplotlib-derive = { version="0.1", path = "../subplotlib-derive" } +subplotlib-derive = { version = "0.1", path = "../subplotlib-derive" } lazy_static = "1" base64 = "0.13" state = "0.5" @@ -32,4 +32,7 @@ remove_dir_all = "0.7" [build-dependencies] glob = "0.3" -subplot-build = { version="0.1", path = "../subplot-build" } +subplot-build = { version = "0.1", path = "../subplot-build" } + +[dev-dependencies] +serde_json = "1.0" diff --git a/subplotlib/build.rs b/subplotlib/build.rs index 5f94883..a809a68 100644 --- a/subplotlib/build.rs +++ b/subplotlib/build.rs @@ -17,9 +17,10 @@ use std::path::Path; fn main() { let subplots = glob("*.md").expect("failed to find subplots in subplotlib"); let tests = Path::new("tests"); + let subplots = subplots.chain(Some(Ok("../subplot.md".into()))); for entry in subplots { let entry = entry.expect("failed to get subplot dir entry in subplotlib"); - let mut inc = tests.join(&entry); + let mut inc = tests.join(&entry.file_name().unwrap()); inc.set_extension("rs"); if !inc.exists() { panic!("missing include file: {}", inc.display()); 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, +} + +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 { + 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 + ); +} diff --git a/subplotlib/tests/subplot.rs b/subplotlib/tests/subplot.rs new file mode 100644 index 0000000..00accd7 --- /dev/null +++ b/subplotlib/tests/subplot.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/subplot.rs")); -- cgit v1.2.1 From 3d764c2b7f3c8871d100297e8d7ed01810df9e0d Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sat, 1 Jan 2022 10:24:16 +0000 Subject: flake: Ensure paths are provided more correctly Signed-off-by: Daniel Silverstone --- flake.nix | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/flake.nix b/flake.nix index cdd50e6..9f1aafe 100644 --- a/flake.nix +++ b/flake.nix @@ -30,11 +30,9 @@ (python3.withPackages test-python-packages) black ]; - shellHook = '' - export SUBPLOT_DOT_PATH=${pkgs.graphviz}/bin/dot - export SUBPLOT_JAVA_PATH=${pkgs.jre}/bin/java - export SUBPLOT_PLANTUML_JAR_PATH=${pkgs.plantuml}/lib/plantuml.jar - ''; + SUBPLOT_DOT_PATH = "${pkgs.graphviz}/bin/dot"; + SUBPLOT_JAVA_PATH = "${pkgs.jre}/bin/java"; + SUBPLOT_PLANTUML_JAR_PATH = "${pkgs.plantuml}/lib/plantuml.jar"; }; }); } -- cgit v1.2.1 From d4856f7b04b5f58118e518fd2a3413363dcad6ce Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sat, 1 Jan 2022 11:13:06 +0000 Subject: check: Cope with polyglot documents Signed-off-by: Daniel Silverstone --- check | 71 +++++++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/check b/check index cb5ffc1..2582709 100755 --- a/check +++ b/check @@ -114,7 +114,7 @@ class Runcmd: ] return subcommand in lines - def codegen(self, md, output, **kwargs): + def codegen(self, md, template, output, **kwargs): """Run the Subplot code generator and the test program it produces""" self.cargo( [ @@ -124,6 +124,7 @@ class Runcmd: "--", f"--resources={os.path.abspath('share')}", "codegen", + f"--template={template}", md, f"--output={output}", ], @@ -160,7 +161,7 @@ class Runcmd: **kwargs, ) - def get_template(self, filename): + def get_templates(self, filename): metadata = self.cargo( [ "run", @@ -181,7 +182,7 @@ class Runcmd: if not impls: sys.exit(f"{filename} does not specify a template") impl_names = [name for name in impls.keys()] - return impl_names[0] + return impl_names def find_files(pattern, pred): @@ -270,38 +271,40 @@ def check_subplots(r): md = os.path.basename(md0) base, _ = os.path.splitext(md) - template = r.get_template(md0) - if template == "python": - test_py = os.path.join(output, f"test-{base}.py") - test_log = os.path.join(output, f"test-{base}.log") - - # Remove test log from previous run, if any. - if os.path.exists(test_log): - os.remove(test_log) - - bindir = get_bin_dir(r) - - r.codegen(md, test_py, cwd=dirname) - p = r.runcmd_unchecked( - [ - "python3", - test_py, - "--log", - test_log, - f"--env=SUBPLOT_DIR={bindir}", - ], - cwd=dirname, - ) - if p.returncode != 0: + for template in r.get_templates(md0): + if template == "python": + test_py = os.path.join(output, f"test-{base}.py") + test_log = os.path.join(output, f"test-{base}.log") + + # Remove test log from previous run, if any. if os.path.exists(test_log): - tail(test_log) - sys.exit(1) - elif template == "bash": - test_sh = os.path.join(output, f"test-{base}.sh") - r.codegen(md, test_sh, cwd=dirname) - r.runcmd(["bash", "-x", test_sh], cwd=dirname) - else: - sys.exit(f"unknown template {template} in {md0}") + os.remove(test_log) + + bindir = get_bin_dir(r) + + r.codegen(md, "python", test_py, cwd=dirname) + p = r.runcmd_unchecked( + [ + "python3", + test_py, + "--log", + test_log, + f"--env=SUBPLOT_DIR={bindir}", + ], + cwd=dirname, + ) + if p.returncode != 0: + if os.path.exists(test_log): + tail(test_log) + sys.exit(1) + elif template == "bash": + test_sh = os.path.join(output, f"test-{base}.sh") + r.codegen(md, "bash", test_sh, cwd=dirname) + r.runcmd(["bash", "-x", test_sh], cwd=dirname) + elif template == "rust": + r.msg(f"Ignoring Rust template in {md0}") + else: + sys.exit(f"unknown template {template} in {md0}") base = os.path.basename(md) base, _ = os.path.splitext(md) -- cgit v1.2.1 From 95b3d27d27a8dab7cb8a5136000a2331ab9b4a4a Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sat, 1 Jan 2022 12:03:51 +0000 Subject: tests: Move files.md to be common between Python and subplotlib Signed-off-by: Daniel Silverstone --- subplotlib/build.rs | 2 + subplotlib/files.md | 102 ---------------- tests/python/daemon.md | 189 ----------------------------- tests/python/files.md | 105 ---------------- tests/python/runcmd.md | 214 --------------------------------- tests/python/runcmd_test.py | 15 --- tests/python/runcmd_test.yaml | 9 -- tests/subplots/common/files.md | 107 +++++++++++++++++ tests/subplots/python/daemon.md | 189 +++++++++++++++++++++++++++++ tests/subplots/python/runcmd.md | 214 +++++++++++++++++++++++++++++++++ tests/subplots/python/runcmd_test.py | 15 +++ tests/subplots/python/runcmd_test.yaml | 9 ++ 12 files changed, 536 insertions(+), 634 deletions(-) delete mode 100644 subplotlib/files.md delete mode 100644 tests/python/daemon.md delete mode 100644 tests/python/files.md delete mode 100644 tests/python/runcmd.md delete mode 100644 tests/python/runcmd_test.py delete mode 100644 tests/python/runcmd_test.yaml create mode 100644 tests/subplots/common/files.md create mode 100644 tests/subplots/python/daemon.md create mode 100644 tests/subplots/python/runcmd.md create mode 100644 tests/subplots/python/runcmd_test.py create mode 100644 tests/subplots/python/runcmd_test.yaml diff --git a/subplotlib/build.rs b/subplotlib/build.rs index a809a68..d1eeefd 100644 --- a/subplotlib/build.rs +++ b/subplotlib/build.rs @@ -18,6 +18,8 @@ fn main() { let subplots = glob("*.md").expect("failed to find subplots in subplotlib"); let tests = Path::new("tests"); let subplots = subplots.chain(Some(Ok("../subplot.md".into()))); + let subplots = subplots + .chain(glob("../tests/subplots/common/*.md").expect("failed to find common subplots")); for entry in subplots { let entry = entry.expect("failed to get subplot dir entry in subplotlib"); let mut inc = tests.join(&entry.file_name().unwrap()); diff --git a/subplotlib/files.md b/subplotlib/files.md deleted file mode 100644 index 22e8f3c..0000000 --- a/subplotlib/files.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Acceptance criteria for the files subplotlib step library -author: The Subplot project -bindings: - - lib/files.yaml -impls: - rust: [] -... - -# Introduction - -The [Subplot][] library `files` provides scenario steps and their -implementations for managing files on the file system during tests. -The library consists of a bindings file `subplotlib/steplibrary/files.yaml` and -implementations in Rust as part of `subplotlib`. - -[subplot]: https://subplot.liw.fi/ - -This document explains the acceptance criteria for the library and how -they're verified. It uses the steps and functions from the `files` -step library. - -# Create on-disk files from embedded files - -Subplot allows the source document to embed test files, and the -`files` library provides steps to create real, on-disk files from -the embedded files. - -```scenario -given file hello.txt -then file hello.txt exists -and file hello.txt contains "hello, world" -and file other.txt does not exist -given file other.txt from hello.txt -then file other.txt exists -and files hello.txt and other.txt match -and only files hello.txt, other.txt exist -``` - -```{#hello.txt .file .numberLines} -hello, world -``` - -# File metadata - -These steps create files and manage their metadata. - -```scenario -given file hello.txt -when I remember metadata for file hello.txt -then file hello.txt has same metadata as before - -when I write "yo" to file hello.txt -then file hello.txt has different metadata from before -``` - -# File modification time - -These steps manipulate and test file modification times. - -```scenario -given file foo.dat has modification time 1970-01-02 03:04:05 -then file foo.dat has a very old modification time - -when I touch file foo.dat -then file foo.dat has a very recent modification time -``` - -# File contents - -These steps verify contents of files. - -```scenario -given file hello.txt -then file hello.txt contains "hello, world" -and file hello.txt matches regex "hello, .*" -and file hello.txt matches regex /hello, .*/ -``` - -# Directories - -There are also a large number of directory based steps and some directory -based behaviour available in creating files which are available in the files -library. - -```scenario -given a directory first -then directory first exists -and directory first is empty -and directory second does not exist -when I remove directory first -then directory first does not exist -when I create directory second -then directory second exists -and directory second is empty -given file second/third/hello.txt from hello.txt -then directory second is not empty -and directory second/third exists -and directory second/third is not empty -when I remove directory second -then directory second does not exist -``` diff --git a/tests/python/daemon.md b/tests/python/daemon.md deleted file mode 100644 index 51c77b4..0000000 --- a/tests/python/daemon.md +++ /dev/null @@ -1,189 +0,0 @@ -# Introduction - -The [Subplot][] library `daemon` for Python provides scenario steps -and their implementations for running a background process and -terminating at the end of the scenario. - -[Subplot]: https://subplot.liw.fi/ - -This document explains the acceptance criteria for the library and how -they're verified. It uses the steps and functions from the -`lib/daemon` library. The scenarios all have the same structure: run a -command, then examine the exit code, verify the process is running. - -# Daemon is started and terminated - -This scenario starts a background process, verifies it's started, and -verifies it's terminated after the scenario ends. - -~~~scenario -given there is no "sleep 12765" process -when I start "sleep 12765" as a background process as sleepyhead -then a process "sleep 12765" is running -when I stop background process sleepyhead -then there is no "sleep 12765" process -~~~ - - -# Daemon takes a while to open its port - -This scenario verifies that if the background process doesn't immediately start -listening on its port, the daemon library handles that correctly. We do this -with a helper script that waits 2 seconds before opening the port. The -lib/daemon code will wait for the script by repeatedly trying to connect. Once -successful, it immediately closes the port, which causes the script to -terminate. - -~~~scenario -given a daemon helper shell script slow-start-daemon.py -given there is no "slow-start-daemon.py" process -when I try to start "./slow-start-daemon.py" as slow-daemon, on port 8888 -then starting the daemon succeeds -when I stop background process slow-daemon -then there is no "slow-start-daemon.py" process -~~~ - -~~~{#slow-start-daemon.py .file .python .numberLines} -#!/usr/bin/env python3 - -import socket -import time - -time.sleep(2) - -s = socket.socket() -s.bind(("127.0.0.1", 8888)) -s.listen() - -(conn, _) = s.accept() -conn.recv(1) -s.close() - -print("OK") -~~~ - -# Daemon never opens the intended port - -This scenario verifies that if the background process never starts -listening on its port, the daemon library handles that correctly. - -~~~scenario -given there is no "sleep 12765" process -when I try to start "sleep 12765" as sleepyhead, on port 8888 -then starting daemon fails with "ConnectionRefusedError" -then a process "sleep 12765" is running -when I stop background process sleepyhead -then there is no "sleep 12765" process -~~~ - - -# Daemon stdout and stderr are retrievable - -Sometimes it's useful for the step functions to be able to retrieve -the stdout or stderr of of the daemon, after it's started, or even -after it's terminated. This scenario verifies that `lib/daemon` can do -that. - -~~~scenario -given a daemon helper shell script chatty-daemon.sh -given there is no "chatty-daemon" process -when I start "./chatty-daemon.sh" as a background process as chatty-daemon -when daemon chatty-daemon has produced output -when I stop background process chatty-daemon -then there is no "chatty-daemon" process -then daemon chatty-daemon stdout is "hi there\n" -then daemon chatty-daemon stderr is "hola\n" -~~~ - -We make for the daemon to exit, to work around a race condition: if -the test program retrieves the daemon's output too fast, it may not -have had time to produce it yet. - - -~~~{#chatty-daemon.sh .file .sh .numberLines} -#!/usr/bin/env bash - -set -euo pipefail - -trap 'exit 0' TERM - -echo hola 1>&2 -echo hi there -~~~ - -# Can specify additional environment variables for daemon - -Some daemons are configured through their environment rather than configuration -files. This scenario verifies that a step can set arbitrary variables in the -daemon's environment. - -~~~scenario -when I start "/usr/bin/env" as a background process as env, with environment {"custom_variable": "has a Value"} -when daemon env has produced output -when I stop background process env -then daemon env stdout contains "custom_variable=has a Value" -~~~ - -~~~scenario -given a daemon helper shell script env-with-port.py -when I try to start "./env-with-port.py 8765" as env-with-port, on port 8765, with environment {"custom_variable": "1337"} -when I stop background process env-with-port -then daemon env-with-port stdout contains "custom_variable=1337" -~~~ - -~~~scenario -given a daemon helper shell script env-with-port.py -when I start "./env-with-port.py 8766" as a background process as another-env-with-port, on port 8766, with environment {"subplot2": "000"} -when daemon another-env-with-port has produced output -when I stop background process another-env-with-port -then daemon another-env-with-port stdout contains "subplot2=000" -~~~ - -It's important that these new environment variables are not inherited by the -steps that follow. To verify that, we run one more scenario which *doesn't* set -any variables, but checks that none of the variables we mentioned above are -present. - -~~~scenario -when I start "/usr/bin/env" as a background process as env2 -when daemon env2 has produced output -when I stop background process env2 -then daemon env2 stdout doesn't contain "custom_variable=has a Value" -then daemon env2 stdout doesn't contain "custom_variable=1337" -then daemon env2 stdout doesn't contain "subplot2=000" -~~~ - -~~~{#env-with-port.py .file .python .numberLines} -#!/usr/bin/env python3 - -import os -import socket -import sys -import time - -for (key, value) in os.environ.items(): - print(f"{key}={value}") - -port = int(sys.argv[1]) -print(f"port is {port}") - -s = socket.socket() -s.bind(("127.0.0.1", port)) -s.listen() - -(conn, _) = s.accept() -conn.recv(1) -s.close() -~~~ - - ---- -title: Acceptance criteria for the lib/daemon Subplot library -author: The Subplot project -bindings: -- lib/daemon.yaml -impls: - python: - - lib/daemon.py - - lib/runcmd.py -... diff --git a/tests/python/files.md b/tests/python/files.md deleted file mode 100644 index 7837da6..0000000 --- a/tests/python/files.md +++ /dev/null @@ -1,105 +0,0 @@ -# Introduction - -The [Subplot][] library `files` provides scenario steps and their -implementations for managing files on the file system during tests. -The library consists of a bindings file `lib/files.yaml` and -implementations in Python in `lib/files.py`. - -[Subplot]: https://subplot.liw.fi/ - -This document explains the acceptance criteria for the library and how -they're verified. It uses the steps and functions from the `files` -library. - -# Create on-disk files from embedded files - -Subplot allows the source document to embed test files, and the -`files` library provides steps to create real, on-disk files from -the embedded files. - -~~~scenario -given file hello.txt -then file hello.txt exists -and file hello.txt contains "hello, world" -and file other.txt does not exist -given file other.txt from hello.txt -then file other.txt exists -and files hello.txt and other.txt match -and only files hello.txt, other.txt exist -~~~ - -~~~{#hello.txt .file .numberLines} -hello, world -~~~ - - -# File metadata - -These steps create files and manage their metadata. - -~~~scenario -given file hello.txt -when I remember metadata for file hello.txt -then file hello.txt has same metadata as before - -when I write "yo" to file hello.txt -then file hello.txt has different metadata from before -~~~ - -# File modification time - -These steps manipulate and test file modification times. - -~~~scenario -given file foo.dat has modification time 1970-01-02 03:04:05 -then file foo.dat has a very old modification time - -when I touch file foo.dat -then file foo.dat has a very recent modification time -~~~ - - -# File contents - -These steps verify contents of files. - -~~~scenario -given file hello.txt -then file hello.txt contains "hello, world" -and file hello.txt matches regex "hello, .*" -and file hello.txt matches regex /hello, .*/ -~~~ - -# Directories - -There are also a large number of directory based steps and some directory -based behaviour available in creating files which are available in the files -library. - -```scenario -given a directory first -then directory first exists -and directory first is empty -and directory second does not exist -when I remove directory first -then directory first does not exist -when I create directory second -then directory second exists -and directory second is empty -given file second/third/hello.txt from hello.txt -then directory second is not empty -and directory second/third exists -and directory second/third is not empty -when I remove directory second -then directory second does not exist -``` - ---- -title: Acceptance criteria for the files Subplot library -author: The Subplot project -bindings: -- lib/files.yaml -impls: - python: - - lib/files.py -... diff --git a/tests/python/runcmd.md b/tests/python/runcmd.md deleted file mode 100644 index 01e6904..0000000 --- a/tests/python/runcmd.md +++ /dev/null @@ -1,214 +0,0 @@ -# Introduction - -The [Subplot][] library `runcmd` for Python provides scenario steps -and their implementations for running Unix commands and examining the -results. The library consists of a bindings file `lib/runcmd.yaml` and -implementations in Python in `lib/runcmd.py`. There is no Bash -version. - -[Subplot]: https://subplot.liw.fi/ - -This document explains the acceptance criteria for the library and how -they're verified. It uses the steps and functions from the -`lib/runcmd` library. The scenarios all have the same structure: run a -command, then examine the exit code, standard output (stdout for -short), or standard error output (stderr) of the command. - -The scenarios use the Unix commands `true` and `false` to -generate exit codes, and `echo` to produce stdout. To generate -stderr, they use the little helper script below. - -~~~{#err.sh .file .sh .numberLines} -#!/bin/sh -echo "$@" 1>&2 -~~~ - -# Check exit code - -These scenarios verify the exit code. To make it easier to write -scenarios in language that flows more naturally, there are a couple of -variations. - -## Successful execution - -~~~scenario -when I run true -then exit code is 0 -and command is successful -~~~ - -## Successful execution in a sub-directory - -~~~scenario -given a directory xyzzy -when I run, in xyzzy, pwd -then exit code is 0 -then command is successful -then stdout contains "/xyzzy" -~~~ - -## Failed execution - -~~~scenario -when I try to run false -then exit code is not 0 -and command fails -~~~ - -## Failed execution in a sub-directory - -~~~scenario -given a directory xyzzy -when I try to run, in xyzzy, false -then exit code is not 0 -and command fails -~~~ - -# Check we can prepend to $PATH - -This scenario verifies that we can add a directory to the beginning of -the PATH environment variable, so that we can have `runcmd` invoke a -binary from our build tree rather than from system directories. This -is especially useful for testing new versions of software that's -already installed on the system. - -~~~scenario -given executable script ls from ls.sh -when I prepend . to PATH -when I run ls -then command is successful -then stdout contains "custom ls, not system ls" -~~~ - -~~~{#ls.sh .file .sh .numberLines} -#!/bin/sh -echo "custom ls, not system ls" -~~~ - -# Check output has what we want - -These scenarios verify that stdout or stderr do have something we want -to have. - -## Check stdout is exactly as wanted - -Note that the string is surrounded by double quotes to make it clear -to the reader what's inside. Also, C-style string escapes are -understood. - -~~~scenario -when I run echo hello, world -then stdout is exactly "hello, world\n" -~~~ - -## Check stderr is exactly as wanted - -~~~scenario -given helper script err.sh for runcmd -when I run sh err.sh hello, world -then stderr is exactly "hello, world\n" -~~~ - -## Check stdout using sub-string search - -Exact string comparisons are not always enough, so we can verify a -sub-string is in output. - -~~~scenario -when I run echo hello, world -then stdout contains "world\n" -and exit code is 0 -~~~ - -## Check stderr using sub-string search - -~~~scenario -given helper script err.sh for runcmd -when I run sh err.sh hello, world -then stderr contains "world\n" -~~~ - -## Check stdout using regular expressions - -Fixed strings are not always enough, so we can verify output matches a -regular expression. Note that the regular expression is not delimited -and does not get any C-style string escaped decoded. - -~~~scenario -when I run echo hello, world -then stdout matches regex world$ -~~~ - -## Check stderr using regular expressions - -~~~scenario -given helper script err.sh for runcmd -when I run sh err.sh hello, world -then stderr matches regex world$ -~~~ - -# Check output doesn't have what we want to avoid - -These scenarios verify that the stdout or stderr do not -have something we want to avoid. - -## Check stdout is not exactly something - -~~~scenario -when I run echo hi -then stdout isn't exactly "hello, world\n" -~~~ - -## Check stderr is not exactly something - -~~~scenario -given helper script err.sh for runcmd -when I run sh err.sh hi -then stderr isn't exactly "hello, world\n" -~~~ - -## Check stdout doesn't contain sub-string - -~~~scenario -when I run echo hi -then stdout doesn't contain "world" -~~~ - -## Check stderr doesn't contain sub-string - -~~~scenario -given helper script err.sh for runcmd -when I run sh err.sh hi -then stderr doesn't contain "world" -~~~ - -## Check stdout doesn't match regular expression - -~~~scenario -when I run echo hi -then stdout doesn't match regex world$ - -~~~ - -## Check stderr doesn't match regular expressions - -~~~scenario -given helper script err.sh for runcmd -when I run sh err.sh hi -then stderr doesn't match regex world$ -~~~ - - ---- -title: Acceptance criteria for the lib/runcmd Subplot library -author: The Subplot project -bindings: -- lib/runcmd.yaml -- runcmd_test.yaml -- lib/files.yaml -impls: - python: - - lib/runcmd.py - - runcmd_test.py - - lib/files.py -... diff --git a/tests/python/runcmd_test.py b/tests/python/runcmd_test.py deleted file mode 100644 index 4aa5f49..0000000 --- a/tests/python/runcmd_test.py +++ /dev/null @@ -1,15 +0,0 @@ -import os - - -def create_script_from_embedded(ctx, filename=None, embedded=None): - files_create_from_embedded_with_other_name = globals()[ - "files_create_from_embedded_with_other_name" - ] - - # Create the file. - files_create_from_embedded_with_other_name( - ctx, filename_on_disk=filename, embedded_file=embedded - ) - - # Make the new file executable. - os.chmod(filename, 0o755) diff --git a/tests/python/runcmd_test.yaml b/tests/python/runcmd_test.yaml deleted file mode 100644 index 2ad981e..0000000 --- a/tests/python/runcmd_test.yaml +++ /dev/null @@ -1,9 +0,0 @@ -- given: "executable script {filename} from {embedded}" - impl: - python: - function: create_script_from_embedded - -- when: "I prepend {dirname} to PATH" - impl: - python: - function: runcmd_prepend_to_path diff --git a/tests/subplots/common/files.md b/tests/subplots/common/files.md new file mode 100644 index 0000000..13d9874 --- /dev/null +++ b/tests/subplots/common/files.md @@ -0,0 +1,107 @@ +# Introduction + +The [Subplot][] library `files` provides scenario steps and their +implementations for managing files on the file system during tests. +The library consists of a bindings file `lib/files.yaml` and +implementations in Python in `lib/files.py` or in Rust within the +`subplotlib` crate. + +[Subplot]: https://subplot.liw.fi/ + +This document explains the acceptance criteria for the library and how +they're verified. It uses the steps and functions from the `files` +library. + +# Create on-disk files from embedded files + +Subplot allows the source document to embed test files, and the +`files` library provides steps to create real, on-disk files from +the embedded files. + +~~~scenario +given file hello.txt +then file hello.txt exists +and file hello.txt contains "hello, world" +and file other.txt does not exist +given file other.txt from hello.txt +then file other.txt exists +and files hello.txt and other.txt match +and only files hello.txt, other.txt exist +~~~ + +~~~{#hello.txt .file .numberLines} +hello, world +~~~ + + +# File metadata + +These steps create files and manage their metadata. + +~~~scenario +given file hello.txt +when I remember metadata for file hello.txt +then file hello.txt has same metadata as before + +when I write "yo" to file hello.txt +then file hello.txt has different metadata from before +~~~ + +# File modification time + +These steps manipulate and test file modification times. + +~~~scenario +given file foo.dat has modification time 1970-01-02 03:04:05 +then file foo.dat has a very old modification time + +when I touch file foo.dat +then file foo.dat has a very recent modification time +~~~ + + +# File contents + +These steps verify contents of files. + +~~~scenario +given file hello.txt +then file hello.txt contains "hello, world" +and file hello.txt matches regex "hello, .*" +and file hello.txt matches regex /hello, .*/ +~~~ + +# Directories + +There are also a large number of directory based steps and some directory +based behaviour available in creating files which are available in the files +library. + +```scenario +given a directory first +then directory first exists +and directory first is empty +and directory second does not exist +when I remove directory first +then directory first does not exist +when I create directory second +then directory second exists +and directory second is empty +given file second/third/hello.txt from hello.txt +then directory second is not empty +and directory second/third exists +and directory second/third is not empty +when I remove directory second +then directory second does not exist +``` + +--- +title: Acceptance criteria for the files Subplot library +author: The Subplot project +bindings: +- lib/files.yaml +impls: + python: + - lib/files.py + rust: [] +... diff --git a/tests/subplots/python/daemon.md b/tests/subplots/python/daemon.md new file mode 100644 index 0000000..51c77b4 --- /dev/null +++ b/tests/subplots/python/daemon.md @@ -0,0 +1,189 @@ +# Introduction + +The [Subplot][] library `daemon` for Python provides scenario steps +and their implementations for running a background process and +terminating at the end of the scenario. + +[Subplot]: https://subplot.liw.fi/ + +This document explains the acceptance criteria for the library and how +they're verified. It uses the steps and functions from the +`lib/daemon` library. The scenarios all have the same structure: run a +command, then examine the exit code, verify the process is running. + +# Daemon is started and terminated + +This scenario starts a background process, verifies it's started, and +verifies it's terminated after the scenario ends. + +~~~scenario +given there is no "sleep 12765" process +when I start "sleep 12765" as a background process as sleepyhead +then a process "sleep 12765" is running +when I stop background process sleepyhead +then there is no "sleep 12765" process +~~~ + + +# Daemon takes a while to open its port + +This scenario verifies that if the background process doesn't immediately start +listening on its port, the daemon library handles that correctly. We do this +with a helper script that waits 2 seconds before opening the port. The +lib/daemon code will wait for the script by repeatedly trying to connect. Once +successful, it immediately closes the port, which causes the script to +terminate. + +~~~scenario +given a daemon helper shell script slow-start-daemon.py +given there is no "slow-start-daemon.py" process +when I try to start "./slow-start-daemon.py" as slow-daemon, on port 8888 +then starting the daemon succeeds +when I stop background process slow-daemon +then there is no "slow-start-daemon.py" process +~~~ + +~~~{#slow-start-daemon.py .file .python .numberLines} +#!/usr/bin/env python3 + +import socket +import time + +time.sleep(2) + +s = socket.socket() +s.bind(("127.0.0.1", 8888)) +s.listen() + +(conn, _) = s.accept() +conn.recv(1) +s.close() + +print("OK") +~~~ + +# Daemon never opens the intended port + +This scenario verifies that if the background process never starts +listening on its port, the daemon library handles that correctly. + +~~~scenario +given there is no "sleep 12765" process +when I try to start "sleep 12765" as sleepyhead, on port 8888 +then starting daemon fails with "ConnectionRefusedError" +then a process "sleep 12765" is running +when I stop background process sleepyhead +then there is no "sleep 12765" process +~~~ + + +# Daemon stdout and stderr are retrievable + +Sometimes it's useful for the step functions to be able to retrieve +the stdout or stderr of of the daemon, after it's started, or even +after it's terminated. This scenario verifies that `lib/daemon` can do +that. + +~~~scenario +given a daemon helper shell script chatty-daemon.sh +given there is no "chatty-daemon" process +when I start "./chatty-daemon.sh" as a background process as chatty-daemon +when daemon chatty-daemon has produced output +when I stop background process chatty-daemon +then there is no "chatty-daemon" process +then daemon chatty-daemon stdout is "hi there\n" +then daemon chatty-daemon stderr is "hola\n" +~~~ + +We make for the daemon to exit, to work around a race condition: if +the test program retrieves the daemon's output too fast, it may not +have had time to produce it yet. + + +~~~{#chatty-daemon.sh .file .sh .numberLines} +#!/usr/bin/env bash + +set -euo pipefail + +trap 'exit 0' TERM + +echo hola 1>&2 +echo hi there +~~~ + +# Can specify additional environment variables for daemon + +Some daemons are configured through their environment rather than configuration +files. This scenario verifies that a step can set arbitrary variables in the +daemon's environment. + +~~~scenario +when I start "/usr/bin/env" as a background process as env, with environment {"custom_variable": "has a Value"} +when daemon env has produced output +when I stop background process env +then daemon env stdout contains "custom_variable=has a Value" +~~~ + +~~~scenario +given a daemon helper shell script env-with-port.py +when I try to start "./env-with-port.py 8765" as env-with-port, on port 8765, with environment {"custom_variable": "1337"} +when I stop background process env-with-port +then daemon env-with-port stdout contains "custom_variable=1337" +~~~ + +~~~scenario +given a daemon helper shell script env-with-port.py +when I start "./env-with-port.py 8766" as a background process as another-env-with-port, on port 8766, with environment {"subplot2": "000"} +when daemon another-env-with-port has produced output +when I stop background process another-env-with-port +then daemon another-env-with-port stdout contains "subplot2=000" +~~~ + +It's important that these new environment variables are not inherited by the +steps that follow. To verify that, we run one more scenario which *doesn't* set +any variables, but checks that none of the variables we mentioned above are +present. + +~~~scenario +when I start "/usr/bin/env" as a background process as env2 +when daemon env2 has produced output +when I stop background process env2 +then daemon env2 stdout doesn't contain "custom_variable=has a Value" +then daemon env2 stdout doesn't contain "custom_variable=1337" +then daemon env2 stdout doesn't contain "subplot2=000" +~~~ + +~~~{#env-with-port.py .file .python .numberLines} +#!/usr/bin/env python3 + +import os +import socket +import sys +import time + +for (key, value) in os.environ.items(): + print(f"{key}={value}") + +port = int(sys.argv[1]) +print(f"port is {port}") + +s = socket.socket() +s.bind(("127.0.0.1", port)) +s.listen() + +(conn, _) = s.accept() +conn.recv(1) +s.close() +~~~ + + +--- +title: Acceptance criteria for the lib/daemon Subplot library +author: The Subplot project +bindings: +- lib/daemon.yaml +impls: + python: + - lib/daemon.py + - lib/runcmd.py +... diff --git a/tests/subplots/python/runcmd.md b/tests/subplots/python/runcmd.md new file mode 100644 index 0000000..01e6904 --- /dev/null +++ b/tests/subplots/python/runcmd.md @@ -0,0 +1,214 @@ +# Introduction + +The [Subplot][] library `runcmd` for Python provides scenario steps +and their implementations for running Unix commands and examining the +results. The library consists of a bindings file `lib/runcmd.yaml` and +implementations in Python in `lib/runcmd.py`. There is no Bash +version. + +[Subplot]: https://subplot.liw.fi/ + +This document explains the acceptance criteria for the library and how +they're verified. It uses the steps and functions from the +`lib/runcmd` library. The scenarios all have the same structure: run a +command, then examine the exit code, standard output (stdout for +short), or standard error output (stderr) of the command. + +The scenarios use the Unix commands `true` and `false` to +generate exit codes, and `echo` to produce stdout. To generate +stderr, they use the little helper script below. + +~~~{#err.sh .file .sh .numberLines} +#!/bin/sh +echo "$@" 1>&2 +~~~ + +# Check exit code + +These scenarios verify the exit code. To make it easier to write +scenarios in language that flows more naturally, there are a couple of +variations. + +## Successful execution + +~~~scenario +when I run true +then exit code is 0 +and command is successful +~~~ + +## Successful execution in a sub-directory + +~~~scenario +given a directory xyzzy +when I run, in xyzzy, pwd +then exit code is 0 +then command is successful +then stdout contains "/xyzzy" +~~~ + +## Failed execution + +~~~scenario +when I try to run false +then exit code is not 0 +and command fails +~~~ + +## Failed execution in a sub-directory + +~~~scenario +given a directory xyzzy +when I try to run, in xyzzy, false +then exit code is not 0 +and command fails +~~~ + +# Check we can prepend to $PATH + +This scenario verifies that we can add a directory to the beginning of +the PATH environment variable, so that we can have `runcmd` invoke a +binary from our build tree rather than from system directories. This +is especially useful for testing new versions of software that's +already installed on the system. + +~~~scenario +given executable script ls from ls.sh +when I prepend . to PATH +when I run ls +then command is successful +then stdout contains "custom ls, not system ls" +~~~ + +~~~{#ls.sh .file .sh .numberLines} +#!/bin/sh +echo "custom ls, not system ls" +~~~ + +# Check output has what we want + +These scenarios verify that stdout or stderr do have something we want +to have. + +## Check stdout is exactly as wanted + +Note that the string is surrounded by double quotes to make it clear +to the reader what's inside. Also, C-style string escapes are +understood. + +~~~scenario +when I run echo hello, world +then stdout is exactly "hello, world\n" +~~~ + +## Check stderr is exactly as wanted + +~~~scenario +given helper script err.sh for runcmd +when I run sh err.sh hello, world +then stderr is exactly "hello, world\n" +~~~ + +## Check stdout using sub-string search + +Exact string comparisons are not always enough, so we can verify a +sub-string is in output. + +~~~scenario +when I run echo hello, world +then stdout contains "world\n" +and exit code is 0 +~~~ + +## Check stderr using sub-string search + +~~~scenario +given helper script err.sh for runcmd +when I run sh err.sh hello, world +then stderr contains "world\n" +~~~ + +## Check stdout using regular expressions + +Fixed strings are not always enough, so we can verify output matches a +regular expression. Note that the regular expression is not delimited +and does not get any C-style string escaped decoded. + +~~~scenario +when I run echo hello, world +then stdout matches regex world$ +~~~ + +## Check stderr using regular expressions + +~~~scenario +given helper script err.sh for runcmd +when I run sh err.sh hello, world +then stderr matches regex world$ +~~~ + +# Check output doesn't have what we want to avoid + +These scenarios verify that the stdout or stderr do not +have something we want to avoid. + +## Check stdout is not exactly something + +~~~scenario +when I run echo hi +then stdout isn't exactly "hello, world\n" +~~~ + +## Check stderr is not exactly something + +~~~scenario +given helper script err.sh for runcmd +when I run sh err.sh hi +then stderr isn't exactly "hello, world\n" +~~~ + +## Check stdout doesn't contain sub-string + +~~~scenario +when I run echo hi +then stdout doesn't contain "world" +~~~ + +## Check stderr doesn't contain sub-string + +~~~scenario +given helper script err.sh for runcmd +when I run sh err.sh hi +then stderr doesn't contain "world" +~~~ + +## Check stdout doesn't match regular expression + +~~~scenario +when I run echo hi +then stdout doesn't match regex world$ + +~~~ + +## Check stderr doesn't match regular expressions + +~~~scenario +given helper script err.sh for runcmd +when I run sh err.sh hi +then stderr doesn't match regex world$ +~~~ + + +--- +title: Acceptance criteria for the lib/runcmd Subplot library +author: The Subplot project +bindings: +- lib/runcmd.yaml +- runcmd_test.yaml +- lib/files.yaml +impls: + python: + - lib/runcmd.py + - runcmd_test.py + - lib/files.py +... diff --git a/tests/subplots/python/runcmd_test.py b/tests/subplots/python/runcmd_test.py new file mode 100644 index 0000000..4aa5f49 --- /dev/null +++ b/tests/subplots/python/runcmd_test.py @@ -0,0 +1,15 @@ +import os + + +def create_script_from_embedded(ctx, filename=None, embedded=None): + files_create_from_embedded_with_other_name = globals()[ + "files_create_from_embedded_with_other_name" + ] + + # Create the file. + files_create_from_embedded_with_other_name( + ctx, filename_on_disk=filename, embedded_file=embedded + ) + + # Make the new file executable. + os.chmod(filename, 0o755) diff --git a/tests/subplots/python/runcmd_test.yaml b/tests/subplots/python/runcmd_test.yaml new file mode 100644 index 0000000..2ad981e --- /dev/null +++ b/tests/subplots/python/runcmd_test.yaml @@ -0,0 +1,9 @@ +- given: "executable script {filename} from {embedded}" + impl: + python: + function: create_script_from_embedded + +- when: "I prepend {dirname} to PATH" + impl: + python: + function: runcmd_prepend_to_path -- cgit v1.2.1 From b9de4d5e810d07b679ad9ab9e46d58e4c76213cc Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sat, 1 Jan 2022 18:09:35 +0000 Subject: tests: Make runcmd.md a common test set now Signed-off-by: Daniel Silverstone --- subplotlib/runcmd.md | 187 ---------------------------- tests/subplots/common/runcmd.md | 216 +++++++++++++++++++++++++++++++++ tests/subplots/common/runcmd_test.py | 15 +++ tests/subplots/common/runcmd_test.rs | 25 ++++ tests/subplots/common/runcmd_test.yaml | 13 ++ tests/subplots/python/runcmd.md | 214 -------------------------------- tests/subplots/python/runcmd_test.py | 15 --- tests/subplots/python/runcmd_test.yaml | 9 -- 8 files changed, 269 insertions(+), 425 deletions(-) delete mode 100644 subplotlib/runcmd.md create mode 100644 tests/subplots/common/runcmd.md create mode 100644 tests/subplots/common/runcmd_test.py create mode 100644 tests/subplots/common/runcmd_test.rs create mode 100644 tests/subplots/common/runcmd_test.yaml delete mode 100644 tests/subplots/python/runcmd.md delete mode 100644 tests/subplots/python/runcmd_test.py delete mode 100644 tests/subplots/python/runcmd_test.yaml diff --git a/subplotlib/runcmd.md b/subplotlib/runcmd.md deleted file mode 100644 index f6b9d85..0000000 --- a/subplotlib/runcmd.md +++ /dev/null @@ -1,187 +0,0 @@ ---- -title: Acceptance criteria for the runcmd step library for subplotlib. -author: The Subplot project -bindings: - - lib/runcmd.yaml - - lib/files.yaml -impls: - rust: [] -... - -# Introduction - -The [Subplot][] step library `runcmd` for Rust provides scenario steps -and their implementations for running Unix commands and examining the -results. The library consists of a bindings file `steplibrary/runcmd.yaml` and -implementations inside the `subplotlib` crate itself. - -[subplot]: https://subplot.liw.fi/ - -This document explains the acceptance criteria for the library and how -they're verified. It uses the steps and functions from the -`steplibrary/runcmd` library. The scenarios all have the same structure: run a -command, then examine the exit code, standard output (stdout for -short), or standard error output (stderr) of the command. - -The scenarios use the Unix commands `true` and `false` to -generate exit codes, and `echo` to produce stdout. To generate -stderr, they use the little helper script below. - -```{#err.sh .file .sh .numberLines} -#!/bin/sh -echo "$@" 1>&2 -``` - -# Check exit code - -These scenarios verify the exit code. To make it easier to write -scenarios in language that flows more naturally, there are a couple of -variations. - -## Successful execution - -```scenario -when I run true -then exit code is 0 -and command is successful -``` - -## Successful execution in a sub-directory - -```scenario -given a directory xyzzy -when I run, in xyzzy, pwd -then exit code is 0 -then command is successful -then stdout contains "/xyzzy" -``` - -## Failed execution - -```scenario -when I try to run false -then exit code is not 0 -and command fails -``` - -## Failed execution in a sub-directory - -```scenario -given a directory xyzzy -when I try to run, in xyzzy, false -then exit code is not 0 -and command fails -``` - -# Check output has what we want - -These scenarios verify that stdout or stderr do have something we want -to have. - -## Check stdout is exactly as wanted - -Note that the string is surrounded by double quotes to make it clear -to the reader what's inside. Also, C-style string escapes are -understood. - -```scenario -when I run echo hello, world -then stdout is exactly "hello, world\n" -``` - -## Check stderr is exactly as wanted - -```scenario -given helper script err.sh for runcmd -when I run sh err.sh hello, world -then stderr is exactly "hello, world\n" -``` - -## Check stdout using sub-string search - -Exact string comparisons are not always enough, so we can verify a -sub-string is in output. - -```scenario -when I run echo hello, world -then stdout contains "world\n" -and exit code is 0 -``` - -## Check stderr using sub-string search - -```scenario -given helper script err.sh for runcmd -when I run sh err.sh hello, world -then stderr contains "world\n" -``` - -## Check stdout using regular expressions - -Fixed strings are not always enough, so we can verify output matches a -regular expression. Note that the regular expression is not delimited -and does not get any C-style string escaped decoded. - -```scenario -when I run echo hello, world -then stdout matches regex world$ -``` - -## Check stderr using regular expressions - -```scenario -given helper script err.sh for runcmd -when I run sh err.sh hello, world -then stderr matches regex world$ -``` - -# Check output doesn't have what we want to avoid - -These scenarios verify that the stdout or stderr do not -have something we want to avoid. - -## Check stdout is not exactly something - -```scenario -when I run echo hi -then stdout isn't exactly "hello, world\n" -``` - -## Check stderr is not exactly something - -```scenario -given helper script err.sh for runcmd -when I run sh err.sh hi -then stderr isn't exactly "hello, world\n" -``` - -## Check stdout doesn't contain sub-string - -```scenario -when I run echo hi -then stdout doesn't contain "world" -``` - -## Check stderr doesn't contain sub-string - -```scenario -given helper script err.sh for runcmd -when I run sh err.sh hi -then stderr doesn't contain "world" -``` - -## Check stdout doesn't match regular expression - -```scenario -when I run echo hi -then stdout doesn't match regex world$ - -``` - -## Check stderr doesn't match regular expressions - -```scenario -given helper script err.sh for runcmd -when I run sh err.sh hi -then stderr doesn't match regex world$ -``` diff --git a/tests/subplots/common/runcmd.md b/tests/subplots/common/runcmd.md new file mode 100644 index 0000000..4f66685 --- /dev/null +++ b/tests/subplots/common/runcmd.md @@ -0,0 +1,216 @@ +# Introduction + +The [Subplot][] library `runcmd` for Python provides scenario steps +and their implementations for running Unix commands and examining the +results. The library consists of a bindings file `lib/runcmd.yaml` and +implementations in Python in `lib/runcmd.py` or in the Rust subplotlib +step library. There is no Bash version. + +[Subplot]: https://subplot.liw.fi/ + +This document explains the acceptance criteria for the library and how +they're verified. It uses the steps and functions from the +`lib/runcmd` library. The scenarios all have the same structure: run a +command, then examine the exit code, standard output (stdout for +short), or standard error output (stderr) of the command. + +The scenarios use the Unix commands `true` and `false` to +generate exit codes, and `echo` to produce stdout. To generate +stderr, they use the little helper script below. + +~~~{#err.sh .file .sh .numberLines} +#!/bin/sh +echo "$@" 1>&2 +~~~ + +# Check exit code + +These scenarios verify the exit code. To make it easier to write +scenarios in language that flows more naturally, there are a couple of +variations. + +## Successful execution + +~~~scenario +when I run true +then exit code is 0 +and command is successful +~~~ + +## Successful execution in a sub-directory + +~~~scenario +given a directory xyzzy +when I run, in xyzzy, pwd +then exit code is 0 +then command is successful +then stdout contains "/xyzzy" +~~~ + +## Failed execution + +~~~scenario +when I try to run false +then exit code is not 0 +and command fails +~~~ + +## Failed execution in a sub-directory + +~~~scenario +given a directory xyzzy +when I try to run, in xyzzy, false +then exit code is not 0 +and command fails +~~~ + +# Check we can prepend to $PATH + +This scenario verifies that we can add a directory to the beginning of +the PATH environment variable, so that we can have `runcmd` invoke a +binary from our build tree rather than from system directories. This +is especially useful for testing new versions of software that's +already installed on the system. + +~~~scenario +given executable script ls from ls.sh +when I prepend . to PATH +when I run ls +then command is successful +then stdout contains "custom ls, not system ls" +~~~ + +~~~{#ls.sh .file .sh .numberLines} +#!/bin/sh +echo "custom ls, not system ls" +~~~ + +# Check output has what we want + +These scenarios verify that stdout or stderr do have something we want +to have. + +## Check stdout is exactly as wanted + +Note that the string is surrounded by double quotes to make it clear +to the reader what's inside. Also, C-style string escapes are +understood. + +~~~scenario +when I run echo hello, world +then stdout is exactly "hello, world\n" +~~~ + +## Check stderr is exactly as wanted + +~~~scenario +given helper script err.sh for runcmd +when I run sh err.sh hello, world +then stderr is exactly "hello, world\n" +~~~ + +## Check stdout using sub-string search + +Exact string comparisons are not always enough, so we can verify a +sub-string is in output. + +~~~scenario +when I run echo hello, world +then stdout contains "world\n" +and exit code is 0 +~~~ + +## Check stderr using sub-string search + +~~~scenario +given helper script err.sh for runcmd +when I run sh err.sh hello, world +then stderr contains "world\n" +~~~ + +## Check stdout using regular expressions + +Fixed strings are not always enough, so we can verify output matches a +regular expression. Note that the regular expression is not delimited +and does not get any C-style string escaped decoded. + +~~~scenario +when I run echo hello, world +then stdout matches regex world$ +~~~ + +## Check stderr using regular expressions + +~~~scenario +given helper script err.sh for runcmd +when I run sh err.sh hello, world +then stderr matches regex world$ +~~~ + +# Check output doesn't have what we want to avoid + +These scenarios verify that the stdout or stderr do not +have something we want to avoid. + +## Check stdout is not exactly something + +~~~scenario +when I run echo hi +then stdout isn't exactly "hello, world\n" +~~~ + +## Check stderr is not exactly something + +~~~scenario +given helper script err.sh for runcmd +when I run sh err.sh hi +then stderr isn't exactly "hello, world\n" +~~~ + +## Check stdout doesn't contain sub-string + +~~~scenario +when I run echo hi +then stdout doesn't contain "world" +~~~ + +## Check stderr doesn't contain sub-string + +~~~scenario +given helper script err.sh for runcmd +when I run sh err.sh hi +then stderr doesn't contain "world" +~~~ + +## Check stdout doesn't match regular expression + +~~~scenario +when I run echo hi +then stdout doesn't match regex world$ + +~~~ + +## Check stderr doesn't match regular expressions + +~~~scenario +given helper script err.sh for runcmd +when I run sh err.sh hi +then stderr doesn't match regex world$ +~~~ + + +--- +title: Acceptance criteria for the lib/runcmd Subplot library +author: The Subplot project +bindings: +- lib/runcmd.yaml +- runcmd_test.yaml +- lib/files.yaml +impls: + python: + - lib/runcmd.py + - runcmd_test.py + - lib/files.py + rust: + - runcmd_test.rs +... diff --git a/tests/subplots/common/runcmd_test.py b/tests/subplots/common/runcmd_test.py new file mode 100644 index 0000000..4aa5f49 --- /dev/null +++ b/tests/subplots/common/runcmd_test.py @@ -0,0 +1,15 @@ +import os + + +def create_script_from_embedded(ctx, filename=None, embedded=None): + files_create_from_embedded_with_other_name = globals()[ + "files_create_from_embedded_with_other_name" + ] + + # Create the file. + files_create_from_embedded_with_other_name( + ctx, filename_on_disk=filename, embedded_file=embedded + ) + + # Make the new file executable. + os.chmod(filename, 0o755) diff --git a/tests/subplots/common/runcmd_test.rs b/tests/subplots/common/runcmd_test.rs new file mode 100644 index 0000000..7759e5f --- /dev/null +++ b/tests/subplots/common/runcmd_test.rs @@ -0,0 +1,25 @@ +use subplotlib::steplibrary::files::{self, Datadir}; +use subplotlib::steplibrary::runcmd::Runcmd; + +#[cfg(unix)] +use std::os::unix::fs::PermissionsExt; + +#[step] +#[context(Datadir)] +fn create_script_from_embedded( + context: &ScenarioContext, + filename: &str, + embedded: SubplotDataFile, +) { + files::create_from_embedded_with_other_name::call(context, filename, embedded)?; + let filename = context.with(|dd: &Datadir| dd.canonicalise_filename(filename), false)?; + let mut perms = std::fs::symlink_metadata(&filename)?.permissions(); + #[cfg(unix)] + perms.set_mode(perms.mode() | 0o111); + std::fs::set_permissions(&filename, perms)?; +} + +#[step] +fn prepend_to_path(context: &mut Runcmd, dirname: &str) { + context.prepend_to_path(dirname); +} diff --git a/tests/subplots/common/runcmd_test.yaml b/tests/subplots/common/runcmd_test.yaml new file mode 100644 index 0000000..daab202 --- /dev/null +++ b/tests/subplots/common/runcmd_test.yaml @@ -0,0 +1,13 @@ +- given: "executable script {filename} from {embedded:file}" + impl: + python: + function: create_script_from_embedded + rust: + function: create_script_from_embedded + +- when: "I prepend {dirname} to PATH" + impl: + python: + function: runcmd_prepend_to_path + rust: + function: prepend_to_path diff --git a/tests/subplots/python/runcmd.md b/tests/subplots/python/runcmd.md deleted file mode 100644 index 01e6904..0000000 --- a/tests/subplots/python/runcmd.md +++ /dev/null @@ -1,214 +0,0 @@ -# Introduction - -The [Subplot][] library `runcmd` for Python provides scenario steps -and their implementations for running Unix commands and examining the -results. The library consists of a bindings file `lib/runcmd.yaml` and -implementations in Python in `lib/runcmd.py`. There is no Bash -version. - -[Subplot]: https://subplot.liw.fi/ - -This document explains the acceptance criteria for the library and how -they're verified. It uses the steps and functions from the -`lib/runcmd` library. The scenarios all have the same structure: run a -command, then examine the exit code, standard output (stdout for -short), or standard error output (stderr) of the command. - -The scenarios use the Unix commands `true` and `false` to -generate exit codes, and `echo` to produce stdout. To generate -stderr, they use the little helper script below. - -~~~{#err.sh .file .sh .numberLines} -#!/bin/sh -echo "$@" 1>&2 -~~~ - -# Check exit code - -These scenarios verify the exit code. To make it easier to write -scenarios in language that flows more naturally, there are a couple of -variations. - -## Successful execution - -~~~scenario -when I run true -then exit code is 0 -and command is successful -~~~ - -## Successful execution in a sub-directory - -~~~scenario -given a directory xyzzy -when I run, in xyzzy, pwd -then exit code is 0 -then command is successful -then stdout contains "/xyzzy" -~~~ - -## Failed execution - -~~~scenario -when I try to run false -then exit code is not 0 -and command fails -~~~ - -## Failed execution in a sub-directory - -~~~scenario -given a directory xyzzy -when I try to run, in xyzzy, false -then exit code is not 0 -and command fails -~~~ - -# Check we can prepend to $PATH - -This scenario verifies that we can add a directory to the beginning of -the PATH environment variable, so that we can have `runcmd` invoke a -binary from our build tree rather than from system directories. This -is especially useful for testing new versions of software that's -already installed on the system. - -~~~scenario -given executable script ls from ls.sh -when I prepend . to PATH -when I run ls -then command is successful -then stdout contains "custom ls, not system ls" -~~~ - -~~~{#ls.sh .file .sh .numberLines} -#!/bin/sh -echo "custom ls, not system ls" -~~~ - -# Check output has what we want - -These scenarios verify that stdout or stderr do have something we want -to have. - -## Check stdout is exactly as wanted - -Note that the string is surrounded by double quotes to make it clear -to the reader what's inside. Also, C-style string escapes are -understood. - -~~~scenario -when I run echo hello, world -then stdout is exactly "hello, world\n" -~~~ - -## Check stderr is exactly as wanted - -~~~scenario -given helper script err.sh for runcmd -when I run sh err.sh hello, world -then stderr is exactly "hello, world\n" -~~~ - -## Check stdout using sub-string search - -Exact string comparisons are not always enough, so we can verify a -sub-string is in output. - -~~~scenario -when I run echo hello, world -then stdout contains "world\n" -and exit code is 0 -~~~ - -## Check stderr using sub-string search - -~~~scenario -given helper script err.sh for runcmd -when I run sh err.sh hello, world -then stderr contains "world\n" -~~~ - -## Check stdout using regular expressions - -Fixed strings are not always enough, so we can verify output matches a -regular expression. Note that the regular expression is not delimited -and does not get any C-style string escaped decoded. - -~~~scenario -when I run echo hello, world -then stdout matches regex world$ -~~~ - -## Check stderr using regular expressions - -~~~scenario -given helper script err.sh for runcmd -when I run sh err.sh hello, world -then stderr matches regex world$ -~~~ - -# Check output doesn't have what we want to avoid - -These scenarios verify that the stdout or stderr do not -have something we want to avoid. - -## Check stdout is not exactly something - -~~~scenario -when I run echo hi -then stdout isn't exactly "hello, world\n" -~~~ - -## Check stderr is not exactly something - -~~~scenario -given helper script err.sh for runcmd -when I run sh err.sh hi -then stderr isn't exactly "hello, world\n" -~~~ - -## Check stdout doesn't contain sub-string - -~~~scenario -when I run echo hi -then stdout doesn't contain "world" -~~~ - -## Check stderr doesn't contain sub-string - -~~~scenario -given helper script err.sh for runcmd -when I run sh err.sh hi -then stderr doesn't contain "world" -~~~ - -## Check stdout doesn't match regular expression - -~~~scenario -when I run echo hi -then stdout doesn't match regex world$ - -~~~ - -## Check stderr doesn't match regular expressions - -~~~scenario -given helper script err.sh for runcmd -when I run sh err.sh hi -then stderr doesn't match regex world$ -~~~ - - ---- -title: Acceptance criteria for the lib/runcmd Subplot library -author: The Subplot project -bindings: -- lib/runcmd.yaml -- runcmd_test.yaml -- lib/files.yaml -impls: - python: - - lib/runcmd.py - - runcmd_test.py - - lib/files.py -... diff --git a/tests/subplots/python/runcmd_test.py b/tests/subplots/python/runcmd_test.py deleted file mode 100644 index 4aa5f49..0000000 --- a/tests/subplots/python/runcmd_test.py +++ /dev/null @@ -1,15 +0,0 @@ -import os - - -def create_script_from_embedded(ctx, filename=None, embedded=None): - files_create_from_embedded_with_other_name = globals()[ - "files_create_from_embedded_with_other_name" - ] - - # Create the file. - files_create_from_embedded_with_other_name( - ctx, filename_on_disk=filename, embedded_file=embedded - ) - - # Make the new file executable. - os.chmod(filename, 0o755) diff --git a/tests/subplots/python/runcmd_test.yaml b/tests/subplots/python/runcmd_test.yaml deleted file mode 100644 index 2ad981e..0000000 --- a/tests/subplots/python/runcmd_test.yaml +++ /dev/null @@ -1,9 +0,0 @@ -- given: "executable script {filename} from {embedded}" - impl: - python: - function: create_script_from_embedded - -- when: "I prepend {dirname} to PATH" - impl: - python: - function: runcmd_prepend_to_path -- cgit v1.2.1