From 72b8c35be4293bd21de123854491c658c53af100 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 Dec 2021 04:31:41 +0100 Subject: Adding upstream version 0.17.0. Signed-off-by: Daniel Baumann --- gitlint/tests/__init__.py | 0 gitlint/tests/base.py | 191 ------- gitlint/tests/cli/test_cli.py | 593 -------------------- gitlint/tests/cli/test_cli_hooks.py | 281 ---------- gitlint/tests/config/test_config.py | 287 ---------- gitlint/tests/config/test_config_builder.py | 264 --------- gitlint/tests/config/test_config_precedence.py | 98 ---- gitlint/tests/config/test_rule_collection.py | 64 --- gitlint/tests/contrib/__init__.py | 0 gitlint/tests/contrib/rules/__init__.py | 0 .../contrib/rules/test_conventional_commit.py | 75 --- gitlint/tests/contrib/rules/test_signedoff_by.py | 32 -- gitlint/tests/contrib/test_contrib_rules.py | 69 --- gitlint/tests/expected/cli/test_cli/test_contrib_1 | 2 - gitlint/tests/expected/cli/test_cli/test_debug_1 | 124 ----- .../expected/cli/test_cli/test_input_stream_1 | 3 - .../cli/test_cli/test_input_stream_debug_1 | 3 - .../cli/test_cli/test_input_stream_debug_2 | 83 --- .../tests/expected/cli/test_cli/test_lint_commit_1 | 2 - .../cli/test_cli/test_lint_multiple_commits_1 | 8 - .../test_cli/test_lint_multiple_commits_config_1 | 6 - .../cli/test_cli/test_lint_staged_msg_filename_1 | 2 - .../cli/test_cli/test_lint_staged_msg_filename_2 | 86 --- .../expected/cli/test_cli/test_lint_staged_stdin_1 | 3 - .../expected/cli/test_cli/test_lint_staged_stdin_2 | 88 --- .../tests/expected/cli/test_cli/test_named_rules_1 | 4 - .../tests/expected/cli/test_cli/test_named_rules_2 | 86 --- .../cli/test_cli_hooks/test_hook_config_1_stderr | 2 - .../cli/test_cli_hooks/test_hook_config_1_stdout | 5 - .../cli/test_cli_hooks/test_hook_edit_1_stderr | 6 - .../cli/test_cli_hooks/test_hook_edit_1_stdout | 14 - .../test_cli_hooks/test_hook_local_commit_1_stderr | 2 - .../test_cli_hooks/test_hook_local_commit_1_stdout | 4 - .../cli/test_cli_hooks/test_hook_no_1_stderr | 2 - .../cli/test_cli_hooks/test_hook_no_1_stdout | 8 - .../cli/test_cli_hooks/test_hook_no_tty_1_stderr | 2 - .../cli/test_cli_hooks/test_hook_no_tty_1_stdout | 5 - .../test_hook_stdin_no_violations_1_stdout | 2 - .../test_hook_stdin_violations_1_stderr | 2 - .../test_hook_stdin_violations_1_stdout | 5 - .../cli/test_cli_hooks/test_hook_yes_1_stderr | 2 - .../cli/test_cli_hooks/test_hook_yes_1_stdout | 4 - .../cli/test_cli_hooks/test_run_hook_negative_1 | 2 - .../cli/test_cli_hooks/test_run_hook_negative_2 | 2 - gitlint/tests/git/test_git.py | 108 ---- gitlint/tests/git/test_git_commit.py | 619 --------------------- gitlint/tests/git/test_git_context.py | 84 --- gitlint/tests/rules/__init__.py | 0 gitlint/tests/rules/test_body_rules.py | 236 -------- gitlint/tests/rules/test_configuration_rules.py | 140 ----- gitlint/tests/rules/test_meta_rules.py | 59 -- gitlint/tests/rules/test_rules.py | 23 - gitlint/tests/rules/test_title_rules.py | 186 ------- gitlint/tests/rules/test_user_rules.py | 256 --------- gitlint/tests/samples/commit_message/fixup | 1 - gitlint/tests/samples/commit_message/merge | 3 - gitlint/tests/samples/commit_message/no-violations | 6 - gitlint/tests/samples/commit_message/revert | 3 - gitlint/tests/samples/commit_message/sample1 | 14 - gitlint/tests/samples/commit_message/sample2 | 1 - gitlint/tests/samples/commit_message/sample3 | 6 - gitlint/tests/samples/commit_message/sample4 | 7 - gitlint/tests/samples/commit_message/sample5 | 7 - gitlint/tests/samples/commit_message/squash | 3 - gitlint/tests/samples/config/gitlintconfig | 15 - gitlint/tests/samples/config/invalid-option-value | 11 - gitlint/tests/samples/config/named-rules | 8 - gitlint/tests/samples/config/no-sections | 1 - .../samples/config/nonexisting-general-option | 13 - gitlint/tests/samples/config/nonexisting-option | 11 - gitlint/tests/samples/config/nonexisting-rule | 11 - gitlint/tests/samples/user_rules/bogus-file.txt | 2 - .../user_rules/import_exception/invalid_python.py | 3 - .../user_rules/incorrect_linerule/my_line_rule.py | 10 - .../tests/samples/user_rules/my_commit_rules.foo | 16 - .../tests/samples/user_rules/my_commit_rules.py | 26 - .../samples/user_rules/parent_package/__init__.py | 13 - .../user_rules/parent_package/my_commit_rules.py | 12 - gitlint/tests/test_cache.py | 57 -- gitlint/tests/test_display.py | 63 --- gitlint/tests/test_hooks.py | 131 ----- gitlint/tests/test_lint.py | 277 --------- gitlint/tests/test_options.py | 224 -------- gitlint/tests/test_utils.py | 77 --- 84 files changed, 5266 deletions(-) delete mode 100644 gitlint/tests/__init__.py delete mode 100644 gitlint/tests/base.py delete mode 100644 gitlint/tests/cli/test_cli.py delete mode 100644 gitlint/tests/cli/test_cli_hooks.py delete mode 100644 gitlint/tests/config/test_config.py delete mode 100644 gitlint/tests/config/test_config_builder.py delete mode 100644 gitlint/tests/config/test_config_precedence.py delete mode 100644 gitlint/tests/config/test_rule_collection.py delete mode 100644 gitlint/tests/contrib/__init__.py delete mode 100644 gitlint/tests/contrib/rules/__init__.py delete mode 100644 gitlint/tests/contrib/rules/test_conventional_commit.py delete mode 100644 gitlint/tests/contrib/rules/test_signedoff_by.py delete mode 100644 gitlint/tests/contrib/test_contrib_rules.py delete mode 100644 gitlint/tests/expected/cli/test_cli/test_contrib_1 delete mode 100644 gitlint/tests/expected/cli/test_cli/test_debug_1 delete mode 100644 gitlint/tests/expected/cli/test_cli/test_input_stream_1 delete mode 100644 gitlint/tests/expected/cli/test_cli/test_input_stream_debug_1 delete mode 100644 gitlint/tests/expected/cli/test_cli/test_input_stream_debug_2 delete mode 100644 gitlint/tests/expected/cli/test_cli/test_lint_commit_1 delete mode 100644 gitlint/tests/expected/cli/test_cli/test_lint_multiple_commits_1 delete mode 100644 gitlint/tests/expected/cli/test_cli/test_lint_multiple_commits_config_1 delete mode 100644 gitlint/tests/expected/cli/test_cli/test_lint_staged_msg_filename_1 delete mode 100644 gitlint/tests/expected/cli/test_cli/test_lint_staged_msg_filename_2 delete mode 100644 gitlint/tests/expected/cli/test_cli/test_lint_staged_stdin_1 delete mode 100644 gitlint/tests/expected/cli/test_cli/test_lint_staged_stdin_2 delete mode 100644 gitlint/tests/expected/cli/test_cli/test_named_rules_1 delete mode 100644 gitlint/tests/expected/cli/test_cli/test_named_rules_2 delete mode 100644 gitlint/tests/expected/cli/test_cli_hooks/test_hook_config_1_stderr delete mode 100644 gitlint/tests/expected/cli/test_cli_hooks/test_hook_config_1_stdout delete mode 100644 gitlint/tests/expected/cli/test_cli_hooks/test_hook_edit_1_stderr delete mode 100644 gitlint/tests/expected/cli/test_cli_hooks/test_hook_edit_1_stdout delete mode 100644 gitlint/tests/expected/cli/test_cli_hooks/test_hook_local_commit_1_stderr delete mode 100644 gitlint/tests/expected/cli/test_cli_hooks/test_hook_local_commit_1_stdout delete mode 100644 gitlint/tests/expected/cli/test_cli_hooks/test_hook_no_1_stderr delete mode 100644 gitlint/tests/expected/cli/test_cli_hooks/test_hook_no_1_stdout delete mode 100644 gitlint/tests/expected/cli/test_cli_hooks/test_hook_no_tty_1_stderr delete mode 100644 gitlint/tests/expected/cli/test_cli_hooks/test_hook_no_tty_1_stdout delete mode 100644 gitlint/tests/expected/cli/test_cli_hooks/test_hook_stdin_no_violations_1_stdout delete mode 100644 gitlint/tests/expected/cli/test_cli_hooks/test_hook_stdin_violations_1_stderr delete mode 100644 gitlint/tests/expected/cli/test_cli_hooks/test_hook_stdin_violations_1_stdout delete mode 100644 gitlint/tests/expected/cli/test_cli_hooks/test_hook_yes_1_stderr delete mode 100644 gitlint/tests/expected/cli/test_cli_hooks/test_hook_yes_1_stdout delete mode 100644 gitlint/tests/expected/cli/test_cli_hooks/test_run_hook_negative_1 delete mode 100644 gitlint/tests/expected/cli/test_cli_hooks/test_run_hook_negative_2 delete mode 100644 gitlint/tests/git/test_git.py delete mode 100644 gitlint/tests/git/test_git_commit.py delete mode 100644 gitlint/tests/git/test_git_context.py delete mode 100644 gitlint/tests/rules/__init__.py delete mode 100644 gitlint/tests/rules/test_body_rules.py delete mode 100644 gitlint/tests/rules/test_configuration_rules.py delete mode 100644 gitlint/tests/rules/test_meta_rules.py delete mode 100644 gitlint/tests/rules/test_rules.py delete mode 100644 gitlint/tests/rules/test_title_rules.py delete mode 100644 gitlint/tests/rules/test_user_rules.py delete mode 100644 gitlint/tests/samples/commit_message/fixup delete mode 100644 gitlint/tests/samples/commit_message/merge delete mode 100644 gitlint/tests/samples/commit_message/no-violations delete mode 100644 gitlint/tests/samples/commit_message/revert delete mode 100644 gitlint/tests/samples/commit_message/sample1 delete mode 100644 gitlint/tests/samples/commit_message/sample2 delete mode 100644 gitlint/tests/samples/commit_message/sample3 delete mode 100644 gitlint/tests/samples/commit_message/sample4 delete mode 100644 gitlint/tests/samples/commit_message/sample5 delete mode 100644 gitlint/tests/samples/commit_message/squash delete mode 100644 gitlint/tests/samples/config/gitlintconfig delete mode 100644 gitlint/tests/samples/config/invalid-option-value delete mode 100644 gitlint/tests/samples/config/named-rules delete mode 100644 gitlint/tests/samples/config/no-sections delete mode 100644 gitlint/tests/samples/config/nonexisting-general-option delete mode 100644 gitlint/tests/samples/config/nonexisting-option delete mode 100644 gitlint/tests/samples/config/nonexisting-rule delete mode 100644 gitlint/tests/samples/user_rules/bogus-file.txt delete mode 100644 gitlint/tests/samples/user_rules/import_exception/invalid_python.py delete mode 100644 gitlint/tests/samples/user_rules/incorrect_linerule/my_line_rule.py delete mode 100644 gitlint/tests/samples/user_rules/my_commit_rules.foo delete mode 100644 gitlint/tests/samples/user_rules/my_commit_rules.py delete mode 100644 gitlint/tests/samples/user_rules/parent_package/__init__.py delete mode 100644 gitlint/tests/samples/user_rules/parent_package/my_commit_rules.py delete mode 100644 gitlint/tests/test_cache.py delete mode 100644 gitlint/tests/test_display.py delete mode 100644 gitlint/tests/test_hooks.py delete mode 100644 gitlint/tests/test_lint.py delete mode 100644 gitlint/tests/test_options.py delete mode 100644 gitlint/tests/test_utils.py (limited to 'gitlint/tests') diff --git a/gitlint/tests/__init__.py b/gitlint/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/gitlint/tests/base.py b/gitlint/tests/base.py deleted file mode 100644 index 017122b..0000000 --- a/gitlint/tests/base.py +++ /dev/null @@ -1,191 +0,0 @@ -# -*- coding: utf-8 -*- - -import contextlib -import copy -import io -import logging -import os -import re -import shutil -import tempfile - -import unittest - -from unittest.mock import patch - -from gitlint.git import GitContext -from gitlint.utils import LOG_FORMAT, DEFAULT_ENCODING - - -class BaseTestCase(unittest.TestCase): - """ 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 - - SAMPLES_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "samples") - EXPECTED_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "expected") - GITLINT_USE_SH_LIB = os.environ.get("GITLINT_USE_SH_LIB", "[NOT SET]") - - 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] - - # Make sure we don't propagate anything to child loggers, we need to do this explicitely 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 - - @staticmethod - @contextlib.contextmanager - def tempdir(): - tmpdir = tempfile.mkdtemp() - try: - yield tmpdir - finally: - shutil.rmtree(tmpdir) - - @staticmethod - def get_sample_path(filename=""): - # Don't join up empty files names because this will add a trailing slash - if filename == "": - return BaseTestCase.SAMPLES_DIR - - return os.path.join(BaseTestCase.SAMPLES_DIR, filename) - - @staticmethod - def get_sample(filename=""): - """ 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: - sample = content.read() - return sample - - @staticmethod - def patch_input(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. """ - expected_path = os.path.join(BaseTestCase.EXPECTED_DIR, filename) - with io.open(expected_path, encoding=DEFAULT_ENCODING) as content: - expected = content.read() - - if variable_dict: - expected = expected.format(**variable_dict) - return expected - - @staticmethod - def get_user_rules_path(): - 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 - 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 - 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""" - gitcontext = BaseTestCase.gitcontext(commit_msg_str, changed_files) - commit = gitcontext.commits[-1] - for attr, value in kwargs.items(): - setattr(commit, attr, value) - 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. """ - 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 """ - 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. - """ - return super().assertRaisesRegex(expected_exception, re.escape(expected_regex), *args, **kwargs) - - def clearlog(self): - """ 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 """ - try: - yield - except expected_exception as exc: - exception_msg = str(exc) - if exception_msg != expected_msg: - error = f"Right exception, wrong message:\n got: {exception_msg}\n expected: {expected_msg}" - raise self.fail(error) - # else: everything is fine, just return - return - except Exception as exc: - raise self.fail(f"Expected '{expected_exception.__name__}' got '{exc.__class__.__name__}'") - - # No exception raised while we expected one - 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__`. - """ - if not ctor_kwargs: - ctor_kwargs = {} - - attr_kwargs = {} - for attr in attr_list: - attr_kwargs[attr] = getattr(obj, attr) - - # For every attr, clone the object and assert the clone and the original object are equal - # Then, change the current attr and assert objects are unequal - for attr in attr_list: - attr_kwargs_copy = copy.deepcopy(attr_kwargs) - attr_kwargs_copy.update(ctor_kwargs) - clone = obj.__class__(**attr_kwargs_copy) - self.assertEqual(obj, clone) - - # Change attribute and assert objects are different (via both attribute set and ctor) - setattr(clone, attr, "föo") - self.assertNotEqual(obj, clone) - attr_kwargs_copy[attr] = "föo" - - self.assertNotEqual(obj, obj.__class__(**attr_kwargs_copy)) - - -class LogCapture(logging.Handler): - """ Mock logging handler used to capture any log messages during tests.""" - - def __init__(self, *args, **kwargs): - logging.Handler.__init__(self, *args, **kwargs) - self.messages = [] - - def emit(self, record): - self.messages.append(self.format(record)) - - def clear(self): - self.messages = [] diff --git a/gitlint/tests/cli/test_cli.py b/gitlint/tests/cli/test_cli.py deleted file mode 100644 index 59ec7af..0000000 --- a/gitlint/tests/cli/test_cli.py +++ /dev/null @@ -1,593 +0,0 @@ -# -*- coding: utf-8 -*- - - -import io -import os -import sys -import platform - -import arrow - -from io import StringIO - -from click.testing import CliRunner - -from unittest.mock import patch - -from gitlint.shell import CommandNotFound - -from gitlint.tests.base import BaseTestCase -from gitlint import cli -from gitlint import __version__ -from gitlint.utils import DEFAULT_ENCODING - - -class CLITests(BaseTestCase): - USAGE_ERROR_CODE = 253 - GIT_CONTEXT_ERROR_CODE = 254 - CONFIG_ERROR_CODE = 255 - GITLINT_SUCCESS_CODE = 0 - - def setUp(self): - super(CLITests, self).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') - 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() - - @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} - - def test_version(self): - """ 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') - def test_lint(self, sh, _): - """ 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", - "#", # git config --get core.commentchar - "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: - 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(result.exit_code, 1) - - @patch('gitlint.cli.get_stdin_data', return_value=False) - @patch('gitlint.git.sh') - def test_lint_multiple_commits(self, sh, _): - """ Test for --commits option """ - - sh.git.side_effect = [ - "6f29bf81a8322a04071bb794666e48c443a90360\n" + # git rev-list - "25053ccec5e28e1bb8f7551fdbb5ab213ada2401\n" + - "4da2656b0dadc76c7ee3fd0243a96cb64007f125\n", - # git log --pretty - "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 - "commit-1-branch-1\ncommit-1-branch-2\n", # git branch --contains - "commit-1/file-1\ncommit-1/file-2\n", # git diff-tree - # git log --pretty - "test åuthor2\x00test-email3@föo.com\x002016-12-04 15:28:15 +0100\x00åbc\n" - "commït-title2\n\ncommït-body2", - "commit-2-branch-1\ncommit-2-branch-2\n", # git branch --contains - "commit-2/file-1\ncommit-2/file-2\n", # git diff-tree - # git log --pretty - "test åuthor3\x00test-email3@föo.com\x002016-12-05 15:28:15 +0100\x00åbc\n" - "commït-title3\n\ncommït-body3", - "commit-3-branch-1\ncommit-3-branch-2\n", # git branch --contains - "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"]) - 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') - def test_lint_multiple_commits_config(self, sh, _): - """ Test for --commits option where some of the commits have gitlint config in the commit message """ - - # 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 - "25053ccec5e28e1bb8f7551fdbb5ab213ada2401\n" + - "4da2656b0dadc76c7ee3fd0243a96cb64007f125\n", - # git log --pretty - "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 - "commit-1-branch-1\ncommit-1-branch-2\n", # git branch --contains - "commit-1/file-1\ncommit-1/file-2\n", # git diff-tree - # git log --pretty - "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", - "commit-2-branch-1\ncommit-2-branch-2\n", # git branch --contains - "commit-2/file-1\ncommit-2/file-2\n", # git diff-tree - # git log --pretty - "test åuthor3\x00test-email3@föo.com\x002016-12-05 15:28:15 +0100\x00åbc\n" - "commït-title3.\n\ncommït-body3", - "commit-3-branch-1\ncommit-3-branch-2\n", # git branch --contains - "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"]) - # 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') - 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 - """ - - # Note that the second commit - sh.git.side_effect = [ - "6f29bf81a8322a04071bb794666e48c443a90360\n" + # git rev-list - "25053ccec5e28e1bb8f7551fdbb5ab213ada2401\n" + - "4da2656b0dadc76c7ee3fd0243a96cb64007f125\n", - # git log --pretty - "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 - "commit-1-branch-1\ncommit-1-branch-2\n", # git branch --contains - "commit-1/file-1\ncommit-1/file-2\n", # git diff-tree - # git log --pretty - "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", - "commit-2-branch-1\ncommit-2-branch-2\n", # git branch --contains - "commit-2/file-1\ncommit-2/file-2\n", # git diff-tree - # git log --pretty - "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", - "commit-3-branch-1\ncommit-3-branch-2\n", # git branch --contains - "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"]) - # 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') - self.assertEqual(stderr.getvalue(), expected) - self.assertEqual(result.exit_code, 2) - - @patch('gitlint.cli.get_stdin_data', return_value=False) - @patch('gitlint.git.sh') - def test_lint_commit(self, sh, _): - """ Test for --commit option """ - - sh.git.side_effect = [ - "6f29bf81a8322a04071bb794666e48c443a90360\n", # git log -1 --pretty=%H - # git log --pretty - "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 - "commit-1-branch-1\ncommit-1-branch-2\n", # git branch --contains - "commit-1/file-1\ncommit-1/file-2\n", # git diff-tree - ] - - 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') - def test_lint_commit_negative(self, sh, _): - """ 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"]) - expected_output = "Error: --commit and --commits are mutually exclusive, use one or the other.\n" - 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') - def test_input_stream(self, _): - """ 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') - 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: - 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) - self.assert_logged(expected_logs) - - @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""" - 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 - "commit-1-branch-1\ncommit-1-branch-2\n", # git branch --contains - "file1.txt\npåth/to/file2.txt\n" # git diff-tree - ] - - 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(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') - def test_lint_staged_stdin(self, sh, _, __): - """ 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 - ] - - 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) - self.assert_logged(expected_logs) - - @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""" - - 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 - ] - - with self.tempdir() as tmpdir: - msg_filename = os.path.join(tmpdir, "msg") - with io.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: - 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) - self.assert_logged(expected_logs) - - @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') - def test_fail_without_commits(self, sh, _): - """ Test for --debug option """ - - 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: - # 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\"") - - # 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\"") - - @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: - f.write("Commït title\n") - - 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") - def test_silent_mode(self, _): - """ 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") - def test_verbosity(self, _): - """ 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: - 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" - - 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') - 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') - def test_debug(self, sh, _): - """ Test for --debug option """ - - sh.git.side_effect = [ - "6f29bf81a8322a04071bb794666e48c443a90360\n" # git rev-list - "25053ccec5e28e1bb8f7551fdbb5ab213ada2401\n" - "4da2656b0dadc76c7ee3fd0243a96cb64007f125\n", - # git log --pretty - "test åuthor1\x00test-email1@föo.com\x002016-12-03 15:28:15 +0100\x00abc\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 - "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" - "commït-title2.\n\ncommït-body2", - "commit-2-branch-1\ncommit-2-branch-2\n", # git branch --contains - "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" - "föobar\nbar", - "commit-3-branch-1\ncommit-3-branch-2\n", # git branch --contains - "commit-3/file-1\ncommit-3/file-2\n", # git diff-tree - ] - - 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"]) - - 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) - self.assert_logged(expected_logs) - - @patch('gitlint.cli.get_stdin_data', return_value="Test tïtle\n") - def test_extra_path(self, _): - """ Test for --extra-path flag """ - # Test extra-path pointing to a directory - 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" - 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: - 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" - 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") - def test_contrib(self, _): - # Test enabled contrib rules - 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') - 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_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") - def test_config_file(self, _): - """ 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) - - def test_config_file_negative(self): - """ 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]) - 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, ["--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, ["--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 """ - 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 - result = self.cli.invoke(cli.cli, ["--target", tmpdir_path]) - # We expect gitlint to tell us that /tmp is not a git repo (this proves that it takes the target parameter - # into account). - self.assertEqual(result.output, "%s is not a git repository.\n" % tmpdir_path) - self.assertEqual(result.exit_code, self.GIT_CONTEXT_ERROR_CODE) - - def test_target_negative(self): - """ 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) - expected_msg = "Error: Invalid value for '--target': Directory '/föo/bar' does not exist." - self.assertEqual(result.output.split("\n")[3], expected_msg) - - # try setting a file as target - target_path = self.get_sample_path(os.path.join("config", "gitlintconfig")) - result = self.cli.invoke(cli.cli, ["--target", target_path]) - self.assertEqual(result.exit_code, self.USAGE_ERROR_CODE) - 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') - def test_generate_config(self, generate_config): - """ 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" - 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 """ - # 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" - 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" - self.assertEqual(result.output, expected_msg) - - @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 """ - 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') - def test_no_commits_in_range(self, sh, _): - """ 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"]) - - self.assert_log_contains("DEBUG: gitlint.cli No commits in range \"master...HEAD\"") - self.assertEqual(result.exit_code, self.GITLINT_SUCCESS_CODE) - - @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: - 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, "") - self.assertEqual(stderr.getvalue(), self.get_expected("cli/test_cli/test_named_rules_1")) - self.assertEqual(result.exit_code, 4) - - # 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) - self.assert_logged(expected_logs) diff --git a/gitlint/tests/cli/test_cli_hooks.py b/gitlint/tests/cli/test_cli_hooks.py deleted file mode 100644 index 825345f..0000000 --- a/gitlint/tests/cli/test_cli_hooks.py +++ /dev/null @@ -1,281 +0,0 @@ -# -*- coding: utf-8 -*- - -import io -from io import StringIO -import os - -from click.testing import CliRunner - -from unittest.mock import patch - -from gitlint.tests.base import BaseTestCase -from gitlint import cli -from gitlint import hooks -from gitlint import config -from gitlint.shell import ErrorReturnCode - -from gitlint.utils import DEFAULT_ENCODING - - -class CLIHookTests(BaseTestCase): - USAGE_ERROR_CODE = 253 - GIT_CONTEXT_ERROR_CODE = 254 - CONFIG_ERROR_CODE = 255 - - def setUp(self): - super(CLIHookTests, self).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') - 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")) - def test_install_hook(self, _, install_hook): - """ 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" - self.assertEqual(result.output, expected) - self.assertEqual(result.exit_code, 0) - expected_config = config.LintConfig() - 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")) - def test_install_hook_target(self, _, install_hook): - """ 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) - expected = "Successfully installed gitlint commit-msg hook in %s\n" % expected_path - self.assertEqual(result.exit_code, 0) - self.assertEqual(result.output, expected) - - expected_config = config.LintConfig() - 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")) - def test_install_hook_negative(self, install_hook): - """ 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") - expected_config = config.LintConfig() - 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")) - def test_uninstall_hook(self, _, uninstall_hook): - """ 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" - self.assertEqual(result.exit_code, 0) - self.assertEqual(result.output, expected) - expected_config = config.LintConfig() - 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")) - def test_uninstall_hook_negative(self, uninstall_hook): - """ 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") - expected_config = config.LintConfig() - expected_config.target = os.path.realpath(os.getcwd()) - 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. - """ - - # No need to patch git as we're passing a msg-filename to run-hook, so no git calls are made. - # Note that this is the case when passing --staged as well, but that's tested as part of the integration tests - # (=end-to-end scenario). - - # Ideally we'd be able to assert that run-hook internally calls the lint cli command, but couldn't make - # that work. Have tried many different variatons of mocking and patching without avail. For now, we just - # check the output which indirectly proves the same thing. - - with self.tempdir() as tmpdir: - msg_filename = os.path.join(tmpdir, "hür") - with io.open(msg_filename, 'w', encoding=DEFAULT_ENCODING) as f: - f.write("WIP: tïtle\n") - - 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(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') - def test_run_hook_edit(self, shell): - """ Test for run-hook subcommand, answering 'e(dit)' after commit-hook """ - - set_editors = [None, "myeditor"] - expected_editors = ["vim -n", "myeditor"] - commit_messages = ["WIP: höok edit 1", "WIP: höok edit 2"] - - for i in range(0, len(set_editors)): - if set_editors[i]: - os.environ['EDITOR'] = set_editors[i] - - 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: - f.write(commit_messages[i] + "\n") - - 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(stderr.getvalue(), expected) - - # exit code = number of violations - self.assertEqual(result.exit_code, 2) - - shell.assert_called_with(expected_editors[i] + " " + msg_filename) - self.assert_log_contains("DEBUG: gitlint.cli run-hook: editing commit message") - 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 """ - - 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: - f.write("WIP: höok no\n") - - 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(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) - # This will cause git to abort the commit - self.assertEqual(result.exit_code, 2) - 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']): - with self.tempdir() as tmpdir: - msg_filename = os.path.join(tmpdir, "hür") - with io.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: - 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(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 - # This will cause git to keep the commit - 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') - def test_run_hook_negative(self, sh, _): - """ 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()}) - 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.exit_code, self.USAGE_ERROR_CODE) - - # CONFIG_ERROR_CODE: incorrect config. Note that this is handled before the hook even runs - result = self.cli.invoke(cli.cli, ["-c", "föo.bár=1", "run-hook"]) - 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") - 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 - """ - - 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') - self.assertEqual(stderr.getvalue(), expected_stderr) - 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") - 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 - """ - - 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') - 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") - 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 - """ - - 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')) - # 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') - 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' - """ - 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", - "#", # git config --get core.commentchar - "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: - result = self.cli.invoke(cli.cli, ["run-hook"]) - 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')) - # 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/tests/config/test_config.py b/gitlint/tests/config/test_config.py deleted file mode 100644 index c3fd78a..0000000 --- a/gitlint/tests/config/test_config.py +++ /dev/null @@ -1,287 +0,0 @@ -# -*- coding: utf-8 -*- - -from unittest.mock import patch - -from gitlint import rules -from gitlint.config import LintConfig, LintConfigError, LintConfigGenerator, GITLINT_CONFIG_TEMPLATE_SRC_PATH -from gitlint import options -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) - - # 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) - - def test_set_rule_option_negative(self): - config = LintConfig() - - # 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) - - # 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) - - # 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')." - with self.assertRaisesMessage(LintConfigError, expected_error_msg): - config.set_rule_option('title-max-length', 'line-length', "föo") - - def test_set_general_option(self): - config = LintConfig() - - # Check that default general options are correct - self.assertTrue(config.ignore_merge_commits) - self.assertTrue(config.ignore_fixup_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.debug) - self.assertEqual(config.verbosity, 3) - active_rule_classes = tuple(type(rule) for rule in config.rules) - self.assertTupleEqual(active_rule_classes, config.default_rule_classes) - - # ignore - set by string - config.set_general_option("ignore", "title-trailing-whitespace, B2") - self.assertEqual(config.ignore, ["title-trailing-whitespace", "B2"]) - - # ignore - set by list - config.set_general_option("ignore", ["T1", "B3"]) - self.assertEqual(config.ignore, ["T1", "B3"]) - - # verbosity - config.set_general_option("verbosity", 1) - self.assertEqual(config.verbosity, 1) - - # ignore_merge_commit - config.set_general_option("ignore-merge-commits", "false") - self.assertFalse(config.ignore_merge_commits) - - # ignore_fixup_commit - config.set_general_option("ignore-fixup-commits", "false") - self.assertFalse(config.ignore_fixup_commits) - - # ignore_squash_commit - config.set_general_option("ignore-squash-commits", "false") - self.assertFalse(config.ignore_squash_commits) - - # ignore_revert_commit - config.set_general_option("ignore-revert-commits", "false") - self.assertFalse(config.ignore_revert_commits) - - # debug - config.set_general_option("debug", "true") - self.assertTrue(config.debug) - - # ignore-stdin - config.set_general_option("ignore-stdin", "true") - self.assertTrue(config.debug) - - # staged - config.set_general_option("staged", "true") - self.assertTrue(config.staged) - - # fail-without-commits - config.set_general_option("fail-without-commits", "true") - self.assertTrue(config.fail_without_commits) - - # target - config.set_general_option("target", self.SAMPLES_DIR) - self.assertEqual(config.target, self.SAMPLES_DIR) - - # extra_path has its own test: test_extra_path and test_extra_path_negative - # contrib has its own tests: test_contrib and test_contrib_negative - - def test_contrib(self): - config = LintConfig() - contrib_rules = ["contrib-title-conventional-commits", "CC1"] - config.set_general_option("contrib", ",".join(contrib_rules)) - self.assertEqual(config.contrib, contrib_rules) - - # Check contrib-title-conventional-commits contrib rule - actual_rule = config.rules.find_rule("contrib-title-conventional-commits") - self.assertTrue(actual_rule.is_contrib) - - self.assertEqual(str(type(actual_rule)), "") - self.assertEqual(actual_rule.id, 'CT1') - self.assertEqual(actual_rule.name, u'contrib-title-conventional-commits') - self.assertEqual(actual_rule.target, rules.CommitMessageTitle) - - expected_rule_option = options.ListOption( - "types", - ["fix", "feat", "chore", "docs", "style", "refactor", "perf", "test", "revert", "ci", "build"], - "Comma separated list of allowed commit types.", - ) - - self.assertListEqual(actual_rule.options_spec, [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)), "") - self.assertEqual(actual_rule.id, 'CC1') - self.assertEqual(actual_rule.name, u'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") - self.assertEqual(actual_rule, config.rules.find_rule("contrib-body-requires-signed-off-by")) - self.assertIsNone(config.rules.find_rule("contrib-title-conventional-commits")) - - # empty value - config.set_general_option("contrib", "") - self.assertListEqual(config.contrib, []) - - def test_contrib_negative(self): - config = LintConfig() - # non-existent contrib rule - with self.assertRaisesMessage(LintConfigError, "No contrib rule with id or name 'föo' found."): - config.contrib = "contrib-title-conventional-commits,föo" - - # 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 self.assertRaisesMessage(LintConfigError, str(side_effect)): - config.contrib = "contrib-title-conventional-commits" - - def test_extra_path(self): - config = LintConfig() - - 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') - self.assertTrue(actual_rule.is_user_defined) - self.assertEqual(str(type(actual_rule)), "") - self.assertEqual(actual_rule.id, 'UC1') - self.assertEqual(actual_rule.name, u'my-üser-commit-rule') - self.assertEqual(actual_rule.target, None) - 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}) - - # reset value (this is a different code path) - config.set_general_option("extra-path", self.SAMPLES_DIR) - self.assertEqual(config.extra_path, self.SAMPLES_DIR) - self.assertIsNone(config.rules.find_rule("UC1")) - - def test_extra_path_negative(self): - config = LintConfig() - regex = "Option extra-path must be either an existing directory or file (current value: 'föo/bar')" - # incorrect extra_path - with self.assertRaisesMessage(LintConfigError, regex): - 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"): - config.extra_path = self.get_sample_path("user_rules/incorrect_linerule") - - def test_set_general_option_negative(self): - config = LintConfig() - - # Note that we shouldn't test whether we can set unicode because python just doesn't allow unicode attributes - with self.assertRaisesMessage(LintConfigError, "'foo' is not a valid gitlint option"): - config.set_general_option("foo", "bår") - - # try setting _config_path, this is a real attribute of LintConfig, but the code should prevent it from - # being set - with self.assertRaisesMessage(LintConfigError, "'_config_path' is not a valid gitlint option"): - config.set_general_option("_config_path", "bår") - - # invalid verbosity - incorrect_values = [-1, "föo"] - for value in incorrect_values: - expected_msg = f"Option 'verbosity' must be a positive integer (current value: '{value}')" - with self.assertRaisesMessage(LintConfigError, expected_msg): - config.verbosity = value - - incorrect_values = [4] - for value in incorrect_values: - with self.assertRaisesMessage(LintConfigError, "Option 'verbosity' must be set between 0 and 3"): - config.verbosity = value - - # invalid ignore_xxx_commits - ignore_attributes = ["ignore_merge_commits", "ignore_fixup_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'"): - 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']: - option_name = attribute.replace("_", "-") - 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')"): - config.target = "föo/bar" - - def test_ignore_independent_from_rules(self): - # Test that the lintconfig rules are not modified when setting config.ignore - # This was different in the past, this test is mostly here to catch regressions - config = LintConfig() - original_rules = config.rules - config.ignore = ["T1", "T2"] - self.assertEqual(config.ignore, ["T1", "T2"]) - self.assertSequenceEqual(config.rules, original_rules) - - def test_config_equality(self): - self.assertEqual(LintConfig(), LintConfig()) - 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"])] - for attr, val in attrs: - config = LintConfig() - setattr(config, attr, val) - self.assertNotEqual(LintConfig(), config) - - # Other attributes don't matter - config1 = LintConfig() - config2 = LintConfig() - config1.foo = "bår" - self.assertEqual(config1, config2) - config2.foo = "dūr" - self.assertEqual(config1, config2) - - -class LintConfigGeneratorTests(BaseTestCase): - @staticmethod - @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/tests/config/test_config_builder.py b/gitlint/tests/config/test_config_builder.py deleted file mode 100644 index e0d7f9b..0000000 --- a/gitlint/tests/config/test_config_builder.py +++ /dev/null @@ -1,264 +0,0 @@ -# -*- coding: utf-8 -*- -import copy - -from gitlint.tests.base import BaseTestCase - -from gitlint.config import LintConfig, LintConfigBuilder, LintConfigError - -from gitlint import rules - - -class LintConfigBuilderTests(BaseTestCase): - def test_set_option(self): - config_builder = LintConfigBuilder() - 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.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}} - 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.verbosity, 2) - - def test_set_from_commit_ignore_all(self): - config = LintConfig() - original_rules = config.rules - original_rule_ids = [rule.id for rule in original_rules] - - config_builder = LintConfigBuilder() - - # nothing gitlint - config_builder.set_config_from_commit(self.gitcommit("tëst\ngitlint\nfoo")) - config = config_builder.build() - self.assertSequenceEqual(config.rules, original_rules) - self.assertListEqual(config.ignore, []) - - # ignore all rules - config_builder.set_config_from_commit(self.gitcommit("tëst\ngitlint-ignore: all\nfoo")) - config = config_builder.build() - self.assertEqual(config.ignore, original_rule_ids) - - # ignore all rules, no space - config_builder.set_config_from_commit(self.gitcommit("tëst\ngitlint-ignore:all\nfoo")) - config = config_builder.build() - self.assertEqual(config.ignore, original_rule_ids) - - # ignore all rules, more spacing - config_builder.set_config_from_commit(self.gitcommit("tëst\ngitlint-ignore: \t all\nfoo")) - config = config_builder.build() - self.assertEqual(config.ignore, original_rule_ids) - - def test_set_from_commit_ignore_specific(self): - # ignore specific rules - config_builder = LintConfigBuilder() - config_builder.set_config_from_commit(self.gitcommit("tëst\ngitlint-ignore: T1, body-hard-tab")) - config = config_builder.build() - self.assertEqual(config.ignore, ["T1", "body-hard-tab"]) - - def test_set_from_config_file(self): - # regular config file load, no problems - config_builder = LintConfigBuilder() - config_builder.set_from_config_file(self.get_sample_path("config/gitlintconfig")) - config = config_builder.build() - - # Do some assertions on the config - self.assertEqual(config.verbosity, 1) - self.assertFalse(config.debug) - self.assertFalse(config.ignore_merge_commits) - 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) - - def test_set_from_config_file_negative(self): - config_builder = LintConfigBuilder() - - # bad config file load - foo_path = self.get_sample_path("föo") - expected_error_msg = f"Invalid file path: {foo_path}" - with self.assertRaisesMessage(LintConfigError, expected_error_msg): - config_builder.set_from_config_file(foo_path) - - # error during file parsing - path = self.get_sample_path("config/no-sections") - expected_error_msg = "File contains no section headers." - # We only match the start of the message here, since the exact message can vary depending on platform - with self.assertRaisesRegex(LintConfigError, expected_error_msg): - config_builder.set_from_config_file(path) - - # non-existing rule - path = self.get_sample_path("config/nonexisting-rule") - config_builder = LintConfigBuilder() - config_builder.set_from_config_file(path) - expected_error_msg = "No such rule 'föobar'" - with self.assertRaisesMessage(LintConfigError, expected_error_msg): - config_builder.build() - - # non-existing general option - path = self.get_sample_path("config/nonexisting-general-option") - config_builder = LintConfigBuilder() - config_builder.set_from_config_file(path) - expected_error_msg = "'foo' is not a valid gitlint option" - with self.assertRaisesMessage(LintConfigError, expected_error_msg): - config_builder.build() - - # non-existing option - path = self.get_sample_path("config/nonexisting-option") - config_builder = LintConfigBuilder() - config_builder.set_from_config_file(path) - expected_error_msg = "Rule 'title-max-length' has no option 'föobar'" - with self.assertRaisesMessage(LintConfigError, expected_error_msg): - config_builder.build() - - # invalid option value - 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')." - with self.assertRaisesMessage(LintConfigError, expected_error_msg): - config_builder.build() - - def test_set_config_from_string_list(self): - config = LintConfig() - - # 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 = 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.verbosity, 1) - - def test_set_config_from_string_list_negative(self): - config_builder = LintConfigBuilder() - - # assert error on incorrect rule - this happens at build time - config_builder.set_config_from_string_list(["föo.bar=1"]) - with self.assertRaisesMessage(LintConfigError, "No such rule 'föo'"): - config_builder.build() - - # no equal sign - expected_msg = "'föo.bar' is an invalid configuration option. Use '.