Merge lp:~jtv/launchpad/db-bug-499404 into lp:launchpad/db-devel

Proposed by Jeroen T. Vermeulen
Status: Rejected
Rejected by: Jeroen T. Vermeulen
Proposed branch: lp:~jtv/launchpad/db-bug-499404
Merge into: lp:launchpad/db-devel
Diff against target: 462 lines (+376/-6)
9 files modified
database/schema/comments.sql (+5/-0)
database/schema/patch-2207-22-0.sql (+16/-0)
database/schema/security.cfg (+1/-0)
lib/lp/buildmaster/interfaces/buildfarmjob.py (+6/-6)
lib/lp/translations/configure.zcml (+16/-0)
lib/lp/translations/interfaces/translationtemplatesbuildjob.py (+52/-0)
lib/lp/translations/model/translationtemplatesbuildbehavior.py (+81/-0)
lib/lp/translations/model/translationtemplatesbuildjob.py (+93/-0)
lib/lp/translations/tests/test_translationtemplatesbuildjob.py (+106/-0)
To merge this branch: bzr merge lp:~jtv/launchpad/db-bug-499404
Reviewer Review Type Date Requested Status
Jonathan Lange (community) db Needs Fixing
Tim Penhey (community) Disapprove
Stuart Bishop (community) db Approve
Muharem Hrnjadovic (community) code Approve
Review via email: mp+16564@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

= Bug 499404 =

This implements some new classes that will be needed for Rosetta to run jobs on the generalised build farm. The job in question is generating translation templates based on a source-code branch, and will require a lot more work before it goes operational.

Generalisation of the build farm is ongoing work, so its APIs etc. are not complete yet. What you find here is:

1. An implementation of IBuildFarmJob—a new class representing a job that can be dispatched to the build farm. It's called TranslationTemplatesBuildJob. The only "payload" in this new class is a reference to the branch that the job is to check out. There's also a reference to Job, which holds the generic part of the jobbing information.

2. A BuildFarmJobSet utility—to produce and navigate jobs of the new type. It also creates a BuildQueue record for each job it creates; these records are matched to the records of the new type in that they both refer to the same Job record.

3. A new IBuildFarmJobBehavior implementation for dispatching the new job type to the build farm, with an adapter associating it with TranslationTemplatesBuildJob. More may be needed there in the future.

There's not much I can do to test the main IBuildFarmJobBehavior method right now; we'll need more of the building to be operational before we get to that. But work is in progress and I'm filing separate bugs for this.

To test:
{{{
./bin/test -vv -t translationtemplatesbuild
}}}

No lint.

Jeroen

Revision history for this message
Muharem Hrnjadovic (al-maisan) wrote :

This is still very much work in progress but as such it looks good!

review: Approve (code)
Revision history for this message
Stuart Bishop (stub) wrote :

Mainly fine.

I prefer the non-unique index names to have an __idx suffix (avoids some probably non-existant namespace issues).

The ON DELETE CASCADE is an issue. If someone attempts to delete a Branch that has a running job, I suspect we actually want to generate an error (or will things somehow progress just fine despite the branch disappearing part of the way through the process?). There is quite a bit of logic in branch deletion on when branches can be deleted, and the DB constraints serve as a safety net for that. The ON DELETE CASCADE on the job column seems sane.

The DB patch is already using an assigned patch number, so nothing to change there.

review: Needs Information (db)
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

> I prefer the non-unique index names to have an __idx suffix (avoids some
> probably non-existant namespace issues).

Done.

> The ON DELETE CASCADE is an issue. If someone attempts to delete a Branch that
> has a running job, I suspect we actually want to generate an error (or will
> things somehow progress just fine despite the branch disappearing part of the
> way through the process?). There is quite a bit of logic in branch deletion on
> when branches can be deleted, and the DB constraints serve as a safety net for
> that. The ON DELETE CASCADE on the job column seems sane.

I didn't feel like weighing down that cleanup logic even further—only to simulate a regular cascade AFAICS. But I'll go the safe route and remove the cascading for now.

Revision history for this message
Jonathan Lange (jml) wrote :

The database patch looks good, but it leaves me wondering, where do the results of the job go?

On the code side, the interface that you have there looks an awful lot like IBranchJob -- should it subclass from it? A lot of the branch jobs use IFooJobSource, rather than IFooJobSet. Perhaps it's worth considering using similar names.

jml

review: Needs Information (db)
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

> The database patch looks good, but it leaves me wondering, where do the
> results of the job go?

That goes through completely separate channels. The slaves that run these jobs are untrusted, so they're not allowed anywhere near the database. A master-side script will pick up the files they produce (as already happens for the existing Soyuz build jobs) and "upload" them to the regular Translations import queue.

> On the code side, the interface that you have there looks an awful lot like
> IBranchJob -- should it subclass from it? A lot of the branch jobs use
> IFooJobSource, rather than IFooJobSet. Perhaps it's worth considering using
> similar names.

An interesting idea, but this class doesn't have a "metadata" attribute yet. It may still sprout one; at that point it'd be the sensible thing to do. I've added a note.

Revision history for this message
Stuart Bishop (stub) wrote :

Fine if we drop ON DELETE CASCADE from the branch foreign key definition.

review: Approve (db)
Revision history for this message
Tim Penhey (thumper) wrote :

I don't think this database patch is necessary. The is a branch job, it should use the branch job table.

review: Disapprove
Revision history for this message
Stuart Bishop (stub) wrote :

> Fine if we drop ON DELETE CASCADE from the branch foreign key definition.

And change the SERIAL UNIQUE to SERIAL PRIMARY KEY.

