Merge lp:~michael.nelson/launchpad/567922-binarypackagebuild-packagebuild into lp:launchpad/db-devel

Proposed by Michael Nelson
Status: Merged
Approved by: Eleanor Berger
Approved revision: no longer in the source branch.
Merged at revision: 9405
Proposed branch: lp:~michael.nelson/launchpad/567922-binarypackagebuild-packagebuild
Merge into: lp:launchpad/db-devel
Prerequisite: lp:~michael.nelson/launchpad/db-package-build-model
Diff against target: 563 lines (+261/-40)
11 files modified
lib/canonical/launchpad/interfaces/_schema_circular_imports.py (+2/-0)
lib/lp/buildmaster/browser/configure.zcml (+14/-0)
lib/lp/buildmaster/browser/packagebuild.py (+42/-0)
lib/lp/buildmaster/configure.zcml (+1/-1)
lib/lp/buildmaster/interfaces/buildbase.py (+3/-0)
lib/lp/buildmaster/interfaces/buildfarmjob.py (+50/-20)
lib/lp/buildmaster/interfaces/packagebuild.py (+31/-6)
lib/lp/buildmaster/model/buildfarmjob.py (+30/-0)
lib/lp/buildmaster/model/packagebuild.py (+27/-0)
lib/lp/buildmaster/tests/test_buildfarmjob.py (+11/-0)
lib/lp/buildmaster/tests/test_packagebuild.py (+50/-13)
To merge this branch: bzr merge lp:~michael.nelson/launchpad/567922-binarypackagebuild-packagebuild
Reviewer Review Type Date Requested Status
Eleanor Berger (community) code Approve
Review via email: mp+24107@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 updates the IBuildFarmJob/IPackageJob interfaces and implementations so that items currently exported on IBinaryPackageBuild will continue to be exported when (in the next branch) the implementation of IBinaryPackageBuild switches to use the new tables.

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_packagebuild -t test_buildfarmjob -t doc/build.txt -t test_buildqueue -t test_sourcepackagerecipebuild -t test_buildpackagejob

