Auto merge of #5574 - servo:unit-tests, r=jack

On my laptop, running `./mach test-unit` goes from about 11 minutes to 22 seconds, when run after `./mach build`.

Fix #5291.
This commit is contained in:
bors-servo 2015-04-07 21:16:49 -05:00
commit 017d105361
41 changed files with 2216 additions and 2023 deletions

49
tests/unit/Cargo.toml Normal file
View file

@ -0,0 +1,49 @@
[package]
name = "unit_tests"
version = "0.0.1"
authors = ["The Servo Project Developers"]
[lib]
name = "unit_tests"
path = "lib.rs"
doctest = false
[dependencies.util]
path = "../../components/util"
[dependencies.gfx]
path = "../../components/gfx"
[dependencies.net]
path = "../../components/net"
[dependencies.net_traits]
path = "../../components/net_traits"
[dependencies.profile]
path = "../../components/profile"
[dependencies.style]
path = "../../components/style"
[dependencies.script]
path = "../../components/script"
[dependencies.geom]
git = "https://github.com/servo/rust-geom"
[dependencies.cssparser]
git = "https://github.com/servo/rust-cssparser"
[dependencies.selectors]
git = "https://github.com/servo/rust-selectors"
[dependencies.string_cache]
git = "https://github.com/servo/string-cache"
[dependencies.string_cache_plugin]
git = "https://github.com/servo/string-cache"
[dependencies]
cookie = "*"
url = "*"

5
tests/unit/gfx/mod.rs Normal file
View file

@ -0,0 +1,5 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
mod text_util;

165
tests/unit/gfx/text_util.rs Normal file
View file

@ -0,0 +1,165 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use gfx::text::util::{CompressionMode, transform_text};
#[test]
fn test_transform_compress_none() {
let test_strs = [
" foo bar",
"foo bar ",
"foo\n bar",
"foo \nbar",
" foo bar \nbaz",
"foo bar baz",
"foobarbaz\n\n",
];
let mode = CompressionMode::CompressNone;
for &test in test_strs.iter() {
let mut new_line_pos = vec![];
let mut trimmed_str = String::new();
transform_text(test, mode, true, &mut trimmed_str, &mut new_line_pos);
assert_eq!(trimmed_str, test)
}
}
#[test]
fn test_transform_discard_newline() {
let test_strs = [
(" foo bar",
" foo bar"),
("foo bar ",
"foo bar "),
("foo\n bar",
"foo bar"),
("foo \nbar",
"foo bar"),
(" foo bar \nbaz",
" foo bar baz"),
("foo bar baz",
"foo bar baz"),
("foobarbaz\n\n",
"foobarbaz"),
];
let mode = CompressionMode::DiscardNewline;
for &(test, oracle) in test_strs.iter() {
let mut new_line_pos = vec![];
let mut trimmed_str = String::new();
transform_text(test, mode, true, &mut trimmed_str, &mut new_line_pos);
assert_eq!(trimmed_str, oracle)
}
}
#[test]
fn test_transform_compress_whitespace() {
let test_strs = [
(" foo bar",
"foo bar"),
("foo bar ",
"foo bar "),
("foo\n bar",
"foo\n bar"),
("foo \nbar",
"foo \nbar"),
(" foo bar \nbaz",
"foo bar \nbaz"),
("foo bar baz",
"foo bar baz"),
("foobarbaz\n\n",
"foobarbaz\n\n"),
];
let mode = CompressionMode::CompressWhitespace;
for &(test, oracle) in test_strs.iter() {
let mut new_line_pos = vec![];
let mut trimmed_str = String::new();
transform_text(test, mode, true, &mut trimmed_str, &mut new_line_pos);
assert_eq!(&*trimmed_str, oracle)
}
}
#[test]
fn test_transform_compress_whitespace_newline() {
let test_strs = vec![
(" foo bar",
"foo bar"),
("foo bar ",
"foo bar "),
("foo\n bar",
"foo bar"),
("foo \nbar",
"foo bar"),
(" foo bar \nbaz",
"foo bar baz"),
("foo bar baz",
"foo bar baz"),
("foobarbaz\n\n",
"foobarbaz "),
];
let mode = CompressionMode::CompressWhitespaceNewline;
for &(test, oracle) in test_strs.iter() {
let mut new_line_pos = vec![];
let mut trimmed_str = String::new();
transform_text(test, mode, true, &mut trimmed_str, &mut new_line_pos);
assert_eq!(&*trimmed_str, oracle)
}
}
#[test]
fn test_transform_compress_whitespace_newline_no_incoming() {
let test_strs = [
(" foo bar",
" foo bar"),
("\nfoo bar",
" foo bar"),
("foo bar ",
"foo bar "),
("foo\n bar",
"foo bar"),
("foo \nbar",
"foo bar"),
(" foo bar \nbaz",
" foo bar baz"),
("foo bar baz",
"foo bar baz"),
("foobarbaz\n\n",
"foobarbaz "),
];
let mode = CompressionMode::CompressWhitespaceNewline;
for &(test, oracle) in test_strs.iter() {
let mut new_line_pos = vec![];
let mut trimmed_str = String::new();
transform_text(test, mode, false, &mut trimmed_str, &mut new_line_pos);
assert_eq!(trimmed_str, oracle)
}
}

27
tests/unit/lib.rs Normal file
View file

@ -0,0 +1,27 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![feature(plugin)]
#![cfg_attr(test, feature(net, alloc))]
#![plugin(string_cache_plugin)]
extern crate cssparser;
extern crate geom;
extern crate gfx;
extern crate net;
extern crate net_traits;
extern crate profile;
extern crate script;
extern crate selectors;
extern crate string_cache;
extern crate style;
extern crate util;
extern crate url;
#[cfg(test)] #[path="gfx/mod.rs"] mod gfx_tests;
#[cfg(test)] #[path="net/mod.rs"] mod net_tests;
#[cfg(test)] #[path="script/mod.rs"] mod script_tests;
#[cfg(test)] #[path="style/mod.rs"] mod style_tests;
#[cfg(test)] #[path="util/mod.rs"] mod util_tests;

104
tests/unit/net/cookie.rs Normal file
View file

