Skip to content

Commit

Permalink
Update docs, remove unnecessary function
Browse files Browse the repository at this point in the history
  • Loading branch information
TehPers committed Aug 18, 2024
1 parent d8da339 commit b58517c
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 43 deletions.
75 changes: 74 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,80 @@ steps:
expected: "0.png"
```

Supports colored messages with the `colors` feature.
Supports:

- colored messages with the `colors` feature
- diffing with the `diff` feature
- async assertions with the `futures` feature
- regular expressions with the `regex` feature

### Output diffs

Check how outputs differ based on their `Display` representations where
available:

```text
---- string_diff stdout ----
thread 'string_diff' panicked at tests\examples.rs:8:5:
assertion failed:
at: tests\examples.rs:8:5 [examples]
subject: "The quick\nbrown fox\njumped over\nthe lazy\ndog."
steps:
to_equal: [1] values not equal
received: "The quick\nbrown fox\njumped over\nthe lazy\ndog."
expected: "the quick brown\nspotted fox\njumped over\nthe lazyish\ndog."
----- diff [1] -----
- the quick brown
+ The quick
- spotted fox
+ brown fox
jumped over
- the lazyish
+ the lazy
dog.
```

Or, for types that only implement `Debug`, use that representation automatically
instead:

```text
---- debug_diff stdout ----
thread 'debug_diff' panicked at tests\examples.rs:28:5:
assertion failed:
at: tests\examples.rs:28:5 [examples]
subject: [A { inner: 1 }, A { inner: 3 }, A { inner: 3 }, A { inner: 512 }, A { inner: 761 }]
steps:
to_equal: [1] values not equal
received: [A { inner: 1 }, A { inner: 3 }, A { inner: 3 }, A { inner: 512 }, A { inner: 761 }]
expected: [A { inner: 1 }, A { inner: 2 }, A { inner: 3 }, A { inner: 513 }, A { inner: 761 }]
----- diff [1] -----
[
A {
inner: 1,
},
A {
- inner: 2,
+ inner: 3,
},
A {
inner: 3,
},
A {
- inner: 513,
+ inner: 512,
},
A {
inner: 761,
},
]
```

For colored output, try running some of the example tests in the `tests/`
directory.

## Built-in assertions

Expand Down
42 changes: 22 additions & 20 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,46 +91,47 @@
/// annotated. Values passed into assertions (and from modifiers to other
/// assertions) are *transparently* annotated.
///
/// An annotated value is a value with an additional string representation
/// attached to it. This string representation is generated either from the
/// value's [`Debug`] representation or from the [stringified] source code
/// itself (if no [`Debug`] implementation is available).
/// An annotated value is a value with additional information about its
/// representations. The source code being annotated is [stringified], and
/// whether the type supports [`Debug`] and/or [`Display`] is also captured in a
/// way where those implementations can be used to format the value.
///
/// Above, it was noted that applying, for example, the [`not`] modifier to an
/// assertion `a` was *functionally* equivalent to calling `not(a)`. In
/// implementation, [`not`] does not actually receive the assertion `a`, but
/// instead receives a special annotated assertion which wraps `a`.
///
/// This annotated assertion is a hidden modifier that annotates the value that
/// it receives. This means that when calling `expect!(1, not, to_equal(2))`,
/// the value being sent from [`not`] to [`to_equal`] is automatically annotated
/// by this macro. Additionally, the `2` parameter to [`to_equal`] is
/// automatically annotated by this macro, so the [`to_equal`] function is
/// actually not receiving an [`i32`], but an annotated version of it.
/// This annotated assertion is created by a hidden modifier that annotates the
/// value that it receives. This means that when calling
/// `expect!(1, not, to_equal(2))`, the value being sent from [`not`] to
/// [`to_equal`] is automatically annotated by this macro. Additionally, the `2`
/// parameter to [`to_equal`] is automatically annotated by this macro, so the
/// [`to_equal`] function is actually not receiving an [`i32`], but an annotated
/// version of it.
///
/// In other words, if the hidden modifier's name is `annotate` and there
/// existed a constructor `Annotated(T)` to construct an annotated value, then
/// the assertion being called could be simplistically represented as
/// `annotate(not(annotate(to_equal(Annotated(2)))))`. Note that the parameter
/// to [`to_equal`] is also annotated, as would any parameters to any modifiers
/// in the chain (if there existed any which accepted parameters).
/// to [`to_equal`] is also annotated, as would be any parameters to any
/// modifiers in the chain (if there existed any which accepted parameters).
///
/// This macro must perform the annotation itself to avoid adding additional
/// bounds to assertions. This is because this macro performs autoref
/// specialization to extract the string representation of the value. Without
/// this, the [`to_equal`] assertion would need to have an additional [`Debug`]
/// constraint on the values that it receives to be able to display those values
/// in case of an assertion failure, meaning that assertion would not be as
/// useful for values that do not have a [`Debug`] representation.
/// in case of an assertion failure for example, meaning that assertion would
/// not be as useful for values that do not have a [`Debug`] representation.
///
/// One limitation of this approach is that values being passed from modifiers
/// to other assertions down the chain do not have a meaningful source
/// representation. If those values do not have a [`Debug`] implementation, then
/// the string representation of those values will not be meaningful. However,
/// assertions can see whether a meaningful string representation is available
/// before generating error messages, and this approach removes the burden on
/// assertions (and users) to constrain their inputs to values that can be
/// meaningfully represented as a string.
/// representation. If those values do not have a [`Debug`] or [`Display`]
/// implementation, then the string representation of those values will not be
/// meaningful. However, assertions can see whether a meaningful string
/// representation is available before generating error messages, and this
/// approach removes the burden on assertions (and users) to constrain their
/// inputs to values that can be meaningfully represented as a string.
///
/// Note that there will not always be a meaningful string representation of a
/// value. For values defined directly in source code (like `2` in the example
Expand All @@ -145,6 +146,7 @@
/// [`Annotated<T>`]: crate::metadata::Annotated
/// [`AnnotatedAssertion<A, T>`]: crate::assertions::AnnotatedAssertion
/// [`Debug`]: std::fmt::Debug
/// [`Display`]: std::fmt::Display
/// [`all`]: crate::prelude::IteratorAssertions::all
/// [`map`]: crate::prelude::GeneralAssertions::map
/// [`not`]: crate::prelude::GeneralAssertions::not
Expand Down
27 changes: 5 additions & 22 deletions src/metadata/annotated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,11 @@ macro_rules! annotated {
}};
}

/// A value annotated with its string representation.
/// A value annotated with information on how to represent it as a string.
///
/// This holds a string representation of the stored value. The string
/// representation is obtained in the following order of precedence:
///
/// 1. the [`Debug`] representation, otherwise...
/// 2. the [stringified](std::stringify) source code (that was annotated).
///
/// The stringified source code is always available as well, which can be
/// helpful for providing error messages that refer to the actual source code
/// of a value.
/// This holds a [stringified](std::stringify) representation of the source code
/// that was annotated as well as functions to extract [`Debug`] and [`Display`]
/// representations of the value, if the annotated value supports it.
///
/// One drawback is that if the annotated value was a variable, the source
/// representation is the name of that variable, which may provide limited
Expand All @@ -45,7 +39,7 @@ macro_rules! annotated {
/// generated by the [`expect!`](crate::expect!) macro (which annotates
/// intermediate values inside of closures). In this case, the only way to
/// generate a meaningful string representation of the value is for that value
/// to implement [`Debug`].
/// to implement [`Debug`] or [`Display`].
///
/// This type makes no guarantees about the string representation of the
/// contained value except for where the representation comes from. Two
Expand Down Expand Up @@ -114,17 +108,6 @@ impl<T> Annotated<T> {
pub fn as_display(&self) -> Option<&dyn Display> {
self.as_display.map(|f| f(&self.value))
}

/// Gets whether this value has a representation other than the stringified
/// source code representation.
///
/// The stringified source code is not always useful (since it can be an
/// intermediate variable name, for example), so sometimes it's helpful to
/// know if a known useful representation of this value exists.
#[inline]
pub fn has_non_stringified_repr(&self) -> bool {
self.as_debug.is_some() || self.as_display.is_some()
}
}

impl<T> Annotated<T>
Expand Down
29 changes: 29 additions & 0 deletions tests/examples.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use expecters::prelude::*;

#[test]
#[ignore = "run this test manually to see the output"]
fn string_diff() {
let left = "The quick\nbrown fox\njumped over\nthe lazy\ndog.";
let right = "the quick brown\nspotted fox\njumped over\nthe lazyish\ndog.";
expect!(left, to_equal(right));
}

#[test]
#[ignore = "run this test manually to see the output"]
fn debug_diff() {
#[derive(PartialEq, Eq, Debug)]
struct A {
inner: i32,
}

impl A {
pub fn new(inner: i32) -> Self {
Self { inner }
}
}

let subject = vec![A::new(1), A::new(3), A::new(3), A::new(512), A::new(761)];
let expected = vec![A::new(1), A::new(2), A::new(3), A::new(513), A::new(761)];

expect!(subject, to_equal(expected));
}

0 comments on commit b58517c

Please sign in to comment.