~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: John Arbash Meinel
  • Date: 2010-09-01 19:38:55 UTC
  • mto: This revision was merged to the branch mainline in revision 5405.
  • Revision ID: john@arbash-meinel.com-20100901193855-bazh9hh25cpm1986
Use the simpler form for the test server

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,
134
135
SUBUNIT_SEEK_SET = 0
135
136
SUBUNIT_SEEK_CUR = 1
136
137
 
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
141
138
 
142
139
class ExtendedTestResult(testtools.TextTestResult):
143
140
    """Accepts, reports and accumulates the results of running tests.
193
190
        self.count = 0
194
191
        self._overall_start_time = time.time()
195
192
        self._strict = strict
196
 
        self._first_thread_leaker_id = None
197
 
        self._tests_leaking_threads_count = 0
198
193
 
199
194
    def stopTestRun(self):
200
195
        run = self.testsRun
239
234
            ok = self.wasStrictlySuccessful()
240
235
        else:
241
236
            ok = self.wasSuccessful()
242
 
        if self._first_thread_leaker_id:
 
237
        if TestCase._first_thread_leaker_id:
243
238
            self.stream.write(
244
239
                '%s is leaking threads among %d leaking tests.\n' % (
245
 
                self._first_thread_leaker_id,
246
 
                self._tests_leaking_threads_count))
 
240
                TestCase._first_thread_leaker_id,
 
241
                TestCase._leaking_threads_tests))
247
242
            # We don't report the main thread as an active one.
248
243
            self.stream.write(
249
244
                '%d non-main threads were left active in the end.\n'
250
 
                % (len(self._active_threads) - 1))
 
245
                % (TestCase._active_threads - 1))
251
246
 
252
247
    def getDescription(self, test):
253
248
        return test.id()
284
279
        super(ExtendedTestResult, self).startTest(test)
285
280
        if self.count == 0:
286
281
            self.startTests()
287
 
        self.count += 1
288
282
        self.report_test_start(test)
289
283
        test.number = self.count
290
284
        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
285
 
296
286
    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
 
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')
315
304
 
316
305
    def _recordTestStartTime(self):
317
306
        """Record that a test has started."""
318
307
        self._start_time = time.time()
319
308
 
 
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
 
320
316
    def addError(self, test, err):
321
317
        """Tell result that test finished with an error.
322
318
 
329
325
        self.report_error(test, err)
330
326
        if self.stop_early:
331
327
            self.stop()
 
328
        self._cleanupLogFile(test)
332
329
 
333
330
    def addFailure(self, test, err):
334
331
        """Tell result that test failed.
342
339
        self.report_failure(test, err)
343
340
        if self.stop_early:
344
341
            self.stop()
 
342
        self._cleanupLogFile(test)
345
343
 
346
344
    def addSuccess(self, test, details=None):
347
345
        """Tell result that test completed successfully.
355
353
                    self._formatTime(benchmark_time),
356
354
                    test.id()))
357
355
        self.report_success(test)
 
356
        self._cleanupLogFile(test)
358
357
        super(ExtendedTestResult, self).addSuccess(test)
359
358
        test._log_contents = ''
360
359
 
398
397
        else:
399
398
            raise errors.BzrError("Unknown whence %r" % whence)
400
399
 
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)))
 
400
    def report_cleaning_up(self):
 
401
        pass
432
402
 
433
403
    def startTestRun(self):
434
404
        self.startTime = time.time()
471
441
        self.pb.finished()
472
442
        super(TextTestResult, self).stopTestRun()
473
443
 
474
 
    def report_tests_starting(self):
475
 
        super(TextTestResult, self).report_tests_starting()
 
444
    def startTestRun(self):
 
445
        super(TextTestResult, self).startTestRun()
476
446
        self.pb.update('[test 0/%d] Starting' % (self.num_tests))
477
447
 
 
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
 
478
453
    def _progress_prefix_text(self):
479
454
        # the longer this text, the less space we have to show the test
480
455
        # name...
502
477
        return a
503
478
 
504
479
    def report_test_start(self, test):
 
480
        self.count += 1
