~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

- stub for revision properties

Show diffs side-by-side

added added

removed removed

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