~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: John Arbash Meinel
  • Date: 2006-04-25 15:05:42 UTC
  • mfrom: (1185.85.85 bzr-encoding)
  • mto: This revision was merged to the branch mainline in revision 1752.
  • Revision ID: john@arbash-meinel.com-20060425150542-c7b518dca9928691
[merge] the old bzr-encoding changes, reparenting them on bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Canonical Ltd
 
1
# Copyright (C) 2005, 2006 by Canonical Ltd
2
2
 
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
 
18
# TODO: Perhaps there should be an API to find out if bzr running under the
 
19
# test suite -- some plugins might want to avoid making intrusive changes if
 
20
# this is the case.  However, we want behaviour under to test to diverge as
 
21
# little as possible, so this should be used rarely if it's added at all.
 
22
# (Suggestion from j-a-meinel, 2005-11-24)
 
23
 
 
24
# NOTE: Some classes in here use camelCaseNaming() rather than
 
25
# underscore_naming().  That's for consistency with unittest; it's not the
 
26
# general style of bzrlib.  Please continue that consistency when adding e.g.
 
27
# new assertFoo() methods.
 
28
 
 
29
import codecs
 
30
from cStringIO import StringIO
 
31
import difflib
 
32
import errno
18
33
import logging
19
 
import unittest
20
 
import tempfile
21
34
import os
 
35
import re
 
36
import shutil
 
37
import stat
22
38
import sys
23
 
 
24
 
from testsweet import run_suite
 
39
import tempfile
 
40
import unittest
 
41
import time
 
42
 
 
43
 
 
44
import bzrlib.branch
 
45
import bzrlib.bzrdir as bzrdir
25
46
import bzrlib.commands
26
 
 
 
47
import bzrlib.errors as errors
 
48
import bzrlib.inventory
 
49
import bzrlib.iterablefile
 
50
import bzrlib.lockdir
 
51
from bzrlib.merge import merge_inner
 
52
import bzrlib.merge3
 
53
import bzrlib.osutils
 
54
import bzrlib.osutils as osutils
 
55
import bzrlib.plugin
 
56
from bzrlib.revision import common_ancestor
 
57
import bzrlib.store
27
58
import bzrlib.trace
28
 
import bzrlib.fetch
 
59
from bzrlib.transport import urlescape, get_transport
 
60
import bzrlib.transport
 
61
from bzrlib.transport.local import LocalRelpathServer
 
62
from bzrlib.transport.readonly import ReadonlyServer
 
63
from bzrlib.trace import mutter
 
64
from bzrlib.tests.TestUtil import TestLoader, TestSuite
 
65
from bzrlib.tests.treeshape import build_tree_contents
 
66
from bzrlib.workingtree import WorkingTree, WorkingTreeFormat2
 
67
 
 
68
default_transport = LocalRelpathServer
29
69
 
30
70
MODULES_TO_TEST = []
31
 
MODULES_TO_DOCTEST = []
32
 
 
33
 
from logging import debug, warning, error
 
71
MODULES_TO_DOCTEST = [
 
72
                      bzrlib.branch,
 
73
                      bzrlib.commands,
 
74
                      bzrlib.errors,
 
75
                      bzrlib.inventory,
 
76
                      bzrlib.iterablefile,
 
77
                      bzrlib.lockdir,
 
78
                      bzrlib.merge3,
 
79
                      bzrlib.option,
 
80
                      bzrlib.osutils,
 
81
                      bzrlib.store
 
82
                      ]
 
83
def packages_to_test():
 
84
    """Return a list of packages to test.
 
85
 
 
86
    The packages are not globally imported so that import failures are
 
87
    triggered when running selftest, not when importing the command.
 
88
    """
 
89
    import bzrlib.doc
 
90
    import bzrlib.tests.blackbox
 
91
    import bzrlib.tests.branch_implementations
 
92
    import bzrlib.tests.bzrdir_implementations
 
93
    import bzrlib.tests.interrepository_implementations
 
94
    import bzrlib.tests.interversionedfile_implementations
 
95
    import bzrlib.tests.repository_implementations
 
96
    import bzrlib.tests.revisionstore_implementations
 
97
    import bzrlib.tests.workingtree_implementations
 
98
    return [
 
99
            bzrlib.doc,
 
100
            bzrlib.tests.blackbox,
 
101
            bzrlib.tests.branch_implementations,
 
102
            bzrlib.tests.bzrdir_implementations,
 
103
            bzrlib.tests.interrepository_implementations,
 
104
            bzrlib.tests.interversionedfile_implementations,
 
105
            bzrlib.tests.repository_implementations,
 
106
            bzrlib.tests.revisionstore_implementations,
 
107
            bzrlib.tests.workingtree_implementations,
 
108
            ]
 
109
 
 
110
 
 
111
class _MyResult(unittest._TextTestResult):
 
112
    """Custom TestResult.
 
113
 
 
114
    Shows output in a different format, including displaying runtime for tests.
 
115
    """
 
116
    stop_early = False
 
117
 
 
118
    def _elapsedTime(self):
 
119
        return "%5dms" % (1000 * (time.time() - self._start_time))
 
120
 
 
121
    def startTest(self, test):
 
122
        unittest.TestResult.startTest(self, test)
 
123
        # In a short description, the important words are in
 
124
        # the beginning, but in an id, the important words are
 
125
        # at the end
 
126
        SHOW_DESCRIPTIONS = False
 
127
        if self.showAll:
 
128
            width = osutils.terminal_width()
 
129
            name_width = width - 15
 
130
            what = None
 
131
            if SHOW_DESCRIPTIONS:
 
132
                what = test.shortDescription()
 
133
                if what:
 
134
                    if len(what) > name_width:
 
135
                        what = what[:name_width-3] + '...'
 
136
            if what is None:
 
137
                what = test.id()
 
138
                if what.startswith('bzrlib.tests.'):
 
139
                    what = what[13:]
 
140
                if len(what) > name_width:
 
141
                    what = '...' + what[3-name_width:]
 
142
            what = what.ljust(name_width)
 
143
            self.stream.write(what)
 
144
        self.stream.flush()
 
145
        self._start_time = time.time()
 
146
 
 
147
    def addError(self, test, err):
 
148
        if isinstance(err[1], TestSkipped):
 
149
            return self.addSkipped(test, err)    
 
150
        unittest.TestResult.addError(self, test, err)
 
151
        if self.showAll:
 
152
            self.stream.writeln("ERROR %s" % self._elapsedTime())
 
153
        elif self.dots:
 
154
            self.stream.write('E')
 
155
        self.stream.flush()
 
156
        if self.stop_early:
 
157
            self.stop()
 
158
 
 
159
    def addFailure(self, test, err):
 
160
        unittest.TestResult.addFailure(self, test, err)
 
161
        if self.showAll:
 
162
            self.stream.writeln(" FAIL %s" % self._elapsedTime())
 
163
        elif self.dots:
 
164
            self.stream.write('F')
 
165
        self.stream.flush()
 
166
        if self.stop_early:
 
167
            self.stop()
 
168
 
 
169
    def addSuccess(self, test):
 
170
        if self.showAll:
 
171
            self.stream.writeln('   OK %s' % self._elapsedTime())
 
172
        elif self.dots:
 
173
            self.stream.write('~')
 
174
        self.stream.flush()
 
175
        unittest.TestResult.addSuccess(self, test)
 
176
 
 
177
    def addSkipped(self, test, skip_excinfo):
 
178
        if self.showAll:
 
179
            print >>self.stream, ' SKIP %s' % self._elapsedTime()
 
180
            print >>self.stream, '     %s' % skip_excinfo[1]
 
181
        elif self.dots:
 
182
            self.stream.write('S')
 
183
        self.stream.flush()
 
184
        # seems best to treat this as success from point-of-view of unittest
 
185
        # -- it actually does nothing so it barely matters :)
 
186
        unittest.TestResult.addSuccess(self, test)
 
187
 
 
188
    def printErrorList(self, flavour, errors):
 
189
        for test, err in errors:
 
190
            self.stream.writeln(self.separator1)
 
191
            self.stream.writeln("%s: %s" % (flavour, self.getDescription(test)))
 
192
            if getattr(test, '_get_log', None) is not None:
 
193
                print >>self.stream
 
194
                print >>self.stream, \
 
195
                        ('vvvv[log from %s]' % test.id()).ljust(78,'-')
 
196
                print >>self.stream, test._get_log()
 
197
                print >>self.stream, \
 
198
                        ('^^^^[log from %s]' % test.id()).ljust(78,'-')
 
199
            self.stream.writeln(self.separator2)
 
200
            self.stream.writeln("%s" % err)
 
201
 
 
202
 
 
203
class TextTestRunner(unittest.TextTestRunner):
 
204
    stop_on_failure = False
 
205
 
 
206
    def _makeResult(self):
 
207
        result = _MyResult(self.stream, self.descriptions, self.verbosity)
 
208
        result.stop_early = self.stop_on_failure
 
209
        return result
 
210
 
 
211
 
 
212
def iter_suite_tests(suite):
 
213
    """Return all tests in a suite, recursing through nested suites"""
 
214
    for item in suite._tests:
 
215
        if isinstance(item, unittest.TestCase):
 
216
            yield item
 
217
        elif isinstance(item, unittest.TestSuite):
 
218
            for r in iter_suite_tests(item):
 
219
                yield r
 
220
        else:
 
221
            raise Exception('unknown object %r inside test suite %r'
 
222
                            % (item, suite))
 
223
 
 
224
 
 
225
class TestSkipped(Exception):
 
226
    """Indicates that a test was intentionally skipped, rather than failing."""
 
227
    # XXX: Not used yet
 
228
 
 
229
 
 
230
class CommandFailed(Exception):
 
231
    pass
 
232
 
 
233
 
 
234
class StringIOWrapper(object):
 
235
    """A wrapper around cStringIO which just adds an encoding attribute.
 
236
    
 
237
    Internally we can check sys.stdout to see what the output encoding
 
238
    should be. However, cStringIO has no encoding attribute that we can
 
239
    set. So we wrap it instead.
 
240
    """
 
241
    encoding='ascii'
 
242
    _cstring = None
 
243
 
 
244
    def __init__(self, s=None):
 
245
        if s is not None:
 
246
            self.__dict__['_cstring'] = StringIO(s)
 
247
        else:
 
248
            self.__dict__['_cstring'] = StringIO()
 
249
 
 
250
    def __getattr__(self, name, getattr=getattr):
 
251
        return getattr(self.__dict__['_cstring'], name)
 
252
 
 
253
    def __setattr__(self, name, val):
 
254
        if name == 'encoding':
 
255
            self.__dict__['encoding'] = val
 
256
        else:
 
257
            return setattr(self._cstring, name, val)
34
258
 
35
259
 
36
260
class TestCase(unittest.TestCase):
41
265
 
42
266
    Error and debug log messages are redirected from their usual
43
267
    location into a temporary file, the contents of which can be
44
 
    retrieved by _get_log().
 
268
    retrieved by _get_log().  We use a real OS file, not an in-memory object,
 
269
    so that it can also capture file IO.  When the test completes this file
 
270
    is read into memory and removed from disk.
45
271
       
46
272
    There are also convenience functions to invoke bzr's command-line
47
 
    routine, and to build and check bzr trees."""
 
273
    routine, and to build and check bzr trees.
 
274
   
 
275
    In addition to the usual method of overriding tearDown(), this class also
 
276
    allows subclasses to register functions into the _cleanups list, which is
 
277
    run in order as the object is torn down.  It's less likely this will be
 
278
    accidentally overlooked.
 
279
    """
48
280
 
49
281
    BZRPATH = 'bzr'
 
282
    _log_file_name = None
 
283
    _log_contents = ''
 
284
 
 
285
    def __init__(self, methodName='testMethod'):
 
286
        super(TestCase, self).__init__(methodName)
 
287
        self._cleanups = []
50
288
 
51
289
    def setUp(self):
52
 
        # this replaces the default testsweet.TestCase; we don't want logging changed
53
290
        unittest.TestCase.setUp(self)
 
291
        self._cleanEnvironment()
54
292
        bzrlib.trace.disable_default_logging()
55
 
        self._enable_file_logging()
56
 
 
57
 
 
58
 
    def _enable_file_logging(self):
 
293
        self._startLogFile()
 
294
 
 
295
    def _ndiff_strings(self, a, b):
 
296
        """Return ndiff between two strings containing lines.
 
297
        
 
298
        A trailing newline is added if missing to make the strings
 
299
        print properly."""
 
300
        if b and b[-1] != '\n':
 
301
            b += '\n'
 
302
        if a and a[-1] != '\n':
 
303
            a += '\n'
 
304
        difflines = difflib.ndiff(a.splitlines(True),
 
305
                                  b.splitlines(True),
 
306
                                  linejunk=lambda x: False,
 
307
                                  charjunk=lambda x: False)
 
308
        return ''.join(difflines)
 
309
 
 
310
    def assertEqualDiff(self, a, b, message=None):
 
311
        """Assert two texts are equal, if not raise an exception.
 
312
        
 
313
        This is intended for use with multi-line strings where it can 
 
314
        be hard to find the differences by eye.
 
315
        """
 
316
        # TODO: perhaps override assertEquals to call this for strings?
 
317
        if a == b:
 
318
            return
 
319
        if message is None:
 
320
            message = "texts not equal:\n"
 
321
        raise AssertionError(message + 
 
322
                             self._ndiff_strings(a, b))      
 
323
        
 
324
    def assertEqualMode(self, mode, mode_test):
 
325
        self.assertEqual(mode, mode_test,
 
326
                         'mode mismatch %o != %o' % (mode, mode_test))
 
327
 
 
328
    def assertStartsWith(self, s, prefix):
 
329
        if not s.startswith(prefix):
 
330
            raise AssertionError('string %r does not start with %r' % (s, prefix))
 
331
 
 
332
    def assertEndsWith(self, s, suffix):
 
333
        if not s.endswith(prefix):
 
334
            raise AssertionError('string %r does not end with %r' % (s, suffix))
 
335
 
 
336
    def assertContainsRe(self, haystack, needle_re):
 
337
        """Assert that a contains something matching a regular expression."""
 
338
        if not re.search(needle_re, haystack):
 
339
            raise AssertionError('pattern "%s" not found in "%s"'
 
340
                    % (needle_re, haystack))
 
341
 
 
342
    def assertSubset(self, sublist, superlist):
 
343
        """Assert that every entry in sublist is present in superlist."""
 
344
        missing = []
 
345
        for entry in sublist:
 
346
            if entry not in superlist:
 
347
                missing.append(entry)
 
348
        if len(missing) > 0:
 
349
            raise AssertionError("value(s) %r not present in container %r" % 
 
350
                                 (missing, superlist))
 
351
 
 
352
    def assertIs(self, left, right):
 
353
        if not (left is right):
 
354
            raise AssertionError("%r is not %r." % (left, right))
 
355
 
 
356
    def assertTransportMode(self, transport, path, mode):
 
357
        """Fail if a path does not have mode mode.
 
358
        
 
359
        If modes are not supported on this transport, the assertion is ignored.
 
360
        """
 
361
        if not transport._can_roundtrip_unix_modebits():
 
362
            return
 
363
        path_stat = transport.stat(path)
 
364
        actual_mode = stat.S_IMODE(path_stat.st_mode)
 
365
        self.assertEqual(mode, actual_mode,
 
366
            'mode of %r incorrect (%o != %o)' % (path, mode, actual_mode))
 
367
 
 
368
    def assertIsInstance(self, obj, kls):
 
369
        """Fail if obj is not an instance of kls"""
 
370
        if not isinstance(obj, kls):
 
371
            self.fail("%r is an instance of %s rather than %s" % (
 
372
                obj, obj.__class__, kls))
 
373
 
 
374
    def _startLogFile(self):
 
375
        """Send bzr and test log messages to a temporary file.
 
376
 
 
377
        The file is removed as the test is torn down.
 
378
        """
59
379
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
60
 
 
61
 
        self._log_file = os.fdopen(fileno, 'w+')
62
 
 
63
 
        hdlr = logging.StreamHandler(self._log_file)
64
 
        hdlr.setLevel(logging.DEBUG)
65
 
        hdlr.setFormatter(logging.Formatter('%(levelname)4.4s  %(message)s'))
66
 
        logging.getLogger('').addHandler(hdlr)
67
 
        logging.getLogger('').setLevel(logging.DEBUG)
68
 
        self._log_hdlr = hdlr
69
 
        debug('opened log file %s', name)
70
 
        
 
380
        encoder, decoder, stream_reader, stream_writer = codecs.lookup('UTF-8')
 
381
        self._log_file = stream_writer(os.fdopen(fileno, 'w+'))
 
382
        self._log_nonce = bzrlib.trace.enable_test_log(self._log_file)
71
383
        self._log_file_name = name
72
 
 
73
 
        
74
 
    def tearDown(self):
75
 
        logging.getLogger('').removeHandler(self._log_hdlr)
76
 
        bzrlib.trace.enable_default_logging()
77
 
        logging.debug('%s teardown', self.id())
 
384
        self.addCleanup(self._finishLogFile)
 
385
 
 
386
    def _finishLogFile(self):
 
387
        """Finished with the log file.
 
388
 
 
389
        Read contents into memory, close, and delete.
 
390
        """
 
391
        bzrlib.trace.disable_test_log(self._log_nonce)
 
392
        self._log_file.seek(0)
 
393
        self._log_contents = self._log_file.read()
78
394
        self._log_file.close()
 
395
        os.remove(self._log_file_name)
 
396
        self._log_file = self._log_file_name = None
 
397
 
 
398
    def addCleanup(self, callable):
 
399
        """Arrange to run a callable when this case is torn down.
 
400
 
 
401
        Callables are run in the reverse of the order they are registered, 
 
402
        ie last-in first-out.
 
403
        """
 
404
        if callable in self._cleanups:
 
405
            raise ValueError("cleanup function %r already registered on %s" 
 
406
                    % (callable, self))
 
407
        self._cleanups.append(callable)
 
408
 
 
409
    def _cleanEnvironment(self):
 
410
        new_env = {
 
411
            'HOME': os.getcwd(),
 
412
            'APPDATA': os.getcwd(),
 
413
            'BZREMAIL': None,
 
414
            'EMAIL': None,
 
415
        }
 
416
        self.__old_env = {}
 
417
        self.addCleanup(self._restoreEnvironment)
 
418
        for name, value in new_env.iteritems():
 
419
            self._captureVar(name, value)
 
420
 
 
421
 
 
422
    def _captureVar(self, name, newvalue):
 
423
        """Set an environment variable, preparing it to be reset when finished."""
 
424
        self.__old_env[name] = os.environ.get(name, None)
 
425
        if newvalue is None:
 
426
            if name in os.environ:
 
427
                del os.environ[name]
 
428
        else:
 
429
            os.environ[name] = newvalue
 
430
 
 
431
    @staticmethod
 
432
    def _restoreVar(name, value):
 
433
        if value is None:
 
434
            if name in os.environ:
 
435
                del os.environ[name]
 
436
        else:
 
437
            os.environ[name] = value
 
438
 
 
439
    def _restoreEnvironment(self):
 
440
        for name, value in self.__old_env.iteritems():
 
441
            self._restoreVar(name, value)
 
442
 
 
443
    def tearDown(self):
 
444
        self._runCleanups()
79
445
        unittest.TestCase.tearDown(self)
80
446
 
 
447
    def _runCleanups(self):
 
448
        """Run registered cleanup functions. 
 
449
 
 
450
        This should only be called from TestCase.tearDown.
 
451
        """
 
452
        # TODO: Perhaps this should keep running cleanups even if 
 
453
        # one of them fails?
 
454
        for cleanup_fn in reversed(self._cleanups):
 
455
            cleanup_fn()
81
456
 
82
457
    def log(self, *args):
83
 
        logging.debug(*args)
 
458
        mutter(*args)
84
459
 
85
460
    def _get_log(self):
86
461
        """Return as a string the log for this test"""
87
 
        return open(self._log_file_name).read()
 
462
        if self._log_file_name:
 
463
            return open(self._log_file_name).read()
 
464
        else:
 
465
            return self._log_contents
 
466
        # TODO: Delete the log after it's been read in
 
467
 
 
468
    def capture(self, cmd, retcode=0):
 
469
        """Shortcut that splits cmd into words, runs, and returns stdout"""
 
470
        return self.run_bzr_captured(cmd.split(), retcode=retcode)[0]
 
471
 
 
472
    def run_bzr_captured(self, argv, retcode=0, encoding=None):
 
473
        """Invoke bzr and return (stdout, stderr).
 
474
 
 
475
        Useful for code that wants to check the contents of the
 
476
        output, the way error messages are presented, etc.
 
477
 
 
478
        This should be the main method for tests that want to exercise the
 
479
        overall behavior of the bzr application (rather than a unit test
 
480
        or a functional test of the library.)
 
481
 
 
482
        Much of the old code runs bzr by forking a new copy of Python, but
 
483
        that is slower, harder to debug, and generally not necessary.
 
484
 
 
485
        This runs bzr through the interface that catches and reports
 
486
        errors, and with logging set to something approximating the
 
487
        default, so that error reporting can be checked.
 
488
 
 
489
        :param argv: arguments to invoke bzr
 
490
        :param retcode: expected return code, or None for don't-care.
 
491
        :param encoding: encoding for sys.stdout and sys.stderr
 
492
        """
 
493
        if encoding is None:
 
494
            encoding = bzrlib.user_encoding
 
495
        stdout = StringIOWrapper()
 
496
        stderr = StringIOWrapper()
 
497
        stdout.encoding = encoding
 
498
        stderr.encoding = encoding
 
499
 
 
500
        self.log('run bzr: %r', argv)
 
501
        # FIXME: don't call into logging here
 
502
        handler = logging.StreamHandler(stderr)
 
503
        handler.setFormatter(bzrlib.trace.QuietFormatter())
 
504
        handler.setLevel(logging.INFO)
 
505
        logger = logging.getLogger('')
 
506
        logger.addHandler(handler)
 
507
        try:
 
508
            result = self.apply_redirected(None, stdout, stderr,
 
509
                                           bzrlib.commands.run_bzr_catch_errors,
 
510
                                           argv)
 
511
        finally:
 
512
            logger.removeHandler(handler)
 
513
        # TODO: jam 20060105 Because we theoretically know the encoding
 
514
        #       of stdout and stderr, we could decode them at this time
 
515
        #       but for now, we will assume that the output of all
 
516
        #       functions
 
517
        out = stdout.getvalue()
 
518
        err = stderr.getvalue()
 
519
        if out:
 
520
            self.log('output:\n%r', out)
 
521
        if err:
 
522
            self.log('errors:\n%r', err)
 
523
        if retcode is not None:
 
524
            self.assertEquals(result, retcode)
 
525
        return out, err
88
526
 
89
527
    def run_bzr(self, *args, **kwargs):
90
528
        """Invoke bzr, as if it were run from the command line.
93
531
        overall behavior of the bzr application (rather than a unit test
94
532
        or a functional test of the library.)
95
533
 
96
 
        Much of the old code runs bzr by forking a new copy of Python, but
97
 
        that is slower, harder to debug, and generally not necessary.
 
534
        This sends the stdout/stderr results into the test's log,
 
535
        where it may be useful for debugging.  See also run_captured.
98
536
        """
99
 
        retcode = kwargs.get('retcode', 0)
100
 
        result = self.apply_redirected(None, None, None,
101
 
                                       bzrlib.commands.run_bzr, args)
102
 
        self.assertEquals(result, retcode)
103
 
        
 
537
        retcode = kwargs.pop('retcode', 0)
 
538
        encoding = kwargs.pop('encoding', None)
 
539
        return self.run_bzr_captured(args, retcode=retcode, encoding=encoding)
 
540
 
 
541
    def run_bzr_decode(self, *args, **kwargs):
 
542
        if kwargs.has_key('encoding'):
 
543
            encoding = kwargs['encoding']
 
544
        else:
 
545
            encoding = bzrlib.user_encoding
 
546
        return self.run_bzr(*args, **kwargs)[0].decode(encoding)
 
547
 
104
548
    def check_inventory_shape(self, inv, shape):
105
 
        """
106
 
        Compare an inventory to a list of expected names.
 
549
        """Compare an inventory to a list of expected names.
107
550
 
108
551
        Fail if they are not precisely equal.
109
552
        """
127
570
        """Call callable with redirected std io pipes.
128
571
 
129
572
        Returns the return code."""
130
 
        from StringIO import StringIO
131
573
        if not callable(a_callable):
132
574
            raise ValueError("a_callable must be callable.")
133
575
        if stdin is None:
134
576
            stdin = StringIO("")
135
577
        if stdout is None:
136
 
            stdout = self._log_file
 
578
            if getattr(self, "_log_file", None) is not None:
 
579
                stdout = self._log_file
 
580
            else:
 
581
                stdout = StringIO()
137
582
        if stderr is None:
138
 
            stderr = self._log_file
 
583
            if getattr(self, "_log_file", None is not None):
 
584
                stderr = self._log_file
 
585
            else:
 
586
                stderr = StringIO()
139
587
        real_stdin = sys.stdin
140
588
        real_stdout = sys.stdout
141
589
        real_stderr = sys.stderr
142
 
        result = None
143
590
        try:
144
591
            sys.stdout = stdout
145
592
            sys.stderr = stderr
146
593
            sys.stdin = stdin
147
 
            result = a_callable(*args, **kwargs)
 
594
            return a_callable(*args, **kwargs)
148
595
        finally:
149
596
            sys.stdout = real_stdout
150
597
            sys.stderr = real_stderr
151
598
            sys.stdin = real_stdin
152
 
        return result
 
599
 
 
600
    def merge(self, branch_from, wt_to):
 
601
        """A helper for tests to do a ui-less merge.
 
602
 
 
603
        This should move to the main library when someone has time to integrate
 
604
        it in.
 
