~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

[merge] 0.7-bugfix: Fix fileid_involved to unescape xml characters, fix StubServer to handle paramiko > 1.5.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
from cStringIO import StringIO
 
25
import difflib
 
26
import errno
18
27
import logging
19
 
import unittest
20
 
import tempfile
21
28
import os
 
29
import re
 
30
import shutil
 
31
import stat
22
32
import sys
23
 
import errno
24
 
import subprocess
25
 
import shutil
 
33
import tempfile
 
34
import unittest
 
35
import time
 
36
import codecs
26
37
 
27
 
import testsweet
 
38
import bzrlib.branch
28
39
import bzrlib.commands
29
 
 
 
40
from bzrlib.errors import BzrError
 
41
import bzrlib.inventory
 
42
import bzrlib.merge3
 
43
import bzrlib.osutils
 
44
import bzrlib.osutils as osutils
 
45
import bzrlib.plugin
 
46
import bzrlib.store
30
47
import bzrlib.trace
31
 
import bzrlib.fetch
32
 
 
 
48
from bzrlib.transport import urlescape
 
49
from bzrlib.trace import mutter
 
50
from bzrlib.tests.TestUtil import TestLoader, TestSuite
 
51
from bzrlib.tests.treeshape import build_tree_contents
33
52
 
34
53
MODULES_TO_TEST = []
35
 
MODULES_TO_DOCTEST = []
36
 
 
37
 
from logging import debug, warning, error
 
54
MODULES_TO_DOCTEST = [
 
55
                      bzrlib.branch,
 
56
                      bzrlib.commands,
 
57
                      bzrlib.errors,
 
58
                      bzrlib.inventory,
 
59
                      bzrlib.merge3,
 
60
                      bzrlib.option,
 
61
                      bzrlib.osutils,
 
62
                      bzrlib.store
 
63
                      ]
 
64
def packages_to_test():
 
65
    import bzrlib.tests.blackbox
 
66
    return [
 
67
            bzrlib.tests.blackbox
 
68
            ]
 
69
 
 
70
 
 
71
class EarlyStoppingTestResultAdapter(object):
 
72
    """An adapter for TestResult to stop at the first first failure or error"""
 
73
 
 
74
    def __init__(self, result):
 
75
        self._result = result
 
76
 
 
77
    def addError(self, test, err):
 
78
        self._result.addError(test, err)
 
79
        self._result.stop()
 
80
 
 
81
    def addFailure(self, test, err):
 
82
        self._result.addFailure(test, err)
 
83
        self._result.stop()
 
84
 
 
85
    def __getattr__(self, name):
 
86
        return getattr(self._result, name)
 
87
 
 
88
    def __setattr__(self, name, value):
 
89
        if name == '_result':
 
90
            object.__setattr__(self, name, value)
 
91
        return setattr(self._result, name, value)
 
92
 
 
93
 
 
94
class _MyResult(unittest._TextTestResult):
 
95
    """Custom TestResult.
 
96
 
 
97
    Shows output in a different format, including displaying runtime for tests.
 
98
    """
 
99
 
 
100
    def _elapsedTime(self):
 
101
        return "%5dms" % (1000 * (time.time() - self._start_time))
 
102
 
 
103
    def startTest(self, test):
 
104
        unittest.TestResult.startTest(self, test)
 
105
        # In a short description, the important words are in
 
106
        # the beginning, but in an id, the important words are
 
107
        # at the end
 
108
        SHOW_DESCRIPTIONS = False
 
109
        if self.showAll:
 
110
            width = osutils.terminal_width()
 
111
            name_width = width - 15
 
112
            what = None
 
113
            if SHOW_DESCRIPTIONS:
 
114
                what = test.shortDescription()
 
115
                if what:
 
116
                    if len(what) > name_width:
 
117
                        what = what[:name_width-3] + '...'
 
118
            if what is None:
 
119
                what = test.id()
 
120
                if what.startswith('bzrlib.tests.'):
 
121
                    what = what[13:]
 
122
                if len(what) > name_width:
 
123
                    what = '...' + what[3-name_width:]
 
124
            what = what.ljust(name_width)
 
125
            self.stream.write(what)
 
126
        self.stream.flush()
 
127
        self._start_time = time.time()
 
128
 
 
129
    def addError(self, test, err):
 
130
        if isinstance(err[1], TestSkipped):
 
131
            return self.addSkipped(test, err)    
 
132
        unittest.TestResult.addError(self, test, err)
 
133
        if self.showAll:
 
134
            self.stream.writeln("ERROR %s" % self._elapsedTime())
 
135
        elif self.dots:
 
136
            self.stream.write('E')
 
137
        self.stream.flush()
 
138
 
 
139
    def addFailure(self, test, err):
 
