Merge lp:~jcsackett/launchpad/add-enums-to-models into lp:launchpad

Proposed by j.c.sackett
Status: Merged
Approved by: Brad Crittenden
Approved revision: no longer in the source branch.
Merged at revision: 11410
Proposed branch: lp:~jcsackett/launchpad/add-enums-to-models
Merge into: lp:launchpad
Prerequisite: lp:~bac/launchpad/progress-enums
Diff against target: 643 lines (+351/-54)
7 files modified
lib/lp/registry/browser/product.py (+3/-17)
lib/lp/registry/configure.zcml (+4/-1)
lib/lp/registry/doc/distribution.txt (+16/-3)
lib/lp/registry/doc/product.txt (+0/-4)
lib/lp/registry/model/distribution.py (+87/-21)
lib/lp/registry/model/product.py (+67/-8)
lib/lp/registry/tests/test_service_usage.py (+174/-0)
To merge this branch: bzr merge lp:~jcsackett/launchpad/add-enums-to-models
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+33255@code.launchpad.net

Commit message

Wraps the usage_enums (e.g. official_translations) on product and distribution in a property that makes use of and sets data on the official_ attrs (e.g. official_rosetta) to help transition over to the usage_enums.

Description of the change

= Summary =

IServiceUsage provides enums to give better data on how a product or distribution uses launchpad, in place of the old bool fields (e.g. official_rosetta). However, when we roll out these enums, they will default to UNKNOWN until we can migrate data.

This branch creates properties using the enums (moved to _[ENUM] format) to use the old bool data until data is set for the enum, so we can start using the enums in code.

== Proposed fix ==

Create properties using getters and setters to intelligent find the data from the new and old attributes, and set them properly moving forward.

== Pre-implementation notes ==

Talked with Curtis.

== Implementation details ==

As above.

This branch incorporates Brad's work on the progress bar, as they need to land together. ~bac/launchpad/progress-enums
== Tests ==

bin/test -vvc -t UsageEnums

== Demo and Q/A ==

Go to a project on launchpad.dev and ensure the configuration and status
links are correct.

= Launchpad lint =

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/registry/configure.zcml
  lib/lp/registry/browser/product.py
  lib/lp/registry/browser/tests/pillar-views.txt
  lib/lp/registry/interfaces/product.py
  lib/lp/registry/model/distribution.py
  lib/lp/registry/model/product.py
  lib/lp/registry/tests/test_distribution.py
  lib/lp/registry/tests/test_product.py

./lib/lp/registry/interfaces/product.py
     901: E301 expected 1 blank line, found 2
./lib/lp/registry/model/distribution.py
    1181: E231 missing whitespace after ','
    1185: E231 missing whitespace after ','
    1197: E231 missing whitespace after ','

E301 from comment issue
E231 from creation of tuple (something = ('some tuple',))

To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :

Hi Jon,

In def test_codehosting_usage(self): why do you make a new distribution instead of using self.distribution?

Same question for def test_bug_tracking_usage(self):

We discussed on IRC refactoring the tests since they are mostly the same.

Otherwise this branch rocks. Look forward to seeing it landed.

review: Approve (code)
Revision history for this message
j.c.sackett (jcsackett) wrote :
Download full text (18.8 KiB)

> In def test_codehosting_usage(self): why do you make a new distribution instead of using self.distribution?
> Same question for def test_bug_tracking_usage(self):

A bad job of refactoring early on? I must have just missed it when I moved the call to makeDistribution up into setUp.

>
> We discussed on IRC refactoring the tests since they are mostly the same.

There is now one BaseTestCase that does most of the work, with subclass that define the target to product or distribution.

Thanks for the pointers, Brad.

=== modified file 'lib/lp/registry/tests/test_distribution.py'
--- lib/lp/registry/tests/test_distribution.py 2010-08-20 18:37:10 +0000
+++ lib/lp/registry/tests/test_distribution.py 2010-08-20 21:48:02 +0000
@@ -10,155 +10,17 @@
 from lazr.lifecycle.snapshot import Snapshot
 from lp.registry.tests.test_distroseries import (
     TestDistroSeriesCurrentSourceReleases)
-from lp.app.enums import ServiceUsage
 from lp.registry.interfaces.distroseries import NoSuchDistroSeries
 from lp.registry.interfaces.series import SeriesStatus
 from lp.registry.interfaces.distribution import IDistribution

 from lp.soyuz.interfaces.distributionsourcepackagerelease import (
     IDistributionSourcePackageRelease)
-from lp.testing import (
- login_person,
- TestCaseWithFactory,
- )
+from lp.testing import TestCaseWithFactory
 from canonical.testing.layers import (
     DatabaseFunctionalLayer, LaunchpadFunctionalLayer)

-class TestDistributionUsageEnums(TestCaseWithFactory):
- """Tests the usage enums for the distribution."""
-
- layer = DatabaseFunctionalLayer
-
- def setUp(self):
- super(TestDistributionUsageEnums, self).setUp()
- self.distribution = self.factory.makeDistribution()
-
- def test_answers_usage_no_data(self):
- # By default, we don't know anything about a distribution
- self.assertEqual(
- ServiceUsage.UNKNOWN,
- self.distribution.answers_usage)
-
- def test_answers_usage_using_bool(self):
- # If the old bool says they use Launchpad, return LAUNCHPAD
- # if the ServiceUsage is unknown.
- login_person(self.distribution.owner)
- self.distribution.official_answers = True
- self.assertEqual(
- ServiceUsage.LAUNCHPAD,
- self.distribution.answers_usage)
-
- def test_answers_usage_with_enum_data(self):
- # If the enum has something other than UNKNOWN as its status,
- # use that.
- login_person(self.distribution.owner)
- self.distribution.answers_usage = ServiceUsage.EXTERNAL
- self.assertEqual(
- ServiceUsage.EXTERNAL,
- self.distribution.answers_usage)
-
- def test_answers_setter(self):
- login_person(self.distribution.owner)
- self.distribution.official_answers = True
- self.distribution.answers_usage = ServiceUsage.EXTERNAL
- self.assertEqual(
- False,
- self.distribution.official_answers)
- self.distribution.answers_usage = ServiceUsage.LAUNCHPAD
- self.assertEqual(
- True,
- self.distribution.official_answers)
-
- def test_codehosting_usage(self):
- ...

Revision history for this message
Robert Collins (lifeless) wrote :

On Sat, Aug 21, 2010 at 9:58 AM, j.c.sackett
<email address hidden> wrote:

> +
> +# Manually create the TestLoader list, because the UsageEnumsBaseTestCase
> +# shouldn't run.
> +test_list = [
> +    __name__ + ".TestDistributionUsageEnums",
> +    __name__ + ".TestProductUsageEnums",
> +    ]
> +
> +
> +def test_suite():
> +    return unittest.TestLoader().loadTestsFromNames(test_list)

There are a few slightly better ways of doing this.

Class UsageEnumsMixinIn
....stuff

class TestDistributionUsageEnums(TestCaseWithFactory, UsageEnumsMixin):
 ...

class TestProductUsageEnums(TestCaseWithFactory, UsageEnumsMixin):
...

is one.

Another is to use testscenarios, and your parameterisation would be
more explicit, but we don't use that in lp today AFAIK, so I won't
suggest adding it in (but you could get much the same helpers from
bzrlib.testing).

These are better because they don't have a class that looks-to-loaders
like a valid test case, but isn't.

