/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

Branch now uses BzrDir reasonably sanely.

Show diffs side-by-side

added added

removed removed

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