(which we would have picked up when staging failed to rebuild since we can't replicate tables without primary keys).

Using BranchJob may indeed be a better alternative - it already has everything you need. If you want a separate table I think we need some sort of rationalization.

Revision history for this message
Jonathan Lange (jml) wrote :

Thinking more about it, I think that Tim is right -- we should use BranchJob for this. There are other branch job implementations with empty metadata dicts -- it's an entirely cromulent approach, and we shouldn't add new tables without a good reason.

Revision history for this message
Jonathan Lange (jml) :
review: Needs Fixing (db)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'database/schema/comments.sql'
2--- database/schema/comments.sql 2009-12-01 13:45:58 +0000
3+++ database/schema/comments.sql 2010-01-06 10:28:19 +0000
4@@ -1837,6 +1837,11 @@
5 COMMENT ON COLUMN TranslationImportQueueEntry.status IS 'The status of the import: 1 Approved, 2 Imported, 3 Deleted, 4 Failed, 5 Needs Review, 6 Blocked.';
6 COMMENT ON COLUMN TranslationImportQueueEntry.error_output IS 'Error output from last import attempt.';
7
8+-- TranslationTemplatesBuildJob
9+COMMENT ON TABLE TranslationTemplatesBuildJob IS 'Buildfarm job for generating translation templates from a branch.';
10+COMMENT ON COLUMN TranslationTemplatesBuildJob.branch IS 'Source code branch that the job will generate templates for.';
11+COMMENT ON COLUMN TranslationTemplatesBuildJob.job IS 'Base Job details.';
12+
13 -- Archive
14 COMMENT ON TABLE Archive IS 'A package archive. Commonly either a distribution''s main_archive or a ppa''s archive.';
15 COMMENT ON COLUMN Archive.owner IS 'Identifies the PPA owner when it has one.';
16
17=== added file 'database/schema/patch-2207-22-0.sql'
18--- database/schema/patch-2207-22-0.sql 1970-01-01 00:00:00 +0000
19+++ database/schema/patch-2207-22-0.sql 2010-01-06 10:28:20 +0000
20@@ -0,0 +1,16 @@
21+-- Copyright 2009 Canonical Ltd. This software is licensed under the
22+-- GNU Affero General Public License version 3 (see the file LICENSE).
23+
24+SET client_min_messages=ERROR;
25+
26+CREATE TABLE TranslationTemplatesBuildJob (
27+ id SERIAL UNIQUE,
28+ branch integer NOT NULL REFERENCES Branch(id),
29+ job integer NOT NULL REFERENCES Job(id) ON DELETE CASCADE);
30+
31+CREATE INDEX translationtemplatesbuildjob__branch__idx
32+ ON TranslationTemplatesBuildJob(branch);
33+CREATE INDEX translationtemplatesbuildjob__job__idx
34+ ON TranslationTemplatesBuildJob(job);
35+
36+INSERT INTO LaunchpadDatabaseRevision VALUES (2207, 22, 0);
37
38=== modified file 'database/schema/security.cfg'
39--- database/schema/security.cfg 2010-01-06 07:05:19 +0000
40+++ database/schema/security.cfg 2010-01-06 10:28:20 +0000
41@@ -293,6 +293,7 @@
42 public.translationgroup = SELECT, INSERT, UPDATE
43 public.translationimportqueueentry = SELECT, INSERT, UPDATE, DELETE
44 public.translationmessage = SELECT, INSERT, UPDATE
45+public.translationtemplatesbuildjob = SELECT, INSERT
46 public.translator = SELECT, INSERT, UPDATE, DELETE
47 public.usertouseremail = SELECT, UPDATE
48 public.validpersoncache = SELECT
49
50=== modified file 'lib/lp/buildmaster/interfaces/buildfarmjob.py'
51--- lib/lp/buildmaster/interfaces/buildfarmjob.py 2009-12-24 14:18:35 +0000
52+++ lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-01-06 10:28:19 +0000
53@@ -46,24 +46,24 @@
54 Build a package from a bazaar branch and a recipe.
55 """)
56
57- TRANSLATION = DBItem(4, """
58- TranslationJob
59+ TRANSLATIONTEMPLATESBUILD = DBItem(4, """
60+ TranslationTemplatesBuildJob
61
62- Perform a translation job.
63+ Generate translation templates from a bazaar branch.
64 """)
65
66
67 class IBuildFarmJob(Interface):
68- """Operations that Soyuz build farm jobs must implement."""
69+ """Operations that jobs for the build farm must implement."""
70
71 def score():
72 """Calculate a job score appropriate for the job type in question."""
73
74 def getLogFileName():
75- """The preferred file name for the log of this Soyuz job."""
76+ """The preferred file name for the log of this job."""
77
78 def getName():
79- """An appropriate name for this Soyuz job."""
80+ """An appropriate name for this job."""
81
82 def jobStarted():
83 """'Job started' life cycle event, handle as appropriate."""
84
85=== modified file 'lib/lp/translations/configure.zcml'
86--- lib/lp/translations/configure.zcml 2009-12-28 22:58:18 +0000
87+++ lib/lp/translations/configure.zcml 2010-01-06 10:28:20 +0000
88@@ -572,6 +572,22 @@
89 interface="lp.translations.interfaces.poexportrequest.IPOExportRequestSet"/>
90 </securedutility>
91
92+ <class
93+ class="lp.translations.model.translationtemplatesbuildjob.TranslationTemplatesBuildJob">
94+ <allow interface="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJob"/>
95+ <allow interface="lp.translations.interfaces.translationtemplatesbuildjob.ITranslationTemplatesBuildJob"/>
96+ </class>
97+ <securedutility
98+ class="lp.translations.model.translationtemplatesbuildjob.TranslationTemplatesBuildJobSet"
99+ provides="lp.translations.interfaces.translationtemplatesbuildjob.ITranslationTemplatesBuildJobSet">
100+ <allow interface="lp.translations.interfaces.translationtemplatesbuildjob.ITranslationTemplatesBuildJobSet"/>
101+ </securedutility>
102+ <adapter
103+ for="lp.translations.interfaces.translationtemplatesbuildjob.ITranslationTemplatesBuildJob"
104+ provides="lp.buildmaster.interfaces.buildfarmjobbehavior.IBuildFarmJobBehavior"
105+ factory="lp.translations.model.translationtemplatesbuildbehavior.TranslationTemplatesBuildBehavior"
106+ permission="zope.Public"/>
107+
108 <webservice:register module="lp.translations.interfaces.webservice" />
109
110 </configure>
111
112=== added file 'lib/lp/translations/interfaces/translationtemplatesbuildjob.py'
113--- lib/lp/translations/interfaces/translationtemplatesbuildjob.py 1970-01-01 00:00:00 +0000
114+++ lib/lp/translations/interfaces/translationtemplatesbuildjob.py 2010-01-06 10:28:19 +0000
115@@ -0,0 +1,52 @@
116+# Copyright 2009 Canonical Ltd. This software is licensed under the
117+# GNU Affero General Public License version 3 (see the file LICENSE).
118+
119+# pylint: disable-msg=E0213
120+
121+__metaclass__ = type
122+
123+__all__ = [
124+ 'ITranslationTemplatesBuildJob',
125+ 'ITranslationTemplatesBuildJobSet',
126+ ]
127+
128+from zope.interface import Interface
129+from zope.schema import Int
130+
131+from lazr.restful.fields import Reference
132+
133+from canonical.launchpad import _
134+
135+from lp.services.job.interfaces.job import IJob
136+from lp.code.interfaces.branch import IBranch
137+
138+
139+class ITranslationTemplatesBuildJob(Interface):
140+ """Build-farm job object for building translation templates."""
141+
142+ id = Int(title=_("ID"), required=True, readonly=True)
143+
144+ job = Reference(
145+ title=_("Generic Job record"),
146+ readonly=True, required=True, schema=IJob)
147+
148+ branch = Reference(
149+ title=_("Source code branch"),
150+ readonly=True, required=True, schema=IBranch)
151+
152+
153+class ITranslationTemplatesBuildJobSet(Interface):
154+ """The container for `ITranslationTemplatesBuildJob`s."""
155+
156+ def new(branch):
157+ """Create new `ITranslationTemplatesBuildJob`.
158+
159+ Also creates the matching `IBuildQueue` and `IJob` entries.
160+
161+ :param branch: A `Branch` that this job is to check out and
162+ generate templates for.
163+ :return: A new `ITranslationTemplatesBuildJob`.
164+ """
165+
166+ def getForJob(job):
167+ """Find `ITranslationTemplatesBuildJob` matching given `Job`."""
168
169=== added file 'lib/lp/translations/model/translationtemplatesbuildbehavior.py'
170--- lib/lp/translations/model/translationtemplatesbuildbehavior.py 1970-01-01 00:00:00 +0000
171+++ lib/lp/translations/model/translationtemplatesbuildbehavior.py 2010-01-06 10:28:19 +0000
172@@ -0,0 +1,81 @@
173+# Copyright 2009 Canonical Ltd. This software is licensed under the
174+# GNU Affero General Public License version 3 (see the file LICENSE).
175+
176+"""An `IBuildFarmJobBehavior` for `TranslationTemplatesBuildJob`.
177+
178+Dispatches translation template build jobs to build-farm slaves.
179+"""
180+
181+__metaclass__ = type
182+__all__ = [
183+ 'TranslationTemplatesBuildBehavior',
184+ ]
185+
186+import socket
187+import xmlrpclib
188+
189+from zope.component import getUtility
190+from zope.interface import implements
191+
192+from canonical.launchpad.interfaces import ILaunchpadCelebrities
193+
194+from lp.buildmaster.interfaces.buildfarmjobbehavior import (
195+ IBuildFarmJobBehavior)
196+from lp.soyuz.interfaces.builder import BuildSlaveFailure
197+from lp.translations.interfaces.translationtemplatesbuildjob import (
198+ ITranslationTemplatesBuildJobSet)
199+from lp.buildmaster.model.buildfarmjobbehavior import (
200+ BuildFarmJobBehaviorBase)
201+
202+
203+class TranslationTemplatesBuildBehavior(BuildFarmJobBehaviorBase):
204+ """Dispatches `TranslationTemplateBuildJob`s to slaves."""
205+ # Note: If this class had a "metadata" dict, it'd effectively be
206+ # implementing IBranchJob as well.
207+ implements(IBuildFarmJobBehavior)
208+
209+ # XXX JeroenVermeulen 2009-12-24 bug=499405: What really goes here?
210+ build_type = 'translation-templates'
211+
212+ def dispatchBuildToSlave(self, build_queue_item, logger):
213+ """See `IBuildFarmJobBehavior`."""
214+ # XXX JeroenVermeulen 2009-12-24 bug=500110: This method is not
215+ # covered by tests yet. Either unify it with Soyuz code into a
216+ # generalised method, or test it.
217+ templatesbuildjob = self._findTranslationTemplatesBuildJob(
218+ build_queue_item)
219+ chroot = self._getChroot()
220+ chroot_sha1 = chroot.content.sha1
221+ self._builder.cacheFileOnSlave(logger, chroot)
222+ buildid = templatesbuildjob.getName()
223+
224+ args = { 'branch_url': build_queue_item.branch.url }
225+ filemap = {}
226+
227+ try:
228+ status, info = self._builder.slave.build(
229+ buildid, self.build_type, chroot_sha1, filemap, args)
230+ except xmlrpclib.Fault, info:
231+ # Mark builder as 'failed'.
232+ logger.debug(
233+ "Disabling builder: %s" % self._builder.url, exc_info=1)
234+ self._builder.failbuilder(
235+ "Exception (%s) when setting up to new job" % info)
236+ raise BuildSlaveFailure
237+ except socket.error, info:
238+ error_message = "Exception (%s) when setting up new job" % info
239+ self._builder.handleTimeout(logger, error_message)
240+ raise BuildSlaveFailure
241+
242+ def _getChroot(self):
243+ ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
244+ return ubuntu.currentseries.nominatedarchindep
245+
246+ def _findTranslationTemplatesBuildJob(self, build_queue_item):
247+ """Find the `TranslationTemplatesBuildJob` for a job.
248+
249+ :param build_queue_item: A `BuildQueue` entry.
250+ :return: The matching `TranslationTemplatesBuildJob`.
251+ """
252+ buildjobset = getUtility(ITranslationTemplatesBuildJobSet)
253+ return buildjobset.getForJob(build_queue_item.job)
254
255=== added file 'lib/lp/translations/model/translationtemplatesbuildjob.py'
256--- lib/lp/translations/model/translationtemplatesbuildjob.py 1970-01-01 00:00:00 +0000
257+++ lib/lp/translations/model/translationtemplatesbuildjob.py 2010-01-06 10:28:20 +0000
258@@ -0,0 +1,93 @@
259+# Copyright 2009 Canonical Ltd. This software is licensed under the
260+# GNU Affero General Public License version 3 (see the file LICENSE).
261+
262+__metaclass__ = type
263+__all__ = [
264+ 'TranslationTemplatesBuildJob'
265+ ]
266+
267+import re
268+from datetime import timedelta
269+
270+from zope.component import getUtility
271+from zope.interface import implements
272+
273+from storm.locals import Int, Reference, Storm
274+
275+from canonical.launchpad.webapp.interfaces import (
276+ DEFAULT_FLAVOR, IStoreSelector, MAIN_STORE, MASTER_FLAVOR)
277+
278+from lp.buildmaster.interfaces.buildfarmjob import BuildFarmJobType
279+from lp.buildmaster.model.buildfarmjob import BuildFarmJob
280+from lp.services.job.model.job import Job
281+from lp.soyuz.model.buildqueue import BuildQueue
282+from lp.translations.interfaces.translationtemplatesbuildjob import (
283+ ITranslationTemplatesBuildJob, ITranslationTemplatesBuildJobSet)
284+
285+
286+class TranslationTemplatesBuildJob(BuildFarmJob, Storm):
287+ """An `IBuildFarmJob` implementation that generates templates."""
288+ implements(ITranslationTemplatesBuildJob)
289+
290+ __storm_table__ = 'TranslationTemplatesBuildJob'
291+
292+ unsafe_chars = '[^a-zA-Z0-9_+-]'
293+
294+ id = Int(primary=True)
295+
296+ job_id = Int(name='job', allow_none=False)
297+ job = Reference(job_id, 'Job.id')
298+
299+ branch_id = Int(name='branch', allow_none=False)
300+ branch = Reference(branch_id, 'Branch.id')
301+
302+ def score(self):
303+ """See `IBuildFarmJob`."""
304+ # Arbitrary score that sorta-kinda seems to match the sort of
305+ # number that BuildPackageJob might give a job like this,
306+ # perhaps. It may not matter that much since these jobs are
307+ # fast.
308+ return 4010
309+
310+ def getLogFileName(self):
311+ """See `IBuildFarmJob`."""
312+ sanitized_name = re.sub(self.unsafe_chars, '_', self.getName())
313+ return "translationtemplates_%s" % sanitized_name
314+
315+ def getName(self):
316+ """See `IBuildFarmJob`."""
317+ return '%s-%d' % (self.branch.name, self.id)
318+
319+
320+class TranslationTemplatesBuildJobSet:
321+ """See `ITranslationTemplatesBuildJobSet`."""
322+ implements(ITranslationTemplatesBuildJobSet)
323+
324+ duration_estimate = timedelta(seconds=10)
325+
326+ def new(self, branch):
327+ """See `ITranslationTemplatesBuildJobSet`."""
328+ store = getUtility(IStoreSelector).get(MAIN_STORE, MASTER_FLAVOR)
329+ job = Job()
330+ store.add(job)
331+
332+ specific_job = TranslationTemplatesBuildJob()
333+ specific_job.branch = branch
334+ specific_job.job = job
335+ store.add(specific_job)
336+
337+ duration_estimate = self.duration_estimate
338+ build_queue_entry = BuildQueue(
339+ estimated_duration=duration_estimate,
340+ job_type=BuildFarmJobType.TRANSLATIONTEMPLATESBUILD,
341+ job=job.id)
342+ store.add(build_queue_entry)
343+
344+ return specific_job
345+
346+ def getForJob(self, job):
347+ """See `ITranslationTemplatesBuildJobSet`."""
348+ store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
349+ return store.find(
350+ TranslationTemplatesBuildJob,
351+ TranslationTemplatesBuildJob.job == job).one()
352
353=== added file 'lib/lp/translations/tests/test_translationtemplatesbuildjob.py'
354--- lib/lp/translations/tests/test_translationtemplatesbuildjob.py 1970-01-01 00:00:00 +0000
355+++ lib/lp/translations/tests/test_translationtemplatesbuildjob.py 2010-01-06 10:28:19 +0000
356@@ -0,0 +1,106 @@
357+# Copyright 2009 Canonical Ltd. This software is licensed under the
358+# GNU Affero General Public License version 3 (see the file LICENSE).
359+
360+__metaclass__ = type
361+
362+from unittest import TestLoader
363+
364+from zope.component import getUtility
365+from zope.security.proxy import removeSecurityProxy
366+
367+from canonical.launchpad.webapp.testing import verifyObject
368+from canonical.testing import ZopelessDatabaseLayer
369+
370+from lp.testing import TestCaseWithFactory
371+
372+from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob
373+from lp.buildmaster.interfaces.buildfarmjobbehavior import (
374+ IBuildFarmJobBehavior)
375+from lp.services.job.model.job import Job
376+from lp.soyuz.interfaces.buildqueue import IBuildQueueSet
377+from lp.soyuz.model.buildqueue import BuildQueue
378+from lp.translations.interfaces.translationtemplatesbuildjob import (
379+ ITranslationTemplatesBuildJob, ITranslationTemplatesBuildJobSet)
380+
381+
382+def get_job_id(job):
383+ """Peek inside a `Job` and retrieve its id."""
384+ return removeSecurityProxy(job).id
385+
386+
387+class TestTranslationTemplatesBuildJob(TestCaseWithFactory):
388+ """Test `TranslationTemplatesBuildJob`."""
389+
390+ layer = ZopelessDatabaseLayer
391+
392+ def setUp(self):
393+ super(TestTranslationTemplatesBuildJob, self).setUp()
394+ self.jobset = getUtility(ITranslationTemplatesBuildJobSet)
395+ self.branch = self.factory.makeBranch()
396+ self.specific_job = self.jobset.new(self.branch)
397+
398+ def test_new_TranslationTemplatesBuildJob(self):
399+ # TranslationTemplateBuildJob implements IBuildFarmJob and
400+ # ITranslationTemplatesBuildJob.
401+ verifyObject(IBuildFarmJob, self.specific_job)
402+ verifyObject(ITranslationTemplatesBuildJob, self.specific_job)
403+
404+ # Each of these jobs knows the branch it will operate on.
405+ self.assertEqual(self.branch, self.specific_job.branch)
406+
407+ def test_has_Job(self):
408+ # Associated with each TranslationTemplateBuildJob is a Job.
409+ base_job = self.specific_job.job
410+ self.assertIsInstance(base_job, Job)
411+
412+ # From a Job, the TranslationTemplatesBuildJobSet can find the
413+ # TranslationTemplatesBuildJob back for us.
414+ self.assertEqual(self.specific_job, self.jobset.getForJob(base_job))
415+
416+ def test_has_BuildQueue(self):
417+ # There's also a BuildQueue item associated with the job.
418+ queueset = getUtility(IBuildQueueSet)
419+ job_id = get_job_id(self.specific_job.job)
420+ buildqueue = queueset.get(job_id)
421+
422+ self.assertIsInstance(buildqueue, BuildQueue)
423+ self.assertEqual(job_id, get_job_id(buildqueue.job))
424+
425+ def test_getName(self):
426+ # Each job gets a unique name.
427+ other_job = self.jobset.new(self.branch)
428+ self.assertNotEqual(self.specific_job.getName(), other_job.getName())
429+
430+ def test_getLogFileName(self):
431+ # Each job has a unique log file name.
432+ other_job = self.jobset.new(self.branch)
433+ self.assertNotEqual(
434+ self.specific_job.getLogFileName(), other_job.getLogFileName())
435+
436+
437+class TestTranslationTemplatesBuildBehavior(TestCaseWithFactory):
438+ """Test `TranslationTemplatesBuildBehavior`."""
439+
440+ layer = ZopelessDatabaseLayer
441+
442+ def setUp(self):
443+ super(TestTranslationTemplatesBuildBehavior, self).setUp()
444+ self.jobset = getUtility(ITranslationTemplatesBuildJobSet)
445+ self.branch = self.factory.makeBranch()
446+ self.specific_job = self.jobset.new(self.branch)
447+ self.behavior = IBuildFarmJobBehavior(self.specific_job)
448+
449+ def test_getChroot(self):
450+ chroot = self.behavior._getChroot()
451+ self.assertNotEqual(None, chroot)
452+
453+ def test_findTranslationTemplatesBuildJob(self):
454+ buildqueue = self.jobset.getForJob(self.specific_job.job)
455+
456+ self.assertEqual(
457+ self.specific_job,
458+ self.behavior._findTranslationTemplatesBuildJob(buildqueue))
459+
460+
461+def test_suite():
462+ return TestLoader().loadTestsFromName(__name__)

Subscribers

People subscribed via source and target branches

to status/vote changes: