Merge lp:~michael.nelson/launchpad/db-build-farm-job-model into lp:launchpad/db-devel

Proposed by Michael Nelson
Status: Merged
Approved by: Michael Nelson
Approved revision: no longer in the source branch.
Merged at revision: 9405
Proposed branch: lp:~michael.nelson/launchpad/db-build-farm-job-model
Merge into: lp:launchpad/db-devel
Prerequisite: lp:~michael.nelson/launchpad/db-build-generalisation-db-changes
Diff against target: 484 lines (+327/-37)
6 files modified
lib/canonical/launchpad/interfaces/_schema_circular_imports.py (+5/-0)
lib/lp/buildmaster/configure.zcml (+15/-0)
lib/lp/buildmaster/interfaces/buildfarmjob.py (+82/-14)
lib/lp/buildmaster/model/buildfarmjob.py (+102/-22)
lib/lp/buildmaster/model/packagebuildfarmjob.py (+3/-1)
lib/lp/buildmaster/tests/test_buildfarmjob.py (+120/-0)
To merge this branch: bzr merge lp:~michael.nelson/launchpad/db-build-farm-job-model
Reviewer Review Type Date Requested Status
Jelmer Vernooij (community) Abstain
Abel Deuring (community) code Approve
Review via email: mp+23913@code.launchpad.net

Description of the change

This branch is part of a pipeline for

https://blueprints.edge.launchpad.net/soyuz/+spec/build-generalisation
https://dev.launchpad.net/LEP/GeneralBuildHistories

In particular, this branch turns BuildFarmJob into a concrete class, while at the same time, continuing support temporarily for classes that still expect this to be an in-memory object.

It is dependent on the pending schema patch in a previous branch.

To test
=======

First update the test db schema (required as the db patch still needs to be updated to remove the old build table):
psql launchpad_ftest_template -f database/schema/pending/michaeln-build-generalisation.sql
bin/py database/schema/security.py -d launchpad_ftest_template

And then:
bin/test -vvt test_buildfarmjob -t doc/build.txt -t test_buildqueue -t test_sourcepackagerecipebuild -t test_translationtemplatesbuildjob

The next stage will be a similar conversion for PackageBuildFarmJob, followed by switching the BinaryPackageBuild from the old build table to the new binarypackagebuild table.

To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) wrote :

This branch is part of a pipeline for

https://blueprints.edge.launchpad.net/soyuz/+spec/build-generalisation
https://dev.launchpad.net/LEP/GeneralBuildHistories

In particular, this branch turns BuildFarmJob into a concrete class, while at the same time, continuing support temporarily for classes that still expect this to be an in-memory object.

It is dependent on the pending schema patch in a previous branch.

To test
=======

First update the test db schema (required as the db patch still needs to be updated to remove the old build table):
psql launchpad_ftest_template -f database/schema/pending/michaeln-build-generalisation.sql
bin/py database/schema/security.py -d launchpad_ftest_template

And then:
bin/test -vvt test_buildfarmjob -t doc/build.txt -t test_buildqueue -t test_sourcepackagerecipebuild -t test_translationtemplatesbuildjob

The next stage will be a similar conversion for PackageBuildFarmJob, followed by switching the BinaryPackageBuild from the old build table to the new binarypackagebuild table.

review: Approve (code)
Revision history for this message
Abel Deuring (adeuring) wrote :
Download full text (4.8 KiB)

Hi Michael,

a nice branch; just just have a few formal nitpicks.

> === modified file 'lib/lp/buildmaster/interfaces/buildfarmjob.py'
> --- lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-04-21 07:15:40 +0000
> +++ lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-04-22 07:42:23 +0000
> @@ -9,15 +9,20 @@
>
> __all__ = [
> 'IBuildFarmJob',
> + 'IBuildFarmJobSource',
> 'IBuildFarmJobDerived',
> 'BuildFarmJobType',
> ]
>
> from zope.interface import Interface, Attribute
> -
> -from canonical.launchpad import _
> +from zope.schema import Bool, Choice, Datetime
> from lazr.enum import DBEnumeratedType, DBItem
> from lazr.restful.fields import Reference
> +
> +from canonical.launchpad import _
> +from canonical.launchpad.interfaces.librarian import ILibraryFileAlias
> +
> +from lp.buildmaster.interfaces.builder import IBuilder
> from lp.soyuz.interfaces.processor import IProcessor
>
>
> @@ -56,6 +61,66 @@
> class IBuildFarmJob(Interface):
> """Operations that jobs for the build farm must implement."""
>
> + id = Attribute('The build farm job ID.')
> +
> + processor = Reference(
> + IProcessor, title=_("Processor"), required=False, readonly=True,
> + description=_(
> + "The Processor required by this build farm job. "
> + "For processor-independent job types please return None."))

It is perhaps my limited English knowledge, but this sounds to me
like a polite request to the implementation class to do the right
thing ;) What about "should|must be None for processor-independent jobs"?

> +
> + virtualized = Bool(
> + title=_('Virtualized'), required=False, readonly=True,
> + description=_(
> + "The virtualization setting required by this build farm job. "
> + "For job types that do not care about virtualization please "
> + "return None."))

Same here.

> +
> + date_created = Datetime(
> + title=_("Date created"), required=True, readonly=True,
> + description=_("The timestamp when the build farm job was created."))
> +
> + date_started = Datetime(
> + title=_("Date started"), required=False, readonly=True,
> + description=_("The timestamp when the build farm job was started."))
> +
> + date_finished = Datetime(
> + title=_("Date finished"), required=False, readonly=True,
> + description=_("The timestamp when the build farm job was finished."))
> +
> + date_first_dispatched = Datetime(
> + title=_("Date finished"), required=False, readonly=True,
> + description=_("The timestamp when the build farm job was finished."))

s/finished/dispatched/

> +
> + builder = Reference(
> + title=_("Builder"), schema=IBuilder, required=False, readonly=True,
> + description=_("The builder assigned to this job."))
> +
> + status = Choice(
> + title=_('Status'), required=True,
> + # Really PackagePublishingPocket, patched in

s/PackagePublishingPocket/BuildStatus/

[...]

> @@ -149,3 +202,17 @@
> accurately based on this job's properties.
> """
>
> +
> +class IBuildFarmJobSource(Interface):
> + """A utility of Build...

Read more...

Revision history for this message
Michael Nelson (michael.nelson) wrote :
Download full text (6.0 KiB)

On Fri, Apr 23, 2010 at 12:53 PM, Abel Deuring
<email address hidden> wrote:
> Hi Michael,
>
> a nice branch; just just have a few formal nitpicks.

Thanks Abel, comments below.

>
>> === modified file 'lib/lp/buildmaster/interfaces/buildfarmjob.py'
>> --- lib/lp/buildmaster/interfaces/buildfarmjob.py     2010-04-21 07:15:40 +0000
>> +++ lib/lp/buildmaster/interfaces/buildfarmjob.py     2010-04-22 07:42:23 +0000
>> @@ -9,15 +9,20 @@
>>
>>  __all__ = [
>>      'IBuildFarmJob',
>> +    'IBuildFarmJobSource',
>>      'IBuildFarmJobDerived',
>>      'BuildFarmJobType',
>>      ]
>>
>>  from zope.interface import Interface, Attribute
>> -
>> -from canonical.launchpad import _
>> +from zope.schema import Bool, Choice, Datetime
>>  from lazr.enum import DBEnumeratedType, DBItem
>>  from lazr.restful.fields import Reference
>> +
>> +from canonical.launchpad import _
>> +from canonical.launchpad.interfaces.librarian import ILibraryFileAlias
>> +
>> +from lp.buildmaster.interfaces.builder import IBuilder
>>  from lp.soyuz.interfaces.processor import IProcessor
>>
>>
>> @@ -56,6 +61,66 @@
>>  class IBuildFarmJob(Interface):
>>      """Operations that jobs for the build farm must implement."""
>>
>> +    id = Attribute('The build farm job ID.')
>> +
>> +    processor = Reference(
>> +        IProcessor, title=_("Processor"), required=False, readonly=True,
>> +        description=_(
>> +            "The Processor required by this build farm job. "
>> +            "For processor-independent job types please return None."))
>
> It is perhaps my limited English knowledge, but this sounds to me
> like a polite request to the implementation class to do the right
> thing ;) What about "should|must be None for processor-independent jobs"?

Yep. This was just a copy-n-paste, but you're right. Updated.

>
>
>> +
>> +    virtualized = Bool(
>> +        title=_('Virtualized'), required=False, readonly=True,
>> +        description=_(
>> +            "The virtualization setting required by this build farm job. "
>> +            "For job types that do not care about virtualization please "
>> +            "return None."))
>
> Same here.

Ditto.

>
>> +
>> +    date_created = Datetime(
>> +        title=_("Date created"), required=True, readonly=True,
>> +        description=_("The timestamp when the build farm job was created."))
>> +
>> +    date_started = Datetime(
>> +        title=_("Date started"), required=False, readonly=True,
>> +        description=_("The timestamp when the build farm job was started."))
>> +
>> +    date_finished = Datetime(
>> +        title=_("Date finished"), required=False, readonly=True,
>> +        description=_("The timestamp when the build farm job was finished."))
>> +
>> +    date_first_dispatched = Datetime(
>> +        title=_("Date finished"), required=False, readonly=True,
>> +        description=_("The timestamp when the build farm job was finished."))
>
> s/finished/dispatched/

Done.

>
>> +
>> +    builder = Reference(
>> +        title=_("Builder"), schema=IBuilder, required=False, readonly=True,
>> +        description=_("The builder assigned to this job."))
>> +
>> +    status = Choice(
>> +        title=_('Status'...

Read more...

1=== modified file 'lib/lp/buildmaster/interfaces/buildfarmjob.py'
2--- lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-04-21 16:08:35 +0000
3+++ lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-04-23 12:13:43 +0000
4@@ -67,14 +67,14 @@
5 IProcessor, title=_("Processor"), required=False, readonly=True,
6 description=_(
7 "The Processor required by this build farm job. "
8- "For processor-independent job types please return None."))
9+ "This should be None for processor-independent job types."))
10
11 virtualized = Bool(
12 title=_('Virtualized'), required=False, readonly=True,
13 description=_(
14 "The virtualization setting required by this build farm job. "
15- "For job types that do not care about virtualization please "
16- "return None."))
17+ "This should be None for job types that do not care whether "
18+ "they run virtualized."))
19
20 date_created = Datetime(
21 title=_("Date created"), required=True, readonly=True,
22@@ -90,7 +90,7 @@
23
24 date_first_dispatched = Datetime(
25 title=_("Date finished"), required=False, readonly=True,
26- description=_("The timestamp when the build farm job was finished."))
27+ description=_("The timestamp when the build farm job was dispatched."))
28
29 builder = Reference(
30 title=_("Builder"), schema=IBuilder, required=False, readonly=True,
31@@ -98,7 +98,7 @@
32
33 status = Choice(
34 title=_('Status'), required=True,
35- # Really PackagePublishingPocket, patched in
36+ # Really BuildStatus, patched in
37 # _schema_circular_imports.py
38 vocabulary=DBEnumeratedType,
39 description=_("The current status of the job."))
40
41=== modified file 'lib/lp/buildmaster/model/buildfarmjob.py'
42--- lib/lp/buildmaster/model/buildfarmjob.py 2010-04-21 16:08:35 +0000
43+++ lib/lp/buildmaster/model/buildfarmjob.py 2010-04-23 12:20:34 +0000
44@@ -71,10 +71,8 @@
45 job_type = DBEnum(
46 name='job_type', allow_none=False, enum=BuildFarmJobType)
47
48- def __init__(self, job_type, status=None, processor=None,
49- virtualized=None):
50- if status is None:
51- status = BuildStatus.NEEDSBUILD
52+ def __init__(self, job_type, status=BuildStatus.NEEDSBUILD,
53+ processor=None, virtualized=None):
54 self.job_type, self.status, self.process, self.virtualized = (
55 job_type,
56 status,
57@@ -83,7 +81,7 @@
58 )
59
60 @classmethod
61- def new(cls, job_type, status=None, processor=None,
62+ def new(cls, job_type, status=BuildStatus.NEEDSBUILD, processor=None,
63 virtualized=None):
64 """See `IBuildFarmJobSource`."""
65 build_farm_job = BuildFarmJob(
Revision history for this message
Jelmer Vernooij (jelmer) :
review: Abstain

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/interfaces/_schema_circular_imports.py'
2--- lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2010-04-23 03:16:22 +0000
3+++ lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2010-05-05 15:46:51 +0000
4@@ -34,6 +34,7 @@
5 from lp.bugs.interfaces.bugtracker import IBugTracker
6 from lp.bugs.interfaces.bugwatch import IBugWatch
7 from lp.buildmaster.interfaces.buildbase import BuildStatus
8+from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob
9 from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuild
10 from lp.soyuz.interfaces.buildrecords import IHasBuildRecords
11 from lp.blueprints.interfaces.specification import ISpecification
12@@ -271,6 +272,10 @@
13 patch_plain_parameter_type(
14 IArchive, 'deletePackagesetUploader', 'packageset', IPackageset)
15
16+
17+# IBuildFarmJob
18+IBuildFarmJob['status'].vocabulary = BuildStatus
19+
20 # IDistribution
21 IDistribution['series'].value_type.schema = IDistroSeries
22 patch_reference_property(
23
24=== modified file 'lib/lp/buildmaster/configure.zcml'
25--- lib/lp/buildmaster/configure.zcml 2010-03-05 13:52:32 +0000
26+++ lib/lp/buildmaster/configure.zcml 2010-05-05 15:46:51 +0000
27@@ -42,6 +42,21 @@
28 permission="zope.Public"/>
29
30
31+ <!-- BuildFarmJob -->
32+ <class
33+ class="lp.buildmaster.model.buildfarmjob.BuildFarmJob">
34+ <allow
35+ interface="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJob" />
36+ </class>
37+ <securedutility
38+ component="lp.buildmaster.model.buildfarmjob.BuildFarmJob"
39+ provides="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJobSource">
40+
41+ <allow
42+ interface="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJobSource" />
43+ </securedutility>
44+
45+
46 <!-- BuildQueue -->
47 <class
48 class="lp.buildmaster.model.buildqueue.BuildQueue">
49
50=== modified file 'lib/lp/buildmaster/interfaces/buildfarmjob.py'
51--- lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-04-28 08:24:54 +0000
52+++ lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-05-05 15:46:51 +0000
53@@ -9,15 +9,20 @@
54
55 __all__ = [
56 'IBuildFarmJob',
57+ 'IBuildFarmJobSource',
58 'IBuildFarmJobDerived',
59 'BuildFarmJobType',
60 ]
61
62 from zope.interface import Interface, Attribute
63-
64-from canonical.launchpad import _
65+from zope.schema import Bool, Choice, Datetime
66 from lazr.enum import DBEnumeratedType, DBItem
67 from lazr.restful.fields import Reference
68+
69+from canonical.launchpad import _
70+from canonical.launchpad.interfaces.librarian import ILibraryFileAlias
71+
72+from lp.buildmaster.interfaces.builder import IBuilder
73 from lp.soyuz.interfaces.processor import IProcessor
74
75
76@@ -56,6 +61,66 @@
77 class IBuildFarmJob(Interface):
78 """Operations that jobs for the build farm must implement."""
79
80+ id = Attribute('The build farm job ID.')
81+
82+ processor = Reference(
83+ IProcessor, title=_("Processor"), required=False, readonly=True,
84+ description=_(
85+ "The Processor required by this build farm job. "
86+ "This should be None for processor-independent job types."))
87+
88+ virtualized = Bool(
89+ title=_('Virtualized'), required=False, readonly=True,
90+ description=_(
91+ "The virtualization setting required by this build farm job. "
92+ "This should be None for job types that do not care whether "
93+ "they run virtualized."))
94+
95+ date_created = Datetime(
96+ title=_("Date created"), required=True, readonly=True,
97+ description=_("The timestamp when the build farm job was created."))
98+
99+ date_started = Datetime(
100+ title=_("Date started"), required=False, readonly=True,
101+ description=_("The timestamp when the build farm job was started."))
102+
103+ date_finished = Datetime(
104+ title=_("Date finished"), required=False, readonly=True,
105+ description=_("The timestamp when the build farm job was finished."))
106+
107+ date_first_dispatched = Datetime(
108+ title=_("Date finished"), required=False, readonly=True,
109+ description=_("The timestamp when the build farm job was dispatched."))
110+
111+ builder = Reference(
112+ title=_("Builder"), schema=IBuilder, required=False, readonly=True,
113+ description=_("The builder assigned to this job."))
114+
115+ status = Choice(
116+ title=_('Status'), required=True,
117+ # Really BuildStatus, patched in
118+ # _schema_circular_imports.py
119+ vocabulary=DBEnumeratedType,
120+ description=_("The current status of the job."))
121+
122+ log = Reference(
123+ schema=ILibraryFileAlias, required=False,
124+ title=_(
125+ "The LibraryFileAlias containing the entire log for this job."))
126+
127+ job_type = Choice(
128+ title=_("Job type"), required=True, readonly=True,
129+ vocabulary=BuildFarmJobType,
130+ description=_("The specific type of job."))
131+
132+ # XXX 2010-04-21 michael.nelson bug=567922. This property
133+ # can be removed once all *Build classes use the concrete
134+ # BuildFarmJob.
135+ has_concrete_build_farm_job = Bool(
136+ title=_('Has concrete build farm job'), required=False,
137+ readonly=True, description=_(
138+ 'Whether this instance is or has a concrete build farm job.'))
139+
140 def score():
141 """Calculate a job score appropriate for the job type in question."""
142
143@@ -77,18 +142,6 @@
144 def jobAborted():
145 """'Job aborted' life cycle event, handle as appropriate."""
146
147- processor = Reference(
148- IProcessor, title=_("Processor"),
149- description=_(
150- "The Processor required by this build farm job. "
151- "For processor-independent job types please return None."))
152-
153- virtualized = Attribute(
154- _(
155- "The virtualization setting required by this build farm job. "
156- "For job types that do not care about virtualization please "
157- "return None."))
158-
159
160 class IBuildFarmJobDerived(Interface):
161 """Common functionality required by classes delegating IBuildFarmJob.
162@@ -151,3 +204,18 @@
163
164 def cleanUp():
165 """Job's finished. Delete its supporting data."""
166+
167+
168+class IBuildFarmJobSource(Interface):
169+ """A utility of BuildFarmJob used to create _things_."""
170+
171+ def new(job_type, status=None, processor=None,
172+ virtualized=None):
173+ """Create a new `IBuildFarmJob`.
174+
175+ :param job_type: A `BuildFarmJobType` item.
176+ :param status: A `BuildStatus` item, defaulting to PENDING.
177+ :param processor: An optional processor for this job.
178+ :param virtualized: An optional boolean indicating whether
179+ this job should be run virtualized.
180+ """
181
182=== modified file 'lib/lp/buildmaster/model/buildfarmjob.py'
183--- lib/lp/buildmaster/model/buildfarmjob.py 2010-04-28 08:24:54 +0000
184+++ lib/lp/buildmaster/model/buildfarmjob.py 2010-05-05 15:46:51 +0000
185@@ -12,23 +12,93 @@
186
187 from lazr.delegates import delegates
188
189+import hashlib
190+import pytz
191+
192+from storm.info import get_obj_info
193+from storm.locals import Bool, DateTime, Int, Reference, Storm
194+from storm.store import Store
195+
196 from zope.component import getUtility
197-from zope.interface import implements
198+from zope.interface import classProvides, implements
199 from zope.security.proxy import removeSecurityProxy
200
201-from storm.store import Store
202-
203+from canonical.database.constants import UTC_NOW
204+from canonical.database.enumcol import DBEnum
205+from canonical.launchpad.interfaces.lpstorm import IMasterStore
206 from canonical.launchpad.webapp.interfaces import (
207 DEFAULT_FLAVOR, IStoreSelector, MAIN_STORE)
208
209+from lp.buildmaster.interfaces.buildbase import BuildStatus
210 from lp.buildmaster.interfaces.buildfarmjob import (
211- IBuildFarmJob, IBuildFarmJobDerived)
212+ BuildFarmJobType, IBuildFarmJob, IBuildFarmJobDerived,
213+ IBuildFarmJobSource)
214 from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet
215
216
217-class BuildFarmJob:
218+class BuildFarmJob(Storm):
219 """A base implementation for `IBuildFarmJob` classes."""
220+
221+ __storm_table__ = 'BuildFarmJob'
222+
223 implements(IBuildFarmJob)
224+ classProvides(IBuildFarmJobSource)
225+
226+ id = Int(primary=True)
227+
228+ processor_id = Int(name='processor', allow_none=True)
229+ processor = Reference(processor_id, 'Processor.id')
230+
231+ virtualized = Bool()
232+
233+ date_created = DateTime(
234+ name='date_created', allow_none=False, tzinfo=pytz.UTC)
235+
236+ date_started = DateTime(
237+ name='date_started', allow_none=True, tzinfo=pytz.UTC)
238+
239+ date_finished = DateTime(
240+ name='date_finished', allow_none=True, tzinfo=pytz.UTC)
241+
242+ date_first_dispatched = DateTime(
243+ name='date_first_dispatched', allow_none=True, tzinfo=pytz.UTC)
244+
245+ builder_id = Int(name='builder', allow_none=True)
246+ builder = Reference(builder_id, 'Builder.id')
247+
248+ status = DBEnum(name='status', allow_none=False, enum=BuildStatus)
249+
250+ log_id = Int(name='log', allow_none=True)
251+ log = Reference(log_id, 'LibraryFileAlias.id')
252+
253+ job_type = DBEnum(
254+ name='job_type', allow_none=False, enum=BuildFarmJobType)
255+
256+ def __init__(self, job_type, status=BuildStatus.NEEDSBUILD,
257+ processor=None, virtualized=None):
258+ self.job_type, self.status, self.process, self.virtualized = (
259+ job_type,
260+ status,
261+ processor,
262+ virtualized,
263+ )
264+
265+ @classmethod
266+ def new(cls, job_type, status=BuildStatus.NEEDSBUILD, processor=None,
267+ virtualized=None):
268+ """See `IBuildFarmJobSource`."""
269+ build_farm_job = BuildFarmJob(
270+ job_type, status, processor, virtualized)
271+
272+ store = IMasterStore(BuildFarmJob)
273+ store.add(build_farm_job)
274+ return build_farm_job
275+
276+ @property
277+ def has_concrete_build_farm_job(self):
278+ """See `IBuildFarmJob`."""
279+ # Check if the object has been added to the store.
280+ return get_obj_info(self).get('store') is not None
281
282 def score(self):
283 """See `IBuildFarmJob`."""
284@@ -48,25 +118,34 @@
285
286 def jobStarted(self):
287 """See `IBuildFarmJob`."""
288- pass
289+ if not self.has_concrete_build_farm_job:
290+ return
291+ self.status = BuildStatus.BUILDING
292+ # The build started, set the start time if not set already.
293+ self.date_started = UTC_NOW
294+ if self.date_first_dispatched is None:
295+ self.date_first_dispatched = UTC_NOW
296
297 def jobReset(self):
298 """See `IBuildFarmJob`."""
299- pass
300-
301- def jobAborted(self):
302- """See `IBuildFarmJob`."""
303- pass
304-
305- @property
306- def processor(self):
307- """See `IBuildFarmJob`."""
308- return None
309-
310- @property
311- def virtualized(self):
312- """See `IBuildFarmJob`."""
313- return None
314+ if not self.has_concrete_build_farm_job:
315+ return
316+ self.status = BuildStatus.NEEDSBUILD
317+ self.date_started = None
318+
319+ # The implementation of aborting a job is the same as resetting
320+ # a job.
321+ jobAborted = jobReset
322+
323+ @staticmethod
324+ def addCandidateSelectionCriteria(processor, virtualized):
325+ """See `IBuildFarmJob`."""
326+ return ('')
327+
328+ @staticmethod
329+ def postprocessCandidate(job, logger):
330+ """See `IBuildFarmJob`."""
331+ return True
332
333
334 class BuildFarmJobDerived:
335@@ -92,7 +171,8 @@
336
337 Sub-classes can override as required.
338 """
339- self._build_farm_job = BuildFarmJob()
340+ self._build_farm_job = BuildFarmJob(
341+ job_type=BuildFarmJobType.PACKAGEBUILD)
342
343 @classmethod
344 def getByJob(cls, job):
345
346=== modified file 'lib/lp/buildmaster/model/packagebuildfarmjob.py'
347--- lib/lp/buildmaster/model/packagebuildfarmjob.py 2010-04-28 08:24:54 +0000
348+++ lib/lp/buildmaster/model/packagebuildfarmjob.py 2010-05-05 15:46:51 +0000
349@@ -26,7 +26,9 @@
350 itself a concrete class. This class (PackageBuildFarmJob)
351 will also be renamed PackageBuild and turned into a concrete class.
352 """
353- super(PackageBuildFarmJob, self).__init__()
354+ # Classes that initialise with a build are not yet using
355+ # the concrete class, so we don't call the superclass'
356+ # initialisation.
357 self.build = build
358
359 def getTitle(self):
360
361=== added file 'lib/lp/buildmaster/tests/test_buildfarmjob.py'
362--- lib/lp/buildmaster/tests/test_buildfarmjob.py 1970-01-01 00:00:00 +0000
363+++ lib/lp/buildmaster/tests/test_buildfarmjob.py 2010-05-05 15:46:51 +0000
364@@ -0,0 +1,120 @@
365+# Copyright 2010 Canonical Ltd. This software is licensed under the
366+# GNU Affero General Public License version 3 (see the file LICENSE).
367+
368+"""Tests for `IBuildFarmJob`."""
369+
370+__metaclass__ = type
371+
372+import unittest
373+
374+from storm.store import Store
375+from zope.component import getUtility
376+
377+from canonical.database.sqlbase import flush_database_updates
378+from canonical.testing.layers import DatabaseFunctionalLayer
379+
380+from lp.buildmaster.interfaces.buildbase import BuildStatus
381+from lp.buildmaster.interfaces.buildfarmjob import (
382+ BuildFarmJobType, IBuildFarmJob, IBuildFarmJobSource)
383+from lp.buildmaster.model.buildfarmjob import BuildFarmJob
384+from lp.testing import TestCaseWithFactory
385+
386+
387+class TestBuildFarmJob(TestCaseWithFactory):
388+ """Tests for the build farm job object."""
389+
390+ layer = DatabaseFunctionalLayer
391+
392+ def setUp(self):
393+ """Create a build farm job with which to test."""
394+ super(TestBuildFarmJob, self).setUp()
395+ self.build_farm_job = self.makeBuildFarmJob()
396+
397+ def makeBuildFarmJob(self):
398+ return getUtility(IBuildFarmJobSource).new(
399+ job_type=BuildFarmJobType.PACKAGEBUILD)
400+
401+ def test_has_concrete_build_farm_job(self):
402+ # This temporary property returns true if the instance
403+ # corresponds to a concrete database record, even if
404+ # db updates have not yet been flushed, and false
405+ # otherwise.
406+ concrete_build_farm_job = self.makeBuildFarmJob()
407+ self.failUnless(concrete_build_farm_job.has_concrete_build_farm_job)
408+
409+ mem_build_farm_job = BuildFarmJob(
410+ job_type=BuildFarmJobType.PACKAGEBUILD)
411+ self.failIf(mem_build_farm_job.has_concrete_build_farm_job)
412+
413+ def test_providesInterface(self):
414+ # BuildFarmJob provides IBuildFarmJob
415+ self.assertProvides(self.build_farm_job, IBuildFarmJob)
416+
417+ def test_saves_record(self):
418+ # A build farm job can be stored in the database.
419+ flush_database_updates()
420+ store = Store.of(self.build_farm_job)
421+ retrieved_job = store.find(
422+ BuildFarmJob,
423+ BuildFarmJob.id == self.build_farm_job.id).one()
424+ self.assertEqual(self.build_farm_job, retrieved_job)
425+
426+ def test_default_values(self):
427+ # A build farm job defaults to the NEEDSBUILD status.
428+ # We flush the database updates to ensure sql defaults
429+ # are set for various attributes.
430+ flush_database_updates()
431+ self.assertEqual(
432+ BuildStatus.NEEDSBUILD, self.build_farm_job.status)
433+ # The date_created is set automatically.
434+ self.assertTrue(self.build_farm_job.date_created is not None)
435+ # The job type is required to create a build farm job.
436+ self.assertEqual(
437+ BuildFarmJobType.PACKAGEBUILD, self.build_farm_job.job_type)
438+ # Other attributes are unset by default.
439+ self.assertEqual(None, self.build_farm_job.processor)
440+ self.assertEqual(None, self.build_farm_job.virtualized)
441+ self.assertEqual(None, self.build_farm_job.date_started)
442+ self.assertEqual(None, self.build_farm_job.date_finished)
443+ self.assertEqual(None, self.build_farm_job.date_first_dispatched)
444+ self.assertEqual(None, self.build_farm_job.builder)
445+ self.assertEqual(None, self.build_farm_job.log)
446+
447+ def test_unimplemented_methods(self):
448+ # A build farm job leaves the implementation of various
449+ # methods for derived classes.
450+ self.assertRaises(NotImplementedError, self.build_farm_job.score)
451+ self.assertRaises(NotImplementedError, self.build_farm_job.getName)
452+ self.assertRaises(NotImplementedError, self.build_farm_job.getTitle)
453+
454+ def test_jobStarted(self):
455+ # Starting a job sets the date_started and status, as well as
456+ # the date first dispatched, if it is the first dispatch of
457+ # this job.
458+ self.build_farm_job.jobStarted()
459+ self.assertTrue(self.build_farm_job.date_first_dispatched is not None)
460+ self.assertTrue(self.build_farm_job.date_started is not None)
461+ self.assertEqual(
462+ BuildStatus.BUILDING, self.build_farm_job.status)
463+
464+ def test_jobReset(self):
465+ # Resetting a job sets its status back to NEEDSBUILD and unsets
466+ # the date_started.
467+ self.build_farm_job.jobStarted()
468+ self.build_farm_job.jobReset()
469+ self.failUnlessEqual(
470+ BuildStatus.NEEDSBUILD, self.build_farm_job.status)
471+ self.failUnless(self.build_farm_job.date_started is None)
472+
473+ def test_jobAborted(self):
474+ # Aborting a job sets its status back to NEEDSBUILD and unsets
475+ # the date_started.
476+ self.build_farm_job.jobStarted()
477+ self.build_farm_job.jobAborted()
478+ self.failUnlessEqual(
479+ BuildStatus.NEEDSBUILD, self.build_farm_job.status)
480+ self.failUnless(self.build_farm_job.date_started is None)
481+
482+
483+def test_suite():
484+ return unittest.TestLoader().loadTestsFromName(__name__)

Subscribers

People subscribed via source and target branches

to status/vote changes: