summaryrefslogtreecommitdiffstats
path: root/gitlint-core/gitlint/tests
diff options
context:
space:
mode:
Diffstat (limited to 'gitlint-core/gitlint/tests')
-rw-r--r--gitlint-core/gitlint/tests/base.py81
-rw-r--r--gitlint-core/gitlint/tests/cli/test_cli.py444
-rw-r--r--gitlint-core/gitlint/tests/cli/test_cli_hooks.py157
-rw-r--r--gitlint-core/gitlint/tests/config/test_config.py109
-rw-r--r--gitlint-core/gitlint/tests/config/test_config_builder.py88
-rw-r--r--gitlint-core/gitlint/tests/config/test_config_precedence.py46
-rw-r--r--gitlint-core/gitlint/tests/config/test_rule_collection.py9
-rw-r--r--gitlint-core/gitlint/tests/contrib/rules/test_authors_commit.py106
-rw-r--r--gitlint-core/gitlint/tests/contrib/rules/test_conventional_commit.py39
-rw-r--r--gitlint-core/gitlint/tests/contrib/rules/test_disallow_cleanup_commits.py35
-rw-r--r--gitlint-core/gitlint/tests/contrib/rules/test_signedoff_by.py5
-rw-r--r--gitlint-core/gitlint/tests/contrib/test_contrib_rules.py34
-rw-r--r--gitlint-core/gitlint/tests/expected/cli/test_cli/test_debug_125
-rw-r--r--gitlint-core/gitlint/tests/expected/cli/test_cli/test_input_stream_debug_27
-rw-r--r--gitlint-core/gitlint/tests/expected/cli/test_cli/test_lint_staged_msg_filename_211
-rw-r--r--gitlint-core/gitlint/tests/expected/cli/test_cli/test_lint_staged_stdin_211
-rw-r--r--gitlint-core/gitlint/tests/expected/cli/test_cli/test_named_rules_27
-rw-r--r--gitlint-core/gitlint/tests/git/test_git.py65
-rw-r--r--gitlint-core/gitlint/tests/git/test_git_commit.py460
-rw-r--r--gitlint-core/gitlint/tests/git/test_git_context.py41
-rw-r--r--gitlint-core/gitlint/tests/rules/test_body_rules.py27
-rw-r--r--gitlint-core/gitlint/tests/rules/test_configuration_rules.py66
-rw-r--r--gitlint-core/gitlint/tests/rules/test_meta_rules.py48
-rw-r--r--gitlint-core/gitlint/tests/rules/test_rules.py2
-rw-r--r--gitlint-core/gitlint/tests/rules/test_title_rules.py56
-rw-r--r--gitlint-core/gitlint/tests/rules/test_user_rules.py69
-rw-r--r--gitlint-core/gitlint/tests/samples/commit_message/fixup_amend1
-rw-r--r--gitlint-core/gitlint/tests/samples/config/AUTHORS2
-rw-r--r--gitlint-core/gitlint/tests/samples/user_rules/import_exception/invalid_python.py1
-rw-r--r--gitlint-core/gitlint/tests/samples/user_rules/incorrect_linerule/my_line_rule.py2
-rw-r--r--gitlint-core/gitlint/tests/samples/user_rules/my_commit_rules.py7
-rw-r--r--gitlint-core/gitlint/tests/samples/user_rules/parent_package/__init__.py1
-rw-r--r--gitlint-core/gitlint/tests/samples/user_rules/parent_package/my_commit_rules.py2
-rw-r--r--gitlint-core/gitlint/tests/test_cache.py4
-rw-r--r--gitlint-core/gitlint/tests/test_deprecation.py23
-rw-r--r--gitlint-core/gitlint/tests/test_display.py22
-rw-r--r--gitlint-core/gitlint/tests/test_hooks.py68
-rw-r--r--gitlint-core/gitlint/tests/test_lint.py188
-rw-r--r--gitlint-core/gitlint/tests/test_options.py15
-rw-r--r--gitlint-core/gitlint/tests/test_utils.py15
40 files changed, 1518 insertions, 881 deletions
diff --git a/gitlint-core/gitlint/tests/base.py b/gitlint-core/gitlint/tests/base.py
index 9d2d165..710efe2 100644
--- a/gitlint-core/gitlint/tests/base.py
+++ b/gitlint-core/gitlint/tests/base.py
@@ -1,8 +1,5 @@
-# -*- coding: utf-8 -*-
-
import contextlib
import copy
-import io
import logging
import os
import re
@@ -13,12 +10,22 @@ import unittest
from unittest.mock import patch
-from gitlint.git import GitContext
+from gitlint.config import LintConfig
+from gitlint.deprecation import Deprecation, LOG as DEPRECATION_LOG
+from gitlint.git import GitContext, GitChangedFileStats
from gitlint.utils import LOG_FORMAT, DEFAULT_ENCODING
+EXPECTED_REGEX_STYLE_SEARCH_DEPRECATION_WARNING = (
+ "WARNING: gitlint.deprecated.regex_style_search {0} - {1}: gitlint will be switching from using "
+ "Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. "
+ "Please review your {1}.regex option accordingly. "
+ "To remove this warning, set general.regex-style-search=True. More details: "
+ "https://jorisroovers.github.io/gitlint/configuration/#regex-style-search"
+)
+
class BaseTestCase(unittest.TestCase):
- """ Base class of which all gitlint unit test classes are derived. Provides a number of convenience methods. """
+ """Base class of which all gitlint unit test classes are derived. Provides a number of convenience methods."""
# In case of assert failures, print the full error message
maxDiff = None
@@ -30,13 +37,24 @@ class BaseTestCase(unittest.TestCase):
def setUp(self):
self.logcapture = LogCapture()
self.logcapture.setFormatter(logging.Formatter(LOG_FORMAT))
- logging.getLogger('gitlint').setLevel(logging.DEBUG)
- logging.getLogger('gitlint').handlers = [self.logcapture]
+ logging.getLogger("gitlint").setLevel(logging.DEBUG)
+ logging.getLogger("gitlint").handlers = [self.logcapture]
+ DEPRECATION_LOG.handlers = [self.logcapture]
# Make sure we don't propagate anything to child loggers, we need to do this explicitly here
# because if you run a specific test file like test_lint.py, we won't be calling the setupLogging() method
# in gitlint.cli that normally takes care of this
- logging.getLogger('gitlint').propagate = False
+ # Example test where this matters (for DEPRECATION_LOG):
+ # gitlint-core/gitlint/tests/rules/test_configuration_rules.py::ConfigurationRuleTests::test_ignore_by_title
+ logging.getLogger("gitlint").propagate = False
+ DEPRECATION_LOG.propagate = False
+
+ # Make sure Deprecation has a clean config set at the start of each test.
+ # Tests that want to specifically test deprecation should override this.
+ Deprecation.config = LintConfig()
+ # Normally Deprecation only logs messages once per process.
+ # For tests we want to log every time, so we reset the warning_msgs set per test.
+ Deprecation.warning_msgs = set()
@staticmethod
@contextlib.contextmanager
@@ -57,25 +75,25 @@ class BaseTestCase(unittest.TestCase):
@staticmethod
def get_sample(filename=""):
- """ Read and return the contents of a file in gitlint/tests/samples """
+ """Read and return the contents of a file in gitlint/tests/samples"""
sample_path = BaseTestCase.get_sample_path(filename)
- with io.open(sample_path, encoding=DEFAULT_ENCODING) as content:
+ with open(sample_path, encoding=DEFAULT_ENCODING) as content:
sample = content.read()
return sample
@staticmethod
def patch_input(side_effect):
- """ Patches the built-in input() with a provided side-effect """
+ """Patches the built-in input() with a provided side-effect"""
module_path = "builtins.input"
patched_module = patch(module_path, side_effect=side_effect)
return patched_module
@staticmethod
def get_expected(filename="", variable_dict=None):
- """ Utility method to read an expected file from gitlint/tests/expected and return it as a string.
- Optionally replace template variables specified by variable_dict. """
+ """Utility method to read an expected file from gitlint/tests/expected and return it as a string.
+ Optionally replace template variables specified by variable_dict."""
expected_path = os.path.join(BaseTestCase.EXPECTED_DIR, filename)
- with io.open(expected_path, encoding=DEFAULT_ENCODING) as content:
+ with open(expected_path, encoding=DEFAULT_ENCODING) as content:
expected = content.read()
if variable_dict:
@@ -87,20 +105,21 @@ class BaseTestCase(unittest.TestCase):
return os.path.join(BaseTestCase.SAMPLES_DIR, "user_rules")
@staticmethod
- def gitcontext(commit_msg_str, changed_files=None, ):
- """ Utility method to easily create gitcontext objects based on a given commit msg string and an optional set of
+ def gitcontext(commit_msg_str, changed_files=None):
+ """Utility method to easily create gitcontext objects based on a given commit msg string and an optional set of
changed files"""
with patch("gitlint.git.git_commentchar") as comment_char:
comment_char.return_value = "#"
gitcontext = GitContext.from_commit_msg(commit_msg_str)
commit = gitcontext.commits[-1]
if changed_files:
- commit.changed_files = changed_files
+ changed_file_stats = {filename: GitChangedFileStats(filename, 8, 3) for filename in changed_files}
+ commit.changed_files_stats = changed_file_stats
return gitcontext
@staticmethod
def gitcommit(commit_msg_str, changed_files=None, **kwargs):
- """ Utility method to easily create git commit given a commit msg string and an optional set of changed files"""
+ """Utility method to easily create git commit given a commit msg string and an optional set of changed files"""
gitcontext = BaseTestCase.gitcontext(commit_msg_str, changed_files)
commit = gitcontext.commits[-1]
for attr, value in kwargs.items():
@@ -108,31 +127,31 @@ class BaseTestCase(unittest.TestCase):
return commit
def assert_logged(self, expected):
- """ Asserts that the logs match an expected string or list.
- This method knows how to compare a passed list of log lines as well as a newline concatenated string
- of all loglines. """
+ """Asserts that the logs match an expected string or list.
+ This method knows how to compare a passed list of log lines as well as a newline concatenated string
+ of all loglines."""
if isinstance(expected, list):
self.assertListEqual(self.logcapture.messages, expected)
else:
self.assertEqual("\n".join(self.logcapture.messages), expected)
def assert_log_contains(self, line):
- """ Asserts that a certain line is in the logs """
+ """Asserts that a certain line is in the logs"""
self.assertIn(line, self.logcapture.messages)
def assertRaisesRegex(self, expected_exception, expected_regex, *args, **kwargs):
- """ Pass-through method to unittest.TestCase.assertRaisesRegex that applies re.escape() to the passed
- `expected_regex`. This is useful to automatically escape all file paths that might be present in the regex.
+ """Pass-through method to unittest.TestCase.assertRaisesRegex that applies re.escape() to the passed
+ `expected_regex`. This is useful to automatically escape all file paths that might be present in the regex.
"""
return super().assertRaisesRegex(expected_exception, re.escape(expected_regex), *args, **kwargs)
def clearlog(self):
- """ Clears the log capture """
+ """Clears the log capture"""
self.logcapture.clear()
@contextlib.contextmanager
def assertRaisesMessage(self, expected_exception, expected_msg): # pylint: disable=invalid-name
- """ Asserts an exception has occurred with a given error message """
+ """Asserts an exception has occurred with a given error message"""
try:
yield
except expected_exception as exc:
@@ -149,10 +168,10 @@ class BaseTestCase(unittest.TestCase):
raise self.fail(f"Expected to raise {expected_exception.__name__}, didn't get an exception at all")
def object_equality_test(self, obj, attr_list, ctor_kwargs=None):
- """ Helper function to easily implement object equality tests.
- Creates an object clone for every passed attribute and checks for (in)equality
- of the original object with the clone based on those attributes' values.
- This function assumes all attributes in `attr_list` can be passed to the ctor of `obj.__class__`.
+ """Helper function to easily implement object equality tests.
+ Creates an object clone for every passed attribute and checks for (in)equality
+ of the original object with the clone based on those attributes' values.
+ This function assumes all attributes in `attr_list` can be passed to the ctor of `obj.__class__`.
"""
if not ctor_kwargs:
ctor_kwargs = {}
@@ -178,7 +197,7 @@ class BaseTestCase(unittest.TestCase):
class LogCapture(logging.Handler):
- """ Mock logging handler used to capture any log messages during tests."""
+ """Mock logging handler used to capture any log messages during tests."""
def __init__(self, *args, **kwargs):
logging.Handler.__init__(self, *args, **kwargs)
diff --git a/gitlint-core/gitlint/tests/cli/test_cli.py b/gitlint-core/gitlint/tests/cli/test_cli.py
index 59ec7af..d18efe9 100644
--- a/gitlint-core/gitlint/tests/cli/test_cli.py
+++ b/gitlint-core/gitlint/tests/cli/test_cli.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-
-
import io
import os
import sys
@@ -29,11 +26,11 @@ class CLITests(BaseTestCase):
GITLINT_SUCCESS_CODE = 0
def setUp(self):
- super(CLITests, self).setUp()
+ super().setUp()
self.cli = CliRunner()
# Patch gitlint.cli.git_version() so that we don't have to patch it separately in every test
- self.git_version_path = patch('gitlint.cli.git_version')
+ self.git_version_path = patch("gitlint.cli.git_version")
cli.git_version = self.git_version_path.start()
cli.git_version.return_value = "git version 1.2.3"
@@ -42,39 +39,44 @@ class CLITests(BaseTestCase):
@staticmethod
def get_system_info_dict():
- """ Returns a dict with items related to system values logged by `gitlint --debug` """
- return {'platform': platform.platform(), "python_version": sys.version, 'gitlint_version': __version__,
- 'GITLINT_USE_SH_LIB': BaseTestCase.GITLINT_USE_SH_LIB, 'target': os.path.realpath(os.getcwd()),
- 'DEFAULT_ENCODING': DEFAULT_ENCODING}
+ """Returns a dict with items related to system values logged by `gitlint --debug`"""
+ return {
+ "platform": platform.platform(),
+ "python_version": sys.version,
+ "gitlint_version": __version__,
+ "GITLINT_USE_SH_LIB": BaseTestCase.GITLINT_USE_SH_LIB,
+ "target": os.path.realpath(os.getcwd()),
+ "DEFAULT_ENCODING": DEFAULT_ENCODING,
+ }
def test_version(self):
- """ Test for --version option """
+ """Test for --version option"""
result = self.cli.invoke(cli.cli, ["--version"])
self.assertEqual(result.output.split("\n")[0], f"cli, version {__version__}")
- @patch('gitlint.cli.get_stdin_data', return_value=False)
- @patch('gitlint.git.sh')
+ @patch("gitlint.cli.get_stdin_data", return_value=False)
+ @patch("gitlint.git.sh")
def test_lint(self, sh, _):
- """ Test for basic simple linting functionality """
+ """Test for basic simple linting functionality"""
sh.git.side_effect = [
"6f29bf81a8322a04071bb794666e48c443a90360",
- "test åuthor\x00test-email@föo.com\x002016-12-03 15:28:15 +0100\x00åbc\n"
- "commït-title\n\ncommït-body",
+ "test åuthor\x00test-email@föo.com\x002016-12-03 15:28:15 +0100\x00åbc\ncommït-title\n\ncommït-body",
"#", # git config --get core.commentchar
+ "1\t4\tfile1.txt\n3\t5\tpåth/to/file2.txt\n",
"commit-1-branch-1\ncommit-1-branch-2\n",
- "file1.txt\npåth/to/file2.txt\n"
]
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli)
- self.assertEqual(stderr.getvalue(), u'3: B5 Body message is too short (11<20): "commït-body"\n')
+ self.assertEqual(stderr.getvalue(), '3: B5 Body message is too short (11<20): "commït-body"\n')
self.assertEqual(result.exit_code, 1)
- @patch('gitlint.cli.get_stdin_data', return_value=False)
- @patch('gitlint.git.sh')
+ @patch("gitlint.cli.get_stdin_data", return_value=False)
+ @patch("gitlint.git.sh")
def test_lint_multiple_commits(self, sh, _):
- """ Test for --commits option """
+ """Test for --commits option"""
+ # fmt: off
sh.git.side_effect = [
"6f29bf81a8322a04071bb794666e48c443a90360\n" + # git rev-list <SHA>
"25053ccec5e28e1bb8f7551fdbb5ab213ada2401\n" +
@@ -83,30 +85,32 @@ class CLITests(BaseTestCase):
"test åuthor1\x00test-email1@föo.com\x002016-12-03 15:28:15 +0100\x00åbc\n"
"commït-title1\n\ncommït-body1",
"#", # git config --get core.commentchar
+ "3\t5\tcommit-1/file-1\n1\t4\tcommit-1/file-2\n", # git diff-tree
"commit-1-branch-1\ncommit-1-branch-2\n", # git branch --contains <sha>
- "commit-1/file-1\ncommit-1/file-2\n", # git diff-tree
# git log --pretty <FORMAT> <SHA>
"test åuthor2\x00test-email3@föo.com\x002016-12-04 15:28:15 +0100\x00åbc\n"
"commït-title2\n\ncommït-body2",
+ "8\t3\tcommit-2/file-1\n1\t5\tcommit-2/file-2\n", # git diff-tree
"commit-2-branch-1\ncommit-2-branch-2\n", # git branch --contains <sha>
- "commit-2/file-1\ncommit-2/file-2\n", # git diff-tree
# git log --pretty <FORMAT> <SHA>
"test åuthor3\x00test-email3@föo.com\x002016-12-05 15:28:15 +0100\x00åbc\n"
"commït-title3\n\ncommït-body3",
+ "7\t2\tcommit-3/file-1\n1\t7\tcommit-3/file-2\n", # git diff-tree
"commit-3-branch-1\ncommit-3-branch-2\n", # git branch --contains <sha>
- "commit-3/file-1\ncommit-3/file-2\n", # git diff-tree
]
+ # fmt: on
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["--commits", "foo...bar"])
self.assertEqual(stderr.getvalue(), self.get_expected("cli/test_cli/test_lint_multiple_commits_1"))
self.assertEqual(result.exit_code, 3)
- @patch('gitlint.cli.get_stdin_data', return_value=False)
- @patch('gitlint.git.sh')
+ @patch("gitlint.cli.get_stdin_data", return_value=False)
+ @patch("gitlint.git.sh")
def test_lint_multiple_commits_config(self, sh, _):
- """ Test for --commits option where some of the commits have gitlint config in the commit message """
+ """Test for --commits option where some of the commits have gitlint config in the commit message"""
+ # fmt: off
# Note that the second commit title has a trailing period that is being ignored by gitlint-ignore: T3
sh.git.side_effect = [
"6f29bf81a8322a04071bb794666e48c443a90360\n" + # git rev-list <SHA>
@@ -116,32 +120,33 @@ class CLITests(BaseTestCase):
"test åuthor1\x00test-email1@föo.com\x002016-12-03 15:28:15 +0100\x00åbc\n"
"commït-title1\n\ncommït-body1",
"#", # git config --get core.commentchar
+ "9\t4\tcommit-1/file-1\n0\t2\tcommit-1/file-2\n", # git diff-tree
"commit-1-branch-1\ncommit-1-branch-2\n", # git branch --contains <sha>
- "commit-1/file-1\ncommit-1/file-2\n", # git diff-tree
# git log --pretty <FORMAT> <SHA>
"test åuthor2\x00test-email2@föo.com\x002016-12-04 15:28:15 +0100\x00åbc\n"
"commït-title2.\n\ncommït-body2\ngitlint-ignore: T3\n",
+ "3\t7\tcommit-2/file-1\n4\t6\tcommit-2/file-2\n", # git diff-tree
"commit-2-branch-1\ncommit-2-branch-2\n", # git branch --contains <sha>
- "commit-2/file-1\ncommit-2/file-2\n", # git diff-tree
# git log --pretty <FORMAT> <SHA>
"test åuthor3\x00test-email3@föo.com\x002016-12-05 15:28:15 +0100\x00åbc\n"
"commït-title3.\n\ncommït-body3",
+ "3\t8\tcommit-3/file-1\n1\t4\tcommit-3/file-2\n", # git diff-tree
"commit-3-branch-1\ncommit-3-branch-2\n", # git branch --contains <sha>
- "commit-3/file-1\ncommit-3/file-2\n", # git diff-tree
]
+ # fmt: on
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["--commits", "foo...bar"])
# We expect that the second commit has no failures because of 'gitlint-ignore: T3' in its commit msg body
self.assertEqual(stderr.getvalue(), self.get_expected("cli/test_cli/test_lint_multiple_commits_config_1"))
self.assertEqual(result.exit_code, 3)
- @patch('gitlint.cli.get_stdin_data', return_value=False)
- @patch('gitlint.git.sh')
+ @patch("gitlint.cli.get_stdin_data", return_value=False)
+ @patch("gitlint.git.sh")
def test_lint_multiple_commits_configuration_rules(self, sh, _):
- """ Test for --commits option where where we have configured gitlint to ignore certain rules for certain commits
- """
+ """Test for --commits option where where we have configured gitlint to ignore certain rules for certain commits"""
+ # fmt: off
# Note that the second commit
sh.git.side_effect = [
"6f29bf81a8322a04071bb794666e48c443a90360\n" + # git rev-list <SHA>
@@ -151,62 +156,78 @@ class CLITests(BaseTestCase):
"test åuthor1\x00test-email1@föo.com\x002016-12-03 15:28:15 +0100\x00åbc\n"
"commït-title1\n\ncommït-body1",
"#", # git config --get core.commentchar
+ "5\t9\tcommit-1/file-1\n1\t4\tcommit-1/file-2\n", # git diff-tree
"commit-1-branch-1\ncommit-1-branch-2\n", # git branch --contains <sha>
- "commit-1/file-1\ncommit-1/file-2\n", # git diff-tree
# git log --pretty <FORMAT> <SHA>
"test åuthor2\x00test-email3@föo.com\x002016-12-04 15:28:15 +0100\x00åbc\n"
# Normally T3 violation (trailing punctuation), but this commit is ignored because of
# config below
"commït-title2.\n\ncommït-body2\n",
+ "4\t7\tcommit-2/file-1\n1\t4\tcommit-2/file-2\n", # git diff-tree
"commit-2-branch-1\ncommit-2-branch-2\n", # git branch --contains <sha>
- "commit-2/file-1\ncommit-2/file-2\n", # git diff-tree
# git log --pretty <FORMAT> <SHA>
"test åuthor3\x00test-email3@föo.com\x002016-12-05 15:28:15 +0100\x00åbc\n"
# Normally T1 and B5 violations, now only T1 because we're ignoring B5 in config below
"commït-title3.\n\ncommït-body3 foo",
+ "1\t9\tcommit-3/file-1\n3\t7\tcommit-3/file-2\n", # git diff-tree
"commit-3-branch-1\ncommit-3-branch-2\n", # git branch --contains <sha>
- "commit-3/file-1\ncommit-3/file-2\n", # git diff-tree
]
-
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
- result = self.cli.invoke(cli.cli, ["--commits", "foo...bar", "-c", "I1.regex=^commït-title2(.*)",
- "-c", "I2.regex=^commït-body3(.*)", "-c", "I2.ignore=B5"])
+ # fmt: on
+
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
+ result = self.cli.invoke(
+ cli.cli,
+ [
+ "--commits",
+ "foo...bar",
+ "-c",
+ "I1.regex=^commït-title2(.*)",
+ "-c",
+ "I2.regex=^commït-body3(.*)",
+ "-c",
+ "I2.ignore=B5",
+ ],
+ )
# We expect that the second commit has no failures because of it matching against I1.regex
# Because we do test for the 3th commit to return violations, this test also ensures that a unique
# config object is passed to each commit lint call
- expected = ("Commit 6f29bf81a8:\n"
- u'3: B5 Body message is too short (12<20): "commït-body1"\n\n'
- "Commit 4da2656b0d:\n"
- u'1: T3 Title has trailing punctuation (.): "commït-title3."\n')
+ expected = (
+ "Commit 6f29bf81a8:\n"
+ '3: B5 Body message is too short (12<20): "commït-body1"\n\n'
+ "Commit 4da2656b0d:\n"
+ '1: T3 Title has trailing punctuation (.): "commït-title3."\n'
+ )
self.assertEqual(stderr.getvalue(), expected)
self.assertEqual(result.exit_code, 2)
- @patch('gitlint.cli.get_stdin_data', return_value=False)
- @patch('gitlint.git.sh')
+ @patch("gitlint.cli.get_stdin_data", return_value=False)
+ @patch("gitlint.git.sh")
def test_lint_commit(self, sh, _):
- """ Test for --commit option """
+ """Test for --commit option"""
+ # fmt: off
sh.git.side_effect = [
"6f29bf81a8322a04071bb794666e48c443a90360\n", # git log -1 <SHA> --pretty=%H
# git log --pretty <FORMAT> <SHA>
"test åuthor1\x00test-email1@föo.com\x002016-12-03 15:28:15 +0100\x00åbc\n"
"WIP: commït-title1\n\ncommït-body1",
"#", # git config --get core.commentchar
+ "4\t5\tcommit-1/file-1\n1\t4\tcommit-1/file-2\n", # git diff-tree
"commit-1-branch-1\ncommit-1-branch-2\n", # git branch --contains <sha>
- "commit-1/file-1\ncommit-1/file-2\n", # git diff-tree
]
+ # fmt: on
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["--commit", "foo"])
self.assertEqual(result.output, "")
self.assertEqual(stderr.getvalue(), self.get_expected("cli/test_cli/test_lint_commit_1"))
self.assertEqual(result.exit_code, 2)
- @patch('gitlint.cli.get_stdin_data', return_value=False)
- @patch('gitlint.git.sh')
+ @patch("gitlint.cli.get_stdin_data", return_value=False)
+ @patch("gitlint.git.sh")
def test_lint_commit_negative(self, sh, _):
- """ Negative test for --commit option """
+ """Negative test for --commit option"""
# Try using --commit and --commits at the same time (not allowed)
result = self.cli.invoke(cli.cli, ["--commit", "foo", "--commits", "foo...bar"])
@@ -214,275 +235,309 @@ class CLITests(BaseTestCase):
self.assertEqual(result.output, expected_output)
self.assertEqual(result.exit_code, self.USAGE_ERROR_CODE)
- @patch('gitlint.cli.get_stdin_data', return_value=u'WIP: tïtle \n')
+ @patch("gitlint.cli.get_stdin_data", return_value="WIP: tïtle \n")
def test_input_stream(self, _):
- """ Test for linting when a message is passed via stdin """
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ """Test for linting when a message is passed via stdin"""
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli)
self.assertEqual(stderr.getvalue(), self.get_expected("cli/test_cli/test_input_stream_1"))
self.assertEqual(result.exit_code, 3)
self.assertEqual(result.output, "")
- @patch('gitlint.cli.get_stdin_data', return_value=u'WIP: tïtle \n')
+ @patch("gitlint.cli.get_stdin_data", return_value="WIP: tïtle \n")
def test_input_stream_debug(self, _):
- """ Test for linting when a message is passed via stdin, and debug is enabled.
- This tests specifically that git commit meta is not fetched when not passing --staged """
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ """Test for linting when a message is passed via stdin, and debug is enabled.
+ This tests specifically that git commit meta is not fetched when not passing --staged"""
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["--debug"])
self.assertEqual(stderr.getvalue(), self.get_expected("cli/test_cli/test_input_stream_debug_1"))
self.assertEqual(result.exit_code, 3)
self.assertEqual(result.output, "")
expected_kwargs = self.get_system_info_dict()
- expected_logs = self.get_expected('cli/test_cli/test_input_stream_debug_2', expected_kwargs)
+ expected_logs = self.get_expected("cli/test_cli/test_input_stream_debug_2", expected_kwargs)
self.assert_logged(expected_logs)
- @patch('gitlint.cli.get_stdin_data', return_value="Should be ignored\n")
- @patch('gitlint.git.sh')
+ @patch("gitlint.cli.get_stdin_data", return_value="Should be ignored\n")
+ @patch("gitlint.git.sh")
def test_lint_ignore_stdin(self, sh, stdin_data):
- """ Test for ignoring stdin when --ignore-stdin flag is enabled"""
+ """Test for ignoring stdin when --ignore-stdin flag is enabled"""
sh.git.side_effect = [
"6f29bf81a8322a04071bb794666e48c443a90360",
- "test åuthor\x00test-email@föo.com\x002016-12-03 15:28:15 +0100\x00åbc\n"
- "commït-title\n\ncommït-body",
- "#", # git config --get core.commentchar
+ "test åuthor\x00test-email@föo.com\x002016-12-03 15:28:15 +0100\x00åbc\ncommït-title\n\ncommït-body",
+ "#", # git config --get core.commentchar
+ "3\t12\tfile1.txt\n8\t5\tpåth/to/file2.txt\n", # git diff-tree
"commit-1-branch-1\ncommit-1-branch-2\n", # git branch --contains <sha>
- "file1.txt\npåth/to/file2.txt\n" # git diff-tree
]
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["--ignore-stdin"])
- self.assertEqual(stderr.getvalue(), u'3: B5 Body message is too short (11<20): "commït-body"\n')
+ self.assertEqual(stderr.getvalue(), '3: B5 Body message is too short (11<20): "commït-body"\n')
self.assertEqual(result.exit_code, 1)
# Assert that we didn't even try to get the stdin data
self.assertEqual(stdin_data.call_count, 0)
- @patch('gitlint.cli.get_stdin_data', return_value=u'WIP: tïtle \n')
- @patch('arrow.now', return_value=arrow.get("2020-02-19T12:18:46.675182+01:00"))
- @patch('gitlint.git.sh')
+ @patch("gitlint.cli.get_stdin_data", return_value="WIP: tïtle \n")
+ @patch("arrow.now", return_value=arrow.get("2020-02-19T12:18:46.675182+01:00"))
+ @patch("gitlint.git.sh")
def test_lint_staged_stdin(self, sh, _, __):
- """ Test for ignoring stdin when --ignore-stdin flag is enabled"""
+ """Test for ignoring stdin when --ignore-stdin flag is enabled"""
sh.git.side_effect = [
- "#", # git config --get core.commentchar
- "föo user\n", # git config --get user.name
- "föo@bar.com\n", # git config --get user.email
- "my-branch\n", # git rev-parse --abbrev-ref HEAD (=current branch)
- "commit-1/file-1\ncommit-1/file-2\n", # git diff-tree
+ "#", # git config --get core.commentchar
+ "1\t5\tcommit-1/file-1\n8\t9\tcommit-1/file-2\n", # git diff-tree
+ "föo user\n", # git config --get user.name
+ "föo@bar.com\n", # git config --get user.email
+ "my-branch\n", # git rev-parse --abbrev-ref HEAD (=current branch)
]
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["--debug", "--staged"])
self.assertEqual(stderr.getvalue(), self.get_expected("cli/test_cli/test_lint_staged_stdin_1"))
self.assertEqual(result.exit_code, 3)
self.assertEqual(result.output, "")
expected_kwargs = self.get_system_info_dict()
- expected_logs = self.get_expected('cli/test_cli/test_lint_staged_stdin_2', expected_kwargs)
+ expected_logs = self.get_expected("cli/test_cli/test_lint_staged_stdin_2", expected_kwargs)
self.assert_logged(expected_logs)
- @patch('arrow.now', return_value=arrow.get("2020-02-19T12:18:46.675182+01:00"))
- @patch('gitlint.git.sh')
+ @patch("arrow.now", return_value=arrow.get("2020-02-19T12:18:46.675182+01:00"))
+ @patch("gitlint.git.sh")
def test_lint_staged_msg_filename(self, sh, _):
- """ Test for ignoring stdin when --ignore-stdin flag is enabled"""
+ """Test for ignoring stdin when --ignore-stdin flag is enabled"""
+ # fmt: off
sh.git.side_effect = [
"#", # git config --get core.commentchar
+ "3\t4\tcommit-1/file-1\n4\t7\tcommit-1/file-2\n", # git diff-tree
"föo user\n", # git config --get user.name
"föo@bar.com\n", # git config --get user.email
"my-branch\n", # git rev-parse --abbrev-ref HEAD (=current branch)
- "commit-1/file-1\ncommit-1/file-2\n", # git diff-tree
]
+ # fmt: on
with self.tempdir() as tmpdir:
msg_filename = os.path.join(tmpdir, "msg")
- with io.open(msg_filename, 'w', encoding=DEFAULT_ENCODING) as f:
+ with open(msg_filename, "w", encoding=DEFAULT_ENCODING) as f:
f.write("WIP: msg-filename tïtle\n")
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["--debug", "--staged", "--msg-filename", msg_filename])
self.assertEqual(stderr.getvalue(), self.get_expected("cli/test_cli/test_lint_staged_msg_filename_1"))
self.assertEqual(result.exit_code, 2)
self.assertEqual(result.output, "")
expected_kwargs = self.get_system_info_dict()
- expected_logs = self.get_expected('cli/test_cli/test_lint_staged_msg_filename_2', expected_kwargs)
+ expected_logs = self.get_expected("cli/test_cli/test_lint_staged_msg_filename_2", expected_kwargs)
self.assert_logged(expected_logs)
- @patch('gitlint.cli.get_stdin_data', return_value=False)
+ @patch("gitlint.cli.get_stdin_data", return_value=False)
def test_lint_staged_negative(self, _):
result = self.cli.invoke(cli.cli, ["--staged"])
self.assertEqual(result.exit_code, self.USAGE_ERROR_CODE)
- self.assertEqual(result.output, ("Error: The 'staged' option (--staged) can only be used when using "
- "'--msg-filename' or when piping data to gitlint via stdin.\n"))
-
- @patch('gitlint.cli.get_stdin_data', return_value=False)
- @patch('gitlint.git.sh')
+ self.assertEqual(
+ result.output,
+ "Error: The 'staged' option (--staged) can only be used when using "
+ "'--msg-filename' or when piping data to gitlint via stdin.\n",
+ )
+
+ @patch("gitlint.cli.get_stdin_data", return_value=False)
+ @patch("gitlint.git.sh")
def test_fail_without_commits(self, sh, _):
- """ Test for --debug option """
+ """Test for --debug option"""
- sh.git.side_effect = [
- "", # First invocation of git rev-list
- "" # Second invocation of git rev-list
- ]
+ sh.git.side_effect = ["", ""] # First invocation of git rev-list # Second invocation of git rev-list
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
# By default, gitlint should silently exit with code GITLINT_SUCCESS when there are no commits
result = self.cli.invoke(cli.cli, ["--commits", "foo..bar"])
self.assertEqual(stderr.getvalue(), "")
self.assertEqual(result.exit_code, cli.GITLINT_SUCCESS)
- self.assert_log_contains("DEBUG: gitlint.cli No commits in range \"foo..bar\"")
+ self.assert_log_contains('DEBUG: gitlint.cli No commits in range "foo..bar"')
# When --fail-without-commits is set, gitlint should hard fail with code USAGE_ERROR_CODE
self.clearlog()
result = self.cli.invoke(cli.cli, ["--commits", "foo..bar", "--fail-without-commits"])
self.assertEqual(result.output, 'Error: No commits in range "foo..bar"\n')
self.assertEqual(result.exit_code, self.USAGE_ERROR_CODE)
- self.assert_log_contains("DEBUG: gitlint.cli No commits in range \"foo..bar\"")
+ self.assert_log_contains('DEBUG: gitlint.cli No commits in range "foo..bar"')
- @patch('gitlint.cli.get_stdin_data', return_value=False)
+ @patch("gitlint.cli.get_stdin_data", return_value=False)
def test_msg_filename(self, _):
expected_output = "3: B6 Body message is missing\n"
with self.tempdir() as tmpdir:
msg_filename = os.path.join(tmpdir, "msg")
- with io.open(msg_filename, 'w', encoding=DEFAULT_ENCODING) as f:
+ with open(msg_filename, "w", encoding=DEFAULT_ENCODING) as f:
f.write("Commït title\n")
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["--msg-filename", msg_filename])
self.assertEqual(stderr.getvalue(), expected_output)
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.output, "")
- @patch('gitlint.cli.get_stdin_data', return_value="WIP: tïtle \n")
+ @patch("gitlint.cli.get_stdin_data", return_value="WIP: tïtle \n")
def test_silent_mode(self, _):
- """ Test for --silent option """
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ """Test for --silent option"""
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["--silent"])
self.assertEqual(stderr.getvalue(), "")
self.assertEqual(result.exit_code, 3)
self.assertEqual(result.output, "")
- @patch('gitlint.cli.get_stdin_data', return_value="WIP: tïtle \n")
+ @patch("gitlint.cli.get_stdin_data", return_value="WIP: tïtle \n")
def test_verbosity(self, _):
- """ Test for --verbosity option """
+ """Test for --verbosity option"""
# We only test -v and -vv, more testing is really not required here
# -v
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["-v"])
self.assertEqual(stderr.getvalue(), "1: T2\n1: T5\n3: B6\n")
self.assertEqual(result.exit_code, 3)
self.assertEqual(result.output, "")
# -vv
- expected_output = "1: T2 Title has trailing whitespace\n" + \
- "1: T5 Title contains the word 'WIP' (case-insensitive)\n" + \
- "3: B6 Body message is missing\n"
+ expected_output = (
+ "1: T2 Title has trailing whitespace\n"
+ + "1: T5 Title contains the word 'WIP' (case-insensitive)\n"
+ + "3: B6 Body message is missing\n"
+ )
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["-vv"], input="WIP: tïtle \n")
self.assertEqual(stderr.getvalue(), expected_output)
self.assertEqual(result.exit_code, 3)
self.assertEqual(result.output, "")
# -vvvv: not supported -> should print a config error
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
- result = self.cli.invoke(cli.cli, ["-vvvv"], input=u'WIP: tïtle \n')
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
+ result = self.cli.invoke(cli.cli, ["-vvvv"], input="WIP: tïtle \n")
self.assertEqual(stderr.getvalue(), "")
self.assertEqual(result.exit_code, CLITests.CONFIG_ERROR_CODE)
self.assertEqual(result.output, "Config Error: Option 'verbosity' must be set between 0 and 3\n")
- @patch('gitlint.cli.get_stdin_data', return_value=False)
- @patch('gitlint.git.sh')
+ @patch("gitlint.cli.get_stdin_data", return_value=False)
+ @patch("gitlint.git.sh")
def test_debug(self, sh, _):
- """ Test for --debug option """
+ """Test for --debug option"""
+ # fmt: off
sh.git.side_effect = [
"6f29bf81a8322a04071bb794666e48c443a90360\n" # git rev-list <SHA>
"25053ccec5e28e1bb8f7551fdbb5ab213ada2401\n"
"4da2656b0dadc76c7ee3fd0243a96cb64007f125\n",
# git log --pretty <FORMAT> <SHA>
- "test åuthor1\x00test-email1@föo.com\x002016-12-03 15:28:15 +0100\x00abc\n"
+ "test åuthor1\x00test-email1@föo.com\x002016-12-03 15:28:15 +0100\x00a123\n"
"commït-title1\n\ncommït-body1",
- "#", # git config --get core.commentchar
- "commit-1-branch-1\ncommit-1-branch-2\n", # git branch --contains <sha>
- "commit-1/file-1\ncommit-1/file-2\n", # git diff-tree
- "test åuthor2\x00test-email2@föo.com\x002016-12-04 15:28:15 +0100\x00abc\n"
+ "#", # git config --get core.commentchar
+ "5\t8\tcommit-1/file-1\n2\t9\tcommit-1/file-2\n", # git diff-tree
+ "commit-1-branch-1\ncommit-1-branch-2\n", # git branch --contains <sha>
+ "test åuthor2\x00test-email2@föo.com\x002016-12-04 15:28:15 +0100\x00b123\n"
"commït-title2.\n\ncommït-body2",
- "commit-2-branch-1\ncommit-2-branch-2\n", # git branch --contains <sha>
- "commit-2/file-1\ncommit-2/file-2\n", # git diff-tree
- "test åuthor3\x00test-email3@föo.com\x002016-12-05 15:28:15 +0100\x00abc\n"
+ "5\t8\tcommit-2/file-1\n7\t9\tcommit-2/file-2\n", # git diff-tree
+ "commit-2-branch-1\ncommit-2-branch-2\n", # git branch --contains <sha>
+ "test åuthor3\x00test-email3@föo.com\x002016-12-05 15:28:15 +0100\x00c123\n"
"föobar\nbar",
- "commit-3-branch-1\ncommit-3-branch-2\n", # git branch --contains <sha>
- "commit-3/file-1\ncommit-3/file-2\n", # git diff-tree
+ "1\t4\tcommit-3/file-1\n3\t4\tcommit-3/file-2\n", # git diff-tree
+ "commit-3-branch-1\ncommit-3-branch-2\n", # git branch --contains <sha>
]
+ # fmt: on
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
config_path = self.get_sample_path(os.path.join("config", "gitlintconfig"))
- result = self.cli.invoke(cli.cli, ["--config", config_path, "--debug", "--commits",
- "foo...bar"])
+ result = self.cli.invoke(cli.cli, ["--config", config_path, "--debug", "--commits", "foo...bar"])
- expected = "Commit 6f29bf81a8:\n3: B5\n\n" + \
- "Commit 25053ccec5:\n1: T3\n3: B5\n\n" + \
- "Commit 4da2656b0d:\n2: B4\n3: B5\n3: B6\n"
+ expected = (
+ "Commit 6f29bf81a8:\n3: B5\n\n"
+ "Commit 25053ccec5:\n1: T3\n3: B5\n\n"
+ "Commit 4da2656b0d:\n2: B4\n3: B5\n3: B6\n"
+ )
self.assertEqual(stderr.getvalue(), expected)
self.assertEqual(result.exit_code, 6)
expected_kwargs = self.get_system_info_dict()
- expected_kwargs.update({'config_path': config_path})
- expected_logs = self.get_expected('cli/test_cli/test_debug_1', expected_kwargs)
+ expected_kwargs.update({"config_path": config_path})
+ expected_logs = self.get_expected("cli/test_cli/test_debug_1", expected_kwargs)
self.assert_logged(expected_logs)
- @patch('gitlint.cli.get_stdin_data', return_value="Test tïtle\n")
+ @patch("gitlint.cli.get_stdin_data", return_value="Test tïtle\n")
def test_extra_path(self, _):
- """ Test for --extra-path flag """
+ """Test for --extra-path flag"""
# Test extra-path pointing to a directory
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
extra_path = self.get_sample_path("user_rules")
result = self.cli.invoke(cli.cli, ["--extra-path", extra_path])
- expected_output = "1: UC1 Commit violåtion 1: \"Contënt 1\"\n" + \
- "3: B6 Body message is missing\n"
+ expected_output = '1: UC1 Commit violåtion 1: "Contënt 1"\n' + "3: B6 Body message is missing\n"
self.assertEqual(stderr.getvalue(), expected_output)
self.assertEqual(result.exit_code, 2)
# Test extra-path pointing to a file
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
extra_path = self.get_sample_path(os.path.join("user_rules", "my_commit_rules.py"))
result = self.cli.invoke(cli.cli, ["--extra-path", extra_path])
- expected_output = "1: UC1 Commit violåtion 1: \"Contënt 1\"\n" + \
- "3: B6 Body message is missing\n"
+ expected_output = '1: UC1 Commit violåtion 1: "Contënt 1"\n' + "3: B6 Body message is missing\n"
+ self.assertEqual(stderr.getvalue(), expected_output)
+ self.assertEqual(result.exit_code, 2)
+
+ @patch("gitlint.cli.get_stdin_data", return_value="Test tïtle\n")
+ def test_extra_path_environment(self, _):
+ """Test for GITLINT_EXTRA_PATH environment variable"""
+ # Test setting extra-path to a directory from an environment variable
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
+ extra_path = self.get_sample_path("user_rules")
+ result = self.cli.invoke(cli.cli, env={"GITLINT_EXTRA_PATH": extra_path})
+
+ expected_output = '1: UC1 Commit violåtion 1: "Contënt 1"\n' + "3: B6 Body message is missing\n"
+ self.assertEqual(stderr.getvalue(), expected_output)
+ self.assertEqual(result.exit_code, 2)
+
+ # Test extra-path pointing to a file from an environment variable
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
+ extra_path = self.get_sample_path(os.path.join("user_rules", "my_commit_rules.py"))
+ result = self.cli.invoke(cli.cli, env={"GITLINT_EXTRA_PATH": extra_path})
+ expected_output = '1: UC1 Commit violåtion 1: "Contënt 1"\n' + "3: B6 Body message is missing\n"
self.assertEqual(stderr.getvalue(), expected_output)
self.assertEqual(result.exit_code, 2)
- @patch('gitlint.cli.get_stdin_data', return_value="Test tïtle\n\nMy body that is long enough")
+ @patch("gitlint.cli.get_stdin_data", return_value="Test tïtle\n\nMy body that is long enough")
def test_contrib(self, _):
# Test enabled contrib rules
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["--contrib", "contrib-title-conventional-commits,CC1"])
- expected_output = self.get_expected('cli/test_cli/test_contrib_1')
+ expected_output = self.get_expected("cli/test_cli/test_contrib_1")
self.assertEqual(stderr.getvalue(), expected_output)
self.assertEqual(result.exit_code, 2)
- @patch('gitlint.cli.get_stdin_data', return_value="Test tïtle\n")
+ @patch("gitlint.cli.get_stdin_data", return_value="Test tïtle\n")
def test_contrib_negative(self, _):
result = self.cli.invoke(cli.cli, ["--contrib", "föobar,CC1"])
self.assertEqual(result.output, "Config Error: No contrib rule with id or name 'föobar' found.\n")
self.assertEqual(result.exit_code, self.CONFIG_ERROR_CODE)
- @patch('gitlint.cli.get_stdin_data', return_value="WIP: tëst")
+ @patch("gitlint.cli.get_stdin_data", return_value="WIP: tëst")
def test_config_file(self, _):
- """ Test for --config option """
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ """Test for --config option"""
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
config_path = self.get_sample_path(os.path.join("config", "gitlintconfig"))
result = self.cli.invoke(cli.cli, ["--config", config_path])
self.assertEqual(result.output, "")
self.assertEqual(stderr.getvalue(), "1: T5\n3: B6\n")
self.assertEqual(result.exit_code, 2)
+ @patch("gitlint.cli.get_stdin_data", return_value="WIP: tëst")
+ def test_config_file_environment(self, _):
+ """Test for GITLINT_CONFIG environment variable"""
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
+ config_path = self.get_sample_path(os.path.join("config", "gitlintconfig"))
+ result = self.cli.invoke(cli.cli, env={"GITLINT_CONFIG": config_path})
+ self.assertEqual(result.output, "")
+ self.assertEqual(stderr.getvalue(), "1: T5\n3: B6\n")
+ self.assertEqual(result.exit_code, 2)
+
def test_config_file_negative(self):
- """ Negative test for --config option """
+ """Negative test for --config option"""
# Directory as config file
config_path = self.get_sample_path("config")
result = self.cli.invoke(cli.cli, ["--config", config_path])
@@ -502,9 +557,30 @@ class CLITests(BaseTestCase):
result = self.cli.invoke(cli.cli, ["--config", config_path])
self.assertEqual(result.exit_code, self.CONFIG_ERROR_CODE)
- @patch('gitlint.cli.get_stdin_data', return_value=False)
+ def test_config_file_negative_environment(self):
+ """Negative test for GITLINT_CONFIG environment variable"""
+ # Directory as config file
+ config_path = self.get_sample_path("config")
+ result = self.cli.invoke(cli.cli, env={"GITLINT_CONFIG": config_path})
+ expected_string = f"Error: Invalid value for '-C' / '--config': File '{config_path}' is a directory."
+ self.assertEqual(result.output.split("\n")[3], expected_string)
+ self.assertEqual(result.exit_code, self.USAGE_ERROR_CODE)
+
+ # Non existing file
+ config_path = self.get_sample_path("föo")
+ result = self.cli.invoke(cli.cli, env={"GITLINT_CONFIG": config_path})
+ expected_string = f"Error: Invalid value for '-C' / '--config': File '{config_path}' does not exist."
+ self.assertEqual(result.output.split("\n")[3], expected_string)
+ self.assertEqual(result.exit_code, self.USAGE_ERROR_CODE)
+
+ # Invalid config file
+ config_path = self.get_sample_path(os.path.join("config", "invalid-option-value"))
+ result = self.cli.invoke(cli.cli, env={"GITLINT_CONFIG": config_path})
+ self.assertEqual(result.exit_code, self.CONFIG_ERROR_CODE)
+
+ @patch("gitlint.cli.get_stdin_data", return_value=False)
def test_target(self, _):
- """ Test for the --target option """
+ """Test for the --target option"""
with self.tempdir() as tmpdir:
tmpdir_path = os.path.realpath(tmpdir)
os.environ["LANGUAGE"] = "C" # Force language to english so we can check for error message
@@ -515,7 +591,7 @@ class CLITests(BaseTestCase):
self.assertEqual(result.exit_code, self.GIT_CONTEXT_ERROR_CODE)
def test_target_negative(self):
- """ Negative test for the --target option """
+ """Negative test for the --target option"""
# try setting a non-existing target
result = self.cli.invoke(cli.cli, ["--target", "/föo/bar"])
self.assertEqual(result.exit_code, self.USAGE_ERROR_CODE)
@@ -529,57 +605,63 @@ class CLITests(BaseTestCase):
expected_msg = f"Error: Invalid value for '--target': Directory '{target_path}' is a file."
self.assertEqual(result.output.split("\n")[3], expected_msg)
- @patch('gitlint.config.LintConfigGenerator.generate_config')
+ @patch("gitlint.config.LintConfigGenerator.generate_config")
def test_generate_config(self, generate_config):
- """ Test for the generate-config subcommand """
+ """Test for the generate-config subcommand"""
result = self.cli.invoke(cli.cli, ["generate-config"], input="tëstfile\n")
self.assertEqual(result.exit_code, self.GITLINT_SUCCESS_CODE)
- expected_msg = "Please specify a location for the sample gitlint config file [.gitlint]: tëstfile\n" + \
- f"Successfully generated {os.path.realpath('tëstfile')}\n"
+ expected_msg = (
+ "Please specify a location for the sample gitlint config file [.gitlint]: tëstfile\n"
+ + f"Successfully generated {os.path.realpath('tëstfile')}\n"
+ )
self.assertEqual(result.output, expected_msg)
generate_config.assert_called_once_with(os.path.realpath("tëstfile"))
def test_generate_config_negative(self):
- """ Negative test for the generate-config subcommand """
+ """Negative test for the generate-config subcommand"""
# Non-existing directory
fake_dir = os.path.abspath("/föo")
fake_path = os.path.join(fake_dir, "bar")
result = self.cli.invoke(cli.cli, ["generate-config"], input=fake_path)
self.assertEqual(result.exit_code, self.USAGE_ERROR_CODE)
- expected_msg = f"Please specify a location for the sample gitlint config file [.gitlint]: {fake_path}\n" + \
- f"Error: Directory '{fake_dir}' does not exist.\n"
+ expected_msg = (
+ f"Please specify a location for the sample gitlint config file [.gitlint]: {fake_path}\n"
+ + f"Error: Directory '{fake_dir}' does not exist.\n"
+ )
self.assertEqual(result.output, expected_msg)
# Existing file
sample_path = self.get_sample_path(os.path.join("config", "gitlintconfig"))
result = self.cli.invoke(cli.cli, ["generate-config"], input=sample_path)
self.assertEqual(result.exit_code, self.USAGE_ERROR_CODE)
- expected_msg = "Please specify a location for the sample gitlint " + \
- f"config file [.gitlint]: {sample_path}\n" + \
- f"Error: File \"{sample_path}\" already exists.\n"
+ expected_msg = (
+ "Please specify a location for the sample gitlint "
+ f"config file [.gitlint]: {sample_path}\n"
+ f'Error: File "{sample_path}" already exists.\n'
+ )
self.assertEqual(result.output, expected_msg)
- @patch('gitlint.cli.get_stdin_data', return_value=False)
- @patch('gitlint.git.sh')
+ @patch("gitlint.cli.get_stdin_data", return_value=False)
+ @patch("gitlint.git.sh")
def test_git_error(self, sh, _):
- """ Tests that the cli handles git errors properly """
+ """Tests that the cli handles git errors properly"""
sh.git.side_effect = CommandNotFound("git")
result = self.cli.invoke(cli.cli)
self.assertEqual(result.exit_code, self.GIT_CONTEXT_ERROR_CODE)
- @patch('gitlint.cli.get_stdin_data', return_value=False)
- @patch('gitlint.git.sh')
+ @patch("gitlint.cli.get_stdin_data", return_value=False)
+ @patch("gitlint.git.sh")
def test_no_commits_in_range(self, sh, _):
- """ Test for --commits with the specified range being empty. """
+ """Test for --commits with the specified range being empty."""
sh.git.side_effect = lambda *_args, **_kwargs: ""
- result = self.cli.invoke(cli.cli, ["--commits", "master...HEAD"])
+ result = self.cli.invoke(cli.cli, ["--commits", "main...HEAD"])
- self.assert_log_contains("DEBUG: gitlint.cli No commits in range \"master...HEAD\"")
+ self.assert_log_contains('DEBUG: gitlint.cli No commits in range "main...HEAD"')
self.assertEqual(result.exit_code, self.GITLINT_SUCCESS_CODE)
- @patch('gitlint.cli.get_stdin_data', return_value="WIP: tëst tïtle")
+ @patch("gitlint.cli.get_stdin_data", return_value="WIP: tëst tïtle")
def test_named_rules(self, _):
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
config_path = self.get_sample_path(os.path.join("config", "named-rules"))
result = self.cli.invoke(cli.cli, ["--config", config_path, "--debug"])
self.assertEqual(result.output, "")
@@ -588,6 +670,6 @@ class CLITests(BaseTestCase):
# Assert debug logs are correct
expected_kwargs = self.get_system_info_dict()
- expected_kwargs.update({'config_path': config_path})
- expected_logs = self.get_expected('cli/test_cli/test_named_rules_2', expected_kwargs)
+ expected_kwargs.update({"config_path": config_path})
+ expected_logs = self.get_expected("cli/test_cli/test_named_rules_2", expected_kwargs)
self.assert_logged(expected_logs)
diff --git a/gitlint-core/gitlint/tests/cli/test_cli_hooks.py b/gitlint-core/gitlint/tests/cli/test_cli_hooks.py
index 825345f..d4311c6 100644
--- a/gitlint-core/gitlint/tests/cli/test_cli_hooks.py
+++ b/gitlint-core/gitlint/tests/cli/test_cli_hooks.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
import io
from io import StringIO
import os
@@ -23,21 +21,21 @@ class CLIHookTests(BaseTestCase):
CONFIG_ERROR_CODE = 255
def setUp(self):
- super(CLIHookTests, self).setUp()
+ super().setUp()
self.cli = CliRunner()
# Patch gitlint.cli.git_version() so that we don't have to patch it separately in every test
- self.git_version_path = patch('gitlint.cli.git_version')
+ self.git_version_path = patch("gitlint.cli.git_version")
cli.git_version = self.git_version_path.start()
cli.git_version.return_value = "git version 1.2.3"
def tearDown(self):
self.git_version_path.stop()
- @patch('gitlint.hooks.GitHookInstaller.install_commit_msg_hook')
- @patch('gitlint.hooks.git_hooks_dir', return_value=os.path.join("/hür", "dur"))
+ @patch("gitlint.hooks.GitHookInstaller.install_commit_msg_hook")
+ @patch("gitlint.hooks.git_hooks_dir", return_value=os.path.join("/hür", "dur"))
def test_install_hook(self, _, install_hook):
- """ Test for install-hook subcommand """
+ """Test for install-hook subcommand"""
result = self.cli.invoke(cli.cli, ["install-hook"])
expected_path = os.path.join("/hür", "dur", hooks.COMMIT_MSG_HOOK_DST_PATH)
expected = f"Successfully installed gitlint commit-msg hook in {expected_path}\n"
@@ -47,10 +45,10 @@ class CLIHookTests(BaseTestCase):
expected_config.target = os.path.realpath(os.getcwd())
install_hook.assert_called_once_with(expected_config)
- @patch('gitlint.hooks.GitHookInstaller.install_commit_msg_hook')
- @patch('gitlint.hooks.git_hooks_dir', return_value=os.path.join("/hür", "dur"))
+ @patch("gitlint.hooks.GitHookInstaller.install_commit_msg_hook")
+ @patch("gitlint.hooks.git_hooks_dir", return_value=os.path.join("/hür", "dur"))
def test_install_hook_target(self, _, install_hook):
- """ Test for install-hook subcommand with a specific --target option specified """
+ """Test for install-hook subcommand with a specific --target option specified"""
# Specified target
result = self.cli.invoke(cli.cli, ["--target", self.SAMPLES_DIR, "install-hook"])
expected_path = os.path.join("/hür", "dur", hooks.COMMIT_MSG_HOOK_DST_PATH)
@@ -62,9 +60,9 @@ class CLIHookTests(BaseTestCase):
expected_config.target = self.SAMPLES_DIR
install_hook.assert_called_once_with(expected_config)
- @patch('gitlint.hooks.GitHookInstaller.install_commit_msg_hook', side_effect=hooks.GitHookInstallerError("tëst"))
+ @patch("gitlint.hooks.GitHookInstaller.install_commit_msg_hook", side_effect=hooks.GitHookInstallerError("tëst"))
def test_install_hook_negative(self, install_hook):
- """ Negative test for install-hook subcommand """
+ """Negative test for install-hook subcommand"""
result = self.cli.invoke(cli.cli, ["install-hook"])
self.assertEqual(result.exit_code, self.GIT_CONTEXT_ERROR_CODE)
self.assertEqual(result.output, "tëst\n")
@@ -72,10 +70,10 @@ class CLIHookTests(BaseTestCase):
expected_config.target = os.path.realpath(os.getcwd())
install_hook.assert_called_once_with(expected_config)
- @patch('gitlint.hooks.GitHookInstaller.uninstall_commit_msg_hook')
- @patch('gitlint.hooks.git_hooks_dir', return_value=os.path.join("/hür", "dur"))
+ @patch("gitlint.hooks.GitHookInstaller.uninstall_commit_msg_hook")
+ @patch("gitlint.hooks.git_hooks_dir", return_value=os.path.join("/hür", "dur"))
def test_uninstall_hook(self, _, uninstall_hook):
- """ Test for uninstall-hook subcommand """
+ """Test for uninstall-hook subcommand"""
result = self.cli.invoke(cli.cli, ["uninstall-hook"])
expected_path = os.path.join("/hür", "dur", hooks.COMMIT_MSG_HOOK_DST_PATH)
expected = f"Successfully uninstalled gitlint commit-msg hook from {expected_path}\n"
@@ -85,9 +83,9 @@ class CLIHookTests(BaseTestCase):
expected_config.target = os.path.realpath(os.getcwd())
uninstall_hook.assert_called_once_with(expected_config)
- @patch('gitlint.hooks.GitHookInstaller.uninstall_commit_msg_hook', side_effect=hooks.GitHookInstallerError("tëst"))
+ @patch("gitlint.hooks.GitHookInstaller.uninstall_commit_msg_hook", side_effect=hooks.GitHookInstallerError("tëst"))
def test_uninstall_hook_negative(self, uninstall_hook):
- """ Negative test for uninstall-hook subcommand """
+ """Negative test for uninstall-hook subcommand"""
result = self.cli.invoke(cli.cli, ["uninstall-hook"])
self.assertEqual(result.exit_code, self.GIT_CONTEXT_ERROR_CODE)
self.assertEqual(result.output, "tëst\n")
@@ -96,8 +94,8 @@ class CLIHookTests(BaseTestCase):
uninstall_hook.assert_called_once_with(expected_config)
def test_run_hook_no_tty(self):
- """ Test for run-hook subcommand.
- When no TTY is available (like is the case for this test), the hook will abort after the first check.
+ """Test for run-hook subcommand.
+ When no TTY is available (like is the case for this test), the hook will abort after the first check.
"""
# No need to patch git as we're passing a msg-filename to run-hook, so no git calls are made.
@@ -110,20 +108,20 @@ class CLIHookTests(BaseTestCase):
with self.tempdir() as tmpdir:
msg_filename = os.path.join(tmpdir, "hür")
- with io.open(msg_filename, 'w', encoding=DEFAULT_ENCODING) as f:
+ with open(msg_filename, "w", encoding=DEFAULT_ENCODING) as f:
f.write("WIP: tïtle\n")
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["--msg-filename", msg_filename, "run-hook"])
- self.assertEqual(result.output, self.get_expected('cli/test_cli_hooks/test_hook_no_tty_1_stdout'))
+ self.assertEqual(result.output, self.get_expected("cli/test_cli_hooks/test_hook_no_tty_1_stdout"))
self.assertEqual(stderr.getvalue(), self.get_expected("cli/test_cli_hooks/test_hook_no_tty_1_stderr"))
# exit code is 1 because aborted (no stdin available)
self.assertEqual(result.exit_code, 1)
- @patch('gitlint.cli.shell')
+ @patch("gitlint.cli.shell")
def test_run_hook_edit(self, shell):
- """ Test for run-hook subcommand, answering 'e(dit)' after commit-hook """
+ """Test for run-hook subcommand, answering 'e(dit)' after commit-hook"""
set_editors = [None, "myeditor"]
expected_editors = ["vim -n", "myeditor"]
@@ -131,20 +129,28 @@ class CLIHookTests(BaseTestCase):
for i in range(0, len(set_editors)):
if set_editors[i]:
- os.environ['EDITOR'] = set_editors[i]
+ os.environ["EDITOR"] = set_editors[i]
+ else:
+ # When set_editors[i] == None, ensure we don't fallback to EDITOR set in shell invocating the tests
+ os.environ.pop("EDITOR", None)
- with self.patch_input(['e', 'e', 'n']):
+ with self.patch_input(["e", "e", "n"]):
with self.tempdir() as tmpdir:
msg_filename = os.path.realpath(os.path.join(tmpdir, "hür"))
- with io.open(msg_filename, 'w', encoding=DEFAULT_ENCODING) as f:
+ with open(msg_filename, "w", encoding=DEFAULT_ENCODING) as f:
f.write(commit_messages[i] + "\n")
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["--msg-filename", msg_filename, "run-hook"])
- self.assertEqual(result.output, self.get_expected('cli/test_cli_hooks/test_hook_edit_1_stdout',
- {"commit_msg": commit_messages[i]}))
- expected = self.get_expected("cli/test_cli_hooks/test_hook_edit_1_stderr",
- {"commit_msg": commit_messages[i]})
+ self.assertEqual(
+ result.output,
+ self.get_expected(
+ "cli/test_cli_hooks/test_hook_edit_1_stdout", {"commit_msg": commit_messages[i]}
+ ),
+ )
+ expected = self.get_expected(
+ "cli/test_cli_hooks/test_hook_edit_1_stderr", {"commit_msg": commit_messages[i]}
+ )
self.assertEqual(stderr.getvalue(), expected)
# exit code = number of violations
@@ -155,17 +161,17 @@ class CLIHookTests(BaseTestCase):
self.assert_log_contains(f"DEBUG: gitlint.cli run-hook: {expected_editors[i]} {msg_filename}")
def test_run_hook_no(self):
- """ Test for run-hook subcommand, answering 'n(o)' after commit-hook """
+ """Test for run-hook subcommand, answering 'n(o)' after commit-hook"""
- with self.patch_input(['n']):
+ with self.patch_input(["n"]):
with self.tempdir() as tmpdir:
msg_filename = os.path.join(tmpdir, "hür")
- with io.open(msg_filename, 'w', encoding=DEFAULT_ENCODING) as f:
+ with open(msg_filename, "w", encoding=DEFAULT_ENCODING) as f:
f.write("WIP: höok no\n")
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["--msg-filename", msg_filename, "run-hook"])
- self.assertEqual(result.output, self.get_expected('cli/test_cli_hooks/test_hook_no_1_stdout'))
+ self.assertEqual(result.output, self.get_expected("cli/test_cli_hooks/test_hook_no_1_stdout"))
self.assertEqual(stderr.getvalue(), self.get_expected("cli/test_cli_hooks/test_hook_no_1_stderr"))
# We decided not to keep the commit message: hook returns number of violations (>0)
@@ -174,16 +180,16 @@ class CLIHookTests(BaseTestCase):
self.assert_log_contains("DEBUG: gitlint.cli run-hook: commit message declined")
def test_run_hook_yes(self):
- """ Test for run-hook subcommand, answering 'y(es)' after commit-hook """
- with self.patch_input(['y']):
+ """Test for run-hook subcommand, answering 'y(es)' after commit-hook"""
+ with self.patch_input(["y"]):
with self.tempdir() as tmpdir:
msg_filename = os.path.join(tmpdir, "hür")
- with io.open(msg_filename, 'w', encoding=DEFAULT_ENCODING) as f:
+ with open(msg_filename, "w", encoding=DEFAULT_ENCODING) as f:
f.write("WIP: höok yes\n")
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["--msg-filename", msg_filename, "run-hook"])
- self.assertEqual(result.output, self.get_expected('cli/test_cli_hooks/test_hook_yes_1_stdout'))
+ self.assertEqual(result.output, self.get_expected("cli/test_cli_hooks/test_hook_yes_1_stdout"))
self.assertEqual(stderr.getvalue(), self.get_expected("cli/test_cli_hooks/test_hook_yes_1_stderr"))
# Exit code is 0 because we decide to keep the commit message
@@ -191,23 +197,23 @@ class CLIHookTests(BaseTestCase):
self.assertEqual(result.exit_code, 0)
self.assert_log_contains("DEBUG: gitlint.cli run-hook: commit message accepted")
- @patch('gitlint.cli.get_stdin_data', return_value=False)
- @patch('gitlint.git.sh')
+ @patch("gitlint.cli.get_stdin_data", return_value=False)
+ @patch("gitlint.git.sh")
def test_run_hook_negative(self, sh, _):
- """ Negative test for the run-hook subcommand: testing whether exceptions are correctly handled when
+ """Negative test for the run-hook subcommand: testing whether exceptions are correctly handled when
running `gitlint run-hook`.
"""
# GIT_CONTEXT_ERROR_CODE: git error
error_msg = b"fatal: not a git repository (or any of the parent directories): .git"
sh.git.side_effect = ErrorReturnCode("full command", b"stdout", error_msg)
result = self.cli.invoke(cli.cli, ["run-hook"])
- expected = self.get_expected('cli/test_cli_hooks/test_run_hook_negative_1', {'git_repo': os.getcwd()})
+ expected = self.get_expected("cli/test_cli_hooks/test_run_hook_negative_1", {"git_repo": os.getcwd()})
self.assertEqual(result.output, expected)
self.assertEqual(result.exit_code, self.GIT_CONTEXT_ERROR_CODE)
# USAGE_ERROR_CODE: incorrect use of gitlint
result = self.cli.invoke(cli.cli, ["--staged", "run-hook"])
- self.assertEqual(result.output, self.get_expected('cli/test_cli_hooks/test_run_hook_negative_2'))
+ self.assertEqual(result.output, self.get_expected("cli/test_cli_hooks/test_run_hook_negative_2"))
self.assertEqual(result.exit_code, self.USAGE_ERROR_CODE)
# CONFIG_ERROR_CODE: incorrect config. Note that this is handled before the hook even runs
@@ -215,67 +221,66 @@ class CLIHookTests(BaseTestCase):
self.assertEqual(result.output, "Config Error: No such rule 'föo'\n")
self.assertEqual(result.exit_code, self.CONFIG_ERROR_CODE)
- @patch('gitlint.cli.get_stdin_data', return_value="WIP: Test hook stdin tïtle\n")
+ @patch("gitlint.cli.get_stdin_data", return_value="WIP: Test hook stdin tïtle\n")
def test_run_hook_stdin_violations(self, _):
- """ Test for passing stdin data to run-hook, expecting some violations. Equivalent of:
- $ echo "WIP: Test hook stdin tïtle" | gitlint run-hook
+ """Test for passing stdin data to run-hook, expecting some violations. Equivalent of:
+ $ echo "WIP: Test hook stdin tïtle" | gitlint run-hook
"""
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["run-hook"])
- expected_stderr = self.get_expected('cli/test_cli_hooks/test_hook_stdin_violations_1_stderr')
+ expected_stderr = self.get_expected("cli/test_cli_hooks/test_hook_stdin_violations_1_stderr")
self.assertEqual(stderr.getvalue(), expected_stderr)
- self.assertEqual(result.output, self.get_expected('cli/test_cli_hooks/test_hook_stdin_violations_1_stdout'))
+ self.assertEqual(result.output, self.get_expected("cli/test_cli_hooks/test_hook_stdin_violations_1_stdout"))
# Hook will auto-abort because we're using stdin. Abort = exit code 1
self.assertEqual(result.exit_code, 1)
- @patch('gitlint.cli.get_stdin_data', return_value="Test tïtle\n\nTest bödy that is long enough")
+ @patch("gitlint.cli.get_stdin_data", return_value="Test tïtle\n\nTest bödy that is long enough")
def test_run_hook_stdin_no_violations(self, _):
- """ Test for passing stdin data to run-hook, expecting *NO* violations, Equivalent of:
- $ echo -e "Test tïtle\n\nTest bödy that is long enough" | gitlint run-hook
+ """Test for passing stdin data to run-hook, expecting *NO* violations, Equivalent of:
+ $ echo -e "Test tïtle\n\nTest bödy that is long enough" | gitlint run-hook
"""
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["run-hook"])
self.assertEqual(stderr.getvalue(), "") # no errors = no stderr output
- expected_stdout = self.get_expected('cli/test_cli_hooks/test_hook_stdin_no_violations_1_stdout')
+ expected_stdout = self.get_expected("cli/test_cli_hooks/test_hook_stdin_no_violations_1_stdout")
self.assertEqual(result.output, expected_stdout)
self.assertEqual(result.exit_code, 0)
- @patch('gitlint.cli.get_stdin_data', return_value="WIP: Test hook config tïtle\n")
+ @patch("gitlint.cli.get_stdin_data", return_value="WIP: Test hook config tïtle\n")
def test_run_hook_config(self, _):
- """ Test that gitlint still respects config when running run-hook, equivalent of:
- $ echo "WIP: Test hook config tïtle" | gitlint -c title-max-length.line-length=5 --ignore B6 run-hook
+ """Test that gitlint still respects config when running run-hook, equivalent of:
+ $ echo "WIP: Test hook config tïtle" | gitlint -c title-max-length.line-length=5 --ignore B6 run-hook
"""
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["-c", "title-max-length.line-length=5", "--ignore", "B6", "run-hook"])
- self.assertEqual(stderr.getvalue(), self.get_expected('cli/test_cli_hooks/test_hook_config_1_stderr'))
- self.assertEqual(result.output, self.get_expected('cli/test_cli_hooks/test_hook_config_1_stdout'))
+ self.assertEqual(stderr.getvalue(), self.get_expected("cli/test_cli_hooks/test_hook_config_1_stderr"))
+ self.assertEqual(result.output, self.get_expected("cli/test_cli_hooks/test_hook_config_1_stdout"))
# Hook will auto-abort because we're using stdin. Abort = exit code 1
self.assertEqual(result.exit_code, 1)
- @patch('gitlint.cli.get_stdin_data', return_value=False)
- @patch('gitlint.git.sh')
+ @patch("gitlint.cli.get_stdin_data", return_value=False)
+ @patch("gitlint.git.sh")
def test_run_hook_local_commit(self, sh, _):
- """ Test running the hook on the last commit-msg from the local repo, equivalent of:
- $ gitlint run-hook
- and then choosing 'e'
+ """Test running the hook on the last commit-msg from the local repo, equivalent of:
+ $ gitlint run-hook
+ and then choosing 'e'
"""
sh.git.side_effect = [
"6f29bf81a8322a04071bb794666e48c443a90360",
- "test åuthor\x00test-email@föo.com\x002016-12-03 15:28:15 +0100\x00åbc\n"
- "WIP: commït-title\n\ncommït-body",
+ "test åuthor\x00test-email@föo.com\x002016-12-03 15:28:15 +0100\x00åbc\nWIP: commït-title\n\ncommït-body",
"#", # git config --get core.commentchar
+ "1\t5\tfile1.txt\n3\t4\tpåth/to/file2.txt\n",
"commit-1-branch-1\ncommit-1-branch-2\n",
- "file1.txt\npåth/to/file2.txt\n"
]
- with self.patch_input(['e']):
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with self.patch_input(["e"]):
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["run-hook"])
- expected = self.get_expected('cli/test_cli_hooks/test_hook_local_commit_1_stderr')
+ expected = self.get_expected("cli/test_cli_hooks/test_hook_local_commit_1_stderr")
self.assertEqual(stderr.getvalue(), expected)
- self.assertEqual(result.output, self.get_expected('cli/test_cli_hooks/test_hook_local_commit_1_stdout'))
+ self.assertEqual(result.output, self.get_expected("cli/test_cli_hooks/test_hook_local_commit_1_stdout"))
# If we can't edit the message, run-hook follows regular gitlint behavior and exit code = # violations
self.assertEqual(result.exit_code, 2)
diff --git a/gitlint-core/gitlint/tests/config/test_config.py b/gitlint-core/gitlint/tests/config/test_config.py
index c3fd78a..852bf75 100644
--- a/gitlint-core/gitlint/tests/config/test_config.py
+++ b/gitlint-core/gitlint/tests/config/test_config.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
from unittest.mock import patch
from gitlint import rules
@@ -9,16 +7,15 @@ from gitlint.tests.base import BaseTestCase
class LintConfigTests(BaseTestCase):
-
def test_set_rule_option(self):
config = LintConfig()
# assert default title line-length
- self.assertEqual(config.get_rule_option('title-max-length', 'line-length'), 72)
+ self.assertEqual(config.get_rule_option("title-max-length", "line-length"), 72)
# change line length and assert it is set
- config.set_rule_option('title-max-length', 'line-length', 60)
- self.assertEqual(config.get_rule_option('title-max-length', 'line-length'), 60)
+ config.set_rule_option("title-max-length", "line-length", 60)
+ self.assertEqual(config.get_rule_option("title-max-length", "line-length"), 60)
def test_set_rule_option_negative(self):
config = LintConfig()
@@ -26,18 +23,20 @@ class LintConfigTests(BaseTestCase):
# non-existing rule
expected_error_msg = "No such rule 'föobar'"
with self.assertRaisesMessage(LintConfigError, expected_error_msg):
- config.set_rule_option(u'föobar', u'lïne-length', 60)
+ config.set_rule_option("föobar", "lïne-length", 60)
# non-existing option
expected_error_msg = "Rule 'title-max-length' has no option 'föobar'"
with self.assertRaisesMessage(LintConfigError, expected_error_msg):
- config.set_rule_option('title-max-length', u'föobar', 60)
+ config.set_rule_option("title-max-length", "föobar", 60)
# invalid option value
- expected_error_msg = "'föo' is not a valid value for option 'title-max-length.line-length'. " + \
- "Option 'line-length' must be a positive integer (current value: 'föo')."
+ expected_error_msg = (
+ "'föo' is not a valid value for option 'title-max-length.line-length'. "
+ "Option 'line-length' must be a positive integer (current value: 'föo')."
+ )
with self.assertRaisesMessage(LintConfigError, expected_error_msg):
- config.set_rule_option('title-max-length', 'line-length', "föo")
+ config.set_rule_option("title-max-length", "line-length", "föo")
def test_set_general_option(self):
config = LintConfig()
@@ -45,12 +44,14 @@ class LintConfigTests(BaseTestCase):
# Check that default general options are correct
self.assertTrue(config.ignore_merge_commits)
self.assertTrue(config.ignore_fixup_commits)
+ self.assertTrue(config.ignore_fixup_amend_commits)
self.assertTrue(config.ignore_squash_commits)
self.assertTrue(config.ignore_revert_commits)
self.assertFalse(config.ignore_stdin)
self.assertFalse(config.staged)
self.assertFalse(config.fail_without_commits)
+ self.assertFalse(config.regex_style_search)
self.assertFalse(config.debug)
self.assertEqual(config.verbosity, 3)
active_rule_classes = tuple(type(rule) for rule in config.rules)
@@ -76,6 +77,10 @@ class LintConfigTests(BaseTestCase):
config.set_general_option("ignore-fixup-commits", "false")
self.assertFalse(config.ignore_fixup_commits)
+ # ignore_fixup_amend_commit
+ config.set_general_option("ignore-fixup-amend-commits", "false")
+ self.assertFalse(config.ignore_fixup_amend_commits)
+
# ignore_squash_commit
config.set_general_option("ignore-squash-commits", "false")
self.assertFalse(config.ignore_squash_commits)
@@ -100,6 +105,10 @@ class LintConfigTests(BaseTestCase):
config.set_general_option("fail-without-commits", "true")
self.assertTrue(config.fail_without_commits)
+ # regex-style-search
+ config.set_general_option("regex-style-search", "true")
+ self.assertTrue(config.regex_style_search)
+
# target
config.set_general_option("target", self.SAMPLES_DIR)
self.assertEqual(config.target, self.SAMPLES_DIR)
@@ -118,8 +127,8 @@ class LintConfigTests(BaseTestCase):
self.assertTrue(actual_rule.is_contrib)
self.assertEqual(str(type(actual_rule)), "<class 'conventional_commit.ConventionalCommit'>")
- self.assertEqual(actual_rule.id, 'CT1')
- self.assertEqual(actual_rule.name, u'contrib-title-conventional-commits')
+ self.assertEqual(actual_rule.id, "CT1")
+ self.assertEqual(actual_rule.name, "contrib-title-conventional-commits")
self.assertEqual(actual_rule.target, rules.CommitMessageTitle)
expected_rule_option = options.ListOption(
@@ -129,15 +138,15 @@ class LintConfigTests(BaseTestCase):
)
self.assertListEqual(actual_rule.options_spec, [expected_rule_option])
- self.assertDictEqual(actual_rule.options, {'types': expected_rule_option})
+ self.assertDictEqual(actual_rule.options, {"types": expected_rule_option})
# Check contrib-body-requires-signed-off-by contrib rule
actual_rule = config.rules.find_rule("contrib-body-requires-signed-off-by")
self.assertTrue(actual_rule.is_contrib)
self.assertEqual(str(type(actual_rule)), "<class 'signedoff_by.SignedOffBy'>")
- self.assertEqual(actual_rule.id, 'CC1')
- self.assertEqual(actual_rule.name, u'contrib-body-requires-signed-off-by')
+ self.assertEqual(actual_rule.id, "CC1")
+ self.assertEqual(actual_rule.name, "contrib-body-requires-signed-off-by")
# reset value (this is a different code path)
config.set_general_option("contrib", "contrib-body-requires-signed-off-by")
@@ -157,7 +166,7 @@ class LintConfigTests(BaseTestCase):
# UserRuleError, RuleOptionError should be re-raised as LintConfigErrors
side_effects = [rules.UserRuleError("üser-rule"), options.RuleOptionError("rüle-option")]
for side_effect in side_effects:
- with patch('gitlint.config.rule_finder.find_rule_classes', side_effect=side_effect):
+ with patch("gitlint.config.rule_finder.find_rule_classes", side_effect=side_effect):
with self.assertRaisesMessage(LintConfigError, str(side_effect)):
config.contrib = "contrib-title-conventional-commits"
@@ -166,15 +175,15 @@ class LintConfigTests(BaseTestCase):
config.set_general_option("extra-path", self.get_user_rules_path())
self.assertEqual(config.extra_path, self.get_user_rules_path())
- actual_rule = config.rules.find_rule('UC1')
+ actual_rule = config.rules.find_rule("UC1")
self.assertTrue(actual_rule.is_user_defined)
self.assertEqual(str(type(actual_rule)), "<class 'my_commit_rules.MyUserCommitRule'>")
- self.assertEqual(actual_rule.id, 'UC1')
- self.assertEqual(actual_rule.name, u'my-üser-commit-rule')
+ self.assertEqual(actual_rule.id, "UC1")
+ self.assertEqual(actual_rule.name, "my-üser-commit-rule")
self.assertEqual(actual_rule.target, None)
- expected_rule_option = options.IntOption('violation-count', 1, "Number of violåtions to return")
+ expected_rule_option = options.IntOption("violation-count", 1, "Number of violåtions to return")
self.assertListEqual(actual_rule.options_spec, [expected_rule_option])
- self.assertDictEqual(actual_rule.options, {'violation-count': expected_rule_option})
+ self.assertDictEqual(actual_rule.options, {"violation-count": expected_rule_option})
# reset value (this is a different code path)
config.set_general_option("extra-path", self.SAMPLES_DIR)
@@ -189,8 +198,9 @@ class LintConfigTests(BaseTestCase):
config.extra_path = "föo/bar"
# extra path contains classes with errors
- with self.assertRaisesMessage(LintConfigError,
- "User-defined rule class 'MyUserLineRule' must have a 'validate' method"):
+ with self.assertRaisesMessage(
+ LintConfigError, "User-defined rule class 'MyUserLineRule' must have a 'validate' method"
+ ):
config.extra_path = self.get_sample_path("user_rules/incorrect_linerule")
def test_set_general_option_negative(self):
@@ -218,31 +228,37 @@ class LintConfigTests(BaseTestCase):
config.verbosity = value
# invalid ignore_xxx_commits
- ignore_attributes = ["ignore_merge_commits", "ignore_fixup_commits", "ignore_squash_commits",
- "ignore_revert_commits"]
+ ignore_attributes = [
+ "ignore_merge_commits",
+ "ignore_fixup_commits",
+ "ignore_fixup_amend_commits",
+ "ignore_squash_commits",
+ "ignore_revert_commits",
+ ]
incorrect_values = [-1, 4, "föo"]
for attribute in ignore_attributes:
for value in incorrect_values:
option_name = attribute.replace("_", "-")
- with self.assertRaisesMessage(LintConfigError,
- f"Option '{option_name}' must be either 'true' or 'false'"):
+ with self.assertRaisesMessage(
+ LintConfigError, f"Option '{option_name}' must be either 'true' or 'false'"
+ ):
setattr(config, attribute, value)
# invalid ignore -> not here because ignore is a ListOption which converts everything to a string before
# splitting which means it it will accept just about everything
# invalid boolean options
- for attribute in ['debug', 'staged', 'ignore_stdin', 'fail_without_commits']:
+ for attribute in ["debug", "staged", "ignore_stdin", "fail_without_commits", "regex_style_search"]:
option_name = attribute.replace("_", "-")
- with self.assertRaisesMessage(LintConfigError,
- f"Option '{option_name}' must be either 'true' or 'false'"):
+ with self.assertRaisesMessage(LintConfigError, f"Option '{option_name}' must be either 'true' or 'false'"):
setattr(config, attribute, "föobar")
# extra-path has its own negative test
# invalid target
- with self.assertRaisesMessage(LintConfigError,
- "Option target must be an existing directory (current value: 'föo/bar')"):
+ with self.assertRaisesMessage(
+ LintConfigError, "Option target must be an existing directory (current value: 'föo/bar')"
+ ):
config.target = "föo/bar"
def test_ignore_independent_from_rules(self):
@@ -259,12 +275,25 @@ class LintConfigTests(BaseTestCase):
self.assertNotEqual(LintConfig(), LintConfigGenerator())
# Ensure LintConfig are not equal if they differ on their attributes
- attrs = [("verbosity", 1), ("rules", []), ("ignore_stdin", True), ("debug", True),
- ("ignore", ["T1"]), ("staged", True), ("_config_path", self.get_sample_path()),
- ("ignore_merge_commits", False), ("ignore_fixup_commits", False),
- ("ignore_squash_commits", False), ("ignore_revert_commits", False),
- ("extra_path", self.get_sample_path("user_rules")), ("target", self.get_sample_path()),
- ("contrib", ["CC1"])]
+ attrs = [
+ ("verbosity", 1),
+ ("rules", []),
+ ("ignore_stdin", True),
+ ("fail_without_commits", True),
+ ("regex_style_search", True),
+ ("debug", True),
+ ("ignore", ["T1"]),
+ ("staged", True),
+ ("_config_path", self.get_sample_path()),
+ ("ignore_merge_commits", False),
+ ("ignore_fixup_commits", False),
+ ("ignore_fixup_amend_commits", False),
+ ("ignore_squash_commits", False),
+ ("ignore_revert_commits", False),
+ ("extra_path", self.get_sample_path("user_rules")),
+ ("target", self.get_sample_path()),
+ ("contrib", ["CC1"]),
+ ]
for attr, val in attrs:
config = LintConfig()
setattr(config, attr, val)
@@ -281,7 +310,7 @@ class LintConfigTests(BaseTestCase):
class LintConfigGeneratorTests(BaseTestCase):
@staticmethod
- @patch('gitlint.config.shutil.copyfile')
+ @patch("gitlint.config.shutil.copyfile")
def test_install_commit_msg_hook_negative(copy):
LintConfigGenerator.generate_config("föo/bar/test")
copy.assert_called_with(GITLINT_CONFIG_TEMPLATE_SRC_PATH, "föo/bar/test")
diff --git a/gitlint-core/gitlint/tests/config/test_config_builder.py b/gitlint-core/gitlint/tests/config/test_config_builder.py
index e0d7f9b..dfb77cd 100644
--- a/gitlint-core/gitlint/tests/config/test_config_builder.py
+++ b/gitlint-core/gitlint/tests/config/test_config_builder.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
import copy
from gitlint.tests.base import BaseTestCase
@@ -14,24 +13,27 @@ class LintConfigBuilderTests(BaseTestCase):
config = config_builder.build()
# assert some defaults
- self.assertEqual(config.get_rule_option('title-max-length', 'line-length'), 72)
- self.assertEqual(config.get_rule_option('body-max-line-length', 'line-length'), 80)
- self.assertListEqual(config.get_rule_option('title-must-not-contain-word', 'words'), ["WIP"])
+ self.assertEqual(config.get_rule_option("title-max-length", "line-length"), 72)
+ self.assertEqual(config.get_rule_option("body-max-line-length", "line-length"), 80)
+ self.assertListEqual(config.get_rule_option("title-must-not-contain-word", "words"), ["WIP"])
self.assertEqual(config.verbosity, 3)
# Make some changes and check blueprint
- config_builder.set_option('title-max-length', 'line-length', 100)
- config_builder.set_option('general', 'verbosity', 2)
- config_builder.set_option('title-must-not-contain-word', 'words', ["foo", "bar"])
- expected_blueprint = {'title-must-not-contain-word': {'words': ['foo', 'bar']},
- 'title-max-length': {'line-length': 100}, 'general': {'verbosity': 2}}
+ config_builder.set_option("title-max-length", "line-length", 100)
+ config_builder.set_option("general", "verbosity", 2)
+ config_builder.set_option("title-must-not-contain-word", "words", ["foo", "bar"])
+ expected_blueprint = {
+ "title-must-not-contain-word": {"words": ["foo", "bar"]},
+ "title-max-length": {"line-length": 100},
+ "general": {"verbosity": 2},
+ }
self.assertDictEqual(config_builder._config_blueprint, expected_blueprint)
# Build config and verify that the changes have occurred and no other changes
config = config_builder.build()
- self.assertEqual(config.get_rule_option('title-max-length', 'line-length'), 100)
- self.assertEqual(config.get_rule_option('body-max-line-length', 'line-length'), 80) # should be unchanged
- self.assertListEqual(config.get_rule_option('title-must-not-contain-word', 'words'), ["foo", "bar"])
+ self.assertEqual(config.get_rule_option("title-max-length", "line-length"), 100)
+ self.assertEqual(config.get_rule_option("body-max-line-length", "line-length"), 80) # should be unchanged
+ self.assertListEqual(config.get_rule_option("title-must-not-contain-word", "words"), ["foo", "bar"])
self.assertEqual(config.verbosity, 2)
def test_set_from_commit_ignore_all(self):
@@ -82,8 +84,8 @@ class LintConfigBuilderTests(BaseTestCase):
self.assertIsNone(config.extra_path)
self.assertEqual(config.ignore, ["title-trailing-whitespace", "B2"])
- self.assertEqual(config.get_rule_option('title-max-length', 'line-length'), 20)
- self.assertEqual(config.get_rule_option('body-max-line-length', 'line-length'), 30)
+ self.assertEqual(config.get_rule_option("title-max-length", "line-length"), 20)
+ self.assertEqual(config.get_rule_option("body-max-line-length", "line-length"), 30)
def test_set_from_config_file_negative(self):
config_builder = LintConfigBuilder()
@@ -129,8 +131,10 @@ class LintConfigBuilderTests(BaseTestCase):
path = self.get_sample_path("config/invalid-option-value")
config_builder = LintConfigBuilder()
config_builder.set_from_config_file(path)
- expected_error_msg = "'föo' is not a valid value for option 'title-max-length.line-length'. " + \
- "Option 'line-length' must be a positive integer (current value: 'föo')."
+ expected_error_msg = (
+ "'föo' is not a valid value for option 'title-max-length.line-length'. "
+ "Option 'line-length' must be a positive integer (current value: 'föo')."
+ )
with self.assertRaisesMessage(LintConfigError, expected_error_msg):
config_builder.build()
@@ -139,14 +143,19 @@ class LintConfigBuilderTests(BaseTestCase):
# change and assert changes
config_builder = LintConfigBuilder()
- config_builder.set_config_from_string_list(['general.verbosity=1', 'title-max-length.line-length=60',
- 'body-max-line-length.line-length=120',
- "title-must-not-contain-word.words=håha"])
+ config_builder.set_config_from_string_list(
+ [
+ "general.verbosity=1",
+ "title-max-length.line-length=60",
+ "body-max-line-length.line-length=120",
+ "title-must-not-contain-word.words=håha",
+ ]
+ )
config = config_builder.build()
- self.assertEqual(config.get_rule_option('title-max-length', 'line-length'), 60)
- self.assertEqual(config.get_rule_option('body-max-line-length', 'line-length'), 120)
- self.assertListEqual(config.get_rule_option('title-must-not-contain-word', 'words'), ["håha"])
+ self.assertEqual(config.get_rule_option("title-max-length", "line-length"), 60)
+ self.assertEqual(config.get_rule_option("body-max-line-length", "line-length"), 120)
+ self.assertListEqual(config.get_rule_option("title-must-not-contain-word", "words"), ["håha"])
self.assertEqual(config.verbosity, 1)
def test_set_config_from_string_list_negative(self):
@@ -175,12 +184,12 @@ class LintConfigBuilderTests(BaseTestCase):
# no period between rule and option names
expected_msg = "'föobar=1' is an invalid configuration option. Use '<rule>.<option>=<value>'"
with self.assertRaisesMessage(LintConfigError, expected_msg):
- config_builder.set_config_from_string_list([u'föobar=1'])
+ config_builder.set_config_from_string_list(["föobar=1"])
def test_rebuild_config(self):
# normal config build
config_builder = LintConfigBuilder()
- config_builder.set_option('general', 'verbosity', 3)
+ config_builder.set_option("general", "verbosity", 3)
lint_config = config_builder.build()
self.assertEqual(lint_config.verbosity, 3)
@@ -193,9 +202,9 @@ class LintConfigBuilderTests(BaseTestCase):
def test_clone(self):
config_builder = LintConfigBuilder()
- config_builder.set_option('general', 'verbosity', 2)
- config_builder.set_option('title-max-length', 'line-length', 100)
- expected = {'title-max-length': {'line-length': 100}, 'general': {'verbosity': 2}}
+ config_builder.set_option("general", "verbosity", 2)
+ config_builder.set_option("title-max-length", "line-length", 100)
+ expected = {"title-max-length": {"line-length": 100}, "general": {"verbosity": 2}}
self.assertDictEqual(config_builder._config_blueprint, expected)
# Clone and verify that the blueprint is the same as the original
@@ -203,7 +212,7 @@ class LintConfigBuilderTests(BaseTestCase):
self.assertDictEqual(cloned_builder._config_blueprint, expected)
# Modify the original and make sure we're not modifying the clone (i.e. check that the copy is a deep copy)
- config_builder.set_option('title-max-length', 'line-length', 120)
+ config_builder.set_option("title-max-length", "line-length", 120)
self.assertDictEqual(cloned_builder._config_blueprint, expected)
def test_named_rules(self):
@@ -215,17 +224,22 @@ class LintConfigBuilderTests(BaseTestCase):
# Add a named rule by setting an option in the config builder that follows the named rule pattern
# Assert that whitespace in the rule name is stripped
- rule_qualifiers = [u'T7:my-extra-rüle', u' T7 : my-extra-rüle ', u'\tT7:\tmy-extra-rüle\t',
- u'T7:\t\n \tmy-extra-rüle\t\n\n', "title-match-regex:my-extra-rüle"]
+ rule_qualifiers = [
+ "T7:my-extra-rüle",
+ " T7 : my-extra-rüle ",
+ "\tT7:\tmy-extra-rüle\t",
+ "T7:\t\n \tmy-extra-rüle\t\n\n",
+ "title-match-regex:my-extra-rüle",
+ ]
for rule_qualifier in rule_qualifiers:
config_builder = LintConfigBuilder()
- config_builder.set_option(rule_qualifier, 'regex', "föo")
+ config_builder.set_option(rule_qualifier, "regex", "föo")
expected_rules = copy.deepcopy(default_rules)
- my_rule = rules.TitleRegexMatches({'regex': "föo"})
+ my_rule = rules.TitleRegexMatches({"regex": "föo"})
my_rule.id = rules.TitleRegexMatches.id + ":my-extra-rüle"
my_rule.name = rules.TitleRegexMatches.name + ":my-extra-rüle"
- expected_rules._rules[u'T7:my-extra-rüle'] = my_rule
+ expected_rules._rules["T7:my-extra-rüle"] = my_rule
self.assertEqual(config_builder.build().rules, expected_rules)
# assert that changing an option on the newly added rule is passed correctly to the RuleCollection
@@ -233,20 +247,20 @@ class LintConfigBuilderTests(BaseTestCase):
# to the same rule
for other_rule_qualifier in rule_qualifiers:
cb = config_builder.clone()
- cb.set_option(other_rule_qualifier, 'regex', other_rule_qualifier + "bōr")
+ cb.set_option(other_rule_qualifier, "regex", other_rule_qualifier + "bōr")
# before setting the expected rule option value correctly, the RuleCollection should be different
self.assertNotEqual(cb.build().rules, expected_rules)
# after setting the option on the expected rule, it should be equal
- my_rule.options['regex'].set(other_rule_qualifier + "bōr")
+ my_rule.options["regex"].set(other_rule_qualifier + "bōr")
self.assertEqual(cb.build().rules, expected_rules)
- my_rule.options['regex'].set("wrong")
+ my_rule.options["regex"].set("wrong")
def test_named_rules_negative(self):
# T7 = title-match-regex
# Invalid rule name
for invalid_name in ["", " ", " ", "\t", "\n", "å b", "å:b", "åb:", ":åb"]:
config_builder = LintConfigBuilder()
- config_builder.set_option(f"T7:{invalid_name}", 'regex', "tëst")
+ config_builder.set_option(f"T7:{invalid_name}", "regex", "tëst")
expected_msg = f"The rule-name part in 'T7:{invalid_name}' cannot contain whitespace, colons or be empty"
with self.assertRaisesMessage(LintConfigError, expected_msg):
config_builder.build()
diff --git a/gitlint-core/gitlint/tests/config/test_config_precedence.py b/gitlint-core/gitlint/tests/config/test_config_precedence.py
index aa4de88..22197e8 100644
--- a/gitlint-core/gitlint/tests/config/test_config_precedence.py
+++ b/gitlint-core/gitlint/tests/config/test_config_precedence.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
from io import StringIO
from click.testing import CliRunner
@@ -13,9 +11,10 @@ from gitlint.config import LintConfigBuilder
class LintConfigPrecedenceTests(BaseTestCase):
def setUp(self):
+ super().setUp()
self.cli = CliRunner()
- @patch('gitlint.cli.get_stdin_data', return_value="WIP:fö\n\nThis is å test message\n")
+ @patch("gitlint.cli.get_stdin_data", return_value="WIP:fö\n\nThis is å test message\n")
def test_config_precedence(self, _):
# TODO(jroovers): this test really only test verbosity, we need to do some refactoring to gitlint.cli
# to more easily test everything
@@ -28,60 +27,63 @@ class LintConfigPrecedenceTests(BaseTestCase):
config_path = self.get_sample_path("config/gitlintconfig")
# 1. commandline convenience flags
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["-vvv", "-c", "general.verbosity=2", "--config", config_path])
self.assertEqual(result.output, "")
self.assertEqual(stderr.getvalue(), "1: T5 Title contains the word 'WIP' (case-insensitive): \"WIP:fö\"\n")
# 2. environment variables
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
- result = self.cli.invoke(cli.cli, ["-c", "general.verbosity=2", "--config", config_path],
- env={"GITLINT_VERBOSITY": "3"})
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
+ result = self.cli.invoke(
+ cli.cli, ["-c", "general.verbosity=2", "--config", config_path], env={"GITLINT_VERBOSITY": "3"}
+ )
self.assertEqual(result.output, "")
self.assertEqual(stderr.getvalue(), "1: T5 Title contains the word 'WIP' (case-insensitive): \"WIP:fö\"\n")
# 3. commandline -c flags
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["-c", "general.verbosity=2", "--config", config_path])
self.assertEqual(result.output, "")
self.assertEqual(stderr.getvalue(), "1: T5 Title contains the word 'WIP' (case-insensitive)\n")
# 4. config file
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli, ["--config", config_path])
self.assertEqual(result.output, "")
self.assertEqual(stderr.getvalue(), "1: T5\n")
# 5. default config
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
result = self.cli.invoke(cli.cli)
self.assertEqual(result.output, "")
self.assertEqual(stderr.getvalue(), "1: T5 Title contains the word 'WIP' (case-insensitive): \"WIP:fö\"\n")
- @patch('gitlint.cli.get_stdin_data', return_value="WIP: This is å test")
+ @patch("gitlint.cli.get_stdin_data", return_value="WIP: This is å test")
def test_ignore_precedence(self, get_stdin_data):
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
# --ignore takes precedence over -c general.ignore
result = self.cli.invoke(cli.cli, ["-c", "general.ignore=T5", "--ignore", "B6"])
self.assertEqual(result.output, "")
self.assertEqual(result.exit_code, 1)
# We still expect the T5 violation, but no B6 violation as --ignore overwrites -c general.ignore
- self.assertEqual(stderr.getvalue(),
- "1: T5 Title contains the word 'WIP' (case-insensitive): \"WIP: This is å test\"\n")
+ self.assertEqual(
+ stderr.getvalue(), "1: T5 Title contains the word 'WIP' (case-insensitive): \"WIP: This is å test\"\n"
+ )
# test that we can also still configure a rule that is first ignored but then not
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
get_stdin_data.return_value = "This is å test"
# --ignore takes precedence over -c general.ignore
- result = self.cli.invoke(cli.cli, ["-c", "general.ignore=title-max-length",
- "-c", "title-max-length.line-length=5",
- "--ignore", "B6"])
+ result = self.cli.invoke(
+ cli.cli,
+ ["-c", "general.ignore=title-max-length", "-c", "title-max-length.line-length=5", "--ignore", "B6"],
+ )
self.assertEqual(result.output, "")
self.assertEqual(result.exit_code, 1)
# We still expect the T1 violation with custom config,
# but no B6 violation as --ignore overwrites -c general.ignore
- self.assertEqual(stderr.getvalue(), "1: T1 Title exceeds max length (14>5): \"This is å test\"\n")
+ self.assertEqual(stderr.getvalue(), '1: T1 Title exceeds max length (14>5): "This is å test"\n')
def test_general_option_after_rule_option(self):
# We used to have a bug where we didn't process general options before setting specific options, this would
@@ -89,10 +91,10 @@ class LintConfigPrecedenceTests(BaseTestCase):
# This test is here to test for regressions against this.
config_builder = LintConfigBuilder()
- config_builder.set_option(u'my-üser-commit-rule', 'violation-count', 3)
+ config_builder.set_option("my-üser-commit-rule", "violation-count", 3)
user_rules_path = self.get_sample_path("user_rules")
- config_builder.set_option('general', 'extra-path', user_rules_path)
+ config_builder.set_option("general", "extra-path", user_rules_path)
config = config_builder.build()
self.assertEqual(config.extra_path, user_rules_path)
- self.assertEqual(config.get_rule_option(u'my-üser-commit-rule', 'violation-count'), 3)
+ self.assertEqual(config.get_rule_option("my-üser-commit-rule", "violation-count"), 3)
diff --git a/gitlint-core/gitlint/tests/config/test_rule_collection.py b/gitlint-core/gitlint/tests/config/test_rule_collection.py
index 17b50cc..ea7039f 100644
--- a/gitlint-core/gitlint/tests/config/test_rule_collection.py
+++ b/gitlint-core/gitlint/tests/config/test_rule_collection.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
from collections import OrderedDict
from gitlint import rules
from gitlint.config import RuleCollection
@@ -7,7 +5,6 @@ from gitlint.tests.base import BaseTestCase
class RuleCollectionTests(BaseTestCase):
-
def test_add_rule(self):
collection = RuleCollection()
collection.add_rule(rules.TitleMaxLength, "my-rüle", {"my_attr": "föo", "my_attr2": 123})
@@ -29,18 +26,18 @@ class RuleCollectionTests(BaseTestCase):
# find by id
expected = rules.TitleMaxLength()
- rule = collection.find_rule('T1')
+ rule = collection.find_rule("T1")
self.assertEqual(rule, expected)
self.assertEqual(rule.my_attr, "föo")
# find by name
expected2 = rules.TitleTrailingWhitespace()
- rule = collection.find_rule('title-trailing-whitespace')
+ rule = collection.find_rule("title-trailing-whitespace")
self.assertEqual(rule, expected2)
self.assertEqual(rule.my_attr, "föo")
# find non-existing
- rule = collection.find_rule(u'föo')
+ rule = collection.find_rule("föo")
self.assertIsNone(rule)
def test_delete_rules_by_attr(self):
diff --git a/gitlint-core/gitlint/tests/contrib/rules/test_authors_commit.py b/gitlint-core/gitlint/tests/contrib/rules/test_authors_commit.py
new file mode 100644
index 0000000..5ea9d8f
--- /dev/null
+++ b/gitlint-core/gitlint/tests/contrib/rules/test_authors_commit.py
@@ -0,0 +1,106 @@
+from collections import namedtuple
+from unittest.mock import patch
+from gitlint.tests.base import BaseTestCase
+from gitlint.rules import RuleViolation
+from gitlint.config import LintConfig
+
+from gitlint.contrib.rules.authors_commit import AllowedAuthors
+
+
+class ContribAuthorsCommitTests(BaseTestCase):
+ def setUp(self):
+ author = namedtuple("Author", "name, email")
+ self.author_1 = author("John Doe", "john.doe@mail.com")
+ self.author_2 = author("Bob Smith", "bob.smith@mail.com")
+ self.rule = AllowedAuthors()
+ self.gitcontext = self.get_gitcontext()
+
+ def get_gitcontext(self):
+ gitcontext = self.gitcontext(self.get_sample("commit_message/sample1"))
+ gitcontext.repository_path = self.get_sample_path("config")
+ return gitcontext
+
+ def get_commit(self, name, email):
+ commit = self.gitcommit("commit_message/sample1", author_name=name, author_email=email)
+ commit.message.context = self.gitcontext
+ return commit
+
+ def test_enable(self):
+ for rule_ref in ["CC3", "contrib-allowed-authors"]:
+ config = LintConfig()
+ config.contrib = [rule_ref]
+ self.assertIn(AllowedAuthors(), config.rules)
+
+ def test_authors_succeeds(self):
+ for author in [self.author_1, self.author_2]:
+ commit = self.get_commit(author.name, author.email)
+ violations = self.rule.validate(commit)
+ self.assertListEqual([], violations)
+
+ def test_authors_email_is_case_insensitive(self):
+ for email in [
+ self.author_2.email.capitalize(),
+ self.author_2.email.lower(),
+ self.author_2.email.upper(),
+ ]:
+ commit = self.get_commit(self.author_2.name, email)
+ violations = self.rule.validate(commit)
+ self.assertListEqual([], violations)
+
+ def test_authors_name_is_case_sensitive(self):
+ for name in [self.author_2.name.lower(), self.author_2.name.upper()]:
+ commit = self.get_commit(name, self.author_2.email)
+ violations = self.rule.validate(commit)
+ expected_violation = RuleViolation(
+ "CC3",
+ f"Author not in 'AUTHORS' file: " f'"{name} <{self.author_2.email}>"',
+ )
+ self.assertListEqual([expected_violation], violations)
+
+ def test_authors_bad_name_fails(self):
+ for name in ["", "root"]:
+ commit = self.get_commit(name, self.author_2.email)
+ violations = self.rule.validate(commit)
+ expected_violation = RuleViolation(
+ "CC3",
+ f"Author not in 'AUTHORS' file: " f'"{name} <{self.author_2.email}>"',
+ )
+ self.assertListEqual([expected_violation], violations)
+
+ def test_authors_bad_email_fails(self):
+ for email in ["", "root@example.com"]:
+ commit = self.get_commit(self.author_2.name, email)
+ violations = self.rule.validate(commit)
+ expected_violation = RuleViolation(
+ "CC3",
+ f"Author not in 'AUTHORS' file: " f'"{self.author_2.name} <{email}>"',
+ )
+ self.assertListEqual([expected_violation], violations)
+
+ def test_authors_invalid_combination_fails(self):
+ commit = self.get_commit(self.author_1.name, self.author_2.email)
+ violations = self.rule.validate(commit)
+ expected_violation = RuleViolation(
+ "CC3",
+ f"Author not in 'AUTHORS' file: " f'"{self.author_1.name} <{self.author_2.email}>"',
+ )
+ self.assertListEqual([expected_violation], violations)
+
+ @patch(
+ "gitlint.contrib.rules.authors_commit.Path.read_text",
+ return_value="John Doe <john.doe@mail.com>",
+ )
+ def test_read_authors_file(self, _mock_read_text):
+ authors, authors_file_name = AllowedAuthors._read_authors_from_file(self.gitcontext)
+ self.assertEqual(authors_file_name, "AUTHORS")
+ self.assertEqual(len(authors), 1)
+ self.assertEqual(authors, {self.author_1})
+
+ @patch(
+ "gitlint.contrib.rules.authors_commit.Path.exists",
+ return_value=False,
+ )
+ def test_read_authors_file_missing_file(self, _mock_iterdir):
+ with self.assertRaises(FileNotFoundError) as err:
+ AllowedAuthors._read_authors_from_file(self.gitcontext)
+ self.assertEqual(err.exception.args[0], "AUTHORS file not found")
diff --git a/gitlint-core/gitlint/tests/contrib/rules/test_conventional_commit.py b/gitlint-core/gitlint/tests/contrib/rules/test_conventional_commit.py
index 5da5cd5..7ce9c89 100644
--- a/gitlint-core/gitlint/tests/contrib/rules/test_conventional_commit.py
+++ b/gitlint-core/gitlint/tests/contrib/rules/test_conventional_commit.py
@@ -1,5 +1,3 @@
-
-# -*- coding: utf-8 -*-
from gitlint.tests.base import BaseTestCase
from gitlint.rules import RuleViolation
from gitlint.contrib.rules.conventional_commit import ConventionalCommit
@@ -7,10 +5,9 @@ from gitlint.config import LintConfig
class ContribConventionalCommitTests(BaseTestCase):
-
def test_enable(self):
# Test that rule can be enabled in config
- for rule_ref in ['CT1', 'contrib-title-conventional-commits']:
+ for rule_ref in ["CT1", "contrib-title-conventional-commits"]:
config = LintConfig()
config.contrib = [rule_ref]
self.assertIn(ConventionalCommit(), config.rules)
@@ -24,28 +21,38 @@ class ContribConventionalCommitTests(BaseTestCase):
self.assertListEqual([], violations)
# assert violation on wrong type
- expected_violation = RuleViolation("CT1", "Title does not start with one of fix, feat, chore, docs,"
- " style, refactor, perf, test, revert, ci, build", "bår: foo")
+ expected_violation = RuleViolation(
+ "CT1",
+ "Title does not start with one of fix, feat, chore, docs, style, refactor, perf, test, revert, ci, build",
+ "bår: foo",
+ )
violations = rule.validate("bår: foo", None)
self.assertListEqual([expected_violation], violations)
# assert violation when use strange chars after correct type
- expected_violation = RuleViolation("CT1", "Title does not start with one of fix, feat, chore, docs,"
- " style, refactor, perf, test, revert, ci, build",
- "feat_wrong_chars: föo")
+ expected_violation = RuleViolation(
+ "CT1",
+ "Title does not start with one of fix, feat, chore, docs, style, refactor, perf, test, revert, ci, build",
+ "feat_wrong_chars: föo",
+ )
violations = rule.validate("feat_wrong_chars: föo", None)
self.assertListEqual([expected_violation], violations)
# assert violation when use strange chars after correct type
- expected_violation = RuleViolation("CT1", "Title does not start with one of fix, feat, chore, docs,"
- " style, refactor, perf, test, revert, ci, build",
- "feat_wrong_chars(scope): föo")
+ expected_violation = RuleViolation(
+ "CT1",
+ "Title does not start with one of fix, feat, chore, docs, style, refactor, perf, test, revert, ci, build",
+ "feat_wrong_chars(scope): föo",
+ )
violations = rule.validate("feat_wrong_chars(scope): föo", None)
self.assertListEqual([expected_violation], violations)
# assert violation on wrong format
- expected_violation = RuleViolation("CT1", "Title does not follow ConventionalCommits.org format "
- "'type(optional-scope): description'", "fix föo")
+ expected_violation = RuleViolation(
+ "CT1",
+ "Title does not follow ConventionalCommits.org format 'type(optional-scope): description'",
+ "fix föo",
+ )
violations = rule.validate("fix föo", None)
self.assertListEqual([expected_violation], violations)
@@ -58,7 +65,7 @@ class ContribConventionalCommitTests(BaseTestCase):
self.assertListEqual([], violations)
# assert no violation when adding new type
- rule = ConventionalCommit({'types': ["föo", "bär"]})
+ rule = ConventionalCommit({"types": ["föo", "bär"]})
for typ in ["föo", "bär"]:
violations = rule.validate(typ + ": hür dur", None)
self.assertListEqual([], violations)
@@ -69,7 +76,7 @@ class ContribConventionalCommitTests(BaseTestCase):
self.assertListEqual([expected_violation], violations)
# assert no violation when adding new type named with numbers
- rule = ConventionalCommit({'types': ["föo123", "123bär"]})
+ rule = ConventionalCommit({"types": ["föo123", "123bär"]})
for typ in ["föo123", "123bär"]:
violations = rule.validate(typ + ": hür dur", None)
self.assertListEqual([], violations)
diff --git a/gitlint-core/gitlint/tests/contrib/rules/test_disallow_cleanup_commits.py b/gitlint-core/gitlint/tests/contrib/rules/test_disallow_cleanup_commits.py
new file mode 100644
index 0000000..841640a
--- /dev/null
+++ b/gitlint-core/gitlint/tests/contrib/rules/test_disallow_cleanup_commits.py
@@ -0,0 +1,35 @@
+from gitlint.tests.base import BaseTestCase
+from gitlint.rules import RuleViolation
+from gitlint.contrib.rules.disallow_cleanup_commits import DisallowCleanupCommits
+
+from gitlint.config import LintConfig
+
+
+class ContribDisallowCleanupCommitsTest(BaseTestCase):
+ def test_enable(self):
+ # Test that rule can be enabled in config
+ for rule_ref in ["CC2", "contrib-disallow-cleanup-commits"]:
+ config = LintConfig()
+ config.contrib = [rule_ref]
+ self.assertIn(DisallowCleanupCommits(), config.rules)
+
+ def test_disallow_fixup_squash_commit(self):
+ # No violations when no 'fixup!' line and no 'squash!' line is present
+ rule = DisallowCleanupCommits()
+ violations = rule.validate(self.gitcommit("Föobar\n\nMy Body"))
+ self.assertListEqual(violations, [])
+
+ # Assert violation when 'fixup!' in title
+ violations = rule.validate(self.gitcommit("fixup! Föobar\n\nMy Body"))
+ expected_violation = RuleViolation("CC2", "Fixup commits are not allowed", line_nr=1)
+ self.assertListEqual(violations, [expected_violation])
+
+ # Assert violation when 'squash!' in title
+ violations = rule.validate(self.gitcommit("squash! Föobar\n\nMy Body"))
+ expected_violation = RuleViolation("CC2", "Squash commits are not allowed", line_nr=1)
+ self.assertListEqual(violations, [expected_violation])
+
+ # Assert violation when 'amend!' in title
+ violations = rule.validate(self.gitcommit("amend! Föobar\n\nMy Body"))
+ expected_violation = RuleViolation("CC2", "Amend commits are not allowed", line_nr=1)
+ self.assertListEqual(violations, [expected_violation])
diff --git a/gitlint-core/gitlint/tests/contrib/rules/test_signedoff_by.py b/gitlint-core/gitlint/tests/contrib/rules/test_signedoff_by.py
index 0369cdc..88ff1db 100644
--- a/gitlint-core/gitlint/tests/contrib/rules/test_signedoff_by.py
+++ b/gitlint-core/gitlint/tests/contrib/rules/test_signedoff_by.py
@@ -1,5 +1,3 @@
-
-# -*- coding: utf-8 -*-
from gitlint.tests.base import BaseTestCase
from gitlint.rules import RuleViolation
from gitlint.contrib.rules.signedoff_by import SignedOffBy
@@ -8,10 +6,9 @@ from gitlint.config import LintConfig
class ContribSignedOffByTests(BaseTestCase):
-
def test_enable(self):
# Test that rule can be enabled in config
- for rule_ref in ['CC1', 'contrib-body-requires-signed-off-by']:
+ for rule_ref in ["CC1", "contrib-body-requires-signed-off-by"]:
config = LintConfig()
config.contrib = [rule_ref]
self.assertIn(SignedOffBy(), config.rules)
diff --git a/gitlint-core/gitlint/tests/contrib/test_contrib_rules.py b/gitlint-core/gitlint/tests/contrib/test_contrib_rules.py
index 8ab6539..bd098c6 100644
--- a/gitlint-core/gitlint/tests/contrib/test_contrib_rules.py
+++ b/gitlint-core/gitlint/tests/contrib/test_contrib_rules.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
import os
from gitlint.tests.base import BaseTestCase
@@ -8,13 +7,12 @@ from gitlint import rule_finder, rules
class ContribRuleTests(BaseTestCase):
-
CONTRIB_DIR = os.path.dirname(os.path.realpath(contrib_rules.__file__))
def test_contrib_tests_exist(self):
- """ Tests that every contrib rule file has an associated test file.
- While this doesn't guarantee that every contrib rule has associated tests (as we don't check the content
- of the tests file), it's a good leading indicator. """
+ """Tests that every contrib rule file has an associated test file.
+ While this doesn't guarantee that every contrib rule has associated tests (as we don't check the content
+ of the tests file), it's a good leading indicator."""
contrib_tests_dir = os.path.dirname(os.path.realpath(contrib_tests.__file__))
contrib_test_files = os.listdir(contrib_tests_dir)
@@ -22,16 +20,18 @@ class ContribRuleTests(BaseTestCase):
# Find all python files in the contrib dir and assert there's a corresponding test file
for filename in os.listdir(self.CONTRIB_DIR):
if filename.endswith(".py") and filename not in ["__init__.py"]:
- expected_test_file = "test_" + filename
- error_msg = "Every Contrib Rule must have associated tests. " + \
- f"Expected test file {os.path.join(contrib_tests_dir, expected_test_file)} not found."
+ expected_test_file = f"test_{filename}"
+ error_msg = (
+ "Every Contrib Rule must have associated tests. "
+ f"Expected test file {os.path.join(contrib_tests_dir, expected_test_file)} not found."
+ )
self.assertIn(expected_test_file, contrib_test_files, error_msg)
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.
+ """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)
@@ -47,10 +47,10 @@ class ContribRuleTests(BaseTestCase):
self.assertTrue(clazz.id.startswith("CB"))
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.
+ """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)
@@ -61,7 +61,7 @@ class ContribRuleTests(BaseTestCase):
self.assertEqual(len(set(class_ids)), len(class_ids))
def test_contrib_rule_instantiated(self):
- """ Tests that all contrib rules can be instantiated without errors. """
+ """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 :-)
diff --git a/gitlint-core/gitlint/tests/expected/cli/test_cli/test_debug_1 b/gitlint-core/gitlint/tests/expected/cli/test_cli/test_debug_1
index fcd5d7e..4bd3b7d 100644
--- a/gitlint-core/gitlint/tests/expected/cli/test_cli/test_debug_1
+++ b/gitlint-core/gitlint/tests/expected/cli/test_cli/test_debug_1
@@ -13,11 +13,13 @@ contrib: []
ignore: title-trailing-whitespace,B2
ignore-merge-commits: False
ignore-fixup-commits: True
+ignore-fixup-amend-commits: True
ignore-squash-commits: True
ignore-revert-commits: True
ignore-stdin: False
staged: False
fail-without-commits: False
+regex-style-search: False
verbosity: 1
debug: True
target: {target}
@@ -59,7 +61,7 @@ target: {target}
B8: body-match-regex
regex=None
M1: author-valid-email
- regex=[^@ ]+@[^@ ]+\.[^@ ]+
+ regex=^[^@ ]+@[^@ ]+\.[^@ ]+
DEBUG: gitlint.cli No --msg-filename flag, no or empty data passed to stdin. Using the local repo.
DEBUG: gitlint.git ('rev-list', 'foo...bar')
@@ -67,8 +69,8 @@ DEBUG: gitlint.cli Linting 3 commit(s)
DEBUG: gitlint.git ('log', '6f29bf81a8322a04071bb794666e48c443a90360', '-1', '--pretty=%aN%x00%aE%x00%ai%x00%P%n%B')
DEBUG: gitlint.git ('config', '--get', 'core.commentchar')
DEBUG: gitlint.lint Linting commit 6f29bf81a8322a04071bb794666e48c443a90360
+DEBUG: gitlint.git ('diff-tree', '--no-commit-id', '--numstat', '-r', '--root', '6f29bf81a8322a04071bb794666e48c443a90360')
DEBUG: gitlint.git ('branch', '--contains', '6f29bf81a8322a04071bb794666e48c443a90360')
-DEBUG: gitlint.git ('diff-tree', '--no-commit-id', '--name-only', '-r', '--root', '6f29bf81a8322a04071bb794666e48c443a90360')
DEBUG: gitlint.lint Commit Object
--- Commit Message ----
commït-title1
@@ -79,15 +81,20 @@ Author: test åuthor1 <test-email1@föo.com>
Date: 2016-12-03 15:28:15 +0100
is-merge-commit: False
is-fixup-commit: False
+is-fixup-amend-commit: False
is-squash-commit: False
is-revert-commit: False
+Parents: ['a123']
Branches: ['commit-1-branch-1', 'commit-1-branch-2']
Changed Files: ['commit-1/file-1', 'commit-1/file-2']
+Changed Files Stats:
+ commit-1/file-1: 5 additions, 8 deletions
+ commit-1/file-2: 2 additions, 9 deletions
-----------------------
DEBUG: gitlint.git ('log', '25053ccec5e28e1bb8f7551fdbb5ab213ada2401', '-1', '--pretty=%aN%x00%aE%x00%ai%x00%P%n%B')
DEBUG: gitlint.lint Linting commit 25053ccec5e28e1bb8f7551fdbb5ab213ada2401
+DEBUG: gitlint.git ('diff-tree', '--no-commit-id', '--numstat', '-r', '--root', '25053ccec5e28e1bb8f7551fdbb5ab213ada2401')
DEBUG: gitlint.git ('branch', '--contains', '25053ccec5e28e1bb8f7551fdbb5ab213ada2401')
-DEBUG: gitlint.git ('diff-tree', '--no-commit-id', '--name-only', '-r', '--root', '25053ccec5e28e1bb8f7551fdbb5ab213ada2401')
DEBUG: gitlint.lint Commit Object
--- Commit Message ----
commït-title2.
@@ -98,15 +105,20 @@ Author: test åuthor2 <test-email2@föo.com>
Date: 2016-12-04 15:28:15 +0100
is-merge-commit: False
is-fixup-commit: False
+is-fixup-amend-commit: False
is-squash-commit: False
is-revert-commit: False
+Parents: ['b123']
Branches: ['commit-2-branch-1', 'commit-2-branch-2']
Changed Files: ['commit-2/file-1', 'commit-2/file-2']
+Changed Files Stats:
+ commit-2/file-1: 5 additions, 8 deletions
+ commit-2/file-2: 7 additions, 9 deletions
-----------------------
DEBUG: gitlint.git ('log', '4da2656b0dadc76c7ee3fd0243a96cb64007f125', '-1', '--pretty=%aN%x00%aE%x00%ai%x00%P%n%B')
DEBUG: gitlint.lint Linting commit 4da2656b0dadc76c7ee3fd0243a96cb64007f125
+DEBUG: gitlint.git ('diff-tree', '--no-commit-id', '--numstat', '-r', '--root', '4da2656b0dadc76c7ee3fd0243a96cb64007f125')
DEBUG: gitlint.git ('branch', '--contains', '4da2656b0dadc76c7ee3fd0243a96cb64007f125')
-DEBUG: gitlint.git ('diff-tree', '--no-commit-id', '--name-only', '-r', '--root', '4da2656b0dadc76c7ee3fd0243a96cb64007f125')
DEBUG: gitlint.lint Commit Object
--- Commit Message ----
föobar
@@ -116,9 +128,14 @@ Author: test åuthor3 <test-email3@föo.com>
Date: 2016-12-05 15:28:15 +0100
is-merge-commit: False
is-fixup-commit: False
+is-fixup-amend-commit: False
is-squash-commit: False
is-revert-commit: False
+Parents: ['c123']
Branches: ['commit-3-branch-1', 'commit-3-branch-2']
Changed Files: ['commit-3/file-1', 'commit-3/file-2']
+Changed Files Stats:
+ commit-3/file-1: 1 additions, 4 deletions
+ commit-3/file-2: 3 additions, 4 deletions
-----------------------
DEBUG: gitlint.cli Exit Code = 6 \ No newline at end of file
diff --git a/gitlint-core/gitlint/tests/expected/cli/test_cli/test_input_stream_debug_2 b/gitlint-core/gitlint/tests/expected/cli/test_cli/test_input_stream_debug_2
index 7c94b45..6d6da43 100644
--- a/gitlint-core/gitlint/tests/expected/cli/test_cli/test_input_stream_debug_2
+++ b/gitlint-core/gitlint/tests/expected/cli/test_cli/test_input_stream_debug_2
@@ -13,11 +13,13 @@ contrib: []
ignore:
ignore-merge-commits: True
ignore-fixup-commits: True
+ignore-fixup-amend-commits: True
ignore-squash-commits: True
ignore-revert-commits: True
ignore-stdin: False
staged: False
fail-without-commits: False
+regex-style-search: False
verbosity: 3
debug: True
target: {target}
@@ -59,7 +61,7 @@ target: {target}
B8: body-match-regex
regex=None
M1: author-valid-email
- regex=[^@ ]+@[^@ ]+\.[^@ ]+
+ regex=^[^@ ]+@[^@ ]+\.[^@ ]+
DEBUG: gitlint.cli Stdin data: 'WIP: tïtle
'
@@ -75,9 +77,12 @@ Author: None <None>
Date: None
is-merge-commit: False
is-fixup-commit: False
+is-fixup-amend-commit: False
is-squash-commit: False
is-revert-commit: False
+Parents: []
Branches: []
Changed Files: []
+Changed Files Stats: {{}}
-----------------------
DEBUG: gitlint.cli Exit Code = 3 \ No newline at end of file
diff --git a/gitlint-core/gitlint/tests/expected/cli/test_cli/test_lint_staged_msg_filename_2 b/gitlint-core/gitlint/tests/expected/cli/test_cli/test_lint_staged_msg_filename_2
index f37ffa0..59b2414 100644
--- a/gitlint-core/gitlint/tests/expected/cli/test_cli/test_lint_staged_msg_filename_2
+++ b/gitlint-core/gitlint/tests/expected/cli/test_cli/test_lint_staged_msg_filename_2
@@ -13,11 +13,13 @@ contrib: []
ignore:
ignore-merge-commits: True
ignore-fixup-commits: True
+ignore-fixup-amend-commits: True
ignore-squash-commits: True
ignore-revert-commits: True
ignore-stdin: False
staged: True
fail-without-commits: False
+regex-style-search: False
verbosity: 3
debug: True
target: {target}
@@ -59,17 +61,17 @@ target: {target}
B8: body-match-regex
regex=None
M1: author-valid-email
- regex=[^@ ]+@[^@ ]+\.[^@ ]+
+ regex=^[^@ ]+@[^@ ]+\.[^@ ]+
DEBUG: gitlint.cli Fetching additional meta-data from staged commit
DEBUG: gitlint.cli Using --msg-filename.
DEBUG: gitlint.git ('config', '--get', 'core.commentchar')
DEBUG: gitlint.cli Linting 1 commit(s)
DEBUG: gitlint.lint Linting commit [SHA UNKNOWN]
+DEBUG: gitlint.git ('diff', '--staged', '--numstat', '-r')
DEBUG: gitlint.git ('config', '--get', 'user.name')
DEBUG: gitlint.git ('config', '--get', 'user.email')
DEBUG: gitlint.git ('rev-parse', '--abbrev-ref', 'HEAD')
-DEBUG: gitlint.git ('diff', '--staged', '--name-only', '-r')
DEBUG: gitlint.lint Commit Object
--- Commit Message ----
WIP: msg-filename tïtle
@@ -78,9 +80,14 @@ Author: föo user <föo@bar.com>
Date: 2020-02-19 12:18:46 +0100
is-merge-commit: False
is-fixup-commit: False
+is-fixup-amend-commit: False
is-squash-commit: False
is-revert-commit: False
+Parents: []
Branches: ['my-branch']
Changed Files: ['commit-1/file-1', 'commit-1/file-2']
+Changed Files Stats:
+ commit-1/file-1: 3 additions, 4 deletions
+ commit-1/file-2: 4 additions, 7 deletions
-----------------------
DEBUG: gitlint.cli Exit Code = 2 \ No newline at end of file
diff --git a/gitlint-core/gitlint/tests/expected/cli/test_cli/test_lint_staged_stdin_2 b/gitlint-core/gitlint/tests/expected/cli/test_cli/test_lint_staged_stdin_2
index 1d1020a..23df7b2 100644
--- a/gitlint-core/gitlint/tests/expected/cli/test_cli/test_lint_staged_stdin_2
+++ b/gitlint-core/gitlint/tests/expected/cli/test_cli/test_lint_staged_stdin_2
@@ -13,11 +13,13 @@ contrib: []
ignore:
ignore-merge-commits: True
ignore-fixup-commits: True
+ignore-fixup-amend-commits: True
ignore-squash-commits: True
ignore-revert-commits: True
ignore-stdin: False
staged: True
fail-without-commits: False
+regex-style-search: False
verbosity: 3
debug: True
target: {target}
@@ -59,7 +61,7 @@ target: {target}
B8: body-match-regex
regex=None
M1: author-valid-email
- regex=[^@ ]+@[^@ ]+\.[^@ ]+
+ regex=^[^@ ]+@[^@ ]+\.[^@ ]+
DEBUG: gitlint.cli Fetching additional meta-data from staged commit
DEBUG: gitlint.cli Stdin data: 'WIP: tïtle
@@ -68,10 +70,10 @@ DEBUG: gitlint.cli Stdin detected and not ignored. Using as input.
DEBUG: gitlint.git ('config', '--get', 'core.commentchar')
DEBUG: gitlint.cli Linting 1 commit(s)
DEBUG: gitlint.lint Linting commit [SHA UNKNOWN]
+DEBUG: gitlint.git ('diff', '--staged', '--numstat', '-r')
DEBUG: gitlint.git ('config', '--get', 'user.name')
DEBUG: gitlint.git ('config', '--get', 'user.email')
DEBUG: gitlint.git ('rev-parse', '--abbrev-ref', 'HEAD')
-DEBUG: gitlint.git ('diff', '--staged', '--name-only', '-r')
DEBUG: gitlint.lint Commit Object
--- Commit Message ----
WIP: tïtle
@@ -80,9 +82,14 @@ Author: föo user <föo@bar.com>
Date: 2020-02-19 12:18:46 +0100
is-merge-commit: False
is-fixup-commit: False
+is-fixup-amend-commit: False
is-squash-commit: False
is-revert-commit: False
+Parents: []
Branches: ['my-branch']
Changed Files: ['commit-1/file-1', 'commit-1/file-2']
+Changed Files Stats:
+ commit-1/file-1: 1 additions, 5 deletions
+ commit-1/file-2: 8 additions, 9 deletions
-----------------------
DEBUG: gitlint.cli Exit Code = 3 \ No newline at end of file
diff --git a/gitlint-core/gitlint/tests/expected/cli/test_cli/test_named_rules_2 b/gitlint-core/gitlint/tests/expected/cli/test_cli/test_named_rules_2
index 83c4bf2..c4491f1 100644
--- a/gitlint-core/gitlint/tests/expected/cli/test_cli/test_named_rules_2
+++ b/gitlint-core/gitlint/tests/expected/cli/test_cli/test_named_rules_2
@@ -13,11 +13,13 @@ contrib: []
ignore:
ignore-merge-commits: True
ignore-fixup-commits: True
+ignore-fixup-amend-commits: True
ignore-squash-commits: True
ignore-revert-commits: True
ignore-stdin: False
staged: False
fail-without-commits: False
+regex-style-search: False
verbosity: 3
debug: True
target: {target}
@@ -59,7 +61,7 @@ target: {target}
B8: body-match-regex
regex=None
M1: author-valid-email
- regex=[^@ ]+@[^@ ]+\.[^@ ]+
+ regex=^[^@ ]+@[^@ ]+\.[^@ ]+
T5:extra-wörds: title-must-not-contain-word:extra-wörds
words=hür,tëst
T5:even-more-wörds: title-must-not-contain-word:even-more-wörds
@@ -78,9 +80,12 @@ Author: None <None>
Date: None
is-merge-commit: False
is-fixup-commit: False
+is-fixup-amend-commit: False
is-squash-commit: False
is-revert-commit: False
+Parents: []
Branches: []
Changed Files: []
+Changed Files Stats: {{}}
-----------------------
DEBUG: gitlint.cli Exit Code = 4 \ No newline at end of file
diff --git a/gitlint-core/gitlint/tests/git/test_git.py b/gitlint-core/gitlint/tests/git/test_git.py
index 7b9b7c6..9c73bd9 100644
--- a/gitlint-core/gitlint/tests/git/test_git.py
+++ b/gitlint-core/gitlint/tests/git/test_git.py
@@ -1,7 +1,6 @@
-# -*- coding: utf-8 -*-
import os
-from unittest.mock import patch
+from unittest.mock import patch, call
from gitlint.shell import ErrorReturnCode, CommandNotFound
@@ -10,25 +9,23 @@ from gitlint.git import GitContext, GitContextError, GitNotInstalledError, git_c
class GitTests(BaseTestCase):
-
# Expected special_args passed to 'sh'
- expected_sh_special_args = {
- '_tty_out': False,
- '_cwd': "fåke/path"
- }
+ expected_sh_special_args = {"_tty_out": False, "_cwd": "fåke/path"}
- @patch('gitlint.git.sh')
+ @patch("gitlint.git.sh")
def test_get_latest_commit_command_not_found(self, sh):
sh.git.side_effect = CommandNotFound("git")
- expected_msg = "'git' command not found. You need to install git to use gitlint on a local repository. " + \
- "See https://git-scm.com/book/en/v2/Getting-Started-Installing-Git on how to install git."
+ expected_msg = (
+ "'git' command not found. You need to install git to use gitlint on a local repository. "
+ + "See https://git-scm.com/book/en/v2/Getting-Started-Installing-Git on how to install git."
+ )
with self.assertRaisesMessage(GitNotInstalledError, expected_msg):
GitContext.from_local_repository("fåke/path")
# assert that commit message was read using git command
sh.git.assert_called_once_with("log", "-1", "--pretty=%H", **self.expected_sh_special_args)
- @patch('gitlint.git.sh')
+ @patch("gitlint.git.sh")
def test_get_latest_commit_git_error(self, sh):
# Current directory not a git repo
err = b"fatal: Not a git repository (or any of the parent directories): .git"
@@ -51,10 +48,10 @@ class GitTests(BaseTestCase):
# assert that commit message was read using git command
sh.git.assert_called_once_with("log", "-1", "--pretty=%H", **self.expected_sh_special_args)
- @patch('gitlint.git.sh')
+ @patch("gitlint.git.sh")
def test_git_no_commits_error(self, sh):
# No commits: returned by 'git log'
- err = b"fatal: your current branch 'master' does not have any commits yet"
+ err = b"fatal: your current branch 'main' does not have any commits yet"
sh.git.side_effect = ErrorReturnCode("git log -1 --pretty=%H", b"", err)
@@ -64,25 +61,38 @@ class GitTests(BaseTestCase):
# assert that commit message was read using git command
sh.git.assert_called_once_with("log", "-1", "--pretty=%H", **self.expected_sh_special_args)
- sh.git.reset_mock()
+ @patch("gitlint.git.sh")
+ def test_git_no_commits_get_branch(self, sh):
+ """Check that we can still read the current branch name when there's no commits. This is useful when
+ when trying to lint the first commit using the --staged flag.
+ """
# Unknown reference 'HEAD' commits: returned by 'git rev-parse'
- err = (b"HEAD"
- b"fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree."
- b"Use '--' to separate paths from revisions, like this:"
- b"'git <command> [<revision>...] -- [<file>...]'")
+ err = (
+ b"HEAD"
+ b"fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree."
+ b"Use '--' to separate paths from revisions, like this:"
+ b"'git <command> [<revision>...] -- [<file>...]'"
+ )
sh.git.side_effect = [
"#\n", # git config --get core.commentchar
- ErrorReturnCode("rev-parse --abbrev-ref HEAD", b"", err)
+ ErrorReturnCode("rev-parse --abbrev-ref HEAD", b"", err),
+ "test-branch", # git branch --show-current
]
- with self.assertRaisesMessage(GitContextError, expected_msg):
- context = GitContext.from_commit_msg("test")
- context.current_branch
+ context = GitContext.from_commit_msg("test")
+ self.assertEqual(context.current_branch, "test-branch")
- # assert that commit message was read using git command
- sh.git.assert_called_with("rev-parse", "--abbrev-ref", "HEAD", _tty_out=False, _cwd=None)
+ # assert that we try using `git rev-parse` first, and if that fails (as will be the case with the first commit),
+ # we fallback to `git branch --show-current` to determine the current branch name.
+ expected_calls = [
+ call("config", "--get", "core.commentchar", _tty_out=False, _cwd=None, _ok_code=[0, 1]),
+ call("rev-parse", "--abbrev-ref", "HEAD", _tty_out=False, _cwd=None),
+ call("branch", "--show-current", _tty_out=False, _cwd=None),
+ ]
+
+ self.assertEqual(sh.git.mock_calls, expected_calls)
@patch("gitlint.git._git")
def test_git_commentchar(self, git):
@@ -93,11 +103,10 @@ class GitTests(BaseTestCase):
git.return_value = "ä"
self.assertEqual(git_commentchar(), "ä")
- git.return_value = ';\n'
- self.assertEqual(git_commentchar(os.path.join("/föo", "bar")), ';')
+ git.return_value = ";\n"
+ self.assertEqual(git_commentchar(os.path.join("/föo", "bar")), ";")
- git.assert_called_with("config", "--get", "core.commentchar", _ok_code=[0, 1],
- _cwd=os.path.join("/föo", "bar"))
+ git.assert_called_with("config", "--get", "core.commentchar", _ok_code=[0, 1], _cwd=os.path.join("/föo", "bar"))
@patch("gitlint.git._git")
def test_git_hooks_dir(self, git):
diff --git a/gitlint-core/gitlint/tests/git/test_git_commit.py b/gitlint-core/gitlint/tests/git/test_git_commit.py
index 02c5795..b27deaf 100644
--- a/gitlint-core/gitlint/tests/git/test_git_commit.py
+++ b/gitlint-core/gitlint/tests/git/test_git_commit.py
@@ -1,6 +1,6 @@
-# -*- coding: utf-8 -*-
import copy
import datetime
+from pathlib import Path
import dateutil
@@ -9,29 +9,33 @@ import arrow
from unittest.mock import patch, call
from gitlint.tests.base import BaseTestCase
-from gitlint.git import GitContext, GitCommit, GitContextError, LocalGitCommit, StagedLocalGitCommit, GitCommitMessage
+from gitlint.git import (
+ GitChangedFileStats,
+ GitContext,
+ GitCommit,
+ GitContextError,
+ LocalGitCommit,
+ StagedLocalGitCommit,
+ GitCommitMessage,
+ GitChangedFileStats,
+)
from gitlint.shell import ErrorReturnCode
class GitCommitTests(BaseTestCase):
-
# Expected special_args passed to 'sh'
- expected_sh_special_args = {
- '_tty_out': False,
- '_cwd': "fåke/path"
- }
+ expected_sh_special_args = {"_tty_out": False, "_cwd": "fåke/path"}
- @patch('gitlint.git.sh')
+ @patch("gitlint.git.sh")
def test_get_latest_commit(self, sh):
sample_sha = "d8ac47e9f2923c7f22d8668e3a1ed04eb4cdbca9"
sh.git.side_effect = [
sample_sha,
- "test åuthor\x00test-emåil@foo.com\x002016-12-03 15:28:15 +0100\x00åbc\n"
- "cömmit-title\n\ncömmit-body",
+ "test åuthor\x00test-emåil@foo.com\x002016-12-03 15:28:15 +0100\x00åbc\ncömmit-title\n\ncömmit-body",
"#", # git config --get core.commentchar
- "file1.txt\npåth/to/file2.txt\n",
- "foöbar\n* hürdur\n"
+ "4\t15\tfile1.txt\n-\t-\tpåth/to/file2.bin\n",
+ "foöbar\n* hürdur\n",
]
context = GitContext.from_local_repository("fåke/path")
@@ -39,10 +43,17 @@ class GitCommitTests(BaseTestCase):
expected_calls = [
call("log", "-1", "--pretty=%H", **self.expected_sh_special_args),
call("log", sample_sha, "-1", "--pretty=%aN%x00%aE%x00%ai%x00%P%n%B", **self.expected_sh_special_args),
- call('config', '--get', 'core.commentchar', _ok_code=[0, 1], **self.expected_sh_special_args),
- call('diff-tree', '--no-commit-id', '--name-only', '-r', '--root', sample_sha,
- **self.expected_sh_special_args),
- call('branch', '--contains', sample_sha, **self.expected_sh_special_args)
+ call("config", "--get", "core.commentchar", _ok_code=[0, 1], **self.expected_sh_special_args),
+ call(
+ "diff-tree",
+ "--no-commit-id",
+ "--numstat",
+ "-r",
+ "--root",
+ sample_sha,
+ **self.expected_sh_special_args,
+ ),
+ call("branch", "--contains", sample_sha, **self.expected_sh_special_args),
]
# Only first 'git log' call should've happened at this point
@@ -55,18 +66,26 @@ class GitCommitTests(BaseTestCase):
self.assertEqual(last_commit.message.body, ["", "cömmit-body"])
self.assertEqual(last_commit.author_name, "test åuthor")
self.assertEqual(last_commit.author_email, "test-emåil@foo.com")
- self.assertEqual(last_commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15,
- tzinfo=dateutil.tz.tzoffset("+0100", 3600)))
+ self.assertEqual(
+ last_commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15, tzinfo=dateutil.tz.tzoffset("+0100", 3600))
+ )
self.assertListEqual(last_commit.parents, ["åbc"])
self.assertFalse(last_commit.is_merge_commit)
self.assertFalse(last_commit.is_fixup_commit)
+ self.assertFalse(last_commit.is_fixup_amend_commit)
self.assertFalse(last_commit.is_squash_commit)
self.assertFalse(last_commit.is_revert_commit)
# First 2 'git log' calls should've happened at this point
self.assertListEqual(sh.git.mock_calls, expected_calls[:3])
- self.assertListEqual(last_commit.changed_files, ["file1.txt", "påth/to/file2.txt"])
+ self.assertListEqual(last_commit.changed_files, ["file1.txt", "påth/to/file2.bin"])
+ expected_file_stats = {
+ "file1.txt": GitChangedFileStats("file1.txt", 4, 15),
+ "påth/to/file2.bin": GitChangedFileStats("påth/to/file2.bin", None, None),
+ }
+ self.assertDictEqual(last_commit.changed_files_stats, expected_file_stats)
+
# 'git diff-tree' should have happened at this point
self.assertListEqual(sh.git.mock_calls, expected_calls[:4])
@@ -74,18 +93,17 @@ class GitCommitTests(BaseTestCase):
# All expected calls should've happened at this point
self.assertListEqual(sh.git.mock_calls, expected_calls)
- @patch('gitlint.git.sh')
+ @patch("gitlint.git.sh")
def test_from_local_repository_specific_refspec(self, sh):
sample_refspec = "åbc123..def456"
sample_sha = "åbc123"
sh.git.side_effect = [
sample_sha, # git rev-list <sample_refspec>
- "test åuthor\x00test-emåil@foo.com\x002016-12-03 15:28:15 +0100\x00åbc\n"
- "cömmit-title\n\ncömmit-body",
+ "test åuthor\x00test-emåil@foo.com\x002016-12-03 15:28:15 +0100\x00åbc\ncömmit-title\n\ncömmit-body",
"#", # git config --get core.commentchar
- "file1.txt\npåth/to/file2.txt\n",
- "foöbar\n* hürdur\n"
+ "7\t10\tfile1.txt\n9\t12\tpåth/to/file2.txt\n",
+ "foöbar\n* hürdur\n",
]
context = GitContext.from_local_repository("fåke/path", refspec=sample_refspec)
@@ -93,10 +111,17 @@ class GitCommitTests(BaseTestCase):
expected_calls = [
call("rev-list", sample_refspec, **self.expected_sh_special_args),
call("log", sample_sha, "-1", "--pretty=%aN%x00%aE%x00%ai%x00%P%n%B", **self.expected_sh_special_args),
- call('config', '--get', 'core.commentchar', _ok_code=[0, 1], **self.expected_sh_special_args),
- call('diff-tree', '--no-commit-id', '--name-only', '-r', '--root', sample_sha,
- **self.expected_sh_special_args),
- call('branch', '--contains', sample_sha, **self.expected_sh_special_args)
+ call("config", "--get", "core.commentchar", _ok_code=[0, 1], **self.expected_sh_special_args),
+ call(
+ "diff-tree",
+ "--no-commit-id",
+ "--numstat",
+ "-r",
+ "--root",
+ sample_sha,
+ **self.expected_sh_special_args,
+ ),
+ call("branch", "--contains", sample_sha, **self.expected_sh_special_args),
]
# Only first 'git log' call should've happened at this point
@@ -109,11 +134,13 @@ class GitCommitTests(BaseTestCase):
self.assertEqual(last_commit.message.body, ["", "cömmit-body"])
self.assertEqual(last_commit.author_name, "test åuthor")
self.assertEqual(last_commit.author_email, "test-emåil@foo.com")
- self.assertEqual(last_commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15,
- tzinfo=dateutil.tz.tzoffset("+0100", 3600)))
+ self.assertEqual(
+ last_commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15, tzinfo=dateutil.tz.tzoffset("+0100", 3600))
+ )
self.assertListEqual(last_commit.parents, ["åbc"])
self.assertFalse(last_commit.is_merge_commit)
self.assertFalse(last_commit.is_fixup_commit)
+ self.assertFalse(last_commit.is_fixup_amend_commit)
self.assertFalse(last_commit.is_squash_commit)
self.assertFalse(last_commit.is_revert_commit)
@@ -121,6 +148,12 @@ class GitCommitTests(BaseTestCase):
self.assertListEqual(sh.git.mock_calls, expected_calls[:3])
self.assertListEqual(last_commit.changed_files, ["file1.txt", "påth/to/file2.txt"])
+ expected_file_stats = {
+ "file1.txt": GitChangedFileStats("file1.txt", 7, 10),
+ "påth/to/file2.txt": GitChangedFileStats("påth/to/file2.txt", 9, 12),
+ }
+ self.assertDictEqual(last_commit.changed_files_stats, expected_file_stats)
+
# 'git diff-tree' should have happened at this point
self.assertListEqual(sh.git.mock_calls, expected_calls[:4])
@@ -128,28 +161,34 @@ class GitCommitTests(BaseTestCase):
# All expected calls should've happened at this point
self.assertListEqual(sh.git.mock_calls, expected_calls)
- @patch('gitlint.git.sh')
+ @patch("gitlint.git.sh")
def test_from_local_repository_specific_commit_hash(self, sh):
sample_hash = "åbc123"
sh.git.side_effect = [
sample_hash, # git log -1 <sample_hash>
- "test åuthor\x00test-emåil@foo.com\x002016-12-03 15:28:15 +0100\x00åbc\n"
- "cömmit-title\n\ncömmit-body",
+ "test åuthor\x00test-emåil@foo.com\x002016-12-03 15:28:15 +0100\x00åbc\ncömmit-title\n\ncömmit-body",
"#", # git config --get core.commentchar
- "file1.txt\npåth/to/file2.txt\n",
- "foöbar\n* hürdur\n"
+ "8\t3\tfile1.txt\n1\t4\tpåth/to/file2.txt\n",
+ "foöbar\n* hürdur\n",
]
- context = GitContext.from_local_repository("fåke/path", commit_hash=sample_hash)
+ context = GitContext.from_local_repository("fåke/path", commit_hashes=[sample_hash])
# assert that commit info was read using git command
expected_calls = [
- call("log", "-1", sample_hash, "--pretty=%H", **self.expected_sh_special_args),
+ call("log", "-1", sample_hash, "--pretty=%H", **self.expected_sh_special_args),
call("log", sample_hash, "-1", "--pretty=%aN%x00%aE%x00%ai%x00%P%n%B", **self.expected_sh_special_args),
- call('config', '--get', 'core.commentchar', _ok_code=[0, 1], **self.expected_sh_special_args),
- call('diff-tree', '--no-commit-id', '--name-only', '-r', '--root', sample_hash,
- **self.expected_sh_special_args),
- call('branch', '--contains', sample_hash, **self.expected_sh_special_args)
+ call("config", "--get", "core.commentchar", _ok_code=[0, 1], **self.expected_sh_special_args),
+ call(
+ "diff-tree",
+ "--no-commit-id",
+ "--numstat",
+ "-r",
+ "--root",
+ sample_hash,
+ **self.expected_sh_special_args,
+ ),
+ call("branch", "--contains", sample_hash, **self.expected_sh_special_args),
]
# Only first 'git log' call should've happened at this point
@@ -162,11 +201,13 @@ class GitCommitTests(BaseTestCase):
self.assertEqual(last_commit.message.body, ["", "cömmit-body"])
self.assertEqual(last_commit.author_name, "test åuthor")
self.assertEqual(last_commit.author_email, "test-emåil@foo.com")
- self.assertEqual(last_commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15,
- tzinfo=dateutil.tz.tzoffset("+0100", 3600)))
+ self.assertEqual(
+ last_commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15, tzinfo=dateutil.tz.tzoffset("+0100", 3600))
+ )
self.assertListEqual(last_commit.parents, ["åbc"])
self.assertFalse(last_commit.is_merge_commit)
self.assertFalse(last_commit.is_fixup_commit)
+ self.assertFalse(last_commit.is_fixup_amend_commit)
self.assertFalse(last_commit.is_squash_commit)
self.assertFalse(last_commit.is_revert_commit)
@@ -174,6 +215,11 @@ class GitCommitTests(BaseTestCase):
self.assertListEqual(sh.git.mock_calls, expected_calls[:3])
self.assertListEqual(last_commit.changed_files, ["file1.txt", "påth/to/file2.txt"])
+ expected_file_stats = {
+ "file1.txt": GitChangedFileStats("file1.txt", 8, 3),
+ "påth/to/file2.txt": GitChangedFileStats("påth/to/file2.txt", 1, 4),
+ }
+ self.assertDictEqual(last_commit.changed_files_stats, expected_file_stats)
# 'git diff-tree' should have happened at this point
self.assertListEqual(sh.git.mock_calls, expected_calls[:4])
@@ -181,17 +227,103 @@ class GitCommitTests(BaseTestCase):
# All expected calls should've happened at this point
self.assertListEqual(sh.git.mock_calls, expected_calls)
- @patch('gitlint.git.sh')
+ @patch("gitlint.git.sh")
+ def test_from_local_repository_multiple_commit_hashes(self, sh):
+ hashes = ["åbc123", "dęf456", "ghí789"]
+ sh.git.side_effect = [
+ *hashes,
+ f"test åuthor {hashes[0]}\x00test-emåil-{hashes[0]}@foo.com\x002016-12-03 15:28:15 +0100\x00åbc\n"
+ f"cömmit-title {hashes[0]}\n\ncömmit-body {hashes[0]}",
+ "#", # git config --get core.commentchar
+ f"test åuthor {hashes[1]}\x00test-emåil-{hashes[1]}@foo.com\x002016-12-03 15:28:15 +0100\x00åbc\n"
+ f"cömmit-title {hashes[1]}\n\ncömmit-body {hashes[1]}",
+ f"test åuthor {hashes[2]}\x00test-emåil-{hashes[2]}@foo.com\x002016-12-03 15:28:15 +0100\x00åbc\n"
+ f"cömmit-title {hashes[2]}\n\ncömmit-body {hashes[2]}",
+ f"2\t5\tfile1-{hashes[0]}.txt\n7\t1\tpåth/to/file2.txt\n",
+ f"2\t5\tfile1-{hashes[1]}.txt\n7\t1\tpåth/to/file2.txt\n",
+ f"2\t5\tfile1-{hashes[2]}.txt\n7\t1\tpåth/to/file2.txt\n",
+ f"foöbar-{hashes[0]}\n* hürdur\n",
+ f"foöbar-{hashes[1]}\n* hürdur\n",
+ f"foöbar-{hashes[2]}\n* hürdur\n",
+ ]
+
+ expected_calls = [
+ call("log", "-1", hashes[0], "--pretty=%H", **self.expected_sh_special_args),
+ call("log", "-1", hashes[1], "--pretty=%H", **self.expected_sh_special_args),
+ call("log", "-1", hashes[2], "--pretty=%H", **self.expected_sh_special_args),
+ call("log", hashes[0], "-1", "--pretty=%aN%x00%aE%x00%ai%x00%P%n%B", **self.expected_sh_special_args),
+ call("config", "--get", "core.commentchar", _ok_code=[0, 1], **self.expected_sh_special_args),
+ call("log", hashes[1], "-1", "--pretty=%aN%x00%aE%x00%ai%x00%P%n%B", **self.expected_sh_special_args),
+ call("log", hashes[2], "-1", "--pretty=%aN%x00%aE%x00%ai%x00%P%n%B", **self.expected_sh_special_args),
+ call(
+ "diff-tree", "--no-commit-id", "--numstat", "-r", "--root", hashes[0], **self.expected_sh_special_args
+ ),
+ call(
+ "diff-tree", "--no-commit-id", "--numstat", "-r", "--root", hashes[1], **self.expected_sh_special_args
+ ),
+ call(
+ "diff-tree", "--no-commit-id", "--numstat", "-r", "--root", hashes[2], **self.expected_sh_special_args
+ ),
+ call("branch", "--contains", hashes[0], **self.expected_sh_special_args),
+ call("branch", "--contains", hashes[1], **self.expected_sh_special_args),
+ call("branch", "--contains", hashes[2], **self.expected_sh_special_args),
+ ]
+
+ context = GitContext.from_local_repository("fåke/path", commit_hashes=hashes)
+
+ # Only first set of 'git log' calls should've happened at this point
+ self.assertEqual(sh.git.mock_calls, expected_calls[:3])
+
+ for i, commit in enumerate(context.commits):
+ expected_hash = hashes[i]
+ self.assertIsInstance(commit, LocalGitCommit)
+ self.assertEqual(commit.sha, expected_hash)
+ self.assertEqual(commit.message.title, f"cömmit-title {expected_hash}")
+ self.assertEqual(commit.message.body, ["", f"cömmit-body {expected_hash}"])
+ self.assertEqual(commit.author_name, f"test åuthor {expected_hash}")
+ self.assertEqual(commit.author_email, f"test-emåil-{expected_hash}@foo.com")
+ self.assertEqual(
+ commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15, tzinfo=dateutil.tz.tzoffset("+0100", 3600))
+ )
+ self.assertListEqual(commit.parents, ["åbc"])
+ self.assertFalse(commit.is_merge_commit)
+ self.assertFalse(commit.is_fixup_commit)
+ self.assertFalse(commit.is_fixup_amend_commit)
+ self.assertFalse(commit.is_squash_commit)
+ self.assertFalse(commit.is_revert_commit)
+
+ # All 'git log' calls should've happened at this point
+ self.assertListEqual(sh.git.mock_calls, expected_calls[:7])
+
+ for i, commit in enumerate(context.commits):
+ expected_hash = hashes[i]
+ self.assertListEqual(commit.changed_files, [f"file1-{expected_hash}.txt", "påth/to/file2.txt"])
+ expected_file_stats = {
+ f"file1-{expected_hash}.txt": GitChangedFileStats(f"file1-{expected_hash}.txt", 2, 5),
+ "påth/to/file2.txt": GitChangedFileStats("påth/to/file2.txt", 7, 1),
+ }
+ self.assertDictEqual(commit.changed_files_stats, expected_file_stats)
+
+ # 'git diff-tree' should have happened at this point
+ self.assertListEqual(sh.git.mock_calls, expected_calls[:10])
+
+ for i, commit in enumerate(context.commits):
+ expected_hash = hashes[i]
+ self.assertListEqual(commit.branches, [f"foöbar-{expected_hash}", "hürdur"])
+
+ # All expected calls should've happened at this point
+ self.assertListEqual(sh.git.mock_calls, expected_calls)
+
+ @patch("gitlint.git.sh")
def test_get_latest_commit_merge_commit(self, sh):
sample_sha = "d8ac47e9f2923c7f22d8668e3a1ed04eb4cdbca9"
sh.git.side_effect = [
sample_sha,
- "test åuthor\x00test-emåil@foo.com\x002016-12-03 15:28:15 +0100\x00åbc def\n"
- "Merge \"foo bår commit\"",
+ 'test åuthor\x00test-emåil@foo.com\x002016-12-03 15:28:15 +0100\x00åbc def\nMerge "foo bår commit"',
"#", # git config --get core.commentchar
- "file1.txt\npåth/to/file2.txt\n",
- "foöbar\n* hürdur\n"
+ "6\t2\tfile1.txt\n1\t4\tpåth/to/file2.txt\n",
+ "foöbar\n* hürdur\n",
]
context = GitContext.from_local_repository("fåke/path")
@@ -199,10 +331,17 @@ class GitCommitTests(BaseTestCase):
expected_calls = [
call("log", "-1", "--pretty=%H", **self.expected_sh_special_args),
call("log", sample_sha, "-1", "--pretty=%aN%x00%aE%x00%ai%x00%P%n%B", **self.expected_sh_special_args),
- call('config', '--get', 'core.commentchar', _ok_code=[0, 1], **self.expected_sh_special_args),
- call('diff-tree', '--no-commit-id', '--name-only', '-r', '--root', sample_sha,
- **self.expected_sh_special_args),
- call('branch', '--contains', sample_sha, **self.expected_sh_special_args)
+ call("config", "--get", "core.commentchar", _ok_code=[0, 1], **self.expected_sh_special_args),
+ call(
+ "diff-tree",
+ "--no-commit-id",
+ "--numstat",
+ "-r",
+ "--root",
+ sample_sha,
+ **self.expected_sh_special_args,
+ ),
+ call("branch", "--contains", sample_sha, **self.expected_sh_special_args),
]
# Only first 'git log' call should've happened at this point
@@ -211,15 +350,17 @@ class GitCommitTests(BaseTestCase):
last_commit = context.commits[-1]
self.assertIsInstance(last_commit, LocalGitCommit)
self.assertEqual(last_commit.sha, sample_sha)
- self.assertEqual(last_commit.message.title, "Merge \"foo bår commit\"")
+ self.assertEqual(last_commit.message.title, 'Merge "foo bår commit"')
self.assertEqual(last_commit.message.body, [])
self.assertEqual(last_commit.author_name, "test åuthor")
self.assertEqual(last_commit.author_email, "test-emåil@foo.com")
- self.assertEqual(last_commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15,
- tzinfo=dateutil.tz.tzoffset("+0100", 3600)))
+ self.assertEqual(
+ last_commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15, tzinfo=dateutil.tz.tzoffset("+0100", 3600))
+ )
self.assertListEqual(last_commit.parents, ["åbc", "def"])
self.assertTrue(last_commit.is_merge_commit)
self.assertFalse(last_commit.is_fixup_commit)
+ self.assertFalse(last_commit.is_fixup_amend_commit)
self.assertFalse(last_commit.is_squash_commit)
self.assertFalse(last_commit.is_revert_commit)
@@ -227,6 +368,11 @@ class GitCommitTests(BaseTestCase):
self.assertListEqual(sh.git.mock_calls, expected_calls[:3])
self.assertListEqual(last_commit.changed_files, ["file1.txt", "påth/to/file2.txt"])
+ expected_file_stats = {
+ "file1.txt": GitChangedFileStats("file1.txt", 6, 2),
+ "påth/to/file2.txt": GitChangedFileStats("påth/to/file2.txt", 1, 4),
+ }
+ self.assertDictEqual(last_commit.changed_files_stats, expected_file_stats)
# 'git diff-tree' should have happened at this point
self.assertListEqual(sh.git.mock_calls, expected_calls[:4])
@@ -234,19 +380,19 @@ class GitCommitTests(BaseTestCase):
# All expected calls should've happened at this point
self.assertListEqual(sh.git.mock_calls, expected_calls)
- @patch('gitlint.git.sh')
+ @patch("gitlint.git.sh")
def test_get_latest_commit_fixup_squash_commit(self, sh):
- commit_types = ["fixup", "squash"]
- for commit_type in commit_types:
+ commit_prefixes = {"fixup": "is_fixup_commit", "squash": "is_squash_commit", "amend": "is_fixup_amend_commit"}
+ for commit_type in commit_prefixes.keys():
sample_sha = "d8ac47e9f2923c7f22d8668e3a1ed04eb4cdbca9"
sh.git.side_effect = [
sample_sha,
"test åuthor\x00test-emåil@foo.com\x002016-12-03 15:28:15 +0100\x00åbc\n"
- f"{commit_type}! \"foo bår commit\"",
+ f'{commit_type}! "foo bår commit"',
"#", # git config --get core.commentchar
- "file1.txt\npåth/to/file2.txt\n",
- "foöbar\n* hürdur\n"
+ "8\t2\tfile1.txt\n7\t3\tpåth/to/file2.txt\n",
+ "foöbar\n* hürdur\n",
]
context = GitContext.from_local_repository("fåke/path")
@@ -254,10 +400,17 @@ class GitCommitTests(BaseTestCase):
expected_calls = [
call("log", "-1", "--pretty=%H", **self.expected_sh_special_args),
call("log", sample_sha, "-1", "--pretty=%aN%x00%aE%x00%ai%x00%P%n%B", **self.expected_sh_special_args),
- call('config', '--get', 'core.commentchar', _ok_code=[0, 1], **self.expected_sh_special_args),
- call('diff-tree', '--no-commit-id', '--name-only', '-r', '--root', sample_sha,
- **self.expected_sh_special_args),
- call('branch', '--contains', sample_sha, **self.expected_sh_special_args)
+ call("config", "--get", "core.commentchar", _ok_code=[0, 1], **self.expected_sh_special_args),
+ call(
+ "diff-tree",
+ "--no-commit-id",
+ "--numstat",
+ "-r",
+ "--root",
+ sample_sha,
+ **self.expected_sh_special_args,
+ ),
+ call("branch", "--contains", sample_sha, **self.expected_sh_special_args),
]
# Only first 'git log' call should've happened at this point
@@ -266,27 +419,31 @@ class GitCommitTests(BaseTestCase):
last_commit = context.commits[-1]
self.assertIsInstance(last_commit, LocalGitCommit)
self.assertEqual(last_commit.sha, sample_sha)
- self.assertEqual(last_commit.message.title, f"{commit_type}! \"foo bår commit\"")
+ self.assertEqual(last_commit.message.title, f'{commit_type}! "foo bår commit"')
self.assertEqual(last_commit.message.body, [])
self.assertEqual(last_commit.author_name, "test åuthor")
self.assertEqual(last_commit.author_email, "test-emåil@foo.com")
- self.assertEqual(last_commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15,
- tzinfo=dateutil.tz.tzoffset("+0100", 3600)))
+ self.assertEqual(
+ last_commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15, tzinfo=dateutil.tz.tzoffset("+0100", 3600))
+ )
self.assertListEqual(last_commit.parents, ["åbc"])
# First 2 'git log' calls should've happened at this point
self.assertEqual(sh.git.mock_calls, expected_calls[:3])
# Asserting that squash and fixup are correct
- for type in commit_types:
- attr = "is_" + type + "_commit"
+ for type, attr in commit_prefixes.items():
self.assertEqual(getattr(last_commit, attr), commit_type == type)
self.assertFalse(last_commit.is_merge_commit)
self.assertFalse(last_commit.is_revert_commit)
self.assertListEqual(last_commit.changed_files, ["file1.txt", "påth/to/file2.txt"])
+ expected_file_stats = {
+ "file1.txt": GitChangedFileStats("file1.txt", 8, 2),
+ "påth/to/file2.txt": GitChangedFileStats("påth/to/file2.txt", 7, 3),
+ }
+ self.assertDictEqual(last_commit.changed_files_stats, expected_file_stats)
- self.assertListEqual(last_commit.changed_files, ["file1.txt", "påth/to/file2.txt"])
# 'git diff-tree' should have happened at this point
self.assertListEqual(sh.git.mock_calls, expected_calls[:4])
@@ -302,14 +459,16 @@ class GitCommitTests(BaseTestCase):
gitcontext = GitContext.from_commit_msg(self.get_sample("commit_message/sample1"))
expected_title = "Commit title contåining 'WIP', as well as trailing punctuation."
- expected_body = ["This line should be empty",
- "This is the first line of the commit message body and it is meant to test a " +
- "line that exceeds the maximum line length of 80 characters.",
- "This line has a tråiling space. ",
- "This line has a trailing tab.\t"]
+ expected_body = [
+ "This line should be empty",
+ "This is the first line of the commit message body and it is meant to test a "
+ + "line that exceeds the maximum line length of 80 characters.",
+ "This line has a tråiling space. ",
+ "This line has a trailing tab.\t",
+ ]
expected_full = expected_title + "\n" + "\n".join(expected_body)
- expected_original = expected_full + (
- "\n# This is a cömmented line\n"
+ expected_original = (
+ expected_full + "\n# This is a cömmented line\n"
"# ------------------------ >8 ------------------------\n"
"# Anything after this line should be cleaned up\n"
"# this line appears on `git commit -v` command\n"
@@ -335,6 +494,7 @@ class GitCommitTests(BaseTestCase):
self.assertListEqual(commit.branches, [])
self.assertFalse(commit.is_merge_commit)
self.assertFalse(commit.is_fixup_commit)
+ self.assertFalse(commit.is_fixup_amend_commit)
self.assertFalse(commit.is_squash_commit)
self.assertFalse(commit.is_revert_commit)
self.assertEqual(len(gitcontext.commits), 1)
@@ -355,6 +515,7 @@ class GitCommitTests(BaseTestCase):
self.assertListEqual(commit.branches, [])
self.assertFalse(commit.is_merge_commit)
self.assertFalse(commit.is_fixup_commit)
+ self.assertFalse(commit.is_fixup_amend_commit)
self.assertFalse(commit.is_squash_commit)
self.assertFalse(commit.is_revert_commit)
self.assertEqual(len(gitcontext.commits), 1)
@@ -376,6 +537,7 @@ class GitCommitTests(BaseTestCase):
self.assertListEqual(commit.branches, [])
self.assertFalse(commit.is_merge_commit)
self.assertFalse(commit.is_fixup_commit)
+ self.assertFalse(commit.is_fixup_amend_commit)
self.assertFalse(commit.is_squash_commit)
self.assertFalse(commit.is_revert_commit)
self.assertEqual(len(gitcontext.commits), 1)
@@ -400,6 +562,7 @@ class GitCommitTests(BaseTestCase):
self.assertFalse(commit.is_merge_commit)
self.assertFalse(commit.is_fixup_commit)
self.assertFalse(commit.is_squash_commit)
+ self.assertFalse(commit.is_fixup_amend_commit)
self.assertFalse(commit.is_revert_commit)
self.assertEqual(len(gitcontext.commits), 1)
@@ -421,18 +584,19 @@ class GitCommitTests(BaseTestCase):
self.assertListEqual(commit.branches, [])
self.assertTrue(commit.is_merge_commit)
self.assertFalse(commit.is_fixup_commit)
+ self.assertFalse(commit.is_fixup_amend_commit)
self.assertFalse(commit.is_squash_commit)
self.assertFalse(commit.is_revert_commit)
self.assertEqual(len(gitcontext.commits), 1)
def test_from_commit_msg_revert_commit(self):
- commit_msg = "Revert \"Prev commit message\"\n\nThis reverts commit a8ad67e04164a537198dea94a4fde81c5592ae9c."
+ commit_msg = 'Revert "Prev commit message"\n\nThis reverts commit a8ad67e04164a537198dea94a4fde81c5592ae9c.'
gitcontext = GitContext.from_commit_msg(commit_msg)
commit = gitcontext.commits[-1]
self.assertIsInstance(commit, GitCommit)
self.assertFalse(isinstance(commit, LocalGitCommit))
- self.assertEqual(commit.message.title, "Revert \"Prev commit message\"")
+ self.assertEqual(commit.message.title, 'Revert "Prev commit message"')
self.assertEqual(commit.message.body, ["", "This reverts commit a8ad67e04164a537198dea94a4fde81c5592ae9c."])
self.assertEqual(commit.message.full, commit_msg)
self.assertEqual(commit.message.original, commit_msg)
@@ -443,13 +607,16 @@ class GitCommitTests(BaseTestCase):
self.assertListEqual(commit.branches, [])
self.assertFalse(commit.is_merge_commit)
self.assertFalse(commit.is_fixup_commit)
+ self.assertFalse(commit.is_fixup_amend_commit)
self.assertFalse(commit.is_squash_commit)
self.assertTrue(commit.is_revert_commit)
self.assertEqual(len(gitcontext.commits), 1)
- def test_from_commit_msg_fixup_squash_commit(self):
- commit_types = ["fixup", "squash"]
- for commit_type in commit_types:
+ def test_from_commit_msg_fixup_squash_amend_commit(self):
+ # mapping between cleanup commit prefixes and the commit object attribute
+ commit_prefixes = {"fixup": "is_fixup_commit", "squash": "is_squash_commit", "amend": "is_fixup_amend_commit"}
+
+ for commit_type in commit_prefixes.keys():
commit_msg = f"{commit_type}! Test message"
gitcontext = GitContext.from_commit_msg(commit_msg)
commit = gitcontext.commits[-1]
@@ -469,34 +636,33 @@ class GitCommitTests(BaseTestCase):
self.assertFalse(commit.is_merge_commit)
self.assertFalse(commit.is_revert_commit)
# Asserting that squash and fixup are correct
- for type in commit_types:
- attr = "is_" + type + "_commit"
- self.assertEqual(getattr(commit, attr), commit_type == type)
+ for type, commit_attr_name in commit_prefixes.items():
+ self.assertEqual(getattr(commit, commit_attr_name), commit_type == type)
- @patch('gitlint.git.sh')
- @patch('arrow.now')
+ @patch("gitlint.git.sh")
+ @patch("arrow.now")
def test_staged_commit(self, now, sh):
# StagedLocalGitCommit()
sh.git.side_effect = [
- "#", # git config --get core.commentchar
- "test åuthor\n", # git config --get user.name
- "test-emåil@foo.com\n", # git config --get user.email
- "my-brånch\n", # git rev-parse --abbrev-ref HEAD
- "file1.txt\npåth/to/file2.txt\n",
+ "#", # git config --get core.commentchar
+ "test åuthor\n", # git config --get user.name
+ "test-emåil@foo.com\n", # git config --get user.email
+ "my-brånch\n", # git rev-parse --abbrev-ref HEAD
+ "4\t2\tfile1.txt\n13\t9\tpåth/to/file2.txt\n",
]
now.side_effect = [arrow.get("2020-02-19T12:18:46.675182+01:00")]
# We use a fixup commit, just to test a non-default path
context = GitContext.from_staged_commit("fixup! Foōbar 123\n\ncömmit-body\n", "fåke/path")
- # git calls we're expexting
+ # git calls we're expecting
expected_calls = [
- call('config', '--get', 'core.commentchar', _ok_code=[0, 1], **self.expected_sh_special_args),
- call('config', '--get', 'user.name', **self.expected_sh_special_args),
- call('config', '--get', 'user.email', **self.expected_sh_special_args),
+ call("config", "--get", "core.commentchar", _ok_code=[0, 1], **self.expected_sh_special_args),
+ call("config", "--get", "user.name", **self.expected_sh_special_args),
+ call("config", "--get", "user.email", **self.expected_sh_special_args),
call("rev-parse", "--abbrev-ref", "HEAD", **self.expected_sh_special_args),
- call("diff", "--staged", "--name-only", "-r", **self.expected_sh_special_args)
+ call("diff", "--staged", "--numstat", "-r", **self.expected_sh_special_args),
]
last_commit = context.commits[-1]
@@ -513,13 +679,15 @@ class GitCommitTests(BaseTestCase):
self.assertEqual(last_commit.author_email, "test-emåil@foo.com")
self.assertListEqual(sh.git.mock_calls, expected_calls[0:3])
- self.assertEqual(last_commit.date, datetime.datetime(2020, 2, 19, 12, 18, 46,
- tzinfo=dateutil.tz.tzoffset("+0100", 3600)))
+ self.assertEqual(
+ last_commit.date, datetime.datetime(2020, 2, 19, 12, 18, 46, tzinfo=dateutil.tz.tzoffset("+0100", 3600))
+ )
now.assert_called_once()
self.assertListEqual(last_commit.parents, [])
self.assertFalse(last_commit.is_merge_commit)
self.assertTrue(last_commit.is_fixup_commit)
+ self.assertFalse(last_commit.is_fixup_amend_commit)
self.assertFalse(last_commit.is_squash_commit)
self.assertFalse(last_commit.is_revert_commit)
@@ -527,42 +695,48 @@ class GitCommitTests(BaseTestCase):
self.assertListEqual(sh.git.mock_calls, expected_calls[0:4])
self.assertListEqual(last_commit.changed_files, ["file1.txt", "påth/to/file2.txt"])
+ expected_file_stats = {
+ "file1.txt": GitChangedFileStats("file1.txt", 4, 2),
+ "påth/to/file2.txt": GitChangedFileStats("påth/to/file2.txt", 13, 9),
+ }
+ self.assertDictEqual(last_commit.changed_files_stats, expected_file_stats)
+
self.assertListEqual(sh.git.mock_calls, expected_calls[0:5])
- @patch('gitlint.git.sh')
+ @patch("gitlint.git.sh")
def test_staged_commit_with_missing_username(self, sh):
- # StagedLocalGitCommit()
-
sh.git.side_effect = [
- "#", # git config --get core.commentchar
- ErrorReturnCode('git config --get user.name', b"", b""),
+ "#", # git config --get core.commentchar
+ ErrorReturnCode("git config --get user.name", b"", b""),
]
expected_msg = "Missing git configuration: please set user.name"
with self.assertRaisesMessage(GitContextError, expected_msg):
ctx = GitContext.from_staged_commit("Foōbar 123\n\ncömmit-body\n", "fåke/path")
- [str(commit) for commit in ctx.commits]
+ ctx.commits[0].author_name # accessing this attribute should raise an exception
- @patch('gitlint.git.sh')
+ @patch("gitlint.git.sh")
def test_staged_commit_with_missing_email(self, sh):
- # StagedLocalGitCommit()
-
sh.git.side_effect = [
- "#", # git config --get core.commentchar
- "test åuthor\n", # git config --get user.name
- ErrorReturnCode('git config --get user.name', b"", b""),
+ "#", # git config --get core.commentchar
+ ErrorReturnCode("git config --get user.email", b"", b""),
]
expected_msg = "Missing git configuration: please set user.email"
with self.assertRaisesMessage(GitContextError, expected_msg):
ctx = GitContext.from_staged_commit("Foōbar 123\n\ncömmit-body\n", "fåke/path")
- [str(commit) for commit in ctx.commits]
+ ctx.commits[0].author_email # accessing this attribute should raise an exception
def test_gitcommitmessage_equality(self):
commit_message1 = GitCommitMessage(GitContext(), "tëst\n\nfoo", "tëst\n\nfoo", "tēst", ["", "föo"])
- attrs = ['original', 'full', 'title', 'body']
+ attrs = ["original", "full", "title", "body"]
self.object_equality_test(commit_message1, attrs, {"context": commit_message1.context})
+ def test_gitchangedfilestats_equality(self):
+ changed_file_stats = GitChangedFileStats(Path("foö/bar"), 5, 13)
+ attrs = ["filepath", "additions", "deletions"]
+ self.object_equality_test(changed_file_stats, attrs)
+
@patch("gitlint.git._git")
def test_gitcommit_equality(self, git):
# git will be called to setup the context (commentchar and current_branch), just return the same value
@@ -573,14 +747,32 @@ class GitCommitTests(BaseTestCase):
now = datetime.datetime.utcnow()
context1 = GitContext()
commit_message1 = GitCommitMessage(context1, "tëst\n\nfoo", "tëst\n\nfoo", "tēst", ["", "föo"])
- commit1 = GitCommit(context1, commit_message1, "shä", now, "Jöhn Smith", "jöhn.smith@test.com", None,
- ["föo/bar"], ["brånch1", "brånch2"])
+ commit1 = GitCommit(
+ context1,
+ commit_message1,
+ "shä",
+ now,
+ "Jöhn Smith",
+ "jöhn.smith@test.com",
+ None,
+ {"föo/bar": GitChangedFileStats("föo/bar", 5, 13)},
+ ["brånch1", "brånch2"],
+ )
context1.commits = [commit1]
context2 = GitContext()
commit_message2 = GitCommitMessage(context2, "tëst\n\nfoo", "tëst\n\nfoo", "tēst", ["", "föo"])
- commit2 = GitCommit(context2, commit_message1, "shä", now, "Jöhn Smith", "jöhn.smith@test.com", None,
- ["föo/bar"], ["brånch1", "brånch2"])
+ commit2 = GitCommit(
+ context2,
+ commit_message1,
+ "shä",
+ now,
+ "Jöhn Smith",
+ "jöhn.smith@test.com",
+ None,
+ {"föo/bar": GitChangedFileStats("föo/bar", 5, 13)},
+ ["brånch1", "brånch2"],
+ )
context2.commits = [commit2]
self.assertEqual(context1, context2)
@@ -588,15 +780,29 @@ class GitCommitTests(BaseTestCase):
self.assertEqual(commit1, commit2)
# Check that objects are unequal when changing a single attribute
- kwargs = {'message': commit1.message, 'sha': commit1.sha, 'date': commit1.date,
- 'author_name': commit1.author_name, 'author_email': commit1.author_email, 'parents': commit1.parents,
- 'changed_files': commit1.changed_files, 'branches': commit1.branches}
-
- self.object_equality_test(commit1, kwargs.keys(), {"context": commit1.context})
+ kwargs = {
+ "message": commit1.message,
+ "sha": commit1.sha,
+ "date": commit1.date,
+ "author_name": commit1.author_name,
+ "author_email": commit1.author_email,
+ "parents": commit1.parents,
+ "branches": commit1.branches,
+ }
+
+ self.object_equality_test(
+ commit1,
+ kwargs.keys(),
+ {"context": commit1.context, "changed_files_stats": {"föo/bar": GitChangedFileStats("föo/bar", 5, 13)}},
+ )
# Check that the is_* attributes that are affected by the commit message affect equality
- special_messages = {'is_merge_commit': "Merge: foöbar", 'is_fixup_commit': "fixup! foöbar",
- 'is_squash_commit': "squash! foöbar", 'is_revert_commit': "Revert: foöbar"}
+ special_messages = {
+ "is_merge_commit": "Merge: foöbar",
+ "is_fixup_commit": "fixup! foöbar",
+ "is_squash_commit": "squash! foöbar",
+ "is_revert_commit": "Revert: foöbar",
+ }
for key in special_messages:
kwargs_copy = copy.deepcopy(kwargs)
clone1 = GitCommit(context=commit1.context, **kwargs_copy)
@@ -607,6 +813,10 @@ class GitCommitTests(BaseTestCase):
clone2.message = GitCommitMessage.from_full_message(context1, "foöbar")
self.assertNotEqual(clone1, clone2)
+ # Check changed_files and changed_files_stats
+ commit2.changed_files_stats = {"föo/bar2": GitChangedFileStats("föo/bar2", 5, 13)}
+ self.assertNotEqual(commit1, commit2)
+
@patch("gitlint.git.git_commentchar")
def test_commit_msg_custom_commentchar(self, patched):
patched.return_value = "ä"
diff --git a/gitlint-core/gitlint/tests/git/test_git_context.py b/gitlint-core/gitlint/tests/git/test_git_context.py
index bb05236..3dcbe4a 100644
--- a/gitlint-core/gitlint/tests/git/test_git_context.py
+++ b/gitlint-core/gitlint/tests/git/test_git_context.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
from unittest.mock import patch, call
from gitlint.tests.base import BaseTestCase
@@ -7,24 +5,16 @@ from gitlint.git import GitContext
class GitContextTests(BaseTestCase):
-
# Expected special_args passed to 'sh'
- expected_sh_special_args = {
- '_tty_out': False,
- '_cwd': "fåke/path"
- }
+ expected_sh_special_args = {"_tty_out": False, "_cwd": "fåke/path"}
- @patch('gitlint.git.sh')
+ @patch("gitlint.git.sh")
def test_gitcontext(self, sh):
-
- sh.git.side_effect = [
- "#", # git config --get core.commentchar
- "\nfoöbar\n"
- ]
+ sh.git.side_effect = ["#", "\nfoöbar\n"] # git config --get core.commentchar
expected_calls = [
call("config", "--get", "core.commentchar", _ok_code=[0, 1], **self.expected_sh_special_args),
- call("rev-parse", "--abbrev-ref", "HEAD", **self.expected_sh_special_args)
+ call("rev-parse", "--abbrev-ref", "HEAD", **self.expected_sh_special_args),
]
context = GitContext("fåke/path")
@@ -38,12 +28,11 @@ class GitContextTests(BaseTestCase):
self.assertEqual(context.current_branch, "foöbar")
self.assertEqual(sh.git.mock_calls, expected_calls)
- @patch('gitlint.git.sh')
+ @patch("gitlint.git.sh")
def test_gitcontext_equality(self, sh):
-
sh.git.side_effect = [
- "û\n", # context1: git config --get core.commentchar
- "û\n", # context2: git config --get core.commentchar
+ "û\n", # context1: git config --get core.commentchar
+ "û\n", # context2: git config --get core.commentchar
"my-brånch\n", # context1: git rev-parse --abbrev-ref HEAD
"my-brånch\n", # context2: git rev-parse --abbrev-ref HEAD
]
@@ -68,17 +57,17 @@ class GitContextTests(BaseTestCase):
# Different comment_char
context3 = GitContext("fåke/path")
context3.commits = ["fōo", "bår"]
- sh.git.side_effect = ([
- "ç\n", # context3: git config --get core.commentchar
- "my-brånch\n" # context3: git rev-parse --abbrev-ref HEAD
- ])
+ sh.git.side_effect = [
+ "ç\n", # context3: git config --get core.commentchar
+ "my-brånch\n", # context3: git rev-parse --abbrev-ref HEAD
+ ]
self.assertNotEqual(context1, context3)
# Different current_branch
context4 = GitContext("fåke/path")
context4.commits = ["fōo", "bår"]
- sh.git.side_effect = ([
- "û\n", # context4: git config --get core.commentchar
- "different-brånch\n" # context4: git rev-parse --abbrev-ref HEAD
- ])
+ sh.git.side_effect = [
+ "û\n", # context4: git config --get core.commentchar
+ "different-brånch\n", # context4: git rev-parse --abbrev-ref HEAD
+ ]
self.assertNotEqual(context1, context4)
diff --git a/gitlint-core/gitlint/tests/rules/test_body_rules.py b/gitlint-core/gitlint/tests/rules/test_body_rules.py
index 812c74a..94b1edf 100644
--- a/gitlint-core/gitlint/tests/rules/test_body_rules.py
+++ b/gitlint-core/gitlint/tests/rules/test_body_rules.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
from gitlint.tests.base import BaseTestCase
from gitlint import rules
@@ -17,7 +16,7 @@ class BodyRuleTests(BaseTestCase):
self.assertListEqual(violations, [expected_violation])
# set line length to 120, and check no violation on length 73
- rule = rules.BodyMaxLineLength({'line-length': 120})
+ rule = rules.BodyMaxLineLength({"line-length": 120})
violations = rule.validate("å" * 73, None)
self.assertIsNone(violations)
@@ -100,14 +99,14 @@ class BodyRuleTests(BaseTestCase):
# set line length to 120, and check violation on length 21
expected_violation = rules.RuleViolation("B5", "Body message is too short (21<120)", "å" * 21, 3)
- rule = rules.BodyMinLength({'min-length': 120})
- commit = self.gitcommit("Title\n\n{0}\n".format("å" * 21)) # pylint: disable=consider-using-f-string
+ rule = rules.BodyMinLength({"min-length": 120})
+ commit = self.gitcommit("Title\n\n{}\n".format("å" * 21)) # pylint: disable=consider-using-f-string
violations = rule.validate(commit)
self.assertListEqual(violations, [expected_violation])
# Make sure we don't get the error if the body-length is exactly the min-length
- rule = rules.BodyMinLength({'min-length': 8})
- commit = self.gitcommit("Tïtle\n\n{0}\n".format("å" * 8)) # pylint: disable=consider-using-f-string
+ rule = rules.BodyMinLength({"min-length": 8})
+ commit = self.gitcommit("Tïtle\n\n{}\n".format("å" * 8)) # pylint: disable=consider-using-f-string
violations = rule.validate(commit)
self.assertIsNone(violations)
@@ -145,7 +144,7 @@ class BodyRuleTests(BaseTestCase):
self.assertIsNone(violations)
# assert error for merge commits if ignore-merge-commits is disabled
- rule = rules.BodyMissing({'ignore-merge-commits': False})
+ rule = rules.BodyMissing({"ignore-merge-commits": False})
violations = rule.validate(commit)
expected_violation = rules.RuleViolation("B6", "Body message is missing", None, 3)
self.assertListEqual(violations, [expected_violation])
@@ -159,7 +158,7 @@ class BodyRuleTests(BaseTestCase):
self.assertIsNone(violations)
# assert no error when no files have changed but certain files need to be mentioned on change
- rule = rules.BodyChangedFileMention({'files': "bar.txt,föo/test.py"})
+ rule = rules.BodyChangedFileMention({"files": "bar.txt,föo/test.py"})
commit = self.gitcommit("This is a test\n\nHere is a mention of föo/test.py")
violations = rule.validate(commit)
self.assertIsNone(violations)
@@ -201,29 +200,29 @@ class BodyRuleTests(BaseTestCase):
# assert no violation on matching regex
# (also note that first body line - in between title and rest of body - is ignored)
- rule = rules.BodyRegexMatches({'regex': "^Bödy(.*)"})
+ rule = rules.BodyRegexMatches({"regex": "^Bödy(.*)"})
violations = rule.validate(commit)
self.assertIsNone(violations)
# assert we can do end matching (and last empty line is ignored)
# (also note that first body line - in between title and rest of body - is ignored)
- rule = rules.BodyRegexMatches({'regex': "My-Commit-Tag: föo$"})
+ rule = rules.BodyRegexMatches({"regex": "My-Commit-Tag: föo$"})
violations = rule.validate(commit)
self.assertIsNone(violations)
# common use-case: matching that a given line is present
- rule = rules.BodyRegexMatches({'regex': "(.*)Föo(.*)"})
+ rule = rules.BodyRegexMatches({"regex": "(.*)Föo(.*)"})
violations = rule.validate(commit)
self.assertIsNone(violations)
# assert violation on non-matching body
- rule = rules.BodyRegexMatches({'regex': "^Tëst(.*)Foo"})
+ rule = rules.BodyRegexMatches({"regex": "^Tëst(.*)Foo"})
violations = rule.validate(commit)
expected_violation = rules.RuleViolation("B8", "Body does not match regex (^Tëst(.*)Foo)", None, 6)
self.assertListEqual(violations, [expected_violation])
# assert no violation on None regex
- rule = rules.BodyRegexMatches({'regex': None})
+ rule = rules.BodyRegexMatches({"regex": None})
violations = rule.validate(commit)
self.assertIsNone(violations)
@@ -231,6 +230,6 @@ class BodyRuleTests(BaseTestCase):
bodies = ["åbc", "åbc\n", "åbc\nföo\n", "åbc\n\n", "åbc\nföo\nblå", "åbc\nföo\nblå\n"]
for body in bodies:
commit = self.gitcommit(body)
- rule = rules.BodyRegexMatches({'regex': ".*"})
+ rule = rules.BodyRegexMatches({"regex": ".*"})
violations = rule.validate(commit)
self.assertIsNone(violations)
diff --git a/gitlint-core/gitlint/tests/rules/test_configuration_rules.py b/gitlint-core/gitlint/tests/rules/test_configuration_rules.py
index 9302da5..9e3b07c 100644
--- a/gitlint-core/gitlint/tests/rules/test_configuration_rules.py
+++ b/gitlint-core/gitlint/tests/rules/test_configuration_rules.py
@@ -1,5 +1,4 @@
-# -*- coding: utf-8 -*-
-from gitlint.tests.base import BaseTestCase
+from gitlint.tests.base import BaseTestCase, EXPECTED_REGEX_STYLE_SEARCH_DEPRECATION_WARNING
from gitlint import rules
from gitlint.config import LintConfig
@@ -22,20 +21,25 @@ class ConfigurationRuleTests(BaseTestCase):
rule.apply(config, commit)
self.assertEqual(config, expected_config)
- expected_log_message = "DEBUG: gitlint.rules Ignoring commit because of rule 'I1': " + \
- "Commit title 'Releäse' matches the regex '^Releäse(.*)', ignoring rules: all"
- self.assert_log_contains(expected_log_message)
+ expected_log_messages = [
+ EXPECTED_REGEX_STYLE_SEARCH_DEPRECATION_WARNING.format("I1", "ignore-by-title"),
+ "DEBUG: gitlint.rules Ignoring commit because of rule 'I1': "
+ "Commit title 'Releäse' matches the regex '^Releäse(.*)', ignoring rules: all",
+ ]
+ self.assert_logged(expected_log_messages)
# Matching regex with specific ignore
- rule = rules.IgnoreByTitle({"regex": "^Releäse(.*)",
- "ignore": "T1,B2"})
+ rule = rules.IgnoreByTitle({"regex": "^Releäse(.*)", "ignore": "T1,B2"})
expected_config = LintConfig()
expected_config.ignore = "T1,B2"
rule.apply(config, commit)
self.assertEqual(config, expected_config)
- expected_log_message = "DEBUG: gitlint.rules Ignoring commit because of rule 'I1': " + \
+ expected_log_messages += [
+ "DEBUG: gitlint.rules Ignoring commit because of rule 'I1': "
"Commit title 'Releäse' matches the regex '^Releäse(.*)', ignoring rules: T1,B2"
+ ]
+ self.assert_logged(expected_log_messages)
def test_ignore_by_body(self):
commit = self.gitcommit("Tïtle\n\nThis is\n a relëase body\n line")
@@ -54,22 +58,26 @@ class ConfigurationRuleTests(BaseTestCase):
rule.apply(config, commit)
self.assertEqual(config, expected_config)
- expected_log_message = "DEBUG: gitlint.rules Ignoring commit because of rule 'I2': " + \
- "Commit message line ' a relëase body' matches the regex '(.*)relëase(.*)'," + \
- " ignoring rules: all"
- self.assert_log_contains(expected_log_message)
+ expected_log_messages = [
+ EXPECTED_REGEX_STYLE_SEARCH_DEPRECATION_WARNING.format("I2", "ignore-by-body"),
+ "DEBUG: gitlint.rules Ignoring commit because of rule 'I2': "
+ "Commit message line ' a relëase body' matches the regex '(.*)relëase(.*)',"
+ " ignoring rules: all",
+ ]
+ self.assert_logged(expected_log_messages)
# Matching regex with specific ignore
- rule = rules.IgnoreByBody({"regex": "(.*)relëase(.*)",
- "ignore": "T1,B2"})
+ rule = rules.IgnoreByBody({"regex": "(.*)relëase(.*)", "ignore": "T1,B2"})
expected_config = LintConfig()
expected_config.ignore = "T1,B2"
rule.apply(config, commit)
self.assertEqual(config, expected_config)
- expected_log_message = "DEBUG: gitlint.rules Ignoring commit because of rule 'I2': " + \
+ expected_log_messages += [
+ "DEBUG: gitlint.rules Ignoring commit because of rule 'I2': "
"Commit message line ' a relëase body' matches the regex '(.*)relëase(.*)', ignoring rules: T1,B2"
- self.assert_log_contains(expected_log_message)
+ ]
+ self.assert_logged(expected_log_messages)
def test_ignore_by_author_name(self):
commit = self.gitcommit("Tïtle\n\nThis is\n a relëase body\n line", author_name="Tëst nåme")
@@ -88,10 +96,13 @@ class ConfigurationRuleTests(BaseTestCase):
rule.apply(config, commit)
self.assertEqual(config, expected_config)
- expected_log_message = ("DEBUG: gitlint.rules Ignoring commit because of rule 'I4': "
- "Commit Author Name 'Tëst nåme' matches the regex '(.*)ëst(.*)',"
- " ignoring rules: all")
- self.assert_log_contains(expected_log_message)
+ expected_log_messages = [
+ EXPECTED_REGEX_STYLE_SEARCH_DEPRECATION_WARNING.format("I4", "ignore-by-author-name"),
+ "DEBUG: gitlint.rules Ignoring commit because of rule 'I4': "
+ "Commit Author Name 'Tëst nåme' matches the regex '(.*)ëst(.*)',"
+ " ignoring rules: all",
+ ]
+ self.assert_logged(expected_log_messages)
# Matching regex with specific ignore
rule = rules.IgnoreByAuthorName({"regex": "(.*)nåme", "ignore": "T1,B2"})
@@ -100,9 +111,11 @@ class ConfigurationRuleTests(BaseTestCase):
rule.apply(config, commit)
self.assertEqual(config, expected_config)
- expected_log_message = ("DEBUG: gitlint.rules Ignoring commit because of rule 'I4': "
- "Commit Author Name 'Tëst nåme' matches the regex '(.*)nåme', ignoring rules: T1,B2")
- self.assert_log_contains(expected_log_message)
+ expected_log_messages += [
+ "DEBUG: gitlint.rules Ignoring commit because of rule 'I4': "
+ "Commit Author Name 'Tëst nåme' matches the regex '(.*)nåme', ignoring rules: T1,B2"
+ ]
+ self.assert_logged(expected_log_messages)
def test_ignore_body_lines(self):
commit1 = self.gitcommit("Tïtle\n\nThis is\n a relëase body\n line")
@@ -128,8 +141,11 @@ class ConfigurationRuleTests(BaseTestCase):
expected_commit.message.original = commit1.message.original
self.assertEqual(commit1, expected_commit)
self.assertEqual(config, LintConfig()) # config shouldn't have been modified
- self.assert_log_contains("DEBUG: gitlint.rules Ignoring line ' a relëase body' because it " +
- "matches '(.*)relëase(.*)'")
+ expected_log_messages = [
+ EXPECTED_REGEX_STYLE_SEARCH_DEPRECATION_WARNING.format("I3", "ignore-body-lines"),
+ "DEBUG: gitlint.rules Ignoring line ' a relëase body' because it " + "matches '(.*)relëase(.*)'",
+ ]
+ self.assert_logged(expected_log_messages)
# Non-Matching regex: no changes expected
commit1 = self.gitcommit("Tïtle\n\nThis is\n a relëase body\n line")
diff --git a/gitlint-core/gitlint/tests/rules/test_meta_rules.py b/gitlint-core/gitlint/tests/rules/test_meta_rules.py
index 568ca3f..0b8a10a 100644
--- a/gitlint-core/gitlint/tests/rules/test_meta_rules.py
+++ b/gitlint-core/gitlint/tests/rules/test_meta_rules.py
@@ -1,5 +1,4 @@
-# -*- coding: utf-8 -*-
-from gitlint.tests.base import BaseTestCase
+from gitlint.tests.base import BaseTestCase, EXPECTED_REGEX_STYLE_SEARCH_DEPRECATION_WARNING
from gitlint.rules import AuthorValidEmail, RuleViolation
@@ -8,8 +7,13 @@ class MetaRuleTests(BaseTestCase):
rule = AuthorValidEmail()
# valid email addresses
- valid_email_addresses = ["föo@bar.com", "Jöhn.Doe@bar.com", "jöhn+doe@bar.com", "jöhn/doe@bar.com",
- "jöhn.doe@subdomain.bar.com"]
+ valid_email_addresses = [
+ "föo@bar.com",
+ "Jöhn.Doe@bar.com",
+ "jöhn+doe@bar.com",
+ "jöhn/doe@bar.com",
+ "jöhn.doe@subdomain.bar.com",
+ ]
for email in valid_email_addresses:
commit = self.gitcommit("", author_email=email)
violations = rule.validate(commit)
@@ -22,19 +26,32 @@ class MetaRuleTests(BaseTestCase):
self.assertIsNone(violations)
# Invalid email addresses: no TLD, no domain, no @, space anywhere (=valid but not allowed by gitlint)
- invalid_email_addresses = ["föo@bar", "JöhnDoe", "Jöhn Doe", "Jöhn Doe@foo.com", " JöhnDoe@foo.com",
- "JöhnDoe@ foo.com", "JöhnDoe@foo. com", "JöhnDoe@foo. com", "@bår.com",
- "föo@.com"]
+ invalid_email_addresses = [
+ "föo@bar",
+ "JöhnDoe",
+ "Jöhn Doe",
+ "Jöhn Doe@foo.com",
+ " JöhnDoe@foo.com",
+ "JöhnDoe@ foo.com",
+ "JöhnDoe@foo. com",
+ "JöhnDoe@foo. com",
+ "@bår.com",
+ "föo@.com",
+ ]
for email in invalid_email_addresses:
commit = self.gitcommit("", author_email=email)
violations = rule.validate(commit)
- self.assertListEqual(violations,
- [RuleViolation("M1", "Author email for commit is invalid", email)])
+ self.assertListEqual(violations, [RuleViolation("M1", "Author email for commit is invalid", email)])
+
+ # Ensure nothing is logged, this relates specifically to a deprecation warning on the use of
+ # re.match vs re.search in the rules (see issue #254)
+ # If no custom regex is used, the rule uses the default regex in combination with re.search
+ self.assert_logged([])
def test_author_valid_email_rule_custom_regex(self):
# regex=None -> the rule isn't applied
rule = AuthorValidEmail()
- rule.options['regex'].set(None)
+ rule.options["regex"].set(None)
emailadresses = ["föo", None, "hür dür"]
for email in emailadresses:
commit = self.gitcommit("", author_email=email)
@@ -42,9 +59,8 @@ class MetaRuleTests(BaseTestCase):
self.assertIsNone(violations)
# Custom domain
- rule = AuthorValidEmail({'regex': "[^@]+@bår.com"})
- valid_email_addresses = [
- "föo@bår.com", "Jöhn.Doe@bår.com", "jöhn+doe@bår.com", "jöhn/doe@bår.com"]
+ rule = AuthorValidEmail({"regex": "[^@]+@bår.com"})
+ valid_email_addresses = ["föo@bår.com", "Jöhn.Doe@bår.com", "jöhn+doe@bår.com", "jöhn/doe@bår.com"]
for email in valid_email_addresses:
commit = self.gitcommit("", author_email=email)
violations = rule.validate(commit)
@@ -55,5 +71,7 @@ class MetaRuleTests(BaseTestCase):
for email in invalid_email_addresses:
commit = self.gitcommit("", author_email=email)
violations = rule.validate(commit)
- self.assertListEqual(violations,
- [RuleViolation("M1", "Author email for commit is invalid", email)])
+ self.assertListEqual(violations, [RuleViolation("M1", "Author email for commit is invalid", email)])
+
+ # When a custom regex is used, a warning should be logged by default
+ self.assert_logged([EXPECTED_REGEX_STYLE_SEARCH_DEPRECATION_WARNING.format("M1", "author-valid-email")])
diff --git a/gitlint-core/gitlint/tests/rules/test_rules.py b/gitlint-core/gitlint/tests/rules/test_rules.py
index 6fcf9bc..199cc7e 100644
--- a/gitlint-core/gitlint/tests/rules/test_rules.py
+++ b/gitlint-core/gitlint/tests/rules/test_rules.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
from gitlint.tests.base import BaseTestCase
from gitlint.rules import Rule, RuleViolation
class RuleTests(BaseTestCase):
-
def test_rule_equality(self):
self.assertEqual(Rule(), Rule())
# Ensure rules are not equal if they differ on their attributes
diff --git a/gitlint-core/gitlint/tests/rules/test_title_rules.py b/gitlint-core/gitlint/tests/rules/test_title_rules.py
index 10b4aab..4796e54 100644
--- a/gitlint-core/gitlint/tests/rules/test_title_rules.py
+++ b/gitlint-core/gitlint/tests/rules/test_title_rules.py
@@ -1,7 +1,15 @@
-# -*- coding: utf-8 -*-
from gitlint.tests.base import BaseTestCase
-from gitlint.rules import TitleMaxLength, TitleTrailingWhitespace, TitleHardTab, TitleMustNotContainWord, \
- TitleTrailingPunctuation, TitleLeadingWhitespace, TitleRegexMatches, RuleViolation, TitleMinLength
+from gitlint.rules import (
+ TitleMaxLength,
+ TitleTrailingWhitespace,
+ TitleHardTab,
+ TitleMustNotContainWord,
+ TitleTrailingPunctuation,
+ TitleLeadingWhitespace,
+ TitleRegexMatches,
+ RuleViolation,
+ TitleMinLength,
+)
class TitleRuleTests(BaseTestCase):
@@ -18,7 +26,7 @@ class TitleRuleTests(BaseTestCase):
self.assertListEqual(violations, [expected_violation])
# set line length to 120, and check no violation on length 73
- rule = TitleMaxLength({'line-length': 120})
+ rule = TitleMaxLength({"line-length": 120})
violations = rule.validate("å" * 73, None)
self.assertIsNone(violations)
@@ -85,31 +93,37 @@ class TitleRuleTests(BaseTestCase):
# match literally
violations = rule.validate("WIP This is å test", None)
- expected_violation = RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)",
- "WIP This is å test")
+ expected_violation = RuleViolation(
+ "T5", "Title contains the word 'WIP' (case-insensitive)", "WIP This is å test"
+ )
self.assertListEqual(violations, [expected_violation])
# match case insensitive
violations = rule.validate("wip This is å test", None)
- expected_violation = RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)",
- "wip This is å test")
+ expected_violation = RuleViolation(
+ "T5", "Title contains the word 'WIP' (case-insensitive)", "wip This is å test"
+ )
self.assertListEqual(violations, [expected_violation])
# match if there is a colon after the word
violations = rule.validate("WIP:This is å test", None)
- expected_violation = RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)",
- "WIP:This is å test")
+ expected_violation = RuleViolation(
+ "T5", "Title contains the word 'WIP' (case-insensitive)", "WIP:This is å test"
+ )
self.assertListEqual(violations, [expected_violation])
# match multiple words
- rule = TitleMustNotContainWord({'words': "wip,test,å"})
+ rule = TitleMustNotContainWord({"words": "wip,test,å"})
violations = rule.validate("WIP:This is å test", None)
- expected_violation = RuleViolation("T5", "Title contains the word 'wip' (case-insensitive)",
- "WIP:This is å test")
- expected_violation2 = RuleViolation("T5", "Title contains the word 'test' (case-insensitive)",
- "WIP:This is å test")
- expected_violation3 = RuleViolation("T5", "Title contains the word 'å' (case-insensitive)",
- "WIP:This is å test")
+ expected_violation = RuleViolation(
+ "T5", "Title contains the word 'wip' (case-insensitive)", "WIP:This is å test"
+ )
+ expected_violation2 = RuleViolation(
+ "T5", "Title contains the word 'test' (case-insensitive)", "WIP:This is å test"
+ )
+ expected_violation3 = RuleViolation(
+ "T5", "Title contains the word 'å' (case-insensitive)", "WIP:This is å test"
+ )
self.assertListEqual(violations, [expected_violation, expected_violation2, expected_violation3])
def test_leading_whitespace(self):
@@ -143,12 +157,12 @@ class TitleRuleTests(BaseTestCase):
self.assertIsNone(violations)
# assert no violation on matching regex
- rule = TitleRegexMatches({'regex': "^US[0-9]*: å"})
+ rule = TitleRegexMatches({"regex": "^US[0-9]*: å"})
violations = rule.validate(commit.message.title, commit)
self.assertIsNone(violations)
# assert violation when no matching regex
- rule = TitleRegexMatches({'regex': "^UÅ[0-9]*"})
+ rule = TitleRegexMatches({"regex": "^UÅ[0-9]*"})
violations = rule.validate(commit.message.title, commit)
expected_violation = RuleViolation("T7", "Title does not match regex (^UÅ[0-9]*)", "US1234: åbc")
self.assertListEqual(violations, [expected_violation])
@@ -166,12 +180,12 @@ class TitleRuleTests(BaseTestCase):
self.assertListEqual(violations, [expected_violation])
# set line length to 3, and check no violation on length 4
- rule = TitleMinLength({'min-length': 3})
+ rule = TitleMinLength({"min-length": 3})
violations = rule.validate("å" * 4, None)
self.assertIsNone(violations)
# assert no violations on length 3 (this asserts we've implemented a *strict* less than)
- rule = TitleMinLength({'min-length': 3})
+ rule = TitleMinLength({"min-length": 3})
violations = rule.validate("å" * 3, None)
self.assertIsNone(violations)
diff --git a/gitlint-core/gitlint/tests/rules/test_user_rules.py b/gitlint-core/gitlint/tests/rules/test_user_rules.py
index d66a7cc..fc8d423 100644
--- a/gitlint-core/gitlint/tests/rules/test_user_rules.py
+++ b/gitlint-core/gitlint/tests/rules/test_user_rules.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
import os
import sys
@@ -33,7 +31,7 @@ class UserRuleTests(BaseTestCase):
# 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")
+ 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"))
@@ -44,10 +42,15 @@ class UserRuleTests(BaseTestCase):
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
+ 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)])
+ 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 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
@@ -67,7 +70,7 @@ class UserRuleTests(BaseTestCase):
classes = find_rule_classes(user_rule_path)
# convert classes to strings and sort them so we can compare them
- class_strings = sorted([str(clazz) for clazz in classes])
+ class_strings = sorted(str(clazz) for clazz in classes)
expected = ["<class 'my_commit_rules.MyUserCommitRule'>", "<class 'parent_package.InitFileRule'>"]
self.assertListEqual(class_strings, expected)
@@ -96,23 +99,23 @@ class UserRuleTests(BaseTestCase):
def test_assert_valid_rule_class(self):
class MyLineRuleClass(rules.LineRule):
- id = 'UC1'
- name = 'my-lïne-rule'
+ id = "UC1"
+ name = "my-lïne-rule"
target = rules.CommitMessageTitle
def validate(self):
pass
class MyCommitRuleClass(rules.CommitRule):
- id = 'UC2'
- name = 'my-cömmit-rule'
+ id = "UC2"
+ name = "my-cömmit-rule"
def validate(self):
pass
class MyConfigurationRuleClass(rules.ConfigurationRule):
- id = 'UC3'
- name = 'my-cönfiguration-rule'
+ id = "UC3"
+ name = "my-cönfiguration-rule"
def apply(self):
pass
@@ -125,8 +128,9 @@ class UserRuleTests(BaseTestCase):
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"):
+ with self.assertRaisesMessage(
+ UserRuleError, "User-defined rule class 'MyUserLineRule' must have a 'validate' method"
+ ):
find_rule_classes(user_rule_path)
def test_assert_valid_rule_class_negative_parent(self):
@@ -134,13 +138,14 @@ class UserRuleTests(BaseTestCase):
class MyRuleClass:
pass
- expected_msg = "User-defined rule class 'MyRuleClass' must extend from gitlint.rules.LineRule, " + \
- "gitlint.rules.CommitRule or gitlint.rules.ConfigurationRule"
+ expected_msg = (
+ "User-defined rule class 'MyRuleClass' must extend from gitlint.rules.LineRule, "
+ "gitlint.rules.CommitRule or gitlint.rules.ConfigurationRule"
+ )
with self.assertRaisesMessage(UserRuleError, expected_msg):
assert_valid_rule_class(MyRuleClass)
def test_assert_valid_rule_class_negative_id(self):
-
for parent_class in [rules.LineRule, rules.CommitRule]:
class MyRuleClass(parent_class):
@@ -159,8 +164,9 @@ class UserRuleTests(BaseTestCase):
# Rule ids must not start with one of the reserved id letters
for letter in ["T", "R", "B", "M", "I"]:
MyRuleClass.id = letter + "1"
- expected_msg = f"The id '{letter}' of 'MyRuleClass' is invalid. " + \
- "Gitlint reserves ids starting with R,T,B,M,I"
+ expected_msg = (
+ f"The id '{letter}' of 'MyRuleClass' is invalid. Gitlint reserves ids starting with R,T,B,M,I"
+ )
with self.assertRaisesMessage(UserRuleError, expected_msg):
assert_valid_rule_class(MyRuleClass)
@@ -181,7 +187,6 @@ class UserRuleTests(BaseTestCase):
assert_valid_rule_class(MyRuleClass)
def test_assert_valid_rule_class_negative_option_spec(self):
-
for parent_class in [rules.LineRule, rules.CommitRule]:
class MyRuleClass(parent_class):
@@ -190,8 +195,10 @@ class UserRuleTests(BaseTestCase):
# if set, option_spec must be a list of gitlint options
MyRuleClass.options_spec = "föo"
- expected_msg = "The options_spec attribute of user-defined rule class 'MyRuleClass' must be a list " + \
+ expected_msg = (
+ "The options_spec attribute of user-defined rule class 'MyRuleClass' must be a list "
"of gitlint.options.RuleOption"
+ )
with self.assertRaisesMessage(UserRuleError, expected_msg):
assert_valid_rule_class(MyRuleClass)
@@ -201,21 +208,23 @@ class UserRuleTests(BaseTestCase):
assert_valid_rule_class(MyRuleClass)
def test_assert_valid_rule_class_negative_validate(self):
-
baseclasses = [rules.LineRule, rules.CommitRule]
for clazz in baseclasses:
+
class MyRuleClass(clazz):
id = "UC1"
name = "my-rüle-class"
- with self.assertRaisesMessage(UserRuleError,
- "User-defined rule class 'MyRuleClass' must have a 'validate' method"):
+ with self.assertRaisesMessage(
+ UserRuleError, "User-defined rule class 'MyRuleClass' must have a 'validate' method"
+ ):
assert_valid_rule_class(MyRuleClass)
# validate attribute - not a method
MyRuleClass.validate = "föo"
- with self.assertRaisesMessage(UserRuleError,
- "User-defined rule class 'MyRuleClass' must have a 'validate' method"):
+ with self.assertRaisesMessage(
+ UserRuleError, "User-defined rule class 'MyRuleClass' must have a 'validate' method"
+ ):
assert_valid_rule_class(MyRuleClass)
def test_assert_valid_rule_class_negative_apply(self):
@@ -241,8 +250,10 @@ class UserRuleTests(BaseTestCase):
pass
# no target
- expected_msg = "The target attribute of the user-defined LineRule class 'MyRuleClass' must be either " + \
- "gitlint.rules.CommitMessageTitle or gitlint.rules.CommitMessageBody"
+ expected_msg = (
+ "The target attribute of the user-defined LineRule class 'MyRuleClass' must be either "
+ "gitlint.rules.CommitMessageTitle or gitlint.rules.CommitMessageBody"
+ )
with self.assertRaisesMessage(UserRuleError, expected_msg):
assert_valid_rule_class(MyRuleClass)
diff --git a/gitlint-core/gitlint/tests/samples/commit_message/fixup_amend b/gitlint-core/gitlint/tests/samples/commit_message/fixup_amend
new file mode 100644
index 0000000..293a2b7
--- /dev/null
+++ b/gitlint-core/gitlint/tests/samples/commit_message/fixup_amend
@@ -0,0 +1 @@
+amend! WIP: This is a fixup cömmit with violations.
diff --git a/gitlint-core/gitlint/tests/samples/config/AUTHORS b/gitlint-core/gitlint/tests/samples/config/AUTHORS
new file mode 100644
index 0000000..1c355d6
--- /dev/null
+++ b/gitlint-core/gitlint/tests/samples/config/AUTHORS
@@ -0,0 +1,2 @@
+John Doe <john.doe@mail.com>
+Bob Smith <bob.smith@mail.com> \ No newline at end of file
diff --git a/gitlint-core/gitlint/tests/samples/user_rules/import_exception/invalid_python.py b/gitlint-core/gitlint/tests/samples/user_rules/import_exception/invalid_python.py
index e75fed3..a123a64 100644
--- a/gitlint-core/gitlint/tests/samples/user_rules/import_exception/invalid_python.py
+++ b/gitlint-core/gitlint/tests/samples/user_rules/import_exception/invalid_python.py
@@ -1,3 +1,2 @@
-# flake8: noqa
# This is invalid python code which will cause an import exception
class MyObject:
diff --git a/gitlint-core/gitlint/tests/samples/user_rules/incorrect_linerule/my_line_rule.py b/gitlint-core/gitlint/tests/samples/user_rules/incorrect_linerule/my_line_rule.py
index 004ef9d..b23b5bf 100644
--- a/gitlint-core/gitlint/tests/samples/user_rules/incorrect_linerule/my_line_rule.py
+++ b/gitlint-core/gitlint/tests/samples/user_rules/incorrect_linerule/my_line_rule.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
from gitlint.rules import LineRule
diff --git a/gitlint-core/gitlint/tests/samples/user_rules/my_commit_rules.py b/gitlint-core/gitlint/tests/samples/user_rules/my_commit_rules.py
index 8b0907e..02c922d 100644
--- a/gitlint-core/gitlint/tests/samples/user_rules/my_commit_rules.py
+++ b/gitlint-core/gitlint/tests/samples/user_rules/my_commit_rules.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
from gitlint.rules import CommitRule, RuleViolation
from gitlint.options import IntOption
@@ -7,11 +5,11 @@ from gitlint.options import IntOption
class MyUserCommitRule(CommitRule):
name = "my-üser-commit-rule"
id = "UC1"
- options_spec = [IntOption('violation-count', 1, "Number of violåtions to return")]
+ options_spec = [IntOption("violation-count", 1, "Number of violåtions to return")]
def validate(self, _commit):
violations = []
- for i in range(1, self.options['violation-count'].value + 1):
+ for i in range(1, self.options["violation-count"].value + 1):
violations.append(RuleViolation(self.id, "Commit violåtion %d" % i, "Contënt %d" % i, i))
return violations
@@ -19,6 +17,7 @@ class MyUserCommitRule(CommitRule):
# The below code is present so that we can test that we actually ignore it
+
def func_should_be_ignored():
pass
diff --git a/gitlint-core/gitlint/tests/samples/user_rules/parent_package/__init__.py b/gitlint-core/gitlint/tests/samples/user_rules/parent_package/__init__.py
index 9ea5371..22c3f65 100644
--- a/gitlint-core/gitlint/tests/samples/user_rules/parent_package/__init__.py
+++ b/gitlint-core/gitlint/tests/samples/user_rules/parent_package/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
# This file is meant to test that we can also load rules from __init__.py files, this was an issue with pypy before.
from gitlint.rules import CommitRule
diff --git a/gitlint-core/gitlint/tests/samples/user_rules/parent_package/my_commit_rules.py b/gitlint-core/gitlint/tests/samples/user_rules/parent_package/my_commit_rules.py
index b143e62..f91cb07 100644
--- a/gitlint-core/gitlint/tests/samples/user_rules/parent_package/my_commit_rules.py
+++ b/gitlint-core/gitlint/tests/samples/user_rules/parent_package/my_commit_rules.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
from gitlint.rules import CommitRule
diff --git a/gitlint-core/gitlint/tests/test_cache.py b/gitlint-core/gitlint/tests/test_cache.py
index 4b1d47a..9c327dc 100644
--- a/gitlint-core/gitlint/tests/test_cache.py
+++ b/gitlint-core/gitlint/tests/test_cache.py
@@ -1,12 +1,10 @@
-# -*- coding: utf-8 -*-
from gitlint.tests.base import BaseTestCase
from gitlint.cache import PropertyCache, cache
class CacheTests(BaseTestCase):
-
class MyClass(PropertyCache):
- """ Simple class that has cached properties, used for testing. """
+ """Simple class that has cached properties, used for testing."""
def __init__(self):
PropertyCache.__init__(self)
diff --git a/gitlint-core/gitlint/tests/test_deprecation.py b/gitlint-core/gitlint/tests/test_deprecation.py
new file mode 100644
index 0000000..d85593a
--- /dev/null
+++ b/gitlint-core/gitlint/tests/test_deprecation.py
@@ -0,0 +1,23 @@
+from gitlint.config import LintConfig
+from gitlint.deprecation import Deprecation
+from gitlint.rules import IgnoreByTitle
+from gitlint.tests.base import EXPECTED_REGEX_STYLE_SEARCH_DEPRECATION_WARNING, BaseTestCase
+
+
+class DeprecationTests(BaseTestCase):
+ def test_get_regex_method(self):
+ config = LintConfig()
+ Deprecation.config = config
+ rule = IgnoreByTitle({"regex": "^Releäse(.*)"})
+
+ # When general.regex-style-search=True, we expect regex.search to be returned and no warning to be logged
+ config.regex_style_search = True
+ regex_method = Deprecation.get_regex_method(rule, rule.options["regex"])
+ self.assertEqual(regex_method, rule.options["regex"].value.search)
+ self.assert_logged([])
+
+ # When general.regex-style-search=False, we expect regex.match to be returned and a warning to be logged
+ config.regex_style_search = False
+ regex_method = Deprecation.get_regex_method(rule, rule.options["regex"])
+ self.assertEqual(regex_method, rule.options["regex"].value.match)
+ self.assert_logged([EXPECTED_REGEX_STYLE_SEARCH_DEPRECATION_WARNING.format("I1", "ignore-by-title")])
diff --git a/gitlint-core/gitlint/tests/test_display.py b/gitlint-core/gitlint/tests/test_display.py
index 167ef96..1f759d2 100644
--- a/gitlint-core/gitlint/tests/test_display.py
+++ b/gitlint-core/gitlint/tests/test_display.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
from io import StringIO
from unittest.mock import patch # pylint: disable=no-name-in-module, import-error
@@ -14,9 +12,9 @@ class DisplayTests(BaseTestCase):
display = Display(LintConfig())
display.config.verbosity = 2
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
# Non exact outputting, should output both v and vv output
- with patch('gitlint.display.stdout', new=StringIO()) as stdout:
+ with patch("gitlint.display.stdout", new=StringIO()) as stdout:
display.v("tëst")
display.vv("tëst2")
# vvvv should be ignored regardless
@@ -25,7 +23,7 @@ class DisplayTests(BaseTestCase):
self.assertEqual("tëst\ntëst2\n", stdout.getvalue())
# exact outputting, should only output v
- with patch('gitlint.display.stdout', new=StringIO()) as stdout:
+ with patch("gitlint.display.stdout", new=StringIO()) as stdout:
display.v("tëst", exact=True)
display.vv("tëst2", exact=True)
# vvvv should be ignored regardless
@@ -33,16 +31,16 @@ class DisplayTests(BaseTestCase):
display.vvv("tëst3.2", exact=True)
self.assertEqual("tëst2\n", stdout.getvalue())
- # standard error should be empty throughtout all of this
- self.assertEqual('', stderr.getvalue())
+ # standard error should be empty throughout all of this
+ self.assertEqual("", stderr.getvalue())
def test_e(self):
display = Display(LintConfig())
display.config.verbosity = 2
- with patch('gitlint.display.stdout', new=StringIO()) as stdout:
+ with patch("gitlint.display.stdout", new=StringIO()) as stdout:
# Non exact outputting, should output both v and vv output
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
display.e("tëst")
display.ee("tëst2")
# vvvv should be ignored regardless
@@ -51,7 +49,7 @@ class DisplayTests(BaseTestCase):
self.assertEqual("tëst\ntëst2\n", stderr.getvalue())
# exact outputting, should only output v
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
display.e("tëst", exact=True)
display.ee("tëst2", exact=True)
# vvvv should be ignored regardless
@@ -59,5 +57,5 @@ class DisplayTests(BaseTestCase):
display.eee("tëst3.2", exact=True)
self.assertEqual("tëst2\n", stderr.getvalue())
- # standard output should be empty throughtout all of this
- self.assertEqual('', stdout.getvalue())
+ # standard output should be empty throughout all of this
+ self.assertEqual("", stdout.getvalue())
diff --git a/gitlint-core/gitlint/tests/test_hooks.py b/gitlint-core/gitlint/tests/test_hooks.py
index 0ce5040..f92b148 100644
--- a/gitlint-core/gitlint/tests/test_hooks.py
+++ b/gitlint-core/gitlint/tests/test_hooks.py
@@ -1,18 +1,20 @@
-# -*- coding: utf-8 -*-
-
import os
from unittest.mock import patch, ANY, mock_open
from gitlint.tests.base import BaseTestCase
from gitlint.config import LintConfig
-from gitlint.hooks import GitHookInstaller, GitHookInstallerError, COMMIT_MSG_HOOK_SRC_PATH, COMMIT_MSG_HOOK_DST_PATH, \
- GITLINT_HOOK_IDENTIFIER
+from gitlint.hooks import (
+ GitHookInstaller,
+ GitHookInstallerError,
+ COMMIT_MSG_HOOK_SRC_PATH,
+ COMMIT_MSG_HOOK_DST_PATH,
+ GITLINT_HOOK_IDENTIFIER,
+)
class HookTests(BaseTestCase):
-
- @patch('gitlint.hooks.git_hooks_dir')
+ @patch("gitlint.hooks.git_hooks_dir")
def test_commit_msg_hook_path(self, git_hooks_dir):
git_hooks_dir.return_value = os.path.join("/föo", "bar")
lint_config = LintConfig()
@@ -24,12 +26,12 @@ class HookTests(BaseTestCase):
self.assertEqual(path, expected_path)
@staticmethod
- @patch('os.chmod')
- @patch('os.stat')
- @patch('gitlint.hooks.shutil.copy')
- @patch('os.path.exists', return_value=False)
- @patch('os.path.isdir', return_value=True)
- @patch('gitlint.hooks.git_hooks_dir')
+ @patch("os.chmod")
+ @patch("os.stat")
+ @patch("gitlint.hooks.shutil.copy")
+ @patch("os.path.exists", return_value=False)
+ @patch("os.path.isdir", return_value=True)
+ @patch("gitlint.hooks.git_hooks_dir")
def test_install_commit_msg_hook(git_hooks_dir, isdir, path_exists, copy, stat, chmod):
lint_config = LintConfig()
lint_config.target = os.path.join("/hür", "dur")
@@ -43,10 +45,10 @@ class HookTests(BaseTestCase):
chmod.assert_called_once_with(expected_dst, ANY)
git_hooks_dir.assert_called_with(lint_config.target)
- @patch('gitlint.hooks.shutil.copy')
- @patch('os.path.exists', return_value=False)
- @patch('os.path.isdir', return_value=True)
- @patch('gitlint.hooks.git_hooks_dir')
+ @patch("gitlint.hooks.shutil.copy")
+ @patch("os.path.exists", return_value=False)
+ @patch("os.path.isdir", return_value=True)
+ @patch("gitlint.hooks.git_hooks_dir")
def test_install_commit_msg_hook_negative(self, git_hooks_dir, isdir, path_exists, copy):
lint_config = LintConfig()
lint_config.target = os.path.join("/hür", "dur")
@@ -64,22 +66,24 @@ class HookTests(BaseTestCase):
isdir.return_value = True
path_exists.return_value = True
expected_dst = os.path.join(git_hooks_dir.return_value, COMMIT_MSG_HOOK_DST_PATH)
- expected_msg = f"There is already a commit-msg hook file present in {expected_dst}.\n" + \
- "gitlint currently does not support appending to an existing commit-msg file."
+ expected_msg = (
+ f"There is already a commit-msg hook file present in {expected_dst}.\n"
+ "gitlint currently does not support appending to an existing commit-msg file."
+ )
with self.assertRaisesMessage(GitHookInstallerError, expected_msg):
GitHookInstaller.install_commit_msg_hook(lint_config)
@staticmethod
- @patch('os.remove')
- @patch('os.path.exists', return_value=True)
- @patch('os.path.isdir', return_value=True)
- @patch('gitlint.hooks.git_hooks_dir')
+ @patch("os.remove")
+ @patch("os.path.exists", return_value=True)
+ @patch("os.path.isdir", return_value=True)
+ @patch("gitlint.hooks.git_hooks_dir")
def test_uninstall_commit_msg_hook(git_hooks_dir, isdir, path_exists, remove):
lint_config = LintConfig()
git_hooks_dir.return_value = os.path.join("/föo", "bar", ".git", "hooks")
lint_config.target = os.path.join("/hür", "dur")
read_data = "#!/bin/sh\n" + GITLINT_HOOK_IDENTIFIER
- with patch('gitlint.hooks.io.open', mock_open(read_data=read_data), create=True):
+ with patch("builtins.open", mock_open(read_data=read_data), create=True):
GitHookInstaller.uninstall_commit_msg_hook(lint_config)
expected_dst = os.path.join(git_hooks_dir.return_value, COMMIT_MSG_HOOK_DST_PATH)
@@ -88,10 +92,10 @@ class HookTests(BaseTestCase):
remove.assert_called_with(expected_dst)
git_hooks_dir.assert_called_with(lint_config.target)
- @patch('os.remove')
- @patch('os.path.exists', return_value=True)
- @patch('os.path.isdir', return_value=True)
- @patch('gitlint.hooks.git_hooks_dir')
+ @patch("os.remove")
+ @patch("os.path.exists", return_value=True)
+ @patch("os.path.isdir", return_value=True)
+ @patch("gitlint.hooks.git_hooks_dir")
def test_uninstall_commit_msg_hook_negative(self, git_hooks_dir, isdir, path_exists, remove):
lint_config = LintConfig()
lint_config.target = os.path.join("/hür", "dur")
@@ -122,10 +126,12 @@ class HookTests(BaseTestCase):
path_exists.return_value = True
read_data = "#!/bin/sh\nfoo"
expected_dst = os.path.join(git_hooks_dir.return_value, COMMIT_MSG_HOOK_DST_PATH)
- expected_msg = f"The commit-msg hook in {expected_dst} was not installed by gitlint " + \
- "(or it was modified).\nUninstallation of 3th party or modified gitlint hooks " + \
- "is not supported."
- with patch('gitlint.hooks.io.open', mock_open(read_data=read_data), create=True):
+ expected_msg = (
+ f"The commit-msg hook in {expected_dst} was not installed by gitlint "
+ "(or it was modified).\nUninstallation of 3th party or modified gitlint hooks "
+ "is not supported."
+ )
+ with patch("builtins.open", mock_open(read_data=read_data), create=True):
with self.assertRaisesMessage(GitHookInstallerError, expected_msg):
GitHookInstaller.uninstall_commit_msg_hook(lint_config)
remove.assert_not_called()
diff --git a/gitlint-core/gitlint/tests/test_lint.py b/gitlint-core/gitlint/tests/test_lint.py
index b743389..2af4615 100644
--- a/gitlint-core/gitlint/tests/test_lint.py
+++ b/gitlint-core/gitlint/tests/test_lint.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
from io import StringIO
from unittest.mock import patch # pylint: disable=no-name-in-module, import-error
@@ -11,23 +9,26 @@ from gitlint.config import LintConfig, LintConfigBuilder
class LintTests(BaseTestCase):
-
def test_lint_sample1(self):
linter = GitLinter(LintConfig())
gitcontext = self.gitcontext(self.get_sample("commit_message/sample1"))
violations = linter.lint(gitcontext.commits[-1])
- expected_errors = [RuleViolation("T3", "Title has trailing punctuation (.)",
- "Commit title contåining 'WIP', as well as trailing punctuation.", 1),
- RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)",
- "Commit title contåining 'WIP', as well as trailing punctuation.", 1),
- RuleViolation("B4", "Second line is not empty", "This line should be empty", 2),
- RuleViolation("B1", "Line exceeds max length (135>80)",
- "This is the first line of the commit message body and it is meant to test " +
- "a line that exceeds the maximum line length of 80 characters.", 3),
- RuleViolation("B2", "Line has trailing whitespace", "This line has a tråiling space. ", 4),
- RuleViolation("B2", "Line has trailing whitespace", "This line has a trailing tab.\t", 5),
- RuleViolation("B3", "Line contains hard tab characters (\\t)",
- "This line has a trailing tab.\t", 5)]
+ # fmt: off
+ expected_errors = [
+ RuleViolation("T3", "Title has trailing punctuation (.)",
+ "Commit title contåining 'WIP', as well as trailing punctuation.", 1),
+ RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)",
+ "Commit title contåining 'WIP', as well as trailing punctuation.", 1),
+ RuleViolation("B4", "Second line is not empty", "This line should be empty", 2),
+ RuleViolation("B1", "Line exceeds max length (135>80)",
+ "This is the first line of the commit message body and it is meant to test " +
+ "a line that exceeds the maximum line length of 80 characters.", 3),
+ RuleViolation("B2", "Line has trailing whitespace", "This line has a tråiling space. ", 4),
+ RuleViolation("B2", "Line has trailing whitespace", "This line has a trailing tab.\t", 5),
+ RuleViolation("B3", "Line contains hard tab characters (\\t)",
+ "This line has a trailing tab.\t", 5)
+ ]
+ # fmt: on
self.assertListEqual(violations, expected_errors)
@@ -35,9 +36,10 @@ class LintTests(BaseTestCase):
linter = GitLinter(LintConfig())
gitcontext = self.gitcontext(self.get_sample("commit_message/sample2"))
violations = linter.lint(gitcontext.commits[-1])
- expected = [RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)",
- "Just a title contåining WIP", 1),
- RuleViolation("B6", "Body message is missing", None, 3)]
+ expected = [
+ RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)", "Just a title contåining WIP", 1),
+ RuleViolation("B6", "Body message is missing", None, 3),
+ ]
self.assertListEqual(violations, expected)
@@ -46,20 +48,24 @@ class LintTests(BaseTestCase):
gitcontext = self.gitcontext(self.get_sample("commit_message/sample3"))
violations = linter.lint(gitcontext.commits[-1])
+ # fmt: off
title = " Commit title containing 'WIP', \tleading and tråiling whitespace and longer than 72 characters."
- expected = [RuleViolation("T1", "Title exceeds max length (95>72)", title, 1),
- RuleViolation("T3", "Title has trailing punctuation (.)", title, 1),
- RuleViolation("T4", "Title contains hard tab characters (\\t)", title, 1),
- RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)", title, 1),
- RuleViolation("T6", "Title has leading whitespace", title, 1),
- RuleViolation("B4", "Second line is not empty", "This line should be empty", 2),
- RuleViolation("B1", "Line exceeds max length (101>80)",
- "This is the first line is meånt to test a line that exceeds the maximum line " +
- "length of 80 characters.", 3),
- RuleViolation("B2", "Line has trailing whitespace", "This line has a trailing space. ", 4),
- RuleViolation("B2", "Line has trailing whitespace", "This line has a tråiling tab.\t", 5),
- RuleViolation("B3", "Line contains hard tab characters (\\t)",
- "This line has a tråiling tab.\t", 5)]
+ expected = [
+ RuleViolation("T1", "Title exceeds max length (95>72)", title, 1),
+ RuleViolation("T3", "Title has trailing punctuation (.)", title, 1),
+ RuleViolation("T4", "Title contains hard tab characters (\\t)", title, 1),
+ RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)", title, 1),
+ RuleViolation("T6", "Title has leading whitespace", title, 1),
+ RuleViolation("B4", "Second line is not empty", "This line should be empty", 2),
+ RuleViolation("B1", "Line exceeds max length (101>80)",
+ "This is the first line is meånt to test a line that exceeds the maximum line " +
+ "length of 80 characters.", 3),
+ RuleViolation("B2", "Line has trailing whitespace", "This line has a trailing space. ", 4),
+ RuleViolation("B2", "Line has trailing whitespace", "This line has a tråiling tab.\t", 5),
+ RuleViolation("B3", "Line contains hard tab characters (\\t)",
+ "This line has a tråiling tab.\t", 5)
+ ]
+ # fmt: on
self.assertListEqual(violations, expected)
@@ -82,26 +88,28 @@ class LintTests(BaseTestCase):
title = " Commit title containing 'WIP', \tleading and tråiling whitespace and longer than 72 characters."
# expect only certain violations because sample5 has a 'gitlint-ignore: T3, T6, body-max-line-length'
- expected = [RuleViolation("T1", "Title exceeds max length (95>72)", title, 1),
- RuleViolation("T4", "Title contains hard tab characters (\\t)", title, 1),
- RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)", title, 1),
- RuleViolation("B4", "Second line is not empty", "This line should be ëmpty", 2),
- RuleViolation("B2", "Line has trailing whitespace", "This line has a tråiling space. ", 4),
- RuleViolation("B2", "Line has trailing whitespace", "This line has a trailing tab.\t", 5),
- RuleViolation("B3", "Line contains hard tab characters (\\t)",
- "This line has a trailing tab.\t", 5)]
+ expected = [
+ RuleViolation("T1", "Title exceeds max length (95>72)", title, 1),
+ RuleViolation("T4", "Title contains hard tab characters (\\t)", title, 1),
+ RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)", title, 1),
+ RuleViolation("B4", "Second line is not empty", "This line should be ëmpty", 2),
+ RuleViolation("B2", "Line has trailing whitespace", "This line has a tråiling space. ", 4),
+ RuleViolation("B2", "Line has trailing whitespace", "This line has a trailing tab.\t", 5),
+ RuleViolation("B3", "Line contains hard tab characters (\\t)", "This line has a trailing tab.\t", 5),
+ ]
self.assertListEqual(violations, expected)
def test_lint_meta(self):
- """ Lint sample2 but also add some metadata to the commit so we that gets linted as well """
+ """Lint sample2 but also add some metadata to the commit so we that gets linted as well"""
linter = GitLinter(LintConfig())
gitcontext = self.gitcontext(self.get_sample("commit_message/sample2"))
gitcontext.commits[0].author_email = "foo bår"
violations = linter.lint(gitcontext.commits[-1])
- expected = [RuleViolation("M1", "Author email for commit is invalid", "foo bår", None),
- RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)",
- "Just a title contåining WIP", 1),
- RuleViolation("B6", "Body message is missing", None, 3)]
+ expected = [
+ RuleViolation("M1", "Author email for commit is invalid", "foo bår", None),
+ RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)", "Just a title contåining WIP", 1),
+ RuleViolation("B6", "Body message is missing", None, 3),
+ ]
self.assertListEqual(violations, expected)
@@ -111,9 +119,10 @@ class LintTests(BaseTestCase):
linter = GitLinter(lint_config)
violations = linter.lint(self.gitcommit(self.get_sample("commit_message/sample3")))
- expected = [RuleViolation("B4", "Second line is not empty", "This line should be empty", 2),
- RuleViolation("B3", "Line contains hard tab characters (\\t)",
- "This line has a tråiling tab.\t", 5)]
+ expected = [
+ RuleViolation("B4", "Second line is not empty", "This line should be empty", 2),
+ RuleViolation("B3", "Line contains hard tab characters (\\t)", "This line has a tråiling tab.\t", 5),
+ ]
self.assertListEqual(violations, expected)
@@ -135,8 +144,9 @@ class LintTests(BaseTestCase):
violations = linter.lint(self.gitcommit(self.get_sample("commit_message/sample2")))
# Normally we'd expect a B6 violation, but that one is skipped because of the specific ignore set above
- expected = [RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)",
- "Just a title contåining WIP", 1)]
+ expected = [
+ RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)", "Just a title contåining WIP", 1)
+ ]
self.assertListEqual(violations, expected)
@@ -145,22 +155,25 @@ class LintTests(BaseTestCase):
linter = GitLinter(lint_config)
lint_config.set_rule_option("I3", "regex", "(.*)tråiling(.*)")
violations = linter.lint(self.gitcommit(self.get_sample("commit_message/sample1")))
- expected_errors = [RuleViolation("T3", "Title has trailing punctuation (.)",
- "Commit title contåining 'WIP', as well as trailing punctuation.", 1),
- RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)",
- "Commit title contåining 'WIP', as well as trailing punctuation.", 1),
- RuleViolation("B4", "Second line is not empty", "This line should be empty", 2),
- RuleViolation("B1", "Line exceeds max length (135>80)",
- "This is the first line of the commit message body and it is meant to test " +
- "a line that exceeds the maximum line length of 80 characters.", 3),
- RuleViolation("B2", "Line has trailing whitespace", "This line has a trailing tab.\t", 4),
- RuleViolation("B3", "Line contains hard tab characters (\\t)",
- "This line has a trailing tab.\t", 4)]
-
+ # fmt: off
+ expected_errors = [
+ RuleViolation("T3", "Title has trailing punctuation (.)",
+ "Commit title contåining 'WIP', as well as trailing punctuation.", 1),
+ RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)",
+ "Commit title contåining 'WIP', as well as trailing punctuation.", 1),
+ RuleViolation("B4", "Second line is not empty", "This line should be empty", 2),
+ RuleViolation("B1", "Line exceeds max length (135>80)",
+ "This is the first line of the commit message body and it is meant to test " +
+ "a line that exceeds the maximum line length of 80 characters.", 3),
+ RuleViolation("B2", "Line has trailing whitespace", "This line has a trailing tab.\t", 4),
+ RuleViolation("B3", "Line contains hard tab characters (\\t)",
+ "This line has a trailing tab.\t", 4)
+ ]
+ # fmt: on
self.assertListEqual(violations, expected_errors)
def test_lint_special_commit(self):
- for commit_type in ["merge", "revert", "squash", "fixup"]:
+ for commit_type in ["merge", "revert", "squash", "fixup", "fixup_amend"]:
commit = self.gitcommit(self.get_sample(f"commit_message/{commit_type}"))
lintconfig = LintConfig()
linter = GitLinter(lintconfig)
@@ -176,7 +189,7 @@ class LintTests(BaseTestCase):
self.assertTrue(len(violations) > 0)
def test_lint_regex_rules(self):
- """ Additional test for title-match-regex, body-match-regex """
+ """Additional test for title-match-regex, body-match-regex"""
commit = self.gitcommit(self.get_sample("commit_message/no-violations"))
lintconfig = LintConfig()
linter = GitLinter(lintconfig)
@@ -192,46 +205,52 @@ class LintTests(BaseTestCase):
self.assertListEqual(violations, [])
# Non-matching regexes should return violations
- rule_regexes = [("title-match-regex", ), ("body-match-regex",)]
+ rule_regexes = [("title-match-regex",), ("body-match-regex",)]
lintconfig.set_rule_option("title-match-regex", "regex", "^Tïtle")
lintconfig.set_rule_option("body-match-regex", "regex", "Sügned-Off-By: (.*)$")
- expected_violations = [RuleViolation("T7", "Title does not match regex (^Tïtle)", "Normal Commit Tïtle", 1),
- RuleViolation("B8", "Body does not match regex (Sügned-Off-By: (.*)$)", None, 6)]
+ expected_violations = [
+ RuleViolation("T7", "Title does not match regex (^Tïtle)", "Normal Commit Tïtle", 1),
+ RuleViolation("B8", "Body does not match regex (Sügned-Off-By: (.*)$)", None, 6),
+ ]
violations = linter.lint(commit)
self.assertListEqual(violations, expected_violations)
def test_print_violations(self):
- violations = [RuleViolation("RULE_ID_1", "Error Messåge 1", "Violating Content 1", None),
- RuleViolation("RULE_ID_2", "Error Message 2", "Violåting Content 2", 2)]
+ violations = [
+ RuleViolation("RULE_ID_1", "Error Messåge 1", "Violating Content 1", None),
+ RuleViolation("RULE_ID_2", "Error Message 2", "Violåting Content 2", 2),
+ ]
linter = GitLinter(LintConfig())
# test output with increasing verbosity
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
linter.config.verbosity = 0
linter.print_violations(violations)
self.assertEqual("", stderr.getvalue())
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
linter.config.verbosity = 1
linter.print_violations(violations)
expected = "-: RULE_ID_1\n2: RULE_ID_2\n"
self.assertEqual(expected, stderr.getvalue())
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
linter.config.verbosity = 2
linter.print_violations(violations)
expected = "-: RULE_ID_1 Error Messåge 1\n2: RULE_ID_2 Error Message 2\n"
self.assertEqual(expected, stderr.getvalue())
- with patch('gitlint.display.stderr', new=StringIO()) as stderr:
+ with patch("gitlint.display.stderr", new=StringIO()) as stderr:
linter.config.verbosity = 3
linter.print_violations(violations)
- expected = "-: RULE_ID_1 Error Messåge 1: \"Violating Content 1\"\n" + \
- "2: RULE_ID_2 Error Message 2: \"Violåting Content 2\"\n"
+ expected = (
+ '-: RULE_ID_1 Error Messåge 1: "Violating Content 1"\n'
+ + '2: RULE_ID_2 Error Message 2: "Violåting Content 2"\n'
+ )
self.assertEqual(expected, stderr.getvalue())
def test_named_rules(self):
- """ Test that when named rules are present, both them and the original (non-named) rules executed """
+ """Test that when named rules are present, both them and the original (non-named) rules executed"""
lint_config = LintConfig()
for rule_name in ["my-ïd", "another-rule-ïd"]:
@@ -240,15 +259,15 @@ class LintTests(BaseTestCase):
lint_config.set_rule_option(rule_id, "words", ["Föo"])
linter = GitLinter(lint_config)
- violations = [RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)", "WIP: Föo bar", 1),
- RuleViolation("T5:another-rule-ïd", "Title contains the word 'Föo' (case-insensitive)",
- "WIP: Föo bar", 1),
- RuleViolation("T5:my-ïd", "Title contains the word 'Föo' (case-insensitive)",
- "WIP: Föo bar", 1)]
+ violations = [
+ RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)", "WIP: Föo bar", 1),
+ RuleViolation("T5:another-rule-ïd", "Title contains the word 'Föo' (case-insensitive)", "WIP: Föo bar", 1),
+ RuleViolation("T5:my-ïd", "Title contains the word 'Föo' (case-insensitive)", "WIP: Föo bar", 1),
+ ]
self.assertListEqual(violations, linter.lint(self.gitcommit("WIP: Föo bar\n\nFoo bår hur dur bla bla")))
def test_ignore_named_rules(self):
- """ Test that named rules can be ignored """
+ """Test that named rules can be ignored"""
# Add named rule to lint config
config_builder = LintConfigBuilder()
@@ -259,9 +278,10 @@ class LintTests(BaseTestCase):
commit = self.gitcommit("WIP: Föo bar\n\nFoo bår hur dur bla bla")
# By default, we expect both the violations of the regular rule as well as the named rule to show up
- violations = [RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)", "WIP: Föo bar", 1),
- RuleViolation("T5:my-ïd", "Title contains the word 'Föo' (case-insensitive)",
- "WIP: Föo bar", 1)]
+ violations = [
+ RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)", "WIP: Föo bar", 1),
+ RuleViolation("T5:my-ïd", "Title contains the word 'Föo' (case-insensitive)", "WIP: Föo bar", 1),
+ ]
self.assertListEqual(violations, linter.lint(commit))
# ignore regular rule: only named rule violations show up
diff --git a/gitlint-core/gitlint/tests/test_options.py b/gitlint-core/gitlint/tests/test_options.py
index eabcfe1..7b146e7 100644
--- a/gitlint-core/gitlint/tests/test_options.py
+++ b/gitlint-core/gitlint/tests/test_options.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
import os
import re
@@ -9,8 +8,14 @@ from gitlint.options import IntOption, BoolOption, StrOption, ListOption, PathOp
class RuleOptionTests(BaseTestCase):
def test_option_equality(self):
- options = {IntOption: 123, StrOption: "foöbar", BoolOption: False, ListOption: ["a", "b"],
- PathOption: ".", RegexOption: "^foöbar(.*)"}
+ options = {
+ IntOption: 123,
+ StrOption: "foöbar",
+ BoolOption: False,
+ ListOption: ["a", "b"],
+ PathOption: ".",
+ RegexOption: "^foöbar(.*)",
+ }
for clazz, val in options.items():
# 2 options are equal if their name, value and description match
option1 = clazz("test-öption", val, "Test Dëscription")
@@ -97,7 +102,7 @@ class RuleOptionTests(BaseTestCase):
self.assertEqual(option.value, True)
# error on incorrect value
- incorrect_values = [1, -1, "foo", "bår", ["foo"], {'foo': "bar"}, None]
+ incorrect_values = [1, -1, "foo", "bår", ["foo"], {"foo": "bar"}, None]
for value in incorrect_values:
with self.assertRaisesMessage(RuleOptionError, "Option 'tëst-name' must be either 'true' or 'false'"):
option.set(value)
@@ -197,7 +202,7 @@ class RuleOptionTests(BaseTestCase):
self.assertEqual(option.value, self.get_sample_path())
# Expect exception if path type is invalid
- option.type = 'föo'
+ option.type = "föo"
expected = "Option tëst-directory type must be one of: 'file', 'dir', 'both' (current: 'föo')"
with self.assertRaisesMessage(RuleOptionError, expected):
option.set("haha")
diff --git a/gitlint-core/gitlint/tests/test_utils.py b/gitlint-core/gitlint/tests/test_utils.py
index 4ec8bda..27036d3 100644
--- a/gitlint-core/gitlint/tests/test_utils.py
+++ b/gitlint-core/gitlint/tests/test_utils.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
from unittest.mock import patch
from gitlint import utils
@@ -7,13 +5,12 @@ from gitlint.tests.base import BaseTestCase
class UtilsTests(BaseTestCase):
-
def tearDown(self):
# Since we're messing around with `utils.PLATFORM_IS_WINDOWS` during these tests, we need to reset
# its value after we're done this doesn't influence other tests
utils.PLATFORM_IS_WINDOWS = utils.platform_is_windows()
- @patch('os.environ')
+ @patch("os.environ")
def test_use_sh_library(self, patched_env):
patched_env.get.return_value = "1"
self.assertEqual(utils.use_sh_library(), True)
@@ -25,15 +22,11 @@ class UtilsTests(BaseTestCase):
self.assertEqual(utils.use_sh_library(), False, invalid_val)
patched_env.get.assert_called_once_with("GITLINT_USE_SH_LIB", None)
- # Assert that when GITLINT_USE_SH_LIB is not set, we fallback to checking whether we're on Windows
- utils.PLATFORM_IS_WINDOWS = True
+ # Assert that when GITLINT_USE_SH_LIB is not set, we fallback to False (not using)
patched_env.get.return_value = None
self.assertEqual(utils.use_sh_library(), False)
- utils.PLATFORM_IS_WINDOWS = False
- self.assertEqual(utils.use_sh_library(), True)
-
- @patch('gitlint.utils.locale')
+ @patch("gitlint.utils.locale")
def test_default_encoding_non_windows(self, mocked_locale):
utils.PLATFORM_IS_WINDOWS = False
mocked_locale.getpreferredencoding.return_value = "foöbar"
@@ -43,7 +36,7 @@ class UtilsTests(BaseTestCase):
mocked_locale.getpreferredencoding.return_value = False
self.assertEqual(utils.getpreferredencoding(), "UTF-8")
- @patch('os.environ')
+ @patch("os.environ")
def test_default_encoding_windows(self, patched_env):
utils.PLATFORM_IS_WINDOWS = True
# Mock out os.environ