← Back to team overview

launchpad-reviewers team mailing list archive

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

 

Steve Kowalik has proposed merging lp:~stevenk/launchpad/bpb-currentcomponent-assertion-part-4 into lp:launchpad with lp:~stevenk/launchpad/bpb-currentcomponent-assertion-part-3 as a prerequisite.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~stevenk/launchpad/bpb-currentcomponent-assertion-part-4/+merge/46535

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.
-- 
https://code.launchpad.net/~stevenk/launchpad/bpb-currentcomponent-assertion-part-4/+merge/46535
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~stevenk/launchpad/bpb-currentcomponent-assertion-part-4 into lp:launchpad.
=== modified file 'lib/lp/soyuz/doc/binarypackagebuild.txt'
--- lib/lp/soyuz/doc/binarypackagebuild.txt	2011-01-17 21:32:16 +0000
+++ lib/lp/soyuz/doc/binarypackagebuild.txt	2011-01-17 21:32:17 +0000
@@ -42,26 +42,6 @@
 
     >>> login(ANONYMOUS)
 
-Partner archive builds are an exception to this rule; they can be retried
-in the release pocket for a released distro.  Let's turn build 9 into a
-partner archive build:
-
-    >>> partner_archive = ubuntu.getArchiveByComponent('partner')
-    >>> removeSecurityProxy(failed_build).archive = partner_archive
-
-The build can now be re-tried:
-
-    >>> failed_build.can_be_retried
-    True
-
-Similarly to PPA builds, they can be retried for release pockets since
-they will happen in another archive.
-
-    >>> removeSecurityProxy(failed_build).archive = cprov.archive
-
-    >>> failed_build.can_be_retried
-    True
-
 storeUploadLog() refuses to override any previously stored
 'upload_log'.
 
@@ -92,251 +72,6 @@
     >>> print failedtoupload_build.upload_log.filename
     upload_22_log.txt
 
-
-== Updating build-dependencies line ==
-
-The IBinaryPackageBuild.dependencies field is only filled when a build
-job is collected as MANUALDEPWAIT, its content is informed by the
-buildd-slave in the apt-dependencies format.
-
-    >>> depwait_build = getUtility(IBinaryPackageBuildSet).getByBuildID(12)
-    >>> print depwait_build.dependencies
-    cpp (>= 4:4.0.1-3), gcc-4.0 (>= 4.0.1-2)
-
-IBinaryPackageBuild.updateDependencies is designed to process this field
-and eliminate dependencies that can be satisfied. It is used as part of
-the auto-depwait processing where all builds marked as MANUALDEPWAIT are
-re-processed and the ones with empty dependencies are re-queued.
-
-If nothing has changed, which is the case of the current
-depwait_build, the 'dependencies' field remains the same.
-
-    >>> old_dep = depwait_build.dependencies
-    >>> depwait_build.updateDependencies()
-    >>> depwait_build.dependencies == old_dep
-    True
-
-A dependency can only be used if it is an a component allowed in our
-context (see above on 'Ogre' components). If we do a build using a
-dependency available in the sample data but published in an unreachable
-component, we will see that the dependency is considered to be unsatisfied.
-See also bug 177827.
-
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> depwait_build.dependencies = u'pmount'
-    >>> flush_database_updates()
-
-'pmount' in hoary/i386 is published in the 'universe' component:
-
-    >>> hoary_i386 = depwait_build.distro_arch_series
-    >>> pmount_pub = hoary_i386[
-    ...     'pmount'].currentrelease.current_publishing_record
-    >>> print pmount_pub.component.name
-    universe
-
-The build is only allowed to depend on packages published in 'main':
-
-    >>> print depwait_build.current_component.name
-    main
-
-    >>> from lp.soyuz.adapters.archivedependencies import (
-    ...     get_components_for_context)
-    >>> print get_components_for_context(
-    ...     depwait_build.current_component, depwait_build.pocket)
-    ['main']
-
-Thus the 'pmount' dependency remains unsatisfied.
-
-    >>> depwait_build.updateDependencies()
-    >>> print depwait_build.dependencies
-    pmount
-
-If we make pmount in hoary/i386 reachable, by moving it to the 'main'
-component, we can see that it will be excluded from the dependencies
-list.
-
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> from lp.soyuz.interfaces.component import IComponentSet
-    >>> main_component = getUtility(IComponentSet)['main']
-    >>> pmount_pub = hoary_i386[
-    ...     'pmount'].currentrelease.current_publishing_record
-    >>> pmount_pub.component = main_component
-    >>> depwait_build.dependencies = u'mozilla-firefox, pmount'
-    >>> from canonical.database.sqlbase import flush_database_caches
-    >>> flush_database_caches()
-    >>> transaction.commit()
-    >>> login(ANONYMOUS)
-
-    >>> flush_database_updates()
-
-Note that only the satisfied dependencies are removed the build
-dependency list.
-
-    >>> depwait_build.updateDependencies()
-    >>> print depwait_build.dependencies
-    mozilla-firefox
-
-'pmount' dependency is also satisfied in the Celso's PPA context,
-even when it is published in a component not allowed in its current
-component domain ('ogre_components'). That's because PPAs implicitly
-depend on all components of its distribution PRIMARY archive.
-
-
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> depwait_build.dependencies = u'biscuit, pmount'
-    >>> universe_component = getUtility(IComponentSet)['universe']
-    >>> pmount_pub.component = universe_component
-    >>> removeSecurityProxy(depwait_build).archive = cprov.archive
-    >>> flush_database_caches()
-    >>> login(ANONYMOUS)
-
-    >>> print get_components_for_context(
-    ...     depwait_build.current_component, depwait_build.pocket)
-    ['main']
-
-    >>> print pmount_pub.component.name
-    universe
-
-    >>> depwait_build.updateDependencies()
-    >>> print depwait_build.dependencies
-    biscuit
-
-Restore depwait_build previous state.
-
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> pmount_pub.component = main_component
-    >>> removeSecurityProxy(depwait_build).archive = ubuntu.main_archive
-    >>> flush_database_caches()
-    >>> login(ANONYMOUS)
-
-=== Retrying DEPWAIT builds ===
-
-It depends on the callsite to decide whether or not to 'retry' a
-build after calling updateDependencies, to encapsulate such decision
-for performing the mentioned  auto-depwait procedure we have a utility
-in IBinaryPackageBuildSet called retryDepWaiting().
-
-    >>> print depwait_build.status.name
-    MANUALDEPWAIT
-    >>> print depwait_build.distro_arch_series.title
-    The Hoary Hedgehog Release for i386 (x86)
-
-In order to allow depwait_build to be retried we will forge a
-'fully-satisfiable' dependencies field.
-
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> depwait_build.dependencies = u'pmount'
-    >>> login(ANONYMOUS)
-    >>> flush_database_updates()
-
-Then we can run the utility method for the target distroarchseries and
-expect depwait_build to be 'retried' and scored.
-
-    >>> getUtility(IBinaryPackageBuildSet).retryDepWaiting(hoaryi386)
-
-    >>> print depwait_build.status.name
-    NEEDSBUILD
-
-    >>> depwait_build.buildqueue_record.lastscore
-    2505
-
-The 'retryDepWaiting' task is performed periodically via cronjob by
-cronscript/buildd-retry-depwait.py. It can be run in parallel with
-other buildd tasks because the procedure is 'atomic' enough, i.e.,
-after the commit the retried jobs are ready to be dispatched.
-
-
-== Build rescoring ==
-
-Some builds can be rescored, to determine if it's possible check the
-can_be_rescored property:
-
-    >>> depwait_build.can_be_rescored
-    True
-
-We need to be at least a buildd-admin to rescore:
-
-    >>> depwait_build.rescore(1000)
-    Traceback (most recent call last):
-    ...
-    Unauthorized:...
-
-    >>> login('celso.providelo@xxxxxxxxxxxxx')
-    >>> depwait_build.rescore(1000)
-    >>> print depwait_build.buildqueue_record.lastscore
-    1000
-
-If a callsite tries to rescore a build that is not in the NEEDSBUILD state,
-a CannotBeRescored exception is raised.
-
-    >>> depwait_build.status = BuildStatus.FAILEDTOUPLOAD
-    >>> depwait_build.rescore(1000)
-    Traceback (most recent call last):
-    ...
-    CannotBeRescored: Build cannot be rescored.
-
-    >>> login(ANONYMOUS)
-
-
-== Build record security ==
-
-IBinaryPackageBuild's content class is wrapped in a Zope security
-wrapper that prevents access to private builds for unauthorised users.
-
-Accessing the cprov builds when logged in as admin will see the records:
-
-    >>> login('admin@xxxxxxxxxxxxx')
-    >>> bob_builds = bob.getBuildRecords(user=admin)
-    >>> print_build_details(bob_builds)
-    cprov: i386 build of privacycheck 666 in ubuntutest breezy-autotest...
-    ubuntu-team: hppa build of mozilla-firefox 0.9 in ubuntu warty RELEASE
-    cprov: hppa build of mozilla-firefox 0.9 in ubuntu warty RELEASE
-    cprov: i386 build of pmount 0.1-1 in ubuntu warty RELEASE
-    cprov: i386 build of cdrkit 1.0 in ubuntu breezy-autotest RELEASE
-    ...
-
-Likewise when logged in as cprov:
-
-    >>> login('celso.providelo@xxxxxxxxxxxxx')
-    >>> bob_builds = bob.getBuildRecords(user=admin)
-    >>> print_build_details(bob_builds)
-    cprov: i386 build of privacycheck 666 in ubuntutest breezy-autotest...
-    ubuntu-team: hppa build of mozilla-firefox 0.9 in ubuntu warty RELEASE
-    cprov: hppa build of mozilla-firefox 0.9 in ubuntu warty RELEASE
-    cprov: i386 build of pmount 0.1-1 in ubuntu warty RELEASE
-    cprov: i386 build of cdrkit 1.0 in ubuntu breezy-autotest RELEASE
-    ...
-
-A user who is a buildd admin is not allowed to see the build records for
-private builds. Even though they are admin, privacy must be maintained.
-
-    >>> login(buildd_admin.preferredemail.email)
-    >>> bob_builds = bob.getBuildRecords(user=admin)
-
-Define a helper function to catch the security exception:
-
-    >>> from zope.security.interfaces import Unauthorized
-    >>> def print_builds_with_exception(builds):
-    ...     try:
-    ...         print_build_details(bob_builds)
-    ...     except Unauthorized:
-    ...         print "Generated Unauthorized exception as expected"
-    ...     else:
-    ...         print "FAIL: should raise Unauthorized exception"
-
-And try to access the builds:
-
-    >>> print_builds_with_exception(bob_builds)
-    Generated Unauthorized exception as expected
-
-When logged in as anonymous this will generate a securtity exception
-when accessing the builds:
-
-    >>> login(ANONYMOUS)
-    >>> bob_builds = bob.getBuildRecords(user=admin)
-    >>> print_builds_with_exception(bob_builds)
-    Generated Unauthorized exception as expected
-
 There are other settable attributes declared in the zcml that require
 launchpad.Edit.
 
