diff options
Diffstat (limited to 'gitlint/tests/cli')
-rw-r--r-- | gitlint/tests/cli/test_cli.py | 78 | ||||
-rw-r--r-- | gitlint/tests/cli/test_cli_hooks.py | 172 |
2 files changed, 214 insertions, 36 deletions
diff --git a/gitlint/tests/cli/test_cli.py b/gitlint/tests/cli/test_cli.py index 4d47f35..88bcfb7 100644 --- a/gitlint/tests/cli/test_cli.py +++ b/gitlint/tests/cli/test_cli.py @@ -1,12 +1,10 @@ # -*- coding: utf-8 -*- -import contextlib + import io import os import sys import platform -import shutil -import tempfile import arrow @@ -34,15 +32,6 @@ from gitlint import __version__ from gitlint.utils import DEFAULT_ENCODING -@contextlib.contextmanager -def tempdir(): - tmpdir = tempfile.mkdtemp() - try: - yield tmpdir - finally: - shutil.rmtree(tmpdir) - - class CLITests(BaseTestCase): USAGE_ERROR_CODE = 253 GIT_CONTEXT_ERROR_CODE = 254 @@ -64,7 +53,8 @@ class CLITests(BaseTestCase): 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())} + '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 """ @@ -118,7 +108,7 @@ class CLITests(BaseTestCase): 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("test_cli/test_lint_multiple_commits_1")) + 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) @@ -152,7 +142,7 @@ class CLITests(BaseTestCase): 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("test_cli/test_lint_multiple_commits_config_1")) + 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) @@ -205,7 +195,7 @@ class CLITests(BaseTestCase): """ 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("test_cli/test_input_stream_1")) + self.assertEqual(stderr.getvalue(), self.get_expected("cli/test_cli/test_input_stream_1")) self.assertEqual(result.exit_code, 3) self.assertEqual(result.output, "") @@ -215,11 +205,11 @@ class CLITests(BaseTestCase): 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("test_cli/test_input_stream_debug_1")) + 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('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") @@ -259,12 +249,12 @@ class CLITests(BaseTestCase): with patch('gitlint.display.stderr', new=StringIO()) as stderr: result = self.cli.invoke(cli.cli, ["--debug", "--staged"]) - self.assertEqual(stderr.getvalue(), self.get_expected("test_cli/test_lint_staged_stdin_1")) + 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('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")) @@ -280,19 +270,19 @@ class CLITests(BaseTestCase): u"commit-1/file-1\ncommit-1/file-2\n", # git diff-tree ] - with tempdir() as tmpdir: + 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(u"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("test_cli/test_lint_staged_msg_filename_1")) + 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('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) @@ -306,7 +296,7 @@ class CLITests(BaseTestCase): def test_msg_filename(self, _): expected_output = u"3: B6 Body message is missing\n" - with tempdir() as tmpdir: + 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(u"Commït title\n") @@ -375,7 +365,7 @@ class CLITests(BaseTestCase): u"commit-2-branch-1\ncommit-2-branch-2\n", # git branch --contains <sha> u"commit-2/file-1\ncommit-2/file-2\n", # git diff-tree u"test åuthor3\x00test-email3@föo.com\x002016-12-05 15:28:15 +0100\x00abc\n" - u"föo\nbar", + u"föobar\nbar", u"commit-3-branch-1\ncommit-3-branch-2\n", # git branch --contains <sha> u"commit-3/file-1\ncommit-3/file-2\n", # git diff-tree ] @@ -394,7 +384,7 @@ class CLITests(BaseTestCase): expected_kwargs = self.get_system_info_dict() expected_kwargs.update({'config_path': config_path}) - expected_logs = self.get_expected('test_cli/test_debug_1', expected_kwargs) + 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=u"Test tïtle\n") @@ -403,7 +393,7 @@ class CLITests(BaseTestCase): # 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, "--debug"]) + result = self.cli.invoke(cli.cli, ["--extra-path", extra_path]) expected_output = u"1: UC1 Commit violåtion 1: \"Contënt 1\"\n" + \ "3: B6 Body message is missing\n" self.assertEqual(stderr.getvalue(), expected_output) @@ -412,7 +402,7 @@ class CLITests(BaseTestCase): # 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, "--debug"]) + result = self.cli.invoke(cli.cli, ["--extra-path", extra_path]) expected_output = u"1: UC1 Commit violåtion 1: \"Contënt 1\"\n" + \ "3: B6 Body message is missing\n" self.assertEqual(stderr.getvalue(), expected_output) @@ -423,7 +413,7 @@ class CLITests(BaseTestCase): # 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('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, 3) @@ -469,13 +459,14 @@ class CLITests(BaseTestCase): @patch('gitlint.cli.get_stdin_data', return_value=False) def test_target(self, _): """ Test for the --target option """ - os.environ["LANGUAGE"] = "C" # Force language to english so we can check for error message - result = self.cli.invoke(cli.cli, ["--target", "/tmp"]) - # We expect gitlint to tell us that /tmp is not a git repo (this proves that it takes the target parameter - # into account). - expected_path = os.path.realpath("/tmp") - self.assertEqual(result.output, "%s is not a git repository.\n" % expected_path) - self.assertEqual(result.exit_code, self.GIT_CONTEXT_ERROR_CODE) + 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 """ @@ -539,3 +530,18 @@ class CLITests(BaseTestCase): self.assert_log_contains(u"DEBUG: gitlint.cli No commits in range \"master...HEAD\"") self.assertEqual(result.exit_code, 0) + + @patch('gitlint.cli.get_stdin_data', return_value=u"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 index 0564808..b5e7fc4 100644 --- a/gitlint/tests/cli/test_cli_hooks.py +++ b/gitlint/tests/cli/test_cli_hooks.py @@ -1,11 +1,19 @@ # -*- coding: utf-8 -*- +import io import os from click.testing import CliRunner try: # python 2.x + from StringIO import StringIO +except ImportError: + # python 3.x + from io import StringIO # pylint: disable=ungrouped-imports + +try: + # python 2.x from mock import patch except ImportError: # python 3.x @@ -16,6 +24,8 @@ from gitlint import cli from gitlint import hooks from gitlint import config +from gitlint.utils import DEFAULT_ENCODING + class CLIHookTests(BaseTestCase): USAGE_ERROR_CODE = 253 @@ -94,3 +104,165 @@ class CLIHookTests(BaseTestCase): expected_config = config.LintConfig() expected_config.target = os.path.realpath(os.getcwd()) uninstall_hook.assert_called_once_with(expected_config) + + def test_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, u"hür") + with io.open(msg_filename, 'w', encoding=DEFAULT_ENCODING) as f: + f.write(u"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_hook_edit(self, shell): + """ Test for run-hook subcommand, answering 'e(dit)' after commit-hook """ + + set_editors = [None, u"myeditor"] + expected_editors = [u"vim -n", u"myeditor"] + commit_messages = [u"WIP: höok edit 1", u"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, u"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(u"DEBUG: gitlint.cli run-hook: editing commit message") + self.assert_log_contains(u"DEBUG: gitlint.cli run-hook: {0} {1}".format(expected_editors[i], + msg_filename)) + + def test_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, u"hür") + with io.open(msg_filename, 'w', encoding=DEFAULT_ENCODING) as f: + f.write(u"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_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, u"hür") + with io.open(msg_filename, 'w', encoding=DEFAULT_ENCODING) as f: + f.write(u"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=u"WIP: Test hook stdin tïtle\n") + def test_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=u"Test tïtle\n\nTest bödy that is long enough") + def test_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=u"WIP: Test hook config tïtle\n") + def test_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_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", + u"test åuthor\x00test-email@föo.com\x002016-12-03 15:28:15 +0100\x00åbc\n" + u"WIP: commït-title\n\ncommït-body", + u"#", # git config --get core.commentchar + u"commit-1-branch-1\ncommit-1-branch-2\n", + u"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) |