diff options
Diffstat (limited to 'tests/test_parsing.py')
-rw-r--r-- | tests/test_parsing.py | 518 |
1 files changed, 258 insertions, 260 deletions
diff --git a/tests/test_parsing.py b/tests/test_parsing.py index b61e2d06..ab8ed098 100644 --- a/tests/test_parsing.py +++ b/tests/test_parsing.py @@ -1,327 +1,262 @@ # coding=utf-8 """ -Unit/functional testing for helper functions/classes in the cmd2.py module. - -These are primarily tests related to parsing. Moreover, they are mostly a port of the old doctest tests which were -problematic because they worked properly for some versions of pyparsing but not for others. +Test the parsing logic in parsing.py Copyright 2017 Todd Leonhardt <todd.leonhardt@gmail.com> Released under MIT license, see LICENSE file """ -from cmd2 import cmd2 import pytest +from cmd2 import cmd2 +from cmd2.parsing import StatementParser -@pytest.fixture -def hist(): - from cmd2.cmd2 import HistoryItem - h = cmd2.History([HistoryItem('first'), HistoryItem('second'), HistoryItem('third'), HistoryItem('fourth')]) - return h -# Case-sensitive parser @pytest.fixture def parser(): - c = cmd2.Cmd() - c.multilineCommands = ['multiline'] - c.parser_manager = cmd2.ParserManager(redirector=c.redirector, terminators=c.terminators, - multilineCommands=c.multilineCommands, legalChars=c.legalChars, - commentGrammars=c.commentGrammars, commentInProgress=c.commentInProgress, - blankLinesAllowed=c.blankLinesAllowed, prefixParser=c.prefixParser, - preparse=c.preparse, postparse=c.postparse, aliases=c.aliases, - shortcuts=c.shortcuts) - return c.parser_manager.main_parser - -# Case-sensitive ParserManager -@pytest.fixture -def cs_pm(): - c = cmd2.Cmd() - c.multilineCommands = ['multiline'] - c.parser_manager = cmd2.ParserManager(redirector=c.redirector, terminators=c.terminators, - multilineCommands=c.multilineCommands, legalChars=c.legalChars, - commentGrammars=c.commentGrammars, commentInProgress=c.commentInProgress, - blankLinesAllowed=c.blankLinesAllowed, prefixParser=c.prefixParser, - preparse=c.preparse, postparse=c.postparse, aliases=c.aliases, - shortcuts=c.shortcuts) - return c.parser_manager - - -@pytest.fixture -def input_parser(): - c = cmd2.Cmd() - return c.parser_manager.input_source_parser - - -def test_history_span(hist): - h = hist - assert h == ['first', 'second', 'third', 'fourth'] - assert h.span('-2..') == ['third', 'fourth'] - assert h.span('2..3') == ['second', 'third'] # Inclusive of end - assert h.span('3') == ['third'] - assert h.span(':') == h - assert h.span('2..') == ['second', 'third', 'fourth'] - assert h.span('-1') == ['fourth'] - assert h.span('-2..-3') == ['third', 'second'] - assert h.span('*') == h - -def test_history_get(hist): - h = hist - assert h == ['first', 'second', 'third', 'fourth'] - assert h.get('') == h - assert h.get('-2') == h[:-2] - assert h.get('5') == [] - assert h.get('2-3') == ['second'] # Exclusive of end - assert h.get('ir') == ['first', 'third'] # Normal string search for all elements containing "ir" - assert h.get('/i.*d/') == ['third'] # Regex string search "i", then anything, then "d" - - -def test_cast(): - cast = cmd2.cast - - # Boolean - assert cast(True, True) == True - assert cast(True, False) == False - assert cast(True, 0) == False - assert cast(True, 1) == True - assert cast(True, 'on') == True - assert cast(True, 'off') == False - assert cast(True, 'ON') == True - assert cast(True, 'OFF') == False - assert cast(True, 'y') == True - assert cast(True, 'n') == False - assert cast(True, 't') == True - assert cast(True, 'f') == False - - # Non-boolean same type - assert cast(1, 5) == 5 - assert cast(3.4, 2.7) == 2.7 - assert cast('foo', 'bar') == 'bar' - assert cast([1,2], [3,4]) == [3,4] - - -def test_cast_problems(capsys): - cast = cmd2.cast - - expected = 'Problem setting parameter (now {}) to {}; incorrect type?\n' - - # Boolean current, with new value not convertible to bool - current = True - new = [True, True] - assert cast(current, new) == current - out, err = capsys.readouterr() - assert out == expected.format(current, new) - - # Non-boolean current, with new value not convertible to current type - current = 1 - new = 'octopus' - assert cast(current, new) == current - out, err = capsys.readouterr() - assert out == expected.format(current, new) - + parser = StatementParser( + allow_redirection=True, + terminators=[';'], + multiline_commands=['multiline'], + aliases={'helpalias': 'help', + '42': 'theanswer', + 'l': '!ls -al', + 'anothermultiline': 'multiline', + 'fake': 'pyscript'}, + shortcuts=[('?', 'help'), ('!', 'shell')] + ) + return parser def test_parse_empty_string(parser): - assert parser.parseString('').dump() == '[]' - -def test_parse_only_comment(parser): - assert parser.parseString('/* empty command */').dump() == '[]' - -def test_parse_single_word(parser): - line = 'plainword' - results = parser.parseString(line) - assert results.command == line - -def test_parse_word_plus_terminator(parser): + statement = parser.parse('') + assert not statement.command + assert not statement.args + assert statement.raw == '' + +@pytest.mark.parametrize('line,tokens', [ + ('command', ['command']), + ('command /* with some comment */ arg', ['command', 'arg']), + ('command arg1 arg2 # comment at the end', ['command', 'arg1', 'arg2']), + ('42 arg1 arg2', ['theanswer', 'arg1', 'arg2']), + ('l', ['shell', 'ls', '-al']) +]) +def test_tokenize(parser, line, tokens): + tokens_to_test = parser.tokenize(line) + assert tokens_to_test == tokens + +@pytest.mark.parametrize('tokens,command,args', [ + ([], None, ''), + (['command'], 'command', ''), + (['command', 'arg1', 'arg2'], 'command', 'arg1 arg2') +]) +def test_command_and_args(parser, tokens, command, args): + (parsed_command, parsed_args) = parser._command_and_args(tokens) + assert command == parsed_command + assert args == parsed_args + +@pytest.mark.parametrize('line', [ + 'plainword', + '"one word"', + "'one word'", +]) +def test_single_word(parser, line): + statement = parser.parse(line) + assert statement.command == line + +def test_word_plus_terminator(parser): line = 'termbare;' - results = parser.parseString(line) - assert results.command == 'termbare' - assert results.terminator == ';' + statement = parser.parse(line) + assert statement.command == 'termbare' + assert statement.terminator == ';' -def test_parse_suffix_after_terminator(parser): +def test_suffix_after_terminator(parser): line = 'termbare; suffx' - results = parser.parseString(line) - assert results.command == 'termbare' - assert results.terminator == ';' - assert results.suffix == 'suffx' + statement = parser.parse(line) + assert statement.command == 'termbare' + assert statement.terminator == ';' + assert statement.suffix == 'suffx' -def test_parse_command_with_args(parser): +def test_command_with_args(parser): line = 'command with args' - results = parser.parseString(line) - assert results.command == 'command' - assert results.args == 'with args' + statement = parser.parse(line) + assert statement.command == 'command' + assert statement.args == 'with args' + assert not statement.pipe_to + +def test_command_with_quoted_args(parser): + line = 'command with "quoted args" and "some not"' + statement = parser.parse(line) + assert statement.command == 'command' + assert statement.args == 'with "quoted args" and "some not"' def test_parse_command_with_args_terminator_and_suffix(parser): line = 'command with args and terminator; and suffix' - results = parser.parseString(line) - assert results.command == 'command' - assert results.args == "with args and terminator" - assert results.terminator == ';' - assert results.suffix == 'and suffix' - -def test_parse_simple_piped(parser): - line = 'simple | piped' - results = parser.parseString(line) - assert results.command == 'simple' - assert results.pipeTo == " piped" - -def test_parse_double_pipe_is_not_a_pipe(parser): + statement = parser.parse(line) + assert statement.command == 'command' + assert statement.args == "with args and terminator" + assert statement.terminator == ';' + assert statement.suffix == 'and suffix' + +def test_hashcomment(parser): + statement = parser.parse('hi # this is all a comment') + assert statement.command == 'hi' + assert not statement.args + assert not statement.pipe_to + +def test_c_comment(parser): + statement = parser.parse('hi /* this is | all a comment */') + assert statement.command == 'hi' + assert not statement.args + assert not statement.pipe_to + +def test_c_comment_empty(parser): + statement = parser.parse('/* this is | all a comment */') + assert not statement.command + assert not statement.args + assert not statement.pipe_to + +def test_parse_what_if_quoted_strings_seem_to_start_comments(parser): + statement = parser.parse('what if "quoted strings /* seem to " start comments?') + assert statement.command == 'what' + assert statement.args == 'if "quoted strings /* seem to " start comments?' + assert not statement.pipe_to + +def test_simple_piped(parser): + statement = parser.parse('simple | piped') + assert statement.command == 'simple' + assert not statement.args + assert statement.pipe_to == 'piped' + +def test_double_pipe_is_not_a_pipe(parser): line = 'double-pipe || is not a pipe' - results = parser.parseString(line) - assert results.command == 'double-pipe' - assert results.args == '|| is not a pipe' - assert not 'pipeTo' in results + statement = parser.parse(line) + assert statement.command == 'double-pipe' + assert statement.args == '|| is not a pipe' + assert not statement.pipe_to -def test_parse_complex_pipe(parser): +def test_complex_pipe(parser): line = 'command with args, terminator;sufx | piped' - results = parser.parseString(line) - assert results.command == 'command' - assert results.args == "with args, terminator" - assert results.terminator == ';' - assert results.suffix == 'sufx' - assert results.pipeTo == ' piped' - -def test_parse_output_redirect(parser): + statement = parser.parse(line) + assert statement.command == 'command' + assert statement.args == "with args, terminator" + assert statement.terminator == ';' + assert statement.suffix == 'sufx' + assert statement.pipe_to == 'piped' + +def test_output_redirect(parser): line = 'output into > afile.txt' - results = parser.parseString(line) - assert results.command == 'output' - assert results.args == 'into' - assert results.output == '>' - assert results.outputTo == 'afile.txt' + statement = parser.parse(line) + assert statement.command == 'output' + assert statement.args == 'into' + assert statement.output == '>' + assert statement.output_to == 'afile.txt' -def test_parse_output_redirect_with_dash_in_path(parser): +def test_output_redirect_with_dash_in_path(parser): line = 'output into > python-cmd2/afile.txt' - results = parser.parseString(line) - assert results.command == 'output' - assert results.args == 'into' - assert results.output == '>' - assert results.outputTo == 'python-cmd2/afile.txt' - - -def test_case_sensitive_parsed_single_word(cs_pm): - line = 'HeLp' - statement = cs_pm.parsed(line) - assert statement.parsed.command == line - - -def test_parse_input_redirect(input_parser): - line = '< afile.txt' - results = input_parser.parseString(line) - assert results.inputFrom == line - -def test_parse_input_redirect_with_dash_in_path(input_parser): - line = "< python-cmd2/afile.txt" - results = input_parser.parseString(line) - assert results.inputFrom == line - -def test_parse_pipe_and_redirect(parser): + statement = parser.parse(line) + assert statement.command == 'output' + assert statement.args == 'into' + assert statement.output == '>' + assert statement.output_to == 'python-cmd2/afile.txt' + +def test_output_redirect_append(parser): + line = 'output appended to >> /tmp/afile.txt' + statement = parser.parse(line) + assert statement.command == 'output' + assert statement.args == 'appended to' + assert statement.output == '>>' + assert statement.output_to == '/tmp/afile.txt' + +def test_pipe_and_redirect(parser): line = 'output into;sufx | pipethrume plz > afile.txt' - results = parser.parseString(line) - assert results.command == 'output' - assert results.args == 'into' - assert results.terminator == ';' - assert results.suffix == 'sufx' - assert results.pipeTo == ' pipethrume plz' - assert results.output == '>' - assert results.outputTo == 'afile.txt' + statement = parser.parse(line) + assert statement.command == 'output' + assert statement.args == 'into' + assert statement.terminator == ';' + assert statement.suffix == 'sufx' + assert statement.pipe_to == 'pipethrume plz' + assert statement.output == '>' + assert statement.output_to == 'afile.txt' def test_parse_output_to_paste_buffer(parser): line = 'output to paste buffer >> ' - results = parser.parseString(line) - assert results.command == 'output' - assert results.args == 'to paste buffer' - assert results.output == '>>' - -def test_parse_ignore_commented_redirectors(parser): - line = 'ignore the /* commented | > */ stuff;' - results = parser.parseString(line) - assert results.command == 'ignore' - assert results.args == 'the /* commented | > */ stuff' - assert results.terminator == ';' - -def test_parse_has_redirect_inside_terminator(parser): + statement = parser.parse(line) + assert statement.command == 'output' + assert statement.args == 'to paste buffer' + assert statement.output == '>>' + +def test_has_redirect_inside_terminator(parser): """The terminator designates the end of the commmand/arguments portion. If a redirector occurs before a terminator, then it will be treated as part of the arguments and not as a redirector.""" line = 'has > inside;' - results = parser.parseString(line) - assert results.command == 'has' - assert results.args == '> inside' - assert results.terminator == ';' - -def test_parse_what_if_quoted_strings_seem_to_start_comments(parser): - line = 'what if "quoted strings /* seem to " start comments?' - results = parser.parseString(line) - assert results.command == 'what' - assert results.args == 'if "quoted strings /* seem to " start comments?' + statement = parser.parse(line) + assert statement.command == 'has' + assert statement.args == '> inside' + assert statement.terminator == ';' def test_parse_unfinished_multiliine_command(parser): line = 'multiline has > inside an unfinished command' - results = parser.parseString(line) - assert results.multilineCommand == 'multiline' - assert not 'args' in results + statement = parser.parse(line) + assert statement.multiline_command == 'multiline' + assert statement.command == 'multiline' + assert statement.args == 'has > inside an unfinished command' + assert not statement.terminator def test_parse_multiline_command_ignores_redirectors_within_it(parser): line = 'multiline has > inside;' - results = parser.parseString(line) - assert results.multilineCommand == 'multiline' - assert results.args == 'has > inside' - assert results.terminator == ';' + statement = parser.parse(line) + assert statement.multiline_command == 'multiline' + assert statement.args == 'has > inside' + assert statement.terminator == ';' def test_parse_multiline_with_incomplete_comment(parser): """A terminator within a comment will be ignored and won't terminate a multiline command. Un-closed comments effectively comment out everything after the start.""" line = 'multiline command /* with comment in progress;' - results = parser.parseString(line) - assert results.multilineCommand == 'multiline' - assert not 'args' in results + statement = parser.parse(line) + assert statement.multiline_command == 'multiline' + assert statement.args == 'command' + assert not statement.terminator def test_parse_multiline_with_complete_comment(parser): line = 'multiline command /* with comment complete */ is done;' - results = parser.parseString(line) - assert results.multilineCommand == 'multiline' - assert results.args == 'command /* with comment complete */ is done' - assert results.terminator == ';' + statement = parser.parse(line) + assert statement.multiline_command == 'multiline' + assert statement.args == 'command is done' + assert statement.terminator == ';' def test_parse_multiline_termninated_by_empty_line(parser): line = 'multiline command ends\n\n' - results = parser.parseString(line) - assert results.multilineCommand == 'multiline' - assert results.args == 'command ends' - assert len(results.terminator) == 2 - assert results.terminator[0] == '\n' - assert results.terminator[1] == '\n' + statement = parser.parse(line) + assert statement.multiline_command == 'multiline' + assert statement.args == 'command ends' + assert statement.terminator == '\n' def test_parse_multiline_ignores_terminators_in_comments(parser): line = 'multiline command "with term; ends" now\n\n' - results = parser.parseString(line) - assert results.multilineCommand == 'multiline' - assert results.args == 'command "with term; ends" now' - assert len(results.terminator) == 2 - assert results.terminator[0] == '\n' - assert results.terminator[1] == '\n' + statement = parser.parse(line) + assert statement.multiline_command == 'multiline' + assert statement.args == 'command "with term; ends" now' + assert statement.terminator == '\n' def test_parse_command_with_unicode_args(parser): line = 'drink café' - results = parser.parseString(line) - assert results.command == 'drink' - assert results.args == 'café' + statement = parser.parse(line) + assert statement.command == 'drink' + assert statement.args == 'café' def test_parse_unicode_command(parser): line = 'café au lait' - results = parser.parseString(line) - assert results.command == 'café' - assert results.args == 'au lait' + statement = parser.parse(line) + assert statement.command == 'café' + assert statement.args == 'au lait' def test_parse_redirect_to_unicode_filename(parser): line = 'dir home > café' - results = parser.parseString(line) - assert results.command == 'dir' - assert results.args == 'home' - assert results.output == '>' - assert results.outputTo == 'café' - -def test_parse_input_redirect_from_unicode_filename(input_parser): - line = '< café' - results = input_parser.parseString(line) - assert results.inputFrom == line - + statement = parser.parse(line) + assert statement.command == 'dir' + assert statement.args == 'home' + assert statement.output == '>' + assert statement.output_to == 'café' def test_empty_statement_raises_exception(): app = cmd2.Cmd() @@ -330,3 +265,66 @@ def test_empty_statement_raises_exception(): with pytest.raises(cmd2.EmptyStatement): app._complete_statement(' ') + +@pytest.mark.parametrize('line,command,args', [ + ('helpalias', 'help', ''), + ('helpalias mycommand', 'help', 'mycommand'), + ('42', 'theanswer', ''), + ('42 arg1 arg2', 'theanswer', 'arg1 arg2'), + ('!ls', 'shell', 'ls'), + ('!ls -al /tmp', 'shell', 'ls -al /tmp'), + ('l', 'shell', 'ls -al') +]) +def test_alias_and_shortcut_expansion(parser, line, command, args): + statement = parser.parse(line) + assert statement.command == command + assert statement.args == args + +def test_alias_on_multiline_command(parser): + line = 'anothermultiline has > inside an unfinished command' + statement = parser.parse(line) + assert statement.multiline_command == 'multiline' + assert statement.command == 'multiline' + assert statement.args == 'has > inside an unfinished command' + assert not statement.terminator + +def test_parse_command_only_command_and_args(parser): + line = 'help history' + statement = parser.parse_command_only(line) + assert statement.command == 'help' + assert statement.args == 'history' + assert statement.command_and_args == line + +def test_parse_command_only_emptyline(parser): + line = '' + statement = parser.parse_command_only(line) + assert statement.command is None + assert statement.args is '' + assert statement.command_and_args is line + +def test_parse_command_only_strips_line(parser): + line = ' help history ' + statement = parser.parse_command_only(line) + assert statement.command == 'help' + assert statement.args == 'history' + assert statement.command_and_args == line.strip() + +def test_parse_command_only_expands_alias(parser): + line = 'fake foobar.py' + statement = parser.parse_command_only(line) + assert statement.command == 'pyscript' + assert statement.args == 'foobar.py' + +def test_parse_command_only_expands_shortcuts(parser): + line = '!cat foobar.txt' + statement = parser.parse_command_only(line) + assert statement.command == 'shell' + assert statement.args == 'cat foobar.txt' + assert statement.command_and_args == line.replace('!', 'shell ') + +def test_parse_command_only_quoted_args(parser): + line = 'l "/tmp/directory with spaces/doit.sh"' + statement = parser.parse_command_only(line) + assert statement.command == 'shell' + assert statement.args == 'ls -al "/tmp/directory with spaces/doit.sh"' + assert statement.command_and_args == line.replace('l', 'shell ls -al') |