/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5557.1.15 by John Arbash Meinel
Merge bzr.dev 5597 to resolve NEWS, aka bzr-2.3.txt
1
# Copyright (C) 2009, 2010, 2011 Canonical Ltd
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
4665.5.20 by Vincent Ladeuil
Fixed as per Martin's review.
16
4665.5.9 by Vincent Ladeuil
Start adding doc.
17
"""Shell-like test scripts.
18
4665.5.21 by Vincent Ladeuil
Fix some typos mentioned by Martin.
19
See developers/testing.html for more explanations.
4665.5.9 by Vincent Ladeuil
Start adding doc.
20
"""
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
21
4665.5.12 by Vincent Ladeuil
Support '...' in expected strings.
22
import doctest
4665.5.17 by Vincent Ladeuil
Implement 'rm' command.
23
import errno
4665.5.19 by Vincent Ladeuil
Implement globbing and enhance cat to accept multiple files.
24
import glob
7065.3.8 by Jelmer Vernooij
Fix tests.
25
import logging
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
26
import os
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
27
import shlex
7065.3.1 by Jelmer Vernooij
Some refactoring.
28
import sys
5283.1.2 by Martin Pool
ScriptRunner strips consistent leading indentation from scripts
29
import textwrap
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
30
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
31
from .. import (
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
32
    osutils,
33
    tests,
7065.3.8 by Jelmer Vernooij
Fix tests.
34
    trace,
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
35
    )
7065.3.1 by Jelmer Vernooij
Some refactoring.
36
from ..tests import ui_testing
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
37
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
38
4665.5.2 by Vincent Ladeuil
Handle simple, double and back quotes.
39
def split(s):
40
    """Split a command line respecting quotes."""
41
    scanner = shlex.shlex(s)
42
    scanner.quotes = '\'"`'
43
    scanner.whitespace_split = True
44
    for t in list(scanner):
4665.5.19 by Vincent Ladeuil
Implement globbing and enhance cat to accept multiple files.
45
        yield t
4665.5.2 by Vincent Ladeuil
Handle simple, double and back quotes.
46
47
48
def _script_to_commands(text, file_name=None):
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
49
    """Turn a script into a list of commands with their associated IOs.
50
4665.5.21 by Vincent Ladeuil
Fix some typos mentioned by Martin.
51
    Each command appears on a line by itself starting with '$ '. It can be
52
    associated with an input that will feed it and an expected output.
53
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
54
    Comments starts with '#' until the end of line.
55
    Empty lines are ignored.
4665.5.21 by Vincent Ladeuil
Fix some typos mentioned by Martin.
56
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
57
    Input and output are full lines terminated by a '\n'.
4665.5.21 by Vincent Ladeuil
Fix some typos mentioned by Martin.
58
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
59
    Input lines start with '<'.
4665.5.21 by Vincent Ladeuil
Fix some typos mentioned by Martin.
60
    Output lines start with nothing.
4665.5.3 by Vincent Ladeuil
Separate error from normal output.
61
    Error lines start with '2>'.
5417.1.4 by Martin Pool
doc
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.
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
66
    """
4665.5.7 by Vincent Ladeuil
Simplify output/errors handling.
67
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
68
    commands = []
4665.5.7 by Vincent Ladeuil
Simplify output/errors handling.
69
70
    def add_command(cmd, input, output, error):
71
        if cmd is not None:
72
            if input is not None:
73
                input = ''.join(input)
74
            if output is not None:
75
                output = ''.join(output)
76
            if error is not None:
77
                error = ''.join(error)
78
            commands.append((cmd, input, output, error))
79
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
80
    cmd_cur = None
81
    cmd_line = 1
82
    lineno = 0
4665.5.3 by Vincent Ladeuil
Separate error from normal output.
83
    input, output, error = None, None, None
5283.1.2 by Martin Pool
ScriptRunner strips consistent leading indentation from scripts
84
    text = textwrap.dedent(text)
5417.1.5 by Martin Pool
Better handling of blank lines in test scripts
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:
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
93
        lineno += 1
94
        # Keep a copy for error reporting
95
        orig = line
7143.15.2 by Jelmer Vernooij
Run autopep8.
96
        comment = line.find('#')
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
97
        if comment >= 0:
98
            # Delete comments
5417.1.5 by Martin Pool
Better handling of blank lines in test scripts
99
            # NB: this syntax means comments are allowed inside output, which
100
            # may be confusing...
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
101
            line = line[0:comment]
102
            line = line.rstrip()
5417.1.5 by Martin Pool
Better handling of blank lines in test scripts
103
            if line == '':
104
                continue
4665.5.20 by Vincent Ladeuil
Fixed as per Martin's review.
105
        if line.startswith('$'):
106
            # Time to output the current command
107
            add_command(cmd_cur, input, output, error)
108
            # And start a new one
109
            cmd_cur = list(split(line[1:]))
110
            cmd_line = lineno
111
            input, output, error = None, None, None
112
        elif line.startswith('<'):
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
113
            if input is None:
114
                if cmd_cur is None:
115
                    raise SyntaxError('No command for that input',
116
                                      (file_name, lineno, 1, orig))
117
                input = []
118
            input.append(line[1:] + '\n')
4665.5.3 by Vincent Ladeuil
Separate error from normal output.
119
        elif line.startswith('2>'):
120
            if error is None:
121
                if cmd_cur is None:
122
                    raise SyntaxError('No command for that error',
123
                                      (file_name, lineno, 1, orig))
124
                error = []
125
            error.append(line[2:] + '\n')
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
126
        else:
4889.1.1 by Martin Pool
Clearer syntax error message from ScriptRunner
127
            # can happen if the first line is not recognized as a command, eg
128
            # if the prompt has leading whitespace
4665.5.20 by Vincent Ladeuil
Fixed as per Martin's review.
129
            if output is None:
130
                if cmd_cur is None:
4889.1.1 by Martin Pool
Clearer syntax error message from ScriptRunner
131
                    raise SyntaxError('No command for line %r' % (line,),
4665.5.20 by Vincent Ladeuil
Fixed as per Martin's review.
132
                                      (file_name, lineno, 1, orig))
133
                output = []
134
            output.append(line + '\n')
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
135
    # Add the last seen command
4665.5.7 by Vincent Ladeuil
Simplify output/errors handling.
136
    add_command(cmd_cur, input, output, error)
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
137
    return commands
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
138
139
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
140
def _scan_redirection_options(args):
141
    """Recognize and process input and output redirections.
142
143
    :param args: The command line arguments
144
7195.5.1 by Martin
Fix remaining whitespace lint in codebase
145
    :return: A tuple containing:
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
146
        - The file name redirected from or None
147
        - The file name redirected to or None
148
        - The mode to open the output file or None
149
        - The reamining arguments
150
    """
4665.5.18 by Vincent Ladeuil
Better redirection handling.
151
    def redirected_file_name(direction, name, args):
152
        if name == '':
153
            try:
154
                name = args.pop(0)
155
            except IndexError:
156
                # We leave the error handling to higher levels, an empty name
157
                # can't be legal.
158
                name = ''
159
        return name
160
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
161
    remaining = []
162
    in_name = None
163
    out_name, out_mode = None, None
4665.5.18 by Vincent Ladeuil
Better redirection handling.
164
    while args:
165
        arg = args.pop(0)
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
166
        if arg.startswith('<'):
4665.5.18 by Vincent Ladeuil
Better redirection handling.
167
            in_name = redirected_file_name('<', arg[1:], args)
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
168
        elif arg.startswith('>>'):
4665.5.18 by Vincent Ladeuil
Better redirection handling.
169
            out_name = redirected_file_name('>>', arg[2:], args)
7045.1.1 by Jelmer Vernooij
Fix another 300 tests.
170
            out_mode = 'a+'
4665.5.18 by Vincent Ladeuil
Better redirection handling.
171
        elif arg.startswith('>',):
172
            out_name = redirected_file_name('>', arg[1:], args)
7045.1.1 by Jelmer Vernooij
Fix another 300 tests.
173
            out_mode = 'w+'
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
174
        else:
175
            remaining.append(arg)
176
    return in_name, out_name, out_mode, remaining
177
178
4665.5.10 by Vincent Ladeuil
Start separating the script runner from the test case.
179
class ScriptRunner(object):
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
180
    """Run a shell-like script from a test.
7143.15.2 by Jelmer Vernooij
Run autopep8.
181
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
182
    Can be used as:
183
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
184
    from breezy.tests import script
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
185
186
    ...
187
188
        def test_bug_nnnnn(self):
189
            sr = script.ScriptRunner()
190
            sr.run_script(self, '''
6622.1.29 by Jelmer Vernooij
Fix some more tests.
191
            $ brz init
192
            $ brz do-this
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
193
            # Boom, error
194
            ''')
195
    """
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
196
4665.5.23 by Vincent Ladeuil
Revert the jail_root parameter addition.
197
    def __init__(self):
4665.5.12 by Vincent Ladeuil
Support '...' in expected strings.
198
        self.output_checker = doctest.OutputChecker()
199
        self.check_options = doctest.ELLIPSIS
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
200
5531.1.2 by Vincent Ladeuil
s/blank_output/null_output/
201
    def run_script(self, test_case, text, null_output_matches_anything=False):
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
202
        """Run a shell-like script as a test.
203
204
        :param test_case: A TestCase instance that should provide the fail(),
205
            assertEqualDiff and _run_bzr_core() methods as well as a 'test_dir'
206
            attribute used as a jail root.
207
208
        :param text: A shell-like script (see _script_to_commands for syntax).
5509.1.2 by Neil Martinsen-Burrell
address vilas review comments
209
5531.1.2 by Vincent Ladeuil
s/blank_output/null_output/
210
        :param null_output_matches_anything: For commands with no specified
5509.1.3 by Neil Martinsen-Burrell
change option name and mention the effect on standard error
211
            output, ignore any output that does happen, including output on
212
            standard error.
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
213
        """
5531.1.2 by Vincent Ladeuil
s/blank_output/null_output/
214
        self.null_output_matches_anything = null_output_matches_anything
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
215
        for cmd, input, output, error in _script_to_commands(text):
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
216
            self.run_command(test_case, cmd, input, output, error)
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
217
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
218
    def run_command(self, test_case, cmd, input, output, error):
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
219
        mname = 'do_' + cmd[0]
220
        method = getattr(self, mname, None)
221
        if method is None:
222
            raise SyntaxError('Command not found "%s"' % (cmd[0],),
5508.2.1 by Martin
In shell-like tests raise a valid SyntaxError if a command is not found
223
                              (None, 1, 1, ' '.join(cmd)))
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
224
        if input is None:
225
            str_input = ''
226
        else:
227
            str_input = ''.join(input)
228
        args = list(self._pre_process_args(cmd[1:]))
229
        retcode, actual_output, actual_error = method(test_case,
230
                                                      str_input, args)
231
5422.3.1 by Martin Pool
Blank scriptrunner output only matches blank
232
        try:
233
            self._check_output(output, actual_output, test_case)
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
234
        except AssertionError as e:
5422.3.1 by Martin Pool
Blank scriptrunner output only matches blank
235
            raise AssertionError(str(e) + " in stdout of command %s" % cmd)
236
        try:
237
            self._check_output(error, actual_error, test_case)
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
238
        except AssertionError as e:
7143.15.2 by Jelmer Vernooij
Run autopep8.
239
            raise AssertionError(str(e)
240
                                 + " in stderr of running command %s" % cmd)
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
241
        if retcode and not error and actual_error:
242
            test_case.fail('In \n\t%s\nUnexpected error: %s'
243
                           % (' '.join(cmd), actual_error))
244
        return retcode, actual_output, actual_error
245
246
    def _check_output(self, expected, actual, test_case):
5422.3.1 by Martin Pool
Blank scriptrunner output only matches blank
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'
7143.15.2 by Jelmer Vernooij
Run autopep8.
254
                               % (expected,))
5509.1.1 by Neil Martinsen-Burrell
add ignore_blanks to run_script to recover old behavior
255
5531.1.2 by Vincent Ladeuil
s/blank_output/null_output/
256
        null_output_matches_anything = getattr(
257
            self, 'null_output_matches_anything', False)
258
        if null_output_matches_anything and expected is None:
5509.1.1 by Neil Martinsen-Burrell
add ignore_blanks to run_script to recover old behavior
259
            return
