~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: John Arbash Meinel
  • Date: 2011-05-11 11:35:28 UTC
  • mto: This revision was merged to the branch mainline in revision 5851.
  • Revision ID: john@arbash-meinel.com-20110511113528-qepibuwxicjrbb2h
Break compatibility with python <2.6.

This includes auditing the code for places where we were doing
explicit 'sys.version' checks and removing them as appropriate.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
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
 
 
63
import bzrlib
63
64
from bzrlib import (
64
65
    branchbuilder,
65
66
    bzrdir,
66
67
    chk_map,
 
68
    commands as _mod_commands,
67
69
    config,
68
70
    debug,
69
71
    errors,
70
72
    hooks,
71
73
    lock as _mod_lock,
 
74
    lockdir,
72
75
    memorytree,
73
76
    osutils,
74
 
    progress,
 
77
    plugin as _mod_plugin,
 
78
    pyutils,
75
79
    ui,
76
80
    urlutils,
77
81
    registry,
 
82
    symbol_versioning,
 
83
    trace,
78
84
    transport as _mod_transport,
79
85
    workingtree,
80
86
    )
81
 
import bzrlib.branch
82
 
import bzrlib.commands
83
 
import bzrlib.timestamp
84
 
import bzrlib.export
85
 
import bzrlib.inventory
86
 
import bzrlib.iterablefile
87
 
import bzrlib.lockdir
88
87
try:
89
88
    import bzrlib.lsprof
90
89
except ImportError:
91
90
    # lsprof not available
92
91
    pass
93
 
from bzrlib.merge import merge_inner
94
 
import bzrlib.merge3
95
 
import bzrlib.plugin
96
 
from bzrlib.smart import client, request, server
97
 
import bzrlib.store
98
 
from bzrlib import symbol_versioning
99
 
from bzrlib.symbol_versioning import (
100
 
    DEPRECATED_PARAMETER,
101
 
    deprecated_function,
102
 
    deprecated_in,
103
 
    deprecated_method,
104
 
    deprecated_passed,
105
 
    )
106
 
import bzrlib.trace
 
92
from bzrlib.smart import client, request
107
93
from bzrlib.transport import (
108
94
    memory,
109
95
    pathfilter,
110
96
    )
111
 
from bzrlib.trace import mutter, note
112
97
from bzrlib.tests import (
113
98
    test_server,
114
99
    TestUtil,
116
101
    )
117
102
from bzrlib.ui import NullProgressView
118
103
from bzrlib.ui.text import TextUIFactory
119
 
import bzrlib.version_info_formats.format_custom
120
 
from bzrlib.workingtree import WorkingTree, WorkingTreeFormat2
121
104
 
122
105
# Mark this python module as being part of the implementation
123
106
# of unittest: this gives us better tracebacks where the last
135
118
SUBUNIT_SEEK_SET = 0
136
119
SUBUNIT_SEEK_CUR = 1
137
120
 
 
121
# These are intentionally brought into this namespace. That way plugins, etc
 
122
# can just "from bzrlib.tests import TestCase, TestLoader, etc"
 
123
TestSuite = TestUtil.TestSuite
 
124
TestLoader = TestUtil.TestLoader
 
125
 
 
126
# Tests should run in a clean and clearly defined environment. The goal is to
 
127
# keep them isolated from the running environment as mush as possible. The test
 
128
# framework ensures the variables defined below are set (or deleted if the
 
129
# value is None) before a test is run and reset to their original value after
 
130
# the test is run. Generally if some code depends on an environment variable,
 
131
# the tests should start without this variable in the environment. There are a
 
132
# few exceptions but you shouldn't violate this rule lightly.
 
133
isolated_environ = {
 
134
    'BZR_HOME': None,
 
135
    'HOME': None,
 
136
    # bzr now uses the Win32 API and doesn't rely on APPDATA, but the
 
137
    # tests do check our impls match APPDATA
 
138
    'BZR_EDITOR': None, # test_msgeditor manipulates this variable
 
139
    'VISUAL': None,
 
140
    'EDITOR': None,
 
141
    'BZR_EMAIL': None,
 
142
    'BZREMAIL': None, # may still be present in the environment
 
143
    'EMAIL': 'jrandom@example.com', # set EMAIL as bzr does not guess
 
144
    'BZR_PROGRESS_BAR': None,
 
145
    'BZR_LOG': None,
 
146
    'BZR_PLUGIN_PATH': None,
 
147
    'BZR_DISABLE_PLUGINS': None,
 
148
    'BZR_PLUGINS_AT': None,
 
149
    'BZR_CONCURRENCY': None,
 
150
    # Make sure that any text ui tests are consistent regardless of
 
151
    # the environment the test case is run in; you may want tests that
 
152
    # test other combinations.  'dumb' is a reasonable guess for tests
 
153
    # going to a pipe or a StringIO.
 
154
    'TERM': 'dumb',
 
155
    'LINES': '25',
 
156
    'COLUMNS': '80',
 
157
    'BZR_COLUMNS': '80',
 
158
    # Disable SSH Agent
 
159
    'SSH_AUTH_SOCK': None,
 
160
    # Proxies
 
161
    'http_proxy': None,
 
162
    'HTTP_PROXY': None,
 
163
    'https_proxy': None,
 
164
    'HTTPS_PROXY': None,
 
165
    'no_proxy': None,
 
166
    'NO_PROXY': None,
 
167
    'all_proxy': None,
 
168
    'ALL_PROXY': None,
 
169
    # Nobody cares about ftp_proxy, FTP_PROXY AFAIK. So far at
 
170
    # least. If you do (care), please update this comment
 
171
    # -- vila 20080401
 
172
    'ftp_proxy': None,
 
173
    'FTP_PROXY': None,
 
174
    'BZR_REMOTE_PATH': None,
 
175
    # Generally speaking, we don't want apport reporting on crashes in
 
176
    # the test envirnoment unless we're specifically testing apport,
 
177
    # so that it doesn't leak into the real system environment.  We
 
178
    # use an env var so it propagates to subprocesses.
 
179
    'APPORT_DISABLE': '1',
 
180
    }
 
181
 
 
182
 
 
183
def override_os_environ(test, env=None):
 
184
    """Modify os.environ keeping a copy.
 
185
    
 
186
    :param test: A test instance
 
187
 
 
188
    :param env: A dict containing variable definitions to be installed
 
189
    """
 
190
    if env is None:
 
191
        env = isolated_environ
 
192
    test._original_os_environ = dict([(var, value)
 
193
                                      for var, value in os.environ.iteritems()])
 
194
    for var, value in env.iteritems():
 
195
        osutils.set_or_unset_env(var, value)
 
196
        if var not in test._original_os_environ:
 