140
        unittest.TestResult.addFailure(self, test, err)
 
141
        if self.showAll:
 
142
            self.stream.writeln(" FAIL %s" % self._elapsedTime())
 
143
        elif self.dots:
 
144
            self.stream.write('F')
 
145
        self.stream.flush()
 
146
 
 
147
    def addSuccess(self, test):
 
148
        if self.showAll:
 
149
            self.stream.writeln('   OK %s' % self._elapsedTime())
 
150
        elif self.dots:
 
151
            self.stream.write('~')
 
152
        self.stream.flush()
 
153
        unittest.TestResult.addSuccess(self, test)
 
154
 
 
155
    def addSkipped(self, test, skip_excinfo):
 
156
        if self.showAll:
 
157
            print >>self.stream, ' SKIP %s' % self._elapsedTime()
 
158
            print >>self.stream, '     %s' % skip_excinfo[1]
 
159
        elif self.dots:
 
160
            self.stream.write('S')
 
161
        self.stream.flush()
 
162
        # seems best to treat this as success from point-of-view of unittest
 
163
        # -- it actually does nothing so it barely matters :)
 
164
        unittest.TestResult.addSuccess(self, test)
 
165
 
 
166
    def printErrorList(self, flavour, errors):
 
167
        for test, err in errors:
 
168
            self.stream.writeln(self.separator1)
 
169
            self.stream.writeln("%s: %s" % (flavour, self.getDescription(test)))
 
170
            if hasattr(test, '_get_log'):
 
171
                print >>self.stream
 
172
                print >>self.stream, \
 
173
                        ('vvvv[log from %s]' % test.id()).ljust(78,'-')
 
174
                print >>self.stream, test._get_log()
 
175
                print >>self.stream, \
 
176
                        ('^^^^[log from %s]' % test.id()).ljust(78,'-')
 
177
            self.stream.writeln(self.separator2)
 
178
            self.stream.writeln("%s" % err)
 
179
 
 
180
 
 
181
class TextTestRunner(unittest.TextTestRunner):
 
182
    stop_on_failure = False
 
183
 
 
184
    def _makeResult(self):
 
185
        result = _MyResult(self.stream, self.descriptions, self.verbosity)
 
186
        if self.stop_on_failure:
 
187
            result = EarlyStoppingTestResultAdapter(result)
 
188
        return result
 
189
 
 
190
 
 
191
def iter_suite_tests(suite):
 
192
    """Return all tests in a suite, recursing through nested suites"""
 
193
    for item in suite._tests:
 
194
        if isinstance(item, unittest.TestCase):
 
195
            yield item
 
196
        elif isinstance(item, unittest.TestSuite):
 
197
            for r in iter_suite_tests(item):
 
198
                yield r
 
199
        else:
 
200
            raise Exception('unknown object %r inside test suite %r'
 
201
                            % (item, suite))
 
202
 
 
203
 
 
204
class TestSkipped(Exception):
 
205
    """Indicates that a test was intentionally skipped, rather than failing."""
 
206
    # XXX: Not used yet
 
207
 
38
208
 
39
209
class CommandFailed(Exception):
40
210
    pass
47
217
 
48
218
    Error and debug log messages are redirected from their usual
49
219
    location into a temporary file, the contents of which can be
50
 
    retrieved by _get_log().
 
220
    retrieved by _get_log().  We use a real OS file, not an in-memory object,
 
221
    so that it can also capture file IO.  When the test completes this file
 
222
    is read into memory and removed from disk.
51
223
       
52
224
    There are also convenience functions to invoke bzr's command-line
53
 
    routine, and to build and check bzr trees."""
 
225
    routine, and to build and check bzr trees.
 
226
   
 
227
    In addition to the usual method of overriding tearDown(), this class also
 
228
    allows subclasses to register functions into the _cleanups list, which is
 
229
    run in order as the object is torn down.  It's less likely this will be
 
230
    accidentally overlooked.
 
231
    """
54
232
 
55
233
    BZRPATH = 'bzr'
 
234
    _log_file_name = None
 
235
    _log_contents = ''
56
236
 
57
237
    def setUp(self):
58
 
        # this replaces the default testsweet.TestCase; we don't want logging changed
59
238
        unittest.TestCase.setUp(self)
 
239
        self._cleanups = []
 
240
        self._cleanEnvironment()
60
241
        bzrlib.trace.disable_default_logging()
61
 
        self._enable_file_logging()
62
 
 
63
 
 
64
 
    def _enable_file_logging(self):
 
242
        self._startLogFile()
 
243
 
 
244
    def _ndiff_strings(self, a, b):
 
245
        """Return ndiff between two strings containing lines.
 
246
        
 
247
        A trailing newline is added if missing to make the strings
 
248
        print properly."""
 
249
        if b and b[-1] != '\n':
 
250
            b += '\n'
 
251
        if a and a[-1] != '\n':
 
252
            a += '\n'
 
253
        difflines = difflib.ndiff(a.splitlines(True),
 
254
                                  b.splitlines(True),
 
255
                                  linejunk=lambda x: False,
 
256
                                  charjunk=lambda x: False)
 
257
        return ''.join(difflines)
 
258
 
 
259
    def assertEqualDiff(self, a, b):
 
260
        """Assert two texts are equal, if not raise an exception.
 
261
        
 
262
        This is intended for use with multi-line strings where it can 
 
263
        be hard to find the differences by eye.
 
264
        """
 
265
        # TODO: perhaps override assertEquals to call this for strings?
 
266
        if a == b:
 
267
            return
 
268
        raise AssertionError("texts not equal:\n" + 
 
269
                             self._ndiff_strings(a, b))      
 
270
        
 
271
    def assertStartsWith(self, s, prefix):
 
272
        if not s.startswith(prefix):
 
273
            raise AssertionError('string %r does not start with %r' % (s, prefix))
 
274
 
 
275
    def assertEndsWith(self, s, suffix):
 
276
        if not s.endswith(prefix):
 
277
            raise AssertionError('string %r does not end with %r' % (s, suffix))
 
278
 
 
279
    def assertContainsRe(self, haystack, needle_re):
 
280
        """Assert that a contains something matching a regular expression."""
 
281
        if not re.search(needle_re, haystack):
 
282
            raise AssertionError('pattern "%s" not found in "%s"'
 
283
                    % (needle_re, haystack))
 
284
 
 
285
    def AssertSubset(self, sublist, superlist):
 
286
        """Assert that every entry in sublist is present in superlist."""
 
287
        missing = []
 
288
        for entry in sublist:
 
289
            if entry not in superlist:
 
290
                missing.append(entry)
 
291
        if len(missing) > 0:
 
292
            raise AssertionError("value(s) %r not present in container %r" % 
 
293
                                 (missing, superlist))
 
294
 
 
295
    def assertTransportMode(self, transport, path, mode):
 
296
        """Fail if a path does not have mode mode.
 
297
        
 
298
        If modes are not supported on this platform, the test is skipped.
 
299
        """
 
300
        if sys.platform == 'win32':
 
301
            return
 
302
        path_stat = transport.stat(path)
 
303
        actual_mode = stat.S_IMODE(path_stat.st_mode)
 
304
        self.assertEqual(mode, actual_mode,
 
305
            'mode of %r incorrect (%o != %o)' % (path, mode, actual_mode))
 
306
 
 
307
    def _startLogFile(self):
 
308
        """Send bzr and test log messages to a temporary file.
 
309
 
 
310
        The file is removed as the test is torn down.
 
311
        """
65
312
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
66
 
 
67
 
        self._log_file = os.fdopen(fileno, 'w+')
68
 
 
69
 
        hdlr = logging.StreamHandler(self._log_file)
70
 
        hdlr.setLevel(logging.DEBUG)
71
 
        hdlr.setFormatter(logging.Formatter('%(levelname)8s  %(message)s'))
72
 
        logging.getLogger('').addHandler(hdlr)
73
 
        logging.getLogger('').setLevel(logging.DEBUG)
74
 
        self._log_hdlr = hdlr
75
 
        debug('opened log file %s', name)
76
 
        
 
313
        encoder, decoder, stream_reader, stream_writer = codecs.lookup('UTF-8')
 
314
        self._log_file = stream_writer(os.fdopen(fileno, 'w+'))
 
315
        bzrlib.trace.enable_test_log(self._log_file)
77
316
        self._log_file_name = name
78
 
 
79
 
        
80
 
    def tearDown(self):
81
 
        logging.getLogger('').removeHandler(self._log_hdlr)
82
 
        bzrlib.trace.enable_default_logging()
83
 
        logging.debug('%s teardown', self.id())
 
317
        self.addCleanup(self._finishLogFile)
 
318
 
 
319
    def _finishLogFile(self):
 
320
        """Finished with the log file.
 
321
 
 
322
        Read contents into memory, close, and delete.
 
323
        """
 
324
        bzrlib.trace.disable_test_log()
 
325
        self._log_file.seek(0)
 
326
        self._log_contents = self._log_file.read()
84
327
        self._log_file.close()
 
328
        os.remove(self._log_file_name)
 
329
        self._log_file = self._log_file_name = None
 
330
 
 
331
    def addCleanup(self, callable):
 
332
        """Arrange to run a callable when this case is torn down.
 
333
 
 
334
        Callables are run in the reverse of the order they are registered, 
 
335
        ie last-in first-out.
 
336
        """
 
337
        if callable in self._cleanups:
 
338
            raise ValueError("cleanup function %r already registered on %s" 
 
339
                    % (callable, self))
 
340
        self._cleanups.append(callable)
 
341
 
 
342
    def _cleanEnvironment(self):
 
343
        new_env = {
 
344
            'HOME': os.getcwd(),
 
345
            'APPDATA': os.getcwd(),
 
346
            'BZREMAIL': None,
 
347
            'EMAIL': None,
 
348
        }
 
349
        self.__old_env = {}
 
350
        self.addCleanup(self._restoreEnvironment)
 
351
        for name, value in new_env.iteritems():
 
352
            self._captureVar(name, value)
 
353
 
 
354
 
 
355
    def _captureVar(self, name, newvalue):
 
356
        """Set an environment variable, preparing it to be reset when finished."""
 
357
        self.__old_env[name] = os.environ.get(name, None)
 
358
        if newvalue is None:
 
359
            if name in os.environ:
 
360
                del os.environ[name]
 
361
        else:
 
362
            os.environ[name] = newvalue
 
363
 
 
364
    @staticmethod
 
365
    def _restoreVar(name, value):
 
366
        if value is None:
 
367
            if name in os.environ:
 
368
                del os.environ[name]
 
369
        else:
 
370
            os.environ[name] = value
 
371
 
 
372
    def _restoreEnvironment(self):
 
373
        for name, value in self.__old_env.iteritems():
 
374
            self._restoreVar(name, value)
 
375
 
 
376
    def tearDown(self):
 
377
        self._runCleanups()
85
378
        unittest.TestCase.tearDown(self)
86
379
 
 
380
    def _runCleanups(self):
 
381
        """Run registered cleanup functions. 
 
382
 
 
383
        This should only be called from TestCase.tearDown.
 
384
        """
 
385
        # TODO: Perhaps this should keep running cleanups even if 
 
386
        # one of them fails?
 
387
        for cleanup_fn in reversed(self._cleanups):
 
388
            cleanup_fn()
87
389
 
88
390
    def log(self, *args):
89
 
        logging.debug(*args)
 
391
        mutter(*args)
90
392
 
91
393
    def _get_log(self):
92
394
        """Return as a string the log for this test"""
93
 
        return open(self._log_file_name).read()
 
395
        if self._log_file_name:
 
396
            return open(self._log_file_name).read()
 
397
        else:
 
398
            return self._log_contents
 
399
        # TODO: Delete the log after it's been read in
 
400
 
 
401
    def capture(self, cmd, retcode=0):
 
402
        """Shortcut that splits cmd into words, runs, and returns stdout"""
 
403
        return self.run_bzr_captured(cmd.split(), retcode=retcode)[0]
 
404
 
 
405
    def run_bzr_captured(self, argv, retcode=0):
 
406
        """Invoke bzr and return (stdout, stderr).
 
407
 
 
408
        Useful for code that wants to check the contents of the
 
409
        output, the way error messages are presented, etc.
 
410
 
 
411
        This should be the main method for tests that want to exercise the
 
412
        overall behavior of the bzr application (rather than a unit test
 
413
        or a functional test of the library.)
 
414
 
 
415
        Much of the old code runs bzr by forking a new copy of Python, but
 
416
        that is slower, harder to debug, and generally not necessary.
 
417
 
 
418
        This runs bzr through the interface that catches and reports
 
419
        errors, and with logging set to something approximating the
 
420
        default, so that error reporting can be checked.
 
421
 
 
422
        argv -- arguments to invoke bzr
 
423
        retcode -- expected return code, or None for don't-care.
 
424
        """
 
425
        stdout = StringIO()
 
426
        stderr = StringIO()
 
427
        self.log('run bzr: %s', ' '.join(argv))
 
428
        # FIXME: don't call into logging here
 
429
        handler = logging.StreamHandler(stderr)
 
430
        handler.setFormatter(bzrlib.trace.QuietFormatter())
 
431
        handler.setLevel(logging.INFO)
 
432
        logger = logging.getLogger('')
 
433
        logger.addHandler(handler)
 
434
        try:
 
435
            result = self.apply_redirected(None, stdout, stderr,
 
436
                                           bzrlib.commands.run_bzr_catch_errors,
 
437
                                           argv)
 
438
        finally:
 
439
            logger.removeHandler(handler)
 
440
        out = stdout.getvalue()
 
441
        err = stderr.getvalue()
 
442
        if out:
 
443
            self.log('output:\n%s', out)
 
444
        if err:
 
445
            self.log('errors:\n%s', err)
 
446
        if retcode is not None:
 
447
            self.assertEquals(result, retcode)
 
448
        return out, err
94
449
 
95
450
    def run_bzr(self, *args, **kwargs):