5531.1.2 by Vincent Ladeuil
s/blank_output/null_output/
260
5422.3.1 by Martin Pool
Blank scriptrunner output only matches blank
261
        expected = expected or ''
4665.5.12 by Vincent Ladeuil
Support '...' in expected strings.
262
        matching = self.output_checker.check_output(
263
            expected, actual, self.check_options)
264
        if not matching:
265
            # Note that we can't use output_checker.output_difference() here
4665.5.15 by Vincent Ladeuil
Catch the retcode for all commands.
266
            # because... the API is broken ('expected' must be a doctest
267
            # specific object of which a 'want' attribute will be our
268
            # 'expected' parameter. So we just fallback to our good old
269
            # assertEqualDiff since we know there *are* differences and the
270
            # output should be decently readable.
5417.1.1 by Martin Pool
ScriptRunner can now cope with commands that prompt for input.
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:
5422.3.5 by Martin Pool
Go back to using assertEqualDiff when a script fails
279
                test_case.assertEqualDiff(expected, actual)
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
280
4665.5.19 by Vincent Ladeuil
Implement globbing and enhance cat to accept multiple files.
281
    def _pre_process_args(self, args):
282
        new_args = []
283
        for arg in args:
284
            # Strip the simple and double quotes since we don't care about
285
            # them.  We leave the backquotes in place though since they have a
286
            # different semantic.
7143.15.2 by Jelmer Vernooij
Run autopep8.
287
            if arg[0] in ('"', "'") and arg[0] == arg[-1]:
4665.5.19 by Vincent Ladeuil
Implement globbing and enhance cat to accept multiple files.
288
                yield arg[1:-1]
289
            else:
290
                if glob.has_magic(arg):
291
                    matches = glob.glob(arg)
292
                    if matches:
293
                        # We care more about order stability than performance
294
                        # here
295
                        matches.sort()
296
                        for m in matches:
297
                            yield m
298
                else:
299
                    yield arg
300
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
301
    def _read_input(self, input, in_name):
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
302
        if in_name is not None:
7045.1.1 by Jelmer Vernooij
Fix another 300 tests.
303
            infile = open(in_name, 'r')
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
304
            try:
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
305
                # Command redirection takes precedence over provided input
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
306
                input = infile.read()
307
            finally:
308
                infile.close()
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
309
        return input
310
311
    def _write_output(self, output, out_name, out_mode):
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
312
        if out_name is not None:
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
313
            outfile = open(out_name, out_mode)
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
314
            try:
315
                outfile.write(output)
316
            finally:
317
                outfile.close()
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
318
            output = None
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
319
        return output
320
6622.1.29 by Jelmer Vernooij
Fix some more tests.
321
    def do_brz(self, test_case, input, args):
7065.3.1 by Jelmer Vernooij
Some refactoring.
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
7065.3.8 by Jelmer Vernooij
Fix tests.
327
328
            # FIXME: don't call into logging here
329
            handler = trace.EncodedStreamHandler(
330
                stderr, errors="replace")
7065.3.1 by Jelmer Vernooij
Some refactoring.
331
        else:
332
            stdout = ui_testing.StringIOWithEncoding()
333
            stderr = ui_testing.StringIOWithEncoding()
334
            stdout.encoding = stderr.encoding = encoding
7065.3.8 by Jelmer Vernooij
Fix tests.
335
            handler = logging.StreamHandler(stderr)
336
        handler.setLevel(logging.INFO)
7065.3.1 by Jelmer Vernooij
Some refactoring.
337
7065.3.8 by Jelmer Vernooij
Fix tests.
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)
7065.3.1 by Jelmer Vernooij
Some refactoring.
346
347
        return retcode, stdout.getvalue(), stderr.getvalue()
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
348
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
349
    def do_cat(self, test_case, input, args):
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
350
        (in_name, out_name, out_mode, args) = _scan_redirection_options(args)
4665.5.19 by Vincent Ladeuil
Implement globbing and enhance cat to accept multiple files.
351
        if args and in_name is not None:
352
            raise SyntaxError('Specify a file OR use redirection')
353
354
        inputs = []
355
        if input:
356
            inputs.append(input)
357
        input_names = args
358
        if in_name:
359
            args.append(in_name)
360
        for in_name in input_names:
361
            try:
362
                inputs.append(self._read_input(None, in_name))
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
363
            except IOError as e:
4789.18.1 by John Arbash Meinel
Rework test_script a little bit.
364
                # Some filenames are illegal on Windows and generate EINVAL
365
                # rather than just saying the filename doesn't exist
366
                if e.errno in (errno.ENOENT, errno.EINVAL):
4665.5.19 by Vincent Ladeuil
Implement globbing and enhance cat to accept multiple files.
367
                    return (1, None,
368
                            '%s: No such file or directory\n' % (in_name,))
4789.18.1 by John Arbash Meinel
Rework test_script a little bit.
369
                raise
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
370
        # Basically cat copy input to output
4665.5.19 by Vincent Ladeuil
Implement globbing and enhance cat to accept multiple files.
371
        output = ''.join(inputs)
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
372
        # Handle output redirections
4665.5.18 by Vincent Ladeuil
Better redirection handling.
373
        try:
374
            output = self._write_output(output, out_name, out_mode)
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
375
        except IOError as e:
4789.18.1 by John Arbash Meinel
Rework test_script a little bit.
376
            # If out_name cannot be created, we may get 'ENOENT', however if
377
            # out_name is something like '', we can get EINVAL
378
            if e.errno in (errno.ENOENT, errno.EINVAL):
4665.5.18 by Vincent Ladeuil
Better redirection handling.
379
                return 1, None, '%s: No such file or directory\n' % (out_name,)
4789.18.1 by John Arbash Meinel
Rework test_script a little bit.
380
            raise
4665.5.15 by Vincent Ladeuil
Catch the retcode for all commands.
381
        return 0, output, None
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
382
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
383
    def do_echo(self, test_case, input, args):
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
384
        (in_name, out_name, out_mode, args) = _scan_redirection_options(args)
4789.18.1 by John Arbash Meinel
Rework test_script a little bit.
385
        if input or in_name:
386
            raise SyntaxError('echo doesn\'t read from stdin')
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
387
        if args:
4665.5.20 by Vincent Ladeuil
Fixed as per Martin's review.
388
            input = ' '.join(args)
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
389
        # Always append a \n'
390
        input += '\n'
391
        # Process output
392
        output = input
393
        # Handle output redirections
4665.5.18 by Vincent Ladeuil
Better redirection handling.
394
        try:
395
            output = self._write_output(output, out_name, out_mode)
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
396
        except IOError as e:
4789.18.1 by John Arbash Meinel
Rework test_script a little bit.
397
            if e.errno in (errno.ENOENT, errno.EINVAL):
4665.5.18 by Vincent Ladeuil
Better redirection handling.
398
                return 1, None, '%s: No such file or directory\n' % (out_name,)
4789.18.1 by John Arbash Meinel
Rework test_script a little bit.
399
            raise
4665.5.15 by Vincent Ladeuil
Catch the retcode for all commands.
400
        return 0, output, None
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
401
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
402
    def _get_jail_root(self, test_case):
403
        return test_case.test_dir
404
4665.5.23 by Vincent Ladeuil
Revert the jail_root parameter addition.
405
    def _ensure_in_jail(self, test_case, path):
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
406
        jail_root = self._get_jail_root(test_case)
4665.5.23 by Vincent Ladeuil
Revert the jail_root parameter addition.
407
        if not osutils.is_inside(jail_root, osutils.normalizepath(path)):
408
            raise ValueError('%s is not inside %s' % (path, jail_root))
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
409
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
410
    def do_cd(self, test_case, input, args):
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
411
        if len(args) > 1:
412
            raise SyntaxError('Usage: cd [dir]')
413
        if len(args) == 1:
414
            d = args[0]
4665.5.23 by Vincent Ladeuil
Revert the jail_root parameter addition.
415
            self._ensure_in_jail(test_case, d)
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
416
        else:
4665.5.23 by Vincent Ladeuil
Revert the jail_root parameter addition.
417
            # The test "home" directory is the root of its jail
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
418
            d = self._get_jail_root(test_case)
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
419
        os.chdir(d)
4665.5.15 by Vincent Ladeuil
Catch the retcode for all commands.
420
        return 0, None, None
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
421
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
422
    def do_mkdir(self, test_case, input, args):
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
423
        if not args or len(args) != 1:
