1 # Copyright (C) 2010 Google Inc. All rights reserved. 2 # 3 # Redistribution and use in source and binary forms, with or without 4 # modification, are permitted provided that the following conditions are 5 # met: 6 # 7 # * Redistributions of source code must retain the above copyright 8 # notice, this list of conditions and the following disclaimer. 9 # * Redistributions in binary form must reproduce the above 10 # copyright notice, this list of conditions and the following disclaimer 11 # in the documentation and/or other materials provided with the 12 # distribution. 13 # * Neither the name of Google Inc. nor the names of its 14 # contributors may be used to endorse or promote products derived from 15 # this software without specific prior written permission. 16 # 17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 import webkitpy.thirdparty.unittest2 as unittest 30 31 from webkitpy.common.host_mock import MockHost 32 from webkitpy.common.system.outputcapture import OutputCapture 33 34 from webkitpy.layout_tests.models.test_configuration import * 35 from webkitpy.layout_tests.models.test_expectations import * 36 37 try: 38 from collections import OrderedDict 39 except ImportError: 40 # Needed for Python < 2.7 41 from webkitpy.thirdparty.ordered_dict import OrderedDict 42 43 44 class Base(unittest.TestCase): 45 # Note that all of these tests are written assuming the configuration 46 # being tested is Windows XP, Release build. 47 48 def __init__(self, testFunc): 49 host = MockHost() 50 self._port = host.port_factory.get('test-win-xp', None) 51 self._exp = None 52 unittest.TestCase.__init__(self, testFunc) 53 54 def get_test(self, test_name): 55 # FIXME: Remove this routine and just reference test names directly. 56 return test_name 57 58 def get_basic_tests(self): 59 return [self.get_test('failures/expected/text.html'), 60 self.get_test('failures/expected/image_checksum.html'), 61 self.get_test('failures/expected/crash.html'), 62 self.get_test('failures/expected/needsrebaseline.html'), 63 self.get_test('failures/expected/needsmanualrebaseline.html'), 64 self.get_test('failures/expected/missing_text.html'), 65 self.get_test('failures/expected/image.html'), 66 self.get_test('passes/text.html')] 67 68 def get_basic_expectations(self): 69 return """ 70 Bug(test) failures/expected/text.html [ Failure ] 71 Bug(test) failures/expected/crash.html [ WontFix ] 72 Bug(test) failures/expected/needsrebaseline.html [ NeedsRebaseline ] 73 Bug(test) failures/expected/needsmanualrebaseline.html [ NeedsManualRebaseline ] 74 Bug(test) failures/expected/missing_image.html [ Rebaseline Missing ] 75 Bug(test) failures/expected/image_checksum.html [ WontFix ] 76 Bug(test) failures/expected/image.html [ WontFix Mac ] 77 """ 78 79 def parse_exp(self, expectations, overrides=None, is_lint_mode=False): 80 expectations_dict = OrderedDict() 81 expectations_dict['expectations'] = expectations 82 if overrides: 83 expectations_dict['overrides'] = overrides 84 self._port.expectations_dict = lambda: expectations_dict 85 expectations_to_lint = expectations_dict if is_lint_mode else None 86 self._exp = TestExpectations(self._port, self.get_basic_tests(), expectations_dict=expectations_to_lint, is_lint_mode=is_lint_mode) 87 88 def assert_exp_list(self, test, results): 89 self.assertEqual(self._exp.get_expectations(self.get_test(test)), set(results)) 90 91 def assert_exp(self, test, result): 92 self.assert_exp_list(test, [result]) 93 94 def assert_bad_expectations(self, expectations, overrides=None): 95 self.assertRaises(ParseError, self.parse_exp, expectations, is_lint_mode=True, overrides=overrides) 96 97 98 class BasicTests(Base): 99 def test_basic(self): 100 self.parse_exp(self.get_basic_expectations()) 101 self.assert_exp('failures/expected/text.html', FAIL) 102 self.assert_exp_list('failures/expected/image_checksum.html', [WONTFIX, SKIP]) 103 self.assert_exp('passes/text.html', PASS) 104 self.assert_exp('failures/expected/image.html', PASS) 105 106 107 class MiscTests(Base): 108 def test_multiple_results(self): 109 self.parse_exp('Bug(x) failures/expected/text.html [ Crash Failure ]') 110 self.assertEqual(self._exp.get_expectations( 111 self.get_test('failures/expected/text.html')), 112 set([FAIL, CRASH])) 113 114 def test_result_was_expected(self): 115 # test basics 116 self.assertEqual(TestExpectations.result_was_expected(PASS, set([PASS]), test_needs_rebaselining=False), True) 117 self.assertEqual(TestExpectations.result_was_expected(FAIL, set([PASS]), test_needs_rebaselining=False), False) 118 119 # test handling of SKIPped tests and results 120 self.assertEqual(TestExpectations.result_was_expected(SKIP, set([CRASH]), test_needs_rebaselining=False), True) 121 122 # test handling of MISSING results and the REBASELINE specifier 123 self.assertEqual(TestExpectations.result_was_expected(MISSING, set([PASS]), test_needs_rebaselining=True), True) 124 self.assertEqual(TestExpectations.result_was_expected(MISSING, set([PASS]), test_needs_rebaselining=False), False) 125 126 self.assertTrue(TestExpectations.result_was_expected(PASS, set([NEEDS_REBASELINE]), test_needs_rebaselining=False)) 127 self.assertTrue(TestExpectations.result_was_expected(MISSING, set([NEEDS_REBASELINE]), test_needs_rebaselining=False)) 128 self.assertTrue(TestExpectations.result_was_expected(TEXT, set([NEEDS_REBASELINE]), test_needs_rebaselining=False)) 129 self.assertTrue(TestExpectations.result_was_expected(IMAGE, set([NEEDS_REBASELINE]), test_needs_rebaselining=False)) 130 self.assertTrue(TestExpectations.result_was_expected(IMAGE_PLUS_TEXT, set([NEEDS_REBASELINE]), test_needs_rebaselining=False)) 131 self.assertTrue(TestExpectations.result_was_expected(AUDIO, set([NEEDS_REBASELINE]), test_needs_rebaselining=False)) 132 self.assertFalse(TestExpectations.result_was_expected(TIMEOUT, set([NEEDS_REBASELINE]), test_needs_rebaselining=False)) 133 self.assertFalse(TestExpectations.result_was_expected(CRASH, set([NEEDS_REBASELINE]), test_needs_rebaselining=False)) 134 135 def test_remove_pixel_failures(self): 136 self.assertEqual(TestExpectations.remove_pixel_failures(set([FAIL])), set([FAIL])) 137 self.assertEqual(TestExpectations.remove_pixel_failures(set([PASS])), set([PASS])) 138 self.assertEqual(TestExpectations.remove_pixel_failures(set([IMAGE])), set([PASS])) 139 self.assertEqual(TestExpectations.remove_pixel_failures(set([FAIL])), set([FAIL])) 140 self.assertEqual(TestExpectations.remove_pixel_failures(set([PASS, IMAGE, CRASH])), set([PASS, CRASH])) 141 142 def test_suffixes_for_expectations(self): 143 self.assertEqual(TestExpectations.suffixes_for_expectations(set([FAIL])), set(['txt', 'png', 'wav'])) 144 self.assertEqual(TestExpectations.suffixes_for_expectations(set([IMAGE])), set(['png'])) 145 self.assertEqual(TestExpectations.suffixes_for_expectations(set([FAIL, IMAGE, CRASH])), set(['txt', 'png', 'wav'])) 146 self.assertEqual(TestExpectations.suffixes_for_expectations(set()), set()) 147 148 def test_category_expectations(self): 149 # This test checks unknown tests are not present in the 150 # expectations and that known test part of a test category is 151 # present in the expectations. 152 exp_str = 'Bug(x) failures/expected [ WontFix ]' 153 self.parse_exp(exp_str) 154 test_name = 'failures/expected/unknown-test.html' 155 unknown_test = self.get_test(test_name) 156 self.assertRaises(KeyError, self._exp.get_expectations, 157 unknown_test) 158 self.assert_exp_list('failures/expected/crash.html', [WONTFIX, SKIP]) 159 160 def test_get_expectations_string(self): 161 self.parse_exp(self.get_basic_expectations()) 162 self.assertEqual(self._exp.get_expectations_string( 163 self.get_test('failures/expected/text.html')), 164 'FAIL') 165 166 def test_expectation_to_string(self): 167 # Normal cases are handled by other tests. 168 self.parse_exp(self.get_basic_expectations()) 169 self.assertRaises(ValueError, self._exp.expectation_to_string, 170 -1) 171 172 def test_get_test_set(self): 173 # Handle some corner cases for this routine not covered by other tests. 174 self.parse_exp(self.get_basic_expectations()) 175 s = self._exp.get_test_set(WONTFIX) 176 self.assertEqual(s, 177 set([self.get_test('failures/expected/crash.html'), 178 self.get_test('failures/expected/image_checksum.html')])) 179 180 def test_parse_warning(self): 181 try: 182 filesystem = self._port.host.filesystem 183 filesystem.write_text_file(filesystem.join(self._port.layout_tests_dir(), 'disabled-test.html-disabled'), 'content') 184 self.get_test('disabled-test.html-disabled'), 185 self.parse_exp("Bug(user) [ FOO ] failures/expected/text.html [ Failure ]\n" 186 "Bug(user) non-existent-test.html [ Failure ]\n" 187 "Bug(user) disabled-test.html-disabled [ ImageOnlyFailure ]", is_lint_mode=True) 188 self.assertFalse(True, "ParseError wasn't raised") 189 except ParseError, e: 190 warnings = ("expectations:1 Unrecognized specifier 'foo' failures/expected/text.html\n" 191 "expectations:2 Path does not exist. non-existent-test.html") 192 self.assertEqual(str(e), warnings) 193 194 def test_parse_warnings_are_logged_if_not_in_lint_mode(self): 195 oc = OutputCapture() 196 try: 197 oc.capture_output() 198 self.parse_exp('-- this should be a syntax error', is_lint_mode=False) 199 finally: 200 _, _, logs = oc.restore_output() 201 self.assertNotEquals(logs, '') 202 203 def test_error_on_different_platform(self): 204 # parse_exp uses a Windows port. Assert errors on Mac show up in lint mode. 205 self.assertRaises(ParseError, self.parse_exp, 206 'Bug(test) [ Mac ] failures/expected/text.html [ Failure ]\nBug(test) [ Mac ] failures/expected/text.html [ Failure ]', 207 is_lint_mode=True) 208 209 def test_error_on_different_build_type(self): 210 # parse_exp uses a Release port. Assert errors on DEBUG show up in lint mode. 211 self.assertRaises(ParseError, self.parse_exp, 212 'Bug(test) [ Debug ] failures/expected/text.html [ Failure ]\nBug(test) [ Debug ] failures/expected/text.html [ Failure ]', 213 is_lint_mode=True) 214 215 def test_overrides(self): 216 self.parse_exp("Bug(exp) failures/expected/text.html [ Failure ]", 217 "Bug(override) failures/expected/text.html [ ImageOnlyFailure ]") 218 self.assert_exp_list('failures/expected/text.html', [FAIL, IMAGE]) 219 220 def test_overrides__directory(self): 221 self.parse_exp("Bug(exp) failures/expected/text.html [ Failure ]", 222 "Bug(override) failures/expected [ Crash ]") 223 self.assert_exp_list('failures/expected/text.html', [FAIL, CRASH]) 224 self.assert_exp_list('failures/expected/image.html', [CRASH]) 225 226 def test_overrides__duplicate(self): 227 self.assert_bad_expectations("Bug(exp) failures/expected/text.html [ Failure ]", 228 "Bug(override) failures/expected/text.html [ ImageOnlyFailure ]\n" 229 "Bug(override) failures/expected/text.html [ Crash ]\n") 230 231 def test_pixel_tests_flag(self): 232 def match(test, result, pixel_tests_enabled): 233 return self._exp.matches_an_expected_result( 234 self.get_test(test), result, pixel_tests_enabled) 235 236 self.parse_exp(self.get_basic_expectations()) 237 self.assertTrue(match('failures/expected/text.html', FAIL, True)) 238 self.assertTrue(match('failures/expected/text.html', FAIL, False)) 239 self.assertFalse(match('failures/expected/text.html', CRASH, True)) 240 self.assertFalse(match('failures/expected/text.html', CRASH, False)) 241 self.assertTrue(match('failures/expected/image_checksum.html', PASS, True)) 242 self.assertTrue(match('failures/expected/image_checksum.html', PASS, False)) 243 self.assertTrue(match('failures/expected/crash.html', PASS, False)) 244 self.assertTrue(match('failures/expected/needsrebaseline.html', TEXT, True)) 245 self.assertFalse(match('failures/expected/needsrebaseline.html', CRASH, True)) 246 self.assertTrue(match('failures/expected/needsmanualrebaseline.html', TEXT, True)) 247 self.assertFalse(match('failures/expected/needsmanualrebaseline.html', CRASH, True)) 248 self.assertTrue(match('passes/text.html', PASS, False)) 249 250 def test_more_specific_override_resets_skip(self): 251 self.parse_exp("Bug(x) failures/expected [ Skip ]\n" 252 "Bug(x) failures/expected/text.html [ ImageOnlyFailure ]\n") 253 self.assert_exp('failures/expected/text.html', IMAGE) 254 self.assertFalse(self._port._filesystem.join(self._port.layout_tests_dir(), 255 'failures/expected/text.html') in 256 self._exp.get_tests_with_result_type(SKIP)) 257 258 def test_bot_test_expectations(self): 259 """Test that expectations are merged rather than overridden when using flaky option 'unexpected'.""" 260 test_name1 = 'failures/expected/text.html' 261 test_name2 = 'passes/text.html' 262 263 expectations_dict = OrderedDict() 264 expectations_dict['expectations'] = "Bug(x) %s [ ImageOnlyFailure ]\nBug(x) %s [ Slow ]\n" % (test_name1, test_name2) 265 self._port.expectations_dict = lambda: expectations_dict 266 267 expectations = TestExpectations(self._port, self.get_basic_tests()) 268 self.assertEqual(expectations.get_expectations(self.get_test(test_name1)), set([IMAGE])) 269 self.assertEqual(expectations.get_expectations(self.get_test(test_name2)), set([SLOW])) 270 271 def bot_expectations(): 272 return {test_name1: ['PASS', 'TIMEOUT'], test_name2: ['CRASH']} 273 self._port.bot_expectations = bot_expectations 274 self._port._options.ignore_flaky_tests = 'unexpected' 275 276 expectations = TestExpectations(self._port, self.get_basic_tests()) 277 self.assertEqual(expectations.get_expectations(self.get_test(test_name1)), set([PASS, IMAGE, TIMEOUT])) 278 self.assertEqual(expectations.get_expectations(self.get_test(test_name2)), set([CRASH, SLOW])) 279 280 class SkippedTests(Base): 281 def check(self, expectations, overrides, skips, lint=False, expected_results=[WONTFIX, SKIP, FAIL]): 282 port = MockHost().port_factory.get('test-win-xp') 283 port._filesystem.write_text_file(port._filesystem.join(port.layout_tests_dir(), 'failures/expected/text.html'), 'foo') 284 expectations_dict = OrderedDict() 285 expectations_dict['expectations'] = expectations 286 if overrides: 287 expectations_dict['overrides'] = overrides 288 port.expectations_dict = lambda: expectations_dict 289 port.skipped_layout_tests = lambda tests: set(skips) 290 expectations_to_lint = expectations_dict if lint else None 291 exp = TestExpectations(port, ['failures/expected/text.html'], expectations_dict=expectations_to_lint, is_lint_mode=lint) 292 self.assertEqual(exp.get_expectations('failures/expected/text.html'), set(expected_results)) 293 294 def test_skipped_tests_work(self): 295 self.check(expectations='', overrides=None, skips=['failures/expected/text.html'], expected_results=[WONTFIX, SKIP]) 296 297 def test_duplicate_skipped_test_fails_lint(self): 298 self.assertRaises(ParseError, self.check, expectations='Bug(x) failures/expected/text.html [ Failure ]\n', 299 overrides=None, skips=['failures/expected/text.html'], lint=True) 300 301 def test_skipped_file_overrides_expectations(self): 302 self.check(expectations='Bug(x) failures/expected/text.html [ Failure ]\n', overrides=None, 303 skips=['failures/expected/text.html']) 304 305 def test_skipped_dir_overrides_expectations(self): 306 self.check(expectations='Bug(x) failures/expected/text.html [ Failure ]\n', overrides=None, 307 skips=['failures/expected']) 308 309 def test_skipped_file_overrides_overrides(self): 310 self.check(expectations='', overrides='Bug(x) failures/expected/text.html [ Failure ]\n', 311 skips=['failures/expected/text.html']) 312 313 def test_skipped_dir_overrides_overrides(self): 314 self.check(expectations='', overrides='Bug(x) failures/expected/text.html [ Failure ]\n', 315 skips=['failures/expected']) 316 317 def test_skipped_entry_dont_exist(self): 318 port = MockHost().port_factory.get('test-win-xp') 319 expectations_dict = OrderedDict() 320 expectations_dict['expectations'] = '' 321 port.expectations_dict = lambda: expectations_dict 322 port.skipped_layout_tests = lambda tests: set(['foo/bar/baz.html']) 323 capture = OutputCapture() 324 capture.capture_output() 325 exp = TestExpectations(port) 326 _, _, logs = capture.restore_output() 327 self.assertEqual('The following test foo/bar/baz.html from the Skipped list doesn\'t exist\n', logs) 328 329 def test_expectations_string(self): 330 self.parse_exp(self.get_basic_expectations()) 331 notrun = 'failures/expected/text.html' 332 self._exp.add_extra_skipped_tests([notrun]) 333 self.assertEqual('NOTRUN', self._exp.get_expectations_string(notrun)) 334 335 336 class ExpectationSyntaxTests(Base): 337 def test_unrecognized_expectation(self): 338 self.assert_bad_expectations('Bug(test) failures/expected/text.html [ Unknown ]') 339 340 def test_macro(self): 341 exp_str = 'Bug(test) [ Win ] failures/expected/text.html [ Failure ]' 342 self.parse_exp(exp_str) 343 self.assert_exp('failures/expected/text.html', FAIL) 344 345 def assert_tokenize_exp(self, line, bugs=None, specifiers=None, expectations=None, warnings=None, comment=None, name='foo.html'): 346 bugs = bugs or [] 347 specifiers = specifiers or [] 348 expectations = expectations or [] 349 warnings = warnings or [] 350 filename = 'TestExpectations' 351 line_number = '1' 352 expectation_line = TestExpectationParser._tokenize_line(filename, line, line_number) 353 self.assertEqual(expectation_line.warnings, warnings) 354 self.assertEqual(expectation_line.name, name) 355 self.assertEqual(expectation_line.filename, filename) 356 self.assertEqual(expectation_line.line_numbers, line_number) 357 if not warnings: 358 self.assertEqual(expectation_line.specifiers, specifiers) 359 self.assertEqual(expectation_line.expectations, expectations) 360 361 def test_comments(self): 362 self.assert_tokenize_exp("# comment", name=None, comment="# comment") 363 self.assert_tokenize_exp("foo.html [ Pass ] # comment", comment="# comment", expectations=['PASS'], specifiers=[]) 364 365 def test_config_specifiers(self): 366 self.assert_tokenize_exp('[ Mac ] foo.html [ Failure ] ', specifiers=['MAC'], expectations=['FAIL']) 367 368 def test_unknown_config(self): 369 self.assert_tokenize_exp('[ Foo ] foo.html [ Pass ]', specifiers=['Foo'], expectations=['PASS']) 370 371 def test_unknown_expectation(self): 372 self.assert_tokenize_exp('foo.html [ Audio ]', warnings=['Unrecognized expectation "Audio"']) 373 374 def test_skip(self): 375 self.assert_tokenize_exp('foo.html [ Skip ]', specifiers=[], expectations=['SKIP']) 376 377 def test_slow(self): 378 self.assert_tokenize_exp('foo.html [ Slow ]', specifiers=[], expectations=['SLOW']) 379 380 def test_wontfix(self): 381 self.assert_tokenize_exp('foo.html [ WontFix ]', specifiers=[], expectations=['WONTFIX', 'SKIP']) 382 self.assert_tokenize_exp('foo.html [ WontFix ImageOnlyFailure ]', specifiers=[], expectations=['WONTFIX', 'SKIP'], 383 warnings=['A test marked Skip or WontFix must not have other expectations.']) 384 385 def test_blank_line(self): 386 self.assert_tokenize_exp('', name=None) 387 388 def test_warnings(self): 389 self.assert_tokenize_exp('[ Mac ]', warnings=['Did not find a test name.', 'Missing expectations.'], name=None) 390 self.assert_tokenize_exp('[ [', warnings=['unexpected "["', 'Missing expectations.'], name=None) 391 self.assert_tokenize_exp('crbug.com/12345 ]', warnings=['unexpected "]"', 'Missing expectations.'], name=None) 392 393 self.assert_tokenize_exp('foo.html crbug.com/12345 ]', warnings=['"crbug.com/12345" is not at the start of the line.', 'Missing expectations.']) 394 self.assert_tokenize_exp('foo.html', warnings=['Missing expectations.']) 395 396 397 class SemanticTests(Base): 398 def test_bug_format(self): 399 self.assertRaises(ParseError, self.parse_exp, 'BUG1234 failures/expected/text.html [ Failure ]', is_lint_mode=True) 400 401 def test_bad_bugid(self): 402 try: 403 self.parse_exp('crbug/1234 failures/expected/text.html [ Failure ]', is_lint_mode=True) 404 self.fail('should have raised an error about a bad bug identifier') 405 except ParseError, exp: 406 self.assertEqual(len(exp.warnings), 3) 407 408 def test_missing_bugid(self): 409 self.parse_exp('failures/expected/text.html [ Failure ]', is_lint_mode=False) 410 self.assertFalse(self._exp.has_warnings()) 411 412 try: 413 self.parse_exp('failures/expected/text.html [ Failure ]', is_lint_mode=True) 414 except ParseError, exp: 415 self.assertEqual(exp.warnings, ['expectations:1 Test lacks BUG specifier. failures/expected/text.html']) 416 417 def test_skip_and_wontfix(self): 418 # Skip is not allowed to have other expectations as well, because those 419 # expectations won't be exercised and may become stale . 420 self.parse_exp('failures/expected/text.html [ Failure Skip ]') 421 self.assertTrue(self._exp.has_warnings()) 422 423 self.parse_exp('failures/expected/text.html [ Crash WontFix ]') 424 self.assertTrue(self._exp.has_warnings()) 425 426 self.parse_exp('failures/expected/text.html [ Pass WontFix ]') 427 self.assertTrue(self._exp.has_warnings()) 428 429 def test_rebaseline(self): 430 # Can't lint a file w/ 'REBASELINE' in it. 431 self.assertRaises(ParseError, self.parse_exp, 432 'Bug(test) failures/expected/text.html [ Failure Rebaseline ]', 433 is_lint_mode=True) 434 435 def test_duplicates(self): 436 self.assertRaises(ParseError, self.parse_exp, """ 437 Bug(exp) failures/expected/text.html [ Failure ] 438 Bug(exp) failures/expected/text.html [ ImageOnlyFailure ]""", is_lint_mode=True) 439 440 self.assertRaises(ParseError, self.parse_exp, 441 self.get_basic_expectations(), overrides=""" 442 Bug(override) failures/expected/text.html [ Failure ] 443 Bug(override) failures/expected/text.html [ ImageOnlyFailure ]""", is_lint_mode=True) 444 445 def test_duplicate_with_line_before_preceding_line(self): 446 self.assert_bad_expectations("""Bug(exp) [ Debug ] failures/expected/text.html [ Failure ] 447 Bug(exp) [ Release ] failures/expected/text.html [ Failure ] 448 Bug(exp) [ Debug ] failures/expected/text.html [ Failure ] 449 """) 450 451 def test_missing_file(self): 452 self.parse_exp('Bug(test) missing_file.html [ Failure ]') 453 self.assertTrue(self._exp.has_warnings(), 1) 454 455 456 class PrecedenceTests(Base): 457 def test_file_over_directory(self): 458 # This tests handling precedence of specific lines over directories 459 # and tests expectations covering entire directories. 460 exp_str = """ 461 Bug(x) failures/expected/text.html [ Failure ] 462 Bug(y) failures/expected [ WontFix ] 463 """ 464 self.parse_exp(exp_str) 465 self.assert_exp('failures/expected/text.html', FAIL) 466 self.assert_exp_list('failures/expected/crash.html', [WONTFIX, SKIP]) 467 468 exp_str = """ 469 Bug(x) failures/expected [ WontFix ] 470 Bug(y) failures/expected/text.html [ Failure ] 471 """ 472 self.parse_exp(exp_str) 473 self.assert_exp('failures/expected/text.html', FAIL) 474 self.assert_exp_list('failures/expected/crash.html', [WONTFIX, SKIP]) 475 476 def test_ambiguous(self): 477 self.assert_bad_expectations("Bug(test) [ Release ] passes/text.html [ Pass ]\n" 478 "Bug(test) [ Win ] passes/text.html [ Failure ]\n") 479 480 def test_more_specifiers(self): 481 self.assert_bad_expectations("Bug(test) [ Release ] passes/text.html [ Pass ]\n" 482 "Bug(test) [ Win Release ] passes/text.html [ Failure ]\n") 483 484 def test_order_in_file(self): 485 self.assert_bad_expectations("Bug(test) [ Win Release ] : passes/text.html [ Failure ]\n" 486 "Bug(test) [ Release ] : passes/text.html [ Pass ]\n") 487 488 def test_macro_overrides(self): 489 self.assert_bad_expectations("Bug(test) [ Win ] passes/text.html [ Pass ]\n" 490 "Bug(test) [ XP ] passes/text.html [ Failure ]\n") 491 492 493 class RemoveConfigurationsTest(Base): 494 def test_remove(self): 495 host = MockHost() 496 test_port = host.port_factory.get('test-win-xp', None) 497 test_port.test_exists = lambda test: True 498 test_port.test_isfile = lambda test: True 499 500 test_config = test_port.test_configuration() 501 test_port.expectations_dict = lambda: {"expectations": """Bug(x) [ Linux Win Release ] failures/expected/foo.html [ Failure ] 502 Bug(y) [ Win Mac Debug ] failures/expected/foo.html [ Crash ] 503 """} 504 expectations = TestExpectations(test_port, self.get_basic_tests()) 505 506 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', test_config) 507 508 self.assertEqual("""Bug(x) [ Linux Win7 Release ] failures/expected/foo.html [ Failure ] 509 Bug(y) [ Win Mac Debug ] failures/expected/foo.html [ Crash ] 510 """, actual_expectations) 511 512 def test_remove_needs_rebaseline(self): 513 host = MockHost() 514 test_port = host.port_factory.get('test-win-xp', None) 515 test_port.test_exists = lambda test: True 516 test_port.test_isfile = lambda test: True 517 518 test_config = test_port.test_configuration() 519 test_port.expectations_dict = lambda: {"expectations": """Bug(x) [ Win ] failures/expected/foo.html [ NeedsRebaseline ] 520 """} 521 expectations = TestExpectations(test_port, self.get_basic_tests()) 522 523 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', test_config) 524 525 self.assertEqual("""Bug(x) [ XP Debug ] failures/expected/foo.html [ NeedsRebaseline ] 526 Bug(x) [ Win7 ] failures/expected/foo.html [ NeedsRebaseline ] 527 """, actual_expectations) 528 529 def test_remove_line_with_comments(self): 530 host = MockHost() 531 test_port = host.port_factory.get('test-win-xp', None) 532 test_port.test_exists = lambda test: True 533 test_port.test_isfile = lambda test: True 534 535 test_config = test_port.test_configuration() 536 test_port.expectations_dict = lambda: {'expectations': """Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ] 537 538 # This comment line should get stripped. As should the preceding line. 539 Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ] 540 """} 541 expectations = TestExpectations(test_port) 542 543 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', test_config) 544 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration()) 545 546 self.assertEqual("""Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ] 547 """, actual_expectations) 548 549 def test_remove_line_with_comments_at_start(self): 550 host = MockHost() 551 test_port = host.port_factory.get('test-win-xp', None) 552 test_port.test_exists = lambda test: True 553 test_port.test_isfile = lambda test: True 554 555 test_config = test_port.test_configuration() 556 test_port.expectations_dict = lambda: {'expectations': """ 557 # This comment line should get stripped. As should the preceding line. 558 Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ] 559 560 Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ] 561 """} 562 expectations = TestExpectations(test_port) 563 564 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', test_config) 565 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration()) 566 567 self.assertEqual(""" 568 Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ] 569 """, actual_expectations) 570 571 def test_remove_line_with_comments_at_end_with_no_trailing_newline(self): 572 host = MockHost() 573 test_port = host.port_factory.get('test-win-xp', None) 574 test_port.test_exists = lambda test: True 575 test_port.test_isfile = lambda test: True 576 577 test_config = test_port.test_configuration() 578 test_port.expectations_dict = lambda: {'expectations': """Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ] 579 580 # This comment line should get stripped. As should the preceding line. 581 Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ]"""} 582 expectations = TestExpectations(test_port) 583 584 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', test_config) 585 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration()) 586 587 self.assertEqual("""Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]""", actual_expectations) 588 589 def test_remove_line_leaves_comments_for_next_line(self): 590 host = MockHost() 591 test_port = host.port_factory.get('test-win-xp', None) 592 test_port.test_exists = lambda test: True 593 test_port.test_isfile = lambda test: True 594 595 test_config = test_port.test_configuration() 596 test_port.expectations_dict = lambda: {'expectations': """ 597 # This comment line should not get stripped. 598 Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ] 599 Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ] 600 """} 601 expectations = TestExpectations(test_port) 602 603 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', test_config) 604 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration()) 605 606 self.assertEqual(""" 607 # This comment line should not get stripped. 608 Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ] 609 """, actual_expectations) 610 611 def test_remove_line_no_whitespace_lines(self): 612 host = MockHost() 613 test_port = host.port_factory.get('test-win-xp', None) 614 test_port.test_exists = lambda test: True 615 test_port.test_isfile = lambda test: True 616 617 test_config = test_port.test_configuration() 618 test_port.expectations_dict = lambda: {'expectations': """ 619 # This comment line should get stripped. 620 Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ] 621 # This comment line should not get stripped. 622 Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ] 623 """} 624 expectations = TestExpectations(test_port) 625 626 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', test_config) 627 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration()) 628 629 self.assertEqual(""" # This comment line should not get stripped. 630 Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ] 631 """, actual_expectations) 632 633 def test_remove_first_line(self): 634 host = MockHost() 635 test_port = host.port_factory.get('test-win-xp', None) 636 test_port.test_exists = lambda test: True 637 test_port.test_isfile = lambda test: True 638 639 test_config = test_port.test_configuration() 640 test_port.expectations_dict = lambda: {'expectations': """Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ] 641 # This comment line should not get stripped. 642 Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ] 643 """} 644 expectations = TestExpectations(test_port) 645 646 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', test_config) 647 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration()) 648 649 self.assertEqual(""" # This comment line should not get stripped. 650 Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ] 651 """, actual_expectations) 652 653 def test_remove_flaky_line(self): 654 host = MockHost() 655 test_port = host.port_factory.get('test-win-xp', None) 656 test_port.test_exists = lambda test: True 657 test_port.test_isfile = lambda test: True 658 659 test_config = test_port.test_configuration() 660 test_port.expectations_dict = lambda: {'expectations': """Bug(x) [ Win ] failures/expected/foo.html [ Failure Timeout ] 661 Bug(y) [ Mac ] failures/expected/foo.html [ Crash ] 662 """} 663 expectations = TestExpectations(test_port) 664 665 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', test_config) 666 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration()) 667 668 self.assertEqual("""Bug(x) [ Win Debug ] failures/expected/foo.html [ Failure Timeout ] 669 Bug(y) [ Mac ] failures/expected/foo.html [ Crash ] 670 """, actual_expectations) 671 672 673 class RebaseliningTest(Base): 674 def test_get_rebaselining_failures(self): 675 # Make sure we find a test as needing a rebaseline even if it is not marked as a failure. 676 self.parse_exp('Bug(x) failures/expected/text.html [ Rebaseline ]\n') 677 self.assertEqual(len(self._exp.get_rebaselining_failures()), 1) 678 679 self.parse_exp(self.get_basic_expectations()) 680 self.assertEqual(len(self._exp.get_rebaselining_failures()), 0) 681 682 683 class TestExpectationsParserTests(unittest.TestCase): 684 def __init__(self, testFunc): 685 host = MockHost() 686 test_port = host.port_factory.get('test-win-xp', None) 687 self._converter = TestConfigurationConverter(test_port.all_test_configurations(), test_port.configuration_specifier_macros()) 688 unittest.TestCase.__init__(self, testFunc) 689 self._parser = TestExpectationParser(host.port_factory.get('test-win-xp', None), [], is_lint_mode=False) 690 691 def test_expectation_line_for_test(self): 692 # This is kind of a silly test, but it at least ensures that we don't throw an error. 693 test_name = 'foo/test.html' 694 expectations = set(["PASS", "IMAGE"]) 695 696 expectation_line = TestExpectationLine() 697 expectation_line.original_string = test_name 698 expectation_line.name = test_name 699 expectation_line.filename = '<Bot TestExpectations>' 700 expectation_line.line_numbers = '0' 701 expectation_line.expectations = expectations 702 self._parser._parse_line(expectation_line) 703 704 self.assertEqual(self._parser.expectation_line_for_test(test_name, expectations), expectation_line) 705 706 707 class TestExpectationSerializationTests(unittest.TestCase): 708 def __init__(self, testFunc): 709 host = MockHost() 710 test_port = host.port_factory.get('test-win-xp', None) 711 self._converter = TestConfigurationConverter(test_port.all_test_configurations(), test_port.configuration_specifier_macros()) 712 unittest.TestCase.__init__(self, testFunc) 713 714 def _tokenize(self, line): 715 return TestExpectationParser._tokenize_line('path', line, 0) 716 717 def assert_round_trip(self, in_string, expected_string=None): 718 expectation = self._tokenize(in_string) 719 if expected_string is None: 720 expected_string = in_string 721 self.assertEqual(expected_string, expectation.to_string(self._converter)) 722 723 def assert_list_round_trip(self, in_string, expected_string=None): 724 host = MockHost() 725 parser = TestExpectationParser(host.port_factory.get('test-win-xp', None), [], is_lint_mode=False) 726 expectations = parser.parse('path', in_string) 727 if expected_string is None: 728 expected_string = in_string 729 self.assertEqual(expected_string, TestExpectations.list_to_string(expectations, self._converter)) 730 731 def test_unparsed_to_string(self): 732 expectation = TestExpectationLine() 733 734 self.assertEqual(expectation.to_string(self._converter), '') 735 expectation.comment = ' Qux.' 736 self.assertEqual(expectation.to_string(self._converter), '# Qux.') 737 expectation.name = 'bar' 738 self.assertEqual(expectation.to_string(self._converter), 'bar # Qux.') 739 expectation.specifiers = ['foo'] 740 # FIXME: case should be preserved here but we can't until we drop the old syntax. 741 self.assertEqual(expectation.to_string(self._converter), '[ FOO ] bar # Qux.') 742 expectation.expectations = ['bAz'] 743 self.assertEqual(expectation.to_string(self._converter), '[ FOO ] bar [ BAZ ] # Qux.') 744 expectation.expectations = ['bAz1', 'baZ2'] 745 self.assertEqual(expectation.to_string(self._converter), '[ FOO ] bar [ BAZ1 BAZ2 ] # Qux.') 746 expectation.specifiers = ['foo1', 'foO2'] 747 self.assertEqual(expectation.to_string(self._converter), '[ FOO1 FOO2 ] bar [ BAZ1 BAZ2 ] # Qux.') 748 expectation.warnings.append('Oh the horror.') 749 self.assertEqual(expectation.to_string(self._converter), '') 750 expectation.original_string = 'Yes it is!' 751 self.assertEqual(expectation.to_string(self._converter), 'Yes it is!') 752 753 def test_unparsed_list_to_string(self): 754 expectation = TestExpectationLine() 755 expectation.comment = 'Qux.' 756 expectation.name = 'bar' 757 expectation.specifiers = ['foo'] 758 expectation.expectations = ['bAz1', 'baZ2'] 759 # FIXME: case should be preserved here but we can't until we drop the old syntax. 760 self.assertEqual(TestExpectations.list_to_string([expectation]), '[ FOO ] bar [ BAZ1 BAZ2 ] #Qux.') 761 762 def test_parsed_to_string(self): 763 expectation_line = TestExpectationLine() 764 expectation_line.bugs = ['Bug(x)'] 765 expectation_line.name = 'test/name/for/realz.html' 766 expectation_line.parsed_expectations = set([IMAGE]) 767 self.assertEqual(expectation_line.to_string(self._converter), None) 768 expectation_line.matching_configurations = set([TestConfiguration('xp', 'x86', 'release')]) 769 self.assertEqual(expectation_line.to_string(self._converter), 'Bug(x) [ XP Release ] test/name/for/realz.html [ ImageOnlyFailure ]') 770 expectation_line.matching_configurations = set([TestConfiguration('xp', 'x86', 'release'), TestConfiguration('xp', 'x86', 'debug')]) 771 self.assertEqual(expectation_line.to_string(self._converter), 'Bug(x) [ XP ] test/name/for/realz.html [ ImageOnlyFailure ]') 772 773 def test_serialize_parsed_expectations(self): 774 expectation_line = TestExpectationLine() 775 expectation_line.parsed_expectations = set([]) 776 parsed_expectation_to_string = dict([[parsed_expectation, expectation_string] for expectation_string, parsed_expectation in TestExpectations.EXPECTATIONS.items()]) 777 self.assertEqual(expectation_line._serialize_parsed_expectations(parsed_expectation_to_string), '') 778 expectation_line.parsed_expectations = set([FAIL]) 779 self.assertEqual(expectation_line._serialize_parsed_expectations(parsed_expectation_to_string), 'fail') 780 expectation_line.parsed_expectations = set([PASS, IMAGE]) 781 self.assertEqual(expectation_line._serialize_parsed_expectations(parsed_expectation_to_string), 'image pass') 782 expectation_line.parsed_expectations = set([FAIL, PASS]) 783 self.assertEqual(expectation_line._serialize_parsed_expectations(parsed_expectation_to_string), 'pass fail') 784 785 def test_serialize_parsed_specifier_string(self): 786 expectation_line = TestExpectationLine() 787 expectation_line.bugs = ['garden-o-matic'] 788 expectation_line.parsed_specifiers = ['the', 'for'] 789 self.assertEqual(expectation_line._serialize_parsed_specifiers(self._converter, []), 'for the') 790 self.assertEqual(expectation_line._serialize_parsed_specifiers(self._converter, ['win']), 'for the win') 791 expectation_line.bugs = [] 792 expectation_line.parsed_specifiers = [] 793 self.assertEqual(expectation_line._serialize_parsed_specifiers(self._converter, []), '') 794 self.assertEqual(expectation_line._serialize_parsed_specifiers(self._converter, ['win']), 'win') 795 796 def test_format_line(self): 797 self.assertEqual(TestExpectationLine._format_line([], ['MODIFIERS'], 'name', ['EXPECTATIONS'], 'comment'), '[ MODIFIERS ] name [ EXPECTATIONS ] #comment') 798 self.assertEqual(TestExpectationLine._format_line([], ['MODIFIERS'], 'name', ['EXPECTATIONS'], None), '[ MODIFIERS ] name [ EXPECTATIONS ]') 799 800 def test_string_roundtrip(self): 801 self.assert_round_trip('') 802 self.assert_round_trip('[') 803 self.assert_round_trip('FOO [') 804 self.assert_round_trip('FOO ] bar') 805 self.assert_round_trip(' FOO [') 806 self.assert_round_trip(' [ FOO ] ') 807 self.assert_round_trip('[ FOO ] bar [ BAZ ]') 808 self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux.') 809 self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux.') 810 self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux. ') 811 self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux. ') 812 self.assert_round_trip('[ FOO ] ] ] bar BAZ') 813 self.assert_round_trip('[ FOO ] ] ] bar [ BAZ ]') 814 self.assert_round_trip('FOO ] ] bar ==== BAZ') 815 self.assert_round_trip('=') 816 self.assert_round_trip('#') 817 self.assert_round_trip('# ') 818 self.assert_round_trip('# Foo') 819 self.assert_round_trip('# Foo') 820 self.assert_round_trip('# Foo :') 821 self.assert_round_trip('# Foo : =') 822 823 def test_list_roundtrip(self): 824 self.assert_list_round_trip('') 825 self.assert_list_round_trip('\n') 826 self.assert_list_round_trip('\n\n') 827 self.assert_list_round_trip('bar') 828 self.assert_list_round_trip('bar\n# Qux.') 829 self.assert_list_round_trip('bar\n# Qux.\n') 830 831 def test_reconstitute_only_these(self): 832 lines = [] 833 reconstitute_only_these = [] 834 835 def add_line(matching_configurations, reconstitute): 836 expectation_line = TestExpectationLine() 837 expectation_line.original_string = "Nay" 838 expectation_line.bugs = ['Bug(x)'] 839 expectation_line.name = 'Yay' 840 expectation_line.parsed_expectations = set([IMAGE]) 841 expectation_line.matching_configurations = matching_configurations 842 lines.append(expectation_line) 843 if reconstitute: 844 reconstitute_only_these.append(expectation_line) 845 846 add_line(set([TestConfiguration('xp', 'x86', 'release')]), True) 847 add_line(set([TestConfiguration('xp', 'x86', 'release'), TestConfiguration('xp', 'x86', 'debug')]), False) 848 serialized = TestExpectations.list_to_string(lines, self._converter) 849 self.assertEqual(serialized, "Bug(x) [ XP Release ] Yay [ ImageOnlyFailure ]\nBug(x) [ XP ] Yay [ ImageOnlyFailure ]") 850 serialized = TestExpectations.list_to_string(lines, self._converter, reconstitute_only_these=reconstitute_only_these) 851 self.assertEqual(serialized, "Bug(x) [ XP Release ] Yay [ ImageOnlyFailure ]\nNay") 852 853 def disabled_test_string_whitespace_stripping(self): 854 # FIXME: Re-enable this test once we rework the code to no longer support the old syntax. 855 self.assert_round_trip('\n', '') 856 self.assert_round_trip(' [ FOO ] bar [ BAZ ]', '[ FOO ] bar [ BAZ ]') 857 self.assert_round_trip('[ FOO ] bar [ BAZ ]', '[ FOO ] bar [ BAZ ]') 858 self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux.', '[ FOO ] bar [ BAZ ] # Qux.') 859 self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux.', '[ FOO ] bar [ BAZ ] # Qux.') 860 self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux.', '[ FOO ] bar [ BAZ ] # Qux.') 861 self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux.', '[ FOO ] bar [ BAZ ] # Qux.') 862