~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-05-21 23:35:20 UTC
  • mfrom: (1721.1.1 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20060521233520-7f8a0248d93bde80
Some tests for bzr ignored. (Robert Collins).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006 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
# TODO: Perhaps there should be an API to find out if bzr running under the
 
19
# test suite -- some plugins might want to avoid making intrusive changes if
 
20
# this is the case.  However, we want behaviour under to test to diverge as
 
21
# little as possible, so this should be used rarely if it's added at all.
 
22
# (Suggestion from j-a-meinel, 2005-11-24)
 
23
 
 
24
# NOTE: Some classes in here use camelCaseNaming() rather than
 
25
# underscore_naming().  That's for consistency with unittest; it's not the
 
26
# general style of bzrlib.  Please continue that consistency when adding e.g.
 
27
# new assertFoo() methods.
 
28
 
 
29
import codecs
 
30
from cStringIO import StringIO
 
31
import difflib
 
32
import errno
 
33
import logging
 
34
import os
 
35
import re
 
36
import stat
 
37
import sys
 
38
import tempfile
 
39
import unittest
 
40
import time
 
41
 
 
42
 
 
43
import bzrlib.branch
 
44
import bzrlib.bzrdir as bzrdir
 
45
import bzrlib.commands
 
46
import bzrlib.errors as errors
 
47
import bzrlib.inventory
 
48
import bzrlib.iterablefile
 
49
import bzrlib.lockdir
 
50
from bzrlib.merge import merge_inner
 
51
import bzrlib.merge3
 
52
import bzrlib.osutils
 
53
import bzrlib.osutils as osutils
 
54
import bzrlib.plugin
 
55
import bzrlib.progress as progress
 
56
from bzrlib.revision import common_ancestor
 
57
import bzrlib.store
 
58
import bzrlib.trace
 
59
from bzrlib.transport import urlescape, get_transport
 
60
import bzrlib.transport
 
61
from bzrlib.transport.local import LocalRelpathServer
 
62
from bzrlib.transport.readonly import ReadonlyServer
 
63
from bzrlib.trace import mutter
 
64
from bzrlib.tests.TestUtil import TestLoader, TestSuite
 
65
from bzrlib.tests.treeshape import build_tree_contents
 
66
from bzrlib.workingtree import WorkingTree, WorkingTreeFormat2
 
67
 
 
68
default_transport = LocalRelpathServer
 
69
 
 
70
MODULES_TO_TEST = []
 
71
MODULES_TO_DOCTEST = [
 
72
                      bzrlib.branch,
 
73
                      bzrlib.commands,
 
74
                      bzrlib.errors,
 
75
                      bzrlib.inventory,
 
76
                      bzrlib.iterablefile,
 
77
                      bzrlib.lockdir,
 
78
                      bzrlib.merge3,
 
79
                      bzrlib.option,
 
80
                      bzrlib.osutils,
 
81
                      bzrlib.store
 
82
                      ]
 
83
def packages_to_test():
 
84
    """Return a list of packages to test.
 
85
 
 
86
    The packages are not globally imported so that import failures are
 
87
    triggered when running selftest, not when importing the command.
 
88
    """
 
89
    import bzrlib.doc
 
90
    import bzrlib.tests.blackbox
 
91
    import bzrlib.tests.branch_implementations
 
92
    import bzrlib.tests.bzrdir_implementations
 
93
    import bzrlib.tests.interrepository_implementations
 
94
    import bzrlib.tests.interversionedfile_implementations
 
95
    import bzrlib.tests.repository_implementations
 
96
    import bzrlib.tests.revisionstore_implementations
 
97
    import bzrlib.tests.workingtree_implementations
 
98
    return [
 
99
            bzrlib.doc,
 
100
            bzrlib.tests.blackbox,
 
101
            bzrlib.tests.branch_implementations,
 
102
            bzrlib.tests.bzrdir_implementations,
 
103
            bzrlib.tests.interrepository_implementations,
 
104
            bzrlib.tests.interversionedfile_implementations,
 
105
            bzrlib.tests.repository_implementations,
 
106
            bzrlib.tests.revisionstore_implementations,
 
107
            bzrlib.tests.workingtree_implementations,
 
108
            ]
 
109
 
 
110
 
 
111
class _MyResult(unittest._TextTestResult):
 
112
    """Custom TestResult.
 
113
 
 
114
    Shows output in a different format, including displaying runtime for tests.
 
115
    """
 
116
    stop_early = False
 
117
    
 
118
    def __init__(self, stream, descriptions, verbosity, pb=None):
 
119
        unittest._TextTestResult.__init__(self, stream, descriptions, verbosity)
 
120
        self.pb = pb
 
121
    
 
122
    def extractBenchmarkTime(self, testCase):
 
123
        """Add a benchmark time for the current test case."""
 
124
        self._benchmarkTime = getattr(testCase, "_benchtime", None)
 
125
    
 
126
    def _elapsedTestTimeString(self):
 
127
        """Return a time string for the overall time the current test has taken."""
 
128
        return self._formatTime(time.time() - self._start_time)
 
129
 
 
130
    def _testTimeString(self):
 
131
        if self._benchmarkTime is not None:
 
132
            return "%s/%s" % (
 
133
                self._formatTime(self._benchmarkTime),
 
134
                self._elapsedTestTimeString())
 
135
        else:
 
136
            return "      %s" % self._elapsedTestTimeString()
 
137
 
 
138
    def _formatTime(self, seconds):
 
139
        """Format seconds as milliseconds with leading spaces."""
 
140
        return "%5dms" % (1000 * seconds)
 
141
 
 
142
    def _ellipsise_unimportant_words(self, a_string, final_width,
 
143
                                   keep_start=False):
 
144
        """Add ellipses (sp?) for overly long strings.
 
145
        
 
146
        :param keep_start: If true preserve the start of a_string rather
 
147
                           than the end of it.
 
148
        """
 
149
        if keep_start:
 
150
            if len(a_string) > final_width:
 
151
                result = a_string[:final_width-3] + '...'
 
152
            else:
 
153
                result = a_string
 
154
        else:
 
155
            if len(a_string) > final_width:
 
156
                result = '...' + a_string[3-final_width:]
 
157
            else:
 
158
                result = a_string
 
159
        return result.ljust(final_width)
 
160
 
 
161
    def startTest(self, test):
 
162
        unittest.TestResult.startTest(self, test)
 
163
        # In a short description, the important words are in
 
164
        # the beginning, but in an id, the important words are
 
165
        # at the end
 
166
        SHOW_DESCRIPTIONS = False
 
167
 
 
168
        if not self.showAll and self.dots and self.pb is not None:
 
169
            final_width = 13
 
170
        else:
 
171
            final_width = osutils.terminal_width()
 
172
            final_width = final_width - 15 - 8
 
173
        what = None
 
174
        if SHOW_DESCRIPTIONS:
 
175
            what = test.shortDescription()
 
176
            if what:
 
177
                what = self._ellipsise_unimportant_words(what, final_width, keep_start=True)
 
178
        if what is None:
 
179
            what = test.id()
 
180
            if what.startswith('bzrlib.tests.'):
 
181
                what = what[13:]
 
182
            what = self._ellipsise_unimportant_words(what, final_width)
 
183
        if self.showAll:
 
184
            self.stream.write(what)
 
185
        elif self.dots and self.pb is not None:
 
186
            self.pb.update(what, self.testsRun - 1, None)
 
187
        self.stream.flush()
 
188
        self._recordTestStartTime()
 
189
 
 
190
    def _recordTestStartTime(self):
 
191
        """Record that a test has started."""
 
192
        self._start_time = time.time()
 
193
 
 
194
    def addError(self, test, err):
 
195
        if isinstance(err[1], TestSkipped):
 
196
            return self.addSkipped(test, err)    
 
197
        unittest.TestResult.addError(self, test, err)
 
198
        self.extractBenchmarkTime(test)
 
199
        if self.showAll:
 
200
            self.stream.writeln("ERROR %s" % self._testTimeString())
 
201
        elif self.dots and self.pb is None:
 
202
            self.stream.write('E')
 
203
        elif self.dots:
 
204
            self.pb.update(self._ellipsise_unimportant_words('ERROR', 13), self.testsRun, None)
 
205
        self.stream.flush()
 
206
        if self.stop_early:
 
207
            self.stop()
 
208
 
 
209
    def addFailure(self, test, err):
 
210
        unittest.TestResult.addFailure(self, test, err)
 
211
        self.extractBenchmarkTime(test)
 
212
        if self.showAll:
 
213
            self.stream.writeln(" FAIL %s" % self._testTimeString())
 
214
        elif self.dots and self.pb is None:
 
215
            self.stream.write('F')
 
216
        elif self.dots:
 
217
            self.pb.update(self._ellipsise_unimportant_words('FAIL', 13), self.testsRun, None)
 
218
        self.stream.flush()
 
219
        if self.stop_early:
 
220
            self.stop()
 
221
 
 
222
    def addSuccess(self, test):
 
223
        self.extractBenchmarkTime(test)
 
224
        if self.showAll:
 
225
            self.stream.writeln('   OK %s' % self._testTimeString())
 
226
        elif self.dots and self.pb is None:
 
227
            self.stream.write('~')
 
228
        elif self.dots:
 
229
            self.pb.update(self._ellipsise_unimportant_words('OK', 13), self.testsRun, None)
 
230
        self.stream.flush()
 
231
        unittest.TestResult.addSuccess(self, test)
 
232
 
 
233
    def addSkipped(self, test, skip_excinfo):
 
234
        self.extractBenchmarkTime(test)
 
235
        if self.showAll:
 
236
            print >>self.stream, ' SKIP %s' % self._testTimeString()
 
237
            print >>self.stream, '     %s' % skip_excinfo[1]
 
238
        elif self.dots and self.pb is None:
 
239
            self.stream.write('S')
 
240
        elif self.dots:
 
241
            self.pb.update(self._ellipsise_unimportant_words('SKIP', 13), self.testsRun, None)
 
242
        self.stream.flush()
 
243
        # seems best to treat this as success from point-of-view of unittest
 
244
        # -- it actually does nothing so it barely matters :)
 
245
        unittest.TestResult.addSuccess(self, test)
 
246
 
 
247
    def printErrorList(self, flavour, errors):
 
248
        for test, err in errors:
 
249
            self.stream.writeln(self.separator1)
 
250
            self.stream.writeln("%s: %s" % (flavour, self.getDescription(test)))
 
251
            if getattr(test, '_get_log', None) is not None:
 
252
                print >>self.stream
 
253
                print >>self.stream, \
 
254
                        ('vvvv[log from %s]' % test.id()).ljust(78,'-')
 
255
                print >>self.stream, test._get_log()
 
256
                print >>self.stream, \
 
257
                        ('^^^^[log from %s]' % test.id()).ljust(78,'-')
 
258
            self.stream.writeln(self.separator2)
 
259
            self.stream.writeln("%s" % err)
 
260
 
 
261
 
 
262
class TextTestRunner(object):
 
263
    stop_on_failure = False
 
264
 
 
265
    def __init__(self,
 
266
                 stream=sys.stderr,
 
267
                 descriptions=0,
 
268
                 verbosity=1,
 
269
                 keep_output=False,
 
270
                 pb=None):
 
271
        self.stream = unittest._WritelnDecorator(stream)
 
272
        self.descriptions = descriptions
 
273
        self.verbosity = verbosity
 
274
        self.keep_output = keep_output
 
275
        self.pb = pb
 
276
 
 
277
    def _makeResult(self):
 
278
        result = _MyResult(self.stream,
 
279
                           self.descriptions,
 
280
                           self.verbosity,
 
281
                           pb=self.pb)
 
282
        result.stop_early = self.stop_on_failure
 
283
        return result
 
284
 
 
285
    def run(self, test):
 
286
        "Run the given test case or test suite."
 
287
        result = self._makeResult()
 
288
        startTime = time.time()
 
289
        if self.pb is not None:
 
290
            self.pb.update('Running tests', 0, test.countTestCases())
 
291
        test.run(result)
 
292
        stopTime = time.time()
 
293
        timeTaken = stopTime - startTime
 
294
        result.printErrors()
 
295
        self.stream.writeln(result.separator2)
 
296
        run = result.testsRun
 
297
        self.stream.writeln("Ran %d test%s in %.3fs" %
 
298
                            (run, run != 1 and "s" or "", timeTaken))
 
299
        self.stream.writeln()
 
300
        if not result.wasSuccessful():
 
301
            self.stream.write("FAILED (")
 
302
            failed, errored = map(len, (result.failures, result.errors))
 
303
            if failed:
 
304
                self.stream.write("failures=%d" % failed)
 
305
            if errored:
 
306
                if failed: self.stream.write(", ")
 
307
                self.stream.write("errors=%d" % errored)
 
308
            self.stream.writeln(")")
 
309
        else:
 
310
            self.stream.writeln("OK")
 
311
        if self.pb is not None:
 
312
            self.pb.update('Cleaning up', 0, 1)
 
313
        # This is still a little bogus, 
 
314
        # but only a little. Folk not using our testrunner will
 
315
        # have to delete their temp directories themselves.
 
316
        test_root = TestCaseInTempDir.TEST_ROOT
 
317
        if result.wasSuccessful() or not self.keep_output:
 
318
            if test_root is not None:
 
319
                    osutils.rmtree(test_root)
 
320
        else:
 
321
            if self.pb is not None:
 
322
                self.pb.note("Failed tests working directories are in '%s'\n",
 
323
                             test_root)
 
324
            else:
 
325
                self.stream.writeln(
 
326
                    "Failed tests working directories are in '%s'\n" %
 
327
                    test_root)
 
328
        TestCaseInTempDir.TEST_ROOT = None
 
329
        if self.pb is not None:
 
330
            self.pb.clear()
 
331
        return result
 
332
 
 
333
 
 
334
def iter_suite_tests(suite):
 
335
    """Return all tests in a suite, recursing through nested suites"""
 
336
    for item in suite._tests:
 
337
        if isinstance(item, unittest.TestCase):
 
338
            yield item
 
339
        elif isinstance(item, unittest.TestSuite):
 
340
            for r in iter_suite_tests(item):
 
341
                yield r
 
342
        else:
 
343
            raise Exception('unknown object %r inside test suite %r'
 
344
                            % (item, suite))
 
345
 
 
346
 
 
347
class TestSkipped(Exception):
 
348
    """Indicates that a test was intentionally skipped, rather than failing."""
 
349
    # XXX: Not used yet
 
350
 
 
351
 
 
352
class CommandFailed(Exception):
 
353
    pass
 
354
 
 
355
class TestCase(unittest.TestCase):
 
356
    """Base class for bzr unit tests.
 
357
    
 
358
    Tests that need access to disk resources should subclass 
 
359
    TestCaseInTempDir not TestCase.
 
360
 
 
361
    Error and debug log messages are redirected from their usual
 
362
    location into a temporary file, the contents of which can be
 
363
    retrieved by _get_log().  We use a real OS file, not an in-memory object,
 
364
    so that it can also capture file IO.  When the test completes this file
 
365
    is read into memory and removed from disk.
 
366
       
 
367
    There are also convenience functions to invoke bzr's command-line
 
368
    routine, and to build and check bzr trees.
 
369
   
 
370
    In addition to the usual method of overriding tearDown(), this class also
 
371
    allows subclasses to register functions into the _cleanups list, which is
 
372
    run in order as the object is torn down.  It's less likely this will be
 
373
    accidentally overlooked.
 
374
    """
 
375
 
 
376
    _log_file_name = None
 
377
    _log_contents = ''
 
378
 
 
379
    def __init__(self, methodName='testMethod'):
 
380
        super(TestCase, self).__init__(methodName)
 
381
        self._cleanups = []
 
382
 
 
383
    def setUp(self):
 
384
        unittest.TestCase.setUp(self)
 
385
        self._cleanEnvironment()
 
386
        bzrlib.trace.disable_default_logging()
 
387
        self._startLogFile()
 
388
        self._benchtime = None
 
389
 
 
390
    def _ndiff_strings(self, a, b):
 
391
        """Return ndiff between two strings containing lines.
 
392
        
 
393
        A trailing newline is added if missing to make the strings
 
394
        print properly."""
 
395
        if b and b[-1] != '\n':
 
396
            b += '\n'
 
397
        if a and a[-1] != '\n':
 
398
            a += '\n'
 
399
        difflines = difflib.ndiff(a.splitlines(True),
 
400
                                  b.splitlines(True),
 
401
                                  linejunk=lambda x: False,
 
402
                                  charjunk=lambda x: False)
 
403
        return ''.join(difflines)
 
404
 
 
405
    def assertEqualDiff(self, a, b, message=None):
 
406
        """Assert two texts are equal, if not raise an exception.
 
407
        
 
408
        This is intended for use with multi-line strings where it can 
 
409
        be hard to find the differences by eye.
 
410
        """
 
411
        # TODO: perhaps override assertEquals to call this for strings?
 
412
        if a == b:
 
413
            return
 
414
        if message is None:
 
415
            message = "texts not equal:\n"
 
416
        raise AssertionError(message + 
 
417
                             self._ndiff_strings(a, b))      
 
418
        
 
419
    def assertEqualMode(self, mode, mode_test):
 
420
        self.assertEqual(mode, mode_test,
 
421
                         'mode mismatch %o != %o' % (mode, mode_test))
 
422
 
 
423
    def assertStartsWith(self, s, prefix):
 
424
        if not s.startswith(prefix):
 
425
            raise AssertionError('string %r does not start with %r' % (s, prefix))
 
426
 
 
427
    def assertEndsWith(self, s, suffix):
 
428
        """Asserts that s ends with suffix."""
 
429
        if not s.endswith(suffix):
 
430
            raise AssertionError('string %r does not end with %r' % (s, suffix))
 
431
 
 
432
    def assertContainsRe(self, haystack, needle_re):
 
433
        """Assert that a contains something matching a regular expression."""
 
434
        if not re.search(needle_re, haystack):
 
435
            raise AssertionError('pattern "%s" not found in "%s"'
 
436
                    % (needle_re, haystack))
 
437
 
 
438
    def assertSubset(self, sublist, superlist):
 
439
        """Assert that every entry in sublist is present in superlist."""
 
440
        missing = []
 
441
        for entry in sublist:
 
442
            if entry not in superlist:
 
443
                missing.append(entry)
 
444
        if len(missing) > 0:
 
445
            raise AssertionError("value(s) %r not present in container %r" % 
 
446
                                 (missing, superlist))
 
447
 
 
448
    def assertIs(self, left, right):
 
449
        if not (left is right):
 
450
            raise AssertionError("%r is not %r." % (left, right))
 
451
 
 
452
    def assertTransportMode(self, transport, path, mode):
 
453
        """Fail if a path does not have mode mode.
 
454
        
 
455
        If modes are not supported on this transport, the assertion is ignored.
 
456
        """
 
457
        if not transport._can_roundtrip_unix_modebits():
 
458
            return
 
459
        path_stat = transport.stat(path)
 
460
        actual_mode = stat.S_IMODE(path_stat.st_mode)
 
461
        self.assertEqual(mode, actual_mode,
 
462
            'mode of %r incorrect (%o != %o)' % (path, mode, actual_mode))
 
463
 
 
464
    def assertIsInstance(self, obj, kls):
 
465
        """Fail if obj is not an instance of kls"""
 
466
        if not isinstance(obj, kls):
 
467
            self.fail("%r is an instance of %s rather than %s" % (
 
468
                obj, obj.__class__, kls))
 
469
 
 
470
    def _startLogFile(self):
 
471
        """Send bzr and test log messages to a temporary file.
 
472
 
 
473
        The file is removed as the test is torn down.
 
474
        """
 
475
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
 
476
        encoder, decoder, stream_reader, stream_writer = codecs.lookup('UTF-8')
 
477
        self._log_file = stream_writer(os.fdopen(fileno, 'w+'))
 
478
        self._log_nonce = bzrlib.trace.enable_test_log(self._log_file)
 
479
        self._log_file_name = name
 
480
        self.addCleanup(self._finishLogFile)
 
481
 
 
482
    def _finishLogFile(self):
 
483
        """Finished with the log file.
 
484
 
 
485
        Read contents into memory, close, and delete.
 
486
        """
 
487
        bzrlib.trace.disable_test_log(self._log_nonce)
 
488
        self._log_file.seek(0)
 
489
        self._log_contents = self._log_file.read()
 
490
        self._log_file.close()
 
491
        os.remove(self._log_file_name)
 
492
        self._log_file = self._log_file_name = None
 
493
 
 
494
    def addCleanup(self, callable):
 
495
        """Arrange to run a callable when this case is torn down.
 
496
 
 
497
        Callables are run in the reverse of the order they are registered, 
 
498
        ie last-in first-out.
 
499
        """
 
500
        if callable in self._cleanups:
 
501
            raise ValueError("cleanup function %r already registered on %s" 
 
502
                    % (callable, self))
 
503
        self._cleanups.append(callable)
 
504
 
 
505
    def _cleanEnvironment(self):
 
506
        new_env = {
 
507
            'HOME': os.getcwd(),
 
508
            'APPDATA': os.getcwd(),
 
509
            'BZREMAIL': None,
 
510
            'EMAIL': None,
 
511
        }
 
512
        self.__old_env = {}
 
513
        self.addCleanup(self._restoreEnvironment)
 
514
        for name, value in new_env.iteritems():
 
515
            self._captureVar(name, value)
 
516
 
 
517
 
 
518
    def _captureVar(self, name, newvalue):
 
519
        """Set an environment variable, preparing it to be reset when finished."""
 
520
        self.__old_env[name] = os.environ.get(name, None)
 
521
        if newvalue is None:
 
522
            if name in os.environ:
 
523
                del os.environ[name]
 
524
        else:
 
525
            os.environ[name] = newvalue
 
526
 
 
527
    @staticmethod
 
528
    def _restoreVar(name, value):
 
529
        if value is None:
 
530
            if name in os.environ:
 
531
                del os.environ[name]
 
532
        else:
 
533
            os.environ[name] = value
 
534
 
 
535
    def _restoreEnvironment(self):
 
536
        for name, value in self.__old_env.iteritems():
 
537
            self._restoreVar(name, value)
 
538
 
 
539
    def tearDown(self):
 
540
        self._runCleanups()
 
541
        unittest.TestCase.tearDown(self)
 
542
 
 
543
    def time(self, callable, *args, **kwargs):
 
544
        """Run callable and accrue the time it takes to the benchmark time."""
 
545
        if self._benchtime is None:
 
546
            self._benchtime = 0
 
547
        start = time.time()
 
548
        try:
 
549
            callable(*args, **kwargs)
 
550
        finally:
 
551
            self._benchtime += time.time() - start
 
552
 
 
553
    def _runCleanups(self):
 
554
        """Run registered cleanup functions. 
 
555
 
 
556
        This should only be called from TestCase.tearDown.
 
557
        """
 
558
        # TODO: Perhaps this should keep running cleanups even if 
 
559
        # one of them fails?
 
560
        for cleanup_fn in reversed(self._cleanups):
 
561
            cleanup_fn()
 
562
 
 
563
    def log(self, *args):
 
564
        mutter(*args)
 
565
 
 
566
    def _get_log(self):
 
567
        """Return as a string the log for this test"""
 
568
        if self._log_file_name:
 
569
            return open(self._log_file_name).read()
 
570
        else:
 
571
            return self._log_contents
 
572
        # TODO: Delete the log after it's been read in
 
573
 
 
574
    def capture(self, cmd, retcode=0):
 
575
        """Shortcut that splits cmd into words, runs, and returns stdout"""
 
576
        return self.run_bzr_captured(cmd.split(), retcode=retcode)[0]
 
577
 
 
578
    def run_bzr_captured(self, argv, retcode=0, stdin=None):
 
579
        """Invoke bzr and return (stdout, stderr).
 
580
 
 
581
        Useful for code that wants to check the contents of the
 
582
        output, the way error messages are presented, etc.
 
583
 
 
584
        This should be the main method for tests that want to exercise the
 
585
        overall behavior of the bzr application (rather than a unit test
 
586
        or a functional test of the library.)
 
587
 
 
588
        Much of the old code runs bzr by forking a new copy of Python, but
 
589
        that is slower, harder to debug, and generally not necessary.
 
590
 
 
591
        This runs bzr through the interface that catches and reports
 
592
        errors, and with logging set to something approximating the
 
593
        default, so that error reporting can be checked.
 
594
 
 
595
        argv -- arguments to invoke bzr
 
596
        retcode -- expected return code, or None for don't-care.
 
597
        :param stdin: A string to be used as stdin for the command.
 
598
        """
 
599
        if stdin is not None:
 
600
            stdin = StringIO(stdin)
 
601
        stdout = StringIO()
 
602
        stderr = StringIO()
 
603
        self.log('run bzr: %s', ' '.join(argv))
 
604
        # FIXME: don't call into logging here
 
605
        handler = logging.StreamHandler(stderr)
 
606
        handler.setFormatter(bzrlib.trace.QuietFormatter())
 
607
        handler.setLevel(logging.INFO)
 
608
        logger = logging.getLogger('')
 
609
        logger.addHandler(handler)
 
610
        old_ui_factory = bzrlib.ui.ui_factory
 
611
        bzrlib.ui.ui_factory = bzrlib.tests.blackbox.TestUIFactory(
 
612
            stdout=stdout,
 
613
            stderr=stderr)
 
614
        bzrlib.ui.ui_factory.stdin = stdin
 
615
        try:
 
616
            result = self.apply_redirected(stdin, stdout, stderr,
 
617
                                           bzrlib.commands.run_bzr_catch_errors,
 
618
                                           argv)
 
619
        finally:
 
620
            logger.removeHandler(handler)
 
621
            bzrlib.ui.ui_factory = old_ui_factory
 
622
        out = stdout.getvalue()
 
623
        err = stderr.getvalue()
 
624
        if out:
 
625
            self.log('output:\n%s', out)
 
626
        if err:
 
627
            self.log('errors:\n%s', err)
 
628
        if retcode is not None:
 
629
            self.assertEquals(result, retcode)
 
630
        return out, err
 
631
 
 
632
    def run_bzr(self, *args, **kwargs):
 
633
        """Invoke bzr, as if it were run from the command line.
 
634
 
 
635
        This should be the main method for tests that want to exercise the
 
636
        overall behavior of the bzr application (rather than a unit test
 
637
        or a functional test of the library.)
 
638
 
 
639
        This sends the stdout/stderr results into the test's log,
 
640
        where it may be useful for debugging.  See also run_captured.
 
641
 
 
642
        :param stdin: A string to be used as stdin for the command.
 
643
        """
 
644
        retcode = kwargs.pop('retcode', 0)
 
645
        stdin = kwargs.pop('stdin', None)
 
646
        return self.run_bzr_captured(args, retcode, stdin)
 
647
 
 
648
    def check_inventory_shape(self, inv, shape):
 
649
        """Compare an inventory to a list of expected names.
 
650
 
 
651
        Fail if they are not precisely equal.
 
652
        """
 
653
        extras = []
 
654
        shape = list(shape)             # copy
 
655
        for path, ie in inv.entries():
 
656
            name = path.replace('\\', '/')
 
657
            if ie.kind == 'dir':
 
658
                name = name + '/'
 
659
            if name in shape:
 
660
                shape.remove(name)
 
661
            else:
 
662
                extras.append(name)
 
663
        if shape:
 
664
            self.fail("expected paths not found in inventory: %r" % shape)
 
665
        if extras:
 
666
            self.fail("unexpected paths found in inventory: %r" % extras)
 
667
 
 
668
    def apply_redirected(self, stdin=None, stdout=None, stderr=None,
 
669
                         a_callable=None, *args, **kwargs):
 
670
        """Call callable with redirected std io pipes.
 
671
 
 
672
        Returns the return code."""
 
673
        if not callable(a_callable):
 
674
            raise ValueError("a_callable must be callable.")
 
675
        if stdin is None:
 
676
            stdin = StringIO("")
 
677
        if stdout is None:
 
678
            if getattr(self, "_log_file", None) is not None:
 
679
                stdout = self._log_file
 
680
            else:
 
681
                stdout = StringIO()
 
682
        if stderr is None:
 
683
            if getattr(self, "_log_file", None is not None):
 
684
                stderr = self._log_file
 
685
            else:
 
686
                stderr = StringIO()
 
687
        real_stdin = sys.stdin
 
688
        real_stdout = sys.stdout
 
689
        real_stderr = sys.stderr
 
690
        try:
 
691
            sys.stdout = stdout
 
692
            sys.stderr = stderr
 
693
            sys.stdin = stdin
 
694
            return a_callable(*args, **kwargs)
 
695
        finally:
 
696
            sys.stdout = real_stdout
 
697
            sys.stderr = real_stderr
 
698
            sys.stdin = real_stdin
 
699
 
 
700
    def merge(self, branch_from, wt_to):
 
701
        """A helper for tests to do a ui-less merge.
 
702
 
 
703
        This should move to the main library when someone has time to integrate
 
704
        it in.
 
705
        """
 
706
        # minimal ui-less merge.
 
707
        wt_to.branch.fetch(branch_from)
 
708
        base_rev = common_ancestor(branch_from.last_revision(),
 
709
                                   wt_to.branch.last_revision(),
 
710
                                   wt_to.branch.repository)
 
711
        merge_inner(wt_to.branch, branch_from.basis_tree(), 
 
712
                    wt_to.branch.repository.revision_tree(base_rev),
 
713
                    this_tree=wt_to)
 
714
        wt_to.add_pending_merge(branch_from.last_revision())
 
715
 
 
716
 
 
717
BzrTestBase = TestCase
 
718
 
 
719
     
 
720
class TestCaseInTempDir(TestCase):
 
721
    """Derived class that runs a test within a temporary directory.
 
722
 
 
723
    This is useful for tests that need to create a branch, etc.
 
724
 
 
725
    The directory is created in a slightly complex way: for each
 
726
    Python invocation, a new temporary top-level directory is created.
 
727
    All test cases create their own directory within that.  If the
 
728
    tests complete successfully, the directory is removed.
 
729
 
 
730
    InTempDir is an old alias for FunctionalTestCase.
 
731
    """
 
732
 
 
733
    TEST_ROOT = None
 
734
    _TEST_NAME = 'test'
 
735
    OVERRIDE_PYTHON = 'python'
 
736
 
 
737
    def check_file_contents(self, filename, expect):
 
738
        self.log("check contents of file %s" % filename)
 
739
        contents = file(filename, 'r').read()
 
740
        if contents != expect:
 
741
            self.log("expected: %r" % expect)
 
742
            self.log("actually: %r" % contents)
 
743
            self.fail("contents of %s not as expected" % filename)
 
744
 
 
745
    def _make_test_root(self):
 
746
        if TestCaseInTempDir.TEST_ROOT is not None:
 
747
            return
 
748
        i = 0
 
749
        while True:
 
750
            root = u'test%04d.tmp' % i
 
751
            try:
 
752
                os.mkdir(root)
 
753
            except OSError, e:
 
754
                if e.errno == errno.EEXIST:
 
755
                    i += 1
 
756
                    continue
 
757
                else:
 
758
                    raise
 
759
            # successfully created
 
760
            TestCaseInTempDir.TEST_ROOT = osutils.abspath(root)
 
761
            break
 
762
        # make a fake bzr directory there to prevent any tests propagating
 
763
        # up onto the source directory's real branch
 
764
        bzrdir.BzrDir.create_standalone_workingtree(TestCaseInTempDir.TEST_ROOT)
 
765
 
 
766
    def setUp(self):
 
767
        super(TestCaseInTempDir, self).setUp()
 
768
        self._make_test_root()
 
769
        _currentdir = os.getcwdu()
 
770
        # shorten the name, to avoid test failures due to path length
 
771
        short_id = self.id().replace('bzrlib.tests.', '') \
 
772
                   .replace('__main__.', '')[-100:]
 
773
        # it's possible the same test class is run several times for
 
774
        # parameterized tests, so make sure the names don't collide.  
 
775
        i = 0
 
776
        while True:
 
777
            if i > 0:
 
778
                candidate_dir = '%s/%s.%d' % (self.TEST_ROOT, short_id, i)
 
779
            else:
 
780
                candidate_dir = '%s/%s' % (self.TEST_ROOT, short_id)
 
781
            if os.path.exists(candidate_dir):
 
782
                i = i + 1
 
783
                continue
 
784
            else:
 
785
                self.test_dir = candidate_dir
 
786
                os.mkdir(self.test_dir)
 
787
                os.chdir(self.test_dir)
 
788
                break
 
789
        os.environ['HOME'] = self.test_dir
 
790
        os.environ['APPDATA'] = self.test_dir
 
791
        def _leaveDirectory():
 
792
            os.chdir(_currentdir)
 
793
        self.addCleanup(_leaveDirectory)
 
794
        
 
795
    def build_tree(self, shape, line_endings='native', transport=None):
 
796
        """Build a test tree according to a pattern.
 
797
 
 
798
        shape is a sequence of file specifications.  If the final
 
799
        character is '/', a directory is created.
 
800
 
 
801
        This doesn't add anything to a branch.
 
802
        :param line_endings: Either 'binary' or 'native'
 
803
                             in binary mode, exact contents are written
 
804
                             in native mode, the line endings match the
 
805
                             default platform endings.
 
806
 
 
807
        :param transport: A transport to write to, for building trees on 
 
808
                          VFS's. If the transport is readonly or None,
 
809
                          "." is opened automatically.
 
810
        """
 
811
        # XXX: It's OK to just create them using forward slashes on windows?
 
812
        if transport is None or transport.is_readonly():
 
813
            transport = get_transport(".")
 
814
        for name in shape:
 
815
            self.assert_(isinstance(name, basestring))
 
816
            if name[-1] == '/':
 
817
                transport.mkdir(urlescape(name[:-1]))
 
818
            else:
 
819
                if line_endings == 'binary':
 
820
                    end = '\n'
 
821
                elif line_endings == 'native':
 
822
                    end = os.linesep
 
823
                else:
 
824
                    raise errors.BzrError('Invalid line ending request %r' % (line_endings,))
 
825
                content = "contents of %s%s" % (name, end)
 
826
                transport.put(urlescape(name), StringIO(content))
 
827
 
 
828
    def build_tree_contents(self, shape):
 
829
        build_tree_contents(shape)
 
830
 
 
831
    def failUnlessExists(self, path):
 
832
        """Fail unless path, which may be abs or relative, exists."""
 
833
        self.failUnless(osutils.lexists(path))
 
834
 
 
835
    def failIfExists(self, path):
 
836
        """Fail if path, which may be abs or relative, exists."""
 
837
        self.failIf(osutils.lexists(path))
 
838
        
 
839
    def assertFileEqual(self, content, path):
 
840
        """Fail if path does not contain 'content'."""
 
841
        self.failUnless(osutils.lexists(path))
 
842
        self.assertEqualDiff(content, open(path, 'r').read())
 
843
 
 
844
 
 
845
class TestCaseWithTransport(TestCaseInTempDir):
 
846
    """A test case that provides get_url and get_readonly_url facilities.
 
847
 
 
848
    These back onto two transport servers, one for readonly access and one for
 
849
    read write access.
 
850
 
 
851
    If no explicit class is provided for readonly access, a
 
852
    ReadonlyTransportDecorator is used instead which allows the use of non disk
 
853
    based read write transports.
 
854
 
 
855
    If an explicit class is provided for readonly access, that server and the 
 
856
    readwrite one must both define get_url() as resolving to os.getcwd().
 
857
    """
 
858
 
 
859
    def __init__(self, methodName='testMethod'):
 
860
        super(TestCaseWithTransport, self).__init__(methodName)
 
861
        self.__readonly_server = None
 
862
        self.__server = None
 
863
        self.transport_server = default_transport
 
864
        self.transport_readonly_server = None
 
865
 
 
866
    def get_readonly_url(self, relpath=None):
 
867
        """Get a URL for the readonly transport.
 
868
 
 
869
        This will either be backed by '.' or a decorator to the transport 
 
870
        used by self.get_url()
 
871
        relpath provides for clients to get a path relative to the base url.
 
872
        These should only be downwards relative, not upwards.
 
873
        """
 
874
        base = self.get_readonly_server().get_url()
 
875
        if relpath is not None:
 
876
            if not base.endswith('/'):
 
877
                base = base + '/'
 
878
            base = base + relpath
 
879
        return base
 
880
 
 
881
    def get_readonly_server(self):
 
882
        """Get the server instance for the readonly transport
 
883
 
 
884
        This is useful for some tests with specific servers to do diagnostics.
 
885
        """
 
886
        if self.__readonly_server is None:
 
887
            if self.transport_readonly_server is None:
 
888
                # readonly decorator requested
 
889
                # bring up the server
 
890
                self.get_url()
 
891
                self.__readonly_server = ReadonlyServer()
 
892
                self.__readonly_server.setUp(self.__server)
 
893
            else:
 
894
                self.__readonly_server = self.transport_readonly_server()
 
895
                self.__readonly_server.setUp()
 
896
            self.addCleanup(self.__readonly_server.tearDown)
 
897
        return self.__readonly_server
 
898
 
 
899
    def get_server(self):
 
900
        """Get the read/write server instance.
 
901
 
 
902
        This is useful for some tests with specific servers that need
 
903
        diagnostics.
 
904
        """
 
905
        if self.__server is None:
 
906
            self.__server = self.transport_server()
 
907
            self.__server.setUp()
 
908
            self.addCleanup(self.__server.tearDown)
 
909
        return self.__server
 
910
 
 
911
    def get_url(self, relpath=None):
 
912
        """Get a URL for the readwrite transport.
 
913
 
 
914
        This will either be backed by '.' or to an equivalent non-file based
 
915
        facility.
 
916
        relpath provides for clients to get a path relative to the base url.
 
917
        These should only be downwards relative, not upwards.
 
918
        """
 
919
        base = self.get_server().get_url()
 
920
        if relpath is not None and relpath != '.':
 
921
            if not base.endswith('/'):
 
922
                base = base + '/'
 
923
            base = base + relpath
 
924
        return base
 
925
 
 
926
    def get_transport(self):
 
927
        """Return a writeable transport for the test scratch space"""
 
928
        t = get_transport(self.get_url())
 
929
        self.assertFalse(t.is_readonly())
 
930
        return t
 
931
 
 
932
    def get_readonly_transport(self):
 
933
        """Return a readonly transport for the test scratch space
 
934
        
 
935
        This can be used to test that operations which should only need
 
936
        readonly access in fact do not try to write.
 
937
        """
 
938
        t = get_transport(self.get_readonly_url())
 
939
        self.assertTrue(t.is_readonly())
 
940
        return t
 
941
 
 
942
    def make_branch(self, relpath, format=None):
 
943
        """Create a branch on the transport at relpath."""
 
944
        repo = self.make_repository(relpath, format=format)
 
945
        return repo.bzrdir.create_branch()
 
946
 
 
947
    def make_bzrdir(self, relpath, format=None):
 
948
        try:
 
949
            url = self.get_url(relpath)
 
950
            segments = relpath.split('/')
 
951
            if segments and segments[-1] not in ('', '.'):
 
952
                parent = self.get_url('/'.join(segments[:-1]))
 
953
                t = get_transport(parent)
 
954
                try:
 
955
                    t.mkdir(segments[-1])
 
956
                except errors.FileExists:
 
957
                    pass
 
958
            if format is None:
 
959
                format=bzrlib.bzrdir.BzrDirFormat.get_default_format()
 
960
            # FIXME: make this use a single transport someday. RBC 20060418
 
961
            return format.initialize_on_transport(get_transport(relpath))
 
962
        except errors.UninitializableFormat:
 
963
            raise TestSkipped("Format %s is not initializable." % format)
 
964
 
 
965
    def make_repository(self, relpath, shared=False, format=None):
 
966
        """Create a repository on our default transport at relpath."""
 
967
        made_control = self.make_bzrdir(relpath, format=format)
 
968
        return made_control.create_repository(shared=shared)
 
969
 
 
970
    def make_branch_and_tree(self, relpath, format=None):
 
971
        """Create a branch on the transport and a tree locally.
 
972
 
 
973
        Returns the tree.
 
974
        """
 
975
        # TODO: always use the local disk path for the working tree,
 
976
        # this obviously requires a format that supports branch references
 
977
        # so check for that by checking bzrdir.BzrDirFormat.get_default_format()
 
978
        # RBC 20060208
 
979
        b = self.make_branch(relpath, format=format)
 
980
        try:
 
981
            return b.bzrdir.create_workingtree()
 
982
        except errors.NotLocalUrl:
 
983
            # new formats - catch No tree error and create
 
984
            # a branch reference and a checkout.
 
985
            # old formats at that point - raise TestSkipped.
 
986
            # TODO: rbc 20060208
 
987
            return WorkingTreeFormat2().initialize(bzrdir.BzrDir.open(relpath))
 
988
 
 
989
    def assertIsDirectory(self, relpath, transport):
 
990
        """Assert that relpath within transport is a directory.
 
991
 
 
992
        This may not be possible on all transports; in that case it propagates
 
993
        a TransportNotPossible.
 
994
        """
 
995
        try:
 
996
            mode = transport.stat(relpath).st_mode
 
997
        except errors.NoSuchFile:
 
998
            self.fail("path %s is not a directory; no such file"
 
999
                      % (relpath))
 
1000
        if not stat.S_ISDIR(mode):
 
1001
            self.fail("path %s is not a directory; has mode %#o"
 
1002
                      % (relpath, mode))
 
1003
 
 
1004
 
 
1005
class ChrootedTestCase(TestCaseWithTransport):
 
1006
    """A support class that provides readonly urls outside the local namespace.
 
1007
 
 
1008
    This is done by checking if self.transport_server is a MemoryServer. if it
 
1009
    is then we are chrooted already, if it is not then an HttpServer is used
 
1010
    for readonly urls.
 
1011
 
 
1012
    TODO RBC 20060127: make this an option to TestCaseWithTransport so it can
 
1013
                       be used without needed to redo it when a different 
 
1014
                       subclass is in use ?
 
1015
    """
 
1016
 
 
1017
    def setUp(self):
 
1018
        super(ChrootedTestCase, self).setUp()
 
1019
        if not self.transport_server == bzrlib.transport.memory.MemoryServer:
 
1020
            self.transport_readonly_server = bzrlib.transport.http.HttpServer
 
1021
 
 
1022
 
 
1023
def filter_suite_by_re(suite, pattern):
 
1024
    result = TestSuite()
 
1025
    filter_re = re.compile(pattern)
 
1026
    for test in iter_suite_tests(suite):
 
1027
        if filter_re.search(test.id()):
 
1028
            result.addTest(test)
 
1029
    return result
 
1030
 
 
1031
 
 
1032
def run_suite(suite, name='test', verbose=False, pattern=".*",
 
1033
              stop_on_failure=False, keep_output=False,
 
1034
              transport=None):
 
1035
    TestCaseInTempDir._TEST_NAME = name
 
1036
    if verbose:
 
1037
        verbosity = 2
 
1038
        pb = None
 
1039
    else:
 
1040
        verbosity = 1
 
1041
        pb = progress.ProgressBar()
 
1042
    runner = TextTestRunner(stream=sys.stdout,
 
1043
                            descriptions=0,
 
1044
                            verbosity=verbosity,
 
1045
                            keep_output=keep_output,
 
1046
                            pb=pb)
 
1047
    runner.stop_on_failure=stop_on_failure
 
1048
    if pattern != '.*':
 
1049
        suite = filter_suite_by_re(suite, pattern)
 
1050
    result = runner.run(suite)
 
1051
    return result.wasSuccessful()
 
1052
 
 
1053
 
 
1054
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
 
1055
             keep_output=False,
 
1056
             transport=None,
 
1057
             test_suite_factory=None):
 