605
        """
 
606
        # minimal ui-less merge.
 
607
        wt_to.branch.fetch(branch_from)
 
608
        base_rev = common_ancestor(branch_from.last_revision(),
 
609
                                   wt_to.branch.last_revision(),
 
610
                                   wt_to.branch.repository)
 
611
        merge_inner(wt_to.branch, branch_from.basis_tree(), 
 
612
                    wt_to.branch.repository.revision_tree(base_rev),
 
613
                    this_tree=wt_to)
 
614
        wt_to.add_pending_merge(branch_from.last_revision())
153
615
 
154
616
 
155
617
BzrTestBase = TestCase
178
640
        if contents != expect:
179
641
            self.log("expected: %r" % expect)
180
642
            self.log("actually: %r" % contents)
181
 
            self.fail("contents of %s not as expected")
 
643
            self.fail("contents of %s not as expected" % filename)
182
644
 
183
645
    def _make_test_root(self):
184
 
        import os
185
 
        import shutil
186
 
        import tempfile
187
 
        
188
646
        if TestCaseInTempDir.TEST_ROOT is not None:
189
647
            return
190
 
        TestCaseInTempDir.TEST_ROOT = os.path.abspath(
191
 
                                 tempfile.mkdtemp(suffix='.tmp',
192
 
                                                  prefix=self._TEST_NAME + '-',
193
 
                                                  dir=os.curdir))
194
 
    
 
648
        i = 0
 
649
        while True:
 
650
            root = u'test%04d.tmp' % i
 
651
            try:
 
652
                os.mkdir(root)
 
653
            except OSError, e:
 
654
                if e.errno == errno.EEXIST:
 
655
                    i += 1
 
656
                    continue
 
657
                else:
 
658
                    raise
 
659
            # successfully created
 
660
            TestCaseInTempDir.TEST_ROOT = osutils.abspath(root)
 
661
            break
195
662
        # make a fake bzr directory there to prevent any tests propagating
196
663
        # up onto the source directory's real branch
197
 
        os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
 
664
        bzrdir.BzrDir.create_standalone_workingtree(TestCaseInTempDir.TEST_ROOT)
198
665
 
199
666
    def setUp(self):
200
667
        super(TestCaseInTempDir, self).setUp()
201
 
        import os
202
668
        self._make_test_root()
203
 
        self._currentdir = os.getcwdu()
204
 
        self.test_dir = os.path.join(self.TEST_ROOT, self.id())
205
 
        os.mkdir(self.test_dir)
206
 
        os.chdir(self.test_dir)
 
669
        _currentdir = os.getcwdu()
 
670
        # shorten the name, to avoid test failures due to path length
 
671
        short_id = self.id().replace('bzrlib.tests.', '') \
 
672
                   .replace('__main__.', '')[-100:]
 
673
        # it's possible the same test class is run several times for
 
674
        # parameterized tests, so make sure the names don't collide.  
 
675
        i = 0
 
676
        while True:
 
677
            if i > 0:
 
678
                candidate_dir = '%s/%s.%d' % (self.TEST_ROOT, short_id, i)
 
679
            else:
 
680
                candidate_dir = '%s/%s' % (self.TEST_ROOT, short_id)
 
681
            if os.path.exists(candidate_dir):
 
682
                i = i + 1
 
683
                continue
 
684
            else:
 
685
                self.test_dir = candidate_dir
 
686
                os.mkdir(self.test_dir)
 
687
                os.chdir(self.test_dir)
 
688
                break
 
689
        os.environ['HOME'] = self.test_dir
 
690
        os.environ['APPDATA'] = self.test_dir
 
691
        def _leaveDirectory():
 
692
            os.chdir(_currentdir)
 
693
        self.addCleanup(_leaveDirectory)
207
694
        
208
 
    def tearDown(self):
209
 
        import os
210
 
        os.chdir(self._currentdir)
211
 
        super(TestCaseInTempDir, self).tearDown()
212
 
 
213
 
    def _formcmd(self, cmd):
214
 
        if isinstance(cmd, basestring):
215
 
            cmd = cmd.split()
216
 
        if cmd[0] == 'bzr':
217
 
            cmd[0] = self.BZRPATH
218
 
            if self.OVERRIDE_PYTHON:
219
 
                cmd.insert(0, self.OVERRIDE_PYTHON)
220
 
        self.log('$ %r' % cmd)
221
 
        return cmd
222
 
 
223
 
    def runcmd(self, cmd, retcode=0):
224
 
        """Run one command and check the return code.
225
 
 
226
 
        Returns a tuple of (stdout,stderr) strings.
227
 
 
228
 
        If a single string is based, it is split into words.
229
 
        For commands that are not simple space-separated words, please
230
 
        pass a list instead."""
231
 
        try:
232
 
            import shutil
233
 
            from subprocess import call
234
 
        except ImportError, e:
235
 
            _need_subprocess()
236
 
            raise
237
 
        cmd = self._formcmd(cmd)
238
 
        self.log('$ ' + ' '.join(cmd))
239
 
        actual_retcode = call(cmd, stdout=self._log_file, stderr=self._log_file)
240
 
        if retcode != actual_retcode:
241
 
            raise CommandFailed("test failed: %r returned %d, expected %d"
242
 
                                % (cmd, actual_retcode, retcode))
243
 
 
244
 
    def backtick(self, cmd, retcode=0):
245
 
        """Run a command and return its output"""
246
 
        try:
247
 
            import shutil
248
 
            from subprocess import Popen, PIPE
249
 
        except ImportError, e:
250
 
            _need_subprocess()
251
 
            raise
252
 
 
253
 
        cmd = self._formcmd(cmd)
254
 
        child = Popen(cmd, stdout=PIPE, stderr=self._log_file)
255
 
        outd, errd = child.communicate()
256
 
        self.log(outd)
257
 
        actual_retcode = child.wait()
258
 
 
259
 
        outd = outd.replace('\r', '')
260
 
 
261
 
        if retcode != actual_retcode:
262
 
            raise CommandFailed("test failed: %r returned %d, expected %d"
263
 
                                % (cmd, actual_retcode, retcode))
264
 
 
265
 
        return outd
266
 
 
267
 
 
268
 
 
269
 
    def build_tree(self, shape):
 
695
    def build_tree(self, shape, line_endings='native', transport=None):
270
696
        """Build a test tree according to a pattern.
271
697
 
272
698
        shape is a sequence of file specifications.  If the final
273
699
        character is '/', a directory is created.
274
700
 
275
701
        This doesn't add anything to a branch.
 
702
        :param line_endings: Either 'binary' or 'native'
 
703
                             in binary mode, exact contents are written
 
704
                             in native mode, the line endings match the
 
705
                             default platform endings.
 
706
 
 
707
        :param transport: A transport to write to, for building trees on 
 
708
                          VFS's. If the transport is readonly or None,
 
709
                          "." is opened automatically.
276
710
        """
277
711
        # XXX: It's OK to just create them using forward slashes on windows?
278
 
        import os
 
712
        if transport is None or transport.is_readonly():
 
713
            transport = get_transport(".")
279
714
        for name in shape:
280
 
            assert isinstance(name, basestring)
 
715
            self.assert_(isinstance(name, basestring))
281
716
            if name[-1] == '/':
282
 
                os.mkdir(name[:-1])
283
 
            else:
284
 
                f = file(name, 'wt')
285
 
                print >>f, "contents of", name
286
 
                f.close()
287
 
                
288
 
 
289
 
 
290
 
class MetaTestLog(TestCase):
291
 
    def test_logging(self):
292
 
        """Test logs are captured when a test fails."""
293
 
        logging.info('an info message')
294
 
        warning('something looks dodgy...')
295
 
        logging.debug('hello, test is running')
296
 
        ##assert 0
297
 
 
298
 
 
299
 
def selftest(verbose=False, pattern=".*"):
300
 
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
 
717
                transport.mkdir(urlescape(name[:-1]))
 
718
            else:
 
719
                if line_endings == 'binary':
 
720
                    end = '\n'
 
721
                elif line_endings == 'native':
 
722
                    end = os.linesep
 
723
                else:
 
724
                    raise errors.BzrError('Invalid line ending request %r' % (line_endings,))
 
725
                content = "contents of %s%s" % (name.encode('utf-8'), end)
 
726
                transport.put(urlescape(name), StringIO(content))
 
727
 
 
728
    def build_tree_contents(self, shape):
 
729
        build_tree_contents(shape)
 
730
 
 
731
    def failUnlessExists(self, path):
 
732
        """Fail unless path, which may be abs or relative, exists."""
 
733
        self.failUnless(osutils.lexists(path))
 
734
 
 
735
    def failIfExists(self, path):
 
736
        """Fail if path, which may be abs or relative, exists."""
 
737
        self.failIf(osutils.lexists(path))
 
738
        
 
739
    def assertFileEqual(self, content, path):
 
740
        """Fail if path does not contain 'content'."""
 
741
        self.failUnless(osutils.lexists(path))
 
742
        self.assertEqualDiff(content, open(path, 'r').read())
 
743
 
 
744
 
 
745
class TestCaseWithTransport(TestCaseInTempDir):
 
746
    """A test case that provides get_url and get_readonly_url facilities.
 
747
 
 
748
    These back onto two transport servers, one for readonly access and one for
 
749
    read write access.
 
750
 
 
751
    If no explicit class is provided for readonly access, a
 
752
    ReadonlyTransportDecorator is used instead which allows the use of non disk
 
753
    based read write transports.
 
754
 
 
755
    If an explicit class is provided for readonly access, that server and the 
 
756
    readwrite one must both define get_url() as resolving to os.getcwd().
 
757
    """
 
758
 
 
759
    def __init__(self, methodName='testMethod'):
 
760
        super(TestCaseWithTransport, self).__init__(methodName)
 
761
        self.__readonly_server = None
 
762
        self.__server = None
 
763
        self.transport_server = default_transport
 
764
        self.transport_readonly_server = None
 
765
 
 
766
    def get_readonly_url(self, relpath=None):
 
767
        """Get a URL for the readonly transport.
 
768
 
 
769
        This will either be backed by '.' or a decorator to the transport 
 
770
        used by self.get_url()
 
771
        relpath provides for clients to get a path relative to the base url.
 
772
        These should only be downwards relative, not upwards.
 
773
        """
 
774
        base = self.get_readonly_server().get_url()
 
775
        if relpath is not None:
 
776
            if not base.endswith('/'):
 
777
                base = base + '/'
 
778
            base = base + relpath
 
779
        return base
 
780
 
 
781
    def get_readonly_server(self):
 
782
        """Get the server instance for the readonly transport
 
783
 
 
784
        This is useful for some tests with specific servers to do diagnostics.
 
785
        """
 
786
        if self.__readonly_server is None:
 
787
            if self.transport_readonly_server is None:
 
788
                # readonly decorator requested
 
789
                # bring up the server
 
790
                self.get_url()
 
791
                self.__readonly_server = ReadonlyServer()
 
792
                self.__readonly_server.setUp(self.__server)
 
793
            else:
 
794
                self.__readonly_server = self.transport_readonly_server()
 
795
                self.__readonly_server.setUp()
 
796
            self.addCleanup(self.__readonly_server.tearDown)
 
797
        return self.__readonly_server
 
798
 
 
799
    def get_server(self):
 
800
        """Get the read/write server instance.
 
801
 
 
802
        This is useful for some tests with specific servers that need
 
803
        diagnostics.
 
804
        """
 
805
        if self.__server is None:
 
806
            self.__server = self.transport_server()
 
807
            self.__server.setUp()
 
808
            self.addCleanup(self.__server.tearDown)
 
809
        return self.__server
 
810
 
 
811
    def get_url(self, relpath=None):
 
812
        """Get a URL for the readwrite transport.
 
813
 
 
814
        This will either be backed by '.' or to an equivalent non-file based
 
815
        facility.
 
816
        relpath provides for clients to get a path relative to the base url.
 
817
        These should only be downwards relative, not upwards.
 
818
        """
 
819
        base = self.get_server().get_url()
 
820
        if relpath is not None and relpath != '.':
 
821
            if not base.endswith('/'):
 
822
                base = base + '/'
 
823
            base = base + relpath
 
824
        return base
 
825
 
 
826
    def get_transport(self):
 
827
        """Return a writeable transport for the test scratch space"""
 
828
        t = get_transport(self.get_url())
 
829
        self.assertFalse(t.is_readonly())
 
830
        return t
 
831
 
 
832
    def get_readonly_transport(self):
 
833
        """Return a readonly transport for the test scratch space
 
834
        
 
835
        This can be used to test that operations which should only need
 
836
        readonly access in fact do not try to write.
 
837
        """
 
838
        t = get_transport(self.get_readonly_url())
 
839
        self.assertTrue(t.is_readonly())
 
840
        return t
 
841
 
 
842
    def make_branch(self, relpath, format=None):
 
843
        """Create a branch on the transport at relpath."""
 
844
        repo = self.make_repository(relpath, format=format)
 
845
        return repo.bzrdir.create_branch()
 
846
 
 
847
    def make_bzrdir(self, relpath, format=None):
 
848
        try:
 
849
            url = self.get_url(relpath)
 
850
            segments = relpath.split('/')
 
851
            if segments and segments[-1] not in ('', '.'):
 
852
                parent = self.get_url('/'.join(segments[:-1]))
 
853
                t = get_transport(parent)
 
854
                try:
 
855
                    t.mkdir(segments[-1])
 
856
                except errors.FileExists:
 
857
                    pass
 
858
            if format is None:
 
859
                format=bzrlib.bzrdir.BzrDirFormat.get_default_format()
 
860
            # FIXME: make this use a single transport someday. RBC 20060418
 
861
            return format.initialize_on_transport(get_transport(relpath))
 
862
        except errors.UninitializableFormat:
 
863
            raise TestSkipped("Format %s is not initializable.")
 
864
 
 
865
    def make_repository(self, relpath, shared=False, format=None):
 
866
        """Create a repository on our default transport at relpath."""
 
867
        made_control = self.make_bzrdir(relpath, format=format)
 
868
        return made_control.create_repository(shared=shared)
 
869
 
 
870
    def make_branch_and_tree(self, relpath, format=None):
 
871
        """Create a branch on the transport and a tree locally.
 
