Merge lp:~vila/bzr/690563-test-isolation into lp:bzr

Proposed by Vincent Ladeuil
Status: Merged
Approved by: John A Meinel
Approved revision: no longer in the source branch.
Merged at revision: 5573
Proposed branch: lp:~vila/bzr/690563-test-isolation
Merge into: lp:bzr
Diff against target: 120 lines (+23/-13)
4 files modified
bzrlib/tests/__init__.py (+7/-1)
bzrlib/tests/test_http.py (+9/-9)
bzrlib/tests/test_https_ca_bundle.py (+3/-3)
doc/en/release-notes/bzr-2.3.txt (+4/-0)
To merge this branch: bzr merge lp:~vila/bzr/690563-test-isolation
Reviewer Review Type Date Requested Status
John A Meinel Needs Fixing
Review via email: mp+43773@code.launchpad.net

Commit message

Fix test isolation leaks destroying env variables.

Description of the change

   BZR_PLUGIN_PATH=-site bzr selftest -s bt.test_import_tariff

suceeding while

   BZR_PLUGIN_PATH=-site bzr selftest --parallel=fork

failing in bzrlib.tests.test_import_tariff.TestImportTariffs.test_simple_local smells like a test isolation issue right ?

The journey was interesting...

After a painful bisection with an instrumented version revealing
that BZR_PLUGIN_PATH was deleted from os.environ somehow, I ended
up with the following suspects:

bzrlib.tests.test_http.TestProxyHttpServer.test_all_proxy(urllib,HTTP/1.0)
bzrlib.tests.test_http.TestProxyHttpServer.test_all_proxy(urllib,HTTP/1.1)
bzrlib.tests.test_http.TestProxyHttpServer.test_all_proxy(pycurl,HTTP/1.0)
bzrlib.tests.test_http.TestProxyHttpServer.test_all_proxy(pycurl,HTTP/1.1)
bzrlib.tests.test_http.TestProxyHttpServer.test_all_proxy_with_no_proxy(urllib,HTTP/1.0)
bzrlib.tests.test_http.TestProxyHttpServer.test_all_proxy_with_no_proxy(urllib,HTTP/1.1)
bzrlib.tests.test_http.TestProxyHttpServer.test_all_proxy_with_no_proxy(pycurl,HTTP/1.0)
bzrlib.tests.test_http.TestProxyHttpServer.test_all_proxy_with_no_proxy(pycurl,HTTP/1.1)
bzrlib.tests.test_import_tariff.TestImportTariffs.test_help_commands
bzrlib.tests.test_import_tariff.TestImportTariffs.test_import_tariffs_working
bzrlib.tests.test_import_tariff.TestImportTariffs.test_simple_local

Deleting test_all_proxy group or test_all_proxy_with_no_proxy
group makes the bug vanish...

The nasty bug is in:

class TestProxyHttpServer(http_utils.TestCaseWithTwoWebservers):

...

    def setUp(self):
        super(TestProxyHttpServer, self).setUp()
        self.transport_secondary_server = http_utils.ProxyServer
        self.build_tree_contents([('foo', 'contents of foo\n'),
                                  ('foo-proxied', 'proxied contents of foo\n')])
        # Let's setup some attributes for tests
        server = self.get_readonly_server()
        self.server_host_port = '%s:%d' % (server.host, server.port)
        if self._testing_pycurl():
            # Oh my ! pycurl does not check for the port as part of
            # no_proxy :-( So we just test the host part
            self.no_proxy_host = server.host
        else:
            self.no_proxy_host = self.server_host_port
        # The secondary server is the proxy
        self.proxy_url = self.get_secondary_url()
        self._old_env = {}

The offending line is the last one, because we have:

class TestCase(testtools.TestCase):

