From b7401b9562792f3c9019609b9302b4d77ff31037 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 18 Aug 2014 22:33:38 +0100 Subject: [PATCH 1/6] Only write the px value in Show for Au The integer Au value is mostly internal and does not make much sense. Printing both values is very verbose and hurts readability of debug logs. --- src/components/util/geometry.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 for Au { From 36ed8d4212c979bca4c4ea54644e6048c1852c23 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 18 Aug 2014 23:28:45 +0100 Subject: [PATCH 2/6] Dump display lists when debug!() is enabled for gfx::display_list --- src/components/gfx/display_list/mod.rs | 25 ++++++++++++++++--------- src/components/layout/layout_task.rs | 1 + 2 files changed, 17 insertions(+), 9 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/layout_task.rs b/src/components/layout/layout_task.rs index e4cba397379..90de75b8bcd 100644 --- a/src/components/layout/layout_task.rs +++ b/src/components/layout/layout_task.rs @@ -718,6 +718,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 From 353b1a17d5fe66097755517dec8cda0dc38f99d5 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 19 Aug 2014 00:56:28 +0100 Subject: [PATCH 3/6] Convert back to physical some layout coordinates. --- src/components/layout/block.rs | 39 +++++++++------- src/components/layout/flow.rs | 12 +++-- src/components/layout/fragment.rs | 76 ++++++++++++++----------------- src/components/layout/inline.rs | 13 ++---- src/support/geom/rust-geom | 2 +- 5 files changed, 71 insertions(+), 71 deletions(-) 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, /// The position of the absolute containing block. - pub absolute_containing_block_position: LogicalPoint, + pub absolute_containing_block_position: Point2D, /// 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, + pub abs_position: Point2D, /// 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, + flow_origin: Point2D, 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) { - 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) { // 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, + flow_origin: Point2D, background_and_border_level: BackgroundAndBorderLevel, inline_fragment_context: Option) -> 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, + offset: Point2D, 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/support/geom/rust-geom b/src/support/geom/rust-geom index b41f144a3a8..c733f78e06b 160000 --- a/src/support/geom/rust-geom +++ b/src/support/geom/rust-geom @@ -1 +1 @@ -Subproject commit b41f144a3a8b6388d0956f341bcffa5bbaecc899 +Subproject commit c733f78e06bd02f7498e93b391e0f6094d91786a From 003c92f9bba2a59fe7d731e1fe51708deab04244 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 19 Aug 2014 01:01:18 +0100 Subject: [PATCH 4/6] Set the origin of the root element per its writing mode. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s not always (0, 0) anymore. --- src/components/layout/layout_task.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/layout/layout_task.rs b/src/components/layout/layout_task.rs index 90de75b8bcd..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 => { From 2e6b34f59895af11e62c9d14b577ffd8a7ef4684 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 19 Aug 2014 01:02:27 +0100 Subject: [PATCH 5/6] Reftests can opt into --experimental --- src/test/harness/reftest/reftest.rs | 10 +- src/test/harness/reftest/reftest.rs.orig | 415 +++++++++++++++++++++++ 2 files changed, 424 insertions(+), 1 deletion(-) create mode 100644 src/test/harness/reftest/reftest.rs.orig 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, 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 or the MIT license +// , 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, + 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 { + 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::(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::>(); + + if pixels.iter().any(|&a| a < 255) { + let output_str = format!("/tmp/servo-reftest-{:06u}-diff.png", reftest.id); + let output = from_str::(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); + } +} From a29ab0e47cd0c4ce71a3b6f61f441c42e5b4f8be Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 9 Jul 2014 20:34:50 +0100 Subject: [PATCH 6/6] Add a reftest for basic block layout in a vertical writing mode. --- src/test/ref/basic.list | 1 + src/test/ref/vertical-lr-blocks.html | 18 ++++++++++++++++++ src/test/ref/vertical-lr-blocks_ref.html | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 src/test/ref/vertical-lr-blocks.html create mode 100644 src/test/ref/vertical-lr-blocks_ref.html 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 @@ + + + + + + +
+

 

+

 

+
+ + 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 @@ + + + + + + +
+

 

+

 

+
+ +