summaryrefslogtreecommitdiffstats
path: root/WebKitTools/Scripts/webkitpy/layout_tests/layout_package
diff options
context:
space:
mode:
authorIain Merrick <husky@google.com>2010-08-19 17:55:56 +0100
committerIain Merrick <husky@google.com>2010-08-23 11:05:40 +0100
commitf486d19d62f1bc33246748b14b14a9dfa617b57f (patch)
tree195485454c93125455a30e553a73981c3816144d /WebKitTools/Scripts/webkitpy/layout_tests/layout_package
parent6ba0b43722d16bc295606bec39f396f596e4fef1 (diff)
downloadexternal_webkit-f486d19d62f1bc33246748b14b14a9dfa617b57f.zip
external_webkit-f486d19d62f1bc33246748b14b14a9dfa617b57f.tar.gz
external_webkit-f486d19d62f1bc33246748b14b14a9dfa617b57f.tar.bz2
Merge WebKit at r65615 : Initial merge by git.
Change-Id: Ifbf384f4531e3b58475a662e38195c2d9152ae79
Diffstat (limited to 'WebKitTools/Scripts/webkitpy/layout_tests/layout_package')
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py90
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_layout_results_generator.py4
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py93
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing.py6
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations.py3
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_failures.py9
6 files changed, 135 insertions, 70 deletions
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 6364511..6343400 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
@@ -54,9 +54,9 @@ _log = logging.getLogger("webkitpy.layout_tests.layout_package."
"dump_render_tree_thread")
-def process_output(port, test_info, test_types, test_args, configuration,
- output_dir, crash, timeout, test_run_time, actual_checksum,
- output, error):
+def _process_output(port, test_info, test_types, test_args, configuration,
+ output_dir, crash, timeout, test_run_time, actual_checksum,
+ output, error):
"""Receives the output from a DumpRenderTree process, subjects it to a
number of tests, and returns a list of failure types the test produced.
@@ -118,6 +118,21 @@ def process_output(port, test_info, test_types, test_args, configuration,
total_time_for_all_diffs, time_for_diffs)
+def _pad_timeout(timeout):
+ """Returns a safe multiple of the per-test timeout value to use
+ to detect hung test threads.
+
+ """
+ # When we're running one test per DumpRenderTree process, we can
+ # enforce a hard timeout. The DumpRenderTree watchdog uses 2.5x
+ # the timeout; we want to be larger than that.
+ return timeout * 3
+
+
+def _milliseconds_to_seconds(msecs):
+ return float(msecs) / 1000.0
+
+
class TestResult(object):
def __init__(self, filename, failures, test_run_time,
@@ -162,7 +177,7 @@ class SingleTestThread(threading.Thread):
driver.run_test(test_info.uri.strip(), test_info.timeout,
test_info.image_hash())
end = time.time()
- self._test_result = process_output(self._port,
+ 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)
@@ -172,8 +187,42 @@ class SingleTestThread(threading.Thread):
return self._test_result
-class TestShellThread(threading.Thread):
+class WatchableThread(threading.Thread):
+ """This class abstracts an interface used by
+ run_webkit_tests.TestRunner._wait_for_threads_to_finish for thread
+ management."""
+ def __init__(self):
+ threading.Thread.__init__(self)
+ self._canceled = False
+ self._exception_info = None
+ self._next_timeout = None
+ self._thread_id = None
+
+ def cancel(self):
+ """Set a flag telling this thread to quit."""
+ self._canceled = True
+
+ def clear_next_timeout(self):
+ """Mark a flag telling this thread to stop setting timeouts."""
+ self._timeout = 0
+
+ def exception_info(self):
+ """If run() terminated on an uncaught exception, return it here
+ ((type, value, traceback) tuple).
+ Returns None if run() terminated normally. Meant to be called after
+ joining this thread."""
+ return self._exception_info
+
+ def id(self):
+ """Return a thread identifier."""
+ return self._thread_id
+
+ def next_timeout(self):
+ """Return the time the test is supposed to finish by."""
+ return self._next_timeout
+
+class TestShellThread(WatchableThread):
def __init__(self, port, filename_list_queue, result_queue,
test_types, test_args, image_path, shell_args, options):
"""Initialize all the local state for this DumpRenderTree thread.
@@ -192,7 +241,7 @@ class TestShellThread(threading.Thread):
command-line options should match those expected by
run_webkit_tests; they are typically passed via the
run_webkit_tests.TestRunner class."""
- threading.Thread.__init__(self)
+ WatchableThread.__init__(self)
self._port = port
self._filename_list_queue = filename_list_queue
self._result_queue = result_queue
@@ -203,8 +252,6 @@ class TestShellThread(threading.Thread):
self._image_path = image_path
self._shell_args = shell_args
self._options = options
- self._canceled = False
- self._exception_info = None
self._directory_timing_stats = {}
self._test_results = []
self._num_tests = 0
@@ -231,17 +278,6 @@ class TestShellThread(threading.Thread):
"""
return self._test_results
- def cancel(self):
- """Set a flag telling this thread to quit."""
- self._canceled = True
-
- def get_exception_info(self):
- """If run() terminated on an uncaught exception, return it here
- ((type, value, traceback) tuple).
- Returns None if run() terminated normally. Meant to be called after
- joining this thread."""
- return self._exception_info
-
def get_total_time(self):
return max(self._stop_time - self._start_time, 0.0)
@@ -251,6 +287,7 @@ class TestShellThread(threading.Thread):
def run(self):
"""Delegate main work to a helper method and watch for uncaught
exceptions."""
+ self._thread_id = thread.get_ident()
self._start_time = time.time()
self._num_tests = 0
try:
@@ -384,10 +421,10 @@ class TestShellThread(threading.Thread):
worker.start()
- # When we're running one test per DumpRenderTree process, we can
- # enforce a hard timeout. The DumpRenderTree watchdog uses 2.5x
- # the timeout; we want to be larger than that.
- worker.join(int(test_info.timeout) * 3.0 / 1000.0)
+ thread_timeout = _milliseconds_to_seconds(
+ _pad_timeout(test_info.timeout))
+ thread._next_timeout = time.time() + thread_timeout
+ worker.join(thread_timeout)
if worker.isAlive():
# If join() returned with the thread still running, the
# DumpRenderTree is completely hung and there's nothing
@@ -433,11 +470,16 @@ class TestShellThread(threading.Thread):
not self._options.pixel_tests)):
image_hash = ""
start = time.time()
+
+ thread_timeout = _milliseconds_to_seconds(
+ _pad_timeout(test_info.timeout))
+ self._next_timeout = start + thread_timeout
+
crash, timeout, actual_checksum, output, error = \
self._driver.run_test(test_info.uri, test_info.timeout, image_hash)
end = time.time()
- result = process_output(self._port, test_info, self._test_types,
+ result = _process_output(self._port, test_info, self._test_types,
self._test_args, self._options.configuration,
self._options.results_directory, crash,
timeout, end - start, actual_checksum,
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_layout_results_generator.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_layout_results_generator.py
index 6c36c93..c6c3066 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_layout_results_generator.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_layout_results_generator.py
@@ -57,7 +57,7 @@ class JSONLayoutResultsGenerator(json_results_generator.JSONResultsGeneratorBase
def __init__(self, port, builder_name, build_name, build_number,
results_file_base_path, builder_base_url,
test_timings, expectations, result_summary, all_tests,
- generate_incremental_results=False):
+ generate_incremental_results=False, test_results_server=None):
"""Modifies the results.json file. Grabs it off the archive directory
if it is not found locally.
@@ -68,7 +68,7 @@ class JSONLayoutResultsGenerator(json_results_generator.JSONResultsGeneratorBase
super(JSONLayoutResultsGenerator, self).__init__(
builder_name, build_name, build_number, results_file_base_path,
builder_base_url, {}, port.test_repository_paths(),
- generate_incremental_results)
+ generate_incremental_results, test_results_server)
self._port = port
self._expectations = expectations
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 e746bc0..15eceee 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
@@ -84,10 +84,14 @@ class JSONResultsGeneratorBase(object):
RESULTS_FILENAME = "results.json"
INCREMENTAL_RESULTS_FILENAME = "incremental_results.json"
+ URL_FOR_TEST_LIST_JSON = \
+ "http://%s/testfile?builder=%s&name=%s&testlistjson=1"
+
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):
+ generate_incremental_results=False,
+ test_results_server=None):
"""Modifies the results.json file. Grabs it off the archive directory
if it is not found locally.
@@ -103,6 +107,9 @@ class JSONResultsGeneratorBase(object):
svn_repositories: A (json_field_name, svn_path) pair for SVN
repositories that tests rely on. The SVN revision will be
included in the JSON with the given json_field_name.
+ generate_incremental_results: If true, generate incremental json file
+ from current run results.
+ test_results_server: server that hosts test results json.
"""
self._builder_name = builder_name
self._build_name = build_name
@@ -121,6 +128,8 @@ class JSONResultsGeneratorBase(object):
if not self._svn_repositories:
self._svn_repositories = {}
+ self._test_results_server = test_results_server
+
self._json = None
self._archived_results = None
@@ -144,25 +153,24 @@ class JSONResultsGeneratorBase(object):
def get_json(self, incremental=False):
"""Gets the results for the results.json file."""
- if incremental:
- results_json = {}
- else:
+ results_json = {}
+ if not incremental:
if self._json:
return self._json
- if not self._archived_results:
- self._archived_results, error = \
- self._get_archived_json_results()
- if error:
- # If there was an error don't write a results.json
- # file at all as it would lose all the information on the
- # bot.
- _log.error("Archive directory is inaccessible. Not "
- "modifying or clobbering the results.json "
- "file: " + str(error))
- return None
+ if self._archived_results:
+ results_json = self._archived_results
- results_json = self._archived_results
+ if not results_json:
+ results_json, error = self._get_archived_json_results(incremental)
+ if error:
+ # If there was an error don't write a results.json
+ # file at all as it would lose all the information on the
+ # bot.
+ _log.error("Archive directory is inaccessible. Not "
+ "modifying or clobbering the results.json "
+ "file: " + str(error))
+ return None
builder_name = self._builder_name
if results_json and builder_name not in results_json:
@@ -186,7 +194,7 @@ class JSONResultsGeneratorBase(object):
all_failing_tests = self._get_failed_test_names()
all_failing_tests.update(tests.iterkeys())
for test in all_failing_tests:
- self._insert_test_time_and_result(test, tests)
+ self._insert_test_time_and_result(test, tests, incremental)
return results_json
@@ -253,24 +261,40 @@ class JSONResultsGeneratorBase(object):
return ""
return ""
- def _get_archived_json_results(self):
+ def _get_archived_json_results(self, for_incremental=False):
"""Reads old results JSON file if it exists.
Returns (archived_results, error) tuple where error is None if results
were successfully read.
+
+ if for_incremental is True, download JSON file that only contains test
+ name list from test-results server. This is for generating incremental
+ JSON so the file generated has info for tests that failed before but
+ pass or are skipped from current run.
"""
results_json = {}
old_results = None
error = None
- if os.path.exists(self._results_file_path):
+ if os.path.exists(self._results_file_path) and not for_incremental:
with codecs.open(self._results_file_path, "r", "utf-8") as file:
old_results = file.read()
- elif self._builder_base_url:
- # Check if we have the archived JSON file on the buildbot server.
- results_file_url = (self._builder_base_url +
- self._build_name + "/" + self.RESULTS_FILENAME)
- _log.error("Local results.json file does not exist. Grabbing "
- "it off the archive at " + results_file_url)
+ elif self._builder_base_url or for_incremental:
+ if for_incremental:
+ if not self._test_results_server:
+ # starting from fresh if no test results server specified.
+ return {}, None
+
+ results_file_url = (self.URL_FOR_TEST_LIST_JSON %
+ (urllib2.quote(self._test_results_server),
+ urllib2.quote(self._builder_name),
+ self.RESULTS_FILENAME))
+ else:
+ # Check if we have the archived JSON file on the buildbot
+ # server.
+ results_file_url = (self._builder_base_url +
+ self._build_name + "/" + self.RESULTS_FILENAME)
+ _log.error("Local results.json file does not exist. Grabbing "
+ "it off the archive at " + results_file_url)
try:
results_file = urllib2.urlopen(results_file_url)
@@ -387,7 +411,7 @@ class JSONResultsGeneratorBase(object):
int(time.time()),
self.TIME)
- def _insert_test_time_and_result(self, test_name, tests):
+ def _insert_test_time_and_result(self, test_name, tests, incremental=False):
""" Insert a test item with its results to the given tests dictionary.
Args:
@@ -401,9 +425,20 @@ class JSONResultsGeneratorBase(object):
tests[test_name] = self._create_results_and_times_json()
thisTest = tests[test_name]
- self._insert_item_run_length_encoded(result, thisTest[self.RESULTS])
- self._insert_item_run_length_encoded(time, thisTest[self.TIMES])
- self._normalize_results_json(thisTest, test_name, tests)
+ if self.RESULTS in thisTest:
+ self._insert_item_run_length_encoded(result, thisTest[self.RESULTS])
+ else:
+ thisTest[self.RESULTS] = [[1, result]]
+
+ if self.TIMES in thisTest:
+ self._insert_item_run_length_encoded(time, thisTest[self.TIMES])
+ else:
+ thisTest[self.TIMES] = [[1, time]]
+
+ # Don't normalize the incremental results json because we need results
+ # for tests that pass or have no data from current run.
+ if not incremental:
+ self._normalize_results_json(thisTest, test_name, tests)
def _convert_json_to_current_version(self, results_json):
"""If the JSON does not match the current version, converts it to the
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing.py
index f838a7b..81cdc9b 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing.py
@@ -123,12 +123,6 @@ def print_options():
help="show detailed help on controlling print output"),
optparse.make_option("-v", "--verbose", action="store_true",
default=False, help="include debug-level logging"),
-
- # FIXME: we should remove this; it's pretty much obsolete with the
- # --print trace-everything option.
- optparse.make_option("--sources", action="store_true",
- help=("show expected result file path for each test "
- "(implies --verbose)")),
]
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations.py
index 38223dd..e154932 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations.py
@@ -460,6 +460,9 @@ class TestExpectationsFile:
return ExpectationsJsonEncoder(separators=(',', ':')).encode(
self._all_expectations)
+ def get_non_fatal_errors(self):
+ return self._non_fatal_errors
+
def contains(self, test):
return test in self._test_to_expectations
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_failures.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_failures.py
index 60bdbca..3be9240 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_failures.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_failures.py
@@ -250,15 +250,6 @@ class FailureImageHashMismatch(FailureWithType):
return "Image mismatch"
-class FailureFuzzyFailure(FailureWithType):
- """Image hashes didn't match."""
- OUT_FILENAMES = ["-actual.png", "-expected.png"]
-
- @staticmethod
- def message():
- return "Fuzzy image match also failed"
-
-
class FailureImageHashIncorrect(FailureWithType):
"""Actual result hash is incorrect."""
# Chrome doesn't know to display a .checksum file as text, so don't bother