...

    def _cleanEnvironment(self):
        new_env = {
...
        self._old_env = {}

Eerk !

So as soon as one of the proxy tests is executed all values saved by TestCase are lost. For bug #690563, BZR_PLUGIN_PATH was the whistle blower. I'm quite amazed that we didn't get more failures due to this bug so far...

So the obvious fix is to use a different variable to hold the values for the proxy tests.

While this patch is enough for the case at end, I'll propose a more robust one (but more invasive and may be even controversial) later.

To post a comment you must log in.
Revision history for this message
John A Meinel (jameinel) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 12/15/2010 8:45 AM, Vincent Ladeuil wrote:
> Vincent Ladeuil has proposed merging lp:~vila/bzr/690563-test-isolation into lp:bzr.
>
> Requested reviews:
> bzr-core (bzr-core)
> Related bugs:
> #690563 bzrlib.tests.test_import_tariff.TestImportTariffs.test_simple_local failure
> https://bugs.launchpad.net/bugs/690563
>
>
> BZR_PLUGIN_PATH=-site bzr selftest -s bt.test_import_tariff
>
> suceeding while
>
> BZR_PLUGIN_PATH=-site bzr selftest --parallel=fork
>
> failing in bzrlib.tests.test_import_tariff.TestImportTariffs.test_simple_local smells like a test isolation issue right ?
>
> The journey was interesting...
>

...

I have to wonder why test_http needs its own save-and-restore env
variables. Rather than just doing it via the regular interface. Given
that they are calling "_install_env" and "_restore_env", versus the
TestCase versions.

Anyway, I''m ok with a fix like this, but maybe calling the variable:

self._http_saved_env = {}

Would make it clearer. This is also one of the very few cases where
using a double underscore for self.__old_env would have actually been
useful. (Since they actually end up as different real names, though that
can also confuse you "why isn't it getting set")

 review: needs_fixing
 merge: approve

John
=:->
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk0I688ACgkQJdeBCYSNAAPE3QCfZXe3Eg1ZikWKJIUvX+UW5cmf
LS0An1pLJpSNDh8RPhIkB6pDkqgwfD5d
=efjP
-----END PGP SIGNATURE-----

review: Needs Fixing
Revision history for this message
Vincent Ladeuil (vila) wrote :

>>>>> John A Meinel <email address hidden> writes:

<snip/>

    > I have to wonder why test_http needs its own save-and-restore env
    > variables.

Indeed. Hysterical raisins I suspect. I don't remember exactly why I did
it this way, probably TestCase wasn't exactly in the same shape as it is
today or I couldn't figure it.

    > Rather than just doing it via the regular interface.

The regular one has also some traps which I why I want to make another patch.

    > Given that they are calling "_install_env" and "_restore_env",
    > versus the TestCase versions.

Yeah, this clearly pre-dates the overrideAttr era, though os.environ
can't use it easily (it's not a dict and it needs special care).

    > Anyway, I''m ok with a fix like this, but maybe calling the variable:

    > self._http_saved_env = {}

    > Would make it clearer.

Ok. Hopefully I will get rid of them soon anyway.

    > This is also one of the very few cases where using a double
    > underscore for self.__old_env would have actually been
    > useful. (Since they actually end up as different real names,
    > though that can also confuse you "why isn't it getting set")

Yup, I almost went this way but that would defeat the trick used by
test_import_tariff (which want to restore the initial values saved
here).

More on that in my next submission on this topic.

--F0E75180F92.1292431208/axe--

Revision history for this message
Vincent Ladeuil (vila) wrote :

sent to pqm by email

Revision history for this message
Martin Pool (mbp) wrote :

sent to pqm by email

Revision history for this message
Martin Pool (mbp) wrote :
Revision history for this message
Vincent Ladeuil (vila) wrote :

sent to pqm by email

Revision history for this message
Vincent Ladeuil (vila) wrote :

sent to pqm by email

Revision history for this message
Vincent Ladeuil (vila) wrote :