424
            raise SyntaxError('Usage: mkdir dir')
425
        d = args[0]
4665.5.23 by Vincent Ladeuil
Revert the jail_root parameter addition.
426
        self._ensure_in_jail(test_case, d)
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
427
        os.mkdir(d)
4665.5.15 by Vincent Ladeuil
Catch the retcode for all commands.
428
        return 0, None, None
4665.5.6 by Vincent Ladeuil
Implement 'bzr' command.
429
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
430
    def do_rm(self, test_case, input, args):
4665.5.17 by Vincent Ladeuil
Implement 'rm' command.
431
        err = None
432
433
        def error(msg, path):
7143.15.2 by Jelmer Vernooij
Run autopep8.
434
            return "rm: cannot remove '%s': %s\n" % (path, msg)
4665.5.17 by Vincent Ladeuil
Implement 'rm' command.
435
436
        force, recursive = False, False
437
        opts = None
438
        if args and args[0][0] == '-':
439
            opts = args.pop(0)[1:]
440
            if 'f' in opts:
441
                force = True
442
                opts = opts.replace('f', '', 1)
443
            if 'r' in opts:
444
                recursive = True
445
                opts = opts.replace('r', '', 1)
446
        if not args or opts:
447
            raise SyntaxError('Usage: rm [-fr] path+')
448
        for p in args:
4665.5.23 by Vincent Ladeuil
Revert the jail_root parameter addition.
449
            self._ensure_in_jail(test_case, p)
4665.5.17 by Vincent Ladeuil
Implement 'rm' command.
450
            # FIXME: Should we put that in osutils ?
451
            try:
452
                os.remove(p)
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
453
            except OSError as e:
4707.1.1 by Vincent Ladeuil
Fix OSX and FreeBSD failures.
454
                # Various OSes raises different exceptions (linux: EISDIR,
455
                #   win32: EACCES, OSX: EPERM) when invoked on a directory
456
                if e.errno in (errno.EISDIR, errno.EPERM, errno.EACCES):
4665.5.17 by Vincent Ladeuil
Implement 'rm' command.
457
                    if recursive:
458
                        osutils.rmtree(p)
459
                    else:
460
                        err = error('Is a directory', p)
461
                        break
462
                elif e.errno == errno.ENOENT:
463
                    if not force:
7143.15.2 by Jelmer Vernooij
Run autopep8.
464
                        err = error('No such file or directory', p)
4665.5.17 by Vincent Ladeuil
Implement 'rm' command.
465
                        break
466
                else:
467
                    raise
468
        if err:
469
            retcode = 1
470
        else:
471
            retcode = 0
472
        return retcode, None, err
473
4953.2.1 by Neil Martinsen-Burrell
add an mv command to shell-like tests
474
    def do_mv(self, test_case, input, args):
475
        err = None
7143.15.2 by Jelmer Vernooij
Run autopep8.
476
4953.2.3 by Vincent Ladeuil
Use os.rename() and fix some typos.
477
        def error(msg, src, dst):
478
            return "mv: cannot move %s to %s: %s\n" % (src, dst, msg)
4953.2.1 by Neil Martinsen-Burrell
add an mv command to shell-like tests
479
480
        if not args or len(args) != 2:
481
            raise SyntaxError("Usage: mv path1 path2")
4953.2.3 by Vincent Ladeuil
Use os.rename() and fix some typos.
482
        src, dst = args
4953.2.1 by Neil Martinsen-Burrell
add an mv command to shell-like tests
483
        try:
4953.2.3 by Vincent Ladeuil
Use os.rename() and fix some typos.
484
            real_dst = dst
485
            if os.path.isdir(dst):
486
                real_dst = os.path.join(dst, os.path.basename(src))
487
            os.rename(src, real_dst)
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
488
        except OSError as e:
4953.2.1 by Neil Martinsen-Burrell
add an mv command to shell-like tests
489
            if e.errno == errno.ENOENT:
4953.2.3 by Vincent Ladeuil
Use os.rename() and fix some typos.
490
                err = error('No such file or directory', src, dst)
4953.2.1 by Neil Martinsen-Burrell
add an mv command to shell-like tests
491
            else:
