Back to Repositories

Testing Pacman Command Correction Rules in TheFuck

This test suite validates the functionality of the pacman rule in TheFuck, which handles command-not-found scenarios in Arch Linux by suggesting package installation commands. The tests verify both direct commands and sudo-prefixed commands, ensuring proper package suggestions and command corrections.

Test Coverage Overview

The test suite provides comprehensive coverage of the pacman rule functionality:
  • Command matching for both direct and sudo-prefixed commands
  • Package suggestion generation for common utilities (vim, convert)
  • Multiple package variant handling (e.g., vim variants)
  • Error message pattern matching

Implementation Analysis

The testing approach uses pytest’s parametrized testing to validate multiple scenarios efficiently:
  • Mock integration with subprocess for package queries
  • Parametrized test cases for different command patterns
  • Separation of matching and command generation logic
  • Dynamic command string formatting based on package manager

Technical Details

Testing infrastructure includes:
  • pytest framework with mark decorators
  • Mock library for subprocess isolation
  • Parametrize fixtures for test case variations
  • Command type handling with custom Command class
  • Environment-aware test skipping

Best Practices Demonstrated

The test suite exemplifies several testing best practices:
  • Isolation of external dependencies through mocking
  • Comprehensive edge case coverage
  • Clear test case organization
  • Environment-aware test execution
  • Maintainable test structure with reusable components

nvbn/thefuck

tests/rules/test_pacman.py

            
import pytest
from mock import patch
from thefuck.rules import pacman
from thefuck.rules.pacman import match, get_new_command
from thefuck.types import Command


pacman_cmd = getattr(pacman, 'pacman', 'pacman')

PKGFILE_OUTPUT_SUDO = 'core/sudo 1.8.13-13/usr/bin/sudo'
PKGFILE_OUTPUT_CONVERT = 'extra/imagemagick 6.9.1.0-1\t/usr/bin/convert'

PKGFILE_OUTPUT_VIM = '''extra/gvim 7.4.712-1        \t/usr/bin/vim
extra/gvim-python3 7.4.712-1\t/usr/bin/vim
extra/vim 7.4.712-1         \t/usr/bin/vim
extra/vim-minimal 7.4.712-1 \t/usr/bin/vim
extra/vim-python3 7.4.712-1 \t/usr/bin/vim'''


@pytest.mark.skipif(not getattr(pacman, 'enabled_by_default', True),
                    reason='Skip if pacman is not available')
@pytest.mark.parametrize('command', [
    Command('vim', 'vim: command not found'),
    Command('sudo vim', 'sudo: vim: command not found')])
def test_match(command):
    assert match(command)


@pytest.mark.parametrize('command, return_value', [
    (Command('vim', 'vim: command not found'), PKGFILE_OUTPUT_VIM),
    (Command('sudo vim', 'sudo: vim: command not found'), PKGFILE_OUTPUT_VIM)])
@patch('thefuck.specific.archlinux.subprocess')
@patch.multiple(pacman, create=True, pacman=pacman_cmd)
def test_match_mocked(subp_mock, command, return_value):
    subp_mock.check_output.return_value = return_value
    assert match(command)


@pytest.mark.parametrize('command', [
    Command('vim', ''), Command('', ''),
    Command('sudo vim', ''), Command('', '')])
def test_not_match(command):
    assert not match(command)


sudo_vim_possibilities = ['{} -S extra/gvim && sudo vim',
                          '{} -S extra/gvim-python3 && sudo vim',
                          '{} -S extra/vim && sudo vim',
                          '{} -S extra/vim-minimal && sudo vim',
                          '{} -S extra/vim-python3 && sudo vim']
sudo_vim_possibilities = [s.format(pacman_cmd) for s in sudo_vim_possibilities]

vim_possibilities = ['{} -S extra/gvim && vim',
                     '{} -S extra/gvim-python3 && vim',
                     '{} -S extra/vim && vim',
                     '{} -S extra/vim-minimal && vim',
                     '{} -S extra/vim-python3 && vim']
vim_possibilities = [s.format(pacman_cmd) for s in vim_possibilities]


@pytest.mark.skipif(not getattr(pacman, 'enabled_by_default', True),
                    reason='Skip if pacman is not available')
@pytest.mark.parametrize('command, new_command', [
    (Command('vim', ''), vim_possibilities),
    (Command('sudo vim', ''), sudo_vim_possibilities),
    (Command('convert', ''), ['{} -S extra/imagemagick && convert'.format(pacman_cmd)]),
    (Command('sudo convert', ''), ['{} -S extra/imagemagick && sudo convert'.format(pacman_cmd)])])
def test_get_new_command(command, new_command, mocker):
    assert get_new_command(command) == new_command


@pytest.mark.parametrize('command, new_command, return_value', [
    (Command('vim', ''), vim_possibilities, PKGFILE_OUTPUT_VIM),
    (Command('sudo vim', ''), sudo_vim_possibilities, PKGFILE_OUTPUT_VIM),
    (Command('convert', ''), ['{} -S extra/imagemagick && convert'.format(pacman_cmd)], PKGFILE_OUTPUT_CONVERT),
    (Command('sudo', ''), ['{} -S core/sudo && sudo'.format(pacman_cmd)], PKGFILE_OUTPUT_SUDO),
    (Command('sudo convert', ''), ['{} -S extra/imagemagick && sudo convert'.format(pacman_cmd)], PKGFILE_OUTPUT_CONVERT)])
@patch('thefuck.specific.archlinux.subprocess')
@patch.multiple(pacman, create=True, pacman=pacman_cmd)
def test_get_new_command_mocked(subp_mock, command, new_command, return_value):
    subp_mock.check_output.return_value = return_value
    assert get_new_command(command) == new_command