Skip to content

Commit

Permalink
chore: Update documentation and tests for async functions in traits
Browse files Browse the repository at this point in the history
  • Loading branch information
audunhalland committed Jan 18, 2024
1 parent f43111b commit 9a4b967
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 52 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ spin = { version = "0.9.8", optional = true }
async-trait = "0.1"
critical-section = { version = "1.1.2", features = ["std"] }
tokio = { version = "1", features = ["full"] }
rustversion = "1"

[lib]
# do not run doctest by default with `cargo hack`. They are tested with a separate `cargo test --doc` run.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,8 @@ These mock APIs can be found in [mock].
* Methods returning data borrowed from non-self arguments (these have to be converted to static reference, e.g. via [`Box::leak`](Box::leak)).
* Methods returning a type containing lifetime parameters. For a mocked return they will have to be `'static`.
* Generic methods using either explicit generic params or argument-position `impl Trait`.
* Async methods when the trait is annotated with `#[async_trait]`.
* Methods that return a future that is an associated type. Requires nightly.
* Methods that are `async` or return `impl Future`.
* `async_trait`-annotated traits.

#### What kinds of traits or methods cannot be mocked?
* Static methods, i.e. no `self` receiver. Static methods with a _default body_ are accepted though, but not mockable.
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,8 @@
//! * Methods returning data borrowed from non-self arguments (these have to be converted to static reference, e.g. via [`Box::leak`](Box::leak)).
//! * Methods returning a type containing lifetime parameters. For a mocked return they will have to be `'static`.
//! * Generic methods using either explicit generic params or argument-position `impl Trait`.
//! * Async methods when the trait is annotated with `#[async_trait]`.
//! * Methods that return a future that is an associated type. Requires nightly.
//! * Methods that are `async` or return `impl Future`.
//! * `async_trait`-annotated traits.
//!
//! #### What kinds of traits or methods cannot be mocked?
//! * Static methods, i.e. no `self` receiver. Static methods with a _default body_ are accepted though, but not mockable.
Expand Down
50 changes: 50 additions & 0 deletions tests/it/async_fn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#[rustversion::since(1.75)]
mod r#async {
use unimock::*;

#[unimock(api = TraitMock)]
trait Trait {
async fn a(&self, arg: i32) -> i32;
async fn b(&self) -> &i32;
async fn c(&self) -> Option<&i32>;
}

#[tokio::test]
async fn test_it() {
let deps = Unimock::new((
TraitMock::a.next_call(matching!(_)).returns(42),
TraitMock::b.next_call(matching!()).returns(42),
TraitMock::c.next_call(matching!()).returns(Some(42)),
));

assert_eq!(42, deps.a(5).await);
assert_eq!(&42, deps.b().await);
assert_eq!(Some(&42), deps.c().await);
}
}

#[rustversion::since(1.75)]
mod rpit_future {
use std::future::Future;

use unimock::*;

#[unimock(api = RpitFutureMock)]
trait RpitFuture {
fn m1(&self) -> impl Future<Output = i32>;
fn m2(&self, arg: i32) -> impl Send + Future<Output = i32>;
fn m3(&self) -> impl Future<Output = i32> + Send;
fn m4(&self) -> impl core::future::Future<Output = i32> + Send;
}

#[tokio::test]
async fn rpit() {
let u = Unimock::new((
RpitFutureMock::m1.next_call(matching!()).returns(1337),
RpitFutureMock::m2.next_call(matching!(42)).returns(1338),
));

assert_eq!(u.m1().await, 1337);
assert_eq!(u.m2(42).await, 1338);
}
}
46 changes: 44 additions & 2 deletions tests/it/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -706,10 +706,44 @@ fn borrow_static_should_work_with_returns_static() {
);
}

#[cfg(feature = "std")]
#[rustversion::since(1.75)]
mod async_argument_borrowing {
use super::*;

#[unimock(api=BorrowParamMock)]
trait BorrowParam {
async fn borrow_param<'a>(&self, arg: &'a str) -> &'a str;
}

#[cfg(feature = "std")]
#[tokio::test]
async fn test_argument_borrowing() {
let unimock = Unimock::new(
BorrowParamMock::borrow_param
.each_call(matching!(_))
.returns("foobar"),
);

assert_eq!("foobar", unimock.borrow_param("input").await);
}

#[cfg(feature = "std")]
#[tokio::test]
async fn test_argument_borrowing_works() {
let unimock = Unimock::new(
BorrowParamMock::borrow_param
.each_call(matching!(_))
.returns("foobar"),
);

unimock.borrow_param("input").await;
}
}

#[cfg(feature = "std")]
mod async_trait_argument_borrowing {
use super::*;

#[unimock(api=BorrowParamMock)]
#[::async_trait::async_trait]
trait BorrowParam {
Expand Down Expand Up @@ -755,10 +789,18 @@ mod lifetime_constrained_output_type {
fn borrow_sync_explicit2<'a, 'b>(&'a self, arg: &'b str) -> Borrowing2<'a, 'b>;
}

#[rustversion::since(1.75)]
#[unimock]
trait BorrowAsync {
async fn borrow_async_elided(&self) -> Borrowing1<'_>;
async fn borrow_async_explicit<'a>(&'a self) -> Borrowing1<'a>;
async fn borrow_async_explicit2<'a, 'b>(&'a self, arg: &'b str) -> Borrowing2<'a, 'b>;
}

#[cfg(feature = "std")]
#[unimock]
#[::async_trait::async_trait]
trait BorrowAsync {
trait BorrowAsyncTrait {
async fn borrow_async_elided(&self) -> Borrowing1<'_>;
async fn borrow_async_explicit<'a>(&'a self) -> Borrowing1<'a>;
async fn borrow_async_explicit2<'a, 'b>(&'a self, arg: &'b str) -> Borrowing2<'a, 'b>;
Expand Down
29 changes: 29 additions & 0 deletions tests/it/default_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,35 @@ mod default_impl_arc {
}
}

#[rustversion::since(1.75)]
mod default_impl_async {
use unimock::*;

type TestResult<T> = Result<T, ()>;

// A point of these tests is that the method names
// in both traits are the same.
// This tests that the delegation is done with fully-qualified syntax.

#[unimock(api = AsyncTraitDefault1Mock)]
trait AsyncTraitDefault1 {
async fn normal_method(&self) -> TestResult<i32>;

async fn default_method(&self) -> TestResult<i32> {
Ok(42)
}
}

#[unimock(api = AsyncTraitDefault2Mock)]
trait AsyncTraitDefault2 {
async fn normal_method(&self) -> TestResult<i32>;

async fn default_method(&self) -> TestResult<i32> {
Ok(42)
}
}
}

#[cfg(feature = "std")]
mod default_impl_async_trait {
use unimock::*;
Expand Down
16 changes: 14 additions & 2 deletions tests/it/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,16 @@ mod combined {
}
}

#[cfg(feature = "std")]
mod async_generic {
use super::*;

#[rustversion::since(1.75)]
#[unimock]
trait AsyncGenericBounds<I: Debug, O: Clone> {
async fn generic_bounds(&self, param: I) -> O;
}

#[cfg(feature = "std")]
#[unimock]
#[async_trait::async_trait]
trait AsyncTraitGenericBounds<I: Debug, O: Clone> {
Expand Down Expand Up @@ -273,8 +279,14 @@ mod generic_combo {
U: 'static;
}

#[cfg(feature = "std")]
#[rustversion::since(1.75)]
#[unimock(api=MockAsyncCombo)]
trait AsyncGenerics<T: 'static + Send> {
async fn ret<U: 'static + Send>(&self, u: U, a: impl Any + Send + 'static) -> T;
}

#[cfg(feature = "std")]
#[unimock(api=MockAsyncTraitCombo)]
#[async_trait::async_trait]
trait AsyncTraitGenerics<T: 'static + Send> {
async fn ret<U: 'static + Send>(&self, u: U, a: impl Any + Send + 'static) -> T;
Expand Down
3 changes: 3 additions & 0 deletions tests/it/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ mod prefix;
#[cfg(all(feature = "pretty-print", any(feature = "std", feature = "spin-lock")))]
mod pretty_mismatches;

#[cfg(feature = "std")]
mod async_fn;

#[cfg(all(feature = "mock-core", feature = "mock-std"))]
mod std;

Expand Down
21 changes: 0 additions & 21 deletions tests/nightly/async_fn_in_trait.rs

This file was deleted.

1 change: 0 additions & 1 deletion tests/nightly/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#![allow(clippy::disallowed_names)]

mod associated_future;
mod async_fn_in_trait;
mod rpit_future;

fn main() {}
22 changes: 0 additions & 22 deletions tests/nightly/rpit_future.rs

This file was deleted.

0 comments on commit 9a4b967

Please sign in to comment.