~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: John Arbash Meinel
  • Date: 2006-01-23 22:33:23 UTC
  • mto: This revision was merged to the branch mainline in revision 1551.
  • Revision ID: john@arbash-meinel.com-20060123223323-16eb865383bf650a
From Jari Alto: Makefile fixes (clean target error suppression)

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
 
from testsweet import TestCase, run_suite, InTempDir, FunctionalTestCase
 
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
 
27
import logging
 
28
import os
 
29
import re
 
30
import shutil
 
31
import stat
 
32
import sys
 
33
import tempfile
 
34
import unittest
 
35
import time
 
36
import codecs
 
37
 
 
38
import bzrlib.branch
19
39
import bzrlib.commands
20
 
import bzrlib.fetch
 
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
 
47
import bzrlib.trace
 
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
21
52
 
22
53
MODULES_TO_TEST = []
23
 
MODULES_TO_DOCTEST = []
24
 
 
25
 
 
26
 
class BzrTestBase(InTempDir):
27
 
    """bzr-specific test base class"""
 
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
 
 
208
 
 
209
class CommandFailed(Exception):
 
210
    pass
 
211
 
 
212
class TestCase(unittest.TestCase):
 
213
    """Base class for bzr unit tests.
 
214
    
 
215
    Tests that need access to disk resources should subclass 
 
216
    TestCaseInTempDir not TestCase.
 
217
 
 
218
    Error and debug log messages are redirected from their usual
 
219
    location into a temporary file, the contents of which can be
 
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.
 
223
       
 
224
    There are also convenience functions to invoke bzr's command-line
 
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
    """
 
232
 
 
233
    BZRPATH = 'bzr'
 
234
    _log_file_name = None
 
235
    _log_contents = ''
 
236
 
 
237
    def setUp(self):
 
238
        unittest.TestCase.setUp(self)
 
239
        self._cleanups = []
 
240
        self._cleanEnvironment()
 
241
        bzrlib.trace.disable_default_logging()
 
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
        """
 
312
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
 
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)
 
316
        self._log_file_name = name
 
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()
 
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()
 
378
        unittest.TestCase.tearDown(self)
 
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()
 
389
 
 
390
    def log(self, *args):
 
391
        mutter(*args)
 
392
 
 
393
    def _get_log(self):
 
394
        """Return as a string the log for this test"""
 
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
 
449
 
28
450
    def run_bzr(self, *args, **kwargs):
29
 
        retcode = kwargs.get('retcode', 0)
30
 
        result = self.apply_redirected(None, None, None,
31
 
                                       bzrlib.commands.run_bzr, args)
32
 
        self.assertEquals(result, retcode)
33
 
        
34
 
 
35
 
def selftest(verbose=False, pattern=".*"):
36
 
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
 
451
        """Invoke bzr, as if it were run from the command line.
 
452
 
 
453
        This should be the main method for tests that want to exercise the
 
454
        overall behavior of the bzr application (rather than a unit test
 
455
        or a functional test of the library.)
 
456
 
 
457
        This sends the stdout/stderr results into the test's log,
 
458
        where it may be useful for debugging.  See also run_captured.
 
459
        """
 
460
        retcode = kwargs.pop('retcode', 0)
 
461
        return self.run_bzr_captured(args, retcode)
 
462
 
 
463
    def check_inventory_shape(self, inv, shape):
 
464
        """Compare an inventory to a list of expected names.
 
465
 
 
466
        Fail if they are not precisely equal.
 
467
        """
 
468
        extras = []
 
469
        shape = list(shape)             # copy
 
470
        for path, ie in inv.entries():
 
471
            name = path.replace('\\', '/')
 
472
            if ie.kind == 'dir':
 
473
                name = name + '/'
 
474
            if name in shape:
 
475
                shape.remove(name)
 
476
            else:
 
477
                extras.append(name)
 
478
        if shape:
 
479
            self.fail("expected paths not found in inventory: %r" % shape)
 
480
        if extras:
 
481
            self.fail("unexpected paths found in inventory: %r" % extras)
 
482
 
 
483
    def apply_redirected(self, stdin=None, stdout=None, stderr=None,
 
484
                         a_callable=None, *args, **kwargs):
 
485
        """Call callable with redirected std io pipes.
 
486
 
 
487
        Returns the return code."""
 
488
        if not callable(a_callable):
 
489
            raise ValueError("a_callable must be callable.")
 
490
        if stdin is None:
 
491
            stdin = StringIO("")
 
492
        if stdout is None:
 
493
            if hasattr(self, "_log_file"):
 
494
                stdout = self._log_file
 
495
            else:
 
496
                stdout = StringIO()
 
497
        if stderr is None:
 
498
            if hasattr(self, "_log_file"):
 
499
                stderr = self._log_file
 
500
            else:
 
501
                stderr = StringIO()
 
502
        real_stdin = sys.stdin
 
503
        real_stdout = sys.stdout
 
504
        real_stderr = sys.stderr
 
505
        try:
 
506
            sys.stdout = stdout
 
507
            sys.stderr = stderr
 
508
            sys.stdin = stdin
 
509
            return a_callable(*args, **kwargs)
 
510
        finally:
 
511
            sys.stdout = real_stdout
 
512
            sys.stderr = real_stderr
 
513
            sys.stdin = real_stdin
 
514
 
 
515
 
 
516
BzrTestBase = TestCase
 
517
 
 
518
     
 
519
class TestCaseInTempDir(TestCase):
 
520
    """Derived class that runs a test within a temporary directory.
 
