mirror of
				https://github.com/nvbn/thefuck.git
				synced 2025-10-30 22:54:14 +00:00 
			
		
		
		
	Merge pull request #324 from nvbn/298-variants
Add ability to select fixed command from variants
This commit is contained in:
		| @@ -1,10 +1,17 @@ | ||||
| from time import sleep | ||||
| from pexpect import TIMEOUT | ||||
|  | ||||
|  | ||||
| def _set_confirmation(proc, require): | ||||
|     proc.sendline(u'mkdir -p ~/.thefuck') | ||||
|     proc.sendline( | ||||
|         u'echo "require_confirmation = {}" > ~/.thefuck/settings.py'.format( | ||||
|             require)) | ||||
|  | ||||
|  | ||||
| def with_confirmation(proc): | ||||
|     """Ensures that command can be fixed when confirmation enabled.""" | ||||
|     proc.sendline(u'mkdir -p ~/.thefuck') | ||||
|     proc.sendline(u'echo "require_confirmation = True" > ~/.thefuck/settings.py') | ||||
|     _set_confirmation(proc, True) | ||||
|  | ||||
|     proc.sendline(u'ehco test') | ||||
|  | ||||
| @@ -17,10 +24,10 @@ def with_confirmation(proc): | ||||
|     assert proc.expect([TIMEOUT, u'test']) | ||||
|  | ||||
|  | ||||
| def history_changed(proc): | ||||
| def history_changed(proc, to): | ||||
|     """Ensures that history changed.""" | ||||
|     proc.send('\033[A') | ||||
|     assert proc.expect([TIMEOUT, u'echo test']) | ||||
|     assert proc.expect([TIMEOUT, to]) | ||||
|  | ||||
|  | ||||
| def history_not_changed(proc): | ||||
| @@ -29,10 +36,29 @@ def history_not_changed(proc): | ||||
|     assert proc.expect([TIMEOUT, u'fuck']) | ||||
|  | ||||
|  | ||||
| def select_command_with_arrows(proc): | ||||
|     """Ensures that command can be selected with arrow keys.""" | ||||
|     _set_confirmation(proc, True) | ||||
|  | ||||
|     proc.sendline(u'git h') | ||||
|     assert proc.expect([TIMEOUT, u"git: 'h' is not a git command."]) | ||||
|  | ||||
|     proc.sendline(u'fuck') | ||||
|     assert proc.expect([TIMEOUT, u'git show']) | ||||
|     proc.send('\033[B') | ||||
|     assert proc.expect([TIMEOUT, u'git push']) | ||||
|     proc.send('\033[B') | ||||
|     assert proc.expect([TIMEOUT, u'git help']) | ||||
|     proc.send('\033[A') | ||||
|     assert proc.expect([TIMEOUT, u'git push']) | ||||
|     proc.send('\n') | ||||
|  | ||||
|     assert proc.expect([TIMEOUT, u'Not a git repository']) | ||||
|  | ||||
|  | ||||
| def refuse_with_confirmation(proc): | ||||
|     """Ensures that fix can be refused when confirmation enabled.""" | ||||
|     proc.sendline(u'mkdir -p ~/.thefuck') | ||||
|     proc.sendline(u'echo "require_confirmation = True" > ~/.thefuck/settings.py') | ||||
|     _set_confirmation(proc, True) | ||||
|  | ||||
|     proc.sendline(u'ehco test') | ||||
|  | ||||
| @@ -47,8 +73,7 @@ def refuse_with_confirmation(proc): | ||||
|  | ||||
| def without_confirmation(proc): | ||||
|     """Ensures that command can be fixed when confirmation disabled.""" | ||||
|     proc.sendline(u'mkdir -p ~/.thefuck') | ||||
|     proc.sendline(u'echo "require_confirmation = False" > ~/.thefuck/settings.py') | ||||
|     _set_confirmation(proc, False) | ||||
|  | ||||
|     proc.sendline(u'ehco test') | ||||
|  | ||||
|   | ||||
| @@ -1,51 +1,53 @@ | ||||
| import pytest | ||||
| from tests.functional.plots import with_confirmation, without_confirmation, \ | ||||
|     refuse_with_confirmation, history_changed, history_not_changed | ||||
|     refuse_with_confirmation, history_changed, history_not_changed, \ | ||||
|     select_command_with_arrows | ||||
| from tests.functional.utils import spawn, functional, images | ||||
|  | ||||
| containers = images(('ubuntu-python3-bash', u''' | ||||
| FROM ubuntu:latest | ||||
| RUN apt-get update | ||||
| RUN apt-get install -yy python3 python3-pip python3-dev | ||||
| 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 | ||||
| RUN apt-get install -yy python python-pip python-dev git | ||||
| RUN pip2 install -U pip setuptools | ||||
| ''')) | ||||
|  | ||||
|  | ||||
| @functional | ||||
| @pytest.mark.parametrize('tag, dockerfile', containers) | ||||
| def test_with_confirmation(tag, dockerfile): | ||||
|     with spawn(tag, dockerfile, u'bash') as proc: | ||||
|         proc.sendline(u"export PS1='$ '") | ||||
|         proc.sendline(u'eval $(thefuck-alias)') | ||||
|         proc.sendline(u'touch $HISTFILE') | ||||
|         with_confirmation(proc) | ||||
|         history_changed(proc) | ||||
| @pytest.fixture(params=containers) | ||||
| def proc(request): | ||||
|     tag, dockerfile = request.param | ||||
|     proc = spawn(request, tag, dockerfile, u'bash') | ||||
|     proc.sendline(u"export PS1='$ '") | ||||
|     proc.sendline(u'eval $(thefuck-alias)') | ||||
|     proc.sendline(u'echo > $HISTFILE') | ||||
|     return proc | ||||
|  | ||||
|  | ||||
| @functional | ||||
| @pytest.mark.parametrize('tag, dockerfile', containers) | ||||
| def test_refuse_with_confirmation(tag, dockerfile): | ||||
|     with spawn(tag, dockerfile, u'bash') as proc: | ||||
|         proc.sendline(u"export PS1='$ '") | ||||
|         proc.sendline(u'eval $(thefuck-alias)') | ||||
|         proc.sendline(u'touch $HISTFILE') | ||||
|         refuse_with_confirmation(proc) | ||||
|         history_not_changed(proc) | ||||
| def test_with_confirmation(proc): | ||||
|     with_confirmation(proc) | ||||
|     history_changed(proc, u'echo test') | ||||
|  | ||||
|  | ||||
| @functional | ||||
| @pytest.mark.parametrize('tag, dockerfile', containers) | ||||
| def test_without_confirmation(tag, dockerfile): | ||||
|     with spawn(tag, dockerfile, u'bash') as proc: | ||||
|         proc.sendline(u"export PS1='$ '") | ||||
|         proc.sendline(u'eval $(thefuck-alias)') | ||||
|         proc.sendline(u'touch $HISTFILE') | ||||
|         without_confirmation(proc) | ||||
|         history_changed(proc) | ||||
| def test_select_command_with_arrows(proc): | ||||
|     select_command_with_arrows(proc) | ||||
|     history_changed(proc, u'git push') | ||||
|  | ||||
|  | ||||
| @functional | ||||
| def test_refuse_with_confirmation(proc): | ||||
|     refuse_with_confirmation(proc) | ||||
|     history_not_changed(proc) | ||||
|  | ||||
|  | ||||
| @functional | ||||
| def test_without_confirmation(proc): | ||||
|     without_confirmation(proc) | ||||
|     history_changed(proc, u'echo test') | ||||
|   | ||||
| @@ -1,53 +1,59 @@ | ||||
| import pytest | ||||
| from tests.functional.plots import with_confirmation, without_confirmation, \ | ||||
|     refuse_with_confirmation | ||||
|     refuse_with_confirmation, select_command_with_arrows | ||||
| from tests.functional.utils import spawn, functional, images, bare | ||||
|  | ||||
| containers = images(('ubuntu-python3-fish', u''' | ||||
| FROM ubuntu:latest | ||||
| RUN apt-get update | ||||
| RUN apt-get install -yy python3 python3-pip python3-dev fish | ||||
| 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 fish | ||||
| RUN apt-get install -yy python python-pip python-dev git | ||||
| RUN pip2 install -U pip setuptools | ||||
| RUN apt-get install -yy fish | ||||
| ''')) | ||||
|  | ||||
|  | ||||
| @functional | ||||
| @pytest.mark.skipif( | ||||
|     bool(bare), reason='https://github.com/travis-ci/apt-source-whitelist/issues/71') | ||||
| @pytest.mark.parametrize('tag, dockerfile', containers) | ||||
| def test_with_confirmation(tag, dockerfile): | ||||
|     with spawn(tag, dockerfile, u'fish') as proc: | ||||
|         proc.sendline(u'thefuck-alias > ~/.config/fish/config.fish') | ||||
|         proc.sendline(u'fish') | ||||
|         with_confirmation(proc) | ||||
| @pytest.fixture(params=containers) | ||||
| def proc(request): | ||||
|     tag, dockerfile = request.param | ||||
|     proc = spawn(request, tag, dockerfile, u'fish') | ||||
|     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') | ||||
| @pytest.mark.parametrize('tag, dockerfile', containers) | ||||
| def test_refuse_with_confirmation(tag, dockerfile): | ||||
|     with spawn(tag, dockerfile, u'fish') as proc: | ||||
|         proc.sendline(u'thefuck-alias > ~/.config/fish/config.fish') | ||||
|         proc.sendline(u'fish') | ||||
|         refuse_with_confirmation(proc) | ||||
| def test_with_confirmation(proc): | ||||
|     with_confirmation(proc) | ||||
|  | ||||
|  | ||||
| @functional | ||||
| @pytest.mark.skipif( | ||||
|     bool(bare), reason='https://github.com/travis-ci/apt-source-whitelist/issues/71') | ||||
| @pytest.mark.parametrize('tag, dockerfile', containers) | ||||
| def test_without_confirmation(tag, dockerfile): | ||||
|     with spawn(tag, dockerfile, u'fish') as proc: | ||||
|         proc.sendline(u'thefuck-alias > ~/.config/fish/config.fish') | ||||
|         proc.sendline(u'fish') | ||||
|         without_confirmation(proc) | ||||
| def test_select_command_with_arrows(proc): | ||||
|     select_command_with_arrows(proc) | ||||
|  | ||||
|  | ||||
| @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) | ||||
|  | ||||
|  | ||||
| @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) | ||||
|  | ||||
| # TODO: ensure that history changes. | ||||
|   | ||||
| @@ -1,47 +1,51 @@ | ||||
| import pytest | ||||
| from tests.functional.utils import spawn, functional, images | ||||
| from tests.functional.plots import with_confirmation, without_confirmation, \ | ||||
|     refuse_with_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 tcsh | ||||
| 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 tcsh | ||||
| RUN apt-get install -yy python python-pip python-dev git | ||||
| RUN pip2 install -U pip setuptools | ||||
| RUN apt-get install -yy tcsh | ||||
| ''')) | ||||
|  | ||||
|  | ||||
| @functional | ||||
| @pytest.mark.parametrize('tag, dockerfile', containers) | ||||
| def test_with_confirmation(tag, dockerfile): | ||||
|     with spawn(tag, dockerfile, u'tcsh') as proc: | ||||
|         proc.sendline(u'tcsh') | ||||
|         proc.sendline(u'eval `thefuck-alias`') | ||||
|         with_confirmation(proc) | ||||
| @pytest.fixture(params=containers) | ||||
| def proc(request): | ||||
|     tag, dockerfile = request.param | ||||
|     proc = spawn(request, tag, dockerfile, u'tcsh') | ||||
|     proc.sendline(u'tcsh') | ||||
|     proc.sendline(u'eval `thefuck-alias`') | ||||
|     return proc | ||||
|  | ||||
|  | ||||
| @functional | ||||
| @pytest.mark.parametrize('tag, dockerfile', containers) | ||||
| def test_refuse_with_confirmation(tag, dockerfile): | ||||
|     with spawn(tag, dockerfile, u'tcsh') as proc: | ||||
|         proc.sendline(u'tcsh') | ||||
|         proc.sendline(u'eval `thefuck-alias`') | ||||
|         refuse_with_confirmation(proc) | ||||
| def test_with_confirmation(proc): | ||||
|     with_confirmation(proc) | ||||
|  | ||||
|  | ||||
| @functional | ||||
| @pytest.mark.parametrize('tag, dockerfile', containers) | ||||
| def test_without_confirmation(tag, dockerfile): | ||||
|     with spawn(tag, dockerfile, u'tcsh') as proc: | ||||
|         proc.sendline(u'tcsh') | ||||
|         proc.sendline(u'eval `thefuck-alias`') | ||||
|         without_confirmation(proc) | ||||
| def test_select_command_with_arrows(proc): | ||||
|     select_command_with_arrows(proc) | ||||
|  | ||||
|  | ||||
| @functional | ||||
| def test_refuse_with_confirmation(proc): | ||||
|     refuse_with_confirmation(proc) | ||||
|  | ||||
|  | ||||
| @functional | ||||
| def test_without_confirmation(proc): | ||||
|     without_confirmation(proc) | ||||
|  | ||||
| # TODO: ensure that history changes. | ||||
|   | ||||
| @@ -1,51 +1,57 @@ | ||||
| import pytest | ||||
| from tests.functional.utils import spawn, functional, images | ||||
| from tests.functional.plots import with_confirmation, without_confirmation, \ | ||||
|     refuse_with_confirmation, history_changed, history_not_changed | ||||
|     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 zsh | ||||
| 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 zsh | ||||
| RUN apt-get install -yy python python-pip python-dev git | ||||
| RUN pip2 install -U pip setuptools | ||||
| RUN apt-get install -yy zsh | ||||
| ''')) | ||||
|  | ||||
|  | ||||
| @functional | ||||
| @pytest.mark.parametrize('tag, dockerfile', containers) | ||||
| def test_with_confirmation(tag, dockerfile): | ||||
|     with spawn(tag, dockerfile, u'zsh') as proc: | ||||
|         proc.sendline(u'eval $(thefuck-alias)') | ||||
|         proc.sendline(u'export HISTFILE=~/.zsh_history') | ||||
|         proc.sendline(u'touch $HISTFILE') | ||||
|         with_confirmation(proc) | ||||
|         history_changed(proc) | ||||
| @pytest.fixture(params=containers) | ||||
| def proc(request): | ||||
|     tag, dockerfile = request.param | ||||
|     proc = spawn(request, tag, dockerfile, u'zsh') | ||||
|     proc.sendline(u'eval $(thefuck-alias)') | ||||
|     proc.sendline(u'export HISTFILE=~/.zsh_history') | ||||
|     proc.sendline(u'echo > $HISTFILE') | ||||
|     proc.sendline(u'export SAVEHIST=100') | ||||
|     proc.sendline(u'export HISTSIZE=100') | ||||
|     proc.sendline(u'setopt INC_APPEND_HISTORY') | ||||
|     return proc | ||||
|  | ||||
|  | ||||
| @functional | ||||
| @pytest.mark.parametrize('tag, dockerfile', containers) | ||||
| def test_refuse_with_confirmation(tag, dockerfile): | ||||
|     with spawn(tag, dockerfile, u'zsh') as proc: | ||||
|         proc.sendline(u'eval $(thefuck-alias)') | ||||
|         proc.sendline(u'export HISTFILE=~/.zsh_history') | ||||
|         proc.sendline(u'touch $HISTFILE') | ||||
|         refuse_with_confirmation(proc) | ||||
|         history_not_changed(proc) | ||||
| def test_with_confirmation(proc): | ||||
|     with_confirmation(proc) | ||||
|     history_changed(proc, u'echo test') | ||||
|  | ||||
|  | ||||
| @functional | ||||
| @pytest.mark.parametrize('tag, dockerfile', containers) | ||||
| def test_without_confirmation(tag, dockerfile): | ||||
|     with spawn(tag, dockerfile, u'zsh') as proc: | ||||
|         proc.sendline(u'eval $(thefuck-alias)') | ||||
|         proc.sendline(u'export HISTFILE=~/.zsh_history') | ||||
|         proc.sendline(u'touch $HISTFILE') | ||||
|         without_confirmation(proc) | ||||
|         history_changed(proc) | ||||
| def test_select_command_with_arrows(proc): | ||||
|     select_command_with_arrows(proc) | ||||
|     history_changed(proc, u'git push') | ||||
|  | ||||
|  | ||||
| @functional | ||||
| def test_refuse_with_confirmation(proc): | ||||
|     refuse_with_confirmation(proc) | ||||
|     history_not_changed(proc) | ||||
|  | ||||
|  | ||||
| @functional | ||||
| def test_without_confirmation(proc): | ||||
|     without_confirmation(proc) | ||||
|     history_changed(proc, u'echo test') | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| import os | ||||
| from contextlib import contextmanager | ||||
| import subprocess | ||||
| import shutil | ||||
| from tempfile import mkdtemp | ||||
| @@ -15,16 +14,17 @@ enabled = os.environ.get('FUNCTIONAL') | ||||
|  | ||||
| def build_container(tag, dockerfile): | ||||
|     tmpdir = mkdtemp() | ||||
|     with Path(tmpdir).joinpath('Dockerfile').open('w') as file: | ||||
|         file.write(dockerfile) | ||||
|     if subprocess.call(['docker', 'build', '--tag={}'.format(tag), tmpdir], | ||||
|                        cwd=root) != 0: | ||||
|         raise Exception("Can't build a container") | ||||
|     shutil.rmtree(tmpdir) | ||||
|     try: | ||||
|         with Path(tmpdir).joinpath('Dockerfile').open('w') as file: | ||||
|             file.write(dockerfile) | ||||
|         if subprocess.call(['docker', 'build', '--tag={}'.format(tag), tmpdir], | ||||
|                            cwd=root) != 0: | ||||
|             raise Exception("Can't build a container") | ||||
|     finally: | ||||
|         shutil.rmtree(tmpdir) | ||||
|  | ||||
|  | ||||
| @contextmanager | ||||
| def spawn(tag, dockerfile, cmd): | ||||
| def spawn(request, tag, dockerfile, cmd): | ||||
|     if bare: | ||||
|         proc = pexpect.spawnu(cmd) | ||||
|     else: | ||||
| @@ -33,13 +33,12 @@ def spawn(tag, dockerfile, cmd): | ||||
|         proc = pexpect.spawnu('docker run --volume {}:/src --tty=true ' | ||||
|                               '--interactive=true {} {}'.format(root, tag, cmd)) | ||||
|         proc.sendline('pip install /src') | ||||
|     proc.sendline('cd /') | ||||
|  | ||||
|     proc.logfile = sys.stdout | ||||
|  | ||||
|     try: | ||||
|         yield proc | ||||
|     finally: | ||||
|         proc.terminate(force=bare) | ||||
|     request.addfinalizer(proc.terminate) | ||||
|     return proc | ||||
|  | ||||
|  | ||||
| def images(*items): | ||||
|   | ||||
| @@ -22,7 +22,7 @@ def test_match(brew_unknown_cmd): | ||||
|  | ||||
| def test_get_new_command(brew_unknown_cmd, brew_unknown_cmd2): | ||||
|     assert get_new_command(Command('brew inst', stderr=brew_unknown_cmd), | ||||
|                            None) == 'brew list' | ||||
|                            None) == ['brew list', 'brew install', 'brew uninstall'] | ||||
|  | ||||
|     assert get_new_command(Command('brew instaa', stderr=brew_unknown_cmd2), | ||||
|                            None) == 'brew install' | ||||
|                            None) == ['brew install', 'brew uninstall', 'brew list'] | ||||
|   | ||||
| @@ -122,8 +122,8 @@ def test_not_match(script, stderr): | ||||
|  | ||||
| @pytest.mark.usefixtures('docker_help') | ||||
| @pytest.mark.parametrize('wrong, fixed', [ | ||||
|     ('pes', 'ps'), | ||||
|     ('tags', 'tag')]) | ||||
|     ('pes', ['ps', 'push', 'pause']), | ||||
|     ('tags', ['tag', 'stats', 'images'])]) | ||||
| def test_get_new_command(wrong, fixed): | ||||
|     command = Command('docker {}'.format(wrong), stderr=stderr(wrong)) | ||||
|     assert get_new_command(command, None) == 'docker {}'.format(fixed) | ||||
|     assert get_new_command(command, None) == ['docker {}'.format(x) for x in fixed] | ||||
|   | ||||
| @@ -50,8 +50,8 @@ def test_match(git_not_command, git_command, git_not_command_one_of_this): | ||||
| def test_get_new_command(git_not_command, git_not_command_one_of_this, | ||||
|                          git_not_command_closest): | ||||
|     assert get_new_command(Command('git brnch', stderr=git_not_command), None) \ | ||||
|            == 'git branch' | ||||
|            == ['git branch'] | ||||
|     assert get_new_command(Command('git st', stderr=git_not_command_one_of_this), | ||||
|                            None) == 'git status' | ||||
|                            None) == ['git stats', 'git stash', 'git stage'] | ||||
|     assert get_new_command(Command('git tags', stderr=git_not_command_closest), | ||||
|                            None) == 'git tag' | ||||
|                            None) == ['git tag', 'git stage'] | ||||
|   | ||||
| @@ -25,4 +25,4 @@ def test_get_new_command(mocker): | ||||
|     mocker.patch('thefuck.rules.gulp_not_task.get_gulp_tasks', return_value=[ | ||||
|         'serve', 'build', 'default']) | ||||
|     command = Command('gulp srve', stdout('srve')) | ||||
|     assert get_new_command(command, None) == 'gulp serve' | ||||
|     assert get_new_command(command, None) == ['gulp serve', 'gulp default'] | ||||
|   | ||||
| @@ -27,8 +27,8 @@ def test_not_match(script, stderr): | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize('cmd, result', [ | ||||
|     ('log', 'heroku logs'), | ||||
|     ('pge', 'heroku pg')]) | ||||
|     ('log', ['heroku logs', 'heroku pg']), | ||||
|     ('pge', ['heroku pg', 'heroku logs'])]) | ||||
| def test_get_new_command(cmd, result): | ||||
|     command = Command('heroku {}'.format(cmd), stderr=suggest_stderr(cmd)) | ||||
|     assert get_new_command(command, None) == result | ||||
|   | ||||
| @@ -9,6 +9,7 @@ def is_not_task(): | ||||
|  | ||||
| Did you mean this? | ||||
|          repl | ||||
|          jar | ||||
| ''' | ||||
|  | ||||
|  | ||||
| @@ -19,4 +20,4 @@ def test_match(is_not_task): | ||||
|  | ||||
| def test_get_new_command(is_not_task): | ||||
|     assert get_new_command(Mock(script='lein rpl --help', stderr=is_not_task), | ||||
|                            None) == 'lein repl --help' | ||||
|                            None) == ['lein repl --help', 'lein jar --help'] | ||||
|   | ||||
| @@ -22,8 +22,8 @@ def test_get_new_command(): | ||||
|     assert get_new_command( | ||||
|         Command(stderr='vom: not found', | ||||
|                 script='vom file.py'), | ||||
|         None) == 'vim file.py' | ||||
|         None) == ['vim file.py'] | ||||
|     assert get_new_command( | ||||
|         Command(stderr='fucck: not found', | ||||
|                 script='fucck'), | ||||
|         Command) == 'fsck' | ||||
|         Command) == ['fsck'] | ||||
|   | ||||
| @@ -61,30 +61,30 @@ def test_not_match(command): | ||||
|     assert not match(command, None) | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize('command, new_command', [ | ||||
| @pytest.mark.parametrize('command, new_commands', [ | ||||
|     (Command('tsuru log', stderr=( | ||||
|         'tsuru: "log" is not a tsuru command. See "tsuru help".\n' | ||||
|         '\nDid you mean?\n' | ||||
|         '\tapp-log\n' | ||||
|         '\tlogin\n' | ||||
|         '\tlogout\n' | ||||
|     )), 'tsuru login'), | ||||
|     )), ['tsuru login', 'tsuru logout', 'tsuru app-log']), | ||||
|     (Command('tsuru app-l', stderr=( | ||||
|         'tsuru: "app-l" is not a tsuru command. See "tsuru help".\n' | ||||
|         '\nDid you mean?\n' | ||||
|         '\tapp-list\n' | ||||
|         '\tapp-log\n' | ||||
|     )), 'tsuru app-log'), | ||||
|     )), ['tsuru app-log', 'tsuru app-list']), | ||||
|     (Command('tsuru user-list', stderr=( | ||||
|         'tsuru: "user-list" is not a tsuru command. See "tsuru help".\n' | ||||
|         '\nDid you mean?\n' | ||||
|         '\tteam-user-list\n' | ||||
|     )), 'tsuru team-user-list'), | ||||
|     )), ['tsuru team-user-list']), | ||||
|     (Command('tsuru targetlist', stderr=( | ||||
|         'tsuru: "targetlist" is not a tsuru command. See "tsuru help".\n' | ||||
|         '\nDid you mean?\n' | ||||
|         '\ttarget-list\n' | ||||
|     )), 'tsuru target-list'), | ||||
|     )), ['tsuru target-list']), | ||||
| ]) | ||||
| def test_get_new_command(command, new_command): | ||||
|     assert get_new_command(command, None) == new_command | ||||
| def test_get_new_command(command, new_commands): | ||||
|     assert get_new_command(command, None) == new_commands | ||||
|   | ||||
							
								
								
									
										88
									
								
								tests/test_corrector.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								tests/test_corrector.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | ||||
| import pytest | ||||
| from pathlib import PosixPath, Path | ||||
| from mock import Mock | ||||
| from thefuck import corrector, conf, types | ||||
| from tests.utils import Rule, Command | ||||
| from thefuck.corrector import make_corrected_commands, get_corrected_commands | ||||
|  | ||||
|  | ||||
| def test_load_rule(mocker): | ||||
|     match = object() | ||||
|     get_new_command = object() | ||||
|     load_source = mocker.patch( | ||||
|         'thefuck.corrector.load_source', | ||||
|         return_value=Mock(match=match, | ||||
|                           get_new_command=get_new_command, | ||||
|                           enabled_by_default=True, | ||||
|                           priority=900, | ||||
|                           requires_output=True)) | ||||
|     assert corrector.load_rule(Path('/rules/bash.py'), settings=Mock(priority={})) \ | ||||
|            == Rule('bash', match, get_new_command, priority=900) | ||||
|     load_source.assert_called_once_with('bash', '/rules/bash.py') | ||||
|  | ||||
|  | ||||
| class TestGetRules(object): | ||||
|     @pytest.fixture(autouse=True) | ||||
|     def glob(self, mocker): | ||||
|         return mocker.patch('thefuck.corrector.Path.glob', return_value=[]) | ||||
|  | ||||
|     def _compare_names(self, rules, names): | ||||
|         return [r.name for r in rules] == names | ||||
|  | ||||
|     @pytest.mark.parametrize('conf_rules, rules', [ | ||||
|         (conf.DEFAULT_RULES, ['bash', 'lisp', 'bash', 'lisp']), | ||||
|         (types.RulesNamesList(['bash']), ['bash', 'bash'])]) | ||||
|     def test_get(self, monkeypatch, glob, conf_rules, rules): | ||||
|         glob.return_value = [PosixPath('bash.py'), PosixPath('lisp.py')] | ||||
|         monkeypatch.setattr('thefuck.corrector.load_source', | ||||
|                             lambda x, _: Rule(x)) | ||||
|         assert self._compare_names( | ||||
|             corrector.get_rules(Path('~'), Mock(rules=conf_rules, priority={})), | ||||
|             rules) | ||||
|  | ||||
|  | ||||
| class TestGetMatchedRules(object): | ||||
|     def test_no_match(self): | ||||
|         assert list(corrector.get_matched_rules( | ||||
|             Command('ls'), [Rule('', lambda *_: False)], | ||||
|             Mock(no_colors=True))) == [] | ||||
|  | ||||
|     def test_match(self): | ||||
|         rule = Rule('', lambda x, _: x.script == 'cd ..') | ||||
|         assert list(corrector.get_matched_rules( | ||||
|             Command('cd ..'), [rule], Mock(no_colors=True))) == [rule] | ||||
|  | ||||
|     def test_when_rule_failed(self, capsys): | ||||
|         all(corrector.get_matched_rules( | ||||
|             Command('ls'), [Rule('test', Mock(side_effect=OSError('Denied')), | ||||
|                                  requires_output=False)], | ||||
|             Mock(no_colors=True, debug=False))) | ||||
|         assert capsys.readouterr()[1].split('\n')[0] == '[WARN] Rule test:' | ||||
|  | ||||
|  | ||||
| class TestGetCorrectedCommands(object): | ||||
|     def test_with_rule_returns_list(self): | ||||
|         rule = Rule(get_new_command=lambda x, _: [x.script + '!', x.script + '@'], | ||||
|                     priority=100) | ||||
|         assert list(make_corrected_commands(Command(script='test'), [rule], None)) \ | ||||
|                == [types.CorrectedCommand(script='test!', priority=100, side_effect=None), | ||||
|                    types.CorrectedCommand(script='test@', priority=200, side_effect=None)] | ||||
|  | ||||
|     def test_with_rule_returns_command(self): | ||||
|         rule = Rule(get_new_command=lambda x, _: x.script + '!', | ||||
|                     priority=100) | ||||
|         assert list(make_corrected_commands(Command(script='test'), [rule], None)) \ | ||||
|                == [types.CorrectedCommand(script='test!', priority=100, side_effect=None)] | ||||
|  | ||||
|  | ||||
| def test_get_corrected_commands(mocker): | ||||
|     command = Command('test', 'test', 'test') | ||||
|     rules = [Rule(match=lambda *_: False), | ||||
|              Rule(match=lambda *_: True, | ||||
|                   get_new_command=lambda x, _: x.script + '!', priority=100), | ||||
|              Rule(match=lambda *_: True, | ||||
|                   get_new_command=lambda x, _: [x.script + '@', x.script + ';'], | ||||
|                   priority=60)] | ||||
|     mocker.patch('thefuck.corrector.get_rules', return_value=rules) | ||||
|     assert [cmd.script for cmd in get_corrected_commands(command, None, Mock(debug=False))]\ | ||||
|         == ['test@', 'test!', 'test;'] | ||||
| @@ -1,61 +1,8 @@ | ||||
| import pytest | ||||
| from subprocess import PIPE | ||||
| from pathlib import PosixPath, Path | ||||
| from mock import Mock | ||||
| from thefuck import main, conf, types | ||||
| from tests.utils import Rule, Command | ||||
|  | ||||
|  | ||||
| def test_load_rule(mocker): | ||||
|     match = object() | ||||
|     get_new_command = object() | ||||
|     load_source = mocker.patch( | ||||
|         'thefuck.main.load_source', | ||||
|         return_value=Mock(match=match, | ||||
|                           get_new_command=get_new_command, | ||||
|                           enabled_by_default=True, | ||||
|                           priority=900, | ||||
|                           requires_output=True)) | ||||
|     assert main.load_rule(Path('/rules/bash.py')) \ | ||||
|            == Rule('bash', match, get_new_command, priority=900) | ||||
|     load_source.assert_called_once_with('bash', '/rules/bash.py') | ||||
|  | ||||
|  | ||||
| class TestGetRules(object): | ||||
|     @pytest.fixture(autouse=True) | ||||
|     def glob(self, mocker): | ||||
|         return mocker.patch('thefuck.main.Path.glob', return_value=[]) | ||||
|  | ||||
|     def _compare_names(self, rules, names): | ||||
|         return [r.name for r in rules] == names | ||||
|  | ||||
|     @pytest.mark.parametrize('conf_rules, rules', [ | ||||
|         (conf.DEFAULT_RULES, ['bash', 'lisp', 'bash', 'lisp']), | ||||
|         (types.RulesNamesList(['bash']), ['bash', 'bash'])]) | ||||
|     def test_get(self, monkeypatch, glob, conf_rules, rules): | ||||
|         glob.return_value = [PosixPath('bash.py'), PosixPath('lisp.py')] | ||||
|         monkeypatch.setattr('thefuck.main.load_source', | ||||
|                             lambda x, _: Rule(x)) | ||||
|         assert self._compare_names( | ||||
|             main.get_rules(Path('~'), Mock(rules=conf_rules, priority={})), | ||||
|             rules) | ||||
|  | ||||
|     @pytest.mark.parametrize('priority, unordered, ordered', [ | ||||
|         ({}, | ||||
|          [Rule('bash', priority=100), Rule('python', priority=5)], | ||||
|          ['python', 'bash']), | ||||
|         ({}, | ||||
|          [Rule('lisp', priority=9999), Rule('c', priority=conf.DEFAULT_PRIORITY)], | ||||
|          ['c', 'lisp']), | ||||
|         ({'python': 9999}, | ||||
|          [Rule('bash', priority=100), Rule('python', priority=5)], | ||||
|          ['bash', 'python'])]) | ||||
|     def test_ordered_by_priority(self, monkeypatch, priority, unordered, ordered): | ||||
|         monkeypatch.setattr('thefuck.main._get_loaded_rules', | ||||
|                             lambda *_: unordered) | ||||
|         assert self._compare_names( | ||||
|             main.get_rules(Path('~'), Mock(priority=priority)), | ||||
|             ordered) | ||||
| from thefuck import main | ||||
| from tests.utils import Command | ||||
|  | ||||
|  | ||||
| class TestGetCommand(object): | ||||
| @@ -79,7 +26,7 @@ class TestGetCommand(object): | ||||
|  | ||||
|     def test_get_command_calls(self, Popen): | ||||
|         assert main.get_command(Mock(env={}), | ||||
|             ['thefuck', 'apt-get', 'search', 'vim']) \ | ||||
|                                 ['thefuck', 'apt-get', 'search', 'vim']) \ | ||||
|                == Command('apt-get search vim', 'stdout', 'stderr') | ||||
|         Popen.assert_called_once_with('apt-get search vim', | ||||
|                                       shell=True, | ||||
| @@ -95,80 +42,3 @@ class TestGetCommand(object): | ||||
|             assert main.get_command(Mock(env={}), args).script == result | ||||
|         else: | ||||
|             assert main.get_command(Mock(env={}), args) is None | ||||
|  | ||||
|  | ||||
| class TestGetMatchedRule(object): | ||||
|     def test_no_match(self): | ||||
|         assert main.get_matched_rule( | ||||
|             Command('ls'), [Rule('', lambda *_: False)], | ||||
|             Mock(no_colors=True)) is None | ||||
|  | ||||
|     def test_match(self): | ||||
|         rule = Rule('', lambda x, _: x.script == 'cd ..') | ||||
|         assert main.get_matched_rule( | ||||
|             Command('cd ..'), [rule], Mock(no_colors=True)) == rule | ||||
|  | ||||
|     def test_when_rule_failed(self, capsys): | ||||
|         main.get_matched_rule( | ||||
|             Command('ls'), [Rule('test', Mock(side_effect=OSError('Denied')))], | ||||
|             Mock(no_colors=True, debug=False)) | ||||
|         assert capsys.readouterr()[1].split('\n')[0] == '[WARN] Rule test:' | ||||
|  | ||||
|  | ||||
| class TestRunRule(object): | ||||
|     @pytest.fixture(autouse=True) | ||||
|     def confirm(self, mocker): | ||||
|         return mocker.patch('thefuck.main.confirm', return_value=True) | ||||
|  | ||||
|     def test_run_rule(self, capsys): | ||||
|         main.run_rule(Rule(get_new_command=lambda *_: 'new-command'), | ||||
|                       Command(), None) | ||||
|         assert capsys.readouterr() == ('new-command\n', '') | ||||
|  | ||||
|     def test_run_rule_with_side_effect(self, capsys): | ||||
|         side_effect = Mock() | ||||
|         settings = Mock(debug=False) | ||||
|         command = Command() | ||||
|         main.run_rule(Rule(get_new_command=lambda *_: 'new-command', | ||||
|                            side_effect=side_effect), | ||||
|                       command, settings) | ||||
|         assert capsys.readouterr() == ('new-command\n', '') | ||||
|         side_effect.assert_called_once_with(command, settings) | ||||
|  | ||||
|     def test_when_not_comfirmed(self, capsys, confirm): | ||||
|         confirm.return_value = False | ||||
|         main.run_rule(Rule(get_new_command=lambda *_: 'new-command'), | ||||
|                       Command(), None) | ||||
|         assert capsys.readouterr() == ('', '') | ||||
|  | ||||
|  | ||||
| class TestConfirm(object): | ||||
|     @pytest.fixture | ||||
|     def stdin(self, mocker): | ||||
|         return mocker.patch('sys.stdin.read', return_value='\n') | ||||
|  | ||||
|     def test_when_not_required(self, capsys): | ||||
|         assert main.confirm('command', None, Mock(require_confirmation=False)) | ||||
|         assert capsys.readouterr() == ('', 'command\n') | ||||
|  | ||||
|     def test_with_side_effect_and_without_confirmation(self, capsys): | ||||
|         assert main.confirm('command', Mock(), Mock(require_confirmation=False)) | ||||
|         assert capsys.readouterr() == ('', 'command (+side effect)\n') | ||||
|  | ||||
|     # `stdin` fixture should be applied after `capsys` | ||||
|     def test_when_confirmation_required_and_confirmed(self, capsys, stdin): | ||||
|         assert main.confirm('command', None, Mock(require_confirmation=True, | ||||
|                                                   no_colors=True)) | ||||
|         assert capsys.readouterr() == ('', 'command [enter/ctrl+c]') | ||||
|  | ||||
|     # `stdin` fixture should be applied after `capsys` | ||||
|     def test_when_confirmation_required_and_confirmed_with_side_effect(self, capsys, stdin): | ||||
|         assert main.confirm('command', Mock(), Mock(require_confirmation=True, | ||||
|                                                     no_colors=True)) | ||||
|         assert capsys.readouterr() == ('', 'command (+side effect) [enter/ctrl+c]') | ||||
|  | ||||
|     def test_when_confirmation_required_and_aborted(self, capsys, stdin): | ||||
|         stdin.side_effect = KeyboardInterrupt | ||||
|         assert not main.confirm('command', None, Mock(require_confirmation=True, | ||||
|                                                       no_colors=True)) | ||||
|         assert capsys.readouterr() == ('', 'command [enter/ctrl+c]Aborted\n') | ||||
|   | ||||
							
								
								
									
										117
									
								
								tests/test_ui.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								tests/test_ui.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,117 @@ | ||||
| # -*- encoding: utf-8 -*- | ||||
|  | ||||
| from mock import Mock | ||||
| import pytest | ||||
| from itertools import islice | ||||
| from thefuck import ui | ||||
| from thefuck.types import CorrectedCommand | ||||
|  | ||||
|  | ||||
| @pytest.fixture | ||||
| def patch_getch(monkeypatch): | ||||
|     def patch(vals): | ||||
|         def getch(): | ||||
|             for val in vals: | ||||
|                 if val == KeyboardInterrupt: | ||||
|                     raise val | ||||
|                 else: | ||||
|                     yield val | ||||
|  | ||||
|         getch_gen = getch() | ||||
|         monkeypatch.setattr('thefuck.ui.getch', lambda: next(getch_gen)) | ||||
|  | ||||
|     return patch | ||||
|  | ||||
|  | ||||
| def test_read_actions(patch_getch): | ||||
|     patch_getch([  # Enter: | ||||
|                    '\n', | ||||
|                    # Enter: | ||||
|                    '\r', | ||||
|                    # Ignored: | ||||
|                    'x', 'y', | ||||
|                    # Up: | ||||
|                    '\x1b', '[', 'A', | ||||
|                    # Down: | ||||
|                    '\x1b', '[', 'B', | ||||
|                    # Ctrl+C: | ||||
|                    KeyboardInterrupt], ) | ||||
|     assert list(islice(ui.read_actions(), 5)) \ | ||||
|            == [ui.SELECT, ui.SELECT, ui.PREVIOUS, ui.NEXT, ui.ABORT] | ||||
|  | ||||
|  | ||||
| def test_command_selector(): | ||||
|     selector = ui.CommandSelector([1, 2, 3]) | ||||
|     assert selector.value == 1 | ||||
|     changes = [] | ||||
|     selector.on_change(changes.append) | ||||
|     selector.next() | ||||
|     assert selector.value == 2 | ||||
|     selector.next() | ||||
|     assert selector.value == 3 | ||||
|     selector.next() | ||||
|     assert selector.value == 1 | ||||
|     selector.previous() | ||||
|     assert selector.value == 3 | ||||
|     assert changes == [1, 2, 3, 1, 3] | ||||
|  | ||||
|  | ||||
| class TestSelectCommand(object): | ||||
|     @pytest.fixture | ||||
|     def commands_with_side_effect(self): | ||||
|         return [CorrectedCommand('ls', lambda *_: None, 100), | ||||
|                 CorrectedCommand('cd', lambda *_: None, 100)] | ||||
|  | ||||
|     @pytest.fixture | ||||
|     def commands(self): | ||||
|         return [CorrectedCommand('ls', None, 100), | ||||
|                 CorrectedCommand('cd', None, 100)] | ||||
|  | ||||
|     def test_without_commands(self, capsys): | ||||
|         assert ui.select_command([], Mock(debug=False, no_color=True)) is None | ||||
|         assert capsys.readouterr() == ('', 'No fuck given\n') | ||||
|  | ||||
|     def test_without_confirmation(self, capsys, commands): | ||||
|         assert ui.select_command(commands, | ||||
|                                  Mock(debug=False, no_color=True, | ||||
|                                       require_confirmation=False)) == commands[0] | ||||
|         assert capsys.readouterr() == ('', 'ls\n') | ||||
|  | ||||
|     def test_without_confirmation_with_side_effects(self, capsys, | ||||
|                                                     commands_with_side_effect): | ||||
|         assert ui.select_command(commands_with_side_effect, | ||||
|                                  Mock(debug=False, no_color=True, | ||||
|                                       require_confirmation=False)) \ | ||||
|                == commands_with_side_effect[0] | ||||
|         assert capsys.readouterr() == ('', 'ls (+side effect)\n') | ||||
|  | ||||
|     def test_with_confirmation(self, capsys, patch_getch, commands): | ||||
|         patch_getch(['\n']) | ||||
|         assert ui.select_command(commands, | ||||
|                                  Mock(debug=False, no_color=True, | ||||
|                                       require_confirmation=True)) == commands[0] | ||||
|         assert capsys.readouterr() == ('', u'\x1b[1K\rls [enter/↑/↓/ctrl+c]\n') | ||||
|  | ||||
|     def test_with_confirmation_abort(self, capsys, patch_getch, commands): | ||||
|         patch_getch([KeyboardInterrupt]) | ||||
|         assert ui.select_command(commands, | ||||
|                                  Mock(debug=False, no_color=True, | ||||
|                                       require_confirmation=True)) is None | ||||
|         assert capsys.readouterr() == ('', u'\x1b[1K\rls [enter/↑/↓/ctrl+c]\nAborted\n') | ||||
|  | ||||
|     def test_with_confirmation_with_side_effct(self, capsys, patch_getch, | ||||
|                                                commands_with_side_effect): | ||||
|         patch_getch(['\n']) | ||||
|         assert ui.select_command(commands_with_side_effect, | ||||
|                                  Mock(debug=False, no_color=True, | ||||
|                                       require_confirmation=True))\ | ||||
|                == commands_with_side_effect[0] | ||||
|         assert capsys.readouterr() == ('', u'\x1b[1K\rls (+side effect) [enter/↑/↓/ctrl+c]\n') | ||||
|  | ||||
|     def test_with_confirmation_select_second(self, capsys, patch_getch, commands): | ||||
|         patch_getch(['\x1b', '[', 'B', '\n']) | ||||
|         assert ui.select_command(commands, | ||||
|                                  Mock(debug=False, no_color=True, | ||||
|                                       require_confirmation=True)) == commands[1] | ||||
|         assert capsys.readouterr() == ( | ||||
|             '', u'\x1b[1K\rls [enter/↑/↓/ctrl+c]\x1b[1K\rcd [enter/↑/↓/ctrl+c]\n') | ||||
| @@ -18,6 +18,7 @@ def test_wrap_settings(override, old, new): | ||||
| @pytest.mark.parametrize('return_value, command, called, result', [ | ||||
|     ('ls -lah', 'sudo ls', 'ls', 'sudo ls -lah'), | ||||
|     ('ls -lah', 'ls', 'ls', 'ls -lah'), | ||||
|     (['ls -lah'], 'sudo ls', 'ls', ['sudo ls -lah']), | ||||
|     (True, 'sudo ls', 'ls', True), | ||||
|     (True, 'ls', 'ls', True), | ||||
|     (False, 'sudo ls', 'ls', False), | ||||
|   | ||||
		Reference in New Issue
	
	Block a user