Merge lp:~stevenk/launchpad/bpb-currentcomponent-assertion-part-4 into lp:launchpad
- bpb-currentcomponent-assertion-part-4
- Merge into devel
Proposed by
Steve Kowalik
Status: | Merged |
---|---|
Approved by: | Steve Kowalik |
Approved revision: | no longer in the source branch. |
Merged at revision: | 12253 |
Proposed branch: | lp:~stevenk/launchpad/bpb-currentcomponent-assertion-part-4 |
Merge into: | lp:launchpad |
Prerequisite: | lp:~stevenk/launchpad/bpb-currentcomponent-assertion-part-3 |
Diff against target: |
863 lines (+330/-426) 5 files modified
lib/lp/soyuz/doc/binarypackagebuild.txt (+0/-424) lib/lp/soyuz/tests/test_build.py (+69/-2) lib/lp/soyuz/tests/test_build_depwait.py (+120/-0) lib/lp/soyuz/tests/test_build_privacy.py (+87/-0) lib/lp/soyuz/tests/test_build_set.py (+54/-0) |
To merge this branch: | bzr merge lp:~stevenk/launchpad/bpb-currentcomponent-assertion-part-4 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gavin Panella (community) | Approve | ||
Review via email: mp+46535@code.launchpad.net |
Commit message
Description of the change
Building on https:/
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/lp/soyuz/doc/binarypackagebuild.txt' |
2 | --- lib/lp/soyuz/doc/binarypackagebuild.txt 2011-01-20 20:49:14 +0000 |
3 | +++ lib/lp/soyuz/doc/binarypackagebuild.txt 2011-01-20 20:49:32 +0000 |
4 | @@ -42,26 +42,6 @@ |
5 | |
6 | >>> login(ANONYMOUS) |
7 | |
8 | -Partner archive builds are an exception to this rule; they can be retried |
9 | -in the release pocket for a released distro. Let's turn build 9 into a |
10 | -partner archive build: |
11 | - |
12 | - >>> partner_archive = ubuntu.getArchiveByComponent('partner') |
13 | - >>> removeSecurityProxy(failed_build).archive = partner_archive |
14 | - |
15 | -The build can now be re-tried: |
16 | - |
17 | - >>> failed_build.can_be_retried |
18 | - True |
19 | - |
20 | -Similarly to PPA builds, they can be retried for release pockets since |
21 | -they will happen in another archive. |
22 | - |
23 | - >>> removeSecurityProxy(failed_build).archive = cprov.archive |
24 | - |
25 | - >>> failed_build.can_be_retried |
26 | - True |
27 | - |
28 | storeUploadLog() refuses to override any previously stored |
29 | 'upload_log'. |
30 | |
31 | @@ -92,251 +72,6 @@ |
32 | >>> print failedtoupload_build.upload_log.filename |
33 | upload_22_log.txt |
34 | |
35 | - |
36 | -== Updating build-dependencies line == |
37 | - |
38 | -The IBinaryPackageBuild.dependencies field is only filled when a build |
39 | -job is collected as MANUALDEPWAIT, its content is informed by the |
40 | -buildd-slave in the apt-dependencies format. |
41 | - |
42 | - >>> depwait_build = getUtility(IBinaryPackageBuildSet).getByBuildID(12) |
43 | - >>> print depwait_build.dependencies |
44 | - cpp (>= 4:4.0.1-3), gcc-4.0 (>= 4.0.1-2) |
45 | - |
46 | -IBinaryPackageBuild.updateDependencies is designed to process this field |
47 | -and eliminate dependencies that can be satisfied. It is used as part of |
48 | -the auto-depwait processing where all builds marked as MANUALDEPWAIT are |
49 | -re-processed and the ones with empty dependencies are re-queued. |
50 | - |
51 | -If nothing has changed, which is the case of the current |
52 | -depwait_build, the 'dependencies' field remains the same. |
53 | - |
54 | - >>> old_dep = depwait_build.dependencies |
55 | - >>> depwait_build.updateDependencies() |
56 | - >>> depwait_build.dependencies == old_dep |
57 | - True |
58 | - |
59 | -A dependency can only be used if it is an a component allowed in our |
60 | -context (see above on 'Ogre' components). If we do a build using a |
61 | -dependency available in the sample data but published in an unreachable |
62 | -component, we will see that the dependency is considered to be unsatisfied. |
63 | -See also bug 177827. |
64 | - |
65 | - >>> login('foo.bar@canonical.com') |
66 | - >>> depwait_build.dependencies = u'pmount' |
67 | - >>> flush_database_updates() |
68 | - |
69 | -'pmount' in hoary/i386 is published in the 'universe' component: |
70 | - |
71 | - >>> hoary_i386 = depwait_build.distro_arch_series |
72 | - >>> pmount_pub = hoary_i386[ |
73 | - ... 'pmount'].currentrelease.current_publishing_record |
74 | - >>> print pmount_pub.component.name |
75 | - universe |
76 | - |
77 | -The build is only allowed to depend on packages published in 'main': |
78 | - |
79 | - >>> print depwait_build.current_component.name |
80 | - main |
81 | - |
82 | - >>> from lp.soyuz.adapters.archivedependencies import ( |
83 | - ... get_components_for_context) |
84 | - >>> print get_components_for_context( |
85 | - ... depwait_build.current_component, depwait_build.pocket) |
86 | - ['main'] |
87 | - |
88 | -Thus the 'pmount' dependency remains unsatisfied. |
89 | - |
90 | - >>> depwait_build.updateDependencies() |
91 | - >>> print depwait_build.dependencies |
92 | - pmount |
93 | - |
94 | -If we make pmount in hoary/i386 reachable, by moving it to the 'main' |
95 | -component, we can see that it will be excluded from the dependencies |
96 | -list. |
97 | - |
98 | - >>> login('foo.bar@canonical.com') |
99 | - >>> from lp.soyuz.interfaces.component import IComponentSet |
100 | - >>> main_component = getUtility(IComponentSet)['main'] |
101 | - >>> pmount_pub = hoary_i386[ |
102 | - ... 'pmount'].currentrelease.current_publishing_record |
103 | - >>> pmount_pub.component = main_component |
104 | - >>> depwait_build.dependencies = u'mozilla-firefox, pmount' |
105 | - >>> from canonical.database.sqlbase import flush_database_caches |
106 | - >>> flush_database_caches() |
107 | - >>> transaction.commit() |
108 | - >>> login(ANONYMOUS) |
109 | - |
110 | - >>> flush_database_updates() |
111 | - |
112 | -Note that only the satisfied dependencies are removed the build |
113 | -dependency list. |
114 | - |
115 | - >>> depwait_build.updateDependencies() |
116 | - >>> print depwait_build.dependencies |
117 | - mozilla-firefox |
118 | - |
119 | -'pmount' dependency is also satisfied in the Celso's PPA context, |
120 | -even when it is published in a component not allowed in its current |
121 | -component domain ('ogre_components'). That's because PPAs implicitly |
122 | -depend on all components of its distribution PRIMARY archive. |
123 | - |
124 | - |
125 | - >>> login('foo.bar@canonical.com') |
126 | - >>> depwait_build.dependencies = u'biscuit, pmount' |
127 | - >>> universe_component = getUtility(IComponentSet)['universe'] |
128 | - >>> pmount_pub.component = universe_component |
129 | - >>> removeSecurityProxy(depwait_build).archive = cprov.archive |
130 | - >>> flush_database_caches() |
131 | - >>> login(ANONYMOUS) |
132 | - |
133 | - >>> print get_components_for_context( |
134 | - ... depwait_build.current_component, depwait_build.pocket) |
135 | - ['main'] |
136 | - |
137 | - >>> print pmount_pub.component.name |
138 | - universe |
139 | - |
140 | - >>> depwait_build.updateDependencies() |
141 | - >>> print depwait_build.dependencies |
142 | - biscuit |
143 | - |
144 | -Restore depwait_build previous state. |
145 | - |
146 | - >>> login('foo.bar@canonical.com') |
147 | - >>> pmount_pub.component = main_component |
148 | - >>> removeSecurityProxy(depwait_build).archive = ubuntu.main_archive |
149 | - >>> flush_database_caches() |
150 | - >>> login(ANONYMOUS) |
151 | - |
152 | -=== Retrying DEPWAIT builds === |
153 | - |
154 | -It depends on the callsite to decide whether or not to 'retry' a |
155 | -build after calling updateDependencies, to encapsulate such decision |
156 | -for performing the mentioned auto-depwait procedure we have a utility |
157 | -in IBinaryPackageBuildSet called retryDepWaiting(). |
158 | - |
159 | - >>> print depwait_build.status.name |
160 | - MANUALDEPWAIT |
161 | - >>> print depwait_build.distro_arch_series.title |
162 | - The Hoary Hedgehog Release for i386 (x86) |
163 | - |
164 | -In order to allow depwait_build to be retried we will forge a |
165 | -'fully-satisfiable' dependencies field. |
166 | - |
167 | - >>> login('foo.bar@canonical.com') |
168 | - >>> depwait_build.dependencies = u'pmount' |
169 | - >>> login(ANONYMOUS) |
170 | - >>> flush_database_updates() |
171 | - |
172 | -Then we can run the utility method for the target distroarchseries and |
173 | -expect depwait_build to be 'retried' and scored. |
174 | - |
175 | - >>> getUtility(IBinaryPackageBuildSet).retryDepWaiting(hoaryi386) |
176 | - |
177 | - >>> print depwait_build.status.name |
178 | - NEEDSBUILD |
179 | - |
180 | - >>> depwait_build.buildqueue_record.lastscore |
181 | - 2505 |
182 | - |
183 | -The 'retryDepWaiting' task is performed periodically via cronjob by |
184 | -cronscript/buildd-retry-depwait.py. It can be run in parallel with |
185 | -other buildd tasks because the procedure is 'atomic' enough, i.e., |
186 | -after the commit the retried jobs are ready to be dispatched. |
187 | - |
188 | - |
189 | -== Build rescoring == |
190 | - |
191 | -Some builds can be rescored, to determine if it's possible check the |
192 | -can_be_rescored property: |
193 | - |
194 | - >>> depwait_build.can_be_rescored |
195 | - True |
196 | - |
197 | -We need to be at least a buildd-admin to rescore: |
198 | - |
199 | - >>> depwait_build.rescore(1000) |
200 | - Traceback (most recent call last): |
201 | - ... |
202 | - Unauthorized:... |
203 | - |
204 | - >>> login('celso.providelo@canonical.com') |
205 | - >>> depwait_build.rescore(1000) |
206 | - >>> print depwait_build.buildqueue_record.lastscore |
207 | - 1000 |
208 | - |
209 | -If a callsite tries to rescore a build that is not in the NEEDSBUILD state, |
210 | -a CannotBeRescored exception is raised. |
211 | - |
212 | - >>> depwait_build.status = BuildStatus.FAILEDTOUPLOAD |
213 | - >>> depwait_build.rescore(1000) |
214 | - Traceback (most recent call last): |
215 | - ... |
216 | - CannotBeRescored: Build cannot be rescored. |
217 | - |
218 | - >>> login(ANONYMOUS) |
219 | - |
220 | - |
221 | -== Build record security == |
222 | - |
223 | -IBinaryPackageBuild's content class is wrapped in a Zope security |
224 | -wrapper that prevents access to private builds for unauthorised users. |
225 | - |
226 | -Accessing the cprov builds when logged in as admin will see the records: |
227 | - |
228 | - >>> login('admin@canonical.com') |
229 | - >>> bob_builds = bob.getBuildRecords(user=admin) |
230 | - >>> print_build_details(bob_builds) |
231 | - cprov: i386 build of privacycheck 666 in ubuntutest breezy-autotest... |
232 | - ubuntu-team: hppa build of mozilla-firefox 0.9 in ubuntu warty RELEASE |
233 | - cprov: hppa build of mozilla-firefox 0.9 in ubuntu warty RELEASE |
234 | - cprov: i386 build of pmount 0.1-1 in ubuntu warty RELEASE |
235 | - cprov: i386 build of cdrkit 1.0 in ubuntu breezy-autotest RELEASE |
236 | - ... |
237 | - |
238 | -Likewise when logged in as cprov: |
239 | - |
240 | - >>> login('celso.providelo@canonical.com') |
241 | - >>> bob_builds = bob.getBuildRecords(user=admin) |
242 | - >>> print_build_details(bob_builds) |
243 | - cprov: i386 build of privacycheck 666 in ubuntutest breezy-autotest... |
244 | - ubuntu-team: hppa build of mozilla-firefox 0.9 in ubuntu warty RELEASE |
245 | - cprov: hppa build of mozilla-firefox 0.9 in ubuntu warty RELEASE |
246 | - cprov: i386 build of pmount 0.1-1 in ubuntu warty RELEASE |
247 | - cprov: i386 build of cdrkit 1.0 in ubuntu breezy-autotest RELEASE |
248 | - ... |
249 | - |
250 | -A user who is a buildd admin is not allowed to see the build records for |
251 | -private builds. Even though they are admin, privacy must be maintained. |
252 | - |
253 | - >>> login(buildd_admin.preferredemail.email) |
254 | - >>> bob_builds = bob.getBuildRecords(user=admin) |
255 | - |
256 | -Define a helper function to catch the security exception: |
257 | - |
258 | - >>> from zope.security.interfaces import Unauthorized |
259 | - >>> def print_builds_with_exception(builds): |
260 | - ... try: |
261 | - ... print_build_details(bob_builds) |
262 | - ... except Unauthorized: |
263 | - ... print "Generated Unauthorized exception as expected" |
264 | - ... else: |
265 | - ... print "FAIL: should raise Unauthorized exception" |
266 | - |
267 | -And try to access the builds: |
268 | - |
269 | - >>> print_builds_with_exception(bob_builds) |
270 | - Generated Unauthorized exception as expected |
271 | - |
272 | -When logged in as anonymous this will generate a securtity exception |
273 | -when accessing the builds: |
274 | - |
275 | - >>> login(ANONYMOUS) |
276 | - >>> bob_builds = bob.getBuildRecords(user=admin) |
277 | - >>> print_builds_with_exception(bob_builds) |
278 | - Generated Unauthorized exception as expected |
279 | - |
280 | There are other settable attributes declared in the zcml that require |
281 | launchpad.Edit. |
282 | |
283 | @@ -479,162 +214,3 @@ |
284 | >>> bq.estimated_duration |
285 | datetime.timedelta(0, 3600) |
286 | |
287 | - |
288 | -== IBinaryPackageBuildSet.getBuildsBySourcePackageRelease() == |
289 | - |
290 | -getBuildsBySourcePackageRelease() will return all the Build records for |
291 | -all the SourcePackageRelease IDs passed. |
292 | - |
293 | -Create some sources with builds: |
294 | - |
295 | - >>> source_one = test_publisher.getPubSource( |
296 | - ... status=PackagePublishingStatus.PUBLISHED, |
297 | - ... sourcename='sourceone') |
298 | - >>> source_two = test_publisher.getPubSource( |
299 | - ... status=PackagePublishingStatus.PUBLISHED, |
300 | - ... sourcename='sourcetwo') |
301 | - >>> build_one = source_one.sourcepackagerelease.createBuild( |
302 | - ... test_publisher.breezy_autotest_hppa, |
303 | - ... PackagePublishingPocket.RELEASE, |
304 | - ... test_publisher.breezy_autotest.main_archive, |
305 | - ... status=BuildStatus.FULLYBUILT) |
306 | - >>> build_two = source_two.sourcepackagerelease.createBuild( |
307 | - ... test_publisher.breezy_autotest_hppa, |
308 | - ... PackagePublishingPocket.RELEASE, |
309 | - ... test_publisher.breezy_autotest.main_archive, |
310 | - ... status=BuildStatus.NEEDSBUILD) |
311 | - |
312 | - >>> source_ids = ( |
313 | - ... source_one.sourcepackagerelease.id, |
314 | - ... source_two.sourcepackagerelease.id, |
315 | - ... ) |
316 | - >>> builds = removeSecurityProxy(bs).getBuildsBySourcePackageRelease( |
317 | - ... source_ids) |
318 | - >>> import operator |
319 | - >>> for build in sorted(builds, key=operator.attrgetter("id")): |
320 | - ... print build.title, build.status.name |
321 | - hppa build of sourceone 666 in ubuntutest breezy-autotest RELEASE |
322 | - FULLYBUILT |
323 | - hppa build of sourcetwo 666 in ubuntutest breezy-autotest RELEASE |
324 | - NEEDSBUILD |
325 | - |
326 | -The results can also be filtered on build state: |
327 | - |
328 | - >>> builds = removeSecurityProxy(bs).getBuildsBySourcePackageRelease( |
329 | - ... source_ids, buildstate=BuildStatus.FULLYBUILT) |
330 | - >>> for build in sorted(builds, key=operator.attrgetter("id")): |
331 | - ... print build.title, build.status.name |
332 | - hppa build of sourceone 666 in ubuntutest breezy-autotest RELEASE |
333 | - FULLYBUILT |
334 | - |
335 | -If there are no matching results then it returns an empty SelectResults. |
336 | - |
337 | - >>> builds = removeSecurityProxy(bs).getBuildsBySourcePackageRelease( |
338 | - ... source_ids, buildstate=BuildStatus.CHROOTWAIT) |
339 | - >>> print builds.count() |
340 | - 0 |
341 | - |
342 | -Supplying an empty list or None for the IDs results in an empty list |
343 | -being returned. |
344 | - |
345 | - >>> removeSecurityProxy(bs).getBuildsBySourcePackageRelease(None) |
346 | - [] |
347 | - |
348 | - >>> removeSecurityProxy(bs).getBuildsBySourcePackageRelease([]) |
349 | - [] |
350 | - |
351 | - |
352 | -== Getting the build records for a particular builder == |
353 | - |
354 | -The getBuildsForBuilder method returns all the builds for the |
355 | -specified builder ID, ordered from most-recently built. |
356 | - |
357 | - Create some source packages with which to test the |
358 | - getBuildsForBuilder method: |
359 | - |
360 | - >>> src_pkg_earlier = test_publisher.getPubSource( |
361 | - ... status=PackagePublishingStatus.PUBLISHED, |
362 | - ... sourcename='earlierbuildsrc', architecturehintlist='hppa i386') |
363 | - >>> src_pkg_later = test_publisher.getPubSource( |
364 | - ... status=PackagePublishingStatus.PUBLISHED, |
365 | - ... sourcename='laterbuildsrc', |
366 | - ... architecturehintlist='hppa i386') |
367 | - |
368 | - Create the builds based on the source packages, with the builds |
369 | - for 'earlierbuildsrc' built one day before the 'laterbuildsrc': |
370 | - |
371 | - >>> frog_builder = getUtility(IBuilderSet)['frog'] |
372 | - >>> bob_builder = getUtility(IBuilderSet)['bob'] |
373 | - |
374 | - >>> earlier_builds = src_pkg_earlier.createMissingBuilds() |
375 | - >>> eg_build_date = earlier_builds[0].date_created |
376 | - >>> for build in earlier_builds: |
377 | - ... build.date_started = eg_build_date - timedelta(1) |
378 | - ... build.date_finished = eg_build_date - timedelta(1) |
379 | - |
380 | - >>> later_builds = src_pkg_later.createMissingBuilds() |
381 | - >>> for build in later_builds: |
382 | - ... build.date_started = eg_build_date |
383 | - ... build.date_finished = eg_build_date |
384 | - |
385 | - Ensure that the i386 builds are created by the 'frog' builder, |
386 | - while the hppa builds are created by 'bob' the builder: |
387 | - |
388 | - >>> builds = earlier_builds + later_builds |
389 | - >>> for build in builds: |
390 | - ... if build.processor.name == u'386': |
391 | - ... build.builder = frog_builder |
392 | - ... else: |
393 | - ... build.builder = bob_builder |
394 | - |
395 | - A call to getBuildsForBuilder returns only those builds that were |
396 | - built by the specified builder, ordered by datebuilt DESC: |
397 | - |
398 | - >>> frog_builds = getUtility(IBinaryPackageBuildSet).getBuildsForBuilder( |
399 | - ... frog_builder.id) |
400 | - >>> print_build_details(frog_builds) |
401 | - ubuntu-team: i386 build of laterbuildsrc 666 in ubuntutest |
402 | - breezy-autotest RELEASE |
403 | - ubuntu-team: i386 build of earlierbuildsrc 666 in ubuntutest |
404 | - breezy-autotest RELEASE |
405 | - |
406 | - |
407 | -== Source publication for builds == |
408 | - |
409 | -The current source publication for a given build is available via |
410 | -its 'current_source_publication' property. |
411 | - |
412 | -We will create a new publication and its corresponding build. |
413 | - |
414 | - >>> original_pub = test_publisher.getPubSource() |
415 | - >>> [build] = original_pub.createMissingBuilds() |
416 | - |
417 | -The publication returned by 'current_source_publication' is the one |
418 | -that originated the build. |
419 | - |
420 | - >>> build.current_source_publication == original_pub |
421 | - True |
422 | - |
423 | -We will override the source publication, moving it from 'main' |
424 | -component (default) to 'universe'. |
425 | - |
426 | - >>> universe_component = getUtility(IComponentSet)['universe'] |
427 | - >>> secure_overridden_pub = original_pub.changeOverride( |
428 | - ... new_component=universe_component) |
429 | - |
430 | -Fetching the corresponding `SourcePackagePublishingHistory` for the |
431 | -comparisons. |
432 | - |
433 | - >>> from lp.soyuz.model.publishing import ( |
434 | - ... SourcePackagePublishingHistory) |
435 | - >>> overridden_pub = SourcePackagePublishingHistory.get( |
436 | - ... secure_overridden_pub.id) |
437 | - |
438 | -An we can see that the build 'current_source_publication' now points |
439 | -to the most recent publication, the overridden one. |
440 | - |
441 | - >>> original_pub == build.current_source_publication |
442 | - False |
443 | - |
444 | - >>> overridden_pub == build.current_source_publication |
445 | - True |
446 | |
447 | === modified file 'lib/lp/soyuz/tests/test_build.py' |
448 | --- lib/lp/soyuz/tests/test_build.py 2011-01-20 20:49:14 +0000 |
449 | +++ lib/lp/soyuz/tests/test_build.py 2011-01-20 20:49:32 +0000 |
450 | @@ -8,6 +8,7 @@ |
451 | timedelta, |
452 | ) |
453 | import pytz |
454 | +import transaction |
455 | from zope.component import getUtility |
456 | from zope.security.proxy import removeSecurityProxy |
457 | |
458 | @@ -17,10 +18,13 @@ |
459 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
460 | from lp.registry.interfaces.series import SeriesStatus |
461 | from lp.soyuz.enums import ( |
462 | + ArchivePurpose, |
463 | BinaryPackageFormat, |
464 | PackagePublishingPriority, |
465 | PackageUploadStatus, |
466 | ) |
467 | +from lp.soyuz.interfaces.binarypackagebuild import CannotBeRescored |
468 | +from lp.soyuz.interfaces.component import IComponentSet |
469 | from lp.soyuz.interfaces.publishing import PackagePublishingStatus |
470 | from lp.soyuz.tests.test_publishing import SoyuzTestPublisher |
471 | from lp.testing import ( |
472 | @@ -75,7 +79,7 @@ |
473 | [build] = spph.createMissingBuilds() |
474 | self.assertEquals(self.distroseries.main_archive, build.archive) |
475 | self.assertEquals(self.distroseries.distribution, build.distribution) |
476 | - self.assertEquals(self.distroseries, build.distroseries) |
477 | + self.assertEquals(self.distroseries, build.distro_series) |
478 | self.assertEquals(self.das, build.distro_arch_series) |
479 | self.assertEquals(PackagePublishingPocket.RELEASE, build.pocket) |
480 | self.assertEquals(self.das.architecturetag, build.arch_tag) |
481 | @@ -160,6 +164,29 @@ |
482 | [build] = spph.createMissingBuilds() |
483 | self.assertFalse(build.can_be_retried) |
484 | |
485 | + def test_partner_retry_for_released_series(self): |
486 | + # Builds for PARTNER can be retried -- even if the distroseries is |
487 | + # released. |
488 | + distroseries = self.factory.makeDistroSeries() |
489 | + das = self.factory.makeDistroArchSeries( |
490 | + distroseries=distroseries, processorfamily=self.pf, |
491 | + supports_virtualized=True) |
492 | + archive = self.factory.makeArchive( |
493 | + purpose=ArchivePurpose.PARTNER, |
494 | + distribution=distroseries.distribution) |
495 | + with person_logged_in(self.admin): |
496 | + distroseries.nominatedarchindep = das |
497 | + distroseries.status = SeriesStatus.OBSOLETE |
498 | + self.publisher.addFakeChroots(distroseries=distroseries) |
499 | + spph = self.publisher.getPubSource( |
500 | + sourcename=self.factory.getUniqueString(), |
501 | + version="%s.1" % self.factory.getUniqueInteger(), |
502 | + distroseries=distroseries, archive=archive) |
503 | + [build] = spph.createMissingBuilds() |
504 | + with person_logged_in(self.admin): |
505 | + build.status = BuildStatus.FAILEDTOBUILD |
506 | + self.assertTrue(build.can_be_retried) |
507 | + |
508 | def test_retry(self): |
509 | # A build can be retried |
510 | spph = self.publisher.getPubSource( |
511 | @@ -189,7 +216,7 @@ |
512 | spph.source_package_version, build.id)) |
513 | expected_url = '%s/%s' % (url_start, expected_filename) |
514 | self.assertEquals(expected_url, build.upload_log_url) |
515 | - |
516 | + |
517 | def test_retry_does_not_modify_first_dispatch(self): |
518 | # Retrying a build does not modify the first dispatch time of the |
519 | # build |
520 | @@ -253,3 +280,43 @@ |
521 | # Verify .binarypackages returns sorted by name |
522 | expected_names.sort() |
523 | self.assertEquals(expected_names, bin_names) |
524 | + |
525 | + def test_cannot_rescore_non_needsbuilds_builds(self): |
526 | + # If a build record isn't in NEEDSBUILD, it can not be rescored. |
527 | + # We will also need to log into an admin to do the rescore. |
528 | + with person_logged_in(self.admin): |
529 | + [bpph] = self.publisher.getPubBinaries( |
530 | + binaryname=self.factory.getUniqueString(), |
531 | + version="%s.1" % self.factory.getUniqueInteger(), |
532 | + distroseries=self.distroseries) |
533 | + build = bpph.binarypackagerelease.build |
534 | + self.assertRaises(CannotBeRescored, build.rescore, 20) |
535 | + |
536 | + def test_rescore_builds(self): |
537 | + # If the user has build-admin privileges, they can rescore builds |
538 | + spph = self.publisher.getPubSource( |
539 | + sourcename=self.factory.getUniqueString(), |
540 | + version="%s.1" % self.factory.getUniqueInteger(), |
541 | + distroseries=self.distroseries) |
542 | + [build] = spph.createMissingBuilds() |
543 | + self.assertEquals(BuildStatus.NEEDSBUILD, build.status) |
544 | + self.assertEquals(2505, build.buildqueue_record.lastscore) |
545 | + with person_logged_in(self.admin): |
546 | + build.rescore(5000) |
547 | + transaction.commit() |
548 | + self.assertEquals(5000, build.buildqueue_record.lastscore) |
549 | + |
550 | + def test_source_publication_override(self): |
551 | + # Components can be overridden in builds. |
552 | + spph = self.publisher.getPubSource( |
553 | + sourcename=self.factory.getUniqueString(), |
554 | + version="%s.1" % self.factory.getUniqueInteger(), |
555 | + distroseries=self.distroseries) |
556 | + [build] = spph.createMissingBuilds() |
557 | + self.assertEquals(spph, build.current_source_publication) |
558 | + universe = getUtility(IComponentSet)['universe'] |
559 | + overridden_spph = spph.changeOverride(new_component=universe) |
560 | + # We can now see current source publication points to the overridden |
561 | + # publication. |
562 | + self.assertNotEquals(spph, build.current_source_publication) |
563 | + self.assertEquals(overridden_spph, build.current_source_publication) |
564 | |
565 | === added file 'lib/lp/soyuz/tests/test_build_depwait.py' |
566 | --- lib/lp/soyuz/tests/test_build_depwait.py 1970-01-01 00:00:00 +0000 |
567 | +++ lib/lp/soyuz/tests/test_build_depwait.py 2011-01-20 20:49:32 +0000 |
568 | @@ -0,0 +1,120 @@ |
569 | +# Copyright 2011 Canonical Ltd. This software is licensed under the |
570 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
571 | + |
572 | +__metaclass__ = type |
573 | + |
574 | +import transaction |
575 | +from zope.component import getUtility |
576 | + |
577 | +from canonical.testing.layers import LaunchpadFunctionalLayer |
578 | +from lp.buildmaster.enums import BuildStatus |
579 | +from lp.registry.interfaces.person import IPersonSet |
580 | +from lp.soyuz.enums import ( |
581 | + ArchivePurpose, |
582 | + PackagePublishingStatus, |
583 | + ) |
584 | +from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet |
585 | +from lp.soyuz.interfaces.component import IComponentSet |
586 | +from lp.soyuz.tests.test_publishing import SoyuzTestPublisher |
587 | +from lp.testing import ( |
588 | + person_logged_in, |
589 | + TestCaseWithFactory, |
590 | + ) |
591 | +from lp.testing.sampledata import ADMIN_EMAIL |
592 | + |
593 | + |
594 | +class TestBuildDepWait(TestCaseWithFactory): |
595 | + |
596 | + layer = LaunchpadFunctionalLayer |
597 | + |
598 | + def setUp(self): |
599 | + super(TestBuildDepWait, self).setUp() |
600 | + self.admin = getUtility(IPersonSet).getByEmail(ADMIN_EMAIL) |
601 | + # Create everything we need to create builds, such as a |
602 | + # DistroArchSeries and a builder. |
603 | + self.pf = self.factory.makeProcessorFamily() |
604 | + pf_proc = self.pf.addProcessor(self.factory.getUniqueString(), '', '') |
605 | + self.distroseries = self.factory.makeDistroSeries() |
606 | + self.das = self.factory.makeDistroArchSeries( |
607 | + distroseries=self.distroseries, processorfamily=self.pf, |
608 | + supports_virtualized=True) |
609 | + self.archive = self.factory.makeArchive( |
610 | + distribution=self.distroseries.distribution, |
611 | + purpose=ArchivePurpose.PRIMARY) |
612 | + with person_logged_in(self.admin): |
613 | + self.publisher = SoyuzTestPublisher() |
614 | + self.publisher.prepareBreezyAutotest() |
615 | + self.distroseries.nominatedarchindep = self.das |
616 | + self.publisher.addFakeChroots(distroseries=self.distroseries) |
617 | + self.builder = self.factory.makeBuilder(processor=pf_proc) |
618 | + |
619 | + def test_update_dependancies(self): |
620 | + # Calling .updateDependencies() on a build will remove those which |
621 | + # are reachable. |
622 | + spph = self.publisher.getPubSource( |
623 | + sourcename=self.factory.getUniqueString(), |
624 | + version="%s.1" % self.factory.getUniqueInteger(), |
625 | + distroseries=self.distroseries, archive=self.archive) |
626 | + [build] = spph.createMissingBuilds() |
627 | + spn = self.factory.getUniqueString() |
628 | + version = "%s.1" % self.factory.getUniqueInteger() |
629 | + with person_logged_in(self.admin): |
630 | + build.status = BuildStatus.MANUALDEPWAIT |
631 | + build.dependencies = unicode(spn) |
632 | + [bpph] = self.publisher.getPubBinaries( |
633 | + binaryname=spn, distroseries=self.distroseries, |
634 | + version=version, builder=self.builder, archive=self.archive, |
635 | + status=PackagePublishingStatus.PUBLISHED) |
636 | + # Commit to make sure stuff hits the database. |
637 | + transaction.commit() |
638 | + build.updateDependencies() |
639 | + self.assertEquals(u'', build.dependencies) |
640 | + |
641 | + def test_update_dependancies_respects_component(self): |
642 | + # Since main can only utilise packages that are published in main, |
643 | + # dependencies are not satisfied if they are not in main. |
644 | + spph = self.publisher.getPubSource( |
645 | + sourcename=self.factory.getUniqueString(), |
646 | + version="%s.1" % self.factory.getUniqueInteger(), |
647 | + distroseries=self.distroseries, archive=self.archive) |
648 | + [build] = spph.createMissingBuilds() |
649 | + spn = self.factory.getUniqueString() |
650 | + version = "%s.1" % self.factory.getUniqueInteger() |
651 | + with person_logged_in(self.admin): |
652 | + build.status = BuildStatus.MANUALDEPWAIT |
653 | + build.dependencies = unicode(spn) |
654 | + [bpph] = self.publisher.getPubBinaries( |
655 | + binaryname=spn, distroseries=self.distroseries, |
656 | + version=version, builder=self.builder, archive=self.archive, |
657 | + status=PackagePublishingStatus.PUBLISHED, |
658 | + component='universe') |
659 | + # Commit to make sure stuff hits the database. |
660 | + transaction.commit() |
661 | + build.updateDependencies() |
662 | + # Since the dependency is in universe, we still can't see it. |
663 | + self.assertEquals(unicode(spn), build.dependencies) |
664 | + with person_logged_in(self.admin): |
665 | + bpph.component = getUtility(IComponentSet)['main'] |
666 | + transaction.commit() |
667 | + # Now that we have moved it main, we can see it. |
668 | + build.updateDependencies() |
669 | + self.assertEquals(u'', build.dependencies) |
670 | + |
671 | + def test_retry_dep_waiting(self): |
672 | + # Builds in MANUALDEPWAIT can be automatically retried. |
673 | + spph = self.publisher.getPubSource( |
674 | + sourcename=self.factory.getUniqueString(), |
675 | + version="%s.1" % self.factory.getUniqueInteger(), |
676 | + distroseries=self.distroseries, archive=self.archive) |
677 | + [build] = spph.createMissingBuilds() |
678 | + with person_logged_in(self.admin): |
679 | + build.status = BuildStatus.MANUALDEPWAIT |
680 | + # .createMissingBuilds() queues the build for us, and we need to |
681 | + # undo that if we're about to retry it. |
682 | + build.buildqueue_record.destroySelf() |
683 | + build.dependencies = u'' |
684 | + # Commit to make sure stuff hits the database. |
685 | + transaction.commit() |
686 | + getUtility(IBinaryPackageBuildSet).retryDepWaiting(self.das) |
687 | + self.assertEquals(BuildStatus.NEEDSBUILD, build.status) |
688 | + self.assertTrue(build.buildqueue_record.lastscore > 0) |
689 | |
690 | === added file 'lib/lp/soyuz/tests/test_build_privacy.py' |
691 | --- lib/lp/soyuz/tests/test_build_privacy.py 1970-01-01 00:00:00 +0000 |
692 | +++ lib/lp/soyuz/tests/test_build_privacy.py 2011-01-20 20:49:32 +0000 |
693 | @@ -0,0 +1,87 @@ |
694 | +# Copyright 2011 Canonical Ltd. This software is licensed under the |
695 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
696 | + |
697 | +__metaclass__ = type |
698 | + |
699 | +from zope.component import getUtility |
700 | +from zope.security.interfaces import Unauthorized |
701 | + |
702 | +from canonical.testing.layers import LaunchpadFunctionalLayer |
703 | +from lp.registry.interfaces.person import IPersonSet |
704 | +from lp.soyuz.interfaces.archive import IArchiveSet |
705 | +from lp.soyuz.tests.test_publishing import SoyuzTestPublisher |
706 | +from lp.testing import ( |
707 | + person_logged_in, |
708 | + TestCaseWithFactory, |
709 | + ) |
710 | +from lp.testing.sampledata import ADMIN_EMAIL |
711 | + |
712 | + |
713 | +class TestBuildPrivacy(TestCaseWithFactory): |
714 | + |
715 | + layer = LaunchpadFunctionalLayer |
716 | + |
717 | + def setUp(self): |
718 | + super(TestBuildPrivacy, self).setUp() |
719 | + # Add everything we need to create builds. |
720 | + self.admin = getUtility(IPersonSet).getByEmail(ADMIN_EMAIL) |
721 | + pf = self.factory.makeProcessorFamily() |
722 | + pf_proc = pf.addProcessor(self.factory.getUniqueString(), '', '') |
723 | + distroseries = self.factory.makeDistroSeries() |
724 | + das = self.factory.makeDistroArchSeries( |
725 | + distroseries=distroseries, processorfamily=pf, |
726 | + supports_virtualized=True) |
727 | + with person_logged_in(self.admin): |
728 | + publisher = SoyuzTestPublisher() |
729 | + publisher.prepareBreezyAutotest() |
730 | + distroseries.nominatedarchindep = das |
731 | + publisher.addFakeChroots(distroseries=distroseries) |
732 | + self.factory.makeBuilder(processor=pf_proc) |
733 | + self.public_archive = self.factory.makeArchive() |
734 | + self.private_archive = self.factory.makeArchive(private=True) |
735 | + # Create one public and one private build. |
736 | + public_spph = publisher.getPubSource( |
737 | + sourcename=self.factory.getUniqueString(), |
738 | + version="%s.1" % self.factory.getUniqueInteger(), |
739 | + distroseries=distroseries, archive=self.public_archive) |
740 | + [public_build] = public_spph.createMissingBuilds() |
741 | + private_spph = publisher.getPubSource( |
742 | + sourcename=self.factory.getUniqueString(), |
743 | + version="%s.1" % self.factory.getUniqueInteger(), |
744 | + distroseries=distroseries, archive=self.private_archive) |
745 | + with person_logged_in(self.admin): |
746 | + [private_build] = private_spph.createMissingBuilds() |
747 | + self.expected_title = '%s build of %s %s in %s %s RELEASE' % ( |
748 | + das.architecturetag, private_spph.source_package_name, |
749 | + private_spph.source_package_version, |
750 | + distroseries.distribution.name, distroseries.name) |
751 | + |
752 | + def test_admin_can_see_private_builds(self): |
753 | + # Admin users can see all builds. |
754 | + with person_logged_in(self.admin): |
755 | + private = getUtility(IArchiveSet).get(self.private_archive.id) |
756 | + [build] = private.getBuildRecords() |
757 | + self.assertEquals(self.expected_title, build.title) |
758 | + |
759 | + def test_owner_can_see_own_private_builds(self): |
760 | + # The owner of the private archive is able to see all builds that |
761 | + # publish to that archive. |
762 | + with person_logged_in(self.private_archive.owner): |
763 | + private = getUtility(IArchiveSet).get(self.private_archive.id) |
764 | + [build] = private.getBuildRecords() |
765 | + self.assertEquals(self.expected_title, build.title) |
766 | + |
767 | + def test_buildd_admin_cannot_see_private_builds(self): |
768 | + # Admins that look after the builders ("buildd-admins"), can not see |
769 | + # private builds. |
770 | + buildd_admin = getUtility(IPersonSet).getByName( |
771 | + 'launchpad-buildd-admins') |
772 | + with person_logged_in(buildd_admin): |
773 | + private = getUtility(IArchiveSet).get(self.private_archive.id) |
774 | + self.assertRaises( |
775 | + Unauthorized, getattr, private, 'getBuildRecords') |
776 | + |
777 | + def test_anonymous_cannot_see_private_builds(self): |
778 | + # An anonymous user can't query the builds for the private archive. |
779 | + private = getUtility(IArchiveSet).get(self.private_archive.id) |
780 | + self.assertRaises(Unauthorized, getattr, private, 'getBuildRecords') |
781 | |
782 | === modified file 'lib/lp/soyuz/tests/test_build_set.py' |
783 | --- lib/lp/soyuz/tests/test_build_set.py 2011-01-20 20:49:14 +0000 |
784 | +++ lib/lp/soyuz/tests/test_build_set.py 2011-01-20 20:49:32 +0000 |
785 | @@ -28,6 +28,7 @@ |
786 | ) |
787 | from lp.testing.sampledata import ADMIN_EMAIL |
788 | |
789 | + |
790 | class TestBuildSet(TestCaseWithFactory): |
791 | |
792 | layer = LaunchpadFunctionalLayer |
793 | @@ -61,6 +62,7 @@ |
794 | self.builder_one = self.factory.makeBuilder(processor=pf_proc_1) |
795 | self.builder_two = self.factory.makeBuilder(processor=pf_proc_2) |
796 | self.builds = [] |
797 | + self.spphs = [] |
798 | |
799 | def setUpBuilds(self): |
800 | for i in range(5): |
801 | @@ -69,6 +71,7 @@ |
802 | sourcename=self.factory.getUniqueString(), |
803 | version="%s.%s" % (self.factory.getUniqueInteger(), i), |
804 | distroseries=self.distroseries, architecturehintlist='any') |
805 | + self.spphs.append(spph) |
806 | builds = spph.createMissingBuilds() |
807 | with person_logged_in(self.admin): |
808 | for b in builds: |
809 | @@ -152,3 +155,54 @@ |
810 | rset = removeSecurityProxy( |
811 | getUtility(IBinaryPackageBuildSet))._prefetchBuildData(build_ids) |
812 | self.assertEquals(len(rset), 4) |
813 | + |
814 | + def test_get_builds_by_source_package_release(self): |
815 | + # We are able to return all of the builds for the source package |
816 | + # release ids passed in. |
817 | + self.setUpBuilds() |
818 | + spphs = self.spphs[:2] |
819 | + ids = [spph.sourcepackagerelease.id for spph in spphs] |
820 | + builds = getUtility( |
821 | + IBinaryPackageBuildSet).getBuildsBySourcePackageRelease(ids) |
822 | + expected_titles = [] |
823 | + for spph in spphs: |
824 | + for das in (self.das_one, self.das_two): |
825 | + expected_titles.append( |
826 | + '%s build of %s %s in %s %s RELEASE' % ( |
827 | + das.architecturetag, spph.source_package_name, |
828 | + spph.source_package_version, |
829 | + self.distroseries.distribution.name, |
830 | + self.distroseries.name)) |
831 | + build_titles = [build.title for build in builds] |
832 | + self.assertEquals(sorted(expected_titles), sorted(build_titles)) |
833 | + |
834 | + def test_get_builds_by_source_package_release_filtering(self): |
835 | + self.setUpBuilds() |
836 | + ids = [self.spphs[-1].sourcepackagerelease.id] |
837 | + builds = getUtility( |
838 | + IBinaryPackageBuildSet).getBuildsBySourcePackageRelease( |
839 | + ids, buildstate=BuildStatus.FAILEDTOBUILD) |
840 | + expected_titles = [] |
841 | + for das in (self.das_one, self.das_two): |
842 | + expected_titles.append( |
843 | + '%s build of %s %s in %s %s RELEASE' % ( |
844 | + das.architecturetag, self.spphs[-1].source_package_name, |
845 | + self.spphs[-1].source_package_version, |
846 | + self.distroseries.distribution.name, |
847 | + self.distroseries.name)) |
848 | + build_titles = [build.title for build in builds] |
849 | + self.assertEquals(sorted(expected_titles), sorted(build_titles)) |
850 | + builds = getUtility( |
851 | + IBinaryPackageBuildSet).getBuildsBySourcePackageRelease( |
852 | + ids, buildstate=BuildStatus.CHROOTWAIT) |
853 | + self.assertEquals([], list(builds)) |
854 | + |
855 | + def test_no_get_builds_by_source_package_release(self): |
856 | + # If no ids or None are passed into .getBuildsBySourcePackageRelease, |
857 | + # an empty list is returned. |
858 | + builds = getUtility( |
859 | + IBinaryPackageBuildSet).getBuildsBySourcePackageRelease(None) |
860 | + self.assertEquals([], builds) |
861 | + builds = getUtility( |
862 | + IBinaryPackageBuildSet).getBuildsBySourcePackageRelease([]) |
863 | + self.assertEquals([], builds) |
Hi Steven,
I have a few comments, but they're all minor, and you could land
without addressing any of them.
Awesome doctest killing!
Gavin.
[1]
+ # We will also need to log into an admin to do the rescore
Full-stop. Ho ho.
[2]
Some lint:
./lib/lp/ soyuz/doc/ binarypackagebu ild.txt soyuz/tests/ test_build. py soyuz/tests/ test_build_ depwait. py soyuz/tests/ test_build_ set.py
1: narrative uses a moin header.
48: source exceeds 78 characters.
113: narrative uses a moin header.
137: source exceeds 78 characters.
138: source exceeds 78 characters.
162: source exceeds 78 characters.
163: source exceeds 78 characters.
./lib/lp/
219: W291 trailing whitespace
219: Line has trailing whitespace.
./lib/lp/
25: E302 expected 2 blank lines, found 1
71: W291 trailing whitespace
71: Line has trailing whitespace.
88: Line exceeds 78 characters.
./lib/lp/
31: E302 expected 2 blank lines, found 1
192: W291 trailing whitespace
192: Line has trailing whitespace.
I guess it's not worth addressing the binarypackagebu ild.txt lint, but
please take a look at the others.
[3]
+ def test_update_ dependancies( self): cies() on a build will update will remove
+ # Calling .updateDependen
+ # those which are reachable.
"will update" or "will remove"?
[4]
+ # Commit to make sure stuff hits the database. commit( )
+ transaction.
Is this absolutely needed? I believe it slows down the test tear-down, updates( ) would be
but I could be wrong. Perhaps flush_database_
sufficient? If so, there are several commits in the new tests that
might be better as flushes instead.
[5]
+ self.assertTrue (build. buildqueue_ record. lastscore > 0)
In case of failure, this might be more informative if written as:
(LessThan is from testtools. matchers. )
But it's not very important; I mention it really to make you aware of
matchers in case they're interesting later on. There are some LP
specific matchers in lp.testing.matchers too.
[6]
In TestBuildPrivac y.setUp( ):
+ [public_build] = public_ spph.createMiss ingBuilds( ) logged_ in(self. admin): spph.createMiss ingBuilds( )
...
+ with person_
+ [private_build] = private_
Is the log-in when creating private_build necessary because it's
private?
[7]
+ def _get_build_ title(self, build):
+ return build.title
This doesn't seem to be very useful!
[8]
+ self.assertRaises(
+ Unauthorized, getattr, private, 'getBuildRecords')
This might read better with a lambda:
This appears twice.
[9]
+ expected_titles = [] titles. append( etag, spph.source_ package_ name, package_ version, es.distribution .name,
+ for spph in spphs:
+ for das in (self.das_one, self.das_two):
+ expected_
+ '%s build of %s %s in %s %s RELEASE' % (
+ das.architectur
+ spph.source_
+ self.distroseri
+ ...