~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

[merge] Jamie Wilkinson, don't export .bzrignore

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 testsweet import TestCase, run_suite, InTempDir, FunctionalTestCase
 
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
19
32
import bzrlib.commands
20
 
import bzrlib.fetch
 
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
 
40
import bzrlib.trace
 
41
from bzrlib.trace import mutter
 
42
from bzrlib.tests.TestUtil import TestLoader, TestSuite
 
43
from bzrlib.tests.treeshape import build_tree_contents
21
44
 
22
45
MODULES_TO_TEST = []
23
 
MODULES_TO_DOCTEST = []
24
 
 
25
 
 
26
 
class BzrTestBase(InTempDir):
27
 
    """bzr-specific test base class"""
 
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
            ]
 
60
 
 
61
 
 
62
class EarlyStoppingTestResultAdapter(object):
 
63
    """An adapter for TestResult to stop at the first first failure or error"""
 
64
 
 
65
    def __init__(self, result):
 
66
        self._result = result
 
67
 
 
68
    def addError(self, test, err):
 
69
        self._result.addError(test, err)
 
70
        self._result.stop()
 
71
 
 
72
    def addFailure(self, test, err):
 
73
        self._result.addFailure(test, err)
 
74
        self._result.stop()
 
75
 
 
76
    def __getattr__(self, name):
 
77
        return getattr(self._result, name)
 
78
 
 
79
    def __setattr__(self, name, value):
 
80
        if name == '_result':
 
81
            object.__setattr__(self, name, value)
 
82
        return setattr(self._result, name, value)
 
83
 
 
84
 
 
85
class _MyResult(unittest._TextTestResult):
 
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))
 
93
 
 
94
    def startTest(self, test):
 
95
        unittest.TestResult.startTest(self, test)
 
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
 
100
        if self.showAll:
 
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)
 
117
        self.stream.flush()
 
118
        self._start_time = time.time()
 
119
 
 
120
    def addError(self, 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')
 
128
        self.stream.flush()
 
129
 
 
130
    def addFailure(self, 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')
 
136
        self.stream.flush()
 
137
 
 
138
    def addSuccess(self, test):
 
139
        if self.showAll:
 
140
            self.stream.writeln('   OK %s' % self._elapsedTime())
 
141
        elif self.dots:
 
142
            self.stream.write('~')
 
143
        self.stream.flush()
 
144
        unittest.TestResult.addSuccess(self, test)
 
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
 
 
157
    def printErrorList(self, flavour, errors):
 
158
        for test, err in errors:
 
159
            self.stream.writeln(self.separator1)
 
160
            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
 
161
            if hasattr(test, '_get_log'):
 
162
                print >>self.stream
 
163
                print >>self.stream, \
 
164
                        ('vvvv[log from %s]' % test).ljust(78,'-')
 
165
                print >>self.stream, test._get_log()
 
166
                print >>self.stream, \
 
167
                        ('^^^^[log from %s]' % test).ljust(78,'-')
 
168
            self.stream.writeln(self.separator2)
 
169
            self.stream.writeln("%s" % err)
 
170
 
 
171
 
 
172
class TextTestRunner(unittest.TextTestRunner):
 
173
    stop_on_failure = False
 
174
 
 
175
    def _makeResult(self):
 
176
        result = _MyResult(self.stream, self.descriptions, self.verbosity)
 
177
        if self.stop_on_failure:
 
178
            result = EarlyStoppingTestResultAdapter(result)
 
179
        return result
 
180
 
 
181
 
 
182
def iter_suite_tests(suite):
 
183
    """Return all tests in a suite, recursing through nested suites"""
 
184
    for item in suite._tests:
 
185
        if isinstance(item, unittest.TestCase):
 
186
            yield item
 
187
        elif isinstance(item, unittest.TestSuite):
 
188
            for r in iter_suite_tests(item):
 
189
                yield r
 
190
        else:
 
191
            raise Exception('unknown object %r inside test suite %r'
 
192
                            % (item, suite))
 
193
 
 
194
 
 
195
class TestSkipped(Exception):
 
196
    """Indicates that a test was intentionally skipped, rather than failing."""
 
197
    # XXX: Not used yet
 
198
 
 
199
 
 
200
class CommandFailed(Exception):
 
201
    pass
 
202
 
 
203
class TestCase(unittest.TestCase):
 
204
    """Base class for bzr unit tests.
 
205
    
 
206
    Tests that need access to disk resources should subclass 
 
207
    TestCaseInTempDir not TestCase.
 
208
 
 
209
    Error and debug log messages are redirected from their usual
 
210
    location into a temporary file, the contents of which can be
 
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.
 
214
       
 
215
    There are also convenience functions to invoke bzr's command-line
 
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
    """
 
223
 
 
224
    BZRPATH = 'bzr'
 
225
    _log_file_name = None
 
226
    _log_contents = ''
 
227
 
 
228
    def setUp(self):
 
229
        unittest.TestCase.setUp(self)
 
230
        self._cleanups = []
 
231
        self._cleanEnvironment()
 
232
        bzrlib.trace.disable_default_logging()
 
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
        """
 
291
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
 
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)
 
295
        self._log_file_name = name
 
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()
 
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()
 
357
        unittest.TestCase.tearDown(self)
 
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
 
 
367
    def log(self, *args):
 
368
        mutter(*args)
 
369
 
 
370
    def _get_log(self):
 
371
        """Return as a string the log for this test"""
 
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):
 
379
        """Shortcut that splits cmd into words, runs, and returns stdout"""
 
380
        return self.run_bzr_captured(cmd.split(), retcode=retcode)[0]
 
381
 
 
382
    def run_bzr_captured(self, argv, retcode=0):
 
383
        """Invoke bzr and return (stdout, stderr).
 
384
 
 
385
        Useful for code that wants to check the contents of the
 
386
        output, the way error messages are presented, etc.
 
387
 
 
388
        This should be the main method for tests that want to exercise the
 
389
        overall behavior of the bzr application (rather than a unit test
 
390
        or a functional test of the library.)
 
391
 
 
392
        Much of the old code runs bzr by forking a new copy of Python, but
 
393
        that is slower, harder to debug, and generally not necessary.
 
394
 
 
395
        This runs bzr through the interface that catches and reports
 
396
        errors, and with logging set to something approximating the
 
397
        default, so that error reporting can be checked.
 
398
 
 
399
        argv -- arguments to invoke bzr
 
400
        retcode -- expected return code, or None for don't-care.
 
401
        """
 
402
        stdout = StringIO()
 
403
        stderr = StringIO()
 
404
        self.log('run bzr: %s', ' '.join(argv))
 
405
        # FIXME: don't call into logging here
 
406
        handler = logging.StreamHandler(stderr)
 
407
        handler.setFormatter(bzrlib.trace.QuietFormatter())
 
408
        handler.setLevel(logging.INFO)
 
409
        logger = logging.getLogger('')
 
410
        logger.addHandler(handler)
 
411
        try:
 
412
            result = self.apply_redirected(None, stdout, stderr,
 
413
                                           bzrlib.commands.run_bzr_catch_errors,
 
414
                                           argv)
 
415
        finally:
 
416
            logger.removeHandler(handler)
 
417
        out = stdout.getvalue()
 
418
        err = stderr.getvalue()
 
419
        if out:
 
420
            self.log('output:\n%s', out)
 
421
        if err:
 
422
            self.log('errors:\n%s', err)
 
423
        if retcode is not None:
 
424
            self.assertEquals(result, retcode)
 
425
        return out, err
 
426
 
28
427
    def run_bzr(self, *args, **kwargs):
29
 
        retcode = kwargs.get('retcode', 0)
30
 
        result = self.apply_redirected(None, None, None,
31
 
                                       bzrlib.commands.run_bzr, args)
32
 
        self.assertEquals(result, retcode)
33
 
        
34
 
 
35
 
def selftest(verbose=False, pattern=".*"):
36
 
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
 
428
        """Invoke bzr, as if it were run from the command line.
 
429
 
 
430
        This should be the main method for tests that want to exercise the
 
431
        overall behavior of the bzr application (rather than a unit test
 
432
        or a functional test of the library.)
 
433
 
 
434
        This sends the stdout/stderr results into the test's log,
 
435
        where it may be useful for debugging.  See also run_captured.
 
436
        """
 
437
        retcode = kwargs.pop('retcode', 0)
 
438
        return self.run_bzr_captured(args, retcode)
 
439
 
 
440
    def check_inventory_shape(self, inv, shape):
 
441
        """Compare an inventory to a list of expected names.
 
442
 
 
443
        Fail if they are not precisely equal.
 
444
        """
 
445
        extras = []
 
446
        shape = list(shape)             # copy
 
447
        for path, ie in inv.entries():
 
448
            name = path.replace('\\', '/')
 
449
            if ie.kind == 'dir':
 
450
                name = name + '/'
 
451
            if name in shape:
 
452
                shape.remove(name)
 
453
            else:
 
454
                extras.append(name)
 
455
        if shape:
 
456
            self.fail("expected paths not found in inventory: %r" % shape)
 
457
        if extras:
 
458
            self.fail("unexpected paths found in inventory: %r" % extras)
 
459
 
 
460
    def apply_redirected(self, stdin=None, stdout=None, stderr=None,
 
461
                         a_callable=None, *args, **kwargs):
 
462
        """Call callable with redirected std io pipes.
 
463
 
 
464
        Returns the return code."""
 
465
        if not callable(a_callable):
 
466
            raise ValueError("a_callable must be callable.")
 
467
        if stdin is None:
 
468
            stdin = StringIO("")
 
469
        if stdout is None:
 
470
            if hasattr(self, "_log_file"):
 
471
                stdout = self._log_file
 
472
            else:
 
473
                stdout = StringIO()
 
474
        if stderr is None:
 
475
            if hasattr(self, "_log_file"):
 
476
                stderr = self._log_file
 
477
            else:
 
478
                stderr = StringIO()
 
479
        real_stdin = sys.stdin
 
480
        real_stdout = sys.stdout
 
481
        real_stderr = sys.stderr
 
482
        try:
 
483
            sys.stdout = stdout
 
484
            sys.stderr = stderr
 
485
            sys.stdin = stdin
 
486
            return a_callable(*args, **kwargs)
 
487
        finally:
 
488
            sys.stdout = real_stdout
 
489
            sys.stderr = real_stderr
 
490
            sys.stdin = real_stdin
 
491
 
 
492
 
 
493
BzrTestBase = TestCase
 
494
 
 
495
     
 
496
class TestCaseInTempDir(TestCase):
 
497
    """Derived class that runs a test within a temporary directory.
 
498
 
 
499
    This is useful for tests that need to create a branch, etc.
 
500
 
 
501
    The directory is created in a slightly complex way: for each
 
502
    Python invocation, a new temporary top-level directory is created.
 
503
    All test cases create their own directory within that.  If the
 
504
    tests complete successfully, the directory is removed.
 
505
 
 
506
    InTempDir is an old alias for FunctionalTestCase.
 
507
    """
 
508
 
 
509
    TEST_ROOT = None
 
510
    _TEST_NAME = 'test'
 
511
    OVERRIDE_PYTHON = 'python'
 
512
 
 
513
    def check_file_contents(self, filename, expect):
 
514
        self.log("check contents of file %s" % filename)
 
515
        contents = file(filename, 'r').read()
 
516
        if contents != expect:
 
517
            self.log("expected: %r" % expect)
 
518
            self.log("actually: %r" % contents)
 
519
            self.fail("contents of %s not as expected" % filename)
 
520
 
 
521
    def _make_test_root(self):
 
522
        if TestCaseInTempDir.TEST_ROOT is not None:
 
523
            return
 
524
        i = 0
 
525
        while True:
 
526
            root = u'test%04d.tmp' % i
 
527
            try:
 
528
                os.mkdir(root)
 
529
            except OSError, e:
 
530
                if e.errno == errno.EEXIST:
 
531
                    i += 1
 
532
                    continue
 
533
                else:
 
534
                    raise
 
535
            # successfully created
 
536
            TestCaseInTempDir.TEST_ROOT = osutils.abspath(root)
 
537
            break
 
538
        # make a fake bzr directory there to prevent any tests propagating
 
539
        # up onto the source directory's real branch
 
540
        os.mkdir(osutils.pathjoin(TestCaseInTempDir.TEST_ROOT, '.bzr'))
 
541
 
 
542
    def setUp(self):
 
543
        super(TestCaseInTempDir, self).setUp()
 
544
        self._make_test_root()
 
545
        _currentdir = os.getcwdu()
 
546
        short_id = self.id().replace('bzrlib.tests.', '') \
 
547
                   .replace('__main__.', '')
 
548
        self.test_dir = osutils.pathjoin(self.TEST_ROOT, short_id)
 
549
        os.mkdir(self.test_dir)
 
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)
 
556
        
 
557
    def build_tree(self, shape, line_endings='native'):
 
558
        """Build a test tree according to a pattern.
 
559
 
 
560
        shape is a sequence of file specifications.  If the final
 
561
        character is '/', a directory is created.
 
562
 
 
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.
 
568
        """
 
569
        # XXX: It's OK to just create them using forward slashes on windows?
 
570
        for name in shape:
 
571
            self.assert_(isinstance(name, basestring))
 
572
            if name[-1] == '/':
 
573
                os.mkdir(name[:-1])
 
574
            else:
 
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,))
 
581
                print >>f, "contents of", name
 
582
                f.close()
 
583
 
 
584
    def build_tree_contents(self, shape):
 
585
        build_tree_contents(shape)
 
586
 
 
587
    def failUnlessExists(self, path):
 
588
        """Fail unless path, which may be abs or relative, exists."""
 
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))
 
594
        
 
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())
 
599
 
 
600
 
 
601
def filter_suite_by_re(suite, pattern):
 
602
    result = TestSuite()
 
603
    filter_re = re.compile(pattern)
 
604
    for test in iter_suite_tests(suite):
 
605
        if filter_re.search(test.id()):
 
606
            result.addTest(test)
 
607
    return result
 
608
 
 
609
 
 
610
def run_suite(suite, name='test', verbose=False, pattern=".*",
 
611
              stop_on_failure=False, keep_output=False):
 
612
    TestCaseInTempDir._TEST_NAME = name
 
613
    if verbose:
 
614
        verbosity = 2
 
615
    else:
 
616
        verbosity = 1
 
617
    runner = TextTestRunner(stream=sys.stdout,
 
618
                            descriptions=0,
 
619
                            verbosity=verbosity)
 
620
    runner.stop_on_failure=stop_on_failure
 
621
    if pattern != '.*':
 
622
        suite = filter_suite_by_re(suite, pattern)
 
623
    result = runner.run(suite)
 
624
    # This is still a little bogus, 
 
625
    # but only a little. Folk not using our testrunner will
 
626
    # have to delete their temp directories themselves.
 
627
    if result.wasSuccessful() or not keep_output:
 
628
        if TestCaseInTempDir.TEST_ROOT is not None:
 
629
            shutil.rmtree(TestCaseInTempDir.TEST_ROOT) 
 
630
    else:
 
631
        print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
 
632
    return result.wasSuccessful()
 
633
 
 
634
 
 
635
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
 
636
             keep_output=False):
 
637
    """Run the whole test suite under the enhanced runner"""
 
638
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern,
 
639
                     stop_on_failure=stop_on_failure, keep_output=keep_output)
37
640
 
38
641
 
39
642
def test_suite():
40
 
    from bzrlib.selftest.TestUtil import TestLoader, TestSuite
41
 
    import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
42
 
    import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
 
643
    """Build and return TestSuite for the whole program."""
43
644
    from doctest import DocTestSuite
44
 
    import os
45
 
    import shutil
46
 
    import time
47
 
    import sys
48
 
    import unittest
49
 
 
50
 
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
51
 
 
52
 
    testmod_names = \
53
 
                  ['bzrlib.selftest.whitebox',
54
 
                   'bzrlib.selftest.versioning',
55
 
                   'bzrlib.selftest.testinv',
56
 
                   'bzrlib.selftest.testmerge3',
57
 
                   'bzrlib.selftest.testhashcache',
58
 
                   'bzrlib.selftest.teststatus',
59
 
                   'bzrlib.selftest.testlog',
60
 
                   'bzrlib.selftest.blackbox',
61
 
                   'bzrlib.selftest.testrevisionnamespaces',
62
 
                   'bzrlib.selftest.testbranch',
63
 
                   'bzrlib.selftest.testrevision',
64
 
                   'bzrlib.selftest.test_merge_core',
65
 
                   'bzrlib.selftest.test_smart_add',
66
 
                   'bzrlib.selftest.testdiff',
67
 
                   'bzrlib.fetch'
 
645
 
 
646
    global MODULES_TO_DOCTEST
 
647
 
 
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_branch',
 
655
                   'bzrlib.tests.test_command',
 
656
                   'bzrlib.tests.test_commit',
 
657
                   'bzrlib.tests.test_commit_merge',
 
658
                   'bzrlib.tests.test_config',
 
659
                   'bzrlib.tests.test_conflicts',
 
660
                   'bzrlib.tests.test_diff',
 
661
                   'bzrlib.tests.test_fetch',
 
662
                   'bzrlib.tests.test_gpg',
 
663
                   'bzrlib.tests.test_graph',
 
664
                   'bzrlib.tests.test_hashcache',
 
665
                   'bzrlib.tests.test_http',
 
666
                   'bzrlib.tests.test_identitymap',
 
667
                   'bzrlib.tests.test_inv',
 
668
                   'bzrlib.tests.test_log',
 
669
                   'bzrlib.tests.test_merge',
 
670
                   'bzrlib.tests.test_merge3',
 
671
                   'bzrlib.tests.test_merge_core',
 
672
                   'bzrlib.tests.test_missing',
 
673
                   'bzrlib.tests.test_msgeditor',
 
674
                   'bzrlib.tests.test_nonascii',
 
675
                   'bzrlib.tests.test_options',
 
676
                   'bzrlib.tests.test_osutils',
 
677
                   'bzrlib.tests.test_parent',
 
678
                   'bzrlib.tests.test_permissions',
 
679
                   'bzrlib.tests.test_plugins',
 
680
                   'bzrlib.tests.test_remove',
 
681
                   'bzrlib.tests.test_revision',
 
682
                   'bzrlib.tests.test_revision_info',
 
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',
68
707
                   ]
69
708
 
70
 
    for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
71
 
              bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
72
 
        if m not in MODULES_TO_DOCTEST:
73
 
            MODULES_TO_DOCTEST.append(m)
74
 
 
75
 
    TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
76
 
    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])
77
713
    print
78
714
    suite = TestSuite()
79
 
    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())
80
725
    for m in MODULES_TO_TEST:
81
 
         suite.addTest(TestLoader().loadTestsFromModule(m))
 
726
        suite.addTest(loader.loadTestsFromModule(m))
82
727
    for m in (MODULES_TO_DOCTEST):
83
728
        suite.addTest(DocTestSuite(m))
84
 
    for p in bzrlib.plugin.all_plugins:
85
 
        if hasattr(p, 'test_suite'):
86
 
            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())
87
732
    return suite
88
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