Skip to content

Commit 21eb9c8

Browse files
author
Andrew J Westlake
committed
Removed obsoleted test functions, fixed documentation for testing utilities
1 parent 67d23bd commit 21eb9c8

File tree

5 files changed

+101
-323
lines changed

5 files changed

+101
-323
lines changed

pyo3-asyncio-macros/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,12 +259,12 @@ pub fn test_main(args: TokenStream) -> TokenStream {
259259
pub test_fn: &'static (dyn Fn() -> std::pin::Pin<Box<dyn std::future::Future<Output = pyo3::PyResult<()>> + Send>> + Send + Sync),
260260
}
261261

262-
impl pyo3_asyncio::testing::TestTrait for Test {
262+
impl pyo3_asyncio::testing::Test for Test {
263263
fn name(&self) -> &str {
264264
self.name.as_str()
265265
}
266266

267-
fn task(self) -> std::pin::Pin<Box<dyn std::future::Future<Output = pyo3::PyResult<()>> + Send>> {
267+
fn task(&self) -> std::pin::Pin<Box<dyn std::future::Future<Output = pyo3::PyResult<()>> + Send>> {
268268
(self.test_fn)()
269269
}
270270
}

src/async_std.rs

Lines changed: 2 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ use pyo3::prelude::*;
55

66
use crate::generic::{self, JoinError, Runtime};
77

8-
/// <span class="module-item stab portability" style="display: inline; border-radius: 3px; padding: 2px; font-size: 80%; line-height: 1.2;"><code>attributes</code></span> Sets up the async-std runtime and runs an async fn as main
8+
/// <span class="module-item stab portability" style="display: inline; border-radius: 3px; padding: 2px; font-size: 80%; line-height: 1.2;"><code>attributes</code></span> Sets up the `async-std` runtime and runs an async fn as main
99
#[cfg(feature = "attributes")]
1010
pub use pyo3_asyncio_macros::async_std_main as main;
1111

12+
/// <span class="module-item stab portability" style="display: inline; border-radius: 3px; padding: 2px; font-size: 80%; line-height: 1.2;"><code>attributes</code></span> Registers an `async-std` test with the `pyo3-asyncio` test harness
1213
#[cfg(feature = "attributes")]
1314
pub use pyo3_asyncio_macros::async_std_test as test;
1415

@@ -106,119 +107,3 @@ where
106107
{
107108
generic::into_coroutine::<AsyncStdRuntime, _>(py, fut)
108109
}
109-
110-
/// <span class="module-item stab portability" style="display: inline; border-radius: 3px; padding: 2px; font-size: 80%; line-height: 1.2;"><code>testing</code></span> Testing Utilities for the async-std runtime.
111-
#[cfg(feature = "testing")]
112-
pub mod testing {
113-
//! # PyO3 Asyncio Testing Utilities
114-
//!
115-
//! This module provides some utilities for parsing test arguments as well as running and filtering
116-
//! a sequence of tests.
117-
//!
118-
//! As mentioned [here](crate#pythons-event-loop), PyO3 Asyncio tests cannot use the default test
119-
//! harness since it doesn't allow Python to gain control over the main thread. Instead, we have to
120-
//! provide our own test harness in order to create integration tests.
121-
//!
122-
//! ## Creating A PyO3 Asyncio Integration Test
123-
//!
124-
//! ### Main Test File
125-
//! First, we need to create the test's main file. Although these tests are considered integration
126-
//! tests, we cannot put them in the `tests` directory since that is a special directory owned by
127-
//! Cargo. Instead, we put our tests in a `pytests` directory, although the name `pytests` is just
128-
//! a convention.
129-
//!
130-
//! `pytests/test_example.rs`
131-
//! ```no_run
132-
//! fn main() {
133-
//!
134-
//! }
135-
//! ```
136-
//!
137-
//! ### Test Manifest Entry
138-
//! Next, we need to add our test file to the Cargo manifest. Add the following section to your
139-
//! `Cargo.toml`
140-
//!
141-
//! ```toml
142-
//! [[test]]
143-
//! name = "test_example"
144-
//! path = "pytests/test_example.rs"
145-
//! harness = false
146-
//! ```
147-
//!
148-
//! At this point you should be able to run the test via `cargo test`
149-
//!
150-
//! ### Using the PyO3 Asyncio Test Harness
151-
//! Now that we've got our test registered with `cargo test`, we can start using the PyO3 Asyncio
152-
//! test harness.
153-
//!
154-
//! In your `Cargo.toml` add the testing feature to `pyo3-asyncio`:
155-
//! ```toml
156-
//! pyo3-asyncio = { version = "0.13", features = ["testing", "async-std-runtime"] }
157-
//! ```
158-
//!
159-
//! Now, in your test's main file, call [`crate::async_std::testing::test_main`]:
160-
//!
161-
//! ```no_run
162-
//! fn main() {
163-
//! pyo3_asyncio::async_std::testing::test_main(
164-
//! "Example Test Suite",
165-
//! Vec::<pyo3_asyncio::testing::Test>::new()
166-
//! );
167-
//! }
168-
//! ```
169-
//!
170-
//! ### Adding Tests to the PyO3 Asyncio Test Harness
171-
//!
172-
//! ```no_run
173-
//! use std::{time::Duration, thread};
174-
//!
175-
//! use pyo3_asyncio::testing::Test;
176-
//!
177-
//! fn main() {
178-
//! pyo3_asyncio::async_std::testing::test_main(
179-
//! "Example Test Suite",
180-
//! vec![
181-
//! Test::new_async(
182-
//! "test_async_sleep".into(),
183-
//! async move {
184-
//! async_std::task::sleep(Duration::from_secs(1)).await;
185-
//! Ok(())
186-
//! }
187-
//! ),
188-
//! pyo3_asyncio::async_std::testing::new_sync_test(
189-
//! "test_sync_sleep".into(),
190-
//! || {
191-
//! thread::sleep(Duration::from_secs(1));
192-
//! Ok(())
193-
//! }
194-
//! )
195-
//! ]
196-
//! );
197-
//! }
198-
//! ```
199-
200-
use async_std::task;
201-
use pyo3::prelude::*;
202-
203-
use crate::{
204-
async_std::AsyncStdRuntime,
205-
generic,
206-
testing::{Test, TestTrait},
207-
};
208-
209-
/// Construct a test from a blocking function (like the traditional `#[test]` attribute)
210-
pub fn new_sync_test<F>(name: String, func: F) -> Test
211-
where
212-
F: FnOnce() -> PyResult<()> + Send + 'static,
213-
{
214-
Test::new_async(name, async move { task::spawn_blocking(func).await })
215-
}
216-
217-
/// Default main function for the async-std test harness.
218-
///
219-
/// This is meant to perform the necessary initialization for most test cases. If you want
220-
/// additional control over the initialization, you can use this function as a template.
221-
pub fn test_main(suite_name: &str, tests: Vec<impl TestTrait + 'static>) {
222-
generic::testing::test_main::<AsyncStdRuntime, _>(suite_name, tests)
223-
}
224-
}

src/generic.rs

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -235,37 +235,3 @@ where
235235

236236
Ok(future_rx)
237237
}
238-
239-
/// <span class="module-item stab portability" style="display: inline; border-radius: 3px; padding: 2px; font-size: 80%; line-height: 1.2;"><code>testing</code></span> Testing utilities for generic runtimes.
240-
#[cfg(feature = "testing")]
241-
pub mod testing {
242-
use pyo3::prelude::*;
243-
244-
use crate::{
245-
dump_err,
246-
generic::{run_until_complete, Runtime},
247-
testing::{parse_args, test_harness, TestTrait},
248-
with_runtime,
249-
};
250-
251-
/// Default main function for the generic test harness.
252-
///
253-
/// This is meant to perform the necessary initialization for most test cases. If you want
254-
/// additional control over the initialization, you can use this
255-
/// function as a template.
256-
pub fn test_main<R, T>(suite_name: &str, tests: Vec<T>)
257-
where
258-
R: Runtime,
259-
T: TestTrait + 'static,
260-
{
261-
Python::with_gil(|py| {
262-
with_runtime(py, || {
263-
let args = parse_args(suite_name);
264-
run_until_complete::<R, _>(py, test_harness(tests, args))?;
265-
Ok(())
266-
})
267-
.map_err(dump_err(py))
268-
.unwrap();
269-
})
270-
}
271-
}

src/testing.rs

Lines changed: 96 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,100 @@
33
//! This module provides some utilities for parsing test arguments as well as running and filtering
44
//! a sequence of tests.
55
//!
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-
//!
106
//! As mentioned [here](crate#pythons-event-loop), PyO3 Asyncio tests cannot use the default test
117
//! harness since it doesn't allow Python to gain control over the main thread. Instead, we have to
128
//! 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+
//! ```
13100
14101
use std::{future::Future, pin::Pin};
15102

@@ -80,47 +167,19 @@ pub fn parse_args(suite_name: &str) -> Args {
80167
}
81168
}
82169

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-
89170
/// Abstract Test Trait
90171
///
91172
/// This trait works in tandem with the pyo3-asyncio-macros to generate test objects that work with
92173
/// the pyo3-asyncio test harness.
93-
pub trait TestTrait: Send {
174+
pub trait Test: Send {
94175
/// Get the name of the test
95176
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>>;
120179
}
121180

122181
/// 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<()> {
124183
stream::iter(tests)
125184
.for_each_concurrent(Some(4), |test| {
126185
let mut ignore = false;
@@ -133,10 +192,9 @@ pub async fn test_harness(tests: Vec<impl TestTrait + 'static>, args: Args) -> P
133192

134193
async move {
135194
if !ignore {
136-
let name = test.name().to_string();
137195
test.task().await.unwrap();
138196

139-
println!("test {} ... ok", name);
197+
println!("test {} ... ok", test.name());
140198
}
141199
}
142200
})

0 commit comments

Comments
 (0)