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

Side by Side Diff: third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/update_test_expectations_unittest.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: Mock out builders and specifiers, dont remove lines if bot results arent available 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 unittest
6 from collections import OrderedDict
7 from webkitpy.common.host_mock import MockHost
8 from webkitpy.layout_tests.models.test_expectations import TestExpectations
9 from webkitpy.layout_tests.port.test import LAYOUT_TEST_DIR
10 from webkitpy.layout_tests.update_test_expectations import RemoveFlakesOMatic
11 from webkitpy.layout_tests.port import builders
12
13
14 class FakeBotTestExpectations(object):
15
16 def __init__(self, results_by_path):
17 self._results = results_by_path
18
19 def all_results_by_path(self):
20 return self._results
21
22
23 class FakeBotTestExpectationsFactory(object):
24
25 def __init__(self):
26 self._all_results_by_builder = {}
Dirk Pranke 2016/03/29 22:39:01 Just make these public instance variables and get
bokan 2016/04/05 12:28:57 Done.
27
28 def set_results(self, results_by_path_by_builder):
29 """Sets the results that will be returned by expectationts_for_builder.
30
31 Args:
32 results_by_path_by_builder (dict): The distinct results seen in
33 at least one run of the test. E.g. if the bot results for
34 mytest.html are:
35 PASS PASS FAIL PASS TIMEOUT
36 then results_by_path_by_builder should be:
37 {
38 'WebKit Linux' : {
39 'mytest.html': ['FAIL', 'PASS', 'TIMEOUT']
40 }
41 }
42 """
43 self._all_results_by_builder = results_by_path_by_builder
44
45 def expectations_for_builder(self, builder):
46 if builder not in self._all_results_by_builder:
47 return None
48
49 return FakeBotTestExpectations(self._all_results_by_builder[builder])
50
51
52 class UpdateTestExpectationsTest(unittest.TestCase):
53
54 def __init__(self, testFunc):
qyearsley 2016/03/21 21:45:25 What is testFunc here? Also, would it make sense
Dirk Pranke 2016/03/29 22:39:01 Don't override __init__ or use setUpClass or tearD
bokan 2016/04/05 12:28:57 It's part of the Python unittest framework. testFu
bokan 2016/04/05 12:28:57 Done.
55 self._host = MockHost()
56 self._port = self._host.port_factory.get('test', None)
57 self._expectation_factory = FakeBotTestExpectationsFactory()
58 self._flake_remover = RemoveFlakesOMatic(self._host,
59 self._port,
60 self._expectation_factory)
61 unittest.TestCase.__init__(self, testFunc)
62
63 filesystem = self._host.filesystem
64 for test in self._get_test_list():
65 filesystem.write_binary_file(filesystem.join(LAYOUT_TEST_DIR, test), '')
66
67 def setUp(self):
68 self._old_builders = builders._exact_matches
qyearsley 2016/03/21 21:45:26 Maybe this attribute would be clearer if it were c
bokan 2016/04/05 12:28:57 Done.
69 self._port.set_configuration_specifier_macros({
70 'mac': ['mac10.10'],
71 'win': ['win7'],
72 'linux': ['precise']
73 })
74
75 def tearDown(self):
76 builders._exact_matches = self._old_builders
Dirk Pranke 2016/03/29 22:39:01 It is really ugly to be mutating module-level stat
bokan 2016/04/05 12:28:57 Yah, I agree. I'll look into doing this as a separ
77
78 def _get_test_list(self):
79 return ['test/a.html',
80 'test/b.html',
81 'test/c.html',
82 'test/d.html',
83 'test/e.html',
84 'test/f.html',
85 'test/g.html']
86
87 def _assert_expectations_match(self, expectations, expected_string):
88 self.assertIsNotNone(expectations)
89 stringified_expectations = "\n".join(x.to_string(None) for x in expectat ions)
90 expected_string = "\n".join(x.strip() for x in expected_string.split("\n "))
91 self.assertEqual(stringified_expectations, expected_string)
92
93 def _parse_expectations(self, expectations):
94 """Parses a TestExpectatoin file given as string.
Dirk Pranke 2016/03/29 22:39:01 nit: s/TestExpectatoin/TestExpectation/.
bokan 2016/04/05 12:28:57 Done.
95
96 This function takes a string representing the contents of the
97 TestExpectations file and parses it, producing the TestExpectations
98 object and sets it on the Port object where the script will read it
99 from.
100
101 Args:
102 expectations: A string containing the contents of the
103 TestExpectations file to use.
104 """
105 expectations_dict = OrderedDict()
106 expectations_dict['expectations'] = expectations
107 self._port.expectations_dict = lambda: expectations_dict
108
109 def test_dont_remove_non_flakes(self):
110 """Tests that lines that aren't flaky are not touched.
111
112 Lines are flaky if they contain a PASS as well as at least one other
113 failing result.
114 """
115 test_expectations_before = """
116 # Even though the results show all passing, none of the
117 # expectations are flaky so we shouldn't remove any.
118 Bug(test) test/a.html [ Pass ]
119 Bug(test) test/b.html [ Timeout ]
120 Bug(test) test/c.html [ Failure Timeout ]
121 Bug(test) test/d.html [ Rebaseline ]
122 Bug(test) test/e.html [ NeedsManualRebaseline ]
123 Bug(test) test/f.html [ NeedsRebaseline ]"""
124
125 builders._exact_matches = {
126 "WebKit Linux": {"port_name": "linux-precise", "specifiers": ['Preci se', 'Release']},
127 }
128 self._port.set_all_build_types(('release',))
129 self._port.set_all_systems((('precise', 'x86_64'),))
130
131 self._parse_expectations(test_expectations_before)
132 self._expectation_factory.set_results({'WebKit Linux': {
133 "test/a.html": ["PASS", "PASS"],
134 "test/b.html": ["PASS", "PASS"],
135 "test/c.html": ["PASS", "PASS"],
136 "test/d.html": ["PASS", "PASS"],
137 "test/e.html": ["PASS", "PASS"],
138 "test/f.html": ["PASS", "PASS"],
139 }})
140 updated_expectations = self._flake_remover.get_updated_test_expectations ()
141 self._assert_expectations_match(updated_expectations, test_expectations_ before)
142
143 def test_dont_remove_rebaselines(self):
144 """Tests that lines with rebaseline expectations are untouched.
145 """
146 test_expectations_before = """
147 # Even though the results show all passing, none of the
148 # expectations are flaky so we shouldn't remove any.
149 Bug(test) test/a.html [ Failure NeedsRebaseline Pass ]
150 Bug(test) test/b.html [ Failure Pass Rebaseline ]
151 Bug(test) test/c.html [ Failure NeedsManualRebaseline Pass ]"""
152
153 builders._exact_matches = {
154 "WebKit Linux": {"port_name": "linux-precise", "specifiers": ['Preci se', 'Release']},
155 }
156 self._port.set_all_build_types(('release',))
157 self._port.set_all_systems((('precise', 'x86_64'),))
158
159 self._parse_expectations(test_expectations_before)
160 self._expectation_factory.set_results({'WebKit Linux': {
161 "test/a.html": ["PASS", "PASS"],
162 "test/b.html": ["PASS", "PASS"],
163 "test/c.html": ["PASS", "PASS"]
164 }})
165 updated_expectations = self._flake_remover.get_updated_test_expectations ()
166 self._assert_expectations_match(updated_expectations, test_expectations_ before)
167
168 def test_all_failure_types(self):
169 """Tests that all failure types are treated as failure."""
170 test_expectations_before = (
171 """Bug(test) test/a.html [ Failure Pass ]
172 Bug(test) test/b.html [ Failure Pass ]
173 Bug(test) test/c.html [ Failure Pass ]
174 Bug(test) test/d.html [ Failure Pass ]
175 # Remove these two since CRASH and TIMEOUT aren't considered Failure .
176 Bug(test) test/e.html [ Failure Pass ]
177 Bug(test) test/f.html [ Failure Pass ]""")
178
179 builders._exact_matches = {
180 "WebKit Linux": {"port_name": "linux-precise", "specifiers": ['Preci se', 'Release']},
181 }
182 self._port.set_all_build_types(('release',))
183 self._port.set_all_systems((('precise', 'x86_64'),))
184
185 self._parse_expectations(test_expectations_before)
186 self._expectation_factory.set_results({'WebKit Linux': {
187 "test/a.html": ["PASS", "IMAGE"],
188 "test/b.html": ["PASS", "TEXT"],
189 "test/c.html": ["PASS", "IMAGE+TEXT"],
190 "test/d.html": ["PASS", "AUDIO"],
191 "test/e.html": ["PASS", "CRASH"],
192 "test/f.html": ["PASS", "TIMEOUT"],
193 }})
194 updated_expectations = self._flake_remover.get_updated_test_expectations ()
195 self._assert_expectations_match(updated_expectations, (
196 """Bug(test) test/a.html [ Failure Pass ]
197 Bug(test) test/b.html [ Failure Pass ]
198 Bug(test) test/c.html [ Failure Pass ]
199 Bug(test) test/d.html [ Failure Pass ]"""))
200
201 def test_basic_one_builder(self):
202 """Tests basic functionality with a single builder.
203
204 Test that flaky expectations with results from a single bot showing the
205 expected failure isn't occuring should be removed. Results with failures
206 of the expected type shouldn't be removed but other kinds of failures
207 allow removal.
208 """
209 test_expectations_before = (
210 """# Remove this since it's passing all runs.
211 Bug(test) test/a.html [ Failure Pass ]
212 # Remove this since, although there's a failure, it's not a timeout.
213 Bug(test) test/b.html [ Pass Timeout ]
214 # Keep since we have both crashes and passes.
215 Bug(test) test/c.html [ Crash Pass ]""")
qyearsley 2016/03/21 21:45:25 Formatting side note: If it's OK to have a newline
bokan 2016/04/05 12:28:57 Yah, but that includes the newline in the string a
216
217 builders._exact_matches = {
218 "WebKit Linux": {"port_name": "linux-precise", "specifiers": ['Preci se', 'Release']},
219 }
220 self._port.set_all_build_types(('release',))
221 self._port.set_all_systems((('precise', 'x86_64'),))
222
223 self._parse_expectations(test_expectations_before)
224 self._expectation_factory.set_results({'WebKit Linux': {
225 "test/a.html": ["PASS", "PASS", "PASS"],
226 "test/b.html": ["PASS", "IMAGE", "PASS"],
227 "test/c.html": ["PASS", "CRASH", "PASS"],
228 }})
229 updated_expectations = self._flake_remover.get_updated_test_expectations ()
230 self._assert_expectations_match(updated_expectations, (
231 """# Keep since we have both crashes and passes.
232 Bug(test) test/c.html [ Crash Pass ]"""))
233
234 def test_all_failure_case(self):
235 """Tests that results with all failures are not treated as non-flaky."""
236 test_expectations_before = (
237 """# Keep since it's all failures.
238 Bug(test) test/a.html [ Failure Pass ]""")
239
240 builders._exact_matches = {
241 "WebKit Linux": {"port_name": "linux-precise", "specifiers": ['Preci se', 'Release']},
242 }
243 self._port.set_all_build_types(('release',))
244 self._port.set_all_systems((('precise', 'x86_64'),))
245
246 self._parse_expectations(test_expectations_before)
247 self._expectation_factory.set_results({'WebKit Linux': {
248 "test/a.html": ["IMAGE", "IMAGE", "IMAGE"],
249 }})
250 updated_expectations = self._flake_remover.get_updated_test_expectations ()
251 self._assert_expectations_match(updated_expectations, (
252 """# Keep since it's all failures.
253 Bug(test) test/a.html [ Failure Pass ]"""))
254
255 def test_empty_test_expectations(self):
256 """Tests that results with all failures are not treated as non-flaky."""
257 test_expectations_before = ""
258
259 builders._exact_matches = {
260 "WebKit Linux": {"port_name": "linux-precise", "specifiers": ['Preci se', 'Release']},
261 }
262 self._port.set_all_build_types(('release',))
263 self._port.set_all_systems((('precise', 'x86_64'),))
264
265 self._parse_expectations(test_expectations_before)
266 self._expectation_factory.set_results({'WebKit Linux': {
267 "test/a.html": ["PASS", "PASS", "PASS"],
268 }})
269 updated_expectations = self._flake_remover.get_updated_test_expectations ()
270 self._assert_expectations_match(updated_expectations, "")
271
272 def test_basic_multiple_builders(self):
273 """Tests basic functionality with multiple builders."""
274 test_expectations_before = (
275 """# Remove since it's passing on both builders.
276 Bug(test) test/a.html [ Failure Pass ]
277 # Keep since it's failing on the Mac builder.
278 Bug(test) test/b.html [ Failure Pass ]
279 # Keep since it's failing on the Linux builder.
280 Bug(test) test/c.html [ Failure Pass ]""")
281
282 builders._exact_matches = {
283 "WebKit Linux": {"port_name": "linux-precise", "specifiers": ['Preci se', 'Release']},
284 "WebKit Mac10.10": {"port_name": "mac-mac10.10", "specifiers": ['Mac 10.10', 'Release']},
285 }
286
287 self._port.set_all_build_types(('release',))
288 self._port.set_all_systems((('mac10.10', 'x86'),
289 ('precise', 'x86_64')))
290
291 self._parse_expectations(test_expectations_before)
292 self._expectation_factory.set_results({
293 'WebKit Linux': {
294 "test/a.html": ["PASS", "PASS", "PASS"],
295 "test/b.html": ["PASS", "PASS", "PASS"],
296 "test/c.html": ["AUDIO", "AUDIO", "AUDIO"],
297 },
298 'WebKit Mac10.10': {
299 "test/a.html": ["PASS", "PASS", "PASS"],
300 "test/b.html": ["PASS", "PASS", "IMAGE"],
301 "test/c.html": ["PASS", "PASS", "PASS"],
302 },
303 })
304 updated_expectations = self._flake_remover.get_updated_test_expectations ()
305 self._assert_expectations_match(updated_expectations, (
306 """# Keep since it's failing on the Mac builder.
307 Bug(test) test/b.html [ Failure Pass ]
308 # Keep since it's failing on the Linux builder.
309 Bug(test) test/c.html [ Failure Pass ]"""))
310
311 def test_multiple_builders_and_platform_specifiers(self):
312 """Tests correct operation with platform specifiers."""
313 test_expectations_before = (
314 """# Keep since it's failing on Mac results.
315 Bug(test) [ Mac ] test/a.html [ Failure Pass ]
316 # Keep since it's failing on the Windows builder.
317 Bug(test) [ Linux Win ] test/b.html [ Failure Pass ]
318 # Remove since it's passing on both Linux and Windows builders.
319 Bug(test) [ Linux Win ] test/c.html [ Failure Pass ]
320 # Remove since it's passing on Mac results
321 Bug(test) [ Mac ] test/d.html [ Failure Pass ]""")
322
323 builders._exact_matches = {
324 "WebKit Win7": {"port_name": "win-win7", "specifiers": ['Win7', 'Rel ease']},
325 "WebKit Linux": {"port_name": "linux-precise", "specifiers": ['Preci se', 'Release']},
326 "WebKit Mac10.10": {"port_name": "mac-mac10.10", "specifiers": ['Mac 10.10', 'Release']},
327 }
328 self._port.set_all_build_types(('release',))
329 self._port.set_all_systems((('mac10.10', 'x86'),
330 ('win7', 'x86'),
331 ('precise', 'x86_64')))
332
333 self._parse_expectations(test_expectations_before)
334 self._expectation_factory.set_results({
335 'WebKit Linux': {
336 "test/a.html": ["PASS", "PASS", "PASS"],
337 "test/b.html": ["PASS", "PASS", "PASS"],
338 "test/c.html": ["PASS", "PASS", "PASS"],
339 "test/d.html": ["IMAGE", "PASS", "PASS"],
340 },
341 'WebKit Mac10.10': {
342 "test/a.html": ["PASS", "PASS", "IMAGE"],
343 "test/b.html": ["PASS", "IMAGE", "PASS"],
344 "test/c.html": ["PASS", "IMAGE", "PASS"],
345 "test/d.html": ["PASS", "PASS", "PASS"],
346 },
347 'WebKit Win7': {
348 "test/a.html": ["PASS", "PASS", "PASS"],
349 "test/b.html": ["IMAGE", "PASS", "PASS"],
350 "test/c.html": ["PASS", "PASS", "PASS"],
351 "test/d.html": ["IMAGE", "PASS", "PASS"],
352 },
353 })
354 updated_expectations = self._flake_remover.get_updated_test_expectations ()
355 self._assert_expectations_match(updated_expectations, (
356 """# Keep since it's failing on Mac results.
357 Bug(test) [ Mac ] test/a.html [ Failure Pass ]
358 # Keep since it's failing on the Windows builder.
359 Bug(test) [ Linux Win ] test/b.html [ Failure Pass ]"""))
360
361 def test_debug_release_specifiers(self):
362 """Tests correct operation of Debug/Release specifiers."""
363 test_expectations_before = (
364 """# Keep since it fails in debug.
365 Bug(test) [ Linux ] test/a.html [ Failure Pass ]
366 # Remove since the failure is in Release, Debug is all PASS.
367 Bug(test) [ Debug ] test/b.html [ Failure Pass ]
368 # Keep since there's a failure in Linux Release.
369 Bug(test) [ Release ] test/c.html [ Failure Pass ]
370 # Remove since the Release Linux builder is all passing.
371 Bug(test) [ Release Linux ] test/d.html [ Failure Pass ]
372 # Remove since all the Linux builders PASS.
373 Bug(test) [ Linux ] test/e.html [ Failure Pass ]""")
374
375 builders._exact_matches = {
376 "WebKit Win7": {"port_name": "win-win7", "specifiers": ['Win7', 'Rel ease']},
377 "WebKit Win7 (dbg)": {"port_name": "win-win7", "specifiers": ['Win7' , 'Debug']},
378 "WebKit Linux": {"port_name": "linux-precise", "specifiers": ['Preci se', 'Release']},
379 "WebKit Linux (dbg)": {"port_name": "linux-precise", "specifiers": [ 'Precise', 'Debug']},
380 }
381 self._port.set_all_build_types(('release', 'debug'))
382 self._port.set_all_systems((('win7', 'x86'),
383 ('precise', 'x86_64')))
384
385 self._parse_expectations(test_expectations_before)
386 self._expectation_factory.set_results({
387 'WebKit Linux': {
388 "test/a.html": ["PASS", "PASS", "PASS"],
389 "test/b.html": ["PASS", "IMAGE", "PASS"],
390 "test/c.html": ["PASS", "IMAGE", "PASS"],
391 "test/d.html": ["PASS", "PASS", "PASS"],
392 "test/e.html": ["PASS", "PASS", "PASS"],
393 },
394 'WebKit Linux (dbg)': {
395 "test/a.html": ["PASS", "IMAGE", "PASS"],
396 "test/b.html": ["PASS", "PASS", "PASS"],
397 "test/c.html": ["PASS", "PASS", "PASS"],
398 "test/d.html": ["IMAGE", "PASS", "PASS"],
399 "test/e.html": ["PASS", "PASS", "PASS"],
400 },
401 'WebKit Win7 (dbg)': {
402 "test/a.html": ["PASS", "PASS", "PASS"],
403 "test/b.html": ["PASS", "PASS", "PASS"],
404 "test/c.html": ["PASS", "PASS", "PASS"],
405 "test/d.html": ["PASS", "IMAGE", "PASS"],
406 "test/e.html": ["PASS", "PASS", "PASS"],
407 },
408 'WebKit Win7': {
409 "test/a.html": ["PASS", "PASS", "PASS"],
410 "test/b.html": ["PASS", "PASS", "IMAGE"],
411 "test/c.html": ["PASS", "PASS", "PASS"],
412 "test/d.html": ["PASS", "IMAGE", "PASS"],
413 "test/e.html": ["PASS", "PASS", "PASS"],
414 },
415 })
416 updated_expectations = self._flake_remover.get_updated_test_expectations ()
417 self._assert_expectations_match(updated_expectations, (
418 """# Keep since it fails in debug.
419 Bug(test) [ Linux ] test/a.html [ Failure Pass ]
420 # Keep since there's a failure in Linux Release.
421 Bug(test) [ Release ] test/c.html [ Failure Pass ]"""))
422
423 def test_preserve_comments_and_whitespace(self):
424 """Tests that comments and whitespace are preserved appropriately.
425
426 Comments and whitespace should be kept unless all the tests grouped
427 below a comment are removed. In that case the comment block should also
428 be removed.
429
430 Ex:
431 # This comment applies to the below tests.
432 Bug(test) test/a.html [ Failure Pass ]
433 Bug(test) test/b.html [ Failure Pass ]
434
435 # <some prose>
436
437 # This is another comment.
438 Bug(test) test/c.html [ Failure Pass ]
439
440 Assuming we removed a.html and c.html we get:
441 # This comment applies to the below tests.
442 Bug(test) test/b.html [ Failure Pass ]
443
444 # <some prose>
445 """
446 test_expectations_before = """
447 # Comment A - Keep since these aren't part of any test.
448 # Comment B - Keep since these aren't part of any test.
449
450 # Comment C - Remove since it's a block belonging to a
451 # Comment D - and a is removed.
452 Bug(test) test/a.html [ Failure Pass ]
453 # Comment E - Keep since it's below a.
454
455
456 # Comment F - Keep since only b is removed
457 Bug(test) test/b.html [ Failure Pass ]
458 Bug(test) test/c.html [ Failure Pass ]
459
460 # Comment G - Should be removed since both d and e will be removed.
461 Bug(test) test/d.html [ Failure Pass ]
462 Bug(test) test/e.html [ Failure Pass ]"""
463
464 builders._exact_matches = {
465 "WebKit Linux": {"port_name": "linux-precise", "specifiers": ['Preci se', 'Release']},
466 }
467 self._port.set_all_build_types(('release',))
468 self._port.set_all_systems((('precise', 'x86_64'),))
469
470 self._parse_expectations(test_expectations_before)
471 self._expectation_factory.set_results({
472 'WebKit Linux': {
473 "test/a.html": ["PASS", "PASS", "PASS"],
474 "test/b.html": ["PASS", "PASS", "PASS"],
475 "test/c.html": ["PASS", "IMAGE", "PASS"],
476 "test/d.html": ["PASS", "PASS", "PASS"],
477 "test/e.html": ["PASS", "PASS", "PASS"],
478 }
479 })
480 updated_expectations = self._flake_remover.get_updated_test_expectations ()
481 self._assert_expectations_match(updated_expectations, (
482 """
483 # Comment A - Keep since these aren't part of any test.
484 # Comment B - Keep since these aren't part of any test.
485 # Comment E - Keep since it's below a.
486
487
488 # Comment F - Keep since only b is removed
489 Bug(test) test/c.html [ Failure Pass ]"""))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698