/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-13 01:29:22 UTC
  • Revision ID: robertc@lifelesslap.robertcollins.net-20051013012922-b0249adeccecc4ed
teach check about ghosts

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