/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

 added test for function fileid_involved

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
import codecs
 
30
 
 
31
import bzrlib.branch
 
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.plugin
 
38
import bzrlib.store
 
39
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
 
43
 
 
44
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
        if isinstance(err[1], TestSkipped):
 
121
            return self.addSkipped(test, err)    
 
122
        unittest.TestResult.addError(self, test, err)
 
123
        if self.showAll:
 
124
            self.stream.writeln("ERROR %s" % self._elapsedTime())
 
125
        elif self.dots:
 
126
            self.stream.write('E')
 
127
        self.stream.flush()
 
128
 
 
129
    def addFailure(self, test, err):
 
130
        unittest.TestResult.addFailure(self, test, err)
 
131
        if self.showAll:
 
132
            self.stream.writeln(" FAIL %s" % self._elapsedTime())
 
133
        elif self.dots:
 
134
            self.stream.write('F')
 
135
        self.stream.flush()
 
136
 
 
137
    def addSuccess(self, test):
 
138
        if self.showAll:
 
139
            self.stream.writeln('   OK %s' % self._elapsedTime())
 
140
        elif self.dots:
 
141
            self.stream.write('~')
 
142
        self.stream.flush()
 
143
        unittest.TestResult.addSuccess(self, test)
 
144
 
 
145
    def addSkipped(self, test, skip_excinfo):
 
146
        if self.showAll:
 
147
            print >>self.stream, ' SKIP %s' % self._elapsedTime()
 
148
            print >>self.stream, '     %s' % skip_excinfo[1]
 
149
        elif self.dots:
 
150
            self.stream.write('S')
 
151
        self.stream.flush()
 
152
        # seems best to treat this as success from point-of-view of unittest
 
153
        # -- it actually does nothing so it barely matters :)
 
154
        unittest.TestResult.addSuccess(self, test)
 
155
 
 
156
    def printErrorList(self, flavour, errors):
 
157
        for test, err in errors:
 
158
            self.stream.writeln(self.separator1)
 
159
            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
 
160
            if hasattr(test, '_get_log'):
 
161
                print >>self.stream
 
162
                print >>self.stream, \
 
163
                        ('vvvv[log from %s]' % test).ljust(78,'-')
 
164
                print >>self.stream, test._get_log()
 
165
                print >>self.stream, \
 
166
                        ('^^^^[log from %s]' % test).ljust(78,'-')
 
167
            self.stream.writeln(self.separator2)
 
168
            self.stream.writeln("%s" % err)
 
169
 
 
170
 
 
171
class TextTestRunner(unittest.TextTestRunner):
 
172
    stop_on_failure = False
 
173
 
 
174
    def _makeResult(self):
 
175
        result = _MyResult(self.stream, self.descriptions, self.verbosity)
 
176
        if self.stop_on_failure:
 
177
            result = EarlyStoppingTestResultAdapter(result)
 
178
        return result
 
179
 
 
180
 
 
181
def iter_suite_tests(suite):
 
182
    """Return all tests in a suite, recursing through nested suites"""
 
183
    for item in suite._tests:
 
184
        if isinstance(item, unittest.TestCase):
 
185
            yield item
 
186
        elif isinstance(item, unittest.TestSuite):
 
187
            for r in iter_suite_tests(item):
 
188
                yield r
 
189
        else:
 
190
            raise Exception('unknown object %r inside test suite %r'
 
191
                            % (item, suite))
 
192
 
 
193
 
 
194
class TestSkipped(Exception):
 
195
    """Indicates that a test was intentionally skipped, rather than failing."""
 
196
    # XXX: Not used yet
 
197
 
 
198
 
 
199
class CommandFailed(Exception):
 
200
    pass
 
201
 
 
202
class TestCase(unittest.TestCase):
 
203
    """Base class for bzr unit tests.
 
204
    
 
205
    Tests that need access to disk resources should subclass 
 
206
    TestCaseInTempDir not TestCase.
 
207
 
 
208
    Error and debug log messages are redirected from their usual
 
209
    location into a temporary file, the contents of which can be
 
210
    retrieved by _get_log().  We use a real OS file, not an in-memory object,
 
211
    so that it can also capture file IO.  When the test completes this file
 
212
    is read into memory and removed from disk.
 
213
       
 
214
    There are also convenience functions to invoke bzr's command-line
 
215
    routine, and to build and check bzr trees.
 
216
   
 
217
    In addition to the usual method of overriding tearDown(), this class also
 
218
    allows subclasses to register functions into the _cleanups list, which is
 
219
    run in order as the object is torn down.  It's less likely this will be
 
220
    accidentally overlooked.
 
221
    """
 
