summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcmd2.py9
-rw-r--r--tests/conftest.py68
-rw-r--r--tests/script.txt2
-rw-r--r--tests/test_cmd2.py186
-rw-r--r--tests/test_transcript.py21
5 files changed, 173 insertions, 113 deletions
diff --git a/cmd2.py b/cmd2.py
index 85f9b752..537c28b1 100755
--- a/cmd2.py
+++ b/cmd2.py
@@ -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