@ -0,0 +1,104 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate "cookie" as cookie_rs;
use net::cookie::Cookie;
use net::cookie_storage::CookieStorage;
use net_traits::CookieSource;
use url::Url;
#[test]
fn test_domain_match() {
assert!(Cookie::domain_match("foo.com", "foo.com"));
assert!(Cookie::domain_match("bar.foo.com", "foo.com"));
assert!(Cookie::domain_match("baz.bar.foo.com", "foo.com"));
assert!(!Cookie::domain_match("bar.foo.com", "bar.com"));
assert!(!Cookie::domain_match("bar.com", "baz.bar.com"));
assert!(!Cookie::domain_match("foo.com", "bar.com"));
assert!(!Cookie::domain_match("bar.com", "bbar.com"));
assert!(Cookie::domain_match("235.132.2.3", "235.132.2.3"));
assert!(!Cookie::domain_match("235.132.2.3", "1.1.1.1"));
assert!(!Cookie::domain_match("235.132.2.3", ".2.3"));
}
#[test]
fn test_default_path() {
assert!(&*Cookie::default_path("/foo/bar/baz/") == "/foo/bar/baz");
assert!(&*Cookie::default_path("/foo/bar/baz") == "/foo/bar");
assert!(&*Cookie::default_path("/foo/") == "/foo");
assert!(&*Cookie::default_path("/foo") == "/");
assert!(&*Cookie::default_path("/") == "/");
assert!(&*Cookie::default_path("") == "/");
assert!(&*Cookie::default_path("foo") == "/");
}
#[test]
fn fn_cookie_constructor() {
use net_traits::CookieSource;
let url = &Url::parse("http://example.com/foo").unwrap();
let gov_url = &Url::parse("http://gov.ac/foo").unwrap();
// cookie name/value test
assert!(cookie_rs::Cookie::parse(" baz ").is_err());
assert!(cookie_rs::Cookie::parse(" = bar ").is_err());
assert!(cookie_rs::Cookie::parse(" baz = ").is_ok());
// cookie domains test
let cookie = cookie_rs::Cookie::parse(" baz = bar; Domain = ").unwrap();
assert!(Cookie::new_wrapped(cookie.clone(), url, CookieSource::HTTP).is_some());
let cookie = Cookie::new_wrapped(cookie, url, CookieSource::HTTP).unwrap();
assert!(&**cookie.cookie.domain.as_ref().unwrap() == "example.com");
// cookie public domains test
let cookie = cookie_rs::Cookie::parse(" baz = bar; Domain = gov.ac").unwrap();
assert!(Cookie::new_wrapped(cookie.clone(), url, CookieSource::HTTP).is_none());
assert!(Cookie::new_wrapped(cookie, gov_url, CookieSource::HTTP).is_some());
// cookie domain matching test
let cookie = cookie_rs::Cookie::parse(" baz = bar ; Secure; Domain = bazample.com").unwrap();
assert!(Cookie::new_wrapped(cookie, url, CookieSource::HTTP).is_none());
let cookie = cookie_rs::Cookie::parse(" baz = bar ; Secure; Path = /foo/bar/").unwrap();
assert!(Cookie::new_wrapped(cookie, url, CookieSource::HTTP).is_some());
let cookie = cookie_rs::Cookie::parse(" baz = bar ; HttpOnly").unwrap();
assert!(Cookie::new_wrapped(cookie, url, CookieSource::NonHTTP).is_none());
let cookie = cookie_rs::Cookie::parse(" baz = bar ; Secure; Path = /foo/bar/").unwrap();
let cookie = Cookie::new_wrapped(cookie, url, CookieSource::HTTP).unwrap();
assert!(cookie.cookie.value == "bar");
assert!(cookie.cookie.name == "baz");
assert!(cookie.cookie.secure);
assert!(&cookie.cookie.path.as_ref().unwrap()[..] == "/foo/bar/");
assert!(&cookie.cookie.domain.as_ref().unwrap()[..] == "example.com");
assert!(cookie.host_only);
let u = &Url::parse("http://example.com/foobar").unwrap();
let cookie = cookie_rs::Cookie::parse("foobar=value;path=/").unwrap();
assert!(Cookie::new_wrapped(cookie, u, CookieSource::HTTP).is_some());
}
#[test]
fn test_sort_order() {
use std::cmp::Ordering;
let url = &Url::parse("http://example.com/foo").unwrap();
let a_wrapped = cookie_rs::Cookie::parse("baz=bar; Path=/foo/bar/").unwrap();
let a = Cookie::new_wrapped(a_wrapped.clone(), url, CookieSource::HTTP).unwrap();
let a_prime = Cookie::new_wrapped(a_wrapped, url, CookieSource::HTTP).unwrap();
let b = cookie_rs::Cookie::parse("baz=bar;Path=/foo/bar/baz/").unwrap();
let b = Cookie::new_wrapped(b, url, CookieSource::HTTP).unwrap();
assert!(b.cookie.path.as_ref().unwrap().len() > a.cookie.path.as_ref().unwrap().len());
assert!(CookieStorage::cookie_comparator(&a, &b) == Ordering::Greater);
assert!(CookieStorage::cookie_comparator(&b, &a) == Ordering::Less);
assert!(CookieStorage::cookie_comparator(&a, &a_prime) == Ordering::Less);
assert!(CookieStorage::cookie_comparator(&a_prime, &a) == Ordering::Greater);
assert!(CookieStorage::cookie_comparator(&a, &a) == Ordering::Equal);
}

View file

