~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

- move 'conflict' and 'resolved' from shipped plugin to regular builtins

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
import difflib
 
20
import errno
18
21
import logging
19
 
import unittest
20
 
import tempfile
21
22
import os
 
23
import re
 
24
import shutil
22
25
import sys
23
 
import subprocess
 
26
import tempfile
 
27
import unittest
 
28
import time
24
29
 
25
 
from testsweet import run_suite
26
30
import bzrlib.commands
27
 
 
28
31
import bzrlib.trace
29
32
import bzrlib.fetch
 
33
import bzrlib.osutils as osutils
 
34
from bzrlib.selftest import TestUtil
 
35
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
30
36
 
31
37
 
32
38
MODULES_TO_TEST = []
34
40
 
35
41
from logging import debug, warning, error
36
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
 
37
148
class CommandFailed(Exception):
38
149
    pass
39
150
 
51
162
    routine, and to build and check bzr trees."""
52
163
 
53
164
    BZRPATH = 'bzr'
 
165
    _log_file_name = None
54
166
 
55
167
    def setUp(self):
56
 
        # this replaces the default testsweet.TestCase; we don't want logging changed
57
168
        unittest.TestCase.setUp(self)
58
169
        bzrlib.trace.disable_default_logging()
59
170
        self._enable_file_logging()
60
171
 
 
172
    def _ndiff_strings(self, a, b):
 
173
        """Return ndiff between two strings containing lines."""
 
174
        difflines = difflib.ndiff(a.splitlines(True),
 
175
                                  b.splitlines(True),
 
176
                                  linejunk=lambda x: False,
 
177
                                  charjunk=lambda x: False)
 
178
        return ''.join(difflines)
61
179
 
 
180
    def assertEqualDiff(self, a, b):
 
181
        """Assert two texts are equal, if not raise an exception.
 
182
        
 
183
        This is intended for use with multi-line strings where it can 
 
184
        be hard to find the differences by eye.
 
185
        """
 
186
        # TODO: perhaps override assertEquals to call this for strings?
 
187
        if a == b:
 
188
            return
 
189
        raise AssertionError("texts not equal:\n" + 
 
190
                             self._ndiff_strings(a, b))      
 
191
        
62
192
    def _enable_file_logging(self):
63
193
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
64
194
 
66
196
 
67
197
        hdlr = logging.StreamHandler(self._log_file)
68
198
        hdlr.setLevel(logging.DEBUG)
69
 
        hdlr.setFormatter(logging.Formatter('%(levelname)4.4s  %(message)s'))
 
199
        hdlr.setFormatter(logging.Formatter('%(levelname)8s  %(message)s'))
70
200
        logging.getLogger('').addHandler(hdlr)
71
201
        logging.getLogger('').setLevel(logging.DEBUG)
72
202
        self._log_hdlr = hdlr
86
216
 
87
217
    def _get_log(self):
88
218
        """Return as a string the log for this test"""
89
 
        return open(self._log_file_name).read()
 
219
        if self._log_file_name:
 
220
            return open(self._log_file_name).read()
 
221
        else:
 
222
            return ''
 
223
 
 
224
    def capture(self, cmd):
 
225
        """Shortcut that splits cmd into words, runs, and returns stdout"""
 
226
        return self.run_bzr_captured(cmd.split())[0]
 
227
 
 
228
    def run_bzr_captured(self, argv, retcode=0):
 
229
        """Invoke bzr and return (result, stdout, stderr).
 
230
 
 
231
        Useful for code that wants to check the contents of the
 
232
        output, the way error messages are presented, etc.
 
233
 
 
234
        This should be the main method for tests that want to exercise the
 
235
        overall behavior of the bzr application (rather than a unit test
 
236
        or a functional test of the library.)
 
237
 
 
238
        Much of the old code runs bzr by forking a new copy of Python, but
 
239
        that is slower, harder to debug, and generally not necessary.
 
240
 
 
241
        This runs bzr through the interface that catches and reports
 
242
        errors, and with logging set to something approximating the
 
243
        default, so that error reporting can be checked.
 
244
 
 
245
        argv -- arguments to invoke bzr
 
246
        retcode -- expected return code, or None for don't-care.
 
247
        """
 
248
        stdout = StringIO()
 
249
        stderr = StringIO()
 
250
        self.log('run bzr: %s', ' '.join(argv))
 
251
        handler = logging.StreamHandler(stderr)
 
252
        handler.setFormatter(bzrlib.trace.QuietFormatter())
 
253
        handler.setLevel(logging.INFO)
 
254
        logger = logging.getLogger('')
 
255
        logger.addHandler(handler)
 
256
        try:
 
257
            result = self.apply_redirected(None, stdout, stderr,
 
258
                                           bzrlib.commands.run_bzr_catch_errors,
 
259
                                           argv)
 
260
        finally:
 
261
            logger.removeHandler(handler)
 
262
        out = stdout.getvalue()
 
263
        err = stderr.getvalue()
 
264
        if out:
 
265
            self.log('output:\n%s', out)
 
266
        if err:
 
267
            self.log('errors:\n%s', err)
 
268
        if retcode is not None:
 
269
            self.assertEquals(result, retcode)
 
270
        return out, err
90
271
 
91
272
    def run_bzr(self, *args, **kwargs):
92
273
        """Invoke bzr, as if it were run from the command line.
95
276
        overall behavior of the bzr application (rather than a unit test
96
277
        or a functional test of the library.)
97
278
 
98
 
        Much of the old code runs bzr by forking a new copy of Python, but
99
 
        that is slower, harder to debug, and generally not necessary.
 
279
        This sends the stdout/stderr results into the test's log,
 
280
        where it may be useful for debugging.  See also run_captured.
100
281
        """
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
 
        
 
282
        retcode = kwargs.pop('retcode', 0)
 
283
        return self.run_bzr_captured(args, retcode)
 
284
 
107
285
    def check_inventory_shape(self, inv, shape):
108
 
        """
109
 
        Compare an inventory to a list of expected names.
 
286
        """Compare an inventory to a list of expected names.
110
287
 
111
288
        Fail if they are not precisely equal.
112
289
        """
130
307
        """Call callable with redirected std io pipes.
131
308
 
132
309
        Returns the return code."""
133
 
        from StringIO import StringIO
134
310
        if not callable(a_callable):
135
311
            raise ValueError("a_callable must be callable.")
136
312
        if stdin is None:
185
361
        if contents != expect:
186
362
            self.log("expected: %r" % expect)
187
363
            self.log("actually: %r" % contents)
188
 
            self.fail("contents of %s not as expected")
 
364
            self.fail("contents of %s not as expected" % filename)
189
365
 
190
366
    def _make_test_root(self):
191
 
        import os
192
 
        import shutil
193
 
        import tempfile
194
 
        
195
367
        if TestCaseInTempDir.TEST_ROOT is not None:
196
368
            return
197
 
        TestCaseInTempDir.TEST_ROOT = os.path.abspath(
198
 
                                 tempfile.mkdtemp(suffix='.tmp',
199
 
                                                  prefix=self._TEST_NAME + '-',
200
 
                                                  dir=os.curdir))
201
 
    
 
369
        i = 0
 
370
        while True:
 
371
            root = 'test%04d.tmp' % i
 
372
            try:
 
373
                os.mkdir(root)
 
374
            except OSError, e:
 
375
                if e.errno == errno.EEXIST:
 
376
                    i += 1
 
377
                    continue
 
378
                else:
 
379
                    raise
 
380
            # successfully created
 
381
            TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
 
382
            break
202
383
        # make a fake bzr directory there to prevent any tests propagating
203
384
        # up onto the source directory's real branch
204
385
        os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
205
386
 
206
387
    def setUp(self):
207
388
        super(TestCaseInTempDir, self).setUp()
208
 
        import os
209
389
        self._make_test_root()
210
390
        self._currentdir = os.getcwdu()
211
 
        self.test_dir = os.path.join(self.TEST_ROOT, self.id())
 
391
        short_id = self.id().replace('bzrlib.selftest.', '') \
 
392
                   .replace('__main__.', '')
 
393
        self.test_dir = os.path.join(self.TEST_ROOT, short_id)
212
394
        os.mkdir(self.test_dir)
213
395
        os.chdir(self.test_dir)
214
396
        
215
397
    def tearDown(self):
216
 
        import os
217
398
        os.chdir(self._currentdir)
218
399
        super(TestCaseInTempDir, self).tearDown()
219
400
 
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
 
 
264
401
    def build_tree(self, shape):
265
402
        """Build a test tree according to a pattern.
266
403
 
270
407
        This doesn't add anything to a branch.
271
408
        """
272
409
        # XXX: It's OK to just create them using forward slashes on windows?
273
 
        import os
274
410
        for name in shape:
275
411
            assert isinstance(name, basestring)
276
412
            if name[-1] == '/':
279
415
                f = file(name, 'wt')
280
416
                print >>f, "contents of", name
281
417
                f.close()
282
 
                
283
418
 
 
419
    def failUnlessExists(self, path):
 
420
        """Fail unless path, which may be abs or relative, exists."""
 
421
        self.failUnless(osutils.lexists(path))
 
422
        
284
423
 
285
424
class MetaTestLog(TestCase):
286
425
    def test_logging(self):
291
430
        ##assert 0
292
431
 
293
432
 
 
433
def filter_suite_by_re(suite, pattern):
 
434
    result = TestUtil.TestSuite()
 
435
    filter_re = re.compile(pattern)
 
436
    for test in iter_suite_tests(suite):
 
437
        if filter_re.search(test.id()):
 
438
            result.addTest(test)
 
439
    return result
 
440
 
 
441
 
 
442
def run_suite(suite, name='test', verbose=False, pattern=".*"):
 
443
    TestCaseInTempDir._TEST_NAME = name
 
444
    if verbose:
 
445
        verbosity = 2
 
446
    else:
 
447
        verbosity = 1
 
448
    runner = TextTestRunner(stream=sys.stdout,
 
449
                            descriptions=0,
 
450
                            verbosity=verbosity)
 
451
    if pattern != '.*':
 
452
        suite = filter_suite_by_re(suite, pattern)
 
453
    result = runner.run(suite)
 
454
    # This is still a little bogus, 
 
455
    # but only a little. Folk not using our testrunner will
 
456
    # have to delete their temp directories themselves.
 
457
    if result.wasSuccessful():
 
458
        if TestCaseInTempDir.TEST_ROOT is not None:
 
459
            shutil.rmtree(TestCaseInTempDir.TEST_ROOT) 
 
460
    else:
 
461
        print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
 
462
    return result.wasSuccessful()
 
463
 
 
464
 
294
465
def selftest(verbose=False, pattern=".*"):
 
466
    """Run the whole test suite under the enhanced runner"""
295
467
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
296
468
 
297
469
 
298
470
def test_suite():
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
 
471
    """Build and return TestSuite for the whole program."""
 
472
    import bzrlib.store, bzrlib.inventory, bzrlib.branch
 
473
    import bzrlib.osutils, bzrlib.merge3, bzrlib.plugin
302
474
    from doctest import DocTestSuite
303
 
    import os
304
 
    import shutil
305
 
    import time
306
 
    import sys
307
475
 
308
476
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
309
477
 
310
478
    testmod_names = \
311
479
                  ['bzrlib.selftest.MetaTestLog',
312
 
                   'bzrlib.selftest.test_parent',
 
480
                   'bzrlib.selftest.testidentitymap',
313
481
                   'bzrlib.selftest.testinv',
314
 
                   'bzrlib.selftest.testfetch',
 
482
                   'bzrlib.selftest.test_ancestry',
 
483
                   'bzrlib.selftest.test_commit',
 
484
                   'bzrlib.selftest.test_commit_merge',
 
485
                   'bzrlib.selftest.testconfig',
315
486
                   'bzrlib.selftest.versioning',
316
 
                   'bzrlib.selftest.whitebox',
317
487
                   'bzrlib.selftest.testmerge3',
318
488
                   'bzrlib.selftest.testmerge',
319
489
                   'bzrlib.selftest.testhashcache',
320
490
                   'bzrlib.selftest.teststatus',
321
491
                   'bzrlib.selftest.testlog',
322
 
                   'bzrlib.selftest.blackbox',
323
492
                   'bzrlib.selftest.testrevisionnamespaces',
324
493
                   'bzrlib.selftest.testbranch',
325
 
                   'bzrlib.selftest.testremotebranch',
326
494
                   'bzrlib.selftest.testrevision',
 
495
                   'bzrlib.selftest.test_revision_info',
327
496
                   'bzrlib.selftest.test_merge_core',
328
497
                   'bzrlib.selftest.test_smart_add',
 
498
                   'bzrlib.selftest.test_bad_files',
329
499
                   'bzrlib.selftest.testdiff',
 
500
                   'bzrlib.selftest.test_parent',
330
501
                   'bzrlib.selftest.test_xml',
331
 
                   'bzrlib.fetch',
 
502
                   'bzrlib.selftest.test_weave',
 
503
                   'bzrlib.selftest.testfetch',
 
504
                   'bzrlib.selftest.whitebox',
332
505
                   'bzrlib.selftest.teststore',
 
506
                   'bzrlib.selftest.blackbox',
 
507
                   'bzrlib.selftest.testsampler',
 
508
                   'bzrlib.selftest.testtransactions',
333
509
                   'bzrlib.selftest.testtransport',
334
510
                   'bzrlib.selftest.testgraph',
 
511
                   'bzrlib.selftest.testworkingtree',
 
512
                   'bzrlib.selftest.test_upgrade',
 
513
                   'bzrlib.selftest.test_conflicts',
 
514
                   'bzrlib.selftest.testtestament',
 
515
                   'bzrlib.selftest.testannotate',
335
516
                   ]
336
517
 
337
518
    for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,