Merge lp:~lifeless/launchpad/registry into lp:launchpad

Proposed by Robert Collins on 2010-08-05
Status: Merged
Approved by: Michael Hudson-Doyle on 2010-08-05
Approved revision: no longer in the source branch.
Merged at revision: 11298
Proposed branch: lp:~lifeless/launchpad/registry
Merge into: lp:launchpad
Diff against target: 182 lines (+53/-40)
4 files modified
lib/lp/registry/tests/test_listteammembers.py (+0/-6)
lib/lp/registry/tests/test_person.py (+7/-8)
lib/lp/testing/_webservice.py (+43/-0)
lib/lp/translations/stories/standalone/xx-pofile-translate-performance.txt (+3/-26)
To merge this branch: bzr merge lp:~lifeless/launchpad/registry
Reviewer Review Type Date Requested Status
Michael Hudson-Doyle 2010-08-05 Approve on 2010-08-05
Review via email: mp+31805@code.launchpad.net

Commit Message

Factor out QueryCounter from the rosetta tests for reuse and cleanup a couple of registry tests.

Description of the Change

Some minor test cleanups from my performance day dive.

To post a comment you must log in.
Michael Hudson-Doyle (mwhudson) wrote :

Have you seen assertStatementCount on lp.testing.TestCase? It's a bit different, but more direct and general (I imagine get_request_statements() uses a storm trace hook at some level), and I wonder if that should be the approach we aim to promote and generalize?

The test cleanups look fine.

review: Needs Information
Robert Collins (lifeless) wrote :

13:19 < lifeless> I looked for it
13:19 < lifeless> but found QueryCounter
13:19 < lifeless> oh yay two totally different ways of doing the same thing.
13:19 < lifeless> uhm
13:20 < lifeless> sigh, thanks.
13:20 < lifeless> I think my refactoring is not harmful
13:20 < lifeless> in fact, I think QueryCounter is useful because its
aimed directly at the request, not at the storm layer
13:20 < lifeless> so its useful for 'is this page request going to be ok'
13:20 < lifeless> which, record_statements on allmembers is not a good
answers for

Michael Hudson-Doyle (mwhudson) wrote :

We talked about this a bit on IRC.

Happy for the branch to land as is. A colon seems to have become misplaced in QueryCounter.__doc__, fix that and you're good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/tests/test_listteammembers.py'
2--- lib/lp/registry/tests/test_listteammembers.py 2010-04-07 20:36:28 +0000
3+++ lib/lp/registry/tests/test_listteammembers.py 2010-08-05 00:26:50 +0000
4@@ -1,8 +1,6 @@
5 # Copyright 2009 Canonical Ltd. This software is licensed under the
6 # GNU Affero General Public License version 3 (see the file LICENSE).
7
8-import unittest
9-
10 from canonical.testing import LaunchpadZopelessLayer
11
12 from lp.registry.scripts import listteammembers
13@@ -107,7 +105,3 @@
14 self.assertRaises(
15 listteammembers.NoSuchTeamError, listteammembers.process_team,
16 'nosuchteam-matey')
17-
18-
19-def test_suite():
20- return unittest.TestLoader().loadTestsFromName(__name__)
21
22=== modified file 'lib/lp/registry/tests/test_person.py'
23--- lib/lp/registry/tests/test_person.py 2010-07-14 16:08:24 +0000
24+++ lib/lp/registry/tests/test_person.py 2010-08-05 00:26:50 +0000
25@@ -3,7 +3,6 @@
26
27 __metaclass__ = type
28
29-import unittest
30 from datetime import datetime
31 import pytz
32 import time
33@@ -35,7 +34,7 @@
34 from lp.bugs.interfaces.bugtask import IllegalRelatedBugTasksParams
35 from lp.answers.model.answercontact import AnswerContact
36 from lp.blueprints.model.specification import Specification
37-from lp.testing import login_person, logout, TestCaseWithFactory
38+from lp.testing import login_person, logout, TestCase, TestCaseWithFactory
39 from lp.testing.views import create_initialized_view
40 from lp.registry.interfaces.person import PrivatePersonLinkageError
41 from canonical.testing.layers import DatabaseFunctionalLayer, reconnect_stores
42@@ -214,12 +213,14 @@
43 self.assertEqual('(\\u0170-tester)>', displayname)
44
45
46-class TestPersonSet(unittest.TestCase):
47+class TestPersonSet(TestCase):
48 """Test `IPersonSet`."""
49 layer = DatabaseFunctionalLayer
50
51 def setUp(self):
52+ TestCase.setUp(self)
53 login(ANONYMOUS)
54+ self.addCleanup(logout)
55 self.person_set = getUtility(IPersonSet)
56
57 def test_isNameBlacklisted(self):
58@@ -299,12 +300,14 @@
59 self.assertEqual(expected, second_account.openid_identifier)
60
61
62-class TestCreatePersonAndEmail(unittest.TestCase):
63+class TestCreatePersonAndEmail(TestCase):
64 """Test `IPersonSet`.createPersonAndEmail()."""
65 layer = DatabaseFunctionalLayer
66
67 def setUp(self):
68+ TestCase.setUp(self)
69 login(ANONYMOUS)
70+ self.addCleanup(logout)
71 self.person_set = getUtility(IPersonSet)
72
73 def test_duplicated_name_not_accepted(self):
74@@ -545,7 +548,3 @@
75 names = [entry['project'].name for entry in results]
76 self.assertEqual(
77 ['cc', 'bb', 'aa', 'dd', 'ee'], names)
78-
79-
80-def test_suite():
81- return unittest.TestLoader().loadTestsFromName(__name__)
82
83=== modified file 'lib/lp/testing/_webservice.py'
84--- lib/lp/testing/_webservice.py 2010-04-28 14:22:03 +0000
85+++ lib/lp/testing/_webservice.py 2010-08-05 00:26:50 +0000
86@@ -13,10 +13,14 @@
87
88
89 import transaction
90+from zope.app.publication.interfaces import IEndRequestEvent
91+from zope.app.testing import ztapi
92 from zope.component import getUtility
93+
94 from launchpadlib.credentials import AccessToken, Credentials
95 from launchpadlib.launchpad import Launchpad
96
97+from canonical.launchpad.webapp.adapter import get_request_statements
98 from canonical.launchpad.webapp.interaction import ANONYMOUS
99 from canonical.launchpad.webapp.interfaces import OAuthPermission
100 from canonical.launchpad.interfaces import (
101@@ -122,3 +126,42 @@
102 transaction.commit()
103 version = version or Launchpad.DEFAULT_VERSION
104 return Launchpad(credentials, service_root, version=version)
105+
106+
107+class QueryCollector:
108+ """Collect database calls made in web requests.
109+
110+ These are only retrievable at the end of a request, and for tests it is
111+ useful to be able to make aassertions about the calls made during a request
112+ : this class provides a tool to gather them in a simple fashion.
113+
114+ :ivar count: The count of db queries the last web request made.
115+ :ivar queries: The list of queries made. See
116+ canonical.launchpad.webapp.adapter.get_request_statements for more
117+ information.
118+ """
119+
120+ def __init__(self):
121+ self._active = False
122+ self.count = None
123+ self.queries = None
124+
125+ def register(self):
126+ """Start counting queries.
127+
128+ Be sure to call unregister when finished with the collector.
129+
130+ After each web request the count and queries attributes are updated.
131+ """
132+ ztapi.subscribe((IEndRequestEvent, ), None, self)
133+ self._active = True
134+
135+ def __call__(self, event):
136+ if self._active:
137+ self.queries = get_request_statements()
138+ self.count = len(self.queries)
139+
140+ def unregister(self):
141+ self._active = False
142+
143+
144
145=== modified file 'lib/lp/translations/stories/standalone/xx-pofile-translate-performance.txt'
146--- lib/lp/translations/stories/standalone/xx-pofile-translate-performance.txt 2009-07-01 20:45:39 +0000
147+++ lib/lp/translations/stories/standalone/xx-pofile-translate-performance.txt 2010-08-05 00:26:50 +0000
148@@ -5,32 +5,9 @@
149 translation suggestions. It's hard to keep database performance for this page
150 under control.
151
152- >>> from canonical.launchpad.webapp.adapter import get_request_statements
153- >>> from zope.app.testing import ztapi
154- >>> from zope.app.publication.interfaces import IEndRequestEvent
155-
156- >>> class QueryCounter:
157- ... """Count database statements performed in last request.
158- ...
159- ... We can't get at this count after the browser has completed its
160- ... request, because that will reset the tracking logic. Instead, we
161- ... subscribe to the Zope "request completed" event and grab our count
162- ... when that event comes along.
163- ... """
164- ... _active = True
165- ... count = None
166- ...
167- ... def __init__(self):
168- ... ztapi.subscribe((IEndRequestEvent, ), None, self)
169- ...
170- ... def __call__(self, event):
171- ... if self._active:
172- ... self.count = len(get_request_statements())
173- ...
174- ... def unregister(self):
175- ... self._active = False
176-
177- >>> query_counter = QueryCounter()
178+ >>> from lp.testing._webservice import QueryCollector
179+ >>> query_counter = QueryCollector()
180+ >>> query_counter.register()
181
182 === Anonymous access ===
183