mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
Merge pull request #3110 from SimonSapin/writing-modes-reftest
Add a basic CSS Writing Modes reftest
This commit is contained in:
commit
a0d70c4cfd
13 changed files with 554 additions and 86 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit b41f144a3a8b6388d0956f341bcffa5bbaecc899
|
||||
Subproject commit c733f78e06bd02f7498e93b391e0f6094d91786a
|
|
@ -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,
|
||||
|
|
415
src/test/harness/reftest/reftest.rs.orig
Normal file
415
src/test/harness/reftest/reftest.rs.orig
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
18
src/test/ref/vertical-lr-blocks.html
Normal file
18
src/test/ref/vertical-lr-blocks.html
Normal file
|
@ -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>
|
18
src/test/ref/vertical-lr-blocks_ref.html
Normal file
18
src/test/ref/vertical-lr-blocks_ref.html
Normal file
|
@ -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>
|
Loading…
Add table
Add a link
Reference in a new issue