def test_assert_valid_rule_class_negative(self): # general test to make sure that incorrect rules will raise an exception user_rule_path = self.get_sample_path("user_rules/incorrect_linerule") with self.assertRaisesMessage( UserRuleError, "User-defined rule class 'MyUserLineRule' must have a 'validate' method" ): find_rule_classes(user_rule_path)
def test_failed_module_import(self): # test importing a bogus module user_rule_path = self.get_sample_path("user_rules/import_exception") # We don't check the entire error message because that is different based on the python version and underlying # operating system expected_msg = "Error while importing extra-path module 'invalid_python'" with self.assertRaisesRegex(UserRuleError, expected_msg): find_rule_classes(user_rule_path)
def contrib(self, value): try: self._contrib.set(value) # Make sure we unload any previously loaded contrib rules when re-setting the value self.rules.delete_rules_by_attr("is_contrib", True) # Load all classes from the contrib directory contrib_dir_path = os.path.dirname( os.path.realpath(contrib_rules.__file__)) rule_classes = rule_finder.find_rule_classes(contrib_dir_path) # For each specified contrib rule, check whether it exists among the contrib classes for rule_id_or_name in self.contrib: rule_class = next((rc for rc in rule_classes if rule_id_or_name in (rc.id, rc.name)), False) # If contrib rule exists, instantiate it and add it to the rules list if rule_class: self.rules.add_rule(rule_class, rule_class.id, {'is_contrib': True}) else: raise LintConfigError( f"No contrib rule with id or name '{rule_id_or_name}' found." ) except (options.RuleOptionError, rules.UserRuleError) as e: raise LintConfigError(str(e)) from e
def contrib(self, value): try: self._contrib.set(value) # Make sure we unload any previously loaded contrib rules when re-setting the value for rule in self.rules: if hasattr(rule, 'is_contrib') and rule.is_contrib: del self._rules[rule.id] # Load all classes from the contrib directory contrib_dir_path = os.path.dirname(os.path.realpath(contrib_rules.__file__)) rule_classes = rule_finder.find_rule_classes(contrib_dir_path) # For each specified contrib rule, check whether it exists among the contrib classes for rule_id_or_name in self.contrib: rule_class = next((rc for rc in rule_classes if rc.id == ustr(rule_id_or_name) or rc.name == ustr(rule_id_or_name)), False) # If contrib rule exists, instantiate it and add it to the rules list if rule_class: rule_obj = rule_class() rule_obj.is_contrib = True self._rules[rule_class.id] = rule_obj else: raise LintConfigError(u"No contrib rule with id or name '{0}' found.".format(ustr(rule_id_or_name))) except (options.RuleOptionError, rules.UserRuleError) as e: raise LintConfigError(ustr(e))
def test_contrib_rule_instantiated(self): """ Tests that all contrib rules can be instantiated without errors. """ rule_classes = rule_finder.find_rule_classes(self.CONTRIB_DIR) # No exceptions = what we want :-) for rule_class in rule_classes: rule_class()
def extra_path(self, value): try: if self.extra_path: self._extra_path.set(value) else: self._extra_path = options.PathOption( 'extra-path', value, "Path to a directory or module with extra user-defined rules", type='both' ) # Make sure we unload any previously loaded extra-path rules for rule in self.rules: if hasattr(rule, 'is_user_defined') and rule.is_user_defined: del self._rules[rule.id] # Find rules in the new extra-path rule_classes = rule_finder.find_rule_classes(self.extra_path) # Add the newly found rules to the existing rules for rule_class in rule_classes: rule_obj = rule_class() rule_obj.is_user_defined = True self._rules[rule_class.id] = rule_obj except (options.RuleOptionError, rules.UserRuleError) as e: raise LintConfigError(ustr(e))
def test_empty_user_classes(self): # Test that we don't find rules if we scan a different directory user_rule_path = self.get_sample_path("config") classes = find_rule_classes(user_rule_path) self.assertListEqual(classes, []) # Importantly, ensure that the directory is not added to the syspath as this happens only when we actually # find modules self.assertNotIn(user_rule_path, sys.path)
def test_extra_path_specified_by_file(self): # Test that find_rule_classes can handle an extra path given as a file name instead of a directory user_rule_path = self.get_sample_path("user_rules") user_rule_module = os.path.join(user_rule_path, "my_commit_rules.py") classes = find_rule_classes(user_rule_module) rule_class = classes[0]() violations = rule_class.validate("false-commit-object (ignored)") self.assertListEqual(violations, [rules.RuleViolation("UC1", u"Commit violåtion 1", u"Contënt 1", 1)])
def test_rules_from_init_file(self): # Test that we can import rules that are defined in __init__.py files # This also tests that we can import rules from python packages. This use to cause issues with pypy # So this is also a regression test for that. user_rule_path = self.get_sample_path(os.path.join("user_rules", "parent_package")) classes = find_rule_classes(user_rule_path) # convert classes to strings and sort them so we can compare them class_strings = sorted([ustr(clazz) for clazz in classes]) expected = [u"<class 'my_commit_rules.MyUserCommitRule'>", u"<class 'parent_package.InitFileRule'>"] self.assertListEqual(class_strings, expected)
def test_contrib_rule_uniqueness(self): """ Tests that all contrib rules have unique identifiers. We can test for this at test time (and not during runtime like rule_finder.assert_valid_rule_class does) because these are contrib rules: once they're part of gitlint they can't change unless they pass this test again. """ rule_classes = rule_finder.find_rule_classes(self.CONTRIB_DIR) # Not very efficient way of checking uniqueness, but it works :-) class_names = [rule_class.name for rule_class in rule_classes] class_ids = [rule_class.id for rule_class in rule_classes] self.assertEqual(len(set(class_names)), len(class_names)) self.assertEqual(len(set(class_ids)), len(class_ids))
def test_contrib_rule_naming_conventions(self): """ Tests that contrib rules follow certain naming conventions. We can test for this at test time (and not during runtime like rule_finder.assert_valid_rule_class does) because these are contrib rules: once they're part of gitlint they can't change unless they pass this test again. """ rule_classes = rule_finder.find_rule_classes(self.CONTRIB_DIR) for clazz in rule_classes: # Contrib rule names start with "contrib-" self.assertTrue(clazz.name.startswith("contrib-")) # Contrib line rules id's start with "CL" if issubclass(clazz, rules.LineRule): if clazz.target == rules.CommitMessageTitle: self.assertTrue(clazz.id.startswith("CT")) elif clazz.target == rules.CommitMessageBody: self.assertTrue(clazz.id.startswith("CB"))
def test_find_rule_classes(self): # Let's find some user classes! user_rule_path = self.get_sample_path("user_rules") classes = find_rule_classes(user_rule_path) # Compare string representations because we can't import MyUserCommitRule here since samples/user_rules is not # a proper python package # Note that the following check effectively asserts that: # - There is only 1 rule recognized and it is MyUserCommitRule # - Other non-python files in the directory are ignored # - Other members of the my_commit_rules module are ignored # (such as func_should_be_ignored, global_variable_should_be_ignored) # - Rules are loaded non-recursively (user_rules/import_exception directory is ignored) self.assertEqual("[<class 'my_commit_rules.MyUserCommitRule'>]", str(classes)) # Assert that we added the new user_rules directory to the system path and modules self.assertIn(user_rule_path, sys.path) self.assertIn("my_commit_rules", sys.modules) # Do some basic asserts on our user rule self.assertEqual(classes[0].id, "UC1") self.assertEqual(classes[0].name, "my-üser-commit-rule") expected_option = options.IntOption('violation-count', 1, "Number of violåtions to return") self.assertListEqual(classes[0].options_spec, [expected_option]) self.assertTrue(hasattr(classes[0], "validate")) # Test that we can instantiate the class and can execute run the validate method and that it returns the # expected result rule_class = classes[0]() violations = rule_class.validate("false-commit-object (ignored)") self.assertListEqual( violations, [rules.RuleViolation("UC1", "Commit violåtion 1", "Contënt 1", 1)]) # Have it return more violations rule_class.options['violation-count'].value = 2 violations = rule_class.validate("false-commit-object (ignored)") self.assertListEqual(violations, [ rules.RuleViolation("UC1", "Commit violåtion 1", "Contënt 1", 1), rules.RuleViolation("UC1", "Commit violåtion 2", "Contënt 2", 2) ])
def extra_path(self, value): try: if self.extra_path: self._extra_path.set(value) else: self._extra_path = options.PathOption( 'extra-path', value, "Path to a directory or module with extra user-defined rules", type='both') # Make sure we unload any previously loaded extra-path rules self.rules.delete_rules_by_attr("is_user_defined", True) # Find rules in the new extra-path and add them to the existing rules rule_classes = rule_finder.find_rule_classes(self.extra_path) self.rules.add_rules(rule_classes, {'is_user_defined': True}) except (options.RuleOptionError, rules.UserRuleError) as e: raise LintConfigError(str(e)) from e
def test_find_rule_classes_nonexisting_path(self): with self.assertRaisesMessage(UserRuleError, "Invalid extra-path: föo/bar"): find_rule_classes("föo/bar")
def test_find_rule_classes_nonexisting_path(self): with self.assertRaisesRegex(UserRuleError, u"Invalid extra-path: föo/bar"): find_rule_classes(u"föo/bar")