222
 
 
223
    BZRPATH = 'bzr'
 
224
    _log_file_name = None
 
225
    _log_contents = ''
 
226
 
 
227
    def setUp(self):
 
228
        unittest.TestCase.setUp(self)
 
229
        self._cleanups = []
 
230
        self._cleanEnvironment()
 
231
        bzrlib.trace.disable_default_logging()
 
232
        self._startLogFile()
 
233
 
 
234
    def _ndiff_strings(self, a, b):
 
235
        """Return ndiff between two strings containing lines.
 
236
        
 
237
        A trailing newline is added if missing to make the strings
 
238
        print properly."""
 
239
        if b and b[-1] != '\n':
 
240
            b += '\n'
 
241
        if a and a[-1] != '\n':
 
242
            a += '\n'
 
243
        difflines = difflib.ndiff(a.splitlines(True),
 
244
                                  b.splitlines(True),
 
245
                                  linejunk=lambda x: False,
 
246
                                  charjunk=lambda x: False)
 
247
        return ''.join(difflines)
 
248
 
 
249
    def assertEqualDiff(self, a, b):
 
250
        """Assert two texts are equal, if not raise an exception.
 
251
        
 
252
        This is intended for use with multi-line strings where it can 
 
253
        be hard to find the differences by eye.
 
254
        """
 
255
        # TODO: perhaps override assertEquals to call this for strings?
 
256
        if a == b:
 
257
            return
 
258
        raise AssertionError("texts not equal:\n" + 
 
259
                             self._ndiff_strings(a, b))      
 
260
 
 
261
    def assertContainsRe(self, haystack, needle_re):
 
262
        """Assert that a contains something matching a regular expression."""
 
263
        if not re.search(needle_re, haystack):
 
264
            raise AssertionError('pattern "%s" not found in "%s"'
 
265
                    % (needle_re, haystack))
 
266
 
 
267
    def _startLogFile(self):
 
268
        """Send bzr and test log messages to a temporary file.
 
269
 
 
270
        The file is removed as the test is torn down.
 
271
        """
 
272
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
 
273
        encoder, decoder, stream_reader, stream_writer = codecs.lookup('UTF-8')
 
274
        self._log_file = stream_writer(os.fdopen(fileno, 'w+'))
 
275
        bzrlib.trace.enable_test_log(self._log_file)
 
276
        self._log_file_name = name
 
277
        self.addCleanup(self._finishLogFile)
 
278
 
 
279
    def _finishLogFile(self):
 
280
        """Finished with the log file.
 
281
 
 
282
        Read contents into memory, close, and delete.
 
283
        """
 
284
        bzrlib.trace.disable_test_log()
 
285
        self._log_file.seek(0)
 
286
        self._log_contents = self._log_file.read()
 
287
        self._log_file.close()
 
288
        os.remove(self._log_file_name)
 
289
        self._log_file = self._log_file_name = None
 
290
 
 
291
    def addCleanup(self, callable):
 
292
        """Arrange to run a callable when this case is torn down.
 
293
 
 
294
        Callables are run in the reverse of the order they are registered, 
 
295
        ie last-in first-out.
 
296
        """
 
297
        if callable in self._cleanups:
 
298
            raise ValueError("cleanup function %r already registered on %s" 
 
299
                    % (callable, self))
 
300
        self._cleanups.append(callable)
 
301
 
 
302
    def _cleanEnvironment(self):
 
303
        new_env = {
 
304
            'HOME': os.getcwd(),
 
305
            'APPDATA': os.getcwd(),
 
306
            'BZREMAIL': None,
 
307
            'EMAIL': None,
 
308
        }
 
309
        self.__old_env = {}
 
310
        self.addCleanup(self._restoreEnvironment)
 
311
        for name, value in new_env.iteritems():
 
312
            self._captureVar(name, value)
 
313
 
 
314
 
 
315
    def _captureVar(self, name, newvalue):
 
316
        """Set an environment variable, preparing it to be reset when finished."""
 
317
        self.__old_env[name] = os.environ.get(name, None)
 
318
        if newvalue is None:
 
