servo: Merge #16870 - Add size_of tests for geckolib selectors (from servo:size_of); r=emilio
authorSimon Sapin <simon.sapin@exyr.org>
Tue, 16 May 2017 04:00:30 -0500
changeset 358599 eefe541d1ec422fde53a3ac5fce81f89ca3fb9a8
parent 358598 8d5128976a5b96f48b0d53a040121178f846b8b0
child 358600 ac59d773e2c278fa50f4cd94013fd1b4e0daa574
push id90353
push userryanvm@gmail.com
push dateWed, 17 May 2017 00:10:47 +0000
treeherdermozilla-inbound@41958333867b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #16870 - Add size_of tests for geckolib selectors (from servo:size_of); r=emilio First step of https://bugzilla.mozilla.org/show_bug.cgi?id=1364148 Source-Repo: https://github.com/servo/servo Source-Revision: ababebcced62d3a9bd09f002f1bf27b1b2691ea6
servo/Cargo.lock
servo/components/selectors/Cargo.toml
servo/components/selectors/gecko_like_types.rs
servo/components/selectors/lib.rs
servo/components/selectors/parser.rs
servo/components/selectors/size_of_tests.rs
servo/components/size_of_test/Cargo.toml
servo/components/size_of_test/lib.rs
servo/components/style/gecko/selector_parser.rs
servo/tests/unit/layout/Cargo.toml
servo/tests/unit/layout/lib.rs
servo/tests/unit/layout/size_of.rs
servo/tests/unit/stylo/Cargo.toml
servo/tests/unit/stylo/size_of.rs
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -1402,16 +1402,17 @@ dependencies = [
 
 [[package]]
 name = "layout_tests"
 version = "0.0.1"
 dependencies = [
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "layout 0.0.1",
  "script_layout_interface 0.0.1",
+ "size_of_test 0.0.1",
 ]
 
 [[package]]
 name = "layout_thread"
 version = "0.0.1"
 dependencies = [
  "app_units 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2438,16 +2439,17 @@ dependencies = [
 name = "selectors"
 version = "0.18.0"
 dependencies = [
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "precomputed-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "size_of_test 0.0.1",
  "smallvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "semver"
 version = "0.1.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
@@ -2728,16 +2730,20 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "siphasher"
 version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "size_of_test"
+version = "0.0.1"
+
+[[package]]
 name = "slab"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "smallvec"
 version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
--- a/servo/components/selectors/Cargo.toml
+++ b/servo/components/selectors/Cargo.toml
@@ -12,15 +12,21 @@ keywords = ["css", "selectors"]
 license = "MPL-2.0"
 
 [lib]
 name = "selectors"
 path = "lib.rs"
 # https://github.com/servo/servo/issues/16710
 doctest = false
 
+[features]
+gecko_like_types = []
+
 [dependencies]
 bitflags = "0.7"
 matches = "0.1"
 cssparser = "0.13.3"
 fnv = "1.0"
 precomputed-hash = "0.1"
 smallvec = "0.3"
+
+[dev-dependencies]
+size_of_test = {path = "../size_of_test"}
new file mode 100644
--- /dev/null
+++ b/servo/components/selectors/gecko_like_types.rs
@@ -0,0 +1,29 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! These types need to have the same size and alignment as the respectively corresponding
+//! types in components/style/gecko/selector_parser.rs
+
+#[derive(Eq, PartialEq, Clone, Debug)]
+#[allow(dead_code)]
+pub enum PseudoClass {
+    Bare,
+    String(Box<[u16]>),
+    MozAny(Box<[()]>),
+}
+
+#[derive(Eq, PartialEq, Clone, Debug)]
+pub enum PseudoElement {
+    A,
+    B,
+}
+
+#[derive(Eq, PartialEq, Clone, Debug)]
+pub struct PseudoElementSelector(PseudoElement, u64);
+
+#[derive(Eq, PartialEq, Clone, Debug, Default)]
+pub struct Atom(usize);
+
+#[derive(Eq, PartialEq, Clone)]
+pub struct Impl;
--- a/servo/components/selectors/lib.rs
+++ b/servo/components/selectors/lib.rs
@@ -2,20 +2,23 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #[macro_use] extern crate bitflags;
 #[macro_use] extern crate cssparser;
 #[macro_use] extern crate matches;
 extern crate fnv;
 extern crate precomputed_hash;
+#[cfg(test)] #[macro_use] extern crate size_of_test;
 extern crate smallvec;
 
 pub mod arcslice;
 pub mod bloom;
 pub mod matching;
 pub mod parser;
+#[cfg(test)] mod size_of_tests;
+#[cfg(any(test, feature = "gecko_like_types"))] pub mod gecko_like_types;
 mod tree;
 pub mod visitor;
 
 pub use parser::{SelectorImpl, Parser, SelectorList};
 pub use tree::Element;
 pub use tree::{MatchAttr, MatchAttrGeneric};
--- a/servo/components/selectors/parser.rs
+++ b/servo/components/selectors/parser.rs
@@ -5,17 +5,16 @@
 use arcslice::ArcSlice;
 use cssparser::{Token, Parser as CssParser, parse_nth, ToCss, serialize_identifier, CssStringWriter};
 use precomputed_hash::PrecomputedHash;
 use smallvec::SmallVec;
 use std::ascii::AsciiExt;
 use std::borrow::{Borrow, Cow};
 use std::cmp;
 use std::fmt::{self, Display, Debug, Write};
-use std::hash::Hash;
 use std::iter::Rev;
 use std::ops::Add;
 use std::slice;
 use tree::SELECTOR_WHITESPACE;
 use visitor::SelectorVisitor;
 
 macro_rules! with_all_bounds {
     (
@@ -49,17 +48,17 @@ macro_rules! with_all_bounds {
         pub trait SelectorImpl: Clone + Sized {
             type AttrValue: $($InSelector)*;
             type Identifier: $($InSelector)* + PrecomputedHash;
             type ClassName: $($InSelector)* + PrecomputedHash;
             type LocalName: $($InSelector)* + Borrow<Self::BorrowedLocalName> + PrecomputedHash;
             type NamespaceUrl: $($CommonBounds)* + Default + Borrow<Self::BorrowedNamespaceUrl> + PrecomputedHash;
             type NamespacePrefix: $($InSelector)* + Default;
             type BorrowedNamespaceUrl: ?Sized + Eq;
-            type BorrowedLocalName: ?Sized + Eq + Hash;
+            type BorrowedLocalName: ?Sized + Eq;
 
             /// non tree-structural pseudo-classes
             /// (see: https://drafts.csswg.org/selectors/#structural-pseudos)
             type NonTSPseudoClass: $($CommonBounds)* + Sized + ToCss + SelectorMethods<Impl = Self>;
 
             /// pseudo-elements
             type PseudoElementSelector: $($CommonBounds)* + Sized + ToCss;
         }
@@ -72,17 +71,17 @@ macro_rules! with_bounds {
             [$($CommonBounds)* + $($FromStr)* + Display]
             [$($CommonBounds)*]
             [$($FromStr)*]
         }
     }
 }
 
 with_bounds! {
-    [Clone + Eq + Hash]
+    [Clone + Eq]
     [From<String> + for<'a> From<&'a str>]
 }
 
 pub trait Parser {
     type Impl: SelectorImpl;
 
     /// This function can return an "Err" pseudo-element in order to support CSS2.1
     /// pseudo-elements.
@@ -108,17 +107,17 @@ pub trait Parser {
     }
 
     fn namespace_for_prefix(&self, _prefix: &<Self::Impl as SelectorImpl>::NamespacePrefix)
                             -> Option<<Self::Impl as SelectorImpl>::NamespaceUrl> {
         None
     }
 }
 
-#[derive(PartialEq, Eq, Hash, Clone, Debug)]
+#[derive(PartialEq, Eq, Clone, Debug)]
 pub struct SelectorList<Impl: SelectorImpl>(pub Vec<Selector<Impl>>);
 
 impl<Impl: SelectorImpl> SelectorList<Impl> {
     /// Parse a comma-separated list of Selectors.
     /// https://drafts.csswg.org/selectors/#grouping
     ///
     /// Return the Selectors or Err if there is an invalid selector.
     pub fn parse<P>(parser: &P, input: &mut CssParser) -> Result<Self, ()>
@@ -131,17 +130,17 @@ impl<Impl: SelectorImpl> SelectorList<Im
 /// Copied from Gecko, where it was noted to be unmeasured.
 const NUM_ANCESTOR_HASHES: usize = 4;
 
 /// The cores parts of a selector used for matching. This exists to make that
 /// information accessibly separately from the specificity and pseudo-element
 /// information that lives on |Selector| proper. We may want to refactor things
 /// and move that information elsewhere, at which point we could rename this
 /// to |Selector|.
-#[derive(PartialEq, Eq, Hash, Clone)]
+#[derive(PartialEq, Eq, Clone)]
 pub struct SelectorInner<Impl: SelectorImpl> {
     /// The selector data.
     pub complex: ComplexSelector<Impl>,
     /// Ancestor hashes for the bloom filter. We precompute these and store
     /// them inline to optimize cache performance during selector matching.
     /// This matters a lot.
     pub ancestor_hashes: [u32; NUM_ANCESTOR_HASHES],
 }
@@ -171,17 +170,17 @@ impl<Impl: SelectorImpl> SelectorInner<I
 
     /// Creates a SelectorInner from a Vec of Components. Used in tests.
     pub fn from_vec(vec: Vec<Component<Impl>>) -> Self {
         let complex = ComplexSelector::from_vec(vec);
         Self::new(complex)
     }
 }
 
-#[derive(PartialEq, Eq, Hash, Clone)]
+#[derive(PartialEq, Eq, Clone)]
 pub struct Selector<Impl: SelectorImpl> {
     pub inner: SelectorInner<Impl>,
     pub pseudo_element: Option<Impl::PseudoElementSelector>,
     pub specificity: u32,
 }
 
 pub trait SelectorMethods {
     type Impl: SelectorImpl;
@@ -274,17 +273,17 @@ impl<Impl: SelectorImpl> SelectorMethods
 /// A ComplexSelectors stores a sequence of simple selectors and combinators. The
 /// iterator classes allow callers to iterate at either the raw sequence level or
 /// at the level of sequences of simple selectors separated by combinators. Most
 /// callers want the higher-level iterator.
 ///
 /// We store selectors internally left-to-right (in parsing order), but the
 /// canonical iteration order is right-to-left (selector matching order). The
 /// iterators abstract over these details.
-#[derive(Clone, Eq, Hash, PartialEq)]
+#[derive(Clone, Eq, PartialEq)]
 pub struct ComplexSelector<Impl: SelectorImpl>(ArcSlice<Component<Impl>>);
 
 impl<Impl: SelectorImpl> ComplexSelector<Impl> {
     /// Returns an iterator over the next sequence of simple selectors. When
     /// a combinator is reached, the iterator will return None, and
     /// next_sequence() may be called to continue to the next sequence.
     pub fn iter(&self) -> SelectorIter<Impl> {
         SelectorIter {
@@ -400,17 +399,17 @@ impl<'a, Impl: SelectorImpl> Iterator fo
                 self.skip_until_ancestor();
             }
         }
 
         self.0.next()
     }
 }
 
-#[derive(Eq, PartialEq, Clone, Copy, Debug, Hash)]
+#[derive(Eq, PartialEq, Clone, Copy, Debug)]
 pub enum Combinator {
     Child,  //  >
     Descendant,  // space
     NextSibling,  // +
     LaterSibling,  // ~
 }
 
 impl Combinator {
@@ -424,17 +423,17 @@ impl Combinator {
         matches!(*self, Combinator::NextSibling | Combinator::LaterSibling)
     }
 }
 
 /// A CSS simple selector or combinator. We store both in the same enum for
 /// optimal packing and cache performance, see [1].
 ///
 /// [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1357973
-#[derive(Eq, PartialEq, Clone, Hash)]
+#[derive(Eq, PartialEq, Clone)]
 pub enum Component<Impl: SelectorImpl> {
     Combinator(Combinator),
     ID(Impl::Identifier),
     Class(Impl::ClassName),
     LocalName(LocalName<Impl>),
     Namespace(Namespace<Impl>),
 
     // Attribute selectors
@@ -512,44 +511,43 @@ impl<Impl: SelectorImpl> Component<Impl>
     pub fn as_combinator(&self) -> Option<Combinator> {
         match *self {
             Component::Combinator(c) => Some(c),
             _ => None,
         }
     }
 }
 
-#[derive(Eq, PartialEq, Clone, Hash, Copy, Debug)]
+#[derive(Eq, PartialEq, Clone, Copy, Debug)]
 pub enum CaseSensitivity {
     CaseSensitive,  // Selectors spec says language-defined, but HTML says sensitive.
     CaseInsensitive,
 }
 
 
-#[derive(Eq, PartialEq, Clone, Hash)]
+#[derive(Eq, PartialEq, Clone)]
 pub struct LocalName<Impl: SelectorImpl> {
     pub name: Impl::LocalName,
     pub lower_name: Impl::LocalName,
 }
 
-#[derive(Eq, PartialEq, Clone, Hash)]
+#[derive(Eq, PartialEq, Clone)]
 pub struct AttrSelector<Impl: SelectorImpl> {
     pub name: Impl::LocalName,
     pub lower_name: Impl::LocalName,
     pub namespace: NamespaceConstraint<Impl>,
 }
 
-#[derive(Eq, PartialEq, Clone, Hash, Debug)]
+#[derive(Eq, PartialEq, Clone, Debug)]
 pub enum NamespaceConstraint<Impl: SelectorImpl> {
     Any,
     Specific(Namespace<Impl>),
 }
 
-/// FIXME(SimonSapin): should Hash only hash the URL? What is it used for?
-#[derive(Eq, PartialEq, Clone, Hash)]
+#[derive(Eq, PartialEq, Clone)]
 pub struct Namespace<Impl: SelectorImpl> {
     pub prefix: Option<Impl::NamespacePrefix>,
     pub url: Impl::NamespaceUrl,
 }
 
 impl<Impl: SelectorImpl> Default for Namespace<Impl> {
     fn default() -> Self {
         Namespace {
@@ -1390,23 +1388,23 @@ fn parse_simple_pseudo_class<P, Impl>(pa
 #[cfg(test)]
 pub mod tests {
     use cssparser::{Parser as CssParser, ToCss, serialize_identifier};
     use std::borrow::Cow;
     use std::collections::HashMap;
     use std::fmt;
     use super::*;
 
-    #[derive(PartialEq, Clone, Debug, Hash, Eq)]
+    #[derive(PartialEq, Clone, Debug, Eq)]
     pub enum PseudoClass {
         Hover,
         Lang(String),
     }
 
-    #[derive(Eq, PartialEq, Clone, Debug, Hash)]
+    #[derive(Eq, PartialEq, Clone, Debug)]
     pub enum PseudoElement {
         Before,
         After,
     }
 
     impl ToCss for PseudoClass {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             match *self {
@@ -1453,17 +1451,17 @@ pub mod tests {
         type NamespaceUrl = DummyAtom;
         type NamespacePrefix = DummyAtom;
         type BorrowedLocalName = DummyAtom;
         type BorrowedNamespaceUrl = DummyAtom;
         type NonTSPseudoClass = PseudoClass;
         type PseudoElementSelector = PseudoElement;
     }
 
-    #[derive(Default, Debug, Hash, Clone, PartialEq, Eq)]
+    #[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
     pub struct DummyAtom(String);
 
     impl fmt::Display for DummyAtom {
         fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
             <String as fmt::Display>::fmt(&self.0, fmt)
         }
     }
 
@@ -1500,17 +1498,17 @@ pub mod tests {
                                                 parser: &mut CssParser)
                                                 -> Result<PseudoClass, ()> {
             match_ignore_ascii_case! { &name,
                 "lang" => Ok(PseudoClass::Lang(try!(parser.expect_ident_or_string()).into_owned())),
                 _ => Err(())
             }
         }
 
-        fn parse_pseudo_element(&self, name: Cow<str>, input: &mut CssParser)
+        fn parse_pseudo_element(&self, name: Cow<str>, _input: &mut CssParser)
                                 -> Result<PseudoElement, ()> {
             match_ignore_ascii_case! { &name,
                 "before" => Ok(PseudoElement::Before),
                 "after" => Ok(PseudoElement::After),
                 _ => Err(())
             }
         }
 
new file mode 100644
--- /dev/null
+++ b/servo/components/selectors/size_of_tests.rs
@@ -0,0 +1,66 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use cssparser::ToCss;
+use gecko_like_types::*;
+use parser::*;
+use precomputed_hash::PrecomputedHash;
+use std::fmt;
+use visitor::SelectorVisitor;
+
+size_of_test!(size_of_selector, Selector<Impl>, 72);
+size_of_test!(size_of_pseudo_element, PseudoElementSelector, 16);
+size_of_test!(size_of_selector_inner, SelectorInner<Impl>, 40);
+size_of_test!(size_of_complex_selector, ComplexSelector<Impl>, 24);
+
+size_of_test!(size_of_component, Component<Impl>, 64);
+size_of_test!(size_of_attr_selector, AttrSelector<Impl>, 48);
+size_of_test!(size_of_pseudo_class, PseudoClass, 24);
+
+
+// Boilerplate
+
+impl SelectorImpl for Impl {
+    type AttrValue = Atom;
+    type Identifier = Atom;
+    type ClassName = Atom;
+    type LocalName = Atom;
+    type NamespaceUrl = Atom;
+    type NamespacePrefix = Atom;
+    type BorrowedLocalName = Atom;
+    type BorrowedNamespaceUrl = Atom;
+    type NonTSPseudoClass = PseudoClass;
+    type PseudoElementSelector = PseudoElementSelector;
+}
+
+impl SelectorMethods for PseudoClass {
+    type Impl = Impl;
+
+    fn visit<V>(&self, _visitor: &mut V) -> bool
+        where V: SelectorVisitor<Impl = Self::Impl> { unimplemented!() }
+}
+
+impl ToCss for PseudoClass {
+    fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write { unimplemented!() }
+}
+
+impl ToCss for PseudoElementSelector {
+    fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write { unimplemented!() }
+}
+
+impl fmt::Display for Atom {
+    fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result { unimplemented!() }
+}
+
+impl From<String> for Atom {
+    fn from(_: String) -> Self { unimplemented!() }
+}
+
+impl<'a> From<&'a str> for Atom {
+    fn from(_: &'a str) -> Self { unimplemented!() }
+}
+
+impl PrecomputedHash for Atom {
+    fn precomputed_hash(&self) -> u32 { unimplemented!() }
+}
copy from servo/components/script_plugins/Cargo.toml
copy to servo/components/size_of_test/Cargo.toml
--- a/servo/components/script_plugins/Cargo.toml
+++ b/servo/components/size_of_test/Cargo.toml
@@ -1,10 +1,9 @@
 [package]
-name = "script_plugins"
+name = "size_of_test"
 version = "0.0.1"
 authors = ["The Servo Project Developers"]
 license = "MPL-2.0"
 publish = false
 
 [lib]
 path = "lib.rs"
-plugin = true
new file mode 100644
--- /dev/null
+++ b/servo/components/size_of_test/lib.rs
@@ -0,0 +1,28 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#[macro_export]
+macro_rules! size_of_test {
+    ($testname: ident, $t: ty, $expected_size: expr) => {
+        #[test]
+        fn $testname() {
+            let new = ::std::mem::size_of::<$t>();
+            let old = $expected_size;
+            if new < old {
+                panic!(
+                    "Your changes have decreased the stack size of {} from {} to {}. \
+                     Good work! Please update the expected size in {}.",
+                    stringify!($t), old, new, file!()
+                )
+            } else if new > old {
+                panic!(
+                    "Your changes have increased the stack size of {} from {} to {}. \
+                     Please consider choosing a design which avoids this increase. \
+                     If you feel that the increase is necessary, update the size in {}.",
+                    stringify!($t), old, new, file!()
+                )
+            }
+        }
+    }
+}
--- a/servo/components/style/gecko/selector_parser.rs
+++ b/servo/components/style/gecko/selector_parser.rs
@@ -24,17 +24,17 @@ bitflags! {
         const PSEUDO_CLASS_INTERNAL = 0x01,
     }
 }
 
 macro_rules! pseudo_class_name {
     (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
      string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
         #[doc = "Our representation of a non tree-structural pseudo-class."]
-        #[derive(Clone, Debug, PartialEq, Eq, Hash)]
+        #[derive(Clone, Debug, PartialEq, Eq)]
         pub enum NonTSPseudoClass {
             $(
                 #[doc = $css]
                 $name,
             )*
             $(
                 #[doc = $s_css]
                 $s_name(Box<[u16]>),
@@ -180,17 +180,17 @@ impl NonTSPseudoClass {
                 }
             }
         }
         apply_non_ts_list!(pseudo_class_geckotype)
     }
 }
 
 /// The dummy struct we use to implement our selector parsing.
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub struct SelectorImpl;
 
 /// Some subset of pseudo-elements in Gecko are sensitive to some state
 /// selectors.
 ///
 /// We store the sensitive states in this struct in order to properly handle
 /// these.
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
--- a/servo/tests/unit/layout/Cargo.toml
+++ b/servo/tests/unit/layout/Cargo.toml
@@ -8,8 +8,9 @@ license = "MPL-2.0"
 name = "layout_tests"
 path = "lib.rs"
 doctest = false
 
 [dependencies]
 atomic_refcell = "0.1"
 layout = {path = "../../../components/layout"}
 script_layout_interface = {path = "../../../components/script_layout_interface"}
+size_of_test = {path = "../../../components/size_of_test"}
--- a/servo/tests/unit/layout/lib.rs
+++ b/servo/tests/unit/layout/lib.rs
@@ -1,10 +1,11 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 extern crate atomic_refcell;
 extern crate layout;
 extern crate script_layout_interface;
+#[macro_use] extern crate size_of_test;
 
 #[cfg(test)] mod align_of;
 #[cfg(all(test, target_pointer_width = "64"))] mod size_of;
--- a/servo/tests/unit/layout/size_of.rs
+++ b/servo/tests/unit/layout/size_of.rs
@@ -1,37 +1,9 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use layout::Fragment;
 use layout::SpecificFragmentInfo;
-use std::mem::size_of;
 
-fn check_size_for(name: &'static str, expected: usize, actual: usize) {
-    if actual < expected {
-        panic!("Your changes have decreased the stack size of {} \
-                from {} to {}. Good work! Please update the size in tests/unit/layout/size_of.rs",
-                name, expected, actual);
-    }
-
-    if actual > expected {
-        panic!("Your changes have increased the stack size of {} \
-                from {} to {}.  Please consider choosing a design which avoids this increase. \
-                If you feel that the increase is necessary, update the size in \
-                tests/unit/layout/size_of.rs.",
-                name, expected, actual);
-    }
-}
-
-#[test]
-fn test_size_of_fragment() {
-    let expected = 160;
-    let actual = size_of::<Fragment>();
-    check_size_for("layout::fragment::Fragment", expected, actual);
-}
-
-#[test]
-fn test_size_of_specific_fragment_info() {
-    let expected = 24;
-    let actual = size_of::<SpecificFragmentInfo>();
-    check_size_for("layout::fragment::SpecificFragmentInfo", expected, actual);
-}
+size_of_test!(test_size_of_fragment, Fragment, 160);
+size_of_test!(test_size_of_specific_fragment_info, SpecificFragmentInfo, 24);
--- a/servo/tests/unit/stylo/Cargo.toml
+++ b/servo/tests/unit/stylo/Cargo.toml
@@ -17,12 +17,12 @@ testing = ["style/testing"]
 [dependencies]
 atomic_refcell = "0.1"
 cssparser = "0.13.3"
 env_logger = "0.4"
 euclid = "0.11"
 libc = "0.2"
 log = {version = "0.3.5", features = ["release_max_level_info"]}
 parking_lot = "0.3"
-selectors = {path = "../../../components/selectors"}
+selectors = {path = "../../../components/selectors", features = ["gecko_like_types"]}
 style_traits = {path = "../../../components/style_traits"}
 geckoservo = {path = "../../../ports/geckolib"}
 style = {path = "../../../components/style", features = ["gecko"]}
--- a/servo/tests/unit/stylo/size_of.rs
+++ b/servo/tests/unit/stylo/size_of.rs
@@ -1,12 +1,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+use selectors::gecko_like_types as dummies;
+use std::mem::{size_of, align_of};
+use style;
+use style::gecko::selector_parser as real;
+
+#[test]
+fn size_of_selectors_dummy_types() {
+    assert_eq!(size_of::<dummies::PseudoClass>(), size_of::<real::NonTSPseudoClass>());
+    assert_eq!(align_of::<dummies::PseudoClass>(), align_of::<real::NonTSPseudoClass>());
+
+    assert_eq!(size_of::<dummies::PseudoElementSelector>(), size_of::<real::PseudoElementSelector>());
+    assert_eq!(align_of::<dummies::PseudoElementSelector>(), align_of::<real::PseudoElementSelector>());
+
+    assert_eq!(size_of::<dummies::Atom>(), size_of::<style::Atom>());
+    assert_eq!(align_of::<dummies::Atom>(), align_of::<style::Atom>());
+}
+
 #[test]
 fn size_of_property_declaration() {
     ::style::properties::test_size_of_property_declaration();
 }
 
 #[test]
 fn size_of_specified_values() {
     ::style::properties::test_size_of_specified_values();