1
0
Fork 0

Adding upstream version 0.18.0.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-13 06:06:11 +01:00
parent 9e88905bdf
commit fe56d9f685
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
118 changed files with 4146 additions and 2087 deletions

View file

@ -1,8 +1,6 @@
# -*- coding: utf-8 -*-
# pylint: disable=bad-option-value,unidiomatic-typecheck,undefined-variable,no-else-return,
# pylint: disable=too-many-function-args,unexpected-keyword-arg
import io
import os
import platform
import shutil
@ -20,8 +18,8 @@ from qa.utils import DEFAULT_ENCODING
class BaseTestCase(TestCase):
""" Base class of which all gitlint integration test classes are derived.
Provides a number of convenience methods. """
"""Base class of which all gitlint integration test classes are derived.
Provides a number of convenience methods."""
# In case of assert failures, print the full error message
maxDiff = None
@ -32,7 +30,7 @@ class BaseTestCase(TestCase):
GITLINT_USAGE_ERROR = 253
def setUp(self):
""" Sets up the integration tests by creating a new temporary git repository """
"""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()
@ -47,7 +45,7 @@ class BaseTestCase(TestCase):
def assertEqualStdout(self, output, expected): # pylint: disable=invalid-name
self.assertIsInstance(output, RunningCommand)
output = output.stdout.decode(DEFAULT_ENCODING)
output = output.replace('\r', '')
output = output.replace("\r", "")
self.assertMultiLineEqual(output, expected)
@staticmethod
@ -56,11 +54,11 @@ class BaseTestCase(TestCase):
return os.path.realpath(f"/tmp/gitlint-test-{timestamp}")
def create_tmp_git_repo(self):
""" Creates a temporary git repository and returns its directory path """
"""Creates a temporary git repository and returns its directory path"""
tmp_git_repo = self.generate_temp_path()
self.tmp_git_repos.append(tmp_git_repo)
git("init", tmp_git_repo)
git("init", "--initial-branch", "main", tmp_git_repo)
# configuring name and email is required in every git repot
git("config", "user.name", "gitlint-test-user", _cwd=tmp_git_repo)
git("config", "user.email", "gitlint@test.com", _cwd=tmp_git_repo)
@ -77,29 +75,43 @@ class BaseTestCase(TestCase):
return tmp_git_repo
@staticmethod
def create_file(parent_dir):
""" Creates a file inside a passed directory. Returns filename."""
def create_file(parent_dir, content=None):
"""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()
full_path = os.path.join(parent_dir, test_filename)
if content:
if isinstance(content, bytes):
open_kwargs = {"mode": "wb"}
else:
open_kwargs = {"mode": "w", "encoding": DEFAULT_ENCODING}
with open(full_path, **open_kwargs) as f: # pylint: disable=unspecified-encoding
f.write(content)
else:
# pylint: disable=consider-using-with
open(full_path, "a", encoding=DEFAULT_ENCODING).close()
return test_filename
@staticmethod
def create_environment(envvars=None):
""" Creates a copy of the current os.environ and adds/overwrites a given set of variables to it """
"""Creates a copy of the current os.environ and adds/overwrites a given set of variables to it"""
environment = os.environ.copy()
if envvars:
environment.update(envvars)
return environment
def create_tmp_git_config(self, contents):
""" Creates an environment with the GIT_CONFIG variable set to a file with the given contents. """
"""Creates an environment with the GIT_CONFIG variable set to a file with the given contents."""
tmp_config = self.create_tmpfile(contents)
return self.create_environment({"GIT_CONFIG": tmp_config})
def create_simple_commit(self, message, out=None, ok_code=None, env=None, git_repo=None, tty_in=False):
""" Creates a simple commit with an empty test file.
:param message: Commit message for the commit. """
def create_simple_commit(
self, message, *, file_contents=None, out=None, ok_code=None, env=None, git_repo=None, tty_in=False
):
"""Creates a simple commit with an empty test file.
:param message: Commit message for the commit."""
git_repo = self.tmp_git_repo if git_repo is None else git_repo
@ -110,23 +122,39 @@ class BaseTestCase(TestCase):
environment = self.create_environment(env)
# Create file and add to git
test_filename = self.create_file(git_repo)
test_filename = self.create_file(git_repo, file_contents)
git("add", test_filename, _cwd=git_repo)
# https://amoffat.github.io/sh/#interactive-callbacks
if not ok_code:
ok_code = [0]
git("commit", "-m", message, _cwd=git_repo, _err_to_out=True, _out=out, _tty_in=tty_in,
_ok_code=ok_code, _env=environment)
git(
"commit",
"-m",
message,
_cwd=git_repo,
_err_to_out=True,
_out=out,
_tty_in=tty_in,
_ok_code=ok_code,
_env=environment,
)
return test_filename
def create_tmpfile(self, content):
""" Utility method to create temp files. These are cleaned at the end of the test """
# Not using a context manager to avoid unneccessary identation in test code
"""Utility method to create temp files. These are cleaned at the end of the test"""
# Not using a context manager to avoid unnecessary indentation in test code
tmpfile, tmpfilepath = tempfile.mkstemp()
self.tmpfiles.append(tmpfilepath)
with io.open(tmpfile, "w", encoding=DEFAULT_ENCODING) as f:
if isinstance(content, bytes):
open_kwargs = {"mode": "wb"}
else:
open_kwargs = {"mode": "w", "encoding": DEFAULT_ENCODING}
with open(tmpfile, **open_kwargs) as f: # pylint: disable=unspecified-encoding
f.write(content)
return tmpfilepath
@staticmethod
@ -149,11 +177,11 @@ class BaseTestCase(TestCase):
@staticmethod
def get_expected(filename="", variable_dict=None):
""" Utility method to read an 'expected' file and return it as a string. Optionally replace template variables
specified by variable_dict. """
"""Utility method to read an 'expected' file and return it as a string. Optionally replace template variables
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)
with io.open(expected_path, encoding=DEFAULT_ENCODING) as file:
with open(expected_path, encoding=DEFAULT_ENCODING) as file:
expected = file.read()
if variable_dict:
@ -162,20 +190,25 @@ class BaseTestCase(TestCase):
@staticmethod
def get_system_info_dict():
""" Returns a dict with items related to system values logged by `gitlint --debug` """
"""Returns a dict with items related to system values logged by `gitlint --debug`"""
expected_gitlint_version = gitlint("--version").replace("gitlint, version ", "").strip()
expected_git_version = git("--version").strip()
return {'platform': platform.platform(), 'python_version': sys.version,
'git_version': expected_git_version, 'gitlint_version': expected_gitlint_version,
'GITLINT_USE_SH_LIB': BaseTestCase.GITLINT_USE_SH_LIB, 'DEFAULT_ENCODING': DEFAULT_ENCODING}
return {
"platform": platform.platform(),
"python_version": sys.version,
"git_version": expected_git_version,
"gitlint_version": expected_gitlint_version,
"GITLINT_USE_SH_LIB": BaseTestCase.GITLINT_USE_SH_LIB,
"DEFAULT_ENCODING": DEFAULT_ENCODING,
}
def get_debug_vars_last_commit(self, git_repo=None):
""" Returns a dict with items related to `gitlint --debug` output for the last commit. """
"""Returns a dict with items related to `gitlint --debug` output for the last commit."""
target_repo = git_repo if git_repo else self.tmp_git_repo
commit_sha = self.get_last_commit_hash(git_repo=target_repo)
expected_date = git("log", "-1", "--pretty=%ai", _tty_out=False, _cwd=target_repo)
expected_date = arrow.get(str(expected_date), "YYYY-MM-DD HH:mm:ss Z").format("YYYY-MM-DD HH:mm:ss Z")
expected_kwargs = self.get_system_info_dict()
expected_kwargs.update({'target': target_repo, 'commit_sha': commit_sha, 'commit_date': expected_date})
expected_kwargs.update({"target": target_repo, "commit_sha": commit_sha, "commit_date": expected_date})
return expected_kwargs

View file

@ -0,0 +1,11 @@
Commit {commit_sha2}:
1: T3 Title has trailing punctuation (.): "Sïmple title2."
3: B6 Body message is missing
Commit {commit_sha1}:
1: T3 Title has trailing punctuation (.): "Sïmple title1."
3: B6 Body message is missing
Commit {commit_sha4}:
1: T3 Title has trailing punctuation (.): "Sïmple title4."
3: B6 Body message is missing

View file

@ -1,3 +1,5 @@
WARNING: I1 - ignore-by-title: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-by-title.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
WARNING: I2 - ignore-by-body: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-by-body.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
Commit {commit_sha0}:
1: T3 Title has trailing punctuation (.): "Sïmple title4."

View file

@ -14,11 +14,13 @@ contrib: []
ignore:
ignore-merge-commits: True
ignore-fixup-commits: True
ignore-fixup-amend-commits: True
ignore-squash-commits: True
ignore-revert-commits: True
ignore-stdin: False
staged: True
fail-without-commits: False
regex-style-search: False
verbosity: 3
debug: True
target: {target}
@ -60,17 +62,17 @@ target: {target}
B8: body-match-regex
regex=None
M1: author-valid-email
regex=[^@ ]+@[^@ ]+\.[^@ ]+
regex=^[^@ ]+@[^@ ]+\.[^@ ]+
DEBUG: gitlint.cli Fetching additional meta-data from staged commit
DEBUG: gitlint.cli Using --msg-filename.
DEBUG: gitlint.git ('config', '--get', 'core.commentchar')
DEBUG: gitlint.cli Linting 1 commit(s)
DEBUG: gitlint.lint Linting commit [SHA UNKNOWN]
DEBUG: gitlint.git ('diff', '--staged', '--numstat', '-r')
DEBUG: gitlint.git ('config', '--get', 'user.name')
DEBUG: gitlint.git ('config', '--get', 'user.email')
DEBUG: gitlint.git ('rev-parse', '--abbrev-ref', 'HEAD')
DEBUG: gitlint.git ('diff', '--staged', '--name-only', '-r')
DEBUG: gitlint.lint Commit Object
--- Commit Message ----
WIP: from fïle test.
@ -79,10 +81,14 @@ Author: gitlint-test-user <gitlint@test.com>
Date: {staged_date}
is-merge-commit: False
is-fixup-commit: False
is-fixup-amend-commit: False
is-squash-commit: False
is-revert-commit: False
Branches: ['master']
Parents: []
Branches: ['main']
Changed Files: {changed_files}
Changed Files Stats:
{changed_files_stats}
-----------------------
1: T3 Title has trailing punctuation (.): "WIP: from fïle test."
1: T5 Title contains the word 'WIP' (case-insensitive): "WIP: from fïle test."

View file

@ -14,11 +14,13 @@ contrib: []
ignore:
ignore-merge-commits: True
ignore-fixup-commits: True
ignore-fixup-amend-commits: True
ignore-squash-commits: True
ignore-revert-commits: True
ignore-stdin: False
staged: True
fail-without-commits: False
regex-style-search: False
verbosity: 3
debug: True
target: {target}
@ -60,7 +62,7 @@ target: {target}
B8: body-match-regex
regex=None
M1: author-valid-email
regex=[^@ ]+@[^@ ]+\.[^@ ]+
regex=^[^@ ]+@[^@ ]+\.[^@ ]+
DEBUG: gitlint.cli Fetching additional meta-data from staged commit
DEBUG: gitlint.cli Stdin data: 'WIP: Pïpe test.
@ -69,10 +71,10 @@ DEBUG: gitlint.cli Stdin detected and not ignored. Using as input.
DEBUG: gitlint.git ('config', '--get', 'core.commentchar')
DEBUG: gitlint.cli Linting 1 commit(s)
DEBUG: gitlint.lint Linting commit [SHA UNKNOWN]
DEBUG: gitlint.git ('diff', '--staged', '--numstat', '-r')
DEBUG: gitlint.git ('config', '--get', 'user.name')
DEBUG: gitlint.git ('config', '--get', 'user.email')
DEBUG: gitlint.git ('rev-parse', '--abbrev-ref', 'HEAD')
DEBUG: gitlint.git ('diff', '--staged', '--name-only', '-r')
DEBUG: gitlint.lint Commit Object
--- Commit Message ----
WIP: Pïpe test.
@ -81,10 +83,14 @@ Author: gitlint-test-user <gitlint@test.com>
Date: {staged_date}
is-merge-commit: False
is-fixup-commit: False
is-fixup-amend-commit: False
is-squash-commit: False
is-revert-commit: False
Branches: ['master']
Parents: []
Branches: ['main']
Changed Files: {changed_files}
Changed Files Stats:
{changed_files_stats}
-----------------------
1: T3 Title has trailing punctuation (.): "WIP: Pïpe test."
1: T5 Title contains the word 'WIP' (case-insensitive): "WIP: Pïpe test."

View file

@ -14,11 +14,13 @@ contrib: ['CC1', 'CT1']
ignore: T1,T2
ignore-merge-commits: True
ignore-fixup-commits: True
ignore-fixup-amend-commits: True
ignore-squash-commits: True
ignore-revert-commits: True
ignore-stdin: True
staged: False
fail-without-commits: True
regex-style-search: False
verbosity: 2
debug: True
target: {target}
@ -60,7 +62,7 @@ target: {target}
B8: body-match-regex
regex=None
M1: author-valid-email
regex=[^@ ]+@[^@ ]+\.[^@ ]+
regex=^[^@ ]+@[^@ ]+\.[^@ ]+
CC1: contrib-body-requires-signed-off-by
CT1: contrib-title-conventional-commits
types=fix,feat,chore,docs,style,refactor,perf,test,revert,ci,build
@ -71,8 +73,8 @@ DEBUG: gitlint.cli Linting 1 commit(s)
DEBUG: gitlint.git ('log', '{commit_sha}', '-1', '--pretty=%aN%x00%aE%x00%ai%x00%P%n%B')
DEBUG: gitlint.git ('config', '--get', 'core.commentchar')
DEBUG: gitlint.lint Linting commit {commit_sha}
DEBUG: gitlint.git ('diff-tree', '--no-commit-id', '--numstat', '-r', '--root', '{commit_sha}')
DEBUG: gitlint.git ('branch', '--contains', '{commit_sha}')
DEBUG: gitlint.git ('diff-tree', '--no-commit-id', '--name-only', '-r', '--root', '{commit_sha}')
DEBUG: gitlint.lint Commit Object
--- Commit Message ----
WIP: Thïs is a title thåt is a bit longer.
@ -84,10 +86,14 @@ Author: gitlint-test-user <gitlint@test.com>
Date: {commit_date}
is-merge-commit: False
is-fixup-commit: False
is-fixup-amend-commit: False
is-squash-commit: False
is-revert-commit: False
Branches: ['master']
Parents: []
Branches: ['main']
Changed Files: {changed_files}
Changed Files Stats:
{changed_files_stats}
-----------------------
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

View file

@ -14,11 +14,13 @@ contrib: []
ignore:
ignore-merge-commits: True
ignore-fixup-commits: True
ignore-fixup-amend-commits: True
ignore-squash-commits: True
ignore-revert-commits: True
ignore-stdin: False
staged: True
fail-without-commits: False
regex-style-search: False
verbosity: 0
debug: True
target: {target}
@ -60,17 +62,17 @@ target: {target}
B8: body-match-regex
regex=None
M1: author-valid-email
regex=[^@ ]+@[^@ ]+\.[^@ ]+
regex=^[^@ ]+@[^@ ]+\.[^@ ]+
DEBUG: gitlint.cli Fetching additional meta-data from staged commit
DEBUG: gitlint.cli Using --msg-filename.
DEBUG: gitlint.git ('config', '--get', 'core.commentchar')
DEBUG: gitlint.cli Linting 1 commit(s)
DEBUG: gitlint.lint Linting commit [SHA UNKNOWN]
DEBUG: gitlint.git ('diff', '--staged', '--numstat', '-r')
DEBUG: gitlint.git ('config', '--get', 'user.name')
DEBUG: gitlint.git ('config', '--get', 'user.email')
DEBUG: gitlint.git ('rev-parse', '--abbrev-ref', 'HEAD')
DEBUG: gitlint.git ('diff', '--staged', '--name-only', '-r')
DEBUG: gitlint.lint Commit Object
--- Commit Message ----
WIP: msg-fïlename test.
@ -79,9 +81,12 @@ Author: gitlint-test-user <gitlint@test.com>
Date: {date}
is-merge-commit: False
is-fixup-commit: False
is-fixup-amend-commit: False
is-squash-commit: False
is-revert-commit: False
Branches: ['master']
Parents: []
Branches: ['main']
Changed Files: []
Changed Files Stats: {{}}
-----------------------
DEBUG: gitlint.cli Exit Code = 3

View file

@ -14,11 +14,13 @@ contrib: []
ignore: title-trailing-punctuation,B2
ignore-merge-commits: True
ignore-fixup-commits: True
ignore-fixup-amend-commits: True
ignore-squash-commits: True
ignore-revert-commits: True
ignore-stdin: False
staged: False
fail-without-commits: False
regex-style-search: False
verbosity: 2
debug: True
target: {target}
@ -60,7 +62,7 @@ target: {target}
B8: body-match-regex
regex=None
M1: author-valid-email
regex=[^@ ]+@[^@ ]+\.[^@ ]+
regex=^[^@ ]+@[^@ ]+\.[^@ ]+
DEBUG: gitlint.cli No --msg-filename flag, no or empty data passed to stdin. Using the local repo.
DEBUG: gitlint.git ('log', '-1', '--pretty=%H')
@ -68,8 +70,8 @@ DEBUG: gitlint.cli Linting 1 commit(s)
DEBUG: gitlint.git ('log', '{commit_sha}', '-1', '--pretty=%aN%x00%aE%x00%ai%x00%P%n%B')
DEBUG: gitlint.git ('config', '--get', 'core.commentchar')
DEBUG: gitlint.lint Linting commit {commit_sha}
DEBUG: gitlint.git ('diff-tree', '--no-commit-id', '--numstat', '-r', '--root', '{commit_sha}')
DEBUG: gitlint.git ('branch', '--contains', '{commit_sha}')
DEBUG: gitlint.git ('diff-tree', '--no-commit-id', '--name-only', '-r', '--root', '{commit_sha}')
DEBUG: gitlint.lint Commit Object
--- Commit Message ----
WIP: Thïs is a title thåt is a bit longer.
@ -81,10 +83,14 @@ Author: gitlint-test-user <gitlint@test.com>
Date: {commit_date}
is-merge-commit: False
is-fixup-commit: False
is-fixup-amend-commit: False
is-squash-commit: False
is-revert-commit: False
Branches: ['master']
Parents: []
Branches: ['main']
Changed Files: {changed_files}
Changed Files Stats:
{changed_files_stats}
-----------------------
1: T1 Title exceeds max length (42>20)
1: T5 Title contains the word 'WIP' (case-insensitive)

View file

@ -0,0 +1,94 @@
DEBUG: gitlint.cli To report issues, please visit https://github.com/jorisroovers/gitlint/issues
DEBUG: gitlint.cli Platform: {platform}
DEBUG: gitlint.cli Python version: {python_version}
DEBUG: gitlint.git ('--version',)
DEBUG: gitlint.cli Git version: {git_version}
DEBUG: gitlint.cli Gitlint version: {gitlint_version}
DEBUG: gitlint.cli GITLINT_USE_SH_LIB: {GITLINT_USE_SH_LIB}
DEBUG: gitlint.cli DEFAULT_ENCODING: {DEFAULT_ENCODING}
DEBUG: gitlint.cli Configuration
config-path: None
[GENERAL]
extra-path: None
contrib: []
ignore:
ignore-merge-commits: True
ignore-fixup-commits: True
ignore-fixup-amend-commits: True
ignore-squash-commits: True
ignore-revert-commits: True
ignore-stdin: False
staged: False
fail-without-commits: False
regex-style-search: False
verbosity: 3
debug: True
target: {target}
[RULES]
I1: ignore-by-title
ignore=all
regex=None
I2: ignore-by-body
ignore=all
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
T6: title-leading-whitespace
T3: title-trailing-punctuation
T4: title-hard-tab
T5: title-must-not-contain-word
words=WIP
T7: title-match-regex
regex=None
T8: title-min-length
min-length=5
B1: body-max-line-length
line-length=80
B5: body-min-length
min-length=20
B6: body-is-missing
ignore-merge-commits=True
B2: body-trailing-whitespace
B3: body-hard-tab
B4: body-first-line-empty
B7: body-changed-file-mention
files=
B8: body-match-regex
regex=None
M1: author-valid-email
regex=^[^@ ]+@[^@ ]+\.[^@ ]+
DEBUG: gitlint.cli No --msg-filename flag, no or empty data passed to stdin. Using the local repo.
DEBUG: gitlint.git ('log', '-1', '--pretty=%H')
DEBUG: gitlint.cli Linting 1 commit(s)
DEBUG: gitlint.git ('log', '{commit_sha}', '-1', '--pretty=%aN%x00%aE%x00%ai%x00%P%n%B')
DEBUG: gitlint.git ('config', '--get', 'core.commentchar')
DEBUG: gitlint.lint Linting commit {commit_sha}
DEBUG: gitlint.git ('diff-tree', '--no-commit-id', '--numstat', '-r', '--root', '{commit_sha}')
DEBUG: gitlint.git ('branch', '--contains', '{commit_sha}')
DEBUG: gitlint.lint Commit Object
--- Commit Message ----
Sïmple commit
--- Meta info ---------
Author: gitlint-test-user <gitlint@test.com>
Date: {commit_date}
is-merge-commit: False
is-fixup-commit: False
is-fixup-amend-commit: False
is-squash-commit: False
is-revert-commit: False
Parents: []
Branches: ['main']
Changed Files: {changed_files}
Changed Files Stats:
{changed_files_stats}
-----------------------
3: B6 Body message is missing
DEBUG: gitlint.cli Exit Code = 1

View file

@ -1,5 +1,5 @@
1: T5 Title contains the word 'WIP' (case-insensitive): "WIP: Thi$ is å title"
1: UC2 Body does not contain a 'Signed-off-by' line
1: UC3 Branch name 'master' does not start with one of ['feature/', 'hotfix/', 'release/']
1: UC3 Branch name 'main' does not start with one of ['feature/', 'hotfix/', 'release/']
1: UL1 Title contains the special character '$': "WIP: Thi$ is å title"
2: B4 Second line is not empty: "Content on the second line"

View file

@ -1,4 +1,4 @@
1: UC2 Body does not contain a 'Signed-off-by' line
1: UC3 Branch name 'master' does not start with one of ['feature/', 'hotfix/', 'release/']
1: UC3 Branch name 'main' does not start with one of ['feature/', 'hotfix/', 'release/']
1: UL1 Title contains the special character '$'
2: B4 Second line is not empty

View file

@ -1,6 +1,6 @@
1: T5 Title contains the word 'WIP' (case-insensitive): "WIP: Thi$ is å title"
1: UC1 Body contains too many lines (2 > 1)
1: UC2 Body does not contain a 'Signed-off-by' line
1: UC3 Branch name 'master' does not start with one of ['feature/', 'hotfix/', 'release/']
1: UC3 Branch name 'main' does not start with one of ['feature/', 'hotfix/', 'release/']
1: UL1 Title contains the special character '$': "WIP: Thi$ is å title"
2: B4 Second line is not empty: "Content on the second line"

View file

@ -1,7 +1,7 @@
1: T5 Title contains the word 'WIP' (case-insensitive): "WIP: Thi$ is å title"
1: UC1 GitContext.current_branch: master
1: UC1 GitContext.current_branch: main
1: UC1 GitContext.commentchar: #
1: UC2 GitCommit.branches: ['master']
1: UC2 GitCommit.branches: ['main']
1: UC2 GitCommit.custom_prop: foöbar
1: UC4 int-öption: 2
1: UC4 str-öption: föo

View file

@ -1,4 +1,4 @@
sh==1.14.2
pytest==6.2.5;
arrow==1.2.1;
sh==1.14.3
pytest==7.0.1;
arrow==1.2.3;
gitlint # no version as you want to test the currently installed version

View file

@ -1,25 +1,25 @@
# -*- coding: utf-8 -*-
from gitlint.rules import CommitRule, RuleViolation, ConfigurationRule
from gitlint.options import IntOption, StrOption, ListOption
class GitContextRule(CommitRule):
""" Rule that tests whether we can correctly access certain gitcontext properties """
"""Rule that tests whether we can correctly access certain gitcontext properties"""
name = "gïtcontext"
id = "UC1"
def validate(self, commit):
violations = [
RuleViolation(self.id, f"GitContext.current_branch: {commit.context.current_branch}", line_nr=1),
RuleViolation(self.id, f"GitContext.commentchar: {commit.context.commentchar}", line_nr=1)
RuleViolation(self.id, f"GitContext.commentchar: {commit.context.commentchar}", line_nr=1),
]
return violations
class GitCommitRule(CommitRule):
""" Rule that tests whether we can correctly access certain commit properties """
"""Rule that tests whether we can correctly access certain commit properties"""
name = "gïtcommit"
id = "UC2"
@ -33,7 +33,8 @@ class GitCommitRule(CommitRule):
class GitlintConfigurationRule(ConfigurationRule):
""" Rule that tests whether we can correctly access the config as well as modify the commit message """
"""Rule that tests whether we can correctly access the config as well as modify the commit message"""
name = "cönfigrule"
id = "UC3"
@ -50,13 +51,16 @@ class GitlintConfigurationRule(ConfigurationRule):
class ConfigurableCommitRule(CommitRule):
""" Rule that tests that we can add configuration to user-defined rules """
"""Rule that tests that we can add configuration to user-defined rules"""
name = "configürable"
id = "UC4"
options_spec = [IntOption("int-öption", 2, "int-öption description"),
StrOption("str-öption", "föo", "int-öption description"),
ListOption("list-öption", ["foo", "bar"], "list-öption description")]
options_spec = [
IntOption("int-öption", 2, "int-öption description"),
StrOption("str-öption", "föo", "int-öption description"),
ListOption("list-öption", ["foo", "bar"], "list-öption description"),
]
def validate(self, _):
violations = [

View file

@ -1,4 +1,3 @@
# This code is mostly duplicated from the `gitlint.shell` module. We consciously duplicate this code as to not depend
# on gitlint internals for our integration testing framework.
@ -7,6 +6,7 @@ from qa.utils import USE_SH_LIB, DEFAULT_ENCODING
if USE_SH_LIB:
from sh import git, echo, gitlint # pylint: disable=unused-import,no-name-in-module,import-error
gitlint = gitlint.bake(_unify_ttys=True, _tty_in=True) # pylint: disable=invalid-name
# import exceptions separately, this makes it a little easier to mock them out in the unit tests
@ -14,17 +14,18 @@ if USE_SH_LIB:
else:
class CommandNotFound(Exception):
""" Exception indicating a command was not found during execution """
"""Exception indicating a command was not found during execution"""
pass
class RunningCommand:
pass
class ShResult(RunningCommand):
""" Result wrapper class. We use this to more easily migrate from using https://amoffat.github.io/sh/ to using
the builtin subprocess module. """
"""Result wrapper class. We use this to more easily migrate from using https://amoffat.github.io/sh/ to using
the builtin subprocess module."""
def __init__(self, full_cmd, stdout, stderr='', exitcode=0):
def __init__(self, full_cmd, stdout, stderr="", exitcode=0):
self.full_cmd = full_cmd
# TODO(jorisroovers): The 'sh' library by default will merge stdout and stderr. We mimic this behavior
# for now until we fully remove the 'sh' library.
@ -36,7 +37,8 @@ else:
return self.stdout
class ErrorReturnCode(ShResult, Exception):
""" ShResult subclass for unexpected results (acts as an exception). """
"""ShResult subclass for unexpected results (acts as an exception)."""
pass
def git(*command_parts, **kwargs):
@ -54,17 +56,17 @@ else:
# If we reach this point and the result has an exit_code that is larger than 0, this means that we didn't
# get an exception (which is the default sh behavior for non-zero exit codes) and so the user is expecting
# a non-zero exit code -> just return the entire result
if hasattr(result, 'exit_code') and result.exit_code > 0:
if hasattr(result, "exit_code") and result.exit_code > 0:
return result
return str(result)
def _exec(*args, **kwargs):
pipe = subprocess.PIPE
popen_kwargs = {'stdout': pipe, 'stderr': pipe, 'shell': kwargs.get('_tty_out', False)}
if '_cwd' in kwargs:
popen_kwargs['cwd'] = kwargs['_cwd']
if '_env' in kwargs:
popen_kwargs['env'] = kwargs['_env']
popen_kwargs = {"stdout": pipe, "stderr": pipe, "shell": kwargs.get("_tty_out", False)}
if "_cwd" in kwargs:
popen_kwargs["cwd"] = kwargs["_cwd"]
if "_env" in kwargs:
popen_kwargs["env"] = kwargs["_env"]
try:
with subprocess.Popen(args, **popen_kwargs) as p:
@ -75,10 +77,10 @@ else:
exit_code = p.returncode
stdout = result[0].decode(DEFAULT_ENCODING)
stderr = result[1] # 'sh' does not decode the stderr bytes to unicode
full_cmd = '' if args is None else ' '.join(args)
full_cmd = "" if args is None else " ".join(args)
# If not _ok_code is specified, then only a 0 exit code is allowed
ok_exit_codes = kwargs.get('_ok_code', [0])
ok_exit_codes = kwargs.get("_ok_code", [0])
if exit_code in ok_exit_codes:
return ShResult(full_cmd, stdout, stderr, exit_code)

View file

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# pylint: disable=too-many-function-args,unexpected-keyword-arg
import re
@ -9,22 +8,22 @@ from qa.base import BaseTestCase
class CommitsTests(BaseTestCase):
""" Integration tests for the --commits argument, i.e. linting multiple commits at once or linting specific commits
"""
"""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 """
"""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)
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 """
"""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)
@ -33,15 +32,46 @@ class CommitsTests(BaseTestCase):
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])
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}
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. """
"""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()
@ -54,13 +84,19 @@ class CommitsTests(BaseTestCase):
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])
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")
self.assertEqualStdout(output, f'Error: No commits in range "{refspec}"\n')
def test_lint_single_commit(self):
""" Tests `gitlint --commits <sha>^...<same sha>` """
"""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")
@ -68,8 +104,7 @@ class CommitsTests(BaseTestCase):
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")
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])
@ -83,8 +118,7 @@ class CommitsTests(BaseTestCase):
# 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")
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)
@ -95,10 +129,10 @@ class CommitsTests(BaseTestCase):
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.
This is the equivalent of doing:
echo "WIP: Pïpe test." | gitlint --staged --debug
"""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:
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")
@ -109,30 +143,45 @@ class CommitsTests(BaseTestCase):
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])
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()
expected_kwargs.update({'changed_files': sorted([filename1, filename2])})
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)
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_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
"""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")
@ -145,28 +194,44 @@ class CommitsTests(BaseTestCase):
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])
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()
expected_kwargs.update({'changed_files': sorted([filename1, filename2])})
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)
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_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' """
"""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)
@ -174,13 +239,16 @@ class CommitsTests(BaseTestCase):
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]}
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 """
"""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)
@ -188,14 +256,17 @@ class CommitsTests(BaseTestCase):
# 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 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]}
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))

View file

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# pylint: disable=too-many-function-args,unexpected-keyword-arg
import re
@ -9,19 +8,24 @@ from qa.utils import DEFAULT_ENCODING
class ConfigTests(BaseTestCase):
""" Integration tests for gitlint configuration and configuration precedence. """
"""Integration tests for gitlint configuration and configuration precedence."""
def test_ignore_by_id(self):
self.create_simple_commit("WIP: Thïs is a title.\nContënt on the second line")
output = gitlint("--ignore", "T5,B4", _tty_in=True, _cwd=self.tmp_git_repo, _ok_code=[1])
expected = "1: T3 Title has trailing punctuation (.): \"WIP: Thïs is a title.\"\n"
expected = '1: T3 Title has trailing punctuation (.): "WIP: Thïs is a title."\n'
self.assertEqualStdout(output, expected)
def test_ignore_by_name(self):
self.create_simple_commit("WIP: Thïs is a title.\nContënt on the second line")
output = gitlint("--ignore", "title-must-not-contain-word,body-first-line-empty",
_cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[1])
expected = "1: T3 Title has trailing punctuation (.): \"WIP: Thïs is a title.\"\n"
output = gitlint(
"--ignore",
"title-must-not-contain-word,body-first-line-empty",
_cwd=self.tmp_git_repo,
_tty_in=True,
_ok_code=[1],
)
expected = '1: T3 Title has trailing punctuation (.): "WIP: Thïs is a title."\n'
self.assertEqualStdout(output, expected)
def test_verbosity(self):
@ -47,8 +51,10 @@ class ConfigTests(BaseTestCase):
self.assertEqualStdout(output, self.get_expected("test_config/test_set_rule_option_1"))
def test_config_from_file(self):
commit_msg = "WIP: Thïs is a title thåt is a bit longer.\nContent on the second line\n" + \
"This line of the body is here because we need it"
commit_msg = (
"WIP: Thïs is a title thåt is a bit longer.\nContent on the second line\n"
"This line of the body is here because we need it"
)
self.create_simple_commit(commit_msg)
config_path = self.get_sample_path("config/gitlintconfig")
output = gitlint("--config", config_path, _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[5])
@ -58,44 +64,66 @@ class ConfigTests(BaseTestCase):
# Test both on existing and new repo (we've had a bug in the past that was unique to empty repos)
repos = [self.tmp_git_repo, self.create_tmp_git_repo()]
for target_repo in repos:
commit_msg = "WIP: Thïs is a title thåt is a bit longer.\nContent on the second line\n" + \
"This line of the body is here because we need it"
commit_msg = (
"WIP: Thïs is a title thåt is a bit longer.\nContent on the second line\n"
"This line of the body is here because we need it"
)
filename = self.create_simple_commit(commit_msg, git_repo=target_repo)
config_path = self.get_sample_path("config/gitlintconfig")
output = gitlint("--config", config_path, "--debug", _cwd=target_repo, _tty_in=True, _ok_code=[5])
expected_kwargs = self.get_debug_vars_last_commit(git_repo=target_repo)
expected_kwargs.update({'config_path': config_path, 'changed_files': [filename]})
self.assertEqualStdout(output, self.get_expected("test_config/test_config_from_file_debug_1",
expected_kwargs))
expected_kwargs.update(
{
"config_path": config_path,
"changed_files": [filename],
"changed_files_stats": f"{filename}: 0 additions, 0 deletions",
}
)
self.assertEqualStdout(
output, self.get_expected("test_config/test_config_from_file_debug_1", expected_kwargs)
)
def test_config_from_env(self):
""" Test for configuring gitlint from environment variables """
"""Test for configuring gitlint from environment variables"""
# We invoke gitlint, configuring it via env variables, we can check whether gitlint picks these up correctly
# by comparing the debug output with what we'd expect
target_repo = self.create_tmp_git_repo()
commit_msg = "WIP: Thïs is a title thåt is a bit longer.\nContent on the second line\n" + \
"This line of the body is here because we need it"
commit_msg = (
"WIP: Thïs is a title thåt is a bit longer.\nContent on the second line\n"
"This line of the body is here because we need it"
)
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_FAIL_WITHOUT_COMMITS": "1", "GITLINT_IGNORE_STDIN": "1",
"GITLINT_TARGET": target_repo,
"GITLINT_COMMITS": self.get_last_commit_hash(git_repo=target_repo)})
env = self.create_environment(
{
"GITLINT_DEBUG": "1",
"GITLINT_VERBOSITY": "2",
"GITLINT_IGNORE": "T1,T2",
"GITLINT_CONTRIB": "CC1,CT1",
"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)
expected_kwargs.update({'changed_files': [filename]})
expected_kwargs.update(
{"changed_files": [filename], "changed_files_stats": f"{filename}: 0 additions, 0 deletions"}
)
self.assertEqualStdout(output, self.get_expected("test_config/test_config_from_env_1", expected_kwargs))
# For some env variables, we need a separate test ast they are mutually exclusive with the ones tested above
tmp_commit_msg_file = self.create_tmpfile("WIP: msg-fïlename test.")
env = self.create_environment({"GITLINT_DEBUG": "1", "GITLINT_TARGET": target_repo,
"GITLINT_SILENT": "1", "GITLINT_STAGED": "1"})
env = self.create_environment(
{"GITLINT_DEBUG": "1", "GITLINT_TARGET": target_repo, "GITLINT_SILENT": "1", "GITLINT_STAGED": "1"}
)
output = gitlint("--msg-filename", tmp_commit_msg_file,
_env=env, _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[3])
output = gitlint(
"--msg-filename", tmp_commit_msg_file, _env=env, _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[3]
)
# Extract date from actual output to insert it into the expected output
# We have to do this since there's no way for us to deterministically know that date otherwise

View file

@ -1,23 +1,29 @@
# -*- coding: utf-8 -*-
# pylint: disable=
from qa.shell import gitlint
from qa.base import BaseTestCase
class ContribRuleTests(BaseTestCase):
""" Integration tests for contrib rules."""
"""Integration tests for contrib rules."""
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=[3])
output = gitlint(
"--contrib", "contrib-title-conventional-commits,CC1", _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=[3])
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=[3],
)
self.assertEqualStdout(output, self.get_expected("test_contrib/test_contrib_rules_with_config_1"))
def test_invalid_contrib_rules(self):

View file

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# pylint: disable=too-many-function-args,unexpected-keyword-arg
import io
import os
from qa.shell import echo, git, gitlint
from qa.base import BaseTestCase
@ -8,7 +6,7 @@ from qa.utils import DEFAULT_ENCODING
class IntegrationTests(BaseTestCase):
""" Simple set of integration tests for gitlint """
"""Simple set of integration tests for gitlint"""
def test_successful(self):
# Test for STDIN with and without a TTY attached
@ -17,8 +15,8 @@ class IntegrationTests(BaseTestCase):
self.assertEqualStdout(output, "")
def test_successful_gitconfig(self):
""" Test gitlint when the underlying repo has specific git config set.
In the past, we've had issues with gitlint failing on some of these, so this acts as a regression test. """
"""Test gitlint when the underlying repo has specific git config set.
In the past, we've had issues with gitlint failing on some of these, so this acts as a regression test."""
# Different commentchar (Note: tried setting this to a special unicode char, but git doesn't like that)
git("config", "--add", "core.commentchar", "$", _cwd=self.tmp_git_repo)
@ -27,8 +25,8 @@ class IntegrationTests(BaseTestCase):
self.assertEqualStdout(output, "")
def test_successful_merge_commit(self):
# Create branch on master
self.create_simple_commit("Cömmit on master\n\nSimple bödy")
# Create branch on main
self.create_simple_commit("Cömmit on main\n\nSimple bödy")
# Create test branch, add a commit and determine the commit hash
git("checkout", "-b", "test-branch", _cwd=self.tmp_git_repo)
@ -37,10 +35,10 @@ class IntegrationTests(BaseTestCase):
self.create_simple_commit(f"{commit_title}\n\nSïmple body")
hash = self.get_last_commit_hash()
# Checkout master and merge the commit
# Checkout main and merge the commit
# We explicitly set the title of the merge commit to the title of the previous commit as this or similar
# behavior is what many tools do that handle merges (like github, gerrit, etc).
git("checkout", "master", _cwd=self.tmp_git_repo)
git("checkout", "main", _cwd=self.tmp_git_repo)
git("merge", "--no-ff", "-m", f"Merge '{commit_title}'", hash, _cwd=self.tmp_git_repo)
# Run gitlint and assert output is empty
@ -54,18 +52,14 @@ class IntegrationTests(BaseTestCase):
def test_fixup_commit(self):
# Create a normal commit and assert that it has a violation
test_filename = self.create_simple_commit("Cömmit on WIP master\n\nSimple bödy that is long enough")
test_filename = self.create_simple_commit("Cömmit on WIP main\n\nSimple bödy that is long enough")
output = gitlint(_cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[1])
expected = "1: T5 Title contains the word 'WIP' (case-insensitive): \"Cömmit on WIP master\"\n"
expected = "1: T5 Title contains the word 'WIP' (case-insensitive): \"Cömmit on WIP main\"\n"
self.assertEqualStdout(output, expected)
# Make a small modification to the commit and commit it using fixup commit
with io.open(os.path.join(self.tmp_git_repo, test_filename), "a", encoding=DEFAULT_ENCODING) as fh:
# Wanted to write a unicode string, but that's obnoxious if you want to do it across Python 2 and 3.
# https://stackoverflow.com/questions/22392377/
# error-writing-a-file-with-file-write-in-python-unicodeencodeerror
# So just keeping it simple - ASCII will here
fh.write("Appending some stuff\n")
with open(os.path.join(self.tmp_git_repo, test_filename), "a", encoding=DEFAULT_ENCODING) as fh:
fh.write("Appending söme stuff\n")
git("add", test_filename, _cwd=self.tmp_git_repo)
@ -78,13 +72,44 @@ class IntegrationTests(BaseTestCase):
# Make sure that if we set the ignore-fixup-commits option to false that we do still see the violations
output = gitlint("-c", "general.ignore-fixup-commits=false", _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[2])
expected = "1: T5 Title contains the word 'WIP' (case-insensitive): \"fixup! Cömmit on WIP master\"\n" + \
expected = (
"1: T5 Title contains the word 'WIP' (case-insensitive): \"fixup! Cömmit on WIP main\"\n"
"3: B6 Body message is missing\n"
)
self.assertEqualStdout(output, expected)
def test_fixup_amend_commit(self):
# Create a normal commit and assert that it has a violation
test_filename = self.create_simple_commit("Cömmit on WIP main\n\nSimple bödy that is long enough")
output = gitlint(_cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[1])
expected = "1: T5 Title contains the word 'WIP' (case-insensitive): \"Cömmit on WIP main\"\n"
self.assertEqualStdout(output, expected)
# Make a small modification to the commit and commit it using fixup=amend commit
with open(os.path.join(self.tmp_git_repo, test_filename), "a", encoding=DEFAULT_ENCODING) as fh:
fh.write("Appending söme stuff\n")
git("add", test_filename, _cwd=self.tmp_git_repo)
# We have to use --no-edit to avoid git starting $EDITOR to modify the commit message that is being amended
git("commit", "--no-edit", f"--fixup=amend:{self.get_last_commit_hash()}", _cwd=self.tmp_git_repo)
# Assert that gitlint does not show an error for the fixup commit
output = gitlint(_cwd=self.tmp_git_repo, _tty_in=True)
# No need to check exit code, the command above throws an exception on > 0 exit codes
self.assertEqualStdout(output, "")
# Make sure that if we set the ignore-fixup-commits option to false that we do still see the violations
output = gitlint(
"-c", "general.ignore-fixup-amend-commits=false", _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[1]
)
expected = "1: T5 Title contains the word 'WIP' (case-insensitive): \"amend! Cömmit on WIP main\"\n"
self.assertEqualStdout(output, expected)
def test_revert_commit(self):
self.create_simple_commit("WIP: Cömmit on master.\n\nSimple bödy")
self.create_simple_commit("WIP: Cömmit on main.\n\nSimple bödy")
hash = self.get_last_commit_hash()
git("revert", hash, _cwd=self.tmp_git_repo)
@ -93,21 +118,22 @@ class IntegrationTests(BaseTestCase):
self.assertEqualStdout(output, "")
# Assert that we do see the error if we disable the ignore-revert-commits option
output = gitlint("-c", "general.ignore-revert-commits=false",
_cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[1])
output = gitlint(
"-c", "general.ignore-revert-commits=false", _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[1]
)
self.assertEqual(output.exit_code, 1)
expected = "1: T5 Title contains the word 'WIP' (case-insensitive): \"Revert \"WIP: Cömmit on master.\"\"\n"
expected = '1: T5 Title contains the word \'WIP\' (case-insensitive): "Revert "WIP: Cömmit on main.""\n'
self.assertEqualStdout(output, expected)
def test_squash_commit(self):
# Create a normal commit and assert that it has a violation
test_filename = self.create_simple_commit("Cömmit on WIP master\n\nSimple bödy that is long enough")
test_filename = self.create_simple_commit("Cömmit on WIP main\n\nSimple bödy that is long enough")
output = gitlint(_cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[1])
expected = "1: T5 Title contains the word 'WIP' (case-insensitive): \"Cömmit on WIP master\"\n"
expected = "1: T5 Title contains the word 'WIP' (case-insensitive): \"Cömmit on WIP main\"\n"
self.assertEqualStdout(output, expected)
# Make a small modification to the commit and commit it using squash commit
with io.open(os.path.join(self.tmp_git_repo, test_filename), "a", encoding=DEFAULT_ENCODING) as fh:
with open(os.path.join(self.tmp_git_repo, test_filename), "a", encoding=DEFAULT_ENCODING) as fh:
# Wanted to write a unicode string, but that's obnoxious if you want to do it across Python 2 and 3.
# https://stackoverflow.com/questions/22392377/
# error-writing-a-file-with-file-write-in-python-unicodeencodeerror
@ -124,10 +150,13 @@ class IntegrationTests(BaseTestCase):
self.assertEqualStdout(output, "")
# Make sure that if we set the ignore-squash-commits option to false that we do still see the violations
output = gitlint("-c", "general.ignore-squash-commits=false",
_cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[2])
expected = "1: T5 Title contains the word 'WIP' (case-insensitive): \"squash! Cömmit on WIP master\"\n" + \
"3: B5 Body message is too short (14<20): \"Töo short body\"\n"
output = gitlint(
"-c", "general.ignore-squash-commits=false", _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[2]
)
expected = (
"1: T5 Title contains the word 'WIP' (case-insensitive): \"squash! Cömmit on WIP main\"\n"
'3: B5 Body message is too short (14<20): "Töo short body"\n'
)
self.assertEqualStdout(output, expected)
@ -139,11 +168,11 @@ class IntegrationTests(BaseTestCase):
def test_msg_filename(self):
tmp_commit_msg_file = self.create_tmpfile("WIP: msg-fïlename test.")
output = gitlint("--msg-filename", tmp_commit_msg_file, _tty_in=True, _ok_code=[3])
output = gitlint("--msg-filename", tmp_commit_msg_file, _tty_in=True, _cwd=self.tmp_git_repo, _ok_code=[3])
self.assertEqualStdout(output, self.get_expected("test_gitlint/test_msg_filename_1"))
def test_msg_filename_no_tty(self):
""" Make sure --msg-filename option also works with no TTY attached """
"""Make sure --msg-filename option also works with no TTY attached"""
tmp_commit_msg_file = self.create_tmpfile("WIP: msg-fïlename NO TTY test.")
# We need to set _err_to_out explicitly for sh to merge stdout and stderr output in case there's
@ -151,34 +180,51 @@ class IntegrationTests(BaseTestCase):
# http://amoffat.github.io/sh/sections/special_arguments.html?highlight=_tty_in#err-to-out
# We need to pass some whitespace to _in as sh will otherwise hang, see
# https://github.com/amoffat/sh/issues/427
output = gitlint("--msg-filename", tmp_commit_msg_file, _in=" ",
_tty_in=False, _err_to_out=True, _ok_code=[3])
output = gitlint(
"--msg-filename",
tmp_commit_msg_file,
_cwd=self.tmp_git_repo,
_in=" ",
_tty_in=False,
_err_to_out=True,
_ok_code=[3],
)
self.assertEqualStdout(output, self.get_expected("test_gitlint/test_msg_filename_no_tty_1"))
def test_no_git_name_set(self):
""" Ensure we print out a helpful message if user.name is not set """
"""Ensure we print out a helpful message if user.name is not set"""
tmp_commit_msg_file = self.create_tmpfile("WIP: msg-fïlename NO name test.")
# Name is checked before email so this isn't strictly
# necessary but seems good for consistency.
env = self.create_tmp_git_config("[user]\n email = test-emåil@foo.com\n")
output = gitlint("--staged", "--msg-filename", tmp_commit_msg_file,
_ok_code=[self.GIT_CONTEXT_ERROR_CODE],
_env=env)
output = gitlint(
"--staged",
"--msg-filename",
tmp_commit_msg_file,
_ok_code=[self.GIT_CONTEXT_ERROR_CODE],
_env=env,
_cwd=self.tmp_git_repo,
)
expected = "Missing git configuration: please set user.name\n"
self.assertEqualStdout(output, expected)
def test_no_git_email_set(self):
""" Ensure we print out a helpful message if user.email is not set """
"""Ensure we print out a helpful message if user.email is not set"""
tmp_commit_msg_file = self.create_tmpfile("WIP: msg-fïlename NO email test.")
env = self.create_tmp_git_config("[user]\n name = test åuthor\n")
output = gitlint("--staged", "--msg-filename", tmp_commit_msg_file,
_ok_code=[self.GIT_CONTEXT_ERROR_CODE],
_env=env)
output = gitlint(
"--staged",
"--msg-filename",
tmp_commit_msg_file,
_ok_code=[self.GIT_CONTEXT_ERROR_CODE],
_env=env,
_cwd=self.tmp_git_repo,
)
expected = "Missing git configuration: please set user.email\n"
self.assertEqualStdout(output, expected)
def test_git_errors(self):
def test_git_empty_repo(self):
# Repo has no commits: caused by `git log`
empty_git_repo = self.create_tmp_git_repo()
output = gitlint(_cwd=empty_git_repo, _tty_in=True, _ok_code=[self.GIT_CONTEXT_ERROR_CODE])
@ -186,7 +232,36 @@ class IntegrationTests(BaseTestCase):
expected = "Current branch has no commits. Gitlint requires at least one commit to function.\n"
self.assertEqualStdout(output, expected)
# Repo has no commits: caused by `git rev-parse`
output = gitlint(echo("WIP: Pïpe test."), "--staged", _cwd=empty_git_repo, _tty_in=False,
_err_to_out=True, _ok_code=[self.GIT_CONTEXT_ERROR_CODE])
def test_git_empty_repo_staged(self):
"""When repo is empty, we can still use gitlint when using --staged flag and piping a message into it"""
empty_git_repo = self.create_tmp_git_repo()
expected = (
'1: T3 Title has trailing punctuation (.): "WIP: Pïpe test."\n'
"1: T5 Title contains the word 'WIP' (case-insensitive): \"WIP: Pïpe test.\"\n"
"3: B6 Body message is missing\n"
)
output = gitlint(
echo("WIP: Pïpe test."), "--staged", _cwd=empty_git_repo, _tty_in=False, _err_to_out=True, _ok_code=[3]
)
self.assertEqualStdout(output, expected)
def test_commit_binary_file(self):
"""When committing a binary file, git shows somewhat different output in diff commands,
this test ensures gitlint deals with that correctly"""
binary_filename = self.create_simple_commit("Sïmple commit", file_contents=bytes([0x48, 0x00, 0x49, 0x00]))
output = gitlint(
"--debug",
_ok_code=1,
_cwd=self.tmp_git_repo,
)
expected_kwargs = self.get_debug_vars_last_commit()
expected_kwargs.update(
{
"changed_files": [binary_filename],
"changed_files_stats": (f"{binary_filename}: None additions, None deletions"),
}
)
expected = self.get_expected("test_gitlint/test_commit_binary_file_1", expected_kwargs)
self.assertEqualStdout(output, expected)

