~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

[merge] jelmer

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005 by Canonical Ltd
 
2
 
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
 
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
 
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
 
 
18
from cStringIO import StringIO
 
19
import difflib
 
20
import errno
 
21
import logging
 
22
import os
 
23
import re
 
24
import shutil
 
25
import sys
 
26
import tempfile
 
27
import unittest
 
28
import time
 
29
 
 
30
from logging import debug, warning, error
 
31
 
 
32
import bzrlib.commands
 
33
import bzrlib.trace
 
34
import bzrlib.osutils as osutils
 
35
from bzrlib.selftest import TestUtil
 
36
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
 
37
from bzrlib.selftest.treeshape import build_tree_contents
 
38
from bzrlib.errors import BzrError
 
39
 
 
40
MODULES_TO_TEST = []
 
41
MODULES_TO_DOCTEST = []
 
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)
 
66
 
 
67
 
 
68
class _MyResult(unittest._TextTestResult):
 
69
    """
 
70
    Custom TestResult.
 
71
 
 
72
    No special behaviour for now.
 
73
    """
 
74
 
 
75
    def _elapsedTime(self):
 
76
        return "(Took %.3fs)" % (time.time() - self._start_time)
 
77
 
 
78
    def startTest(self, test):
 
79
        unittest.TestResult.startTest(self, test)
 
80
        # TODO: Maybe show test.shortDescription somewhere?
 
81
        what = test.shortDescription() or test.id()        
 
82
        if self.showAll:
 
83
            self.stream.write('%-70.70s' % what)
 
84
        self.stream.flush()
 
85
        self._start_time = time.time()
 
86
 
 
87
    def addError(self, test, err):
 
88
        unittest.TestResult.addError(self, test, err)
 
89
        if self.showAll:
 
90
            self.stream.writeln("ERROR %s" % self._elapsedTime())
 
91
        elif self.dots:
 
92
            self.stream.write('E')
 
93
        self.stream.flush()
 
94
 
 
95
    def addFailure(self, test, err):
 
96
        unittest.TestResult.addFailure(self, test, err)
 
97
        if self.showAll:
 
98
            self.stream.writeln("FAIL %s" % self._elapsedTime())
 
99
        elif self.dots:
 
100
            self.stream.write('F')
 
101
        self.stream.flush()
 
102
 
 
103
    def addSuccess(self, test):
 
104
        if self.showAll:
 
105
            self.stream.writeln('OK %s' % self._elapsedTime())
 
106
        elif self.dots:
 
107
            self.stream.write('~')
 
108
        self.stream.flush()
 
109
        unittest.TestResult.addSuccess(self, test)
 
110
 
 
111
    def printErrorList(self, flavour, errors):
 
112
        for test, err in errors:
 
113
            self.stream.writeln(self.separator1)
 
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:')
 
118
                print >>self.stream, test._get_log()
 
119
            self.stream.writeln(self.separator2)
 
120
            self.stream.writeln("%s" % err)
 
121
 
 
122
 
 
123
class TextTestRunner(unittest.TextTestRunner):
 
124
    stop_on_failure = False
 
125
 
 
126
    def _makeResult(self):
 
127
        result = _MyResult(self.stream, self.descriptions, self.verbosity)
 
128
        if self.stop_on_failure:
 
129
            result = EarlyStoppingTestResultAdapter(result)
 
130
        return result
 
131
 
 
132
 
 
133
def iter_suite_tests(suite):
 
134
    """Return all tests in a suite, recursing through nested suites"""
 
135
    for item in suite._tests:
 
136
        if isinstance(item, unittest.TestCase):
 
137
            yield item
 
138
        elif isinstance(item, unittest.TestSuite):
 
139
            for r in iter_suite_tests(item):
 
140
                yield r
 
141
        else:
 
142
            raise Exception('unknown object %r inside test suite %r'
 
143
                            % (item, suite))
 
144
 
 
145
 
 
146
class TestSkipped(Exception):
 
147
    """Indicates that a test was intentionally skipped, rather than failing."""
 
148
    # XXX: Not used yet
 
149
 
 
150
 
 
151
class CommandFailed(Exception):
 
152
    pass
 
153
 
 
154
class TestCase(unittest.TestCase):
 
155
    """Base class for bzr unit tests.
 
156
    
 
157
    Tests that need access to disk resources should subclass 
 
158
    TestCaseInTempDir not TestCase.
 
159
 
 
160
    Error and debug log messages are redirected from their usual
 
161
    location into a temporary file, the contents of which can be
 
162
    retrieved by _get_log().  We use a real OS file, not an in-memory object,
 
163
    so that it can also capture file IO.  When the test completes this file
 
164
    is read into memory and removed from disk.
 
165
       
 
166
    There are also convenience functions to invoke bzr's command-line
 
167
    routine, and to build and check bzr trees.
 
168
   
 
169
    In addition to the usual method of overriding tearDown(), this class also
 
170
    allows subclasses to register functions into the _cleanups list, which is
 
171
    run in order as the object is torn down.  It's less likely this will be
 
172
    accidentally overlooked.
 
173
    """
 
174
 
 
175
    BZRPATH = 'bzr'
 
176
    _log_file_name = None
 
177
    _log_contents = ''
 
178
 
 
179
    def setUp(self):
 
180
        unittest.TestCase.setUp(self)
 
181
        self._cleanups = []
 
182
        self._cleanEnvironment()
 
183
        bzrlib.trace.disable_default_logging()
 
184
        self._startLogFile()
 
185
 
 
186
    def _ndiff_strings(self, a, b):
 
187
        """Return ndiff between two strings containing lines.
 
188
        
 
189
        A trailing newline is added if missing to make the strings
 
190
        print properly."""
 
191
        if b and b[-1] != '\n':
 
192
            b += '\n'
 
193
        if a and a[-1] != '\n':
 
194
            a += '\n'
 
195
        difflines = difflib.ndiff(a.splitlines(True),
 
196
                                  b.splitlines(True),
 
197
                                  linejunk=lambda x: False,
 
198
                                  charjunk=lambda x: False)
 
199
        return ''.join(difflines)
 
200
 
 
201
    def assertEqualDiff(self, a, b):
 
202
        """Assert two texts are equal, if not raise an exception.
 
203
        
 
204
        This is intended for use with multi-line strings where it can 
 
205
        be hard to find the differences by eye.
 
206
        """
 
207
        # TODO: perhaps override assertEquals to call this for strings?
 
208
        if a == b:
 
209
            return
 
210
        raise AssertionError("texts not equal:\n" + 
 
211
                             self._ndiff_strings(a, b))      
 
212
 
 
213
    def assertContainsRe(self, haystack, needle_re):
 
214
        """Assert that a contains something matching a regular expression."""
 
215
        if not re.search(needle_re, haystack):
 
216
            raise AssertionError('pattern "%s" not found in "%s"'
 
217
                    % (needle_re, haystack))
 
218
 
 
219
    def _startLogFile(self):
 
220
        """Send bzr and test log messages to a temporary file.
 
221
 
 
222
        The file is removed as the test is torn down.
 
223
        """
 
224
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
 
225
        self._log_file = os.fdopen(fileno, 'w+')
 
226
        bzrlib.trace.enable_test_log(self._log_file)
 
227
        debug('opened log file %s', name)
 
228
        self._log_file_name = name
 
229
        self.addCleanup(self._finishLogFile)
 
230
 
 
231
    def _finishLogFile(self):
 
232
        """Finished with the log file.
 
233
 
 
234
        Read contents into memory, close, and delete.
 
235
        """
 
236
        bzrlib.trace.disable_test_log()
 
237
        self._log_file.seek(0)
 
238
        self._log_contents = self._log_file.read()
 
239
        self._log_file.close()
 
240
        os.remove(self._log_file_name)
 
241
        self._log_file = self._log_file_name = None
 
242
 
 
243
    def addCleanup(self, callable):
 
244
        """Arrange to run a callable when this case is torn down.
 
245
 
 
246
        Callables are run in the reverse of the order they are registered, 
 
247
        ie last-in first-out.
 
248
        """
 
249
        if callable in self._cleanups:
 
250
            raise ValueError("cleanup function %r already registered on %s" 
 
251
                    % (callable, self))
 
252
        self._cleanups.append(callable)
 
253
 
 
254
    def _cleanEnvironment(self):
 
255
        new_env = {
 
256
            'HOME': os.getcwd(),
 
257
            'APPDATA': os.getcwd(),
 
258
            'BZREMAIL': None,
 
259
            'EMAIL': None,
 
260
        }
 
