Adding upstream version 0.19.1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
9b31168df6
commit
528df549e3
111 changed files with 2058 additions and 1676 deletions
43
qa/base.py
43
qa/base.py
|
@ -1,20 +1,16 @@
|
|||
# pylint: disable=bad-option-value,unidiomatic-typecheck,undefined-variable,no-else-return,
|
||||
# pylint: disable=too-many-function-args,unexpected-keyword-arg
|
||||
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
from datetime import datetime
|
||||
from uuid import uuid4
|
||||
from datetime import datetime, timezone
|
||||
from unittest import TestCase
|
||||
from uuid import uuid4
|
||||
|
||||
import arrow
|
||||
|
||||
|
||||
from qa.shell import git, gitlint, RunningCommand
|
||||
from qa.utils import DEFAULT_ENCODING
|
||||
from qa.shell import RunningCommand, git, gitlint
|
||||
from qa.utils import FILE_ENCODING, PLATFORM_IS_WINDOWS, TERMINAL_ENCODING
|
||||
|
||||
|
||||
class BaseTestCase(TestCase):
|
||||
|
@ -40,18 +36,19 @@ class BaseTestCase(TestCase):
|
|||
for tmpfile in self.tmpfiles:
|
||||
os.remove(tmpfile)
|
||||
for repo in self.tmp_git_repos:
|
||||
shutil.rmtree(repo)
|
||||
# On windows we need to ignore errors because git might still be holding on to some files
|
||||
shutil.rmtree(repo, ignore_errors=PLATFORM_IS_WINDOWS)
|
||||
|
||||
def assertEqualStdout(self, output, expected): # pylint: disable=invalid-name
|
||||
def assertEqualStdout(self, output, expected):
|
||||
self.assertIsInstance(output, RunningCommand)
|
||||
output = output.stdout.decode(DEFAULT_ENCODING)
|
||||
output = output.stdout.decode(TERMINAL_ENCODING)
|
||||
output = output.replace("\r", "")
|
||||
self.assertMultiLineEqual(output, expected)
|
||||
|
||||
@staticmethod
|
||||
def generate_temp_path():
|
||||
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S-%f")
|
||||
return os.path.realpath(f"/tmp/gitlint-test-{timestamp}")
|
||||
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S-%f")
|
||||
return os.path.realpath(f"/tmp/gitlint-test-{timestamp}") # noqa
|
||||
|
||||
def create_tmp_git_repo(self):
|
||||
"""Creates a temporary git repository and returns its directory path"""
|
||||
|
@ -72,6 +69,9 @@ class BaseTestCase(TestCase):
|
|||
# http://stackoverflow.com/questions/5581857/git-and-the-umlaut-problem-on-mac-os-x
|
||||
git("config", "core.precomposeunicode", "true", _cwd=tmp_git_repo)
|
||||
|
||||
# Git now does commit message cleanup by default (e.g. removing trailing whitespace), disable that for testing
|
||||
git("config", "commit.cleanup", "verbatim", _cwd=tmp_git_repo)
|
||||
|
||||
return tmp_git_repo
|
||||
|
||||
@staticmethod
|
||||
|
@ -84,13 +84,12 @@ class BaseTestCase(TestCase):
|
|||
if isinstance(content, bytes):
|
||||
open_kwargs = {"mode": "wb"}
|
||||
else:
|
||||
open_kwargs = {"mode": "w", "encoding": DEFAULT_ENCODING}
|
||||
open_kwargs = {"mode": "w", "encoding": FILE_ENCODING}
|
||||
|
||||
with open(full_path, **open_kwargs) as f: # pylint: disable=unspecified-encoding
|
||||
with open(full_path, **open_kwargs) as f:
|
||||
f.write(content)
|
||||
else:
|
||||
# pylint: disable=consider-using-with
|
||||
open(full_path, "a", encoding=DEFAULT_ENCODING).close()
|
||||
open(full_path, "a", encoding=FILE_ENCODING).close() # noqa: SIM115 (Use context handler for opening files)
|
||||
|
||||
return test_filename
|
||||
|
||||
|
@ -150,9 +149,9 @@ class BaseTestCase(TestCase):
|
|||
if isinstance(content, bytes):
|
||||
open_kwargs = {"mode": "wb"}
|
||||
else:
|
||||
open_kwargs = {"mode": "w", "encoding": DEFAULT_ENCODING}
|
||||
open_kwargs = {"mode": "w", "encoding": FILE_ENCODING}
|
||||
|
||||
with open(tmpfile, **open_kwargs) as f: # pylint: disable=unspecified-encoding
|
||||
with open(tmpfile, **open_kwargs) as f:
|
||||
f.write(content)
|
||||
|
||||
return tmpfilepath
|
||||
|
@ -181,7 +180,8 @@ class BaseTestCase(TestCase):
|
|||
specified by variable_dict."""
|
||||
expected_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "expected")
|
||||
expected_path = os.path.join(expected_dir, filename)
|
||||
with open(expected_path, encoding=DEFAULT_ENCODING) as file:
|
||||
# Expected files are UTF-8 encoded (not dependent on the system's default encoding)
|
||||
with open(expected_path, encoding=FILE_ENCODING) as file:
|
||||
expected = file.read()
|
||||
|
||||
if variable_dict:
|
||||
|
@ -199,7 +199,8 @@ class BaseTestCase(TestCase):
|
|||
"git_version": expected_git_version,
|
||||
"gitlint_version": expected_gitlint_version,
|
||||
"GITLINT_USE_SH_LIB": BaseTestCase.GITLINT_USE_SH_LIB,
|
||||
"DEFAULT_ENCODING": DEFAULT_ENCODING,
|
||||
"TERMINAL_ENCODING": TERMINAL_ENCODING,
|
||||
"FILE_ENCODING": FILE_ENCODING,
|
||||
}
|
||||
|
||||
def get_debug_vars_last_commit(self, git_repo=None):
|
||||
|
|
|
@ -5,7 +5,8 @@ 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 TERMINAL_ENCODING: {TERMINAL_ENCODING}
|
||||
DEBUG: gitlint.cli FILE_ENCODING: {FILE_ENCODING}
|
||||
DEBUG: gitlint.cli Configuration
|
||||
config-path: None
|
||||
[GENERAL]
|
||||
|
|
|
@ -5,7 +5,8 @@ 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 TERMINAL_ENCODING: {TERMINAL_ENCODING}
|
||||
DEBUG: gitlint.cli FILE_ENCODING: {FILE_ENCODING}
|
||||
DEBUG: gitlint.cli Configuration
|
||||
config-path: None
|
||||
[GENERAL]
|
||||
|
|
|
@ -5,7 +5,8 @@ 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 TERMINAL_ENCODING: {TERMINAL_ENCODING}
|
||||
DEBUG: gitlint.cli FILE_ENCODING: {FILE_ENCODING}
|
||||
DEBUG: gitlint.cli Configuration
|
||||
config-path: None
|
||||
[GENERAL]
|
||||
|
|
|
@ -5,7 +5,8 @@ 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 TERMINAL_ENCODING: {TERMINAL_ENCODING}
|
||||
DEBUG: gitlint.cli FILE_ENCODING: {FILE_ENCODING}
|
||||
DEBUG: gitlint.cli Configuration
|
||||
config-path: None
|
||||
[GENERAL]
|
||||
|
|
|
@ -5,7 +5,8 @@ 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 TERMINAL_ENCODING: {TERMINAL_ENCODING}
|
||||
DEBUG: gitlint.cli FILE_ENCODING: {FILE_ENCODING}
|
||||
DEBUG: gitlint.cli Configuration
|
||||
config-path: {config_path}
|
||||
[GENERAL]
|
||||
|
|
|
@ -5,7 +5,8 @@ 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 TERMINAL_ENCODING: {TERMINAL_ENCODING}
|
||||
DEBUG: gitlint.cli FILE_ENCODING: {FILE_ENCODING}
|
||||
DEBUG: gitlint.cli Configuration
|
||||
config-path: None
|
||||
[GENERAL]
|
||||
|
|
3
qa/expected/test_rules/test_ignore_rules_1
Normal file
3
qa/expected/test_rules/test_ignore_rules_1
Normal file
|
@ -0,0 +1,3 @@
|
|||
1: T5 Title contains the word 'WIP' (case-insensitive): "WIP: Commït Tïtle"
|
||||
3: B3 Line contains hard tab characters (\t): "Sïmple commit body"
|
||||
4: B2 Line has trailing whitespace: "Anōther Line "
|
2
qa/expected/test_rules/test_ignore_rules_2
Normal file
2
qa/expected/test_rules/test_ignore_rules_2
Normal file
|
@ -0,0 +1,2 @@
|
|||
1: T5 Title contains the word 'WIP' (case-insensitive): "WIP: Commït Tïtle"
|
||||
3: B3 Line contains hard tab characters (\t): "Sïmple commit body"
|
2
qa/expected/test_rules/test_match_regex_rules_1
Normal file
2
qa/expected/test_rules/test_match_regex_rules_1
Normal file
|
@ -0,0 +1,2 @@
|
|||
1: T7 Title does not match regex (foo): "Thåt dûr bår"
|
||||
4: B8 Body does not match regex (bar)
|
|
@ -2,3 +2,4 @@
|
|||
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
|
||||
3: B3 Line contains hard tab characters (\t)
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
sh==1.14.3
|
||||
pytest==7.0.1;
|
||||
arrow==1.2.3;
|
||||
gitlint # no version as you want to test the currently installed version
|
|
@ -1,5 +1,5 @@
|
|||
from gitlint.rules import CommitRule, RuleViolation, ConfigurationRule
|
||||
from gitlint.options import IntOption, StrOption, ListOption
|
||||
from gitlint.options import IntOption, ListOption, StrOption
|
||||
from gitlint.rules import CommitRule, ConfigurationRule, RuleViolation
|
||||
|
||||
|
||||
class GitContextRule(CommitRule):
|
||||
|
@ -64,9 +64,9 @@ class ConfigurableCommitRule(CommitRule):
|
|||
|
||||
def validate(self, _):
|
||||
violations = [
|
||||
RuleViolation(self.id, f"int-öption: {self.options[u'int-öption'].value}", line_nr=1),
|
||||
RuleViolation(self.id, f"str-öption: {self.options[u'str-öption'].value}", line_nr=1),
|
||||
RuleViolation(self.id, f"list-öption: {self.options[u'list-öption'].value}", line_nr=1),
|
||||
RuleViolation(self.id, f"int-öption: {self.options['int-öption'].value}", line_nr=1),
|
||||
RuleViolation(self.id, f"str-öption: {self.options['str-öption'].value}", line_nr=1),
|
||||
RuleViolation(self.id, f"list-öption: {self.options['list-öption'].value}", line_nr=1),
|
||||
]
|
||||
|
||||
return violations
|
||||
|
|
83
qa/shell.py
83
qa/shell.py
|
@ -2,24 +2,31 @@
|
|||
# on gitlint internals for our integration testing framework.
|
||||
|
||||
import subprocess
|
||||
from qa.utils import USE_SH_LIB, DEFAULT_ENCODING
|
||||
|
||||
from qa.utils import TERMINAL_ENCODING, USE_SH_LIB
|
||||
|
||||
if USE_SH_LIB:
|
||||
from sh import git, echo, gitlint # pylint: disable=unused-import,no-name-in-module,import-error
|
||||
from sh import (
|
||||
echo,
|
||||
git,
|
||||
gitlint,
|
||||
)
|
||||
|
||||
gitlint = gitlint.bake(_unify_ttys=True, _tty_in=True) # pylint: disable=invalid-name
|
||||
gitlint = gitlint.bake(_unify_ttys=True, _tty_in=True)
|
||||
|
||||
# import exceptions separately, this makes it a little easier to mock them out in the unit tests
|
||||
from sh import CommandNotFound, ErrorReturnCode, RunningCommand # pylint: disable=import-error
|
||||
from sh import (
|
||||
CommandNotFound,
|
||||
ErrorReturnCode,
|
||||
RunningCommand,
|
||||
)
|
||||
else:
|
||||
|
||||
class CommandNotFound(Exception):
|
||||
"""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
|
||||
|
@ -29,18 +36,35 @@ else:
|
|||
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.
|
||||
self.stdout = stdout + stderr.decode(DEFAULT_ENCODING)
|
||||
self.stderr = stderr
|
||||
self._stdout = stdout + stderr
|
||||
self._stderr = stderr
|
||||
self.exit_code = exitcode
|
||||
|
||||
def __str__(self):
|
||||
return self.stdout.decode(TERMINAL_ENCODING)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.stdout
|
||||
|
||||
@property
|
||||
def stdout(self):
|
||||
return self._stdout
|
||||
|
||||
@property
|
||||
def stderr(self):
|
||||
return self._stderr
|
||||
|
||||
def __getattr__(self, p):
|
||||
# https://github.com/amoffat/sh/blob/e0ed8e244e9d973ef4e0749b2b3c2695e7b5255b/sh.py#L952=
|
||||
_unicode_methods = set(dir(str())) # noqa
|
||||
if p in _unicode_methods:
|
||||
return getattr(str(self), p)
|
||||
|
||||
raise AttributeError
|
||||
|
||||
class ErrorReturnCode(ShResult, Exception):
|
||||
"""ShResult subclass for unexpected results (acts as an exception)."""
|
||||
|
||||
pass
|
||||
|
||||
def git(*command_parts, **kwargs):
|
||||
return run_command("git", *command_parts, **kwargs)
|
||||
|
||||
|
@ -51,31 +75,36 @@ else:
|
|||
return run_command("gitlint", *command_parts, **kwargs)
|
||||
|
||||
def run_command(command, *args, **kwargs):
|
||||
args = [command] + list(args)
|
||||
result = _exec(*args, **kwargs)
|
||||
# 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:
|
||||
return result
|
||||
return str(result)
|
||||
args = [command, *list(args)]
|
||||
return _exec(*args, **kwargs)
|
||||
|
||||
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": subprocess.PIPE,
|
||||
"stderr": subprocess.PIPE,
|
||||
"stdin": subprocess.PIPE,
|
||||
"shell": kwargs.get("_tty_out", False),
|
||||
"cwd": kwargs.get("_cwd", None),
|
||||
"env": kwargs.get("_env", None),
|
||||
}
|
||||
|
||||
stdin_input = None
|
||||
if len(args) > 1 and isinstance(args[1], ShResult):
|
||||
stdin_input = args[1].stdout
|
||||
# pop args[1] from the array and use it as stdin
|
||||
args = list(args)
|
||||
args.pop(1)
|
||||
popen_kwargs["stdin"] = subprocess.PIPE
|
||||
|
||||
try:
|
||||
with subprocess.Popen(args, **popen_kwargs) as p:
|
||||
result = p.communicate()
|
||||
result = p.communicate(stdin_input)
|
||||
|
||||
except FileNotFoundError as exc:
|
||||
raise CommandNotFound from exc
|
||||
|
||||
exit_code = p.returncode
|
||||
stdout = result[0].decode(DEFAULT_ENCODING)
|
||||
stdout = result[0]
|
||||
stderr = result[1] # 'sh' does not decode the stderr bytes to unicode
|
||||
full_cmd = "" if args is None else " ".join(args)
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
# pylint: disable=too-many-function-args,unexpected-keyword-arg
|
||||
import re
|
||||
|
||||
import arrow
|
||||
|
||||
from qa.shell import echo, git, gitlint
|
||||
from qa.base import BaseTestCase
|
||||
from qa.shell import echo, git, gitlint
|
||||
|
||||
|
||||
class CommitsTests(BaseTestCase):
|
||||
|
@ -111,6 +110,11 @@ class CommitsTests(BaseTestCase):
|
|||
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)
|
||||
|
@ -129,7 +133,7 @@ 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
|
||||
"""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
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
# pylint: disable=too-many-function-args,unexpected-keyword-arg
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
from qa.shell import gitlint
|
||||
from qa.base import BaseTestCase
|
||||
from qa.utils import DEFAULT_ENCODING
|
||||
from qa.shell import gitlint
|
||||
|
||||
|
||||
class ConfigTests(BaseTestCase):
|
||||
|
@ -69,7 +67,7 @@ class ConfigTests(BaseTestCase):
|
|||
"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")
|
||||
config_path = self.get_sample_path(os.path.join("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)
|
||||
|
@ -128,7 +126,7 @@ class ConfigTests(BaseTestCase):
|
|||
# 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
|
||||
p = re.compile("Date: (.*)\n", re.UNICODE | re.MULTILINE)
|
||||
result = p.search(output.stdout.decode(DEFAULT_ENCODING))
|
||||
result = p.search(str(output))
|
||||
date = result.group(1).strip()
|
||||
expected_kwargs.update({"date": date})
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# pylint: disable=
|
||||
from qa.shell import gitlint
|
||||
from qa.base import BaseTestCase
|
||||
from qa.shell import gitlint
|
||||
|
||||
|
||||
class ContribRuleTests(BaseTestCase):
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# pylint: disable=too-many-function-args,unexpected-keyword-arg
|
||||
import os
|
||||
from qa.shell import echo, git, gitlint
|
||||
|
||||
from qa.base import BaseTestCase
|
||||
from qa.utils import DEFAULT_ENCODING
|
||||
from qa.shell import echo, git, gitlint
|
||||
from qa.utils import FILE_ENCODING
|
||||
|
||||
|
||||
class IntegrationTests(BaseTestCase):
|
||||
|
@ -58,7 +58,7 @@ class IntegrationTests(BaseTestCase):
|
|||
self.assertEqualStdout(output, expected)
|
||||
|
||||
# Make a small modification to the commit and commit it using fixup commit
|
||||
with 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=FILE_ENCODING) as fh:
|
||||
fh.write("Appending söme stuff\n")
|
||||
|
||||
git("add", test_filename, _cwd=self.tmp_git_repo)
|
||||
|
@ -87,7 +87,7 @@ class IntegrationTests(BaseTestCase):
|
|||
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:
|
||||
with open(os.path.join(self.tmp_git_repo, test_filename), "a", encoding=FILE_ENCODING) as fh:
|
||||
fh.write("Appending söme stuff\n")
|
||||
|
||||
git("add", test_filename, _cwd=self.tmp_git_repo)
|
||||
|
@ -133,7 +133,7 @@ class IntegrationTests(BaseTestCase):
|
|||
self.assertEqualStdout(output, expected)
|
||||
|
||||
# Make a small modification to the commit and commit it using squash commit
|
||||
with 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=FILE_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
|
||||
|
@ -252,7 +252,7 @@ class IntegrationTests(BaseTestCase):
|
|||
binary_filename = self.create_simple_commit("Sïmple commit", file_contents=bytes([0x48, 0x00, 0x49, 0x00]))
|
||||
output = gitlint(
|
||||
"--debug",
|
||||
_ok_code=1,
|
||||
_ok_code=[1],
|
||||
_cwd=self.tmp_git_repo,
|
||||
)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# pylint: disable=too-many-function-args,unexpected-keyword-arg
|
||||
import os
|
||||
from qa.shell import git, gitlint
|
||||
|
||||
from qa.base import BaseTestCase
|
||||
from qa.shell import git, gitlint
|
||||
|
||||
|
||||
class HookTests(BaseTestCase):
|
||||
|
@ -30,18 +30,16 @@ class HookTests(BaseTestCase):
|
|||
|
||||
# install git commit-msg hook and assert output
|
||||
output_installed = gitlint("install-hook", _cwd=self.tmp_git_repo)
|
||||
expected_installed = (
|
||||
f"Successfully installed gitlint commit-msg hook in {self.tmp_git_repo}/.git/hooks/commit-msg\n"
|
||||
)
|
||||
commit_msg_hook_path = os.path.join(self.tmp_git_repo, ".git", "hooks", "commit-msg")
|
||||
expected_installed = f"Successfully installed gitlint commit-msg hook in {commit_msg_hook_path}\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 = (
|
||||
f"Successfully uninstalled gitlint commit-msg hook from {self.tmp_git_repo}/.git/hooks/commit-msg\n"
|
||||
)
|
||||
commit_msg_hook_path = os.path.join(self.tmp_git_repo, ".git", "hooks", "commit-msg")
|
||||
expected_uninstalled = f"Successfully uninstalled gitlint commit-msg hook from {commit_msg_hook_path}\n"
|
||||
|
||||
self.assertEqualStdout(output_uninstalled, expected_uninstalled)
|
||||
super().tearDown()
|
||||
|
@ -171,10 +169,10 @@ class HookTests(BaseTestCase):
|
|||
|
||||
output_installed = gitlint("install-hook", _cwd=worktree_dir)
|
||||
expected_hook_path = os.path.join(tmp_git_repo, ".git", "hooks", "commit-msg")
|
||||
expected_msg = f"Successfully installed gitlint commit-msg hook in {expected_hook_path}\r\n"
|
||||
self.assertEqual(output_installed, expected_msg)
|
||||
expected_msg = f"Successfully installed gitlint commit-msg hook in {expected_hook_path}\n"
|
||||
self.assertEqualStdout(output_installed, expected_msg)
|
||||
|
||||
output_uninstalled = gitlint("uninstall-hook", _cwd=worktree_dir)
|
||||
expected_hook_path = os.path.join(tmp_git_repo, ".git", "hooks", "commit-msg")
|
||||
expected_msg = f"Successfully uninstalled gitlint commit-msg hook from {expected_hook_path}\r\n"
|
||||
self.assertEqual(output_uninstalled, expected_msg)
|
||||
expected_msg = f"Successfully uninstalled gitlint commit-msg hook from {expected_hook_path}\n"
|
||||
self.assertEqualStdout(output_uninstalled, expected_msg)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from qa.shell import gitlint
|
||||
from qa.base import BaseTestCase
|
||||
from qa.shell import gitlint
|
||||
|
||||
|
||||
class NamedRuleTests(BaseTestCase):
|
||||
|
|
61
qa/test_rules.py
Normal file
61
qa/test_rules.py
Normal file
|
@ -0,0 +1,61 @@
|
|||
from qa.base import BaseTestCase
|
||||
from qa.shell import gitlint
|
||||
|
||||
|
||||
class RuleTests(BaseTestCase):
|
||||
"""
|
||||
Tests for specific rules that are worth testing as integration tests.
|
||||
It's not a goal to test every edge case of each rule, that's what the unit tests do.
|
||||
"""
|
||||
|
||||
def test_match_regex_rules(self):
|
||||
"""
|
||||
Test that T7 (title-match-regex) and B8 (body-match-regex) work as expected.
|
||||
By default, these rules don't do anything, only when setting a custom regex will they run.
|
||||
"""
|
||||
|
||||
commit_msg = "Thåt dûr bår\n\nSïmple commit message body"
|
||||
self.create_simple_commit(commit_msg)
|
||||
|
||||
# Assert violations when T7 and B8 regexes don't match
|
||||
output = gitlint("-c", "T7.regex=foo", "-c", "B8.regex=bar", _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[2])
|
||||
self.assertEqualStdout(output, self.get_expected("test_rules/test_match_regex_rules_1"))
|
||||
|
||||
# Assert no violations when T7 and B8 regexes do match
|
||||
output = gitlint("-c", "T7.regex=^Thåt", "-c", "B8.regex=commit message", _cwd=self.tmp_git_repo, _tty_in=True)
|
||||
self.assertEqualStdout(output, "")
|
||||
|
||||
def test_ignore_rules(self):
|
||||
"""
|
||||
Test that ignore rules work as expected:
|
||||
ignore-by-title, ignore-by-body, ignore-by-author-name, ignore-body-lines
|
||||
By default, these rules don't do anything, only when setting a custom regex will they run.
|
||||
"""
|
||||
commit_msg = "WIP: Commït Tïtle\n\nSïmple commit\tbody\nAnōther Line \nLåst Line"
|
||||
self.create_simple_commit(commit_msg)
|
||||
|
||||
# Assert violations when not ignoring anything
|
||||
output = gitlint(_cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[3])
|
||||
self.assertEqualStdout(output, self.get_expected("test_rules/test_ignore_rules_1"))
|
||||
|
||||
# Simple convenience function that passes in common arguments for this test
|
||||
def invoke_gitlint(*args, **kwargs):
|
||||
return gitlint(
|
||||
*args, "-c", "general.regex-style-search=True", **kwargs, _cwd=self.tmp_git_repo, _tty_in=True
|
||||
)
|
||||
|
||||
# ignore-by-title
|
||||
output = invoke_gitlint("-c", "ignore-by-title.regex=Commït")
|
||||
self.assertEqualStdout(output, "")
|
||||
|
||||
# ignore-by-body
|
||||
output = invoke_gitlint("-c", "ignore-by-body.regex=Anōther Line")
|
||||
self.assertEqualStdout(output, "")
|
||||
|
||||
# ignore-by-author-name
|
||||
output = invoke_gitlint("-c", "ignore-by-author-name.regex=gitlint-test-user")
|
||||
self.assertEqualStdout(output, "")
|
||||
|
||||
# ignore-body-lines
|
||||
output = invoke_gitlint("-c", "ignore-body-lines.regex=^Anōther", _ok_code=[2])
|
||||
self.assertEqualStdout(output, self.get_expected("test_rules/test_ignore_rules_2"))
|
|
@ -1,8 +1,8 @@
|
|||
# pylint: disable=too-many-function-args,unexpected-keyword-arg
|
||||
import subprocess
|
||||
from qa.shell import echo, gitlint
|
||||
|
||||
from qa.base import BaseTestCase
|
||||
from qa.utils import DEFAULT_ENCODING
|
||||
from qa.shell import echo, gitlint
|
||||
from qa.utils import FILE_ENCODING, TERMINAL_ENCODING
|
||||
|
||||
|
||||
class StdInTests(BaseTestCase):
|
||||
|
@ -33,7 +33,7 @@ class StdInTests(BaseTestCase):
|
|||
# http://amoffat.github.io/sh/sections/special_arguments.html?highlight=_tty_in#err-to-out
|
||||
output = gitlint(echo("-n", ""), _cwd=self.tmp_git_repo, _tty_in=False, _err_to_out=True, _ok_code=[3])
|
||||
|
||||
self.assertEqual(output, self.get_expected("test_stdin/test_stdin_pipe_empty_1"))
|
||||
self.assertEqualStdout(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)
|
||||
|
@ -42,7 +42,7 @@ class StdInTests(BaseTestCase):
|
|||
"""
|
||||
tmp_commit_msg_file = self.create_tmpfile("WIP: STDIN ïs a file test.")
|
||||
|
||||
with open(tmp_commit_msg_file, encoding=DEFAULT_ENCODING) as file_handle:
|
||||
with open(tmp_commit_msg_file, encoding=FILE_ENCODING) as file_handle: # noqa: SIM117
|
||||
# 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.
|
||||
|
@ -50,4 +50,4 @@ class StdInTests(BaseTestCase):
|
|||
"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"))
|
||||
self.assertEqual(output.decode(TERMINAL_ENCODING), self.get_expected("test_stdin/test_stdin_file_1"))
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# pylint: disable=too-many-function-args,unexpected-keyword-arg
|
||||
from qa.shell import gitlint
|
||||
from qa.base import BaseTestCase
|
||||
from qa.shell import gitlint
|
||||
|
||||
|
||||
class UserDefinedRuleTests(BaseTestCase):
|
||||
|
@ -19,7 +18,7 @@ class UserDefinedRuleTests(BaseTestCase):
|
|||
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)
|
||||
output = gitlint("--extra-path", extra_path, _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[4])
|
||||
output = gitlint("--extra-path", extra_path, _cwd=self.tmp_git_repo, _tty_in=True, _ok_code=[5])
|
||||
self.assertEqualStdout(output, self.get_expected("test_user_defined/test_user_defined_rules_examples_2"))
|
||||
|
||||
def test_user_defined_rules_examples_with_config(self):
|
||||
|
|
42
qa/utils.py
42
qa/utils.py
|
@ -1,8 +1,6 @@
|
|||
# pylint: disable=bad-option-value,unidiomatic-typecheck,undefined-variable,no-else-return
|
||||
import platform
|
||||
import os
|
||||
|
||||
import locale
|
||||
import os
|
||||
import platform
|
||||
|
||||
########################################################################################################################
|
||||
# PLATFORM_IS_WINDOWS
|
||||
|
@ -31,32 +29,20 @@ def use_sh_library():
|
|||
USE_SH_LIB = use_sh_library()
|
||||
|
||||
########################################################################################################################
|
||||
# DEFAULT_ENCODING
|
||||
# TERMINAL_ENCODING
|
||||
# Encoding for reading gitlint command output
|
||||
|
||||
|
||||
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."""
|
||||
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
|
||||
# (on Linux/MacOS the `getpreferredencoding()` call will take care of this).
|
||||
# We fallback to UTF-8
|
||||
if PLATFORM_IS_WINDOWS:
|
||||
default_encoding = "UTF-8"
|
||||
for env_var in ["LC_ALL", "LC_CTYPE", "LANG"]:
|
||||
encoding = os.environ.get(env_var, False)
|
||||
if encoding:
|
||||
# Support dotted (C.UTF-8) and non-dotted (C or UTF-8) charsets:
|
||||
# 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 :]
|
||||
else:
|
||||
default_encoding = encoding
|
||||
break
|
||||
|
||||
return default_encoding
|
||||
"""Use local.getpreferredencoding() or fallback to UTF-8."""
|
||||
return locale.getpreferredencoding() or "UTF-8"
|
||||
|
||||
|
||||
DEFAULT_ENCODING = getpreferredencoding()
|
||||
TERMINAL_ENCODING = getpreferredencoding()
|
||||
|
||||
|
||||
########################################################################################################################
|
||||
# FILE_ENCODING
|
||||
|
||||
# Encoding for reading/writing files within the tests, this is always UTF-8
|
||||
FILE_ENCODING = "UTF-8"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue