~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: Vincent Ladeuil
  • Date: 2010-10-07 06:08:01 UTC
  • mto: This revision was merged to the branch mainline in revision 5491.
  • Revision ID: v.ladeuil+lp@free.fr-20101007060801-wfdhizfhfmctl8qa
Fix some typos and propose a release planning.

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
40
39
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,
75
74
    ui,
76
75
    urlutils,
77
76
    registry,
135
134
SUBUNIT_SEEK_SET = 0
136
135
SUBUNIT_SEEK_CUR = 1
137
136
 
 
137
# These are intentionally brought into this namespace. That way plugins, etc
 
138
# can just "from bzrlib.tests import TestCase, TestLoader, etc"
 
139
TestSuite = TestUtil.TestSuite
 
140
TestLoader = TestUtil.TestLoader
138
141
 
139
142
class ExtendedTestResult(testtools.TextTestResult):
140
143
    """Accepts, reports and accumulates the results of running tests.
190
193
        self.count = 0
191
194
        self._overall_start_time = time.time()
192
195
        self._strict = strict
 
196
        self._first_thread_leaker_id = None
 
197
        self._tests_leaking_threads_count = 0
193
198
 
194
199
    def stopTestRun(self):
195
200
        run = self.testsRun
234
239
            ok = self.wasStrictlySuccessful()
235
240
        else:
236
241
            ok = self.wasSuccessful()
237
 
        if TestCase._first_thread_leaker_id:
 
242
        if self._first_thread_leaker_id:
238
243
            self.stream.write(
239
244
                '%s is leaking threads among %d leaking tests.\n' % (
240
 
                TestCase._first_thread_leaker_id,
241
 
                TestCase._leaking_threads_tests))
 
245
                self._first_thread_leaker_id,
 
246
                self._tests_leaking_threads_count))
242
247
            # We don't report the main thread as an active one.
243
248
            self.stream.write(
244
249
                '%d non-main threads were left active in the end.\n'
245
 
                % (TestCase._active_threads - 1))
 
250
                % (len(self._active_threads) - 1))
246
251
 
247
252
    def getDescription(self, test):
248
253
        return test.id()
279
284
        super(ExtendedTestResult, self).startTest(test)
280
285
        if self.count == 0:
281
286
            self.startTests()
 
287
        self.count += 1
282
288
        self.report_test_start(test)
283
289
        test.number = self.count
284
290
        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)
285
295
 
286
296
    def startTests(self):
287
 
        import platform
288
 
        if getattr(sys, 'frozen', None) is None:
289
 
            bzr_path = osutils.realpath(sys.argv[0])
290
 
        else:
291
 
            bzr_path = sys.executable
292
 
        self.stream.write(
293
 
            'bzr selftest: %s\n' % (bzr_path,))
294
 
        self.stream.write(
295
 
            '   %s\n' % (
296
 
                    bzrlib.__path__[0],))
297
 
        self.stream.write(
298
 
            '   bzr-%s python-%s %s\n' % (
299
 
                    bzrlib.version_string,
300
 
                    bzrlib._format_version_tuple(sys.version_info),
301
 
                    platform.platform(aliased=1),
302
 
                    ))
303
 
        self.stream.write('\n')
 
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
304
315
 
305
316
    def _recordTestStartTime(self):
306
317
        """Record that a test has started."""
307
318
        self._start_time = time.time()
308
319
 
309
 
    def _cleanupLogFile(self, test):
310
 
        # We can only do this if we have one of our TestCases, not if
311
 
        # we have a doctest.
312
 
        setKeepLogfile = getattr(test, 'setKeepLogfile', None)
313
 
        if setKeepLogfile is not None:
314
 
            setKeepLogfile()
315
 
 
316
320
    def addError(self, test, err):
317
321
        """Tell result that test finished with an error.
318
322
 
325
329
        self.report_error(test, err)
326
330
        if self.stop_early:
327
331
            self.stop()
328
 
        self._cleanupLogFile(test)
329
332
 
330
333
    def addFailure(self, test, err):
331
334
        """Tell result that test failed.
339
342
        self.report_failure(test, err)
340
343
        if self.stop_early:
341
344
            self.stop()
342
 
        self._cleanupLogFile(test)
343
345
 
344
346
    def addSuccess(self, test, details=None):
345
347
        """Tell result that test completed successfully.
353
355
                    self._formatTime(benchmark_time),