261
        self.__old_env = {}
 
262
        self.addCleanup(self._restoreEnvironment)
 
263
        for name, value in new_env.iteritems():
 
264
            self._captureVar(name, value)
 
265
 
 
266
 
 
267
    def _captureVar(self, name, newvalue):
 
268
        """Set an environment variable, preparing it to be reset when finished."""
 
269
        self.__old_env[name] = os.environ.get(name, None)
 
270
        if newvalue is None:
 
271
            if name in os.environ:
 
272
                del os.environ[name]
 
273
        else:
 
274
            os.environ[name] = newvalue
 
275
 
 
276
    @staticmethod
 
277
    def _restoreVar(name, value):
 
278
        if value is None:
 
279
            if name in os.environ:
 
280
                del os.environ[name]
 
281
        else:
 
282
            os.environ[name] = value
 
283
 
 
284
    def _restoreEnvironment(self):
 
285
        for name, value in self.__old_env.iteritems():
 
286
            self._restoreVar(name, value)
 
287
 
 
288
    def tearDown(self):
 
289
        self._runCleanups()
 
290
        unittest.TestCase.tearDown(self)
 
291
 
 
292
    def _runCleanups(self):
 
293
        """Run registered cleanup functions. 
 
294
 
 
295
        This should only be called from TestCase.tearDown.
 
296
        """
 
297
        for callable in reversed(self._cleanups):
 
298
            callable()
 
299
 
 
300
    def log(self, *args):
 
301
        logging.debug(*args)
 
302
 
 
303
    def _get_log(self):
 
304
        """Return as a string the log for this test"""
 
305
        if self._log_file_name:
 
306
            return open(self._log_file_name).read()
 
307
        else:
 
308
            return self._log_contents
 
309
 
 
310
    def capture(self, cmd, retcode=0):
 
311
        """Shortcut that splits cmd into words, runs, and returns stdout"""
 
312
        return self.run_bzr_captured(cmd.split(), retcode=retcode)[0]
 
313
 
 
314
    def run_bzr_captured(self, argv, retcode=0):
 
315
        """Invoke bzr and return (stdout, stderr).
 
316
 
 
317
        Useful for code that wants to check the contents of the
 
318
        output, the way error messages are presented, etc.
 
319
 
 
320
        This should be the main method for tests that want to exercise the
 
321
        overall behavior of the bzr application (rather than a unit test
 
322
        or a functional test of the library.)
 
323
 
 
324
        Much of the old code runs bzr by forking a new copy of Python, but
 
325
        that is slower, harder to debug, and generally not necessary.
 
326
 
 
327
        This runs bzr through the interface that catches and reports
 
328
        errors, and with logging set to something approximating the
 
329
        default, so that error reporting can be checked.
 
330
 
 
331
        argv -- arguments to invoke bzr
 
332
        retcode -- expected return code, or None for don't-care.
 
333
        """
 
334
        stdout = StringIO()
 
335
        stderr = StringIO()
 
336
        self.log('run bzr: %s', ' '.join(argv))
 
337
        handler = logging.StreamHandler(stderr)
 
338
        handler.setFormatter(bzrlib.trace.QuietFormatter())
 
339
        handler.setLevel(logging.INFO)
 
340
        logger = logging.getLogger('')
 
341
        logger.addHandler(handler)
 
342
        try:
 
343
            result = self.apply_redirected(None, stdout, stderr,
 
344
                                           bzrlib.commands.run_bzr_catch_errors,
 
345
                                           argv)
 
346
        finally:
 
347
            logger.removeHandler(handler)
 
348
        out = stdout.getvalue()
 
349
        err = stderr.getvalue()
 
350
        if out:
 
351
            self.log('output:\n%s', out)
 
352
        if err:
 
353
            self.log('errors:\n%s', err)
 
354
        if retcode is not None:
 
355
            self.assertEquals(result, retcode)
 
356
        return out, err
 
357
 
 
358
    def run_bzr(self, *args, **kwargs):
 
359
        """Invoke bzr, as if it were run from the command line.
 
360
 
 
361
        This should be the main method for tests that want to exercise the
 
362
        overall behavior of the bzr application (rather than a unit test
 
363
        or a functional test of the library.)
 
364
 
 
365
        This sends the stdout/stderr results into the test's log,
 
366
        where it may be useful for debugging.  See also run_captured.
 
367
        """
 
