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

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

Issue 2125633002: Fill out implementation of UpdateTestExpectations script (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@updateTestExpectations2
Patch Set: Created 4 years, 5 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 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:
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
55 expectations_file) 55 expectations_file)
56 56
57 57
58 class RemoveFlakesOMatic(object): 58 class RemoveFlakesOMatic(object):
59 def __init__(self, host, port, bot_test_expectations_factory): 59 def __init__(self, host, port, bot_test_expectations_factory):
60 self._host = host 60 self._host = host
61 self._port = port 61 self._port = port
62 self._expectations_factory = bot_test_expectations_factory 62 self._expectations_factory = bot_test_expectations_factory
63 self.builder_results_by_path = {} 63 self.builder_results_by_path = {}
64 64
65 def _can_delete_line(self, test_expectation_line):
66 """Returns whether a given line in the expectations can be removed.
67
68 Uses results from builder bots to determine if a given line is stale and
69 can safely be removed from the TestExpectations file. (i.e. remove if
70 the bots show that it's not flaky.) There's also some rules about when
qyearsley 2016/07/08 17:55:03 There's also some rules -> There are also some rul
bokan 2016/07/08 21:13:00 Done.
71 not to remove lines (e.g. never remove lines with Rebaseline
72 expectations, don't remove non-flaky expectations, etc.)
73
74 Args:
75 test_expectation_line (TestExpectationLine): A line in the test
76 expectation file to test for possible removal.
77
78 Returns: True if the line can be removed, False otherwise.
qyearsley 2016/07/08 17:55:04 To be consistent with the docstrings below, this c
bokan 2016/07/08 21:13:00 Done.
79 """
80 expectations = test_expectation_line.expectations
81 if len(expectations) < 2:
82 return False
83
84 if self._has_unstrippable_expectations(expectations):
85 return False
86
87 if not self._has_pass_expectation(expectations):
88 return False
qyearsley 2016/07/08 17:55:03 Is "PASS" an "unstrippable" expectation? Would it
bokan 2016/07/08 21:13:00 No, it's a bit subtle but has_unstrippable_expecta
89
90 # The line can be deleted if the only expectation on the line that appea rs in the actual
91 # results is the PASS expectation.
92 for config in test_expectation_line.matching_configurations:
93 builder_name = self._host.builders.builder_name_for_specifiers(confi g.version, config.build_type)
94
95 if not builder_name:
96 _log.error('Failed to get builder for config [%s, %s, %s]' % (co nfig.version, config.architecture, config.build_type))
qyearsley 2016/07/08 17:55:03 For logging methods, if you give more than one arg
bokan 2016/07/08 21:13:00 Done.
97 # TODO(bokan): Matching configurations often give us bots that d on't have a
98 # builder in builders.py's exact_matches. Should we ignore those or be conservative
99 # and assume we need these expectations to make a decision?
100 return False
101
102 if builder_name not in self.builder_results_by_path.keys():
103 _log.error('Failed to find results for builder "%s"' % builder_n ame)
104 return False
105
106 results_by_path = self.builder_results_by_path[builder_name]
107
108 # No results means the tests were all skipped or all results are pas sing.
109 if test_expectation_line.path not in results_by_path.keys():
110 continue
111
112 results_for_single_test = results_by_path[test_expectation_line.path ]
113
114 if self._expectations_that_were_met(test_expectation_line, results_f or_single_test) != set(['PASS']):
115 return False
116
117 return True
118
119 def _has_pass_expectation(self, expectations):
120 return 'PASS' in expectations
121
122 def _expectations_that_were_met(self, test_expectation_line, results_for_sin gle_test):
123 """Returns the set of expectations that appear in the given results.
124
125 e.g. If the test expectations is:
126 bug(test) fast/test.html [Crash Failure Pass]
127
128 And the results are ['TEXT', 'PASS', 'PASS', 'TIMEOUT']
129
130 This method would return [Pass Failure]
qyearsley 2016/07/08 17:55:03 Maybe the "flowiness" of this section could be imp
bokan 2016/07/08 21:13:00 Done.
131
132 Args:
133 test_expectation_line: A TestExpectationLine object
134 results_for_single_test: A list of result strings.
135 e.g. ['IMAGE', 'IMAGE', 'PASS']
136
137 Returns:
138 A set containing expectations that occured in the results.
139 """
140 # TODO(bokan): Does this not exist in a more central place?
qyearsley 2016/07/08 17:55:03 Good question, it seems like it should, so if it d
bokan 2016/07/08 21:13:00 Ok, I'll leave this TODO here for now and either f
141 def replace_failing_with_fail(expectation):
142 if expectation in ('TEXT', 'IMAGE', 'IMAGE+TEXT', 'AUDIO'):
143 return 'FAIL'
144 else:
145 return expectation
146
147 actual_results = {replace_failing_with_fail(r) for r in results_for_sing le_test}
148
149 return set(test_expectation_line.expectations) & actual_results
150
151 def _has_unstrippable_expectations(self, expectations):
152 """ Returns whether any of the given expectations are considered unstrip pable.
qyearsley 2016/07/08 17:55:04 Extra space character can be removed.
bokan 2016/07/08 21:13:00 Done.
153
154 Unstrippable expectations are those which should stop a line from being
155 removed regardless of builder bot results.
156
157 Args:
158 expectations: A list of string expectations.
159 E.g. ['PASS', 'FAIL' 'CRASH']
160
161 Returns:
162 True if at least one of the expectations is unstrippable. False
163 otherwise.
164 """
165 unstrippable_expectations = ('REBASELINE', 'NEEDSREBASELINE',
166 'NEEDSMANUALREBASELINE', 'SLOW',
167 'SKIP')
168 return any(s in expectations for s in unstrippable_expectations)
169
65 def get_updated_test_expectations(self): 170 def get_updated_test_expectations(self):
66 """Filters out passing lines from TestExpectations file. 171 """Filters out passing lines from TestExpectations file.
67 172
68 Reads the current TestExpectatoins file and, using results from the 173 Reads the current TestExpectatoins file and, using results from the
69 build bots, removes lines that are passing. That is, removes lines that 174 build bots, removes lines that are passing. That is, removes lines that
70 were not needed to keep the bots green. 175 were not needed to keep the bots green.
71 176
72 Returns: A TestExpectations object with the passing lines filtered out. 177 Returns: A TestExpectations object with the passing lines filtered out.
73 """ 178 """
74 test_expectations = TestExpectations(self._port, include_overrides=False ).expectations() 179 test_expectations = TestExpectations(self._port, include_overrides=False ).expectations()
75 # TODO: Filter the expectations based on results. 180
181 self.builder_results_by_path = {}
182 for builder_name in self._host.builders.all_builder_names():
183 expectations_for_builder = (
184 self._expectations_factory.expectations_for_builder(builder_name )
185 )
186
187 if not expectations_for_builder:
188 # This is not fatal since we may not need to check these
189 # results. If we do need these results we'll log an error later
190 # when trying to check against them.
191 _log.warn('Downloaded results are missing results for builder "% s"' % builder_name)
192 continue
193
194 self.builder_results_by_path[builder_name] = (
195 expectations_for_builder.all_results_by_path()
196 )
qyearsley 2016/07/08 17:55:04 This block of code (lines 181-196), which builds u
bokan 2016/07/08 21:13:00 Done.
197
198 expectations_to_remove = []
199
200 for expectation in test_expectations:
201 if self._can_delete_line(expectation):
202 expectations_to_remove.append(expectation)
203
204 for expectation in expectations_to_remove:
205 index = test_expectations.index(expectation)
206 test_expectations.remove(expectation)
207
208 # Remove associated comments and whitespace if we've removed the las t expectation under
209 # a comment block. Only remove a comment block if it's not separated from the test
210 # expectation line by whitespace.
211 if index == len(test_expectations) or test_expectations[index].is_wh itespace() or test_expectations[index].is_comment():
212 removed_whitespace = False
213 while index and test_expectations[index - 1].is_whitespace():
214 index = index - 1
qyearsley 2016/07/08 17:55:03 `index = index - 1` can also be written as `index
bokan 2016/07/08 21:13:00 Done.
215 test_expectations.pop(index)
216 removed_whitespace = True
217
218 if not removed_whitespace:
219 while index and test_expectations[index - 1].is_comment():
220 index = index - 1
221 test_expectations.pop(index)
222
223 while index and test_expectations[index - 1].is_whitespace() :
224 index = index - 1
225 test_expectations.pop(index)
qyearsley 2016/07/08 17:55:03 This block of code (removing associated comments a
bokan 2016/07/08 21:13:00 Done.
226
76 return test_expectations 227 return test_expectations
77 228
78 def write_test_expectations(self, test_expectations, test_expectations_file) : 229 def write_test_expectations(self, test_expectations, test_expectations_file) :
79 """Writes the given TestExpectations object to the filesystem. 230 """Writes the given TestExpectations object to the filesystem.
80 231
81 Args: 232 Args:
82 test_expectatoins: The TestExpectations object to write. 233 test_expectatoins: The TestExpectations object to write.
83 test_expectations_file: The full file path of the Blink 234 test_expectations_file: The full file path of the Blink
84 TestExpectations file. This file will be overwritten. 235 TestExpectations file. This file will be overwritten.
85 """ 236 """
86 self._host.filesystem.write_text_file( 237 self._host.filesystem.write_text_file(
87 test_expectations_file, 238 test_expectations_file,
88 TestExpectations.list_to_string(test_expectations, reconstitute_only _these=[])) 239 TestExpectations.list_to_string(test_expectations, reconstitute_only _these=[]))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698