sent to pqm by email

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/tests/__init__.py'
2--- bzrlib/tests/__init__.py 2010-12-02 10:41:05 +0000
3+++ bzrlib/tests/__init__.py 2010-12-16 14:41:46 +0000
4@@ -3842,7 +3842,13 @@
5 return []
6 return [
7 'bzrlib',
8- 'bzrlib.branchbuilder',
9+ # FIXME: Fixing bug #690563 revealed an isolation problem in the single
10+ # doctest for branchbuilder. Uncomment this when bug #321320 is fixed
11+ # to ensure the issue is addressed (note that to reproduce the bug in
12+ # the doctest below, one should comment the 'email' config var in
13+ # bazaar.conf (or anywhere else). This means an setup where *no* user
14+ # is being set at all in the environment.
15+# 'bzrlib.branchbuilder',
16 'bzrlib.decorators',
17 'bzrlib.export',
18 'bzrlib.inventory',
19
20=== modified file 'bzrlib/tests/test_http.py'
21--- bzrlib/tests/test_http.py 2010-12-14 16:27:12 +0000
22+++ bzrlib/tests/test_http.py 2010-12-16 14:41:46 +0000
23@@ -1075,15 +1075,15 @@
24
25 def setUp(self):
26 tests.TestCase.setUp(self)
27- self._old_env = {}
28+ self._http_saved_env = {}
29 self.addCleanup(self._restore_env)
30
31 def _install_env(self, env):
32 for name, value in env.iteritems():
33- self._old_env[name] = osutils.set_or_unset_env(name, value)
34+ self._http_saved_env[name] = osutils.set_or_unset_env(name, value)
35
36 def _restore_env(self):
37- for name, value in self._old_env.iteritems():
38+ for name, value in self._http_saved_env.iteritems():
39 osutils.set_or_unset_env(name, value)
40
41 def _proxied_request(self):
42@@ -1136,7 +1136,7 @@
43 self.no_proxy_host = self.server_host_port
44 # The secondary server is the proxy
45 self.proxy_url = self.get_secondary_url()
46- self._old_env = {}
47+ self._http_saved_env = {}
48
49 def _testing_pycurl(self):
50 # TODO: This is duplicated for lots of the classes in this file
51@@ -1145,10 +1145,10 @@
52
53 def _install_env(self, env):
54 for name, value in env.iteritems():
55- self._old_env[name] = osutils.set_or_unset_env(name, value)
56+ self._http_saved_env[name] = osutils.set_or_unset_env(name, value)
57
58 def _restore_env(self):
59- for name, value in self._old_env.iteritems():
60+ for name, value in self._http_saved_env.iteritems():
61 osutils.set_or_unset_env(name, value)
62
63 def proxied_in_env(self, env):
64@@ -1707,7 +1707,7 @@
65
66 def setUp(self):
67 super(TestProxyAuth, self).setUp()
68- self._old_env = {}
69+ self._http_saved_env = {}
70 self.addCleanup(self._restore_env)
71 # Override the contents to avoid false positives
72 self.build_tree_contents([('a', 'not proxied contents of a\n'),
73@@ -1722,10 +1722,10 @@
74
75 def _install_env(self, env):
76 for name, value in env.iteritems():
77- self._old_env[name] = osutils.set_or_unset_env(name, value)
78+ self._http_saved_env[name] = osutils.set_or_unset_env(name, value)
79
80 def _restore_env(self):
81- for name, value in self._old_env.iteritems():
82+ for name, value in self._http_saved_env.iteritems():
83 osutils.set_or_unset_env(name, value)
84
85 def test_empty_pass(self):
86
87=== modified file 'bzrlib/tests/test_https_ca_bundle.py'
88--- bzrlib/tests/test_https_ca_bundle.py 2009-03-23 14:59:43 +0000
89+++ bzrlib/tests/test_https_ca_bundle.py 2010-12-16 14:41:46 +0000
90@@ -36,13 +36,13 @@
91 'CURL_CA_BUNDLE': None,
92 'PATH': None,
93 }
94- self._old_env = {}
95+ self._http_saved_env = {}
96 self.addCleanup(self._restore)
97 for name, value in new_env.iteritems():
98- self._old_env[name] = osutils.set_or_unset_env(name, None)
99+ self._http_saved_env[name] = osutils.set_or_unset_env(name, None)
100
101 def _restore(self):
102- for name, value in self._old_env.iteritems():
103+ for name, value in self._http_saved_env.iteritems():
104 osutils.set_or_unset_env(name, value)
105
106 def _make_file(self, in_dir='.'):
107
108=== modified file 'doc/en/release-notes/bzr-2.3.txt'
109--- doc/en/release-notes/bzr-2.3.txt 2010-12-16 06:24:14 +0000
110+++ doc/en/release-notes/bzr-2.3.txt 2010-12-16 14:41:46 +0000
111@@ -40,6 +40,10 @@
112 designated by the action. This will *ignore* all differences that would
113 have been merge cleanly otherwise. (Vincent Ladeuil, #638451)
114
115+* ``bt.test_http`` was breaking ``os.environ`` by erasing the values saved by
116+ ``TestCase`` leading to ``bt.test_import_tariff`` failures.
117+ (Vincent Ladeuil, #690563)
118+
119 Bug Fixes
120 *********
121