From 1312af87e5908ac252c0659a60216e96ec5b23bd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 14 Jun 2020 11:11:24 +0200 Subject: Merging upstream version 2.5.1. Signed-off-by: Daniel Baumann --- tests/commands/autoupdate_test.py | 32 +++++++++-- tests/commands/hook_impl_test.py | 10 +++- tests/commands/install_uninstall_test.py | 57 +++++++++++++++++++ tests/commands/migrate_config_test.py | 7 ++- tests/commands/run_test.py | 19 ++++++- tests/git_test.py | 5 ++ tests/languages/helpers_test.py | 2 +- tests/languages/python_test.py | 96 +++++++++++++++++++++++++++----- tests/repository_test.py | 8 ++- tests/store_test.py | 9 ++- 10 files changed, 212 insertions(+), 33 deletions(-) (limited to 'tests') diff --git a/tests/commands/autoupdate_test.py b/tests/commands/autoupdate_test.py index 25161d1..bd89c1d 100644 --- a/tests/commands/autoupdate_test.py +++ b/tests/commands/autoupdate_test.py @@ -414,9 +414,9 @@ def test_autoupdate_local_hooks(in_git_dir, store): config = sample_local_config() add_config_to_repo('.', config) assert autoupdate(C.CONFIG_FILE, store, freeze=False, tags_only=False) == 0 - new_config_writen = read_config('.') - assert len(new_config_writen['repos']) == 1 - assert new_config_writen['repos'][0] == config + new_config_written = read_config('.') + assert len(new_config_written['repos']) == 1 + assert new_config_written['repos'][0] == config def test_autoupdate_local_hooks_with_out_of_date_repo( @@ -429,9 +429,9 @@ def test_autoupdate_local_hooks_with_out_of_date_repo( config = {'repos': [local_config, stale_config]} write_config('.', config) assert autoupdate(C.CONFIG_FILE, store, freeze=False, tags_only=False) == 0 - new_config_writen = read_config('.') - assert len(new_config_writen['repos']) == 2 - assert new_config_writen['repos'][0] == local_config + new_config_written = read_config('.') + assert len(new_config_written['repos']) == 2 + assert new_config_written['repos'][0] == local_config def test_autoupdate_meta_hooks(tmpdir, store): @@ -474,3 +474,23 @@ def test_updates_old_format_to_new_format(tmpdir, capsys, store): ) out, _ = capsys.readouterr() assert out == 'Configuration has been migrated.\n' + + +def test_maintains_rev_quoting_style(tmpdir, out_of_date, store): + fmt = ( + 'repos:\n' + '- repo: {path}\n' + ' rev: "{rev}"\n' + ' hooks:\n' + ' - id: foo\n' + '- repo: {path}\n' + " rev: '{rev}'\n" + ' hooks:\n' + ' - id: foo\n' + ) + cfg = tmpdir.join(C.CONFIG_FILE) + cfg.write(fmt.format(path=out_of_date.path, rev=out_of_date.original_rev)) + + assert autoupdate(str(cfg), store, freeze=False, tags_only=False) == 0 + expected = fmt.format(path=out_of_date.path, rev=out_of_date.head_rev) + assert cfg.read() == expected diff --git a/tests/commands/hook_impl_test.py b/tests/commands/hook_impl_test.py index ddf65b7..2fc0146 100644 --- a/tests/commands/hook_impl_test.py +++ b/tests/commands/hook_impl_test.py @@ -96,6 +96,7 @@ def test_run_legacy_recursive(tmpdir): ('pre-merge-commit', []), ('pre-push', ['branch_name', 'remote_name']), ('commit-msg', ['.git/COMMIT_EDITMSG']), + ('post-commit', []), ('post-checkout', ['old_head', 'new_head', '1']), # multiple choices for commit-editmsg ('prepare-commit-msg', ['.git/COMMIT_EDITMSG']), @@ -117,7 +118,7 @@ def test_check_args_length_error_too_many_plural(): ) -def test_check_args_length_error_too_many_singluar(): +def test_check_args_length_error_too_many_singular(): with pytest.raises(SystemExit) as excinfo: hook_impl._check_args_length('commit-msg', []) msg, = excinfo.value.args @@ -149,6 +150,13 @@ def test_run_ns_commit_msg(): assert ns.commit_msg_filename == '.git/COMMIT_MSG' +def test_run_ns_post_commit(): + ns = hook_impl._run_ns('post-commit', True, (), b'') + assert ns is not None + assert ns.hook_stage == 'post-commit' + assert ns.color is True + + 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 66b9190..5809a3f 100644 --- a/tests/commands/install_uninstall_test.py +++ b/tests/commands/install_uninstall_test.py @@ -726,6 +726,32 @@ def test_commit_msg_legacy(commit_msg_repo, tempdir_factory, store): assert second_line.startswith('Must have "Signed off by:"...') +def test_post_commit_integration(tempdir_factory, store): + path = git_dir(tempdir_factory) + config = [ + { + 'repo': 'local', + 'hooks': [{ + 'id': 'post-commit', + 'name': 'Post commit', + 'entry': 'touch post-commit.tmp', + 'language': 'system', + 'always_run': True, + 'verbose': True, + 'stages': ['post-commit'], + }], + }, + ] + write_config(path, config) + with cwd(path): + _get_commit_output(tempdir_factory) + assert not os.path.exists('post-commit.tmp') + + install(C.CONFIG_FILE, store, hook_types=['post-commit']) + _get_commit_output(tempdir_factory) + assert os.path.exists('post-commit.tmp') + + def test_post_checkout_integration(tempdir_factory, store): path = git_dir(tempdir_factory) config = [ @@ -763,6 +789,37 @@ def test_post_checkout_integration(tempdir_factory, store): assert 'some_file' not in stderr +def test_skips_post_checkout_unstaged_changes(tempdir_factory, store): + path = git_dir(tempdir_factory) + config = { + 'repo': 'local', + 'hooks': [{ + 'id': 'fail', + 'name': 'fail', + 'entry': 'fail', + 'language': 'fail', + 'always_run': True, + 'stages': ['post-checkout'], + }], + } + write_config(path, config) + with cwd(path): + cmd_output('git', 'add', '.') + _get_commit_output(tempdir_factory) + + install(C.CONFIG_FILE, store, hook_types=['pre-commit']) + install(C.CONFIG_FILE, store, hook_types=['post-checkout']) + + # make an unstaged change so staged_files_only fires + open('file', 'a').close() + cmd_output('git', 'add', 'file') + with open('file', 'w') as f: + f.write('unstaged changes') + + retc, out = _get_commit_output(tempdir_factory, all_files=False) + assert retc == 0 + + def test_prepare_commit_msg_integration_failing( failing_prepare_commit_msg_repo, tempdir_factory, store, ): diff --git a/tests/commands/migrate_config_test.py b/tests/commands/migrate_config_test.py index efc0d1c..6a049d5 100644 --- a/tests/commands/migrate_config_test.py +++ b/tests/commands/migrate_config_test.py @@ -1,6 +1,7 @@ import pytest import pre_commit.constants as C +from pre_commit.clientlib import InvalidConfigError from pre_commit.commands.migrate_config import _indent from pre_commit.commands.migrate_config import migrate_config @@ -147,10 +148,10 @@ def test_migrate_config_sha_to_rev(tmpdir): @pytest.mark.parametrize('contents', ('', '\n')) -def test_empty_configuration_file_user_error(tmpdir, contents): +def test_migrate_config_invalid_configuration(tmpdir, contents): cfg = tmpdir.join(C.CONFIG_FILE) cfg.write(contents) - with tmpdir.as_cwd(): - assert not migrate_config(C.CONFIG_FILE) + with tmpdir.as_cwd(), pytest.raises(InvalidConfigError): + migrate_config(C.CONFIG_FILE) # even though the config is invalid, this should be a noop assert cfg.read() == contents diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py index c51bcff..2461ed5 100644 --- a/tests/commands/run_test.py +++ b/tests/commands/run_test.py @@ -939,7 +939,7 @@ def test_classifier_normalizes_filenames_on_windows_to_forward_slashes(tmpdir): tmpdir.join('a/b/c').ensure() with mock.patch.object(os, 'altsep', '/'): with mock.patch.object(os, 'sep', '\\'): - classifier = Classifier((r'a\b\c',)) + classifier = Classifier.from_config((r'a\b\c',), '', '^$') assert classifier.filenames == ['a/b/c'] @@ -947,7 +947,7 @@ def test_classifier_does_not_normalize_backslashes_non_windows(tmpdir): with mock.patch.object(os.path, 'lexists', return_value=True): with mock.patch.object(os, 'altsep', None): with mock.patch.object(os, 'sep', '/'): - classifier = Classifier((r'a/b\c',)) + classifier = Classifier.from_config((r'a/b\c',), '', '^$') assert classifier.filenames == [r'a/b\c'] @@ -1022,3 +1022,18 @@ def test_args_hook_only(cap_out, store, repo_with_passing_hook): run_opts(hook='do_not_commit'), ) assert b'identity-copy' not in printed + + +def test_skipped_without_any_setup_for_post_checkout(in_git_dir, store): + environ = {'_PRE_COMMIT_SKIP_POST_CHECKOUT': '1'} + opts = run_opts(hook_stage='post-checkout') + assert run(C.CONFIG_FILE, store, opts, environ=environ) == 0 + + +def test_pre_commit_env_variable_set(cap_out, store, repo_with_passing_hook): + args = run_opts() + environ: EnvironT = {} + ret, printed = _do_run( + cap_out, store, repo_with_passing_hook, args, environ, + ) + assert environ['PRE_COMMIT'] == '1' diff --git a/tests/git_test.py b/tests/git_test.py index e73a6f2..fafd4a6 100644 --- a/tests/git_test.py +++ b/tests/git_test.py @@ -186,3 +186,8 @@ def test_no_git_env(): 'GIT_SSH': '/usr/bin/ssh', 'GIT_SSH_COMMAND': 'ssh -o', } + + +def test_init_repo_no_hooks(tmpdir): + git.init_repo(str(tmpdir), remote='dne') + assert not tmpdir.join('.git/hooks').exists() diff --git a/tests/languages/helpers_test.py b/tests/languages/helpers_test.py index c52e947..fa493cc 100644 --- a/tests/languages/helpers_test.py +++ b/tests/languages/helpers_test.py @@ -78,5 +78,5 @@ def test_target_concurrency_cpu_count_not_implemented(): def test_shuffled_is_deterministic(): seq = [str(i) for i in range(10)] - expected = ['3', '7', '8', '2', '4', '6', '5', '1', '0', '9'] + expected = ['4', '0', '5', '1', '8', '6', '2', '3', '7', '9'] assert helpers._shuffled(seq) == expected diff --git a/tests/languages/python_test.py b/tests/languages/python_test.py index 34c6c7f..c419ad6 100644 --- a/tests/languages/python_test.py +++ b/tests/languages/python_test.py @@ -5,10 +5,23 @@ from unittest import mock import pytest import pre_commit.constants as C +from pre_commit.envcontext import envcontext from pre_commit.languages import python from pre_commit.prefix import Prefix +def test_read_pyvenv_cfg(tmpdir): + pyvenv_cfg = tmpdir.join('pyvenv.cfg') + pyvenv_cfg.write( + '# I am a comment\n' + '\n' + 'foo = bar\n' + 'version-info=123\n', + ) + expected = {'foo': 'bar', 'version-info': '123'} + assert python._read_pyvenv_cfg(pyvenv_cfg) == expected + + def test_norm_version_expanduser(): home = os.path.expanduser('~') if os.name == 'nt': # pragma: nt cover @@ -21,6 +34,10 @@ def test_norm_version_expanduser(): assert result == expected_path +def test_norm_version_of_default_is_sys_executable(): + assert python.norm_version('default') == os.path.realpath(sys.executable) + + @pytest.mark.parametrize('v', ('python3.6', 'python3', 'python')) def test_sys_executable_matches(v): with mock.patch.object(sys, 'version_info', (3, 6, 7)): @@ -49,27 +66,78 @@ def test_find_by_sys_executable(exe, realpath, expected): assert python._find_by_sys_executable() == expected -def test_healthy_types_py_in_cwd(tmpdir): +@pytest.fixture +def python_dir(tmpdir): with tmpdir.as_cwd(): prefix = tmpdir.join('prefix').ensure_dir() prefix.join('setup.py').write('import setuptools; setuptools.setup()') prefix = Prefix(str(prefix)) - python.install_environment(prefix, C.DEFAULT, ()) + yield prefix, tmpdir - # even if a `types.py` file exists, should still be healthy - tmpdir.join('types.py').ensure() - assert python.healthy(prefix, C.DEFAULT) is True +def test_healthy_default_creator(python_dir): + prefix, tmpdir = python_dir -def test_healthy_python_goes_missing(tmpdir): - with tmpdir.as_cwd(): - prefix = tmpdir.join('prefix').ensure_dir() - prefix.join('setup.py').write('import setuptools; setuptools.setup()') - prefix = Prefix(str(prefix)) + python.install_environment(prefix, C.DEFAULT, ()) + + # should be healthy right after creation + assert python.healthy(prefix, C.DEFAULT) is True + + # even if a `types.py` file exists, should still be healthy + tmpdir.join('types.py').ensure() + assert python.healthy(prefix, C.DEFAULT) is True + + +def test_healthy_venv_creator(python_dir): + # venv creator produces slightly different pyvenv.cfg + prefix, tmpdir = python_dir + + with envcontext((('VIRTUALENV_CREATOR', 'venv'),)): python.install_environment(prefix, C.DEFAULT, ()) - exe_name = 'python' if sys.platform != 'win32' else 'python.exe' - py_exe = prefix.path(python.bin_dir('py_env-default'), exe_name) - os.remove(py_exe) + assert python.healthy(prefix, C.DEFAULT) is True + + +def test_unhealthy_python_goes_missing(python_dir): + prefix, tmpdir = python_dir + + python.install_environment(prefix, C.DEFAULT, ()) + + exe_name = 'python' if sys.platform != 'win32' else 'python.exe' + py_exe = prefix.path(python.bin_dir('py_env-default'), exe_name) + os.remove(py_exe) + + assert python.healthy(prefix, C.DEFAULT) is False + + +def test_unhealthy_with_version_change(python_dir): + prefix, tmpdir = python_dir + + python.install_environment(prefix, C.DEFAULT, ()) + + with open(prefix.path('py_env-default/pyvenv.cfg'), 'w') as f: + f.write('version_info = 1.2.3\n') + + assert python.healthy(prefix, C.DEFAULT) is False + + +def test_unhealthy_system_version_changes(python_dir): + prefix, tmpdir = python_dir + + python.install_environment(prefix, C.DEFAULT, ()) + + with open(prefix.path('py_env-default/pyvenv.cfg'), 'a') as f: + f.write('base-executable = /does/not/exist\n') + + assert python.healthy(prefix, C.DEFAULT) is False + + +def test_unhealthy_old_virtualenv(python_dir): + prefix, tmpdir = python_dir + + python.install_environment(prefix, C.DEFAULT, ()) + + # simulate "old" virtualenv by deleting this file + os.remove(prefix.path('py_env-default/pyvenv.cfg')) - assert python.healthy(prefix, C.DEFAULT) is False + assert python.healthy(prefix, C.DEFAULT) is False diff --git a/tests/repository_test.py b/tests/repository_test.py index 3c7a637..2ac7886 100644 --- a/tests/repository_test.py +++ b/tests/repository_test.py @@ -33,7 +33,7 @@ from testing.util import cwd from testing.util import get_resource_path from testing.util import skipif_cant_run_docker from testing.util import skipif_cant_run_swift -from testing.util import xfailif_no_venv +from testing.util import xfailif_windows from testing.util import xfailif_windows_no_ruby @@ -163,7 +163,6 @@ def test_python_hook_weird_setup_cfg(in_git_dir, tempdir_factory, store): ) -@xfailif_no_venv def test_python_venv(tempdir_factory, store): # pragma: no cover (no venv) _test_hook_repo( tempdir_factory, store, 'python_venv_hooks_repo', @@ -243,6 +242,7 @@ def test_run_a_node_hook(tempdir_factory, store): ) +@xfailif_windows # pragma: win32 no cover def test_run_a_node_hook_default_version(tempdir_factory, store): # make sure that this continues to work for platforms where node is not # installed at the system @@ -252,6 +252,7 @@ def test_run_a_node_hook_default_version(tempdir_factory, store): test_run_a_node_hook(tempdir_factory, store) +@xfailif_windows # pragma: win32 no cover def test_run_versioned_node_hook(tempdir_factory, store): _test_hook_repo( tempdir_factory, store, 'node_versioned_hooks_repo', @@ -534,6 +535,7 @@ def test_additional_ruby_dependencies_installed(tempdir_factory, store): assert 'tins' in output +@xfailif_windows # pragma: win32 no cover def test_additional_node_dependencies_installed(tempdir_factory, store): path = make_repo(tempdir_factory, 'node_hooks_repo') config = make_config_from_repo(path) @@ -880,7 +882,7 @@ def test_manifest_hooks(tempdir_factory, store): require_serial=False, stages=( 'commit', 'merge-commit', 'prepare-commit-msg', 'commit-msg', - 'manual', 'post-checkout', 'push', + 'post-commit', 'manual', 'post-checkout', 'push', ), types=['file'], verbose=False, diff --git a/tests/store_test.py b/tests/store_test.py index 5866616..6a4e900 100644 --- a/tests/store_test.py +++ b/tests/store_test.py @@ -25,7 +25,8 @@ def test_our_session_fixture_works(): def test_get_default_directory_defaults_to_home(): # Not we use the module level one which is not mocked ret = _get_default_directory() - assert ret == os.path.join(os.path.expanduser('~/.cache'), 'pre-commit') + expected = os.path.realpath(os.path.expanduser('~/.cache/pre-commit')) + assert ret == expected def test_adheres_to_xdg_specification(): @@ -33,7 +34,8 @@ def test_adheres_to_xdg_specification(): os.environ, {'XDG_CACHE_HOME': '/tmp/fakehome'}, ): ret = _get_default_directory() - assert ret == os.path.join('/tmp/fakehome', 'pre-commit') + expected = os.path.realpath('/tmp/fakehome/pre-commit') + assert ret == expected def test_uses_environment_variable_when_present(): @@ -41,7 +43,8 @@ def test_uses_environment_variable_when_present(): os.environ, {'PRE_COMMIT_HOME': '/tmp/pre_commit_home'}, ): ret = _get_default_directory() - assert ret == '/tmp/pre_commit_home' + expected = os.path.realpath('/tmp/pre_commit_home') + assert ret == expected def test_store_init(store): -- cgit v1.2.3