Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (C) 2010 Google Inc. All rights reserved. | 1 # Copyright (C) 2010 Google Inc. All rights reserved. |
| 2 # Copyright (C) 2010 Gabor Rapcsanyi (rgabor@inf.u-szeged.hu), University of Sze ged | 2 # Copyright (C) 2010 Gabor Rapcsanyi (rgabor@inf.u-szeged.hu), University of Sze ged |
| 3 # | 3 # |
| 4 # Redistribution and use in source and binary forms, with or without | 4 # Redistribution and use in source and binary forms, with or without |
| 5 # modification, are permitted provided that the following conditions are | 5 # modification, are permitted provided that the following conditions are |
| 6 # met: | 6 # met: |
| 7 # | 7 # |
| 8 # * Redistributions of source code must retain the above copyright | 8 # * Redistributions of source code must retain the above copyright |
| 9 # notice, this list of conditions and the following disclaimer. | 9 # notice, this list of conditions and the following disclaimer. |
| 10 # * Redistributions in binary form must reproduce the above | 10 # * Redistributions in binary form must reproduce the above |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 32 This includes finding tests to run, reading the test expectations, | 32 This includes finding tests to run, reading the test expectations, |
| 33 starting the required helper servers, deciding the order and way to | 33 starting the required helper servers, deciding the order and way to |
| 34 run the tests, retrying fails tests and collecting the test results, | 34 run the tests, retrying fails tests and collecting the test results, |
| 35 including crash logs, and mismatches with expectations. | 35 including crash logs, and mismatches with expectations. |
| 36 | 36 |
| 37 The Manager object has a constructor and one main method called run. | 37 The Manager object has a constructor and one main method called run. |
| 38 """ | 38 """ |
| 39 | 39 |
| 40 import json | 40 import json |
| 41 import logging | 41 import logging |
| 42 import os | |
| 43 import platform | |
| 42 import random | 44 import random |
| 43 import re | 45 import re |
| 44 import sys | 46 import sys |
| 45 import time | 47 import time |
| 48 import types | |
| 46 | 49 |
| 47 from webkitpy.common.net.file_uploader import FileUploader | 50 from webkitpy.common.net.file_uploader import FileUploader |
| 48 from webkitpy.layout_tests.controllers.layout_test_finder import LayoutTestFinde r | 51 from webkitpy.layout_tests.controllers.layout_test_finder import LayoutTestFinde r |
| 49 from webkitpy.layout_tests.controllers.layout_test_runner import LayoutTestRunne r | 52 from webkitpy.layout_tests.controllers.layout_test_runner import LayoutTestRunne r |
| 50 from webkitpy.layout_tests.controllers.test_result_writer import TestResultWrite r | 53 from webkitpy.layout_tests.controllers.test_result_writer import TestResultWrite r |
| 51 from webkitpy.layout_tests.layout_package import json_results_generator | 54 from webkitpy.layout_tests.layout_package import json_results_generator |
| 52 from webkitpy.layout_tests.models import test_expectations | 55 from webkitpy.layout_tests.models import test_expectations |
| 53 from webkitpy.layout_tests.models import test_failures | 56 from webkitpy.layout_tests.models import test_failures |
| 54 from webkitpy.layout_tests.models import test_run_results | 57 from webkitpy.layout_tests.models import test_run_results |
| 55 from webkitpy.layout_tests.models.test_input import TestInput | 58 from webkitpy.layout_tests.models.test_input import TestInput |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 70 """Initialize test runner data structures. | 73 """Initialize test runner data structures. |
| 71 | 74 |
| 72 Args: | 75 Args: |
| 73 port: An object implementing platform-specific functionality. | 76 port: An object implementing platform-specific functionality. |
| 74 options: An options argument which contains command line options. | 77 options: An options argument which contains command line options. |
| 75 printer: A Printer object to record updates to. | 78 printer: A Printer object to record updates to. |
| 76 """ | 79 """ |
| 77 self._port = port | 80 self._port = port |
| 78 self._filesystem = port.host.filesystem | 81 self._filesystem = port.host.filesystem |
| 79 self._options = options | 82 self._options = options |
| 83 self._args = [] | |
| 80 self._printer = printer | 84 self._printer = printer |
| 81 self._expectations = None | 85 self._expectations = None |
| 82 | 86 |
| 83 self.HTTP_SUBDIR = 'http' + port.TEST_PATH_SEPARATOR | 87 self.HTTP_SUBDIR = 'http' + port.TEST_PATH_SEPARATOR |
| 84 self.INSPECTOR_SUBDIR = 'inspector' + port.TEST_PATH_SEPARATOR | 88 self.INSPECTOR_SUBDIR = 'inspector' + port.TEST_PATH_SEPARATOR |
| 85 self.PERF_SUBDIR = 'perf' | 89 self.PERF_SUBDIR = 'perf' |
| 86 self.WEBSOCKET_SUBDIR = 'websocket' + port.TEST_PATH_SEPARATOR | 90 self.WEBSOCKET_SUBDIR = 'websocket' + port.TEST_PATH_SEPARATOR |
| 87 self.LAYOUT_TESTS_DIRECTORY = 'LayoutTests' | 91 self.LAYOUT_TESTS_DIRECTORY = 'LayoutTests' |
| 88 self.ARCHIVED_RESULTS_LIMIT = 25 | 92 self.ARCHIVED_RESULTS_LIMIT = 25 |
| 89 self._http_server_started = False | 93 self._http_server_started = False |
| 90 self._wptserve_started = False | 94 self._wptserve_started = False |
| 91 self._websockets_server_started = False | 95 self._websockets_server_started = False |
| 92 | 96 |
| 93 self._random_seed = None | 97 self._random_seed = None |
| 94 self._results_directory = self._port.results_directory() | 98 self._results_directory = self._port.results_directory() |
| 95 self._finder = LayoutTestFinder(self._port, self._options) | 99 self._finder = LayoutTestFinder(self._port, self._options) |
| 96 self._runner = LayoutTestRunner(self._options, self._port, self._printer , self._results_directory, self._test_is_slow) | 100 self._runner = LayoutTestRunner(self._options, self._port, self._printer , self._results_directory, self._test_is_slow) |
| 97 | 101 |
| 102 def _env(self): | |
| 103 env = { | |
| 104 'args': self._args, | |
| 105 # os.environ needs to be converted to a dict otherwise it can't be | |
| 106 # json serialized | |
| 107 'env': dict(os.environ), | |
|
Dirk Pranke
2016/06/21 21:57:54
The args and environ are dumped to stderr as part
mithro
2016/06/22 08:42:24
Does the logging output get saved with the test re
Dirk Pranke
2016/06/23 23:57:26
Well, the logging output is on the builder alongsi
| |
| 108 # self._options also isn't a dictionary object, so convert it too. | |
| 109 'options': self._options.__dict__, | |
|
chrishall
2016/08/08 04:32:57
Minor nitpick:
above to convert environ we used `
| |
| 110 'platform': {}, | |
| 111 } | |
| 112 | |
| 113 # Capture the information python knows | |
| 114 for func in dir(platform): | |
|
Dirk Pranke
2016/06/21 21:57:54
Is platform actually defined somewhere? I'm also a
mithro
2016/06/22 08:42:24
platform is a standard library module - https://do
Dirk Pranke
2016/06/23 23:57:26
I was actually referring to the symbol itself. For
chrishall
2016/08/08 04:32:57
If our goal is to produce an easy-to-consume JSON
| |
| 115 f = getattr(platform, func) | |
| 116 if func.startswith("_") or func in ("popen",) or isinstance(f, types .ModuleType): | |
| 117 continue | |
| 118 if callable(f): | |
| 119 try: | |
| 120 env['platform'][func] = f() | |
| 121 except Exception: | |
| 122 pass | |
| 123 else: | |
| 124 env['platform'][func] = f | |
| 125 | |
| 126 if self._random_seed: | |
| 127 env['random-seed'] = self._random_seed | |
| 128 | |
| 129 return env | |
| 130 | |
| 98 def run(self, args): | 131 def run(self, args): |
| 99 """Run the tests and return a RunDetails object with the results.""" | 132 """Run the tests and return a RunDetails object with the results.""" |
| 100 start_time = time.time() | 133 start_time = time.time() |
| 101 self._printer.write_update("Collecting tests ...") | 134 self._printer.write_update("Collecting tests ...") |
| 102 running_all_tests = False | 135 running_all_tests = False |
| 103 try: | 136 try: |
| 137 self._args = args | |
| 104 paths, test_names, running_all_tests = self._collect_tests(args) | 138 paths, test_names, running_all_tests = self._collect_tests(args) |
| 105 except IOError: | 139 except IOError: |
| 106 # This is raised if --test-list doesn't exist | 140 # This is raised if --test-list doesn't exist |
| 107 return test_run_results.RunDetails(exit_code=test_run_results.NO_TES TS_EXIT_STATUS) | 141 return test_run_results.RunDetails(exit_code=test_run_results.NO_TES TS_EXIT_STATUS) |
| 108 | 142 |
| 109 self._printer.write_update("Parsing expectations ...") | 143 self._printer.write_update("Parsing expectations ...") |
| 110 self._expectations = test_expectations.TestExpectations(self._port, test _names) | 144 self._expectations = test_expectations.TestExpectations(self._port, test _names) |
| 111 | 145 |
| 112 tests_to_run, tests_to_skip = self._prepare_lists(paths, test_names) | 146 tests_to_run, tests_to_skip = self._prepare_lists(paths, test_names) |
| 113 self._printer.print_found(len(test_names), len(tests_to_run), self._opti ons.repeat_each, self._options.iterations) | 147 self._printer.print_found(len(test_names), len(tests_to_run), self._opti ons.repeat_each, self._options.iterations) |
| (...skipping 382 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 496 | 530 |
| 497 def _tests_to_retry(self, run_results): | 531 def _tests_to_retry(self, run_results): |
| 498 # TODO(ojan): This should also check that result.type != test_expectatio ns.MISSING since retrying missing expectations is silly. | 532 # TODO(ojan): This should also check that result.type != test_expectatio ns.MISSING since retrying missing expectations is silly. |
| 499 # But that's a bit tricky since we only consider the last retry attempt for the count of unexpected regressions. | 533 # But that's a bit tricky since we only consider the last retry attempt for the count of unexpected regressions. |
| 500 return [result.test_name for result in run_results.unexpected_results_by _name.values( | 534 return [result.test_name for result in run_results.unexpected_results_by _name.values( |
| 501 ) if result.type != test_expectations.PASS] | 535 ) if result.type != test_expectations.PASS] |
| 502 | 536 |
| 503 def _write_json_files(self, summarized_full_results, summarized_failing_resu lts, initial_results, running_all_tests): | 537 def _write_json_files(self, summarized_full_results, summarized_failing_resu lts, initial_results, running_all_tests): |
| 504 _log.debug("Writing JSON files in %s." % self._results_directory) | 538 _log.debug("Writing JSON files in %s." % self._results_directory) |
| 505 | 539 |
| 540 # Write the environment the test ran under into env.json file | |
| 541 env_json_path = self._filesystem.join(self._results_directory, "env.json ") | |
| 542 json_results_generator.write_json(self._filesystem, self._env(), env_jso n_path) | |
|
Dirk Pranke
2016/06/23 23:57:26
see comment above about just using the existing js
| |
| 543 | |
| 506 # FIXME: Upload stats.json to the server and delete times_ms. | 544 # FIXME: Upload stats.json to the server and delete times_ms. |
| 507 times_trie = json_results_generator.test_timings_trie(initial_results.re sults_by_name.values()) | 545 times_trie = json_results_generator.test_timings_trie(initial_results.re sults_by_name.values()) |
| 508 times_json_path = self._filesystem.join(self._results_directory, "times_ ms.json") | 546 times_json_path = self._filesystem.join(self._results_directory, "times_ ms.json") |
| 509 json_results_generator.write_json(self._filesystem, times_trie, times_js on_path) | 547 json_results_generator.write_json(self._filesystem, times_trie, times_js on_path) |
| 510 | 548 |
| 511 # Save out the times data so we can use it for --fastest in the future. | 549 # Save out the times data so we can use it for --fastest in the future. |
| 512 if running_all_tests: | 550 if running_all_tests: |
| 513 bot_test_times_path = self._port.bot_test_times_path() | 551 bot_test_times_path = self._port.bot_test_times_path() |
| 514 self._filesystem.maybe_make_directory(self._filesystem.dirname(bot_t est_times_path)) | 552 self._filesystem.maybe_make_directory(self._filesystem.dirname(bot_t est_times_path)) |
| 515 json_results_generator.write_json(self._filesystem, times_trie, bot_ test_times_path) | 553 json_results_generator.write_json(self._filesystem, times_trie, bot_ test_times_path) |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 577 | 615 |
| 578 stats = {} | 616 stats = {} |
| 579 for result in initial_results.results_by_name.values(): | 617 for result in initial_results.results_by_name.values(): |
| 580 if result.type != test_expectations.SKIP: | 618 if result.type != test_expectations.SKIP: |
| 581 stats[result.test_name] = {'results': (_worker_number(result.wor ker_name), result.test_number, result.pid, int( | 619 stats[result.test_name] = {'results': (_worker_number(result.wor ker_name), result.test_number, result.pid, int( |
| 582 result.test_run_time * 1000), int(result.total_run_time * 10 00))} | 620 result.test_run_time * 1000), int(result.total_run_time * 10 00))} |
| 583 stats_trie = {} | 621 stats_trie = {} |
| 584 for name, value in stats.iteritems(): | 622 for name, value in stats.iteritems(): |
| 585 json_results_generator.add_path_to_trie(name, value, stats_trie) | 623 json_results_generator.add_path_to_trie(name, value, stats_trie) |
| 586 return stats_trie | 624 return stats_trie |
| OLD | NEW |