750
749
self.check_timing(ShortDelayTestCase('test_short_delay'),
753
def _patch_get_bzr_source_tree(self):
754
# Reading from the actual source tree breaks isolation, but we don't
755
# want to assume that thats *all* that would happen.
756
self.overrideAttr(bzrlib.version, '_get_bzr_source_tree', lambda: None)
758
def test_assigned_benchmark_file_stores_date(self):
759
self._patch_get_bzr_source_tree()
761
result = bzrlib.tests.TextTestResult(self._log_file,
766
output_string = output.getvalue()
767
# if you are wondering about the regexp please read the comment in
768
# test_bench_history (bzrlib.tests.test_selftest.TestRunner)
769
# XXX: what comment? -- Andrew Bennetts
770
self.assertContainsRe(output_string, "--date [0-9.]+")
772
def test_benchhistory_records_test_times(self):
773
self._patch_get_bzr_source_tree()
774
result_stream = StringIO()
775
result = bzrlib.tests.TextTestResult(
779
bench_history=result_stream
782
# we want profile a call and check that its test duration is recorded
783
# make a new test instance that when run will generate a benchmark
784
example_test_case = TestTestResult("_time_hello_world_encoding")
785
# execute the test, which should succeed and record times
786
example_test_case.run(result)
787
lines = result_stream.getvalue().splitlines()
788
self.assertEqual(2, len(lines))
789
self.assertContainsRe(lines[1],
790
" *[0-9]+ms bzrlib.tests.test_selftest.TestTestResult"
791
"._time_hello_world_encoding")
793
752
def _time_hello_world_encoding(self):
794
753
"""Profile two sleep calls
835
794
self.assertContainsRe(output,
836
795
r"LSProf output for <type 'unicode'>\(\('world',\), {'errors': 'replace'}\)\n")
797
def test_uses_time_from_testtools(self):
798
"""Test case timings in verbose results should use testtools times"""
800
class TimeAddedVerboseTestResult(tests.VerboseTestResult):
801
def startTest(self, test):
802
self.time(datetime.datetime.utcfromtimestamp(1.145))
803
super(TimeAddedVerboseTestResult, self).startTest(test)
804
def addSuccess(self, test):
805
self.time(datetime.datetime.utcfromtimestamp(51.147))
806
super(TimeAddedVerboseTestResult, self).addSuccess(test)
807
def report_tests_starting(self): pass
809
self.get_passing_test().run(TimeAddedVerboseTestResult(sio, 0, 2))
810
self.assertEndsWith(sio.getvalue(), "OK 50002ms\n")
838
812
def test_known_failure(self):
839
813
"""A KnownFailure being raised should trigger several result actions."""
840
814
class InstrumentedTestResult(tests.ExtendedTestResult):
841
815
def stopTestRun(self): pass
842
def startTests(self): pass
843
def report_test_start(self, test): pass
816
def report_tests_starting(self): pass
844
817
def report_known_failure(self, test, err=None, details=None):
845
818
self._call = test, 'known failure'
846
819
result = InstrumentedTestResult(None, None, None, None)
1193
def _patch_get_bzr_source_tree(self):
1194
# Reading from the actual source tree breaks isolation, but we don't
1195
# want to assume that thats *all* that would happen.
1196
self._get_source_tree_calls = []
1198
self._get_source_tree_calls.append("called")
1200
self.overrideAttr(bzrlib.version, '_get_bzr_source_tree', new_get)
1202
def test_bench_history(self):
1203
# tests that the running the benchmark passes bench_history into
1204
# the test result object. We can tell that happens if
1205
# _get_bzr_source_tree is called.
1206
self._patch_get_bzr_source_tree()
1207
test = TestRunner('dummy_test')
1209
runner = tests.TextTestRunner(stream=self._log_file,
1210
bench_history=output)
1211
result = self.run_test_runner(runner, test)
1212
output_string = output.getvalue()
1213
self.assertContainsRe(output_string, "--date [0-9.]+")
1214
self.assertLength(1, self._get_source_tree_calls)
1176
def test_verbose_test_count(self):
1177
"""A verbose test run reports the right test count at the start"""
1178
suite = TestUtil.TestSuite([
1179
unittest.FunctionTestCase(lambda:None),
1180
unittest.FunctionTestCase(lambda:None)])
1181
self.assertEqual(suite.countTestCases(), 2)
1183
runner = tests.TextTestRunner(stream=stream, verbosity=2)
1184
# Need to use the CountingDecorator as that's what sets num_tests
1185
result = self.run_test_runner(runner, tests.CountingDecorator(suite))
1186
self.assertStartsWith(stream.getvalue(), "running 2 tests")
1216
1188
def test_startTestRun(self):
1217
1189
"""run should call result.startTestRun()"""
1219
class LoggingDecorator(tests.ForwardingResult):
1191
class LoggingDecorator(ExtendedToOriginalDecorator):
1220
1192
def startTestRun(self):
1221
tests.ForwardingResult.startTestRun(self)
1193
ExtendedToOriginalDecorator.startTestRun(self)
1222
1194
calls.append('startTestRun')
1223
1195
test = unittest.FunctionTestCase(lambda:None)
1224
1196
stream = StringIO()
1655
1644
self.assertEqual('original', obj.test_attr)
1647
class _MissingFeature(tests.Feature):
1650
missing_feature = _MissingFeature()
1653
def _get_test(name):
1654
"""Get an instance of a specific example test.
1656
We protect this in a function so that they don't auto-run in the test
1660
class ExampleTests(tests.TestCase):
1662
def test_fail(self):
1663
mutter('this was a failing test')
1664
self.fail('this test will fail')
1666
def test_error(self):
1667
mutter('this test errored')
1668
raise RuntimeError('gotcha')
1670
def test_missing_feature(self):
1671
mutter('missing the feature')
1672
self.requireFeature(missing_feature)
1674
def test_skip(self):
1675
mutter('this test will be skipped')
1676
raise tests.TestSkipped('reason')
1678
def test_success(self):
1679
mutter('this test succeeds')
1681
def test_xfail(self):
1682
mutter('test with expected failure')
1683
self.knownFailure('this_fails')
1685
def test_unexpected_success(self):
1686
mutter('test with unexpected success')
1687
self.expectFailure('should_fail', lambda: None)
1689
return ExampleTests(name)
1692
class TestTestCaseLogDetails(tests.TestCase):
1694
def _run_test(self, test_name):
1695
test = _get_test(test_name)
1696
result = testtools.TestResult()
1700
def test_fail_has_log(self):
1701
result = self._run_test('test_fail')
1702
self.assertEqual(1, len(result.failures))
1703
result_content = result.failures[0][1]
1704
self.assertContainsRe(result_content, 'Text attachment: log')
1705
self.assertContainsRe(result_content, 'this was a failing test')
1707
def test_error_has_log(self):
1708
result = self._run_test('test_error')
1709
self.assertEqual(1, len(result.errors))
1710
result_content = result.errors[0][1]
1711
self.assertContainsRe(result_content, 'Text attachment: log')
1712
self.assertContainsRe(result_content, 'this test errored')
1714
def test_skip_has_no_log(self):
1715
result = self._run_test('test_skip')
1716
self.assertEqual(['reason'], result.skip_reasons.keys())
1717
skips = result.skip_reasons['reason']
1718
self.assertEqual(1, len(skips))
1720
self.assertFalse('log' in test.getDetails())
1722
def test_missing_feature_has_no_log(self):
1723
# testtools doesn't know about addNotSupported, so it just gets
1724
# considered as a skip
1725
result = self._run_test('test_missing_feature')
1726
self.assertEqual([missing_feature], result.skip_reasons.keys())
1727
skips = result.skip_reasons[missing_feature]
1728
self.assertEqual(1, len(skips))
1730
self.assertFalse('log' in test.getDetails())
1732
def test_xfail_has_no_log(self):
1733
result = self._run_test('test_xfail')
1734
self.assertEqual(1, len(result.expectedFailures))
1735
result_content = result.expectedFailures[0][1]
1736
self.assertNotContainsRe(result_content, 'Text attachment: log')
1737
self.assertNotContainsRe(result_content, 'test with expected failure')
1739
def test_unexpected_success_has_log(self):
1740
result = self._run_test('test_unexpected_success')
1741
self.assertEqual(1, len(result.unexpectedSuccesses))
1742
# Inconsistency, unexpectedSuccesses is a list of tests,
1743
# expectedFailures is a list of reasons?
1744
test = result.unexpectedSuccesses[0]
1745
details = test.getDetails()
1746
self.assertTrue('log' in details)
1749
class TestTestCloning(tests.TestCase):
1750
"""Tests that test cloning of TestCases (as used by multiply_tests)."""
1752
def test_cloned_testcase_does_not_share_details(self):
1753
"""A TestCase cloned with clone_test does not share mutable attributes
1754
such as details or cleanups.
1756
class Test(tests.TestCase):
1758
self.addDetail('foo', Content('text/plain', lambda: 'foo'))
1759
orig_test = Test('test_foo')
1760
cloned_test = tests.clone_test(orig_test, orig_test.id() + '(cloned)')
1761
orig_test.run(unittest.TestResult())
1762
self.assertEqual('foo', orig_test.getDetails()['foo'].iter_bytes())
1763
self.assertEqual(None, cloned_test.getDetails().get('foo'))
1765
def test_double_apply_scenario_preserves_first_scenario(self):
1766
"""Applying two levels of scenarios to a test preserves the attributes
1767
added by both scenarios.
1769
class Test(tests.TestCase):
1772
test = Test('test_foo')
1773
scenarios_x = [('x=1', {'x': 1}), ('x=2', {'x': 2})]
1774
scenarios_y = [('y=1', {'y': 1}), ('y=2', {'y': 2})]
1775
suite = tests.multiply_tests(test, scenarios_x, unittest.TestSuite())
1776
suite = tests.multiply_tests(suite, scenarios_y, unittest.TestSuite())
1777
all_tests = list(tests.iter_suite_tests(suite))
1778
self.assertLength(4, all_tests)
1779
all_xys = sorted((t.x, t.y) for t in all_tests)
1780
self.assertEqual([(1, 1), (1, 2), (2, 1), (2, 2)], all_xys)
1658
1783
# NB: Don't delete this; it's not actually from 0.11!
1659
1784
@deprecated_function(deprecated_in((0, 11, 0)))
1660
1785
def sample_deprecated_function():
1971
2093
load_list='missing file name', list_only=True)
2096
class TestSubunitLogDetails(tests.TestCase, SelfTestHelper):
2098
_test_needs_features = [features.subunit]
2100
def run_subunit_stream(self, test_name):
2101
from subunit import ProtocolTestCase
2103
return TestUtil.TestSuite([_get_test(test_name)])
2104
stream = self.run_selftest(runner_class=tests.SubUnitBzrRunner,
2105
test_suite_factory=factory)
2106
test = ProtocolTestCase(stream)
2107
result = testtools.TestResult()
2109
content = stream.getvalue()
2110
return content, result
2112
def test_fail_has_log(self):
2113
content, result = self.run_subunit_stream('test_fail')
2114
self.assertEqual(1, len(result.failures))
2115
self.assertContainsRe(content, '(?m)^log$')
2116
self.assertContainsRe(content, 'this test will fail')
2118
def test_error_has_log(self):
2119
content, result = self.run_subunit_stream('test_error')
2120
self.assertContainsRe(content, '(?m)^log$')
2121
self.assertContainsRe(content, 'this test errored')
2123
def test_skip_has_no_log(self):
2124
content, result = self.run_subunit_stream('test_skip')
2125
self.assertNotContainsRe(content, '(?m)^log$')
2126
self.assertNotContainsRe(content, 'this test will be skipped')
2127
self.assertEqual(['reason'], result.skip_reasons.keys())
2128
skips = result.skip_reasons['reason']
2129
self.assertEqual(1, len(skips))
2131
# RemotedTestCase doesn't preserve the "details"
2132
## self.assertFalse('log' in test.getDetails())
2134
def test_missing_feature_has_no_log(self):
2135
content, result = self.run_subunit_stream('test_missing_feature')
2136
self.assertNotContainsRe(content, '(?m)^log$')
2137
self.assertNotContainsRe(content, 'missing the feature')
2138
self.assertEqual(['_MissingFeature\n'], result.skip_reasons.keys())
2139
skips = result.skip_reasons['_MissingFeature\n']
2140
self.assertEqual(1, len(skips))
2142
# RemotedTestCase doesn't preserve the "details"
2143
## self.assertFalse('log' in test.getDetails())
2145
def test_xfail_has_no_log(self):
2146
content, result = self.run_subunit_stream('test_xfail')
2147
self.assertNotContainsRe(content, '(?m)^log$')
2148
self.assertNotContainsRe(content, 'test with expected failure')
2149
self.assertEqual(1, len(result.expectedFailures))
2150
result_content = result.expectedFailures[0][1]
2151
self.assertNotContainsRe(result_content, 'Text attachment: log')
2152
self.assertNotContainsRe(result_content, 'test with expected failure')
2154
def test_unexpected_success_has_log(self):
2155
content, result = self.run_subunit_stream('test_unexpected_success')
2156
self.assertContainsRe(content, '(?m)^log$')
2157
self.assertContainsRe(content, 'test with unexpected success')
2158
self.expectFailure('subunit treats "unexpectedSuccess"'
2159
' as a plain success',
2160
self.assertEqual, 1, len(result.unexpectedSuccesses))
2161
self.assertEqual(1, len(result.unexpectedSuccesses))
2162
test = result.unexpectedSuccesses[0]
2163
# RemotedTestCase doesn't preserve the "details"
2164
## self.assertTrue('log' in test.getDetails())
2166
def test_success_has_no_log(self):
2167
content, result = self.run_subunit_stream('test_success')
2168
self.assertEqual(1, result.testsRun)
2169
self.assertNotContainsRe(content, '(?m)^log$')
2170
self.assertNotContainsRe(content, 'this test succeeds')
1974
2173
class TestRunBzr(tests.TestCase):
2960
3165
self.assertEquals('bzrlib.plugins', tpr.resolve_alias('bp'))
3168
class TestThreadLeakDetection(tests.TestCase):
3169
"""Ensure when tests leak threads we detect and report it"""
3171
class LeakRecordingResult(tests.ExtendedTestResult):
3173
tests.ExtendedTestResult.__init__(self, StringIO(), 0, 1)
3175
def _report_thread_leak(self, test, leaks, alive):
3176
self.leaks.append((test, leaks))
3178
def test_testcase_without_addCleanups(self):
3179
"""Check old TestCase instances don't break with leak detection"""
3180
class Test(unittest.TestCase):
3183
result = self.LeakRecordingResult()
3185
result.startTestRun()
3187
result.stopTestRun()
3188
self.assertEqual(result._tests_leaking_threads_count, 0)
3189
self.assertEqual(result.leaks, [])
3191
def test_thread_leak(self):
3192
"""Ensure a thread that outlives the running of a test is reported
3194
Uses a thread that blocks on an event, and is started by the inner
3195
test case. As the thread outlives the inner case's run, it should be
3196
detected as a leak, but the event is then set so that the thread can
3197
be safely joined in cleanup so it's not leaked for real.
3199
event = threading.Event()
3200
thread = threading.Thread(name="Leaker", target=event.wait)
3201
class Test(tests.TestCase):
3202
def test_leak(self):
3204
result = self.LeakRecordingResult()
3205
test = Test("test_leak")
3206
self.addCleanup(thread.join)
3207
self.addCleanup(event.set)
3208
result.startTestRun()
3210
result.stopTestRun()
3211
self.assertEqual(result._tests_leaking_threads_count, 1)
3212
self.assertEqual(result._first_thread_leaker_id, test.id())
3213
self.assertEqual(result.leaks, [(test, set([thread]))])
3214
self.assertContainsString(result.stream.getvalue(), "leaking threads")
3216
def test_multiple_leaks(self):
3217
"""Check multiple leaks are blamed on the test cases at fault
3219
Same concept as the previous test, but has one inner test method that
3220
leaks two threads, and one that doesn't leak at all.
3222
event = threading.Event()
3223
thread_a = threading.Thread(name="LeakerA", target=event.wait)
3224
thread_b = threading.Thread(name="LeakerB", target=event.wait)
3225
thread_c = threading.Thread(name="LeakerC", target=event.wait)
3226
class Test(tests.TestCase):
3227
def test_first_leak(self):
3229
def test_second_no_leak(self):
3231
def test_third_leak(self):
3234
result = self.LeakRecordingResult()
3235
first_test = Test("test_first_leak")
3236
third_test = Test("test_third_leak")
3237
self.addCleanup(thread_a.join)
3238
self.addCleanup(thread_b.join)
3239
self.addCleanup(thread_c.join)
3240
self.addCleanup(event.set)
3241
result.startTestRun()
3243
[first_test, Test("test_second_no_leak"), third_test]
3245
result.stopTestRun()
3246
self.assertEqual(result._tests_leaking_threads_count, 2)
3247
self.assertEqual(result._first_thread_leaker_id, first_test.id())
3248
self.assertEqual(result.leaks, [
3249
(first_test, set([thread_b])),
3250
(third_test, set([thread_a, thread_c]))])
3251
self.assertContainsString(result.stream.getvalue(), "leaking threads")
3254
class TestPostMortemDebugging(tests.TestCase):
3255
"""Check post mortem debugging works when tests fail or error"""
3257
class TracebackRecordingResult(tests.ExtendedTestResult):
3259
tests.ExtendedTestResult.__init__(self, StringIO(), 0, 1)
3260
self.postcode = None
3261
def _post_mortem(self, tb=None):
3262
"""Record the code object at the end of the current traceback"""
3263
tb = tb or sys.exc_info()[2]
3266
while next is not None:
3269
self.postcode = tb.tb_frame.f_code
3270
def report_error(self, test, err):
3272
def report_failure(self, test, err):
3275
def test_location_unittest_error(self):
3276
"""Needs right post mortem traceback with erroring unittest case"""
3277
class Test(unittest.TestCase):
3280
result = self.TracebackRecordingResult()
3282
self.assertEqual(result.postcode, Test.runTest.func_code)
3284
def test_location_unittest_failure(self):
3285
"""Needs right post mortem traceback with failing unittest case"""
3286
class Test(unittest.TestCase):
3288
raise self.failureException
3289
result = self.TracebackRecordingResult()
3291
self.assertEqual(result.postcode, Test.runTest.func_code)
3293
def test_location_bt_error(self):
3294
"""Needs right post mortem traceback with erroring bzrlib.tests case"""
3295
class Test(tests.TestCase):
3296
def test_error(self):
3298
result = self.TracebackRecordingResult()
3299
Test("test_error").run(result)
3300
self.assertEqual(result.postcode, Test.test_error.func_code)
3302
def test_location_bt_failure(self):
3303
"""Needs right post mortem traceback with failing bzrlib.tests case"""
3304
class Test(tests.TestCase):
3305
def test_failure(self):
3306
raise self.failureException
3307
result = self.TracebackRecordingResult()
3308
Test("test_failure").run(result)
3309
self.assertEqual(result.postcode, Test.test_failure.func_code)
3311
def test_env_var_triggers_post_mortem(self):
3312
"""Check pdb.post_mortem is called iff BZR_TEST_PDB is set"""
3314
result = tests.ExtendedTestResult(StringIO(), 0, 1)
3315
post_mortem_calls = []
3316
self.overrideAttr(pdb, "post_mortem", post_mortem_calls.append)
3317
self.overrideEnv('BZR_TEST_PDB', None)
3318
result._post_mortem(1)
3319
self.overrideEnv('BZR_TEST_PDB', 'on')
3320
result._post_mortem(2)
3321
self.assertEqual([2], post_mortem_calls)
2963
3324
class TestRunSuite(tests.TestCase):
2965
3326
def test_runner_class(self):
2976
3337
self.verbosity)
2977
3338
tests.run_suite(suite, runner_class=MyRunner, stream=StringIO())
2978
3339
self.assertLength(1, calls)
3342
class TestEnvironHandling(tests.TestCase):
3344
def test_overrideEnv_None_called_twice_doesnt_leak(self):
3345
self.failIf('MYVAR' in os.environ)
3346
self.overrideEnv('MYVAR', '42')
3347
# We use an embedded test to make sure we fix the _captureVar bug
3348
class Test(tests.TestCase):
3350
# The first call save the 42 value
3351
self.overrideEnv('MYVAR', None)
3352
self.assertEquals(None, os.environ.get('MYVAR'))
3353
# Make sure we can call it twice
3354
self.overrideEnv('MYVAR', None)
3355
self.assertEquals(None, os.environ.get('MYVAR'))
3357
result = tests.TextTestResult(output, 0, 1)
3358
Test('test_me').run(result)
3359
if not result.wasStrictlySuccessful():
3360
self.fail(output.getvalue())
3361
# We get our value back
3362
self.assertEquals('42', os.environ.get('MYVAR'))
3365
class TestIsolatedEnv(tests.TestCase):
3366
"""Test isolating tests from os.environ.
3368
Since we use tests that are already isolated from os.environ a bit of care
3369
should be taken when designing the tests to avoid bootstrap side-effects.
3370
The tests start an already clean os.environ which allow doing valid
3371
assertions about which variables are present or not and design tests around
3375
class ScratchMonkey(tests.TestCase):
3380
def test_basics(self):
3381
# Make sure we know the definition of BZR_HOME: not part of os.environ
3382
# for tests.TestCase.
3383
self.assertTrue('BZR_HOME' in tests.isolated_environ)
3384
self.assertEquals(None, tests.isolated_environ['BZR_HOME'])
3385
# Being part of isolated_environ, BZR_HOME should not appear here
3386
self.assertFalse('BZR_HOME' in os.environ)
3387
# Make sure we know the definition of LINES: part of os.environ for
3389
self.assertTrue('LINES' in tests.isolated_environ)
3390
self.assertEquals('25', tests.isolated_environ['LINES'])
3391
self.assertEquals('25', os.environ['LINES'])
3393
def test_injecting_unknown_variable(self):
3394
# BZR_HOME is known to be absent from os.environ
3395
test = self.ScratchMonkey('test_me')
3396
tests.override_os_environ(test, {'BZR_HOME': 'foo'})
3397
self.assertEquals('foo', os.environ['BZR_HOME'])
3398
tests.restore_os_environ(test)
3399
self.assertFalse('BZR_HOME' in os.environ)
3401
def test_injecting_known_variable(self):
3402
test = self.ScratchMonkey('test_me')
3403
# LINES is known to be present in os.environ
3404
tests.override_os_environ(test, {'LINES': '42'})
3405
self.assertEquals('42', os.environ['LINES'])
3406
tests.restore_os_environ(test)
3407
self.assertEquals('25', os.environ['LINES'])
3409
def test_deleting_variable(self):
3410
test = self.ScratchMonkey('test_me')
3411
# LINES is known to be present in os.environ
3412
tests.override_os_environ(test, {'LINES': None})
3413
self.assertTrue('LINES' not in os.environ)
3414
tests.restore_os_environ(test)
3415
self.assertEquals('25', os.environ['LINES'])
3418
class TestDocTestSuiteIsolation(tests.TestCase):
3419
"""Test that `tests.DocTestSuite` isolates doc tests from os.environ.
3421
Since tests.TestCase alreay provides an isolation from os.environ, we use
3422
the clean environment as a base for testing. To precisely capture the
3423
isolation provided by tests.DocTestSuite, we use doctest.DocTestSuite to
3426
We want to make sure `tests.DocTestSuite` respect `tests.isolated_environ`,
3427
not `os.environ` so each test overrides it to suit its needs.
3431
def get_doctest_suite_for_string(self, klass, string):
3432
class Finder(doctest.DocTestFinder):
3434
def find(*args, **kwargs):
3435
test = doctest.DocTestParser().get_doctest(
3436
string, {}, 'foo', 'foo.py', 0)
3439
suite = klass(test_finder=Finder())
3442
def run_doctest_suite_for_string(self, klass, string):
3443
suite = self.get_doctest_suite_for_string(klass, string)
3445
result = tests.TextTestResult(output, 0, 1)
3447
return result, output
3449
def assertDocTestStringSucceds(self, klass, string):
3450
result, output = self.run_doctest_suite_for_string(klass, string)
3451
if not result.wasStrictlySuccessful():
3452
self.fail(output.getvalue())
3454
def assertDocTestStringFails(self, klass, string):
3455
result, output = self.run_doctest_suite_for_string(klass, string)
3456
if result.wasStrictlySuccessful():
3457
self.fail(output.getvalue())
3459
def test_injected_variable(self):
3460
self.overrideAttr(tests, 'isolated_environ', {'LINES': '42'})
3463
>>> os.environ['LINES']
3466
# doctest.DocTestSuite fails as it sees '25'
3467
self.assertDocTestStringFails(doctest.DocTestSuite, test)
3468
# tests.DocTestSuite sees '42'
3469
self.assertDocTestStringSucceds(tests.IsolatedDocTestSuite, test)
3471
def test_deleted_variable(self):
3472
self.overrideAttr(tests, 'isolated_environ', {'LINES': None})
3475
>>> os.environ.get('LINES')
3477
# doctest.DocTestSuite fails as it sees '25'
3478
self.assertDocTestStringFails(doctest.DocTestSuite, test)
3479
# tests.DocTestSuite sees None
3480
self.assertDocTestStringSucceds(tests.IsolatedDocTestSuite, test)