~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/__init__.py

  • Committer: Robert Collins
  • Date: 2005-10-14 04:22:22 UTC
  • mto: This revision was merged to the branch mainline in revision 1456.
  • Revision ID: robertc@lifelesslap.robertcollins.net-20051014042222-5d1209d88d5bfef5
LocationConfig section retrieval falls into my lap

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
 
74
204
        
75
205
        self._log_file_name = name
76
206
 
77
 
        
78
207
    def tearDown(self):
79
208
        logging.getLogger('').removeHandler(self._log_hdlr)
80
209
        bzrlib.trace.enable_default_logging()
82
211
        self._log_file.close()
83
212
        unittest.TestCase.tearDown(self)
84
213
 
85
 
 
86
214
    def log(self, *args):
87
215
        logging.debug(*args)
88
216
 
89
217
    def _get_log(self):
90
218
        """Return as a string the log for this test"""
91
 
        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
92
271
 
93
272
    def run_bzr(self, *args, **kwargs):
94
273
        """Invoke bzr, as if it were run from the command line.
97
276
        overall behavior of the bzr application (rather than a unit test
98
277
        or a functional test of the library.)
99
278
 
100
 
        Much of the old code runs bzr by forking a new copy of Python, but
101
 
        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.
102
281
        """
103
 
        retcode = kwargs.get('retcode', 0)
104
 
        result = self.apply_redirected(None, None, None,
105
 
                                       bzrlib.commands.run_bzr, args)
106
 
        self.assertEquals(result, retcode)
107
 
        
108
 
        
 
282
        retcode = kwargs.pop('retcode', 0)
 
283
        return self.run_bzr_captured(args, retcode)
 
284
 
109
285
    def check_inventory_shape(self, inv, shape):
110
 
        """
111
 
        Compare an inventory to a list of expected names.
 
286
        """Compare an inventory to a list of expected names.
112
287
 
113
288
        Fail if they are not precisely equal.
114
289
        """
132
307
        """Call callable with redirected std io pipes.
133
308
 
134
309
        Returns the return code."""
135
 
        from StringIO import StringIO
136
310
        if not callable(a_callable):
137
311
            raise ValueError("a_callable must be callable.")
138
312
        if stdin is None:
139
313
            stdin = StringIO("")
140
314
        if stdout is None:
141
 
            stdout = self._log_file
 
315
            if hasattr(self, "_log_file"):
 
316
                stdout = self._log_file
 
317
            else:
 
318
                stdout = StringIO()
142
319
        if stderr is None:
143
 
            stderr = self._log_file
 
320
            if hasattr(self, "_log_file"):
 
321
                stderr = self._log_file
 
322
            else:
 
323
                stderr = StringIO()
144
324
        real_stdin = sys.stdin
145
325
        real_stdout = sys.stdout
146
326
        real_stderr = sys.stderr
181
361
        if contents != expect:
182
362
            self.log("expected: %r" % expect)
183
363
            self.log("actually: %r" % contents)
184
 
            self.fail("contents of %s not as expected")
 
364
            self.fail("contents of %s not as expected" % filename)
185
365
 
186
366
    def _make_test_root(self):
187
 
        import os
188
 
        import shutil
189
 
        import tempfile
190
 
        
191
367
        if TestCaseInTempDir.TEST_ROOT is not None:
192
368
            return
193
 
        TestCaseInTempDir.TEST_ROOT = os.path.abspath(
194
 
                                 tempfile.mkdtemp(suffix='.tmp',
195
 
                                                  prefix=self._TEST_NAME + '-',
196
 
                                                  dir=os.curdir))
197
 
    
 
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
198
383
        # make a fake bzr directory there to prevent any tests propagating
199
384
        # up onto the source directory's real branch
200
385
        os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
201
386
 
202
387
    def setUp(self):
203
388
        super(TestCaseInTempDir, self).setUp()
204
 
        import os
205
389
        self._make_test_root()
206
390
        self._currentdir = os.getcwdu()
207
 
        short_id = self.id().replace('bzrlib.selftest.', '')
 
391
        short_id = self.id().replace('bzrlib.selftest.', '') \
 
392
                   .replace('__main__.', '')
208
393
        self.test_dir = os.path.join(self.TEST_ROOT, short_id)
209
394
        os.mkdir(self.test_dir)
210
395
        os.chdir(self.test_dir)
211
396
        
212
397
    def tearDown(self):
213
 
        import os
214
398
        os.chdir(self._currentdir)
215
399
        super(TestCaseInTempDir, self).tearDown()
216
400
 
217
 
    def _formcmd(self, cmd):
218
 
        if isinstance(cmd, basestring):
219
 
            cmd = cmd.split()
220
 
        if cmd[0] == 'bzr':
221
 
            cmd[0] = self.BZRPATH
222
 
            if self.OVERRIDE_PYTHON:
223
 
                cmd.insert(0, self.OVERRIDE_PYTHON)
224
 
        self.log('$ %r' % cmd)
225
 
        return cmd
226
 
 
227
 
    def runcmd(self, cmd, retcode=0):
228
 
        """Run one command and check the return code.
