Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(325)

Side by Side Diff: third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py

Issue 2308283002: Allow seeding the random layout test order and write out seed. (Closed)
Patch Set: Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
82 self.HTTP_SUBDIR = 'http' + port.TEST_PATH_SEPARATOR 82 self.HTTP_SUBDIR = 'http' + port.TEST_PATH_SEPARATOR
83 self.INSPECTOR_SUBDIR = 'inspector' + port.TEST_PATH_SEPARATOR 83 self.INSPECTOR_SUBDIR = 'inspector' + port.TEST_PATH_SEPARATOR
84 self.PERF_SUBDIR = 'perf' 84 self.PERF_SUBDIR = 'perf'
85 self.WEBSOCKET_SUBDIR = 'websocket' + port.TEST_PATH_SEPARATOR 85 self.WEBSOCKET_SUBDIR = 'websocket' + port.TEST_PATH_SEPARATOR
86 self.LAYOUT_TESTS_DIRECTORY = 'LayoutTests' 86 self.LAYOUT_TESTS_DIRECTORY = 'LayoutTests'
87 self.ARCHIVED_RESULTS_LIMIT = 25 87 self.ARCHIVED_RESULTS_LIMIT = 25
88 self._http_server_started = False 88 self._http_server_started = False
89 self._wptserve_started = False 89 self._wptserve_started = False
90 self._websockets_server_started = False 90 self._websockets_server_started = False
91 91
92 self._random_seed = None
92 self._results_directory = self._port.results_directory() 93 self._results_directory = self._port.results_directory()
93 self._finder = LayoutTestFinder(self._port, self._options) 94 self._finder = LayoutTestFinder(self._port, self._options)
94 self._runner = LayoutTestRunner(self._options, self._port, self._printer , self._results_directory, self._test_is_slow) 95 self._runner = LayoutTestRunner(self._options, self._port, self._printer , self._results_directory, self._test_is_slow)
95 96
96 def run(self, args): 97 def run(self, args):
97 """Run the tests and return a RunDetails object with the results.""" 98 """Run the tests and return a RunDetails object with the results."""
98 start_time = time.time() 99 start_time = time.time()
99 self._printer.write_update("Collecting tests ...") 100 self._printer.write_update("Collecting tests ...")
100 running_all_tests = False 101 running_all_tests = False
101 try: 102 try:
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
185 enabled_pixel_tests_in_retry, only_include_failing=True) 186 enabled_pixel_tests_in_retry, only_include_failing=True)
186 187
187 exit_code = summarized_failing_results['num_regressions'] 188 exit_code = summarized_failing_results['num_regressions']
188 if exit_code > test_run_results.MAX_FAILURES_EXIT_STATUS: 189 if exit_code > test_run_results.MAX_FAILURES_EXIT_STATUS:
189 _log.warning('num regressions (%d) exceeds max exit status (%d)', 190 _log.warning('num regressions (%d) exceeds max exit status (%d)',
190 exit_code, test_run_results.MAX_FAILURES_EXIT_STATUS) 191 exit_code, test_run_results.MAX_FAILURES_EXIT_STATUS)
191 exit_code = test_run_results.MAX_FAILURES_EXIT_STATUS 192 exit_code = test_run_results.MAX_FAILURES_EXIT_STATUS
192 193
193 if not self._options.dry_run: 194 if not self._options.dry_run:
194 self._write_json_files(summarized_full_results, summarized_failing_r esults, initial_results, running_all_tests) 195 self._write_json_files(summarized_full_results, summarized_failing_r esults, initial_results, running_all_tests)
196 if self._random_seed:
197 self._write_random_seed()
195 198
196 if self._options.write_full_results_to: 199 if self._options.write_full_results_to:
197 self._filesystem.copyfile(self._filesystem.join(self._results_di rectory, "full_results.json"), 200 self._filesystem.copyfile(self._filesystem.join(self._results_di rectory, "full_results.json"),
198 self._options.write_full_results_to) 201 self._options.write_full_results_to)
199 202
200 self._upload_json_files() 203 self._upload_json_files()
201 204
202 results_path = self._filesystem.join(self._results_directory, "resul ts.html") 205 results_path = self._filesystem.join(self._results_directory, "resul ts.html")
203 self._copy_results_html_file(results_path) 206 self._copy_results_html_file(results_path)
204 if initial_results.keyboard_interrupted: 207 if initial_results.keyboard_interrupted:
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
246 tests_to_run = [test for test in test_names if test not in tests_to_skip ] 249 tests_to_run = [test for test in test_names if test not in tests_to_skip ]
247 250
248 if not tests_to_run: 251 if not tests_to_run:
249 return tests_to_run, tests_to_skip 252 return tests_to_run, tests_to_skip
250 253
251 # Create a sorted list of test files so the subset chunk, 254 # Create a sorted list of test files so the subset chunk,
252 # if used, contains alphabetically consecutive tests. 255 # if used, contains alphabetically consecutive tests.
253 if self._options.order == 'natural': 256 if self._options.order == 'natural':
254 tests_to_run.sort(key=self._port.test_key) 257 tests_to_run.sort(key=self._port.test_key)
255 elif self._options.order == 'random': 258 elif self._options.order == 'random':
256 random.shuffle(tests_to_run) 259 if self._options.seed is not None:
257 elif self._options.order == 'random-seeded': 260 self._random_seed = self._options.seed
258 rnd = random.Random() 261 else:
259 rnd.seed(4) # http://xkcd.com/221/ 262 self._random_seed = int(time.time())
ojan 2016/09/06 16:53:29 I think we should do a fixed value. Using current
qyearsley 2016/09/06 17:22:48 If the seed is output with the results, it should
jeffcarp 2016/09/06 17:22:51 Is that still true if we log the seed in results.j
ojan 2016/09/06 21:27:11 Think of this from the perspective of a sheriff tr
jeffcarp 2016/09/06 22:57:30 If this would make it hard to tell whether a faili
ojan 2016/09/06 23:07:43 I think this is a good idea. You probably want it
260 rnd.shuffle(tests_to_run) 263 _log.info("Test random order seed: %d", self._random_seed)
264 tests_to_run.sort()
265 rand = random.Random()
266 rand.seed(self._random_seed)
267 rand.shuffle(tests_to_run)
261 268
262 tests_to_run, tests_in_other_chunks = self._finder.split_into_chunks(tes ts_to_run) 269 tests_to_run, tests_in_other_chunks = self._finder.split_into_chunks(tes ts_to_run)
263 self._expectations.add_extra_skipped_tests(tests_in_other_chunks) 270 self._expectations.add_extra_skipped_tests(tests_in_other_chunks)
264 tests_to_skip.update(tests_in_other_chunks) 271 tests_to_skip.update(tests_in_other_chunks)
265 272
266 return tests_to_run, tests_to_skip 273 return tests_to_run, tests_to_skip
267 274
275 def _write_random_seed(self):
276 """Writes the random seed used for randomized test order."""
277 assert self._options.order == 'random' and self._random_seed
278 path = self._filesystem.join(self._results_directory, 'random-seed.txt')
279 contents = '%d\n' % self._random_seed
280 _log.info('%s %s', path, contents)
281 self._filesystem.write_text_file(path, contents)
qyearsley 2016/09/05 00:15:28 We could also potentially: - Not write the seed t
Dirk Pranke 2016/09/06 01:19:57 I would include the seed as a field in the results
mithro 2016/09/06 02:02:25 I'm happy with Dirk's suggestions. The aim is to
qyearsley 2016/09/06 17:22:48 Aye, SGTM
282
268 def _test_input_for_file(self, test_file): 283 def _test_input_for_file(self, test_file):
269 return TestInput(test_file, 284 return TestInput(test_file,
270 self._options.slow_time_out_ms if self._test_is_slow(te st_file) else self._options.time_out_ms, 285 self._options.slow_time_out_ms if self._test_is_slow(te st_file) else self._options.time_out_ms,
271 self._test_requires_lock(test_file), 286 self._test_requires_lock(test_file),
272 should_add_missing_baselines=(self._options.new_test_re sults and not self._test_is_expected_missing(test_file))) 287 should_add_missing_baselines=(self._options.new_test_re sults and not self._test_is_expected_missing(test_file)))
273 288
274 def _test_requires_lock(self, test_file): 289 def _test_requires_lock(self, test_file):
275 """Return True if the test needs to be locked when running multiple 290 """Return True if the test needs to be locked when running multiple
276 instances of this test runner. 291 instances of this test runner.
277 292
(...skipping 275 matching lines...) Expand 10 before | Expand all | Expand 10 after
553 568
554 stats = {} 569 stats = {}
555 for result in initial_results.results_by_name.values(): 570 for result in initial_results.results_by_name.values():
556 if result.type != test_expectations.SKIP: 571 if result.type != test_expectations.SKIP:
557 stats[result.test_name] = {'results': (_worker_number(result.wor ker_name), result.test_number, result.pid, int( 572 stats[result.test_name] = {'results': (_worker_number(result.wor ker_name), result.test_number, result.pid, int(
558 result.test_run_time * 1000), int(result.total_run_time * 10 00))} 573 result.test_run_time * 1000), int(result.total_run_time * 10 00))}
559 stats_trie = {} 574 stats_trie = {}
560 for name, value in stats.iteritems(): 575 for name, value in stats.iteritems():
561 json_results_generator.add_path_to_trie(name, value, stats_trie) 576 json_results_generator.add_path_to_trie(name, value, stats_trie)
562 return stats_trie 577 return stats_trie
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698