@@ -479,162 +214,3 @@
     >>> bq.estimated_duration
     datetime.timedelta(0, 3600)
 
-
-== IBinaryPackageBuildSet.getBuildsBySourcePackageRelease() ==
-
-getBuildsBySourcePackageRelease() will return all the Build records for
-all the SourcePackageRelease IDs passed.
-
-Create some sources with builds:
-
-    >>> source_one = test_publisher.getPubSource(
-    ...     status=PackagePublishingStatus.PUBLISHED,
-    ...     sourcename='sourceone')
-    >>> source_two = test_publisher.getPubSource(
-    ...     status=PackagePublishingStatus.PUBLISHED,
-    ...     sourcename='sourcetwo')
-    >>> build_one = source_one.sourcepackagerelease.createBuild(
-    ...         test_publisher.breezy_autotest_hppa,
-    ...         PackagePublishingPocket.RELEASE,
-    ...         test_publisher.breezy_autotest.main_archive,
-    ...         status=BuildStatus.FULLYBUILT)
-    >>> build_two = source_two.sourcepackagerelease.createBuild(
-    ...         test_publisher.breezy_autotest_hppa,
-    ...         PackagePublishingPocket.RELEASE,
-    ...         test_publisher.breezy_autotest.main_archive,
-    ...         status=BuildStatus.NEEDSBUILD)
-
-    >>> source_ids = (
-    ...     source_one.sourcepackagerelease.id,
-    ...     source_two.sourcepackagerelease.id,
-    ...     )
-    >>> builds = removeSecurityProxy(bs).getBuildsBySourcePackageRelease(
-    ...     source_ids)
-    >>> import operator
-    >>> for build in sorted(builds, key=operator.attrgetter("id")):
-    ...     print build.title, build.status.name
-    hppa build of sourceone 666 in ubuntutest breezy-autotest RELEASE
-        FULLYBUILT
-    hppa build of sourcetwo 666 in ubuntutest breezy-autotest RELEASE
-        NEEDSBUILD
-
-The results can also be filtered on build state:
-
-    >>> builds = removeSecurityProxy(bs).getBuildsBySourcePackageRelease(
-    ...     source_ids, buildstate=BuildStatus.FULLYBUILT)
-    >>> for build in sorted(builds, key=operator.attrgetter("id")):
-    ...     print build.title, build.status.name
-    hppa build of sourceone 666 in ubuntutest breezy-autotest RELEASE
-        FULLYBUILT
-
-If there are no matching results then it returns an empty SelectResults.
-
-    >>> builds = removeSecurityProxy(bs).getBuildsBySourcePackageRelease(
-    ...     source_ids, buildstate=BuildStatus.CHROOTWAIT)
-    >>> print builds.count()
-    0
-
-Supplying an empty list or None for the IDs results in an empty list
-being returned.
-
-    >>> removeSecurityProxy(bs).getBuildsBySourcePackageRelease(None)
-    []
-
-    >>> removeSecurityProxy(bs).getBuildsBySourcePackageRelease([])
-    []
-
-
-== Getting the build records for a particular builder ==
-
-The getBuildsForBuilder method returns all the builds for the
-specified builder ID, ordered from most-recently built.
-
-    Create some source packages with which to test the
-    getBuildsForBuilder method:
-
-    >>> src_pkg_earlier = test_publisher.getPubSource(
-    ...     status=PackagePublishingStatus.PUBLISHED,
-    ...     sourcename='earlierbuildsrc', architecturehintlist='hppa i386')
-    >>> src_pkg_later = test_publisher.getPubSource(
-    ...     status=PackagePublishingStatus.PUBLISHED,
-    ...     sourcename='laterbuildsrc',
-    ...     architecturehintlist='hppa i386')
-
-    Create the builds based on the source packages, with the builds
-    for 'earlierbuildsrc' built one day before the 'laterbuildsrc':
-
-    >>> frog_builder = getUtility(IBuilderSet)['frog']
-    >>> bob_builder = getUtility(IBuilderSet)['bob']
-
-    >>> earlier_builds = src_pkg_earlier.createMissingBuilds()
-    >>> eg_build_date = earlier_builds[0].date_created
-    >>> for build in earlier_builds:
-    ...     build.date_started = eg_build_date - timedelta(1)
-    ...     build.date_finished = eg_build_date - timedelta(1)
-
-    >>> later_builds = src_pkg_later.createMissingBuilds()
-    >>> for build in later_builds:
-    ...     build.date_started = eg_build_date
-    ...     build.date_finished = eg_build_date
-
-    Ensure that the i386 builds are created by the 'frog' builder,
-    while the hppa builds are created by 'bob' the builder:
-
-    >>> builds = earlier_builds + later_builds
-    >>> for build in builds:
-    ...     if build.processor.name == u'386':
-    ...         build.builder = frog_builder
-    ...     else:
-    ...         build.builder = bob_builder
-
-    A call to getBuildsForBuilder returns only those builds that were
-    built by the specified builder, ordered by datebuilt DESC:
-
-    >>> frog_builds = getUtility(IBinaryPackageBuildSet).getBuildsForBuilder(
-    ...     frog_builder.id)
-    >>> print_build_details(frog_builds)
-    ubuntu-team: i386 build of laterbuildsrc 666 in ubuntutest
-        breezy-autotest RELEASE
-    ubuntu-team: i386 build of earlierbuildsrc 666 in ubuntutest
-        breezy-autotest RELEASE
-
-
-== Source publication for builds ==
-
-The current source publication for a given build is available via
-its 'current_source_publication' property.
-
-We will create a new publication and its corresponding build.
-
-    >>> original_pub = test_publisher.getPubSource()
-    >>> [build] = original_pub.createMissingBuilds()
-
-The publication returned by 'current_source_publication' is the one
-that originated the build.
-
-    >>> build.current_source_publication == original_pub
-    True
-
-We will override the source publication, moving it from 'main'
-component (default) to 'universe'.
-
-    >>> universe_component = getUtility(IComponentSet)['universe']
-    >>> secure_overridden_pub = original_pub.changeOverride(
-    ...     new_component=universe_component)
-
-Fetching the corresponding `SourcePackagePublishingHistory` for the
-comparisons.
-
-    >>> from lp.soyuz.model.publishing import (
-    ...     SourcePackagePublishingHistory)
-    >>> overridden_pub = SourcePackagePublishingHistory.get(
-    ...     secure_overridden_pub.id)
-
-An we can see that the build 'current_source_publication' now points
-to the most recent publication, the overridden one.
-
-    >>> original_pub == build.current_source_publication
-    False
-
-    >>> overridden_pub == build.current_source_publication
-    True

=== modified file 'lib/lp/soyuz/tests/test_build.py'
--- lib/lp/soyuz/tests/test_build.py	2011-01-17 21:32:16 +0000
+++ lib/lp/soyuz/tests/test_build.py	2011-01-17 21:32:17 +0000
@@ -8,6 +8,7 @@
     timedelta,
     )
 import pytz
+import transaction
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
 
@@ -17,10 +18,13 @@
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.interfaces.series import SeriesStatus
 from lp.soyuz.enums import (
+    ArchivePurpose,
     BinaryPackageFormat,
     PackagePublishingPriority,
     PackageUploadStatus,
     )
+from lp.soyuz.interfaces.binarypackagebuild import CannotBeRescored
+from lp.soyuz.interfaces.component import IComponentSet
 from lp.soyuz.interfaces.publishing import PackagePublishingStatus
 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
 from lp.testing import (
@@ -75,7 +79,7 @@
         [build] = spph.createMissingBuilds()
         self.assertEquals(self.distroseries.main_archive, build.archive)
         self.assertEquals(self.distroseries.distribution, build.distribution)
-        self.assertEquals(self.distroseries, build.distroseries)
+        self.assertEquals(self.distroseries, build.distro_series)
         self.assertEquals(self.das, build.distro_arch_series)
         self.assertEquals(PackagePublishingPocket.RELEASE, build.pocket)
         self.assertEquals(self.das.architecturetag, build.arch_tag)
@@ -160,6 +164,29 @@
         [build] = spph.createMissingBuilds()
         self.assertFalse(build.can_be_retried)
 
+    def test_partner_retry_for_released_series(self):
+        # Builds for PARTNER can be retried -- even if the distroseries is
+        # released.
+        distroseries = self.factory.makeDistroSeries()
+        das = self.factory.makeDistroArchSeries(
+            distroseries=distroseries, processorfamily=self.pf,
+            supports_virtualized=True)
+        archive = self.factory.makeArchive(
+            purpose=ArchivePurpose.PARTNER,
+            distribution=distroseries.distribution)
+        with person_logged_in(self.admin):
+            distroseries.nominatedarchindep = das
+            distroseries.status = SeriesStatus.OBSOLETE
+            self.publisher.addFakeChroots(distroseries=distroseries)
+        spph = self.publisher.getPubSource(
+            sourcename=self.factory.getUniqueString(),
+            version="%s.1" % self.factory.getUniqueInteger(),
+            distroseries=distroseries, archive=archive)
+        [build] = spph.createMissingBuilds()
+        with person_logged_in(self.admin):
+            build.status = BuildStatus.FAILEDTOBUILD
+        self.assertTrue(build.can_be_retried)
+
     def test_retry(self):
         # A build can be retried
         spph = self.publisher.getPubSource(
@@ -253,3 +280,43 @@
         # Verify .binarypackages returns sorted by name
         expected_names.sort()
         self.assertEquals(expected_names, bin_names)
+
+    def test_cannot_rescore_non_needsbuilds_builds(self):
+        # If a build record isn't in NEEDSBUILD, it can not be rescored.
+        # We will also need to log into an admin to do the rescore
+        with person_logged_in(self.admin):
+            [bpph] = self.publisher.getPubBinaries(
+                binaryname=self.factory.getUniqueString(),
+                version="%s.1" % self.factory.getUniqueInteger(),
+                distroseries=self.distroseries)
+            build = bpph.binarypackagerelease.build
+            self.assertRaises(CannotBeRescored, build.rescore, 20)
+
+    def test_rescore_builds(self):
+        # If the user has build-admin privileges, they can rescore builds
+        spph = self.publisher.getPubSource(
+            sourcename=self.factory.getUniqueString(),
+            version="%s.1" % self.factory.getUniqueInteger(),
+            distroseries=self.distroseries)
+        [build] = spph.createMissingBuilds()
+        self.assertEquals(BuildStatus.NEEDSBUILD, build.status)
+        self.assertEquals(2505, build.buildqueue_record.lastscore)
+        with person_logged_in(self.admin):
+            build.rescore(5000)
+            transaction.commit()
+        self.assertEquals(5000, build.buildqueue_record.lastscore)
+
+    def test_source_publication_override(self):
+        # Components can be overridden in builds.
+        spph = self.publisher.getPubSource(
+            sourcename=self.factory.getUniqueString(),
+            version="%s.1" % self.factory.getUniqueInteger(),
+            distroseries=self.distroseries)
+        [build] = spph.createMissingBuilds()
+        self.assertEquals(spph, build.current_source_publication)
+        universe = getUtility(IComponentSet)['universe']
+        overridden_spph = spph.changeOverride(new_component=universe)
+        # We can now see current source publication points to the overridden
+        # publication.
+        self.assertNotEquals(spph, build.current_source_publication)
+        self.assertEquals(overridden_spph, build.current_source_publication)

=== added file 'lib/lp/soyuz/tests/test_build_depwait.py'
--- lib/lp/soyuz/tests/test_build_depwait.py	1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/tests/test_build_depwait.py	2011-01-17 21:32:17 +0000
@@ -0,0 +1,118 @@
+# Copyright 2011 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+__metaclass__ = type
+
+import transaction
+from zope.component import getUtility
+
+from canonical.testing.layers import LaunchpadFunctionalLayer
+from lp.buildmaster.enums import BuildStatus
+from lp.registry.interfaces.person import IPersonSet
+from lp.soyuz.enums import (
+    ArchivePurpose,
+    PackagePublishingStatus,
+    )
+from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
+from lp.soyuz.interfaces.component import IComponentSet
+from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
+from lp.testing import (
+    person_logged_in,
+    TestCaseWithFactory,
+    )
+from lp.testing.sampledata import ADMIN_EMAIL
+
+class TestBuildDepWait(TestCaseWithFactory):
+
+    layer = LaunchpadFunctionalLayer
+
+    def setUp(self):
+        super(TestBuildDepWait, self).setUp()
+        self.admin = getUtility(IPersonSet).getByEmail(ADMIN_EMAIL)
+        # Create everything we need to create builds, such as a
+        # DistroArchSeries and a builder.
+        self.pf = self.factory.makeProcessorFamily()
+        pf_proc = self.pf.addProcessor(self.factory.getUniqueString(), '', '')
+        self.distroseries = self.factory.makeDistroSeries()
+        self.das = self.factory.makeDistroArchSeries(
+            distroseries=self.distroseries, processorfamily=self.pf,
+            supports_virtualized=True)
+        self.archive = self.factory.makeArchive(
+            distribution=self.distroseries.distribution,
+            purpose=ArchivePurpose.PRIMARY)
+        with person_logged_in(self.admin):
+            self.publisher = SoyuzTestPublisher()
+            self.publisher.prepareBreezyAutotest()
+            self.distroseries.nominatedarchindep = self.das
+            self.publisher.addFakeChroots(distroseries=self.distroseries)
+            self.builder = self.factory.makeBuilder(processor=pf_proc)
+
+    def test_update_dependancies(self):
+        # Calling .updateDependencies() on a build will update will remove
+        # those which are reachable.
+        spph = self.publisher.getPubSource(
+            sourcename=self.factory.getUniqueString(),
+            version="%s.1" % self.factory.getUniqueInteger(),
+            distroseries=self.distroseries, archive=self.archive)
+        [build] = spph.createMissingBuilds()
+        spn = self.factory.getUniqueString()
+        version = "%s.1" % self.factory.getUniqueInteger()
+        with person_logged_in(self.admin):
+            build.status = BuildStatus.MANUALDEPWAIT
+            build.dependencies = unicode(spn)
+            [bpph] = self.publisher.getPubBinaries(
+                binaryname=spn, distroseries=self.distroseries,
+                version=version, builder=self.builder, archive=self.archive,
+                status=PackagePublishingStatus.PUBLISHED)
+            # Commit to make sure stuff hits the database.
+            transaction.commit()
+        build.updateDependencies()
+        self.assertEquals(u'', build.dependencies)
+        
+    def test_update_dependancies_respects_component(self):
+        # Since main can only utilise packages that are published in main,
+        # dependencies are not satisfied if they are not in main.
+        spph = self.publisher.getPubSource(
+            sourcename=self.factory.getUniqueString(),
+            version="%s.1" % self.factory.getUniqueInteger(),
+            distroseries=self.distroseries, archive=self.archive)
+        [build] = spph.createMissingBuilds()
+        spn = self.factory.getUniqueString()
+        version = "%s.1" % self.factory.getUniqueInteger()
+        with person_logged_in(self.admin):
+            build.status = BuildStatus.MANUALDEPWAIT
+            build.dependencies = unicode(spn)
+            [bpph] = self.publisher.getPubBinaries(
+                binaryname=spn, distroseries=self.distroseries,
+                version=version, builder=self.builder, archive=self.archive,
+                status=PackagePublishingStatus.PUBLISHED, component='universe')
+            # Commit to make sure stuff hits the database.
+            transaction.commit()
+        build.updateDependencies()
+        # Since the dependency is in universe, we still can't see it.
+        self.assertEquals(unicode(spn), build.dependencies)
+        with person_logged_in(self.admin):
+            bpph.component = getUtility(IComponentSet)['main']
+            transaction.commit()
+        # Now that we have moved it main, we can see it.
+        build.updateDependencies()
+        self.assertEquals(u'', build.dependencies)
+
+    def test_retry_dep_waiting(self):
+        # Builds in MANUALDEPWAIT can be automatically retried.
+        spph = self.publisher.getPubSource(
+            sourcename=self.factory.getUniqueString(),
+            version="%s.1" % self.factory.getUniqueInteger(),
+            distroseries=self.distroseries, archive=self.archive)
+        [build] = spph.createMissingBuilds()
+        with person_logged_in(self.admin):
+            build.status = BuildStatus.MANUALDEPWAIT
+            # .createMissingBuilds() queues the build for us, and we need to
+            # undo that if we're about to retry it.
+            build.buildqueue_record.destroySelf()
+            build.dependencies = u''
+            # Commit to make sure stuff hits the database.
+            transaction.commit()
+        getUtility(IBinaryPackageBuildSet).retryDepWaiting(self.das)
+        self.assertEquals(BuildStatus.NEEDSBUILD, build.status)
+        self.assertTrue(build.buildqueue_record.lastscore > 0)

=== added file 'lib/lp/soyuz/tests/test_build_privacy.py'
--- lib/lp/soyuz/tests/test_build_privacy.py	1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/tests/test_build_privacy.py	2011-01-17 21:32:17 +0000
@@ -0,0 +1,92 @@
+# Copyright 2011 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+__metaclass__ = type
+
+from zope.component import getUtility
+from zope.security.interfaces import Unauthorized
+
+from canonical.testing.layers import LaunchpadFunctionalLayer
+from lp.registry.interfaces.person import IPersonSet
+from lp.soyuz.interfaces.archive import IArchiveSet
+from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
+from lp.testing import (
+    person_logged_in,
+    TestCaseWithFactory,
+    )
+from lp.testing.sampledata import ADMIN_EMAIL
+
+
+class TestBuildPrivacy(TestCaseWithFactory):
+
+    layer = LaunchpadFunctionalLayer
+
+    def setUp(self):
+        super(TestBuildPrivacy, self).setUp()
+        # Add everything we need to create builds.
+        self.admin = getUtility(IPersonSet).getByEmail(ADMIN_EMAIL)
+        pf = self.factory.makeProcessorFamily()
+        pf_proc = pf.addProcessor(self.factory.getUniqueString(), '', '')
+        distroseries = self.factory.makeDistroSeries()
+        das = self.factory.makeDistroArchSeries(
+            distroseries=distroseries, processorfamily=pf,
+            supports_virtualized=True)
+        with person_logged_in(self.admin):
+            publisher = SoyuzTestPublisher()
+            publisher.prepareBreezyAutotest()
+            distroseries.nominatedarchindep = das
+            publisher.addFakeChroots(distroseries=distroseries)
+            self.factory.makeBuilder(processor=pf_proc)
+        self.public_archive = self.factory.makeArchive()
+        self.private_archive = self.factory.makeArchive(private=True)
+        # Create one public and one private build.
+        public_spph = publisher.getPubSource(
+            sourcename=self.factory.getUniqueString(),
+            version="%s.1" % self.factory.getUniqueInteger(),
+            distroseries=distroseries, archive=self.public_archive)
+        [public_build] = public_spph.createMissingBuilds()
+        private_spph = publisher.getPubSource(
+            sourcename=self.factory.getUniqueString(),
+            version="%s.1" % self.factory.getUniqueInteger(),
+            distroseries=distroseries, archive=self.private_archive)
+        with person_logged_in(self.admin):
+            [private_build] = private_spph.createMissingBuilds()
+        self.expected_title = '%s build of %s %s in %s %s RELEASE' % (
+            das.architecturetag, private_spph.source_package_name,
+            private_spph.source_package_version,
+            distroseries.distribution.name, distroseries.name)
+
+    def _get_build_title(self, build):
+        return build.title
+
+    def test_admin_can_see_private_builds(self):
+        # Admin users can see all builds.
+        with person_logged_in(self.admin):
+            private = getUtility(IArchiveSet).get(self.private_archive.id)
+            [build] = private.getBuildRecords()
+            self.assertEquals(
+                self.expected_title, self._get_build_title(build))
+
+    def test_owner_can_see_own_private_builds(self):
+        # The owner of the private archive is able to see all builds that
+        # publish to that archive.
+        with person_logged_in(self.private_archive.owner):
+            private = getUtility(IArchiveSet).get(self.private_archive.id)
+            [build] = private.getBuildRecords()
+            self.assertEquals(
+                self.expected_title, self._get_build_title(build))
+
+    def test_buildd_admin_cannot_see_private_builds(self):
+        # Admins that look after the builders ("buildd-admins"), can not see
+        # private builds.
+        buildd_admin = getUtility(IPersonSet).getByName(
+            'launchpad-buildd-admins')
+        with person_logged_in(buildd_admin):
+            private = getUtility(IArchiveSet).get(self.private_archive.id)
+            self.assertRaises(
+                Unauthorized, getattr, private, 'getBuildRecords')
+
+    def test_anonymous_cannot_see_private_builds(self):
+        # An anonymous user can't query the builds for the private archive.
+        private = getUtility(IArchiveSet).get(self.private_archive.id)
+        self.assertRaises(Unauthorized, getattr, private, 'getBuildRecords')

=== modified file 'lib/lp/soyuz/tests/test_build_set.py'
--- lib/lp/soyuz/tests/test_build_set.py	2011-01-17 21:32:16 +0000
+++ lib/lp/soyuz/tests/test_build_set.py	2011-01-17 21:32:17 +0000
@@ -61,6 +61,7 @@
             self.builder_one = self.factory.makeBuilder(processor=pf_proc_1)
             self.builder_two = self.factory.makeBuilder(processor=pf_proc_2)
         self.builds = []
+        self.spphs = []
 
     def setUpBuilds(self):
         for i in range(5):
@@ -69,6 +70,7 @@
                 sourcename=self.factory.getUniqueString(),
                 version="%s.%s" % (self.factory.getUniqueInteger(), i),
                 distroseries=self.distroseries, architecturehintlist='any')
+            self.spphs.append(spph)
             builds = spph.createMissingBuilds()
             with person_logged_in(self.admin):
                 for b in builds:
@@ -152,3 +154,54 @@
         rset = removeSecurityProxy(
             getUtility(IBinaryPackageBuildSet))._prefetchBuildData(build_ids)
         self.assertEquals(len(rset), 4)
+
+    def test_get_builds_by_source_package_release(self):
+        # We are able to return all of the builds for the source package
+        # release ids passed in.
+        self.setUpBuilds()
+        spphs = self.spphs[:2]
+        ids = [spph.sourcepackagerelease.id for spph in spphs]
+        builds = getUtility(
+            IBinaryPackageBuildSet).getBuildsBySourcePackageRelease(ids)
+        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,
+                        self.distroseries.name))
+        build_titles = [build.title for build in builds]
+        self.assertEquals(sorted(expected_titles), sorted(build_titles))
+
+    def test_get_builds_by_source_package_release_filtering(self):
+        self.setUpBuilds()
+        ids = [self.spphs[-1].sourcepackagerelease.id]
+        builds = getUtility(
+            IBinaryPackageBuildSet).getBuildsBySourcePackageRelease(
+                ids, buildstate=BuildStatus.FAILEDTOBUILD)
+        expected_titles = []
+        for das in (self.das_one, self.das_two):
+            expected_titles.append(
+                '%s build of %s %s in %s %s RELEASE' % (
+                    das.architecturetag, self.spphs[-1].source_package_name,
+                    self.spphs[-1].source_package_version,
+                    self.distroseries.distribution.name,
+                    self.distroseries.name))
+        build_titles = [build.title for build in builds] 
+        self.assertEquals(sorted(expected_titles), sorted(build_titles))
+        builds = getUtility(
+            IBinaryPackageBuildSet).getBuildsBySourcePackageRelease(
+                ids, buildstate=BuildStatus.CHROOTWAIT)
+        self.assertEquals([], list(builds))
+
+    def test_no_get_builds_by_source_package_release(self):
+        # If no ids or None are passed into .getBuildsBySourcePackageRelease,
+        # an empty list is returned.
+        builds = getUtility(
+            IBinaryPackageBuildSet).getBuildsBySourcePackageRelease(None)
+        self.assertEquals([], builds)
+        builds = getUtility(
+            IBinaryPackageBuildSet).getBuildsBySourcePackageRelease([])
+        self.assertEquals([], builds)