/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
4763.2.4 by John Arbash Meinel
merge bzr.2.1 in preparation for NEWS entry.
1
# Copyright (C) 2009, 2010 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
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
25
import os
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
26
import shlex
5283.1.2 by Martin Pool
ScriptRunner strips consistent leading indentation from scripts
27
import textwrap
4665.5.12 by Vincent Ladeuil
Support '...' in expected strings.
28
from cStringIO import StringIO
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
29
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
30
from bzrlib import (
31
    osutils,
32
    tests,
33
    )
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
34
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
35
4665.5.2 by Vincent Ladeuil
Handle simple, double and back quotes.
36
def split(s):
37
    """Split a command line respecting quotes."""
38
    scanner = shlex.shlex(s)
39
    scanner.quotes = '\'"`'
40
    scanner.whitespace_split = True
41
    for t in list(scanner):
4665.5.19 by Vincent Ladeuil
Implement globbing and enhance cat to accept multiple files.
42
        yield t
4665.5.2 by Vincent Ladeuil
Handle simple, double and back quotes.
43
44
45
def _script_to_commands(text, file_name=None):
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
46
    """Turn a script into a list of commands with their associated IOs.
47
4665.5.21 by Vincent Ladeuil
Fix some typos mentioned by Martin.
48
    Each command appears on a line by itself starting with '$ '. It can be
49
    associated with an input that will feed it and an expected output.
50
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
51
    Comments starts with '#' until the end of line.
52
    Empty lines are ignored.
4665.5.21 by Vincent Ladeuil
Fix some typos mentioned by Martin.
53
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
54
    Input and output are full lines terminated by a '\n'.
4665.5.21 by Vincent Ladeuil
Fix some typos mentioned by Martin.
55
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
56
    Input lines start with '<'.
4665.5.21 by Vincent Ladeuil
Fix some typos mentioned by Martin.
57
    Output lines start with nothing.
4665.5.3 by Vincent Ladeuil
Separate error from normal output.
58
    Error lines start with '2>'.
5417.1.4 by Martin Pool
doc
59
60
    :return: A sequence of ([args], input, output, errors), where the args are
61
        split in to words, and the input, output, and errors are just strings,
62
        typically containing newlines.
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
63
    """
4665.5.7 by Vincent Ladeuil
Simplify output/errors handling.
64
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
65
    commands = []
4665.5.7 by Vincent Ladeuil
Simplify output/errors handling.
66
67
    def add_command(cmd, input, output, error):
68
        if cmd is not None:
69
            if input is not None:
70
                input = ''.join(input)
71
            if output is not None:
72
                output = ''.join(output)
73
            if error is not None:
74
                error = ''.join(error)
75
            commands.append((cmd, input, output, error))
76
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
77
    cmd_cur = None
78
    cmd_line = 1
79
    lineno = 0
4665.5.3 by Vincent Ladeuil
Separate error from normal output.
80
    input, output, error = None, None, None
5283.1.2 by Martin Pool
ScriptRunner strips consistent leading indentation from scripts
81
    text = textwrap.dedent(text)
5417.1.5 by Martin Pool
Better handling of blank lines in test scripts
82
    lines = text.split('\n')
83
    # to make use of triple-quoted strings easier, we ignore a blank line
84
    # right at the start and right at the end; the rest are meaningful
85
    if lines and lines[0] == '':
86
        del lines[0]
87
    if lines and lines[-1] == '':
88
        del lines[-1]
89
    for line in lines:
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
90
        lineno += 1
91
        # Keep a copy for error reporting
92
        orig = line
93
        comment =  line.find('#')
94
        if comment >= 0:
95
            # Delete comments
5417.1.5 by Martin Pool
Better handling of blank lines in test scripts
96
            # NB: this syntax means comments are allowed inside output, which
97
            # may be confusing...
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
98
            line = line[0:comment]
99
            line = line.rstrip()
5417.1.5 by Martin Pool
Better handling of blank lines in test scripts
100
            if line == '':
101
                continue
4665.5.20 by Vincent Ladeuil
Fixed as per Martin's review.
102
        if line.startswith('$'):
103
            # Time to output the current command
104
            add_command(cmd_cur, input, output, error)
105
            # And start a new one
106
            cmd_cur = list(split(line[1:]))
107
            cmd_line = lineno
108
            input, output, error = None, None, None
109
        elif line.startswith('<'):
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
110
            if input is None:
111
                if cmd_cur is None:
112
                    raise SyntaxError('No command for that input',
113
                                      (file_name, lineno, 1, orig))
114
                input = []
115
            input.append(line[1:] + '\n')
4665.5.3 by Vincent Ladeuil
Separate error from normal output.
116
        elif line.startswith('2>'):
117
            if error is None:
118
                if cmd_cur is None:
119
                    raise SyntaxError('No command for that error',
120
                                      (file_name, lineno, 1, orig))
121
                error = []
122
            error.append(line[2:] + '\n')
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
123
        else:
4889.1.1 by Martin Pool
Clearer syntax error message from ScriptRunner
124
            # can happen if the first line is not recognized as a command, eg
125
            # if the prompt has leading whitespace
4665.5.20 by Vincent Ladeuil
Fixed as per Martin's review.
126
            if output is None:
127
                if cmd_cur is None:
4889.1.1 by Martin Pool
Clearer syntax error message from ScriptRunner
128
                    raise SyntaxError('No command for line %r' % (line,),
4665.5.20 by Vincent Ladeuil
Fixed as per Martin's review.
129
                                      (file_name, lineno, 1, orig))
130
                output = []
131
            output.append(line + '\n')
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
132
    # Add the last seen command
4665.5.7 by Vincent Ladeuil
Simplify output/errors handling.
133
    add_command(cmd_cur, input, output, error)
4665.5.1 by Vincent Ladeuil
Start some shell-like capability to write tests.
134
    return commands
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
135
136
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
137
def _scan_redirection_options(args):
138
    """Recognize and process input and output redirections.
139
140
    :param args: The command line arguments
141
142
    :return: A tuple containing: 
143
        - The file name redirected from or None
144
        - The file name redirected to or None
145
        - The mode to open the output file or None
146
        - The reamining arguments
147
    """
4665.5.18 by Vincent Ladeuil
Better redirection handling.
148
    def redirected_file_name(direction, name, args):
149
        if name == '':
150
            try:
151
                name = args.pop(0)
152
            except IndexError:
153
                # We leave the error handling to higher levels, an empty name
154
                # can't be legal.
155
                name = ''
156
        return name
157
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
158
    remaining = []
159
    in_name = None
160
    out_name, out_mode = None, None
4665.5.18 by Vincent Ladeuil
Better redirection handling.
161
    while args:
162
        arg = args.pop(0)
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
163
        if arg.startswith('<'):
4665.5.18 by Vincent Ladeuil
Better redirection handling.
164
            in_name = redirected_file_name('<', arg[1:], args)
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
165
        elif arg.startswith('>>'):
4665.5.18 by Vincent Ladeuil
Better redirection handling.
166
            out_name = redirected_file_name('>>', arg[2:], args)
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
167
            out_mode = 'ab+'
4665.5.18 by Vincent Ladeuil
Better redirection handling.
168
        elif arg.startswith('>',):
169
            out_name = redirected_file_name('>', arg[1:], args)
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
170
            out_mode = 'wb+'
171
        else:
172
            remaining.append(arg)
173
    return in_name, out_name, out_mode, remaining
174
175
4665.5.10 by Vincent Ladeuil
Start separating the script runner from the test case.
176
class ScriptRunner(object):
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
177
    """Run a shell-like script from a test.
178
    
179
    Can be used as:
180
181
    from bzrlib.tests import script
182
183
    ...
184
185
        def test_bug_nnnnn(self):
186
            sr = script.ScriptRunner()
187
            sr.run_script(self, '''
188
            $ bzr init
189
            $ bzr do-this
190
            # Boom, error
191
            ''')
192
    """
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
193
4665.5.23 by Vincent Ladeuil
Revert the jail_root parameter addition.
194
    def __init__(self):
