← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~lifeless/launchpad/registry into lp:launchpad/devel

 

Robert Collins has proposed merging lp:~lifeless/launchpad/registry into lp:launchpad/devel.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)


Another step along my arc of fixing the /participation API performance. This adds a matcher for query counts and a dependency on a newer testtools to get LessThan.
-- 
https://code.launchpad.net/~lifeless/launchpad/registry/+merge/31830
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~lifeless/launchpad/registry into lp:launchpad/devel.
=== modified file 'lib/lp/testing/matchers.py'
--- lib/lp/testing/matchers.py	2010-08-02 20:56:25 +0000
+++ lib/lp/testing/matchers.py	2010-08-05 10:04:46 +0000
@@ -5,6 +5,7 @@
 __all__ = [
     'DoesNotProvide',
     'DoesNotCorrectlyProvide',
+    'HasQueryCount',
     'IsNotProxied',
     'IsProxied',
     'Provides',
@@ -16,6 +17,8 @@
     BrokenImplementation, BrokenMethodImplementation, DoesNotImplement)
 from zope.security.proxy import builtin_isinstance, Proxy
 
+from testtools.content import Content
+from testtools.content_type import ContentType
 from testtools.matchers import Matcher, Mismatch
 
 
@@ -89,6 +92,45 @@
         return None
 
 
+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.
+    """
+
+    def __init__(self, count_matcher):
+        """Create a HasQueryCount that will match using count_matcher."""
+        self.count_matcher = count_matcher
+
+    def __str__(self):
+        return "HasQueryCount(%s)" % self.count_matcher
+
+    def match(self, something):
+        mismatch = self.count_matcher.match(something.count)
+        if mismatch is None:
+            return None
+        return _MismatchedQueryCount(mismatch, something)
+
+
+class _MismatchedQueryCount(Mismatch):
+    """The Mismatch for a HasQueryCount matcher."""
+
+    def __init__(self, mismatch, query_collector):
+        self.count_mismatch = mismatch
+        self.query_collector = query_collector
+
+    def describe(self):
+        return "queries do not match: %s" % (self.count_mismatch.describe(),)
+
+    def get_details(self):
+        result = []
+        for query in self.query_collector.queries:
+            result.append(unicode(query).encode('utf8'))
+        return {'queries': Content(ContentType('text', 'plain',
+            {'charset': 'utf8'}), lambda:['\n'.join(result)])}
+ 
+
 class IsNotProxied(Mismatch):
     """An object is not proxied."""
 

=== modified file 'lib/lp/testing/tests/test_matchers.py'
--- lib/lp/testing/tests/test_matchers.py	2010-08-02 19:52:59 +0000
+++ lib/lp/testing/tests/test_matchers.py	2010-08-05 10:04:46 +0000
@@ -11,8 +11,11 @@
 
 from lp.testing import TestCase
 from lp.testing.matchers import (
-    DoesNotCorrectlyProvide, DoesNotProvide, IsNotProxied, IsProxied,
-    Provides, ProvidesAndIsProxied)
+    DoesNotCorrectlyProvide, DoesNotProvide, HasQueryCount, IsNotProxied,
+    IsProxied, Provides, ProvidesAndIsProxied)
+from lp.testing._webservice import QueryCollector
+
+from testtools.matchers import Is, Not, LessThan
 
 
 class ITestInterface(Interface):
@@ -169,3 +172,41 @@
         obj = ProxyFactory(object(), checker=NamesChecker())
         matcher = ProvidesAndIsProxied(ITestInterface)
         self.assertIsInstance(matcher.match(obj), DoesNotProvide)
+
+
+class TestQueryMatching(TestCase):
+    """Query matching is a work in progress and can be factored out more.
+
+    For now its pretty hard coded to the initial use case and overlaps some
+    unwritten hypothetical testtools infrastructure - e.g. permitting use of
+    attrgetter and the like.
+    """
+
+    def test_match(self):
+        matcher = HasQueryCount(Is(3))
+        collector = QueryCollector()
+        collector.count = 3 
+        # not inspected
+        del collector.queries
+        self.assertThat(matcher.match(collector), Is(None))
+
+    def test_mismatch(self):
+        matcher = HasQueryCount(LessThan(2))
+        collector = QueryCollector()
+        collector.count = 2
+        collector.queries = [("foo", "bar"), ("baaz", "quux")]
+        mismatch = matcher.match(collector)
+        self.assertThat(mismatch, Not(Is(None)))
+        details = mismatch.get_details()
+        lines = []
+        for name, content in details.items():
+            self.assertEqual("queries", name)
+            self.assertEqual("text", content.content_type.type)
+            lines.append(''.join(content.iter_text()))
+        self.assertEqual(["('foo', 'bar')\n('baaz', 'quux')"],
+            lines)
+        self.assertEqual(
+            "queries do not match: %s" % (LessThan(2).match(2).describe(),),
+            mismatch.describe())
+
+

=== modified file 'versions.cfg'
--- versions.cfg	2010-07-26 16:00:01 +0000
+++ versions.cfg	2010-08-05 10:04:46 +0000
@@ -67,7 +67,8 @@
 # This is Storm 0.15 with r342 cherry-picked which fixes a memory leak
 # important for message sharing migration script.
 storm = 0.15danilo-storm-launchpad-r342
-testtools = 0.9.2
+# Has the LessThan matcher.
+testtools = 0.9.6dev91
 transaction = 1.0.0
 Twisted = 10.1.0
 uuid = 1.30


Follow ups