~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

[merge] jam-integration 1495

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
 
18
18
from cStringIO import StringIO
 
19
import difflib
 
20
import errno
19
21
import logging
20
 
import unittest
21
 
import tempfile
22
22
import os
 
23
import re
 
24
import shutil
23
25
import sys
24
 
import errno
25
 
import subprocess
26
 
import shutil
27
 
import re
 
26
import tempfile
 
27
import unittest
 
28
import time
 
29
import codecs
28
30
 
 
31
import bzrlib.branch
29
32
import bzrlib.commands
 
33
from bzrlib.errors import BzrError
 
34
import bzrlib.inventory
 
35
import bzrlib.merge3
 
36
import bzrlib.osutils
 
37
import bzrlib.osutils as osutils
 
38
import bzrlib.plugin
 
39
import bzrlib.store
30
40
import bzrlib.trace
31
 
import bzrlib.fetch
32
 
from bzrlib.selftest import TestUtil
33
 
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
34
 
 
 
41
from bzrlib.trace import mutter
 
42
from bzrlib.tests.TestUtil import TestLoader, TestSuite
 
43
from bzrlib.tests.treeshape import build_tree_contents
35
44
 
36
45
MODULES_TO_TEST = []
37
 
MODULES_TO_DOCTEST = []
38
 
 
39
 
from logging import debug, warning, error
40
 
 
 
46
MODULES_TO_DOCTEST = [
 
47
                      bzrlib.branch,
 
48
                      bzrlib.commands,
 
49
                      bzrlib.errors,
 
50
                      bzrlib.inventory,
 
51
                      bzrlib.merge3,
 
52
                      bzrlib.osutils,
 
53
                      bzrlib.store,
 
54
                      ]
 
55
def packages_to_test():
 
56
    import bzrlib.tests.blackbox
 
57
    return [
 
58
            bzrlib.tests.blackbox
 
59
            ]
41
60
 
42
61
 
43
62
class EarlyStoppingTestResultAdapter(object):
64
83
 
65
84
 
66
85
class _MyResult(unittest._TextTestResult):
67
 
    """
68
 
    Custom TestResult.
69
 
 
70
 
    No special behaviour for now.
71
 
    """
 
86
    """Custom TestResult.
 
87
 
 
88
    Shows output in a different format, including displaying runtime for tests.
 
89
    """
 
90
 
 
91
    def _elapsedTime(self):
 
92
        return "%5dms" % (1000 * (time.time() - self._start_time))
72
93
 
73
94
    def startTest(self, test):
74
95
        unittest.TestResult.startTest(self, test)
75
 
        # TODO: Maybe show test.shortDescription somewhere?
76
 
        what = test.shortDescription() or test.id()        
 
96
        # In a short description, the important words are in
 
97
        # the beginning, but in an id, the important words are
 
98
        # at the end
 
99
        SHOW_DESCRIPTIONS = False
77
100
        if self.showAll:
78
 
            self.stream.write('%-70.70s' % what)
 
101
            width = osutils.terminal_width()
 
102
            name_width = width - 15
 
103
            what = None
 
104
            if SHOW_DESCRIPTIONS:
 
105
                what = test.shortDescription()
 
106
                if what:
 
107
                    if len(what) > name_width:
 
108
                        what = what[:name_width-3] + '...'
 
109
            if what is None:
 
110
                what = test.id()
 
111
                if what.startswith('bzrlib.tests.'):
 
112
                    what = what[13:]
 
113
                if len(what) > name_width:
 
114
                    what = '...' + what[3-name_width:]
 
115
            what = what.ljust(name_width)
 
116
            self.stream.write(what)
79
117
        self.stream.flush()
 
118
        self._start_time = time.time()
80
119
 
81
120
    def addError(self, test, err):
82
 
        super(_MyResult, self).addError(test, err)
 
121
        if isinstance(err[1], TestSkipped):
 