354
356
                    test.id()))
355
357
        self.report_success(test)
356
 
        self._cleanupLogFile(test)
357
358
        super(ExtendedTestResult, self).addSuccess(test)
358
359
        test._log_contents = ''
359
360
 
397
398
        else:
398
399
            raise errors.BzrError("Unknown whence %r" % whence)
399
400
 
400
 
    def report_cleaning_up(self):
401
 
        pass
 
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)))
402
432
 
403
433
    def startTestRun(self):
404
434
        self.startTime = time.time()
441
471
        self.pb.finished()
442
472
        super(TextTestResult, self).stopTestRun()
443
473
 
444
 
    def startTestRun(self):
445
 
        super(TextTestResult, self).startTestRun()
 
474
    def report_tests_starting(self):
 
475
        super(TextTestResult, self).report_tests_starting()
446
476
        self.pb.update('[test 0/%d] Starting' % (self.num_tests))
447
477
 
448
 
    def printErrors(self):
449
 
        # clear the pb to make room for the error listing
450
 
        self.pb.clear()
451
 
        super(TextTestResult, self).printErrors()
452
 
 
453
478
    def _progress_prefix_text(self):
454
479
        # the longer this text, the less space we have to show the test
455
480
        # name...
477
502
        return a
478
503
 
479
504
    def report_test_start(self, test):
480
 
        self.count += 1
481
505
        self.pb.update(
482
506
                self._progress_prefix_text()
483
507
                + ' '
510
534
    def report_unsupported(self, test, feature):
511
535
        """test cannot be run because feature is missing."""
512
536
 
513
 
    def report_cleaning_up(self):
514
 
        self.pb.update('Cleaning up')
515
 
 
516
537
 
517
538
class VerboseTestResult(ExtendedTestResult):
518
539
    """Produce long output, with one line per test run plus times"""
525
546
            result = a_string
526
547
        return result.ljust(final_width)
527
548
 
528
 
    def startTestRun(self):
529
 
        super(VerboseTestResult, self).startTestRun()
 
549
    def report_tests_starting(self):
530
550
        self.stream.write('running %d tests...\n' % self.num_tests)
 
551
        super(VerboseTestResult, self).report_tests_starting()
531
552
 
532
553
    def report_test_start(self, test):
533
 
        self.count += 1
534
554
        name = self._shortened_test_description(test)
535
555
        width = osutils.terminal_width()
536
556
        if width is not None:
786
806
    routine, and to build and check bzr trees.
787
807
 
788
808
    In addition to the usual method of overriding tearDown(), this class also
789
 
    allows subclasses to register functions into the _cleanups list, which is
 
809
    allows subclasses to register cleanup functions via addCleanup, which are
790
810
    run in order as the object is torn down.  It's less likely this will be
791
811
    accidentally overlooked.
792
812
    """
793
813
 
794
 
    _active_threads = None
795
 
    _leaking_threads_tests = 0
796
 
    _first_thread_leaker_id = None
797
 
    _log_file_name = None
 
814
    _log_file = None
798
815
    # record lsprof data when performing benchmark calls.
799
816
    _gather_lsprof_in_benchmarks = False
800
817
 
801
818
    def __init__(self, methodName='testMethod'):
802
819
        super(TestCase, self).__init__(methodName)
803
 
        self._cleanups = []
804
820
        self._directory_isolation = True
805
821
        self.exception_handlers.insert(0,
806
822
            (UnavailableFeature, self._do_unsupported_or_skip))
824
840
        self._track_transports()
825
841
        self._track_locks()
826
842
        self._clear_debug_flags()
827
 
        TestCase._active_threads = threading.activeCount()
828
 
        self.addCleanup(self._check_leaked_threads)
829
843
 
830
844
    def debug(self):
831
845
        # debug a frame up.
832
846
        import pdb
833
847
        pdb.Pdb().set_trace(sys._getframe().f_back)
834
848
 
835
 
    def _check_leaked_threads(self):
836
 
        active = threading.activeCount()
837
 
        leaked_threads = active - TestCase._active_threads
838
 
        TestCase._active_threads = active
839
 
        # If some tests make the number of threads *decrease*, we'll consider
840
 
        # that they are just observing old threads dieing, not agressively kill
841
 
        # random threads. So we don't report these tests as leaking. The risk
842
 
        # is that we have false positives that way (the test see 2 threads
843
 
        # going away but leak one) but it seems less likely than the actual
844
 
        # false positives (the test see threads going away and does not leak).
845
 
        if leaked_threads > 0:
846
 
            if 'threads' in selftest_debug_flags:
847
 
                print '%s is leaking, active is now %d' % (self.id(), active)
848
 
            TestCase._leaking_threads_tests += 1
849
 
            if TestCase._first_thread_leaker_id is None:
850
 
                TestCase._first_thread_leaker_id = self.id()
 
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]
851
864
 
852
865
    def _clear_debug_flags(self):
853
866
        """Prevent externally set debug flags affecting tests.
1454
1467
 
1455
1468
        The file is removed as the test is torn down.
1456
1469
        """
1457
 
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
1458
 
        self._log_file = os.fdopen(fileno, 'w+')
 
1470
        self._log_file = StringIO()
1459
1471
        self._log_memento = bzrlib.trace.push_log_file(self._log_file)
1460
 
        self._log_file_name = name
1461
1472
        self.addCleanup(self._finishLogFile)
1462
1473
 
1463
1474
    def _finishLogFile(self):
1485
1496
        """
1486
1497
        debug.debug_flags.discard('strict_locks')
1487
1498
 
1488
 
    def addCleanup(self, callable, *args, **kwargs):
1489
 
        """Arrange to run a callable when this case is torn down.
1490
 
 
1491
 
        Callables are run in the reverse of the order they are registered,
1492
 
        ie last-in first-out.
1493
 
        """
1494
 
        self._cleanups.append((callable, args, kwargs))
1495
 
 
1496
1499
    def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
1497
1500
        """Overrides an object attribute restoring it after the test.
1498
1501
 
1582
1585
        """This test has failed for some known reason."""
1583
1586
        raise KnownFailure(reason)
1584
1587
 
 
1588
    def _suppress_log(self):
 
1589
        """Remove the log info from details."""
 
1590
        self.discardDetail('log')
 
1591
 
1585
1592
    def _do_skip(self, result, reason):
 
1593
        self._suppress_log()
1586
1594
        addSkip = getattr(result, 'addSkip', None)
1587
1595
        if not callable(addSkip):
1588
1596
            result.addSuccess(result)
1591
1599
 
1592
1600
    @staticmethod
1593
1601
    def _do_known_failure(self, result, e):
 
1602
        self._suppress_log()
1594
1603
        err = sys.exc_info()
1595
1604
        addExpectedFailure = getattr(result, 'addExpectedFailure', None)
1596
1605
        if addExpectedFailure is not None:
1604
1613
            reason = 'No reason given'
1605
1614
        else:
1606
1615
            reason = e.args[0]
 
1616
        self._suppress_log ()
1607
1617
        addNotApplicable = getattr(result, 'addNotApplicable', None)
1608
1618
        if addNotApplicable is not None:
1609
1619
            result.addNotApplicable(self, reason)
1611
1621
            self._do_skip(result, reason)
1612
1622
 
1613
1623
    @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
1614
1644
    def _do_unsupported_or_skip(self, result, e):
1615
1645
        reason = e.args[0]
 
1646
        self._suppress_log()
1616
1647
        addNotSupported = getattr(result, 'addNotSupported', None)
1617
1648
        if addNotSupported is not None:
1618
1649
            result.addNotSupported(self, reason)
1665
1696
                unicodestr = self._log_contents.decode('utf8', 'replace')
1666
1697
                self._log_contents = unicodestr.encode('utf8')
1667
1698
            return self._log_contents
1668
 
        import bzrlib.trace
1669
 
        if bzrlib.trace._trace_file:
1670
 
            # flush the log file, to get all content
1671
 
            bzrlib.trace._trace_file.flush()
1672
 
        if self._log_file_name is not None:
1673
 
            logfile = open(self._log_file_name)
1674
 
            try:
1675
 
                log_contents = logfile.read()
1676
 
            finally:
1677
 
                logfile.close()
 
1699
        if self._log_file is not None:
 
1700
            log_contents = self._log_file.getvalue()
1678
1701
            try:
1679
1702
                log_contents.decode('utf8')
1680
1703
            except UnicodeDecodeError:
1681
1704
                unicodestr = log_contents.decode('utf8', 'replace')
1682
1705
                log_contents = unicodestr.encode('utf8')
1683
1706
            if not keep_log_file:
1684
 
                close_attempts = 0
1685
 
                max_close_attempts = 100
1686
 
                first_close_error = None
1687
 
                while close_attempts < max_close_attempts:
1688
 
                    close_attempts += 1
1689
 
                    try:
1690
 
                        self._log_file.close()
1691
 
                    except IOError, ioe:
1692
 
                        if ioe.errno is None:
1693
 
                            # No errno implies 'close() called during
1694
 
                            # concurrent operation on the same file object', so
1695
 
                            # retry.  Probably a thread is trying to write to
1696
 
                            # the log file.
1697
 
                            if first_close_error is None:
1698
 
                                first_close_error = ioe
1699
 
                            continue
1700
 
                        raise
1701
 
                    else:
1702
 
                        break
1703
 
                if close_attempts > 1:
1704
 
                    sys.stderr.write(
1705
 
                        'Unable to close log file on first attempt, '
1706
 
                        'will retry: %s\n' % (first_close_error,))
1707
 
                    if close_attempts == max_close_attempts:
1708
 
                        sys.stderr.write(
1709
 
                            'Unable to close log file after %d attempts.\n'
1710
 
                            % (max_close_attempts,))
1711
1707
                self._log_file = None
1712
1708
                # Permit multiple calls to get_log until we clean it up in
1713
1709
                # finishLogFile
1714
1710
                self._log_contents = log_contents
1715
 
                try:
1716
 
                    os.remove(self._log_file_name)
1717
 
                except OSError, e:
1718
 
                    if sys.platform == 'win32' and e.errno == errno.EACCES:
1719
 
                        sys.stderr.write(('Unable to delete log file '
1720
 
                                             ' %r\n' % self._log_file_name))
1721
 
                    else:
1722
 
                        raise
1723
 
                self._log_file_name = None
1724
1711
            return log_contents
1725
1712
        else:
1726
 
            return "No log file content and no log file name."
 
1713
            return "No log file content."
1727
1714
 
1728
1715
    def get_log(self):
1729
1716
        """Get a unicode string containing the log from bzrlib.trace.
1944
1931
            variables. A value of None will unset the env variable.
1945
1932
            The values must be strings. The change will only occur in the
1946
1933
            child, so you don't need to fix the environment after running.
1947
 
        :param skip_if_plan_to_signal: raise TestSkipped when true and os.kill
1948
 
            is not available.
 
1934
        :param skip_if_plan_to_signal: raise TestSkipped when true and system
 
1935
            doesn't support signalling subprocesses.
1949
1936
        :param allow_plugins: If False (default) pass --no-plugins to bzr.
1950
1937
 
1951
1938
        :returns: Popen object for the started process.
1952
1939
        """
1953
1940
        if skip_if_plan_to_signal:
1954
 
            if not getattr(os, 'kill', None):
1955
 
                raise TestSkipped("os.kill not available.")
 
1941
            if os.name != "posix":
 
1942
                raise TestSkipped("Sending signals not supported")
1956
1943
 
1957
1944
        if env_changes is None:
1958
1945
            env_changes = {}
3012
2999
 
3013
3000
 
3014
3001
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.")
3015
3005
    concurrency = osutils.local_concurrency()
3016
3006
    if concurrency == 1:
3017
3007
        return suite
3658
3648
        'bzrlib.tests.commands',
3659
3649
        'bzrlib.tests.doc_generate',
3660
3650
        'bzrlib.tests.per_branch',
 
3651
        'bzrlib.tests.per_bzrdir',
3661
3652
        'bzrlib.tests.per_controldir',
3662
3653
        'bzrlib.tests.per_controldir_colo',
3663
3654
        'bzrlib.tests.per_foreign_vcs',
4069
4060
    """
4070
4061
    new_test = copy.copy(test)
4071
4062
    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 = {}
4072
4075
    return new_test
4073
4076
 
4074
4077
 
4500
4503
try:
4501
4504
    from subunit import TestProtocolClient
4502
4505
    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
 
4503
4516
    class SubUnitBzrRunner(TextTestRunner):
4504
4517
        def run(self, test):
4505
4518
            result = AutoTimingTestResultDecorator(
4506
 
                TestProtocolClient(self.stream))
 
4519
                SubUnitBzrProtocolClient(self.stream))
4507
4520
            test.run(result)
4508
4521
            return result
4509
4522
except ImportError: