Skip to content

Commit b0c3fa4

Browse files
committed
Fixed bugs, removed dead code, and added unit tests
Fixed a bug where CmdResult named tuple Truthiness wasn't working in Python 2 Removed unused cmd2.History.search() method. Added unit tests for: - History class - cast() function - CmdResult namedtuple
1 parent 482e9ce commit b0c3fa4

4 files changed

Lines changed: 104 additions & 24 deletions

File tree

cmd2.py

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1967,24 +1967,6 @@ def _to_index(self, raw):
19671967
result = None
19681968
return result
19691969

1970-
def search(self, target):
1971-
""" Search the history for a particular term.
1972-
1973-
:param target: str - term to search for
1974-
:return: List[str] - list of matches
1975-
"""
1976-
# If there is no history yet, don't try to search through a non-existent list, just return an empty list
1977-
if len(self) < 1:
1978-
return []
1979-
1980-
target = target.strip()
1981-
if target[0] == target[-1] == '/' and len(target) > 1:
1982-
target = target[1:-1]
1983-
else:
1984-
target = re.escape(target)
1985-
pattern = re.compile(target, re.IGNORECASE)
1986-
return [s for s in self if pattern.search(s)]
1987-
19881970
spanpattern = re.compile(r'^\s*(?P<start>-?\d+)?\s*(?P<separator>:|(\.{2,}))?\s*(?P<end>-?\d+)?\s*$')
19891971

19901972
def span(self, raw):
@@ -2287,6 +2269,10 @@ def __bool__(self):
22872269
"""If err is an empty string, treat the result as a success; otherwise treat it as a failure."""
22882270
return not self.err
22892271

2272+
def __nonzero__(self):
2273+
"""Python 2 uses this method for determining Truthiness"""
2274+
return self.__bool__()
2275+
22902276

22912277
if __name__ == '__main__':
22922278
# If run as the main application, simply start a bare-bones cmd2 application with only built-in functionality.

tests/test_cmd2.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,3 +1154,44 @@ def test_clipboard_failure(capsys):
11541154
out, err = capsys.readouterr()
11551155
assert out == ''
11561156
assert 'Cannot redirect to paste buffer; install ``xclip`` and re-run to enable' in err
1157+
1158+
1159+
def test_run_command_with_empty_arg(base_app):
1160+
command = 'help'
1161+
run_cmd(base_app, command)
1162+
out = run_cmd(base_app, 'run')
1163+
expected = normalize('{}\n\n'.format(command) + BASE_HELP)
1164+
assert out == expected
1165+
1166+
def test_run_command_with_empty_history(base_app):
1167+
out = run_cmd(base_app, 'run')
1168+
assert out == []
1169+
1170+
1171+
class CmdResultApp(cmd2.Cmd):
1172+
def __init__(self, *args, **kwargs):
1173+
# Need to use this older form of invoking super class constructor to support Python 2.x and Python 3.x
1174+
cmd2.Cmd.__init__(self, *args, **kwargs)
1175+
1176+
def do_affirmative(self, arg):
1177+
self._last_result = cmd2.CmdResult(arg)
1178+
1179+
def do_negative(self, arg):
1180+
self._last_result = cmd2.CmdResult('', arg)
1181+
1182+
@pytest.fixture
1183+
def cmdresult_app():
1184+
app = CmdResultApp()
1185+
app.stdout = StdOut()
1186+
return app
1187+
1188+
def test_cmdresult(cmdresult_app):
1189+
arg = 'foo'
1190+
run_cmd(cmdresult_app, 'affirmative {}'.format(arg))
1191+
assert cmdresult_app._last_result
1192+
assert cmdresult_app._last_result == cmd2.CmdResult(arg)
1193+
1194+
arg = 'bar'
1195+
run_cmd(cmdresult_app, 'negative {}'.format(arg))
1196+
assert not cmdresult_app._last_result
1197+
assert cmdresult_app._last_result == cmd2.CmdResult('', arg)