368
        retcode = kwargs.pop('retcode', 0)
 
369
        return self.run_bzr_captured(args, retcode)
 
370
 
 
371
    def check_inventory_shape(self, inv, shape):
 
372
        """Compare an inventory to a list of expected names.
 
373
 
 
374
        Fail if they are not precisely equal.
 
375
        """
 
376
        extras = []
 
377
        shape = list(shape)             # copy
 
378
        for path, ie in inv.entries():
 
379
            name = path.replace('\\', '/')
 
380
            if ie.kind == 'dir':
 
381
                name = name + '/'
 
382
            if name in shape:
 
383
                shape.remove(name)
 
384
            else:
 
385
                extras.append(name)
 
386
        if shape:
 
387
            self.fail("expected paths not found in inventory: %r" % shape)
 
388
        if extras:
 
389
            self.fail("unexpected paths found in inventory: %r" % extras)
 
390
 
 
391
    def apply_redirected(self, stdin=None, stdout=None, stderr=None,
 
392
                         a_callable=None, *args, **kwargs):
 
393
        """Call callable with redirected std io pipes.
 
394
 
 
395
        Returns the return code."""
 
396
        if not callable(a_callable):
 
397
            raise ValueError("a_callable must be callable.")
 
398
        if stdin is None:
 
399
            stdin = StringIO("")
 
400
        if stdout is None:
 
401
            if hasattr(self, "_log_file"):
 
402
                stdout = self._log_file
 
403
            else:
 
404
                stdout = StringIO()
 
405
        if stderr is None:
 
406
            if hasattr(self, "_log_file"):
 
407
                stderr = self._log_file
 
408
            else:
 
409
                stderr = StringIO()
 
410
        real_stdin = sys.stdin
 
411
        real_stdout = sys.stdout
 
412
        real_stderr = sys.stderr
 
413
        try:
 
414
            sys.stdout = stdout
 
415
            sys.stderr = stderr
 
416
            sys.stdin = stdin
 
417
            return a_callable(*args, **kwargs)
 
418
        finally:
 
419
            sys.stdout = real_stdout
 
420
            sys.stderr = real_stderr
 
421
            sys.stdin = real_stdin
 
422
 
 
423
 
 
424
BzrTestBase = TestCase
 
425
 
 
426
     
 
427
class TestCaseInTempDir(TestCase):
 
428
    """Derived class that runs a test within a temporary directory.
 
429
 
 
430
    This is useful for tests that need to create a branch, etc.
 
431
 
 
432
    The directory is created in a slightly complex way: for each
 
433
    Python invocation, a new temporary top-level directory is created.
 
434
    All test cases create their own directory within that.  If the
 
435
    tests complete successfully, the directory is removed.
 
436
 
 
437
    InTempDir is an old alias for FunctionalTestCase.
 
438
    """
 
439
 
 
440
    TEST_ROOT = None
 
441
    _TEST_NAME = 'test'
 
442
    OVERRIDE_PYTHON = 'python'
 
443
 
 
444
    def check_file_contents(self, filename, expect):
 
445
        self.log("check contents of file %s" % filename)
 
446
        contents = file(filename, 'r').read()
 
447
        if contents != expect:
 
448
            self.log("expected: %r" % expect)
 
449
            self.log("actually: %r" % contents)
 
450
            self.fail("contents of %s not as expected" % filename)
 
451
 
 
452
    def _make_test_root(self):
 
453
        if TestCaseInTempDir.TEST_ROOT is not None:
 
454
            return
 
455
        i = 0
 
456
        while True:
 
457
            root = u'test%04d.tmp' % i
 
458
            try:
 
459
                os.mkdir(root)
 
460
            except OSError, e:
 
461
                if e.errno == errno.EEXIST:
 
462
                    i += 1
 
463
                    continue
 
464
                else:
 
465
                    raise
 
466
            # successfully created
 
467
            TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
 
468
            break
 
469
        # make a fake bzr directory there to prevent any tests propagating
 
470
        # up onto the source directory's real branch
 
471
        os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
 
472
 
 
473
    def setUp(self):
 
474
        super(TestCaseInTempDir, self).setUp()
 
475
        self._make_test_root()
 
476
        _currentdir = os.getcwdu()
 
