/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

Got propogation under test

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