Merge lp:~stevenk/launchpad/generic-overrides into lp:launchpad

Proposed by Steve Kowalik
Status: Merged
Approved by: Steve Kowalik
Approved revision: no longer in the source branch.
Merged at revision: 13041
Proposed branch: lp:~stevenk/launchpad/generic-overrides
Merge into: lp:launchpad
Diff against target: 469 lines (+438/-6)
3 files modified
lib/lp/soyuz/adapters/overrides.py (+215/-0)
lib/lp/soyuz/adapters/tests/test_overrides.py (+223/-0)
lib/lp/soyuz/adapters/tests/test_packagelocation.py (+0/-6)
To merge this branch: bzr merge lp:~stevenk/launchpad/generic-overrides
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+60730@code.launchpad.net

Commit message

[r=allenap][no-qa] Add three generic override support classes.

Description of the change

Add a generic overrides support class.

In this context, overrides are a description of which component, section and priority a source or binary package should be stored in. This is most used in sync'ing from Debian, where the component in Debian is 'main', but Ubuntu has the source in 'universe'. It also allows further copies or uploads to the same archive/distroseries to respect the previous settings and use them for that upload.

Nothing makes use of it in this branch, but it sets up the ground work. The package copier will make use of this framework in a future branch, and the plan is to have the publisher and the upload processor also make use of this framework.

I have also done two drive-bys in this branch, I moved a test into adapters/tests, and I deleted the test harness stuff from another test.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :
Download full text (8.6 KiB)

Looks good, but I have some comments about the choice of API. There
are also some problems in the way you're measuring query counts which
means that the tests have missed some problems. So, the meaty bits of
this branch are good, but it still needs some attention before
landing.

[1]

+ """Override policy that returns the SPN, component and section for
+ the latest published source publication, or the BPN, DAS, component,
+ section and priority for the latest published binary publication.
+ """

For the sake of our future selves and our children, please can you
write out SPN, BPN, etc. in full?

Also, the docstring policy is meant to be something like:

    """Short, single-line summary.

    Longer explanation.
    """

I assume it's like this so that it looks good in autogenerated
documentation, but I don't know how many people use it. I certainly
only look at the code.

[2]

+class BaseOverridePolicy:
+
+ def calculateOverrides(self, archive, distroseries, pocket, sources=None,
+ binaries=None):
+ raise NotImplementedError("Must be implemented by sub-class.")

Looking at the implementations - none of which want to be passed both
sources and binaries - I wonder why not split this into two methods,
calculateSourceOverrides() and calculateBinaryOverrides().

[3]

+ def findSourceOverrides(self, archive, distroseries, pocket, spns):
+ store = IStore(SourcePackagePublishingHistory)
+ already_published = DecoratedResultSet(
+ store.find(
+ (SQL("""DISTINCT ON (
+ SourcePackageRelease.sourcepackagename) 0 AS ignore"""),
+ SourcePackageRelease.sourcepackagenameID,
+ SourcePackagePublishingHistory.componentID,
+ SourcePackagePublishingHistory.sectionID),
+ SourcePackagePublishingHistory.pocket == pocket,
+ SourcePackagePublishingHistory.archiveID == archive.id,
+ SourcePackagePublishingHistory.distroseriesID ==
+ distroseries.id,
+ SourcePackagePublishingHistory.status.is_in(
+ active_publishing_status),
+ SourcePackageRelease.id ==
+ SourcePackagePublishingHistory.sourcepackagereleaseID,
+ SourcePackageRelease.sourcepackagenameID.is_in(
+ spn.id for spn in spns)).order_by(
+ SourcePackageRelease.sourcepackagenameID,
+ Desc(SourcePackagePublishingHistory.datecreated),
+ Desc(SourcePackagePublishingHistory.id)),
+ source_resolve_ids, pre_iter_hook=source_eager_load)
+ return list(already_published)

Why not join directly to SourcePackageName, Component and Section?
That would save the confusing dance with source_resolve_ids() and
source_eager_load() (both of which might be better off defined in
findSourceOverrides() too).