122
            return self.addSkipped(test, err)    
 
123
        unittest.TestResult.addError(self, test, err)
 
124
        if self.showAll:
 
125
            self.stream.writeln("ERROR %s" % self._elapsedTime())
 
126
        elif self.dots:
 
127
            self.stream.write('E')
83
128
        self.stream.flush()
84
129
 
85
130
    def addFailure(self, test, err):
86
 
        super(_MyResult, self).addFailure(test, err)
 
131
        unittest.TestResult.addFailure(self, test, err)
 
132
        if self.showAll:
 
133
            self.stream.writeln(" FAIL %s" % self._elapsedTime())
 
134
        elif self.dots:
 
135
            self.stream.write('F')
87
136
        self.stream.flush()
88
137
 
89
138
    def addSuccess(self, test):
90
139
        if self.showAll:
91
 
            self.stream.writeln('OK')
 
140
            self.stream.writeln('   OK %s' % self._elapsedTime())
92
141
        elif self.dots:
93
142
            self.stream.write('~')
94
143
        self.stream.flush()
95
144
        unittest.TestResult.addSuccess(self, test)
96
145
 
 
146
    def addSkipped(self, test, skip_excinfo):
 
147
        if self.showAll:
 
148
            print >>self.stream, ' SKIP %s' % self._elapsedTime()
 
149
            print >>self.stream, '     %s' % skip_excinfo[1]
 
150
        elif self.dots:
 
151
            self.stream.write('S')
 
152
        self.stream.flush()
 
153
        # seems best to treat this as success from point-of-view of unittest
 
154
        # -- it actually does nothing so it barely matters :)
 
155
        unittest.TestResult.addSuccess(self, test)
 
156
 
97
157
    def printErrorList(self, flavour, errors):
98
158
        for test, err in errors:
99
159
            self.stream.writeln(self.separator1)
100
160
            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
101
161
            if hasattr(test, '_get_log'):
102
 
                self.stream.writeln()
103
 
                self.stream.writeln('log from this test:')
 
162
                print >>self.stream
 
163
                print >>self.stream, \
 
164
                        ('vvvv[log from %s]' % test).ljust(78,'-')
104
165
                print >>self.stream, test._get_log()
 
166
                print >>self.stream, \
 
167
                        ('^^^^[log from %s]' % test).ljust(78,'-')
105
168
            self.stream.writeln(self.separator2)
106
169
            self.stream.writeln("%s" % err)
107
170
 
108
171
 
109
172
class TextTestRunner(unittest.TextTestRunner):
 
173
    stop_on_failure = False
110
174
 
111
175
    def _makeResult(self):
112
176
        result = _MyResult(self.stream, self.descriptions, self.verbosity)
113
 
        return EarlyStoppingTestResultAdapter(result)
 
177
        if self.stop_on_failure:
 
178
            result = EarlyStoppingTestResultAdapter(result)
 
179
        return result
114
180
 
115
181
 
116
182
def iter_suite_tests(suite):
142
208
 
143
209
    Error and debug log messages are redirected from their usual
144
210
    location into a temporary file, the contents of which can be
145
 
    retrieved by _get_log().
 
211
    retrieved by _get_log().  We use a real OS file, not an in-memory object,
 
212
    so that it can also capture file IO.  When the test completes this file
 
213
    is read into memory and removed from disk.
146
214
       
147
215
    There are also convenience functions to invoke bzr's command-line
148
 
    routine, and to build and check bzr trees."""
 
216
    routine, and to build and check bzr trees.
 
217
   
 
218
    In addition to the usual method of overriding tearDown(), this class also
 
219
    allows subclasses to register functions into the _cleanups list, which is
 
220
    run in order as the object is torn down.  It's less likely this will be
 
221
    accidentally overlooked.
 
222
    """
149
223
 
150
224
    BZRPATH = 'bzr'
 
225
    _log_file_name = None
 
226
    _log_contents = ''
