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