The next stage will be to add the methods currently defined for IBinaryPackageBuild to IBuildFarmJob/IPackageJob before 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
Eleanor Berger (intellectronica) :
review: Approve (code)

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-05-05 15:49:06 +0000
3+++ lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2010-05-05 15:49:09 +0000
4@@ -35,6 +35,7 @@
5 from lp.bugs.interfaces.bugwatch import IBugWatch
6 from lp.buildmaster.interfaces.buildbase import BuildStatus
7 from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob
8+from lp.buildmaster.interfaces.buildqueue import IBuildQueue
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@@ -275,6 +276,7 @@
13
14 # IBuildFarmJob
15 IBuildFarmJob['status'].vocabulary = BuildStatus
16+IBuildFarmJob['buildqueue_record'].schema = IBuildQueue
17
18 # IDistribution
19 IDistribution['series'].value_type.schema = IDistroSeries
20
21=== added directory 'lib/lp/buildmaster/browser'
22=== added file 'lib/lp/buildmaster/browser/__init__.py'
23=== added file 'lib/lp/buildmaster/browser/configure.zcml'
24--- lib/lp/buildmaster/browser/configure.zcml 1970-01-01 00:00:00 +0000
25+++ lib/lp/buildmaster/browser/configure.zcml 2010-05-05 15:49:09 +0000
26@@ -0,0 +1,14 @@
27+<!-- Copyright 2010 Canonical Ltd. This software is licensed under the
28+ GNU Affero General Public License version 3 (see the file LICENSE).
29+-->
30+
31+<configure
32+ xmlns="http://namespaces.zope.org/zope"
33+ xmlns:browser="http://namespaces.zope.org/browser"
34+ xmlns:i18n="http://namespaces.zope.org/i18n"
35+ xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc"
36+ i18n_domain="launchpad">
37+ <browser:url
38+ for="lp.buildmaster.interfaces.packagebuild.IPackageBuild"
39+ urldata="lp.buildmaster.browser.packagebuild.PackageBuildUrl"/>
40+</configure>
41
42=== added file 'lib/lp/buildmaster/browser/packagebuild.py'
43--- lib/lp/buildmaster/browser/packagebuild.py 1970-01-01 00:00:00 +0000
44+++ lib/lp/buildmaster/browser/packagebuild.py 2010-05-05 15:49:09 +0000
45@@ -0,0 +1,42 @@
46+# Copyright 2010 Canonical Ltd. This software is licensed under the
47+# GNU Affero General Public License version 3 (see the file LICENSE).
48+
49+"""URLs for PackageBuild classes."""
50+
51+from zope.interface import implements
52+
53+from canonical.launchpad.webapp.interfaces import ICanonicalUrlData
54+
55+
56+class PackageBuildUrl:
57+ """Dynamic URL declaration for IPackageBuild classes.
58+
59+ When dealing with distribution builds we want to present them
60+ under IDistributionSourcePackageRelease url:
61+
62+ /ubuntu/+source/foo/1.0/+build/1234
63+
64+ On the other hand, PPA builds will be presented under the PPA page:
65+
66+ /~cprov/+archive/+build/1235
67+
68+ Copy archives will be presented under the archives page:
69+ /ubuntu/+archive/my-special-archive/+build/1234
70+ """
71+ implements(ICanonicalUrlData)
72+ rootsite = None
73+
74+ def __init__(self, context):
75+ self.context = context
76+
77+ @property
78+ def inside(self):
79+ if self.context.archive.is_ppa or self.context.archive.is_copy:
80+ return self.context.archive
81+ else:
82+ return self.context.distributionsourcepackagerelease
83+
84+ @property
85+ def path(self):
86+ return u"+build/%d" % self.context.build_farm_job.id
87+
88
89=== modified file 'lib/lp/buildmaster/configure.zcml'
90--- lib/lp/buildmaster/configure.zcml 2010-05-05 15:49:06 +0000
91+++ lib/lp/buildmaster/configure.zcml 2010-05-05 15:49:09 +0000
92@@ -8,7 +8,7 @@
93 xmlns:i18n="http://namespaces.zope.org/i18n"
94 xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc"
95 i18n_domain="launchpad">
96-
97+ <include package=".browser"/>
98
99 <!-- Builder -->
100 <class
101
102=== modified file 'lib/lp/buildmaster/interfaces/buildbase.py'
103--- lib/lp/buildmaster/interfaces/buildbase.py 2010-05-04 15:38:08 +0000
104+++ lib/lp/buildmaster/interfaces/buildbase.py 2010-05-05 15:49:09 +0000
105@@ -113,6 +113,9 @@
106
107 class IBuildBase(Interface):
108 """Common interface shared by farm jobs that build a package."""
109+ # XXX 2010-04-21 michael.nelson bug=567922. This interface
110+ # can be removed once all *Build classes inherit from
111+ # IBuildFarmJob/IPackageBuild.
112
113 # XXX: wgrant 2010-01-20 bug=507712: Most of these attribute names
114 # are bad.
115
116=== modified file 'lib/lp/buildmaster/interfaces/buildfarmjob.py'
117--- lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-05-05 15:49:06 +0000
118+++ lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-05-05 15:49:09 +0000
119@@ -15,8 +15,9 @@
120 ]
121
122 from zope.interface import Interface, Attribute
123-from zope.schema import Bool, Choice, Datetime
124+from zope.schema import Bool, Choice, Datetime, TextLine
125 from lazr.enum import DBEnumeratedType, DBItem
126+from lazr.restful.declarations import exported
127 from lazr.restful.fields import Reference
128
129 from canonical.launchpad import _
130@@ -34,25 +35,25 @@
131 """
132
133 PACKAGEBUILD = DBItem(1, """
134- PackageBuildJob
135+ Binary package build
136
137 Build a source package.
138 """)
139
140 BRANCHBUILD = DBItem(2, """
141- BranchBuildJob
142+ Branch build
143
144 Build a package from a bazaar branch.
145 """)
146
147 RECIPEBRANCHBUILD = DBItem(3, """
148- RecipeBranchBuildJob
149+ Recipe branch build
150
151 Build a package from a bazaar branch and a recipe.
152 """)
153
154 TRANSLATIONTEMPLATESBUILD = DBItem(4, """
155- TranslationTemplatesBuildJob
156+ Translation template build
157
158 Generate translation templates from a bazaar branch.
159 """)
160@@ -76,38 +77,65 @@
161 "This should be None for job types that do not care whether "
162 "they run virtualized."))
163
164- date_created = Datetime(
165- title=_("Date created"), required=True, readonly=True,
166- description=_("The timestamp when the build farm job was created."))
167+ date_created = exported(
168+ Datetime(
169+ title=_("Date created"), required=True, readonly=True,
170+ description=_(
171+ "The timestamp when the build farm job was created.")),
172+ ("1.0", dict(exported=True, exported_as="datecreated")))
173
174 date_started = Datetime(
175 title=_("Date started"), required=False, readonly=True,
176 description=_("The timestamp when the build farm job was started."))
177
178- date_finished = Datetime(
179- title=_("Date finished"), required=False, readonly=True,
180- description=_("The timestamp when the build farm job was finished."))
181+ date_finished = exported(
182+ Datetime(
183+ title=_("Date finished"), required=False, readonly=True,
184+ description=_("The timestamp when the build farm job was finished.")),
185+ ("1.0", dict(exported=True, exported_as="datebuilt")))
186
187- date_first_dispatched = Datetime(
188- title=_("Date finished"), required=False, readonly=True,
189- description=_("The timestamp when the build farm job was dispatched."))
190+ date_first_dispatched = exported(
191+ Datetime(
192+ title=_("Date finished"), required=False, readonly=True,
193+ description=_("The actual build start time. Set when the build "
194+ "is dispatched the first time and not changed in "
195+ "subsequent build attempts.")))
196
197 builder = Reference(
198 title=_("Builder"), schema=IBuilder, required=False, readonly=True,
199 description=_("The builder assigned to this job."))
200
201- status = Choice(
202- title=_('Status'), required=True,
203- # Really BuildStatus, patched in
204- # _schema_circular_imports.py
205- vocabulary=DBEnumeratedType,
206- description=_("The current status of the job."))
207+ buildqueue_record = Reference(
208+ # Really IBuildQueue, set in _schema_circular_imports to avoid
209+ # circular import.
210+ schema=Interface, required=True,
211+ title=_("Corresponding BuildQueue record"))
212+
213+ status = exported(
214+ Choice(
215+ title=_('Status'), required=True,
216+ # Really BuildStatus, patched in
217+ # _schema_circular_imports.py
218+ vocabulary=DBEnumeratedType,
219+ description=_("The current status of the job.")),
220+ ("1.0", dict(exported=True, exported_as="buildstate")))
221
222 log = Reference(
223 schema=ILibraryFileAlias, required=False,
224 title=_(
225 "The LibraryFileAlias containing the entire log for this job."))
226
227+ log_url = exported(
228+ TextLine(
229+ title=_("Build Log URL"), required=False,
230+ description=_("A URL for the build log. None if there is no "
231+ "log available.")),
232+ ("1.0", dict(exported=True, exported_as="build_log_url")))
233+
234+ is_private = Bool(
235+ title=_("is private"), required=False, readonly=True,
236+ description=_("Whether the build should be treated as private."))
237+
238 job_type = Choice(
239 title=_("Job type"), required=True, readonly=True,
240 vocabulary=BuildFarmJobType,
241@@ -121,6 +149,8 @@
242 readonly=True, description=_(
243 'Whether this instance is or has a concrete build farm job.'))
244
245+ title = exported(TextLine(title=_("Title"), required=False))
246+
247 def score():
248 """Calculate a job score appropriate for the job type in question."""
249
250
251=== modified file 'lib/lp/buildmaster/interfaces/packagebuild.py'
252--- lib/lp/buildmaster/interfaces/packagebuild.py 2010-05-05 15:49:06 +0000
253+++ lib/lp/buildmaster/interfaces/packagebuild.py 2010-05-05 15:49:09 +0000
254@@ -11,11 +11,13 @@
255
256 from zope.interface import Interface, Attribute
257 from zope.schema import Choice, Object, TextLine
258+from lazr.restful.declarations import exported
259 from lazr.restful.fields import Reference
260
261 from canonical.launchpad import _
262 from canonical.launchpad.interfaces.librarian import ILibraryFileAlias
263 from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob
264+from lp.registry.interfaces.distribution import IDistribution
265 from lp.registry.interfaces.pocket import PackagePublishingPocket
266 from lp.soyuz.interfaces.archive import IArchive
267
268@@ -25,15 +27,17 @@
269
270 id = Attribute('The package build ID.')
271
272- archive = Reference(
273+ archive = exported(
274+ Reference(
275 title=_('Archive'), schema=IArchive,
276 required=True, readonly=True,
277- description=_('The Archive context for this build.'))
278+ description=_('The Archive context for this build.')))
279
280- pocket = Choice(
281+ pocket = exported(
282+ Choice(
283 title=_('Pocket'), required=True,
284 vocabulary=PackagePublishingPocket,
285- description=_('The build targeted pocket.'))
286+ description=_('The build targeted pocket.')))
287
288 upload_log = Object(
289 schema=ILibraryFileAlias, required=False,
290@@ -41,15 +45,36 @@
291 'build resulting in an upload that could not be processed '
292 'successfully. Otherwise it will be None.'))
293
294- dependencies = TextLine(
295+ upload_log_url = exported(
296+ TextLine(
297+ title=_("Upload Log URL"), required=False,
298+ description=_("A URL for failed upload logs."
299+ "Will be None if there was no failure.")))
300+
301+ dependencies = exported(
302+ TextLine(
303 title=_('Dependencies'), required=False,
304 description=_('Debian-like dependency line that must be satisfied'
305- ' before attempting to build this request.'))
306+ ' before attempting to build this request.')))
307
308 build_farm_job = Reference(
309 title=_('Build farm job'), schema=IBuildFarmJob, required=True,
310 readonly=True, description=_('The base build farm job.'))
311
312+ policy_name = TextLine(
313+ title=_("Policy name"), required=True,
314+ description=_("The upload policy to use for handling these builds."))
315+
316+ current_component = Attribute(
317+ 'Component where the source related to this build was last '
318+ 'published.')
319+
320+ distribution = exported(
321+ Reference(
322+ schema=IDistribution,
323+ title=_("Distribution"), required=True,
324+ description=_("Shortcut for its distribution.")))
325+
326
327 class IPackageBuildSource(Interface):
328 """A utility of this interface used to create _things_."""
329
330=== modified file 'lib/lp/buildmaster/model/buildfarmjob.py'
331--- lib/lp/buildmaster/model/buildfarmjob.py 2010-05-05 15:49:06 +0000
332+++ lib/lp/buildmaster/model/buildfarmjob.py 2010-05-05 15:49:09 +0000
333@@ -100,6 +100,11 @@
334 # Check if the object has been added to the store.
335 return get_obj_info(self).get('store') is not None
336
337+ @property
338+ def title(self):
339+ """See `IBuildFarmJob`."""
340+ return self.job_type.title
341+
342 def score(self):
343 """See `IBuildFarmJob`."""
344 raise NotImplementedError
345@@ -147,6 +152,31 @@
346 """See `IBuildFarmJob`."""
347 return True
348
349+ @property
350+ def buildqueue_record(self):
351+ """See `IBuildFarmJob`."""
352+ # Need to update the db schema for buildpackagejob before
353+ # this can be implemented here.
354+ return None
355+
356+ @property
357+ def is_private(self):
358+ """See `IBuildFarmJob`.
359+
360+ This base implementation assumes build farm jobs are public, but
361+ derived implementations can override as required.
362+ """
363+ return False
364+
365+ @property
366+ def log_url(self):
367+ """See `IBuildFarmJob`.
368+
369+ This base implementation of the property always returns None. Derived
370+ implementations need to override for their specific context.
371+ """
372+ return None
373+
374
375 class BuildFarmJobDerived:
376 """See `IBuildFarmJobDerived`."""
377
378=== modified file 'lib/lp/buildmaster/model/packagebuild.py'
379--- lib/lp/buildmaster/model/packagebuild.py 2010-05-05 15:49:06 +0000
380+++ lib/lp/buildmaster/model/packagebuild.py 2010-05-05 15:49:09 +0000
381@@ -15,6 +15,8 @@
382
383 from canonical.database.constants import UTC_NOW
384 from canonical.database.enumcol import DBEnum
385+from canonical.launchpad.browser.librarian import (
386+ ProxiedLibraryFileAlias)
387 from canonical.launchpad.interfaces.lpstorm import IMasterStore
388
389 from lp.buildmaster.interfaces.buildbase import BuildStatus
390@@ -23,6 +25,9 @@
391 IPackageBuild, IPackageBuildSource)
392 from lp.buildmaster.model.buildfarmjob import BuildFarmJobDerived
393 from lp.registry.interfaces.pocket import PackagePublishingPocket
394+from lp.soyuz.adapters.archivedependencies import (
395+ default_component_dependency_name)
396+from lp.soyuz.interfaces.component import IComponentSet
397
398
399 class PackageBuild(BuildFarmJobDerived, Storm):
400@@ -50,6 +55,9 @@
401 build_farm_job_id = Int(name='build_farm_job', allow_none=False)
402 build_farm_job = Reference(build_farm_job_id, 'BuildFarmJob.id')
403
404+ policy_name = 'buildd'
405+ distribution = None
406+
407 def __init__(self, build):
408 """Construct a PackageBuild.
409
410@@ -136,6 +144,25 @@
411
412 self.build.buildstate = BuildStatus.NEEDSBUILD
413
414+ @property
415+ def current_component(self):
416+ """See `IPackageBuild`."""
417+ return getUtility(IComponentSet)[default_component_dependency_name]
418+
419+ @property
420+ def upload_log_url(self):
421+ """See `IBuildBase`."""
422+ if self.upload_log is None:
423+ return None
424+ return ProxiedLibraryFileAlias(self.upload_log, self).http_url
425+
426+ @property
427+ def log_url(self):
428+ """See `IBuildFarmJob`."""
429+ if self.log is None:
430+ return None
431+ return ProxiedLibraryFileAlias(self.log, self).http_url
432+
433
434 class PackageBuildDerived(BuildFarmJobDerived):
435 """Override the base delegate to use a build farm job specific to
436
437=== modified file 'lib/lp/buildmaster/tests/test_buildfarmjob.py'
438--- lib/lp/buildmaster/tests/test_buildfarmjob.py 2010-05-05 15:49:06 +0000
439+++ lib/lp/buildmaster/tests/test_buildfarmjob.py 2010-05-05 15:49:09 +0000
440@@ -79,6 +79,7 @@
441 self.assertEqual(None, self.build_farm_job.date_first_dispatched)
442 self.assertEqual(None, self.build_farm_job.builder)
443 self.assertEqual(None, self.build_farm_job.log)
444+ self.assertEqual(None, self.build_farm_job.log_url)
445
446 def test_unimplemented_methods(self):
447 # A build farm job leaves the implementation of various
448@@ -115,6 +116,16 @@
449 BuildStatus.NEEDSBUILD, self.build_farm_job.status)
450 self.failUnless(self.build_farm_job.date_started is None)
451
452+ def test_title(self):
453+ # The default title simply uses the job type's title.
454+ self.assertEqual(
455+ self.build_farm_job.job_type.title,
456+ self.build_farm_job.title)
457+
458+ def test_buildqueue_record(self):
459+ self.failUnless(
460+ False, "Update schema and move buildqueue_record implementation")
461+
462
463 def test_suite():
464 return unittest.TestLoader().loadTestsFromName(__name__)
465
466=== modified file 'lib/lp/buildmaster/tests/test_packagebuild.py'
467--- lib/lp/buildmaster/tests/test_packagebuild.py 2010-05-05 15:49:06 +0000
468+++ lib/lp/buildmaster/tests/test_packagebuild.py 2010-05-05 15:49:09 +0000
469@@ -9,9 +9,10 @@
470
471 from storm.store import Store
472 from zope.component import getUtility
473+from zope.security.proxy import removeSecurityProxy
474
475 from canonical.database.sqlbase import flush_database_updates
476-from canonical.testing.layers import DatabaseFunctionalLayer
477+from canonical.testing.layers import LaunchpadFunctionalLayer
478
479 from lp.buildmaster.interfaces.buildfarmjob import BuildFarmJobType
480 from lp.buildmaster.interfaces.packagebuild import (
481@@ -24,34 +25,70 @@
482 class TestPackageBuild(TestCaseWithFactory):
483 """Tests for the package build object."""
484
485- layer = DatabaseFunctionalLayer
486-
487- def makePackageBuild(self):
488+ layer = LaunchpadFunctionalLayer
489+
490+ def setUp(self):
491+ """Create a package build with which to test."""
492+ super(TestPackageBuild, self).setUp()
493+ joe = self.factory.makePerson(name="joe")
494+ joes_ppa = self.factory.makeArchive(owner=joe)
495+ self.package_build = self.makePackageBuild(archive=joes_ppa)
496+
497+ def makePackageBuild(self, archive=None):
498+ if archive is None:
499+ archive = self.factory.makeArchive()
500+
501 return getUtility(IPackageBuildSource).new(
502 job_type=BuildFarmJobType.PACKAGEBUILD,
503 virtualized=True,
504- archive=self.factory.makeArchive(),
505+ archive=archive,
506 pocket=PackagePublishingPocket.RELEASE)
507
508 def test_providesInterface(self):
509 # PackageBuild provides IPackageBuild
510- package_build = self.makePackageBuild()
511- self.assertProvides(package_build, IPackageBuild)
512+ self.assertProvides(self.package_build, IPackageBuild)
513
514 def test_saves_record(self):
515 # A package build can be stored in the database.
516- package_build = self.makePackageBuild()
517 flush_database_updates()
518- store = Store.of(package_build)
519+ store = Store.of(self.package_build)
520 retrieved_build = store.find(
521 PackageBuild,
522- PackageBuild.id == package_build.id).one()
523- self.assertEqual(package_build, retrieved_build)
524+ PackageBuild.id == self.package_build.id).one()
525+ self.assertEqual(self.package_build, retrieved_build)
526
527 def test_getTitle_not_implemented(self):
528 # Classes deriving from PackageBuild must provide getTitle.
529- package_build = self.makePackageBuild()
530- self.assertRaises(NotImplementedError, package_build.getTitle)
531+ self.assertRaises(NotImplementedError, self.package_build.getTitle)
532+
533+ def test_default_values(self):
534+ # PackageBuild has a number of default values.
535+ self.failUnlessEqual('buildd', self.package_build.policy_name)
536+ self.failUnlessEqual(
537+ 'multiverse', self.package_build.current_component.name)
538+ self.failUnlessEqual(None, self.package_build.distribution)
539+
540+ def test_log_url(self):
541+ # The url of the build log file is determined by the PackageBuild.
542+ lfa = self.factory.makeLibraryFileAlias('mybuildlog.txt')
543+ removeSecurityProxy(self.package_build).log = lfa
544+ log_url = self.package_build.log_url
545+ self.failUnlessEqual(
546+ 'http://launchpad.dev/~joe/'
547+ '+archive/ppa/+build/%d/+files/mybuildlog.txt' % (
548+ self.package_build.build_farm_job.id),
549+ log_url)
550+
551+ def test_upload_log_url(self):
552+ # The url of the upload log file is determined by the PackageBuild.
553+ lfa = self.factory.makeLibraryFileAlias('myuploadlog.txt')
554+ removeSecurityProxy(self.package_build).upload_log = lfa
555+ log_url = self.package_build.upload_log_url
556+ self.failUnlessEqual(
557+ 'http://launchpad.dev/~joe/'
558+ '+archive/ppa/+build/%d/+files/myuploadlog.txt' % (
559+ self.package_build.build_farm_job.id),
560+ log_url)
561
562
563 def test_suite():

Subscribers

People subscribed via source and target branches

to status/vote changes: