← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wgrant/launchpad/even-slimmer-bfjo into lp:launchpad

 

William Grant has proposed merging lp:~wgrant/launchpad/even-slimmer-bfjo into lp:launchpad with lp:~wgrant/launchpad/slimmer-bfjo as a prerequisite.

Commit message:
Move IBuildFarmJobOld.score() to IBuildFarmJob.calculateScore().

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~wgrant/launchpad/even-slimmer-bfjo/+merge/194774

Move IBuildFarmJobOld.score() to IBuildFarmJob.calculateScore(). IBFJO shall perish.
-- 
https://code.launchpad.net/~wgrant/launchpad/even-slimmer-bfjo/+merge/194774
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/even-slimmer-bfjo into lp:launchpad.
=== modified file 'lib/lp/buildmaster/interfaces/buildfarmjob.py'
--- lib/lp/buildmaster/interfaces/buildfarmjob.py	2013-11-12 00:57:36 +0000
+++ lib/lp/buildmaster/interfaces/buildfarmjob.py	2013-11-12 00:57:36 +0000
@@ -59,9 +59,6 @@
     BuildQueue have been transitioned to the new database schema.
     """
 
-    def score():
-        """Calculate a job score appropriate for the job type in question."""
-
     def getByJob(job):
         """Get the specific `IBuildFarmJob` for the given `Job`.
 
@@ -228,6 +225,9 @@
     def gotFailure():
         """Increment the failure_count for this job."""
 
+    def calculateScore():
+        """Calculate the build queue priority for this job."""
+
     title = exported(TextLine(title=_("Title"), required=False),
                      as_of="beta")
 

=== modified file 'lib/lp/buildmaster/model/buildfarmjob.py'
--- lib/lp/buildmaster/model/buildfarmjob.py	2013-11-12 00:57:36 +0000
+++ lib/lp/buildmaster/model/buildfarmjob.py	2013-11-12 00:57:36 +0000
@@ -69,10 +69,6 @@
         job_ids = [job.id for job in jobs]
         return IStore(cls).find(cls, cls.job_id.is_in(job_ids))
 
