~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: Martin Pool
  • Date: 2010-09-13 05:01:57 UTC
  • mto: This revision was merged to the branch mainline in revision 5420.
  • Revision ID: mbp@sourcefrog.net-20100913050157-j7h02ik50793x9pg
Mention --coverage in selftest help

Show diffs side-by-side

added added

removed removed

Lines of Context:
36
36
import errno
37
37
import itertools
38
38
import logging
 
39
import math
39
40
import os
40
 
import platform
41
41
import pprint
42
42
import random
43
43
import re
71
71
    lock as _mod_lock,
72
72
    memorytree,
73
73
    osutils,
 
74
    progress,
74
75
    ui,
75
76
    urlutils,
76
77
    registry,
193
194
        self.count = 0
194
195
        self._overall_start_time = time.time()
195
196
        self._strict = strict
196
 
        self._first_thread_leaker_id = None
197
 
        self._tests_leaking_threads_count = 0
198
197
 
199
198
    def stopTestRun(self):
200
199
        run = self.testsRun
239
238
            ok = self.wasStrictlySuccessful()
240
239
        else:
241
240
            ok = self.wasSuccessful()
242
 
        if self._first_thread_leaker_id:
 
241
        if TestCase._first_thread_leaker_id:
243
242
            self.stream.write(
244
243
                '%s is leaking threads among %d leaking tests.\n' % (
245
 
                self._first_thread_leaker_id,
246
 
                self._tests_leaking_threads_count))
 
244
                TestCase._first_thread_leaker_id,
 
245
                TestCase._leaking_threads_tests))
247
246
            # We don't report the main thread as an active one.
248
247
            self.stream.write(
249
248
                '%d non-main threads were left active in the end.\n'
250
 
                % (len(self._active_threads) - 1))
 
249
                % (TestCase._active_threads - 1))
251
250
 
252
251
    def getDescription(self, test):
253
252
        return test.id()
284
283
        super(ExtendedTestResult, self).startTest(test)
285
284
        if self.count == 0:
286
285
            self.startTests()
287
 
        self.count += 1
288
286
        self.report_test_start(test)
289
287
        test.number = self.count
290
288
        self._recordTestStartTime()
291
 
        # Only check for thread leaks if the test case supports cleanups
292
 
        addCleanup = getattr(test, "addCleanup", None)
293
 
        if addCleanup is not None:
294
 
            addCleanup(self._check_leaked_threads, test)
295
289
 
296
290
    def startTests(self):
297
 
        self.report_tests_starting()
298
 
        self._active_threads = threading.enumerate()
299
 
 
300
 
    def _check_leaked_threads(self, test):
301
 
        """See if any threads have leaked since last call
302
 
 
303
 
        A sample of live threads is stored in the _active_threads attribute,
304
 
        when this method runs it compares the current live threads and any not
305
 
        in the previous sample are treated as having leaked.
306
 
        """
307
 
        now_active_threads = set(threading.enumerate())
308
 
        threads_leaked = now_active_threads.difference(self._active_threads)
309
 
        if threads_leaked:
310
 
            self._report_thread_leak(test, threads_leaked, now_active_threads)
311
 
            self._tests_leaking_threads_count += 1
312
 
            if self._first_thread_leaker_id is None:
313
 
                self._first_thread_leaker_id = test.id()
314
 
            self._active_threads = now_active_threads
 
291
        import platform
 
292
        if getattr(sys, 'frozen', None) is None:
 
293
            bzr_path = osutils.realpath(sys.argv[0])
 
294
        else:
 
295
            bzr_path = sys.executable
 
296
        self.stream.write(
 
297
            'bzr selftest: %s\n' % (bzr_path,))
 
298
        self.stream.write(
 
299
            '   %s\n' % (
 
300
                    bzrlib.__path__[0],))
 
301
        self.stream.write(
 
302
            '   bzr-%s python-%s %s\n' % (
 
303
                    bzrlib.version_string,
 
304
                    bzrlib._format_version_tuple(sys.version_info),
 
305
                    platform.platform(aliased=1),
 
306
                    ))
 
307
        self.stream.write('\n')
315
308
 
316
309
    def _recordTestStartTime(self):
317
310
        """Record that a test has started."""
318
311
        self._start_time = time.time()
319
312
 
 
313
    def _cleanupLogFile(self, test):
 
314
        # We can only do this if we have one of our TestCases, not if
 
315
        # we have a doctest.
 
316
        setKeepLogfile = getattr(test, 'setKeepLogfile', None)
 