521
 
 
522
    This is useful for tests that need to create a branch, etc.
 
523
 
 
524
    The directory is created in a slightly complex way: for each
 
525
    Python invocation, a new temporary top-level directory is created.
 
526
    All test cases create their own directory within that.  If the
 
527
    tests complete successfully, the directory is removed.
 
528
 
 
529
    InTempDir is an old alias for FunctionalTestCase.
 
530
    """
 
531
 
 
532
    TEST_ROOT = None
 
533
    _TEST_NAME = 'test'
 
534
    OVERRIDE_PYTHON = 'python'
 
535
 
 
536
    def check_file_contents(self, filename, expect):
 
537
        self.log("check contents of file %s" % filename)
 
538
        contents = file(filename, 'r').read()
 
539
        if contents != expect:
 
540
            self.log("expected: %r" % expect)
 
541
            self.log("actually: %r" % contents)
 
542
            self.fail("contents of %s not as expected" % filename)
 
543
 
 
544
    def _make_test_root(self):
 
545
        if TestCaseInTempDir.TEST_ROOT is not None:
 
546
            return
 
547
        i = 0
 
548
        while True:
 
549
            root = u'test%04d.tmp' % i
 
550
            try:
 
551
                os.mkdir(root)
 
552
            except OSError, e:
 
553
                if e.errno == errno.EEXIST:
 
554
                    i += 1
 
555
                    continue
 
556
                else:
 
557
                    raise
 
558
            # successfully created
 
559
            TestCaseInTempDir.TEST_ROOT = osutils.abspath(root)
 
560
            break
 
561
        # make a fake bzr directory there to prevent any tests propagating
 
562
        # up onto the source directory's real branch
 
563
        os.mkdir(osutils.pathjoin(TestCaseInTempDir.TEST_ROOT, '.bzr'))
 
564
 
 
565
    def setUp(self):
 
566
        super(TestCaseInTempDir, self).setUp()
 
567
        self._make_test_root()
 
568
        _currentdir = os.getcwdu()
 
569
        short_id = self.id().replace('bzrlib.tests.', '') \
 
570
                   .replace('__main__.', '')
 
571
        self.test_dir = osutils.pathjoin(self.TEST_ROOT, short_id)
 
572
        os.mkdir(self.test_dir)
 
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)
 
579
        
 
580
    def build_tree(self, shape, line_endings='native', transport=None):
 
581
        """Build a test tree according to a pattern.
 
582
 
 
583
        shape is a sequence of file specifications.  If the final
 
584
        character is '/', a directory is created.
 
585
 
 
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.
 
595
        """
 
596
        # XXX: It's OK to just create them using forward slashes on windows?
 
597
        if transport is None or transport.is_readonly():
 
598
            transport = bzrlib.transport.get_transport(".")
 
599
        for name in shape:
 
600
            self.assert_(isinstance(name, basestring))
 
601
            if name[-1] == '/':
 
602
                transport.mkdir(urlescape(name[:-1]))
 
603
            else:
 
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):
 
666
    """Run the whole test suite under the enhanced runner"""
 
667
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern,
 
668
                     stop_on_failure=stop_on_failure, keep_output=keep_output)
37
669
 
38
670
 
39
671
def test_suite():
40
 
    from bzrlib.selftest.TestUtil import TestLoader, TestSuite
41
 
    import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
42
 
    import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
 
672
    """Build and return TestSuite for the whole program."""
43
673
    from doctest import DocTestSuite
44
 
    import os
45
 
    import shutil
46
 
    import time
47
 
    import sys
48
 
    import unittest
49
 
 
50
 
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
51
 
 
52
 
    testmod_names = \
53
 
                  ['bzrlib.selftest.whitebox',
54
 
                   'bzrlib.selftest.versioning',
55
 
                   'bzrlib.selftest.testinv',
56
 
                   'bzrlib.selftest.testmerge3',
57
 
                   'bzrlib.selftest.testhashcache',
58
 
                   'bzrlib.selftest.teststatus',
59
 
                   'bzrlib.selftest.testlog',
60
 
                   'bzrlib.selftest.blackbox',
61
 
                   'bzrlib.selftest.testrevisionnamespaces',
62
 
                   'bzrlib.selftest.testbranch',
63
 
                   'bzrlib.selftest.testrevision',
64
 
                   'bzrlib.selftest.test_merge_core',
65
 
                   'bzrlib.selftest.test_smart_add',
66
 
                   'bzrlib.selftest.testdiff',
67
 
                   'bzrlib.fetch'
 
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',
68
737
                   ]
69
 
 
70
 
    for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
71
 
              bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
72
 
        if m not in MODULES_TO_DOCTEST:
73
 
            MODULES_TO_DOCTEST.append(m)
74
 
 
75
 
    TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
76
 
    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])
77
745
    print
78
746
    suite = TestSuite()
79
 
    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())
80
763
    for m in MODULES_TO_TEST:
81
 
         suite.addTest(TestLoader().loadTestsFromModule(m))
 
764
        suite.addTest(loader.loadTestsFromModule(m))
82
765
    for m in (MODULES_TO_DOCTEST):
83
766
        suite.addTest(DocTestSuite(m))
84
 
    for p in bzrlib.plugin.all_plugins:
85
 
        if hasattr(p, 'test_suite'):
86
 
            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())
87
770
    return suite
88
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