summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2022-10-22 11:59:50 +0000
committerLars Wirzenius <liw@liw.fi>2022-10-22 11:59:50 +0000
commit1bcbb535490511e0bc4f2826356a16bdd05360b0 (patch)
tree1e26b5616d61e9fc917278012f75d707bbfd97fa
parent990c506d347a6aa09af68bc6de670858cf59f133 (diff)
parent7f52ffc37e1a366f78ba3709f15b09ca22ca98c9 (diff)
downloadsubplot-1bcbb535490511e0bc4f2826356a16bdd05360b0.tar.gz
Merge branch 'filepath-as-path' into 'main'
(derive): Support steps with &Path arguments Closes #288 See merge request subplot/subplot!295
-rw-r--r--share/common/lib/files.yaml68
-rw-r--r--share/common/lib/runcmd.yaml12
-rw-r--r--share/rust/template/macros.rs.tera5
-rw-r--r--src/bindings.rs8
-rw-r--r--subplotlib-derive/src/lib.rs30
-rw-r--r--subplotlib/src/steplibrary/files.rs83
-rw-r--r--subplotlib/src/steplibrary/runcmd.rs12
-rw-r--r--tests/subplots/common/runcmd_test.rs5
-rw-r--r--tests/subplots/common/runcmd_test.yaml7
9 files changed, 170 insertions, 60 deletions
diff --git a/share/common/lib/files.yaml b/share/common/lib/files.yaml
index e4d9b6b..cb39b96 100644
--- a/share/common/lib/files.yaml
+++ b/share/common/lib/files.yaml
@@ -18,15 +18,18 @@
python:
function: files_create_from_embedded_with_other_name
types:
+ filename_on_disk: path
embedded_file: file
-- when: I write "(?P<text>.*)" to file (?P<filename>\S+)
- regex: true
+- when: I write "{text}" to file {filename}
impl:
rust:
function: subplotlib::steplibrary::files::create_from_text
python:
function: files_create_from_text
+ types:
+ filename: path
+ text: text
# Manage directories (distinct from files).
@@ -36,6 +39,8 @@
function: subplotlib::steplibrary::files::make_directory
python:
function: files_make_directory
+ types:
+ path: path
- when: I create directory {path}
impl:
@@ -43,6 +48,8 @@
function: subplotlib::steplibrary::files::make_directory
python:
function: files_make_directory
+ types:
+ path: path
- when: I remove directory {path}
impl:
@@ -50,6 +57,8 @@
function: subplotlib::steplibrary::files::remove_directory
python:
function: files_remove_directory
+ types:
+ path: path
- then: directory {path} exists
impl:
@@ -57,6 +66,8 @@
function: subplotlib::steplibrary::files::path_exists
python:
function: files_directory_exists
+ types:
+ path: path
- then: directory {path} does not exist
impl:
@@ -64,6 +75,8 @@
function: subplotlib::steplibrary::files::path_does_not_exist
python:
function: files_directory_does_not_exist
+ types:
+ path: path
- then: directory {path} is empty
impl:
@@ -71,6 +84,8 @@
function: subplotlib::steplibrary::files::path_is_empty
python:
function: files_directory_is_empty
+ types:
+ path: path
- then: directory {path} is not empty
impl:
@@ -78,6 +93,8 @@
function: subplotlib::steplibrary::files::path_is_not_empty
python:
function: files_directory_is_not_empty
+ types:
+ path: path
# File metadata management and testing.
@@ -89,6 +106,7 @@
python:
function: files_touch_with_timestamp
types:
+ filename: path
mtime: text
- when: I remember metadata for file {filename}
@@ -97,6 +115,8 @@
function: subplotlib::steplibrary::files::remember_metadata
python:
function: files_remember_metadata
+ types:
+ filename: path
- when: I touch file {filename}
impl:
@@ -104,6 +124,8 @@
function: subplotlib::steplibrary::files::touch
python:
function: files_touch
+ types:
+ filename: path
- then: file {filename} has same metadata as before
impl:
@@ -111,6 +133,8 @@
function: subplotlib::steplibrary::files::has_remembered_metadata
python:
function: files_has_remembered_metadata
+ types:
+ filename: path
- then: file {filename} has different metadata from before
impl:
@@ -118,6 +142,8 @@
function: subplotlib::steplibrary::files::has_different_metadata
python:
function: files_has_different_metadata
+ types:
+ filename: path
- then: file {filename} has changed from before
impl:
@@ -125,6 +151,8 @@
function: subplotlib::steplibrary::files::has_different_metadata
python:
function: files_has_different_metadata
+ types:
+ filename: path
- then: file {filename} has a very recent modification time
impl:
@@ -132,6 +160,8 @@
function: subplotlib::steplibrary::files::mtime_is_recent
python:
function: files_mtime_is_recent
+ types:
+ filename: path
- then: file {filename} has a very old modification time
impl:
@@ -139,6 +169,8 @@
function: subplotlib::steplibrary::files::mtime_is_ancient
python:
function: files_mtime_is_ancient
+ types:
+ filename: path
# Testing file existence.
@@ -148,6 +180,8 @@
function: subplotlib::steplibrary::files::file_exists
python:
function: files_file_exists
+ types:
+ filename: path
- then: file {filename} does not exist
impl:
@@ -155,6 +189,8 @@
function: subplotlib::steplibrary::files::file_does_not_exist
python:
function: files_file_does_not_exist
+ types:
+ filename: path
- then: only files (?P<filenames>.+) exist
impl:
@@ -166,37 +202,45 @@
# Tests on file content.
-- then: file (?P<filename>\S+) contains "(?P<data>.*)"
- regex: true
+- then: file {filename} contains "{data}"
impl:
rust:
function: subplotlib::steplibrary::files::file_contains
python:
function: files_file_contains
+ types:
+ filename: path
+ data: text
-- then: file (?P<filename>\S+) doesn't contain "(?P<data>.*)"
- regex: true
+- then: file {filename} doesn't contain "{data}"
impl:
rust:
function: subplotlib::steplibrary::files::file_doesnt_contain
python:
function: files_file_doesnt_contain
+ types:
+ filename: path
+ data: text
-- then: file (?P<filename>\S+) matches regex /(?P<regex>.*)/
- regex: true
+- then: file {filename} matches regex /{regex}/
impl:
rust:
function: subplotlib::steplibrary::files::file_matches_regex
python:
function: files_file_matches_regex
+ types:
+ filename: path
+ regex: text
-- then: file (?P<filename>\S+) matches regex "(?P<regex>.*)"
- regex: true
+- then: file {filename} matches regex "{regex}"
impl:
rust:
function: subplotlib::steplibrary::files::file_matches_regex
python:
function: files_file_matches_regex
+ types:
+ filename: path
+ regex: text
- then: files {filename1} and {filename2} match
impl:
@@ -204,3 +248,7 @@
function: subplotlib::steplibrary::files::file_match
python:
function: files_match
+ types:
+ filename1: path
+ filename2: path
+
diff --git a/share/common/lib/runcmd.yaml b/share/common/lib/runcmd.yaml
index 5847613..7be2c05 100644
--- a/share/common/lib/runcmd.yaml
+++ b/share/common/lib/runcmd.yaml
@@ -23,12 +23,16 @@
rust:
function: subplotlib::steplibrary::runcmd::run
-- when: I run, in {dirname}, {argv0}{args:text}
+- when: I run, in {dirname}, {argv0}{args}
impl:
python:
function: runcmd_step_in
rust:
function: subplotlib::steplibrary::runcmd::run_in
+ types:
+ dirname: path
+ argv0: word
+ args: text
- when: I try to run {argv0}{args:text}
impl:
@@ -37,12 +41,16 @@
rust:
function: subplotlib::steplibrary::runcmd::try_to_run
-- when: I try to run, in {dirname}, {argv0}{args:text}
+- when: I try to run, in {dirname}, {argv0}{args}
impl:
python:
function: runcmd_try_to_run_in
rust:
function: subplotlib::steplibrary::runcmd::try_to_run_in
+ types:
+ dirname: path
+ argv0: word
+ args: text
# Steps to examine exit code of latest command.
diff --git a/share/rust/template/macros.rs.tera b/share/rust/template/macros.rs.tera
index 7a65e0f..104eb23 100644
--- a/share/rust/template/macros.rs.tera
+++ b/share/rust/template/macros.rs.tera
@@ -8,8 +8,7 @@
{% if type in ['number', 'int', 'uint'] %}{{text}}
{%- elif type in ['text', 'word']%}
// "{{text | commentsafe }}"
- &base64_decode("{{text | base64}}"
- )
+ &base64_decode("{{text | base64}}")
{%- elif type in ['file'] %}
{
use std::path::PathBuf;
@@ -21,6 +20,8 @@
.expect("Unable to find file at runtime")
.clone()
}
+ {%- elif type in ['path'] %}
+ std::path::PathBuf::from(base64_decode("{{ text | base64 }}"))
{%- else %} /* WOAH unknown type {{step.types[name]}} */ {{text}}
{%- endif %}
)
diff --git a/src/bindings.rs b/src/bindings.rs
index 8c33cc4..8e3531b 100644
--- a/src/bindings.rs
+++ b/src/bindings.rs
@@ -49,6 +49,10 @@ pub enum CaptureType {
/// for one of the embedded files in the document, otherwise codegen will
/// refuse to run.
File,
+
+ /// Paths are sequences of non-whitespace characters which will be interpreted
+ /// as a file-path (e.g. PathBuf) or similar when processed by a step
+ Path,
}
impl FromStr for CaptureType {
@@ -62,6 +66,7 @@ impl FromStr for CaptureType {
"uint" => Ok(Self::Uint),
"number" => Ok(Self::Number),
"file" => Ok(Self::File),
+ "path" => Ok(Self::Path),
_ => Err(SubplotError::UnknownTypeInBinding(value.to_string())),
}
}
@@ -77,6 +82,7 @@ impl CaptureType {
Self::Uint => "uint",
Self::Number => "number",
Self::File => "file",
+ Self::Path => "path",
}
}
@@ -89,6 +95,7 @@ impl CaptureType {
Self::Uint => r"\d+",
Self::Number => r"-?\d+(\.\d+)?",
Self::File => r"\S+",
+ Self::Path => r"\S+",
}
}
}
@@ -787,6 +794,7 @@ lazy_static! {
CaptureType::Uint,
CaptureType::Number,
CaptureType::File,
+ CaptureType::Path,
]).iter().copied() {
// This Unwrap is okay because we shouldn't have any bugs in the
// regular expressions here, and if we did, it'd be bad for everyone
diff --git a/subplotlib-derive/src/lib.rs b/subplotlib-derive/src/lib.rs
index 86f5c5e..7aef0dc 100644
--- a/subplotlib-derive/src/lib.rs
+++ b/subplotlib-derive/src/lib.rs
@@ -29,6 +29,25 @@ fn ty_is_borrow_str(ty: &Type) -> bool {
}
}
+fn ty_is_borrow_path(ty: &Type) -> bool {
+ if let Type::Reference(ty) = ty {
+ if ty.mutability.is_none() && ty.lifetime.is_none() {
+ if let Type::Path(pp) = &*ty.elem {
+ pp.path.is_ident("Path")
+ } else {
+ // not a path, so not &Path
+ false
+ }
+ } else {
+ // mutable, or a lifetime stated, so not &Path
+ false
+ }
+ } else {
+ // Not & so not &Path
+ false
+ }
+}
+
fn ty_is_datafile(ty: &Type) -> bool {
if let Type::Path(ty) = ty {
ty.path.is_ident("SubplotDataFile")
@@ -242,6 +261,8 @@ fn process_step(mut input: ItemFn) -> proc_macro2::TokenStream {
.map(|(id, ty)| {
let ty = if ty_is_borrow_str(ty) {
parse_quote!(::std::string::String)
+ } else if ty_is_borrow_path(ty) {
+ parse_quote!(::std::path::PathBuf)
} else {
ty.clone()
};
@@ -278,6 +299,13 @@ fn process_step(mut input: ItemFn) -> proc_macro2::TokenStream {
self
}
}
+ } else if ty_is_borrow_path(ty) {
+ quote! {
+ pub fn #id<P: Into<std::path::PathBuf>>(mut self, value: P) -> Self {
+ self.#id = value.into();
+ self
+ }
+ }
} else {
quote! {
pub fn #id(mut self, value: #ty) -> Self {
@@ -292,7 +320,7 @@ fn process_step(mut input: ItemFn) -> proc_macro2::TokenStream {
let buildargs: Vec<_> = fields
.iter()
.map(|(id, ty)| {
- if ty_is_borrow_str(ty) {
+ if ty_is_borrow_str(ty) || ty_is_borrow_path(ty) {
quote! {
&self.#id
}
diff --git a/subplotlib/src/steplibrary/files.rs b/subplotlib/src/steplibrary/files.rs
index 09cde56..8abe546 100644
--- a/subplotlib/src/steplibrary/files.rs
+++ b/subplotlib/src/steplibrary/files.rs
@@ -7,7 +7,7 @@ use std::collections::{HashMap, HashSet};
use std::ffi::OsString;
use std::fs::{self, Metadata, OpenOptions};
use std::io::{self, Write};
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime};
use filetime::FileTime;
@@ -31,7 +31,7 @@ pub use super::datadir::Datadir;
/// Because files can typically only be named in Subplot documents, we assume they
/// all have names which can be rendered as utf-8 strings.
pub struct Files {
- metadata: HashMap<String, Metadata>,
+ metadata: HashMap<PathBuf, Metadata>,
}
impl ContextElement for Files {
@@ -50,7 +50,7 @@ impl ContextElement for Files {
#[step]
#[context(Datadir)]
pub fn create_from_embedded(context: &ScenarioContext, embedded_file: SubplotDataFile) {
- let filename_on_disk = format!("{}", embedded_file.name().display());
+ let filename_on_disk = PathBuf::from(format!("{}", embedded_file.name().display()));
create_from_embedded_with_other_name::call(context, &filename_on_disk, embedded_file)?;
}
@@ -63,7 +63,7 @@ pub fn create_from_embedded(context: &ScenarioContext, embedded_file: SubplotDat
#[step]
pub fn create_from_embedded_with_other_name(
context: &Datadir,
- filename_on_disk: &str,
+ filename_on_disk: &Path,
embedded_file: SubplotDataFile,
) {
let filename_on_disk = PathBuf::from(filename_on_disk);
@@ -86,7 +86,7 @@ pub fn create_from_embedded_with_other_name(
/// Sets the modification time for the given filename to the provided mtime.
/// If the file does not exist, it will be created.
#[step]
-pub fn touch_with_timestamp(context: &Datadir, filename: &str, mtime: &str) {
+pub fn touch_with_timestamp(context: &Datadir, filename: &Path, mtime: &str) {
let fd = format_description!(
"[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour]:[offset_minute]"
);
@@ -112,7 +112,7 @@ pub fn touch_with_timestamp(context: &Datadir, filename: &str, mtime: &str) {
///
/// Create/replace the given file with the given content.
#[step]
-pub fn create_from_text(context: &Datadir, text: &str, filename: &str) {
+pub fn create_from_text(context: &Datadir, text: &str, filename: &Path) {
context.open_write(filename)?.write_all(text.as_bytes())?;
}
@@ -125,7 +125,7 @@ pub fn create_from_text(context: &Datadir, text: &str, filename: &str) {
#[step]
#[context(Datadir)]
#[context(Files)]
-pub fn remember_metadata(context: &ScenarioContext, filename: &str) {
+pub fn remember_metadata(context: &ScenarioContext, filename: &Path) {
let full_path = context.with(
|context: &Datadir| context.canonicalise_filename(filename),
false,
@@ -147,7 +147,7 @@ pub fn remember_metadata(context: &ScenarioContext, filename: &str) {
/// This will create the named file if it does not exist, and then it will ensure that the
/// file's modification time is set to the current time.
#[step]
-pub fn touch(context: &Datadir, filename: &str) {
+pub fn touch(context: &Datadir, filename: &Path) {
let full_path = context.canonicalise_filename(filename)?;
let now = FileTime::now();
// If the file doesn't exist, create it
@@ -167,13 +167,13 @@ pub fn touch(context: &Datadir, filename: &str) {
///
/// This simple step will succeed if the given filename exists in some sense.
#[step]
-pub fn file_exists(context: &Datadir, filename: &str) {
+pub fn file_exists(context: &Datadir, filename: &Path) {
let full_path = context.canonicalise_filename(filename)?;
match fs::metadata(full_path) {
Ok(_) => (),
Err(e) => {
if matches!(e.kind(), io::ErrorKind::NotFound) {
- throw!(format!("file '{}' was not found", filename))
+ throw!(format!("file '{}' was not found", filename.display()))
} else {
throw!(e);
}
@@ -187,11 +187,14 @@ pub fn file_exists(context: &Datadir, filename: &str) {
///
/// This simple step will succeed if the given filename does not exist in any sense.
#[step]
-pub fn file_does_not_exist(context: &Datadir, filename: &str) {
+pub fn file_does_not_exist(context: &Datadir, filename: &Path) {
let full_path = context.canonicalise_filename(filename)?;
match fs::metadata(full_path) {
Ok(_) => {
- throw!(format!("file '{}' was unexpectedly found", filename))
+ throw!(format!(
+ "file '{}' was unexpectedly found",
+ filename.display()
+ ))
}
Err(e) => {
if !matches!(e.kind(), io::ErrorKind::NotFound) {
@@ -229,11 +232,11 @@ pub fn only_these_exist(context: &Datadir, filenames: &str) {
/// This will load the content of the named file and ensure it contains the given string.
/// Note: this assumes everything is utf-8 encoded. If not, things will fail.
#[step]
-pub fn file_contains(context: &Datadir, filename: &str, data: &str) {
+pub fn file_contains(context: &Datadir, filename: &Path, data: &str) {
let full_path = context.canonicalise_filename(filename)?;
let body = fs::read_to_string(full_path)?;
if !body.contains(data) {
- println!("file {} contains:\n{}", filename, body);
+ println!("file {} contains:\n{}", filename.display(), body);
throw!("expected file content not found");
}
}
@@ -245,11 +248,11 @@ pub fn file_contains(context: &Datadir, filename: &str, data: &str) {
/// This will load the content of the named file and ensure it lacks the given string.
/// Note: this assumes everything is utf-8 encoded. If not, things will fail.
#[step]
-pub fn file_doesnt_contain(context: &Datadir, filename: &str, data: &str) {
+pub fn file_doesnt_contain(context: &Datadir, filename: &Path, data: &str) {
let full_path = context.canonicalise_filename(filename)?;
let body = fs::read_to_string(full_path)?;
if body.contains(data) {
- println!("file {} contains:\n{}", filename, body);
+ println!("file {} contains:\n{}", filename.display(), body);
throw!("unexpected file content found");
}
}
@@ -262,12 +265,12 @@ pub fn file_doesnt_contain(context: &Datadir, filename: &str, data: &str) {
/// matches the given regular expression. This step will fail if the file is not utf-8
/// encoded, or if the regex fails to compile
#[step]
-pub fn file_matches_regex(context: &Datadir, filename: &str, regex: &str) {
+pub fn file_matches_regex(context: &Datadir, filename: &Path, regex: &str) {
let full_path = context.canonicalise_filename(filename)?;
let regex = Regex::new(regex)?;
let body = fs::read_to_string(full_path)?;
if !regex.is_match(&body) {
- println!("file {} contains:\n{}", filename, body);
+ println!("file {} contains:\n{}", filename.display(), body);
throw!("file content does not match given regex");
}
}
@@ -278,7 +281,7 @@ pub fn file_matches_regex(context: &Datadir, filename: &str, regex: &str) {
///
/// This loads the content of the given two files as **bytes** and checks they mach.
#[step]
-pub fn file_match(context: &Datadir, filename1: &str, filename2: &str) {
+pub fn file_match(context: &Datadir, filename1: &Path, filename2: &Path) {
let full_path1 = context.canonicalise_filename(filename1)?;
let full_path2 = context.canonicalise_filename(filename2)?;
let body1 = fs::read(full_path1)?;
@@ -286,12 +289,12 @@ pub fn file_match(context: &Datadir, filename1: &str, filename2: &str) {
if body1 != body2 {
println!(
"file {} contains:\n{}",
- filename1,
+ filename1.display(),
String::from_utf8_lossy(&body1)
);
println!(
"file {} contains:\n{}",
- filename2,
+ filename2.display(),
String::from_utf8_lossy(&body2)
);
throw!("file contents do not match each other");
@@ -312,7 +315,7 @@ pub fn file_match(context: &Datadir, filename1: &str, filename2: &str) {
#[step]
#[context(Datadir)]
#[context(Files)]
-pub fn has_remembered_metadata(context: &ScenarioContext, filename: &str) {
+pub fn has_remembered_metadata(context: &ScenarioContext, filename: &Path) {
let full_path = context.with(
|context: &Datadir| context.canonicalise_filename(filename),
false,
@@ -327,10 +330,13 @@ pub fn has_remembered_metadata(context: &ScenarioContext, filename: &str) {
|| metadata.len() != remembered.len()
|| metadata.is_file() != remembered.is_file()
{
- throw!(format!("metadata change detected for {}", filename));
+ throw!(format!(
+ "metadata change detected for {}",
+ filename.display()
+ ));
}
} else {
- throw!(format!("no remembered metadata for {}", filename));
+ throw!(format!("no remembered metadata for {}", filename.display()));
}
}
@@ -348,7 +354,7 @@ pub fn has_remembered_metadata(context: &ScenarioContext, filename: &str) {
#[step]
#[context(Datadir)]
#[context(Files)]
-pub fn has_different_metadata(context: &ScenarioContext, filename: &str) {
+pub fn has_different_metadata(context: &ScenarioContext, filename: &Path) {
let full_path = context.with(
|context: &Datadir| context.canonicalise_filename(filename),
false,
@@ -363,10 +369,13 @@ pub fn has_different_metadata(context: &ScenarioContext, filename: &str) {
&& metadata.len() == remembered.len()
&& metadata.is_file() == remembered.is_file()
{
- throw!(format!("metadata change not detected for {}", filename));
+ throw!(format!(
+ "metadata change not detected for {}",
+ filename.display()
+ ));
}
} else {
- throw!(format!("no remembered metadata for {}", filename));
+ throw!(format!("no remembered metadata for {}", filename.display()));
}
}
@@ -376,13 +385,13 @@ pub fn has_different_metadata(context: &ScenarioContext, filename: &str) {
///
/// Specifically this checks that the given file has been modified in the past 5 seconds.
#[step]
-pub fn mtime_is_recent(context: &Datadir, filename: &str) {
+pub fn mtime_is_recent(context: &Datadir, filename: &Path) {
let full_path = context.canonicalise_filename(filename)?;
let metadata = fs::metadata(full_path)?;
let mtime = metadata.modified()?;
let diff = SystemTime::now().duration_since(mtime)?;
if diff > (Duration::from_secs(5)) {
- throw!(format!("{} is older than 5 seconds", filename));
+ throw!(format!("{} is older than 5 seconds", filename.display()));
}
}
@@ -392,13 +401,13 @@ pub fn mtime_is_recent(context: &Datadir, filename: &str) {
///
/// Specifically this checks that the file was modified at least 39 years ago.
#[step]
-pub fn mtime_is_ancient(context: &Datadir, filename: &str) {
+pub fn mtime_is_ancient(context: &Datadir, filename: &Path) {
let full_path = context.canonicalise_filename(filename)?;
let metadata = fs::metadata(full_path)?;
let mtime = metadata.modified()?;
let diff = SystemTime::now().duration_since(mtime)?;
if diff < (Duration::from_secs(39 * 365 * 24 * 3600)) {
- throw!(format!("{} is younger than 39 years", filename));
+ throw!(format!("{} is younger than 39 years", filename.display()));
}
}
@@ -408,7 +417,7 @@ pub fn mtime_is_ancient(context: &Datadir, filename: &str) {
///
/// This is the equivalent of `mkdir -p` within the data directory for the scenario.
#[step]
-pub fn make_directory(context: &Datadir, path: &str) {
+pub fn make_directory(context: &Datadir, path: &Path) {
context.create_dir_all(path)?;
}
@@ -418,7 +427,7 @@ pub fn make_directory(context: &Datadir, path: &str) {
///
/// This is the equivalent of `rm -rf` within the data directory for the scenario.
#[step]
-pub fn remove_directory(context: &Datadir, path: &str) {
+pub fn remove_directory(context: &Datadir, path: &Path) {
let full_path = context.canonicalise_filename(path)?;
remove_dir_all::remove_dir_all(full_path)?;
}
@@ -430,7 +439,7 @@ pub fn remove_directory(context: &Datadir, path: &str) {
/// This ensures that the given path exists in the data directory for the scenario and
/// that it is a directory itself.
#[step]
-pub fn path_exists(context: &Datadir, path: &str) {
+pub fn path_exists(context: &Datadir, path: &Path) {
let full_path = context.canonicalise_filename(path)?;
if !fs::metadata(&full_path)?.is_dir() {
throw!(format!(
@@ -447,7 +456,7 @@ pub fn path_exists(context: &Datadir, path: &str) {
/// This ensures that the given path does not exist in the data directory. If it exists
/// and is not a directory, then this will also fail.
#[step]
-pub fn path_does_not_exist(context: &Datadir, path: &str) {
+pub fn path_does_not_exist(context: &Datadir, path: &Path) {
let full_path = context.canonicalise_filename(path)?;
match fs::metadata(&full_path) {
Ok(_) => throw!(format!("{} exists", full_path.display())),
@@ -466,7 +475,7 @@ pub fn path_does_not_exist(context: &Datadir, path: &str) {
/// This checks that the given path inside the data directory exists and is an
/// empty directory itself.
#[step]
-pub fn path_is_empty(context: &Datadir, path: &str) {
+pub fn path_is_empty(context: &Datadir, path: &Path) {
let full_path = context.canonicalise_filename(path)?;
let mut iter = fs::read_dir(&full_path)?;
match iter.next() {
@@ -484,7 +493,7 @@ pub fn path_is_empty(context: &Datadir, path: &str) {
/// directory itself. The step also asserts that the given directory contains at least
/// one entry.
#[step]
-pub fn path_is_not_empty(context: &Datadir, path: &str) {
+pub fn path_is_not_empty(context: &Datadir, path: &Path) {
let full_path = context.canonicalise_filename(path)?;
let mut iter = fs::read_dir(&full_path)?;
match iter.next() {
diff --git a/subplotlib/src/steplibrary/runcmd.rs b/subplotlib/src/steplibrary/runcmd.rs
index b90f8b7..6692441 100644
--- a/subplotlib/src/steplibrary/runcmd.rs
+++ b/subplotlib/src/steplibrary/runcmd.rs
@@ -56,7 +56,9 @@ static DEFAULT_PATHS: &[&str] = &[
];
// This us used internally to force CWD for running commands
-const USE_CWD: &str = "\0USE_CWD";
+lazy_static! {
+ static ref USE_CWD: PathBuf = PathBuf::from("\0USE_CWD");
+}
impl ContextElement for Runcmd {
fn scenario_starts(&mut self) -> StepResult {
@@ -229,7 +231,7 @@ pub fn run(context: &ScenarioContext, argv0: &str, args: &str) {
#[step]
#[context(Datadir)]
#[context(Runcmd)]
-pub fn run_in(context: &ScenarioContext, dirname: &str, argv0: &str, args: &str) {
+pub fn run_in(context: &ScenarioContext, dirname: &Path, argv0: &str, args: &str) {
try_to_run_in::call(context, dirname, argv0, args)?;
exit_code_is::call(context, 0)?;
}
@@ -244,7 +246,7 @@ pub fn run_in(context: &ScenarioContext, dirname: &str, argv0: &str, args: &str)
#[context(Datadir)]
#[context(Runcmd)]
pub fn try_to_run(context: &ScenarioContext, argv0: &str, args: &str) {
- try_to_run_in::call(context, USE_CWD, argv0, args)?;
+ try_to_run_in::call(context, &USE_CWD, argv0, args)?;
}
/// Run the given command in the given subpath of the data directory
@@ -256,7 +258,7 @@ pub fn try_to_run(context: &ScenarioContext, argv0: &str, args: &str) {
#[step]
#[context(Datadir)]
#[context(Runcmd)]
-pub fn try_to_run_in(context: &ScenarioContext, dirname: &str, argv0: &str, args: &str) {
+pub fn try_to_run_in(context: &ScenarioContext, dirname: &Path, argv0: &str, args: &str) {
// This is the core of runcmd and is how we handle things
let argv0: PathBuf = if argv0.starts_with('.') {
context.with(
@@ -270,7 +272,7 @@ pub fn try_to_run_in(context: &ScenarioContext, dirname: &str, argv0: &str, args
|datadir: &Datadir| Ok(datadir.base_path().to_path_buf()),
false,
)?;
- if dirname != USE_CWD {
+ if dirname != USE_CWD.as_path() {
datadir = datadir.join(dirname);
}
let mut proc = Command::new(&argv0);
diff --git a/tests/subplots/common/runcmd_test.rs b/tests/subplots/common/runcmd_test.rs
index 7759e5f..d3a7ca5 100644
--- a/tests/subplots/common/runcmd_test.rs
+++ b/tests/subplots/common/runcmd_test.rs
@@ -1,3 +1,4 @@
+use std::path::Path;
use subplotlib::steplibrary::files::{self, Datadir};
use subplotlib::steplibrary::runcmd::Runcmd;
@@ -8,7 +9,7 @@ use std::os::unix::fs::PermissionsExt;
#[context(Datadir)]
fn create_script_from_embedded(
context: &ScenarioContext,
- filename: &str,
+ filename: &Path,
embedded: SubplotDataFile,
) {
files::create_from_embedded_with_other_name::call(context, filename, embedded)?;
@@ -20,6 +21,6 @@ fn create_script_from_embedded(
}
#[step]
-fn prepend_to_path(context: &mut Runcmd, dirname: &str) {
+fn prepend_to_path(context: &mut Runcmd, dirname: &Path) {
context.prepend_to_path(dirname);
}
diff --git a/tests/subplots/common/runcmd_test.yaml b/tests/subplots/common/runcmd_test.yaml
index daab202..e53c2d7 100644
--- a/tests/subplots/common/runcmd_test.yaml
+++ b/tests/subplots/common/runcmd_test.yaml
@@ -1,9 +1,12 @@
-- given: "executable script {filename} from {embedded:file}"
+- given: "executable script {filename} from {embedded}"
impl:
python:
function: create_script_from_embedded
rust:
function: create_script_from_embedded
+ types:
+ filename: path
+ embedded: file
- when: "I prepend {dirname} to PATH"
impl:
@@ -11,3 +14,5 @@
function: runcmd_prepend_to_path
rust:
function: prepend_to_path
+ types:
+ dirname: path