Tests pass on windows

This commit is contained in:
Anthony Sottile 2015-01-18 19:45:44 -08:00
parent 56e5c4eb2d
commit 143ed94500
21 changed files with 224 additions and 109 deletions

View file

@ -2,11 +2,12 @@ from __future__ import print_function
from __future__ import unicode_literals from __future__ import unicode_literals
import os.path import os.path
import shutil
from pre_commit.util import rmtree
def clean(runner): def clean(runner):
if os.path.exists(runner.store.directory): if os.path.exists(runner.store.directory):
shutil.rmtree(runner.store.directory) rmtree(runner.store.directory)
print('Cleaned {0}.'.format(runner.store.directory)) print('Cleaned {0}.'.format(runner.store.directory))
return 0 return 0

View file

@ -6,5 +6,17 @@ PY3 = str is not bytes
if PY2: # pragma: no cover (PY2 only) if PY2: # pragma: no cover (PY2 only)
text = unicode # flake8: noqa text = unicode # flake8: noqa
def n(s):
if isinstance(s, bytes):
return s
else:
return s.encode('UTF-8')
else: # pragma: no cover (PY3 only) else: # pragma: no cover (PY3 only)
text = str text = str
def n(s):
if isinstance(s, text):
return s
else:
return s.decode('UTF-8')

View file

@ -16,7 +16,7 @@ logger = logging.getLogger('pre_commit')
def get_root(): def get_root():
path = os.getcwd() path = os.getcwd()
while len(path) > 1: while path != os.path.normpath(os.path.join(path, '../')):
if os.path.exists(os.path.join(path, '.git')): if os.path.exists(os.path.join(path, '.git')):
return path return path
else: else:

View file

@ -13,7 +13,7 @@ ENVIRONMENT_DIR = 'node_env'
class NodeEnv(helpers.Environment): class NodeEnv(helpers.Environment):
@property @property
def env_prefix(self): def env_prefix(self):
return '. {{prefix}}{0}/bin/activate &&'.format(ENVIRONMENT_DIR) return ". '{{prefix}}{0}/bin/activate' &&".format(ENVIRONMENT_DIR)
@contextlib.contextmanager @contextlib.contextmanager
@ -37,7 +37,7 @@ def install_environment(repo_cmd_runner, version='default'):
repo_cmd_runner.run(cmd) repo_cmd_runner.run(cmd)
with in_env(repo_cmd_runner) as node_env: with in_env(repo_cmd_runner) as node_env:
node_env.run('cd {prefix} && npm install -g') node_env.run("cd '{prefix}' && npm install -g")
def run_hook(repo_cmd_runner, hook, file_args): def run_hook(repo_cmd_runner, hook, file_args):

View file

@ -1,6 +1,10 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import contextlib import contextlib
import distutils.spawn
import os
import virtualenv
from pre_commit.languages import helpers from pre_commit.languages import helpers
from pre_commit.util import clean_path_on_failure from pre_commit.util import clean_path_on_failure
@ -12,7 +16,12 @@ ENVIRONMENT_DIR = 'py_env'
class PythonEnv(helpers.Environment): class PythonEnv(helpers.Environment):
@property @property
def env_prefix(self): def env_prefix(self):
return '. {{prefix}}{0}/bin/activate &&'.format(ENVIRONMENT_DIR) return ". '{{prefix}}{0}activate' &&".format(
virtualenv.path_locations(
ENVIRONMENT_DIR,
)[-1].rstrip(os.sep) + os.sep,
'activate',
)
@contextlib.contextmanager @contextlib.contextmanager
@ -20,6 +29,15 @@ def in_env(repo_cmd_runner):
yield PythonEnv(repo_cmd_runner) yield PythonEnv(repo_cmd_runner)
def norm_version(version):
if os.name == 'nt': # pragma: no cover (windows)
if not distutils.spawn.find_executable(version):
# The default place for python on windows is:
# C:\PythonXX\python.exe
version = r'C:\{0}\python.exe'.format(version.replace('.', ''))
return version
def install_environment(repo_cmd_runner, version='default'): def install_environment(repo_cmd_runner, version='default'):
assert repo_cmd_runner.exists('setup.py') assert repo_cmd_runner.exists('setup.py')
@ -27,10 +45,10 @@ def install_environment(repo_cmd_runner, version='default'):
with clean_path_on_failure(repo_cmd_runner.path(ENVIRONMENT_DIR)): with clean_path_on_failure(repo_cmd_runner.path(ENVIRONMENT_DIR)):
venv_cmd = ['virtualenv', '{{prefix}}{0}'.format(ENVIRONMENT_DIR)] venv_cmd = ['virtualenv', '{{prefix}}{0}'.format(ENVIRONMENT_DIR)]
if version != 'default': if version != 'default':
venv_cmd.extend(['-p', version]) venv_cmd.extend(['-p', norm_version(version)])
repo_cmd_runner.run(venv_cmd) repo_cmd_runner.run(venv_cmd)
with in_env(repo_cmd_runner) as env: with in_env(repo_cmd_runner) as env:
env.run('cd {prefix} && pip install .') env.run("cd '{prefix}' && pip install .")
def run_hook(repo_cmd_runner, hook, file_args): def run_hook(repo_cmd_runner, hook, file_args):

