~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

add a clean target

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 cStringIO import StringIO
19
18
import logging
20
19
import unittest
21
20
import tempfile
22
21
import os
23
22
import sys
24
 
import errno
25
23
import subprocess
26
 
import shutil
27
 
import re
28
24
 
 
25
from testsweet import run_suite
29
26
import bzrlib.commands
 
27
 
30
28
import bzrlib.trace
31
29
import bzrlib.fetch
32
 
from bzrlib.selftest import TestUtil
33
 
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
34
30
 
35
31
 
36
32
MODULES_TO_TEST = []
38
34
 
39
35
from logging import debug, warning, error
40
36
 
41
 
 
42
 
 
43
 
class EarlyStoppingTestResultAdapter(object):
44
 
    """An adapter for TestResult to stop at the first first failure or error"""
45
 
 
46
 
    def __init__(self, result):
47
 
        self._result = result
48
 
 
49
 
    def addError(self, test, err):
50
 
        self._result.addError(test, err)
51
 
        self._result.stop()
52
 
 
53
 
    def addFailure(self, test, err):
54
 
        self._result.addFailure(test, err)
55
 
        self._result.stop()
56
 
 
57
 
    def __getattr__(self, name):
58
 
        return getattr(self._result, name)
59
 
 
60
 
    def __setattr__(self, name, value):
61
 
        if name == '_result':
62
 
            object.__setattr__(self, name, value)
63
 
        return setattr(self._result, name, value)
64
 
 
65
 
 
66
 
class _MyResult(unittest._TextTestResult):
67
 
    """
68
 
    Custom TestResult.
69
 
 
70
 
    No special behaviour for now.
71
 
    """
72
 
 
73
 
    def startTest(self, test):
74
 
        unittest.TestResult.startTest(self, test)
75
 
        # TODO: Maybe show test.shortDescription somewhere?
76
 
        what = test.shortDescription() or test.id()        
77
 
        if self.showAll:
78
 
            self.stream.write('%-70.70s' % what)
79
 
        self.stream.flush()
80
 
 
81
 
    def addError(self, test, err):
82
 
        super(_MyResult, self).addError(test, err)
83
 
        self.stream.flush()
84
 
 
85
 
    def addFailure(self, test, err):
86
 
        super(_MyResult, self).addFailure(test, err)
87
 
        self.stream.flush()
88
 
 
89
 
    def addSuccess(self, test):
90
 
        if self.showAll:
91
 
            self.stream.writeln('OK')
92
 
        elif self.dots:
93
 
            self.stream.write('~')
94
 
        self.stream.flush()
95
 
        unittest.TestResult.addSuccess(self, test)
96
 
 
97
 
    def printErrorList(self, flavour, errors):
98
 
        for test, err in errors:
99
 
            self.stream.writeln(self.separator1)
100
 
            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
101
 
            if hasattr(test, '_get_log'):
102
 
                self.stream.writeln()
103
 
                self.stream.writeln('log from this test:')
104
 
                print >>self.stream, test._get_log()
105
 
            self.stream.writeln(self.separator2)
106
 
            self.stream.writeln("%s" % err)
107
 
 
108
 
 
109
 
class TextTestRunner(unittest.TextTestRunner):
110
 
 
111
 
    def _makeResult(self):
112
 
        result = _MyResult(self.stream, self.descriptions, self.verbosity)
113
 
        return EarlyStoppingTestResultAdapter(result)
114
 
 
115
 
 
116
 
def iter_suite_tests(suite):
117
 
    """Return all tests in a suite, recursing through nested suites"""
118
 
    for item in suite._tests:
119
 
        if isinstance(item, unittest.TestCase):
120
 
            yield item
121
 
        elif isinstance(item, unittest.TestSuite):
122
 
            for r in iter_suite_tests(item):
123
 
                yield r
124
 
        else:
125
 
            raise Exception('unknown object %r inside test suite %r'
126
 
                            % (item, suite))
127
 
 
128
 
 
129
 
class TestSkipped(Exception):
130
 
    """Indicates that a test was intentionally skipped, rather than failing."""
131
 
    # XXX: Not used yet
132
 
 
133
 
 
134
37
class CommandFailed(Exception):
135
38
    pass
136
39
 
150
53
    BZRPATH = 'bzr'
151
54
 
152
55
    def setUp(self):
 
56
        # this replaces the default testsweet.TestCase; we don't want logging changed
153
57
        unittest.TestCase.setUp(self)
154
58
        bzrlib.trace.disable_default_logging()
155
59
        self._enable_file_logging()
162
66
 
163
67
        hdlr = logging.StreamHandler(self._log_file)
164
68
        hdlr.setLevel(logging.DEBUG)
165
 
        hdlr.setFormatter(logging.Formatter('%(levelname)8s  %(message)s'))
 
69
        hdlr.setFormatter(logging.Formatter('%(levelname)4.4s  %(message)s'))
166
70
        logging.getLogger('').addHandler(hdlr)
167
71
        logging.getLogger('').setLevel(logging.DEBUG)
168
72
        self._log_hdlr = hdlr
184
88
        """Return as a string the log for this test"""
185
89
        return open(self._log_file_name).read()
186
90
 
187
 
 
188
 
    def capture(self, cmd):
189
 
        """Shortcut that splits cmd into words, runs, and returns stdout"""
190
 
        return self.run_bzr_captured(cmd.split())[0]
191
 
 
192
 
    def run_bzr_captured(self, argv, retcode=0):
193
 
        """Invoke bzr and return (result, stdout, stderr).
194
 
 
195
 
        Useful for code that wants to check the contents of the
196
 
        output, the way error messages are presented, etc.
 
91
    def run_bzr(self, *args, **kwargs):
 
92
        """Invoke bzr, as if it were run from the command line.
197
93
 
198
94
        This should be the main method for tests that want to exercise the
199
95
        overall behavior of the bzr application (rather than a unit test
201
97
 
202
98
        Much of the old code runs bzr by forking a new copy of Python, but
203
99
        that is slower, harder to debug, and generally not necessary.
204
 
 
205
 
        This runs bzr through the interface that catches and reports
206
 
        errors, and with logging set to something approximating the
207
 
        default, so that error reporting can be checked.
208
 
 
209
 
        argv -- arguments to invoke bzr
210
 
        retcode -- expected return code, or None for don't-care.
211
 
        """
212
 
        stdout = StringIO()
213
 
        stderr = StringIO()
214
 
        self.log('run bzr: %s', ' '.join(argv))
215
 
        handler = logging.StreamHandler(stderr)
216
 
        handler.setFormatter(bzrlib.trace.QuietFormatter())
217
 
        handler.setLevel(logging.INFO)
218
 
        logger = logging.getLogger('')
219
 
        logger.addHandler(handler)
220
 
        try:
221
 
            result = self.apply_redirected(None, stdout, stderr,
222
 
                                           bzrlib.commands.run_bzr_catch_errors,
223
 
                                           argv)
224
 
        finally:
225
 
            logger.removeHandler(handler)
226
 
        out = stdout.getvalue()
227
 
        err = stderr.getvalue()
228
 
        if out:
229
 
            self.log('output:\n%s', out)
230
 
        if err:
231
 
            self.log('errors:\n%s', err)
232
 
        if retcode is not None:
233
 
            self.assertEquals(result, retcode)
234
 
        return out, err
235
 
 
236
 
    def run_bzr(self, *args, **kwargs):
237
 
        """Invoke bzr, as if it were run from the command line.
238
 
 
239
 
        This should be the main method for tests that want to exercise the
240
 
        overall behavior of the bzr application (rather than a unit test
241
 
        or a functional test of the library.)
242
 
 
243
 
        This sends the stdout/stderr results into the test's log,
244
 
        where it may be useful for debugging.  See also run_captured.
245
 
        """
246
 
        retcode = kwargs.pop('retcode', 0)
247
 
        return self.run_bzr_captured(args, retcode)
248
 
 
 
100
        """
 
101
        retcode = kwargs.get('retcode', 0)
 
102
        result = self.apply_redirected(None, None, None,
 
103
                                       bzrlib.commands.run_bzr, args)
 
104
        self.assertEquals(result, retcode)
 
105
        
 
106
        
249
107
    def check_inventory_shape(self, inv, shape):
250
 
        """Compare an inventory to a list of expected names.
 
108
        """
 
109
        Compare an inventory to a list of expected names.
251
110
 
252
111
        Fail if they are not precisely equal.
253
112
        """
271
130
        """Call callable with redirected std io pipes.
272
131
 
273
132
        Returns the return code."""
 
133
        from StringIO import StringIO
274
134
        if not callable(a_callable):
275
135
            raise ValueError("a_callable must be callable.")
276
136
        if stdin is None:
325
185
        if contents != expect:
326
186
            self.log("expected: %r" % expect)
327
187
            self.log("actually: %r" % contents)
328
 
            self.fail("contents of %s not as expected" % filename)
 
188
            self.fail("contents of %s not as expected")
329
189
 
330
190
    def _make_test_root(self):
 
191
        import os
 
192
        import shutil
 
193
        import tempfile
 
194
        
331
195
        if TestCaseInTempDir.TEST_ROOT is not None:
332
196
            return
333
 
        i = 0
334
 
        while True:
335
 
            root = 'test%04d.tmp' % i
336
 
            try:
337
 
                os.mkdir(root)
338
 
            except OSError, e:
339
 
                if e.errno == errno.EEXIST:
340
 
                    i += 1
341
 
                    continue
342
 
                else:
343
 
                    raise
344
 
            # successfully created
345
 
            TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
346
 
            break
 
197
        TestCaseInTempDir.TEST_ROOT = os.path.abspath(
 
198
                                 tempfile.mkdtemp(suffix='.tmp',
 
199
                                                  prefix=self._TEST_NAME + '-',
 
200
                                                  dir=os.curdir))
 
201
    
347
202
        # make a fake bzr directory there to prevent any tests propagating
348
203
        # up onto the source directory's real branch
349
204
        os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
350
205
 
351
206
    def setUp(self):
352
207
        super(TestCaseInTempDir, self).setUp()
 
208
        import os
353
209
        self._make_test_root()
354
210
        self._currentdir = os.getcwdu()
355
 
        short_id = self.id().replace('bzrlib.selftest.', '') \
356
 
                   .replace('__main__.', '')
357
 
        self.test_dir = os.path.join(self.TEST_ROOT, short_id)
 
211
        self.test_dir = os.path.join(self.TEST_ROOT, self.id())
358
212
        os.mkdir(self.test_dir)
359
213
        os.chdir(self.test_dir)
360
214
        
361
215
    def tearDown(self):
 
216
        import os
362
217
        os.chdir(self._currentdir)
363
218
        super(TestCaseInTempDir, self).tearDown()
364
219
 
 
220
    def _formcmd(self, cmd):
 
221
        if isinstance(cmd, basestring):
 
222
            cmd = cmd.split()
 
223
        if cmd[0] == 'bzr':
 
224
            cmd[0] = self.BZRPATH
 
225
            if self.OVERRIDE_PYTHON:
 
226
                cmd.insert(0, self.OVERRIDE_PYTHON)
 
227
        self.log('$ %r' % cmd)
 
228
        return cmd
 
229
 
 
230
    def runcmd(self, cmd, retcode=0):
 
231
        """Run one command and check the return code.
 
232
 
 
233
        Returns a tuple of (stdout,stderr) strings.
 
234
 
 
235
        If a single string is based, it is split into words.
 
236
        For commands that are not simple space-separated words, please
 
237
        pass a list instead."""
 
238
        cmd = self._formcmd(cmd)
 
239
        self.log('$ ' + ' '.join(cmd))
 
240
        actual_retcode = subprocess.call(cmd, stdout=self._log_file,
 
241
                                         stderr=self._log_file)
 
242
        if retcode != actual_retcode:
 
243
            raise CommandFailed("test failed: %r returned %d, expected %d"
 
244
                                % (cmd, actual_retcode, retcode))
 
245
 
 
246
    def backtick(self, cmd, retcode=0):
 
247
        """Run a command and return its output"""
 
248
        cmd = self._formcmd(cmd)
 
249
        child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self._log_file)
 
250
        outd, errd = child.communicate()
 
251
        self.log(outd)
 
252
        actual_retcode = child.wait()
 
253
 
 
254
        outd = outd.replace('\r', '')
 
255
 
 
256
        if retcode != actual_retcode:
 
257
            raise CommandFailed("test failed: %r returned %d, expected %d"
 
258
                                % (cmd, actual_retcode, retcode))
 
259
 
 
260
        return outd
 
261
 
 
262
 
 
263
 
365
264
    def build_tree(self, shape):
366
265
        """Build a test tree according to a pattern.
367
266
 
371
270
        This doesn't add anything to a branch.
372
271
        """
373
272
        # XXX: It's OK to just create them using forward slashes on windows?
 
273
        import os
374
274
        for name in shape:
375
275
            assert isinstance(name, basestring)
376
276
            if name[-1] == '/':
379
279
                f = file(name, 'wt')
380
280
                print >>f, "contents of", name
381
281
                f.close()
 
282
                
382
283
 
383
 
    def failUnlessExists(self, path):
384
 
        """Fail unless path, which may be abs or relative, exists."""
385
 
        self.failUnless(os.path.exists(path))
386
 
        
387
284
 
388
285
class MetaTestLog(TestCase):
389
286
    def test_logging(self):
394
291
        ##assert 0
395
292
 
396
293
 
397
 
def filter_suite_by_re(suite, pattern):
398
 
    result = TestUtil.TestSuite()
399
 
    filter_re = re.compile(pattern)
400
 
    for test in iter_suite_tests(suite):
401
 
        if filter_re.match(test.id()):
402
 
            result.addTest(test)
403
 
    return result
404
 
 
405
 
 
406
 
def filter_suite_by_names(suite, wanted_names):
407
 
    """Return a new suite containing only selected tests.
408
 
    
409
 
    Names are considered to match if any name is a substring of the 
410
 
    fully-qualified test id (i.e. the class ."""
411
 
    result = TestSuite()
412
 
    for test in iter_suite_tests(suite):
413
 
        this_id = test.id()
414
 
        for p in wanted_names:
415
 
            if this_id.find(p) != -1:
416
 
                result.addTest(test)
417
 
    return result
418
 
 
419
 
 
420
 
def run_suite(suite, name='test', verbose=False, pattern=".*", testnames=None):
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 testnames:
430
 
        suite = filter_suite_by_names(suite, testnames)
431
 
    if pattern != '.*':
432
 
        suite = filter_suite_by_re(suite, pattern)
433
 
    result = runner.run(suite)
434
 
    # This is still a little bogus, 
435
 
    # but only a little. Folk not using our testrunner will
436
 
    # have to delete their temp directories themselves.
437
 
    if result.wasSuccessful():
438
 
        if TestCaseInTempDir.TEST_ROOT is not None:
439
 
            shutil.rmtree(TestCaseInTempDir.TEST_ROOT) 
440
 
    else:
441
 
        print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
442
 
    return result.wasSuccessful()
443
 
 
444
 
 
445
 
def selftest(verbose=False, pattern=".*", testnames=None):
446
 
    """Run the whole test suite under the enhanced runner"""
447
 
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern,
448
 
                     testnames=testnames)
 
294
def selftest(verbose=False, pattern=".*"):
 
295
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
449
296
 
450
297
 
451
298
def test_suite():
452
 
    """Build and return TestSuite for the whole program."""
453
 
    import bzrlib.store, bzrlib.inventory, bzrlib.branch
454
 
    import bzrlib.osutils, bzrlib.merge3, bzrlib.plugin
 
299
    from bzrlib.selftest.TestUtil import TestLoader, TestSuite
 
300
    import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
 
301
    import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
455
302
    from doctest import DocTestSuite
 
303
    import os
 
304
    import shutil
 
305
    import time
 
306
    import sys
456
307
 
457
308
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
458
309
 
459
310
    testmod_names = \
460
311
                  ['bzrlib.selftest.MetaTestLog',
 
312
                   'bzrlib.selftest.test_parent',
461
313
                   'bzrlib.selftest.testinv',
462
 
                   'bzrlib.selftest.test_ancestry',
463
 
                   'bzrlib.selftest.test_commit',
464
 
                   'bzrlib.selftest.test_commit_merge',
 
314
                   'bzrlib.selftest.testfetch',
465
315
                   'bzrlib.selftest.versioning',
 
316
                   'bzrlib.selftest.whitebox',
466
317
                   'bzrlib.selftest.testmerge3',
467
318
                   'bzrlib.selftest.testmerge',
468
319
                   'bzrlib.selftest.testhashcache',
469
320
                   'bzrlib.selftest.teststatus',
470
321
                   'bzrlib.selftest.testlog',
 
322
                   'bzrlib.selftest.blackbox',
471
323
                   'bzrlib.selftest.testrevisionnamespaces',
472
324
                   'bzrlib.selftest.testbranch',
 
325
                   'bzrlib.selftest.testremotebranch',
473
326
                   'bzrlib.selftest.testrevision',
474
 
                   'bzrlib.selftest.test_revision_info',
475
327
                   'bzrlib.selftest.test_merge_core',
476
328
                   'bzrlib.selftest.test_smart_add',
477
 
                   'bzrlib.selftest.test_bad_files',
478
329
                   'bzrlib.selftest.testdiff',
479
 
                   'bzrlib.selftest.test_parent',
480
330
                   'bzrlib.selftest.test_xml',
481
 
                   'bzrlib.selftest.test_weave',
482
 
                   'bzrlib.selftest.testfetch',
483
 
                   'bzrlib.selftest.whitebox',
 
331
                   'bzrlib.fetch',
484
332
                   'bzrlib.selftest.teststore',
485
 
                   'bzrlib.selftest.blackbox',
486
 
                   'bzrlib.selftest.testtransport',
487
333
                   'bzrlib.selftest.testgraph',
488
 
                   'bzrlib.selftest.testworkingtree',
489
 
                   'bzrlib.selftest.test_upgrade',
490
334
                   ]
491
335
 
492
336
    for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,