197
            # The var is new, add it with a value of None, so
 
198
            # restore_os_environ will delete it
 
199
            test._original_os_environ[var] = None
 
200
 
 
201
 
 
202
def restore_os_environ(test):
 
203
    """Restore os.environ to its original state.
 
204
 
 
205
    :param test: A test instance previously passed to override_os_environ.
 
206
    """
 
207
    for var, value in test._original_os_environ.iteritems():
 
208
        # Restore the original value (or delete it if the value has been set to
 
209
        # None in override_os_environ).
 
210
        osutils.set_or_unset_env(var, value)
 
211
 
138
212
 
139
213
class ExtendedTestResult(testtools.TextTestResult):
140
214
    """Accepts, reports and accumulates the results of running tests.
190
264
        self.count = 0
191
265
        self._overall_start_time = time.time()
192
266
        self._strict = strict
 
267
        self._first_thread_leaker_id = None
 
268
        self._tests_leaking_threads_count = 0
 
269
        self._traceback_from_test = None
193
270
 
194
271
    def stopTestRun(self):
195
272
        run = self.testsRun
234
311
            ok = self.wasStrictlySuccessful()
235
312
        else:
236
313
            ok = self.wasSuccessful()
237
 
        if TestCase._first_thread_leaker_id:
 
314
        if self._first_thread_leaker_id:
238
315
            self.stream.write(
239
316
                '%s is leaking threads among %d leaking tests.\n' % (
240
 
                TestCase._first_thread_leaker_id,
241
 
                TestCase._leaking_threads_tests))
 
317
                self._first_thread_leaker_id,
 
318
                self._tests_leaking_threads_count))
242
319
            # We don't report the main thread as an active one.
243
320
            self.stream.write(
244
321
                '%d non-main threads were left active in the end.\n'
245
 
                % (TestCase._active_threads - 1))
 
322
                % (len(self._active_threads) - 1))
246
323
 
247
324
    def getDescription(self, test):
248
325
        return test.id()
255
332
 
256
333
    def _elapsedTestTimeString(self):
257
334
        """Return a time string for the overall time the current test has taken."""
258
 
        return self._formatTime(time.time() - self._start_time)
 
335
        return self._formatTime(self._delta_to_float(
 
336
            self._now() - self._start_datetime))
259
337
 
260
338
    def _testTimeString(self, testCase):
261
339
        benchmark_time = self._extractBenchmarkTime(testCase)
275
353
        what = re.sub(r'^bzrlib\.tests\.', '', what)
276
354
        return what
277
355
 
 
356
    # GZ 2010-10-04: Cloned tests may end up harmlessly calling this method
 
357
    #                multiple times in a row, because the handler is added for
 
358
    #                each test but the container list is shared between cases.
 
359
    #                See lp:498869 lp:625574 and lp:637725 for background.
 
360
    def _record_traceback_from_test(self, exc_info):
 
361
        """Store the traceback from passed exc_info tuple till"""
 
362
        self._traceback_from_test = exc_info[2]
 
363
 
278
364
    def startTest(self, test):
279
365
        super(ExtendedTestResult, self).startTest(test)
280
366
        if self.count == 0:
281
367
            self.startTests()
 
368
        self.count += 1
282
369
        self.report_test_start(test)
283
370
        test.number = self.count
284
371
        self._recordTestStartTime()
 
372
        # Make testtools cases give us the real traceback on failure
 
373
        addOnException = getattr(test, "addOnException", None)
 
374
        if addOnException is not None:
 
375
            addOnException(self._record_traceback_from_test)
 
376
        # Only check for thread leaks on bzrlib derived test cases
 
377
        if isinstance(test, TestCase):
 
378
            test.addCleanup(self._check_leaked_threads, test)
285
379
 
286
380
    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')
 
381
        self.report_tests_starting()
 
382
        self._active_threads = threading.enumerate()
 
383
 
 
384
    def stopTest(self, test):
 
385
        self._traceback_from_test = None
 
386
 
 
387
    def _check_leaked_threads(self, test):
 
388
        """See if any threads have leaked since last call
 
389
 
 
390
        A sample of live threads is stored in the _active_threads attribute,
 
391
        when this method runs it compares the current live threads and any not
 
392
        in the previous sample are treated as having leaked.
 
393
        """
 
394
        now_active_threads = set(threading.enumerate())
 
395
        threads_leaked = now_active_threads.difference(self._active_threads)
 
396
        if threads_leaked:
 
397
            self._report_thread_leak(test, threads_leaked, now_active_threads)
 
398
            self._tests_leaking_threads_count += 1
 
399
            if self._first_thread_leaker_id is None:
 
400
                self._first_thread_leaker_id = test.id()
 
401
            self._active_threads = now_active_threads
304
402
 
305
403
    def _recordTestStartTime(self):
306
404
        """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()
 
405
        self._start_datetime = self._now()
315
406
 
316
407
    def addError(self, test, err):
317
408
        """Tell result that test finished with an error.
319
410
        Called from the TestCase run() method when the test
320
411
        fails with an unexpected error.
321
412
        """
322
 
        self._post_mortem()
 
413
        self._post_mortem(self._traceback_from_test)
323
414
        super(ExtendedTestResult, self).addError(test, err)
324
415
        self.error_count += 1
325
416
        self.report_error(test, err)
326
417
        if self.stop_early:
327
418
            self.stop()
328
 
        self._cleanupLogFile(test)
329
419
 
330
420
    def addFailure(self, test, err):
331
421
        """Tell result that test failed.
333
423
        Called from the TestCase run() method when the test
334
424
        fails because e.g. an assert() method failed.
335
425
        """
336
 
        self._post_mortem()
 
426
        self._post_mortem(self._traceback_from_test)
337
427
        super(ExtendedTestResult, self).addFailure(test, err)
338
428
        self.failure_count += 1
339
429
        self.report_failure(test, err)
340
430
        if self.stop_early:
341
431
            self.stop()
342
 
        self._cleanupLogFile(test)
343
432
 
344
433
    def addSuccess(self, test, details=None):
345
434
        """Tell result that test completed successfully.
353
442
                    self._formatTime(benchmark_time),
354
443
                    test.id()))
355
444
        self.report_success(test)
356
 
        self._cleanupLogFile(test)
357
445
        super(ExtendedTestResult, self).addSuccess(test)
358
446
        test._log_contents = ''
359
447
 
383
471
        self.not_applicable_count += 1
384
472
        self.report_not_applicable(test, reason)
385
473
 
386
 
    def _post_mortem(self):
 
474
    def _post_mortem(self, tb=None):
387
475
        """Start a PDB post mortem session."""
388
476
        if os.environ.get('BZR_TEST_PDB', None):
389
 
            import pdb;pdb.post_mortem()
 
477
            import pdb
 
478
            pdb.post_mortem(tb)
390
479
 
391
480
    def progress(self, offset, whence):
392
481
        """The test is adjusting the count of tests to run."""
397
486
        else:
398
487
            raise errors.BzrError("Unknown whence %r" % whence)
399
488
 
400
 
    def report_cleaning_up(self):
401
 
        pass
 
489
    def report_tests_starting(self):
 
490
        """Display information before the test run begins"""
 
491
        if getattr(sys, 'frozen', None) is None:
 
492
            bzr_path = osutils.realpath(sys.argv[0])
 
493
        else:
 
494
            bzr_path = sys.executable
 
495
        self.stream.write(
 
496
            'bzr selftest: %s\n' % (bzr_path,))
 
497
        self.stream.write(
 
498
            '   %s\n' % (
 
499
                    bzrlib.__path__[0],))
 
500
        self.stream.write(
 
501
            '   bzr-%s python-%s %s\n' % (
 
502
                    bzrlib.version_string,
 
503
                    bzrlib._format_version_tuple(sys.version_info),
 
504
                    platform.platform(aliased=1),
 
505
                    ))
 
506
        self.stream.write('\n')
 
507
 
 
508
    def report_test_start(self, test):
 
509
        """Display information on the test just about to be run"""
 
510
 
 
511
    def _report_thread_leak(self, test, leaked_threads, active_threads):
 
512
        """Display information on a test that leaked one or more threads"""
 
513
        # GZ 2010-09-09: A leak summary reported separately from the general
 
514
        #                thread debugging would be nice. Tests under subunit
 
515
        #                need something not using stream, perhaps adding a
 
516
        #                testtools details object would be fitting.
 
517
        if 'threads' in selftest_debug_flags:
 
518
            self.stream.write('%s is leaking, active is now %d\n' %
 
519
                (test.id(), len(active_threads)))
402
520
 
403
521
    def startTestRun(self):
404
522
        self.startTime = time.time()
441
559
        self.pb.finished()
442
560
        super(TextTestResult, self).stopTestRun()
443
561
 
444
 
    def startTestRun(self):
445
 
        super(TextTestResult, self).startTestRun()
 
562
    def report_tests_starting(self):
 
563
        super(TextTestResult, self).report_tests_starting()
446
564
        self.pb.update('[test 0/%d] Starting' % (self.num_tests))
447
565
 
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
566
    def _progress_prefix_text(self):
454
567
        # the longer this text, the less space we have to show the test
455
568
        # name...
477
590
        return a
478
591
 
479
592
    def report_test_start(self, test):
480
 
        self.count += 1
481
593
        self.pb.update(
482
594
                self._progress_prefix_text()
483
595
                + ' '
510
622
    def report_unsupported(self, test, feature):
511
623
        """test cannot be run because feature is missing."""
512
624
 
513
 
    def report_cleaning_up(self):
514
 
        self.pb.update('Cleaning up')
515
 
 
516
625
 
517
626
class VerboseTestResult(ExtendedTestResult):
518
627
    """Produce long output, with one line per test run plus times"""
525
634
            result = a_string
526
635
        return result.ljust(final_width)
527
636
 
528
 
    def startTestRun(self):
529
 
        super(VerboseTestResult, self).startTestRun()
 
637
    def report_tests_starting(self):
530
638
        self.stream.write('running %d tests...\n' % self.num_tests)
 
639
        super(VerboseTestResult, self).report_tests_starting()
531
640
 
532
641
    def report_test_start(self, test):
533
 
        self.count += 1
534
642
        name = self._shortened_test_description(test)
535
643
        width = osutils.terminal_width()
536
644
        if width is not None:
614
722
            encode = codec[0]
615
723
        else:
616
724
            encode = codec.encode
617
 
        stream = osutils.UnicodeOrBytesToBytesWriter(encode, stream)
 
725
        # GZ 2010-09-08: Really we don't want to be writing arbitrary bytes,
 
726
        #                so should swap to the plain codecs.StreamWriter
 
727
        stream = osutils.UnicodeOrBytesToBytesWriter(encode, stream,
 
728
            "backslashreplace")
618
729
        stream.encoding = new_encoding
619
730
        self.stream = stream
620
731
        self.descriptions = descriptions
770
881
        return NullProgressView()
771
882
 
772
883
 
 
884
def isolated_doctest_setUp(test):
 
885
    override_os_environ(test)
 
886
 
 
887
 
 
888
def isolated_doctest_tearDown(test):
 
889
    restore_os_environ(test)
 
890
 
 
891
 
 
892
def IsolatedDocTestSuite(*args, **kwargs):
 
893
    """Overrides doctest.DocTestSuite to handle isolation.
 
894
 
 
895
    The method is really a factory and users are expected to use it as such.
 
896
    """
 
897
    
 
898
    kwargs['setUp'] = isolated_doctest_setUp
 
899
    kwargs['tearDown'] = isolated_doctest_tearDown
 
900
    return doctest.DocTestSuite(*args, **kwargs)
 
901
 
 
902
 
773
903
class TestCase(testtools.TestCase):
774
904
    """Base class for bzr unit tests.
775
905
 
786
916
    routine, and to build and check bzr trees.
787
917
 
788
918
    In addition to the usual method of overriding tearDown(), this class also
789
 
    allows subclasses to register functions into the _cleanups list, which is
 
919
    allows subclasses to register cleanup functions via addCleanup, which are
790
920
    run in order as the object is torn down.  It's less likely this will be
791
921
    accidentally overlooked.
792
922
    """
793
923
 
794
 
    _active_threads = None
795
 
    _leaking_threads_tests = 0
796
 
    _first_thread_leaker_id = None
797
 
    _log_file_name = None
 
924
    _log_file = None
798
925
    # record lsprof data when performing benchmark calls.
799
926
    _gather_lsprof_in_benchmarks = False
800
927
 
801
928
    def __init__(self, methodName='testMethod'):
802
929
        super(TestCase, self).__init__(methodName)
803
 
        self._cleanups = []
804
930
        self._directory_isolation = True
805
931
        self.exception_handlers.insert(0,
806
932
            (UnavailableFeature, self._do_unsupported_or_skip))
824
950
        self._track_transports()
825
951
        self._track_locks()
826
952
        self._clear_debug_flags()
827
 
        TestCase._active_threads = threading.activeCount()
828
 
        self.addCleanup(self._check_leaked_threads)
 
953
        # Isolate global verbosity level, to make sure it's reproducible
 
954
        # between tests.  We should get rid of this altogether: bug 656694. --
 
955
        # mbp 20101008
 
956
        self.overrideAttr(bzrlib.trace, '_verbosity_level', 0)
 
957
        # Isolate config option expansion until its default value for bzrlib is
 
958
        # settled on or a the FIXME associated with _get_expand_default_value
 
959
        # is addressed -- vila 20110219
 
960
        self.overrideAttr(config, '_expand_default_value', None)
829
961
 
830
962
    def debug(self):
831
963
        # debug a frame up.
832
964
        import pdb
833
965
        pdb.Pdb().set_trace(sys._getframe().f_back)
834
966
 
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()
 
967
    def discardDetail(self, name):
 
968
        """Extend the addDetail, getDetails api so we can remove a detail.
 