I tested it to see if I was missing something, but it seems not. The
following definition passes the tests:

    def findSourceOverrides(self, archive, distroseries, pocket, spns):
        store = IStore(SourcePacka...

Read more...

review: Needs Fixing
Revision history for this message
Gavin Panella (allenap) wrote :

> [4]
> ...
> Also, see [2].

Actually, see [3].

Revision history for this message
Steve Kowalik (stevenk) wrote :

[1]: Both done, thanks.

[2]: After thinking about this for a while, I JFDI'd, and I think you'll agree it looks cleaner.

[3]: Mainly because {Source,Binary}PackageName are large tables, and I don't want to bog down the query planner by making a complex query even more complex.

[4]: Nice catch! Missing ORDER BY added.

[5]: Shifted around, and switched to bulk.load().

[6]: There is, so I can call them directly from inside UbuntuOverridePolicy.

[7]: Indeed, it was masking the failure in the eager loading functions, thanks. It also pointed out that both source and binary query counts were wrong -- I was wondering why the source call only issued two queries when I was expecting four.

[8]: We may as well materialize them, since the caller is just going to loop over it anyway.

Revision history for this message
Steve Kowalik (stevenk) wrote :

[3]: Further, lots of the components and section will be the same, so we gain a large penalty by having to construct lots of similar objects inside Storm.

Revision history for this message
Gavin Panella (allenap) wrote :

Tip top, land it :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'lib/lp/soyuz/adapters/overrides.py'
2--- lib/lp/soyuz/adapters/overrides.py 1970-01-01 00:00:00 +0000
3+++ lib/lp/soyuz/adapters/overrides.py 2011-05-13 08:32:29 +0000
4@@ -0,0 +1,215 @@
5+# Copyright 2011 Canonical Ltd. This software is licensed under the
6+# GNU Affero General Public License version 3 (see the file LICENSE).
7+
8+"""Generic Override Policy classes.
9+"""
10+
11+__metaclass__ = type
12+
13+__all__ = [
14+ 'FromExistingOverridePolicy',
15+ 'UbuntuOverridePolicy',
16+ 'UnknownOverridePolicy',
17+ ]
18+
19+
20+from storm.expr import (
21+ And,
22+ Desc,
23+ Or,
24+ SQL,
25+ )
26+
27+from canonical.launchpad.components.decoratedresultset import (
28+ DecoratedResultSet,
29+ )
30+from canonical.launchpad.interfaces.lpstorm import IStore
31+from lp.registry.model.sourcepackagename import SourcePackageName
32+from lp.services.database import bulk
33+from lp.soyuz.interfaces.publishing import active_publishing_status
34+from lp.soyuz.model.binarypackagename import BinaryPackageName
35+from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
36+from lp.soyuz.model.component import Component
37+from lp.soyuz.model.distroarchseries import DistroArchSeries
38+from lp.soyuz.model.publishing import (
39+ BinaryPackagePublishingHistory,
40+ SourcePackagePublishingHistory,
41+ )
42+from lp.soyuz.model.section import Section
43+from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
44+
45+
46+class BaseOverridePolicy:
47+
48+ def calculateSourceOverrides(self, archive, distroseries, pocket,
49+ sources):
50+ raise NotImplementedError()
51+
52+ def calculateBinaryOverrides(self, archive, distroseries, pocket,
53+ binaries):
54+ raise NotImplementedError()
55+
56+
57+class FromExistingOverridePolicy(BaseOverridePolicy):
58+ """Override policy that only searches for existing publications.
59+
60+ Override policy that returns the SourcePackageName, component and
61+ section for the latest published source publication, or the
62+ BinaryPackageName, DistroArchSeries, component, section and priority
63+ for the latest published binary publication.
64+ """
65+
66+ def calculateSourceOverrides(self, archive, distroseries, pocket, spns):
67+ store = IStore(SourcePackagePublishingHistory)
68+ def eager_load(rows):
69+ bulk.load(Component, (row[2] for row in rows))
70+ bulk.load(Section, (row[3] for row in rows))
71+ already_published = DecoratedResultSet(
72+ store.find(
73+ (SQL("""DISTINCT ON (
74+ SourcePackageRelease.sourcepackagename) 0 AS ignore"""),
75+ SourcePackageRelease.sourcepackagenameID,
76+ SourcePackagePublishingHistory.componentID,
77+ SourcePackagePublishingHistory.sectionID),
78+ SourcePackagePublishingHistory.pocket == pocket,
79+ SourcePackagePublishingHistory.archiveID == archive.id,
80+ SourcePackagePublishingHistory.distroseriesID ==
81+ distroseries.id,
82+ SourcePackagePublishingHistory.status.is_in(
83+ active_publishing_status),
84+ SourcePackageRelease.id ==
85+ SourcePackagePublishingHistory.sourcepackagereleaseID,
86+ SourcePackageRelease.sourcepackagenameID.is_in(
87+ spn.id for spn in spns)).order_by(
88+ SourcePackageRelease.sourcepackagenameID,
89+ Desc(SourcePackagePublishingHistory.datecreated),
90+ Desc(SourcePackagePublishingHistory.id)),
91+ id_resolver(
92+ (SourcePackageName, Component, Section), slice(1, None)),
93+ pre_iter_hook=eager_load)
94+ return list(already_published)
95+
96+ def calculateBinaryOverrides(self, archive, distroseries, pocket,
97+ binaries):
98+ store = IStore(BinaryPackagePublishingHistory)
99+ def eager_load(rows):
100+ bulk.load(Component, (row[3] for row in rows))
101+ bulk.load(Section, (row[4] for row in rows))
102+ expanded = calculate_target_das(distroseries, binaries)
103+ candidates = (
104+ make_package_condition(archive, das, bpn)
105+ for bpn, das in expanded)
106+ already_published = DecoratedResultSet(
107+ store.find(
108+ (SQL("""DISTINCT ON (
109+ BinaryPackagePublishingHistory.distroarchseries,
110+ BinaryPackageRelease.binarypackagename) 0
111+ AS ignore"""),
112+ BinaryPackageRelease.binarypackagenameID,
113+ BinaryPackagePublishingHistory.distroarchseriesID,
114+ BinaryPackagePublishingHistory.componentID,
115+ BinaryPackagePublishingHistory.sectionID,
116+ BinaryPackagePublishingHistory.priority),
117+ BinaryPackagePublishingHistory.pocket == pocket,
118+ BinaryPackagePublishingHistory.status.is_in(
119+ active_publishing_status),
120+ BinaryPackageRelease.id ==
121+ BinaryPackagePublishingHistory.binarypackagereleaseID,
122+ Or(*candidates)).order_by(
123+ BinaryPackagePublishingHistory.distroarchseriesID,
124+ BinaryPackageRelease.binarypackagenameID,
125+ Desc(BinaryPackagePublishingHistory.datecreated),
126+ Desc(BinaryPackagePublishingHistory.id)),
127+ id_resolver(
128+ (BinaryPackageName, DistroArchSeries, Component, Section,
129+ None),
130+ slice(1, None)),
131+ pre_iter_hook=eager_load)
132+ return list(already_published)
133+
134+
135+class UnknownOverridePolicy(BaseOverridePolicy):
136+ """Override policy that returns defaults.
137+
138+ Override policy that assumes everything passed in doesn't exist, so
139+ returns the defaults.
140+ """
141+
142+ def calculateSourceOverrides(self, archive, distroseries, pocket,
143+ sources):
144+ default_component = archive.default_component or 'universe'
145+ return [(source, default_component, None) for source in sources]
146+
147+ def calculateBinaryOverrides(self, archive, distroseries, pocket,
148+ binaries):
149+ default_component = archive.default_component or 'universe'
150+ return [
151+ (binary, das, default_component, None, None)
152+ for binary, das in calculate_target_das(distroseries, binaries)]
153+
154+
155+class UbuntuOverridePolicy(FromExistingOverridePolicy,
156+ UnknownOverridePolicy):
157+ """Override policy for Ubuntu.
158+
159+ An override policy that incorporates both the from existing policy
160+ and the unknown policy.
161+ """
162+
163+ def calculateSourceOverrides(self, archive, distroseries, pocket,
164+ sources):
165+ total = set(sources)
166+ overrides = FromExistingOverridePolicy.calculateSourceOverrides(
167+ self, archive, distroseries, pocket, sources)
168+ existing = set(override[0] for override in overrides)
169+ missing = total.difference(existing)
170+ if missing:
171+ unknown = UnknownOverridePolicy.calculateSourceOverrides(
172+ self, archive, distroseries, pocket, missing)
173+ overrides.extend(unknown)
174+ return overrides
175+
176+ def calculateBinaryOverrides(self, archive, distroseries, pocket,
177+ binaries):
178+ total = set(binaries)
179+ overrides = FromExistingOverridePolicy.calculateBinaryOverrides(
180+ self, archive, distroseries, pocket, binaries)
181+ existing = set((
182+ overide[0], overide[1].architecturetag)
183+ for overide in overrides)
184+ missing = total.difference(existing)
185+ if missing:
186+ unknown = UnknownOverridePolicy.calculateBinaryOverrides(
187+ self, archive, distroseries, pocket, missing)
188+ overrides.extend(unknown)
189+ return overrides
190+
191+
192+def calculate_target_das(distroseries, binaries):
193+ arch_map = dict(
194+ (arch.architecturetag, arch)
195+ for arch in distroseries.enabled_architectures)
196+
197+ with_das = []
198+ for bpn, archtag in binaries:
199+ if archtag is not None:
200+ with_das.append((bpn, arch_map.get(archtag)))
201+ else:
202+ with_das.append((bpn, distroseries.nominatedarchindep))
203+ return with_das
204+
205+
206+def make_package_condition(archive, das, bpn):
207+ return And(
208+ BinaryPackagePublishingHistory.archiveID == archive.id,
209+ BinaryPackagePublishingHistory.distroarchseriesID == das.id,
210+ BinaryPackageRelease.binarypackagenameID == bpn.id)
211+
212+
213+def id_resolver(lookups, slice):
214+ def _resolve(row):
215+ store = IStore(SourcePackagePublishingHistory)
216+ return tuple(
217+ (value if cls is None else store.get(cls, value))
218+ for value, cls in zip(row[slice], lookups))
219+ return _resolve
220
221=== renamed file 'lib/lp/soyuz/tests/test_copypolicy.py' => 'lib/lp/soyuz/adapters/tests/test_copypolicy.py'
222=== added file 'lib/lp/soyuz/adapters/tests/test_overrides.py'
223--- lib/lp/soyuz/adapters/tests/test_overrides.py 1970-01-01 00:00:00 +0000
224+++ lib/lp/soyuz/adapters/tests/test_overrides.py 2011-05-13 08:32:29 +0000
225@@ -0,0 +1,223 @@
226+# Copyright 2011 Canonical Ltd. This software is licensed under the
227+# GNU Affero General Public License version 3 (see the file LICENSE).
228+
229+"""Test generic override policy classes."""
230+
231+from testtools.matchers import Equals
232+
233+from canonical.database.sqlbase import flush_database_caches
234+from canonical.testing.layers import LaunchpadZopelessLayer
235+from lp.services.database import bulk
236+from lp.soyuz.adapters.overrides import (
237+ FromExistingOverridePolicy,
238+ UbuntuOverridePolicy,
239+ UnknownOverridePolicy,
240+ )
241+from lp.soyuz.enums import PackagePublishingStatus
242+from lp.testing import (
243+ StormStatementRecorder,
244+ TestCaseWithFactory,
245+ )
246+from lp.testing.matchers import HasQueryCount
247+
248+
249+class TestOverrides(TestCaseWithFactory):
250+
251+ layer = LaunchpadZopelessLayer
252+
253+ def test_no_source_overrides(self):
254+ # If the spn is not published in the given archive/distroseries, an
255+ # empty list is returned.
256+ spn = self.factory.makeSourcePackageName()
257+ distroseries = self.factory.makeDistroSeries()
258+ pocket = self.factory.getAnyPocket()
259+ policy = FromExistingOverridePolicy()
260+ overrides = policy.calculateSourceOverrides(
261+ distroseries.main_archive, distroseries, pocket, (spn,))
262+ self.assertEqual([], overrides)
263+
264+ def test_source_overrides(self):
265+ # When the spn is published in the given archive/distroseries, the
266+ # overrides for that archive/distroseries are returned.
267+ spph = self.factory.makeSourcePackagePublishingHistory()
268+ policy = FromExistingOverridePolicy()
269+ overrides = policy.calculateSourceOverrides(
270+ spph.distroseries.main_archive, spph.distroseries, spph.pocket,
271+ (spph.sourcepackagerelease.sourcepackagename,))
272+ expected = [(
273+ spph.sourcepackagerelease.sourcepackagename,
274+ spph.component, spph.section)]
275+ self.assertEqual(expected, overrides)
276+
277+ def test_source_overrides_latest_only_is_returned(self):
278+ # When the spn is published multiple times in the given
279+ # archive/distroseries, the latest publication's overrides are
280+ # returned.
281+ spn = self.factory.makeSourcePackageName()
282+ distroseries = self.factory.makeDistroSeries()
283+ published_spr = self.factory.makeSourcePackageRelease(
284+ sourcepackagename=spn)
285+ self.factory.makeSourcePackagePublishingHistory(
286+ sourcepackagerelease=published_spr, distroseries=distroseries,
287+ status=PackagePublishingStatus.PUBLISHED)
288+ spr = self.factory.makeSourcePackageRelease(
289+ sourcepackagename=spn)
290+ spph = self.factory.makeSourcePackagePublishingHistory(
291+ sourcepackagerelease=spr, distroseries=distroseries)
292+ policy = FromExistingOverridePolicy()
293+ overrides = policy.calculateSourceOverrides(
294+ distroseries.main_archive, distroseries, spph.pocket, (spn,))
295+ self.assertEqual([(spn, spph.component, spph.section)], overrides)
296+
297+ def test_source_overrides_constant_query_count(self):
298+ # The query count is constant, no matter how many sources are
299+ # checked.
300+ spns = []
301+ distroseries = self.factory.makeDistroSeries()
302+ pocket = self.factory.getAnyPocket()
303+ for i in xrange(10):
304+ spph = self.factory.makeSourcePackagePublishingHistory(
305+ distroseries=distroseries, archive=distroseries.main_archive,
306+ pocket=pocket)
307+ spns.append(spph.sourcepackagerelease.sourcepackagename)
308+ flush_database_caches()
309+ distroseries.main_archive
310+ bulk.reload(spns)
311+ policy = FromExistingOverridePolicy()
312+ with StormStatementRecorder() as recorder:
313+ policy.calculateSourceOverrides(
314+ spph.distroseries.main_archive, spph.distroseries,
315+ spph.pocket, spns)
316+ self.assertThat(recorder, HasQueryCount(Equals(4)))
317+
318+ def test_no_binary_overrides(self):
319+ # if the given binary is not published in the given distroarchseries,
320+ # an empty list is returned.
321+ distroseries = self.factory.makeDistroSeries()
322+ das = self.factory.makeDistroArchSeries(distroseries=distroseries)
323+ distroseries.nominatedarchindep = das
324+ bpn = self.factory.makeBinaryPackageName()
325+ pocket = self.factory.getAnyPocket()
326+ policy = FromExistingOverridePolicy()
327+ overrides = policy.calculateBinaryOverrides(
328+ distroseries.main_archive, distroseries, pocket, ((bpn, None),))
329+ self.assertEqual([], overrides)
330+
331+ def test_binary_overrides(self):
332+ # When a binary is published in the given distroarchseries, the
333+ # overrides are returned.
334+ bpph = self.factory.makeBinaryPackagePublishingHistory()
335+ distroseries = bpph.distroarchseries.distroseries
336+ distroseries.nominatedarchindep = bpph.distroarchseries
337+ policy = FromExistingOverridePolicy()
338+ overrides = policy.calculateBinaryOverrides(
339+ distroseries.main_archive, distroseries, bpph.pocket,
340+ ((bpph.binarypackagerelease.binarypackagename, None),))
341+ expected = [(
342+ bpph.binarypackagerelease.binarypackagename,
343+ bpph.distroarchseries, bpph.component, bpph.section,
344+ bpph.priority)]
345+ self.assertEqual(expected, overrides)
346+
347+ def test_binary_overrides_constant_query_count(self):
348+ # The query count is constant, no matter how many bpn-das pairs are
349+ # checked.
350+ bpns = []
351+ distroarchseries = self.factory.makeDistroArchSeries()
352+ distroseries = distroarchseries.distroseries
353+ distroseries.nominatedarchindep = distroarchseries
354+ pocket = self.factory.getAnyPocket()
355+ for i in xrange(10):
356+ bpph = self.factory.makeBinaryPackagePublishingHistory(
357+ distroarchseries=distroarchseries,
358+ archive=distroseries.main_archive, pocket=pocket)
359+ bpns.append((bpph.binarypackagerelease.binarypackagename, None))
360+ flush_database_caches()
361+ distroseries.main_archive
362+ bulk.reload(bpn[0] for bpn in bpns)
363+ policy = FromExistingOverridePolicy()
364+ with StormStatementRecorder() as recorder:
365+ policy.calculateBinaryOverrides(
366+ distroseries.main_archive, distroseries, pocket, bpns)
367+ self.assertThat(recorder, HasQueryCount(Equals(4)))
368+
369+ def test_unknown_sources(self):
370+ # If the unknown policy is used, it does no checks, just returns the
371+ # defaults.
372+ spph = self.factory.makeSourcePackagePublishingHistory()
373+ policy = UnknownOverridePolicy()
374+ overrides = policy.calculateSourceOverrides(
375+ spph.distroseries.main_archive, spph.distroseries, spph.pocket,
376+ (spph.sourcepackagerelease.sourcepackagename,))
377+ expected = [(spph.sourcepackagerelease.sourcepackagename, 'universe',
378+ None)]
379+ self.assertEqual(expected, overrides)
380+
381+ def test_unknown_binaries(self):
382+ # If the unknown policy is used, it does no checks, just returns the
383+ # defaults.
384+ bpph = self.factory.makeBinaryPackagePublishingHistory()
385+ distroseries = bpph.distroarchseries.distroseries
386+ distroseries.nominatedarchindep = bpph.distroarchseries
387+ policy = UnknownOverridePolicy()
388+ overrides = policy.calculateBinaryOverrides(
389+ distroseries.main_archive, distroseries, bpph.pocket,
390+ ((bpph.binarypackagerelease.binarypackagename, None),))
391+ expected = [(bpph.binarypackagerelease.binarypackagename,
392+ bpph.distroarchseries, 'universe', None, None)]
393+ self.assertEqual(expected, overrides)
394+
395+ def test_ubuntu_override_policy_sources(self):
396+ # The Ubuntu policy incorporates both the existing and the unknown
397+ # policy.
398+ spns = [self.factory.makeSourcePackageName()]
399+ expected = [(spns[0], 'universe', None)]
400+ distroseries = self.factory.makeDistroSeries()
401+ pocket = self.factory.getAnyPocket()
402+ for i in xrange(8):
403+ spph = self.factory.makeSourcePackagePublishingHistory(
404+ distroseries=distroseries, archive=distroseries.main_archive,
405+ pocket=pocket)
406+ spns.append(spph.sourcepackagerelease.sourcepackagename)
407+ expected.append((
408+ spph.sourcepackagerelease.sourcepackagename, spph.component,
409+ spph.section))
410+ spns.append(self.factory.makeSourcePackageName())
411+ expected.append((spns[-1], 'universe', None))
412+ policy = UbuntuOverridePolicy()
413+ overrides = policy.calculateSourceOverrides(
414+ distroseries.main_archive, distroseries, pocket, spns)
415+ self.assertEqual(10, len(overrides))
416+ self.assertContentEqual(expected, overrides)
417+
418+ def test_ubuntu_override_policy_binaries(self):
419+ # The Ubuntu policy incorporates both the existing and the unknown
420+ # policy.
421+ distroseries = self.factory.makeDistroSeries()
422+ pocket = self.factory.getAnyPocket()
423+ bpn = self.factory.makeBinaryPackageName()
424+ bpns = []
425+ expected = []
426+ for i in xrange(3):
427+ distroarchseries = self.factory.makeDistroArchSeries(
428+ distroseries=distroseries)
429+ bpr = self.factory.makeBinaryPackageRelease(
430+ binarypackagename=bpn)
431+ bpph = self.factory.makeBinaryPackagePublishingHistory(
432+ binarypackagerelease=bpr, distroarchseries=distroarchseries,
433+ archive=distroseries.main_archive, pocket=pocket)
434+ bpns.append((bpn, distroarchseries.architecturetag))
435+ expected.append((
436+ bpn, distroarchseries, bpph.component, bpph.section,
437+ bpph.priority))
438+ for i in xrange(2):
439+ distroarchseries = self.factory.makeDistroArchSeries(
440+ distroseries=distroseries)
441+ bpns.append((bpn, distroarchseries.architecturetag))
442+ expected.append((bpn, distroarchseries, 'universe', None, None))
443+ distroseries.nominatedarchindep = distroarchseries
444+ policy = UbuntuOverridePolicy()
445+ overrides = policy.calculateBinaryOverrides(
446+ distroseries.main_archive, distroseries, pocket, bpns)
447+ self.assertEqual(5, len(overrides))
448+ self.assertContentEqual(expected, overrides)
449
450=== modified file 'lib/lp/soyuz/adapters/tests/test_packagelocation.py'
451--- lib/lp/soyuz/adapters/tests/test_packagelocation.py 2010-10-04 19:50:45 +0000
452+++ lib/lp/soyuz/adapters/tests/test_packagelocation.py 2011-05-13 08:32:29 +0000
453@@ -3,8 +3,6 @@
454
455 """Test PackageLocation class."""
456
457-import unittest
458-
459 from zope.component import getUtility
460
461 from canonical.testing.layers import LaunchpadZopelessLayer
462@@ -260,7 +258,3 @@
463 str(location_ubuntu_packageset),
464 'Primary Archive for Ubuntu Linux: '
465 'hoary-RELEASE [foo-packageset]')
466-
467-
468-def test_suite():
469- return unittest.TestLoader().loadTestsFromName(__name__)