~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

  • Committer: Robert Collins
  • Date: 2005-10-18 05:26:22 UTC
  • mto: This revision was merged to the branch mainline in revision 1463.
  • Revision ID: robertc@robertcollins.net-20051018052622-653d638c9e26fde4
fix broken tests

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
67
 
 
68
 
default_transport = LocalRelpathServer
 
34
from bzrlib.selftest import TestUtil
 
35
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
 
36
from bzrlib.selftest.treeshape import build_tree_contents
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
 
205
125
 
206
126
    def _makeResult(self):
207
127
        result = _MyResult(self.stream, self.descriptions, self.verbosity)
208
 
        result.stop_early = self.stop_on_failure
 
128
        if self.stop_on_failure:
 
129
            result = EarlyStoppingTestResultAdapter(result)
209
130
        return result
210
131
 
211
132
 
238
159
 
239
160
    Error and debug log messages are redirected from their usual
240
161
    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.
 
162
    retrieved by _get_log().
244
163
       
245
164
    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
 
    """
 
165
    routine, and to build and check bzr trees."""
253
166
 
254
167
    BZRPATH = 'bzr'
255
168
    _log_file_name = None
256
 
    _log_contents = ''
257
 
 
258
 
    def __init__(self, methodName='testMethod'):
259
 
        super(TestCase, self).__init__(methodName)
260
 
        self._cleanups = []
261
169
 
262
170
    def setUp(self):
263
171
        unittest.TestCase.setUp(self)
264
 
        self._cleanEnvironment()
 
172
        self.oldenv = os.environ.get('HOME', None)
 
173
        os.environ['HOME'] = os.getcwd()
 
174
        self.bzr_email = os.environ.get('BZREMAIL')
 
175
        if self.bzr_email is not None:
 
176
            del os.environ['BZREMAIL']
 
177
        self.email = os.environ.get('EMAIL')
 
178
        if self.email is not None:
 
179
            del os.environ['EMAIL']
265
180
        bzrlib.trace.disable_default_logging()
266
 
        self._startLogFile()
 
181
        self._enable_file_logging()
267
182
 
268
183
    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'
 
184
        """Return ndiff between two strings containing lines."""
277
185
        difflines = difflib.ndiff(a.splitlines(True),
278
186
                                  b.splitlines(True),
279
187
                                  linejunk=lambda x: False,
280
188
                                  charjunk=lambda x: False)
281
189
        return ''.join(difflines)
282
190
 
283
 
    def assertEqualDiff(self, a, b, message=None):
 
191
    def assertEqualDiff(self, a, b):
284
192
        """Assert two texts are equal, if not raise an exception.
285
193
        
286
194
        This is intended for use with multi-line strings where it can 
289
197
        # TODO: perhaps override assertEquals to call this for strings?
290
198
        if a == b:
291
199
            return
292
 
        if message is None:
293
 
            message = "texts not equal:\n"
294
 
        raise AssertionError(message + 
 
200
        raise AssertionError("texts not equal:\n" + 
295
201
                             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
202
 
309
203
    def assertContainsRe(self, haystack, needle_re):
310
204
        """Assert that a contains something matching a regular expression."""
311
205
        if not re.search(needle_re, haystack):
312
206
            raise AssertionError('pattern "%s" not found in "%s"'
313
207
                    % (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
208
        
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
 
        """
 
209
    def _enable_file_logging(self):
352
210
        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)
 
211
 
 
212
        self._log_file = os.fdopen(fileno, 'w+')
 
213
 
 
214
        hdlr = logging.StreamHandler(self._log_file)
 
215
        hdlr.setLevel(logging.DEBUG)
 
216
        hdlr.setFormatter(logging.Formatter('%(levelname)8s  %(message)s'))
 
217
        logging.getLogger('').addHandler(hdlr)
 
218
        logging.getLogger('').setLevel(logging.DEBUG)
 
219
        self._log_hdlr = hdlr
 
220
        debug('opened log file %s', name)
 
221
        
356
222
        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()
 
223
 
 
224
    def tearDown(self):
 
225
        os.environ['HOME'] = self.oldenv
 
226
        if os.environ.get('BZREMAIL') is not None:
 
227
            del os.environ['BZREMAIL']
 
228
        if self.bzr_email is not None:
 
229
            os.environ['BZREMAIL'] = self.bzr_email
 
230
        if os.environ.get('EMAIL') is not None:
 
231
            del os.environ['EMAIL']
 
232
        if self.email is not None:
 
233
            os.environ['EMAIL'] = self.email
 
234
        logging.getLogger('').removeHandler(self._log_hdlr)
 
235
        bzrlib.trace.enable_default_logging()
 
236
        logging.debug('%s teardown', self.id())
367
237
        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
238
        unittest.TestCase.tearDown(self)
419
239
 
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
240
    def log(self, *args):
431
 
        mutter(*args)
 
241
        logging.debug(*args)
432
242
 
433
243
    def _get_log(self):
434
244
        """Return as a string the log for this test"""
435
245
        if self._log_file_name:
436
246
            return open(self._log_file_name).read()
437
247
        else:
438
 
            return self._log_contents
439
 
        # TODO: Delete the log after it's been read in
 
248
            return ''
440
249
 
441
 
    def capture(self, cmd, retcode=0):
 
250
    def capture(self, cmd):
442
251
        """Shortcut that splits cmd into words, runs, and returns stdout"""
443
 
        return self.run_bzr_captured(cmd.split(), retcode=retcode)[0]
 
252
        return self.run_bzr_captured(cmd.split())[0]
444
253
 
445
254
    def run_bzr_captured(self, argv, retcode=0):
446
 
        """Invoke bzr and return (stdout, stderr).
 
255
        """Invoke bzr and return (result, stdout, stderr).
447
256
 
448
257
        Useful for code that wants to check the contents of the
449
258
        output, the way error messages are presented, etc.
465
274
        stdout = StringIO()
466
275
        stderr = StringIO()
467
276
        self.log('run bzr: %s', ' '.join(argv))
468
 
        # FIXME: don't call into logging here
469
277
        handler = logging.StreamHandler(stderr)
470
278
        handler.setFormatter(bzrlib.trace.QuietFormatter())
471
279
        handler.setLevel(logging.INFO)
530
338
        if stdin is None:
531
339
            stdin = StringIO("")
532
340
        if stdout is None:
533
 
            if getattr(self, "_log_file", None) is not None:
 
341
            if hasattr(self, "_log_file"):
534
342
                stdout = self._log_file
535
343
            else:
536
344
                stdout = StringIO()
537
345
        if stderr is None:
538
 
            if getattr(self, "_log_file", None is not None):
 
346
            if hasattr(self, "_log_file"):
539
347
                stderr = self._log_file
540
348
            else:
541
349
                stderr = StringIO()
552
360
            sys.stderr = real_stderr
553
361
            sys.stdin = real_stdin
554
362
 
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
363
 
572
364
BzrTestBase = TestCase
573
365
 
602
394
            return
603
395
        i = 0
604
396
        while True:
605
 
            root = u'test%04d.tmp' % i
 
397
            root = 'test%04d.tmp' % i
606
398
            try:
607
399
                os.mkdir(root)
608
400
            except OSError, e:
612
404
                else:
613
405
                    raise
614
406
            # successfully created
615
 
            TestCaseInTempDir.TEST_ROOT = osutils.abspath(root)
 
407
            TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
616
408
            break
617
409
        # make a fake bzr directory there to prevent any tests propagating
618
410
        # up onto the source directory's real branch
619
 
        bzrdir.BzrDir.create_standalone_workingtree(TestCaseInTempDir.TEST_ROOT)
 
411
        os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
620
412
 
621
413
    def setUp(self):
 
414
        self._make_test_root()
 
415
        self._currentdir = os.getcwdu()
 
416
        short_id = self.id().replace('bzrlib.selftest.', '') \
 
417
                   .replace('__main__.', '')
 
418
        self.test_dir = os.path.join(self.TEST_ROOT, short_id)
 
419
        os.mkdir(self.test_dir)
 
420
        os.chdir(self.test_dir)
622
421
        super(TestCaseInTempDir, self).setUp()
623
 
        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)
649
422
        
650
 
    def build_tree(self, shape, line_endings='native', transport=None):
 
423
    def tearDown(self):
 
424
        os.chdir(self._currentdir)
 
425
        super(TestCaseInTempDir, self).tearDown()
 
426
 
 
427
    def build_tree(self, shape):
651
428
        """Build a test tree according to a pattern.
652
429
 
653
430
        shape is a sequence of file specifications.  If the final
654
431
        character is '/', a directory is created.
655
432
 
656
433
        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
434
        """
666
435
        # 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
436
        for name in shape:
670
 
            self.assert_(isinstance(name, basestring))
 
437
            assert isinstance(name, basestring)
671
438
            if name[-1] == '/':
672
 
                transport.mkdir(urlescape(name[:-1]))
 
439
                os.mkdir(name[:-1])
673
440
            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))
 
441
                f = file(name, 'wt')
 
442
                print >>f, "contents of", name
 
443
                f.close()
682
444
 
683
445
    def build_tree_contents(self, shape):
684
 
        build_tree_contents(shape)
 
446
        bzrlib.selftest.build_tree_contents(shape)
685
447
 
686
448
    def failUnlessExists(self, path):
687
449
        """Fail unless path, which may be abs or relative, exists."""
688
450
        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
 
451
        
 
452
 
 
453
class MetaTestLog(TestCase):
 
454
    def test_logging(self):
 
455
        """Test logs are captured when a test fails."""
 
456
        logging.info('an info message')
 
457
        warning('something looks dodgy...')
 
458
        logging.debug('hello, test is running')
 
459
        ## assert 0
876
460
 
877
461
 
878
462
def filter_suite_by_re(suite, pattern):
879
 
    result = TestSuite()
 
