summaryrefslogtreecommitdiffstats
path: root/qa/test_commits.py
blob: 11d1851ec17f527c9927913fce76991bcd836ca7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
import re

import arrow

from qa.base import BaseTestCase
from qa.shell import echo, git, gitlint


class CommitsTests(BaseTestCase):
    """Integration tests for the --commits argument, i.e. linting multiple commits or linting specific commits"""

    def test_successful(self):
        """Test linting multiple commits without violations"""
        git("checkout", "-b", "test-branch-commits-base", _cwd=self.tmp_git_repo)
        self.create_simple_commit("Sïmple title\n\nSimple bödy describing the commit")
        git("checkout", "-b", "test-branch-commits", _cwd=self.tmp_git_repo)
        self.create_simple_commit("Sïmple title2\n\nSimple bödy describing the commit2")
        self.create_simple_commit("Sïmple title3\n\nSimple bödy describing the commit3")
        output = gitlint(
            "--commits", "test-branch-commits-base...test-branch-commits", _cwd=self.tmp_git_repo, _tty_in=True
        )
        self.assertEqualStdout(output, "")

    def test_violations(self):
        """Test linting multiple commits with violations"""
        git("checkout", "-b", "test-branch-commits-violations-base", _cwd=self.tmp_git_repo)
        self.create_simple_commit("Sïmple title.\n")
        git("checkout", "-b", "test-branch-commits-violations", _cwd=self.tmp_git_repo)

        self.create_simple_commit("Sïmple title2.\n")
        commit_sha1 = self.get_last_commit_hash()[:10]
        self.create_simple_commit("Sïmple title3.\n")
        commit_sha2 = self.get_last_commit_hash()[:10]
        output = gitlint(
            "--commits",
            "test-branch-commits-violations-base...test-branch-commits-violations",
            _cwd=self.tmp_git_repo,
            _tty_in=True,
            _ok_code=[4],
        )

        self.assertEqual(output.exit_code, 4)
        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_csv_hash_list(self):
        """Test linting multiple commits (comma-separated) with violations"""
        git("checkout", "-b", "test-branch-commits-violations-base", _cwd=self.tmp_git_repo)
        self.create_simple_commit("Sïmple title1.\n")
        commit_sha1 = self.get_last_commit_hash()[:10]
        git("checkout", "-b", "test-branch-commits-violations", _cwd=self.tmp_git_repo)

        self.create_simple_commit("Sïmple title2.\n")
        commit_sha2 = self.get_last_commit_hash()[:10]
        self.create_simple_commit("Sïmple title3.\n")
        self.create_simple_commit("Sïmple title4.\n")
        commit_sha4 = self.get_last_commit_hash()[:10]

        # Lint subset of the commits in a specific order, passed in via csv list
        output = gitlint(
            "--commits",
            f"{commit_sha2},{commit_sha1},{commit_sha4}",
            _cwd=self.tmp_git_repo,
            _tty_in=True,
            _ok_code=[6],
        )

        self.assertEqual(output.exit_code, 6)
        expected_kwargs = {"commit_sha1": commit_sha1, "commit_sha2": commit_sha2, "commit_sha4": commit_sha4}
        self.assertEqualStdout(output, self.get_expected("test_commits/test_csv_hash_list_1", expected_kwargs))

    def test_lint_empty_commit_range(self):
        """Tests `gitlint --commits <sha>^...<sha>` --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: <exclusive sha>..<inclusive sha> -> 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 <sha>^...<same sha>`"""
        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")

        expected = '1: T3 Title has trailing punctuation (.): "Sïmple title2."\n' + "3: B6 Body message is missing\n"

        # Lint using --commit <commit sha>
        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 using --commits <commit sha>,
        output = gitlint("--commits", f"{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 <refspec> 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 <sha>^...<sha> is not correct refspec in case <sha> 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 <refspec> is not supported when <refspec> 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 and fetch additional meta-data
        from the underlying repository. The easiest way to test this is by inspecting `--debug` output.
        This is the equivalent of doing:
        echo "WIP: Pïpe test." | gitlint --staged --debug
        """
        # Create a commit first, before we stage changes. This ensures the repo is properly initialized.
        self.create_simple_commit("Sïmple title.\n")

        # Add some files, stage them: they should show up in the debug output as changed file
        filename1 = self.create_file(self.tmp_git_repo)
        git("add", filename1, _cwd=self.tmp_git_repo)
        filename2 = self.create_file(self.tmp_git_repo)
        git("add", filename2, _cwd=self.tmp_git_repo)

        output = gitlint(
            echo("WIP: Pïpe test."),
            "--staged",
            "--debug",
            _cwd=self.tmp_git_repo,
            _tty_in=False,
            _err_to_out=True,
            _ok_code=[3],
        )

        # Determine variable parts of expected output
        expected_kwargs = self.get_debug_vars_last_commit()
        filenames = sorted([filename1, filename2])
        expected_kwargs.update(
            {
                "changed_files": filenames,
                "changed_files_stats": (
                    f"{filenames[0]}: 0 additions, 0 deletions\n  {filenames[1]}: 0 additions, 0 deletions"
                ),
            }
        )

        # It's not really possible to determine the "Date: ..." line that is part of the debug output as this date
        # is not taken from git but instead generated by gitlint itself. As a workaround, we extract the date from the
        # gitlint output using a regex, parse the date to ensure the format is correct, and then pass that as an
        # expected variable.
        matches = re.search(r"^Date:\s+(.*)", str(output), re.MULTILINE)
        if matches:
            expected_date = arrow.get(str(matches.group(1)), "YYYY-MM-DD HH:mm:ss Z").format("YYYY-MM-DD HH:mm:ss Z")
            expected_kwargs["staged_date"] = expected_date

        self.assertEqualStdout(output, self.get_expected("test_commits/test_lint_staged_stdin_1", expected_kwargs))
        self.assertEqual(output.exit_code, 3)

    def test_lint_staged_msg_filename(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.
        This is the equivalent of doing:
        gitlint --msg-filename /tmp/my-commit-msg --staged --debug
        """
        # Create a commit first, before we stage changes. This ensures the repo is properly initialized.
        self.create_simple_commit("Sïmple title.\n")

        # Add some files, stage them: they should show up in the debug output as changed file
        filename1 = self.create_file(self.tmp_git_repo)
        git("add", filename1, _cwd=self.tmp_git_repo)
        filename2 = self.create_file(self.tmp_git_repo)
        git("add", filename2, _cwd=self.tmp_git_repo)

        tmp_commit_msg_file = self.create_tmpfile("WIP: from fïle test.")

        output = gitlint(
            "--msg-filename",
            tmp_commit_msg_file,
            "--staged",
            "--debug",
            _cwd=self.tmp_git_repo,
            _tty_in=False,
            _err_to_out=True,
            _ok_code=[3],
        )

        # Determine variable parts of expected output
        expected_kwargs = self.get_debug_vars_last_commit()
        filenames = sorted([filename1, filename2])
        expected_kwargs.update(
            {
                "changed_files": filenames,
                "changed_files_stats": (
                    f"{filenames[0]}: 0 additions, 0 deletions\n  {filenames[1]}: 0 additions, 0 deletions"
                ),
            }
        )

        # It's not really possible to determine the "Date: ..." line that is part of the debug output as this date
        # is not taken from git but instead generated by gitlint itself. As a workaround, we extract the date from the
        # gitlint output using a regex, parse the date to ensure the format is correct, and then pass that as an
        # expected variable.
        matches = re.search(r"^Date:\s+(.*)", str(output), re.MULTILINE)
        if matches:
            expected_date = arrow.get(str(matches.group(1)), "YYYY-MM-DD HH:mm:ss Z").format("YYYY-MM-DD HH:mm:ss Z")
            expected_kwargs["staged_date"] = expected_date

        expected = self.get_expected("test_commits/test_lint_staged_msg_filename_1", expected_kwargs)
        self.assertEqualStdout(output, expected)
        self.assertEqual(output.exit_code, 3)

    def test_lint_head(self):
        """Testing whether we can also recognize special refs like 'HEAD'"""
        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)
        self.create_simple_commit("Sïmple title", git_repo=tmp_git_repo)
        self.create_simple_commit("WIP: Sïmple title\n\nSimple bödy describing the commit", git_repo=tmp_git_repo)
        output = gitlint("--commits", "HEAD", _cwd=tmp_git_repo, _tty_in=True, _ok_code=[3])
        revlist = git("rev-list", "HEAD", _tty_in=True, _cwd=tmp_git_repo).split()

        expected_kwargs = {
            "commit_sha0": revlist[0][:10],
            "commit_sha1": revlist[1][:10],
            "commit_sha2": revlist[2][:10],
        }

        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 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)
        # Normally, this commit will give T3 (trailing-punctuation), T5 (WIP) and B5 (bod-too-short) violations
        # But in this case only B5 because T3 and T5 are being ignored because of config
        self.create_simple_commit("Release: WIP tïtle.\n\nShort", git_repo=tmp_git_repo)
        # In the following 2 commits, the T3 violations are as normal
        self.create_simple_commit("Sïmple WIP title3.\n\nThis is \ta relëase commit\nMore info", git_repo=tmp_git_repo)
        self.create_simple_commit("Sïmple title4.\n\nSimple bödy describing the commit4", git_repo=tmp_git_repo)
        revlist = git("rev-list", "HEAD", _tty_in=True, _cwd=tmp_git_repo).split()

        config_path = self.get_sample_path("config/ignore-release-commits")
        output = gitlint("--commits", "HEAD", "--config", config_path, _cwd=tmp_git_repo, _tty_in=True, _ok_code=[4])

        expected_kwargs = {
            "commit_sha0": revlist[0][:10],
            "commit_sha1": revlist[1][:10],
            "commit_sha2": revlist[2][:10],
            "commit_sha3": revlist[3][:10],
        }
        self.assertEqualStdout(output, self.get_expected("test_commits/test_ignore_commits_1", expected_kwargs))