229
 
 
230
 
        Returns a tuple of (stdout,stderr) strings.
231
 
 
232
 
        If a single string is based, it is split into words.
233
 
        For commands that are not simple space-separated words, please
234
 
        pass a list instead."""
235
 
        cmd = self._formcmd(cmd)
236
 
        self.log('$ ' + ' '.join(cmd))
237
 
        actual_retcode = subprocess.call(cmd, stdout=self._log_file,
238
 
                                         stderr=self._log_file)
239
 
        if retcode != actual_retcode:
240
 
            raise CommandFailed("test failed: %r returned %d, expected %d"
241
 
                                % (cmd, actual_retcode, retcode))
242
 
 
243
 
    def backtick(self, cmd, retcode=0):
244
 
        """Run a command and return its output"""
245
 
        cmd = self._formcmd(cmd)
246
 
        child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self._log_file)
247
 
        outd, errd = child.communicate()
248
 
        self.log(outd)
249
 
        actual_retcode = child.wait()
250
 
 
251
 
        outd = outd.replace('\r', '')
252
 
 
253
 
        if retcode != actual_retcode:
254
 
            raise CommandFailed("test failed: %r returned %d, expected %d"
255
 
                                % (cmd, actual_retcode, retcode))
256
 
 
257
 
        return outd
258
 
 
259
 
 
260
 
 
261
401
    def build_tree(self, shape):
262
402
        """Build a test tree according to a pattern.
263
403
 
267
407
        This doesn't add anything to a branch.
268
408
        """
269
409
        # XXX: It's OK to just create them using forward slashes on windows?
270
 
        import os
271
410
        for name in shape:
272
411
            assert isinstance(name, basestring)
273
412
            if name[-1] == '/':
276
415
                f = file(name, 'wt')
277
416
                print >>f, "contents of", name
278
417
                f.close()
279
 
                
280
418
 
 
419
    def failUnlessExists(self, path):
 
420
        """Fail unless path, which may be abs or relative, exists."""
 
421
        self.failUnless(osutils.lexists(path))
 
422
        
281
423
 
282
424
class MetaTestLog(TestCase):
283
425
    def test_logging(self):
288
430
        ##assert 0
289
431
 
290
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
 
291
465
def selftest(verbose=False, pattern=".*"):
292
466
    """Run the whole test suite under the enhanced runner"""
293
467
    return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
295
469
 
296
470
def test_suite():
297
471
    """Build and return TestSuite for the whole program."""
298
 
    from bzrlib.selftest.TestUtil import TestLoader, TestSuite
299
 
    import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
300
 
    import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
 
472
    import bzrlib.store, bzrlib.inventory, bzrlib.branch
 
473
    import bzrlib.osutils, bzrlib.merge3, bzrlib.plugin
301
474
    from doctest import DocTestSuite
302
 
    import os
303
 
    import shutil
304
 
    import time
305
 
    import sys
306
475
 
307
476
    global MODULES_TO_TEST, MODULES_TO_DOCTEST
308
477
 
309
478
    testmod_names = \
310
479
                  ['bzrlib.selftest.MetaTestLog',
 
480
                   'bzrlib.selftest.testidentitymap',
311
481
                   'bzrlib.selftest.testinv',
 
482
                   'bzrlib.selftest.test_ancestry',
 
483
                   'bzrlib.selftest.test_commit',
 
484
                   'bzrlib.selftest.test_commit_merge',
 
485
                   'bzrlib.selftest.testconfig',
312
486
                   'bzrlib.selftest.versioning',
313
487
                   'bzrlib.selftest.testmerge3',
 
488
                   'bzrlib.selftest.testmerge',
314
489
                   'bzrlib.selftest.testhashcache',
315
490
                   'bzrlib.selftest.teststatus',
316
491
                   'bzrlib.selftest.testlog',
317
492
                   'bzrlib.selftest.testrevisionnamespaces',
318
493
                   'bzrlib.selftest.testbranch',
319
494
                   'bzrlib.selftest.testrevision',
 
495
                   'bzrlib.selftest.test_revision_info',
320
496
                   'bzrlib.selftest.test_merge_core',
321
497
                   'bzrlib.selftest.test_smart_add',
 
498
                   'bzrlib.selftest.test_bad_files',
322
499
                   'bzrlib.selftest.testdiff',
323
500
                   'bzrlib.selftest.test_parent',
324
501
                   'bzrlib.selftest.test_xml',
 
502
                   'bzrlib.selftest.test_weave',
325
503
                   'bzrlib.selftest.testfetch',
326
504
                   'bzrlib.selftest.whitebox',
327
505
                   'bzrlib.selftest.teststore',
328
506
                   'bzrlib.selftest.blackbox',
 
507
                   'bzrlib.selftest.testsampler',
 
508
                   'bzrlib.selftest.testtransactions',
 
509
                   'bzrlib.selftest.testtransport',
 
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',
329
516
                   ]
330
517
 
331
518
    for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,