~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/script.py

  • Committer: Vincent Ladeuil
  • Date: 2009-09-24 06:53:43 UTC
  • mfrom: (4665.5.24 shell-like-tests)
  • mto: This revision was merged to the branch mainline in revision 4714.
  • Revision ID: v.ladeuil+lp@free.fr-20090924065343-nszjtbyg3x5swu0k
Last tweaks and NEWS enrty for shell-like tests

Show diffs side-by-side

added added

removed removed

Lines of Context:
158
158
 
159
159
 
160
160
class ScriptRunner(object):
161
 
 
162
 
    def __init__(self, test_case):
163
 
        self.test_case = test_case
 
161
    """Run a shell-like script from a test.
 
162
    
 
163
    Can be used as:
 
164
 
 
165
    from bzrlib.tests import script
 
166
 
 
167
    ...
 
168
 
 
169
        def test_bug_nnnnn(self):
 
170
            sr = script.ScriptRunner()
 
171
            sr.run_script(self, '''
 
172
            $ bzr init
 
173
            $ bzr do-this
 
174
            # Boom, error
 
175
            ''')
 
176
    """
 
177
 
 
178
    def __init__(self):
164
179
        self.output_checker = doctest.OutputChecker()
165
180
        self.check_options = doctest.ELLIPSIS
166
181
 
167
 
    def run_script(self, text):
 
182
    def run_script(self, test_case, text):
 
183
        """Run a shell-like script as a test.
 
184
 
 
185
        :param test_case: A TestCase instance that should provide the fail(),
 
186
            assertEqualDiff and _run_bzr_core() methods as well as a 'test_dir'
 
187
            attribute used as a jail root.
 
188
 
 
189
        :param text: A shell-like script (see _script_to_commands for syntax).
 
190
        """
168
191
        for cmd, input, output, error in _script_to_commands(text):
169
 
            self.run_command(cmd, input, output, error)
170
 
 
171
 
    def _check_output(self, expected, actual):
 
192
            self.run_command(test_case, cmd, input, output, error)
 
193
 
 
194
    def run_command(self, test_case, cmd, input, output, error):
 
195
        mname = 'do_' + cmd[0]
 
196
        method = getattr(self, mname, None)
 
197
        if method is None:
 
198
            raise SyntaxError('Command not found "%s"' % (cmd[0],),
 
199
                              None, 1, ' '.join(cmd))
 
200
        if input is None:
 
201
            str_input = ''
 
202
        else:
 
203
            str_input = ''.join(input)
 
204
        args = list(self._pre_process_args(cmd[1:]))
 
205
        retcode, actual_output, actual_error = method(test_case,
 
206
                                                      str_input, args)
 
207
 
 
208
        self._check_output(output, actual_output, test_case)
 
209
        self._check_output(error, actual_error, test_case)
 
210
        if retcode and not error and actual_error:
 
211
            test_case.fail('In \n\t%s\nUnexpected error: %s'
 
212
                           % (' '.join(cmd), actual_error))
 
213
        return retcode, actual_output, actual_error
 
214
 
 
215
    def _check_output(self, expected, actual, test_case):
172
216
        if expected is None:
173
217
            # Specifying None means: any output is accepted
174
218
            return
175
219
        if actual is None:
176
 
            self.test_case.fail('Unexpected: %s' % actual)
 
220
            test_case.fail('Unexpected: %s' % actual)
177
221
        matching = self.output_checker.check_output(
178
222
            expected, actual, self.check_options)
179
223
        if not matching:
183
227
            # 'expected' parameter. So we just fallback to our good old
184
228
            # assertEqualDiff since we know there *are* differences and the
185
229
            # output should be decently readable.
186
 
            self.test_case.assertEqualDiff(expected, actual)
 
230
            test_case.assertEqualDiff(expected, actual)
187
231
 
188
232
    def _pre_process_args(self, args):
189
233
        new_args = []
205
249
                else:
206
250
                    yield arg
207
251
 
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
 
 
228
252
    def _read_input(self, input, in_name):
229
253
        if in_name is not None:
230
254
            infile = open(in_name, 'rb')
245
269
            output = None
246
270
        return output
247
271
 
248
 
    def do_bzr(self, input, args):