317
        if setKeepLogfile is not None:
 
318
            setKeepLogfile()
 
319
 
320
320
    def addError(self, test, err):
321
321
        """Tell result that test finished with an error.
322
322
 
329
329
        self.report_error(test, err)
330
330
        if self.stop_early:
331
331
            self.stop()
 
332
        self._cleanupLogFile(test)
332
333
 
333
334
    def addFailure(self, test, err):
334
335
        """Tell result that test failed.
342
343
        self.report_failure(test, err)
343
344
        if self.stop_early:
344
345
            self.stop()
 
346
        self._cleanupLogFile(test)
345
347
 
346
348
    def addSuccess(self, test, details=None):
347
349
        """Tell result that test completed successfully.
355
357
                    self._formatTime(benchmark_time),
356
358
                    test.id()))
357
359
        self.report_success(test)
 
360
        self._cleanupLogFile(test)
358
361
        super(ExtendedTestResult, self).addSuccess(test)
359
362
        test._log_contents = ''
360
363
 
398
401
        else:
399
402
            raise errors.BzrError("Unknown whence %r" % whence)
400
403
 
401
 
    def report_tests_starting(self):
402
 
        """Display information before the test run begins"""
403
 
        if getattr(sys, 'frozen', None) is None:
404
 
            bzr_path = osutils.realpath(sys.argv[0])
405
 
        else:
406
 
            bzr_path = sys.executable
407
 
        self.stream.write(
408
 
            'bzr selftest: %s\n' % (bzr_path,))
409
 
        self.stream.write(
410
 
            '   %s\n' % (
411
 
                    bzrlib.__path__[0],))
412
 
        self.stream.write(
413
 
            '   bzr-%s python-%s %s\n' % (
414
 
                    bzrlib.version_string,
415
 
                    bzrlib._format_version_tuple(sys.version_info),
416
 
                    platform.platform(aliased=1),
417
 
                    ))
418
 
        self.stream.write('\n')
419
 
 
420
 
    def report_test_start(self, test):
421
 
        """Display information on the test just about to be run"""
422
 
 
423
 
    def _report_thread_leak(self, test, leaked_threads, active_threads):
424
 
        """Display information on a test that leaked one or more threads"""
425
 
        # GZ 2010-09-09: A leak summary reported separately from the general
426
 
        #                thread debugging would be nice. Tests under subunit
427
 
        #                need something not using stream, perhaps adding a
428
 
        #                testtools details object would be fitting.
429
 
        if 'threads' in selftest_debug_flags:
430
 
            self.stream.write('%s is leaking, active is now %d\n' %
431
 
                (test.id(), len(active_threads)))
 
404
    def report_cleaning_up(self):
 
405
        pass
432
406
 
433
407
    def startTestRun(self):
434
408
        self.startTime = time.time()
471
445
        self.pb.finished()
472
446
        super(TextTestResult, self).stopTestRun()
473
447
 
474
 
    def report_tests_starting(self):
475
 
        super(TextTestResult, self).report_tests_starting()
 
448
    def startTestRun(self):
 
449
        super(TextTestResult, self).startTestRun()
476
450
        self.pb.update('[test 0/%d] Starting' % (self.num_tests))
477
451
 
 
452
    def printErrors(self):
 
453
        # clear the pb to make room for the error listing
 
454
        self.pb.clear()
 
455
        super(TextTestResult, self).printErrors()
 
456
 
478
457
    def _progress_prefix_text(self):
479
458
        # the longer this text, the less space we have to show the test
480
459
        # name...
502
481
        return a
503
482
 
504
483
    def report_test_start(self, test):
 
484
        self.count += 1
505
485
        self.pb.update(
506
486
                self._progress_prefix_text()
507
487
                + ' '
534
514
    def report_unsupported(self, test, feature):
535
515
        """test cannot be run because feature is missing."""
536
516
 
 
517
    def report_cleaning_up(self):
 
518
        self.pb.update('Cleaning up')
 
519
 
537
520
 
538
521
class VerboseTestResult(ExtendedTestResult):
539
522
    """Produce long output, with one line per test run plus times"""
546
529
            result = a_string
547
530
        return result.ljust(final_width)
548
531
 
549
 
    def report_tests_starting(self):
 
532
    def startTestRun(self):
 
533
        super(VerboseTestResult, self).startTestRun()
550
534
        self.stream.write('running %d tests...\n' % self.num_tests)
551
 
        super(VerboseTestResult, self).report_tests_starting()
552
535
 
553
536
    def report_test_start(self, test):
 
537
        self.count += 1
554
538
        name = self._shortened_test_description(test)
555
539
        width = osutils.terminal_width()
556
540
        if width is not None:
806
790
    routine, and to build and check bzr trees.
807
791
 
808
792
    In addition to the usual method of overriding tearDown(), this class also
809
 
    allows subclasses to register cleanup functions via addCleanup, which are
 
793
    allows subclasses to register functions into the _cleanups list, which is
810
794
    run in order as the object is torn down.  It's less likely this will be
811
795
    accidentally overlooked.
812
796
    """
