├── src ├── matchers │ ├── char │ │ ├── mod.rs │ │ └── equal.rs │ ├── map │ │ ├── mod.rs │ │ ├── empty.rs │ │ └── length.rs │ ├── string │ │ ├── mod.rs │ │ ├── equal.rs │ │ ├── numeric.rs │ │ ├── regex.rs │ │ ├── empty.rs │ │ ├── case.rs │ │ ├── boundary.rs │ │ └── length.rs │ ├── collection │ │ ├── mod.rs │ │ ├── duplicate.rs │ │ ├── empty.rs │ │ ├── sort.rs │ │ ├── bound.rs │ │ ├── predicate.rs │ │ ├── length.rs │ │ ├── equal.rs │ │ ├── membership.rs │ │ ├── increasing_decreasing.rs │ │ └── min_max.rs │ ├── bool │ │ └── mod.rs │ ├── option │ │ ├── predicate.rs │ │ └── mod.rs │ ├── result │ │ ├── predicate.rs │ │ └── mod.rs │ ├── date │ │ └── mod.rs │ ├── equal.rs │ ├── range │ │ └── mod.rs │ ├── float │ │ └── mod.rs │ ├── mod.rs │ ├── ordered │ │ └── mod.rs │ └── int │ │ └── mod.rs ├── assertions │ ├── map │ │ ├── mod.rs │ │ └── size.rs │ ├── string │ │ ├── mod.rs │ │ ├── case.rs │ │ ├── numeric.rs │ │ ├── equal.rs │ │ ├── regex.rs │ │ └── boundary.rs │ ├── collection │ │ ├── mod.rs │ │ ├── bound.rs │ │ ├── sort.rs │ │ ├── duplicate.rs │ │ └── predicate.rs │ ├── mod.rs │ ├── bool │ │ └── mod.rs │ ├── option │ │ ├── mod.rs │ │ └── predicate.rs │ ├── result │ │ ├── mod.rs │ │ └── predicate.rs │ ├── equal.rs │ ├── int │ │ └── mod.rs │ └── char │ │ └── mod.rs └── lib.rs ├── .gitignore ├── .github └── workflows │ └── build.yml ├── Cargo.toml ├── LICENSE-MIT └── tests ├── map.rs └── collection.rs /src/matchers/char/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod equal; 2 | -------------------------------------------------------------------------------- /src/assertions/map/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod membership; 2 | pub mod size; 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | .idea 4 | tarpaulin-report.html -------------------------------------------------------------------------------- /src/matchers/map/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod empty; 2 | pub mod length; 3 | pub mod membership; 4 | -------------------------------------------------------------------------------- /src/assertions/string/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod boundary; 2 | pub mod case; 3 | pub mod equal; 4 | pub mod length; 5 | pub mod membership; 6 | pub mod numeric; 7 | #[cfg(feature = "regex")] 8 | pub mod regex; 9 | -------------------------------------------------------------------------------- /src/matchers/string/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod boundary; 2 | pub mod case; 3 | pub mod empty; 4 | pub mod equal; 5 | pub mod length; 6 | pub mod membership; 7 | pub mod numeric; 8 | #[cfg(feature = "regex")] 9 | pub mod regex; 10 | -------------------------------------------------------------------------------- /src/assertions/collection/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bound; 2 | pub mod duplicate; 3 | pub mod equal; 4 | pub mod increasing_decreasing; 5 | pub mod membership; 6 | pub mod size; 7 | pub mod sort; 8 | pub mod predicate; 9 | pub mod min_max; 10 | -------------------------------------------------------------------------------- /src/matchers/collection/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bound; 2 | pub mod duplicate; 3 | pub mod empty; 4 | pub mod equal; 5 | pub mod increasing_decreasing; 6 | pub mod length; 7 | pub mod membership; 8 | pub mod sort; 9 | pub mod predicate; 10 | pub mod min_max; 11 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: clearcheck 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | container: 18 | image: xd009642/tarpaulin:develop-nightly 19 | options: --security-opt seccomp=unconfined 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | 24 | - name: Run Clippy 25 | run: cargo clippy --all-targets --all-features 26 | 27 | - name: Build 28 | run: cargo build --verbose --all-features 29 | 30 | - name: Generate code coverage 31 | run: cargo +nightly tarpaulin --verbose --all-features --workspace --timeout 120 --out xml 32 | 33 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "clearcheck" 3 | version = "0.0.2" 4 | edition = "2021" 5 | authors = ["Sarthak Makhija"] 6 | description = """ 7 | Elegant and extensible assertions in rust. 8 | """ 9 | readme = "README.md" 10 | repository = "https://github.com/SarthakMakhija/clearcheck" 11 | license = "MIT OR Apache-2.0" 12 | keywords = ["clearcheck", "assert", "assertions", "elegant", "elegant-assertions"] 13 | categories = ["development-tools", "development-tools::testing"] 14 | include = ["src/", "LICENSE-*", "README.md", "tests/"] 15 | 16 | [package.metadata.docs.rs] 17 | all-features = true 18 | 19 | [features] 20 | date = ["dep:chrono"] 21 | file = ["dep:walkdir"] 22 | num = ["dep:num"] 23 | regex = ["dep:regex"] 24 | 25 | [dependencies] 26 | chrono = { version = "0.4.31", optional = true } 27 | num = { version = "0.4.1", optional = true } 28 | regex = { version = "1.10.2", optional = true } 29 | walkdir = { version = "2.4.0", features = [], optional = true } 30 | 31 | [dev-dependencies] 32 | tempdir = "0.3.7" 33 | -------------------------------------------------------------------------------- /src/matchers/char/equal.rs: -------------------------------------------------------------------------------- 1 | //! provides [IgnoreCaseEqualityMatcher] for char. 2 | 3 | use crate::matchers::equal::IgnoreCaseEqualityMatcher; 4 | use crate::matchers::{Matcher, MatcherResult}; 5 | 6 | impl Matcher for IgnoreCaseEqualityMatcher { 7 | fn test(&self, value: &char) -> MatcherResult { 8 | MatcherResult::formatted( 9 | value.eq_ignore_ascii_case(&self.other), 10 | format!("{} should match {}", value, self.other), 11 | format!("{} should not match {}", value, self.other), 12 | ) 13 | } 14 | } 15 | 16 | #[cfg(test)] 17 | mod tests { 18 | use crate::assertions::bool::TrueFalseAssertion; 19 | use crate::matchers::equal::be_equal_ignoring_case; 20 | use crate::matchers::Matcher; 21 | 22 | #[test] 23 | fn should_be_equal() { 24 | let based = be_equal_ignoring_case('a'); 25 | based.test(&'a').passed.should_be_true(); 26 | } 27 | 28 | #[test] 29 | fn should_not_be_equal() { 30 | let based = be_equal_ignoring_case('b'); 31 | based.test(&'a').passed.should_be_false(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Sarthak Makhija 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/matchers/string/equal.rs: -------------------------------------------------------------------------------- 1 | //! provides [IgnoreCaseEqualityMatcher] for &str. 2 | 3 | use crate::matchers::equal::IgnoreCaseEqualityMatcher; 4 | use crate::matchers::{Matcher, MatcherResult}; 5 | 6 | impl Matcher for IgnoreCaseEqualityMatcher<&str> 7 | where T: AsRef 8 | { 9 | fn test(&self, value: &T) -> MatcherResult { 10 | MatcherResult::formatted( 11 | value.as_ref().eq_ignore_ascii_case(self.other), 12 | format!("{:?} should equal {:?}", value.as_ref(), self.other), 13 | format!("{:?} should not equal {:?}", value.as_ref(), self.other), 14 | ) 15 | } 16 | } 17 | 18 | #[cfg(test)] 19 | mod tests { 20 | use crate::assertions::bool::TrueFalseAssertion; 21 | use crate::matchers::equal::be_equal_ignoring_case; 22 | use crate::matchers::Matcher; 23 | 24 | #[test] 25 | fn should_equal() { 26 | let matcher = be_equal_ignoring_case("ASSERT"); 27 | matcher.test(&"assert").passed.should_be_true(); 28 | } 29 | 30 | #[test] 31 | #[should_panic] 32 | fn should_equal_but_was_not() { 33 | let matcher = be_equal_ignoring_case("assert"); 34 | matcher.test(&"assert4J").passed.should_be_true(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/assertions/mod.rs: -------------------------------------------------------------------------------- 1 | //! Assertions serve as the cornerstone of the test cases, defining the exact expectations the code must fulfill. 2 | //! They act as a contract, ensuring that each data type (/data structure) adheres to its intended behavior. 3 | //! clearcheck provides plethora of ready-made assertions. 4 | //! 5 | //! Let's take an example. 6 | //! 7 | //! ```rust 8 | //! use clearcheck::assertions::collection::duplicate::DuplicateContentAssertion; 9 | //! use clearcheck::assertions::collection::membership::MembershipAssertion; 10 | //! use clearcheck::assertions::collection::size::SizeAssertion; 11 | //! 12 | //! let keywords = ["testing", "automation", "clearcheck", "junit"]; 13 | //! keywords.should_not_be_empty() 14 | //! .should_have_size_in_inclusive_range(4..=10) 15 | //! .should_not_contain_duplicates() 16 | //! .should_contain_any(vec!["junit", "clearcheck", "testing"]) 17 | //! .should_not_contain_any(vec!["scalatest", "gotest"]); 18 | //! ``` 19 | 20 | pub mod bool; 21 | pub mod char; 22 | pub mod collection; 23 | #[cfg(feature = "date")] 24 | pub mod date; 25 | pub mod equal; 26 | #[cfg(feature = "file")] 27 | pub mod file; 28 | #[cfg(feature = "num")] 29 | pub mod float; 30 | #[cfg(feature = "num")] 31 | pub mod int; 32 | pub mod map; 33 | pub mod option; 34 | pub mod ordered; 35 | pub mod result; 36 | pub mod string; 37 | -------------------------------------------------------------------------------- /tests/map.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use clearcheck::assertions::collection::size::SizeAssertion; 4 | use clearcheck::assertions::map::membership::{ 5 | KeyMembershipAssertion, KeyValueMembershipAssertion, NoMembershipAssertion, 6 | ValueMembershipAssertion, 7 | }; 8 | 9 | #[derive(Eq, Debug, PartialEq, Hash)] 10 | struct Book { 11 | id: usize, 12 | title: &'static str, 13 | } 14 | 15 | impl Book { 16 | fn new(id: usize, title: &'static str) -> Self { 17 | Book { id, title } 18 | } 19 | } 20 | 21 | #[test] 22 | fn should_match_all_books_by_name() { 23 | let mut book_id_by_name = HashMap::new(); 24 | book_id_by_name.insert("Database internals", 1); 25 | book_id_by_name.insert("Designing data intensive applications", 2); 26 | 27 | book_id_by_name 28 | .should_not_be_empty() 29 | .should_contain_key("Database internals") 30 | .should_contain_value(&1) 31 | .should_have_at_least_size(2) 32 | .should_contain("Database internals", &1); 33 | } 34 | 35 | #[test] 36 | fn should_match_all_books() { 37 | let mut book_rank_by_name = HashMap::new(); 38 | book_rank_by_name.insert(Book::new(20, "Patterns of Distributed Systems"), 1); 39 | book_rank_by_name.insert(Book::new(21, "Designing data intensive applications"), 2); 40 | book_rank_by_name.insert(Book::new(21, "Database internals"), 3); 41 | 42 | book_rank_by_name 43 | .should_not_be_empty() 44 | .should_contain_key(&Book::new(20, "Patterns of Distributed Systems")) 45 | .should_not_contain_key(&Book::new(25, "Rust in action")) 46 | .should_have_size_in_inclusive_range(1..=5) 47 | .should_contain_any_of_values(vec![&1, &3, &4]); 48 | } 49 | -------------------------------------------------------------------------------- /src/matchers/bool/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::matchers::{Matcher, MatcherResult}; 2 | 3 | /// TrueFalseMatcher provides a way to assert whether boolean values evaluate to true or false. 4 | /// 5 | /// # Example 6 | ///``` 7 | /// use clearcheck::matchers::bool::be_true; 8 | /// use clearcheck::matchers::Matcher; 9 | /// 10 | /// let matcher = be_true(); 11 | /// assert!(matcher.test(&true).passed()); 12 | /// ``` 13 | pub enum TrueFalseMatcher { 14 | True, 15 | False, 16 | } 17 | 18 | impl Matcher for TrueFalseMatcher { 19 | fn test(&self, value: &bool) -> MatcherResult { 20 | match self { 21 | TrueFalseMatcher::True => { 22 | MatcherResult::new(*value, "Value should be TRUE", "Value should not be TRUE") 23 | } 24 | TrueFalseMatcher::False => MatcherResult::new( 25 | !(*value), 26 | "Value should be FALSE", 27 | "Value should not be FALSE", 28 | ), 29 | } 30 | } 31 | } 32 | 33 | /// Creates a TrueFalseMatcher::True instance for asserting true. 34 | pub fn be_true() -> TrueFalseMatcher { 35 | TrueFalseMatcher::True 36 | } 37 | 38 | /// Creates a TrueFalseMatcher::False instance for asserting false. 39 | pub fn be_false() -> TrueFalseMatcher { 40 | TrueFalseMatcher::False 41 | } 42 | 43 | #[cfg(test)] 44 | mod tests { 45 | use crate::assertions::bool::TrueFalseAssertion; 46 | use crate::matchers::bool::{be_false, be_true}; 47 | use crate::matchers::Matcher; 48 | 49 | #[test] 50 | fn should_be_true() { 51 | let matcher = be_true(); 52 | matcher.test(&true).passed.should_be_true(); 53 | } 54 | 55 | #[test] 56 | fn should_be_false() { 57 | let matcher = be_false(); 58 | matcher.test(&false).passed.should_be_true(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/assertions/bool/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::matchers::bool::{be_false, be_true}; 2 | use crate::matchers::Should; 3 | 4 | /// TrueFalseAssertion enables assertions about whether a boolean evaluates to true or false. 5 | pub trait TrueFalseAssertion { 6 | /// - Asserts that the boolean evaluates to true. 7 | /// - Returns a reference to self for fluent chaining. 8 | /// - Panics if the assertion fails. 9 | /// # Example 10 | /// ``` 11 | /// use clearcheck::assertions::bool::TrueFalseAssertion; 12 | /// 13 | /// let value = true; 14 | /// value.should_be_true(); 15 | /// ``` 16 | fn should_be_true(&self) -> &Self; 17 | 18 | /// - Asserts that the boolean evaluates to false. 19 | /// - Returns a reference to self for fluent chaining. 20 | /// - Panics if the assertion fails. 21 | /// # Example 22 | /// ``` 23 | /// use clearcheck::assertions::bool::TrueFalseAssertion; 24 | /// 25 | /// let value = false; 26 | /// value.should_be_false(); 27 | /// ``` 28 | fn should_be_false(&self) -> &Self; 29 | } 30 | 31 | impl TrueFalseAssertion for bool { 32 | fn should_be_true(&self) -> &Self { 33 | self.should(&be_true()); 34 | self 35 | } 36 | 37 | fn should_be_false(&self) -> &Self { 38 | self.should(&be_false()); 39 | self 40 | } 41 | } 42 | 43 | #[cfg(test)] 44 | mod tests { 45 | use crate::assertions::bool::TrueFalseAssertion; 46 | 47 | #[test] 48 | fn should_be_true() { 49 | let value = true; 50 | value.should_be_true(); 51 | } 52 | 53 | #[test] 54 | #[should_panic] 55 | fn should_be_true_but_was_not() { 56 | let value = false; 57 | value.should_be_true(); 58 | } 59 | 60 | #[test] 61 | fn should_be_false() { 62 | let value = false; 63 | value.should_be_false(); 64 | } 65 | 66 | #[test] 67 | #[should_panic] 68 | fn should_be_false_but_was_not() { 69 | let value = true; 70 | value.should_be_false(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/matchers/option/predicate.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use crate::matchers::{Matcher, MatcherResult}; 4 | 5 | /// SomePredicateMatcher offers a flexible way to assert whether the Option value is both Some and that the contained value meets certain conditions defined by the predicate. 6 | /// 7 | /// # Example 8 | ///``` 9 | /// use clearcheck::matchers::Matcher; 10 | /// use clearcheck::matchers::option::predicate::satisfy; 11 | /// 12 | /// let matcher = satisfy(|value| value > &&400); 13 | /// assert!(matcher.test(&Some(1000)).passed()); 14 | /// ``` 15 | pub struct SomePredicateMatcher 16 | where F: Fn(&&T) -> bool 17 | { 18 | predicate: F, 19 | _inner: PhantomData, 20 | } 21 | 22 | impl Matcher> for SomePredicateMatcher 23 | where F: Fn(&&T) -> bool 24 | { 25 | fn test(&self, value: &Option) -> MatcherResult { 26 | MatcherResult::new( 27 | value.as_ref().filter(&self.predicate).is_some(), 28 | "Option value should satisfy the given predicate", 29 | "Option value should not satisfy the given predicate", 30 | ) 31 | } 32 | } 33 | 34 | /// Creates a SomePredicateMatcher that asserts whether the Option value is both Some and that the contained value meets certain conditions defined by the predicate. 35 | pub fn satisfy(predicate: F) -> SomePredicateMatcher 36 | where F: Fn(&&T) -> bool 37 | { 38 | SomePredicateMatcher { 39 | predicate, 40 | _inner: PhantomData, 41 | } 42 | } 43 | 44 | #[cfg(test)] 45 | mod tests { 46 | use crate::assertions::bool::TrueFalseAssertion; 47 | use crate::matchers::Matcher; 48 | use crate::matchers::option::predicate::satisfy; 49 | 50 | #[test] 51 | fn should_be_some_and_satisfy_the_predicate() { 52 | let matcher = satisfy(|value| value > &&400); 53 | matcher.test(&Some(1000)).passed.should_be_true(); 54 | } 55 | 56 | #[test] 57 | #[should_panic] 58 | fn should_be_some_and_satisfy_the_predicate_but_did_not() { 59 | let matcher = satisfy(|value| value > &&400); 60 | matcher.test(&Some(100)).passed.should_be_true(); 61 | } 62 | } -------------------------------------------------------------------------------- /src/assertions/option/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod predicate; 2 | 3 | use std::fmt::Debug; 4 | 5 | use crate::matchers::option::{be_none, be_some}; 6 | use crate::matchers::Should; 7 | 8 | /// SomeNoneAssertion enables assertions about whether an Option evaluates to Some or None. 9 | pub trait SomeNoneAssertion { 10 | /// - Asserts that the Option evaluates to Some. 11 | /// - Returns a reference to self for fluent chaining. 12 | /// - Panics if the assertion fails. 13 | /// # Example 14 | /// ``` 15 | /// use clearcheck::assertions::option::SomeNoneAssertion; 16 | /// 17 | /// let value = Some(32); 18 | /// value.should_be_some(); 19 | /// ``` 20 | fn should_be_some(&self) -> &Self; 21 | 22 | /// - Asserts that the Option evaluates to None. 23 | /// - Returns a reference to self for fluent chaining. 24 | /// - Panics if the assertion fails. 25 | /// # Example 26 | /// ``` 27 | /// use clearcheck::assertions::option::SomeNoneAssertion; 28 | /// 29 | /// let value: Option = None; 30 | /// value.should_be_none(); 31 | /// ``` 32 | fn should_be_none(&self) -> &Self; 33 | } 34 | 35 | impl SomeNoneAssertion for Option 36 | where 37 | T: Debug, 38 | { 39 | fn should_be_some(&self) -> &Self { 40 | self.should(&be_some()); 41 | self 42 | } 43 | 44 | fn should_be_none(&self) -> &Self { 45 | self.should(&be_none()); 46 | self 47 | } 48 | } 49 | 50 | #[cfg(test)] 51 | mod tests { 52 | use crate::assertions::option::SomeNoneAssertion; 53 | 54 | #[test] 55 | fn should_be_none() { 56 | let option: Option = None; 57 | option.should_be_none(); 58 | } 59 | 60 | #[test] 61 | #[should_panic] 62 | fn should_be_none_but_was_not() { 63 | let option = Some("junit"); 64 | option.should_be_none(); 65 | } 66 | 67 | #[test] 68 | fn should_be_some() { 69 | let option = Some("junit"); 70 | option.should_be_some(); 71 | } 72 | 73 | #[test] 74 | #[should_panic] 75 | fn should_be_some_but_was_not() { 76 | let option: Option = None; 77 | option.should_be_some(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/assertions/result/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod predicate; 2 | 3 | use std::fmt::Debug; 4 | 5 | use crate::matchers::result::{be_err, be_ok}; 6 | use crate::matchers::Should; 7 | 8 | /// OkErrAssertion enables assertions about whether a Result evaluates to Ok or Err. 9 | pub trait OkErrAssertion { 10 | /// - Asserts that the Result evaluates to Ok. 11 | /// - Returns a reference to self for fluent chaining. 12 | /// - Panics if the assertion fails. 13 | /// # Example 14 | /// ``` 15 | /// use clearcheck::assertions::result::OkErrAssertion; 16 | /// 17 | /// let value: Result = Ok(32); 18 | /// value.should_be_ok(); 19 | /// ``` 20 | fn should_be_ok(&self) -> &Self; 21 | 22 | /// - Asserts that the Result evaluates to Err. 23 | /// - Returns a reference to self for fluent chaining. 24 | /// - Panics if the assertion fails. 25 | /// # Example 26 | /// ``` 27 | /// use clearcheck::assertions::result::OkErrAssertion; 28 | /// 29 | /// let value: Result = Err("example error"); 30 | /// value.should_be_err(); 31 | /// ``` 32 | fn should_be_err(&self) -> &Self; 33 | } 34 | 35 | impl OkErrAssertion for Result 36 | where 37 | T: Debug, 38 | E: Debug, 39 | { 40 | fn should_be_ok(&self) -> &Self { 41 | self.should(&be_ok()); 42 | self 43 | } 44 | 45 | fn should_be_err(&self) -> &Self { 46 | self.should(&be_err()); 47 | self 48 | } 49 | } 50 | 51 | #[cfg(test)] 52 | mod tests { 53 | use crate::assertions::result::OkErrAssertion; 54 | 55 | #[test] 56 | fn should_be_ok() { 57 | let result: Result = Ok(100); 58 | result.should_be_ok(); 59 | } 60 | 61 | #[test] 62 | #[should_panic] 63 | fn should_be_ok_but_was_not() { 64 | let result: Result = Err("test error"); 65 | result.should_be_ok(); 66 | } 67 | 68 | #[test] 69 | fn should_be_err() { 70 | let result: Result = Err("test error"); 71 | result.should_be_err(); 72 | } 73 | 74 | #[test] 75 | #[should_panic] 76 | fn should_be_err_but_was_not() { 77 | let result: Result = Ok(100); 78 | result.should_be_err(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/matchers/result/predicate.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use crate::matchers::{Matcher, MatcherResult}; 3 | 4 | /// OkPredicateMatcher offers a flexible way to assert whether the Result value is both Ok and that the contained value meets certain conditions defined by the predicate. 5 | /// 6 | /// # Example 7 | ///``` 8 | /// use clearcheck::matchers::Matcher; 9 | /// use clearcheck::matchers::result::predicate::satisfy; 10 | /// 11 | /// let matcher = satisfy(|value| value > &400); 12 | /// let value: Result = Ok(1000); 13 | /// 14 | /// assert!(matcher.test(&value).passed()); 15 | /// ``` 16 | pub struct OkPredicateMatcher 17 | where F: Fn(&T) -> bool 18 | { 19 | predicate: F, 20 | _inner: PhantomData, 21 | } 22 | 23 | impl Matcher> for OkPredicateMatcher 24 | where F: Fn(&T) -> bool 25 | { 26 | fn test(&self, value: &Result) -> MatcherResult { 27 | MatcherResult::new( 28 | value.as_ref().is_ok_and(&self.predicate), 29 | "Result value should satisfy the given predicate", 30 | "Result value should not satisfy the given predicate", 31 | ) 32 | } 33 | } 34 | 35 | /// Creates an OkPredicateMatcher that asserts whether the Result value is both Ok and that the contained value meets certain conditions defined by the predicate. 36 | pub fn satisfy(predicate: F) -> OkPredicateMatcher 37 | where F: Fn(&T) -> bool 38 | { 39 | OkPredicateMatcher { 40 | predicate, 41 | _inner: PhantomData, 42 | } 43 | } 44 | 45 | #[cfg(test)] 46 | mod tests { 47 | use crate::assertions::bool::TrueFalseAssertion; 48 | use crate::matchers::Matcher; 49 | use crate::matchers::result::predicate::satisfy; 50 | 51 | #[test] 52 | fn should_be_ok_and_satisfy_the_predicate() { 53 | let matcher = satisfy(|value| value > &400); 54 | let value: Result = Ok(1000); 55 | 56 | matcher.test(&value).passed.should_be_true(); 57 | } 58 | 59 | #[test] 60 | #[should_panic] 61 | fn should_be_ok_and_satisfy_the_predicate_but_did_not() { 62 | let matcher = satisfy(|value| value > &400); 63 | let value: Result = Ok(100); 64 | 65 | matcher.test(&value).passed.should_be_true(); 66 | } 67 | } -------------------------------------------------------------------------------- /src/matchers/string/numeric.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::str::FromStr; 3 | 4 | use crate::matchers::{Matcher, MatcherResult}; 5 | 6 | /// NumericMatcher offers a flexible way to assert whether a string is numeric. 7 | /// 8 | /// # Example 9 | ///``` 10 | /// use clearcheck::matchers::string::numeric::be_numeric; 11 | /// use clearcheck::matchers::Matcher; 12 | /// 13 | /// let matcher = be_numeric::(); 14 | /// assert!(matcher.test(&"12345").passed()); 15 | /// ``` 16 | pub struct NumericMatcher { 17 | _inner: PhantomData, 18 | } 19 | 20 | impl, M: FromStr> Matcher for NumericMatcher { 21 | fn test(&self, value: &T) -> MatcherResult { 22 | let parse_result = value.as_ref().parse::(); 23 | MatcherResult::formatted( 24 | parse_result.is_ok(), 25 | format!("{:?} should be numeric", value.as_ref()), 26 | format!("{:?} should not be numeric", value.as_ref()), 27 | ) 28 | } 29 | } 30 | 31 | /// Creates a NumericMatcher that asserts whether a string is numeric. 32 | pub fn be_numeric() -> NumericMatcher { 33 | NumericMatcher { 34 | _inner: PhantomData, 35 | } 36 | } 37 | 38 | #[cfg(test)] 39 | mod tests { 40 | use crate::assertions::bool::TrueFalseAssertion; 41 | use crate::matchers::Matcher; 42 | use crate::matchers::string::numeric::be_numeric; 43 | 44 | #[test] 45 | fn should_be_numeric_i32() { 46 | let matcher = be_numeric::(); 47 | let value = "123"; 48 | matcher.test(&value).passed.should_be_true(); 49 | } 50 | 51 | #[test] 52 | fn should_be_numeric_f64() { 53 | let matcher = be_numeric::(); 54 | let value = "123.45"; 55 | matcher.test(&value).passed.should_be_true(); 56 | } 57 | 58 | #[test] 59 | #[should_panic] 60 | fn should_be_numeric_i32_but_was_not() { 61 | let matcher = be_numeric::(); 62 | let value = "123a"; 63 | matcher.test(&value).passed.should_be_true(); 64 | } 65 | 66 | #[test] 67 | #[should_panic] 68 | fn should_be_numeric_f64_but_was_not() { 69 | let matcher = be_numeric::(); 70 | let value = "123.45a"; 71 | matcher.test(&value).passed.should_be_true(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tests/collection.rs: -------------------------------------------------------------------------------- 1 | use clearcheck::assertions::collection::duplicate::DuplicateContentAssertion; 2 | use clearcheck::assertions::collection::membership::MembershipAssertion; 3 | use clearcheck::assertions::collection::size::SizeAssertion; 4 | use clearcheck::assertions::collection::sort::SortAssertion; 5 | use clearcheck::assertions::ordered::OrderedAssertion; 6 | 7 | #[derive(Eq, Debug, PartialEq)] 8 | struct Book { 9 | id: usize, 10 | title: &'static str, 11 | } 12 | 13 | impl Book { 14 | fn new(id: usize, title: &'static str) -> Self { 15 | Book { id, title } 16 | } 17 | } 18 | 19 | #[test] 20 | fn should_match_all_books() { 21 | let library = vec![ 22 | Book::new(1, "Database internals"), 23 | Book::new(2, "Designing data intensive applications"), 24 | Book::new(3, "Learning rust"), 25 | Book::new(4, "Rust in action"), 26 | ]; 27 | 28 | library 29 | .should_not_be_empty() 30 | .should_not_contain_duplicates() 31 | .should_have_at_least_size(3) 32 | .should_contain_all(vec![ 33 | &Book::new(3, "Learning rust"), 34 | &Book::new(4, "Rust in action"), 35 | ]); 36 | } 37 | 38 | #[test] 39 | #[should_panic] 40 | fn should_not_match_all_book() { 41 | let library = vec![ 42 | Book::new(1, "Database internals"), 43 | Book::new(2, "Designing data intensive applications"), 44 | Book::new(3, "Learning rust"), 45 | Book::new(4, "Rust in action"), 46 | ]; 47 | 48 | library 49 | .should_not_be_empty() 50 | .should_have_at_least_size(3) 51 | .should_not_contain_duplicates() 52 | .should_contain_all(vec![ 53 | &Book::new(3, "Learning rust"), 54 | &Book::new(4, "Designing a KV storage engine"), 55 | ]); 56 | } 57 | 58 | #[test] 59 | fn should_match_all_strings() { 60 | let libraries = vec!["clearcheck", "gotest", "junit", "scalatest"]; 61 | libraries 62 | .should_not_be_empty() 63 | .should_not_contain_duplicates() 64 | .should_contain("clearcheck") 65 | .should_be_sorted_ascending() 66 | .should_be_less_than(&vec![ 67 | "clearcheck", 68 | "gotest", 69 | "junit", 70 | "scalatest", 71 | "ziptest", 72 | ]); 73 | } 74 | -------------------------------------------------------------------------------- /src/matchers/string/regex.rs: -------------------------------------------------------------------------------- 1 | use regex::Regex; 2 | 3 | use crate::matchers::{Matcher, MatcherResult}; 4 | 5 | /// RegexMatcher offers a flexible way to assert whether a string matches a regular expression. 6 | /// 7 | /// # Example 8 | ///``` 9 | /// use regex::Regex; 10 | /// use clearcheck::matchers::string::regex::match_with; 11 | /// use clearcheck::matchers::Matcher; 12 | /// 13 | /// let matcher = match_with(Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap()); 14 | /// assert!(matcher.test(&"Started clearcheck on On 2024-01-02.").passed()); 15 | /// ``` 16 | pub struct RegexMatcher { 17 | regexp: Regex, 18 | } 19 | 20 | impl> Matcher for RegexMatcher { 21 | fn test(&self, value: &T) -> MatcherResult { 22 | MatcherResult::formatted( 23 | self.regexp.is_match(value.as_ref()), 24 | format!( 25 | "{:?} should match the regular expression {:?}", 26 | value.as_ref(), self.regexp 27 | ), 28 | format!( 29 | "{:?} should not match the regular expression {:?}", 30 | value.as_ref(), self.regexp 31 | ), 32 | ) 33 | } 34 | } 35 | 36 | /// Creates a RegexMatcher that asserts whether a string matches the given regular expression. 37 | pub fn match_with(regular_expression: Regex) -> RegexMatcher { 38 | RegexMatcher { 39 | regexp: regular_expression, 40 | } 41 | } 42 | 43 | #[cfg(all(test, feature = "regex"))] 44 | mod tests { 45 | use crate::assertions::bool::TrueFalseAssertion; 46 | use crate::matchers::string::regex::match_with; 47 | use crate::matchers::Matcher; 48 | use regex::Regex; 49 | 50 | #[test] 51 | fn should_match_regular_expression() { 52 | let regex = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap(); 53 | let str = "Started clearcheck on On 2024-01-02."; 54 | 55 | let matcher = match_with(regex); 56 | matcher.test(&str).passed.should_be_true(); 57 | } 58 | 59 | #[test] 60 | #[should_panic] 61 | fn should_match_regular_expression_but_it_did_not() { 62 | let regex = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap(); 63 | let str = "Started clearcheck on On 02nd January 2024"; 64 | 65 | let matcher = match_with(regex); 66 | matcher.test(&str).passed.should_be_true(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/matchers/option/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::matchers::{Matcher, MatcherResult}; 2 | 3 | pub mod predicate; 4 | 5 | /// SomeNoneMatcher provides a way to assert whether [`Option`] values evaluate to Some or None. 6 | /// 7 | /// # Example 8 | ///``` 9 | /// use clearcheck::matchers::Matcher; 10 | /// use clearcheck::matchers::option::be_some; 11 | /// 12 | /// let matcher = be_some(); 13 | /// assert!(matcher.test(&Some("clearcheck")).passed()); 14 | /// ``` 15 | pub enum SomeNoneMatcher { 16 | Some, 17 | None, 18 | } 19 | 20 | impl Matcher> for SomeNoneMatcher { 21 | fn test(&self, value: &Option) -> MatcherResult { 22 | match self { 23 | SomeNoneMatcher::Some => MatcherResult::new( 24 | value.is_some(), 25 | "Value should be Some", 26 | "Value should not be Some", 27 | ), 28 | SomeNoneMatcher::None => MatcherResult::new( 29 | value.is_none(), 30 | "Value should be None", 31 | "Value should not be None", 32 | ), 33 | } 34 | } 35 | } 36 | 37 | /// Creates a SomeNoneMatcher::Some instance for asserting that an option value evaluates to Some. 38 | pub fn be_some() -> SomeNoneMatcher { 39 | SomeNoneMatcher::Some 40 | } 41 | 42 | /// Creates a SomeNoneMatcher::None instance for asserting that an option value evaluates to None. 43 | pub fn be_none() -> SomeNoneMatcher { 44 | SomeNoneMatcher::None 45 | } 46 | 47 | #[cfg(test)] 48 | mod tests { 49 | use crate::assertions::bool::TrueFalseAssertion; 50 | use crate::matchers::option::{be_none, be_some}; 51 | use crate::matchers::Matcher; 52 | 53 | #[test] 54 | fn should_be_some() { 55 | let matcher = be_some(); 56 | matcher.test(&Some(10)).passed.should_be_true(); 57 | } 58 | 59 | #[test] 60 | #[should_panic] 61 | fn should_be_some_but_was_not() { 62 | let matcher = be_some(); 63 | matcher.test(&None::<()>).passed.should_be_true(); 64 | } 65 | 66 | #[test] 67 | fn should_be_none() { 68 | let matcher = be_none(); 69 | matcher.test(&None::<()>).passed.should_be_true(); 70 | } 71 | 72 | #[test] 73 | #[should_panic] 74 | fn should_be_none_but_was_not() { 75 | let matcher = be_none(); 76 | matcher.test(&Some(10)).passed.should_be_true(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/matchers/string/empty.rs: -------------------------------------------------------------------------------- 1 | use crate::matchers::{Matcher, MatcherResult}; 2 | 3 | /// StringEmptyMatcher offers a flexible way to assert whether a string is empty (no characters). 4 | /// 5 | /// # Example 6 | ///``` 7 | /// use clearcheck::matchers::string::empty::be_empty; 8 | /// use clearcheck::matchers::Matcher; 9 | /// 10 | /// let matcher = be_empty(); 11 | /// assert!(matcher.test(&"").passed()); 12 | /// ``` 13 | pub enum StringEmptyMatcher { 14 | Empty, 15 | NotEmpty, 16 | } 17 | 18 | impl Matcher for StringEmptyMatcher 19 | where T: AsRef 20 | { 21 | fn test(&self, value: &T) -> MatcherResult { 22 | match self { 23 | StringEmptyMatcher::Empty => MatcherResult::new( 24 | value.as_ref().is_empty(), 25 | "Value should be empty", 26 | "Value should not be empty", 27 | ), 28 | StringEmptyMatcher::NotEmpty => MatcherResult::new( 29 | !value.as_ref().is_empty(), 30 | "Value should not be empty", 31 | "Value should be empty", 32 | ), 33 | } 34 | } 35 | } 36 | 37 | /// Creates a StringEmptyMatcher that asserts whether a string is empty. 38 | pub fn be_empty() -> StringEmptyMatcher { 39 | StringEmptyMatcher::Empty 40 | } 41 | 42 | /// Creates a StringEmptyMatcher that asserts whether a string is not empty. 43 | pub fn not_be_empty() -> StringEmptyMatcher { 44 | StringEmptyMatcher::NotEmpty 45 | } 46 | 47 | #[cfg(test)] 48 | mod string_tests { 49 | use crate::assertions::bool::TrueFalseAssertion; 50 | use crate::matchers::Matcher; 51 | use crate::matchers::string::empty::{be_empty, not_be_empty}; 52 | 53 | #[test] 54 | fn should_be_empty() { 55 | let matcher = be_empty(); 56 | matcher.test(&"").passed.should_be_true(); 57 | } 58 | 59 | #[test] 60 | #[should_panic] 61 | fn should_be_empty_but_was_not() { 62 | let matcher = be_empty(); 63 | matcher.test(&"goselect").passed.should_be_true(); 64 | } 65 | 66 | #[test] 67 | fn should_not_be_empty() { 68 | let matcher = not_be_empty(); 69 | matcher.test(&"goselect").passed.should_be_true(); 70 | } 71 | 72 | #[test] 73 | #[should_panic] 74 | fn should_not_be_empty_but_was() { 75 | let matcher = not_be_empty(); 76 | matcher.test(&"").passed.should_be_true(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/matchers/result/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod predicate; 2 | 3 | use crate::matchers::{Matcher, MatcherResult}; 4 | 5 | /// OkErrMatcher provides a way to assert whether [`Result`] values evaluate to Ok or Err. 6 | /// 7 | /// # Example 8 | ///``` 9 | /// use clearcheck::matchers::Matcher; 10 | /// use clearcheck::matchers::result::be_ok; 11 | /// 12 | /// let matcher = be_ok(); 13 | /// let value: Result = Ok(100); 14 | /// 15 | /// assert!(matcher.test(&value).passed()); 16 | /// ``` 17 | pub enum OkErrMatcher { 18 | Ok, 19 | Err, 20 | } 21 | 22 | impl Matcher> for OkErrMatcher { 23 | fn test(&self, value: &Result) -> MatcherResult { 24 | match self { 25 | OkErrMatcher::Ok => MatcherResult::new( 26 | value.is_ok(), 27 | "Value should be Ok", 28 | "Value should not be Ok", 29 | ), 30 | OkErrMatcher::Err => MatcherResult::new( 31 | value.is_err(), 32 | "Value should be Err", 33 | "Value should not be Err", 34 | ), 35 | } 36 | } 37 | } 38 | 39 | /// Creates an OkErrMatcher::Ok instance for asserting that a result value evaluates to Ok. 40 | pub fn be_ok() -> OkErrMatcher { 41 | OkErrMatcher::Ok 42 | } 43 | 44 | /// Creates an OkErrMatcher::Err instance for asserting that a result value evaluates to Err. 45 | pub fn be_err() -> OkErrMatcher { 46 | OkErrMatcher::Err 47 | } 48 | 49 | #[cfg(test)] 50 | mod tests { 51 | use crate::assertions::bool::TrueFalseAssertion; 52 | use crate::matchers::result::{be_err, be_ok}; 53 | use crate::matchers::Matcher; 54 | 55 | #[test] 56 | fn should_be_ok() { 57 | let matcher = be_ok(); 58 | matcher.test(&Ok::(12)).passed.should_be_true(); 59 | } 60 | 61 | #[test] 62 | #[should_panic] 63 | fn should_be_ok_but_was_not() { 64 | let matcher = be_ok(); 65 | matcher 66 | .test(&Err::("test error")) 67 | .passed 68 | .should_be_true(); 69 | } 70 | 71 | #[test] 72 | fn should_be_err() { 73 | let matcher = be_err(); 74 | matcher 75 | .test(&Err::("test error")) 76 | .passed 77 | .should_be_true(); 78 | } 79 | 80 | #[test] 81 | #[should_panic] 82 | fn should_be_err_but_was_not() { 83 | let matcher = be_err(); 84 | matcher.test(&Ok::(12)).passed.should_be_true(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/matchers/string/case.rs: -------------------------------------------------------------------------------- 1 | use crate::matchers::{Matcher, MatcherResult}; 2 | 3 | /// CaseMatcher offers a flexible way to assert that a string is either lowercase or uppercase. 4 | /// 5 | /// # Example 6 | ///``` 7 | /// use clearcheck::matchers::Matcher; 8 | /// use clearcheck::matchers::string::case::be_lowercase; 9 | /// 10 | /// let matcher = be_lowercase(); 11 | /// assert!(matcher.test(&"clearcheck").passed()); 12 | /// ``` 13 | pub enum CaseMatcher { 14 | Lower, 15 | Upper, 16 | } 17 | 18 | impl Matcher for CaseMatcher 19 | where T: AsRef + PartialEq 20 | { 21 | fn test(&self, value: &T) -> MatcherResult { 22 | match self { 23 | CaseMatcher::Lower => MatcherResult::formatted( 24 | value.as_ref() == value.as_ref().to_lowercase(), 25 | format!("{:?} should be lowercase", value.as_ref()), 26 | format!("{:?} should not be lowercase", value.as_ref()), 27 | ), 28 | CaseMatcher::Upper => MatcherResult::formatted( 29 | value.as_ref() == value.as_ref().to_uppercase(), 30 | format!("{:?} should be uppercase", value.as_ref()), 31 | format!("{:?} should not be uppercase", value.as_ref()), 32 | ), 33 | } 34 | } 35 | } 36 | 37 | /// Creates a CaseMatcher that asserts whether a string value is composed of lowercase letters. 38 | pub fn be_lowercase() -> CaseMatcher { 39 | CaseMatcher::Lower 40 | } 41 | 42 | /// Creates a CaseMatcher that asserts whether a string value is composed of uppercase letters. 43 | pub fn be_uppercase() -> CaseMatcher { 44 | CaseMatcher::Upper 45 | } 46 | 47 | #[cfg(test)] 48 | mod tests { 49 | use crate::assertions::bool::TrueFalseAssertion; 50 | use crate::matchers::string::case::{be_lowercase, be_uppercase}; 51 | use crate::matchers::Matcher; 52 | 53 | #[test] 54 | fn should_be_lowercase() { 55 | let matcher = be_lowercase(); 56 | matcher.test(&"goselect").passed.should_be_true(); 57 | } 58 | 59 | #[test] 60 | #[should_panic] 61 | fn should_be_lowercase_but_was_not() { 62 | let matcher = be_lowercase(); 63 | matcher.test(&"GoSelect").passed.should_be_true(); 64 | } 65 | 66 | #[test] 67 | fn should_be_uppercase() { 68 | let matcher = be_uppercase(); 69 | matcher.test(&"GOSELECT").passed.should_be_true(); 70 | } 71 | 72 | #[test] 73 | #[should_panic] 74 | fn should_be_uppercase_but_was_not() { 75 | let matcher = be_uppercase(); 76 | matcher.test(&"GoSelect").passed.should_be_true(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/matchers/string/boundary.rs: -------------------------------------------------------------------------------- 1 | use crate::matchers::{Matcher, MatcherResult}; 2 | 3 | /// BoundaryMatcher offers a flexible way to assert that a string begins or ends with specific values. 4 | /// 5 | /// # Example 6 | ///``` 7 | /// use clearcheck::matchers::Matcher; 8 | /// use clearcheck::matchers::string::boundary::begin_with; 9 | /// 10 | /// let matcher = begin_with("clear"); 11 | /// assert!(matcher.test(&"clearcheck").passed()); 12 | /// ``` 13 | pub enum BoundaryMatcher { 14 | Begin(&'static str), 15 | End(&'static str), 16 | } 17 | 18 | impl Matcher for BoundaryMatcher 19 | where T: AsRef 20 | { 21 | fn test(&self, value: &T) -> MatcherResult { 22 | match self { 23 | BoundaryMatcher::Begin(prefix) => MatcherResult::formatted( 24 | value.as_ref().starts_with(prefix), 25 | format!("{:?} should begin with {:?}", value.as_ref(), prefix), 26 | format!("{:?} should not begin with {:?}", value.as_ref(), prefix), 27 | ), 28 | BoundaryMatcher::End(suffix) => MatcherResult::formatted( 29 | value.as_ref().ends_with(suffix), 30 | format!("{:?} should end with {:?}", value.as_ref(), suffix), 31 | format!("{:?} should not end with {:?}", value.as_ref(), suffix), 32 | ), 33 | } 34 | } 35 | } 36 | 37 | /// Creates a BoundaryMatcher that asserts whether a string value begins with the given prefix. 38 | pub fn begin_with(prefix: &'static str) -> BoundaryMatcher { 39 | BoundaryMatcher::Begin(prefix) 40 | } 41 | 42 | /// Creates a BoundaryMatcher that asserts whether a string value ends with the given suffix. 43 | pub fn end_with(suffix: &'static str) -> BoundaryMatcher { 44 | BoundaryMatcher::End(suffix) 45 | } 46 | 47 | #[cfg(test)] 48 | mod tests { 49 | use crate::assertions::bool::TrueFalseAssertion; 50 | use crate::matchers::Matcher; 51 | use crate::matchers::string::boundary::{begin_with, end_with}; 52 | 53 | #[test] 54 | fn should_begin_with() { 55 | let matcher = begin_with("go"); 56 | matcher.test(&"goselect").passed.should_be_true(); 57 | } 58 | 59 | #[test] 60 | #[should_panic] 61 | fn should_begin_with_but_did_not() { 62 | let matcher = begin_with("go"); 63 | matcher.test(&"select").passed.should_be_true(); 64 | } 65 | 66 | #[test] 67 | fn should_end_with() { 68 | let matcher = end_with("elect"); 69 | matcher.test(&"goselect").passed.should_be_true(); 70 | } 71 | 72 | #[test] 73 | #[should_panic] 74 | fn should_end_with_but_did_not() { 75 | let matcher = end_with("go"); 76 | matcher.test(&"select").passed.should_be_true(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/matchers/collection/duplicate.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use crate::matchers::{Matcher, MatcherResult}; 4 | 5 | /// DuplicateContentMatcher offers a flexible way to assert whether a collection contains any duplicates. 6 | /// 7 | /// clearcheck implements DuplicateContentMatcher for collection types including vector, arrays and reference to slices. 8 | /// 9 | /// # Example 10 | ///``` 11 | /// use clearcheck::matchers::collection::duplicate::contain_duplicates; 12 | /// use clearcheck::matchers::Matcher; 13 | /// 14 | /// let matcher = contain_duplicates(); 15 | /// let collection = vec!["junit", "clearcheck", "junit"]; 16 | /// 17 | /// assert!(matcher.test(&collection).passed()); 18 | /// ``` 19 | pub struct DuplicateContentMatcher; 20 | 21 | impl DuplicateContentMatcher { 22 | fn test(&self, collection: &[T]) -> MatcherResult { 23 | let mut unique = Vec::new(); 24 | collection.iter().for_each(|source| { 25 | if !unique.contains(&source) { 26 | unique.push(source) 27 | } 28 | }); 29 | 30 | MatcherResult::formatted( 31 | unique.len() != collection.len(), 32 | format!("{:?} should have duplicates", collection), 33 | format!("{:?} should not have duplicates", collection), 34 | ) 35 | } 36 | } 37 | 38 | impl Matcher> for DuplicateContentMatcher { 39 | fn test(&self, collection: &Vec) -> MatcherResult { 40 | self.test(collection) 41 | } 42 | } 43 | 44 | impl Matcher<[T; N]> for DuplicateContentMatcher { 45 | fn test(&self, collection: &[T; N]) -> MatcherResult { 46 | self.test(collection as &[T]) 47 | } 48 | } 49 | 50 | impl Matcher<&[T]> for DuplicateContentMatcher { 51 | fn test(&self, collection: &&[T]) -> MatcherResult { 52 | self.test(collection) 53 | } 54 | } 55 | 56 | /// Creates a DuplicateContentMatcher that asserts whether the underlying collection contains any duplicates. 57 | pub fn contain_duplicates() -> DuplicateContentMatcher { 58 | DuplicateContentMatcher 59 | } 60 | 61 | #[cfg(test)] 62 | mod tests { 63 | use crate::assertions::bool::TrueFalseAssertion; 64 | use crate::matchers::collection::duplicate::contain_duplicates; 65 | 66 | #[test] 67 | fn should_contains_duplicates() { 68 | let matcher = contain_duplicates(); 69 | let collection = vec!["junit", "assert4j", "junit"]; 70 | matcher.test(&collection).passed.should_be_true(); 71 | } 72 | 73 | #[test] 74 | #[should_panic] 75 | fn should_contains_duplicates_but_it_did_not() { 76 | let matcher = contain_duplicates(); 77 | let collection = vec!["junit", "assert4j", ""]; 78 | matcher.test(&collection).passed.should_be_true(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/matchers/map/empty.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::hash::Hash; 3 | 4 | use crate::matchers::{Matcher, MatcherResult}; 5 | 6 | /// MapEmptyMatcher offers a flexible way to assert whether a HashMap is empty. 7 | /// 8 | /// # Example 9 | ///``` 10 | /// use std::collections::HashMap; 11 | /// use clearcheck::matchers::map::empty::be_empty; 12 | /// use clearcheck::matchers::Matcher; 13 | /// 14 | /// let key_value: HashMap = HashMap::new(); 15 | /// let matcher = be_empty(); 16 | /// 17 | /// assert!(matcher.test(&key_value).passed()); 18 | /// ``` 19 | pub enum MapEmptyMatcher { 20 | Empty, 21 | NotEmpty, 22 | } 23 | 24 | impl Matcher> for MapEmptyMatcher { 25 | fn test(&self, collection: &HashMap) -> MatcherResult { 26 | match self { 27 | MapEmptyMatcher::Empty => MatcherResult::new( 28 | collection.is_empty(), 29 | "Map should be empty", 30 | "Map should not be empty", 31 | ), 32 | MapEmptyMatcher::NotEmpty => MatcherResult::new( 33 | !collection.is_empty(), 34 | "Map should not be empty", 35 | "Map should be empty", 36 | ), 37 | } 38 | } 39 | } 40 | 41 | /// Creates a MapEmptyMatcher that asserts whether a HashMap is empty. 42 | pub fn be_empty() -> MapEmptyMatcher { 43 | MapEmptyMatcher::Empty 44 | } 45 | 46 | /// Creates a MapEmptyMatcher that asserts whether a HashMap is not empty. 47 | pub fn not_be_empty() -> MapEmptyMatcher { 48 | MapEmptyMatcher::NotEmpty 49 | } 50 | 51 | #[cfg(test)] 52 | mod map_tests { 53 | use std::collections::HashMap; 54 | 55 | use crate::assertions::bool::TrueFalseAssertion; 56 | use crate::matchers::map::empty::{be_empty, not_be_empty}; 57 | use crate::matchers::Matcher; 58 | 59 | #[test] 60 | fn should_be_empty() { 61 | let key_value: HashMap = HashMap::new(); 62 | let matcher = be_empty(); 63 | matcher.test(&key_value).passed.should_be_true(); 64 | } 65 | 66 | #[test] 67 | #[should_panic] 68 | fn should_be_empty_but_was_not() { 69 | let mut key_value: HashMap<&str, &str> = HashMap::new(); 70 | key_value.insert("java", "junit"); 71 | 72 | let matcher = be_empty(); 73 | matcher.test(&key_value).passed.should_be_true(); 74 | } 75 | 76 | #[test] 77 | fn should_not_be_empty() { 78 | let mut key_value: HashMap<&str, &str> = HashMap::new(); 79 | key_value.insert("java", "junit"); 80 | 81 | let matcher = not_be_empty(); 82 | matcher.test(&key_value).passed.should_be_true(); 83 | } 84 | 85 | #[test] 86 | #[should_panic] 87 | fn should_not_be_empty_but_was() { 88 | let key_value: HashMap<&str, &str> = HashMap::new(); 89 | 90 | let matcher = not_be_empty(); 91 | matcher.test(&key_value).passed.should_be_true(); 92 | } 93 | } -------------------------------------------------------------------------------- /src/assertions/string/case.rs: -------------------------------------------------------------------------------- 1 | use crate::matchers::Should; 2 | use crate::matchers::string::case::{be_lowercase, be_uppercase}; 3 | 4 | /// CaseAssertion enables assertions about whether a string (or str) is lowercase or uppercase. 5 | pub trait CaseAssertion { 6 | /// - Asserts that the string is lowercase. 7 | /// - Returns a reference to self for fluent chaining. 8 | /// - Panics if the assertion fails. 9 | /// # Example 10 | /// ``` 11 | /// use clearcheck::assertions::string::case::CaseAssertion; 12 | /// 13 | /// let name = "clearcheck"; 14 | /// name.should_be_lower_case(); 15 | /// ``` 16 | fn should_be_lower_case(&self) -> &Self; 17 | 18 | /// - Asserts that the string is uppercase. 19 | /// - Returns a reference to self for fluent chaining. 20 | /// - Panics if the assertion fails. 21 | /// # Example 22 | /// ``` 23 | /// use clearcheck::assertions::string::case::CaseAssertion; 24 | /// 25 | /// let name = "CLEARCHECK"; 26 | /// name.should_be_upper_case(); 27 | /// ``` 28 | fn should_be_upper_case(&self) -> &Self; 29 | } 30 | 31 | impl CaseAssertion for T 32 | where T: AsRef + PartialEq { 33 | fn should_be_lower_case(&self) -> &Self { 34 | self.should(&be_lowercase()); 35 | self 36 | } 37 | 38 | fn should_be_upper_case(&self) -> &Self { 39 | self.should(&be_uppercase()); 40 | self 41 | } 42 | } 43 | 44 | #[cfg(test)] 45 | mod tests { 46 | use crate::assertions::string::case::CaseAssertion; 47 | 48 | #[test] 49 | fn should_be_lower_case() { 50 | let name = "assert4j"; 51 | name.should_be_lower_case(); 52 | } 53 | 54 | #[test] 55 | #[should_panic] 56 | fn should_be_lower_case_but_was_not() { 57 | let name = "assert4J"; 58 | name.should_be_lower_case(); 59 | } 60 | 61 | #[test] 62 | fn should_be_upper_case() { 63 | let name = "ASSERT4J"; 64 | name.should_be_upper_case(); 65 | } 66 | 67 | #[test] 68 | #[should_panic] 69 | fn should_be_upper_case_but_was_not() { 70 | let name = "assert4J"; 71 | name.should_be_upper_case(); 72 | } 73 | } 74 | 75 | #[cfg(test)] 76 | mod string_tests { 77 | use crate::assertions::string::case::CaseAssertion; 78 | 79 | #[test] 80 | fn should_be_lower_case() { 81 | let name = String::from("assert4j"); 82 | name.should_be_lower_case(); 83 | } 84 | 85 | #[test] 86 | #[should_panic] 87 | fn should_be_lower_case_but_was_not() { 88 | let name = String::from("ASSERT4J"); 89 | name.should_be_lower_case(); 90 | } 91 | 92 | #[test] 93 | fn should_be_upper_case() { 94 | let name = String::from("ASSERT4J"); 95 | name.should_be_upper_case(); 96 | } 97 | 98 | #[test] 99 | #[should_panic] 100 | fn should_be_upper_case_but_was_not() { 101 | let name = String::from("assert4J"); 102 | name.should_be_upper_case(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/assertions/string/numeric.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use crate::matchers::{Should, ShouldNot}; 4 | use crate::matchers::string::numeric::be_numeric; 5 | 6 | /// NumericAssertion enables assertions about whether a string (or str) is numeric. 7 | pub trait NumericAssertion { 8 | /// - Asserts that the string is numeric. 9 | /// - Returns a reference to self for fluent chaining. 10 | /// - Panics if the assertion fails. 11 | /// # Example 12 | /// ``` 13 | /// use clearcheck::assertions::string::numeric::NumericAssertion; 14 | /// 15 | /// let value = "12345"; 16 | /// value.should_be_numeric::(); 17 | /// ``` 18 | fn should_be_numeric(&self) -> &Self; 19 | 20 | /// - Asserts that the string is not numeric. 21 | /// - Returns a reference to self for fluent chaining. 22 | /// - Panics if the assertion fails. 23 | /// # Example 24 | /// ``` 25 | /// use clearcheck::assertions::string::numeric::NumericAssertion; 26 | /// 27 | /// let name = "assert4j"; 28 | /// name.should_not_be_numeric::(); 29 | /// ``` 30 | fn should_not_be_numeric(&self) -> &Self; 31 | } 32 | 33 | impl NumericAssertion for S 34 | where S: AsRef 35 | { 36 | fn should_be_numeric(&self) -> &Self { 37 | self.should(&be_numeric::()); 38 | self 39 | } 40 | 41 | fn should_not_be_numeric(&self) -> &Self { 42 | self.should_not(&be_numeric::()); 43 | self 44 | } 45 | } 46 | 47 | #[cfg(test)] 48 | mod tests { 49 | use crate::assertions::string::numeric::NumericAssertion; 50 | 51 | #[test] 52 | fn should_be_numeric() { 53 | let value = "1234"; 54 | value.should_be_numeric::(); 55 | } 56 | 57 | #[test] 58 | #[should_panic] 59 | fn should_be_numeric_but_was_not() { 60 | let value = "1234a"; 61 | value.should_be_numeric::(); 62 | } 63 | 64 | #[test] 65 | fn should_not_be_numeric() { 66 | let value = "1234a"; 67 | value.should_not_be_numeric::(); 68 | } 69 | 70 | #[test] 71 | #[should_panic] 72 | fn should_not_be_numeric_but_was() { 73 | let value = "1234"; 74 | value.should_not_be_numeric::(); 75 | } 76 | } 77 | 78 | #[cfg(test)] 79 | mod string_tests { 80 | use crate::assertions::string::numeric::NumericAssertion; 81 | 82 | #[test] 83 | fn should_be_numeric() { 84 | let value = String::from("1234"); 85 | value.should_be_numeric::(); 86 | } 87 | 88 | #[test] 89 | #[should_panic] 90 | fn should_be_numeric_but_was_not() { 91 | let value = String::from("1234a"); 92 | value.should_be_numeric::(); 93 | } 94 | 95 | #[test] 96 | fn should_not_be_numeric() { 97 | let value = String::from("1234a"); 98 | value.should_not_be_numeric::(); 99 | } 100 | 101 | #[test] 102 | #[should_panic] 103 | fn should_not_be_numeric_but_was() { 104 | let value = String::from("1234"); 105 | value.should_not_be_numeric::(); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/assertions/option/predicate.rs: -------------------------------------------------------------------------------- 1 | use crate::matchers::{Should, ShouldNot}; 2 | use crate::matchers::option::be_some; 3 | use crate::matchers::option::predicate::satisfy; 4 | 5 | /// SomePredicateAssertion enables assertions about whether the Option value is both Some and that the contained value meets or doesn't meet certain conditions defined by a predicate. 6 | pub trait SomePredicateAssertion { 7 | /// - Asserts that the Option value is Some and satisfies the given predicate. 8 | /// - Returns a reference to self for fluent chaining. 9 | /// - Panics if the assertion fails. 10 | /// # Example 11 | /// ``` 12 | /// use clearcheck::assertions::option::predicate::SomePredicateAssertion; 13 | /// 14 | /// let option = Some(100); 15 | /// option.should_be_some_and_satisfy(|value| value > &&50); 16 | /// ``` 17 | fn should_be_some_and_satisfy bool>(&self, predicate: F) -> &Self; 18 | 19 | /// - Asserts that the Option value is Some and does not satisfy the given predicate. 20 | /// - Returns a reference to self for fluent chaining. 21 | /// - Panics if the assertion fails. 22 | /// # Example 23 | /// ``` 24 | /// use clearcheck::assertions::option::predicate::SomePredicateAssertion; 25 | /// 26 | /// let option = Some(100); 27 | /// option.should_be_some_and_not_satisfy(|value| value > &&500); 28 | /// ``` 29 | fn should_be_some_and_not_satisfy bool>(&self, predicate: F) -> &Self; 30 | } 31 | 32 | impl SomePredicateAssertion for Option { 33 | fn should_be_some_and_satisfy bool>(&self, predicate: F) -> &Self { 34 | self.should(&be_some()); 35 | self.should(&satisfy(predicate)); 36 | self 37 | } 38 | 39 | fn should_be_some_and_not_satisfy bool>(&self, predicate: F) -> &Self { 40 | self.should(&be_some()); 41 | self.should_not(&satisfy(predicate)); 42 | self 43 | } 44 | } 45 | 46 | #[cfg(test)] 47 | mod tests { 48 | use crate::assertions::option::predicate::SomePredicateAssertion; 49 | 50 | #[test] 51 | fn should_be_some_and_satisfy_predicate() { 52 | let option = Some(100); 53 | option.should_be_some_and_satisfy(|value| value > &&50); 54 | } 55 | 56 | #[test] 57 | #[should_panic] 58 | fn should_be_some_and_satisfy_predicate_but_it_did_not() { 59 | let option = Some(100); 60 | option.should_be_some_and_satisfy(|value| value > &&100); 61 | } 62 | 63 | #[test] 64 | #[should_panic] 65 | fn should_be_some_and_satisfy_predicate_but_it_was_none() { 66 | let option: Option = None; 67 | option.should_be_some_and_satisfy(|value| value > &&100); 68 | } 69 | 70 | #[test] 71 | fn should_be_some_and_not_satisfy_predicate() { 72 | let option = Some(100); 73 | option.should_be_some_and_not_satisfy(|value| value > &&500); 74 | } 75 | 76 | #[test] 77 | #[should_panic] 78 | fn should_be_some_and_not_satisfy_predicate_but_it_did() { 79 | let option = Some(100); 80 | option.should_be_some_and_not_satisfy(|value| value > &&50); 81 | } 82 | 83 | #[test] 84 | #[should_panic] 85 | fn should_be_some_and_not_satisfy_predicate_but_it_was_none() { 86 | let option: Option = None; 87 | option.should_be_some_and_not_satisfy(|value| value > &&100); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/assertions/result/predicate.rs: -------------------------------------------------------------------------------- 1 | use crate::matchers::{Should, ShouldNot}; 2 | use crate::matchers::result::predicate::satisfy; 3 | use crate::matchers::result::be_ok; 4 | 5 | /// OkPredicateAssertion enables assertions about whether the Result value is both Ok and that the contained value meets or doesn't meet certain conditions defined by a predicate. 6 | pub trait OkPredicateAssertion { 7 | /// - Asserts that the Result value is Ok and satisfies the given predicate. 8 | /// - Returns a reference to self for fluent chaining. 9 | /// - Panics if the assertion fails. 10 | /// # Example 11 | /// ``` 12 | /// use clearcheck::assertions::result::predicate::OkPredicateAssertion; 13 | /// 14 | /// let value: Result = Ok(1000); 15 | /// value.should_be_ok_and_satisfy(|value| value > &50); 16 | /// ``` 17 | fn should_be_ok_and_satisfy bool>(&self, predicate: F) -> &Self; 18 | 19 | /// - Asserts that the Result value is Ok and does not satisfy the given predicate. 20 | /// - Returns a reference to self for fluent chaining. 21 | /// - Panics if the assertion fails. 22 | /// # Example 23 | /// ``` 24 | /// use clearcheck::assertions::result::predicate::OkPredicateAssertion; 25 | /// 26 | /// let value: Result = Ok(100); 27 | /// value.should_be_ok_and_not_satisfy(|value| value > &500); 28 | /// ``` 29 | fn should_be_ok_and_not_satisfy bool>(&self, predicate: F) -> &Self; 30 | } 31 | 32 | impl OkPredicateAssertion for Result { 33 | fn should_be_ok_and_satisfy bool>(&self, predicate: F) -> &Self { 34 | self.should(&be_ok()); 35 | self.should(&satisfy(predicate)); 36 | self 37 | } 38 | 39 | fn should_be_ok_and_not_satisfy bool>(&self, predicate: F) -> &Self { 40 | self.should(&be_ok()); 41 | self.should_not(&satisfy(predicate)); 42 | self 43 | } 44 | } 45 | 46 | #[cfg(test)] 47 | mod tests { 48 | use crate::assertions::result::predicate::OkPredicateAssertion; 49 | 50 | #[test] 51 | fn should_be_ok_and_satisfy_predicate() { 52 | let value: Result = Ok(1000); 53 | value.should_be_ok_and_satisfy(|value| value > &50); 54 | } 55 | 56 | #[test] 57 | #[should_panic] 58 | fn should_be_ok_and_satisfy_predicate_but_it_did_not() { 59 | let value: Result = Ok(10); 60 | value.should_be_ok_and_satisfy(|value| value > &100); 61 | } 62 | 63 | #[test] 64 | #[should_panic] 65 | fn should_be_ok_and_satisfy_predicate_but_it_was_err() { 66 | let value: Result = Err("test error"); 67 | value.should_be_ok_and_satisfy(|value| value > &100); 68 | } 69 | 70 | #[test] 71 | fn should_be_ok_and_not_satisfy_predicate() { 72 | let value: Result = Ok(100); 73 | value.should_be_ok_and_not_satisfy(|value| value > &500); 74 | } 75 | 76 | #[test] 77 | #[should_panic] 78 | fn should_be_ok_and_not_satisfy_predicate_but_it_did() { 79 | let value: Result = Ok(100); 80 | value.should_be_ok_and_not_satisfy(|value| value > &50); 81 | } 82 | 83 | #[test] 84 | #[should_panic] 85 | fn should_be_ok_and_not_satisfy_predicate_but_it_was_err() { 86 | let value: Result = Err("test error"); 87 | value.should_be_ok_and_not_satisfy(|value| value > &100); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/assertions/string/equal.rs: -------------------------------------------------------------------------------- 1 | use crate::matchers::{Should, ShouldNot}; 2 | use crate::matchers::equal::be_equal_ignoring_case; 3 | 4 | /// IgnoreCaseEqualityAssertion enables assertions about whether a string (or str) equals other string, with case ignored. 5 | pub trait IgnoreCaseEqualityAssertion { 6 | /// - Asserts that the string equals other string, with case ignored. 7 | /// - Returns a reference to self for fluent chaining. 8 | /// - Panics if the assertion fails. 9 | /// # Example 10 | /// ``` 11 | /// use clearcheck::assertions::string::equal::IgnoreCaseEqualityAssertion; 12 | /// 13 | /// let name = "clearcheck"; 14 | /// name.should_be_equal_ignoring_case("CLearCheck"); 15 | /// ``` 16 | fn should_be_equal_ignoring_case(&self, other: &str) -> &Self; 17 | 18 | /// - Asserts that the string does not equal other string, with case ignored. 19 | /// - Returns a reference to self for fluent chaining. 20 | /// - Panics if the assertion fails. 21 | /// # Example 22 | /// ``` 23 | /// use clearcheck::assertions::string::equal::IgnoreCaseEqualityAssertion; 24 | /// 25 | /// let name = "clearcheck"; 26 | /// name.should_not_be_equal_ignoring_case("CLEARCHECK-001"); 27 | /// ``` 28 | fn should_not_be_equal_ignoring_case(&self, other: &str) -> &Self; 29 | } 30 | 31 | impl IgnoreCaseEqualityAssertion for T 32 | where T: AsRef { 33 | fn should_be_equal_ignoring_case(&self, other: &str) -> &Self { 34 | self.should(&be_equal_ignoring_case(other)); 35 | self 36 | } 37 | 38 | fn should_not_be_equal_ignoring_case(&self, other: &str) -> &Self { 39 | self.should_not(&be_equal_ignoring_case(other)); 40 | self 41 | } 42 | } 43 | 44 | #[cfg(test)] 45 | mod tests { 46 | use crate::assertions::string::equal::IgnoreCaseEqualityAssertion; 47 | 48 | #[test] 49 | fn should_be_equal() { 50 | let name = "john"; 51 | name.should_be_equal_ignoring_case("JOHN"); 52 | } 53 | 54 | #[test] 55 | #[should_panic] 56 | fn should_be_equal_but_was_not() { 57 | let name = "johnR"; 58 | name.should_be_equal_ignoring_case("JOHN"); 59 | } 60 | 61 | #[test] 62 | fn should_not_be_equal() { 63 | let name = "john"; 64 | name.should_not_be_equal_ignoring_case("JOHN-R"); 65 | } 66 | 67 | #[test] 68 | #[should_panic] 69 | fn should_not_be_equal_but_was() { 70 | let name = "john"; 71 | name.should_not_be_equal_ignoring_case("JOHN"); 72 | } 73 | } 74 | 75 | #[cfg(test)] 76 | mod string_tests { 77 | use crate::assertions::string::equal::IgnoreCaseEqualityAssertion; 78 | 79 | #[test] 80 | fn should_be_equal() { 81 | let name = String::from("john"); 82 | name.should_be_equal_ignoring_case("JOHN"); 83 | } 84 | 85 | #[test] 86 | #[should_panic] 87 | fn should_be_equal_but_was_not() { 88 | let name = String::from("johnR"); 89 | name.should_be_equal_ignoring_case("JOHN"); 90 | } 91 | 92 | #[test] 93 | fn should_not_be_equal() { 94 | let name = String::from("john"); 95 | name.should_not_be_equal_ignoring_case("JOHN-R"); 96 | } 97 | 98 | #[test] 99 | #[should_panic] 100 | fn should_not_be_equal_but_was() { 101 | let name = String::from("john"); 102 | name.should_not_be_equal_ignoring_case("JOHN"); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/matchers/collection/empty.rs: -------------------------------------------------------------------------------- 1 | use crate::matchers::{Matcher, MatcherResult}; 2 | 3 | /// CollectionEmptyMatcher offers a flexible way to assert whether a collection is empty. 4 | /// 5 | /// clearcheck implements CollectionEmptyMatcher for collection types including vector, arrays and reference to slices. 6 | /// 7 | /// # Example 8 | ///``` 9 | /// use clearcheck::matchers::collection::empty::be_empty; 10 | /// use clearcheck::matchers::Matcher; 11 | /// 12 | /// let collection: Vec = vec![]; 13 | /// let matcher = be_empty(); 14 | /// 15 | /// assert!(matcher.test(&collection).passed()); 16 | /// ``` 17 | pub enum CollectionEmptyMatcher { 18 | Empty, 19 | NotEmpty, 20 | } 21 | 22 | impl Matcher> for CollectionEmptyMatcher { 23 | fn test(&self, collection: &Vec) -> MatcherResult { 24 | self.test_length(collection) 25 | } 26 | } 27 | 28 | impl Matcher<[T; N]> for CollectionEmptyMatcher { 29 | fn test(&self, collection: &[T; N]) -> MatcherResult { 30 | self.test_length(collection as &[T]) 31 | } 32 | } 33 | 34 | impl Matcher<&[T]> for CollectionEmptyMatcher { 35 | fn test(&self, collection: &&[T]) -> MatcherResult { 36 | self.test_length(collection) 37 | } 38 | } 39 | 40 | impl CollectionEmptyMatcher { 41 | pub fn test_length(&self, collection: &[T]) -> MatcherResult { 42 | match self { 43 | CollectionEmptyMatcher::Empty => MatcherResult::new( 44 | collection.is_empty(), 45 | "Collection should be empty", 46 | "Collection should not be empty", 47 | ), 48 | CollectionEmptyMatcher::NotEmpty => MatcherResult::new( 49 | !collection.is_empty(), 50 | "Collection should not be empty", 51 | "Collection should be empty", 52 | ), 53 | } 54 | } 55 | } 56 | 57 | /// Creates a CollectionEmptyMatcher that asserts whether the underlying collection is empty. 58 | pub fn be_empty() -> CollectionEmptyMatcher { 59 | CollectionEmptyMatcher::Empty 60 | } 61 | 62 | /// Creates a CollectionEmptyMatcher that asserts whether the underlying collection is not empty. 63 | pub fn not_be_empty() -> CollectionEmptyMatcher { 64 | CollectionEmptyMatcher::NotEmpty 65 | } 66 | 67 | #[cfg(test)] 68 | mod collection_tests { 69 | use crate::assertions::bool::TrueFalseAssertion; 70 | use crate::matchers::collection::empty::{be_empty, not_be_empty}; 71 | use crate::matchers::Matcher; 72 | 73 | #[test] 74 | fn should_be_empty() { 75 | let collection: Vec = vec![]; 76 | let matcher = be_empty(); 77 | matcher.test(&collection).passed.should_be_true(); 78 | } 79 | 80 | #[test] 81 | #[should_panic] 82 | fn should_be_empty_but_was_not() { 83 | let collection = vec![1, 2, 3]; 84 | let matcher = be_empty(); 85 | matcher.test(&collection).passed.should_be_true(); 86 | } 87 | 88 | #[test] 89 | fn should_not_be_empty() { 90 | let collection = vec![1, 2, 3]; 91 | let matcher = not_be_empty(); 92 | matcher.test(&collection).passed.should_be_true(); 93 | } 94 | 95 | #[test] 96 | #[should_panic] 97 | fn should_not_be_empty_but_was() { 98 | let collection: Vec = vec![]; 99 | let matcher = not_be_empty(); 100 | matcher.test(&collection).passed.should_be_true(); 101 | } 102 | } -------------------------------------------------------------------------------- /src/matchers/collection/sort.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use crate::matchers::{Matcher, MatcherResult}; 4 | 5 | /// SortMatcher offers a flexible way to assert whether a collection is sorted in ascending or descending order. 6 | /// 7 | /// clearcheck implements SortMatcher for collection types including vector, arrays and reference to slices. 8 | /// 9 | /// # Example 10 | ///``` 11 | /// use clearcheck::matchers::collection::sort::{be_sorted_ascending, SortMatcher}; 12 | /// use clearcheck::matchers::Matcher; 13 | /// 14 | /// let matcher = be_sorted_ascending(); 15 | /// let collection = vec!["clearcheck", "junit"]; 16 | /// 17 | /// assert!(matcher.test(&collection).passed()); 18 | /// ``` 19 | pub enum SortMatcher { 20 | Ascending, 21 | Descending, 22 | } 23 | 24 | impl SortMatcher { 25 | fn test(&self, collection: &[T]) -> MatcherResult { 26 | match self { 27 | SortMatcher::Ascending => MatcherResult::formatted( 28 | (0..collection.len() - 1).all(|index| collection[index] <= collection[index + 1]), 29 | format!("{:?} should be sorted ascending", collection), 30 | format!("{:?} should not be sorted ascending", collection), 31 | ), 32 | SortMatcher::Descending => MatcherResult::formatted( 33 | (0..collection.len() - 1).all(|index| collection[index] >= collection[index + 1]), 34 | format!("{:?} should be sorted descending", collection), 35 | format!("{:?} should not be sorted descending", collection), 36 | ), 37 | } 38 | } 39 | } 40 | 41 | impl Matcher> for SortMatcher { 42 | fn test(&self, collection: &Vec) -> MatcherResult { 43 | self.test(collection) 44 | } 45 | } 46 | 47 | impl Matcher<[T; N]> for SortMatcher { 48 | fn test(&self, collection: &[T; N]) -> MatcherResult { 49 | self.test(collection as &[T]) 50 | } 51 | } 52 | 53 | impl Matcher<&[T]> for SortMatcher { 54 | fn test(&self, collection: &&[T]) -> MatcherResult { 55 | self.test(collection) 56 | } 57 | } 58 | 59 | /// Creates an SortMatcher that asserts whether the elements in a collection are sorted in ascending order. 60 | pub fn be_sorted_ascending() -> SortMatcher { 61 | SortMatcher::Ascending 62 | } 63 | 64 | /// Creates an SortMatcher that asserts whether the elements in a collection are sorted in descending order. 65 | pub fn be_sorted_descending() -> SortMatcher { 66 | SortMatcher::Descending 67 | } 68 | 69 | #[cfg(test)] 70 | mod tests { 71 | use crate::assertions::bool::TrueFalseAssertion; 72 | use crate::matchers::collection::sort::{be_sorted_ascending, be_sorted_descending}; 73 | 74 | #[test] 75 | fn should_be_sorted_ascending() { 76 | let matcher = be_sorted_ascending(); 77 | let collection = vec!["assert4j", "junit"]; 78 | matcher.test(&collection).passed.should_be_true(); 79 | } 80 | 81 | #[test] 82 | #[should_panic] 83 | fn should_be_sorted_ascending_but_was_not() { 84 | let matcher = be_sorted_ascending(); 85 | let collection = vec!["junit", "assert4j"]; 86 | matcher.test(&collection).passed.should_be_true(); 87 | } 88 | 89 | #[test] 90 | fn should_be_sorted_descending() { 91 | let matcher = be_sorted_descending(); 92 | let collection = vec!["junit", "assert4j"]; 93 | matcher.test(&collection).passed.should_be_true(); 94 | } 95 | 96 | #[test] 97 | #[should_panic] 98 | fn should_be_sorted_descending_but_was_not() { 99 | let matcher = be_sorted_descending(); 100 | let collection = vec!["assert4j", "junit"]; 101 | matcher.test(&collection).passed.should_be_true(); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/matchers/collection/bound.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use crate::matchers::{Matcher, MatcherResult}; 4 | 5 | /// BoundMatcher offers a flexible way to assert whether a value is bounded by either an upper or lower bound. 6 | /// 7 | /// Works with any data type that implements the PartialOrd trait. 8 | /// 9 | /// clearcheck implements BoundMatcher for collection types including vector, arrays and reference to slices. 10 | /// 11 | /// # Example 12 | ///``` 13 | /// use clearcheck::matchers::collection::bound::have_upper_bound; 14 | /// use clearcheck::matchers::Matcher; 15 | /// 16 | /// let matcher = have_upper_bound(4); 17 | /// let collection = vec![1, 2, 3, 4]; 18 | /// 19 | /// assert!(matcher.test(&collection).passed()); 20 | /// ``` 21 | pub enum BoundMatcher { 22 | Upper(T), 23 | Lower(T), 24 | } 25 | 26 | impl BoundMatcher 27 | where 28 | T: PartialOrd + Debug, 29 | { 30 | fn test(&self, collection: &[T]) -> MatcherResult { 31 | match self { 32 | BoundMatcher::Upper(bound) => MatcherResult::formatted( 33 | collection.iter().all(|source| source <= bound), 34 | format!("{:?} should have upper bound {:?}", collection, bound), 35 | format!("{:?} should not have upper bound {:?}", collection, bound), 36 | ), 37 | BoundMatcher::Lower(bound) => MatcherResult::formatted( 38 | collection.iter().all(|source| source >= bound), 39 | format!("{:?} should have lower bound {:?}", collection, bound), 40 | format!("{:?} should not have lower bound {:?}", collection, bound), 41 | ), 42 | } 43 | } 44 | } 45 | 46 | impl Matcher> for BoundMatcher { 47 | fn test(&self, collection: &Vec) -> MatcherResult { 48 | self.test(collection) 49 | } 50 | } 51 | 52 | impl Matcher<[T; N]> for BoundMatcher { 53 | fn test(&self, collection: &[T; N]) -> MatcherResult { 54 | self.test(collection as &[T]) 55 | } 56 | } 57 | 58 | impl Matcher<&[T]> for BoundMatcher { 59 | fn test(&self, collection: &&[T]) -> MatcherResult { 60 | self.test(collection) 61 | } 62 | } 63 | 64 | /// Creates a BoundMatcher that asserts whether a value has the given upper bound. 65 | pub fn have_upper_bound(bound: T) -> BoundMatcher { 66 | BoundMatcher::Upper(bound) 67 | } 68 | 69 | /// Creates a BoundMatcher that asserts whether a value has the given lower bound. 70 | pub fn have_lower_bound(bound: T) -> BoundMatcher { 71 | BoundMatcher::Lower(bound) 72 | } 73 | 74 | #[cfg(test)] 75 | mod tests { 76 | use crate::assertions::bool::TrueFalseAssertion; 77 | use crate::matchers::collection::bound::{have_lower_bound, have_upper_bound}; 78 | 79 | #[test] 80 | fn should_have_an_upper_bound() { 81 | let matcher = have_upper_bound(4); 82 | let collection = vec![1, 2, 3, 4]; 83 | 84 | matcher.test(&collection).passed.should_be_true(); 85 | } 86 | 87 | #[test] 88 | #[should_panic] 89 | fn should_have_an_upper_bound_but_was_not() { 90 | let matcher = have_upper_bound(3); 91 | let collection = vec![1, 2, 3, 4]; 92 | 93 | matcher.test(&collection).passed.should_be_true(); 94 | } 95 | 96 | #[test] 97 | fn should_have_a_lower_bound() { 98 | let matcher = have_lower_bound(1); 99 | let collection = vec![1, 2, 3, 4]; 100 | 101 | matcher.test(&collection).passed.should_be_true(); 102 | } 103 | 104 | #[test] 105 | #[should_panic] 106 | fn should_have_a_lower_bound_but_was_not() { 107 | let matcher = have_lower_bound(3); 108 | let collection = vec![1, 2, 3, 4]; 109 | 110 | matcher.test(&collection).passed.should_be_true(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/matchers/date/mod.rs: -------------------------------------------------------------------------------- 1 | use chrono::{Datelike, NaiveDate}; 2 | 3 | use crate::matchers::{Matcher, MatcherResult}; 4 | 5 | /// DateMatcher offers a flexible way to make assertions about specific date attributes. 6 | /// 7 | /// # Example 8 | ///``` 9 | /// use chrono::NaiveDate; 10 | /// use clearcheck::matchers::date::have_same_year; 11 | /// use clearcheck::matchers::Matcher; 12 | /// 13 | /// let date = NaiveDate::from_ymd_opt(2024, 1, 10).unwrap(); 14 | /// let matcher = have_same_year(2024); 15 | /// 16 | /// assert!(matcher.test(&date).passed()); 17 | /// ``` 18 | pub enum DateMatcher { 19 | SameYear(i32), 20 | SameMonth(u32), 21 | SameDay(u32), 22 | LeapYear, 23 | } 24 | 25 | impl Matcher for DateMatcher { 26 | fn test(&self, value: &NaiveDate) -> MatcherResult { 27 | match self { 28 | DateMatcher::SameYear(other) => MatcherResult::formatted( 29 | value.year() == *other, 30 | format!("{:?} should have the same year as {:?}", value, other), 31 | format!("{:?} should not have the same year as {:?}", value, other), 32 | ), 33 | DateMatcher::SameMonth(other) => MatcherResult::formatted( 34 | value.month() == *other, 35 | format!("{:?} should have the same month as {:?}", value, other), 36 | format!("{:?} should not have the same month as {:?}", value, other), 37 | ), 38 | DateMatcher::SameDay(other) => MatcherResult::formatted( 39 | value.day() == *other, 40 | format!("{:?} should have the same day as {:?}", value, other), 41 | format!("{:?} should not have the same day as {:?}", value, other), 42 | ), 43 | DateMatcher::LeapYear => MatcherResult::formatted( 44 | value.leap_year(), 45 | format!("{:?} should be a leap year", value), 46 | format!("{:?} should not be a leap year", value), 47 | ), 48 | } 49 | } 50 | } 51 | 52 | /// Creates a DateMatcher that asserts whether a date has the same year as the given year. 53 | pub fn have_same_year(year: i32) -> DateMatcher { 54 | DateMatcher::SameYear(year) 55 | } 56 | 57 | /// Creates a DateMatcher that asserts whether a date has the same month as the given month. 58 | pub fn have_same_month(month: u32) -> DateMatcher { 59 | DateMatcher::SameMonth(month) 60 | } 61 | 62 | /// Creates a DateMatcher that asserts whether a date has the same day as the given day. 63 | pub fn have_same_day(day: u32) -> DateMatcher { 64 | DateMatcher::SameDay(day) 65 | } 66 | 67 | /// Creates a DateMatcher that asserts whether a date falls in the leap year. 68 | pub fn be_a_leap_year() -> DateMatcher { 69 | DateMatcher::LeapYear 70 | } 71 | 72 | #[cfg(all(test, feature = "date"))] 73 | mod tests { 74 | use crate::assertions::bool::TrueFalseAssertion; 75 | use crate::matchers::date::{be_a_leap_year, have_same_day, have_same_month, have_same_year}; 76 | use crate::matchers::Matcher; 77 | use chrono::NaiveDate; 78 | 79 | #[test] 80 | fn should_have_same_year() { 81 | let date = NaiveDate::from_ymd_opt(2024, 1, 10).unwrap(); 82 | let matcher = have_same_year(2024); 83 | matcher.test(&date).passed.should_be_true(); 84 | } 85 | 86 | #[test] 87 | fn should_have_same_month() { 88 | let date = NaiveDate::from_ymd_opt(2024, 1, 10).unwrap(); 89 | let matcher = have_same_month(1); 90 | matcher.test(&date).passed.should_be_true(); 91 | } 92 | 93 | #[test] 94 | fn should_have_same_day() { 95 | let date = NaiveDate::from_ymd_opt(2024, 1, 10).unwrap(); 96 | let matcher = have_same_day(10); 97 | matcher.test(&date).passed.should_be_true(); 98 | } 99 | 100 | #[test] 101 | fn should_be_a_leap_year() { 102 | let date = NaiveDate::from_ymd_opt(2020, 1, 10).unwrap(); 103 | let matcher = be_a_leap_year(); 104 | matcher.test(&date).passed.should_be_true(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/matchers/equal.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use crate::matchers::{Matcher, MatcherResult}; 4 | 5 | /// EqualityMatcher offers a flexible way to assert the equality between two values of the same type. 6 | /// 7 | /// Works with any data type that implements the Eq trait. 8 | /// 9 | /// clearcheck implements EqualityMatcher for any T: Eq + Debug. 10 | /// 11 | /// # Example 12 | ///``` 13 | /// use clearcheck::matchers::equal::be_equal; 14 | /// use clearcheck::matchers::Matcher; 15 | /// 16 | /// #[derive(Debug, Eq, PartialEq)] 17 | /// struct Book { 18 | /// name: &'static str, 19 | /// } 20 | /// 21 | /// let books = [ 22 | /// Book { name: "Database internals"}, 23 | /// Book { name: "Rust in action" }, 24 | /// ]; 25 | /// let matcher = be_equal([ 26 | /// Book { name: "Database internals" }, 27 | /// Book { name: "Rust in action" }, 28 | /// ]); 29 | /// 30 | /// assert!(matcher.test(&books).passed()); 31 | /// ``` 32 | pub struct EqualityMatcher { 33 | pub other: T, 34 | } 35 | 36 | /// IgnoreCaseEqualityMatcher offers a flexible way to assert the equality between two values of same type, ignoring case differences. 37 | /// 38 | /// clearcheck implements IgnoreCaseEqualityMatcher for the following: 39 | /// - char 40 | /// - &str 41 | /// - `Vec` where T: `AsRef` + Debug + Eq, 42 | /// - `&[T]` where T: `AsRef` + Debug + Eq, 43 | /// - [String; N] and [&str; N] 44 | /// 45 | /// # Example 46 | ///``` 47 | /// use clearcheck::matchers::equal::be_equal_ignoring_case; 48 | /// use clearcheck::matchers::Matcher; 49 | /// 50 | /// let collection = vec!["junit", "CLEARCHECK"]; 51 | /// let matcher = be_equal_ignoring_case(vec!["JUNIT", "CLEARCHECK"]); 52 | /// 53 | /// assert!(matcher.test(&collection).passed()); 54 | /// ``` 55 | pub struct IgnoreCaseEqualityMatcher { 56 | pub other: T, 57 | } 58 | 59 | /// Creates an EqualityMatcher that asserts whether a value equals the given value. 60 | pub fn be_equal(other: T) -> EqualityMatcher { 61 | EqualityMatcher { other } 62 | } 63 | 64 | /// Creates an IgnoreCaseEqualityMatcher that asserts whether a value equals the given value, ignoring case differences. 65 | pub fn be_equal_ignoring_case(other: T) -> IgnoreCaseEqualityMatcher { 66 | IgnoreCaseEqualityMatcher { other } 67 | } 68 | 69 | impl Matcher for EqualityMatcher { 70 | fn test(&self, value: &T) -> MatcherResult { 71 | MatcherResult::formatted( 72 | value == &self.other, 73 | format!("{:?} should equal {:?}", value, self.other), 74 | format!("{:?} should not equal {:?}", value, self.other), 75 | ) 76 | } 77 | } 78 | 79 | #[cfg(test)] 80 | mod tests { 81 | use crate::assertions::bool::TrueFalseAssertion; 82 | use crate::matchers::equal::be_equal; 83 | use crate::matchers::Matcher; 84 | 85 | #[derive(Debug, Eq, PartialEq)] 86 | struct Book { 87 | name: &'static str, 88 | } 89 | 90 | #[test] 91 | fn should_equal() { 92 | let books = [ 93 | Book { 94 | name: "Database internals", 95 | }, 96 | Book { 97 | name: "Rust in action", 98 | }, 99 | ]; 100 | let matcher = be_equal([ 101 | Book { 102 | name: "Database internals", 103 | }, 104 | Book { 105 | name: "Rust in action", 106 | }, 107 | ]); 108 | matcher.test(&books).passed.should_be_true(); 109 | } 110 | 111 | #[test] 112 | #[should_panic] 113 | fn should_not_equal() { 114 | let books = vec![ 115 | Book { 116 | name: "Database internals", 117 | }, 118 | Book { 119 | name: "Rust in action", 120 | }, 121 | ]; 122 | let target = vec![Book { 123 | name: "Database internals", 124 | }]; 125 | 126 | let matcher = be_equal(target); 127 | matcher.test(&books).passed.should_be_true(); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/matchers/range/mod.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::ops::{Range, RangeInclusive}; 3 | 4 | use crate::matchers::{Matcher, MatcherResult}; 5 | 6 | /// RangeMatcher offers a flexible way to assert whether a value falls within a specified range. 7 | /// 8 | /// Supports both closed ranges (inclusive of endpoints) and half-open ranges (exclusive of the upper endpoint). 9 | /// 10 | /// Works with any data type that implements the Debug and PartialOrd traits. 11 | /// 12 | /// # Example 13 | ///``` 14 | /// use clearcheck::matchers::Matcher; 15 | /// use clearcheck::matchers::range::be_in_inclusive_range; 16 | /// 17 | /// let matcher = be_in_inclusive_range(1..=4); 18 | /// assert!(matcher.test(&2).passed()); 19 | /// ``` 20 | pub enum RangeMatcher { 21 | Closed(&'static str, RangeInclusive), 22 | HalfOpen(&'static str, Range), 23 | } 24 | 25 | impl Matcher for RangeMatcher 26 | where 27 | T: PartialOrd + Debug, 28 | { 29 | fn test(&self, value: &T) -> MatcherResult { 30 | match self { 31 | RangeMatcher::Closed(message_prefix, range) => MatcherResult::formatted( 32 | range.contains(value), 33 | format!( 34 | "{:?} {:?} should fall in the range {:?}", 35 | message_prefix, value, range 36 | ), 37 | format!( 38 | "{:?} {:?} should not fall in the range {:?}", 39 | message_prefix, value, range 40 | ), 41 | ), 42 | RangeMatcher::HalfOpen(message_prefix, range) => MatcherResult::formatted( 43 | range.contains(value), 44 | format!( 45 | "{:?} {:?} should fall in the range {:?}", 46 | message_prefix, value, range 47 | ), 48 | format!( 49 | "{:?} {:?} should not fall in the range {:?}", 50 | message_prefix, value, range 51 | ), 52 | ), 53 | } 54 | } 55 | } 56 | 57 | /// Creates a RangeMatcher that asserts whether a value falls within the given inclusive range. 58 | pub fn be_in_inclusive_range(range: RangeInclusive) -> RangeMatcher { 59 | RangeMatcher::Closed("Value", range) 60 | } 61 | 62 | /// Creates a RangeMatcher that asserts whether a value falls within the given exclusive range. 63 | pub fn be_in_exclusive_range(range: Range) -> RangeMatcher { 64 | RangeMatcher::HalfOpen("Value", range) 65 | } 66 | 67 | /// Creates a RangeMatcher that asserts whether the length (of collection, hashmap, string) falls within the given inclusive range. 68 | pub fn have_length_in_inclusive_range(range: RangeInclusive) -> RangeMatcher { 69 | RangeMatcher::Closed("Length", range) 70 | } 71 | 72 | /// Creates a RangeMatcher that asserts whether the length (of collection, hashmap, string) falls within the given exclusive range. 73 | pub fn have_length_in_exclusive_range(range: Range) -> RangeMatcher { 74 | RangeMatcher::HalfOpen("Length", range) 75 | } 76 | 77 | #[cfg(test)] 78 | mod tests { 79 | use crate::assertions::bool::TrueFalseAssertion; 80 | use crate::matchers::range::{be_in_exclusive_range, be_in_inclusive_range}; 81 | use crate::matchers::Matcher; 82 | 83 | #[test] 84 | fn should_be_in_inclusive_range() { 85 | let matcher = be_in_inclusive_range(1..=4); 86 | matcher.test(&2).passed.should_be_true(); 87 | } 88 | 89 | #[test] 90 | #[should_panic] 91 | fn should_be_in_inclusive_range_but_was_not() { 92 | let matcher = be_in_inclusive_range(1..=4); 93 | matcher.test(&5).passed.should_be_true(); 94 | } 95 | 96 | #[test] 97 | fn should_be_in_exclusive_range() { 98 | let matcher = be_in_exclusive_range(1..4); 99 | matcher.test(&2).passed.should_be_true(); 100 | } 101 | 102 | #[test] 103 | #[should_panic] 104 | fn should_be_in_exclusive_range_but_was_not() { 105 | let matcher = be_in_exclusive_range(1..4); 106 | matcher.test(&4).passed.should_be_true(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/assertions/collection/bound.rs: -------------------------------------------------------------------------------- 1 | use crate::matchers::collection::bound::{have_lower_bound, have_upper_bound}; 2 | use crate::matchers::Should; 3 | 4 | /// BoundAssertion enables assertions about the expected bounds on elements within a collection. 5 | pub trait BoundAssertion 6 | where 7 | T: PartialOrd 8 | { 9 | /// - Asserts that all elements in the collection are less than or equal to the given element. 10 | /// - Returns a reference to self for fluent chaining. 11 | /// - Panics if the assertion fails. 12 | /// # Example 13 | /// ``` 14 | /// use clearcheck::assertions::collection::bound::BoundAssertion; 15 | /// 16 | /// let collection = vec![1, 2, 3, 4]; 17 | /// collection.should_have_upper_bound(4); 18 | /// ``` 19 | fn should_have_upper_bound(&self, element: T) -> &Self; 20 | 21 | /// - Asserts that all elements in the collection are greater than or equal to the given element. 22 | /// - Returns a reference to self for fluent chaining. 23 | /// - Panics if the assertion fails. 24 | /// # Example 25 | /// ``` 26 | /// use clearcheck::assertions::collection::bound::BoundAssertion; 27 | /// 28 | /// let collection = vec![1, 2, 3, 4]; 29 | /// collection.should_have_lower_bound(1); 30 | /// ``` 31 | fn should_have_lower_bound(&self, element: T) -> &Self; 32 | } 33 | 34 | impl BoundAssertion for Vec 35 | where 36 | T: std::fmt::Debug, 37 | T: PartialOrd, 38 | { 39 | fn should_have_upper_bound(&self, element: T) -> &Self { 40 | (self as &[T]).should_have_upper_bound(element); 41 | self 42 | } 43 | 44 | fn should_have_lower_bound(&self, element: T) -> &Self { 45 | (self as &[T]).should_have_lower_bound(element); 46 | self 47 | } 48 | } 49 | 50 | impl BoundAssertion for [T; N] 51 | where 52 | T: std::fmt::Debug, 53 | T: PartialOrd, 54 | { 55 | fn should_have_upper_bound(&self, element: T) -> &Self { 56 | (self as &[T]).should_have_upper_bound(element); 57 | self 58 | } 59 | 60 | fn should_have_lower_bound(&self, element: T) -> &Self { 61 | (self as &[T]).should_have_lower_bound(element); 62 | self 63 | } 64 | } 65 | 66 | impl BoundAssertion for [T] 67 | where 68 | T: std::fmt::Debug, 69 | T: PartialOrd, 70 | { 71 | fn should_have_upper_bound(&self, element: T) -> &Self { 72 | self.should(&have_upper_bound(element)); 73 | self 74 | } 75 | 76 | fn should_have_lower_bound(&self, element: T) -> &Self { 77 | self.should(&have_lower_bound(element)); 78 | self 79 | } 80 | } 81 | 82 | #[cfg(test)] 83 | mod tests { 84 | use crate::assertions::collection::bound::BoundAssertion; 85 | 86 | #[test] 87 | fn should_have_an_upper_bound() { 88 | let collection = vec![1, 2, 3, 4]; 89 | collection.should_have_upper_bound(4); 90 | } 91 | 92 | #[test] 93 | #[should_panic] 94 | fn should_have_an_upper_bound_but_was_not() { 95 | let collection = vec![1, 2, 3, 4]; 96 | collection.should_have_upper_bound(1); 97 | } 98 | 99 | #[test] 100 | fn should_have_a_lower_bound() { 101 | let collection = vec![1, 2, 3, 4]; 102 | collection.should_have_lower_bound(1); 103 | } 104 | 105 | #[test] 106 | #[should_panic] 107 | fn should_have_a_lower_bound_but_was_not() { 108 | let collection = vec![1, 2, 3, 4]; 109 | collection.should_have_lower_bound(3); 110 | } 111 | } 112 | 113 | #[cfg(test)] 114 | mod array_tests { 115 | use crate::assertions::collection::bound::BoundAssertion; 116 | 117 | #[test] 118 | fn should_have_an_upper_bound() { 119 | let collection = [1, 2, 3, 4]; 120 | collection.should_have_upper_bound(4); 121 | } 122 | 123 | #[test] 124 | #[should_panic] 125 | fn should_have_an_upper_bound_but_was_not() { 126 | let collection = [1, 2, 3, 4]; 127 | collection.should_have_upper_bound(1); 128 | } 129 | 130 | #[test] 131 | fn should_have_a_lower_bound() { 132 | let collection = [1, 2, 3, 4]; 133 | collection.should_have_lower_bound(1); 134 | } 135 | 136 | #[test] 137 | #[should_panic] 138 | fn should_have_a_lower_bound_but_was_not() { 139 | let collection = [1, 2, 3, 4]; 140 | collection.should_have_lower_bound(3); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/matchers/float/mod.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use num::Float; 4 | 5 | use crate::matchers::{Matcher, MatcherResult}; 6 | 7 | /// FloatMatcher offers a flexible way to make assertions about specific float attributes. 8 | /// 9 | /// # Example 10 | ///``` 11 | /// use clearcheck::matchers::float::be_zero; 12 | /// use clearcheck::matchers::Matcher; 13 | /// 14 | /// let value: f64 = 0.0; 15 | /// let matcher = be_zero(); 16 | /// 17 | /// assert!(matcher.test(&value).passed()); 18 | /// ``` 19 | pub enum FloatMatcher { 20 | NaN, 21 | Zero, 22 | Positive, 23 | Negative, 24 | } 25 | 26 | impl Matcher for FloatMatcher { 27 | fn test(&self, value: &T) -> MatcherResult { 28 | match self { 29 | FloatMatcher::NaN => MatcherResult::formatted( 30 | value.is_nan(), 31 | format!("{:?} should be NaN", value), 32 | format!("{:?} should not be NaN", value), 33 | ), 34 | FloatMatcher::Zero => MatcherResult::formatted( 35 | value.is_zero(), 36 | format!("{:?} should be zero", value), 37 | format!("{:?} should not be zero", value), 38 | ), 39 | FloatMatcher::Positive => MatcherResult::formatted( 40 | value.is_sign_positive(), 41 | format!("{:?} should be positive", value), 42 | format!("{:?} should not be positive", value), 43 | ), 44 | FloatMatcher::Negative => MatcherResult::formatted( 45 | value.is_sign_negative(), 46 | format!("{:?} should be negative", value), 47 | format!("{:?} should not be negative", value), 48 | ), 49 | } 50 | } 51 | } 52 | 53 | /// Creates a FloatMatcher that asserts whether a floating value is NaN (not a number). 54 | pub fn be_nan() -> FloatMatcher { 55 | FloatMatcher::NaN 56 | } 57 | 58 | /// Creates a FloatMatcher that asserts whether a floating value is zero. 59 | pub fn be_zero() -> FloatMatcher { 60 | FloatMatcher::Zero 61 | } 62 | 63 | /// Creates a FloatMatcher that asserts whether a floating value is positive. 64 | pub fn be_positive() -> FloatMatcher { 65 | FloatMatcher::Positive 66 | } 67 | 68 | /// Creates a FloatMatcher that asserts whether a floating value is negative. 69 | pub fn be_negative() -> FloatMatcher { 70 | FloatMatcher::Negative 71 | } 72 | 73 | #[cfg(all(test, feature = "num"))] 74 | mod tests { 75 | use crate::assertions::bool::TrueFalseAssertion; 76 | use crate::matchers::float::{be_nan, be_negative, be_positive, be_zero}; 77 | use crate::matchers::Matcher; 78 | use num::Float; 79 | 80 | #[test] 81 | fn should_be_nan() { 82 | let value: f64 = Float::nan(); 83 | let matcher = be_nan(); 84 | matcher.test(&value).passed.should_be_true(); 85 | } 86 | 87 | #[test] 88 | #[should_panic] 89 | fn should_be_nan_but_was_not() { 90 | let value: f64 = 1.10; 91 | let matcher = be_nan(); 92 | matcher.test(&value).passed.should_be_true(); 93 | } 94 | 95 | #[test] 96 | fn should_be_zero() { 97 | let value: f64 = 0.0; 98 | let matcher = be_zero(); 99 | matcher.test(&value).passed.should_be_true(); 100 | } 101 | 102 | #[test] 103 | #[should_panic] 104 | fn should_be_zero_but_was_not() { 105 | let value: f64 = 1.10; 106 | let matcher = be_zero(); 107 | matcher.test(&value).passed.should_be_true(); 108 | } 109 | 110 | #[test] 111 | fn should_be_positive() { 112 | let value: f64 = 1.0; 113 | let matcher = be_positive(); 114 | matcher.test(&value).passed.should_be_true(); 115 | } 116 | 117 | #[test] 118 | #[should_panic] 119 | fn should_be_positive_but_was_not() { 120 | let value: f64 = -1.10; 121 | let matcher = be_positive(); 122 | matcher.test(&value).passed.should_be_true(); 123 | } 124 | 125 | #[test] 126 | fn should_be_negative() { 127 | let value: f64 = -1.0; 128 | let matcher = be_negative(); 129 | matcher.test(&value).passed.should_be_true(); 130 | } 131 | 132 | #[test] 133 | #[should_panic] 134 | fn should_be_negative_but_was_not() { 135 | let value: f64 = 1.10; 136 | let matcher = be_negative(); 137 | matcher.test(&value).passed.should_be_true(); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/assertions/string/regex.rs: -------------------------------------------------------------------------------- 1 | use regex::Regex; 2 | 3 | use crate::matchers::{Should, ShouldNot}; 4 | use crate::matchers::string::regex::match_with; 5 | 6 | /// RegularExpressionAssertion enables assertions about whether a string (or str) matches a regular expression. 7 | pub trait RegularExpressionAssertion { 8 | /// - Asserts that the string matches the given regular expression. 9 | /// - Returns a reference to self for fluent chaining. 10 | /// - Panics if the assertion fails. 11 | /// # Example 12 | /// ``` 13 | /// use regex::Regex; 14 | /// use clearcheck::assertions::string::regex::RegularExpressionAssertion; 15 | /// 16 | /// let regex = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap(); 17 | /// let phrase = "Started clearcheck on 2024-01-02."; 18 | /// phrase.should_match(regex); 19 | /// ``` 20 | fn should_match(&self, regex: Regex) -> &Self; 21 | 22 | /// - Asserts that the string does not match the given regular expression. 23 | /// - Returns a reference to self for fluent chaining. 24 | /// - Panics if the assertion fails. 25 | /// # Example 26 | /// ``` 27 | /// use regex::Regex; 28 | /// use clearcheck::assertions::string::regex::RegularExpressionAssertion; 29 | /// 30 | /// let regex = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap(); 31 | /// let phrase = String::from("Started clearcheck on 02nd January 2024"); 32 | /// phrase.should_not_match(regex); 33 | /// ``` 34 | fn should_not_match(&self, regex: Regex) -> &Self; 35 | } 36 | 37 | impl RegularExpressionAssertion for T 38 | where T: AsRef { 39 | fn should_match(&self, regex: Regex) -> &Self { 40 | self.should(&match_with(regex)); 41 | self 42 | } 43 | 44 | fn should_not_match(&self, regex: Regex) -> &Self { 45 | self.should_not(&match_with(regex)); 46 | self 47 | } 48 | } 49 | 50 | #[cfg(all(test, feature = "regex"))] 51 | mod tests { 52 | use regex::Regex; 53 | 54 | use crate::assertions::string::regex::RegularExpressionAssertion; 55 | 56 | #[test] 57 | fn should_match_regular_expression() { 58 | let regex = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap(); 59 | let str = "Started clearcheck on On 2024-01-02."; 60 | str.should_match(regex); 61 | } 62 | 63 | #[test] 64 | #[should_panic] 65 | fn should_match_regular_expression_but_it_did_not() { 66 | let regex = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap(); 67 | let str = "Started clearcheck on On 02nd January 2024"; 68 | str.should_match(regex); 69 | } 70 | 71 | #[test] 72 | fn should_not_match_regular_expression() { 73 | let regex = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap(); 74 | let str = "Started clearcheck on On 02nd January 2024"; 75 | str.should_not_match(regex); 76 | } 77 | 78 | #[test] 79 | #[should_panic] 80 | fn should_not_match_regular_expression_but_it_did() { 81 | let regex = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap(); 82 | let str = "Started clearcheck on On 2024-01-02."; 83 | str.should_not_match(regex); 84 | } 85 | } 86 | 87 | #[cfg(all(test, feature = "regex"))] 88 | mod string_tests { 89 | use regex::Regex; 90 | 91 | use crate::assertions::string::regex::RegularExpressionAssertion; 92 | 93 | #[test] 94 | fn should_match_regular_expression() { 95 | let regex = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap(); 96 | let str = String::from("Started clearcheck on 2024-01-02."); 97 | str.should_match(regex); 98 | } 99 | 100 | #[test] 101 | #[should_panic] 102 | fn should_match_regular_expression_but_it_did_not() { 103 | let regex = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap(); 104 | let str = String::from("Started clearcheck on 02nd January 2024"); 105 | str.should_match(regex); 106 | } 107 | 108 | #[test] 109 | fn should_not_match_regular_expression() { 110 | let regex = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap(); 111 | let str = String::from("Started clearcheck on 02nd January 2024"); 112 | str.should_not_match(regex); 113 | } 114 | 115 | #[test] 116 | #[should_panic] 117 | fn should_not_match_regular_expression_but_it_did() { 118 | let regex = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap(); 119 | let str = String::from("Started clearcheck on 2024-01-02."); 120 | str.should_not_match(regex); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/matchers/string/length.rs: -------------------------------------------------------------------------------- 1 | use crate::matchers::{Matcher, MatcherResult}; 2 | 3 | /// StringLengthMatcher offers a flexible way to assert various length properties of string. 4 | /// 5 | /// # Example 6 | ///``` 7 | /// use clearcheck::matchers::string::length::have_atleast_same_length; 8 | /// use clearcheck::matchers::Matcher; 9 | /// 10 | /// let matcher = have_atleast_same_length(3); 11 | /// assert!(matcher.test(&"clearcheck").passed()); 12 | /// ``` 13 | pub enum StringLengthMatcher { 14 | Same(usize), 15 | Atleast(usize), 16 | Atmost(usize), 17 | } 18 | 19 | impl Matcher for StringLengthMatcher 20 | where T: AsRef 21 | { 22 | fn test(&self, value: &T) -> MatcherResult { 23 | match self { 24 | StringLengthMatcher::Same(input_length) => MatcherResult::formatted( 25 | value.as_ref().len() == *input_length, 26 | format!( 27 | "{:?} length {:?} should be {:?}", 28 | value.as_ref(), input_length, input_length, 29 | ), 30 | format!( 31 | "{:?} length {:?} should not be {:?}", 32 | value.as_ref(), input_length, input_length, 33 | ), 34 | ), 35 | StringLengthMatcher::Atleast(input_length) => MatcherResult::formatted( 36 | value.as_ref().len() >= *input_length, 37 | format!( 38 | "{:?} length {:?} should be atleast {:?}", 39 | value.as_ref(), input_length, input_length, 40 | ), 41 | format!( 42 | "{:?} length {:?} should not be atleast {:?}", 43 | value.as_ref(), input_length, input_length, 44 | ), 45 | ), 46 | StringLengthMatcher::Atmost(input_length) => MatcherResult::formatted( 47 | value.as_ref().len() <= *input_length, 48 | format!( 49 | "{:?} length {:?} should be atmost {:?}", 50 | value.as_ref(), input_length, input_length, 51 | ), 52 | format!( 53 | "{:?} length {:?} should not be atmost {:?}", 54 | value.as_ref(), input_length, input_length, 55 | ), 56 | ), 57 | } 58 | } 59 | } 60 | 61 | /// Creates a StringLengthMatcher that asserts whether the length of a string is same as the given length. 62 | pub fn have_same_length(length: usize) -> StringLengthMatcher { 63 | StringLengthMatcher::Same(length) 64 | } 65 | 66 | /// Creates a StringLengthMatcher that asserts whether the length of a string is greater than or equal to the given length. 67 | pub fn have_atleast_same_length(length: usize) -> StringLengthMatcher { 68 | StringLengthMatcher::Atleast(length) 69 | } 70 | 71 | /// Creates a StringLengthMatcher that asserts whether the length of a string is less than or equal to the given length. 72 | pub fn have_atmost_same_length(length: usize) -> StringLengthMatcher { 73 | StringLengthMatcher::Atmost(length) 74 | } 75 | 76 | #[cfg(test)] 77 | mod tests { 78 | use crate::assertions::bool::TrueFalseAssertion; 79 | use crate::matchers::Matcher; 80 | use crate::matchers::string::length::{have_atleast_same_length, have_atmost_same_length, have_same_length}; 81 | 82 | #[test] 83 | fn should_have_same_length() { 84 | let matcher = have_same_length(4); 85 | matcher.test(&"test").passed.should_be_true(); 86 | } 87 | 88 | #[test] 89 | #[should_panic] 90 | fn should_have_same_length_but_was_not() { 91 | let matcher = have_same_length(4); 92 | matcher.test(&"clearcheck").passed.should_be_true(); 93 | } 94 | 95 | #[test] 96 | fn should_have_atleast_same_length() { 97 | let matcher = have_atleast_same_length(4); 98 | matcher.test(&"clearcheck").passed.should_be_true(); 99 | } 100 | 101 | #[test] 102 | #[should_panic] 103 | fn should_have_atleast_same_length_but_was_not() { 104 | let matcher = have_atleast_same_length(20); 105 | matcher.test(&"clearcheck").passed.should_be_true(); 106 | } 107 | 108 | #[test] 109 | fn should_have_atmost_length() { 110 | let matcher = have_atmost_same_length(5); 111 | matcher.test(&"junit").passed.should_be_true(); 112 | } 113 | 114 | #[test] 115 | #[should_panic] 116 | fn should_have_atmost_length_but_was_not() { 117 | let matcher = have_atmost_same_length(3); 118 | matcher.test(&"junit").passed.should_be_true(); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/assertions/collection/sort.rs: -------------------------------------------------------------------------------- 1 | use crate::matchers::collection::sort::{be_sorted_ascending, be_sorted_descending}; 2 | use crate::matchers::Should; 3 | 4 | /// SortAssertion enables assertions about whether a collection's elements are sorted in a specific order. 5 | pub trait SortAssertion 6 | where 7 | T: PartialOrd, 8 | { 9 | /// - Asserts that the elements of the collection are in ascending order (non-decreasing, allowing duplicates). 10 | /// - Returns a reference to self for fluent chaining. 11 | /// - Panics if the assertion fails. 12 | /// # Example 13 | /// ``` 14 | /// use clearcheck::assertions::collection::sort::SortAssertion; 15 | /// 16 | /// let collection = vec![1, 2, 3, 3, 5]; 17 | /// collection.should_be_sorted_ascending(); 18 | /// ``` 19 | fn should_be_sorted_ascending(&self) -> &Self; 20 | 21 | /// - Asserts that the elements of the collection are in descending order (non-increasing, allowing duplicates). 22 | /// - Returns a reference to self for fluent chaining. 23 | /// - Panics if the assertion fails. 24 | /// # Example 25 | /// ``` 26 | /// use clearcheck::assertions::collection::sort::SortAssertion; 27 | /// 28 | /// let collection = vec![5, 4, 3, 3, 2, 1]; 29 | /// collection.should_be_sorted_descending(); 30 | /// ``` 31 | fn should_be_sorted_descending(&self) -> &Self; 32 | } 33 | 34 | impl SortAssertion for Vec 35 | where 36 | T: std::fmt::Debug + PartialOrd, 37 | { 38 | fn should_be_sorted_ascending(&self) -> &Self { 39 | (self as &[T]).should_be_sorted_ascending(); 40 | self 41 | } 42 | 43 | fn should_be_sorted_descending(&self) -> &Self { 44 | (self as &[T]).should_be_sorted_descending(); 45 | self 46 | } 47 | } 48 | 49 | impl SortAssertion for [T; N] 50 | where 51 | T: std::fmt::Debug + PartialOrd, 52 | { 53 | fn should_be_sorted_ascending(&self) -> &Self { 54 | (self as &[T]).should_be_sorted_ascending(); 55 | self 56 | } 57 | 58 | fn should_be_sorted_descending(&self) -> &Self { 59 | (self as &[T]).should_be_sorted_descending(); 60 | self 61 | } 62 | } 63 | 64 | impl SortAssertion for [T] 65 | where 66 | T: std::fmt::Debug + PartialOrd, 67 | { 68 | fn should_be_sorted_ascending(&self) -> &Self { 69 | self.should(&be_sorted_ascending()); 70 | self 71 | } 72 | 73 | fn should_be_sorted_descending(&self) -> &Self { 74 | self.should(&be_sorted_descending()); 75 | self 76 | } 77 | } 78 | 79 | #[cfg(test)] 80 | mod tests { 81 | use crate::assertions::collection::sort::SortAssertion; 82 | 83 | #[test] 84 | fn should_be_sorted_in_ascending_order() { 85 | let collection = vec!["actual", "assert", "catch", "testify"]; 86 | collection.should_be_sorted_ascending(); 87 | } 88 | 89 | #[test] 90 | #[should_panic] 91 | fn should_be_sorted_in_ascending_order_but_was_not() { 92 | let collection = vec!["actual", "testify", "catch"]; 93 | collection.should_be_sorted_ascending(); 94 | } 95 | 96 | #[test] 97 | fn should_be_sorted_in_descending_order() { 98 | let collection = vec!["testify", "catch", "assert", "actual"]; 99 | collection.should_be_sorted_descending(); 100 | } 101 | 102 | #[test] 103 | #[should_panic] 104 | fn should_be_sorted_in_descending_order_but_was_not() { 105 | let collection = vec!["actual", "testify", "catch"]; 106 | collection.should_be_sorted_descending(); 107 | } 108 | } 109 | 110 | #[cfg(test)] 111 | mod array_tests { 112 | use crate::assertions::collection::sort::SortAssertion; 113 | 114 | #[test] 115 | fn should_be_sorted_in_ascending_order() { 116 | let collection = ["actual", "assert", "catch", "testify"]; 117 | collection.should_be_sorted_ascending(); 118 | } 119 | 120 | #[test] 121 | #[should_panic] 122 | fn should_be_sorted_in_ascending_order_but_was_not() { 123 | let collection = ["actual", "testify", "catch"]; 124 | collection.should_be_sorted_ascending(); 125 | } 126 | 127 | #[test] 128 | fn should_be_sorted_in_descending_order() { 129 | let collection = ["testify", "catch", "assert", "actual"]; 130 | collection.should_be_sorted_descending(); 131 | } 132 | 133 | #[test] 134 | #[should_panic] 135 | fn should_be_sorted_in_descending_order_but_was_not() { 136 | let collection = ["actual", "testify", "catch"]; 137 | collection.should_be_sorted_descending(); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/assertions/collection/duplicate.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use crate::matchers::collection::duplicate::contain_duplicates; 4 | use crate::matchers::{Should, ShouldNot}; 5 | 6 | /// DuplicateContentAssertion enables assertions about whether a collection contains duplicate elements. 7 | pub trait DuplicateContentAssertion { 8 | /// - Asserts that the collection contains atleast one duplicate element. 9 | /// - Returns a reference to self for fluent chaining. 10 | /// - Panics if the assertion fails. 11 | /// # Example 12 | /// ``` 13 | /// use clearcheck::assertions::collection::duplicate::DuplicateContentAssertion; 14 | /// 15 | /// let collection = ["junit", "testify", "clearcheck", "testify"]; 16 | /// collection.should_contain_duplicates(); 17 | /// ``` 18 | fn should_contain_duplicates(&self) -> &Self; 19 | 20 | /// - Asserts that the collection does not contain any duplicate element. 21 | /// - Returns a reference to self for fluent chaining. 22 | /// - Panics if the assertion fails. 23 | /// # Example 24 | /// ``` 25 | /// use clearcheck::assertions::collection::duplicate::DuplicateContentAssertion; 26 | /// 27 | /// let collection = ["junit", "testify", "clearcheck"]; 28 | /// collection.should_not_contain_duplicates(); 29 | /// ``` 30 | fn should_not_contain_duplicates(&self) -> &Self; 31 | } 32 | 33 | impl DuplicateContentAssertion for Vec 34 | where 35 | T: Debug, 36 | T: Eq, 37 | { 38 | fn should_contain_duplicates(&self) -> &Self { 39 | (self as &[T]).should_contain_duplicates(); 40 | self 41 | } 42 | 43 | fn should_not_contain_duplicates(&self) -> &Self { 44 | (self as &[T]).should_not_contain_duplicates(); 45 | self 46 | } 47 | } 48 | 49 | impl DuplicateContentAssertion for [T; N] 50 | where 51 | T: Debug, 52 | T: Eq, 53 | { 54 | fn should_contain_duplicates(&self) -> &Self { 55 | (self as &[T]).should_contain_duplicates(); 56 | self 57 | } 58 | 59 | fn should_not_contain_duplicates(&self) -> &Self { 60 | (self as &[T]).should_not_contain_duplicates(); 61 | self 62 | } 63 | } 64 | 65 | impl DuplicateContentAssertion for [T] 66 | where 67 | T: Debug, 68 | T: Eq, 69 | { 70 | fn should_contain_duplicates(&self) -> &Self { 71 | self.should(&contain_duplicates()); 72 | self 73 | } 74 | 75 | fn should_not_contain_duplicates(&self) -> &Self { 76 | self.should_not(&contain_duplicates()); 77 | self 78 | } 79 | } 80 | 81 | #[cfg(test)] 82 | mod tests { 83 | use crate::assertions::collection::duplicate::DuplicateContentAssertion; 84 | 85 | #[test] 86 | fn should_contain_duplicates() { 87 | let collection = vec!["junit", "testify", "assert4j", "testify"]; 88 | collection.should_contain_duplicates(); 89 | } 90 | 91 | #[test] 92 | #[should_panic] 93 | fn should_contain_duplicates_but_it_did_not() { 94 | let collection = vec!["junit", "testify", "assert4j", ""]; 95 | collection.should_contain_duplicates(); 96 | } 97 | 98 | #[test] 99 | fn should_not_contain_duplicates() { 100 | let collection = vec!["junit", "testify", "assert4j", "catch"]; 101 | collection.should_not_contain_duplicates(); 102 | } 103 | 104 | #[test] 105 | #[should_panic] 106 | fn should_not_contain_duplicates_but_it_contained() { 107 | let collection = vec!["junit", "testify", "assert4j", "testify"]; 108 | collection.should_not_contain_duplicates(); 109 | } 110 | } 111 | 112 | #[cfg(test)] 113 | mod array_tests { 114 | use crate::assertions::collection::duplicate::DuplicateContentAssertion; 115 | 116 | #[test] 117 | fn should_contain_duplicates() { 118 | let collection = ["junit", "testify", "assert4j", "testify"]; 119 | collection.should_contain_duplicates(); 120 | } 121 | 122 | #[test] 123 | #[should_panic] 124 | fn should_contain_duplicates_but_it_did_not() { 125 | let collection = ["junit", "testify", "assert4j", ""]; 126 | collection.should_contain_duplicates(); 127 | } 128 | 129 | #[test] 130 | fn should_not_contain_duplicates() { 131 | let collection = ["junit", "testify", "assert4j", "catch"]; 132 | collection.should_not_contain_duplicates(); 133 | } 134 | 135 | #[test] 136 | #[should_panic] 137 | fn should_not_contain_duplicates_but_it_contained() { 138 | let collection = ["junit", "testify", "assert4j", "testify"]; 139 | collection.should_not_contain_duplicates(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/matchers/mod.rs: -------------------------------------------------------------------------------- 1 | //! Matchers provide the granular tools for carrying out the assertions. 2 | //! They examine the data and verify that the data conforms to specific criteria. 3 | //! 4 | //! Let's take an example of a collection matcher. 5 | //! 6 | //! ``` 7 | //! use clearcheck::matchers::collection::membership::contain_all; 8 | //! use clearcheck::matchers::Matcher; 9 | //! 10 | //! let collection = vec!["clearcheck", "testify", "assert4j", "xunit"]; 11 | //! let all_to_be_contained = vec!["testify", "assert4j", "xunit"]; 12 | //! 13 | //! let matcher = contain_all(all_to_be_contained); 14 | //! assert!(matcher.test(&collection).passed()); 15 | //! ``` 16 | 17 | pub mod bool; 18 | pub mod char; 19 | pub mod collection; 20 | pub mod compose; 21 | #[cfg(feature = "date")] 22 | pub mod date; 23 | pub mod equal; 24 | #[cfg(feature = "file")] 25 | pub mod file; 26 | #[cfg(feature = "num")] 27 | pub mod float; 28 | #[cfg(feature = "num")] 29 | pub mod int; 30 | pub mod map; 31 | pub mod option; 32 | pub mod ordered; 33 | pub mod range; 34 | pub mod result; 35 | pub mod string; 36 | 37 | /// Should provides a convenient way to express positive assertions within tests, indicating that a value should meet a certain condition. 38 | pub trait Should { 39 | /// - Takes a matcher as input and performs an assertion against the value itself. 40 | /// - Panics if the assertion fails, indicating that the value did not match the matcher's expectations. 41 | fn should(&self, matcher: &dyn Matcher); 42 | } 43 | 44 | /// ShouldNot provides a convenient way to express negative assertions within tests, indicating that a value should not meet a certain condition. 45 | pub trait ShouldNot { 46 | /// - Takes a matcher as input and performs an inverted assertion against the value itself. 47 | /// - Inverts the result of the matcher's test method, ensuring the value does not match. 48 | /// - Panics if the inverted assertion fails, indicating that the value unexpectedly matched the matcher. 49 | fn should_not(&self, matcher: &dyn Matcher); 50 | } 51 | 52 | impl Should for T { 53 | fn should(&self, matcher: &dyn Matcher) { 54 | let matcher_result = matcher.test(self); 55 | if !matcher_result.passed { 56 | panic!("assertion failed: {}", matcher_result.failure_message); 57 | } 58 | } 59 | } 60 | 61 | impl ShouldNot for T { 62 | fn should_not(&self, matcher: &dyn Matcher) { 63 | let matcher_result = matcher.test(self); 64 | let passed = !matcher_result.passed; 65 | if !passed { 66 | panic!( 67 | "assertion failed: {}", 68 | matcher_result.inverted_failure_message 69 | ); 70 | } 71 | } 72 | } 73 | 74 | /// Matcher defines the core functionality of matchers. All the matchers implement `Matcher` trait. 75 | pub trait Matcher { 76 | fn test(&self, value: &T) -> MatcherResult; 77 | } 78 | 79 | /// BoxWrap provides a `boxed` method to wrap a Matcher into Box object. 80 | /// 81 | /// It is used to compose matchers in [`crate::matchers::compose::Matchers`]. 82 | /// 83 | /// BoxWrap is implemented for any `T: Matcher`. 84 | pub trait BoxWrap { 85 | fn boxed(self) -> Box>; 86 | } 87 | 88 | impl + 'static> BoxWrap for T { 89 | fn boxed(self) -> Box> { 90 | Box::new(self) 91 | } 92 | } 93 | 94 | /// MatcherResult defines the result of a matcher execution. 95 | pub struct MatcherResult { 96 | passed: bool, 97 | failure_message: String, 98 | inverted_failure_message: String, 99 | } 100 | 101 | impl MatcherResult { 102 | /// Creates a new instance of MatcherResult using failure_message and inverted_failure_message of type &'static str. 103 | pub fn new( 104 | passed: bool, 105 | failure_message: &'static str, 106 | inverted_failure_message: &'static str, 107 | ) -> Self { 108 | MatcherResult::formatted( 109 | passed, 110 | failure_message.to_string(), 111 | inverted_failure_message.to_string(), 112 | ) 113 | } 114 | 115 | /// Creates a new instance of MatcherResult using failure_message and inverted_failure_message of type String. 116 | pub fn formatted( 117 | passed: bool, 118 | failure_message: String, 119 | inverted_failure_message: String, 120 | ) -> Self { 121 | MatcherResult { 122 | passed, 123 | failure_message, 124 | inverted_failure_message, 125 | } 126 | } 127 | 128 | /// Returns true if the result of a matcher execution was successful, false otherwise. 129 | pub fn passed(&self) -> bool { 130 | self.passed 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/matchers/collection/predicate.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::marker::PhantomData; 3 | 4 | use crate::matchers::{Matcher, MatcherResult}; 5 | 6 | /// PredicateMatcher offers a flexible way to assert whether the elements in a collection satisfy the given predicate. 7 | /// 8 | /// clearcheck implements PredicateMatcher for collection types including vector, arrays and reference to slices. 9 | /// 10 | /// # Example 11 | ///``` 12 | /// use clearcheck::matchers::collection::predicate::satisfy_for_any; 13 | /// use clearcheck::matchers::Matcher; 14 | /// 15 | /// let collection = vec!["junit", "testify", "xunit"]; 16 | /// let matcher = satisfy_for_any(|element: &&str| element.len() > 6); 17 | /// 18 | /// assert!(matcher.test(&collection).passed()); 19 | /// ``` 20 | pub enum PredicateMatcher 21 | where F: Fn(&T) -> bool, 22 | T: Eq 23 | { 24 | SatisfyAny(F, PhantomData), 25 | SatisfyAll(F, PhantomData), 26 | } 27 | 28 | impl PredicateMatcher 29 | where F: Fn(&T) -> bool, 30 | T: Eq + Debug 31 | { 32 | fn test(&self, collection: &[T]) -> MatcherResult { 33 | match self { 34 | PredicateMatcher::SatisfyAny(predicate, _) => 35 | MatcherResult::formatted( 36 | collection.iter().any(predicate), 37 | format!("{:?} should satisfy the given predicate for any of the elements", collection), 38 | format!("{:?} should not satisfy the given predicate for any of the elements", collection), 39 | ), 40 | PredicateMatcher::SatisfyAll(predicate, _) => 41 | MatcherResult::formatted( 42 | collection.iter().all(predicate), 43 | format!("{:?} should satisfy the given predicate for all the elements", collection), 44 | format!("{:?} should not satisfy the given predicate for all the elements", collection), 45 | ), 46 | } 47 | } 48 | } 49 | 50 | impl Matcher> for PredicateMatcher 51 | where 52 | F: Fn(&T) -> bool, 53 | T: Eq + Debug, 54 | { 55 | fn test(&self, collection: &Vec) -> MatcherResult { 56 | self.test(collection) 57 | } 58 | } 59 | 60 | impl Matcher<[T; N]> for PredicateMatcher 61 | where 62 | F: Fn(&T) -> bool, 63 | T: Eq + Debug, 64 | { 65 | fn test(&self, collection: &[T; N]) -> MatcherResult { 66 | self.test(collection) 67 | } 68 | } 69 | 70 | impl Matcher<&[T]> for PredicateMatcher 71 | where 72 | F: Fn(&T) -> bool, 73 | T: Eq + Debug, 74 | { 75 | fn test(&self, collection: &&[T]) -> MatcherResult { 76 | self.test(collection) 77 | } 78 | } 79 | 80 | /// Creates a PredicateMatcher that asserts whether any of the elements in a collection satisfy the given predicate. 81 | pub fn satisfy_for_any(predicate: F) -> PredicateMatcher 82 | where 83 | F: Fn(&T) -> bool, 84 | T: Eq + Debug, 85 | { 86 | PredicateMatcher::SatisfyAny(predicate, PhantomData) 87 | } 88 | 89 | /// Creates a PredicateMatcher that asserts whether all the elements in a collection satisfy the given predicate. 90 | pub fn satisfy_for_all(predicate: F) -> PredicateMatcher 91 | where 92 | F: Fn(&T) -> bool, 93 | T: Eq + Debug, 94 | { 95 | PredicateMatcher::SatisfyAll(predicate, PhantomData) 96 | } 97 | 98 | #[cfg(test)] 99 | mod tests { 100 | use crate::assertions::bool::TrueFalseAssertion; 101 | use crate::matchers::collection::predicate::{satisfy_for_all, satisfy_for_any}; 102 | 103 | #[test] 104 | fn should_satisfy_for_any() { 105 | let collection = vec!["junit", "testify", "xunit"]; 106 | let matcher = satisfy_for_any(|element: &&str| element.len() > 6); 107 | matcher.test(&collection).passed.should_be_true(); 108 | } 109 | 110 | #[test] 111 | #[should_panic] 112 | fn should_satisfy_for_any_but_id_did_not() { 113 | let collection = vec!["unit4j", "testify"]; 114 | let matcher = satisfy_for_any(|element: &&str| element.starts_with("clear")); 115 | matcher.test(&collection).passed.should_be_true(); 116 | } 117 | #[test] 118 | fn should_satisfy_for_all() { 119 | let collection = vec!["junit", "testify"]; 120 | let matcher = satisfy_for_all(|element: &&str| element.len() > 3); 121 | matcher.test(&collection).passed.should_be_true(); 122 | } 123 | 124 | #[test] 125 | #[should_panic] 126 | fn should_satisfy_for_all_but_id_did_not() { 127 | let collection = vec!["unit4j", "testify"]; 128 | let matcher = satisfy_for_all(|element: &&str| element.starts_with("clear")); 129 | matcher.test(&collection).passed.should_be_true(); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/matchers/ordered/mod.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use crate::matchers::{Matcher, MatcherResult}; 4 | 5 | /// OrderedMatcher offers a flexible way to assert ordering relationships between values. 6 | /// 7 | /// Works with any data type that implements the PartialOrd trait. 8 | /// 9 | /// # Example 10 | ///``` 11 | /// use clearcheck::matchers::Matcher; 12 | /// use clearcheck::matchers::ordered::be_greater_than; 13 | /// 14 | /// let value = 100; 15 | /// let matcher = be_greater_than(90); 16 | /// 17 | /// assert!(matcher.test(&value).passed()); 18 | /// ``` 19 | pub enum OrderedMatcher { 20 | Gt(T), 21 | Gte(T), 22 | Lt(T), 23 | Lte(T), 24 | } 25 | 26 | impl Matcher for OrderedMatcher { 27 | fn test(&self, value: &T) -> MatcherResult { 28 | match self { 29 | OrderedMatcher::Gt(other) => MatcherResult::formatted( 30 | value > other, 31 | format!("{:?} should be greater than {:?}", value, other), 32 | format!("{:?} should not be greater than {:?}", value, other), 33 | ), 34 | OrderedMatcher::Gte(other) => MatcherResult::formatted( 35 | value >= other, 36 | format!("{:?} should be greater than equals to {:?}", value, other), 37 | format!( 38 | "{:?} should not be greater than equals to {:?}", 39 | value, other 40 | ), 41 | ), 42 | OrderedMatcher::Lt(other) => MatcherResult::formatted( 43 | value < other, 44 | format!("{:?} should be less than {:?}", value, other), 45 | format!("{:?} should not be less than {:?}", value, other), 46 | ), 47 | OrderedMatcher::Lte(other) => MatcherResult::formatted( 48 | value <= other, 49 | format!("{:?} should be less than equals to {:?}", value, other), 50 | format!("{:?} should not be less than equals to {:?}", value, other), 51 | ), 52 | } 53 | } 54 | } 55 | 56 | /// Creates an OrderedMatcher that asserts whether a value is greater than the given value. 57 | pub fn be_greater_than(other: T) -> OrderedMatcher { 58 | OrderedMatcher::Gt(other) 59 | } 60 | 61 | /// Creates an OrderedMatcher that asserts whether a value is greater than or equal to the given value. 62 | pub fn be_greater_than_equal_to(other: T) -> OrderedMatcher { 63 | OrderedMatcher::Gte(other) 64 | } 65 | 66 | /// Creates an OrderedMatcher that asserts whether a value is less than the given value. 67 | pub fn be_less_than(other: T) -> OrderedMatcher { 68 | OrderedMatcher::Lt(other) 69 | } 70 | 71 | /// Creates an OrderedMatcher that asserts whether a value is less than or equal to the given value. 72 | pub fn be_less_than_equal_to(other: T) -> OrderedMatcher { 73 | OrderedMatcher::Lte(other) 74 | } 75 | 76 | #[cfg(test)] 77 | mod tests { 78 | use crate::assertions::bool::TrueFalseAssertion; 79 | use crate::matchers::ordered::{ 80 | be_greater_than, be_greater_than_equal_to, be_less_than, be_less_than_equal_to, 81 | }; 82 | use crate::matchers::Matcher; 83 | 84 | #[test] 85 | fn should_be_greater_than() { 86 | let value = 100; 87 | let matcher = be_greater_than(90); 88 | matcher.test(&value).passed.should_be_true(); 89 | } 90 | 91 | #[test] 92 | #[should_panic] 93 | fn should_be_greater_than_but_was_not() { 94 | let value = 80; 95 | let matcher = be_greater_than(90); 96 | matcher.test(&value).passed.should_be_true(); 97 | } 98 | 99 | #[test] 100 | fn should_be_greater_than_equal_to() { 101 | let value = 100; 102 | let matcher = be_greater_than_equal_to(100); 103 | matcher.test(&value).passed.should_be_true(); 104 | } 105 | 106 | #[test] 107 | #[should_panic] 108 | fn should_be_greater_than_equal_to_but_was_not() { 109 | let value = 80; 110 | let matcher = be_greater_than_equal_to(90); 111 | matcher.test(&value).passed.should_be_true(); 112 | } 113 | 114 | #[test] 115 | fn should_be_less_than() { 116 | let value = 80; 117 | let matcher = be_less_than(90); 118 | matcher.test(&value).passed.should_be_true(); 119 | } 120 | 121 | #[test] 122 | #[should_panic] 123 | fn should_be_less_than_but_was_not() { 124 | let value = 100; 125 | let matcher = be_less_than(90); 126 | matcher.test(&value).passed.should_be_true(); 127 | } 128 | 129 | #[test] 130 | fn should_be_less_than_equal_to() { 131 | let value = 100; 132 | let matcher = be_less_than_equal_to(100); 133 | matcher.test(&value).passed.should_be_true(); 134 | } 135 | 136 | #[test] 137 | #[should_panic] 138 | fn should_be_less_than_equal_to_but_was_not() { 139 | let value = 100; 140 | let matcher = be_less_than_equal_to(90); 141 | matcher.test(&value).passed.should_be_true(); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/matchers/int/mod.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use num::Integer; 4 | 5 | use crate::matchers::{Matcher, MatcherResult}; 6 | 7 | /// IntMatcher offers a flexible way to make assertions about specific integer attributes. 8 | /// 9 | /// # Example 10 | ///``` 11 | /// use clearcheck::matchers::int::be_positive; 12 | /// use clearcheck::matchers::Matcher; 13 | /// 14 | /// let value = 10; 15 | /// let matcher = be_positive(); 16 | /// 17 | /// assert!(matcher.test(&value).passed()); 18 | /// ``` 19 | pub enum IntMatcher { 20 | Positive, 21 | Negative, 22 | Even, 23 | Odd, 24 | Zero, 25 | } 26 | 27 | impl Matcher for IntMatcher { 28 | fn test(&self, value: &T) -> MatcherResult { 29 | match self { 30 | IntMatcher::Positive => MatcherResult::formatted( 31 | *value > T::default(), 32 | format!("{:?} should be positive", value), 33 | format!("{:?} should not be positive", value), 34 | ), 35 | IntMatcher::Negative => MatcherResult::formatted( 36 | *value < T::default(), 37 | format!("{:?} should be negative", value), 38 | format!("{:?} should not be negative", value), 39 | ), 40 | IntMatcher::Even => MatcherResult::formatted( 41 | value.is_even(), 42 | format!("{:?} should be even", value), 43 | format!("{:?} should not be even", value), 44 | ), 45 | IntMatcher::Odd => MatcherResult::formatted( 46 | value.is_odd(), 47 | format!("{:?} should be odd", value), 48 | format!("{:?} should not be odd", value), 49 | ), 50 | IntMatcher::Zero => MatcherResult::formatted( 51 | *value == T::default(), 52 | format!("{:?} should be zero", value), 53 | format!("{:?} should not be zero", value), 54 | ), 55 | } 56 | } 57 | } 58 | 59 | /// Creates an IntMatcher that asserts whether an integer value is positive. 60 | pub fn be_positive() -> IntMatcher { 61 | IntMatcher::Positive 62 | } 63 | 64 | /// Creates an IntMatcher that asserts whether an integer value is negative. 65 | pub fn be_negative() -> IntMatcher { 66 | IntMatcher::Negative 67 | } 68 | 69 | /// Creates an IntMatcher that asserts whether an integer value is even. 70 | pub fn be_even() -> IntMatcher { 71 | IntMatcher::Even 72 | } 73 | 74 | /// Creates an IntMatcher that asserts whether an integer value is odd. 75 | pub fn be_odd() -> IntMatcher { 76 | IntMatcher::Odd 77 | } 78 | 79 | /// Creates an IntMatcher that asserts whether an integer value is zero. 80 | pub fn be_zero() -> IntMatcher { 81 | IntMatcher::Zero 82 | } 83 | 84 | #[cfg(all(test, feature = "num"))] 85 | mod tests { 86 | use crate::assertions::bool::TrueFalseAssertion; 87 | use crate::matchers::int::{be_even, be_negative, be_odd, be_positive, be_zero}; 88 | use crate::matchers::Matcher; 89 | 90 | #[test] 91 | fn should_be_positive() { 92 | let value = 10; 93 | let matcher = be_positive(); 94 | matcher.test(&value).passed.should_be_true(); 95 | } 96 | 97 | #[test] 98 | #[should_panic] 99 | fn should_be_positive_but_was_not() { 100 | let value = -1; 101 | let matcher = be_positive(); 102 | matcher.test(&value).passed.should_be_true(); 103 | } 104 | 105 | #[test] 106 | fn should_be_negative() { 107 | let value = -10; 108 | let matcher = be_negative(); 109 | matcher.test(&value).passed.should_be_true(); 110 | } 111 | 112 | #[test] 113 | #[should_panic] 114 | fn should_be_negative_but_was_not() { 115 | let value = 1; 116 | let matcher = be_negative(); 117 | matcher.test(&value).passed.should_be_true(); 118 | } 119 | 120 | #[test] 121 | fn should_be_even() { 122 | let value = -10; 123 | let matcher = be_even(); 124 | matcher.test(&value).passed.should_be_true(); 125 | } 126 | 127 | #[test] 128 | #[should_panic] 129 | fn should_be_even_but_was_not() { 130 | let value = -1; 131 | let matcher = be_even(); 132 | matcher.test(&value).passed.should_be_true(); 133 | } 134 | 135 | #[test] 136 | fn should_be_odd() { 137 | let value = -11; 138 | let matcher = be_odd(); 139 | matcher.test(&value).passed.should_be_true(); 140 | } 141 | 142 | #[test] 143 | #[should_panic] 144 | fn should_be_odd_but_was_not() { 145 | let value = -10; 146 | let matcher = be_odd(); 147 | matcher.test(&value).passed.should_be_true(); 148 | } 149 | 150 | #[test] 151 | fn should_be_zero() { 152 | let value = 0; 153 | let matcher = be_zero(); 154 | matcher.test(&value).passed.should_be_true(); 155 | } 156 | 157 | #[test] 158 | #[should_panic] 159 | fn should_be_zero_but_was_not() { 160 | let value = -10; 161 | let matcher = be_zero(); 162 | matcher.test(&value).passed.should_be_true(); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/assertions/equal.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Borrow; 2 | use std::fmt::Debug; 3 | 4 | use crate::matchers::{Should, ShouldNot}; 5 | use crate::matchers::equal::be_equal; 6 | 7 | /// EqualityAssertion enables assertions about the equality of two values of type T: Eq. 8 | pub trait EqualityAssertion { 9 | /// - Asserts that the value held by self is equal to other. 10 | /// - Returns a reference to self for fluent chaining. 11 | /// - Panics if the assertion fails. 12 | /// # Example 13 | /// ``` 14 | /// use clearcheck::assertions::equal::EqualityAssertion; 15 | /// 16 | /// #[derive(Debug, Eq, PartialEq)] 17 | /// struct Book { 18 | /// name: &'static str, 19 | /// } 20 | /// 21 | /// let books = vec![ 22 | /// Book {name: "Database internals"}, 23 | /// Book {name: "Rust in action"} 24 | /// ]; 25 | /// let other = vec![ 26 | /// Book {name: "Database internals"}, 27 | /// Book {name: "Rust in action"} 28 | /// ]; 29 | /// books.should_equal(&other); 30 | /// ``` 31 | fn should_equal(&self, other: &Q) -> &Self 32 | where 33 | T: Borrow, 34 | Q: Eq + Debug + ?Sized; 35 | 36 | /// - Asserts that the value held by self is not equal to other. 37 | /// - Returns a reference to self for fluent chaining. 38 | /// - Panics if the assertion fails. 39 | /// # Example 40 | /// ``` 41 | /// use clearcheck::assertions::equal::EqualityAssertion; 42 | /// 43 | /// #[derive(Debug, Eq, PartialEq)] 44 | /// struct Book { 45 | /// name: &'static str, 46 | /// } 47 | /// 48 | /// let books = vec![ 49 | /// Book {name: "Database internals"}, 50 | /// ]; 51 | /// let other = vec![ 52 | /// Book {name: "Database internals"}, 53 | /// Book {name: "Rust in action"}, 54 | /// ]; 55 | /// books.should_not_equal(&other); 56 | /// ``` 57 | fn should_not_equal(&self, other: &Q) -> &Self 58 | where 59 | T: Borrow, 60 | Q: Eq + Debug + ?Sized; 61 | } 62 | 63 | impl EqualityAssertion for T { 64 | fn should_equal(&self, other: &Q) -> &Self 65 | where 66 | T: Borrow, 67 | Q: Eq + Debug + ?Sized, 68 | { 69 | self.borrow().should(&be_equal(other)); 70 | self 71 | } 72 | 73 | fn should_not_equal(&self, other: &Q) -> &Self 74 | where 75 | T: Borrow, 76 | Q: Eq + Debug + ?Sized, 77 | { 78 | self.borrow().should_not(&be_equal(other)); 79 | self 80 | } 81 | } 82 | 83 | #[cfg(test)] 84 | mod tests { 85 | use crate::assertions::equal::EqualityAssertion; 86 | 87 | #[derive(Debug, Eq, PartialEq)] 88 | struct Book { 89 | name: &'static str, 90 | } 91 | 92 | #[test] 93 | fn should_equal() { 94 | let books = &vec![ 95 | Book { 96 | name: "Database internals", 97 | }, 98 | Book { 99 | name: "Rust in action", 100 | }, 101 | ]; 102 | let target = vec![ 103 | Book { 104 | name: "Database internals", 105 | }, 106 | Book { 107 | name: "Rust in action", 108 | }, 109 | ]; 110 | books.should_equal(&target); 111 | } 112 | 113 | #[test] 114 | #[should_panic] 115 | fn should_equal_but_was_not() { 116 | let books = &vec![ 117 | Book { 118 | name: "Database internals", 119 | }, 120 | Book { 121 | name: "Rust in action", 122 | }, 123 | ]; 124 | let target = vec![ 125 | Book { 126 | name: "Database internals", 127 | }, 128 | Book { 129 | name: "Learning Rust", 130 | }, 131 | ]; 132 | books.should_equal(&target); 133 | } 134 | 135 | #[test] 136 | fn should_not_equal() { 137 | let books = &vec![ 138 | Book { 139 | name: "Database internals", 140 | }, 141 | Book { 142 | name: "Rust in action", 143 | }, 144 | ]; 145 | let target = vec![Book { 146 | name: "Database internals", 147 | }]; 148 | books.should_not_equal(&target); 149 | } 150 | 151 | #[test] 152 | #[should_panic] 153 | fn should_not_equal_but_was() { 154 | let books = &vec![ 155 | Book { 156 | name: "Database internals", 157 | }, 158 | Book { 159 | name: "Rust in action", 160 | }, 161 | ]; 162 | let target = vec![ 163 | Book { 164 | name: "Database internals", 165 | }, 166 | Book { 167 | name: "Rust in action", 168 | }, 169 | ]; 170 | books.should_not_equal(&target); 171 | } 172 | 173 | #[test] 174 | fn should_equal_strings() { 175 | let name = "junit"; 176 | name.should_equal("junit"); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/matchers/collection/length.rs: -------------------------------------------------------------------------------- 1 | use crate::matchers::{Matcher, MatcherResult}; 2 | 3 | /// CollectionLengthMatcher offers a flexible way to assert various length properties of collections. 4 | /// 5 | /// clearcheck implements CollectionLengthMatcher for collection types including vector, arrays and reference to slices. 6 | /// 7 | /// # Example 8 | ///``` 9 | /// use clearcheck::matchers::collection::length::have_atleast_same_length; 10 | /// use clearcheck::matchers::Matcher; 11 | /// 12 | /// let matcher = have_atleast_same_length(3); 13 | /// let collection = vec![1, 2, 3, 4]; 14 | /// 15 | /// assert!(matcher.test(&collection).passed()); 16 | /// ``` 17 | pub enum CollectionLengthMatcher { 18 | Same(usize), 19 | Atleast(usize), 20 | Atmost(usize), 21 | } 22 | 23 | impl Matcher> for CollectionLengthMatcher { 24 | fn test(&self, collection: &Vec) -> MatcherResult { 25 | self.test_length(collection.len()) 26 | } 27 | } 28 | 29 | impl Matcher<[T; N]> for CollectionLengthMatcher { 30 | fn test(&self, collection: &[T; N]) -> MatcherResult { 31 | self.test_length(collection.len()) 32 | } 33 | } 34 | 35 | impl Matcher<&[T]> for CollectionLengthMatcher { 36 | fn test(&self, collection: &&[T]) -> MatcherResult { 37 | self.test_length(collection.len()) 38 | } 39 | } 40 | 41 | impl CollectionLengthMatcher { 42 | fn test_length(&self, input_length: usize) -> MatcherResult { 43 | let message_prefix = "Collection"; 44 | match self { 45 | CollectionLengthMatcher::Same(length) => MatcherResult::formatted( 46 | input_length == *length, 47 | format!( 48 | "{:?} length {:?} should be {:?}", 49 | message_prefix, input_length, length 50 | ), 51 | format!( 52 | "{:?} length {:?} should not be {:?}", 53 | message_prefix, input_length, length 54 | ), 55 | ), 56 | CollectionLengthMatcher::Atleast(length) => MatcherResult::formatted( 57 | input_length >= *length, 58 | format!( 59 | "{:?} length {:?} should be atleast {:?}", 60 | message_prefix, input_length, length 61 | ), 62 | format!( 63 | "{:?} length {:?} should not be atleast {:?}", 64 | message_prefix, input_length, length 65 | ), 66 | ), 67 | CollectionLengthMatcher::Atmost(length) => MatcherResult::formatted( 68 | input_length <= *length, 69 | format!( 70 | "{:?} length {:?} should be atmost {:?}", 71 | message_prefix, input_length, length 72 | ), 73 | format!( 74 | "{:?} length {:?} should not be atmost {:?}", 75 | message_prefix, input_length, length 76 | ), 77 | ), 78 | } 79 | } 80 | } 81 | 82 | /// Creates a CollectionLengthMatcher that asserts whether the length of a collection is same as the given length. 83 | pub fn have_same_length(length: usize) -> CollectionLengthMatcher { 84 | CollectionLengthMatcher::Same(length) 85 | } 86 | 87 | /// Creates a CollectionLengthMatcher that asserts whether the length of a collection is greater than or equal to the given length. 88 | pub fn have_atleast_same_length(length: usize) -> CollectionLengthMatcher { 89 | CollectionLengthMatcher::Atleast(length) 90 | } 91 | 92 | /// Creates a CollectionLengthMatcher that asserts whether the length of a collection is less than or equal to the given length. 93 | pub fn have_atmost_same_length(length: usize) -> CollectionLengthMatcher { 94 | CollectionLengthMatcher::Atmost(length) 95 | } 96 | 97 | #[cfg(test)] 98 | mod tests { 99 | use crate::assertions::bool::TrueFalseAssertion; 100 | use crate::matchers::collection::length::{have_atleast_same_length, have_atmost_same_length, have_same_length}; 101 | use crate::matchers::Matcher; 102 | 103 | #[test] 104 | fn should_have_same_length() { 105 | let matcher = have_same_length(4); 106 | matcher.test(&vec![1, 2, 3, 4]).passed.should_be_true(); 107 | } 108 | 109 | #[test] 110 | #[should_panic] 111 | fn should_have_same_length_but_was_not() { 112 | let matcher = have_same_length(5); 113 | matcher.test(&vec![1, 2, 3, 4]).passed.should_be_true(); 114 | } 115 | 116 | #[test] 117 | fn should_have_atleast_same_length() { 118 | let matcher = have_atleast_same_length(3); 119 | matcher.test(&vec![1, 2, 3, 4]).passed.should_be_true(); 120 | } 121 | 122 | #[test] 123 | #[should_panic] 124 | fn should_have_atleast_same_length_but_was_not() { 125 | let matcher = have_atleast_same_length(6); 126 | matcher.test(&vec![1, 2, 3, 4]).passed.should_be_true(); 127 | } 128 | 129 | #[test] 130 | fn should_have_atmost_length() { 131 | let matcher = have_atmost_same_length(4); 132 | matcher.test(&vec![1, 2, 3, 4]).passed.should_be_true(); 133 | } 134 | 135 | #[test] 136 | #[should_panic] 137 | fn should_have_atmost_length_but_was_not() { 138 | let matcher = have_atmost_same_length(3); 139 | matcher.test(&vec![1, 2, 3, 4]).passed.should_be_true(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Clearcheck: Elegant and extensible assertions in Rust 2 | //! 3 | //! clearcheck is designed to make assertion statements as clear and concise as possible. 4 | //! It allows chaining multiple assertions together for a fluent and intuitive syntax, leading to more self-documenting test cases. 5 | //! 6 | //!```rust 7 | //! use clearcheck::assertions::string::length::LengthAssertion; 8 | //! use clearcheck::assertions::string::membership::MembershipAssertion; 9 | //! use clearcheck::assertions::string::numeric::NumericAssertion; 10 | //! use clearcheck::assertions::string::regex::RegularExpressionAssertion; 11 | //! 12 | //! let pass_phrase = "P@@sw0rd1 zebra alpha"; 13 | //! pass_phrase.should_not_be_empty() 14 | //! .should_have_at_least_length(10) 15 | //! .should_contain_all_characters(vec!['@', ' ']) 16 | //! .should_contain_a_digit() 17 | //! .should_not_contain_ignoring_case("pass") 18 | //! .should_not_contain_ignoring_case("word"); 19 | //!``` 20 | //! # Key features 21 | //! 22 | //! - **Fluent API**: Chain assertions for a natural and readable experience. 23 | //! - **Extensive assertions**: Variety of assertions covering common validation needs. 24 | //! - **Customizable**: Extend with your own assertions for specific domain requirements. 25 | //! - **Type-safe**: Built with Rust's type system for reliable and expressive assertions. 26 | //! - **Custom assertions**: Craft assertions tailored to your exact needs, ensuring comprehensive validations for various data structures. 27 | //! 28 | //! # Rust features 29 | //! 30 | //! clearcheck crate supports the following features: 31 | //! - date enables [assertions on date](assertions::date::DateAssertion) 32 | //! - file enables [assertions on filepath](assertions::file::FileAssertion) 33 | //! - num enables [assertions on float](assertions::float::FloatAssertion) and [assertions on integer](assertions::int::IntAssertion) 34 | //! - regex enables [regular expression assertions on string](assertions::string::regex) 35 | //! 36 | //! # Assertions vs Matchers 37 | //! 38 | //! **Assertions** serve as the cornerstone of the test cases, defining the exact expectations the code must fulfill. 39 | //! They act as a contract, ensuring that each data type (/data structure) adheres to its intended behavior. 40 | //! 41 | //! **Matchers**, on the other hand, provide the granular tools for carrying out these assertions. 42 | //! They examine data and verify that the data conforms to specific criteria. 43 | //! 44 | //! In essence, assertions orchestrate the high-level validation logic, while matchers act as the 45 | //! code-level inspectors, ensuring every detail aligns with the expectations. 46 | //! 47 | //! # Unleashing the power of custom matchers and assertions 48 | //! 49 | //! While this crate comes loaded with a plethora of ready-made assertions, sometimes your testing needs demand a bespoke touch. 50 | //! clearcheck allows crafting your own custom matchers and assertions! 51 | //! 52 | //! The possibilities are endless: 53 | //! 54 | //! - **Domain-specific validation**: Craft assertions that understand the nuances of your business logic. 55 | //! - **Enhanced readability**: Write clear and concise matchers that mirror your domain vocabulary, making your tests self-documenting and understandable. 56 | //! - **Reduced redundancy**: Eliminate repetitive code by encapsulating complex validation logic within reusable matchers. 57 | //! 58 | //! Let's craft a custom password matcher that enforces the following: 59 | //! 60 | //! - Password must not be empty. 61 | //! - Password must have a minimum length of 10 characters. 62 | //! - Password must contain at least one digit. 63 | //! - Password must contain any of the following characters: '@', '#'. 64 | //! - Password must not begin with the string "pass" (case-insensitive). 65 | //! - Password must not contain the strings "pass" or "word" (case-insensitive). 66 | //! 67 | //!```rust 68 | //! //1. Write a matcher. 69 | //! use std::fmt::Debug; 70 | //! 71 | //! use clearcheck::matchers::{BoxWrap, Should}; 72 | //! use clearcheck::matchers::compose::{Matchers, MatchersBuilder}; 73 | //! use clearcheck::matchers::string::boundary::begin_with; 74 | //! use clearcheck::matchers::string::empty::be_empty; 75 | //! use clearcheck::matchers::string::length::have_atleast_same_length; 76 | //! use clearcheck::matchers::string::membership::{ 77 | //! contain_a_digit, 78 | //! contain_any_of_characters, 79 | //! contain_ignoring_case 80 | //! }; 81 | //! 82 | //! fn be_a_valid_password + Debug>() -> Matchers { 83 | //! MatchersBuilder::start_building_with_inverted(be_empty().boxed()) 84 | //! .push(have_atleast_same_length(10).boxed()) 85 | //! .push(contain_a_digit().boxed()) 86 | //! .push(contain_any_of_characters(vec!['@', '#']).boxed()) 87 | //! .push_inverted(begin_with("pass").boxed()) 88 | //! .push_inverted(contain_ignoring_case("pass").boxed()) 89 | //! .push_inverted(contain_ignoring_case("word").boxed()) 90 | //! .combine_as_and() 91 | //! } 92 | //! 93 | //! //2. Combine the matcher into a powerful assertion for valid passwords. 94 | //! trait PasswordAssertion { 95 | //! fn should_be_a_valid_password(&self) -> &Self; 96 | //! } 97 | //! 98 | //! impl PasswordAssertion for &str { 99 | //! fn should_be_a_valid_password(&self) -> &Self { 100 | //! self.should(&be_a_valid_password()); 101 | //! self 102 | //! } 103 | //! } 104 | //! 105 | //! //3. That's it. Use the password assertion. 106 | //! #[test] 107 | //! fn should_be_a_valid_password() { 108 | //! let password = "P@@sw0rd9082"; 109 | //! password.should_be_a_valid_password(); 110 | //! } 111 | //! ``` 112 | 113 | pub mod assertions; 114 | pub mod matchers; -------------------------------------------------------------------------------- /src/matchers/map/length.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::hash::Hash; 3 | 4 | use crate::matchers::{Matcher, MatcherResult}; 5 | 6 | /// MapLengthMatcher offers a flexible way to assert various length properties of HashMap. 7 | /// 8 | /// # Example 9 | ///``` 10 | /// use std::collections::HashMap; 11 | /// use clearcheck::matchers::map::length::have_atleast_same_length; 12 | /// use clearcheck::matchers::Matcher; 13 | /// 14 | /// let mut key_value = HashMap::new(); 15 | /// key_value.insert("rust", "clearcheck"); 16 | /// key_value.insert("java", "junit"); 17 | /// 18 | /// let matcher = have_atleast_same_length(2); 19 | /// 20 | /// assert!(matcher.test(&key_value).passed()); 21 | /// ``` 22 | pub enum MapLengthMatcher { 23 | Same(usize), 24 | Atleast(usize), 25 | Atmost(usize), 26 | } 27 | 28 | impl Matcher> for MapLengthMatcher { 29 | fn test(&self, collection: &HashMap) -> MatcherResult { 30 | self.test_length(collection.len()) 31 | } 32 | } 33 | 34 | impl MapLengthMatcher { 35 | fn test_length(&self, input_length: usize) -> MatcherResult { 36 | let message_prefix = "Map"; 37 | match self { 38 | MapLengthMatcher::Same(length) => MatcherResult::formatted( 39 | input_length == *length, 40 | format!( 41 | "{:?} length {:?} should be {:?}", 42 | message_prefix, input_length, length 43 | ), 44 | format!( 45 | "{:?} length {:?} should not be {:?}", 46 | message_prefix, input_length, length 47 | ), 48 | ), 49 | MapLengthMatcher::Atleast(length) => MatcherResult::formatted( 50 | input_length >= *length, 51 | format!( 52 | "{:?} length {:?} should be atleast {:?}", 53 | message_prefix, input_length, length 54 | ), 55 | format!( 56 | "{:?} length {:?} should not be atleast {:?}", 57 | message_prefix, input_length, length 58 | ), 59 | ), 60 | MapLengthMatcher::Atmost(length) => MatcherResult::formatted( 61 | input_length <= *length, 62 | format!( 63 | "{:?} length {:?} should be atmost {:?}", 64 | message_prefix, input_length, length 65 | ), 66 | format!( 67 | "{:?} length {:?} should not be atmost {:?}", 68 | message_prefix, input_length, length 69 | ), 70 | ), 71 | } 72 | } 73 | } 74 | 75 | /// Creates a MapLengthMatcher that asserts whether the length of a HashMap is same as the given length. 76 | pub fn have_same_length(length: usize) -> MapLengthMatcher { 77 | MapLengthMatcher::Same(length) 78 | } 79 | 80 | /// Creates a MapLengthMatcher that asserts whether the length of a HashMap is greater than or equal to the given length. 81 | pub fn have_atleast_same_length(length: usize) -> MapLengthMatcher { 82 | MapLengthMatcher::Atleast(length) 83 | } 84 | 85 | /// Creates a MapLengthMatcher that asserts whether the length of a HashMap is less than or equal to the given length. 86 | pub fn have_atmost_same_length(length: usize) -> MapLengthMatcher { 87 | MapLengthMatcher::Atmost(length) 88 | } 89 | 90 | #[cfg(test)] 91 | mod tests { 92 | use std::collections::HashMap; 93 | 94 | use crate::assertions::bool::TrueFalseAssertion; 95 | use crate::matchers::map::length::{have_atleast_same_length, have_atmost_same_length, have_same_length}; 96 | use crate::matchers::Matcher; 97 | 98 | #[test] 99 | fn should_have_same_length() { 100 | let mut key_value = HashMap::new(); 101 | key_value.insert(1, 10); 102 | key_value.insert(2, 20); 103 | 104 | let matcher = have_same_length(2); 105 | matcher.test(&key_value).passed.should_be_true(); 106 | } 107 | 108 | #[test] 109 | #[should_panic] 110 | fn should_have_same_length_but_was_not() { 111 | let mut key_value = HashMap::new(); 112 | key_value.insert(1, 10); 113 | key_value.insert(2, 20); 114 | 115 | let matcher = have_same_length(5); 116 | matcher.test(&key_value).passed.should_be_true(); 117 | } 118 | 119 | #[test] 120 | fn should_have_atleast_same_length() { 121 | let mut key_value = HashMap::new(); 122 | key_value.insert(1, 10); 123 | key_value.insert(2, 20); 124 | 125 | let matcher = have_atleast_same_length(2); 126 | matcher.test(&key_value).passed.should_be_true(); 127 | } 128 | 129 | #[test] 130 | #[should_panic] 131 | fn should_have_atleast_same_length_but_was_not() { 132 | let mut key_value = HashMap::new(); 133 | key_value.insert(1, 10); 134 | key_value.insert(2, 20); 135 | 136 | let matcher = have_atleast_same_length(6); 137 | matcher.test(&key_value).passed.should_be_true(); 138 | } 139 | 140 | #[test] 141 | fn should_have_atmost_length() { 142 | let mut key_value = HashMap::new(); 143 | key_value.insert(1, 10); 144 | key_value.insert(2, 20); 145 | 146 | let matcher = have_atmost_same_length(2); 147 | matcher.test(&key_value).passed.should_be_true(); 148 | } 149 | 150 | #[test] 151 | #[should_panic] 152 | fn should_have_atmost_length_but_was_not() { 153 | let mut key_value = HashMap::new(); 154 | key_value.insert(1, 10); 155 | key_value.insert(2, 20); 156 | 157 | let matcher = have_atmost_same_length(1); 158 | matcher.test(&key_value).passed.should_be_true(); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/assertions/int/mod.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use num::Integer; 4 | 5 | use crate::matchers::int::{be_even, be_negative, be_odd, be_positive, be_zero}; 6 | use crate::matchers::{Should, ShouldNot}; 7 | 8 | /// IntAssertion enables assertions about various properties of integers. 9 | /// 10 | /// It offers a fluent interface for chaining multiple assertions. 11 | /// 12 | /// # Example 13 | /// ``` 14 | /// use clearcheck::assertions::int::IntAssertion; 15 | /// use clearcheck::assertions::ordered::OrderedAssertion; 16 | /// 17 | /// let value = 24; 18 | /// value 19 | /// .should_be_positive() 20 | /// .should_be_even() 21 | /// .should_be_in_inclusive_range(10..=40); 22 | /// ``` 23 | pub trait IntAssertion { 24 | /// - Asserts that the integer value is positive. 25 | /// - Returns a reference to self for fluent chaining. 26 | /// - Panics if the assertion fails. 27 | /// # Example 28 | /// ``` 29 | /// use clearcheck::assertions::int::IntAssertion; 30 | /// 31 | /// let value = 10; 32 | /// value.should_be_positive(); 33 | /// ``` 34 | fn should_be_positive(&self) -> &Self; 35 | 36 | /// - Asserts that the integer value is negative. 37 | /// - Returns a reference to self for fluent chaining. 38 | /// - Panics if the assertion fails. 39 | /// # Example 40 | /// ``` 41 | /// use clearcheck::assertions::int::IntAssertion; 42 | /// 43 | /// let value = -10; 44 | /// value.should_be_negative(); 45 | /// ``` 46 | fn should_be_negative(&self) -> &Self; 47 | 48 | /// - Asserts that the integer value is even. 49 | /// - Returns a reference to self for fluent chaining. 50 | /// - Panics if the assertion fails. 51 | /// # Example 52 | /// ``` 53 | /// use clearcheck::assertions::int::IntAssertion; 54 | /// 55 | /// let value = 14; 56 | /// value.should_be_even(); 57 | /// ``` 58 | fn should_be_even(&self) -> &Self; 59 | 60 | /// - Asserts that the integer value is odd. 61 | /// - Returns a reference to self for fluent chaining. 62 | /// - Panics if the assertion fails. 63 | /// # Example 64 | /// ``` 65 | /// use clearcheck::assertions::int::IntAssertion; 66 | /// 67 | /// let value = 5; 68 | /// value.should_be_odd(); 69 | /// ``` 70 | fn should_be_odd(&self) -> &Self; 71 | 72 | /// - Asserts that the integer value is zero. 73 | /// - Returns a reference to self for fluent chaining. 74 | /// - Panics if the assertion fails. 75 | /// # Example 76 | /// ``` 77 | /// use clearcheck::assertions::int::IntAssertion; 78 | /// 79 | /// let value = 0; 80 | /// value.should_be_zero(); 81 | /// ``` 82 | fn should_be_zero(&self) -> &Self; 83 | 84 | /// - Asserts that the integer value is not zero. 85 | /// - Returns a reference to self for fluent chaining. 86 | /// - Panics if the assertion fails. 87 | /// # Example 88 | /// ``` 89 | /// use clearcheck::assertions::int::IntAssertion; 90 | /// 91 | /// let value = 5; 92 | /// value.should_not_be_zero(); 93 | /// ``` 94 | fn should_not_be_zero(&self) -> &Self; 95 | } 96 | 97 | impl IntAssertion for T { 98 | fn should_be_positive(&self) -> &Self { 99 | self.should(&be_positive()); 100 | self 101 | } 102 | 103 | fn should_be_negative(&self) -> &Self { 104 | self.should(&be_negative()); 105 | self 106 | } 107 | 108 | fn should_be_even(&self) -> &Self { 109 | self.should(&be_even()); 110 | self 111 | } 112 | 113 | fn should_be_odd(&self) -> &Self { 114 | self.should(&be_odd()); 115 | self 116 | } 117 | 118 | fn should_be_zero(&self) -> &Self { 119 | self.should(&be_zero()); 120 | self 121 | } 122 | 123 | fn should_not_be_zero(&self) -> &Self { 124 | self.should_not(&be_zero()); 125 | self 126 | } 127 | } 128 | 129 | #[cfg(all(test, feature = "num"))] 130 | mod tests { 131 | use crate::assertions::int::IntAssertion; 132 | 133 | #[test] 134 | fn should_be_positive() { 135 | let value = 10; 136 | value.should_be_positive(); 137 | } 138 | 139 | #[test] 140 | #[should_panic] 141 | fn should_be_positive_but_was_not() { 142 | let value = -10; 143 | value.should_be_positive(); 144 | } 145 | 146 | #[test] 147 | fn should_be_negative() { 148 | let value = -10; 149 | value.should_be_negative(); 150 | } 151 | 152 | #[test] 153 | #[should_panic] 154 | fn should_be_negative_but_was_not() { 155 | let value = 10; 156 | value.should_be_negative(); 157 | } 158 | 159 | #[test] 160 | fn should_be_even() { 161 | let value = 10; 162 | value.should_be_even(); 163 | } 164 | 165 | #[test] 166 | #[should_panic] 167 | fn should_be_even_but_was_not() { 168 | let value = -11; 169 | value.should_be_even(); 170 | } 171 | 172 | #[test] 173 | fn should_be_odd() { 174 | let value = 11; 175 | value.should_be_odd(); 176 | } 177 | 178 | #[test] 179 | #[should_panic] 180 | fn should_be_odd_but_was_not() { 181 | let value = -10; 182 | value.should_be_odd(); 183 | } 184 | 185 | #[test] 186 | fn should_be_zero() { 187 | let value = 0; 188 | value.should_be_zero(); 189 | } 190 | 191 | #[test] 192 | #[should_panic] 193 | fn should_be_zero_but_was_not() { 194 | let value = -10; 195 | value.should_be_zero(); 196 | } 197 | 198 | #[test] 199 | fn should_not_be_zero() { 200 | let value = 1; 201 | value.should_not_be_zero(); 202 | } 203 | 204 | #[test] 205 | #[should_panic] 206 | fn should_not_be_zero_but_was() { 207 | let value = 0; 208 | value.should_not_be_zero(); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/matchers/collection/equal.rs: -------------------------------------------------------------------------------- 1 | //! provides [IgnoreCaseEqualityMatcher] for collection of elements where the elements can be represented as strings. 2 | 3 | use std::collections::HashSet; 4 | use std::fmt::Debug; 5 | 6 | use crate::matchers::equal::IgnoreCaseEqualityMatcher; 7 | use crate::matchers::{Matcher, MatcherResult}; 8 | 9 | impl Matcher<[String; N]> for IgnoreCaseEqualityMatcher<[String; N]> { 10 | fn test(&self, collection: &[String; N]) -> MatcherResult { 11 | let one: HashSet<_> = collection 12 | .iter() 13 | .map(|source| source.to_lowercase()) 14 | .collect(); 15 | let other: HashSet<_> = self 16 | .other 17 | .iter() 18 | .map(|source| source.to_lowercase()) 19 | .collect(); 20 | 21 | MatcherResult::formatted( 22 | one == other, 23 | format!("{:?} should equal {:?}", collection, self.other), 24 | format!("{:?} should not equal {:?}", collection, self.other), 25 | ) 26 | } 27 | } 28 | 29 | impl Matcher<[&str; N]> for IgnoreCaseEqualityMatcher<[&str; N]> { 30 | fn test(&self, collection: &[&str; N]) -> MatcherResult { 31 | let one: HashSet<_> = collection 32 | .iter() 33 | .map(|source| source.to_lowercase()) 34 | .collect(); 35 | let other: HashSet<_> = self 36 | .other 37 | .iter() 38 | .map(|source| source.to_lowercase()) 39 | .collect(); 40 | 41 | MatcherResult::formatted( 42 | one == other, 43 | format!("{:?} should equal {:?}", collection, self.other), 44 | format!("{:?} should not equal {:?}", collection, self.other), 45 | ) 46 | } 47 | } 48 | 49 | impl Matcher> for IgnoreCaseEqualityMatcher> 50 | where 51 | T: AsRef + Debug + Eq, 52 | { 53 | fn test(&self, collection: &Vec) -> MatcherResult { 54 | let one: HashSet<_> = collection 55 | .iter() 56 | .map(|source| source.as_ref().to_lowercase()) 57 | .collect(); 58 | let other: HashSet<_> = self 59 | .other 60 | .iter() 61 | .map(|source| source.as_ref().to_lowercase()) 62 | .collect(); 63 | 64 | MatcherResult::formatted( 65 | one == other, 66 | format!("{:?} should equal {:?}", collection, self.other), 67 | format!("{:?} should not equal {:?}", collection, self.other), 68 | ) 69 | } 70 | } 71 | 72 | impl Matcher<&[T]> for IgnoreCaseEqualityMatcher<&[T]> 73 | where 74 | T: AsRef + Debug + Eq, 75 | { 76 | fn test(&self, collection: &&[T]) -> MatcherResult { 77 | let one: HashSet<_> = collection 78 | .iter() 79 | .map(|source| source.as_ref().to_lowercase()) 80 | .collect(); 81 | let other: HashSet<_> = self 82 | .other 83 | .iter() 84 | .map(|source| source.as_ref().to_lowercase()) 85 | .collect(); 86 | 87 | MatcherResult::formatted( 88 | one == other, 89 | format!("{:?} should equal {:?}", collection, self.other), 90 | format!("{:?} should not equal {:?}", collection, self.other), 91 | ) 92 | } 93 | } 94 | 95 | #[cfg(test)] 96 | mod vector_tests { 97 | use crate::assertions::bool::TrueFalseAssertion; 98 | use crate::matchers::equal::be_equal_ignoring_case; 99 | use crate::matchers::Matcher; 100 | 101 | #[test] 102 | fn should_equal_ignoring_case() { 103 | let collection = vec!["junit", "clearcheck", "gotest"]; 104 | let other = vec!["JUNIT", "clearcheck", "GoTest"]; 105 | 106 | let matcher = be_equal_ignoring_case(other); 107 | matcher.test(&collection).passed.should_be_true(); 108 | } 109 | 110 | #[test] 111 | #[should_panic] 112 | fn should_equal_ignoring_case_but_was_not() { 113 | let collection = vec!["junit", "clearcheck", "gotest"]; 114 | let other = vec!["JUNIT", "ASSERT", "GoTest"]; 115 | 116 | let matcher = be_equal_ignoring_case(other); 117 | matcher.test(&collection).passed.should_be_true(); 118 | } 119 | } 120 | 121 | #[cfg(test)] 122 | mod array_tests { 123 | use crate::assertions::bool::TrueFalseAssertion; 124 | use crate::matchers::equal::be_equal_ignoring_case; 125 | use crate::matchers::Matcher; 126 | 127 | #[test] 128 | fn should_equal_ignoring_case() { 129 | let collection = ["junit", "clearcheck", "gotest"]; 130 | let other = ["JUNIT", "clearcheck", "GoTest"]; 131 | 132 | let matcher = be_equal_ignoring_case(other); 133 | matcher.test(&collection).passed.should_be_true(); 134 | } 135 | 136 | #[test] 137 | #[should_panic] 138 | fn should_equal_ignoring_case_but_was_not() { 139 | let collection = ["junit", "clearcheck", "gotest"]; 140 | let other = ["JUNIT", "ASSERT", "GoTest"]; 141 | 142 | let matcher = be_equal_ignoring_case(other); 143 | matcher.test(&collection).passed.should_be_true(); 144 | } 145 | } 146 | 147 | #[cfg(test)] 148 | mod slice_tests { 149 | use crate::assertions::bool::TrueFalseAssertion; 150 | use crate::matchers::equal::be_equal_ignoring_case; 151 | use crate::matchers::Matcher; 152 | 153 | #[test] 154 | fn should_equal_ignoring_case() { 155 | let collection: &[&str] = &["junit", "clearcheck", "gotest"]; 156 | let other: &[&str] = &["JUNIT", "clearcheck", "GoTest"]; 157 | 158 | let matcher = be_equal_ignoring_case(other); 159 | matcher.test(&collection).passed.should_be_true(); 160 | } 161 | 162 | #[test] 163 | #[should_panic] 164 | fn should_equal_ignoring_case_but_was_not() { 165 | let collection: &[&str] = &["junit", "clearcheck", "gotest"]; 166 | let other: &[&str] = &["JUNIT", "ASSERT", "GoTest"]; 167 | 168 | let matcher = be_equal_ignoring_case(other); 169 | matcher.test(&collection).passed.should_be_true(); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/matchers/collection/membership.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use crate::matchers::{Matcher, MatcherResult}; 4 | 5 | /// MembershipMatcher offers a flexible way to assert the presence or absence of specific elements within collections. 6 | /// 7 | /// Works with any data type that implements the Eq and Debug trait. 8 | /// 9 | /// clearcheck implements MembershipMatcher for collection types including vector, arrays and reference to slices. 10 | /// 11 | /// # Example 12 | ///``` 13 | /// use clearcheck::matchers::collection::membership::contain_all; 14 | /// use clearcheck::matchers::Matcher; 15 | /// 16 | /// let collection = vec!["clearcheck", "testify", "assert4j", "xunit"]; 17 | /// let all_to_be_contained = vec!["testify", "assert4j", "xunit"]; 18 | /// 19 | /// let matcher = contain_all(all_to_be_contained); 20 | /// 21 | /// assert!(matcher.test(&collection).passed()); 22 | /// ``` 23 | pub enum MembershipMatcher { 24 | Contain(T), 25 | ContainAll(Vec), 26 | ContainAny(Vec), 27 | } 28 | 29 | impl MembershipMatcher { 30 | fn test(&self, collection: &[T]) -> MatcherResult { 31 | match self { 32 | MembershipMatcher::Contain(element) => MatcherResult::formatted( 33 | collection.contains(element), 34 | format!("{:?} should contain {:?}", collection, element), 35 | format!("{:?} should not contain {:?}", collection, element), 36 | ), 37 | MembershipMatcher::ContainAll(target) => { 38 | let missing = target 39 | .iter() 40 | .filter(|element| !collection.contains(element)) 41 | .collect::>(); 42 | 43 | MatcherResult::formatted( 44 | missing.is_empty(), 45 | format!( 46 | "{:?} should contain {:?} but was missing {:?}", 47 | collection, target, missing 48 | ), 49 | format!("{:?} should not contain {:?}", collection, target), 50 | ) 51 | } 52 | MembershipMatcher::ContainAny(target) => MatcherResult::formatted( 53 | target.iter().any(|source| collection.contains(source)), 54 | format!("{:?} should contain any of {:?}", collection, target), 55 | format!("{:?} should not contain any of {:?}", collection, target), 56 | ), 57 | } 58 | } 59 | } 60 | 61 | impl Matcher> for MembershipMatcher 62 | where 63 | T: Eq + Debug, 64 | { 65 | fn test(&self, collection: &Vec) -> MatcherResult { 66 | self.test(collection) 67 | } 68 | } 69 | 70 | impl Matcher<[T; N]> for MembershipMatcher 71 | where 72 | T: Eq + Debug, 73 | { 74 | fn test(&self, collection: &[T; N]) -> MatcherResult { 75 | self.test(collection as &[T]) 76 | } 77 | } 78 | 79 | impl Matcher<&[T]> for MembershipMatcher 80 | where 81 | T: Eq + Debug, 82 | { 83 | fn test(&self, collection: &&[T]) -> MatcherResult { 84 | self.test(collection) 85 | } 86 | } 87 | 88 | /// Creates a MembershipMatcher that asserts whether a collection contains the given element. 89 | pub fn contain(element: T) -> MembershipMatcher 90 | where 91 | T: Eq + Debug, 92 | { 93 | MembershipMatcher::Contain(element) 94 | } 95 | 96 | /// Creates a MembershipMatcher that asserts whether a collection contains all the given elements. 97 | pub fn contain_all(elements: Vec) -> MembershipMatcher 98 | where 99 | T: Eq + Debug, 100 | { 101 | MembershipMatcher::ContainAll(elements) 102 | } 103 | 104 | /// Creates a MembershipMatcher that asserts whether a collection contains any of the given elements. 105 | pub fn contain_any(elements: Vec) -> MembershipMatcher 106 | where 107 | T: Eq + Debug, 108 | { 109 | MembershipMatcher::ContainAny(elements) 110 | } 111 | 112 | #[cfg(test)] 113 | mod tests { 114 | use crate::assertions::bool::TrueFalseAssertion; 115 | use crate::matchers::collection::membership::{contain, contain_all, contain_any}; 116 | 117 | #[test] 118 | fn should_contain() { 119 | let collection = vec!["junit", "testify"]; 120 | let matcher = contain("junit"); 121 | matcher.test(&collection).passed.should_be_true(); 122 | } 123 | 124 | #[test] 125 | #[should_panic] 126 | fn should_contain_but_id_did_not() { 127 | let collection = vec!["unit4j", "testify"]; 128 | let matcher = contain("junit"); 129 | matcher.test(&collection).passed.should_be_true(); 130 | } 131 | 132 | #[test] 133 | fn should_contain_all_elements() { 134 | let collection = vec!["junit", "testify", "assert4j", "xunit"]; 135 | let all_to_be_contained = vec!["testify", "assert4j", "xunit"]; 136 | let matcher = contain_all(all_to_be_contained); 137 | matcher.test(&collection).passed.should_be_true(); 138 | } 139 | 140 | #[test] 141 | #[should_panic] 142 | fn should_contain_all_elements_but_it_did_not() { 143 | let collection = vec!["junit", "testify", "assert4j", "xunit"]; 144 | let all_to_be_contained = vec!["testify", "assert", "xunit"]; 145 | let matcher = contain_all(all_to_be_contained); 146 | matcher.test(&collection).passed.should_be_true(); 147 | } 148 | 149 | #[test] 150 | fn should_contain_any_of_elements() { 151 | let collection = vec!["junit", "testify", "assert4j", "xunit"]; 152 | let to_be_contained = vec!["testify", "catch", "xunit"]; 153 | let matcher = contain_any(to_be_contained); 154 | matcher.test(&collection).passed.should_be_true(); 155 | } 156 | 157 | #[test] 158 | #[should_panic] 159 | fn should_contain_any_of_elements_but_it_did_not() { 160 | let collection = vec!["junit", "testify", "assert4j", "xunit"]; 161 | let to_be_contained = vec!["catch", "catch2"]; 162 | let matcher = contain_any(to_be_contained); 163 | matcher.test(&collection).passed.should_be_true(); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/assertions/string/boundary.rs: -------------------------------------------------------------------------------- 1 | use crate::matchers::{Should, ShouldNot}; 2 | use crate::matchers::string::boundary::{begin_with, end_with}; 3 | 4 | /// BoundaryAssertion enables assertions about the beginning and the ending boundaries of string (or str) values. 5 | /// 6 | /// It offers a fluent interface for chaining multiple assertions. 7 | /// 8 | /// # Example 9 | /// ``` 10 | /// use clearcheck::assertions::string::boundary::BoundaryAssertion; 11 | /// 12 | /// let value = "clearcheck"; 13 | /// value 14 | /// .should_begin_with("clear") 15 | /// .should_end_with("check"); 16 | /// ``` 17 | pub trait BoundaryAssertion { 18 | /// - Asserts that the string begins with the given prefix. 19 | /// - Returns a reference to self for fluent chaining. 20 | /// - Panics if the assertion fails. 21 | /// # Example 22 | /// ``` 23 | /// use clearcheck::assertions::string::boundary::BoundaryAssertion; 24 | /// 25 | /// let value = "clearcheck"; 26 | /// value.should_begin_with("clear"); 27 | /// ``` 28 | fn should_begin_with(&self, prefix: &'static str) -> &Self; 29 | 30 | /// - Asserts that the string does not begin with the given prefix 31 | /// - Returns a reference to self for fluent chaining. 32 | /// - Panics if the assertion fails. 33 | /// # Example 34 | /// ``` 35 | /// use clearcheck::assertions::string::boundary::BoundaryAssertion; 36 | /// 37 | /// let value = "clearcheck"; 38 | /// value.should_not_begin_with("rust"); 39 | /// ``` 40 | fn should_not_begin_with(&self, prefix: &'static str) -> &Self; 41 | 42 | /// - Asserts that the string ends with the given suffix. 43 | /// - Returns a reference to self for fluent chaining. 44 | /// - Panics if the assertion fails. 45 | /// # Example 46 | /// ``` 47 | /// use clearcheck::assertions::string::boundary::BoundaryAssertion; 48 | /// 49 | /// let value = "clearcheck"; 50 | /// value.should_end_with("check"); 51 | /// ``` 52 | fn should_end_with(&self, suffix: &'static str) -> &Self; 53 | 54 | /// - Asserts that the string does not end with the given suffix. 55 | /// - Returns a reference to self for fluent chaining. 56 | /// - Panics if the assertion fails. 57 | /// # Example 58 | /// ``` 59 | /// use clearcheck::assertions::string::boundary::BoundaryAssertion; 60 | /// 61 | /// let value = "clearcheck"; 62 | /// value.should_not_end_with("test"); 63 | /// ``` 64 | fn should_not_end_with(&self, suffix: &'static str) -> &Self; 65 | } 66 | 67 | impl BoundaryAssertion for T 68 | where T: AsRef { 69 | fn should_begin_with(&self, prefix: &'static str) -> &Self { 70 | self.should(&begin_with(prefix)); 71 | self 72 | } 73 | 74 | fn should_not_begin_with(&self, prefix: &'static str) -> &Self { 75 | self.should_not(&begin_with(prefix)); 76 | self 77 | } 78 | 79 | fn should_end_with(&self, suffix: &'static str) -> &Self { 80 | self.should(&end_with(suffix)); 81 | self 82 | } 83 | 84 | fn should_not_end_with(&self, suffix: &'static str) -> &Self { 85 | self.should_not(&end_with(suffix)); 86 | self 87 | } 88 | } 89 | 90 | #[cfg(test)] 91 | mod tests { 92 | use crate::assertions::string::boundary::BoundaryAssertion; 93 | 94 | #[test] 95 | fn should_begin_with() { 96 | let library = "cacheD"; 97 | library.should_begin_with("cache"); 98 | } 99 | 100 | #[test] 101 | #[should_panic] 102 | fn should_begin_with_but_it_did_not() { 103 | let library = "junit"; 104 | library.should_begin_with("unit"); 105 | } 106 | 107 | #[test] 108 | fn should_not_begin_with() { 109 | let library = "junit"; 110 | library.should_not_begin_with("cache"); 111 | } 112 | 113 | #[test] 114 | #[should_panic] 115 | fn should_not_begin_with_but_it_did() { 116 | let library = "junit"; 117 | library.should_not_begin_with("jun"); 118 | } 119 | 120 | #[test] 121 | fn should_end_with() { 122 | let library = "goselect"; 123 | library.should_end_with("select"); 124 | } 125 | 126 | #[test] 127 | #[should_panic] 128 | fn should_end_with_but_it_did_not() { 129 | let library = "junit"; 130 | library.should_end_with("et"); 131 | } 132 | 133 | #[test] 134 | fn should_not_end_with() { 135 | let library = "junit"; 136 | library.should_not_end_with("cache"); 137 | } 138 | 139 | #[test] 140 | #[should_panic] 141 | fn should_not_end_with_but_it_did() { 142 | let library = "junit"; 143 | library.should_not_end_with("unit"); 144 | } 145 | } 146 | 147 | #[cfg(test)] 148 | mod string_tests { 149 | use crate::assertions::string::boundary::BoundaryAssertion; 150 | 151 | #[test] 152 | fn should_begin_with() { 153 | let library = String::from("cacheD"); 154 | library.should_begin_with("cache"); 155 | } 156 | 157 | #[test] 158 | #[should_panic] 159 | fn should_begin_with_but_it_did_not() { 160 | let library = String::from("junit"); 161 | library.should_begin_with("unit"); 162 | } 163 | 164 | #[test] 165 | fn should_not_begin_with() { 166 | let library = String::from("junit"); 167 | library.should_not_begin_with("cache"); 168 | } 169 | 170 | #[test] 171 | #[should_panic] 172 | fn should_not_begin_with_but_it_did() { 173 | let library = String::from("junit"); 174 | library.should_not_begin_with("jun"); 175 | } 176 | 177 | #[test] 178 | fn should_end_with() { 179 | let library = String::from("goselect"); 180 | library.should_end_with("select"); 181 | } 182 | 183 | #[test] 184 | #[should_panic] 185 | fn should_end_with_but_it_did_not() { 186 | let library = String::from("junit"); 187 | library.should_end_with("et"); 188 | } 189 | 190 | #[test] 191 | fn should_not_end_with() { 192 | let library = String::from("junit"); 193 | library.should_not_end_with("cache"); 194 | } 195 | 196 | #[test] 197 | #[should_panic] 198 | fn should_not_end_with_but_it_did() { 199 | let library = String::from("junit"); 200 | library.should_not_end_with("unit"); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/matchers/collection/increasing_decreasing.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use crate::matchers::{Matcher, MatcherResult}; 4 | 5 | /// OrderedMatcher offers a flexible way to assert whether a sequence of values exhibits a specific pattern of increasing or decreasing behavior. 6 | /// 7 | /// clearcheck implements OrderedMatcher for collection types including vector, arrays and reference to slices. 8 | /// 9 | /// # Example 10 | ///``` 11 | /// use clearcheck::matchers::collection::increasing_decreasing::be_monotonically_increasing; 12 | /// use clearcheck::matchers::Matcher; 13 | /// 14 | /// let matcher = be_monotonically_increasing(); 15 | /// let collection = vec![1, 2, 3, 4, 4, 5, 5, 6]; 16 | /// 17 | /// assert!(matcher.test(&collection).passed()); 18 | /// ``` 19 | pub enum IncreasingDecreasingMatcher { 20 | MonotonicallyIncreasing, 21 | MonotonicallyDecreasing, 22 | StrictlyIncreasing, 23 | StrictlyDecreasing, 24 | } 25 | 26 | impl IncreasingDecreasingMatcher { 27 | fn test(&self, collection: &[T]) -> MatcherResult { 28 | match self { 29 | IncreasingDecreasingMatcher::MonotonicallyIncreasing => MatcherResult::formatted( 30 | collection.windows(2).all(|window| window[0] <= window[1]), 31 | format!("{:?} should be monotonically increasing", collection), 32 | format!("{:?} should not be monotonically increasing", collection), 33 | ), 34 | IncreasingDecreasingMatcher::MonotonicallyDecreasing => MatcherResult::formatted( 35 | collection.windows(2).all(|window| window[0] >= window[1]), 36 | format!("{:?} should be monotonically decreasing", collection), 37 | format!("{:?} should not be monotonically decreasing", collection), 38 | ), 39 | IncreasingDecreasingMatcher::StrictlyIncreasing => MatcherResult::formatted( 40 | collection.windows(2).all(|window| window[0] < window[1]), 41 | format!("{:?} should be strictly increasing", collection), 42 | format!("{:?} should not be strictly increasing", collection), 43 | ), 44 | IncreasingDecreasingMatcher::StrictlyDecreasing => MatcherResult::formatted( 45 | collection.windows(2).all(|window| window[0] > window[1]), 46 | format!("{:?} should be strictly decreasing", collection), 47 | format!("{:?} should not be strictly decreasing", collection), 48 | ), 49 | } 50 | } 51 | } 52 | 53 | impl Matcher> for IncreasingDecreasingMatcher { 54 | fn test(&self, collection: &Vec) -> MatcherResult { 55 | self.test(collection) 56 | } 57 | } 58 | 59 | impl Matcher<[T; N]> for IncreasingDecreasingMatcher { 60 | fn test(&self, collection: &[T; N]) -> MatcherResult { 61 | self.test(collection as &[T]) 62 | } 63 | } 64 | 65 | impl Matcher<&[T]> for IncreasingDecreasingMatcher { 66 | fn test(&self, collection: &&[T]) -> MatcherResult { 67 | self.test(collection) 68 | } 69 | } 70 | 71 | /// Creates an IncreasingDecreasingMatcher that asserts whether the elements in a collection are in non-decreasing order (allowing consecutive equal elements). 72 | pub fn be_monotonically_increasing() -> IncreasingDecreasingMatcher { 73 | IncreasingDecreasingMatcher::MonotonicallyIncreasing 74 | } 75 | 76 | /// Creates an IncreasingDecreasingMatcher that asserts that the elements in a collection are in non-increasing order (allowing consecutive equal elements). 77 | pub fn be_monotonically_decreasing() -> IncreasingDecreasingMatcher { 78 | IncreasingDecreasingMatcher::MonotonicallyDecreasing 79 | } 80 | 81 | /// Creates an IncreasingDecreasingMatcher that asserts that the elements in a collection are in strictly increasing order (no consecutive elements can be equal). 82 | pub fn be_strictly_increasing() -> IncreasingDecreasingMatcher { 83 | IncreasingDecreasingMatcher::StrictlyIncreasing 84 | } 85 | 86 | /// Creates an IncreasingDecreasingMatcher that asserts that the elements in a collection are in strictly decreasing order (no consecutive elements can be equal). 87 | pub fn be_strictly_decreasing() -> IncreasingDecreasingMatcher { 88 | IncreasingDecreasingMatcher::StrictlyDecreasing 89 | } 90 | 91 | #[cfg(test)] 92 | mod test { 93 | use crate::assertions::bool::TrueFalseAssertion; 94 | use crate::matchers::collection::increasing_decreasing::{ 95 | be_monotonically_decreasing, be_monotonically_increasing, be_strictly_decreasing, 96 | be_strictly_increasing, 97 | }; 98 | 99 | #[test] 100 | fn should_be_monotonically_increasing() { 101 | let matcher = be_monotonically_increasing(); 102 | let collection = vec![1, 2, 3, 4, 4, 5, 5, 6]; 103 | matcher.test(&collection).passed.should_be_true(); 104 | } 105 | 106 | #[test] 107 | #[should_panic] 108 | fn should_be_monotonically_increasing_but_was_not() { 109 | let matcher = be_monotonically_increasing(); 110 | let collection = vec![1, 2, 3, 4, 4, 2, 5, 6]; 111 | matcher.test(&collection).passed.should_be_true(); 112 | } 113 | 114 | #[test] 115 | fn should_be_monotonically_decreasing() { 116 | let matcher = be_monotonically_decreasing(); 117 | let collection = vec![6, 6, 5, 5, 4, 4, 2]; 118 | matcher.test(&collection).passed.should_be_true(); 119 | } 120 | 121 | #[test] 122 | #[should_panic] 123 | fn should_be_monotonically_decreasing_but_was_not() { 124 | let matcher = be_monotonically_increasing(); 125 | let collection = vec![6, 6, 5, 5, 4, 4, 5]; 126 | matcher.test(&collection).passed.should_be_true(); 127 | } 128 | 129 | #[test] 130 | fn should_be_strictly_increasing() { 131 | let matcher = be_strictly_increasing(); 132 | let collection = vec![1, 3, 5, 7, 9]; 133 | matcher.test(&collection).passed.should_be_true(); 134 | } 135 | 136 | #[test] 137 | #[should_panic] 138 | fn should_be_strictly_increasing_but_was_not() { 139 | let matcher = be_strictly_increasing(); 140 | let collection = vec![1, 5, 7, 9, 9]; 141 | matcher.test(&collection).passed.should_be_true(); 142 | } 143 | 144 | #[test] 145 | fn should_be_strictly_decreasing() { 146 | let matcher = be_strictly_decreasing(); 147 | let collection = vec![9, 7, 5, 3, 1]; 148 | matcher.test(&collection).passed.should_be_true(); 149 | } 150 | 151 | #[test] 152 | #[should_panic] 153 | fn should_be_strictly_decreasing_but_was_not() { 154 | let matcher = be_strictly_decreasing(); 155 | let collection = vec![9, 7, 5, 3, 1, 1]; 156 | matcher.test(&collection).passed.should_be_true(); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/assertions/char/mod.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Range, RangeInclusive}; 2 | 3 | use crate::matchers::equal::be_equal_ignoring_case; 4 | use crate::matchers::range::{be_in_exclusive_range, be_in_inclusive_range}; 5 | use crate::matchers::{Should, ShouldNot}; 6 | 7 | /// RangeAssertion enables assertions about whether a character falls within a specified range. 8 | pub trait RangeAssertion { 9 | /// - Asserts that the character falls within the given inclusive range. 10 | /// - Returns a reference to self for fluent chaining. 11 | /// - Panics if the assertion fails. 12 | /// # Example 13 | /// ``` 14 | /// use clearcheck::assertions::char::RangeAssertion; 15 | /// 16 | /// let letter = 'd'; 17 | /// letter.should_be_in_inclusive_range('a'..='d'); 18 | /// ``` 19 | fn should_be_in_inclusive_range(&self, range: RangeInclusive) -> &Self; 20 | 21 | /// - Asserts that the character does not fall within the given inclusive range. 22 | /// - Returns a reference to self for fluent chaining. 23 | /// - Panics if the assertion fails. 24 | /// # Example 25 | /// ``` 26 | /// use clearcheck::assertions::char::RangeAssertion; 27 | /// 28 | /// let letter = 'd'; 29 | /// letter.should_not_be_in_inclusive_range('a'..='c'); 30 | /// ``` 31 | fn should_not_be_in_inclusive_range(&self, range: RangeInclusive) -> &Self; 32 | 33 | /// - Asserts that the character falls within the given exclusive range. 34 | /// - Returns a reference to self for fluent chaining. 35 | /// - Panics if the assertion fails. 36 | /// # Example 37 | /// ``` 38 | /// use clearcheck::assertions::char::RangeAssertion; 39 | /// 40 | /// let letter = 'd'; 41 | /// letter.should_be_in_exclusive_range('a'..'e'); 42 | /// ``` 43 | fn should_be_in_exclusive_range(&self, range: Range) -> &Self; 44 | 45 | /// - Asserts that the character does not fall within the given exclusive range. 46 | /// - Returns a reference to self for fluent chaining. 47 | /// - Panics if the assertion fails. 48 | /// # Example 49 | /// ``` 50 | /// use clearcheck::assertions::char::RangeAssertion; 51 | /// 52 | /// let letter = 'd'; 53 | /// letter.should_not_be_in_exclusive_range('a'..'d'); 54 | /// ``` 55 | fn should_not_be_in_exclusive_range(&self, range: Range) -> &Self; 56 | } 57 | 58 | /// IgnoreCaseEqualityAssertion enables assertions about whether a character equals other character, with case ignored. 59 | pub trait IgnoreCaseEqualityAssertion { 60 | /// - Asserts that the character equals other character, with case ignored. 61 | /// - Returns a reference to self for fluent chaining. 62 | /// - Panics if the assertion fails. 63 | /// # Example 64 | /// ``` 65 | /// use clearcheck::assertions::char::IgnoreCaseEqualityAssertion; 66 | /// let letter = 'd'; 67 | /// letter.should_be_equal_ignoring_case('D'); 68 | /// ``` 69 | fn should_be_equal_ignoring_case(&self, other: char) -> &Self; 70 | 71 | /// - Asserts that the character does not equal other character, with case ignored. 72 | /// - Returns a reference to self for fluent chaining. 73 | /// - Panics if the assertion fails. 74 | /// # Example 75 | /// ``` 76 | /// use clearcheck::assertions::char::IgnoreCaseEqualityAssertion; 77 | /// let letter = 'd'; 78 | /// letter.should_not_be_equal_ignoring_case('E'); 79 | /// ``` 80 | fn should_not_be_equal_ignoring_case(&self, other: char) -> &Self; 81 | } 82 | 83 | impl RangeAssertion for char { 84 | fn should_be_in_inclusive_range(&self, range: RangeInclusive) -> &Self { 85 | self.should(&be_in_inclusive_range(range)); 86 | self 87 | } 88 | 89 | fn should_not_be_in_inclusive_range(&self, range: RangeInclusive) -> &Self { 90 | self.should_not(&be_in_inclusive_range(range)); 91 | self 92 | } 93 | 94 | fn should_be_in_exclusive_range(&self, range: Range) -> &Self { 95 | self.should(&be_in_exclusive_range(range)); 96 | self 97 | } 98 | 99 | fn should_not_be_in_exclusive_range(&self, range: Range) -> &Self { 100 | self.should_not(&be_in_exclusive_range(range)); 101 | self 102 | } 103 | } 104 | 105 | impl IgnoreCaseEqualityAssertion for char { 106 | fn should_be_equal_ignoring_case(&self, other: char) -> &Self { 107 | self.should(&be_equal_ignoring_case(other)); 108 | self 109 | } 110 | 111 | fn should_not_be_equal_ignoring_case(&self, other: char) -> &Self { 112 | self.should_not(&be_equal_ignoring_case(other)); 113 | self 114 | } 115 | } 116 | 117 | #[cfg(test)] 118 | mod range_tests { 119 | use crate::assertions::char::RangeAssertion; 120 | 121 | #[test] 122 | fn should_be_in_the_inclusive_range() { 123 | let letter = 'd'; 124 | letter.should_be_in_inclusive_range('a'..='d'); 125 | } 126 | 127 | #[test] 128 | #[should_panic] 129 | fn should_be_in_the_inclusive_range_but_was_not() { 130 | let letter = 'd'; 131 | letter.should_be_in_inclusive_range('a'..='c'); 132 | } 133 | 134 | #[test] 135 | fn should_not_be_in_the_inclusive_range() { 136 | let letter = 'd'; 137 | letter.should_not_be_in_inclusive_range('a'..='c'); 138 | } 139 | 140 | #[test] 141 | #[should_panic] 142 | fn should_not_be_in_the_inclusive_range_but_was() { 143 | let letter = 'd'; 144 | letter.should_not_be_in_inclusive_range('a'..='d'); 145 | } 146 | 147 | #[test] 148 | fn should_be_in_the_exclusive_range() { 149 | let letter = 'd'; 150 | letter.should_be_in_exclusive_range('a'..'e'); 151 | } 152 | 153 | #[test] 154 | #[should_panic] 155 | fn should_be_in_the_exclusive_range_but_was_not() { 156 | let letter = 'd'; 157 | letter.should_be_in_exclusive_range('a'..'d'); 158 | } 159 | 160 | #[test] 161 | fn should_not_be_in_the_exclusive_range() { 162 | let letter = 'd'; 163 | letter.should_not_be_in_exclusive_range('a'..'d'); 164 | } 165 | 166 | #[test] 167 | #[should_panic] 168 | fn should_not_be_in_the_exclusive_range_but_was() { 169 | let letter = 'd'; 170 | letter.should_not_be_in_exclusive_range('a'..'e'); 171 | } 172 | } 173 | 174 | #[cfg(test)] 175 | mod equal_tests { 176 | use crate::assertions::char::IgnoreCaseEqualityAssertion; 177 | 178 | #[test] 179 | fn should_be_equal_ignoring_case() { 180 | let letter = 'd'; 181 | letter.should_be_equal_ignoring_case('D'); 182 | } 183 | 184 | #[test] 185 | #[should_panic] 186 | fn should_be_equal_ignoring_case_but_was_not() { 187 | let letter = 'd'; 188 | letter.should_be_equal_ignoring_case('E'); 189 | } 190 | 191 | #[test] 192 | fn should_not_be_equal_ignoring_case() { 193 | let letter = 'd'; 194 | letter.should_not_be_equal_ignoring_case('E'); 195 | } 196 | 197 | #[test] 198 | #[should_panic] 199 | fn should_not_be_equal_ignoring_case_but_was() { 200 | let letter = 'd'; 201 | letter.should_not_be_equal_ignoring_case('D'); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/assertions/map/size.rs: -------------------------------------------------------------------------------- 1 | //! SizeAssertion enables assertions about the size of the HashMap. 2 | //! 3 | //! It offers a fluent interface for chaining multiple assertions. 4 | //! 5 | //! # Example 6 | //! ```rust 7 | //! use std::collections::HashMap; 8 | //! use clearcheck::assertions::collection::size::SizeAssertion; 9 | //! 10 | //! let mut key_value = HashMap::new(); 11 | //! key_value.insert("rust", "assert"); 12 | //! 13 | //! key_value.should_have_at_least_size(1) 14 | //! .should_have_size_in_inclusive_range(1..=5); 15 | //! ``` 16 | //! 17 | //! Refer to the trait [SizeAssertion]. 18 | 19 | use std::collections::HashMap; 20 | use std::hash::Hash; 21 | use std::ops::{Range, RangeInclusive}; 22 | 23 | use crate::assertions::collection::size::SizeAssertion; 24 | use crate::matchers::{Should, ShouldNot}; 25 | use crate::matchers::map::length::{ 26 | have_atleast_same_length, have_atmost_same_length, have_same_length, 27 | }; 28 | use crate::matchers::range::{have_length_in_exclusive_range, have_length_in_inclusive_range}; 29 | 30 | impl SizeAssertion for HashMap 31 | where 32 | K: Hash + Eq 33 | { 34 | fn should_have_size(&self, size: usize) -> &Self { 35 | self.should(&have_same_length(size)); 36 | self 37 | } 38 | 39 | fn should_not_have_size(&self, size: usize) -> &Self { 40 | self.should_not(&have_same_length(size)); 41 | self 42 | } 43 | 44 | fn should_have_at_least_size(&self, size: usize) -> &Self { 45 | self.should(&have_atleast_same_length(size)); 46 | self 47 | } 48 | 49 | fn should_have_at_most_size(&self, size: usize) -> &Self { 50 | self.should(&have_atmost_same_length(size)); 51 | self 52 | } 53 | 54 | fn should_be_same_size_as(&self, other: &[U]) -> &Self { 55 | self.should_have_size(other.len()); 56 | self 57 | } 58 | 59 | fn should_have_size_in_inclusive_range(&self, range: RangeInclusive) -> &Self { 60 | self.len().should(&have_length_in_inclusive_range(range)); 61 | self 62 | } 63 | 64 | fn should_not_have_size_in_inclusive_range(&self, range: RangeInclusive) -> &Self { 65 | self.len() 66 | .should_not(&have_length_in_inclusive_range(range)); 67 | self 68 | } 69 | 70 | fn should_have_size_in_exclusive_range(&self, range: Range) -> &Self { 71 | self.len().should(&have_length_in_exclusive_range(range)); 72 | self 73 | } 74 | 75 | fn should_not_have_size_in_exclusive_range(&self, range: Range) -> &Self { 76 | self.len() 77 | .should_not(&have_length_in_exclusive_range(range)); 78 | self 79 | } 80 | } 81 | 82 | #[cfg(test)] 83 | mod tests { 84 | use std::collections::HashMap; 85 | 86 | use crate::assertions::collection::size::SizeAssertion; 87 | 88 | #[test] 89 | fn should_have_size_as_1() { 90 | let mut key_value = HashMap::new(); 91 | key_value.insert("rust", "assert"); 92 | key_value.should_have_size(1); 93 | } 94 | 95 | #[test] 96 | #[should_panic] 97 | fn should_have_size_3_but_was_not() { 98 | let mut key_value = HashMap::new(); 99 | key_value.insert("rust", "assert"); 100 | key_value.should_have_size(3); 101 | } 102 | 103 | #[test] 104 | fn should_not_have_size_3() { 105 | let mut key_value = HashMap::new(); 106 | key_value.insert("rust", "assert"); 107 | key_value.should_not_have_size(3); 108 | } 109 | 110 | #[test] 111 | #[should_panic] 112 | fn should_not_have_size_1_but_was() { 113 | let mut key_value = HashMap::new(); 114 | key_value.insert("rust", "assert"); 115 | key_value.should_not_have_size(1); 116 | } 117 | 118 | #[test] 119 | fn should_have_at_least_size_1() { 120 | let mut key_value = HashMap::new(); 121 | key_value.insert("rust", "assert"); 122 | key_value.should_have_at_least_size(1); 123 | } 124 | 125 | #[test] 126 | #[should_panic] 127 | fn should_have_at_least_size_2_but_was_not() { 128 | let mut key_value = HashMap::new(); 129 | key_value.insert("rust", "assert"); 130 | key_value.should_have_at_least_size(2); 131 | } 132 | 133 | #[test] 134 | fn should_have_at_most_size_1() { 135 | let mut key_value = HashMap::new(); 136 | key_value.insert("rust", "assert"); 137 | key_value.should_have_at_most_size(1); 138 | } 139 | 140 | #[test] 141 | #[should_panic] 142 | fn should_have_at_most_size_1_but_was_not() { 143 | let mut key_value = HashMap::new(); 144 | key_value.insert("rust", "assert"); 145 | key_value.insert("java", "assert4j"); 146 | key_value.should_have_at_most_size(1); 147 | } 148 | 149 | #[test] 150 | fn should_be_same_size_as_other() { 151 | let mut key_value = HashMap::new(); 152 | key_value.insert("rust", "assert"); 153 | key_value.should_be_same_size_as(&[1]); 154 | } 155 | 156 | #[test] 157 | #[should_panic] 158 | fn should_be_same_size_as_other_but_was_not() { 159 | let mut key_value = HashMap::new(); 160 | key_value.insert("rust", "assert"); 161 | key_value.should_be_same_size_as(&[1, 2, 3]); 162 | } 163 | 164 | #[test] 165 | fn should_have_size_in_the_inclusive_range() { 166 | let mut key_value = HashMap::new(); 167 | key_value.insert("rust", "assert"); 168 | key_value.should_have_size_in_inclusive_range(1..=8); 169 | } 170 | 171 | #[test] 172 | #[should_panic] 173 | fn should_have_size_in_the_inclusive_range_but_was_not() { 174 | let mut key_value = HashMap::new(); 175 | key_value.insert("rust", "assert"); 176 | key_value.should_have_size_in_inclusive_range(3..=4); 177 | } 178 | 179 | #[test] 180 | fn should_not_have_size_in_the_inclusive_range() { 181 | let mut key_value = HashMap::new(); 182 | key_value.insert("rust", "assert"); 183 | key_value.should_not_have_size_in_inclusive_range(3..=4); 184 | } 185 | 186 | #[test] 187 | #[should_panic] 188 | fn should_not_have_size_in_the_inclusive_range_but_was() { 189 | let mut key_value = HashMap::new(); 190 | key_value.insert("rust", "assert"); 191 | key_value.should_not_have_size_in_inclusive_range(1..=2); 192 | } 193 | 194 | #[test] 195 | fn should_have_size_in_the_exclusive_range() { 196 | let mut key_value = HashMap::new(); 197 | key_value.insert("rust", "assert"); 198 | key_value.should_have_size_in_exclusive_range(1..3); 199 | } 200 | 201 | #[test] 202 | #[should_panic] 203 | fn should_have_size_in_the_range_but_was_not() { 204 | let mut key_value = HashMap::new(); 205 | key_value.insert("rust", "assert"); 206 | key_value.should_have_size_in_exclusive_range(3..8); 207 | } 208 | 209 | #[test] 210 | fn should_not_have_size_in_the_exclusive_range() { 211 | let mut key_value = HashMap::new(); 212 | key_value.insert("rust", "assert"); 213 | key_value.should_not_have_size_in_exclusive_range(3..4); 214 | } 215 | 216 | #[test] 217 | #[should_panic] 218 | fn should_not_have_size_in_the_exclusive_range_but_was() { 219 | let mut key_value = HashMap::new(); 220 | key_value.insert("rust", "assert"); 221 | key_value.should_not_have_size_in_exclusive_range(1..9); 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/assertions/collection/predicate.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use crate::matchers::{Should, ShouldNot}; 4 | use crate::matchers::collection::predicate::{satisfy_for_all, satisfy_for_any}; 5 | 6 | /// PredicateAssertion enables assertions about whether the elements in a collection satisfy the given predicate. 7 | pub trait PredicateAssertion 8 | where 9 | T: Eq 10 | { 11 | /// - Asserts that all the elements in the collection satisfy the given predicate. 12 | /// - Returns a reference to self for fluent chaining. 13 | /// - Panics if the assertion fails. 14 | /// # Example 15 | /// ``` 16 | /// use clearcheck::assertions::collection::predicate::PredicateAssertion; 17 | /// 18 | /// let collection = vec!["clearcheck-1", "junit-2", "assert-3"]; 19 | /// collection.should_satisfy_for_all(|element| element.chars().any(|ch| ch.is_numeric())); 20 | /// ``` 21 | fn should_satisfy_for_all(&self, predicate: F) -> &Self 22 | where 23 | F: Fn(&T) -> bool; 24 | 25 | /// - Asserts that not all the elements in the collection satisfy the given predicate. 26 | /// - Returns a reference to self for fluent chaining. 27 | /// - Panics if the assertion fails. 28 | /// # Example 29 | /// ``` 30 | /// use clearcheck::assertions::collection::predicate::PredicateAssertion; 31 | /// 32 | /// let collection = vec!["clearcheck", "junit-2", "assert-3"]; 33 | /// collection.should_not_satisfy_for_all(|element| element.chars().any(|ch| ch.is_numeric())); 34 | /// ``` 35 | fn should_not_satisfy_for_all(&self, predicate: F) -> &Self 36 | where 37 | F: Fn(&T) -> bool; 38 | 39 | /// - Asserts that any of the elements in the collection satisfy the given predicate. 40 | /// - Returns a reference to self for fluent chaining. 41 | /// - Panics if the assertion fails. 42 | /// # Example 43 | /// ``` 44 | /// use clearcheck::assertions::collection::predicate::PredicateAssertion; 45 | /// 46 | /// let collection = vec!["clearcheck", "junit-1", "assert"]; 47 | /// collection.should_satisfy_for_any(|element| element.chars().any(|ch| ch.is_numeric())); 48 | /// ``` 49 | fn should_satisfy_for_any(&self, predicate: F) -> &Self 50 | where 51 | F: Fn(&T) -> bool; 52 | 53 | /// - Asserts that none of the elements in the collection satisfy the given predicate. 54 | /// - Returns a reference to self for fluent chaining. 55 | /// - Panics if the assertion fails. 56 | /// # Example 57 | /// ``` 58 | /// use clearcheck::assertions::collection::predicate::PredicateAssertion; 59 | /// 60 | /// let collection = vec!["clearcheck", "junit", "assert"]; 61 | /// collection.should_not_satisfy_for_any(|element| element.chars().any(|ch| ch.is_numeric())); 62 | /// ``` 63 | fn should_not_satisfy_for_any(&self, predicate: F) -> &Self 64 | where 65 | F: Fn(&T) -> bool; 66 | } 67 | 68 | impl PredicateAssertion for Vec 69 | where 70 | T: Debug, 71 | T: Eq, 72 | { 73 | fn should_satisfy_for_all(&self, predicate: F) -> &Self where F: Fn(&T) -> bool { 74 | (self as &[T]).should_satisfy_for_all(predicate); 75 | self 76 | } 77 | 78 | fn should_not_satisfy_for_all(&self, predicate: F) -> &Self where F: Fn(&T) -> bool { 79 | (self as &[T]).should_not_satisfy_for_all(predicate); 80 | self 81 | } 82 | 83 | fn should_satisfy_for_any(&self, predicate: F) -> &Self where F: Fn(&T) -> bool { 84 | (self as &[T]).should_satisfy_for_any(predicate); 85 | self 86 | } 87 | 88 | fn should_not_satisfy_for_any(&self, predicate: F) -> &Self where F: Fn(&T) -> bool { 89 | (self as &[T]).should_not_satisfy_for_any(predicate); 90 | self 91 | } 92 | } 93 | 94 | impl PredicateAssertion for [T; N] 95 | where 96 | T: Debug, 97 | T: Eq, 98 | { 99 | fn should_satisfy_for_all(&self, predicate: F) -> &Self where F: Fn(&T) -> bool { 100 | (self as &[T]).should_satisfy_for_all(predicate); 101 | self 102 | } 103 | 104 | fn should_not_satisfy_for_all(&self, predicate: F) -> &Self where F: Fn(&T) -> bool { 105 | (self as &[T]).should_not_satisfy_for_all(predicate); 106 | self 107 | } 108 | 109 | fn should_satisfy_for_any(&self, predicate: F) -> &Self where F: Fn(&T) -> bool { 110 | (self as &[T]).should_satisfy_for_any(predicate); 111 | self 112 | } 113 | 114 | fn should_not_satisfy_for_any(&self, predicate: F) -> &Self where F: Fn(&T) -> bool { 115 | (self as &[T]).should_not_satisfy_for_any(predicate); 116 | self 117 | } 118 | } 119 | 120 | impl PredicateAssertion for [T] 121 | where 122 | T: Debug, 123 | T: Eq, 124 | { 125 | fn should_satisfy_for_all(&self, predicate: F) -> &Self where F: Fn(&T) -> bool { 126 | self.should(&satisfy_for_all(predicate)); 127 | self 128 | } 129 | 130 | fn should_not_satisfy_for_all(&self, predicate: F) -> &Self where F: Fn(&T) -> bool { 131 | self.should_not(&satisfy_for_all(predicate)); 132 | self 133 | } 134 | 135 | fn should_satisfy_for_any(&self, predicate: F) -> &Self where F: Fn(&T) -> bool { 136 | self.should(&satisfy_for_any(predicate)); 137 | self 138 | } 139 | 140 | fn should_not_satisfy_for_any(&self, predicate: F) -> &Self where F: Fn(&T) -> bool { 141 | self.should_not(&satisfy_for_any(predicate)); 142 | self 143 | } 144 | } 145 | 146 | #[cfg(test)] 147 | mod tests { 148 | use crate::assertions::collection::predicate::PredicateAssertion; 149 | 150 | #[test] 151 | fn should_satisfy_for_all_a_character_must_be_numeric() { 152 | let collection = vec!["clearcheck-1", "junit-2", "assert-3"]; 153 | collection.should_satisfy_for_all(|element| element.chars().any(|ch| ch.is_numeric())); 154 | } 155 | 156 | #[test] 157 | #[should_panic] 158 | fn should_satisfy_for_all_a_character_must_be_numeric_but_it_did_not() { 159 | let collection = vec!["clearcheck", "junit-2", "assert-3"]; 160 | collection.should_satisfy_for_all(|element| element.chars().any(|ch| ch.is_numeric())); 161 | } 162 | 163 | #[test] 164 | fn should_not_satisfy_for_all_a_character_must_be_numeric() { 165 | let collection = vec!["clearcheck", "junit-2", "assert-3"]; 166 | collection.should_not_satisfy_for_all(|element| element.chars().any(|ch| ch.is_numeric())); 167 | } 168 | 169 | #[test] 170 | #[should_panic] 171 | fn should_not_satisfy_for_all_a_character_must_be_numeric_but_it_did() { 172 | let collection = vec!["clearcheck-1", "junit-2", "assert-3"]; 173 | collection.should_not_satisfy_for_all(|element| element.chars().any(|ch| ch.is_numeric())); 174 | } 175 | 176 | #[test] 177 | fn should_satisfy_for_any_a_character_must_be_numeric() { 178 | let collection = vec!["clearcheck-1", "junit", "assert-3"]; 179 | collection.should_satisfy_for_any(|element| element.chars().any(|ch| ch.is_numeric())); 180 | } 181 | 182 | #[test] 183 | #[should_panic] 184 | fn should_satisfy_for_any_a_character_must_be_numeric_but_it_did_not() { 185 | let collection = vec!["clearcheck", "junit", "assert"]; 186 | collection.should_satisfy_for_any(|element| element.chars().any(|ch| ch.is_numeric())); 187 | } 188 | 189 | #[test] 190 | fn should_not_satisfy_for_any_a_character_must_be_numeric() { 191 | let collection = vec!["clearcheck", "junit", "assert"]; 192 | collection.should_not_satisfy_for_any(|element| element.chars().any(|ch| ch.is_numeric())); 193 | } 194 | 195 | #[test] 196 | #[should_panic] 197 | fn should_not_satisfy_for_any_a_character_must_be_numeric_but_it_did() { 198 | let collection = vec!["clearcheck", "junit-2", "assert"]; 199 | collection.should_not_satisfy_for_any(|element| element.chars().any(|ch| ch.is_numeric())); 200 | } 201 | } -------------------------------------------------------------------------------- /src/matchers/collection/min_max.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::ops::{Range, RangeInclusive}; 3 | 4 | use crate::matchers::{Matcher, MatcherResult}; 5 | 6 | /// MembershipMatcher offers a flexible way for verifying the minimum and maximum values within a collection. 7 | /// 8 | /// clearcheck implements MembershipMatcher for collection types including vector, arrays and reference to slices. 9 | /// 10 | /// # Example 11 | ///``` 12 | /// use clearcheck::matchers::collection::min_max::have_min_in_inclusive_range; 13 | /// use clearcheck::matchers::Matcher; 14 | /// 15 | /// let collection = vec!["assert", "clearcheck", "junit"]; 16 | /// let matcher = have_min_in_inclusive_range("assert"..="junit"); 17 | /// 18 | /// assert!(matcher.test(&collection).passed()); 19 | /// ``` 20 | pub enum MinMaxMatcher { 21 | Min(T), 22 | Max(T), 23 | MinInInclusiveRange(RangeInclusive), 24 | MinInExclusiveRange(Range), 25 | MaxInInclusiveRange(RangeInclusive), 26 | MaxInExclusiveRange(Range), 27 | } 28 | 29 | impl MinMaxMatcher { 30 | fn test(&self, collection: &[T]) -> MatcherResult { 31 | match self { 32 | MinMaxMatcher::Min(min) => MatcherResult::formatted( 33 | collection.iter().min() == Some(min), 34 | format!("{:?} should have {:?} as the minimum element", collection, min), 35 | format!("{:?} should not have {:?} as the minimum element", collection, min), 36 | ), 37 | MinMaxMatcher::Max(max) => MatcherResult::formatted( 38 | collection.iter().max() == Some(max), 39 | format!("{:?} should have {:?} as the maximum element", collection, max), 40 | format!("{:?} should not have {:?} as the maximum element", collection, max), 41 | ), 42 | MinMaxMatcher::MinInInclusiveRange(range) => MatcherResult::formatted( 43 | collection.iter().min().is_some_and(|min| range.contains(min)), 44 | format!("{:?} should have minimum in the range {:?}", collection, range), 45 | format!("{:?} should not have minimum in the range {:?}", collection, range), 46 | ), 47 | MinMaxMatcher::MinInExclusiveRange(range) => MatcherResult::formatted( 48 | collection.iter().min().is_some_and(|min| range.contains(min)), 49 | format!("{:?} should have minimum in the range {:?}", collection, range), 50 | format!("{:?} should not have minimum in the range {:?}", collection, range), 51 | ), 52 | MinMaxMatcher::MaxInInclusiveRange(range) => MatcherResult::formatted( 53 | collection.iter().max().is_some_and(|max| range.contains(max)), 54 | format!("{:?} should have maximum in the range {:?}", collection, range), 55 | format!("{:?} should not have maximum in the range {:?}", collection, range), 56 | ), 57 | MinMaxMatcher::MaxInExclusiveRange(range) => MatcherResult::formatted( 58 | collection.iter().max().is_some_and(|max| range.contains(max)), 59 | format!("{:?} should have maximum in the range {:?}", collection, range), 60 | format!("{:?} should not have maximum in the range {:?}", collection, range), 61 | ), 62 | } 63 | } 64 | } 65 | 66 | impl Matcher> for MinMaxMatcher { 67 | fn test(&self, collection: &Vec) -> MatcherResult { 68 | self.test(collection) 69 | } 70 | } 71 | 72 | impl Matcher<[T; N]> for MinMaxMatcher { 73 | fn test(&self, collection: &[T; N]) -> MatcherResult { 74 | self.test(collection as &[T]) 75 | } 76 | } 77 | 78 | impl Matcher<&[T]> for MinMaxMatcher { 79 | fn test(&self, collection: &&[T]) -> MatcherResult { 80 | self.test(collection) 81 | } 82 | } 83 | 84 | /// Creates a MinMaxMatcher that asserts whether the minimum value in the underlying collection equals the given minimum value. 85 | pub fn have_min(min: T) -> MinMaxMatcher { 86 | MinMaxMatcher::Min(min) 87 | } 88 | 89 | /// Creates a MinMaxMatcher that asserts whether the maximum value in the underlying collection equals the given maximum value. 90 | pub fn have_max(max: T) -> MinMaxMatcher { 91 | MinMaxMatcher::Max(max) 92 | } 93 | 94 | /// Creates a MinMaxMatcher that asserts whether the minimum value in the underlying collection falls within the given inclusive range. 95 | pub fn have_min_in_inclusive_range(range: RangeInclusive) -> MinMaxMatcher { 96 | MinMaxMatcher::MinInInclusiveRange(range) 97 | } 98 | 99 | /// Creates a MinMaxMatcher that asserts whether the minimum value in the underlying collection falls within the given exclusive range. 100 | pub fn have_min_in_exclusive_range(range: Range) -> MinMaxMatcher { 101 | MinMaxMatcher::MinInExclusiveRange(range) 102 | } 103 | 104 | /// Creates a MinMaxMatcher that asserts whether the maximum value in the underlying collection falls within the given inclusive range. 105 | pub fn have_max_in_inclusive_range(range: RangeInclusive) -> MinMaxMatcher { 106 | MinMaxMatcher::MaxInInclusiveRange(range) 107 | } 108 | 109 | /// Creates a MinMaxMatcher that asserts whether the maximum value in the underlying collection falls within the given exclusive range. 110 | pub fn have_max_in_exclusive_range(range: Range) -> MinMaxMatcher { 111 | MinMaxMatcher::MaxInExclusiveRange(range) 112 | } 113 | 114 | #[cfg(test)] 115 | mod tests { 116 | use crate::assertions::bool::TrueFalseAssertion; 117 | use crate::matchers::collection::min_max::{have_max, have_min}; 118 | 119 | #[test] 120 | fn should_a_min_element() { 121 | let collection = vec!["assert", "clearcheck", "junit"]; 122 | let matcher = have_min("assert"); 123 | 124 | matcher.test(&collection).passed.should_be_true(); 125 | } 126 | 127 | #[test] 128 | #[should_panic] 129 | fn should_have_the_given_min_element_but_was_not() { 130 | let collection = vec!["assert", "clearcheck", "junit"]; 131 | let matcher = have_min("junit"); 132 | 133 | matcher.test(&collection).passed.should_be_true(); 134 | } 135 | 136 | #[test] 137 | fn should_a_max_element() { 138 | let collection = vec!["assert", "clearcheck", "junit"]; 139 | let matcher = have_max("junit"); 140 | 141 | matcher.test(&collection).passed.should_be_true(); 142 | } 143 | 144 | #[test] 145 | #[should_panic] 146 | fn should_have_the_given_max_element_but_was_not() { 147 | let collection = vec!["assert", "clearcheck", "junit"]; 148 | let matcher = have_max("clearcheck"); 149 | 150 | matcher.test(&collection).passed.should_be_true(); 151 | } 152 | } 153 | 154 | #[cfg(test)] 155 | mod range_tests { 156 | use crate::assertions::bool::TrueFalseAssertion; 157 | use crate::matchers::collection::min_max::{have_max_in_exclusive_range, have_max_in_inclusive_range, have_min_in_exclusive_range, have_min_in_inclusive_range}; 158 | 159 | #[test] 160 | fn should_a_min_in_inclusive_range() { 161 | let collection = vec!["assert", "clearcheck", "junit"]; 162 | let matcher = have_min_in_inclusive_range("assert"..="junit"); 163 | 164 | matcher.test(&collection).passed.should_be_true(); 165 | } 166 | 167 | #[test] 168 | fn should_a_min_in_exclusive_range() { 169 | let collection = vec!["assert", "clearcheck", "junit"]; 170 | let matcher = have_min_in_exclusive_range("assert".."junit"); 171 | 172 | matcher.test(&collection).passed.should_be_true(); 173 | } 174 | 175 | #[test] 176 | fn should_a_max_in_inclusive_range() { 177 | let collection = vec!["assert", "clearcheck", "junit"]; 178 | let matcher = have_max_in_inclusive_range("assert"..="junit"); 179 | 180 | matcher.test(&collection).passed.should_be_true(); 181 | } 182 | 183 | #[test] 184 | fn should_a_max_in_exclusive_range() { 185 | let collection = vec!["assert", "clearcheck", "junit"]; 186 | let matcher = have_max_in_exclusive_range("assert".."testify"); 187 | 188 | matcher.test(&collection).passed.should_be_true(); 189 | } 190 | } --------------------------------------------------------------------------------