57
57
//! ### Adding Tests to the PyO3 Asyncio Test Harness
58
58
//!
59
59
//! For `async-std` use the [`pyo3_asyncio::async_std::test`](crate::async_std::test) attribute:
60
- //! ```ignore
60
+ //! ```no_run
61
61
//! use std::{time::Duration, thread};
62
62
//!
63
63
//! use pyo3::prelude::*;
74
74
//! Ok(())
75
75
//! }
76
76
//!
77
- //! pyo3_asyncio::testing::test_main!(#[pyo3_asyncio::async_std::main], "Example Test Suite");
77
+ //! // ...
78
+ //! #
79
+ //! # // Doctests don't detect main fn when using the test_main!() macro, so we expand it into the
80
+ //! # // components of that macro instead.
81
+ //! #
82
+ //! # pyo3_asyncio::testing::test_structs!();
83
+ //! #
84
+ //! # #[pyo3_asyncio::async_std::main]
85
+ //! # async fn main() -> PyResult<()> {
86
+ //! # pyo3_asyncio::testing::test_main_body!("Example Test Suite");
87
+ //! #
88
+ //! # Ok(())
89
+ //! # }
78
90
//! ```
79
91
//!
80
92
//! For `tokio` use the [`pyo3_asyncio::tokio::test`](crate::tokio::test) attribute:
81
- //! ```ignore
93
+ //! ```no_run
82
94
//! use std::{time::Duration, thread};
83
95
//!
84
96
//! use pyo3::prelude::*;
95
107
//! Ok(())
96
108
//! }
97
109
//!
98
- //! pyo3_asyncio::testing::test_main!(#[pyo3_asyncio::tokio::main], "Example Test Suite");
110
+ //! // ...
111
+ //! #
112
+ //! # // Doctests don't detect main fn when using the test_main!() macro, so we expand it into the
113
+ //! # // components of that macro instead.
114
+ //! #
115
+ //! # pyo3_asyncio::testing::test_structs!();
116
+ //! #
117
+ //! # #[pyo3_asyncio::tokio::main]
118
+ //! # async fn main() -> PyResult<()> {
119
+ //! # pyo3_asyncio::testing::test_main_body!("Example Test Suite");
120
+ //! #
121
+ //! # Ok(())
122
+ //! # }
123
+ //! ```
124
+ //!
125
+ //! ### Caveats
126
+ //!
127
+ //! The `test_main!()` macro _must_ be placed in the crate root. The `inventory` crate places
128
+ //! restrictions on the structures used by the `#[test]` attributes that force us to create a custom
129
+ //! `Test` structure in the crate root. If `test_main!()` is not expanded in the crate root, then
130
+ //! the resolution of `crate::Test` will fail and the test will not compile.
131
+ //!
132
+ //! #### Lib Tests and Doc Tests
133
+ //!
134
+ //! Unfortunately, these utilities will only run in integration tests. Running lib tests are out of
135
+ //! the question since we need control over the main function.
136
+ //!
137
+ //! You can however perform compilation checks for lib tests and doc tests. This is more handy for
138
+ //! doc tests than it is for lib tests, but it's there if you want it.
139
+ //!
140
+ //! #### Expanding `test_main!()` for Doc Tests
141
+ //!
142
+ //! For some reason, doc tests don't interpret the `test_main!()` macro as providing `fn main()` and
143
+ //! will wrap the test body in another `fn main()`. For technical reasons listed in the Caveats
144
+ //! section above, this is problematic because the `Test` structure will not be inside the crate
145
+ //! root anymore.
146
+ //!
147
+ //! To get around this, we can instead expand `test_main!()` into its components for the doc test:
148
+ //! * [`test_structs!()`](crate::testing::test_structs)
149
+ //! * [`test_main_body!(suite_name: &'static str)`](crate::testing::test_main_body)
150
+ //!
151
+ //! The following `test_main!()` macro:
152
+ //!
153
+ //! ```no_run
154
+ //! pyo3_asyncio::testing::test_main!(#[pyo3_asyncio::async_std::main], "Example Test Suite");
155
+ //! ```
156
+ //!
157
+ //! Is equivalent to this expansion:
158
+ //!
159
+ //! ```no_run
160
+ //! use pyo3::prelude::*;
161
+ //!
162
+ //! pyo3_asyncio::testing::test_structs!();
163
+ //!
164
+ //! #[pyo3_asyncio::async_std::main]
165
+ //! async fn main() -> PyResult<()> {
166
+ //! pyo3_asyncio::testing::test_main_body!("Example Test Suite");
167
+ //! Ok(())
168
+ //! }
169
+ //! ```
170
+ //!
171
+ //! #### Allowing Compilation Checks in Lib Tests
172
+ //!
173
+ //! In order to allow the `#[test]` attributes to expand, we need to expand the `test_structs!()`
174
+ //! macro in the crate root. After that, `pyo3-asyncio` tests can be defined anywhere. Again, these
175
+ //! will not run, but they will be compile-checked during testing.
176
+ //!
177
+ //! `my-crate/src/lib.rs`
178
+ //! ```no_run
179
+ //! #[cfg(test)]
180
+ //! pyo3_asyncio::testing::test_structs!();
181
+ //!
182
+ //! #[cfg(test)]
183
+ //! mod tests {
184
+ //! use pyo3::prelude::*;
185
+ //!
186
+ //! use crate as pyo3_asyncio;
187
+ //!
188
+ //! #[pyo3_asyncio::async_std::test]
189
+ //! async fn test_async_std_async_test_compiles() -> PyResult<()> {
190
+ //! Ok(())
191
+ //! }
192
+ //! #[pyo3_asyncio::async_std::test]
193
+ //! fn test_async_std_sync_test_compiles() -> PyResult<()> {
194
+ //! Ok(())
195
+ //! }
196
+ //!
197
+ //! #[pyo3_asyncio::tokio::test]
198
+ //! async fn test_tokio_async_test_compiles() -> PyResult<()> {
199
+ //! Ok(())
200
+ //! }
201
+ //! #[pyo3_asyncio::tokio::test]
202
+ //! fn test_tokio_sync_test_compiles() -> PyResult<()> {
203
+ //! Ok(())
204
+ //! }
205
+ //! }
99
206
//! ```
100
207
101
208
use std:: { future:: Future , pin:: Pin } ;
@@ -108,6 +215,14 @@ use pyo3::prelude::*;
108
215
#[ cfg( feature = "attributes" ) ]
109
216
pub use pyo3_asyncio_macros:: test_main;
110
217
218
+ /// <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>
219
+ #[ cfg( feature = "attributes" ) ]
220
+ pub use pyo3_asyncio_macros:: test_structs;
221
+
222
+ /// <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>
223
+ #[ cfg( feature = "attributes" ) ]
224
+ pub use pyo3_asyncio_macros:: test_main_body;
225
+
111
226
/// Args that should be provided to the test program
112
227
///
113
228
/// These args are meant to mirror the default test harness's args.
@@ -202,3 +317,32 @@ pub async fn test_harness(tests: Vec<impl Test + 'static>, args: Args) -> PyResu
202
317
203
318
Ok ( ( ) )
204
319
}
320
+
321
+ #[ cfg( test) ]
322
+ mod tests {
323
+ use pyo3:: prelude:: * ;
324
+
325
+ use crate as pyo3_asyncio;
326
+
327
+ #[ cfg( feature = "async-std-runtime" ) ]
328
+ #[ pyo3_asyncio:: async_std:: test]
329
+ async fn test_async_std_async_test_compiles ( ) -> PyResult < ( ) > {
330
+ Ok ( ( ) )
331
+ }
332
+ #[ cfg( feature = "async-std-runtime" ) ]
333
+ #[ pyo3_asyncio:: async_std:: test]
334
+ fn test_async_std_sync_test_compiles ( ) -> PyResult < ( ) > {
335
+ Ok ( ( ) )
336
+ }
337
+
338
+ #[ cfg( feature = "tokio-runtime" ) ]
339
+ #[ pyo3_asyncio:: tokio:: test]
340
+ async fn test_tokio_async_test_compiles ( ) -> PyResult < ( ) > {
341
+ Ok ( ( ) )
342
+ }
343
+ #[ cfg( feature = "tokio-runtime" ) ]
344
+ #[ pyo3_asyncio:: tokio:: test]
345
+ fn test_tokio_sync_test_compiles ( ) -> PyResult < ( ) > {
346
+ Ok ( ( ) )
347
+ }
348
+ }
0 commit comments