477
        short_id = self.id().replace('bzrlib.selftest.', '') \
 
478
                   .replace('__main__.', '')
 
479
        self.test_dir = os.path.join(self.TEST_ROOT, short_id)
 
480
        os.mkdir(self.test_dir)
 
481
        os.chdir(self.test_dir)
 
482
        os.environ['HOME'] = self.test_dir
 
483
        def _leaveDirectory():
 
484
            os.chdir(_currentdir)
 
485
        self.addCleanup(_leaveDirectory)
 
486
        
 
487
    def build_tree(self, shape, line_endings='native'):
 
488
        """Build a test tree according to a pattern.
 
489
 
 
490
        shape is a sequence of file specifications.  If the final
 
491
        character is '/', a directory is created.
 
492
 
 
493
        This doesn't add anything to a branch.
 
494
        :param line_endings: Either 'binary' or 'native'
 
495
                             in binary mode, exact contents are written
 
496
                             in native mode, the line endings match the
 
497
                             default platform endings.
 
498
        """
 
499
        # XXX: It's OK to just create them using forward slashes on windows?
 
500
        for name in shape:
 
501
            self.assert_(isinstance(name, basestring))
 
502
            if name[-1] == '/':
 
503
                os.mkdir(name[:-1])
 
504
            else:
 
505
                if line_endings == 'binary':
 
506
                    f = file(name, 'wb')
 
507
                elif line_endings == 'native':
 
508
                    f = file(name, 'wt')
 
509
                else:
 
510
                    raise BzrError('Invalid line ending request %r' % (line_endings,))
 
511
                print >>f, "contents of", name
 
512
                f.close()
 
513
 
 
514
    def build_tree_contents(self, shape):
 
515
        bzrlib.selftest.build_tree_contents(shape)
 
516
 
 
517
    def failUnlessExists(self, path):
 
518
        """Fail unless path, which may be abs or relative, exists."""
 
519
        self.failUnless(osutils.lexists(path))
 
520
        
 
521
    def assertFileEqual(self, content, path):
 
522
        """Fail if path does not contain 'content'."""
 
523
        self.failUnless(osutils.lexists(path))
 
524
        self.assertEqualDiff(content, open(path, 'r').read())
 
525
        
 
526
 
 
527
class MetaTestLog(TestCase):
 
528
    def test_logging(self):
 
529
        """Test logs are captured when a test fails."""
 
530
        self.log('a test message')
 
531
        self.assertContainsRe(self._get_log(), 'a test message\n')
 
532
 
 
533
 
 
534
def filter_suite_by_re(suite, pattern):
 
535
    result = TestUtil.TestSuite()
 
536
    filter_re = re.compile(pattern)
 
537
    for test in iter_suite_tests(suite):
 
538
        if filter_re.search(test.id()):
 
539
            result.addTest(test)
 
540
    return result
 
541
 
 
542
 
 
543
def run_suite(suite, name='test', verbose=False, pattern=".*",
 
544
              stop_on_failure=False, keep_output=False):
 
545
    TestCaseInTempDir._TEST_NAME = name
 
546
    if verbose:
 
547
        verbosity = 2
 
548
    else:
 
549
        verbosity = 1
 
550
    runner = TextTestRunner(stream=sys.stdout,
 
551
                            descriptions=0,
 
552
                            verbosity=verbosity)
 
553
    runner.stop_on_failure=stop_on_failure
 
554
    if pattern != '.*':
 
555
        suite = filter_suite_by_re(suite, pattern)
 
556
    result = runner.run(suite)
 
557
    # This is still a little bogus, 
 
558
    # but only a little. Folk not using our testrunner will
 
559
    # have to delete their temp directories themselves.
 
560
    if result.wasSuccessful() or not keep_output:
 
561
        if TestCaseInTempDir.TEST_ROOT is not None:
 
562
            shutil.rmtree(TestCaseInTempDir.TEST_ROOT) 
 
563
    else:
 
564
        print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
 
565
    return result.wasSuccessful()
 
566
 
 
567
 
 
568
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
 
569
             keep_output=False):
 
570
    """Run the whole test suite under the enhanced runner"""
 
571
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern,
 
572
                     stop_on_failure=stop_on_failure, keep_output=keep_output)
 
573
 
 
574
 
 
575
def test_suite():
 
