webkitpy: print nicer errors while linting expectations files, remove redundant tests
authordpranke@chromium.org <dpranke@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Jan 2012 01:11:27 +0000 (01:11 +0000)
committerdpranke@chromium.org <dpranke@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Jan 2012 01:11:27 +0000 (01:11 +0000)
https://bugs.webkit.org/show_bug.cgi?id=76955

Reviewed by Adam Barth.

This change cleans up the errors that are printed so that the
are more "quickfix" compatible (path:lineno).

Also, there were a bunch of redundant tests between
layout_tests.models.test_expectations_unittest and
style.checkers.test_expectations_unittest. The latter should
just expect the former to do most of the testing.

* Scripts/webkitpy/layout_tests/models/test_expectations.py:
(TestExpectationParser._parse_modifiers):
(TestExpectations._report_errors):
(TestExpectations._add_skipped_tests):
* Scripts/webkitpy/layout_tests/models/test_expectations_unittest.py:
(test_parse_error_nonfatal):
(SemanticTests.test_bad_bugid):
* Scripts/webkitpy/style/checkers/test_expectations.py:
(TestExpectationsChecker.__init__):
* Scripts/webkitpy/style/checkers/test_expectations_unittest.py:
(TestExpectationsTestCase.assert_lines_lint):
(TestExpectationsTestCase.test_valid_expectations):
(TestExpectationsTestCase.test_invalid_expectations):
(TestExpectationsTestCase.test_tab):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@105948 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Tools/ChangeLog
Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py
Tools/Scripts/webkitpy/layout_tests/models/test_expectations_unittest.py
Tools/Scripts/webkitpy/style/checkers/test_expectations.py
Tools/Scripts/webkitpy/style/checkers/test_expectations_unittest.py

index f254531..b9f93bd 100644 (file)
@@ -1,3 +1,33 @@
+2012-01-25  Dirk Pranke  <dpranke@chromium.org>
+
+        webkitpy: print nicer errors while linting expectations files, remove redundant tests
+        https://bugs.webkit.org/show_bug.cgi?id=76955
+
+        Reviewed by Adam Barth.
+
+        This change cleans up the errors that are printed so that the
+        are more "quickfix" compatible (path:lineno).
+
+        Also, there were a bunch of redundant tests between
+        layout_tests.models.test_expectations_unittest and
+        style.checkers.test_expectations_unittest. The latter should
+        just expect the former to do most of the testing.
+
+        * Scripts/webkitpy/layout_tests/models/test_expectations.py:
+        (TestExpectationParser._parse_modifiers):
+        (TestExpectations._report_errors):
+        (TestExpectations._add_skipped_tests):
+        * Scripts/webkitpy/layout_tests/models/test_expectations_unittest.py:
+        (test_parse_error_nonfatal):
+        (SemanticTests.test_bad_bugid):
+        * Scripts/webkitpy/style/checkers/test_expectations.py:
+        (TestExpectationsChecker.__init__):
+        * Scripts/webkitpy/style/checkers/test_expectations_unittest.py:
+        (TestExpectationsTestCase.assert_lines_lint):
+        (TestExpectationsTestCase.test_valid_expectations):
+        (TestExpectationsTestCase.test_invalid_expectations):
+        (TestExpectationsTestCase.test_tab):
+
 2012-01-25  Eric Seidel  <eric@webkit.org>
 
         webkit-patch apply-* should always continue after failures
index cfa2cce..012135a 100644 (file)
@@ -221,6 +221,7 @@ class TestExpectationParser(object):
 
     def _parse_modifiers(self, expectation_line):
         has_wontfix = False
+        has_bugid = False
         parsed_specifiers = set()
         for modifier in expectation_line.modifiers:
             if modifier in TestExpectations.MODIFIERS:
@@ -228,6 +229,7 @@ class TestExpectationParser(object):
                 if modifier == self.WONTFIX_MODIFIER:
                     has_wontfix = True
             elif modifier.startswith(self.BUG_MODIFIER_PREFIX):
+                has_bugid = True
                 if re.match(self.BUG_MODIFIER_REGEX, modifier):
                     expectation_line.errors.append('BUG\d+ is not allowed, must be one of BUGCR\d+, BUGWK\d+, BUGV8_\d+, or a non-numeric bug identifier.')
                 else:
@@ -235,7 +237,7 @@ class TestExpectationParser(object):
             else:
                 parsed_specifiers.add(modifier)
 
-        if not expectation_line.parsed_bug_modifiers and not has_wontfix:
+        if not expectation_line.parsed_bug_modifiers and not has_wontfix and not has_bugid:
             expectation_line.warnings.append('Test lacks BUG modifier.')
 
         if self._allow_rebaseline_modifier and self.REBASELINE_MODIFIER in expectation_line.modifiers:
@@ -783,25 +785,25 @@ class TestExpectations(object):
     def _report_errors(self):
         errors = []
         warnings = []
+        test_expectation_path = self._port.path_to_test_expectations_file()
+        if test_expectation_path.startswith(self._port.path_from_webkit_base()):
+            test_expectation_path = self._port.host.filesystem.relpath(test_expectation_path, self._port.path_from_webkit_base())
         for expectation in self._expectations:
             for error in expectation.errors:
-                errors.append("Line:%s %s %s" % (expectation.line_number, error, expectation.name if expectation.expectations else expectation.original_string))
+                errors.append('%s:%d %s %s' % (test_expectation_path, expectation.line_number, error, expectation.name if expectation.expectations else expectation.original_string))
             for warning in expectation.warnings:
-                warnings.append("Line:%s %s %s" % (expectation.line_number, warning, expectation.name if expectation.expectations else expectation.original_string))
+                warnings.append('%s:%d %s %s' % (test_expectation_path, expectation.line_number, warning, expectation.name if expectation.expectations else expectation.original_string))
 
         for warning in self._skipped_tests_warnings:
-            warnings.append(warning)
+            warnings.append('%s%s' % (test_expectation_path, warning))
 
         if errors or warnings:
-            test_expectation_path = self._port.path_to_test_expectations_file()
-            failure_title = "FAILURES IN %s" % test_expectation_path
-
             if errors:
-                raise ParseError(fatal=True, errors=[failure_title] + errors)
+                raise ParseError(fatal=True, errors=sorted(errors + warnings))
             if warnings:
                 self._has_warnings = True
                 if self._is_lint_mode:
-                    raise ParseError(fatal=False, errors=[failure_title] + warnings)
+                    raise ParseError(fatal=False, errors=warnings)
 
     def _process_tests_without_expectations(self):
         if self._full_test_list:
@@ -832,8 +834,7 @@ class TestExpectations(object):
             return
         for index, test in enumerate(self._expectations, start=1):
             if test.name and test.name in tests_to_skip:
-                self._skipped_tests_warnings.append('The %s test from test_expectations.txt in line %d is also in Skipped!' %
-                            (test.name, index))
+                self._skipped_tests_warnings.append(':%d %s is also in a Skipped file.' % (index, test.name))
         skipped_tests = '\n'.join(map(lambda test_path: 'BUG_SKIPPED SKIP : %s = FAIL' % test_path, tests_to_skip))
         for test in self._parser.parse(skipped_tests):
             self._model.add_expectation_line(test, overrides_allowed=True)
index e9d6620..a294f36 100644 (file)
@@ -199,11 +199,10 @@ SKIP : failures/expected/image.html""")
             self.assertFalse(True, "ParseError wasn't raised")
         except ParseError, e:
             self.assertTrue(e.fatal)
-            exp_errors = [u"FAILURES IN %s" % self._port.path_to_test_expectations_file(),
-                          u"Line:1 Unrecognized modifier 'foo' failures/expected/text.html",
-                          u"Line:2 Missing expectations SKIP : failures/expected/image.html"]
-            self.assertEqual(str(e), '\n'.join(map(str, exp_errors)))
-            self.assertEqual(e.errors, exp_errors)
+            exp_errors = [u":1 Test lacks BUG modifier. failures/expected/text.html",
+                          u":1 Unrecognized modifier 'foo' failures/expected/text.html",
+                          u":2 Missing expectations SKIP : failures/expected/image.html"]
+            self.assertEqual(str(e), '\n'.join(self._port.path_to_test_expectations_file() + str(error) for error in exp_errors))
 
     def test_parse_error_nonfatal(self):
         try:
@@ -211,10 +210,8 @@ SKIP : failures/expected/image.html""")
             self.assertFalse(True, "ParseError wasn't raised")
         except ParseError, e:
             self.assertFalse(e.fatal)