4665.5.12 by Vincent Ladeuil
Support '...' in expected strings.
195
        self.output_checker = doctest.OutputChecker()
196
        self.check_options = doctest.ELLIPSIS
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
197
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
198
    def run_script(self, test_case, text):
199
        """Run a shell-like script as a test.
200
201
        :param test_case: A TestCase instance that should provide the fail(),
202
            assertEqualDiff and _run_bzr_core() methods as well as a 'test_dir'
203
            attribute used as a jail root.
204
205
        :param text: A shell-like script (see _script_to_commands for syntax).
206
        """
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
207
        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.
208
            self.run_command(test_case, cmd, input, output, error)
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
209
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
210
    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.
211
        mname = 'do_' + cmd[0]
212
        method = getattr(self, mname, None)
213
        if method is None:
214
            raise SyntaxError('Command not found "%s"' % (cmd[0],),
215
                              None, 1, ' '.join(cmd))
216
        if input is None:
217
            str_input = ''
218
        else:
219
            str_input = ''.join(input)
220
        args = list(self._pre_process_args(cmd[1:]))
221
        retcode, actual_output, actual_error = method(test_case,
222
                                                      str_input, args)
223
5422.3.1 by Martin Pool
Blank scriptrunner output only matches blank
224
        try:
225
            self._check_output(output, actual_output, test_case)
226
        except AssertionError, e:
227
            raise AssertionError(str(e) + " in stdout of command %s" % cmd)
228
        try:
229
            self._check_output(error, actual_error, test_case)
230
        except AssertionError, e:
231
            raise AssertionError(str(e) +
232
                " in stderr of running command %s" % cmd)
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
233
        if retcode and not error and actual_error:
234
            test_case.fail('In \n\t%s\nUnexpected error: %s'
235
                           % (' '.join(cmd), actual_error))
236
        return retcode, actual_output, actual_error
237
238
    def _check_output(self, expected, actual, test_case):
5422.3.1 by Martin Pool
Blank scriptrunner output only matches blank
239
        if not actual:
240
            if expected is None:
241
                return
242
            elif expected == '...\n':
243
                return
244
            else:
245
                test_case.fail('expected output: %r, but found nothing'
246
                            % (expected,))
247
        expected = expected or ''
4665.5.12 by Vincent Ladeuil
Support '...' in expected strings.
248
        matching = self.output_checker.check_output(
249
            expected, actual, self.check_options)
250
        if not matching:
251
            # Note that we can't use output_checker.output_difference() here
4665.5.15 by Vincent Ladeuil
Catch the retcode for all commands.
252
            # because... the API is broken ('expected' must be a doctest
253
            # specific object of which a 'want' attribute will be our
254
            # 'expected' parameter. So we just fallback to our good old
255
            # assertEqualDiff since we know there *are* differences and the
256
            # output should be decently readable.
5417.1.1 by Martin Pool
ScriptRunner can now cope with commands that prompt for input.
257
            #
258
            # As a special case, we allow output that's missing a final
259
            # newline to match an expected string that does have one, so that
260
            # we can match a prompt printed on one line, then input given on
261
            # the next line.
262
            if expected == actual + '\n':
263
                pass
264
            else:
5422.3.5 by Martin Pool
Go back to using assertEqualDiff when a script fails
265
                test_case.assertEqualDiff(expected, actual)
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
266
4665.5.19 by Vincent Ladeuil
Implement globbing and enhance cat to accept multiple files.
267
    def _pre_process_args(self, args):
268
        new_args = []
269
        for arg in args:
270
            # Strip the simple and double quotes since we don't care about
271
            # them.  We leave the backquotes in place though since they have a
272
            # different semantic.
273
            if arg[0] in  ('"', "'") and arg[0] == arg[-1]:
274
                yield arg[1:-1]
275
            else:
276
                if glob.has_magic(arg):
277
                    matches = glob.glob(arg)
278
                    if matches:
279
                        # We care more about order stability than performance
280
                        # here
281
                        matches.sort()
282
                        for m in matches:
283
                            yield m
284
                else:
285
                    yield arg
286
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
287
    def _read_input(self, input, in_name):
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
288
        if in_name is not None:
289
            infile = open(in_name, 'rb')
290
            try:
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
291
                # Command redirection takes precedence over provided input
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
292
                input = infile.read()
293
            finally:
294
                infile.close()
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
295
        return input
296
297
    def _write_output(self, output, out_name, out_mode):
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
298
        if out_name is not None:
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
299
            outfile = open(out_name, out_mode)
4665.5.4 by Vincent Ladeuil
Implement a 'cat' command.
300
            try:
301
                outfile.write(output)
302
            finally:
303
                outfile.close()
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
304
            output = None
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
305
        return output
306
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
307
    def do_bzr(self, test_case, input, args):
308
        retcode, out, err = test_case._run_bzr_core(
4665.5.10 by Vincent Ladeuil
Start separating the script runner from the test case.
309
            args, retcode=None, encoding=None, stdin=input, working_dir=None)
4665.5.15 by Vincent Ladeuil
Catch the retcode for all commands.
310
        return retcode, out, err
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
311
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
312
    def do_cat(self, test_case, input, args):
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
313
        (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.
314
        if args and in_name is not None:
315
            raise SyntaxError('Specify a file OR use redirection')
316
317
        inputs = []
318
        if input:
319
            inputs.append(input)
320
        input_names = args
321
        if in_name:
322
            args.append(in_name)
323
        for in_name in input_names:
324
            try:
325
                inputs.append(self._read_input(None, in_name))
326
            except IOError, e:
4789.18.1 by John Arbash Meinel
Rework test_script a little bit.
327
                # Some filenames are illegal on Windows and generate EINVAL
328
                # rather than just saying the filename doesn't exist
329
                if e.errno in (errno.ENOENT, errno.EINVAL):
4665.5.19 by Vincent Ladeuil
Implement globbing and enhance cat to accept multiple files.
330
                    return (1, None,
331
                            '%s: No such file or directory\n' % (in_name,))
4789.18.1 by John Arbash Meinel
Rework test_script a little bit.
332
                raise
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
333
        # Basically cat copy input to output
4665.5.19 by Vincent Ladeuil
Implement globbing and enhance cat to accept multiple files.
334
        output = ''.join(inputs)
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
335
        # Handle output redirections
4665.5.18 by Vincent Ladeuil
Better redirection handling.
336
        try:
337
            output = self._write_output(output, out_name, out_mode)
338
        except IOError, e:
4789.18.1 by John Arbash Meinel
Rework test_script a little bit.
339
            # If out_name cannot be created, we may get 'ENOENT', however if
340
            # out_name is something like '', we can get EINVAL
341
            if e.errno in (errno.ENOENT, errno.EINVAL):
4665.5.18 by Vincent Ladeuil
Better redirection handling.
342
                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.
343
            raise
4665.5.15 by Vincent Ladeuil
Catch the retcode for all commands.
344
        return 0, output, None
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
345
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
346
    def do_echo(self, test_case, input, args):
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
347
        (in_name, out_name, out_mode, args) = _scan_redirection_options(args)
4789.18.1 by John Arbash Meinel
Rework test_script a little bit.
348
        if input or in_name:
349
            raise SyntaxError('echo doesn\'t read from stdin')
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
350
        if args:
4665.5.20 by Vincent Ladeuil
Fixed as per Martin's review.
351
            input = ' '.join(args)
4665.5.8 by Vincent Ladeuil
Implement 'echo' command.
352
        # Always append a \n'
353
        input += '\n'
354
        # Process output
355
        output = input
356
        # Handle output redirections
4665.5.18 by Vincent Ladeuil
Better redirection handling.
357
        try:
358
            output = self._write_output(output, out_name, out_mode)
359
        except IOError, e:
4789.18.1 by John Arbash Meinel
Rework test_script a little bit.
360
            if e.errno in (errno.ENOENT, errno.EINVAL):
4665.5.18 by Vincent Ladeuil
Better redirection handling.
361
                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.
362
            raise
4665.5.15 by Vincent Ladeuil
Catch the retcode for all commands.
363
        return 0, output, None
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
364
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
365
    def _get_jail_root(self, test_case):
366
        return test_case.test_dir
367
4665.5.23 by Vincent Ladeuil
Revert the jail_root parameter addition.
368
    def _ensure_in_jail(self, test_case, path):
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
369
        jail_root = self._get_jail_root(test_case)
4665.5.23 by Vincent Ladeuil
Revert the jail_root parameter addition.
370
        if not osutils.is_inside(jail_root, osutils.normalizepath(path)):
371
            raise ValueError('%s is not inside %s' % (path, jail_root))
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
372
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
373
    def do_cd(self, test_case, input, args):
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
374
        if len(args) > 1:
375
            raise SyntaxError('Usage: cd [dir]')
376
        if len(args) == 1:
377
            d = args[0]
4665.5.23 by Vincent Ladeuil
Revert the jail_root parameter addition.
378
            self._ensure_in_jail(test_case, d)
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
379
        else:
4665.5.23 by Vincent Ladeuil
Revert the jail_root parameter addition.
380
            # 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.
381
            d = self._get_jail_root(test_case)
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
382
        os.chdir(d)
4665.5.15 by Vincent Ladeuil
Catch the retcode for all commands.
383
        return 0, None, None
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
384
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
385
    def do_mkdir(self, test_case, input, args):
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
386
        if not args or len(args) != 1:
387
            raise SyntaxError('Usage: mkdir dir')
388
        d = args[0]
4665.5.23 by Vincent Ladeuil
Revert the jail_root parameter addition.
389
        self._ensure_in_jail(test_case, d)
4665.5.5 by Vincent Ladeuil
Implement 'cd' and 'mkdir'.
390
        os.mkdir(d)
4665.5.15 by Vincent Ladeuil
Catch the retcode for all commands.
391
        return 0, None, None
4665.5.6 by Vincent Ladeuil
Implement 'bzr' command.
392
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
393
    def do_rm(self, test_case, input, args):
4665.5.17 by Vincent Ladeuil
Implement 'rm' command.
394
        err = None
395
396
        def error(msg, path):
397
            return  "rm: cannot remove '%s': %s\n" % (path, msg)
398
399
        force, recursive = False, False
400
        opts = None
401
        if args and args[0][0] == '-':
402
            opts = args.pop(0)[1:]
403
            if 'f' in opts:
404
                force = True
405
                opts = opts.replace('f', '', 1)
406
            if 'r' in opts:
407
                recursive = True
408
                opts = opts.replace('r', '', 1)
409
        if not args or opts:
410
            raise SyntaxError('Usage: rm [-fr] path+')
411
        for p in args:
4665.5.23 by Vincent Ladeuil
Revert the jail_root parameter addition.
412
            self._ensure_in_jail(test_case, p)
4665.5.17 by Vincent Ladeuil
Implement 'rm' command.
413
            # FIXME: Should we put that in osutils ?
414
            try:
415
                os.remove(p)
416
            except OSError, e:
4707.1.1 by Vincent Ladeuil
Fix OSX and FreeBSD failures.
417
                # Various OSes raises different exceptions (linux: EISDIR,
418
                #   win32: EACCES, OSX: EPERM) when invoked on a directory
419
                if e.errno in (errno.EISDIR, errno.EPERM, errno.EACCES):
4665.5.17 by Vincent Ladeuil
Implement 'rm' command.
420
                    if recursive:
421
                        osutils.rmtree(p)
422
                    else:
423
                        err = error('Is a directory', p)
424
                        break
425
                elif e.errno == errno.ENOENT:
426
                    if not force:
427
                        err =  error('No such file or directory', p)
428
                        break
429
                else:
430
                    raise
431
        if err:
432
            retcode = 1
433
        else:
434
            retcode = 0
435
        return retcode, None, err
436
4953.2.1 by Neil Martinsen-Burrell
add an mv command to shell-like tests
437
    def do_mv(self, test_case, input, args):
438
        err = None
4953.2.3 by Vincent Ladeuil
Use os.rename() and fix some typos.
439
        def error(msg, src, dst):
440
            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
441
442
        if not args or len(args) != 2:
443
            raise SyntaxError("Usage: mv path1 path2")
4953.2.3 by Vincent Ladeuil
Use os.rename() and fix some typos.
444
        src, dst = args
4953.2.1 by Neil Martinsen-Burrell
add an mv command to shell-like tests
445
        try:
4953.2.3 by Vincent Ladeuil
Use os.rename() and fix some typos.
446
            real_dst = dst
447
            if os.path.isdir(dst):
448
                real_dst = os.path.join(dst, os.path.basename(src))
449
            os.rename(src, real_dst)
4953.2.1 by Neil Martinsen-Burrell
add an mv command to shell-like tests
450
        except OSError, e:
451
            if e.errno == errno.ENOENT:
4953.2.3 by Vincent Ladeuil
Use os.rename() and fix some typos.
452
                err = error('No such file or directory', src, dst)
4953.2.1 by Neil Martinsen-Burrell
add an mv command to shell-like tests
453
            else:
454
                raise
455
        if err:
456
            retcode = 1
457
        else:
458
            retcode = 0
459
        return retcode, None, err
460
461
4665.5.10 by Vincent Ladeuil
Start separating the script runner from the test case.
462
4665.5.11 by Vincent Ladeuil
Create a new test case based on TestCaseWithMemoryTransport.
463
class TestCaseWithMemoryTransportAndScript(tests.TestCaseWithMemoryTransport):
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
464
    """Helper class to experiment shell-like test and memory fs.
465
466
    This not intended to be used outside of experiments in implementing memoy
467
    based file systems and evolving bzr so that test can use only memory based
468
    resources.
469
    """
4665.5.11 by Vincent Ladeuil
Create a new test case based on TestCaseWithMemoryTransport.
470
471
    def setUp(self):
472
        super(TestCaseWithMemoryTransportAndScript, self).setUp()
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
473
        self.script_runner = ScriptRunner()
4665.5.11 by Vincent Ladeuil
Create a new test case based on TestCaseWithMemoryTransport.
474
475
    def run_script(self, script):
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
476
        return self.script_runner.run_script(self, script)
4665.5.11 by Vincent Ladeuil
Create a new test case based on TestCaseWithMemoryTransport.
477
478
    def run_command(self, cmd, input, output, error):
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
479
        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.
480
481
4665.5.10 by Vincent Ladeuil
Start separating the script runner from the test case.
482
class TestCaseWithTransportAndScript(tests.TestCaseWithTransport):
4665.5.22 by Vincent Ladeuil
Try to relax the test_case/script runner coupling.
483
    """Helper class to quickly define shell-like tests.
484
485
    Can be used as:
486
487
    from bzrlib.tests import script
488
489
490
    class TestBug(script.TestCaseWithTransportAndScript):
491
492
        def test_bug_nnnnn(self):
493
            self.run_script('''
494
            $ bzr init
495
            $ bzr do-this
496
            # Boom, error
497
            ''')
498
    """
4665.5.10 by Vincent Ladeuil
Start separating the script runner from the test case.
499
500
    def setUp(self):
501
        super(TestCaseWithTransportAndScript, self).setUp()
4665.5.23 by Vincent Ladeuil
Revert the jail_root parameter addition.
502
        self.script_runner = ScriptRunner()
503
4665.5.10 by Vincent Ladeuil
Start separating the script runner from the test case.
504
    def run_script(self, script):
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
505
        return self.script_runner.run_script(self, script)
4665.5.10 by Vincent Ladeuil
Start separating the script runner from the test case.
506
507
    def run_command(self, cmd, input, output, error):
4665.5.24 by Vincent Ladeuil
Scripts can be used without imposing a test base class.
508
        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.
509
5283.1.1 by Martin Pool
Add helper function script.run_script and suggest using it
510
511
def run_script(test_case, script_string):
512
    """Run the given script within a testcase"""
513
    return ScriptRunner().run_script(test_case, script_string)