~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/script.py

  • Committer: Jelmer Vernooij
  • Date: 2012-01-24 13:14:06 UTC
  • mto: (6445.4.5 nested-trees-spec)
  • mto: This revision was merged to the branch mainline in revision 6518.
  • Revision ID: jelmer@samba.org-20120124131406-wedftkorbpv37bm0
Import nested tree doc from devnotes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2009, 2010 Canonical Ltd
 
1
# Copyright (C) 2009, 2010, 2011 Canonical Ltd
2
2
#
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
25
25
import os
26
26
import shlex
27
27
import textwrap
28
 
from cStringIO import StringIO
29
28
 
30
29
from bzrlib import (
31
30
    osutils,
56
55
    Input lines start with '<'.
57
56
    Output lines start with nothing.
58
57
    Error lines start with '2>'.
 
58
 
 
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.
59
62
    """
60
63
 
61
64
    commands = []
75
78
    lineno = 0
76
79
    input, output, error = None, None, None
77
80
    text = textwrap.dedent(text)
78
 
    for line in text.split('\n'):
 
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] == '':
 
85
        del lines[0]
 
86
    if lines and lines[-1] == '':
 
87
        del lines[-1]
 
88
    for line in lines:
79
89
        lineno += 1
80
90
        # Keep a copy for error reporting
81
91
        orig = line
82
92
        comment =  line.find('#')
83
93
        if comment >= 0:
84
94
            # Delete comments
 
95
            # NB: this syntax means comments are allowed inside output, which
 
96
            # may be confusing...
85
97
            line = line[0:comment]
86
98
            line = line.rstrip()
87
 
        if line == '':
88
 
            # Ignore empty lines
89
 
            continue
 
99
            if line == '':
 
100
                continue
90
101
        if line.startswith('$'):
91
102
            # Time to output the current command
92
103
            add_command(cmd_cur, input, output, error)
183
194
        self.output_checker = doctest.OutputChecker()
184
195
        self.check_options = doctest.ELLIPSIS
185
196
 
186
 
    def run_script(self, test_case, text):
 
197
    def run_script(self, test_case, text, null_output_matches_anything=False):
187
198
        """Run a shell-like script as a test.
188
199
 
189
200
        :param test_case: A TestCase instance that should provide the fail(),
191
202
            attribute used as a jail root.
192
203
 
193
204
        :param text: A shell-like script (see _script_to_commands for syntax).
 
205
 
 
206
        :param null_output_matches_anything: For commands with no specified
 
207
            output, ignore any output that does happen, including output on
 
208
            standard error.
194
209
        """
 
210
        self.null_output_matches_anything = null_output_matches_anything
195
211
        for cmd, input, output, error in _script_to_commands(text):
196
212
            self.run_command(test_case, cmd, input, output, error)
197
213
 
200
216
        method = getattr(self, mname, None)
201
217
        if method is None:
202
218
            raise SyntaxError('Command not found "%s"' % (cmd[0],),
203
 
                              None, 1, ' '.join(cmd))
 
219
                              (None, 1, 1, ' '.join(cmd)))
204
220
        if input is None:
205
221
            str_input = ''
206
222
        else:
209
225
        retcode, actual_output, actual_error = method(test_case,
210
226
                                                      str_input, args)
211
227
 
212
 
        self._check_output(output, actual_output, test_case)
213
 
        self._check_output(error, actual_error, test_case)
 
228
        try:
 
229
            self._check_output(output, actual_output, test_case)
 
230
        except AssertionError, e:
 
231
            raise AssertionError(str(e) + " in stdout of command %s" % cmd)
 
232
        try:
 
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)
214
237
        if retcode and not error and actual_error:
215
238
            test_case.fail('In \n\t%s\nUnexpected error: %s'
216
239
                           % (' '.join(cmd), actual_error))
217
240
        return retcode, actual_output, actual_error
218
241
 
219
242
    def _check_output(self, expected, actual, test_case):
220
 
        if expected is None:
221
 
            # Specifying None means: any output is accepted
 
243
        if not actual:
 
244
            if expected is None:
 
245
                return
 
246
            elif expected == '...\n':
 
247
                return
 
248
            else:
 
249
                test_case.fail('expected output: %r, but found nothing'
 
250
                            % (expected,))
 
251
 
 
252
        null_output_matches_anything = getattr(
 
253
            self, 'null_output_matches_anything', False)
 
254
        if null_output_matches_anything and expected is None:
222
255
            return
223
 
        if actual is None:
224
 
            test_case.fail('We expected output: %r, but found None'
225
 
                           % (expected,))
 
256
 
 
257
        expected = expected or ''
226
258
        matching = self.output_checker.check_output(
227
259
            expected, actual, self.check_options)
228
260
        if not matching:
232
264
            # 'expected' parameter. So we just fallback to our good old
233
265
            # assertEqualDiff since we know there *are* differences and the
234
266
            # output should be decently readable.
235
 
            test_case.assertEqualDiff(expected, actual)
 
267
            #
 
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
 
271
            # the next line.
 
272
            if expected == actual + '\n':
 
273
                pass
 
274
            else:
 
275
                test_case.assertEqualDiff(expected, actual)
236
276
 
237
277
    def _pre_process_args(self, args):
238
278
        new_args = []
441
481
    def setUp(self):
442
482
        super(TestCaseWithMemoryTransportAndScript, self).setUp()
443
483
        self.script_runner = ScriptRunner()
 
484
        # FIXME: See shelf_ui.Shelver._char_based. This allow using shelve in
 
485
        # scripts while providing a line-based input (better solution in
 
486
        # progress). -- vila 2011-09-28
 
487
        self.overrideEnv('INSIDE_EMACS', '1')
444
488
 
445
 
    def run_script(self, script):
446
 
        return self.script_runner.run_script(self, script)
 
489
    def run_script(self, script, null_output_matches_anything=False):
 
490
        return self.script_runner.run_script(self, script, 
 
491
                   null_output_matches_anything=null_output_matches_anything)
447
492
 
448
493
    def run_command(self, cmd, input, output, error):
449
494
        return self.script_runner.run_command(self, cmd, input, output, error)
470
515
    def setUp(self):
471
516
        super(TestCaseWithTransportAndScript, self).setUp()
472
517
        self.script_runner = ScriptRunner()
 
518
        # FIXME: See shelf_ui.Shelver._char_based. This allow using shelve in
 
519
        # scripts while providing a line-based input (better solution in
 
520
        # progress). -- vila 2011-09-28
 
521
        self.overrideEnv('INSIDE_EMACS', '1')
473
522
 
474
 
    def run_script(self, script):
475
 
        return self.script_runner.run_script(self, script)
 
523
    def run_script(self, script, null_output_matches_anything=False):
 
524
        return self.script_runner.run_script(self, script,
 
525
                   null_output_matches_anything=null_output_matches_anything)
476
526
 
477
527
    def run_command(self, cmd, input, output, error):
478
528
        return self.script_runner.run_command(self, cmd, input, output, error)
479
529
 
480
530
 
481
 
def run_script(test_case, script_string):
 
531
def run_script(test_case, script_string, null_output_matches_anything=False):
482
532
    """Run the given script within a testcase"""
483
 
    return ScriptRunner().run_script(test_case, script_string)
 
533
    return ScriptRunner().run_script(test_case, script_string,
 
534
               null_output_matches_anything=null_output_matches_anything)
 
535