diff options
Diffstat (limited to 'tests/unit/net/image_cache_task.rs')
-rw-r--r-- | tests/unit/net/image_cache_task.rs | 570 |
1 files changed, 570 insertions, 0 deletions
diff --git a/tests/unit/net/image_cache_task.rs b/tests/unit/net/image_cache_task.rs new file mode 100644 index 00000000000..22b0920e8ae --- /dev/null +++ b/tests/unit/net/image_cache_task.rs @@ -0,0 +1,570 @@ +use net::image_cache_task::*; +use net_traits::image_cache_task::ImageResponseMsg::*; +use net_traits::image_cache_task::Msg::*; + +use net::resource_task::start_sending; +use net_traits::{ControlMsg, Metadata, ProgressMsg, ResourceTask}; +use net_traits::image_cache_task::{ImageCacheTask, ImageResponseMsg, Msg}; +use net_traits::ProgressMsg::{Payload, Done}; +use profile::time; +use std::sync::mpsc::{Sender, channel, Receiver}; +use url::Url; +use util::taskpool::TaskPool; + +static TEST_IMAGE: &'static [u8] = include_bytes!("test.jpeg"); + +pub fn test_image_bin() -> Vec<u8> { + TEST_IMAGE.iter().map(|&x| x).collect() +} + +trait ImageCacheTaskHelper { + fn wait_for_store(&self) -> Receiver<()>; + fn wait_for_store_prefetched(&self) -> Receiver<()>; +} + +impl ImageCacheTaskHelper for ImageCacheTask { + fn wait_for_store(&self) -> Receiver<()> { + let (chan, port) = channel(); + self.send(Msg::WaitForStore(chan)); + port + } + + fn wait_for_store_prefetched(&self) -> Receiver<()> { + let (chan, port) = channel(); + self.send(Msg::WaitForStorePrefetched(chan)); + port + } +} + +trait Closure { + fn invoke(&self, _response: Sender<ProgressMsg>) { } +} +struct DoesNothing; +impl Closure for DoesNothing { } + +struct JustSendOK { + url_requested_chan: Sender<()>, +} +impl Closure for JustSendOK { + fn invoke(&self, response: Sender<ProgressMsg>) { + self.url_requested_chan.send(()).unwrap(); + response.send(Done(Ok(()))).unwrap(); + } +} + +struct SendTestImage; +impl Closure for SendTestImage { + fn invoke(&self, response: Sender<ProgressMsg>) { + response.send(Payload(test_image_bin())).unwrap(); + response.send(Done(Ok(()))).unwrap(); + } +} + +struct SendBogusImage; +impl Closure for SendBogusImage { + fn invoke(&self, response: Sender<ProgressMsg>) { + response.send(Payload(vec!())).unwrap(); + response.send(Done(Ok(()))).unwrap(); + } +} + +struct SendTestImageErr; +impl Closure for SendTestImageErr { + fn invoke(&self, response: Sender<ProgressMsg>) { + response.send(Payload(test_image_bin())).unwrap(); + response.send(Done(Err("".to_string()))).unwrap(); + } +} + +struct WaitSendTestImage { + wait_port: Receiver<()>, +} +impl Closure for WaitSendTestImage { + fn invoke(&self, response: Sender<ProgressMsg>) { + // Don't send the data until after the client requests + // the image + self.wait_port.recv().unwrap(); + response.send(Payload(test_image_bin())).unwrap(); + response.send(Done(Ok(()))).unwrap(); + } +} + +struct WaitSendTestImageErr { + wait_port: Receiver<()>, +} +impl Closure for WaitSendTestImageErr { + fn invoke(&self, response: Sender<ProgressMsg>) { + // Don't send the data until after the client requests + // the image + self.wait_port.recv().unwrap(); + response.send(Payload(test_image_bin())).unwrap(); + response.send(Done(Err("".to_string()))).unwrap(); + } +} + +fn mock_resource_task<T: Closure + Send + 'static>(on_load: Box<T>) -> ResourceTask { + spawn_listener(move |port: Receiver<ControlMsg>| { + loop { + match port.recv().unwrap() { + ControlMsg::Load(response) => { + let chan = start_sending(response.consumer, Metadata::default( + Url::parse("file:///fake").unwrap())); + on_load.invoke(chan); + } + ControlMsg::Exit => break, + _ => {} + } + } + }) +} + +fn profiler() -> time::ProfilerChan { + time::Profiler::create(None) +} + +#[test] +fn should_exit_on_request() { + let mock_resource_task = mock_resource_task(Box::new(DoesNothing)); + + let image_cache_task: ImageCacheTask = ImageCacheTaskFactory::new(mock_resource_task.clone(), + TaskPool::new(4), profiler(), + LoadPlaceholder::Ignore); + + image_cache_task.exit(); + mock_resource_task.send(ControlMsg::Exit).unwrap(); +} + +#[test] +#[should_panic] +fn should_panic_if_unprefetched_image_is_requested() { + let mock_resource_task = mock_resource_task(Box::new(DoesNothing)); + + let image_cache_task: ImageCacheTask = ImageCacheTaskFactory::new(mock_resource_task.clone(), + TaskPool::new(4), profiler(), + LoadPlaceholder::Preload); + let url = Url::parse("file:///").unwrap(); + + let (chan, port) = channel(); + image_cache_task.send(Msg::GetImage(url, chan)); + port.recv().unwrap(); +} + +#[test] +fn should_request_url_from_resource_task_on_prefetch() { + let (url_requested_chan, url_requested) = channel(); + + let mock_resource_task = mock_resource_task(Box::new(JustSendOK { url_requested_chan: url_requested_chan})); + + let image_cache_task: ImageCacheTask = ImageCacheTaskFactory::new(mock_resource_task.clone(), + TaskPool::new(4), profiler(), + LoadPlaceholder::Preload); + let url = Url::parse("file:///").unwrap(); + + image_cache_task.send(Prefetch(url)); + url_requested.recv().unwrap(); + image_cache_task.exit(); + mock_resource_task.send(ControlMsg::Exit).unwrap(); +} + +#[test] +fn should_not_request_url_from_resource_task_on_multiple_prefetches() { + let (url_requested_chan, url_requested) = channel(); + + let mock_resource_task = mock_resource_task(Box::new(JustSendOK { url_requested_chan: url_requested_chan})); + + let image_cache_task: ImageCacheTask = ImageCacheTaskFactory::new(mock_resource_task.clone(), + TaskPool::new(4), profiler(), + LoadPlaceholder::Ignore); + let url = Url::parse("file:///").unwrap(); + + image_cache_task.send(Prefetch(url.clone())); + image_cache_task.send(Prefetch(url)); + url_requested.recv().unwrap(); + image_cache_task.exit(); + mock_resource_task.send(ControlMsg::Exit).unwrap(); + match url_requested.try_recv() { + Err(_) => (), + Ok(_) => panic!(), + }; +} + +#[test] +fn should_return_image_not_ready_if_data_has_not_arrived() { + let (wait_chan, wait_port) = channel(); + + let mock_resource_task = mock_resource_task(Box::new(WaitSendTestImage{wait_port: wait_port})); + + let image_cache_task: ImageCacheTask = ImageCacheTaskFactory::new(mock_resource_task.clone(), + TaskPool::new(4), profiler(), + LoadPlaceholder::Ignore); + let url = Url::parse("file:///").unwrap(); + + image_cache_task.send(Prefetch(url.clone())); + image_cache_task.send(Decode(url.clone())); + let (response_chan, response_port) = channel(); + image_cache_task.send(Msg::GetImage(url, response_chan)); + assert!(response_port.recv().unwrap() == ImageResponseMsg::ImageNotReady); + wait_chan.send(()).unwrap(); + image_cache_task.exit(); + mock_resource_task.send(ControlMsg::Exit).unwrap(); +} + +#[test] +fn should_return_decoded_image_data_if_data_has_arrived() { + let mock_resource_task = mock_resource_task(Box::new(SendTestImage)); + + let image_cache_task: ImageCacheTask = ImageCacheTaskFactory::new(mock_resource_task.clone(), + TaskPool::new(4), profiler(), + LoadPlaceholder::Preload); + let url = Url::parse("file:///").unwrap(); + + let join_port = image_cache_task.wait_for_store(); + + image_cache_task.send(Prefetch(url.clone())); + image_cache_task.send(Decode(url.clone())); + + // Wait until our mock resource task has sent the image to the image cache + join_port.recv().unwrap(); + + let (response_chan, response_port) = channel(); + image_cache_task.send(Msg::GetImage(url, response_chan)); + match response_port.recv().unwrap() { + ImageResponseMsg::ImageReady(_) => (), + _ => panic!("bleh") + } + + image_cache_task.exit(); + mock_resource_task.send(ControlMsg::Exit).unwrap(); +} + +#[test] +fn should_return_decoded_image_data_for_multiple_requests() { + let mock_resource_task = mock_resource_task(Box::new(SendTestImage)); + + let image_cache_task: ImageCacheTask = ImageCacheTaskFactory::new(mock_resource_task.clone(), + TaskPool::new(4), profiler(), + LoadPlaceholder::Preload); + let url = Url::parse("file:///").unwrap(); + + let join_port = image_cache_task.wait_for_store(); + + image_cache_task.send(Prefetch(url.clone())); + image_cache_task.send(Decode(url.clone())); + + // Wait until our mock resource task has sent the image to the image cache + join_port.recv().unwrap(); + + for _ in 0..2 { + let (response_chan, response_port) = channel(); + image_cache_task.send(Msg::GetImage(url.clone(), response_chan)); + match response_port.recv().unwrap() { + ImageResponseMsg::ImageReady(_) => (), + _ => panic!("bleh") + } + } + + image_cache_task.exit(); + mock_resource_task.send(ControlMsg::Exit).unwrap(); +} + +#[test] +fn should_not_request_image_from_resource_task_if_image_is_already_available() { + let (image_bin_sent_chan, image_bin_sent) = channel(); + + let (resource_task_exited_chan, resource_task_exited) = channel(); + + let mock_resource_task = spawn_listener(move |port: Receiver<ControlMsg>| { + loop { + match port.recv().unwrap() { + ControlMsg::Load(response) => { + let chan = start_sending(response.consumer, Metadata::default( + Url::parse("file:///fake").unwrap())); + chan.send(Payload(test_image_bin())); + chan.send(Done(Ok(()))); + image_bin_sent_chan.send(()); + } + ControlMsg::Exit => { + resource_task_exited_chan.send(()); + break + } + _ => {} + } + } + }); + + let image_cache_task: ImageCacheTask = ImageCacheTaskFactory::new(mock_resource_task.clone(), + TaskPool::new(4), profiler(), + LoadPlaceholder::Ignore); + let url = Url::parse("file:///").unwrap(); + + image_cache_task.send(Prefetch(url.clone())); + + // Wait until our mock resource task has sent the image to the image cache + image_bin_sent.recv().unwrap(); + + image_cache_task.send(Prefetch(url.clone())); + + image_cache_task.exit(); + mock_resource_task.send(ControlMsg::Exit); + + resource_task_exited.recv().unwrap(); + + // Our resource task should not have received another request for the image + // because it's already cached + match image_bin_sent.try_recv() { + Err(_) => (), + Ok(_) => panic!(), + } +} + +#[test] +fn should_not_request_image_from_resource_task_if_image_fetch_already_failed() { + let (image_bin_sent_chan, image_bin_sent) = channel(); + + let (resource_task_exited_chan, resource_task_exited) = channel(); + let mock_resource_task = spawn_listener(move |port: Receiver<ControlMsg>| { + loop { + match port.recv().unwrap() { + ControlMsg::Load(response) => { + let chan = start_sending(response.consumer, Metadata::default( + Url::parse("file:///fake").unwrap())); + chan.send(Payload(test_image_bin())); + chan.send(Done(Err("".to_string()))); + image_bin_sent_chan.send(()); + } + ControlMsg::Exit => { + resource_task_exited_chan.send(()); + break + } + _ => {} + } + } + }); + + let image_cache_task: ImageCacheTask = ImageCacheTaskFactory::new(mock_resource_task.clone(), + TaskPool::new(4), profiler(), + LoadPlaceholder::Ignore); + let url = Url::parse("file:///").unwrap(); + + image_cache_task.send(Prefetch(url.clone())); + image_cache_task.send(Decode(url.clone())); + + // Wait until our mock resource task has sent the image to the image cache + image_bin_sent.recv().unwrap(); + + image_cache_task.send(Prefetch(url.clone())); + image_cache_task.send(Decode(url.clone())); + + image_cache_task.exit(); + mock_resource_task.send(ControlMsg::Exit); + + resource_task_exited.recv().unwrap(); + + // Our resource task should not have received another request for the image + // because it's already cached + match image_bin_sent.try_recv() { + Err(_) => (), + Ok(_) => panic!(), + } +} + +#[test] +fn should_return_failed_if_image_bin_cannot_be_fetched() { + let mock_resource_task = mock_resource_task(Box::new(SendTestImageErr)); + + let image_cache_task: ImageCacheTask = ImageCacheTaskFactory::new(mock_resource_task.clone(), + TaskPool::new(4), profiler(), + LoadPlaceholder::Preload); + let url = Url::parse("file:///").unwrap(); + + let join_port = image_cache_task.wait_for_store_prefetched(); + + image_cache_task.send(Prefetch(url.clone())); + image_cache_task.send(Decode(url.clone())); + + // Wait until our mock resource task has sent the image to the image cache + join_port.recv().unwrap(); + + let (response_chan, response_port) = channel(); + image_cache_task.send(Msg::GetImage(url, response_chan)); + match response_port.recv().unwrap() { + ImageResponseMsg::ImageFailed => (), + _ => panic!("bleh") + } + + image_cache_task.exit(); + mock_resource_task.send(ControlMsg::Exit); +} + +#[test] +fn should_return_failed_for_multiple_get_image_requests_if_image_bin_cannot_be_fetched() { + let mock_resource_task = mock_resource_task(Box::new(SendTestImageErr)); + + let image_cache_task: ImageCacheTask = ImageCacheTaskFactory::new(mock_resource_task.clone(), + TaskPool::new(4), profiler(), + LoadPlaceholder::Preload); + let url = Url::parse("file:///").unwrap(); + + let join_port = image_cache_task.wait_for_store_prefetched(); + + image_cache_task.send(Prefetch(url.clone())); + image_cache_task.send(Decode(url.clone())); + + // Wait until our mock resource task has sent the image to the image cache + join_port.recv().unwrap(); + + let (response_chan, response_port) = channel(); + image_cache_task.send(Msg::GetImage(url.clone(), response_chan)); + match response_port.recv().unwrap() { + ImageResponseMsg::ImageFailed => (), + _ => panic!("bleh") + } + + // And ask again, we should get the same response + let (response_chan, response_port) = channel(); + image_cache_task.send(Msg::GetImage(url, response_chan)); + match response_port.recv().unwrap() { + ImageResponseMsg::ImageFailed => (), + _ => panic!("bleh") + } + + image_cache_task.exit(); + mock_resource_task.send(ControlMsg::Exit); +} + +#[test] +fn should_return_failed_if_image_decode_fails() { + let mock_resource_task = mock_resource_task(Box::new(SendBogusImage)); + + let image_cache_task: ImageCacheTask = ImageCacheTaskFactory::new(mock_resource_task.clone(), + TaskPool::new(4), profiler(), + LoadPlaceholder::Preload); + let url = Url::parse("file:///").unwrap(); + + let join_port = image_cache_task.wait_for_store(); + + image_cache_task.send(Prefetch(url.clone())); + image_cache_task.send(Decode(url.clone())); + + // Wait until our mock resource task has sent the image to the image cache + join_port.recv().unwrap(); + + // Make the request + let (response_chan, response_port) = channel(); + image_cache_task.send(Msg::GetImage(url, response_chan)); + + match response_port.recv().unwrap() { + ImageResponseMsg::ImageFailed => (), + _ => panic!("bleh") + } + + image_cache_task.exit(); + mock_resource_task.send(ControlMsg::Exit); +} + +#[test] +fn should_return_image_on_wait_if_image_is_already_loaded() { + let mock_resource_task = mock_resource_task(Box::new(SendTestImage)); + + let image_cache_task: ImageCacheTask = ImageCacheTaskFactory::new(mock_resource_task.clone(), + TaskPool::new(4), profiler(), + LoadPlaceholder::Preload); + let url = Url::parse("file:///").unwrap(); + + let join_port = image_cache_task.wait_for_store(); + + image_cache_task.send(Prefetch(url.clone())); + image_cache_task.send(Decode(url.clone())); + + // Wait until our mock resource task has sent the image to the image cache + join_port.recv().unwrap(); + + let (response_chan, response_port) = channel(); + image_cache_task.send(Msg::WaitForImage(url, response_chan)); + match response_port.recv().unwrap() { + ImageResponseMsg::ImageReady(..) => (), + _ => panic!("bleh") + } + + image_cache_task.exit(); + mock_resource_task.send(ControlMsg::Exit); +} + +#[test] +fn should_return_image_on_wait_if_image_is_not_yet_loaded() { + let (wait_chan, wait_port) = channel(); + + let mock_resource_task = mock_resource_task(Box::new(WaitSendTestImage {wait_port: wait_port})); + + let image_cache_task: ImageCacheTask = ImageCacheTaskFactory::new(mock_resource_task.clone(), + TaskPool::new(4), profiler(), + LoadPlaceholder::Ignore); + let url = Url::parse("file:///").unwrap(); + + image_cache_task.send(Prefetch(url.clone())); + image_cache_task.send(Decode(url.clone())); + + let (response_chan, response_port) = channel(); + image_cache_task.send(Msg::WaitForImage(url, response_chan)); + + wait_chan.send(()); + + match response_port.recv().unwrap() { + ImageResponseMsg::ImageReady(..) => (), + _ => panic!("bleh") + } + + image_cache_task.exit(); + mock_resource_task.send(ControlMsg::Exit); +} + +#[test] +fn should_return_image_failed_on_wait_if_image_fails_to_load() { + let (wait_chan, wait_port) = channel(); + + let mock_resource_task = mock_resource_task(Box::new(WaitSendTestImageErr{wait_port: wait_port})); + + let image_cache_task: ImageCacheTask = ImageCacheTaskFactory::new(mock_resource_task.clone(), + TaskPool::new(4), profiler(), + LoadPlaceholder::Ignore); + let url = Url::parse("file:///").unwrap(); + + image_cache_task.send(Prefetch(url.clone())); + image_cache_task.send(Decode(url.clone())); + + let (response_chan, response_port) = channel(); + image_cache_task.send(Msg::WaitForImage(url, response_chan)); + + wait_chan.send(()); + + match response_port.recv().unwrap() { + ImageResponseMsg::ImageFailed => (), + _ => panic!("bleh") + } + + image_cache_task.exit(); + mock_resource_task.send(ControlMsg::Exit); +} + +#[test] +fn sync_cache_should_wait_for_images() { + let mock_resource_task = mock_resource_task(Box::new(SendTestImage)); + + let image_cache_task: ImageCacheTask = ImageCacheTaskFactory::new_sync(mock_resource_task.clone(), + TaskPool::new(4), profiler(), + LoadPlaceholder::Preload); + let url = Url::parse("file:///").unwrap(); + + image_cache_task.send(Prefetch(url.clone())); + image_cache_task.send(Decode(url.clone())); + + let (response_chan, response_port) = channel(); + image_cache_task.send(Msg::GetImage(url, response_chan)); + match response_port.recv().unwrap() { + ImageResponseMsg::ImageReady(_) => (), + _ => panic!("bleh") + } + + image_cache_task.exit(); + mock_resource_task.send(ControlMsg::Exit); +} |