From 84c011ae6133fb007046d8eb66c01d0de80b3500 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 8 Mar 2021 18:48:51 +0100 Subject: Merging upstream version 2.11.0. Signed-off-by: Daniel Baumann --- tests/clientlib_test.py | 3 +- tests/commands/hook_impl_test.py | 9 +++ tests/commands/install_uninstall_test.py | 47 +++++++++++++- tests/commands/run_test.py | 9 +++ tests/git_test.py | 11 ++++ tests/languages/r_test.py | 104 +++++++++++++++++++++++++++++++ tests/main_test.py | 13 ++++ tests/repository_test.py | 50 ++++++++++++++- 8 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 tests/languages/r_test.py (limited to 'tests') diff --git a/tests/clientlib_test.py b/tests/clientlib_test.py index 6bdb0d6..ff3cce3 100644 --- a/tests/clientlib_test.py +++ b/tests/clientlib_test.py @@ -228,7 +228,8 @@ def test_warn_mutable_rev_invalid(caplog, rev): 'Mutable references are never updated after first install and are ' 'not supported. ' 'See https://pre-commit.com/#using-the-latest-version-for-a-repository ' # noqa: E501 - 'for more details.', + 'for more details. ' + 'Hint: `pre-commit autoupdate` often fixes this.', ), ] diff --git a/tests/commands/hook_impl_test.py b/tests/commands/hook_impl_test.py index 2fc0146..c38b9ca 100644 --- a/tests/commands/hook_impl_test.py +++ b/tests/commands/hook_impl_test.py @@ -97,6 +97,7 @@ def test_run_legacy_recursive(tmpdir): ('pre-push', ['branch_name', 'remote_name']), ('commit-msg', ['.git/COMMIT_EDITMSG']), ('post-commit', []), + ('post-merge', ['1']), ('post-checkout', ['old_head', 'new_head', '1']), # multiple choices for commit-editmsg ('prepare-commit-msg', ['.git/COMMIT_EDITMSG']), @@ -157,6 +158,14 @@ def test_run_ns_post_commit(): assert ns.color is True +def test_run_ns_post_merge(): + ns = hook_impl._run_ns('post-merge', True, ('1',), b'') + assert ns is not None + assert ns.hook_stage == 'post-merge' + assert ns.color is True + assert ns.is_squash_merge == '1' + + def test_run_ns_post_checkout(): ns = hook_impl._run_ns('post-checkout', True, ('a', 'b', 'c'), b'') assert ns is not None diff --git a/tests/commands/install_uninstall_test.py b/tests/commands/install_uninstall_test.py index 7a4b906..bd28654 100644 --- a/tests/commands/install_uninstall_test.py +++ b/tests/commands/install_uninstall_test.py @@ -259,7 +259,10 @@ def _path_without_us(): exe = find_executable('pre-commit', _environ=env) while exe: parts = env['PATH'].split(os.pathsep) - after = [x for x in parts if x.lower() != os.path.dirname(exe).lower()] + after = [ + x for x in parts + if x.lower().rstrip(os.sep) != os.path.dirname(exe).lower() + ] if parts == after: raise AssertionError(exe, parts) env['PATH'] = os.pathsep.join(after) @@ -759,6 +762,48 @@ def test_post_commit_integration(tempdir_factory, store): assert os.path.exists('post-commit.tmp') +def test_post_merge_integration(tempdir_factory, store): + path = git_dir(tempdir_factory) + config = [ + { + 'repo': 'local', + 'hooks': [{ + 'id': 'post-merge', + 'name': 'Post merge', + 'entry': 'touch post-merge.tmp', + 'language': 'system', + 'always_run': True, + 'verbose': True, + 'stages': ['post-merge'], + }], + }, + ] + write_config(path, config) + with cwd(path): + # create a simple diamond of commits for a non-trivial merge + open('init', 'a').close() + cmd_output('git', 'add', '.') + git_commit() + + open('master', 'a').close() + cmd_output('git', 'add', '.') + git_commit() + + cmd_output('git', 'checkout', '-b', 'branch', 'HEAD^') + open('branch', 'a').close() + cmd_output('git', 'add', '.') + git_commit() + + cmd_output('git', 'checkout', 'master') + install(C.CONFIG_FILE, store, hook_types=['post-merge']) + retc, stdout, stderr = cmd_output_mocked_pre_commit_home( + 'git', 'merge', 'branch', + tempdir_factory=tempdir_factory, + ) + assert retc == 0 + assert os.path.exists('post-merge.tmp') + + def test_post_checkout_integration(tempdir_factory, store): path = git_dir(tempdir_factory) config = [ diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py index eaea813..4cd70fd 100644 --- a/tests/commands/run_test.py +++ b/tests/commands/run_test.py @@ -494,6 +494,15 @@ def test_all_push_options_ok(cap_out, store, repo_with_passing_hook): assert b'Specify both --from-ref and --to-ref.' not in printed +def test_is_squash_merge(cap_out, store, repo_with_passing_hook): + args = run_opts(is_squash_merge='1') + environ: MutableMapping[str, str] = {} + ret, printed = _do_run( + cap_out, store, repo_with_passing_hook, args, environ, + ) + assert environ['PRE_COMMIT_IS_SQUASH_MERGE'] == '1' + + def test_checkout_type(cap_out, store, repo_with_passing_hook): args = run_opts(from_ref='', to_ref='', checkout_type='1') environ: MutableMapping[str, str] = {} diff --git a/tests/git_test.py b/tests/git_test.py index 69fd206..51d5f8c 100644 --- a/tests/git_test.py +++ b/tests/git_test.py @@ -38,6 +38,17 @@ def test_get_root_bare_worktree(tmpdir): assert git.get_root() == os.path.abspath('.') +def test_get_root_worktree_in_git(tmpdir): + src = tmpdir.join('src').ensure_dir() + cmd_output('git', 'init', str(src)) + git_commit(cwd=str(src)) + + cmd_output('git', 'worktree', 'add', '.git/trees/foo', 'HEAD', cwd=src) + + with src.join('.git/trees/foo').as_cwd(): + assert git.get_root() == os.path.abspath('.') + + def test_get_staged_files_deleted(in_git_dir): in_git_dir.join('test').ensure() cmd_output('git', 'add', 'test') diff --git a/tests/languages/r_test.py b/tests/languages/r_test.py new file mode 100644 index 0000000..5c046ef --- /dev/null +++ b/tests/languages/r_test.py @@ -0,0 +1,104 @@ +import os.path + +import pytest + +from pre_commit.languages import r +from testing.fixtures import make_config_from_repo +from testing.fixtures import make_repo +from tests.repository_test import _get_hook_no_install + + +def _test_r_parsing( + tempdir_factory, + store, + hook_id, + expected_hook_expr={}, + expected_args={}, +): + repo_path = 'r_hooks_repo' + path = make_repo(tempdir_factory, repo_path) + config = make_config_from_repo(path) + hook = _get_hook_no_install(config, store, hook_id) + ret = r._cmd_from_hook(hook) + expected_cmd = 'Rscript' + expected_opts = ( + '--no-save', '--no-restore', '--no-site-file', '--no-environ', + ) + expected_path = os.path.join( + hook.prefix.prefix_dir, '.'.join([hook_id, 'R']), + ) + expected = ( + expected_cmd, + *expected_opts, + *(expected_hook_expr or (expected_path,)), + *expected_args, + ) + assert ret == expected + + +def test_r_parsing_file_no_opts_no_args(tempdir_factory, store): + hook_id = 'parse-file-no-opts-no-args' + _test_r_parsing(tempdir_factory, store, hook_id) + + +def test_r_parsing_file_opts_no_args(tempdir_factory, store): + with pytest.raises(ValueError) as excinfo: + r._entry_validate(['Rscript', '--no-init', '/path/to/file']) + + msg = excinfo.value.args + assert msg == ( + 'The only valid syntax is `Rscript -e {expr}`', + 'or `Rscript path/to/hook/script`', + ) + + +def test_r_parsing_file_no_opts_args(tempdir_factory, store): + hook_id = 'parse-file-no-opts-args' + expected_args = ['--no-cache'] + _test_r_parsing( + tempdir_factory, store, hook_id, expected_args=expected_args, + ) + + +def test_r_parsing_expr_no_opts_no_args1(tempdir_factory, store): + hook_id = 'parse-expr-no-opts-no-args-1' + _test_r_parsing( + tempdir_factory, store, hook_id, expected_hook_expr=('-e', '1+1'), + ) + + +def test_r_parsing_expr_no_opts_no_args2(tempdir_factory, store): + with pytest.raises(ValueError) as execinfo: + r._entry_validate(['Rscript', '-e', '1+1', '-e', 'letters']) + msg = execinfo.value.args + assert msg == ('You can supply at most one expression.',) + + +def test_r_parsing_expr_opts_no_args2(tempdir_factory, store): + with pytest.raises(ValueError) as execinfo: + r._entry_validate( + [ + 'Rscript', '--vanilla', '-e', '1+1', '-e', 'letters', + ], + ) + msg = execinfo.value.args + assert msg == ( + 'The only valid syntax is `Rscript -e {expr}`', + 'or `Rscript path/to/hook/script`', + ) + + +def test_r_parsing_expr_args_in_entry2(tempdir_factory, store): + with pytest.raises(ValueError) as execinfo: + r._entry_validate(['Rscript', '-e', 'expr1', '--another-arg']) + + msg = execinfo.value.args + assert msg == ('You can supply at most one expression.',) + + +def test_r_parsing_expr_non_Rscirpt(tempdir_factory, store): + with pytest.raises(ValueError) as execinfo: + r._entry_validate(['AnotherScript', '-e', '{{}}']) + + msg = execinfo.value.args + assert msg == ('entry must start with `Rscript`.',) diff --git a/tests/main_test.py b/tests/main_test.py index 2460bd8..1ad8d41 100644 --- a/tests/main_test.py +++ b/tests/main_test.py @@ -7,7 +7,9 @@ import pytest import pre_commit.constants as C from pre_commit import main from pre_commit.errors import FatalError +from pre_commit.util import cmd_output from testing.auto_namedtuple import auto_namedtuple +from testing.util import cwd @pytest.mark.parametrize( @@ -54,6 +56,17 @@ def test_adjust_args_and_chdir_relative_things(in_git_dir): assert args.files == [os.path.join('foo', 'f1'), os.path.join('foo', 'f2')] +@pytest.mark.skipif(os.name != 'nt', reason='windows feature') +def test_install_on_subst(in_git_dir, store): # pragma: posix no cover + assert not os.path.exists('Z:') + cmd_output('subst', 'Z:', str(in_git_dir)) + try: + with cwd('Z:'): + test_adjust_args_and_chdir_noop('Z:\\') + finally: + cmd_output('subst', '/d', 'Z:') + + def test_adjust_args_and_chdir_non_relative_config(in_git_dir): in_git_dir.join('foo').ensure_dir().chdir() diff --git a/tests/repository_test.py b/tests/repository_test.py index 1b58164..b6f7fb2 100644 --- a/tests/repository_test.py +++ b/tests/repository_test.py @@ -279,6 +279,54 @@ def test_node_hook_with_npm_userconfig_set(tempdir_factory, store, tmpdir): test_run_a_node_hook(tempdir_factory, store) +def test_r_hook(tempdir_factory, store): + _test_hook_repo( + tempdir_factory, store, 'r_hooks_repo', + 'hello-world', [os.devnull], + b'Hello, World, from R!\n', + ) + + +def test_r_inline_hook(tempdir_factory, store): + _test_hook_repo( + tempdir_factory, store, 'r_hooks_repo', + 'hello-world-inline', ['some-file'], + b'Hi-there, some-file, from R!\n', + ) + + +def test_r_with_additional_dependencies_hook(tempdir_factory, store): + _test_hook_repo( + tempdir_factory, store, 'r_hooks_repo', + 'additional-deps', [os.devnull], + b'OK\n', + config_kwargs={ + 'hooks': [{ + 'id': 'additional-deps', + 'additional_dependencies': ['cachem@1.0.4'], + }], + }, + ) + + +def test_r_local_with_additional_dependencies_hook(store): + config = { + 'repo': 'local', + 'hooks': [{ + 'id': 'local-r', + 'name': 'local-r', + 'entry': 'Rscript -e', + 'language': 'r', + 'args': ['if (packageVersion("R6") == "2.1.3") cat("OK\n")'], + 'additional_dependencies': ['R6@2.1.3'], + }], + } + hook = _get_hook(config, store, 'local-r') + ret, out = _hook_run(hook, (), color=False) + assert ret == 0 + assert _norm_out(out) == b'OK\n' + + def test_run_a_ruby_hook(tempdir_factory, store): _test_hook_repo( tempdir_factory, store, 'ruby_hooks_repo', @@ -953,7 +1001,7 @@ def test_manifest_hooks(tempdir_factory, store): require_serial=False, stages=( 'commit', 'merge-commit', 'prepare-commit-msg', 'commit-msg', - 'post-commit', 'manual', 'post-checkout', 'push', + 'post-commit', 'manual', 'post-checkout', 'push', 'post-merge', ), types=['file'], types_or=[], -- cgit v1.2.3