969
 
 
970
        eg. bzr always adds the 'log' detail at startup, but we don't want to
 
971
        include it for skipped, xfail, etc tests.
 
972
 
 
973
        It is safe to call this for a detail that doesn't exist, in case this
 
974
        gets called multiple times.
 
975
        """
 
976
        # We cheat. details is stored in __details which means we shouldn't
 
977
        # touch it. but getDetails() returns the dict directly, so we can
 
978
        # mutate it.
 
979
        details = self.getDetails()
 
980
        if name in details:
 
981
            del details[name]
851
982
 
852
983
    def _clear_debug_flags(self):
853
984
        """Prevent externally set debug flags affecting tests.
864
995
 
865
996
    def _clear_hooks(self):
866
997
        # prevent hooks affecting tests
 
998
        known_hooks = hooks.known_hooks
867
999
        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)
 
1000
        for key, (parent, name) in known_hooks.iter_parent_objects():
 
1001
            current_hooks = getattr(parent, name)
871
1002
            self._preserved_hooks[parent] = (name, current_hooks)
 
1003
        self._preserved_lazy_hooks = hooks._lazy_hooks
 
1004
        hooks._lazy_hooks = {}
872
1005
        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)
 
1006
        for key, (parent, name) in known_hooks.iter_parent_objects():
 
1007
            factory = known_hooks.get(key)
875
1008
            setattr(parent, name, factory())
876
1009
        # this hook should always be installed
877
1010
        request._install_hook()
971
1104
            try:
972
1105
                workingtree.WorkingTree.open(path)
973
1106
            except (errors.NotBranchError, errors.NoWorkingTree):
974
 
                return
 
1107
                raise TestSkipped('Needs a working tree of bzr sources')
975
1108
        finally:
976
1109
            self.enable_directory_isolation()
977
1110
 
1089
1222
        except UnicodeError, e:
1090
1223
            # If we can't compare without getting a UnicodeError, then
1091
1224
            # obviously they are different
1092
 
            mutter('UnicodeError: %s', e)
 
1225
            trace.mutter('UnicodeError: %s', e)
1093
1226
        if message:
1094
1227
            message += '\n'
1095
1228
        raise AssertionError("%snot equal:\na = %s\nb = %s\n"
1134
1267
                         'st_mtime did not match')
1135
1268
        self.assertEqual(expected.st_ctime, actual.st_ctime,
1136
1269
                         'st_ctime did not match')
1137
 
        if sys.platform != 'win32':
 
1270
        if sys.platform == 'win32':
1138
1271
            # On Win32 both 'dev' and 'ino' cannot be trusted. In python2.4 it
1139
1272
            # is 'dev' that varies, in python 2.5 (6?) it is st_ino that is
1140
 
            # odd. Regardless we shouldn't actually try to assert anything
1141
 
            # about their values
 
1273
            # odd. We just force it to always be 0 to avoid any problems.
 
1274
            self.assertEqual(0, expected.st_dev)
 
1275
            self.assertEqual(0, actual.st_dev)
 
1276
            self.assertEqual(0, expected.st_ino)
 
1277
            self.assertEqual(0, actual.st_ino)
 
1278
        else:
1142
1279
            self.assertEqual(expected.st_dev, actual.st_dev,
1143
1280
                             'st_dev did not match')
1144
1281
            self.assertEqual(expected.st_ino, actual.st_ino,
1155
1292
    def assertLogsError(self, exception_class, func, *args, **kwargs):
1156
1293
        """Assert that func(*args, **kwargs) quietly logs a specific exception.
1157
1294
        """
1158
 
        from bzrlib import trace
1159
1295
        captured = []
1160
1296
        orig_log_exception_quietly = trace.log_exception_quietly
1161
1297
        try:
1210
1346
        if haystack.find(needle) == -1:
1211
1347
            self.fail("string %r not found in '''%s'''" % (needle, haystack))
1212
1348
 
 
1349
    def assertNotContainsString(self, haystack, needle):
 
1350
        if haystack.find(needle) != -1:
 
1351
            self.fail("string %r found in '''%s'''" % (needle, haystack))
 
1352
 
1213
1353
    def assertSubset(self, sublist, superlist):
1214
1354
        """Assert that every entry in sublist is present in superlist."""
1215
1355
        missing = set(sublist) - set(superlist)
1304
1444
 
1305
1445
    def assertFileEqual(self, content, path):
1306
1446
        """Fail if path does not contain 'content'."""
1307
 
        self.failUnlessExists(path)
 
1447
        self.assertPathExists(path)
1308
1448
        f = file(path, 'rb')
1309
1449
        try:
1310
1450
            s = f.read()
1320
1460
        else:
1321
1461
            self.assertEqual(expected_docstring, obj.__doc__)
1322
1462
 
 
1463
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
1323
1464
    def failUnlessExists(self, path):
 
1465
        return self.assertPathExists(path)
 
1466
 
 
1467
    def assertPathExists(self, path):
1324
1468
        """Fail unless path or paths, which may be abs or relative, exist."""
1325
1469
        if not isinstance(path, basestring):
1326
1470
            for p in path:
1327
 
                self.failUnlessExists(p)
 
1471
                self.assertPathExists(p)
1328
1472
        else:
1329
 
            self.failUnless(osutils.lexists(path),path+" does not exist")
 
1473
            self.assertTrue(osutils.lexists(path),
 
1474
                path + " does not exist")
1330
1475
 
 
1476
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
1331
1477
    def failIfExists(self, path):
 
1478
        return self.assertPathDoesNotExist(path)
 
1479
 
 
1480
    def assertPathDoesNotExist(self, path):
1332
1481
        """Fail if path or paths, which may be abs or relative, exist."""
1333
1482
        if not isinstance(path, basestring):
1334
1483
            for p in path:
1335
 
                self.failIfExists(p)
 
1484
                self.assertPathDoesNotExist(p)
1336
1485
        else:
1337
 
            self.failIf(osutils.lexists(path),path+" exists")
 
1486
            self.assertFalse(osutils.lexists(path),
 
1487
                path + " exists")
1338
1488
 
1339
1489
    def _capture_deprecation_warnings(self, a_callable, *args, **kwargs):
1340
1490
        """A helper for callDeprecated and applyDeprecated.
1454
1604
 
1455
1605
        The file is removed as the test is torn down.
1456
1606
        """
1457
 
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
1458
 
        self._log_file = os.fdopen(fileno, 'w+')
1459
 
        self._log_memento = bzrlib.trace.push_log_file(self._log_file)
1460
 
        self._log_file_name = name
 
1607
        self._log_file = StringIO()
 
1608
        self._log_memento = trace.push_log_file(self._log_file)
1461
1609
        self.addCleanup(self._finishLogFile)
1462
1610
 
1463
1611
    def _finishLogFile(self):
1465
1613
 
1466
1614
        Close the file and delete it, unless setKeepLogfile was called.
1467
1615
        """
1468
 
        if bzrlib.trace._trace_file:
 
1616
        if trace._trace_file:
1469
1617
            # flush the log file, to get all content
1470
 
            bzrlib.trace._trace_file.flush()
1471
 
        bzrlib.trace.pop_log_file(self._log_memento)
 
1618
            trace._trace_file.flush()
 
1619
        trace.pop_log_file(self._log_memento)
1472
1620
        # Cache the log result and delete the file on disk
1473
1621
        self._get_log(False)
1474
1622
 
1485
1633
        """
1486
1634
        debug.debug_flags.discard('strict_locks')
1487
1635
 
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
1636
    def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
1497
1637
        """Overrides an object attribute restoring it after the test.
1498
1638
 
1512
1652
            setattr(obj, attr_name, new)
1513
1653
        return value
1514
1654
 
 
1655
    def overrideEnv(self, name, new):
 
1656
        """Set an environment variable, and reset it after the test.
 
1657
 
 
1658
        :param name: The environment variable name.
 
1659
 
 
1660
        :param new: The value to set the variable to. If None, the 
 
1661
            variable is deleted from the environment.
 
1662
 
 
1663
        :returns: The actual variable value.
 
1664
        """
 
1665
        value = osutils.set_or_unset_env(name, new)
 
1666
        self.addCleanup(osutils.set_or_unset_env, name, value)
 
1667
        return value
 
1668
 
1515
1669
    def _cleanEnvironment(self):
1516
 
        new_env = {
1517
 
            'BZR_HOME': None, # Don't inherit BZR_HOME to all the tests.
1518
 
            'HOME': os.getcwd(),
1519
 
            # bzr now uses the Win32 API and doesn't rely on APPDATA, but the
1520
 
            # tests do check our impls match APPDATA
1521
 
            'BZR_EDITOR': None, # test_msgeditor manipulates this variable
1522
 
            'VISUAL': None,
1523
 
            'EDITOR': None,
1524
 
            'BZR_EMAIL': None,
1525
 
            'BZREMAIL': None, # may still be present in the environment
1526
 
            'EMAIL': 'jrandom@example.com', # set EMAIL as bzr does not guess
1527
 
            'BZR_PROGRESS_BAR': None,
1528
 
            'BZR_LOG': None,
1529
 
            'BZR_PLUGIN_PATH': None,
1530
 
            'BZR_DISABLE_PLUGINS': None,
1531
 
            'BZR_PLUGINS_AT': None,
1532
 
            'BZR_CONCURRENCY': None,
1533
 
            # Make sure that any text ui tests are consistent regardless of
1534
 
            # the environment the test case is run in; you may want tests that
1535
 
            # test other combinations.  'dumb' is a reasonable guess for tests
1536
 
            # going to a pipe or a StringIO.
1537
 
            'TERM': 'dumb',
1538
 
            'LINES': '25',
1539
 
            'COLUMNS': '80',
1540
 
            'BZR_COLUMNS': '80',
1541
 
            # SSH Agent
1542
 
            'SSH_AUTH_SOCK': None,
1543
 
            # Proxies
1544
 
            'http_proxy': None,
1545
 
            'HTTP_PROXY': None,
1546
 
            'https_proxy': None,
1547
 
            'HTTPS_PROXY': None,
1548
 
            'no_proxy': None,
1549
 
            'NO_PROXY': None,
1550
 
            'all_proxy': None,
1551
 
            'ALL_PROXY': None,
1552
 
            # Nobody cares about ftp_proxy, FTP_PROXY AFAIK. So far at
1553
 
            # least. If you do (care), please update this comment
1554
 
            # -- vila 20080401
1555
 
            'ftp_proxy': None,
1556
 
            'FTP_PROXY': None,
1557
 
            'BZR_REMOTE_PATH': None,
1558
 
            # Generally speaking, we don't want apport reporting on crashes in
1559
 
            # the test envirnoment unless we're specifically testing apport,
1560
 
            # so that it doesn't leak into the real system environment.  We
1561
 
            # use an env var so it propagates to subprocesses.
1562
 
            'APPORT_DISABLE': '1',
1563
 
        }
1564
 
        self._old_env = {}
1565
 
        self.addCleanup(self._restoreEnvironment)
1566
 
        for name, value in new_env.iteritems():
1567
 
            self._captureVar(name, value)
1568
 
 
1569
 
    def _captureVar(self, name, newvalue):
1570
 
        """Set an environment variable, and reset it when finished."""
1571
 
        self._old_env[name] = osutils.set_or_unset_env(name, newvalue)
1572
 
 
1573
 
    def _restoreEnvironment(self):
1574
 
        for name, value in self._old_env.iteritems():
1575
 
            osutils.set_or_unset_env(name, value)
 
1670
        for name, value in isolated_environ.iteritems():
 
1671
            self.overrideEnv(name, value)
1576
1672
 
1577
1673
    def _restoreHooks(self):
1578
1674
        for klass, (name, hooks) in self._preserved_hooks.items():
1579
1675
            setattr(klass, name, hooks)
 
1676
        hooks._lazy_hooks = self._preserved_lazy_hooks
1580
1677
 
1581
1678
    def knownFailure(self, reason):
1582
1679
        """This test has failed for some known reason."""
1583
1680
        raise KnownFailure(reason)
1584
1681
 
 
1682
    def _suppress_log(self):
 
1683
        """Remove the log info from details."""
 
1684
        self.discardDetail('log')
 
1685
 
1585
1686
    def _do_skip(self, result, reason):
 
1687
        self._suppress_log()
1586
1688
        addSkip = getattr(result, 'addSkip', None)
1587
1689
        if not callable(addSkip):
1588
1690
            result.addSuccess(result)
1591
1693
 
1592
1694
    @staticmethod
1593
1695
    def _do_known_failure(self, result, e):
 
1696
        self._suppress_log()
1594
1697
        err = sys.exc_info()
1595
1698
        addExpectedFailure = getattr(result, 'addExpectedFailure', None)
1596
1699
        if addExpectedFailure is not None:
1604
1707
            reason = 'No reason given'
1605
1708
        else:
1606
1709
            reason = e.args[0]
 
1710
        self._suppress_log ()
1607
1711
        addNotApplicable = getattr(result, 'addNotApplicable', None)
1608
1712
        if addNotApplicable is not None:
1609
1713
            result.addNotApplicable(self, reason)
1611
1715
            self._do_skip(result, reason)
1612
1716
 
1613
1717
    @staticmethod
 
1718
    def _report_skip(self, result, err):
 
1719
        """Override the default _report_skip.
 
1720
 
 
1721
        We want to strip the 'log' detail. If we waint until _do_skip, it has
 
1722
        already been formatted into the 'reason' string, and we can't pull it
 
1723
        out again.
 
1724
        """
 
1725
        self._suppress_log()
 
1726
        super(TestCase, self)._report_skip(self, result, err)
 
1727
 
 
1728
    @staticmethod
 
1729
    def _report_expected_failure(self, result, err):
 
1730
        """Strip the log.
 
1731
 
 
1732
        See _report_skip for motivation.
 
1733
        """
 
1734
        self._suppress_log()
 
1735
        super(TestCase, self)._report_expected_failure(self, result, err)
 
1736
 
 
1737
    @staticmethod
1614
1738
    def _do_unsupported_or_skip(self, result, e):
1615
1739
        reason = e.args[0]
 
1740
        self._suppress_log()
1616
1741
        addNotSupported = getattr(result, 'addNotSupported', None)
1617
1742
        if addNotSupported is not None:
1618
1743
            result.addNotSupported(self, reason)
1644
1769
            self._benchtime += time.time() - start
1645
1770
 
1646
1771
    def log(self, *args):
1647
 
        mutter(*args)
 
1772
        trace.mutter(*args)
1648
1773
 
1649
1774
    def _get_log(self, keep_log_file=False):
1650
1775
        """Internal helper to get the log from bzrlib.trace for this test.
1665
1790
                unicodestr = self._log_contents.decode('utf8', 'replace')
1666
1791
                self._log_contents = unicodestr.encode('utf8')
1667
1792
            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()
 
1793
        if self._log_file is not None:
 
1794
            log_contents = self._log_file.getvalue()
1678
1795
            try:
1679
1796
                log_contents.decode('utf8')
1680
1797
            except UnicodeDecodeError:
1681
1798
                unicodestr = log_contents.decode('utf8', 'replace')
1682
1799
                log_contents = unicodestr.encode('utf8')
1683
1800
            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
1801
                self._log_file = None
1712
1802
                # Permit multiple calls to get_log until we clean it up in
1713
1803
                # finishLogFile
1714
1804
                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
1805
            return log_contents
1725
1806
        else:
1726
 
            return "No log file content and no log file name."
 
1807
            return "No log file content."
1727
1808
 
1728
1809
    def get_log(self):
1729
1810
        """Get a unicode string containing the log from bzrlib.trace.
1779
1860
 
1780
1861
        try:
1781
1862
            try:
1782
 
                result = self.apply_redirected(ui.ui_factory.stdin,
 
1863
                result = self.apply_redirected(
 
1864
                    ui.ui_factory.stdin,
1783
1865
                    stdout, stderr,
1784
 
                    bzrlib.commands.run_bzr_catch_user_errors,
 
1866
                    _mod_commands.run_bzr_catch_user_errors,
1785
1867
                    args)
1786
1868
            except KeyboardInterrupt:
1787
1869
                # Reraise KeyboardInterrupt with contents of redirected stdout
1944
2026
            variables. A value of None will unset the env variable.
1945
2027
            The values must be strings. The change will only occur in the
1946
2028
            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.
 
2029
        :param skip_if_plan_to_signal: raise TestSkipped when true and system
 
2030
            doesn't support signalling subprocesses.
1949
2031
        :param allow_plugins: If False (default) pass --no-plugins to bzr.
1950
2032
 
1951
2033
        :returns: Popen object for the started process.
1952
2034
        """
1953
2035
        if skip_if_plan_to_signal:
1954
 
            if not getattr(os, 'kill', None):
1955
 
                raise TestSkipped("os.kill not available.")
 
2036
            if os.name != "posix":
 
2037
                raise TestSkipped("Sending signals not supported")
1956
2038
 
1957
2039
        if env_changes is None:
1958
2040
            env_changes = {}
2037
2119
        if retcode is not None and retcode != process.returncode:
2038
2120
            if process_args is None:
2039
2121
                process_args = "(unknown args)"
2040
 
            mutter('Output of bzr %s:\n%s', process_args, out)
2041
 
            mutter('Error for bzr %s:\n%s', process_args, err)
 
2122
            trace.mutter('Output of bzr %s:\n%s', process_args, out)
 
2123
            trace.mutter('Error for bzr %s:\n%s', process_args, err)
2042
2124
            self.fail('Command bzr %s failed with retcode %s != %s'
2043
2125
                      % (process_args, retcode, process.returncode))
2044
2126
        return [out, err]
2045
2127
 
2046
 
    def check_inventory_shape(self, inv, shape):
2047
 
        """Compare an inventory to a list of expected names.
 
2128
    def check_tree_shape(self, tree, shape):
 
2129
        """Compare a tree to a list of expected names.
2048
2130
 
2049
2131
        Fail if they are not precisely equal.
2050
2132
        """
2051
2133
        extras = []
2052
2134
        shape = list(shape)             # copy
2053
 
        for path, ie in inv.entries():
 
2135
        for path, ie in tree.iter_entries_by_dir():
2054
2136
            name = path.replace('\\', '/')
2055
2137
            if ie.kind == 'directory':
2056
2138
                name = name + '/'
2057
 
            if name in shape:
 
2139
            if name == "/":
 
2140
                pass # ignore root entry
 
2141
            elif name in shape:
2058
2142
                shape.remove(name)
2059
2143
            else:
2060
2144
                extras.append(name)
2101
2185
 
2102
2186
        Tests that expect to provoke LockContention errors should call this.
2103
2187
        """
2104
 
        self.overrideAttr(bzrlib.lockdir, '_DEFAULT_TIMEOUT_SECONDS', 0)
 
2188
        self.overrideAttr(lockdir, '_DEFAULT_TIMEOUT_SECONDS', 0)
2105
2189
 
2106
2190
    def make_utf8_encoded_stringio(self, encoding_type=None):
2107
2191
        """Return a StringIOWrapper instance, that will encode Unicode
2431
2515
        test_home_dir = self.test_home_dir
2432
2516
        if isinstance(test_home_dir, unicode):
2433
2517
            test_home_dir = test_home_dir.encode(sys.getfilesystemencoding())
2434
 
        os.environ['HOME'] = test_home_dir
2435
 
        os.environ['BZR_HOME'] = test_home_dir
 
2518
        self.overrideEnv('HOME', test_home_dir)
 
2519
        self.overrideEnv('BZR_HOME', test_home_dir)
2436
2520
 
2437
2521
    def setUp(self):
2438
2522
        super(TestCaseWithMemoryTransport, self).setUp()
3012
3096
 
3013
3097
 
3014
3098
def fork_decorator(suite):
 
3099
    if getattr(os, "fork", None) is None:
 
3100
        raise errors.BzrCommandError("platform does not support fork,"
 
3101
            " try --parallel=subprocess instead.")
3015
3102
    concurrency = osutils.local_concurrency()
3016
3103
    if concurrency == 1:
3017
3104
        return suite
3341
3428
    return result
3342
3429
 
3343
3430
 
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):
 
3431
class ProfileResult(testtools.ExtendedToOriginalDecorator):
3377
3432
    """Generate profiling data for all activity between start and success.
3378
3433
    
3379
3434
    The profile data is appended to the test's _benchcalls attribute and can
3391
3446
        # unavoidably fail.
3392
3447
        bzrlib.lsprof.BzrProfiler.profiler_block = 0
3393
3448
        self.profiler.start()
3394
 
        ForwardingResult.startTest(self, test)
 
3449
        testtools.ExtendedToOriginalDecorator.startTest(self, test)
3395
3450
 
3396
3451
    def addSuccess(self, test):
3397
3452
        stats = self.profiler.stop()
3401
3456
            test._benchcalls = []
3402
3457
            calls = test._benchcalls
3403
3458
        calls.append(((test.id(), "", ""), stats))
3404
 
        ForwardingResult.addSuccess(self, test)
 
3459
        testtools.ExtendedToOriginalDecorator.addSuccess(self, test)
3405
3460
 
3406
3461
    def stopTest(self, test):
3407
 
        ForwardingResult.stopTest(self, test)
 
3462
        testtools.ExtendedToOriginalDecorator.stopTest(self, test)
3408
3463
        self.profiler = None
3409
3464
 
3410
3465
 
3617
3672
                key, obj, help=help, info=info, override_existing=False)
