Merge lp:~abentley/launchpad/test-feature-flags into lp:launchpad/db-devel

Proposed by Aaron Bentley
Status: Merged
Approved by: Paul Hummer
Approved revision: no longer in the source branch.
Merged at revision: 9850
Proposed branch: lp:~abentley/launchpad/test-feature-flags
Merge into: lp:launchpad/db-devel
Diff against target: 130 lines (+83/-4)
2 files modified
lib/lp/testing/__init__.py (+40/-4)
lib/lp/testing/tests/test_testing.py (+43/-0)
To merge this branch: bzr merge lp:~abentley/launchpad/test-feature-flags
Reviewer Review Type Date Requested Status
Paul Hummer (community) Approve
Review via email: mp+37061@code.launchpad.net

Commit message

Provide test helpers for feature flags.

Description of the change

= Summary =
Provide test helpers for feature flags.

== Proposed fix ==
Provide a feature_flags context manager that allows feature flags to work
Provide a set_feature_flag function that allows a feature flag to be set.

== Pre-implementation notes ==
None, but these are based on the memcache tests that Robert gave as an example
of good feature flags tests.

== Implementation details ==
None

== Tests ==
bin/test -vvt TestFeatureFlags testing

== Demo and Q/A ==
None

= Launchpad lint =

The import issues are because testing imports from other modules to provide a
central location to import from. The rest are bogus.

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/testing/__init__.py
  lib/lp/testing/tests/test_testing.py

./lib/lp/testing/__init__.py
     132: 'anonymous_logged_in' imported but unused
     132: 'with_anonymous_login' imported but unused
     132: 'is_logged_in' imported but unused
     151: 'launchpadlib_for' imported but unused
     151: 'launchpadlib_credentials_for' imported but unused
     132: 'person_logged_in' imported but unused
     151: 'oauth_access_token_for' imported but unused
     132: 'login_celebrity' imported but unused
     132: 'with_celebrity_logged_in' imported but unused
     150: 'test_tales' imported but unused
     132: 'celebrity_logged_in' imported but unused
     132: 'run_with_login' imported but unused
     132: 'with_person_logged_in' imported but unused
     132: 'login_team' imported but unused
     132: 'login_person' imported but unused
     132: 'login_as' imported but unused
     692: E301 expected 1 blank line, found 0
     875: E301 expected 1 blank line, found 0
     901: E302 expected 2 blank lines, found 1
     977: E302 expected 2 blank lines, found 1

To post a comment you must log in.
Revision history for this message
Paul Hummer (rockstar) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/testing/__init__.py'
2--- lib/lp/testing/__init__.py 2010-09-22 18:37:57 +0000
3+++ lib/lp/testing/__init__.py 2010-09-29 20:38:55 +0000
4@@ -112,7 +112,10 @@
5 )
6 from canonical.launchpad.webapp.errorlog import ErrorReportEvent
7 from canonical.launchpad.webapp.interaction import ANONYMOUS
8-from canonical.launchpad.webapp.servers import WebServiceTestRequest
9+from canonical.launchpad.webapp.servers import (
10+ LaunchpadTestRequest,
11+ WebServiceTestRequest,
12+ )
13 from canonical.launchpad.windmill.testing import constants
14 from lp.codehosting.vfs import (
15 branch_id_to_path,
16@@ -120,6 +123,10 @@
17 )
18 from lp.registry.interfaces.packaging import IPackagingUtil
19 from lp.services.osutils import override_environ
20+from lp.services import features
21+from lp.services.features.flags import FeatureController
22+from lp.services.features.model import getFeatureStore, FeatureFlag
23+from lp.services.features.webapp import ScopesFromRequest
24 # Import the login helper functions here as it is a much better
25 # place to import them from in tests.
26 from lp.testing._login import (
27@@ -222,9 +229,10 @@
28
29 class StormStatementRecorder:
30 """A storm tracer to count queries.
31-
32- This exposes the count and queries as lp.testing._webservice.QueryCollector
33- does permitting its use with the HasQueryCount matcher.
34+
35+ This exposes the count and queries as
36+ lp.testing._webservice.QueryCollector does permitting its use with the
37+ HasQueryCount matcher.
38
39 It also meets the context manager protocol, so you can gather queries
40 easily:
41@@ -875,6 +883,19 @@
42 zope.event.subscribers[:] = old_subscribers
43
44
45+@contextmanager
46+def feature_flags():
47+ """Provide a context in which feature flags work."""
48+ empty_request = LaunchpadTestRequest()
49+ old_features = getattr(features.per_thread, 'features', None)
50+ features.per_thread.features = FeatureController(
51+ ScopesFromRequest(empty_request).lookup)
52+ try:
53+ yield
54+ finally:
55+ features.per_thread.features = old_features
56+
57+
58 # XXX: This doesn't seem like a generically-useful testing function. Perhaps
59 # it should go in a sub-module or something? -- jml
60 def get_lsb_information():
61@@ -977,6 +998,21 @@
62 return contents
63
64
65+def set_feature_flag(name, value, scope=u'default', priority=1):
66+ """Set a feature flag to the specified value.
67+
68+ In order to access the flag, use the feature_flags context manager or
69+ populate features.per_thread.features some other way.
70+ :param name: The name of the flag.
71+ :param value: The value of the flag.
72+ :param scope: The scope in which the specified value applies.
73+ """
74+ assert getattr(features.per_thread, 'features', None) is not None
75+ flag = FeatureFlag(
76+ scope=scope, flag=name, value=value, priority=priority)
77+ getFeatureStore().add(flag)
78+
79+
80 def validate_mock_class(mock_class):
81 """Validate method signatures in mock classes derived from real classes.
82
83
84=== added file 'lib/lp/testing/tests/test_testing.py'
85--- lib/lp/testing/tests/test_testing.py 1970-01-01 00:00:00 +0000
86+++ lib/lp/testing/tests/test_testing.py 2010-09-29 20:38:55 +0000
87@@ -0,0 +1,43 @@
88+# Copyright 2010 Canonical Ltd. This software is licensed under the
89+# GNU Affero General Public License version 3 (see the file LICENSE).
90+
91+from __future__ import with_statement
92+
93+"""Tests for the testing module."""
94+
95+__metaclass__ = type
96+
97+import unittest
98+
99+from canonical.testing.layers import DatabaseFunctionalLayer
100+from lp.services.features import getFeatureFlag
101+from lp.testing import (
102+ feature_flags,
103+ set_feature_flag,
104+ TestCase,
105+ )
106+
107+
108+class TestFeatureFlags(TestCase):
109+
110+ layer = DatabaseFunctionalLayer
111+
112+ def test_set_feature_flags_raises_if_not_available(self):
113+ """set_feature_flags prevents mistakes mistakes by raising."""
114+ self.assertRaises(AssertionError, set_feature_flag, u'name', u'value')
115+
116+ def test_flags_set_within_feature_flags_context(self):
117+ """In the feature_flags context, set/get works."""
118+ self.useContext(feature_flags())
119+ set_feature_flag(u'name', u'value')
120+ self.assertEqual('value', getFeatureFlag('name'))
121+
122+ def test_flags_unset_outside_feature_flags_context(self):
123+ """get fails when used outside the feature_flags context."""
124+ with feature_flags():
125+ set_feature_flag(u'name', u'value')
126+ self.assertIs(None, getFeatureFlag('name'))
127+
128+
129+def test_suite():
130+ return unittest.TestLoader().loadTestsFromName(__name__)

Subscribers

People subscribed via source and target branches

to status/vote changes: