Fix 'static_files' and 'serve' tests.

This commit is contained in:
Jeb Rosen 2019-08-14 11:29:55 -07:00 committed by Sergio Benitez
parent b780f9d8e0
commit 8c8598b4fd
5 changed files with 89 additions and 69 deletions

View File

@ -45,7 +45,7 @@ mod static_tests {
"inner/", "inner/",
]; ];
fn assert_file(client: &Client, prefix: &str, path: &str, exists: bool) { async fn assert_file(client: &Client, prefix: &str, path: &str, exists: bool) {
let full_path = format!("/{}/{}", prefix, path); let full_path = format!("/{}/{}", prefix, path);
let mut response = client.get(full_path).dispatch(); let mut response = client.get(full_path).dispatch();
if exists { if exists {
@ -59,50 +59,60 @@ mod static_tests {
let mut file = File::open(path).expect("open file"); let mut file = File::open(path).expect("open file");
let mut expected_contents = String::new(); let mut expected_contents = String::new();
file.read_to_string(&mut expected_contents).expect("read file"); file.read_to_string(&mut expected_contents).expect("read file");
assert_eq!(response.body_string_wait(), Some(expected_contents)); assert_eq!(response.body_string().await, Some(expected_contents));
} else { } else {
assert_eq!(response.status(), Status::NotFound); assert_eq!(response.status(), Status::NotFound);
} }
} }
fn assert_all(client: &Client, prefix: &str, paths: &[&str], exist: bool) { async fn assert_all(client: &Client, prefix: &str, paths: &[&str], exist: bool) {
paths.iter().for_each(|path| assert_file(client, prefix, path, exist)) for path in paths.iter() {
assert_file(client, prefix, path, exist).await;
}
} }
#[test] #[test]
fn test_static_no_index() { fn test_static_no_index() {
let client = Client::new(rocket()).expect("valid rocket"); rocket::async_test(async {
assert_all(&client, "no_index", REGULAR_FILES, true); let client = Client::new(rocket()).expect("valid rocket");
assert_all(&client, "no_index", HIDDEN_FILES, false); assert_all(&client, "no_index", REGULAR_FILES, true).await;
assert_all(&client, "no_index", INDEXED_DIRECTORIES, false); assert_all(&client, "no_index", HIDDEN_FILES, false).await;
assert_all(&client, "no_index", INDEXED_DIRECTORIES, false).await;
})
} }
#[test] #[test]
fn test_static_hidden() { fn test_static_hidden() {
let client = Client::new(rocket()).expect("valid rocket"); rocket::async_test(async {
assert_all(&client, "dots", REGULAR_FILES, true); let client = Client::new(rocket()).expect("valid rocket");
assert_all(&client, "dots", HIDDEN_FILES, true); assert_all(&client, "dots", REGULAR_FILES, true).await;
assert_all(&client, "dots", INDEXED_DIRECTORIES, false); assert_all(&client, "dots", HIDDEN_FILES, true).await;
assert_all(&client, "dots", INDEXED_DIRECTORIES, false).await;
})
} }
#[test] #[test]
fn test_static_index() { fn test_static_index() {
let client = Client::new(rocket()).expect("valid rocket"); rocket::async_test(async {
assert_all(&client, "index", REGULAR_FILES, true); let client = Client::new(rocket()).expect("valid rocket");
assert_all(&client, "index", HIDDEN_FILES, false); assert_all(&client, "index", REGULAR_FILES, true).await;
assert_all(&client, "index", INDEXED_DIRECTORIES, true); assert_all(&client, "index", HIDDEN_FILES, false).await;
assert_all(&client, "index", INDEXED_DIRECTORIES, true).await;
assert_all(&client, "default", REGULAR_FILES, true); assert_all(&client, "default", REGULAR_FILES, true).await;
assert_all(&client, "default", HIDDEN_FILES, false); assert_all(&client, "default", HIDDEN_FILES, false).await;
assert_all(&client, "default", INDEXED_DIRECTORIES, true); assert_all(&client, "default", INDEXED_DIRECTORIES, true).await;
})
} }
#[test] #[test]
fn test_static_all() { fn test_static_all() {
let client = Client::new(rocket()).expect("valid rocket"); rocket::async_test(async {
assert_all(&client, "both", REGULAR_FILES, true); let client = Client::new(rocket()).expect("valid rocket");
assert_all(&client, "both", HIDDEN_FILES, true); assert_all(&client, "both", REGULAR_FILES, true).await;
assert_all(&client, "both", INDEXED_DIRECTORIES, true); assert_all(&client, "both", HIDDEN_FILES, true).await;
assert_all(&client, "both", INDEXED_DIRECTORIES, true).await;
})
} }
#[test] #[test]
@ -121,29 +131,31 @@ mod static_tests {
#[test] #[test]
fn test_forwarding() { fn test_forwarding() {
use rocket::http::RawStr; rocket::async_test(async {
use rocket::{get, routes}; use rocket::http::RawStr;
use rocket::{get, routes};
#[get("/<value>", rank = 20)] #[get("/<value>", rank = 20)]
fn catch_one(value: String) -> String { value } fn catch_one(value: String) -> String { value }
#[get("/<a>/<b>", rank = 20)] #[get("/<a>/<b>", rank = 20)]
fn catch_two(a: &RawStr, b: &RawStr) -> String { format!("{}/{}", a, b) } fn catch_two(a: &RawStr, b: &RawStr) -> String { format!("{}/{}", a, b) }
let rocket = rocket().mount("/default", routes![catch_one, catch_two]); let rocket = rocket().mount("/default", routes![catch_one, catch_two]);
let client = Client::new(rocket).expect("valid rocket"); let client = Client::new(rocket).expect("valid rocket");
let mut response = client.get("/default/ireallydontexist").dispatch(); let mut response = client.get("/default/ireallydontexist").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.body_string_wait().unwrap(), "ireallydontexist"); assert_eq!(response.body_string().await.unwrap(), "ireallydontexist");
let mut response = client.get("/default/idont/exist").dispatch(); let mut response = client.get("/default/idont/exist").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.body_string_wait().unwrap(), "idont/exist"); assert_eq!(response.body_string().await.unwrap(), "idont/exist");
assert_all(&client, "both", REGULAR_FILES, true); assert_all(&client, "both", REGULAR_FILES, true).await;
assert_all(&client, "both", HIDDEN_FILES, true); assert_all(&client, "both", HIDDEN_FILES, true).await;
assert_all(&client, "both", INDEXED_DIRECTORIES, true); assert_all(&client, "both", INDEXED_DIRECTORIES, true).await;
})
} }
#[test] #[test]

View File

@ -1,5 +1,5 @@
use std::fmt; use std::fmt;
use std::rc::Rc; use std::sync::Arc;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::borrow::Cow; use std::borrow::Cow;
@ -67,16 +67,16 @@ use crate::local::Client;
/// [`mut_dispatch`]: #method.mut_dispatch /// [`mut_dispatch`]: #method.mut_dispatch
pub struct LocalRequest<'c> { pub struct LocalRequest<'c> {
client: &'c Client, client: &'c Client,
// This pointer exists to access the `Rc<Request>` mutably inside of // This pointer exists to access the `Arc<Request>` mutably inside of
// `LocalRequest`. This is the only place that a `Request` can be accessed // `LocalRequest`. This is the only place that a `Request` can be accessed
// mutably. This is accomplished via the private `request_mut()` method. // mutably. This is accomplished via the private `request_mut()` method.
ptr: *mut Request<'c>, ptr: *mut Request<'c>,
// This `Rc` exists so that we can transfer ownership to the `LocalResponse` // This `Arc` exists so that we can transfer ownership to the `LocalResponse`
// selectively on dispatch. This is necessary because responses may point // selectively on dispatch. This is necessary because responses may point
// into the request, and thus the request and all of its data needs to be // into the request, and thus the request and all of its data needs to be
// alive while the response is accessible. // alive while the response is accessible.
// //
// Because both a `LocalRequest` and a `LocalResponse` can hold an `Rc` to // Because both a `LocalRequest` and a `LocalResponse` can hold an `Arc` to
// the same `Request`, _and_ the `LocalRequest` can mutate the request, we // the same `Request`, _and_ the `LocalRequest` can mutate the request, we
// must ensure that 1) neither `LocalRequest` not `LocalResponse` are `Sync` // must ensure that 1) neither `LocalRequest` not `LocalResponse` are `Sync`
// or `Send` and 2) mutations carried out in `LocalRequest` are _stable_: // or `Send` and 2) mutations carried out in `LocalRequest` are _stable_:
@ -85,7 +85,7 @@ pub struct LocalRequest<'c> {
// even if the `Request` is mutated by a `LocalRequest`, those mutations are // even if the `Request` is mutated by a `LocalRequest`, those mutations are
// not observable by `LocalResponse`. // not observable by `LocalResponse`.
// //
// The first is ensured by the embedding of the `Rc` type which is neither // The first is ensured by the embedding of the `Arc` type which is neither
// `Send` nor `Sync`. The second is more difficult to argue. First, observe // `Send` nor `Sync`. The second is more difficult to argue. First, observe
// that any methods of `LocalRequest` that _remove_ values from `Request` // that any methods of `LocalRequest` that _remove_ values from `Request`
// only remove _Copy_ values, in particular, `SocketAddr`. Second, the // only remove _Copy_ values, in particular, `SocketAddr`. Second, the
@ -94,7 +94,7 @@ pub struct LocalRequest<'c> {
// `Response`. And finally, observe how all of the data stored in `Request` // `Response`. And finally, observe how all of the data stored in `Request`
// is converted into its owned counterpart before insertion, ensuring stable // is converted into its owned counterpart before insertion, ensuring stable
// addresses. Together, these properties guarantee the second condition. // addresses. Together, these properties guarantee the second condition.
request: Rc<Request<'c>>, request: Arc<Request<'c>>,
data: Vec<u8>, data: Vec<u8>,
uri: Cow<'c, str>, uri: Cow<'c, str>,
} }
@ -118,8 +118,8 @@ impl<'c> LocalRequest<'c> {
} }
// See the comments on the structure for what's going on here. // See the comments on the structure for what's going on here.
let mut request = Rc::new(request); let mut request = Arc::new(request);
let ptr = Rc::get_mut(&mut request).unwrap() as *mut Request<'_>; let ptr = Arc::get_mut(&mut request).unwrap() as *mut Request<'_>;
LocalRequest { client, ptr, request, uri, data: vec![] } LocalRequest { client, ptr, request, uri, data: vec![] }
} }
@ -150,7 +150,7 @@ impl<'c> LocalRequest<'c> {
fn long_lived_request<'a>(&mut self) -> &'a mut Request<'c> { fn long_lived_request<'a>(&mut self) -> &'a mut Request<'c> {
// See the comments in the structure for the argument of correctness. // See the comments in the structure for the argument of correctness.
// Additionally, the caller must ensure that the owned instance of // Additionally, the caller must ensure that the owned instance of
// `Rc<Request>` remains valid as long as the returned reference can be // `Arc<Request>` remains valid as long as the returned reference can be
// accessed. // accessed.
unsafe { &mut *self.ptr } unsafe { &mut *self.ptr }
} }
@ -393,7 +393,7 @@ impl<'c> LocalRequest<'c> {
fn _dispatch( fn _dispatch(
client: &'c Client, client: &'c Client,
request: &'c mut Request<'c>, request: &'c mut Request<'c>,
owned_request: Rc<Request<'c>>, owned_request: Arc<Request<'c>>,
uri: &str, uri: &str,
data: Vec<u8> data: Vec<u8>
) -> LocalResponse<'c> { ) -> LocalResponse<'c> {
@ -454,7 +454,7 @@ impl fmt::Debug for LocalRequest<'_> {
/// when invoking methods, a `LocalResponse` can be treated exactly as if it /// when invoking methods, a `LocalResponse` can be treated exactly as if it
/// were a `Response`. /// were a `Response`.
pub struct LocalResponse<'c> { pub struct LocalResponse<'c> {
_request: Rc<Request<'c>>, _request: Arc<Request<'c>>,
response: Response<'c>, response: Response<'c>,
} }

View File

@ -1,3 +1,5 @@
#![feature(async_await)]
extern crate rocket; extern crate rocket;
extern crate rocket_contrib; extern crate rocket_contrib;

View File

@ -6,14 +6,14 @@ use rocket::http::Status;
use super::rocket; use super::rocket;
fn test_query_file<T> (path: &str, file: T, status: Status) async fn test_query_file<T> (path: &str, file: T, status: Status)
where T: Into<Option<&'static str>> where T: Into<Option<&'static str>>
{ {
let client = Client::new(rocket()).unwrap(); let client = Client::new(rocket()).unwrap();
let mut response = client.get(path).dispatch(); let mut response = client.get(path).dispatch();
assert_eq!(response.status(), status); assert_eq!(response.status(), status);
let body_data = response.body_bytes_wait(); let body_data = response.body_bytes().await;
if let Some(filename) = file.into() { if let Some(filename) = file.into() {
let expected_data = read_file_content(filename); let expected_data = read_file_content(filename);
assert!(body_data.map_or(false, |s| s == expected_data)); assert!(body_data.map_or(false, |s| s == expected_data));
@ -30,27 +30,35 @@ fn read_file_content(path: &str) -> Vec<u8> {
#[test] #[test]
fn test_index_html() { fn test_index_html() {
test_query_file("/", "static/index.html", Status::Ok); rocket::async_test(async {
test_query_file("/?v=1", "static/index.html", Status::Ok); test_query_file("/", "static/index.html", Status::Ok).await;
test_query_file("/?this=should&be=ignored", "static/index.html", Status::Ok); test_query_file("/?v=1", "static/index.html", Status::Ok).await;
test_query_file("/?this=should&be=ignored", "static/index.html", Status::Ok).await;
})
} }
#[test] #[test]
fn test_hidden_file() { fn test_hidden_file() {
test_query_file("/hidden/hi.txt", "static/hidden/hi.txt", Status::Ok); rocket::async_test(async {
test_query_file("/hidden/hi.txt?v=1", "static/hidden/hi.txt", Status::Ok); test_query_file("/hidden/hi.txt", "static/hidden/hi.txt", Status::Ok).await;
test_query_file("/hidden/hi.txt?v=1&a=b", "static/hidden/hi.txt", Status::Ok); test_query_file("/hidden/hi.txt?v=1", "static/hidden/hi.txt", Status::Ok).await;
test_query_file("/hidden/hi.txt?v=1&a=b", "static/hidden/hi.txt", Status::Ok).await;
})
} }
#[test] #[test]
fn test_icon_file() { fn test_icon_file() {
test_query_file("/rocket-icon.jpg", "static/rocket-icon.jpg", Status::Ok); rocket::async_test(async {
test_query_file("/rocket-icon.jpg", "static/rocket-icon.jpg", Status::Ok); test_query_file("/rocket-icon.jpg", "static/rocket-icon.jpg", Status::Ok).await;
test_query_file("/rocket-icon.jpg", "static/rocket-icon.jpg", Status::Ok).await;
})
} }
#[test] #[test]
fn test_invalid_path() { fn test_invalid_path() {
test_query_file("/thou_shalt_not_exist", None, Status::NotFound); rocket::async_test(async {
test_query_file("/thou/shalt/not/exist", None, Status::NotFound); test_query_file("/thou_shalt_not_exist", None, Status::NotFound).await;
test_query_file("/thou/shalt/not/exist?a=b&c=d", None, Status::NotFound); test_query_file("/thou/shalt/not/exist", None, Status::NotFound).await;
test_query_file("/thou/shalt/not/exist?a=b&c=d", None, Status::NotFound).await;
})
} }

View File

@ -67,8 +67,7 @@ if [ "$1" = "--contrib" ]; then
msgpack msgpack
tera_templates tera_templates
handlebars_templates handlebars_templates
# TODO.async: serve needs tests to use tokio runtime, blocked on #1071 serve
# serve
helmet helmet
diesel_postgres_pool diesel_postgres_pool
diesel_sqlite_pool diesel_sqlite_pool
@ -87,9 +86,8 @@ if [ "$1" = "--contrib" ]; then
pushd "${CONTRIB_LIB_ROOT}" > /dev/null 2>&1 pushd "${CONTRIB_LIB_ROOT}" > /dev/null 2>&1
# TODO.async: default_features includes `serve` echo ":: Building and testing contrib [default]..."
# echo ":: Building and testing contrib [default]..." CARGO_INCREMENTAL=0 cargo test
# CARGO_INCREMENTAL=0 cargo test
for feature in "${FEATURES[@]}"; do for feature in "${FEATURES[@]}"; do
echo ":: Building and testing contrib [${feature}]..." echo ":: Building and testing contrib [${feature}]..."