~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

- refactor handling of short option names

Show diffs side-by-side

added added

removed removed

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