~launchpad-pqm/launchpad/devel

12510.5.1 by Aaron Bentley
Test that SourcepackageRecipe.setPackaging works.
1
# Copyright 2009, 2011 Canonical Ltd.  This software is licensed under the
8687.15.22 by Karl Fogel
Add the copyright header block to the remaining .py files.
2
# GNU Affero General Public License version 3 (see the file LICENSE).
7362.13.12 by Jonathan Lange
Add a path property to ISourcePackage.
3
4
"""Unit tests for ISourcePackage implementations."""
5
6
__metaclass__ = type
7
12769.1.2 by Abel Deuring
SourcePackage.setpackaging() now deletes an existing Packaging record and creates a new one, if one exists, in order to trigger the creation of jobs for translation sharing.
8
from lazr.lifecycle.event import (
9
    ObjectCreatedEvent,
10
    ObjectDeletedEvent,
11
    )
13130.1.12 by Curtis Hovey
Sorted imports.
12
from storm.locals import Store
13
import transaction
7921.1.4 by Jonathan Lange
Initial implementation of getting official branch for sourcepackage.
14
from zope.component import getUtility
13139.3.2 by Francis J. Lacoste
Protect setBranch through launchpad.Edit
15
from zope.security.checker import canAccess
7921.1.18 by Jonathan Lange
Add a test to guarantee that you cannot set a branch.
16
from zope.security.interfaces import Unauthorized
13139.3.1 by Francis J. Lacoste
Define launchpad.Edit permission on ISourcePackge.
17
from zope.security.management import checkPermission
7921.1.4 by Jonathan Lange
Initial implementation of getting official branch for sourcepackage.
18
13130.1.6 by Curtis Hovey
Move ILaunchpadCelebrity to lp.app.
19
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
13139.3.3 by Francis J. Lacoste
Remove IMakeOfficialBranchLinks non-utility.
20
from lp.code.model.seriessourcepackagebranch import (
21
    SeriesSourcePackageBranchSet,
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
22
    )
9378.7.10 by Jonathan Lange
Handle the case where no partner archive exists.
23
from lp.registry.interfaces.distribution import NoPartnerArchive
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
24
from lp.registry.interfaces.pocket import PackagePublishingPocket
10054.26.1 by Adi Roiban
Refactor DistroSeriesStatus to SeriesStatus; Don't prompt for setting up translations for obsolete product series.
25
from lp.registry.interfaces.series import SeriesStatus
13991.1.4 by Curtis Hovey
Moved official DSP branch into SourcePackage becaus it is the
26
from lp.registry.model.distributionsourcepackage import (
27
    DistributionSourcePackage,
28
    )
12510.5.15 by Aaron Bentley
Add paranoid deletePackaging test.
29
from lp.registry.model.packaging import Packaging
11411.6.12 by Julian Edwards
Move PackagePublishingStatus/Priority
30
from lp.soyuz.enums import (
31
    ArchivePurpose,
32
    PackagePublishingStatus,
33
    )
9378.7.9 by Jonathan Lange
Return the partner archive if it exists and the package was last uploaded
34
from lp.soyuz.interfaces.component import IComponentSet
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
35
from lp.testing import (
12769.1.2 by Abel Deuring
SourcePackage.setpackaging() now deletes an existing Packaging record and creates a new one, if one exists, in order to trigger the creation of jobs for translation sharing.
36
    EventRecorder,
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
37
    person_logged_in,
38
    TestCaseWithFactory,
12510.5.1 by Aaron Bentley
Test that SourcepackageRecipe.setPackaging works.
39
    WebServiceTestCase,
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
40
    )
14612.2.1 by William Grant
format-imports on lib/. So many imports.
41
from lp.testing.layers import DatabaseFunctionalLayer
10374.1.2 by Edwin Grubbs
Added tests.
42
from lp.testing.views import create_initialized_view
7362.13.12 by Jonathan Lange
Add a path property to ISourcePackage.
43
44
45
class TestSourcePackage(TestCaseWithFactory):
46
47
    layer = DatabaseFunctionalLayer
48
49
    def test_path(self):
50
        sourcepackage = self.factory.makeSourcePackage()
51
        self.assertEqual(
52
            '%s/%s/%s' % (
53
                sourcepackage.distribution.name,
54
                sourcepackage.distroseries.name,
55
                sourcepackage.sourcepackagename.name),
56
            sourcepackage.path)
57
7921.1.4 by Jonathan Lange
Initial implementation of getting official branch for sourcepackage.
58
    def test_getBranch_no_branch(self):
59
        # If there's no official branch for that pocket of a source package,
60
        # getBranch returns None.
61
        sourcepackage = self.factory.makeSourcePackage()
62
        branch = sourcepackage.getBranch(PackagePublishingPocket.RELEASE)
63
        self.assertIs(None, branch)
64
65
    def test_getBranch_exists(self):
66
        # If there is a SeriesSourcePackageBranch entry for that source
67
        # package and pocket, then return the branch.
68
        sourcepackage = self.factory.makeSourcePackage()
69
        registrant = self.factory.makePerson()
70
        branch = self.factory.makePackageBranch(sourcepackage=sourcepackage)
13139.3.3 by Francis J. Lacoste
Remove IMakeOfficialBranchLinks non-utility.
71
        SeriesSourcePackageBranchSet.new(
7921.1.4 by Jonathan Lange
Initial implementation of getting official branch for sourcepackage.
72
            sourcepackage.distroseries, PackagePublishingPocket.RELEASE,
73
            sourcepackage.sourcepackagename, branch, registrant)
74
        official_branch = sourcepackage.getBranch(
75
            PackagePublishingPocket.RELEASE)
76
        self.assertEqual(branch, official_branch)
77
7921.1.8 by Jonathan Lange
Add a setBranch method to sourcepackage.
78
    def test_setBranch(self):
79
        # We can set the official branch for a pocket of a source package.
80
        sourcepackage = self.factory.makeSourcePackage()
81
        pocket = PackagePublishingPocket.RELEASE
82
        registrant = self.factory.makePerson()
83
        branch = self.factory.makePackageBranch(sourcepackage=sourcepackage)
13139.3.3 by Francis J. Lacoste
Remove IMakeOfficialBranchLinks non-utility.
84
        with person_logged_in(sourcepackage.distribution.owner):
85
            sourcepackage.setBranch(pocket, branch, registrant)
