Home | History | Annotate | Download | only in layout_package
      1 #!/usr/bin/python
      2 # Copyright (C) 2010 Google Inc. All rights reserved.
      3 #
      4 # Redistribution and use in source and binary forms, with or without
      5 # modification, are permitted provided that the following conditions are
      6 # met:
      7 #
      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
     13 # distribution.
     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.
     17 #
     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.
     29 
     30 """Unit tests for test_expectations.py."""
     31 
     32 import unittest
     33 
     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 *
     37 
     38 class FunctionsTest(unittest.TestCase):
     39     def test_result_was_expected(self):
     40         # test basics
     41         self.assertEquals(result_was_expected(PASS, set([PASS]),
     42                                               False, False), True)
     43         self.assertEquals(result_was_expected(TEXT, set([PASS]),
     44                                               False, False), False)
     45 
     46         # test handling of FAIL expectations
     47         self.assertEquals(result_was_expected(IMAGE_PLUS_TEXT, set([FAIL]),
     48                                               False, False), True)
     49         self.assertEquals(result_was_expected(IMAGE, set([FAIL]),
     50                                               False, False), True)
     51         self.assertEquals(result_was_expected(TEXT, set([FAIL]),
     52                                               False, False), True)
     53         self.assertEquals(result_was_expected(CRASH, set([FAIL]),
     54                                               False, False), False)
     55 
     56         # test handling of SKIPped tests and results
     57         self.assertEquals(result_was_expected(SKIP, set([CRASH]),
     58                                               False, True), True)
     59         self.assertEquals(result_was_expected(SKIP, set([CRASH]),
     60                                               False, False), False)
     61 
     62         # test handling of MISSING results and the REBASELINE modifier
     63         self.assertEquals(result_was_expected(MISSING, set([PASS]),
     64                                               True, False), True)
     65         self.assertEquals(result_was_expected(MISSING, set([PASS]),
     66                                               False, False), False)
     67 
     68     def test_remove_pixel_failures(self):
     69         self.assertEquals(remove_pixel_failures(set([TEXT])),
     70                           set([TEXT]))
     71         self.assertEquals(remove_pixel_failures(set([PASS])),
     72                           set([PASS]))
     73         self.assertEquals(remove_pixel_failures(set([IMAGE])),
     74                           set([PASS]))
     75         self.assertEquals(remove_pixel_failures(set([IMAGE_PLUS_TEXT])),
     76                           set([TEXT]))
     77         self.assertEquals(remove_pixel_failures(set([PASS, IMAGE, CRASH])),
     78                           set([PASS, CRASH]))
     79 
     80 
     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.
     84 
     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
     88         self._exp = None
     89         unittest.TestCase.__init__(self, testFunc)
     90 
     91     def get_test(self, test_name):
     92         return self._fs.join(self._port.layout_tests_dir(), test_name)
     93 
     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')]
    101 
    102     def get_basic_expectations(self):
    103         return """
    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
    109 """
    110 
    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,
    118              overrides=overrides)
    119 
    120     def assert_exp(self, test, result):
    121         self.assertEquals(self._exp.get_expectations(self.get_test(test)),
    122                           set([result]))
    123 
    124 
    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)
    132 
    133 
    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')),
    139             set([TEXT, CRASH]))
    140 
    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.
    145         exp_str = """
    146 BUGX WONTFIX : failures/expected = IMAGE
    147 """
    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,
    152                           unknown_test)
    153         self.assert_exp('failures/expected/crash.html', IMAGE)
    154 
    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')), [])
    159 
    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)
    165 
    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')),
    170                           'TEXT')
    171 
    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,
    176                           -1)
    177 
    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)
    182         self.assertEqual(s,
    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)
    186         self.assertEqual(s,
    187             set([self.get_test('failures/expected/crash.html')]))
    188         s = self._exp._expected_failures.get_test_set(WONTFIX, CRASH,
    189                                                       include_skips=False)
    190         self.assertEqual(s, set([]))
    191 
    192     def test_parse_error_fatal(self):
    193         try:
    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)
    203 
    204     def test_parse_error_nonfatal(self):
    205         try:
    206             self.parse_exp('SKIP : failures/expected/text.html = TEXT',
    207                            is_lint_mode=True)
    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)
    214 
    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)
    219 
    220     def test_overrides__duplicate(self):
    221         self.assertRaises(ParseError, self.parse_exp,
    222              "BUG_EXP: failures/expected/text.html = TEXT",
    223              """
    224 BUG_OVERRIDE : failures/expected/text.html = IMAGE
    225 BUG_OVERRIDE : failures/expected/text.html = CRASH
    226 """)
    227 
    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)
    232 
    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,
    239                               True))
    240         self.assertTrue(match('failures/expected/image_checksum.html', PASS,
    241                               False))
    242         self.assertTrue(match('failures/expected/crash.html', SKIP, False))
    243         self.assertTrue(match('passes/text.html', PASS, False))
    244 
    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))
    252 
    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')
    258 
    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')
    263 
    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 :')
    268 
    269     def test_too_many_equals_signs(self):
    270         self.assertRaises(ParseError, self.parse_exp,
    271                           'BUG_TEST: failures/expected/text.html = TEXT = IMAGE')
    272 
    273     def test_unrecognized_expectation(self):
    274         self.assertRaises(ParseError, self.parse_exp,
    275                           'BUG_TEST: failures/expected/text.html = UNKNOWN')
    276 
    277     def test_macro(self):
    278         exp_str = """
    279 BUG_TEST WIN-XP : failures/expected/text.html = TEXT
    280 """
    281         self.parse_exp(exp_str)
    282         self.assert_exp('failures/expected/text.html', TEXT)
    283 
    284 
    285 class SemanticTests(Base):
    286     def test_bug_format(self):
    287         self.assertRaises(ParseError, self.parse_exp, 'BUG1234 : failures/expected/text.html = TEXT')
    288 
    289     def test_missing_bugid(self):
    290         # This should log a non-fatal error.
    291         self.parse_exp('SLOW : failures/expected/text.html = TEXT')
    292         self.assertEqual(
    293             len(self._exp._expected_failures.get_non_fatal_errors()), 1)
    294 
    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')
    299 
    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',
    304             is_lint_mode=True)
    305 
    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""")
    310 
    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""", )
    315 
    316     def test_missing_file(self):
    317         # This should log a non-fatal error.
    318         self.parse_exp('BUG_TEST : missing_file.html = TEXT')
    319         self.assertEqual(
    320             len(self._exp._expected_failures.get_non_fatal_errors()), 1)
    321 
    322 
    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.
    327         exp_str = """
    328 BUGX : failures/expected/text.html = TEXT
    329 BUGX WONTFIX : failures/expected = IMAGE
    330 """
    331         self.parse_exp(exp_str)
    332         self.assert_exp('failures/expected/text.html', TEXT)
    333         self.assert_exp('failures/expected/crash.html', IMAGE)
    334 
    335         exp_str = """
    336 BUGX WONTFIX : failures/expected = IMAGE
    337 BUGX : failures/expected/text.html = TEXT
    338 """
    339         self.parse_exp(exp_str)
    340         self.assert_exp('failures/expected/text.html', TEXT)
    341         self.assert_exp('failures/expected/crash.html', IMAGE)
    342 
    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
    347 """)
    348 
    349     def test_more_modifiers(self):
    350         exp_str = """
    351 BUG_TEST RELEASE : passes/text.html = PASS
    352 BUG_TEST WIN RELEASE : passes/text.html = TEXT
    353 """
    354         self.assertRaises(ParseError, self.parse_exp, exp_str)
    355 
    356     def test_order_in_file(self):
    357         exp_str = """
    358 BUG_TEST WIN RELEASE : passes/text.html = TEXT
    359 BUG_TEST RELEASE : passes/text.html = PASS
    360 """
    361         self.assertRaises(ParseError, self.parse_exp, exp_str)
    362 
    363     def test_version_overrides(self):
    364         exp_str = """
    365 BUG_TEST WIN : passes/text.html = PASS
    366 BUG_TEST WIN XP : passes/text.html = TEXT
    367 """
    368         self.assertRaises(ParseError, self.parse_exp, exp_str)
    369 
    370     def test_macro_overrides(self):
    371         exp_str = """
    372 BUG_TEST WIN : passes/text.html = PASS
    373 BUG_TEST WIN-XP : passes/text.html = TEXT
    374 """
    375         self.assertRaises(ParseError, self.parse_exp, exp_str)
    376 
    377 
    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)
    384 
    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')
    392 
    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)
    396 
    397 
    398 class ModifierTests(unittest.TestCase):
    399     def setUp(self):
    400         port_obj = port.get('test-win-xp', None)
    401         self.config = port_obj.test_configuration()
    402         self.matcher = ModifierMatcher(self.config)
    403 
    404     def match(self, modifiers, expected_num_matches=-1, values=None, num_errors=0):
    405         matcher = self.matcher
    406         if values:
    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))
    415 
    416     def test_bad_match_modifier(self):
    417         self.match(['foo'], num_errors=1)
    418 
    419     def test_none(self):
    420         self.match([], 0)
    421 
    422     def test_one(self):
    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)
    431 
    432     def test_two(self):
    433         self.match(['xp', 'release'], 2)
    434         self.match(['win7', 'release'], -1)
    435         self.match(['win7', 'xp'], 1)
    436 
    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)
    442 
    443     def test_four(self):
    444         self.match(['xp', 'release', 'cpu', 'x86'], 4)
    445         self.match(['win7', 'xp', 'release', 'cpu'], 3)
    446         self.match(['win7', 'xp', 'debug', 'cpu'], -1)
    447 
    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)
    452 
    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)
    459 
    460     def test_unknown_option(self):
    461         self.match(['vms'], num_errors=1)
    462 
    463     def test_duplicate_bugs(self):
    464         # BUG* regexes can appear multiple times.
    465         self.match(['bugfoo', 'bugbar'], 0)
    466 
    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)
    470 
    471     def test_regexes_are_ignored(self):
    472         self.match(['bug123xy', 'rebaseline', 'wontfix', 'slow', 'skip'], 0)
    473 
    474     def test_none_is_invalid(self):
    475         self.match(['none'], num_errors=1)
    476 
    477 
    478 if __name__ == '__main__':
    479     unittest.main()
    480