View file

@ -31,3 +31,4 @@ class LoggingHandler(logging.Handler):
record.getMessage(), record.getMessage(),
) )
) )
sys.stdout.flush()

View file

@ -3,10 +3,11 @@ from __future__ import print_function
from __future__ import unicode_literals from __future__ import unicode_literals
import os.path import os.path
import shutil
from pre_commit import five
from pre_commit.util import cmd_output from pre_commit.util import cmd_output
from pre_commit.util import cwd from pre_commit.util import cwd
from pre_commit.util import rmtree
from pre_commit.util import tarfile_open from pre_commit.util import tarfile_open
from pre_commit.util import tmpdir from pre_commit.util import tmpdir
@ -50,11 +51,9 @@ def make_archive(name, repo, ref, destdir):
# We don't want the '.git' directory # We don't want the '.git' directory
# It adds a bunch of size to the archive and we don't use it at # It adds a bunch of size to the archive and we don't use it at
# runtime # runtime
shutil.rmtree(os.path.join(tempdir, '.git')) rmtree(os.path.join(tempdir, '.git'))
# XXX: py2.6 derps if filename is unicode while writing with tarfile_open(five.n(output_path), 'w|gz') as tf:
# XXX: str() is used to preserve behavior in py3
with tarfile_open(str(output_path), 'w|gz') as tf:
tf.add(tempdir, name) tf.add(tempdir, name)
return output_path return output_path

View file

@ -8,6 +8,7 @@ from pre_commit import five
# TODO: smell: import side-effects # TODO: smell: import side-effects
try:
COLS = int( COLS = int(
subprocess.Popen( subprocess.Popen(
['tput', 'cols'], stdout=subprocess.PIPE, ['tput', 'cols'], stdout=subprocess.PIPE,
@ -15,6 +16,8 @@ COLS = int(
# Default in the case of no terminal # Default in the case of no terminal
80 80
) )
except OSError: # pragma: no cover (windows)
COLS = 80
def get_hook_message( def get_hook_message(

View file

@ -28,7 +28,7 @@ def _get_default_directory():
""" """
return os.environ.get( return os.environ.get(
'PRE_COMMIT_HOME', 'PRE_COMMIT_HOME',
os.path.join(os.environ['HOME'], '.pre-commit'), os.path.join(os.path.expanduser('~'), '.pre-commit'),
) )

View file

@ -1,16 +1,20 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import contextlib import contextlib
import errno
import functools import functools
import os import os
import os.path import os.path
import shutil import shutil
import stat
import subprocess import subprocess
import tarfile import tarfile
import tempfile import tempfile
import pkg_resources import pkg_resources
from pre_commit import five
@contextlib.contextmanager @contextlib.contextmanager
def cwd(path): def cwd(path):
@ -46,7 +50,7 @@ def clean_path_on_failure(path):
yield yield
except BaseException: except BaseException:
if os.path.exists(path): if os.path.exists(path):
shutil.rmtree(path) rmtree(path)
raise raise
@ -78,7 +82,7 @@ def tmpdir():
try: try:
yield tempdir yield tempdir
finally: finally:
shutil.rmtree(tempdir) rmtree(tempdir)
def resource_filename(filename): def resource_filename(filename):
@ -135,6 +139,13 @@ def cmd_output(*cmd, **kwargs):
if stdin is not None: if stdin is not None:
stdin = stdin.encode('UTF-8') stdin = stdin.encode('UTF-8')
# py2/py3 on windows are more strict about the types here
cmd = [five.n(arg) for arg in cmd]
kwargs['env'] = dict(
(five.n(key), five.n(value))
for key, value in kwargs.pop('env', {}).items()
) or None
popen_kwargs.update(kwargs) popen_kwargs.update(kwargs)
proc = __popen(cmd, **popen_kwargs) proc = __popen(cmd, **popen_kwargs)
stdout, stderr = proc.communicate(stdin) stdout, stderr = proc.communicate(stdin)
@ -150,3 +161,15 @@ def cmd_output(*cmd, **kwargs):
) )
return proc.returncode, stdout, stderr return proc.returncode, stdout, stderr
def rmtree(path):
"""On windows, rmtree fails for readonly dirs."""
def handle_remove_readonly(func, path, exc): # pragma: no cover (windows)
excvalue = exc[1]
if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES:
os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
func(path)
else:
raise
shutil.rmtree(path, ignore_errors=False, onerror=handle_remove_readonly)

View file

@ -1,5 +1,5 @@
- id: system-hook-with-spaces - id: system-hook-with-spaces
name: System hook with spaces name: System hook with spaces
entry: /usr/bin/python -c 'import sys; print("Hello World")' entry: bash -c 'echo "Hello World"'
language: system language: system
files: \.sh$ files: \.sh$

View file

@ -49,8 +49,27 @@ def is_valid_according_to_schema(obj, schema):
return False return False
def skipif_slowtests_false(func): skipif_slowtests_false = pytest.mark.skipif(
return pytest.mark.skipif(
os.environ.get('slowtests') == 'false', os.environ.get('slowtests') == 'false',
reason='slowtests=false', reason='slowtests=false',
)(func) )
xfailif_windows_no_ruby = pytest.mark.xfail(
os.name == 'nt',
reason='Ruby support not yet implemented on windows.',
)
xfailif_windows_no_node = pytest.mark.xfail(
os.name == 'nt',
reason='Node support not yet implemented on windows.',
)
def platform_supports_pcre():
return cmd_output('grep', '-P', '', os.devnull, retcode=None)[0] == 1
xfailif_no_pcre_support = pytest.mark.xfail(
not platform_supports_pcre(),
reason='grep -P is not supported on this platform',
)

View file

@ -1,9 +1,9 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import os.path import os.path
import shutil
from pre_commit.commands.clean import clean from pre_commit.commands.clean import clean
from pre_commit.util import rmtree
def test_clean(runner_with_mocked_store): def test_clean(runner_with_mocked_store):
@ -14,7 +14,7 @@ def test_clean(runner_with_mocked_store):
def test_clean_empty(runner_with_mocked_store): def test_clean_empty(runner_with_mocked_store):
"""Make sure clean succeeds when we the directory doesn't exist.""" """Make sure clean succeeds when we the directory doesn't exist."""
shutil.rmtree(runner_with_mocked_store.store.directory) rmtree(runner_with_mocked_store.store.directory)
assert not os.path.exists(runner_with_mocked_store.store.directory) assert not os.path.exists(runner_with_mocked_store.store.directory)
clean(runner_with_mocked_store) clean(runner_with_mocked_store)
assert not os.path.exists(runner_with_mocked_store.store.directory) assert not os.path.exists(runner_with_mocked_store.store.directory)

View file

@ -5,7 +5,6 @@ import io
import os import os
import os.path import os.path
import re import re
import stat
import subprocess import subprocess
import sys import sys
@ -63,8 +62,7 @@ def test_install_pre_commit(tmpdir_factory):
pre_push='' pre_push=''
) )
assert pre_commit_contents == expected_contents assert pre_commit_contents == expected_contents
stat_result = os.stat(runner.pre_commit_path) assert os.access(runner.pre_commit_path, os.X_OK)
assert stat_result.st_mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
ret = install(runner, hook_type='pre-push') ret = install(runner, hook_type='pre-push')
assert ret == 0 assert ret == 0
@ -120,19 +118,19 @@ def _get_commit_output(
# osx does this different :( # osx does this different :(
FILES_CHANGED = ( FILES_CHANGED = (
r'(' r'('
r' 1 file changed, 0 insertions\(\+\), 0 deletions\(-\)\n' r' 1 file changed, 0 insertions\(\+\), 0 deletions\(-\)\r?\n'
r'|' r'|'
r' 0 files changed\n' r' 0 files changed\r?\n'
r')' r')'
) )
NORMAL_PRE_COMMIT_RUN = re.compile( NORMAL_PRE_COMMIT_RUN = re.compile(
r'^\[INFO\] Initializing environment for .+\.\n' r'^\[INFO\] Initializing environment for .+\.\r?\n'
r'Bash hook\.+Passed\n' r'Bash hook\.+Passed\r?\n'
r'\[master [a-f0-9]{7}\] Commit!\n' + r'\[master [a-f0-9]{7}\] Commit!\r?\n' +
FILES_CHANGED + FILES_CHANGED +
r' create mode 100644 foo\n$' r' create mode 100644 foo\r?\n$'
) )
@ -166,7 +164,7 @@ def test_environment_not_sourced(tmpdir_factory):
ret, stdout, stderr = cmd_output( ret, stdout, stderr = cmd_output(
'git', 'commit', '--allow-empty', '-m', 'foo', 'git', 'commit', '--allow-empty', '-m', 'foo',
env={'HOME': os.environ['HOME']}, env={'HOME': os.path.expanduser('~')},
retcode=None, retcode=None,
) )
assert ret == 1 assert ret == 1
@ -178,13 +176,13 @@ def test_environment_not_sourced(tmpdir_factory):
FAILING_PRE_COMMIT_RUN = re.compile( FAILING_PRE_COMMIT_RUN = re.compile(
r'^\[INFO\] Initializing environment for .+\.\n' r'^\[INFO\] Initializing environment for .+\.\r?\n'
r'Failing hook\.+Failed\n' r'Failing hook\.+Failed\r?\n'
r'hookid: failing_hook\n' r'hookid: failing_hook\r?\n'
r'\n' r'\r?\n'
r'Fail\n' r'Fail\r?\n'
r'foo\n' r'foo\r?\n'
r'\n$' r'\r?\n$'
) )
@ -199,10 +197,10 @@ def test_failing_hooks_returns_nonzero(tmpdir_factory):
EXISTING_COMMIT_RUN = re.compile( EXISTING_COMMIT_RUN = re.compile(
r'^legacy hook\n' r'^legacy hook\r?\n'
r'\[master [a-f0-9]{7}\] Commit!\n' + r'\[master [a-f0-9]{7}\] Commit!\r?\n' +
FILES_CHANGED + FILES_CHANGED +
r' create mode 100644 baz\n$' r' create mode 100644 baz\r?\n$'
) )
@ -253,9 +251,9 @@ def test_install_existing_hook_no_overwrite_idempotent(tmpdir_factory):
FAIL_OLD_HOOK = re.compile( FAIL_OLD_HOOK = re.compile(
r'fail!\n' r'fail!\r?\n'
r'\[INFO\] Initializing environment for .+\.\n' r'\[INFO\] Initializing environment for .+\.\r?\n'
r'Bash hook\.+Passed\n' r'Bash hook\.+Passed\r?\n'
) )
@ -363,10 +361,10 @@ def test_uninstall_doesnt_remove_not_our_hooks(tmpdir_factory):
PRE_INSTALLED = re.compile( PRE_INSTALLED = re.compile(
r'Bash hook\.+Passed\n' r'Bash hook\.+Passed\r?\n'
r'\[master [a-f0-9]{7}\] Commit!\n' + r'\[master [a-f0-9]{7}\] Commit!\r?\n' +
FILES_CHANGED + FILES_CHANGED +
r' create mode 100644 foo\n$' r' create mode 100644 foo\r?\n$'
) )
@ -394,8 +392,10 @@ def test_installed_from_venv(tmpdir_factory):
ret, output = _get_commit_output( ret, output = _get_commit_output(
tmpdir_factory, tmpdir_factory,
env_base={ env_base={
'HOME': os.environ['HOME'], 'HOME': os.path.expanduser('~'),
'TERM': os.environ.get('TERM', ''), 'TERM': os.environ.get('TERM', ''),
# Windows needs this to import `random`
'SYSTEMROOT': os.environ.get('SYSTEMROOT', ''),
}, },
) )
assert ret == 0 assert ret == 0

View file

@ -275,7 +275,9 @@ def test_stdout_write_bug_py26(
): ):
with cwd(repo_with_failing_hook): with cwd(repo_with_failing_hook):
# Add bash hook on there again # Add bash hook on there again
with io.open('.pre-commit-config.yaml', 'a+') as config_file: with io.open(
'.pre-commit-config.yaml', 'a+', encoding='UTF-8',
) as config_file:
config_file.write(' args: [""]\n') config_file.write(' args: [""]\n')
cmd_output('git', 'add', '.pre-commit-config.yaml') cmd_output('git', 'add', '.pre-commit-config.yaml')
stage_a_file() stage_a_file()

View file

@ -37,13 +37,13 @@ def test_error_handler_fatal_error(mocked_log_and_exit):
) )
assert re.match( assert re.match(
'Traceback \(most recent call last\):\n' r'Traceback \(most recent call last\):\n'
' File ".+/pre_commit/error_handler.py", line \d+, in error_handler\n' r' File ".+pre_commit.error_handler.py", line \d+, in error_handler\n'
' yield\n' r' yield\n'
' File ".+/tests/error_handler_test.py", line \d+, ' r' File ".+tests.error_handler_test.py", line \d+, '
'in test_error_handler_fatal_error\n' r'in test_error_handler_fatal_error\n'
' raise exc\n' r' raise exc\n'
'(pre_commit\.errors\.)?FatalError: just a test\n', r'(pre_commit\.errors\.)?FatalError: just a test\n',
mocked_log_and_exit.call_args[0][2], mocked_log_and_exit.call_args[0][2],
) )
@ -60,13 +60,13 @@ def test_error_handler_uncaught_error(mocked_log_and_exit):
mock.ANY, mock.ANY,
) )
assert re.match( assert re.match(
'Traceback \(most recent call last\):\n' r'Traceback \(most recent call last\):\n'
' File ".+/pre_commit/error_handler.py", line \d+, in error_handler\n' r' File ".+pre_commit.error_handler.py", line \d+, in error_handler\n'
' yield\n' r' yield\n'
' File ".+/tests/error_handler_test.py", line \d+, ' r' File ".+tests.error_handler_test.py", line \d+, '
'in test_error_handler_uncaught_error\n' r'in test_error_handler_uncaught_error\n'
' raise exc\n' r' raise exc\n'
'ValueError: another test\n', r'ValueError: another test\n',
mocked_log_and_exit.call_args[0][2], mocked_log_and_exit.call_args[0][2],
) )

View file

@ -3,8 +3,10 @@ from __future__ import unicode_literals
import os.path import os.path
from pre_commit.languages.ruby import _install_rbenv from pre_commit.languages.ruby import _install_rbenv
from testing.util import xfailif_windows_no_ruby
@xfailif_windows_no_ruby
def test_install_rbenv(cmd_runner): def test_install_rbenv(cmd_runner):
_install_rbenv(cmd_runner) _install_rbenv(cmd_runner)
# Should have created rbenv directory # Should have created rbenv directory
@ -18,11 +20,12 @@ def test_install_rbenv(cmd_runner):
[ [
'bash', 'bash',
'-c', '-c',
'. {prefix}/rbenv/bin/activate && rbenv --help', ". '{prefix}rbenv/bin/activate' && rbenv --help",
], ],
) )
@xfailif_windows_no_ruby
def test_install_rbenv_with_version(cmd_runner): def test_install_rbenv_with_version(cmd_runner):
_install_rbenv(cmd_runner, version='1.9.3p547') _install_rbenv(cmd_runner, version='1.9.3p547')
@ -31,6 +34,6 @@ def test_install_rbenv_with_version(cmd_runner):
[ [
'bash', 'bash',
'-c', '-c',
'. {prefix}/rbenv/bin/activate && rbenv install --help', ". '{prefix}rbenv/bin/activate' && rbenv install --help",
], ],
) )

View file

@ -6,14 +6,22 @@ import subprocess
import mock import mock
import pytest import pytest
from pre_commit import five
from pre_commit.prefixed_command_runner import _replace_cmd from pre_commit.prefixed_command_runner import _replace_cmd
from pre_commit.prefixed_command_runner import PrefixedCommandRunner from pre_commit.prefixed_command_runner import PrefixedCommandRunner
from pre_commit.util import CalledProcessError from pre_commit.util import CalledProcessError
def norm_slash(input_tup):
return tuple(x.replace('/', os.sep) for x in input_tup)
def test_CalledProcessError_str(): def test_CalledProcessError_str():
error = CalledProcessError( error = CalledProcessError(
1, [str('git'), str('status')], 0, (str('stdout'), str('stderr')) 1,
[five.n('git'), five.n('status')],
0,
(five.n('stdout'), five.n('stderr')),
) )
assert str(error) == ( assert str(error) == (
"Command: ['git', 'status']\n" "Command: ['git', 'status']\n"
@ -28,7 +36,7 @@ def test_CalledProcessError_str():
def test_CalledProcessError_str_nooutput(): def test_CalledProcessError_str_nooutput():
error = CalledProcessError( error = CalledProcessError(
1, [str('git'), str('status')], 0, (str(''), str('')) 1, [five.n('git'), five.n('status')], 0, (five.n(''), five.n(''))
) )
assert str(error) == ( assert str(error) == (
"Command: ['git', 'status']\n" "Command: ['git', 'status']\n"
@ -65,13 +73,15 @@ def test_replace_cmd(input, kwargs, expected_output):
@pytest.mark.parametrize(('input', 'expected_prefix'), ( @pytest.mark.parametrize(('input', 'expected_prefix'), (
('.', './'), norm_slash(('.', './')),
('foo', 'foo/'), norm_slash(('foo', 'foo/')),
('bar/', 'bar/'), norm_slash(('bar/', 'bar/')),
('foo/bar', 'foo/bar/'), norm_slash(('foo/bar', 'foo/bar/')),
('foo/bar/', 'foo/bar/'), norm_slash(('foo/bar/', 'foo/bar/')),
)) ))
def test_init_normalizes_path_endings(input, expected_prefix): def test_init_normalizes_path_endings(input, expected_prefix):
input = input.replace('/', os.sep)
expected_prefix = expected_prefix.replace('/', os.sep)
instance = PrefixedCommandRunner(input) instance = PrefixedCommandRunner(input)
assert instance.prefix_dir == expected_prefix assert instance.prefix_dir == expected_prefix
@ -82,7 +92,8 @@ def test_run_substitutes_prefix(popen_mock, makedirs_mock):
) )
ret = instance.run(['{prefix}bar', 'baz'], retcode=None) ret = instance.run(['{prefix}bar', 'baz'], retcode=None)
popen_mock.assert_called_once_with( popen_mock.assert_called_once_with(
('prefix/bar', 'baz'), [five.n(os.path.join('prefix', 'bar')), five.n('baz')],
env=None,
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
@ -91,12 +102,12 @@ def test_run_substitutes_prefix(popen_mock, makedirs_mock):
PATH_TESTS = ( PATH_TESTS = (
('foo', '', 'foo'), norm_slash(('foo', '', 'foo')),
('foo', 'bar', 'foo/bar'), norm_slash(('foo', 'bar', 'foo/bar')),
('foo/bar', '../baz', 'foo/baz'), norm_slash(('foo/bar', '../baz', 'foo/baz')),
('./', 'bar', 'bar'), norm_slash(('./', 'bar', 'bar')),
('./', '', '.'), norm_slash(('./', '', '.')),
('/tmp/foo', '/tmp/bar', '/tmp/bar'), norm_slash(('/tmp/foo', '/tmp/bar', '/tmp/bar')),
) )
@ -110,7 +121,7 @@ def test_path(prefix, path_end, expected_output):
def test_path_multiple_args(): def test_path_multiple_args():
instance = PrefixedCommandRunner('foo') instance = PrefixedCommandRunner('foo')
ret = instance.path('bar', 'baz') ret = instance.path('bar', 'baz')
assert ret == 'foo/bar/baz' assert ret == os.path.join('foo', 'bar', 'baz')
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -133,12 +144,13 @@ def test_from_command_runner_preserves_popen(popen_mock, makedirs_mock):
second = PrefixedCommandRunner.from_command_runner(first, 'bar') second = PrefixedCommandRunner.from_command_runner(first, 'bar')
second.run(['foo/bar/baz'], retcode=None) second.run(['foo/bar/baz'], retcode=None)
popen_mock.assert_called_once_with( popen_mock.assert_called_once_with(
('foo/bar/baz',), [five.n('foo/bar/baz')],
env=None,
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
) )
makedirs_mock.assert_called_once_with('foo/bar/') makedirs_mock.assert_called_once_with(os.path.join('foo', 'bar') + os.sep)
def test_create_path_if_not_exists(in_tmpdir): def test_create_path_if_not_exists(in_tmpdir):

View file

@ -19,6 +19,9 @@ from testing.fixtures import git_dir
from testing.fixtures import make_config_from_repo from testing.fixtures import make_config_from_repo
from testing.fixtures import make_repo from testing.fixtures import make_repo
from testing.util import skipif_slowtests_false from testing.util import skipif_slowtests_false
from testing.util import xfailif_no_pcre_support
from testing.util import xfailif_windows_no_node
from testing.util import xfailif_windows_no_ruby
def _test_hook_repo( def _test_hook_repo(
@ -39,14 +42,14 @@ def _test_hook_repo(
][0] ][0]
ret = repo.run_hook(hook_dict, args) ret = repo.run_hook(hook_dict, args)
assert ret[0] == expected_return_code assert ret[0] == expected_return_code
assert ret[1] == expected assert ret[1].replace('\r\n', '\n') == expected
@pytest.mark.integration @pytest.mark.integration
def test_python_hook(tmpdir_factory, store): def test_python_hook(tmpdir_factory, store):
_test_hook_repo( _test_hook_repo(
tmpdir_factory, store, 'python_hooks_repo', tmpdir_factory, store, 'python_hooks_repo',
'foo', ['/dev/null'], "['/dev/null']\nHello World\n", 'foo', [os.devnull], "['{0}']\nHello World\n".format(os.devnull),
) )
@ -71,11 +74,14 @@ def test_python_hook_args_with_spaces(tmpdir_factory, store):
def test_versioned_python_hook(tmpdir_factory, store): def test_versioned_python_hook(tmpdir_factory, store):
_test_hook_repo( _test_hook_repo(
tmpdir_factory, store, 'python3_hooks_repo', tmpdir_factory, store, 'python3_hooks_repo',
'python3-hook', ['/dev/null'], "3.3\n['/dev/null']\nHello World\n", 'python3-hook',
[os.devnull],
"3.3\n['{0}']\nHello World\n".format(os.devnull),
) )
@skipif_slowtests_false @skipif_slowtests_false
@xfailif_windows_no_node
@pytest.mark.integration @pytest.mark.integration
def test_run_a_node_hook(tmpdir_factory, store): def test_run_a_node_hook(tmpdir_factory, store):
_test_hook_repo( _test_hook_repo(
@ -85,6 +91,7 @@ def test_run_a_node_hook(tmpdir_factory, store):
@skipif_slowtests_false @skipif_slowtests_false
@xfailif_windows_no_node
@pytest.mark.integration @pytest.mark.integration
def test_run_versioned_node_hook(tmpdir_factory, store): def test_run_versioned_node_hook(tmpdir_factory, store):
_test_hook_repo( _test_hook_repo(
@ -94,6 +101,7 @@ def test_run_versioned_node_hook(tmpdir_factory, store):
@skipif_slowtests_false @skipif_slowtests_false
@xfailif_windows_no_ruby
@pytest.mark.integration @pytest.mark.integration
def test_run_a_ruby_hook(tmpdir_factory, store): def test_run_a_ruby_hook(tmpdir_factory, store):
_test_hook_repo( _test_hook_repo(
@ -103,6 +111,7 @@ def test_run_a_ruby_hook(tmpdir_factory, store):
@skipif_slowtests_false @skipif_slowtests_false
@xfailif_windows_no_ruby
@pytest.mark.integration @pytest.mark.integration
def test_run_versioned_ruby_hook(tmpdir_factory, store): def test_run_versioned_ruby_hook(tmpdir_factory, store):
_test_hook_repo( _test_hook_repo(
@ -139,6 +148,7 @@ def test_run_hook_with_spaced_args(tmpdir_factory, store):
) )
@xfailif_no_pcre_support
@pytest.mark.integration @pytest.mark.integration
def test_pcre_hook_no_match(tmpdir_factory, store): def test_pcre_hook_no_match(tmpdir_factory, store):
path = git_dir(tmpdir_factory) path = git_dir(tmpdir_factory)
@ -160,6 +170,7 @@ def test_pcre_hook_no_match(tmpdir_factory, store):
) )
@xfailif_no_pcre_support
@pytest.mark.integration @pytest.mark.integration
def test_pcre_hook_matching(tmpdir_factory, store): def test_pcre_hook_matching(tmpdir_factory, store):
path = git_dir(tmpdir_factory) path = git_dir(tmpdir_factory)
@ -183,6 +194,7 @@ def test_pcre_hook_matching(tmpdir_factory, store):
) )
@xfailif_no_pcre_support
@pytest.mark.integration @pytest.mark.integration
def test_pcre_many_files(tmpdir_factory, store): def test_pcre_many_files(tmpdir_factory, store):
# This is intended to simulate lots of passing files and one failing file # This is intended to simulate lots of passing files and one failing file
@ -202,6 +214,14 @@ def test_pcre_many_files(tmpdir_factory, store):
) )
def _norm_pwd(path):
# Under windows bash's temp and windows temp is different.
# This normalizes to the bash /tmp
return cmd_output(
'bash', '-c', "cd '{0}' && pwd".format(path),
)[1].strip()
@pytest.mark.integration @pytest.mark.integration
def test_cwd_of_hook(tmpdir_factory, store): def test_cwd_of_hook(tmpdir_factory, store):
# Note: this doubles as a test for `system` hooks # Note: this doubles as a test for `system` hooks
@ -209,7 +229,7 @@ def test_cwd_of_hook(tmpdir_factory, store):
with cwd(path): with cwd(path):
_test_hook_repo( _test_hook_repo(
tmpdir_factory, store, 'prints_cwd_repo', tmpdir_factory, store, 'prints_cwd_repo',
'prints_cwd', ['-L'], path + '\n', 'prints_cwd', ['-L'], _norm_pwd(path) + '\n',
) )
@ -288,7 +308,9 @@ def test_control_c_control_c_on_install(tmpdir_factory, store):
with mock.patch.object( with mock.patch.object(
PythonEnv, 'run', side_effect=MyKeyboardInterrupt, PythonEnv, 'run', side_effect=MyKeyboardInterrupt,
): ):
with mock.patch.object(shutil, 'rmtree', MyKeyboardInterrupt): with mock.patch.object(
shutil, 'rmtree', side_effect=MyKeyboardInterrupt,
):
repo.run_hook(hook, []) repo.run_hook(hook, [])
# Should have made an environment, however this environment is broken! # Should have made an environment, however this environment is broken!
@ -347,7 +369,7 @@ def test_tags_on_repositories(in_tmpdir, tmpdir_factory, store):
) )
ret = repo_1.run_hook(repo_1.hooks[0][1], ['-L']) ret = repo_1.run_hook(repo_1.hooks[0][1], ['-L'])
assert ret[0] == 0 assert ret[0] == 0
assert ret[1].strip() == in_tmpdir assert ret[1].strip() == _norm_pwd(in_tmpdir)
repo_2 = Repository.create( repo_2 = Repository.create(
make_config_from_repo(git_dir_2, sha=tag), store, make_config_from_repo(git_dir_2, sha=tag), store,

View file

@ -41,8 +41,8 @@ def test_create_changes_to_git_root(tmpdir_factory):
def test_config_file_path(): def test_config_file_path():
runner = Runner('foo/bar') runner = Runner(os.path.join('foo', 'bar'))
expected_path = os.path.join('foo/bar', C.CONFIG_FILE) expected_path = os.path.join('foo', 'bar', C.CONFIG_FILE)
assert runner.config_file_path == expected_path assert runner.config_file_path == expected_path
@ -53,18 +53,18 @@ def test_repositories(tmpdir_factory, mock_out_store_directory):
def test_pre_commit_path(): def test_pre_commit_path():
runner = Runner('foo/bar') runner = Runner(os.path.join('foo', 'bar'))
expected_path = os.path.join('foo/bar', '.git/hooks/pre-commit') expected_path = os.path.join('foo', 'bar', '.git', 'hooks', 'pre-commit')
assert runner.pre_commit_path == expected_path assert runner.pre_commit_path == expected_path
def test_pre_push_path(): def test_pre_push_path():
runner = Runner('foo/bar') runner = Runner(os.path.join('foo', 'bar'))
expected_path = os.path.join('foo/bar', '.git/hooks/pre-push') expected_path = os.path.join('foo', 'bar', '.git', 'hooks', 'pre-push')
assert runner.pre_push_path == expected_path assert runner.pre_push_path == expected_path
def test_cmd_runner(mock_out_store_directory): def test_cmd_runner(mock_out_store_directory):
runner = Runner('foo/bar') runner = Runner(os.path.join('foo', 'bar'))
ret = runner.cmd_runner ret = runner.cmd_runner
assert ret.prefix_dir == os.path.join(mock_out_store_directory) + '/' assert ret.prefix_dir == os.path.join(mock_out_store_directory) + os.sep

View file

@ -4,7 +4,6 @@ from __future__ import unicode_literals
import io import io
import os import os
import os.path import os.path
import shutil
import sqlite3 import sqlite3
import mock import mock
@ -15,6 +14,7 @@ from pre_commit.store import _get_default_directory
from pre_commit.store import Store from pre_commit.store import Store
from pre_commit.util import cmd_output from pre_commit.util import cmd_output
from pre_commit.util import cwd from pre_commit.util import cwd
from pre_commit.util import rmtree
from testing.fixtures import git_dir from testing.fixtures import git_dir
from testing.util import get_head_sha from testing.util import get_head_sha
@ -30,7 +30,7 @@ def test_our_session_fixture_works():
def test_get_default_directory_defaults_to_home(): def test_get_default_directory_defaults_to_home():
# Not we use the module level one which is not mocked # Not we use the module level one which is not mocked
ret = _get_default_directory() ret = _get_default_directory()
assert ret == os.path.join(os.environ['HOME'], '.pre-commit') assert ret == os.path.join(os.path.expanduser('~'), '.pre-commit')
def test_uses_environment_variable_when_present(): def test_uses_environment_variable_when_present():
@ -61,7 +61,7 @@ def test_store_require_created_does_not_create_twice(store):
store.require_created() store.require_created()
# We intentionally delete the directory here so we can figure out if it # We intentionally delete the directory here so we can figure out if it
# calls it again. # calls it again.
shutil.rmtree(store.directory) rmtree(store.directory)
assert not os.path.exists(store.directory) assert not os.path.exists(store.directory)
# Call require_created, this should not trigger a call to create # Call require_created, this should not trigger a call to create
store.require_created() store.require_created()