Merge lp:~james-w/launchpad/test-package-cloner into lp:launchpad

Proposed by James Westby
Status: Merged
Merged at revision: 11142
Proposed branch: lp:~james-w/launchpad/test-package-cloner
Merge into: lp:launchpad
Prerequisite: lp:~james-w/launchpad/copy-archive-package-sets
Diff against target: 1506 lines (+745/-619)
4 files modified
lib/lp/soyuz/model/packagecloner.py (+63/-5)
lib/lp/soyuz/scripts/populate_archive.py (+2/-78)
lib/lp/soyuz/scripts/tests/test_populatearchive.py (+21/-536)
lib/lp/soyuz/tests/test_packagecloner.py (+659/-0)
To merge this branch: bzr merge lp:~james-w/launchpad/test-package-cloner
Reviewer Review Type Date Requested Status
Paul Hummer (community) code Approve
Review via email: mp+28405@code.launchpad.net

Commit message

Make PackageCloner create builds, and add some tests for it.

Description of the change

Summary

This moves creating builds from populate_archive.py to PackageCloner.

In addition it also adds to direct tests for the latter, moving most of the tests
that were testing its behaviour from using the script to do so to testing directly.

Pre-implementation notes

Checked with Julian that he was fine with PackageCloner doing this.

Implementation details

Nothing particularly interesting here, just a lot of code movement.

Tests

./bin/test -s lp.soyuz.scripts.tests -m test_populatearchive
./bin/test -s lp.soyuz.tests -m test_packagecloner

Demo and Q/A

None

lint

Linting changed files:
  lib/lp/soyuz/model/packagecloner.py
  lib/lp/soyuz/scripts/populate_archive.py
  lib/lp/soyuz/scripts/tests/test_populatearchive.py
  lib/lp/soyuz/tests/test_packagecloner.py

== Pyflakes notices ==

lib/lp/soyuz/scripts/populate_archive.py
    112: local variable 'ignore_this' is assigned to but never used
    279: local variable 'ignore_result' is assigned to but never used

Thanks,

James

To post a comment you must log in.
Revision history for this message
Paul Hummer (rockstar) wrote :

James, thanks for doing this. Do you need someone to land this for you?

review: Approve (code)
Revision history for this message
James Westby (james-w) wrote :

On Thu, 24 Jun 2010 16:18:27 -0000, Paul Hummer <email address hidden> wrote:
> Review: Approve code
> James, thanks for doing this. Do you need someone to land this for you?

No, I have that power if you can toggle the status for me.

Thanks,

James

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/soyuz/model/packagecloner.py'
2--- lib/lp/soyuz/model/packagecloner.py 2010-07-02 03:47:49 +0000
3+++ lib/lp/soyuz/model/packagecloner.py 2010-07-16 03:28:56 +0000
4@@ -11,11 +11,15 @@
5 ]
6
7
8+import transaction
9+
10 from zope.component import getUtility
11 from zope.interface import implements
12+from zope.security.proxy import removeSecurityProxy
13
14 from canonical.database.constants import UTC_NOW
15 from canonical.database.sqlbase import quote, sqlvalues
16+from lp.soyuz.interfaces.archivearch import IArchiveArchSet
17 from lp.soyuz.interfaces.publishing import PackagePublishingStatus
18 from lp.soyuz.interfaces.packagecloner import IPackageCloner
19 from canonical.launchpad.webapp.interfaces import (
20@@ -50,7 +54,8 @@
21
22 implements(IPackageCloner)
23
24- def clonePackages(self, origin, destination, distroarchseries_list=None):
25+ def clonePackages(self, origin, destination, distroarchseries_list=None,
26+ proc_families=None):
27 """Copies packages from origin to destination package location.
28
29 Binary packages are only copied for the `DistroArchSeries` pairs
30@@ -64,6 +69,7 @@
31 distroarchseries instances.
32 @param distroarchseries_list: the binary packages will be copied
33 for the distroarchseries pairs specified (if any).
34+ @param the processor families to create builds for.
35 """
36 # First clone the source packages.
37 self._clone_source_packages(origin, destination)
38@@ -75,6 +81,49 @@
39 self._clone_binary_packages(
40 origin, destination, origin_das, destination_das)
41
42+ if proc_families is None:
43+ proc_families = []
44+
45+ self._create_missing_builds(
46+ destination.distroseries, destination.archive, proc_families)
47+
48+ def _create_missing_builds(self, distroseries, archive, proc_families):
49+ """Create builds for all cloned source packages.
50+
51+ :param distroseries: the distro series for which to create builds.
52+ :param archive: the archive for which to create builds.
53+ :param proc_families: the list of processor families for
54+ which to create builds.
55+ """
56+ # Avoid circular imports.
57+ from lp.soyuz.interfaces.publishing import active_publishing_status
58+
59+ # Listify the architectures to avoid hitting this MultipleJoin
60+ # multiple times.
61+ architectures = list(distroseries.architectures)
62+
63+ # Filter the list of DistroArchSeries so that only the ones
64+ # specified in proc_families remain
65+ architectures = [architecture for architecture in architectures
66+ if architecture.processorfamily in proc_families]
67+
68+ if len(architectures) == 0:
69+ return
70+
71+ # Both, PENDING and PUBLISHED sources will be considered for
72+ # as PUBLISHED. It's part of the assumptions made in:
73+ # https://launchpad.net/soyuz/+spec/build-unpublished-source
74+ sources_published = archive.getPublishedSources(
75+ distroseries=distroseries, status=active_publishing_status)
76+
77+ def get_spn(pub):
78+ """Return the source package name for a publishing record."""
79+ return pub.sourcepackagerelease.sourcepackagename.name
80+
81+ for pubrec in sources_published:
82+ pubrec.createMissingBuilds(architectures_available=architectures)
83+ # Commit to avoid MemoryError: bug 304459
84+ transaction.commit()
85
86 def _clone_binary_packages(self, origin, destination, origin_das,
87 destination_das):
88@@ -153,6 +202,17 @@
89 """ % sqlvalues(
90 PackagePublishingStatus.SUPERSEDED, UTC_NOW))
91
92+ def get_family(archivearch):
93+ """Extract the processor family from an `IArchiveArch`."""
94+ return removeSecurityProxy(archivearch).processorfamily
95+
96+ proc_families = [
97+ get_family(archivearch) for archivearch
98+ in getUtility(IArchiveArchSet).getByArchive(destination.archive)]
99+
100+ self._create_missing_builds(
101+ destination.distroseries, destination.archive, proc_families)
102+
103 def _compute_packageset_delta(self, origin):
104 """Given a source/target archive find obsolete or missing packages.
105
106@@ -219,7 +279,7 @@
107 PackagePublishingStatus.PENDING,
108 PackagePublishingStatus.PUBLISHED,
109 origin.distroseries, origin.pocket)
110-
111+
112 if origin.component is not None:
113 find_origin_only_packages += (
114 " AND secsrc.component = %s" % quote(origin.component))
115@@ -288,7 +348,7 @@
116 PackagePublishingStatus.PENDING,
117 PackagePublishingStatus.PUBLISHED,
118 destination.distroseries, destination.pocket)
119-
120+
121 if destination.component is not None:
122 pop_query += (
123 " AND secsrc.component = %s" % quote(destination.component))
124@@ -389,5 +449,3 @@
125 logger.info('New packages: %d' % len(new_info))
126 for info in new_info:
127 logger.info('* %s (%s)' % info)
128-
129-
130
131=== modified file 'lib/lp/soyuz/scripts/populate_archive.py'
132--- lib/lp/soyuz/scripts/populate_archive.py 2010-07-02 03:47:49 +0000
133+++ lib/lp/soyuz/scripts/populate_archive.py 2010-07-16 03:28:56 +0000
134@@ -248,10 +248,6 @@
135 """Extract the processor family from an `IArchiveArch`."""
136 return removeSecurityProxy(archivearch).processorfamily
137
138- proc_families = [
139- get_family(archivearch) for archivearch
140- in getUtility(IArchiveArchSet).getByArchive(copy_archive)]
141-
142 # Now instantiate the package copy request that will capture the
143 # archive population parameters in the database.
144 pcr = getUtility(IPackageCopyRequestSet).new(
145@@ -269,12 +265,8 @@
146 if merge_copy_flag:
147 pkg_cloner.mergeCopy(the_origin, the_destination)
148 else:
149- pkg_cloner.clonePackages(the_origin, the_destination)
150-
151- # Create builds for the cloned packages.
152- self._createMissingBuilds(
153- the_destination.distroseries, the_destination.archive,
154- proc_families)
155+ pkg_cloner.clonePackages(
156+ the_origin, the_destination, proc_families=proc_families)
157
158 # Mark the package copy request as completed.
159 pcr.markAsCompleted()
160@@ -399,71 +391,3 @@
161 "--nonvirtualized", dest="nonvirtualized", default=False,
162 action="store_true",
163 help='Create the archive as nonvirtual if specified.')
164-
165- def _createMissingBuilds(
166- self, distroseries, archive, proc_families):
167- """Create builds for all cloned source packages.
168-
169- :param distroseries: the distro series for which to create builds.
170- :param archive: the archive for which to create builds.
171- :param proc_families: the list of processor families for
172- which to create builds.
173- """
174- # Avoid circular imports.
175- from lp.soyuz.interfaces.publishing import active_publishing_status
176-
177- self.logger.info("Processing %s." % distroseries.name)
178-
179- # Listify the architectures to avoid hitting this MultipleJoin
180- # multiple times.
181- architectures = list(distroseries.architectures)
182-
183- # Filter the list of DistroArchSeries so that only the ones
184- # specified on the command line remain.
185- architectures = [architecture for architecture in architectures
186- if architecture.processorfamily in proc_families]
187-
188- if len(architectures) == 0:
189- self.logger.info(
190- "No DistroArchSeries left for %s, done." % distroseries.name)
191- return
192-
193- self.logger.info(
194- "Supported architectures: %s." %
195- " ".join(arch_series.architecturetag
196- for arch_series in architectures))
197-
198- # Both, PENDING and PUBLISHED sources will be considered for
199- # as PUBLISHED. It's part of the assumptions made in:
200- # https://launchpad.net/soyuz/+spec/build-unpublished-source
201- sources_published = archive.getPublishedSources(
202- distroseries=distroseries, status=active_publishing_status)
203-
204- self.logger.info(
205- "Found %d source(s) published." % sources_published.count())
206-
207- def get_spn(pub):
208- """Return the source package name for a publishing record."""
209- return pub.sourcepackagerelease.sourcepackagename.name
210-
211- archindep_unavailable = distroseries.nominatedarchindep not in (
212- architectures)
213-
214- for pubrec in sources_published:
215- if (pubrec.sourcepackagerelease.architecturehintlist == "all"
216- and archindep_unavailable):
217- self.logger.info(
218- "Skipping %s, arch-all package can't be built since %s "
219- "is not requested" % (
220- get_spn(pubrec),
221- distroseries.nominatedarchindep.architecturetag))
222- continue
223-
224- builds = pubrec.createMissingBuilds(
225- architectures_available=architectures, logger=self.logger)
226- if len(builds) == 0:
227- self.logger.info("%s has no builds." % get_spn(pubrec))
228- else:
229- self.logger.info(
230- "%s has %s build(s)." % (get_spn(pubrec), len(builds)))
231- self.txn.commit()
232
233=== modified file 'lib/lp/soyuz/scripts/tests/test_populatearchive.py'
234--- lib/lp/soyuz/scripts/tests/test_populatearchive.py 2010-07-02 03:47:49 +0000
235+++ lib/lp/soyuz/scripts/tests/test_populatearchive.py 2010-07-16 03:28:56 +0000
236@@ -21,14 +21,10 @@
237 from lp.buildmaster.interfaces.buildbase import BuildStatus
238 from lp.registry.interfaces.distribution import IDistributionSet
239 from lp.registry.interfaces.person import IPersonSet
240-from lp.registry.interfaces.pocket import PackagePublishingPocket
241 from lp.services.job.interfaces.job import JobStatus
242 from lp.soyuz.interfaces.archive import ArchivePurpose, IArchiveSet
243 from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
244-from lp.soyuz.interfaces.packagecopyrequest import (
245- IPackageCopyRequestSet, PackageCopyStatus)
246 from lp.soyuz.interfaces.publishing import PackagePublishingStatus
247-from lp.soyuz.model.processor import ProcessorFamilySet
248 from lp.soyuz.scripts.ftpmaster import PackageLocationError, SoyuzScriptError
249 from lp.soyuz.scripts.populate_archive import ArchivePopulator
250 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
251@@ -41,18 +37,6 @@
252 return pub.sourcepackagerelease.sourcepackagename
253
254
255-class PackageInfo:
256-
257- def __init__(self, name, version,
258- status=PackagePublishingStatus.PUBLISHED, component="main",
259- arch_hint=None):
260- self.name = name
261- self.version = version
262- self.status = status
263- self.component = component
264- self.arch_hint = arch_hint
265-
266-
267 class TestPopulateArchiveScript(TestCaseWithFactory):
268 """Test the copy-package.py script."""
269
270@@ -86,6 +70,15 @@
271 stdout, stderr = process.communicate()
272 return (process.returncode, stdout, stderr)
273
274+ def getScript(self, test_args=None):
275+ """Return an ArchivePopulator instance."""
276+ if test_args is None:
277+ test_args = []
278+ script = ArchivePopulator("test copy archives", test_args=test_args)
279+ script.logger = QuietFakeLogger()
280+ script.txn = self.layer.txn
281+ return script
282+
283 def testCopyArchiveCreation(self):
284 """Start archive population, check data before and after.
285
286@@ -151,530 +144,11 @@
287
288 self.assertEqual(build_spns, self.expected_build_spns)
289
290- def createSourceDistroSeries(self):
291- """Create a DistroSeries suitable for copying.
292-
293- Creates a distroseries with a DistroArchSeries and nominatedarchindep,
294- which makes it suitable for copying because it will create some builds.
295- """
296- distro_name = "foobuntu"
297- distro = self.factory.makeDistribution(name=distro_name)
298- distroseries_name = "maudlin"
299- distroseries = self.factory.makeDistroSeries(
300- distribution=distro, name=distroseries_name)
301- das = self.factory.makeDistroArchSeries(
302- distroseries=distroseries, architecturetag="i386",
303- processorfamily=ProcessorFamilySet().getByName("x86"),
304- supports_virtualized=True)
305- distroseries.nominatedarchindep = das
306- return distroseries
307-
308- def createTargetOwner(self):
309- """Create a person suitable to own a copy archive."""
310- person_name = "copy-archive-owner"
311- owner = self.factory.makePerson(name=person_name)
312- return owner
313-
314- def getTargetArchiveName(self, distribution):
315- """Get a suitable name for a copy archive.
316-
317- It also checks that the archive doesn't currently exist.
318- """
319- archive_name = "msa%s" % int(time.time())
320- copy_archive = getUtility(IArchiveSet).getByDistroPurpose(
321- distribution, ArchivePurpose.COPY, archive_name)
322- # This is a sanity check: a copy archive with this name should not
323- # exist yet.
324- self.assertIs(None, copy_archive)
325- return archive_name
326-
327- def createSourcePublication(self, info, distroseries):
328- """Create a SourcePackagePublishingHistory based on a PackageInfo."""
329- if info.arch_hint is None:
330- arch_hint = "any"
331- else:
332- arch_hint = info.arch_hint
333-
334- self.factory.makeSourcePackagePublishingHistory(
335- sourcepackagename=self.factory.getOrMakeSourcePackageName(
336- name=info.name),
337- distroseries=distroseries, component=self.factory.makeComponent(
338- info.component),
339- version=info.version, architecturehintlist=arch_hint,
340- archive=distroseries.distribution.main_archive,
341- status=info.status, pocket=PackagePublishingPocket.RELEASE)
342-
343- def createSourcePublications(self, package_infos, distroseries):
344- """Create a source publication for each item in package_infos."""
345- for package_info in package_infos:
346- self.createSourcePublication(package_info, distroseries)
347-
348- def getScript(self, test_args=None):
349- """Return an ArchivePopulator instance."""
350- if test_args is None:
351- test_args = []
352- script = ArchivePopulator("test copy archives", test_args=test_args)
353- script.logger = QuietFakeLogger()
354- script.txn = self.layer.txn
355- return script
356-
357- def copyArchive(self, distroseries, archive_name, owner,
358- architectures=None, component="main", from_user=None,
359- from_archive=None, packageset_names=None, nonvirtualized=False):
360- """Run the copy-archive script."""
361- extra_args = [
362- '--from-distribution', distroseries.distribution.name,
363- '--from-suite', distroseries.name,
364- '--to-distribution', distroseries.distribution.name,
365- '--to-suite', distroseries.name,
366- '--to-archive', archive_name,
367- '--to-user', owner.name,
368- '--reason',
369- '"copy archive from %s"' % datetime.ctime(datetime.utcnow()),
370- '--component', component,
371- ]
372-
373- if from_user is not None:
374- extra_args.extend(["--from-user", from_user])
375-
376- if from_archive is not None:
377- extra_args.extend(["--from-archive", from_archive])
378-
379- if architectures is None:
380- architectures = ["386"]
381-
382- if nonvirtualized:
383- extra_args.extend(["--nonvirtualized"])
384-
385- for architecture in architectures:
386- extra_args.extend(['-a', architecture])
387-
388- if packageset_names is None:
389- packageset_names = []
390-
391- for packageset_name in packageset_names:
392- extra_args.extend(['--package-set', packageset_name])
393-
394- script = self.getScript(test_args=extra_args)
395- script.mainTask()
396-
397- # Make sure the copy archive with the desired name was
398- # created
399- copy_archive = getUtility(IArchiveSet).getByDistroPurpose(
400- distroseries.distribution, ArchivePurpose.COPY, archive_name)
401- self.assertTrue(copy_archive is not None)
402-
403- # Ascertain that the new copy archive was created with the 'enabled'
404- # flag turned off.
405- self.assertFalse(copy_archive.enabled)
406-
407- # Assert the virtualization is correct.
408- virtual = not nonvirtualized
409- self.assertEqual(copy_archive.require_virtualized, virtual)
410-
411- return copy_archive
412-
413- def checkCopiedSources(self, archive, distroseries, expected):
414- """Check the sources published in an archive against an expected set.
415-
416- Given an archive and a target distroseries the sources published in
417- that distroseries are checked against a set of PackageInfo to
418- ensure that the correct package names and versions are published.
419- """
420- expected_set = set([(info.name, info.version) for info in expected])
421- sources = archive.getPublishedSources(
422- distroseries=distroseries, status=self.pending_statuses)
423- actual_set = set()
424- for source in sources:
425- source = removeSecurityProxy(source)
426- actual_set.add(
427- (source.source_package_name, source.source_package_version))
428- self.assertEqual(expected_set, actual_set)
429-
430- def createSourceDistribution(self, package_infos):
431- """Create a distribution to be the source of a copy archive."""
432- distroseries = self.createSourceDistroSeries()
433- self.createSourcePublications(package_infos, distroseries)
434- return distroseries
435-
436- def makeCopyArchive(self, package_infos, component="main",
437- nonvirtualized=False):
438- """Make a copy archive based on a new distribution."""
439- owner = self.createTargetOwner()
440- distroseries = self.createSourceDistribution(package_infos)
441- archive_name = self.getTargetArchiveName(distroseries.distribution)
442- copy_archive = self.copyArchive(
443- distroseries, archive_name, owner, component=component,
444- nonvirtualized=nonvirtualized)
445- return (copy_archive, distroseries)
446-
447- def checkBuilds(self, archive, package_infos):
448- """Check the build records pending in an archive.
449-
450- Given a set of PackageInfo objects check that each has a build
451- created for it.
452- """
453- expected_builds = list(
454- [(info.name, info.version) for info in package_infos])
455- builds = list(
456- getUtility(IBinaryPackageBuildSet).getBuildsForArchive(
457- archive, status=BuildStatus.NEEDSBUILD))
458- actual_builds = list()
459- for build in builds:
460- naked_build = removeSecurityProxy(build)
461- spr = naked_build.source_package_release
462- actual_builds.append((spr.name, spr.version))
463- self.assertEqual(sorted(expected_builds), sorted(actual_builds))
464-
465- def testCopyArchiveRunScript(self):
466- """Check that we can exec the script to copy an archive."""
467- package_info = PackageInfo(
468- "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
469- owner = self.createTargetOwner()
470- distroseries = self.createSourceDistribution([package_info])
471- archive_name = self.getTargetArchiveName(distroseries.distribution)
472- # We must commit as we are going to exec a script that will run
473- # in a different transaction and must be able to see the
474- # objects we just created.
475- self.layer.commit()
476-
477- extra_args = [
478- '--from-distribution', distroseries.distribution.name,
479- '--from-suite', distroseries.name,
480- '--to-distribution', distroseries.distribution.name,
481- '--to-suite', distroseries.name,
482- '--to-archive', archive_name,
483- '--to-user', owner.name,
484- '--reason',
485- '"copy archive from %s"' % datetime.ctime(datetime.utcnow()),
486- '--component', "main",
487- '-a', '386',
488- ]
489- (exitcode, out, err) = self.runWrapperScript(extra_args)
490- # Check for zero exit code.
491- self.assertEqual(
492- exitcode, 0, "\n=> %s\n=> %s\n=> %s\n" % (exitcode, out, err))
493- # Make sure the copy archive with the desired name was
494- # created
495- copy_archive = getUtility(IArchiveSet).getByDistroPurpose(
496- distroseries.distribution, ArchivePurpose.COPY, archive_name)
497- self.assertTrue(copy_archive is not None)
498-
499- # Ascertain that the new copy archive was created with the 'enabled'
500- # flag turned off.
501- self.assertFalse(copy_archive.enabled)
502-
503- # Also, make sure that the builds for the new copy archive will be
504- # carried out on non-virtual builders.
505- self.assertTrue(copy_archive.require_virtualized)
506- self.checkCopiedSources(
507- copy_archive, distroseries, [package_info])
508-
509- def testCopyArchiveCreateCopiesPublished(self):
510- """Test that PUBLISHED sources are copied."""
511- package_info = PackageInfo(
512- "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
513- copy_archive, distroseries = self.makeCopyArchive([package_info])
514- self.checkCopiedSources(
515- copy_archive, distroseries, [package_info])
516-
517- def testCopyArchiveCreateCopiesPending(self):
518- """Test that PENDING sources are copied."""
519- package_info = PackageInfo(
520- "bzr", "2.1", status=PackagePublishingStatus.PENDING)
521- copy_archive, distroseries = self.makeCopyArchive([package_info])
522- self.checkCopiedSources(
523- copy_archive, distroseries, [package_info])
524-
525- def testCopyArchiveCreateDoesntCopySuperseded(self):
526- """Test that SUPERSEDED sources are not copied."""
527- package_info = PackageInfo(
528- "bzr", "2.1", status=PackagePublishingStatus.SUPERSEDED)
529- copy_archive, distroseries = self.makeCopyArchive([package_info])
530- self.checkCopiedSources(
531- copy_archive, distroseries, [])
532-
533- def testCopyArchiveCreateDoesntCopyDeleted(self):
534- """Test that DELETED sources are not copied."""
535- package_info = PackageInfo(
536- "bzr", "2.1", status=PackagePublishingStatus.DELETED)
537- copy_archive, distroseries = self.makeCopyArchive([package_info])
538- self.checkCopiedSources(
539- copy_archive, distroseries, [])
540-
541- def testCopyArchiveCreateDoesntCopyObsolete(self):
542- """Test that OBSOLETE sources are not copied."""
543- package_info = PackageInfo(
544- "bzr", "2.1", status=PackagePublishingStatus.OBSOLETE)
545- copy_archive, distroseries = self.makeCopyArchive([package_info])
546- self.checkCopiedSources(
547- copy_archive, distroseries, [])
548-
549- def testCopyArchiveCreatesBuilds(self):
550- """Test that a copy archive creates builds for the copied packages."""
551- package_info = PackageInfo(
552- "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
553- copy_archive, distroseries = self.makeCopyArchive([package_info])
554- self.checkBuilds(copy_archive, [package_info])
555-
556- def testCopyArchiveArchTagNotAvailableInSource(self):
557- """Test creating a copy archive for an arch not in the source.
558-
559- If we request a copy to an architecture that doesn't have
560- a DistroArchSeries in the source then we won't get any builds
561- created in the copy archive.
562- """
563- family = self.factory.makeProcessorFamily(name="armel")
564- self.factory.makeProcessor(family=family, name="armel")
565- package_info = PackageInfo(
566- "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
567- owner = self.createTargetOwner()
568- # Creates an archive with just x86
569- distroseries = self.createSourceDistribution([package_info])
570- archive_name = self.getTargetArchiveName(distroseries.distribution)
571- # Different architecture, so there won't be any builds
572- copy_archive = self.copyArchive(
573- distroseries, archive_name, owner, architectures=["armel"])
574- self.checkBuilds(copy_archive, [])
575-
576- # Also, make sure the package copy request status was updated.
577- [pcr] = getUtility(
578- IPackageCopyRequestSet).getByTargetArchive(copy_archive)
579- self.assertTrue(pcr.status == PackageCopyStatus.COMPLETE)
580-
581- # This date is set when the copy request makes the transition to
582- # the "in progress" state.
583- self.assertTrue(pcr.date_started is not None)
584- # This date is set when the copy request makes the transition to
585- # the "completed" state.
586- self.assertTrue(pcr.date_completed is not None)
587- self.assertTrue(pcr.date_started <= pcr.date_completed)
588-
589- def testMultipleArchTagsWithSubsetInSource(self):
590- """Try copy archive population with multiple architecture tags.
591-
592- The user may specify a number of given architecture tags on the
593- command line.
594- The script should create build records only for the specified
595- architecture tags that are supported by the destination distro series.
596-
597- In this (test) case the script should create the build records for the
598- '386' architecture.
599- """
600- family = self.factory.makeProcessorFamily(name="armel")
601- self.factory.makeProcessor(family=family, name="armel")
602- package_info = PackageInfo(
603- "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
604- owner = self.createTargetOwner()
605- # Creates an archive with just x86
606- distroseries = self.createSourceDistribution([package_info])
607- archive_name = self.getTargetArchiveName(distroseries.distribution)
608- # There is only a DAS for i386, so armel won't produce any
609- # builds
610- copy_archive = self.copyArchive(
611- distroseries, archive_name, owner,
612- architectures=["386", "armel"])
613- self.checkBuilds(copy_archive, [package_info])
614-
615- def testCopyArchiveCreatesSubsetOfBuilds(self):
616- """Create a copy archive with a subset of the architectures.
617-
618- We copy from an archive with multiple architecture DistroArchSeries,
619- but request only one of those architectures in the target,
620- so we only get builds for that one architecture.
621- """
622- package_info = PackageInfo(
623- "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
624- owner = self.createTargetOwner()
625- distroseries = self.createSourceDistribution([package_info])
626- self.factory.makeDistroArchSeries(
627- distroseries=distroseries, architecturetag="amd64",
628- processorfamily=ProcessorFamilySet().getByName("amd64"),
629- supports_virtualized=True)
630- archive_name = self.getTargetArchiveName(distroseries.distribution)
631- copy_archive = self.copyArchive(
632- distroseries, archive_name, owner,
633- architectures=["386"])
634- # We only get a single build, as we only requested 386, not
635- # amd64 too
636- self.checkBuilds(copy_archive, [package_info])
637-
638- def testNoBuildsForArchAll(self):
639- # If we have a copy for an architecture that is not the
640- # nominatedarchindep architecture, then we don't want to create
641- # builds for arch-all packages, as they can't be built at all
642- # and createMissingBuilds blows up when it checks that.
643- package_info = PackageInfo(
644- "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED,
645- arch_hint="all")
646- owner = self.createTargetOwner()
647- distroseries = self.createSourceDistribution([package_info])
648- self.factory.makeDistroArchSeries(
649- distroseries=distroseries, architecturetag="amd64",
650- processorfamily=ProcessorFamilySet().getByName("amd64"),
651- supports_virtualized=True)
652- archive_name = self.getTargetArchiveName(distroseries.distribution)
653- copy_archive = self.copyArchive(
654- distroseries, archive_name, owner,
655- architectures=["amd64"])
656- # We don't get any builds since amd64 is not the
657- # nomindatedarchindep, i386 is.
658- self.assertEqual(
659- distroseries.nominatedarchindep.architecturetag, "i386")
660- self.checkBuilds(copy_archive, [])
661-
662- def testMultipleArchTags(self):
663- """Test copying an archive with multiple architectures.
664-
665- We create a source with two architectures, and then request
666- a copy of both, so we get a build for each of those architectures.
667- """
668- package_info = PackageInfo(
669- "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
670- owner = self.createTargetOwner()
671- distroseries = self.createSourceDistribution([package_info])
672- self.factory.makeDistroArchSeries(
673- distroseries=distroseries, architecturetag="amd64",
674- processorfamily=ProcessorFamilySet().getByName("amd64"),
675- supports_virtualized=True)
676- archive_name = self.getTargetArchiveName(distroseries.distribution)
677- copy_archive = self.copyArchive(
678- distroseries, archive_name, owner,
679- architectures=["386", "amd64"])
680- self.checkBuilds(copy_archive, [package_info, package_info])
681-
682- def testCopyArchiveCopiesRightComponents(self):
683- """Test that packages from the right components are copied.
684-
685- When copying you specify a component, that component should
686- limit the packages copied. We create a source in main and one in
687- universe, and then copy with --component main, and expect to see
688- only main in the copy.
689- """
690- package_info_universe = PackageInfo(
691- "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED,
692- component="universe")
693- package_info_main = PackageInfo(
694- "apt", "2.2", status=PackagePublishingStatus.PUBLISHED,
695- component="main")
696- package_infos_both = [package_info_universe, package_info_main]
697- copy_archive, distroseries = self.makeCopyArchive(
698- package_infos_both, component="main")
699- self.checkBuilds(copy_archive, [package_info_main])
700-
701- def testCopyArchiveSubsetsBasedOnPackageset(self):
702- """Test that --package-set limits the sources copied."""
703- package_infos = [
704- PackageInfo(
705- "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
706- PackageInfo(
707- "apt", "2.2", status=PackagePublishingStatus.PUBLISHED),
708- ]
709- owner = self.createTargetOwner()
710- distroseries = self.createSourceDistribution(package_infos)
711- packageset_name = u"apt-packageset"
712- spn = self.factory.getOrMakeSourcePackageName(name="apt")
713- self.factory.makePackageset(
714- name=packageset_name, distroseries=distroseries, packages=(spn,))
715- archive_name = self.getTargetArchiveName(distroseries.distribution)
716- copy_archive = self.copyArchive(
717- distroseries, archive_name, owner,
718- packageset_names=[packageset_name])
719- self.checkCopiedSources(
720- copy_archive, distroseries, [package_infos[1]])
721-
722- def testCopyArchiveUnionsPackagesets(self):
723- """Test that package sets are unioned when copying archives."""
724- package_infos = [
725- PackageInfo(
726- "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
727- PackageInfo(
728- "apt", "2.2", status=PackagePublishingStatus.PUBLISHED),
729- PackageInfo(
730- "gcc", "4.5", status=PackagePublishingStatus.PUBLISHED),
731- ]
732- owner = self.createTargetOwner()
733- distroseries = self.createSourceDistribution(package_infos)
734- apt_packageset_name = u"apt-packageset"
735- apt_spn = self.factory.getOrMakeSourcePackageName(name="apt")
736- gcc_packageset_name = u"gcc-packageset"
737- gcc_spn = self.factory.getOrMakeSourcePackageName(name="gcc")
738- self.factory.makePackageset(
739- name=apt_packageset_name, distroseries=distroseries,
740- packages=(apt_spn,))
741- self.factory.makePackageset(
742- name=gcc_packageset_name, distroseries=distroseries,
743- packages=(gcc_spn,))
744- archive_name = self.getTargetArchiveName(distroseries.distribution)
745- copy_archive = self.copyArchive(
746- distroseries, archive_name, owner,
747- packageset_names=[apt_packageset_name, gcc_packageset_name])
748- self.checkCopiedSources(
749- copy_archive, distroseries, package_infos[1:])
750-
751- def testCopyArchiveRecursivelyCopiesPackagesets(self):
752- """Test that package set copies include subsets."""
753- package_infos = [
754- PackageInfo(
755- "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
756- PackageInfo(
757- "apt", "2.2", status=PackagePublishingStatus.PUBLISHED),
758- PackageInfo(
759- "gcc", "4.5", status=PackagePublishingStatus.PUBLISHED),
760- ]
761- owner = self.createTargetOwner()
762- distroseries = self.createSourceDistribution(package_infos)
763- apt_packageset_name = u"apt-packageset"
764- apt_spn = self.factory.getOrMakeSourcePackageName(name="apt")
765- gcc_packageset_name = u"gcc-packageset"
766- gcc_spn = self.factory.getOrMakeSourcePackageName(name="gcc")
767- apt_packageset = self.factory.makePackageset(
768- name=apt_packageset_name, distroseries=distroseries,
769- packages=(apt_spn,))
770- gcc_packageset = self.factory.makePackageset(
771- name=gcc_packageset_name, distroseries=distroseries,
772- packages=(gcc_spn,))
773- apt_packageset.add((gcc_packageset,))
774- archive_name = self.getTargetArchiveName(distroseries.distribution)
775- copy_archive = self.copyArchive(
776- distroseries, archive_name, owner,
777- packageset_names=[apt_packageset_name])
778- self.checkCopiedSources(
779- copy_archive, distroseries, package_infos[1:])
780-
781- def testCopyFromPPA(self):
782- """Test we can create a copy archive with a PPA as the source."""
783- ppa_owner_name = "ppa-owner"
784- ppa_name = "ppa"
785- ppa_owner = self.factory.makePerson(name=ppa_owner_name)
786- distroseries = self.createSourceDistroSeries()
787- ppa = self.factory.makeArchive(
788- name=ppa_name, purpose=ArchivePurpose.PPA,
789- distribution=distroseries.distribution, owner=ppa_owner)
790- package_info = PackageInfo(
791- "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED,
792- component="universe")
793- self.factory.makeSourcePackagePublishingHistory(
794- sourcepackagename=self.factory.getOrMakeSourcePackageName(
795- name=package_info.name),
796- distroseries=distroseries, component=self.factory.makeComponent(
797- package_info.component),
798- version=package_info.version, archive=ppa,
799- status=package_info.status, architecturehintlist='any',
800- pocket=PackagePublishingPocket.RELEASE)
801- owner = self.createTargetOwner()
802- archive_name = self.getTargetArchiveName(distroseries.distribution)
803- copy_archive = self.copyArchive(
804- distroseries, archive_name, owner, from_user=ppa_owner_name,
805- from_archive=ppa_name, component=package_info.component)
806- self.checkCopiedSources(
807- copy_archive, distroseries, [package_info])
808-
809 def runScript(
810 self, archive_name=None, suite='hoary', user='salgado',
811 exists_before=None, exists_after=None, exception_type=None,
812 exception_text=None, extra_args=None, copy_archive_name=None,
813- reason=None, output_substr=None):
814+ reason=None, output_substr=None, nonvirtualized=False):
815 """Run the script to test.
816
817 :type archive_name: `str`
818@@ -748,6 +222,9 @@
819 reason = "copy archive, %s" % datetime.ctime(datetime.utcnow())
820 script_args.extend(['--reason', reason])
821
822+ if nonvirtualized:
823+ script_args.append('--nonvirtualized')
824+
825 if extra_args is not None:
826 script_args.extend(extra_args)
827
828@@ -847,6 +324,14 @@
829 exception_text="Could not find packageset No such package set"
830 " (in the specified distro series): '%s'." % unknown_packageset)
831
832+ def testNonvirtualized(self):
833+ """--nonvirtualized means the archive won't require virtualization."""
834+ copy_archive = self.runScript(
835+ archive_name="copy-archive-test", exists_before=False,
836+ exists_after=True, nonvirtualized=True,
837+ extra_args=['-a', '386'])
838+ self.assertFalse(copy_archive.require_virtualized)
839+
840 def testPackagesetDelta(self):
841 """Try to calculate the delta between two source package sets."""
842 hoary = getUtility(IDistributionSet)['ubuntu']['hoary']
843
844=== added file 'lib/lp/soyuz/tests/test_packagecloner.py'
845--- lib/lp/soyuz/tests/test_packagecloner.py 1970-01-01 00:00:00 +0000
846+++ lib/lp/soyuz/tests/test_packagecloner.py 2010-07-16 03:28:56 +0000
847@@ -0,0 +1,659 @@
848+# Copyright 2009 Canonical Ltd. This software is licensed under the
849+# GNU Affero General Public License version 3 (see the file LICENSE).
850+
851+__metaclass__ = type
852+
853+from zope.component import getUtility
854+from zope.security.proxy import removeSecurityProxy
855+
856+from canonical.testing import LaunchpadZopelessLayer
857+
858+from lp.buildmaster.interfaces.buildbase import BuildStatus
859+from lp.registry.interfaces.pocket import PackagePublishingPocket
860+from lp.soyuz.adapters.packagelocation import PackageLocation
861+from lp.soyuz.interfaces.archive import ArchivePurpose
862+from lp.soyuz.interfaces.archivearch import IArchiveArchSet
863+from lp.soyuz.interfaces.component import IComponentSet
864+from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
865+from lp.soyuz.interfaces.packagecloner import IPackageCloner
866+from lp.soyuz.interfaces.publishing import (
867+ IPublishingSet, PackagePublishingStatus)
868+from lp.soyuz.model.processor import ProcessorFamilySet
869+from lp.testing import TestCaseWithFactory
870+
871+
872+class PackageInfo:
873+
874+ def __init__(self, name, version,
875+ status=PackagePublishingStatus.PUBLISHED, component="main"):
876+ self.name = name
877+ self.version = version
878+ self.status = status
879+ self.component = component
880+
881+
882+class PackageClonerTests(TestCaseWithFactory):
883+
884+ layer = LaunchpadZopelessLayer
885+
886+ def checkCopiedSources(self, archive, distroseries, expected):
887+ """Check the sources published in an archive against an expected set.
888+
889+ Given an archive and a target distroseries the sources published in
890+ that distroseries are checked against a set of PackageInfo to
891+ ensure that the correct package names and versions are published.
892+ """
893+ expected_set = set([(info.name, info.version) for info in expected])
894+ sources = archive.getPublishedSources(
895+ distroseries=distroseries,
896+ status=(PackagePublishingStatus.PENDING,
897+ PackagePublishingStatus.PUBLISHED))
898+ actual_set = set()
899+ for source in sources:
900+ source = removeSecurityProxy(source)
901+ actual_set.add(
902+ (source.source_package_name, source.source_package_version))
903+ self.assertEqual(expected_set, actual_set)
904+
905+ def createSourceDistribution(self, package_infos):
906+ """Create a distribution to be the source of a copy archive."""
907+ distroseries = self.createSourceDistroSeries()
908+ self.createSourcePublications(package_infos, distroseries)
909+ return distroseries
910+
911+ def createSourceDistroSeries(self):
912+ """Create a DistroSeries suitable for copying.
913+
914+ Creates a distroseries with a DistroArchSeries and nominatedarchindep,
915+ which makes it suitable for copying because it will create some builds.
916+ """
917+ distro_name = "foobuntu"
918+ distro = self.factory.makeDistribution(name=distro_name)
919+ distroseries_name = "maudlin"
920+ distroseries = self.factory.makeDistroSeries(
921+ distribution=distro, name=distroseries_name)
922+ das = self.factory.makeDistroArchSeries(
923+ distroseries=distroseries, architecturetag="i386",
924+ processorfamily=ProcessorFamilySet().getByName("x86"),
925+ supports_virtualized=True)
926+ distroseries.nominatedarchindep = das
927+ return distroseries
928+
929+ def getTargetArchive(self, distribution):
930+ """Get a target archive for copying in to."""
931+ return self.factory.makeArchive(
932+ name="test-copy-archive", purpose=ArchivePurpose.COPY,
933+ distribution=distribution)
934+
935+ def createSourcePublication(self, info, distroseries):
936+ """Create a SourcePackagePublishingHistory based on a PackageInfo."""
937+ archive = distroseries.distribution.main_archive
938+ sources = archive.getPublishedSources(
939+ distroseries=distroseries,
940+ status=(PackagePublishingStatus.PENDING,
941+ PackagePublishingStatus.PUBLISHED),
942+ name=info.name, exact_match=True)
943+ for src in sources:
944+ src.supersede()
945+ self.factory.makeSourcePackagePublishingHistory(
946+ sourcepackagename=self.factory.getOrMakeSourcePackageName(
947+ name=info.name),
948+ distroseries=distroseries, component=self.factory.makeComponent(
949+ info.component),
950+ version=info.version, architecturehintlist='any',
951+ archive=archive, status=info.status,
952+ pocket=PackagePublishingPocket.RELEASE)
953+
954+ def createSourcePublications(self, package_infos, distroseries):
955+ """Create a source publication for each item in package_infos."""
956+ for package_info in package_infos:
957+ self.createSourcePublication(package_info, distroseries)
958+
959+ def makeCopyArchive(self, package_infos, component="main",
960+ source_pocket=None, target_pocket=None,
961+ proc_families=None):
962+ """Make a copy archive based on a new distribution."""
963+ distroseries = self.createSourceDistribution(package_infos)
964+ copy_archive = self.getTargetArchive(distroseries.distribution)
965+ to_component = getUtility(IComponentSet).ensure(component)
966+ self.copyArchive(
967+ copy_archive, distroseries, from_pocket=source_pocket,
968+ to_pocket=target_pocket, to_component=to_component,
969+ proc_families=proc_families)
970+ return (copy_archive, distroseries)
971+
972+ def checkBuilds(self, archive, package_infos):
973+ """Check the build records pending in an archive.
974+
975+ Given a set of PackageInfo objects check that each has a build
976+ created for it.
977+ """
978+ expected_builds = list(
979+ [(info.name, info.version) for info in package_infos])
980+ builds = list(
981+ getUtility(IBinaryPackageBuildSet).getBuildsForArchive(
982+ archive, status=BuildStatus.NEEDSBUILD))
983+ actual_builds = list()
984+ for build in builds:
985+ naked_build = removeSecurityProxy(build)
986+ spr = naked_build.source_package_release
987+ actual_builds.append((spr.name, spr.version))
988+ self.assertEqual(sorted(expected_builds), sorted(actual_builds))
989+
990+ def copyArchive(self, to_archive, to_distroseries, from_archive=None,
991+ from_distroseries=None, from_pocket=None, to_pocket=None,
992+ to_component=None, packagesets=None, proc_families=None):
993+ """Use a PackageCloner to copy an archive."""
994+ if from_distroseries is None:
995+ from_distroseries = to_distroseries
996+ if from_archive is None:
997+ from_archive = from_distroseries.distribution.main_archive
998+ if from_pocket is None:
999+ from_pocket = PackagePublishingPocket.RELEASE
1000+ if to_pocket is None:
1001+ to_pocket = PackagePublishingPocket.RELEASE
1002+ if packagesets is None:
1003+ packagesets = []
1004+ origin = PackageLocation(
1005+ from_archive, from_distroseries.distribution, from_distroseries,
1006+ from_pocket)
1007+ destination = PackageLocation(
1008+ to_archive, to_distroseries.distribution, to_distroseries,
1009+ to_pocket)
1010+ origin.packagesets = packagesets
1011+ if to_component is not None:
1012+ destination.component = to_component
1013+ cloner = getUtility(IPackageCloner)
1014+ cloner.clonePackages(
1015+ origin, destination, distroarchseries_list=None,
1016+ proc_families=proc_families)
1017+ return cloner
1018+
1019+ def testCopiesPublished(self):
1020+ """Test that PUBLISHED sources are copied."""
1021+ package_info = PackageInfo(
1022+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
1023+ copy_archive, distroseries = self.makeCopyArchive([package_info])
1024+ self.checkCopiedSources(
1025+ copy_archive, distroseries, [package_info])
1026+
1027+ def testCopiesPending(self):
1028+ """Test that PENDING sources are copied."""
1029+ package_info = PackageInfo(
1030+ "bzr", "2.1", status=PackagePublishingStatus.PENDING)
1031+ copy_archive, distroseries = self.makeCopyArchive([package_info])
1032+ self.checkCopiedSources(
1033+ copy_archive, distroseries, [package_info])
1034+
1035+ def testDoesntCopySuperseded(self):
1036+ """Test that SUPERSEDED sources are not copied."""
1037+ package_info = PackageInfo(
1038+ "bzr", "2.1", status=PackagePublishingStatus.SUPERSEDED)
1039+ copy_archive, distroseries = self.makeCopyArchive([package_info])
1040+ self.checkCopiedSources(
1041+ copy_archive, distroseries, [])
1042+
1043+ def testDoesntCopyDeleted(self):
1044+ """Test that DELETED sources are not copied."""
1045+ package_info = PackageInfo(
1046+ "bzr", "2.1", status=PackagePublishingStatus.DELETED)
1047+ copy_archive, distroseries = self.makeCopyArchive([package_info])
1048+ self.checkCopiedSources(
1049+ copy_archive, distroseries, [])
1050+
1051+ def testDoesntCopyObsolete(self):
1052+ """Test that OBSOLETE sources are not copied."""
1053+ package_info = PackageInfo(
1054+ "bzr", "2.1", status=PackagePublishingStatus.OBSOLETE)
1055+ copy_archive, distroseries = self.makeCopyArchive([package_info])
1056+ self.checkCopiedSources(
1057+ copy_archive, distroseries, [])
1058+
1059+ def testCopiesAllComponents(self):
1060+ """Test that packages from all components are copied.
1061+
1062+ When copying you specify a component, but that component doesn't
1063+ limit the packages copied. We create a source in main and one in
1064+ universe, and then copy with --component main, and expect to see
1065+ both sources in the copy.
1066+ """
1067+ package_infos = [
1068+ PackageInfo(
1069+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED,
1070+ component="universe"),
1071+ PackageInfo(
1072+ "apt", "2.2", status=PackagePublishingStatus.PUBLISHED,
1073+ component="main")]
1074+ copy_archive, distroseries = self.makeCopyArchive(package_infos,
1075+ component="main")
1076+ self.checkCopiedSources(copy_archive, distroseries, package_infos)
1077+
1078+ def testSubsetsBasedOnPackageset(self):
1079+ """Test that --package-set limits the sources copied."""
1080+ package_infos = [
1081+ PackageInfo(
1082+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
1083+ PackageInfo(
1084+ "apt", "2.2", status=PackagePublishingStatus.PUBLISHED),
1085+ ]
1086+ distroseries = self.createSourceDistribution(package_infos)
1087+ spn = self.factory.getOrMakeSourcePackageName(name="apt")
1088+ packageset = self.factory.makePackageset(
1089+ distroseries=distroseries, packages=(spn,))
1090+ copy_archive = self.getTargetArchive(distroseries.distribution)
1091+ self.copyArchive(copy_archive, distroseries, packagesets=[packageset])
1092+ self.checkCopiedSources(
1093+ copy_archive, distroseries, [package_infos[1]])
1094+
1095+ def testUnionsPackagesets(self):
1096+ """Test that package sets are unioned when copying archives."""
1097+ package_infos = [
1098+ PackageInfo(
1099+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
1100+ PackageInfo(
1101+ "apt", "2.2", status=PackagePublishingStatus.PUBLISHED),
1102+ PackageInfo(
1103+ "gcc", "4.5", status=PackagePublishingStatus.PUBLISHED),
1104+ ]
1105+ distroseries = self.createSourceDistribution(package_infos)
1106+ apt_spn = self.factory.getOrMakeSourcePackageName(name="apt")
1107+ gcc_spn = self.factory.getOrMakeSourcePackageName(name="gcc")
1108+ apt_packageset = self.factory.makePackageset(
1109+ distroseries=distroseries, packages=(apt_spn,))
1110+ gcc_packageset = self.factory.makePackageset(
1111+ distroseries=distroseries, packages=(gcc_spn,))
1112+ copy_archive = self.getTargetArchive(distroseries.distribution)
1113+ self.copyArchive(
1114+ copy_archive, distroseries,
1115+ packagesets=[apt_packageset, gcc_packageset])
1116+ self.checkCopiedSources(
1117+ copy_archive, distroseries, package_infos[1:])
1118+
1119+ def testRecursivelyCopiesPackagesets(self):
1120+ """Test that package set copies include subsets."""
1121+ package_infos = [
1122+ PackageInfo(
1123+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
1124+ PackageInfo(
1125+ "apt", "2.2", status=PackagePublishingStatus.PUBLISHED),
1126+ PackageInfo(
1127+ "gcc", "4.5", status=PackagePublishingStatus.PUBLISHED),
1128+ ]
1129+ distroseries = self.createSourceDistribution(package_infos)
1130+ apt_spn = self.factory.getOrMakeSourcePackageName(name="apt")
1131+ gcc_spn = self.factory.getOrMakeSourcePackageName(name="gcc")
1132+ apt_packageset = self.factory.makePackageset(
1133+ distroseries=distroseries, packages=(apt_spn,))
1134+ gcc_packageset = self.factory.makePackageset(
1135+ distroseries=distroseries, packages=(gcc_spn,))
1136+ apt_packageset.add((gcc_packageset,))
1137+ copy_archive = self.getTargetArchive(distroseries.distribution)
1138+ self.copyArchive(
1139+ copy_archive, distroseries, packagesets=[apt_packageset])
1140+ self.checkCopiedSources(
1141+ copy_archive, distroseries, package_infos[1:])
1142+
1143+ def testCloneFromPPA(self):
1144+ """Test we can create a copy archive with a PPA as the source."""
1145+ distroseries = self.createSourceDistroSeries()
1146+ ppa = self.factory.makeArchive(
1147+ purpose=ArchivePurpose.PPA,
1148+ distribution=distroseries.distribution)
1149+ package_info = PackageInfo(
1150+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED,
1151+ component="universe")
1152+ self.factory.makeSourcePackagePublishingHistory(
1153+ sourcepackagename=self.factory.getOrMakeSourcePackageName(
1154+ name=package_info.name),
1155+ distroseries=distroseries, component=self.factory.makeComponent(
1156+ package_info.component),
1157+ version=package_info.version, archive=ppa,
1158+ status=package_info.status, architecturehintlist='any',
1159+ pocket=PackagePublishingPocket.RELEASE)
1160+ copy_archive = self.getTargetArchive(distroseries.distribution)
1161+ self.copyArchive(copy_archive, distroseries, from_archive=ppa)
1162+ self.checkCopiedSources(
1163+ copy_archive, distroseries, [package_info])
1164+
1165+ def testCreatesNoBuildsWithNoProcFamilies(self):
1166+ """Test that no builds are created if we specify no proc families."""
1167+ package_info = PackageInfo(
1168+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
1169+ copy_archive, distroseries = self.makeCopyArchive([package_info])
1170+ self.checkBuilds(copy_archive, [])
1171+
1172+ def testCreatesBuilds(self):
1173+ """Test that a copy archive creates builds for the copied packages."""
1174+ package_info = PackageInfo(
1175+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
1176+ # This is the processor family for the DAS that the source has,
1177+ # so we expect to get builds.
1178+ proc_families = [ProcessorFamilySet().getByName("x86")]
1179+ copy_archive, distroseries = self.makeCopyArchive(
1180+ [package_info], proc_families=proc_families)
1181+ self.checkBuilds(copy_archive, [package_info])
1182+
1183+ def testNoBuildsIfProcFamilyNotInSource(self):
1184+ """Test that no builds are created for a proc family without a DAS."""
1185+ package_info = PackageInfo(
1186+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
1187+ # This is a processor family without a DAS in the source, so
1188+ # we expect no builds.
1189+ family = self.factory.makeProcessorFamily(name="armel")
1190+ self.factory.makeProcessor(family=family, name="armel")
1191+ proc_families = [family]
1192+ copy_archive, distroseries = self.makeCopyArchive(
1193+ [package_info], proc_families=proc_families)
1194+ self.checkBuilds(copy_archive, [])
1195+
1196+ def testBuildsOnlyForProcFamiliesInSource(self):
1197+ """Test that builds are only created for proc families in source."""
1198+ package_info = PackageInfo(
1199+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
1200+ # One of these processor families has a DAS in the source, so
1201+ # we expect one set of builds
1202+ family = self.factory.makeProcessorFamily(name="armel")
1203+ self.factory.makeProcessor(family=family, name="armel")
1204+ proc_families = [family, ProcessorFamilySet().getByName("x86")]
1205+ copy_archive, distroseries = self.makeCopyArchive(
1206+ [package_info], proc_families=proc_families)
1207+ self.checkBuilds(copy_archive, [package_info])
1208+
1209+ def testCreatesSubsetOfBuilds(self):
1210+ """Test that builds are only created for requested families."""
1211+ package_info = PackageInfo(
1212+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
1213+ distroseries = self.createSourceDistribution([package_info])
1214+ # Create a DAS for a second family
1215+ self.factory.makeDistroArchSeries(
1216+ distroseries=distroseries, architecturetag="amd64",
1217+ processorfamily=ProcessorFamilySet().getByName("amd64"),
1218+ supports_virtualized=True)
1219+ # The request builds for only one of the families, so we
1220+ # expect just one build for each source
1221+ proc_families = [ProcessorFamilySet().getByName("x86")]
1222+ copy_archive = self.getTargetArchive(distroseries.distribution)
1223+ self.copyArchive(
1224+ copy_archive, distroseries, proc_families=proc_families)
1225+ self.checkBuilds(copy_archive, [package_info])
1226+
1227+ def testCreatesMultipleBuilds(self):
1228+ """Test that multiple families result in mutiple builds."""
1229+ package_info = PackageInfo(
1230+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
1231+ distroseries = self.createSourceDistribution([package_info])
1232+ # Create a DAS for a second family
1233+ amd64_family = ProcessorFamilySet().getByName("amd64")
1234+ self.factory.makeDistroArchSeries(
1235+ distroseries=distroseries, architecturetag="amd64",
1236+ processorfamily=amd64_family, supports_virtualized=True)
1237+ # The request builds for both families, so we expect two builds
1238+ # per source.
1239+ proc_families = [ProcessorFamilySet().getByName("x86"), amd64_family]
1240+ copy_archive = self.getTargetArchive(distroseries.distribution)
1241+ self.copyArchive(
1242+ copy_archive, distroseries, proc_families=proc_families)
1243+ self.checkBuilds(copy_archive, [package_info, package_info])
1244+
1245+
1246+ def diffArchives(self, target_archive, target_distroseries,
1247+ source_archive=None, source_distroseries=None):
1248+ """Run a packageSetDiff of two archives."""
1249+ if source_distroseries is None:
1250+ source_distroseries = target_distroseries
1251+ if source_archive is None:
1252+ source_archive = source_distroseries.distribution.main_archive
1253+ source_location = PackageLocation(
1254+ source_archive, source_distroseries.distribution,
1255+ source_distroseries, PackagePublishingPocket.RELEASE)
1256+ target_location = PackageLocation(
1257+ target_archive, target_distroseries.distribution,
1258+ target_distroseries, PackagePublishingPocket.RELEASE)
1259+ cloner = getUtility(IPackageCloner)
1260+ return cloner.packageSetDiff(source_location, target_location)
1261+
1262+ def checkPackageDiff(self, expected_changed, expected_new, actual,
1263+ archive):
1264+ """Check that the diff of two archives is as expected."""
1265+ actual_changed_keys, actual_new_keys = actual
1266+ expected_changed_tuples = [(e.name, e.version)
1267+ for e in expected_changed]
1268+ expected_new_tuples = [(e.name, e.version) for e in expected_new]
1269+ def get_tuples(source_keys):
1270+ tuples = []
1271+ for source_key in source_keys:
1272+ source = getUtility(IPublishingSet).getByIdAndArchive(
1273+ source_key, archive, source=True)
1274+ self.assertNotEqual(source, None, "Got a non-existant "
1275+ "source publishing record: %d" % source_key)
1276+ naked_source = removeSecurityProxy(source)
1277+ tuples.append(
1278+ (naked_source.source_package_name,
1279+ naked_source.source_package_version))
1280+ return tuples
1281+ actual_changed_tuples = get_tuples(actual_changed_keys)
1282+ actual_new_tuples = get_tuples(actual_new_keys)
1283+ self.assertEqual(expected_changed_tuples, actual_changed_tuples)
1284+ self.assertEqual(expected_new_tuples, actual_new_tuples)
1285+
1286+ def testPackageSetDiffWithNothingNew(self):
1287+ """Test packageSetDiff."""
1288+ package_info = PackageInfo(
1289+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
1290+ copy_archive, distroseries = self.makeCopyArchive([package_info])
1291+ diff = self.diffArchives(copy_archive, distroseries)
1292+ self.checkPackageDiff(
1293+ [], [], diff, distroseries.distribution.main_archive)
1294+
1295+ def testPackageSetDiffWithNewPackages(self):
1296+ package_info = PackageInfo(
1297+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
1298+ copy_archive, distroseries = self.makeCopyArchive([package_info])
1299+ package_infos = [
1300+ PackageInfo(
1301+ "apt", "1.2", status=PackagePublishingStatus.PUBLISHED),
1302+ PackageInfo(
1303+ "gcc", "4.5", status=PackagePublishingStatus.PENDING),
1304+ ]
1305+ self.createSourcePublications(package_infos, distroseries)
1306+ diff = self.diffArchives(copy_archive, distroseries)
1307+ self.checkPackageDiff(
1308+ [], package_infos, diff, distroseries.distribution.main_archive)
1309+
1310+ def testPackageSetDiffWithChangedPackages(self):
1311+ package_infos = [
1312+ PackageInfo(
1313+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
1314+ PackageInfo(
1315+ "apt", "1.2", status=PackagePublishingStatus.PUBLISHED),
1316+ ]
1317+ copy_archive, distroseries = self.makeCopyArchive(package_infos)
1318+ package_infos = [
1319+ PackageInfo(
1320+ "bzr", "2.2", status=PackagePublishingStatus.PUBLISHED),
1321+ PackageInfo(
1322+ "apt", "1.3", status=PackagePublishingStatus.PENDING),
1323+ ]
1324+ self.createSourcePublications(package_infos, distroseries)
1325+ diff = self.diffArchives(copy_archive, distroseries)
1326+ self.checkPackageDiff(
1327+ package_infos, [], diff, distroseries.distribution.main_archive)
1328+
1329+ def testPackageSetDiffWithBoth(self):
1330+ package_infos = [
1331+ PackageInfo(
1332+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
1333+ PackageInfo(
1334+ "apt", "1.2", status=PackagePublishingStatus.PUBLISHED),
1335+ ]
1336+ copy_archive, distroseries = self.makeCopyArchive(package_infos)
1337+ package_infos = [
1338+ PackageInfo(
1339+ "bzr", "2.2", status=PackagePublishingStatus.PUBLISHED),
1340+ PackageInfo(
1341+ "gcc", "1.3", status=PackagePublishingStatus.PENDING),
1342+ ]
1343+ self.createSourcePublications(package_infos, distroseries)
1344+ diff = self.diffArchives(copy_archive, distroseries)
1345+ self.checkPackageDiff(
1346+ [package_infos[0]], [package_infos[1]], diff,
1347+ distroseries.distribution.main_archive)
1348+
1349+
1350+ def mergeCopy(self, target_archive, target_distroseries,
1351+ source_archive=None, source_distroseries=None):
1352+ if source_distroseries is None:
1353+ source_distroseries = target_distroseries
1354+ if source_archive is None:
1355+ source_archive = source_distroseries.distribution.main_archive
1356+ source_location = PackageLocation(
1357+ source_archive, source_distroseries.distribution,
1358+ source_distroseries, PackagePublishingPocket.RELEASE)
1359+ target_location = PackageLocation(
1360+ target_archive, target_distroseries.distribution,
1361+ target_distroseries, PackagePublishingPocket.RELEASE)
1362+ cloner = getUtility(IPackageCloner)
1363+ return cloner.mergeCopy(source_location, target_location)
1364+
1365+ def testMergeCopyNoChanges(self):
1366+ package_info = PackageInfo(
1367+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
1368+ copy_archive, distroseries = self.makeCopyArchive([package_info])
1369+ self.mergeCopy(copy_archive, distroseries)
1370+ self.checkCopiedSources(
1371+ copy_archive, distroseries, [package_info])
1372+
1373+ def testMergeCopyWithNewPackages(self):
1374+ package_info = PackageInfo(
1375+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
1376+ copy_archive, distroseries = self.makeCopyArchive([package_info])
1377+ package_infos = [
1378+ PackageInfo(
1379+ "apt", "1.2", status=PackagePublishingStatus.PUBLISHED),
1380+ PackageInfo(
1381+ "gcc", "4.5", status=PackagePublishingStatus.PENDING),
1382+ ]
1383+ self.createSourcePublications(package_infos, distroseries)
1384+ self.mergeCopy(copy_archive, distroseries)
1385+ self.checkCopiedSources(
1386+ copy_archive, distroseries, [package_info] + package_infos)
1387+
1388+ def testMergeCopyWithChangedPackages(self):
1389+ package_infos = [
1390+ PackageInfo(
1391+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
1392+ PackageInfo(
1393+ "apt", "1.2", status=PackagePublishingStatus.PUBLISHED),
1394+ ]
1395+ copy_archive, distroseries = self.makeCopyArchive(package_infos)
1396+ package_infos = [
1397+ PackageInfo(
1398+ "bzr", "2.2", status=PackagePublishingStatus.PUBLISHED),
1399+ PackageInfo(
1400+ "apt", "1.3", status=PackagePublishingStatus.PENDING),
1401+ ]
1402+ self.createSourcePublications(package_infos, distroseries)
1403+ self.mergeCopy(copy_archive, distroseries)
1404+ # Critically there is only one record for each info, as the
1405+ # others have been obsoleted.
1406+ self.checkCopiedSources(
1407+ copy_archive, distroseries, package_infos)
1408+
1409+ def testMergeCopyWithBoth(self):
1410+ package_infos = [
1411+ PackageInfo(
1412+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
1413+ PackageInfo(
1414+ "apt", "1.2", status=PackagePublishingStatus.PUBLISHED),
1415+ ]
1416+ copy_archive, distroseries = self.makeCopyArchive(package_infos)
1417+ package_infos2 = [
1418+ PackageInfo(
1419+ "bzr", "2.2", status=PackagePublishingStatus.PUBLISHED),
1420+ PackageInfo(
1421+ "gcc", "1.3", status=PackagePublishingStatus.PENDING),
1422+ ]
1423+ self.createSourcePublications(package_infos2, distroseries)
1424+ self.mergeCopy(copy_archive, distroseries)
1425+ # Again bzr is obsoleted, gcc is added and apt remains.
1426+ self.checkCopiedSources(
1427+ copy_archive, distroseries, [package_infos[1]] + package_infos2)
1428+
1429+ def setArchiveArchitectures(self, archive, proc_families):
1430+ """Associate the archive with the processor families."""
1431+ aa_set = getUtility(IArchiveArchSet)
1432+ for proc_family in proc_families:
1433+ aa_set.new(archive, proc_family)
1434+
1435+ def testMergeCopyCreatesBuilds(self):
1436+ package_infos = [
1437+ PackageInfo(
1438+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
1439+ PackageInfo(
1440+ "apt", "1.2", status=PackagePublishingStatus.PUBLISHED),
1441+ ]
1442+ proc_families = [ProcessorFamilySet().getByName("x86")]
1443+ copy_archive, distroseries = self.makeCopyArchive(
1444+ package_infos, proc_families=proc_families)
1445+ self.setArchiveArchitectures(copy_archive, proc_families)
1446+ package_infos2 = [
1447+ PackageInfo(
1448+ "bzr", "2.2", status=PackagePublishingStatus.PUBLISHED),
1449+ PackageInfo(
1450+ "gcc", "1.3", status=PackagePublishingStatus.PENDING),
1451+ ]
1452+ self.createSourcePublications(package_infos2, distroseries)
1453+ self.mergeCopy(copy_archive, distroseries)
1454+ # We get all builds, as superseding bzr doesn't cancel the
1455+ # build
1456+ self.checkBuilds(copy_archive, package_infos + package_infos2)
1457+
1458+ def testMergeCopyCreatesNoBuildsWhenNoArchitectures(self):
1459+ package_infos = [
1460+ PackageInfo(
1461+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
1462+ ]
1463+ # We specify no processor families at creation time
1464+ copy_archive, distroseries = self.makeCopyArchive(
1465+ package_infos, proc_families=[])
1466+ package_infos2 = [
1467+ PackageInfo(
1468+ "bzr", "2.2", status=PackagePublishingStatus.PUBLISHED),
1469+ ]
1470+ self.createSourcePublications(package_infos2, distroseries)
1471+ self.mergeCopy(copy_archive, distroseries)
1472+ # And so we get no builds at merge time
1473+ self.checkBuilds(copy_archive, [])
1474+
1475+ def testMergeCopyCreatesBuildsForMultipleArchitectures(self):
1476+ package_infos = [
1477+ PackageInfo(
1478+ "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
1479+ PackageInfo(
1480+ "apt", "1.2", status=PackagePublishingStatus.PUBLISHED),
1481+ ]
1482+ distroseries = self.createSourceDistribution(package_infos)
1483+ # Create a DAS for a second family
1484+ amd64_family = ProcessorFamilySet().getByName("amd64")
1485+ self.factory.makeDistroArchSeries(
1486+ distroseries=distroseries, architecturetag="amd64",
1487+ processorfamily=amd64_family, supports_virtualized=True)
1488+ # The request builds for both families, so we expect two builds
1489+ # per source.
1490+ proc_families = [ProcessorFamilySet().getByName("x86"), amd64_family]
1491+ copy_archive = self.getTargetArchive(distroseries.distribution)
1492+ self.setArchiveArchitectures(copy_archive, proc_families)
1493+ self.copyArchive(
1494+ copy_archive, distroseries, proc_families=proc_families)
1495+ package_infos2 = [
1496+ PackageInfo(
1497+ "bzr", "2.2", status=PackagePublishingStatus.PUBLISHED),
1498+ PackageInfo(
1499+ "gcc", "1.3", status=PackagePublishingStatus.PENDING),
1500+ ]
1501+ self.createSourcePublications(package_infos2, distroseries)
1502+ self.mergeCopy(copy_archive, distroseries)
1503+ # We get all builds twice, one for each architecture.
1504+ self.checkBuilds(
1505+ copy_archive,
1506+ package_infos + package_infos + package_infos2 + package_infos2)