96
451
        """Invoke bzr, as if it were run from the command line.
99
454
        overall behavior of the bzr application (rather than a unit test
100
455
        or a functional test of the library.)
101
456
 
102
 
        Much of the old code runs bzr by forking a new copy of Python, but
103
 
        that is slower, harder to debug, and generally not necessary.
 
457
        This sends the stdout/stderr results into the test's log,
 
458
        where it may be useful for debugging.  See also run_captured.
104
459
        """
105
 
        retcode = kwargs.get('retcode', 0)
106
 
        result = self.apply_redirected(None, None, None,
107
 
                                       bzrlib.commands.run_bzr, args)
108
 
        self.assertEquals(result, retcode)
109
 
        
110
 
        
 
460
        retcode = kwargs.pop('retcode', 0)
 
461
        return self.run_bzr_captured(args, retcode)
 
462
 
111
463
    def check_inventory_shape(self, inv, shape):
112
 
        """
113
 
        Compare an inventory to a list of expected names.
 
464
        """Compare an inventory to a list of expected names.
114
465
 
115
466
        Fail if they are not precisely equal.
116
467
        """
134
485
        """Call callable with redirected std io pipes.
135
486
 
136
487
        Returns the return code."""
137
 
        from StringIO import StringIO
138
488
        if not callable(a_callable):
139
489
            raise ValueError("a_callable must be callable.")
140
490
        if stdin is None:
141
491
            stdin = StringIO("")
142
492
        if stdout is None:
143
 
            stdout = self._log_file
 
493
            if hasattr(self, "_log_file"):
 
494
                stdout = self._log_file
 
495
            else:
 
496
                stdout = StringIO()
144
497
        if stderr is None:
145
 
            stderr = self._log_file
 
498
            if hasattr(self, "_log_file"):
 
499
                stderr = self._log_file
 
500
            else:
 
501
                stderr = StringIO()
146
502
        real_stdin = sys.stdin
147
503
        real_stdout = sys.stdout
148
504
        real_stderr = sys.stderr
183
539
        if contents != expect:
184
540
            self.log("expected: %r" % expect)
185
541
            self.log("actually: %r" % contents)
186
 
            self.fail("contents of %s not as expected")
 
542
            self.fail("contents of %s not as expected" % filename)
187
543
 
188
544
    def _make_test_root(self):
189
545
        if TestCaseInTempDir.TEST_ROOT is not None:
190
546
            return
191
547
        i = 0
192
548
        while True:
193
 
            root = 'test%04d.tmp' % i
 
549
            root = u'test%04d.tmp' % i
194
550
            try:
195
551
                os.mkdir(root)
196
552
            except OSError, e:
200
556
                else:
201
557
                    raise
202
558
            # successfully created
203
 
            TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
 
559
            TestCaseInTempDir.TEST_ROOT = osutils.abspath(root)
204
560
            break
205
561
        # make a fake bzr directory there to prevent any tests propagating
206
562
        # up onto the source directory's real branch
207
 
        os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
 
563
        os.mkdir(osutils.pathjoin(TestCaseInTempDir.TEST_ROOT, '.bzr'))
208
564
 
209
565
    def setUp(self):
210
566
        super(TestCaseInTempDir, self).setUp()
211
 
        import os
212
567
        self._make_test_root()
213
 
        self._currentdir = os.getcwdu()
214
 
        short_id = self.id().replace('bzrlib.selftest.', '') \
 
568
        _currentdir = os.getcwdu()
 
569
        short_id = self.id().replace('bzrlib.tests.', '') \
215
570
                   .replace('__main__.', '')
216
 
        self.test_dir = os.path.join(self.TEST_ROOT, short_id)
 
571
        self.test_dir = osutils.pathjoin(self.TEST_ROOT, short_id)
217
572
        os.mkdir(self.test_dir)
218
573
        os.chdir(self.test_dir)
 
574
        os.environ['HOME'] = self.test_dir
 
575
        os.environ['APPDATA'] = self.test_dir
 
576
        def _leaveDirectory():
 
577
            os.chdir(_currentdir)
 
578
        self.addCleanup(_leaveDirectory)
219
579
        
220
 
    def tearDown(self):
221
 
        import os
222
 
        os.chdir(self._currentdir)
223
 
        super(TestCaseInTempDir, self).tearDown()
224
 
 
225
 
    def _formcmd(self, cmd):
226
 
        if isinstance(cmd, basestring):
227
 
            cmd = cmd.split()
228
 
        if cmd[0] == 'bzr':
229
 
            cmd[0] = self.BZRPATH
230
 
            if self.OVERRIDE_PYTHON:
231
 
                cmd.insert(0, self.OVERRIDE_PYTHON)
232
 
        self.log('$ %r' % cmd)
233
 
        return cmd
234
 
 
235
 
    def runcmd(self, cmd, retcode=0):
236
 
        """Run one command and check the return code.
237
 
 
238
 
        Returns a tuple of (stdout,stderr) strings.
239
 
 
240
 
        If a single string is based, it is split into words.
241
 
        For commands that are not simple space-separated words, please
242
 
        pass a list instead."""
243
 
        cmd = self._formcmd(cmd)
244
 
        self.log('$ ' + ' '.join(cmd))
245
 
        actual_retcode = subprocess.call(cmd, stdout=self._log_file,
246
 
                                         stderr=self._log_file)
247
 
        if retcode != actual_retcode:
248
 
            raise CommandFailed("test failed: %r returned %d, expected %d"
249
 
                                % (cmd, actual_retcode, retcode))
250
 
 
251
 
    def backtick(self, cmd, retcode=0):
252
 
        """Run a command and return its output"""
253
 
        cmd = self._formcmd(cmd)
254
 
        child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self._log_file)
255
 
        outd, errd = child.communicate()
256
 
        self.log(outd)
257
 
        actual_retcode = child.wait()
258
 
 
259
 
        outd = outd.replace('\r', '')
260
 
 
261
 
        if retcode != actual_retcode:
262
 
            raise CommandFailed("test failed: %r returned %d, expected %d"
263
 
                                % (cmd, actual_retcode, retcode))
264
 
 
265
 
        return outd
266
 
 
267
 
 
268
 
 
269
 
    def build_tree(self, shape):
 
580
    def build_tree(self, shape, line_endings='native', transport=None):
270
581
        """Build a test tree according to a pattern.
271
582
 
272
583
        shape is a sequence of file specifications.  If the final
273
584
        character is '/', a directory is created.
274
585
 
275
586
        This doesn't add anything to a branch.
 
587
        :param line_endings: Either 'binary' or 'native'
 
588
                             in binary mode, exact contents are written
 
589
                             in native mode, the line endings match the
 
590
                             default platform endings.
 
591
 
 
592
        :param transport: A transport to write to, for building trees on 
 
593
                          VFS's. If the transport is readonly or None,
 
594
                          "." is opened automatically.
276
595
        """
277
596
        # XXX: It's OK to just create them using forward slashes on windows?
278
 
        import os
 
597
        if transport is None or transport.is_readonly():
 
598
            transport = bzrlib.transport.get_transport(".")
279
599
        for name in shape:
280
 
            assert isinstance(name, basestring)
 
600
            self.assert_(isinstance(name, basestring))
281
601
            if name[-1] == '/':
282
 
                os.mkdir(name[:-1])
 
602
                transport.mkdir(urlescape(name[:-1]))
283
603
            else:
284
 
                f = file(name, 'wt')
285
 
                print >>f, "contents of", name
286
 
                f.close()
287
 
                
288
 
 
289
 
 
290
 
class MetaTestLog(TestCase):
291
 
    def test_logging(self):
292
 
        """Test logs are captured when a test fails."""
293
 
        logging.info('an info message')
294
 
        warning('something looks dodgy...')
295
 
        logging.debug('hello, test is running')
296
 
        ##assert 0
297
 
 
298
 
 
299
 
def selftest(verbose=False, pattern=".*"):
 
604
                if line_endings == 'binary':
 
605
                    end = '\n'
 
606
                elif line_endings == 'native':
 
607
                    end = os.linesep
 
608
                else:
 
609
                    raise BzrError('Invalid line ending request %r' % (line_endings,))
 
610
                content = "contents of %s%s" % (name, end)
 
611
                transport.put(urlescape(name), StringIO(content))
 
612
 
 
613
    def build_tree_contents(self, shape):
 
614
        build_tree_contents(shape)
 
615
 
 
616
    def failUnlessExists(self, path):
 
617
        """Fail unless path, which may be abs or relative, exists."""
 
618
        self.failUnless(osutils.lexists(path))
 
619
 
 
620
    def failIfExists(self, path):
 
621
        """Fail if path, which may be abs or relative, exists."""
 
622
        self.failIf(osutils.lexists(path))
 
623
        
 
624
    def assertFileEqual(self, content, path):
 
625
        """Fail if path does not contain 'content'."""
 
626
        self.failUnless(osutils.lexists(path))
 
627
        self.assertEqualDiff(content, open(path, 'r').read())
 
628
 
 
629
 
 
630
def filter_suite_by_re(suite, pattern):
 
631
    result = TestSuite()
 
632
    filter_re = re.compile(pattern)
 
633
    for test in iter_suite_tests(suite):
 
634
        if filter_re.search(test.id()):
 
635
            result.addTest(test)
 
636
    return result
 
637
 
 
638
 
 
639
def run_suite(suite, name='test', verbose=False, pattern=".*",
 
640
              stop_on_failure=False, keep_output=False):
 
641
    TestCaseInTempDir._TEST_NAME = name
 
642
    if verbose:
 
643
        verbosity = 2
 
644
    else:
 
645
        verbosity = 1
 
646
    runner = TextTestRunner(stream=sys.stdout,
 
647
                            descriptions=0,
 
648
                            verbosity=verbosity)
 
649
    runner.stop_on_failure=stop_on_failure
 
650
    if pattern != '.*':
 
651
        suite = filter_suite_by_re(suite, pattern)
 
652
    result = runner.run(suite)
 
653
    # This is still a little bogus, 
 
654
    # but only a little. Folk not using our testrunner will
 
655
    # have to delete their temp directories themselves.
 
656
    if result.wasSuccessful() or not keep_output:
 
657
        if TestCaseInTempDir.TEST_ROOT is not None:
 
658
            shutil.rmtree(TestCaseInTempDir.TEST_ROOT) 
 
659
    else:
 
660
        print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
 
661
    return result.wasSuccessful()
 
662
 
 
663
 
 
664
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
 
665
             keep_output=False):
300
666
    """Run the whole test suite under the enhanced runner"""
301
 
    return testsweet.run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
 
667
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern,
 
668
                     stop_on_failure=stop_on_failure, keep_output=keep_output)
302
669
 
303
670
 
304
671
def test_suite():
305
672
    """Build and return TestSuite for the whole program."""
306
 
    from bzrlib.selftest.TestUtil import TestLoader, TestSuite
307
 
    import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
308
 
    import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
309
673
    from doctest import DocTestSuite
310
 
    import os
311
 
    import shutil
312
 
    import time
313
 
    import sys
314
 
 
315
 
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
316
 
 
317
 
    testmod_names = \
318
 
                  ['bzrlib.selftest.MetaTestLog',
319
 
                   'bzrlib.selftest.testinv',
320
 
                   'bzrlib.selftest.versioning',
321
 
                   'bzrlib.selftest.testmerge3',
322
 
                   'bzrlib.selftest.testhashcache',
323
 
                   'bzrlib.selftest.teststatus',
324
 
                   'bzrlib.selftest.testlog',
325
 
                   'bzrlib.selftest.testrevisionnamespaces',
326
 
                   'bzrlib.selftest.testbranch',
327
 
#                   'bzrlib.selftest.testrevision',
328
 
#                   'bzrlib.selftest.test_merge_core',
329
 
                   'bzrlib.selftest.test_smart_add',
330
 
                   'bzrlib.selftest.testdiff',
331
 
#                   'bzrlib.selftest.test_parent',
332
 
                   'bzrlib.selftest.test_xml',
333
 
                   'bzrlib.selftest.test_weave',
334
 
#                   'bzrlib.selftest.testfetch',
335
 
#                   'bzrlib.selftest.whitebox',
336
 
                   'bzrlib.selftest.teststore',
337
 
#                   'bzrlib.selftest.blackbox',
 
674
 
 
675
    global MODULES_TO_DOCTEST
 
676
 
 
677
    testmod_names = [ \
 
678
                   'bzrlib.tests.test_ancestry',
 
679
                   'bzrlib.tests.test_annotate',
 
680
                   'bzrlib.tests.test_api',
 
681
                   'bzrlib.tests.test_bad_files',
 
682
                   'bzrlib.tests.test_basis_inventory',
 
683
                   'bzrlib.tests.test_branch',
 
684
                   'bzrlib.tests.test_command',
 
685
                   'bzrlib.tests.test_commit',
 
686
                   'bzrlib.tests.test_commit_merge',
 
687
                   'bzrlib.tests.test_config',
 
688
                   'bzrlib.tests.test_conflicts',
 
689
                   'bzrlib.tests.test_diff',
 
690
                   'bzrlib.tests.test_fetch',
 
691
                   'bzrlib.tests.test_fileid_involved',
 
692
                   'bzrlib.tests.test_gpg',
 
693
                   'bzrlib.tests.test_graph',
 
694
                   'bzrlib.tests.test_hashcache',
 
695
                   'bzrlib.tests.test_http',
 
696
                   'bzrlib.tests.test_identitymap',
 
697
                   'bzrlib.tests.test_inv',
 
698
                   'bzrlib.tests.test_log',
 
699
                   'bzrlib.tests.test_merge',
 
700
                   'bzrlib.tests.test_merge3',
 
701
                   'bzrlib.tests.test_merge_core',
 
702
                   'bzrlib.tests.test_missing',
 
703
                   'bzrlib.tests.test_msgeditor',
 
704
                   'bzrlib.tests.test_nonascii',
 
705
                   'bzrlib.tests.test_options',
 
706
                   'bzrlib.tests.test_osutils',
 
707
                   'bzrlib.tests.test_parent',
 
708
                   'bzrlib.tests.test_permissions',
 
709
                   'bzrlib.tests.test_plugins',
 
710
                   'bzrlib.tests.test_remove',
 
711
                   'bzrlib.tests.test_revision',
 
712
                   'bzrlib.tests.test_revisionnamespaces',
 
713
                   'bzrlib.tests.test_revprops',
 
714
                   'bzrlib.tests.test_reweave',
 
715
                   'bzrlib.tests.test_rio',
 
716
                   'bzrlib.tests.test_sampler',
 
717
                   'bzrlib.tests.test_selftest',
 
718
                   'bzrlib.tests.test_setup',
 
719
                   'bzrlib.tests.test_sftp_transport',
 
720
                   'bzrlib.tests.test_smart_add',
 
721
                   'bzrlib.tests.test_source',
 
722
                   'bzrlib.tests.test_status',
 
723
                   'bzrlib.tests.test_store',
 
724
                   'bzrlib.tests.test_symbol_versioning',
 
725
                   'bzrlib.tests.test_testament',
 
726
                   'bzrlib.tests.test_trace',
 
727
                   'bzrlib.tests.test_transactions',
 
728
                   'bzrlib.tests.test_transport',
 
729
                   'bzrlib.tests.test_tsort',
 
730
                   'bzrlib.tests.test_ui',
 
731
                   'bzrlib.tests.test_uncommit',
 
732
                   'bzrlib.tests.test_upgrade',
 
733
                   'bzrlib.tests.test_weave',
 
734
                   'bzrlib.tests.test_whitebox',
 
735
                   'bzrlib.tests.test_workingtree',
 
736
                   'bzrlib.tests.test_xml',
338
737
                   ]
339
 
 
340
 
    for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
341
 
              bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
342
 
        if m not in MODULES_TO_DOCTEST:
343
 
            MODULES_TO_DOCTEST.append(m)
344
 
 
345
 
    TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
346
 
    print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
 
738
    test_transport_implementations = [
 
739
        'bzrlib.tests.test_transport_implementations']
 
740
 
 
741
    TestCase.BZRPATH = osutils.pathjoin(
 
742
            osutils.realpath(osutils.dirname(bzrlib.__path__[0])), 'bzr')
 
743
    print '%10s: %s' % ('bzr', osutils.realpath(sys.argv[0]))
 
744
    print '%10s: %s' % ('bzrlib', bzrlib.__path__[0])
347
745
    print
348
746
    suite = TestSuite()
349
 
    suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
 
747
    # python2.4's TestLoader.loadTestsFromNames gives very poor 
 
748
    # errors if it fails to load a named module - no indication of what's
 
749
    # actually wrong, just "no such module".  We should probably override that
 
750
    # class, but for the moment just load them ourselves. (mbp 20051202)
 
751
    loader = TestLoader()
 
752
    from bzrlib.transport import TransportTestProviderAdapter
 
753
    adapter = TransportTestProviderAdapter()
 
754
    for mod_name in test_transport_implementations:
 
755
        mod = _load_module_by_name(mod_name)
 
756
        for test in iter_suite_tests(loader.loadTestsFromModule(mod)):
 
757
            suite.addTests(adapter.adapt(test))
 
758
    for mod_name in testmod_names:
 
759
        mod = _load_module_by_name(mod_name)
 
760
        suite.addTest(loader.loadTestsFromModule(mod))
 
761
    for package in packages_to_test():
 
762
        suite.addTest(package.test_suite())
350
763
    for m in MODULES_TO_TEST:
351
 
         suite.addTest(TestLoader().loadTestsFromModule(m))
 
764
        suite.addTest(loader.loadTestsFromModule(m))
352
765
    for m in (MODULES_TO_DOCTEST):
353
766
        suite.addTest(DocTestSuite(m))
354
 
    for p in bzrlib.plugin.all_plugins:
355
 
        if hasattr(p, 'test_suite'):
356
 
            suite.addTest(p.test_suite())
 
767
    for name, plugin in bzrlib.plugin.all_plugins().items():
 
768
        if hasattr(plugin, 'test_suite'):
 
769
            suite.addTest(plugin.test_suite())
357
770
    return suite
358
771
 
 
772
 
 
773
def _load_module_by_name(mod_name):
 
774
    parts = mod_name.split('.')
 
775
    module = __import__(mod_name)
 
776
    del parts[0]
 
777
    # for historical reasons python returns the top-level module even though
 
778
    # it loads the submodule; we need to walk down to get the one we want.
 
779
    while parts:
 
780
        module = getattr(module, parts.pop(0))
 
781
    return module