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

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

Issue 1766583002: Added update_test_expectations script to remove non-flaky test expectations. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: tests++ Created 4 years, 9 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
(Empty)
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
3 # found in the LICENSE file.
4
5 import argparse
6
7 from webkitpy.layout_tests.port import builders
8 from webkitpy.layout_tests.models.test_expectations import TestExpectations
9 from webkitpy.layout_tests.layout_package.bot_test_expectations import BotTestEx pectationsFactory
10
11
12 def main(host, argv):
13 parser = argparse.ArgumentParser(epilog=("""
14 Scans the TestExpectations file
15 and uses results from actual builder bots runs to remove tests that are
16 marked as flaky but don't fail in the specified way.
17
18 E.g. If a test has this expectation:
19 bug(test) fast/test.html [ Failure Pass ]
20
21 And all the runs on builders have passed the line will be removed.
22 Additionally, the runs don't all have to be Passing to remove the line;
23 as long as the non-Passing results are of a type not specified in the
24 expectation (e.g. Crash) this line will be remoed.
qyearsley 2016/03/10 19:13:58 Excellent, this is helpful. I'm not sure, but may
bokan 2016/03/16 20:24:53 Done.
25 """), formatter_class=argparse.RawTextHelpFormatter)
26 args = parser.parse_args(argv)
27
28 port = host.port_factory.get()
29 bot_test_expectations_factory = BotTestExpectationsFactory()
30
31 remove_flakes_o_matic = RemoveFlakesOMatic(host,
32 port,
33 bot_test_expectations_factory)
34
35 test_expectations = remove_flakes_o_matic.get_updated_test_expectations()
36
37 expectations_file = port.path_to_generic_test_expectations_file()
38
39 remove_flakes_o_matic.write_test_expectations(test_expectations,
40 expectations_file)
41
42
43 class RemoveFlakesOMatic:
44 def __init__(self, host, port, bot_test_expectations_factory):
45 self._host = host
46 self._port = port
qyearsley 2016/03/10 19:13:58 Host is the host on which the expectations file wi
bokan 2016/03/16 20:24:53 I'm not 100% clear on what Host and Port are but I
47 self._expectations_factory = bot_test_expectations_factory
48
49 def _can_delete_line(self, test_expectation_line):
50 """Returns whether a given line in the expectations can be removed based
51 on its results on the bots (i.e. remove if the bots show its not flaky.)
52
53 Args:
54 test_expectation_line (TestExpectationLine): A line in the test
55 expectation file to test for possible removal.
56
qyearsley 2016/03/10 19:13:58 Optional docstring recommendations: - It's faste
bokan 2016/03/16 20:24:53 Thanks, done.
57 """
58 expectations = test_expectation_line.expectations
59 if len(expectations) < 2:
60 return False
61
62 if self._has_unstrippable_expectations(expectations):
63 return False
64
65 if not self._has_pass_expectation(expectations):
66 return False
67
68 # The line can be deleted if the only expectation on the line that appea rs in the actual
69 # results is the PASS expectation.
70 for config in test_expectation_line.matching_configurations:
71 builder_name = builders.builder_name_for_specifiers(config.version, config.build_type)
72
73 if not builder_name:
74 # TODO(bokan): We probably want to report a problem if we can't
75 # turn the specifiers into a builder name.
76 # TODO(bokan): Matching configurations often give us bots that d on't have a
77 # builder in builders.py's exact_matches. Should we ignore those or be conservative
78 # and assume we need these expectations to make a decision? Also , testing isn't
79 # well suited to changing the list of builders/configurations so if we want to skip
80 # here the tests will have to specify results for all builders.
81 continue
82
83 if builder_name not in self.builder_results_by_path.keys():
84 # TODO(bokan): We probably want to report a problem if we can't get results for
85 # a bot.
qyearsley 2016/03/10 19:13:58 Sounds reasonable -- question is, should it be con
bokan 2016/03/16 20:24:53 I think not because, for example, if we have an ex
86 continue
87
88 results_by_path = self.builder_results_by_path[builder_name]
89
90 # No results means the test was all skipped or all results are passi ng.
91 if test_expectation_line.path not in results_by_path.keys():
92 continue
93
94 results_for_single_test = results_by_path[test_expectation_line.path ]
95
96 if self._met_expectations(test_expectation_line, results_for_single_ test) != set(['PASS']):
97 return False
98
99 return True
100
101 def _has_pass_expectation(self, expectations):
102 return 'PASS' in expectations
103
104 def _met_expectations(self, test_expectation_line, results_for_single_test):
qyearsley 2016/03/10 19:13:58 The name of this function currently suggests it mi
bokan 2016/03/16 20:24:53 I don't really like testing private internals but
105 # TODO(bokan): Does this not exist in a more central place?
106 def replace_failing_with_fail(expectation):
107 if expectation in ('TEXT', 'IMAGE', 'IMAGE+TEXT', 'AUDIO'):
108 return 'FAIL'
109 else:
110 return expectation
111
112 actual_results = {replace_failing_with_fail(r) for r in results_for_sing le_test}
113
114 return set(test_expectation_line.expectations) & actual_results
115
116 def _has_unstrippable_expectations(self, expectations):
qyearsley 2016/03/10 19:13:58 expectations here is a list of strings, right?
bokan 2016/03/16 20:24:53 Right, docstring added.
117 unstrippable_expectations = ('REBASELINE', 'NEEDSREBASELINE',
118 'NEEDSMANUALREBASELINE', 'SLOW')
119 return any(s in expectations for s in unstrippable_expectations)
120
121 def get_updated_test_expectations(self):
122 test_expectations = TestExpectations(self._port, include_overrides=False ).expectations()
123
124 self.builder_results_by_path = {}
125 for builder_name in builders.all_builder_names():
126 expectations_for_builder = (
127 self._expectations_factory.expectations_for_builder(builder_name )
128 )
129
130 # TODO(bokan): What to do if we can't get the results for a builder?
131 if not expectations_for_builder:
132 continue
133
134 self.builder_results_by_path[builder_name] = (
135 expectations_for_builder.all_results_by_path()
136 )
qyearsley 2016/03/10 19:13:58 Does getting the builder results require making ne
bokan 2016/03/16 20:24:53 Not sure since that all happens elsewhere but my b
137
138 expectations_to_remove = []
139
140 for expectation in test_expectations:
141 if self._can_delete_line(expectation):
142 expectations_to_remove.append(expectation)
143
144 for expectation in expectations_to_remove:
145 index = test_expectations.index(expectation)
146 test_expectations.remove(expectation)
147
148 # Remove associated comments and whitespace if we've removed the las t expectation under
149 # a comment block. Only remove a comment block if it's not separated from the tests by
150 # whitespace.
151 if index == len(test_expectations) or test_expectations[index].is_wh itespace_or_comment():
152 removed_whitespace = False
153 while index and test_expectations[index - 1].is_whitespace():
154 index = index - 1
155 test_expectations.pop(index)
156 removed_whitespace = True
157
158 if not removed_whitespace:
159 while index and test_expectations[index - 1].is_comment():
160 index = index - 1
161 test_expectations.pop(index)
162
163 while index and test_expectations[index - 1].is_whitespace() :
164 index = index - 1
165 test_expectations.pop(index)
166
167 return test_expectations
168
169 def write_test_expectations(self, test_expectations, test_expectations_file) :
170 self._host.filesystem.write_text_file(
171 test_expectations_file,
172 TestExpectations.list_to_string(test_expectations, reconstitute_only _these=[]))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698