summaryrefslogtreecommitdiffstats
path: root/WebKitTools/Scripts/webkitpy
diff options
context:
space:
mode:
authorIain Merrick <husky@google.com>2010-09-13 16:35:48 +0100
committerIain Merrick <husky@google.com>2010-09-16 12:10:42 +0100
commit5abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306 (patch)
treeddce1aa5e3b6967a69691892e500897558ff8ab6 /WebKitTools/Scripts/webkitpy
parent12bec63ec71e46baba27f0bd9bd9d8067683690a (diff)
downloadexternal_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')
-rw-r--r--WebKitTools/Scripts/webkitpy/common/config/committers.py22
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/data/failures/expected/hang.html1
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/data/http/tests/passes/text-expected.txt1
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/data/http/tests/passes/text.html1
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/data/http/tests/ssl/text-expected.txt1
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/data/http/tests/ssl/text.html1
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/data/platform/test/test_expectations.txt1
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/data/websocket/tests/passes/text-expected.txt1
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/data/websocket/tests/passes/text.html1
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests.py33
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests_unittest.py49
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py42
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread_unittest.py49
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py50
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/base.py119
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py135
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py45
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py20
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py10
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/factory.py10
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/factory_unittest.py56
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/lighttpd.conf2
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/mac_unittest.py23
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/port_testcase.py88
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/qt.py6
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/server_process.py11
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/test.py10
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py89
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/webkit_unittest.py68
-rwxr-xr-xWebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py51
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py50
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queries.py26
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queries_unittest.py10
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py26
-rwxr-xr-xWebKitTools/Scripts/webkitpy/tool/main.py2
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/mocktool.py19
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