~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

  • Committer: Aaron Bentley
  • Date: 2005-09-19 02:52:24 UTC
  • mto: (1185.1.29)
  • mto: This revision was merged to the branch mainline in revision 1390.
  • Revision ID: aaron.bentley@utoronto.ca-20050919025224-1cc3c70640086e09
TODO re tests

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
 
from cStringIO import StringIO
19
 
import difflib
20
 
import errno
21
18
import logging
 
19
import unittest
 
20
import tempfile
22
21
import os
23
 
import re
24
 
import shutil
25
22
import sys
26
 
import tempfile
27
 
import unittest
28
 
import time
29
 
import codecs
 
23
import errno
 
24
from warnings import warn
 
25
from cStringIO import StringIO
30
26
 
31
 
import bzrlib.branch
 
27
import testsweet
32
28
import bzrlib.commands
33
 
from bzrlib.errors import BzrError
34
 
import bzrlib.inventory
35
 
import bzrlib.merge3
36
 
import bzrlib.osutils
37
 
import bzrlib.plugin
38
 
import bzrlib.store
 
29
 
39
30
import bzrlib.trace
40
 
from bzrlib.trace import mutter
41
 
from bzrlib.tests.TestUtil import TestLoader, TestSuite
42
 
from bzrlib.tests.treeshape import build_tree_contents
 
31
import bzrlib.fetch
 
32
 
43
33
 
44
34
MODULES_TO_TEST = []
45
 
MODULES_TO_DOCTEST = [
46
 
                      bzrlib.branch,
47
 
                      bzrlib.commands,
48
 
                      bzrlib.errors,
49
 
                      bzrlib.inventory,
50
 
                      bzrlib.merge3,
51
 
                      bzrlib.osutils,
52
 
                      bzrlib.store,
53
 
                      ]
54
 
def packages_to_test():
55
 
    import bzrlib.tests.blackbox
56
 
    return [
57
 
            bzrlib.tests.blackbox
58
 
            ]
59
 
 
60
 
 
61
 
class EarlyStoppingTestResultAdapter(object):
62
 
    """An adapter for TestResult to stop at the first first failure or error"""
63
 
 
64
 
    def __init__(self, result):
65
 
        self._result = result
66
 
 
67
 
    def addError(self, test, err):
68
 
        self._result.addError(test, err)
69
 
        self._result.stop()
70
 
 
71
 
    def addFailure(self, test, err):
72
 
        self._result.addFailure(test, err)
73
 
        self._result.stop()
74
 
 
75
 
    def __getattr__(self, name):
76
 
        return getattr(self._result, name)
77
 
 
78
 
    def __setattr__(self, name, value):
79
 
        if name == '_result':
80
 
            object.__setattr__(self, name, value)
81
 
        return setattr(self._result, name, value)
82
 
 
83
 
 
84
 
class _MyResult(unittest._TextTestResult):
85
 
    """Custom TestResult.
86
 
 
87
 
    Shows output in a different format, including displaying runtime for tests.
88
 
    """
89
 
 
90
 
    def _elapsedTime(self):
91
 
        return "%5dms" % (1000 * (time.time() - self._start_time))
92
 
 
93
 
    def startTest(self, test):
94
 
        unittest.TestResult.startTest(self, test)
95
 
        # In a short description, the important words are in
96
 
        # the beginning, but in an id, the important words are
97
 
        # at the end
98
 
        SHOW_DESCRIPTIONS = False
99
 
        if self.showAll:
100
 
            width = bzrlib.osutils.terminal_width()
101
 
            name_width = width - 15
102
 
            what = None
103
 
            if SHOW_DESCRIPTIONS:
104
 
                what = test.shortDescription()
105
 
                if what:
106
 
                    if len(what) > name_width:
107
 
                        what = what[:name_width-3] + '...'
108
 
            if what is None:
109
 
                what = test.id()
110
 
                if what.startswith('bzrlib.tests.'):
111
 
                    what = what[13:]
112
 
                if len(what) > name_width:
113
 
                    what = '...' + what[3-name_width:]
114
 
            what = what.ljust(name_width)
115
 
            self.stream.write(what)
116
 
        self.stream.flush()
117
 
        self._start_time = time.time()
118
 
 
119
 
    def addError(self, test, err):
120
 
        unittest.TestResult.addError(self, test, err)
121
 
        if self.showAll:
122
 
            self.stream.writeln("ERROR %s" % self._elapsedTime())
123
 
        elif self.dots:
124
 
            self.stream.write('E')
125
 
        self.stream.flush()
126
 
 
127
 
    def addFailure(self, test, err):
128
 
        unittest.TestResult.addFailure(self, test, err)
129
 
        if self.showAll:
130
 
            self.stream.writeln(" FAIL %s" % self._elapsedTime())
131
 
        elif self.dots:
132
 
            self.stream.write('F')
133
 
        self.stream.flush()
134
 
 
135
 
    def addSuccess(self, test):
136
 
        if self.showAll:
137
 
            self.stream.writeln('   OK %s' % self._elapsedTime())
138
 
        elif self.dots:
139
 
            self.stream.write('~')
140
 
        self.stream.flush()
141
 
        unittest.TestResult.addSuccess(self, test)
142
 
 
143
 
    def printErrorList(self, flavour, errors):
144
 
        for test, err in errors:
145
 
            self.stream.writeln(self.separator1)
146
 
            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
147
 
            if hasattr(test, '_get_log'):
148
 
                self.stream.writeln()
149
 
                self.stream.writeln('log from this test:')
150
 
                print >>self.stream, test._get_log()
151
 
            self.stream.writeln(self.separator2)
152
 
            self.stream.writeln("%s" % err)
153
 
 
154
 
 
155
 
class TextTestRunner(unittest.TextTestRunner):
156
 
    stop_on_failure = False
157
 
 
158
 
    def _makeResult(self):
159
 
        result = _MyResult(self.stream, self.descriptions, self.verbosity)
160
 
        if self.stop_on_failure:
161
 
            result = EarlyStoppingTestResultAdapter(result)
162
 
        return result
163
 
 
164
 
 
165
 
def iter_suite_tests(suite):
166
 
    """Return all tests in a suite, recursing through nested suites"""
167
 
    for item in suite._tests:
168
 
        if isinstance(item, unittest.TestCase):
169
 
            yield item
170
 
        elif isinstance(item, unittest.TestSuite):
171
 
            for r in iter_suite_tests(item):
172
 
                yield r
173
 
        else:
174
 
            raise Exception('unknown object %r inside test suite %r'
175
 
                            % (item, suite))
176
 
 
177
 
 
178
 
class TestSkipped(Exception):
179
 
    """Indicates that a test was intentionally skipped, rather than failing."""
180
 
    # XXX: Not used yet
181
 
 
 
35
MODULES_TO_DOCTEST = []
 
36
 
 
37
from logging import debug, warning, error
182
38
 
183
39
class CommandFailed(Exception):
184
40
    pass
191
47
 
192
48
    Error and debug log messages are redirected from their usual
193
49
    location into a temporary file, the contents of which can be
194
 
    retrieved by _get_log().  We use a real OS file, not an in-memory object,
195
 
    so that it can also capture file IO.  When the test completes this file
196
 
    is read into memory and removed from disk.
 
50
    retrieved by _get_log().
197
51
       
198
52
    There are also convenience functions to invoke bzr's command-line
199
 
    routine, and to build and check bzr trees.
200
 
   
201
 
    In addition to the usual method of overriding tearDown(), this class also
202
 
    allows subclasses to register functions into the _cleanups list, which is
203
 
    run in order as the object is torn down.  It's less likely this will be
204
 
    accidentally overlooked.
205
 
    """
 
53
    routine, and to build and check bzr trees."""
206
54
 
207
55
    BZRPATH = 'bzr'
208
 
    _log_file_name = None
209
 
    _log_contents = ''
210
56
 
211
57
    def setUp(self):
 
58
        # this replaces the default testsweet.TestCase; we don't want logging changed
212
59
        unittest.TestCase.setUp(self)
213
 
        self._cleanups = []
214
 
        self._cleanEnvironment()
