Fix or ignore remaining doc tests in 'core'.

This adds an unstable "async_test" function for use in tests, to ensure
they stay working while refactoring other APIs.
This commit is contained in:
Jeb Rosen 2019-08-14 09:30:59 -07:00 committed by Sergio Benitez
parent aee7f35095
commit b780f9d8e0
8 changed files with 116 additions and 55 deletions

View File

@ -133,20 +133,22 @@ impl Data {
/// # Example
///
/// ```rust
/// # #![feature(async_await)]
/// use std::io;
/// use futures::io::AllowStdIo;
/// use rocket::Data;
///
/// fn handler(mut data: Data) -> io::Result<String> {
/// async fn handler(mut data: Data) -> io::Result<String> {
/// // write all of the data to stdout
/// data.stream_to(&mut io::stdout())
/// .map(|n| format!("Wrote {} bytes.", n))
/// let written = data.stream_to(AllowStdIo::new(io::stdout())).await?;
/// Ok(format!("Wrote {} bytes.", written))
/// }
/// ```
#[inline(always)]
pub fn stream_to<'w, W: AsyncWrite + Unpin>(self, writer: &'w mut W) -> impl Future<Output = io::Result<u64>> + 'w {
pub fn stream_to<'w, W: AsyncWrite + Unpin + 'w>(self, mut writer: W) -> impl Future<Output = io::Result<u64>> + 'w {
Box::pin(async move {
let stream = self.open();
stream.copy_into(writer).await
stream.copy_into(&mut writer).await
})
}
@ -159,19 +161,20 @@ impl Data {
/// # Example
///
/// ```rust
/// # #![feature(async_await)]
/// use std::io;
/// use rocket::Data;
///
/// fn handler(mut data: Data) -> io::Result<String> {
/// data.stream_to_file("/static/file")
/// .map(|n| format!("Wrote {} bytes to /static/file", n))
/// async fn handler(mut data: Data) -> io::Result<String> {
/// let written = data.stream_to_file("/static/file").await?;
/// Ok(format!("Wrote {} bytes to /static/file", written))
/// }
/// ```
#[inline(always)]
pub fn stream_to_file<P: AsRef<Path> + Send + 'static>(self, path: P) -> impl Future<Output = io::Result<u64>> {
Box::pin(async move {
let mut file = tokio::fs::File::create(path).compat().await?.compat();
self.stream_to(&mut file).await
let file = tokio::fs::File::create(path).compat().await?.compat();
self.stream_to(file).await
})
}

View File

@ -149,3 +149,13 @@ pub fn ignite() -> Rocket {
pub fn custom(config: config::Config) -> Rocket {
Rocket::custom(config)
}
// TODO.async: More thoughtful plan for async tests
/// WARNING: This is unstable! Do not use this method outside of Rocket!
#[doc(hidden)]
pub fn async_test(fut: impl std::future::Future<Output = ()> + Send + 'static) {
use futures::future::{FutureExt, TryFutureExt};
let mut runtime = tokio::runtime::Runtime::new().expect("create tokio runtime");
runtime.block_on(fut.boxed().unit_error().compat()).expect("unit_error future returned Err");
}

View File

@ -18,16 +18,20 @@ use yansi::Paint;
///
/// ```rust
/// # #![feature(proc_macro_hygiene)]
/// use std::io::{self, Read};
/// use std::io;
///
/// use futures::io::AsyncReadExt;
///
/// # use rocket::post;
/// use rocket::Data;
/// use rocket::response::Debug;
///
/// use rocket::AsyncReadExt as _;
///
/// #[post("/", format = "plain", data = "<data>")]
/// fn post(data: Data) -> Result<String, Debug<io::Error>> {
/// async fn post(data: Data) -> Result<String, Debug<io::Error>> {
/// let mut name = String::with_capacity(32);
/// data.open().take(32).read_to_string(&mut name)?;
/// data.open().take(32).read_to_string(&mut name).await?;
/// Ok(name)
/// }
/// ```

View File

@ -166,7 +166,7 @@ use crate::request::Request;
/// use rocket::response::{self, Response, Responder};
/// use rocket::http::ContentType;
///
/// impl Responder<'r> for Person {
/// impl<'r> Responder<'r> for Person {
/// fn respond_to(self, _: &'r Request) -> response::ResultFuture<'r> {
/// Box::pin(async move {
/// Response::build()

View File

@ -268,11 +268,12 @@ impl<'r> ResponseBuilder<'r> {
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::hyper::header::Accept;
/// use rocket::http::Header;
/// use rocket::http::hyper::header::ACCEPT;
///
/// let response = Response::build()
/// .header_adjoin(Accept::json())
/// .header_adjoin(Accept::text())
/// .header_adjoin(Header::new(ACCEPT.as_str(), "application/json"))
/// .header_adjoin(Header::new(ACCEPT.as_str(), "text/plain"))
/// .finalize();
///
/// assert_eq!(response.headers().get("Accept").count(), 2);
@ -338,20 +339,23 @@ impl<'r> ResponseBuilder<'r> {
self
}
// TODO.async: un-ignore this test once Seek/AsyncSeek situation has been resolved.
/// Sets the body of the `Response` to be the fixed-sized `body`.
///
/// # Example
///
/// ```rust
/// ```rust,ignore
/// # #![feature(async_await)]
/// use rocket::Response;
/// use std::fs::File;
/// use futures::compat::{AsyncRead01CompatExt, Future01CompatExt};
/// use tokio::fs::File;
/// # use std::io;
///
/// # #[allow(dead_code)]
/// # fn test() -> io::Result<()> {
/// # async fn test() -> io::Result<()> {
/// # #[allow(unused_variables)]
/// let response = Response::build()
/// .sized_body(File::open("body.txt")?)
/// .sized_body(File::open("body.txt").compat().await?.compat())
/// .finalize();
/// # Ok(())
/// # }
@ -369,15 +373,17 @@ impl<'r> ResponseBuilder<'r> {
/// # Example
///
/// ```rust
/// # #![feature(async_await)]
/// use rocket::Response;
/// use std::fs::File;
/// use futures::compat::{AsyncRead01CompatExt, Future01CompatExt};
/// use tokio::fs::File;
/// # use std::io;
///
/// # #[allow(dead_code)]
/// # fn test() -> io::Result<()> {
/// # async fn test() -> io::Result<()> {
/// # #[allow(unused_variables)]
/// let response = Response::build()
/// .streamed_body(File::open("body.txt")?)
/// .streamed_body(File::open("body.txt").compat().await?.compat())
/// .finalize();
/// # Ok(())
/// # }
@ -396,15 +402,17 @@ impl<'r> ResponseBuilder<'r> {
/// # Example
///
/// ```rust
/// # #![feature(async_await)]
/// use rocket::Response;
/// use std::fs::File;
/// use futures::compat::{AsyncRead01CompatExt, Future01CompatExt};
/// use tokio::fs::File;
/// # use std::io;
///
/// # #[allow(dead_code)]
/// # fn test() -> io::Result<()> {
/// # async fn test() -> io::Result<()> {
/// # #[allow(unused_variables)]
/// let response = Response::build()
/// .chunked_body(File::open("body.txt")?, 8096)
/// .chunked_body(File::open("body.txt").compat().await?.compat(), 8096)
/// .finalize();
/// # Ok(())
/// # }
@ -814,15 +822,16 @@ impl<'r> Response<'r> {
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::hyper::header::Accept;
/// use rocket::http::Header;
/// use rocket::http::hyper::header::ACCEPT;
///
/// let mut response = Response::new();
/// response.adjoin_header(Accept::json());
/// response.adjoin_header(Accept::text());
/// response.adjoin_header(Header::new(ACCEPT.as_str(), "application/json"));
/// response.adjoin_header(Header::new(ACCEPT.as_str(), "text/plain"));
///
/// let mut accept_headers = response.headers().iter();
/// assert_eq!(accept_headers.next(), Some(Accept::json().into()));
/// assert_eq!(accept_headers.next(), Some(Accept::text().into()));
/// assert_eq!(accept_headers.next(), Some(Header::new(ACCEPT.as_str(), "application/json")));
/// assert_eq!(accept_headers.next(), Some(Header::new(ACCEPT.as_str(), "text/plain")));
/// assert_eq!(accept_headers.next(), None);
/// ```
#[inline(always)]
@ -887,14 +896,17 @@ impl<'r> Response<'r> {
/// # Example
///
/// ```rust
/// # #![feature(async_await)]
/// use std::io::Cursor;
/// use rocket::Response;
///
/// # rocket::async_test(async {
/// let mut response = Response::new();
/// assert!(response.body().is_none());
///
/// response.set_sized_body(Cursor::new("Hello, world!"));
/// assert_eq!(response.body_string(), Some("Hello, world!".to_string()));
/// assert_eq!(response.body_string().await, Some("Hello, world!".to_string()));
/// # })
/// ```
#[inline(always)]
pub fn body(&mut self) -> Option<Body<&mut (dyn AsyncRead + Unpin + Send)>> {
@ -916,15 +928,18 @@ impl<'r> Response<'r> {
/// # Example
///
/// ```rust
/// # #![feature(async_await)]
/// use std::io::Cursor;
/// use rocket::Response;
///
/// # rocket::async_test(async {
/// let mut response = Response::new();
/// assert!(response.body().is_none());
///
/// response.set_sized_body(Cursor::new("Hello, world!"));
/// assert_eq!(response.body_string(), Some("Hello, world!".to_string()));
/// assert_eq!(response.body_string().await, Some("Hello, world!".to_string()));
/// assert!(response.body().is_none());
/// # })
/// ```
#[inline(always)]
pub fn body_string(&mut self) -> impl Future<Output = Option<String>> + 'r {
@ -944,15 +959,18 @@ impl<'r> Response<'r> {
/// # Example
///
/// ```rust
/// # #![feature(async_await)]
/// use std::io::Cursor;
/// use rocket::Response;
///
/// # rocket::async_test(async {
/// let mut response = Response::new();
/// assert!(response.body().is_none());
///
/// response.set_sized_body(Cursor::new("hi!"));
/// assert_eq!(response.body_bytes(), Some(vec![0x68, 0x69, 0x21]));
/// assert_eq!(response.body_bytes().await, Some(vec![0x68, 0x69, 0x21]));
/// assert!(response.body().is_none());
/// # })
/// ```
#[inline(always)]
pub fn body_bytes(&mut self) -> impl Future<Output = Option<Vec<u8>>> + 'r {
@ -971,9 +989,11 @@ impl<'r> Response<'r> {
/// # Example
///
/// ```rust
/// # #![feature(async_await)]
/// use std::io::Cursor;
/// use rocket::Response;
///
/// # rocket::async_test(async {
/// let mut response = Response::new();
/// assert!(response.body().is_none());
///
@ -981,9 +1001,13 @@ impl<'r> Response<'r> {
/// assert!(response.body().is_some());
///
/// let body = response.take_body();
/// let body_string = body.and_then(|b| b.into_string());
/// let body_string = match body {
/// Some(b) => b.into_string().await,
/// None => None,
/// };
/// assert_eq!(body_string, Some("Hello, world!".to_string()));
/// assert!(response.body().is_none());
/// # })
/// ```
#[inline(always)]
pub fn take_body(&mut self) -> Option<Body<Pin<Box<dyn AsyncRead + Send + 'r>>>> {
@ -1015,12 +1039,15 @@ impl<'r> Response<'r> {
/// # Example
///
/// ```rust
/// # #![feature(async_await)]
/// use std::io::Cursor;
/// use rocket::Response;
///
/// # rocket::async_test(async {
/// let mut response = Response::new();
/// response.set_sized_body(Cursor::new("Hello, world!"));
/// assert_eq!(response.body_string(), Some("Hello, world!".to_string()));
/// assert_eq!(response.body_string().await, Some("Hello, world!".to_string()));
/// # })
/// ```
#[inline]
pub fn set_sized_body<B>(&mut self, mut body: B)
@ -1041,12 +1068,17 @@ impl<'r> Response<'r> {
/// # Example
///
/// ```rust
/// use std::io::{AsyncRead, repeat};
/// # #![feature(async_await)]
/// use std::io::repeat;
/// use futures::io::AsyncReadExt;
/// use rocket::Response;
/// use rocket::AsyncReadExt as _;
///
/// # rocket::async_test(async {
/// let mut response = Response::new();
/// response.set_streamed_body(repeat(97).take(5));
/// assert_eq!(response.body_string(), Some("aaaaa".to_string()));
/// assert_eq!(response.body_string().await, Some("aaaaa".to_string()));
/// # })
/// ```
#[inline(always)]
pub fn set_streamed_body<B>(&mut self, body: B) where B: AsyncRead + Send + 'r {
@ -1059,12 +1091,17 @@ impl<'r> Response<'r> {
/// # Example
///
/// ```rust
/// use std::io::{AsyncRead, repeat};
/// # #![feature(async_await)]
/// use std::io::repeat;
/// use futures::io::AsyncReadExt;
/// use rocket::Response;
/// use rocket::AsyncReadExt as _;
///
/// # rocket::async_test(async {
/// let mut response = Response::new();
/// response.set_chunked_body(repeat(97).take(5), 10);
/// assert_eq!(response.body_string(), Some("aaaaa".to_string()));
/// assert_eq!(response.body_string().await, Some("aaaaa".to_string()));
/// # })
/// ```
#[inline(always)]
pub fn set_chunked_body<B>(&mut self, body: B, chunk_size: u64)
@ -1079,15 +1116,18 @@ impl<'r> Response<'r> {
/// # Example
///
/// ```rust
/// # #![feature(async_await)]
/// use std::io::Cursor;
/// use rocket::response::{Response, Body};
///
/// # rocket::async_test(async {
/// let body = Body::Sized(Cursor::new("Hello!"), 6);
///
/// let mut response = Response::new();
/// response.set_raw_body(body);
///
/// assert_eq!(response.body_string(), Some("Hello!".to_string()));
/// assert_eq!(response.body_string().await, Some("Hello!".to_string()));
/// # })
/// ```
#[inline(always)]
pub fn set_raw_body<T>(&mut self, body: Body<T>)

View File

@ -50,13 +50,15 @@ impl<'r, R> Created<R> {
/// status::Created::new("http://myservice.com/resource.json")
/// }
///
/// # rocket::async_test(async move {
/// # let rocket = rocket::ignite().mount("/", routes![create]);
/// # let client = Client::new(rocket).unwrap();
/// let mut response = client.get("/").dispatch();
/// let mut response = client.get("/").dispatch().await;
///
/// let loc = response.headers().get_one("Location");
/// assert_eq!(loc, Some("http://myservice.com/resource.json"));
/// assert!(response.body().is_none());
/// });
/// ```
pub fn new<L: Into<Cow<'static, str>>>(location: L) -> Self {
Created(location.into(), None, None)
@ -80,11 +82,12 @@ impl<'r, R> Created<R> {
/// .body("{ 'resource': 'Hello, world!' }")
/// }
///
/// # rocket::async_test(async move {
/// # let rocket = rocket::ignite().mount("/", routes![create]);
/// # let client = Client::new(rocket).unwrap();
/// let mut response = client.get("/").dispatch();
/// let mut response = client.get("/").dispatch().await;
///
/// let body = response.body_string();
/// let body = response.body_string().await;
/// assert_eq!(body.unwrap(), "{ 'resource': 'Hello, world!' }");
///
/// let loc = response.headers().get_one("Location");
@ -92,6 +95,7 @@ impl<'r, R> Created<R> {
///
/// let etag = response.headers().get_one("ETag");
/// assert_eq!(etag, None);
/// # });
/// ```
pub fn body(mut self, responder: R) -> Self
where R: Responder<'r>
@ -116,11 +120,12 @@ impl<'r, R> Created<R> {
/// .tagged_body("{ 'resource': 'Hello, world!' }")
/// }
///
/// # rocket::async_test(async move {
/// # let rocket = rocket::ignite().mount("/", routes![create]);
/// # let client = Client::new(rocket).unwrap();
/// let mut response = client.get("/").dispatch();
/// let mut response = client.get("/").dispatch().await;
///
/// let body = response.body_string();
/// let body = response.body_string().await;
/// assert_eq!(body.unwrap(), "{ 'resource': 'Hello, world!' }");
///
/// let loc = response.headers().get_one("Location");
@ -128,6 +133,7 @@ impl<'r, R> Created<R> {
///
/// let etag = response.headers().get_one("ETag");
/// assert_eq!(etag, Some(r#""13046220615156895040""#));
/// # });
/// ```
pub fn tagged_body(mut self, responder: R) -> Self
where R: Responder<'r> + Hash

View File

@ -24,10 +24,11 @@ impl<T: AsyncRead> Stream<T> {
///
/// ```rust
/// use std::io;
/// use futures::io::AllowStdIo;
/// use rocket::response::Stream;
///
/// # #[allow(unused_variables)]
/// let response = Stream::chunked(io::stdin(), 10);
/// let response = Stream::chunked(AllowStdIo::new(io::stdin()), 10);
/// ```
pub fn chunked(reader: T, chunk_size: u64) -> Stream<T> {
Stream(reader, chunk_size)
@ -49,10 +50,11 @@ impl<T: AsyncRead + Debug> Debug for Stream<T> {
///
/// ```rust
/// use std::io;
/// use futures::io::AllowStdIo;
/// use rocket::response::Stream;
///
/// # #[allow(unused_variables)]
/// let response = Stream::from(io::stdin());
/// let response = Stream::from(AllowStdIo::new(io::stdin()));
/// ```
impl<T: AsyncRead> From<T> for Stream<T> {
fn from(reader: T) -> Self {

View File

@ -106,16 +106,12 @@ elif [ "$1" = "--core" ]; then
pushd "${CORE_LIB_ROOT}" > /dev/null 2>&1
echo ":: Building and testing core [no features]..."
# TODO.async: --lib because doc tests are not complete
CARGO_INCREMENTAL=0 cargo test --no-default-features --lib
# CARGO_INCREMENTAL=0 cargo test --no-default-features
CARGO_INCREMENTAL=0 cargo test --no-default-features
for feature in "${FEATURES[@]}"; do
echo ":: Building and testing core [${feature}]..."
# TODO.async: --lib because doc tests are not complete
CARGO_INCREMENTAL=0 cargo test --no-default-features --features "${feature}" --lib
# CARGO_INCREMENTAL=0 cargo test --no-default-features --features "${feature}"
CARGO_INCREMENTAL=0 cargo test --no-default-features --features "${feature}"
done
popd > /dev/null 2>&1