872
 
 
873
        Returns the tree.
 
874
        """
 
875
        # TODO: always use the local disk path for the working tree,
 
876
        # this obviously requires a format that supports branch references
 
877
        # so check for that by checking bzrdir.BzrDirFormat.get_default_format()
 
878
        # RBC 20060208
 
879
        b = self.make_branch(relpath, format=format)
 
880
        try:
 
881
            return b.bzrdir.create_workingtree()
 
882
        except errors.NotLocalUrl:
 
883
            # new formats - catch No tree error and create
 
884
            # a branch reference and a checkout.
 
885
            # old formats at that point - raise TestSkipped.
 
886
            # TODO: rbc 20060208
 
887
            return WorkingTreeFormat2().initialize(bzrdir.BzrDir.open(relpath))
 
888
 
 
889
    def assertIsDirectory(self, relpath, transport):
 
890
        """Assert that relpath within transport is a directory.
 
891
 
 
892
        This may not be possible on all transports; in that case it propagates
 
893
        a TransportNotPossible.
 
894
        """
 
895
        try:
 
896
            mode = transport.stat(relpath).st_mode
 
897
        except errors.NoSuchFile:
 
898
            self.fail("path %s is not a directory; no such file"
 
899
                      % (relpath))
 
900
        if not stat.S_ISDIR(mode):
 
901
            self.fail("path %s is not a directory; has mode %#o"
 
902
                      % (relpath, mode))
 
903
 
 
904
 
 
905
class ChrootedTestCase(TestCaseWithTransport):
 
906
    """A support class that provides readonly urls outside the local namespace.
 
907
 
 
908
    This is done by checking if self.transport_server is a MemoryServer. if it
 
909
    is then we are chrooted already, if it is not then an HttpServer is used
 
910
    for readonly urls.
 
911
 
 
912
    TODO RBC 20060127: make this an option to TestCaseWithTransport so it can
 
913
                       be used without needed to redo it when a different 
 
914
                       subclass is in use ?
 
915
    """
 
916
 
 
917
    def setUp(self):
 
918
        super(ChrootedTestCase, self).setUp()
 
919
        if not self.transport_server == bzrlib.transport.memory.MemoryServer:
 
920
            self.transport_readonly_server = bzrlib.transport.http.HttpServer
 
921
 
 
922
 
 
923
def filter_suite_by_re(suite, pattern):
 
924
    result = TestSuite()
 
925
    filter_re = re.compile(pattern)
 
926
    for test in iter_suite_tests(suite):
 
927
        if filter_re.search(test.id()):
 
928
            result.addTest(test)
 
929
    return result
 
930
 
 
931
 
 
932
def run_suite(suite, name='test', verbose=False, pattern=".*",
 
933
              stop_on_failure=False, keep_output=False,
 
934
              transport=None):
 
935
    TestCaseInTempDir._TEST_NAME = name
 
936
    if verbose:
 
937
        verbosity = 2
 
938
    else:
 
939
        verbosity = 1
 
940
    runner = TextTestRunner(stream=sys.stdout,
 
941
                            descriptions=0,
 
942
                            verbosity=verbosity)
 
943
    runner.stop_on_failure=stop_on_failure
 
944
    if pattern != '.*':
 
945
        suite = filter_suite_by_re(suite, pattern)
 
946
    result = runner.run(suite)
 
947
    # This is still a little bogus, 
 
948
    # but only a little. Folk not using our testrunner will
 
949
    # have to delete their temp directories themselves.
 
950
    test_root = TestCaseInTempDir.TEST_ROOT
 
951
    if result.wasSuccessful() or not keep_output:
 
952
        if test_root is not None:
 
953
            print 'Deleting test root %s...' % test_root
 
954
            try:
 
955
                shutil.rmtree(test_root)
 
956
            finally:
 
957
                print
 
958
    else:
 
959
        print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
 
960
    return result.wasSuccessful()
 
961
 
 
962
 
 
963
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
 
964
             keep_output=False,
 
965
             transport=None):
 
966
    """Run the whole test suite under the enhanced runner"""
 
967
    global default_transport
 
968
    if transport is None:
 
969
        transport = default_transport
 
970
    old_transport = default_transport
 
971
    default_transport = transport
 
972
    suite = test_suite()
 
973
    try:
 
974
        return run_suite(suite, 'testbzr', verbose=verbose, pattern=pattern,
 
975
                     stop_on_failure=stop_on_failure, keep_output=keep_output,
 
976
                     transport=transport)
 
977
    finally:
 
978
        default_transport = old_transport
 
979
 
301
980
 
302
981
 
303
982
def test_suite():
304
 
    from bzrlib.selftest.TestUtil import TestLoader, TestSuite
305
 
    import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
306
 
    import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
 
983
    """Build and return TestSuite for the whole program."""
307
984
    from doctest import DocTestSuite
308
 
    import os
309
 
    import shutil
310
 
    import time
311
 
    import sys
312
 
 
313
 
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
314
 
 
315
 
    testmod_names = \
316
 
                  ['bzrlib.selftest.MetaTestLog',
317
 
                   'bzrlib.selftest.testinv',
318
 
                   'bzrlib.selftest.testfetch',
319
 
                   'bzrlib.selftest.versioning',
320
 
                   'bzrlib.selftest.whitebox',
321
 
                   'bzrlib.selftest.testmerge3',
322
 
                   'bzrlib.selftest.testhashcache',
323
 
                   'bzrlib.selftest.teststatus',
324
 
                   'bzrlib.selftest.testlog',
325
 
                   'bzrlib.selftest.blackbox',
326
 
                   'bzrlib.selftest.testrevisionnamespaces',
327
 
                   'bzrlib.selftest.testbranch',
328
 
                   'bzrlib.selftest.testrevision',
329
 
                   'bzrlib.selftest.test_merge_core',
330
 
                   'bzrlib.selftest.test_smart_add',
331
 
                   'bzrlib.selftest.testdiff',
332
 
                   'bzrlib.fetch'
 
985
 
 
986
    global MODULES_TO_DOCTEST
 
987
 
 
988
    testmod_names = [ \
 
989
                   'bzrlib.tests.test_ancestry',
 
990
                   'bzrlib.tests.test_annotate',
 
991
                   'bzrlib.tests.test_api',
 
992
                   'bzrlib.tests.test_bad_files',
 
993
                   'bzrlib.tests.test_branch',
 
994
                   'bzrlib.tests.test_bzrdir',
 
995
                   'bzrlib.tests.test_command',
 
996
                   'bzrlib.tests.test_commit',
 
997
                   'bzrlib.tests.test_commit_merge',
 
998
                   'bzrlib.tests.test_config',
 
999
                   'bzrlib.tests.test_conflicts',
 
1000
                   'bzrlib.tests.test_decorators',
 
1001
                   'bzrlib.tests.test_diff',
 
1002
                   'bzrlib.tests.test_doc_generate',
 
1003
                   'bzrlib.tests.test_errors',
 
1004
                   'bzrlib.tests.test_escaped_store',
 
1005
                   'bzrlib.tests.test_fetch',
 
1006
                   'bzrlib.tests.test_gpg',
 
1007
                   'bzrlib.tests.test_graph',
 
1008
                   'bzrlib.tests.test_hashcache',
 
1009
                   'bzrlib.tests.test_http',
 
1010
                   'bzrlib.tests.test_identitymap',
 
1011
                   'bzrlib.tests.test_inv',
 
1012
                   'bzrlib.tests.test_knit',
 
1013
                   'bzrlib.tests.test_lockdir',
 
1014
                   'bzrlib.tests.test_lockable_files',
 
1015
                   'bzrlib.tests.test_log',
 
1016
                   'bzrlib.tests.test_merge',
 
1017
                   'bzrlib.tests.test_merge3',
 
1018
                   'bzrlib.tests.test_merge_core',
 
1019
                   'bzrlib.tests.test_missing',
 
1020
                   'bzrlib.tests.test_msgeditor',
 
1021
                   'bzrlib.tests.test_nonascii',
 
1022
                   'bzrlib.tests.test_options',
 
1023
                   'bzrlib.tests.test_osutils',
 
1024
                   'bzrlib.tests.test_patch',
 
1025
                   'bzrlib.tests.test_permissions',
 
1026
                   'bzrlib.tests.test_plugins',
 
1027
                   'bzrlib.tests.test_progress',
 
1028
                   'bzrlib.tests.test_reconcile',
 
1029
                   'bzrlib.tests.test_repository',
 
1030
                   'bzrlib.tests.test_revision',
 
1031
                   'bzrlib.tests.test_revisionnamespaces',
 
1032
                   'bzrlib.tests.test_revprops',
 
1033
                   'bzrlib.tests.test_rio',
 
1034
                   'bzrlib.tests.test_sampler',
 
1035
                   'bzrlib.tests.test_selftest',
 
1036
                   'bzrlib.tests.test_setup',
 
1037
                   'bzrlib.tests.test_sftp_transport',
 
1038
                   'bzrlib.tests.test_smart_add',
 
1039
                   'bzrlib.tests.test_source',
 
1040
                   'bzrlib.tests.test_store',
 
1041
                   'bzrlib.tests.test_symbol_versioning',
 
1042
                   'bzrlib.tests.test_testament',
 
1043
                   'bzrlib.tests.test_textfile',
 
1044
                   'bzrlib.tests.test_textmerge',
 
1045
                   'bzrlib.tests.test_trace',
 
1046
                   'bzrlib.tests.test_transactions',
 
1047
                   'bzrlib.tests.test_transform',
 
1048
                   'bzrlib.tests.test_transport',
 
1049
                   'bzrlib.tests.test_tsort',
 
1050
                   'bzrlib.tests.test_tuned_gzip',
 
1051
                   'bzrlib.tests.test_ui',
 
1052
                   'bzrlib.tests.test_upgrade',
 
1053
                   'bzrlib.tests.test_versionedfile',
 
1054
                   'bzrlib.tests.test_weave',
 
1055
                   'bzrlib.tests.test_whitebox',
 
1056
                   'bzrlib.tests.test_workingtree',
 
1057
                   'bzrlib.tests.test_xml',
333
1058
                   ]
334
 
 
335
 
    for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
336
 
              bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
337
 
        if m not in MODULES_TO_DOCTEST:
338
 
            MODULES_TO_DOCTEST.append(m)
339
 
 
340
 
    TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
341
 
    print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
 
1059
    test_transport_implementations = [
 
1060
        'bzrlib.tests.test_transport_implementations']
 
1061
 
 
1062
    TestCase.BZRPATH = osutils.pathjoin(
 
1063
            osutils.realpath(osutils.dirname(bzrlib.__path__[0])), 'bzr')
 
1064
    print '%10s: %s' % ('bzr', osutils.realpath(sys.argv[0]))
 
1065
    print '%10s: %s' % ('bzrlib', bzrlib.__path__[0])
342
1066
    print
343
1067
    suite = TestSuite()
344
 
    suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
 
1068
    # python2.4's TestLoader.loadTestsFromNames gives very poor 
 
1069
    # errors if it fails to load a named module - no indication of what's
 
1070
    # actually wrong, just "no such module".  We should probably override that
 
1071
    # class, but for the moment just load them ourselves. (mbp 20051202)
 
1072
    loader = TestLoader()
 
1073
    from bzrlib.transport import TransportTestProviderAdapter
 
1074
    adapter = TransportTestProviderAdapter()
 
1075
    adapt_modules(test_transport_implementations, adapter, loader, suite)
 
1076
    for mod_name in testmod_names:
 
1077
        mod = _load_module_by_name(mod_name)
 
1078
        suite.addTest(loader.loadTestsFromModule(mod))
 
1079
    for package in packages_to_test():
 
1080
        suite.addTest(package.test_suite())
345
1081
    for m in MODULES_TO_TEST:
346
 
         suite.addTest(TestLoader().loadTestsFromModule(m))
 
1082
        suite.addTest(loader.loadTestsFromModule(m))
347
1083
    for m in (MODULES_TO_DOCTEST):
348
1084
        suite.addTest(DocTestSuite(m))
349
 
    for p in bzrlib.plugin.all_plugins:
350
 
        if hasattr(p, 'test_suite'):
351
 
            suite.addTest(p.test_suite())
 
1085
    for name, plugin in bzrlib.plugin.all_plugins().items():
 
1086
        if getattr(plugin, 'test_suite', None) is not None:
 
1087
            suite.addTest(plugin.test_suite())
352
1088
    return suite
353
1089
 
 
1090
 
 
1091
def adapt_modules(mods_list, adapter, loader, suite):
 
1092
    """Adapt the modules in mods_list using adapter and add to suite."""
 
1093
    for mod_name in mods_list:
 
1094
        mod = _load_module_by_name(mod_name)
 
1095
        for test in iter_suite_tests(loader.loadTestsFromModule(mod)):
 
1096
            suite.addTests(adapter.adapt(test))
 
1097
 
 
1098
 
 
1099
def _load_module_by_name(mod_name):
 
1100
    parts = mod_name.split('.')
 
1101
    module = __import__(mod_name)
 
1102
    del parts[0]
 
1103
    # for historical reasons python returns the top-level module even though
 
1104
    # it loads the submodule; we need to walk down to get the one we want.
 
1105
    while parts:
 
1106
        module = getattr(module, parts.pop(0))
 
1107
    return module