576
    """Build and return TestSuite for the whole program."""
 
577
    import bzrlib.store, bzrlib.inventory, bzrlib.branch
 
578
    import bzrlib.osutils, bzrlib.merge3, bzrlib.plugin
 
579
    from doctest import DocTestSuite
 
580
 
 
581
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
 
582
 
 
583
    # FIXME: If these fail to load, e.g. because of a syntax error, the
 
584
    # exception is hidden by unittest.  Sucks.  Should either fix that or
 
585
    # perhaps import them and pass them to unittest as modules.
 
586
    testmod_names = \
 
587
                  ['bzrlib.selftest.MetaTestLog',
 
588
                   'bzrlib.selftest.testapi',
 
589
                   'bzrlib.selftest.testgpg',
 
590
                   'bzrlib.selftest.testidentitymap',
 
591
                   'bzrlib.selftest.testinv',
 
592
                   'bzrlib.selftest.test_ancestry',
 
593
                   'bzrlib.selftest.test_commit',
 
594
                   'bzrlib.selftest.test_command',
 
595
                   'bzrlib.selftest.test_commit_merge',
 
596
                   'bzrlib.selftest.testconfig',
 
597
                   'bzrlib.selftest.versioning',
 
598
                   'bzrlib.selftest.testmerge3',
 
599
                   'bzrlib.selftest.testmerge',
 
600
                   'bzrlib.selftest.testhashcache',
 
601
                   'bzrlib.selftest.teststatus',
 
602
                   'bzrlib.selftest.testlog',
 
603
                   'bzrlib.selftest.testrevisionnamespaces',
 
604
                   'bzrlib.selftest.testbranch',
 
605
                   'bzrlib.selftest.testrevision',
 
606
                   'bzrlib.selftest.test_revision_info',
 
607
                   'bzrlib.selftest.test_merge_core',
 
608
                   'bzrlib.selftest.test_smart_add',
 
609
                   'bzrlib.selftest.test_bad_files',
 
610
                   'bzrlib.selftest.testdiff',
 
611
                   'bzrlib.selftest.test_parent',
 
612
                   'bzrlib.selftest.test_xml',
 
613
                   'bzrlib.selftest.test_weave',
 
614
                   'bzrlib.selftest.testfetch',
 
615
                   'bzrlib.selftest.whitebox',
 
616
                   'bzrlib.selftest.teststore',
 
617
                   'bzrlib.selftest.blackbox',
 
618
                   'bzrlib.selftest.testsampler',
 
619
                   'bzrlib.selftest.testtransactions',
 
620
                   'bzrlib.selftest.testtransport',
 
621
                   'bzrlib.selftest.testsftp',
 
622
                   'bzrlib.selftest.testgraph',
 
623
                   'bzrlib.selftest.testworkingtree',
 
624
                   'bzrlib.selftest.test_upgrade',
 
625
                   'bzrlib.selftest.test_conflicts',
 
626
                   'bzrlib.selftest.testtestament',
 
627
                   'bzrlib.selftest.testannotate',
 
628
                   'bzrlib.selftest.testrevprops',
 
629
                   'bzrlib.selftest.testoptions',
 
630
                   'bzrlib.selftest.testhttp',
 
631
                   'bzrlib.selftest.testnonascii',
 
632
                   'bzrlib.selftest.testreweave',
 
633
                   'bzrlib.selftest.testtsort',
 
634
                   'bzrlib.selftest.testtrace',
 
635
                   ]
 
636
 
 
637
    for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
 
638
              bzrlib.osutils, bzrlib.commands, bzrlib.merge3,
 
639
              bzrlib.errors,
 
640
              ):
 
641
        if m not in MODULES_TO_DOCTEST:
 
642
            MODULES_TO_DOCTEST.append(m)
 
643
 
 
644
    TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
 
645
    print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
 
646
    print
 
647
    suite = TestSuite()
 
648
    suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
 
649
    for m in MODULES_TO_TEST:
 
650
         suite.addTest(TestLoader().loadTestsFromModule(m))
 
651
    for m in (MODULES_TO_DOCTEST):
 
652
        suite.addTest(DocTestSuite(m))
 
653
    for p in bzrlib.plugin.all_plugins:
 
654
        if hasattr(p, 'test_suite'):
 
655
            suite.addTest(p.test_suite())
 
656
    return suite
 
657