summaryrefslogtreecommitdiff
path: root/subplotlib
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2021-12-27 14:29:28 +0000
committerDaniel Silverstone <dsilvers@digital-scurf.org>2022-01-01 10:40:57 +0000
commit0fd065044076888285f42e0105f07a0c924b2819 (patch)
tree307c0a3358f7c57faff9445d2cfc5cacd0b1961b /subplotlib
parentd1f336e141c3533c5e4dda049f03dfe168f99470 (diff)
downloadsubplot-0fd065044076888285f42e0105f07a0c924b2819.tar.gz
subplotlib: Run subplot.md in Rust too
Signed-off-by: Daniel Silverstone <dsilvers@digital-scurf.org>
Diffstat (limited to 'subplotlib')
-rw-r--r--subplotlib/Cargo.toml7
-rw-r--r--subplotlib/build.rs3
-rw-r--r--subplotlib/subplot-rust-support.rs219
-rw-r--r--subplotlib/tests/subplot.rs1
4 files changed, 227 insertions, 3 deletions
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<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
+ );
+}
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"));