Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
File diff suppressed because one or more lines are too long
+6
View File
@@ -0,0 +1,6 @@
{
"git": {
"sha1": "8d80cea3896753c8674ad138ab620e8b17b923d4"
},
"path_in_vcs": "rstest"
}
+1086
View File
File diff suppressed because it is too large Load Diff
+102
View File
@@ -0,0 +1,102 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.70.0"
name = "rstest"
version = "0.25.0"
authors = ["Michele d'Amico <michele.damico@gmail.com>"]
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = """
Rust fixture based test framework. It use procedural macro
to implement fixtures and table based tests.
"""
homepage = "https://github.com/la10736/rstest"
readme = "README.md"
keywords = [
"test",
"fixture",
]
categories = ["development-tools::testing"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/la10736/rstest"
[features]
async-timeout = [
"dep:futures-timer",
"dep:futures-util",
"rstest_macros/async-timeout",
]
crate-name = ["rstest_macros/crate-name"]
default = [
"async-timeout",
"crate-name",
]
[lib]
name = "rstest"
path = "src/lib.rs"
[[test]]
name = "integration"
path = "tests/integration.rs"
[dependencies.futures-timer]
version = "3.0.3"
optional = true
[dependencies.futures-util]
version = "0.3.30"
optional = true
[dependencies.rstest_macros]
version = "0.25.0"
default-features = false
[dev-dependencies.actix-rt]
version = "2.9.0"
[dev-dependencies.async-std]
version = "1.13.0"
features = ["attributes"]
[dev-dependencies.lazy_static]
version = "1.5.0"
[dev-dependencies.mytest]
version = "0.24.0"
default-features = false
package = "rstest"
[dev-dependencies.pretty_assertions]
version = "1.4.1"
[dev-dependencies.temp_testdir]
version = "0.2.3"
[dev-dependencies.tokio]
version = "1.38.1"
features = [
"rt",
"macros",
]
[dev-dependencies.unindent]
version = "0.2.3"
[build-dependencies.rustc_version]
version = "0.4.1"
+47
View File
@@ -0,0 +1,47 @@
[package]
authors = ["Michele d'Amico <michele.damico@gmail.com>"]
categories = ["development-tools::testing"]
description = """
Rust fixture based test framework. It use procedural macro
to implement fixtures and table based tests.
"""
edition = "2021"
homepage = "https://github.com/la10736/rstest"
keywords = ["test", "fixture"]
license = "MIT OR Apache-2.0"
name = "rstest"
readme = "README.md"
repository = "https://github.com/la10736/rstest"
rust-version = "1.70.0"
version = "0.25.0"
[features]
async-timeout = [
"dep:futures-timer",
"dep:futures-util",
"rstest_macros/async-timeout",
]
crate-name = ["rstest_macros/crate-name"]
default = ["async-timeout", "crate-name"]
[lib]
[dependencies]
futures-timer = { version = "3.0.3", optional = true }
futures-util = { version = "0.3.30", optional = true }
rstest_macros = { version = "0.25.0", path = "../rstest_macros", default-features = false }
[dev-dependencies]
actix-rt = "2.9.0"
async-std = { version = "1.13.0", features = ["attributes"] }
lazy_static = "1.5.0"
mytest = { package = "rstest", version = "0.24.0", default-features = false }
pretty_assertions = "1.4.1"
rstest_reuse = { path = "../rstest_reuse" }
rstest_test = { path = "../rstest_test" }
temp_testdir = "0.2.3"
tokio = { version = "1.38.1", features = ["rt", "macros"] }
unindent = "0.2.3"
[build-dependencies]
rustc_version = "0.4.1"
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2018-19 Michele d'Amico
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+18
View File
@@ -0,0 +1,18 @@
Copyright 2018-19 Michele d'Amico
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+595
View File
@@ -0,0 +1,595 @@
[![Crate][crate-image]][crate-link]
[![Docs][docs-image]][docs-link]
[![Status][test-action-image]][test-action-link]
[![Apache 2.0 Licensed][license-apache-image]][license-apache-link]
[![MIT Licensed][license-mit-image]][license-mit-link]
# Fixture-based test framework for Rust
## Introduction
`rstest` uses procedural macros to help you on writing
fixtures and table-based tests. To use it, add the
following lines to your `Cargo.toml` file:
```toml
[dev-dependencies]
rstest = "0.25.0"
```
### Features
- `async-timeout`: `timeout` for `async` tests (Default enabled)
- `crate-name`: Import `rstest` package with different name (Default enabled)
### Fixture
The core idea is that you can inject your test dependencies
by passing them as test arguments. In the following example,
a `fixture` is defined and then used in two tests,
simply providing it as an argument:
```rust
use rstest::*;
#[fixture]
pub fn fixture() -> u32 { 42 }
#[rstest]
fn should_success(fixture: u32) {
    assert_eq!(fixture, 42);
}
#[rstest]
fn should_fail(fixture: u32) {
    assert_ne!(fixture, 42);
}
```
### Parametrize
You can also inject values in some other ways. For instance, you can
create a set of tests by simply providing the injected values for each
case: `rstest` will generate an independent test for each case.
```rust
use rstest::rstest;
#[rstest]
#[case(0, 0)]
#[case(1, 1)]
#[case(2, 1)]
#[case(3, 2)]
#[case(4, 3)]
fn fibonacci_test(#[case] input: u32, #[case] expected: u32) {
assert_eq!(expected, fibonacci(input))
}
```
Running `cargo test` in this case executes five tests:
```bash
running 5 tests
test fibonacci_test::case_1 ... ok
test fibonacci_test::case_2 ... ok
test fibonacci_test::case_3 ... ok
test fibonacci_test::case_4 ... ok
test fibonacci_test::case_5 ... ok
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
```
If you need to just providing a bunch of values for which you
need to run your test, you can use `#[values(list, of, values)]`
argument attribute:
```rust
use rstest::rstest;
#[rstest]
fn should_be_invalid(
#[values(None, Some(""), Some(" "))]
value: Option<&str>
) {
assert!(!valid(value))
}
```
Or create a _matrix_ test by using _list of values_ for some
variables that will generate the cartesian product of all the
values.
#### Use Parametrize definition in more tests
If you need to use a test list for more than one test you can use [`rstest_reuse`][reuse-crate-link]
crate. With this helper crate you can define a template and use it everywhere.
```rust
use rstest::rstest;
use rstest_reuse::{self, *};
#[template]
#[rstest]
#[case(2, 2)]
#[case(4/2, 2)]
fn two_simple_cases(#[case] a: u32, #[case] b: u32) {}
#[apply(two_simple_cases)]
fn it_works(#[case] a: u32, #[case] b: u32) {
assert!(a == b);
}
```
See [`rstest_reuse`][reuse-crate-link] for more details.
#### Feature flagged cases
In case you want certain test cases to only be present if a certain feature is
enabled, use `#[cfg_attr(feature = …, case(…))]`:
```rust
use rstest::rstest;
#[rstest]
#[case(2, 2)]
#[cfg_attr(feature = "frac", case(4/2, 2))]
fn it_works(#[case] a: u32, #[case] b: u32) {
assert!(a == b);
}
```
This also works with [`rstest_reuse`][reuse-crate-link].
### Magic Conversion
If you need a value where its type implement `FromStr()` trait you can use a literal
string to build it:
```rust
# use rstest::rstest;
# use std::net::SocketAddr;
#[rstest]
#[case("1.2.3.4:8080", 8080)]
#[case("127.0.0.1:9000", 9000)]
fn check_port(#[case] addr: SocketAddr, #[case] expected: u16) {
assert_eq!(expected, addr.port());
}
```
You can use this feature also in value list and in fixture default value.
### Async
`rstest` provides out of the box `async` support. Just mark your
test function as `async`, and it'll use `#[async-std::test]` to
annotate it. This feature can be really useful to build async
parametric tests using a tidy syntax:
```rust
use rstest::*;
#[rstest]
#[case(5, 2, 3)]
#[should_panic]
#[case(42, 40, 1)]
async fn my_async_test(#[case] expected: u32, #[case] a: u32, #[case] b: u32) {
assert_eq!(expected, async_sum(a, b).await);
}
```
Currently, only `async-std` is supported out of the box. But if you need to use
another runtime that provide its own test attribute (i.e. `tokio::test` or
`actix_rt::test`) you can use it in your `async` test like described in
[Inject Test Attribute](#inject-test-attribute).
To use this feature, you need to enable `attributes` in the `async-std`
features list in your `Cargo.toml`:
```toml
async-std = { version = "1.13", features = ["attributes"] }
```
If your test input is an async value (fixture or test parameter) you can use `#[future]`
attribute to remove `impl Future<Output = T>` boilerplate and just use `T`:
```rust
use rstest::*;
#[fixture]
async fn base() -> u32 { 42 }
#[rstest]
#[case(21, async { 2 })]
#[case(6, async { 7 })]
async fn my_async_test(#[future] base: u32, #[case] expected: u32, #[future]
#[case] div: u32) {
assert_eq!(expected, base.await / div.await);
}
```
As you noted you should `.await` all _future_ values and this sometimes can be really boring.
In this case you can use `#[future(awt)]` to _awaiting_ an input or annotating your function
with `#[awt]` attributes to globally `.await` all your _future_ inputs. Previous code can be
simplified like follow:
```rust
use rstest::*;
# #[fixture]
# async fn base() -> u32 { 42 }
#[rstest]
#[case(21, async { 2 })]
#[case(6, async { 7 })]
#[awt]
async fn global(#[future] base: u32, #[case] expected: u32, #[future]
#[case] div: u32) {
assert_eq!(expected, base / div);
}
#[rstest]
#[case(21, async { 2 })]
#[case(6, async { 7 })]
async fn single(#[future] base: u32, #[case] expected: u32, #[future(awt)]
#[case] div: u32) {
assert_eq!(expected, base.await / div);
}
```
### Files path as input arguments
If you need to create a test for each file in a given location you can use
`#[files("glob path syntax")]` attribute to generate a test for each file that
satisfy the given glob path.
```rust
#[rstest]
fn for_each_file(#[files("src/**/*.rs")]
#[exclude("test")] path: PathBuf) {
assert!(check_file(&path))
}
```
The default behavior is to ignore the files that start with `"."`, but you can
modify this by use `#[include_dot_files]` attribute. The `files` attribute can be
used more than once on the same variable, and you can also create some custom
exclusion rules with the `#[exclude("regex")]` attributes that filter out all
paths that verify the regular expression.
You can pass in environment variables by using `${ENV_VAR_NAME}` in the glob
path, e.g. `#[files("${SOME_ENV}/hello")]`. To set a default value for the
environment variable, use `${ENV_VAR_NAME:-default_value}`.
Files are resolved at compile time against your Cargo project root
(the `CARGO_MANIFEST_DIR` environment variable). If you need to change this
behavior, you can use the `#[base_dir = "..."]` attribute to specify a different
base directory. That directory MUST exist, and will be used as the root for
the files, as well as to resolve the relative path when creating the test name.
Similar to the `files` attribute, you can use `${ENV_VAR_NAME}` in the `base_dir`.
### Use `#[once]` Fixture
If you need to a fixture that should be initialized just once for all tests
you can use `#[once]` attribute. `rstest` call your fixture function just once and
return a reference to your function result to all your tests:
```rust
#[fixture]
#[once]
fn once_fixture() -> i32 { 42 }
#[rstest]
fn single(once_fixture: &i32) {
// All tests that use once_fixture will share the same reference to once_fixture()
// function result.
assert_eq!(&42, once_fixture)
}
```
### Test `#[timeout()]`
You can define an execution timeout for your tests with `#[timeout(<duration>)]` attribute. Timeout
works both for sync and async tests and is runtime agnostic. `#[timeout(<duration>)]` take an
expression that should return a `std::time::Duration`. Follow a simple async example:
```rust
use rstest::*;
use std::time::Duration;
async fn delayed_sum(a: u32, b: u32, delay: Duration) -> u32 {
async_std::task::sleep(delay).await;
a + b
}
#[rstest]
#[timeout(Duration::from_millis(80))]
async fn single_pass() {
assert_eq!(4, delayed_sum(2, 2, ms(10)).await);
}
```
In this case test pass because the delay is just 10 milliseconds and timeout is
80 milliseconds.
You can use `timeout` attribute like any other attribute in your tests, and you can
override a group timeout with a case specific one. In the follow example we have
3 tests where first and third use 100 milliseconds but the second one use 10 milliseconds.
Another valuable point in this example is to use an expression to compute the
duration.
```rust
fn ms(ms: u32) -> Duration {
Duration::from_millis(ms.into())
}
#[rstest]
#[case::pass(ms(1), 4)]
#[timeout(ms(10))]
#[case::fail_timeout(ms(60), 4)]
#[case::fail_value(ms(1), 5)]
#[timeout(ms(100))]
async fn group_one_timeout_override(#[case] delay: Duration, #[case] expected: u32) {
assert_eq!(expected, delayed_sum(2, 2, delay).await);
}
```
If you want to use `timeout` for `async` test you need to use `async-timeout`
feature (enabled by default).
### Default timeout
You can set a default timeout for test using the `RSTEST_TIMEOUT` environment variable.
The value is in seconds and is evaluated on test compile time.
### Inject Test Attribute
If you would like to use another `test` attribute for your test you can simply
indicate it in your test function's attributes. For instance if you want
to test some async function with use `actix_rt::test` attribute you can just write:
```rust
use rstest::*;
use actix_rt;
use std::future::Future;
#[rstest]
#[case(2, async { 4 })]
#[case(21, async { 42 })]
#[actix_rt::test]
async fn my_async_test(#[case] a: u32, #[case]
#[future] result: u32) {
assert_eq!(2 * a, result.await);
}
```
Just the attributes that ends with `test` (last path segment) can be injected.
## Test `Context` object
You can have a [`Context`] object for your test just by annotate an argument by `#[context]` attribute.
This object contains some useful information both to implement simple logics and debugging stuff.
```rust
use rstest::{rstest, Context};
#[rstest]
#[case::a_description(42)]
fn my_test(#[context] ctx: Context, #[case] _c: u32) {
assert_eq!("my_test", ctx.name);
assert_eq!(Some("a_description"), ctx.description);
assert_eq!(Some(0), ctx.case);
std::thread::sleep(std::time::Duration::from_millis(100));
assert!(ctx.start.elapsed() >= std::time::Duration::from_millis(100));
}
```
## Local lifetime and `#[by_ref]` attribute
In some cases you may want to use a local lifetime for some arguments of your test.
In these cases you can use the `#[by_ref]` attribute then use the reference instead
the value.
```rust
enum E<'a> {
A(bool),
B(&'a Cell<E<'a>>),
}
fn make_e_from_bool<'a>(_bump: &'a (), b: bool) -> E<'a> {
E::A(b)
}
#[fixture]
fn bump() -> () {}
#[rstest]
#[case(true, E::A(true))]
fn it_works<'a>(#[by_ref] bump: &'a (), #[case] b: bool, #[case] expected: E<'a>) {
let actual = make_e_from_bool(&bump, b);
assert_eq!(actual, expected);
}
```
You can use `#[by_ref]` attribute for all arguments of your test and not just for fixture
but also for cases, values and files.
## Complete Example
All these features can be used together with a mixture of fixture variables,
fixed cases and a bunch of values. For instance, you might need two
test cases which test for panics, one for a logged-in user and one for a guest user.
```rust
use rstest::*;
#[fixture]
fn repository() -> InMemoryRepository {
let mut r = InMemoryRepository::default();
// fill repository with some data
r
}
#[fixture]
fn alice() -> User {
User::logged("Alice", "2001-10-04", "London", "UK")
}
#[rstest]
#[case::authorized_user(alice())] // We can use `fixture` also as standard function
#[case::guest(User::Guest)] // We can give a name to every case : `guest` in this case
// and `authorized_user`
#[should_panic(expected = "Invalid query error")] // We would test a panic
fn should_be_invalid_query_error(
repository: impl Repository,
#[case] user: User,
#[values(" ", "^%$some#@invalid!chars", ".n.o.d.o.t.s.")] query: &str,
) {
repository.find_items(&user, query).unwrap();
}
```
This example will generate exactly 6 tests grouped by 2 different cases:
```text
running 6 tests
test should_be_invalid_query_error::case_1_authorized_user::query_1_____ - should panic ... ok
test should_be_invalid_query_error::case_2_guest::query_2_____someinvalid_chars__ - should panic ... ok
test should_be_invalid_query_error::case_1_authorized_user::query_2_____someinvalid_chars__ - should panic ... ok
test should_be_invalid_query_error::case_2_guest::query_3____n_o_d_o_t_s___ - should panic ... ok
test should_be_invalid_query_error::case_1_authorized_user::query_3____n_o_d_o_t_s___ - should panic ... ok
test should_be_invalid_query_error::case_2_guest::query_1_____ - should panic ... ok
test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
```
Note that the names of the values _try_ to convert the input expression in a
Rust valid identifier name to help you find which tests fail.
## More
Is that all? Not quite yet!
A fixture can be injected by another fixture, and they can be called
using just some of its arguments.
```rust
#[fixture]
fn user(#[default("Alice")] name: &str, #[default(22)] age: u8) -> User {
User::new(name, age)
}
#[rstest]
fn is_alice(user: User) {
assert_eq!(user.name(), "Alice")
}
#[rstest]
fn is_22(user: User) {
assert_eq!(user.age(), 22)
}
#[rstest]
fn is_bob(#[with("Bob")] user: User) {
assert_eq!(user.name(), "Bob")
}
#[rstest]
fn is_42(#[with("", 42)] user: User) {
assert_eq!(user.age(), 42)
}
```
As you noted you can provide default values without the need of a fixture
to define it.
Finally, if you need tracing the input values you can just
add the `trace` attribute to your test to enable the dump of all input
variables.
```rust
#[rstest]
#[case(42, "FortyTwo", ("minus twelve", -12))]
#[case(24, "TwentyFour", ("minus twentyfour", -24))]
#[trace] //This attribute enable tracing
fn should_fail(#[case] number: u32, #[case] name: &str, #[case] tuple: (&str, i32)) {
assert!(false); // <- stdout come out just for failed tests
}
```
```text
running 2 tests
test should_fail::case_1 ... FAILED
test should_fail::case_2 ... FAILED
failures:
---- should_fail::case_1 stdout ----
------------ TEST ARGUMENTS ------------
number = 42
name = "FortyTwo"
tuple = ("minus twelve", -12)
-------------- TEST START --------------
thread 'should_fail::case_1' panicked at 'assertion failed: false', src/main.rs:64:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
---- should_fail::case_2 stdout ----
------------ TEST ARGUMENTS ------------
number = 24
name = "TwentyFour"
tuple = ("minus twentyfour", -24)
-------------- TEST START --------------
thread 'should_fail::case_2' panicked at 'assertion failed: false', src/main.rs:64:5
failures:
should_fail::case_1
should_fail::case_2
test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out
```
In case one or more variables don't implement the `Debug` trait, an error
is raised, but it's also possible to exclude a variable using the
`#[notrace]` argument attribute.
You can learn more on [Docs][docs-link] and find more examples in
[`tests/resources`](/rstest/tests/resources) directory.
## Rust version compatibility
The minimum supported Rust version is 1.67.1.
## Changelog
See [CHANGELOG.md](/CHANGELOG.md)
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](/LICENSE-APACHE) or
[license-apache-link])
* MIT license [LICENSE-MIT](/LICENSE-MIT) or [license-MIT-link]
at your option.
[//]: # (links)
[crate-image]: https://img.shields.io/crates/v/rstest.svg
[crate-link]: https://crates.io/crates/rstest
[docs-image]: https://docs.rs/rstest/badge.svg
[docs-link]: https://docs.rs/rstest/
[test-action-image]: https://github.com/la10736/rstest/workflows/Test/badge.svg
[test-action-link]: https://github.com/la10736/rstest/actions?query=workflow:Test
[license-apache-image]: https://img.shields.io/badge/license-Apache2.0-blue.svg
[license-mit-image]: https://img.shields.io/badge/license-MIT-blue.svg
[license-apache-link]: http://www.apache.org/licenses/LICENSE-2.0
[license-MIT-link]: http://opensource.org/licenses/MIT
[reuse-crate-link]: https://crates.io/crates/rstest_reuse
+32
View File
@@ -0,0 +1,32 @@
/// A test context.
#[non_exhaustive]
pub struct Context {
/// The complete module test path
pub module: &'static str,
/// The test function name
pub name: &'static str,
/// The test description if present
pub description: Option<&'static str>,
/// The cardinal case number if it's a test case
pub case: Option<usize>,
/// Start time
pub start: std::time::Instant,
}
impl Context {
/// Create a new test context. This function set also the start time to the current time.
pub fn new(
module: &'static str,
name: &'static str,
description: Option<&'static str>,
case: Option<usize>,
) -> Self {
Self {
module,
name,
description,
case,
start: std::time::Instant::now(),
}
}
}
File diff suppressed because it is too large Load Diff
+104
View File
@@ -0,0 +1,104 @@
pub struct Magic<T>(pub std::marker::PhantomData<T>);
pub trait ViaParseDebug<'a, T> {
fn magic_conversion(&self, input: &'a str) -> T;
}
impl<'a, T> ViaParseDebug<'a, T> for &&Magic<T>
where
T: std::str::FromStr,
T::Err: std::fmt::Debug,
{
fn magic_conversion(&self, input: &'a str) -> T {
T::from_str(input).unwrap()
}
}
pub trait ViaParse<'a, T> {
fn magic_conversion(&self, input: &'a str) -> T;
}
impl<'a, T> ViaParse<'a, T> for &Magic<T>
where
T: std::str::FromStr,
{
fn magic_conversion(&self, input: &'a str) -> T {
match T::from_str(input) {
Ok(v) => v,
Err(_) => {
panic!(
"Cannot parse '{}' to get {}",
input,
std::any::type_name::<T>()
);
}
}
}
}
pub trait ViaIdent<'a, T> {
fn magic_conversion(&self, input: &'a str) -> T;
}
impl<'a> ViaIdent<'a, &'a str> for &&Magic<&'a str> {
fn magic_conversion(&self, input: &'a str) -> &'a str {
input
}
}
#[cfg(test)]
mod test {
use super::*;
use std::str::FromStr;
#[test]
fn should_return_the_same_slice_string() {
assert_eq!(
"something",
(&&&Magic::<&str>(std::marker::PhantomData)).magic_conversion("something")
);
}
#[test]
fn should_parse_via_parse_debug() {
assert_eq!(
42u32,
(&&&Magic::<u32>(std::marker::PhantomData)).magic_conversion("42")
);
}
#[test]
fn should_parse_via_parse_no_error_debug() {
struct S(String);
struct E;
impl FromStr for S {
type Err = E;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(S(s.to_owned()))
}
}
assert_eq!(
"some",
(&&&Magic::<S>(std::marker::PhantomData))
.magic_conversion("some")
.0
);
}
#[test]
#[should_panic(expected = "MyTypeName")]
fn should_show_error() {
struct MyTypeName;
struct E;
impl FromStr for MyTypeName {
type Err = E;
fn from_str(_s: &str) -> Result<Self, Self::Err> {
Err(E)
}
}
(&&&Magic::<MyTypeName>(std::marker::PhantomData)).magic_conversion("");
}
}
+199
View File
@@ -0,0 +1,199 @@
use std::{sync::mpsc, thread, time::Duration};
#[cfg(feature = "async-timeout")]
use futures_timer::Delay;
#[cfg(feature = "async-timeout")]
use futures_util::{select, Future, FutureExt};
pub fn execute_with_timeout_sync<T: 'static + Send, F: FnOnce() -> T + Send + 'static>(
code: F,
timeout: Duration,
) -> T {
let (sender, receiver) = mpsc::channel();
let thread = if let Some(name) = thread::current().name() {
thread::Builder::new().name(name.to_string())
} else {
thread::Builder::new()
};
let handle = thread.spawn(move || sender.send(code())).unwrap();
match receiver.recv_timeout(timeout) {
Ok(result) => {
// Unwraps are safe because we got a result from the thread, which is not a `SendError`,
// and there was no panic within the thread which caused a disconnect.
handle.join().unwrap().unwrap();
result
}
Err(mpsc::RecvTimeoutError::Timeout) => panic!("Timeout {:?} expired", timeout),
Err(mpsc::RecvTimeoutError::Disconnected) => match handle.join() {
Err(any) => std::panic::resume_unwind(any),
Ok(_) => unreachable!(),
},
}
}
#[cfg(feature = "async-timeout")]
pub async fn execute_with_timeout_async<T, Fut: Future<Output = T>, F: FnOnce() -> Fut>(
code: F,
timeout: Duration,
) -> T {
select! {
() = async {
Delay::new(timeout).await;
}.fuse() => panic!("Timeout {:?} expired", timeout),
out = code().fuse() => out,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "async-timeout")]
mod async_version {
use super::*;
use std::time::Duration;
async fn delayed_sum(a: u32, b: u32, delay: Duration) -> u32 {
async_std::task::sleep(delay).await;
a + b
}
async fn test(delay: Duration) {
let result = delayed_sum(2, 2, delay).await;
assert_eq!(result, 4);
}
mod use_async_std_runtime {
use super::*;
#[async_std::test]
#[should_panic]
async fn should_fail() {
execute_with_timeout_async(
|| test(Duration::from_millis(40)),
Duration::from_millis(10),
)
.await
}
#[async_std::test]
async fn should_pass() {
execute_with_timeout_async(
|| test(Duration::from_millis(10)),
Duration::from_millis(40),
)
.await
}
#[async_std::test]
#[should_panic = "inner message"]
async fn should_fail_for_panic_with_right_panic_message() {
execute_with_timeout_async(
|| async {
panic!("inner message");
},
Duration::from_millis(30),
)
.await
}
#[async_std::test]
async fn should_compile_also_with_no_copy_move() {
struct S {}
async fn test(_s: S) {
assert!(true);
}
let s = S {};
execute_with_timeout_async(move || test(s), Duration::from_millis(20)).await
}
}
mod use_tokio_runtime {
use super::*;
#[tokio::test]
#[should_panic]
async fn should_fail() {
execute_with_timeout_async(
|| test(Duration::from_millis(40)),
Duration::from_millis(10),
)
.await
}
#[async_std::test]
#[should_panic = "inner message"]
async fn should_fail_for_panic_with_right_panic_message() {
execute_with_timeout_async(
|| async {
panic!("inner message");
},
Duration::from_millis(30),
)
.await
}
#[tokio::test]
async fn should_pass() {
execute_with_timeout_async(
|| test(Duration::from_millis(10)),
Duration::from_millis(40),
)
.await
}
}
}
mod thread_version {
use super::*;
pub fn delayed_sum(a: u32, b: u32, delay: Duration) -> u32 {
std::thread::sleep(delay);
a + b
}
fn test(delay: Duration) {
let result = delayed_sum(2, 2, delay);
assert_eq!(result, 4);
}
#[test]
fn should_pass() {
execute_with_timeout_sync(
|| test(Duration::from_millis(30)),
Duration::from_millis(70),
)
}
#[test]
#[should_panic = "inner message"]
fn should_fail_for_panic_with_right_panic_message() {
execute_with_timeout_sync(
|| {
panic!("inner message");
},
Duration::from_millis(100),
)
}
#[test]
#[should_panic]
fn should_fail() {
execute_with_timeout_sync(
|| test(Duration::from_millis(70)),
Duration::from_millis(30),
)
}
#[test]
fn should_compile_also_with_no_copy_move() {
struct S {}
fn test(_s: S) {
assert!(true);
}
let s = S {};
execute_with_timeout_sync(move || test(s), Duration::from_millis(20))
}
}
}
+455
View File
@@ -0,0 +1,455 @@
use std::path::Path;
pub use unindent::Unindent;
use super::resources;
use mytest::*;
use rstest_test::{assert_in, assert_not_in, Project, Stringable, TestResults};
fn prj(res: &str) -> Project {
let path = Path::new("fixture").join(res);
crate::prj().set_code_file(resources(path))
}
fn run_test(res: &str) -> (std::process::Output, String) {
let prj = prj(res);
(
prj.run_tests().unwrap(),
prj.get_name().to_owned().to_string(),
)
}
mod should {
use rstest_test::{assert_regex, CountMessageOccurrence};
use super::*;
#[test]
fn use_input_fixtures() {
let (output, _) = run_test("simple_injection.rs");
TestResults::new().ok("success").fail("fail").assert(output);
}
#[test]
fn create_a_struct_that_return_the_fixture() {
let (output, _) = run_test("fixture_struct.rs");
TestResults::new()
.ok("resolve_new")
.ok("resolve_default")
.ok("injected_new")
.ok("injected_default")
.assert(output);
}
#[test]
fn be_accessible_from_other_module() {
let (output, _) = run_test("from_other_module.rs");
TestResults::new().ok("struct_access").assert(output);
}
#[test]
fn not_show_any_warning() {
let (output, _) = run_test("no_warning.rs");
assert_not_in!(output.stderr.str(), "warning:");
}
#[test]
fn rename() {
let (output, _) = run_test("rename.rs");
TestResults::new().ok("test").assert(output);
}
mod accept_and_return {
use super::*;
#[test]
fn impl_traits() {
let (output, _) = run_test("impl.rs");
TestResults::new()
.ok("base_impl_return")
.ok("nested_impl_return")
.ok("nested_multiple_impl_return")
.ok("base_impl_input")
.ok("nested_impl_input")
.ok("nested_multiple_impl_input")
.assert(output);
}
#[test]
fn dyn_traits() {
let (output, _) = run_test("dyn.rs");
TestResults::new()
.ok("test_dyn_box")
.ok("test_dyn_ref")
.ok("test_dyn_box_resolve")
.ok("test_dyn_ref_resolve")
.assert(output);
}
}
#[rstest]
#[case::base("async_fixture.rs")]
#[case::use_global("await_complete_fixture.rs")]
#[case::use_selective("await_partial_fixture.rs")]
fn resolve_async_fixture(#[case] code: &str) {
let prj = prj(code);
prj.add_dependency("async-std", r#"{version="*", features=["attributes"]}"#);
let output = prj.run_tests().unwrap();
TestResults::new()
.ok("default_is_async")
.ok("use_async_fixture")
.ok("use_async_impl_output")
.ok("use_async_nest_fixture_default")
.ok("use_async_nest_fixture_injected")
.ok("use_async_nest_fixture_with_default")
.ok("use_two_args_mix_fixture")
.ok("use_two_args_mix_fixture_inject_first")
.ok("use_two_args_mix_fixture_inject_both")
.assert(output);
}
#[test]
fn resolve_fixture_generics_by_fixture_input() {
let (output, _) = run_test("resolve.rs");
TestResults::new()
.ok("test_u32")
.ok("test_i32")
.assert(output);
}
#[test]
fn use_defined_return_type_if_any() {
let (output, _) = run_test("defined_return_type.rs");
TestResults::new()
.ok("resolve")
.ok("resolve_partial")
.ok("resolve_attrs")
.ok("resolve_partial_attrs")
.assert(output);
}
#[test]
fn clean_up_default_from_unused_generics() {
let (output, _) = run_test("clean_up_default_generics.rs");
TestResults::new()
.ok("resolve")
.ok("resolve_partial")
.assert(output);
}
#[test]
fn apply_partial_fixture() {
let (output, _) = run_test("partial.rs");
TestResults::new()
.ok("default")
.ok("t_partial_1")
.ok("t_partial_2")
.ok("t_complete")
.assert(output);
}
#[test]
fn apply_partial_fixture_from_value_attribute() {
let (output, _) = run_test("partial_in_attr.rs");
TestResults::new()
.ok("default")
.ok("t_partial_1")
.ok("t_partial_2")
.ok("t_complete")
.assert(output);
}
#[rstest]
#[case::compact_form("default.rs")]
#[case::attrs_form("default_in_attrs.rs")]
fn use_input_values_if_any(#[case] file: &str) {
let (output, _) = run_test(file);
TestResults::new()
.ok("test_simple")
.ok("test_simple_changed")
.ok("test_double")
.ok("test_double_changed")
.ok("test_mixed")
.assert(output);
}
#[test]
fn convert_literal_string_for_default_values() {
let (output, _) = run_test("default_conversion.rs");
assert_regex!(
"Cannot parse 'error' to get [a-z:_0-9]*MyType",
output.stdout.str()
);
TestResults::new()
.ok("test_base")
.ok("test_byte_array")
.ok("test_convert_custom")
.fail("test_fail_conversion")
.assert(output);
}
#[rstest]
#[case("once.rs")]
#[case::no_return("once_no_return.rs")]
#[case::defined_type("once_defined_type.rs")]
fn accept_once_attribute_and_call_fixture_just_once(#[case] fname: &str) {
let project = prj(fname).with_nocapture();
let output = project.run_tests().unwrap();
// Just to see the errors if fixture doesn't compile
assert_in!(output.stderr.str(), "Exec fixture() just once");
let occurrences = output.stderr.str().count("Exec fixture() just once");
assert_eq!(1, occurrences);
}
mod show_correct_errors {
use super::*;
use std::process::Output;
use rstest::{fixture, rstest};
#[fixture]
#[once]
fn errors_rs() -> (Output, String) {
run_test("errors.rs")
}
#[rstest]
fn when_cannot_resolve_fixture(errors_rs: &(Output, String)) {
let (output, name) = errors_rs.clone();
assert_in!(output.stderr.str(), "error[E0433]: ");
assert_in!(
output.stderr.str(),
format!(
r#"
--> {name}/src/lib.rs:14:33
|
14 | fn error_cannot_resolve_fixture(no_fixture: u32) {{"#
)
.unindent()
);
}
#[rstest]
fn on_mismatched_types_inner(errors_rs: &(Output, String)) {
let (output, name) = errors_rs.clone();
assert_in!(
output.stderr.str(),
format!(
r#"
error[E0308]: mismatched types
--> {name}/src/lib.rs:10:18
|
10 | let a: u32 = "";
"#
)
.unindent()
);
}
#[rstest]
fn on_mismatched_types_argument(errors_rs: &(Output, String)) {
let (output, name) = errors_rs.clone();
assert_in!(
output.stderr.str(),
format!(
r#"
error[E0308]: mismatched types
--> {name}/src/lib.rs:17:29
"#
)
.unindent()
);
assert_in!(
output.stderr.str(),
r#"
17 | fn error_fixture_wrong_type(fixture: String) {}
| ^^^^^^"#
.unindent()
);
}
#[rstest]
fn on_invalid_fixture(errors_rs: &(Output, String)) {
let (output, name) = errors_rs.clone();
assert_in!(
output.stderr.str(),
format!(
"
error: Missed argument: 'not_a_fixture' should be a test function argument.
--> {name}/src/lib.rs:19:11
|
19 | #[fixture(not_a_fixture(24))]
| ^^^^^^^^^^^^^
"
)
.unindent()
);
}
#[rstest]
fn on_duplicate_fixture_argument(errors_rs: &(Output, String)) {
let (output, name) = errors_rs.clone();
assert_in!(
output.stderr.str(),
format!(
r#"
error: Duplicate argument: 'f' is already defined.
--> {name}/src/lib.rs:32:23
|
32 | #[fixture(f("first"), f("second"))]
| ^
"#
)
.unindent()
);
}
#[rstest]
fn on_destruct_implicit_fixture(errors_rs: &(Output, String)) {
let (output, name) = errors_rs.clone();
assert_in!(
output.stderr.str(),
format!(
r#"
error: To destruct a fixture you should provide a path to resolve it by '#[from(...)]' attribute.
--> {name}/src/lib.rs:48:35
|
48 | fn error_destruct_without_resolve(T(a): T) {{}}
| ^^^^^^^
"#
)
.unindent()
);
}
#[rstest]
fn on_destruct_explicit_fixture_without_from(errors_rs: &(Output, String)) {
let (output, name) = errors_rs.clone();
assert_in!(
output.stderr.str(),
format!(
r#"
error: To destruct a fixture you should provide a path to resolve it by '#[from(...)]' attribute.
--> {name}/src/lib.rs:51:57
|
51 | fn error_destruct_without_resolve_also_with(#[with(21)] T(a): T) {{}}
| ^^^^^^^
"#
)
.unindent()
);
assert_eq!(
1,
output.stderr.str().count("51 | fn error_destruct_without")
)
}
#[fixture]
#[once]
fn errors_once_rs() -> (Output, String) {
run_test("errors_once.rs")
}
#[rstest]
fn once_async(errors_once_rs: &(Output, String)) {
let (output, name) = errors_once_rs.clone();
assert_in!(
output.stderr.str(),
format!(
r#"
error: Cannot apply #[once] to async fixture.
--> {}/src/lib.rs:4:1
|
4 | #[once]
"#,
name
)
.unindent()
);
}
#[rstest]
fn once_generic_type(errors_once_rs: &(Output, String)) {
let (output, name) = errors_once_rs.clone();
assert_in!(
output.stderr.str(),
format!(
r#"
error: Cannot apply #[once] on generic fixture.
--> {}/src/lib.rs:9:1
|
9 | #[once]
"#,
name
)
.unindent()
);
}
#[rstest]
fn once_generic_impl(errors_once_rs: &(Output, String)) {
let (output, name) = errors_once_rs.clone();
assert_in!(
output.stderr.str(),
format!(
r#"
error: Cannot apply #[once] on generic fixture.
--> {}/src/lib.rs:15:1
|
15 | #[once]
"#,
name
)
.unindent()
);
}
#[rstest]
fn once_on_not_sync_type(errors_once_rs: &(Output, String)) {
let (output, name) = errors_once_rs.clone();
assert_in!(
output.stderr.str(),
format!(
r#"
error[E0277]: `Cell<u32>` cannot be shared between threads safely
--> {}/src/lib.rs:20:1
|
20 | #[fixture]
| ^^^^^^^^^^ `Cell<u32>` cannot be shared between threads safely
"#,
name,
)
.unindent(),
);
}
}
}
+35
View File
@@ -0,0 +1,35 @@
use rstest_test::{sanitize_name, testname, Project};
/// Rstest integration tests
mod rstest;
/// Fixture's integration tests
mod fixture;
use lazy_static::lazy_static;
use std::path::{Path, PathBuf};
use temp_testdir::TempDir;
lazy_static! {
static ref ROOT_DIR: TempDir = TempDir::default().permanent();
static ref ROOT_PROJECT: Project = Project::new(ROOT_DIR.as_ref());
}
pub fn base_prj() -> Project {
let prj_name = sanitize_name(testname());
ROOT_PROJECT.subproject(&prj_name)
}
pub fn prj() -> Project {
let prj_name = sanitize_name(testname());
let prj = ROOT_PROJECT.subproject(&prj_name);
prj.add_local_dependency("rstest");
prj
}
pub fn resources<O: AsRef<Path>>(name: O) -> PathBuf {
Path::new("tests").join("resources").join(name)
}
@@ -0,0 +1,73 @@
use std::io::prelude::*;
use rstest::*;
#[fixture]
async fn async_u32() -> u32 {
42
}
#[fixture]
async fn nest_fixture(#[future] async_u32: u32) -> u32 {
async_u32.await
}
#[fixture(fortytwo = async { 42 })]
async fn nest_fixture_with_default(#[future] fortytwo: u32) -> u32 {
fortytwo.await
}
#[rstest]
async fn default_is_async() {
assert_eq!(42, async_u32::default().await);
}
#[rstest]
async fn use_async_nest_fixture_default(#[future] nest_fixture: u32) {
assert_eq!(42, nest_fixture.await);
}
#[rstest(nest_fixture(async { 24 }))]
async fn use_async_nest_fixture_injected(#[future] nest_fixture: u32) {
assert_eq!(24, nest_fixture.await);
}
#[rstest]
async fn use_async_nest_fixture_with_default(#[future] nest_fixture_with_default: u32) {
assert_eq!(42, nest_fixture_with_default.await);
}
#[rstest]
async fn use_async_fixture(#[future] async_u32: u32) {
assert_eq!(42, async_u32.await);
}
#[fixture]
async fn async_impl_output() -> impl Read {
std::io::Cursor::new(vec![1, 2, 3, 4, 5])
}
#[rstest]
async fn use_async_impl_output<T: Read>(#[future] async_impl_output: T) {
let reader = async_impl_output.await;
}
#[fixture(four = async { 4 }, two = 2)]
async fn two_args_mix_fixture(#[future] four: u32, two: u32) -> u32 {
four.await * 10 + two
}
#[rstest]
async fn use_two_args_mix_fixture(#[future] two_args_mix_fixture: u32) {
assert_eq!(42, two_args_mix_fixture.await);
}
#[rstest(two_args_mix_fixture(async { 5 }))]
async fn use_two_args_mix_fixture_inject_first(#[future] two_args_mix_fixture: u32) {
assert_eq!(52, two_args_mix_fixture.await);
}
#[rstest(two_args_mix_fixture(async { 3 }, 1))]
async fn use_two_args_mix_fixture_inject_both(#[future] two_args_mix_fixture: u32) {
assert_eq!(31, two_args_mix_fixture.await);
}
@@ -0,0 +1,105 @@
use std::io::prelude::*;
use rstest::*;
#[fixture]
async fn async_u32() -> u32 {
42
}
#[fixture]
#[awt]
async fn nest_fixture(#[future] async_u32: u32) -> u32 {
async_u32
}
#[fixture]
#[awt]
async fn nest_fixture_with_default(
#[future]
#[default(async { 42 })]
fortytwo: u32,
) -> u32 {
fortytwo
}
#[rstest]
async fn default_is_async() {
assert_eq!(42, async_u32::default().await);
}
#[rstest]
#[awt]
async fn use_async_nest_fixture_default(#[future] nest_fixture: u32) {
assert_eq!(42, nest_fixture);
}
#[rstest]
#[awt]
async fn use_async_nest_fixture_injected(
#[future]
#[with(async { 24 })]
nest_fixture: u32,
) {
assert_eq!(24, nest_fixture);
}
#[rstest]
#[awt]
async fn use_async_nest_fixture_with_default(#[future] nest_fixture_with_default: u32) {
assert_eq!(42, nest_fixture_with_default);
}
#[rstest]
#[awt]
async fn use_async_fixture(#[future] async_u32: u32) {
assert_eq!(42, async_u32);
}
#[fixture]
async fn async_impl_output() -> impl Read {
std::io::Cursor::new(vec![1, 2, 3, 4, 5])
}
#[rstest]
#[awt]
async fn use_async_impl_output<T: Read>(#[future] async_impl_output: T) {
let reader = async_impl_output;
}
#[fixture]
#[awt]
async fn two_args_mix_fixture(
#[future]
#[default(async { 4 })]
four: u32,
#[default(2)] two: u32,
) -> u32 {
four * 10 + two
}
#[rstest]
#[awt]
async fn use_two_args_mix_fixture(#[future] two_args_mix_fixture: u32) {
assert_eq!(42, two_args_mix_fixture);
}
#[rstest]
#[awt]
async fn use_two_args_mix_fixture_inject_first(
#[future]
#[with(async { 5 })]
two_args_mix_fixture: u32,
) {
assert_eq!(52, two_args_mix_fixture);
}
#[rstest]
#[awt]
async fn use_two_args_mix_fixture_inject_both(
#[future]
#[with(async { 3 }, 1)]
two_args_mix_fixture: u32,
) {
assert_eq!(31, two_args_mix_fixture);
}
@@ -0,0 +1,94 @@
use std::io::prelude::*;
use rstest::*;
#[fixture]
async fn async_u32() -> u32 {
42
}
#[fixture]
async fn nest_fixture(#[future(awt)] async_u32: u32) -> u32 {
async_u32
}
#[fixture]
async fn nest_fixture_with_default(
#[future(awt)]
#[default(async { 42 })]
fortytwo: u32,
) -> u32 {
fortytwo
}
#[rstest]
async fn default_is_async() {
assert_eq!(42, async_u32::default().await);
}
#[rstest]
async fn use_async_nest_fixture_default(#[future(awt)] nest_fixture: u32) {
assert_eq!(42, nest_fixture);
}
#[rstest]
async fn use_async_nest_fixture_injected(
#[future(awt)]
#[with(async { 24 })]
nest_fixture: u32,
) {
assert_eq!(24, nest_fixture);
}
#[rstest]
async fn use_async_nest_fixture_with_default(#[future(awt)] nest_fixture_with_default: u32) {
assert_eq!(42, nest_fixture_with_default);
}
#[rstest]
async fn use_async_fixture(#[future(awt)] async_u32: u32) {
assert_eq!(42, async_u32);
}
#[fixture]
async fn async_impl_output() -> impl Read {
std::io::Cursor::new(vec![1, 2, 3, 4, 5])
}
#[rstest]
async fn use_async_impl_output<T: Read>(#[future(awt)] async_impl_output: T) {
let reader = async_impl_output;
}
#[fixture]
async fn two_args_mix_fixture(
#[future(awt)]
#[default(async { 4 })]
four: u32,
#[default(2)] two: u32,
) -> u32 {
four * 10 + two
}
#[rstest]
async fn use_two_args_mix_fixture(#[future(awt)] two_args_mix_fixture: u32) {
assert_eq!(42, two_args_mix_fixture);
}
#[rstest]
async fn use_two_args_mix_fixture_inject_first(
#[future(awt)]
#[with(async { 5 })]
two_args_mix_fixture: u32,
) {
assert_eq!(52, two_args_mix_fixture);
}
#[rstest]
async fn use_two_args_mix_fixture_inject_both(
#[future(awt)]
#[with(async { 3 }, 1)]
two_args_mix_fixture: u32,
) {
assert_eq!(31, two_args_mix_fixture);
}
@@ -0,0 +1,31 @@
use rstest::*;
#[fixture]
fn s() -> &'static str {
"42"
}
#[fixture]
fn fx<S: ToString>(s: S) -> usize {
s.to_string().len()
}
#[fixture]
fn sum() -> usize {
42
}
#[fixture]
fn fx_double<S: ToString>(sum: usize, s: S) -> usize {
s.to_string().len() + sum
}
#[test]
fn resolve() {
assert_eq!(2, fx::default())
}
#[test]
fn resolve_partial() {
assert_eq!(12, fx_double::partial_1(10))
}
@@ -0,0 +1,46 @@
use rstest::{fixture, rstest};
#[fixture(value = 42)]
pub fn simple(value: u32) -> u32 {
value
}
#[fixture(value = 21, mult = 2)]
pub fn double(value: u32, mult: u32) -> u32 {
value * mult
}
#[fixture]
pub fn middle() -> u32 {
2
}
#[fixture(value = 21, mult = 4)]
pub fn mixed(value: u32, middle: u32, mult: u32) -> u32 {
value * mult / middle
}
#[rstest]
fn test_simple(simple: u32) {
assert_eq!(simple, 42)
}
#[rstest(simple(21))]
fn test_simple_changed(simple: u32) {
assert_eq!(simple, 21)
}
#[rstest]
fn test_double(double: u32) {
assert_eq!(double, 42)
}
#[rstest(double(20, 3))]
fn test_double_changed(double: u32) {
assert_eq!(double, 60)
}
#[rstest]
fn test_mixed(mixed: u32) {
assert_eq!(mixed, 42)
}
@@ -0,0 +1,51 @@
use rstest::{fixture, rstest};
use std::net::{Ipv4Addr, SocketAddr};
struct MyType(String);
struct E;
impl core::str::FromStr for MyType {
type Err = E;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"error" => Err(E),
inner => Ok(MyType(inner.to_owned())),
}
}
}
#[fixture]
fn base(#[default("1.2.3.4")] ip: Ipv4Addr, #[default(r#"8080"#)] port: u16) -> SocketAddr {
SocketAddr::new(ip.into(), port)
}
#[fixture]
fn fail(#[default("error")] t: MyType) -> MyType {
t
}
#[fixture]
fn valid(#[default("some")] t: MyType) -> MyType {
t
}
#[rstest]
fn test_base(base: SocketAddr) {
assert_eq!(base, "1.2.3.4:8080".parse().unwrap());
}
#[fixture]
fn byte_array(#[default(b"1234")] some: &[u8]) -> usize {
some.len()
}
#[rstest]
fn test_byte_array(byte_array: usize) {
assert_eq!(4, byte_array);
}
#[rstest]
fn test_convert_custom(valid: MyType) {}
#[rstest]
fn test_fail_conversion(fail: MyType) {}
@@ -0,0 +1,46 @@
use rstest::{fixture, rstest};
#[fixture]
pub fn simple(#[default(42)] value: u32) -> u32 {
value
}
#[fixture]
pub fn double(#[default(20 + 1)] value: u32, #[default(1 + 1)] mult: u32) -> u32 {
value * mult
}
#[fixture]
pub fn middle() -> u32 {
2
}
#[fixture]
pub fn mixed(#[default(21)] value: u32, middle: u32, #[default(2 + 2)] mult: u32) -> u32 {
value * mult / middle
}
#[rstest]
fn test_simple(simple: u32) {
assert_eq!(simple, 42)
}
#[rstest(simple(21))]
fn test_simple_changed(simple: u32) {
assert_eq!(simple, 21)
}
#[rstest]
fn test_double(double: u32) {
assert_eq!(double, 42)
}
#[rstest(double(20, 3))]
fn test_double_changed(double: u32) {
assert_eq!(double, 60)
}
#[rstest]
fn test_mixed(mixed: u32) {
assert_eq!(mixed, 42)
}
@@ -0,0 +1,43 @@
use rstest::*;
#[fixture]
pub fn i() -> u32 {
42
}
#[fixture]
pub fn j() -> i32 {
-42
}
#[fixture(::default<impl Iterator<Item=(u32, i32)>>::partial_1<impl Iterator<Item=(I,i32)>>)]
pub fn fx<I, J>(i: I, j: J) -> impl Iterator<Item=(I, J)> {
std::iter::once((i, j))
}
#[test]
fn resolve() {
assert_eq!((42, -42), fx::default().next().unwrap())
}
#[test]
fn resolve_partial() {
assert_eq!((42.0, -42), fx::partial_1(42.0).next().unwrap())
}
#[fixture]
#[default(impl Iterator<Item=(u32, i32)>)]
#[partial_1(impl Iterator<Item=(I,i32)>)]
pub fn fx_attrs<I, J>(i: I, j: J) -> impl Iterator<Item=(I, J)> {
std::iter::once((i, j))
}
#[test]
fn resolve_attrs() {
assert_eq!((42, -42), fx_attrs::default().next().unwrap())
}
#[test]
fn resolve_partial_attrs() {
assert_eq!((42.0, -42), fx_attrs::partial_1(42.0).next().unwrap())
}
@@ -0,0 +1,41 @@
use rstest::*;
#[fixture]
fn dyn_box() -> Box<dyn Iterator<Item=i32>> {
Box::new(std::iter::once(42))
}
#[fixture]
fn dyn_ref() -> &'static dyn ToString {
&42
}
#[fixture]
fn dyn_box_resolve(mut dyn_box: Box<dyn Iterator<Item=i32>>) -> i32 {
dyn_box.next().unwrap()
}
#[fixture]
fn dyn_ref_resolve(dyn_ref: &dyn ToString) -> String {
dyn_ref.to_string()
}
#[rstest]
fn test_dyn_box(mut dyn_box: Box<dyn Iterator<Item=i32>>) {
assert_eq!(42, dyn_box.next().unwrap())
}
#[rstest]
fn test_dyn_ref(dyn_ref: &dyn ToString) {
assert_eq!("42", dyn_ref.to_string())
}
#[rstest]
fn test_dyn_box_resolve(dyn_box_resolve: i32) {
assert_eq!(42, dyn_box_resolve)
}
#[rstest]
fn test_dyn_ref_resolve(dyn_ref_resolve: String) {
assert_eq!("42", dyn_ref_resolve)
}
@@ -0,0 +1,51 @@
use rstest::*;
#[fixture]
pub fn fixture() -> u32 {
42
}
#[fixture]
fn error_inner(fixture: u32) {
let a: u32 = "";
}
#[fixture]
fn error_cannot_resolve_fixture(no_fixture: u32) {}
#[fixture]
fn error_fixture_wrong_type(fixture: String) {}
#[fixture(not_a_fixture(24))]
fn error_inject_an_invalid_fixture(fixture: String) {}
#[fixture]
fn name() -> &'static str {
"name"
}
#[fixture]
fn f(name: &str) -> String {
name.to_owned()
}
#[fixture(f("first"), f("second"))]
fn error_inject_a_fixture_more_than_once(f: String) {}
struct T(u32);
#[fixture]
fn structed() -> T {
T(42)
}
#[fixture]
fn structed_injectd(fixture: u32) -> T {
T(fixture)
}
#[fixture]
fn error_destruct_without_resolve(T(a): T) {}
#[fixture]
fn error_destruct_without_resolve_also_with(#[with(21)] T(a): T) {}
@@ -0,0 +1,24 @@
use rstest::*;
#[fixture]
#[once]
async fn error_async_once_fixture() {
}
#[fixture]
#[once]
fn error_generics_once_fixture<T: std::fmt::Debug>() -> T {
42
}
#[fixture]
#[once]
fn error_generics_once_fixture() -> impl Iterator<Item = u32> {
std::iter::once(42)
}
#[fixture]
#[once]
fn error_once_fixture_not_sync() -> std::cell::Cell<u32> {
std::cell::Cell::new(42)
}
@@ -0,0 +1,44 @@
use rstest::fixture;
trait Mult {
fn mult(&self, n: u32) -> u32;
}
struct M(u32);
impl Mult for M {
fn mult(&self, n: u32) -> u32 {
n * self.0
}
}
#[fixture]
fn my_fixture() -> u32 { 42 }
#[fixture]
fn multiplier() -> M {
M(2)
}
#[fixture]
fn my_fixture_injected(my_fixture: u32, multiplier: impl Mult) -> u32 { multiplier.mult(my_fixture) }
#[test]
fn resolve_new() {
assert_eq!(42, my_fixture::get());
}
#[test]
fn resolve_default() {
assert_eq!(42, my_fixture::default());
}
#[test]
fn injected_new() {
assert_eq!(63, my_fixture_injected::get(21, M(3)));
}
#[test]
fn injected_default() {
assert_eq!(84, my_fixture_injected::default());
}
@@ -0,0 +1,14 @@
mod my_mod {
use rstest::{fixture};
#[fixture]
pub fn mod_fixture() -> u32 { 42 }
}
use my_mod::mod_fixture;
#[test]
fn struct_access() {
assert_eq!(42, mod_fixture::default());
}
@@ -0,0 +1,57 @@
use rstest::*;
#[fixture]
fn fx_base_impl_return() -> impl Iterator<Item=u32> { std::iter::once(42) }
#[fixture]
fn fx_base_impl_input(mut fx_base_impl_return: impl Iterator<Item=u32>) -> u32 {
fx_base_impl_return.next().unwrap()
}
#[rstest]
fn base_impl_return(mut fx_base_impl_return: impl Iterator<Item=u32>) {
assert_eq!(42, fx_base_impl_return.next().unwrap());
}
#[rstest]
fn base_impl_input(mut fx_base_impl_input: u32) {
assert_eq!(42, fx_base_impl_input);
}
#[fixture]
fn fx_nested_impl_return() -> impl Iterator<Item=impl ToString> { std::iter::once(42) }
#[fixture]
fn fx_nested_impl_input(mut fx_nested_impl_return: impl Iterator<Item=impl ToString>) -> String {
fx_nested_impl_return.next().unwrap().to_string()
}
#[rstest]
fn nested_impl_return(mut fx_nested_impl_return: impl Iterator<Item=impl ToString>) {
assert_eq!("42", fx_nested_impl_return.next().unwrap().to_string());
}
#[rstest]
fn nested_impl_input(mut fx_nested_impl_input: String) {
assert_eq!("42", &fx_nested_impl_input);
}
#[fixture]
fn fx_nested_multiple_impl_return() -> (impl Iterator<Item=impl ToString>, impl ToString) {
(std::iter::once(42), 42i32)
}
#[fixture]
fn fx_nested_multiple_impl_input(mut fx_nested_multiple_impl_return: (impl Iterator<Item=impl ToString>, impl ToString)) -> bool {
fx_nested_multiple_impl_return.0.next().unwrap().to_string() == fx_nested_multiple_impl_return.1.to_string()
}
#[rstest]
fn nested_multiple_impl_return(mut fx_nested_multiple_impl_return: (impl Iterator<Item=impl ToString>, impl ToString)) {
assert_eq!(fx_nested_multiple_impl_return.0.next().unwrap().to_string(), fx_nested_multiple_impl_return.1.to_string());
}
#[rstest]
fn nested_multiple_impl_input(fx_nested_multiple_impl_input: bool) {
assert!(fx_nested_multiple_impl_input);
}
@@ -0,0 +1,17 @@
use rstest::*;
#[fixture]
fn val() -> i32 {
21
}
#[fixture]
fn fortytwo(mut val: i32) -> i32 {
val *= 2;
val
}
#[rstest]
fn the_test(fortytwo: i32) {
assert_eq!(fortytwo, 42);
}
@@ -0,0 +1,21 @@
use rstest::{fixture, rstest};
#[fixture]
#[once]
fn once_fixture() -> u32 {
eprintln!("Exec fixture() just once");
42
}
#[rstest]
fn base(once_fixture: &u32) {
assert_eq!(&42, once_fixture);
}
#[rstest]
#[case(2)]
#[case(3)]
#[case(7)]
fn cases(once_fixture: &u32, #[case] divisor: u32) {
assert_eq!(0, *once_fixture % divisor);
}
@@ -0,0 +1,33 @@
use rstest::{fixture, rstest};
#[fixture]
#[default(u32)]
#[partial_1(u32)]
#[once]
fn once_fixture(#[default(())] a: (), #[default(())] b: ()) -> u32 {
eprintln!("Exec fixture() just once");
42
}
#[rstest]
fn base(once_fixture: &u32) {
assert_eq!(&42, once_fixture);
}
#[rstest]
fn base_partial(#[with(())] once_fixture: &u32) {
assert_eq!(&42, once_fixture);
}
#[rstest]
fn base_complete(#[with((), ())] once_fixture: &u32) {
assert_eq!(&42, once_fixture);
}
#[rstest]
#[case(2)]
#[case(3)]
#[case(7)]
fn cases(once_fixture: &u32, #[case] divisor: u32) {
assert_eq!(0, *once_fixture % divisor);
}
@@ -0,0 +1,20 @@
use rstest::*;
#[fixture]
#[once]
fn once_fixture() {
eprintln!("Exec fixture() just once");
}
#[rstest]
fn base(_once_fixture: ()) {
assert!(true);
}
#[rstest]
#[case()]
#[case()]
#[case()]
fn cases(_once_fixture: ()) {
assert!(true);
}
@@ -0,0 +1,40 @@
use rstest::*;
#[fixture]
fn f1() -> u32 { 0 }
#[fixture]
fn f2() -> u32 { 0 }
#[fixture]
fn f3() -> u32 { 0 }
#[fixture]
fn fixture(f1: u32, f2: u32, f3: u32) -> u32 { f1 + 10 * f2 + 100 * f3 }
#[fixture(fixture(7))]
fn partial_1(fixture: u32) -> u32 { fixture }
#[fixture(fixture(2, 4))]
fn partial_2(fixture: u32) -> u32 { fixture }
#[fixture(fixture(2, 4, 5))]
fn complete(fixture: u32) -> u32 { fixture }
#[rstest]
fn default(fixture: u32) {
assert_eq!(fixture, 0);
}
#[rstest]
fn t_partial_1(partial_1: u32) {
assert_eq!(partial_1, 7);
}
#[rstest]
fn t_partial_2(partial_2: u32) {
assert_eq!(partial_2, 42);
}
#[rstest]
fn t_complete(complete: u32) {
assert_eq!(complete, 542);
}
@@ -0,0 +1,54 @@
use rstest::*;
#[fixture]
fn f1() -> u32 {
0
}
#[fixture]
fn f2() -> u32 {
0
}
#[fixture]
fn f3() -> u32 {
0
}
#[fixture]
fn fixture(f1: u32, f2: u32, f3: u32) -> u32 {
f1 + 10 * f2 + 100 * f3
}
#[fixture]
fn partial_1(#[with(7)] fixture: u32) -> u32 {
fixture
}
#[fixture]
fn partial_2(#[with(2, 4)] fixture: u32) -> u32 {
fixture
}
#[fixture]
fn complete(#[with(2, 4, 5)] fixture: u32) -> u32 {
fixture
}
#[rstest]
fn default(fixture: u32) {
assert_eq!(fixture, 0);
}
#[rstest]
fn t_partial_1(partial_1: u32) {
assert_eq!(partial_1, 7);
}
#[rstest]
fn t_partial_2(partial_2: u32) {
assert_eq!(partial_2, 42);
}
#[rstest]
fn t_complete(complete: u32) {
assert_eq!(complete, 542);
}
@@ -0,0 +1,64 @@
use rstest::*;
#[fixture]
fn very_long_and_boring_name(#[default(42)] inject: u32) -> u32 {
inject
}
mod sub_module {
use super::*;
#[fixture]
pub fn mod_fixture() -> u32 {
42
}
}
#[fixture(very_long_and_boring_name as foo)]
fn compact(foo: u32) -> u32 {
foo
}
#[fixture(very_long_and_boring_name(21) as foo)]
fn compact_injected(foo: u32) -> u32 {
foo
}
#[fixture(sub_module::mod_fixture as foo)]
fn compact_from_mod(foo: u32) -> u32 {
foo
}
#[fixture]
fn attribute(#[from(very_long_and_boring_name)] foo: u32) -> u32 {
foo
}
#[fixture]
fn attribute_mod(#[from(sub_module::mod_fixture)] foo: u32) -> u32 {
foo
}
#[fixture]
fn attribute_injected(
#[from(very_long_and_boring_name)]
#[with(21)]
foo: u32,
) -> u32 {
foo
}
#[rstest]
fn test(
compact: u32,
attribute: u32,
attribute_mod: u32,
compact_from_mod: u32,
compact_injected: u32,
attribute_injected: u32,
) {
assert_eq!(compact, attribute);
assert_eq!(attribute, attribute_mod);
assert_eq!(attribute_mod, compact_from_mod);
assert_eq!(compact_injected, attribute_injected);
}
@@ -0,0 +1,42 @@
use rstest::{rstest, fixture};
pub trait Tr {
fn get() -> Self;
}
impl Tr for i32 {
fn get() -> Self {
42
}
}
impl Tr for u32 {
fn get() -> Self {
42
}
}
#[fixture]
pub fn f<T: Tr>() -> T {
T::get()
}
#[fixture]
pub fn fu32(f: u32) -> u32 {
f
}
#[fixture]
pub fn fi32(f: i32) -> i32 {
f
}
#[rstest]
fn test_u32(fu32: u32) {
assert_eq!(fu32, 42)
}
#[rstest]
fn test_i32(fi32: i32) {
assert_eq!(fi32, 42)
}
@@ -0,0 +1,17 @@
use rstest::{rstest, fixture};
#[fixture]
fn root() -> u32 { 21 }
#[fixture]
fn injection(root: u32) -> u32 { 2 * root }
#[rstest]
fn success(injection: u32) {
assert_eq!(42, injection);
}
#[rstest]
fn fail(injection: u32) {
assert_eq!(41, injection);
}
@@ -0,0 +1,38 @@
use rstest::*;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
#[rstest]
fn start_with_name(
#[files("files/**/*.txt")]
#[by_ref]
path: &PathBuf,
) {
let name = path.file_name().unwrap();
let mut f = File::open(&path).unwrap();
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
assert!(contents.starts_with(name.to_str().unwrap()))
}
#[fixture]
fn f() -> u32 {
42
}
#[rstest]
#[case(42)]
fn test(
#[by_ref] f: &u32,
#[case]
#[by_ref]
c: &u32,
#[values(42, 142)]
#[by_ref]
v: &u32,
) {
assert_eq!(f, c);
assert_eq!(*c, *v % 100);
}
@@ -0,0 +1,4 @@
use rstest::rstest;
#[rstest(one, two, three)]
fn should_show_error_for_no_case(one: u32, two: u32, three: u32) {}
@@ -0,0 +1,18 @@
use rstest::*;
#[rstest]
#[case::pass(42, async { 42 })]
#[case::fail(42, async { 41 })]
#[should_panic]
#[case::pass_panic(42, async { 41 })]
#[should_panic]
#[case::fail_panic(42, async { 42 })]
async fn my_async_test(#[case] expected: u32, #[case] #[future] value: u32) {
assert_eq!(expected, value.await);
}
#[rstest]
#[case::pass(42, async { 42 })]
async fn my_async_test_revert(#[case] expected: u32, #[future] #[case] value: u32) {
assert_eq!(expected, value.await);
}
@@ -0,0 +1,28 @@
use rstest::*;
#[rstest]
#[case::pass(42, async { 42 })]
#[case::fail(42, async { 41 })]
#[should_panic]
#[case::pass_panic(42, async { 41 })]
#[should_panic]
#[case::fail_panic(42, async { 42 })]
async fn my_async_test(
#[case] expected: u32,
#[case]
#[future(awt)]
value: u32,
) {
assert_eq!(expected, value);
}
#[rstest]
#[case::pass(42, async { 42 })]
async fn my_async_test_revert(
#[case] expected: u32,
#[future(awt)]
#[case]
value: u32,
) {
assert_eq!(expected, value);
}
@@ -0,0 +1,30 @@
use rstest::*;
#[rstest]
#[case::pass(42, async { 42 })]
#[case::fail(42, async { 41 })]
#[should_panic]
#[case::pass_panic(42, async { 41 })]
#[should_panic]
#[case::fail_panic(42, async { 42 })]
#[awt]
async fn my_async_test(
#[case] expected: u32,
#[case]
#[future]
value: u32,
) {
assert_eq!(expected, value);
}
#[rstest]
#[case::pass(42, async { 42 })]
#[awt]
async fn my_async_test_revert(
#[case] expected: u32,
#[future]
#[case]
value: u32,
) {
assert_eq!(expected, value);
}
@@ -0,0 +1,24 @@
use rstest::*;
#[rstest]
#[case::pass(async { 3 })]
#[awt]
async fn my_mut_test_global_awt(
#[future]
#[case]
mut a: i32,
) {
a = 4;
assert_eq!(a, 4);
}
#[rstest]
#[case::pass(async { 3 })]
async fn my_mut_test_local_awt(
#[future(awt)]
#[case]
mut a: i32,
) {
a = 4;
assert_eq!(a, 4);
}
@@ -0,0 +1,24 @@
use rstest::rstest;
#[rstest(
val,
case::no_panic(0),
#[should_panic]
case::panic(2),
#[should_panic(expected="expected")]
case::panic_with_message(3),
case::no_panic_but_fail(1),
#[should_panic]
case::panic_but_fail(0),
#[should_panic(expected="other")]
case::panic_with_wrong_message(3),
)]
fn attribute_per_case(val: i32) {
match val {
0 => assert!(true),
1 => assert!(false),
2 => panic!("No catch"),
3 => panic!("expected"),
_ => panic!("Not defined"),
}
}
@@ -0,0 +1,10 @@
use rstest::rstest;
#[cfg(test)]
#[rstest(a, b, case(42), case(1, 2), case(43))]
fn error_less_arguments(a: u32, b: u32) {}
#[cfg(test)]
#[rstest(a, case(42, 43), case(12), case(24, 34))]
fn error_too_much_arguments(a: u32) {}
@@ -0,0 +1,11 @@
use rstest::rstest;
#[rstest(
expected,
case::user_test_description(true),
case(true),
case::user_test_description_fail(false)
)]
fn description(expected: bool) {
assert!(expected);
}
@@ -0,0 +1,10 @@
use rstest::*;
#[rstest]
#[case::first_no_dump("Please don't trace me")]
#[trace]
#[case::dump_me("Trace it!")]
#[case::last_no_dump("Please don't trace me")]
fn cases(#[case] s: &str) {
assert!(false);
}
@@ -0,0 +1,21 @@
use rstest::*;
use actix_rt;
use std::future::Future;
#[rstest(expected, value,
case::pass(42, 42),
#[should_panic]
case::panic(41, 42),
case::fail(1, 42)
)]
#[test]
fn sync(expected: u32, value: u32) { assert_eq!(expected, value); }
#[rstest(expected, value,
case::pass(42, async { 42 }),
#[should_panic]
case::panic(41, async { 42 }),
case::fail(1, async { 42 })
)]
#[actix_rt::test]
async fn fn_async(expected: u32, value: impl Future<Output=u32>) { assert_eq!(expected, value.await); }
@@ -0,0 +1,5 @@
use rstest::rstest;
#[cfg(test)]
#[rstest(f, case(42), case(24))]
fn error_param_not_exist() {}
@@ -0,0 +1,5 @@
use rstest::rstest;
#[cfg(test)]
#[rstest(a,b,c, case(1,2,3), case(3,2,1))]
fn error_param_not_exist(b: u32) {}
@@ -0,0 +1,54 @@
use rstest::*;
#[fixture]
fn f1() -> u32 {
0
}
#[fixture]
fn f2() -> u32 {
0
}
#[fixture]
fn f3() -> u32 {
0
}
#[fixture]
fn fixture(f1: u32, f2: u32, f3: u32) -> u32 {
f1 + 10 * f2 + 100 * f3
}
#[rstest(expected, case(0), case(1000))]
fn default(fixture: u32, expected: u32) {
assert_eq!(fixture, expected);
}
#[rstest(fixture(7), expected, case(7), case(1000))]
fn partial_1(fixture: u32, expected: u32) {
assert_eq!(fixture, expected);
}
#[rstest(expected, case(7), case(1000))]
fn partial_attr_1(#[with(7)] fixture: u32, expected: u32) {
assert_eq!(fixture, expected);
}
#[rstest(fixture(2, 4), expected, case(42), case(1000))]
fn partial_2(fixture: u32, expected: u32) {
assert_eq!(fixture, expected);
}
#[rstest(expected, case(42), case(1000))]
fn partial_attr_2(#[with(2, 4)] fixture: u32, expected: u32) {
assert_eq!(fixture, expected);
}
#[rstest(fixture(2, 4, 5), expected, case(542), case(1000))]
fn complete(fixture: u32, expected: u32) {
assert_eq!(fixture, expected);
}
#[rstest(expected, case(542), case(1000))]
fn complete_attr(#[with(2, 4, 5)] fixture: u32, expected: u32) {
assert_eq!(fixture, expected);
}
@@ -0,0 +1,10 @@
use rstest::rstest;
#[rstest(
expected, input,
case(4, "ciao"),
case(3, "Foo")
)]
fn strlen_test(expected: usize, input: &str) {
assert_eq!(expected, input.len());
}
@@ -0,0 +1,37 @@
use rstest::rstest;
#[rstest]
#[case::ciao(4, "ciao")]
#[should_panic]
#[case::panic(42, "Foo")]
#[case::foo(3, "Foo")]
fn all(#[case] expected: usize, #[case] input: &str) {
assert_eq!(expected, input.len());
}
#[rstest(expected, input)]
#[case::ciao(4, "ciao")]
#[case::foo(3, "Foo")]
#[should_panic]
#[case::panic(42, "Foo")]
fn just_cases(expected: usize, input: &str) {
assert_eq!(expected, input.len());
}
#[rstest(
case::ciao(4, "ciao"),
case::foo(3, "Foo"),
#[should_panic]
case::panic(42, "Foo"),
)]
fn just_args(#[case] expected: usize, #[case] input: &str) {
assert_eq!(expected, input.len());
}
#[rstest]
#[case(0, "ciao")]
#[case(0, "Foo")]
#[should_panic]
fn all_panic(#[case] expected: usize, #[case] input: &str) {
assert_eq!(expected, input.len());
}
@@ -0,0 +1,33 @@
use rstest::*;
#[rstest]
#[case::description(42)]
fn with_case(#[context] ctx: Context, #[case] _c: u32) {
assert_eq!("with_case", ctx.name);
assert_eq!(Some("description"), ctx.description);
assert_eq!(Some(0), ctx.case);
}
#[rstest]
fn without_case(#[context] ctx: Context) {
assert_eq!("without_case", ctx.name);
assert_eq!(None, ctx.description);
assert_eq!(None, ctx.case);
}
mod first {
mod inner {
use rstest::*;
#[rstest]
fn test(#[context] ctx: Context) {
assert!(ctx.module.ends_with("first::inner"));
}
}
}
#[rstest]
fn measure_time(#[context] ctx: Context) {
std::thread::sleep(std::time::Duration::from_millis(100));
assert!(ctx.start.elapsed() >= std::time::Duration::from_millis(100));
}
@@ -0,0 +1,75 @@
use rstest::*;
use std::net::SocketAddr;
#[rstest]
#[case(true, "1.2.3.4:42")]
#[case(true, r#"4.3.2.1:24"#)]
#[case(false, "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443")]
#[case(false, r#"[2aa1:db8:85a3:8af:1319:8a2e:375:4873]:344"#)]
#[case(false, "this.is.not.a.socket.address")]
#[case(false, r#"this.is.not.a.socket.address"#)]
fn cases(#[case] expected: bool, #[case] addr: SocketAddr) {
assert_eq!(expected, addr.is_ipv4());
}
#[rstest]
fn values(
#[values(
"1.2.3.4:42",
r#"4.3.2.1:24"#,
"this.is.not.a.socket.address",
r#"this.is.not.a.socket.address"#
)]
addr: SocketAddr,
) {
assert!(addr.is_ipv4())
}
#[rstest]
#[case(b"12345")]
fn not_convert_byte_array(#[case] cases: &[u8], #[values(b"abc")] values: &[u8]) {
assert_eq!(5, cases.len());
assert_eq!(3, values.len());
}
trait MyTrait {
fn my_trait(&self) -> u32 {
42
}
}
impl MyTrait for &str {}
#[rstest]
#[case("impl", "nothing")]
fn not_convert_impl(#[case] that_impl: impl MyTrait, #[case] s: &str) {
assert_eq!(42, that_impl.my_trait());
assert_eq!(42, s.my_trait());
}
#[rstest]
#[case("1.2.3.4", "1.2.3.4:42")]
#[case("1.2.3.4".to_owned(), "1.2.3.4:42")]
fn not_convert_generics<S: AsRef<str>>(#[case] ip: S, #[case] addr: SocketAddr) {
assert_eq!(addr.ip().to_string(), ip.as_ref());
}
struct MyType(String);
struct E;
impl core::str::FromStr for MyType {
type Err = E;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"error" => Err(E),
inner => Ok(MyType(inner.to_owned())),
}
}
}
#[rstest]
#[case("hello", "hello")]
#[case("doesn't mater", "error")]
fn convert_without_debug(#[case] expected: &str, #[case] converted: MyType) {
assert_eq!(expected, converted.0);
}
@@ -0,0 +1,75 @@
use other_name::*;
use std::net::SocketAddr;
#[rstest]
#[case(true, "1.2.3.4:42")]
#[case(true, r#"4.3.2.1:24"#)]
#[case(false, "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443")]
#[case(false, r#"[2aa1:db8:85a3:8af:1319:8a2e:375:4873]:344"#)]
#[case(false, "this.is.not.a.socket.address")]
#[case(false, r#"this.is.not.a.socket.address"#)]
fn cases(#[case] expected: bool, #[case] addr: SocketAddr) {
assert_eq!(expected, addr.is_ipv4());
}
#[rstest]
fn values(
#[values(
"1.2.3.4:42",
r#"4.3.2.1:24"#,
"this.is.not.a.socket.address",
r#"this.is.not.a.socket.address"#
)]
addr: SocketAddr,
) {
assert!(addr.is_ipv4())
}
#[rstest]
#[case(b"12345")]
fn not_convert_byte_array(#[case] cases: &[u8], #[values(b"abc")] values: &[u8]) {
assert_eq!(5, cases.len());
assert_eq!(3, values.len());
}
trait MyTrait {
fn my_trait(&self) -> u32 {
42
}
}
impl MyTrait for &str {}
#[rstest]
#[case("impl", "nothing")]
fn not_convert_impl(#[case] that_impl: impl MyTrait, #[case] s: &str) {
assert_eq!(42, that_impl.my_trait());
assert_eq!(42, s.my_trait());
}
#[rstest]
#[case("1.2.3.4", "1.2.3.4:42")]
#[case("1.2.3.4".to_owned(), "1.2.3.4:42")]
fn not_convert_generics<S: AsRef<str>>(#[case] ip: S, #[case] addr: SocketAddr) {
assert_eq!(addr.ip().to_string(), ip.as_ref());
}
struct MyType(String);
struct E;
impl core::str::FromStr for MyType {
type Err = E;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"error" => Err(E),
inner => Ok(MyType(inner.to_owned())),
}
}
}
#[rstest]
#[case("hello", "hello")]
#[case("doesn't mater", "error")]
fn convert_without_debug(#[case] expected: &str, #[case] converted: MyType) {
assert_eq!(expected, converted.0);
}
@@ -0,0 +1,79 @@
use rstest::*;
struct T {
a: u32,
b: u32,
}
impl T {
fn new(a: u32, b: u32) -> Self {
Self { a, b }
}
}
struct S(u32, u32);
#[fixture]
fn fix() -> T {
T::new(1, 42)
}
#[fixture]
fn named() -> S {
S(1, 42)
}
#[fixture]
fn tuple() -> (u32, u32) {
(1, 42)
}
#[fixture]
fn swap(#[from(fix)] T { a, b }: T) -> T {
T::new(b, a)
}
#[rstest]
fn swapped(#[from(swap)] T { a, b }: T) {
assert_eq!(a, 42);
assert_eq!(b, 1);
}
#[rstest]
#[case::two_times_twenty_one(T::new(2, 21))]
#[case::six_times_seven(T{ a: 6, b: 7 })]
fn cases_destruct(
#[from(fix)] T { a, b }: T,
#[case] T { a: c, b: d }: T,
#[values(T::new(42, 1), T{ a: 3, b: 14})] T { a: e, b: f }: T,
) {
assert_eq!(a * b, 42);
assert_eq!(c * d, 42);
assert_eq!(e * f, 42);
}
#[rstest]
#[case::two_times_twenty_one(S(2, 21))]
#[case::six_times_seven(S(6, 7))]
fn cases_destruct_named_tuple(
#[from(named)] S(a, b): S,
#[case] S(c, d): S,
#[values(S(42, 1), S(3, 14))] S(e, f): S,
) {
assert_eq!(a * b, 42);
assert_eq!(c * d, 42);
assert_eq!(e * f, 42);
}
#[rstest]
#[case::two_times_twenty_one((2, 21))]
#[case::six_times_seven((6, 7))]
fn cases_destruct_tuple(
#[from(tuple)] (a, b): (u32, u32),
#[case] (c, d): (u32, u32),
#[values((42, 1), (3, 14))] (e, f): (u32, u32),
) {
assert_eq!(a * b, 42);
assert_eq!(c * d, 42);
assert_eq!(e * f, 42);
}
@@ -0,0 +1,62 @@
use rstest::*;
#[derive(Debug)]
struct A {}
#[fixture]
fn fu32() -> u32 {
42
}
#[fixture]
fn fstring() -> String {
"A String".to_string()
}
#[fixture]
fn ftuple() -> (A, String, i32) {
(A {}, "A String".to_string(), -12)
}
#[rstest]
#[trace]
fn single_fail(fu32: u32, fstring: String, ftuple: (A, String, i32)) {
assert!(false);
}
#[rstest]
fn no_trace_single_fail(fu32: u32, fstring: String, ftuple: (A, String, i32)) {
assert!(false);
}
#[rstest]
#[case(42, "str", ("ss", -12))]
#[case(24, "trs", ("tt", -24))]
#[trace]
fn cases_fail(#[case] u: u32, #[case] s: &str, #[case] t: (&str, i32)) {
assert!(false);
}
#[rstest]
#[case(42, "str", ("ss", -12))]
#[case(24, "trs", ("tt", -24))]
fn no_trace_cases_fail(#[case] u: u32, #[case] s: &str, #[case] t: (&str, i32)) {
assert!(false);
}
#[rstest]
#[trace]
fn matrix_fail(
#[values(1, 3)] u: u32,
#[values("rst", "srt")] s: &str,
#[values(("SS", -12), ("TT", -24))] t: (&str, i32),
) {
assert!(false);
}
#[rstest]
fn no_trace_matrix_fail(
#[values(1, 3)] u: u32,
#[values("rst", "srt")] s: &str,
#[values(("SS", -12), ("TT", -24))] t: (&str, i32),
) {
assert!(false);
}
@@ -0,0 +1,63 @@
use rstest::*;
#[derive(Debug)]
struct A {}
#[fixture]
fn fu32() -> u32 {
42
}
#[fixture]
fn fstring() -> String {
"A String".to_string()
}
#[fixture]
fn ftuple() -> (A, String, i32) {
(A {}, "A String".to_string(), -12)
}
#[rstest(::trace)]
fn single_fail(fu32: u32, fstring: String, ftuple: (A, String, i32)) {
assert!(false);
}
#[rstest]
fn no_trace_single_fail(fu32: u32, fstring: String, ftuple: (A, String, i32)) {
assert!(false);
}
#[rstest(u, s, t,
case(42, "str", ("ss", -12)),
case(24, "trs", ("tt", -24))
::trace
)]
fn cases_fail(u: u32, s: &str, t: (&str, i32)) {
assert!(false);
}
#[rstest(u, s, t,
case(42, "str", ("ss", -12)),
case(24, "trs", ("tt", -24))
)]
fn no_trace_cases_fail(u: u32, s: &str, t: (&str, i32)) {
assert!(false);
}
#[rstest(
u => [1, 2],
s => ["rst", "srt"],
t => [("SS", -12), ("TT", -24)]
::trace
)]
fn matrix_fail(u: u32, s: &str, t: (&str, i32)) {
assert!(false);
}
#[rstest(
u => [1, 2],
s => ["rst", "srt"],
t => [("SS", -12), ("TT", -24)]
)]
fn no_trace_matrix_fail(u: u32, s: &str, t: (&str, i32)) {
assert!(false);
}
@@ -0,0 +1,51 @@
use rstest::*;
struct A;
struct B;
#[derive(Debug)]
struct D;
#[fixture]
fn fu32() -> u32 {
42
}
#[fixture]
fn fb() -> B {
B {}
}
#[fixture]
fn fd() -> D {
D {}
}
#[fixture]
fn fa() -> A {
A {}
}
#[rstest]
#[trace]
fn simple(fu32: u32, #[notrace] fa: A, #[notrace] fb: B, fd: D) {
assert!(false);
}
#[rstest]
#[trace]
#[case(A{}, B{}, D{})]
fn cases(fu32: u32, #[case] #[notrace] a: A, #[case] #[notrace] b: B, #[case] d: D) {
assert!(false);
}
#[rstest]
#[trace]
fn matrix(
fu32: u32,
#[notrace]
#[values(A{})]
a: A,
#[notrace]
#[values(B{})]
b: B,
#[values(D{}) ] dd: D,
) {
assert!(false);
}
@@ -0,0 +1,41 @@
use rstest::*;
struct A;
struct B;
#[derive(Debug)]
struct D;
#[fixture]
fn fu32() -> u32 { 42 }
#[fixture]
fn fb() -> B { B {} }
#[fixture]
fn fd() -> D { D {} }
#[fixture]
fn fa() -> A { A {} }
#[rstest(
::trace::notrace(fa,fb))
]
fn simple(fu32: u32, fa: A, fb: B, fd: D) {
assert!(false);
}
#[rstest(a,b,d,
case(A{}, B{}, D{})
::trace::notrace(a,b))
]
fn cases(fu32: u32, a: A, b: B, d: D) {
assert!(false);
}
#[rstest(
a => [A{}],
b => [B{}],
dd => [D{}],
::trace::notrace(a,b))
]
fn matrix(fu32: u32, a: A, b: B, dd: D) {
assert!(false);
}
@@ -0,0 +1,21 @@
struct S;
#[rustfmt::skip] mod _skip_format {
use rstest::*; use super::*;
#[fixture]
fn fixture() -> S { S {} }
#[rstest]
#[trace]
fn single(fixture: S) {}
#[rstest(s)]
#[trace]
#[case(S{})]
fn cases(s: S) {}
#[rstest(
s => [S{}])]
#[trace]
fn matrix(s: S) {}
}
@@ -0,0 +1,21 @@
struct S;
#[rustfmt::skip] mod _skip_format {
use rstest::*; use super::*;
#[fixture]
fn fixture() -> S { S {} }
#[rstest(
::trace)]
fn single(fixture: S) {}
#[rstest(s,
case(S{})
::trace)]
fn cases(s: S) {}
#[rstest(
s => [S{}]
::trace)]
fn matrix(s: S) {}
}
@@ -0,0 +1,126 @@
use rstest::*;
#[fixture]
pub fn fixture() -> u32 {
42
}
#[rstest(f, case(42))]
fn error_inner(f: i32) {
let a: u32 = "";
}
#[rstest(f, case(42))]
fn error_cannot_resolve_fixture(no_fixture: u32, f: u32) {}
#[rstest(f, case(42))]
fn error_fixture_wrong_type(fixture: String, f: u32) {}
#[rstest(f, case(42))]
fn error_case_wrong_type(f: &str) {}
#[rstest(condition,
case(vec![1,2,3].contains(2)))
]
fn error_in_arbitrary_rust_code_cases(condition: bool) {
assert!(condition)
}
#[rstest(f, case(42), not_a_fixture(24))]
fn error_inject_an_invalid_fixture(f: u32) {}
#[fixture]
fn n() -> u32 {
24
}
#[fixture]
fn f(n: u32) -> u32 {
2 * n
}
#[rstest(f, f(42), case(12))]
fn error_inject_a_fixture_that_is_already_a_case(f: u32) {}
#[rstest(f(42), f, case(12))]
fn error_define_case_that_is_already_an_injected_fixture(f: u32) {}
#[rstest(v, f(42), f(42), case(12))]
fn error_inject_a_fixture_more_than_once(v: u32, f: u32) {}
#[rstest(f => [42])]
fn error_matrix_wrong_type(f: &str) {}
#[rstest(condition => [vec![1,2,3].contains(2)] )]
fn error_arbitrary_rust_code_matrix(condition: bool) {
assert!(condition)
}
#[rstest(empty => [])]
fn error_empty_list(empty: &str) {}
#[rstest(not_exist_1 => [42],
not_exist_2 => [42])]
fn error_no_match_args() {}
#[rstest(f => [41, 42], f(42))]
fn error_inject_a_fixture_that_is_already_a_value_list(f: u32) {}
#[rstest(f(42), f => [41, 42])]
fn error_define_a_value_list_that_is_already_an_injected_fixture(f: u32) {}
#[rstest(a, case(42), a => [42])]
fn error_define_a_value_list_that_is_already_a_case_arg(a: u32) {}
#[rstest(a => [42], a, case(42))]
fn error_define_a_case_arg_that_is_already_a_value_list(a: u32) {}
#[rstest(a => [42, 24], a => [24, 42])]
fn error_define_a_value_list_that_is_already_a_value_list(f: u32) {}
#[rstest(a, a, case(42))]
fn error_define_a_case_arg_that_is_already_a_case_arg(a: u32) {}
struct S;
#[rstest]
#[case("donald duck")]
fn error_convert_to_type_that_not_implement_from_str(#[case] s: S) {}
#[rstest]
#[case(async { "hello" } )]
async fn error_future_on_impl_type(
#[case]
#[future]
s: impl AsRef<str>,
) {
}
#[rstest]
#[case(async { 42 } )]
async fn error_future_more_than_once(
#[case]
#[future]
#[future]
a: i32,
) {
}
#[rstest]
#[timeout]
fn error_timeout_without_arg() {}
#[rstest]
#[timeout(some -> strange -> invalid -> expression)]
fn error_timeout_without_expression_arg() {}
#[rstest]
#[timeout(42)]
fn error_timeout_without_duration() {}
#[rstest]
fn error_absolute_path_files(#[files("/tmp/tmp.Q81idVZYAV/*.txt")] path: std::path::PathBuf) {}
struct T(u32, u32);
#[rstest]
#[case(T(3, 4))]
fn wrong_destruct_fixture(T(a, b): T, #[with(42)] T(c, d): T) {}
@@ -0,0 +1,142 @@
use rstest::*;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
#[rstest]
fn start_with_name(
#[files("files/**/*.txt")]
#[exclude("exclude")]
#[files("../files_test_sub_folder/**/*.txt")]
path: PathBuf,
) {
let name = path.file_name().unwrap();
let mut f = File::open(&path).unwrap();
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
assert!(contents.starts_with(name.to_str().unwrap()))
}
#[rstest]
fn start_with_name_file_mode(
#[files("files/**/*.txt")]
#[exclude("exclude")]
#[files("../files_test_sub_folder/**/*.txt")]
#[mode = path]
path: PathBuf,
) {
let name = path.file_name().unwrap();
let mut f = File::open(&path).unwrap();
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
assert!(contents.starts_with(name.to_str().unwrap()))
}
#[rstest]
fn start_with_name_with_include(
#[files("files/**/*.txt")]
#[exclude("exclude")]
#[include_dot_files]
path: PathBuf,
) {
let name = path.file_name().unwrap();
let mut f = File::open(&path).unwrap();
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
assert!(contents.starts_with(name.to_str().unwrap()))
}
#[rstest]
fn ignore_missing_env_vars(
#[ignore_missing_env_vars]
#[files("files/**/${__SHOULD_NOT_BE_DECLARED__}*.txt")]
#[exclude("exclude")]
path: PathBuf,
) {
let name = path.file_name().unwrap();
let mut f = File::open(&path).unwrap();
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
assert!(contents.starts_with(name.to_str().unwrap()))
}
#[rstest]
fn env_vars(
#[files("${FILES_ENV_VAR}/**/*.txt")]
#[exclude("exclude")]
path: PathBuf,
) {
let name = path.file_name().unwrap();
let mut f = File::open(&path).unwrap();
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
assert!(contents.starts_with(name.to_str().unwrap()))
}
#[rstest]
fn env_vars_unknown(
#[files("${__UNKNOWN__:-files}/**/*.txt")]
#[exclude("exclude")]
path: PathBuf,
) {
let name = path.file_name().unwrap();
let mut f = File::open(&path).unwrap();
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
assert!(contents.starts_with(name.to_str().unwrap()))
}
#[rstest]
fn env_vars_base_dir(
#[files("**/*.txt")]
#[base_dir = "files"]
#[exclude("exclude")]
path: PathBuf,
) {
let name = path.file_name().unwrap();
let mut f = File::open(&path).unwrap();
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
assert!(contents.starts_with(name.to_str().unwrap()))
}
#[rstest]
fn include_str(
#[files("files/**/*.txt")]
#[exclude("exclude")]
#[files("../files_test_sub_folder/**/*.txt")]
#[mode = str]
contents: &str,
) {
assert!(contents.len() != 0)
}
#[rstest]
fn include_bytes(
#[files("files/**/*.txt")]
#[exclude("exclude")]
#[files("../files_test_sub_folder/**/*.txt")]
#[mode = bytes]
contents: &[u8],
) {
assert!(contents.len() != 0)
}
mod module {
#[rstest::rstest]
fn pathbuf_need_not_be_in_scope(
#[files("files/**/*.txt")]
#[exclude("exclude")]
#[include_dot_files]
path: std::path::PathBuf,
) {
let _ = path;
}
}
@@ -0,0 +1,18 @@
use rstest::*;
#[fixture]
fn fixture() -> String { "str".to_owned() }
#[rstest]
fn simple<S: AsRef<str>>(fixture: S) {
assert_eq!(3, fixture.as_ref().len());
}
#[rstest(
expected, input,
case(4, String::from("ciao")),
case(3, "Foo")
)]
fn strlen_test<S: AsRef<str>>(expected: usize, input: S) {
assert_eq!(expected, input.as_ref().len());
}
@@ -0,0 +1,35 @@
use rstest::*;
#[fixture]
fn inject() -> u32 {
0
}
#[fixture]
fn ex() -> u32 {
42
}
#[fixture]
fn fix(inject: u32, ex: u32) -> bool {
(inject * 2) == ex
}
#[rstest(
fix(21),
a,
case(21, 2),
expected => [4, 2*3-2],
)]
#[case::second(14, 3)]
fn happy(
fix: bool,
a: u32,
#[case] b: u32,
expected: usize,
#[values("ciao", "buzz")] input: &str,
) {
assert!(fix);
assert_eq!(a * b, 42);
assert_eq!(expected, input.len());
}
@@ -0,0 +1,6 @@
use rstest::*;
#[rstest]
#[case(42, 2)]
#[case(43, 3)]
fn test(#[case] _ignore1: u32, #[case] _ignore2: u32, #[values(1, 2, 3, 4)] _ignore3: u32) {}
@@ -0,0 +1,16 @@
use rstest::*;
use sqlx::SqlitePool;
struct FixtureStruct {}
#[fixture]
fn my_fixture() -> FixtureStruct {
FixtureStruct {}
}
#[rstest]
#[sqlx::test]
async fn test_db(my_fixture: FixtureStruct, #[ignore] pool: SqlitePool) {
assert!(true);
}
@@ -0,0 +1,18 @@
use rstest::*;
#[fixture]
fn fixture() -> String { "str".to_owned() }
#[rstest]
fn simple(fixture: impl AsRef<str>) {
assert_eq!(3, fixture.as_ref().len());
}
#[rstest(
expected, input,
case(4, String::from("ciao")),
case(3, "Foo")
)]
fn strlen_test(expected: usize, input: impl AsRef<str>) {
assert_eq!(expected, input.as_ref().len());
}
@@ -0,0 +1,21 @@
use rstest::*;
enum E<'a> {
A(bool),
B(&'a std::cell::Cell<E<'a>>),
}
#[rstest]
#[case(E::A(true))]
fn case<'a>(#[case] e: E<'a>) {}
#[rstest]
fn values<'a>(#[values(E::A(true))] e: E<'a>) {}
#[fixture]
fn e<'a>() -> E<'a> {
E::A(true)
}
#[rstest]
fn fixture<'a>(e: E<'a>) {}
@@ -0,0 +1,28 @@
use std::cell::Cell;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum E<'a> {
A(bool),
B(&'a Cell<E<'a>>),
}
fn make_e_from_bool<'a>(_bump: &'a (), b: bool) -> E<'a> {
E::A(b)
}
#[cfg(test)]
mod tests {
use rstest::*;
use super::*;
#[fixture]
fn bump() -> () {}
#[rstest]
#[case(true, E::A(true))]
fn it_works<'a>(#[by_ref] bump: &'a (), #[case] b: bool, #[case] expected: E<'a>) {
let actual = make_e_from_bool(&bump, b);
assert_eq!(actual, expected);
}
}
@@ -0,0 +1,12 @@
use rstest::*;
#[rstest]
async fn my_async_test(
#[future]
#[values(async { 1 }, async { 2 })]
first: u32,
#[values(42, 21)]
second: u32
) {
assert_eq!(42, first.await * second);
}
@@ -0,0 +1,12 @@
use rstest::*;
#[rstest]
async fn my_async_test(
#[future(awt)]
#[values(async { 1 }, async { 2 })]
first: u32,
#[values(42, 21)]
second: u32
) {
assert_eq!(42, first * second);
}
@@ -0,0 +1,13 @@
use rstest::*;
#[rstest]
#[awt]
async fn my_async_test(
#[future]
#[values(async { 1 }, async { 2 })]
first: u32,
#[values(42, 21)]
second: u32
) {
assert_eq!(42, first * second);
}
@@ -0,0 +1,17 @@
use rstest::*;
use actix_rt;
use std::future::Future;
#[rstest(
first => [1, 2],
second => [2, 1],
)]
#[test]
fn sync(first: u32, second: u32) { assert_eq!(2, first * second); }
#[rstest(
first => [async { 1 }, async { 2 }],
second => [2, 1],
)]
#[actix_rt::test]
async fn fn_async(first: impl Future<Output=u32>, second: u32) { assert_eq!(2, first.await * second); }
@@ -0,0 +1,54 @@
use rstest::*;
#[fixture]
fn f1() -> u32 {
0
}
#[fixture]
fn f2() -> u32 {
0
}
#[fixture]
fn f3() -> u32 {
0
}
#[fixture]
fn fixture(f1: u32, f2: u32, f3: u32) -> u32 {
f1 + f2 + 2 * f3
}
#[rstest(a => [0, 1], b => [0, 2])]
fn default(fixture: u32, a: u32, b: u32) {
assert_eq!(fixture, a * b);
}
#[rstest(a => [0, 1], b => [0, 2], fixture(1))]
fn partial_1(fixture: u32, a: u32, b: u32) {
assert_eq!(fixture, a * b);
}
#[rstest(a => [0, 1], b => [0, 2])]
fn partial_attr_1(#[with(1)] fixture: u32, a: u32, b: u32) {
assert_eq!(fixture, a * b);
}
#[rstest(a => [0, 1], b => [0, 2], fixture(0, 2))]
fn partial_2(fixture: u32, a: u32, b: u32) {
assert_eq!(fixture, a * b);
}
#[rstest(a => [0, 1], b => [0, 2])]
fn partial_attr_2(#[with(0, 2)] fixture: u32, a: u32, b: u32) {
assert_eq!(fixture, a * b);
}
#[rstest(a => [0, 1], b => [0, 2], fixture(0, 0, 1))]
fn complete(fixture: u32, a: u32, b: u32) {
assert_eq!(fixture, a * b);
}
#[rstest(a => [0, 1], b => [0, 2])]
fn complete_attr(#[with(0, 0, 1)] fixture: u32, a: u32, b: u32) {
assert_eq!(fixture, a * b);
}
@@ -0,0 +1,9 @@
use rstest::rstest;
#[rstest(
expected => [4, 2*3-2],
input => ["ciao", "buzz"],
)]
fn strlen_test(expected: usize, input: &str) {
assert_eq!(expected, input.len());
}
@@ -0,0 +1,20 @@
use rstest::rstest;
#[rstest]
fn both(#[values(4, 2*3-2)] expected: usize, #[values("ciao", "buzz")] input: &str) {
assert_eq!(expected, input.len());
}
#[rstest(
input => ["ciao", "buzz"]
)]
fn first(#[values(4, 2*3-2)] expected: usize, input: &str) {
assert_eq!(expected, input.len());
}
#[rstest(
expected => [4, 2*3-2]
)]
fn second(expected: usize, #[values("ciao", "buzz")] input: &str) {
assert_eq!(expected, input.len());
}
@@ -0,0 +1,29 @@
use rstest::*;
#[fixture]
pub fn fixture() -> u32 { 42 }
#[rstest]
fn should_success(mut fixture: u32) {
fixture += 1;
assert_eq!(fixture, 43);
}
#[rstest]
fn should_fail(mut fixture: u32) {
fixture += 1;
assert_ne!(fixture, 43);
}
#[rstest(
expected, val,
case(45, 1),
case(46, 2),
case(47, 2)
)]
fn add_test(mut fixture: u32, expected: u32, mut val: u32) {
fixture += 1;
val += fixture + 1;
assert_eq!(expected, val);
}
@@ -0,0 +1,33 @@
#![no_std]
use rstest::{fixture, rstest};
#[fixture]
#[once]
fn once_fixture() -> u64 {
42
}
#[fixture]
#[once]
fn empty_once_fixture() {}
#[fixture]
async fn async_fixture() -> u64 {
42
}
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
#[rstest]
#[case("2", "2", "4")]
fn it_works(#[case] left: u64, #[case] right: u64, #[case] expected: u64) {
assert_eq!(add(left, right), expected);
}
#[rstest]
async fn async_works(#[future] async_fixture: u64) {
assert_eq!(42, async_fixture.await);
}
@@ -0,0 +1,27 @@
use rstest::*;
#[fixture]
pub fn fixture() -> u32 { 42 }
#[rstest]
#[should_panic]
fn should_success(fixture: u32) {
assert_ne!(fixture, 42);
}
#[rstest]
#[should_panic]
fn should_fail(fixture: u32) {
assert_eq!(fixture, 42);
}
#[rstest(
expected, input,
case(4, 5),
case(3, 2),
case(3, 3)
)]
#[should_panic]
fn fail(expected: i32, input: i32) {
assert_eq!(expected, input);
}
@@ -0,0 +1,11 @@
use rstest::rstest;
#[rstest]
struct Foo;
#[rstest]
impl Foo {}
#[rstest]
mod mod_baz {}
@@ -0,0 +1,9 @@
use rstest::*;
#[fixture]
fn can_be_ignored() {}
#[rstest]
fn ignore_input(_can_be_ignored: ()) {
assert!(true);
}
@@ -0,0 +1,49 @@
use rstest::*;
#[fixture]
fn very_long_and_boring_name(#[default(42)] inject: u32) -> u32 {
inject
}
mod sub_module {
use super::*;
#[fixture]
pub fn mod_fixture() -> u32 {
42
}
}
#[rstest(very_long_and_boring_name as foo)]
fn compact(foo: u32) {
assert!(42 == foo);
}
#[rstest(sub_module::mod_fixture as foo)]
fn compact_mod(foo: u32) {
assert!(42 == foo);
}
#[rstest(very_long_and_boring_name(21) as foo)]
fn compact_injected(foo: u32) {
assert!(21 == foo);
}
#[rstest]
fn attribute(#[from(very_long_and_boring_name)] foo: u32) {
assert!(42 == foo);
}
#[rstest]
fn attribute_mod(#[from(sub_module::mod_fixture)] foo: u32) {
assert!(42 == foo);
}
#[rstest]
fn attribute_injected(
#[from(very_long_and_boring_name)]
#[with(21)]
foo: u32,
) {
assert!(21 == foo);
}
@@ -0,0 +1,19 @@
use rstest::rstest;
#[rstest]
fn should_success() -> Result<(), &'static str> {
Ok(())
}
#[rstest]
fn should_fail() -> Result<(), &'static str> {
Err("Return Error")
}
#[rstest(ret,
case::should_success(Ok(())),
case::should_fail(Err("Return Error"))
)]
fn return_type(ret: Result<(), &'static str>) -> Result<(), &'static str> {
ret
}
@@ -0,0 +1,26 @@
use rstest::*;
#[fixture]
async fn fixture() -> u32 { 42 }
#[rstest]
async fn should_pass(#[future] fixture: u32) {
assert_eq!(fixture.await, 42);
}
#[rstest]
async fn should_fail(#[future] fixture: u32) {
assert_ne!(fixture.await, 42);
}
#[rstest]
#[should_panic]
async fn should_panic_pass(#[future] fixture: u32) {
panic!(format!("My panic -> fixture = {}", fixture.await));
}
#[rstest]
#[should_panic]
async fn should_panic_fail(#[future] fixture: u32) {
assert_eq!(fixture.await, 42);
}
@@ -0,0 +1,28 @@
use rstest::*;
#[fixture]
async fn fixture() -> u32 {
42
}
#[rstest]
async fn should_pass(#[future(awt)] fixture: u32) {
assert_eq!(fixture, 42);
}
#[rstest]
async fn should_fail(#[future(awt)] fixture: u32) {
assert_ne!(fixture, 42);
}
#[rstest]
#[should_panic]
async fn should_panic_pass(#[future(awt)] fixture: u32) {
panic!(format!("My panic -> fixture = {}", fixture));
}
#[rstest]
#[should_panic]
async fn should_panic_fail(#[future(awt)] fixture: u32) {
assert_eq!(fixture, 42);
}
@@ -0,0 +1,32 @@
use rstest::*;
#[fixture]
async fn fixture() -> u32 {
42
}
#[rstest]
#[awt]
async fn should_pass(#[future] fixture: u32) {
assert_eq!(fixture, 42);
}
#[rstest]
#[awt]
async fn should_fail(#[future] fixture: u32) {
assert_ne!(fixture, 42);
}
#[rstest]
#[awt]
#[should_panic]
async fn should_panic_pass(#[future] fixture: u32) {
panic!(format!("My panic -> fixture = {}", fixture));
}
#[rstest]
#[awt]
#[should_panic]
async fn should_panic_fail(#[future] fixture: u32) {
assert_eq!(fixture, 42);
}
@@ -0,0 +1,16 @@
use rstest::*;
#[derive(Debug)]
struct A {}
#[fixture]
fn fu32() -> u32 { 42 }
#[fixture]
fn fstring() -> String { "A String".to_string() }
#[fixture]
fn ftuple() -> (A, String, i32) { (A{}, "A String".to_string(), -12) }
#[rstest(::trace)]
fn should_fail(fu32: u32, fstring: String, ftuple: (A, String, i32)) {
assert!(false);
}
@@ -0,0 +1,41 @@
use rstest::*;
use actix_rt;
#[fixture]
fn a() -> u32 {
42
}
#[rstest]
#[test]
fn sync_case(a: u32) {}
#[rstest]
#[test]
#[should_panic]
fn sync_case_panic(a: u32) { panic!("panic") }
#[rstest]
#[test]
fn sync_case_fail(a: u32) { assert_eq!(2, a); }
#[rstest]
#[test]
fn sync_case_panic_fail(a: u32) { panic!("panic") }
#[rstest]
#[actix_rt::test]
async fn async_case(a: u32) {}
#[rstest]
#[actix_rt::test]
async fn async_case_fail(a: u32) { assert_eq!(2, a); }
#[rstest]
#[actix_rt::test]
#[should_panic]
async fn async_case_panic(a: u32) { panic!("panic") }
#[rstest]
#[actix_rt::test]
async fn async_case_panic_fail(a: u32) { panic!("panic") }
@@ -0,0 +1,54 @@
use rstest::*;
#[fixture]
fn f1() -> u32 {
0
}
#[fixture]
fn f2() -> u32 {
0
}
#[fixture]
fn f3() -> u32 {
0
}
#[fixture]
fn fixture(f1: u32, f2: u32, f3: u32) -> u32 {
f1 + 10 * f2 + 100 * f3
}
#[rstest]
fn default(fixture: u32) {
assert_eq!(fixture, 0);
}
#[rstest(fixture(7))]
fn partial_1(fixture: u32) {
assert_eq!(fixture, 7);
}
#[rstest]
fn partial_attr_1(#[with(7)] fixture: u32) {
assert_eq!(fixture, 7);
}
#[rstest(fixture(2, 4))]
fn partial_2(fixture: u32) {
assert_eq!(fixture, 42);
}
#[rstest]
fn partial_attr_2(#[with(2, 4)] fixture: u32) {
assert_eq!(fixture, 42);
}
#[rstest(fixture(2, 4, 5))]
fn complete(fixture: u32) {
assert_eq!(fixture, 542);
}
#[rstest]
fn complete_attr(#[with(2, 4, 5)] fixture: u32) {
assert_eq!(fixture, 542);
}
@@ -0,0 +1,32 @@
use rstest::*;
pub trait Tr {
fn get() -> Self;
}
impl Tr for i32 {
fn get() -> Self {
42
}
}
impl Tr for u32 {
fn get() -> Self {
42
}
}
#[fixture]
pub fn fgen<T: Tr>() -> T {
T::get()
}
#[rstest]
fn generics_u32(fgen: u32) {
assert_eq!(fgen, 42u32);
}
#[rstest]
fn generics_i32(fgen: i32) {
assert_eq!(fgen, 42i32);
}
@@ -0,0 +1,14 @@
use rstest::*;
#[fixture]
pub fn fixture() -> u32 { 42 }
#[rstest]
fn should_success(fixture: u32) {
assert_eq!(fixture, 42);
}
#[rstest]
fn should_fail(fixture: u32) {
assert_ne!(fixture, 42);
}
@@ -0,0 +1,232 @@
use rstest::*;
use std::time::Duration;
fn ms(ms: u32) -> Duration {
Duration::from_millis(ms.into())
}
mod thread {
use super::*;
fn delayed_sum(a: u32, b: u32, delay: Duration) -> u32 {
std::thread::sleep(delay);
a + b
}
#[rstest]
#[timeout(ms(80))]
fn single_pass() {
assert_eq!(4, delayed_sum(2, 2, ms(10)));
}
#[rstest]
#[timeout(ms(100))]
fn single_fail_value() {
assert_eq!(5, delayed_sum(2, 2, ms(1)));
}
#[rstest]
#[timeout(ms(1000))]
#[should_panic = "user message"]
fn fail_with_user_message() {
panic!("user message");
}
#[rstest]
#[timeout(ms(10))]
fn single_fail_timeout() {
assert_eq!(4, delayed_sum(2, 2, ms(80)));
}
#[rstest]
#[timeout(ms(80))]
#[case(ms(10))]
fn one_pass(#[case] delay: Duration) {
assert_eq!(4, delayed_sum(2, 2, delay));
}
#[rstest]
#[timeout(ms(10))]
#[case(ms(80))]
fn one_fail_timeout(#[case] delay: Duration) {
assert_eq!(4, delayed_sum(2, 2, delay));
}
#[rstest]
#[timeout(ms(100))]
#[case(ms(1))]
fn one_fail_value(#[case] delay: Duration) {
assert_eq!(5, delayed_sum(2, 2, delay));
}
#[rstest]
#[case::pass(ms(1), 4)]
#[case::fail_timeout(ms(80), 4)]
#[case::fail_value(ms(1), 5)]
#[timeout(ms(40))]
fn group_same_timeout(#[case] delay: Duration, #[case] expected: u32) {
assert_eq!(expected, delayed_sum(2, 2, delay));
}
#[rstest]
#[timeout(ms(100))]
#[case::pass(ms(1), 4)]
#[timeout(ms(30))]
#[case::fail_timeout(ms(70), 4)]
#[timeout(ms(100))]
#[case::fail_value(ms(1), 5)]
fn group_single_timeout(#[case] delay: Duration, #[case] expected: u32) {
assert_eq!(expected, delayed_sum(2, 2, delay));
}
#[rstest]
#[case::pass(ms(1), 4)]
#[timeout(ms(10))]
#[case::fail_timeout(ms(60), 4)]
#[case::fail_value(ms(1), 5)]
#[timeout(ms(100))]
fn group_one_timeout_override(#[case] delay: Duration, #[case] expected: u32) {
assert_eq!(expected, delayed_sum(2, 2, delay));
}
struct S {}
#[rstest]
#[case(S{})]
fn compile_with_no_copy_arg(#[case] _s: S) {
assert!(true);
}
#[fixture]
fn no_copy() -> S {
S {}
}
#[rstest]
fn compile_with_no_copy_fixture(no_copy: S) {
assert!(true);
}
#[rstest]
fn default_timeout_failure() {
assert_eq!(4, delayed_sum(2, 2, ms(1100)));
}
}
mod async_std_cases {
use super::*;
async fn delayed_sum(a: u32, b: u32, delay: Duration) -> u32 {
async_std::task::sleep(delay).await;
a + b
}
#[rstest]
#[timeout(ms(80))]
async fn single_pass() {
assert_eq!(4, delayed_sum(2, 2, ms(10)).await);
}
#[rstest]
#[timeout(ms(10))]
async fn single_fail_timeout() {
assert_eq!(4, delayed_sum(2, 2, ms(80)).await);
}
#[rstest]
#[timeout(ms(100))]
async fn single_fail_value() {
assert_eq!(5, delayed_sum(2, 2, ms(1)).await);
}
#[rstest]
#[timeout(ms(1000))]
#[should_panic = "user message"]
async fn fail_with_user_message() {
panic! {"user message"};
}
#[rstest]
#[timeout(ms(80))]
#[case(ms(10))]
async fn one_pass(#[case] delay: Duration) {
assert_eq!(4, delayed_sum(2, 2, delay).await);
}
#[rstest]
#[timeout(ms(10))]
#[case(ms(80))]
async fn one_fail_timeout(#[case] delay: Duration) {
assert_eq!(4, delayed_sum(2, 2, delay).await);
}
#[rstest]
#[timeout(ms(100))]
#[case(ms(1))]
async fn one_fail_value(#[case] delay: Duration) {
assert_eq!(5, delayed_sum(2, 2, delay).await);
}
#[rstest]
#[case::pass(ms(1), 4)]
#[case::fail_timeout(ms(80), 4)]
#[case::fail_value(ms(1), 5)]
#[timeout(ms(40))]
async fn group_same_timeout(#[case] delay: Duration, #[case] expected: u32) {
assert_eq!(expected, delayed_sum(2, 2, delay).await);
}
#[rstest]
#[timeout(ms(100))]
#[case::pass(ms(1), 4)]
#[timeout(ms(30))]
#[case::fail_timeout(ms(70), 4)]
#[timeout(ms(100))]
#[case::fail_value(ms(1), 5)]
async fn group_single_timeout(#[case] delay: Duration, #[case] expected: u32) {
assert_eq!(expected, delayed_sum(2, 2, delay).await);
}
#[rstest]
#[case::pass(ms(1), 4)]
#[timeout(ms(10))]
#[case::fail_timeout(ms(60), 4)]
#[case::fail_value(ms(1), 5)]
#[timeout(ms(100))]
async fn group_one_timeout_override(#[case] delay: Duration, #[case] expected: u32) {
assert_eq!(expected, delayed_sum(2, 2, delay).await);
}
struct S {}
#[rstest]
#[case(S{})]
async fn compile_with_no_copy_arg(#[case] _s: S) {
assert!(true);
}
#[fixture]
fn no_copy() -> S {
S {}
}
#[rstest]
fn compile_with_no_copy_fixture(_no_copy: S) {
assert!(true);
}
#[fixture]
async fn a_fix() -> S {
S {}
}
#[rstest]
fn compile_with_async_fixture(#[future] a_fix: S) {
assert!(true);
}
#[rstest]
async fn compile_with_async_awt_fixture(#[future(awt)] a_fix: S) {
assert!(true);
}
}
@@ -0,0 +1,17 @@
use rstest::*;
use std::time::Duration;
fn ms(ms: u32) -> Duration {
Duration::from_millis(ms.into())
}
async fn delayed_sum(a: u32, b: u32,delay: Duration) -> u32 {
async_std::task::sleep(delay).await;
a + b
}
#[rstest]
#[timeout(ms(80))]
async fn single_pass() {
assert_eq!(4, delayed_sum(2, 2, ms(10)).await);
}
@@ -0,0 +1,114 @@
use other_name::*;
use std::time::Duration;
fn ms(ms: u32) -> Duration {
Duration::from_millis(ms.into())
}
mod thread {
use super::*;
fn delayed_sum(a: u32, b: u32, delay: Duration) -> u32 {
std::thread::sleep(delay);
a + b
}
#[rstest]
#[timeout(ms(80))]
fn single_pass() {
assert_eq!(4, delayed_sum(2, 2, ms(10)));
}
#[rstest]
#[timeout(ms(100))]
fn single_fail_value() {
assert_eq!(5, delayed_sum(2, 2, ms(1)));
}
#[rstest]
#[timeout(ms(1000))]
#[should_panic = "user message"]
fn fail_with_user_message() {
panic!("user message");
}
#[rstest]
#[timeout(ms(10))]
fn single_fail_timeout() {
assert_eq!(4, delayed_sum(2, 2, ms(80)));
}
#[rstest]
#[timeout(ms(80))]
#[case(ms(10))]
fn one_pass(#[case] delay: Duration) {
assert_eq!(4, delayed_sum(2, 2, delay));
}
#[rstest]
#[timeout(ms(10))]
#[case(ms(80))]
fn one_fail_timeout(#[case] delay: Duration) {
assert_eq!(4, delayed_sum(2, 2, delay));
}
#[rstest]
#[timeout(ms(100))]
#[case(ms(1))]
fn one_fail_value(#[case] delay: Duration) {
assert_eq!(5, delayed_sum(2, 2, delay));
}
#[rstest]
#[case::pass(ms(1), 4)]
#[case::fail_timeout(ms(80), 4)]
#[case::fail_value(ms(1), 5)]
#[timeout(ms(40))]
fn group_same_timeout(#[case] delay: Duration, #[case] expected: u32) {
assert_eq!(expected, delayed_sum(2, 2, delay));
}
#[rstest]
#[timeout(ms(100))]
#[case::pass(ms(1), 4)]
#[timeout(ms(30))]
#[case::fail_timeout(ms(70), 4)]
#[timeout(ms(100))]
#[case::fail_value(ms(1), 5)]
fn group_single_timeout(#[case] delay: Duration, #[case] expected: u32) {
assert_eq!(expected, delayed_sum(2, 2, delay));
}
#[rstest]
#[case::pass(ms(1), 4)]
#[timeout(ms(10))]
#[case::fail_timeout(ms(60), 4)]
#[case::fail_value(ms(1), 5)]
#[timeout(ms(100))]
fn group_one_timeout_override(#[case] delay: Duration, #[case] expected: u32) {
assert_eq!(expected, delayed_sum(2, 2, delay));
}
struct S {}
#[rstest]
#[case(S{})]
fn compile_with_no_copy_arg(#[case] _s: S) {
assert!(true);
}
#[fixture]
fn no_copy() -> S {
S {}
}
#[rstest]
fn compile_with_no_copy_fixture(no_copy: S) {
assert!(true);
}
#[rstest]
fn default_timeout_failure() {
assert_eq!(4, delayed_sum(2, 2, ms(1100)));
}
}
@@ -0,0 +1,25 @@
use rstest::*;
#[fixture]
fn f() -> String {
"f".to_owned()
}
fn append(s: &mut String, a: &str) -> String {
s.push_str("-");
s.push_str(a);
s.clone()
}
#[rstest]
#[case(append(&mut f, "a"), "f-a", "f-a-b")]
fn use_mutate_fixture(
mut f: String,
#[case] a: String,
#[values(append(&mut f, "b"))] b: String,
#[case] expected_a: &str,
#[case] expected_b: &str,
) {
assert_eq!(expected_a, a);
assert_eq!(expected_b, b);
}
@@ -0,0 +1,21 @@
use rstest::*;
enum Application {
Python,
Node,
Go,
}
enum Method {
GET,
POST,
PUT,
HEAD,
}
#[rstest]
fn name_values(
#[values(Application::Python, Application::Node, Application::Go)] _val: Application,
#[values(Method::GET, Method::POST, Method::PUT, Method::HEAD)] _method: Method,
) {
}
File diff suppressed because it is too large Load Diff