7921.1.8 by Jonathan Lange
Add a setBranch method to sourcepackage.
86
        self.assertEqual(branch, sourcepackage.getBranch(pocket))
13991.1.4 by Curtis Hovey
Moved official DSP branch into SourcePackage becaus it is the
87
        # A DSP was created for the official branch.
88
        new_dsp = DistributionSourcePackage._get(
89
            sourcepackage.distribution, sourcepackage.sourcepackagename)
90
        self.assertIsNot(None, new_dsp)
7921.1.8 by Jonathan Lange
Add a setBranch method to sourcepackage.
91
8211.4.7 by Jonathan Lange
Make it possible to change the linked branch, in a somewhat crappy way.
92
    def test_change_branch_once_set(self):
93
        # We can change the official branch for a a pocket of a source package
94
        # even after it has already been set.
95
        sourcepackage = self.factory.makeSourcePackage()
96
        pocket = PackagePublishingPocket.RELEASE
97
        registrant = self.factory.makePerson()
98
        branch = self.factory.makePackageBranch(sourcepackage=sourcepackage)
99
        new_branch = self.factory.makePackageBranch(
100
            sourcepackage=sourcepackage)
13139.3.3 by Francis J. Lacoste
Remove IMakeOfficialBranchLinks non-utility.
101
        with person_logged_in(sourcepackage.distribution.owner):
102
            sourcepackage.setBranch(pocket, branch, registrant)
103
            sourcepackage.setBranch(pocket, new_branch, registrant)
8211.4.7 by Jonathan Lange
Make it possible to change the linked branch, in a somewhat crappy way.
104
        self.assertEqual(new_branch, sourcepackage.getBranch(pocket))
105
8211.4.3 by Jonathan Lange
Failing test for breaking an official source package branch link.
106
    def test_unsetBranch(self):
107
        # Setting the official branch for a pocket to 'None' breaks the link
108
        # between the branch and pocket.
109
        sourcepackage = self.factory.makeSourcePackage()
110
        pocket = PackagePublishingPocket.RELEASE
111
        registrant = self.factory.makePerson()
112
        branch = self.factory.makePackageBranch(sourcepackage=sourcepackage)
13139.3.3 by Francis J. Lacoste
Remove IMakeOfficialBranchLinks non-utility.
113
        with person_logged_in(sourcepackage.distribution.owner):
114
            sourcepackage.setBranch(pocket, branch, registrant)
115
            sourcepackage.setBranch(pocket, None, registrant)
8211.4.3 by Jonathan Lange
Failing test for breaking an official source package branch link.
116
        self.assertIs(None, sourcepackage.getBranch(pocket))
117
13991.1.6 by Curtis Hovey
Delete the official DSP if there is no publishing history when unsetting
118
    def test_unsetBranch_delete_unpublished_dsp(self):
119
        # Setting the official branch for a pocket to 'None' deletes the
120
        # official DSP record if there is no SPPH.
121
        sourcepackage = self.factory.makeSourcePackage()
122
        pocket = PackagePublishingPocket.RELEASE
123
        registrant = self.factory.makePerson()
124
        branch = self.factory.makePackageBranch(sourcepackage=sourcepackage)
125
        with person_logged_in(sourcepackage.distribution.owner):
126
            sourcepackage.setBranch(pocket, branch, registrant)
127
            sourcepackage.setBranch(pocket, None, registrant)
128
        new_dsp = DistributionSourcePackage._get(
129
            sourcepackage.distribution, sourcepackage.sourcepackagename)
130
        self.assertIs(None, new_dsp)
131
7940.5.6 by Jonathan Lange
Add a linked_branches property to ISourcePackage.
132
    def test_linked_branches(self):
133
        # ISourcePackage.linked_branches is a mapping of pockets to branches.
134
        sourcepackage = self.factory.makeSourcePackage()
135
        pocket = PackagePublishingPocket.RELEASE
136
        registrant = self.factory.makePerson()
137
        branch = self.factory.makePackageBranch(sourcepackage=sourcepackage)
13139.3.3 by Francis J. Lacoste
Remove IMakeOfficialBranchLinks non-utility.
138
        with person_logged_in(sourcepackage.distribution.owner):
139
            sourcepackage.setBranch(pocket, branch, registrant)
7940.5.6 by Jonathan Lange
Add a linked_branches property to ISourcePackage.
140
        self.assertEqual(
141
            [(pocket, branch)], list(sourcepackage.linked_branches))
142
8275.3.3 by Jonathan Lange
Get the suite source package from the source package object.
143
    def test_getSuiteSourcePackage(self):
144
        # ISourcePackage.getSuiteSourcePackage returns the suite source
145
        # package object for the given pocket.
146
        sourcepackage = self.factory.makeSourcePackage()
147
        pocket = PackagePublishingPocket.RELEASE
148
        ssp = sourcepackage.getSuiteSourcePackage(pocket)
149
        self.assertEqual(sourcepackage, ssp.sourcepackage)
150
        self.assertEqual(pocket, ssp.pocket)
151
7940.5.13 by Jonathan Lange
Add a getPocketPath method to sourcepackage.
152
    def test_path_to_release_pocket(self):
153
        # ISourcePackage.getPocketPath returns the path to a pocket. For the
154
        # RELEASE pocket, it's the same as the package path.
155
        sourcepackage = self.factory.makeSourcePackage()
156
        pocket = PackagePublishingPocket.RELEASE
157
        self.assertEqual(
158
            sourcepackage.path, sourcepackage.getPocketPath(pocket))
159
160
    def test_path_to_non_release_pocket(self):
161
        # ISourcePackage.getPocketPath returns the path to a pocket. For a
162
        # non-RELEASE pocket, it's the same as the package path, except with
163
        # series-pocket for the middle component.
164
        sourcepackage = self.factory.makeSourcePackage()
165
        pocket = PackagePublishingPocket.SECURITY
166
        path = '%s/%s-%s/%s' % (
167
            sourcepackage.distribution.name,
168
            sourcepackage.distroseries.name,
169
            pocket.name.lower(),
170
            sourcepackage.name)
171
        self.assertEqual(path, sourcepackage.getPocketPath(pocket))
172
8031.4.5 by Jonathan Lange
Extend ISourcePackage to make it easy to get the development focus version.
173
    def test_development_version(self):
174
        # ISourcePackage.development_version gets the development version of
175
        # the source package.
8031.4.20 by Jonathan Lange
Clarify test_sourcepackage and make sure that the factory does what you
176
        distribution = self.factory.makeDistribution()
13597.1.1 by Jeroen Vermeulen
Get rid of factory.make[Ubuntu]DistroRelease. And some lint.
177
        dev_series = self.factory.makeDistroSeries(
10054.26.1 by Adi Roiban
Refactor DistroSeriesStatus to SeriesStatus; Don't prompt for setting up translations for obsolete product series.
178
            distribution=distribution, status=SeriesStatus.DEVELOPMENT)
13597.1.1 by Jeroen Vermeulen
Get rid of factory.make[Ubuntu]DistroRelease. And some lint.
179
        other_series = self.factory.makeDistroSeries(
10054.26.1 by Adi Roiban
Refactor DistroSeriesStatus to SeriesStatus; Don't prompt for setting up translations for obsolete product series.
180
            distribution=distribution, status=SeriesStatus.OBSOLETE)
8031.4.20 by Jonathan Lange
Clarify test_sourcepackage and make sure that the factory does what you
181
        self.assertEqual(dev_series, distribution.currentseries)
182
        dev_sourcepackage = self.factory.makeSourcePackage(
183
            distroseries=dev_series)
184
        other_sourcepackage = self.factory.makeSourcePackage(
185
            distroseries=other_series,
186
            sourcepackagename=dev_sourcepackage.sourcepackagename)
187
        self.assertEqual(
188
            dev_sourcepackage, other_sourcepackage.development_version)
189
        self.assertEqual(
190
            dev_sourcepackage, dev_sourcepackage.development_version)
8031.4.5 by Jonathan Lange
Extend ISourcePackage to make it easy to get the development focus version.
191
8880.1.2 by Jonathan Lange
Add a distribution_sourcepackage property to ISourcePackage.
192
    def test_distribution_sourcepackage(self):
193
        # ISourcePackage.distribution_sourcepackage is the distribution source
194
        # package for the ISourcePackage.
195
        sourcepackage = self.factory.makeSourcePackage()
196
        distribution = sourcepackage.distribution
197
        distribution_sourcepackage = distribution.getSourcePackage(
198
            sourcepackage.sourcepackagename)
199
        self.assertEqual(
200
            distribution_sourcepackage,
201
            sourcepackage.distribution_sourcepackage)
202
9378.7.2 by Jonathan Lange
Add a default_archive property to the ISourcePackage class.
203
    def test_default_archive(self):
204
        # The default archive of a source package is the primary archive of
205
        # its distribution.
206
        sourcepackage = self.factory.makeSourcePackage()
207
        distribution = sourcepackage.distribution
208
        self.assertEqual(
9378.7.11 by Jonathan Lange
Change the interface so that it's no longer a property but rather a function.
209
            distribution.main_archive, sourcepackage.get_default_archive())
9378.7.2 by Jonathan Lange
Add a default_archive property to the ISourcePackage class.
210
9378.7.9 by Jonathan Lange
Return the partner archive if it exists and the package was last uploaded
211
    def test_default_archive_partner(self):
212
        # If the source package was most recently uploaded to a partner
213
        # component, then its default archive is the partner archive for the
214
        # distribution.
215
        sourcepackage = self.factory.makeSourcePackage()
216
        partner = getUtility(IComponentSet)['partner']
217
        self.factory.makeSourcePackagePublishingHistory(
218
            sourcepackagename=sourcepackage.sourcepackagename,
219
            distroseries=sourcepackage.distroseries,
220
            component=partner,
221
            status=PackagePublishingStatus.PUBLISHED)
222
        distribution = sourcepackage.distribution
223
        expected_archive = self.factory.makeArchive(
224
            distribution=distribution,
225
            purpose=ArchivePurpose.PARTNER)
9378.7.11 by Jonathan Lange
Change the interface so that it's no longer a property but rather a function.
226
        self.assertEqual(
227
            expected_archive, sourcepackage.get_default_archive())
228
229
    def test_default_archive_specified_component(self):
230
        # If the component is explicitly specified as partner, then we return
231
        # the partner archive.
232
        sourcepackage = self.factory.makeSourcePackage()
233
        partner = getUtility(IComponentSet)['partner']
234
        distribution = sourcepackage.distribution
235
        expected_archive = self.factory.makeArchive(
236
            distribution=distribution,
237
            purpose=ArchivePurpose.PARTNER)
238
        self.assertEqual(
239
            expected_archive,
240
            sourcepackage.get_default_archive(component=partner))
9378.7.9 by Jonathan Lange
Return the partner archive if it exists and the package was last uploaded
241
9378.7.10 by Jonathan Lange
Handle the case where no partner archive exists.
242
    def test_default_archive_partner_doesnt_exist(self):
243
        # If the default archive ought to be the partner archive (because the
244
        # last published upload was to a partner component) then
245
        # default_archive will raise an exception.
246
        sourcepackage = self.factory.makeSourcePackage()
247
        partner = getUtility(IComponentSet)['partner']
248
        self.factory.makeSourcePackagePublishingHistory(
249
            sourcepackagename=sourcepackage.sourcepackagename,
250
            distroseries=sourcepackage.distroseries,
251
            component=partner,
252
            status=PackagePublishingStatus.PUBLISHED)
253
        self.assertRaises(
9378.7.11 by Jonathan Lange
Change the interface so that it's no longer a property but rather a function.
254
            NoPartnerArchive, sourcepackage.get_default_archive)
9378.7.10 by Jonathan Lange
Handle the case where no partner archive exists.
255
11266.4.1 by Jon Sackett
Fix plus tests for bug 612408
256
    def test_source_package_summary_no_releases_returns_None(self):
257
        sourcepackage = self.factory.makeSourcePackage()
258
        self.assertEqual(sourcepackage.summary, None)
259
260
    def test_source_package_summary_with_releases_returns_None(self):
261
        sourcepackage = self.factory.makeSourcePackage()
11266.4.4 by Curtis Hovey
Finally fixed test_source_package_summary_with_binaries_returns_list. Expected summary data coming through.
262
        self.factory.makeSourcePackageRelease(
263
            sourcepackagename=sourcepackage.sourcepackagename)
11266.4.1 by Jon Sackett
Fix plus tests for bug 612408
264
        self.assertEqual(sourcepackage.summary, None)
265
266
    def test_source_package_summary_with_binaries_returns_list(self):
11266.4.4 by Curtis Hovey
Finally fixed test_source_package_summary_with_binaries_returns_list. Expected summary data coming through.
267
        sp = getUtility(
268
            ILaunchpadCelebrities).ubuntu['warty'].getSourcePackage(
269
            'mozilla-firefox')
11266.4.5 by Curtis Hovey
Updated expected_summary declaration per style guidelines.
270
271
        expected_summary = (
272
            u'mozilla-firefox: Mozilla Firefox Web Browser\n'
273
            u'mozilla-firefox-data: No summary available for '
274
            u'mozilla-firefox-data in ubuntu warty.')
275
        self.assertEqual(''.join(expected_summary), sp.summary)
11266.4.4 by Curtis Hovey
Finally fixed test_source_package_summary_with_binaries_returns_list. Expected summary data coming through.
276
12510.5.15 by Aaron Bentley
Add paranoid deletePackaging test.
277
    def test_deletePackaging(self):
278
        """Ensure deletePackaging completely removes packaging."""
279
        packaging = self.factory.makePackagingLink()
280
        packaging_id = packaging.id
281
        store = Store.of(packaging)
12769.1.2 by Abel Deuring
SourcePackage.setpackaging() now deletes an existing Packaging record and creates a new one, if one exists, in order to trigger the creation of jobs for translation sharing.
282
        with person_logged_in(packaging.owner):
283
            packaging.sourcepackage.deletePackaging()
13139.3.5 by Francis J. Lacoste
Fix lint.
284
        result = store.find(Packaging, Packaging.id == packaging_id)
12510.5.15 by Aaron Bentley
Add paranoid deletePackaging test.
285
        self.assertIs(None, result.one())
286
12769.1.2 by Abel Deuring
SourcePackage.setpackaging() now deletes an existing Packaging record and creates a new one, if one exists, in order to trigger the creation of jobs for translation sharing.
287
    def test_setPackaging__new(self):
288
        """setPackaging() creates a Packaging link."""
289
        sourcepackage = self.factory.makeSourcePackage()
290
        productseries = self.factory.makeProductSeries()
291
        sourcepackage.setPackaging(
292
            productseries, owner=self.factory.makePerson())
293
        packaging = sourcepackage.direct_packaging
294
        self.assertEqual(packaging.productseries, productseries)
295
296
    def test_setPackaging__change_existing_entry(self):
297
        """setPackaging() changes existing Packaging links."""
298
        sourcepackage = self.factory.makeSourcePackage()
299
        productseries = self.factory.makeProductSeries()
300
        other_series = self.factory.makeProductSeries()
301
        owner = self.factory.makePerson()
302
        with EventRecorder() as recorder:
303
            with person_logged_in(owner):
304
                sourcepackage.setPackaging(productseries, owner=owner)
305
                sourcepackage.setPackaging(other_series, owner=owner)
306
                packaging = sourcepackage.direct_packaging
307
                self.assertEqual(packaging.productseries, other_series)
308
        # The first call of setPackaging() created an ObjectCreatedEvent;
309
        # the second call created an ObjectDeletedEvent for the deletion
310
        # of the old packaging link, and another ObjectCreatedEvent
311
        # for the new Packaging.
312
        event1, event2, event3 = recorder.events
313
        self.assertIsInstance(event1, ObjectCreatedEvent)
314
        self.assertIsInstance(event2, ObjectDeletedEvent)
315
        self.assertIsInstance(event3, ObjectCreatedEvent)
316
317
    def test_setPackaging__change_existing_entry_different_users(self):
12828.8.5 by Abel Deuring
removed the property ProductSeries.user_can_set_branch; added SourcePackage.setPackagingReturnSharingDetailPermissions(); interface definition of Person.canWrite(), Person.canAccess moved from IPerson to IPersonSpecialRestricted.
318
        """An ordinary user cannot change a Packaging defined by
12769.1.2 by Abel Deuring
SourcePackage.setpackaging() now deletes an existing Packaging record and creates a new one, if one exists, in order to trigger the creation of jobs for translation sharing.
319
        somebody else.
320
        """
321
        sourcepackage = self.factory.makeSourcePackage()
322
        productseries = self.factory.makeProductSeries()
323
        other_series = self.factory.makeProductSeries()
324
        owner = self.factory.makePerson()
325
        other_user = self.factory.makePerson()
326
        sourcepackage.setPackaging(productseries, owner=owner)
327
        with person_logged_in(other_user):
328
            self.assertRaises(
329
                Unauthorized, sourcepackage.setPackaging,
330
                other_series, owner=other_user)
331
12828.8.5 by Abel Deuring
removed the property ProductSeries.user_can_set_branch; added SourcePackage.setPackagingReturnSharingDetailPermissions(); interface definition of Person.canWrite(), Person.canAccess moved from IPerson to IPersonSpecialRestricted.
332
    def test_setPackagingReturnSharingDetailPermissions__ordinary_user(self):
333
        """An ordinary user can create a packaging link but he cannot
334
        set the series' branch or translation syncronisation settings,
335
        or the translation usage settings of the product.
336
        """
337
        sourcepackage = self.factory.makeSourcePackage()
338
        productseries = self.factory.makeProductSeries()
339
        packaging_owner = self.factory.makePerson()
340
        with person_logged_in(packaging_owner):
341
            permissions = (
342
                sourcepackage.setPackagingReturnSharingDetailPermissions(
343
                    productseries, packaging_owner))
344
            self.assertEqual(productseries, sourcepackage.productseries)
345
            self.assertFalse(
346
                packaging_owner.canWrite(productseries, 'branch'))
347
            self.assertFalse(
348
                packaging_owner.canWrite(
349
                    productseries, 'translations_autoimport_mode'))
350
            self.assertFalse(
351
                packaging_owner.canWrite(
352
                    productseries.product, 'translations_usage'))
353
            expected = {
12891.1.2 by Aaron Bentley
Provide permission details in page cache, refresh when changing productseries.
354
                'user_can_change_product_series': True,
355
                'user_can_change_branch': False,
356
                'user_can_change_translation_usage': False,
357
                'user_can_change_translations_autoimport_mode': False,
358
                }
359
            self.assertEqual(expected, permissions)
360
361
    def test_getSharingDetailPermissions__ordinary_user(self):
362
        """An ordinary user cannot set the series' branch or translation
363
        synchronisation settings, or the translation usage settings of the
364
        product.
365
        """
