Skip to content

Commit f273901

Browse files
authored
Merge pull request #89 from python-cmd2/transcript_fixes
Fixed transcript testing issues
2 parents 90330f2 + f3fa226 commit f273901

File tree

5 files changed

+32
-56
lines changed

5 files changed

+32
-56
lines changed

cmd2.py

Lines changed: 20 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ class Cmd(cmd.Cmd):
618618
for editor in ['vim', 'vi', 'emacs', 'nano', 'pico', 'gedit', 'kate', 'subl', 'geany', 'atom']:
619619
if _which(editor):
620620
break
621-
feedback_to_output = False # Do include nonessentials in >, | output
621+
feedback_to_output = True # Do include nonessentials in >, | output
622622
locals_in_py = True
623623
quiet = False # Do not suppress nonessential output
624624
timing = False # Prints elapsed time for each command
@@ -1691,7 +1691,7 @@ def run_transcript_tests(self, callargs):
16911691
:param callargs: List[str] - list of transcript test file names
16921692
"""
16931693
class TestMyAppCase(Cmd2TestCase):
1694-
CmdApp = self.__class__
1694+
cmdapp = self
16951695

16961696
self.__class__.testfiles = callargs
16971697
sys.argv = [sys.argv[0]] # the --test argument upsets unittest.main()
@@ -1731,12 +1731,12 @@ def cmdloop(self, intro=None):
17311731
if callopts.test:
17321732
self._transcript_files = callargs
17331733

1734+
# Always run the preloop first
1735+
self.preloop()
1736+
17341737
if self._transcript_files is not None:
17351738
self.run_transcript_tests(self._transcript_files)
17361739
else:
1737-
# Always run the preloop first
1738-
self.preloop()
1739-
17401740
# If an intro was supplied in the method call, allow it to override the default
17411741
if intro is not None:
17421742
self.intro = intro
@@ -1754,8 +1754,8 @@ def cmdloop(self, intro=None):
17541754
if not stop:
17551755
self._cmdloop()
17561756

1757-
# Run the postloop() no matter what
1758-
self.postloop()
1757+
# Run the postloop() no matter what
1758+
self.postloop()
17591759

17601760

17611761
class HistoryItem(str):
@@ -1960,25 +1960,11 @@ def restore(self):
19601960
setattr(self.obj, attrib, getattr(self, attrib))
19611961

19621962

1963-
class Borg(object):
1964-
"""All instances of any Borg subclass will share state.
1965-
from Python Cookbook, 2nd Ed., recipe 6.16"""
1966-
_shared_state = {}
1967-
1968-
def __new__(cls, *a, **k):
1969-
obj = object.__new__(cls)
1970-
obj.__dict__ = cls._shared_state
1971-
return obj
1972-
1973-
1974-
class OutputTrap(Borg):
1975-
"""Instantiate an OutputTrap to divert/capture ALL stdout output. For use in unit testing.
1976-
Call `tearDown()` to return to normal output."""
1963+
class OutputTrap(object):
1964+
"""Instantiate an OutputTrap to divert/capture ALL stdout output. For use in transcript testing."""
19771965

19781966
def __init__(self):
19791967
self.contents = ''
1980-
self.old_stdout = sys.stdout
1981-
sys.stdout = self
19821968

19831969
def write(self, txt):
19841970
"""Add text to the internal contents.
@@ -1996,17 +1982,12 @@ def read(self):
19961982
self.contents = ''
19971983
return result
19981984

1999-
def tear_down(self):
2000-
"""Restores normal output."""
2001-
sys.stdout = self.old_stdout
2002-
self.contents = ''
2003-
20041985

20051986
class Cmd2TestCase(unittest.TestCase):
20061987
"""Subclass this, setting CmdApp, to make a unittest.TestCase class
20071988
that will execute the commands in a transcript file and expect the results shown.
20081989
See example.py"""
2009-
CmdApp = None
1990+
cmdapp = None
20101991
regexPattern = pyparsing.QuotedString(quoteChar=r'/', escChar='\\', multiline=True, unquoteResults=True)
20111992
regexPattern.ignore(pyparsing.cStyleComment)
20121993
notRegexPattern = pyparsing.Word(pyparsing.printables)
@@ -2016,7 +1997,7 @@ class Cmd2TestCase(unittest.TestCase):
20161997

20171998
def fetchTranscripts(self):
20181999
self.transcripts = {}
2019-
for fileset in self.CmdApp.testfiles:
2000+
for fileset in self.cmdapp.testfiles:
20202001
for fname in glob.glob(fileset):
20212002
tfile = open(fname)
20222003
self.transcripts[fname] = iter(tfile.readlines())
@@ -2025,17 +2006,15 @@ def fetchTranscripts(self):
20252006
raise Exception("No test files found - nothing to test.")
20262007

20272008
def setUp(self):
2028-
if self.CmdApp:
2029-
self.outputTrap = OutputTrap()
2030-
self.cmdapp = self.CmdApp()
2009+
if self.cmdapp:
20312010
self.fetchTranscripts()
20322011

2033-
# Make sure any required initialization gets done and flush the output buffer
2034-
self.cmdapp.preloop()
2035-
self.outputTrap.read()
2012+
# Trap stdout
2013+
self._orig_stdout = self.cmdapp.stdout
2014+
self.cmdapp.stdout = OutputTrap()
20362015

20372016
def runTest(self): # was testall
2038-
if self.CmdApp:
2017+
if self.cmdapp:
20392018
its = sorted(self.transcripts.items())
20402019
for (fname, transcript) in its:
20412020
self._test_transcript(fname, transcript)
@@ -2071,7 +2050,7 @@ def _test_transcript(self, fname, transcript):
20712050
# Send the command into the application and capture the resulting output
20722051
# TODO: Should we get the return value and act if stop == True?
20732052
self.cmdapp.onecmd_plus_hooks(command)
2074-
result = self.outputTrap.read()
2053+
result = self.cmdapp.stdout.read()
20752054
# Read the expected result from transcript
20762055
if line.startswith(self.cmdapp.prompt):
20772056
message = '\nFile %s, line %d\nCommand was:\n%r\nExpected: (nothing)\nGot:\n%r\n' % \
@@ -2098,11 +2077,9 @@ def _test_transcript(self, fname, transcript):
20982077
self.assertTrue(re.match(expected, result, re.MULTILINE | re.DOTALL), message)
20992078

21002079
def tearDown(self):
2101-
if self.CmdApp:
2102-
# Make sure any required cleanup gets done
2103-
self.cmdapp.postloop()
2104-
2105-
self.outputTrap.tear_down()
2080+
if self.cmdapp:
2081+
# Restore stdout
2082+
self.cmdapp.stdout = self._orig_stdout
21062083

21072084

21082085
def namedtuple_with_two_defaults(typename, field_names, default_values=('', '')):

tests/conftest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
default_file_name: command.txt
5656
echo: False
5757
editor: vim
58-
feedback_to_output: False
58+
feedback_to_output: True
5959
locals_in_py: True
6060
prompt: (Cmd)
6161
quiet: False
@@ -75,7 +75,7 @@
7575
default_file_name: command.txt # for ``save``, ``load``, etc.
7676
echo: False # Echo command issued into output
7777
editor: vim # Program used by ``edit``
78-
feedback_to_output: False # include nonessentials in `|`, `>` results
78+
feedback_to_output: True # include nonessentials in `|`, `>` results
7979
locals_in_py: True # Allow access to your application in py via self
8080
prompt: (Cmd) # The prompt issued to solicit input
8181
quiet: False # Don't print nonessential feedback

tests/test_cmd2.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -266,16 +266,15 @@ def test_base_relative_load(base_app, request):
266266
assert out == expected
267267

268268

269-
def test_base_save(base_app, capsys):
269+
def test_base_save(base_app):
270270
# TODO: Use a temporary directory for the file
271271
filename = 'deleteme.txt'
272272
run_cmd(base_app, 'help')
273273
run_cmd(base_app, 'help save')
274274

275275
# Test the * form of save which saves all commands from history
276-
run_cmd(base_app, 'save * {}'.format(filename))
277-
out, err = capsys.readouterr()
278-
assert out == 'Saved to {}\n'.format(filename)
276+
out = run_cmd(base_app, 'save * {}'.format(filename))
277+
assert out == normalize('Saved to {}\n'.format(filename))
279278
expected = normalize("""
280279
help
281280
@@ -288,18 +287,16 @@ def test_base_save(base_app, capsys):
288287
assert content == expected
289288

290289
# Test the N form of save which saves a numbered command from history
291-
run_cmd(base_app, 'save 1 {}'.format(filename))
292-
out, err = capsys.readouterr()
293-
assert out == 'Saved to {}\n'.format(filename)
290+
out = run_cmd(base_app, 'save 1 {}'.format(filename))
291+
assert out == normalize('Saved to {}\n'.format(filename))
294292
expected = normalize('help')
295293
with open(filename) as f:
296294
content = normalize(f.read())
297295
assert content == expected
298296

299297
# Test the blank form of save which saves the most recent command from history
300-
run_cmd(base_app, 'save {}'.format(filename))
301-
out, err = capsys.readouterr()
302-
assert out == 'Saved to {}\n'.format(filename)
298+
out = run_cmd(base_app, 'save {}'.format(filename))
299+
assert out == normalize('Saved to {}\n'.format(filename))
303300
expected = normalize('save 1 {}'.format(filename))
304301
with open(filename) as f:
305302
content = normalize(f.read())
@@ -397,6 +394,7 @@ def test_send_to_paste_buffer(base_app):
397394

398395

399396
def test_base_timing(base_app, capsys):
397+
base_app.feedback_to_output = False
400398
out = run_cmd(base_app, 'set timing True')
401399
expected = normalize("""timing - was: False
402400
now: True

tests/test_transcript.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ def test_base_with_transcript(_cmdline_app):
149149
-------------------------[6]
150150
say -ps --repeat=5 goodnight, Gracie
151151
(Cmd) run 4
152+
say -ps --repeat=5 goodnight, Gracie
152153
OODNIGHT, GRACIEGAY
153154
OODNIGHT, GRACIEGAY
154155
OODNIGHT, GRACIEGAY

tests/transcript_regex.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ debug: False
1010
default_file_name: command.txt
1111
echo: False
1212
editor: /([^\s]+)/
13-
feedback_to_output: False
13+
feedback_to_output: True
1414
locals_in_py: True
1515
maxrepeats: 3
1616
prompt: (Cmd)

0 commit comments

Comments
 (0)