summaryrefslogtreecommitdiffstats
path: root/servo/tests/unit/style/parsing
diff options
context:
space:
mode:
Diffstat (limited to 'servo/tests/unit/style/parsing')
-rw-r--r--servo/tests/unit/style/parsing/animation.rs21
-rw-r--r--servo/tests/unit/style/parsing/background.rs233
-rw-r--r--servo/tests/unit/style/parsing/border.rs219
-rw-r--r--servo/tests/unit/style/parsing/box_.rs22
-rw-r--r--servo/tests/unit/style/parsing/column.rs30
-rw-r--r--servo/tests/unit/style/parsing/effects.rs99
-rw-r--r--servo/tests/unit/style/parsing/image.rs154
-rw-r--r--servo/tests/unit/style/parsing/inherited_text.rs46
-rw-r--r--servo/tests/unit/style/parsing/mod.rs150
-rw-r--r--servo/tests/unit/style/parsing/outline.rs26
-rw-r--r--servo/tests/unit/style/parsing/position.rs145
-rw-r--r--servo/tests/unit/style/parsing/selectors.rs35
-rw-r--r--servo/tests/unit/style/parsing/supports.rs19
-rw-r--r--servo/tests/unit/style/parsing/text_overflow.rs30
-rw-r--r--servo/tests/unit/style/parsing/transition_duration.rs17
-rw-r--r--servo/tests/unit/style/parsing/transition_timing_function.rs62
16 files changed, 1308 insertions, 0 deletions
diff --git a/servo/tests/unit/style/parsing/animation.rs b/servo/tests/unit/style/parsing/animation.rs
new file mode 100644
index 0000000000..1b7481c4af
--- /dev/null
+++ b/servo/tests/unit/style/parsing/animation.rs
@@ -0,0 +1,21 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+use parsing::parse;
+use servo_atoms::Atom;
+use style::parser::Parse;
+use style::properties::longhands::animation_name;
+use style::values::specified::AnimationIterationCount;
+use style::values::{CustomIdent, KeyframesName};
+use style_traits::ToCss;
+
+#[test]
+fn test_animation_iteration() {
+ assert_roundtrip_with_context!(AnimationIterationCount::parse, "0", "0");
+ assert_roundtrip_with_context!(AnimationIterationCount::parse, "0.1", "0.1");
+ assert_roundtrip_with_context!(AnimationIterationCount::parse, "infinite", "infinite");
+
+ // Negative numbers are invalid
+ assert!(parse(AnimationIterationCount::parse, "-1").is_err());
+}
diff --git a/servo/tests/unit/style/parsing/background.rs b/servo/tests/unit/style/parsing/background.rs
new file mode 100644
index 0000000000..55b1dbcd25
--- /dev/null
+++ b/servo/tests/unit/style/parsing/background.rs
@@ -0,0 +1,233 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+use parsing::parse;
+use style::properties::longhands::background_size;
+use style::properties::longhands::{
+ background_attachment, background_clip, background_color, background_image,
+};
+use style::properties::longhands::{
+ background_origin, background_position_x, background_position_y, background_repeat,
+};
+use style::properties::shorthands::background;
+
+#[test]
+fn background_shorthand_should_parse_all_available_properties_when_specified() {
+ let input = "url(\"http://servo/test.png\") top center / 200px 200px repeat-x fixed padding-box content-box red";
+ let result = parse(background::parse_value, input).unwrap();
+
+ assert_eq!(
+ result.background_image,
+ parse_longhand!(background_image, "url(\"http://servo/test.png\")")
+ );
+ assert_eq!(
+ result.background_position_x,
+ parse_longhand!(background_position_x, "center")
+ );
+ assert_eq!(
+ result.background_position_y,
+ parse_longhand!(background_position_y, "top")
+ );
+ assert_eq!(
+ result.background_size,
+ parse_longhand!(background_size, "200px 200px")
+ );
+ assert_eq!(
+ result.background_repeat,
+ parse_longhand!(background_repeat, "repeat-x")
+ );
+ assert_eq!(
+ result.background_attachment,
+ parse_longhand!(background_attachment, "fixed")
+ );
+ assert_eq!(
+ result.background_origin,
+ parse_longhand!(background_origin, "padding-box")
+ );
+ assert_eq!(
+ result.background_clip,
+ parse_longhand!(background_clip, "content-box")
+ );
+ assert_eq!(
+ result.background_color,
+ parse_longhand!(background_color, "red")
+ );
+}
+
+#[test]
+fn background_shorthand_should_parse_when_some_fields_set() {
+ let result = parse(background::parse_value, "14px 40px repeat-y").unwrap();
+
+ assert_eq!(
+ result.background_position_x,
+ parse_longhand!(background_position_x, "14px")
+ );
+ assert_eq!(
+ result.background_position_y,
+ parse_longhand!(background_position_y, "40px")
+ );
+ assert_eq!(
+ result.background_repeat,
+ parse_longhand!(background_repeat, "repeat-y")
+ );
+
+ let result = parse(
+ background::parse_value,
+ "url(\"http://servo/test.png\") repeat blue",
+ )
+ .unwrap();
+
+ assert_eq!(
+ result.background_image,
+ parse_longhand!(background_image, "url(\"http://servo/test.png\")")
+ );
+ assert_eq!(
+ result.background_repeat,
+ parse_longhand!(background_repeat, "repeat")
+ );
+ assert_eq!(
+ result.background_color,
+ parse_longhand!(background_color, "blue")
+ );
+
+ let result = parse(background::parse_value, "padding-box").unwrap();
+
+ assert_eq!(
+ result.background_origin,
+ parse_longhand!(background_origin, "padding-box")
+ );
+ assert_eq!(
+ result.background_clip,
+ parse_longhand!(background_clip, "padding-box")
+ );
+
+ let result = parse(background::parse_value, "url(\"http://servo/test.png\")").unwrap();
+
+ assert_eq!(
+ result.background_image,
+ parse_longhand!(background_image, "url(\"http://servo/test.png\")")
+ );
+}
+
+#[test]
+fn background_shorthand_should_parse_comma_separated_declarations() {
+ let input =
+ "url(\"http://servo/test.png\") top left no-repeat, url(\"http://servo/test.png\") \
+ center / 100% 100% no-repeat, white";
+ let result = parse(background::parse_value, input).unwrap();
+
+ assert_eq!(
+ result.background_image,
+ parse_longhand!(
+ background_image,
+ "url(\"http://servo/test.png\"), \
+ url(\"http://servo/test.png\"), none"
+ )
+ );
+ assert_eq!(
+ result.background_position_x,
+ parse_longhand!(background_position_x, "left, center, 0%")
+ );
+ assert_eq!(
+ result.background_position_y,
+ parse_longhand!(background_position_y, "top, center, 0%")
+ );
+ assert_eq!(
+ result.background_repeat,
+ parse_longhand!(background_repeat, "no-repeat, no-repeat, repeat")
+ );
+ assert_eq!(
+ result.background_clip,
+ parse_longhand!(background_clip, "border-box, border-box, border-box")
+ );
+ assert_eq!(
+ result.background_origin,
+ parse_longhand!(
+ background_origin,
+ "padding-box, padding-box, \
+ padding-box"
+ )
+ );
+ assert_eq!(
+ result.background_size,
+ parse_longhand!(background_size, "auto auto, 100% 100%, auto auto")
+ );
+ assert_eq!(
+ result.background_attachment,
+ parse_longhand!(background_attachment, "scroll, scroll, scroll")
+ );
+ assert_eq!(
+ result.background_color,
+ parse_longhand!(background_color, "white")
+ );
+}
+
+#[test]
+fn background_shorthand_should_parse_position_and_size_correctly() {
+ let result = parse(background::parse_value, "7px 4px").unwrap();
+
+ assert_eq!(
+ result.background_position_x,
+ parse_longhand!(background_position_x, "7px")
+ );
+ assert_eq!(
+ result.background_position_y,
+ parse_longhand!(background_position_y, "4px")
+ );
+
+ let result = parse(background::parse_value, "7px 4px / 30px 20px").unwrap();
+
+ assert_eq!(
+ result.background_position_x,
+ parse_longhand!(background_position_x, "7px")
+ );
+ assert_eq!(
+ result.background_position_y,
+ parse_longhand!(background_position_y, "4px")
+ );
+ assert_eq!(
+ result.background_size,
+ parse_longhand!(background_size, "30px 20px")
+ );
+
+ assert!(parse(background::parse_value, "/ 30px 20px").is_err());
+
+ assert!(parse(background::parse_value, "repeat-x / 30px 20px").is_err());
+}
+
+#[test]
+fn background_shorthand_should_parse_origin_and_clip_correctly() {
+ let result = parse(background::parse_value, "padding-box content-box").unwrap();
+
+ assert_eq!(
+ result.background_origin,
+ parse_longhand!(background_origin, "padding-box")
+ );
+ assert_eq!(
+ result.background_clip,
+ parse_longhand!(background_clip, "content-box")
+ );
+
+ let result = parse(background::parse_value, "padding-box padding-box").unwrap();
+
+ assert_eq!(
+ result.background_origin,
+ parse_longhand!(background_origin, "padding-box")
+ );
+ assert_eq!(
+ result.background_clip,
+ parse_longhand!(background_clip, "padding-box")
+ );
+
+ let result = parse(background::parse_value, "padding-box").unwrap();
+
+ assert_eq!(
+ result.background_origin,
+ parse_longhand!(background_origin, "padding-box")
+ );
+ assert_eq!(
+ result.background_clip,
+ parse_longhand!(background_clip, "padding-box")
+ );
+}
diff --git a/servo/tests/unit/style/parsing/border.rs b/servo/tests/unit/style/parsing/border.rs
new file mode 100644
index 0000000000..8e474ca542
--- /dev/null
+++ b/servo/tests/unit/style/parsing/border.rs
@@ -0,0 +1,219 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+use parsing::parse;
+use style::parser::Parse;
+use style::properties::longhands::{border_image_outset, border_image_repeat, border_image_slice};
+use style::properties::longhands::{border_image_source, border_image_width};
+use style::properties::shorthands::border_image;
+use style::properties::MaybeBoxed;
+use style::values::specified::BorderRadius;
+use style_traits::ToCss;
+
+macro_rules! assert_longhand {
+ ($parsed_shorthand: expr, $prop: ident, $value_string: expr) => {
+ assert_eq!(
+ $parsed_shorthand.$prop,
+ parse_longhand!($prop, $value_string).maybe_boxed()
+ )
+ };
+}
+
+macro_rules! assert_initial {
+ ($parsed_shorthand: expr, $prop: ident) => {
+ assert_eq!(
+ $parsed_shorthand.$prop,
+ $prop::get_initial_specified_value().maybe_boxed()
+ )
+ };
+}
+
+macro_rules! assert_border_radius_values {
+ ($input:expr; $tlw:expr, $trw:expr, $brw:expr, $blw:expr ;
+ $tlh:expr, $trh:expr, $brh:expr, $blh:expr) => {
+ let input = parse(BorderRadius::parse, $input)
+ .expect(&format!("Failed parsing {} as border radius", $input));
+ assert_eq!(
+ ::style_traits::ToCss::to_css_string(&input.top_left.0.width()),
+ $tlw
+ );
+ assert_eq!(
+ ::style_traits::ToCss::to_css_string(&input.top_right.0.width()),
+ $trw
+ );
+ assert_eq!(
+ ::style_traits::ToCss::to_css_string(&input.bottom_right.0.width()),
+ $brw
+ );
+ assert_eq!(
+ ::style_traits::ToCss::to_css_string(&input.bottom_left.0.width()),
+ $blw
+ );
+ assert_eq!(
+ ::style_traits::ToCss::to_css_string(&input.top_left.0.height()),
+ $tlh
+ );
+ assert_eq!(
+ ::style_traits::ToCss::to_css_string(&input.top_right.0.height()),
+ $trh
+ );
+ assert_eq!(
+ ::style_traits::ToCss::to_css_string(&input.bottom_right.0.height()),
+ $brh
+ );
+ assert_eq!(
+ ::style_traits::ToCss::to_css_string(&input.bottom_left.0.height()),
+ $blh
+ );
+ };
+}
+
+#[test]
+fn test_border_radius() {
+ assert_border_radius_values!("10px";
+ "10px", "10px", "10px", "10px" ;
+ "10px", "10px", "10px", "10px");
+ assert_border_radius_values!("10px 20px";
+ "10px", "20px", "10px", "20px" ;
+ "10px", "20px", "10px", "20px");
+ assert_border_radius_values!("10px 20px 30px";
+ "10px", "20px", "30px", "20px" ;
+ "10px", "20px", "30px", "20px");
+ assert_border_radius_values!("10px 20px 30px 40px";
+ "10px", "20px", "30px", "40px" ;
+ "10px", "20px", "30px", "40px");
+ assert_border_radius_values!("10% / 20px";
+ "10%", "10%", "10%", "10%" ;
+ "20px", "20px", "20px", "20px");
+ assert_border_radius_values!("10px / 20px 30px";
+ "10px", "10px", "10px", "10px" ;
+ "20px", "30px", "20px", "30px");
+ assert_border_radius_values!("10px 20px 30px 40px / 1px 2px 3px 4px";
+ "10px", "20px", "30px", "40px" ;
+ "1px", "2px", "3px", "4px");
+ assert_border_radius_values!("10px 20px 30px 40px / 1px 2px 3px 4px";
+ "10px", "20px", "30px", "40px" ;
+ "1px", "2px", "3px", "4px");
+ assert_border_radius_values!("10px 20px 30px 40px / 1px 2px 3px 4px";
+ "10px", "20px", "30px", "40px" ;
+ "1px", "2px", "3px", "4px");
+ assert_border_radius_values!("10px -20px 30px 40px";
+ "10px", "10px", "10px", "10px";
+ "10px", "10px", "10px", "10px");
+ assert_border_radius_values!("10px 20px -30px 40px";
+ "10px", "20px", "10px", "20px";
+ "10px", "20px", "10px", "20px");
+ assert_border_radius_values!("10px 20px 30px -40px";
+ "10px", "20px", "30px", "20px";
+ "10px", "20px", "30px", "20px");
+ assert!(parse(BorderRadius::parse, "-10px 20px 30px 40px").is_err());
+}
+
+#[test]
+fn border_image_shorthand_should_parse_when_all_properties_specified() {
+ let input = "linear-gradient(red, blue) 30 30% 45 fill / 20px 40px / 10px round stretch";
+ let result = parse(border_image::parse_value, input).unwrap();
+
+ assert_longhand!(result, border_image_source, "linear-gradient(red, blue)");
+ assert_longhand!(result, border_image_slice, "30 30% 45 fill");
+ assert_longhand!(result, border_image_width, "20px 40px");
+ assert_longhand!(result, border_image_outset, "10px");
+ assert_longhand!(result, border_image_repeat, "round stretch");
+}
+
+#[test]
+fn border_image_shorthand_should_parse_without_width() {
+ let input = "linear-gradient(red, blue) 30 30% 45 fill / / 10px round stretch";
+ let result = parse(border_image::parse_value, input).unwrap();
+
+ assert_longhand!(result, border_image_source, "linear-gradient(red, blue)");
+ assert_longhand!(result, border_image_slice, "30 30% 45 fill");
+ assert_longhand!(result, border_image_outset, "10px");
+ assert_longhand!(result, border_image_repeat, "round stretch");
+ assert_initial!(result, border_image_width);
+}
+
+#[test]
+fn border_image_shorthand_should_parse_without_outset() {
+ let input = "linear-gradient(red, blue) 30 30% 45 fill / 20px 40px round";
+ let result = parse(border_image::parse_value, input).unwrap();
+
+ assert_longhand!(result, border_image_source, "linear-gradient(red, blue)");
+ assert_longhand!(result, border_image_slice, "30 30% 45 fill");
+ assert_longhand!(result, border_image_width, "20px 40px");
+ assert_longhand!(result, border_image_repeat, "round");
+ assert_initial!(result, border_image_outset);
+}
+
+#[test]
+fn border_image_shorthand_should_parse_without_width_or_outset() {
+ let input = "linear-gradient(red, blue) 30 30% 45 fill round";
+ let result = parse(border_image::parse_value, input).unwrap();
+
+ assert_longhand!(result, border_image_source, "linear-gradient(red, blue)");
+ assert_longhand!(result, border_image_slice, "30 30% 45 fill");
+ assert_longhand!(result, border_image_repeat, "round");
+ assert_initial!(result, border_image_width);
+ assert_initial!(result, border_image_outset);
+}
+
+#[test]
+fn border_image_shorthand_should_parse_with_just_source() {
+ let result = parse(border_image::parse_value, "linear-gradient(red, blue)").unwrap();
+
+ assert_longhand!(result, border_image_source, "linear-gradient(red, blue)");
+ assert_initial!(result, border_image_slice);
+ assert_initial!(result, border_image_width);
+ assert_initial!(result, border_image_outset);
+ assert_initial!(result, border_image_repeat);
+}
+
+#[test]
+fn border_image_outset_should_error_on_negative_length() {
+ let result = parse(border_image_outset::parse, "-1em");
+ assert!(result.is_err());
+}
+
+#[test]
+fn border_image_outset_should_error_on_negative_number() {
+ let result = parse(border_image_outset::parse, "-15");
+ assert!(result.is_err());
+}
+
+#[test]
+fn border_image_outset_should_return_number_on_plain_zero() {
+ let result = parse(border_image_outset::parse, "0");
+ assert_eq!(result.unwrap(), parse_longhand!(border_image_outset, "0"));
+}
+
+#[test]
+fn border_image_outset_should_return_length_on_length_zero() {
+ let result = parse(border_image_outset::parse, "0em");
+ assert_eq!(result.unwrap(), parse_longhand!(border_image_outset, "0em"));
+}
+
+#[test]
+fn test_border_style() {
+ use style::values::specified::BorderStyle;
+
+ assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"none"#);
+ assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"hidden"#);
+ assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"solid"#);
+ assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"double"#);
+ assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"dotted"#);
+ assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"dashed"#);
+ assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"groove"#);
+ assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"ridge"#);
+ assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"inset"#);
+ assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"outset"#);
+}
+
+#[test]
+fn test_border_spacing() {
+ use style::properties::longhands::border_spacing;
+
+ assert_parser_exhausted!(border_spacing::parse, "1px rubbish", false);
+ assert_parser_exhausted!(border_spacing::parse, "1px", true);
+ assert_parser_exhausted!(border_spacing::parse, "1px 2px", true);
+}
diff --git a/servo/tests/unit/style/parsing/box_.rs b/servo/tests/unit/style/parsing/box_.rs
new file mode 100644
index 0000000000..cec950c3cd
--- /dev/null
+++ b/servo/tests/unit/style/parsing/box_.rs
@@ -0,0 +1,22 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+use parsing::parse;
+use style_traits::ToCss;
+
+#[test]
+fn test_transform_translate() {
+ use style::properties::longhands::transform;
+ assert_roundtrip_with_context!(transform::parse, "translate(2px)");
+ assert_roundtrip_with_context!(transform::parse, "translate(2px, 5px)");
+ assert!(parse(transform::parse, "translate(2px foo)").is_err());
+ assert!(parse(transform::parse, "perspective(-10px)").is_err());
+}
+
+#[test]
+fn test_unexhausted_transform() {
+ use style::properties::longhands::transform;
+ assert_parser_exhausted!(transform::parse, "rotate(70deg)foo", false);
+ assert_parser_exhausted!(transform::parse, "rotate(70deg) foo", false);
+}
diff --git a/servo/tests/unit/style/parsing/column.rs b/servo/tests/unit/style/parsing/column.rs
new file mode 100644
index 0000000000..6e0e86182a
--- /dev/null
+++ b/servo/tests/unit/style/parsing/column.rs
@@ -0,0 +1,30 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+use parsing::parse;
+use style_traits::ToCss;
+
+#[test]
+fn test_column_width() {
+ use style::properties::longhands::column_width;
+
+ assert_roundtrip_with_context!(column_width::parse, "auto");
+ assert_roundtrip_with_context!(column_width::parse, "6px");
+ assert_roundtrip_with_context!(column_width::parse, "2.5em");
+ assert_roundtrip_with_context!(column_width::parse, "0.3vw");
+
+ assert!(parse(column_width::parse, "-6px").is_err());
+}
+
+#[test]
+fn test_column_gap() {
+ use style::properties::longhands::column_gap;
+
+ assert_roundtrip_with_context!(column_gap::parse, "normal");
+ assert_roundtrip_with_context!(column_gap::parse, "6px");
+ assert_roundtrip_with_context!(column_gap::parse, "2.5em");
+ assert_roundtrip_with_context!(column_gap::parse, "0.3vw");
+
+ assert!(parse(column_gap::parse, "-6px").is_err());
+}
diff --git a/servo/tests/unit/style/parsing/effects.rs b/servo/tests/unit/style/parsing/effects.rs
new file mode 100644
index 0000000000..1b377aca54
--- /dev/null
+++ b/servo/tests/unit/style/parsing/effects.rs
@@ -0,0 +1,99 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+use parsing::parse;
+use style::properties::longhands::{perspective_origin, transform_origin};
+use style_traits::ToCss;
+
+#[test]
+fn test_clip() {
+ use style::properties::longhands::clip;
+
+ assert_roundtrip_with_context!(clip::parse, "auto");
+ assert_roundtrip_with_context!(clip::parse, "rect(1px, 2px, 3px, 4px)");
+ assert_roundtrip_with_context!(clip::parse, "rect(1px, auto, auto, 4px)");
+ assert_roundtrip_with_context!(clip::parse, "rect(auto, auto, auto, auto)");
+
+ // Non-standard syntax
+ assert_roundtrip_with_context!(
+ clip::parse,
+ "rect(1px 2px 3px 4px)",
+ "rect(1px, 2px, 3px, 4px)"
+ );
+ assert_roundtrip_with_context!(
+ clip::parse,
+ "rect(auto 2px 3px auto)",
+ "rect(auto, 2px, 3px, auto)"
+ );
+ assert_roundtrip_with_context!(
+ clip::parse,
+ "rect(1px auto auto 4px)",
+ "rect(1px, auto, auto, 4px)"
+ );
+ assert_roundtrip_with_context!(
+ clip::parse,
+ "rect(auto auto auto auto)",
+ "rect(auto, auto, auto, auto)"
+ );
+}
+
+#[test]
+fn test_effects_parser_exhaustion() {
+ assert_parser_exhausted!(perspective_origin::parse, "1px 1px", true);
+ assert_parser_exhausted!(transform_origin::parse, "1px 1px", true);
+
+ assert_parser_exhausted!(perspective_origin::parse, "1px some-rubbish", false);
+ assert_parser_exhausted!(transform_origin::parse, "1px some-rubbish", false);
+}
+
+#[test]
+fn test_parse_factor() {
+ use parsing::parse;
+ use style::properties::longhands::filter;
+
+ assert!(parse(filter::parse, "brightness(0)").is_ok());
+ assert!(parse(filter::parse, "brightness(55)").is_ok());
+ assert!(parse(filter::parse, "brightness(100)").is_ok());
+
+ assert!(parse(filter::parse, "contrast(0)").is_ok());
+ assert!(parse(filter::parse, "contrast(55)").is_ok());
+ assert!(parse(filter::parse, "contrast(100)").is_ok());
+
+ assert!(parse(filter::parse, "grayscale(0)").is_ok());
+ assert!(parse(filter::parse, "grayscale(55)").is_ok());
+ assert!(parse(filter::parse, "grayscale(100)").is_ok());
+
+ assert!(parse(filter::parse, "invert(0)").is_ok());
+ assert!(parse(filter::parse, "invert(55)").is_ok());
+ assert!(parse(filter::parse, "invert(100)").is_ok());
+
+ assert!(parse(filter::parse, "opacity(0)").is_ok());
+ assert!(parse(filter::parse, "opacity(55)").is_ok());
+ assert!(parse(filter::parse, "opacity(100)").is_ok());
+
+ assert!(parse(filter::parse, "sepia(0)").is_ok());
+ assert!(parse(filter::parse, "sepia(55)").is_ok());
+ assert!(parse(filter::parse, "sepia(100)").is_ok());
+
+ assert!(parse(filter::parse, "saturate(0)").is_ok());
+ assert!(parse(filter::parse, "saturate(55)").is_ok());
+ assert!(parse(filter::parse, "saturate(100)").is_ok());
+
+ // Negative numbers are invalid for certain filters
+ assert!(parse(filter::parse, "brightness(-1)").is_err());
+ assert!(parse(filter::parse, "contrast(-1)").is_err());
+ assert!(parse(filter::parse, "grayscale(-1)").is_err());
+ assert!(parse(filter::parse, "invert(-1)").is_err());
+ assert!(parse(filter::parse, "opacity(-1)").is_err());
+ assert!(parse(filter::parse, "sepia(-1)").is_err());
+ assert!(parse(filter::parse, "saturate(-1)").is_err());
+}
+
+#[test]
+fn blur_radius_should_not_accept_negavite_values() {
+ use style::properties::longhands::box_shadow;
+ assert!(parse(box_shadow::parse, "1px 1px -1px").is_err()); // for -ve values
+ assert!(parse(box_shadow::parse, "1px 1px 0").is_ok()); // for zero
+ assert!(parse(box_shadow::parse, "1px 1px 1px").is_ok()); // for +ve value
+}
diff --git a/servo/tests/unit/style/parsing/image.rs b/servo/tests/unit/style/parsing/image.rs
new file mode 100644
index 0000000000..33ccb88cb0
--- /dev/null
+++ b/servo/tests/unit/style/parsing/image.rs
@@ -0,0 +1,154 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+use style::parser::Parse;
+use style::values::specified::image::*;
+use style_traits::ToCss;
+
+#[test]
+fn test_linear_gradient() {
+ // Parsing from the right
+ assert_roundtrip_with_context!(Image::parse, "linear-gradient(to left, red, green)");
+
+ // Parsing from the left
+ assert_roundtrip_with_context!(Image::parse, "linear-gradient(to right, red, green)");
+
+ // Parsing with two values for <side-or-corner>
+ assert_roundtrip_with_context!(Image::parse, "linear-gradient(to right top, red, green)");
+
+ // Parsing with <angle>
+ assert_roundtrip_with_context!(Image::parse, "linear-gradient(45deg, red, green)");
+
+ // Parsing with more than two entries in <color-stop-list>
+ assert_roundtrip_with_context!(Image::parse, "linear-gradient(red, yellow, green)");
+
+ // Parsing with percentage in the <color-stop-list>
+ assert_roundtrip_with_context!(Image::parse, "linear-gradient(red, green, yellow 50%)");
+
+ // Parsing without <angle> and <side-or-corner>
+ assert_roundtrip_with_context!(Image::parse, "linear-gradient(red, green)");
+}
+
+#[test]
+fn test_radial_gradient() {
+ // Parsing with all values
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(circle closest-side at 20px 30px, red, green)"
+ );
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(ellipse closest-side at 20px 30px, red, green)",
+ "radial-gradient(closest-side at 20px 30px, red, green)"
+ );
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(closest-side circle at 20px 30px, red, green)",
+ "radial-gradient(circle closest-side at 20px 30px, red, green)"
+ );
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(closest-side ellipse at 20px 30px, red, green)",
+ "radial-gradient(closest-side at 20px 30px, red, green)"
+ );
+
+ // Parsing with <shape-keyword> and <size> reversed
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(closest-side circle at 20px 30px, red, green)",
+ "radial-gradient(circle closest-side at 20px 30px, red, green)"
+ );
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(closest-corner ellipse at 20px 30px, red, green)",
+ "radial-gradient(closest-corner at 20px 30px, red, green)"
+ );
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(30px circle, red, green)",
+ "radial-gradient(30px at center center, red, green)"
+ );
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(30px 40px ellipse, red, green)",
+ "radial-gradient(30px 40px at center center, red, green)"
+ );
+
+ // Parsing without <size>
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(circle, red, green)",
+ "radial-gradient(circle at center center, red, green)"
+ );
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(ellipse, red, green)",
+ "radial-gradient(at center center, red, green)"
+ );
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(circle at 20px 30px, red, green)"
+ );
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(ellipse at 20px 30px, red, green)",
+ "radial-gradient(at 20px 30px, red, green)"
+ );
+
+ // Parsing without <shape-keyword>
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(20px at 20px 30px, red, green)"
+ );
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(20px 30px at left center, red, green)"
+ );
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(closest-side at center, red, green)",
+ "radial-gradient(closest-side at center center, red, green)"
+ );
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(20px, red, green)",
+ "radial-gradient(20px at center center, red, green)"
+ );
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(20px 30px, red, green)",
+ "radial-gradient(20px 30px at center center, red, green)"
+ );
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(closest-side, red, green)",
+ "radial-gradient(closest-side at center center, red, green)"
+ );
+
+ // Parsing without <shape-keyword> and <size>
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(at center, red, green)",
+ "radial-gradient(at center center, red, green)"
+ );
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(at center bottom, red, green)"
+ );
+ assert_roundtrip_with_context!(Image::parse, "radial-gradient(at 40px 50px, red, green)");
+
+ // Parsing with just color stops
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "radial-gradient(red, green)",
+ "radial-gradient(at center center, red, green)"
+ );
+
+ // Parsing repeating radial gradient
+ assert_roundtrip_with_context!(
+ Image::parse,
+ "repeating-radial-gradient(red, green)",
+ "repeating-radial-gradient(at center center, red, green)"
+ );
+}
diff --git a/servo/tests/unit/style/parsing/inherited_text.rs b/servo/tests/unit/style/parsing/inherited_text.rs
new file mode 100644
index 0000000000..90ec16f7b0
--- /dev/null
+++ b/servo/tests/unit/style/parsing/inherited_text.rs
@@ -0,0 +1,46 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+use parsing::parse;
+use style::values::generics::text::Spacing;
+
+#[test]
+fn negative_letter_spacing_should_parse_properly() {
+ use style::properties::longhands::letter_spacing;
+ use style::values::specified::length::{FontRelativeLength, Length, NoCalcLength};
+
+ let negative_value = parse_longhand!(letter_spacing, "-0.5em");
+ let expected = Spacing::Value(Length::NoCalc(NoCalcLength::FontRelative(
+ FontRelativeLength::Em(-0.5),
+ )));
+ assert_eq!(negative_value, expected);
+}
+
+#[test]
+fn negative_word_spacing_should_parse_properly() {
+ use style::properties::longhands::word_spacing;
+ use style::values::specified::length::{FontRelativeLength, LengthPercentage, NoCalcLength};
+
+ let negative_value = parse_longhand!(word_spacing, "-0.5em");
+ let expected = Spacing::Value(LengthPercentage::Length(NoCalcLength::FontRelative(
+ FontRelativeLength::Em(-0.5),
+ )));
+ assert_eq!(negative_value, expected);
+}
+
+#[test]
+fn line_height_should_return_number_on_plain_zero() {
+ use style::properties::longhands::line_height;
+
+ let result = parse(line_height::parse, "0").unwrap();
+ assert_eq!(result, parse_longhand!(line_height, "0"));
+}
+
+#[test]
+fn line_height_should_return_length_on_length_zero() {
+ use style::properties::longhands::line_height;
+
+ let result = parse(line_height::parse, "0px").unwrap();
+ assert_eq!(result, parse_longhand!(line_height, "0px"));
+}
diff --git a/servo/tests/unit/style/parsing/mod.rs b/servo/tests/unit/style/parsing/mod.rs
new file mode 100644
index 0000000000..095f74c960
--- /dev/null
+++ b/servo/tests/unit/style/parsing/mod.rs
@@ -0,0 +1,150 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+//! Tests for parsing and serialization of values/properties
+
+use cssparser::{Parser, ParserInput};
+use style::context::QuirksMode;
+use style::parser::ParserContext;
+use style::stylesheets::{CssRuleType, Origin};
+use style_traits::{ParseError, ParsingMode};
+
+fn parse<T, F>(f: F, s: &'static str) -> Result<T, ParseError<'static>>
+where
+ F: for<'t> Fn(&ParserContext, &mut Parser<'static, 't>) -> Result<T, ParseError<'static>>,
+{
+ let mut input = ParserInput::new(s);
+ parse_input(f, &mut input)
+}
+
+fn parse_input<'i: 't, 't, T, F>(f: F, input: &'t mut ParserInput<'i>) -> Result<T, ParseError<'i>>
+where
+ F: Fn(&ParserContext, &mut Parser<'i, 't>) -> Result<T, ParseError<'i>>,
+{
+ let url = ::servo_url::ServoUrl::parse("http://localhost").unwrap();
+ let context = ParserContext::new(
+ Origin::Author,
+ &url,
+ Some(CssRuleType::Style),
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ None,
+ None,
+ );
+ let mut parser = Parser::new(input);
+ f(&context, &mut parser)
+}
+
+fn parse_entirely<T, F>(f: F, s: &'static str) -> Result<T, ParseError<'static>>
+where
+ F: for<'t> Fn(&ParserContext, &mut Parser<'static, 't>) -> Result<T, ParseError<'static>>,
+{
+ let mut input = ParserInput::new(s);
+ parse_entirely_input(f, &mut input)
+}
+
+fn parse_entirely_input<'i: 't, 't, T, F>(
+ f: F,
+ input: &'t mut ParserInput<'i>,
+) -> Result<T, ParseError<'i>>
+where
+ F: Fn(&ParserContext, &mut Parser<'i, 't>) -> Result<T, ParseError<'i>>,
+{
+ parse_input(
+ |context, parser| parser.parse_entirely(|p| f(context, p)),
+ input,
+ )
+}
+
+// This is a macro so that the file/line information
+// is preserved in the panic
+macro_rules! assert_roundtrip_with_context {
+ ($fun:expr, $string:expr) => {
+ assert_roundtrip_with_context!($fun, $string, $string);
+ };
+ ($fun:expr, $input:expr, $output:expr) => {{
+ let mut input = ::cssparser::ParserInput::new($input);
+ let serialized = super::parse_input(
+ |context, i| {
+ let parsed = $fun(context, i).expect(&format!("Failed to parse {}", $input));
+ let serialized = ToCss::to_css_string(&parsed);
+ assert_eq!(serialized, $output);
+ Ok(serialized)
+ },
+ &mut input,
+ )
+ .unwrap();
+
+ let mut input = ::cssparser::ParserInput::new(&serialized);
+ let unwrapped = super::parse_input(
+ |context, i| {
+ let re_parsed =
+ $fun(context, i).expect(&format!("Failed to parse serialization {}", $input));
+ let re_serialized = ToCss::to_css_string(&re_parsed);
+ assert_eq!(serialized, re_serialized);
+ Ok(())
+ },
+ &mut input,
+ )
+ .unwrap();
+ unwrapped
+ }};
+}
+
+macro_rules! assert_roundtrip {
+ ($fun:expr, $string:expr) => {
+ assert_roundtrip!($fun, $string, $string);
+ };
+ ($fun:expr, $input:expr, $output:expr) => {
+ let mut input = ParserInput::new($input);
+ let mut parser = Parser::new(&mut input);
+ let parsed = $fun(&mut parser).expect(&format!("Failed to parse {}", $input));
+ let serialized = ToCss::to_css_string(&parsed);
+ assert_eq!(serialized, $output);
+
+ let mut input = ParserInput::new(&serialized);
+ let mut parser = Parser::new(&mut input);
+ let re_parsed =
+ $fun(&mut parser).expect(&format!("Failed to parse serialization {}", $input));
+ let re_serialized = ToCss::to_css_string(&re_parsed);
+ assert_eq!(serialized, re_serialized)
+ };
+}
+
+macro_rules! assert_parser_exhausted {
+ ($fun:expr, $string:expr, $should_exhausted:expr) => {{
+ parse(
+ |context, input| {
+ let parsed = $fun(context, input);
+ assert_eq!(parsed.is_ok(), true);
+ assert_eq!(input.is_exhausted(), $should_exhausted);
+ Ok(())
+ },
+ $string,
+ )
+ .unwrap()
+ }};
+}
+
+macro_rules! parse_longhand {
+ ($name:ident, $s:expr) => {
+ parse($name::parse, $s).unwrap()
+ };
+}
+
+mod animation;
+mod background;
+mod border;
+mod box_;
+mod column;
+mod effects;
+mod image;
+mod inherited_text;
+mod outline;
+mod position;
+mod selectors;
+mod supports;
+mod text_overflow;
+mod transition_duration;
+mod transition_timing_function;
diff --git a/servo/tests/unit/style/parsing/outline.rs b/servo/tests/unit/style/parsing/outline.rs
new file mode 100644
index 0000000000..6d909d9bc5
--- /dev/null
+++ b/servo/tests/unit/style/parsing/outline.rs
@@ -0,0 +1,26 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+use parsing::parse;
+use style_traits::ToCss;
+
+#[test]
+fn test_outline_style() {
+ use style::properties::longhands::outline_style;
+
+ assert_roundtrip_with_context!(outline_style::parse, r#"auto"#);
+ assert_roundtrip_with_context!(outline_style::parse, r#"none"#);
+ assert_roundtrip_with_context!(outline_style::parse, r#"solid"#);
+ assert_roundtrip_with_context!(outline_style::parse, r#"double"#);
+ assert_roundtrip_with_context!(outline_style::parse, r#"dotted"#);
+ assert_roundtrip_with_context!(outline_style::parse, r#"dashed"#);
+ assert_roundtrip_with_context!(outline_style::parse, r#"groove"#);
+ assert_roundtrip_with_context!(outline_style::parse, r#"ridge"#);
+ assert_roundtrip_with_context!(outline_style::parse, r#"inset"#);
+ assert_roundtrip_with_context!(outline_style::parse, r#"outset"#);
+
+ // The outline-style property accepts the same values as border-style,
+ // except that 'hidden' is not a legal outline style.
+ assert!(parse(outline_style::parse, r#"hidden"#).is_err());
+}
diff --git a/servo/tests/unit/style/parsing/position.rs b/servo/tests/unit/style/parsing/position.rs
new file mode 100644
index 0000000000..ce73c7a0d2
--- /dev/null
+++ b/servo/tests/unit/style/parsing/position.rs
@@ -0,0 +1,145 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+use parsing::{parse, parse_entirely};
+use style::parser::Parse;
+use style::values::specified::position::*;
+use style_traits::ToCss;
+
+#[test]
+fn test_position() {
+ // Serialization is not actually specced
+ // though these are the values expected by basic-shape
+ // https://github.com/w3c/csswg-drafts/issues/368
+ assert_roundtrip_with_context!(Position::parse, "center", "center center");
+ assert_roundtrip_with_context!(Position::parse, "top left", "left top");
+ assert_roundtrip_with_context!(Position::parse, "left top", "left top");
+ assert_roundtrip_with_context!(Position::parse, "top right", "right top");
+ assert_roundtrip_with_context!(Position::parse, "right top", "right top");
+ assert_roundtrip_with_context!(Position::parse, "bottom left", "left bottom");
+ assert_roundtrip_with_context!(Position::parse, "left bottom", "left bottom");
+ assert_roundtrip_with_context!(Position::parse, "left center", "left center");
+ assert_roundtrip_with_context!(Position::parse, "right center", "right center");
+ assert_roundtrip_with_context!(Position::parse, "center top", "center top");
+ assert_roundtrip_with_context!(Position::parse, "center bottom", "center bottom");
+ assert_roundtrip_with_context!(Position::parse, "center 10px", "center 10px");
+ assert_roundtrip_with_context!(Position::parse, "center 10%", "center 10%");
+ assert_roundtrip_with_context!(Position::parse, "right 10%", "right 10%");
+
+ // Only keywords can be reordered
+ assert!(parse_entirely(Position::parse, "top 40%").is_err());
+ assert!(parse_entirely(Position::parse, "40% left").is_err());
+
+ // 3 and 4 value serialization
+ assert_roundtrip_with_context!(Position::parse, "left 10px top 15px", "left 10px top 15px");
+ assert_roundtrip_with_context!(Position::parse, "top 15px left 10px", "left 10px top 15px");
+ assert_roundtrip_with_context!(Position::parse, "left 10% top 15px", "left 10% top 15px");
+ assert_roundtrip_with_context!(Position::parse, "top 15px left 10%", "left 10% top 15px");
+ assert_roundtrip_with_context!(Position::parse, "left top 15px", "left top 15px");
+ assert_roundtrip_with_context!(Position::parse, "top 15px left", "left top 15px");
+ assert_roundtrip_with_context!(Position::parse, "left 10px top", "left 10px top");
+ assert_roundtrip_with_context!(Position::parse, "top left 10px", "left 10px top");
+ assert_roundtrip_with_context!(Position::parse, "right 10px bottom", "right 10px bottom");
+ assert_roundtrip_with_context!(Position::parse, "bottom right 10px", "right 10px bottom");
+ assert_roundtrip_with_context!(Position::parse, "center right 10px", "right 10px center");
+ assert_roundtrip_with_context!(Position::parse, "center bottom 10px", "center bottom 10px");
+
+ // Invalid 3 value positions
+ assert!(parse_entirely(Position::parse, "20px 30px 20px").is_err());
+ assert!(parse_entirely(Position::parse, "top 30px 20px").is_err());
+ assert!(parse_entirely(Position::parse, "50% bottom 20%").is_err());
+
+ // Only horizontal and vertical keywords can have positions
+ assert!(parse_entirely(Position::parse, "center 10px left 15px").is_err());
+ assert!(parse_entirely(Position::parse, "center 10px 15px").is_err());
+ assert!(parse_entirely(Position::parse, "center 10px bottom").is_err());
+
+ // "Horizontal Horizontal" or "Vertical Vertical" positions cause error
+ assert!(parse_entirely(Position::parse, "left right").is_err());
+ assert!(parse_entirely(Position::parse, "left 10px right").is_err());
+ assert!(parse_entirely(Position::parse, "left 10px right 15%").is_err());
+ assert!(parse_entirely(Position::parse, "top bottom").is_err());
+ assert!(parse_entirely(Position::parse, "top 10px bottom").is_err());
+ assert!(parse_entirely(Position::parse, "top 10px bottom 15%").is_err());
+
+ // Logical keywords are not supported in Position yet.
+ assert!(parse(Position::parse, "x-start").is_err());
+ assert!(parse(Position::parse, "y-end").is_err());
+ assert!(parse(Position::parse, "x-start y-end").is_err());
+ assert!(parse(Position::parse, "x-end 10px").is_err());
+ assert!(parse(Position::parse, "y-start 20px").is_err());
+ assert!(parse(Position::parse, "x-start bottom 10%").is_err());
+ assert!(parse_entirely(Position::parse, "left y-start 10%").is_err());
+ assert!(parse(Position::parse, "x-start 20px y-end 10%").is_err());
+}
+
+#[test]
+fn test_horizontal_position() {
+ // One value serializations.
+ assert_roundtrip_with_context!(HorizontalPosition::parse, "20px", "20px");
+ assert_roundtrip_with_context!(HorizontalPosition::parse, "25%", "25%");
+ assert_roundtrip_with_context!(HorizontalPosition::parse, "center", "center");
+ assert_roundtrip_with_context!(HorizontalPosition::parse, "left", "left");
+ assert_roundtrip_with_context!(HorizontalPosition::parse, "right", "right");
+
+ // Two value serializations.
+ assert_roundtrip_with_context!(HorizontalPosition::parse, "right 10px", "right 10px");
+
+ // Invalid horizontal positions.
+ assert!(parse(HorizontalPosition::parse, "top").is_err());
+ assert!(parse(HorizontalPosition::parse, "bottom").is_err());
+ assert!(parse(HorizontalPosition::parse, "y-start").is_err());
+ assert!(parse(HorizontalPosition::parse, "y-end").is_err());
+ assert!(parse(HorizontalPosition::parse, "y-end 20px ").is_err());
+ assert!(parse(HorizontalPosition::parse, "bottom 20px").is_err());
+ assert!(parse(HorizontalPosition::parse, "bottom top").is_err());
+ assert!(parse_entirely(HorizontalPosition::parse, "20px y-end").is_err());
+ assert!(parse_entirely(HorizontalPosition::parse, "20px top").is_err());
+ assert!(parse_entirely(HorizontalPosition::parse, "left center").is_err());
+ assert!(parse_entirely(HorizontalPosition::parse, "left top").is_err());
+ assert!(parse_entirely(HorizontalPosition::parse, "left right").is_err());
+ assert!(parse_entirely(HorizontalPosition::parse, "20px 30px").is_err());
+ assert!(parse_entirely(HorizontalPosition::parse, "10px left").is_err());
+ assert!(parse_entirely(HorizontalPosition::parse, "x-end 20%").is_err());
+ assert!(parse_entirely(HorizontalPosition::parse, "20px x-start").is_err());
+
+ // Logical keywords are not supported in Position yet.
+ assert!(parse(HorizontalPosition::parse, "x-start").is_err());
+ assert!(parse(HorizontalPosition::parse, "x-end").is_err());
+}
+
+#[test]
+fn test_vertical_position() {
+ // One value serializations.
+ assert_roundtrip_with_context!(VerticalPosition::parse, "20px", "20px");
+ assert_roundtrip_with_context!(VerticalPosition::parse, "25%", "25%");
+ assert_roundtrip_with_context!(VerticalPosition::parse, "center", "center");
+ assert_roundtrip_with_context!(VerticalPosition::parse, "top", "top");
+ assert_roundtrip_with_context!(VerticalPosition::parse, "bottom", "bottom");
+
+ // Two value serializations.
+ assert_roundtrip_with_context!(VerticalPosition::parse, "bottom 10px", "bottom 10px");
+
+ // Invalid vertical positions.
+ assert!(parse(VerticalPosition::parse, "left").is_err());
+ assert!(parse(VerticalPosition::parse, "right").is_err());
+ assert!(parse(VerticalPosition::parse, "x-start").is_err());
+ assert!(parse(VerticalPosition::parse, "x-end").is_err());
+ assert!(parse(VerticalPosition::parse, "x-end 20px").is_err());
+ assert!(parse(VerticalPosition::parse, "left 20px").is_err());
+ assert!(parse(VerticalPosition::parse, "left center").is_err());
+ assert!(parse(VerticalPosition::parse, "left top").is_err());
+ assert!(parse(VerticalPosition::parse, "left right").is_err());
+ assert!(parse_entirely(VerticalPosition::parse, "20px x-end").is_err());
+ assert!(parse_entirely(VerticalPosition::parse, "20px right").is_err());
+ assert!(parse_entirely(VerticalPosition::parse, "bottom top").is_err());
+ assert!(parse_entirely(VerticalPosition::parse, "20px 30px").is_err());
+ assert!(parse_entirely(VerticalPosition::parse, "10px top").is_err());
+ assert!(parse_entirely(VerticalPosition::parse, "y-end 20%").is_err());
+ assert!(parse_entirely(VerticalPosition::parse, "20px y-start").is_err());
+
+ // Logical keywords are not supported in Position yet.
+ assert!(parse(VerticalPosition::parse, "y-start").is_err());
+ assert!(parse(VerticalPosition::parse, "y-end").is_err());
+}
diff --git a/servo/tests/unit/style/parsing/selectors.rs b/servo/tests/unit/style/parsing/selectors.rs
new file mode 100644
index 0000000000..1ce7498caa
--- /dev/null
+++ b/servo/tests/unit/style/parsing/selectors.rs
@@ -0,0 +1,35 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+use cssparser::{Parser, ParserInput, ToCss};
+use selectors::parser::SelectorList;
+use style::selector_parser::{SelectorImpl, SelectorParser};
+use style::stylesheets::{Namespaces, Origin};
+use style_traits::ParseError;
+
+fn parse_selector<'i, 't>(
+ input: &mut Parser<'i, 't>,
+) -> Result<SelectorList<SelectorImpl>, ParseError<'i>> {
+ let mut ns = Namespaces::default();
+ ns.prefixes.insert("svg".into(), ns!(svg));
+ let parser = SelectorParser {
+ stylesheet_origin: Origin::UserAgent,
+ namespaces: &ns,
+ url_data: None,
+ };
+ SelectorList::parse(&parser, input)
+}
+
+#[test]
+fn test_selectors() {
+ assert_roundtrip!(parse_selector, "div");
+ assert_roundtrip!(parse_selector, "svg|circle");
+ assert_roundtrip!(parse_selector, "p:before", "p::before");
+ assert_roundtrip!(
+ parse_selector,
+ "[border=\"0\"]:-servo-nonzero-border ~ ::-servo-details-summary"
+ );
+ assert_roundtrip!(parse_selector, "* > *");
+ assert_roundtrip!(parse_selector, "*|* + *", "* + *");
+}
diff --git a/servo/tests/unit/style/parsing/supports.rs b/servo/tests/unit/style/parsing/supports.rs
new file mode 100644
index 0000000000..f54ee48bab
--- /dev/null
+++ b/servo/tests/unit/style/parsing/supports.rs
@@ -0,0 +1,19 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+use cssparser::{Parser, ParserInput};
+use style::stylesheets::supports_rule::SupportsCondition;
+use style_traits::ToCss;
+
+#[test]
+fn test_supports_condition() {
+ assert_roundtrip!(SupportsCondition::parse, "(margin: 1px)");
+ assert_roundtrip!(SupportsCondition::parse, "not (--be: to be)");
+ assert_roundtrip!(
+ SupportsCondition::parse,
+ "(color: blue) and future-extension(4)"
+ );
+ assert_roundtrip!(SupportsCondition::parse, "future-\\1 extension(4)");
+ assert_roundtrip!(SupportsCondition::parse, "((test))");
+}
diff --git a/servo/tests/unit/style/parsing/text_overflow.rs b/servo/tests/unit/style/parsing/text_overflow.rs
new file mode 100644
index 0000000000..505e6ffb8c
--- /dev/null
+++ b/servo/tests/unit/style/parsing/text_overflow.rs
@@ -0,0 +1,30 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+use parsing::parse;
+use style_traits::ToCss;
+
+#[test]
+fn test_text_overflow() {
+ use style::properties::longhands::text_overflow;
+
+ assert_roundtrip_with_context!(text_overflow::parse, r#"clip"#);
+ assert_roundtrip_with_context!(text_overflow::parse, r#"ellipsis"#);
+ assert_roundtrip_with_context!(text_overflow::parse, r#"clip ellipsis"#);
+ assert_roundtrip_with_context!(text_overflow::parse, r#""x""#);
+ assert_roundtrip_with_context!(text_overflow::parse, r#"'x'"#, r#""x""#);
+ assert_roundtrip_with_context!(text_overflow::parse, r#"clip "x""#);
+ assert_roundtrip_with_context!(text_overflow::parse, r#""x" clip"#);
+ assert_roundtrip_with_context!(text_overflow::parse, r#""x" "y""#);
+}
+
+#[test]
+fn test_text_overflow_parser_exhaustion() {
+ use style::properties::longhands::text_overflow;
+
+ assert_parser_exhausted!(text_overflow::parse, r#"clip rubbish"#, false);
+ assert_parser_exhausted!(text_overflow::parse, r#"clip"#, true);
+ assert_parser_exhausted!(text_overflow::parse, r#"ellipsis"#, true);
+ assert_parser_exhausted!(text_overflow::parse, r#"clip ellipsis"#, true);
+}
diff --git a/servo/tests/unit/style/parsing/transition_duration.rs b/servo/tests/unit/style/parsing/transition_duration.rs
new file mode 100644
index 0000000000..c771a58944
--- /dev/null
+++ b/servo/tests/unit/style/parsing/transition_duration.rs
@@ -0,0 +1,17 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+use parsing::parse;
+use style::properties::longhands::transition_duration;
+
+#[test]
+fn test_positive_transition_duration() {
+ assert!(parse(transition_duration::parse, "5s").is_ok());
+ assert!(parse(transition_duration::parse, "0s").is_ok());
+}
+
+#[test]
+fn test_negative_transition_duration() {
+ assert!(parse(transition_duration::parse, "-5s").is_err());
+}
diff --git a/servo/tests/unit/style/parsing/transition_timing_function.rs b/servo/tests/unit/style/parsing/transition_timing_function.rs
new file mode 100644
index 0000000000..5fbb89709e
--- /dev/null
+++ b/servo/tests/unit/style/parsing/transition_timing_function.rs
@@ -0,0 +1,62 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+use parsing::parse;
+use style::properties::longhands::transition_timing_function;
+use style_traits::ToCss;
+
+#[test]
+fn test_cubic_bezier() {
+ assert_roundtrip_with_context!(
+ transition_timing_function::parse,
+ "cubic-bezier(0, 0, 0, 0)"
+ );
+ assert_roundtrip_with_context!(
+ transition_timing_function::parse,
+ "cubic-bezier(0.25, 0, 0.5, 0)"
+ );
+ assert_roundtrip_with_context!(
+ transition_timing_function::parse,
+ "cubic-bezier(1, 1, 1, 1)"
+ );
+
+ // p1x and p2x values must be in range [0, 1]
+ assert!(parse(
+ transition_timing_function::parse,
+ "cubic-bezier(-1, 0, 0, 0"
+ )
+ .is_err());
+ assert!(parse(
+ transition_timing_function::parse,
+ "cubic-bezier(0, 0, -1, 0"
+ )
+ .is_err());
+ assert!(parse(
+ transition_timing_function::parse,
+ "cubic-bezier(-1, 0, -1, 0"
+ )
+ .is_err());
+
+ assert!(parse(transition_timing_function::parse, "cubic-bezier(2, 0, 0, 0").is_err());
+ assert!(parse(transition_timing_function::parse, "cubic-bezier(0, 0, 2, 0").is_err());
+ assert!(parse(transition_timing_function::parse, "cubic-bezier(2, 0, 2, 0").is_err());
+}
+
+#[test]
+fn test_steps() {
+ assert_roundtrip_with_context!(transition_timing_function::parse, "steps(1)");
+ assert_roundtrip_with_context!(transition_timing_function::parse, "steps( 1)", "steps(1)");
+ assert_roundtrip_with_context!(transition_timing_function::parse, "steps(1, start)");
+ assert_roundtrip_with_context!(
+ transition_timing_function::parse,
+ "steps(2, end) ",
+ "steps(2)"
+ );
+
+ // Step interval value must be an integer greater than 0
+ assert!(parse(transition_timing_function::parse, "steps(0)").is_err());
+ assert!(parse(transition_timing_function::parse, "steps(0.5)").is_err());
+ assert!(parse(transition_timing_function::parse, "steps(-1)").is_err());
+ assert!(parse(transition_timing_function::parse, "steps(1, middle)").is_err());
+}