~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: Jelmer Vernooij
  • Date: 2010-12-20 11:57:14 UTC
  • mto: This revision was merged to the branch mainline in revision 5577.
  • Revision ID: jelmer@samba.org-20101220115714-2ru3hfappjweeg7q
Don't use no-plugins.

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
55
55
import testtools
56
56
# nb: check this before importing anything else from within it
57
57
_testtools_version = getattr(testtools, '__version__', ())
58
 
if _testtools_version < (0, 9, 2):
59
 
    raise ImportError("need at least testtools 0.9.2: %s is %r"
 
58
if _testtools_version < (0, 9, 5):
 
59
    raise ImportError("need at least testtools 0.9.5: %s is %r"
60
60
        % (testtools.__file__, _testtools_version))
61
61
from testtools import content
62
62
 
71
71
    lock as _mod_lock,
72
72
    memorytree,
73
73
    osutils,
74
 
    progress,
 
74
    pyutils,
75
75
    ui,
76
76
    urlutils,
77
77
    registry,
90
90
except ImportError:
91
91
    # lsprof not available
92
92
    pass
93
 
from bzrlib.merge import merge_inner
94
93
import bzrlib.merge3
95
94
import bzrlib.plugin
96
 
from bzrlib.smart import client, request, server
 
95
from bzrlib.smart import client, request
97
96
import bzrlib.store
98
97
from bzrlib import symbol_versioning
99
98
from bzrlib.symbol_versioning import (
117
116
from bzrlib.ui import NullProgressView
118
117
from bzrlib.ui.text import TextUIFactory
119
118
import bzrlib.version_info_formats.format_custom
120
 
from bzrlib.workingtree import WorkingTree, WorkingTreeFormat2
121
119
 
122
120
# Mark this python module as being part of the implementation
123
121
# of unittest: this gives us better tracebacks where the last
135
133
SUBUNIT_SEEK_SET = 0
136
134
SUBUNIT_SEEK_CUR = 1
137
135
 
 
136
# These are intentionally brought into this namespace. That way plugins, etc
 
137
# can just "from bzrlib.tests import TestCase, TestLoader, etc"
 
138
TestSuite = TestUtil.TestSuite
 
139
TestLoader = TestUtil.TestLoader
138
140
 
139
141
class ExtendedTestResult(testtools.TextTestResult):
140
142
    """Accepts, reports and accumulates the results of running tests.
190
192
        self.count = 0
191
193
        self._overall_start_time = time.time()
192
194
        self._strict = strict
 
195
        self._first_thread_leaker_id = None
 
196
        self._tests_leaking_threads_count = 0
 
197
        self._traceback_from_test = None
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()
255
260
 
256
261
    def _elapsedTestTimeString(self):
257
262
        """Return a time string for the overall time the current test has taken."""
258
 
        return self._formatTime(time.time() - self._start_time)
 
263
        return self._formatTime(self._delta_to_float(
 
264
            self._now() - self._start_datetime))
259
265
 
260
266
    def _testTimeString(self, testCase):
261
267
        benchmark_time = self._extractBenchmarkTime(testCase)
275
281
        what = re.sub(r'^bzrlib\.tests\.', '', what)
276
282
        return what
277
283
 
 
284
    # GZ 2010-10-04: Cloned tests may end up harmlessly calling this method
 
285
    #                multiple times in a row, because the handler is added for
 
286
    #                each test but the container list is shared between cases.
 
287
    #                See lp:498869 lp:625574 and lp:637725 for background.
 
288
    def _record_traceback_from_test(self, exc_info):
 
289
        """Store the traceback from passed exc_info tuple till"""
 
290
        self._traceback_from_test = exc_info[2]
 
291
 
278
292
    def startTest(self, test):
279
293
        super(ExtendedTestResult, self).startTest(test)
280
294
        if self.count == 0:
281
295
            self.startTests()
 
296
        self.count += 1
282
297
        self.report_test_start(test)
283
298
        test.number = self.count
284
299
        self._recordTestStartTime()
 
300
        # Make testtools cases give us the real traceback on failure
 
301
        addOnException = getattr(test, "addOnException", None)
 
302
        if addOnException is not None:
 
303
            addOnException(self._record_traceback_from_test)
 
304
        # Only check for thread leaks if the test case supports cleanups
 
305
        addCleanup = getattr(test, "addCleanup", None)
 
306
        if addCleanup is not None:
 
307
            addCleanup(self._check_leaked_threads, test)
285
308
 
286
309
    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')
 
310
        self.report_tests_starting()
 
311
        self._active_threads = threading.enumerate()
 
312
 
 
313
    def stopTest(self, test):
 
314
        self._traceback_from_test = None
 
315
 
 
316
    def _check_leaked_threads(self, test):
 
317
        """See if any threads have leaked since last call
 
318
 
 
319
        A sample of live threads is stored in the _active_threads attribute,
 
320
        when this method runs it compares the current live threads and any not
 
321
        in the previous sample are treated as having leaked.
 
322
        """
 
323
        now_active_threads = set(threading.enumerate())
 
324
        threads_leaked = now_active_threads.difference(self._active_threads)
 
325
        if threads_leaked:
 
326
            self._report_thread_leak(test, threads_leaked, now_active_threads)
 
327
            self._tests_leaking_threads_count += 1
 
328
            if self._first_thread_leaker_id is None:
 
329
                self._first_thread_leaker_id = test.id()
 
330
            self._active_threads = now_active_threads
304
331
 
305
332
    def _recordTestStartTime(self):
306
333
        """Record that a test has started."""
307
 
        self._start_time = time.time()
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()
 
334
        self._start_datetime = self._now()
315
335
 
316
336
    def addError(self, test, err):
317
337
        """Tell result that test finished with an error.
319
339
        Called from the TestCase run() method when the test
320
340
        fails with an unexpected error.
321
341
        """
322
 
        self._post_mortem()
 
342
        self._post_mortem(self._traceback_from_test)
323
343
        super(ExtendedTestResult, self).addError(test, err)
324
344
        self.error_count += 1
325
345
        self.report_error(test, err)
326
346
        if self.stop_early:
327
347
            self.stop()
328
 
        self._cleanupLogFile(test)
329
348
 
330
349
    def addFailure(self, test, err):
331
350
        """Tell result that test failed.
333
352
        Called from the TestCase run() method when the test
334
353
        fails because e.g. an assert() method failed.
335
354
        """
336
 
        self._post_mortem()
 
355
        self._post_mortem(self._traceback_from_test)
337
356
        super(ExtendedTestResult, self).addFailure(test, err)
338
357
        self.failure_count += 1
339
358
        self.report_failure(test, err)
340
359
        if self.stop_early:
341
360
            self.stop()
342
 
        self._cleanupLogFile(test)
343
361
 
344
362
    def addSuccess(self, test, details=None):
345
363
        """Tell result that test completed successfully.
353
371
                    self._formatTime(benchmark_time),
354
372
                    test.id()))
355
373
        self.report_success(test)
356
 
        self._cleanupLogFile(test)
357
374
        super(ExtendedTestResult, self).addSuccess(test)
358
375
        test._log_contents = ''
359
376
 
383
400
        self.not_applicable_count += 1
384
401
        self.report_not_applicable(test, reason)
385
402
 
386
 
    def _post_mortem(self):
 
403
    def _post_mortem(self, tb=None):
387
404
        """Start a PDB post mortem session."""
388
405
        if os.environ.get('BZR_TEST_PDB', None):
389
 
            import pdb;pdb.post_mortem()
 
406
            import pdb
 
407
            pdb.post_mortem(tb)
390
408
 
391
409
    def progress(self, offset, whence):
392
410
        """The test is adjusting the count of tests to run."""
397
415
        else:
398
416
            raise errors.BzrError("Unknown whence %r" % whence)
399
417
 
400
 
    def report_cleaning_up(self):
401
 
        pass
 
418
    def report_tests_starting(self):
 
419
        """Display information before the test run begins"""
 
420
        if getattr(sys, 'frozen', None) is None:
 
421
            bzr_path = osutils.realpath(sys.argv[0])
 
422
        else:
 
423
            bzr_path = sys.executable
 
424
        self.stream.write(
 
425
            'bzr selftest: %s\n' % (bzr_path,))
 
426
        self.stream.write(
 
427
            '   %s\n' % (
 
428
                    bzrlib.__path__[0],))
 
429
        self.stream.write(
 
430
            '   bzr-%s python-%s %s\n' % (
 
431
                    bzrlib.version_string,
 
432
                    bzrlib._format_version_tuple(sys.version_info),
 
433
                    platform.platform(aliased=1),
 
434
                    ))
 
435
        self.stream.write('\n')
 
436
 
 
437
    def report_test_start(self, test):
 
438
        """Display information on the test just about to be run"""
 
439
 
 
440
    def _report_thread_leak(self, test, leaked_threads, active_threads):
 
441
        """Display information on a test that leaked one or more threads"""
 
442
        # GZ 2010-09-09: A leak summary reported separately from the general
 
443
        #                thread debugging would be nice. Tests under subunit
 
444
        #                need something not using stream, perhaps adding a
 
445
        #                testtools details object would be fitting.
 
446
        if 'threads' in selftest_debug_flags:
 
447
            self.stream.write('%s is leaking, active is now %d\n' %
 
448
                (test.id(), len(active_threads)))
402
449
 
403
450
    def startTestRun(self):
404
451
        self.startTime = time.time()
441
488
        self.pb.finished()
442
489
        super(TextTestResult, self).stopTestRun()
443
490
 
444
 
    def startTestRun(self):
445
 
        super(TextTestResult, self).startTestRun()
 
491
    def report_tests_starting(self):
 
492
        super(TextTestResult, self).report_tests_starting()
446
493
        self.pb.update('[test 0/%d] Starting' % (self.num_tests))
447
494
 
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
495
    def _progress_prefix_text(self):
454
496
        # the longer this text, the less space we have to show the test
455
497
        # name...
477
519
        return a
478
520
 
479
521
    def report_test_start(self, test):
480
 
        self.count += 1
481
522
        self.pb.update(
482
523
                self._progress_prefix_text()
483
524
                + ' '
510
551
    def report_unsupported(self, test, feature):
511
552
        """test cannot be run because feature is missing."""
512
553
 
513
 
    def report_cleaning_up(self):
514
 
        self.pb.update('Cleaning up')
515
 
 
516
554
 
517
555
class VerboseTestResult(ExtendedTestResult):
518
556
    """Produce long output, with one line per test run plus times"""
525
563
            result = a_string
526
564
        return result.ljust(final_width)
527
565
 
528
 
    def startTestRun(self):
529
 
        super(VerboseTestResult, self).startTestRun()
 
566
    def report_tests_starting(self):
530
567
        self.stream.write('running %d tests...\n' % self.num_tests)
 
568
        super(VerboseTestResult, self).report_tests_starting()
531
569
 
532
570
    def report_test_start(self, test):
533
 
        self.count += 1
534
571
        name = self._shortened_test_description(test)
535
572
        width = osutils.terminal_width()
536
573
        if width is not None:
614
651
            encode = codec[0]
615
652
        else:
616
653
            encode = codec.encode
617
 
        stream = osutils.UnicodeOrBytesToBytesWriter(encode, stream)
 
654
        # GZ 2010-09-08: Really we don't want to be writing arbitrary bytes,
 
655
        #                so should swap to the plain codecs.StreamWriter
 
656
        stream = osutils.UnicodeOrBytesToBytesWriter(encode, stream,
 
657
            "backslashreplace")
618
658
        stream.encoding = new_encoding
619
659
        self.stream = stream
620
660
        self.descriptions = descriptions
786
826
    routine, and to build and check bzr trees.
787
827
 
788
828
    In addition to the usual method of overriding tearDown(), this class also
789
 
    allows subclasses to register functions into the _cleanups list, which is
 
829
    allows subclasses to register cleanup functions via addCleanup, which are
790
830
    run in order as the object is torn down.  It's less likely this will be
791
831
    accidentally overlooked.
792
832
    """
793
833
 
794
 
    _active_threads = None
795
 
    _leaking_threads_tests = 0
796
 
    _first_thread_leaker_id = None
797
 
    _log_file_name = None
 
834
    _log_file = None
798
835
    # record lsprof data when performing benchmark calls.
799
836
    _gather_lsprof_in_benchmarks = False
800
837
 
801
838
    def __init__(self, methodName='testMethod'):
802
839
        super(TestCase, self).__init__(methodName)
803
 
        self._cleanups = []
804
840
        self._directory_isolation = True
805
841
        self.exception_handlers.insert(0,
806
842
            (UnavailableFeature, self._do_unsupported_or_skip))
824
860
        self._track_transports()
825
861
        self._track_locks()
826
862
        self._clear_debug_flags()
827
 
        TestCase._active_threads = threading.activeCount()
828
 
        self.addCleanup(self._check_leaked_threads)
 
863
        # Isolate global verbosity level, to make sure it's reproducible
 
864
        # between tests.  We should get rid of this altogether: bug 656694. --
 
865
        # mbp 20101008
 
866
        self.overrideAttr(bzrlib.trace, '_verbosity_level', 0)
829
867
 
830
868
    def debug(self):
831
869
        # debug a frame up.
832
870
        import pdb
833
871
        pdb.Pdb().set_trace(sys._getframe().f_back)
834
872
 
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()
 
873
    def discardDetail(self, name):
 
874
        """Extend the addDetail, getDetails api so we can remove a detail.
 
875
 
 
876
        eg. bzr always adds the 'log' detail at startup, but we don't want to
 
877
        include it for skipped, xfail, etc tests.
 
878
 
 
879
        It is safe to call this for a detail that doesn't exist, in case this
 
880
        gets called multiple times.
 
881
        """
 
882
        # We cheat. details is stored in __details which means we shouldn't
 
883
        # touch it. but getDetails() returns the dict directly, so we can
 
884
        # mutate it.
 
885
        details = self.getDetails()
 
886
        if name in details:
 
887
            del details[name]
851
888
 
852
889
    def _clear_debug_flags(self):
853
890
        """Prevent externally set debug flags affecting tests.
864
901
 
865
902
    def _clear_hooks(self):
866
903
        # prevent hooks affecting tests
 
904
        known_hooks = hooks.known_hooks
867
905
        self._preserved_hooks = {}
868
 
        for key, factory in hooks.known_hooks.items():
869
 
            parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
870
 
            current_hooks = hooks.known_hooks_key_to_object(key)
 
906
        for key, (parent, name) in known_hooks.iter_parent_objects():
 
907
            current_hooks = getattr(parent, name)
871
908
            self._preserved_hooks[parent] = (name, current_hooks)
872
909
        self.addCleanup(self._restoreHooks)
873
 
        for key, factory in hooks.known_hooks.items():
874
 
            parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
 
910
        for key, (parent, name) in known_hooks.iter_parent_objects():
 
911
            factory = known_hooks.get(key)
875
912
            setattr(parent, name, factory())
876
913
        # this hook should always be installed
877
914
        request._install_hook()
971
1008
            try:
972
1009
                workingtree.WorkingTree.open(path)
973
1010
            except (errors.NotBranchError, errors.NoWorkingTree):
974
 
                return
 
1011
                raise TestSkipped('Needs a working tree of bzr sources')
975
1012
        finally:
976
1013
            self.enable_directory_isolation()
977
1014
 
1454
1491
 
1455
1492
        The file is removed as the test is torn down.
1456
1493
        """
1457
 
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
1458
 
        self._log_file = os.fdopen(fileno, 'w+')
 
1494
        self._log_file = StringIO()
1459
1495
        self._log_memento = bzrlib.trace.push_log_file(self._log_file)
1460
 
        self._log_file_name = name
1461
1496
        self.addCleanup(self._finishLogFile)
1462
1497
 
1463
1498
    def _finishLogFile(self):
1485
1520
        """
1486
1521
        debug.debug_flags.discard('strict_locks')
1487
1522
 
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
1523
    def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
1497
1524
        """Overrides an object attribute restoring it after the test.
1498
1525
 
1582
1609
        """This test has failed for some known reason."""
1583
1610
        raise KnownFailure(reason)
1584
1611
 
 
1612
    def _suppress_log(self):
 
1613
        """Remove the log info from details."""
 
1614
        self.discardDetail('log')
 
1615
 
1585
1616
    def _do_skip(self, result, reason):
 
1617
        self._suppress_log()
1586
1618
        addSkip = getattr(result, 'addSkip', None)
1587
1619
        if not callable(addSkip):
1588
1620
            result.addSuccess(result)
1591
1623
 
1592
1624
    @staticmethod
1593
1625
    def _do_known_failure(self, result, e):
 
1626
        self._suppress_log()
1594
1627
        err = sys.exc_info()
1595
1628
        addExpectedFailure = getattr(result, 'addExpectedFailure', None)
1596
1629
        if addExpectedFailure is not None:
1604
1637
            reason = 'No reason given'
1605
1638
        else:
1606
1639
            reason = e.args[0]
 
1640
        self._suppress_log ()
1607
1641
        addNotApplicable = getattr(result, 'addNotApplicable', None)
1608
1642
        if addNotApplicable is not None:
1609
1643
            result.addNotApplicable(self, reason)
1611
1645
            self._do_skip(result, reason)
1612
1646
 
1613
1647
    @staticmethod
 
1648
    def _report_skip(self, result, err):
 
1649
        """Override the default _report_skip.
 
1650
 
 
1651
        We want to strip the 'log' detail. If we waint until _do_skip, it has
 
1652
        already been formatted into the 'reason' string, and we can't pull it
 
1653
        out again.
 
1654
        """
 
1655
        self._suppress_log()
 
1656
        super(TestCase, self)._report_skip(self, result, err)
 
1657
 
 
1658
    @staticmethod
 
1659
    def _report_expected_failure(self, result, err):
 
1660
        """Strip the log.
 
1661
 
 
1662
        See _report_skip for motivation.
 
1663
        """
 
1664
        self._suppress_log()
 
1665
        super(TestCase, self)._report_expected_failure(self, result, err)
 
1666
 
 
1667
    @staticmethod
1614
1668
    def _do_unsupported_or_skip(self, result, e):
1615
1669
        reason = e.args[0]
 
1670
        self._suppress_log()
1616
1671
        addNotSupported = getattr(result, 'addNotSupported', None)
1617
1672
        if addNotSupported is not None:
1618
1673
            result.addNotSupported(self, reason)
1665
1720
                unicodestr = self._log_contents.decode('utf8', 'replace')
1666
1721
                self._log_contents = unicodestr.encode('utf8')
1667
1722
            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()
 
1723
        if self._log_file is not None:
 
1724
            log_contents = self._log_file.getvalue()
1678
1725
            try:
1679
1726
                log_contents.decode('utf8')
1680
1727
            except UnicodeDecodeError:
1681
1728
                unicodestr = log_contents.decode('utf8', 'replace')
1682
1729
                log_contents = unicodestr.encode('utf8')
1683
1730
            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
1731
                self._log_file = None
1712
1732
                # Permit multiple calls to get_log until we clean it up in
1713
1733
                # finishLogFile
1714
1734
                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
1735
            return log_contents
1725
1736
        else:
1726
 
            return "No log file content and no log file name."
 
1737
            return "No log file content."
1727
1738
 
1728
1739
    def get_log(self):
1729
1740
        """Get a unicode string containing the log from bzrlib.trace.
1944
1955
            variables. A value of None will unset the env variable.
1945
1956
            The values must be strings. The change will only occur in the
1946
1957
            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.
 
1958
        :param skip_if_plan_to_signal: raise TestSkipped when true and system
 
1959
            doesn't support signalling subprocesses.
1949
1960
        :param allow_plugins: If False (default) pass --no-plugins to bzr.
1950
1961
 
1951
1962
        :returns: Popen object for the started process.
1952
1963
        """
1953
1964
        if skip_if_plan_to_signal:
1954
 
            if not getattr(os, 'kill', None):
1955
 
                raise TestSkipped("os.kill not available.")
 
1965
            if os.name != "posix":
 
1966
                raise TestSkipped("Sending signals not supported")
1956
1967
 
1957
1968
        if env_changes is None:
1958
1969
            env_changes = {}
2443
2454
                self.addCleanup(t.disconnect)
2444
2455
            return t
2445
2456
 
2446
 
        orig_get_transport = self.overrideAttr(_mod_transport, 'get_transport',
 
2457
        orig_get_transport = self.overrideAttr(_mod_transport, '_get_transport',
2447
2458
                                               get_transport_with_cleanup)
2448
2459
        self._make_test_root()
2449
2460
        self.addCleanup(os.chdir, os.getcwdu())
3012
3023
 
3013
3024
 
3014
3025
def fork_decorator(suite):
 
3026
    if getattr(os, "fork", None) is None:
 
3027
        raise errors.BzrCommandError("platform does not support fork,"
 
3028
            " try --parallel=subprocess instead.")
3015
3029
    concurrency = osutils.local_concurrency()
3016
3030
    if concurrency == 1:
3017
3031
        return suite
3341
3355
    return result
3342
3356
 
3343
3357
 
3344
 
class ForwardingResult(unittest.TestResult):
3345
 
 
3346
 
    def __init__(self, target):
3347
 
        unittest.TestResult.__init__(self)
3348
 
        self.result = target
3349
 
 
3350
 
    def startTest(self, test):
3351
 
        self.result.startTest(test)
3352
 
 
3353
 
    def stopTest(self, test):
3354
 
        self.result.stopTest(test)
3355
 
 
3356
 
    def startTestRun(self):
3357
 
        self.result.startTestRun()
3358
 
 
3359
 
    def stopTestRun(self):
3360
 
        self.result.stopTestRun()
3361
 
 
3362
 
    def addSkip(self, test, reason):
3363
 
        self.result.addSkip(test, reason)
3364
 
 
3365
 
    def addSuccess(self, test):
3366
 
        self.result.addSuccess(test)
3367
 
 
3368
 
    def addError(self, test, err):
3369
 
        self.result.addError(test, err)
3370
 
 
3371
 
    def addFailure(self, test, err):
3372
 
        self.result.addFailure(test, err)
3373
 
ForwardingResult = testtools.ExtendedToOriginalDecorator
3374
 
 
3375
 
 
3376
 
class ProfileResult(ForwardingResult):
 
3358
class ProfileResult(testtools.ExtendedToOriginalDecorator):
3377
3359
    """Generate profiling data for all activity between start and success.
3378
3360
    
3379
3361
    The profile data is appended to the test's _benchcalls attribute and can
3391
3373
        # unavoidably fail.
3392
3374
        bzrlib.lsprof.BzrProfiler.profiler_block = 0
3393
3375
        self.profiler.start()
3394
 
        ForwardingResult.startTest(self, test)
 
3376
        testtools.ExtendedToOriginalDecorator.startTest(self, test)
3395
3377
 
3396
3378
    def addSuccess(self, test):
3397
3379
        stats = self.profiler.stop()
3401
3383
            test._benchcalls = []
3402
3384
            calls = test._benchcalls
3403
3385
        calls.append(((test.id(), "", ""), stats))
3404
 
        ForwardingResult.addSuccess(self, test)
 
3386
        testtools.ExtendedToOriginalDecorator.addSuccess(self, test)
3405
3387
 
3406
3388
    def stopTest(self, test):
3407
 
        ForwardingResult.stopTest(self, test)
 
3389
        testtools.ExtendedToOriginalDecorator.stopTest(self, test)
3408
3390
        self.profiler = None
3409
3391
 
3410
3392
 
3658
3640
        'bzrlib.tests.commands',
3659
3641
        'bzrlib.tests.doc_generate',
3660
3642
        'bzrlib.tests.per_branch',
 
3643
        'bzrlib.tests.per_bzrdir',
3661
3644
        'bzrlib.tests.per_controldir',
3662
3645
        'bzrlib.tests.per_controldir_colo',
3663
3646
        'bzrlib.tests.per_foreign_vcs',
3782
3765
        'bzrlib.tests.test_permissions',
3783
3766
        'bzrlib.tests.test_plugins',
3784
3767
        'bzrlib.tests.test_progress',
 
3768
        'bzrlib.tests.test_pyutils',
3785
3769
        'bzrlib.tests.test_read_bundle',
3786
3770
        'bzrlib.tests.test_reconcile',
3787
3771
        'bzrlib.tests.test_reconfigure',
3796
3780
        'bzrlib.tests.test_rio',
3797
3781
        'bzrlib.tests.test_rules',
3798
3782
        'bzrlib.tests.test_sampler',
 
3783
        'bzrlib.tests.test_scenarios',
3799
3784
        'bzrlib.tests.test_script',
3800
3785
        'bzrlib.tests.test_selftest',
3801
3786
        'bzrlib.tests.test_serializer',
3857
3842
        return []
3858
3843
    return [
3859
3844
        'bzrlib',
3860
 
        'bzrlib.branchbuilder',
 
3845
        # FIXME: Fixing bug #690563 revealed an isolation problem in the single
 
3846
        # doctest for branchbuilder. Uncomment this when bug #321320 is fixed
 
3847
        # to ensure the issue is addressed (note that to reproduce the bug in
 
3848
        # the doctest below, one should comment the 'email' config var in
 
3849
        # bazaar.conf (or anywhere else). This means an setup where *no* user
 
3850
        # is being set at all in the environment.
 
3851
#       'bzrlib.branchbuilder',
3861
3852
        'bzrlib.decorators',
3862
3853
        'bzrlib.export',
3863
3854
        'bzrlib.inventory',
3865
3856
        'bzrlib.lockdir',
3866
3857
        'bzrlib.merge3',
3867
3858
        'bzrlib.option',
 
3859
        'bzrlib.pyutils',
3868
3860
        'bzrlib.symbol_versioning',
3869
3861
        'bzrlib.tests',
3870
3862
        'bzrlib.tests.fixtures',
3871
3863
        'bzrlib.timestamp',
 
3864
        'bzrlib.transport.http',
3872
3865
        'bzrlib.version_info_formats.format_custom',
3873
3866
        ]
3874
3867
 
3977
3970
    return suite
3978
3971
 
3979
3972
 
3980
 
def multiply_scenarios(scenarios_left, scenarios_right):
 
3973
def multiply_scenarios(*scenarios):
 
3974
    """Multiply two or more iterables of scenarios.
 
3975
 
 
3976
    It is safe to pass scenario generators or iterators.
 
3977
 
 
3978
    :returns: A list of compound scenarios: the cross-product of all 
 
3979
        scenarios, with the names concatenated and the parameters
 
3980
        merged together.
 
3981
    """
 
3982
    return reduce(_multiply_two_scenarios, map(list, scenarios))
 
3983
 
 
3984
 
 
3985
def _multiply_two_scenarios(scenarios_left, scenarios_right):
3981
3986
    """Multiply two sets of scenarios.
3982
3987
 
3983
3988
    :returns: the cartesian product of the two sets of scenarios, that is
4069
4074
    """
4070
4075
    new_test = copy.copy(test)
4071
4076
    new_test.id = lambda: new_id
 
4077
    # XXX: Workaround <https://bugs.launchpad.net/testtools/+bug/637725>, which
 
4078
    # causes cloned tests to share the 'details' dict.  This makes it hard to
 
4079
    # read the test output for parameterized tests, because tracebacks will be
 
4080
    # associated with irrelevant tests.
 
4081
    try:
 
4082
        details = new_test._TestCase__details
 
4083
    except AttributeError:
 
4084
        # must be a different version of testtools than expected.  Do nothing.
 
4085
        pass
 
4086
    else:
 
4087
        # Reset the '__details' dict.
 
4088
        new_test._TestCase__details = {}
4072
4089
    return new_test
4073
4090
 
4074
4091
 
4095
4112
        the module is available.
4096
4113
    """
4097
4114
 
4098
 
    py_module = __import__(py_module_name, {}, {}, ['NO_SUCH_ATTRIB'])
 
4115
    py_module = pyutils.get_named_object(py_module_name)
4099
4116
    scenarios = [
4100
4117
        ('python', {'module': py_module}),
4101
4118
    ]
4254
4271
            symbol_versioning.warn(depr_msg + use_msg, DeprecationWarning)
4255
4272
            # Import the new feature and use it as a replacement for the
4256
4273
            # deprecated one.
4257
 
            mod = __import__(self._replacement_module, {}, {},
4258
 
                             [self._replacement_name])
4259
 
            self._feature = getattr(mod, self._replacement_name)
 
4274
            self._feature = pyutils.get_named_object(
 
4275
                self._replacement_module, self._replacement_name)
4260
4276
 
4261
4277
    def _probe(self):
4262
4278
        self._ensure()
4500
4516
try:
4501
4517
    from subunit import TestProtocolClient
4502
4518
    from subunit.test_results import AutoTimingTestResultDecorator
 
4519
    class SubUnitBzrProtocolClient(TestProtocolClient):
 
4520
 
 
4521
        def addSuccess(self, test, details=None):
 
4522
            # The subunit client always includes the details in the subunit
 
4523
            # stream, but we don't want to include it in ours.
 
4524
            if details is not None and 'log' in details:
 
4525
                del details['log']
 
4526
            return super(SubUnitBzrProtocolClient, self).addSuccess(
 
4527
                test, details)
 
4528
 
4503
4529
    class SubUnitBzrRunner(TextTestRunner):
4504
4530
        def run(self, test):
4505
4531
            result = AutoTimingTestResultDecorator(
4506
 
                TestProtocolClient(self.stream))
 
4532
                SubUnitBzrProtocolClient(self.stream))
4507
4533
            test.run(result)
4508
4534
            return result
4509
4535
except ImportError: