launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #19478
[Merge] lp:~wgrant/launchpad/deconfuse-querycollector into lp:launchpad
William Grant has proposed merging lp:~wgrant/launchpad/deconfuse-querycollector into lp:launchpad.
Commit message:
Rename QueryCollector to RequestTimelineCollector and make it public.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~wgrant/launchpad/deconfuse-querycollector/+merge/272683
Rename QueryCollector to RequestTimelineCollector and make it public.
The distinction between it and StormStatementRecorder was previously entirely unclear, and tests had to import it from a private module.
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/deconfuse-querycollector into lp:launchpad.
=== modified file 'lib/lp/blueprints/browser/tests/test_sprint.py'
--- lib/lp/blueprints/browser/tests/test_sprint.py 2014-07-02 07:36:03 +0000
+++ lib/lp/blueprints/browser/tests/test_sprint.py 2015-09-29 03:35:42 +0000
@@ -9,8 +9,10 @@
from zope.security.proxy import removeSecurityProxy
from lp.app.enums import InformationType
-from lp.testing import BrowserTestCase
-from lp.testing._webservice import QueryCollector
+from lp.testing import (
+ BrowserTestCase,
+ RequestTimelineCollector,
+ )
from lp.testing.layers import DatabaseFunctionalLayer
from lp.testing.matchers import (
BrowsesWithQueryLimit,
@@ -37,7 +39,7 @@
blueprint = self.factory.makeSpecification()
link = blueprint.linkSprint(sprint, blueprint.owner)
link.acceptBy(sprint.owner)
- with QueryCollector() as recorder:
+ with RequestTimelineCollector() as recorder:
self.getViewBrowser(sprint)
self.assertThat(recorder, HasQueryCount(Equals(28)))
@@ -50,6 +52,6 @@
owner = removeSecurityProxy(blueprint).owner
link = removeSecurityProxy(blueprint).linkSprint(sprint, owner)
link.acceptBy(sprint.owner)
- with QueryCollector() as recorder:
+ with RequestTimelineCollector() as recorder:
self.getViewBrowser(sprint)
self.assertThat(recorder, HasQueryCount(Equals(20)))
=== modified file 'lib/lp/blueprints/browser/tests/test_views.py'
--- lib/lp/blueprints/browser/tests/test_views.py 2013-01-29 02:16:13 +0000
+++ lib/lp/blueprints/browser/tests/test_views.py 2015-09-29 03:35:42 +0000
@@ -17,9 +17,9 @@
from lp.testing import (
login,
logout,
+ RequestTimelineCollector,
TestCaseWithFactory,
)
-from lp.testing._webservice import QueryCollector
from lp.testing.layers import DatabaseFunctionalLayer
from lp.testing.matchers import HasQueryCount
from lp.testing.sampledata import ADMIN_EMAIL
@@ -63,7 +63,7 @@
for _ in range(10):
specs.append(self.factory.makeSpecification(
**{targettype: target}))
- collector = QueryCollector()
+ collector = RequestTimelineCollector()
collector.register()
self.addCleanup(collector.unregister)
url = canonical_url(target) + "/+assignments"
=== modified file 'lib/lp/bugs/tests/test_bugs_webservice.py'
--- lib/lp/bugs/tests/test_bugs_webservice.py 2012-12-20 20:09:02 +0000
+++ lib/lp/bugs/tests/test_bugs_webservice.py 2015-09-29 03:35:42 +0000
@@ -42,9 +42,9 @@
login_person,
logout,
person_logged_in,
+ RequestTimelineCollector,
TestCaseWithFactory,
)
-from lp.testing._webservice import QueryCollector
from lp.testing.layers import (
DatabaseFunctionalLayer,
LaunchpadFunctionalLayer,
@@ -210,7 +210,7 @@
self.factory.makeBugAttachment(self.bug)
webservice = LaunchpadWebServiceCaller(
'launchpad-library', 'salgado-change-anything')
- collector = QueryCollector()
+ collector = RequestTimelineCollector()
collector.register()
self.addCleanup(collector.unregister)
url = '/bugs/%d/attachments?ws.size=75' % self.bug.id
@@ -245,7 +245,7 @@
self.factory.makeBugComment(bug)
webservice = LaunchpadWebServiceCaller(
'launchpad-library', 'salgado-change-anything')
- collector = QueryCollector()
+ collector = RequestTimelineCollector()
collector.register()
self.addCleanup(collector.unregister)
url = '/bugs/%d/messages?ws.size=75' % bug.id
=== modified file 'lib/lp/bugs/tests/test_bugsubscription.py'
--- lib/lp/bugs/tests/test_bugsubscription.py 2012-04-16 23:02:44 +0000
+++ lib/lp/bugs/tests/test_bugsubscription.py 2015-09-29 03:35:42 +0000
@@ -13,9 +13,9 @@
from lp.testing import (
launchpadlib_for,
person_logged_in,
+ RequestTimelineCollector,
TestCaseWithFactory,
)
-from lp.testing._webservice import QueryCollector
from lp.testing.layers import DatabaseFunctionalLayer
from lp.testing.matchers import HasQueryCount
@@ -120,7 +120,7 @@
status=TeamMembershipStatus.ADMIN)
self.bug.subscribe(team_2, team_2.teamowner)
- collector = QueryCollector()
+ collector = RequestTimelineCollector()
collector.register()
self.addCleanup(collector.unregister)
with person_logged_in(self.subscriber):
=== modified file 'lib/lp/buildmaster/tests/test_webservice.py'
--- lib/lp/buildmaster/tests/test_webservice.py 2014-06-20 12:02:25 +0000
+++ lib/lp/buildmaster/tests/test_webservice.py 2015-09-29 03:35:42 +0000
@@ -17,9 +17,9 @@
admin_logged_in,
api_url,
logout,
+ RequestTimelineCollector,
TestCaseWithFactory,
)
-from lp.testing._webservice import QueryCollector
from lp.testing.layers import DatabaseFunctionalLayer
from lp.testing.matchers import HasQueryCount
from lp.testing.pages import (
@@ -43,7 +43,7 @@
builder)
names.append(builder.name)
logout()
- with QueryCollector() as recorder:
+ with RequestTimelineCollector() as recorder:
builders = self.webservice.get(
'/builders', api_version='devel').jsonBody()
self.assertContentEqual(
=== modified file 'lib/lp/code/model/tests/test_branchset.py'
--- lib/lp/code/model/tests/test_branchset.py 2012-09-18 18:36:09 +0000
+++ lib/lp/code/model/tests/test_branchset.py 2015-09-29 03:35:42 +0000
@@ -14,9 +14,9 @@
from lp.testing import (
login_person,
logout,
+ RequestTimelineCollector,
TestCaseWithFactory,
)
-from lp.testing._webservice import QueryCollector
from lp.testing.layers import DatabaseFunctionalLayer
from lp.testing.matchers import HasQueryCount
from lp.testing.pages import LaunchpadWebServiceCaller
@@ -48,7 +48,7 @@
def test_api_branches_query_count(self):
webservice = LaunchpadWebServiceCaller()
- collector = QueryCollector()
+ collector = RequestTimelineCollector()
collector.register()
self.addCleanup(collector.unregister)
# Get 'all' of the 50 branches this collection is limited to - rather
=== modified file 'lib/lp/registry/browser/tests/test_milestone.py'
--- lib/lp/registry/browser/tests/test_milestone.py 2015-01-29 14:14:01 +0000
+++ lib/lp/registry/browser/tests/test_milestone.py 2015-09-29 03:35:42 +0000
@@ -28,10 +28,10 @@
login_team,
logout,
person_logged_in,
+ RequestTimelineCollector,
StormStatementRecorder,
TestCaseWithFactory,
)
-from lp.testing._webservice import QueryCollector
from lp.testing.layers import DatabaseFunctionalLayer
from lp.testing.matchers import (
BrowsesWithQueryLimit,
@@ -443,7 +443,7 @@
# Seed the cookie cache and any other cross-request state we may gain
# in future. See lp.services.webapp.serssion: _get_secret.
browser.open(milestone_url)
- collector = QueryCollector()
+ collector = RequestTimelineCollector()
collector.register()
self.addCleanup(collector.unregister)
browser.open(milestone_url)
=== modified file 'lib/lp/registry/tests/test_person.py'
--- lib/lp/registry/tests/test_person.py 2015-07-14 13:04:52 +0000
+++ lib/lp/registry/tests/test_person.py 2015-09-29 03:35:42 +0000
@@ -75,10 +75,10 @@
login_person,
logout,
person_logged_in,
+ RequestTimelineCollector,
StormStatementRecorder,
TestCaseWithFactory,
)
-from lp.testing._webservice import QueryCollector
from lp.testing.dbuser import dbuser
from lp.testing.layers import DatabaseFunctionalLayer
from lp.testing.matchers import HasQueryCount
@@ -1254,7 +1254,7 @@
team.addMember(self.factory.makePerson(), team.teamowner)
team.addMember(self.factory.makePerson(), team.teamowner)
webservice = LaunchpadWebServiceCaller()
- collector = QueryCollector()
+ collector = RequestTimelineCollector()
collector.register()
self.addCleanup(collector.unregister)
url = "/~%s/participants" % team.name
=== modified file 'lib/lp/soyuz/browser/tests/test_archive_packages.py'
--- lib/lp/soyuz/browser/tests/test_archive_packages.py 2015-06-30 01:07:40 +0000
+++ lib/lp/soyuz/browser/tests/test_archive_packages.py 2015-09-29 03:35:42 +0000
@@ -35,9 +35,9 @@
login_person,
person_logged_in,
record_two_runs,
+ RequestTimelineCollector,
TestCaseWithFactory,
)
-from lp.testing._webservice import QueryCollector
from lp.testing.layers import (
DatabaseFunctionalLayer,
LaunchpadFunctionalLayer,
@@ -183,7 +183,7 @@
def test_source_query_counts(self):
query_baseline = 43
# Assess the baseline.
- collector = QueryCollector()
+ collector = RequestTimelineCollector()
collector.register()
self.addCleanup(collector.unregister)
ppa = self.factory.makeArchive()
@@ -224,7 +224,7 @@
def test_binary_query_counts(self):
query_baseline = 40
# Assess the baseline.
- collector = QueryCollector()
+ collector = RequestTimelineCollector()
collector.register()
self.addCleanup(collector.unregister)
ppa = self.factory.makeArchive()
=== modified file 'lib/lp/soyuz/browser/tests/test_publishing_webservice.py'
--- lib/lp/soyuz/browser/tests/test_publishing_webservice.py 2013-05-01 00:23:31 +0000
+++ lib/lp/soyuz/browser/tests/test_publishing_webservice.py 2015-09-29 03:35:42 +0000
@@ -10,9 +10,9 @@
from lp.testing import (
api_url,
person_logged_in,
+ RequestTimelineCollector,
TestCaseWithFactory,
)
-from lp.testing._webservice import QueryCollector
from lp.testing.layers import LaunchpadFunctionalLayer
from lp.testing.pages import webservice_for_person
@@ -52,7 +52,7 @@
query_counts = []
for i in range(3):
flush_database_caches()
- with QueryCollector() as collector:
+ with RequestTimelineCollector() as collector:
response = webservice.named_get(
url, 'binaryFileUrls', include_meta=True,
api_version='devel')
=== modified file 'lib/lp/soyuz/tests/test_archive.py'
--- lib/lp/soyuz/tests/test_archive.py 2015-09-25 13:54:46 +0000
+++ lib/lp/soyuz/tests/test_archive.py 2015-09-29 03:35:42 +0000
@@ -102,9 +102,9 @@
login,
login_person,
person_logged_in,
+ RequestTimelineCollector,
TestCaseWithFactory,
)
-from lp.testing._webservice import QueryCollector
from lp.testing.layers import (
DatabaseFunctionalLayer,
LaunchpadFunctionalLayer,
@@ -2377,7 +2377,7 @@
webservice = webservice_for_person(
ppa.owner, permission=OAuthPermission.READ_PRIVATE)
- collector = QueryCollector()
+ collector = RequestTimelineCollector()
collector.register()
self.addCleanup(collector.unregister)
=== modified file 'lib/lp/testing/__init__.py'
--- lib/lp/testing/__init__.py 2015-09-10 16:18:19 +0000
+++ lib/lp/testing/__init__.py 2015-09-29 03:35:42 +0000
@@ -105,6 +105,7 @@
)
from testtools.testcase import ExpectedException as TTExpectedException
import transaction
+from zope.app.testing import ztapi
from zope.component import (
ComponentLookupError,
getMultiAdapter,
@@ -117,6 +118,7 @@
verifyClass,
verifyObject as zope_verifyObject,
)
+from zope.publisher.interfaces import IEndRequestEvent
from zope.publisher.interfaces.browser import IBrowserRequest
from zope.security.management import queryInteraction
from zope.security.proxy import (
@@ -144,6 +146,7 @@
from lp.services.osutils import override_environ
from lp.services.webapp import canonical_url
from lp.services.webapp.adapter import (
+ get_request_statements,
print_queries,
start_sql_logging,
stop_sql_logging,
@@ -313,11 +316,13 @@
class StormStatementRecorder:
- """A storm tracer to count queries.
-
- This exposes the count and queries as
- lp.testing._webservice.QueryCollector does permitting its use with the
- HasQueryCount matcher.
+ """A Storm tracer to record all database queries.
+
+ Use the HasQueryCount matcher to check that code makes efficient use
+ of the database.
+
+ Similar to `RequestTimelineCollector`, but can operate outside a web
+ request context and only collects Storm queries.
It also meets the context manager protocol, so you can gather queries
easily:
@@ -364,6 +369,53 @@
return out.getvalue()
+class RequestTimelineCollector:
+ """Collect timeline events logged in web requests.
+
+ These are only retrievable at the end of a request, and for tests it is
+ useful to be able to make assertions about the calls made during a
+ request: this class provides a tool to gather them in a simple fashion.
+
+ See `StormStatementRecorder` for a Storm-specific collector that
+ works outside a request.
+
+ :ivar count: The count of db queries the last web request made.
+ :ivar queries: The list of queries made. See
+ lp.services.webapp.adapter.get_request_statements for more
+ information.
+ """
+
+ def __init__(self):
+ self._active = False
+ self.count = None
+ self.queries = None
+
+ def register(self):
+ """Start counting queries.
+
+ Be sure to call unregister when finished with the collector.
+
+ After each web request the count and queries attributes are updated.
+ """
+ ztapi.subscribe((IEndRequestEvent, ), None, self)
+ self._active = True
+
+ def __enter__(self):
+ self.register()
+ return self
+
+ def __call__(self, event):
+ if self._active:
+ self.queries = get_request_statements()
+ self.count = len(self.queries)
+
+ def unregister(self):
+ self._active = False
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.unregister()
+
+
def record_statements(function, *args, **kwargs):
"""Run the function and record the sql statements that are executed.
=== modified file 'lib/lp/testing/_webservice.py'
--- lib/lp/testing/_webservice.py 2014-06-13 15:07:12 +0000
+++ lib/lp/testing/_webservice.py 2015-09-29 03:35:42 +0000
@@ -20,14 +20,11 @@
)
from launchpadlib.launchpad import Launchpad
import transaction
-from zope.app.testing import ztapi
from zope.component import getUtility
-from zope.publisher.interfaces import IEndRequestEvent
import zope.testing.cleanup
from lp.registry.interfaces.person import IPersonSet
from lp.services.oauth.interfaces import IOAuthConsumerSet
-from lp.services.webapp.adapter import get_request_statements
from lp.services.webapp.interaction import ANONYMOUS
from lp.services.webapp.interfaces import OAuthPermission
from lp.services.webapp.publisher import canonical_url
@@ -145,47 +142,3 @@
zope.testing.cleanup.addCleanUp(_clean_up_cache, (cache,))
return Launchpad(credentials, None, None, service_root=service_root,
version=version, cache=cache)
-
-
-class QueryCollector:
- """Collect database calls made in web requests.
-
- These are only retrievable at the end of a request, and for tests it is
- useful to be able to make assertions about the calls made during a
- request: this class provides a tool to gather them in a simple fashion.
-
- :ivar count: The count of db queries the last web request made.
- :ivar queries: The list of queries made. See
- lp.services.webapp.adapter.get_request_statements for more
- information.
- """
-
- def __init__(self):
- self._active = False
- self.count = None
- self.queries = None
-
- def register(self):
- """Start counting queries.
-
- Be sure to call unregister when finished with the collector.
-
- After each web request the count and queries attributes are updated.
- """
- ztapi.subscribe((IEndRequestEvent, ), None, self)
- self._active = True
-
- def __enter__(self):
- self.register()
- return self
-
- def __call__(self, event):
- if self._active:
- self.queries = get_request_statements()
- self.count = len(self.queries)
-
- def unregister(self):
- self._active = False
-
- def __exit__(self, exc_type, exc_value, traceback):
- self.unregister()
=== modified file 'lib/lp/testing/matchers.py'
--- lib/lp/testing/matchers.py 2013-04-15 07:03:38 +0000
+++ lib/lp/testing/matchers.py 2015-09-29 03:35:42 +0000
@@ -46,9 +46,11 @@
from lp.services.database.sqlbase import flush_database_caches
from lp.services.webapp import canonical_url
from lp.services.webapp.batching import BatchNavigator
-from lp.testing import normalize_whitespace
+from lp.testing import (
+ normalize_whitespace,
+ RequestTimelineCollector,
+ )
from lp.testing._login import person_logged_in
-from lp.testing._webservice import QueryCollector
class BrowsesWithQueryLimit(Matcher):
@@ -80,18 +82,12 @@
context, view_name=self.view_name, **self.options)
browser = setupBrowserForUser(self.user)
flush_database_caches()
- collector = QueryCollector()
- collector.register()
- try:
+ with RequestTimelineCollector() as collector:
browser.open(context_url)
- counter = HasQueryCount(LessThan(self.query_limit))
- # When bug 724691 is fixed, this can become an AnnotateMismatch to
- # describe the object being rendered.
- return counter.match(collector)
- finally:
- # Unregister now in case this method is called multiple
- # times in a single test.
- collector.unregister()
+ counter = HasQueryCount(LessThan(self.query_limit))
+ # When bug 724691 is fixed, this can become an AnnotateMismatch to
+ # describe the object being rendered.
+ return counter.match(collector)
def __str__(self):
return "BrowsesWithQueryLimit(%s, %s)" % (self.query_limit, self.user)
@@ -168,10 +164,13 @@
class HasQueryCount(Matcher):
- """Adapt a Binary Matcher to the query count on a QueryCollector.
-
- If there is a mismatch, the queries from the collector are provided as a
- test attachment.
+ """Adapt a Binary Matcher to a query count.
+
+ May match against a StormStatementRecorder or a
+ RequestTimelineCollector.
+
+ If there is a mismatch, the queries from the collector are provided
+ as a test attachment.
"""
def __init__(self, count_matcher):
=== modified file 'lib/lp/testing/tests/test_matchers.py'
--- lib/lp/testing/tests/test_matchers.py 2015-07-08 16:05:11 +0000
+++ lib/lp/testing/tests/test_matchers.py 2015-09-29 03:35:42 +0000
@@ -18,10 +18,10 @@
from zope.security.proxy import ProxyFactory
from lp.testing import (
+ RequestTimelineCollector,
TestCase,
TestCaseWithFactory,
)
-from lp.testing._webservice import QueryCollector
from lp.testing.layers import DatabaseFunctionalLayer
from lp.testing.matchers import (
BrowsesWithQueryLimit,
@@ -206,7 +206,7 @@
def test_match(self):
matcher = HasQueryCount(Is(3))
- collector = QueryCollector()
+ collector = RequestTimelineCollector()
collector.count = 3
# not inspected
del collector.queries
@@ -214,7 +214,7 @@
def test_mismatch(self):
matcher = HasQueryCount(LessThan(2))
- collector = QueryCollector()
+ collector = RequestTimelineCollector()
collector.count = 2
collector.queries = [("foo", "bar"), ("baaz", "quux")]
mismatch = matcher.match(collector)
=== modified file 'lib/lp/translations/stories/standalone/xx-pofile-translate-performance.txt'
--- lib/lp/translations/stories/standalone/xx-pofile-translate-performance.txt 2010-08-03 03:43:09 +0000
+++ lib/lp/translations/stories/standalone/xx-pofile-translate-performance.txt 2015-09-29 03:35:42 +0000
@@ -5,8 +5,8 @@
translation suggestions. It's hard to keep database performance for this page
under control.
- >>> from lp.testing._webservice import QueryCollector
- >>> query_counter = QueryCollector()
+ >>> from lp.testing import RequestTimelineCollector
+ >>> query_counter = RequestTimelineCollector()
>>> query_counter.register()
=== Anonymous access ===
Follow ups