-    def score(self):
-        """See `IBuildFarmJobOld`."""
-        raise NotImplementedError
-
     def cleanUp(self):
         """See `IBuildFarmJob`.
 
@@ -223,6 +219,10 @@
         """See `IBuildFarmJob`."""
         self.failure_count += 1
 
+    def calculateScore(self):
+        """See `IBuildFarmJob`."""
+        raise NotImplementedError
+
 
 class SpecificBuildFarmJobSourceMixin:
 

=== modified file 'lib/lp/buildmaster/model/buildqueue.py'
--- lib/lp/buildmaster/model/buildqueue.py	2013-11-12 00:57:36 +0000
+++ lib/lp/buildmaster/model/buildqueue.py	2013-11-12 00:57:36 +0000
@@ -180,7 +180,7 @@
             return
         # Allow the `IBuildFarmJob` instance with the data/logic specific to
         # the job at hand to calculate the score as appropriate.
-        self.lastscore = self.specific_job.score()
+        self.lastscore = self.specific_job.build.calculateScore()
 
     def markAsBuilding(self, builder):
         """See `IBuildQueue`."""

=== modified file 'lib/lp/code/model/sourcepackagerecipebuild.py'
--- lib/lp/code/model/sourcepackagerecipebuild.py	2013-11-12 00:57:36 +0000
+++ lib/lp/code/model/sourcepackagerecipebuild.py	2013-11-12 00:57:36 +0000
@@ -305,6 +305,9 @@
         store.remove(self)
         store.remove(self.build_farm_job)
 
+    def calculateScore(self):
+        return 2505 + self.archive.relative_build_score
+
     @classmethod
     def getByID(cls, build_id):
         """See `ISourcePackageRecipeBuildSource`."""
@@ -464,6 +467,3 @@
         store = IMasterStore(cls)
         store.add(specific_job)
         return specific_job
-
-    def score(self):
-        return 2505 + self.build.archive.relative_build_score

=== modified file 'lib/lp/soyuz/model/binarypackagebuild.py'
--- lib/lp/soyuz/model/binarypackagebuild.py	2013-11-12 00:57:36 +0000
+++ lib/lp/soyuz/model/binarypackagebuild.py	2013-11-12 00:57:36 +0000
@@ -90,11 +90,20 @@
     IBinaryPackageBuildSet,
     UnparsableDependencies,
     )
+from lp.soyuz.interfaces.buildpackagejob import (
+    COPY_ARCHIVE_SCORE_PENALTY,
+    PRIVATE_ARCHIVE_SCORE_BONUS,
+    SCORE_BY_COMPONENT,
+    SCORE_BY_POCKET,
+    SCORE_BY_URGENCY,
+    )
 from lp.soyuz.interfaces.distroarchseries import IDistroArchSeries
+from lp.soyuz.interfaces.packageset import IPackagesetSet
 from lp.soyuz.model.binarypackagename import BinaryPackageName
 from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
 from lp.soyuz.model.buildpackagejob import BuildPackageJob
 from lp.soyuz.model.files import BinaryPackageFile
+from lp.soyuz.model.packageset import Packageset
 from lp.soyuz.model.queue import (
     PackageUpload,
     PackageUploadBuild,
@@ -309,6 +318,42 @@
             distribution=self.distribution,
             sourcepackagerelease=self.source_package_release)
 
+    def calculateScore(self):
+        """See `IBuildFarmJob`."""
+        score = 0
+
+        # Private builds get uber score.
+        if self.archive.private:
+            score += PRIVATE_ARCHIVE_SCORE_BONUS
+
+        if self.archive.is_copy:
+            score -= COPY_ARCHIVE_SCORE_PENALTY
+
+        score += self.archive.relative_build_score
+
+        # Language packs don't get any of the usual package-specific
+        # score bumps, as they unduly delay the building of packages in
+        # the main component otherwise.
+        if self.source_package_release.section.name == 'translations':
+            return score
+
+        # Calculates the urgency-related part of the score.
+        score += SCORE_BY_URGENCY[self.source_package_release.urgency]
+
+        # Calculates the pocket-related part of the score.
+        score += SCORE_BY_POCKET[self.pocket]
+
+        # Calculates the component-related part of the score.
+        score += SCORE_BY_COMPONENT.get(self.current_component.name, 0)
+
+        # Calculates the package-set-related part of the score.
+        package_sets = getUtility(IPackagesetSet).setsIncludingSource(
+            self.source_package_release.name, distroseries=self.distro_series)
+        if not self.archive.is_ppa and not package_sets.is_empty():
+            score += package_sets.max(Packageset.relative_build_score)
+
+        return score
+
     def getBinaryPackageNamesForDisplay(self):
         """See `IBuildView`."""
         store = Store.of(self)

=== modified file 'lib/lp/soyuz/model/buildpackagejob.py'
--- lib/lp/soyuz/model/buildpackagejob.py	2013-11-12 00:57:36 +0000
+++ lib/lp/soyuz/model/buildpackagejob.py	2013-11-12 00:57:36 +0000
@@ -19,16 +19,7 @@
 from lp.services.database.bulk import load_related
 from lp.services.database.interfaces import IStore
 from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
-from lp.soyuz.interfaces.buildpackagejob import (
-    COPY_ARCHIVE_SCORE_PENALTY,
-    IBuildPackageJob,
-    PRIVATE_ARCHIVE_SCORE_BONUS,
-    SCORE_BY_COMPONENT,
-    SCORE_BY_POCKET,
-    SCORE_BY_URGENCY,
-    )
-from lp.soyuz.interfaces.packageset import IPackagesetSet
-from lp.soyuz.model.packageset import Packageset
+from lp.soyuz.interfaces.buildpackagejob import IBuildPackageJob
 
 
 class BuildPackageJob(BuildFarmJobOld, Storm):
@@ -56,44 +47,6 @@
             [BuildPackageJob.job_id.is_in([job.id for job in jobs]),
              BuildPackageJob.build_id == BinaryPackageBuild.id]))
 
-    def score(self):
-        """See `IBuildPackageJob`."""
-        score = 0
-
-        # Private builds get uber score.
-        if self.build.archive.private:
-            score += PRIVATE_ARCHIVE_SCORE_BONUS
-
-        if self.build.archive.is_copy:
-            score -= COPY_ARCHIVE_SCORE_PENALTY
-
-        score += self.build.archive.relative_build_score
-
-        # Language packs don't get any of the usual package-specific
-        # score bumps, as they unduly delay the building of packages in
-        # the main component otherwise.
-        if self.build.source_package_release.section.name == 'translations':
-            return score
-
-        # Calculates the urgency-related part of the score.
-        score += SCORE_BY_URGENCY[self.build.source_package_release.urgency]
-
-        # Calculates the pocket-related part of the score.
-        score += SCORE_BY_POCKET[self.build.pocket]
-
-        # Calculates the component-related part of the score.
-        score += SCORE_BY_COMPONENT.get(
-            self.build.current_component.name, 0)
-
-        # Calculates the package-set-related part of the score.
-        package_sets = getUtility(IPackagesetSet).setsIncludingSource(
-            self.build.source_package_release.name,
-            distroseries=self.build.distro_series)
-        if not self.build.archive.is_ppa and not package_sets.is_empty():
-            score += package_sets.max(Packageset.relative_build_score)
-
-        return score
-
     @classmethod
     def preloadJobsData(cls, jobs):
         from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild

=== modified file 'lib/lp/soyuz/tests/test_binarypackagebuild.py'
--- lib/lp/soyuz/tests/test_binarypackagebuild.py	2013-11-12 00:57:36 +0000
+++ lib/lp/soyuz/tests/test_binarypackagebuild.py	2013-11-12 00:57:36 +0000
@@ -9,15 +9,19 @@
     )
 
 import pytz
+from simplejson import dumps
 from storm.store import Store
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
 
+from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.buildmaster.enums import BuildStatus
 from lp.buildmaster.interfaces.buildqueue import IBuildQueue
 from lp.buildmaster.interfaces.packagebuild import IPackageBuild
 from lp.buildmaster.model.buildqueue import BuildQueue
+from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.interfaces.series import SeriesStatus
+from lp.registry.interfaces.sourcepackage import SourcePackageUrgency
 from lp.services.job.model.job import Job
 from lp.services.log.logger import DevNullLogger
 from lp.services.webapp.interaction import ANONYMOUS
@@ -31,7 +35,14 @@
     IBinaryPackageBuildSet,
     UnparsableDependencies,
     )
-from lp.soyuz.interfaces.buildpackagejob import IBuildPackageJob
+from lp.soyuz.interfaces.buildpackagejob import (
+    COPY_ARCHIVE_SCORE_PENALTY,
+    IBuildPackageJob,
+    PRIVATE_ARCHIVE_SCORE_BONUS,
+    SCORE_BY_COMPONENT,
+    SCORE_BY_POCKET,
+    SCORE_BY_URGENCY,
+    )
 from lp.soyuz.interfaces.component import IComponentSet
 from lp.soyuz.model.binarypackagebuild import (
     BinaryPackageBuild,
@@ -40,9 +51,11 @@
 from lp.soyuz.model.buildpackagejob import BuildPackageJob
 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
 from lp.testing import (
+    anonymous_logged_in,
     api_url,
     login,
     logout,
+    person_logged_in,
     TestCaseWithFactory,
     )
 from lp.testing.layers import (
@@ -541,3 +554,239 @@
         removeSecurityProxy(archive).permit_obsolete_series_uploads = True
         BinaryPackageBuildSet.postprocessCandidate(job, DevNullLogger())
         self.assertEqual(BuildStatus.NEEDSBUILD, build.status)
+
+
+class TestCalculateScore(TestCaseWithFactory):
+
+    layer = DatabaseFunctionalLayer
+
+    def makeBuild(self, purpose=None, private=False, component="main",
+                  urgency="high", pocket="RELEASE", section_name=None):
+        if purpose is not None or private:
+            archive = self.factory.makeArchive(
+                purpose=purpose, private=private)
+        else:
+            archive = None
+        spph = self.factory.makeSourcePackagePublishingHistory(
+            archive=archive, component=component, urgency=urgency,
+            section_name=section_name)
+        naked_spph = removeSecurityProxy(spph)  # needed for private archives
+        return removeSecurityProxy(
+            self.factory.makeBinaryPackageBuild(
+                source_package_release=naked_spph.sourcepackagerelease,
+                pocket=pocket))
+
+    # The defaults for pocket, component, and urgency here match those in
+    # makeBuildJob.
+    def assertCorrectScore(self, build, pocket="RELEASE", component="main",
+                           urgency="high", other_bonus=0):
+        self.assertEqual(
+            (SCORE_BY_POCKET[PackagePublishingPocket.items[pocket.upper()]] +
+             SCORE_BY_COMPONENT[component] +
+             SCORE_BY_URGENCY[SourcePackageUrgency.items[urgency.upper()]] +
+             other_bonus), build.calculateScore())
+
+    def test_score_unusual_component(self):
+        spph = self.factory.makeSourcePackagePublishingHistory(
+            component="unusual")
+        build = self.factory.makeBinaryPackageBuild(
+            source_package_release=spph.sourcepackagerelease)
+        # For now just test that it doesn't raise an Exception
+        build.calculateScore()
+
+    def test_main_release_low_score(self):
+        # 1500 (RELEASE) + 1000 (main) + 5 (low) = 2505.
+        build = self.makeBuild(component="main", urgency="low")
+        self.assertCorrectScore(build, "RELEASE", "main", "low")
+
+    def test_copy_archive_main_release_low_score(self):
+        # 1500 (RELEASE) + 1000 (main) + 5 (low) - 2600 (copy archive) = -95.
+        # With this penalty, even language-packs and build retries will be
+        # built before copy archives.
+        build = self.makeBuild(
+            purpose="COPY", component="main", urgency="low")
+        self.assertCorrectScore(
+            build, "RELEASE", "main", "low", -COPY_ARCHIVE_SCORE_PENALTY)
+
+    def test_copy_archive_relative_score_is_applied(self):
+        # Per-archive relative build scores are applied, in this case
+        # exactly offsetting the copy-archive penalty.
+        build = self.makeBuild(
+            purpose="COPY", component="main", urgency="low")
+        removeSecurityProxy(build.archive).relative_build_score = 2600
+        self.assertCorrectScore(
+            build, "RELEASE", "main", "low",
+            -COPY_ARCHIVE_SCORE_PENALTY + 2600)
+
+    def test_archive_negative_relative_score_is_applied(self):
+        # Negative per-archive relative build scores are allowed.
+        build = self.makeBuild(component="main", urgency="low")
+        removeSecurityProxy(build.archive).relative_build_score = -100
+        self.assertCorrectScore(build, "RELEASE", "main", "low", -100)
+
+    def test_private_archive_bonus_is_applied(self):
+        # Private archives get a bonus of 10000.
+        build = self.makeBuild(private=True, component="main", urgency="high")
+        self.assertCorrectScore(
+            build, "RELEASE", "main", "high", PRIVATE_ARCHIVE_SCORE_BONUS)
+
+    def test_main_release_low_recent_score(self):
+        # 1500 (RELEASE) + 1000 (main) + 5 (low) = 2505.
+        build = self.makeBuild(component="main", urgency="low")
+        self.assertCorrectScore(build, "RELEASE", "main", "low")
+
+    def test_universe_release_high_five_minutes_score(self):
+        # 1500 (RELEASE) + 250 (universe) + 15 (high) = 1765.
+        build = self.makeBuild(component="universe", urgency="high")
+        self.assertCorrectScore(build, "RELEASE", "universe", "high")
+
+    def test_multiverse_release_medium_fifteen_minutes_score(self):
+        # 1500 (RELEASE) + 0 (multiverse) + 10 (medium) = 1510.
+        build = self.makeBuild(component="multiverse", urgency="medium")
+        self.assertCorrectScore(build, "RELEASE", "multiverse", "medium")
+
+    def test_main_release_emergency_thirty_minutes_score(self):
+        # 1500 (RELEASE) + 1000 (main) + 20 (emergency) = 2520.
+        build = self.makeBuild(component="main", urgency="emergency")
+        self.assertCorrectScore(build, "RELEASE", "main", "emergency")
+
+    def test_restricted_release_low_one_hour_score(self):
+        # 1500 (RELEASE) + 750 (restricted) + 5 (low) = 2255.
+        build = self.makeBuild(component="restricted", urgency="low")
+        self.assertCorrectScore(build, "RELEASE", "restricted", "low")
+
+    def test_backports_score(self):
+        # BACKPORTS is the lowest-priority pocket.
+        build = self.makeBuild(pocket="BACKPORTS")
+        self.assertCorrectScore(build, "BACKPORTS")
+
+    def test_release_score(self):
+        # RELEASE ranks next above BACKPORTS.
+        build = self.makeBuild(pocket="RELEASE")
+        self.assertCorrectScore(build, "RELEASE")
+
+    def test_proposed_updates_score(self):
+        # PROPOSED and UPDATES both rank next above RELEASE.  The reason why
+        # PROPOSED and UPDATES have the same priority is because sources in
+        # both pockets are submitted to the same policy and should reach
+        # their audience as soon as possible (see more information about
+        # this decision in bug #372491).
+        proposed_build = self.makeBuild(pocket="PROPOSED")
+        self.assertCorrectScore(proposed_build, "PROPOSED")
+        updates_build = self.makeBuild(pocket="UPDATES")
+        self.assertCorrectScore(updates_build, "UPDATES")
+
+    def test_security_updates_score(self):
+        # SECURITY is the top-ranked pocket.
+        build = self.makeBuild(pocket="SECURITY")
+        self.assertCorrectScore(build, "SECURITY")
+
+    def test_score_packageset(self):
+        # Package sets alter the score of official packages for their
+        # series.
+        build = self.makeBuild(
+            component="main", urgency="low", purpose=ArchivePurpose.PRIMARY)
+        packageset = self.factory.makePackageset(
+            distroseries=build.distro_series)
+        removeSecurityProxy(packageset).add(
+            [build.source_package_release.sourcepackagename])
+        removeSecurityProxy(packageset).relative_build_score = 100
+        self.assertCorrectScore(build, "RELEASE", "main", "low", 100)
+
+    def test_score_packageset_in_ppa(self):
+        # Package set score boosts don't affect PPA packages.
+        build = self.makeBuild(
+            component="main", urgency="low", purpose=ArchivePurpose.PPA)
+        packageset = self.factory.makePackageset(
+            distroseries=build.distro_series)
+        removeSecurityProxy(packageset).add(
+            [build.source_package_release.sourcepackagename])
+        removeSecurityProxy(packageset).relative_build_score = 100
+        self.assertCorrectScore(build, "RELEASE", "main", "low", 0)
+
+    def test_translations_score(self):
+        # Language packs (the translations section) don't get any
+        # package-specific score bumps. They always have the archive's
+        # base score.
+        build = self.makeBuild(section_name='translations')
+        removeSecurityProxy(build.archive).relative_build_score = 666
+        self.assertEqual(666, build.calculateScore())
+
+    def assertScoreReadableByAnyone(self, obj):
+        """An object's build score is readable by anyone."""
+        with person_logged_in(obj.owner):
+            obj_url = api_url(obj)
+        removeSecurityProxy(obj).relative_build_score = 100
+        webservice = webservice_for_person(
+            self.factory.makePerson(), permission=OAuthPermission.WRITE_PUBLIC)
+        entry = webservice.get(obj_url, api_version="devel").jsonBody()
+        self.assertEqual(100, entry["relative_build_score"])
+
+    def assertScoreNotWriteableByOwner(self, obj):
+        """Being an object's owner does not allow changing its build score.
+
+        This affects a site-wide resource, and is thus restricted to
+        launchpad-buildd-admins.
+        """
+        with person_logged_in(obj.owner):
+            obj_url = api_url(obj)
+        webservice = webservice_for_person(
+            obj.owner, permission=OAuthPermission.WRITE_PUBLIC)
+        entry = webservice.get(obj_url, api_version="devel").jsonBody()
+        response = webservice.patch(
+            entry["self_link"], "application/json",
+            dumps(dict(relative_build_score=100)))
+        self.assertEqual(401, response.status)
+        new_entry = webservice.get(obj_url, api_version="devel").jsonBody()
+        self.assertEqual(0, new_entry["relative_build_score"])
+
+    def assertScoreWriteableByTeam(self, obj, team):
+        """Members of TEAM can change an object's build score."""
+        with person_logged_in(obj.owner):
+            obj_url = api_url(obj)
+        person = self.factory.makePerson(member_of=[team])
+        webservice = webservice_for_person(
+            person, permission=OAuthPermission.WRITE_PUBLIC)
+        entry = webservice.get(obj_url, api_version="devel").jsonBody()
+        response = webservice.patch(
+            entry["self_link"], "application/json",
+            dumps(dict(relative_build_score=100)))
+        self.assertEqual(209, response.status)
+        self.assertEqual(100, response.jsonBody()["relative_build_score"])
+
+    def test_score_packageset_readable(self):
+        # A packageset's build score is readable by anyone.
+        packageset = self.factory.makePackageset()
+        self.assertScoreReadableByAnyone(packageset)
+
+    def test_score_packageset_forbids_non_buildd_admin(self):
+        # Being the owner of a packageset is not enough to allow changing
+        # its build score, since this affects a site-wide resource.
+        packageset = self.factory.makePackageset()
+        self.assertScoreNotWriteableByOwner(packageset)
+
+    def test_score_packageset_allows_buildd_admin(self):
+        # Buildd admins can change a packageset's build score.
+        packageset = self.factory.makePackageset()
+        self.assertScoreWriteableByTeam(
+            packageset, getUtility(ILaunchpadCelebrities).buildd_admin)
+
+    def test_score_archive_readable(self):
+        # An archive's build score is readable by anyone.
+        archive = self.factory.makeArchive()
+        self.assertScoreReadableByAnyone(archive)
+
+    def test_score_archive_forbids_non_buildd_admin(self):
+        # Being the owner of an archive is not enough to allow changing its
+        # build score, since this affects a site-wide resource.
+        archive = self.factory.makeArchive()
+        self.assertScoreNotWriteableByOwner(archive)
+
+    def test_score_archive_allows_buildd_and_commercial_admin(self):
+        # Buildd and commercial admins can change an archive's build score.
+        archive = self.factory.makeArchive()
+        self.assertScoreWriteableByTeam(
+            archive, getUtility(ILaunchpadCelebrities).buildd_admin)
+        with anonymous_logged_in():
+            self.assertScoreWriteableByTeam(
+                archive, getUtility(ILaunchpadCelebrities).commercial_admin)

=== modified file 'lib/lp/soyuz/tests/test_buildpackagejob.py'
--- lib/lp/soyuz/tests/test_buildpackagejob.py	2013-11-12 00:57:36 +0000
+++ lib/lp/soyuz/tests/test_buildpackagejob.py	2013-11-12 00:57:36 +0000
@@ -5,44 +5,23 @@
 
 from datetime import timedelta
 
-from simplejson import dumps
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
 
-from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.buildmaster.enums import BuildStatus
 from lp.buildmaster.interfaces.builder import IBuilderSet
-from lp.registry.interfaces.pocket import PackagePublishingPocket
-from lp.registry.interfaces.sourcepackage import SourcePackageUrgency
 from lp.services.database.interfaces import IStore
-from lp.services.webapp.interfaces import OAuthPermission
 from lp.soyuz.enums import (
     ArchivePurpose,
     PackagePublishingStatus,
     )
 from lp.soyuz.interfaces.buildfarmbuildjob import IBuildFarmBuildJob
-from lp.soyuz.interfaces.buildpackagejob import (
-    COPY_ARCHIVE_SCORE_PENALTY,
-    IBuildPackageJob,
-    PRIVATE_ARCHIVE_SCORE_BONUS,
-    SCORE_BY_COMPONENT,
-    SCORE_BY_POCKET,
-    SCORE_BY_URGENCY,
-    )
+from lp.soyuz.interfaces.buildpackagejob import IBuildPackageJob
 from lp.soyuz.interfaces.processor import IProcessorSet
 from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
-from lp.testing import (
-    anonymous_logged_in,
-    api_url,
-    person_logged_in,
-    TestCaseWithFactory,
-    )
-from lp.testing.layers import (
-    DatabaseFunctionalLayer,
-    LaunchpadZopelessLayer,
-    )
-from lp.testing.pages import webservice_for_person
+from lp.testing import TestCaseWithFactory
+from lp.testing.layers import LaunchpadZopelessLayer
 
 
 def find_job(test, name, processor='386'):
@@ -199,240 +178,3 @@
         build_farm_job = bq.specific_job
         self.assertProvides(build_farm_job, IBuildPackageJob)
         self.assertProvides(build_farm_job, IBuildFarmBuildJob)
-
-
-class TestBuildPackageJobScore(TestCaseWithFactory):
-
-    layer = DatabaseFunctionalLayer
-
-    def makeBuildJob(self, purpose=None, private=False, component="main",
-                     urgency="high", pocket="RELEASE", section_name=None):
-        if purpose is not None or private:
-            archive = self.factory.makeArchive(
-                purpose=purpose, private=private)
-        else:
-            archive = None
-        spph = self.factory.makeSourcePackagePublishingHistory(
-            archive=archive, component=component, urgency=urgency,
-            section_name=section_name)
-        naked_spph = removeSecurityProxy(spph)  # needed for private archives
-        build = self.factory.makeBinaryPackageBuild(
-            source_package_release=naked_spph.sourcepackagerelease,
-            pocket=pocket)
-        return removeSecurityProxy(build).makeJob()
-
-    # The defaults for pocket, component, and urgency here match those in
-    # makeBuildJob.
-    def assertCorrectScore(self, job, pocket="RELEASE", component="main",
-                           urgency="high", other_bonus=0):
-        self.assertEqual(
-            (SCORE_BY_POCKET[PackagePublishingPocket.items[pocket.upper()]] +
-             SCORE_BY_COMPONENT[component] +
-             SCORE_BY_URGENCY[SourcePackageUrgency.items[urgency.upper()]] +
-             other_bonus), job.score())
-
-    def test_score_unusual_component(self):
-        spph = self.factory.makeSourcePackagePublishingHistory(
-            component="unusual")
-        build = self.factory.makeBinaryPackageBuild(
-            source_package_release=spph.sourcepackagerelease)
-        build.queueBuild()
-        job = build.buildqueue_record.specific_job
-        # For now just test that it doesn't raise an Exception
-        job.score()
-
-    def test_main_release_low_score(self):
-        # 1500 (RELEASE) + 1000 (main) + 5 (low) = 2505.
-        job = self.makeBuildJob(component="main", urgency="low")
-        self.assertCorrectScore(job, "RELEASE", "main", "low")
-
-    def test_copy_archive_main_release_low_score(self):
-        # 1500 (RELEASE) + 1000 (main) + 5 (low) - 2600 (copy archive) = -95.
-        # With this penalty, even language-packs and build retries will be
-        # built before copy archives.
-        job = self.makeBuildJob(
-            purpose="COPY", component="main", urgency="low")
-        self.assertCorrectScore(
-            job, "RELEASE", "main", "low", -COPY_ARCHIVE_SCORE_PENALTY)
-
-    def test_copy_archive_relative_score_is_applied(self):
-        # Per-archive relative build scores are applied, in this case
-        # exactly offsetting the copy-archive penalty.
-        job = self.makeBuildJob(
-            purpose="COPY", component="main", urgency="low")
-        removeSecurityProxy(job.build.archive).relative_build_score = 2600
-        self.assertCorrectScore(
-            job, "RELEASE", "main", "low", -COPY_ARCHIVE_SCORE_PENALTY + 2600)
-
-    def test_archive_negative_relative_score_is_applied(self):
-        # Negative per-archive relative build scores are allowed.
-        job = self.makeBuildJob(component="main", urgency="low")
-        removeSecurityProxy(job.build.archive).relative_build_score = -100
-        self.assertCorrectScore(job, "RELEASE", "main", "low", -100)
-
-    def test_private_archive_bonus_is_applied(self):
-        # Private archives get a bonus of 10000.
-        job = self.makeBuildJob(private=True, component="main", urgency="high")
-        self.assertCorrectScore(
-            job, "RELEASE", "main", "high", PRIVATE_ARCHIVE_SCORE_BONUS)
-
-    def test_main_release_low_recent_score(self):
-        # 1500 (RELEASE) + 1000 (main) + 5 (low) = 2505.
-        job = self.makeBuildJob(component="main", urgency="low")
-        self.assertCorrectScore(job, "RELEASE", "main", "low")
-
-    def test_universe_release_high_five_minutes_score(self):
-        # 1500 (RELEASE) + 250 (universe) + 15 (high) = 1765.
-        job = self.makeBuildJob(component="universe", urgency="high")
-        self.assertCorrectScore(job, "RELEASE", "universe", "high")
-
-    def test_multiverse_release_medium_fifteen_minutes_score(self):
-        # 1500 (RELEASE) + 0 (multiverse) + 10 (medium) = 1510.
-        job = self.makeBuildJob(component="multiverse", urgency="medium")
-        self.assertCorrectScore(job, "RELEASE", "multiverse", "medium")
-
-    def test_main_release_emergency_thirty_minutes_score(self):
-        # 1500 (RELEASE) + 1000 (main) + 20 (emergency) = 2520.
-        job = self.makeBuildJob(component="main", urgency="emergency")
-        self.assertCorrectScore(job, "RELEASE", "main", "emergency")
-
-    def test_restricted_release_low_one_hour_score(self):
-        # 1500 (RELEASE) + 750 (restricted) + 5 (low) = 2255.
-        job = self.makeBuildJob(component="restricted", urgency="low")
-        self.assertCorrectScore(job, "RELEASE", "restricted", "low")
-
-    def test_backports_score(self):
-        # BACKPORTS is the lowest-priority pocket.
-        job = self.makeBuildJob(pocket="BACKPORTS")
-        self.assertCorrectScore(job, "BACKPORTS")
-
-    def test_release_score(self):
-        # RELEASE ranks next above BACKPORTS.
-        job = self.makeBuildJob(pocket="RELEASE")
-        self.assertCorrectScore(job, "RELEASE")
-
-    def test_proposed_updates_score(self):
-        # PROPOSED and UPDATES both rank next above RELEASE.  The reason why
-        # PROPOSED and UPDATES have the same priority is because sources in
-        # both pockets are submitted to the same policy and should reach
-        # their audience as soon as possible (see more information about
-        # this decision in bug #372491).
-        proposed_job = self.makeBuildJob(pocket="PROPOSED")
-        self.assertCorrectScore(proposed_job, "PROPOSED")
-        updates_job = self.makeBuildJob(pocket="UPDATES")
-        self.assertCorrectScore(updates_job, "UPDATES")
-
-    def test_security_updates_score(self):
-        # SECURITY is the top-ranked pocket.
-        job = self.makeBuildJob(pocket="SECURITY")
-        self.assertCorrectScore(job, "SECURITY")
-
-    def test_score_packageset(self):
-        # Package sets alter the score of official packages for their
-        # series.
-        job = self.makeBuildJob(
-            component="main", urgency="low", purpose=ArchivePurpose.PRIMARY)
-        packageset = self.factory.makePackageset(
-            distroseries=job.build.distro_series)
-        removeSecurityProxy(packageset).add(
-            [job.build.source_package_release.sourcepackagename])
-        removeSecurityProxy(packageset).relative_build_score = 100
-        self.assertCorrectScore(job, "RELEASE", "main", "low", 100)
-
-    def test_score_packageset_in_ppa(self):
-        # Package set score boosts don't affect PPA packages.
-        job = self.makeBuildJob(
-            component="main", urgency="low", purpose=ArchivePurpose.PPA)
-        packageset = self.factory.makePackageset(
-            distroseries=job.build.distro_series)
-        removeSecurityProxy(packageset).add(
-            [job.build.source_package_release.sourcepackagename])
-        removeSecurityProxy(packageset).relative_build_score = 100
-        self.assertCorrectScore(job, "RELEASE", "main", "low", 0)
-
-    def test_translations_score(self):
-        # Language packs (the translations section) don't get any
-        # package-specific score bumps. They always have the archive's
-        # base score.
-        job = self.makeBuildJob(section_name='translations')
-        removeSecurityProxy(job.build.archive).relative_build_score = 666
-        self.assertEqual(666, job.score())
-
-    def assertScoreReadableByAnyone(self, obj):
-        """An object's build score is readable by anyone."""
-        with person_logged_in(obj.owner):
-            obj_url = api_url(obj)
-        removeSecurityProxy(obj).relative_build_score = 100
-        webservice = webservice_for_person(
-            self.factory.makePerson(), permission=OAuthPermission.WRITE_PUBLIC)
-        entry = webservice.get(obj_url, api_version="devel").jsonBody()
-        self.assertEqual(100, entry["relative_build_score"])
-
-    def assertScoreNotWriteableByOwner(self, obj):
-        """Being an object's owner does not allow changing its build score.
-
-        This affects a site-wide resource, and is thus restricted to
-        launchpad-buildd-admins.
-        """
-        with person_logged_in(obj.owner):
-            obj_url = api_url(obj)
-        webservice = webservice_for_person(
-            obj.owner, permission=OAuthPermission.WRITE_PUBLIC)
-        entry = webservice.get(obj_url, api_version="devel").jsonBody()
-        response = webservice.patch(
-            entry["self_link"], "application/json",
-            dumps(dict(relative_build_score=100)))
-        self.assertEqual(401, response.status)
-        new_entry = webservice.get(obj_url, api_version="devel").jsonBody()
-        self.assertEqual(0, new_entry["relative_build_score"])
-
-    def assertScoreWriteableByTeam(self, obj, team):
-        """Members of TEAM can change an object's build score."""
-        with person_logged_in(obj.owner):
-            obj_url = api_url(obj)
-        person = self.factory.makePerson(member_of=[team])
-        webservice = webservice_for_person(
-            person, permission=OAuthPermission.WRITE_PUBLIC)
-        entry = webservice.get(obj_url, api_version="devel").jsonBody()
-        response = webservice.patch(
-            entry["self_link"], "application/json",
-            dumps(dict(relative_build_score=100)))
-        self.assertEqual(209, response.status)
-        self.assertEqual(100, response.jsonBody()["relative_build_score"])
-
-    def test_score_packageset_readable(self):
-        # A packageset's build score is readable by anyone.
-        packageset = self.factory.makePackageset()
-        self.assertScoreReadableByAnyone(packageset)
-
-    def test_score_packageset_forbids_non_buildd_admin(self):
-        # Being the owner of a packageset is not enough to allow changing
-        # its build score, since this affects a site-wide resource.
-        packageset = self.factory.makePackageset()
-        self.assertScoreNotWriteableByOwner(packageset)
-
-    def test_score_packageset_allows_buildd_admin(self):
-        # Buildd admins can change a packageset's build score.
-        packageset = self.factory.makePackageset()
-        self.assertScoreWriteableByTeam(
-            packageset, getUtility(ILaunchpadCelebrities).buildd_admin)
-
-    def test_score_archive_readable(self):
-        # An archive's build score is readable by anyone.
-        archive = self.factory.makeArchive()
-        self.assertScoreReadableByAnyone(archive)
-
-    def test_score_archive_forbids_non_buildd_admin(self):
-        # Being the owner of an archive is not enough to allow changing its
-        # build score, since this affects a site-wide resource.
-        archive = self.factory.makeArchive()
-        self.assertScoreNotWriteableByOwner(archive)
-
-    def test_score_archive_allows_buildd_and_commercial_admin(self):
-        # Buildd and commercial admins can change an archive's build score.
-        archive = self.factory.makeArchive()
-        self.assertScoreWriteableByTeam(
-            archive, getUtility(ILaunchpadCelebrities).buildd_admin)
-        with anonymous_logged_in():
-            self.assertScoreWriteableByTeam(
-                archive, getUtility(ILaunchpadCelebrities).commercial_admin)

=== modified file 'lib/lp/translations/browser/translationtemplatesbuild.py'
--- lib/lp/translations/browser/translationtemplatesbuild.py	2012-04-16 23:02:44 +0000
+++ lib/lp/translations/browser/translationtemplatesbuild.py	2013-11-12 00:57:36 +0000
@@ -13,7 +13,7 @@
 from lp.app.browser.tales import DateTimeFormatterAPI
 from lp.registry.interfaces.productseries import IProductSeriesSet
 from lp.services.webapp.publisher import LaunchpadView
-from lp.translations.model.translationtemplatesbuildjob import (
+from lp.translations.model.translationtemplatesbuild import (
     HARDCODED_TRANSLATIONTEMPLATESBUILD_SCORE,
     )
 

=== modified file 'lib/lp/translations/model/translationtemplatesbuild.py'
--- lib/lp/translations/model/translationtemplatesbuild.py	2013-11-12 00:57:36 +0000
+++ lib/lp/translations/model/translationtemplatesbuild.py	2013-11-12 00:57:36 +0000
@@ -5,6 +5,7 @@
 
 __metaclass__ = type
 __all__ = [
+    'HARDCODED_TRANSLATIONTEMPLATESBUILD_SCORE',
     'TranslationTemplatesBuild',
     ]
 
@@ -52,6 +53,9 @@
     )
 
 
+HARDCODED_TRANSLATIONTEMPLATESBUILD_SCORE = 2510
+
+
 class TranslationTemplatesBuild(SpecificBuildFarmJobSourceMixin,
                                 BuildFarmJobMixin, Storm):
     """A `BuildFarmJob` extension for translation templates builds."""
@@ -200,3 +204,10 @@
         if self.log is None:
             return None
         return self.log.http_url
+
+    def calculateScore(self):
+        """See `IBuildFarmJob`."""
+        # Hard-code score for now.  Most PPA jobs start out at 2505;
+        # TranslationTemplateBuildJobs are fast so we want them at a
+        # higher priority.
+        return HARDCODED_TRANSLATIONTEMPLATESBUILD_SCORE

=== modified file 'lib/lp/translations/model/translationtemplatesbuildjob.py'
--- lib/lp/translations/model/translationtemplatesbuildjob.py	2013-09-02 08:11:58 +0000
+++ lib/lp/translations/model/translationtemplatesbuildjob.py	2013-11-12 00:57:36 +0000
@@ -3,7 +3,6 @@
 
 __metaclass__ = type
 __all__ = [
-    'HARDCODED_TRANSLATIONTEMPLATESBUILD_SCORE',
     'TranslationTemplatesBuildJob',
     ]
 
@@ -43,9 +42,6 @@
 from lp.translations.pottery.detect_intltool import is_intltool_structure
 
 
-HARDCODED_TRANSLATIONTEMPLATESBUILD_SCORE = 2510
-
-
 class TranslationTemplatesBuildJob(BuildFarmJobOld, BranchJobDerived):
     """An `IBuildFarmJob` implementation that generates templates.
 
@@ -58,13 +54,6 @@
 
     duration_estimate = timedelta(seconds=10)
 
-    def score(self):
-        """See `IBuildFarmJob`."""
-        # Hard-code score for now.  Most PPA jobs start out at 2505;
-        # TranslationTemplateBuildJobs are fast so we want them at a
-        # higher priority.
-        return HARDCODED_TRANSLATIONTEMPLATESBUILD_SCORE
-
     def cleanUp(self):
         """See `IBuildFarmJob`."""
         # This class is not itself database-backed.  But it delegates to


Follow ups