249
 
        retcode, out, err = self.test_case._run_bzr_core(
 
272
    def do_bzr(self, test_case, input, args):
 
273
        retcode, out, err = test_case._run_bzr_core(
250
274
            args, retcode=None, encoding=None, stdin=input, working_dir=None)
251
275
        return retcode, out, err
252
276
 
253
 
    def do_cat(self, input, args):
 
277
    def do_cat(self, test_case, input, args):
254
278
        (in_name, out_name, out_mode, args) = _scan_redirection_options(args)
255
279
        if args and in_name is not None:
256
280
            raise SyntaxError('Specify a file OR use redirection')
278
302
                return 1, None, '%s: No such file or directory\n' % (out_name,)
279
303
        return 0, output, None
280
304
 
281
 
    def do_echo(self, input, args):
 
305
    def do_echo(self, test_case, input, args):
282
306
        (in_name, out_name, out_mode, args) = _scan_redirection_options(args)
283
307
        if input and args:
284
308
                raise SyntaxError('Specify parameters OR use redirection')
301
325
                return 1, None, '%s: No such file or directory\n' % (out_name,)
302
326
        return 0, output, None
303
327
 
304
 
    def _ensure_in_jail(self, path):
305
 
        jail_root = self.test_case.get_jail_root()
 
328
    def _get_jail_root(self, test_case):
 
329
        return test_case.test_dir
 
330
 
 
331
    def _ensure_in_jail(self, test_case, path):
 
332
        jail_root = self._get_jail_root(test_case)
306
333
        if not osutils.is_inside(jail_root, osutils.normalizepath(path)):
307
334
            raise ValueError('%s is not inside %s' % (path, jail_root))
308
335
 
309
 
    def do_cd(self, input, args):
 
336
    def do_cd(self, test_case, input, args):
310
337
        if len(args) > 1:
311
338
            raise SyntaxError('Usage: cd [dir]')
312
339
        if len(args) == 1:
313
340
            d = args[0]
314
 
            self._ensure_in_jail(d)
 
341
            self._ensure_in_jail(test_case, d)
315
342
        else:
316
 
            d = self.test_case.get_jail_root()
 
343
            # The test "home" directory is the root of its jail
 
344
            d = self._get_jail_root(test_case)
317
345
        os.chdir(d)
318
346
        return 0, None, None
319
347
 
320
 
    def do_mkdir(self, input, args):
 
348
    def do_mkdir(self, test_case, input, args):
321
349
        if not args or len(args) != 1:
322
350
            raise SyntaxError('Usage: mkdir dir')
323
351
        d = args[0]
324
 
        self._ensure_in_jail(d)
 
352
        self._ensure_in_jail(test_case, d)
325
353
        os.mkdir(d)
326
354
        return 0, None, None
327
355
 
328
 
    def do_rm(self, input, args):
 
356
    def do_rm(self, test_case, input, args):
329
357
        err = None
330
358
 
331
359
        def error(msg, path):
344
372
        if not args or opts:
345
373
            raise SyntaxError('Usage: rm [-fr] path+')
346
374
        for p in args:
347
 
            self._ensure_in_jail(p)
 
375
            self._ensure_in_jail(test_case, p)
348
376
            # FIXME: Should we put that in osutils ?
349
377
            try:
350
378
                os.remove(p)
371
399
 
372
400
 
373
401
class TestCaseWithMemoryTransportAndScript(tests.TestCaseWithMemoryTransport):
 
402
    """Helper class to experiment shell-like test and memory fs.
 
403
 
 
404
    This not intended to be used outside of experiments in implementing memoy
 
405
    based file systems and evolving bzr so that test can use only memory based
 
406
    resources.
 
407
    """
374
408
 
375
409
    def setUp(self):
376
410
        super(TestCaseWithMemoryTransportAndScript, self).setUp()
377
 
        self.script_runner = ScriptRunner(self)
378
 
        # Break the circular dependency
379
 
        def break_dependency():
380
 
            self.script_runner = None
381
 
        self.addCleanup(break_dependency)
382
 
 
383
 
    def get_jail_root(self):
384
 
        raise NotImplementedError(self.get_jail_root)
 
411
        self.script_runner = ScriptRunner()
385
412
 
386
413
    def run_script(self, script):
387
 
        return self.script_runner.run_script(script)
 
414
        return self.script_runner.run_script(self, script)
388
415
 
389
416
    def run_command(self, cmd, input, output, error):
390
 
        return self.script_runner.run_command(cmd, input, output, error)
 
417
        return self.script_runner.run_command(self, cmd, input, output, error)
391
418
 
392
419
 
393
420
class TestCaseWithTransportAndScript(tests.TestCaseWithTransport):
 
421
    """Helper class to quickly define shell-like tests.
 
422
 
 
423
    Can be used as:
 
424
 
 
425
    from bzrlib.tests import script
 
426
 
 
427
 
 
428
    class TestBug(script.TestCaseWithTransportAndScript):
 
429
 
 
430
        def test_bug_nnnnn(self):
 
431
            self.run_script('''
 
432
            $ bzr init
 
433
            $ bzr do-this
 
434
            # Boom, error
 
435
            ''')
 
436
    """
394
437
 
395
438
    def setUp(self):
396
439
        super(TestCaseWithTransportAndScript, self).setUp()
397
 
        self.script_runner = ScriptRunner(self)
398
 
        # Break the circular dependency
399
 
        def break_dependency():
400
 
            self.script_runner = None
401
 
        self.addCleanup(break_dependency)
402
 
 
403
 
    def get_jail_root(self):
404
 
        return self.test_dir
 
440
        self.script_runner = ScriptRunner()
405
441
 
406
442
    def run_script(self, script):
407
 
        return self.script_runner.run_script(script)
 
443
        return self.script_runner.run_script(self, script)
408
444
 
409
445
    def run_command(self, cmd, input, output, error):
410
 
        return self.script_runner.run_command(cmd, input, output, error)
 
446
        return self.script_runner.run_command(self, cmd, input, output, error)
 
447