463
    result = TestUtil.TestSuite()
880
464
    filter_re = re.compile(pattern)
881
465
    for test in iter_suite_tests(suite):
882
466
        if filter_re.search(test.id()):
885
469
 
886
470
 
887
471
def run_suite(suite, name='test', verbose=False, pattern=".*",
888
 
              stop_on_failure=False, keep_output=False,
889
 
              transport=None):
 
472
              stop_on_failure=False):
890
473
    TestCaseInTempDir._TEST_NAME = name
891
474
    if verbose:
892
475
        verbosity = 2
902
485
    # This is still a little bogus, 
903
486
    # but only a little. Folk not using our testrunner will
904
487
    # 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
 
488
    if result.wasSuccessful():
 
489
        if TestCaseInTempDir.TEST_ROOT is not None:
 
490
            shutil.rmtree(TestCaseInTempDir.TEST_ROOT) 
913
491
    else:
914
492
        print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
915
493
    return result.wasSuccessful()
916
494
 
917
495
 
918
 
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
919
 
             keep_output=False,
920
 
             transport=None):
 
496
def selftest(verbose=False, pattern=".*", stop_on_failure=True):
921
497
    """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
 
 
 
498
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern,
 
499
                     stop_on_failure=stop_on_failure)
935
500
 
936
501
 
937
502
def test_suite():
938
503
    """Build and return TestSuite for the whole program."""
 
504
    import bzrlib.store, bzrlib.inventory, bzrlib.branch
 
505
    import bzrlib.osutils, bzrlib.merge3, bzrlib.plugin
939
506
    from doctest import DocTestSuite
940
507
 
941
 
    global MODULES_TO_DOCTEST
 
508
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
942
509
 
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',
 
510
    testmod_names = \
 
511
                  ['bzrlib.selftest.MetaTestLog',
 
512
                   'bzrlib.selftest.testgpg',
 
513
                   'bzrlib.selftest.testidentitymap',
 
514
                   'bzrlib.selftest.testinv',
 
515
                   'bzrlib.selftest.test_ancestry',
 
516
                   'bzrlib.selftest.test_commit',
 
517
                   'bzrlib.selftest.test_commit_merge',
 
518
                   'bzrlib.selftest.testconfig',
 
519
                   'bzrlib.selftest.versioning',
 
520
                   'bzrlib.selftest.testmerge3',
 
521
                   'bzrlib.selftest.testmerge',
 
522
                   'bzrlib.selftest.testhashcache',
 
523
                   'bzrlib.selftest.teststatus',
 
524
                   'bzrlib.selftest.testlog',
 
525
                   'bzrlib.selftest.testrevisionnamespaces',
 
526
                   'bzrlib.selftest.testbranch',
 
527
                   'bzrlib.selftest.testrevision',
 
528
                   'bzrlib.selftest.test_revision_info',
 
529
                   'bzrlib.selftest.test_merge_core',
 
530
                   'bzrlib.selftest.test_smart_add',
 
531
                   'bzrlib.selftest.test_bad_files',
 
532
                   'bzrlib.selftest.testdiff',
 
533
                   'bzrlib.selftest.test_parent',
 
534
                   'bzrlib.selftest.test_xml',
 
535
                   'bzrlib.selftest.test_weave',
 
536
                   'bzrlib.selftest.testfetch',
 
537
                   'bzrlib.selftest.whitebox',
 
538
                   'bzrlib.selftest.teststore',
 
539
                   'bzrlib.selftest.blackbox',
 
540
                   'bzrlib.selftest.testsampler',
 
541
                   'bzrlib.selftest.testtransactions',
 
542
                   'bzrlib.selftest.testtransport',
 
543
                   'bzrlib.selftest.testgraph',
 
544
                   'bzrlib.selftest.testworkingtree',
 
545
                   'bzrlib.selftest.test_upgrade',
 
546
                   'bzrlib.selftest.test_conflicts',
 
547
                   'bzrlib.selftest.testtestament',
 
548
                   'bzrlib.selftest.testannotate',
 
549
                   'bzrlib.selftest.testrevprops',
 
550
                   'bzrlib.selftest.testoptions',
1013
551
                   ]
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])
 
552
 
 
553
    for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
 
554
              bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
 
555
        if m not in MODULES_TO_DOCTEST:
 
556
            MODULES_TO_DOCTEST.append(m)
 
557
 
 
558
    TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
 
559
    print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
1021
560
    print
1022
561
    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())
 
562
    suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
1036
563
    for m in MODULES_TO_TEST:
1037
 
        suite.addTest(loader.loadTestsFromModule(m))
 
564
         suite.addTest(TestLoader().loadTestsFromModule(m))
1038
565
    for m in (MODULES_TO_DOCTEST):
1039
566
        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())
 
567
    for p in bzrlib.plugin.all_plugins:
 
568
        if hasattr(p, 'test_suite'):
 
569
            suite.addTest(p.test_suite())
1043
570
    return suite
1044
571
 
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