~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

Factor out another win32 special case and add platform independent tests for it.

Show diffs side-by-side

added added

removed removed

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