1
# Copyright (C) 2009, 2010, 2011 Canonical Ltd
1
# Copyright (C) 2009 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
55
55
Input lines start with '<'.
56
56
Output lines start with nothing.
57
57
Error lines start with '2>'.
59
:return: A sequence of ([args], input, output, errors), where the args are
60
split in to words, and the input, output, and errors are just strings,
61
typically containing newlines.
79
75
input, output, error = None, None, None
80
text = textwrap.dedent(text)
81
lines = text.split('\n')
82
# to make use of triple-quoted strings easier, we ignore a blank line
83
# right at the start and right at the end; the rest are meaningful
84
if lines and lines[0] == '':
86
if lines and lines[-1] == '':
76
for line in text.split('\n'):
90
78
# Keep a copy for error reporting
92
80
comment = line.find('#')
95
# NB: this syntax means comments are allowed inside output, which
97
83
line = line[0:comment]
98
84
line = line.rstrip()
101
88
if line.startswith('$'):
102
89
# Time to output the current command
103
90
add_command(cmd_cur, input, output, error)
121
108
error.append(line[2:] + '\n')
123
# can happen if the first line is not recognized as a command, eg
124
# if the prompt has leading whitespace
125
110
if output is None:
126
111
if cmd_cur is None:
127
raise SyntaxError('No command for line %r' % (line,),
112
raise SyntaxError('No command for that output',
128
113
(file_name, lineno, 1, orig))
130
115
output.append(line + '\n')
194
179
self.output_checker = doctest.OutputChecker()
195
180
self.check_options = doctest.ELLIPSIS
197
def run_script(self, test_case, text, null_output_matches_anything=False):
182
def run_script(self, test_case, text):
198
183
"""Run a shell-like script as a test.
200
185
:param test_case: A TestCase instance that should provide the fail(),
202
187
attribute used as a jail root.
204
189
:param text: A shell-like script (see _script_to_commands for syntax).
206
:param null_output_matches_anything: For commands with no specified
207
output, ignore any output that does happen, including output on
210
self.null_output_matches_anything = null_output_matches_anything
211
191
for cmd, input, output, error in _script_to_commands(text):
212
192
self.run_command(test_case, cmd, input, output, error)
216
196
method = getattr(self, mname, None)
217
197
if method is None:
218
198
raise SyntaxError('Command not found "%s"' % (cmd[0],),
219
(None, 1, 1, ' '.join(cmd)))
199
None, 1, ' '.join(cmd))
220
200
if input is None:
225
205
retcode, actual_output, actual_error = method(test_case,
229
self._check_output(output, actual_output, test_case)
230
except AssertionError, e:
231
raise AssertionError(str(e) + " in stdout of command %s" % cmd)
233
self._check_output(error, actual_error, test_case)
234
except AssertionError, e:
235
raise AssertionError(str(e) +
236
" in stderr of running command %s" % cmd)
208
self._check_output(output, actual_output, test_case)
209
self._check_output(error, actual_error, test_case)
237
210
if retcode and not error and actual_error:
238
211
test_case.fail('In \n\t%s\nUnexpected error: %s'
239
212
% (' '.join(cmd), actual_error))
240
213
return retcode, actual_output, actual_error
242
215
def _check_output(self, expected, actual, test_case):
246
elif expected == '...\n':
249
test_case.fail('expected output: %r, but found nothing'
252
null_output_matches_anything = getattr(
253
self, 'null_output_matches_anything', False)
254
if null_output_matches_anything and expected is None:
217
# Specifying None means: any output is accepted
257
expected = expected or ''
220
test_case.fail('We expected output: %r, but found None'
258
222
matching = self.output_checker.check_output(
259
223
expected, actual, self.check_options)
264
228
# 'expected' parameter. So we just fallback to our good old
265
229
# assertEqualDiff since we know there *are* differences and the
266
230
# output should be decently readable.
268
# As a special case, we allow output that's missing a final
269
# newline to match an expected string that does have one, so that
270
# we can match a prompt printed on one line, then input given on
272
if expected == actual + '\n':
275
test_case.assertEqualDiff(expected, actual)
231
test_case.assertEqualDiff(expected, actual)
277
233
def _pre_process_args(self, args):
445
401
return retcode, None, err
447
def do_mv(self, test_case, input, args):
449
def error(msg, src, dst):
450
return "mv: cannot move %s to %s: %s\n" % (src, dst, msg)
452
if not args or len(args) != 2:
453
raise SyntaxError("Usage: mv path1 path2")
457
if os.path.isdir(dst):
458
real_dst = os.path.join(dst, os.path.basename(src))
459
os.rename(src, real_dst)
461
if e.errno == errno.ENOENT:
462
err = error('No such file or directory', src, dst)
469
return retcode, None, err
473
404
class TestCaseWithMemoryTransportAndScript(tests.TestCaseWithMemoryTransport):
474
405
"""Helper class to experiment shell-like test and memory fs.
482
413
super(TestCaseWithMemoryTransportAndScript, self).setUp()
483
414
self.script_runner = ScriptRunner()
485
def run_script(self, script, null_output_matches_anything=False):
486
return self.script_runner.run_script(self, script,
487
null_output_matches_anything=null_output_matches_anything)
416
def run_script(self, script):
417
return self.script_runner.run_script(self, script)
489
419
def run_command(self, cmd, input, output, error):
490
420
return self.script_runner.run_command(self, cmd, input, output, error)
512
442
super(TestCaseWithTransportAndScript, self).setUp()
513
443
self.script_runner = ScriptRunner()
515
def run_script(self, script, null_output_matches_anything=False):
516
return self.script_runner.run_script(self, script,
517
null_output_matches_anything=null_output_matches_anything)
445
def run_script(self, script):
446
return self.script_runner.run_script(self, script)
519
448
def run_command(self, cmd, input, output, error):
520
449
return self.script_runner.run_command(self, cmd, input, output, error)
523
def run_script(test_case, script_string, null_output_matches_anything=False):
524
"""Run the given script within a testcase"""
525
return ScriptRunner().run_script(test_case, script_string,
526
null_output_matches_anything=null_output_matches_anything)