319
            if name in os.environ:
 
320
                del os.environ[name]
 
321
        else:
 
322
            os.environ[name] = newvalue
 
323
 
 
324
    @staticmethod
 
325
    def _restoreVar(name, value):
 
326
        if value is None:
 
327
            if name in os.environ:
 
328
                del os.environ[name]
 
329
        else:
 
330
            os.environ[name] = value
 
331
 
 
332
    def _restoreEnvironment(self):
 
333
        for name, value in self.__old_env.iteritems():
 
334
            self._restoreVar(name, value)
 
335
 
 
336
    def tearDown(self):
 
337
        self._runCleanups()
 
338
        unittest.TestCase.tearDown(self)
 
339
 
 
340
    def _runCleanups(self):
 
341
        """Run registered cleanup functions. 
 
342
 
 
343
        This should only be called from TestCase.tearDown.
 
344
        """
 
345
        for cleanup_fn in reversed(self._cleanups):
 
346
            cleanup_fn()
 
347
 
 
348
    def log(self, *args):
 
349
        mutter(*args)
 
350
 
 
351
    def _get_log(self):
 
352
        """Return as a string the log for this test"""
 
353
        if self._log_file_name:
 
354
            return open(self._log_file_name).read()
 
355
        else:
 
356
            return self._log_contents
 
357
        # TODO: Delete the log after it's been read in
 
358
 
 
359
    def capture(self, cmd, retcode=0):
 
360
        """Shortcut that splits cmd into words, runs, and returns stdout"""
 
361
        return self.run_bzr_captured(cmd.split(), retcode=retcode)[0]
 
362
 
 
363
    def run_bzr_captured(self, argv, retcode=0):
 
364
        """Invoke bzr and return (stdout, stderr).
 
365
 
 
366
        Useful for code that wants to check the contents of the
 
367
        output, the way error messages are presented, etc.
 
368
 
 
369
        This should be the main method for tests that want to exercise the
 
370
        overall behavior of the bzr application (rather than a unit test
 
371
        or a functional test of the library.)
 
372
 
 
373
        Much of the old code runs bzr by forking a new copy of Python, but
 
374
        that is slower, harder to debug, and generally not necessary.
 
375
 
 
376
        This runs bzr through the interface that catches and reports
 
377
        errors, and with logging set to something approximating the
 
378
        default, so that error reporting can be checked.
 
379
 
 
380
        argv -- arguments to invoke bzr
 
381
        retcode -- expected return code, or None for don't-care.
 
382
        """
 
383
        stdout = StringIO()
 
384
        stderr = StringIO()
 
385
        self.log('run bzr: %s', ' '.join(argv))
 
386
        # FIXME: don't call into logging here
 
387
        handler = logging.StreamHandler(stderr)
 
388
        handler.setFormatter(bzrlib.trace.QuietFormatter())
 
389
        handler.setLevel(logging.INFO)
 
390
        logger = logging.getLogger('')
 
391
        logger.addHandler(handler)
 
392
        try:
 
393
            result = self.apply_redirected(None, stdout, stderr,
 
394
                                           bzrlib.commands.run_bzr_catch_errors,
 
395
                                           argv)
 
396
        finally:
 
397
            logger.removeHandler(handler)
 
398
        out = stdout.getvalue()
 
399
        err = stderr.getvalue()
 
400
        if out:
 
401
            self.log('output:\n%s', out)
 
402
        if err:
 
403
            self.log('errors:\n%s', err)
 
404
        if retcode is not None:
 
405
            self.assertEquals(result, retcode)
 
406
        return out, err
 
407
 
 
408
    def run_bzr(self, *args, **kwargs):
 
409
        """Invoke bzr, as if it were run from the command line.
 
410
 
 
411
        This should be the main method for tests that want to exercise the
 
412
        overall behavior of the bzr application (rather than a unit test
 
413
        or a functional test of the library.)
 
414
 
 
415
        This sends the stdout/stderr results into the test's log,
 
416
        where it may be useful for debugging.  See also run_captured.
 
417
        """
 
418
        retcode = kwargs.pop('retcode', 0)
 
419
        return self.run_bzr_captured(args, retcode)
 
420
 
 
421
    def check_inventory_shape(self, inv, shape):
 
422
        """Compare an inventory to a list of expected names.
 
423
 
 
424
        Fail if they are not precisely equal.
 
425
        """
 
426
        extras = []
 
427
        shape = list(shape)             # copy
 
428
        for path, ie in inv.entries():
 
429
            name = path.replace('\\', '/')
 
430
            if ie.kind == 'dir':
 
431
                name = name + '/'
 
432
            if name in shape:
 
433
                shape.remove(name)
 
434
            else:
 
435
                extras.append(name)
 
436
        if shape:
 
437
            self.fail("expected paths not found in inventory: %r" % shape)
 
438
        if extras:
 
439
            self.fail("unexpected paths found in inventory: %r" % extras)
 
440
 
 
441
    def apply_redirected(self, stdin=None, stdout=None, stderr=None,
 
442
                         a_callable=None, *args, **kwargs):
 
443
        """Call callable with redirected std io pipes.
 
444
 
 
445
        Returns the return code."""
 
446
        if not callable(a_callable):
 
447
            raise ValueError("a_callable must be callable.")
 
448
        if stdin is None:
 
449
            stdin = StringIO("")
 
450
        if stdout is None:
 
451
            if hasattr(self, "_log_file"):
 
452
                stdout = self._log_file
 
453
            else:
 
454
                stdout = StringIO()
 
455
        if stderr is None:
 
456
            if hasattr(self, "_log_file"):
 
457
                stderr = self._log_file
 
458
            else:
 
459
                stderr = StringIO()
 
460
        real_stdin = sys.stdin
 
461
        real_stdout = sys.stdout
 
462
        real_stderr = sys.stderr
 
463
        try:
 
464
            sys.stdout = stdout
 
465
            sys.stderr = stderr
 
466
            sys.stdin = stdin
 
467
            return a_callable(*args, **kwargs)
 
468
        finally:
 
469
            sys.stdout = real_stdout
 
470
            sys.stderr = real_stderr
 
471
            sys.stdin = real_stdin
 
472
 
 
473
 
 
474
BzrTestBase = TestCase
 
475
 
 
476
     
 
477
class TestCaseInTempDir(TestCase):
 
478
    """Derived class that runs a test within a temporary directory.
 
479
 
 
480
    This is useful for tests that need to create a branch, etc.
 
481
 
 
482
    The directory is created in a slightly complex way: for each
 
483
    Python invocation, a new temporary top-level directory is created.
 
484
    All test cases create their own directory within that.  If the
 
485
    tests complete successfully, the directory is removed.
 
486
 
 
487
    InTempDir is an old alias for FunctionalTestCase.
 
488
    """
 
489
 
 
490
    TEST_ROOT = None
 
491
    _TEST_NAME = 'test'
 
492
    OVERRIDE_PYTHON = 'python'
 
493
 
 
494
    def check_file_contents(self, filename, expect):
 
495
        self.log("check contents of file %s" % filename)
 
496
        contents = file(filename, 'r').read()
 
497
        if contents != expect:
 
498
            self.log("expected: %r" % expect)
 
499
            self.log("actually: %r" % contents)
 
500
            self.fail("contents of %s not as expected" % filename)
 
501
 
 
502
    def _make_test_root(self):
 
503
        if TestCaseInTempDir.TEST_ROOT is not None:
 
504
            return
 
505
        i = 0
 
506
        while True:
 
507
            root = u'test%04d.tmp' % i
 
508
            try:
 
509
                os.mkdir(root)
 
510
            except OSError, e:
 
511
                if e.errno == errno.EEXIST:
 
512
                    i += 1
 
513
                    continue
 
514
                else:
 
515
                    raise
 
516
            # successfully created
 
517
            TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
 
518
            break
 
519
        # make a fake bzr directory there to prevent any tests propagating
 
520
        # up onto the source directory's real branch
 
521
        os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
 
522
 
 
523
    def setUp(self):
 
524
        super(TestCaseInTempDir, self).setUp()
 
525
        self._make_test_root()
 
526
        _currentdir = os.getcwdu()
 
527
        short_id = self.id().replace('bzrlib.tests.', '') \
 
528
                   .replace('__main__.', '')
 
529
        self.test_dir = os.path.join(self.TEST_ROOT, short_id)
 
530
        os.mkdir(self.test_dir)
 
531
        os.chdir(self.test_dir)
 
532
        os.environ['HOME'] = self.test_dir
 
533
        def _leaveDirectory():
 
534
            os.chdir(_currentdir)
 
535
        self.addCleanup(_leaveDirectory)
 
536
        
 
537
    def build_tree(self, shape, line_endings='native'):
 
538
        """Build a test tree according to a pattern.
 
539
 
 
540
        shape is a sequence of file specifications.  If the final
 
541
        character is '/', a directory is created.
 
542
 
 
543
        This doesn't add anything to a branch.
 
544
        :param line_endings: Either 'binary' or 'native'
 
545
                             in binary mode, exact contents are written
 
546
                             in native mode, the line endings match the
 
547
                             default platform endings.
 
548
        """
 
549
        # XXX: It's OK to just create them using forward slashes on windows?
 
550
        for name in shape:
 
551
            self.assert_(isinstance(name, basestring))
 
552
            if name[-1] == '/':
 
553
                os.mkdir(name[:-1])
 
554
            else:
 
555
                if line_endings == 'binary':
 
556
                    f = file(name, 'wb')
 
557
                elif line_endings == 'native':
 
558
                    f = file(name, 'wt')
 
559
                else:
 
560
                    raise BzrError('Invalid line ending request %r' % (line_endings,))
 
561
                print >>f, "contents of", name
 
562
                f.close()
 
563
 
 
564
    def build_tree_contents(self, shape):
 
565
        build_tree_contents(shape)
 
566
 
 
567
    def failUnlessExists(self, path):
 
568
        """Fail unless path, which may be abs or relative, exists."""
 
569
        self.failUnless(bzrlib.osutils.lexists(path))
 
570
        
 
571
    def assertFileEqual(self, content, path):
 
572
        """Fail if path does not contain 'content'."""
 
573
        self.failUnless(bzrlib.osutils.lexists(path))
 
574
        self.assertEqualDiff(content, open(path, 'r').read())
 
575
        
 
576
 
 
577
def filter_suite_by_re(suite, pattern):
 
578
    result = TestSuite()
 
579
    filter_re = re.compile(pattern)
 
580
    for test in iter_suite_tests(suite):
 
581
        if filter_re.search(test.id()):
 
582
            result.addTest(test)
 
583
    return result
 
584
 
 
585
 
 
586
def run_suite(suite, name='test', verbose=False, pattern=".*",
 
587
              stop_on_failure=False, keep_output=False):
 
588
    TestCaseInTempDir._TEST_NAME = name
 
589
    if verbose:
 
590
        verbosity = 2
 
591
    else:
 
592
        verbosity = 1
 
593
    runner = TextTestRunner(stream=sys.stdout,
 
594
                            descriptions=0,
 
595
                            verbosity=verbosity)
 
596
    runner.stop_on_failure=stop_on_failure
 
597
    if pattern != '.*':
 
598
        suite = filter_suite_by_re(suite, pattern)
 
599
    result = runner.run(suite)
 
600
    # This is still a little bogus, 
 
601
    # but only a little. Folk not using our testrunner will
 
602
    # have to delete their temp directories themselves.
 
603
    if result.wasSuccessful() or not keep_output:
 
604
        if TestCaseInTempDir.TEST_ROOT is not None:
 
605
            shutil.rmtree(TestCaseInTempDir.TEST_ROOT) 
 
606
    else:
 
607
        print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
 
608
    return result.wasSuccessful()
 
609
 
 
610
 
 
611
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
 
612
             keep_output=False):
 
613
    """Run the whole test suite under the enhanced runner"""
 
614
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern,
 
615
                     stop_on_failure=stop_on_failure, keep_output=keep_output)
 
616
 
 
617
 
 
618
def test_suite():
 
619
    """Build and return TestSuite for the whole program."""
 
620
    from doctest import DocTestSuite
 
621
 
 
622
    global MODULES_TO_DOCTEST
 
623
 
 
624
    testmod_names = [ \
 
625
                   'bzrlib.tests.test_ancestry',
 
626
                   'bzrlib.tests.test_annotate',
 
627
                   'bzrlib.tests.test_api',
 
628
                   'bzrlib.tests.test_bad_files',
 
629
                   'bzrlib.tests.test_branch',
 
630
                   'bzrlib.tests.test_command',
 
631
                   'bzrlib.tests.test_commit',
 
632
                   'bzrlib.tests.test_commit_merge',
 
633
                   'bzrlib.tests.test_config',
 
634
                   'bzrlib.tests.test_conflicts',
 
635
                   'bzrlib.tests.test_diff',
 
636
                   'bzrlib.tests.test_fetch',
 
637
                   'bzrlib.tests.test_gpg',
 
638
                   'bzrlib.tests.test_graph',
 
639
                   'bzrlib.tests.test_hashcache',
 
640
                   'bzrlib.tests.test_http',
 
641
                   'bzrlib.tests.test_identitymap',
 
642
                   'bzrlib.tests.test_inv',
 
643
                   'bzrlib.tests.test_log',
 
644
                   'bzrlib.tests.test_merge',
 
645
                   'bzrlib.tests.test_merge3',
 
646
                   'bzrlib.tests.test_merge_core',
 
647
                   'bzrlib.tests.test_missing',
 
648
                   'bzrlib.tests.test_msgeditor',
 
649
                   'bzrlib.tests.test_nonascii',
 
650
                   'bzrlib.tests.test_options',
 
651
                   'bzrlib.tests.test_parent',
 
652
                   'bzrlib.tests.test_plugins',
 
653
                   'bzrlib.tests.test_remove',
 
654
                   'bzrlib.tests.test_revision',
 
655
                   'bzrlib.tests.test_revision_info',
 
656
                   'bzrlib.tests.test_revisionnamespaces',
 
657
                   'bzrlib.tests.test_revprops',
 
658
                   'bzrlib.tests.test_reweave',
 
659
                   'bzrlib.tests.test_rio',
 
660
                   'bzrlib.tests.test_sampler',
 
661
                   'bzrlib.tests.test_selftest',
 
662
                   'bzrlib.tests.test_setup',
 
663
                   'bzrlib.tests.test_sftp',
 
664
                   'bzrlib.tests.test_smart_add',
 
665
                   'bzrlib.tests.test_source',
 
666
                   'bzrlib.tests.test_status',
 
667
                   'bzrlib.tests.test_store',
 
668
                   'bzrlib.tests.test_testament',
 
669
                   'bzrlib.tests.test_trace',
 
670
                   'bzrlib.tests.test_transactions',
 
671
                   'bzrlib.tests.test_transport',
 
672
                   'bzrlib.tests.test_tsort',
 
673
                   'bzrlib.tests.test_ui',
 
674
                   'bzrlib.tests.test_uncommit',
 
675
                   'bzrlib.tests.test_upgrade',
 
676
                   'bzrlib.tests.test_weave',
 
677
                   'bzrlib.tests.test_whitebox',
 
678
                   'bzrlib.tests.test_workingtree',
 
679
                   'bzrlib.tests.test_xml',
 
680
                   'bzrlib.tests.test_file_involved',
 
681
                   ]
 
682
 
 
683
    print '%10s: %s' % ('bzr', os.path.realpath(sys.argv[0]))
 
684
    print '%10s: %s' % ('bzrlib', bzrlib.__path__[0])
 
685
    print
 
686
    suite = TestSuite()
 
687
    # python2.4's TestLoader.loadTestsFromNames gives very poor 
 
688
    # errors if it fails to load a named module - no indication of what's
 
689
    # actually wrong, just "no such module".  We should probably override that
 
690
    # class, but for the moment just load them ourselves. (mbp 20051202)
 
691
    loader = TestLoader()
 
692
    for mod_name in testmod_names:
 
693
        mod = _load_module_by_name(mod_name)
 
694
        suite.addTest(loader.loadTestsFromModule(mod))
 
695
    for package in packages_to_test():
 
696
        suite.addTest(package.test_suite())
 
697
    for m in MODULES_TO_TEST:
 
698
        suite.addTest(loader.loadTestsFromModule(m))
 
699
    for m in (MODULES_TO_DOCTEST):
 
700
        suite.addTest(DocTestSuite(m))
 
701
    for name, plugin in bzrlib.plugin.all_plugins().items():
 
702
        if hasattr(plugin, 'test_suite'):
 
703
            suite.addTest(plugin.test_suite())
 
704
    return suite
 
705
 
 
706
 
 
707
def _load_module_by_name(mod_name):
 
708
    parts = mod_name.split('.')
 
709
    module = __import__(mod_name)
 
710
    del parts[0]
 
711
    # for historical reasons python returns the top-level module even though
 
712
    # it loads the submodule; we need to walk down to get the one we want.
 
713
    while parts:
 
714
        module = getattr(module, parts.pop(0))
 
715
    return module