Модульный тест для скрипта завершения Bash

Я хотел бы написать модульный тест для (довольно сложного) сценария завершения Bash, предпочтительно с Python - просто что-то, что программно получает значения завершения Bash. Тест должен выглядеть так:

def test_completion():
  # trigger_completion should return what a user should get on triggering 
  # Bash completion like this: 'pbt createkvm<TAB>' 
  assert trigger_completion('pbt createkvm') == "module1 module2 module3" 

Как я могу программно смоделировать завершение Bash, чтобы проверить значения завершения внутри набора тестов для моего инструмента?


person Wolkenarchitekt    schedule 04.02.2012    source источник
comment
Помогает ли это? Хотя я сам не пробовал.   -  person Vikas    schedule 04.02.2012
comment
Если вы хотите имитировать взаимодействие с пользователем, expect — ваш друг — выполните настройку fs/env в python, а затем запустите bash из ожидаемого сценария с подпроцессом. Побег может быть сложным, но в остальном это довольно просто.   -  person je4d    schedule 11.02.2012
comment
@ je4d - Что ожидать в этом контексте?   -  person Lou    schedule 10.12.2020


Ответы (2)


Скажем, у вас есть скрипт завершения bash в файле с именем asdf-completion, содержащий:

_asdf() {
COMPREPLY=()
local cur prev
cur=$(_get_cword)
COMPREPLY=( $( compgen -W "one two three four five six" -- "$cur") )
return 0
}    
complete -F _asdf asdf

Это использует функцию оболочки _asdf для обеспечения завершения вымышленной команды asdf. Если мы установим правильные переменные среды (из справочной страницы bash), то мы можем получить тот же результат, то есть размещение потенциальных расширений в переменной COMPREPLY. Вот пример выполнения этого в юнит-тесте:

import subprocess
import unittest

class BashTestCase(unittest.TestCase):
    def test_complete(self):
        completion_file="asdf-completion"
        partial_word="f"
        cmd=["asdf", "other", "arguments", partial_word]
        cmdline = ' '.join(cmd)

        out = subprocess.Popen(['bash', '-i', '-c',
            r'source {compfile}; COMP_LINE="{cmdline}" COMP_WORDS=({cmdline}) COMP_CWORD={cword} COMP_POINT={cmdlen} $(complete -p {cmd} | sed "s/.*-F \\([^ ]*\\) .*/\\1/") && echo ${{COMPREPLY[*]}}'.format(
                compfile=completion_file, cmdline=cmdline, cmdlen=len(cmdline), cmd=cmd[0], cword=cmd.index(partial_word)
                )],
            stdout=subprocess.PIPE)
        stdout, stderr = out.communicate()
        self.assertEqual(stdout, "four five\n")

if (__name__=='__main__'):
    unittest.main()

Это должно работать для любых дополнений, использующих -F, но может работать и для других.

Комментарий je4d к использованию expect хорош для более полного теста.

person bonsaiviking    schedule 29.02.2012
comment
Итак, если я правильно читаю ваш sed (и я сомневаюсь в этом) ... вы берете вывод complete -p {cmd} и удаляете все до complete -F, сохраняя имя функции без начального пробела? Итак, если ваш complete -p {cmd} вывел complete -F _comp_func somecmd, тогда ваш sed получит _comp_func ... я близко? - person Lou; 10.12.2020
comment
@Лу Верно. Форматирование строк Python помещает строки параметров в переменные среды в команде оболочки. Сама команда считывает файл с функциями завершения, а затем вызывает функцию завершения (извлекаемую, как вы описали) так же, как это делает Bash внутри. Наконец, он выводит переменную COMPREPLY, чтобы subprocess.communicate() мог прочитать ее в STDOUT. - person bonsaiviking; 10.12.2020
comment
О, отлично, спасибо за разъяснение! Seds может быть трудно читать, если вы их не написали, я думаю :) - person Lou; 11.12.2020

решение bonsaiviking почти сработало для меня. Мне пришлось изменить скрипт строки bash. Я добавил лишний ';' разделитель для исполняемого скрипта bash, иначе выполнение не будет работать в Mac OS X. Не совсем понимаю, почему.

Я также немного обобщил инициализацию различных аргументов COMP_ для обработки различных случаев, с которыми я столкнулся.

Окончательное решение — это вспомогательный класс для проверки завершения bash из python, чтобы приведенный выше тест был записан как:

from completion import BashCompletionTest

class AdsfTestCase(BashCompletionTest):
    def test_orig(self):
        self.run_complete("other arguments f", "four five")

    def run_complete(self, command, expected):
        completion_file="adsf-completion"
        program="asdf"
        super(AdsfTestCase, self).run_complete(completion_file, program, command, expected)


if (__name__=='__main__'):
    unittest.main()

Библиотека завершения находится по адресу https://github.com/lacostej/unity3d-bash-completion/blob/master/lib/completion.py

person coffeebreaks    schedule 21.04.2012