tests/test_parsing.py

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,69 @@ def test_remaining_args():
4949

5050
def test_history_span(hist):
5151
h = hist
52+
assert h == ['first', 'second', 'third', 'fourth']
5253
assert h.span('-2..') == ['third', 'fourth']
53-
assert h.span('2..3') == ['second', 'third']
54+
assert h.span('2..3') == ['second', 'third'] # Inclusive of end
5455
assert h.span('3') == ['third']
55-
assert h.span(':') == ['first', 'second', 'third', 'fourth']
56+
assert h.span(':') == h
5657
assert h.span('2..') == ['second', 'third', 'fourth']
5758
assert h.span('-1') == ['fourth']
5859
assert h.span('-2..-3') == ['third', 'second']
60+
assert h.span('*') == h
5961

60-
def test_history_search(hist):
61-
assert hist.search('o') == ['second', 'fourth']
62-
assert hist.search('/IR/') == ['first', 'third']
62+
def test_history_get(hist):
63+
h = hist
64+
assert h == ['first', 'second', 'third', 'fourth']
65+
assert h.get('') == h
66+
assert h.get('-2') == h[:-2]
67+
assert h.get('5') == []
68+
assert h.get('2-3') == ['second'] # Exclusive of end
69+
assert h.get('ir') == ['first', 'third'] # Normal string search for all elements containing "ir"
70+
assert h.get('/i.*d/') == ['third'] # Regex string search "i", then anything, then "d"
71+
72+
73+
def test_cast():
74+
cast = cmd2.cast
75+
76+
# Boolean
77+
assert cast(True, True) == True
78+
assert cast(True, False) == False
79+
assert cast(True, 0) == False
80+
assert cast(True, 1) == True
81+
assert cast(True, 'on') == True
82+
assert cast(True, 'off') == False
83+
assert cast(True, 'ON') == True
84+
assert cast(True, 'OFF') == False
85+
assert cast(True, 'y') == True
86+
assert cast(True, 'n') == False
87+
assert cast(True, 't') == True
88+
assert cast(True, 'f') == False
89+
90+
# Non-boolean same type
91+
assert cast(1, 5) == 5
92+
assert cast(3.4, 2.7) == 2.7
93+
assert cast('foo', 'bar') == 'bar'
94+
assert cast([1,2], [3,4]) == [3,4]
95+
96+
97+
def test_cast_problems(capsys):
98+
cast = cmd2.cast
99+
100+
expected = 'Problem setting parameter (now {}) to {}; incorrect type?\n'
101+
102+
# Boolean current, with new value not convertible to bool
103+
current = True
104+
new = [True, True]
105+
assert cast(current, new) == current
106+
out, err = capsys.readouterr()
107+
assert out == expected.format(current, new)
108+
109+
# Non-boolean current, with new value not convertible to current type
110+
current = 1
111+
new = 'octopus'
112+
assert cast(current, new) == current
113+
out, err = capsys.readouterr()
114+
assert out == expected.format(current, new)
63115

64116

65117
def test_parse_empty_string(parser):

tests/test_transcript.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def __init__(self, *args, **kwargs):
3030

3131
# Need to use this older form of invoking super class constructor to support Python 2.x and Python 3.x
3232
Cmd.__init__(self, *args, **kwargs)
33+
self.intro = 'This is an intro banner ...'
3334

3435
# Configure how arguments are parsed for @options commands
3536
set_posix_shlex(False)
@@ -224,7 +225,7 @@ def test_optarser_options_with_spaces_in_quotes(_demo_app):
224225

225226
def test_commands_at_invocation():
226227
testargs = ["prog", "say hello", "say Gracie", "quit"]
227-
expected = "hello\nGracie\n"
228+
expected = "This is an intro banner ...\nhello\nGracie\n"
228229
with mock.patch.object(sys, 'argv', testargs):
229230
app = CmdLineApp()
230231
app.stdout = StdOut()

0 commit comments

Comments
 (0)