2016-05-08 13:20:37 -03:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
2015-05-02 04:29:55 +02:00
|
|
|
import pytest
|
2016-05-08 13:20:37 -03:00
|
|
|
import warnings
|
2019-05-21 20:47:47 +02:00
|
|
|
from mock import Mock, call, patch
|
2024-12-21 11:20:25 +08:00
|
|
|
from thefuck.utils import (
|
|
|
|
default_settings,
|
|
|
|
memoize,
|
|
|
|
get_closest,
|
|
|
|
get_all_executables,
|
|
|
|
replace_argument,
|
|
|
|
get_all_matched_commands,
|
|
|
|
is_app,
|
|
|
|
for_app,
|
|
|
|
cache,
|
|
|
|
get_valid_history_without_current,
|
|
|
|
_cache,
|
|
|
|
get_close_matches,
|
|
|
|
)
|
2017-08-31 17:58:56 +02:00
|
|
|
from thefuck.types import Command
|
2015-04-22 20:18:53 +02:00
|
|
|
|
|
|
|
|
2024-12-21 11:20:25 +08:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"override, old, new",
|
|
|
|
[
|
|
|
|
({"key": "val"}, {}, {"key": "val"}),
|
|
|
|
({"key": "new-val"}, {"key": "val"}, {"key": "val"}),
|
|
|
|
(
|
|
|
|
{"key": "new-val", "unset": "unset"},
|
|
|
|
{"key": "val"},
|
|
|
|
{"key": "val", "unset": "unset"},
|
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
2015-09-07 13:00:29 +03:00
|
|
|
def test_default_settings(settings, override, old, new):
|
|
|
|
settings.clear()
|
|
|
|
settings.update(old)
|
2016-10-05 11:08:02 -04:00
|
|
|
default_settings(override)(lambda _: _)(None)
|
2015-09-07 13:00:29 +03:00
|
|
|
assert settings == new
|
2015-04-22 16:45:38 +02:00
|
|
|
|
|
|
|
|
2015-05-22 17:07:01 +03:00
|
|
|
def test_memoize():
|
2024-12-21 11:20:25 +08:00
|
|
|
fn = Mock(__name__="fn")
|
2015-05-22 17:07:01 +03:00
|
|
|
memoized = memoize(fn)
|
|
|
|
memoized()
|
|
|
|
memoized()
|
|
|
|
fn.assert_called_once_with()
|
2015-07-08 21:30:24 +03:00
|
|
|
|
|
|
|
|
2024-12-21 11:20:25 +08:00
|
|
|
@pytest.mark.usefixtures("no_memoize")
|
2015-07-10 17:06:05 +03:00
|
|
|
def test_no_memoize():
|
2024-12-21 11:20:25 +08:00
|
|
|
fn = Mock(__name__="fn")
|
2015-07-10 17:06:05 +03:00
|
|
|
memoized = memoize(fn)
|
|
|
|
memoized()
|
|
|
|
memoized()
|
|
|
|
assert fn.call_count == 2
|
|
|
|
|
|
|
|
|
2015-07-08 21:30:24 +03:00
|
|
|
class TestGetClosest(object):
|
|
|
|
def test_when_can_match(self):
|
2024-12-21 11:20:25 +08:00
|
|
|
assert "branch" == get_closest("brnch", ["branch", "status"])
|
2015-07-08 21:30:24 +03:00
|
|
|
|
|
|
|
def test_when_cant_match(self):
|
2024-12-21 11:20:25 +08:00
|
|
|
assert "status" == get_closest("st", ["status", "reset"])
|
2015-07-20 19:25:29 +03:00
|
|
|
|
|
|
|
def test_without_fallback(self):
|
2024-12-21 11:20:25 +08:00
|
|
|
assert get_closest("st", ["status", "reset"], fallback_to_first=False) is None
|
2015-07-20 21:04:49 +03:00
|
|
|
|
|
|
|
|
2018-10-08 22:32:30 +02:00
|
|
|
class TestGetCloseMatches(object):
|
2024-12-21 11:20:25 +08:00
|
|
|
@patch("thefuck.utils.difflib_get_close_matches")
|
2018-10-08 22:32:30 +02:00
|
|
|
def test_call_with_n(self, difflib_mock):
|
2024-12-21 11:20:25 +08:00
|
|
|
get_close_matches("", [], 1)
|
2018-10-08 22:32:30 +02:00
|
|
|
assert difflib_mock.call_args[0][2] == 1
|
|
|
|
|
2024-12-21 11:20:25 +08:00
|
|
|
@patch("thefuck.utils.difflib_get_close_matches")
|
2018-10-08 22:32:30 +02:00
|
|
|
def test_call_without_n(self, difflib_mock, settings):
|
2024-12-21 11:20:25 +08:00
|
|
|
get_close_matches("", [])
|
|
|
|
assert difflib_mock.call_args[0][2] == settings.get("num_close_matches")
|
2018-10-08 22:32:30 +02:00
|
|
|
|
|
|
|
|
2015-07-20 21:04:49 +03:00
|
|
|
@pytest.fixture
|
|
|
|
def get_aliases(mocker):
|
2024-12-21 11:20:25 +08:00
|
|
|
mocker.patch(
|
|
|
|
"thefuck.shells.shell.get_aliases",
|
|
|
|
return_value=["vim", "apt-get", "fsck", "fuck"],
|
|
|
|
)
|
2015-07-20 21:04:49 +03:00
|
|
|
|
|
|
|
|
2024-12-21 11:20:25 +08:00
|
|
|
@pytest.mark.usefixtures("no_memoize", "get_aliases")
|
2015-09-06 13:37:48 +03:00
|
|
|
def test_get_all_executables():
|
2015-07-20 21:04:49 +03:00
|
|
|
all_callables = get_all_executables()
|
2024-12-21 11:20:25 +08:00
|
|
|
assert "vim" in all_callables
|
|
|
|
assert "fsck" in all_callables
|
|
|
|
assert "fuck" not in all_callables
|
2015-07-24 00:39:56 +03:00
|
|
|
|
|
|
|
|
2019-05-21 20:47:47 +02:00
|
|
|
@pytest.fixture
|
|
|
|
def os_environ_pathsep(monkeypatch, path, pathsep):
|
2024-12-21 11:20:25 +08:00
|
|
|
env = {"PATH": path}
|
|
|
|
monkeypatch.setattr("os.environ", env)
|
|
|
|
monkeypatch.setattr("os.pathsep", pathsep)
|
2019-05-21 20:47:47 +02:00
|
|
|
return env
|
|
|
|
|
|
|
|
|
2024-12-21 11:20:25 +08:00
|
|
|
@pytest.mark.usefixtures("no_memoize", "os_environ_pathsep")
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"path, pathsep",
|
|
|
|
[("/foo:/bar:/baz:/foo/bar", ":"), (r"C:\\foo;C:\\bar;C:\\baz;C:\\foo\\bar", ";")],
|
|
|
|
)
|
2019-05-21 20:47:47 +02:00
|
|
|
def test_get_all_executables_pathsep(path, pathsep):
|
2024-12-21 11:20:25 +08:00
|
|
|
with patch("thefuck.utils.Path") as Path_mock:
|
2019-05-21 20:47:47 +02:00
|
|
|
get_all_executables()
|
|
|
|
Path_mock.assert_has_calls([call(p) for p in path.split(pathsep)], True)
|
|
|
|
|
|
|
|
|
2024-12-21 11:20:25 +08:00
|
|
|
@pytest.mark.usefixtures("no_memoize", "os_environ_pathsep")
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"path, pathsep, excluded",
|
|
|
|
[
|
|
|
|
("/foo:/bar:/baz:/foo/bar:/mnt/foo", ":", "/mnt/foo"),
|
|
|
|
(r"C:\\foo;C:\\bar;C:\\baz;C:\\foo\\bar;Z:\\foo", ";", r"Z:\\foo"),
|
|
|
|
],
|
|
|
|
)
|
2021-04-21 18:43:21 +01:00
|
|
|
def test_get_all_executables_exclude_paths(path, pathsep, excluded, settings):
|
|
|
|
settings.init()
|
|
|
|
settings.excluded_search_path_prefixes = [excluded]
|
2024-12-21 11:20:25 +08:00
|
|
|
with patch("thefuck.utils.Path") as Path_mock:
|
2021-04-21 18:43:21 +01:00
|
|
|
get_all_executables()
|
|
|
|
path_list = path.split(pathsep)
|
|
|
|
assert call(path_list[-1]) not in Path_mock.mock_calls
|
|
|
|
assert all(call(p) in Path_mock.mock_calls for p in path_list[:-1])
|
|
|
|
|
|
|
|
|
2024-12-21 11:20:25 +08:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"args, result",
|
|
|
|
[
|
|
|
|
(("apt-get instol vim", "instol", "install"), "apt-get install vim"),
|
|
|
|
(("git brnch", "brnch", "branch"), "git branch"),
|
|
|
|
],
|
|
|
|
)
|
2015-07-24 00:39:56 +03:00
|
|
|
def test_replace_argument(args, result):
|
|
|
|
assert replace_argument(*args) == result
|
2015-07-27 22:29:02 -03:00
|
|
|
|
|
|
|
|
2024-12-21 11:20:25 +08:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"stderr, result",
|
|
|
|
[
|
|
|
|
(
|
|
|
|
(
|
|
|
|
"git: 'cone' is not a git command. See 'git --help'.\n"
|
|
|
|
"\n"
|
|
|
|
"Did you mean one of these?\n"
|
|
|
|
"\tclone"
|
|
|
|
),
|
|
|
|
["clone"],
|
|
|
|
),
|
|
|
|
(
|
|
|
|
(
|
|
|
|
"git: 're' is not a git command. See 'git --help'.\n"
|
|
|
|
"\n"
|
|
|
|
"Did you mean one of these?\n"
|
|
|
|
"\trebase\n"
|
|
|
|
"\treset\n"
|
|
|
|
"\tgrep\n"
|
|
|
|
"\trm"
|
|
|
|
),
|
|
|
|
["rebase", "reset", "grep", "rm"],
|
|
|
|
),
|
|
|
|
(
|
|
|
|
(
|
|
|
|
'tsuru: "target" is not a tsuru command. See "tsuru help".\n'
|
|
|
|
"\n"
|
|
|
|
"Did you mean one of these?\n"
|
|
|
|
"\tservice-add\n"
|
|
|
|
"\tservice-bind\n"
|
|
|
|
"\tservice-doc\n"
|
|
|
|
"\tservice-info\n"
|
|
|
|
"\tservice-list\n"
|
|
|
|
"\tservice-remove\n"
|
|
|
|
"\tservice-status\n"
|
|
|
|
"\tservice-unbind"
|
|
|
|
),
|
|
|
|
[
|
|
|
|
"service-add",
|
|
|
|
"service-bind",
|
|
|
|
"service-doc",
|
|
|
|
"service-info",
|
|
|
|
"service-list",
|
|
|
|
"service-remove",
|
|
|
|
"service-status",
|
|
|
|
"service-unbind",
|
|
|
|
],
|
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
2015-07-27 22:29:02 -03:00
|
|
|
def test_get_all_matched_commands(stderr, result):
|
|
|
|
assert list(get_all_matched_commands(stderr)) == result
|
2015-08-27 16:08:29 +03:00
|
|
|
|
|
|
|
|
2024-12-21 11:20:25 +08:00
|
|
|
@pytest.mark.usefixtures("no_memoize")
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"script, names, result",
|
|
|
|
[
|
|
|
|
("/usr/bin/git diff", ["git", "hub"], True),
|
|
|
|
("/bin/hdfs dfs -rm foo", ["hdfs"], True),
|
|
|
|
("git diff", ["git", "hub"], True),
|
|
|
|
("hub diff", ["git", "hub"], True),
|
|
|
|
("hg diff", ["git", "hub"], False),
|
|
|
|
],
|
|
|
|
)
|
2015-08-27 16:08:29 +03:00
|
|
|
def test_is_app(script, names, result):
|
2024-12-21 11:20:25 +08:00
|
|
|
assert is_app(Command(script, ""), *names) == result
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.usefixtures("no_memoize")
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"script, names, result",
|
|
|
|
[
|
|
|
|
("/usr/bin/git diff", ["git", "hub"], True),
|
|
|
|
("/bin/hdfs dfs -rm foo", ["hdfs"], True),
|
|
|
|
("git diff", ["git", "hub"], True),
|
|
|
|
("hub diff", ["git", "hub"], True),
|
|
|
|
("hg diff", ["git", "hub"], False),
|
|
|
|
],
|
|
|
|
)
|
2015-08-27 16:08:29 +03:00
|
|
|
def test_for_app(script, names, result):
|
|
|
|
@for_app(*names)
|
2015-09-07 13:00:29 +03:00
|
|
|
def match(command):
|
2015-08-27 16:08:29 +03:00
|
|
|
return True
|
|
|
|
|
2024-12-21 11:20:25 +08:00
|
|
|
assert match(Command(script, "")) == result
|
2015-09-02 11:10:03 +03:00
|
|
|
|
|
|
|
|
|
|
|
class TestCache(object):
|
|
|
|
@pytest.fixture
|
|
|
|
def shelve(self, mocker):
|
|
|
|
value = {}
|
|
|
|
|
2015-09-02 11:54:58 +03:00
|
|
|
class _Shelve(object):
|
|
|
|
def __init__(self, path):
|
|
|
|
pass
|
2015-09-02 11:10:03 +03:00
|
|
|
|
2015-09-02 11:54:58 +03:00
|
|
|
def __setitem__(self, k, v):
|
|
|
|
value[k] = v
|
|
|
|
|
|
|
|
def __getitem__(self, k):
|
|
|
|
return value[k]
|
|
|
|
|
|
|
|
def get(self, k, v=None):
|
|
|
|
return value.get(k, v)
|
|
|
|
|
|
|
|
def close(self):
|
|
|
|
return
|
|
|
|
|
2024-12-21 11:20:25 +08:00
|
|
|
mocker.patch("thefuck.utils.shelve.open", new_callable=lambda: _Shelve)
|
2015-09-02 11:10:03 +03:00
|
|
|
return value
|
|
|
|
|
2017-10-10 08:30:26 +02:00
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
def enable_cache(self, monkeypatch, shelve):
|
2024-12-21 11:20:25 +08:00
|
|
|
monkeypatch.setattr("thefuck.utils.cache.disabled", False)
|
2017-10-10 08:31:45 +02:00
|
|
|
_cache._init_db()
|
2017-10-10 08:30:26 +02:00
|
|
|
|
2015-09-02 11:10:03 +03:00
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
def mtime(self, mocker):
|
2024-12-21 11:20:25 +08:00
|
|
|
mocker.patch("thefuck.utils.os.path.getmtime", return_value=0)
|
2015-09-02 11:10:03 +03:00
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def fn(self):
|
2024-12-21 11:20:25 +08:00
|
|
|
@cache("~/.bashrc")
|
2015-09-02 11:10:03 +03:00
|
|
|
def fn():
|
2024-12-21 11:20:25 +08:00
|
|
|
return "test"
|
2015-09-02 11:10:03 +03:00
|
|
|
|
|
|
|
return fn
|
|
|
|
|
2015-09-02 11:54:58 +03:00
|
|
|
@pytest.fixture
|
2017-10-10 08:30:26 +02:00
|
|
|
def key(self, monkeypatch):
|
2024-12-21 11:20:25 +08:00
|
|
|
monkeypatch.setattr("thefuck.utils.Cache._get_key", lambda *_: "key")
|
|
|
|
return "key"
|
2015-09-02 11:54:58 +03:00
|
|
|
|
|
|
|
def test_with_blank_cache(self, shelve, fn, key):
|
2015-09-02 11:10:03 +03:00
|
|
|
assert shelve == {}
|
2024-12-21 11:20:25 +08:00
|
|
|
assert fn() == "test"
|
|
|
|
assert shelve == {key: {"etag": "0", "value": "test"}}
|
2015-09-02 11:54:58 +03:00
|
|
|
|
|
|
|
def test_with_filled_cache(self, shelve, fn, key):
|
2024-12-21 11:20:25 +08:00
|
|
|
cache_value = {key: {"etag": "0", "value": "new-value"}}
|
2015-09-02 11:10:03 +03:00
|
|
|
shelve.update(cache_value)
|
2024-12-21 11:20:25 +08:00
|
|
|
assert fn() == "new-value"
|
2015-09-02 11:10:03 +03:00
|
|
|
assert shelve == cache_value
|
|
|
|
|
2015-09-02 11:54:58 +03:00
|
|
|
def test_when_etag_changed(self, shelve, fn, key):
|
2024-12-21 11:20:25 +08:00
|
|
|
shelve.update({key: {"etag": "-1", "value": "old-value"}})
|
|
|
|
assert fn() == "test"
|
|
|
|
assert shelve == {key: {"etag": "0", "value": "test"}}
|
2015-09-07 13:00:29 +03:00
|
|
|
|
|
|
|
|
2016-03-13 15:10:37 +03:00
|
|
|
class TestGetValidHistoryWithoutCurrent(object):
|
2021-06-29 12:26:13 -07:00
|
|
|
@pytest.fixture(autouse=True)
|
2016-05-08 13:20:37 -03:00
|
|
|
def fail_on_warning(self):
|
2024-12-21 11:20:25 +08:00
|
|
|
warnings.simplefilter("error")
|
2016-05-08 13:20:37 -03:00
|
|
|
yield
|
|
|
|
warnings.resetwarnings()
|
|
|
|
|
2016-03-13 15:10:37 +03:00
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
def history(self, mocker):
|
2024-12-21 11:20:25 +08:00
|
|
|
mock = mocker.patch("thefuck.shells.shell.get_history")
|
2019-12-16 21:55:19 +01:00
|
|
|
# Passing as an argument causes `UnicodeDecodeError`
|
2021-11-23 15:45:46 +02:00
|
|
|
# with newer pytest and python 2.7
|
2024-12-21 11:20:25 +08:00
|
|
|
mock.return_value = [
|
|
|
|
"le cat",
|
|
|
|
"fuck",
|
|
|
|
"ls cat",
|
|
|
|
"diff x",
|
|
|
|
"nocommand x",
|
|
|
|
"café ô",
|
|
|
|
]
|
2019-12-16 21:55:19 +01:00
|
|
|
return mock
|
2016-03-13 15:10:37 +03:00
|
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
def alias(self, mocker):
|
2024-12-21 11:20:25 +08:00
|
|
|
return mocker.patch("thefuck.utils.get_alias", return_value="fuck")
|
2016-03-13 15:10:37 +03:00
|
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
2017-08-03 12:18:05 +02:00
|
|
|
def bins(self, mocker):
|
2016-05-08 13:20:37 -03:00
|
|
|
callables = list()
|
2024-12-21 11:20:25 +08:00
|
|
|
for name in ["diff", "ls", "café"]:
|
2016-05-08 13:20:37 -03:00
|
|
|
bin_mock = mocker.Mock(name=name)
|
|
|
|
bin_mock.configure_mock(name=name, is_dir=lambda: False)
|
|
|
|
callables.append(bin_mock)
|
|
|
|
path_mock = mocker.Mock(iterdir=mocker.Mock(return_value=callables))
|
2024-12-21 11:20:25 +08:00
|
|
|
return mocker.patch("thefuck.utils.Path", return_value=path_mock)
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"script, result",
|
|
|
|
[
|
|
|
|
("le cat", ["ls cat", "diff x"]),
|
|
|
|
("diff x", ["ls cat"]),
|
|
|
|
("fuck", ["ls cat", "diff x"]),
|
|
|
|
("cafe ô", ["ls cat", "diff x"]),
|
|
|
|
],
|
|
|
|
)
|
2016-03-13 15:10:37 +03:00
|
|
|
def test_get_valid_history_without_current(self, script, result):
|
2024-12-21 11:20:25 +08:00
|
|
|
command = Command(script, "")
|
2016-03-13 15:10:37 +03:00
|
|
|
assert get_valid_history_without_current(command) == result
|