1#![forbid(unsafe_code)]
2#![deny(
3 clippy::dbg_macro,
4 missing_copy_implementations,
5 rustdoc::missing_crate_level_docs,
6 missing_debug_implementations,
7 missing_docs,
8 nonstandard_style,
9 unused_qualifications
10)]
11
12mod assertions;
66
67mod test_transport;
68use std::future::{Future, IntoFuture};
69use std::process::Termination;
70
71pub use test_transport::TestTransport;
72
73mod test_conn;
74pub use test_conn::TestConn;
75
76pub mod methods;
77pub mod prelude {
78 pub use crate::{
82 assert_body, assert_body_contains, assert_headers, assert_not_handled, assert_ok,
83 assert_response, assert_status, block_on, connector, init, methods::*,
84 };
85
86 pub use trillium::{Conn, Method, Status};
87}
88
89pub use trillium::{Method, Status};
90
91pub use url::Url;
92
93pub fn init(handler: &mut impl trillium::Handler) {
95 let mut info = "testing".into();
96 block_on(handler.init(&mut info))
97}
98
99pub use futures_lite;
101pub use futures_lite::{AsyncRead, AsyncReadExt, AsyncWrite};
102
103mod server_connector;
104pub use server_connector::{connector, ServerConnector};
105
106use trillium_server_common::Config;
107pub use trillium_server_common::{Connector, ObjectSafeConnector, Server, ServerHandle};
108
109#[derive(Debug)]
110pub struct SpawnHandle<F>(F);
115impl<F> IntoFuture for SpawnHandle<F>
116where
117 F: Future,
118{
119 type IntoFuture = F;
120 type Output = F::Output;
121 fn into_future(self) -> Self::IntoFuture {
122 self.0
123 }
124}
125
126cfg_if::cfg_if! {
127 if #[cfg(feature = "smol")] {
128 pub fn config() -> Config<impl Server, ()> {
130 trillium_smol::config()
131 }
132
133 pub fn spawn<Fut, Out>(future: Fut) -> SpawnHandle<impl Future<Output = Option<Out>>>
135 where
136 Fut: Future<Output = Out> + Send + 'static,
137 Out: Send + 'static
138 {
139 let (tx, rx) = async_channel::bounded::<Out>(1);
140 trillium_smol::async_global_executor::spawn(async move { let _ = tx.send(future.await).await; }).detach();
141 SpawnHandle(async move {
142 let rx = rx;
143 rx.recv().await.ok()
144 })
145 }
146
147 pub fn client_config() -> impl Connector {
149 ClientConfig::default()
150 }
151 pub use trillium_smol::async_global_executor::block_on;
152 pub use trillium_smol::ClientConfig;
153
154 } else if #[cfg(feature = "async-std")] {
155 pub fn config() -> Config<impl Server, ()> {
157 trillium_async_std::config()
158 }
159 pub use trillium_async_std::async_std::task::block_on;
160 pub use trillium_async_std::ClientConfig;
161
162 pub fn spawn<Fut, Out>(future: Fut) -> SpawnHandle<impl Future<Output = Option<Out>>>
164 where
165 Fut: Future<Output = Out> + Send + 'static,
166 Out: Send + 'static
167 {
168 let (tx, rx) = async_channel::bounded::<Out>(1);
169 trillium_async_std::async_std::task::spawn(async move { let _ = tx.send(future.await).await; });
170 SpawnHandle(async move {
171 let rx = rx;
172 rx.recv().await.ok()
173 })
174 }
175 pub fn client_config() -> impl Connector {
177 ClientConfig::default()
178 }
179 } else if #[cfg(feature = "tokio")] {
180 pub fn config() -> Config<impl Server, ()> {
182 trillium_tokio::config()
183 }
184 pub use trillium_tokio::ClientConfig;
185 pub use trillium_tokio::block_on;
186 pub fn spawn<Fut, Out>(future: Fut) -> SpawnHandle<impl Future<Output = Option<Out>>>
188 where
189 Fut: Future<Output = Out> + Send + 'static,
190 Out: Send + 'static
191 {
192 let (tx, rx) = async_channel::bounded::<Out>(1);
193 trillium_tokio::tokio::task::spawn(async move { let _ = tx.send(future.await).await; });
194 SpawnHandle(async move {
195 let rx = rx;
196 rx.recv().await.ok()
197 })
198 }
199 pub fn client_config() -> impl Connector {
201 ClientConfig::default()
202 }
203 } else {
204 pub fn config() -> Config<impl Server, ()> {
206 Config::<RuntimelessServer, ()>::new()
207 }
208
209 pub use RuntimelessClientConfig as ClientConfig;
210
211 pub fn client_config() -> impl Connector {
213 RuntimelessClientConfig::default()
214 }
215
216 pub use futures_lite::future::block_on;
217
218 pub fn spawn<Fut, Out>(future: Fut) -> SpawnHandle<impl Future<Output = Option<Out>>>
220 where
221 Fut: Future<Output = Out> + Send + 'static,
222 Out: Send + 'static
223 {
224 let (tx, rx) = async_channel::bounded::<Out>(1);
225 std::thread::spawn(move || { let _ = tx.send_blocking(block_on(future)); });
226 SpawnHandle(async move {
227 let rx = rx;
228 rx.recv().await.ok()
229 })
230 }
231 }
232}
233
234mod with_server;
235pub use with_server::{with_server, with_transport};
236
237mod runtimeless;
238pub use runtimeless::{RuntimelessClientConfig, RuntimelessServer};
239
240pub type TestResult = Result<(), Box<dyn std::error::Error>>;
242
243#[track_caller]
245pub fn harness<F, Fut, Output>(test: F) -> Output
246where
247 F: FnOnce() -> Fut,
248 Fut: Future<Output = Output>,
249 Output: Termination,
250{
251 block_on(test())
252}