1058
    """Run the whole test suite under the enhanced runner"""
 
1059
    global default_transport
 
1060
    if transport is None:
 
1061
        transport = default_transport
 
1062
    old_transport = default_transport
 
1063
    default_transport = transport
 
1064
    try:
 
1065
        if test_suite_factory is None:
 
1066
            suite = test_suite()
 
1067
        else:
 
1068
            suite = test_suite_factory()
 
1069
        return run_suite(suite, 'testbzr', verbose=verbose, pattern=pattern,
 
1070
                     stop_on_failure=stop_on_failure, keep_output=keep_output,
 
1071
                     transport=transport)
 
1072
    finally:
 
1073
        default_transport = old_transport
 
1074
 
 
1075
 
 
1076
def test_suite():
 
1077
    """Build and return TestSuite for the whole of bzrlib.
 
1078
    
 
1079
    This function can be replaced if you need to change the default test
 
1080
    suite on a global basis, but it is not encouraged.
 
1081
    """
 
1082
    from doctest import DocTestSuite
 
1083
 
 
1084
    global MODULES_TO_DOCTEST
 
1085
 
 
1086
    testmod_names = [ \
 
1087
                   'bzrlib.tests.test_ancestry',
 
1088
                   'bzrlib.tests.test_api',
 
1089
                   'bzrlib.tests.test_bad_files',
 
1090
                   'bzrlib.tests.test_branch',
 
1091
                   'bzrlib.tests.test_bzrdir',
 
1092
                   'bzrlib.tests.test_command',
 
1093
                   'bzrlib.tests.test_commit',
 
1094
                   'bzrlib.tests.test_commit_merge',
 
1095
                   'bzrlib.tests.test_config',
 
1096
                   'bzrlib.tests.test_conflicts',
 
1097
                   'bzrlib.tests.test_decorators',
 
1098
                   'bzrlib.tests.test_diff',
 
1099
                   'bzrlib.tests.test_doc_generate',
 
1100
                   'bzrlib.tests.test_errors',
 
1101
                   'bzrlib.tests.test_escaped_store',
 
1102
                   'bzrlib.tests.test_fetch',
 
1103
                   'bzrlib.tests.test_gpg',
 
1104
                   'bzrlib.tests.test_graph',
 
1105
                   'bzrlib.tests.test_hashcache',
 
1106
                   'bzrlib.tests.test_http',
 
1107
                   'bzrlib.tests.test_identitymap',
 
1108
                   'bzrlib.tests.test_inv',
 
1109
                   'bzrlib.tests.test_knit',
 
1110
                   'bzrlib.tests.test_lockdir',
 
1111
                   'bzrlib.tests.test_lockable_files',
 
1112
                   'bzrlib.tests.test_log',
 
1113
                   'bzrlib.tests.test_merge',
 
1114
                   'bzrlib.tests.test_merge3',
 
1115
                   'bzrlib.tests.test_merge_core',
 
1116
                   'bzrlib.tests.test_missing',
 
1117
                   'bzrlib.tests.test_msgeditor',
 
1118
                   'bzrlib.tests.test_nonascii',
 
1119
                   'bzrlib.tests.test_options',
 
1120
                   'bzrlib.tests.test_osutils',
 
1121
                   'bzrlib.tests.test_patch',
 
1122
                   'bzrlib.tests.test_permissions',
 
1123
                   'bzrlib.tests.test_plugins',
 
1124
                   'bzrlib.tests.test_progress',
 
1125
                   'bzrlib.tests.test_reconcile',
 
1126
                   'bzrlib.tests.test_repository',
 
1127
                   'bzrlib.tests.test_revision',
 
1128
                   'bzrlib.tests.test_revisionnamespaces',
 
1129
                   'bzrlib.tests.test_revprops',
 
1130
                   'bzrlib.tests.test_rio',
 
1131
                   'bzrlib.tests.test_sampler',
 
1132
                   'bzrlib.tests.test_selftest',
 
1133
                   'bzrlib.tests.test_setup',
 
1134
                   'bzrlib.tests.test_sftp_transport',
 
1135
                   'bzrlib.tests.test_smart_add',
 
1136
                   'bzrlib.tests.test_source',
 
1137
                   'bzrlib.tests.test_status',
 
1138
                   'bzrlib.tests.test_store',
 
1139
                   'bzrlib.tests.test_symbol_versioning',
 
1140
                   'bzrlib.tests.test_testament',
 
1141
                   'bzrlib.tests.test_textfile',
 
1142
                   'bzrlib.tests.test_textmerge',
 
1143
                   'bzrlib.tests.test_trace',
 
1144
                   'bzrlib.tests.test_transactions',
 
1145
                   'bzrlib.tests.test_transform',
 
1146
                   'bzrlib.tests.test_transport',
 
1147
                   'bzrlib.tests.test_tsort',
 
1148
                   'bzrlib.tests.test_tuned_gzip',
 
1149
                   'bzrlib.tests.test_ui',
 
1150
                   'bzrlib.tests.test_upgrade',
 
1151
                   'bzrlib.tests.test_versionedfile',
 
1152
                   'bzrlib.tests.test_weave',
 
1153
                   'bzrlib.tests.test_whitebox',
 
1154
                   'bzrlib.tests.test_workingtree',
 
1155
                   'bzrlib.tests.test_xml',
 
1156
                   ]
 
1157
    test_transport_implementations = [
 
1158
        'bzrlib.tests.test_transport_implementations']
 
1159
 
 
1160
    suite = TestSuite()
 
1161
    loader = TestUtil.TestLoader()
 
1162
    from bzrlib.transport import TransportTestProviderAdapter
 
1163
    adapter = TransportTestProviderAdapter()
 
1164
    adapt_modules(test_transport_implementations, adapter, loader, suite)
 
1165
    suite.addTest(loader.loadTestsFromModuleNames(testmod_names))
 
1166
    for package in packages_to_test():
 
1167
        suite.addTest(package.test_suite())
 
1168
    for m in MODULES_TO_TEST:
 
1169
        suite.addTest(loader.loadTestsFromModule(m))
 
1170
    for m in (MODULES_TO_DOCTEST):
 
1171
        suite.addTest(DocTestSuite(m))
 
1172
    for name, plugin in bzrlib.plugin.all_plugins().items():
 
1173
        if getattr(plugin, 'test_suite', None) is not None:
 
1174
            suite.addTest(plugin.test_suite())
 
1175
    return suite
 
1176
 
 
1177
 
 
1178
def adapt_modules(mods_list, adapter, loader, suite):
 
1179
    """Adapt the modules in mods_list using adapter and add to suite."""
 
1180
    for test in iter_suite_tests(loader.loadTestsFromModuleNames(mods_list)):
 
1181
        suite.addTests(adapter.adapt(test))