diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /servo/tests/unit/style/properties | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'servo/tests/unit/style/properties')
-rw-r--r-- | servo/tests/unit/style/properties/mod.rs | 70 | ||||
-rw-r--r-- | servo/tests/unit/style/properties/scaffolding.rs | 85 | ||||
-rw-r--r-- | servo/tests/unit/style/properties/serialization.rs | 773 |
3 files changed, 928 insertions, 0 deletions
diff --git a/servo/tests/unit/style/properties/mod.rs b/servo/tests/unit/style/properties/mod.rs new file mode 100644 index 0000000000..06a5c2086f --- /dev/null +++ b/servo/tests/unit/style/properties/mod.rs @@ -0,0 +1,70 @@ +/* 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::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) +} + +macro_rules! assert_roundtrip_with_context { + ($fun:expr, $string:expr) => { + assert_roundtrip_with_context!($fun, $string, $string); + }; + ($fun:expr, $input:expr, $output:expr) => {{ + let serialized = parse( + |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) + }, + $input, + ) + .unwrap(); + + let mut input = ::cssparser::ParserInput::new(&serialized); + let unwrapped = 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 + }}; +} + +mod scaffolding; +mod serialization; diff --git a/servo/tests/unit/style/properties/scaffolding.rs b/servo/tests/unit/style/properties/scaffolding.rs new file mode 100644 index 0000000000..68e1b96ddf --- /dev/null +++ b/servo/tests/unit/style/properties/scaffolding.rs @@ -0,0 +1,85 @@ +/* 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 serde_json::{self, Value}; +use std::env; +use std::fs::{remove_file, File}; +use std::path::Path; +use std::process::Command; + +#[test] +fn properties_list_json() { + let top = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()) + .join("..") + .join("..") + .join(".."); + let json = top + .join("target") + .join("doc") + .join("servo") + .join("css-properties.json"); + if json.exists() { + remove_file(&json).unwrap() + } + let python = env::var("PYTHON").ok().unwrap_or_else(find_python); + let script = top + .join("components") + .join("style") + .join("properties") + .join("build.py"); + let status = Command::new(python) + .arg(&script) + .arg("servo") + .arg("html") + .arg("regular") + .status() + .unwrap(); + assert!(status.success(), "{:?}", status); + + let properties: Value = serde_json::from_reader(File::open(json).unwrap()).unwrap(); + assert!(properties.as_object().unwrap().len() > 100); + assert!(properties.as_object().unwrap().contains_key("margin")); + assert!(properties.as_object().unwrap().contains_key("margin-top")); +} + +#[cfg(windows)] +fn find_python() -> String { + if Command::new("python2.7.exe") + .arg("--version") + .output() + .is_ok() + { + return "python2.7.exe".to_owned(); + } + + if Command::new("python27.exe") + .arg("--version") + .output() + .is_ok() + { + return "python27.exe".to_owned(); + } + + if Command::new("python.exe").arg("--version").output().is_ok() { + return "python.exe".to_owned(); + } + + panic!("Can't find python (tried python27.exe and python.exe)! Try fixing PATH or setting the PYTHON env var"); +} + +#[cfg(not(windows))] +fn find_python() -> String { + if Command::new("python2.7") + .arg("--version") + .output() + .unwrap() + .status + .success() + { + "python2.7" + } else { + "python" + } + .to_owned() +} diff --git a/servo/tests/unit/style/properties/serialization.rs b/servo/tests/unit/style/properties/serialization.rs new file mode 100644 index 0000000000..86fb09039e --- /dev/null +++ b/servo/tests/unit/style/properties/serialization.rs @@ -0,0 +1,773 @@ +/* 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 properties::{parse, parse_input}; +use style::computed_values::display::T as Display; +use style::properties::declaration_block::PropertyDeclarationBlock; +use style::properties::parse_property_declaration_list; +use style::properties::{Importance, PropertyDeclaration}; +use style::values::specified::url::SpecifiedUrl; +use style::values::specified::NoCalcLength; +use style::values::specified::{BorderSideWidth, BorderStyle, Color}; +use style::values::specified::{Length, LengthPercentage, LengthPercentageOrAuto}; +use style::values::RGBA; +use style_traits::ToCss; +use stylesheets::block_from; + +trait ToCssString { + fn to_css_string(&self) -> String; +} + +impl ToCssString for PropertyDeclarationBlock { + fn to_css_string(&self) -> String { + let mut css = String::new(); + self.to_css(&mut css).unwrap(); + css + } +} + +#[test] +fn property_declaration_block_should_serialize_correctly() { + use style::properties::longhands::overflow_x::SpecifiedValue as OverflowValue; + + let declarations = vec![ + ( + PropertyDeclaration::Width(LengthPercentageOrAuto::Length(NoCalcLength::from_px( + 70f32, + ))), + Importance::Normal, + ), + ( + PropertyDeclaration::MinHeight(LengthPercentage::Length(NoCalcLength::from_px(20f32))), + Importance::Normal, + ), + ( + PropertyDeclaration::Height(LengthPercentageOrAuto::Length(NoCalcLength::from_px( + 20f32, + ))), + Importance::Important, + ), + ( + PropertyDeclaration::Display(Display::InlineBlock), + Importance::Normal, + ), + ( + PropertyDeclaration::OverflowX(OverflowValue::Auto), + Importance::Normal, + ), + ( + PropertyDeclaration::OverflowY(OverflowValue::Auto), + Importance::Normal, + ), + ]; + + let block = block_from(declarations); + + let css_string = block.to_css_string(); + + assert_eq!( + css_string, + "width: 70px; min-height: 20px; height: 20px !important; display: inline-block; overflow: auto;" + ); +} + +mod shorthand_serialization { + pub use super::*; + + pub fn shorthand_properties_to_string(properties: Vec<PropertyDeclaration>) -> String { + let block = block_from(properties.into_iter().map(|d| (d, Importance::Normal))); + + block.to_css_string() + } + + mod four_sides_shorthands { + pub use super::*; + + // we can use margin as a base to test out the different combinations + // but afterwards, we only need to to one test per "four sides shorthand" + #[test] + fn all_equal_properties_should_serialize_to_one_value() { + let mut properties = Vec::new(); + + let px_70 = LengthPercentageOrAuto::Length(NoCalcLength::from_px(70f32)); + properties.push(PropertyDeclaration::MarginTop(px_70.clone())); + properties.push(PropertyDeclaration::MarginRight(px_70.clone())); + properties.push(PropertyDeclaration::MarginBottom(px_70.clone())); + properties.push(PropertyDeclaration::MarginLeft(px_70)); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!(serialization, "margin: 70px;"); + } + + #[test] + fn equal_vertical_and_equal_horizontal_properties_should_serialize_to_two_value() { + let mut properties = Vec::new(); + + let vertical_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(10f32)); + let horizontal_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(5f32)); + + properties.push(PropertyDeclaration::MarginTop(vertical_px.clone())); + properties.push(PropertyDeclaration::MarginRight(horizontal_px.clone())); + properties.push(PropertyDeclaration::MarginBottom(vertical_px)); + properties.push(PropertyDeclaration::MarginLeft(horizontal_px)); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!(serialization, "margin: 10px 5px;"); + } + + #[test] + fn different_vertical_and_equal_horizontal_properties_should_serialize_to_three_values() { + let mut properties = Vec::new(); + + let top_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(8f32)); + let bottom_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(10f32)); + let horizontal_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(5f32)); + + properties.push(PropertyDeclaration::MarginTop(top_px)); + properties.push(PropertyDeclaration::MarginRight(horizontal_px.clone())); + properties.push(PropertyDeclaration::MarginBottom(bottom_px)); + properties.push(PropertyDeclaration::MarginLeft(horizontal_px)); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!(serialization, "margin: 8px 5px 10px;"); + } + + #[test] + fn different_properties_should_serialize_to_four_values() { + let mut properties = Vec::new(); + + let top_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(8f32)); + let right_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(12f32)); + let bottom_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(10f32)); + let left_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(14f32)); + + properties.push(PropertyDeclaration::MarginTop(top_px)); + properties.push(PropertyDeclaration::MarginRight(right_px)); + properties.push(PropertyDeclaration::MarginBottom(bottom_px)); + properties.push(PropertyDeclaration::MarginLeft(left_px)); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!(serialization, "margin: 8px 12px 10px 14px;"); + } + + #[test] + fn different_longhands_should_serialize_to_long_form() { + let mut properties = Vec::new(); + + let solid = BorderStyle::Solid; + + properties.push(PropertyDeclaration::BorderTopStyle(solid.clone())); + properties.push(PropertyDeclaration::BorderRightStyle(solid.clone())); + properties.push(PropertyDeclaration::BorderBottomStyle(solid.clone())); + properties.push(PropertyDeclaration::BorderLeftStyle(solid.clone())); + + let px_30 = BorderSideWidth::Length(Length::from_px(30f32)); + let px_10 = BorderSideWidth::Length(Length::from_px(10f32)); + + properties.push(PropertyDeclaration::BorderTopWidth(px_30.clone())); + properties.push(PropertyDeclaration::BorderRightWidth(px_30.clone())); + properties.push(PropertyDeclaration::BorderBottomWidth(px_30.clone())); + properties.push(PropertyDeclaration::BorderLeftWidth(px_10.clone())); + + let blue = Color::rgba(RGBA::new(0, 0, 255, 255)); + + properties.push(PropertyDeclaration::BorderTopColor(blue.clone())); + properties.push(PropertyDeclaration::BorderRightColor(blue.clone())); + properties.push(PropertyDeclaration::BorderBottomColor(blue.clone())); + properties.push(PropertyDeclaration::BorderLeftColor(blue.clone())); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!(serialization, + "border-style: solid; border-width: 30px 30px 30px 10px; border-color: rgb(0, 0, 255);"); + } + + #[test] + fn same_longhands_should_serialize_correctly() { + let mut properties = Vec::new(); + + let solid = BorderStyle::Solid; + + properties.push(PropertyDeclaration::BorderTopStyle(solid.clone())); + properties.push(PropertyDeclaration::BorderRightStyle(solid.clone())); + properties.push(PropertyDeclaration::BorderBottomStyle(solid.clone())); + properties.push(PropertyDeclaration::BorderLeftStyle(solid.clone())); + + let px_30 = BorderSideWidth::Length(Length::from_px(30f32)); + + properties.push(PropertyDeclaration::BorderTopWidth(px_30.clone())); + properties.push(PropertyDeclaration::BorderRightWidth(px_30.clone())); + properties.push(PropertyDeclaration::BorderBottomWidth(px_30.clone())); + properties.push(PropertyDeclaration::BorderLeftWidth(px_30.clone())); + + let blue = Color::rgba(RGBA::new(0, 0, 255, 255)); + + properties.push(PropertyDeclaration::BorderTopColor(blue.clone())); + properties.push(PropertyDeclaration::BorderRightColor(blue.clone())); + properties.push(PropertyDeclaration::BorderBottomColor(blue.clone())); + properties.push(PropertyDeclaration::BorderLeftColor(blue.clone())); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!( + serialization, + "border-style: solid; border-width: 30px; border-color: rgb(0, 0, 255);" + ); + } + + #[test] + fn padding_should_serialize_correctly() { + use style::values::specified::NonNegativeLengthPercentage; + + let mut properties = Vec::new(); + + let px_10: NonNegativeLengthPercentage = NoCalcLength::from_px(10f32).into(); + let px_15: NonNegativeLengthPercentage = NoCalcLength::from_px(15f32).into(); + properties.push(PropertyDeclaration::PaddingTop(px_10.clone())); + properties.push(PropertyDeclaration::PaddingRight(px_15.clone())); + properties.push(PropertyDeclaration::PaddingBottom(px_10)); + properties.push(PropertyDeclaration::PaddingLeft(px_15)); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!(serialization, "padding: 10px 15px;"); + } + + #[test] + fn border_width_should_serialize_correctly() { + let mut properties = Vec::new(); + + let top_px = BorderSideWidth::Length(Length::from_px(10f32)); + let bottom_px = BorderSideWidth::Length(Length::from_px(10f32)); + + let right_px = BorderSideWidth::Length(Length::from_px(15f32)); + let left_px = BorderSideWidth::Length(Length::from_px(15f32)); + + properties.push(PropertyDeclaration::BorderTopWidth(top_px)); + properties.push(PropertyDeclaration::BorderRightWidth(right_px)); + properties.push(PropertyDeclaration::BorderBottomWidth(bottom_px)); + properties.push(PropertyDeclaration::BorderLeftWidth(left_px)); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!(serialization, "border-width: 10px 15px;"); + } + + #[test] + fn border_width_with_keywords_should_serialize_correctly() { + let mut properties = Vec::new(); + + let top_px = BorderSideWidth::Thin; + let right_px = BorderSideWidth::Medium; + let bottom_px = BorderSideWidth::Thick; + let left_px = BorderSideWidth::Length(Length::from_px(15f32)); + + properties.push(PropertyDeclaration::BorderTopWidth(top_px)); + properties.push(PropertyDeclaration::BorderRightWidth(right_px)); + properties.push(PropertyDeclaration::BorderBottomWidth(bottom_px)); + properties.push(PropertyDeclaration::BorderLeftWidth(left_px)); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!(serialization, "border-width: thin medium thick 15px;"); + } + + #[test] + fn border_color_should_serialize_correctly() { + let mut properties = Vec::new(); + + let red = Color::rgba(RGBA::new(255, 0, 0, 255)); + let blue = Color::rgba(RGBA::new(0, 0, 255, 255)); + + properties.push(PropertyDeclaration::BorderTopColor(blue.clone())); + properties.push(PropertyDeclaration::BorderRightColor(red.clone())); + properties.push(PropertyDeclaration::BorderBottomColor(blue)); + properties.push(PropertyDeclaration::BorderLeftColor(red)); + + let serialization = shorthand_properties_to_string(properties); + + // TODO: Make the rgb test show border-color as blue red instead of below tuples + assert_eq!( + serialization, + "border-color: rgb(0, 0, 255) rgb(255, 0, 0);" + ); + } + + #[test] + fn border_style_should_serialize_correctly() { + let mut properties = Vec::new(); + + let solid = BorderStyle::Solid; + let dotted = BorderStyle::Dotted; + properties.push(PropertyDeclaration::BorderTopStyle(solid.clone())); + properties.push(PropertyDeclaration::BorderRightStyle(dotted.clone())); + properties.push(PropertyDeclaration::BorderBottomStyle(solid)); + properties.push(PropertyDeclaration::BorderLeftStyle(dotted)); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!(serialization, "border-style: solid dotted;"); + } + + use style::values::specified::{BorderCornerRadius, Percentage}; + + #[test] + fn border_radius_should_serialize_correctly() { + let mut properties = Vec::new(); + properties.push(PropertyDeclaration::BorderTopLeftRadius(Box::new( + BorderCornerRadius::new(Percentage::new(0.01).into(), Percentage::new(0.05).into()), + ))); + properties.push(PropertyDeclaration::BorderTopRightRadius(Box::new( + BorderCornerRadius::new(Percentage::new(0.02).into(), Percentage::new(0.06).into()), + ))); + properties.push(PropertyDeclaration::BorderBottomRightRadius(Box::new( + BorderCornerRadius::new(Percentage::new(0.03).into(), Percentage::new(0.07).into()), + ))); + properties.push(PropertyDeclaration::BorderBottomLeftRadius(Box::new( + BorderCornerRadius::new(Percentage::new(0.04).into(), Percentage::new(0.08).into()), + ))); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!(serialization, "border-radius: 1% 2% 3% 4% / 5% 6% 7% 8%;"); + } + } + + mod border_shorthands { + use super::*; + + #[test] + fn border_top_and_color() { + let mut properties = Vec::new(); + properties.push(PropertyDeclaration::BorderTopWidth( + BorderSideWidth::Length(Length::from_px(1.)), + )); + properties.push(PropertyDeclaration::BorderTopStyle(BorderStyle::Solid)); + let c = Color::Numeric { + parsed: RGBA::new(255, 0, 0, 255), + authored: Some("green".to_string().into_boxed_str()), + }; + properties.push(PropertyDeclaration::BorderTopColor(c)); + let c = Color::Numeric { + parsed: RGBA::new(0, 255, 0, 255), + authored: Some("red".to_string().into_boxed_str()), + }; + properties.push(PropertyDeclaration::BorderTopColor(c.clone())); + properties.push(PropertyDeclaration::BorderBottomColor(c.clone())); + properties.push(PropertyDeclaration::BorderLeftColor(c.clone())); + properties.push(PropertyDeclaration::BorderRightColor(c.clone())); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!( + serialization, + "border-top: 1px solid red; border-color: red;" + ); + } + + #[test] + fn border_color_and_top() { + let mut properties = Vec::new(); + let c = Color::Numeric { + parsed: RGBA::new(0, 255, 0, 255), + authored: Some("red".to_string().into_boxed_str()), + }; + properties.push(PropertyDeclaration::BorderTopColor(c.clone())); + properties.push(PropertyDeclaration::BorderBottomColor(c.clone())); + properties.push(PropertyDeclaration::BorderLeftColor(c.clone())); + properties.push(PropertyDeclaration::BorderRightColor(c.clone())); + + properties.push(PropertyDeclaration::BorderTopWidth( + BorderSideWidth::Length(Length::from_px(1.)), + )); + properties.push(PropertyDeclaration::BorderTopStyle(BorderStyle::Solid)); + let c = Color::Numeric { + parsed: RGBA::new(255, 0, 0, 255), + authored: Some("green".to_string().into_boxed_str()), + }; + properties.push(PropertyDeclaration::BorderTopColor(c)); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!( + serialization, + "border-color: green red red; border-top: 1px solid green;" + ); + } + + // we can use border-top as a base to test out the different combinations + // but afterwards, we only need to to one test per "directional border shorthand" + + #[test] + fn directional_border_should_show_all_properties_when_values_are_set() { + let mut properties = Vec::new(); + + let width = BorderSideWidth::Length(Length::from_px(4f32)); + let style = BorderStyle::Solid; + let color = RGBA::new(255, 0, 0, 255).into(); + + properties.push(PropertyDeclaration::BorderTopWidth(width)); + properties.push(PropertyDeclaration::BorderTopStyle(style)); + properties.push(PropertyDeclaration::BorderTopColor(color)); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!(serialization, "border-top: 4px solid rgb(255, 0, 0);"); + } + + fn get_border_property_values() -> (BorderSideWidth, BorderStyle, Color) { + ( + BorderSideWidth::Length(Length::from_px(4f32)), + BorderStyle::Solid, + Color::currentcolor(), + ) + } + + #[test] + fn border_top_should_serialize_correctly() { + let mut properties = Vec::new(); + let (width, style, color) = get_border_property_values(); + properties.push(PropertyDeclaration::BorderTopWidth(width)); + properties.push(PropertyDeclaration::BorderTopStyle(style)); + properties.push(PropertyDeclaration::BorderTopColor(color)); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!(serialization, "border-top: 4px solid;"); + } + + #[test] + fn border_right_should_serialize_correctly() { + let mut properties = Vec::new(); + let (width, style, color) = get_border_property_values(); + properties.push(PropertyDeclaration::BorderRightWidth(width)); + properties.push(PropertyDeclaration::BorderRightStyle(style)); + properties.push(PropertyDeclaration::BorderRightColor(color)); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!(serialization, "border-right: 4px solid;"); + } + + #[test] + fn border_bottom_should_serialize_correctly() { + let mut properties = Vec::new(); + let (width, style, color) = get_border_property_values(); + properties.push(PropertyDeclaration::BorderBottomWidth(width)); + properties.push(PropertyDeclaration::BorderBottomStyle(style)); + properties.push(PropertyDeclaration::BorderBottomColor(color)); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!(serialization, "border-bottom: 4px solid;"); + } + + #[test] + fn border_left_should_serialize_correctly() { + let mut properties = Vec::new(); + let (width, style, color) = get_border_property_values(); + properties.push(PropertyDeclaration::BorderLeftWidth(width)); + properties.push(PropertyDeclaration::BorderLeftStyle(style)); + properties.push(PropertyDeclaration::BorderLeftColor(color)); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!(serialization, "border-left: 4px solid;"); + } + + #[test] + fn border_should_serialize_correctly() { + // According to https://drafts.csswg.org/css-backgrounds-3/#the-border-shorthands, + // the ‘border’ shorthand resets ‘border-image’ to its initial value. To verify the + // serialization of 'border' shorthand, we need to set 'border-image' as well. + let block_text = "\ + border-top: 4px solid; \ + border-right: 4px solid; \ + border-bottom: 4px solid; \ + border-left: 4px solid; \ + border-image: none;"; + + let block = + parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap(); + + let serialization = block.to_css_string(); + + assert_eq!(serialization, "border: 4px solid;"); + } + } + + mod background { + use super::*; + + #[test] + fn background_should_serialize_all_available_properties_when_specified() { + let block_text = "\ + background-color: rgb(255, 0, 0); \ + background-image: url(\"http://servo/test.png\"); \ + background-repeat: repeat-x; \ + background-attachment: scroll; \ + background-size: 70px 50px; \ + background-position-x: 7px; \ + background-position-y: bottom 4px; \ + background-origin: border-box; \ + background-clip: padding-box;"; + + let block = + parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap(); + + let serialization = block.to_css_string(); + + assert_eq!( + serialization, + "background: rgb(255, 0, 0) url(\"http://servo/test.png\") repeat-x \ + scroll left 7px bottom 4px / 70px 50px border-box padding-box;" + ); + } + + #[test] + fn background_should_combine_origin_and_clip_properties_when_equal() { + let block_text = "\ + background-color: rgb(255, 0, 0); \ + background-image: url(\"http://servo/test.png\"); \ + background-repeat: repeat-x; \ + background-attachment: scroll; \ + background-size: 70px 50px; \ + background-position-x: 7px; \ + background-position-y: 4px; \ + background-origin: padding-box; \ + background-clip: padding-box;"; + + let block = + parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap(); + + let serialization = block.to_css_string(); + + assert_eq!( + serialization, + "background: rgb(255, 0, 0) url(\"http://servo/test.png\") repeat-x \ + scroll 7px 4px / 70px 50px padding-box;" + ); + } + + #[test] + fn serialize_multiple_backgrounds() { + let block_text = "\ + background-color: rgb(0, 0, 255); \ + background-image: url(\"http://servo/test.png\"), none; \ + background-repeat: repeat-x, repeat-y; \ + background-attachment: scroll, scroll; \ + background-size: 70px 50px, 20px 30px; \ + background-position-x: 7px, 70px; \ + background-position-y: 4px, 40px; \ + background-origin: border-box, padding-box; \ + background-clip: padding-box, padding-box;"; + + let block = + parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap(); + + let serialization = block.to_css_string(); + + assert_eq!( + serialization, "background: \ + url(\"http://servo/test.png\") repeat-x scroll 7px 4px / 70px 50px border-box padding-box, \ + rgb(0, 0, 255) none repeat-y scroll 70px 40px / 20px 30px padding-box;" + ); + } + + #[test] + fn serialize_multiple_backgrounds_unequal_property_lists() { + // When the lengths of property values are different, the shorthand serialization + // should not be used. Previously the implementation cycled values if the lists were + // uneven. This is incorrect, in that we should serialize to a shorthand only when the + // lists have the same length (this affects background, transition and animation). + // https://github.com/servo/servo/issues/15398 ) + // With background, the color is one exception as it should only appear once for + // multiple backgrounds. + // Below background-origin only has one value. + let block_text = "\ + background-color: rgb(0, 0, 255); \ + background-image: url(\"http://servo/test.png\"), none; \ + background-repeat: repeat-x, repeat-y; \ + background-attachment: scroll, scroll; \ + background-size: 70px 50px, 20px 30px; \ + background-position: 7px 4px, 5px 6px; \ + background-origin: border-box; \ + background-clip: padding-box, padding-box;"; + + let block = + parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap(); + + let serialization = block.to_css_string(); + + assert_eq!(serialization, block_text); + } + + #[test] + fn background_position_should_be_a_valid_form_its_longhands() { + // If there is any longhand consisted of both keyword and position, + // the shorthand result should be the 4-value format. + let block_text = "\ + background-position-x: 30px;\ + background-position-y: bottom 20px;"; + let block = + parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap(); + let serialization = block.to_css_string(); + assert_eq!(serialization, "background-position: left 30px bottom 20px;"); + + // If there is no longhand consisted of both keyword and position, + // the shorthand result should be the 2-value format. + let block_text = "\ + background-position-x: center;\ + background-position-y: 20px;"; + let block = + parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap(); + let serialization = block.to_css_string(); + assert_eq!(serialization, "background-position: center 20px;"); + } + } + + mod quotes { + pub use super::*; + + #[test] + fn should_serialize_none_correctly() { + use style::properties::longhands::quotes; + + assert_roundtrip_with_context!(quotes::parse, "none"); + } + } + + mod animation { + pub use super::*; + + #[test] + fn serialize_single_animation() { + let block_text = "\ + animation-name: bounce;\ + animation-duration: 1s;\ + animation-timing-function: ease-in;\ + animation-delay: 0s;\ + animation-direction: normal;\ + animation-fill-mode: forwards;\ + animation-iteration-count: infinite;\ + animation-play-state: paused;"; + + let block = + parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap(); + + let serialization = block.to_css_string(); + + assert_eq!( + serialization, + "animation: 1s ease-in 0s infinite normal forwards paused bounce;" + ) + } + + #[test] + fn serialize_multiple_animations() { + let block_text = "\ + animation-name: bounce, roll;\ + animation-duration: 1s, 0.2s;\ + animation-timing-function: ease-in, linear;\ + animation-delay: 0s, 1s;\ + animation-direction: normal, reverse;\ + animation-fill-mode: forwards, backwards;\ + animation-iteration-count: infinite, 2;\ + animation-play-state: paused, running;"; + + let block = + parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap(); + + let serialization = block.to_css_string(); + + assert_eq!( + serialization, + "animation: 1s ease-in 0s infinite normal forwards paused bounce, \ + 0.2s linear 1s 2 reverse backwards running roll;" + ); + } + + #[test] + fn serialize_multiple_animations_unequal_property_lists() { + // When the lengths of property values are different, the shorthand serialization + // should not be used. Previously the implementation cycled values if the lists were + // uneven. This is incorrect, in that we should serialize to a shorthand only when the + // lists have the same length (this affects background, transition and animation). + // https://github.com/servo/servo/issues/15398 ) + let block_text = "\ + animation-name: bounce, roll, flip, jump; \ + animation-duration: 1s, 0.2s; \ + animation-timing-function: ease-in, linear; \ + animation-delay: 0s, 1s, 0.5s; \ + animation-direction: normal; \ + animation-fill-mode: forwards, backwards; \ + animation-iteration-count: infinite, 2; \ + animation-play-state: paused, running;"; + + let block = + parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap(); + + let serialization = block.to_css_string(); + + assert_eq!(serialization, block_text); + } + + #[test] + fn serialize_multiple_without_all_properties_returns_longhand() { + // timing function and direction are missing, so no shorthand is returned. + let block_text = "animation-name: bounce, roll; \ + animation-duration: 1s, 0.2s; \ + animation-delay: 0s, 1s; \ + animation-fill-mode: forwards, backwards; \ + animation-iteration-count: infinite, 2; \ + animation-play-state: paused, running;"; + + let block = + parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap(); + + let serialization = block.to_css_string(); + + assert_eq!(serialization, block_text); + } + } + + mod keywords { + pub use super::*; + #[test] + fn css_wide_keywords_should_be_parsed() { + let block_text = "--a:inherit;"; + let block = + parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap(); + + let serialization = block.to_css_string(); + assert_eq!(serialization, "--a: inherit;"); + } + + #[test] + fn non_keyword_custom_property_should_be_unparsed() { + let block_text = "--main-color: #06c;"; + let block = + parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap(); + + let serialization = block.to_css_string(); + assert_eq!(serialization, block_text); + } + } + + mod effects { + pub use super::*; + pub use style::properties::longhands::box_shadow::SpecifiedValue as BoxShadowList; + pub use style::values::specified::effects::{BoxShadow, SimpleShadow}; + + #[test] + fn box_shadow_should_serialize_correctly() { + use style::values::specified::length::NonNegativeLength; + + let mut properties = Vec::new(); + let shadow_val = BoxShadow { + base: SimpleShadow { + color: None, + horizontal: Length::from_px(1f32), + vertical: Length::from_px(2f32), + blur: Some(NonNegativeLength::from_px(3f32)), + }, + spread: Some(Length::from_px(4f32)), + inset: false, + }; + let shadow_decl = BoxShadowList(vec![shadow_val]); + properties.push(PropertyDeclaration::BoxShadow(shadow_decl)); + let shadow_css = "box-shadow: 1px 2px 3px 4px;"; + let shadow = + parse(|c, i| Ok(parse_property_declaration_list(c, i)), shadow_css).unwrap(); + + assert_eq!(shadow.to_css_string(), shadow_css); + } + } +} |