Merge lp:~michael.nelson/launchpad/450124-findBuildCandidate_improvements into lp:launchpad

Proposed by Michael Nelson
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~michael.nelson/launchpad/450124-findBuildCandidate_improvements
Merge into: lp:launchpad
Diff against target: 340 lines
5 files modified
lib/lp/soyuz/doc/buildd-dispatching.txt (+12/-38)
lib/lp/soyuz/interfaces/builder.py (+4/-0)
lib/lp/soyuz/model/builder.py (+3/-3)
lib/lp/soyuz/tests/test_builder.py (+127/-0)
lib/lp/testing/factory.py (+29/-0)
To merge this branch: bzr merge lp:~michael.nelson/launchpad/450124-findBuildCandidate_improvements
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+13334@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Michael Nelson (michael.nelson) wrote :

= Summary =

Ensures that private PPAs are able to have builds for the same
architecture building in parallel.

== Pre-implementation notes ==

Chatted with Julian about other options - see bug 450124 for
documentation of those discussions.

== Implementation details ==

I had to create a unittest as the documentation is getting unreadable. I
also reverted the additional specific test that I had added to the
documentation now that it is covered by the unittest.

== Tests ==

bin/test -vvt TestFindBuildCandidate

== Demo and Q/A ==

= 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/soyuz/model/builder.py
  lib/lp/testing/factory.py
  lib/lp/soyuz/doc/buildd-dispatching.txt
  lib/lp/soyuz/tests/test_builder.py
  lib/lp/soyuz/interfaces/builder.py

--
Michael

Revision history for this message
Gavin Panella (allenap) wrote :
Download full text (6.1 KiB)

Hi Michael,

I have a few comments about the tests which you can take or
leave. Other than that, all good as far as I can tell :)

Gavin.

[...]
> === added file 'lib/lp/soyuz/tests/test_builder.py'
> --- lib/lp/soyuz/tests/test_builder.py 1970-01-01 00:00:00 +0000
> +++ lib/lp/soyuz/tests/test_builder.py 2009-10-14 10:55:53 +0000
> @@ -0,0 +1,116 @@
> +# Copyright 2009 Canonical Ltd. This software is licensed under the
> +# GNU Affero General Public License version 3 (see the file LICENSE).
> +
> +"""Test Builder features."""
> +
> +import unittest
> +
> +from zope.component import getUtility
> +
> +from canonical.testing import LaunchpadZopelessLayer
> +from lp.soyuz.interfaces.archive import ArchivePurpose
> +from lp.soyuz.interfaces.builder import IBuilderSet
> +from lp.soyuz.interfaces.build import BuildStatus
> +from lp.soyuz.interfaces.publishing import PackagePublishingStatus
> +from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
> +from lp.testing import TestCaseWithFactory
> +
> +
> +class TestFindBuildCandidate(TestCaseWithFactory):
> +
> + layer = LaunchpadZopelessLayer
> +
> + def setUp(self):
> + """Publish some builds for the test archive."""
> + super(TestFindBuildCandidate, self).setUp()
> + self.publisher = SoyuzTestPublisher()
> + self.publisher.prepareBreezyAutotest()
> +
> + # Create two PPAs and add some builds to each.
> + self.ppa_joe = self.factory.makeArchive(name="joesppa")
> + self.ppa_jim = self.factory.makeArchive(name="jimsppa")
> +
> + self.publisher.getPubSource(
> + sourcename="gedit", status=PackagePublishingStatus.PUBLISHED,
> + archive=self.ppa_joe).createMissingBuilds()
> + self.publisher.getPubSource(
> + sourcename="firefox", status=PackagePublishingStatus.PUBLISHED,
> + archive=self.ppa_joe).createMissingBuilds()
> +
> + self.publisher.getPubSource(
> + sourcename="gedit", status=PackagePublishingStatus.PUBLISHED,
> + archive=self.ppa_jim).createMissingBuilds()
> + self.publisher.getPubSource(
> + sourcename="firefox", status=PackagePublishingStatus.PUBLISHED,
> + archive=self.ppa_jim).createMissingBuilds()
> +
> + # Create two i386 builders ready to build PPA builds.
> + builder_set = getUtility(IBuilderSet)
> + self.builder1 = self.factory.makeBuilder(name='bob2')
> + self.builder2 = self.factory.makeBuilder(name='frog2')
> +
> + # Grab the first build, ensure that it is what we expect
> + # (ie. the first build from joesppa) and set it building.
> + self.first_job = self.builder1.findBuildCandidate()
> + self.failUnlessEqual('joesppa', self.first_job.build.archive.name)
> + self.failUnlessEqual(
> + u'i386 build of gedit 666 in ubuntutest breezy-autotest RELEASE',
> + self.first_job.build.title)
> + self.first_job.build.buildstate = BuildStatus.BUILDING
> + self.first_job.build.builder = self.builder1

