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