~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to testsweet.py

  • Committer: Martin Pool
  • Date: 2005-07-17 18:06:53 UTC
  • Revision ID: mbp@sourcefrog.net-20050717180653-f16d08bd74610f6d
- update more weave code to use intsets

Show diffs side-by-side

added added

removed removed

Lines of Context:
30
30
* utilities to run external commands and check their return code
31
31
  and/or output
32
32
 
33
 
Test cases should normally subclass testsweet.TestCase.  The test runner should
 
33
Test cases should normally subclass TestBase.  The test runner should
34
34
call runsuite().
35
35
 
36
36
This is meant to become independent of bzr, though that's not quite
37
37
true yet.
38
38
"""  
39
39
 
40
 
import unittest
41
 
import sys
42
 
 
43
 
# XXX: Don't need this anymore now we depend on python2.4
 
40
 
 
41
from unittest import TestResult, TestCase
 
42
 
44
43
def _need_subprocess():
45
44
    sys.stderr.write("sorry, this test suite requires the subprocess module\n"
46
45
                     "this is shipped with python2.4 and available separately for 2.3\n")
50
49
    pass
51
50
 
52
51
 
 
52
 
53
53
class TestSkipped(Exception):
54
54
    """Indicates that a test was intentionally skipped, rather than failing."""
55
55
    # XXX: Not used yet
56
56
 
57
57
 
58
 
class TestCase(unittest.TestCase):
 
58
class TestBase(TestCase):
59
59
    """Base class for bzr test cases.
60
60
 
61
61
    Just defines some useful helper functions; doesn't actually test
72
72
 
73
73
 
74
74
    def setUp(self):
75
 
        super(TestCase, self).setUp()
76
 
        # save stdout & stderr so there's no leakage from code-under-test
77
 
        self.real_stdout = sys.stdout
78
 
        self.real_stderr = sys.stderr
79
 
        sys.stdout = sys.stderr = TestCase.TEST_LOG
 
75
        super(TestBase, self).setUp()
80
76
        self.log("%s setup" % self.id())
81
77
 
 
78
 
82
79
    def tearDown(self):
83
 
        sys.stdout = self.real_stdout
84
 
        sys.stderr = self.real_stderr
 
80
        super(TestBase, self).tearDown()
85
81
        self.log("%s teardown" % self.id())
86
82
        self.log('')
87
 
        super(TestCase, self).tearDown()
 
83
 
88
84
 
89
85
    def formcmd(self, cmd):
90
86
        if isinstance(cmd, basestring):
173
169
 
174
170
    def log(self, msg):
175
171
        """Log a message to a progress file"""
176
 
        # XXX: The problem with this is that code that writes straight
177
 
        # to the log file won't be shown when we display the log
178
 
        # buffer; would be better to not have the in-memory buffer and
179
 
        # instead just a log file per test, which is read in and
180
 
        # displayed if the test fails.  That seems to imply one log
181
 
        # per test case, not globally.  OK?
182
172
        self._log_buf = self._log_buf + str(msg) + '\n'
183
173
        print >>self.TEST_LOG, msg
184
174
 
215
205
            
216
206
 
217
207
 
218
 
class InTempDir(TestCase):
 
208
class InTempDir(TestBase):
219
209
    """Base class for tests run in a temporary branch."""
220
210
    def setUp(self):
221
 
        super(InTempDir, self).setUp()
222
211
        import os
223
212
        self.test_dir = os.path.join(self.TEST_ROOT, self.__class__.__name__)
224
213
        os.mkdir(self.test_dir)
227
216
    def tearDown(self):
228
217
        import os
229
218
        os.chdir(self.TEST_ROOT)
230
 
        super(InTempDir, self).tearDown()
231
 
 
232
 
 
233
 
class _MyResult(unittest._TextTestResult):
 
219
 
 
220
 
 
221
 
 
222
 
 
223
 
 
224
class _MyResult(TestResult):
234
225
    """
235
226
    Custom TestResult.
236
227
 
237
228
    No special behaviour for now.
238
229
    """
239
 
    def __init__(self, out, style):
240
 
        super(_MyResult, self).__init__(out, False, 0)
 
230
    def __init__(self, out):
241
231
        self.out = out
242
 
        assert style in ('none', 'progress', 'verbose')
243
 
        self.style = style
 
232
        TestResult.__init__(self)
244
233
 
245
234
    def startTest(self, test):
246
 
        super(_MyResult, self).startTest(test)
247
235
        # TODO: Maybe show test.shortDescription somewhere?
248
236
        what = test.id()
249
237
        # python2.3 has the bad habit of just "runit" for doctests
250
238
        if what == 'runit':
251
239
            what = test.shortDescription()
252
 
        if self.style == 'verbose':
253
 
            print >>self.out, '%-60.60s' % what,
254
 
            self.out.flush()
 
240
        
 
241
        print >>self.out, '%-60.60s' % what,
 
242
        self.out.flush()
 
243
        TestResult.startTest(self, test)
 
244
 
 
245
    def stopTest(self, test):
 
246
        # print
 
247
        TestResult.stopTest(self, test)
 
248
 
255
249
 
256
250
    def addError(self, test, err):
257
 
        if self.style == 'verbose':
258
 
            print >>self.out, 'ERROR'
259
 
        elif self.style == 'progress':
260
 
            self.stream.write('E')
261
 
        self.stream.flush()
262
 
        super(_MyResult, self).addError(test, err)
 
251
        print >>self.out, 'ERROR'
 
252
        TestResult.addError(self, test, err)
 
253
        _show_test_failure('error', test, err, self.out)
263
254
 
264
255
    def addFailure(self, test, err):
265
 
        if self.style == 'verbose':
266
 
            print >>self.out, 'FAILURE'
267
 
        elif self.style == 'progress':
268
 
            self.stream.write('F')
269
 
        self.stream.flush()
270
 
        super(_MyResult, self).addFailure(test, err)
 
256
        print >>self.out, 'FAILURE'
 
257
        TestResult.addFailure(self, test, err)
 
258
        _show_test_failure('failure', test, err, self.out)
271
259
 
272
260
    def addSuccess(self, test):
273
 
        if self.style == 'verbose':
274
 
            print >>self.out, 'OK'
275
 
        elif self.style == 'progress':
276
 
            self.stream.write('~')
277
 
        self.stream.flush()
278
 
        super(_MyResult, self).addSuccess(test)
279
 
 
280
 
    def printErrors(self):
281
 
        if self.style == 'progress':
282
 
            self.stream.writeln()
283
 
        super(_MyResult, self).printErrors()
284
 
 
285
 
    def printErrorList(self, flavour, errors):
286
 
        for test, err in errors:
287
 
            self.stream.writeln(self.separator1)
288
 
            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
289
 
            self.stream.writeln(self.separator2)
290
 
            self.stream.writeln("%s" % err)
291
 
            if isinstance(test, TestCase):
292
 
                self.stream.writeln()
293
 
                self.stream.writeln('log from this test:')
294
 
                print >>self.stream, test._log_buf
295
 
 
296
 
 
297
 
class TestSuite(unittest.TestSuite):
298
 
    
299
 
    def __init__(self, tests=(), name='test'):
300
 
        super(TestSuite, self).__init__(tests)
301
 
        self._name = name
302
 
 
303
 
    def run(self, result):
304
 
        import os
305
 
        import shutil
306
 
        import time
307
 
        
308
 
        self._setup_test_log()
309
 
        self._setup_test_dir()
310
 
        print
311
 
    
312
 
        return super(TestSuite,self).run(result)
313
 
 
314
 
    def _setup_test_log(self):
315
 
        import time
316
 
        import os
317
 
        
318
 
        log_filename = os.path.abspath(self._name + '.log')
319
 
        # line buffered
320
 
        TestCase.TEST_LOG = open(log_filename, 'wt', buffering=1)
321
 
    
322
 
        print >>TestCase.TEST_LOG, "tests run at " + time.ctime()
323
 
        print '%-30s %s' % ('test log', log_filename)
324
 
 
325
 
    def _setup_test_dir(self):
326
 
        import os
327
 
        import shutil
328
 
        
329
 
        TestCase.ORIG_DIR = os.getcwdu()
330
 
        TestCase.TEST_ROOT = os.path.abspath(self._name + '.tmp')
331
 
    
332
 
        print '%-30s %s' % ('running tests in', TestCase.TEST_ROOT)
333
 
    
334
 
        if os.path.exists(TestCase.TEST_ROOT):
335
 
            shutil.rmtree(TestCase.TEST_ROOT)
336
 
        os.mkdir(TestCase.TEST_ROOT)
337
 
        os.chdir(TestCase.TEST_ROOT)
338
 
    
339
 
        # make a fake bzr directory there to prevent any tests propagating
340
 
        # up onto the source directory's real branch
341
 
        os.mkdir(os.path.join(TestCase.TEST_ROOT, '.bzr'))
342
 
 
343
 
 
344
 
class TextTestRunner(unittest.TextTestRunner):
345
 
 
346
 
    def __init__(self, stream=sys.stderr, descriptions=1, verbosity=0, style='progress'):
347
 
        super(TextTestRunner, self).__init__(stream, descriptions, verbosity)
348
 
        self.style = style
349
 
 
350
 
    def _makeResult(self):
351
 
        return _MyResult(self.stream, self.style)
352
 
 
353
 
    # If we want the old 4 line summary output (count, 0 failures, 0 errors)
354
 
    # we can override run() too.
355
 
 
356
 
 
357
 
def run_suite(a_suite, name='test', verbose=False):
358
 
    suite = TestSuite((a_suite,),name)
359
 
    if verbose:
360
 
        style = 'verbose'
361
 
    else:
362
 
        style = 'progress'
363
 
    runner = TextTestRunner(stream=sys.stdout, style=style)
364
 
    result = runner.run(suite)
 
261
        print >>self.out, 'OK'
 
262
        TestResult.addSuccess(self, test)
 
263
 
 
264
 
 
265
 
 
266
def run_suite(suite, name="test"):
 
267
    import os
 
268
    import shutil
 
269
    import time
 
270
    import sys
 
271
    
 
272
    _setup_test_log(name)
 
273
    _setup_test_dir(name)
 
274
    print
 
275
 
 
276
    # save stdout & stderr so there's no leakage from code-under-test
 
277
    real_stdout = sys.stdout
 
278
    real_stderr = sys.stderr
 
279
    sys.stdout = sys.stderr = TestBase.TEST_LOG
 
280
    try:
 
281
        result = _MyResult(real_stdout)
 
282
        suite.run(result)
 
283
    finally:
 
284
        sys.stdout = real_stdout
 
285
        sys.stderr = real_stderr
 
286
 
 
287
    _show_results(result)
 
288
 
365
289
    return result.wasSuccessful()
 
290
 
 
291
 
 
292
 
 
293
def _setup_test_log(name):
 
294
    import time
 
295
    import os
 
296
    
 
297
    log_filename = os.path.abspath(name + '.log')
 
298
    TestBase.TEST_LOG = open(log_filename, 'wt', buffering=1) # line buffered
 
299
 
 
300
    print >>TestBase.TEST_LOG, "tests run at " + time.ctime()
 
301
    print '%-30s %s' % ('test log', log_filename)
 
302
 
 
303
 
 
304
def _setup_test_dir(name):
 
305
    import os
 
306
    import shutil
 
307
    
 
308
    TestBase.ORIG_DIR = os.getcwdu()
 
309
    TestBase.TEST_ROOT = os.path.abspath(name + '.tmp')
 
310
 
 
311
    print '%-30s %s' % ('running tests in', TestBase.TEST_ROOT)
 
312
 
 
313
    if os.path.exists(TestBase.TEST_ROOT):
 
314
        shutil.rmtree(TestBase.TEST_ROOT)
 
315
    os.mkdir(TestBase.TEST_ROOT)
 
316
    os.chdir(TestBase.TEST_ROOT)
 
317
 
 
318
    # make a fake bzr directory there to prevent any tests propagating
 
319
    # up onto the source directory's real branch
 
320
    os.mkdir(os.path.join(TestBase.TEST_ROOT, '.bzr'))
 
321
 
 
322
    
 
323
 
 
324
def _show_results(result):
 
325
     print
 
326
     print '%4d tests run' % result.testsRun
 
327
     print '%4d errors' % len(result.errors)
 
328
     print '%4d failures' % len(result.failures)
 
329
 
 
330
 
 
331
 
 
332
def _show_test_failure(kind, case, exc_info, out):
 
333
    from traceback import print_exception
 
334
    
 
335
    print >>out, '-' * 60
 
336
    print >>out, case
 
337
    
 
338
    desc = case.shortDescription()
 
339
    if desc:
 
340
        print >>out, '   (%s)' % desc
 
341
         
 
342
    print_exception(exc_info[0], exc_info[1], exc_info[2], None, out)
 
343
        
 
344
    if isinstance(case, TestBase):
 
345
        print >>out
 
346
        print >>out, 'log from this test:'
 
347
        print >>out, case._log_buf
 
348
         
 
349
    print >>out, '-' * 60
 
350
    
 
351