Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/snapbox/src/data/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ impl From<Inline> for DataSource {
impl std::fmt::Display for DataSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.inner {
DataSourceInner::Path(value) => crate::dir::display_relpath(value).fmt(f),
DataSourceInner::Path(value) => crate::utils::display_relpath(value).fmt(f),
DataSourceInner::Inline(value) => value.fmt(f),
}
}
Expand Down
104 changes: 0 additions & 104 deletions crates/snapbox/src/dir/fixture.rs
Original file line number Diff line number Diff line change
@@ -1,104 +0,0 @@
/// Working directory for tests
#[derive(Debug)]
pub struct PathFixture(PathFixtureInner);

#[derive(Debug)]
enum PathFixtureInner {
None,
Immutable(std::path::PathBuf),
#[cfg(feature = "dir")]
MutablePath(std::path::PathBuf),
#[cfg(feature = "dir")]
MutableTemp {
temp: tempfile::TempDir,
path: std::path::PathBuf,
},
}

impl PathFixture {
pub fn none() -> Self {
Self(PathFixtureInner::None)
}

pub fn immutable(target: &std::path::Path) -> Self {
Self(PathFixtureInner::Immutable(target.to_owned()))
}

#[cfg(feature = "dir")]
pub fn mutable_temp() -> Result<Self, crate::assert::Error> {
let temp = tempfile::tempdir().map_err(|e| e.to_string())?;
// We need to get the `/private` prefix on Mac so variable substitutions work
// correctly
let path = crate::dir::canonicalize(temp.path())
.map_err(|e| format!("Failed to canonicalize {}: {}", temp.path().display(), e))?;
Ok(Self(PathFixtureInner::MutableTemp { temp, path }))
}

#[cfg(feature = "dir")]
pub fn mutable_at(target: &std::path::Path) -> Result<Self, crate::assert::Error> {
let _ = std::fs::remove_dir_all(target);
std::fs::create_dir_all(target)
.map_err(|e| format!("Failed to create {}: {}", target.display(), e))?;
Ok(Self(PathFixtureInner::MutablePath(target.to_owned())))
}

#[cfg(feature = "dir")]
pub fn with_template(
self,
template_root: &std::path::Path,
) -> Result<Self, crate::assert::Error> {
match &self.0 {
PathFixtureInner::None | PathFixtureInner::Immutable(_) => {
return Err("Sandboxing is disabled".into());
}
PathFixtureInner::MutablePath(path) | PathFixtureInner::MutableTemp { path, .. } => {
crate::debug!(
"Initializing {} from {}",
path.display(),
template_root.display()
);
super::copy_template(template_root, path)?;
}
}

Ok(self)
}

pub fn is_mutable(&self) -> bool {
match &self.0 {
PathFixtureInner::None | PathFixtureInner::Immutable(_) => false,
#[cfg(feature = "dir")]
PathFixtureInner::MutablePath(_) => true,
#[cfg(feature = "dir")]
PathFixtureInner::MutableTemp { .. } => true,
}
}

pub fn path(&self) -> Option<&std::path::Path> {
match &self.0 {
PathFixtureInner::None => None,
PathFixtureInner::Immutable(path) => Some(path.as_path()),
#[cfg(feature = "dir")]
PathFixtureInner::MutablePath(path) => Some(path.as_path()),
#[cfg(feature = "dir")]
PathFixtureInner::MutableTemp { path, .. } => Some(path.as_path()),
}
}

/// Explicitly close to report errors
pub fn close(self) -> Result<(), std::io::Error> {
match self.0 {
PathFixtureInner::None | PathFixtureInner::Immutable(_) => Ok(()),
#[cfg(feature = "dir")]
PathFixtureInner::MutablePath(_) => Ok(()),
#[cfg(feature = "dir")]
PathFixtureInner::MutableTemp { temp, .. } => temp.close(),
}
}
}

impl Default for PathFixture {
fn default() -> Self {
Self::none()
}
}
5 changes: 2 additions & 3 deletions crates/snapbox/src/dir/mod.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
//! Initialize working directories and assert on how they've changed

mod diff;
mod fixture;
mod ops;
mod root;
#[cfg(test)]
mod tests;

pub use diff::FileType;
pub use diff::PathDiff;
pub use fixture::PathFixture;
#[cfg(feature = "dir")]
pub use ops::copy_template;
pub use ops::resolve_dir;
pub use ops::strip_trailing_slash;
#[cfg(feature = "dir")]
pub use ops::Walk;
pub use root::DirRoot;

#[cfg(feature = "dir")]
pub(crate) use ops::canonicalize;
pub(crate) use ops::display_relpath;
pub(crate) use ops::shallow_copy;
17 changes: 2 additions & 15 deletions crates/snapbox/src/dir/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ impl Iterator for Walk {
}
}

/// Copy a template into a [`PathFixture`][super::PathFixture]
/// Copy a template into a [`DirRoot`][super::DirRoot]
///
/// Note: Generally you'll use [`PathFixture::with_template`][super::PathFixture::with_template] instead.
/// Note: Generally you'll use [`DirRoot::with_template`][super::DirRoot::with_template] instead.
///
/// Note: Ignores `.keep` files
#[cfg(feature = "dir")]
Expand Down Expand Up @@ -171,16 +171,3 @@ pub(crate) fn canonicalize(path: &std::path::Path) -> Result<std::path::PathBuf,
pub fn strip_trailing_slash(path: &std::path::Path) -> &std::path::Path {
path.components().as_path()
}

pub(crate) fn display_relpath(path: impl AsRef<std::path::Path>) -> String {
let path = path.as_ref();
let relpath = if let Ok(cwd) = std::env::current_dir() {
match path.strip_prefix(cwd) {
Ok(path) => path,
Err(_) => path,
}
} else {
path
};
relpath.display().to_string()
}
104 changes: 104 additions & 0 deletions crates/snapbox/src/dir/root.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/// Working directory for tests
#[derive(Debug)]
pub struct DirRoot(DirRootInner);

#[derive(Debug)]
enum DirRootInner {
None,
Immutable(std::path::PathBuf),
#[cfg(feature = "dir")]
MutablePath(std::path::PathBuf),
#[cfg(feature = "dir")]
MutableTemp {
temp: tempfile::TempDir,
path: std::path::PathBuf,
},
}

impl DirRoot {
pub fn none() -> Self {
Self(DirRootInner::None)
}

pub fn immutable(target: &std::path::Path) -> Self {
Self(DirRootInner::Immutable(target.to_owned()))
}

#[cfg(feature = "dir")]
pub fn mutable_temp() -> Result<Self, crate::assert::Error> {
let temp = tempfile::tempdir().map_err(|e| e.to_string())?;
// We need to get the `/private` prefix on Mac so variable substitutions work
// correctly
let path = crate::dir::canonicalize(temp.path())
.map_err(|e| format!("Failed to canonicalize {}: {}", temp.path().display(), e))?;
Ok(Self(DirRootInner::MutableTemp { temp, path }))
}

#[cfg(feature = "dir")]
pub fn mutable_at(target: &std::path::Path) -> Result<Self, crate::assert::Error> {
let _ = std::fs::remove_dir_all(target);
std::fs::create_dir_all(target)
.map_err(|e| format!("Failed to create {}: {}", target.display(), e))?;
Ok(Self(DirRootInner::MutablePath(target.to_owned())))
}

#[cfg(feature = "dir")]
pub fn with_template(
self,
template_root: &std::path::Path,
) -> Result<Self, crate::assert::Error> {
match &self.0 {
DirRootInner::None | DirRootInner::Immutable(_) => {
return Err("Sandboxing is disabled".into());
}
DirRootInner::MutablePath(path) | DirRootInner::MutableTemp { path, .. } => {
crate::debug!(
"Initializing {} from {}",
path.display(),
template_root.display()
);
super::copy_template(template_root, path)?;
}
}

Ok(self)
}

pub fn is_mutable(&self) -> bool {
match &self.0 {
DirRootInner::None | DirRootInner::Immutable(_) => false,
#[cfg(feature = "dir")]
DirRootInner::MutablePath(_) => true,
#[cfg(feature = "dir")]
DirRootInner::MutableTemp { .. } => true,
}
}

pub fn path(&self) -> Option<&std::path::Path> {
match &self.0 {
DirRootInner::None => None,
DirRootInner::Immutable(path) => Some(path.as_path()),
#[cfg(feature = "dir")]
DirRootInner::MutablePath(path) => Some(path.as_path()),
#[cfg(feature = "dir")]
DirRootInner::MutableTemp { path, .. } => Some(path.as_path()),
}
}

/// Explicitly close to report errors
pub fn close(self) -> Result<(), std::io::Error> {
match self.0 {
DirRootInner::None | DirRootInner::Immutable(_) => Ok(()),
#[cfg(feature = "dir")]
DirRootInner::MutablePath(_) => Ok(()),
#[cfg(feature = "dir")]
DirRootInner::MutableTemp { temp, .. } => temp.close(),
}
}
}

impl Default for DirRoot {
fn default() -> Self {
Self::none()
}
}
2 changes: 1 addition & 1 deletion crates/snapbox/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
//! [`Output`][std::process::Output].
//!
//! Testing Filesystem Interactions:
//! - [`dir::PathFixture`]: Working directory for tests
//! - [`dir::DirRoot`]: Working directory for tests
//! - [`Assert`]: Diff a directory against files present in a pattern directory
//!
//! You can also build your own version of these with the lower-level building blocks these are
Expand Down
13 changes: 13 additions & 0 deletions crates/snapbox/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,16 @@ pub use crate::cargo_rustc_current_dir;
pub use crate::current_dir;
#[doc(inline)]
pub use crate::current_rs;

pub fn display_relpath(path: impl AsRef<std::path::Path>) -> String {
let path = path.as_ref();
let relpath = if let Ok(cwd) = std::env::current_dir() {
match path.strip_prefix(cwd) {
Ok(path) => path,
Err(_) => path,
}
} else {
path
};
relpath.display().to_string()
}
12 changes: 6 additions & 6 deletions crates/trycmd/src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ impl Case {

#[cfg(feature = "filesystem")]
if let Mode::Dump(_) = mode {
// Handled as part of PathFixture
// Handled as part of DirRoot
} else {
let fixture_root = self.path.with_extension("out");
if fixture_root.exists() {
Expand Down Expand Up @@ -1016,20 +1016,20 @@ fn fs_context(
cwd: Option<&std::path::Path>,
sandbox: bool,
mode: &crate::Mode,
) -> Result<snapbox::dir::PathFixture, crate::Error> {
) -> Result<snapbox::dir::DirRoot, crate::Error> {
if sandbox {
#[cfg(feature = "filesystem")]
match mode {
crate::Mode::Dump(root) => {
let target = root.join(path.with_extension("out").file_name().unwrap());
let mut context = snapbox::dir::PathFixture::mutable_at(&target)?;
let mut context = snapbox::dir::DirRoot::mutable_at(&target)?;
if let Some(cwd) = cwd {
context = context.with_template(cwd)?;
}
Ok(context)
}
crate::Mode::Fail | crate::Mode::Overwrite => {
let mut context = snapbox::dir::PathFixture::mutable_temp()?;
let mut context = snapbox::dir::DirRoot::mutable_temp()?;
if let Some(cwd) = cwd {
context = context.with_template(cwd)?;
}
Expand All @@ -1040,7 +1040,7 @@ fn fs_context(
Err("Sandboxing is disabled".into())
} else {
Ok(cwd
.map(snapbox::dir::PathFixture::immutable)
.unwrap_or_else(snapbox::dir::PathFixture::none))
.map(snapbox::dir::DirRoot::immutable)
.unwrap_or_else(snapbox::dir::DirRoot::none))
}
}