From b8d423e7d13686d6627571d6c4adf12661d82147 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 13 Oct 2021 07:34:54 +0200 Subject: Adding upstream version 0.16.0. Signed-off-by: Daniel Baumann --- qa/base.py | 40 +++++++++--------- .../test_commits/test_lint_staged_msg_filename_1 | 4 ++ qa/expected/test_commits/test_lint_staged_stdin_1 | 4 ++ qa/expected/test_config/test_config_from_env_1 | 4 ++ qa/expected/test_config/test_config_from_env_2 | 4 ++ .../test_config/test_config_from_file_debug_1 | 4 ++ qa/expected/test_contrib/test_contrib_rules_1 | 1 - .../test_contrib/test_contrib_rules_with_config_1 | 1 - qa/requirements.txt | 6 +-- qa/shell.py | 4 +- qa/test_commits.py | 47 ++++++++++++++++++++-- qa/test_config.py | 3 +- qa/test_contrib.py | 4 +- qa/test_hooks.py | 31 +++++++------- qa/test_stdin.py | 8 ++-- 15 files changed, 112 insertions(+), 53 deletions(-) (limited to 'qa') diff --git a/qa/base.py b/qa/base.py index acb921d..d3c8e81 100644 --- a/qa/base.py +++ b/qa/base.py @@ -29,25 +29,20 @@ class BaseTestCase(TestCase): GITLINT_USE_SH_LIB = os.environ.get("GITLINT_USE_SH_LIB", "[NOT SET]") GIT_CONTEXT_ERROR_CODE = 254 - - @classmethod - def setUpClass(cls): - """ Sets up the integration tests by creating a new temporary git repository """ - cls.tmp_git_repos = [] - cls.tmp_git_repo = cls.create_tmp_git_repo() - - @classmethod - def tearDownClass(cls): - """ Cleans up the temporary git repositories """ - for repo in cls.tmp_git_repos: - shutil.rmtree(repo) + GITLINT_USAGE_ERROR = 253 def setUp(self): + """ Sets up the integration tests by creating a new temporary git repository """ self.tmpfiles = [] + self.tmp_git_repos = [] + self.tmp_git_repo = self.create_tmp_git_repo() def tearDown(self): + # Clean up temporary files and repos for tmpfile in self.tmpfiles: os.remove(tmpfile) + for repo in self.tmp_git_repos: + shutil.rmtree(repo) def assertEqualStdout(self, output, expected): # pylint: disable=invalid-name self.assertIsInstance(output, RunningCommand) @@ -55,16 +50,15 @@ class BaseTestCase(TestCase): output = output.replace('\r', '') self.assertMultiLineEqual(output, expected) - @classmethod - def generate_temp_path(cls): + @staticmethod + def generate_temp_path(): timestamp = datetime.now().strftime("%Y%m%d-%H%M%S-%f") return os.path.realpath(f"/tmp/gitlint-test-{timestamp}") - @classmethod - def create_tmp_git_repo(cls): + def create_tmp_git_repo(self): """ Creates a temporary git repository and returns its directory path """ - tmp_git_repo = cls.generate_temp_path() - cls.tmp_git_repos.append(tmp_git_repo) + tmp_git_repo = self.generate_temp_path() + self.tmp_git_repos.append(tmp_git_repo) git("init", tmp_git_repo) # configuring name and email is required in every git repot @@ -86,6 +80,7 @@ class BaseTestCase(TestCase): def create_file(parent_dir): """ Creates a file inside a passed directory. Returns filename.""" test_filename = "test-fïle-" + str(uuid4()) + # pylint: disable=consider-using-with io.open(os.path.join(parent_dir, test_filename), 'a', encoding=DEFAULT_ENCODING).close() return test_filename @@ -158,11 +153,12 @@ class BaseTestCase(TestCase): specified by variable_dict. """ expected_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "expected") expected_path = os.path.join(expected_dir, filename) - expected = io.open(expected_path, encoding=DEFAULT_ENCODING).read() + with io.open(expected_path, encoding=DEFAULT_ENCODING) as file: + expected = file.read() - if variable_dict: - expected = expected.format(**variable_dict) - return expected + if variable_dict: + expected = expected.format(**variable_dict) + return expected @staticmethod def get_system_info_dict(): diff --git a/qa/expected/test_commits/test_lint_staged_msg_filename_1 b/qa/expected/test_commits/test_lint_staged_msg_filename_1 index eb2682f..901ea27 100644 --- a/qa/expected/test_commits/test_lint_staged_msg_filename_1 +++ b/qa/expected/test_commits/test_lint_staged_msg_filename_1 @@ -18,6 +18,7 @@ ignore-squash-commits: True ignore-revert-commits: True ignore-stdin: False staged: True +fail-without-commits: False verbosity: 3 debug: True target: {target} @@ -30,6 +31,9 @@ target: {target} regex=None I3: ignore-body-lines regex=None + I4: ignore-by-author-name + ignore=all + regex=None T1: title-max-length line-length=72 T2: title-trailing-whitespace diff --git a/qa/expected/test_commits/test_lint_staged_stdin_1 b/qa/expected/test_commits/test_lint_staged_stdin_1 index 76b5048..e4677c3 100644 --- a/qa/expected/test_commits/test_lint_staged_stdin_1 +++ b/qa/expected/test_commits/test_lint_staged_stdin_1 @@ -18,6 +18,7 @@ ignore-squash-commits: True ignore-revert-commits: True ignore-stdin: False staged: True +fail-without-commits: False verbosity: 3 debug: True target: {target} @@ -30,6 +31,9 @@ target: {target} regex=None I3: ignore-body-lines regex=None + I4: ignore-by-author-name + ignore=all + regex=None T1: title-max-length line-length=72 T2: title-trailing-whitespace diff --git a/qa/expected/test_config/test_config_from_env_1 b/qa/expected/test_config/test_config_from_env_1 index f3947bb..60f6690 100644 --- a/qa/expected/test_config/test_config_from_env_1 +++ b/qa/expected/test_config/test_config_from_env_1 @@ -18,6 +18,7 @@ ignore-squash-commits: True ignore-revert-commits: True ignore-stdin: True staged: False +fail-without-commits: True verbosity: 2 debug: True target: {target} @@ -30,6 +31,9 @@ target: {target} regex=None I3: ignore-body-lines regex=None + I4: ignore-by-author-name + ignore=all + regex=None T1: title-max-length line-length=72 T2: title-trailing-whitespace diff --git a/qa/expected/test_config/test_config_from_env_2 b/qa/expected/test_config/test_config_from_env_2 index 8d36672..e9ebd67 100644 --- a/qa/expected/test_config/test_config_from_env_2 +++ b/qa/expected/test_config/test_config_from_env_2 @@ -18,6 +18,7 @@ ignore-squash-commits: True ignore-revert-commits: True ignore-stdin: False staged: True +fail-without-commits: False verbosity: 0 debug: True target: {target} @@ -30,6 +31,9 @@ target: {target} regex=None I3: ignore-body-lines regex=None + I4: ignore-by-author-name + ignore=all + regex=None T1: title-max-length line-length=72 T2: title-trailing-whitespace diff --git a/qa/expected/test_config/test_config_from_file_debug_1 b/qa/expected/test_config/test_config_from_file_debug_1 index 540c3a0..6ad5ec4 100644 --- a/qa/expected/test_config/test_config_from_file_debug_1 +++ b/qa/expected/test_config/test_config_from_file_debug_1 @@ -18,6 +18,7 @@ ignore-squash-commits: True ignore-revert-commits: True ignore-stdin: False staged: False +fail-without-commits: False verbosity: 2 debug: True target: {target} @@ -30,6 +31,9 @@ target: {target} regex=None I3: ignore-body-lines regex=None + I4: ignore-by-author-name + ignore=all + regex=None T1: title-max-length line-length=20 T2: title-trailing-whitespace diff --git a/qa/expected/test_contrib/test_contrib_rules_1 b/qa/expected/test_contrib/test_contrib_rules_1 index 6876c80..6ab7512 100644 --- a/qa/expected/test_contrib/test_contrib_rules_1 +++ b/qa/expected/test_contrib/test_contrib_rules_1 @@ -1,4 +1,3 @@ 1: CC1 Body does not contain a 'Signed-off-by' line -1: CT1 Title does not start with one of fix, feat, chore, docs, style, refactor, perf, test, revert, ci, build: "WIP Thi$ is å title" 1: CT1 Title does not follow ConventionalCommits.org format 'type(optional-scope): description': "WIP Thi$ is å title" 1: T5 Title contains the word 'WIP' (case-insensitive): "WIP Thi$ is å title" diff --git a/qa/expected/test_contrib/test_contrib_rules_with_config_1 b/qa/expected/test_contrib/test_contrib_rules_with_config_1 index d5b5cf8..6ab7512 100644 --- a/qa/expected/test_contrib/test_contrib_rules_with_config_1 +++ b/qa/expected/test_contrib/test_contrib_rules_with_config_1 @@ -1,4 +1,3 @@ 1: CC1 Body does not contain a 'Signed-off-by' line -1: CT1 Title does not start with one of föo, bår: "WIP Thi$ is å title" 1: CT1 Title does not follow ConventionalCommits.org format 'type(optional-scope): description': "WIP Thi$ is å title" 1: T5 Title contains the word 'WIP' (case-insensitive): "WIP Thi$ is å title" diff --git a/qa/requirements.txt b/qa/requirements.txt index 95d49d4..5a4f660 100644 --- a/qa/requirements.txt +++ b/qa/requirements.txt @@ -1,4 +1,4 @@ -sh==1.14.1 -pytest==6.2.3; -arrow==1.0.3; +sh==1.14.2 +pytest==6.2.5; +arrow==1.2.0; gitlint # no version as you want to test the currently installed version diff --git a/qa/shell.py b/qa/shell.py index 97dcd2c..630843f 100644 --- a/qa/shell.py +++ b/qa/shell.py @@ -67,8 +67,8 @@ else: popen_kwargs['env'] = kwargs['_env'] try: - p = subprocess.Popen(args, **popen_kwargs) - result = p.communicate() + with subprocess.Popen(args, **popen_kwargs) as p: + result = p.communicate() except FileNotFoundError as exc: raise CommandNotFound from exc diff --git a/qa/test_commits.py b/qa/test_commits.py index 389ad66..92e1087 100644 --- a/qa/test_commits.py +++ b/qa/test_commits.py @@ -40,19 +40,60 @@ class CommitsTests(BaseTestCase): expected_kwargs = {'commit_sha1': commit_sha1, 'commit_sha2': commit_sha2} self.assertEqualStdout(output, self.get_expected("test_commits/test_violations_1", expected_kwargs)) + def test_lint_empty_commit_range(self): + """ Tests `gitlint --commits ^...` --fail-without-commits where the provided range is empty. """ + self.create_simple_commit("Sïmple title.\n") + self.create_simple_commit("Sïmple title2.\n") + commit_sha = self.get_last_commit_hash() + # git revspec -> 2 dots: .. -> empty range when using same start and end sha + refspec = f"{commit_sha}..{commit_sha}" + + # Regular gitlint invocation should run without issues + output = gitlint("--commits", refspec, _cwd=self.tmp_git_repo, _tty_in=True) + self.assertEqual(output.exit_code, 0) + self.assertEqualStdout(output, "") + + # Gitlint should fail when --fail-without-commits is used + output = gitlint("--commits", refspec, "--fail-without-commits", _cwd=self.tmp_git_repo, _tty_in=True, + _ok_code=[self.GITLINT_USAGE_ERROR]) + self.assertEqual(output.exit_code, self.GITLINT_USAGE_ERROR) + self.assertEqualStdout(output, f"Error: No commits in range \"{refspec}\"\n") + def test_lint_single_commit(self): - """ Tests `gitlint --commits ` """ + """ Tests `gitlint --commits ^...` """ self.create_simple_commit("Sïmple title.\n") + first_commit_sha = self.get_last_commit_hash() self.create_simple_commit("Sïmple title2.\n") commit_sha = self.get_last_commit_hash() refspec = f"{commit_sha}^...{commit_sha}" self.create_simple_commit("Sïmple title3.\n") - output = gitlint("--commits", refspec, _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[2]) + expected = ("1: T3 Title has trailing punctuation (.): \"Sïmple title2.\"\n" + "3: B6 Body message is missing\n") + + # Lint using --commit + output = gitlint("--commit", commit_sha, _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[2]) self.assertEqual(output.exit_code, 2) self.assertEqualStdout(output, expected) + # Lint a single commit using --commits pointing to the single commit + output = gitlint("--commits", refspec, _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[2]) + self.assertEqual(output.exit_code, 2) + self.assertEqualStdout(output, expected) + + # Lint the first commit in the repository. This is a use-case that is not supported by --commits + # As ^... is not correct refspec in case points to the initial commit (which has no parents) + expected = ("1: T3 Title has trailing punctuation (.): \"Sïmple title.\"\n" + + "3: B6 Body message is missing\n") + output = gitlint("--commit", first_commit_sha, _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[2]) + self.assertEqual(output.exit_code, 2) + self.assertEqualStdout(output, expected) + + # Assert that indeed --commits is not supported when points the the first commit + refspec = f"{first_commit_sha}^...{first_commit_sha}" + output = gitlint("--commits", refspec, _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[254]) + self.assertEqual(output.exit_code, 254) + def test_lint_staged_stdin(self): """ Tests linting a staged commit. Gitint should lint the passed commit message andfetch additional meta-data from the underlying repository. The easiest way to test this is by inspecting `--debug` output. @@ -139,7 +180,7 @@ class CommitsTests(BaseTestCase): self.assertEqualStdout(output, self.get_expected("test_commits/test_lint_head_1", expected_kwargs)) def test_ignore_commits(self): - """ Tests multiple commits of which some rules get igonored because of ignore-* rules """ + """ Tests multiple commits of which some rules get ignored because of ignore-* rules """ # Create repo and some commits tmp_git_repo = self.create_tmp_git_repo() self.create_simple_commit("Sïmple title.\n\nSimple bödy describing the commit", git_repo=tmp_git_repo) diff --git a/qa/test_config.py b/qa/test_config.py index 9c00b95..432a2c5 100644 --- a/qa/test_config.py +++ b/qa/test_config.py @@ -80,7 +80,8 @@ class ConfigTests(BaseTestCase): filename = self.create_simple_commit(commit_msg, git_repo=target_repo) env = self.create_environment({"GITLINT_DEBUG": "1", "GITLINT_VERBOSITY": "2", "GITLINT_IGNORE": "T1,T2", "GITLINT_CONTRIB": "CC1,CT1", - "GITLINT_IGNORE_STDIN": "1", "GITLINT_TARGET": target_repo, + "GITLINT_FAIL_WITHOUT_COMMITS": "1", "GITLINT_IGNORE_STDIN": "1", + "GITLINT_TARGET": target_repo, "GITLINT_COMMITS": self.get_last_commit_hash(git_repo=target_repo)}) output = gitlint(_env=env, _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[5]) expected_kwargs = self.get_debug_vars_last_commit(git_repo=target_repo) diff --git a/qa/test_contrib.py b/qa/test_contrib.py index e599d50..d71229a 100644 --- a/qa/test_contrib.py +++ b/qa/test_contrib.py @@ -10,14 +10,14 @@ class ContribRuleTests(BaseTestCase): def test_contrib_rules(self): self.create_simple_commit("WIP Thi$ is å title\n\nMy bödy that is a bit longer than 20 chars") output = gitlint("--contrib", "contrib-title-conventional-commits,CC1", - _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[4]) + _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[3]) self.assertEqualStdout(output, self.get_expected("test_contrib/test_contrib_rules_1")) def test_contrib_rules_with_config(self): self.create_simple_commit("WIP Thi$ is å title\n\nMy bödy that is a bit longer than 20 chars") output = gitlint("--contrib", "contrib-title-conventional-commits,CC1", "-c", "contrib-title-conventional-commits.types=föo,bår", - _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[4]) + _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[3]) self.assertEqualStdout(output, self.get_expected("test_contrib/test_contrib_rules_with_config_1")) def test_invalid_contrib_rules(self): diff --git a/qa/test_hooks.py b/qa/test_hooks.py index 32abcb0..b78100e 100644 --- a/qa/test_hooks.py +++ b/qa/test_hooks.py @@ -9,14 +9,15 @@ class HookTests(BaseTestCase): """ Integration tests for gitlint commitmsg hooks""" VIOLATIONS = ['gitlint: checking commit message...\n', - u'1: T3 Title has trailing punctuation (.): "WIP: This ïs a title."\n', - u'1: T5 Title contains the word \'WIP\' (case-insensitive): "WIP: This ïs a title."\n', - u'2: B4 Second line is not empty: "Contënt on the second line"\n', + '1: T3 Title has trailing punctuation (.): "WIP: This ïs a title."\n', + '1: T5 Title contains the word \'WIP\' (case-insensitive): "WIP: This ïs a title."\n', + '2: B4 Second line is not empty: "Contënt on the second line"\n', '3: B6 Body message is missing\n', '-----------------------------------------------\n', 'gitlint: \x1b[31mYour commit message contains violations.\x1b[0m\n'] def setUp(self): + super().setUp() self.responses = [] self.response_index = 0 self.githook_output = [] @@ -28,16 +29,19 @@ class HookTests(BaseTestCase): # install git commit-msg hook and assert output output_installed = gitlint("install-hook", _cwd=self.tmp_git_repo) - expected_installed = "Successfully installed gitlint commit-msg hook in %s/.git/hooks/commit-msg\n" % \ - self.tmp_git_repo + expected_installed = ("Successfully installed gitlint commit-msg hook in " + f"{self.tmp_git_repo}/.git/hooks/commit-msg\n") + self.assertEqualStdout(output_installed, expected_installed) def tearDown(self): # uninstall git commit-msg hook and assert output output_uninstalled = gitlint("uninstall-hook", _cwd=self.tmp_git_repo) - expected_uninstalled = "Successfully uninstalled gitlint commit-msg hook from %s/.git/hooks/commit-msg\n" % \ - self.tmp_git_repo + expected_uninstalled = ("Successfully uninstalled gitlint commit-msg hook from " + f"{self.tmp_git_repo}/.git/hooks/commit-msg\n") + self.assertEqualStdout(output_uninstalled, expected_uninstalled) + super().tearDown() def _violations(self): # Make a copy of the violations array so that we don't inadvertently edit it in the test (like I did :D) @@ -60,9 +64,9 @@ class HookTests(BaseTestCase): short_hash = self.get_last_commit_short_hash() expected_output = ["gitlint: checking commit message...\n", "gitlint: \x1b[32mOK\x1b[0m (no violations in commit message)\n", - "[master %s] This ïs a title\n" % short_hash, + f"[master {short_hash}] This ïs a title\n", " 1 file changed, 0 insertions(+), 0 deletions(-)\n", - " create mode 100644 %s\n" % test_filename] + f" create mode 100644 {test_filename}\n"] self.assertListEqual(expected_output, self.githook_output) def test_commit_hook_continue(self): @@ -76,10 +80,9 @@ class HookTests(BaseTestCase): expected_output = self._violations() expected_output += ["Continue with commit anyways (this keeps the current commit message)? " + "[y(es)/n(no)/e(dit)] " + - "[master %s] WIP: This ïs a title. Contënt on the second line\n" - % short_hash, + f"[master {short_hash}] WIP: This ïs a title. Contënt on the second line\n", " 1 file changed, 0 insertions(+), 0 deletions(-)\n", - " create mode 100644 %s\n" % test_filename] + f" create mode 100644 {test_filename}\n"] assert len(self.githook_output) == len(expected_output) for output, expected in zip(self.githook_output, expected_output): @@ -124,9 +127,9 @@ class HookTests(BaseTestCase): expected_output += self._violations()[1:] expected_output += ['Continue with commit anyways (this keeps the current commit message)? ' + "[y(es)/n(no)/e(dit)] " + - "[master %s] WIP: This ïs a title. Contënt on the second line\n" % short_hash, + f"[master {short_hash}] WIP: This ïs a title. Contënt on the second line\n", " 1 file changed, 0 insertions(+), 0 deletions(-)\n", - " create mode 100644 %s\n" % test_filename] + f" create mode 100644 {test_filename}\n"] assert len(self.githook_output) == len(expected_output) for output, expected in zip(self.githook_output, expected_output): diff --git a/qa/test_stdin.py b/qa/test_stdin.py index 18d6e7e..c98580e 100644 --- a/qa/test_stdin.py +++ b/qa/test_stdin.py @@ -50,7 +50,7 @@ class StdInTests(BaseTestCase): # We need to use subprocess.Popen() here instead of sh because when passing a file_handle to sh, it will # deal with reading the file itself instead of passing it on to gitlint as a STDIN. Since we're trying to # test for the condition where stat.S_ISREG == True that won't work for us here. - p = subprocess.Popen("gitlint", stdin=file_handle, cwd=self.tmp_git_repo, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - output, _ = p.communicate() - self.assertEqual(output.decode(DEFAULT_ENCODING), self.get_expected("test_stdin/test_stdin_file_1")) + with subprocess.Popen("gitlint", stdin=file_handle, cwd=self.tmp_git_repo, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as p: + output, _ = p.communicate() + self.assertEqual(output.decode(DEFAULT_ENCODING), self.get_expected("test_stdin/test_stdin_file_1")) -- cgit v1.2.3