492
                raise
493
        if err:
494
            retcode = 1
495
        else:
496
            retcode = 0
497
        return retcode, None, err
498
499
4665.5.11 by Vincent Ladeuil
Create a new test case based on TestCaseWithMemoryTransport.
500
class TestCaseWithMemoryTransportAndScript(tests.TestCaseWithMemoryTransport):
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
501
    """Helper class to experiment shell-like test and memory fs.
502
503
    This not intended to be used outside of experiments in implementing memoy
504
    based file systems and evolving bzr so that test can use only memory based
505
    resources.
506
    """
4665.5.11 by Vincent Ladeuil
Create a new test case based on TestCaseWithMemoryTransport.
507
508
    def setUp(self):
509
        super(TestCaseWithMemoryTransportAndScript, self).setUp()
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
510
        self.script_runner = ScriptRunner()
6155.5.5 by Vincent Ladeuil
Allows bzr shelve to be used in test scripts and under emacs shells
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')
4665.5.11 by Vincent Ladeuil
Create a new test case based on TestCaseWithMemoryTransport.
515
5531.1.2 by Vincent Ladeuil
s/blank_output/null_output/
516
    def run_script(self, script, null_output_matches_anything=False):
7143.15.2 by Jelmer Vernooij
Run autopep8.
517
        return self.script_runner.run_script(self, script,
518
                                             null_output_matches_anything=null_output_matches_anything)
4665.5.11 by Vincent Ladeuil
Create a new test case based on TestCaseWithMemoryTransport.
519
520
    def run_command(self, cmd, input, output, error):
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
521
        return self.script_runner.run_command(self, cmd, input, output, error)
4665.5.11 by Vincent Ladeuil
Create a new test case based on TestCaseWithMemoryTransport.
522
523
4665.5.10 by Vincent Ladeuil
Start separating the script runner from the test case.
524
class TestCaseWithTransportAndScript(tests.TestCaseWithTransport):
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
525
    """Helper class to quickly define shell-like tests.
526
527
    Can be used as:
528
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
529
    from breezy.tests import script
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
530
531
532
    class TestBug(script.TestCaseWithTransportAndScript):
533
534
        def test_bug_nnnnn(self):
535
            self.run_script('''
6622.1.29 by Jelmer Vernooij
Fix some more tests.
536
            $ brz init
537
            $ brz do-this
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
538
            # Boom, error
539
            ''')
540
    """
4665.5.10 by Vincent Ladeuil
Start separating the script runner from the test case.
541
542
    def setUp(self):
543
        super(TestCaseWithTransportAndScript, self).setUp()
4665.5.23 by Vincent Ladeuil
Revert the jail_root parameter addition.
544
        self.script_runner = ScriptRunner()
6155.5.5 by Vincent Ladeuil
Allows bzr shelve to be used in test scripts and under emacs shells
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')
4665.5.23 by Vincent Ladeuil
Revert the jail_root parameter addition.
549
5531.1.2 by Vincent Ladeuil
s/blank_output/null_output/
550
    def run_script(self, script, null_output_matches_anything=False):
5509.1.2 by Neil Martinsen-Burrell
address vilas review comments
551
        return self.script_runner.run_script(self, script,
7143.15.2 by Jelmer Vernooij
Run autopep8.
552
                                             null_output_matches_anything=null_output_matches_anything)
4665.5.10 by Vincent Ladeuil
Start separating the script runner from the test case.
553
554
    def run_command(self, cmd, input, output, error):
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
555
        return self.script_runner.run_command(self, cmd, input, output, error)
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
556
5283.1.1 by Martin Pool
Add helper function script.run_script and suggest using it
557
5531.1.2 by Vincent Ladeuil
s/blank_output/null_output/
558
def run_script(test_case, script_string, null_output_matches_anything=False):
5283.1.1 by Martin Pool
Add helper function script.run_script and suggest using it
559
    """Run the given script within a testcase"""
5509.1.2 by Neil Martinsen-Burrell
address vilas review comments
560
    return ScriptRunner().run_script(test_case, script_string,
7143.15.2 by Jelmer Vernooij
Run autopep8.
561
                                     null_output_matches_anything=null_output_matches_anything)