diff options
author | Iain Merrick <husky@google.com> | 2010-08-19 17:55:56 +0100 |
---|---|---|
committer | Iain Merrick <husky@google.com> | 2010-08-23 11:05:40 +0100 |
commit | f486d19d62f1bc33246748b14b14a9dfa617b57f (patch) | |
tree | 195485454c93125455a30e553a73981c3816144d /WebKitTools/Scripts/webkitpy/layout_tests/layout_package | |
parent | 6ba0b43722d16bc295606bec39f396f596e4fef1 (diff) | |
download | external_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')
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 |