Merge lp:~cjwatson/launchpad/archive-dependencies-unittest into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18378
Proposed branch: lp:~cjwatson/launchpad/archive-dependencies-unittest
Merge into: lp:launchpad
Diff against target: 1256 lines (+541/-643)
5 files modified
lib/lp/soyuz/adapters/tests/test_archivedependencies.py (+517/-0)
lib/lp/soyuz/doc/archive-dependencies.txt (+0/-637)
lib/lp/soyuz/interfaces/archive.py (+2/-2)
lib/lp/soyuz/tests/test_archive.py (+14/-1)
lib/lp/testing/factory.py (+8/-3)
To merge this branch: bzr merge lp:~cjwatson/launchpad/archive-dependencies-unittest
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+323129@code.launchpad.net

Commit message

Convert archive-dependencies.txt to unit tests. Make InvalidExternalDependencies have a slightly more sensible message in the process.

Description of the change

I needed to do this to make it less painful to change get_sources_list_for_building to talk to the keyserver, which I need in order to fix bug 1626739.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'lib/lp/soyuz/adapters/tests/test_archivedependencies.py'
--- lib/lp/soyuz/adapters/tests/test_archivedependencies.py 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/adapters/tests/test_archivedependencies.py 2017-04-25 11:47:06 +0000
@@ -0,0 +1,517 @@
1# Copyright 2017 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Test archive dependencies."""
5
6from __future__ import absolute_import, print_function, unicode_literals
7
8__metaclass__ = type
9
10from testtools.matchers import StartsWith
11import transaction
12from zope.component import getUtility
13
14from lp.registry.interfaces.distribution import IDistributionSet
15from lp.registry.interfaces.pocket import PackagePublishingPocket
16from lp.services.log.logger import BufferLogger
17from lp.soyuz.adapters.archivedependencies import (
18 default_component_dependency_name,
19 default_pocket_dependency,
20 get_components_for_context,
21 get_primary_current_component,
22 get_sources_list_for_building,
23 pocket_dependencies,
24 )
25from lp.soyuz.enums import PackagePublishingStatus
26from lp.soyuz.interfaces.archive import IArchive
27from lp.soyuz.interfaces.component import IComponentSet
28from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
29from lp.testing import TestCaseWithFactory
30from lp.testing.layers import (
31 LaunchpadZopelessLayer,
32 ZopelessDatabaseLayer,
33 )
34
35
36class TestOgreModel(TestCaseWithFactory):
37 """Test ogre-model component handling.
38
39 The Ubuntu "ogre model" (cf. Shrek) ensures that build-dependencies are
40 consistent with the component in which the source is published.
41 """
42
43 layer = ZopelessDatabaseLayer
44
45 def setUpComponents(self, distroseries, component_names):
46 for component_name in component_names:
47 component = getUtility(IComponentSet)[component_name]
48 self.factory.makeComponentSelection(distroseries, component)
49
50 def assertComponentMap(self, expected, distroseries, pocket):
51 for component_name, expected_components in expected.items():
52 component = getUtility(IComponentSet)[component_name]
53 self.assertEqual(
54 expected_components,
55 get_components_for_context(component, distroseries, pocket))
56
57 def test_strict_supported_component_dependencies(self):
58 # In strict-supported-component-dependencies mode, a source
59 # published in main is only allowed to build-depend on binaries also
60 # published in main, while a source published in universe is allowed
61 # to build-depend on main and universe.
62 distroseries = self.factory.makeDistroSeries()
63 expected = {
64 "main": ["main"],
65 "restricted": ["main", "restricted"],
66 "universe": ["main", "universe"],
67 "multiverse": ["main", "restricted", "universe", "multiverse"],
68 "partner": ["partner"],
69 }
70 self.setUpComponents(distroseries, expected.keys())
71 self.assertComponentMap(
72 expected, distroseries, PackagePublishingPocket.RELEASE)
73
74 def test_lax_supported_component_dependencies(self):
75 # In lax-supported-component-dependencies mode, source packages in
76 # "supported" components (main and restricted) may additionally
77 # build-depend on binary packages in "unsupported" components
78 # (universe and multiverse).
79 distroseries = self.factory.makeDistroSeries()
80 distroseries.strict_supported_component_dependencies = False
81 expected = {
82 "main": ["main", "universe"],
83 "restricted": ["main", "restricted", "universe", "multiverse"],
84 "universe": ["main", "universe"],
85 "multiverse": ["main", "restricted", "universe", "multiverse"],
86 "partner": ["partner"],
87 }
88 self.setUpComponents(distroseries, expected.keys())
89 self.assertComponentMap(
90 expected, distroseries, PackagePublishingPocket.RELEASE)
91
92 def test_backports(self):
93 # Source packages in the BACKPORTS pocket are allowed to
94 # build-depend on binary packages in any component. This avoids
95 # having to make potentially-invasive changes to accommodate
96 # backporting to stable series.
97 distroseries = self.factory.makeDistroSeries()
98 expected = {
99 "main": ["main", "restricted", "universe", "multiverse"],
100 "restricted": ["main", "restricted", "universe", "multiverse"],
101 "universe": ["main", "restricted", "universe", "multiverse"],
102 "multiverse": ["main", "restricted", "universe", "multiverse"],
103 "partner": ["main", "restricted", "universe", "multiverse"],
104 }
105 self.setUpComponents(distroseries, expected.keys())
106 self.assertComponentMap(
107 expected, distroseries, PackagePublishingPocket.BACKPORTS)
108
109
110class TestSourcesList(TestCaseWithFactory):
111 """Test sources.list contents for building, and related mechanisms."""
112
113 layer = LaunchpadZopelessLayer
114
115 ubuntu_components = [
116 "main", "restricted", "universe", "multiverse", "partner"]
117
118 def setUp(self):
119 super(TestSourcesList, self).setUp()
120 self.publisher = SoyuzTestPublisher()
121 self.ubuntu = getUtility(IDistributionSet).getByName("ubuntu")
122 self.hoary = self.ubuntu.getSeries("hoary")
123 self.publisher.addFakeChroots(self.hoary)
124 self.publisher.setUpDefaultDistroSeries(self.hoary)
125 for component_name in self.ubuntu_components:
126 component = getUtility(IComponentSet)[component_name]
127 if component not in self.hoary.components:
128 self.factory.makeComponentSelection(self.hoary, component)
129
130 def test_defaults(self):
131 # Non-primary archives by default use the Release, Security and
132 # Updates pockets from the primary archive, and all its available
133 # components.
134 self.assertEqual(
135 PackagePublishingPocket.UPDATES, default_pocket_dependency)
136 self.assertEqual("multiverse", default_component_dependency_name)
137 self.assertEqual(
138 (PackagePublishingPocket.RELEASE,
139 PackagePublishingPocket.SECURITY,
140 PackagePublishingPocket.UPDATES),
141 pocket_dependencies[default_pocket_dependency])
142
143 def makeArchive(self, publish_binary=False, **kwargs):
144 archive = self.factory.makeArchive(distribution=self.ubuntu, **kwargs)
145 if publish_binary:
146 self.publisher.getPubBinaries(
147 archive=archive, status=PackagePublishingStatus.PUBLISHED)
148 return archive
149
150 def makeBuild(self, **kwargs):
151 pub_source = self.publisher.getPubSource(**kwargs)
152 [build] = pub_source.createMissingBuilds()
153 return build
154
155 def assertPrimaryCurrentComponent(self, expected, build):
156 self.assertEqual(
157 expected,
158 get_primary_current_component(
159 build.archive, build.distro_series,
160 build.source_package_release.name).name)
161
162 def assertSourcesList(self, expected, build, **kwargs):
163 expected_lines = []
164 for archive_or_prefix, suffixes in expected:
165 if IArchive.providedBy(archive_or_prefix):
166 prefix = "deb %s " % archive_or_prefix.archive_url
167 else:
168 prefix = archive_or_prefix + " "
169 expected_lines.extend([prefix + suffix for suffix in suffixes])
170 sources_list = get_sources_list_for_building(
171 build, build.distro_arch_series, build.source_package_release.name,
172 **kwargs)
173 self.assertEqual(expected_lines, sources_list)
174
175 def test_ppa_with_no_binaries(self):
176 # If there are no published binaries in a PPA, only its primary
177 # archive dependencies need to be considered.
178 ppa = self.makeArchive()
179 build = self.makeBuild(archive=ppa)
180 self.assertEqual(
181 0, ppa.getAllPublishedBinaries(
182 distroarchseries=build.distro_arch_series,
183 status=PackagePublishingStatus.PUBLISHED).count())
184 self.assertSourcesList(
185 [(self.ubuntu.main_archive, [
186 "hoary main restricted universe multiverse",
187 "hoary-security main restricted universe multiverse",
188 "hoary-updates main restricted universe multiverse",
189 ]),
190 ], build)
191
192 def test_ppa_with_binaries(self):
193 # If there are binaries published in a PPA, then the PPA is
194 # considered as well as its primary dependencies.
195 ppa = self.makeArchive(publish_binary=True)
196 build = self.makeBuild(archive=ppa)
197 self.assertSourcesList(
198 [(ppa, ["hoary main"]),
199 (self.ubuntu.main_archive, [
200 "hoary main restricted universe multiverse",
201 "hoary-security main restricted universe multiverse",
202 "hoary-updates main restricted universe multiverse",
203 ]),
204 ], build)
205
206 def test_dependent_ppa_with_no_binaries(self):
207 # A depended-upon PPA is not considered if it has no published
208 # binaries.
209 lower_ppa = self.makeArchive()
210 upper_ppa = self.makeArchive(publish_binary=True)
211 upper_ppa.addArchiveDependency(
212 lower_ppa, PackagePublishingPocket.RELEASE,
213 getUtility(IComponentSet)["main"])
214 build = self.makeBuild(archive=upper_ppa)
215 self.assertSourcesList(
216 [(upper_ppa, ["hoary main"]),
217 (self.ubuntu.main_archive, [
218 "hoary main restricted universe multiverse",
219 "hoary-security main restricted universe multiverse",
220 "hoary-updates main restricted universe multiverse",
221 ]),
222 ], build)
223
224 def test_dependent_ppa_with_binaries(self):
225 # A depended-upon PPA is considered if it has published binaries.
226 lower_ppa = self.makeArchive(publish_binary=True)
227 upper_ppa = self.makeArchive(publish_binary=True)
228 upper_ppa.addArchiveDependency(
229 lower_ppa, PackagePublishingPocket.RELEASE,
230 getUtility(IComponentSet)["main"])
231 build = self.makeBuild(archive=upper_ppa)
232 self.assertSourcesList(
233 [(upper_ppa, ["hoary main"]),
234 (lower_ppa, ["hoary main"]),
235 (self.ubuntu.main_archive, [
236 "hoary main restricted universe multiverse",
237 "hoary-security main restricted universe multiverse",
238 "hoary-updates main restricted universe multiverse",
239 ]),
240 ], build)
241
242 def test_lax_supported_component_dependencies(self):
243 # Dependencies for series with
244 # strict_supported_component_dependencies=False are reasonable.
245 # PPAs only have the "main" component.
246 lower_ppa = self.makeArchive(publish_binary=True)
247 upper_ppa = self.makeArchive(publish_binary=True)
248 upper_ppa.addArchiveDependency(
249 lower_ppa, PackagePublishingPocket.RELEASE,
250 getUtility(IComponentSet)["main"])
251 upper_ppa.addArchiveDependency(
252 self.ubuntu.main_archive, PackagePublishingPocket.UPDATES,
253 getUtility(IComponentSet)["restricted"])
254 build = self.makeBuild(archive=upper_ppa)
255 self.assertSourcesList(
256 [(upper_ppa, ["hoary main"]),
257 (lower_ppa, ["hoary main"]),
258 (self.ubuntu.main_archive, [
259 "hoary main restricted",
260 "hoary-security main restricted",
261 "hoary-updates main restricted",
262 ]),
263 ], build)
264 self.hoary.strict_supported_component_dependencies = False
265 transaction.commit()
266 self.assertSourcesList(
267 [(upper_ppa, ["hoary main"]),
268 (lower_ppa, ["hoary main"]),
269 (self.ubuntu.main_archive, [
270 "hoary main restricted universe multiverse",
271 "hoary-security main restricted universe multiverse",
272 "hoary-updates main restricted universe multiverse",
273 ]),
274 ], build)
275
276 def test_no_op_primary_archive_dependency(self):
277 # Overriding the default primary archive dependencies with exactly
278 # the same values has no effect.
279 ppa = self.makeArchive()
280 ppa.addArchiveDependency(
281 self.ubuntu.main_archive, PackagePublishingPocket.UPDATES,
282 getUtility(IComponentSet)["multiverse"])
283 build = self.makeBuild(archive=ppa)
284 self.assertSourcesList(
285 [(self.ubuntu.main_archive, [
286 "hoary main restricted universe multiverse",
287 "hoary-security main restricted universe multiverse",
288 "hoary-updates main restricted universe multiverse",
289 ]),
290 ], build)
291
292 def test_primary_archive_dependency_security(self):
293 # The primary archive dependency can be modified to behave as an
294 # embargoed archive that builds security updates. This is done by
295 # setting the SECURITY pocket dependencies (RELEASE and SECURITY)
296 # and following the component dependencies of the component where
297 # the source was last published in the primary archive.
298 ppa = self.makeArchive()
299 ppa.addArchiveDependency(
300 self.ubuntu.main_archive, PackagePublishingPocket.SECURITY)
301 build = self.makeBuild(archive=ppa)
302 self.assertPrimaryCurrentComponent("universe", build)
303 self.assertSourcesList(
304 [(self.ubuntu.main_archive, [
305 "hoary main universe",
306 "hoary-security main universe",
307 ]),
308 ], build)
309 self.publisher.getPubSource(
310 sourcename="with-ancestry", version="1.0",
311 archive=self.ubuntu.main_archive)
312 [build_with_ancestry] = self.publisher.getPubSource(
313 sourcename="with-ancestry", version="1.1",
314 archive=ppa).createMissingBuilds()
315 self.assertPrimaryCurrentComponent("main", build_with_ancestry)
316 self.assertSourcesList(
317 [(self.ubuntu.main_archive, [
318 "hoary main",
319 "hoary-security main",
320 ]),
321 ], build_with_ancestry)
322
323 def test_primary_archive_dependency_release(self):
324 # The primary archive dependency can be modified to behave as a
325 # pristine build environment based only on what was included in the
326 # original release of the corresponding series.
327 ppa = self.makeArchive()
328 ppa.addArchiveDependency(
329 self.ubuntu.main_archive, PackagePublishingPocket.RELEASE,
330 getUtility(IComponentSet)["restricted"])
331 build = self.makeBuild(archive=ppa)
332 self.assertSourcesList(
333 [(self.ubuntu.main_archive, ["hoary main restricted"])], build)
334
335 def test_primary_archive_dependency_proposed(self):
336 # The primary archive dependency can be modified to extend the build
337 # environment for PROPOSED.
338 ppa = self.makeArchive()
339 ppa.addArchiveDependency(
340 self.ubuntu.main_archive, PackagePublishingPocket.PROPOSED,
341 getUtility(IComponentSet)["multiverse"])
342 build = self.makeBuild(archive=ppa)
343 self.assertSourcesList(
344 [(self.ubuntu.main_archive, [
345 "hoary main restricted universe multiverse",
346 "hoary-security main restricted universe multiverse",
347 "hoary-updates main restricted universe multiverse",
348 "hoary-proposed main restricted universe multiverse",
349 ]),
350 ], build)
351
352 def test_primary_archive_dependency_backports(self):
353 # The primary archive dependency can be modified to extend the build
354 # environment for PROPOSED.
355 ppa = self.makeArchive()
356 ppa.addArchiveDependency(
357 self.ubuntu.main_archive, PackagePublishingPocket.BACKPORTS,
358 getUtility(IComponentSet)["multiverse"])
359 build = self.makeBuild(archive=ppa)
360 self.assertSourcesList(
361 [(self.ubuntu.main_archive, [
362 "hoary main restricted universe multiverse",
363 "hoary-security main restricted universe multiverse",
364 "hoary-updates main restricted universe multiverse",
365 "hoary-backports main restricted universe multiverse",
366 ]),
367 ], build)
368
369 def test_partner(self):
370 # Similarly to what happens with PPA builds, partner builds may
371 # depend on any component in the primary archive. This behaviour
372 # allows scenarios where partner packages may use other
373 # restricted/non-free applications from multiverse, and also other
374 # partner applications.
375 primary, partner = self.ubuntu.all_distro_archives
376 self.publisher.getPubBinaries(
377 archive=partner, component="partner",
378 status=PackagePublishingStatus.PUBLISHED)
379 build = self.makeBuild(archive=partner, component="partner")
380 self.assertSourcesList(
381 [(partner, ["hoary partner"]),
382 (primary, [
383 "hoary main restricted universe multiverse",
384 "hoary-security main restricted universe multiverse",
385 "hoary-updates main restricted universe multiverse",
386 ]),
387 ], build)
388
389 def test_partner_proposed(self):
390 # The partner archive's PROPOSED pocket builds against itself, but
391 # still uses the default UPDATES dependency for the primary archive
392 # unless overridden by ArchiveDependency.
393 primary, partner = self.ubuntu.all_distro_archives
394 self.publisher.getPubBinaries(
395 archive=partner, component="partner",
396 status=PackagePublishingStatus.PUBLISHED)
397 self.publisher.getPubBinaries(
398 archive=partner, component="partner",
399 status=PackagePublishingStatus.PUBLISHED,
400 pocket=PackagePublishingPocket.PROPOSED)
401 build = self.makeBuild(
402 archive=partner, component="partner",
403 pocket=PackagePublishingPocket.PROPOSED)
404 self.assertSourcesList(
405 [(partner, [
406 "hoary partner",
407 "hoary-proposed partner",
408 ]),
409 (primary, [
410 "hoary main restricted universe multiverse",
411 "hoary-security main restricted universe multiverse",
412 "hoary-updates main restricted universe multiverse",
413 ]),
414 ], build)
415
416 def test_archive_external_dependencies(self):
417 # An archive can be manually given additional external dependencies.
418 # If present, "%(series)s" is replaced with the series name for the
419 # build being dispatched.
420 ppa = self.makeArchive(publish_binary=True)
421 ppa.external_dependencies = (
422 "deb http://user:pass@repository zoing everything\n"
423 "deb http://user:pass@repository %(series)s public private\n"
424 "deb http://user:pass@repository %(series)s-extra public")
425 build = self.makeBuild(archive=ppa)
426 self.assertSourcesList(
427 [(ppa, ["hoary main"]),
428 ("deb http://user:pass@repository", [
429 "zoing everything",
430 "hoary public private",
431 "hoary-extra public",
432 ]),
433 (self.ubuntu.main_archive, [
434 "hoary main restricted universe multiverse",
435 "hoary-security main restricted universe multiverse",
436 "hoary-updates main restricted universe multiverse",
437 ]),
438 ], build)
439
440 def test_build_external_dependencies(self):
441 # A single build can be manually given additional external
442 # dependencies.
443 ppa = self.makeArchive(publish_binary=True)
444 build = self.makeBuild(archive=ppa)
445 build.api_external_dependencies = (
446 "deb http://user:pass@repository foo bar")
447 self.assertSourcesList(
448 [(ppa, ["hoary main"]),
449 ("deb http://user:pass@repository", ["foo bar"]),
450 (self.ubuntu.main_archive, [
451 "hoary main restricted universe multiverse",
452 "hoary-security main restricted universe multiverse",
453 "hoary-updates main restricted universe multiverse",
454 ]),
455 ], build)
456
457 def test_build_tools(self):
458 # We can force an extra build tools line to be added to
459 # sources.list, which is useful for specialised build types.
460 ppa = self.makeArchive(publish_binary=True)
461 build = self.makeBuild(archive=ppa)
462 self.assertSourcesList(
463 [(ppa, ["hoary main"]),
464 ("deb http://example.org", ["hoary main"]),
465 (self.ubuntu.main_archive, [
466 "hoary main restricted universe multiverse",
467 "hoary-security main restricted universe multiverse",
468 "hoary-updates main restricted universe multiverse",
469 ]),
470 ], build, tools_source="deb http://example.org %(series)s main")
471
472 def test_build_tools_bad_formatting(self):
473 # If tools_source is badly formatted, we log the error but don't
474 # blow up. (Note the missing "s" at the end of "%(series)".)
475 ppa = self.makeArchive(publish_binary=True)
476 build = self.makeBuild(archive=ppa)
477 logger = BufferLogger()
478 self.assertSourcesList(
479 [(ppa, ["hoary main"]),
480 (self.ubuntu.main_archive, [
481 "hoary main restricted universe multiverse",
482 "hoary-security main restricted universe multiverse",
483 "hoary-updates main restricted universe multiverse",
484 ]),
485 ],
486 build, tools_source="deb http://example.org %(series) main",
487 logger=logger)
488 self.assertThat(logger.getLogBuffer(), StartsWith(
489 "ERROR Exception processing build tools sources.list entry:\n"))
490
491 def test_overlay(self):
492 # An overlay distroseries is a derived distribution which works like
493 # a PPA. This means that the parent's details gets added to the
494 # sources.list passed to the builders.
495 depdistro = self.factory.makeDistribution(
496 "depdistro", publish_base_url="http://archive.launchpad.dev/")
497 depseries = self.factory.makeDistroSeries(
498 distribution=depdistro, name="depseries")
499 self.factory.makeDistroArchSeries(
500 distroseries=depseries, architecturetag="i386")
501 self.publisher.addFakeChroots(depseries)
502 for component_name in self.ubuntu_components:
503 component = getUtility(IComponentSet)[component_name]
504 self.factory.makeComponentSelection(depseries, component)
505 self.factory.makeDistroSeriesParent(
506 derived_series=self.hoary, parent_series=depseries,
507 initialized=True, is_overlay=True,
508 pocket=PackagePublishingPocket.SECURITY,
509 component=getUtility(IComponentSet)["universe"])
510 build = self.makeBuild()
511 self.assertSourcesList(
512 [(self.ubuntu.main_archive, ["hoary main"]),
513 (depdistro.main_archive, [
514 "depseries main universe",
515 "depseries-security main universe",
516 ]),
517 ], build)
0518
=== removed file 'lib/lp/soyuz/doc/archive-dependencies.txt'
--- lib/lp/soyuz/doc/archive-dependencies.txt 2016-04-07 00:04:42 +0000
+++ lib/lp/soyuz/doc/archive-dependencies.txt 1970-01-01 00:00:00 +0000
@@ -1,637 +0,0 @@
1= Archive dependencies =
2
3`ArchiveDependencies` class models archive dependencies mechanics and
4is used to provided the contents of 'sources_list' file used to build
5sources in the given IBuildQueue context.
6
7
8== Testing scenario setup ==
9
10We use `SoyuzTestPublisher` to generate a source publications and
11build candidates for ubuntu/hoary.
12
13 >>> from lp.registry.interfaces.distribution import IDistributionSet
14 >>> from lp.soyuz.interfaces.component import IComponentSet
15 >>> from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
16 >>> from lp.testing import login
17
18 >>> login('foo.bar@canonical.com')
19
20 >>> test_publisher = SoyuzTestPublisher()
21
22 >>> ubuntu = getUtility(IDistributionSet).getByName('ubuntu')
23 >>> hoary = ubuntu.getSeries('hoary')
24
25 >>> test_publisher.addFakeChroots(hoary)
26 >>> _ = test_publisher.setUpDefaultDistroSeries(hoary)
27
28 >>> ubuntu_components = [
29 ... 'main', 'restricted', 'universe', 'multiverse', 'partner']
30 >>> for component_name in ubuntu_components:
31 ... component = getUtility(IComponentSet)[component_name]
32 ... if component not in hoary.components:
33 ... _ = factory.makeComponentSelection(hoary, component)
34
35
36== Static dependency maps ==
37
38`pocket_dependencies` contains a static map of the default ubuntu
39pocket dependencies.
40
41 >>> from lp.soyuz.adapters.archivedependencies import pocket_dependencies
42
43 >>> def show_pocket_deps():
44 ... print "Pocket | Dependencies"
45 ... print "----------+---------------"
46 ... for (key, value) in sorted(pocket_dependencies.items()):
47 ... print "%7s |" % (key.name,),
48 ... for pocket in value:
49 ... print pocket.name,
50 ... print
51
52 >>> show_pocket_deps()
53 Pocket | Dependencies
54 ----------+---------------
55 RELEASE | RELEASE
56 SECURITY | RELEASE SECURITY
57 UPDATES | RELEASE SECURITY UPDATES
58 PROPOSED | RELEASE SECURITY UPDATES PROPOSED
59 BACKPORTS | RELEASE SECURITY UPDATES BACKPORTS
60
61
62== 'Ogre' components ==
63
64The ubuntu 'ogre-model' ensures that build dependencies are
65consistently spread according to the source target component, i.e. a
66source published in 'main' component is only allowed to depend on
67binaries also published in 'main', on the other hand a source
68published in 'universe' is allowed to depend on binaries published in
69'main' and 'universe' components.
70
71A proper name for this "model" would be 'cross-component-dependency'.
72
73 >>> from lp.services.database.sqlbase import flush_database_caches
74
75 >>> from zope.security.proxy import removeSecurityProxy
76 >>> from lp.registry.interfaces.pocket import (
77 ... PackagePublishingPocket)
78 >>> from lp.soyuz.adapters.archivedependencies import (
79 ... get_components_for_context)
80 >>> from lp.soyuz.enums import ArchivePurpose
81
82 >>> def testOgreComponents(build):
83 ... print " Component | Ogre-Model"
84 ... print "-----------+---------------"
85 ... for component in ubuntu_components:
86 ... component = getUtility(IComponentSet)[component]
87 ... npub = removeSecurityProxy(build.current_source_publication)
88 ... npub.component = component
89 ... flush_database_caches()
90 ... components_term = " ".join(get_components_for_context(
91 ... build.current_component, build.distro_series,
92 ... build.pocket))
93 ... print '%10s | %s' % (build.current_component.name,
94 ... components_term)
95
96 >>> archive = factory.makeArchive(
97 ... distribution=ubuntu, purpose=ArchivePurpose.PRIMARY)
98 >>> ogre_build = factory.makeBinaryPackageBuild(
99 ... distroarchseries=hoary.architectures[0], archive=archive,
100 ... pocket=PackagePublishingPocket.RELEASE)
101 >>> testOgreComponents(ogre_build)
102 Component | Ogre-Model
103 -----------+---------------
104 main | main
105 restricted | main restricted
106 universe | main universe
107 multiverse | main restricted universe multiverse
108 partner | partner
109
110Series with strict_supported_component_dependencies=False additionally allow
111source packages in 'supported' components (main and restricted) to
112build-depend on binary packages in 'unsupported' components (universe and
113multiverse).
114
115 >>> from lp.buildmaster.interfaces.processor import IProcessorSet
116
117 >>> lax_distroseries = factory.makeDistroSeries(
118 ... distribution=archive.distribution, name='lax')
119 >>> lax_distroseries.strict_supported_component_dependencies = False
120 >>> lax_distroarchseries = factory.makeDistroArchSeries(
121 ... distroseries=lax_distroseries, architecturetag='i386',
122 ... processor=getUtility(IProcessorSet).getByName('386'))
123 >>> test_publisher.addFakeChroots(lax_distroseries)
124 >>> for component_name in ubuntu_components:
125 ... component = getUtility(IComponentSet)[component_name]
126 ... _ = factory.makeComponentSelection(lax_distroseries, component)
127 >>> lax_ogre_build = factory.makeBinaryPackageBuild(
128 ... distroarchseries=lax_distroarchseries, archive=archive,
129 ... pocket=PackagePublishingPocket.RELEASE)
130 >>> testOgreComponents(lax_ogre_build)
131 Component | Ogre-Model
132 -----------+---------------
133 main | main universe
134 restricted | main restricted universe multiverse
135 universe | main universe
136 multiverse | main restricted universe multiverse
137 partner | partner
138
139As fixed for bug #198936, builds for the BACKPORTS pocket are allowed
140to use any component available, independently of the component they
141are currently published. This special-case is important because it
142avoids changes to accommodate the backported source in the already
143released series.
144
145Ultimately, it means that a build targeted to the BACKPORTS pocket
146will behave as if it were published in the multiverse component,
147despite the component it is actually published in.
148
149 >>> back_build = factory.makeBinaryPackageBuild(
150 ... distroarchseries=hoary.architectures[0],
151 ... archive=archive, pocket=PackagePublishingPocket.BACKPORTS)
152 >>> testOgreComponents(back_build)
153 Component | Ogre-Model
154 -----------+---------------
155 main | main restricted universe multiverse
156 restricted | main restricted universe multiverse
157 universe | main restricted universe multiverse
158 multiverse | main restricted universe multiverse
159 partner | main restricted universe multiverse
160
161
162== Sources.list contents for building ==
163
164We will use Celso's PPA for testing these mechanisms.
165
166 >>> from lp.registry.interfaces.person import IPersonSet
167 >>> cprov = getUtility(IPersonSet).getByName('cprov')
168 >>> print cprov.archive.displayname
169 PPA for Celso Providelo
170
171Non-primary archives by default use primary Release, Security and Updates
172pockets and all its available components.
173
174 >>> from lp.soyuz.adapters.archivedependencies import (
175 ... default_component_dependency_name, default_pocket_dependency)
176
177 >>> print default_pocket_dependency
178 Updates
179
180 >>> print default_component_dependency_name
181 multiverse
182
183The default values get applied to their corresponding dependency maps
184and then will expand to distinct values that will be used to produce
185the building 'sources_list' contents.
186
187 >>> for pocket in pocket_dependencies[default_pocket_dependency]:
188 ... print pocket
189 Release
190 Security
191 Updates
192
193 >>> for component_name in get_components_for_context(
194 ... getUtility(IComponentSet)[default_component_dependency_name],
195 ... hoary, pocket):
196 ... print component_name
197 main
198 restricted
199 universe
200 multiverse
201
202We will create a testing source publication and probe its build
203environment.
204
205 >>> pub_source = test_publisher.getPubSource(
206 ... version='1.1', archive=cprov.archive)
207 >>> [a_build] = pub_source.createMissingBuilds()
208
209Now we can verify if get_sources_list_for_building() method returns the
210expected content for building the just-created source.
211
212 >>> from lp.soyuz.adapters.archivedependencies import (
213 ... get_sources_list_for_building)
214
215 >>> def print_building_sources_list(candidate):
216 ... sources_list = get_sources_list_for_building(
217 ... candidate, candidate.distro_arch_series,
218 ... candidate.source_package_release.name)
219 ... for line in sources_list:
220 ... print line
221
222Note that only the default ubuntu dependencies for a public PPA will be
223considered when building the source candidate. That's because there is
224no binary published in Celso's PPA hoary/i386, so there is
225no need to request the builder to load its archive indexes.
226
227 >>> from lp.soyuz.enums import PackagePublishingStatus
228
229 >>> cprov.archive.getAllPublishedBinaries(
230 ... distroarchseries=a_build.distro_arch_series,
231 ... status=PackagePublishingStatus.PUBLISHED).count()
232 0
233
234 >>> print_building_sources_list(a_build)
235 deb http://archive.launchpad.dev/ubuntu hoary
236 main restricted universe multiverse
237 deb http://archive.launchpad.dev/ubuntu hoary-security
238 main restricted universe multiverse
239 deb http://archive.launchpad.dev/ubuntu hoary-updates
240 main restricted universe multiverse
241
242Once we publish a test binary in Celso's PPA hoary/i386,
243this archive becomes relevant for building, and thus listed in the
244returned 'sources_list' content.
245
246 >>> pub_binaries = test_publisher.getPubBinaries(
247 ... binaryname='dep-bin', archive=cprov.archive,
248 ... status=PackagePublishingStatus.PUBLISHED)
249
250 >>> print_building_sources_list(a_build)
251 deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
252 deb http://archive.launchpad.dev/ubuntu hoary
253 main restricted universe multiverse
254 deb http://archive.launchpad.dev/ubuntu hoary-security
255 main restricted universe multiverse
256 deb http://archive.launchpad.dev/ubuntu hoary-updates
257 main restricted universe multiverse
258
259Similarly, unpopulated PPA dependencies are *not* listed in the building
260'sources_list'.
261
262 >>> mark = getUtility(IPersonSet).getByName('mark')
263 >>> archive_dependency = cprov.archive.addArchiveDependency(
264 ... mark.archive, PackagePublishingPocket.RELEASE,
265 ... getUtility(IComponentSet)['main'])
266 >>> print_building_sources_list(a_build)
267 deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
268 deb http://archive.launchpad.dev/ubuntu hoary
269 main restricted universe multiverse
270 deb http://archive.launchpad.dev/ubuntu hoary-security
271 main restricted universe multiverse
272 deb http://archive.launchpad.dev/ubuntu hoary-updates
273 main restricted universe multiverse
274
275But *populated* PPA dependencies *are* listed in the building 'sources_list'.
276
277 >>> pub_binaries = test_publisher.getPubBinaries(
278 ... binaryname='dep-bin', archive=mark.archive,
279 ... status=PackagePublishingStatus.PUBLISHED)
280 >>> print_building_sources_list(a_build)
281 deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
282 deb http://ppa.launchpad.dev/mark/ppa/ubuntu hoary main
283 deb http://archive.launchpad.dev/ubuntu hoary
284 main restricted universe multiverse
285 deb http://archive.launchpad.dev/ubuntu hoary-security
286 main restricted universe multiverse
287 deb http://archive.launchpad.dev/ubuntu hoary-updates
288 main restricted universe multiverse
289
290Dependencies for series with strict_supported_component_dependencies=False
291are reasonable too. PPAs only have the 'main' component.
292
293 >>> lax_pub_source = test_publisher.getPubSource(
294 ... version='1.2', distroseries=lax_distroseries,
295 ... archive=cprov.archive)
296 >>> [lax_build] = lax_pub_source.createMissingBuilds()
297 >>> _ = test_publisher.getPubBinaries(
298 ... binaryname='dep-bin', distroseries=lax_distroseries,
299 ... archive=cprov.archive, status=PackagePublishingStatus.PUBLISHED)
300 >>> _ = test_publisher.getPubBinaries(
301 ... binaryname='dep-bin', distroseries=lax_distroseries,
302 ... archive=mark.archive, status=PackagePublishingStatus.PUBLISHED)
303 >>> print_building_sources_list(lax_build)
304 deb http://ppa.launchpad.dev/cprov/ppa/ubuntu lax main
305 deb http://ppa.launchpad.dev/mark/ppa/ubuntu lax main
306 deb http://archive.launchpad.dev/ubuntu lax
307 main restricted universe multiverse
308 deb http://archive.launchpad.dev/ubuntu lax-security
309 main restricted universe multiverse
310 deb http://archive.launchpad.dev/ubuntu lax-updates
311 main restricted universe multiverse
312
313 >>> cprov.archive.removeArchiveDependency(mark.archive)
314
315What when we supply invalid dependencies, an exception is raised.
316
317 >>> cprov.archive.external_dependencies = (
318 ... "Malformed format string here --> %(series)")
319 Traceback (most recent call last):
320 InvalidExternalDependencies: (InvalidExternalDependencies(...),
321 "Invalid external dependencies:\nMalformed format string here
322 --> %(series): Must start with 'deb'\nMalformed format string here
323 --> %(series): Invalid URL\n")
324
325
326== Overriding default primary archive dependencies ==
327
328Despite being private or public, default primary archive dependencies
329can be overridden by simply creating a `ArchiveDependency`record
330targeted to the primary archive.
331
332The 'pocket' and 'component' dependency attributes can be adjusted to
333produce the desired build behaviour.
334
335By default, public PPAs depend on all of the pocket dependencies of
336UPDATES, and all of the primary archive's 'multiverse' component
337dependencies.
338
339 >>> print_building_sources_list(a_build)
340 deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
341 deb http://archive.launchpad.dev/ubuntu hoary
342 main restricted universe multiverse
343 deb http://archive.launchpad.dev/ubuntu hoary-security
344 main restricted universe multiverse
345 deb http://archive.launchpad.dev/ubuntu hoary-updates
346 main restricted universe multiverse
347
348The default build behaviour will remain unchanged when we override the
349default primary archive dependencies with exactly the same values.
350
351 >>> default_dependency = cprov.archive.addArchiveDependency(
352 ... ubuntu.main_archive, PackagePublishingPocket.UPDATES,
353 ... getUtility(IComponentSet)['multiverse'])
354
355 >>> print_building_sources_list(a_build)
356 deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
357 deb http://archive.launchpad.dev/ubuntu hoary
358 main restricted universe multiverse
359 deb http://archive.launchpad.dev/ubuntu hoary-security
360 main restricted universe multiverse
361 deb http://archive.launchpad.dev/ubuntu hoary-updates
362 main restricted universe multiverse
363
364 >>> cprov.archive.removeArchiveDependency(ubuntu.main_archive)
365
366The dependency can be modified to behave as an embargoed archive that
367builds security updates. This is done by setting the SECURITY pocket
368dependencies (RELEASE and SECURITY) and following the component
369dependencies of the component where the source was last published in
370the primary archive.
371
372 >>> security_dependency = cprov.archive.addArchiveDependency(
373 ... ubuntu.main_archive, PackagePublishingPocket.SECURITY)
374
375 >>> from lp.soyuz.adapters.archivedependencies import (
376 ... get_primary_current_component)
377
378 >>> print get_primary_current_component(a_build.archive,
379 ... a_build.distro_series, a_build.source_package_release.name).name
380 universe
381
382 >>> print_building_sources_list(a_build)
383 deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
384 deb http://archive.launchpad.dev/ubuntu hoary
385 main universe
386 deb http://archive.launchpad.dev/ubuntu hoary-security
387 main universe
388
389 >>> _ = test_publisher.getPubSource(
390 ... sourcename='with-ancestry', version='1.0',
391 ... archive=ubuntu.main_archive)
392 >>> [build_with_ancestry] = test_publisher.getPubSource(
393 ... sourcename='with-ancestry', version='1.1',
394 ... archive=cprov.archive).createMissingBuilds()
395 >>> print get_primary_current_component(
396 ... build_with_ancestry.archive, build_with_ancestry.distro_series,
397 ... build_with_ancestry.source_package_release.name).name
398 main
399 >>> print_building_sources_list(build_with_ancestry)
400 deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
401 deb http://archive.launchpad.dev/ubuntu hoary main
402 deb http://archive.launchpad.dev/ubuntu hoary-security main
403
404 >>> cprov.archive.removeArchiveDependency(ubuntu.main_archive)
405
406It's also possible to modify the PPA to act as a super-free and
407pristine build environment based only on what was included in the
408original ubuntu release.
409
410 >>> release_dependency = cprov.archive.addArchiveDependency(
411 ... ubuntu.main_archive, PackagePublishingPocket.RELEASE,
412 ... getUtility(IComponentSet)['restricted'])
413
414 >>> print_building_sources_list(a_build)
415 deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
416 deb http://archive.launchpad.dev/ubuntu hoary main restricted
417
418 >>> cprov.archive.removeArchiveDependency(ubuntu.main_archive)
419
420The PPA can also be configured to extend the ubuntu PROPOSED build
421environment.
422
423 >>> proposed_dependency = cprov.archive.addArchiveDependency(
424 ... ubuntu.main_archive, PackagePublishingPocket.PROPOSED,
425 ... getUtility(IComponentSet)['multiverse'])
426
427 >>> print_building_sources_list(a_build)
428 deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
429 deb http://archive.launchpad.dev/ubuntu hoary
430 main restricted universe multiverse
431 deb http://archive.launchpad.dev/ubuntu hoary-security
432 main restricted universe multiverse
433 deb http://archive.launchpad.dev/ubuntu hoary-updates
434 main restricted universe multiverse
435 deb http://archive.launchpad.dev/ubuntu hoary-proposed
436 main restricted universe multiverse
437
438 >>> cprov.archive.removeArchiveDependency(ubuntu.main_archive)
439
440Similarly an extension of the BACKPORTS environment can be set.
441
442 >>> backports_dependency = cprov.archive.addArchiveDependency(
443 ... ubuntu.main_archive, PackagePublishingPocket.BACKPORTS,
444 ... getUtility(IComponentSet)['multiverse'])
445
446 >>> print_building_sources_list(a_build)
447 deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
448 deb http://archive.launchpad.dev/ubuntu hoary
449 main restricted universe multiverse
450 deb http://archive.launchpad.dev/ubuntu hoary-security
451 main restricted universe multiverse
452 deb http://archive.launchpad.dev/ubuntu hoary-updates
453 main restricted universe multiverse
454 deb http://archive.launchpad.dev/ubuntu hoary-backports
455 main restricted universe multiverse
456
457 >>> cprov.archive.removeArchiveDependency(ubuntu.main_archive)
458
459
460== Partner archive builds ==
461
462Similarly to what happens with PPA builds, PARTNER builds may depend
463on any Ubuntu component in the PRIMARY archive. This behaviour allows
464scenarios where partner packages may use other restricted/non-free
465applications from 'multiverse', for instance 'sun-java', and also
466other partner applications by default.
467
468 # Populate the ubuntutest PARTNER archive with one built and one
469 # pending build source.
470 >>> primary, partner = ubuntu.all_distro_archives
471 >>> unused_source = test_publisher.getPubSource(
472 ... archive=partner, component='partner')
473 >>> unused = test_publisher.getPubBinaries(
474 ... pub_source=unused_source,
475 ... status=PackagePublishingStatus.PUBLISHED)
476 >>> pub_source = test_publisher.getPubSource(
477 ... version='1.2', archive=partner, component='partner')
478 >>> [partner_build] = pub_source.createMissingBuilds()
479
480 >>> print_building_sources_list(partner_build)
481 deb http://archive.launchpad.dev/ubuntu-partner hoary partner
482 deb http://archive.launchpad.dev/ubuntu hoary
483 main restricted universe multiverse
484 deb http://archive.launchpad.dev/ubuntu hoary-security
485 main restricted universe multiverse
486 deb http://archive.launchpad.dev/ubuntu hoary-updates
487 main restricted universe multiverse
488
489PARTNER's PROPOSED pocket builds against itself, but still uses the
490default UPDATES dependency for PRIMARY unless overridden by
491ArchiveDependency.
492
493 >>> proposed_source = test_publisher.getPubSource(
494 ... version='1.2', archive=partner, component='partner',
495 ... pocket=PackagePublishingPocket.PROPOSED)
496 >>> unused = test_publisher.getPubBinaries(
497 ... archive=partner, distroseries=proposed_source.distroseries,
498 ... pocket=PackagePublishingPocket.PROPOSED,
499 ... status=PackagePublishingStatus.PUBLISHED)
500 >>> [partner_proposed_build] = proposed_source.createMissingBuilds()
501
502 >>> print_building_sources_list(partner_proposed_build)
503 deb http://archive.launchpad.dev/ubuntu-partner hoary partner
504 deb http://archive.launchpad.dev/ubuntu-partner hoary-proposed partner
505 deb http://archive.launchpad.dev/ubuntu hoary
506 main restricted universe multiverse
507 deb http://archive.launchpad.dev/ubuntu hoary-security
508 main restricted universe multiverse
509 deb http://archive.launchpad.dev/ubuntu hoary-updates
510 main restricted universe multiverse
511
512== External build dependencies ==
513
514Via an administrator change, any PPA hosted in launchpad can be
515assigned to one or more 'external' build dependencies additionally to
516the internal ones.
517
518There is a column on IArchive called 'external_dependencies' which can be set
519for any hosted PPA. It is a string listing the comma-separated external
520dependencies in the debian sources_list format.
521
522 deb http[s]://[user:pass@]<host>[/path] %(series)s[-pocket] [components]
523
524The '%(series)s' part is optional and will be replaced on-the-fly with
525the series name for the build record being dispatched.
526
527We will create some dependencies for Celso's PPA.
528
529 >>> cprov.archive.external_dependencies = (
530 ... "deb http://user:pass@repository zoing everything\n"
531 ... "deb http://user:pass@repository %(series)s public private\n"
532 ... "deb http://user:pass@repository %(series)s-extra public")
533
534Now builds in Celso's PPA will use the external dependencies.
535
536 >>> print_building_sources_list(a_build)
537 deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
538 deb http://user:pass@repository zoing everything
539 deb http://user:pass@repository hoary public private
540 deb http://user:pass@repository hoary-extra public
541 deb http://archive.launchpad.dev/ubuntu hoary
542 main restricted universe multiverse
543 deb http://archive.launchpad.dev/ubuntu hoary-security
544 main restricted universe multiverse
545 deb http://archive.launchpad.dev/ubuntu hoary-updates
546 main restricted universe multiverse
547
548 >>> cprov.archive.external_dependencies = None
549
550We can also set external dependencies for a single build.
551
552 >>> a_build.api_external_dependencies = (
553 ... u"deb http://user:pass@repository foo bar")
554 >>> print_building_sources_list(a_build)
555 deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
556 deb http://user:pass@repository foo bar
557 deb http://archive.launchpad.dev/ubuntu hoary
558 main restricted universe multiverse
559 deb http://archive.launchpad.dev/ubuntu hoary-security
560 main restricted universe multiverse
561 deb http://archive.launchpad.dev/ubuntu hoary-updates
562 main restricted universe multiverse
563 >>> a_build.api_external_dependencies = None
564
565
566== Build tools sources.list entries ==
567
568We can force an extra build tools line to be added to the sources.list,
569which is useful for specialised build types.
570
571 >>> for line in get_sources_list_for_building(
572 ... a_build, a_build.distro_arch_series,
573 ... a_build.source_package_release.name,
574 ... tools_source="deb http://example.org %(series)s main"):
575 ... print line
576 deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
577 deb http://example.org hoary main
578 deb http://archive.launchpad.dev/ubuntu hoary
579 main restricted universe multiverse
580 deb http://archive.launchpad.dev/ubuntu hoary-security
581 main restricted universe multiverse
582 deb http://archive.launchpad.dev/ubuntu hoary-updates
583 main restricted universe multiverse
584
585If tools_source is badly formatted, we log the error but don't blow up.
586(Note the missing "s" at the end of "%(series)".)
587
588 >>> from lp.services.log.logger import BufferLogger
589 >>> logger = BufferLogger()
590 >>> for line in get_sources_list_for_building(
591 ... a_build, a_build.distro_arch_series,
592 ... a_build.source_package_release.name,
593 ... tools_source="deb http://example.org %(series) main",
594 ... logger=logger):
595 ... print line
596 deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
597 deb http://archive.launchpad.dev/ubuntu hoary
598 main restricted universe multiverse
599 deb http://archive.launchpad.dev/ubuntu hoary-security
600 main restricted universe multiverse
601 deb http://archive.launchpad.dev/ubuntu hoary-updates
602 main restricted universe multiverse
603 >>> print logger.getLogBuffer()
604 ERROR Exception processing build tools sources.list entry:
605 ...
606
607
608== Overlays ==
609
610An overlay distroseries is a derived distribution which works like a PPA.
611This means that the parent's details gets added to the sources.list passed to
612the builders.
613
614 >>> depdistro = factory.makeDistribution('depdistro',
615 ... publish_base_url=u'http://archive.launchpad.dev/')
616 >>> depseries = factory.makeDistroSeries(
617 ... name='depseries', distribution=depdistro)
618 >>> deparchseries = factory.makeDistroArchSeries(
619 ... distroseries = depseries, architecturetag = 'i386')
620 >>> test_publisher.addFakeChroots(depseries)
621 >>> for component_name in ubuntu_components:
622 ... component = getUtility(IComponentSet)[component_name]
623 ... _ = factory.makeComponentSelection(depseries, component)
624 >>> universe_component = getUtility(IComponentSet)['universe']
625 >>> dsp = factory.makeDistroSeriesParent(
626 ... derived_series=hoary, parent_series=depseries,
627 ... initialized=True, is_overlay=True,
628 ... pocket=PackagePublishingPocket.SECURITY,
629 ... component=universe_component)
630 >>> pub_source = test_publisher.getPubSource(
631 ... version='1.1', archive=hoary.main_archive)
632 >>> [hoary_build] = pub_source.createMissingBuilds()
633 >>> print_building_sources_list(hoary_build)
634 deb http://archive.launchpad.dev/ubuntu hoary main
635 deb http://archive.launchpad.dev/depdistro depseries main universe
636 deb http://archive.launchpad.dev/depdistro depseries-security
637 main universe
6380
=== modified file 'lib/lp/soyuz/interfaces/archive.py'
--- lib/lp/soyuz/interfaces/archive.py 2016-11-14 19:55:07 +0000
+++ lib/lp/soyuz/interfaces/archive.py 2017-04-25 11:47:06 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2016 Canonical Ltd. This software is licensed under the1# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Archive interfaces."""4"""Archive interfaces."""
@@ -306,7 +306,7 @@
306306
307 def __init__(self, errors):307 def __init__(self, errors):
308 error_msg = 'Invalid external dependencies:\n%s\n' % '\n'.join(errors)308 error_msg = 'Invalid external dependencies:\n%s\n' % '\n'.join(errors)
309 super(Exception, self).__init__(self, error_msg)309 super(Exception, self).__init__(error_msg)
310 self.errors = errors310 self.errors = errors
311311
312312
313313
=== modified file 'lib/lp/soyuz/tests/test_archive.py'
--- lib/lp/soyuz/tests/test_archive.py 2016-11-07 16:42:23 +0000
+++ lib/lp/soyuz/tests/test_archive.py 2017-04-25 11:47:06 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2016 Canonical Ltd. This software is licensed under the1# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Test Archive features."""4"""Test Archive features."""
@@ -78,6 +78,7 @@
78 DuplicateTokenName,78 DuplicateTokenName,
79 IArchiveSet,79 IArchiveSet,
80 InsufficientUploadRights,80 InsufficientUploadRights,
81 InvalidExternalDependencies,
81 InvalidPocketForPartnerArchive,82 InvalidPocketForPartnerArchive,
82 InvalidPocketForPPA,83 InvalidPocketForPPA,
83 NAMED_AUTH_TOKEN_FEATURE_FLAG,84 NAMED_AUTH_TOKEN_FEATURE_FLAG,
@@ -1767,6 +1768,18 @@
1767 "person-name-.*/dependency/ubuntu distroseries-.* main")1768 "person-name-.*/dependency/ubuntu distroseries-.* main")
1768 self.assertThat(sources_list[0], matches)1769 self.assertThat(sources_list[0], matches)
17691770
1771 def test_invalid_external_dependencies(self):
1772 """Trying to set invalid external dependencies raises an exception."""
1773 ppa = self.factory.makeArchive()
1774 self.assertRaisesWithContent(
1775 InvalidExternalDependencies,
1776 "Invalid external dependencies:\n"
1777 "Malformed format string here --> %(series): "
1778 "Must start with 'deb'\n"
1779 "Malformed format string here --> %(series): Invalid URL\n",
1780 setattr, ppa, "external_dependencies",
1781 "Malformed format string here --> %(series)")
1782
17701783
1771class TestFindDepCandidates(TestCaseWithFactory):1784class TestFindDepCandidates(TestCaseWithFactory):
1772 """Tests for Archive.findDepCandidates."""1785 """Tests for Archive.findDepCandidates."""
17731786
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2016-12-02 12:04:11 +0000
+++ lib/lp/testing/factory.py 2017-04-25 11:47:06 +0000
@@ -2,7 +2,7 @@
2# NOTE: The first line above must stay first; do not move the copyright2# NOTE: The first line above must stay first; do not move the copyright
3# notice to the top. See http://www.python.org/dev/peps/pep-0263/.3# notice to the top. See http://www.python.org/dev/peps/pep-0263/.
4#4#
5# Copyright 2009-2016 Canonical Ltd. This software is licensed under the5# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
6# GNU Affero General Public License version 3 (see the file LICENSE).6# GNU Affero General Public License version 3 (see the file LICENSE).
77
8"""Testing infrastructure for the Launchpad application.8"""Testing infrastructure for the Launchpad application.
@@ -268,7 +268,10 @@
268 )268 )
269from lp.services.oauth.interfaces import IOAuthConsumerSet269from lp.services.oauth.interfaces import IOAuthConsumerSet
270from lp.services.openid.model.openididentifier import OpenIdIdentifier270from lp.services.openid.model.openididentifier import OpenIdIdentifier
271from lp.services.propertycache import clear_property_cache271from lp.services.propertycache import (
272 clear_property_cache,
273 get_property_cache,
274 )
272from lp.services.temporaryblobstorage.interfaces import (275from lp.services.temporaryblobstorage.interfaces import (
273 ITemporaryStorageManager,276 ITemporaryStorageManager,
274 )277 )
@@ -2832,8 +2835,10 @@
2832 if not IComponent.providedBy(component):2835 if not IComponent.providedBy(component):
2833 component = self.makeComponent(component)2836 component = self.makeComponent(component)
28342837
2835 return ComponentSelection(2838 selection = ComponentSelection(
2836 distroseries=distroseries, component=component)2839 distroseries=distroseries, component=component)
2840 del get_property_cache(distroseries).components
2841 return selection
28372842
2838 def makeArchive(self, distribution=None, owner=None, name=None,2843 def makeArchive(self, distribution=None, owner=None, name=None,
2839 purpose=None, enabled=True, private=False,2844 purpose=None, enabled=True, private=False,