Title: [97820] trunk/Tools
Revision
97820
Author
[email protected]
Date
2011-10-18 17:47:26 -0700 (Tue, 18 Oct 2011)

Log Message

watchlist: Should try to run if it can and not throw on mistakes.
https://bugs.webkit.org/show_bug.cgi?id=70358

Reviewed by Adam Barth.

* Scripts/webkitpy/common/system/outputcapture.py: Add the ability to capture log output.
* Scripts/webkitpy/common/watchlist/watchlistloader_unittest.py: Adapt to the logging of errors.
* Scripts/webkitpy/common/watchlist/watchlistparser.py: Change to log problems and fix problems when found.
* Scripts/webkitpy/common/watchlist/watchlistparser_unittest.py: Adapt to the logging of errors.
* Scripts/webkitpy/common/watchlist/watchlistrule.py: Expose a way to remove instructions.
* Scripts/webkitpy/common/watchlist/watchlistrule_unittest.py: Test the new function.

Modified Paths

Diff

Modified: trunk/Tools/ChangeLog (97819 => 97820)


--- trunk/Tools/ChangeLog	2011-10-19 00:40:30 UTC (rev 97819)
+++ trunk/Tools/ChangeLog	2011-10-19 00:47:26 UTC (rev 97820)
@@ -1,3 +1,17 @@
+2011-10-18  David Levin  <[email protected]>
+
+        watchlist: Should try to run if it can and not throw on mistakes.
+        https://bugs.webkit.org/show_bug.cgi?id=70358
+
+        Reviewed by Adam Barth.
+
+        * Scripts/webkitpy/common/system/outputcapture.py: Add the ability to capture log output.
+        * Scripts/webkitpy/common/watchlist/watchlistloader_unittest.py: Adapt to the logging of errors.
+        * Scripts/webkitpy/common/watchlist/watchlistparser.py: Change to log problems and fix problems when found.
+        * Scripts/webkitpy/common/watchlist/watchlistparser_unittest.py: Adapt to the logging of errors.
+        * Scripts/webkitpy/common/watchlist/watchlistrule.py: Expose a way to remove instructions.
+        * Scripts/webkitpy/common/watchlist/watchlistrule_unittest.py: Test the new function.
+
 2011-10-18  Sam Weinig  <[email protected]>
 
         Move uses of C-SPI out of WKView.h and into WKViewPrivate.h

Modified: trunk/Tools/Scripts/webkitpy/common/system/outputcapture.py (97819 => 97820)


--- trunk/Tools/Scripts/webkitpy/common/system/outputcapture.py	2011-10-19 00:40:30 UTC (rev 97819)
+++ trunk/Tools/Scripts/webkitpy/common/system/outputcapture.py	2011-10-19 00:47:26 UTC (rev 97820)
@@ -28,6 +28,7 @@
 #
 # Class for unittest support.  Used for capturing stderr/stdout.
 
+import logging
 import sys
 import unittest
 from StringIO import StringIO
@@ -50,20 +51,32 @@
         return captured_output
 
     def capture_output(self):
+        self._logs = StringIO()
+        self._logs_handler = logging.StreamHandler(self._logs)
+        self._logs_handler.setLevel(logging.INFO)
+        logging.getLogger().addHandler(self._logs_handler)
         return (self._capture_output_with_name("stdout"), self._capture_output_with_name("stderr"))
 
     def restore_output(self):
-        return (self._restore_output_with_name("stdout"), self._restore_output_with_name("stderr"))
+        logging.getLogger().removeHandler(self._logs_handler)
+        self._logs_handler.flush()
+        self._logs.flush()
+        logs_string = self._logs.getvalue()
+        delattr(self, '_logs_handler')
+        delattr(self, '_logs')
+        return (self._restore_output_with_name("stdout"), self._restore_output_with_name("stderr"), logs_string)
 
-    def assert_outputs(self, testcase, function, args=[], kwargs={}, expected_stdout="", expected_stderr="", expected_exception=None):
+    def assert_outputs(self, testcase, function, args=[], kwargs={}, expected_stdout="", expected_stderr="", expected_exception=None, expected_logs=None):
         self.capture_output()
         if expected_exception:
             return_value = testcase.assertRaises(expected_exception, function, *args, **kwargs)
         else:
             return_value = function(*args, **kwargs)
-        (stdout_string, stderr_string) = self.restore_output()
+        (stdout_string, stderr_string, logs_string) = self.restore_output()
         testcase.assertEqual(stdout_string, expected_stdout)
         testcase.assertEqual(stderr_string, expected_stderr)
+        if expected_logs is not None:
+            testcase.assertEqual(logs_string, expected_logs)
         # This is a little strange, but I don't know where else to return this information.
         return return_value
 

Modified: trunk/Tools/Scripts/webkitpy/common/watchlist/watchlistloader_unittest.py (97819 => 97820)


--- trunk/Tools/Scripts/webkitpy/common/watchlist/watchlistloader_unittest.py	2011-10-19 00:40:30 UTC (rev 97819)
+++ trunk/Tools/Scripts/webkitpy/common/watchlist/watchlistloader_unittest.py	2011-10-19 00:47:26 UTC (rev 97820)
@@ -31,6 +31,7 @@
 from webkitpy.common import webkitunittest
 from webkitpy.common.system import filesystem_mock
 from webkitpy.common.system import filesystem
+from webkitpy.common.system.outputcapture import OutputCapture
 from webkitpy.common.watchlist.watchlistloader import WatchListLoader
 
 
@@ -41,4 +42,4 @@
 
     def test_watch_list_load(self):
         # Test parsing of the checked-in watch list.
-        WatchListLoader(filesystem.FileSystem()).load()
+        OutputCapture().assert_outputs(self, WatchListLoader(filesystem.FileSystem()).load, expected_logs="")

Modified: trunk/Tools/Scripts/webkitpy/common/watchlist/watchlistparser.py (97819 => 97820)


--- trunk/Tools/Scripts/webkitpy/common/watchlist/watchlistparser.py	2011-10-19 00:40:30 UTC (rev 97819)
+++ trunk/Tools/Scripts/webkitpy/common/watchlist/watchlistparser.py	2011-10-19 00:47:26 UTC (rev 97820)
@@ -28,6 +28,7 @@
 
 
 import difflib
+import logging
 import re
 
 from webkitpy.common.watchlist.amountchangedpattern import AmountChangedPattern
@@ -38,13 +39,17 @@
 from webkitpy.common.config.committers import CommitterList
 
 
+_log = logging.getLogger(__name__)
+
+
 class WatchListParser(object):
     _DEFINITIONS = 'DEFINITIONS'
     _CC_RULES = 'CC_RULES'
     _MESSAGE_RULES = 'MESSAGE_RULES'
     _INVALID_DEFINITION_NAME_REGEX = r'\|'
 
