diff options
author | Iain Merrick <husky@google.com> | 2010-09-13 16:35:48 +0100 |
---|---|---|
committer | Iain Merrick <husky@google.com> | 2010-09-16 12:10:42 +0100 |
commit | 5abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306 (patch) | |
tree | ddce1aa5e3b6967a69691892e500897558ff8ab6 /WebKitTools/Scripts/webkitpy | |
parent | 12bec63ec71e46baba27f0bd9bd9d8067683690a (diff) | |
download | external_webkit-5abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306.zip external_webkit-5abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306.tar.gz external_webkit-5abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306.tar.bz2 |
Merge WebKit at r67178 : Initial merge by git.
Change-Id: I57e01163b6866cb029cdadf405a0394a3918bc18
Diffstat (limited to 'WebKitTools/Scripts/webkitpy')
36 files changed, 918 insertions, 211 deletions
diff --git a/WebKitTools/Scripts/webkitpy/common/config/committers.py b/WebKitTools/Scripts/webkitpy/common/config/committers.py index 19ebc5f..6a45cab 100644 --- a/WebKitTools/Scripts/webkitpy/common/config/committers.py +++ b/WebKitTools/Scripts/webkitpy/common/config/committers.py @@ -74,7 +74,6 @@ committers_unable_to_review = [ Committer("Andrei Popescu", "andreip@google.com", "andreip"), Committer("Andrew Wellington", ["andrew@webkit.org", "proton@wiretapped.net"], "proton"), Committer("Andras Becsi", "abecsi@webkit.org", "bbandix"), - Committer("Andreas Kling", "andreas.kling@nokia.com", "kling"), Committer("Andy Estes", "aestes@apple.com", "estes"), Committer("Anthony Ricaud", "rik@webkit.org", "rik"), Committer("Anton Muhin", "antonm@chromium.org", "antonm"), @@ -90,6 +89,7 @@ committers_unable_to_review = [ Committer("Chang Shu", "Chang.Shu@nokia.com"), Committer("Chris Evans", "cevans@google.com"), Committer("Chris Petersen", "cpetersen@apple.com", "cpetersen"), + Committer("Chris Rogers", "crogers@google.com", "crogers"), Committer("Christian Dywan", ["christian@twotoasts.de", "christian@webkit.org"]), Committer("Collin Jackson", "collinj@webkit.org"), Committer("Csaba Osztrogonac", "ossy@webkit.org", "ossy"), @@ -110,12 +110,12 @@ committers_unable_to_review = [ Committer("Girish Ramakrishnan", ["girish@forwardbias.in", "ramakrishnan.girish@gmail.com"]), Committer("Graham Dennis", ["Graham.Dennis@gmail.com", "gdennis@webkit.org"]), Committer("Greg Bolsinga", "bolsinga@apple.com"), + Committer("Hans Wennborg", "hans@chromium.org", "hwennborg"), Committer("Hin-Chung Lam", ["hclam@google.com", "hclam@chromium.org"]), Committer("Ilya Tikhonovsky", "loislo@chromium.org", "loislo"), Committer("Jakob Petsovits", ["jpetsovits@rim.com", "jpetso@gmx.at"], "jpetso"), Committer("Jakub Wieczorek", "jwieczorek@webkit.org", "fawek"), Committer("James Hawkins", ["jhawkins@chromium.org", "jhawkins@google.com"], "jhawkins"), - Committer("James Robinson", ["jamesr@chromium.org", "jamesr@google.com"], "jamesr"), Committer("Jay Civelli", "jcivelli@chromium.org", "jcivelli"), Committer("Jens Alfke", ["snej@chromium.org", "jens@apple.com"]), Committer("Jer Noble", "jer.noble@apple.com", "jernoble"), @@ -146,6 +146,7 @@ committers_unable_to_review = [ Committer("Matt Perry", "mpcomplete@chromium.org"), Committer("Maxime Britto", ["maxime.britto@gmail.com", "britto@apple.com"]), Committer("Maxime Simon", ["simon.maxime@gmail.com", "maxime.simon@webkit.org"], "maxime.simon"), + Committer("Michael Saboff", "msaboff@apple.com"), Committer("Michelangelo De Simone", "michelangelo@webkit.org", "michelangelo"), Committer("Mike Belshe", ["mbelshe@chromium.org", "mike@belshe.com"]), Committer("Mike Fenton", ["mifenton@rim.com", "mike.fenton@torchmobile.com"], "mfenton"), @@ -159,9 +160,10 @@ committers_unable_to_review = [ Committer("Philippe Normand", ["pnormand@igalia.com", "philn@webkit.org"], "philn-tp"), Committer("Pierre d'Herbemont", ["pdherbemont@free.fr", "pdherbemont@apple.com"], "pdherbemont"), Committer("Pierre-Olivier Latour", "pol@apple.com", "pol"), - Committer("Robert Hogan", ["robert@webkit.org", "robert@roberthogan.net"], "mwenge"), + Committer("Robert Hogan", ["robert@webkit.org", "robert@roberthogan.net", "lists@roberthogan.net"], "mwenge"), Committer("Roland Steiner", "rolandsteiner@chromium.org"), Committer("Ryosuke Niwa", "rniwa@webkit.org", "rniwa"), + Committer("Satish Sampath", "satish@chromium.org"), Committer("Scott Violet", "sky@chromium.org", "sky"), Committer("Stephen White", "senorblanco@chromium.org", "senorblanco"), Committer("Tony Gentilcore", "tonyg@chromium.org", "tonyg-cr"), @@ -192,14 +194,15 @@ reviewers_list = [ Reviewer("Ada Chan", "adachan@apple.com", "chanada"), Reviewer("Adam Barth", "abarth@webkit.org", "abarth"), Reviewer("Adam Roben", "aroben@apple.com", "aroben"), - Reviewer("Adam Treat", ["treat@kde.org", "treat@webkit.org"], "manyoso"), + Reviewer("Adam Treat", ["treat@kde.org", "treat@webkit.org", "atreat@rim.com"], "manyoso"), Reviewer("Adele Peterson", "adele@apple.com", "adele"), Reviewer("Alexey Proskuryakov", ["ap@webkit.org", "ap@apple.com"], "ap"), Reviewer("Alice Liu", "alice.liu@apple.com", "aliu"), Reviewer("Alp Toker", ["alp@nuanti.com", "alp@atoker.com", "alp@webkit.org"], "alp"), Reviewer("Anders Carlsson", ["andersca@apple.com", "acarlsson@apple.com"], "andersca"), - Reviewer("Antonio Gomes", "tonikitoo@webkit.org", "tonikitoo"), - Reviewer("Antti Koivisto", ["koivisto@iki.fi", "antti@apple.com"], "anttik"), + Reviewer("Andreas Kling", "andreas.kling@nokia.com", "kling"), + Reviewer("Antonio Gomes", ["tonikitoo@webkit.org", "agomes@rim.com"], "tonikitoo"), + Reviewer("Antti Koivisto", ["koivisto@iki.fi", "antti@apple.com", "antti.j.koivisto@nokia.com"], "anttik"), Reviewer("Ariya Hidayat", ["ariya@sencha.com", "ariya.hidayat@gmail.com", "ariya@webkit.org"], "ariya"), Reviewer("Beth Dakin", "bdakin@apple.com", "dethbakin"), Reviewer("Brady Eidson", "beidson@apple.com", "bradee-oh"), @@ -228,21 +231,22 @@ reviewers_list = [ Reviewer("George Staikos", ["staikos@kde.org", "staikos@webkit.org"]), Reviewer("Gustavo Noronha Silva", ["gns@gnome.org", "kov@webkit.org", "gustavo.noronha@collabora.co.uk"], "kov"), Reviewer("Holger Freyther", ["zecke@selfish.org", "zecke@webkit.org"], "zecke"), + Reviewer("James Robinson", ["jamesr@chromium.org", "jamesr@google.com"], "jamesr"), Reviewer("Jan Alonzo", ["jmalonzo@gmail.com", "jmalonzo@webkit.org"], "janm"), Reviewer("Jeremy Orlow", "jorlow@chromium.org", "jorlow"), Reviewer("Jian Li", "jianli@chromium.org", "jianli"), Reviewer("John Sullivan", "sullivan@apple.com", "sullivan"), Reviewer("Jon Honeycutt", "jhoneycutt@apple.com", "jhoneycutt"), - Reviewer("Joseph Pecoraro", "joepeck@webkit.org", "JoePeck"), + Reviewer("Joseph Pecoraro", ["joepeck@webkit.org", "pecoraro@apple.com"], "JoePeck"), Reviewer("Justin Garcia", "justin.garcia@apple.com", "justing"), Reviewer("Ken Kocienda", "kocienda@apple.com"), - Reviewer("Kenneth Rohde Christiansen", ["kenneth@webkit.org", "kenneth.christiansen@openbossa.org"], "kenne"), + Reviewer("Kenneth Rohde Christiansen", ["kenneth@webkit.org", "kenneth.christiansen@openbossa.org", "kenneth.christiansen@gmail.com"], "kenne"), Reviewer("Kenneth Russell", "kbr@google.com", "kbr_google"), Reviewer("Kent Tamura", "tkent@chromium.org", "tkent"), Reviewer("Kevin Decker", "kdecker@apple.com", "superkevin"), Reviewer("Kevin McCullough", "kmccullough@apple.com", "maculloch"), Reviewer("Kevin Ollivier", ["kevino@theolliviers.com", "kevino@webkit.org"], "kollivier"), - Reviewer("Lars Knoll", ["lars@trolltech.com", "lars@kde.org"], "lars"), + Reviewer("Lars Knoll", ["lars@trolltech.com", "lars@kde.org", "lars.knoll@nokia.com"], "lars"), Reviewer("Laszlo Gombos", "laszlo.1.gombos@nokia.com", "lgombos"), Reviewer("Maciej Stachowiak", "mjs@apple.com", "othermaciej"), Reviewer("Mark Rowe", "mrowe@apple.com", "bdash"), diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/data/failures/expected/hang.html b/WebKitTools/Scripts/webkitpy/layout_tests/data/failures/expected/hang.html new file mode 100644 index 0000000..4e0de08 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/layout_tests/data/failures/expected/hang.html @@ -0,0 +1 @@ +timeout-thread diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/data/http/tests/passes/text-expected.txt b/WebKitTools/Scripts/webkitpy/layout_tests/data/http/tests/passes/text-expected.txt new file mode 100644 index 0000000..2b38a06 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/layout_tests/data/http/tests/passes/text-expected.txt @@ -0,0 +1 @@ +text-txt diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/data/http/tests/passes/text.html b/WebKitTools/Scripts/webkitpy/layout_tests/data/http/tests/passes/text.html new file mode 100644 index 0000000..8e27be7 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/layout_tests/data/http/tests/passes/text.html @@ -0,0 +1 @@ +text diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/data/http/tests/ssl/text-expected.txt b/WebKitTools/Scripts/webkitpy/layout_tests/data/http/tests/ssl/text-expected.txt new file mode 100644 index 0000000..2b38a06 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/layout_tests/data/http/tests/ssl/text-expected.txt @@ -0,0 +1 @@ +text-txt diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/data/http/tests/ssl/text.html b/WebKitTools/Scripts/webkitpy/layout_tests/data/http/tests/ssl/text.html new file mode 100644 index 0000000..8e27be7 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/layout_tests/data/http/tests/ssl/text.html @@ -0,0 +1 @@ +text diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/data/platform/test/test_expectations.txt b/WebKitTools/Scripts/webkitpy/layout_tests/data/platform/test/test_expectations.txt index 16556e3..0619fde 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/data/platform/test/test_expectations.txt +++ b/WebKitTools/Scripts/webkitpy/layout_tests/data/platform/test/test_expectations.txt @@ -8,5 +8,6 @@ WONTFIX : failures/expected/missing_image.html = MISSING PASS WONTFIX : failures/expected/missing_text.html = MISSING PASS WONTFIX : failures/expected/text.html = TEXT WONTFIX : failures/expected/timeout.html = TIMEOUT +WONTFIX SKIP : failures/expected/hang.html = TIMEOUT WONTFIX SKIP : failures/expected/keyboard.html = CRASH WONTFIX SKIP : failures/expected/exception.html = CRASH diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/data/websocket/tests/passes/text-expected.txt b/WebKitTools/Scripts/webkitpy/layout_tests/data/websocket/tests/passes/text-expected.txt new file mode 100644 index 0000000..2b38a06 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/layout_tests/data/websocket/tests/passes/text-expected.txt @@ -0,0 +1 @@ +text-txt diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/data/websocket/tests/passes/text.html b/WebKitTools/Scripts/webkitpy/layout_tests/data/websocket/tests/passes/text.html new file mode 100644 index 0000000..8e27be7 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/layout_tests/data/websocket/tests/passes/text.html @@ -0,0 +1 @@ +text diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests.py b/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests.py index bb63f5e..c543d91 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests.py @@ -130,6 +130,32 @@ def extract_platforms(paths): return platforms +def has_intermediate_results(test, fallbacks, matching_platform, + path_exists=os.path.exists): + """Returns True if there is a test result that causes us to not delete + this duplicate. + + For example, chromium-linux may be a duplicate of the checked in result, + but chromium-win may have a different result checked in. In this case, + we need to keep the duplicate results. + + Args: + test: The test name. + fallbacks: A list of platforms we fall back on. + matching_platform: The platform that we found the duplicate test + result. We can stop checking here. + path_exists: Optional parameter that allows us to stub out + os.path.exists for testing. + """ + for platform in fallbacks: + if platform == matching_platform: + return False + test_path = os.path.join('LayoutTests', 'platform', platform, test) + if path_exists(test_path): + return True + return False + + def find_dups(hashes, port_fallbacks): """Yields info about redundant test expectations. Args: @@ -151,7 +177,12 @@ def find_dups(hashes, port_fallbacks): for platform in platforms.keys(): for fallback in port_fallbacks[platform]: if fallback in platforms.keys(): - yield test, platform, fallback, platforms[platform] + # We have to verify that there isn't an intermediate result + # that causes this duplicate hash to exist. + if not has_intermediate_results(test, + port_fallbacks[platform], fallback): + path = os.path.join('LayoutTests', platforms[platform]) + yield test, platform, fallback, path def deduplicate(glob_pattern): diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests_unittest.py index 66dda32..be2e381 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests_unittest.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests_unittest.py @@ -89,6 +89,47 @@ class ListDuplicatesTest(unittest.TestCase): deduplicate_tests._BASE_PLATFORM: 'what/'}, deduplicate_tests.extract_platforms(['platform/foo/bar', 'what/'])) + def test_has_intermediate_results(self): + test_cases = ( + # If we found a duplicate in our first fallback, we have no + # intermediate results. + (False, ('fast/foo-expected.txt', + ['chromium-win', 'chromium', 'base'], + 'chromium-win', + lambda path: True)), + # Since chromium-win has a result, we have an intermediate result. + (True, ('fast/foo-expected.txt', + ['chromium-win', 'chromium', 'base'], + 'chromium', + lambda path: True)), + # There are no intermediate results. + (False, ('fast/foo-expected.txt', + ['chromium-win', 'chromium', 'base'], + 'chromium', + lambda path: False)), + # There are no intermediate results since a result for chromium is + # our duplicate file. + (False, ('fast/foo-expected.txt', + ['chromium-win', 'chromium', 'base'], + 'chromium', + lambda path: path == 'LayoutTests/platform/chromium/fast/foo-expected.txt')), + # We have an intermediate result in 'chromium' even though our + # duplicate is with the file in 'base'. + (True, ('fast/foo-expected.txt', + ['chromium-win', 'chromium', 'base'], + 'base', + lambda path: path == 'LayoutTests/platform/chromium/fast/foo-expected.txt')), + # We have an intermediate result in 'chromium-win' even though our + # duplicate is in 'base'. + (True, ('fast/foo-expected.txt', + ['chromium-win', 'chromium', 'base'], + 'base', + lambda path: path == 'LayoutTests/platform/chromium-win/fast/foo-expected.txt')), + ) + for expected, inputs in test_cases: + self.assertEquals(expected, + deduplicate_tests.has_intermediate_results(*inputs)) + def test_unique(self): MockExecutive.response = ( '100644 blob 5053240b3353f6eb39f7cb00259785f16d121df2\tLayoutTests/mac/foo-expected.txt\n' @@ -116,12 +157,12 @@ class ListDuplicatesTest(unittest.TestCase): self.assertEquals(('git', 'ls-tree', '-r', 'HEAD', 'LayoutTests'), MockExecutive.last_run_command[-1]) self.assertEquals(2, len(result)) self.assertEquals({'test': 'animage.png', - 'path': 'platform/chromium-linux/animage.png', + 'path': 'LayoutTests/platform/chromium-linux/animage.png', 'fallback': 'chromium-win', 'platform': 'chromium-linux'}, result[0]) self.assertEquals({'test': 'foo-expected.txt', - 'path': 'platform/chromium-linux/foo-expected.txt', + 'path': 'LayoutTests/platform/chromium-linux/foo-expected.txt', 'fallback': 'chromium-win', 'platform': 'chromium-linux'}, result[1]) @@ -131,7 +172,7 @@ class ListDuplicatesTest(unittest.TestCase): self.assertEquals(('git', 'ls-tree', '-r', 'HEAD', 'LayoutTests'), MockExecutive.last_run_command[-1]) self.assertEquals(1, len(result)) self.assertEquals({'test': 'foo-expected.txt', - 'path': 'platform/chromium-linux/foo-expected.txt', + 'path': 'LayoutTests/platform/chromium-linux/foo-expected.txt', 'fallback': 'chromium-win', 'platform': 'chromium-linux'}, result[0]) @@ -141,7 +182,7 @@ class ListDuplicatesTest(unittest.TestCase): self.assertEquals(('git', 'ls-tree', '-r', 'HEAD', 'LayoutTests'), MockExecutive.last_run_command[-1]) self.assertEquals(1, len(result)) self.assertEquals({'test': 'animage.png', - 'path': 'platform/chromium-linux/animage.png', + 'path': 'LayoutTests/platform/chromium-linux/animage.png', 'fallback': 'chromium-win', 'platform': 'chromium-linux'}, result[0]) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py index ec33086..9b963ca 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py @@ -47,6 +47,7 @@ import sys import thread import threading import time +import traceback import test_failures @@ -54,6 +55,23 @@ _log = logging.getLogger("webkitpy.layout_tests.layout_package." "dump_render_tree_thread") +def find_thread_stack(id): + """Returns a stack object that can be used to dump a stack trace for + the given thread id (or None if the id is not found).""" + for thread_id, stack in sys._current_frames().items(): + if thread_id == id: + return stack + return None + + +def log_stack(stack): + """Log a stack trace to log.error().""" + for filename, lineno, name, line in traceback.extract_stack(stack): + _log.error('File: "%s", line %d, in %s' % (filename, lineno, name)) + if line: + _log.error(' %s' % line.strip()) + + def _process_output(port, test_info, test_types, test_args, configuration, output_dir, crash, timeout, test_run_time, actual_checksum, output, error): @@ -167,6 +185,7 @@ class SingleTestThread(threading.Thread): self._test_args = test_args self._configuration = configuration self._output_dir = output_dir + self._driver = None def run(self): self._covered_run() @@ -175,18 +194,19 @@ class SingleTestThread(threading.Thread): # FIXME: this is a separate routine to work around a bug # in coverage: see http://bitbucket.org/ned/coveragepy/issue/85. test_info = self._test_info - driver = self._port.create_driver(self._image_path, self._shell_args) - driver.start() + self._driver = self._port.create_driver(self._image_path, + self._shell_args) + self._driver.start() start = time.time() crash, timeout, actual_checksum, output, error = \ - driver.run_test(test_info.uri.strip(), test_info.timeout, - test_info.image_hash()) + self._driver.run_test(test_info.uri.strip(), test_info.timeout, + test_info.image_hash()) end = time.time() self._test_result = _process_output(self._port, test_info, self._test_types, self._test_args, self._configuration, self._output_dir, crash, timeout, end - start, actual_checksum, output, error) - driver.stop() + self._driver.stop() def get_test_result(self): return self._test_result @@ -312,9 +332,7 @@ class TestShellThread(WatchableThread): # Save the exception for our caller to see. self._exception_info = sys.exc_info() self._stop_time = time.time() - # Re-raise it and die. - _log.error('%s dying, exception raised: %s' % (self.getName(), - self._exception_info)) + _log.error('%s dying, exception raised' % self.getName()) self._stop_time = time.time() @@ -426,7 +444,7 @@ class TestShellThread(WatchableThread): worker.start() thread_timeout = _milliseconds_to_seconds( - _pad_timeout(test_info.timeout)) + _pad_timeout(int(test_info.timeout))) thread._next_timeout = time.time() + thread_timeout worker.join(thread_timeout) if worker.isAlive(): @@ -439,11 +457,13 @@ class TestShellThread(WatchableThread): # that tradeoff in order to avoid losing the rest of this # thread's results. _log.error('Test thread hung: killing all DumpRenderTrees') - worker._driver.stop() + if worker._driver: + worker._driver.stop() try: result = worker.get_test_result() except AttributeError, e: + # This gets raised if the worker thread has already exited. failures = [] _log.error('Cannot get results of test: %s' % test_info.filename) @@ -476,7 +496,7 @@ class TestShellThread(WatchableThread): start = time.time() thread_timeout = _milliseconds_to_seconds( - _pad_timeout(test_info.timeout)) + _pad_timeout(int(test_info.timeout))) self._next_timeout = start + thread_timeout crash, timeout, actual_checksum, output, error = \ diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread_unittest.py new file mode 100644 index 0000000..63f86d9 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread_unittest.py @@ -0,0 +1,49 @@ +# Copyright (C) 2010 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""""Tests code paths not covered by the regular unit tests.""" + +import sys +import unittest + +import dump_render_tree_thread + + +class Test(unittest.TestCase): + def test_find_thread_stack_found(self): + id, stack = sys._current_frames().items()[0] + found_stack = dump_render_tree_thread.find_thread_stack(id) + self.assertNotEqual(found_stack, None) + + def test_find_thread_stack_not_found(self): + found_stack = dump_render_tree_thread.find_thread_stack(0) + self.assertEqual(found_stack, None) + + +if __name__ == '__main__': + unittest.main() diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py index 15eceee..765b4d8 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py @@ -37,6 +37,8 @@ import time import urllib2 import xml.dom.minidom +from webkitpy.layout_tests.layout_package import test_results_uploader + import webkitpy.thirdparty.simplejson as simplejson # A JSON results generator for generic tests. @@ -85,13 +87,14 @@ class JSONResultsGeneratorBase(object): INCREMENTAL_RESULTS_FILENAME = "incremental_results.json" URL_FOR_TEST_LIST_JSON = \ - "http://%s/testfile?builder=%s&name=%s&testlistjson=1" + "http://%s/testfile?builder=%s&name=%s&testlistjson=1&testtype=%s" def __init__(self, builder_name, build_name, build_number, results_file_base_path, builder_base_url, test_results_map, svn_repositories=None, generate_incremental_results=False, - test_results_server=None): + test_results_server=None, + test_type=""): """Modifies the results.json file. Grabs it off the archive directory if it is not found locally. @@ -129,6 +132,7 @@ class JSONResultsGeneratorBase(object): self._svn_repositories = {} self._test_results_server = test_results_server + self._test_type = test_type self._json = None self._archived_results = None @@ -287,7 +291,8 @@ class JSONResultsGeneratorBase(object): results_file_url = (self.URL_FOR_TEST_LIST_JSON % (urllib2.quote(self._test_results_server), urllib2.quote(self._builder_name), - self.RESULTS_FILENAME)) + self.RESULTS_FILENAME, + urllib2.quote(self._test_type))) else: # Check if we have the archived JSON file on the buildbot # server. @@ -518,9 +523,33 @@ class JSONResultsGenerator(JSONResultsGeneratorBase): # The flag is for backward compatibility. output_json_in_init = True + def _upload_json_files(self): + if not self._test_results_server or not self._test_type: + return + + _log.info("Uploading JSON files for %s to the server: %s", + self._builder_name, self._test_results_server) + attrs = [("builder", self._builder_name), ("testtype", self._test_type)] + json_files = [self.INCREMENTAL_RESULTS_FILENAME] + + files = [(file, os.path.join(self._results_directory, file)) + for file in json_files] + uploader = test_results_uploader.TestResultsUploader( + self._test_results_server) + try: + # Set uploading timeout in case appengine server is having problem. + # 120 seconds are more than enough to upload test results. + uploader.upload(attrs, files, 120) + except Exception, err: + _log.error("Upload failed: %s" % err) + return + + _log.info("JSON files uploaded.") + def __init__(self, port, builder_name, build_name, build_number, results_file_base_path, builder_base_url, - test_timings, failures, passed_tests, skipped_tests, all_tests): + test_timings, failures, passed_tests, skipped_tests, all_tests, + test_results_server=None, test_type=None): """Generates a JSON results file. Args @@ -536,8 +565,13 @@ class JSONResultsGenerator(JSONResultsGeneratorBase): skipped_tests: A set containing all the skipped tests. all_tests: List of all the tests that were run. This should not include skipped tests. + test_results_server: server that hosts test results json. + test_type: the test type. """ + self._test_type = test_type + self._results_directory = results_file_base_path + # Create a map of (name, TestResult). test_results_map = dict() get = test_results_map.get @@ -557,10 +591,16 @@ class JSONResultsGenerator(JSONResultsGeneratorBase): if test not in test_results_map: test_results_map[test] = TestResult(test) + # Generate the JSON with incremental flag enabled. + # (This should also output the full result for now.) super(JSONResultsGenerator, self).__init__( builder_name, build_name, build_number, results_file_base_path, builder_base_url, test_results_map, - svn_repositories=port.test_repository_paths()) + svn_repositories=port.test_repository_paths(), + generate_incremental_results=True, + test_results_server=test_results_server, + test_type=test_type) if self.__class__.output_json_in_init: self.generate_json_output() + self._upload_json_files() diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py index 0dda774..9125f9e 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py @@ -120,7 +120,7 @@ class Port(object): def check_image_diff(self, override_step=None, logging=True): """This routine is used to check whether image_diff binary exists.""" - raise NotImplemented('Port.check_image_diff') + raise NotImplementedError('Port.check_image_diff') def compare_text(self, expected_text, actual_text): """Return whether or not the two strings are *not* equal. This @@ -141,26 +141,9 @@ class Port(object): |tolerance| should be a percentage value (0.0 - 100.0). If it is omitted, the port default tolerance value is used. - While this is a generic routine, we include it in the Port - interface so that it can be overriden for testing purposes.""" - executable = self._path_to_image_diff() - - if diff_filename: - cmd = [executable, '--diff', expected_filename, actual_filename, - diff_filename] - else: - cmd = [executable, expected_filename, actual_filename] + """ + raise NotImplementedError('Port.diff_image') - result = True - try: - if self._executive.run_command(cmd, return_exit_code=True) == 0: - return False - except OSError, e: - if e.errno == errno.ENOENT or e.errno == errno.EACCES: - _compare_available = False - else: - raise e - return result def diff_text(self, expected_text, actual_text, expected_filename, actual_filename): @@ -305,6 +288,18 @@ class Port(object): """Return the absolute path to the top of the LayoutTests directory.""" return self.path_from_webkit_base('LayoutTests') + def skips_layout_test(self, test_name): + """Figures out if the givent test is being skipped or not. + + Test categories are handled as well.""" + for test_or_category in self.skipped_layout_tests(): + if test_or_category == test_name: + return True + category = os.path.join(self.layout_tests_dir(), test_or_category) + if os.path.isdir(category) and test_name.startswith(test_or_category): + return True + return False + def maybe_make_directory(self, *path): """Creates the specified directory if it doesn't already exist.""" try: @@ -341,84 +336,16 @@ class Port(object): if the port does not use expectations files.""" raise NotImplementedError('Port.path_to_test_expectations_file') - def remove_directory(self, *path): - """Recursively removes a directory, even if it's marked read-only. - - Remove the directory located at *path, if it exists. - - shutil.rmtree() doesn't work on Windows if any of the files - or directories are read-only, which svn repositories and - some .svn files are. We need to be able to force the files - to be writable (i.e., deletable) as we traverse the tree. - - Even with all this, Windows still sometimes fails to delete a file, - citing a permission error (maybe something to do with antivirus - scans or disk indexing). The best suggestion any of the user - forums had was to wait a bit and try again, so we do that too. - It's hand-waving, but sometimes it works. :/ - """ - file_path = os.path.join(*path) - if not os.path.exists(file_path): - return - - win32 = False - if sys.platform == 'win32': - win32 = True - # Some people don't have the APIs installed. In that case we'll do - # without. - try: - win32api = __import__('win32api') - win32con = __import__('win32con') - except ImportError: - win32 = False - - def remove_with_retry(rmfunc, path): - os.chmod(path, os.stat.S_IWRITE) - if win32: - win32api.SetFileAttributes(path, - win32con.FILE_ATTRIBUTE_NORMAL) - try: - return rmfunc(path) - except EnvironmentError, e: - if e.errno != errno.EACCES: - raise - print 'Failed to delete %s: trying again' % repr(path) - time.sleep(0.1) - return rmfunc(path) - else: - - def remove_with_retry(rmfunc, path): - if os.path.islink(path): - return os.remove(path) - else: - return rmfunc(path) - - for root, dirs, files in os.walk(file_path, topdown=False): - # For POSIX: making the directory writable guarantees - # removability. Windows will ignore the non-read-only - # bits in the chmod value. - os.chmod(root, 0770) - for name in files: - remove_with_retry(os.remove, os.path.join(root, name)) - for name in dirs: - remove_with_retry(os.rmdir, os.path.join(root, name)) - - remove_with_retry(os.rmdir, file_path) - - def test_platform_name(self): - return self._name - def relative_test_filename(self, filename): """Relative unix-style path for a filename under the LayoutTests directory. Filenames outside the LayoutTests directory should raise an error.""" - # FIXME This should assert() here but cannot due to printing_unittest.Testprinter - # assert(filename.startswith(self.layout_tests_dir())) + assert(filename.startswith(self.layout_tests_dir())) return filename[len(self.layout_tests_dir()) + 1:] def results_directory(self): """Absolute path to the place to store the test results.""" - raise NotImplemented('Port.results_directory') + raise NotImplementedError('Port.results_directory') def setup_test_run(self): """Perform port-specific work at the beginning of a test run.""" @@ -608,7 +535,6 @@ class Port(object): _wdiff_available = False return "" raise - assert(False) # Should never be reached. _pretty_patch_error_html = "Failed to run PrettyPatch, see error console." @@ -636,7 +562,7 @@ class Port(object): return self._pretty_patch_error_html def _webkit_build_directory(self, args): - args = [self.script_path("webkit-build-directory")] + args + args = ["perl", self.script_path("webkit-build-directory")] + args return self._executive.run_command(args).rstrip() def _configuration_file_path(self): @@ -691,6 +617,10 @@ class Port(object): """Returns the full path to the test driver (DumpRenderTree).""" raise NotImplementedError('Port.path_to_driver') + def _path_to_webcore_library(self): + """Returns the full path to a built copy of WebCore.""" + raise NotImplementedError('Port.path_to_webcore_library') + def _path_to_helper(self): """Returns the full path to the layout_test_helper binary, which is used to help configure the system for the test run, or None @@ -808,10 +738,5 @@ class Driver: if it has exited.""" raise NotImplementedError('Driver.poll') - def returncode(self): - """Returns the system-specific returncode if the Driver has stopped or - exited.""" - raise NotImplementedError('Driver.returncode') - def stop(self): raise NotImplementedError('Driver.stop') diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py index f821353..1cc426f 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py @@ -27,15 +27,49 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import base -import unittest +import os +import StringIO +import sys import tempfile +import unittest from webkitpy.common.system.executive import Executive, ScriptError from webkitpy.thirdparty.mock import Mock -class PortTest(unittest.TestCase): +# FIXME: This makes StringIO objects work with "with". Remove +# when we upgrade to 2.6. +class NewStringIO(StringIO.StringIO): + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + pass + + +class MockExecutive(): + def __init__(self, exception): + self._exception = exception + + def run_command(self, *args, **kwargs): + raise self._exception + +class UnitTestPort(base.Port): + """Subclass of base.Port used for unit testing.""" + def __init__(self, configuration_contents=None, executive_exception=None): + base.Port.__init__(self) + self._configuration_contents = configuration_contents + if executive_exception: + self._executive = MockExecutive(executive_exception) + + def _open_configuration_file(self): + if self._configuration_contents: + return NewStringIO(self._configuration_contents) + return base.Port._open_configuration_file(self) + + +class PortTest(unittest.TestCase): def test_format_wdiff_output_as_html(self): output = "OUTPUT %s %s %s" % (base.Port._WDIFF_DEL, base.Port._WDIFF_ADD, base.Port._WDIFF_END) html = base.Port()._format_wdiff_output_as_html(output) @@ -63,6 +97,26 @@ class PortTest(unittest.TestCase): new_file.flush() return new_file + def test_pretty_patch_os_error(self): + port = UnitTestPort(executive_exception=OSError) + self.assertEqual(port.pretty_patch_text("patch.txt"), + port._pretty_patch_error_html) + + # This tests repeated calls to make sure we cache the result. + self.assertEqual(port.pretty_patch_text("patch.txt"), + port._pretty_patch_error_html) + + def test_pretty_patch_script_error(self): + # FIXME: This is some ugly white-box test hacking ... + base._pretty_patch_available = True + port = UnitTestPort(executive_exception=ScriptError) + self.assertEqual(port.pretty_patch_text("patch.txt"), + port._pretty_patch_error_html) + + # This tests repeated calls to make sure we cache the result. + self.assertEqual(port.pretty_patch_text("patch.txt"), + port._pretty_patch_error_html) + def test_run_wdiff(self): executive = Executive() # This may fail on some systems. We could ask the port @@ -109,6 +163,79 @@ class PortTest(unittest.TestCase): self.assertFalse(base._wdiff_available) base._wdiff_available = True + def test_default_configuration_notfound(self): + port = UnitTestPort() + self.assertEqual(port.default_configuration(), "Release") + + def test_layout_tests_skipping(self): + port = base.Port() + port.skipped_layout_tests = lambda: ['foo/bar.html', 'media'] + self.assertTrue(port.skips_layout_test('foo/bar.html')) + self.assertTrue(port.skips_layout_test('media/video-zoom.html')) + self.assertFalse(port.skips_layout_test('foo/foo.html')) + + def test_default_configuration_found(self): + port = UnitTestPort(configuration_contents="Debug") + self.assertEqual(port.default_configuration(), "Debug") + + def test_default_configuration_unknown(self): + port = UnitTestPort(configuration_contents="weird_value") + self.assertEqual(port.default_configuration(), "weird_value") + + def test_setup_test_run(self): + port = base.Port() + # This routine is a no-op. We just test it for coverage. + port.setup_test_run() + + +class VirtualTest(unittest.TestCase): + """Tests that various methods expected to be virtual are.""" + def assertVirtual(self, method, *args, **kwargs): + self.assertRaises(NotImplementedError, method, *args, **kwargs) + + def test_virtual_methods(self): + port = base.Port() + self.assertVirtual(port.baseline_path) + self.assertVirtual(port.baseline_search_path) + self.assertVirtual(port.check_build, None) + self.assertVirtual(port.check_image_diff) + self.assertVirtual(port.create_driver, None, None) + self.assertVirtual(port.diff_image, None, None) + self.assertVirtual(port.path_to_test_expectations_file) + self.assertVirtual(port.test_platform_name) + self.assertVirtual(port.results_directory) + self.assertVirtual(port.show_html_results_file, None) + self.assertVirtual(port.test_expectations) + self.assertVirtual(port.test_base_platform_names) + self.assertVirtual(port.test_platform_name) + self.assertVirtual(port.test_platforms) + self.assertVirtual(port.test_platform_name_to_name, None) + self.assertVirtual(port.version) + self.assertVirtual(port._path_to_apache) + self.assertVirtual(port._path_to_apache_config_file) + self.assertVirtual(port._path_to_driver) + self.assertVirtual(port._path_to_helper) + self.assertVirtual(port._path_to_image_diff) + self.assertVirtual(port._path_to_lighttpd) + self.assertVirtual(port._path_to_lighttpd_modules) + self.assertVirtual(port._path_to_lighttpd_php) + self.assertVirtual(port._path_to_wdiff) + self.assertVirtual(port._shut_down_http_server, None) + + def test_virtual_driver_method(self): + self.assertRaises(NotImplementedError, base.Driver, base.Port, "", None) + self.assertVirtual(base.Driver, base.Port, "", None) + + def test_virtual_driver_methods(self): + class VirtualDriver(base.Driver): + def __init__(self): + pass + + driver = VirtualDriver() + self.assertVirtual(driver.run_test, None, None, None) + self.assertVirtual(driver.poll) + self.assertVirtual(driver.stop) + class DriverTest(unittest.TestCase): @@ -124,3 +251,7 @@ class DriverTest(unittest.TestCase): command_with_spaces = "valgrind --smc-check=\"check with spaces!\" --foo" expected_parse = ["valgrind", "--smc-check=check with spaces!", "--foo"] self._assert_wrapper(command_with_spaces, expected_parse) + + +if __name__ == '__main__': + unittest.main() diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py index 6cfc0b8..896eab1 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py @@ -46,6 +46,8 @@ import base import http_server from webkitpy.common.system.executive import Executive +from webkitpy.layout_tests.layout_package import test_files +from webkitpy.layout_tests.layout_package import test_expectations # Chromium DRT on OSX uses WebKitDriver. if sys.platform == 'darwin': @@ -124,6 +126,26 @@ class ChromiumPort(base.Port): return check_file_exists(image_diff_path, 'image diff exe', override_step, logging) + def diff_image(self, expected_filename, actual_filename, + diff_filename=None, tolerance=0): + executable = self._path_to_image_diff() + if diff_filename: + cmd = [executable, '--diff', expected_filename, actual_filename, + diff_filename] + else: + cmd = [executable, expected_filename, actual_filename] + + result = True + try: + if self._executive.run_command(cmd, return_exit_code=True) == 0: + return False + except OSError, e: + if e.errno == errno.ENOENT or e.errno == errno.EACCES: + _compare_available = False + else: + raise e + return result + def driver_name(self): return "test_shell" @@ -162,7 +184,7 @@ class ChromiumPort(base.Port): shutil.rmtree(cachedir) def show_results_html_file(self, results_filename): - uri = self.filename_to_uri(results_filename) + uri = self.get_absolute_path(results_filename) if self._options.use_drt: # FIXME: This should use User.open_url webbrowser.open(uri, new=1) @@ -233,6 +255,24 @@ class ChromiumPort(base.Port): with codecs.open(overrides_path, "r", "utf-8") as file: return file.read() + drt_overrides + def skipped_layout_tests(self, extra_test_files=None): + expectations_str = self.test_expectations() + overrides_str = self.test_expectations_overrides() + test_platform_name = self.test_platform_name() + is_debug_mode = False + + all_test_files = test_files.gather_test_files(self, '*') + if extra_test_files: + all_test_files.update(extra_test_files) + + expectations = test_expectations.TestExpectations( + self, all_test_files, expectations_str, test_platform_name, + is_debug_mode, is_lint_mode=True, + tests_are_present=False, overrides=overrides_str) + tests_dir = self.layout_tests_dir() + return [self.relative_test_filename(test) + for test in expectations.get_tests_with_result_type(test_expectations.SKIP)] + def test_platform_names(self): return self.test_base_platform_names() + ('win-xp', 'win-vista', 'win-7') @@ -330,9 +370,6 @@ class ChromiumDriver(base.Driver): # http://bugs.python.org/issue1731717 return self._proc.poll() - def returncode(self): - return self._proc.returncode - def _write_command_and_read_line(self, input=None): """Returns a tuple: (line, did_crash)""" try: diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py index a32eafd..7a005b1 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py @@ -32,6 +32,7 @@ import chromium_mac import chromium_win import unittest import StringIO +import os from webkitpy.thirdparty.mock import Mock @@ -95,3 +96,22 @@ class ChromiumDriverTest(unittest.TestCase): '/xcodebuild/Release/ImageDiff')) # FIXME: Figure out how this is going to work on Windows. #port = chromium_win.ChromiumWinPort('test-port', options=MockOptions()) + + def test_skipped_layout_tests(self): + class MockOptions: + def __init__(self): + self.use_drt = True + + port = chromium_linux.ChromiumLinuxPort('test-port', options=MockOptions()) + + fake_test = os.path.join(port.layout_tests_dir(), "fast/js/not-good.js") + + port.test_expectations = lambda: """BUG_TEST SKIP : fast/js/not-good.js = TEXT +DEFER LINUX WIN : fast/js/very-good.js = TIMEOUT PASS""" + port.test_expectations_overrides = lambda: '' + + skipped_tests = port.skipped_layout_tests(extra_test_files=[fake_test, ]) + self.assertTrue("fast/js/not-good.js" in skipped_tests) + +if __name__ == '__main__': + unittest.main() diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py index e01bd2f..1af01ad 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py @@ -70,14 +70,11 @@ def _read_file(path, mode='r'): def _write_file(path, contents, mode='w'): """Write the string to the specified path. - Returns nothing if the write fails, instead of raising an IOError. + Writes should never fail, so we may raise IOError. """ - try: - with open(path, mode) as f: + with open(path, mode) as f: f.write(contents) - except IOError: - pass class DryRunPort(object): @@ -134,9 +131,6 @@ class DryrunDriver(base.Driver): def poll(self): return None - def returncode(self): - return 0 - def run_test(self, uri, timeoutms, image_hash): test_name = self._uri_to_test(uri) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/factory.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/factory.py index 258bf33..5704f65 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/factory.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/factory.py @@ -32,6 +32,10 @@ import sys +ALL_PORT_NAMES = ['test', 'dryrun', 'mac', 'win', 'gtk', 'qt', 'chromium-mac', + 'chromium-linux', 'chromium-win', 'google-chrome-win', + 'google-chrome-mac', 'google-chrome-linux32', 'google-chrome-linux64'] + def get(port_name=None, options=None): """Returns an object implementing the Port interface. If @@ -88,3 +92,9 @@ def get(port_name=None, options=None): return google_chrome.GetGoogleChromePort(port_name, options) raise NotImplementedError('unsupported port: %s' % port_to_use) + + +def get_all(options=None): + """Returns all the objects implementing the Port interface.""" + return dict([(port_name, get(port_name, options=options)) + for port_name in ALL_PORT_NAMES]) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/factory_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/factory_unittest.py index d8dffdf..c0a4c5e 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/factory_unittest.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/factory_unittest.py @@ -34,6 +34,7 @@ import chromium_mac import chromium_win import dryrun import factory +import google_chrome import gtk import mac import qt @@ -69,16 +70,16 @@ class FactoryTest(unittest.TestCase): def tearDown(self): sys.platform = self.real_sys_platform - def assert_port(self, port_name, expected_port): + def assert_port(self, port_name, expected_port, port_obj=None): """Helper assert for port_name. Args: port_name: port name to get port object. expected_port: class of expected port object. - + port_obj: optional port object """ - self.assertTrue(isinstance(factory.get(port_name=port_name), - expected_port)) + port_obj = port_obj or factory.get(port_name=port_name) + self.assertTrue(isinstance(port_obj, expected_port)) def assert_platform_port(self, platform, options, expected_port): """Helper assert for platform and options. @@ -114,6 +115,18 @@ class FactoryTest(unittest.TestCase): self.assert_platform_port("cygwin", None, win.WinPort) self.assert_platform_port("cygwin", self.webkit_options, win.WinPort) + def test_google_chrome(self): + # The actual Chrome class names aren't available so we test that the + # objects we get are at least subclasses of the Chromium versions. + self.assert_port("google-chrome-linux32", + chromium_linux.ChromiumLinuxPort) + self.assert_port("google-chrome-linux64", + chromium_linux.ChromiumLinuxPort) + self.assert_port("google-chrome-win", + chromium_win.ChromiumWinPort) + self.assert_port("google-chrome-mac", + chromium_mac.ChromiumMacPort) + def test_gtk(self): self.assert_port("gtk", gtk.GtkPort) @@ -136,3 +149,38 @@ class FactoryTest(unittest.TestCase): chromium_win.ChromiumWinPort) self.assert_platform_port("cygwin", self.chromium_options, chromium_win.ChromiumWinPort) + + def test_get_all_ports(self): + ports = factory.get_all() + for name in factory.ALL_PORT_NAMES: + self.assertTrue(name in ports.keys()) + self.assert_port("test", test.TestPort, ports["test"]) + self.assert_port("dryrun-test", dryrun.DryRunPort, ports["dryrun"]) + self.assert_port("dryrun-mac", dryrun.DryRunPort, ports["dryrun"]) + self.assert_port("mac", mac.MacPort, ports["mac"]) + self.assert_port("win", win.WinPort, ports["win"]) + self.assert_port("gtk", gtk.GtkPort, ports["gtk"]) + self.assert_port("qt", qt.QtPort, ports["qt"]) + self.assert_port("chromium-mac", chromium_mac.ChromiumMacPort, + ports["chromium-mac"]) + self.assert_port("chromium-linux", chromium_linux.ChromiumLinuxPort, + ports["chromium-linux"]) + self.assert_port("chromium-win", chromium_win.ChromiumWinPort, + ports["chromium-win"]) + + def test_unknown_specified(self): + # Test what happens when you specify an unknown port. + orig_platform = sys.platform + self.assertRaises(NotImplementedError, factory.get, + port_name='unknown') + + def test_unknown_default(self): + # Test what happens when you're running on an unknown platform. + orig_platform = sys.platform + sys.platform = 'unknown' + self.assertRaises(NotImplementedError, factory.get) + sys.platform = orig_platform + + +if __name__ == '__main__': + unittest.main() diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/lighttpd.conf b/WebKitTools/Scripts/webkitpy/layout_tests/port/lighttpd.conf index 2e9c82e..26ca22f 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/lighttpd.conf +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/lighttpd.conf @@ -22,7 +22,7 @@ mimetype.assign = ( ".htm" => "text/html", ".xhtml" => "application/xhtml+xml", ".xhtmlmp" => "application/vnd.wap.xhtml+xml", - ".js" => "text/javascript", + ".js" => "application/x-javascript", ".log" => "text/plain", ".conf" => "text/plain", ".text" => "text/plain", diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/mac_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/mac_unittest.py index ae7d40c..327b19e 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/mac_unittest.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/mac_unittest.py @@ -26,14 +26,27 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import StringIO +import sys import unittest + import mac -import StringIO +import port_testcase + -class MacTest(unittest.TestCase): +class MacTest(port_testcase.PortTestCase): + def make_port(self, options=port_testcase.MockOptions()): + if sys.platform != 'darwin': + return None + port_obj = mac.MacPort(options=options) + port_obj._options.results_directory = port_obj.results_directory() + port_obj._options.configuration = 'Release' + return port_obj def test_skipped_file_paths(self): - port = mac.MacPort() + port = self.make_port() + if not port: + return skipped_paths = port._skipped_file_paths() # FIXME: _skipped_file_paths should return WebKit-relative paths. # So to make it unit testable, we strip the WebKit directory from the path. @@ -57,7 +70,9 @@ svg/batik/text/smallFonts.svg ] def test_skipped_file_paths(self): - port = mac.MacPort() + port = self.make_port() + if not port: + return skipped_file = StringIO.StringIO(self.example_skipped_file) self.assertEqual(port._tests_from_skipped_file(skipped_file), self.example_skipped_tests) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/port_testcase.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/port_testcase.py new file mode 100644 index 0000000..2d650f5 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/port_testcase.py @@ -0,0 +1,88 @@ +# Copyright (C) 2010 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Unit testing base class for Port implementations.""" + +import os +import tempfile +import unittest + + +class MockOptions(object): + def __init__(self, + results_directory='layout-test-results', + use_apache=True, + configuration='Release'): + self.results_directory = results_directory + self.use_apache = use_apache + self.configuration = configuration + + +class PortTestCase(unittest.TestCase): + """Tests the WebKit port implementation.""" + def make_port(self, options=MockOptions()): + """Override in subclass.""" + raise NotImplementedError() + + def test_http_server(self): + port = self.make_port() + if not port: + return + port.start_http_server() + port.stop_http_server() + + def test_image_diff(self): + port = self.make_port() + if not port: + return + + # FIXME: not sure why this shouldn't always be True + #self.assertTrue(port.check_image_diff()) + if not port.check_image_diff(): + return + + dir = port.layout_tests_dir() + file1 = os.path.join(dir, 'fast', 'css', 'button_center.png') + file2 = os.path.join(dir, 'fast', 'css', + 'remove-shorthand-expected.png') + tmpfile = tempfile.mktemp() + + self.assertFalse(port.diff_image(file1, file1)) + self.assertTrue(port.diff_image(file1, file2)) + + self.assertTrue(port.diff_image(file1, file2, tmpfile)) + # FIXME: this may not be being written? + # self.assertTrue(os.path.exists(tmpfile)) + # os.remove(tmpfile) + + def test_websocket_server(self): + port = self.make_port() + if not port: + return + port.start_websocket_server() + port.stop_websocket_server() diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/qt.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/qt.py index 41b2ba0..158c633 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/qt.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/qt.py @@ -93,6 +93,12 @@ class QtPort(WebKitPort): def _path_to_driver(self): return self._build_path('bin/DumpRenderTree') + def _path_to_webcore_library(self): + return self._build_path('lib/libQtWebKit.so') + + def _runtime_feature_list(self): + return None + def setup_environ_for_server(self): env = webkit.WebKitPort.setup_environ_for_server(self) env['QTWEBKIT_PLUGIN_PATH'] = self._build_path('lib/plugins') diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/server_process.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/server_process.py index 62ca693..8e0bc11 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/server_process.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/server_process.py @@ -29,7 +29,6 @@ """Package that implements the ServerProcess wrapper class""" -import fcntl import logging import os import select @@ -37,6 +36,8 @@ import signal import subprocess import sys import time +if sys.platform != 'win32': + import fcntl from webkitpy.common.system.executive import Executive @@ -109,14 +110,6 @@ class ServerProcess: return self._proc.poll() return None - def returncode(self): - """Returns the exit code from the subprcoess; returns None if the - process hasn't exited (this is a wrapper around subprocess.returncode). - """ - if self._proc: - return self._proc.returncode - return None - def write(self, input): """Write a request to the subprocess. The subprocess is (re-)start()'ed if is not already running.""" diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py index e309334..a3a16c3 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py @@ -76,6 +76,9 @@ class TestPort(base.Port): def options(self): return self._options + def skipped_layout_tests(self): + return [] + def path_to_test_expectations_file(self): return self.path_from_webkit_base('WebKitTools', 'Scripts', 'webkitpy', 'layout_tests', 'data', 'platform', 'test', @@ -145,9 +148,6 @@ class TestDriver(base.Driver): def poll(self): return True - def returncode(self): - return 0 - def run_test(self, uri, timeoutms, image_hash): basename = uri[(uri.rfind("/") + 1):uri.rfind(".html")] @@ -179,6 +179,7 @@ class TestDriver(base.Driver): raise ValueError('exception from ' + basename) crash = 'crash' in basename + timeout = 'timeout' in basename or 'hang' in basename timeout = 'timeout' in basename if 'text' in basename: output = basename + '_failed-txt\n' @@ -196,6 +197,9 @@ class TestDriver(base.Driver): f.write(basename + "-png\n") if 'checksum' in basename: checksum = basename + "_failed-checksum\n" + + if 'hang' in basename: + time.sleep((float(timeoutms) * 4) / 1000.0) else: crash = False timeout = False diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py index cedc028..b085ceb 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # Copyright (C) 2010 Google Inc. All rights reserved. +# Copyright (C) 2010 Gabor Rapcsanyi <rgabor@inf.u-szeged.hu>, University of Szeged # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -41,6 +42,9 @@ import signal import sys import time import webbrowser +import operator +import tempfile +import shutil from webkitpy.common.system.executive import Executive @@ -217,6 +221,66 @@ class WebKitPort(base.Port): "platform/win", ] + def _runtime_feature_list(self): + """Return the supported features of DRT. If a port doesn't support + this DRT switch, it has to override this method to return None""" + driver_path = self._path_to_driver() + feature_list = ' '.join(os.popen(driver_path + " --print-supported-features 2>&1").readlines()) + if "SupportedFeatures:" in feature_list: + return feature_list + return None + + def _supported_symbol_list(self): + """Return the supported symbols of WebCore.""" + webcore_library_path = self._path_to_webcore_library() + if not webcore_library_path: + return None + symbol_list = ' '.join(os.popen("nm " + webcore_library_path).readlines()) + return symbol_list + + def _directories_for_features(self): + """Return the supported feature dictionary. The keys are the + features and the values are the directories in lists.""" + directories_for_features = { + "Accelerated Compositing": ["compositing"], + "3D Rendering": ["animations/3d", "transforms/3d"], + } + return directories_for_features + + def _directories_for_symbols(self): + """Return the supported feature dictionary. The keys are the + symbols and the values are the directories in lists.""" + directories_for_symbol = { + "MathMLElement": ["mathml"], + "GraphicsLayer": ["compositing"], + "WebCoreHas3DRendering": ["animations/3d", "transforms/3d"], + "WebGLShader": ["fast/canvas/webgl"], + "WMLElement": ["http/tests/wml", "fast/wml", "wml"], + "parseWCSSInputProperty": ["fast/wcss"], + "isXHTMLMPDocument": ["fast/xhtmlmp"], + } + return directories_for_symbol + + def _skipped_tests_for_unsupported_features(self): + """Return the directories of unsupported tests. Search for the + symbols in the symbol_list, if found add the corresponding + directories to the skipped directory list.""" + feature_list = self._runtime_feature_list() + directories = self._directories_for_features() + + # if DRT feature detection not supported + if not feature_list: + feature_list = self._supported_symbol_list() + directories = self._directories_for_symbols() + + if not feature_list: + return [] + + skipped_directories = [directories[feature] + for feature in directories.keys() + if feature not in feature_list] + return reduce(operator.add, skipped_directories) + def _tests_for_disabled_features(self): # FIXME: This should use the feature detection from # webkitperl/features.pm to match run-webkit-tests. @@ -238,7 +302,8 @@ class WebKitPort(base.Port): "http/tests/webarchive", "svg/custom/image-with-prefix-in-webarchive.svg", ] - return disabled_feature_tests + webarchive_tests + unsupported_feature_tests = self._skipped_tests_for_unsupported_features() + return disabled_feature_tests + webarchive_tests + unsupported_feature_tests def _tests_from_skipped_file(self, skipped_file): tests_to_skip = [] @@ -279,14 +344,17 @@ class WebKitPort(base.Port): # This routine reads those files and turns contents into the # format expected by test_expectations. + tests_to_skip = self.skipped_layout_tests() + skip_lines = map(lambda test_path: "BUG_SKIPPED SKIP : %s = FAIL" % + test_path, tests_to_skip) + return "\n".join(skip_lines) + + def skipped_layout_tests(self): # Use a set to allow duplicates tests_to_skip = set(self._expectations_from_skipped_files()) - tests_to_skip.update(self._tests_for_other_platforms()) tests_to_skip.update(self._tests_for_disabled_features()) - skip_lines = map(lambda test_path: "BUG_SKIPPED SKIP : %s = FAIL" % - test_path, tests_to_skip) - return "\n".join(skip_lines) + return tests_to_skip def test_platform_name(self): return self._name + self.version() @@ -306,6 +374,9 @@ class WebKitPort(base.Port): def _path_to_driver(self): return self._build_path('DumpRenderTree') + def _path_to_webcore_library(self): + return None + def _path_to_helper(self): return None @@ -338,6 +409,10 @@ class WebKitDriver(base.Driver): self._port = port # FIXME: driver_options is never used. self._image_path = image_path + self._driver_tempdir = tempfile.mkdtemp(prefix='DumpRenderTree-') + + def __del__(self): + shutil.rmtree(self._driver_tempdir) def start(self): command = [] @@ -348,6 +423,7 @@ class WebKitDriver(base.Driver): command.append('--pixel-tests') environment = self._port.setup_environ_for_server() environment['DYLD_FRAMEWORK_PATH'] = self._port._build_path() + environment['DUMPRENDERTREE_TEMP'] = self._driver_tempdir self._server_process = server_process.ServerProcess(self._port, "DumpRenderTree", command, environment) @@ -359,9 +435,6 @@ class WebKitDriver(base.Driver): self._server_process.start() return - def returncode(self): - return self._server_process.returncode() - # FIXME: This function is huge. def run_test(self, uri, timeoutms, image_hash): if uri.startswith("file:///"): diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit_unittest.py new file mode 100644 index 0000000..fbfadc3 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit_unittest.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# Copyright (C) 2010 Gabor Rapcsanyi <rgabor@inf.u-szeged.hu>, University of Szeged +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import unittest + +from webkitpy.layout_tests.port.webkit import WebKitPort + + +class TestWebKitPort(WebKitPort): + def __init__(self, symbol_list=None, feature_list=None): + self.symbol_list = symbol_list + self.feature_list = feature_list + + def _runtime_feature_list(self): + return self.feature_list + + def _supported_symbol_list(self): + return self.symbol_list + + def _tests_for_other_platforms(self): + return ["media", ] + + def _tests_for_disabled_features(self): + return ["accessibility", ] + + def _skipped_file_paths(self): + return [] + +class WebKitPortTest(unittest.TestCase): + + def test_skipped_directories_for_symbols(self): + supported_symbols = ["GraphicsLayer", "WebCoreHas3DRendering", "isXHTMLMPDocument", "fooSymbol"] + expected_directories = set(["mathml", "fast/canvas/webgl", "http/tests/wml", "fast/wml", "wml", "fast/wcss"]) + result_directories = set(TestWebKitPort(supported_symbols, None)._skipped_tests_for_unsupported_features()) + self.assertEqual(result_directories, expected_directories) + + def test_skipped_directories_for_features(self): + supported_features = ["Accelerated Compositing", "Foo Feature"] + expected_directories = set(["animations/3d", "transforms/3d"]) + result_directories = set(TestWebKitPort(None, supported_features)._skipped_tests_for_unsupported_features()) + self.assertEqual(result_directories, expected_directories) + + def test_skipped_layout_tests(self): + self.assertEqual(TestWebKitPort(None, None).skipped_layout_tests(), + set(["media", "accessibility"])) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py b/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py index 4132260..2e2da6d 100755 --- a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py @@ -53,7 +53,6 @@ import logging import math import optparse import os -import pdb import platform import Queue import random @@ -371,7 +370,7 @@ class TestRunner: assert(test_size > 0) except: _log.critical("invalid chunk '%s'" % chunk_value) - sys.exit(1) + return None # Get the number of tests num_tests = len(test_files) @@ -675,8 +674,7 @@ class TestRunner: # clear that testing was aborted. Otherwise, # the tests that did not run would be assumed # to have passed. - raise (exception_info[0], exception_info[1], - exception_info[2]) + raise exception_info[0], exception_info[1], exception_info[2] if thread.isAlive(): some_thread_is_alive = True @@ -1350,11 +1348,18 @@ class TestRunner: def read_test_files(files): tests = [] for file in files: - # FIXME: This could be cleaner using a list comprehension. - for line in codecs.open(file, "r", "utf-8"): - line = test_expectations.strip_comments(line) - if line: - tests.append(line) + try: + with codecs.open(file, 'r', 'utf-8') as file_contents: + # FIXME: This could be cleaner using a list comprehension. + for line in file_contents: + line = test_expectations.strip_comments(line) + if line: + tests.append(line) + except IOError, e: + if e.errno == errno.ENOENT: + _log.critical('') + _log.critical('--test-list file "%s" not found' % file) + raise return tests @@ -1398,7 +1403,12 @@ def run(port, options, args, regular_output=sys.stderr, test_runner._print_config() printer.print_update("Collecting tests ...") - test_runner.collect_tests(args, last_unexpected_results) + try: + test_runner.collect_tests(args, last_unexpected_results) + except IOError, e: + if e.errno == errno.ENOENT: + return -1 + raise printer.print_update("Parsing expectations ...") if options.lint_test_files: @@ -1695,31 +1705,14 @@ def parse_args(args=None): return options, args -def _find_thread_stack(id): - """Returns a stack object that can be used to dump a stack trace for - the given thread id (or None if the id is not found).""" - for thread_id, stack in sys._current_frames().items(): - if thread_id == id: - return stack - return None - - -def _log_stack(stack): - """Log a stack trace to log.error().""" - for filename, lineno, name, line in traceback.extract_stack(stack): - _log.error('File: "%s", line %d, in %s' % (filename, lineno, name)) - if line: - _log.error(' %s' % line.strip()) - - def _log_wedged_thread(thread): """Log information about the given thread state.""" id = thread.id() - stack = _find_thread_stack(id) + stack = dump_render_tree_thread.find_thread_stack(id) assert(stack is not None) _log.error("") _log.error("thread %s (%d) is wedged" % (thread.getName(), id)) - _log_stack(stack) + dump_render_tree_thread.log_stack(stack) _log.error("") diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py index 4cbfdfc..aa96962 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py @@ -32,9 +32,9 @@ import codecs import logging import os -import pdb import Queue import sys +import tempfile import thread import time import threading @@ -59,7 +59,10 @@ def passing_run(args=[], port_obj=None, record_results=False, new_args.extend(args) if not tests_included: # We use the glob to test that globbing works. - new_args.extend(['passes', 'failures/expected/*']) + new_args.extend(['passes', + 'http/tests', + 'websocket/tests', + 'failures/expected/*']) options, parsed_args = run_webkit_tests.parse_args(new_args) if port_obj is None: port_obj = port.get(options.platform, options) @@ -71,10 +74,12 @@ def logging_run(args=[], tests_included=False): new_args = ['--no-record-results'] if not '--platform' in args: new_args.extend(['--platform', 'test']) - if args: - new_args.extend(args) + new_args.extend(args) if not tests_included: - new_args.extend(['passes', 'failures/expected/*']) + new_args.extend(['passes', + 'http/tests' + 'websocket/tests', + 'failures/expected/*']) options, parsed_args = run_webkit_tests.parse_args(new_args) port_obj = port.get(options.platform, options) buildbot_output = array_stream.ArrayStream() @@ -119,6 +124,14 @@ class MainTest(unittest.TestCase): self.assertTrue(out.empty()) self.assertFalse(err.empty()) + def test_hung_thread(self): + res, out, err = logging_run(['--run-singly', '--time-out-ms=50', + 'failures/expected/hang.html'], + tests_included=True) + self.assertEqual(res, 0) + self.assertFalse(out.empty()) + self.assertFalse(err.empty()) + def test_keyboard_interrupt(self): # Note that this also tests running a test marked as SKIP if # you specify it explicitly. @@ -175,6 +188,19 @@ class MainTest(unittest.TestCase): # FIXME: verify # of tests run self.assertTrue(passing_run(['passes/text.html'], tests_included=True)) + def test_test_list(self): + filename = tempfile.mktemp() + tmpfile = file(filename, mode='w+') + tmpfile.write('passes/text.html') + tmpfile.close() + self.assertTrue(passing_run(['--test-list=%s' % filename], + tests_included=True)) + os.remove(filename) + res, out, err = logging_run(['--test-list=%s' % filename], + tests_included=True) + self.assertEqual(res, -1) + self.assertFalse(err.empty()) + def test_unexpected_failures(self): # Run tests including the unexpected failures. res, out, err = logging_run(tests_included=True) @@ -279,6 +305,13 @@ class DryrunTest(unittest.TestCase): self.assertTrue(passing_run(['--platform', 'dryrun-mac', 'fast/html'])) + def test_test(self): + res, out, err = logging_run(['--platform', 'dryrun-test', + '--pixel-tests']) + self.assertEqual(res, 2) + self.assertFalse(out.empty()) + self.assertFalse(err.empty()) + class TestThread(dump_render_tree_thread.WatchableThread): def __init__(self, started_queue, stopping_queue): @@ -388,13 +421,6 @@ class StandaloneFunctionsTest(unittest.TestCase): self.assertFalse(child_thread.isAlive()) oc.restore_output() - def test_find_thread_stack(self): - id, stack = sys._current_frames().items()[0] - found_stack = run_webkit_tests._find_thread_stack(id) - self.assertNotEqual(found_stack, None) - - found_stack = run_webkit_tests._find_thread_stack(0) - self.assertEqual(found_stack, None) if __name__ == '__main__': unittest.main() diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queries.py b/WebKitTools/Scripts/webkitpy/tool/commands/queries.py index 91ce5e9..9b8d162 100644 --- a/WebKitTools/Scripts/webkitpy/tool/commands/queries.py +++ b/WebKitTools/Scripts/webkitpy/tool/commands/queries.py @@ -37,6 +37,7 @@ from webkitpy.common.system.user import User from webkitpy.tool.grammar import pluralize from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand from webkitpy.common.system.deprecated_logging import log +from webkitpy.layout_tests import port class BugsToCommit(AbstractDeclarativeCommand): @@ -284,3 +285,28 @@ and displayes the status of each builder.""" for builder in tool.buildbot.builder_statuses(): status_string = "ok" if builder["is_green"] else "FAIL" print "%s : %s" % (status_string.ljust(4), builder["name"]) + + +class SkippedPorts(AbstractDeclarativeCommand): + name = "skipped-ports" + help_text = "Print the list of ports skipping the given layout test(s)" + long_help = """Scans the the Skipped file of each port and figure +out what ports are skipping the test(s). Categories are taken in account too.""" + argument_names = "TEST_NAME" + + def execute(self, options, args, tool): + class Options: + # Required for chromium port. + use_drt = True + + results = dict([(test_name, []) for test_name in args]) + for port_name, port_object in tool.port_factory.get_all(options=Options).iteritems(): + for test_name in args: + if port_object.skips_layout_test(test_name): + results[test_name].append(port_name) + + for test_name, ports in results.iteritems(): + if ports: + print "Ports skipping test %r: %s" % (test_name, ', '.join(ports)) + else: + print "Test %r is not skipped by any port." % test_name diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queries_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/queries_unittest.py index 98ed545..7dddfe7 100644 --- a/WebKitTools/Scripts/webkitpy/tool/commands/queries_unittest.py +++ b/WebKitTools/Scripts/webkitpy/tool/commands/queries_unittest.py @@ -61,3 +61,13 @@ class QueryCommandsTest(CommandsTest): def test_tree_status(self): expected_stdout = "ok : Builder1\nok : Builder2\n" self.assert_execute_outputs(TreeStatus(), None, expected_stdout) + + def test_skipped_ports(self): + expected_stdout = "Ports skipping test 'media/foo/bar.html': test_port1, test_port2\n" + self.assert_execute_outputs(SkippedPorts(), ("media/foo/bar.html",), expected_stdout) + + expected_stdout = "Ports skipping test 'foo': test_port1\n" + self.assert_execute_outputs(SkippedPorts(), ("foo",), expected_stdout) + + expected_stdout = "Test 'media' is not skipped by any port.\n" + self.assert_execute_outputs(SkippedPorts(), ("media",), expected_stdout) diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py index f86e9a2..fd6543c 100644 --- a/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py +++ b/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py @@ -28,12 +28,14 @@ import os +from webkitpy.common.checkout.scm import CheckoutNeedsUpdate from webkitpy.common.net.bugzilla import Attachment from webkitpy.common.system.outputcapture import OutputCapture from webkitpy.thirdparty.mock import Mock from webkitpy.tool.commands.commandtest import CommandsTest from webkitpy.tool.commands.queues import * from webkitpy.tool.commands.queuestest import QueuesTest +from webkitpy.tool.commands.stepsequence import StepSequence from webkitpy.tool.mocktool import MockTool, MockSCM @@ -133,6 +135,16 @@ class AbstractReviewQueueTest(CommandsTest): self.assertFalse(queue.is_terminal_status("Foo")) +class NeedsUpdateSequence(StepSequence): + def _run(self, tool, options, state): + raise CheckoutNeedsUpdate([], 1, "", None) + + +class AlwaysCommitQueueTool(object): + def command_by_name(self, name): + return CommitQueue + + class CommitQueueTest(QueuesTest): def test_commit_queue(self): expected_stderr = { @@ -226,6 +238,20 @@ MOCK: update_work_items: commit-queue [106, 197] attachments.sort(queue._patch_cmp) self.assertEqual(attachments, expected_sort) + def test_auto_retry(self): + queue = CommitQueue() + options = Mock() + options.parent_command = "commit-queue" + tool = AlwaysCommitQueueTool() + sequence = NeedsUpdateSequence(None) + + expected_stderr = "Commit failed because the checkout is out of date. Please update and try again.\n" + OutputCapture().assert_outputs(self, sequence.run_and_handle_errors, [tool, options], expected_exception=TryAgain, expected_stderr=expected_stderr) + + self.assertEquals(options.update, True) + self.assertEquals(options.build, False) + self.assertEquals(options.test, False) + class RietveldUploadQueueTest(QueuesTest): def test_rietveld_upload_queue(self): diff --git a/WebKitTools/Scripts/webkitpy/tool/main.py b/WebKitTools/Scripts/webkitpy/tool/main.py index 0dd5017..9531b63 100755 --- a/WebKitTools/Scripts/webkitpy/tool/main.py +++ b/WebKitTools/Scripts/webkitpy/tool/main.py @@ -40,6 +40,7 @@ from webkitpy.common.net.rietveld import Rietveld from webkitpy.common.net.irc.ircproxy import IRCProxy from webkitpy.common.system.executive import Executive from webkitpy.common.system.user import User +from webkitpy.layout_tests import port import webkitpy.tool.commands as commands # FIXME: Remove these imports once all the commands are in the root of the # command package. @@ -76,6 +77,7 @@ class WebKitPatch(MultiCommandTool): self._checkout = None self.status_server = StatusServer() self.codereview = Rietveld(self.executive) + self.port_factory = port.factory def scm(self): # Lazily initialize SCM to not error-out before command line parsing (or when running non-scm commands). diff --git a/WebKitTools/Scripts/webkitpy/tool/mocktool.py b/WebKitTools/Scripts/webkitpy/tool/mocktool.py index 7eb8f4c..e3d36ce 100644 --- a/WebKitTools/Scripts/webkitpy/tool/mocktool.py +++ b/WebKitTools/Scripts/webkitpy/tool/mocktool.py @@ -556,6 +556,24 @@ class MockRietveld(): log("MOCK: Uploading patch to rietveld") +class MockTestPort1(): + + def skips_layout_test(self, test_name): + return test_name in ["media/foo/bar.html", "foo"] + + +class MockTestPort2(): + + def skips_layout_test(self, test_name): + return test_name == "media/foo/bar.html" + + +class MockPortFactory(): + + def get_all(self, options=None): + return {"test_port1": MockTestPort1(), "test_port2": MockTestPort2()} + + class MockTool(): def __init__(self, log_executive=False): @@ -570,6 +588,7 @@ class MockTool(): self.status_server = MockStatusServer() self.irc_password = "MOCK irc password" self.codereview = MockRietveld(self.executive) + self.port_factory = MockPortFactory() def scm(self): return self._scm |