1
# Copyright (C) 2009, 2010, 2011 Canonical Ltd
1
# Copyright (C) 2009, 2010 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
59
55
Input lines start with '<'.
60
56
Output lines start with nothing.
61
57
Error lines start with '2>'.
63
:return: A sequence of ([args], input, output, errors), where the args are
64
split in to words, and the input, output, and errors are just strings,
65
typically containing newlines.
83
75
input, output, error = None, None, None
84
text = textwrap.dedent(text)
85
lines = text.split('\n')
86
# to make use of triple-quoted strings easier, we ignore a blank line
87
# right at the start and right at the end; the rest are meaningful
88
if lines and lines[0] == '':
90
if lines and lines[-1] == '':
76
for line in text.split('\n'):
94
78
# Keep a copy for error reporting
96
comment = line.find('#')
80
comment = line.find('#')
99
# NB: this syntax means comments are allowed inside output, which
100
# may be confusing...
101
83
line = line[0:comment]
102
84
line = line.rstrip()
105
88
if line.startswith('$'):
106
89
# Time to output the current command
107
90
add_command(cmd_cur, input, output, error)
143
126
:param args: The command line arguments
145
:return: A tuple containing:
128
:return: A tuple containing:
146
129
- The file name redirected from or None
147
130
- The file name redirected to or None
148
131
- The mode to open the output file or None
167
150
in_name = redirected_file_name('<', arg[1:], args)
168
151
elif arg.startswith('>>'):
169
152
out_name = redirected_file_name('>>', arg[2:], args)
171
154
elif arg.startswith('>',):
172
155
out_name = redirected_file_name('>', arg[1:], args)
175
158
remaining.append(arg)
176
159
return in_name, out_name, out_mode, remaining
179
162
class ScriptRunner(object):
180
163
"""Run a shell-like script from a test.
184
from breezy.tests import script
167
from bzrlib.tests import script
188
171
def test_bug_nnnnn(self):
189
172
sr = script.ScriptRunner()
190
173
sr.run_script(self, '''
198
181
self.output_checker = doctest.OutputChecker()
199
182
self.check_options = doctest.ELLIPSIS
201
def run_script(self, test_case, text, null_output_matches_anything=False):
184
def run_script(self, test_case, text):
202
185
"""Run a shell-like script as a test.
204
187
:param test_case: A TestCase instance that should provide the fail(),
206
189
attribute used as a jail root.
208
191
:param text: A shell-like script (see _script_to_commands for syntax).
210
:param null_output_matches_anything: For commands with no specified
211
output, ignore any output that does happen, including output on
214
self.null_output_matches_anything = null_output_matches_anything
215
193
for cmd, input, output, error in _script_to_commands(text):
216
194
self.run_command(test_case, cmd, input, output, error)
220
198
method = getattr(self, mname, None)
221
199
if method is None:
222
200
raise SyntaxError('Command not found "%s"' % (cmd[0],),
223
(None, 1, 1, ' '.join(cmd)))
201
None, 1, ' '.join(cmd))
224
202
if input is None:
229
207
retcode, actual_output, actual_error = method(test_case,
233
self._check_output(output, actual_output, test_case)
234
except AssertionError as e:
235
raise AssertionError(str(e) + " in stdout of command %s" % cmd)
237
self._check_output(error, actual_error, test_case)
238
except AssertionError as e:
239
raise AssertionError(str(e)
240
+ " in stderr of running command %s" % cmd)
210
self._check_output(output, actual_output, test_case)
211
self._check_output(error, actual_error, test_case)
241
212
if retcode and not error and actual_error:
242
213
test_case.fail('In \n\t%s\nUnexpected error: %s'
243
214
% (' '.join(cmd), actual_error))
244
215
return retcode, actual_output, actual_error
246
217
def _check_output(self, expected, actual, test_case):
250
elif expected == '...\n':
253
test_case.fail('expected output: %r, but found nothing'
256
null_output_matches_anything = getattr(
257
self, 'null_output_matches_anything', False)
258
if null_output_matches_anything and expected is None:
219
# Specifying None means: any output is accepted
261
expected = expected or ''
222
test_case.fail('We expected output: %r, but found None'
262
224
matching = self.output_checker.check_output(
263
225
expected, actual, self.check_options)
268
230
# 'expected' parameter. So we just fallback to our good old
269
231
# assertEqualDiff since we know there *are* differences and the
270
232
# output should be decently readable.
272
# As a special case, we allow output that's missing a final
273
# newline to match an expected string that does have one, so that
274
# we can match a prompt printed on one line, then input given on
276
if expected == actual + '\n':
279
test_case.assertEqualDiff(expected, actual)
233
test_case.assertEqualDiff(expected, actual)
281
235
def _pre_process_args(self, args):
284
238
# Strip the simple and double quotes since we don't care about
285
239
# them. We leave the backquotes in place though since they have a
286
240
# different semantic.
287
if arg[0] in ('"', "'") and arg[0] == arg[-1]:
241
if arg[0] in ('"', "'") and arg[0] == arg[-1]:
290
244
if glob.has_magic(arg):
301
255
def _read_input(self, input, in_name):
302
256
if in_name is not None:
303
infile = open(in_name, 'r')
257
infile = open(in_name, 'rb')
305
259
# Command redirection takes precedence over provided input
306
260
input = infile.read()
321
def do_brz(self, test_case, input, args):
322
encoding = osutils.get_user_encoding()
323
if sys.version_info[0] == 2:
324
stdout = ui_testing.BytesIOWithEncoding()
325
stderr = ui_testing.BytesIOWithEncoding()
326
stdout.encoding = stderr.encoding = encoding
328
# FIXME: don't call into logging here
329
handler = trace.EncodedStreamHandler(
330
stderr, errors="replace")
332
stdout = ui_testing.StringIOWithEncoding()
333
stderr = ui_testing.StringIOWithEncoding()
334
stdout.encoding = stderr.encoding = encoding
335
handler = logging.StreamHandler(stderr)
336
handler.setLevel(logging.INFO)
338
logger = logging.getLogger('')
339
logger.addHandler(handler)
341
retcode = test_case._run_bzr_core(
342
args, encoding=encoding, stdin=input, stdout=stdout,
343
stderr=stderr, working_dir=None)
345
logger.removeHandler(handler)
347
return retcode, stdout.getvalue(), stderr.getvalue()
275
def do_bzr(self, test_case, input, args):
276
retcode, out, err = test_case._run_bzr_core(
277
args, retcode=None, encoding=None, stdin=input, working_dir=None)
278
return retcode, out, err
349
280
def do_cat(self, test_case, input, args):
350
281
(in_name, out_name, out_mode, args) = _scan_redirection_options(args)
360
291
for in_name in input_names:
362
293
inputs.append(self._read_input(None, in_name))
364
295
# Some filenames are illegal on Windows and generate EINVAL
365
296
# rather than just saying the filename doesn't exist
366
297
if e.errno in (errno.ENOENT, errno.EINVAL):
372
303
# Handle output redirections
374
305
output = self._write_output(output, out_name, out_mode)
376
307
# If out_name cannot be created, we may get 'ENOENT', however if
377
308
# out_name is something like '', we can get EINVAL
378
309
if e.errno in (errno.ENOENT, errno.EINVAL):
393
324
# Handle output redirections
395
326
output = self._write_output(output, out_name, out_mode)
397
328
if e.errno in (errno.ENOENT, errno.EINVAL):
398
329
return 1, None, '%s: No such file or directory\n' % (out_name,)
433
364
def error(msg, path):
434
return "rm: cannot remove '%s': %s\n" % (path, msg)
365
return "rm: cannot remove '%s': %s\n" % (path, msg)
436
367
force, recursive = False, False
450
381
# FIXME: Should we put that in osutils ?
454
385
# Various OSes raises different exceptions (linux: EISDIR,
455
386
# win32: EACCES, OSX: EPERM) when invoked on a directory
456
387
if e.errno in (errno.EISDIR, errno.EPERM, errno.EACCES):
474
405
def do_mv(self, test_case, input, args):
477
407
def error(msg, src, dst):
478
408
return "mv: cannot move %s to %s: %s\n" % (src, dst, msg)
485
415
if os.path.isdir(dst):
486
416
real_dst = os.path.join(dst, os.path.basename(src))
487
417
os.rename(src, real_dst)
489
419
if e.errno == errno.ENOENT:
490
420
err = error('No such file or directory', src, dst)
509
440
super(TestCaseWithMemoryTransportAndScript, self).setUp()
510
441
self.script_runner = ScriptRunner()
511
# FIXME: See shelf_ui.Shelver._char_based. This allow using shelve in
512
# scripts while providing a line-based input (better solution in
513
# progress). -- vila 2011-09-28
514
self.overrideEnv('INSIDE_EMACS', '1')
516
def run_script(self, script, null_output_matches_anything=False):
517
return self.script_runner.run_script(self, script,
518
null_output_matches_anything=null_output_matches_anything)
443
def run_script(self, script):
444
return self.script_runner.run_script(self, script)
520
446
def run_command(self, cmd, input, output, error):
521
447
return self.script_runner.run_command(self, cmd, input, output, error)
543
469
super(TestCaseWithTransportAndScript, self).setUp()
544
470
self.script_runner = ScriptRunner()
545
# FIXME: See shelf_ui.Shelver._char_based. This allow using shelve in
546
# scripts while providing a line-based input (better solution in
547
# progress). -- vila 2011-09-28
548
self.overrideEnv('INSIDE_EMACS', '1')
550
def run_script(self, script, null_output_matches_anything=False):
551
return self.script_runner.run_script(self, script,
552
null_output_matches_anything=null_output_matches_anything)
472
def run_script(self, script):
473
return self.script_runner.run_script(self, script)
554
475
def run_command(self, cmd, input, output, error):
555
476
return self.script_runner.run_command(self, cmd, input, output, error)
558
def run_script(test_case, script_string, null_output_matches_anything=False):
559
"""Run the given script within a testcase"""
560
return ScriptRunner().run_script(test_case, script_string,
561
null_output_matches_anything=null_output_matches_anything)