/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/script.py

  • Committer: Jelmer Vernooij
  • Date: 2020-04-05 19:11:34 UTC
  • mto: (7490.7.16 work)
  • mto: This revision was merged to the branch mainline in revision 7501.
  • Revision ID: jelmer@jelmer.uk-20200405191134-0aebh8ikiwygxma5
Populate the .gitignore file.

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
22
22
import doctest
23
23
import errno
24
24
import glob
 
25
import logging
25
26
import os
26
27
import shlex
27
 
from cStringIO import StringIO
 
28
import sys
 
29
import textwrap
28
30
 
29
 
from bzrlib import (
 
31
from .. import (
30
32
    osutils,
31
33
    tests,
 
34
    trace,
32
35
    )
 
36
from ..tests import ui_testing
33
37
 
34
38
 
35
39
def split(s):
55
59
    Input lines start with '<'.
56
60
    Output lines start with nothing.
57
61
    Error lines start with '2>'.
 
62
 
 
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.
58
66
    """
59
67
 
60
68
    commands = []
73
81
    cmd_line = 1
74
82
    lineno = 0
75
83
    input, output, error = None, None, None
76
 
    for line in text.split('\n'):
 
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] == '':
 
89
        del lines[0]
 
90
    if lines and lines[-1] == '':
 
91
        del lines[-1]
 
92
    for line in lines:
77
93
        lineno += 1
78
94
        # Keep a copy for error reporting
79
95
        orig = line
80
 
        comment =  line.find('#')
 
96
        comment = line.find('#')
81
97
        if comment >= 0:
82
98
            # Delete comments
 
99
            # NB: this syntax means comments are allowed inside output, which
 
100
            # may be confusing...
83
101
            line = line[0:comment]
84
102
            line = line.rstrip()
85
 
        if line == '':
86
 
            # Ignore empty lines
87
 
            continue
 
103
            if line == '':
 
104
                continue
88
105
        if line.startswith('$'):
89
106
            # Time to output the current command
90
107
            add_command(cmd_cur, input, output, error)
125
142
 
126
143
    :param args: The command line arguments
127
144
 
128
 
    :return: A tuple containing: 
 
145
    :return: A tuple containing:
129
146
        - The file name redirected from or None
130
147
        - The file name redirected to or None
131
148
        - The mode to open the output file or None
150
167
            in_name = redirected_file_name('<', arg[1:], args)
151
168
        elif arg.startswith('>>'):
152
169
            out_name = redirected_file_name('>>', arg[2:], args)
153
 
            out_mode = 'ab+'
 
170
            out_mode = 'a+'
154
171
        elif arg.startswith('>',):
155
172
            out_name = redirected_file_name('>', arg[1:], args)
156
 
            out_mode = 'wb+'
 
173
            out_mode = 'w+'
157
174
        else:
158
175
            remaining.append(arg)
159
176
    return in_name, out_name, out_mode, remaining
161
178
 
162
179
class ScriptRunner(object):
163
180
    """Run a shell-like script from a test.
164
 
    
 
181
 
165
182
    Can be used as:
166
183
 
167
 
    from bzrlib.tests import script
 
184
    from breezy.tests import script
168
185
 
169
186
    ...
170
187
 
171
188
        def test_bug_nnnnn(self):
172
189
            sr = script.ScriptRunner()
173
190
            sr.run_script(self, '''
174
 
            $ bzr init
175
 
            $ bzr do-this
 
191
            $ brz init
 
192
            $ brz do-this
176
193
            # Boom, error
177
194
            ''')
178
195
    """
181
198
        self.output_checker = doctest.OutputChecker()
182
199
        self.check_options = doctest.ELLIPSIS
183
200
 
184
 
    def run_script(self, test_case, text):
 
201
    def run_script(self, test_case, text, null_output_matches_anything=False):
185
202
        """Run a shell-like script as a test.
186
203
 
187
204
        :param test_case: A TestCase instance that should provide the fail(),
189
206
            attribute used as a jail root.
190
207
 
191
208
        :param text: A shell-like script (see _script_to_commands for syntax).
 
209
 
 
210
        :param null_output_matches_anything: For commands with no specified
 
211
            output, ignore any output that does happen, including output on
 
212
            standard error.
192
213
        """
 
214
        self.null_output_matches_anything = null_output_matches_anything
193
215
        for cmd, input, output, error in _script_to_commands(text):
194
216
            self.run_command(test_case, cmd, input, output, error)
195
217
 
198
220
        method = getattr(self, mname, None)
199
221
        if method is None:
200
222
            raise SyntaxError('Command not found "%s"' % (cmd[0],),
201
 
                              None, 1, ' '.join(cmd))
 
223
                              (None, 1, 1, ' '.join(cmd)))
202
224
        if input is None:
203
225
            str_input = ''
204
226
        else:
207
229
        retcode, actual_output, actual_error = method(test_case,
208
230
                                                      str_input, args)
209
231
 
210
 
        self._check_output(output, actual_output, test_case)
211
 
        self._check_output(error, actual_error, test_case)
 
232
        try:
 
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)
 
236
        try:
 
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)
212
241
        if retcode and not error and actual_error:
213
242
            test_case.fail('In \n\t%s\nUnexpected error: %s'
214
243
                           % (' '.join(cmd), actual_error))
215
244
        return retcode, actual_output, actual_error
216
245
 
217
246
    def _check_output(self, expected, actual, test_case):
218
 
        if expected is None:
219
 
            # Specifying None means: any output is accepted
 
247
        if not actual:
 
248
            if expected is None:
 
249
                return
 
250
            elif expected == '...\n':
 
251
                return
 
252
            else:
 
253
                test_case.fail('expected output: %r, but found nothing'
 
254
                               % (expected,))
 
255
 
 
256
        null_output_matches_anything = getattr(
 
257
            self, 'null_output_matches_anything', False)
 
258
        if null_output_matches_anything and expected is None:
220
259
            return
221
 
        if actual is None:
222
 
            test_case.fail('We expected output: %r, but found None'
223
 
                           % (expected,))
 
260
 
 
261
        expected = expected or ''
224
262
        matching = self.output_checker.check_output(
225
263
            expected, actual, self.check_options)
226
264
        if not matching:
230
268
            # 'expected' parameter. So we just fallback to our good old
231
269
            # assertEqualDiff since we know there *are* differences and the
232
270
            # output should be decently readable.
233
 
            test_case.assertEqualDiff(expected, actual)
 
271
            #
 
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
 
275
            # the next line.
 
276
            if expected == actual + '\n':
 
277
                pass
 
278
            else:
 
279
                test_case.assertEqualDiff(expected, actual)
234
280
 
235
281
    def _pre_process_args(self, args):
236
282
        new_args = []
238
284
            # Strip the simple and double quotes since we don't care about
239
285
            # them.  We leave the backquotes in place though since they have a
240
286
            # different semantic.
241
 
            if arg[0] in  ('"', "'") and arg[0] == arg[-1]:
 
287
            if arg[0] in ('"', "'") and arg[0] == arg[-1]:
242
288
                yield arg[1:-1]
243
289
            else:
244
290
                if glob.has_magic(arg):
254
300
 
255
301
    def _read_input(self, input, in_name):
256
302
        if in_name is not None:
257
 
            infile = open(in_name, 'rb')
 
303
            infile = open(in_name, 'r')
258
304
            try:
259
305
                # Command redirection takes precedence over provided input
260
306
                input = infile.read()
272
318
            output = None
273
319
        return output
274
320
 
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
 
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
 
327
 
 
328
            # FIXME: don't call into logging here
 
329
            handler = trace.EncodedStreamHandler(
 
330
                stderr, errors="replace")
 
331
        else:
 
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)
 
337
 
 
338
        logger = logging.getLogger('')
 
339
        logger.addHandler(handler)
 
340
        try:
 
341
            retcode = test_case._run_bzr_core(
 
342
                args, encoding=encoding, stdin=input, stdout=stdout,
 
343
                stderr=stderr, working_dir=None)
 
344
        finally:
 
345
            logger.removeHandler(handler)
 
346
 
 
347
        return retcode, stdout.getvalue(), stderr.getvalue()
279
348
 
280
349
    def do_cat(self, test_case, input, args):
281
350
        (in_name, out_name, out_mode, args) = _scan_redirection_options(args)
291
360
        for in_name in input_names:
292
361
            try:
293
362
                inputs.append(self._read_input(None, in_name))
294
 
            except IOError, e:
 
363
            except IOError as e:
295
364
                # Some filenames are illegal on Windows and generate EINVAL
296
365
                # rather than just saying the filename doesn't exist
297
366
                if e.errno in (errno.ENOENT, errno.EINVAL):
303
372
        # Handle output redirections
304
373
        try:
305
374
            output = self._write_output(output, out_name, out_mode)
306
 
        except IOError, e:
 
375
        except IOError as e:
307
376
            # If out_name cannot be created, we may get 'ENOENT', however if
308
377
            # out_name is something like '', we can get EINVAL
309
378
            if e.errno in (errno.ENOENT, errno.EINVAL):
324
393
        # Handle output redirections
325
394
        try:
326
395
            output = self._write_output(output, out_name, out_mode)
327
 
        except IOError, e:
 
396
        except IOError as e:
328
397
            if e.errno in (errno.ENOENT, errno.EINVAL):
329
398
                return 1, None, '%s: No such file or directory\n' % (out_name,)
330
399
            raise
362
431
        err = None
363
432
 
364
433
        def error(msg, path):
365
 
            return  "rm: cannot remove '%s': %s\n" % (path, msg)
 
434
            return "rm: cannot remove '%s': %s\n" % (path, msg)
366
435
 
367
436
        force, recursive = False, False
368
437
        opts = None
381
450
            # FIXME: Should we put that in osutils ?
382
451
            try:
383
452
                os.remove(p)
384
 
            except OSError, e:
 
453
            except OSError as e:
385
454
                # Various OSes raises different exceptions (linux: EISDIR,
386
455
                #   win32: EACCES, OSX: EPERM) when invoked on a directory
387
456
                if e.errno in (errno.EISDIR, errno.EPERM, errno.EACCES):
392
461
                        break
393
462
                elif e.errno == errno.ENOENT:
394
463
                    if not force:
395
 
                        err =  error('No such file or directory', p)
 
464
                        err = error('No such file or directory', p)
396
465
                        break
397
466
                else:
398
467
                    raise
404
473
 
405
474
    def do_mv(self, test_case, input, args):
406
475
        err = None
 
476
 
407
477
        def error(msg, src, dst):
408
478
            return "mv: cannot move %s to %s: %s\n" % (src, dst, msg)
409
479
 
415
485
            if os.path.isdir(dst):
416
486
                real_dst = os.path.join(dst, os.path.basename(src))
417
487
            os.rename(src, real_dst)
418
 
        except OSError, e:
 
488
        except OSError as e:
419
489
            if e.errno == errno.ENOENT:
420
490
                err = error('No such file or directory', src, dst)
421
491
            else:
427
497
        return retcode, None, err
428
498
 
429
499
 
430
 
 
431
500
class TestCaseWithMemoryTransportAndScript(tests.TestCaseWithMemoryTransport):
432
501
    """Helper class to experiment shell-like test and memory fs.
433
502
 
439
508
    def setUp(self):
440
509
        super(TestCaseWithMemoryTransportAndScript, self).setUp()
441
510
        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')
442
515
 
443
 
    def run_script(self, script):
444
 
        return self.script_runner.run_script(self, script)
 
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)
445
519
 
446
520
    def run_command(self, cmd, input, output, error):
447
521
        return self.script_runner.run_command(self, cmd, input, output, error)
452
526
 
453
527
    Can be used as:
454
528
 
455
 
    from bzrlib.tests import script
 
529
    from breezy.tests import script
456
530
 
457
531
 
458
532
    class TestBug(script.TestCaseWithTransportAndScript):
459
533
 
460
534
        def test_bug_nnnnn(self):
461
535
            self.run_script('''
462
 
            $ bzr init
463
 
            $ bzr do-this
 
536
            $ brz init
 
537
            $ brz do-this
464
538
            # Boom, error
465
539
            ''')
466
540
    """
468
542
    def setUp(self):
469
543
        super(TestCaseWithTransportAndScript, self).setUp()
470
544
        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')
471
549
 
472
 
    def run_script(self, script):
473
 
        return self.script_runner.run_script(self, script)
 
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)
474
553
 
475
554
    def run_command(self, cmd, input, output, error):
476
555
        return self.script_runner.run_command(self, cmd, input, output, error)
477
556
 
 
557
 
 
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)