From 1d5e40a7c9314ef647d0c04b62cae2a76e688368 Mon Sep 17 00:00:00 2001 From: Josh Date: Sat, 29 Feb 2020 20:22:30 -0600 Subject: [PATCH] #962: Add the new apt_unable_to_locate rule Fix #962 --- README.md | 1 + tests/rules/test_apt_unable_to_locate.py | 153 +++++++++++++++++++++++ thefuck/rules/apt_unable_to_locate.py | 55 ++++++++ 3 files changed, 209 insertions(+) create mode 100644 tests/rules/test_apt_unable_to_locate.py create mode 100644 thefuck/rules/apt_unable_to_locate.py diff --git a/README.md b/README.md index 4ff50cd2..3aeca8db 100644 --- a/README.md +++ b/README.md @@ -313,6 +313,7 @@ The following rules are enabled by default on specific platforms only: * `apt_get_search` – changes trying to search using `apt-get` with searching using `apt-cache`; * `apt_invalid_operation` – fixes invalid `apt` and `apt-get` calls, like `apt-get isntall vim`; * `apt_list_upgradable` – helps you run `apt list --upgradable` after `apt update`; +* `apt_unable_to_locate rule` – search for packages similar to the one intended with `apt-get` or `apt`; * `apt_upgrade` – helps you run `apt upgrade` after `apt list --upgradable`; * `brew_cask_dependency` – installs cask dependencies; * `brew_install` – fixes formula name for `brew install`; diff --git a/tests/rules/test_apt_unable_to_locate.py b/tests/rules/test_apt_unable_to_locate.py new file mode 100644 index 00000000..7ed99d2b --- /dev/null +++ b/tests/rules/test_apt_unable_to_locate.py @@ -0,0 +1,153 @@ +from io import BytesIO + +import pytest +from thefuck.rules.apt_unable_to_locate import match, get_new_command, _get_search_results +from thefuck.types import Command + +invalid_operation = 'E: Unable to locate package {}'.format +apt_rabbitmq_search_results = b''' kamailio-rabbitmq-modules/bionic 5.1.2-1ubuntu2 amd64 + RabbitMQ and AMQP integration modules for the Kamailio SIP server + +libanyevent-rabbitmq-perl/bionic 1.19+dfsg-1 all + asynchronous and multi channel Perl AMQP client + +libmojo-rabbitmq-client-perl/bionic 0.1.0-1 all + Mojo::IOLoop based RabbitMQ client + +libmono-messaging-rabbitmq4.0-cil/bionic 4.6.2.7+dfsg-1ubuntu1 all + Mono Messaging RabbitMQ library (for CLI 4.0) + +libmono-rabbitmq4.0-cil/bionic 4.6.2.7+dfsg-1ubuntu1 all + Mono RabbitMQ.Client library (for CLI 4.0) + +librabbitmq-client-java/bionic 5.0.0-1 all + RabbitMQ Java client + +librabbitmq-dbg/bionic-updates,bionic-security 0.8.0-1ubuntu0.18.04.2 amd64 + AMQP client library written in C - Debug Files + +librabbitmq-dev/bionic-updates,bionic-security 0.8.0-1ubuntu0.18.04.2 amd64 + AMQP client library written in C - Dev Files + +librabbitmq4/bionic-updates,bionic-security 0.8.0-1ubuntu0.18.04.2 amd64 + AMQP client library written in C + +nagios-plugins-rabbitmq/bionic-updates 1:1.2.0-2.2ubuntu0.18.04.1 all + Set of Nagios checks useful for monitoring a RabbitMQ server + +opensips-rabbitmq-module/bionic 2.2.2-3build4 amd64 + Interface module to interact with a RabbitMQ server + +puppet-module-puppetlabs-rabbitmq/bionic 5.3.1-2 all + Puppet module for rabbitmq, manage everything from vhosts to exchanges + +rabbitmq-server/bionic 3.6.10-1 all + AMQP server written in Erlang + +''' +apt_get_rabbitmq_search_results = b''' +kamailio-rabbitmq-modules - RabbitMQ and AMQP integration modules for the Kamailio SIP server +libanyevent-rabbitmq-perl - asynchronous and multi channel Perl AMQP client +libmojo-rabbitmq-client-perl - Mojo::IOLoop based RabbitMQ client +libmono-messaging-rabbitmq4.0-cil - Mono Messaging RabbitMQ library (for CLI 4.0) +libmono-rabbitmq4.0-cil - Mono RabbitMQ.Client library (for CLI 4.0) +librabbitmq-client-java - RabbitMQ Java client +librabbitmq-dbg - AMQP client library written in C - Debug Files +librabbitmq-dev - AMQP client library written in C - Dev Files +librabbitmq4 - AMQP client library written in C +nagios-plugins-rabbitmq - Set of Nagios checks useful for monitoring a RabbitMQ server +opensips-rabbitmq-module - Interface module to interact with a RabbitMQ server +puppet-module-puppetlabs-rabbitmq - Puppet module for rabbitmq, manage everything from vhosts to exchanges +rabbitmq-server - AMQP server written in Erlang +''' +rabbitmq_search_search_results = [ + 'kamailio-rabbitmq-modules', + 'libanyevent-rabbitmq-perl', + 'libmojo-rabbitmq-client-perl', + 'libmono-messaging-rabbitmq4.0-cil', + 'libmono-rabbitmq4.0-cil', + 'librabbitmq-client-java', + 'librabbitmq-dbg', + 'librabbitmq-dev', + 'librabbitmq4', + 'nagios-plugins-rabbitmq', + 'opensips-rabbitmq-module', + 'puppet-module-puppetlabs-rabbitmq', + 'rabbitmq-server' +] + + +@pytest.mark.parametrize( + 'command', + [ + ( + Command('apt install rabbitmq', invalid_operation('rabbitmq')) + ), + ( + Command('apt-get install rabbitmq', invalid_operation('rabbitmq')) + ) + ] +) +def test_match(command): + assert match(command) + + +@pytest.mark.parametrize( + 'command', + [ + ( + Command('yarn install reactjs', 'a_bad_cmd: command not found') + ), + ( + Command('npm install reactjs', 'a_bad_cmd: command not found') + ), + ( + Command('apt upgrade', 'a_bad_cmd: command not found') + ) + ] +) +def test_not_match(command): + + assert not match(command) + + +@pytest.fixture +def set_search(mocker): + mock = mocker.patch('subprocess.Popen') + + def _set_text(text): + mock.return_value.stdout = BytesIO(text) + + return _set_text + + +@pytest.mark.parametrize( + 'app, command, search_text, search_results', + [ + ('apt', 'rabbitmq', apt_rabbitmq_search_results, rabbitmq_search_search_results), + ('apt-get', 'rabbitmq', apt_get_rabbitmq_search_results, rabbitmq_search_search_results), + ] +) +def test_get_search_results(set_search, app, command, search_text, search_results): + set_search(search_text) + assert _get_search_results(app, command) == search_results + + +@pytest.mark.parametrize( + 'command, expected_command, search_text', + [ + ( + Command('sudo apt install rabbitmq', invalid_operation('rabbitmq')), + [ + 'sudo apt install librabbitmq4', + 'sudo apt install rabbitmq-server', + 'sudo apt install librabbitmq-dev' + ], + apt_rabbitmq_search_results, + ) + ] +) +def test_get_new_command(set_search, command, expected_command, search_text): + set_search(search_text) + actual_command = get_new_command(command) + assert actual_command == expected_command diff --git a/thefuck/rules/apt_unable_to_locate.py b/thefuck/rules/apt_unable_to_locate.py new file mode 100644 index 00000000..fd68eecb --- /dev/null +++ b/thefuck/rules/apt_unable_to_locate.py @@ -0,0 +1,55 @@ +import subprocess + +from thefuck.specific.sudo import sudo_support +from thefuck.utils import for_app, replace_command + + +@sudo_support +@for_app('apt', 'apt-get') +def match(command): + return "E: Unable to locate package" in command.output + + +def _parse_apt_search_results(search_text_lines): + search_results = [] + + for line in search_text_lines: + line = line.decode().strip() + if line.find('/') > 0: + search_results.append(line.split('/')[0]) + + return search_results + + +def _parse_apt_cache_search_results(search_text_lines): + search_results = [] + + for line in search_text_lines: + line = line.decode().strip() + if line.find(' - ') > 0: # spaces are important + search_results.append(line.split(' - ')[0]) + + return search_results + + +def _get_search_results(app, command): + _parser = _parse_apt_search_results + + if app == 'apt-get': + app = 'apt-cache' + _parser = _parse_apt_cache_search_results + + proc = subprocess.Popen([app, 'search', command], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + lines = proc.stdout.readlines() + + return _parser(lines) + + +@sudo_support +def get_new_command(command): + invalid_operation = command.script_parts[-1] + search_results = _get_search_results(command.script_parts[0], invalid_operation) + + return replace_command(command, invalid_operation, search_results)