813
797
 
 
798
    _active_threads = None
 
799
    _leaking_threads_tests = 0
 
800
    _first_thread_leaker_id = None
814
801
    _log_file = None
815
802
    # record lsprof data when performing benchmark calls.
816
803
    _gather_lsprof_in_benchmarks = False
817
804
 
818
805
    def __init__(self, methodName='testMethod'):
819
806
        super(TestCase, self).__init__(methodName)
 
807
        self._cleanups = []
820
808
        self._directory_isolation = True
821
809
        self.exception_handlers.insert(0,
822
810
            (UnavailableFeature, self._do_unsupported_or_skip))
840
828
        self._track_transports()
841
829
        self._track_locks()
842
830
        self._clear_debug_flags()
 
831
        TestCase._active_threads = threading.activeCount()
 
832
        self.addCleanup(self._check_leaked_threads)
843
833
 
844
834
    def debug(self):
845
835
        # debug a frame up.
846
836
        import pdb
847
837
        pdb.Pdb().set_trace(sys._getframe().f_back)
848
838
 
849
 
    def discardDetail(self, name):
850
 
        """Extend the addDetail, getDetails api so we can remove a detail.
851
 
 
852
 
        eg. bzr always adds the 'log' detail at startup, but we don't want to
853
 
        include it for skipped, xfail, etc tests.
854
 
 
855
 
        It is safe to call this for a detail that doesn't exist, in case this
856
 
        gets called multiple times.
857
 
        """
858
 
        # We cheat. details is stored in __details which means we shouldn't
859
 
        # touch it. but getDetails() returns the dict directly, so we can
860
 
        # mutate it.
861
 
        details = self.getDetails()
862
 
        if name in details:
863
 
            del details[name]
 
839
    def _check_leaked_threads(self):
 
840
        active = threading.activeCount()
 
841
        leaked_threads = active - TestCase._active_threads
 
842
        TestCase._active_threads = active
 
843
        # If some tests make the number of threads *decrease*, we'll consider
 
844
        # that they are just observing old threads dieing, not agressively kill
 
845
        # random threads. So we don't report these tests as leaking. The risk
 
846
        # is that we have false positives that way (the test see 2 threads
 
847
        # going away but leak one) but it seems less likely than the actual
 
848
        # false positives (the test see threads going away and does not leak).
 
849
        if leaked_threads > 0:
 
850
            if 'threads' in selftest_debug_flags:
 
851
                print '%s is leaking, active is now %d' % (self.id(), active)
 
852
            TestCase._leaking_threads_tests += 1
 
853
            if TestCase._first_thread_leaker_id is None:
 
854
                TestCase._first_thread_leaker_id = self.id()
864
855
 
865
856
    def _clear_debug_flags(self):
866
857
        """Prevent externally set debug flags affecting tests.
984
975
            try:
985
976
                workingtree.WorkingTree.open(path)
986
977
            except (errors.NotBranchError, errors.NoWorkingTree):
987
 
                raise TestSkipped('Needs a working tree of bzr sources')
 
978
                return
988
979
        finally:
989
980
            self.enable_directory_isolation()
990
981
 
1496
1487
        """
1497
1488
        debug.debug_flags.discard('strict_locks')
1498
1489
 
 
1490
    def addCleanup(self, callable, *args, **kwargs):
 
1491
        """Arrange to run a callable when this case is torn down.
 
1492
 
 
1493
        Callables are run in the reverse of the order they are registered,
 
1494
        ie last-in first-out.
 
1495
        """
 
1496
        self._cleanups.append((callable, args, kwargs))
 
1497
 
1499
1498
    def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
1500
1499
        """Overrides an object attribute restoring it after the test.
1501
1500
 
1585
1584
        """This test has failed for some known reason."""
1586
1585
        raise KnownFailure(reason)
1587
1586
 
1588
 
    def _suppress_log(self):
1589
 
        """Remove the log info from details."""
1590
 
        self.discardDetail('log')
1591
 
 
1592
1587
    def _do_skip(self, result, reason):
1593
 
        self._suppress_log()
1594
1588
        addSkip = getattr(result, 'addSkip', None)
1595
1589
        if not callable(addSkip):
1596
1590
            result.addSuccess(result)
1599
1593
 
1600
1594
    @staticmethod
1601
1595
    def _do_known_failure(self, result, e):
1602
 
        self._suppress_log()
1603
1596
        err = sys.exc_info()
1604
1597
        addExpectedFailure = getattr(result, 'addExpectedFailure', None)
1605
1598
        if addExpectedFailure is not None:
1613
1606
            reason = 'No reason given'
1614
1607
        else:
1615
1608
            reason = e.args[0]
1616
 
        self._suppress_log ()
1617
1609
        addNotApplicable = getattr(result, 'addNotApplicable', None)
1618
1610
        if addNotApplicable is not None:
1619
1611
            result.addNotApplicable(self, reason)
1621
1613
            self._do_skip(result, reason)
1622
1614
 
1623
1615
    @staticmethod
1624
 
    def _report_skip(self, result, err):
1625
 
        """Override the default _report_skip.
1626
 
 
1627
 
        We want to strip the 'log' detail. If we waint until _do_skip, it has
1628
 
        already been formatted into the 'reason' string, and we can't pull it
1629
 
        out again.
1630
 
        """
1631
 
        self._suppress_log()
1632
 
        super(TestCase, self)._report_skip(self, result, err)
1633
 
 
1634
 
    @staticmethod
1635
 
    def _report_expected_failure(self, result, err):
1636
 
        """Strip the log.
1637
 
 
1638
 
        See _report_skip for motivation.
1639
 
        """
1640
 
        self._suppress_log()
1641
 
        super(TestCase, self)._report_expected_failure(self, result, err)
1642
 
 
1643
 
    @staticmethod
1644
1616
    def _do_unsupported_or_skip(self, result, e):
1645
1617
        reason = e.args[0]
1646
 
        self._suppress_log()
1647
1618
        addNotSupported = getattr(result, 'addNotSupported', None)
1648
1619
        if addNotSupported is not None:
1649
1620
            result.addNotSupported(self, reason)
2999
2970
 
3000
2971
 
3001
2972
def fork_decorator(suite):
3002
 
    if getattr(os, "fork", None) is None:
3003
 
        raise errors.BzrCommandError("platform does not support fork,"
3004
 
            " try --parallel=subprocess instead.")
3005
2973
    concurrency = osutils.local_concurrency()
3006
2974
    if concurrency == 1:
3007
2975
        return suite
4060
4028
    """
4061
4029
    new_test = copy.copy(test)
4062
4030
    new_test.id = lambda: new_id
4063
 
    # XXX: Workaround <https://bugs.launchpad.net/testtools/+bug/637725>, which
4064
 
    # causes cloned tests to share the 'details' dict.  This makes it hard to
4065
 
    # read the test output for parameterized tests, because tracebacks will be
4066
 
    # associated with irrelevant tests.
4067
 
    try:
4068
 
        details = new_test._TestCase__details
4069
 
    except AttributeError:
4070
 
        # must be a different version of testtools than expected.  Do nothing.
4071
 
        pass
4072
 
    else:
4073
 
        # Reset the '__details' dict.
4074
 
        new_test._TestCase__details = {}
4075
4031
    return new_test
4076
4032
 
4077
4033
 
4503
4459
try:
4504
4460
    from subunit import TestProtocolClient
4505
4461
    from subunit.test_results import AutoTimingTestResultDecorator
4506
 
    class SubUnitBzrProtocolClient(TestProtocolClient):
4507
 
 
4508
 
        def addSuccess(self, test, details=None):
4509
 
            # The subunit client always includes the details in the subunit
4510
 
            # stream, but we don't want to include it in ours.
4511
 
            if details is not None and 'log' in details:
4512
 
                del details['log']
4513
 
            return super(SubUnitBzrProtocolClient, self).addSuccess(
4514
 
                test, details)
4515
 
 
4516
4462
    class SubUnitBzrRunner(TextTestRunner):
4517
4463
        def run(self, test):
4518
4464
            result = AutoTimingTestResultDecorator(
4519
 
                SubUnitBzrProtocolClient(self.stream))
 
4465
                TestProtocolClient(self.stream))
4520
4466
            test.run(result)
4521
4467
            return result
4522
4468
except ImportError: