Merge lp:~jameinel/bzr/2.4-overridAttr-non-existant into lp:bzr/2.4

Proposed by John A Meinel
Status: Merged
Approved by: John A Meinel
Approved revision: no longer in the source branch.
Merged at revision: 6073
Proposed branch: lp:~jameinel/bzr/2.4-overridAttr-non-existant
Merge into: lp:bzr/2.4
Diff against target: 92 lines (+48/-6)
2 files modified
bzrlib/tests/__init__.py (+8/-2)
bzrlib/tests/test_selftest.py (+40/-4)
To merge this branch: bzr merge lp:~jameinel/bzr/2.4-overridAttr-non-existant
Reviewer Review Type Date Requested Status
Martin Packman (community) Approve
Review via email: mp+132871@code.launchpad.net

Commit message

Teach TestCase.overrideAttr how to handle attributes that don't exist yet.

Description of the change

This is a precursor branch for another fix I want to land.

Essentially, TestCase.overrideAttr didn't handle the case where you want to set an attribute and that attribute doesn't exist. (I'm using it in a follow up branch to force an OS level function to a known state.)

I'm just submitting it now because it should be clearer factored out into 2 patches.

To post a comment you must log in.
Revision history for this message
Martin Packman (gz) wrote :

Looks fine, but I'm not sure having special logic for the case where new is uninited and value is uninited makes sense. That doesn't seem like something a test author would do deliberately, except perhaps if they were later in the test setting the attribute to something and wanted the cleanup at the end, in which case the value would leak through in this case.

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

IIRC we didn't implement the unexisting attr case because we didn't need it.

Later on, testtools provided 'patch' which takes this case into account.

I think it would be better to use the testtools version for your specific case.

Also, unless you intend to cut a release for 2.4, I think we should avoid landing patches on stable releases for which we don't intend to make a release, the SRU process is resource intensive and we still haven't a 2.6.0 release yet...

Revision history for this message
John A Meinel (jameinel) wrote :

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

On 11/6/2012 12:06 PM, Martin Packman wrote:
> Review: Approve
>
> Looks fine, but I'm not sure having special logic for the case
> where new is uninited and value is uninited makes sense. That
> doesn't seem like something a test author would do deliberately,
> except perhaps if they were later in the test setting the attribute
> to something and wanted the cleanup at the end, in which case the
> value would leak through in this case.
>

It happens explicitly on Windows when I'm testing that the test case
handles when ENOTSUP doesn't exist. Either I support that case, or I
have to add logic around:
 if getattr(foo, "bar", None) is not None:
    self.overrideAttr(foo, "bar")

In other words, I still need to only remove the attribute if it
exists, and I'd rather have that logic in one place.

John
=:->

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (Cygwin)
Comment: Using GnuPG with Mozilla - http://www.enigmail.net/

iEYEARECAAYFAlCYzZ4ACgkQJdeBCYSNAAOZ4gCgx2Z8Lk4Ud0wRXD9TlRb2cx8w
tVQAn28G5xt6eGbwuuzy+CeOrM55Fwtx
=em4F
-----END PGP SIGNATURE-----

Revision history for this message
John A Meinel (jameinel) wrote :

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

On 11/6/2012 12:39 PM, Vincent Ladeuil wrote:
> IIRC we didn't implement the unexisting attr case because we didn't
> need it.
>
> Later on, testtools provided 'patch' which takes this case into
> account.
>
> I think it would be better to use the testtools version for your
> specific case.
>
> Also, unless you intend to cut a release for 2.4, I think we should
> avoid landing patches on stable releases for which we don't intend
> to make a release, the SRU process is resource intensive and we
> still haven't a 2.6.0 release yet...
>

So I intended to do the SSH fdatasync patch in the release that added
fdatasync (2.4). I'd rather have that patch in a 2.4 series that
people can download if they want even if we don't SRU it, rather than
only land it in something newer.

I don't plan on SRUing this, but I still feel it is worth landing it
in a branch. If we ever do a 2.4 SRU, then we have this patch
available, rather than having to think about it after the fact, and
possibly backport a fix.

I tried grepping through the codebase, and could find no references to
testtools.monkey.patch. Even in bzr.dev tip.

Also, reading the code in monkey.py it *doesn't* handle the delattr
case. At least in 0.9.14.final.0 that I have here.

John
=:->
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (Cygwin)
Comment: Using GnuPG with Mozilla - http://www.enigmail.net/

iEYEARECAAYFAlCYz8sACgkQJdeBCYSNAANxmACg2DZAYhtSTKtWOMUScq5t5PMe
658AoNSuBXU/cFzZ4hB8O2CRYHjRKacm
=GGKp
-----END PGP SIGNATURE-----

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

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> On 11/6/2012 12:39 PM, Vincent Ladeuil wrote:
> > IIRC we didn't implement the unexisting attr case because we didn't
> > need it.
> >
> > Later on, testtools provided 'patch' which takes this case into
> > account.
> >
> > I think it would be better to use the testtools version for your
> > specific case.
> >
> > Also, unless you intend to cut a release for 2.4, I think we should
> > avoid landing patches on stable releases for which we don't intend
> > to make a release, the SRU process is resource intensive and we
> > still haven't a 2.6.0 release yet...
> >
>
> So I intended to do the SSH fdatasync patch in the release that added
> fdatasync (2.4). I'd rather have that patch in a 2.4 series that
> people can download if they want even if we don't SRU it, rather than
> only land it in something newer.

Daggy fixing as you did is enough (and a Good Thing anyway) for people to easily get the patch if they need.

>
> I don't plan on SRUing this,

Then don't make a potentially critical and required SRU harder by adding code that is not mandatory.

> I tried grepping through the codebase, and could find no references to
> testtools.monkey.patch. Even in bzr.dev tip.

Yes, because the existing overrideAttr cover all the needed cases.

>
> Also, reading the code in monkey.py it *doesn't* handle the delattr
> case. At least in 0.9.14.final.0 that I have here.

It is there in 0.9.12 and... still there on tip, check again (hint: restore()).

Revision history for this message
John A Meinel (jameinel) wrote :

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

>> Also, reading the code in monkey.py it *doesn't* handle the
>> delattr case. At least in 0.9.14.final.0 that I have here.
>
> It is there in 0.9.12 and... still there on tip, check again (hint:
> restore()).
>

It will delete an attribute that did not previously exist. There is
*no way* to specify that you want to delete an attribute that already
exists. (Hint *patch*)

John
=:->

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (Cygwin)
Comment: Using GnuPG with Mozilla - http://www.enigmail.net/

iEYEARECAAYFAlCZV1oACgkQJdeBCYSNAANGQACfQP4RdjnkBz6nYnochK/lvd9J
x1UAnA7E/yJinS5cxkveUYCOqBqSwn/g
=nb5c
-----END PGP SIGNATURE-----

Revision history for this message
John A Meinel (jameinel) wrote :

sent to pqm by email

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

> It will delete an attribute that did not previously exist. There is *no way* to specify that you want to delete an attribute that already exists. (Hint *patch*)

So you're taking the same syntax as 'patch' to mean something completely different ?

And you don't mention it in the docstring, news entry, cover letter ?

The fact that I mis-read your patch and that you fail to explain the difference with patch clearly demonstrate that other people will fall in the same trap.

Not to mention the other trap described by mgz.

A far safer and clearer idiom would be:

overrideAttr(obj, name) # Guarantees isolation
delattr(obj, name) # Testing what happens when name is deleted

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 2011-11-30 22:47:17 +0000
3+++ bzrlib/tests/__init__.py 2012-11-05 11:40:33 +0000
4@@ -1751,9 +1751,15 @@
5
6 :returns: The actual attr value.
7 """
8- value = getattr(obj, attr_name)
9 # The actual value is captured by the call below
10- self.addCleanup(setattr, obj, attr_name, value)
11+ value = getattr(obj, attr_name, _unitialized_attr)
12+ if value is _unitialized_attr:
13+ # When the test completes, the attribute should not exist, but if
14+ # we aren't setting a value, we don't need to do anything.
15+ if new is not _unitialized_attr:
16+ self.addCleanup(delattr, obj, attr_name)
17+ else:
18+ self.addCleanup(setattr, obj, attr_name, value)
19 if new is not _unitialized_attr:
20 setattr(obj, attr_name, new)
21 return value
22
23=== modified file 'bzrlib/tests/test_selftest.py'
24--- bzrlib/tests/test_selftest.py 2011-10-26 16:42:12 +0000
25+++ bzrlib/tests/test_selftest.py 2012-11-05 11:40:33 +0000
26@@ -1651,6 +1651,12 @@
27 self.assertRaises(AssertionError,
28 self.assertListRaises, _TestException, success_generator)
29
30+ def _run_successful_test(self, test):
31+ result = testtools.TestResult()
32+ test.run(result)
33+ self.assertTrue(result.wasSuccessful())
34+ return result
35+
36 def test_overrideAttr_without_value(self):
37 self.test_attr = 'original' # Define a test attribute
38 obj = self # Make 'obj' visible to the embedded test
39@@ -1666,8 +1672,7 @@
40 obj.test_attr = 'modified'
41 self.assertEqual('modified', obj.test_attr)
42
43- test = Test('test_value')
44- test.run(unittest.TestResult())
45+ self._run_successful_test(Test('test_value'))
46 self.assertEqual('original', obj.test_attr)
47
48 def test_overrideAttr_with_value(self):
49@@ -1683,10 +1688,41 @@
50 self.assertEqual('original', self.orig)
51 self.assertEqual('modified', obj.test_attr)
52
53- test = Test('test_value')
54- test.run(unittest.TestResult())
55+ self._run_successful_test(Test('test_value'))
56 self.assertEqual('original', obj.test_attr)
57
58+ def test_overrideAttr_with_no_existing_value_and_value(self):
59+ # Do not define the test_attribute
60+ obj = self # Make 'obj' visible to the embedded test
61+ class Test(tests.TestCase):
62+
63+ def setUp(self):
64+ tests.TestCase.setUp(self)
65+ self.orig = self.overrideAttr(obj, 'test_attr', new='modified')
66+
67+ def test_value(self):
68+ self.assertEqual(tests._unitialized_attr, self.orig)
69+ self.assertEqual('modified', obj.test_attr)
70+
71+ self._run_successful_test(Test('test_value'))
72+ self.assertRaises(AttributeError, getattr, obj, 'test_attr')
73+
74+ def test_overrideAttr_with_no_existing_value_and_no_value(self):
75+ # Do not define the test_attribute
76+ obj = self # Make 'obj' visible to the embedded test
77+ class Test(tests.TestCase):
78+
79+ def setUp(self):
80+ tests.TestCase.setUp(self)
81+ self.orig = self.overrideAttr(obj, 'test_attr')
82+
83+ def test_value(self):
84+ self.assertEqual(tests._unitialized_attr, self.orig)
85+ self.assertRaises(AttributeError, getattr, obj, 'test_attr')
86+
87+ self._run_successful_test(Test('test_value'))
88+ self.assertRaises(AttributeError, getattr, obj, 'test_attr')
89+
90 def test_recordCalls(self):
91 from bzrlib.tests import test_selftest
92 calls = self.recordCalls(

Subscribers

People subscribed via source and target branches