Merge lp:~abentley/launchpad/skip-repeat-builds into lp:launchpad/db-devel

Proposed by Aaron Bentley
Status: Merged
Approved by: Brad Crittenden
Approved revision: no longer in the source branch.
Merged at revision: 9481
Proposed branch: lp:~abentley/launchpad/skip-repeat-builds
Merge into: lp:launchpad/db-devel
Diff against target: 362 lines (+176/-24)
8 files modified
lib/lp/code/browser/sourcepackagerecipe.py (+14/-7)
lib/lp/code/browser/tests/test_sourcepackagerecipe.py (+24/-3)
lib/lp/code/errors.py (+27/-6)
lib/lp/code/interfaces/webservice.py (+4/-3)
lib/lp/code/model/sourcepackagerecipe.py (+9/-1)
lib/lp/code/model/sourcepackagerecipebuild.py (+3/-0)
lib/lp/code/model/tests/test_sourcepackagerecipe.py (+76/-2)
lib/lp/code/model/tests/test_sourcepackagerecipebuild.py (+19/-2)
To merge this branch: bzr merge lp:~abentley/launchpad/skip-repeat-builds
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+27954@code.launchpad.net

Commit message

Avoid creating duplicate pending builds.

Description of the change

= Summary =
Fix bug #595686: duplicate pending builds can be created.

== Proposed fix ==
Raise BuildAlreadyPending SourcePackageRecipe.requestBuild is called and the
arguments match a build already pending. Handle this in the UI as a validation
error. Handle this SourcePackageRecipeBuild.makeDailyBuilds by skipping to the
next distroseries.

== Pre-implementation notes ==
Preimplementation was with thumper

== Implementation details ==
Also added tests for TooManyBuilds on the webservice. Some quota tests had to
be updated so that they did not raise BuildAlreadyPending instead of
TooManyBuilds.

== Tests ==
bin/test -t test_request_builds_rejects_duplicate -t test_requestBuildRejectRepeats -t test_requestBuildRejectOverQuota -t test_makeDailyBuilds_skips_pending

== Demo and Q/A ==
Create a build. Attempt to create a duplicate build. You should get an error
that the build is already pending.

= Launchpad lint =

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

Linting changed files:
  lib/lp/code/model/tests/test_sourcepackagerecipebuild.py
  lib/lp/code/interfaces/webservice.py
  lib/lp/code/errors.py
  lib/lp/code/model/tests/test_sourcepackagerecipe.py
  lib/lp/code/model/sourcepackagerecipebuild.py
  lib/lp/code/browser/sourcepackagerecipe.py
  lib/lp/code/model/sourcepackagerecipe.py
  lib/lp/code/browser/tests/test_sourcepackagerecipe.py

== Pyflakes notices ==

lib/lp/code/interfaces/webservice.py
    8: 'TooManyBuilds' imported but unused
    8: 'CodeImportAlreadyRunning' imported but unused
    8: 'CodeImportNotInReviewedState' imported but unused
    8: 'BuildAlreadyPending' imported but unused
    8: 'BranchMergeProposalExists' imported but unused
    11: 'IBranchSet' imported but unused
    11: 'BranchCreatorNotOwner' imported but unused
    11: 'IBranch' imported but unused
    11: 'BranchExists' imported but unused
    11: 'BranchCreatorNotMemberOfOwnerTeam' imported but unused
    14: 'IBranchMergeProposal' imported but unused
    15: 'IBranchSubscription' imported but unused
    16: 'ICodeImport' imported but unused
    17: 'ICodeReviewComment' imported but unused
    18: 'ICodeReviewVoteReference' imported but unused
    19: 'IStaticDiff' imported but unused
    19: 'IDiff' imported but unused
    19: 'IPreviewDiff' imported but unused
    20: 'ISourcePackageRecipe' imported but unused
    21: 'ISourcePackageRecipeBuild' imported but unused

== Pylint notices ==

lib/lp/code/interfaces/webservice.py
    19: [W0611] Unused import IDiff
    8: [W0611] Unused import TooManyBuilds
    15: [W0611] Unused import IBranchSubscription
    11: [W0611] Unused import BranchCreatorNotOwner
    8: [W0611] Unused import CodeImportNotInReviewedState
    14: [W0611] Unused import IBranchMergeProposal
    8: [W0611] Unused import CodeImportAlreadyRunning
    11: [W0611] Unused import BranchCreatorNotMemberOfOwnerTeam
    16: [W0611] Unused import ICodeImport
    20: [W0611] Unused import ISourcePackageRecipe
    18: [W0611] Unused import ICodeReviewVoteReference
    11: [W0611] Unused import IBranchSet
    19: [W0611] Unused import IStaticDiff
    8: [W0611] Unused import BuildAlreadyPending
    11: [W0611] Unused import IBranch
    19: [W0611] Unused import IPreviewDiff
    8: [W0611] Unused import BranchMergeProposalExists
    11: [W0611] Unused import BranchExists
    21: [W0611] Unused import ISourcePackageRecipeBuild
    17: [W0611] Unused import ICodeReviewComment

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

Hi Aaron,

Please add a docstring for test_requestBuildRejectRepeats.

The docstring for test_requestBuildRejectOverQuota is identical to the previous. c-n-p error?

Otherwise this branch looks really straightforward. Thanks.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/code/browser/sourcepackagerecipe.py'
--- lib/lp/code/browser/sourcepackagerecipe.py 2010-06-16 18:47:46 +0000
+++ lib/lp/code/browser/sourcepackagerecipe.py 2010-06-18 20:02:28 +0000
@@ -40,7 +40,7 @@
40from canonical.launchpad.webapp.sorting import sorted_dotted_numbers40from canonical.launchpad.webapp.sorting import sorted_dotted_numbers
41from canonical.widgets.itemswidgets import LabeledMultiCheckBoxWidget41from canonical.widgets.itemswidgets import LabeledMultiCheckBoxWidget
42from lp.buildmaster.interfaces.buildbase import BuildStatus42from lp.buildmaster.interfaces.buildbase import BuildStatus
43from lp.code.errors import ForbiddenInstruction43from lp.code.errors import BuildAlreadyPending, ForbiddenInstruction
44from lp.code.interfaces.branch import NoSuchBranch44from lp.code.interfaces.branch import NoSuchBranch
45from lp.code.interfaces.sourcepackagerecipe import (45from lp.code.interfaces.sourcepackagerecipe import (
46 ISourcePackageRecipe, ISourcePackageRecipeSource, MINIMAL_RECIPE_TEXT)46 ISourcePackageRecipe, ISourcePackageRecipeSource, MINIMAL_RECIPE_TEXT)
@@ -219,11 +219,9 @@
219 label = title219 label = title
220220
221 @property221 @property
222 def next_url(self):222 def cancel_url(self):
223 return canonical_url(self.context)223 return canonical_url(self.context)
224224
225 cancel_url = next_url
226
227 def validate(self, data):225 def validate(self, data):
228 over_quota_distroseries = []226 over_quota_distroseries = []
229 for distroseries in data['distros']:227 for distroseries in data['distros']:
@@ -239,9 +237,18 @@
239 def request_action(self, action, data):237 def request_action(self, action, data):
240 """User action for requesting a number of builds."""238 """User action for requesting a number of builds."""
241 for distroseries in data['distros']:239 for distroseries in data['distros']:
242 self.context.requestBuild(240 try:
243 data['archive'], self.user, distroseries,241 self.context.requestBuild(
244 PackagePublishingPocket.RELEASE, manual=True)242 data['archive'], self.user, distroseries,
243 PackagePublishingPocket.RELEASE, manual=True)
244 except BuildAlreadyPending, e:
245 self.setFieldError(
246 'distros',
247 'An identical build is already pending for %s.' %
248 e.distroseries)
249 return
250 self.next_url = self.cancel_url
251
245252
246253
247class SourcePackageRecipeBuildNavigation(Navigation, FileNavigationMixin):254class SourcePackageRecipeBuildNavigation(Navigation, FileNavigationMixin):
248255
=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2010-06-18 07:54:36 +0000
+++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2010-06-18 20:02:28 +0000
@@ -592,9 +592,10 @@
592 supports_virtualized=True)592 supports_virtualized=True)
593593
594 recipe = self.makeRecipe()594 recipe = self.makeRecipe()
595 [recipe.requestBuild(595 for x in range(5):
596 self.ppa, self.chef, woody, PackagePublishingPocket.RELEASE)596 build = recipe.requestBuild(
597 for x in range(5)]597 self.ppa, self.chef, woody, PackagePublishingPocket.RELEASE)
598 removeSecurityProxy(build).buildstate = BuildStatus.FULLYBUILT
598599
599 browser = self.getViewBrowser(recipe, '+request-builds')600 browser = self.getViewBrowser(recipe, '+request-builds')
600 browser.getControl('Woody').click()601 browser.getControl('Woody').click()
@@ -602,6 +603,26 @@
602 self.assertIn("You have exceeded today's quota for ubuntu woody.",603 self.assertIn("You have exceeded today's quota for ubuntu woody.",
603 extract_text(find_main_content(browser.contents)))604 extract_text(find_main_content(browser.contents)))
604605
606 def test_request_builds_rejects_duplicate(self):
607 """Over-quota build requests cause validation failures."""
608 woody = self.factory.makeDistroSeries(
609 name='woody', displayname='Woody',
610 distribution=self.ppa.distribution)
611 woody.nominatedarchindep = woody.newArch(
612 'i386', ProcessorFamily.get(1), False, self.factory.makePerson(),
613 supports_virtualized=True)
614
615 recipe = self.makeRecipe()
616 recipe.requestBuild(
617 self.ppa, self.chef, woody, PackagePublishingPocket.RELEASE)
618
619 browser = self.getViewBrowser(recipe, '+request-builds')
620 browser.getControl('Woody').click()
621 browser.getControl('Request builds').click()
622 self.assertIn(
623 "An identical build is already pending for ubuntu woody.",
624 extract_text(find_main_content(browser.contents)))
625
605626
606class TestSourcePackageRecipeBuildView(BrowserTestCase):627class TestSourcePackageRecipeBuildView(BrowserTestCase):
607 """Test behaviour of SourcePackageReciptBuildView."""628 """Test behaviour of SourcePackageReciptBuildView."""
608629
=== modified file 'lib/lp/code/errors.py'
--- lib/lp/code/errors.py 2010-06-11 05:05:52 +0000
+++ lib/lp/code/errors.py 2010-06-18 20:02:28 +0000
@@ -7,6 +7,7 @@
7__all__ = [7__all__ = [
8 'BadBranchMergeProposalSearchContext',8 'BadBranchMergeProposalSearchContext',
9 'BadStateTransition',9 'BadStateTransition',
10 'BuildAlreadyPending',
10 'BranchMergeProposalExists',11 'BranchMergeProposalExists',
11 'CodeImportAlreadyRequested',12 'CodeImportAlreadyRequested',
12 'CodeImportAlreadyRunning',13 'CodeImportAlreadyRunning',
@@ -113,13 +114,33 @@
113 self.newest_supported = newest_supported114 self.newest_supported = newest_supported
114115
115116
116class TooManyBuilds(Exception):117class RecipeBuildException(Exception):
117 """A build was requested that exceeded the quota."""
118118
119 def __init__(self, recipe, distroseries):119 def __init__(self, recipe, distroseries, template):
120 self.recipe = recipe120 self.recipe = recipe
121 self.distroseries = distroseries121 self.distroseries = distroseries
122 msg = (122 msg = template % {'recipe': recipe, 'distroseries': distroseries}
123 'You have exceeded your quota for recipe %s for distroseries %s'
124 % (self.recipe, distroseries))
125 Exception.__init__(self, msg)123 Exception.__init__(self, msg)
124
125
126class TooManyBuilds(RecipeBuildException):
127 """A build was requested that exceeded the quota."""
128
129 webservice_error(400)
130
131 def __init__(self, recipe, distroseries):
132 RecipeBuildException.__init__(
133 self, recipe, distroseries,
134 'You have exceeded your quota for recipe %(recipe)s for'
135 ' distroseries %(distroseries)s')
136
137
138class BuildAlreadyPending(RecipeBuildException):
139 """A build was requested when an identical build was already pending."""
140
141 webservice_error(400)
142
143 def __init__(self, recipe, distroseries):
144 RecipeBuildException.__init__(
145 self, recipe, distroseries,
146 'An identical build of this recipe is already pending.')
126147
=== modified file 'lib/lp/code/interfaces/webservice.py'
--- lib/lp/code/interfaces/webservice.py 2010-04-23 02:33:32 +0000
+++ lib/lp/code/interfaces/webservice.py 2010-06-18 20:02:28 +0000
@@ -6,8 +6,8 @@
6# The exceptions are imported so that they can produce the special6# The exceptions are imported so that they can produce the special
7# status code defined by webservice_error when they are raised.7# status code defined by webservice_error when they are raised.
8from lp.code.errors import (8from lp.code.errors import (
9 BranchMergeProposalExists, CodeImportAlreadyRunning,9 BuildAlreadyPending, BranchMergeProposalExists, CodeImportAlreadyRunning,
10 CodeImportNotInReviewedState)10 CodeImportNotInReviewedState, TooManyBuilds)
11from lp.code.interfaces.branch import (11from lp.code.interfaces.branch import (
12 IBranch, IBranchSet, BranchCreatorNotMemberOfOwnerTeam,12 IBranch, IBranchSet, BranchCreatorNotMemberOfOwnerTeam,
13 BranchCreatorNotOwner, BranchExists)13 BranchCreatorNotOwner, BranchExists)
@@ -18,4 +18,5 @@
18from lp.code.interfaces.codereviewvote import ICodeReviewVoteReference18from lp.code.interfaces.codereviewvote import ICodeReviewVoteReference
19from lp.code.interfaces.diff import IDiff, IPreviewDiff, IStaticDiff19from lp.code.interfaces.diff import IDiff, IPreviewDiff, IStaticDiff
20from lp.code.interfaces.sourcepackagerecipe import ISourcePackageRecipe20from lp.code.interfaces.sourcepackagerecipe import ISourcePackageRecipe
21from lp.code.interfaces.sourcepackagerecipebuild import ISourcePackageRecipeBuild21from lp.code.interfaces.sourcepackagerecipebuild import (
22 ISourcePackageRecipeBuild)
2223
=== modified file 'lib/lp/code/model/sourcepackagerecipe.py'
--- lib/lp/code/model/sourcepackagerecipe.py 2010-06-16 19:47:27 +0000
+++ lib/lp/code/model/sourcepackagerecipe.py 2010-06-18 20:02:28 +0000
@@ -24,7 +24,8 @@
24from canonical.database.datetimecol import UtcDateTimeCol24from canonical.database.datetimecol import UtcDateTimeCol
25from canonical.launchpad.interfaces.lpstorm import IMasterStore, IStore25from canonical.launchpad.interfaces.lpstorm import IMasterStore, IStore
2626
27from lp.code.errors import TooManyBuilds27from lp.buildmaster.interfaces.buildbase import BuildStatus
28from lp.code.errors import BuildAlreadyPending, TooManyBuilds
28from lp.code.interfaces.sourcepackagerecipe import (29from lp.code.interfaces.sourcepackagerecipe import (
29 ISourcePackageRecipe, ISourcePackageRecipeSource,30 ISourcePackageRecipe, ISourcePackageRecipeSource,
30 ISourcePackageRecipeData)31 ISourcePackageRecipeData)
@@ -193,6 +194,13 @@
193 raise reject_reason194 raise reject_reason
194 if self.isOverQuota(requester, distroseries):195 if self.isOverQuota(requester, distroseries):
195 raise TooManyBuilds(self, distroseries)196 raise TooManyBuilds(self, distroseries)
197 pending = IStore(self).find(SourcePackageRecipeBuild,
198 SourcePackageRecipeBuild.recipe_id == self.id,
199 SourcePackageRecipeBuild.distroseries_id == distroseries.id,
200 SourcePackageRecipeBuild.archive_id == archive.id,
201 SourcePackageRecipeBuild.buildstate == BuildStatus.NEEDSBUILD)
202 if pending.any() is not None:
203 raise BuildAlreadyPending(self, distroseries)
196204
197 build = getUtility(ISourcePackageRecipeBuildSource).new(distroseries,205 build = getUtility(ISourcePackageRecipeBuildSource).new(distroseries,
198 self, requester, archive)206 self, requester, archive)
199207
=== modified file 'lib/lp/code/model/sourcepackagerecipebuild.py'
--- lib/lp/code/model/sourcepackagerecipebuild.py 2010-06-17 13:38:27 +0000
+++ lib/lp/code/model/sourcepackagerecipebuild.py 2010-06-18 20:02:28 +0000
@@ -33,6 +33,7 @@
33from lp.buildmaster.model.buildbase import BuildBase33from lp.buildmaster.model.buildbase import BuildBase
34from lp.buildmaster.model.buildqueue import BuildQueue34from lp.buildmaster.model.buildqueue import BuildQueue
35from lp.buildmaster.model.buildfarmjob import BuildFarmJobOldDerived35from lp.buildmaster.model.buildfarmjob import BuildFarmJobOldDerived
36from lp.code.errors import BuildAlreadyPending
36from lp.code.interfaces.sourcepackagerecipebuild import (37from lp.code.interfaces.sourcepackagerecipebuild import (
37 ISourcePackageRecipeBuildJob, ISourcePackageRecipeBuildJobSource,38 ISourcePackageRecipeBuildJob, ISourcePackageRecipeBuildJobSource,
38 ISourcePackageRecipeBuild, ISourcePackageRecipeBuildSource)39 ISourcePackageRecipeBuild, ISourcePackageRecipeBuildSource)
@@ -229,6 +230,8 @@
229 build = recipe.requestBuild(230 build = recipe.requestBuild(
230 recipe.daily_build_archive, recipe.owner,231 recipe.daily_build_archive, recipe.owner,
231 distroseries, PackagePublishingPocket.RELEASE)232 distroseries, PackagePublishingPocket.RELEASE)
233 except BuildAlreadyPending:
234 continue
232 except:235 except:
233 info = sys.exc_info()236 info = sys.exc_info()
234 errorlog.globalErrorUtility.raising(info)237 errorlog.globalErrorUtility.raising(info)
235238
=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/model/tests/test_sourcepackagerecipe.py 2010-06-17 13:38:27 +0000
+++ lib/lp/code/model/tests/test_sourcepackagerecipe.py 2010-06-18 20:02:28 +0000
@@ -28,10 +28,12 @@
28from lp.soyuz.interfaces.archive import (28from lp.soyuz.interfaces.archive import (
29 ArchiveDisabled, ArchivePurpose, CannotUploadToArchive,29 ArchiveDisabled, ArchivePurpose, CannotUploadToArchive,
30 InvalidPocketForPPA)30 InvalidPocketForPPA)
31from lp.buildmaster.interfaces.buildbase import BuildStatus
31from lp.buildmaster.interfaces.buildqueue import IBuildQueue32from lp.buildmaster.interfaces.buildqueue import IBuildQueue
32from lp.buildmaster.model.buildqueue import BuildQueue33from lp.buildmaster.model.buildqueue import BuildQueue
33from lp.code.errors import (34from lp.code.errors import (
34 ForbiddenInstruction, TooManyBuilds, TooNewRecipeFormat)35 BuildAlreadyPending, ForbiddenInstruction, TooManyBuilds,
36 TooNewRecipeFormat)
35from lp.code.interfaces.sourcepackagerecipe import (37from lp.code.interfaces.sourcepackagerecipe import (
36 ISourcePackageRecipe, ISourcePackageRecipeSource, MINIMAL_RECIPE_TEXT)38 ISourcePackageRecipe, ISourcePackageRecipeSource, MINIMAL_RECIPE_TEXT)
37from lp.code.interfaces.sourcepackagerecipebuild import (39from lp.code.interfaces.sourcepackagerecipebuild import (
@@ -296,14 +298,41 @@
296 series = list(recipe.distroseries)[0]298 series = list(recipe.distroseries)[0]
297 archive = self.factory.makeArchive(owner=requester)299 archive = self.factory.makeArchive(owner=requester)
298 def request_build():300 def request_build():
299 recipe.requestBuild(archive, requester, series,301 build = recipe.requestBuild(archive, requester, series,
300 PackagePublishingPocket.RELEASE)302 PackagePublishingPocket.RELEASE)
303 removeSecurityProxy(build).buildstate = BuildStatus.FULLYBUILT
301 [request_build() for num in range(5)]304 [request_build() for num in range(5)]
302 e = self.assertRaises(TooManyBuilds, request_build)305 e = self.assertRaises(TooManyBuilds, request_build)
303 self.assertIn(306 self.assertIn(
304 'You have exceeded your quota for recipe requester/myrecipe',307 'You have exceeded your quota for recipe requester/myrecipe',
305 str(e))308 str(e))
306309
310 def test_requestBuildRejectRepeats(self):
311 """Reject build requests that are identical to pending builds."""
312 recipe = self.factory.makeSourcePackageRecipe()
313 series = list(recipe.distroseries)[0]
314 archive = self.factory.makeArchive(owner=recipe.owner)
315 old_build = recipe.requestBuild(archive, recipe.owner, series,
316 PackagePublishingPocket.RELEASE)
317 self.assertRaises(
318 BuildAlreadyPending, recipe.requestBuild, archive, recipe.owner,
319 series, PackagePublishingPocket.RELEASE)
320 # Varying archive allows build.
321 recipe.requestBuild(
322 self.factory.makeArchive(owner=recipe.owner), recipe.owner,
323 series, PackagePublishingPocket.RELEASE)
324 # Varying distroseries allows build.
325 new_distroseries = self.factory.makeDistroSeries()
326 new_distroseries.nominatedarchindep = new_distroseries.newArch(
327 'i386', ProcessorFamily.get(1), False, recipe.owner,
328 supports_virtualized=True)
329 recipe.requestBuild(archive, recipe.owner,
330 new_distroseries, PackagePublishingPocket.RELEASE)
331 # Changing status of old build allows new build.
332 removeSecurityProxy(old_build).buildstate = BuildStatus.FULLYBUILT
333 recipe.requestBuild(archive, recipe.owner, series,
334 PackagePublishingPocket.RELEASE)
335
307 def test_sourcepackagerecipe_description(self):336 def test_sourcepackagerecipe_description(self):
308 """Ensure that the SourcePackageRecipe has a proper description."""337 """Ensure that the SourcePackageRecipe has a proper description."""
309 description = u'The whoozits and whatzits.'338 description = u'The whoozits and whatzits.'
@@ -675,6 +704,51 @@
675 archive=archive, distroseries=distroseries,704 archive=archive, distroseries=distroseries,
676 pocket=PackagePublishingPocket.RELEASE.title)705 pocket=PackagePublishingPocket.RELEASE.title)
677706
707 def test_requestBuildRejectRepeat(self):
708 """Build requests are rejected if already pending."""
709 person = self.factory.makePerson()
710 archive = self.factory.makeArchive(owner=person)
711 distroseries = self.factory.makeDistroSeries()
712 distroseries_i386 = distroseries.newArch(
713 'i386', ProcessorFamily.get(1), False, person,
714 supports_virtualized=True)
715 distroseries.nominatedarchindep = distroseries_i386
716
717 recipe, user, launchpad = self.makeRecipe(person)
718 distroseries = ws_object(launchpad, distroseries)
719 archive = ws_object(launchpad, archive)
720 recipe.requestBuild(
721 archive=archive, distroseries=distroseries,
722 pocket=PackagePublishingPocket.RELEASE.title)
723 e = self.assertRaises(Exception, recipe.requestBuild,
724 archive=archive, distroseries=distroseries,
725 pocket=PackagePublishingPocket.RELEASE.title)
726 self.assertIn('BuildAlreadyPending', str(e))
727
728 def test_requestBuildRejectOverQuota(self):
729 """Build requests are rejected if they exceed quota."""
730 person = self.factory.makePerson()
731 archives = [self.factory.makeArchive(owner=person) for x in range(6)]
732 distroseries = self.factory.makeDistroSeries()
733 distroseries_i386 = distroseries.newArch(
734 'i386', ProcessorFamily.get(1), False, person,
735 supports_virtualized=True)
736 distroseries.nominatedarchindep = distroseries_i386
737
738 recipe, user, launchpad = self.makeRecipe(person)
739 distroseries = ws_object(launchpad, distroseries)
740 for archive in archives[:-1]:
741 archive = ws_object(launchpad, archive)
742 recipe.requestBuild(
743 archive=archive, distroseries=distroseries,
744 pocket=PackagePublishingPocket.RELEASE.title)
745
746 archive = ws_object(launchpad, archives[-1])
747 e = self.assertRaises(Exception, recipe.requestBuild,
748 archive=archive, distroseries=distroseries,
749 pocket=PackagePublishingPocket.RELEASE.title)
750 self.assertIn('TooManyBuilds', str(e))
751
678752
679def test_suite():753def test_suite():
680 return unittest.TestLoader().loadTestsFromName(__name__)754 return unittest.TestLoader().loadTestsFromName(__name__)
681755
=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipebuild.py'
--- lib/lp/code/model/tests/test_sourcepackagerecipebuild.py 2010-06-16 19:47:27 +0000
+++ lib/lp/code/model/tests/test_sourcepackagerecipebuild.py 2010-06-18 20:02:28 +0000
@@ -236,11 +236,28 @@
236 self.assertEqual(list(recipe.distroseries), [build.distroseries])236 self.assertEqual(list(recipe.distroseries), [build.distroseries])
237237
238 def test_makeDailyBuilds_clears_is_stale(self):238 def test_makeDailyBuilds_clears_is_stale(self):
239 recipe = self.factory.makeSourcePackageRecipe(build_daily=True,239 recipe = self.factory.makeSourcePackageRecipe(
240 is_stale=True)240 build_daily=True, is_stale=True)
241 SourcePackageRecipeBuild.makeDailyBuilds()[0]241 SourcePackageRecipeBuild.makeDailyBuilds()[0]
242 self.assertFalse(recipe.is_stale)242 self.assertFalse(recipe.is_stale)
243243
244 def test_makeDailyBuilds_skips_pending(self):
245 """When creating daily builds, skip ones that are already pending."""
246 recipe = self.factory.makeSourcePackageRecipe(
247 build_daily=True, is_stale=True)
248 first_distroseries = list(recipe.distroseries)[0]
249 recipe.requestBuild(
250 recipe.daily_build_archive, recipe.owner, first_distroseries,
251 PackagePublishingPocket.RELEASE)
252 second_distroseries = self.factory.makeDistroSeries()
253 second_distroseries.nominatedarchindep = second_distroseries.newArch(
254 'i386', ProcessorFamily.get(1), False, self.factory.makePerson(),
255 supports_virtualized=True)
256 recipe.distroseries.add(second_distroseries)
257 builds = SourcePackageRecipeBuild.makeDailyBuilds()
258 self.assertEqual(
259 [second_distroseries], [build.distroseries for build in builds])
260
244 def test_getRecentBuilds(self):261 def test_getRecentBuilds(self):
245 """Recent builds match the same person, series and receipe.262 """Recent builds match the same person, series and receipe.
246263

Subscribers

People subscribed via source and target branches

to status/vote changes: