Merge lp:~vila/bzr/321320-isolate-doc-tests into lp:bzr
- 321320-isolate-doc-tests
- Merge into bzr.dev
Status: | Merged |
---|---|
Approved by: | Vincent Ladeuil |
Approved revision: | no longer in the source branch. |
Merged at revision: | 5583 |
Proposed branch: | lp:~vila/bzr/321320-isolate-doc-tests |
Merge into: | lp:bzr |
Prerequisite: | lp:~vila/bzr/cleanup |
Diff against target: |
395 lines (+244/-63) 4 files modified
bzrlib/tests/__init__.py (+110/-58) bzrlib/tests/test_selftest.py (+122/-3) doc/developers/testing.txt (+5/-2) doc/en/release-notes/bzr-2.3.txt (+7/-0) |
To merge this branch: | bzr merge lp:~vila/bzr/321320-isolate-doc-tests |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrew Bennetts | Needs Fixing | ||
Review via email: mp+44230@code.launchpad.net |
Commit message
Isolate doctests from os.environ
Description of the change
This isolates doctests from os.environ, fixing bug #321320.
We have a set of variables that we want to be isolated from the external os.environ.
I moved them out of 'Test_case.
I then created a override_
I've thought about being even more radical here by modifying the selftest *command* to save/restore os.environ so that tests can just start with an empty os.environ and install whatever they need, but there are several problems with this approach:
- other test runners won't support it,
- this may trigger weird issues with lazy imports (what if the import is sometimes done before the os.environ is clean and sometimes not, etc),
- this would make it harder to write tests for this code (note that the tests added here rely on being properly isolated. I've commented heavily on that as it's easy to overlook).
Martin Packman (gz) wrote : | # |
Vincent Ladeuil (vila) wrote : | # |
> Some quick notes without getting into a proper review:
>
> Main concern is that I think the doctests need more of
> bzrlib.
> _cleanEnvironment call, or perhaps even TestCaseWithMem
> equivalent. This would mean pulling a lot more code off the TestCase class so
> it could be shared. I'm particularly thinking of TestCase.
> similar things. Not having cleanUp usable from doctest setUp without clever
> hacks is particularly annoying.
You wouldn't be able to call it from a doctest isn't it ?
But even then, DocTestSuite is a *function* which uses DocTestCase (the class) internally, I'm not against the principle of rewriting python's doctest, but I don't think it's in bzr scope :)
>
>
> + 'http_proxy': None,
> + 'HTTP_PROXY': None,
>
> Scope creepitis: I'd much prefer this was handled by the
> osutils.
> environments shouldn't have to do extra work.
But it's handled this way because on OSes with case sensitive envs, both can be (and are) used !
Andrew Bennetts (spiv) wrote : | # |
17 + 'HOME': os.getcwd(),
There's a small behaviour difference here, although I think it's probably ok: this calculates the isolated $HOME once, at module import time, rather that during each test's setUp. Given that tests that care about this probably use TestCaseWithMem
194 + doc_suite = DocTestSuite(
195 + mod, optionflags=
That's a bit odd: shouldn't both DocTestSuite and REPORT_
240 + Since we use tests that are already isolated from os.environ abit of care
Typo: “a bit” is two words.
Andrew Bennetts (spiv) wrote : | # |
My suspicion was right: this branch does not appear to fix 321320, except sometimes by accident.
If you e.g. run 'bzr selftest' from your home dir, HOME is set to your real home, and config values in your normal ~/.bazaar/
Vincent Ladeuil (vila) wrote : | # |
Nice catch !
Thinking more about it, I think the issue is that giving a value to a variable via isolated_environ is just wrong.
If set HOME to None there (but still set it to os.getcwd() for TestCase), I get no failures. I didn't dare before but this makes sense. A test that *requires* HOME to be set for whatever reason, should make sure it's set or fail.
Re-running the test suite without setting HOME *at all* ....<drum roll> pass !
The other variables set here are:
- EMAIL: root cause for the branchbuilder doctest failure, this one is under discussion about whether the *feature* of making whoami mandatory is a good one. Setting it here masked the issue and *users* faced the consequences, I'll leave it for now, but I'm convinced we should set it to None and let the related tests themselves care about it (including the currently hidden failures),
- TERM, LINES, COLUMNS, BZR_COLUMNS: again, the tests needing these settings should set these variables explicitly, we'll get a better tracking of which tests really care about them.
That being said, I'll investigate further and submit a more robust solution, two reviewers agreeing on the same issue is worth addressing isn't it ? :D
Vincent Ladeuil (vila) wrote : | # |
So, it turns out that deleting HOME from the environment doesn't
annoy our test suite even slightly, so I did just that. I then
run this branch on babune to see how our various supported OSes
react against homeless tests and the result is: nothing. Not a
single test failed, much to my surprise I should say.
But once you accept that indeed our code and our tests don't need
it, it feels cleaner (and should address spiv's concerns about
GPG, if it doesn't, yell !).
Note that we still have tests that write to $HOME/.bzr.log
(better tracked by running with 'BZR_HOME=. ./bzr selftest'), but
they aren't doctests and as such are out of scope for this fix.
If someone (mgz ?) really feels strongly about adding more
isolation to doctests then feel free to do so, I've prepare
things a bit by adding two functions isolated_
isolated_
should be possible to force them to run in a tmpdir.
But since there wasn't any leak I could find that would benefit
from such an isolation, I stopped there.
Now, to come back to spiv's first comment:
> My suspicion was right: this branch does not appear to fix 321320, except sometimes by accident.
> If you e.g. run 'bzr selftest' from your home dir, HOME is set to your real home, and config values in your normal ~/.bazaar/
Would you mind checking this behaviour with bzr trunk ?
And is it fixed now ?
If it isn't, can you point me to the *doctest* you think is exhibiting this behaviour ?
Robert Collins (lifeless) wrote : | # |
I suggest using a fixture (lp:python-fixtures) or similar rather than
a pair of functions. Its a bit cleaner and will be less code
(particularly if you reuse Fixture rather than rolling your own).
Martin Packman (gz) wrote : | # |
> If someone (mgz ?) really feels strongly about adding more
> isolation to doctests then feel free to do so, I've prepare
> things a bit by adding two functions isolated_
> isolated_
> should be possible to force them to run in a tmpdir.
This may as well land if it fixes the problems with doctests people are actually having, even if it's not a complete solution.
Anyone with for instance, a plugin that adds a hook and doesn't run selftest with --no-plugins will still risk random behaviour though right? I'd like a note in the developer documentation saying doctests are not as robustly isolated as general tests.
Do you also want to uncomment the doctest we disabled the other day in this branch?
Vincent Ladeuil (vila) wrote : | # |
> This may as well land if it fixes the problems with doctests people are
> actually having, even if it's not a complete solution.
Ok.
>
> Anyone with for instance, a plugin that adds a hook and doesn't run selftest
> with --no-plugins will still risk random behaviour though right?
Right.
> I'd like a
> note in the developer documentation saying doctests are not as robustly
> isolated as general tests.
Ok.
> Do you also want to uncomment the doctest we disabled the other day in this
> branch?
I did and used it to ensure my fix addressed the related problem.
Vincent Ladeuil (vila) wrote : | # |
sent to pqm by email
Vincent Ladeuil (vila) wrote : | # |
sent to pqm by email
Vincent Ladeuil (vila) wrote : | # |
sent to pqm by email
Vincent Ladeuil (vila) wrote : | # |
sent to pqm by email
Vincent Ladeuil (vila) wrote : | # |
sent to pqm by email
Preview Diff
1 | === modified file 'bzrlib/tests/__init__.py' |
2 | --- bzrlib/tests/__init__.py 2010-12-26 16:23:16 +0000 |
3 | +++ bzrlib/tests/__init__.py 2011-01-07 11:18:35 +0000 |
4 | @@ -123,6 +123,93 @@ |
5 | TestSuite = TestUtil.TestSuite |
6 | TestLoader = TestUtil.TestLoader |
7 | |
8 | +# Tests should run in a clean and clearly defined environment. The goal is to |
9 | +# keep them isolated from the running environment as mush as possible. The test |
10 | +# framework ensures the variables defined below are set (or deleted if the |
11 | +# value is None) before a test is run and reset to their original value after |
12 | +# the test is run. Generally if some code depends on an environment variable, |
13 | +# the tests should start without this variable in the environment. There are a |
14 | +# few exceptions but you shouldn't violate this rule lightly. |
15 | +isolated_environ = { |
16 | + 'BZR_HOME': None, |
17 | + 'HOME': None, |
18 | + # bzr now uses the Win32 API and doesn't rely on APPDATA, but the |
19 | + # tests do check our impls match APPDATA |
20 | + 'BZR_EDITOR': None, # test_msgeditor manipulates this variable |
21 | + 'VISUAL': None, |
22 | + 'EDITOR': None, |
23 | + 'BZR_EMAIL': None, |
24 | + 'BZREMAIL': None, # may still be present in the environment |
25 | + 'EMAIL': 'jrandom@example.com', # set EMAIL as bzr does not guess |
26 | + 'BZR_PROGRESS_BAR': None, |
27 | + 'BZR_LOG': None, |
28 | + 'BZR_PLUGIN_PATH': None, |
29 | + 'BZR_DISABLE_PLUGINS': None, |
30 | + 'BZR_PLUGINS_AT': None, |
31 | + 'BZR_CONCURRENCY': None, |
32 | + # Make sure that any text ui tests are consistent regardless of |
33 | + # the environment the test case is run in; you may want tests that |
34 | + # test other combinations. 'dumb' is a reasonable guess for tests |
35 | + # going to a pipe or a StringIO. |
36 | + 'TERM': 'dumb', |
37 | + 'LINES': '25', |
38 | + 'COLUMNS': '80', |
39 | + 'BZR_COLUMNS': '80', |
40 | + # Disable SSH Agent |
41 | + 'SSH_AUTH_SOCK': None, |
42 | + # Proxies |
43 | + 'http_proxy': None, |
44 | + 'HTTP_PROXY': None, |
45 | + 'https_proxy': None, |
46 | + 'HTTPS_PROXY': None, |
47 | + 'no_proxy': None, |
48 | + 'NO_PROXY': None, |
49 | + 'all_proxy': None, |
50 | + 'ALL_PROXY': None, |
51 | + # Nobody cares about ftp_proxy, FTP_PROXY AFAIK. So far at |
52 | + # least. If you do (care), please update this comment |
53 | + # -- vila 20080401 |
54 | + 'ftp_proxy': None, |
55 | + 'FTP_PROXY': None, |
56 | + 'BZR_REMOTE_PATH': None, |
57 | + # Generally speaking, we don't want apport reporting on crashes in |
58 | + # the test envirnoment unless we're specifically testing apport, |
59 | + # so that it doesn't leak into the real system environment. We |
60 | + # use an env var so it propagates to subprocesses. |
61 | + 'APPORT_DISABLE': '1', |
62 | + } |
63 | + |
64 | + |
65 | +def override_os_environ(test, env=None): |
66 | + """Modify os.environ keeping a copy. |
67 | + |
68 | + :param test: A test instance |
69 | + |
70 | + :param env: A dict containing variable definitions to be installed |
71 | + """ |
72 | + if env is None: |
73 | + env = isolated_environ |
74 | + test._original_os_environ = dict([(var, value) |
75 | + for var, value in os.environ.iteritems()]) |
76 | + for var, value in env.iteritems(): |
77 | + osutils.set_or_unset_env(var, value) |
78 | + if var not in test._original_os_environ: |
79 | + # The var is new, add it with a value of None, so |
80 | + # restore_os_environ will delete it |
81 | + test._original_os_environ[var] = None |
82 | + |
83 | + |
84 | +def restore_os_environ(test): |
85 | + """Restore os.environ to its original state. |
86 | + |
87 | + :param test: A test instance previously passed to override_os_environ. |
88 | + """ |
89 | + for var, value in test._original_os_environ.iteritems(): |
90 | + # Restore the original value (or delete it if the value has been set to |
91 | + # None in override_os_environ). |
92 | + osutils.set_or_unset_env(var, value) |
93 | + |
94 | + |
95 | class ExtendedTestResult(testtools.TextTestResult): |
96 | """Accepts, reports and accumulates the results of running tests. |
97 | |
98 | @@ -794,6 +881,25 @@ |
99 | return NullProgressView() |
100 | |
101 | |
102 | +def isolated_doctest_setUp(test): |
103 | + override_os_environ(test) |
104 | + |
105 | + |
106 | +def isolated_doctest_tearDown(test): |
107 | + restore_os_environ(test) |
108 | + |
109 | + |
110 | +def IsolatedDocTestSuite(*args, **kwargs): |
111 | + """Overrides doctest.DocTestSuite to handle isolation. |
112 | + |
113 | + The method is really a factory and users are expected to use it as such. |
114 | + """ |
115 | + |
116 | + kwargs['setUp'] = isolated_doctest_setUp |
117 | + kwargs['tearDown'] = isolated_doctest_tearDown |
118 | + return doctest.DocTestSuite(*args, **kwargs) |
119 | + |
120 | + |
121 | class TestCase(testtools.TestCase): |
122 | """Base class for bzr unit tests. |
123 | |
124 | @@ -1538,55 +1644,7 @@ |
125 | return value |
126 | |
127 | def _cleanEnvironment(self): |
128 | - new_env = { |
129 | - 'BZR_HOME': None, # Don't inherit BZR_HOME to all the tests. |
130 | - 'HOME': os.getcwd(), |
131 | - # bzr now uses the Win32 API and doesn't rely on APPDATA, but the |
132 | - # tests do check our impls match APPDATA |
133 | - 'BZR_EDITOR': None, # test_msgeditor manipulates this variable |
134 | - 'VISUAL': None, |
135 | - 'EDITOR': None, |
136 | - 'BZR_EMAIL': None, |
137 | - 'BZREMAIL': None, # may still be present in the environment |
138 | - 'EMAIL': 'jrandom@example.com', # set EMAIL as bzr does not guess |
139 | - 'BZR_PROGRESS_BAR': None, |
140 | - 'BZR_LOG': None, |
141 | - 'BZR_PLUGIN_PATH': None, |
142 | - 'BZR_DISABLE_PLUGINS': None, |
143 | - 'BZR_PLUGINS_AT': None, |
144 | - 'BZR_CONCURRENCY': None, |
145 | - # Make sure that any text ui tests are consistent regardless of |
146 | - # the environment the test case is run in; you may want tests that |
147 | - # test other combinations. 'dumb' is a reasonable guess for tests |
148 | - # going to a pipe or a StringIO. |
149 | - 'TERM': 'dumb', |
150 | - 'LINES': '25', |
151 | - 'COLUMNS': '80', |
152 | - 'BZR_COLUMNS': '80', |
153 | - # SSH Agent |
154 | - 'SSH_AUTH_SOCK': None, |
155 | - # Proxies |
156 | - 'http_proxy': None, |
157 | - 'HTTP_PROXY': None, |
158 | - 'https_proxy': None, |
159 | - 'HTTPS_PROXY': None, |
160 | - 'no_proxy': None, |
161 | - 'NO_PROXY': None, |
162 | - 'all_proxy': None, |
163 | - 'ALL_PROXY': None, |
164 | - # Nobody cares about ftp_proxy, FTP_PROXY AFAIK. So far at |
165 | - # least. If you do (care), please update this comment |
166 | - # -- vila 20080401 |
167 | - 'ftp_proxy': None, |
168 | - 'FTP_PROXY': None, |
169 | - 'BZR_REMOTE_PATH': None, |
170 | - # Generally speaking, we don't want apport reporting on crashes in |
171 | - # the test envirnoment unless we're specifically testing apport, |
172 | - # so that it doesn't leak into the real system environment. We |
173 | - # use an env var so it propagates to subprocesses. |
174 | - 'APPORT_DISABLE': '1', |
175 | - } |
176 | - for name, value in new_env.iteritems(): |
177 | + for name, value in isolated_environ.iteritems(): |
178 | self.overrideEnv(name, value) |
179 | |
180 | def _captureVar(self, name, newvalue): |
181 | @@ -3836,13 +3894,7 @@ |
182 | return [] |
183 | return [ |
184 | 'bzrlib', |
185 | - # FIXME: Fixing bug #690563 revealed an isolation problem in the single |
186 | - # doctest for branchbuilder. Uncomment this when bug #321320 is fixed |
187 | - # to ensure the issue is addressed (note that to reproduce the bug in |
188 | - # the doctest below, one should comment the 'email' config var in |
189 | - # bazaar.conf (or anywhere else). This means an setup where *no* user |
190 | - # is being set at all in the environment. |
191 | -# 'bzrlib.branchbuilder', |
192 | + 'bzrlib.branchbuilder', |
193 | 'bzrlib.decorators', |
194 | 'bzrlib.export', |
195 | 'bzrlib.inventory', |
196 | @@ -3914,8 +3966,8 @@ |
197 | try: |
198 | # note that this really does mean "report only" -- doctest |
199 | # still runs the rest of the examples |
200 | - doc_suite = doctest.DocTestSuite(mod, |
201 | - optionflags=doctest.REPORT_ONLY_FIRST_FAILURE) |
202 | + doc_suite = IsolatedDocTestSuite( |
203 | + mod, optionflags=doctest.REPORT_ONLY_FIRST_FAILURE) |
204 | except ValueError, e: |
205 | print '**failed to get doctest for: %s\n%s' % (mod, e) |
206 | raise |
207 | |
208 | === modified file 'bzrlib/tests/test_selftest.py' |
209 | --- bzrlib/tests/test_selftest.py 2010-12-26 16:23:16 +0000 |
210 | +++ bzrlib/tests/test_selftest.py 2011-01-07 11:18:35 +0000 |
211 | @@ -17,7 +17,7 @@ |
212 | """Tests for the test framework.""" |
213 | |
214 | from cStringIO import StringIO |
215 | -from doctest import ELLIPSIS |
216 | +import doctest |
217 | import os |
218 | import signal |
219 | import sys |
220 | @@ -92,7 +92,7 @@ |
221 | "text", "plain", {"charset": "utf8"}))) |
222 | self.assertThat(u"".join(log.iter_text()), Equals(self.get_log())) |
223 | self.assertThat(self.get_log(), |
224 | - DocTestMatches(u"...a test message\n", ELLIPSIS)) |
225 | + DocTestMatches(u"...a test message\n", doctest.ELLIPSIS)) |
226 | |
227 | |
228 | class TestUnicodeFilename(tests.TestCase): |
229 | @@ -3210,7 +3210,8 @@ |
230 | tpr.register('bar', 'bBB.aAA.rRR') |
231 | self.assertEquals('bbb.aaa.rrr', tpr.get('bar')) |
232 | self.assertThat(self.get_log(), |
233 | - DocTestMatches("...bar...bbb.aaa.rrr...BB.aAA.rRR", ELLIPSIS)) |
234 | + DocTestMatches("...bar...bbb.aaa.rrr...BB.aAA.rRR", |
235 | + doctest.ELLIPSIS)) |
236 | |
237 | def test_get_unknown_prefix(self): |
238 | tpr = self._get_registry() |
239 | @@ -3453,3 +3454,121 @@ |
240 | self.fail(output.getvalue()) |
241 | # We get our value back |
242 | self.assertEquals('42', os.environ.get('MYVAR')) |
243 | + |
244 | + |
245 | +class TestIsolatedEnv(tests.TestCase): |
246 | + """Test isolating tests from os.environ. |
247 | + |
248 | + Since we use tests that are already isolated from os.environ a bit of care |
249 | + should be taken when designing the tests to avoid bootstrap side-effects. |
250 | + The tests start an already clean os.environ which allow doing valid |
251 | + assertions about which variables are present or not and design tests around |
252 | + these assertions. |
253 | + """ |
254 | + |
255 | + class ScratchMonkey(tests.TestCase): |
256 | + |
257 | + def test_me(self): |
258 | + pass |
259 | + |
260 | + def test_basics(self): |
261 | + # Make sure we know the definition of BZR_HOME: not part of os.environ |
262 | + # for tests.TestCase. |
263 | + self.assertTrue('BZR_HOME' in tests.isolated_environ) |
264 | + self.assertEquals(None, tests.isolated_environ['BZR_HOME']) |
265 | + # Being part of isolated_environ, BZR_HOME should not appear here |
266 | + self.assertFalse('BZR_HOME' in os.environ) |
267 | + # Make sure we know the definition of LINES: part of os.environ for |
268 | + # tests.TestCase |
269 | + self.assertTrue('LINES' in tests.isolated_environ) |
270 | + self.assertEquals('25', tests.isolated_environ['LINES']) |
271 | + self.assertEquals('25', os.environ['LINES']) |
272 | + |
273 | + def test_injecting_unknown_variable(self): |
274 | + # BZR_HOME is known to be absent from os.environ |
275 | + test = self.ScratchMonkey('test_me') |
276 | + tests.override_os_environ(test, {'BZR_HOME': 'foo'}) |
277 | + self.assertEquals('foo', os.environ['BZR_HOME']) |
278 | + tests.restore_os_environ(test) |
279 | + self.assertFalse('BZR_HOME' in os.environ) |
280 | + |
281 | + def test_injecting_known_variable(self): |
282 | + test = self.ScratchMonkey('test_me') |
283 | + # LINES is known to be present in os.environ |
284 | + tests.override_os_environ(test, {'LINES': '42'}) |
285 | + self.assertEquals('42', os.environ['LINES']) |
286 | + tests.restore_os_environ(test) |
287 | + self.assertEquals('25', os.environ['LINES']) |
288 | + |
289 | + def test_deleting_variable(self): |
290 | + test = self.ScratchMonkey('test_me') |
291 | + # LINES is known to be present in os.environ |
292 | + tests.override_os_environ(test, {'LINES': None}) |
293 | + self.assertTrue('LINES' not in os.environ) |
294 | + tests.restore_os_environ(test) |
295 | + self.assertEquals('25', os.environ['LINES']) |
296 | + |
297 | + |
298 | +class TestDocTestSuiteIsolation(tests.TestCase): |
299 | + """Test that `tests.DocTestSuite` isolates doc tests from os.environ. |
300 | + |
301 | + Since tests.TestCase alreay provides an isolation from os.environ, we use |
302 | + the clean environment as a base for testing. To precisely capture the |
303 | + isolation provided by tests.DocTestSuite, we use doctest.DocTestSuite to |
304 | + compare against. |
305 | + |
306 | + We want to make sure `tests.DocTestSuite` respect `tests.isolated_environ`, |
307 | + not `os.environ` so each test overrides it to suit its needs. |
308 | + |
309 | + """ |
310 | + |
311 | + def get_doctest_suite_for_string(self, klass, string): |
312 | + class Finder(doctest.DocTestFinder): |
313 | + |
314 | + def find(*args, **kwargs): |
315 | + test = doctest.DocTestParser().get_doctest( |
316 | + string, {}, 'foo', 'foo.py', 0) |
317 | + return [test] |
318 | + |
319 | + suite = klass(test_finder=Finder()) |
320 | + return suite |
321 | + |
322 | + def run_doctest_suite_for_string(self, klass, string): |
323 | + suite = self.get_doctest_suite_for_string(klass, string) |
324 | + output = StringIO() |
325 | + result = tests.TextTestResult(output, 0, 1) |
326 | + suite.run(result) |
327 | + return result, output |
328 | + |
329 | + def assertDocTestStringSucceds(self, klass, string): |
330 | + result, output = self.run_doctest_suite_for_string(klass, string) |
331 | + if not result.wasStrictlySuccessful(): |
332 | + self.fail(output.getvalue()) |
333 | + |
334 | + def assertDocTestStringFails(self, klass, string): |
335 | + result, output = self.run_doctest_suite_for_string(klass, string) |
336 | + if result.wasStrictlySuccessful(): |
337 | + self.fail(output.getvalue()) |
338 | + |
339 | + def test_injected_variable(self): |
340 | + self.overrideAttr(tests, 'isolated_environ', {'LINES': '42'}) |
341 | + test = """ |
342 | + >>> import os |
343 | + >>> os.environ['LINES'] |
344 | + '42' |
345 | + """ |
346 | + # doctest.DocTestSuite fails as it sees '25' |
347 | + self.assertDocTestStringFails(doctest.DocTestSuite, test) |
348 | + # tests.DocTestSuite sees '42' |
349 | + self.assertDocTestStringSucceds(tests.IsolatedDocTestSuite, test) |
350 | + |
351 | + def test_deleted_variable(self): |
352 | + self.overrideAttr(tests, 'isolated_environ', {'LINES': None}) |
353 | + test = """ |
354 | + >>> import os |
355 | + >>> os.environ.get('LINES') |
356 | + """ |
357 | + # doctest.DocTestSuite fails as it sees '25' |
358 | + self.assertDocTestStringFails(doctest.DocTestSuite, test) |
359 | + # tests.DocTestSuite sees None |
360 | + self.assertDocTestStringSucceds(tests.IsolatedDocTestSuite, test) |
361 | |
362 | === modified file 'doc/developers/testing.txt' |
363 | --- doc/developers/testing.txt 2010-12-16 17:45:14 +0000 |
364 | +++ doc/developers/testing.txt 2011-01-07 11:18:35 +0000 |
365 | @@ -329,8 +329,11 @@ |
366 | We make selective use of doctests__. In general they should provide |
367 | *examples* within the API documentation which can incidentally be tested. We |
368 | don't try to test every important case using doctests |--| regular Python |
369 | -tests are generally a better solution. That is, we just use doctests to |
370 | -make our documentation testable, rather than as a way to make tests. |
371 | +tests are generally a better solution. That is, we just use doctests to make |
372 | +our documentation testable, rather than as a way to make tests. Be aware that |
373 | +doctests are not as well isolated as the unit tests, if you need more |
374 | +isolation, you're likely want to write unit tests anyway if only to get a |
375 | +better control of the test environment. |
376 | |
377 | Most of these are in ``bzrlib/doc/api``. More additions are welcome. |
378 | |
379 | |
380 | === modified file 'doc/en/release-notes/bzr-2.3.txt' |
381 | --- doc/en/release-notes/bzr-2.3.txt 2010-12-24 16:30:36 +0000 |
382 | +++ doc/en/release-notes/bzr-2.3.txt 2011-01-07 11:18:35 +0000 |
383 | @@ -90,6 +90,13 @@ |
384 | suite. This can include new facilities for writing tests, fixes to |
385 | spurious test failures and changes to the way things should be tested. |
386 | |
387 | +* ``bzrlib.tests`` defines ``isolated_environ`` with the definitions of all |
388 | + the environment variables the tests should care about. It also defines |
389 | + ``override_os_environ`` and ``restore_os_environ`` to properly implement |
390 | + isolation from ``os.environ`` for tests. ``bzrlib.tests`` now defines a |
391 | + ``DocTestSuite`` class using this facility for all ``bzrlib`` |
392 | + doctests. (Vincent Ladeuil, #321320) |
393 | + |
394 | * Catch exceptions related to bug #637821 during test cleanup to avoid |
395 | spurious failures. (Vincent Ladeuil, #686008). |
396 |
Some quick notes without getting into a proper review:
Main concern is that I think the doctests need more of bzrlib. tests.TestCase. setUp to be properly isolated, not just the _cleanEnvironment call, or perhaps even TestCaseWithMem oryTransport. setUp equivalent. This would mean pulling a lot more code off the TestCase class so it could be shared. I'm particularly thinking of TestCase. _clear_ hooks and similar things. Not having cleanUp usable from doctest setUp without clever hacks is particularly annoying.
+ 'http_proxy': None,
+ 'HTTP_PROXY': None,
Scope creepitis: I'd much prefer this was handled by the osutils. set_or_ unset_env or similar, platforms without case sensitive environments shouldn't have to do extra work.