215
60
        bzrlib.trace.disable_default_logging()
216
 
        self._startLogFile()
217
 
 
218
 
    def _ndiff_strings(self, a, b):
219
 
        """Return ndiff between two strings containing lines.
220
 
        
221
 
        A trailing newline is added if missing to make the strings
222
 
        print properly."""
223
 
        if b and b[-1] != '\n':
224
 
            b += '\n'
225
 
        if a and a[-1] != '\n':
226
 
            a += '\n'
227
 
        difflines = difflib.ndiff(a.splitlines(True),
228
 
                                  b.splitlines(True),
229
 
                                  linejunk=lambda x: False,
230
 
                                  charjunk=lambda x: False)
231
 
        return ''.join(difflines)
232
 
 
233
 
    def assertEqualDiff(self, a, b):
234
 
        """Assert two texts are equal, if not raise an exception.
235
 
        
236
 
        This is intended for use with multi-line strings where it can 
237
 
        be hard to find the differences by eye.
238
 
        """
239
 
        # TODO: perhaps override assertEquals to call this for strings?
240
 
        if a == b:
241
 
            return
242
 
        raise AssertionError("texts not equal:\n" + 
243
 
                             self._ndiff_strings(a, b))      
244
 
 
245
 
    def assertContainsRe(self, haystack, needle_re):
246
 
        """Assert that a contains something matching a regular expression."""
247
 
        if not re.search(needle_re, haystack):
248
 
            raise AssertionError('pattern "%s" not found in "%s"'
249
 
                    % (needle_re, haystack))
250
 
 
251
 
    def _startLogFile(self):
252
 
        """Send bzr and test log messages to a temporary file.
253
 
 
254
 
        The file is removed as the test is torn down.
255
 
        """
 
61
        self._enable_file_logging()
 
62
 
 
63
 
 
64
    def _enable_file_logging(self):
256
65
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
257
 
        encoder, decoder, stream_reader, stream_writer = codecs.lookup('UTF-8')
258
 
        self._log_file = stream_writer(os.fdopen(fileno, 'w+'))
259
 
        bzrlib.trace.enable_test_log(self._log_file)
 
66
 
 
67
        self._log_file = os.fdopen(fileno, 'w+')
 
68
 
 
69
        hdlr = logging.StreamHandler(self._log_file)
 
70
        hdlr.setLevel(logging.DEBUG)
 
71
        hdlr.setFormatter(logging.Formatter('%(levelname)4.4s  %(message)s'))
 
72
        logging.getLogger('').addHandler(hdlr)
 
73
        logging.getLogger('').setLevel(logging.DEBUG)
 
74
        self._log_hdlr = hdlr
 
75
        debug('opened log file %s', name)
 
76
        
260
77
        self._log_file_name = name
261
 
        self.addCleanup(self._finishLogFile)
262
 
 
263
 
    def _finishLogFile(self):
264
 
        """Finished with the log file.
265
 
 
266
 
        Read contents into memory, close, and delete.
267
 
        """
268
 
        bzrlib.trace.disable_test_log()
269
 
        self._log_file.seek(0)
270
 
        self._log_contents = self._log_file.read()
 
78
 
 
79
    def tearDown(self):
 
80
        logging.getLogger('').removeHandler(self._log_hdlr)
 
81
        bzrlib.trace.enable_default_logging()
 
82
        logging.debug('%s teardown', self.id())
271
83
        self._log_file.close()
272
 
        os.remove(self._log_file_name)
273
 
        self._log_file = self._log_file_name = None
274
 
 
275
 
    def addCleanup(self, callable):
276
 
        """Arrange to run a callable when this case is torn down.
277
 
 
278
 
        Callables are run in the reverse of the order they are registered, 
279
 
        ie last-in first-out.
280
 
        """
281
 
        if callable in self._cleanups:
282
 
            raise ValueError("cleanup function %r already registered on %s" 
283
 
                    % (callable, self))
284
 
        self._cleanups.append(callable)
285
 
 
286
 
    def _cleanEnvironment(self):
287
 
        new_env = {
288
 
            'HOME': os.getcwd(),
289
 
            'APPDATA': os.getcwd(),
290
 
            'BZREMAIL': None,
291
 
            'EMAIL': None,
292
 
        }
