/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

  • Committer: Robert Collins
  • Date: 2006-02-25 00:32:59 UTC
  • mto: (1587.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 1588.
  • Revision ID: robertc@robertcollins.net-20060225003259-eafe402b13b8b16e
Only reconcile if doing so will perform gc or correct ancestry.

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