launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #16174
[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