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

Side by Side Diff: third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_expectations_updater.py

Issue 2687093005: WPT importer: Use concise lists of version specifiers in expectation lines. (Closed)
Patch Set: Implement specifier simplification part Created 3 years, 10 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 layout test expectations and baselines when updating w3c tests. 5 """Updates layout test expectations and baselines when updating w3c tests.
6 6
7 Specifically, this class fetches results from try bots for the current CL, then 7 Specifically, this class fetches results from try bots for the current CL, then
8 (1) downloads new baseline files for any tests that can be rebaselined, and 8 (1) downloads new baseline files for any tests that can be rebaselined, and
9 (2) updates the generic TestExpectations file for any other failing tests. 9 (2) updates the generic TestExpectations file for any other failing tests.
10 """ 10 """
11 11
12 import argparse 12 import argparse
13 import copy 13 import copy
14 import logging 14 import logging
15 15
16 from webkitpy.common.memoized import memoized
16 from webkitpy.common.net.git_cl import GitCL 17 from webkitpy.common.net.git_cl import GitCL
17 from webkitpy.common.net.rietveld import Rietveld 18 from webkitpy.common.net.rietveld import Rietveld
18 from webkitpy.common.webkit_finder import WebKitFinder 19 from webkitpy.common.webkit_finder import WebKitFinder
19 from webkitpy.layout_tests.models.test_expectations import TestExpectationLine 20 from webkitpy.layout_tests.models.test_expectations import TestExpectationLine, TestExpectations
20 from webkitpy.w3c.test_parser import TestParser 21 from webkitpy.w3c.test_parser import TestParser
21 22
22 _log = logging.getLogger(__name__) 23 _log = logging.getLogger(__name__)
23 24
24 MARKER_COMMENT = '# ====== New tests from w3c-test-autoroller added here ======' 25 MARKER_COMMENT = '# ====== New tests from w3c-test-autoroller added here ======'
25 26
26 27
27 class WPTExpectationsUpdater(object): 28 class WPTExpectationsUpdater(object):
28 29
29 def __init__(self, host): 30 def __init__(self, host):
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
66 test_expectation_lines = self.create_line_list(test_expectations) 67 test_expectation_lines = self.create_line_list(test_expectations)
67 self.write_to_test_expectations(test_expectation_lines) 68 self.write_to_test_expectations(test_expectation_lines)
68 return 0 69 return 0
69 70
70 def get_issue_number(self): 71 def get_issue_number(self):
71 """Returns current CL number. Can be replaced in unit tests.""" 72 """Returns current CL number. Can be replaced in unit tests."""
72 return GitCL(self.host).get_issue_number() 73 return GitCL(self.host).get_issue_number()
73 74
74 def get_try_bots(self): 75 def get_try_bots(self):
75 """Returns try bot names. Can be replaced in unit tests.""" 76 """Returns try bot names. Can be replaced in unit tests."""
77 # TODO(qyearsley): This method is unnecessary; unit tests can set up
78 # a BuilderList with try builder names, instead of overriding this.
76 return self.host.builders.all_try_builder_names() 79 return self.host.builders.all_try_builder_names()
77 80
78 def get_failing_results_dict(self, build): 81 def get_failing_results_dict(self, build):
79 """Returns a nested dict of failing test results. 82 """Returns a nested dict of failing test results.
80 83
81 Retrieves a full list of layout test results from a builder result URL. 84 Retrieves a full list of layout test results from a builder result URL.
82 Collects the builder name, platform and a list of tests that did not 85 Collects the builder name, platform and a list of tests that did not
83 run as expected. 86 run as expected.
84 87
85 Args: 88 Args:
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
121 test_name = result.test_name() 124 test_name = result.test_name()
122 test_dict[test_name] = { 125 test_dict[test_name] = {
123 full_port_name: { 126 full_port_name: {
124 'expected': result.expected_results(), 127 'expected': result.expected_results(),
125 'actual': result.actual_results(), 128 'actual': result.actual_results(),
126 'bug': 'crbug.com/626703' 129 'bug': 'crbug.com/626703'
127 } 130 }
128 } 131 }
129 return test_dict 132 return test_dict
130 133
131 def _port_name_to_platform_specifier(self, port_name):
132 """Maps a port name to the platform specifier used in expectation lines.
133
134 For example:
135 linux-trusty -> Trusty
136 mac-mac10.11 -> Mac10.11.
137 """
138 builder_name = self.host.builders.builder_name_for_port_name(port_name)
139 specifiers = self.host.builders.specifiers_for_builder(builder_name)
140 return specifiers[0]
141
142 def merge_dicts(self, target, source, path=None): 134 def merge_dicts(self, target, source, path=None):
143 """Recursively merges nested dictionaries. 135 """Recursively merges nested dictionaries.
144 136
145 Args: 137 Args:
146 target: First dictionary, which is updated based on source. 138 target: First dictionary, which is updated based on source.
147 source: Second dictionary, not modified. 139 source: Second dictionary, not modified.
148 140
149 Returns: 141 Returns:
150 An updated target dictionary. 142 An updated target dictionary.
151 """ 143 """
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
254 'platform': { 246 'platform': {
255 'expected: 'PASS', 247 'expected: 'PASS',
256 'actual': 'FAIL', 248 'actual': 'FAIL',
257 'bug': 'crbug.com/11111' 249 'bug': 'crbug.com/11111'
258 } 250 }
259 } 251 }
260 } 252 }
261 253
262 Returns: 254 Returns:
263 A list of test expectations lines with the format: 255 A list of test expectations lines with the format:
264 ['BUG_URL [PLATFORM(S)] TEST_MAME [EXPECTATION(S)]'] 256 ['BUG_URL [PLATFORM(S)] TEST_MAME [EXPECTATION(S)]']
jeffcarp 2017/02/13 20:42:18 I know this isn't part of the CL but should this b
qyearsley 2017/02/14 00:00:51 Yep! Unless mame has some meaning I'm not aware of
265 """ 257 """
266 line_list = [] 258 line_list = []
267 for test_name, port_results in merged_results.iteritems(): 259 for test_name, port_results in merged_results.iteritems():
268 for port_names in port_results: 260 for port_names in sorted(port_results):
269 if test_name.startswith('external'): 261 if test_name.startswith('external'):
270 bug_part = port_results[port_names]['bug'] 262 line_parts = [port_results[port_names]['bug']]
271 specifier_part = '[ %s ]' % ' '.join(self.to_list(port_names )) 263 specifier_part = self.specifier_part(self.to_list(port_names ), test_name)
272 expectations_part = '[ %s ]' % ' '.join(self.get_expectation s(port_results[port_names])) 264 if specifier_part:
273 line = ' '.join([bug_part, specifier_part, test_name, expect ations_part]) 265 line_parts.append(specifier_part)
274 line_list.append(line) 266 line_parts.append(test_name)
267 line_parts.append('[ %s ]' % ' '.join(self.get_expectations( port_results[port_names])))
268 line_list.append(' '.join(line_parts))
275 return line_list 269 return line_list
276 270
271 def specifier_part(self, port_names, test_name):
272 """Returns the specifier part for a new test expectations line.
273
274 Args:
275 port_names: A list of full port names that the line should apply to.
276 test_name: The test name for the expectation line.
277
278 Returns:
279 The specifier part of the new expectation line, e.g. "[ Mac ]".
280 This will be an empty string if the line should apply to all platfor ms.
281 """
282 specifiers = []
283 for name in sorted(port_names):
284 specifiers.append(self.host.builders.version_specifier_for_port_name (name))
285 port = self.host.port_factory.get()
286 specifiers = self.simplify_specifiers(specifiers, port.configuration_spe cifier_macros())
287 specifiers.extend(self.skipped_specifiers(test_name))
qyearsley 2017/02/13 19:54:23 The purpose of this is so that Android is included
288 if not specifiers:
289 return ''
290 return '[ %s ]' % ' '.join(specifiers)
291
277 @staticmethod 292 @staticmethod
278 def to_list(tuple_or_value): 293 def to_list(tuple_or_value):
294 """Converts a tuple to a list, and a string value to a one-item list."""
279 if isinstance(tuple_or_value, tuple): 295 if isinstance(tuple_or_value, tuple):
280 return list(tuple_or_value) 296 return list(tuple_or_value)
281 return [tuple_or_value] 297 return [tuple_or_value]
282 298
299 def skipped_specifiers(self, test_name):
300 """Returns a list of platform specifiers for which the test is skipped." ""
301 # TODO(qyearsley): Change Port.skips_test so that this can be simplified .
302 specifiers = []
303 for port in self.all_try_builder_ports():
304 generic_expectations = TestExpectations(port, tests=[test_name], inc lude_overrides=False)
305 full_expectations = TestExpectations(port, tests=[test_name], includ e_overrides=True)
306 if port.skips_test(test_name, generic_expectations, full_expectation s):
307 specifiers.append(self.host.builders.version_specifier_for_port_ name(port.name()))
308 return specifiers
309
310 @memoized
311 def all_try_builder_ports(self):
312 """Returns a list of Port objects for all try builders."""
313 return [self.host.port_factory.get_from_builder_name(name) for name in s elf.get_try_bots()]
314
315 @staticmethod
316 def simplify_specifiers(specifiers, configuration_specifier_macros): # pyli nt: disable=unused-argument
317 """Converts a list of specifiers to an equivalent and maybe shorter list .
318
319 The input strings are all case-insensitive, but the strings in the
320 return value will all be capitalized.
321
322 Args:
323 specifiers: A list of lower-case specifiers.
324 configuration_specifier_macros: A dict mapping "macros" for
325 groups of specifiers to lists of specific specifiers. In
326 practice, this is a dict mapping operating systems to
327 supported versions, e.g. {"win": ["win7", "win10"]}.
328
329 Returns:
330 A shortened list of specifiers. For example, ["win7", "win10"]
331 would be converted to ["Win"]. If the given list covers all
332 supported platforms, then an empty list is returned.
333 """
334 specifiers = {specifier.lower() for specifier in specifiers}
335 for macro, versions in configuration_specifier_macros.iteritems():
336 versions = set(versions)
337 if versions <= specifiers:
338 specifiers -= versions
339 specifiers.add(macro)
340 if specifiers == set(configuration_specifier_macros):
341 return []
342 return sorted(specifier.capitalize() for specifier in specifiers)
qyearsley 2017/02/13 19:54:23 This is the main part of this change.
343
283 def write_to_test_expectations(self, line_list): 344 def write_to_test_expectations(self, line_list):
284 """Writes to TestExpectations. 345 """Writes to TestExpectations.
285 346
286 The place in the file where the new lines are inserted is after a 347 The place in the file where the new lines are inserted is after a
287 marker comment line. If this marker comment line is not found, it will 348 marker comment line. If this marker comment line is not found, it will
288 be added to the end of the file. 349 be added to the end of the file.
289 350
290 Args: 351 Args:
291 line_list: A list of lines to add to the TestExpectations file. 352 line_list: A list of lines to add to the TestExpectations file.
292 """ 353 """
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
376 437
377 Args: 438 Args:
378 test_path: A file path relative to the layout tests directory. 439 test_path: A file path relative to the layout tests directory.
379 This might correspond to a deleted file or a non-test. 440 This might correspond to a deleted file or a non-test.
380 """ 441 """
381 absolute_path = self.host.filesystem.join(self.finder.layout_tests_dir() , test_path) 442 absolute_path = self.host.filesystem.join(self.finder.layout_tests_dir() , test_path)
382 test_parser = TestParser(absolute_path, self.host) 443 test_parser = TestParser(absolute_path, self.host)
383 if not test_parser.test_doc: 444 if not test_parser.test_doc:
384 return False 445 return False
385 return test_parser.is_jstest() 446 return test_parser.is_jstest()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698