1
0
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:
Vladimir Iakovlev
2015-07-31 15:39:57 +03:00
34 changed files with 663 additions and 422 deletions

View File

@@ -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')

View File

@@ -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')

View File

@@ -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.

View File

@@ -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.

View File

@@ -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')

View File

@@ -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):

View File

@@ -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']

View File

@@ -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]

View File

@@ -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']

View File

@@ -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']

View File

@@ -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

View File

@@ -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']

View File

@@ -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']

View File

@@ -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
View 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;']

View File

@@ -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
View 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')

View File

@@ -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),