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

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

Issue 2381093003: Add switch to update-test-expectations to show removed lines in dashboard (Closed)
Patch Set: Addressed feedback Created 4 years, 2 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
« no previous file with comments | « no previous file | third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/update_test_expectations_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2016 The Chromium Authors. All rights reserved. 1 # Copyright 2016 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Updates TestExpectations based on results in builder bots. 5 """Updates TestExpectations based on results in builder bots.
6 6
7 Scans the TestExpectations file and uses results from actual builder bots runs 7 Scans the TestExpectations file and uses results from actual builder bots runs
8 to remove tests that are marked as flaky but don't fail in the specified way. 8 to remove tests that are marked as flaky but don't fail in the specified way.
9 9
10 E.g. If a test has this expectation: 10 E.g. If a test has this expectation:
11 bug(test) fast/test.html [ Failure Pass ] 11 bug(test) fast/test.html [ Failure Pass ]
12 12
13 And all the runs on builders have passed the line will be removed. 13 And all the runs on builders have passed the line will be removed.
14 14
15 Additionally, the runs don't all have to be Passing to remove the line; 15 Additionally, the runs don't all have to be Passing to remove the line;
16 as long as the non-Passing results are of a type not specified in the 16 as long as the non-Passing results are of a type not specified in the
17 expectation this line will be removed. For example, if this is the 17 expectation this line will be removed. For example, if this is the
18 expectation: 18 expectation:
19 19
20 bug(test) fast/test.html [ Crash Pass ] 20 bug(test) fast/test.html [ Crash Pass ]
21 21
22 But the results on the builders show only Passes and Timeouts, the line 22 But the results on the builders show only Passes and Timeouts, the line
23 will be removed since there's no Crash results. 23 will be removed since there's no Crash results.
24 """ 24 """
25 25
26 import argparse 26 import argparse
27 import logging 27 import logging
28 import webbrowser
28 29
29 from webkitpy.layout_tests.models.test_expectations import TestExpectations 30 from webkitpy.layout_tests.models.test_expectations import TestExpectations
31 from webkitpy.tool.commands.flaky_tests import FlakyTests
30 32
31 _log = logging.getLogger(__name__) 33 _log = logging.getLogger(__name__)
32 34
33 35
34 def main(host, bot_test_expectations_factory, argv): 36 def main(host, bot_test_expectations_factory, argv):
35 parser = argparse.ArgumentParser(epilog=__doc__, formatter_class=argparse.Ra wTextHelpFormatter) 37 parser = argparse.ArgumentParser(epilog=__doc__, formatter_class=argparse.Ra wTextHelpFormatter)
36 parser.add_argument('--verbose', '-v', action='store_true', default=False, h elp='enable more verbose logging') 38 parser.add_argument('--verbose', '-v', action='store_true', default=False, h elp='enable more verbose logging')
39 parser.add_argument('--show-results',
40 '-s',
41 action='store_true',
42 default=False,
43 help='Open results dashboard for all removed lines')
37 args = parser.parse_args(argv) 44 args = parser.parse_args(argv)
38 45
39 logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO, f ormat="%(levelname)s: %(message)s") 46 logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO, f ormat="%(levelname)s: %(message)s")
40 47
41 port = host.port_factory.get() 48 port = host.port_factory.get()
42 expectations_file = port.path_to_generic_test_expectations_file() 49 expectations_file = port.path_to_generic_test_expectations_file()
43 if not host.filesystem.isfile(expectations_file): 50 if not host.filesystem.isfile(expectations_file):
44 _log.warning("Didn't find generic expectations file at: " + expectations _file) 51 _log.warning("Didn't find generic expectations file at: " + expectations _file)
45 return 1 52 return 1
46 53
47 remove_flakes_o_matic = RemoveFlakesOMatic(host, 54 remove_flakes_o_matic = RemoveFlakesOMatic(host,
48 port, 55 port,
49 bot_test_expectations_factory) 56 bot_test_expectations_factory,
57 webbrowser)
50 58
51 test_expectations = remove_flakes_o_matic.get_updated_test_expectations() 59 test_expectations = remove_flakes_o_matic.get_updated_test_expectations()
52 60
61 if args.show_results:
62 remove_flakes_o_matic.show_removed_results()
63
53 remove_flakes_o_matic.write_test_expectations(test_expectations, 64 remove_flakes_o_matic.write_test_expectations(test_expectations,
54 expectations_file) 65 expectations_file)
55 return 0 66 return 0
56 67
57 68
58 class RemoveFlakesOMatic(object): 69 class RemoveFlakesOMatic(object):
59 70
60 def __init__(self, host, port, bot_test_expectations_factory): 71 def __init__(self, host, port, bot_test_expectations_factory, browser):
61 self._host = host 72 self._host = host
62 self._port = port 73 self._port = port
63 self._expectations_factory = bot_test_expectations_factory 74 self._expectations_factory = bot_test_expectations_factory
64 self._builder_results_by_path = {} 75 self._builder_results_by_path = {}
76 self._browser = browser
77 self._expectations_to_remove_list = None
65 78
66 def _can_delete_line(self, test_expectation_line): 79 def _can_delete_line(self, test_expectation_line):
67 """Returns whether a given line in the expectations can be removed. 80 """Returns whether a given line in the expectations can be removed.
68 81
69 Uses results from builder bots to determine if a given line is stale and 82 Uses results from builder bots to determine if a given line is stale and
70 can safely be removed from the TestExpectations file. (i.e. remove if 83 can safely be removed from the TestExpectations file. (i.e. remove if
71 the bots show that it's not flaky.) There are also some rules about when 84 the bots show that it's not flaky.) There are also some rules about when
72 not to remove lines (e.g. never remove lines with Rebaseline 85 not to remove lines (e.g. never remove lines with Rebaseline
73 expectations, don't remove non-flaky expectations, etc.) 86 expectations, don't remove non-flaky expectations, etc.)
74 87
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after
248 # Remove all comments above the removed line. 261 # Remove all comments above the removed line.
249 while removed_index > 0 and expectations[removed_index - 1].is_comment() : 262 while removed_index > 0 and expectations[removed_index - 1].is_comment() :
250 removed_index -= 1 263 removed_index -= 1
251 expectations.pop(removed_index) 264 expectations.pop(removed_index)
252 265
253 # Remove all whitespace above the comments. 266 # Remove all whitespace above the comments.
254 while removed_index > 0 and expectations[removed_index - 1].is_whitespac e(): 267 while removed_index > 0 and expectations[removed_index - 1].is_whitespac e():
255 removed_index -= 1 268 removed_index -= 1
256 expectations.pop(removed_index) 269 expectations.pop(removed_index)
257 270
271 def _expectations_to_remove(self):
272 """Computes and returns the expectation lines that should be removed.
273
274 returns a list of TestExpectationLines that can be removed from the test expectations
275 file. The result is memoized so that subsequent calls will not recompute the result.
276 """
277 if self._expectations_to_remove_list is not None:
278 return self._expectations_to_remove_list
279
280 self._builder_results_by_path = self._get_builder_results_by_path()
281 self._expectations_to_remove_list = []
282 test_expectations = TestExpectations(self._port, include_overrides=False ).expectations()
283
284 for expectation in test_expectations:
285 if self._can_delete_line(expectation):
286 self._expectations_to_remove_list.append(expectation)
287
288 return self._expectations_to_remove_list
289
258 def get_updated_test_expectations(self): 290 def get_updated_test_expectations(self):
259 """Filters out passing lines from TestExpectations file. 291 """Filters out passing lines from TestExpectations file.
260 292
261 Reads the current TestExpectations file and, using results from the 293 Reads the current TestExpectations file and, using results from the
262 build bots, removes lines that are passing. That is, removes lines that 294 build bots, removes lines that are passing. That is, removes lines that
263 were not needed to keep the bots green. 295 were not needed to keep the bots green.
264 296
265 Returns: 297 Returns:
266 A TestExpectations object with the passing lines filtered out. 298 A TestExpectations object with the passing lines filtered out.
267 """ 299 """
268 300
269 self._builder_results_by_path = self._get_builder_results_by_path()
270
271 expectations_to_remove = []
272 test_expectations = TestExpectations(self._port, include_overrides=False ).expectations() 301 test_expectations = TestExpectations(self._port, include_overrides=False ).expectations()
273 302 for expectation in self._expectations_to_remove():
274 for expectation in test_expectations:
275 if self._can_delete_line(expectation):
276 expectations_to_remove.append(expectation)
277
278 for expectation in expectations_to_remove:
279 index = test_expectations.index(expectation) 303 index = test_expectations.index(expectation)
280 test_expectations.remove(expectation) 304 test_expectations.remove(expectation)
281 305
282 # Remove associated comments and whitespace if we've removed the las t expectation under 306 # Remove associated comments and whitespace if we've removed the las t expectation under
283 # a comment block. Only remove a comment block if it's not separated from the test 307 # a comment block. Only remove a comment block if it's not separated from the test
284 # expectation line by whitespace. 308 # expectation line by whitespace.
285 self._remove_associated_comments_and_whitespace(test_expectations, i ndex) 309 self._remove_associated_comments_and_whitespace(test_expectations, i ndex)
286 310
287 return test_expectations 311 return test_expectations
288 312
313 def show_removed_results(self):
314 """Opens removed lines in the results dashboard.
315
316 Opens the results dashboard in the browser, showing all the tests for li nes that the script
317 removed from the TestExpectations file and allowing the user to manually confirm the
318 results.
319 """
320 removed_test_names = ','.join(x.name for x in self._expectations_to_remo ve())
321 url = FlakyTests.FLAKINESS_DASHBOARD_URL % removed_test_names
322
323 _log.info('Opening results dashboard: ' + url)
324 self._browser.open(url)
325
289 def write_test_expectations(self, test_expectations, test_expectations_file) : 326 def write_test_expectations(self, test_expectations, test_expectations_file) :
290 """Writes the given TestExpectations object to the filesystem. 327 """Writes the given TestExpectations object to the filesystem.
291 328
292 Args: 329 Args:
293 test_expectations: The TestExpectations object to write. 330 test_expectations: The TestExpectations object to write.
294 test_expectations_file: The full file path of the Blink 331 test_expectations_file: The full file path of the Blink
295 TestExpectations file. This file will be overwritten. 332 TestExpectations file. This file will be overwritten.
296 """ 333 """
297 self._host.filesystem.write_text_file( 334 self._host.filesystem.write_text_file(
298 test_expectations_file, 335 test_expectations_file,
299 TestExpectations.list_to_string(test_expectations, reconstitute_only _these=[])) 336 TestExpectations.list_to_string(test_expectations, reconstitute_only _these=[]))
OLDNEW
« no previous file with comments | « no previous file | third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/update_test_expectations_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698