Merge lp:~stevenk/launchpad/bpb-currentcomponent-assertion-part-4 into lp:launchpad

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
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+46535@code.launchpad.net

Description of the change

Building on https://code.launchpad.net/~stevenk/launchpad/bpb-currentcomponent-assertion-part-3/+merge/46209, this branch moves even more of binarypackagebuild.txt into unit tests.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :
Download full text (3.5 KiB)

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/binarypackagebuild.txt
       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/soyuz/tests/test_build.py
     219: W291 trailing whitespace
     219: Line has trailing whitespace.
./lib/lp/soyuz/tests/test_build_depwait.py
      25: E302 expected 2 blank lines, found 1
      71: W291 trailing whitespace
      71: Line has trailing whitespace.
      88: Line exceeds 78 characters.
./lib/lp/soyuz/tests/test_build_set.py
      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 binarypackagebuild.txt lint, but
please take a look at the others.

[3]

+ def test_update_dependancies(self):
+ # Calling .updateDependencies() on a build will update will remove
+ # those which are reachable.

"will update" or "will remove"?

[4]

+ # Commit to make sure stuff hits the database.
+ transaction.commit()

Is this absolutely needed? I believe it slows down the test tear-down,
but I could be wrong. Perhaps flush_database_updates() would be
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:

        self.assertThat(LessThan(0, build.buildqueue_record.lastscore))

(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 TestBuildPrivacy.setUp():

+ [public_build] = public_spph.createMissingBuilds()
...
+ with person_logged_in(self.admin):
+ [private_build] = private_spph.createMissingBuilds()

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:

            self.assertRaises(
                Unauthorized, lambda: private.getBuildRecords)

This appears twice.

[9]

+ expected_titles = []
+ for spph in spphs:
+ for das in (self.das_one, self.das_two):
+ expected_titles.append(
+ '%s build of %s %s in %s %s RELEASE' % (
+ das.architecturetag, spph.source_package_name,
+ spph.source_package_version,
+ self.distroseries.distribution.name,
+ ...

Read more...

review: Approve

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)