@ -0,0 +1,75 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use net_traits::LoadData;
use net_traits::ProgressMsg::{Payload, Done};
#[cfg(test)]
fn assert_parse(url: &'static str,
content_type: Option<(String, String)>,
charset: Option<String>,
data: Option<Vec<u8>>) {
use std::sync::mpsc::channel;
use url::Url;
use net::data_loader::load;
let (start_chan, start_port) = channel();
load(LoadData::new(Url::parse(url).unwrap(), start_chan));
let response = start_port.recv().unwrap();
assert_eq!(&response.metadata.content_type, &content_type);
assert_eq!(&response.metadata.charset, &charset);
let progress = response.progress_port.recv().unwrap();
match data {
None => {
assert_eq!(progress, Done(Err("invalid data uri".to_string())));
}
Some(dat) => {
assert_eq!(progress, Payload(dat));
assert_eq!(response.progress_port.recv().unwrap(), Done(Ok(())));
}
}
}
#[test]
fn empty_invalid() {
assert_parse("data:", None, None, None);
}
#[test]
fn plain() {
assert_parse("data:,hello%20world", None, None, Some(b"hello world".iter().map(|&x| x).collect()));
}
#[test]
fn plain_ct() {
assert_parse("data:text/plain,hello",
Some(("text".to_string(), "plain".to_string())), None, Some(b"hello".iter().map(|&x| x).collect()));
}
#[test]
fn plain_charset() {
assert_parse("data:text/plain;charset=latin1,hello",
Some(("text".to_string(), "plain".to_string())), Some("latin1".to_string()), Some(b"hello".iter().map(|&x| x).collect()));
}
#[test]
fn base64() {
assert_parse("data:;base64,C62+7w==", None, None, Some(vec!(0x0B, 0xAD, 0xBE, 0xEF)));
}
#[test]
fn base64_ct() {
assert_parse("data:application/octet-stream;base64,C62+7w==",
Some(("application".to_string(), "octet-stream".to_string())), None, Some(vec!(0x0B, 0xAD, 0xBE, 0xEF)));
}
#[test]
fn base64_charset() {
assert_parse("data:text/plain;charset=koi8-r;base64,8PLl9+XkIO3l5Pfl5A==",
Some(("text".to_string(), "plain".to_string())), Some("koi8-r".to_string()),
Some(vec!(0xF0, 0xF2, 0xE5, 0xF7, 0xE5, 0xE4, 0x20, 0xED, 0xE5, 0xE4, 0xF7, 0xE5, 0xE4)));
}

View file

@ -0,0 +1,574 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
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);
}

8
tests/unit/net/mod.rs Normal file
View file

@ -0,0 +1,8 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
mod cookie;
mod data_loader;
mod image_cache_task;
mod resource_task;

View file

@ -0,0 +1,184 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use net::resource_task::{new_resource_task, parse_hostsfile, replace_hosts};
use net_traits::{ControlMsg, LoadData};
use net_traits::ProgressMsg;
use std::borrow::ToOwned;
use std::boxed;
use std::collections::HashMap;
use std::sync::mpsc::channel;
use url::Url;
#[test]
fn test_exit() {
let resource_task = new_resource_task(None);
resource_task.send(ControlMsg::Exit).unwrap();
}
#[test]
fn test_bad_scheme() {
let resource_task = new_resource_task(None);
let (start_chan, start) = channel();
let url = Url::parse("bogus://whatever").unwrap();
resource_task.send(ControlMsg::Load(LoadData::new(url, start_chan))).unwrap();
let response = start.recv().unwrap();
match response.progress_port.recv().unwrap() {
ProgressMsg::Done(result) => { assert!(result.is_err()) }
_ => panic!("bleh")
}
resource_task.send(ControlMsg::Exit).unwrap();
}
#[test]
fn test_parse_hostsfile() {
let mock_hosts_file_content = "127.0.0.1 foo.bar.com\n127.0.0.2 servo.test.server";
let hosts_table = parse_hostsfile(mock_hosts_file_content);
assert_eq!(2, (*hosts_table).len());
assert_eq!("127.0.0.1".to_owned(), *hosts_table.get(&"foo.bar.com".to_owned()).unwrap());
assert_eq!("127.0.0.2".to_owned(), *hosts_table.get(&"servo.test.server".to_owned()).unwrap());
}
#[test]
fn test_parse_malformed_hostsfile() {
let mock_hosts_file_content = "malformed file\n127.0.0.1 foo.bar.com\nservo.test.server 127.0.0.1";
let hosts_table = parse_hostsfile(mock_hosts_file_content);
assert_eq!(1, (*hosts_table).len());
assert_eq!("127.0.0.1".to_owned(), *hosts_table.get(&"foo.bar.com".to_owned()).unwrap());
}
#[test]
fn test_parse_hostsfile_with_line_comment() {
let mock_hosts_file_content = "# this is a line comment\n127.0.0.1 foo.bar.com\n# anothercomment";
let hosts_table = parse_hostsfile(mock_hosts_file_content);
assert_eq!(1, (*hosts_table).len());
assert_eq!("127.0.0.1".to_owned(), *hosts_table.get(&"foo.bar.com".to_owned()).unwrap());
}
#[test]
fn test_parse_hostsfile_with_end_of_line_comment() {
let mock_hosts_file_content = "127.0.0.1 foo.bar.com # line ending comment\n127.0.0.2 servo.test.server #comment";
let hosts_table = parse_hostsfile(mock_hosts_file_content);
assert_eq!(2, (*hosts_table).len());
assert_eq!("127.0.0.1".to_owned(), *hosts_table.get(&"foo.bar.com".to_owned()).unwrap());
assert_eq!("127.0.0.2".to_owned(), *hosts_table.get(&"servo.test.server".to_owned()).unwrap());
}
#[test]
fn test_parse_hostsfile_with_2_hostnames_for_1_address() {
let mock_hosts_file_content = "127.0.0.1 foo.bar.com baz.bar.com";
let hosts_table = parse_hostsfile(mock_hosts_file_content);
assert_eq!(2, (*hosts_table).len());
assert_eq!("127.0.0.1".to_owned(), *hosts_table.get(&"foo.bar.com".to_owned()).unwrap());
assert_eq!("127.0.0.1".to_owned(), *hosts_table.get(&"baz.bar.com".to_owned()).unwrap());
}
#[test]
fn test_parse_hostsfile_with_4_hostnames_for_1_address() {
let mock_hosts_file_content = "127.0.0.1 moz.foo.com moz.bar.com moz.baz.com moz.moz.com";
let hosts_table = parse_hostsfile(mock_hosts_file_content);
assert_eq!(4, (*hosts_table).len());
assert_eq!("127.0.0.1".to_owned(), *hosts_table.get(&"moz.foo.com".to_owned()).unwrap());
assert_eq!("127.0.0.1".to_owned(), *hosts_table.get(&"moz.bar.com".to_owned()).unwrap());
assert_eq!("127.0.0.1".to_owned(), *hosts_table.get(&"moz.baz.com".to_owned()).unwrap());
assert_eq!("127.0.0.1".to_owned(), *hosts_table.get(&"moz.moz.com".to_owned()).unwrap());
}
#[test]
fn test_parse_hostsfile_with_tabs_instead_spaces() {
let mock_hosts_file_content = "127.0.0.1\tfoo.bar.com\n127.0.0.2\tservo.test.server";
let hosts_table = parse_hostsfile(mock_hosts_file_content);
assert_eq!(2, (*hosts_table).len());
assert_eq!("127.0.0.1".to_owned(), *hosts_table.get(&"foo.bar.com".to_owned()).unwrap());
assert_eq!("127.0.0.2".to_owned(), *hosts_table.get(&"servo.test.server".to_owned()).unwrap());
}
#[test]
fn test_parse_hostsfile_with_valid_ipv4_addresses()
{
let mock_hosts_file_content = "255.255.255.255 foo.bar.com\n169.0.1.201 servo.test.server\n192.168.5.0 servo.foo.com";
let hosts_table = parse_hostsfile(mock_hosts_file_content);
assert_eq!(3, (*hosts_table).len());
}
#[test]
fn test_parse_hostsfile_with_invalid_ipv4_addresses()
{
let mock_hosts_file_content = "256.255.255.255 foo.bar.com\n169.0.1000.201 servo.test.server \
\n192.168.5.500 servo.foo.com\n192.abc.100.2 test.servo.com";
let hosts_table = parse_hostsfile(mock_hosts_file_content);
assert_eq!(0, (*hosts_table).len());
}
#[test]
fn test_parse_hostsfile_with_valid_ipv6_addresses()
{
let mock_hosts_file_content = "2001:0db8:0000:0000:0000:ff00:0042:8329 foo.bar.com\n\
2001:db8:0:0:0:ff00:42:8329 moz.foo.com\n\
2001:db8::ff00:42:8329 foo.moz.com moz.moz.com\n\
0000:0000:0000:0000:0000:0000:0000:0001 bar.moz.com\n\
::1 foo.bar.baz baz.foo.com\n\
2001:0DB8:85A3:0042:1000:8A2E:0370:7334 baz.bar.moz\n\
2002:0DB8:85A3:0042:1000:8A2E:0370:7334/96 baz2.bar.moz\n\
2002:0DB8:85A3:0042:1000:8A2E:0370:7334/128 baz3.bar.moz\n\
:: unspecified.moz.com\n\
::/128 unspecified.address.com";
let hosts_table = parse_hostsfile(mock_hosts_file_content);
assert_eq!(12, (*hosts_table).len());
}
#[test]
fn test_parse_hostsfile_with_invalid_ipv6_addresses()
{
let mock_hosts_file_content = "12001:0db8:0000:0000:0000:ff00:0042:8329 foo.bar.com\n\
2001:zdb8:0:0:0:gg00:42:t329 moz.foo.com\n\
2001:db8::ff00:42:8329:1111:1111:42 foo.moz.com moz.moz.com\n\
2002:0DB8:85A3:0042:1000:8A2E:0370:7334/1289 baz3.bar.moz";
let hosts_table = parse_hostsfile(mock_hosts_file_content);
assert_eq!(0, (*hosts_table).len());
}
#[test]
fn test_parse_hostsfile_with_end_of_line_whitespace()
{
let mock_hosts_file_content = "127.0.0.1 foo.bar.com \n\
2001:db8:0:0:0:ff00:42:8329 moz.foo.com\n \
127.0.0.2 servo.test.server ";
let hosts_table = parse_hostsfile(mock_hosts_file_content);
assert_eq!(3, (*hosts_table).len());
assert_eq!("127.0.0.1".to_owned(), *hosts_table.get(&"foo.bar.com".to_owned()).unwrap());
assert_eq!("2001:db8:0:0:0:ff00:42:8329".to_owned(), *hosts_table.get(&"moz.foo.com".to_owned()).unwrap());
assert_eq!("127.0.0.2".to_owned(), *hosts_table.get(&"servo.test.server".to_owned()).unwrap());
}
#[test]
fn test_replace_hosts() {
use std::net::TcpListener;
let mut host_table_box = Box::new(HashMap::new());
host_table_box.insert("foo.bar.com".to_owned(), "127.0.0.1".to_owned());
host_table_box.insert("servo.test.server".to_owned(), "127.0.0.2".to_owned());
let host_table: *mut HashMap<String, String> = unsafe {
boxed::into_raw(host_table_box)
};
//Start the TCP server
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let port = listener.socket_addr().unwrap().port();
//Start the resource task and make a request to our TCP server
let resource_task = new_resource_task(None);
let (start_chan, _) = channel();
let url = Url::parse(&format!("http://foo.bar.com:{}", port)).unwrap();
resource_task.send(ControlMsg::Load(replace_hosts(LoadData::new(url, start_chan), host_table))).unwrap();
match listener.accept() {
Ok(..) => assert!(true, "received request"),
Err(_) => assert!(false, "error")
}
resource_task.send(ControlMsg::Exit).unwrap();
}

BIN
tests/unit/net/test.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

6
tests/unit/script/mod.rs Normal file
View file

@ -0,0 +1,6 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#[cfg(target_pointer_width = "64")] mod size_of;
mod textinput;

View file

@ -0,0 +1,48 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use script::dom::characterdata::CharacterData;
use script::dom::element::Element;
use script::dom::eventtarget::EventTarget;
use script::dom::htmldivelement::HTMLDivElement;
use script::dom::htmlelement::HTMLElement;
use script::dom::htmlspanelement::HTMLSpanElement;
use script::dom::node::Node;
use script::dom::text::Text;
use std::mem::size_of;
// Macro so that we can stringify type names
// I'd really prefer the tests themselves to be run at plugin time,
// however rustc::middle doesn't have access to the full type data
macro_rules! sizeof_checker (
($testname: ident, $t:ty, $known_size:expr) => (
#[test]
fn $testname() {
let new = size_of::<$t>();
let old = $known_size;
if new < old {
panic!("Your changes have decreased the stack size of commonly used DOM struct {} from {} to {}. \
Good work! Please update the size in script/tests.rs",
stringify!($t), old, new)
} else if new > old {
panic!("Your changes have increased the stack size of commonly used DOM struct {} from {} to {}. \
These structs are present in large quantities in the DOM, and increasing the size \
may dramatically affect our memory footprint. Please consider choosing a design which \
avoids this increase. If you feel that the increase is necessary, \
update to the new size in script/tests.rs.",
stringify!($t), old, new)
}
});
);
// Update the sizes here
sizeof_checker!(size_event_target, EventTarget, 48);
sizeof_checker!(size_node, Node, 216);
sizeof_checker!(size_element, Element, 328);
sizeof_checker!(size_htmlelement, HTMLElement, 344);
sizeof_checker!(size_div, HTMLDivElement, 344);
sizeof_checker!(size_span, HTMLSpanElement, 344);
sizeof_checker!(size_text, Text, 248);
sizeof_checker!(size_characterdata, CharacterData, 248);

View file

@ -0,0 +1,167 @@
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use script::textinput::{TextInput, Selection, Lines, DeleteDir};
use std::borrow::ToOwned;
#[test]
fn test_textinput_delete_char() {
let mut textinput = TextInput::new(Lines::Single, "abcdefg".to_owned());
textinput.adjust_horizontal(2, Selection::NotSelected);
textinput.delete_char(DeleteDir::Backward);
assert_eq!(textinput.get_content(), "acdefg");
textinput.delete_char(DeleteDir::Forward);
assert_eq!(textinput.get_content(), "adefg");
textinput.adjust_horizontal(2, Selection::Selected);
textinput.delete_char(DeleteDir::Forward);
assert_eq!(textinput.get_content(), "afg");
}
#[test]
fn test_textinput_insert_char() {
let mut textinput = TextInput::new(Lines::Single, "abcdefg".to_owned());
textinput.adjust_horizontal(2, Selection::NotSelected);
textinput.insert_char('a');
assert_eq!(textinput.get_content(), "abacdefg");
textinput.adjust_horizontal(2, Selection::Selected);
textinput.insert_char('b');
assert_eq!(textinput.get_content(), "ababefg");
}
#[test]
fn test_textinput_get_sorted_selection() {
let mut textinput = TextInput::new(Lines::Single, "abcdefg".to_owned());
textinput.adjust_horizontal(2, Selection::NotSelected);
textinput.adjust_horizontal(2, Selection::Selected);
let (begin, end) = textinput.get_sorted_selection();
assert_eq!(begin.index, 2);
assert_eq!(end.index, 4);
textinput.clear_selection();
textinput.adjust_horizontal(-2, Selection::Selected);
let (begin, end) = textinput.get_sorted_selection();
assert_eq!(begin.index, 2);
assert_eq!(end.index, 4);
}
#[test]
fn test_textinput_replace_selection() {
let mut textinput = TextInput::new(Lines::Single, "abcdefg".to_owned());
textinput.adjust_horizontal(2, Selection::NotSelected);
textinput.adjust_horizontal(2, Selection::Selected);
textinput.replace_selection("xyz".to_owned());
assert_eq!(textinput.get_content(), "abxyzefg");
}
#[test]
fn test_textinput_current_line_length() {
let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned());
assert_eq!(textinput.current_line_length(), 3);
textinput.adjust_vertical(1, Selection::NotSelected);
assert_eq!(textinput.current_line_length(), 2);
textinput.adjust_vertical(1, Selection::NotSelected);
assert_eq!(textinput.current_line_length(), 1);
}
#[test]
fn test_textinput_adjust_vertical() {
let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned());
textinput.adjust_horizontal(3, Selection::NotSelected);
textinput.adjust_vertical(1, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 1);
assert_eq!(textinput.edit_point.index, 2);
textinput.adjust_vertical(-1, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point.index, 2);
textinput.adjust_vertical(2, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 2);
assert_eq!(textinput.edit_point.index, 1);
}
#[test]
fn test_textinput_adjust_horizontal() {
let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned());
textinput.adjust_horizontal(4, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 1);
assert_eq!(textinput.edit_point.index, 0);
textinput.adjust_horizontal(1, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 1);
assert_eq!(textinput.edit_point.index, 1);
textinput.adjust_horizontal(2, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 2);
assert_eq!(textinput.edit_point.index, 0);
textinput.adjust_horizontal(-1, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 1);
assert_eq!(textinput.edit_point.index, 2);
}
#[test]
fn test_textinput_handle_return() {
let mut single_line_textinput = TextInput::new(Lines::Single, "abcdef".to_owned());
single_line_textinput.adjust_horizontal(3, Selection::NotSelected);
single_line_textinput.handle_return();
assert_eq!(single_line_textinput.get_content(), "abcdef");
let mut multi_line_textinput = TextInput::new(Lines::Multiple, "abcdef".to_owned());
multi_line_textinput.adjust_horizontal(3, Selection::NotSelected);
multi_line_textinput.handle_return();
assert_eq!(multi_line_textinput.get_content(), "abc\ndef");
}
#[test]
fn test_textinput_select_all() {
let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned());
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point.index, 0);
textinput.select_all();
assert_eq!(textinput.edit_point.line, 2);
assert_eq!(textinput.edit_point.index, 1);
}
#[test]
fn test_textinput_get_content() {
let single_line_textinput = TextInput::new(Lines::Single, "abcdefg".to_owned());
assert_eq!(single_line_textinput.get_content(), "abcdefg");
let multi_line_textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned());
assert_eq!(multi_line_textinput.get_content(), "abc\nde\nf");
}
#[test]
fn test_textinput_set_content() {
let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned());
assert_eq!(textinput.get_content(), "abc\nde\nf");
textinput.set_content("abc\nf".to_owned());
assert_eq!(textinput.get_content(), "abc\nf");
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point.index, 0);
textinput.adjust_horizontal(3, Selection::Selected);
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point.index, 3);
textinput.set_content("de".to_owned());
assert_eq!(textinput.get_content(), "de");
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point.index, 2);
}

View file

@ -0,0 +1,416 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use geom::size::TypedSize2D;
use style::stylesheets::{iter_stylesheet_media_rules, iter_stylesheet_style_rules, Stylesheet};
use style::stylesheets::Origin;
use style::media_queries::*;
use style::values::specified;
use std::borrow::ToOwned;
use url::Url;
use util::geometry::Au;
fn test_media_rule<F>(css: &str, callback: F) where F: Fn(&MediaQueryList, &str) {
let url = Url::parse("http://localhost").unwrap();
let stylesheet = Stylesheet::from_str(css, url, Origin::Author);
let mut rule_count = 0;
iter_stylesheet_media_rules(&stylesheet, |rule| {
rule_count += 1;
callback(&rule.media_queries, css);
});
assert!(rule_count > 0);
}
fn media_query_test(device: &Device, css: &str, expected_rule_count: u32) {
let url = Url::parse("http://localhost").unwrap();
let ss = Stylesheet::from_str(css, url, Origin::Author);
let mut rule_count: u32 = 0;
iter_stylesheet_style_rules(&ss, device, |_| rule_count += 1);
assert!(rule_count == expected_rule_count, css.to_owned());
}
#[test]
fn test_mq_empty() {
test_media_rule("@media { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_mq_screen() {
test_media_rule("@media screen { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media only screen { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media not screen { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_mq_print() {
test_media_rule("@media print { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media only print { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media not print { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_mq_unknown() {
test_media_rule("@media fridge { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media only glass { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media not wood { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_mq_all() {
test_media_rule("@media all { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media only all { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media not all { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_mq_or() {
test_media_rule("@media screen, print { }", |list, css| {
assert!(list.media_queries.len() == 2, css.to_owned());
let q0 = &list.media_queries[0];
assert!(q0.qualifier == None, css.to_owned());
assert!(q0.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q0.expressions.len() == 0, css.to_owned());
let q1 = &list.media_queries[1];
assert!(q1.qualifier == None, css.to_owned());
assert!(q1.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q1.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_mq_default_expressions() {
test_media_rule("@media (min-width: 100px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Min(w)) => assert!(w == specified::Length::Absolute(Au::from_px(100))),
_ => panic!("wrong expression type"),
}
});
test_media_rule("@media (max-width: 43px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(43))),
_ => panic!("wrong expression type"),
}
});
}
#[test]
fn test_mq_expressions() {
test_media_rule("@media screen and (min-width: 100px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Min(w)) => assert!(w == specified::Length::Absolute(Au::from_px(100))),
_ => panic!("wrong expression type"),
}
});
test_media_rule("@media print and (max-width: 43px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(43))),
_ => panic!("wrong expression type"),
}
});
test_media_rule("@media fridge and (max-width: 52px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(52))),
_ => panic!("wrong expression type"),
}
});
}
#[test]
fn test_mq_multiple_expressions() {
test_media_rule("@media (min-width: 100px) and (max-width: 200px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 2, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Min(w)) => assert!(w == specified::Length::Absolute(Au::from_px(100))),
_ => panic!("wrong expression type"),
}
match q.expressions[1] {
Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(200))),
_ => panic!("wrong expression type"),
}
});
test_media_rule("@media not screen and (min-width: 100px) and (max-width: 200px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 2, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Min(w)) => assert!(w == specified::Length::Absolute(Au::from_px(100))),
_ => panic!("wrong expression type"),
}
match q.expressions[1] {
Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(200))),
_ => panic!("wrong expression type"),
}
});
}
#[test]
fn test_mq_malformed_expressions() {
test_media_rule("@media (min-width: 100blah) and (max-width: 200px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media screen and (height: 200px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media (min-width: 30em foo bar) {}", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media not {}", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media not (min-width: 300px) {}", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media , {}", |list, css| {
assert!(list.media_queries.len() == 2, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
let q = &list.media_queries[1];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media screen 4px, print {}", |list, css| {
assert!(list.media_queries.len() == 2, css.to_owned());
let q0 = &list.media_queries[0];
assert!(q0.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q0.media_type == MediaQueryType::All, css.to_owned());
assert!(q0.expressions.len() == 0, css.to_owned());
let q1 = &list.media_queries[1];
assert!(q1.qualifier == None, css.to_owned());
assert!(q1.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q1.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media screen, {}", |list, css| {
assert!(list.media_queries.len() == 2, css.to_owned());
let q0 = &list.media_queries[0];
assert!(q0.qualifier == None, css.to_owned());
assert!(q0.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q0.expressions.len() == 0, css.to_owned());
let q1 = &list.media_queries[1];
assert!(q1.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q1.media_type == MediaQueryType::All, css.to_owned());
assert!(q1.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_matching_simple() {
let device = Device {
media_type: MediaType::Screen,
viewport_size: TypedSize2D(200.0, 100.0),
};
media_query_test(&device, "@media not all { a { color: red; } }", 0);
media_query_test(&device, "@media not screen { a { color: red; } }", 0);
media_query_test(&device, "@media not print { a { color: red; } }", 1);
media_query_test(&device, "@media unknown { a { color: red; } }", 0);
media_query_test(&device, "@media not unknown { a { color: red; } }", 1);
media_query_test(&device, "@media { a { color: red; } }", 1);
media_query_test(&device, "@media screen { a { color: red; } }", 1);
media_query_test(&device, "@media print { a { color: red; } }", 0);
}
#[test]
fn test_matching_width() {
let device = Device {
media_type: MediaType::Screen,
viewport_size: TypedSize2D(200.0, 100.0),
};
media_query_test(&device, "@media { a { color: red; } }", 1);
media_query_test(&device, "@media (min-width: 50px) { a { color: red; } }", 1);
media_query_test(&device, "@media (min-width: 150px) { a { color: red; } }", 1);
media_query_test(&device, "@media (min-width: 300px) { a { color: red; } }", 0);
media_query_test(&device, "@media screen and (min-width: 50px) { a { color: red; } }", 1);
media_query_test(&device, "@media screen and (min-width: 150px) { a { color: red; } }", 1);
media_query_test(&device, "@media screen and (min-width: 300px) { a { color: red; } }", 0);
media_query_test(&device, "@media not screen and (min-width: 50px) { a { color: red; } }", 0);
media_query_test(&device, "@media not screen and (min-width: 150px) { a { color: red; } }", 0);
media_query_test(&device, "@media not screen and (min-width: 300px) { a { color: red; } }", 1);
media_query_test(&device, "@media (max-width: 50px) { a { color: red; } }", 0);
media_query_test(&device, "@media (max-width: 150px) { a { color: red; } }", 0);
media_query_test(&device, "@media (max-width: 300px) { a { color: red; } }", 1);
media_query_test(&device, "@media screen and (min-width: 50px) and (max-width: 100px) { a { color: red; } }", 0);
media_query_test(&device, "@media screen and (min-width: 250px) and (max-width: 300px) { a { color: red; } }", 0);
media_query_test(&device, "@media screen and (min-width: 50px) and (max-width: 250px) { a { color: red; } }", 1);
media_query_test(&device, "@media not screen and (min-width: 50px) and (max-width: 100px) { a { color: red; } }", 1);
media_query_test(&device, "@media not screen and (min-width: 250px) and (max-width: 300px) { a { color: red; } }", 1);
media_query_test(&device, "@media not screen and (min-width: 50px) and (max-width: 250px) { a { color: red; } }", 0);
media_query_test(&device, "@media not screen and (min-width: 3.1em) and (max-width: 6em) { a { color: red; } }", 1);
media_query_test(&device, "@media not screen and (min-width: 16em) and (max-width: 19.75em) { a { color: red; } }", 1);
media_query_test(&device, "@media not screen and (min-width: 3em) and (max-width: 250px) { a { color: red; } }", 0);
}
#[test]
fn test_matching_invalid() {
let device = Device {
media_type: MediaType::Screen,
viewport_size: TypedSize2D(200.0, 100.0),
};
media_query_test(&device, "@media fridge { a { color: red; } }", 0);
media_query_test(&device, "@media screen and (height: 100px) { a { color: red; } }", 0);
media_query_test(&device, "@media not print and (width: 100) { a { color: red; } }", 0);
}

16
tests/unit/style/mod.rs Normal file
View file

@ -0,0 +1,16 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use util::logical_geometry::WritingMode;
use style::properties::{INITIAL_VALUES, get_writing_mode};
mod stylesheets;
mod media_queries;
#[test]
fn initial_writing_mode_is_empty() {
assert_eq!(get_writing_mode(INITIAL_VALUES.get_inheritedbox()), WritingMode::empty())
}

View file

@ -0,0 +1,138 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::borrow::ToOwned;
use std::sync::Arc;
use cssparser;
use selectors::parser::*;
use string_cache::Atom;
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, DeclaredValue, longhands};
use style::stylesheets::{CSSRule, StyleRule, Origin, Stylesheet};
use url::Url;
#[test]
fn test_parse_stylesheet() {
let css = r"
@namespace url(http://www.w3.org/1999/xhtml);
/* FIXME: only if scripting is enabled */
input[type=hidden i] { display: none !important; }
html , body /**/ { display: block; }
#d1 > .ok { background: blue; }
";
let url = Url::parse("about::test").unwrap();
let stylesheet = Stylesheet::from_str(css, url, Origin::UserAgent);
assert_eq!(stylesheet, Stylesheet {
origin: Origin::UserAgent,
rules: vec![
CSSRule::Namespace(None, ns!(HTML)),
CSSRule::Style(StyleRule {
selectors: vec![
Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec![
SimpleSelector::Namespace(ns!(HTML)),
SimpleSelector::LocalName(LocalName {
name: atom!(input),
lower_name: atom!(input),
}),
SimpleSelector::AttrEqual(AttrSelector {
name: atom!(type),
lower_name: atom!(type),
namespace: NamespaceConstraint::Specific(ns!("")),
}, "hidden".to_owned(), CaseSensitivity::CaseInsensitive)
],
next: None,
}),
pseudo_element: None,
specificity: (0 << 20) + (1 << 10) + (1 << 0),
},
],
declarations: PropertyDeclarationBlock {
normal: Arc::new(vec![]),
important: Arc::new(vec![
PropertyDeclaration::Display(DeclaredValue::SpecifiedValue(
longhands::display::SpecifiedValue::none)),
]),
},
}),
CSSRule::Style(StyleRule {
selectors: vec![
Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec![
SimpleSelector::Namespace(ns!(HTML)),
SimpleSelector::LocalName(LocalName {
name: atom!(html),
lower_name: atom!(html),
}),
],
next: None,
}),
pseudo_element: None,
specificity: (0 << 20) + (0 << 10) + (1 << 0),
},
Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec![
SimpleSelector::Namespace(ns!(HTML)),
SimpleSelector::LocalName(LocalName {
name: atom!(body),
lower_name: atom!(body),
}),
],
next: None,
}),
pseudo_element: None,
specificity: (0 << 20) + (0 << 10) + (1 << 0),
},
],
declarations: PropertyDeclarationBlock {
normal: Arc::new(vec![
PropertyDeclaration::Display(DeclaredValue::SpecifiedValue(
longhands::display::SpecifiedValue::block)),
]),
important: Arc::new(vec![]),
},
}),
CSSRule::Style(StyleRule {
selectors: vec![
Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec![
SimpleSelector::Class(Atom::from_slice("ok")),
],
next: Some((Box::new(CompoundSelector {
simple_selectors: vec![
SimpleSelector::ID(Atom::from_slice("d1")),
],
next: None,
}), Combinator::Child)),
}),
pseudo_element: None,
specificity: (1 << 20) + (1 << 10) + (0 << 0),
},
],
declarations: PropertyDeclarationBlock {
normal: Arc::new(vec![
PropertyDeclaration::BackgroundSize(DeclaredValue::Initial),
PropertyDeclaration::BackgroundImage(DeclaredValue::Initial),
PropertyDeclaration::BackgroundAttachment(DeclaredValue::Initial),
PropertyDeclaration::BackgroundRepeat(DeclaredValue::Initial),
PropertyDeclaration::BackgroundPosition(DeclaredValue::Initial),
PropertyDeclaration::BackgroundColor(DeclaredValue::SpecifiedValue(
longhands::background_color::SpecifiedValue {
authored: Some("blue".to_owned()),
parsed: cssparser::Color::RGBA(cssparser::RGBA {
red: 0., green: 0., blue: 1., alpha: 1.
}),
}
)),
]),
important: Arc::new(vec![]),
},
}),
],
});
}

53
tests/unit/util/cache.rs Normal file
View file

@ -0,0 +1,53 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::cell::Cell;
use util::cache::{HashCache, LRUCache};
#[test]
fn test_hashcache() {
let mut cache: HashCache<usize, Cell<&str>> = HashCache::new();
cache.insert(1, Cell::new("one"));
assert!(cache.find(&1).is_some());
assert!(cache.find(&2).is_none());
cache.find_or_create(&2, |_v| { Cell::new("two") });
assert!(cache.find(&1).is_some());
assert!(cache.find(&2).is_some());
}
#[test]
fn test_lru_cache() {
let one = Cell::new("one");
let two = Cell::new("two");
let three = Cell::new("three");
let four = Cell::new("four");
// Test normal insertion.
let mut cache: LRUCache<usize,Cell<&str>> = LRUCache::new(2); // (_, _) (cache is empty)
cache.insert(1, one); // (1, _)
cache.insert(2, two); // (1, 2)
cache.insert(3, three); // (2, 3)
assert!(cache.find(&1).is_none()); // (2, 3) (no change)
assert!(cache.find(&3).is_some()); // (2, 3)
assert!(cache.find(&2).is_some()); // (3, 2)
// Test that LRU works (this insertion should replace 3, not 2).
cache.insert(4, four); // (2, 4)
assert!(cache.find(&1).is_none()); // (2, 4) (no change)
assert!(cache.find(&2).is_some()); // (4, 2)
assert!(cache.find(&3).is_none()); // (4, 2) (no change)
assert!(cache.find(&4).is_some()); // (2, 4) (no change)
// Test find_or_create.
cache.find_or_create(&1, |_| { Cell::new("one") }); // (4, 1)
assert!(cache.find(&1).is_some()); // (4, 1) (no change)
assert!(cache.find(&2).is_none()); // (4, 1) (no change)
assert!(cache.find(&3).is_none()); // (4, 1) (no change)
assert!(cache.find(&4).is_some()); // (1, 4)
}

View file

@ -0,0 +1,69 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use geom::{Size2D, Point2D, SideOffsets2D, Rect};
use util::logical_geometry::{WritingMode, LogicalSize, LogicalPoint, LogicalMargin, LogicalRect,
FLAG_RTL, FLAG_VERTICAL, FLAG_VERTICAL_LR, FLAG_SIDEWAYS_LEFT};
#[cfg(test)]
fn modes() -> [WritingMode; 10] {
[
WritingMode::empty(),
FLAG_VERTICAL,
FLAG_VERTICAL | FLAG_VERTICAL_LR,
FLAG_VERTICAL | FLAG_VERTICAL_LR | FLAG_SIDEWAYS_LEFT,
FLAG_VERTICAL | FLAG_SIDEWAYS_LEFT,
FLAG_RTL,
FLAG_VERTICAL | FLAG_RTL,
FLAG_VERTICAL | FLAG_VERTICAL_LR | FLAG_RTL,
FLAG_VERTICAL | FLAG_VERTICAL_LR | FLAG_SIDEWAYS_LEFT | FLAG_RTL,
FLAG_VERTICAL | FLAG_SIDEWAYS_LEFT | FLAG_RTL,
]
}
#[test]
fn test_size_round_trip() {
let physical = Size2D(1u32, 2u32);
for &mode in modes().iter() {
let logical = LogicalSize::from_physical(mode, physical);
assert!(logical.to_physical(mode) == physical);
assert!(logical.width(mode) == 1);
assert!(logical.height(mode) == 2);
}
}
#[test]
fn test_point_round_trip() {
let physical = Point2D(1u32, 2u32);
let container = Size2D(100, 200);
for &mode in modes().iter() {
let logical = LogicalPoint::from_physical(mode, physical, container);
assert!(logical.to_physical(mode, container) == physical);
assert!(logical.x(mode, container) == 1);
assert!(logical.y(mode, container) == 2);
}
}
#[test]
fn test_margin_round_trip() {
let physical = SideOffsets2D::new(1u32, 2u32, 3u32, 4u32);
for &mode in modes().iter() {
let logical = LogicalMargin::from_physical(mode, physical);
assert!(logical.to_physical(mode) == physical);
assert!(logical.top(mode) == 1);
assert!(logical.right(mode) == 2);
assert!(logical.bottom(mode) == 3);
assert!(logical.left(mode) == 4);
}
}
#[test]
fn test_rect_round_trip() {
let physical = Rect(Point2D(1u32, 2u32), Size2D(3u32, 4u32));
let container = Size2D(100, 200);
for &mode in modes().iter() {
let logical = LogicalRect::from_physical(mode, physical, container);
assert!(logical.to_physical(mode, container) == physical);
}
}

8
tests/unit/util/mod.rs Normal file
View file

@ -0,0 +1,8 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
mod cache;
mod logical_geometry;
mod task;
mod vec;

13
tests/unit/util/task.rs Normal file
View file

@ -0,0 +1,13 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::borrow::ToOwned;
use util::task::spawn_named;
#[test]
fn spawn_named_test() {
spawn_named("Test".to_owned(), move || {
println!("I can run!");
});
}

68
tests/unit/util/vec.rs Normal file
View file

@ -0,0 +1,68 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::fmt::Debug;
use util::vec::BinarySearchMethods;
#[cfg(test)]
fn test_find_all_elems<T: PartialEq + PartialOrd + Eq + Ord>(arr: &[T]) {
let mut i = 0;
while i < arr.len() {
assert!(test_match(&arr[i], arr.binary_search_(&arr[i])));
i += 1;
}
}
#[cfg(test)]
fn test_miss_all_elems<T: PartialEq + PartialOrd + Eq + Ord + Debug>(arr: &[T], misses: &[T]) {
let mut i = 0;
while i < misses.len() {
let res = arr.binary_search_(&misses[i]);
println!("{:?} == {:?} ?", misses[i], res);
assert!(!test_match(&misses[i], arr.binary_search_(&misses[i])));
i += 1;
}
}
#[cfg(test)]
fn test_match<T: PartialEq>(b: &T, a: Option<&T>) -> bool {
match a {
None => false,
Some(t) => t == b
}
}
#[test]
fn should_find_all_elements() {
let arr_odd = [1u32, 2, 4, 6, 7, 8, 9];
let arr_even = [1u32, 2, 5, 6, 7, 8, 9, 42];
let arr_double = [1u32, 1, 2, 2, 6, 8, 22];
let arr_one = [234986325u32];
let arr_two = [3044u32, 8393];
let arr_three = [12u32, 23, 34];
test_find_all_elems(&arr_odd);
test_find_all_elems(&arr_even);
test_find_all_elems(&arr_double);
test_find_all_elems(&arr_one);
test_find_all_elems(&arr_two);
test_find_all_elems(&arr_three);
}
#[test]
fn should_not_find_missing_elements() {
let arr_odd = [1u32, 2, 4, 6, 7, 8, 9];
let arr_even = [1u32, 2, 5, 6, 7, 8, 9, 42];
let arr_double = [1u32, 1, 2, 2, 6, 8, 22];
let arr_one = [234986325u32];
let arr_two = [3044u32, 8393];
let arr_three = [12u32, 23, 34];
test_miss_all_elems(&arr_odd, &[-22, 0, 3, 5, 34938, 10, 11, 12]);
test_miss_all_elems(&arr_even, &[-1, 0, 3, 34938, 10, 11, 12]);
test_miss_all_elems(&arr_double, &[-1, 0, 3, 4, 34938, 10, 11, 12, 234, 234, 33]);
test_miss_all_elems(&arr_one, &[-1, 0, 3, 34938, 10, 11, 12, 234, 234, 33]);
test_miss_all_elems(&arr_two, &[-1, 0, 3, 34938, 10, 11, 12, 234, 234, 33]);
test_miss_all_elems(&arr_three, &[-2, 0, 1, 2, 3, 34938, 10, 11, 234, 234, 33]);
}