2 # Copyright (C) 2010 Google Inc. All rights reserved.
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
8 # * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
14 # * Neither the name of Google Inc. nor the names of its
15 # contributors may be used to endorse or promote products derived from
16 # this software without specific prior written permission.
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 """Unit tests for test_expectations.py."""
34 from webkitpy.layout_tests import port
35 from webkitpy.layout_tests.port import base
36 from webkitpy.layout_tests.layout_package.test_expectations import *
38 class FunctionsTest(unittest.TestCase):
39 def test_result_was_expected(self):
41 self.assertEquals(result_was_expected(PASS, set([PASS]),
43 self.assertEquals(result_was_expected(TEXT, set([PASS]),
46 # test handling of FAIL expectations
47 self.assertEquals(result_was_expected(IMAGE_PLUS_TEXT, set([FAIL]),
49 self.assertEquals(result_was_expected(IMAGE, set([FAIL]),
51 self.assertEquals(result_was_expected(TEXT, set([FAIL]),
53 self.assertEquals(result_was_expected(CRASH, set([FAIL]),
56 # test handling of SKIPped tests and results
57 self.assertEquals(result_was_expected(SKIP, set([CRASH]),
59 self.assertEquals(result_was_expected(SKIP, set([CRASH]),
62 # test handling of MISSING results and the REBASELINE modifier
63 self.assertEquals(result_was_expected(MISSING, set([PASS]),
65 self.assertEquals(result_was_expected(MISSING, set([PASS]),
68 def test_remove_pixel_failures(self):
69 self.assertEquals(remove_pixel_failures(set([TEXT])),
71 self.assertEquals(remove_pixel_failures(set([PASS])),
73 self.assertEquals(remove_pixel_failures(set([IMAGE])),
75 self.assertEquals(remove_pixel_failures(set([IMAGE_PLUS_TEXT])),
77 self.assertEquals(remove_pixel_failures(set([PASS, IMAGE, CRASH])),
81 class Base(unittest.TestCase):
82 # Note that all of these tests are written assuming the configuration
83 # being tested is Windows XP, Release build.
85 def __init__(self, testFunc, setUp=None, tearDown=None, description=None):
86 self._port = port.get('test-win-xp', None)
87 self._fs = self._port._filesystem
89 unittest.TestCase.__init__(self, testFunc)
91 def get_test(self, test_name):
92 return self._fs.join(self._port.layout_tests_dir(), test_name)
94 def get_basic_tests(self):
95 return [self.get_test('failures/expected/text.html'),
96 self.get_test('failures/expected/image_checksum.html'),
97 self.get_test('failures/expected/crash.html'),
98 self.get_test('failures/expected/missing_text.html'),
99 self.get_test('failures/expected/image.html'),
100 self.get_test('passes/text.html')]
102 def get_basic_expectations(self):
104 BUG_TEST : failures/expected/text.html = TEXT
105 BUG_TEST WONTFIX SKIP : failures/expected/crash.html = CRASH
106 BUG_TEST REBASELINE : failures/expected/missing_image.html = MISSING
107 BUG_TEST WONTFIX : failures/expected/image_checksum.html = IMAGE
108 BUG_TEST WONTFIX MAC : failures/expected/image.html = IMAGE
111 def parse_exp(self, expectations, overrides=None, is_lint_mode=False):
112 test_config = self._port.test_configuration()
113 self._exp = TestExpectations(self._port,
114 tests=self.get_basic_tests(),
115 expectations=expectations,
116 test_config=test_config,
117 is_lint_mode=is_lint_mode,
120 def assert_exp(self, test, result):
121 self.assertEquals(self._exp.get_expectations(self.get_test(test)),
125 class BasicTests(Base):
126 def test_basic(self):
127 self.parse_exp(self.get_basic_expectations())
128 self.assert_exp('failures/expected/text.html', TEXT)
129 self.assert_exp('failures/expected/image_checksum.html', IMAGE)
130 self.assert_exp('passes/text.html', PASS)
131 self.assert_exp('failures/expected/image.html', PASS)
134 class MiscTests(Base):
135 def test_multiple_results(self):
136 self.parse_exp('BUGX : failures/expected/text.html = TEXT CRASH')
137 self.assertEqual(self._exp.get_expectations(
138 self.get_test('failures/expected/text.html')),
141 def test_category_expectations(self):
142 # This test checks unknown tests are not present in the
143 # expectations and that known test part of a test category is
144 # present in the expectations.
146 BUGX WONTFIX : failures/expected = IMAGE
148 self.parse_exp(exp_str)
149 test_name = 'failures/expected/unknown-test.html'
150 unknown_test = self.get_test(test_name)
151 self.assertRaises(KeyError, self._exp.get_expectations,
153 self.assert_exp('failures/expected/crash.html', IMAGE)
155 def test_get_options(self):
156 self.parse_exp(self.get_basic_expectations())
157 self.assertEqual(self._exp.get_options(
158 self.get_test('passes/text.html')), [])
160 def test_expectations_json_for_all_platforms(self):
161 self.parse_exp(self.get_basic_expectations())
162 json_str = self._exp.get_expectations_json_for_all_platforms()
163 # FIXME: test actual content?
164 self.assertTrue(json_str)
166 def test_get_expectations_string(self):
167 self.parse_exp(self.get_basic_expectations())
168 self.assertEquals(self._exp.get_expectations_string(
169 self.get_test('failures/expected/text.html')),
172 def test_expectation_to_string(self):
173 # Normal cases are handled by other tests.
174 self.parse_exp(self.get_basic_expectations())
175 self.assertRaises(ValueError, self._exp.expectation_to_string,
178 def test_get_test_set(self):
179 # Handle some corner cases for this routine not covered by other tests.
180 self.parse_exp(self.get_basic_expectations())
181 s = self._exp._expected_failures.get_test_set(WONTFIX)
183 set([self.get_test('failures/expected/crash.html'),
184 self.get_test('failures/expected/image_checksum.html')]))
185 s = self._exp._expected_failures.get_test_set(WONTFIX, CRASH)
187 set([self.get_test('failures/expected/crash.html')]))
188 s = self._exp._expected_failures.get_test_set(WONTFIX, CRASH,
190 self.assertEqual(s, set([]))
192 def test_parse_error_fatal(self):
194 self.parse_exp("""FOO : failures/expected/text.html = TEXT
195 SKIP : failures/expected/image.html""")
196 self.assertFalse(True, "ParseError wasn't raised")
197 except ParseError, e:
198 self.assertTrue(e.fatal)
199 exp_errors = [u"Line:1 Unrecognized option 'foo' failures/expected/text.html",
200 u"Line:2 Missing expectations. [' failures/expected/image.html']"]
201 self.assertEqual(str(e), '\n'.join(map(str, exp_errors)))
202 self.assertEqual(e.errors, exp_errors)
204 def test_parse_error_nonfatal(self):
206 self.parse_exp('SKIP : failures/expected/text.html = TEXT',
208 self.assertFalse(True, "ParseError wasn't raised")
209 except ParseError, e:
210 self.assertFalse(e.fatal)
211 exp_errors = [u'Line:1 Test lacks BUG modifier. failures/expected/text.html']
212 self.assertEqual(str(e), '\n'.join(map(str, exp_errors)))
213 self.assertEqual(e.errors, exp_errors)
215 def test_overrides(self):
216 self.parse_exp("BUG_EXP: failures/expected/text.html = TEXT",
217 "BUG_OVERRIDE : failures/expected/text.html = IMAGE")
218 self.assert_exp('failures/expected/text.html', IMAGE)
220 def test_overrides__duplicate(self):
221 self.assertRaises(ParseError, self.parse_exp,
222 "BUG_EXP: failures/expected/text.html = TEXT",
224 BUG_OVERRIDE : failures/expected/text.html = IMAGE
225 BUG_OVERRIDE : failures/expected/text.html = CRASH
228 def test_pixel_tests_flag(self):
229 def match(test, result, pixel_tests_enabled):
230 return self._exp.matches_an_expected_result(
231 self.get_test(test), result, pixel_tests_enabled)
233 self.parse_exp(self.get_basic_expectations())
234 self.assertTrue(match('failures/expected/text.html', TEXT, True))
235 self.assertTrue(match('failures/expected/text.html', TEXT, False))
236 self.assertFalse(match('failures/expected/text.html', CRASH, True))
237 self.assertFalse(match('failures/expected/text.html', CRASH, False))
238 self.assertTrue(match('failures/expected/image_checksum.html', IMAGE,
240 self.assertTrue(match('failures/expected/image_checksum.html', PASS,
242 self.assertTrue(match('failures/expected/crash.html', SKIP, False))
243 self.assertTrue(match('passes/text.html', PASS, False))
245 def test_more_specific_override_resets_skip(self):
246 self.parse_exp("BUGX SKIP : failures/expected = TEXT\n"
247 "BUGX : failures/expected/text.html = IMAGE\n")
248 self.assert_exp('failures/expected/text.html', IMAGE)
249 self.assertFalse(self._port._filesystem.join(self._port.layout_tests_dir(),
250 'failures/expected/text.html') in
251 self._exp.get_tests_with_result_type(SKIP))
253 class ExpectationSyntaxTests(Base):
254 def test_missing_expectation(self):
255 # This is missing the expectation.
256 self.assertRaises(ParseError, self.parse_exp,
257 'BUG_TEST: failures/expected/text.html')
259 def test_missing_colon(self):
260 # This is missing the modifiers and the ':'
261 self.assertRaises(ParseError, self.parse_exp,
262 'failures/expected/text.html = TEXT')
264 def disabled_test_too_many_colons(self):
265 # FIXME: Enable this test and fix the underlying bug.
266 self.assertRaises(ParseError, self.parse_exp,
267 'BUG_TEST: failures/expected/text.html = PASS :')
269 def test_too_many_equals_signs(self):
270 self.assertRaises(ParseError, self.parse_exp,
271 'BUG_TEST: failures/expected/text.html = TEXT = IMAGE')
273 def test_unrecognized_expectation(self):
274 self.assertRaises(ParseError, self.parse_exp,
275 'BUG_TEST: failures/expected/text.html = UNKNOWN')
277 def test_macro(self):
279 BUG_TEST WIN-XP : failures/expected/text.html = TEXT
281 self.parse_exp(exp_str)
282 self.assert_exp('failures/expected/text.html', TEXT)
285 class SemanticTests(Base):
286 def test_bug_format(self):
287 self.assertRaises(ParseError, self.parse_exp, 'BUG1234 : failures/expected/text.html = TEXT')
289 def test_missing_bugid(self):
290 # This should log a non-fatal error.
291 self.parse_exp('SLOW : failures/expected/text.html = TEXT')
293 len(self._exp._expected_failures.get_non_fatal_errors()), 1)
295 def test_slow_and_timeout(self):
296 # A test cannot be SLOW and expected to TIMEOUT.
297 self.assertRaises(ParseError, self.parse_exp,
298 'BUG_TEST SLOW : failures/expected/timeout.html = TIMEOUT')
300 def test_rebaseline(self):
301 # Can't lint a file w/ 'REBASELINE' in it.
302 self.assertRaises(ParseError, self.parse_exp,
303 'BUG_TEST REBASELINE : failures/expected/text.html = TEXT',
306 def test_duplicates(self):
307 self.assertRaises(ParseError, self.parse_exp, """
308 BUG_EXP : failures/expected/text.html = TEXT
309 BUG_EXP : failures/expected/text.html = IMAGE""")
311 self.assertRaises(ParseError, self.parse_exp,
312 self.get_basic_expectations(), overrides="""
313 BUG_OVERRIDE : failures/expected/text.html = TEXT
314 BUG_OVERRIDE : failures/expected/text.html = IMAGE""", )
316 def test_missing_file(self):
317 # This should log a non-fatal error.
318 self.parse_exp('BUG_TEST : missing_file.html = TEXT')
320 len(self._exp._expected_failures.get_non_fatal_errors()), 1)
323 class PrecedenceTests(Base):
324 def test_file_over_directory(self):
325 # This tests handling precedence of specific lines over directories
326 # and tests expectations covering entire directories.
328 BUGX : failures/expected/text.html = TEXT
329 BUGX WONTFIX : failures/expected = IMAGE
331 self.parse_exp(exp_str)
332 self.assert_exp('failures/expected/text.html', TEXT)
333 self.assert_exp('failures/expected/crash.html', IMAGE)
336 BUGX WONTFIX : failures/expected = IMAGE
337 BUGX : failures/expected/text.html = TEXT
339 self.parse_exp(exp_str)
340 self.assert_exp('failures/expected/text.html', TEXT)
341 self.assert_exp('failures/expected/crash.html', IMAGE)
343 def test_ambiguous(self):
344 self.assertRaises(ParseError, self.parse_exp, """
345 BUG_TEST RELEASE : passes/text.html = PASS
346 BUG_TEST WIN : passes/text.html = FAIL
349 def test_more_modifiers(self):
351 BUG_TEST RELEASE : passes/text.html = PASS
352 BUG_TEST WIN RELEASE : passes/text.html = TEXT
354 self.assertRaises(ParseError, self.parse_exp, exp_str)
356 def test_order_in_file(self):
358 BUG_TEST WIN RELEASE : passes/text.html = TEXT
359 BUG_TEST RELEASE : passes/text.html = PASS
361 self.assertRaises(ParseError, self.parse_exp, exp_str)
363 def test_version_overrides(self):
365 BUG_TEST WIN : passes/text.html = PASS
366 BUG_TEST WIN XP : passes/text.html = TEXT
368 self.assertRaises(ParseError, self.parse_exp, exp_str)
370 def test_macro_overrides(self):
372 BUG_TEST WIN : passes/text.html = PASS
373 BUG_TEST WIN-XP : passes/text.html = TEXT
375 self.assertRaises(ParseError, self.parse_exp, exp_str)
378 class RebaseliningTest(Base):
379 """Test rebaselining-specific functionality."""
380 def assertRemove(self, input_expectations, tests, expected_expectations):
381 self.parse_exp(input_expectations)
382 actual_expectations = self._exp.remove_rebaselined_tests(tests)
383 self.assertEqual(expected_expectations, actual_expectations)
385 def test_remove(self):
386 self.assertRemove('BUGX REBASELINE : failures/expected/text.html = TEXT\n'
387 'BUGY : failures/expected/image.html = IMAGE\n'
388 'BUGZ REBASELINE : failures/expected/crash.html = CRASH\n',
389 ['failures/expected/text.html'],
390 'BUGY : failures/expected/image.html = IMAGE\n'
391 'BUGZ REBASELINE : failures/expected/crash.html = CRASH\n')
393 def test_no_get_rebaselining_failures(self):
394 self.parse_exp(self.get_basic_expectations())
395 self.assertEqual(len(self._exp.get_rebaselining_failures()), 0)
398 class ModifierTests(unittest.TestCase):
400 port_obj = port.get('test-win-xp', None)
401 self.config = port_obj.test_configuration()
402 self.matcher = ModifierMatcher(self.config)
404 def match(self, modifiers, expected_num_matches=-1, values=None, num_errors=0):
405 matcher = self.matcher
407 matcher = ModifierMatcher(self.FakeTestConfiguration(values))
408 match_result = matcher.match(modifiers)
409 self.assertEqual(len(match_result.warnings), 0)
410 self.assertEqual(len(match_result.errors), num_errors)
411 self.assertEqual(match_result.num_matches, expected_num_matches,
412 'match(%s, %s) returned -> %d, expected %d' %
413 (modifiers, str(self.config.values()),
414 match_result.num_matches, expected_num_matches))
416 def test_bad_match_modifier(self):
417 self.match(['foo'], num_errors=1)
423 self.match(['xp'], 1)
424 self.match(['win'], 1)
425 self.match(['release'], 1)
426 self.match(['cpu'], 1)
427 self.match(['x86'], 1)
428 self.match(['leopard'], -1)
429 self.match(['gpu'], -1)
430 self.match(['debug'], -1)
433 self.match(['xp', 'release'], 2)
434 self.match(['win7', 'release'], -1)
435 self.match(['win7', 'xp'], 1)
437 def test_three(self):
438 self.match(['win7', 'xp', 'release'], 2)
439 self.match(['xp', 'debug', 'x86'], -1)
440 self.match(['xp', 'release', 'x86'], 3)
441 self.match(['xp', 'cpu', 'release'], 3)
444 self.match(['xp', 'release', 'cpu', 'x86'], 4)
445 self.match(['win7', 'xp', 'release', 'cpu'], 3)
446 self.match(['win7', 'xp', 'debug', 'cpu'], -1)
448 def test_case_insensitivity(self):
449 self.match(['Win'], num_errors=1)
450 self.match(['WIN'], num_errors=1)
451 self.match(['win'], 1)
453 def test_duplicates(self):
454 self.match(['release', 'release'], num_errors=1)
455 self.match(['win-xp', 'xp'], num_errors=1)
456 self.match(['win-xp', 'win-xp'], num_errors=1)
457 self.match(['xp', 'release', 'xp', 'release'], num_errors=2)
458 self.match(['rebaseline', 'rebaseline'], num_errors=1)
460 def test_unknown_option(self):
461 self.match(['vms'], num_errors=1)
463 def test_duplicate_bugs(self):
464 # BUG* regexes can appear multiple times.
465 self.match(['bugfoo', 'bugbar'], 0)
467 def test_invalid_combinations(self):
468 # FIXME: This should probably raise an error instead of NO_MATCH.
469 self.match(['mac', 'xp'], num_errors=0)
471 def test_regexes_are_ignored(self):
472 self.match(['bug123xy', 'rebaseline', 'wontfix', 'slow', 'skip'], 0)
474 def test_none_is_invalid(self):
475 self.match(['none'], num_errors=1)
478 if __name__ == '__main__':