-            exp_errors = [u'FAILURES IN %s' % self._port.path_to_test_expectations_file(),
-                          u'Line:1 Test lacks BUG modifier. failures/expected/text.html']
-            self.assertEqual(str(e), '\n'.join(map(str, exp_errors)))
-            self.assertEqual(e.errors, exp_errors)
+            exp_errors = [u':1 Test lacks BUG modifier. failures/expected/text.html']
+            self.assertEqual(str(e), '\n'.join(self._port.path_to_test_expectations_file() + str(error) for error in exp_errors))
 
     def test_error_on_different_platform(self):
         # parse_exp uses a Windows port. Assert errors on Mac show up in lint mode.
@@ -315,6 +312,14 @@ class SemanticTests(Base):
     def test_bug_format(self):
         self.assertRaises(ParseError, self.parse_exp, 'BUG1234 : failures/expected/text.html = TEXT')
 
+    def test_bad_bugid(self):
+        try:
+            self.parse_exp('BUG1234 SLOW : failures/expected/text.html = TEXT')
+            self.fail('should have raised an error about a bad bug identifier')
+        except ParseError, exp:
+            self.assertEquals(exp.fatal, True)
+            self.assertEquals(len(exp.errors), 1)
+
     def test_missing_bugid(self):
         # This should log a non-fatal error.
         self.parse_exp('SLOW : failures/expected/text.html = TEXT')
index 1d202f3..7ddd688 100644 (file)
@@ -64,7 +64,7 @@ class TestExpectationsChecker(object):
         self._handle_style_error = handle_style_error
         self._handle_style_error.turn_off_line_filtering()
         self._tab_checker = TabChecker(file_path, handle_style_error)
-        self._output_regex = re.compile('Line:(?P<line>\d+)\s*(?P<message>.+)')
+        self._output_regex = re.compile('.*test_expectations.txt:(?P<line>\d+)\s*(?P<message>.+)')
 
         # FIXME: host should be a required parameter, not an optional one.
         host = host or Host()
index 92d1fe6..10cc7de 100644 (file)
@@ -76,7 +76,7 @@ class TestExpectationsTestCase(unittest.TestCase):
         self._expect_port_for_expectations_path(None, "/")
         self._expect_port_for_expectations_path("ChromiumMacPort", "/mock-checkout/LayoutTests/chromium-mac/test_expectations.txt")
 
-    def assert_lines_lint(self, lines, expected):
+    def assert_lines_lint(self, lines, should_pass, expected_output=None):
         self._error_collector.reset_errors()
         checker = TestExpectationsChecker('test/test_expectations.txt',
                                           self._error_collector, host=MockHost())
@@ -84,109 +84,19 @@ class TestExpectationsTestCase(unittest.TestCase):
                                         tests=[self._test_file],
                                         overrides=None)
         checker.check_tabs(lines)
-        self.assertEqual(expected, self._error_collector.get_errors())
+        if should_pass:
+            self.assertEqual('', self._error_collector.get_errors())
+        elif expected_output:
+            self.assertEquals(expected_output, self._error_collector.get_errors())
+        else:
+            self.assertNotEquals('', self._error_collector.get_errors())
         self.assertTrue(self._error_collector.turned_off_filtering)
 
     def test_valid_expectations(self):
-        self.assert_lines_lint(
-            ["BUGCR1234 MAC : passes/text.html = PASS FAIL"],
-            "")
-        self.assert_lines_lint(
-            ["SKIP BUGCR1234 : passes/text.html = TIMEOUT PASS"],
-            "")
-        self.assert_lines_lint(
-            ["BUGCR1234 DEBUG : passes/text.html = TIMEOUT PASS"],
-            "")
-        self.assert_lines_lint(
-            ["BUGCR1234 DEBUG SKIP : passes/text.html = TIMEOUT PASS"],
-            "")
-        self.assert_lines_lint(
-            ["BUGCR1234 MAC DEBUG SKIP : passes/text.html = TIMEOUT PASS"],
-            "")
-        self.assert_lines_lint(
-            ["BUGCR1234 DEBUG MAC : passes/text.html = TIMEOUT PASS"],
-            "")
-        self.assert_lines_lint(
-            ["SLOW BUGCR1234 : passes/text.html = PASS"],
-            "")
-        self.assert_lines_lint(
-            ["WONTFIX SKIP : passes/text.html = TIMEOUT"],
-            "")
-
-    def test_modifier_errors(self):
-        self.assert_lines_lint(
-            ["BUG1234 : passes/text.html = FAIL"],
-            "BUG\\d+ is not allowed, must be one of BUGCR\\d+, BUGWK\\d+, BUGV8_\\d+, or a non-numeric bug identifier. passes/text.html  [test/expectations] [5]")
-
-    def test_valid_modifiers(self):
-        self.assert_lines_lint(
-            ["INVALID-MODIFIER : passes/text.html = PASS"],
-            "Unrecognized modifier 'invalid-modifier' "
-            "passes/text.html  [test/expectations] [5]")
-        self.assert_lines_lint(
-            ["SKIP : passes/text.html = PASS"],
-            "Test lacks BUG modifier. "
-            "passes/text.html  [test/expectations] [2]")
-
-    def test_expectation_errors(self):
-        self.assert_lines_lint(
-            ["missing expectations"],
-            "Missing a ':' missing expectations  [test/expectations] [5]")
-        self.assert_lines_lint(
-            ["SLOW : passes/text.html = TIMEOUT"],
-            "A test can not be both SLOW and TIMEOUT. "
-            "If it times out indefinitely, then it should be just TIMEOUT. "
-            "passes/text.html  [test/expectations] [5]")
-        self.assert_lines_lint(
-            ["BUGWK1 : does/not/exist.html = FAIL"],
-            "Path does not exist. does/not/exist.html  [test/expectations] [2]")
-
-    def test_parse_expectations(self):
-        self.assert_lines_lint(
-            ["BUGWK1 : passes/text.html = PASS"],
-            "")
-        self.assert_lines_lint(
-            ["BUGWK1 : passes/text.html = UNSUPPORTED"],
-            "Unsupported expectation: unsupported "
-            "passes/text.html  [test/expectations] [5]")
-        self.assert_lines_lint(
-            ["BUGWK1 : passes/text.html = PASS UNSUPPORTED"],
-            "Unsupported expectation: unsupported "
-            "passes/text.html  [test/expectations] [5]")
-
-    def test_already_seen_test(self):
-        self.assert_lines_lint(
-            ["BUGWK1 : passes/text.html = PASS",
-             "BUGWK1 : passes/text.html = TIMEOUT"],
-            "Duplicate or ambiguous expectation. %s  [test/expectations] [5]" % self._test_file)
-
-        self.assert_lines_lint(
-            ["BUGWK1 LEOPARD : passes/text.html = PASS",
-             "BUGWK1 MAC : passes/text.html = TIMEOUT"],
-            "More specific entry on line 1 overrides line 2 passes/text.html  [test/expectations] [5]")
-
-        self.assert_lines_lint(
-            ["BUGWK1 LEOPARD : passes/text.html = PASS",
-             "BUGWK1 LEOPARD RELEASE : passes/text.html = TIMEOUT"],
-            "More specific entry on line 2 overrides line 1 passes/text.html  [test/expectations] [5]")
-
-        self.assert_lines_lint(
-            ["BUGWK1 RELEASE : passes/text.html = PASS",
-             "BUGWK1 CPU : passes/text.html = TIMEOUT"],
-            "Entries on line 1 and line 2 match overlapping sets of configurations passes/text.html  [test/expectations] [5]")
-
-        self.assert_lines_lint(
-            ["BUGWK1 WIN : passes/text.html = PASS",
-             "BUGWK1 MAC : passes/text.html = TIMEOUT"],
-            "")
-
-        self.assert_lines_lint(
-            ["BUGWK1 LEOPARD DEBUG : passes/text.html = PASS",
-             "BUGWK1 LEOPARD RELEASE : passes/text.html = TIMEOUT"],
-            "")
+        self.assert_lines_lint(["BUGCR1234 MAC : passes/text.html = PASS FAIL"], should_pass=True)
 
+    def test_invalid_expectations(self):
+        self.assert_lines_lint(["BUG1234 : passes/text.html = GIVE UP"], should_pass=False)
 
     def test_tab(self):
-        self.assert_lines_lint(
-            ["\tBUGWK1 : passes/text.html = PASS"],
-            "Line contains tab character.  [whitespace/tab] [5]")
+        self.assert_lines_lint(["\tBUGWK1 : passes/text.html = PASS"], should_pass=False, expected_output="Line contains tab character.  [whitespace/tab] [5]")