That looks like a lot of set-up. If there's anything there that's
specific to only one test_* method, consider moving it o...

Read more...

review: Approve
Revision history for this message
Michael Nelson (michael.nelson) wrote :
Download full text (6.4 KiB)

Gavin Panella wrote:
> Review: Approve
> Hi Michael,
>
> I have a few comments about the tests which you can take or
> leave. Other than that, all good as far as I can tell :)
>
> Gavin.

Thanks Gavin.

>
>
> [...]
>> === added file 'lib/lp/soyuz/tests/test_builder.py'
>> --- lib/lp/soyuz/tests/test_builder.py 1970-01-01 00:00:00 +0000
>> +++ lib/lp/soyuz/tests/test_builder.py 2009-10-14 10:55:53 +0000
>> @@ -0,0 +1,116 @@
>> +# Copyright 2009 Canonical Ltd. This software is licensed under the
>> +# GNU Affero General Public License version 3 (see the file LICENSE).
>> +
>> +"""Test Builder features."""
>> +
>> +import unittest
>> +
>> +from zope.component import getUtility
>> +
>> +from canonical.testing import LaunchpadZopelessLayer
>> +from lp.soyuz.interfaces.archive import ArchivePurpose
>> +from lp.soyuz.interfaces.builder import IBuilderSet
>> +from lp.soyuz.interfaces.build import BuildStatus
>> +from lp.soyuz.interfaces.publishing import PackagePublishingStatus
>> +from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
>> +from lp.testing import TestCaseWithFactory
>> +
>> +
>> +class TestFindBuildCandidate(TestCaseWithFactory):
>> +
>> + layer = LaunchpadZopelessLayer
>> +
>> + def setUp(self):
>> + """Publish some builds for the test archive."""
>> + super(TestFindBuildCandidate, self).setUp()
>> + self.publisher = SoyuzTestPublisher()
>> + self.publisher.prepareBreezyAutotest()
>> +
>> + # Create two PPAs and add some builds to each.
>> + self.ppa_joe = self.factory.makeArchive(name="joesppa")
>> + self.ppa_jim = self.factory.makeArchive(name="jimsppa")
>> +
>> + self.publisher.getPubSource(
>> + sourcename="gedit", status=PackagePublishingStatus.PUBLISHED,
>> + archive=self.ppa_joe).createMissingBuilds()
>> + self.publisher.getPubSource(
>> + sourcename="firefox", status=PackagePublishingStatus.PUBLISHED,
>> + archive=self.ppa_joe).createMissingBuilds()
>> +
>> + self.publisher.getPubSource(
>> + sourcename="gedit", status=PackagePublishingStatus.PUBLISHED,
>> + archive=self.ppa_jim).createMissingBuilds()
>> + self.publisher.getPubSource(
>> + sourcename="firefox", status=PackagePublishingStatus.PUBLISHED,
>> + archive=self.ppa_jim).createMissingBuilds()
>> +
>> + # Create two i386 builders ready to build PPA builds.
>> + builder_set = getUtility(IBuilderSet)
>> + self.builder1 = self.factory.makeBuilder(name='bob2')
>> + self.builder2 = self.factory.makeBuilder(name='frog2')
>> +
>> + # Grab the first build, ensure that it is what we expect
>> + # (ie. the first build from joesppa) and set it building.
>> + self.first_job = self.builder1.findBuildCandidate()
>> + self.failUnlessEqual('joesppa', self.first_job.build.archive.name)
>> + self.failUnlessEqual(
>> + u'i386 build of gedit 666 in ubuntutest breezy-autotest RELEASE',
>> + self.first_job.build.title)
>> + self.first_job.build.buildstate = BuildStatus.BUILDING
>> + self.first_job.build.builder = ...

