~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/script.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-09-19 00:32:14 UTC
  • mfrom: (4685.2.1 bzr.dev)
  • Revision ID: pqm@pqm.ubuntu.com-20090919003214-2dli9jc4y5xhjj3n
(mbp for garyvdm) Revert rename of
        test_merge_uncommitted_otherbasis_ancestor_of_thisbasis.

Show diffs side-by-side

added added

removed removed

Lines of Context:
107
107
                error = []
108
108
            error.append(line[2:] + '\n')
109
109
        else:
110
 
            # can happen if the first line is not recognized as a command, eg
111
 
            # if the prompt has leading whitespace
112
110
            if output is None:
113
111
                if cmd_cur is None:
114
 
                    raise SyntaxError('No command for line %r' % (line,),
 
112
                    raise SyntaxError('No command for that output',
115
113
                                      (file_name, lineno, 1, orig))
116
114
                output = []
117
115
            output.append(line + '\n')
160
158
 
161
159
 
162
160
class ScriptRunner(object):
163
 
    """Run a shell-like script from a test.
164
 
    
165
 
    Can be used as:
166
 
 
167
 
    from bzrlib.tests import script
168
 
 
169
 
    ...
170
 
 
171
 
        def test_bug_nnnnn(self):
172
 
            sr = script.ScriptRunner()
173
 
            sr.run_script(self, '''
174
 
            $ bzr init
175
 
            $ bzr do-this
176
 
            # Boom, error
177
 
            ''')
178
 
    """
179
 
 
180
 
    def __init__(self):
 
161
 
 
162
    def __init__(self, test_case):
 
163
        self.test_case = test_case
181
164
        self.output_checker = doctest.OutputChecker()
182
165
        self.check_options = doctest.ELLIPSIS
183
166
 
184
 
    def run_script(self, test_case, text):
185
 
        """Run a shell-like script as a test.
186
 
 
187
 
        :param test_case: A TestCase instance that should provide the fail(),
188
 
            assertEqualDiff and _run_bzr_core() methods as well as a 'test_dir'
189
 
            attribute used as a jail root.
190
 
 
191
 
        :param text: A shell-like script (see _script_to_commands for syntax).
192
 
        """
 
167
    def run_script(self, text):
193
168
        for cmd, input, output, error in _script_to_commands(text):
194
 
            self.run_command(test_case, cmd, input, output, error)
195
 
 
196
 
    def run_command(self, test_case, cmd, input, output, error):
197
 
        mname = 'do_' + cmd[0]
198
 
        method = getattr(self, mname, None)
199
 
        if method is None:
200
 
            raise SyntaxError('Command not found "%s"' % (cmd[0],),
201
 
                              None, 1, ' '.join(cmd))
202
 
        if input is None:
203
 
            str_input = ''
204
 
        else:
205
 
            str_input = ''.join(input)
206
 
        args = list(self._pre_process_args(cmd[1:]))
207
 
        retcode, actual_output, actual_error = method(test_case,
208
 
                                                      str_input, args)
209
 
 
210
 
        self._check_output(output, actual_output, test_case)
211
 
        self._check_output(error, actual_error, test_case)
212
 
        if retcode and not error and actual_error:
213
 
            test_case.fail('In \n\t%s\nUnexpected error: %s'
214
 
                           % (' '.join(cmd), actual_error))
215
 
        return retcode, actual_output, actual_error
216
 
 
217
 
    def _check_output(self, expected, actual, test_case):
 
169
            self.run_command(cmd, input, output, error)
 
170
 
 
171
    def _check_output(self, expected, actual):
218
172
        if expected is None:
219
173
            # Specifying None means: any output is accepted
220
174
            return
221
175
        if actual is None:
222
 
            test_case.fail('We expected output: %r, but found None'
223
 
                           % (expected,))
 
176
            self.test_case.fail('Unexpected: %s' % actual)
224
177
        matching = self.output_checker.check_output(
225
178
            expected, actual, self.check_options)
226
179
        if not matching:
230
183
            # 'expected' parameter. So we just fallback to our good old
231
184
            # assertEqualDiff since we know there *are* differences and the
232
185
            # output should be decently readable.
233
 
            test_case.assertEqualDiff(expected, actual)
 
186
            self.test_case.assertEqualDiff(expected, actual)
234
187
 
235
188
    def _pre_process_args(self, args):
236
189
        new_args = []
252
205
                else:
253
206
                    yield arg
254
207
 
 
208
    def run_command(self, cmd, input, output, error):
 
209
        mname = 'do_' + cmd[0]
 
210
        method = getattr(self, mname, None)
 
211
        if method is None:
 
212
            raise SyntaxError('Command not found "%s"' % (cmd[0],),
 
213
                              None, 1, ' '.join(cmd))
 
214
        if input is None:
 
215
            str_input = ''
 
216
        else:
 
217
            str_input = ''.join(input)
 
218
        args = list(self._pre_process_args(cmd[1:]))
 
219
        retcode, actual_output, actual_error = method(str_input, args)
 
220
 
 
221
        self._check_output(output, actual_output)
 
222
        self._check_output(error, actual_error)
 
223
        if retcode and not error and actual_error:
 
224
            self.test_case.fail('In \n\t%s\nUnexpected error: %s'
 
225
                                % (' '.join(cmd), actual_error))
 
226
        return retcode, actual_output, actual_error
 
227
 
255
228
    def _read_input(self, input, in_name):
256
229
        if in_name is not None:
257
230
            infile = open(in_name, 'rb')
272
245
            output = None
273
246
        return output
274
247
 
275
 
    def do_bzr(self, test_case, input, args):
276
 
        retcode, out, err = test_case._run_bzr_core(
 
248
    def do_bzr(self, input, args):
 
249
        retcode, out, err = self.test_case._run_bzr_core(
277
250
            args, retcode=None, encoding=None, stdin=input, working_dir=None)
278
251
        return retcode, out, err
279
252
 
280
 
    def do_cat(self, test_case, input, args):
 
253
    def do_cat(self, input, args):
281
254
        (in_name, out_name, out_mode, args) = _scan_redirection_options(args)
282
255
        if args and in_name is not None:
283
256
            raise SyntaxError('Specify a file OR use redirection')
292
265
            try:
293
266
                inputs.append(self._read_input(None, in_name))
294
267
            except IOError, e:
295
 
                # Some filenames are illegal on Windows and generate EINVAL
296
 
                # rather than just saying the filename doesn't exist
297
 
                if e.errno in (errno.ENOENT, errno.EINVAL):
 
268
                if e.errno == errno.ENOENT:
298
269
                    return (1, None,
299
270
                            '%s: No such file or directory\n' % (in_name,))
300
 
                raise
301
271
        # Basically cat copy input to output
302
272
        output = ''.join(inputs)
303
273
        # Handle output redirections
304
274
        try:
305
275
            output = self._write_output(output, out_name, out_mode)
306
276
        except IOError, e:
307
 
            # If out_name cannot be created, we may get 'ENOENT', however if
308
 
            # out_name is something like '', we can get EINVAL
309
 
            if e.errno in (errno.ENOENT, errno.EINVAL):
 
277
            if e.errno == errno.ENOENT:
310
278
                return 1, None, '%s: No such file or directory\n' % (out_name,)
311
 
            raise
312
279
        return 0, output, None
313
280
 
314
 
    def do_echo(self, test_case, input, args):
 
281
    def do_echo(self, input, args):
315
282
        (in_name, out_name, out_mode, args) = _scan_redirection_options(args)
316
 
        if input or in_name:
317
 
            raise SyntaxError('echo doesn\'t read from stdin')
 
283
        if input and args:
 
284
                raise SyntaxError('Specify parameters OR use redirection')
318
285
        if args:
319
286
            input = ' '.join(args)
 
287
        try:
 
288
            input = self._read_input(input, in_name)
 
289
        except IOError, e:
 
290
            if e.errno == errno.ENOENT:
 
291
                return 1, None, '%s: No such file or directory\n' % (in_name,)
320
292
        # Always append a \n'
321
293
        input += '\n'
322
294
        # Process output
325
297
        try:
326
298
            output = self._write_output(output, out_name, out_mode)
327
299
        except IOError, e:
328
 
            if e.errno in (errno.ENOENT, errno.EINVAL):
 
300
            if e.errno == errno.ENOENT:
329
301
                return 1, None, '%s: No such file or directory\n' % (out_name,)
330
 
            raise
331
302
        return 0, output, None
332
303
 
333
 
    def _get_jail_root(self, test_case):
334
 
        return test_case.test_dir
335
 
 
336
 
    def _ensure_in_jail(self, test_case, path):
337
 
        jail_root = self._get_jail_root(test_case)
 
304
    def _ensure_in_jail(self, path):
 
305
        jail_root = self.test_case.get_jail_root()
338
306
        if not osutils.is_inside(jail_root, osutils.normalizepath(path)):
339
307
            raise ValueError('%s is not inside %s' % (path, jail_root))
340
308
 
341
 
    def do_cd(self, test_case, input, args):
 
309
    def do_cd(self, input, args):
342
310
        if len(args) > 1:
343
311
            raise SyntaxError('Usage: cd [dir]')
344
312
        if len(args) == 1:
345
313
            d = args[0]
346
 
            self._ensure_in_jail(test_case, d)
 
314
            self._ensure_in_jail(d)
347
315
        else:
348
 
            # The test "home" directory is the root of its jail
349
 
            d = self._get_jail_root(test_case)
 
316
            d = self.test_case.get_jail_root()
350
317
        os.chdir(d)
351
318
        return 0, None, None
352
319
 
353
 
    def do_mkdir(self, test_case, input, args):
 
320
    def do_mkdir(self, input, args):
354
321
        if not args or len(args) != 1:
355
322
            raise SyntaxError('Usage: mkdir dir')
356
323
        d = args[0]
357
 
        self._ensure_in_jail(test_case, d)
 
324
        self._ensure_in_jail(d)
358
325
        os.mkdir(d)
359
326
        return 0, None, None
360
327
 
361
 
    def do_rm(self, test_case, input, args):
 
328
    def do_rm(self, input, args):
362
329
        err = None
363
330
 
364
331
        def error(msg, path):
377
344
        if not args or opts:
378
345
            raise SyntaxError('Usage: rm [-fr] path+')
379
346
        for p in args:
380
 
            self._ensure_in_jail(test_case, p)
 
347
            self._ensure_in_jail(p)
381
348
            # FIXME: Should we put that in osutils ?
382
349
            try:
383
350
                os.remove(p)
384
351
            except OSError, e:
385
 
                # Various OSes raises different exceptions (linux: EISDIR,
386
 
                #   win32: EACCES, OSX: EPERM) when invoked on a directory
387
 
                if e.errno in (errno.EISDIR, errno.EPERM, errno.EACCES):
 
352
                if e.errno == errno.EISDIR:
388
353
                    if recursive:
389
354
                        osutils.rmtree(p)
390
355
                    else:
402
367
            retcode = 0
403
368
        return retcode, None, err
404
369
 
405
 
    def do_mv(self, test_case, input, args):
406
 
        err = None
407
 
        def error(msg, src, dst):
408
 
            return "mv: cannot move %s to %s: %s\n" % (src, dst, msg)
409
 
 
410
 
        if not args or len(args) != 2:
411
 
            raise SyntaxError("Usage: mv path1 path2")
412
 
        src, dst = args
413
 
        try:
414
 
            real_dst = dst
415
 
            if os.path.isdir(dst):
416
 
                real_dst = os.path.join(dst, os.path.basename(src))
417
 
            os.rename(src, real_dst)
418
 
        except OSError, e:
419
 
            if e.errno == errno.ENOENT:
420
 
                err = error('No such file or directory', src, dst)
421
 
            else:
422
 
                raise
423
 
        if err:
424
 
            retcode = 1
425
 
        else:
426
 
            retcode = 0
427
 
        return retcode, None, err
428
 
 
429
 
 
430
370
 
431
371
class TestCaseWithMemoryTransportAndScript(tests.TestCaseWithMemoryTransport):
432
 
    """Helper class to experiment shell-like test and memory fs.
433
 
 
434
 
    This not intended to be used outside of experiments in implementing memoy
435
 
    based file systems and evolving bzr so that test can use only memory based
436
 
    resources.
437
 
    """
438
372
 
439
373
    def setUp(self):
440
374
        super(TestCaseWithMemoryTransportAndScript, self).setUp()
441
 
        self.script_runner = ScriptRunner()
 
375
        self.script_runner = ScriptRunner(self)
 
376
        # Break the circular dependency
 
377
        def break_dependency():
 
378
            self.script_runner = None
 
379
        self.addCleanup(break_dependency)
 
380
 
 
381
    def get_jail_root(self):
 
382
        raise NotImplementedError(self.get_jail_root)
442
383
 
443
384
    def run_script(self, script):
444
 
        return self.script_runner.run_script(self, script)
 
385
        return self.script_runner.run_script(script)
445
386
 
446
387
    def run_command(self, cmd, input, output, error):
447
 
        return self.script_runner.run_command(self, cmd, input, output, error)
 
388
        return self.script_runner.run_command(cmd, input, output, error)
448
389
 
449
390
 
450
391
class TestCaseWithTransportAndScript(tests.TestCaseWithTransport):
451
 
    """Helper class to quickly define shell-like tests.
452
 
 
453
 
    Can be used as:
454
 
 
455
 
    from bzrlib.tests import script
456
 
 
457
 
 
458
 
    class TestBug(script.TestCaseWithTransportAndScript):
459
 
 
460
 
        def test_bug_nnnnn(self):
461
 
            self.run_script('''
462
 
            $ bzr init
463
 
            $ bzr do-this
464
 
            # Boom, error
465
 
            ''')
466
 
    """
467
392
 
468
393
    def setUp(self):
469
394
        super(TestCaseWithTransportAndScript, self).setUp()
470
 
        self.script_runner = ScriptRunner()
 
395
        self.script_runner = ScriptRunner(self)
 
396
        # Break the circular dependency
 
397
        def break_dependency():
 
398
            self.script_runner = None
 
399
        self.addCleanup(break_dependency)
 
400
 
 
401
    def get_jail_root(self):
 
402
        return self.test_dir
471
403
 
472
404
    def run_script(self, script):
473
 
        return self.script_runner.run_script(self, script)
 
405
        return self.script_runner.run_script(script)
474
406
 
475
407
    def run_command(self, cmd, input, output, error):
476
 
        return self.script_runner.run_command(self, cmd, input, output, error)
477
 
 
 
408
        return self.script_runner.run_command(cmd, input, output, error)