diff --git a/README.md b/README.md index 48b4b0f..ce3cc71 100644 --- a/README.md +++ b/README.md @@ -308,6 +308,7 @@ following rules are enabled by default: * `no_such_file` – creates missing directories with `mv` and `cp` commands; * `omnienv_no_such_command` – fixes wrong commands for `goenv`, `nodenv`, `pyenv` and `rbenv` (eg.: `pyenv isntall` or `goenv list`); * `open` – either prepends `http://` to address passed to `open` or create a new file or directory and passes it to `open`; +* `ping` – fixes cannot resolve host issues when `http(s)://` is prepended (eg. copy url from browser address bar); * `pip_install` – fixes permission issues with `pip install` commands by adding `--user` or prepending `sudo` if necessary; * `pip_unknown_command` – fixes wrong `pip` commands, for example `pip instatl/pip install`; * `php_s` – replaces `-s` by `-S` when trying to run a local php server; diff --git a/tests/rules/test_ping.py b/tests/rules/test_ping.py new file mode 100644 index 0000000..9308bdc --- /dev/null +++ b/tests/rules/test_ping.py @@ -0,0 +1,57 @@ +import pytest +from thefuck.rules.ping import get_new_command, match +from thefuck.types import Command + + +cannot_resolve_output = ''' +ping: cannot resolve {}: Unknown host +'''.format + +resolve_output = ''' +PING {} ({}): 56 data bytes +64 bytes from {}: icmp_seq=0 ttl=64 time=0.050 ms +'''.format + +@pytest.fixture +def match_output(host): + return cannot_resolve_output(host) + +@pytest.mark.parametrize('script, host', [ + ('ping https://google.com', 'https://google.com'), + ('ping http://google.com', 'http://google.com'), + ('ping http://google.com/', 'http://google.com/'), + ('ping http://google.com/?q=test', 'http://google.com/?q=test'), + ('ping ftp://192.168.0.1', 'ftp://192.168.0.1'), + ('ping -c 10 ftp://192.168.0.1', 'ftp://192.168.0.1'), # allow options at the beginning + ('ping ftp://192.168.0.1 -c 10', 'ftp://192.168.0.1'), # allow options at the end + ('ping -c 10 ftp://192.168.0.1 -t 10', 'ftp://192.168.0.1'), # allow options in between +]) +def test_match(match_output, script, host): + assert match(Command(script, match_output)) + +@pytest.fixture +def no_match_output(resolvable, host, ip): + if resolvable: + return resolve_output(host, ip, ip) + else: + return cannot_resolve_output(host) + +@pytest.mark.parametrize('script, resolvable, host, ip', [ + ('ping http:/google.com/', False, 'http:/google.com/', None), # invalid url + ('ping google.com', True, 'google.com', '142.250.70.174'), + ('ping 127.0.0.1', True, '127.0.0.1', '127.0.0.1'), + ('ping -c 10 google.com', True, 'google.com', '142.250.70.174'), + ('ping google.com -c 10', True, 'google.com', '142.250.70.174'), +]) +def test_not_match(no_match_output, script, resolvable, host, ip): + assert not match(Command(script, no_match_output)) + + +@pytest.mark.parametrize('new_command, script, host', [ + ('ping google.com', 'ping https://google.com', 'https://google.com'), + ('ping -c 10 google.com', 'ping -c 10 https://google.com', 'https://google.com'), + ('ping google.com -c 10', 'ping https://google.com -c 10', 'https://google.com'), + ('ping -c 10 google.com -t 10', 'ping -c 10 https://google.com -t 10', 'https://google.com'), +]) +def test_get_new_command(match_output, new_command, script, host): + assert get_new_command(Command(script, match_output)) == new_command diff --git a/thefuck/rules/ping.py b/thefuck/rules/ping.py new file mode 100644 index 0000000..ca3c81d --- /dev/null +++ b/thefuck/rules/ping.py @@ -0,0 +1,37 @@ +# -*- encoding: utf-8 -*- +from six.moves.urllib.parse import urlparse +from thefuck.utils import for_app, memoize + + +@memoize +def get_hostname_from_url(maybeUrl): + try: + results = urlparse(maybeUrl) + return results.hostname + except Exception: + return None + + +@memoize +def get_index_of_url(parts): + for i, part in enumerate(parts): + if get_hostname_from_url(part): + return i + return None + + +@for_app('ping') +def match(command): + if 'cannot resolve' not in command.output: + return False + + # It only fix if the command has a valid url + index_of_url = get_index_of_url(command.script_parts) + return index_of_url is not None + + +def get_new_command(command): + index_of_url = get_index_of_url(command.script_parts) + url = command.script_parts[index_of_url] + hostname = get_hostname_from_url(url) + return ' '.join(command.script_parts[:index_of_url] + [hostname] + command.script_parts[index_of_url + 1:])