~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

  • Committer: mbp at sourcefrog
  • Date: 2005-04-01 04:04:04 UTC
  • Revision ID: mbp@sourcefrog.net-20050401040404-c5cd90f539e27f91
bzr commit --help now works

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