Merge lp:~michael.nelson/launchpad/create-source-recipe-build2 into lp:launchpad

Proposed by Michael Nelson
Status: Merged
Approved by: Muharem Hrnjadovic
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~michael.nelson/launchpad/create-source-recipe-build2
Merge into: lp:launchpad
Diff against target: 501 lines (+180/-37)
12 files modified
lib/lp/archiveuploader/permission.py (+19/-10)
lib/lp/archiveuploader/tests/test_permission.py (+8/-0)
lib/lp/buildmaster/interfaces/buildbase.py (+1/-1)
lib/lp/buildmaster/model/buildbase.py (+9/-5)
lib/lp/buildmaster/tests/test_buildbase.py (+0/-8)
lib/lp/soyuz/interfaces/sourcepackagerecipe.py (+12/-0)
lib/lp/soyuz/model/build.py (+4/-9)
lib/lp/soyuz/model/sourcepackagerecipe.py (+29/-0)
lib/lp/soyuz/model/sourcepackagerecipebuild.py (+2/-1)
lib/lp/soyuz/tests/test_build.py (+24/-1)
lib/lp/soyuz/tests/test_sourcepackagerecipe.py (+67/-0)
lib/lp/soyuz/tests/test_sourcepackagerecipebuild.py (+5/-2)
To merge this branch: bzr merge lp:~michael.nelson/launchpad/create-source-recipe-build2
Reviewer Review Type Date Requested Status
Muharem Hrnjadovic (community) Approve
Review via email: mp+18535@code.launchpad.net

Commit message

Adds ISourcePackageRecipe.requestBuild(), and ensures that classes that say they implement IBuildBase actually do (bug 510919)

To post a comment you must log in.
Revision history for this message
Michael Nelson (michael.nelson) wrote :

= Summary =
This branch builds on the work that james_w had approved at:

https://code.edge.launchpad.net/~james-w/launchpad/create-source-recipe-build/+merge/17435

I merged devel, so am not able to specify it as the pre-requisite branch (without showing tons of diff), so you can check my specific changes at:

http://pastebin.ubuntu.com/368292/

or with bzr diff -r 10188..10194

It does the following:
 1. Deals with the review feedback from the previous branch,
 2. Ensures that IBuildBase is provided by both SourcePackageRecipeBuild and (Binary)Build (bug 510919)
 3. Cleans some lint.

== Proposed fix ==

== Pre-implementation notes ==

Chatted with james_w briefly about what needed to be done.

== Implementation details ==

== Tests ==

bin/test -vv -t test_requestBuild -t TestBuildInterface

== Demo and Q/A ==

= Launchpad lint =

I'm not sure why lint is complaining about 'self' being missing from interface declarations?

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/soyuz/tests/test_sourcepackagerecipebuild.py
  lib/lp/archiveuploader/uploadprocessor.py
  lib/lp/archiveuploader/permission.py
  lib/lp/buildmaster/model/buildbase.py
  lib/lp/soyuz/tests/test_build.py
  lib/lp/soyuz/tests/test_sourcepackagerecipe.py
  lib/lp/buildmaster/tests/test_buildbase.py
  lib/lp/soyuz/model/sourcepackagerecipebuild.py
  lib/lp/archiveuploader/tests/test_permission.py
  lib/lp/soyuz/model/sourcepackagerecipe.py
  lib/lp/buildmaster/interfaces/buildbase.py
  lib/lp/soyuz/interfaces/sourcepackagerecipe.py
  lib/lp/soyuz/model/build.py

== Pylint notices ==

lib/lp/soyuz/tests/test_sourcepackagerecipe.py
    11: [F0401] Unable to import 'bzrlib.plugins.builder.recipe' (No module named builder)

lib/lp/buildmaster/interfaces/buildbase.py
    14: [F0401] Unable to import 'lazr.enum' (No module named enum)
    15: [F0401] Unable to import 'lazr.restful.declarations' (No module named restful)
    16: [F0401] Unable to import 'lazr.restful.fields' (No module named restful)

lib/lp/soyuz/interfaces/sourcepackagerecipe.py
    14: [F0401] Unable to import 'lazr.restful.fields' (No module named restful)
    77: [E0211, ISourcePackageRecipe.getReferencedBranches] Method has no argument
    80: [E0213, ISourcePackageRecipe.requestBuild] Method should have "self" as first argument
    95: [E0213, ISourcePackageRecipeSource.new] Method should have "self" as first argument

Revision history for this message
Muharem Hrnjadovic (al-maisan) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/archiveuploader/permission.py'
2--- lib/lp/archiveuploader/permission.py 2010-01-15 01:37:51 +0000
3+++ lib/lp/archiveuploader/permission.py 2010-02-05 10:56:18 +0000
4@@ -21,24 +21,21 @@
5 from lp.soyuz.interfaces.archive import ArchivePurpose
6
7
8-class CannotUploadToArchive:
9+class CannotUploadToArchive(Exception):
10 """A reason for not being able to upload to an archive."""
11
12 _fmt = '%(person)s has no upload rights to %(archive)s.'
13
14 def __init__(self, **args):
15 """Construct a `CannotUploadToArchive`."""
16- self._message = self._fmt % args
17-
18- def __str__(self):
19- return self._message
20-
21-
22-class CannotUploadToPocket:
23+ Exception.__init__(self, self._fmt % args)
24+
25+
26+class CannotUploadToPocket(Exception):
27 """Returned when a pocket is closed for uploads."""
28
29 def __init__(self, distroseries, pocket):
30- super(CannotUploadToPocket, self).__init__(
31+ Exception.__init__(self,
32 "Not permitted to upload to the %s pocket in a series in the "
33 "'%s' state." % (pocket.name, distroseries.status.name))
34
35@@ -73,7 +70,7 @@
36 "Signer is not permitted to upload to the component '%(component)s'.")
37
38 def __init__(self, component):
39- super(NoRightsForComponent, self).__init__(component=component.name)
40+ CannotUploadToArchive.__init__(self, component=component.name)
41
42
43 class InvalidPocketForPPA(CannotUploadToArchive):
44@@ -88,6 +85,15 @@
45 _fmt = "Partner uploads must be for the RELEASE or PROPOSED pocket."
46
47
48+class ArchiveDisabled(CannotUploadToArchive):
49+ """Uploading to a disabled archive is not allowed."""
50+
51+ _fmt = ("%(archive_name)s is disabled.")
52+
53+ def __init__(self, archive_name):
54+ CannotUploadToArchive.__init__(self, archive_name=archive_name)
55+
56+
57 def components_valid_for(archive, person):
58 """Return the components that 'person' can upload to 'archive'.
59
60@@ -202,6 +208,9 @@
61 :return: CannotUploadToArchive if 'person' cannot upload to the archive,
62 None otherwise.
63 """
64+ if not archive.enabled:
65+ return ArchiveDisabled(archive.displayname)
66+
67 # For PPAs...
68 if archive.is_ppa:
69 if not archive.canUpload(person):
70
71=== modified file 'lib/lp/archiveuploader/tests/test_permission.py'
72--- lib/lp/archiveuploader/tests/test_permission.py 2009-12-13 11:55:40 +0000
73+++ lib/lp/archiveuploader/tests/test_permission.py 2010-02-05 10:56:18 +0000
74@@ -241,6 +241,14 @@
75 self.assertCanUpload(
76 person, spn, archive, component_a, strict_component=False)
77
78+ def test_cannot_upload_to_disabled_archive(self):
79+ spn = self.factory.makeSourcePackageName()
80+ archive = self.factory.makeArchive()
81+ removeSecurityProxy(archive).disable()
82+ component = self.factory.makeComponent()
83+ self.assertCannotUpload(u"%s is disabled." % (archive.displayname),
84+ archive.owner, spn, archive, component)
85+
86
87 def test_suite():
88 return unittest.TestLoader().loadTestsFromName(__name__)
89
90=== modified file 'lib/lp/buildmaster/interfaces/buildbase.py'
91--- lib/lp/buildmaster/interfaces/buildbase.py 2010-01-22 04:11:36 +0000
92+++ lib/lp/buildmaster/interfaces/buildbase.py 2010-02-05 10:56:18 +0000
93@@ -109,7 +109,7 @@
94 title=_("Distribution"), required=True,
95 description=_("Shortcut for its distribution.")))
96
97- def getUploaderCommand(upload_leaf):
98+ def getUploaderCommand(upload_leaf, uploader_logfilename):
99 """Get the command to run as the uploader.
100
101 :return: A list of command line arguments, beginning with the
102
103=== modified file 'lib/lp/buildmaster/model/buildbase.py'
104--- lib/lp/buildmaster/model/buildbase.py 2010-01-21 22:09:51 +0000
105+++ lib/lp/buildmaster/model/buildbase.py 2010-02-05 10:56:18 +0000
106@@ -16,7 +16,6 @@
107 import subprocess
108
109 from storm.store import Store
110-from zope.interface import implements
111 from zope.security.proxy import removeSecurityProxy
112
113 from canonical.config import config
114@@ -24,16 +23,21 @@
115 from canonical.database.sqlbase import (
116 clear_current_connection_cache, cursor, flush_database_updates)
117 from canonical.librarian.utils import copy_and_close
118-from lp.buildmaster.interfaces.buildbase import IBuildBase
119 from lp.registry.interfaces.pocket import pocketsuffix
120 from lp.soyuz.interfaces.build import BuildStatus
121 from lp.soyuz.model.buildqueue import BuildQueue
122
123
124 class BuildBase:
125-
126- implements(IBuildBase)
127-
128+ """A mixin class providing functionality for farm jobs that build a
129+ package.
130+
131+ Note: this class does not implement IBuildBase as we currently duplicate
132+ the properties defined on IBuildBase on the inheriting class tables.
133+ BuildBase cannot therefore implement IBuildBase itself, as storm requires
134+ that the corresponding __storm_table__ be defined for the class. Instead,
135+ the classes using the BuildBase mixin must ensure that they implement IBuildBase.
136+ """
137 policy_name = 'buildd'
138
139 def getUploadLeaf(self, build_id, now=None):
140
141=== modified file 'lib/lp/buildmaster/tests/test_buildbase.py'
142--- lib/lp/buildmaster/tests/test_buildbase.py 2010-01-21 22:35:21 +0000
143+++ lib/lp/buildmaster/tests/test_buildbase.py 2010-02-05 10:56:18 +0000
144@@ -11,7 +11,6 @@
145
146 from canonical.config import config
147 from canonical.testing.layers import DatabaseFunctionalLayer
148-from lp.buildmaster.interfaces.buildbase import IBuildBase
149 from lp.buildmaster.model.buildbase import BuildBase
150 from lp.registry.interfaces.pocket import pocketsuffix
151 from lp.testing import TestCase, TestCaseWithFactory
152@@ -20,13 +19,6 @@
153 class TestBuildBase(TestCase):
154 """Tests for `IBuildBase`."""
155
156- def disabled_test_build_base_provides_interface(self):
157- # XXX: JonathanLange 2010-01-22 bug=510919: BuildBase is supposed to
158- # implement IBuildBase, but doesn't atm. Since it's not the focus of
159- # the branch, we'll postpone the work.
160- build_base = BuildBase()
161- self.assertProvides(build_base, IBuildBase)
162-
163 def test_getUploadLeaf(self):
164 # getUploadLeaf returns the current time, followed by the build id.
165 build_base = BuildBase()
166
167=== modified file 'lib/lp/soyuz/interfaces/sourcepackagerecipe.py'
168--- lib/lp/soyuz/interfaces/sourcepackagerecipe.py 2010-01-14 21:31:28 +0000
169+++ lib/lp/soyuz/interfaces/sourcepackagerecipe.py 2010-02-05 10:56:18 +0000
170@@ -29,6 +29,7 @@
171 """A forbidden instruction was found in the recipe."""
172
173 def __init__(self, instruction_name):
174+ super(ForbiddenInstruction, self).__init__()
175 self.instruction_name = instruction_name
176
177
178@@ -36,6 +37,7 @@
179 """The format of the recipe supplied was too new."""
180
181 def __init__(self, supplied_format, newest_supported):
182+ super(TooNewRecipeFormat, self).__init__()
183 self.supplied_format = supplied_format
184 self.newest_supported = newest_supported
185
186@@ -75,6 +77,16 @@
187 def getReferencedBranches():
188 """An iterator of the branches referenced by this recipe."""
189
190+ def requestBuild(archive, distroseries, requester, pocket):
191+ """Request that the recipe be built in to the specified archive.
192+
193+ :param archive: The IArchive which you want the build to end up in.
194+ :param requester: the person requesting the build.
195+ :param pocket: the pocket that should be targeted.
196+ :raises: various specific upload errors if the requestor is not
197+ able to upload to the archive.
198+ """
199+
200
201 class ISourcePackageRecipeSource(Interface):
202 """A utility of this interface can be used to create and access recipes.
203
204=== modified file 'lib/lp/soyuz/model/build.py'
205--- lib/lp/soyuz/model/build.py 2010-01-20 22:09:26 +0000
206+++ lib/lp/soyuz/model/build.py 2010-02-05 10:56:18 +0000
207@@ -11,9 +11,6 @@
208 import datetime
209 import logging
210 import operator
211-import os
212-import subprocess
213-import time
214
215 from zope.interface import implements
216 from zope.component import getUtility
217@@ -30,8 +27,7 @@
218 from canonical.database.datetimecol import UtcDateTimeCol
219 from canonical.database.enumcol import EnumCol
220 from canonical.database.sqlbase import (
221- clear_current_connection_cache, flush_database_updates, cursor,
222- quote_like, SQLBase, sqlvalues)
223+ cursor, quote_like, SQLBase, sqlvalues)
224 from canonical.launchpad.components.decoratedresultset import (
225 DecoratedResultSet)
226 from canonical.launchpad.database.librarian import (
227@@ -47,17 +43,16 @@
228 from canonical.launchpad.webapp.interfaces import (
229 IStoreSelector, MAIN_STORE, DEFAULT_FLAVOR)
230 from canonical.launchpad.webapp.tales import DurationFormatterAPI
231-from canonical.librarian.utils import copy_and_close
232 from lp.archivepublisher.utils import get_ppa_reference
233 from lp.buildmaster.interfaces.buildfarmjob import BuildFarmJobType
234 from lp.buildmaster.model.buildbase import BuildBase
235-from lp.registry.interfaces.pocket import (PackagePublishingPocket,
236- pocketsuffix)
237+from lp.registry.interfaces.pocket import PackagePublishingPocket
238 from lp.services.job.model.job import Job
239 from lp.soyuz.adapters.archivedependencies import get_components_for_building
240 from lp.soyuz.interfaces.archive import ArchivePurpose
241 from lp.soyuz.interfaces.build import (
242 BuildStatus, BuildSetStatus, CannotBeRescored, IBuild, IBuildSet)
243+from lp.buildmaster.interfaces.buildbase import IBuildBase
244 from lp.buildmaster.interfaces.builder import IBuilderSet
245 from lp.soyuz.interfaces.publishing import active_publishing_status
246 from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
247@@ -71,7 +66,7 @@
248
249
250 class Build(BuildBase, SQLBase):
251- implements(IBuild)
252+ implements(IBuildBase, IBuild)
253 _table = 'Build'
254 _defaultOrder = 'id'
255
256
257=== modified file 'lib/lp/soyuz/model/sourcepackagerecipe.py'
258--- lib/lp/soyuz/model/sourcepackagerecipe.py 2010-01-13 03:18:49 +0000
259+++ lib/lp/soyuz/model/sourcepackagerecipe.py 2010-02-05 10:56:18 +0000
260@@ -10,16 +10,27 @@
261
262 from storm.locals import Int, Reference, Store, Storm, Unicode
263
264+from zope.component import getUtility
265 from zope.interface import classProvides, implements
266
267 from canonical.database.datetimecol import UtcDateTimeCol
268 from canonical.launchpad.interfaces.lpstorm import IMasterStore
269
270+from lp.soyuz.interfaces.archive import ArchivePurpose
271+from lp.soyuz.interfaces.component import IComponentSet
272+from lp.archiveuploader.permission import check_upload_to_archive
273 from lp.soyuz.interfaces.sourcepackagerecipe import (
274 ISourcePackageRecipe, ISourcePackageRecipeSource)
275+from lp.soyuz.interfaces.sourcepackagerecipebuild import (
276+ ISourcePackageRecipeBuildSource)
277 from lp.soyuz.model.sourcepackagerecipedata import _SourcePackageRecipeData
278
279
280+class NonPPABuildRequest(Exception):
281+ """A build was requested to a non-PPA and this is currently
282+ unsupported."""
283+
284+
285 class SourcePackageRecipe(Storm):
286 """See `ISourcePackageRecipe` and `ISourcePackageRecipeSource`."""
287
288@@ -82,3 +93,21 @@
289 sprecipe.name = name
290 store.add(sprecipe)
291 return sprecipe
292+
293+ def requestBuild(self, archive, requester, pocket):
294+ """See `ISourcePackageRecipe`."""
295+ if archive.purpose != ArchivePurpose.PPA:
296+ raise NonPPABuildRequest
297+ component = getUtility(IComponentSet)["multiverse"]
298+ reject_reason = check_upload_to_archive(
299+ requester, self.distroseries, self.sourcepackagename,
300+ archive, component, pocket)
301+ if reject_reason is not None:
302+ raise reject_reason
303+
304+ sourcepackage = self.distroseries.getSourcePackage(
305+ self.sourcepackagename)
306+ build = getUtility(ISourcePackageRecipeBuildSource).new(sourcepackage,
307+ self, requester, archive)
308+ build.queueBuild()
309+ return build
310
311=== modified file 'lib/lp/soyuz/model/sourcepackagerecipebuild.py'
312--- lib/lp/soyuz/model/sourcepackagerecipebuild.py 2010-01-22 04:01:17 +0000
313+++ lib/lp/soyuz/model/sourcepackagerecipebuild.py 2010-02-05 10:56:18 +0000
314@@ -21,6 +21,7 @@
315 from zope.component import getUtility
316 from zope.interface import classProvides, implements
317
318+from lp.buildmaster.interfaces.buildbase import IBuildBase
319 from lp.buildmaster.interfaces.buildfarmjob import BuildFarmJobType
320 from lp.buildmaster.model.buildbase import BuildBase
321 from lp.buildmaster.model.packagebuildfarmjob import PackageBuildFarmJob
322@@ -41,7 +42,7 @@
323
324 policy_name = 'recipe'
325
326- implements(ISourcePackageRecipeBuild)
327+ implements(IBuildBase, ISourcePackageRecipeBuild)
328 classProvides(ISourcePackageRecipeBuildSource)
329
330 build_farm_job_type = BuildFarmJobType.RECIPEBRANCHBUILD
331
332=== modified file 'lib/lp/soyuz/tests/test_build.py'
333--- lib/lp/soyuz/tests/test_build.py 2010-01-11 23:43:59 +0000
334+++ lib/lp/soyuz/tests/test_build.py 2010-02-05 10:56:18 +0000
335@@ -3,6 +3,8 @@
336
337 """Test Build features."""
338
339+from datetime import datetime, timedelta
340+import pytz
341 import unittest
342
343 from storm.store import Store
344@@ -10,9 +12,10 @@
345
346 from canonical.testing import LaunchpadZopelessLayer
347 from lp.services.job.model.job import Job
348+from lp.buildmaster.interfaces.buildbase import IBuildBase
349 from lp.buildmaster.interfaces.builder import IBuilderSet
350 from lp.soyuz.interfaces.component import IComponentSet
351-from lp.soyuz.interfaces.build import BuildStatus, IBuildSet
352+from lp.soyuz.interfaces.build import BuildStatus, IBuild, IBuildSet
353 from lp.soyuz.interfaces.publishing import PackagePublishingStatus
354 from lp.soyuz.model.buildqueue import BuildQueue
355 from lp.soyuz.model.buildpackagejob import BuildPackageJob
356@@ -21,6 +24,26 @@
357 from lp.testing import TestCaseWithFactory
358
359
360+class TestBuildInterface(TestCaseWithFactory):
361+
362+ layer = LaunchpadZopelessLayer
363+
364+ def test_providesInterfaces(self):
365+ # Build provides IBuildBase and IBuild.
366+ publisher = SoyuzTestPublisher()
367+ publisher.prepareBreezyAutotest()
368+ gedit_src_hist = publisher.getPubSource(
369+ sourcename="gedit", status=PackagePublishingStatus.PUBLISHED)
370+ build = gedit_src_hist.createMissingBuilds()[0]
371+
372+ # The IBuild.calculated_buildstart property asserts
373+ # that both datebuilt and buildduration are set.
374+ build.datebuilt = datetime.now(pytz.UTC)
375+ build.buildduration = timedelta(0, 1)
376+
377+ self.assertProvides(build, IBuildBase)
378+ self.assertProvides(build, IBuild)
379+
380 class TestBuildUpdateDependencies(TestCaseWithFactory):
381
382 layer = LaunchpadZopelessLayer
383
384=== modified file 'lib/lp/soyuz/tests/test_sourcepackagerecipe.py'
385--- lib/lp/soyuz/tests/test_sourcepackagerecipe.py 2010-01-13 03:18:49 +0000
386+++ lib/lp/soyuz/tests/test_sourcepackagerecipe.py 2010-02-05 10:56:18 +0000
387@@ -10,14 +10,33 @@
388
389 from bzrlib.plugins.builder.recipe import RecipeParser
390
391+from storm.locals import Store
392+
393 from zope.component import getUtility
394 from zope.security.interfaces import Unauthorized
395+from zope.security.proxy import removeSecurityProxy
396
397 from canonical.testing.layers import DatabaseFunctionalLayer
398
399+from lp.archiveuploader.permission import (
400+ ArchiveDisabled, CannotUploadToArchive, InvalidPocketForPPA)
401+from lp.registry.interfaces.pocket import PackagePublishingPocket
402+from lp.services.job.interfaces.job import (
403+ IJob, JobStatus)
404+from lp.soyuz.interfaces.archive import ArchivePurpose
405+from lp.soyuz.interfaces.buildqueue import (
406+ IBuildQueue)
407 from lp.soyuz.interfaces.sourcepackagerecipe import (
408 ForbiddenInstruction, ISourcePackageRecipe, ISourcePackageRecipeSource,
409 TooNewRecipeFormat)
410+from lp.soyuz.interfaces.sourcepackagerecipebuild import (
411+ ISourcePackageRecipeBuild, ISourcePackageRecipeBuildJob)
412+from lp.soyuz.model.buildqueue import (
413+ BuildQueue)
414+from lp.soyuz.model.sourcepackagerecipebuild import (
415+ SourcePackageRecipeBuildJob)
416+from lp.soyuz.model.sourcepackagerecipe import (
417+ NonPPABuildRequest)
418 from lp.testing import login_person, TestCaseWithFactory
419
420
421@@ -154,6 +173,54 @@
422 TooNewRecipeFormat,
423 self.makeSourcePackageRecipeFromBuilderRecipe, builder_recipe)
424
425+ def test_requestBuild(self):
426+ recipe = self.factory.makeSourcePackageRecipe()
427+ ppa = self.factory.makeArchive()
428+ build = recipe.requestBuild(ppa, ppa.owner,
429+ PackagePublishingPocket.RELEASE)
430+ self.assertProvides(build, ISourcePackageRecipeBuild)
431+ self.assertEqual(build.archive, ppa)
432+ self.assertEqual(build.distroseries, recipe.distroseries)
433+ self.assertEqual(build.requester, ppa.owner)
434+ store = Store.of(build)
435+ store.flush()
436+ build_job = store.find(SourcePackageRecipeBuildJob,
437+ SourcePackageRecipeBuildJob.build_id==build.id).one()
438+ self.assertProvides(build_job, ISourcePackageRecipeBuildJob)
439+ self.assertTrue(build_job.virtualized)
440+ job = build_job.job
441+ self.assertProvides(job, IJob)
442+ self.assertEquals(job.status, JobStatus.WAITING)
443+ build_queue = store.find(BuildQueue, BuildQueue.job==job.id).one()
444+ self.assertProvides(build_queue, IBuildQueue)
445+ self.assertTrue(build_queue.virtualized)
446+
447+ def test_requestBuildRejectsNotPPA(self):
448+ recipe = self.factory.makeSourcePackageRecipe()
449+ not_ppa = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY)
450+ self.assertRaises(NonPPABuildRequest, recipe.requestBuild, not_ppa,
451+ not_ppa.owner, PackagePublishingPocket.RELEASE)
452+
453+ def test_requestBuildRejectsNoPermission(self):
454+ recipe = self.factory.makeSourcePackageRecipe()
455+ ppa = self.factory.makeArchive()
456+ requester = self.factory.makePerson()
457+ self.assertRaises(CannotUploadToArchive, recipe.requestBuild, ppa,
458+ requester, PackagePublishingPocket.RELEASE)
459+
460+ def test_requestBuildRejectsInvalidPocket(self):
461+ recipe = self.factory.makeSourcePackageRecipe()
462+ ppa = self.factory.makeArchive()
463+ self.assertRaises(InvalidPocketForPPA, recipe.requestBuild, ppa,
464+ ppa.owner, PackagePublishingPocket.BACKPORTS)
465+
466+ def test_requestBuildRejectsDisabledArchive(self):
467+ recipe = self.factory.makeSourcePackageRecipe()
468+ ppa = self.factory.makeArchive()
469+ removeSecurityProxy(ppa).disable()
470+ self.assertRaises(ArchiveDisabled, recipe.requestBuild, ppa,
471+ ppa.owner, PackagePublishingPocket.RELEASE)
472+
473
474 class TestRecipeBranchRoundTripping(TestCaseWithFactory):
475
476
477=== modified file 'lib/lp/soyuz/tests/test_sourcepackagerecipebuild.py'
478--- lib/lp/soyuz/tests/test_sourcepackagerecipebuild.py 2010-01-20 01:24:01 +0000
479+++ lib/lp/soyuz/tests/test_sourcepackagerecipebuild.py 2010-02-05 10:56:18 +0000
480@@ -13,6 +13,7 @@
481
482 from canonical.testing.layers import DatabaseFunctionalLayer
483
484+from lp.buildmaster.interfaces.buildbase import IBuildBase
485 from lp.soyuz.interfaces.buildqueue import IBuildQueue
486 from lp.soyuz.interfaces.sourcepackagerecipebuild import (
487 ISourcePackageRecipeBuildJob, ISourcePackageRecipeBuild,
488@@ -33,9 +34,11 @@
489 archive=self.factory.makeArchive(),
490 requester=self.factory.makePerson())
491
492- def test_providesInterface(self):
493- # SourcePackageRecipeBuild provides ISourcePackageRecipeBuild.
494+ def test_providesInterfaces(self):
495+ # SourcePackageRecipeBuild provides IBuildBase and
496+ # ISourcePackageRecipeBuild.
497 spb = self.makeSourcePackageRecipeBuild()
498+ self.assertProvides(spb, IBuildBase)
499 self.assertProvides(spb, ISourcePackageRecipeBuild)
500
501 def test_saves_record(self):