3
3
//! This module provides some utilities for parsing test arguments as well as running and filtering
4
4
//! a sequence of tests.
5
5
//!
6
- //! > These utilities are completely independent of the runtime being used. To
7
- //! > get a more complete picture of how to run tests for PyO3 Asyncio applications, see
8
- //! > [`crate::async_std::testing`] or [`crate::tokio::testing`] for more details.
9
- //!
10
6
//! As mentioned [here](crate#pythons-event-loop), PyO3 Asyncio tests cannot use the default test
11
7
//! harness since it doesn't allow Python to gain control over the main thread. Instead, we have to
12
8
//! provide our own test harness in order to create integration tests.
9
+ //!
10
+ //! ### Main Test File
11
+ //! First, we need to create the test's main file. Although these tests are considered integration
12
+ //! tests, we cannot put them in the `tests` directory since that is a special directory owned by
13
+ //! Cargo. Instead, we put our tests in a `pytests` directory, although the name `pytests` is just
14
+ //! a convention.
15
+ //!
16
+ //! We'll also want to provide the test's main function. It's highly recommended that you use the
17
+ //! [`pyo3_asyncio::testing::test_main!`](pyo3_asyncio_macros::test_main) macro as it will take all of the tests marked with
18
+ //! [`#[pyo3_asyncio::tokio::test]`](crate::tokio::test) or
19
+ //! [`#[pyo3_asyncio::async_std::test]`](crate::async_std::test) and run them automatically.
20
+ //!
21
+ //! `pytests/test_example.rs` for the `tokio` runtime:
22
+ //! ```no_run
23
+ //! pyo3_asyncio::testing::test_main!(#[pyo3_asyncio::tokio::main], "Example Test Suite");
24
+ //! ```
25
+ //!
26
+ //! `pytests/test_example.rs` for the `async-std` runtime:
27
+ //! ```no_run
28
+ //! pyo3_asyncio::testing::test_main!(#[pyo3_asyncio::async_std::main], "Example Test Suite");
29
+ //! ```
30
+ //!
31
+ //! ### Cargo Configuration
32
+ //! Next, we need to add our test file to the Cargo manifest. Add the following section to your
33
+ //! `Cargo.toml`
34
+ //!
35
+ //! ```toml
36
+ //! [[test]]
37
+ //! name = "test_example"
38
+ //! path = "pytests/test_example.rs"
39
+ //! harness = false
40
+ //! ```
41
+ //!
42
+ //! Also, add the `testing` feature to `pyo3-asyncio` and select your preferred runtime:
43
+ //! ```toml
44
+ //! [dependencies]
45
+ //! pyo3-asyncio = { version = "0.13", features = ["testing", "async-std-runtime"] }
46
+ //! ```
47
+ //!
48
+ //! In order for the `test_main!` macro to find your tests, you'll also need to add an extra
49
+ //! `dev-dependency` for [`inventory`](https://github.com/dtolnay/inventory):
50
+ //! ```toml
51
+ //! [dev-dependencies]
52
+ //! inventory = "0.1"
53
+ //! ```
54
+ //!
55
+ //! At this point you should be able to run the test via `cargo test`
56
+ //!
57
+ //! ### Adding Tests to the PyO3 Asyncio Test Harness
58
+ //!
59
+ //! For `async-std` use the [`pyo3_asyncio::async_std::test`](crate::async_std::test) attribute:
60
+ //! ```ignore
61
+ //! use std::{time::Duration, thread};
62
+ //!
63
+ //! use pyo3::prelude::*;
64
+ //!
65
+ //! #[pyo3_asyncio::async_std::test]
66
+ //! async fn test_async_sleep() -> PyResult<()> {
67
+ //! async_std::task::sleep(Duration::from_secs(1)).await;
68
+ //! Ok(())
69
+ //! }
70
+ //!
71
+ //! #[pyo3_asyncio::async_std::test]
72
+ //! fn test_blocking_sleep() -> PyResult<()> {
73
+ //! thread::sleep(Duration::from_secs(1));
74
+ //! Ok(())
75
+ //! }
76
+ //!
77
+ //! pyo3_asyncio::testing::test_main!(#[pyo3_asyncio::async_std::main], "Example Test Suite");
78
+ //! ```
79
+ //!
80
+ //! For `tokio` use the [`pyo3_asyncio::tokio::test`](crate::tokio::test) attribute:
81
+ //! ```ignore
82
+ //! use std::{time::Duration, thread};
83
+ //!
84
+ //! use pyo3::prelude::*;
85
+ //!
86
+ //! #[pyo3_asyncio::tokio::test]
87
+ //! async fn test_async_sleep() -> PyResult<()> {
88
+ //! tokio::time::sleep(Duration::from_secs(1)).await;
89
+ //! Ok(())
90
+ //! }
91
+ //!
92
+ //! #[pyo3_asyncio::tokio::test]
93
+ //! fn test_blocking_sleep() -> PyResult<()> {
94
+ //! thread::sleep(Duration::from_secs(1));
95
+ //! Ok(())
96
+ //! }
97
+ //!
98
+ //! pyo3_asyncio::testing::test_main!(#[pyo3_asyncio::tokio::main], "Example Test Suite");
99
+ //! ```
13
100
14
101
use std:: { future:: Future , pin:: Pin } ;
15
102
@@ -80,47 +167,19 @@ pub fn parse_args(suite_name: &str) -> Args {
80
167
}
81
168
}
82
169
83
- /// Wrapper around a test function or future to be passed to the test harness
84
- pub struct Test {
85
- name : String ,
86
- task : Pin < Box < dyn Future < Output = PyResult < ( ) > > + Send > > ,
87
- }
88
-
89
170
/// Abstract Test Trait
90
171
///
91
172
/// This trait works in tandem with the pyo3-asyncio-macros to generate test objects that work with
92
173
/// the pyo3-asyncio test harness.
93
- pub trait TestTrait : Send {
174
+ pub trait Test : Send {
94
175
/// Get the name of the test
95
176
fn name ( & self ) -> & str ;
96
- /// Move into the task that runs the test
97
- fn task ( self ) -> Pin < Box < dyn Future < Output = PyResult < ( ) > > + Send > > ;
98
- }
99
-
100
- impl TestTrait for Test {
101
- fn name ( & self ) -> & str {
102
- self . name . as_str ( )
103
- }
104
- fn task ( self ) -> Pin < Box < dyn Future < Output = PyResult < ( ) > > + Send > > {
105
- self . task
106
- }
107
- }
108
-
109
- impl Test {
110
- /// Construct a test from a future
111
- pub fn new_async (
112
- name : String ,
113
- fut : impl Future < Output = PyResult < ( ) > > + Send + ' static ,
114
- ) -> Self {
115
- Self {
116
- name,
117
- task : Box :: pin ( fut) ,
118
- }
119
- }
177
+ /// Instantiate the task that runs the test
178
+ fn task ( & self ) -> Pin < Box < dyn Future < Output = PyResult < ( ) > > + Send > > ;
120
179
}
121
180
122
181
/// Run a sequence of tests while applying any necessary filtering from the `Args`
123
- pub async fn test_harness ( tests : Vec < impl TestTrait + ' static > , args : Args ) -> PyResult < ( ) > {
182
+ pub async fn test_harness ( tests : Vec < impl Test + ' static > , args : Args ) -> PyResult < ( ) > {
124
183
stream:: iter ( tests)
125
184
. for_each_concurrent ( Some ( 4 ) , |test| {
126
185
let mut ignore = false ;
@@ -133,10 +192,9 @@ pub async fn test_harness(tests: Vec<impl TestTrait + 'static>, args: Args) -> P
133
192
134
193
async move {
135
194
if !ignore {
136
- let name = test. name ( ) . to_string ( ) ;
137
195
test. task ( ) . await . unwrap ( ) ;
138
196
139
- println ! ( "test {} ... ok" , name) ;
197
+ println ! ( "test {} ... ok" , test . name( ) ) ;
140
198
}
141
199
}
142
200
} )
0 commit comments