From 6c9876d76052d89af3edccbc66b073b085d9ecdb Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 18 Oct 2018 21:21:44 -0600 Subject: [PATCH 1/7] fix: Re-structure API Working towards figuring out structure (#19). Instead of mixing things together, leaving them in modules to cleanly separate the two distinct but related parts of the API. BREAKING CHANGE: Top-level types are now in `fixture`. --- src/assert.rs | 6 +++--- src/{fs.rs => fixture.rs} | 0 src/lib.rs | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) rename src/{fs.rs => fixture.rs} (100%) diff --git a/src/assert.rs b/src/assert.rs index 61e5f05..73d2f51 100644 --- a/src/assert.rs +++ b/src/assert.rs @@ -31,7 +31,7 @@ use predicates::str::PredicateStrExt; use predicates_core; use predicates_tree::CaseTreeExt; -use fs; +use fixture; /// Assert the state of files within [`TempDir`]. /// @@ -67,7 +67,7 @@ pub trait PathAssert { P: predicates_core::Predicate; } -impl PathAssert for fs::TempDir { +impl PathAssert for fixture::TempDir { fn assert(&self, pred: I) -> &Self where I: IntoPathPredicate

, @@ -78,7 +78,7 @@ impl PathAssert for fs::TempDir { } } -impl PathAssert for fs::ChildPath { +impl PathAssert for fixture::ChildPath { fn assert(&self, pred: I) -> &Self where I: IntoPathPredicate

, diff --git a/src/fs.rs b/src/fixture.rs similarity index 100% rename from src/fs.rs rename to src/fixture.rs diff --git a/src/lib.rs b/src/lib.rs index 89031f5..8095755 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,19 +47,19 @@ extern crate predicates_tree; extern crate tempfile; pub mod assert; -pub use assert::PathAssert; - -mod fs; -pub use fs::*; - +pub mod fixture; pub mod errors; +// Pulling this in for convenience-sake +#[doc(inline)] +pub use fixture::TempDir; + /// Extension traits that are useful to have available. pub mod prelude { pub use assert::PathAssert; - pub use fs::FileTouch; - pub use fs::FileWriteBin; - pub use fs::FileWriteStr; - pub use fs::PathChild; - pub use fs::PathCopy; + pub use fixture::FileTouch; + pub use fixture::FileWriteBin; + pub use fixture::FileWriteStr; + pub use fixture::PathChild; + pub use fixture::PathCopy; } From d04cd8f975f13104e2fd0c7ad6b3cb82c2239701 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 18 Oct 2018 21:24:36 -0600 Subject: [PATCH 2/7] fix: Expose errors where relevant in the API The errors were only fixture related, so moved them there to better help people navigate the API. This is a part of #19. --- src/fixture.rs | 24 +++++++++++------------- src/lib.rs | 2 +- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/fixture.rs b/src/fixture.rs index 49d9877..1718194 100644 --- a/src/fixture.rs +++ b/src/fixture.rs @@ -6,9 +6,7 @@ use std::path; use globwalk; use tempfile; -use errors; -use errors::ResultChainExt; - +pub use errors::*; pub use tempfile::TempDir; /// Access paths within [`TempDir`] for testing. @@ -196,14 +194,14 @@ pub trait PathCopy { /// temp.copy_from(".", &["*.rs"]).unwrap(); /// temp.close().unwrap(); /// ``` - fn copy_from(&self, source: P, patterns: &[S]) -> Result<(), errors::FixtureError> + fn copy_from(&self, source: P, patterns: &[S]) -> Result<(), FixtureError> where P: AsRef, S: AsRef; } impl PathCopy for tempfile::TempDir { - fn copy_from(&self, source: P, patterns: &[S]) -> Result<(), errors::FixtureError> + fn copy_from(&self, source: P, patterns: &[S]) -> Result<(), FixtureError> where P: AsRef, S: AsRef, @@ -213,7 +211,7 @@ impl PathCopy for tempfile::TempDir { } impl PathCopy for ChildPath { - fn copy_from(&self, source: P, patterns: &[S]) -> Result<(), errors::FixtureError> + fn copy_from(&self, source: P, patterns: &[S]) -> Result<(), FixtureError> where P: AsRef, S: AsRef, @@ -241,21 +239,21 @@ fn copy_files( target: &path::Path, source: &path::Path, patterns: &[S], -) -> Result<(), errors::FixtureError> +) -> Result<(), FixtureError> where S: AsRef, { // `walkdir`, on Windows, seems to convert "." into "" which then fails. let source = source .canonicalize() - .chain(errors::FixtureError::new(errors::FixtureKind::Walk))?; + .chain(FixtureError::new(FixtureKind::Walk))?; for entry in globwalk::GlobWalkerBuilder::from_patterns(&source, patterns) .follow_links(true) .build() - .chain(errors::FixtureError::new(errors::FixtureKind::Walk))? + .chain(FixtureError::new(FixtureKind::Walk))? { println!("{:?}", entry); - let entry = entry.chain(errors::FixtureError::new(errors::FixtureKind::Walk))?; + let entry = entry.chain(FixtureError::new(FixtureKind::Walk))?; let rel = entry .path() .strip_prefix(&source) @@ -263,12 +261,12 @@ where let target_path = target.join(rel); if entry.file_type().is_dir() { fs::create_dir_all(target_path) - .chain(errors::FixtureError::new(errors::FixtureKind::CreateDir))?; + .chain(FixtureError::new(FixtureKind::CreateDir))?; } else if entry.file_type().is_file() { fs::create_dir_all(target_path.parent().expect("at least `target` exists")) - .chain(errors::FixtureError::new(errors::FixtureKind::CreateDir))?; + .chain(FixtureError::new(FixtureKind::CreateDir))?; fs::copy(entry.path(), target_path) - .chain(errors::FixtureError::new(errors::FixtureKind::CopyFile))?; + .chain(FixtureError::new(FixtureKind::CopyFile))?; } } Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 8095755..4cc5a29 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,7 +48,7 @@ extern crate tempfile; pub mod assert; pub mod fixture; -pub mod errors; +mod errors; // Pulling this in for convenience-sake #[doc(inline)] From 1c63651fcb918dd4cc7cf926d6ac01dbab159b4d Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 18 Oct 2018 21:24:50 -0600 Subject: [PATCH 3/7] chore: Suppress warning --- src/fixture.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/fixture.rs b/src/fixture.rs index 1718194..dda66e6 100644 --- a/src/fixture.rs +++ b/src/fixture.rs @@ -1,3 +1,5 @@ +//! Helpers for initializing a directory of files to run tests against. + use std::fs; use std::io; use std::io::Write; From e7f174aae24a2e67e5195ffce5f91993e391589f Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 18 Oct 2018 21:33:31 -0600 Subject: [PATCH 4/7] feat(assert): Accept Predicate Fixes #25 --- src/assert.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/assert.rs b/src/assert.rs index 73d2f51..616e0c5 100644 --- a/src/assert.rs +++ b/src/assert.rs @@ -222,6 +222,70 @@ impl IntoPathPredicate for &'static str { } } +// Keep `predicates` concrete Predicates out of our public API. +/// Predicate used by `IntoPathPredicate` for `str` +#[derive(Debug, Clone)] +pub struct StrPathPredicate>( + predicates::path::FileContentPredicate< + predicates::str::Utf8Predicate

, + >, +); + +impl

StrPathPredicate

+where + P: predicates_core::Predicate, +{ + pub(crate) fn new(value: P) -> Self { + let pred = value.from_utf8().from_file_path(); + StrPathPredicate(pred) + } +} + +impl

predicates_core::reflection::PredicateReflection for StrPathPredicate

+where + P: predicates_core::Predicate, +{ + fn parameters<'a>( + &'a self, + ) -> Box> + 'a> { + self.0.parameters() + } + + /// Nested `Predicate`s of the current `Predicate`. + fn children<'a>(&'a self) -> Box> + 'a> { + self.0.children() + } +} + +impl

predicates_core::Predicate for StrPathPredicate

+where + P: predicates_core::Predicate, +{ + fn eval(&self, item: &path::Path) -> bool { + self.0.eval(item) + } +} + +impl

fmt::Display for StrPathPredicate

+where + P: predicates_core::Predicate, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl

IntoPathPredicate> for P +where + P: predicates_core::Predicate, +{ + type Predicate = StrPathPredicate

; + + fn into_path(self) -> Self::Predicate { + Self::Predicate::new(self) + } +} + #[cfg(test)] mod test { use super::*; From 1a603cbe9acb7ce65675e8318c5db09c94e56249 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 19 Oct 2018 07:27:47 -0600 Subject: [PATCH 5/7] docs: Get all examples running --- src/assert.rs | 14 ++++++++++---- src/lib.rs | 5 ++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/assert.rs b/src/assert.rs index 616e0c5..1a8a836 100644 --- a/src/assert.rs +++ b/src/assert.rs @@ -4,8 +4,11 @@ //! //! # Examples //! -//! ```rust,ignore -//! use assert_fs::*; +//! ```rust +//! extern crate assert_fs; +//! extern crate predicates; +//! +//! use assert_fs::prelude::*; //! use predicates::prelude::*; //! //! let temp = assert_fs::TempDir::new().unwrap(); @@ -37,8 +40,11 @@ use fixture; /// /// # Examples /// -/// ```rust,ignore -/// use assert_fs::*; +/// ```rust +/// extern crate assert_fs; +/// extern crate predicates; +/// +/// use assert_fs::prelude::*; /// use predicates::prelude::*; /// /// let temp = assert_fs::TempDir::new().unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 4cc5a29..5956800 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,10 @@ //! //! Here is a trivial example: //! -//! ```rust,ignore +//! ```rust +//! extern crate assert_fs; +//! extern crate predicates; +//! //! use assert_fs::prelude::*; //! use predicates::prelude::*; //! From 8939ce2f0aaf697068cfebf35e06c84b78d117d0 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 25 Oct 2018 20:47:40 -0600 Subject: [PATCH 6/7] docs: Expand coverage Fixes #21 --- src/assert.rs | 138 ++++++++++++++++++++++++++++++++++++++++++++++--- src/fixture.rs | 13 +++-- src/lib.rs | 26 ++++++++-- 3 files changed, 158 insertions(+), 19 deletions(-) diff --git a/src/assert.rs b/src/assert.rs index 1a8a836..057faf7 100644 --- a/src/assert.rs +++ b/src/assert.rs @@ -38,6 +38,14 @@ use fixture; /// Assert the state of files within [`TempDir`]. /// +/// This uses [`IntoPathPredicate`] to provide short-hands for common cases, accepting: +/// - `Predicate` for validating a path. +/// - `Predicate` for validating the content of the file. +/// - `&Path` which must have the same file content. +/// - `&[u8]` or `&str` representing the content of the file. +/// +/// See [`predicates`] for more predicates. +/// /// # Examples /// /// ```rust @@ -59,14 +67,47 @@ use fixture; /// temp.close().unwrap(); /// ``` /// -/// - See [`predicates::prelude`] for more predicates. -/// - See [`IntoPathPredicate`] for other built-in conversions. -/// /// [`TempDir`]: ../struct.TempDir.html -/// [`predicates::prelude`]: https://docs.rs/predicates/0.9.0/predicates/prelude/ +/// [`predicates`]: https://docs.rs/predicates /// [`IntoPathPredicate`]: trait.IntoPathPredicate.html pub trait PathAssert { - /// Wrap with an interface for that provides assertions on the `TempDir`. + /// Assert the state of files within [`TempDir`]. + /// + /// This uses [`IntoPathPredicate`] to provide short-hands for common cases, accepting: + /// - `Predicate` for validating a path. + /// - `Predicate` for validating the content of the file. + /// - `&Path` which must have the same file content. + /// - `&[u8]` or `&str` representing the content of the file. + /// + /// See [`predicates`] for more predicates. + /// + /// # Examples + /// + /// ```rust + /// extern crate assert_fs; + /// extern crate predicates; + /// + /// use assert_fs::prelude::*; + /// use predicates::prelude::*; + /// + /// let temp = assert_fs::TempDir::new().unwrap(); + /// let input_file = temp.child("foo.txt"); + /// input_file.touch().unwrap(); + /// + /// // ... do something with input_file ... + /// + /// input_file.assert(""); + /// // or + /// input_file.assert(predicate::str::is_empty()); + /// + /// temp.child("bar.txt").assert(predicate::path::missing()); + /// + /// temp.close().unwrap(); + /// ``` + /// + /// [`TempDir`]: ../struct.TempDir.html + /// [`predicates`]: https://docs.rs/predicates + /// [`IntoPathPredicate`]: trait.IntoPathPredicate.html fn assert(&self, pred: I) -> &Self where I: IntoPathPredicate

, @@ -108,6 +149,26 @@ where /// Used by [`PathAssert`] to convert Self into the needed [`Predicate`]. /// +/// # Examples +/// +/// ```rust +/// extern crate assert_fs; +/// extern crate predicates; +/// +/// use std::path; +/// +/// use assert_fs::prelude::*; +/// use predicates::prelude::*; +/// +/// let temp = assert_fs::TempDir::new().unwrap(); +/// +/// // ... do something with input_file ... +/// +/// temp.child("bar.txt").assert(predicate::path::missing()); // Uses IntoPathPredicate +/// +/// temp.close().unwrap(); +/// ``` +/// /// [`PathAssert`]: trait.PathAssert.html /// [`Predicate`]: https://docs.rs/predicates-core/0.9.0/predicates_core/trait.Predicate.html pub trait IntoPathPredicate

@@ -133,7 +194,26 @@ where } // Keep `predicates` concrete Predicates out of our public API. -/// Predicate used by `IntoPathPredicate` for bytes +/// [Predicate] used by [`IntoPathPredicate`] for bytes. +/// +/// # Example +/// +/// ```rust +/// use assert_fs::prelude::*; +/// +/// let temp = assert_fs::TempDir::new().unwrap(); +/// let input_file = temp.child("foo.txt"); +/// input_file.touch().unwrap(); +/// +/// // ... do something with input_file ... +/// +/// input_file.assert(b"" as &[u8]); // uses BytesContentPathPredicate +/// +/// temp.close().unwrap(); +/// ``` +/// +/// [`IntoPathPredicate`]: trait.IntoPathPredicate.html +/// [Predicate]: https://docs.rs/predicates-core/1.0.0/predicates_core/trait.Predicate.html #[derive(Debug)] pub struct BytesContentPathPredicate( predicates::path::FileContentPredicate>, @@ -180,7 +260,26 @@ impl IntoPathPredicate for &'static [u8] { } // Keep `predicates` concrete Predicates out of our public API. -/// Predicate used by `IntoPathPredicate` for `str` +/// [Predicate] used by `IntoPathPredicate` for `str`. +/// +/// # Example +/// +/// ```rust +/// use assert_fs::prelude::*; +/// +/// let temp = assert_fs::TempDir::new().unwrap(); +/// let input_file = temp.child("foo.txt"); +/// input_file.touch().unwrap(); +/// +/// // ... do something with input_file ... +/// +/// input_file.assert(""); // Uses StrContentPathPredicate +/// +/// temp.close().unwrap(); +/// ``` +/// +/// [`IntoPathPredicate`]: trait.IntoPathPredicate.html +/// [Predicate]: https://docs.rs/predicates-core/1.0.0/predicates_core/trait.Predicate.html #[derive(Debug, Clone)] pub struct StrContentPathPredicate( predicates::path::FileContentPredicate< @@ -229,7 +328,30 @@ impl IntoPathPredicate for &'static str { } // Keep `predicates` concrete Predicates out of our public API. -/// Predicate used by `IntoPathPredicate` for `str` +/// [Predicate] used by `IntoPathPredicate` for `str` predicates. +/// +/// # Example +/// +/// ```rust +/// extern crate assert_fs; +/// extern crate predicates; +/// +/// use assert_fs::prelude::*; +/// use predicates::prelude::*; +/// +/// let temp = assert_fs::TempDir::new().unwrap(); +/// let input_file = temp.child("foo.txt"); +/// input_file.touch().unwrap(); +/// +/// // ... do something with input_file ... +/// +/// input_file.assert(predicate::str::is_empty()); // Uses StrPathPredicate +/// +/// temp.close().unwrap(); +/// ``` +/// +/// [`IntoPathPredicate`]: trait.IntoPathPredicate.html +/// [Predicate]: https://docs.rs/predicates-core/1.0.0/predicates_core/trait.Predicate.html #[derive(Debug, Clone)] pub struct StrPathPredicate>( predicates::path::FileContentPredicate< diff --git a/src/fixture.rs b/src/fixture.rs index dda66e6..bbfd44e 100644 --- a/src/fixture.rs +++ b/src/fixture.rs @@ -1,4 +1,4 @@ -//! Helpers for initializing a directory of files to run tests against. +//! Initialize the filesystem to use as test fixtures. use std::fs; use std::io; @@ -13,7 +13,7 @@ pub use tempfile::TempDir; /// Access paths within [`TempDir`] for testing. /// -/// See [`ChildPath`] Trait Implementations. +/// See [`ChildPath`] trait implementations. /// /// ```rust /// use assert_fs::prelude::*; @@ -27,7 +27,7 @@ pub use tempfile::TempDir; /// [`TempDir`]: struct.TempDir.html /// [`ChildPath`]: struct.ChildPath.html pub trait PathChild { - /// Create a path within the temp directory. + /// Access a path within the temp directory. /// /// # Examples /// @@ -35,8 +35,8 @@ pub trait PathChild { /// use assert_fs::prelude::*; /// /// let temp = assert_fs::TempDir::new().unwrap(); - /// println!("{:?}", temp.path()); - /// println!("{:?}", temp.child("foo/bar.txt").path()); + /// println!("{}", temp.path().display()); + /// println!("{}", temp.child("foo/bar.txt").path().display()); /// temp.close().unwrap(); /// ``` fn child

(&self, path: P) -> ChildPath @@ -78,7 +78,7 @@ pub struct ChildPath { } impl ChildPath { - /// Wrap a path for use with special built extension traits. + /// Wrap a path for use with extension traits. /// /// See trait implementations or [`PathChild`] for more details. /// @@ -254,7 +254,6 @@ where .build() .chain(FixtureError::new(FixtureKind::Walk))? { - println!("{:?}", entry); let entry = entry.chain(FixtureError::new(FixtureKind::Walk))?; let rel = entry .path() diff --git a/src/lib.rs b/src/lib.rs index 5956800..1501bf8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,18 @@ //! assert_fs = "0.9" //! ``` //! +//! ## Overview +//! +//! Setting up a fixture +//! - [`TempDir`] +//! - [`touch`][`FileTouch`] a [`ChildPath`] +//! - [`write_binary`][`FileWriteBin`] a [`ChildPath`] +//! - [`write_str`][`FileWriteStr`] a [`ChildPath`] +//! - [`copy_from`][`PathCopy`] a pristine folder to a [`ChildPath`] or [`TempDir`] +//! +//! Validating +//! - [`assert`][`PathAssert`] a [`ChildPath`] or [`TempDir`] +//! //! ## Example //! //! Here is a trivial example: @@ -34,12 +46,18 @@ //! temp.close().unwrap(); //! ``` //! +//! ## Relevant crates +//! +//! Other crates that might be useful in testing command line programs. +//! //! [`TempDir`]: struct.TempDir.html -//! [`FileTouch`]: trait.FileTouch.html -//! [`FileWriteBin`]: trait.FileWriteBin.html -//! [`FileWriteStr`]: trait.FileWriteStr.html -//! [`PathCopy`]: trait.PathCopy.html +//! [`ChildPath`]: fixture/struct.ChildPath.html +//! [`FileTouch`]: fixture/trait.FileTouch.html +//! [`FileWriteBin`]: fixture/trait.FileWriteBin.html +//! [`FileWriteStr`]: fixture/trait.FileWriteStr.html +//! [`PathCopy`]: fixture/trait.PathCopy.html //! [`PathAssert`]: assert/trait.PathAssert.html +//! [dir-diff]: https://crates.io/crates/dir-diff #![warn(missing_docs)] From 430a821209965b1cd4b8cbb7561d9326714b2074 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 25 Oct 2018 21:02:29 -0600 Subject: [PATCH 7/7] chore: rustfmt --- src/assert.rs | 4 +--- src/fixture.rs | 6 ++---- src/lib.rs | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/assert.rs b/src/assert.rs index 057faf7..f0adf8c 100644 --- a/src/assert.rs +++ b/src/assert.rs @@ -354,9 +354,7 @@ impl IntoPathPredicate for &'static str { /// [Predicate]: https://docs.rs/predicates-core/1.0.0/predicates_core/trait.Predicate.html #[derive(Debug, Clone)] pub struct StrPathPredicate>( - predicates::path::FileContentPredicate< - predicates::str::Utf8Predicate

, - >, + predicates::path::FileContentPredicate>, ); impl

StrPathPredicate

diff --git a/src/fixture.rs b/src/fixture.rs index bbfd44e..21db45e 100644 --- a/src/fixture.rs +++ b/src/fixture.rs @@ -261,13 +261,11 @@ where .expect("entries to be under `source`"); let target_path = target.join(rel); if entry.file_type().is_dir() { - fs::create_dir_all(target_path) - .chain(FixtureError::new(FixtureKind::CreateDir))?; + fs::create_dir_all(target_path).chain(FixtureError::new(FixtureKind::CreateDir))?; } else if entry.file_type().is_file() { fs::create_dir_all(target_path.parent().expect("at least `target` exists")) .chain(FixtureError::new(FixtureKind::CreateDir))?; - fs::copy(entry.path(), target_path) - .chain(FixtureError::new(FixtureKind::CopyFile))?; + fs::copy(entry.path(), target_path).chain(FixtureError::new(FixtureKind::CopyFile))?; } } Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 1501bf8..d7b9ae5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,8 +68,8 @@ extern crate predicates_tree; extern crate tempfile; pub mod assert; -pub mod fixture; mod errors; +pub mod fixture; // Pulling this in for convenience-sake #[doc(inline)]