diff options
author | Todd Leonhardt <todd.leonhardt@gmail.com> | 2017-02-04 12:37:38 -0500 |
---|---|---|
committer | Todd Leonhardt <todd.leonhardt@gmail.com> | 2017-02-04 12:37:38 -0500 |
commit | 71d7465c570ea98f6df4ca6b4e88f10c77f4eba0 (patch) | |
tree | 859de63a1a8a35ea42f951a8002f07382773398e | |
parent | b4379b6cc54f263d2f56128dcbd7cd896c386e74 (diff) | |
download | cmd2-git-71d7465c570ea98f6df4ca6b4e88f10c77f4eba0.tar.gz |
Added more and better unit tests for load and save commands
-rwxr-xr-x | cmd2.py | 9 | ||||
-rw-r--r-- | tests/conftest.py | 68 | ||||
-rw-r--r-- | tests/script.txt | 2 | ||||
-rw-r--r-- | tests/test_cmd2.py | 186 | ||||
-rw-r--r-- | tests/test_transcript.py | 21 |
5 files changed, 173 insertions, 113 deletions
@@ -531,7 +531,8 @@ class Cmd(cmd.Cmd): if sys.platform[:3] == 'win': editor = 'notepad' else: - for editor in ['gedit', 'kate', 'vim', 'vi', 'emacs', 'nano', 'pico']: + # Favor command-line editors first so we don't leave the terminal to edit + for editor in ['vim', 'vi', 'emacs', 'nano', 'pico', 'gedit', 'kate', 'subl', 'geany', 'atom']: if _which(editor): break @@ -1332,7 +1333,8 @@ class Cmd(cmd.Cmd): elif args.idx: saveme = self.history[int(args.idx) - 1] else: - saveme = self.history[-1] + # Since this save command has already been added to history, need to go one more back for previous + saveme = self.history[-2] try: f = open(os.path.expanduser(fname), 'w') f.write(saveme) @@ -1374,7 +1376,8 @@ class Cmd(cmd.Cmd): def do_load(self, arg=None): """Runs script of command(s) from a file or URL.""" - if arg is None: + # If arg is None or arg is an empty string, use the default filename + if not arg: targetname = self.default_file_name else: arg = arg.split(None, 1) diff --git a/tests/conftest.py b/tests/conftest.py index e1204086..64cdfd2e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,15 +4,70 @@ # # Copyright 2016 Federico Ceratto <federico.ceratto@gmail.com> # Released under MIT license, see LICENSE file +import sys from pytest import fixture import cmd2 +# Help text for base cmd2.Cmd application +BASE_HELP = """Documented commands (type help <topic>): +======================================== +_load ed history list py save shortcuts +_relative_load edit l load r set show +cmdenvironment hi li pause run shell + +Undocumented commands: +====================== +EOF eof exit help q quit +""" + +# Help text for the history command +HELP_HISTORY = """history [arg]: lists past commands issued + + | no arg: list all + | arg is integer: list one history item, by index + | arg is string: string search + | arg is /enclosed in forward-slashes/: regular expression search + +Usage: history [options] (limit on which commands to include) + +Options: + -h, --help show this help message and exit + -s, --script Script format; no separation lines +""" + +# Output from the shortcuts command with default built-in shortcuts +SHORTCUTS_TXT = """Single-key shortcuts for other commands: +!: shell +?: help +@: load +@@: _relative_load +""" + +expect_colors = True +if sys.platform.startswith('win'): + expect_colors = False +# Output from the show command with default settings +SHOW_TXT = """abbrev: True +case_insensitive: True +colors: {} +continuation_prompt: > +debug: False +default_file_name: command.txt +echo: False +feedback_to_output: False +prompt: (Cmd) +quiet: False +timing: False +""".format(expect_colors) + + class StdOut(object): + """ Toy class for replacing self.stdout in cmd2.Cmd instances fror unit testing. """ def __init__(self): - self.clear() + self.buffer = '' def write(self, s): self.buffer += s @@ -24,19 +79,24 @@ class StdOut(object): self.buffer = '' -def _normalize(block): - # normalize a block of text to perform comparison +def normalize(block): + """ Normalize a block of text to perform comparison. + + Strip newlines from the very beginning and very end Then split into separate lines and strip trailing whitespace + from each line. + """ assert isinstance(block, str) block = block.strip('\n') return [line.rstrip() for line in block.splitlines()] def run_cmd(app, cmd): + """ Clear StdOut buffer, run the command, extract the buffer contents, """ app.stdout.clear() app.onecmd_plus_hooks(cmd) out = app.stdout.buffer app.stdout.clear() - return _normalize(out) + return normalize(out) @fixture diff --git a/tests/script.txt b/tests/script.txt new file mode 100644 index 00000000..1e18262a --- /dev/null +++ b/tests/script.txt @@ -0,0 +1,2 @@ +help +help history diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index e4c9c1e7..24a6c4a0 100644 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -9,10 +9,9 @@ import os import sys import mock -from six import StringIO import cmd2 -from conftest import run_cmd, _normalize +from conftest import run_cmd, normalize, BASE_HELP, HELP_HISTORY, SHORTCUTS_TXT, SHOW_TXT def test_ver(): @@ -21,77 +20,25 @@ def test_ver(): def test_base_help(base_app): out = run_cmd(base_app, 'help') - expected = _normalize(""" -Documented commands (type help <topic>): -======================================== -_load ed history list py save shortcuts -_relative_load edit l load r set show -cmdenvironment hi li pause run shell - -Undocumented commands: -====================== -EOF eof exit help q quit -""") + expected = normalize(BASE_HELP) assert out == expected def test_base_help_history(base_app): out = run_cmd(base_app, 'help history') - expected = _normalize(""" -history [arg]: lists past commands issued - - | no arg: list all - | arg is integer: list one history item, by index - | arg is string: string search - | arg is /enclosed in forward-slashes/: regular expression search - -Usage: history [options] (limit on which commands to include) - -Options: - -h, --help show this help message and exit - -s, --script Script format; no separation lines -""") + expected = normalize(HELP_HISTORY) assert out == expected def test_base_shortcuts(base_app): out = run_cmd(base_app, 'shortcuts') - expected = _normalize(""" -Single-key shortcuts for other commands: -!: shell -?: help -@: load -@@: _relative_load -""") - assert out == expected - - -def notest_base_(base_app): - out = run_cmd(base_app, 'shortcuts') - expected = _normalize(""" -""") + expected = normalize(SHORTCUTS_TXT) assert out == expected def test_base_show(base_app): - import sys out = run_cmd(base_app, 'show') - expect_colors = True - if sys.platform.startswith('win'): - expect_colors = False - expected = _normalize(""" -abbrev: True -case_insensitive: True -colors: {} -continuation_prompt: > -debug: False -default_file_name: command.txt -echo: False -feedback_to_output: False -prompt: (Cmd) -quiet: False -timing: False -""".format(expect_colors)) + expected = normalize(SHOW_TXT) # ignore "editor: vi" (could be others) out = [l for l in out if not l.startswith('editor: ')] assert out == expected @@ -99,7 +46,7 @@ timing: False def test_base_set(base_app): out = run_cmd(base_app, 'set quiet True') - expected = _normalize(""" + expected = normalize(""" quiet - was: False now: True """) @@ -112,11 +59,11 @@ now: True def test_base_set_not_supported(base_app, capsys): run_cmd(base_app, 'set qqq True') out, err = capsys.readouterr() - expected = _normalize(""" + expected = normalize(""" EXCEPTION of type 'LookupError' occured with message: 'Parameter 'qqq' not supported (type 'show' for list of parameters).' To enable full traceback, run the following command: 'set debug true' """) - assert _normalize(str(err)) == expected + assert normalize(str(err)) == expected def test_base_shell(base_app, monkeypatch): @@ -146,7 +93,7 @@ def test_base_history(base_app): run_cmd(base_app, 'help') run_cmd(base_app, 'shortcuts') out = run_cmd(base_app, 'history') - expected = _normalize(""" + expected = normalize(""" -------------------------[1] help -------------------------[2] @@ -155,14 +102,14 @@ shortcuts assert out == expected out = run_cmd(base_app, 'history he') - expected = _normalize(""" + expected = normalize(""" -------------------------[1] help """) assert out == expected out = run_cmd(base_app, 'history sh') - expected = _normalize(""" + expected = normalize(""" -------------------------[2] shortcuts """) @@ -173,25 +120,16 @@ def test_base_list(base_app): run_cmd(base_app, 'help') run_cmd(base_app, 'shortcuts') out = run_cmd(base_app, 'list') - expected = _normalize(""" + expected = normalize(""" -------------------------[2] shortcuts """) assert out == expected -def test_base_load(base_app): - m = mock.Mock(return_value=StringIO('set quiet True\n')) - base_app.read_file_or_url = m - run_cmd(base_app, 'load myfname') - assert m.called - m.assert_called_once_with('myfname') - # TODO: Figure out how to check stdout or stderr during a do_load() - - def test_base_cmdenvironment(base_app): out = run_cmd(base_app, 'cmdenvironment') - expected = _normalize(""" + expected = normalize(""" Commands are not case-sensitive. Commands may be terminated with: [';'] @@ -200,34 +138,100 @@ def test_base_cmdenvironment(base_app): assert out[2].strip().startswith('Settable parameters: ') # Settable parameters can be listed in any order, so need to validate carefully using unordered sets - settable_params = set(['continuation_prompt', 'default_file_name', 'prompt', 'abbrev', 'quiet', - 'case_insensitive', 'colors', 'echo', 'timing', 'editor', - 'feedback_to_output', 'debug']) + settable_params = {'continuation_prompt', 'default_file_name', 'prompt', 'abbrev', 'quiet', 'case_insensitive', + 'colors', 'echo', 'timing', 'editor', 'feedback_to_output', 'debug'} out_params = set(out[2].split("Settable parameters: ")[1].split()) - assert settable_params == out_params +def test_base_load(base_app, request): + test_dir = os.path.dirname(request.module.__file__) + filename = os.path.join(test_dir, 'script.txt') + + # The way the load command works, we can't directly capture its stdout or stderr + run_cmd(base_app, 'load {}'.format(filename)) + + # But what we can do is check the history to see what commands have been run ... + out = run_cmd(base_app, 'history') + + # TODO: Figure out why when we unit test the command this way the commands from the script aren't shown in history + # NOTE: It works correctly when we run it at the command line + expected = normalize(""" +-------------------------[1] +load {} +""".format(filename)) + assert out == expected + + +def test_base_load_default_file(base_app, capsys): + # The way the load command works, we can't directly capture its stdout or stderr + run_cmd(base_app, 'load') + out, err = capsys.readouterr() + + # The default file 'command.txt' doesn't exist, so we should get an error message + expected = normalize("""ERROR: Problem accessing script from command.txt: +[Errno 2] No such file or directory: 'command.txt.txt'' +To enable full traceback, run the following command: 'set debug true' +""") + assert normalize(str(err)) == expected + + +def test_base_relative_load(base_app, request): + test_dir = os.path.dirname(request.module.__file__) + filename = os.path.join(test_dir, 'script.txt') + + # The way the load command works, we can't directly capture its stdout or stderr + run_cmd(base_app, '_relative_load {}'.format(filename)) + + # But what we can do is check the history to see what commands have been run ... + out = run_cmd(base_app, 'history') + + # TODO: Figure out why when we unit test the command this way the commands from the script aren't shown in history + # NOTE: It works correctly when we run it at the command line + expected = normalize(""" +-------------------------[1] +_relative_load {} +""".format(filename)) + assert out == expected + + def test_base_save(base_app, capsys): # TODO: Use a temporary directory for the file filename = 'deleteme.txt' run_cmd(base_app, 'help') run_cmd(base_app, 'help save') + + # Test the * form of save which saves all commands from history run_cmd(base_app, 'save * {}'.format(filename)) out, err = capsys.readouterr() assert out == 'Saved to {}\n'.format(filename) - - expected = _normalize(""" + expected = normalize(""" help help save save * deleteme.txt """) + with open(filename) as f: + content = normalize(f.read()) + assert content == expected + # Test the N form of save which saves a numbered command from history + run_cmd(base_app, 'save 1 {}'.format(filename)) + out, err = capsys.readouterr() + assert out == 'Saved to {}\n'.format(filename) + expected = normalize('help') with open(filename) as f: - content = _normalize(f.read()) + content = normalize(f.read()) + assert content == expected + # Test the blank form of save which saves the most recent command from history + run_cmd(base_app, 'save {}'.format(filename)) + out, err = capsys.readouterr() + assert out == 'Saved to {}\n'.format(filename) + expected = normalize('save 1 {}'.format(filename)) + with open(filename) as f: + content = normalize(f.read()) assert content == expected # Delete file that was created @@ -238,20 +242,10 @@ def test_output_redirection(base_app): # TODO: Use a temporary directory/file for this file filename = 'out.txt' run_cmd(base_app, 'help > {}'.format(filename)) - expected = _normalize(""" -Documented commands (type help <topic>): -======================================== -_load ed history list py save shortcuts -_relative_load edit l load r set show -cmdenvironment hi li pause run shell - -Undocumented commands: -====================== -EOF eof exit help q quit -""") + expected = normalize(BASE_HELP) with open(filename) as f: - content = _normalize(f.read()) + content = normalize(f.read()) assert content == expected @@ -264,8 +258,8 @@ def test_pipe_to_shell(base_app): out = run_cmd(base_app, 'help help | wc') if sys.platform == "win32": - expected = _normalize("1 5 24") + expected = normalize("1 5 24") else: - expected = _normalize("1 5 20") + expected = normalize("1 5 20") assert out[0].strip() == expected[0].strip() diff --git a/tests/test_transcript.py b/tests/test_transcript.py index 2c717f58..8b38c671 100644 --- a/tests/test_transcript.py +++ b/tests/test_transcript.py @@ -9,7 +9,7 @@ Released under MIT license, see LICENSE file import pytest from cmd2 import Cmd, make_option, options, Cmd2TestCase -from conftest import run_cmd, StdOut, _normalize +from conftest import run_cmd, StdOut, normalize class CmdLineApp(Cmd): @@ -76,13 +76,13 @@ def _get_transcript_blocks(transcript): for line in transcript.splitlines(): if line.startswith('(Cmd) '): if cmd is not None: - yield cmd, _normalize(expected) + yield cmd, normalize(expected) cmd = line[6:] expected = '' else: expected += line + '\n' - yield cmd, _normalize(expected) + yield cmd, normalize(expected) def test_base_with_transcript(_cmdline_app): @@ -163,7 +163,7 @@ class TestMyAppCase(Cmd2TestCase): def test_optparser(_cmdline_app, capsys): run_cmd(_cmdline_app, 'say -h') out, err = capsys.readouterr() - expected = _normalize(""" + expected = normalize(""" Repeats what you tell me to. Usage: speak [options] (text to say) @@ -174,13 +174,13 @@ Options: -r REPEAT, --repeat=REPEAT output [n] times""") # NOTE: For some reason this extra cast to str is required for Python 2.7 but not 3.x - assert _normalize(str(out)) == expected + assert normalize(str(out)) == expected def test_optparser_nosuchoption(_cmdline_app, capsys): run_cmd(_cmdline_app, 'say -a') out, err = capsys.readouterr() - expected = _normalize(""" + expected = normalize(""" no such option: -a Repeats what you tell me to. Usage: speak [options] (text to say) @@ -191,23 +191,24 @@ Options: -s, --shout N00B EMULATION MODE -r REPEAT, --repeat=REPEAT output [n] times""") - assert _normalize(str(out)) == expected + assert normalize(str(out)) == expected def test_comment_stripping(_cmdline_app): out = run_cmd(_cmdline_app, 'speak it was /* not */ delicious! # Yuck!') - expected = _normalize("""it was delicious!""") + expected = normalize("""it was delicious!""") assert out == expected def test_optarser_correct_args_with_quotes_and_midline_options(_cmdline_app): out = run_cmd(_cmdline_app, "speak 'This is a' -s test of the emergency broadcast system!") - expected = _normalize("""THIS IS A TEST OF THE EMERGENCY BROADCAST SYSTEM!""") + expected = normalize("""THIS IS A TEST OF THE EMERGENCY BROADCAST SYSTEM!""") assert out == expected + def test_optarser_options_with_spaces_in_quotes(_demo_app): out = run_cmd(_demo_app, "hello foo -n 'Bugs Bunny' bar baz") - expected = _normalize("""Hello Bugs Bunny""") + expected = normalize("""Hello Bugs Bunny""") assert out == expected |