View file

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# pylint: disable=too-many-function-args,unexpected-keyword-arg
import os
from qa.shell import git, gitlint
@ -6,15 +5,17 @@ from qa.base import BaseTestCase
class HookTests(BaseTestCase):
""" Integration tests for gitlint commitmsg hooks"""
"""Integration tests for gitlint commitmsg hooks"""
VIOLATIONS = ['gitlint: checking commit message...\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']
VIOLATIONS = [
"gitlint: checking commit message...\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()
@ -29,16 +30,18 @@ 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 "
f"{self.tmp_git_repo}/.git/hooks/commit-msg\n")
expected_installed = (
f"Successfully installed gitlint commit-msg hook in {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 "
f"{self.tmp_git_repo}/.git/hooks/commit-msg\n")
expected_uninstalled = (
f"Successfully uninstalled gitlint commit-msg hook from {self.tmp_git_repo}/.git/hooks/commit-msg\n"
)
self.assertEqualStdout(output_uninstalled, expected_uninstalled)
super().tearDown()
@ -58,63 +61,72 @@ class HookTests(BaseTestCase):
self.response_index = (self.response_index + 1) % len(self.responses)
def test_commit_hook_no_violations(self):
test_filename = self.create_simple_commit("This ïs a title\n\nBody contënt that should work",
out=self._interact, tty_in=True)
test_filename = self.create_simple_commit(
"This ïs a title\n\nBody contënt that should work", out=self._interact, tty_in=True
)
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",
f"[master {short_hash}] This ïs a title\n",
" 1 file changed, 0 insertions(+), 0 deletions(-)\n",
f" create mode 100644 {test_filename}\n"]
self.assertListEqual(expected_output, self.githook_output)
expected_output = [
"gitlint: checking commit message...\n",
"gitlint: \x1b[32mOK\x1b[0m (no violations in commit message)\n",
f"[main {short_hash}] This ïs a title\n",
" 1 file changed, 0 insertions(+), 0 deletions(-)\n",
f" create mode 100644 {test_filename}\n",
]
for output, expected in zip(self.githook_output, expected_output):
self.assertMultiLineEqual(output.replace("\r", ""), expected.replace("\r", ""))
def test_commit_hook_continue(self):
self.responses = ["y"]
test_filename = self.create_simple_commit("WIP: This ïs a title.\nContënt on the second line",
out=self._interact, tty_in=True)
test_filename = self.create_simple_commit(
"WIP: This ïs a title.\nContënt on the second line", out=self._interact, tty_in=True
)
# Determine short commit-msg hash, needed to determine expected output
short_hash = self.get_last_commit_short_hash()
expected_output = self._violations()
expected_output += ["Continue with commit anyways (this keeps the current commit message)? " +
"[y(es)/n(no)/e(dit)] " +
f"[master {short_hash}] WIP: This ïs a title. Contënt on the second line\n",
" 1 file changed, 0 insertions(+), 0 deletions(-)\n",
f" create mode 100644 {test_filename}\n"]
expected_output += [
"Continue with commit anyways (this keeps the current commit message)? "
"[y(es)/n(no)/e(dit)] "
f"[main {short_hash}] WIP: This ïs a title. Contënt on the second line\n",
" 1 file changed, 0 insertions(+), 0 deletions(-)\n",
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):
self.assertMultiLineEqual(
output.replace('\r', ''),
expected.replace('\r', ''))
self.assertMultiLineEqual(output.replace("\r", ""), expected.replace("\r", ""))
def test_commit_hook_abort(self):
self.responses = ["n"]
test_filename = self.create_simple_commit("WIP: This ïs a title.\nContënt on the second line",
out=self._interact, ok_code=1, tty_in=True)
test_filename = self.create_simple_commit(
"WIP: This ïs a title.\nContënt on the second line", out=self._interact, ok_code=1, tty_in=True
)
git("rm", "-f", test_filename, _cwd=self.tmp_git_repo)
# Determine short commit-msg hash, needed to determine expected output
expected_output = self._violations()
expected_output += ["Continue with commit anyways (this keeps the current commit message)? " +
"[y(es)/n(no)/e(dit)] " +
"Commit aborted.\n",
"Your commit message: \n",
"-----------------------------------------------\n",
"WIP: This ïs a title.\n",
"Contënt on the second line\n",
"-----------------------------------------------\n"]
expected_output += [
"Continue with commit anyways (this keeps the current commit message)? "
"[y(es)/n(no)/e(dit)] "
"Commit aborted.\n",
"Your commit message: \n",
"-----------------------------------------------\n",
"WIP: This ïs a title.\n",
"Contënt on the second line\n",
"-----------------------------------------------\n",
]
self.assertListEqual(expected_output, self.githook_output)
for output, expected in zip(self.githook_output, expected_output):
self.assertMultiLineEqual(output.replace("\r", ""), expected.replace("\r", ""))
def test_commit_hook_edit(self):
self.responses = ["e", "y"]
env = {"EDITOR": ":"}
test_filename = self.create_simple_commit("WIP: This ïs a title.\nContënt on the second line",
out=self._interact, env=env, tty_in=True)
test_filename = self.create_simple_commit(
"WIP: This ïs a title.\nContënt on the second line", out=self._interact, env=env, tty_in=True
)
git("rm", "-f", test_filename, _cwd=self.tmp_git_repo)
short_hash = git("rev-parse", "--short", "HEAD", _cwd=self.tmp_git_repo, _tty_in=True).replace("\n", "")
@ -122,23 +134,23 @@ class HookTests(BaseTestCase):
# Determine short commit-msg hash, needed to determine expected output
expected_output = self._violations()
expected_output += ['Continue with commit anyways (this keeps the current commit message)? ' +
'[y(es)/n(no)/e(dit)] ' + self._violations()[0]]
expected_output += [
"Continue with commit anyways (this keeps the current commit message)? [y(es)/n(no)/e(dit)] "
+ self._violations()[0]
]
expected_output += self._violations()[1:]
expected_output += ['Continue with commit anyways (this keeps the current commit message)? ' +
"[y(es)/n(no)/e(dit)] " +
f"[master {short_hash}] WIP: This ïs a title. Contënt on the second line\n",
" 1 file changed, 0 insertions(+), 0 deletions(-)\n",
f" create mode 100644 {test_filename}\n"]
expected_output += [
"Continue with commit anyways (this keeps the current commit message)? [y(es)/n(no)/e(dit)] "
f"[main {short_hash}] WIP: This ïs a title. Contënt on the second line\n",
" 1 file changed, 0 insertions(+), 0 deletions(-)\n",
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):
self.assertMultiLineEqual(
output.replace('\r', ''),
expected.replace('\r', ''))
self.assertMultiLineEqual(output.replace("\r", ""), expected.replace("\r", ""))
def test_commit_hook_worktree(self):
""" Tests that hook installation and un-installation also work in git worktrees.
"""Tests that hook installation and un-installation also work in git worktrees.
Test steps:
```sh
git init <tmpdir>

View file

@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
from qa.shell import gitlint
from qa.base import BaseTestCase
class NamedRuleTests(BaseTestCase):
""" Integration tests for named rules."""
"""Integration tests for named rules."""
def test_named_rule(self):
commit_msg = "WIP: thåt dûr bår\n\nSïmple commit body"
@ -18,6 +17,7 @@ class NamedRuleTests(BaseTestCase):
self.create_simple_commit(commit_msg)
config_path = self.get_sample_path("config/named-user-rules")
extra_path = self.get_sample_path("user_rules/extra")
output = gitlint("--extra-path", extra_path, "--config", config_path, _cwd=self.tmp_git_repo, _tty_in=True,
_ok_code=[9])
output = gitlint(
"--extra-path", extra_path, "--config", config_path, _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[9]
)
self.assertEqualStdout(output, self.get_expected("test_named_rules/test_named_user_rule_1"))

View file

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# pylint: disable=too-many-function-args,unexpected-keyword-arg
import io
import subprocess
from qa.shell import echo, gitlint
from qa.base import BaseTestCase
@ -8,25 +6,24 @@ from qa.utils import DEFAULT_ENCODING
class StdInTests(BaseTestCase):
""" Integration tests for various STDIN scenarios for gitlint """
"""Integration tests for various STDIN scenarios for gitlint"""
def test_stdin_pipe(self):
""" Test piping input into gitlint.
This is the equivalent of doing:
$ echo "foo" | gitlint
"""Test piping input into gitlint.
This is the equivalent of doing:
$ echo "foo" | gitlint
"""
# NOTE: There is no use in testing this with _tty_in=True, because if you pipe something into a command
# there never is a TTY connected to stdin (per definition). We're setting _tty_in=False here to be explicit
# but note that this is always true when piping something into a command.
output = gitlint(echo("WIP: Pïpe test."),
_cwd=self.tmp_git_repo, _tty_in=False, _err_to_out=True, _ok_code=[3])
output = gitlint(echo("WIP: Pïpe test."), _cwd=self.tmp_git_repo, _tty_in=False, _err_to_out=True, _ok_code=[3])
self.assertEqualStdout(output, self.get_expected("test_stdin/test_stdin_pipe_1"))
def test_stdin_pipe_empty(self):
""" Test the scenario where no TTY is attached and nothing is piped into gitlint. This occurs in
CI runners like Jenkins and Gitlab, see https://github.com/jorisroovers/gitlint/issues/42 for details.
This is the equivalent of doing:
$ echo -n "" | gitlint
"""Test the scenario where no TTY is attached and nothing is piped into gitlint. This occurs in
CI runners like Jenkins and Gitlab, see https://github.com/jorisroovers/gitlint/issues/42 for details.
This is the equivalent of doing:
$ echo -n "" | gitlint
"""
commit_msg = "WIP: This ïs a title.\nContent on the sëcond line"
self.create_simple_commit(commit_msg)
@ -39,18 +36,18 @@ class StdInTests(BaseTestCase):
self.assertEqual(output, self.get_expected("test_stdin/test_stdin_pipe_empty_1"))
def test_stdin_file(self):
""" Test the scenario where STDIN is a regular file (stat.S_ISREG = True)
This is the equivalent of doing:
$ gitlint < myfile
"""Test the scenario where STDIN is a regular file (stat.S_ISREG = True)
This is the equivalent of doing:
$ gitlint < myfile
"""
tmp_commit_msg_file = self.create_tmpfile("WIP: STDIN ïs a file test.")
with io.open(tmp_commit_msg_file, encoding=DEFAULT_ENCODING) as file_handle:
with open(tmp_commit_msg_file, encoding=DEFAULT_ENCODING) as file_handle:
# 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.
with subprocess.Popen("gitlint", stdin=file_handle, cwd=self.tmp_git_repo,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as p:
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"))

View file

@ -1,14 +1,13 @@
# -*- coding: utf-8 -*-
# pylint: disable=too-many-function-args,unexpected-keyword-arg
from qa.shell import gitlint
from qa.base import BaseTestCase
class UserDefinedRuleTests(BaseTestCase):
""" Integration tests for user-defined rules."""
"""Integration tests for user-defined rules."""
def test_user_defined_rules_examples1(self):
""" Test the user defined rules in the top-level `examples/` directory """
"""Test the user defined rules in the top-level `examples/` directory"""
extra_path = self.get_example_path()
commit_msg = "WIP: Thi$ is å title\nContent on the second line"
self.create_simple_commit(commit_msg)
@ -16,7 +15,7 @@ class UserDefinedRuleTests(BaseTestCase):
self.assertEqualStdout(output, self.get_expected("test_user_defined/test_user_defined_rules_examples_1"))
def test_user_defined_rules_examples2(self):
""" Test the user defined rules in the top-level `examples/` directory """
"""Test the user defined rules in the top-level `examples/` directory"""
extra_path = self.get_example_path()
commit_msg = "Release: Thi$ is å title\nContent on the second line\n$This line is ignored \nThis isn't\t\n"
self.create_simple_commit(commit_msg)
@ -24,12 +23,19 @@ class UserDefinedRuleTests(BaseTestCase):
self.assertEqualStdout(output, self.get_expected("test_user_defined/test_user_defined_rules_examples_2"))
def test_user_defined_rules_examples_with_config(self):
""" Test the user defined rules in the top-level `examples/` directory """
"""Test the user defined rules in the top-level `examples/` directory"""
extra_path = self.get_example_path()
commit_msg = "WIP: Thi$ is å title\nContent on the second line"
self.create_simple_commit(commit_msg)
output = gitlint("--extra-path", extra_path, "-c", "body-max-line-count.max-line-count=1",
_cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[6])
output = gitlint(
"--extra-path",
extra_path,
"-c",
"body-max-line-count.max-line-count=1",
_cwd=self.tmp_git_repo,
_tty_in=True,
_ok_code=[6],
)
expected_path = "test_user_defined/test_user_defined_rules_examples_with_config_1"
self.assertEqualStdout(output, self.get_expected(expected_path))
@ -38,12 +44,15 @@ class UserDefinedRuleTests(BaseTestCase):
commit_msg = "WIP: Thi$ is å title\nContent on the second line"
self.create_simple_commit(commit_msg)
output = gitlint("--extra-path", extra_path, _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[9])
self.assertEqualStdout(output, self.get_expected("test_user_defined/test_user_defined_rules_extra_1",
{'repo-path': self.tmp_git_repo}))
self.assertEqualStdout(
output,
self.get_expected("test_user_defined/test_user_defined_rules_extra_1", {"repo-path": self.tmp_git_repo}),
)
def test_invalid_user_defined_rules(self):
extra_path = self.get_sample_path("user_rules/incorrect_linerule")
self.create_simple_commit("WIP: test")
output = gitlint("--extra-path", extra_path, _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[255])
self.assertEqualStdout(output,
"Config Error: User-defined rule class 'MyUserLineRule' must have a 'validate' method\n")
self.assertEqualStdout(
output, "Config Error: User-defined rule class 'MyUserLineRule' must have a 'validate' method\n"
)

View file

@ -22,7 +22,7 @@ PLATFORM_IS_WINDOWS = platform_is_windows()
def use_sh_library():
gitlint_use_sh_lib_env = os.environ.get('GITLINT_QA_USE_SH_LIB', None)
gitlint_use_sh_lib_env = os.environ.get("GITLINT_QA_USE_SH_LIB", None)
if gitlint_use_sh_lib_env:
return gitlint_use_sh_lib_env == "1"
return not PLATFORM_IS_WINDOWS
@ -35,8 +35,8 @@ USE_SH_LIB = use_sh_library()
def getpreferredencoding():
""" Modified version of local.getpreferredencoding() that takes into account LC_ALL, LC_CTYPE, LANG env vars
on windows and falls back to UTF-8. """
"""Modified version of local.getpreferredencoding() that takes into account LC_ALL, LC_CTYPE, LANG env vars
on windows and falls back to UTF-8."""
default_encoding = locale.getpreferredencoding() or "UTF-8"
# On Windows, we mimic git/linux by trying to read the LC_ALL, LC_CTYPE, LANG env vars manually
@ -51,7 +51,7 @@ def getpreferredencoding():
# If encoding contains a dot: split and use second part, otherwise use everything
dot_index = encoding.find(".")
if dot_index != -1:
default_encoding = encoding[dot_index + 1:]
default_encoding = encoding[dot_index + 1 :]
else:
default_encoding = encoding
break