-Rob

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/browser/product.py'
2--- lib/lp/registry/browser/product.py 2010-08-22 20:27:48 +0000
3+++ lib/lp/registry/browser/product.py 2010-08-22 20:27:49 +0000
4@@ -50,8 +50,6 @@
5 )
6 from operator import attrgetter
7
8-from lazr.delegates import delegates
9-from lazr.restful.interface import copy_field
10 import pytz
11 from z3c.ptcompat import ViewPageTemplateFile
12 from zope.app.form.browser import (
13@@ -79,7 +77,8 @@
14
15 from canonical.cachedproperty import cachedproperty
16 from canonical.config import config
17-<<<<<<< TREE
18+from lazr.delegates import delegates
19+from lazr.restful.interface import copy_field
20 from canonical.launchpad import (
21 _,
22 helpers,
23@@ -89,20 +88,6 @@
24 MultiStepView,
25 StepView,
26 )
27-=======
28-from lazr.delegates import delegates
29-from lazr.restful.interface import copy_field
30-from canonical.launchpad import _
31-from lp.services.fields import PillarAliases, PublicPersonChoice
32-from lp.app.enums import ServiceUsage
33-from lp.app.errors import NotFoundError
34-from lp.app.interfaces.headings import IEditableContextTitle
35-from lp.blueprints.browser.specificationtarget import (
36- HasSpecificationsMenuMixin)
37-from lp.bugs.interfaces.bugtask import RESOLVED_BUGTASK_STATUSES
38-from lp.code.browser.sourcepackagerecipelisting import HasRecipesMenuMixin
39-from lp.services.worlddata.interfaces.country import ICountry
40->>>>>>> MERGE-SOURCE
41 from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
42 from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet
43 from canonical.launchpad.mail import (
44@@ -157,6 +142,7 @@
45 QuestionTargetFacetMixin,
46 QuestionTargetTraversalMixin,
47 )
48+from lp.app.enums import ServiceUsage
49 from lp.app.errors import NotFoundError
50 from lp.app.interfaces.headings import IEditableContextTitle
51 from lp.blueprints.browser.specificationtarget import (
52
53=== modified file 'lib/lp/registry/configure.zcml'
54--- lib/lp/registry/configure.zcml 2010-08-22 20:27:48 +0000
55+++ lib/lp/registry/configure.zcml 2010-08-22 20:27:49 +0000
56@@ -1090,7 +1090,7 @@
57 interface="lp.registry.interfaces.product.IProductEditRestricted"/>
58 <require
59 permission="launchpad.Edit"
60- set_schema="lp.app.interfaces.launchpad.IServiceUsage"/>
61+ set_schema="lp.app.interfaces.launchpad.IServiceUsage"/>
62 <require
63 permission="launchpad.Edit"
64 set_attributes="bug_reporting_guidelines
65@@ -1405,6 +1405,9 @@
66 permission="launchpad.Edit"
67 interface="lp.registry.interfaces.distribution.IDistributionEditRestricted"/>
68 <require
69+ permission="launchpad.Edit"
70+ set_schema="lp.app.interfaces.launchpad.IServiceUsage"/>
71+ <require
72 permission="launchpad.Moderate"
73 interface="lp.registry.interfaces.distribution.IDistributionDriverRestricted"/>
74 <require
75
76=== modified file 'lib/lp/registry/doc/distribution.txt'
77--- lib/lp/registry/doc/distribution.txt 2010-08-06 14:49:35 +0000
78+++ lib/lp/registry/doc/distribution.txt 2010-08-22 20:27:49 +0000
79@@ -397,13 +397,26 @@
80 >>> print ubuntu.bug_tracking_usage.name
81 LAUNCHPAD
82
83-While the other attributes are not yet used.
84+While the other attributes track the other official_ attributes.
85
86+ >>> print ubuntu.official_rosetta
87+ True
88 >>> print ubuntu.translations_usage.name
89- UNKNOWN
90+ LAUNCHPAD
91+ >>> print ubuntu.official_answers
92+ True
93 >>> print ubuntu.answers_usage.name
94- UNKNOWN
95+ LAUNCHPAD
96+ >>> print ubuntu.official_blueprints
97+ True
98 >>> print ubuntu.blueprints_usage.name
99+ LAUNCHPAD
100+
101+If the official_ attributes are False, the usage enums don't know anything.
102+
103+ >>> login_person(ubuntu.owner.teamowner)
104+ >>> ubuntu.official_rosetta = False
105+ >>> print ubuntu.translations_usage.name
106 UNKNOWN
107
108 A distribution *cannot* specify that it uses codehosting. Currently there's
109
110=== modified file 'lib/lp/registry/doc/product.txt'
111--- lib/lp/registry/doc/product.txt 2010-08-06 14:49:35 +0000
112+++ lib/lp/registry/doc/product.txt 2010-08-22 20:27:49 +0000
113@@ -501,15 +501,11 @@
114 False
115 >>> print firefox.codehosting_usage.name
116 UNKNOWN
117- >>> print firefox.uses_launchpad
118- False
119 >>> firefox.development_focus.branch = factory.makeBranch(product=firefox)
120 >>> print firefox.official_codehosting
121 True
122 >>> print firefox.codehosting_usage.name
123 LAUNCHPAD
124- >>> print firefox.uses_launchpad
125- True
126
127 We can also find all the products that have branches.
128
129
130=== modified file 'lib/lp/registry/model/distribution.py'
131--- lib/lp/registry/model/distribution.py 2010-08-20 20:31:18 +0000
132+++ lib/lp/registry/model/distribution.py 2010-08-22 20:27:49 +0000
133@@ -63,6 +63,11 @@
134 ILaunchpadCelebrities,
135 )
136 from canonical.launchpad.interfaces.lpstorm import IStore
137+from lp.registry.model.announcement import MakesAnnouncements
138+from lp.soyuz.model.archive import Archive
139+from lp.soyuz.model.binarypackagename import BinaryPackageName
140+from lp.soyuz.model.binarypackagerelease import (
141+ BinaryPackageRelease)
142 from canonical.launchpad.validators.name import (
143 sanitize_name,
144 valid_name,
145@@ -273,7 +278,6 @@
146 else:
147 alsoProvides(self, IDerivativeDistribution)
148
149-
150 @property
151 def uploaders(self):
152 """See `IDistribution`."""
153@@ -310,21 +314,85 @@
154 return True in (self.official_malone, self.official_rosetta,
155 self.official_blueprints, self.official_answers)
156
157- answers_usage = EnumCol(
158+ _answers_usage = EnumCol(
159 dbName="answers_usage", notNull=True,
160 schema=ServiceUsage,
161 default=ServiceUsage.UNKNOWN)
162- blueprints_usage = EnumCol(
163+
164+ def _get_answers_usage(self):
165+ if self._answers_usage != ServiceUsage.UNKNOWN:
166+ # If someone has set something with the enum, use it.
167+ return self._answers_usage
168+ elif self.official_answers:
169+ return ServiceUsage.LAUNCHPAD
170+ return self._answers_usage
171+
172+ def _set_answers_usage(self, val):
173+ self._answers_usage = val
174+ if val == ServiceUsage.LAUNCHPAD:
175+ self.official_answers = True
176+ else:
177+ self.official_answers = False
178+
179+ answers_usage = property(
180+ _get_answers_usage,
181+ _set_answers_usage,
182+ doc="Indicates if the product uses the answers service.")
183+
184+ _blueprints_usage = EnumCol(
185 dbName="blueprints_usage", notNull=True,
186 schema=ServiceUsage,
187 default=ServiceUsage.UNKNOWN)
188- translations_usage = EnumCol(
189+
190+ def _get_blueprints_usage(self):
191+ if self._blueprints_usage != ServiceUsage.UNKNOWN:
192+ # If someone has set something with the enum, use it.
193+ return self._blueprints_usage
194+ elif self.official_blueprints:
195+ return ServiceUsage.LAUNCHPAD
196+ return self._blueprints_usage
197+
198+ def _set_blueprints_usage(self, val):
199+ self._blueprints_usage = val
200+ if val == ServiceUsage.LAUNCHPAD:
201+ self.official_blueprints = True
202+ else:
203+ self.official_blueprints = False
204+
205+ blueprints_usage = property(
206+ _get_blueprints_usage,
207+ _set_blueprints_usage,
208+ doc="Indicates if the product uses the blueprints service.")
209+
210+ _translations_usage = EnumCol(
211 dbName="translations_usage", notNull=True,
212 schema=ServiceUsage,
213 default=ServiceUsage.UNKNOWN)
214+
215+ def _get_translations_usage(self):
216+ if self._translations_usage != ServiceUsage.UNKNOWN:
217+ # If someone has set something with the enum, use it.
218+ return self._translations_usage
219+ elif self.official_rosetta:
220+ return ServiceUsage.LAUNCHPAD
221+ return self._translations_usage
222+
223+ def _set_translations_usage(self, val):
224+ self._translations_usage = val
225+ if val == ServiceUsage.LAUNCHPAD:
226+ self.official_rosetta = True
227+ else:
228+ self.official_rosetta = False
229+
230+ translations_usage = property(
231+ _get_translations_usage,
232+ _set_translations_usage,
233+ doc="Indicates if the product uses the translations service.")
234+
235 @property
236 def codehosting_usage(self):
237 return ServiceUsage.NOT_APPLICABLE
238+
239 @property
240 def bug_tracking_usage(self):
241 if not self.official_malone:
242@@ -551,9 +619,9 @@
243 if not self.full_functionality:
244 return None
245
246- urls = {'http_base_url' : http_base_url,
247- 'ftp_base_url' : ftp_base_url,
248- 'rsync_base_url' : rsync_base_url}
249+ urls = {'http_base_url': http_base_url,
250+ 'ftp_base_url': ftp_base_url,
251+ 'rsync_base_url': rsync_base_url}
252 for name, value in urls.items():
253 if value is not None:
254 urls[name] = IDistributionMirror[name].normalize(value)
255@@ -766,7 +834,7 @@
256
257 # filter based on completion. see the implementation of
258 # Specification.is_complete() for more details
259- completeness = Specification.completeness_clause
260+ completeness = Specification.completeness_clause
261
262 if SpecificationFilter.COMPLETE in filter:
263 query += ' AND ( %s ) ' % completeness
264@@ -1091,15 +1159,15 @@
265 find_spec = (
266 DistributionSourcePackageCache,
267 SourcePackageName,
268- SQL('rank(fti, ftq(%s)) AS rank' % sqlvalues(text))
269+ SQL('rank(fti, ftq(%s)) AS rank' % sqlvalues(text)),
270 )
271 origin = [
272 DistributionSourcePackageCache,
273 Join(
274 SourcePackageName,
275 DistributionSourcePackageCache.sourcepackagename ==
276- SourcePackageName.id
277- )
278+ SourcePackageName.id,
279+ ),
280 ]
281
282 publishing_condition = ''
283@@ -1140,8 +1208,7 @@
284 quote(text), quote_like(text), has_packaging_condition,
285 publishing_condition)
286 dsp_caches_with_ranks = store.using(*origin).find(
287- find_spec, condition
288- ).order_by('rank DESC')
289+ find_spec, condition).order_by('rank DESC')
290
291 return dsp_caches_with_ranks
292
293@@ -1160,8 +1227,7 @@
294 cache, source_package_name, rank = result
295 return DistributionSourcePackage(
296 self,
297- source_package_name
298- )
299+ source_package_name)
300
301 # Return the decorated result set so the consumer of these
302 # results will only see DSPs
303@@ -1225,7 +1291,7 @@
304 extra_clauses = (
305 BinaryPackageRelease.binarypackagenameID ==
306 DistroSeriesPackageCache.binarypackagenameID,
307- Match(search_vector_column, query_function)
308+ Match(search_vector_column, query_function),
309 )
310 where_spec = (self._binaryPackageSearchClause + extra_clauses)
311
312@@ -1481,11 +1547,11 @@
313 # XXX Julian 2007-08-16
314 # These component names should be Soyuz-wide constants.
315 componentMapToArchivePurpose = {
316- 'main' : ArchivePurpose.PRIMARY,
317- 'restricted' : ArchivePurpose.PRIMARY,
318- 'universe' : ArchivePurpose.PRIMARY,
319- 'multiverse' : ArchivePurpose.PRIMARY,
320- 'partner' : ArchivePurpose.PARTNER,
321+ 'main': ArchivePurpose.PRIMARY,
322+ 'restricted': ArchivePurpose.PRIMARY,
323+ 'universe': ArchivePurpose.PRIMARY,
324+ 'multiverse': ArchivePurpose.PRIMARY,
325+ 'partner': ArchivePurpose.PARTNER,
326 'contrib': ArchivePurpose.PRIMARY,
327 'non-free': ArchivePurpose.PRIMARY,
328 }
329
330=== modified file 'lib/lp/registry/model/product.py'
331--- lib/lp/registry/model/product.py 2010-08-22 20:27:48 +0000
332+++ lib/lp/registry/model/product.py 2010-08-22 20:27:49 +0000
333@@ -39,10 +39,6 @@
334 from zope.security.proxy import removeSecurityProxy
335
336 from canonical.cachedproperty import cachedproperty
337-<<<<<<< TREE
338-=======
339-from lazr.delegates import delegates
340->>>>>>> MERGE-SOURCE
341 from canonical.database.constants import UTC_NOW
342 from canonical.database.datetimecol import UtcDateTimeCol
343 from canonical.database.enumcol import EnumCol
344@@ -67,7 +63,6 @@
345 MAIN_STORE,
346 )
347 from canonical.launchpad.webapp.sorting import sorted_version_numbers
348-from canonical.lazr.utils import safe_hasattr
349 from lp.answers.interfaces.faqtarget import IFAQTarget
350 from lp.answers.interfaces.questioncollection import (
351 QUESTION_STATUS_DEFAULT_SEARCH,
352@@ -323,24 +318,26 @@
353 # XXX Need to remove official_codehosting column from Product
354 # table.
355 return self.development_focus.branch is not None
356+
357 @property
358 def official_anything(self):
359 return True in (self.official_malone, self.official_rosetta,
360 self.official_blueprints, self.official_answers,
361 self.official_codehosting)
362
363- answers_usage = EnumCol(
364+ _answers_usage = EnumCol(
365 dbName="answers_usage", notNull=True,
366 schema=ServiceUsage,
367 default=ServiceUsage.UNKNOWN)
368- blueprints_usage = EnumCol(
369+ _blueprints_usage = EnumCol(
370 dbName="blueprints_usage", notNull=True,
371 schema=ServiceUsage,
372 default=ServiceUsage.UNKNOWN)
373- translations_usage = EnumCol(
374+ _translations_usage = EnumCol(
375 dbName="translations_usage", notNull=True,
376 schema=ServiceUsage,
377 default=ServiceUsage.UNKNOWN)
378+
379 @property
380 def codehosting_usage(self):
381 if self.development_focus.branch is None:
382@@ -350,6 +347,7 @@
383 elif self.development_focus.branch.branch_type == BranchType.MIRRORED:
384 return ServiceUsage.EXTERNAL
385 return ServiceUsage.NOT_APPLICABLE
386+
387 @property
388 def bug_tracking_usage(self):
389 if self.official_malone:
390@@ -358,6 +356,7 @@
391 return ServiceUsage.UNKNOWN
392 else:
393 return ServiceUsage.EXTERNAL
394+
395 @property
396 def uses_launchpad(self):
397 """Does this distribution actually use Launchpad?"""
398@@ -573,6 +572,66 @@
399 super(Product, self).__storm_invalidated__()
400 self._cached_licenses = None
401
402+ def _get_answers_usage(self):
403+ if self._answers_usage != ServiceUsage.UNKNOWN:
404+ # If someone has set something with the enum, use it.
405+ return self._answers_usage
406+ elif self.official_answers:
407+ return ServiceUsage.LAUNCHPAD
408+ return self._answers_usage
409+
410+ def _set_answers_usage(self, val):
411+ self._answers_usage = val
412+ if val == ServiceUsage.LAUNCHPAD:
413+ self.official_answers = True
414+ else:
415+ self.official_answers = False
416+
417+ answers_usage = property(
418+ _get_answers_usage,
419+ _set_answers_usage,
420+ doc="Indicates if the product uses the answers service.")
421+
422+ def _get_blueprints_usage(self):
423+ if self._blueprints_usage != ServiceUsage.UNKNOWN:
424+ # If someone has set something with the enum, use it.
425+ return self._blueprints_usage
426+ elif self.official_blueprints:
427+ return ServiceUsage.LAUNCHPAD
428+ return self._blueprints_usage
429+
430+ def _set_blueprints_usage(self, val):
431+ self._blueprints_usage = val
432+ if val == ServiceUsage.LAUNCHPAD:
433+ self.official_blueprints = True
434+ else:
435+ self.official_blueprints = False
436+
437+ blueprints_usage = property(
438+ _get_blueprints_usage,
439+ _set_blueprints_usage,
440+ doc="Indicates if the product uses the blueprints service.")
441+
442+ def _get_translations_usage(self):
443+ if self._translations_usage != ServiceUsage.UNKNOWN:
444+ # If someone has set something with the enum, use it.
445+ return self._translations_usage
446+ elif self.official_rosetta:
447+ return ServiceUsage.LAUNCHPAD
448+ return self._translations_usage
449+
450+ def _set_translations_usage(self, val):
451+ self._translations_usage = val
452+ if val == ServiceUsage.LAUNCHPAD:
453+ self.official_rosetta = True
454+ else:
455+ self.official_rosetta = False
456+
457+ translations_usage = property(
458+ _get_translations_usage,
459+ _set_translations_usage,
460+ doc="Indicates if the product uses the translations service.")
461+
462 def _getLicenses(self):
463 """Get the licenses as a tuple."""
464 if self._cached_licenses is None:
465
466=== added file 'lib/lp/registry/tests/test_service_usage.py'
467--- lib/lp/registry/tests/test_service_usage.py 1970-01-01 00:00:00 +0000
468+++ lib/lp/registry/tests/test_service_usage.py 2010-08-22 20:27:49 +0000
469@@ -0,0 +1,174 @@
470+# Copyright 2010 Canonical Ltd. This software is licensed under the
471+# GNU Affero General Public License version 3 (see the file LICENSE).
472+
473+__metaclass__ = type
474+
475+import unittest
476+
477+from canonical.testing import DatabaseFunctionalLayer
478+
479+from lp.app.enums import ServiceUsage
480+from lp.testing import (
481+ login_person,
482+ TestCaseWithFactory,
483+ )
484+
485+
486+class UsageEnumsMixin(object):
487+ """Base class for testing the UsageEnums on their pillars."""
488+
489+ def setUp(self):
490+ self.target = None
491+
492+ def test_answers_usage_no_data(self):
493+ # By default, we don't know anything about a target
494+ self.assertEqual(
495+ ServiceUsage.UNKNOWN,
496+ self.target.answers_usage)
497+
498+ def test_answers_usage_using_bool(self):
499+ # If the old bool says they use Launchpad, return LAUNCHPAD
500+ # if the ServiceUsage is unknown.
501+ login_person(self.target.owner)
502+ self.target.official_answers = True
503+ self.assertEqual(
504+ ServiceUsage.LAUNCHPAD,
505+ self.target.answers_usage)
506+
507+ def test_answers_usage_with_enum_data(self):
508+ # If the enum has something other than UNKNOWN as its status,
509+ # use that.
510+ login_person(self.target.owner)
511+ self.target.answers_usage = ServiceUsage.EXTERNAL
512+ self.assertEqual(
513+ ServiceUsage.EXTERNAL,
514+ self.target.answers_usage)
515+
516+ def test_answers_setter(self):
517+ login_person(self.target.owner)
518+ self.target.official_answers = True
519+ self.target.answers_usage = ServiceUsage.EXTERNAL
520+ self.assertEqual(
521+ False,
522+ self.target.official_answers)
523+ self.target.answers_usage = ServiceUsage.LAUNCHPAD
524+ self.assertEqual(
525+ True,
526+ self.target.official_answers)
527+
528+ def test_codehosting_usage(self):
529+ # Only test get for codehosting; this has no setter because the
530+ # state is derived from other data.
531+ self.assertEqual(
532+ ServiceUsage.UNKNOWN,
533+ self.target.codehosting_usage)
534+
535+ def test_translations_usage_no_data(self):
536+ # By default, we don't know anything about a target
537+ self.assertEqual(
538+ ServiceUsage.UNKNOWN,
539+ self.target.translations_usage)
540+
541+ def test_translations_usage_using_bool(self):
542+ # If the old bool says they use Launchpad, return LAUNCHPAD
543+ # if the ServiceUsage is unknown.
544+ login_person(self.target.owner)
545+ self.target.official_rosetta = True
546+ self.assertEqual(
547+ ServiceUsage.LAUNCHPAD,
548+ self.target.translations_usage)
549+
550+ def test_translations_usage_with_enum_data(self):
551+ # If the enum has something other than UNKNOWN as its status,
552+ # use that.
553+ login_person(self.target.owner)
554+ self.target.translations_usage = ServiceUsage.EXTERNAL
555+ self.assertEqual(
556+ ServiceUsage.EXTERNAL,
557+ self.target.translations_usage)
558+
559+ def test_translations_setter(self):
560+ login_person(self.target.owner)
561+ self.target.official_rosetta = True
562+ self.target.translations_usage = ServiceUsage.EXTERNAL
563+ self.assertEqual(
564+ False,
565+ self.target.official_rosetta)
566+ self.target.translations_usage = ServiceUsage.LAUNCHPAD
567+ self.assertEqual(
568+ True,
569+ self.target.official_rosetta)
570+
571+ def test_bug_tracking_usage(self):
572+ # Only test get for bug_tracking; this has no setter because the
573+ # state is derived from other data.
574+ self.assertEqual(
575+ ServiceUsage.UNKNOWN,
576+ self.target.bug_tracking_usage)
577+
578+ def test_blueprints_usage_no_data(self):
579+ # By default, we don't know anything about a target
580+ self.assertEqual(
581+ ServiceUsage.UNKNOWN,
582+ self.target.blueprints_usage)
583+
584+ def test_blueprints_usage_using_bool(self):
585+ # If the old bool says they use Launchpad, return LAUNCHPAD
586+ # if the ServiceUsage is unknown.
587+ login_person(self.target.owner)
588+ self.target.official_blueprints = True
589+ self.assertEqual(
590+ ServiceUsage.LAUNCHPAD,
591+ self.target.blueprints_usage)
592+
593+ def test_blueprints_usage_with_enum_data(self):
594+ # If the enum has something other than UNKNOWN as its status,
595+ # use that.
596+ login_person(self.target.owner)
597+ self.target.blueprints_usage = ServiceUsage.EXTERNAL
598+ self.assertEqual(
599+ ServiceUsage.EXTERNAL,
600+ self.target.blueprints_usage)
601+
602+ def test_blueprints_setter(self):
603+ login_person(self.target.owner)
604+ self.target.official_blueprints = True
605+ self.target.blueprints_usage = ServiceUsage.EXTERNAL
606+ self.assertEqual(
607+ False,
608+ self.target.official_blueprints)
609+ self.target.blueprints_usage = ServiceUsage.LAUNCHPAD
610+ self.assertEqual(
611+ True,
612+ self.target.official_blueprints)
613+
614+
615+class TestDistributionUsageEnums(TestCaseWithFactory, UsageEnumsMixin):
616+ """Tests the usage enums for the distribution."""
617+
618+ layer = DatabaseFunctionalLayer
619+
620+ def setUp(self):
621+ super(TestDistributionUsageEnums, self).setUp()
622+ self.target = self.factory.makeDistribution()
623+
624+ def test_codehosting_usage(self):
625+ # This method must be changed for Distribution, because its
626+ # enum defaults to different data.
627+ self.assertEqual(
628+ ServiceUsage.NOT_APPLICABLE,
629+ self.target.codehosting_usage)
630+
631+
632+class TestProductUsageEnums(TestCaseWithFactory, UsageEnumsMixin):
633+ """Tests the usage enums for the product."""
634+
635+ layer = DatabaseFunctionalLayer
636+
637+ def setUp(self):
638+ super(TestProductUsageEnums, self).setUp()
639+ self.target = self.factory.makeProduct()
640+
641+
642+def test_suite():
643+ return unittest.TestLoader().loadTestsFromName(__name__)