293
 
        self.__old_env = {}
294
 
        self.addCleanup(self._restoreEnvironment)
295
 
        for name, value in new_env.iteritems():
296
 
            self._captureVar(name, value)
297
 
 
298
 
 
299
 
    def _captureVar(self, name, newvalue):
300
 
        """Set an environment variable, preparing it to be reset when finished."""
301
 
        self.__old_env[name] = os.environ.get(name, None)
302
 
        if newvalue is None:
303
 
            if name in os.environ:
304
 
                del os.environ[name]
305
 
        else:
306
 
            os.environ[name] = newvalue
307
 
 
308
 
    @staticmethod
309
 
    def _restoreVar(name, value):
310
 
        if value is None:
311
 
            if name in os.environ:
312
 
                del os.environ[name]
313
 
        else:
314
 
            os.environ[name] = value
315
 
 
316
 
    def _restoreEnvironment(self):
317
 
        for name, value in self.__old_env.iteritems():
318
 
            self._restoreVar(name, value)
319
 
 
320
 
    def tearDown(self):
321
 
        self._runCleanups()
322
84
        unittest.TestCase.tearDown(self)
323
85
 
324
 
    def _runCleanups(self):
325
 
        """Run registered cleanup functions. 
326
 
 
327
 
        This should only be called from TestCase.tearDown.
328
 
        """
329
 
        for cleanup_fn in reversed(self._cleanups):
330
 
            cleanup_fn()
331
 
 
332
86
    def log(self, *args):
333
 
        mutter(*args)
 
87
        logging.debug(*args)
334
88
 
335
89
    def _get_log(self):
336
90
        """Return as a string the log for this test"""
337
 
        if self._log_file_name:
338
 
            return open(self._log_file_name).read()
339
 
        else:
340
 
            return self._log_contents
341
 
        # TODO: Delete the log after it's been read in
342
 
 
343
 
    def capture(self, cmd, retcode=0):
 
91
        return open(self._log_file_name).read()
 
92
 
 
93
 
 
94
    def capture(self, cmd):
344
95
        """Shortcut that splits cmd into words, runs, and returns stdout"""
345
 
        return self.run_bzr_captured(cmd.split(), retcode=retcode)[0]
 
96
        return self.run_bzr_captured(cmd.split())[0]
 
97
 
346
98
 
347
99
    def run_bzr_captured(self, argv, retcode=0):
348
 
        """Invoke bzr and return (stdout, stderr).
 
100
        """Invoke bzr and return (result, stdout, stderr).
349
101
 
350
102
        Useful for code that wants to check the contents of the
351
103
        output, the way error messages are presented, etc.
367
119
        stdout = StringIO()
368
120
        stderr = StringIO()
369
121
        self.log('run bzr: %s', ' '.join(argv))
370
 
        # FIXME: don't call into logging here
371
122
        handler = logging.StreamHandler(stderr)
372
123
        handler.setFormatter(bzrlib.trace.QuietFormatter())
373
124
        handler.setLevel(logging.INFO)
389
140
            self.assertEquals(result, retcode)
390
141
        return out, err
391
142
 
 
143
 
392
144
    def run_bzr(self, *args, **kwargs):
393
145
        """Invoke bzr, as if it were run from the command line.
394
146
 
402
154
        retcode = kwargs.pop('retcode', 0)
403
155
        return self.run_bzr_captured(args, retcode)
404
156
 
 
157
 
405
158
    def check_inventory_shape(self, inv, shape):
406
 
        """Compare an inventory to a list of expected names.
 
159
        """
 
160
        Compare an inventory to a list of expected names.
407
161
 
408
162
        Fail if they are not precisely equal.
409
163
        """
481
235
        if contents != expect:
482
236
            self.log("expected: %r" % expect)
483
237
            self.log("actually: %r" % contents)
484
 
            self.fail("contents of %s not as expected" % filename)
 
238
            self.fail("contents of %s not as expected")
485
239
 
486
240
    def _make_test_root(self):
487
241
        if TestCaseInTempDir.TEST_ROOT is not None:
488
242
            return
489
243
        i = 0
490
244
        while True:
491
 
            root = u'test%04d.tmp' % i
 
245
            root = 'test%04d.tmp' % i
492
246
            try:
493
247
                os.mkdir(root)
494
248
            except OSError, e:
507
261
    def setUp(self):
508
262
        super(TestCaseInTempDir, self).setUp()
509
263
        self._make_test_root()
510
 
        _currentdir = os.getcwdu()
511
 
        short_id = self.id().replace('bzrlib.tests.', '') \
512
 
                   .replace('__main__.', '')
513
 
        self.test_dir = os.path.join(self.TEST_ROOT, short_id)
 
264
        self._currentdir = os.getcwdu()
 
265
        self.test_dir = os.path.join(self.TEST_ROOT, self.id())
514
266
        os.mkdir(self.test_dir)
515
267
        os.chdir(self.test_dir)
516
 
        os.environ['HOME'] = self.test_dir
517
 
        def _leaveDirectory():
518
 
            os.chdir(_currentdir)
519
 
        self.addCleanup(_leaveDirectory)
520
268
        
521
 
    def build_tree(self, shape, line_endings='native'):
 
269
    def tearDown(self):
 
270
        os.chdir(self._currentdir)
 
271
        super(TestCaseInTempDir, self).tearDown()
 
272
 
 
273
    
 
274
    def build_tree(self, shape):
522
275
        """Build a test tree according to a pattern.
523
276
 
524
277
        shape is a sequence of file specifications.  If the final
525
278
        character is '/', a directory is created.
526
279
 
527
280
        This doesn't add anything to a branch.
528
 
        :param line_endings: Either 'binary' or 'native'
529
 
                             in binary mode, exact contents are written
530
 
                             in native mode, the line endings match the
531
 
                             default platform endings.
532
281
        """
533
282
        # XXX: It's OK to just create them using forward slashes on windows?
534
283
        for name in shape:
535
 
            self.assert_(isinstance(name, basestring))
 
284
            assert isinstance(name, basestring)
536
285
            if name[-1] == '/':
537
286
                os.mkdir(name[:-1])
538
287
            else:
539
 
                if line_endings == 'binary':
540
 
                    f = file(name, 'wb')
541
 
                elif line_endings == 'native':
542
 
                    f = file(name, 'wt')
543
 
                else:
544
 
                    raise BzrError('Invalid line ending request %r' % (line_endings,))
 
288
                f = file(name, 'wt')
545
289
                print >>f, "contents of", name
546
290
                f.close()
547
 
 
548
 
    def build_tree_contents(self, shape):
549
 
        build_tree_contents(shape)
550
 
 
551
 
    def failUnlessExists(self, path):
552
 
        """Fail unless path, which may be abs or relative, exists."""
553
 
        self.failUnless(bzrlib.osutils.lexists(path))
554
 
        
555
 
    def assertFileEqual(self, content, path):
556
 
        """Fail if path does not contain 'content'."""
557
 
        self.failUnless(bzrlib.osutils.lexists(path))
558
 
        self.assertEqualDiff(content, open(path, 'r').read())
559
 
        
560
 
 
561
 
def filter_suite_by_re(suite, pattern):
562
 
    result = TestSuite()
563
 
    filter_re = re.compile(pattern)
564
 
    for test in iter_suite_tests(suite):
565
 
        if filter_re.search(test.id()):
566
 
            result.addTest(test)
567
 
    return result
568
 
 
569
 
 
570
 
def run_suite(suite, name='test', verbose=False, pattern=".*",
571
 
              stop_on_failure=False, keep_output=False):
572
 
    TestCaseInTempDir._TEST_NAME = name
573
 
    if verbose:
574
 
        verbosity = 2
575
 
    else:
576
 
        verbosity = 1
577
 
    runner = TextTestRunner(stream=sys.stdout,
578
 
                            descriptions=0,
579
 
                            verbosity=verbosity)
580
 
    runner.stop_on_failure=stop_on_failure
581
 
    if pattern != '.*':
582
 
        suite = filter_suite_by_re(suite, pattern)
583
 
    result = runner.run(suite)
584
 
    # This is still a little bogus, 
585
 
    # but only a little. Folk not using our testrunner will
586
 
    # have to delete their temp directories themselves.
587
 
    if result.wasSuccessful() or not keep_output:
588
 
        if TestCaseInTempDir.TEST_ROOT is not None:
589
 
            shutil.rmtree(TestCaseInTempDir.TEST_ROOT) 
590
 
    else:
591
 
        print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
592
 
    return result.wasSuccessful()
593
 
 
594
 
 
595
 
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
596
 
             keep_output=False):
597
 
    """Run the whole test suite under the enhanced runner"""
598
 
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern,
599
 
                     stop_on_failure=stop_on_failure, keep_output=keep_output)
 
291
                
 
292
 
 
293
 
 
294
class MetaTestLog(TestCase):
 
295
    def test_logging(self):
 
296
        """Test logs are captured when a test fails."""
 
297
        logging.info('an info message')
 
298
        warning('something looks dodgy...')
 
299
        logging.debug('hello, test is running')
 
300
        ##assert 0
 
301
 
 
302
 
 
303
def selftest(verbose=False, pattern=".*"):
 
304
    return testsweet.run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
600
305
 
601
306
 
602
307
def test_suite():
603
 
    """Build and return TestSuite for the whole program."""
 
308
    from bzrlib.selftest.TestUtil import TestLoader, TestSuite
 
309
    import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
 
310
    import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
604
311
    from doctest import DocTestSuite
605
312
 
606
 
    global MODULES_TO_DOCTEST
 
313
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
607
314
 
608
 
    testmod_names = [ \
609
 
                   'bzrlib.tests.test_api',
610
 
                   'bzrlib.tests.test_gpg',
611
 
                   'bzrlib.tests.test_identitymap',
612
 
                   'bzrlib.tests.test_inv',
613
 
                   'bzrlib.tests.test_ancestry',
614
 
                   'bzrlib.tests.test_commit',
615
 
                   'bzrlib.tests.test_command',
616
 
                   'bzrlib.tests.test_commit_merge',
617
 
                   'bzrlib.tests.test_config',
618
 
                   'bzrlib.tests.test_merge3',
619
 
                   'bzrlib.tests.test_merge',
620
 
                   'bzrlib.tests.test_hashcache',
621
 
                   'bzrlib.tests.test_status',
622
 
                   'bzrlib.tests.test_log',
623
 
                   'bzrlib.tests.test_revisionnamespaces',
624
 
                   'bzrlib.tests.test_branch',
625
 
                   'bzrlib.tests.test_revision',
626
 
                   'bzrlib.tests.test_revision_info',
627
 
                   'bzrlib.tests.test_merge_core',
628
 
                   'bzrlib.tests.test_smart_add',
629
 
                   'bzrlib.tests.test_bad_files',
630
 
                   'bzrlib.tests.test_diff',
631
 
                   'bzrlib.tests.test_parent',
632
 
                   'bzrlib.tests.test_xml',
633
 
                   'bzrlib.tests.test_weave',
634
 
                   'bzrlib.tests.test_fetch',
635
 
                   'bzrlib.tests.test_whitebox',
636
 
                   'bzrlib.tests.test_store',
637
 
                   'bzrlib.tests.test_sampler',
638
 
                   'bzrlib.tests.test_transactions',
639
 
                   'bzrlib.tests.test_transport',
640
 
                   'bzrlib.tests.test_sftp',
641
 
                   'bzrlib.tests.test_graph',
642
 
                   'bzrlib.tests.test_workingtree',
643
 
                   'bzrlib.tests.test_upgrade',
644
 
                   'bzrlib.tests.test_uncommit',
645
 
                   'bzrlib.tests.test_ui',
646
 
                   'bzrlib.tests.test_conflicts',
647
 
                   'bzrlib.tests.test_testament',
648
 
                   'bzrlib.tests.test_annotate',
649
 
                   'bzrlib.tests.test_revprops',
650
 
                   'bzrlib.tests.test_options',
651
 
                   'bzrlib.tests.test_http',
652
 
                   'bzrlib.tests.test_nonascii',
653
 
                   'bzrlib.tests.test_plugins',
654
 
                   'bzrlib.tests.test_reweave',
655
 
                   'bzrlib.tests.test_tsort',
656
 
                   'bzrlib.tests.test_trace',
657
 
                   'bzrlib.tests.test_rio',
658
 
                   'bzrlib.tests.test_msgeditor',
659
 
                   'bzrlib.tests.test_selftest',
 
315
    testmod_names = \
 
316
                  ['bzrlib.selftest.MetaTestLog',
 
317
                   'bzrlib.selftest.test_parent',
 
318
                   'bzrlib.selftest.testinv',
 
319
                   'bzrlib.selftest.testfetch',
 
320
                   'bzrlib.selftest.versioning',
 
321
                   'bzrlib.selftest.whitebox',
 
322
                   'bzrlib.selftest.testmerge3',
 
323
                   'bzrlib.selftest.testmerge',
 
324
                   'bzrlib.selftest.testhashcache',
 
325
                   'bzrlib.selftest.teststatus',
 
326
                   'bzrlib.selftest.testlog',
 
327
                   'bzrlib.selftest.blackbox',
 
328
                   'bzrlib.selftest.testrevisionnamespaces',
 
329
                   'bzrlib.selftest.testbranch',
 
330
                   'bzrlib.selftest.testremotebranch',
 
331
                   'bzrlib.selftest.testrevision',
 
332
                   'bzrlib.selftest.test_merge_core',
 
333
                   'bzrlib.selftest.test_smart_add',
 
334
                   'bzrlib.selftest.testdiff',
 
335
                   'bzrlib.selftest.test_xml',
 
336
                   'bzrlib.fetch',
 
337
                   'bzrlib.selftest.teststore',
 
338
                   'bzrlib.selftest.testgraph',
660
339
                   ]
661
340
 
662
 
    print '%10s: %s' % ('bzr', os.path.realpath(sys.argv[0]))
663
 
    print '%10s: %s' % ('bzrlib', bzrlib.__path__[0])
 
341
    for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
 
342
              bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
 
343
        if m not in MODULES_TO_DOCTEST:
 
344
            MODULES_TO_DOCTEST.append(m)
 
345
 
 
346
    TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
 
347
    print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
664
348
    print
665
349
    suite = TestSuite()
666
 
    # python2.4's TestLoader.loadTestsFromNames gives very poor 
667
 
    # errors if it fails to load a named module - no indication of what's
668
 
    # actually wrong, just "no such module".  We should probably override that
669
 
    # class, but for the moment just load them ourselves. (mbp 20051202)
670
 
    loader = TestLoader()
671
 
    for mod_name in testmod_names:
672
 
        mod = _load_module_by_name(mod_name)
673
 
        suite.addTest(loader.loadTestsFromModule(mod))
674
 
    for package in packages_to_test():
675
 
        suite.addTest(package.test_suite())
 
350
    suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
676
351
    for m in MODULES_TO_TEST:
677
 
        suite.addTest(loader.loadTestsFromModule(m))
 
352
         suite.addTest(TestLoader().loadTestsFromModule(m))
678
353
    for m in (MODULES_TO_DOCTEST):
679
354
        suite.addTest(DocTestSuite(m))
680
 
    for name, plugin in bzrlib.plugin.all_plugins().items():
681
 
        if hasattr(plugin, 'test_suite'):
682
 
            suite.addTest(plugin.test_suite())
 
355
    for p in bzrlib.plugin.all_plugins:
 
356
        if hasattr(p, 'test_suite'):
 
357
            suite.addTest(p.test_suite())
683
358
    return suite
684
359
 
685
 
 
686
 
def _load_module_by_name(mod_name):
687
 
    parts = mod_name.split('.')
688
 
    module = __import__(mod_name)
689
 
    del parts[0]
690
 
    # for historical reasons python returns the top-level module even though
691
 
    # it loads the submodule; we need to walk down to get the one we want.
692
 
    while parts:
693
 
        module = getattr(module, parts.pop(0))
694
 
    return module