Changeset 85875 in webkit
- Timestamp:
- May 5, 2011 1:55:18 PM (13 years ago)
- Location:
- trunk/Tools
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Tools/ChangeLog
r85871 r85875 1 2011-05-05 Dirk Pranke <dpranke@chromium.org> 2 3 Reviewed by Ojan Vafai. 4 5 new-run-webkit-tests: merge TestExpectations, TestExpectationsFile classes 6 https://bugs.webkit.org/show_bug.cgi?id=60002 7 8 * Scripts/webkitpy/layout_tests/layout_package/manager.py: 9 * Scripts/webkitpy/layout_tests/layout_package/printing.py: 10 * Scripts/webkitpy/layout_tests/layout_package/result_summary.py: 11 * Scripts/webkitpy/layout_tests/layout_package/test_expectations.py: 12 * Scripts/webkitpy/layout_tests/layout_package/test_expectations_unittest.py: 13 * Scripts/webkitpy/style/checkers/test_expectations.py: 14 1 15 2011-05-05 Eric Seidel <eric@webkit.org> 2 16 -
trunk/Tools/Scripts/webkitpy/layout_tests/layout_package/manager.py
r85344 r85875 70 70 BUILDER_BASE_URL = "http://build.chromium.org/buildbot/layout_test_results/" 71 71 72 TestExpectations File = test_expectations.TestExpectationsFile72 TestExpectations = test_expectations.TestExpectations 73 73 74 74 … … 110 110 num_regressions = 0 111 111 keywords = {} 112 for expecation_string, expectation_enum in TestExpectations File.EXPECTATIONS.iteritems():112 for expecation_string, expectation_enum in TestExpectations.EXPECTATIONS.iteritems(): 113 113 keywords[expectation_enum] = expecation_string.upper() 114 114 115 for modifier_string, modifier_enum in TestExpectations File.MODIFIERS.iteritems():115 for modifier_string, modifier_enum in TestExpectations.MODIFIERS.iteritems(): 116 116 keywords[modifier_enum] = modifier_string.upper() 117 117 … … 915 915 def _char_for_result(self, result): 916 916 result = result.lower() 917 if result in TestExpectations File.EXPECTATIONS:918 result_enum_value = TestExpectations File.EXPECTATIONS[result]917 if result in TestExpectations.EXPECTATIONS: 918 result_enum_value = TestExpectations.EXPECTATIONS[result] 919 919 else: 920 result_enum_value = TestExpectations File.MODIFIERS[result]920 result_enum_value = TestExpectations.MODIFIERS[result] 921 921 return json_layout_results_generator.JSONLayoutResultsGenerator.FAILURE_TO_CHAR[result_enum_value] 922 922 … … 1233 1233 self._printer.print_actual("=> %s (%d):" % (heading, not_passing)) 1234 1234 1235 for result in TestExpectations File.EXPECTATION_ORDER:1235 for result in TestExpectations.EXPECTATION_ORDER: 1236 1236 if result == test_expectations.PASS: 1237 1237 continue 1238 1238 results = (result_summary.tests_by_expectation[result] & 1239 1239 result_summary.tests_by_timeline[timeline]) 1240 desc = TestExpectations File.EXPECTATION_DESCRIPTIONS[result]1240 desc = TestExpectations.EXPECTATION_DESCRIPTIONS[result] 1241 1241 if not_passing and len(results): 1242 1242 pct = len(results) * 100.0 / not_passing -
trunk/Tools/Scripts/webkitpy/layout_tests/layout_package/printing.py
r85609 r85875 39 39 _log = logging.getLogger("webkitpy.layout_tests.printer") 40 40 41 TestExpectations File = test_expectations.TestExpectationsFile41 TestExpectations = test_expectations.TestExpectations 42 42 43 43 NUM_SLOW_TESTS_TO_LOG = 10 … … 369 369 def _print_unexpected_test_result(self, result): 370 370 """Prints one unexpected test result line.""" 371 desc = TestExpectations File.EXPECTATION_DESCRIPTIONS[result.type][0]371 desc = TestExpectations.EXPECTATION_DESCRIPTIONS[result.type][0] 372 372 self.write(" %s -> unexpected %s" % 373 373 (self._port.relative_test_filename(result.filename), … … 487 487 488 488 if len(flaky): 489 descriptions = TestExpectations File.EXPECTATION_DESCRIPTIONS489 descriptions = TestExpectations.EXPECTATION_DESCRIPTIONS 490 490 for key, tests in flaky.iteritems(): 491 result = TestExpectations File.EXPECTATIONS[key.lower()]491 result = TestExpectations.EXPECTATIONS[key.lower()] 492 492 self._buildbot_stream.write("Unexpected flakiness: %s (%d)\n" 493 493 % (descriptions[result][1], len(tests))) … … 498 498 actual = result['actual'].split(" ") 499 499 expected = result['expected'].split(" ") 500 result = TestExpectations File.EXPECTATIONS[key.lower()]500 result = TestExpectations.EXPECTATIONS[key.lower()] 501 501 new_expectations_list = list(set(actual) | set(expected)) 502 502 self._buildbot_stream.write(" %s = %s\n" % … … 506 506 507 507 if len(regressions): 508 descriptions = TestExpectations File.EXPECTATION_DESCRIPTIONS508 descriptions = TestExpectations.EXPECTATION_DESCRIPTIONS 509 509 for key, tests in regressions.iteritems(): 510 result = TestExpectations File.EXPECTATIONS[key.lower()]510 result = TestExpectations.EXPECTATIONS[key.lower()] 511 511 self._buildbot_stream.write( 512 512 "Regressions: Unexpected %s : (%d)\n" % ( -
trunk/Tools/Scripts/webkitpy/layout_tests/layout_package/result_summary.py
r79837 r85875 37 37 _log = logging.getLogger("webkitpy.layout_tests.run_webkit_tests") 38 38 39 TestExpectations File = test_expectations.TestExpectationsFile39 TestExpectations = test_expectations.TestExpectations 40 40 41 41 … … 60 60 self.failures = {} 61 61 self.tests_by_expectation[test_expectations.SKIP] = set() 62 for expectation in TestExpectations File.EXPECTATIONS.values():62 for expectation in TestExpectations.EXPECTATIONS.values(): 63 63 self.tests_by_expectation[expectation] = set() 64 for timeline in TestExpectations File.TIMELINES.values():64 for timeline in TestExpectations.TIMELINES.values(): 65 65 self.tests_by_timeline[timeline] = ( 66 66 expectations.get_tests_with_timeline(timeline)) -
trunk/Tools/Scripts/webkitpy/layout_tests/layout_package/test_expectations.py
r84633 r85875 85 85 86 86 87 def strip_comments(line): 88 """Strips comments from a line and return None if the line is empty 89 or else the contents of line with leading and trailing spaces removed 90 and all other whitespace collapsed""" 91 92 commentIndex = line.find('//') 93 if commentIndex is -1: 94 commentIndex = len(line) 95 96 line = re.sub(r'\s+', ' ', line[:commentIndex].strip()) 97 if line == '': 98 return None 99 else: 100 return line 101 102 103 class ParseError(Exception): 104 def __init__(self, fatal, errors): 105 self.fatal = fatal 106 self.errors = errors 107 108 def __str__(self): 109 return '\n'.join(map(str, self.errors)) 110 111 def __repr__(self): 112 return 'ParseError(fatal=%s, errors=%s)' % (self.fatal, self.errors) 113 114 115 class ModifiersAndExpectations: 116 """A holder for modifiers and expectations on a test that serializes to 117 JSON.""" 118 119 def __init__(self, modifiers, expectations): 120 self.modifiers = modifiers 121 self.expectations = expectations 122 123 124 class ExpectationsJsonEncoder(simplejson.JSONEncoder): 125 """JSON encoder that can handle ModifiersAndExpectations objects.""" 126 def default(self, obj): 127 # A ModifiersAndExpectations object has two fields, each of which 128 # is a dict. Since JSONEncoders handle all the builtin types directly, 129 # the only time this routine should be called is on the top level 130 # object (i.e., the encoder shouldn't recurse). 131 assert isinstance(obj, ModifiersAndExpectations) 132 return {"modifiers": obj.modifiers, 133 "expectations": obj.expectations} 134 135 87 136 class TestExpectations: 137 """Test expectations consist of lines with specifications of what 138 to expect from layout test cases. The test cases can be directories 139 in which case the expectations apply to all test cases in that 140 directory and any subdirectory. The format is along the lines of: 141 142 LayoutTests/fast/js/fixme.js = FAIL 143 LayoutTests/fast/js/flaky.js = FAIL PASS 144 LayoutTests/fast/js/crash.js = CRASH TIMEOUT FAIL PASS 145 ... 146 147 To add other options: 148 SKIP : LayoutTests/fast/js/no-good.js = TIMEOUT PASS 149 DEBUG : LayoutTests/fast/js/no-good.js = TIMEOUT PASS 150 DEBUG SKIP : LayoutTests/fast/js/no-good.js = TIMEOUT PASS 151 LINUX DEBUG SKIP : LayoutTests/fast/js/no-good.js = TIMEOUT PASS 152 LINUX WIN : LayoutTests/fast/js/no-good.js = TIMEOUT PASS 153 154 SKIP: Doesn't run the test. 155 SLOW: The test takes a long time to run, but does not timeout indefinitely. 156 WONTFIX: For tests that we never intend to pass on a given platform. 157 158 Notes: 159 -A test cannot be both SLOW and TIMEOUT 160 -A test should only be one of IMAGE, TEXT, IMAGE+TEXT, AUDIO, or FAIL. 161 FAIL is a legacy value that currently means either IMAGE, 162 TEXT, or IMAGE+TEXT. Once we have finished migrating the expectations, 163 we should change FAIL to have the meaning of IMAGE+TEXT and remove the 164 IMAGE+TEXT identifier. 165 -A test can be included twice, but not via the same path. 166 -If a test is included twice, then the more precise path wins. 167 -CRASH tests cannot be WONTFIX 168 """ 169 88 170 TEST_LIST = "test_expectations.txt" 89 171 90 def __init__(self, port, tests, expectations, test_config, 91 is_lint_mode, overrides=None): 172 EXPECTATIONS = {'pass': PASS, 173 'fail': FAIL, 174 'text': TEXT, 175 'image': IMAGE, 176 'image+text': IMAGE_PLUS_TEXT, 177 'audio': AUDIO, 178 'timeout': TIMEOUT, 179 'crash': CRASH, 180 'missing': MISSING} 181 182 EXPECTATION_DESCRIPTIONS = {SKIP: ('skipped', 'skipped'), 183 PASS: ('pass', 'passes'), 184 FAIL: ('failure', 'failures'), 185 TEXT: ('text diff mismatch', 186 'text diff mismatch'), 187 IMAGE: ('image mismatch', 'image mismatch'), 188 IMAGE_PLUS_TEXT: ('image and text mismatch', 189 'image and text mismatch'), 190 AUDIO: ('audio mismatch', 'audio mismatch'), 191 CRASH: ('DumpRenderTree crash', 192 'DumpRenderTree crashes'), 193 TIMEOUT: ('test timed out', 'tests timed out'), 194 MISSING: ('no expected result found', 195 'no expected results found')} 196 197 EXPECTATION_ORDER = (PASS, CRASH, TIMEOUT, MISSING, IMAGE_PLUS_TEXT, 198 TEXT, IMAGE, AUDIO, FAIL, SKIP) 199 200 BUILD_TYPES = ('debug', 'release') 201 202 MODIFIERS = {'skip': SKIP, 203 'wontfix': WONTFIX, 204 'slow': SLOW, 205 'rebaseline': REBASELINE, 206 'none': NONE} 207 208 TIMELINES = {'wontfix': WONTFIX, 209 'now': NOW} 210 211 RESULT_TYPES = {'skip': SKIP, 212 'pass': PASS, 213 'fail': FAIL, 214 'flaky': FLAKY} 215 216 @classmethod 217 def expectation_from_string(cls, string): 218 assert(' ' not in string) # This only handles one expectation at a time. 219 return cls.EXPECTATIONS.get(string.lower()) 220 221 def __init__(self, port, tests, expectations, 222 test_config, is_lint_mode, overrides=None): 92 223 """Loads and parses the test expectations given in the string. 93 224 Args: … … 105 236 and downstream expectations). 106 237 """ 107 self._expected_failures = TestExpectationsFile(port, expectations,108 tests, test_config, is_lint_mode,109 overrides=overrides)110 111 # TODO(ojan): Allow for removing skipped tests when getting the list of112 # tests to run, but not when getting metrics.113 # TODO(ojan): Replace the Get* calls here with the more sane API exposed114 # by TestExpectationsFile below. Maybe merge the two classes entirely?115 116 def get_expectations_json_for_all_platforms(self):117 return (118 self._expected_failures.get_expectations_json_for_all_platforms())119 120 def get_rebaselining_failures(self):121 return (self._expected_failures.get_test_set(REBASELINE, FAIL) |122 self._expected_failures.get_test_set(REBASELINE, IMAGE) |123 self._expected_failures.get_test_set(REBASELINE, TEXT) |124 self._expected_failures.get_test_set(REBASELINE,125 IMAGE_PLUS_TEXT) |126 self._expected_failures.get_test_set(REBASELINE, AUDIO))127 128 def get_options(self, test):129 return self._expected_failures.get_options(test)130 131 def get_expectations(self, test):132 return self._expected_failures.get_expectations(test)133 134 def get_expectations_string(self, test):135 """Returns the expectatons for the given test as an uppercase string.136 If there are no expectations for the test, then "PASS" is returned."""137 expectations = self.get_expectations(test)138 retval = []139 140 for expectation in expectations:141 retval.append(self.expectation_to_string(expectation))142 143 return " ".join(retval)144 145 def expectation_to_string(self, expectation):146 """Return the uppercased string equivalent of a given expectation."""147 for item in TestExpectationsFile.EXPECTATIONS.items():148 if item[1] == expectation:149 return item[0].upper()150 raise ValueError(expectation)151 152 @classmethod153 def expectation_from_string(cls, string):154 assert(' ' not in string) # This only handles one expectation at a time.155 return TestExpectationsFile.EXPECTATIONS.get(string.lower())156 157 def get_tests_with_result_type(self, result_type):158 return self._expected_failures.get_tests_with_result_type(result_type)159 160 def get_tests_with_timeline(self, timeline):161 return self._expected_failures.get_tests_with_timeline(timeline)162 163 def matches_an_expected_result(self, test, result,164 pixel_tests_are_enabled):165 expected_results = self._expected_failures.get_expectations(test)166 if not pixel_tests_are_enabled:167 expected_results = remove_pixel_failures(expected_results)168 return result_was_expected(result, expected_results,169 self.is_rebaselining(test), self.has_modifier(test, SKIP))170 171 def is_rebaselining(self, test):172 return self._expected_failures.has_modifier(test, REBASELINE)173 174 def has_modifier(self, test, modifier):175 return self._expected_failures.has_modifier(test, modifier)176 177 def remove_rebaselined_tests(self, tests):178 return self._expected_failures.remove_rebaselined_tests(tests)179 180 181 def strip_comments(line):182 """Strips comments from a line and return None if the line is empty183 or else the contents of line with leading and trailing spaces removed184 and all other whitespace collapsed"""185 186 commentIndex = line.find('//')187 if commentIndex is -1:188 commentIndex = len(line)189 190 line = re.sub(r'\s+', ' ', line[:commentIndex].strip())191 if line == '':192 return None193 else:194 return line195 196 197 class ParseError(Exception):198 def __init__(self, fatal, errors):199 self.fatal = fatal200 self.errors = errors201 202 def __str__(self):203 return '\n'.join(map(str, self.errors))204 205 def __repr__(self):206 return 'ParseError(fatal=%s, errors=%s)' % (self.fatal, self.errors)207 208 209 class ModifiersAndExpectations:210 """A holder for modifiers and expectations on a test that serializes to211 JSON."""212 213 def __init__(self, modifiers, expectations):214 self.modifiers = modifiers215 self.expectations = expectations216 217 218 class ExpectationsJsonEncoder(simplejson.JSONEncoder):219 """JSON encoder that can handle ModifiersAndExpectations objects."""220 def default(self, obj):221 # A ModifiersAndExpectations object has two fields, each of which222 # is a dict. Since JSONEncoders handle all the builtin types directly,223 # the only time this routine should be called is on the top level224 # object (i.e., the encoder shouldn't recurse).225 assert isinstance(obj, ModifiersAndExpectations)226 return {"modifiers": obj.modifiers,227 "expectations": obj.expectations}228 229 230 class TestExpectationsFile:231 """Test expectation files consist of lines with specifications of what232 to expect from layout test cases. The test cases can be directories233 in which case the expectations apply to all test cases in that234 directory and any subdirectory. The format of the file is along the235 lines of:236 237 LayoutTests/fast/js/fixme.js = FAIL238 LayoutTests/fast/js/flaky.js = FAIL PASS239 LayoutTests/fast/js/crash.js = CRASH TIMEOUT FAIL PASS240 ...241 242 To add other options:243 SKIP : LayoutTests/fast/js/no-good.js = TIMEOUT PASS244 DEBUG : LayoutTests/fast/js/no-good.js = TIMEOUT PASS245 DEBUG SKIP : LayoutTests/fast/js/no-good.js = TIMEOUT PASS246 LINUX DEBUG SKIP : LayoutTests/fast/js/no-good.js = TIMEOUT PASS247 LINUX WIN : LayoutTests/fast/js/no-good.js = TIMEOUT PASS248 249 SKIP: Doesn't run the test.250 SLOW: The test takes a long time to run, but does not timeout indefinitely.251 WONTFIX: For tests that we never intend to pass on a given platform.252 253 Notes:254 -A test cannot be both SLOW and TIMEOUT255 -A test should only be one of IMAGE, TEXT, IMAGE+TEXT, AUDIO, or FAIL.256 FAIL is a legacy value that currently means either IMAGE,257 TEXT, or IMAGE+TEXT. Once we have finished migrating the expectations,258 we should change FAIL to have the meaning of IMAGE+TEXT and remove the259 IMAGE+TEXT identifier.260 -A test can be included twice, but not via the same path.261 -If a test is included twice, then the more precise path wins.262 -CRASH tests cannot be WONTFIX263 """264 265 EXPECTATIONS = {'pass': PASS,266 'fail': FAIL,267 'text': TEXT,268 'image': IMAGE,269 'image+text': IMAGE_PLUS_TEXT,270 'audio': AUDIO,271 'timeout': TIMEOUT,272 'crash': CRASH,273 'missing': MISSING}274 275 EXPECTATION_DESCRIPTIONS = {SKIP: ('skipped', 'skipped'),276 PASS: ('pass', 'passes'),277 FAIL: ('failure', 'failures'),278 TEXT: ('text diff mismatch',279 'text diff mismatch'),280 IMAGE: ('image mismatch', 'image mismatch'),281 IMAGE_PLUS_TEXT: ('image and text mismatch',282 'image and text mismatch'),283 AUDIO: ('audio mismatch', 'audio mismatch'),284 CRASH: ('DumpRenderTree crash',285 'DumpRenderTree crashes'),286 TIMEOUT: ('test timed out', 'tests timed out'),287 MISSING: ('no expected result found',288 'no expected results found')}289 290 EXPECTATION_ORDER = (PASS, CRASH, TIMEOUT, MISSING, IMAGE_PLUS_TEXT,291 TEXT, IMAGE, AUDIO, FAIL, SKIP)292 293 BUILD_TYPES = ('debug', 'release')294 295 MODIFIERS = {'skip': SKIP,296 'wontfix': WONTFIX,297 'slow': SLOW,298 'rebaseline': REBASELINE,299 'none': NONE}300 301 TIMELINES = {'wontfix': WONTFIX,302 'now': NOW}303 304 RESULT_TYPES = {'skip': SKIP,305 'pass': PASS,306 'fail': FAIL,307 'flaky': FLAKY}308 309 def __init__(self, port, expectations, full_test_list,310 test_config, is_lint_mode, overrides=None):311 # See argument documentation in TestExpectation(), above.312 313 238 self._port = port 314 239 self._fs = port._filesystem 315 240 self._expectations = expectations 316 self._full_test_list = full_test_list241 self._full_test_list = tests 317 242 self._test_config = test_config 318 243 self._is_lint_mode = is_lint_mode … … 363 288 self._handle_any_read_errors() 364 289 self._process_tests_without_expectations() 290 291 # TODO(ojan): Allow for removing skipped tests when getting the list of 292 # tests to run, but not when getting metrics. 293 294 def get_rebaselining_failures(self): 295 return (self.get_test_set(REBASELINE, FAIL) | 296 self.get_test_set(REBASELINE, IMAGE) | 297 self.get_test_set(REBASELINE, TEXT) | 298 self.get_test_set(REBASELINE, IMAGE_PLUS_TEXT) | 299 self.get_test_set(REBASELINE, AUDIO)) 300 301 def get_expectations_string(self, test): 302 """Returns the expectatons for the given test as an uppercase string. 303 If there are no expectations for the test, then "PASS" is returned.""" 304 expectations = self.get_expectations(test) 305 retval = [] 306 307 for expectation in expectations: 308 retval.append(self.expectation_to_string(expectation)) 309 310 return " ".join(retval) 311 312 def expectation_to_string(self, expectation): 313 """Return the uppercased string equivalent of a given expectation.""" 314 for item in self.EXPECTATIONS.items(): 315 if item[1] == expectation: 316 return item[0].upper() 317 raise ValueError(expectation) 318 319 def matches_an_expected_result(self, test, result, pixel_tests_are_enabled): 320 expected_results = self.get_expectations(test) 321 if not pixel_tests_are_enabled: 322 expected_results = remove_pixel_failures(expected_results) 323 return result_was_expected(result, 324 expected_results, 325 self.is_rebaselining(test), 326 self.has_modifier(test, SKIP)) 327 328 def is_rebaselining(self, test): 329 return self.has_modifier(test, REBASELINE) 330 365 331 366 332 def _handle_any_read_errors(self): … … 847 813 # We don't include the "none" modifier because it isn't actually legal. 848 814 REGEXES_TO_IGNORE = (['bug\w+'] + 849 TestExpectations File.MODIFIERS.keys()[:-1])815 TestExpectations.MODIFIERS.keys()[:-1]) 850 816 DUPLICATE_REGEXES_ALLOWED = ['bug\w+'] 851 817 -
trunk/Tools/Scripts/webkitpy/layout_tests/layout_package/test_expectations_unittest.py
r82705 r85875 179 179 # Handle some corner cases for this routine not covered by other tests. 180 180 self.parse_exp(self.get_basic_expectations()) 181 s = self._exp. _expected_failures.get_test_set(WONTFIX)181 s = self._exp.get_test_set(WONTFIX) 182 182 self.assertEqual(s, 183 183 set([self.get_test('failures/expected/crash.html'), 184 184 self.get_test('failures/expected/image_checksum.html')])) 185 s = self._exp. _expected_failures.get_test_set(WONTFIX, CRASH)185 s = self._exp.get_test_set(WONTFIX, CRASH) 186 186 self.assertEqual(s, 187 187 set([self.get_test('failures/expected/crash.html')])) 188 s = self._exp._expected_failures.get_test_set(WONTFIX, CRASH, 189 include_skips=False) 188 s = self._exp.get_test_set(WONTFIX, CRASH, include_skips=False) 190 189 self.assertEqual(s, set([])) 191 190 … … 291 290 self.parse_exp('SLOW : failures/expected/text.html = TEXT') 292 291 self.assertEqual( 293 len(self._exp. _expected_failures.get_non_fatal_errors()), 1)292 len(self._exp.get_non_fatal_errors()), 1) 294 293 295 294 def test_slow_and_timeout(self): … … 317 316 # This should log a non-fatal error. 318 317 self.parse_exp('BUG_TEST : missing_file.html = TEXT') 319 self.assertEqual( 320 len(self._exp._expected_failures.get_non_fatal_errors()), 1) 318 self.assertEqual(len(self._exp.get_non_fatal_errors()), 1) 321 319 322 320 -
trunk/Tools/Scripts/webkitpy/style/checkers/test_expectations.py
r77163 r85875 89 89 expectations = None 90 90 try: 91 expectations = test_expectations.TestExpectations File(92 port=self._port_obj, expectations=expectations_str, full_test_list=tests,91 expectations = test_expectations.TestExpectations( 92 port=self._port_obj, expectations=expectations_str, tests=tests, 93 93 test_config=self._port_obj.test_configuration(), 94 94 is_lint_mode=True, overrides=overrides)
Note: See TracChangeset
for help on using the changeset viewer.