3618
3673
        except KeyError:
3619
3674
            actual = self.get(key)
3620
 
            note('Test prefix alias %s is already used for %s, ignoring %s'
3621
 
                 % (key, actual, obj))
 
3675
            trace.note(
 
3676
                'Test prefix alias %s is already used for %s, ignoring %s'
 
3677
                % (key, actual, obj))
3622
3678
 
3623
3679
    def resolve_alias(self, id_start):
3624
3680
        """Replace the alias by the prefix in the given string.
3658
3714
        'bzrlib.tests.commands',
3659
3715
        'bzrlib.tests.doc_generate',
3660
3716
        'bzrlib.tests.per_branch',
 
3717
        'bzrlib.tests.per_bzrdir',
3661
3718
        'bzrlib.tests.per_controldir',
3662
3719
        'bzrlib.tests.per_controldir_colo',
3663
3720
        'bzrlib.tests.per_foreign_vcs',
3673
3730
        'bzrlib.tests.per_repository',
3674
3731
        'bzrlib.tests.per_repository_chk',
3675
3732
        'bzrlib.tests.per_repository_reference',
 
3733
        'bzrlib.tests.per_repository_vf',
3676
3734
        'bzrlib.tests.per_uifactory',
3677
3735
        'bzrlib.tests.per_versionedfile',
3678
3736
        'bzrlib.tests.per_workingtree',
3712
3770
        'bzrlib.tests.test_commit_merge',
3713
3771
        'bzrlib.tests.test_config',
3714
3772
        'bzrlib.tests.test_conflicts',
 
3773
        'bzrlib.tests.test_controldir',
3715
3774
        'bzrlib.tests.test_counted_lock',
3716
3775
        'bzrlib.tests.test_crash',
3717
3776
        'bzrlib.tests.test_decorators',
3718
3777
        'bzrlib.tests.test_delta',
3719
3778
        'bzrlib.tests.test_debug',
3720
 
        'bzrlib.tests.test_deprecated_graph',
3721
3779
        'bzrlib.tests.test_diff',
3722
3780
        'bzrlib.tests.test_directory_service',
3723
3781
        'bzrlib.tests.test_dirstate',
3768
3826
        'bzrlib.tests.test_merge3',
3769
3827
        'bzrlib.tests.test_merge_core',
3770
3828
        'bzrlib.tests.test_merge_directive',
 
3829
        'bzrlib.tests.test_mergetools',
3771
3830
        'bzrlib.tests.test_missing',
3772
3831
        'bzrlib.tests.test_msgeditor',
3773
3832
        'bzrlib.tests.test_multiparent',
3782
3841
        'bzrlib.tests.test_permissions',
3783
3842
        'bzrlib.tests.test_plugins',
3784
3843
        'bzrlib.tests.test_progress',
 
3844
        'bzrlib.tests.test_pyutils',
3785
3845
        'bzrlib.tests.test_read_bundle',
3786
3846
        'bzrlib.tests.test_reconcile',
3787
3847
        'bzrlib.tests.test_reconfigure',
3796
3856
        'bzrlib.tests.test_rio',
3797
3857
        'bzrlib.tests.test_rules',
3798
3858
        'bzrlib.tests.test_sampler',
 
3859
        'bzrlib.tests.test_scenarios',
3799
3860
        'bzrlib.tests.test_script',
3800
3861
        'bzrlib.tests.test_selftest',
3801
3862
        'bzrlib.tests.test_serializer',
3821
3882
        'bzrlib.tests.test_testament',
3822
3883
        'bzrlib.tests.test_textfile',
3823
3884
        'bzrlib.tests.test_textmerge',
 
3885
        'bzrlib.tests.test_cethread',
3824
3886
        'bzrlib.tests.test_timestamp',
3825
3887
        'bzrlib.tests.test_trace',
3826
3888
        'bzrlib.tests.test_transactions',
3859
3921
        'bzrlib',
3860
3922
        'bzrlib.branchbuilder',
3861
3923
        'bzrlib.decorators',
3862
 
        'bzrlib.export',
3863
3924
        'bzrlib.inventory',
3864
3925
        'bzrlib.iterablefile',
3865
3926
        'bzrlib.lockdir',
3866
3927
        'bzrlib.merge3',
3867
3928
        'bzrlib.option',
 
3929
        'bzrlib.pyutils',
3868
3930
        'bzrlib.symbol_versioning',
3869
3931
        'bzrlib.tests',
3870
3932
        'bzrlib.tests.fixtures',
3871
3933
        'bzrlib.timestamp',
 
3934
        'bzrlib.transport.http',
3872
3935
        'bzrlib.version_info_formats.format_custom',
3873
3936
        ]
3874
3937
 
3927
3990
        try:
3928
3991
            # note that this really does mean "report only" -- doctest
3929
3992
            # still runs the rest of the examples
3930
 
            doc_suite = doctest.DocTestSuite(mod,
3931
 
                optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
 
3993
            doc_suite = IsolatedDocTestSuite(
 
3994
                mod, optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
3932
3995
        except ValueError, e:
3933
3996
            print '**failed to get doctest for: %s\n%s' % (mod, e)
3934
3997
            raise
3937
4000
        suite.addTest(doc_suite)
3938
4001
 
3939
4002
    default_encoding = sys.getdefaultencoding()
3940
 
    for name, plugin in bzrlib.plugin.plugins().items():
 
4003
    for name, plugin in _mod_plugin.plugins().items():
3941
4004
        if not interesting_module(plugin.module.__name__):
3942
4005
            continue
3943
4006
        plugin_suite = plugin.test_suite()
3949
4012
        if plugin_suite is not None:
3950
4013
            suite.addTest(plugin_suite)
3951
4014
        if default_encoding != sys.getdefaultencoding():
3952
 
            bzrlib.trace.warning(
 
4015
            trace.warning(
3953
4016
                'Plugin "%s" tried to reset default encoding to: %s', name,
3954
4017
                sys.getdefaultencoding())
3955
4018
            reload(sys)
3970
4033
            # Some tests mentioned in the list are not in the test suite. The
3971
4034
            # list may be out of date, report to the tester.
3972
4035
            for id in not_found:
3973
 
                bzrlib.trace.warning('"%s" not found in the test suite', id)
 
4036
                trace.warning('"%s" not found in the test suite', id)
3974
4037
        for id in duplicates:
3975
 
            bzrlib.trace.warning('"%s" is used as an id by several tests', id)
 
4038
            trace.warning('"%s" is used as an id by several tests', id)
3976
4039
 
3977
4040
    return suite
3978
4041
 
3979
4042
 
3980
 
def multiply_scenarios(scenarios_left, scenarios_right):
 
4043
def multiply_scenarios(*scenarios):
 
4044
    """Multiply two or more iterables of scenarios.
 
