55
56
# nb: check this before importing anything else from within it
56
57
_testtools_version = getattr(testtools, '__version__', ())
57
if _testtools_version < (0, 9, 2):
58
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"
59
60
% (testtools.__file__, _testtools_version))
60
61
from testtools import content
62
64
from bzrlib import (
68
commands as _mod_commands,
77
plugin as _mod_plugin,
84
transport as _mod_transport,
80
import bzrlib.commands
81
import bzrlib.timestamp
83
import bzrlib.inventory
84
import bzrlib.iterablefile
87
88
import bzrlib.lsprof
88
89
except ImportError:
89
90
# lsprof not available
91
from bzrlib.merge import merge_inner
94
from bzrlib.smart import client, request, server
96
from bzrlib import symbol_versioning
97
from bzrlib.symbol_versioning import (
92
from bzrlib.smart import client, request
105
93
from bzrlib.transport import (
110
import bzrlib.transport
111
from bzrlib.trace import mutter, note
112
97
from bzrlib.tests import (
116
from bzrlib.tests.http_server import HttpServer
117
from bzrlib.tests.TestUtil import (
121
from bzrlib.tests.treeshape import build_tree_contents
122
102
from bzrlib.ui import NullProgressView
123
103
from bzrlib.ui.text import TextUIFactory
124
import bzrlib.version_info_formats.format_custom
125
from bzrlib.workingtree import WorkingTree, WorkingTreeFormat2
127
105
# Mark this python module as being part of the implementation
128
106
# of unittest: this gives us better tracebacks where the last
140
118
SUBUNIT_SEEK_SET = 0
141
119
SUBUNIT_SEEK_CUR = 1
144
class ExtendedTestResult(unittest._TextTestResult):
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
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.
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
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,
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.
159
'SSH_AUTH_SOCK': None,
169
# Nobody cares about ftp_proxy, FTP_PROXY AFAIK. So far at
170
# least. If you do (care), please update this comment
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',
183
def override_os_environ(test, env=None):
184
"""Modify os.environ keeping a copy.
186
:param test: A test instance
188
:param env: A dict containing variable definitions to be installed
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
202
def restore_os_environ(test):
203
"""Restore os.environ to its original state.
205
:param test: A test instance previously passed to override_os_environ.
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)
213
class ExtendedTestResult(testtools.TextTestResult):
145
214
"""Accepts, reports and accumulates the results of running tests.
147
216
Compared to the unittest version this class adds support for
196
265
self._overall_start_time = time.time()
197
266
self._strict = strict
267
self._first_thread_leaker_id = None
268
self._tests_leaking_threads_count = 0
269
self._traceback_from_test = None
199
271
def stopTestRun(self):
200
272
run = self.testsRun
201
273
actionTaken = "Ran"
202
274
stopTime = time.time()
203
275
timeTaken = stopTime - self.startTime
205
self.stream.writeln(self.separator2)
206
self.stream.writeln("%s %d test%s in %.3fs" % (actionTaken,
276
# GZ 2010-07-19: Seems testtools has no printErrors method, and though
277
# the parent class method is similar have to duplicate
278
self._show_list('ERROR', self.errors)
279
self._show_list('FAIL', self.failures)
280
self.stream.write(self.sep2)
281
self.stream.write("%s %d test%s in %.3fs\n\n" % (actionTaken,
207
282
run, run != 1 and "s" or "", timeTaken))
208
self.stream.writeln()
209
283
if not self.wasSuccessful():
210
284
self.stream.write("FAILED (")
211
285
failed, errored = map(len, (self.failures, self.errors))
218
292
if failed or errored: self.stream.write(", ")
219
293
self.stream.write("known_failure_count=%d" %
220
294
self.known_failure_count)
221
self.stream.writeln(")")
295
self.stream.write(")\n")
223
297
if self.known_failure_count:
224
self.stream.writeln("OK (known_failures=%d)" %
298
self.stream.write("OK (known_failures=%d)\n" %
225
299
self.known_failure_count)
227
self.stream.writeln("OK")
301
self.stream.write("OK\n")
228
302
if self.skip_count > 0:
229
303
skipped = self.skip_count
230
self.stream.writeln('%d test%s skipped' %
304
self.stream.write('%d test%s skipped\n' %
231
305
(skipped, skipped != 1 and "s" or ""))
232
306
if self.unsupported:
233
307
for feature, count in sorted(self.unsupported.items()):
234
self.stream.writeln("Missing feature '%s' skipped %d tests." %
308
self.stream.write("Missing feature '%s' skipped %d tests.\n" %
235
309
(feature, count))
237
311
ok = self.wasStrictlySuccessful()
239
313
ok = self.wasSuccessful()
240
if TestCase._first_thread_leaker_id:
314
if self._first_thread_leaker_id:
241
315
self.stream.write(
242
316
'%s is leaking threads among %d leaking tests.\n' % (
243
TestCase._first_thread_leaker_id,
244
TestCase._leaking_threads_tests))
317
self._first_thread_leaker_id,
318
self._tests_leaking_threads_count))
245
319
# We don't report the main thread as an active one.
246
320
self.stream.write(
247
321
'%d non-main threads were left active in the end.\n'
248
% (TestCase._active_threads - 1))
322
% (len(self._active_threads) - 1))
250
324
def getDescription(self, test):
276
351
def _shortened_test_description(self, test):
278
what = re.sub(r'^bzrlib\.(tests|benchmarks)\.', '', what)
353
what = re.sub(r'^bzrlib\.tests\.', '', what)
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]
281
364
def startTest(self, test):
282
unittest.TestResult.startTest(self, test)
365
super(ExtendedTestResult, self).startTest(test)
283
366
if self.count == 0:
284
367
self.startTests()
285
369
self.report_test_start(test)
286
370
test.number = self.count
287
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)
380
def stopTest(self, test):
381
super(ExtendedTestResult, self).stopTest(test)
382
# Manually break cycles, means touching various private things but hey
383
getDetails = getattr(test, "getDetails", None)
384
if getDetails is not None:
386
type_equality_funcs = getattr(test, "_type_equality_funcs", None)
387
if type_equality_funcs is not None:
388
type_equality_funcs.clear()
389
self._traceback_from_test = None
289
391
def startTests(self):
291
if getattr(sys, 'frozen', None) is None:
292
bzr_path = osutils.realpath(sys.argv[0])
294
bzr_path = sys.executable
296
'bzr selftest: %s\n' % (bzr_path,))
299
bzrlib.__path__[0],))
301
' bzr-%s python-%s %s\n' % (
302
bzrlib.version_string,
303
bzrlib._format_version_tuple(sys.version_info),
304
platform.platform(aliased=1),
306
self.stream.write('\n')
392
self.report_tests_starting()
393
self._active_threads = threading.enumerate()
395
def _check_leaked_threads(self, test):
396
"""See if any threads have leaked since last call
398
A sample of live threads is stored in the _active_threads attribute,
399
when this method runs it compares the current live threads and any not
400
in the previous sample are treated as having leaked.
402
now_active_threads = set(threading.enumerate())
403
threads_leaked = now_active_threads.difference(self._active_threads)
405
self._report_thread_leak(test, threads_leaked, now_active_threads)
406
self._tests_leaking_threads_count += 1
407
if self._first_thread_leaker_id is None:
408
self._first_thread_leaker_id = test.id()
409
self._active_threads = now_active_threads
308
411
def _recordTestStartTime(self):
309
412
"""Record that a test has started."""
310
self._start_time = time.time()
312
def _cleanupLogFile(self, test):
313
# We can only do this if we have one of our TestCases, not if
315
setKeepLogfile = getattr(test, 'setKeepLogfile', None)
316
if setKeepLogfile is not None:
413
self._start_datetime = self._now()
319
415
def addError(self, test, err):
320
416
"""Tell result that test finished with an error.
356
450
self._formatTime(benchmark_time),
358
452
self.report_success(test)
359
self._cleanupLogFile(test)
360
unittest.TestResult.addSuccess(self, test)
453
super(ExtendedTestResult, self).addSuccess(test)
361
454
test._log_contents = ''
363
456
def addExpectedFailure(self, test, err):
364
457
self.known_failure_count += 1
365
458
self.report_known_failure(test, err)
460
def addUnexpectedSuccess(self, test, details=None):
461
"""Tell result the test unexpectedly passed, counting as a failure
463
When the minimum version of testtools required becomes 0.9.8 this
464
can be updated to use the new handling there.
466
super(ExtendedTestResult, self).addFailure(test, details=details)
467
self.failure_count += 1
468
self.report_unexpected_success(test,
469
"".join(details["reason"].iter_text()))
367
473
def addNotSupported(self, test, feature):
368
474
"""The test will not be run because of a missing feature.
401
508
raise errors.BzrError("Unknown whence %r" % whence)
403
def report_cleaning_up(self):
510
def report_tests_starting(self):
511
"""Display information before the test run begins"""
512
if getattr(sys, 'frozen', None) is None:
513
bzr_path = osutils.realpath(sys.argv[0])
515
bzr_path = sys.executable
517
'bzr selftest: %s\n' % (bzr_path,))
520
bzrlib.__path__[0],))
522
' bzr-%s python-%s %s\n' % (
523
bzrlib.version_string,
524
bzrlib._format_version_tuple(sys.version_info),
525
platform.platform(aliased=1),
527
self.stream.write('\n')
529
def report_test_start(self, test):
530
"""Display information on the test just about to be run"""
532
def _report_thread_leak(self, test, leaked_threads, active_threads):
533
"""Display information on a test that leaked one or more threads"""
534
# GZ 2010-09-09: A leak summary reported separately from the general
535
# thread debugging would be nice. Tests under subunit
536
# need something not using stream, perhaps adding a
537
# testtools details object would be fitting.
538
if 'threads' in selftest_debug_flags:
539
self.stream.write('%s is leaking, active is now %d\n' %
540
(test.id(), len(active_threads)))
406
542
def startTestRun(self):
407
543
self.startTime = time.time()
551
684
return '%s%s' % (indent, err[1])
553
686
def report_error(self, test, err):
554
self.stream.writeln('ERROR %s\n%s'
687
self.stream.write('ERROR %s\n%s\n'
555
688
% (self._testTimeString(test),
556
689
self._error_summary(err)))
558
691
def report_failure(self, test, err):
559
self.stream.writeln(' FAIL %s\n%s'
692
self.stream.write(' FAIL %s\n%s\n'
560
693
% (self._testTimeString(test),
561
694
self._error_summary(err)))
563
696
def report_known_failure(self, test, err):
564
self.stream.writeln('XFAIL %s\n%s'
697
self.stream.write('XFAIL %s\n%s\n'
565
698
% (self._testTimeString(test),
566
699
self._error_summary(err)))
701
def report_unexpected_success(self, test, reason):
702
self.stream.write(' FAIL %s\n%s: %s\n'
703
% (self._testTimeString(test),
704
"Unexpected success. Should have failed",
568
707
def report_success(self, test):
569
self.stream.writeln(' OK %s' % self._testTimeString(test))
708
self.stream.write(' OK %s\n' % self._testTimeString(test))
570
709
for bench_called, stats in getattr(test, '_benchcalls', []):
571
self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
710
self.stream.write('LSProf output for %s(%s, %s)\n' % bench_called)
572
711
stats.pprint(file=self.stream)
573
712
# flush the stream so that we get smooth output. This verbose mode is
574
713
# used to show the output in PQM.
575
714
self.stream.flush()
577
716
def report_skip(self, test, reason):
578
self.stream.writeln(' SKIP %s\n%s'
717
self.stream.write(' SKIP %s\n%s\n'
579
718
% (self._testTimeString(test), reason))
581
720
def report_not_applicable(self, test, reason):
582
self.stream.writeln(' N/A %s\n %s'
721
self.stream.write(' N/A %s\n %s\n'
583
722
% (self._testTimeString(test), reason))
585
724
def report_unsupported(self, test, feature):
586
725
"""test cannot be run because feature is missing."""
587
self.stream.writeln("NODEP %s\n The feature '%s' is not available."
726
self.stream.write("NODEP %s\n The feature '%s' is not available.\n"
588
727
%(self._testTimeString(test), feature))
773
915
return NullProgressView()
918
def isolated_doctest_setUp(test):
919
override_os_environ(test)
922
def isolated_doctest_tearDown(test):
923
restore_os_environ(test)
926
def IsolatedDocTestSuite(*args, **kwargs):
927
"""Overrides doctest.DocTestSuite to handle isolation.
929
The method is really a factory and users are expected to use it as such.
932
kwargs['setUp'] = isolated_doctest_setUp
933
kwargs['tearDown'] = isolated_doctest_tearDown
934
return doctest.DocTestSuite(*args, **kwargs)
776
937
class TestCase(testtools.TestCase):
777
938
"""Base class for bzr unit tests.
789
950
routine, and to build and check bzr trees.
791
952
In addition to the usual method of overriding tearDown(), this class also
792
allows subclasses to register functions into the _cleanups list, which is
953
allows subclasses to register cleanup functions via addCleanup, which are
793
954
run in order as the object is torn down. It's less likely this will be
794
955
accidentally overlooked.
797
_active_threads = None
798
_leaking_threads_tests = 0
799
_first_thread_leaker_id = None
800
_log_file_name = None
801
959
# record lsprof data when performing benchmark calls.
802
960
_gather_lsprof_in_benchmarks = False
804
962
def __init__(self, methodName='testMethod'):
805
963
super(TestCase, self).__init__(methodName)
807
964
self._directory_isolation = True
808
965
self.exception_handlers.insert(0,
809
966
(UnavailableFeature, self._do_unsupported_or_skip))
827
980
self._track_transports()
828
981
self._track_locks()
829
982
self._clear_debug_flags()
830
TestCase._active_threads = threading.activeCount()
831
self.addCleanup(self._check_leaked_threads)
983
# Isolate global verbosity level, to make sure it's reproducible
984
# between tests. We should get rid of this altogether: bug 656694. --
986
self.overrideAttr(bzrlib.trace, '_verbosity_level', 0)
987
# Isolate config option expansion until its default value for bzrlib is
988
# settled on or a the FIXME associated with _get_expand_default_value
989
# is addressed -- vila 20110219
990
self.overrideAttr(config, '_expand_default_value', None)
991
self._log_files = set()
834
994
# debug a frame up.
836
996
pdb.Pdb().set_trace(sys._getframe().f_back)
838
def _check_leaked_threads(self):
839
active = threading.activeCount()
840
leaked_threads = active - TestCase._active_threads
841
TestCase._active_threads = active
842
# If some tests make the number of threads *decrease*, we'll consider
843
# that they are just observing old threads dieing, not agressively kill
844
# random threads. So we don't report these tests as leaking. The risk
845
# is that we have false positives that way (the test see 2 threads
846
# going away but leak one) but it seems less likely than the actual
847
# false positives (the test see threads going away and does not leak).
848
if leaked_threads > 0:
849
TestCase._leaking_threads_tests += 1
850
if TestCase._first_thread_leaker_id is None:
851
TestCase._first_thread_leaker_id = self.id()
998
def discardDetail(self, name):
999
"""Extend the addDetail, getDetails api so we can remove a detail.
1001
eg. bzr always adds the 'log' detail at startup, but we don't want to
1002
include it for skipped, xfail, etc tests.
1004
It is safe to call this for a detail that doesn't exist, in case this
1005
gets called multiple times.
1007
# We cheat. details is stored in __details which means we shouldn't
1008
# touch it. but getDetails() returns the dict directly, so we can
1010
details = self.getDetails()
853
1014
def _clear_debug_flags(self):
854
1015
"""Prevent externally set debug flags affecting tests.
866
1027
def _clear_hooks(self):
867
1028
# prevent hooks affecting tests
1029
known_hooks = hooks.known_hooks
868
1030
self._preserved_hooks = {}
869
for key, factory in hooks.known_hooks.items():
870
parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
871
current_hooks = hooks.known_hooks_key_to_object(key)
1031
for key, (parent, name) in known_hooks.iter_parent_objects():
1032
current_hooks = getattr(parent, name)
872
1033
self._preserved_hooks[parent] = (name, current_hooks)
1034
self._preserved_lazy_hooks = hooks._lazy_hooks
1035
hooks._lazy_hooks = {}
873
1036
self.addCleanup(self._restoreHooks)
874
for key, factory in hooks.known_hooks.items():
875
parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
1037
for key, (parent, name) in known_hooks.iter_parent_objects():
1038
factory = known_hooks.get(key)
876
1039
setattr(parent, name, factory())
877
1040
# this hook should always be installed
878
1041
request._install_hook()
1135
1298
'st_mtime did not match')
1136
1299
self.assertEqual(expected.st_ctime, actual.st_ctime,
1137
1300
'st_ctime did not match')
1138
if sys.platform != 'win32':
1301
if sys.platform == 'win32':
1139
1302
# On Win32 both 'dev' and 'ino' cannot be trusted. In python2.4 it
1140
1303
# is 'dev' that varies, in python 2.5 (6?) it is st_ino that is
1141
# odd. Regardless we shouldn't actually try to assert anything
1142
# about their values
1304
# odd. We just force it to always be 0 to avoid any problems.
1305
self.assertEqual(0, expected.st_dev)
1306
self.assertEqual(0, actual.st_dev)
1307
self.assertEqual(0, expected.st_ino)
1308
self.assertEqual(0, actual.st_ino)
1143
1310
self.assertEqual(expected.st_dev, actual.st_dev,
1144
1311
'st_dev did not match')
1145
1312
self.assertEqual(expected.st_ino, actual.st_ino,
1154
1321
length, len(obj_with_len), obj_with_len))
1156
1323
def assertLogsError(self, exception_class, func, *args, **kwargs):
1157
"""Assert that func(*args, **kwargs) quietly logs a specific exception.
1324
"""Assert that `func(*args, **kwargs)` quietly logs a specific error.
1159
from bzrlib import trace
1161
1327
orig_log_exception_quietly = trace.log_exception_quietly
1164
1330
orig_log_exception_quietly()
1165
captured.append(sys.exc_info())
1331
captured.append(sys.exc_info()[1])
1166
1332
trace.log_exception_quietly = capture
1167
1333
func(*args, **kwargs)
1169
1335
trace.log_exception_quietly = orig_log_exception_quietly
1170
1336
self.assertLength(1, captured)
1171
err = captured[0][1]
1172
1338
self.assertIsInstance(err, exception_class)
1322
1492
self.assertEqual(expected_docstring, obj.__doc__)
1494
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
1324
1495
def failUnlessExists(self, path):
1496
return self.assertPathExists(path)
1498
def assertPathExists(self, path):
1325
1499
"""Fail unless path or paths, which may be abs or relative, exist."""
1326
1500
if not isinstance(path, basestring):
1328
self.failUnlessExists(p)
1502
self.assertPathExists(p)
1330
self.failUnless(osutils.lexists(path),path+" does not exist")
1504
self.assertTrue(osutils.lexists(path),
1505
path + " does not exist")
1507
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
1332
1508
def failIfExists(self, path):
1509
return self.assertPathDoesNotExist(path)
1511
def assertPathDoesNotExist(self, path):
1333
1512
"""Fail if path or paths, which may be abs or relative, exist."""
1334
1513
if not isinstance(path, basestring):
1336
self.failIfExists(p)
1515
self.assertPathDoesNotExist(p)
1338
self.failIf(osutils.lexists(path),path+" exists")
1517
self.assertFalse(osutils.lexists(path),
1340
1520
def _capture_deprecation_warnings(self, a_callable, *args, **kwargs):
1341
1521
"""A helper for callDeprecated and applyDeprecated.
1456
1637
The file is removed as the test is torn down.
1458
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
1459
self._log_file = os.fdopen(fileno, 'w+')
1460
self._log_memento = bzrlib.trace.push_log_file(self._log_file)
1461
self._log_file_name = name
1639
pseudo_log_file = StringIO()
1640
def _get_log_contents_for_weird_testtools_api():
1641
return [pseudo_log_file.getvalue().decode(
1642
"utf-8", "replace").encode("utf-8")]
1643
self.addDetail("log", content.Content(content.ContentType("text",
1644
"plain", {"charset": "utf8"}),
1645
_get_log_contents_for_weird_testtools_api))
1646
self._log_file = pseudo_log_file
1647
self._log_memento = trace.push_log_file(self._log_file)
1462
1648
self.addCleanup(self._finishLogFile)
1464
1650
def _finishLogFile(self):
1513
1689
setattr(obj, attr_name, new)
1692
def overrideEnv(self, name, new):
1693
"""Set an environment variable, and reset it after the test.
1695
:param name: The environment variable name.
1697
:param new: The value to set the variable to. If None, the
1698
variable is deleted from the environment.
1700
:returns: The actual variable value.
1702
value = osutils.set_or_unset_env(name, new)
1703
self.addCleanup(osutils.set_or_unset_env, name, value)
1516
1706
def _cleanEnvironment(self):
1518
'BZR_HOME': None, # Don't inherit BZR_HOME to all the tests.
1519
'HOME': os.getcwd(),
1520
# bzr now uses the Win32 API and doesn't rely on APPDATA, but the
1521
# tests do check our impls match APPDATA
1522
'BZR_EDITOR': None, # test_msgeditor manipulates this variable
1526
'BZREMAIL': None, # may still be present in the environment
1528
'BZR_PROGRESS_BAR': None,
1530
'BZR_PLUGIN_PATH': None,
1531
'BZR_DISABLE_PLUGINS': None,
1532
'BZR_PLUGINS_AT': None,
1533
'BZR_CONCURRENCY': None,
1534
# Make sure that any text ui tests are consistent regardless of
1535
# the environment the test case is run in; you may want tests that
1536
# test other combinations. 'dumb' is a reasonable guess for tests
1537
# going to a pipe or a StringIO.
1541
'BZR_COLUMNS': '80',
1543
'SSH_AUTH_SOCK': None,
1547
'https_proxy': None,
1548
'HTTPS_PROXY': None,
1553
# Nobody cares about ftp_proxy, FTP_PROXY AFAIK. So far at
1554
# least. If you do (care), please update this comment
1558
'BZR_REMOTE_PATH': None,
1559
# Generally speaking, we don't want apport reporting on crashes in
1560
# the test envirnoment unless we're specifically testing apport,
1561
# so that it doesn't leak into the real system environment. We
1562
# use an env var so it propagates to subprocesses.
1563
'APPORT_DISABLE': '1',
1566
self.addCleanup(self._restoreEnvironment)
1567
for name, value in new_env.iteritems():
1568
self._captureVar(name, value)
1570
def _captureVar(self, name, newvalue):
1571
"""Set an environment variable, and reset it when finished."""
1572
self._old_env[name] = osutils.set_or_unset_env(name, newvalue)
1574
def _restoreEnvironment(self):
1575
for name, value in self._old_env.iteritems():
1576
osutils.set_or_unset_env(name, value)
1707
for name, value in isolated_environ.iteritems():
1708
self.overrideEnv(name, value)
1578
1710
def _restoreHooks(self):
1579
1711
for klass, (name, hooks) in self._preserved_hooks.items():
1580
1712
setattr(klass, name, hooks)
1713
self._preserved_hooks.clear()
1714
bzrlib.hooks._lazy_hooks = self._preserved_lazy_hooks
1715
self._preserved_lazy_hooks.clear()
1582
1717
def knownFailure(self, reason):
1583
1718
"""This test has failed for some known reason."""
1584
1719
raise KnownFailure(reason)
1721
def _suppress_log(self):
1722
"""Remove the log info from details."""
1723
self.discardDetail('log')
1586
1725
def _do_skip(self, result, reason):
1726
self._suppress_log()
1587
1727
addSkip = getattr(result, 'addSkip', None)
1588
1728
if not callable(addSkip):
1589
1729
result.addSuccess(result)
1612
1754
self._do_skip(result, reason)
1757
def _report_skip(self, result, err):
1758
"""Override the default _report_skip.
1760
We want to strip the 'log' detail. If we waint until _do_skip, it has
1761
already been formatted into the 'reason' string, and we can't pull it
1764
self._suppress_log()
1765
super(TestCase, self)._report_skip(self, result, err)
1768
def _report_expected_failure(self, result, err):
1771
See _report_skip for motivation.
1773
self._suppress_log()
1774
super(TestCase, self)._report_expected_failure(self, result, err)
1615
1777
def _do_unsupported_or_skip(self, result, e):
1616
1778
reason = e.args[0]
1779
self._suppress_log()
1617
1780
addNotSupported = getattr(result, 'addNotSupported', None)
1618
1781
if addNotSupported is not None:
1619
1782
result.addNotSupported(self, reason)
1645
1808
self._benchtime += time.time() - start
1647
1810
def log(self, *args):
1650
def _get_log(self, keep_log_file=False):
1651
"""Internal helper to get the log from bzrlib.trace for this test.
1653
Please use self.getDetails, or self.get_log to access this in test case
1656
:param keep_log_file: When True, if the log is still a file on disk
1657
leave it as a file on disk. When False, if the log is still a file
1658
on disk, the log file is deleted and the log preserved as
1660
:return: A string containing the log.
1662
if self._log_contents is not None:
1664
self._log_contents.decode('utf8')
1665
except UnicodeDecodeError:
1666
unicodestr = self._log_contents.decode('utf8', 'replace')
1667
self._log_contents = unicodestr.encode('utf8')
1668
return self._log_contents
1670
if bzrlib.trace._trace_file:
1671
# flush the log file, to get all content
1672
bzrlib.trace._trace_file.flush()
1673
if self._log_file_name is not None:
1674
logfile = open(self._log_file_name)
1676
log_contents = logfile.read()
1680
log_contents.decode('utf8')
1681
except UnicodeDecodeError:
1682
unicodestr = log_contents.decode('utf8', 'replace')
1683
log_contents = unicodestr.encode('utf8')
1684
if not keep_log_file:
1686
max_close_attempts = 100
1687
first_close_error = None
1688
while close_attempts < max_close_attempts:
1691
self._log_file.close()
1692
except IOError, ioe:
1693
if ioe.errno is None:
1694
# No errno implies 'close() called during
1695
# concurrent operation on the same file object', so
1696
# retry. Probably a thread is trying to write to
1698
if first_close_error is None:
1699
first_close_error = ioe
1704
if close_attempts > 1:
1706
'Unable to close log file on first attempt, '
1707
'will retry: %s\n' % (first_close_error,))
1708
if close_attempts == max_close_attempts:
1710
'Unable to close log file after %d attempts.\n'
1711
% (max_close_attempts,))
1712
self._log_file = None
1713
# Permit multiple calls to get_log until we clean it up in
1715
self._log_contents = log_contents
1717
os.remove(self._log_file_name)
1719
if sys.platform == 'win32' and e.errno == errno.EACCES:
1720
sys.stderr.write(('Unable to delete log file '
1721
' %r\n' % self._log_file_name))
1724
self._log_file_name = None
1727
return "No log file content and no log file name."
1729
1813
def get_log(self):
1730
1814
"""Get a unicode string containing the log from bzrlib.trace.
1945
2030
variables. A value of None will unset the env variable.
1946
2031
The values must be strings. The change will only occur in the
1947
2032
child, so you don't need to fix the environment after running.
1948
:param skip_if_plan_to_signal: raise TestSkipped when true and os.kill
2033
:param skip_if_plan_to_signal: raise TestSkipped when true and system
2034
doesn't support signalling subprocesses.
1950
2035
:param allow_plugins: If False (default) pass --no-plugins to bzr.
2036
:param stderr: file to use for the subprocess's stderr. Valid values
2037
are those valid for the stderr argument of `subprocess.Popen`.
2038
Default value is ``subprocess.PIPE``.
1952
2040
:returns: Popen object for the started process.
1954
2042
if skip_if_plan_to_signal:
1955
if not getattr(os, 'kill', None):
1956
raise TestSkipped("os.kill not available.")
2043
if os.name != "posix":
2044
raise TestSkipped("Sending signals not supported")
1958
2046
if env_changes is None:
1959
2047
env_changes = {}
2090
def _add_subprocess_log(self, log_file_path):
2091
if len(self._log_files) == 0:
2092
# Register an addCleanup func. We do this on the first call to
2093
# _add_subprocess_log rather than in TestCase.setUp so that this
2094
# addCleanup is registered after any cleanups for tempdirs that
2095
# subclasses might create, which will probably remove the log file
2097
self.addCleanup(self._subprocess_log_cleanup)
2098
# self._log_files is a set, so if a log file is reused we won't grab it
2100
self._log_files.add(log_file_path)
2102
def _subprocess_log_cleanup(self):
2103
for count, log_file_path in enumerate(self._log_files):
2104
# We use buffer_now=True to avoid holding the file open beyond
2105
# the life of this function, which might interfere with e.g.
2106
# cleaning tempdirs on Windows.
2107
# XXX: Testtools 0.9.5 doesn't have the content_from_file helper
2108
#detail_content = content.content_from_file(
2109
# log_file_path, buffer_now=True)
2110
with open(log_file_path, 'rb') as log_file:
2111
log_file_bytes = log_file.read()
2112
detail_content = content.Content(content.ContentType("text",
2113
"plain", {"charset": "utf8"}), lambda: [log_file_bytes])
2114
self.addDetail("start_bzr_subprocess-log-%d" % (count,),
1997
2117
def _popen(self, *args, **kwargs):
1998
2118
"""Place a call to Popen.
2000
2120
Allows tests to override this method to intercept the calls made to
2001
2121
Popen for introspection.
2003
return Popen(*args, **kwargs)
2123
return subprocess.Popen(*args, **kwargs)
2005
2125
def get_source_path(self):
2006
2126
"""Return the path of the directory containing bzrlib."""
2036
2156
if retcode is not None and retcode != process.returncode:
2037
2157
if process_args is None:
2038
2158
process_args = "(unknown args)"
2039
mutter('Output of bzr %s:\n%s', process_args, out)
2040
mutter('Error for bzr %s:\n%s', process_args, err)
2159
trace.mutter('Output of bzr %s:\n%s', process_args, out)
2160
trace.mutter('Error for bzr %s:\n%s', process_args, err)
2041
2161
self.fail('Command bzr %s failed with retcode %s != %s'
2042
2162
% (process_args, retcode, process.returncode))
2043
2163
return [out, err]
2045
def check_inventory_shape(self, inv, shape):
2046
"""Compare an inventory to a list of expected names.
2165
def check_tree_shape(self, tree, shape):
2166
"""Compare a tree to a list of expected names.
2048
2168
Fail if they are not precisely equal.
2051
2171
shape = list(shape) # copy
2052
for path, ie in inv.entries():
2172
for path, ie in tree.iter_entries_by_dir():
2053
2173
name = path.replace('\\', '/')
2054
2174
if ie.kind == 'directory':
2055
2175
name = name + '/'
2177
pass # ignore root entry
2057
2179
shape.remove(name)
2059
2181
extras.append(name)
2408
2530
made_control = self.make_bzrdir(relpath, format=format)
2409
2531
return made_control.create_repository(shared=shared)
2411
def make_smart_server(self, path):
2533
def make_smart_server(self, path, backing_server=None):
2534
if backing_server is None:
2535
backing_server = self.get_server()
2412
2536
smart_server = test_server.SmartTCPServer_for_testing()
2413
self.start_server(smart_server, self.get_server())
2414
remote_transport = get_transport(smart_server.get_url()).clone(path)
2537
self.start_server(smart_server, backing_server)
2538
remote_transport = _mod_transport.get_transport(smart_server.get_url()
2415
2540
return remote_transport
2417
2542
def make_branch_and_memory_tree(self, relpath, format=None):
2427
2552
test_home_dir = self.test_home_dir
2428
2553
if isinstance(test_home_dir, unicode):
2429
2554
test_home_dir = test_home_dir.encode(sys.getfilesystemencoding())
2430
os.environ['HOME'] = test_home_dir
2431
os.environ['BZR_HOME'] = test_home_dir
2555
self.overrideEnv('HOME', test_home_dir)
2556
self.overrideEnv('BZR_HOME', test_home_dir)
2433
2558
def setUp(self):
2434
2559
super(TestCaseWithMemoryTransport, self).setUp()
2560
# Ensure that ConnectedTransport doesn't leak sockets
2561
def get_transport_with_cleanup(*args, **kwargs):
2562
t = orig_get_transport(*args, **kwargs)
2563
if isinstance(t, _mod_transport.ConnectedTransport):
2564
self.addCleanup(t.disconnect)
2567
orig_get_transport = self.overrideAttr(_mod_transport, 'get_transport',
2568
get_transport_with_cleanup)
2435
2569
self._make_test_root()
2436
2570
self.addCleanup(os.chdir, os.getcwdu())
2437
2571
self.makeAndChdirToTestDir()
3191
3331
def partition_tests(suite, count):
3192
3332
"""Partition suite into count lists of tests."""
3194
tests = list(iter_suite_tests(suite))
3195
tests_per_process = int(math.ceil(float(len(tests)) / count))
3196
for block in range(count):
3197
low_test = block * tests_per_process
3198
high_test = low_test + tests_per_process
3199
process_tests = tests[low_test:high_test]
3200
result.append(process_tests)
3333
# This just assigns tests in a round-robin fashion. On one hand this
3334
# splits up blocks of related tests that might run faster if they shared
3335
# resources, but on the other it avoids assigning blocks of slow tests to
3336
# just one partition. So the slowest partition shouldn't be much slower
3338
partitions = [list() for i in range(count)]
3339
tests = iter_suite_tests(suite)
3340
for partition, test in itertools.izip(itertools.cycle(partitions), tests):
3341
partition.append(test)
3204
3345
def workaround_zealous_crypto_random():
3311
3452
if '--no-plugins' in sys.argv:
3312
3453
argv.append('--no-plugins')
3313
# stderr=STDOUT would be ideal, but until we prevent noise on
3314
# stderr it can interrupt the subunit protocol.
3315
process = Popen(argv, stdin=PIPE, stdout=PIPE, stderr=PIPE,
3454
# stderr=subprocess.STDOUT would be ideal, but until we prevent
3455
# noise on stderr it can interrupt the subunit protocol.
3456
process = subprocess.Popen(argv, stdin=subprocess.PIPE,
3457
stdout=subprocess.PIPE,
3458
stderr=subprocess.PIPE,
3317
3460
test = TestInSubprocess(process, test_list_file_name)
3318
3461
result.append(test)
3325
class ForwardingResult(unittest.TestResult):
3327
def __init__(self, target):
3328
unittest.TestResult.__init__(self)
3329
self.result = target
3331
def startTest(self, test):
3332
self.result.startTest(test)
3334
def stopTest(self, test):
3335
self.result.stopTest(test)
3337
def startTestRun(self):
3338
self.result.startTestRun()
3340
def stopTestRun(self):
3341
self.result.stopTestRun()
3343
def addSkip(self, test, reason):
3344
self.result.addSkip(test, reason)
3346
def addSuccess(self, test):
3347
self.result.addSuccess(test)
3349
def addError(self, test, err):
3350
self.result.addError(test, err)
3352
def addFailure(self, test, err):
3353
self.result.addFailure(test, err)
3354
ForwardingResult = testtools.ExtendedToOriginalDecorator
3357
class ProfileResult(ForwardingResult):
3468
class ProfileResult(testtools.ExtendedToOriginalDecorator):
3358
3469
"""Generate profiling data for all activity between start and success.
3360
3471
The profile data is appended to the test's _benchcalls attribute and can
3633
3750
'bzrlib.tests.blackbox',
3634
3751
'bzrlib.tests.commands',
3752
'bzrlib.tests.doc_generate',
3635
3753
'bzrlib.tests.per_branch',
3636
3754
'bzrlib.tests.per_bzrdir',
3637
'bzrlib.tests.per_bzrdir_colo',
3755
'bzrlib.tests.per_controldir',
3756
'bzrlib.tests.per_controldir_colo',
3638
3757
'bzrlib.tests.per_foreign_vcs',
3639
3758
'bzrlib.tests.per_interrepository',
3640
3759
'bzrlib.tests.per_intertree',
3648
3767
'bzrlib.tests.per_repository',
3649
3768
'bzrlib.tests.per_repository_chk',
3650
3769
'bzrlib.tests.per_repository_reference',
3770
'bzrlib.tests.per_repository_vf',
3651
3771
'bzrlib.tests.per_uifactory',
3652
3772
'bzrlib.tests.per_versionedfile',
3653
3773
'bzrlib.tests.per_workingtree',
3654
3774
'bzrlib.tests.test__annotator',
3655
3775
'bzrlib.tests.test__bencode',
3776
'bzrlib.tests.test__btree_serializer',
3656
3777
'bzrlib.tests.test__chk_map',
3657
3778
'bzrlib.tests.test__dirstate_helpers',
3658
3779
'bzrlib.tests.test__groupcompress',
3686
3807
'bzrlib.tests.test_commit_merge',
3687
3808
'bzrlib.tests.test_config',
3688
3809
'bzrlib.tests.test_conflicts',
3810
'bzrlib.tests.test_controldir',
3689
3811
'bzrlib.tests.test_counted_lock',
3690
3812
'bzrlib.tests.test_crash',
3691
3813
'bzrlib.tests.test_decorators',
3692
3814
'bzrlib.tests.test_delta',
3693
3815
'bzrlib.tests.test_debug',
3694
'bzrlib.tests.test_deprecated_graph',
3695
3816
'bzrlib.tests.test_diff',
3696
3817
'bzrlib.tests.test_directory_service',
3697
3818
'bzrlib.tests.test_dirstate',
3699
3820
'bzrlib.tests.test_eol_filters',
3700
3821
'bzrlib.tests.test_errors',
3701
3822
'bzrlib.tests.test_export',
3823
'bzrlib.tests.test_export_pot',
3702
3824
'bzrlib.tests.test_extract',
3703
3825
'bzrlib.tests.test_fetch',
3826
'bzrlib.tests.test_fixtures',
3704
3827
'bzrlib.tests.test_fifo_cache',
3705
3828
'bzrlib.tests.test_filters',
3706
3829
'bzrlib.tests.test_ftp_transport',
3734
3859
'bzrlib.tests.test_lru_cache',
3735
3860
'bzrlib.tests.test_lsprof',
3736
3861
'bzrlib.tests.test_mail_client',
3862
'bzrlib.tests.test_matchers',
3737
3863
'bzrlib.tests.test_memorytree',
3738
3864
'bzrlib.tests.test_merge',
3739
3865
'bzrlib.tests.test_merge3',
3740
3866
'bzrlib.tests.test_merge_core',
3741
3867
'bzrlib.tests.test_merge_directive',
3868
'bzrlib.tests.test_mergetools',
3742
3869
'bzrlib.tests.test_missing',
3743
3870
'bzrlib.tests.test_msgeditor',
3744
3871
'bzrlib.tests.test_multiparent',
3788
3917
'bzrlib.tests.test_switch',
3789
3918
'bzrlib.tests.test_symbol_versioning',
3790
3919
'bzrlib.tests.test_tag',
3920
'bzrlib.tests.test_test_server',
3791
3921
'bzrlib.tests.test_testament',
3792
3922
'bzrlib.tests.test_textfile',
3793
3923
'bzrlib.tests.test_textmerge',
3924
'bzrlib.tests.test_cethread',
3794
3925
'bzrlib.tests.test_timestamp',
3795
3926
'bzrlib.tests.test_trace',
3796
3927
'bzrlib.tests.test_transactions',
3806
3938
'bzrlib.tests.test_upgrade',
3807
3939
'bzrlib.tests.test_upgrade_stacked',
3808
3940
'bzrlib.tests.test_urlutils',
3941
'bzrlib.tests.test_utextwrap',
3809
3942
'bzrlib.tests.test_version',
3810
3943
'bzrlib.tests.test_version_info',
3944
'bzrlib.tests.test_versionedfile',
3811
3945
'bzrlib.tests.test_weave',
3812
3946
'bzrlib.tests.test_whitebox',
3813
3947
'bzrlib.tests.test_win32utils',
3937
4073
# Some tests mentioned in the list are not in the test suite. The
3938
4074
# list may be out of date, report to the tester.
3939
4075
for id in not_found:
3940
bzrlib.trace.warning('"%s" not found in the test suite', id)
4076
trace.warning('"%s" not found in the test suite', id)
3941
4077
for id in duplicates:
3942
bzrlib.trace.warning('"%s" is used as an id by several tests', id)
4078
trace.warning('"%s" is used as an id by several tests', id)
3947
def multiply_scenarios(scenarios_left, scenarios_right):
4083
def multiply_scenarios(*scenarios):
4084
"""Multiply two or more iterables of scenarios.
4086
It is safe to pass scenario generators or iterators.
4088
:returns: A list of compound scenarios: the cross-product of all
4089
scenarios, with the names concatenated and the parameters
4092
return reduce(_multiply_two_scenarios, map(list, scenarios))
4095
def _multiply_two_scenarios(scenarios_left, scenarios_right):
3948
4096
"""Multiply two sets of scenarios.
3950
4098
:returns: the cartesian product of the two sets of scenarios, that is
4445
4611
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
4448
# Kept for compatibility, use bzrlib.tests.features.subunit instead
4449
SubUnitFeature = _CompatabilityThunkFeature(
4450
deprecated_in((2,1,0)),
4451
'bzrlib.tests.features', 'SubUnitFeature', 'subunit')
4452
4614
# Only define SubUnitBzrRunner if subunit is available.
4454
4616
from subunit import TestProtocolClient
4455
4617
from subunit.test_results import AutoTimingTestResultDecorator
4618
class SubUnitBzrProtocolClient(TestProtocolClient):
4620
def addSuccess(self, test, details=None):
4621
# The subunit client always includes the details in the subunit
4622
# stream, but we don't want to include it in ours.
4623
if details is not None and 'log' in details:
4625
return super(SubUnitBzrProtocolClient, self).addSuccess(
4456
4628
class SubUnitBzrRunner(TextTestRunner):
4457
4629
def run(self, test):
4458
4630
result = AutoTimingTestResultDecorator(
4459
TestProtocolClient(self.stream))
4631
SubUnitBzrProtocolClient(self.stream))
4460
4632
test.run(result)
4462
4634
except ImportError: