diff --git a/.travis.yml b/.travis.yml index 65dc39fb..c74827ff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ addons: - pandoc - git env: - - FUNCTIONAL=true BARE=true + - FUNCTIONAL=true install: - pip install coveralls - pip install -r requirements.txt @@ -23,5 +23,5 @@ install: - rm -rf build script: - export COVERAGE_PYTHON_VERSION=python-${TRAVIS_PYTHON_VERSION:0:1} - - coverage run --source=thefuck,tests -m py.test -v --capture=sys + - coverage run --source=thefuck,tests -m py.test -v --capture=sys --run-without-docker after_success: coveralls diff --git a/requirements.txt b/requirements.txt index 76ca36a2..05a9aea6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ setuptools>=17.1 pexpect pypandoc pytest-benchmark +pytest-docker-pexpect diff --git a/tests/functional/plots.py b/tests/functional/plots.py index 611742f2..87d49aa5 100644 --- a/tests/functional/plots.py +++ b/tests/functional/plots.py @@ -1,6 +1,3 @@ -from pexpect import TIMEOUT - - def _set_confirmation(proc, require): proc.sendline(u'mkdir -p ~/.thefuck') proc.sendline( @@ -8,7 +5,7 @@ def _set_confirmation(proc, require): require)) -def with_confirmation(proc): +def with_confirmation(proc, TIMEOUT): """Ensures that command can be fixed when confirmation enabled.""" _set_confirmation(proc, True) @@ -23,19 +20,19 @@ def with_confirmation(proc): assert proc.expect([TIMEOUT, u'test']) -def history_changed(proc, to): +def history_changed(proc, TIMEOUT, to): """Ensures that history changed.""" proc.send('\033[A') assert proc.expect([TIMEOUT, to]) -def history_not_changed(proc): +def history_not_changed(proc, TIMEOUT): """Ensures that history not changed.""" proc.send('\033[A') assert proc.expect([TIMEOUT, u'fuck']) -def select_command_with_arrows(proc): +def select_command_with_arrows(proc, TIMEOUT): """Ensures that command can be selected with arrow keys.""" _set_confirmation(proc, True) @@ -55,7 +52,7 @@ def select_command_with_arrows(proc): assert proc.expect([TIMEOUT, u'Not a git repository']) -def refuse_with_confirmation(proc): +def refuse_with_confirmation(proc, TIMEOUT): """Ensures that fix can be refused when confirmation enabled.""" _set_confirmation(proc, True) @@ -70,7 +67,7 @@ def refuse_with_confirmation(proc): assert proc.expect([TIMEOUT, u'Aborted']) -def without_confirmation(proc): +def without_confirmation(proc, TIMEOUT): """Ensures that command can be fixed when confirmation disabled.""" _set_confirmation(proc, False) diff --git a/tests/functional/test_bash.py b/tests/functional/test_bash.py index 849f7329..53d779b7 100644 --- a/tests/functional/test_bash.py +++ b/tests/functional/test_bash.py @@ -2,27 +2,28 @@ import pytest from tests.functional.plots import with_confirmation, without_confirmation, \ refuse_with_confirmation, history_changed, history_not_changed, \ select_command_with_arrows -from tests.functional.utils import spawn, functional, images +from tests.functional.utils import functional -containers = images(('ubuntu-python3-bash', u''' -FROM ubuntu:latest -RUN apt-get update -RUN apt-get install -yy python3 python3-pip python3-dev git -RUN pip3 install -U setuptools -RUN ln -s /usr/bin/pip3 /usr/bin/pip -'''), - ('ubuntu-python2-bash', u''' -FROM ubuntu:latest -RUN apt-get update -RUN apt-get install -yy python python-pip python-dev git -RUN pip2 install -U pip setuptools -''')) +containers = ((u'thefuck/ubuntu-python3-bash', + u'''FROM ubuntu:latest + RUN apt-get update + RUN apt-get install -yy python3 python3-pip python3-dev git + RUN pip3 install -U setuptools + RUN ln -s /usr/bin/pip3 /usr/bin/pip''', + u'bash'), + (u'thefuck/ubuntu-python2-bash', + u'''FROM ubuntu:latest + RUN apt-get update + RUN apt-get install -yy python python-pip python-dev git + RUN pip2 install -U pip setuptools''', + u'bash')) @pytest.fixture(params=containers) -def proc(request): - tag, dockerfile = request.param - proc = spawn(request, tag, dockerfile, u'bash') +def proc(request, spawnu, run_without_docker): + proc = spawnu(*request.param) + if not run_without_docker: + proc.sendline(u"pip install /src") proc.sendline(u"export PS1='$ '") proc.sendline(u'eval $(thefuck --alias)') proc.sendline(u'echo > $HISTFILE') @@ -30,24 +31,24 @@ def proc(request): @functional -def test_with_confirmation(proc): - with_confirmation(proc) - history_changed(proc, u'echo test') +def test_with_confirmation(proc, TIMEOUT): + with_confirmation(proc, TIMEOUT) + history_changed(proc, TIMEOUT, u'echo test') @functional -def test_select_command_with_arrows(proc): - select_command_with_arrows(proc) - history_changed(proc, u'git push') +def test_select_command_with_arrows(proc, TIMEOUT): + select_command_with_arrows(proc, TIMEOUT) + history_changed(proc, TIMEOUT, u'git push') @functional -def test_refuse_with_confirmation(proc): - refuse_with_confirmation(proc) - history_not_changed(proc) +def test_refuse_with_confirmation(proc, TIMEOUT): + refuse_with_confirmation(proc, TIMEOUT) + history_not_changed(proc, TIMEOUT) @functional -def test_without_confirmation(proc): - without_confirmation(proc) - history_changed(proc, u'echo test') +def test_without_confirmation(proc, TIMEOUT): + without_confirmation(proc, TIMEOUT) + history_changed(proc, TIMEOUT, u'echo test') diff --git a/tests/functional/test_fish.py b/tests/functional/test_fish.py index a70efa10..636fb3c5 100644 --- a/tests/functional/test_fish.py +++ b/tests/functional/test_fish.py @@ -1,59 +1,55 @@ import pytest from tests.functional.plots import with_confirmation, without_confirmation, \ refuse_with_confirmation, select_command_with_arrows -from tests.functional.utils import spawn, functional, images, bare +from tests.functional.utils import functional -containers = images(('ubuntu-python3-fish', u''' -FROM ubuntu:latest -RUN apt-get update -RUN apt-get install -yy python3 python3-pip python3-dev fish git -RUN pip3 install -U setuptools -RUN ln -s /usr/bin/pip3 /usr/bin/pip -RUN apt-get install -yy fish -'''), - ('ubuntu-python2-fish', u''' -FROM ubuntu:latest -RUN apt-get update -RUN apt-get install -yy python python-pip python-dev git -RUN pip2 install -U pip setuptools -RUN apt-get install -yy fish -''')) +containers = (('thefuck/ubuntu-python3-fish', + u'''FROM ubuntu:latest + RUN apt-get update + RUN apt-get install -yy python3 python3-pip python3-dev fish git + RUN pip3 install -U setuptools + RUN ln -s /usr/bin/pip3 /usr/bin/pip + RUN apt-get install -yy fish''', + u'fish'), + ('thefuck/ubuntu-python2-fish', + u'''FROM ubuntu:latest + RUN apt-get update + RUN apt-get install -yy python python-pip python-dev git + RUN pip2 install -U pip setuptools + RUN apt-get install -yy fish''', + u'fish')) @pytest.fixture(params=containers) -def proc(request): - tag, dockerfile = request.param - proc = spawn(request, tag, dockerfile, u'fish') +def proc(request, spawnu): + proc = spawnu(*request.param) + proc.sendline(u"pip install /src") proc.sendline(u'thefuck --alias > ~/.config/fish/config.fish') proc.sendline(u'fish') return proc @functional -@pytest.mark.skipif( - bool(bare), reason='https://github.com/travis-ci/apt-source-whitelist/issues/71') -def test_with_confirmation(proc): - with_confirmation(proc) +@pytest.mark.skip_without_docker +def test_with_confirmation(proc, TIMEOUT): + with_confirmation(proc, TIMEOUT) @functional -@pytest.mark.skipif( - bool(bare), reason='https://github.com/travis-ci/apt-source-whitelist/issues/71') -def test_select_command_with_arrows(proc): - select_command_with_arrows(proc) +@pytest.mark.skip_without_docker +def test_select_command_with_arrows(proc, TIMEOUT): + select_command_with_arrows(proc, TIMEOUT) @functional -@pytest.mark.skipif( - bool(bare), reason='https://github.com/travis-ci/apt-source-whitelist/issues/71') -def test_refuse_with_confirmation(proc): - refuse_with_confirmation(proc) +@pytest.mark.skip_without_docker +def test_refuse_with_confirmation(proc, TIMEOUT): + refuse_with_confirmation(proc, TIMEOUT) @functional -@pytest.mark.skipif( - bool(bare), reason='https://github.com/travis-ci/apt-source-whitelist/issues/71') -def test_without_confirmation(proc): - without_confirmation(proc) +@pytest.mark.skip_without_docker +def test_without_confirmation(proc, TIMEOUT): + without_confirmation(proc, TIMEOUT) # TODO: ensure that history changes. diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index cd4b9faa..2fece023 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -1,25 +1,26 @@ import pytest -from pexpect import TIMEOUT -from tests.functional.utils import spawn, functional, bare +from thefuck.main import _get_current_version +from tests.functional.utils import functional -envs = ((u'bash', 'ubuntu-bash', u''' + +envs = ((u'bash', 'thefuck/ubuntu-bash', u''' FROM ubuntu:latest RUN apt-get update RUN apt-get install -yy bash -'''), (u'bash', 'generic-bash', u''' +'''), (u'bash', 'thefuck/generic-bash', u''' FROM fedora:latest RUN dnf install -yy python-devel sudo wget gcc ''')) @functional -@pytest.mark.skipif( - bool(bare), reason="Can't be tested in bare run") +@pytest.mark.skip_without_docker @pytest.mark.parametrize('shell, tag, dockerfile', envs) -def test_installation(request, shell, tag, dockerfile): - proc = spawn(request, tag, dockerfile, shell, install=False) +def test_installation(spawnu, shell, TIMEOUT, tag, dockerfile): + proc = spawnu(tag, dockerfile, shell) proc.sendline(u'cat /src/install.sh | sh - && $0') proc.sendline(u'thefuck --version') - assert proc.expect([TIMEOUT, u'The Fuck'], timeout=600) + assert proc.expect([TIMEOUT, u'thefuck {}'.format(_get_current_version())], + timeout=600) proc.sendline(u'fuck') assert proc.expect([TIMEOUT, u'No fucks given']) diff --git a/tests/functional/test_performance.py b/tests/functional/test_performance.py index 2310f158..1582a875 100644 --- a/tests/functional/test_performance.py +++ b/tests/functional/test_performance.py @@ -1,7 +1,6 @@ -from pexpect import TIMEOUT import pytest import time -from tests.functional.utils import spawn, functional, bare +from tests.functional.utils import functional dockerfile = u''' FROM ubuntu:latest @@ -11,24 +10,17 @@ RUN pip3 install -U setuptools RUN ln -s /usr/bin/pip3 /usr/bin/pip RUN adduser --disabled-password --gecos '' test ENV SEED "{seed}" -COPY thefuck /src WORKDIR /src -RUN pip install . USER test RUN echo 'eval $(thefuck --alias)' > /home/test/.bashrc RUN echo > /home/test/.bash_history RUN git config --global user.email "you@example.com" RUN git config --global user.name "Your Name" +USER root '''.format(seed=time.time()) -@pytest.fixture -def proc(request): - return spawn(request, 'ubuntu-python3-bash-performance', - dockerfile, u'bash', install=False, copy_src=True) - - -def plot(proc): +def plot(proc, TIMEOUT): proc.sendline(u'cd /home/test/') proc.sendline(u'fuck') assert proc.expect([TIMEOUT, u'No fucks given']) @@ -49,8 +41,11 @@ def plot(proc): @functional -@pytest.mark.skipif( - bool(bare), reason='Would lie on a bare run') +@pytest.mark.skip_without_docker @pytest.mark.benchmark(min_rounds=10) -def test_performance(proc, benchmark): - assert benchmark(plot, proc) is None +def test_performance(spawnu, TIMEOUT, benchmark): + proc = spawnu(u'thefuck/ubuntu-python3-bash-performance', + dockerfile, u'bash') + proc.sendline(u'pip install /src') + proc.sendline(u'su test') + assert benchmark(plot, proc, TIMEOUT) is None diff --git a/tests/functional/test_tcsh.py b/tests/functional/test_tcsh.py index 8114e205..0614cdb1 100644 --- a/tests/functional/test_tcsh.py +++ b/tests/functional/test_tcsh.py @@ -1,51 +1,52 @@ import pytest -from tests.functional.utils import spawn, functional, images +from tests.functional.utils import functional from tests.functional.plots import with_confirmation, without_confirmation, \ refuse_with_confirmation, select_command_with_arrows -containers = images(('ubuntu-python3-tcsh', u''' -FROM ubuntu:latest -RUN apt-get update -RUN apt-get install -yy python3 python3-pip python3-dev git -RUN pip3 install -U setuptools -RUN ln -s /usr/bin/pip3 /usr/bin/pip -RUN apt-get install -yy tcsh -'''), - ('ubuntu-python2-tcsh', u''' -FROM ubuntu:latest -RUN apt-get update -RUN apt-get install -yy python python-pip python-dev git -RUN pip2 install -U pip setuptools -RUN apt-get install -yy tcsh -''')) +containers = (('thefuck/ubuntu-python3-tcsh', + u'''FROM ubuntu:latest + RUN apt-get update + RUN apt-get install -yy python3 python3-pip python3-dev git + RUN pip3 install -U setuptools + RUN ln -s /usr/bin/pip3 /usr/bin/pip + RUN apt-get install -yy tcsh''', + u'tcsh'), + ('thefuck/ubuntu-python2-tcsh', + u'''FROM ubuntu:latest + RUN apt-get update + RUN apt-get install -yy python python-pip python-dev git + RUN pip2 install -U pip setuptools + RUN apt-get install -yy tcsh''', + u'tcsh')) @pytest.fixture(params=containers) -def proc(request): - tag, dockerfile = request.param - proc = spawn(request, tag, dockerfile, u'tcsh') +def proc(request, spawnu, run_without_docker): + proc = spawnu(*request.param) + if not run_without_docker: + proc.sendline(u'pip install /src') proc.sendline(u'tcsh') proc.sendline(u'eval `thefuck --alias`') return proc @functional -def test_with_confirmation(proc): - with_confirmation(proc) +def test_with_confirmation(proc, TIMEOUT): + with_confirmation(proc, TIMEOUT) @functional -def test_select_command_with_arrows(proc): - select_command_with_arrows(proc) +def test_select_command_with_arrows(proc, TIMEOUT): + select_command_with_arrows(proc, TIMEOUT) @functional -def test_refuse_with_confirmation(proc): - refuse_with_confirmation(proc) +def test_refuse_with_confirmation(proc, TIMEOUT): + refuse_with_confirmation(proc, TIMEOUT) @functional -def test_without_confirmation(proc): - without_confirmation(proc) +def test_without_confirmation(proc, TIMEOUT): + without_confirmation(proc, TIMEOUT) # TODO: ensure that history changes. diff --git a/tests/functional/test_zsh.py b/tests/functional/test_zsh.py index 01d0584a..498d22ed 100644 --- a/tests/functional/test_zsh.py +++ b/tests/functional/test_zsh.py @@ -1,29 +1,30 @@ import pytest -from tests.functional.utils import spawn, functional, images +from tests.functional.utils import functional from tests.functional.plots import with_confirmation, without_confirmation, \ refuse_with_confirmation, history_changed, history_not_changed, select_command_with_arrows -containers = images(('ubuntu-python3-zsh', u''' -FROM ubuntu:latest -RUN apt-get update -RUN apt-get install -yy python3 python3-pip python3-dev git -RUN pip3 install -U setuptools -RUN ln -s /usr/bin/pip3 /usr/bin/pip -RUN apt-get install -yy zsh -'''), - ('ubuntu-python2-zsh', u''' -FROM ubuntu:latest -RUN apt-get update -RUN apt-get install -yy python python-pip python-dev git -RUN pip2 install -U pip setuptools -RUN apt-get install -yy zsh -''')) +containers = (('ubuntu-python3-zsh', + u'''FROM ubuntu:latest + RUN apt-get update + RUN apt-get install -yy python3 python3-pip python3-dev git + RUN pip3 install -U setuptools + RUN ln -s /usr/bin/pip3 /usr/bin/pip + RUN apt-get install -yy zsh''', + u'zsh'), + ('ubuntu-python2-zsh', + u'''FROM ubuntu:latest + RUN apt-get update + RUN apt-get install -yy python python-pip python-dev git + RUN pip2 install -U pip setuptools + RUN apt-get install -yy zsh''', + u'zsh')) @pytest.fixture(params=containers) -def proc(request): - tag, dockerfile = request.param - proc = spawn(request, tag, dockerfile, u'zsh') +def proc(request, spawnu, run_without_docker): + proc = spawnu(*request.param) + if not run_without_docker: + proc.sendline(u'pip install /src') proc.sendline(u'eval $(thefuck --alias)') proc.sendline(u'export HISTFILE=~/.zsh_history') proc.sendline(u'echo > $HISTFILE') @@ -34,24 +35,24 @@ def proc(request): @functional -def test_with_confirmation(proc): - with_confirmation(proc) - history_changed(proc, u'echo test') +def test_with_confirmation(proc, TIMEOUT): + with_confirmation(proc, TIMEOUT) + history_changed(proc, TIMEOUT, u'echo test') @functional -def test_select_command_with_arrows(proc): - select_command_with_arrows(proc) - history_changed(proc, u'git push') +def test_select_command_with_arrows(proc, TIMEOUT): + select_command_with_arrows(proc, TIMEOUT) + history_changed(proc, TIMEOUT, u'git push') @functional -def test_refuse_with_confirmation(proc): - refuse_with_confirmation(proc) - history_not_changed(proc) +def test_refuse_with_confirmation(proc, TIMEOUT): + refuse_with_confirmation(proc, TIMEOUT) + history_not_changed(proc, TIMEOUT) @functional -def test_without_confirmation(proc): - without_confirmation(proc) - history_changed(proc, u'echo test') +def test_without_confirmation(proc, TIMEOUT): + without_confirmation(proc, TIMEOUT) + history_changed(proc, TIMEOUT, u'echo test') diff --git a/tests/functional/utils.py b/tests/functional/utils.py index 7042c088..18dcb9df 100644 --- a/tests/functional/utils.py +++ b/tests/functional/utils.py @@ -1,65 +1,8 @@ import pytest import os -import subprocess -import shutil -from tempfile import mkdtemp -from pathlib import Path -import sys -import pexpect -from tests.utils import root - -bare = os.environ.get('BARE') enabled = os.environ.get('FUNCTIONAL') - -def build_container(tag, dockerfile, copy_src=False): - tmpdir = mkdtemp() - try: - if copy_src: - subprocess.call(['cp', '-a', str(root), tmpdir]) - dockerfile_path = Path(tmpdir).joinpath('Dockerfile') - with dockerfile_path.open('w') as file: - file.write(dockerfile) - if subprocess.call(['docker', 'build', '--tag={}'.format(tag), tmpdir]) != 0: - raise Exception("Can't build a container") - finally: - shutil.rmtree(tmpdir) - - -def spawn(request, tag, dockerfile, cmd, install=True, copy_src=False): - if bare: - proc = pexpect.spawnu(cmd) - else: - tag = 'thefuck/{}'.format(tag) - build_container(tag, dockerfile, copy_src) - proc = pexpect.spawnu('docker run --rm=true --volume {}:/src --tty=true ' - '--interactive=true {} {}'.format(root, tag, cmd)) - if install: - proc.sendline('pip install /src') - - proc.sendline('cd /') - - proc.logfile = sys.stdout - - def _finalizer(): - proc.terminate() - if not bare: - container_id = subprocess.check_output(['docker', 'ps']) \ - .decode('utf-8').split('\n')[-2].split()[0] - subprocess.check_call(['docker', 'kill', container_id]) - - request.addfinalizer(_finalizer) - return proc - - -def images(*items): - if bare: - return [items[0]] - else: - return items - - functional = pytest.mark.skipif( not enabled, reason='Functional tests are disabled by default.') diff --git a/thefuck/main.py b/thefuck/main.py index 1b059c3d..94aaef5b 100644 --- a/thefuck/main.py +++ b/thefuck/main.py @@ -103,6 +103,10 @@ def fix_command(): run_command(command, selected_command, settings) +def _get_current_version(): + return pkg_resources.require('thefuck')[0].version + + def print_alias(entry_point=True): if entry_point: warn('`thefuck-alias` is deprecated, use `thefuck --alias` instead.') @@ -120,8 +124,7 @@ def main(): parser = ArgumentParser(prog='thefuck') parser.add_argument('-v', '--version', action='version', - version='%(prog)s {}'.format( - pkg_resources.require('thefuck')[0].version)) + version='%(prog)s {}'.format(_get_current_version())) parser.add_argument('-a', '--alias', action='store_true', help='[custom-alias-name] prints alias for current shell')