← Back to team overview

launchpad-reviewers team mailing list archive

[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