diff options
author | Simon Sapin <simon.sapin@exyr.org> | 2014-08-27 01:07:45 +0100 |
---|---|---|
committer | Simon Sapin <simon.sapin@exyr.org> | 2014-08-27 01:07:45 +0100 |
commit | a0d70c4cfd57f3bdea6e150cc3d6e4ece7ead1dd (patch) | |
tree | 67a6c483062e6c3a2edd3c5f586b4a1556d40a56 /src | |
parent | c1ec0b11e92f395d0748955dcf25800ccacfd623 (diff) | |
parent | a29ab0e47cd0c4ce71a3b6f61f441c42e5b4f8be (diff) | |
download | servo-a0d70c4cfd57f3bdea6e150cc3d6e4ece7ead1dd.tar.gz servo-a0d70c4cfd57f3bdea6e150cc3d6e4ece7ead1dd.zip |
Merge pull request #3110 from SimonSapin/writing-modes-reftest
Add a basic CSS Writing Modes reftest
Diffstat (limited to 'src')
-rw-r--r-- | src/components/gfx/display_list/mod.rs | 25 | ||||
-rw-r--r-- | src/components/layout/block.rs | 39 | ||||
-rw-r--r-- | src/components/layout/flow.rs | 12 | ||||
-rw-r--r-- | src/components/layout/fragment.rs | 76 | ||||
-rw-r--r-- | src/components/layout/inline.rs | 13 | ||||
-rw-r--r-- | src/components/layout/layout_task.rs | 8 | ||||
-rw-r--r-- | src/components/util/geometry.rs | 3 | ||||
m--------- | src/support/geom/rust-geom | 0 | ||||
-rw-r--r-- | src/test/harness/reftest/reftest.rs | 10 | ||||
-rw-r--r-- | src/test/harness/reftest/reftest.rs.orig | 415 | ||||
-rw-r--r-- | src/test/ref/basic.list | 1 | ||||
-rw-r--r-- | src/test/ref/vertical-lr-blocks.html | 18 | ||||
-rw-r--r-- | src/test/ref/vertical-lr-blocks_ref.html | 18 |
13 files changed, 553 insertions, 85 deletions
diff --git a/src/components/gfx/display_list/mod.rs b/src/components/gfx/display_list/mod.rs index 21c8c8a25b5..0ad7d945ae1 100644 --- a/src/components/gfx/display_list/mod.rs +++ b/src/components/gfx/display_list/mod.rs @@ -316,7 +316,6 @@ impl DisplayList { } } - /// Appends the given item to the display list. pub fn push(&mut self, item: DisplayItem) { self.list.push(item) @@ -328,6 +327,14 @@ impl DisplayList { self.list.append(other.list) } + pub fn debug(&self) { + if log_enabled!(::log::DEBUG) { + for item in self.list.iter() { + item.debug_with_level(0); + } + } + } + /// Draws the display list into the given render context. The display list must be flattened /// first for correct painting. pub fn draw_into_context(&self, render_context: &mut RenderContext) { @@ -724,14 +731,14 @@ impl DisplayItem { } pub fn debug_with_level(&self, level: uint) { - let mut indent = String::new(); - for _ in range(0, level) { - indent.push_str("| ") - } - debug!("{}+ {}", indent, self); - for child in self.children() { - child.debug_with_level(level + 1); - } + let mut indent = String::new(); + for _ in range(0, level) { + indent.push_str("| ") + } + debug!("{}+ {}", indent, self); + for child in self.children() { + child.debug_with_level(level + 1); + } } } diff --git a/src/components/layout/block.rs b/src/components/layout/block.rs index ea65756c7de..4c17cebc97f 100644 --- a/src/components/layout/block.rs +++ b/src/components/layout/block.rs @@ -1110,16 +1110,18 @@ impl BlockFlow { .relative_containing_block_size, None); + // FIXME(#2795): Get the real container size + let container_size = Size2D::zero(); + // Add the box that starts the block context. let mut display_list = DisplayList::new(); - let mut accumulator = - self.fragment.build_display_list(&mut display_list, - layout_context, - self.base.abs_position - .add_point(&offset) - + rel_offset, - background_border_level, - None); + let mut accumulator = self.fragment.build_display_list( + &mut display_list, + layout_context, + self.base.abs_position + (offset + rel_offset).to_physical( + self.base.writing_mode, container_size), + background_border_level, + None); let mut child_layers = DList::new(); for kid in self.base.child_iter() { @@ -1592,17 +1594,22 @@ impl Flow for BlockFlow { } fn compute_absolute_position(&mut self) { + // FIXME(#2795): Get the real container size + let container_size = Size2D::zero(); + if self.is_absolutely_positioned() { + let position_start = self.base.position.start.to_physical( + self.base.writing_mode, container_size); self.base .absolute_position_info .absolute_containing_block_position = if self.is_fixed() { // The viewport is initially at (0, 0). - self.base.position.start + position_start } else { // Absolute position of the containing block + position of absolute flow w/r/t the // containing block. self.base.absolute_position_info.absolute_containing_block_position - .add_point(&self.base.position.start) + + position_start }; // Set the absolute position, which will be passed down later as part @@ -1622,8 +1629,8 @@ impl Flow for BlockFlow { if self.is_positioned() { self.base.absolute_position_info.absolute_containing_block_position = self.base.abs_position - .add_point(&self.generated_containing_block_rect().start) - + relative_offset + + (self.generated_containing_block_rect().start + + relative_offset).to_physical(self.base.writing_mode, container_size) } let float_offset = if self.is_float() { @@ -1640,14 +1647,14 @@ impl Flow for BlockFlow { // Process children. let this_position = self.base.abs_position; + let writing_mode = self.base.writing_mode; for kid in self.base.child_iter() { if !kid.is_absolutely_positioned() { let kid_base = flow::mut_base(kid); - kid_base.abs_position = - this_position - .add_point(&kid_base.position.start) + kid_base.abs_position = this_position + ( + kid_base.position.start .add_point(&float_offset) - + relative_offset; + + relative_offset).to_physical(writing_mode, container_size); kid_base.absolute_position_info = absolute_position_info } } diff --git a/src/components/layout/flow.rs b/src/components/layout/flow.rs index e401a1f75c6..27091eb7665 100644 --- a/src/components/layout/flow.rs +++ b/src/components/layout/flow.rs @@ -46,13 +46,15 @@ use table_cell::TableCellFlow; use wrapper::ThreadSafeLayoutNode; use collections::dlist::DList; +use geom::Point2D; use gfx::display_list::DisplayList; use gfx::render_task::RenderLayer; use servo_msg::compositor_msg::LayerId; use servo_util::geometry::Au; use servo_util::logical_geometry::WritingMode; -use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize}; +use servo_util::logical_geometry::{LogicalRect, LogicalSize}; use std::mem; +use std::num::Zero; use std::fmt; use std::iter::Zip; use std::sync::atomics::{AtomicUint, Relaxed, SeqCst}; @@ -596,7 +598,7 @@ pub struct AbsolutePositionInfo { /// The size of the containing block for relatively-positioned descendants. pub relative_containing_block_size: LogicalSize<Au>, /// The position of the absolute containing block. - pub absolute_containing_block_position: LogicalPoint<Au>, + pub absolute_containing_block_position: Point2D<Au>, /// Whether the absolute containing block forces positioned descendants to be layerized. /// /// FIXME(pcwalton): Move into `FlowFlags`. @@ -609,7 +611,7 @@ impl AbsolutePositionInfo { // of the root layer. AbsolutePositionInfo { relative_containing_block_size: LogicalSize::zero(writing_mode), - absolute_containing_block_position: LogicalPoint::zero(writing_mode), + absolute_containing_block_position: Zero::zero(), layers_needed_for_positioned_flows: false, } } @@ -660,7 +662,7 @@ pub struct BaseFlow { pub collapsible_margins: CollapsibleMargins, /// The position of this flow in page coordinates, computed during display list construction. - pub abs_position: LogicalPoint<Au>, + pub abs_position: Point2D<Au>, /// Details about descendants with position 'absolute' or 'fixed' for which we are the /// containing block. This is in tree order. This includes any direct children. @@ -724,7 +726,7 @@ impl BaseFlow { floats: Floats::new(writing_mode), collapsible_margins: CollapsibleMargins::new(), - abs_position: LogicalPoint::zero(writing_mode), + abs_position: Zero::zero(), abs_descendants: Descendants::new(), absolute_static_i_offset: Au::new(0), fixed_static_i_offset: Au::new(0), diff --git a/src/components/layout/fragment.rs b/src/components/layout/fragment.rs index 7f61517a328..316f29c0d23 100644 --- a/src/components/layout/fragment.rs +++ b/src/components/layout/fragment.rs @@ -37,7 +37,7 @@ use servo_net::image::holder::ImageHolder; use servo_net::local_image_cache::LocalImageCache; use servo_util::geometry::Au; use servo_util::geometry; -use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, LogicalMargin}; +use servo_util::logical_geometry::{LogicalRect, LogicalSize, LogicalMargin}; use servo_util::range::*; use servo_util::namespace; use servo_util::smallvec::SmallVec; @@ -776,15 +776,15 @@ impl Fragment { fn build_debug_borders_around_text_fragments(&self, display_list: &mut DisplayList, - flow_origin: LogicalPoint<Au>, + flow_origin: Point2D<Au>, text_fragment: &ScannedTextFragmentInfo) { - let mut fragment_bounds = self.border_box.clone(); - fragment_bounds.start.i = fragment_bounds.start.i + flow_origin.i; - fragment_bounds.start.b = fragment_bounds.start.b + flow_origin.b; // FIXME(#2795): Get the real container size let container_size = Size2D::zero(); - let absolute_fragment_bounds = fragment_bounds.to_physical( - self.style.writing_mode, container_size); + // Fragment position wrt to the owning flow. + let fragment_bounds = self.border_box.to_physical(self.style.writing_mode, container_size); + let absolute_fragment_bounds = Rect( + fragment_bounds.origin + flow_origin, + fragment_bounds.size); // Compute the text fragment bounds and draw a border surrounding them. let border_display_item = box BorderDisplayItem { @@ -797,13 +797,11 @@ impl Fragment { // Draw a rectangle representing the baselines. let ascent = text_fragment.run.ascent(); - let baseline = LogicalRect::new( - self.style.writing_mode, - fragment_bounds.start.i, - fragment_bounds.start.b + ascent, - fragment_bounds.size.inline, - Au(0) - ).to_physical(self.style.writing_mode, container_size); + let mut baseline = self.border_box.clone(); + baseline.start.b = baseline.start.b + ascent; + baseline.size.block = Au(0); + let mut baseline = baseline.to_physical(self.style.writing_mode, container_size); + baseline.origin = baseline.origin + flow_origin; let line_display_item = box LineDisplayItem { base: BaseDisplayItem::new(baseline, self.node, ContentStackingLevel), @@ -815,14 +813,14 @@ impl Fragment { fn build_debug_borders_around_fragment(&self, display_list: &mut DisplayList, - flow_origin: LogicalPoint<Au>) { - let mut fragment_bounds = self.border_box.clone(); - fragment_bounds.start.i = fragment_bounds.start.i + flow_origin.i; - fragment_bounds.start.b = fragment_bounds.start.b + flow_origin.b; + flow_origin: Point2D<Au>) { // FIXME(#2795): Get the real container size let container_size = Size2D::zero(); - let absolute_fragment_bounds = fragment_bounds.to_physical( - self.style.writing_mode, container_size); + // Fragment position wrt to the owning flow. + let fragment_bounds = self.border_box.to_physical(self.style.writing_mode, container_size); + let absolute_fragment_bounds = Rect( + fragment_bounds.origin + flow_origin, + fragment_bounds.size); // This prints a debug border around the border of this fragment. let border_display_item = box BorderDisplayItem { @@ -845,18 +843,17 @@ impl Fragment { pub fn build_display_list(&self, display_list: &mut DisplayList, layout_context: &LayoutContext, - flow_origin: LogicalPoint<Au>, + flow_origin: Point2D<Au>, background_and_border_level: BackgroundAndBorderLevel, inline_fragment_context: Option<InlineFragmentContext>) -> ChildDisplayListAccumulator { - // Fragment position wrt to the owning flow. - let mut fragment_bounds = self.border_box.clone(); - fragment_bounds.start.i = fragment_bounds.start.i + flow_origin.i; - fragment_bounds.start.b = fragment_bounds.start.b + flow_origin.b; // FIXME(#2795): Get the real container size let container_size = Size2D::zero(); - let absolute_fragment_bounds = fragment_bounds.to_physical( - self.style.writing_mode, container_size); + // Fragment position wrt to the owning flow. + let fragment_bounds = self.border_box.to_physical(self.style.writing_mode, container_size); + let absolute_fragment_bounds = Rect( + fragment_bounds.origin + flow_origin, + fragment_bounds.size); debug!("Fragment::build_display_list at rel={}, abs={}: {}", self.border_box, absolute_fragment_bounds, @@ -1413,21 +1410,18 @@ impl Fragment { #[inline(never)] fn finalize_position_and_size_of_iframe(&self, iframe_fragment: &IframeFragmentInfo, - offset: LogicalPoint<Au>, + offset: Point2D<Au>, layout_context: &LayoutContext) { - let inline_start = offset.i + self.margin.inline_start + self.border_padding.inline_start; - let block_start = offset.b + self.margin.block_start + self.border_padding.block_start; - let inline_size = self.content_box().size.inline; - let block_size = self.content_box().size.block; - // FIXME(#2795): Get the real container size - let container_size = Size2D::zero(); - let rect = LogicalRect::new( - self.style.writing_mode, - geometry::to_frac_px(inline_start) as f32, - geometry::to_frac_px(block_start) as f32, - geometry::to_frac_px(inline_size) as f32, - geometry::to_frac_px(block_size) as f32 - ).to_physical(self.style.writing_mode, container_size); + let mbp = (self.margin + self.border_padding).to_physical(self.style.writing_mode); + let content_size = self.content_box().size.to_physical(self.style.writing_mode); + + let left = offset.x + mbp.left; + let top = offset.y + mbp.top; + let width = content_size.width; + let height = content_size.height; + let origin = Point2D(geometry::to_frac_px(left) as f32, geometry::to_frac_px(top) as f32); + let size = Size2D(geometry::to_frac_px(width) as f32, geometry::to_frac_px(height) as f32); + let rect = Rect(origin, size); debug!("finalizing position and size of iframe for {:?},{:?}", iframe_fragment.pipeline_id, diff --git a/src/components/layout/inline.rs b/src/components/layout/inline.rs index 1a427f79c0b..9b6b0af28d7 100644 --- a/src/components/layout/inline.rs +++ b/src/components/layout/inline.rs @@ -16,7 +16,7 @@ use text; use wrapper::ThreadSafeLayoutNode; use collections::{Deque, RingBuf}; -use geom::Size2D; +use geom::Rect; use gfx::display_list::ContentLevel; use gfx::font::FontMetrics; use gfx::font_context::FontContext; @@ -927,12 +927,8 @@ impl InlineFlow { } pub fn build_display_list_inline(&mut self, layout_context: &LayoutContext) { - let abs_rect = LogicalRect::from_point_size( - self.base.writing_mode, self.base.abs_position, self.base.position.size); - // FIXME(#2795): Get the real container size - let container_size = Size2D::zero(); - if !abs_rect.to_physical(self.base.writing_mode, container_size) - .intersects(&layout_context.shared.dirty) { + let size = self.base.position.size.to_physical(self.base.writing_mode); + if !Rect(self.base.abs_position, size).intersects(&layout_context.shared.dirty) { return } @@ -947,7 +943,8 @@ impl InlineFlow { Some(context)); drop(fragment.build_display_list(&mut self.base.display_list, layout_context, - self.base.abs_position + rel_offset, + self.base.abs_position.add_size( + &rel_offset.to_physical(self.base.writing_mode)), ContentLevel, Some(context))); } diff --git a/src/components/layout/layout_task.rs b/src/components/layout/layout_task.rs index e4cba397379..e104ea443fe 100644 --- a/src/components/layout/layout_task.rs +++ b/src/components/layout/layout_task.rs @@ -46,6 +46,7 @@ use gfx::font_cache_task::{FontCacheTask}; use servo_net::local_image_cache::{ImageResponder, LocalImageCache}; use servo_util::geometry::Au; use servo_util::geometry; +use servo_util::logical_geometry::LogicalPoint; use servo_util::opts::Opts; use servo_util::smallvec::{SmallVec, SmallVec1}; use servo_util::time::{TimeProfilerChan, profile}; @@ -694,10 +695,10 @@ impl LayoutTask { if data.goal == ReflowForDisplay { let writing_mode = flow::base(layout_root.get()).writing_mode; profile(time::LayoutDispListBuildCategory, self.time_profiler_chan.clone(), || { - // FIXME(#2795): Get the real container size - let container_size = Size2D::zero(); shared_layout_ctx.dirty = flow::base(layout_root.get()).position.to_physical( - writing_mode, container_size); + writing_mode, self.screen_size); + flow::mut_base(layout_root.get_mut()).abs_position = + LogicalPoint::zero(writing_mode).to_physical(writing_mode, self.screen_size); match self.parallel_traversal { None => { @@ -718,6 +719,7 @@ impl LayoutTask { let root_display_list = mem::replace(&mut flow::mut_base(layout_root.get_mut()).display_list, DisplayList::new()); + root_display_list.debug(); let display_list = Arc::new(root_display_list.flatten(ContentStackingLevel)); // FIXME(pcwalton): This is really ugly and can't handle overflow: scroll. Refactor diff --git a/src/components/util/geometry.rs b/src/components/util/geometry.rs index 70882f17178..2b1082fa3d2 100644 --- a/src/components/util/geometry.rs +++ b/src/components/util/geometry.rs @@ -79,8 +79,7 @@ impl Default for Au { impl fmt::Show for Au { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let Au(n) = *self; - write!(f, "Au(au={} px={})", n, to_frac_px(*self)) + write!(f, "{}px", to_frac_px(*self)) }} impl Add<Au,Au> for Au { diff --git a/src/support/geom/rust-geom b/src/support/geom/rust-geom -Subproject b41f144a3a8b6388d0956f341bcffa5bbaecc89 +Subproject c733f78e06bd02f7498e93b391e0f6094d91786 diff --git a/src/test/harness/reftest/reftest.rs b/src/test/harness/reftest/reftest.rs index f0ec11ddc98..57738bbf517 100644 --- a/src/test/harness/reftest/reftest.rs +++ b/src/test/harness/reftest/reftest.rs @@ -114,6 +114,7 @@ struct Reftest { servo_args: Vec<String>, render_mode: RenderMode, is_flaky: bool, + experimental: bool, } struct TestLine<'a> { @@ -166,12 +167,14 @@ fn parse_lists(file: &str, servo_args: &[String], render_mode: RenderMode, id_of let mut conditions_list = test_line.conditions.split(','); let mut flakiness = RenderMode::empty(); + let mut experimental = false; for condition in conditions_list { match condition { "flaky_cpu" => flakiness.insert(CpuRendering), "flaky_gpu" => flakiness.insert(GpuRendering), "flaky_linux" => flakiness.insert(LinuxTarget), "flaky_macos" => flakiness.insert(MacOsTarget), + "experimental" => experimental = true, _ => (), } } @@ -184,6 +187,7 @@ fn parse_lists(file: &str, servo_args: &[String], render_mode: RenderMode, id_of render_mode: render_mode, servo_args: servo_args.iter().map(|x| x.clone()).collect(), is_flaky: render_mode.intersects(flakiness), + experimental: experimental, }; tests.push(make_test(reftest)); @@ -212,7 +216,11 @@ fn capture(reftest: &Reftest, side: uint) -> png::Image { if reftest.render_mode.contains(CpuRendering) { args.push("-c".to_string()); } - args.push_all_move(vec!("-f".to_string(), "-o".to_string(), filename.clone(), reftest.files[side].clone())); + if reftest.experimental { + args.push("--experimental".to_string()); + } + args.push_all(["-f".to_string(), "-o".to_string(), filename.clone(), + reftest.files[side].clone()]); let retval = match Command::new("./servo").args(args.as_slice()).status() { Ok(status) => status, diff --git a/src/test/harness/reftest/reftest.rs.orig b/src/test/harness/reftest/reftest.rs.orig new file mode 100644 index 00000000000..645a8d55307 --- /dev/null +++ b/src/test/harness/reftest/reftest.rs.orig @@ -0,0 +1,415 @@ +// 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. + +extern crate png; +extern crate std; +extern crate test; +extern crate regex; + +use std::ascii::StrAsciiExt; +use std::io; +use std::io::{File, Reader, Command}; +use std::io::process::ExitStatus; +use std::os; +use test::{AutoColor, DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn}; +use test::run_tests_console; +use regex::Regex; + + +bitflags!( + flags RenderMode: u32 { + static CpuRendering = 0x00000001, + static GpuRendering = 0x00000010, + static LinuxTarget = 0x00000100, + static MacOsTarget = 0x00001000, + static AndroidTarget = 0x00010000 + } +) + + +fn main() { + let args = os::args(); + let mut parts = args.tail().split(|e| "--" == e.as_slice()); + + let harness_args = parts.next().unwrap(); // .split() is never empty + let servo_args = parts.next().unwrap_or(&[]); + + let (render_mode_string, base_path, testname) = match harness_args { + [] | [_] => fail!("USAGE: cpu|gpu base_path [testname regex]"), + [ref render_mode_string, ref base_path] => (render_mode_string, base_path, None), + [ref render_mode_string, ref base_path, ref testname, ..] => (render_mode_string, base_path, Some(Regex::new(testname.as_slice()).unwrap())), + }; + + let mut render_mode = match render_mode_string.as_slice() { + "cpu" => CpuRendering, + "gpu" => GpuRendering, + _ => fail!("First argument must specify cpu or gpu as rendering mode") + }; + if cfg!(target_os = "linux") { + render_mode.insert(LinuxTarget); + } + if cfg!(target_os = "macos") { + render_mode.insert(MacOsTarget); + } + if cfg!(target_os = "android") { + render_mode.insert(AndroidTarget); + } + + let mut all_tests = vec!(); + println!("Scanning {} for manifests\n", base_path); + + for file in io::fs::walk_dir(&Path::new(base_path.as_slice())).unwrap() { + let maybe_extension = file.extension_str(); + match maybe_extension { + Some(extension) => { + if extension.to_ascii_lower().as_slice() == "list" && file.is_file() { + let manifest = file.as_str().unwrap(); + let tests = parse_lists(manifest, servo_args, render_mode); + println!("\t{} [{} tests]", manifest, tests.len()); + all_tests.push_all_move(tests); + } + } + _ => {} + } + } + + let test_opts = TestOpts { + filter: testname, + run_ignored: false, + logfile: None, + run_tests: true, + run_benchmarks: false, + ratchet_noise_percent: None, + ratchet_metrics: None, + save_metrics: None, + test_shard: None, + nocapture: false, + color: AutoColor + }; + + match run_tests_console(&test_opts, all_tests) { + Ok(false) => os::set_exit_status(1), // tests failed + Err(_) => os::set_exit_status(2), // I/O-related failure + _ => (), + } +} + +#[deriving(PartialEq)] +enum ReftestKind { + Same, + Different, +} + +struct Reftest { + name: String, + kind: ReftestKind, + files: [String, ..2], + id: uint, + servo_args: Vec<String>, + render_mode: RenderMode, +<<<<<<< HEAD + is_flaky: bool, +||||||| merged common ancestors + flakiness: uint, +======= + flakiness: uint, + experimental: bool, +>>>>>>> Reftests can opt into --experimental +} + +struct TestLine<'a> { + conditions: &'a str, + kind: &'a str, + file_left: &'a str, + file_right: &'a str, +} + +fn parse_lists(file: &str, servo_args: &[String], render_mode: RenderMode) -> Vec<TestDescAndFn> { + let mut tests = Vec::new(); + let mut next_id = 0; + let file_path = Path::new(file); + let contents = File::open_mode(&file_path, io::Open, io::Read) + .and_then(|mut f| f.read_to_string()) + .ok().expect("Could not read file"); + + for line in contents.as_slice().lines() { +<<<<<<< HEAD + // ignore comments or empty lines + if line.starts_with("#") || line.is_empty() { + continue; + } + + let parts: Vec<&str> = line.split(' ').filter(|p| !p.is_empty()).collect(); + + let test_line = match parts.len() { + 3 => TestLine { + conditions: "", + kind: parts[0], + file_left: parts[1], + file_right: parts[2], +||||||| merged common ancestors + // ignore comments or empty lines + if line.starts_with("#") || line.is_empty() { + continue; + } + + let parts: Vec<&str> = line.split(' ').filter(|p| !p.is_empty()).collect(); + + let test_line = match parts.len() { + 3 => { + TestLine { + conditions: "", + kind: parts[0], + file_left: parts[1], + file_right: parts[2], + } + }, + 4 => { + TestLine { + conditions: parts[0], + kind: parts[1], + file_left: parts[2], + file_right: parts[3], + } + }, + _ => { + fail!("reftest line: '{:s}' doesn't match '[CONDITIONS] KIND LEFT RIGHT'", line); + } + }; + + let kind = match test_line.kind { + "==" => Same, + "!=" => Different, + part => fail!("reftest line: '{:s}' has invalid kind '{:s}'", line, part) + }; + let src_path = file_path.dir_path(); + let src_dir = src_path.display().to_string(); + let file_left = src_dir.clone().append("/").append(test_line.file_left); + let file_right = src_dir.append("/").append(test_line.file_right); + + let mut conditions_list = test_line.conditions.split(','); + let mut flakiness = 0; + for condition in conditions_list { + match condition { + "flaky_cpu" => { + flakiness |= CpuRendering as uint; +======= + // ignore comments or empty lines + if line.starts_with("#") || line.is_empty() { + continue; + } + + let parts: Vec<&str> = line.split(' ').filter(|p| !p.is_empty()).collect(); + + let test_line = match parts.len() { + 3 => { + TestLine { + conditions: "", + kind: parts[0], + file_left: parts[1], + file_right: parts[2], + } + }, + 4 => { + TestLine { + conditions: parts[0], + kind: parts[1], + file_left: parts[2], + file_right: parts[3], + } + }, + _ => { + fail!("reftest line: '{:s}' doesn't match '[CONDITIONS] KIND LEFT RIGHT'", line); + } + }; + + let kind = match test_line.kind { + "==" => Same, + "!=" => Different, + part => fail!("reftest line: '{:s}' has invalid kind '{:s}'", line, part) + }; + let src_path = file_path.dir_path(); + let src_dir = src_path.display().to_string(); + let file_left = src_dir.clone().append("/").append(test_line.file_left); + let file_right = src_dir.append("/").append(test_line.file_right); + + let mut conditions_list = test_line.conditions.split(','); + let mut flakiness = 0; + let mut experimental = false; + for condition in conditions_list { + match condition { + "flaky_cpu" => { + flakiness |= CpuRendering as uint; +>>>>>>> Reftests can opt into --experimental + }, + 4 => TestLine { + conditions: parts[0], + kind: parts[1], + file_left: parts[2], + file_right: parts[3], + }, +<<<<<<< HEAD + _ => fail!("reftest line: '{:s}' doesn't match '[CONDITIONS] KIND LEFT RIGHT'", line), + }; + + let kind = match test_line.kind { + "==" => Same, + "!=" => Different, + part => fail!("reftest line: '{:s}' has invalid kind '{:s}'", line, part) + }; + let src_path = file_path.dir_path(); + let src_dir = src_path.display().to_string(); + let file_left = src_dir.clone().append("/").append(test_line.file_left); + let file_right = src_dir.append("/").append(test_line.file_right); + + let mut conditions_list = test_line.conditions.split(','); + let mut flakiness = RenderMode::empty(); + for condition in conditions_list { + match condition { + "flaky_cpu" => flakiness.insert(CpuRendering), + "flaky_gpu" => flakiness.insert(GpuRendering), + "flaky_linux" => flakiness.insert(LinuxTarget), + "flaky_macos" => flakiness.insert(MacOsTarget), + _ => (), + } + } + + let reftest = Reftest { + name: format!("{} {} {}", test_line.file_left, test_line.kind, test_line.file_right), + kind: kind, + files: [file_left, file_right], + id: next_id, + render_mode: render_mode, + servo_args: servo_args.iter().map(|x| x.clone()).collect(), + is_flaky: render_mode.intersects(flakiness), + }; + + next_id += 1; + + tests.push(make_test(reftest)); +||||||| merged common ancestors + _ => {} + } + } + + let reftest = Reftest { + name: test_line.file_left.to_string().append(" / ").append(test_line.file_right), + kind: kind, + files: [file_left, file_right], + id: next_id, + render_mode: render_mode, + servo_args: servo_args.iter().map(|x| x.clone()).collect(), + flakiness: flakiness, + }; + + next_id += 1; + + tests.push(make_test(reftest)); +======= + "experimental" => { + experimental = true; + }, + _ => {} + } + } + + let reftest = Reftest { + name: test_line.file_left.to_string().append(" / ").append(test_line.file_right), + kind: kind, + files: [file_left, file_right], + id: next_id, + render_mode: render_mode, + servo_args: servo_args.iter().map(|x| x.clone()).collect(), + flakiness: flakiness, + experimental: experimental, + }; + + next_id += 1; + + tests.push(make_test(reftest)); +>>>>>>> Reftests can opt into --experimental + } + tests +} + +fn make_test(reftest: Reftest) -> TestDescAndFn { + let name = reftest.name.clone(); + TestDescAndFn { + desc: TestDesc { + name: DynTestName(name), + ignore: false, + should_fail: false, + }, + testfn: DynTestFn(proc() { + check_reftest(reftest); + }), + } +} + +fn capture(reftest: &Reftest, side: uint) -> png::Image { + let filename = format!("/tmp/servo-reftest-{:06u}-{:u}.png", reftest.id, side); + let mut args = reftest.servo_args.clone(); + // GPU rendering is the default + if reftest.render_mode.contains(CpuRendering) { + args.push("-c".to_string()); + } + if reftest.experimental { + args.push("--experimental".to_string()); + } + args.push_all(["-f".to_string(), "-o".to_string(), filename.clone(), + reftest.files[side].clone()]); + + let retval = match Command::new("./servo").args(args.as_slice()).status() { + Ok(status) => status, + Err(e) => fail!("failed to execute process: {}", e), + }; + assert!(retval == ExitStatus(0)); + + png::load_png(&from_str::<Path>(filename.as_slice()).unwrap()).unwrap() +} + +fn check_reftest(reftest: Reftest) { + let left = capture(&reftest, 0); + let right = capture(&reftest, 1); + + let pixels = left.pixels.iter().zip(right.pixels.iter()).map(|(&a, &b)| { + if a as i8 - b as i8 == 0 { + // White for correct + 0xFF + } else { + // "1100" in the RGBA channel with an error for an incorrect value + // This results in some number of C0 and FFs, which is much more + // readable (and distinguishable) than the previous difference-wise + // scaling but does not require reconstructing the actual RGBA pixel. + 0xC0 + } + }).collect::<Vec<u8>>(); + + if pixels.iter().any(|&a| a < 255) { + let output_str = format!("/tmp/servo-reftest-{:06u}-diff.png", reftest.id); + let output = from_str::<Path>(output_str.as_slice()).unwrap(); + + let mut img = png::Image { + width: left.width, + height: left.height, + color_type: png::RGBA8, + pixels: pixels, + }; + let res = png::store_png(&mut img, &output); + assert!(res.is_ok()); + + match (reftest.kind, reftest.is_flaky) { + (Same, true) => println!("flaky test - rendering difference: {}", output_str), + (Same, false) => fail!("rendering difference: {}", output_str), + (Different, _) => {} // Result was different and that's what was expected + } + } else { + assert!(reftest.is_flaky || reftest.kind == Same); + } +} diff --git a/src/test/ref/basic.list b/src/test/ref/basic.list index 13835bf64ee..9884fe140b4 100644 --- a/src/test/ref/basic.list +++ b/src/test/ref/basic.list @@ -86,6 +86,7 @@ flaky_cpu == linebreak_simple_a.html linebreak_simple_b.html == position_fixed_overflow_a.html position_fixed_overflow_b.html == noscript.html noscript_ref.html == pseudo_inherit.html pseudo_inherit_ref.html +experimental == vertical-lr-blocks.html vertical-lr-blocks_ref.html == float_intrinsic_height.html float_intrinsic_height_ref.html == table_auto_width.html table_auto_width_ref.html == inline_whitespace_b.html inline_whitespace_ref.html diff --git a/src/test/ref/vertical-lr-blocks.html b/src/test/ref/vertical-lr-blocks.html new file mode 100644 index 00000000000..c68fee90788 --- /dev/null +++ b/src/test/ref/vertical-lr-blocks.html @@ -0,0 +1,18 @@ +<!doctype html> +<html> +<head> +<style> +html { writing-mode: vertical-rl } +body { margin: 10px } +div { border: blue solid 5px; line-height: 30px; height: 500px } +p { background: green; margin: 40px 20px } +p + p { margin-top: 60px } +</style> +</head> +<body> +<div> +<p> </p> +<p> </p> +</div> +</body> +</html> diff --git a/src/test/ref/vertical-lr-blocks_ref.html b/src/test/ref/vertical-lr-blocks_ref.html new file mode 100644 index 00000000000..8581695c49f --- /dev/null +++ b/src/test/ref/vertical-lr-blocks_ref.html @@ -0,0 +1,18 @@ +<!doctype html> +<html> +<head> +<style> +div { border: blue solid 5px; position: absolute; + top: 10px; right: 10px; bottom: 10px; width: 120px; height: 500px } +p { background: green; margin: 0; position: absolute; + top: 40px; right: 20px; bottom: 40px; width: 30px } +p + p { right: 70px; top: 60px } +</style> +</head> +<body> +<div> +<p> </p> +<p> </p> +</div> +</body> +</html> |