Changeset 91103 in webkit
- Timestamp:
- Jul 15, 2011 1:41:18 PM (13 years ago)
- Location:
- trunk/Tools
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Tools/ChangeLog
r91092 r91103 1 2011-07-15 Dimitri Glazkov <dglazkov@chromium.org> 2 3 Clean up test_expectations.py after refactorings. 4 https://bugs.webkit.org/show_bug.cgi?id=64620 5 6 * Renamed all variables holding TestExpectationLine to expectation_line to avoid confusion with 7 its sub-part, the actual expectation. 8 * Renamed all references to options to modifier to eliminate dual terminology. 9 * Made a bunch of parser constants and changed all callsites to use them. 10 * Various other minor clean-ups. 11 12 Reviewed by Adam Barth. 13 14 * Scripts/webkitpy/layout_tests/models/test_expectations.py: Cleaned up stuff. 15 * Scripts/webkitpy/layout_tests/models/test_expectations_unittest.py: Changed callsites after cleanup. 16 * Scripts/webkitpy/style/checkers/test_expectations_unittest.py: Ditto. 17 1 18 2011-07-15 Dimitri Glazkov <dglazkov@chromium.org> 2 19 -
trunk/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py
r91092 r91103 166 166 """Provides parsing facilities for lines in the test_expectation.txt file.""" 167 167 168 BUG_MODIFIER_PREFIX = 'bug' 169 BUG_MODIFIER_REGEX = 'bug\d+' 170 REBASELINE_MODIFIER = 'rebaseline' 171 SKIP_MODIFIER = 'skip' 172 SLOW_MODIFIER = 'slow' 173 WONTFIX_MODIFIER = 'wontfix' 174 175 TIMEOUT_EXPECTATION = 'timeout' 176 168 177 def __init__(self, port, test_config, full_test_list, allow_rebaseline_modifier): 169 178 self._port = port … … 172 181 self._allow_rebaseline_modifier = allow_rebaseline_modifier 173 182 174 def parse(self, expectation): 175 expectation.num_matches = self._check_options(self._matcher, expectation) 176 if expectation.num_matches == ModifierMatcher.NO_MATCH: 183 def parse(self, expectation_line): 184 self._matcher.match(expectation_line) 185 self._check_semantics(expectation_line) 186 187 if expectation_line.num_matches == ModifierMatcher.NO_MATCH: 177 188 return 178 189 179 self._check_ options_against_expectations(expectation)180 181 if self._check_path_does_not_exist(expectation ):190 self._check_modifiers_against_expectations(expectation_line) 191 192 if self._check_path_does_not_exist(expectation_line): 182 193 return 183 194 184 195 if not self._full_test_list: 185 expectation .matching_tests = [expectation.name]186 else: 187 expectation .matching_tests = self._collect_matching_tests(expectation.name)188 189 expectation .parsed_modifiers = [modifier for modifier in expectation.modifiers if modifier in TestExpectations.MODIFIERS]190 self._parse_expectations(expectation )196 expectation_line.matching_tests = [expectation_line.name] 197 else: 198 expectation_line.matching_tests = self._collect_matching_tests(expectation_line.name) 199 200 expectation_line.parsed_modifiers = [modifier for modifier in expectation_line.modifiers if modifier in TestExpectations.MODIFIERS] 201 self._parse_expectations(expectation_line) 191 202 192 203 def _parse_expectations(self, expectation_line): … … 200 211 expectation_line.parsed_expectations = result 201 212 202 def _check_options(self, matcher, expectation): 203 match_result = matcher.match(expectation) 204 self._check_semantics(expectation) 205 return match_result.num_matches 206 207 def _check_semantics(self, expectation): 208 has_wontfix = 'wontfix' in expectation.modifiers 213 def _check_semantics(self, expectation_line): 214 has_wontfix = self.WONTFIX_MODIFIER in expectation_line.modifiers 209 215 has_bug = False 210 for opt in expectation.modifiers:211 if opt.startswith('bug'):216 for modifier in expectation_line.modifiers: 217 if modifier.startswith(self.BUG_MODIFIER_PREFIX): 212 218 has_bug = True 213 if re.match( 'bug\d+', opt):214 expectation .errors.append('BUG\d+ is not allowed, must be one of BUGCR\d+, BUGWK\d+, BUGV8_\d+, or a non-numeric bug identifier.')219 if re.match(self.BUG_MODIFIER_REGEX, modifier): 220 expectation_line.errors.append('BUG\d+ is not allowed, must be one of BUGCR\d+, BUGWK\d+, BUGV8_\d+, or a non-numeric bug identifier.') 215 221 216 222 if not has_bug and not has_wontfix: 217 expectation .warnings.append('Test lacks BUG modifier.')218 219 if self._allow_rebaseline_modifier and 'rebaseline' in expectation.modifiers:220 expectation .errors.append('REBASELINE should only be used for running rebaseline.py. Cannot be checked in.')221 222 def _check_ options_against_expectations(self, expectation):223 if 'slow' in expectation.modifiers and 'timeout' in expectation.expectations:224 expectation .errors.append('A test can not be both SLOW and TIMEOUT. If it times out indefinitely, then it should be just TIMEOUT.')225 226 def _check_path_does_not_exist(self, expectation ):223 expectation_line.warnings.append('Test lacks BUG modifier.') 224 225 if self._allow_rebaseline_modifier and self.REBASELINE_MODIFIER in expectation_line.modifiers: 226 expectation_line.errors.append('REBASELINE should only be used for running rebaseline.py. Cannot be checked in.') 227 228 def _check_modifiers_against_expectations(self, expectation_line): 229 if self.SLOW_MODIFIER in expectation_line.modifiers and self.TIMEOUT_EXPECTATION in expectation_line.expectations: 230 expectation_line.errors.append('A test can not be both SLOW and TIMEOUT. If it times out indefinitely, then it should be just TIMEOUT.') 231 232 def _check_path_does_not_exist(self, expectation_line): 227 233 # WebKit's way of skipping tests is to add a -disabled suffix. 228 234 # So we should consider the path existing if the path or the 229 235 # -disabled version exists. 230 if (not self._port.test_exists(expectation .name)231 and not self._port.test_exists(expectation .name + '-disabled')):236 if (not self._port.test_exists(expectation_line.name) 237 and not self._port.test_exists(expectation_line.name + '-disabled')): 232 238 # Log a warning here since you hit this case any 233 239 # time you update test_expectations.txt without syncing 234 240 # the LayoutTests directory 235 expectation .warnings.append('Path does not exist.')241 expectation_line.warnings.append('Path does not exist.') 236 242 return True 237 243 return False … … 270 276 271 277 """ 272 expectation = TestExpectationLine()278 expectation_line = TestExpectationLine() 273 279 comment_index = expectation_string.find("//") 274 280 if comment_index == -1: 275 281 comment_index = len(expectation_string) 276 282 else: 277 expectation .comment = expectation_string[comment_index + 2:]283 expectation_line.comment = expectation_string[comment_index + 2:] 278 284 279 285 remaining_string = re.sub(r"\s+", " ", expectation_string[:comment_index].strip()) 280 286 if len(remaining_string) == 0: 281 return expectation 287 return expectation_line 282 288 283 289 parts = remaining_string.split(':') 284 290 if len(parts) != 2: 285 expectation .errors.append("Missing a ':'" if len(parts) < 2 else "Extraneous ':'")291 expectation_line.errors.append("Missing a ':'" if len(parts) < 2 else "Extraneous ':'") 286 292 else: 287 293 test_and_expectation = parts[1].split('=') 288 294 if len(test_and_expectation) != 2: 289 expectation .errors.append("Missing expectations" if len(test_and_expectation) < 2 else "Extraneous '='")290 291 if expectation .is_malformed():292 expectation .comment = expectation_string293 else: 294 expectation .modifiers = cls._split_space_separated(parts[0])295 expectation .name = test_and_expectation[0].strip()296 expectation .expectations = cls._split_space_separated(test_and_expectation[1])297 298 return expectation 295 expectation_line.errors.append("Missing expectations" if len(test_and_expectation) < 2 else "Extraneous '='") 296 297 if expectation_line.is_malformed(): 298 expectation_line.comment = expectation_string 299 else: 300 expectation_line.modifiers = cls._split_space_separated(parts[0]) 301 expectation_line.name = test_and_expectation[0].strip() 302 expectation_line.expectations = cls._split_space_separated(test_and_expectation[1]) 303 304 return expectation_line 299 305 300 306 @classmethod 301 307 def tokenize_list(cls, expectations_string): 302 308 """Returns a list of TestExpectationLines, one for each line in expectations_string.""" 303 expectation s = []309 expectation_lines = [] 304 310 for line in expectations_string.split("\n"): 305 expectation s.append(cls.tokenize(line))306 return expectation s311 expectation_lines.append(cls.tokenize(line)) 312 return expectation_lines 307 313 308 314 @classmethod … … 346 352 self._test_to_expectations = {} 347 353 348 # Maps a test to its list of options (string values) 349 self._test_to_options = {} 350 351 # Maps a test to its list of modifiers: the constants associated with 352 # the options minus any bug or platform strings 354 # Maps a test to list of its modifiers (string values) 353 355 self._test_to_modifiers = {} 354 356 … … 395 397 return self._timeline_to_tests[timeline] 396 398 397 def get_options(self, test): 398 """This returns the entire set of options for the given test 399 (the modifiers plus the BUGXXXX identifier). This is used by the 400 LTTF dashboard.""" 401 return self._test_to_options[test] 399 def get_modifiers(self, test): 400 """This returns modifiers for the given test (the modifiers plus the BUGXXXX identifier). This is used by the LTTF dashboard.""" 401 return self._test_to_modifiers[test] 402 402 403 403 def has_modifier(self, test, modifier): … … 410 410 return self._test_to_expectations[test] 411 411 412 def add_tests(self, lineno, expectation , overrides_allowed):412 def add_tests(self, lineno, expectation_line, overrides_allowed): 413 413 """Returns a list of errors, encountered while matching modifiers.""" 414 414 415 if expectation .is_invalid():415 if expectation_line.is_invalid(): 416 416 return 417 417 418 for test in expectation .matching_tests:419 if self._already_seen_better_match(test, expectation , lineno, overrides_allowed):418 for test in expectation_line.matching_tests: 419 if self._already_seen_better_match(test, expectation_line, lineno, overrides_allowed): 420 420 continue 421 421 422 self._clear_expectations_for_test(test, expectation .name)423 self._test_list_paths[test] = (self._port.normalize_test_name(expectation .name), expectation.num_matches, lineno)424 self.add_test(test, expectation , overrides_allowed)422 self._clear_expectations_for_test(test, expectation_line.name) 423 self._test_list_paths[test] = (self._port.normalize_test_name(expectation_line.name), expectation_line.num_matches, lineno) 424 self.add_test(test, expectation_line, overrides_allowed) 425 425 426 426 def add_test(self, test, expectation_line, overrides_allowed): … … 440 440 self._expectation_to_tests[expectation].add(test) 441 441 442 self._test_to_options[test] = expectation_line.modifiers 443 self._test_to_modifiers[test] = set() 442 self._test_to_modifiers[test] = expectation_line.modifiers 444 443 for modifier in expectation_line.parsed_modifiers: 445 444 mod_value = TestExpectations.MODIFIERS[modifier] 446 445 self._modifier_to_tests[mod_value].add(test) 447 self._test_to_modifiers[test].add(mod_value) 448 449 if 'wontfix' in expectation_line.parsed_modifiers: 446 447 if TestExpectationParser.WONTFIX_MODIFIER in expectation_line.parsed_modifiers: 450 448 self._timeline_to_tests[WONTFIX].add(test) 451 449 else: 452 450 self._timeline_to_tests[NOW].add(test) 453 451 454 if 'skip'in expectation_line.parsed_modifiers:452 if TestExpectationParser.SKIP_MODIFIER in expectation_line.parsed_modifiers: 455 453 self._result_type_to_tests[SKIP].add(test) 456 454 elif expectation_line.parsed_expectations == set([PASS]): … … 488 486 set_of_tests.remove(test) 489 487 490 def _already_seen_better_match(self, test, expectation , lineno, overrides_allowed):488 def _already_seen_better_match(self, test, expectation_line, lineno, overrides_allowed): 491 489 """Returns whether we've seen a better match already in the file. 492 490 493 Returns True if we've already seen a expectation .name that matches more of the test491 Returns True if we've already seen a expectation_line.name that matches more of the test 494 492 than this path does 495 493 """ … … 500 498 501 499 prev_base_path, prev_num_matches, prev_lineno = self._test_list_paths[test] 502 base_path = self._port.normalize_test_name(expectation .name)500 base_path = self._port.normalize_test_name(expectation_line.name) 503 501 504 502 if len(prev_base_path) > len(base_path): … … 533 531 # to be warnings and return False". 534 532 535 if prev_num_matches == expectation .num_matches:536 expectation .errors.append('Duplicate or ambiguous %s.' % expectation_source)533 if prev_num_matches == expectation_line.num_matches: 534 expectation_line.errors.append('Duplicate or ambiguous %s.' % expectation_source) 537 535 return True 538 536 539 if prev_num_matches < expectation .num_matches:540 expectation .errors.append('More specific entry on line %d overrides line %d' % (lineno, prev_lineno))537 if prev_num_matches < expectation_line.num_matches: 538 expectation_line.errors.append('More specific entry on line %d overrides line %d' % (lineno, prev_lineno)) 541 539 # FIXME: return False if we want more specific to win. 542 540 return True 543 541 544 expectation .errors.append('More specific entry on line %d overrides line %d' % (prev_lineno, lineno))542 expectation_line.errors.append('More specific entry on line %d overrides line %d' % (prev_lineno, lineno)) 545 543 return True 546 544 … … 557 555 ... 558 556 559 To add other options:557 To add modifiers: 560 558 SKIP : LayoutTests/fast/js/no-good.js = TIMEOUT PASS 561 559 DEBUG : LayoutTests/fast/js/no-good.js = TIMEOUT PASS … … 611 609 BUILD_TYPES = ('debug', 'release') 612 610 613 MODIFIERS = { 'skip': SKIP,614 'wontfix': WONTFIX,615 'slow': SLOW,616 'rebaseline': REBASELINE,611 MODIFIERS = {TestExpectationParser.SKIP_MODIFIER: SKIP, 612 TestExpectationParser.WONTFIX_MODIFIER: WONTFIX, 613 TestExpectationParser.SLOW_MODIFIER: SLOW, 614 TestExpectationParser.REBASELINE_MODIFIER: REBASELINE, 617 615 'none': NONE} 618 616 619 TIMELINES = { 'wontfix': WONTFIX,617 TIMELINES = {TestExpectationParser.WONTFIX_MODIFIER: WONTFIX, 620 618 'now': NOW} 621 619 … … 699 697 700 698 # FIXME: Change the callsites to use TestExpectationsModel and remove. 701 def get_ options(self, test):702 return self._model.get_ options(test)699 def get_modifiers(self, test): 700 return self._model.get_modifiers(test) 703 701 704 702 # FIXME: Change the callsites to use TestExpectationsModel and remove. … … 783 781 return TestExpectationSerializer.list_to_string(filter(without_rebaseline_modifier, self._expectations)) 784 782 785 def _add_to_all_expectations(self, test, options, expectations):783 def _add_to_all_expectations(self, test, modifiers, expectations): 786 784 if not test in self._all_expectations: 787 785 self._all_expectations[test] = [] 788 786 self._all_expectations[test].append( 789 ModifiersAndExpectations( options, expectations))787 ModifiersAndExpectations(modifiers, expectations)) 790 788 791 789 def _add_expectations(self, expectation_list, overrides_allowed): … … 805 803 806 804 class ModifierMatchResult(object): 807 def __init__(self, options):805 def __init__(self, modifiers): 808 806 self.num_matches = ModifierMatcher.NO_MATCH 809 self. options = options810 self. modifiers = []807 self._modifiers = modifiers 808 self._matched_modifiers = [] 811 809 self._matched_regexes = set() 812 810 self._matched_macros = set() … … 863 861 DUPLICATE_REGEXES_ALLOWED = ['bug\w+'] 864 862 865 # Magic value returned when the options don't match.863 # Magic value returned when the modifiers don't match. 866 864 NO_MATCH = -1 867 865 … … 892 890 self._all_modifiers.add(modifier) 893 891 894 def match(self, expectation ):892 def match(self, expectation_line): 895 893 """Checks a expectation.modifiers against the config set in the constructor. 896 894 Options may be either actual modifier strings, "macro" strings 897 895 that get expanded to a list of modifiers, or strings that are allowed 898 to be ignored. All of the options must be passed in in lower case.896 to be ignored. All of the modifiers must be passed in in lower case. 899 897 900 898 Returns the number of matching categories, or NO_MATCH (-1) if it 901 899 doesn't match or there were errors found. Matches are prioritized 902 900 by the number of matching categories, because the more specific 903 the options list, the more categories will match. 904 905 The results of the most recent match are available in the 'options', 906 'modifiers', 'num_matches', 'errors', and 'warnings' properties. 901 the modifier list, the more categories will match. 907 902 """ 908 old_error_count = len(expectation.errors) 909 result = ModifierMatchResult(expectation.modifiers) 910 self._parse(expectation, result) 911 if old_error_count != len(expectation.errors): 912 return result 913 self._count_matches(result) 914 return result 915 916 def _parse(self, expectation, result): 903 old_error_count = len(expectation_line.errors) 904 result = ModifierMatchResult(expectation_line.modifiers) 905 self._parse(expectation_line, result) 906 if old_error_count == len(expectation_line.errors): 907 self._count_matches(result) 908 expectation_line.num_matches = result.num_matches 909 910 def _parse(self, expectation_line, result): 917 911 # FIXME: Should we warn about lines having every value in a category? 918 for option in result.options:919 self._parse_one(expectation , option, result)920 921 def _parse_one(self, expectation , option, result):922 if optionin self._all_modifiers:923 self._add_modifier(expectation , option, result)924 elif optionin self.macros:925 self._expand_macro(expectation , option, result)926 elif not self._matches_any_regex(expectation , option, result):927 expectation .errors.append("Unrecognized option '%s'" % option)928 929 def _add_modifier(self, expectation , option, result):930 if option in result.modifiers:931 expectation .errors.append("More than one '%s'" % option)932 else: 933 result. modifiers.append(option)934 935 def _expand_macro(self, expectation , macro, result):912 for modifier in result._modifiers: 913 self._parse_one(expectation_line, modifier, result) 914 915 def _parse_one(self, expectation_line, modifier, result): 916 if modifier in self._all_modifiers: 917 self._add_modifier(expectation_line, modifier, result) 918 elif modifier in self.macros: 919 self._expand_macro(expectation_line, modifier, result) 920 elif not self._matches_any_regex(expectation_line, modifier, result): 921 expectation_line.errors.append("Unrecognized modifier '%s'" % modifier) 922 923 def _add_modifier(self, expectation_line, modifier, result): 924 if modifier in result._matched_modifiers: 925 expectation_line.errors.append("More than one '%s'" % modifier) 926 else: 927 result._matched_modifiers.append(modifier) 928 929 def _expand_macro(self, expectation_line, macro, result): 936 930 if macro in result._matched_macros: 937 expectation .errors.append("More than one '%s'" % macro)931 expectation_line.errors.append("More than one '%s'" % macro) 938 932 return 939 933 940 934 mods = [] 941 935 for modifier in self.macros[macro]: 942 if modifier in result. options:943 expectation .errors.append("Can't specify both modifier '%s' and macro '%s'" % (modifier, macro))936 if modifier in result._modifiers: 937 expectation_line.errors.append("Can't specify both modifier '%s' and macro '%s'" % (modifier, macro)) 944 938 else: 945 939 mods.append(modifier) 946 940 result._matched_macros.add(macro) 947 result. modifiers.extend(mods)948 949 def _matches_any_regex(self, expectation , option, result):941 result._matched_modifiers.extend(mods) 942 943 def _matches_any_regex(self, expectation_line, modifier, result): 950 944 for regex_str, pattern in self.regexes_to_ignore.iteritems(): 951 if pattern.match( option):952 self._handle_regex_match(expectation , regex_str, result)945 if pattern.match(modifier): 946 self._handle_regex_match(expectation_line, regex_str, result) 953 947 return True 954 948 return False 955 949 956 def _handle_regex_match(self, expectation , regex_str, result):950 def _handle_regex_match(self, expectation_line, regex_str, result): 957 951 if (regex_str in result._matched_regexes and 958 952 regex_str not in self.DUPLICATE_REGEXES_ALLOWED): 959 expectation .errors.append("More than one optionmatching '%s'" %953 expectation_line.errors.append("More than one modifier matching '%s'" % 960 954 regex_str) 961 955 else: … … 964 958 def _count_matches(self, result): 965 959 """Returns the number of modifiers that match the test config.""" 966 categorized_modifiers = self._group_by_category(result. modifiers)960 categorized_modifiers = self._group_by_category(result._matched_modifiers) 967 961 result.num_matches = 0 968 962 for category, modifier in self.test_config.items(): -
trunk/Tools/Scripts/webkitpy/layout_tests/models/test_expectations_unittest.py
r91092 r91103 152 152 self.assert_exp('failures/expected/crash.html', IMAGE) 153 153 154 def test_get_ options(self):155 self.parse_exp(self.get_basic_expectations()) 156 self.assertEqual(self._exp.get_ options(154 def test_get_modifiers(self): 155 self.parse_exp(self.get_basic_expectations()) 156 self.assertEqual(self._exp.get_modifiers( 157 157 self.get_test('passes/text.html')), []) 158 158 … … 195 195 except ParseError, e: 196 196 self.assertTrue(e.fatal) 197 exp_errors = [u"Line:1 Unrecognized option'foo' failures/expected/text.html",197 exp_errors = [u"Line:1 Unrecognized modifier 'foo' failures/expected/text.html", 198 198 u"Line:2 Missing expectations SKIP : failures/expected/image.html"] 199 199 self.assertEqual(str(e), '\n'.join(map(str, exp_errors))) … … 397 397 expectation = TestExpectationLine() 398 398 expectation.modifiers = modifiers 399 match _result = matcher.match(expectation)399 matcher.match(expectation) 400 400 self.assertEqual(len(expectation.warnings), 0) 401 401 self.assertEqual(len(expectation.errors), num_errors) 402 self.assertEqual( match_result.num_matches, expected_num_matches,402 self.assertEqual(expectation.num_matches, expected_num_matches, 403 403 'match(%s, %s) returned -> %d, expected %d' % 404 404 (modifiers, str(self.config.values()), 405 match_result.num_matches, expected_num_matches))405 expectation.num_matches, expected_num_matches)) 406 406 407 407 def test_bad_match_modifier(self): … … 449 449 self.match(['rebaseline', 'rebaseline'], num_errors=1) 450 450 451 def test_unknown_ option(self):451 def test_unknown_modifier(self): 452 452 self.match(['vms'], num_errors=1) 453 453 -
trunk/Tools/Scripts/webkitpy/style/checkers/test_expectations_unittest.py
r91071 r91103 113 113 self.assert_lines_lint( 114 114 ["INVALID-MODIFIER : passes/text.html = PASS"], 115 "Unrecognized option'invalid-modifier' "115 "Unrecognized modifier 'invalid-modifier' " 116 116 "passes/text.html [test/expectations] [5]") 117 117 self.assert_lines_lint(
Note: See TracChangeset
for help on using the changeset viewer.