1 # Copyright (C) 2010 Google Inc. All rights reserved.
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
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
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.
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.
29 import webkitpy.thirdparty.unittest2 as unittest
31 from webkitpy.common.host_mock import MockHost
32 from webkitpy.common.system.outputcapture import OutputCapture
34 from webkitpy.layout_tests.models.test_configuration import *
35 from webkitpy.layout_tests.models.test_expectations import *
38 from collections import OrderedDict
40 # Needed for Python < 2.7
41 from webkitpy.thirdparty.ordered_dict import OrderedDict
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.
48 def __init__(self, testFunc):
50 self._port = host.port_factory.get('test-win-xp', None)
52 unittest.TestCase.__init__(self, testFunc)
54 def get_test(self, test_name):
55 # FIXME: Remove this routine and just reference test names directly.
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')]
68 def get_basic_expectations(self):
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 ]
79 def parse_exp(self, expectations, overrides=None, is_lint_mode=False):
80 expectations_dict = OrderedDict()
81 expectations_dict['expectations'] = expectations
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)
88 def assert_exp_list(self, test, results):
89 self.assertEqual(self._exp.get_expectations(self.get_test(test)), set(results))
91 def assert_exp(self, test, result):
92 self.assert_exp_list(test, [result])
94 def assert_bad_expectations(self, expectations, overrides=None):
95 self.assertRaises(ParseError, self.parse_exp, expectations, is_lint_mode=True, overrides=overrides)
98 class BasicTests(Base):
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)
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')),
114 def test_result_was_expected(self):
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)
119 # test handling of SKIPped tests and results
120 self.assertEqual(TestExpectations.result_was_expected(SKIP, set([CRASH]), test_needs_rebaselining=False), True)
121 self.assertEqual(TestExpectations.result_was_expected(SKIP, set([LEAK]), test_needs_rebaselining=False), True)
123 # test handling of MISSING results and the REBASELINE specifier
124 self.assertEqual(TestExpectations.result_was_expected(MISSING, set([PASS]), test_needs_rebaselining=True), True)
125 self.assertEqual(TestExpectations.result_was_expected(MISSING, set([PASS]), test_needs_rebaselining=False), False)
127 self.assertTrue(TestExpectations.result_was_expected(PASS, set([NEEDS_REBASELINE]), test_needs_rebaselining=False))
128 self.assertTrue(TestExpectations.result_was_expected(MISSING, set([NEEDS_REBASELINE]), test_needs_rebaselining=False))
129 self.assertTrue(TestExpectations.result_was_expected(TEXT, set([NEEDS_REBASELINE]), test_needs_rebaselining=False))
130 self.assertTrue(TestExpectations.result_was_expected(IMAGE, set([NEEDS_REBASELINE]), test_needs_rebaselining=False))
131 self.assertTrue(TestExpectations.result_was_expected(IMAGE_PLUS_TEXT, set([NEEDS_REBASELINE]), test_needs_rebaselining=False))
132 self.assertTrue(TestExpectations.result_was_expected(AUDIO, set([NEEDS_REBASELINE]), test_needs_rebaselining=False))
133 self.assertFalse(TestExpectations.result_was_expected(TIMEOUT, set([NEEDS_REBASELINE]), test_needs_rebaselining=False))
134 self.assertFalse(TestExpectations.result_was_expected(CRASH, set([NEEDS_REBASELINE]), test_needs_rebaselining=False))
135 self.assertFalse(TestExpectations.result_was_expected(LEAK, set([NEEDS_REBASELINE]), test_needs_rebaselining=False))
137 def test_remove_pixel_failures(self):
138 self.assertEqual(TestExpectations.remove_pixel_failures(set([FAIL])), set([FAIL]))
139 self.assertEqual(TestExpectations.remove_pixel_failures(set([PASS])), set([PASS]))
140 self.assertEqual(TestExpectations.remove_pixel_failures(set([IMAGE])), set([PASS]))
141 self.assertEqual(TestExpectations.remove_pixel_failures(set([FAIL])), set([FAIL]))
142 self.assertEqual(TestExpectations.remove_pixel_failures(set([PASS, IMAGE, CRASH])), set([PASS, CRASH]))
144 def test_suffixes_for_expectations(self):
145 self.assertEqual(TestExpectations.suffixes_for_expectations(set([FAIL])), set(['txt', 'png', 'wav']))
146 self.assertEqual(TestExpectations.suffixes_for_expectations(set([IMAGE])), set(['png']))
147 self.assertEqual(TestExpectations.suffixes_for_expectations(set([FAIL, IMAGE, CRASH])), set(['txt', 'png', 'wav']))
148 self.assertEqual(TestExpectations.suffixes_for_expectations(set()), set())
150 def test_category_expectations(self):
151 # This test checks unknown tests are not present in the
152 # expectations and that known test part of a test category is
153 # present in the expectations.
154 exp_str = 'Bug(x) failures/expected [ WontFix ]'
155 self.parse_exp(exp_str)
156 test_name = 'failures/expected/unknown-test.html'
157 unknown_test = self.get_test(test_name)
158 self.assertRaises(KeyError, self._exp.get_expectations,
160 self.assert_exp_list('failures/expected/crash.html', [WONTFIX, SKIP])
162 def test_get_expectations_string(self):
163 self.parse_exp(self.get_basic_expectations())
164 self.assertEqual(self._exp.get_expectations_string(
165 self.get_test('failures/expected/text.html')),
168 def test_expectation_to_string(self):
169 # Normal cases are handled by other tests.
170 self.parse_exp(self.get_basic_expectations())
171 self.assertRaises(ValueError, self._exp.expectation_to_string,
174 def test_get_test_set(self):
175 # Handle some corner cases for this routine not covered by other tests.
176 self.parse_exp(self.get_basic_expectations())
177 s = self._exp.get_test_set(WONTFIX)
179 set([self.get_test('failures/expected/crash.html'),
180 self.get_test('failures/expected/image_checksum.html')]))
182 def test_needs_rebaseline_reftest(self):
184 filesystem = self._port.host.filesystem
185 filesystem.write_text_file(filesystem.join(self._port.layout_tests_dir(), 'failures/expected/needsrebaseline.html'), 'content')
186 filesystem.write_text_file(filesystem.join(self._port.layout_tests_dir(), 'failures/expected/needsrebaseline-expected.html'), 'content')
187 filesystem.write_text_file(filesystem.join(self._port.layout_tests_dir(), 'failures/expected/needsmanualrebaseline.html'), 'content')
188 filesystem.write_text_file(filesystem.join(self._port.layout_tests_dir(), 'failures/expected/needsmanualrebaseline-expected.html'), 'content')
189 self.parse_exp("""Bug(user) failures/expected/needsrebaseline.html [ NeedsRebaseline ]
190 Bug(user) failures/expected/needsmanualrebaseline.html [ NeedsManualRebaseline ]""", is_lint_mode=True)
191 self.assertFalse(True, "ParseError wasn't raised")
192 except ParseError, e:
193 warnings = """expectations:1 A reftest cannot be marked as NeedsRebaseline/NeedsManualRebaseline failures/expected/needsrebaseline.html
194 expectations:2 A reftest cannot be marked as NeedsRebaseline/NeedsManualRebaseline failures/expected/needsmanualrebaseline.html"""
195 self.assertEqual(str(e), warnings)
197 def test_parse_warning(self):
199 filesystem = self._port.host.filesystem
200 filesystem.write_text_file(filesystem.join(self._port.layout_tests_dir(), 'disabled-test.html-disabled'), 'content')
201 self.get_test('disabled-test.html-disabled'),
202 self.parse_exp("Bug(user) [ FOO ] failures/expected/text.html [ Failure ]\n"
203 "Bug(user) non-existent-test.html [ Failure ]\n"
204 "Bug(user) disabled-test.html-disabled [ ImageOnlyFailure ]", is_lint_mode=True)
205 self.assertFalse(True, "ParseError wasn't raised")
206 except ParseError, e:
207 warnings = ("expectations:1 Unrecognized specifier 'foo' failures/expected/text.html\n"
208 "expectations:2 Path does not exist. non-existent-test.html")
209 self.assertEqual(str(e), warnings)
211 def test_parse_warnings_are_logged_if_not_in_lint_mode(self):
215 self.parse_exp('-- this should be a syntax error', is_lint_mode=False)
217 _, _, logs = oc.restore_output()
218 self.assertNotEquals(logs, '')
220 def test_error_on_different_platform(self):
221 # parse_exp uses a Windows port. Assert errors on Mac show up in lint mode.
222 self.assertRaises(ParseError, self.parse_exp,
223 'Bug(test) [ Mac ] failures/expected/text.html [ Failure ]\nBug(test) [ Mac ] failures/expected/text.html [ Failure ]',
226 def test_error_on_different_build_type(self):
227 # parse_exp uses a Release port. Assert errors on DEBUG show up in lint mode.
228 self.assertRaises(ParseError, self.parse_exp,
229 'Bug(test) [ Debug ] failures/expected/text.html [ Failure ]\nBug(test) [ Debug ] failures/expected/text.html [ Failure ]',
232 def test_overrides(self):
233 self.parse_exp("Bug(exp) failures/expected/text.html [ Failure ]",
234 "Bug(override) failures/expected/text.html [ ImageOnlyFailure ]")
235 self.assert_exp_list('failures/expected/text.html', [FAIL, IMAGE])
237 def test_overrides__directory(self):
238 self.parse_exp("Bug(exp) failures/expected/text.html [ Failure ]",
239 "Bug(override) failures/expected [ Crash ]")
240 self.assert_exp_list('failures/expected/text.html', [FAIL, CRASH])
241 self.assert_exp_list('failures/expected/image.html', [CRASH])
243 def test_overrides__duplicate(self):
244 self.assert_bad_expectations("Bug(exp) failures/expected/text.html [ Failure ]",
245 "Bug(override) failures/expected/text.html [ ImageOnlyFailure ]\n"
246 "Bug(override) failures/expected/text.html [ Crash ]\n")
248 def test_pixel_tests_flag(self):
249 def match(test, result, pixel_tests_enabled):
250 return self._exp.matches_an_expected_result(
251 self.get_test(test), result, pixel_tests_enabled)
253 self.parse_exp(self.get_basic_expectations())
254 self.assertTrue(match('failures/expected/text.html', FAIL, True))
255 self.assertTrue(match('failures/expected/text.html', FAIL, False))
256 self.assertFalse(match('failures/expected/text.html', CRASH, True))
257 self.assertFalse(match('failures/expected/text.html', CRASH, False))
258 self.assertTrue(match('failures/expected/image_checksum.html', PASS, True))
259 self.assertTrue(match('failures/expected/image_checksum.html', PASS, False))
260 self.assertTrue(match('failures/expected/crash.html', PASS, False))
261 self.assertTrue(match('failures/expected/needsrebaseline.html', TEXT, True))
262 self.assertFalse(match('failures/expected/needsrebaseline.html', CRASH, True))
263 self.assertTrue(match('failures/expected/needsmanualrebaseline.html', TEXT, True))
264 self.assertFalse(match('failures/expected/needsmanualrebaseline.html', CRASH, True))
265 self.assertTrue(match('passes/text.html', PASS, False))
267 def test_more_specific_override_resets_skip(self):
268 self.parse_exp("Bug(x) failures/expected [ Skip ]\n"
269 "Bug(x) failures/expected/text.html [ ImageOnlyFailure ]\n")
270 self.assert_exp('failures/expected/text.html', IMAGE)
271 self.assertFalse(self._port._filesystem.join(self._port.layout_tests_dir(),
272 'failures/expected/text.html') in
273 self._exp.get_tests_with_result_type(SKIP))
275 def test_bot_test_expectations(self):
276 """Test that expectations are merged rather than overridden when using flaky option 'unexpected'."""
277 test_name1 = 'failures/expected/text.html'
278 test_name2 = 'passes/text.html'
280 expectations_dict = OrderedDict()
281 expectations_dict['expectations'] = "Bug(x) %s [ ImageOnlyFailure ]\nBug(x) %s [ Slow ]\n" % (test_name1, test_name2)
282 self._port.expectations_dict = lambda: expectations_dict
284 expectations = TestExpectations(self._port, self.get_basic_tests())
285 self.assertEqual(expectations.get_expectations(self.get_test(test_name1)), set([IMAGE]))
286 self.assertEqual(expectations.get_expectations(self.get_test(test_name2)), set([SLOW]))
288 def bot_expectations():
289 return {test_name1: ['PASS', 'TIMEOUT'], test_name2: ['CRASH']}
290 self._port.bot_expectations = bot_expectations
291 self._port._options.ignore_flaky_tests = 'unexpected'
293 expectations = TestExpectations(self._port, self.get_basic_tests())
294 self.assertEqual(expectations.get_expectations(self.get_test(test_name1)), set([PASS, IMAGE, TIMEOUT]))
295 self.assertEqual(expectations.get_expectations(self.get_test(test_name2)), set([CRASH, SLOW]))
297 class SkippedTests(Base):
298 def check(self, expectations, overrides, skips, lint=False, expected_results=[WONTFIX, SKIP, FAIL]):
299 port = MockHost().port_factory.get('test-win-xp')
300 port._filesystem.write_text_file(port._filesystem.join(port.layout_tests_dir(), 'failures/expected/text.html'), 'foo')
301 expectations_dict = OrderedDict()
302 expectations_dict['expectations'] = expectations
304 expectations_dict['overrides'] = overrides
305 port.expectations_dict = lambda: expectations_dict
306 port.skipped_layout_tests = lambda tests: set(skips)
307 expectations_to_lint = expectations_dict if lint else None
308 exp = TestExpectations(port, ['failures/expected/text.html'], expectations_dict=expectations_to_lint, is_lint_mode=lint)
309 self.assertEqual(exp.get_expectations('failures/expected/text.html'), set(expected_results))
311 def test_skipped_tests_work(self):
312 self.check(expectations='', overrides=None, skips=['failures/expected/text.html'], expected_results=[WONTFIX, SKIP])
314 def test_duplicate_skipped_test_fails_lint(self):
315 self.assertRaises(ParseError, self.check, expectations='Bug(x) failures/expected/text.html [ Failure ]\n',
316 overrides=None, skips=['failures/expected/text.html'], lint=True)
318 def test_skipped_file_overrides_expectations(self):
319 self.check(expectations='Bug(x) failures/expected/text.html [ Failure ]\n', overrides=None,
320 skips=['failures/expected/text.html'])
322 def test_skipped_dir_overrides_expectations(self):
323 self.check(expectations='Bug(x) failures/expected/text.html [ Failure ]\n', overrides=None,
324 skips=['failures/expected'])
326 def test_skipped_file_overrides_overrides(self):
327 self.check(expectations='', overrides='Bug(x) failures/expected/text.html [ Failure ]\n',
328 skips=['failures/expected/text.html'])
330 def test_skipped_dir_overrides_overrides(self):
331 self.check(expectations='', overrides='Bug(x) failures/expected/text.html [ Failure ]\n',
332 skips=['failures/expected'])
334 def test_skipped_entry_dont_exist(self):
335 port = MockHost().port_factory.get('test-win-xp')
336 expectations_dict = OrderedDict()
337 expectations_dict['expectations'] = ''
338 port.expectations_dict = lambda: expectations_dict
339 port.skipped_layout_tests = lambda tests: set(['foo/bar/baz.html'])
340 capture = OutputCapture()
341 capture.capture_output()
342 exp = TestExpectations(port)
343 _, _, logs = capture.restore_output()
344 self.assertEqual('The following test foo/bar/baz.html from the Skipped list doesn\'t exist\n', logs)
346 def test_expectations_string(self):
347 self.parse_exp(self.get_basic_expectations())
348 notrun = 'failures/expected/text.html'
349 self._exp.add_extra_skipped_tests([notrun])
350 self.assertEqual('NOTRUN', self._exp.get_expectations_string(notrun))
353 class ExpectationSyntaxTests(Base):
354 def test_unrecognized_expectation(self):
355 self.assert_bad_expectations('Bug(test) failures/expected/text.html [ Unknown ]')
357 def test_macro(self):
358 exp_str = 'Bug(test) [ Win ] failures/expected/text.html [ Failure ]'
359 self.parse_exp(exp_str)
360 self.assert_exp('failures/expected/text.html', FAIL)
362 def assert_tokenize_exp(self, line, bugs=None, specifiers=None, expectations=None, warnings=None, comment=None, name='foo.html'):
364 specifiers = specifiers or []
365 expectations = expectations or []
366 warnings = warnings or []
367 filename = 'TestExpectations'
369 expectation_line = TestExpectationParser._tokenize_line(filename, line, line_number)
370 self.assertEqual(expectation_line.warnings, warnings)
371 self.assertEqual(expectation_line.name, name)
372 self.assertEqual(expectation_line.filename, filename)
373 self.assertEqual(expectation_line.line_numbers, line_number)
375 self.assertEqual(expectation_line.specifiers, specifiers)
376 self.assertEqual(expectation_line.expectations, expectations)
378 def test_comments(self):
379 self.assert_tokenize_exp("# comment", name=None, comment="# comment")
380 self.assert_tokenize_exp("foo.html [ Pass ] # comment", comment="# comment", expectations=['PASS'], specifiers=[])
382 def test_config_specifiers(self):
383 self.assert_tokenize_exp('[ Mac ] foo.html [ Failure ] ', specifiers=['MAC'], expectations=['FAIL'])
385 def test_unknown_config(self):
386 self.assert_tokenize_exp('[ Foo ] foo.html [ Pass ]', specifiers=['Foo'], expectations=['PASS'])
388 def test_unknown_expectation(self):
389 self.assert_tokenize_exp('foo.html [ Audio ]', warnings=['Unrecognized expectation "Audio"'])
392 self.assert_tokenize_exp('foo.html [ Skip ]', specifiers=[], expectations=['SKIP'])
395 self.assert_tokenize_exp('foo.html [ Slow ]', specifiers=[], expectations=['SLOW'])
397 def test_wontfix(self):
398 self.assert_tokenize_exp('foo.html [ WontFix ]', specifiers=[], expectations=['WONTFIX', 'SKIP'])
399 self.assert_tokenize_exp('foo.html [ WontFix ImageOnlyFailure ]', specifiers=[], expectations=['WONTFIX', 'SKIP'],
400 warnings=['A test marked Skip or WontFix must not have other expectations.'])
402 def test_blank_line(self):
403 self.assert_tokenize_exp('', name=None)
405 def test_warnings(self):
406 self.assert_tokenize_exp('[ Mac ]', warnings=['Did not find a test name.', 'Missing expectations.'], name=None)
407 self.assert_tokenize_exp('[ [', warnings=['unexpected "["', 'Missing expectations.'], name=None)
408 self.assert_tokenize_exp('crbug.com/12345 ]', warnings=['unexpected "]"', 'Missing expectations.'], name=None)
410 self.assert_tokenize_exp('foo.html crbug.com/12345 ]', warnings=['"crbug.com/12345" is not at the start of the line.', 'Missing expectations.'])
411 self.assert_tokenize_exp('foo.html', warnings=['Missing expectations.'])
414 class SemanticTests(Base):
415 def test_bug_format(self):
416 self.assertRaises(ParseError, self.parse_exp, 'BUG1234 failures/expected/text.html [ Failure ]', is_lint_mode=True)
418 def test_bad_bugid(self):
420 self.parse_exp('crbug/1234 failures/expected/text.html [ Failure ]', is_lint_mode=True)
421 self.fail('should have raised an error about a bad bug identifier')
422 except ParseError, exp:
423 self.assertEqual(len(exp.warnings), 3)
425 def test_missing_bugid(self):
426 self.parse_exp('failures/expected/text.html [ Failure ]', is_lint_mode=False)
427 self.assertFalse(self._exp.has_warnings())
430 self.parse_exp('failures/expected/text.html [ Failure ]', is_lint_mode=True)
431 except ParseError, exp:
432 self.assertEqual(exp.warnings, ['expectations:1 Test lacks BUG specifier. failures/expected/text.html'])
434 def test_skip_and_wontfix(self):
435 # Skip is not allowed to have other expectations as well, because those
436 # expectations won't be exercised and may become stale .
437 self.parse_exp('failures/expected/text.html [ Failure Skip ]')
438 self.assertTrue(self._exp.has_warnings())
440 self.parse_exp('failures/expected/text.html [ Crash WontFix ]')
441 self.assertTrue(self._exp.has_warnings())
443 self.parse_exp('failures/expected/text.html [ Pass WontFix ]')
444 self.assertTrue(self._exp.has_warnings())
446 def test_rebaseline(self):
447 # Can't lint a file w/ 'REBASELINE' in it.
448 self.assertRaises(ParseError, self.parse_exp,
449 'Bug(test) failures/expected/text.html [ Failure Rebaseline ]',
452 def test_duplicates(self):
453 self.assertRaises(ParseError, self.parse_exp, """
454 Bug(exp) failures/expected/text.html [ Failure ]
455 Bug(exp) failures/expected/text.html [ ImageOnlyFailure ]""", is_lint_mode=True)
457 self.assertRaises(ParseError, self.parse_exp,
458 self.get_basic_expectations(), overrides="""
459 Bug(override) failures/expected/text.html [ Failure ]
460 Bug(override) failures/expected/text.html [ ImageOnlyFailure ]""", is_lint_mode=True)
462 def test_duplicate_with_line_before_preceding_line(self):
463 self.assert_bad_expectations("""Bug(exp) [ Debug ] failures/expected/text.html [ Failure ]
464 Bug(exp) [ Release ] failures/expected/text.html [ Failure ]
465 Bug(exp) [ Debug ] failures/expected/text.html [ Failure ]
468 def test_missing_file(self):
469 self.parse_exp('Bug(test) missing_file.html [ Failure ]')
470 self.assertTrue(self._exp.has_warnings(), 1)
473 class PrecedenceTests(Base):
474 def test_file_over_directory(self):
475 # This tests handling precedence of specific lines over directories
476 # and tests expectations covering entire directories.
478 Bug(x) failures/expected/text.html [ Failure ]
479 Bug(y) failures/expected [ WontFix ]
481 self.parse_exp(exp_str)
482 self.assert_exp('failures/expected/text.html', FAIL)
483 self.assert_exp_list('failures/expected/crash.html', [WONTFIX, SKIP])
486 Bug(x) failures/expected [ WontFix ]
487 Bug(y) failures/expected/text.html [ Failure ]
489 self.parse_exp(exp_str)
490 self.assert_exp('failures/expected/text.html', FAIL)
491 self.assert_exp_list('failures/expected/crash.html', [WONTFIX, SKIP])
493 def test_ambiguous(self):
494 self.assert_bad_expectations("Bug(test) [ Release ] passes/text.html [ Pass ]\n"
495 "Bug(test) [ Win ] passes/text.html [ Failure ]\n")
497 def test_more_specifiers(self):
498 self.assert_bad_expectations("Bug(test) [ Release ] passes/text.html [ Pass ]\n"
499 "Bug(test) [ Win Release ] passes/text.html [ Failure ]\n")
501 def test_order_in_file(self):
502 self.assert_bad_expectations("Bug(test) [ Win Release ] : passes/text.html [ Failure ]\n"
503 "Bug(test) [ Release ] : passes/text.html [ Pass ]\n")
505 def test_macro_overrides(self):
506 self.assert_bad_expectations("Bug(test) [ Win ] passes/text.html [ Pass ]\n"
507 "Bug(test) [ XP ] passes/text.html [ Failure ]\n")
510 class RemoveConfigurationsTest(Base):
511 def test_remove(self):
513 test_port = host.port_factory.get('test-win-xp', None)
514 test_port.test_exists = lambda test: True
515 test_port.test_isfile = lambda test: True
517 test_config = test_port.test_configuration()
518 test_port.expectations_dict = lambda: {"expectations": """Bug(x) [ Linux Win Release ] failures/expected/foo.html [ Failure ]
519 Bug(y) [ Win Mac Debug ] failures/expected/foo.html [ Crash ]
521 expectations = TestExpectations(test_port, self.get_basic_tests())
523 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', test_config)
525 self.assertEqual("""Bug(x) [ Linux Win7 Release ] failures/expected/foo.html [ Failure ]
526 Bug(y) [ Win Mac Debug ] failures/expected/foo.html [ Crash ]
527 """, actual_expectations)
529 def test_remove_needs_rebaseline(self):
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
535 test_config = test_port.test_configuration()
536 test_port.expectations_dict = lambda: {"expectations": """Bug(x) [ Win ] failures/expected/foo.html [ NeedsRebaseline ]
538 expectations = TestExpectations(test_port, self.get_basic_tests())
540 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', test_config)
542 self.assertEqual("""Bug(x) [ XP Debug ] failures/expected/foo.html [ NeedsRebaseline ]
543 Bug(x) [ Win7 ] failures/expected/foo.html [ NeedsRebaseline ]
544 """, actual_expectations)
546 def test_remove_line_with_comments(self):
548 test_port = host.port_factory.get('test-win-xp', None)
549 test_port.test_exists = lambda test: True
550 test_port.test_isfile = lambda test: True
552 test_config = test_port.test_configuration()
553 test_port.expectations_dict = lambda: {'expectations': """Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
555 # This comment line should get stripped. As should the preceding line.
556 Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ]
558 expectations = TestExpectations(test_port)
560 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', test_config)
561 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration())
563 self.assertEqual("""Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
564 """, actual_expectations)
566 def test_remove_line_with_comments_at_start(self):
568 test_port = host.port_factory.get('test-win-xp', None)
569 test_port.test_exists = lambda test: True
570 test_port.test_isfile = lambda test: True
572 test_config = test_port.test_configuration()
573 test_port.expectations_dict = lambda: {'expectations': """
574 # This comment line should get stripped. As should the preceding line.
575 Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ]
577 Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
579 expectations = TestExpectations(test_port)
581 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', test_config)
582 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration())
585 Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
586 """, actual_expectations)
588 def test_remove_line_with_comments_at_end_with_no_trailing_newline(self):
590 test_port = host.port_factory.get('test-win-xp', None)
591 test_port.test_exists = lambda test: True
592 test_port.test_isfile = lambda test: True
594 test_config = test_port.test_configuration()
595 test_port.expectations_dict = lambda: {'expectations': """Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
597 # This comment line should get stripped. As should the preceding line.
598 Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ]"""}
599 expectations = TestExpectations(test_port)
601 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', test_config)
602 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration())
604 self.assertEqual("""Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]""", actual_expectations)
606 def test_remove_line_leaves_comments_for_next_line(self):
608 test_port = host.port_factory.get('test-win-xp', None)
609 test_port.test_exists = lambda test: True
610 test_port.test_isfile = lambda test: True
612 test_config = test_port.test_configuration()
613 test_port.expectations_dict = lambda: {'expectations': """
614 # This comment line should not get stripped.
615 Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ]
616 Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
618 expectations = TestExpectations(test_port)
620 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', test_config)
621 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration())
624 # This comment line should not get stripped.
625 Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
626 """, actual_expectations)
628 def test_remove_line_no_whitespace_lines(self):
630 test_port = host.port_factory.get('test-win-xp', None)
631 test_port.test_exists = lambda test: True
632 test_port.test_isfile = lambda test: True
634 test_config = test_port.test_configuration()
635 test_port.expectations_dict = lambda: {'expectations': """
636 # This comment line should get stripped.
637 Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ]
638 # This comment line should not get stripped.
639 Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
641 expectations = TestExpectations(test_port)
643 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', test_config)
644 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration())
646 self.assertEqual(""" # This comment line should not get stripped.
647 Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
648 """, actual_expectations)
650 def test_remove_first_line(self):
652 test_port = host.port_factory.get('test-win-xp', None)
653 test_port.test_exists = lambda test: True
654 test_port.test_isfile = lambda test: True
656 test_config = test_port.test_configuration()
657 test_port.expectations_dict = lambda: {'expectations': """Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ]
658 # This comment line should not get stripped.
659 Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
661 expectations = TestExpectations(test_port)
663 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', test_config)
664 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration())
666 self.assertEqual(""" # This comment line should not get stripped.
667 Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
668 """, actual_expectations)
670 def test_remove_flaky_line(self):
672 test_port = host.port_factory.get('test-win-xp', None)
673 test_port.test_exists = lambda test: True
674 test_port.test_isfile = lambda test: True
676 test_config = test_port.test_configuration()
677 test_port.expectations_dict = lambda: {'expectations': """Bug(x) [ Win ] failures/expected/foo.html [ Failure Timeout ]
678 Bug(y) [ Mac ] failures/expected/foo.html [ Crash ]
680 expectations = TestExpectations(test_port)
682 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', test_config)
683 actual_expectations = expectations.remove_configuration_from_test('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration())
685 self.assertEqual("""Bug(x) [ Win Debug ] failures/expected/foo.html [ Failure Timeout ]
686 Bug(y) [ Mac ] failures/expected/foo.html [ Crash ]
687 """, actual_expectations)
690 class RebaseliningTest(Base):
691 def test_get_rebaselining_failures(self):
692 # Make sure we find a test as needing a rebaseline even if it is not marked as a failure.
693 self.parse_exp('Bug(x) failures/expected/text.html [ Rebaseline ]\n')
694 self.assertEqual(len(self._exp.get_rebaselining_failures()), 1)
696 self.parse_exp(self.get_basic_expectations())
697 self.assertEqual(len(self._exp.get_rebaselining_failures()), 0)
700 class TestExpectationsParserTests(unittest.TestCase):
701 def __init__(self, testFunc):
703 test_port = host.port_factory.get('test-win-xp', None)
704 self._converter = TestConfigurationConverter(test_port.all_test_configurations(), test_port.configuration_specifier_macros())
705 unittest.TestCase.__init__(self, testFunc)
706 self._parser = TestExpectationParser(host.port_factory.get('test-win-xp', None), [], is_lint_mode=False)
708 def test_expectation_line_for_test(self):
709 # This is kind of a silly test, but it at least ensures that we don't throw an error.
710 test_name = 'foo/test.html'
711 expectations = set(["PASS", "IMAGE"])
713 expectation_line = TestExpectationLine()
714 expectation_line.original_string = test_name
715 expectation_line.name = test_name
716 expectation_line.filename = '<Bot TestExpectations>'
717 expectation_line.line_numbers = '0'
718 expectation_line.expectations = expectations
719 self._parser._parse_line(expectation_line)
721 self.assertEqual(self._parser.expectation_line_for_test(test_name, expectations), expectation_line)
724 class TestExpectationSerializationTests(unittest.TestCase):
725 def __init__(self, testFunc):
727 test_port = host.port_factory.get('test-win-xp', None)
728 self._converter = TestConfigurationConverter(test_port.all_test_configurations(), test_port.configuration_specifier_macros())
729 unittest.TestCase.__init__(self, testFunc)
731 def _tokenize(self, line):
732 return TestExpectationParser._tokenize_line('path', line, 0)
734 def assert_round_trip(self, in_string, expected_string=None):
735 expectation = self._tokenize(in_string)
736 if expected_string is None:
737 expected_string = in_string
738 self.assertEqual(expected_string, expectation.to_string(self._converter))
740 def assert_list_round_trip(self, in_string, expected_string=None):
742 parser = TestExpectationParser(host.port_factory.get('test-win-xp', None), [], is_lint_mode=False)
743 expectations = parser.parse('path', in_string)
744 if expected_string is None:
745 expected_string = in_string
746 self.assertEqual(expected_string, TestExpectations.list_to_string(expectations, self._converter))
748 def test_unparsed_to_string(self):
749 expectation = TestExpectationLine()
751 self.assertEqual(expectation.to_string(self._converter), '')
752 expectation.comment = ' Qux.'
753 self.assertEqual(expectation.to_string(self._converter), '# Qux.')
754 expectation.name = 'bar'
755 self.assertEqual(expectation.to_string(self._converter), 'bar # Qux.')
756 expectation.specifiers = ['foo']
757 # FIXME: case should be preserved here but we can't until we drop the old syntax.
758 self.assertEqual(expectation.to_string(self._converter), '[ FOO ] bar # Qux.')
759 expectation.expectations = ['bAz']
760 self.assertEqual(expectation.to_string(self._converter), '[ FOO ] bar [ BAZ ] # Qux.')
761 expectation.expectations = ['bAz1', 'baZ2']
762 self.assertEqual(expectation.to_string(self._converter), '[ FOO ] bar [ BAZ1 BAZ2 ] # Qux.')
763 expectation.specifiers = ['foo1', 'foO2']
764 self.assertEqual(expectation.to_string(self._converter), '[ FOO1 FOO2 ] bar [ BAZ1 BAZ2 ] # Qux.')
765 expectation.warnings.append('Oh the horror.')
766 self.assertEqual(expectation.to_string(self._converter), '')
767 expectation.original_string = 'Yes it is!'
768 self.assertEqual(expectation.to_string(self._converter), 'Yes it is!')
770 def test_unparsed_list_to_string(self):
771 expectation = TestExpectationLine()
772 expectation.comment = 'Qux.'
773 expectation.name = 'bar'
774 expectation.specifiers = ['foo']
775 expectation.expectations = ['bAz1', 'baZ2']
776 # FIXME: case should be preserved here but we can't until we drop the old syntax.
777 self.assertEqual(TestExpectations.list_to_string([expectation]), '[ FOO ] bar [ BAZ1 BAZ2 ] #Qux.')
779 def test_parsed_to_string(self):
780 expectation_line = TestExpectationLine()
781 expectation_line.bugs = ['Bug(x)']
782 expectation_line.name = 'test/name/for/realz.html'
783 expectation_line.parsed_expectations = set([IMAGE])
784 self.assertEqual(expectation_line.to_string(self._converter), None)
785 expectation_line.matching_configurations = set([TestConfiguration('xp', 'x86', 'release')])
786 self.assertEqual(expectation_line.to_string(self._converter), 'Bug(x) [ XP Release ] test/name/for/realz.html [ ImageOnlyFailure ]')
787 expectation_line.matching_configurations = set([TestConfiguration('xp', 'x86', 'release'), TestConfiguration('xp', 'x86', 'debug')])
788 self.assertEqual(expectation_line.to_string(self._converter), 'Bug(x) [ XP ] test/name/for/realz.html [ ImageOnlyFailure ]')
790 def test_serialize_parsed_expectations(self):
791 expectation_line = TestExpectationLine()
792 expectation_line.parsed_expectations = set([])
793 parsed_expectation_to_string = dict([[parsed_expectation, expectation_string] for expectation_string, parsed_expectation in TestExpectations.EXPECTATIONS.items()])
794 self.assertEqual(expectation_line._serialize_parsed_expectations(parsed_expectation_to_string), '')
795 expectation_line.parsed_expectations = set([FAIL])
796 self.assertEqual(expectation_line._serialize_parsed_expectations(parsed_expectation_to_string), 'fail')
797 expectation_line.parsed_expectations = set([PASS, IMAGE])
798 self.assertEqual(expectation_line._serialize_parsed_expectations(parsed_expectation_to_string), 'image pass')
799 expectation_line.parsed_expectations = set([FAIL, PASS])
800 self.assertEqual(expectation_line._serialize_parsed_expectations(parsed_expectation_to_string), 'pass fail')
802 def test_serialize_parsed_specifier_string(self):
803 expectation_line = TestExpectationLine()
804 expectation_line.bugs = ['garden-o-matic']
805 expectation_line.parsed_specifiers = ['the', 'for']
806 self.assertEqual(expectation_line._serialize_parsed_specifiers(self._converter, []), 'for the')
807 self.assertEqual(expectation_line._serialize_parsed_specifiers(self._converter, ['win']), 'for the win')
808 expectation_line.bugs = []
809 expectation_line.parsed_specifiers = []
810 self.assertEqual(expectation_line._serialize_parsed_specifiers(self._converter, []), '')
811 self.assertEqual(expectation_line._serialize_parsed_specifiers(self._converter, ['win']), 'win')
813 def test_format_line(self):
814 self.assertEqual(TestExpectationLine._format_line([], ['MODIFIERS'], 'name', ['EXPECTATIONS'], 'comment'), '[ MODIFIERS ] name [ EXPECTATIONS ] #comment')
815 self.assertEqual(TestExpectationLine._format_line([], ['MODIFIERS'], 'name', ['EXPECTATIONS'], None), '[ MODIFIERS ] name [ EXPECTATIONS ]')
817 def test_string_roundtrip(self):
818 self.assert_round_trip('')
819 self.assert_round_trip('[')
820 self.assert_round_trip('FOO [')
821 self.assert_round_trip('FOO ] bar')
822 self.assert_round_trip(' FOO [')
823 self.assert_round_trip(' [ FOO ] ')
824 self.assert_round_trip('[ FOO ] bar [ BAZ ]')
825 self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux.')
826 self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux.')
827 self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux. ')
828 self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux. ')
829 self.assert_round_trip('[ FOO ] ] ] bar BAZ')
830 self.assert_round_trip('[ FOO ] ] ] bar [ BAZ ]')
831 self.assert_round_trip('FOO ] ] bar ==== BAZ')
832 self.assert_round_trip('=')
833 self.assert_round_trip('#')
834 self.assert_round_trip('# ')
835 self.assert_round_trip('# Foo')
836 self.assert_round_trip('# Foo')
837 self.assert_round_trip('# Foo :')
838 self.assert_round_trip('# Foo : =')
840 def test_list_roundtrip(self):
841 self.assert_list_round_trip('')
842 self.assert_list_round_trip('\n')
843 self.assert_list_round_trip('\n\n')
844 self.assert_list_round_trip('bar')
845 self.assert_list_round_trip('bar\n# Qux.')
846 self.assert_list_round_trip('bar\n# Qux.\n')
848 def test_reconstitute_only_these(self):
850 reconstitute_only_these = []
852 def add_line(matching_configurations, reconstitute):
853 expectation_line = TestExpectationLine()
854 expectation_line.original_string = "Nay"
855 expectation_line.bugs = ['Bug(x)']
856 expectation_line.name = 'Yay'
857 expectation_line.parsed_expectations = set([IMAGE])
858 expectation_line.matching_configurations = matching_configurations
859 lines.append(expectation_line)
861 reconstitute_only_these.append(expectation_line)
863 add_line(set([TestConfiguration('xp', 'x86', 'release')]), True)
864 add_line(set([TestConfiguration('xp', 'x86', 'release'), TestConfiguration('xp', 'x86', 'debug')]), False)
865 serialized = TestExpectations.list_to_string(lines, self._converter)
866 self.assertEqual(serialized, "Bug(x) [ XP Release ] Yay [ ImageOnlyFailure ]\nBug(x) [ XP ] Yay [ ImageOnlyFailure ]")
867 serialized = TestExpectations.list_to_string(lines, self._converter, reconstitute_only_these=reconstitute_only_these)
868 self.assertEqual(serialized, "Bug(x) [ XP Release ] Yay [ ImageOnlyFailure ]\nNay")
870 def disabled_test_string_whitespace_stripping(self):
871 # FIXME: Re-enable this test once we rework the code to no longer support the old syntax.
872 self.assert_round_trip('\n', '')
873 self.assert_round_trip(' [ FOO ] bar [ BAZ ]', '[ FOO ] bar [ BAZ ]')
874 self.assert_round_trip('[ FOO ] bar [ BAZ ]', '[ FOO ] bar [ BAZ ]')
875 self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux.', '[ FOO ] bar [ BAZ ] # Qux.')
876 self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux.', '[ FOO ] bar [ BAZ ] # Qux.')
877 self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux.', '[ FOO ] bar [ BAZ ] # Qux.')
878 self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux.', '[ FOO ] bar [ BAZ ] # Qux.')