4045
 
 
4046
    It is safe to pass scenario generators or iterators.
 
4047
 
 
4048
    :returns: A list of compound scenarios: the cross-product of all 
 
4049
        scenarios, with the names concatenated and the parameters
 
4050
        merged together.
 
4051
    """
 
4052
    return reduce(_multiply_two_scenarios, map(list, scenarios))
 
4053
 
 
4054
 
 
4055
def _multiply_two_scenarios(scenarios_left, scenarios_right):
3981
4056
    """Multiply two sets of scenarios.
3982
4057
 
3983
4058
    :returns: the cartesian product of the two sets of scenarios, that is
4069
4144
    """
4070
4145
    new_test = copy.copy(test)
4071
4146
    new_test.id = lambda: new_id
 
4147
    # XXX: Workaround <https://bugs.launchpad.net/testtools/+bug/637725>, which
 
4148
    # causes cloned tests to share the 'details' dict.  This makes it hard to
 
4149
    # read the test output for parameterized tests, because tracebacks will be
 
4150
    # associated with irrelevant tests.
 
4151
    try:
 
4152
        details = new_test._TestCase__details
 
4153
    except AttributeError:
 
4154
        # must be a different version of testtools than expected.  Do nothing.
 
4155
        pass
 
4156
    else:
 
4157
        # Reset the '__details' dict.
 
4158
        new_test._TestCase__details = {}
4072
4159
    return new_test
4073
4160
 
4074
4161
 
4095
4182
        the module is available.
4096
4183
    """
4097
4184
 
4098
 
    py_module = __import__(py_module_name, {}, {}, ['NO_SUCH_ATTRIB'])
 
4185
    py_module = pyutils.get_named_object(py_module_name)
4099
4186
    scenarios = [
4100
4187
        ('python', {'module': py_module}),
4101
4188
    ]
4254
4341
            symbol_versioning.warn(depr_msg + use_msg, DeprecationWarning)
4255
4342
            # Import the new feature and use it as a replacement for the
4256
4343
            # deprecated one.
4257
 
            mod = __import__(self._replacement_module, {}, {},
4258
 
                             [self._replacement_name])
4259
 
            self._feature = getattr(mod, self._replacement_name)
 
4344
            self._feature = pyutils.get_named_object(
 
4345
                self._replacement_module, self._replacement_name)
4260
4346
 
4261
4347
    def _probe(self):
4262
4348
        self._ensure()
4293
4379
        return self.module_name
4294
4380
 
4295
4381
 
4296
 
# This is kept here for compatibility, it is recommended to use
4297
 
# 'bzrlib.tests.feature.paramiko' instead
4298
 
ParamikoFeature = _CompatabilityThunkFeature(
4299
 
    deprecated_in((2,1,0)),
4300
 
    'bzrlib.tests.features', 'ParamikoFeature', 'paramiko')
4301
 
 
4302
 
 
4303
4382
def probe_unicode_in_user_encoding():
4304
4383
    """Try to encode several unicode strings to use in unicode-aware tests.
4305
4384
    Return first successfull match.
4492
4571
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
4493
4572
 
4494
4573
 
4495
 
# Kept for compatibility, use bzrlib.tests.features.subunit instead
4496
 
SubUnitFeature = _CompatabilityThunkFeature(
4497
 
    deprecated_in((2,1,0)),
4498
 
    'bzrlib.tests.features', 'SubUnitFeature', 'subunit')
4499
4574
# Only define SubUnitBzrRunner if subunit is available.
4500
4575
try:
4501
4576
    from subunit import TestProtocolClient
4502
4577
    from subunit.test_results import AutoTimingTestResultDecorator
 
4578
    class SubUnitBzrProtocolClient(TestProtocolClient):
 
4579
 
 
4580
        def addSuccess(self, test, details=None):
 
4581
            # The subunit client always includes the details in the subunit
 
4582
            # stream, but we don't want to include it in ours.
 
4583
            if details is not None and 'log' in details:
 
4584
                del details['log']
 
4585
            return super(SubUnitBzrProtocolClient, self).addSuccess(
 
4586
                test, details)
 
4587
 
4503
4588
    class SubUnitBzrRunner(TextTestRunner):
4504
4589
        def run(self, test):
4505
4590
            result = AutoTimingTestResultDecorator(
4506
 
                TestProtocolClient(self.stream))
 
4591
                SubUnitBzrProtocolClient(self.stream))
4507
4592
            test.run(result)
4508
4593
            return result
4509
4594
except ImportError: