Title: [119654] trunk/Tools
Revision
119654
Author
[email protected]
Date
2012-06-06 17:49:05 -0700 (Wed, 06 Jun 2012)

Log Message

webkitpy: add support for an ordered dict of test expectations
https://bugs.webkit.org/show_bug.cgi?id=87802

Reviewed by Ojan Vafai.

As per https://bugs.webkit.org/show_bug.cgi?id=65834 we want to
support a cascaded list of test expectations files. An easy way
to think of this is as an ordered dictionary of name -> contents
for files, where the name is usually a path on disk (I say
usually because we want to support other kinds of expectations
like the compile-time skips for unsupported features in
webkit.py, and we want to continue to support "in-memory" test
expectations that don't require a filesystem).

Conveniently there is an OrderedDict implementation in Python
2.7+ and it is available as a backport, so the first step in
adding this support is to use that implementation. Subsequent
patches will update the test_expectations.py module (and other
callers) to access the dict directly.

This patch just changes the base internal implementation and
provides wrappers for compatibility. The derived ports
(WebKitPort, TestPort, etc.) still need to be updated.

* Scripts/webkitpy/layout_tests/port/base.py:
(Port._expectations_dict):
(Port.test_expectations):
(Port):
(Port._expectations_overrides_dict):
(Port.test_expectations_overrides):
* Scripts/webkitpy/layout_tests/port/port_testcase.py:
(PortTestCase.test_expectations_ordering):
(PortTestCase):
(PortTestCase.test_expectations_overrides_ordering):
* Scripts/webkitpy/thirdparty/ordered_dict.py: Added.
(OrderedDict):
(OrderedDict.__init__):
(OrderedDict.clear):
(OrderedDict.__setitem__):
(OrderedDict.__delitem__):
(OrderedDict.__iter__):
(OrderedDict.__reversed__):
(OrderedDict.popitem):
(OrderedDict.__reduce__):
(OrderedDict.__repr__):
(OrderedDict.copy):
(OrderedDict.fromkeys):

Modified Paths

Added Paths

Diff

Modified: trunk/Tools/ChangeLog (119653 => 119654)


--- trunk/Tools/ChangeLog	2012-06-07 00:39:22 UTC (rev 119653)
+++ trunk/Tools/ChangeLog	2012-06-07 00:49:05 UTC (rev 119654)
@@ -1,3 +1,53 @@
+2012-05-29  Dirk Pranke  <[email protected]>
+
+        webkitpy: add support for an ordered dict of test expectations
+        https://bugs.webkit.org/show_bug.cgi?id=87802
+
+        Reviewed by Ojan Vafai.
+
+        As per https://bugs.webkit.org/show_bug.cgi?id=65834 we want to
+        support a cascaded list of test expectations files. An easy way
+        to think of this is as an ordered dictionary of name -> contents
+        for files, where the name is usually a path on disk (I say
+        usually because we want to support other kinds of expectations
+        like the compile-time skips for unsupported features in
+        webkit.py, and we want to continue to support "in-memory" test
+        expectations that don't require a filesystem).
+
+        Conveniently there is an OrderedDict implementation in Python
+        2.7+ and it is available as a backport, so the first step in
+        adding this support is to use that implementation. Subsequent
+        patches will update the test_expectations.py module (and other
+        callers) to access the dict directly.
+
+        This patch just changes the base internal implementation and
+        provides wrappers for compatibility. The derived ports
+        (WebKitPort, TestPort, etc.) still need to be updated.
+
+        * Scripts/webkitpy/layout_tests/port/base.py:
+        (Port._expectations_dict):
+        (Port.test_expectations):
+        (Port):
+        (Port._expectations_overrides_dict):
+        (Port.test_expectations_overrides):
+        * Scripts/webkitpy/layout_tests/port/port_testcase.py:
+        (PortTestCase.test_expectations_ordering):
+        (PortTestCase):
+        (PortTestCase.test_expectations_overrides_ordering):
+        * Scripts/webkitpy/thirdparty/ordered_dict.py: Added.
+        (OrderedDict):
+        (OrderedDict.__init__):
+        (OrderedDict.clear):
+        (OrderedDict.__setitem__):
+        (OrderedDict.__delitem__):
+        (OrderedDict.__iter__):
+        (OrderedDict.__reversed__):
+        (OrderedDict.popitem):
+        (OrderedDict.__reduce__):
+        (OrderedDict.__repr__):
+        (OrderedDict.copy):
+        (OrderedDict.fromkeys):
+
 2012-06-06  Mario Sanchez Prada  <[email protected]>
 
         Unreviewed gardening. Unit test fails on GTK 64bit debug bot.

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/base.py (119653 => 119654)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/base.py	2012-06-07 00:39:22 UTC (rev 119653)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/base.py	2012-06-07 00:49:05 UTC (rev 119654)
@@ -36,6 +36,13 @@
 import os
 import re
 
+try:
+    from collections import OrderedDict
+except ImportError:
+    # Needed for Python < 2.7
+    from webkitpy.thirdparty.ordered_dict import OrderedDict
+
+
 from webkitpy.common import find_files
 from webkitpy.common import read_checksum_from_png
 from webkitpy.common.memoized import memoized
@@ -887,13 +894,40 @@
         # some ports have Skipped files which are returned as part of test_expectations().
         return self._filesystem.exists(self.path_to_test_expectations_file())
 
+    def _expectations_dict(self):
+        """Returns an OrderedDict of name -> expectations strings. The names
+        are expected to be (but not required to be) paths in the filesystem.
+        If the name is a path, the file can be considered updatable for things
+        like rebaselining, so don't use names that are paths if they're not paths.
+        Generally speaking the ordering should be files in the filesystem in
+        cascade order (test_expectations.txt followed by Skipped, if the port
+        honors both formats), then any built-in expectations (e.g., from compile-time
+        exclusions), then --additional-expectations options."""
+        # FIXME: rename this to test_expectations() once all the callers are updated to know about the ordered dict.
+        overrides = OrderedDict()
+        path = self.path_to_test_expectations_file()
+        overrides[path] = self._filesystem.read_text_file(path)
+        return overrides
+
     def test_expectations(self):
         """Returns the test expectations for this port.
 
         Basically this string should contain the equivalent of a
         test_expectations file. See test_expectations.py for more details."""
-        return self._filesystem.read_text_file(self.path_to_test_expectations_file())
+        return ''.join(self._expectations_dict().values())
 
+    def _expectations_overrides_dict(self):
+        # FIXME: merge this into test_expectations() when _expectations_dict() is renamed.
+        overrides = OrderedDict()
+        for path in self.get_option('additional_expectations', []):
+            expanded_path = self._filesystem.expanduser(path)
+            if self._filesystem.exists(expanded_path):
+                _log.debug("reading additional_expectations from path '%s'" % path)
+                overrides[path] = self._filesystem.read_text_file(path)
+            else:
+                _log.warning("additional_expectations path '%s' does not exist" % path)
+        return overrides
+
     def test_expectations_overrides(self):
         """Returns an optional set of overrides for the test_expectations.
 
@@ -901,14 +935,10 @@
         it is possible that you might need "downstream" expectations that
         temporarily override the "upstream" expectations until the port can
         sync up the two repos."""
-        overrides = ''
-        for path in self.get_option('additional_expectations', []):
-            if self._filesystem.exists(self._filesystem.expanduser(path)):
-                _log.debug("reading additional_expectations from path '%s'" % path)
-                overrides += self._filesystem.read_text_file(self._filesystem.expanduser(path))
-            else:
-                _log.warning("additional_expectations path '%s' does not exist" % path)
-        return overrides or None
+        overrides = self._expectations_overrides_dict()
+        if overrides:
+            return ''.join(overrides.values())
+        return None
 
     def repository_paths(self):
         """Returns a list of (repository_name, repository_path) tuples of its depending code base.

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py (119653 => 119654)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py	2012-06-07 00:39:22 UTC (rev 119653)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py	2012-06-07 00:49:05 UTC (rev 119654)
@@ -339,7 +339,11 @@
         self.assertEquals(port.test_expectations_overrides(),
                           SKIA_OVERRIDES + ADDITIONAL_EXPECTATIONS)
 
+    def test_expectations_ordering(self):
+        # since we don't implement self.port_name in ChromiumPort.
+        pass
 
+
 class ChromiumPortLoggingTest(logtesting.LoggingTestCase):
     def test_check_sys_deps(self):
         port = ChromiumPortTest.TestLinuxPort()

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py (119653 => 119654)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py	2012-06-07 00:39:22 UTC (rev 119653)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py	2012-06-07 00:49:05 UTC (rev 119654)
@@ -339,7 +339,24 @@
             port.host.filesystem.maybe_make_directory(directory)
         self.assertEquals(port._build_path(), expected_path)
 
+    def test_expectations_ordering(self):
+        port = self.make_port()
+        path = port.path_to_test_expectations_file()
+        if not port._filesystem.exists(path):
+            port._filesystem.write_text_file(path, '')
+        ordered_dict = port._expectations_dict()
+        self.assertEquals(path, ordered_dict.keys()[0])
 
+    def test_expectations_overrides_ordering(self):
+        options = MockOptions(additional_expectations=['/tmp/foo', '/tmp/bar'])
+        port = self.make_port(options=options)
+        port._filesystem.write_text_file('/tmp/foo', 'foo')
+        port._filesystem.write_text_file('/tmp/bar', 'bar')
+        ordered_dict = port._expectations_overrides_dict()
+        self.assertEquals(ordered_dict.keys(), options.additional_expectations)
+        self.assertEquals(ordered_dict.values(), ['foo', 'bar'])
+
+
 # FIXME: This class and main() should be merged into test-webkitpy.
 class EnhancedTestLoader(unittest.TestLoader):
     integration_tests = False

Added: trunk/Tools/Scripts/webkitpy/thirdparty/ordered_dict.py (0 => 119654)


--- trunk/Tools/Scripts/webkitpy/thirdparty/ordered_dict.py	                        (rev 0)
+++ trunk/Tools/Scripts/webkitpy/thirdparty/ordered_dict.py	2012-06-07 00:49:05 UTC (rev 119654)
@@ -0,0 +1,89 @@
+# Copyright (c) 2009 Raymond Hettinger.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+# This code is obtained from http://code.activestate.com/recipes/576669/
+
+from collections import MutableMapping
+
+class OrderedDict(dict, MutableMapping):
+
+    # Methods with direct access to underlying attributes
+
+    def __init__(self, *args, **kwds):
+        if len(args) > 1:
+            raise TypeError('expected at 1 argument, got %d', len(args))
+        if not hasattr(self, '_keys'):
+            self._keys = []
+        self.update(*args, **kwds)
+
+    def clear(self):
+        del self._keys[:]
+        dict.clear(self)
+
+    def __setitem__(self, key, value):
+        if key not in self:
+            self._keys.append(key)
+        dict.__setitem__(self, key, value)
+
+    def __delitem__(self, key):
+        dict.__delitem__(self, key)
+        self._keys.remove(key)
+
+    def __iter__(self):
+        return iter(self._keys)
+
+    def __reversed__(self):
+        return reversed(self._keys)
+
+    def popitem(self):
+        if not self:
+            raise KeyError
+        key = self._keys.pop()
+        value = dict.pop(self, key)
+        return key, value
+
+    def __reduce__(self):
+        items = [[k, self[k]] for k in self]
+        inst_dict = vars(self).copy()
+        inst_dict.pop('_keys', None)
+        return (self.__class__, (items,), inst_dict)
+
+    # Methods with indirect access via the above methods
+
+    setdefault = MutableMapping.setdefault
+    update = MutableMapping.update
+    pop = MutableMapping.pop
+    keys = MutableMapping.keys
+    values = MutableMapping.values
+    items = MutableMapping.items
+
+    def __repr__(self):
+        pairs = ', '.join(map('%r: %r'.__mod__, self.items()))
+        return '%s({%s})' % (self.__class__.__name__, pairs)
+
+    def copy(self):
+        return self.__class__(self)
+
+    @classmethod
+    def fromkeys(cls, iterable, value=None):
+        d = cls()
+        for key in iterable:
+            d[key] = value
+        return d
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to