Esempio n. 1
0
    def watch_list(self):
        config_path = self.filesystem.dirname(self.filesystem.path_to_module('webkitpy.common.config'))
        watch_list_full_path = self.filesystem.join(config_path, 'watchlist')
        if not self.filesystem.exists(watch_list_full_path):
            raise Exception('Watch list file (%s) not found.' % watch_list_full_path)

        watch_list_contents = self.filesystem.read_text_file(watch_list_full_path)
        return WatchListParser().parse(watch_list_contents)
Esempio n. 2
0
    def check(self, lines):
        def log_to_style_error(message):
            # Always report line 0 since we don't have anything better.
            self._handle_style_error(0,
                                     'watchlist/general', 5,
                                     message)

        WatchListParser(log_error=log_to_style_error).parse('\n'.join(lines))
 def setUp(self):
     self._watch_list_parser = WatchListParser()
class WatchListTest(unittest.TestCase):
    def setUp(self):
        self._watch_list_parser = WatchListParser()

    def test_filename_definition_no_matches(self):
        watch_list = self._watch_list_parser.parse(
            '{'
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "filename": r".*\\MyFileName\\.cpp",'
            '        },'
            '     },'
            '    "CC_RULES": {'
            '        "WatchList1": ['
            '            "*****@*****.**",'
            '        ],'
           '    },'
            '}')
        self.assertEqual(set([]), watch_list.find_matching_definitions(DIFF_TEST_DATA))

    def test_filename_definition(self):
        watch_list = self._watch_list_parser.parse(
            '{'
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "filename": r"WebCore/rendering/style/StyleFlexibleBoxData\.h",'
            '        },'
            '     },'
            '    "CC_RULES": {'
            '        "WatchList1": ['
            '            "*****@*****.**",'
            '        ],'
           '    },'
            '}')
        self.assertEqual(set(['WatchList1']), watch_list.find_matching_definitions(DIFF_TEST_DATA))

    def test_cc_rules_simple(self):
        watch_list = self._watch_list_parser.parse(
            '{'
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "filename": r"WebCore/rendering/style/StyleFlexibleBoxData\.h",'
            '        },'
            '     },'
            '    "CC_RULES": {'
            '        "WatchList1": ['
            '            "*****@*****.**",'
            '        ],'
           '    },'
            '}')
        cc_and_messages = watch_list.determine_cc_and_messages(DIFF_TEST_DATA)
        self.assertEqual({
                'cc_list': ['*****@*****.**'],
                'messages': [],
                }, cc_and_messages)

    def test_cc_rules_complex(self):
        watch_list = self._watch_list_parser.parse(
            '{'
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "filename": r"WebCore/rendering/style/StyleFlexibleBoxData\.h",'
            '        },'
            '        "WatchList2": {'
            '            "filename": r"WillNotMatch",'
            '        },'
            '        "WatchList3": {'
            '            "filename": r"WillNotMatch",'
            '        },'
            '     },'
            '    "CC_RULES": {'
            '        "WatchList2|WatchList1|WatchList3": [ "*****@*****.**", ],'
            '    },'
            '}')
        cc_and_messages = watch_list.determine_cc_and_messages(DIFF_TEST_DATA)
        self.assertEqual({
                'cc_list': ['*****@*****.**'],
                'messages': [],
                }, cc_and_messages)

    def test_cc_and_message_rules_complex(self):
        watch_list = self._watch_list_parser.parse(
            '{'
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "filename": r"WebCore/rendering/style/StyleFlexibleBoxData\.h",'
            '        },'
            '        "WatchList2": {'
            '            "filename": r"WillNotMatch",'
            '        },'
            '        "WatchList3": {'
            '            "filename": r"WillNotMatch",'
            '        },'
            '     },'
            '    "CC_RULES": {'
            '        "WatchList2|WatchList1|WatchList3": [ "*****@*****.**", ],'
            '    },'
            '    "MESSAGE_RULES": {'
            '        "WatchList2|WatchList1|WatchList3": [ "msg1", "msg2", ],'
            '    },'
            '}')
        cc_and_messages = watch_list.determine_cc_and_messages(DIFF_TEST_DATA)
        self.assertEqual({
                'cc_list': ['*****@*****.**'],
                'messages': ['msg1', 'msg2'],
                }, cc_and_messages)

    def test_cc_and_message_rules_no_matches(self):
        watch_list = self._watch_list_parser.parse(
            '{'
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "filename": r"WebCore/rendering/style/ThisFileDoesNotExist\.h",'
            '        },'
            '        "WatchList2": {'
            '            "filename": r"WillNotMatch",'
            '        },'
            '        "WatchList3": {'
            '            "filename": r"WillNotMatch",'
            '        },'
            '     },'
            '    "CC_RULES": {'
            '        "WatchList2|WatchList1|WatchList3": [ "*****@*****.**", ],'
            '    },'
            '    "MESSAGE_RULES": {'
            '        "WatchList2|WatchList1|WatchList3": [ "msg1", "msg2", ],'
            '    },'
            '}')
        cc_and_messages = watch_list.determine_cc_and_messages(DIFF_TEST_DATA)
        self.assertEqual({
                'cc_list': [],
                'messages': [],
                }, cc_and_messages)

    def test_added_match(self):
        watch_list = self._watch_list_parser.parse(
            '{'
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "in_added_lines": r"RenderStyle::initialBoxOrient",'
            '        },'
            '        "WatchList2": {'
            '            "in_deleted_lines": r"RenderStyle::initialBoxOrient",'
            '        },'
            '     },'
            '    "CC_RULES": {'
            '        "WatchList1": [ "*****@*****.**", ],'
            '        "WatchList2": [ "*****@*****.**", ],'
            '    },'
            '}')
        cc_and_messages = watch_list.determine_cc_and_messages(DIFF_TEST_DATA)
        self.assertEqual({
                'cc_list': ['*****@*****.**'],
                'messages': [],
                }, cc_and_messages)

    def test_deleted_match(self):
        watch_list = self._watch_list_parser.parse(
            '{'
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "in_added_lines": r"unsigned orient: 1;",'
            '        },'
            '        "WatchList2": {'
            '            "in_deleted_lines": r"unsigned orient: 1;",'
            '        },'
            '     },'
            '    "CC_RULES": {'
            '        "WatchList1": [ "*****@*****.**", ],'
            '        "WatchList2": [ "*****@*****.**", ],'
            '    },'
            '}')
        cc_and_messages = watch_list.determine_cc_and_messages(DIFF_TEST_DATA)
        self.assertEqual({
                'cc_list': ['*****@*****.**'],
                'messages': [],
                }, cc_and_messages)

    def test_more_and_less_match(self):
        watch_list = self._watch_list_parser.parse(
            '{'
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            # This pattern is in both added and deleted lines, so no match.
            '            "more": r"userSelect == o\.userSelect",'
            '        },'
            '        "WatchList2": {'
            '            "more": r"boxOrient\(o\.boxOrient\)",'
            '        },'
            '        "WatchList3": {'
            '            "less": r"unsigned orient"'
            '        },'
            '     },'
            '    "CC_RULES": {'
            '        "WatchList1": [ "*****@*****.**", ],'
            '        "WatchList2": [ "*****@*****.**", ],'
            '    },'
            '    "MESSAGE_RULES": {'
            '        "WatchList3": ["Test message."],'
            '    },'
            '}')
        cc_and_messages = watch_list.determine_cc_and_messages(DIFF_TEST_DATA)
        self.assertEqual({
                'cc_list': ['*****@*****.**'],
                'messages': ["Test message."],
                }, cc_and_messages)

    def test_complex_match(self):
        watch_list = self._watch_list_parser.parse(
            '{'
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "filename": r"WebCore/rendering/style/StyleRareInheritedData\.cpp",'
            '            "in_added_lines": r"\&\& boxOrient == o\.boxOrient;",'
            '            "in_deleted_lines": r"\&\& userSelect == o\.userSelect;",'
            '            "more": r"boxOrient\(o\.boxOrient\)",'
            '        },'
            '        "WatchList2": {'
            '            "filename": r"WebCore/rendering/style/StyleRareInheritedData\.cpp",'
            '            "in_added_lines": r"RenderStyle::initialBoxOrient",'
            '            "less": r"userSelect;"'
            '        },'
            # WatchList3 won't match because these two patterns aren't in the same file.
            '        "WatchList3": {'
            '            "in_added_lines": r"RenderStyle::initialBoxOrient",'
            '            "in_deleted_lines": r"unsigned orient: 1;",'
            '        },'
            '     },'
            '    "CC_RULES": {'
            '        "WatchList1": [ "*****@*****.**", ],'
            '        "WatchList3": [ "*****@*****.**", ],'
            '    },'
            '    "MESSAGE_RULES": {'
            '        "WatchList2": ["This is a test message."],'
            '    },'
            '}')
        cc_and_messages = watch_list.determine_cc_and_messages(DIFF_TEST_DATA)
        self.assertEqual({
                'cc_list': ['*****@*****.**'],
                'messages': ["This is a test message."],
                }, cc_and_messages)
 def setUp(self):
     webkitunittest.TestCase.setUp(self)
     self._watch_list_parser = WatchListParser()
Esempio n. 6
0
 def setUp(self):
     self._watch_list_parser = WatchListParser()
Esempio n. 7
0
class WatchListTest(unittest.TestCase):
    def setUp(self):
        self._watch_list_parser = WatchListParser()

    def test_filename_definition_no_matches(self):
        watch_list = self._watch_list_parser.parse(
            "{"
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "filename": r".*\\MyFileName\\.cpp",'
            "        },"
            "     },"
            '    "CC_RULES": {'
            '        "WatchList1": ['
            '            "*****@*****.**",'
            "        ],"
            "    },"
            "}"
        )
        self.assertEqual(set([]), watch_list.find_matching_definitions(DIFF_TEST_DATA))

    def test_filename_definition(self):
        watch_list = self._watch_list_parser.parse(
            "{"
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "filename": r"WebCore/rendering/style/StyleFlexibleBoxData\.h",'
            "        },"
            "     },"
            '    "CC_RULES": {'
            '        "WatchList1": ['
            '            "*****@*****.**",'
            "        ],"
            "    },"
            "}"
        )
        self.assertEqual(set(["WatchList1"]), watch_list.find_matching_definitions(DIFF_TEST_DATA))

    def test_cc_rules_simple(self):
        watch_list = self._watch_list_parser.parse(
            "{"
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "filename": r"WebCore/rendering/style/StyleFlexibleBoxData\.h",'
            "        },"
            "     },"
            '    "CC_RULES": {'
            '        "WatchList1": ['
            '            "*****@*****.**",'
            "        ],"
            "    },"
            "}"
        )
        cc_and_messages = watch_list.determine_cc_and_messages(DIFF_TEST_DATA)
        self.assertEqual({"cc_list": ["*****@*****.**"], "messages": []}, cc_and_messages)

    def test_cc_rules_complex(self):
        watch_list = self._watch_list_parser.parse(
            "{"
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "filename": r"WebCore/rendering/style/StyleFlexibleBoxData\.h",'
            "        },"
            '        "WatchList2": {'
            '            "filename": r"WillNotMatch",'
            "        },"
            '        "WatchList3": {'
            '            "filename": r"WillNotMatch",'
            "        },"
            "     },"
            '    "CC_RULES": {'
            '        "WatchList2|WatchList1|WatchList3": [ "*****@*****.**", ],'
            "    },"
            "}"
        )
        cc_and_messages = watch_list.determine_cc_and_messages(DIFF_TEST_DATA)
        self.assertEqual({"cc_list": ["*****@*****.**"], "messages": []}, cc_and_messages)

    def test_cc_and_message_rules_complex(self):
        watch_list = self._watch_list_parser.parse(
            "{"
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "filename": r"WebCore/rendering/style/StyleFlexibleBoxData\.h",'
            "        },"
            '        "WatchList2": {'
            '            "filename": r"WillNotMatch",'
            "        },"
            '        "WatchList3": {'
            '            "filename": r"WillNotMatch",'
            "        },"
            "     },"
            '    "CC_RULES": {'
            '        "WatchList2|WatchList1|WatchList3": [ "*****@*****.**", ],'
            "    },"
            '    "MESSAGE_RULES": {'
            '        "WatchList2|WatchList1|WatchList3": [ "msg1", "msg2", ],'
            "    },"
            "}"
        )
        cc_and_messages = watch_list.determine_cc_and_messages(DIFF_TEST_DATA)
        self.assertEqual({"cc_list": ["*****@*****.**"], "messages": ["msg1", "msg2"]}, cc_and_messages)

    def test_cc_and_message_rules_no_matches(self):
        watch_list = self._watch_list_parser.parse(
            "{"
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "filename": r"WebCore/rendering/style/ThisFileDoesNotExist\.h",'
            "        },"
            '        "WatchList2": {'
            '            "filename": r"WillNotMatch",'
            "        },"
            '        "WatchList3": {'
            '            "filename": r"WillNotMatch",'
            "        },"
            "     },"
            '    "CC_RULES": {'
            '        "WatchList2|WatchList1|WatchList3": [ "*****@*****.**", ],'
            "    },"
            '    "MESSAGE_RULES": {'
            '        "WatchList2|WatchList1|WatchList3": [ "msg1", "msg2", ],'
            "    },"
            "}"
        )
        cc_and_messages = watch_list.determine_cc_and_messages(DIFF_TEST_DATA)
        self.assertEqual({"cc_list": [], "messages": []}, cc_and_messages)

    def test_added_match(self):
        watch_list = self._watch_list_parser.parse(
            "{"
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "in_added_lines": r"RenderStyle::initialBoxOrient",'
            "        },"
            '        "WatchList2": {'
            '            "in_deleted_lines": r"RenderStyle::initialBoxOrient",'
            "        },"
            "     },"
            '    "CC_RULES": {'
            '        "WatchList1": [ "*****@*****.**", ],'
            '        "WatchList2": [ "*****@*****.**", ],'
            "    },"
            "}"
        )
        cc_and_messages = watch_list.determine_cc_and_messages(DIFF_TEST_DATA)
        self.assertEqual({"cc_list": ["*****@*****.**"], "messages": []}, cc_and_messages)

    def test_deleted_match(self):
        watch_list = self._watch_list_parser.parse(
            "{"
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "in_added_lines": r"unsigned orient: 1;",'
            "        },"
            '        "WatchList2": {'
            '            "in_deleted_lines": r"unsigned orient: 1;",'
            "        },"
            "     },"
            '    "CC_RULES": {'
            '        "WatchList1": [ "*****@*****.**", ],'
            '        "WatchList2": [ "*****@*****.**", ],'
            "    },"
            "}"
        )
        cc_and_messages = watch_list.determine_cc_and_messages(DIFF_TEST_DATA)
        self.assertEqual({"cc_list": ["*****@*****.**"], "messages": []}, cc_and_messages)

    def test_more_and_less_match(self):
        watch_list = self._watch_list_parser.parse(
            "{"
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            # This pattern is in both added and deleted lines, so no match.
            '            "more": r"userSelect == o\.userSelect",'
            "        },"
            '        "WatchList2": {'
            '            "more": r"boxOrient\(o\.boxOrient\)",'
            "        },"
            '        "WatchList3": {'
            '            "less": r"unsigned orient"'
            "        },"
            "     },"
            '    "CC_RULES": {'
            '        "WatchList1": [ "*****@*****.**", ],'
            '        "WatchList2": [ "*****@*****.**", ],'
            "    },"
            '    "MESSAGE_RULES": {'
            '        "WatchList3": ["Test message."],'
            "    },"
            "}"
        )
        cc_and_messages = watch_list.determine_cc_and_messages(DIFF_TEST_DATA)
        self.assertEqual({"cc_list": ["*****@*****.**"], "messages": ["Test message."]}, cc_and_messages)

    def test_complex_match(self):
        watch_list = self._watch_list_parser.parse(
            "{"
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "filename": r"WebCore/rendering/style/StyleRareInheritedData\.cpp",'
            '            "in_added_lines": r"\&\& boxOrient == o\.boxOrient;",'
            '            "in_deleted_lines": r"\&\& userSelect == o\.userSelect;",'
            '            "more": r"boxOrient\(o\.boxOrient\)",'
            "        },"
            '        "WatchList2": {'
            '            "filename": r"WebCore/rendering/style/StyleRareInheritedData\.cpp",'
            '            "in_added_lines": r"RenderStyle::initialBoxOrient",'
            '            "less": r"userSelect;"'
            "        },"
            # WatchList3 won't match because these two patterns aren't in the same file.
            '        "WatchList3": {'
            '            "in_added_lines": r"RenderStyle::initialBoxOrient",'
            '            "in_deleted_lines": r"unsigned orient: 1;",'
            "        },"
            "     },"
            '    "CC_RULES": {'
            '        "WatchList1": [ "*****@*****.**", ],'
            '        "WatchList3": [ "*****@*****.**", ],'
            "    },"
            '    "MESSAGE_RULES": {'
            '        "WatchList2": ["This is a test message."],'
            "    },"
            "}"
        )
        cc_and_messages = watch_list.determine_cc_and_messages(DIFF_TEST_DATA)
        self.assertEqual({"cc_list": ["*****@*****.**"], "messages": ["This is a test message."]}, cc_and_messages)
Esempio n. 8
0
class WatchListParserTest(webkitunittest.TestCase):
    def setUp(self):
        webkitunittest.TestCase.setUp(self)
        self._watch_list_parser = WatchListParser()

    def test_bad_section(self):
        watch_list = ('{"FOO": {}}')
        with OutputCapture(level=logging.INFO) as captured:
            self._watch_list_parser.parse(watch_list)
        self.assertEqual(
            captured.root.log.getvalue(),
            'Unknown section "FOO" in watch list.\n',
        )

    def test_section_typo(self):
        watch_list = ('{"DEFINTIONS": {}}')
        with OutputCapture(level=logging.INFO) as captured:
            self._watch_list_parser.parse(watch_list)
        self.assertEqual(
            captured.root.log.getvalue(),
            'Unknown section "DEFINTIONS" in watch list.\n\nPerhaps it should be DEFINITIONS.\n',
        )

    def test_bad_definition(self):
        watch_list = ('{'
                      '    "DEFINITIONS": {'
                      '        "WatchList1|A": {'
                      '            "filename": r".*MyFileName\\.cpp",'
                      '        },'
                      '     },'
                      '}')

        with OutputCapture(level=logging.INFO) as captured:
            self._watch_list_parser.parse(watch_list)
        self.assertEqual(
            captured.root.log.getvalue(),
            'Invalid character "|" in definition "WatchList1|A".\n')

    def test_bad_filename_regex(self):
        watch_list = ('{'
                      '    "DEFINITIONS": {'
                      '        "WatchList1": {'
                      '            "filename": r"*",'
                      '            "more": r"RefCounted",'
                      '        },'
                      '     },'
                      '    "CC_RULES": {'
                      '        "WatchList1": ["*****@*****.**"],'
                      '     },'
                      '}')

        with OutputCapture(level=logging.INFO) as captured:
            self._watch_list_parser.parse(watch_list)
        if sys.version_info > (3, 0):
            expected_log = 'The regex "*" is invalid due to "nothing to repeat at position 0".\n'
        else:
            expected_log = 'The regex "*" is invalid due to "nothing to repeat".\n'
        self.assertEqual(captured.root.log.getvalue(), expected_log)

    def test_bad_more_regex(self):
        watch_list = ('{'
                      '    "DEFINITIONS": {'
                      '        "WatchList1": {'
                      '            "filename": r"aFileName\\.cpp",'
                      '            "more": r"*",'
                      '        },'
                      '     },'
                      '    "CC_RULES": {'
                      '        "WatchList1": ["*****@*****.**"],'
                      '     },'
                      '}')

        with OutputCapture(level=logging.INFO) as captured:
            self._watch_list_parser.parse(watch_list)
        if sys.version_info > (3, 0):
            expected_log = 'The regex "*" is invalid due to "nothing to repeat at position 0".\n'
        else:
            expected_log = 'The regex "*" is invalid due to "nothing to repeat".\n'
        self.assertEqual(captured.root.log.getvalue(), expected_log)

    def test_bad_match_type(self):
        watch_list = (
            '{'
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "nothing_matches_this": r".*MyFileName\\.cpp",'
            '            "filename": r".*MyFileName\\.cpp",'
            '        },'
            '     },'
            '    "CC_RULES": {'
            '        "WatchList1": ["*****@*****.**"],'
            '     },'
            '}')

        with OutputCapture(level=logging.INFO) as captured:
            self._watch_list_parser.parse(watch_list)
        self.assertEqual(
            captured.root.log.getvalue(),
            'Unknown pattern type "nothing_matches_this" in definition "WatchList1".\n',
        )

    def test_match_type_typo(self):
        watch_list = ('{'
                      '    "DEFINITIONS": {'
                      '        "WatchList1": {'
                      '            "iflename": r".*MyFileName\\.cpp",'
                      '            "more": r"RefCounted",'
                      '        },'
                      '     },'
                      '    "CC_RULES": {'
                      '        "WatchList1": ["*****@*****.**"],'
                      '     },'
                      '}')

        with OutputCapture(level=logging.INFO) as captured:
            self._watch_list_parser.parse(watch_list)
        self.assertEqual(
            captured.root.log.getvalue(),
            'Unknown pattern type "iflename" in definition "WatchList1".\n\nPerhaps it should be filename.\n',
        )

    def test_empty_definition(self):
        watch_list = ('{'
                      '    "DEFINITIONS": {'
                      '        "WatchList1": {'
                      '        },'
                      '     },'
                      '    "CC_RULES": {'
                      '        "WatchList1": ["*****@*****.**"],'
                      '     },'
                      '}')

        with OutputCapture(level=logging.INFO) as captured:
            self._watch_list_parser.parse(watch_list)
        self.assertEqual(
            captured.root.log.getvalue(),
            'The definition "WatchList1" has no patterns, so it should be deleted.\n',
        )

    def test_empty_cc_rule(self):
        watch_list = ('{'
                      '    "DEFINITIONS": {'
                      '        "WatchList1": {'
                      '            "filename": r".*MyFileName\\.cpp",'
                      '        },'
                      '     },'
                      '    "CC_RULES": {'
                      '        "WatchList1": [],'
                      '     },'
                      '}')

        with OutputCapture(level=logging.INFO) as captured:
            self._watch_list_parser.parse(watch_list)
        self.assertEqual(
            captured.root.log.getvalue(),
            'A rule for definition "WatchList1" is empty, so it should be deleted.\nThe following definitions are not '
            'used and should be removed: WatchList1\n',
        )

    def test_cc_rule_with_invalid_email(self):
        watch_list = ('{'
                      '    "DEFINITIONS": {'
                      '        "WatchList1": {'
                      '            "filename": r".*MyFileName\\.cpp",'
                      '        },'
                      '     },'
                      '    "CC_RULES": {'
                      '        "WatchList1": ["*****@*****.**"],'
                      '     },'
                      '}')

        with OutputCapture(level=logging.INFO) as captured:
            self._watch_list_parser.parse(watch_list)
        self.assertEqual(
            captured.root.log.getvalue(),
            'The email alias [email protected] which is in the watchlist is not listed as a contributor in '
            'contributors.json\n',
        )

    def test_cc_rule_with_secondary_email(self):
        # FIXME: We should provide a mock of CommitterList so that we can test this on fake data.
        watch_list = ('{'
                      '    "DEFINITIONS": {'
                      '        "WatchList1": {'
                      '            "filename": r".*MyFileName\\.cpp",'
                      '        },'
                      '     },'
                      '    "CC_RULES": {'
                      '        "WatchList1": ["*****@*****.**"],'
                      '     },'
                      '}')

        with OutputCapture(level=logging.INFO) as captured:
            self._watch_list_parser.parse(watch_list)
        self.assertEqual(captured.root.log.getvalue(), '')

    def test_empty_message_rule(self):
        watch_list = ('{'
                      '    "DEFINITIONS": {'
                      '        "WatchList1": {'
                      '            "filename": r".*MyFileName\\.cpp",'
                      '        },'
                      '     },'
                      '    "MESSAGE_RULES": {'
                      '        "WatchList1": ['
                      '        ],'
                      '     },'
                      '}')

        with OutputCapture(level=logging.INFO) as captured:
            self._watch_list_parser.parse(watch_list)
        self.assertEqual(
            captured.root.log.getvalue(),
            'A rule for definition "WatchList1" is empty, so it should be deleted.\nThe following definitions are not '
            'used and should be removed: WatchList1\n',
        )

    def test_unused_defintion(self):
        watch_list = ('{'
                      '    "DEFINITIONS": {'
                      '        "WatchList1": {'
                      '            "filename": r".*MyFileName\\.cpp",'
                      '        },'
                      '     },'
                      '}')

        with OutputCapture(level=logging.INFO) as captured:
            self._watch_list_parser.parse(watch_list)
        self.assertEqual(
            captured.root.log.getvalue(),
            'The following definitions are not used and should be removed: WatchList1\n',
        )

    def test_cc_rule_with_undefined_defintion(self):
        watch_list = ('{'
                      '    "CC_RULES": {'
                      '        "WatchList1": ["*****@*****.**"]'
                      '     },'
                      '}')

        with OutputCapture(level=logging.INFO) as captured:
            self._watch_list_parser.parse(watch_list)
        self.assertEqual(
            captured.root.log.getvalue(),
            '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 = ('{'
                      '    "MESSAGE_RULES": {'
                      '        "WatchList1": ["The message."]'
                      '     },'
                      '}')

        with OutputCapture(level=logging.INFO) as captured:
            self._watch_list_parser.parse(watch_list)
        self.assertEqual(
            captured.root.log.getvalue(),
            '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 = ('{'
                      '    "DEFINITIONS": {'
                      '        "WatchList1": {'
                      '            "filename": r".*MyFileName\\.cpp",'
                      '        },'
                      '     },'
                      '    "CC_RULES": {'
                      '        "WatchList": ["*****@*****.**"]'
                      '     },'
                      '    "MESSAGE_RULES": {'
                      '        "WatchList1": ["*****@*****.**"]'
                      '     },'
                      '}')

        with OutputCapture(level=logging.INFO) as captured:
            self._watch_list_parser.parse(watch_list)
        self.assertEqual(
            captured.root.log.getvalue(),
            'In section "CC_RULES", the following definitions are not used and should be removed: '
            'WatchList\n\nPerhaps it should be WatchList1.\n',
        )

    def test_cc_rule_with_complex_logic(self):
        watch_list = (
            '{'
            '    "DEFINITIONS": {'
            '        "WatchList1": {'
            '            "filename": r".*MyFileName\\.cpp",'
            '        },'
            '        "WatchList2": {'
            '            "filename": r".*MyFileName\\.h",'
            '        },'
            '        "WatchList3": {'
            '            "filename": r".*MyFileName\\.o",'
            '        },'
            '     },'
            '    "CC_RULES": {'
            '        "!WatchList1&!WatchList2|!WatchList3&!WatchListUndefined": ["*****@*****.**"]'
            '     },'
            '    "MESSAGE_RULES": {'
            '        "!WatchList1|WatchList2&!WatchList3|!WatchListUndefined": ["*****@*****.**"]'
            '     },'
            '}')

        with OutputCapture(level=logging.INFO) as captured:
            self._watch_list_parser.parse(watch_list)
        self.assertEqual(
            captured.root.log.getvalue(),
            '''In section "CC_RULES", the following definitions are not used and should be removed: WatchListUndefined

Perhaps it should be WatchList3 or WatchList2 or WatchList1.
In section "MESSAGE_RULES", the following definitions are not used and should be removed: WatchListUndefined

Perhaps it should be WatchList3 or WatchList2 or WatchList1.
''',
        )