151
227
 
152
228
    def setUp(self):
153
229
        unittest.TestCase.setUp(self)
 
230
        self._cleanups = []
 
231
        self._cleanEnvironment()
154
232
        bzrlib.trace.disable_default_logging()
155
 
        self._enable_file_logging()
156
 
 
157
 
 
158
 
    def _enable_file_logging(self):
 
233
        self._startLogFile()
 
234
 
 
235
    def _ndiff_strings(self, a, b):
 
236
        """Return ndiff between two strings containing lines.
 
237
        
 
238
        A trailing newline is added if missing to make the strings
 
239
        print properly."""
 
240
        if b and b[-1] != '\n':
 
241
            b += '\n'
 
242
        if a and a[-1] != '\n':
 
243
            a += '\n'
 
244
        difflines = difflib.ndiff(a.splitlines(True),
 
245
                                  b.splitlines(True),
 
246
                                  linejunk=lambda x: False,
 
247
                                  charjunk=lambda x: False)
 
248
        return ''.join(difflines)
 
249
 
 
250
    def assertEqualDiff(self, a, b):
 
251
        """Assert two texts are equal, if not raise an exception.
 
252
        
 
253
        This is intended for use with multi-line strings where it can 
 
254
        be hard to find the differences by eye.
 
255
        """
 
256
        # TODO: perhaps override assertEquals to call this for strings?
 
257
        if a == b:
 
258
            return
 
259
        raise AssertionError("texts not equal:\n" + 
 
260
                             self._ndiff_strings(a, b))      
 
261
        
 
262
    def assertStartsWith(self, s, prefix):
 
263
        if not s.startswith(prefix):
 
264
            raise AssertionError('string %r does not start with %r' % (s, prefix))
 
265
 
 
266
    def assertEndsWith(self, s, suffix):
 
267
        if not s.endswith(prefix):
 
268
            raise AssertionError('string %r does not end with %r' % (s, suffix))
 
269
 
 
270
    def assertContainsRe(self, haystack, needle_re):
 
271
        """Assert that a contains something matching a regular expression."""
 
272
        if not re.search(needle_re, haystack):
 
273
            raise AssertionError('pattern "%s" not found in "%s"'
 
274
                    % (needle_re, haystack))
 
275
 
 
276
    def AssertSubset(self, sublist, superlist):
 
277
        """Assert that every entry in sublist is present in superlist."""
 
278
        missing = []
 
279
        for entry in sublist:
 
280
            if entry not in superlist:
 
281
                missing.append(entry)
 
282
        if len(missing) > 0:
 
283
            raise AssertionError("value(s) %r not present in container %r" % 
 
284
                                 (missing, superlist))
 
285
 
 
286
    def _startLogFile(self):
 
287
        """Send bzr and test log messages to a temporary file.
 
288
 
 
289
        The file is removed as the test is torn down.
 
290
        """
159
291
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
160
 
 
161
 
        self._log_file = os.fdopen(fileno, 'w+')
162
 
 
163
 
        hdlr = logging.StreamHandler(self._log_file)
164
 
        hdlr.setLevel(logging.DEBUG)
165
 
        hdlr.setFormatter(logging.Formatter('%(levelname)8s  %(message)s'))
166
 
        logging.getLogger('').addHandler(hdlr)
167
 
        logging.getLogger('').setLevel(logging.DEBUG)
168
 
        self._log_hdlr = hdlr
169
 
        debug('opened log file %s', name)
170
 
        
 
292
        encoder, decoder, stream_reader, stream_writer = codecs.lookup('UTF-8')
 
293
        self._log_file = stream_writer(os.fdopen(fileno, 'w+'))
 
294
        bzrlib.trace.enable_test_log(self._log_file)
171
295
        self._log_file_name = name
172
 
 
173
 
    def tearDown(self):
174
 
        logging.getLogger('').removeHandler(self._log_hdlr)
175
 
        bzrlib.trace.enable_default_logging()
176
 
        logging.debug('%s teardown', self.id())
 
296
        self.addCleanup(self._finishLogFile)
 
297
 
 
298
    def _finishLogFile(self):
 
299
        """Finished with the log file.
 
300
 
 
301
        Read contents into memory, close, and delete.
 
302
        """
 
303
        bzrlib.trace.disable_test_log()
 
304
        self._log_file.seek(0)
 
305
        self._log_contents = self._log_file.read()
177
306
        self._log_file.close()
 
307
        os.remove(self._log_file_name)
 
308
        self._log_file = self._log_file_name = None
 
309
 
 
310
    def addCleanup(self, callable):
 
311
        """Arrange to run a callable when this case is torn down.
 
312
 
 
313
        Callables are run in the reverse of the order they are registered, 
 
314
        ie last-in first-out.
 
315
        """
 
316
        if callable in self._cleanups:
 
317
            raise ValueError("cleanup function %r already registered on %s" 
 
318
                    % (callable, self))
 
319
        self._cleanups.append(callable)
 
320
 
 
321
    def _cleanEnvironment(self):
 
322
        new_env = {
 
323
            'HOME': os.getcwd(),
 
324
            'APPDATA': os.getcwd(),
 
325
            'BZREMAIL': None,
 
326
            'EMAIL': None,
 
327
        }
 
328
        self.__old_env = {}
 
329
        self.addCleanup(self._restoreEnvironment)
 
330
        for name, value in new_env.iteritems():
 
331
            self._captureVar(name, value)
 
332
 
 
333
 
 
334
    def _captureVar(self, name, newvalue):
 
335
        """Set an environment variable, preparing it to be reset when finished."""
 
336
        self.__old_env[name] = os.environ.get(name, None)
 
337
        if newvalue is None:
 
338
            if name in os.environ:
 
339
                del os.environ[name]
 
340
        else:
 
341
            os.environ[name] = newvalue
 
342
 
 
343
    @staticmethod
 
344
    def _restoreVar(name, value):
 
345
        if value is None:
 
346
            if name in os.environ:
 
347
                del os.environ[name]
 
348
        else:
 
349
            os.environ[name] = value
 
350
 
 
351
    def _restoreEnvironment(self):
 
352
        for name, value in self.__old_env.iteritems():
 
353
            self._restoreVar(name, value)
 
354
 
 
355
    def tearDown(self):
 
356
        self._runCleanups()
178
357
        unittest.TestCase.tearDown(self)
179
358
 
 
359
    def _runCleanups(self):
 
360
        """Run registered cleanup functions. 
 
361
 
 
362
        This should only be called from TestCase.tearDown.
 
363
        """
 
364
        for cleanup_fn in reversed(self._cleanups):
 
365
            cleanup_fn()
 
366
 
180
367
    def log(self, *args):
181
 
        logging.debug(*args)
 
368
        mutter(*args)
182
369
 
183
370
    def _get_log(self):
184
371
        """Return as a string the log for this test"""
185
 
        return open(self._log_file_name).read()
186
 
 
187
 
 
188
 
    def capture(self, cmd):
 
372
        if self._log_file_name:
 
373
            return open(self._log_file_name).read()
 
374
        else:
 
375
            return self._log_contents
 
376
        # TODO: Delete the log after it's been read in
 
377
 
 
378
    def capture(self, cmd, retcode=0):
189
379
        """Shortcut that splits cmd into words, runs, and returns stdout"""
190
 
        return self.run_bzr_captured(cmd.split())[0]
 
380
        return self.run_bzr_captured(cmd.split(), retcode=retcode)[0]
191
381
 
192
382
    def run_bzr_captured(self, argv, retcode=0):
193
 
        """Invoke bzr and return (result, stdout, stderr).
 
383
        """Invoke bzr and return (stdout, stderr).
194
384
 
195
385
        Useful for code that wants to check the contents of the
196
386
        output, the way error messages are presented, etc.
212
402
        stdout = StringIO()
213
403
        stderr = StringIO()
214
404
        self.log('run bzr: %s', ' '.join(argv))
 
405
        # FIXME: don't call into logging here
215
406
        handler = logging.StreamHandler(stderr)
216
407
        handler.setFormatter(bzrlib.trace.QuietFormatter())
217
408
        handler.setLevel(logging.INFO)
332
523
            return
333
524
        i = 0
334
525
        while True:
335
 
            root = 'test%04d.tmp' % i
 
526
            root = u'test%04d.tmp' % i
336
527
            try:
337
528
                os.mkdir(root)
338
529
            except OSError, e:
342
533
                else:
343
534
                    raise
344
535
            # successfully created
345
 
            TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
 
536
            TestCaseInTempDir.TEST_ROOT = osutils.abspath(root)
346
537
            break
347
538
        # make a fake bzr directory there to prevent any tests propagating
348
539
        # up onto the source directory's real branch
349
 
        os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
 
540
        os.mkdir(osutils.pathjoin(TestCaseInTempDir.TEST_ROOT, '.bzr'))
350
541
 
351
542
    def setUp(self):
352
543
        super(TestCaseInTempDir, self).setUp()
353
544
        self._make_test_root()
354
 
        self._currentdir = os.getcwdu()
355
 
        short_id = self.id().replace('bzrlib.selftest.', '') \
 
545
        _currentdir = os.getcwdu()
 
546
        short_id = self.id().replace('bzrlib.tests.', '') \
356
547
                   .replace('__main__.', '')
357
 
        self.test_dir = os.path.join(self.TEST_ROOT, short_id)
 
548
        self.test_dir = osutils.pathjoin(self.TEST_ROOT, short_id)
358
549
        os.mkdir(self.test_dir)
359
550
        os.chdir(self.test_dir)
 
551
        os.environ['HOME'] = self.test_dir
 
552
        os.environ['APPDATA'] = self.test_dir
 
553
        def _leaveDirectory():
 
554
            os.chdir(_currentdir)
 
555
        self.addCleanup(_leaveDirectory)
360
556
        
361
 
    def tearDown(self):
362
 
        os.chdir(self._currentdir)
363
 
        super(TestCaseInTempDir, self).tearDown()
364
 
 
365
 
    def build_tree(self, shape):
 
557
    def build_tree(self, shape, line_endings='native'):
366
558
        """Build a test tree according to a pattern.
367
559
 
368
560
        shape is a sequence of file specifications.  If the final
369
561
        character is '/', a directory is created.
370
562
 
371
563
        This doesn't add anything to a branch.
 
564
        :param line_endings: Either 'binary' or 'native'
 
565
                             in binary mode, exact contents are written
 
566
                             in native mode, the line endings match the
 
567
                             default platform endings.
372
568
        """
373
569
        # XXX: It's OK to just create them using forward slashes on windows?
374
570
        for name in shape:
375
 
            assert isinstance(name, basestring)
 
571
            self.assert_(isinstance(name, basestring))
376
572
            if name[-1] == '/':
377
573
                os.mkdir(name[:-1])
378
574
            else:
379
 
                f = file(name, 'wt')
 
575
                if line_endings == 'binary':
 
576
                    f = file(name, 'wb')
 
577
                elif line_endings == 'native':
 
578
                    f = file(name, 'wt')
 
579
                else:
 
580
                    raise BzrError('Invalid line ending request %r' % (line_endings,))
380
581
                print >>f, "contents of", name
381
582
                f.close()
382
583
 
 
584
    def build_tree_contents(self, shape):
 
585
        build_tree_contents(shape)
 
586
 
383
587
    def failUnlessExists(self, path):
384
588
        """Fail unless path, which may be abs or relative, exists."""
385
 
        self.failUnless(os.path.exists(path))
 
589
        self.failUnless(osutils.lexists(path))
 
590
 
 
591
    def failIfExists(self, path):
 
592
        """Fail if path, which may be abs or relative, exists."""
 
593
        self.failIf(osutils.lexists(path))
386
594
        
387
 
 
388
 
class MetaTestLog(TestCase):
389
 
    def test_logging(self):
390
 
        """Test logs are captured when a test fails."""
391
 
        logging.info('an info message')
392
 
        warning('something looks dodgy...')
393
 
        logging.debug('hello, test is running')
394
 
        ##assert 0
 
595
    def assertFileEqual(self, content, path):
 
596
        """Fail if path does not contain 'content'."""
 
597
        self.failUnless(osutils.lexists(path))
 
598
        self.assertEqualDiff(content, open(path, 'r').read())
395
599
 
396
600
 
397
601
def filter_suite_by_re(suite, pattern):
398
 
    result = TestUtil.TestSuite()
 
602
    result = TestSuite()
399
603
    filter_re = re.compile(pattern)
400
604
    for test in iter_suite_tests(suite):
401
 
        if filter_re.match(test.id()):
 
605
        if filter_re.search(test.id()):
402
606
            result.addTest(test)
403
607
    return result
404
608
 
405
609
 
406
 
def filter_suite_by_names(suite, wanted_names):
407
 
    """Return a new suite containing only selected tests.
408
 
    
409
 
    Names are considered to match if any name is a substring of the 
410
 
    fully-qualified test id (i.e. the class ."""
411
 
    result = TestSuite()
412
 
    for test in iter_suite_tests(suite):
413
 
        this_id = test.id()
414
 
        for p in wanted_names:
415
 
            if this_id.find(p) != -1:
416
 
                result.addTest(test)
417
 
    return result
418
 
 
419
 
 
420
 
def run_suite(suite, name='test', verbose=False, pattern=".*", testnames=None):
 
610
def run_suite(suite, name='test', verbose=False, pattern=".*",
 
611
              stop_on_failure=False, keep_output=False):
421
612
    TestCaseInTempDir._TEST_NAME = name
422
613
    if verbose:
423
614
        verbosity = 2
426
617
    runner = TextTestRunner(stream=sys.stdout,
427
618
                            descriptions=0,
428
619
                            verbosity=verbosity)
429
 
    if testnames:
430
 
        suite = filter_suite_by_names(suite, testnames)
 
620
    runner.stop_on_failure=stop_on_failure
431
621
    if pattern != '.*':
432
622
        suite = filter_suite_by_re(suite, pattern)
433
623
    result = runner.run(suite)
434
624
    # This is still a little bogus, 
435
625
    # but only a little. Folk not using our testrunner will
436
626
    # have to delete their temp directories themselves.
437
 
    if result.wasSuccessful():
 
627
    if result.wasSuccessful() or not keep_output:
438
628
        if TestCaseInTempDir.TEST_ROOT is not None:
439
629
            shutil.rmtree(TestCaseInTempDir.TEST_ROOT) 
440
630
    else:
442
632
    return result.wasSuccessful()
443
633
 
444
634
 
445
 
def selftest(verbose=False, pattern=".*", testnames=None):
 
635
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
 
636
             keep_output=False):
446
637
    """Run the whole test suite under the enhanced runner"""
447
638
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern,
448
 
                     testnames=testnames)
 
639
                     stop_on_failure=stop_on_failure, keep_output=keep_output)
449
640
 
450
641
 
451
642
def test_suite():
452
643
    """Build and return TestSuite for the whole program."""
453
 
    import bzrlib.store, bzrlib.inventory, bzrlib.branch
454
 
    import bzrlib.osutils, bzrlib.merge3, bzrlib.plugin
455
644
    from doctest import DocTestSuite
456
645
 
457
 
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
 
646
    global MODULES_TO_DOCTEST
458
647
 
459
 
    testmod_names = \
460
 
                  ['bzrlib.selftest.MetaTestLog',
461
 
                   'bzrlib.selftest.testinv',
462
 
                   'bzrlib.selftest.test_ancestry',
463
 
                   'bzrlib.selftest.test_commit',
464
 
                   'bzrlib.selftest.test_commit_merge',
465
 
                   'bzrlib.selftest.versioning',
466
 
                   'bzrlib.selftest.testmerge3',
467
 
                   'bzrlib.selftest.testmerge',
468
 
                   'bzrlib.selftest.testhashcache',
469
 
                   'bzrlib.selftest.teststatus',
470
 
                   'bzrlib.selftest.testlog',
471
 
                   'bzrlib.selftest.testrevisionnamespaces',
472
 
                   'bzrlib.selftest.testbranch',
473
 
                   'bzrlib.selftest.testrevision',
474
 
                   'bzrlib.selftest.test_revision_info',
475
 
                   'bzrlib.selftest.test_merge_core',
476
 
                   'bzrlib.selftest.test_smart_add',
477
 
                   'bzrlib.selftest.test_bad_files',
478
 
                   'bzrlib.selftest.testdiff',
479
 
                   'bzrlib.selftest.test_parent',
480
 
                   'bzrlib.selftest.test_xml',
481
 
                   'bzrlib.selftest.test_weave',
482
 
                   'bzrlib.selftest.testfetch',
483
 
                   'bzrlib.selftest.whitebox',
484
 
                   'bzrlib.selftest.teststore',
485
 
                   'bzrlib.selftest.blackbox',
486
 
                   'bzrlib.selftest.testsampler',
487
 
                   'bzrlib.selftest.testtransport',
488
 
                   'bzrlib.selftest.testgraph',
489
 
                   'bzrlib.selftest.testworkingtree',
490
 
                   'bzrlib.selftest.test_upgrade',
491
 
                   'bzrlib.selftest.test_conflicts',
 
648
    testmod_names = [ \
 
649
                   'bzrlib.tests.test_ancestry',
 
650
                   'bzrlib.tests.test_annotate',
 
651
                   'bzrlib.tests.test_api',
 
652
                   'bzrlib.tests.test_bad_files',
 
653
                   'bzrlib.tests.test_basis_inventory',
 
654
                   'bzrlib.tests.test_bound_sftp',
 
655
                   'bzrlib.tests.test_branch',
 
656
                   'bzrlib.tests.test_command',
 
657
                   'bzrlib.tests.test_commit',
 
658
                   'bzrlib.tests.test_commit_merge',
 
659
                   'bzrlib.tests.test_config',
 
660
                   'bzrlib.tests.test_conflicts',
 
661
                   'bzrlib.tests.test_diff',
 
662
                   'bzrlib.tests.test_fetch',
 
663
                   'bzrlib.tests.test_gpg',
 
664
                   'bzrlib.tests.test_graph',
 
665
                   'bzrlib.tests.test_hashcache',
 
666
                   'bzrlib.tests.test_http',
 
667
                   'bzrlib.tests.test_identitymap',
 
668
                   'bzrlib.tests.test_inv',
 
669
                   'bzrlib.tests.test_log',
 
670
                   'bzrlib.tests.test_merge',
 
671
                   'bzrlib.tests.test_merge3',
 
672
                   'bzrlib.tests.test_merge_core',
 
673
                   'bzrlib.tests.test_missing',
 
674
                   'bzrlib.tests.test_msgeditor',
 
675
                   'bzrlib.tests.test_nonascii',
 
676
                   'bzrlib.tests.test_options',
 
677
                   'bzrlib.tests.test_osutils',
 
678
                   'bzrlib.tests.test_parent',
 
679
                   'bzrlib.tests.test_permissions',
 
680
                   'bzrlib.tests.test_plugins',
 
681
                   'bzrlib.tests.test_remove',
 
682
                   'bzrlib.tests.test_revision',
 
683
                   'bzrlib.tests.test_revisionnamespaces',
 
684
                   'bzrlib.tests.test_revprops',
 
685
                   'bzrlib.tests.test_reweave',
 
686
                   'bzrlib.tests.test_rio',
 
687
                   'bzrlib.tests.test_sampler',
 
688
                   'bzrlib.tests.test_selftest',
 
689
                   'bzrlib.tests.test_setup',
 
690
                   'bzrlib.tests.test_sftp_transport',
 
691
                   'bzrlib.tests.test_smart_add',
 
692
                   'bzrlib.tests.test_source',
 
693
                   'bzrlib.tests.test_status',
 
694
                   'bzrlib.tests.test_store',
 
695
                   'bzrlib.tests.test_testament',
 
696
                   'bzrlib.tests.test_trace',
 
697
                   'bzrlib.tests.test_transactions',
 
698
                   'bzrlib.tests.test_transport',
 
699
                   'bzrlib.tests.test_tsort',
 
700
                   'bzrlib.tests.test_ui',
 
701
                   'bzrlib.tests.test_uncommit',
 
702
                   'bzrlib.tests.test_upgrade',
 
703
                   'bzrlib.tests.test_weave',
 
704
                   'bzrlib.tests.test_whitebox',
 
705
                   'bzrlib.tests.test_workingtree',
 
706
                   'bzrlib.tests.test_xml',
492
707
                   ]
493
708
 
494
 
    for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
495
 
              bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
496
 
        if m not in MODULES_TO_DOCTEST:
497
 
            MODULES_TO_DOCTEST.append(m)
498
 
 
499
 
    TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
500
 
    print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
 
709
    TestCase.BZRPATH = osutils.pathjoin(
 
710
            osutils.realpath(osutils.dirname(bzrlib.__path__[0])), 'bzr')
 
711
    print '%10s: %s' % ('bzr', osutils.realpath(sys.argv[0]))
 
712
    print '%10s: %s' % ('bzrlib', bzrlib.__path__[0])
501
713
    print
502
714
    suite = TestSuite()
503
 
    suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
 
715
    # python2.4's TestLoader.loadTestsFromNames gives very poor 
 
716
    # errors if it fails to load a named module - no indication of what's
 
717
    # actually wrong, just "no such module".  We should probably override that
 
718
    # class, but for the moment just load them ourselves. (mbp 20051202)
 
719
    loader = TestLoader()
 
720
    for mod_name in testmod_names:
 
721
        mod = _load_module_by_name(mod_name)
 
722
        suite.addTest(loader.loadTestsFromModule(mod))
 
723
    for package in packages_to_test():
 
724
        suite.addTest(package.test_suite())
504
725
    for m in MODULES_TO_TEST:
505
 
         suite.addTest(TestLoader().loadTestsFromModule(m))
 
726
        suite.addTest(loader.loadTestsFromModule(m))
506
727
    for m in (MODULES_TO_DOCTEST):
507
728
        suite.addTest(DocTestSuite(m))
508
 
    for p in bzrlib.plugin.all_plugins:
509
 
        if hasattr(p, 'test_suite'):
510
 
            suite.addTest(p.test_suite())
 
729
    for name, plugin in bzrlib.plugin.all_plugins().items():
 
730
        if hasattr(plugin, 'test_suite'):
 
731
            suite.addTest(plugin.test_suite())
511
732
    return suite
512
733
 
 
734
 
 
735
def _load_module_by_name(mod_name):
 
736
    parts = mod_name.split('.')
 
737
    module = __import__(mod_name)
 
738
    del parts[0]
 
739
    # for historical reasons python returns the top-level module even though
 
740
    # it loads the submodule; we need to walk down to get the one we want.
 
741
    while parts:
 
742
        module = getattr(module, parts.pop(0))
 
743
    return module