366
        packaging = self.factory.makePackagingLink()
367
        sourcepackage = packaging.sourcepackage
368
        productseries = packaging.productseries
369
        with person_logged_in(packaging.owner):
370
            permissions = sourcepackage.getSharingDetailPermissions()
371
            self.assertEqual(productseries, sourcepackage.productseries)
372
            self.assertFalse(
373
                packaging.owner.canWrite(productseries, 'branch'))
374
            self.assertFalse(
375
                packaging.owner.canWrite(
376
                    productseries, 'translations_autoimport_mode'))
377
            self.assertFalse(
378
                packaging.owner.canWrite(
379
                    productseries.product, 'translations_usage'))
380
            expected = {
381
                'user_can_change_product_series': True,
382
                'user_can_change_branch': False,
383
                'user_can_change_translation_usage': False,
384
                'user_can_change_translations_autoimport_mode': False,
385
                }
386
            self.assertEqual(expected, permissions)
387
388
    def makeDistinctOwnerProductSeries(self):
389
        # Ensure productseries owner is distinct from product owner.
390
        return self.factory.makeProductSeries(
391
            owner=self.factory.makePerson())
392
393
    def test_getSharingDetailPermissions__series_owner(self):
12828.8.5 by Abel Deuring
removed the property ProductSeries.user_can_set_branch; added SourcePackage.setPackagingReturnSharingDetailPermissions(); interface definition of Person.canWrite(), Person.canAccess moved from IPerson to IPersonSpecialRestricted.
394
        """A product series owner can create a packaging link, and he can
395
        set the series' branch or translation syncronisation settings,
396
        but he cannot set the translation usage settings of the product.
397
        """
12891.1.2 by Aaron Bentley
Provide permission details in page cache, refresh when changing productseries.
398
        productseries = self.makeDistinctOwnerProductSeries()
399
        series_owner = productseries.owner
400
        # Ensure productseries owner is distinct from product owner.
12828.8.5 by Abel Deuring
removed the property ProductSeries.user_can_set_branch; added SourcePackage.setPackagingReturnSharingDetailPermissions(); interface definition of Person.canWrite(), Person.canAccess moved from IPerson to IPersonSpecialRestricted.
401
        productseries = self.factory.makeProductSeries(
12891.1.2 by Aaron Bentley
Provide permission details in page cache, refresh when changing productseries.
402
            owner=series_owner)
12828.8.5 by Abel Deuring
removed the property ProductSeries.user_can_set_branch; added SourcePackage.setPackagingReturnSharingDetailPermissions(); interface definition of Person.canWrite(), Person.canAccess moved from IPerson to IPersonSpecialRestricted.
403
        with person_logged_in(series_owner):
12891.1.2 by Aaron Bentley
Provide permission details in page cache, refresh when changing productseries.
404
            packaging = self.factory.makePackagingLink(
405
                productseries=productseries, owner=series_owner)
406
            sourcepackage = packaging.sourcepackage
407
            permissions = sourcepackage.getSharingDetailPermissions()
12828.8.5 by Abel Deuring
removed the property ProductSeries.user_can_set_branch; added SourcePackage.setPackagingReturnSharingDetailPermissions(); interface definition of Person.canWrite(), Person.canAccess moved from IPerson to IPersonSpecialRestricted.
408
            self.assertEqual(productseries, sourcepackage.productseries)
409
            self.assertTrue(series_owner.canWrite(productseries, 'branch'))
410
            self.assertTrue(
411
                series_owner.canWrite(
412
                    productseries, 'translations_autoimport_mode'))
413
            self.assertFalse(
414
                series_owner.canWrite(
415
                    productseries.product, 'translations_usage'))
416
            expected = {
12891.1.2 by Aaron Bentley
Provide permission details in page cache, refresh when changing productseries.
417
                'user_can_change_product_series': True,
12828.8.5 by Abel Deuring
removed the property ProductSeries.user_can_set_branch; added SourcePackage.setPackagingReturnSharingDetailPermissions(); interface definition of Person.canWrite(), Person.canAccess moved from IPerson to IPersonSpecialRestricted.
418
                'user_can_change_branch': True,
419
                'user_can_change_translation_usage': False,
420
                'user_can_change_translations_autoimport_mode': True,
421
                }
422
            self.assertEqual(expected, permissions)
423
12891.1.2 by Aaron Bentley
Provide permission details in page cache, refresh when changing productseries.
424
    def test_getSharingDetailPermissions__product_owner(self):
425
        """A product owner can create a packaging link, and he can set the
426
        series' branch and the translation syncronisation settings, and the
427
        translation usage settings of the product.
12828.8.5 by Abel Deuring
removed the property ProductSeries.user_can_set_branch; added SourcePackage.setPackagingReturnSharingDetailPermissions(); interface definition of Person.canWrite(), Person.canAccess moved from IPerson to IPersonSpecialRestricted.
428
        """
12891.1.2 by Aaron Bentley
Provide permission details in page cache, refresh when changing productseries.
429
        productseries = self.makeDistinctOwnerProductSeries()
430
        product = productseries.product
12828.8.5 by Abel Deuring
removed the property ProductSeries.user_can_set_branch; added SourcePackage.setPackagingReturnSharingDetailPermissions(); interface definition of Person.canWrite(), Person.canAccess moved from IPerson to IPersonSpecialRestricted.
431
        with person_logged_in(product.owner):
12891.1.2 by Aaron Bentley
Provide permission details in page cache, refresh when changing productseries.
432
            packaging = self.factory.makePackagingLink(
433
                productseries=productseries, owner=product.owner)
434
            sourcepackage = packaging.sourcepackage
435
            permissions = sourcepackage.getSharingDetailPermissions()
12828.8.5 by Abel Deuring
removed the property ProductSeries.user_can_set_branch; added SourcePackage.setPackagingReturnSharingDetailPermissions(); interface definition of Person.canWrite(), Person.canAccess moved from IPerson to IPersonSpecialRestricted.
436
            self.assertEqual(productseries, sourcepackage.productseries)
437
            self.assertTrue(product.owner.canWrite(productseries, 'branch'))
438
            self.assertTrue(
439
                product.owner.canWrite(
440
                    productseries, 'translations_autoimport_mode'))
441
            self.assertTrue(
442
                product.owner.canWrite(
443
                    productseries.product, 'translations_usage'))
444
            expected = {
12891.1.2 by Aaron Bentley
Provide permission details in page cache, refresh when changing productseries.
445
                'user_can_change_product_series': True,
12828.8.5 by Abel Deuring
removed the property ProductSeries.user_can_set_branch; added SourcePackage.setPackagingReturnSharingDetailPermissions(); interface definition of Person.canWrite(), Person.canAccess moved from IPerson to IPersonSpecialRestricted.
446
                'user_can_change_branch': True,
447
                'user_can_change_translation_usage': True,
448
                'user_can_change_translations_autoimport_mode': True,
449
                }
450
            self.assertEqual(expected, permissions)
451
12891.1.2 by Aaron Bentley
Provide permission details in page cache, refresh when changing productseries.
452
    def test_getSharingDetailPermissions_change_product(self):
453
        """Test user_can_change_product_series.
454
455
        Until a Packaging is created, anyone can change product series.
456
        Afterward, random people cannot change product series.
457
        """
458
        sourcepackage = self.factory.makeSourcePackage()
459
        person1 = self.factory.makePerson()
460
        person2 = self.factory.makePerson()
461
462
        def can_change_product_series():
463
            return sourcepackage.getSharingDetailPermissions()[
464
                    'user_can_change_product_series']
465
        with person_logged_in(person1):
466
            self.assertTrue(can_change_product_series())
467
        with person_logged_in(person2):
468
            self.assertTrue(can_change_product_series())
469
        self.factory.makePackagingLink(
470
            sourcepackage=sourcepackage, owner=person1)
471
        with person_logged_in(person1):
472
            self.assertTrue(can_change_product_series())
473
        with person_logged_in(person2):
474
            self.assertFalse(can_change_product_series())
475
476
    def test_getSharingDetailPermissions_no_product_series(self):
477
        sourcepackage = self.factory.makeSourcePackage()
478
        expected = {
479
            'user_can_change_product_series': True,
480
            'user_can_change_branch': False,
481
            'user_can_change_translation_usage': False,
482
            'user_can_change_translations_autoimport_mode': False}
13139.3.3 by Francis J. Lacoste
Remove IMakeOfficialBranchLinks non-utility.
483
        with person_logged_in(self.factory.makePerson()):
484
            self.assertEqual(
485
                expected, sourcepackage.getSharingDetailPermissions())
12891.1.2 by Aaron Bentley
Provide permission details in page cache, refresh when changing productseries.
486
487
    def test_getSharingDetailPermissions_no_user(self):
488
        sourcepackage = self.factory.makeSourcePackage()
489
        expected = {
490
            'user_can_change_product_series': False,
491
            'user_can_change_branch': False,
492
            'user_can_change_translation_usage': False,
493
            'user_can_change_translations_autoimport_mode': False}
494
        self.assertEqual(
495
            expected, sourcepackage.getSharingDetailPermissions())
496
14341.1.4 by Steve Kowalik
Remove errant DSP.driver property and write tests for {,D}SP.drivers.
497
    def test_drivers_are_distroseries(self):
498
        # SP.drivers returns the drivers for the distroseries.
499
        distroseries = self.factory.makeDistroSeries()
500
        sourcepackage = self.factory.makeSourcePackage(
501
            distroseries=distroseries)
14341.1.5 by Steve Kowalik
Assert the distro or series drivers are set.
502
        self.assertNotEqual([], distroseries.drivers)
14341.1.4 by Steve Kowalik
Remove errant DSP.driver property and write tests for {,D}SP.drivers.
503
        self.assertEqual(sourcepackage.drivers, distroseries.drivers)
504
14538.3.1 by Curtis Hovey
Added IHasOwner to ISourcePackage
505
    def test_personHasDriverRights_true(self):
14449.3.1 by Curtis Hovey
Add HasDriversMixin to SourcePackage to so that it can implement all the
506
        # A distroseries driver has driver permissions on source packages.
507
        distroseries = self.factory.makeDistroSeries()
508
        sourcepackage = self.factory.makeSourcePackage(
509
            distroseries=distroseries)
510
        driver = distroseries.drivers[0]
511
        self.assertTrue(sourcepackage.personHasDriverRights(driver))
512
14538.3.2 by Curtis Hovey
Fixed comment.
513
    def test_personHasDriverRights_false(self):
514
        # A non-owner/driver/admin does not have driver rights.
14538.3.1 by Curtis Hovey
Added IHasOwner to ISourcePackage
515
        distroseries = self.factory.makeDistroSeries()
516
        sourcepackage = self.factory.makeSourcePackage(
517
            distroseries=distroseries)
518
        non_priv_user = self.factory.makePerson()
519
        self.assertFalse(sourcepackage.personHasDriverRights(non_priv_user))
520
521
    def test_owner_is_distroseries_owner(self):
522
        # The source package owner differs to the ditroseries owner.
523
        distroseries = self.factory.makeDistroSeries()
524
        sourcepackage = self.factory.makeSourcePackage(
525
            distroseries=distroseries)
526
        self.assertIsNot(None, sourcepackage.owner)
527
        self.assertEqual(distroseries.owner, sourcepackage.owner)
528
        self.assertTrue(
529
            sourcepackage.personHasDriverRights(distroseries.owner))
530
7362.13.12 by Jonathan Lange
Add a path property to ISourcePackage.
531
12510.5.1 by Aaron Bentley
Test that SourcepackageRecipe.setPackaging works.
532
class TestSourcePackageWebService(WebServiceTestCase):
533
534
    def test_setPackaging(self):
12510.5.13 by Aaron Bentley
Updated copyright.
535
        """setPackaging is accessible and works."""
12510.5.1 by Aaron Bentley
Test that SourcepackageRecipe.setPackaging works.
536
        sourcepackage = self.factory.makeSourcePackage()
537
        self.assertIs(None, sourcepackage.direct_packaging)
538
        productseries = self.factory.makeProductSeries()
539
        transaction.commit()
540
        ws_sourcepackage = self.wsObject(sourcepackage)
541
        ws_productseries = self.wsObject(productseries)
542
        ws_sourcepackage.setPackaging(productseries=ws_productseries)
543
        transaction.commit()
544
        self.assertEqual(
545
            productseries, sourcepackage.direct_packaging.productseries)
546
12510.5.2 by Aaron Bentley
Allow packaging to be deleted over web service.
547
    def test_deletePackaging(self):
548
        """Deleting a packaging should work."""
549
        packaging = self.factory.makePackagingLink()
550
        sourcepackage = packaging.sourcepackage
551
        transaction.commit()
12769.1.2 by Abel Deuring
SourcePackage.setpackaging() now deletes an existing Packaging record and creates a new one, if one exists, in order to trigger the creation of jobs for translation sharing.
552
        self.wsObject(sourcepackage, user=packaging.owner).deletePackaging()
12510.5.2 by Aaron Bentley
Allow packaging to be deleted over web service.
553
        transaction.commit()
554
        self.assertIs(None, sourcepackage.direct_packaging)
555
556
    def test_deletePackaging_with_no_packaging(self):
557
        """Deleting when there's no packaging should be a no-op."""
558
        sourcepackage = self.factory.makeSourcePackage()
559
        transaction.commit()
560
        self.wsObject(sourcepackage).deletePackaging()
561
        transaction.commit()
562
        self.assertIs(None, sourcepackage.direct_packaging)
563
12510.5.1 by Aaron Bentley
Test that SourcepackageRecipe.setPackaging works.
564
7921.1.18 by Jonathan Lange
Add a test to guarantee that you cannot set a branch.
565
class TestSourcePackageSecurity(TestCaseWithFactory):
13139.3.1 by Francis J. Lacoste
Define launchpad.Edit permission on ISourcePackge.
566
    """Tests for source package security."""
7921.1.18 by Jonathan Lange
Add a test to guarantee that you cannot set a branch.
567
568
    layer = DatabaseFunctionalLayer
569
13139.3.1 by Francis J. Lacoste
Define launchpad.Edit permission on ISourcePackge.
570
    def test_admins_have_launchpad_Edit(self):
571
        admin = self.factory.makeAdministrator()
572
        sourcepackage = self.factory.makeSourcePackage()
573
        with person_logged_in(admin):
13228.2.3 by Francis J. Lacoste
Think positive!
574
            self.assertTrue(
13139.3.1 by Francis J. Lacoste
Define launchpad.Edit permission on ISourcePackge.
575
                checkPermission('launchpad.Edit', sourcepackage),
576
                "Administrators should have launchpad.Edit on source "
577
                "packages.")
578
579
    def test_distro_owner_have_launchpad_Edit(self):
580
        sourcepackage = self.factory.makeSourcePackage()
581
        with person_logged_in(sourcepackage.distribution.owner):
13228.2.3 by Francis J. Lacoste
Think positive!
582
            self.assertTrue(
13139.3.1 by Francis J. Lacoste
Define launchpad.Edit permission on ISourcePackge.
583
                checkPermission('launchpad.Edit', sourcepackage),
584
                "Distribution owner should have launchpad.Edit on source "
585
                "packages.")
586
13228.2.1 by Francis J. Lacoste
Use verifyUpload() instead of checkUpload() since we don't really care about the pocket for changing sourcepackage meta-data.
587
    def test_uploader_has_launchpad_edit(self):
13139.3.1 by Francis J. Lacoste
Define launchpad.Edit permission on ISourcePackge.
588
        sourcepackage = self.factory.makeSourcePackage()
589
        uploader = self.factory.makePerson()
13139.3.5 by Francis J. Lacoste
Fix lint.
590
        archive = sourcepackage.get_default_archive()
13139.3.1 by Francis J. Lacoste
Define launchpad.Edit permission on ISourcePackge.
591
        with person_logged_in(sourcepackage.distribution.main_archive.owner):
592
            archive.newPackageUploader(uploader, sourcepackage.name)
593
        with person_logged_in(uploader):
13228.2.3 by Francis J. Lacoste
Think positive!
594
            self.assertTrue(
13139.3.1 by Francis J. Lacoste
Define launchpad.Edit permission on ISourcePackge.
595
                checkPermission('launchpad.Edit', sourcepackage),
596
                "Uploader to the package should have launchpad.Edit on "
597
                "source packages.")
598
13228.2.1 by Francis J. Lacoste
Use verifyUpload() instead of checkUpload() since we don't really care about the pocket for changing sourcepackage meta-data.
599
    def test_uploader_has_launchpad_edit_on_obsolete_series(self):
13597.1.1 by Jeroen Vermeulen
Get rid of factory.make[Ubuntu]DistroRelease. And some lint.
600
        obsolete_series = self.factory.makeDistroSeries(
13228.2.1 by Francis J. Lacoste
Use verifyUpload() instead of checkUpload() since we don't really care about the pocket for changing sourcepackage meta-data.
601
            status=SeriesStatus.OBSOLETE)
602
        sourcepackage = self.factory.makeSourcePackage(
603
            distroseries=obsolete_series)
604
        uploader = self.factory.makePerson()
605
        archive = sourcepackage.get_default_archive()
606
        with person_logged_in(sourcepackage.distribution.main_archive.owner):
607
            archive.newPackageUploader(uploader, sourcepackage.name)
608
        with person_logged_in(uploader):
13228.2.3 by Francis J. Lacoste
Think positive!
609
            self.assertTrue(
13228.2.1 by Francis J. Lacoste
Use verifyUpload() instead of checkUpload() since we don't really care about the pocket for changing sourcepackage meta-data.
610
                checkPermission('launchpad.Edit', sourcepackage),
611
                "Uploader to the package should have launchpad.Edit on "
612
                "source packages in an OBSOLETE series.")
613
614
    def test_uploader_have_launchpad_edit_on_current_series(self):
13597.1.1 by Jeroen Vermeulen
Get rid of factory.make[Ubuntu]DistroRelease. And some lint.
615
        current_series = self.factory.makeDistroSeries(
13228.2.1 by Francis J. Lacoste
Use verifyUpload() instead of checkUpload() since we don't really care about the pocket for changing sourcepackage meta-data.
616
            status=SeriesStatus.CURRENT)
617
        sourcepackage = self.factory.makeSourcePackage(
618
            distroseries=current_series)
619
        uploader = self.factory.makePerson()
620
        archive = sourcepackage.get_default_archive()
621
        with person_logged_in(sourcepackage.distribution.main_archive.owner):
622
            archive.newPackageUploader(uploader, sourcepackage.name)
623
        with person_logged_in(uploader):
13228.2.3 by Francis J. Lacoste
Think positive!
624
            self.assertTrue(
13228.2.1 by Francis J. Lacoste
Use verifyUpload() instead of checkUpload() since we don't really care about the pocket for changing sourcepackage meta-data.
625
                checkPermission('launchpad.Edit', sourcepackage),
626
                "Uploader to the package should have launchpad.Edit on "
627
                "source packages in a CURRENT series.")
628
629
    def test_uploader_have_launchpad_edit_on_supported_series(self):
13597.1.1 by Jeroen Vermeulen
Get rid of factory.make[Ubuntu]DistroRelease. And some lint.
630
        supported_series = self.factory.makeDistroSeries(
13228.2.1 by Francis J. Lacoste
Use verifyUpload() instead of checkUpload() since we don't really care about the pocket for changing sourcepackage meta-data.
631
            status=SeriesStatus.SUPPORTED)
632
        sourcepackage = self.factory.makeSourcePackage(
633
            distroseries=supported_series)
634
        uploader = self.factory.makePerson()
635
        archive = sourcepackage.get_default_archive()
636
        with person_logged_in(sourcepackage.distribution.main_archive.owner):
637
            archive.newPackageUploader(uploader, sourcepackage.name)
638
        with person_logged_in(uploader):
13228.2.3 by Francis J. Lacoste
Think positive!
639
            self.assertTrue(
13228.2.1 by Francis J. Lacoste
Use verifyUpload() instead of checkUpload() since we don't really care about the pocket for changing sourcepackage meta-data.
640
                checkPermission('launchpad.Edit', sourcepackage),
641
                "Uploader to the package should have launchpad.Edit on "
642
                "source packages in a SUPPORTED series.")
643
13139.3.9 by Francis J. Lacoste
Spelling clarification suggested by bac.
644
    def test_john_doe_cannot_edit(self):
13139.3.1 by Francis J. Lacoste
Define launchpad.Edit permission on ISourcePackge.
645
        sourcepackage = self.factory.makeSourcePackage()
646
        john_doe = self.factory.makePerson()
647
        with person_logged_in(john_doe):
648
            self.failIf(
649
                checkPermission('launchpad.Edit', sourcepackage),
650
                "Random user shouldn't have launchpad.Edit on source "
651
                "packages.")
652
7921.1.18 by Jonathan Lange
Add a test to guarantee that you cannot set a branch.
653
    def test_cannot_setBranch(self):
654
        sourcepackage = self.factory.makeSourcePackage()
13139.3.2 by Francis J. Lacoste
Protect setBranch through launchpad.Edit
655
        self.failIf(
656
            canAccess(sourcepackage, 'setBranch'),
657
            "setBranch should only be available to admins and uploaders")
7921.1.18 by Jonathan Lange
Add a test to guarantee that you cannot set a branch.
658
659
10374.1.2 by Edwin Grubbs
Added tests.
660
class TestSourcePackageViews(TestCaseWithFactory):
661
    """Tests for source package view classes."""
662
663
    layer = DatabaseFunctionalLayer
664
665
    def setUp(self):
666
        TestCaseWithFactory.setUp(self)
667
        self.owner = self.factory.makePerson()
668
        self.product = self.factory.makeProduct(
669
            name='bonkers', displayname='Bonkers', owner=self.owner)
670
671
        self.obsolete_productseries = self.factory.makeProductSeries(
672
            name='obsolete', product=self.product)
11262.2.3 by Curtis Hovey
Remove shouting from registry tests
673
        with person_logged_in(self.product.owner):
674
            self.obsolete_productseries.status = SeriesStatus.OBSOLETE
10374.1.2 by Edwin Grubbs
Added tests.
675
676
        self.dev_productseries = self.factory.makeProductSeries(
677
            name='current', product=self.product)
11262.2.3 by Curtis Hovey
Remove shouting from registry tests
678
        with person_logged_in(self.product.owner):
679
            self.dev_productseries.status = SeriesStatus.DEVELOPMENT
10374.1.2 by Edwin Grubbs
Added tests.
680
681
        self.distribution = self.factory.makeDistribution(
682
            name='youbuntu', displayname='Youbuntu', owner=self.owner)
13597.1.1 by Jeroen Vermeulen
Get rid of factory.make[Ubuntu]DistroRelease. And some lint.
683
        self.distroseries = self.factory.makeDistroSeries(
684
            name='busy', distribution=self.distribution)
10374.1.2 by Edwin Grubbs
Added tests.
685
        self.sourcepackagename = self.factory.makeSourcePackageName(
686
            name='bonkers')
687
        self.package = self.factory.makeSourcePackage(
688
            sourcepackagename=self.sourcepackagename,
689
            distroseries=self.distroseries)
690
691
    def test_editpackaging_obsolete_series_in_vocabulary(self):
692
        # The sourcepackage's current product series is included in
693
        # the vocabulary even if it is obsolete.
694
        self.package.setPackaging(self.obsolete_productseries, self.owner)
695
        form = {
696
            'field.product': 'bonkers',
697
            'field.actions.continue': 'Continue',
698
            'field.__visited_steps__': 'sourcepackage_change_upstream_step1',
699
            }
700
        view = create_initialized_view(
701
            self.package, name='+edit-packaging', form=form,
702
            principal=self.owner)
703
        self.assertEqual([], view.view.errors)
704
        self.assertEqual(
705
            self.obsolete_productseries,
706
            view.view.form_fields['productseries'].field.default,
707
            "The form's default productseries must be the current one.")
708
        options = [term.token
709
                   for term in view.view.widgets['productseries'].vocabulary]
710
        self.assertEqual(
711
            ['trunk', 'current', 'obsolete'], options,
712
            "The obsolete series must be in the vocabulary.")
713
714
    def test_editpackaging_obsolete_series_not_in_vocabulary(self):
715
        # Obsolete productseries are normally not in the vocabulary.
716
        form = {
717
            'field.product': 'bonkers',
718
            'field.actions.continue': 'Continue',
719
            'field.__visited_steps__': 'sourcepackage_change_upstream_step1',
720
            }
721
        view = create_initialized_view(
722
            self.package, name='+edit-packaging', form=form,
723
            principal=self.owner)
724
        self.assertEqual([], view.view.errors)
725
        self.assertEqual(
726
            None,
727
            view.view.form_fields['productseries'].field.default,
728
            "The form's default productseries must be None.")
729
        options = [term.token
730
                   for term in view.view.widgets['productseries'].vocabulary]
731
        self.assertEqual(
732
            ['trunk', 'current'], options,
733
            "The obsolete series must NOT be in the vocabulary.")