Read more...

1=== modified file 'lib/lp/soyuz/tests/test_builder.py'
2--- lib/lp/soyuz/tests/test_builder.py 2009-10-14 09:43:42 +0000
3+++ lib/lp/soyuz/tests/test_builder.py 2009-10-14 14:22:29 +0000
4@@ -16,39 +16,46 @@
5 from lp.testing import TestCaseWithFactory
6
7
8-class TestFindBuildCandidate(TestCaseWithFactory):
9+class TestFindBuildCandidateBase(TestCaseWithFactory):
10+ """Setup the test publisher and some builders."""
11
12 layer = LaunchpadZopelessLayer
13
14 def setUp(self):
15- """Publish some builds for the test archive."""
16- super(TestFindBuildCandidate, self).setUp()
17+ super(TestFindBuildCandidateBase, self).setUp()
18 self.publisher = SoyuzTestPublisher()
19 self.publisher.prepareBreezyAutotest()
20
21- # Create two PPAs and add some builds to each.
22- self.ppa_joe = self.factory.makeArchive(name="joesppa")
23- self.ppa_jim = self.factory.makeArchive(name="jimsppa")
24-
25- self.publisher.getPubSource(
26- sourcename="gedit", status=PackagePublishingStatus.PUBLISHED,
27- archive=self.ppa_joe).createMissingBuilds()
28- self.publisher.getPubSource(
29- sourcename="firefox", status=PackagePublishingStatus.PUBLISHED,
30- archive=self.ppa_joe).createMissingBuilds()
31-
32- self.publisher.getPubSource(
33- sourcename="gedit", status=PackagePublishingStatus.PUBLISHED,
34- archive=self.ppa_jim).createMissingBuilds()
35- self.publisher.getPubSource(
36- sourcename="firefox", status=PackagePublishingStatus.PUBLISHED,
37- archive=self.ppa_jim).createMissingBuilds()
38-
39 # Create two i386 builders ready to build PPA builds.
40 builder_set = getUtility(IBuilderSet)
41 self.builder1 = self.factory.makeBuilder(name='bob2')
42 self.builder2 = self.factory.makeBuilder(name='frog2')
43
44+
45+class TestFindBuildCandidatePPA(TestFindBuildCandidateBase):
46+
47+ def setUp(self):
48+ """Publish some builds for the test archive."""
49+ super(TestFindBuildCandidatePPA, self).setUp()
50+
51+ # Create two PPAs and add some builds to each.
52+ self.ppa_joe = self.factory.makeArchive(name="joesppa")
53+ self.ppa_jim = self.factory.makeArchive(name="jimsppa")
54+
55+ self.publisher.getPubSource(
56+ sourcename="gedit", status=PackagePublishingStatus.PUBLISHED,
57+ archive=self.ppa_joe).createMissingBuilds()
58+ self.publisher.getPubSource(
59+ sourcename="firefox", status=PackagePublishingStatus.PUBLISHED,
60+ archive=self.ppa_joe).createMissingBuilds()
61+
62+ self.publisher.getPubSource(
63+ sourcename="gedit", status=PackagePublishingStatus.PUBLISHED,
64+ archive=self.ppa_jim).createMissingBuilds()
65+ self.publisher.getPubSource(
66+ sourcename="firefox", status=PackagePublishingStatus.PUBLISHED,
67+ archive=self.ppa_jim).createMissingBuilds()
68+
69 # Grab the first build, ensure that it is what we expect
70 # (ie. the first build from joesppa) and set it building.
71 self.first_job = self.builder1.findBuildCandidate()
72@@ -80,9 +87,14 @@
73 next_job = self.builder2.findBuildCandidate()
74 self.failUnlessEqual('joesppa', next_job.build.archive.name)
75
76- def test_findBuildCandidate_for_non_ppa(self):
77- # Normal archives are not restricted to serial builds per
78- # arch.
79+
80+class TestFindBuildCandidateDistroArchive(TestFindBuildCandidateBase):
81+
82+ def setUp(self):
83+ """Publish some builds for the test archive."""
84+ super(TestFindBuildCandidateDistroArchive, self).setUp()
85+ # Create a primary archive and publish some builds for the
86+ # queue.
87 non_ppa = self.factory.makeArchive(
88 name="primary", purpose=ArchivePurpose.PRIMARY)
89
90@@ -93,10 +105,9 @@
91 sourcename="firefox", status=PackagePublishingStatus.PUBLISHED,
92 archive=non_ppa).createMissingBuilds()[0]
93
94- # Rescore our primary builds so that they'll be returned before
95- # the PPA ones.
96- gedit_build.buildqueue_record.manualScore(3000)
97- firefox_build.buildqueue_record.manualScore(3000)
98+ def test_findBuildCandidate_for_non_ppa(self):
99+ # Normal archives are not restricted to serial builds per
100+ # arch.
101
102 next_job = self.builder2.findBuildCandidate()
103 self.failUnlessEqual('primary', next_job.build.archive.name)
Revision history for this message
Gavin Panella (allenap) wrote :

That's a beauty :)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/soyuz/doc/buildd-dispatching.txt'
2--- lib/lp/soyuz/doc/buildd-dispatching.txt 2009-10-12 11:46:43 +0000
3+++ lib/lp/soyuz/doc/buildd-dispatching.txt 2009-10-14 14:28:13 +0000
4@@ -202,20 +202,15 @@
5
6 == PPA build dispatching ==
7
8-Create two Build records of the same source targeted for a PPA archive:
9+Create a new Build record of the same source targeted for a PPA archive:
10
11 >>> from lp.registry.interfaces.person import IPersonSet
12 >>> cprov = getUtility(IPersonSet).getByName('cprov')
13- >>> warty = getUtility(IDistributionSet)['ubuntu']['warty']
14- >>> warty_i386 = warty['i386']
15
16- # One for hoary and one for warty.
17 >>> ppa_build = sprf.sourcepackagerelease.createBuild(
18 ... hoary_i386, PackagePublishingPocket.RELEASE, cprov.archive)
19- >>> ppa_build_2 = sprf.sourcepackagerelease.createBuild(
20- ... warty_i386, PackagePublishingPocket.RELEASE, cprov.archive)
21
22-Create BuildQueue records and inspect some parameters:
23+Create BuildQueue record and inspect some parameters:
24
25 >>> ppa_job = ppa_build.createBuildQueueEntry()
26 >>> ppa_job.id
27@@ -224,9 +219,6 @@
28 True
29 >>> ppa_job.buildstart == None
30 True
31- >>> ppa_job_2 = ppa_build_2.createBuildQueueEntry()
32- >>> ppa_job_2.id
33- 4
34
35 The build job's archive requires virtualized builds.
36
37@@ -248,10 +240,10 @@
38 >>> print job
39 None
40
41-In order to enable 'bob' to find and build the PPA jobs, we have to
42+In order to enable 'bob' to find and build the PPA job, we have to
43 change it to virtualized. This is because PPA builds will only build
44-on virtualized builders. We also need to make sure the builds' sources
45-are published, or they will also be ignored (by superseding it). We can
46+on virtualized builders. We also need to make sure this build's source
47+is published, or it will also be ignored (by superseding it). We can
48 do this by copying the existing publication in Ubuntu.
49
50 >>> from lp.soyuz.model.publishing import (
51@@ -261,11 +253,6 @@
52 ... sourcepackagerelease=ppa_job.build.sourcepackagerelease)
53 >>> new_pub = old_pub.copyTo(
54 ... old_pub.distroseries, old_pub.pocket, ppa_job.build.archive)
55- >>> [old_pub_2] = SourcePackagePublishingHistory.selectBy(
56- ... distroseries=ppa_job_2.build.distroseries,
57- ... sourcepackagerelease=ppa_job_2.build.sourcepackagerelease)
58- >>> new_pub_2 = old_pub_2.copyTo(
59- ... old_pub_2.distroseries, old_pub_2.pocket, ppa_job_2.build.archive)
60
61 >>> bob_builder.virtualized = True
62 >>> syncUpdate(bob_builder)
63@@ -274,6 +261,9 @@
64 >>> ppa_job.id == job.id
65 True
66
67+For further details regarding IBuilder.findBuildCandidate() please see
68+lib/lp/soyuz/tests/test_builder.py.
69+
70 Start buildd-slave to be able to dispatch jobs.
71
72 >>> BuilddSlaveTestSetup().setUp()
73@@ -309,30 +299,14 @@
74 >>> ppa_job.buildstart == get_transaction_timestamp()
75 True
76
77-At this point, the second build job - for the same archive and architecture -
78-will not be found as a build candidate for other builders. This is to
79-avoid nightly builds using all the builders for an arch at once.
80-
81- >>> print frog_builder.findBuildCandidate()
82- None
83-
84-But once the first build (for the same archive and architecture) finishes,
85-the second build will be available as a build candidate for both builders.
86-
87- >>> ppa_job.build.buildstate = BuildStatus.FAILEDTOBUILD
88- >>> ppa_job.destroySelf()
89- >>> print frog_builder.findBuildCandidate().id
90- 4
91- >>> print bob_builder.findBuildCandidate().id
92- 4
93-
94 Shutdown builder slave, mark the ppa build record as failed, remove the
95 buildqueue record and make 'bob' builder non-virtual again, so the
96 environment is back to the initial state.
97
98 >>> BuilddSlaveTestSetup().tearDown()
99- >>> ppa_job_2.build.buildstate = BuildStatus.FAILEDTOBUILD
100- >>> ppa_job_2.destroySelf()
101+
102+ >>> ppa_job.build.buildstate = BuildStatus.FAILEDTOBUILD
103+ >>> ppa_job.destroySelf()
104 >>> bob_builder.virtualized = False
105 >>> flush_database_updates()
106
107@@ -355,7 +329,7 @@
108
109 >>> sec_job = sec_build.createBuildQueueEntry()
110 >>> sec_job.id
111- 5
112+ 4
113 >>> print sec_job.builder
114 None
115 >>> print sec_job.buildstart
116
117=== modified file 'lib/lp/soyuz/interfaces/builder.py'
118--- lib/lp/soyuz/interfaces/builder.py 2009-08-27 14:48:38 +0000
119+++ lib/lp/soyuz/interfaces/builder.py 2009-10-14 14:28:13 +0000
120@@ -262,6 +262,10 @@
121
122 The pending BuildQueue item with the highest score for this builder
123 ProcessorFamily or None if no candidate is available.
124+
125+ For public PPA builds, subsequent builds for a given ppa and
126+ architecture will not be returned until the current build for
127+ the ppa and architecture is finished.
128 """
129
130 def dispatchBuildCandidate(candidate):
131
132=== modified file 'lib/lp/soyuz/model/builder.py'
133--- lib/lp/soyuz/model/builder.py 2009-10-12 15:56:25 +0000
134+++ lib/lp/soyuz/model/builder.py 2009-10-14 14:28:13 +0000
135@@ -570,8 +570,8 @@
136 """ % sqlvalues(self.virtualized))
137
138 # Ensure that if a currently-building build exists for the same
139- # ppa archive and architecture currently building then we don't
140- # consider another as a candidate.
141+ # public ppa archive and architecture currently building then
142+ # we don't consider another as a candidate.
143 clauses.append("""
144 NOT EXISTS (
145 SELECT Build.id
146@@ -579,6 +579,7 @@
147 WHERE
148 build2.archive = build.archive AND
149 archive.purpose = %s AND
150+ archive.private IS FALSE AND
151 build2.distroarchseries = distroarchseries2.id AND
152 distroarchseries2.processorfamily = %s AND
153 build2.buildstate = %s)
154@@ -586,7 +587,6 @@
155 ArchivePurpose.PPA, self.processor.family, BuildStatus.BUILDING))
156
157 query = " AND ".join(clauses)
158-
159 candidate = BuildQueue.selectFirst(
160 query, clauseTables=clauseTables, prejoins=['build'],
161 orderBy=['-buildqueue.lastscore', 'build.id'])
162
163=== added file 'lib/lp/soyuz/tests/test_builder.py'
164--- lib/lp/soyuz/tests/test_builder.py 1970-01-01 00:00:00 +0000
165+++ lib/lp/soyuz/tests/test_builder.py 2009-10-14 14:28:13 +0000
166@@ -0,0 +1,127 @@
167+# Copyright 2009 Canonical Ltd. This software is licensed under the
168+# GNU Affero General Public License version 3 (see the file LICENSE).
169+
170+"""Test Builder features."""
171+
172+import unittest
173+
174+from zope.component import getUtility
175+
176+from canonical.testing import LaunchpadZopelessLayer
177+from lp.soyuz.interfaces.archive import ArchivePurpose
178+from lp.soyuz.interfaces.builder import IBuilderSet
179+from lp.soyuz.interfaces.build import BuildStatus
180+from lp.soyuz.interfaces.publishing import PackagePublishingStatus
181+from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
182+from lp.testing import TestCaseWithFactory
183+
184+
185+class TestFindBuildCandidateBase(TestCaseWithFactory):
186+ """Setup the test publisher and some builders."""
187+
188+ layer = LaunchpadZopelessLayer
189+
190+ def setUp(self):
191+ super(TestFindBuildCandidateBase, self).setUp()
192+ self.publisher = SoyuzTestPublisher()
193+ self.publisher.prepareBreezyAutotest()
194+
195+ # Create two i386 builders ready to build PPA builds.
196+ builder_set = getUtility(IBuilderSet)
197+ self.builder1 = self.factory.makeBuilder(name='bob2')
198+ self.builder2 = self.factory.makeBuilder(name='frog2')
199+
200+
201+class TestFindBuildCandidatePPA(TestFindBuildCandidateBase):
202+
203+ def setUp(self):
204+ """Publish some builds for the test archive."""
205+ super(TestFindBuildCandidatePPA, self).setUp()
206+
207+ # Create two PPAs and add some builds to each.
208+ self.ppa_joe = self.factory.makeArchive(name="joesppa")
209+ self.ppa_jim = self.factory.makeArchive(name="jimsppa")
210+
211+ self.publisher.getPubSource(
212+ sourcename="gedit", status=PackagePublishingStatus.PUBLISHED,
213+ archive=self.ppa_joe).createMissingBuilds()
214+ self.publisher.getPubSource(
215+ sourcename="firefox", status=PackagePublishingStatus.PUBLISHED,
216+ archive=self.ppa_joe).createMissingBuilds()
217+
218+ self.publisher.getPubSource(
219+ sourcename="gedit", status=PackagePublishingStatus.PUBLISHED,
220+ archive=self.ppa_jim).createMissingBuilds()
221+ self.publisher.getPubSource(
222+ sourcename="firefox", status=PackagePublishingStatus.PUBLISHED,
223+ archive=self.ppa_jim).createMissingBuilds()
224+
225+ # Grab the first build, ensure that it is what we expect
226+ # (ie. the first build from joesppa) and set it building.
227+ self.first_job = self.builder1.findBuildCandidate()
228+ self.failUnlessEqual('joesppa', self.first_job.build.archive.name)
229+ self.failUnlessEqual(
230+ u'i386 build of gedit 666 in ubuntutest breezy-autotest RELEASE',
231+ self.first_job.build.title)
232+ self.first_job.build.buildstate = BuildStatus.BUILDING
233+ self.first_job.build.builder = self.builder1
234+
235+ def test_findBuildCandidate_first_build_started(self):
236+ # Once a build for an ppa+arch has started, a second one for the
237+ # same ppa+arch will not be a candidate.
238+ next_job = self.builder2.findBuildCandidate()
239+ self.failIfEqual('joesppa', next_job.build.archive.name)
240+
241+ def test_findBuildCandidate_first_build_finished(self):
242+ # When joe's first ppa build finishes, his second i386 build
243+ # will be the next build candidate.
244+ self.first_job.build.buildstate = BuildStatus.FAILEDTOBUILD
245+ next_job = self.builder2.findBuildCandidate()
246+ self.failUnlessEqual('joesppa', next_job.build.archive.name)
247+
248+ def test_findBuildCandidate_for_private_ppa(self):
249+ # If a ppa is private it will be able to have parallel builds
250+ # for the one architecture.
251+ self.ppa_joe.private = True
252+ self.ppa_joe.buildd_secret = 'sekrit'
253+ next_job = self.builder2.findBuildCandidate()
254+ self.failUnlessEqual('joesppa', next_job.build.archive.name)
255+
256+
257+class TestFindBuildCandidateDistroArchive(TestFindBuildCandidateBase):
258+
259+ def setUp(self):
260+ """Publish some builds for the test archive."""
261+ super(TestFindBuildCandidateDistroArchive, self).setUp()
262+ # Create a primary archive and publish some builds for the
263+ # queue.
264+ non_ppa = self.factory.makeArchive(
265+ name="primary", purpose=ArchivePurpose.PRIMARY)
266+
267+ gedit_build = self.publisher.getPubSource(
268+ sourcename="gedit", status=PackagePublishingStatus.PUBLISHED,
269+ archive=non_ppa).createMissingBuilds()[0]
270+ firefox_build = self.publisher.getPubSource(
271+ sourcename="firefox", status=PackagePublishingStatus.PUBLISHED,
272+ archive=non_ppa).createMissingBuilds()[0]
273+
274+ def test_findBuildCandidate_for_non_ppa(self):
275+ # Normal archives are not restricted to serial builds per
276+ # arch.
277+
278+ next_job = self.builder2.findBuildCandidate()
279+ self.failUnlessEqual('primary', next_job.build.archive.name)
280+ self.failUnlessEqual(
281+ 'gedit', next_job.build.sourcepackagerelease.name)
282+
283+ # Now even if we set the build building, we'll still get the
284+ # second non-ppa build for the same archive as the next candidate.
285+ next_job.build.buildstate = BuildStatus.BUILDING
286+ next_job.build.builder = self.builder2
287+ next_job = self.builder2.findBuildCandidate()
288+ self.failUnlessEqual('primary', next_job.build.archive.name)
289+ self.failUnlessEqual(
290+ 'firefox', next_job.build.sourcepackagerelease.name)
291+
292+def test_suite():
293+ return unittest.TestLoader().loadTestsFromName(__name__)
294
295=== modified file 'lib/lp/testing/factory.py'
296--- lib/lp/testing/factory.py 2009-10-06 00:01:08 +0000
297+++ lib/lp/testing/factory.py 2009-10-14 14:28:13 +0000
298@@ -106,6 +106,7 @@
299 ISourcePackageNameSet)
300 from lp.registry.interfaces.ssh import ISSHKeySet, SSHKeyType
301 from lp.services.worlddata.interfaces.language import ILanguageSet
302+from lp.soyuz.interfaces.builder import IBuilderSet
303 from lp.soyuz.interfaces.component import IComponentSet
304 from lp.soyuz.interfaces.packageset import IPackagesetSet
305 from lp.testing import run_with_login, time_counter
306@@ -1485,6 +1486,34 @@
307 owner=owner, purpose=purpose,
308 distribution=distribution, name=name)
309
310+ def makeBuilder(self, processor=None, url=None, name=None, title=None,
311+ description=None, owner=None, active=True,
312+ virtualized=True, vm_host=None):
313+ """Make a new builder for i386 virtualized builds by default.
314+
315+ Note: the builder returned will not be able to actually build -
316+ we currently have a build slave setup for 'bob' only in the
317+ test environment.
318+ See lib/canonical/buildd/tests/buildd-slave-test.conf
319+ """
320+ if processor is None:
321+ processor_fam = ProcessorFamilySet().getByName('x86')
322+ processor = processor_fam.processors[0]
323+ if url is None:
324+ url = 'http://%s:8221/' % self.getUniqueString()
325+ if name is None:
326+ name = self.getUniqueString()
327+ if title is None:
328+ title = self.getUniqueString()
329+ if description is None:
330+ description = self.getUniqueString()
331+ if owner is None:
332+ owner = self.makePerson()
333+
334+ return getUtility(IBuilderSet).new(
335+ processor, url, name, title, description, owner, active,
336+ virtualized, vm_host)
337+
338 def makePOTemplate(self, productseries=None, distroseries=None,
339 sourcepackagename=None, owner=None, name=None,
340 translation_domain=None, path=None):