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

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

Issue 2341173002: Move tokenize_line method from TestExpectationParser to TestExpectationLine. (Closed)
Patch Set: Created 4 years, 3 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/models/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 (C) 2010 Google Inc. All rights reserved. 1 # Copyright (C) 2010 Google Inc. All rights reserved.
2 # 2 #
3 # Redistribution and use in source and binary forms, with or without 3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are 4 # modification, are permitted provided that the following conditions are
5 # met: 5 # met:
6 # 6 #
7 # * Redistributions of source code must retain the above copyright 7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer. 8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above 9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer 10 # copyright notice, this list of conditions and the following disclaimer
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
98 else: 98 else:
99 self._all_tests = set() 99 self._all_tests = set()
100 100
101 self._is_lint_mode = is_lint_mode 101 self._is_lint_mode = is_lint_mode
102 102
103 def parse(self, filename, expectations_string): 103 def parse(self, filename, expectations_string):
104 expectation_lines = [] 104 expectation_lines = []
105 line_number = 0 105 line_number = 0
106 for line in expectations_string.split("\n"): 106 for line in expectations_string.split("\n"):
107 line_number += 1 107 line_number += 1
108 test_expectation = self._tokenize_line(filename, line, line_number) 108 test_expectation = TestExpectationLine.tokenize_line(filename, line, line_number)
109 self._parse_line(test_expectation) 109 self._parse_line(test_expectation)
110 expectation_lines.append(test_expectation) 110 expectation_lines.append(test_expectation)
111 return expectation_lines 111 return expectation_lines
112 112
113 def _create_expectation_line(self, test_name, expectations, file_name): 113 def _create_expectation_line(self, test_name, expectations, file_name):
114 expectation_line = TestExpectationLine() 114 expectation_line = TestExpectationLine()
115 expectation_line.original_string = test_name 115 expectation_line.original_string = test_name
116 expectation_line.name = test_name 116 expectation_line.name = test_name
117 expectation_line.filename = file_name 117 expectation_line.filename = file_name
118 expectation_line.expectations = expectations 118 expectation_line.expectations = expectations
119 return expectation_line 119 return expectation_line
120 120
121 def expectation_line_for_test(self, test_name, expectations): 121 def expectation_line_for_test(self, test_name, expectations):
122 expectation_line = self._create_expectation_line(test_name, expectations , '<Bot TestExpectations>') 122 expectation_line = self._create_expectation_line(test_name, expectations , '<Bot TestExpectations>')
123 self._parse_line(expectation_line) 123 self._parse_line(expectation_line)
124 return expectation_line 124 return expectation_line
125 125
126 def expectation_for_skipped_test(self, test_name): 126 def expectation_for_skipped_test(self, test_name):
127 if not self._port.test_exists(test_name): 127 if not self._port.test_exists(test_name):
128 _log.warning('The following test %s from the Skipped list doesn\'t e xist', test_name) 128 _log.warning('The following test %s from the Skipped list doesn\'t e xist', test_name)
129 expectation_line = self._create_expectation_line(test_name, [TestExpecta tionParser.PASS_EXPECTATION], '<Skipped file>') 129 expectation_line = self._create_expectation_line(test_name, [TestExpecta tionParser.PASS_EXPECTATION], '<Skipped file>')
130 expectation_line.expectations = [TestExpectationParser.SKIP_MODIFIER, Te stExpectationParser.WONTFIX_MODIFIER] 130 expectation_line.expectations = [TestExpectationParser.SKIP_MODIFIER, Te stExpectationParser.WONTFIX_MODIFIER]
131 expectation_line.is_skipped_outside_expectations_file = True 131 expectation_line.is_extra_skipped_test = True
132 self._parse_line(expectation_line) 132 self._parse_line(expectation_line)
133 return expectation_line 133 return expectation_line
134 134
135 def _parse_line(self, expectation_line): 135 def _parse_line(self, expectation_line):
136 if not expectation_line.name: 136 if not expectation_line.name:
137 return 137 return
138 138
139 if not self._check_test_exists(expectation_line): 139 if not self._check_test_exists(expectation_line):
140 return 140 return
141 141
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
214 if not expectation_line.is_file: 214 if not expectation_line.is_file:
215 # this is a test category, return all the tests of the category. 215 # this is a test category, return all the tests of the category.
216 expectation_line.matching_tests = [test for test in self._all_tests if test.startswith(expectation_line.path)] 216 expectation_line.matching_tests = [test for test in self._all_tests if test.startswith(expectation_line.path)]
217 return 217 return
218 218
219 # this is a test file, do a quick check if it's in the 219 # this is a test file, do a quick check if it's in the
220 # full test suite. 220 # full test suite.
221 if expectation_line.path in self._all_tests: 221 if expectation_line.path in self._all_tests:
222 expectation_line.matching_tests.append(expectation_line.path) 222 expectation_line.matching_tests.append(expectation_line.path)
223 223
224
225 class TestExpectationLine(object):
226 """Represents a line in test expectations file."""
227
228 def __init__(self):
229 """Initializes a blank-line equivalent of an expectation."""
230 self.original_string = None
231 self.filename = None # this is the path to the expectations file for th is line
232 self.line_numbers = "0"
233 self.name = None # this is the path in the line itself
234 self.path = None # this is the normpath of self.name
235 self.bugs = []
236 self.specifiers = []
237 self.parsed_specifiers = []
238 self.matching_configurations = set()
239 self.expectations = []
240 self.parsed_expectations = set()
241 self.comment = None
242 self.matching_tests = []
243 self.warnings = []
244 self.is_extra_skipped_test = False
245
246 def __str__(self):
247 return "TestExpectationLine{name=%s, matching_configurations=%s, origina l_string=%s}" % (
248 self.name, self.matching_configurations, self.original_string)
249
250 def __eq__(self, other):
251 return (self.original_string == other.original_string
252 and self.filename == other.filename
253 and self.line_numbers == other.line_numbers
254 and self.name == other.name
255 and self.path == other.path
256 and self.bugs == other.bugs
257 and self.specifiers == other.specifiers
258 and self.parsed_specifiers == other.parsed_specifiers
259 and self.matching_configurations == other.matching_configuration s
260 and self.expectations == other.expectations
261 and self.parsed_expectations == other.parsed_expectations
262 and self.comment == other.comment
263 and self.matching_tests == other.matching_tests
264 and self.warnings == other.warnings
265 and self.is_extra_skipped_test == other.is_extra_skipped_test)
266
267 def is_invalid(self):
268 return bool(self.warnings and self.warnings != [TestExpectationParser.MI SSING_BUG_WARNING])
269
270 def is_flaky(self):
271 return len(self.parsed_expectations) > 1
272
273 def is_comment(self):
274 return bool(re.match(r"^\s*#.*$", self.original_string))
275
276 def is_whitespace(self):
277 return not self.original_string.strip()
278
279
224 # FIXME: Update the original specifiers and remove this once the old syntax is gone. 280 # FIXME: Update the original specifiers and remove this once the old syntax is gone.
225 _configuration_tokens_list = [ 281 _configuration_tokens_list = [
226 'Mac', 'Mac10.9', 'Mac10.10', 'Mac10.11', 'Retina', 282 'Mac', 'Mac10.9', 'Mac10.10', 'Mac10.11', 'Retina',
227 'Win', 'Win7', 'Win10', 283 'Win', 'Win7', 'Win10',
228 'Linux', 'Precise', 'Trusty', 284 'Linux', 'Precise', 'Trusty',
229 'Android', 285 'Android',
230 'Release', 286 'Release',
231 'Debug', 287 'Debug',
232 ] 288 ]
233 289
234 _configuration_tokens = dict((token, token.upper()) for token in _configurat ion_tokens_list) 290 _configuration_tokens = dict((token, token.upper()) for token in _configurat ion_tokens_list)
235 _inverted_configuration_tokens = dict((value, name) for name, value in _conf iguration_tokens.iteritems()) 291 _inverted_configuration_tokens = dict((value, name) for name, value in _conf iguration_tokens.iteritems())
236 292
237 # FIXME: Update the original specifiers list and remove this once the old sy ntax is gone. 293 # FIXME: Update the original specifiers list and remove this once the old sy ntax is gone.
238 _expectation_tokens = { 294 _expectation_tokens = {
239 'Crash': 'CRASH', 295 'Crash': 'CRASH',
240 'Leak': 'LEAK', 296 'Leak': 'LEAK',
241 'Failure': 'FAIL', 297 'Failure': 'FAIL',
242 MISSING_KEYWORD: 'MISSING', 298 MISSING_KEYWORD: 'MISSING',
243 'Pass': 'PASS', 299 'Pass': 'PASS',
244 'Rebaseline': 'REBASELINE', 300 'Rebaseline': 'REBASELINE',
245 NEEDS_REBASELINE_KEYWORD: 'NEEDSREBASELINE', 301 NEEDS_REBASELINE_KEYWORD: 'NEEDSREBASELINE',
246 NEEDS_MANUAL_REBASELINE_KEYWORD: 'NEEDSMANUALREBASELINE', 302 NEEDS_MANUAL_REBASELINE_KEYWORD: 'NEEDSMANUALREBASELINE',
247 'Skip': 'SKIP', 303 'Skip': 'SKIP',
248 'Slow': 'SLOW', 304 'Slow': 'SLOW',
249 'Timeout': 'TIMEOUT', 305 'Timeout': 'TIMEOUT',
250 'WontFix': 'WONTFIX', 306 'WontFix': 'WONTFIX',
251 } 307 }
252 308
253 _inverted_expectation_tokens = dict( 309 inverted_expectation_tokens = dict(
254 [(value, name) for name, value in _expectation_tokens.iteritems()] + 310 [(value, name) for name, value in _expectation_tokens.iteritems()] +
255 [('TEXT', 'Failure'), ('IMAGE', 'Failure'), ('IMAGE+TEXT', 'Failure'), ( 'AUDIO', 'Failure')]) 311 [('TEXT', 'Failure'), ('IMAGE', 'Failure'), ('IMAGE+TEXT', 'Failure'), ( 'AUDIO', 'Failure')])
256 312
257 # FIXME: Seems like these should be classmethods on TestExpectationLine inst ead of TestExpectationParser.
258 @classmethod 313 @classmethod
259 def _tokenize_line(cls, filename, expectation_string, line_number): 314 def tokenize_line(cls, filename, expectation_string, line_number):
260 """Tokenizes a line from TestExpectations and returns an unparsed TestEx pectationLine instance using the old format. 315 """Tokenizes a line from TestExpectations and returns an unparsed TestEx pectationLine instance using the old format.
261 316
262 The new format for a test expectation line is: 317 The new format for a test expectation line is:
263 318
264 [[bugs] [ "[" <configuration specifiers> "]" <name> [ "[" <expectations> "]" ["#" <comment>] 319 [[bugs] [ "[" <configuration specifiers> "]" <name> [ "[" <expectations> "]" ["#" <comment>]
265 320
266 Any errant whitespace is not preserved. 321 Any errant whitespace is not preserved.
267 """ 322 """
268 expectation_line = TestExpectationLine() 323 expectation_line = TestExpectationLine()
269 expectation_line.original_string = expectation_string 324 expectation_line.original_string = expectation_string
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
380 if not expectations and not has_unrecognized_expectation: 435 if not expectations and not has_unrecognized_expectation:
381 warnings.append('Missing expectations.') 436 warnings.append('Missing expectations.')
382 437
383 expectation_line.bugs = bugs 438 expectation_line.bugs = bugs
384 expectation_line.specifiers = specifiers 439 expectation_line.specifiers = specifiers
385 expectation_line.expectations = expectations 440 expectation_line.expectations = expectations
386 expectation_line.name = name 441 expectation_line.name = name
387 expectation_line.warnings = warnings 442 expectation_line.warnings = warnings
388 return expectation_line 443 return expectation_line
389 444
390 @classmethod
391 def _split_space_separated(cls, space_separated_string):
392 """Splits a space-separated string into an array."""
393 return [part.strip() for part in space_separated_string.strip().split(' ')]
qyearsley 2016/09/15 21:02:31 Note: This CL removes this method which appears un
394
395
396 class TestExpectationLine(object):
397 """Represents a line in test expectations file."""
398
399 def __init__(self):
400 """Initializes a blank-line equivalent of an expectation."""
401 self.original_string = None
402 self.filename = None # this is the path to the expectations file for th is line
403 self.line_numbers = "0"
404 self.name = None # this is the path in the line itself
405 self.path = None # this is the normpath of self.name
406 self.bugs = []
407 self.specifiers = []
408 self.parsed_specifiers = []
409 self.matching_configurations = set()
410 self.expectations = []
411 self.parsed_expectations = set()
412 self.comment = None
413 self.matching_tests = []
414 self.warnings = []
415 self.is_skipped_outside_expectations_file = False
qyearsley 2016/09/15 21:02:31 Note: This CL renames this variable to is_extra_sk
416
417 def __str__(self):
418 return "TestExpectationLine{name=%s, matching_configurations=%s, origina l_string=%s}" % (
419 self.name, self.matching_configurations, self.original_string)
420
421 def __eq__(self, other):
422 return (self.original_string == other.original_string
423 and self.filename == other.filename
424 and self.line_numbers == other.line_numbers
425 and self.name == other.name
426 and self.path == other.path
427 and self.bugs == other.bugs
428 and self.specifiers == other.specifiers
429 and self.parsed_specifiers == other.parsed_specifiers
430 and self.matching_configurations == other.matching_configuration s
431 and self.expectations == other.expectations
432 and self.parsed_expectations == other.parsed_expectations
433 and self.comment == other.comment
434 and self.matching_tests == other.matching_tests
435 and self.warnings == other.warnings
436 and self.is_skipped_outside_expectations_file == other.is_skippe d_outside_expectations_file)
437
438 def is_invalid(self):
439 return bool(self.warnings and self.warnings != [TestExpectationParser.MI SSING_BUG_WARNING])
440
441 def is_flaky(self):
442 return len(self.parsed_expectations) > 1
443
444 def is_comment(self):
445 return bool(re.match(r"^\s*#.*$", self.original_string))
446
447 def is_whitespace(self):
448 return not self.original_string.strip()
449
450 @staticmethod 445 @staticmethod
451 def create_passing_expectation(test): 446 def create_passing_expectation(test):
452 expectation_line = TestExpectationLine() 447 expectation_line = TestExpectationLine()
453 expectation_line.name = test 448 expectation_line.name = test
454 expectation_line.path = test 449 expectation_line.path = test
455 expectation_line.parsed_expectations = set([PASS]) 450 expectation_line.parsed_expectations = set([PASS])
456 expectation_line.expectations = set(['PASS']) 451 expectation_line.expectations = set(['PASS'])
457 expectation_line.matching_tests = [test] 452 expectation_line.matching_tests = [test]
458 return expectation_line 453 return expectation_line
459 454
(...skipping 17 matching lines...) Expand all
477 result.name = line1.name 472 result.name = line1.name
478 result.path = line1.path 473 result.path = line1.path
479 result.parsed_expectations = set(line1.parsed_expectations) | set(line2. parsed_expectations) 474 result.parsed_expectations = set(line1.parsed_expectations) | set(line2. parsed_expectations)
480 result.expectations = list(set(line1.expectations) | set(line2.expectati ons)) 475 result.expectations = list(set(line1.expectations) | set(line2.expectati ons))
481 result.bugs = list(set(line1.bugs) | set(line2.bugs)) 476 result.bugs = list(set(line1.bugs) | set(line2.bugs))
482 result.specifiers = list(set(line1.specifiers) | set(line2.specifiers)) 477 result.specifiers = list(set(line1.specifiers) | set(line2.specifiers))
483 result.parsed_specifiers = list(set(line1.parsed_specifiers) | set(line2 .parsed_specifiers)) 478 result.parsed_specifiers = list(set(line1.parsed_specifiers) | set(line2 .parsed_specifiers))
484 result.matching_configurations = set(line1.matching_configurations) | se t(line2.matching_configurations) 479 result.matching_configurations = set(line1.matching_configurations) | se t(line2.matching_configurations)
485 result.matching_tests = list(list(set(line1.matching_tests) | set(line2. matching_tests))) 480 result.matching_tests = list(list(set(line1.matching_tests) | set(line2. matching_tests)))
486 result.warnings = list(set(line1.warnings) | set(line2.warnings)) 481 result.warnings = list(set(line1.warnings) | set(line2.warnings))
487 result.is_skipped_outside_expectations_file = (line1.is_skipped_outside_ expectations_file or 482 result.is_extra_skipped_test = line1.is_extra_skipped_test or line2.is_e xtra_skipped_test
488 line2.is_skipped_outside_ expectations_file)
489 return result 483 return result
490 484
491 def to_string(self, test_configuration_converter=None, include_specifiers=Tr ue, 485 def to_string(self, test_configuration_converter=None, include_specifiers=Tr ue,
492 include_expectations=True, include_comment=True): 486 include_expectations=True, include_comment=True):
493 parsed_expectation_to_string = dict( 487 parsed_expectation_to_string = dict(
494 [[parsed_expectation, expectation_string] 488 [[parsed_expectation, expectation_string]
495 for expectation_string, parsed_expectation in TestExpectations.EXPE CTATIONS.items()]) 489 for expectation_string, parsed_expectation in TestExpectations.EXPE CTATIONS.items()])
496 490
497 if self.is_invalid(): 491 if self.is_invalid():
498 return self.original_string or '' 492 return self.original_string or ''
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
531 return ' '.join(result) 525 return ' '.join(result)
532 526
533 @staticmethod 527 @staticmethod
534 def _filter_redundant_expectations(expectations): 528 def _filter_redundant_expectations(expectations):
535 if set(expectations) == set(['Pass', 'Skip']): 529 if set(expectations) == set(['Pass', 'Skip']):
536 return ['Skip'] 530 return ['Skip']
537 if set(expectations) == set(['Pass', 'Slow']): 531 if set(expectations) == set(['Pass', 'Slow']):
538 return ['Slow'] 532 return ['Slow']
539 return expectations 533 return expectations
540 534
541 @staticmethod 535 @classmethod
542 def _format_line(bugs, specifiers, name, expectations, comment, include_spec ifiers=True, 536 def _format_line(cls, bugs, specifiers, name, expectations, comment, include _specifiers=True,
543 include_expectations=True, include_comment=True): 537 include_expectations=True, include_comment=True):
544 new_specifiers = [] 538 new_specifiers = []
545 new_expectations = [] 539 new_expectations = []
546 for specifier in specifiers: 540 for specifier in specifiers:
547 # FIXME: Make this all work with the mixed-cased specifiers (e.g. Wo ntFix, Slow, etc). 541 # FIXME: Make this all work with the mixed-cased specifiers (e.g. Wo ntFix, Slow, etc).
548 specifier = specifier.upper() 542 specifier = specifier.upper()
549 new_specifiers.append(TestExpectationParser._inverted_configuration_ tokens.get(specifier, specifier)) 543 new_specifiers.append(cls._inverted_configuration_tokens.get(specifi er, specifier))
qyearsley 2016/09/15 21:02:31 Note: This CL changes this to a classmethod to ind
550 544
551 for expectation in expectations: 545 for expectation in expectations:
552 expectation = expectation.upper() 546 expectation = expectation.upper()
553 new_expectations.append(TestExpectationParser._inverted_expectation_ tokens.get(expectation, expectation)) 547 new_expectations.append(cls.inverted_expectation_tokens.get(expectat ion, expectation))
554 548
555 result = '' 549 result = ''
556 if include_specifiers and (bugs or new_specifiers): 550 if include_specifiers and (bugs or new_specifiers):
557 if bugs: 551 if bugs:
558 result += ' '.join(bugs) + ' ' 552 result += ' '.join(bugs) + ' '
559 if new_specifiers: 553 if new_specifiers:
560 result += '[ %s ] ' % ' '.join(new_specifiers) 554 result += '[ %s ] ' % ' '.join(new_specifiers)
561 result += name 555 result += name
562 if include_expectations and new_expectations: 556 if include_expectations and new_expectations:
563 new_expectations = TestExpectationLine._filter_redundant_expectation s(new_expectations) 557 new_expectations = TestExpectationLine._filter_redundant_expectation s(new_expectations)
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
664 def get_expectation_line(self, test): 658 def get_expectation_line(self, test):
665 return self._test_to_expectation_line.get(test) 659 return self._test_to_expectation_line.get(test)
666 660
667 def get_expectations(self, test): 661 def get_expectations(self, test):
668 return self._test_to_expectations[test] 662 return self._test_to_expectations[test]
669 663
670 def get_expectations_string(self, test): 664 def get_expectations_string(self, test):
671 """Returns the expectations for the given test as an uppercase string. 665 """Returns the expectations for the given test as an uppercase string.
672 If there are no expectations for the test, then "PASS" is returned. 666 If there are no expectations for the test, then "PASS" is returned.
673 """ 667 """
674 if self.get_expectation_line(test).is_skipped_outside_expectations_file: 668 if self.get_expectation_line(test).is_extra_skipped_test:
675 return 'NOTRUN' 669 return 'NOTRUN'
676 670
677 expectations = self.get_expectations(test) 671 expectations = self.get_expectations(test)
678 retval = [] 672 retval = []
679 673
680 # FIXME: WontFix should cause the test to get skipped without artificial ly adding SKIP to the expectations list. 674 # FIXME: WontFix should cause the test to get skipped without artificial ly adding SKIP to the expectations list.
681 if WONTFIX in expectations and SKIP in expectations: 675 if WONTFIX in expectations and SKIP in expectations:
682 expectations.remove(SKIP) 676 expectations.remove(SKIP)
683 677
684 for expectation in expectations: 678 for expectation in expectations:
(...skipping 518 matching lines...) Expand 10 before | Expand all | Expand 10 after
1203 # If reconstitute_only_these is an empty list, we want to return ori ginal_string. 1197 # If reconstitute_only_these is an empty list, we want to return ori ginal_string.
1204 # So we need to compare reconstitute_only_these to None, not just ch eck if it's falsey. 1198 # So we need to compare reconstitute_only_these to None, not just ch eck if it's falsey.
1205 if reconstitute_only_these is None or expectation_line in reconstitu te_only_these: 1199 if reconstitute_only_these is None or expectation_line in reconstitu te_only_these:
1206 return expectation_line.to_string(test_configuration_converter) 1200 return expectation_line.to_string(test_configuration_converter)
1207 return expectation_line.original_string 1201 return expectation_line.original_string
1208 1202
1209 def nones_out(expectation_line): 1203 def nones_out(expectation_line):
1210 return expectation_line is not None 1204 return expectation_line is not None
1211 1205
1212 return "\n".join(filter(nones_out, map(serialize, expectation_lines))) 1206 return "\n".join(filter(nones_out, map(serialize, expectation_lines)))
OLDNEW
« no previous file with comments | « no previous file | third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_expectations_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698