/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

  • Committer: Robert Collins
  • Date: 2005-10-11 22:57:45 UTC
  • Revision ID: robertc@lifelesslap.robertcollins.net-20051011225745-5c02a0cfc85b36d1
fixup the verbose-does-nothing for add - add a --quiet instead

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005 by Canonical Ltd
 
2
 
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
 
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
 
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
 
 
18
from cStringIO import StringIO
 
19
import logging
 
20
import unittest
 
21
import tempfile
 
22
import os
 
23
import errno
 
24
import re
 
25
import shutil
 
26
import subprocess
 
27
import sys
 
28
import time
 
29
 
 
30
import bzrlib.commands
 
31
import bzrlib.trace
 
32
import bzrlib.fetch
 
33
from bzrlib.selftest import TestUtil
 
34
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
 
35
 
 
36
 
 
37
MODULES_TO_TEST = []
 
38
MODULES_TO_DOCTEST = []
 
39
 
 
40
from logging import debug, warning, error
 
41
 
 
42
 
 
43
 
 
44
class EarlyStoppingTestResultAdapter(object):
 
45
    """An adapter for TestResult to stop at the first first failure or error"""
 
46
 
 
47
    def __init__(self, result):
 
48
        self._result = result
 
49
 
 
50
    def addError(self, test, err):
 
51
        self._result.addError(test, err)
 
52
        self._result.stop()
 
53
 
 
54
    def addFailure(self, test, err):
 
55
        self._result.addFailure(test, err)
 
56
        self._result.stop()
 
57
 
 
58
    def __getattr__(self, name):
 
59
        return getattr(self._result, name)
 
60
 
 
61
    def __setattr__(self, name, value):
 
62
        if name == '_result':
 
63
            object.__setattr__(self, name, value)
 
64
        return setattr(self._result, name, value)
 
65
 
 
66
 
 
67
class _MyResult(unittest._TextTestResult):
 
68
    """
 
69
    Custom TestResult.
 
70
 
 
71
    No special behaviour for now.
 
72
    """
 
73
 
 
74
    def _elapsedTime(self):
 
75
        return "(Took %.3fs)" % (time.time() - self._start_time)
 
76
 
 
77
    def startTest(self, test):
 
78
        unittest.TestResult.startTest(self, test)
 
79
        # TODO: Maybe show test.shortDescription somewhere?
 
80
        what = test.shortDescription() or test.id()        
 
81
        if self.showAll:
 
82
            self.stream.write('%-70.70s' % what)
 
83
        self.stream.flush()
 
84
        self._start_time = time.time()
 
85
 
 
86
    def addError(self, test, err):
 
87
        unittest.TestResult.addError(self, test, err)
 
88
        if self.showAll:
 
89
            self.stream.writeln("ERROR %s" % self._elapsedTime())
 
90
        elif self.dots:
 
91
            self.stream.write('E')
 
92
        self.stream.flush()
 
93
 
 
94
    def addFailure(self, test, err):
 
95
        unittest.TestResult.addFailure(self, test, err)
 
96
        if self.showAll:
 
97
            self.stream.writeln("FAIL %s" % self._elapsedTime())
 
98
        elif self.dots:
 
99
            self.stream.write('F')
 
100
        self.stream.flush()
 
101
 
 
102
    def addSuccess(self, test):
 
103
        if self.showAll:
 
104
            self.stream.writeln('OK %s' % self._elapsedTime())
 
105
        elif self.dots:
 
106
            self.stream.write('~')
 
107
        self.stream.flush()
 
108
        unittest.TestResult.addSuccess(self, test)
 
109
 
 
110
    def printErrorList(self, flavour, errors):
 
111
        for test, err in errors:
 
112
            self.stream.writeln(self.separator1)
 
113
            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
 
114
            if hasattr(test, '_get_log'):
 
115
                self.stream.writeln()
 
116
                self.stream.writeln('log from this test:')
 
117
                print >>self.stream, test._get_log()
 
118
            self.stream.writeln(self.separator2)
 
119
            self.stream.writeln("%s" % err)
 
120
 
 
121
 
 
122
class TextTestRunner(unittest.TextTestRunner):
 
123
 
 
124
    def _makeResult(self):
 
125
        result = _MyResult(self.stream, self.descriptions, self.verbosity)
 
126
        return EarlyStoppingTestResultAdapter(result)
 
127
 
 
128
 
 
129
def iter_suite_tests(suite):
 
130
    """Return all tests in a suite, recursing through nested suites"""
 
131
    for item in suite._tests:
 
132
        if isinstance(item, unittest.TestCase):
 
133
            yield item
 
134
        elif isinstance(item, unittest.TestSuite):
 
135
            for r in iter_suite_tests(item):
 
136
                yield r
 
137
        else:
 
138
            raise Exception('unknown object %r inside test suite %r'
 
139
                            % (item, suite))
 
140
 
 
141
 
 
142
class TestSkipped(Exception):
 
143
    """Indicates that a test was intentionally skipped, rather than failing."""
 
144
    # XXX: Not used yet
 
145
 
 
146
 
 
147
class CommandFailed(Exception):
 
148
    pass
 
149
 
 
150
class TestCase(unittest.TestCase):
 
151
    """Base class for bzr unit tests.
 
152
    
 
153
    Tests that need access to disk resources should subclass 
 
154
    TestCaseInTempDir not TestCase.
 
155
 
 
156
    Error and debug log messages are redirected from their usual
 
157
    location into a temporary file, the contents of which can be
 
158
    retrieved by _get_log().
 
159
       
 
160
    There are also convenience functions to invoke bzr's command-line
 
161
    routine, and to build and check bzr trees."""
 
162
 
 
163
    BZRPATH = 'bzr'
 
164
 
 
165
    def setUp(self):
 
166
        unittest.TestCase.setUp(self)
 
167
        bzrlib.trace.disable_default_logging()
 
168
        self._enable_file_logging()
 
169
 
 
170
 
 
171
    def _enable_file_logging(self):
 
172
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
 
173
 
 
174
        self._log_file = os.fdopen(fileno, 'w+')
 
175
 
 
176
        hdlr = logging.StreamHandler(self._log_file)
 
177
        hdlr.setLevel(logging.DEBUG)
 
178
        hdlr.setFormatter(logging.Formatter('%(levelname)8s  %(message)s'))
 
179
        logging.getLogger('').addHandler(hdlr)
 
180
        logging.getLogger('').setLevel(logging.DEBUG)
 
181
        self._log_hdlr = hdlr
 
182
        debug('opened log file %s', name)
 
183
        
 
184
        self._log_file_name = name
 
185
 
 
186
    def tearDown(self):
 
187
        logging.getLogger('').removeHandler(self._log_hdlr)
 
188
        bzrlib.trace.enable_default_logging()
 
189
        logging.debug('%s teardown', self.id())
 
190
        self._log_file.close()
 
191
        unittest.TestCase.tearDown(self)
 
192
 
 
193
    def log(self, *args):
 
194
        logging.debug(*args)
 
195
 
 
196
    def _get_log(self):
 
197
        """Return as a string the log for this test"""
 
198
        return open(self._log_file_name).read()
 
199
 
 
200
 
 
201
    def capture(self, cmd):
 
202
        """Shortcut that splits cmd into words, runs, and returns stdout"""
 
203
        return self.run_bzr_captured(cmd.split())[0]
 
204
 
 
205
    def run_bzr_captured(self, argv, retcode=0):
 
206
        """Invoke bzr and return (result, stdout, stderr).
 
207
 
 
208
        Useful for code that wants to check the contents of the
 
209
        output, the way error messages are presented, etc.
 
210
 
 
211
        This should be the main method for tests that want to exercise the
 
212
        overall behavior of the bzr application (rather than a unit test
 
213
        or a functional test of the library.)
 
214
 
 
215
        Much of the old code runs bzr by forking a new copy of Python, but
 
216
        that is slower, harder to debug, and generally not necessary.
 
217
 
 
218
        This runs bzr through the interface that catches and reports
 
219
        errors, and with logging set to something approximating the
 
220
        default, so that error reporting can be checked.
 
221
 
 
222
        argv -- arguments to invoke bzr
 
223
        retcode -- expected return code, or None for don't-care.
 
224
        """
 
225
        stdout = StringIO()
 
226
        stderr = StringIO()
 
227
        self.log('run bzr: %s', ' '.join(argv))
 
228
        handler = logging.StreamHandler(stderr)
 
229
        handler.setFormatter(bzrlib.trace.QuietFormatter())
 
230
        handler.setLevel(logging.INFO)
 
231
        logger = logging.getLogger('')
 
232
        logger.addHandler(handler)
 
233
        try:
 
234
            result = self.apply_redirected(None, stdout, stderr,
 
235
                                           bzrlib.commands.run_bzr_catch_errors,
 
236
                                           argv)
 
237
        finally:
 
238
            logger.removeHandler(handler)
 
239
        out = stdout.getvalue()
 
240
        err = stderr.getvalue()
 
241
        if out:
 
242
            self.log('output:\n%s', out)
 
243
        if err:
 
244
            self.log('errors:\n%s', err)
 
245
        if retcode is not None:
 
246
            self.assertEquals(result, retcode)
 
247
        return out, err
 
248
 
 
249
    def run_bzr(self, *args, **kwargs):
 
250
        """Invoke bzr, as if it were run from the command line.
 
251
 
 
252
        This should be the main method for tests that want to exercise the
 
253
        overall behavior of the bzr application (rather than a unit test
 
254
        or a functional test of the library.)
 
255
 
 
256
        This sends the stdout/stderr results into the test's log,
 
257
        where it may be useful for debugging.  See also run_captured.
 
258
        """
 
259
        retcode = kwargs.pop('retcode', 0)
 
260
        return self.run_bzr_captured(args, retcode)
 
261
 
 
262
    def check_inventory_shape(self, inv, shape):
 
263
        """Compare an inventory to a list of expected names.
 
264
 
 
265
        Fail if they are not precisely equal.
 
266
        """
 
267
        extras = []
 
268
        shape = list(shape)             # copy
 
269
        for path, ie in inv.entries():
 
270
            name = path.replace('\\', '/')
 
271
            if ie.kind == 'dir':
 
272
                name = name + '/'
 
273
            if name in shape:
 
274
                shape.remove(name)
 
275
            else:
 
276
                extras.append(name)
 
277
        if shape:
 
278
            self.fail("expected paths not found in inventory: %r" % shape)
 
279
        if extras:
 
280
            self.fail("unexpected paths found in inventory: %r" % extras)
 
281
 
 
282
    def apply_redirected(self, stdin=None, stdout=None, stderr=None,
 
283
                         a_callable=None, *args, **kwargs):
 
284
        """Call callable with redirected std io pipes.
 
285
 
 
286
        Returns the return code."""
 
287
        if not callable(a_callable):
 
288
            raise ValueError("a_callable must be callable.")
 
289
        if stdin is None:
 
290
            stdin = StringIO("")
 
291
        if stdout is None:
 
292
            if hasattr(self, "_log_file"):
 
293
                stdout = self._log_file
 
294
            else:
 
295
                stdout = StringIO()
 
296
        if stderr is None:
 
297
            if hasattr(self, "_log_file"):
 
298
                stderr = self._log_file
 
299
            else:
 
300
                stderr = StringIO()
 
301
        real_stdin = sys.stdin
 
302
        real_stdout = sys.stdout
 
303
        real_stderr = sys.stderr
 
304
        try:
 
305
            sys.stdout = stdout
 
306
            sys.stderr = stderr
 
307
            sys.stdin = stdin
 
308
            return a_callable(*args, **kwargs)
 
309
        finally:
 
310
            sys.stdout = real_stdout
 
311
            sys.stderr = real_stderr
 
312
            sys.stdin = real_stdin
 
313
 
 
314
 
 
315
BzrTestBase = TestCase
 
316
 
 
317
     
 
318
class TestCaseInTempDir(TestCase):
 
319
    """Derived class that runs a test within a temporary directory.
 
320
 
 
321
    This is useful for tests that need to create a branch, etc.
 
322
 
 
323
    The directory is created in a slightly complex way: for each
 
324
    Python invocation, a new temporary top-level directory is created.
 
325
    All test cases create their own directory within that.  If the
 
326
    tests complete successfully, the directory is removed.
 
327
 
 
328
    InTempDir is an old alias for FunctionalTestCase.
 
329
    """
 
330
 
 
331
    TEST_ROOT = None
 
332
    _TEST_NAME = 'test'
 
333
    OVERRIDE_PYTHON = 'python'
 
334
 
 
335
    def check_file_contents(self, filename, expect):
 
336
        self.log("check contents of file %s" % filename)
 
337
        contents = file(filename, 'r').read()
 
338
        if contents != expect:
 
339
            self.log("expected: %r" % expect)
 
340
            self.log("actually: %r" % contents)
 
341
            self.fail("contents of %s not as expected" % filename)
 
342
 
 
343
    def _make_test_root(self):
 
344
        if TestCaseInTempDir.TEST_ROOT is not None:
 
345
            return
 
346
        i = 0
 
347
        while True:
 
348
            root = 'test%04d.tmp' % i
 
349
            try:
 
350
                os.mkdir(root)
 
351
            except OSError, e:
 
352
                if e.errno == errno.EEXIST:
 
353
                    i += 1
 
354
                    continue
 
355
                else:
 
356
                    raise
 
357
            # successfully created
 
358
            TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
 
359
            break
 
360
        # make a fake bzr directory there to prevent any tests propagating
 
361
        # up onto the source directory's real branch
 
362
        os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
 
363
 
 
364
    def setUp(self):
 
365
        super(TestCaseInTempDir, self).setUp()
 
366
        self._make_test_root()
 
367
        self._currentdir = os.getcwdu()
 
368
        short_id = self.id().replace('bzrlib.selftest.', '') \
 
369
                   .replace('__main__.', '')
 
370
        self.test_dir = os.path.join(self.TEST_ROOT, short_id)
 
371
        os.mkdir(self.test_dir)
 
372
        os.chdir(self.test_dir)
 
373
        
 
374
    def tearDown(self):
 
375
        os.chdir(self._currentdir)
 
376
        super(TestCaseInTempDir, self).tearDown()
 
377
 
 
378
    def build_tree(self, shape):
 
379
        """Build a test tree according to a pattern.
 
380
 
 
381
        shape is a sequence of file specifications.  If the final
 
382
        character is '/', a directory is created.
 
383
 
 
384
        This doesn't add anything to a branch.
 
385
        """
 
386
        # XXX: It's OK to just create them using forward slashes on windows?
 
387
        for name in shape:
 
388
            assert isinstance(name, basestring)
 
389
            if name[-1] == '/':
 
390
                os.mkdir(name[:-1])
 
391
            else:
 
392
                f = file(name, 'wt')
 
393
                print >>f, "contents of", name
 
394
                f.close()
 
395
 
 
396
    def failUnlessExists(self, path):
 
397
        """Fail unless path, which may be abs or relative, exists."""
 
398
        self.failUnless(os.path.exists(path))
 
399
        
 
400
 
 
401
class MetaTestLog(TestCase):
 
402
    def test_logging(self):
 
403
        """Test logs are captured when a test fails."""
 
404
        logging.info('an info message')
 
405
        warning('something looks dodgy...')
 
406
        logging.debug('hello, test is running')
 
407
        ##assert 0
 
408
 
 
409
 
 
410
def filter_suite_by_re(suite, pattern):
 
411
    result = TestUtil.TestSuite()
 
412
    filter_re = re.compile(pattern)
 
413
    for test in iter_suite_tests(suite):
 
414
        if filter_re.search(test.id()):
 
415
            result.addTest(test)
 
416
    return result
 
417
 
 
418
 
 
419
def run_suite(suite, name='test', verbose=False, pattern=".*"):
 
420
    TestCaseInTempDir._TEST_NAME = name
 
421
    if verbose:
 
422
        verbosity = 2
 
423
    else:
 
424
        verbosity = 1
 
425
    runner = TextTestRunner(stream=sys.stdout,
 
426
                            descriptions=0,
 
427
                            verbosity=verbosity)
 
428
    if pattern != '.*':
 
429
        suite = filter_suite_by_re(suite, pattern)
 
430
    result = runner.run(suite)
 
431
    # This is still a little bogus, 
 
432
    # but only a little. Folk not using our testrunner will
 
433
    # have to delete their temp directories themselves.
 
434
    if result.wasSuccessful():
 
435
        if TestCaseInTempDir.TEST_ROOT is not None:
 
436
            shutil.rmtree(TestCaseInTempDir.TEST_ROOT) 
 
437
    else:
 
438
        print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
 
439
    return result.wasSuccessful()
 
440
 
 
441
 
 
442
def selftest(verbose=False, pattern=".*"):
 
443
    """Run the whole test suite under the enhanced runner"""
 
444
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
 
445
 
 
446
 
 
447
def test_suite():
 
448
    """Build and return TestSuite for the whole program."""
 
449
    import bzrlib.store, bzrlib.inventory, bzrlib.branch
 
450
    import bzrlib.osutils, bzrlib.merge3, bzrlib.plugin
 
451
    from doctest import DocTestSuite
 
452
 
 
453
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
 
454
 
 
455
    testmod_names = \
 
456
                  ['bzrlib.selftest.MetaTestLog',
 
457
                   'bzrlib.selftest.testidentitymap',
 
458
                   'bzrlib.selftest.testinv',
 
459
                   'bzrlib.selftest.test_ancestry',
 
460
                   'bzrlib.selftest.test_commit',
 
461
                   'bzrlib.selftest.test_commit_merge',
 
462
                   'bzrlib.selftest.testconfig',
 
463
                   'bzrlib.selftest.versioning',
 
464
                   'bzrlib.selftest.testmerge3',
 
465
                   'bzrlib.selftest.testmerge',
 
466
                   'bzrlib.selftest.testhashcache',
 
467
                   'bzrlib.selftest.teststatus',
 
468
                   'bzrlib.selftest.testlog',
 
469
                   'bzrlib.selftest.testrevisionnamespaces',
 
470
                   'bzrlib.selftest.testbranch',
 
471
                   'bzrlib.selftest.testrevision',
 
472
                   'bzrlib.selftest.test_revision_info',
 
473
                   'bzrlib.selftest.test_merge_core',
 
474
                   'bzrlib.selftest.test_smart_add',
 
475
                   'bzrlib.selftest.test_bad_files',
 
476
                   'bzrlib.selftest.testdiff',
 
477
                   'bzrlib.selftest.test_parent',
 
478
                   'bzrlib.selftest.test_xml',
 
479
                   'bzrlib.selftest.test_weave',
 
480
                   'bzrlib.selftest.testfetch',
 
481
                   'bzrlib.selftest.whitebox',
 
482
                   'bzrlib.selftest.teststore',
 
483
                   'bzrlib.selftest.blackbox',
 
484
                   'bzrlib.selftest.testsampler',
 
485
                   'bzrlib.selftest.testtransactions',
 
486
                   'bzrlib.selftest.testtransport',
 
487
                   'bzrlib.selftest.testgraph',
 
488
                   'bzrlib.selftest.testworkingtree',
 
489
                   'bzrlib.selftest.test_upgrade',
 
490
                   'bzrlib.selftest.test_conflicts',
 
491
                   ]
 
492
 
 
493
    for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
 
494
              bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
 
495
        if m not in MODULES_TO_DOCTEST:
 
496
            MODULES_TO_DOCTEST.append(m)
 
497
 
 
498
    TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
 
499
    print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
 
500
    print
 
501
    suite = TestSuite()
 
502
    suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
 
503
    for m in MODULES_TO_TEST:
 
504
         suite.addTest(TestLoader().loadTestsFromModule(m))
 
505
    for m in (MODULES_TO_DOCTEST):
 
506
        suite.addTest(DocTestSuite(m))
 
507
    for p in bzrlib.plugin.all_plugins:
 
508
        if hasattr(p, 'test_suite'):
 
509
            suite.addTest(p.test_suite())
 
510
    return suite
 
511