-    def __init__(self):
+    def __init__(self, log_error=None):
+        self._log_error = log_error or _log.error
         self._section_parsers = {
             self._DEFINITIONS: self._parse_definition_section,
             self._CC_RULES: self._parse_cc_rules,
@@ -68,9 +73,10 @@
         for section in dictionary:
             parser = self._section_parsers.get(section)
             if not parser:
-                raise Exception(('Unknown section "%s" in watch list.'
-                                 + self._suggest_words(section, self._section_parsers.keys()))
-                                % section)
+                self._log_error(('Unknown section "%s" in watch list.'
+                                + self._suggest_words(section, self._section_parsers.keys()))
+                               % section)
+                continue
             parser(dictionary[section], watch_list)
 
         self._validate(watch_list)
@@ -90,21 +96,24 @@
         for name in definition_section:
             invalid_character = re.search(self._INVALID_DEFINITION_NAME_REGEX, name)
             if invalid_character:
-                raise Exception('Invalid character "%s" in definition "%s".' % (invalid_character.group(0), name))
+                self._log_error('Invalid character "%s" in definition "%s".' % (invalid_character.group(0), name))
+                continue
 
             definition = definition_section[name]
             definitions[name] = []
             for pattern_type in definition:
                 pattern_parser = self._definition_pattern_parsers.get(pattern_type)
                 if not pattern_parser:
-                    raise Exception(('Unknown pattern type "%s" in definition "%s".'
+                    self._log_error(('Unknown pattern type "%s" in definition "%s".'
                                      + self._suggest_words(pattern_type, self._definition_pattern_parsers.keys()))
                                     % (pattern_type, name))
+                    continue
 
                 pattern = pattern_parser(definition[pattern_type])
                 definitions[name].append(pattern)
             if not definitions[name]:
-                raise Exception('The definition "%s" has no patterns, so it should be deleted.' % name)
+                self._log_error('The definition "%s" has no patterns, so it should be deleted.' % name)
+                continue
         watch_list.definitions = definitions
 
     def _parse_rules(self, rules_section):
@@ -112,7 +121,8 @@
         for complex_definition in rules_section:
             instructions = rules_section[complex_definition]
             if not instructions:
-                raise Exception('A rule for definition "%s" is empty, so it should be deleted.' % complex_definition)
+                self._log_error('A rule for definition "%s" is empty, so it should be deleted.' % complex_definition)
+                continue
             rules.append(WatchListRule(complex_definition, instructions))
         return rules
 
@@ -132,15 +142,20 @@
 
         contributors = CommitterList()
         for cc_rule in watch_list.cc_rules:
-            for email in cc_rule.instructions():
+            # Copy the instructions since we'll be remove items from the original list and
+            # modifying a list while iterating through it leads to undefined behavior.
+            intructions_copy = cc_rule.instructions()[:]
+            for email in intructions_copy:
                 if not contributors.contributor_by_email(email):
-                    raise Exception("The email alias %s which is in the watchlist is not listed as a contributor in committers.py" % email)
+                    cc_rule.remove_instruction(email)
+                    self._log_error("The email alias %s which is in the watchlist is not listed as a contributor in committers.py" % email)
+                    continue
 
     def _verify_all_definitions_are_used(self, watch_list, used_definitions):
         definitions_not_used = set(watch_list.definitions.keys())
         definitions_not_used.difference_update(used_definitions)
         if definitions_not_used:
-            raise Exception('The following definitions are not used and should be removed: %s' % (', '.join(definitions_not_used)))
+            self._log_error('The following definitions are not used and should be removed: %s' % (', '.join(definitions_not_used)))
 
     def _validate_definitions(self, definitions, rules_section_name, watch_list):
         declared_definitions = watch_list.definitions.keys()
@@ -151,7 +166,7 @@
             suggestions = ''
             if len(definition_set) == 1:
                 suggestions = self._suggest_words(set().union(definition_set).pop(), declared_definitions)
-            raise Exception('In section "%s", the following definitions are not used and should be removed: %s%s' % (rules_section_name, ', '.join(definition_set), suggestions))
+            self._log_error('In section "%s", the following definitions are not used and should be removed: %s%s' % (rules_section_name, ', '.join(definition_set), suggestions))
 
     def _rule_definitions_as_set(self, rules):
         definition_set = set()

Modified: trunk/Tools/Scripts/webkitpy/common/watchlist/watchlistparser_unittest.py (97819 => 97820)


--- trunk/Tools/Scripts/webkitpy/common/watchlist/watchlistparser_unittest.py	2011-10-19 00:40:30 UTC (rev 97819)
+++ trunk/Tools/Scripts/webkitpy/common/watchlist/watchlistparser_unittest.py	2011-10-19 00:47:26 UTC (rev 97820)
@@ -28,7 +28,13 @@
 
 '''Unit tests for watchlistparser.py.'''
 
+
+import logging
+import sys
+
+
 from webkitpy.common import webkitunittest
+from webkitpy.common.system.outputcapture import OutputCapture
 from webkitpy.common.watchlist.watchlistparser import WatchListParser
 
 
@@ -38,19 +44,18 @@
         self._watch_list_parser = WatchListParser()
 
     def test_bad_section(self):
-        watch_list_with_bad_section = ('{"FOO": {}}')
-        self.assertRaisesRegexp(Exception, 'Unknown section "FOO" in watch list.',
-                                self._watch_list_parser.parse, watch_list_with_bad_section)
+        watch_list = ('{"FOO": {}}')
+        OutputCapture().assert_outputs(self, self._watch_list_parser.parse, args=[watch_list],
+                                       expected_logs='Unknown section "FOO" in watch list.\n')
 
     def test_section_typo(self):
-        watch_list_with_bad_section = ('{"DEFINTIONS": {}}')
-        self.assertRaisesRegexp(Exception,
-                                r'Unknown section "DEFINTIONS" in watch list\.\s*'
-                                + r'Perhaps it should be DEFINITIONS\.',
-                                self._watch_list_parser.parse, watch_list_with_bad_section)
+        watch_list = ('{"DEFINTIONS": {}}')
+        OutputCapture().assert_outputs(self, self._watch_list_parser.parse, args=[watch_list],
+                                       expected_logs='Unknown section "DEFINTIONS" in watch list.'
+                                       + '\n\nPerhaps it should be DEFINITIONS.\n')
 
     def test_bad_definition(self):
-        watch_list_with_bad_definition = (
+        watch_list = (
             '{'
             '    "DEFINITIONS": {'
             '        "WatchList1|A": {'
@@ -59,35 +64,43 @@
             '     },'
             '}')
 
-        self.assertRaisesRegexp(Exception, r'Invalid character "\|" in definition "WatchList1\|A"\.',
-                                self._watch_list_parser.parse, watch_list_with_bad_definition)
+        OutputCapture().assert_outputs(self, self._watch_list_parser.parse, args=[watch_list],
+                                       expected_logs='Invalid character "|" in definition "WatchList1|A".\n')
 
     def test_bad_match_type(self):
-        watch_list_with_bad_match_type = (
+        watch_list = (
             '{'
             '    "DEFINITIONS": {'
             '        "WatchList1": {'
             '            "nothing_matches_this": r".*\\MyFileName\\.cpp",'
+            '            "filename": r".*\\MyFileName\\.cpp",'
             '        },'
             '     },'
+            '    "CC_RULES": {'
+            '        "WatchList1": ["[email protected]"],'
+            '     },'
             '}')
 
-        self.assertRaisesRegexp(Exception, 'Unknown pattern type "nothing_matches_this" in definition "WatchList1".',
-                                self._watch_list_parser.parse, watch_list_with_bad_match_type)
+        OutputCapture().assert_outputs(self, self._watch_list_parser.parse, args=[watch_list],
+                                       expected_logs='Unknown pattern type "nothing_matches_this" in definition "WatchList1".\n')
 
     def test_match_type_typo(self):
-        watch_list_with_bad_match_type = (
+        watch_list = (
             '{'
             '    "DEFINITIONS": {'
             '        "WatchList1": {'
             '            "iflename": r".*\\MyFileName\\.cpp",'
+            '            "more": r"RefCounted",'
             '        },'
             '     },'
+            '    "CC_RULES": {'
+            '        "WatchList1": ["[email protected]"],'
+            '     },'
             '}')
 
-        self.assertRaisesRegexp(Exception, r'Unknown pattern type "iflename" in definition "WatchList1"\.\s*'
-                                + r'Perhaps it should be filename\.',
-                                self._watch_list_parser.parse, watch_list_with_bad_match_type)
+        OutputCapture().assert_outputs(self, self._watch_list_parser.parse, args=[watch_list],
+                                       expected_logs='Unknown pattern type "iflename" in definition "WatchList1".'
+                                       + '\n\nPerhaps it should be filename.\n')
 
     def test_empty_definition(self):
         watch_list = (
@@ -96,10 +109,13 @@
             '        "WatchList1": {'
             '        },'
             '     },'
+            '    "CC_RULES": {'
+            '        "WatchList1": ["[email protected]"],'
+            '     },'
             '}')
 
-        self.assertRaisesRegexp(Exception, r'The definition "WatchList1" has no patterns, so it should be deleted.',
-                                self._watch_list_parser.parse, watch_list)
+        OutputCapture().assert_outputs(self, self._watch_list_parser.parse, args=[watch_list],
+                                       expected_logs='The definition "WatchList1" has no patterns, so it should be deleted.\n')
 
     def test_empty_cc_rule(self):
         watch_list = (
@@ -114,8 +130,9 @@
             '     },'
             '}')
 
-        self.assertRaisesRegexp(Exception, r'A rule for definition "WatchList1" is empty, so it should be deleted.',
-                                self._watch_list_parser.parse, watch_list)
+        OutputCapture().assert_outputs(self, self._watch_list_parser.parse, args=[watch_list],
+                                       expected_logs='A rule for definition "WatchList1" is empty, so it should be deleted.\n'
+                                       + 'The following definitions are not used and should be removed: WatchList1\n')
 
     def test_cc_rule_with_invalid_email(self):
         watch_list = (
@@ -130,8 +147,9 @@
             '     },'
             '}')
 
-        self.assertRaisesRegexp(Exception, r'The email alias levin\+bad\[email protected] which is in the watchlist is not listed as a contributor in committers\.py',
-                                self._watch_list_parser.parse, watch_list)
+        OutputCapture().assert_outputs(self, self._watch_list_parser.parse, args=[watch_list],
+                                       expected_logs='The email alias [email protected] which is'
+                                       + ' in the watchlist is not listed as a contributor in committers.py\n')
 
     def test_empty_message_rule(self):
         watch_list = (
@@ -147,8 +165,9 @@
             '     },'
             '}')
 
-        self.assertRaisesRegexp(Exception, r'A rule for definition "WatchList1" is empty, so it should be deleted.',
-                                self._watch_list_parser.parse, watch_list)
+        OutputCapture().assert_outputs(self, self._watch_list_parser.parse, args=[watch_list],
+                                       expected_logs='A rule for definition "WatchList1" is empty, so it should be deleted.\n'
+                                       + 'The following definitions are not used and should be removed: WatchList1\n')
 
     def test_unused_defintion(self):
         watch_list = (
@@ -160,8 +179,8 @@
             '     },'
             '}')
 
-        self.assertRaisesRegexp(Exception, r'The following definitions are not used and should be removed: WatchList1',
-                                self._watch_list_parser.parse, watch_list)
+        OutputCapture().assert_outputs(self, self._watch_list_parser.parse, args=[watch_list],
+                                       expected_logs='The following definitions are not used and should be removed: WatchList1\n')
 
     def test_cc_rule_with_undefined_defintion(self):
         watch_list = (
@@ -171,8 +190,8 @@
             '     },'
             '}')
 
-        self.assertRaisesRegexp(Exception, r'In section "CC_RULES", the following definitions are not used and should be removed: WatchList1',
-                                self._watch_list_parser.parse, watch_list)
+        OutputCapture().assert_outputs(self, self._watch_list_parser.parse, args=[watch_list],
+                                       expected_logs='In section "CC_RULES", the following definitions are not used and should be removed: WatchList1\n')
 
     def test_message_rule_with_undefined_defintion(self):
         watch_list = (
@@ -182,8 +201,8 @@
             '     },'
             '}')
 
-        self.assertRaisesRegexp(Exception, r'In section "MESSAGE_RULES", the following definitions are not used and should be removed: WatchList1',
-                                self._watch_list_parser.parse, watch_list)
+        OutputCapture().assert_outputs(self, self._watch_list_parser.parse, args=[watch_list],
+                                       expected_logs='In section "MESSAGE_RULES", the following definitions are not used and should be removed: WatchList1\n')
 
     def test_cc_rule_with_undefined_defintion_with_suggestion(self):
         watch_list = (
@@ -201,6 +220,6 @@
             '     },'
             '}')
 
-        self.assertRaisesRegexp(Exception, r'In section "CC_RULES", the following definitions are not used and should be removed: WatchList\s*'
-                                r'Perhaps it should be WatchList1\.',
-                                self._watch_list_parser.parse, watch_list)
+        OutputCapture().assert_outputs(self, self._watch_list_parser.parse, args=[watch_list],
+                                       expected_logs='In section "CC_RULES", the following definitions are not used and should be removed: WatchList'
+                                       + '\n\nPerhaps it should be WatchList1.\n')

Modified: trunk/Tools/Scripts/webkitpy/common/watchlist/watchlistrule.py (97819 => 97820)


--- trunk/Tools/Scripts/webkitpy/common/watchlist/watchlistrule.py	2011-10-19 00:40:30 UTC (rev 97819)
+++ trunk/Tools/Scripts/webkitpy/common/watchlist/watchlistrule.py	2011-10-19 00:47:26 UTC (rev 97820)
@@ -41,3 +41,6 @@
 
     def instructions(self):
         return self._instructions
+
+    def remove_instruction(self, instruction):
+        self._instructions.remove(instruction)

Modified: trunk/Tools/Scripts/webkitpy/common/watchlist/watchlistrule_unittest.py (97819 => 97820)


--- trunk/Tools/Scripts/webkitpy/common/watchlist/watchlistrule_unittest.py	2011-10-19 00:40:30 UTC (rev 97819)
+++ trunk/Tools/Scripts/webkitpy/common/watchlist/watchlistrule_unittest.py	2011-10-19 00:47:26 UTC (rev 97820)
@@ -32,11 +32,17 @@
 
 
 class WatchListRuleTest(unittest.TestCase):
-    def test_action_list(self):
+    def test_instruction_list(self):
         instructions = ['a', 'b']
         rule = WatchListRule('definition1', instructions[:])
         self.assertEqual(instructions, rule.instructions())
 
+    def test_remove_instruction(self):
+        instructions = ['a', 'b']
+        rule = WatchListRule('definition1', instructions[:])
+        rule.remove_instruction('b')
+        self.assertEqual(['a'], rule.instructions())
+
     def test_simple_definition(self):
         definition_name = 'definition1'
         rule = WatchListRule(definition_name, [])
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to