505
481
        self.pb.update(
506
482
                self._progress_prefix_text()
507
483
                + ' '
534
510
    def report_unsupported(self, test, feature):
535
511
        """test cannot be run because feature is missing."""
536
512
 
 
513
    def report_cleaning_up(self):
 
514
        self.pb.update('Cleaning up')
 
515
 
537
516
 
538
517
class VerboseTestResult(ExtendedTestResult):
539
518
    """Produce long output, with one line per test run plus times"""
546
525
            result = a_string
547
526
        return result.ljust(final_width)
548
527
 
549
 
    def report_tests_starting(self):
 
528
    def startTestRun(self):
 
529
        super(VerboseTestResult, self).startTestRun()
550
530
        self.stream.write('running %d tests...\n' % self.num_tests)
551
 
        super(VerboseTestResult, self).report_tests_starting()
552
531
 
553
532
    def report_test_start(self, test):
 
533
        self.count += 1
554
534
        name = self._shortened_test_description(test)
555
535
        width = osutils.terminal_width()
556
536
        if width is not None:
806
786
    routine, and to build and check bzr trees.
807
787
 
808
788
    In addition to the usual method of overriding tearDown(), this class also
809
 
    allows subclasses to register cleanup functions via addCleanup, which are
 
789
    allows subclasses to register functions into the _cleanups list, which is
810
790
    run in order as the object is torn down.  It's less likely this will be
811
791
    accidentally overlooked.
812
792
    """
813
793
 
814
 
    _log_file = None
 
794
    _active_threads = None
 
795
    _leaking_threads_tests = 0
 
796
    _first_thread_leaker_id = None
 
797
    _log_file_name = None
815
798
    # record lsprof data when performing benchmark calls.
816
799
    _gather_lsprof_in_benchmarks = False
817
800
 
818
801
    def __init__(self, methodName='testMethod'):
819
802
        super(TestCase, self).__init__(methodName)
 
803
        self._cleanups = []
820
804
        self._directory_isolation = True
821
805
        self.exception_handlers.insert(0,
822
806
            (UnavailableFeature, self._do_unsupported_or_skip))
840
824
        self._track_transports()
841
825
        self._track_locks()
842
826
        self._clear_debug_flags()
 
827
        TestCase._active_threads = threading.activeCount()
 
828
        self.addCleanup(self._check_leaked_threads)
843
829
 
844
830
    def debug(self):
845
831
        # debug a frame up.
846
832
        import pdb
847
833
        pdb.Pdb().set_trace(sys._getframe().f_back)
848
834
 
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]
 
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()
864
851
 
865
852
    def _clear_debug_flags(self):
866
853
        """Prevent externally set debug flags affecting tests.
984
971
            try:
985
972
                workingtree.WorkingTree.open(path)
986
973
            except (errors.NotBranchError, errors.NoWorkingTree):
987
 
                raise TestSkipped('Needs a working tree of bzr sources')
 
974
                return
988
975
        finally:
989
976
            self.enable_directory_isolation()
990
977
 
1467
1454
 
1468
1455
        The file is removed as the test is torn down.
1469
1456
        """
1470
 
        self._log_file = StringIO()
 
1457
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
 
1458
        self._log_file = os.fdopen(fileno, 'w+')
1471
1459
        self._log_memento = bzrlib.trace.push_log_file(self._log_file)
 
1460
        self._log_file_name = name
1472
1461
        self.addCleanup(self._finishLogFile)
1473
1462
 
1474
1463
    def _finishLogFile(self):
1496
1485
        """
1497
1486
        debug.debug_flags.discard('strict_locks')
1498
1487
 
 
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
 
1499
1496
    def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
1500
1497
        """Overrides an object attribute restoring it after the test.
1501
1498
 
1585
1582
        """This test has failed for some known reason."""
1586
1583
        raise KnownFailure(reason)
1587
1584
 
1588
 
    def _suppress_log(self):
1589
 
        """Remove the log info from details."""
1590
 
        self.discardDetail('log')
1591
 
 
1592
1585
    def _do_skip(self, result, reason):
1593
 
        self._suppress_log()
1594
1586
        addSkip = getattr(result, 'addSkip', None)
1595
1587
        if not callable(addSkip):
1596
1588
            result.addSuccess(result)
1599
1591
 
1600
1592
    @staticmethod
1601
1593
    def _do_known_failure(self, result, e):
1602
 
        self._suppress_log()
1603
1594
        err = sys.exc_info()
1604
1595
        addExpectedFailure = getattr(result, 'addExpectedFailure', None)
1605
1596
        if addExpectedFailure is not None:
1613
1604
            reason = 'No reason given'
1614
1605
        else:
1615
1606
            reason = e.args[0]
1616
 
        self._suppress_log ()
1617
1607
        addNotApplicable = getattr(result, 'addNotApplicable', None)
1618
1608
        if addNotApplicable is not None:
1619
1609
            result.addNotApplicable(self, reason)
1621
1611
            self._do_skip(result, reason)
1622
1612
 
1623
1613
    @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
1614
    def _do_unsupported_or_skip(self, result, e):
1645
1615
        reason = e.args[0]
1646
 
        self._suppress_log()
1647
1616
        addNotSupported = getattr(result, 'addNotSupported', None)
1648
1617
        if addNotSupported is not None:
1649
1618
            result.addNotSupported(self, reason)
1696
1665
                unicodestr = self._log_contents.decode('utf8', 'replace')
1697
1666
                self._log_contents = unicodestr.encode('utf8')
1698
1667
            return self._log_contents
1699
 
        if self._log_file is not None:
1700
 
            log_contents = self._log_file.getvalue()
 
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()
1701
1678
            try:
1702
1679
                log_contents.decode('utf8')
1703
1680
            except UnicodeDecodeError:
1704
1681
                unicodestr = log_contents.decode('utf8', 'replace')
1705
1682
                log_contents = unicodestr.encode('utf8')
1706
1683
            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,))
1707
1711
                self._log_file = None
1708
1712
                # Permit multiple calls to get_log until we clean it up in
1709
1713
                # finishLogFile
1710
1714
                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
1711
1724
            return log_contents
1712
1725
        else:
1713
 
            return "No log file content."
 
1726
            return "No log file content and no log file name."
1714
1727
 
1715
1728
    def get_log(self):
1716
1729
        """Get a unicode string containing the log from bzrlib.trace.
1931
1944
            variables. A value of None will unset the env variable.
1932
1945
            The values must be strings. The change will only occur in the
1933
1946
            child, so you don't need to fix the environment after running.
1934
 
        :param skip_if_plan_to_signal: raise TestSkipped when true and system
1935
 
            doesn't support signalling subprocesses.
 
1947
        :param skip_if_plan_to_signal: raise TestSkipped when true and os.kill
 
1948
            is not available.
1936
1949
        :param allow_plugins: If False (default) pass --no-plugins to bzr.
1937
1950
 
1938
1951
        :returns: Popen object for the started process.
1939
1952
        """
1940
1953
        if skip_if_plan_to_signal:
1941
 
            if os.name != "posix":
1942
 
                raise TestSkipped("Sending signals not supported")
 
1954
            if not getattr(os, 'kill', None):
 
1955
                raise TestSkipped("os.kill not available.")
1943
1956
 
1944
1957
        if env_changes is None:
1945
1958
            env_changes = {}
2999
3012
 
3000
3013
 
3001
3014
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
3015
    concurrency = osutils.local_concurrency()
3006
3016
    if concurrency == 1:
3007
3017
        return suite
3648
3658
        'bzrlib.tests.commands',
3649
3659
        'bzrlib.tests.doc_generate',
3650
3660
        'bzrlib.tests.per_branch',
3651
 
        'bzrlib.tests.per_bzrdir',
3652
3661
        'bzrlib.tests.per_controldir',
3653
3662
        'bzrlib.tests.per_controldir_colo',
3654
3663
        'bzrlib.tests.per_foreign_vcs',
4060
4069
    """
4061
4070
    new_test = copy.copy(test)
4062
4071
    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
4072
    return new_test
4076
4073
 
4077
4074
 
4503
4500
try:
4504
4501
    from subunit import TestProtocolClient
4505
4502
    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
4503
    class SubUnitBzrRunner(TextTestRunner):
4517
4504
        def run(self, test):
4518
4505
            result = AutoTimingTestResultDecorator(
4519
 
                SubUnitBzrProtocolClient(self.stream))
 
4506
                TestProtocolClient(self.stream))
4520
4507
            test.run(result)
4521
4508
            return result
4522
4509
except ImportError: