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
=== modified file 'lib/lp/soyuz/model/packagecloner.py'
--- lib/lp/soyuz/model/packagecloner.py 2010-07-02 03:47:49 +0000
+++ lib/lp/soyuz/model/packagecloner.py 2010-07-16 03:28:56 +0000
@@ -11,11 +11,15 @@
11 ]11 ]
1212
1313
14import transaction
15
14from zope.component import getUtility16from zope.component import getUtility
15from zope.interface import implements17from zope.interface import implements
18from zope.security.proxy import removeSecurityProxy
1619
17from canonical.database.constants import UTC_NOW20from canonical.database.constants import UTC_NOW
18from canonical.database.sqlbase import quote, sqlvalues21from canonical.database.sqlbase import quote, sqlvalues
22from lp.soyuz.interfaces.archivearch import IArchiveArchSet
19from lp.soyuz.interfaces.publishing import PackagePublishingStatus23from lp.soyuz.interfaces.publishing import PackagePublishingStatus
20from lp.soyuz.interfaces.packagecloner import IPackageCloner24from lp.soyuz.interfaces.packagecloner import IPackageCloner
21from canonical.launchpad.webapp.interfaces import (25from canonical.launchpad.webapp.interfaces import (
@@ -50,7 +54,8 @@
5054
51 implements(IPackageCloner)55 implements(IPackageCloner)
5256
53 def clonePackages(self, origin, destination, distroarchseries_list=None):57 def clonePackages(self, origin, destination, distroarchseries_list=None,
58 proc_families=None):
54 """Copies packages from origin to destination package location.59 """Copies packages from origin to destination package location.
5560
56 Binary packages are only copied for the `DistroArchSeries` pairs61 Binary packages are only copied for the `DistroArchSeries` pairs
@@ -64,6 +69,7 @@
64 distroarchseries instances.69 distroarchseries instances.
65 @param distroarchseries_list: the binary packages will be copied70 @param distroarchseries_list: the binary packages will be copied
66 for the distroarchseries pairs specified (if any).71 for the distroarchseries pairs specified (if any).
72 @param the processor families to create builds for.
67 """73 """
68 # First clone the source packages.74 # First clone the source packages.
69 self._clone_source_packages(origin, destination)75 self._clone_source_packages(origin, destination)
@@ -75,6 +81,49 @@
75 self._clone_binary_packages(81 self._clone_binary_packages(
76 origin, destination, origin_das, destination_das)82 origin, destination, origin_das, destination_das)
7783
84 if proc_families is None:
85 proc_families = []
86
87 self._create_missing_builds(
88 destination.distroseries, destination.archive, proc_families)
89
90 def _create_missing_builds(self, distroseries, archive, proc_families):
91 """Create builds for all cloned source packages.
92
93 :param distroseries: the distro series for which to create builds.
94 :param archive: the archive for which to create builds.
95 :param proc_families: the list of processor families for
96 which to create builds.
97 """
98 # Avoid circular imports.
99 from lp.soyuz.interfaces.publishing import active_publishing_status
100
101 # Listify the architectures to avoid hitting this MultipleJoin
102 # multiple times.
103 architectures = list(distroseries.architectures)
104
105 # Filter the list of DistroArchSeries so that only the ones
106 # specified in proc_families remain
107 architectures = [architecture for architecture in architectures
108 if architecture.processorfamily in proc_families]
109
110 if len(architectures) == 0:
111 return
112
113 # Both, PENDING and PUBLISHED sources will be considered for
114 # as PUBLISHED. It's part of the assumptions made in:
115 # https://launchpad.net/soyuz/+spec/build-unpublished-source
116 sources_published = archive.getPublishedSources(
117 distroseries=distroseries, status=active_publishing_status)
118
119 def get_spn(pub):
120 """Return the source package name for a publishing record."""
121 return pub.sourcepackagerelease.sourcepackagename.name
122
123 for pubrec in sources_published:
124 pubrec.createMissingBuilds(architectures_available=architectures)
125 # Commit to avoid MemoryError: bug 304459
126 transaction.commit()
78127
79 def _clone_binary_packages(self, origin, destination, origin_das,128 def _clone_binary_packages(self, origin, destination, origin_das,
80 destination_das):129 destination_das):
@@ -153,6 +202,17 @@
153 """ % sqlvalues(202 """ % sqlvalues(
154 PackagePublishingStatus.SUPERSEDED, UTC_NOW))203 PackagePublishingStatus.SUPERSEDED, UTC_NOW))
155204
205 def get_family(archivearch):
206 """Extract the processor family from an `IArchiveArch`."""
207 return removeSecurityProxy(archivearch).processorfamily
208
209 proc_families = [
210 get_family(archivearch) for archivearch
211 in getUtility(IArchiveArchSet).getByArchive(destination.archive)]
212
213 self._create_missing_builds(
214 destination.distroseries, destination.archive, proc_families)
215
156 def _compute_packageset_delta(self, origin):216 def _compute_packageset_delta(self, origin):
157 """Given a source/target archive find obsolete or missing packages.217 """Given a source/target archive find obsolete or missing packages.
158218
@@ -219,7 +279,7 @@
219 PackagePublishingStatus.PENDING,279 PackagePublishingStatus.PENDING,
220 PackagePublishingStatus.PUBLISHED,280 PackagePublishingStatus.PUBLISHED,
221 origin.distroseries, origin.pocket)281 origin.distroseries, origin.pocket)
222 282
223 if origin.component is not None:283 if origin.component is not None:
224 find_origin_only_packages += (284 find_origin_only_packages += (
225 " AND secsrc.component = %s" % quote(origin.component))285 " AND secsrc.component = %s" % quote(origin.component))
@@ -288,7 +348,7 @@
288 PackagePublishingStatus.PENDING,348 PackagePublishingStatus.PENDING,
289 PackagePublishingStatus.PUBLISHED,349 PackagePublishingStatus.PUBLISHED,
290 destination.distroseries, destination.pocket)350 destination.distroseries, destination.pocket)
291 351
292 if destination.component is not None:352 if destination.component is not None:
293 pop_query += (353 pop_query += (
294 " AND secsrc.component = %s" % quote(destination.component))354 " AND secsrc.component = %s" % quote(destination.component))
@@ -389,5 +449,3 @@
389 logger.info('New packages: %d' % len(new_info))449 logger.info('New packages: %d' % len(new_info))
390 for info in new_info:450 for info in new_info:
391 logger.info('* %s (%s)' % info)451 logger.info('* %s (%s)' % info)
392
393
394452
=== modified file 'lib/lp/soyuz/scripts/populate_archive.py'
--- lib/lp/soyuz/scripts/populate_archive.py 2010-07-02 03:47:49 +0000
+++ lib/lp/soyuz/scripts/populate_archive.py 2010-07-16 03:28:56 +0000
@@ -248,10 +248,6 @@
248 """Extract the processor family from an `IArchiveArch`."""248 """Extract the processor family from an `IArchiveArch`."""
249 return removeSecurityProxy(archivearch).processorfamily249 return removeSecurityProxy(archivearch).processorfamily
250250
251 proc_families = [
252 get_family(archivearch) for archivearch
253 in getUtility(IArchiveArchSet).getByArchive(copy_archive)]
254
255 # Now instantiate the package copy request that will capture the251 # Now instantiate the package copy request that will capture the
256 # archive population parameters in the database.252 # archive population parameters in the database.
257 pcr = getUtility(IPackageCopyRequestSet).new(253 pcr = getUtility(IPackageCopyRequestSet).new(
@@ -269,12 +265,8 @@
269 if merge_copy_flag:265 if merge_copy_flag:
270 pkg_cloner.mergeCopy(the_origin, the_destination)266 pkg_cloner.mergeCopy(the_origin, the_destination)
271 else:267 else:
272 pkg_cloner.clonePackages(the_origin, the_destination)268 pkg_cloner.clonePackages(
273269 the_origin, the_destination, proc_families=proc_families)
274 # Create builds for the cloned packages.
275 self._createMissingBuilds(
276 the_destination.distroseries, the_destination.archive,
277 proc_families)
278270
279 # Mark the package copy request as completed.271 # Mark the package copy request as completed.
280 pcr.markAsCompleted()272 pcr.markAsCompleted()
@@ -399,71 +391,3 @@
399 "--nonvirtualized", dest="nonvirtualized", default=False,391 "--nonvirtualized", dest="nonvirtualized", default=False,
400 action="store_true",392 action="store_true",
401 help='Create the archive as nonvirtual if specified.')393 help='Create the archive as nonvirtual if specified.')
402
403 def _createMissingBuilds(
404 self, distroseries, archive, proc_families):
405 """Create builds for all cloned source packages.
406
407 :param distroseries: the distro series for which to create builds.
408 :param archive: the archive for which to create builds.
409 :param proc_families: the list of processor families for
410 which to create builds.
411 """
412 # Avoid circular imports.
413 from lp.soyuz.interfaces.publishing import active_publishing_status
414
415 self.logger.info("Processing %s." % distroseries.name)
416
417 # Listify the architectures to avoid hitting this MultipleJoin
418 # multiple times.
419 architectures = list(distroseries.architectures)
420
421 # Filter the list of DistroArchSeries so that only the ones
422 # specified on the command line remain.
423 architectures = [architecture for architecture in architectures
424 if architecture.processorfamily in proc_families]
425
426 if len(architectures) == 0:
427 self.logger.info(
428 "No DistroArchSeries left for %s, done." % distroseries.name)
429 return
430
431 self.logger.info(
432 "Supported architectures: %s." %
433 " ".join(arch_series.architecturetag
434 for arch_series in architectures))
435
436 # Both, PENDING and PUBLISHED sources will be considered for
437 # as PUBLISHED. It's part of the assumptions made in:
438 # https://launchpad.net/soyuz/+spec/build-unpublished-source
439 sources_published = archive.getPublishedSources(
440 distroseries=distroseries, status=active_publishing_status)
441
442 self.logger.info(
443 "Found %d source(s) published." % sources_published.count())
444
445 def get_spn(pub):
446 """Return the source package name for a publishing record."""
447 return pub.sourcepackagerelease.sourcepackagename.name
448
449 archindep_unavailable = distroseries.nominatedarchindep not in (
450 architectures)
451
452 for pubrec in sources_published:
453 if (pubrec.sourcepackagerelease.architecturehintlist == "all"
454 and archindep_unavailable):
455 self.logger.info(
456 "Skipping %s, arch-all package can't be built since %s "
457 "is not requested" % (
458 get_spn(pubrec),
459 distroseries.nominatedarchindep.architecturetag))
460 continue
461
462 builds = pubrec.createMissingBuilds(
463 architectures_available=architectures, logger=self.logger)
464 if len(builds) == 0:
465 self.logger.info("%s has no builds." % get_spn(pubrec))
466 else:
467 self.logger.info(
468 "%s has %s build(s)." % (get_spn(pubrec), len(builds)))
469 self.txn.commit()
470394
=== modified file 'lib/lp/soyuz/scripts/tests/test_populatearchive.py'
--- lib/lp/soyuz/scripts/tests/test_populatearchive.py 2010-07-02 03:47:49 +0000
+++ lib/lp/soyuz/scripts/tests/test_populatearchive.py 2010-07-16 03:28:56 +0000
@@ -21,14 +21,10 @@
21from lp.buildmaster.interfaces.buildbase import BuildStatus21from lp.buildmaster.interfaces.buildbase import BuildStatus
22from lp.registry.interfaces.distribution import IDistributionSet22from lp.registry.interfaces.distribution import IDistributionSet
23from lp.registry.interfaces.person import IPersonSet23from lp.registry.interfaces.person import IPersonSet
24from lp.registry.interfaces.pocket import PackagePublishingPocket
25from lp.services.job.interfaces.job import JobStatus24from lp.services.job.interfaces.job import JobStatus
26from lp.soyuz.interfaces.archive import ArchivePurpose, IArchiveSet25from lp.soyuz.interfaces.archive import ArchivePurpose, IArchiveSet
27from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet26from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
28from lp.soyuz.interfaces.packagecopyrequest import (
29 IPackageCopyRequestSet, PackageCopyStatus)
30from lp.soyuz.interfaces.publishing import PackagePublishingStatus27from lp.soyuz.interfaces.publishing import PackagePublishingStatus
31from lp.soyuz.model.processor import ProcessorFamilySet
32from lp.soyuz.scripts.ftpmaster import PackageLocationError, SoyuzScriptError28from lp.soyuz.scripts.ftpmaster import PackageLocationError, SoyuzScriptError
33from lp.soyuz.scripts.populate_archive import ArchivePopulator29from lp.soyuz.scripts.populate_archive import ArchivePopulator
34from lp.soyuz.tests.test_publishing import SoyuzTestPublisher30from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
@@ -41,18 +37,6 @@
41 return pub.sourcepackagerelease.sourcepackagename37 return pub.sourcepackagerelease.sourcepackagename
4238
4339
44class PackageInfo:
45
46 def __init__(self, name, version,
47 status=PackagePublishingStatus.PUBLISHED, component="main",
48 arch_hint=None):
49 self.name = name
50 self.version = version
51 self.status = status
52 self.component = component
53 self.arch_hint = arch_hint
54
55
56class TestPopulateArchiveScript(TestCaseWithFactory):40class TestPopulateArchiveScript(TestCaseWithFactory):
57 """Test the copy-package.py script."""41 """Test the copy-package.py script."""
5842
@@ -86,6 +70,15 @@
86 stdout, stderr = process.communicate()70 stdout, stderr = process.communicate()
87 return (process.returncode, stdout, stderr)71 return (process.returncode, stdout, stderr)
8872
73 def getScript(self, test_args=None):
74 """Return an ArchivePopulator instance."""
75 if test_args is None:
76 test_args = []
77 script = ArchivePopulator("test copy archives", test_args=test_args)
78 script.logger = QuietFakeLogger()
79 script.txn = self.layer.txn
80 return script
81
89 def testCopyArchiveCreation(self):82 def testCopyArchiveCreation(self):
90 """Start archive population, check data before and after.83 """Start archive population, check data before and after.
9184
@@ -151,530 +144,11 @@
151144
152 self.assertEqual(build_spns, self.expected_build_spns)145 self.assertEqual(build_spns, self.expected_build_spns)
153146
154 def createSourceDistroSeries(self):
155 """Create a DistroSeries suitable for copying.
156
157 Creates a distroseries with a DistroArchSeries and nominatedarchindep,
158 which makes it suitable for copying because it will create some builds.
159 """
160 distro_name = "foobuntu"
161 distro = self.factory.makeDistribution(name=distro_name)
162 distroseries_name = "maudlin"
163 distroseries = self.factory.makeDistroSeries(
164 distribution=distro, name=distroseries_name)
165 das = self.factory.makeDistroArchSeries(
166 distroseries=distroseries, architecturetag="i386",
167 processorfamily=ProcessorFamilySet().getByName("x86"),
168 supports_virtualized=True)
169 distroseries.nominatedarchindep = das
170 return distroseries
171
172 def createTargetOwner(self):
173 """Create a person suitable to own a copy archive."""
174 person_name = "copy-archive-owner"
175 owner = self.factory.makePerson(name=person_name)
176 return owner
177
178 def getTargetArchiveName(self, distribution):
179 """Get a suitable name for a copy archive.
180
181 It also checks that the archive doesn't currently exist.
182 """
183 archive_name = "msa%s" % int(time.time())
184 copy_archive = getUtility(IArchiveSet).getByDistroPurpose(
185 distribution, ArchivePurpose.COPY, archive_name)
186 # This is a sanity check: a copy archive with this name should not
187 # exist yet.
188 self.assertIs(None, copy_archive)
189 return archive_name
190
191 def createSourcePublication(self, info, distroseries):
192 """Create a SourcePackagePublishingHistory based on a PackageInfo."""
193 if info.arch_hint is None:
194 arch_hint = "any"
195 else:
196 arch_hint = info.arch_hint
197
198 self.factory.makeSourcePackagePublishingHistory(
199 sourcepackagename=self.factory.getOrMakeSourcePackageName(
200 name=info.name),
201 distroseries=distroseries, component=self.factory.makeComponent(
202 info.component),
203 version=info.version, architecturehintlist=arch_hint,
204 archive=distroseries.distribution.main_archive,
205 status=info.status, pocket=PackagePublishingPocket.RELEASE)
206
207 def createSourcePublications(self, package_infos, distroseries):
208 """Create a source publication for each item in package_infos."""
209 for package_info in package_infos:
210 self.createSourcePublication(package_info, distroseries)
211
212 def getScript(self, test_args=None):
213 """Return an ArchivePopulator instance."""
214 if test_args is None:
215 test_args = []
216 script = ArchivePopulator("test copy archives", test_args=test_args)
217 script.logger = QuietFakeLogger()
218 script.txn = self.layer.txn
219 return script
220
221 def copyArchive(self, distroseries, archive_name, owner,
222 architectures=None, component="main", from_user=None,
223 from_archive=None, packageset_names=None, nonvirtualized=False):
224 """Run the copy-archive script."""
225 extra_args = [
226 '--from-distribution', distroseries.distribution.name,
227 '--from-suite', distroseries.name,
228 '--to-distribution', distroseries.distribution.name,
229 '--to-suite', distroseries.name,
230 '--to-archive', archive_name,
231 '--to-user', owner.name,
232 '--reason',
233 '"copy archive from %s"' % datetime.ctime(datetime.utcnow()),
234 '--component', component,
235 ]
236
237 if from_user is not None:
238 extra_args.extend(["--from-user", from_user])
239
240 if from_archive is not None:
241 extra_args.extend(["--from-archive", from_archive])
242
243 if architectures is None:
244 architectures = ["386"]
245
246 if nonvirtualized:
247 extra_args.extend(["--nonvirtualized"])
248
249 for architecture in architectures:
250 extra_args.extend(['-a', architecture])
251
252 if packageset_names is None:
253 packageset_names = []
254
255 for packageset_name in packageset_names:
256 extra_args.extend(['--package-set', packageset_name])
257
258 script = self.getScript(test_args=extra_args)
259 script.mainTask()
260
261 # Make sure the copy archive with the desired name was
262 # created
263 copy_archive = getUtility(IArchiveSet).getByDistroPurpose(
264 distroseries.distribution, ArchivePurpose.COPY, archive_name)
265 self.assertTrue(copy_archive is not None)
266
267 # Ascertain that the new copy archive was created with the 'enabled'
268 # flag turned off.
269 self.assertFalse(copy_archive.enabled)
270
271 # Assert the virtualization is correct.
272 virtual = not nonvirtualized
273 self.assertEqual(copy_archive.require_virtualized, virtual)
274
275 return copy_archive
276
277 def checkCopiedSources(self, archive, distroseries, expected):
278 """Check the sources published in an archive against an expected set.
279
280 Given an archive and a target distroseries the sources published in
281 that distroseries are checked against a set of PackageInfo to
282 ensure that the correct package names and versions are published.
283 """
284 expected_set = set([(info.name, info.version) for info in expected])
285 sources = archive.getPublishedSources(
286 distroseries=distroseries, status=self.pending_statuses)
287 actual_set = set()
288 for source in sources:
289 source = removeSecurityProxy(source)
290 actual_set.add(
291 (source.source_package_name, source.source_package_version))
292 self.assertEqual(expected_set, actual_set)
293
294 def createSourceDistribution(self, package_infos):
295 """Create a distribution to be the source of a copy archive."""
296 distroseries = self.createSourceDistroSeries()
297 self.createSourcePublications(package_infos, distroseries)
298 return distroseries
299
300 def makeCopyArchive(self, package_infos, component="main",
301 nonvirtualized=False):
302 """Make a copy archive based on a new distribution."""
303 owner = self.createTargetOwner()
304 distroseries = self.createSourceDistribution(package_infos)
305 archive_name = self.getTargetArchiveName(distroseries.distribution)
306 copy_archive = self.copyArchive(
307 distroseries, archive_name, owner, component=component,
308 nonvirtualized=nonvirtualized)
309 return (copy_archive, distroseries)
310
311 def checkBuilds(self, archive, package_infos):
312 """Check the build records pending in an archive.
313
314 Given a set of PackageInfo objects check that each has a build
315 created for it.
316 """
317 expected_builds = list(
318 [(info.name, info.version) for info in package_infos])
319 builds = list(
320 getUtility(IBinaryPackageBuildSet).getBuildsForArchive(
321 archive, status=BuildStatus.NEEDSBUILD))
322 actual_builds = list()
323 for build in builds:
324 naked_build = removeSecurityProxy(build)
325 spr = naked_build.source_package_release
326 actual_builds.append((spr.name, spr.version))
327 self.assertEqual(sorted(expected_builds), sorted(actual_builds))
328
329 def testCopyArchiveRunScript(self):
330 """Check that we can exec the script to copy an archive."""
331 package_info = PackageInfo(
332 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
333 owner = self.createTargetOwner()
334 distroseries = self.createSourceDistribution([package_info])
335 archive_name = self.getTargetArchiveName(distroseries.distribution)
336 # We must commit as we are going to exec a script that will run
337 # in a different transaction and must be able to see the
338 # objects we just created.
339 self.layer.commit()
340
341 extra_args = [
342 '--from-distribution', distroseries.distribution.name,
343 '--from-suite', distroseries.name,
344 '--to-distribution', distroseries.distribution.name,
345 '--to-suite', distroseries.name,
346 '--to-archive', archive_name,
347 '--to-user', owner.name,
348 '--reason',
349 '"copy archive from %s"' % datetime.ctime(datetime.utcnow()),
350 '--component', "main",
351 '-a', '386',
352 ]
353 (exitcode, out, err) = self.runWrapperScript(extra_args)
354 # Check for zero exit code.
355 self.assertEqual(
356 exitcode, 0, "\n=> %s\n=> %s\n=> %s\n" % (exitcode, out, err))
357 # Make sure the copy archive with the desired name was
358 # created
359 copy_archive = getUtility(IArchiveSet).getByDistroPurpose(
360 distroseries.distribution, ArchivePurpose.COPY, archive_name)
361 self.assertTrue(copy_archive is not None)
362
363 # Ascertain that the new copy archive was created with the 'enabled'
364 # flag turned off.
365 self.assertFalse(copy_archive.enabled)
366
367 # Also, make sure that the builds for the new copy archive will be
368 # carried out on non-virtual builders.
369 self.assertTrue(copy_archive.require_virtualized)
370 self.checkCopiedSources(
371 copy_archive, distroseries, [package_info])
372
373 def testCopyArchiveCreateCopiesPublished(self):
374 """Test that PUBLISHED sources are copied."""
375 package_info = PackageInfo(
376 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
377 copy_archive, distroseries = self.makeCopyArchive([package_info])
378 self.checkCopiedSources(
379 copy_archive, distroseries, [package_info])
380
381 def testCopyArchiveCreateCopiesPending(self):
382 """Test that PENDING sources are copied."""
383 package_info = PackageInfo(
384 "bzr", "2.1", status=PackagePublishingStatus.PENDING)
385 copy_archive, distroseries = self.makeCopyArchive([package_info])
386 self.checkCopiedSources(
387 copy_archive, distroseries, [package_info])
388
389 def testCopyArchiveCreateDoesntCopySuperseded(self):
390 """Test that SUPERSEDED sources are not copied."""
391 package_info = PackageInfo(
392 "bzr", "2.1", status=PackagePublishingStatus.SUPERSEDED)
393 copy_archive, distroseries = self.makeCopyArchive([package_info])
394 self.checkCopiedSources(
395 copy_archive, distroseries, [])
396
397 def testCopyArchiveCreateDoesntCopyDeleted(self):
398 """Test that DELETED sources are not copied."""
399 package_info = PackageInfo(
400 "bzr", "2.1", status=PackagePublishingStatus.DELETED)
401 copy_archive, distroseries = self.makeCopyArchive([package_info])
402 self.checkCopiedSources(
403 copy_archive, distroseries, [])
404
405 def testCopyArchiveCreateDoesntCopyObsolete(self):
406 """Test that OBSOLETE sources are not copied."""
407 package_info = PackageInfo(
408 "bzr", "2.1", status=PackagePublishingStatus.OBSOLETE)
409 copy_archive, distroseries = self.makeCopyArchive([package_info])
410 self.checkCopiedSources(
411 copy_archive, distroseries, [])
412
413 def testCopyArchiveCreatesBuilds(self):
414 """Test that a copy archive creates builds for the copied packages."""
415 package_info = PackageInfo(
416 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
417 copy_archive, distroseries = self.makeCopyArchive([package_info])
418 self.checkBuilds(copy_archive, [package_info])
419
420 def testCopyArchiveArchTagNotAvailableInSource(self):
421 """Test creating a copy archive for an arch not in the source.
422
423 If we request a copy to an architecture that doesn't have
424 a DistroArchSeries in the source then we won't get any builds
425 created in the copy archive.
426 """
427 family = self.factory.makeProcessorFamily(name="armel")
428 self.factory.makeProcessor(family=family, name="armel")
429 package_info = PackageInfo(
430 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
431 owner = self.createTargetOwner()
432 # Creates an archive with just x86
433 distroseries = self.createSourceDistribution([package_info])
434 archive_name = self.getTargetArchiveName(distroseries.distribution)
435 # Different architecture, so there won't be any builds
436 copy_archive = self.copyArchive(
437 distroseries, archive_name, owner, architectures=["armel"])
438 self.checkBuilds(copy_archive, [])
439
440 # Also, make sure the package copy request status was updated.
441 [pcr] = getUtility(
442 IPackageCopyRequestSet).getByTargetArchive(copy_archive)
443 self.assertTrue(pcr.status == PackageCopyStatus.COMPLETE)
444
445 # This date is set when the copy request makes the transition to
446 # the "in progress" state.
447 self.assertTrue(pcr.date_started is not None)
448 # This date is set when the copy request makes the transition to
449 # the "completed" state.
450 self.assertTrue(pcr.date_completed is not None)
451 self.assertTrue(pcr.date_started <= pcr.date_completed)
452
453 def testMultipleArchTagsWithSubsetInSource(self):
454 """Try copy archive population with multiple architecture tags.
455
456 The user may specify a number of given architecture tags on the
457 command line.
458 The script should create build records only for the specified
459 architecture tags that are supported by the destination distro series.
460
461 In this (test) case the script should create the build records for the
462 '386' architecture.
463 """
464 family = self.factory.makeProcessorFamily(name="armel")
465 self.factory.makeProcessor(family=family, name="armel")
466 package_info = PackageInfo(
467 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
468 owner = self.createTargetOwner()
469 # Creates an archive with just x86
470 distroseries = self.createSourceDistribution([package_info])
471 archive_name = self.getTargetArchiveName(distroseries.distribution)
472 # There is only a DAS for i386, so armel won't produce any
473 # builds
474 copy_archive = self.copyArchive(
475 distroseries, archive_name, owner,
476 architectures=["386", "armel"])
477 self.checkBuilds(copy_archive, [package_info])
478
479 def testCopyArchiveCreatesSubsetOfBuilds(self):
480 """Create a copy archive with a subset of the architectures.
481
482 We copy from an archive with multiple architecture DistroArchSeries,
483 but request only one of those architectures in the target,
484 so we only get builds for that one architecture.
485 """
486 package_info = PackageInfo(
487 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
488 owner = self.createTargetOwner()
489 distroseries = self.createSourceDistribution([package_info])
490 self.factory.makeDistroArchSeries(
491 distroseries=distroseries, architecturetag="amd64",
492 processorfamily=ProcessorFamilySet().getByName("amd64"),
493 supports_virtualized=True)
494 archive_name = self.getTargetArchiveName(distroseries.distribution)
495 copy_archive = self.copyArchive(
496 distroseries, archive_name, owner,
497 architectures=["386"])
498 # We only get a single build, as we only requested 386, not
499 # amd64 too
500 self.checkBuilds(copy_archive, [package_info])
501
502 def testNoBuildsForArchAll(self):
503 # If we have a copy for an architecture that is not the
504 # nominatedarchindep architecture, then we don't want to create
505 # builds for arch-all packages, as they can't be built at all
506 # and createMissingBuilds blows up when it checks that.
507 package_info = PackageInfo(
508 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED,
509 arch_hint="all")
510 owner = self.createTargetOwner()
511 distroseries = self.createSourceDistribution([package_info])
512 self.factory.makeDistroArchSeries(
513 distroseries=distroseries, architecturetag="amd64",
514 processorfamily=ProcessorFamilySet().getByName("amd64"),
515 supports_virtualized=True)
516 archive_name = self.getTargetArchiveName(distroseries.distribution)
517 copy_archive = self.copyArchive(
518 distroseries, archive_name, owner,
519 architectures=["amd64"])
520 # We don't get any builds since amd64 is not the
521 # nomindatedarchindep, i386 is.
522 self.assertEqual(
523 distroseries.nominatedarchindep.architecturetag, "i386")
524 self.checkBuilds(copy_archive, [])
525
526 def testMultipleArchTags(self):
527 """Test copying an archive with multiple architectures.
528
529 We create a source with two architectures, and then request
530 a copy of both, so we get a build for each of those architectures.
531 """
532 package_info = PackageInfo(
533 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
534 owner = self.createTargetOwner()
535 distroseries = self.createSourceDistribution([package_info])
536 self.factory.makeDistroArchSeries(
537 distroseries=distroseries, architecturetag="amd64",
538 processorfamily=ProcessorFamilySet().getByName("amd64"),
539 supports_virtualized=True)
540 archive_name = self.getTargetArchiveName(distroseries.distribution)
541 copy_archive = self.copyArchive(
542 distroseries, archive_name, owner,
543 architectures=["386", "amd64"])
544 self.checkBuilds(copy_archive, [package_info, package_info])
545
546 def testCopyArchiveCopiesRightComponents(self):
547 """Test that packages from the right components are copied.
548
549 When copying you specify a component, that component should
550 limit the packages copied. We create a source in main and one in
551 universe, and then copy with --component main, and expect to see
552 only main in the copy.
553 """
554 package_info_universe = PackageInfo(
555 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED,
556 component="universe")
557 package_info_main = PackageInfo(
558 "apt", "2.2", status=PackagePublishingStatus.PUBLISHED,
559 component="main")
560 package_infos_both = [package_info_universe, package_info_main]
561 copy_archive, distroseries = self.makeCopyArchive(
562 package_infos_both, component="main")
563 self.checkBuilds(copy_archive, [package_info_main])
564
565 def testCopyArchiveSubsetsBasedOnPackageset(self):
566 """Test that --package-set limits the sources copied."""
567 package_infos = [
568 PackageInfo(
569 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
570 PackageInfo(
571 "apt", "2.2", status=PackagePublishingStatus.PUBLISHED),
572 ]
573 owner = self.createTargetOwner()
574 distroseries = self.createSourceDistribution(package_infos)
575 packageset_name = u"apt-packageset"
576 spn = self.factory.getOrMakeSourcePackageName(name="apt")
577 self.factory.makePackageset(
578 name=packageset_name, distroseries=distroseries, packages=(spn,))
579 archive_name = self.getTargetArchiveName(distroseries.distribution)
580 copy_archive = self.copyArchive(
581 distroseries, archive_name, owner,
582 packageset_names=[packageset_name])
583 self.checkCopiedSources(
584 copy_archive, distroseries, [package_infos[1]])
585
586 def testCopyArchiveUnionsPackagesets(self):
587 """Test that package sets are unioned when copying archives."""
588 package_infos = [
589 PackageInfo(
590 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
591 PackageInfo(
592 "apt", "2.2", status=PackagePublishingStatus.PUBLISHED),
593 PackageInfo(
594 "gcc", "4.5", status=PackagePublishingStatus.PUBLISHED),
595 ]
596 owner = self.createTargetOwner()
597 distroseries = self.createSourceDistribution(package_infos)
598 apt_packageset_name = u"apt-packageset"
599 apt_spn = self.factory.getOrMakeSourcePackageName(name="apt")
600 gcc_packageset_name = u"gcc-packageset"
601 gcc_spn = self.factory.getOrMakeSourcePackageName(name="gcc")
602 self.factory.makePackageset(
603 name=apt_packageset_name, distroseries=distroseries,
604 packages=(apt_spn,))
605 self.factory.makePackageset(
606 name=gcc_packageset_name, distroseries=distroseries,
607 packages=(gcc_spn,))
608 archive_name = self.getTargetArchiveName(distroseries.distribution)
609 copy_archive = self.copyArchive(
610 distroseries, archive_name, owner,
611 packageset_names=[apt_packageset_name, gcc_packageset_name])
612 self.checkCopiedSources(
613 copy_archive, distroseries, package_infos[1:])
614
615 def testCopyArchiveRecursivelyCopiesPackagesets(self):
616 """Test that package set copies include subsets."""
617 package_infos = [
618 PackageInfo(
619 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
620 PackageInfo(
621 "apt", "2.2", status=PackagePublishingStatus.PUBLISHED),
622 PackageInfo(
623 "gcc", "4.5", status=PackagePublishingStatus.PUBLISHED),
624 ]
625 owner = self.createTargetOwner()
626 distroseries = self.createSourceDistribution(package_infos)
627 apt_packageset_name = u"apt-packageset"
628 apt_spn = self.factory.getOrMakeSourcePackageName(name="apt")
629 gcc_packageset_name = u"gcc-packageset"
630 gcc_spn = self.factory.getOrMakeSourcePackageName(name="gcc")
631 apt_packageset = self.factory.makePackageset(
632 name=apt_packageset_name, distroseries=distroseries,
633 packages=(apt_spn,))
634 gcc_packageset = self.factory.makePackageset(
635 name=gcc_packageset_name, distroseries=distroseries,
636 packages=(gcc_spn,))
637 apt_packageset.add((gcc_packageset,))
638 archive_name = self.getTargetArchiveName(distroseries.distribution)
639 copy_archive = self.copyArchive(
640 distroseries, archive_name, owner,
641 packageset_names=[apt_packageset_name])
642 self.checkCopiedSources(
643 copy_archive, distroseries, package_infos[1:])
644
645 def testCopyFromPPA(self):
646 """Test we can create a copy archive with a PPA as the source."""
647 ppa_owner_name = "ppa-owner"
648 ppa_name = "ppa"
649 ppa_owner = self.factory.makePerson(name=ppa_owner_name)
650 distroseries = self.createSourceDistroSeries()
651 ppa = self.factory.makeArchive(
652 name=ppa_name, purpose=ArchivePurpose.PPA,
653 distribution=distroseries.distribution, owner=ppa_owner)
654 package_info = PackageInfo(
655 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED,
656 component="universe")
657 self.factory.makeSourcePackagePublishingHistory(
658 sourcepackagename=self.factory.getOrMakeSourcePackageName(
659 name=package_info.name),
660 distroseries=distroseries, component=self.factory.makeComponent(
661 package_info.component),
662 version=package_info.version, archive=ppa,
663 status=package_info.status, architecturehintlist='any',
664 pocket=PackagePublishingPocket.RELEASE)
665 owner = self.createTargetOwner()
666 archive_name = self.getTargetArchiveName(distroseries.distribution)
667 copy_archive = self.copyArchive(
668 distroseries, archive_name, owner, from_user=ppa_owner_name,
669 from_archive=ppa_name, component=package_info.component)
670 self.checkCopiedSources(
671 copy_archive, distroseries, [package_info])
672
673 def runScript(147 def runScript(
674 self, archive_name=None, suite='hoary', user='salgado',148 self, archive_name=None, suite='hoary', user='salgado',
675 exists_before=None, exists_after=None, exception_type=None,149 exists_before=None, exists_after=None, exception_type=None,
676 exception_text=None, extra_args=None, copy_archive_name=None,150 exception_text=None, extra_args=None, copy_archive_name=None,
677 reason=None, output_substr=None):151 reason=None, output_substr=None, nonvirtualized=False):
678 """Run the script to test.152 """Run the script to test.
679153
680 :type archive_name: `str`154 :type archive_name: `str`
@@ -748,6 +222,9 @@
748 reason = "copy archive, %s" % datetime.ctime(datetime.utcnow())222 reason = "copy archive, %s" % datetime.ctime(datetime.utcnow())
749 script_args.extend(['--reason', reason])223 script_args.extend(['--reason', reason])
750224
225 if nonvirtualized:
226 script_args.append('--nonvirtualized')
227
751 if extra_args is not None:228 if extra_args is not None:
752 script_args.extend(extra_args)229 script_args.extend(extra_args)
753230
@@ -847,6 +324,14 @@
847 exception_text="Could not find packageset No such package set"324 exception_text="Could not find packageset No such package set"
848 " (in the specified distro series): '%s'." % unknown_packageset)325 " (in the specified distro series): '%s'." % unknown_packageset)
849326
327 def testNonvirtualized(self):
328 """--nonvirtualized means the archive won't require virtualization."""
329 copy_archive = self.runScript(
330 archive_name="copy-archive-test", exists_before=False,
331 exists_after=True, nonvirtualized=True,
332 extra_args=['-a', '386'])
333 self.assertFalse(copy_archive.require_virtualized)
334
850 def testPackagesetDelta(self):335 def testPackagesetDelta(self):
851 """Try to calculate the delta between two source package sets."""336 """Try to calculate the delta between two source package sets."""
852 hoary = getUtility(IDistributionSet)['ubuntu']['hoary']337 hoary = getUtility(IDistributionSet)['ubuntu']['hoary']
853338
=== added file 'lib/lp/soyuz/tests/test_packagecloner.py'
--- lib/lp/soyuz/tests/test_packagecloner.py 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/tests/test_packagecloner.py 2010-07-16 03:28:56 +0000
@@ -0,0 +1,659 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4__metaclass__ = type
5
6from zope.component import getUtility
7from zope.security.proxy import removeSecurityProxy
8
9from canonical.testing import LaunchpadZopelessLayer
10
11from lp.buildmaster.interfaces.buildbase import BuildStatus
12from lp.registry.interfaces.pocket import PackagePublishingPocket
13from lp.soyuz.adapters.packagelocation import PackageLocation
14from lp.soyuz.interfaces.archive import ArchivePurpose
15from lp.soyuz.interfaces.archivearch import IArchiveArchSet
16from lp.soyuz.interfaces.component import IComponentSet
17from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
18from lp.soyuz.interfaces.packagecloner import IPackageCloner
19from lp.soyuz.interfaces.publishing import (
20 IPublishingSet, PackagePublishingStatus)
21from lp.soyuz.model.processor import ProcessorFamilySet
22from lp.testing import TestCaseWithFactory
23
24
25class PackageInfo:
26
27 def __init__(self, name, version,
28 status=PackagePublishingStatus.PUBLISHED, component="main"):
29 self.name = name
30 self.version = version
31 self.status = status
32 self.component = component
33
34
35class PackageClonerTests(TestCaseWithFactory):
36
37 layer = LaunchpadZopelessLayer
38
39 def checkCopiedSources(self, archive, distroseries, expected):
40 """Check the sources published in an archive against an expected set.
41
42 Given an archive and a target distroseries the sources published in
43 that distroseries are checked against a set of PackageInfo to
44 ensure that the correct package names and versions are published.
45 """
46 expected_set = set([(info.name, info.version) for info in expected])
47 sources = archive.getPublishedSources(
48 distroseries=distroseries,
49 status=(PackagePublishingStatus.PENDING,
50 PackagePublishingStatus.PUBLISHED))
51 actual_set = set()
52 for source in sources:
53 source = removeSecurityProxy(source)
54 actual_set.add(
55 (source.source_package_name, source.source_package_version))
56 self.assertEqual(expected_set, actual_set)
57
58 def createSourceDistribution(self, package_infos):
59 """Create a distribution to be the source of a copy archive."""
60 distroseries = self.createSourceDistroSeries()
61 self.createSourcePublications(package_infos, distroseries)
62 return distroseries
63
64 def createSourceDistroSeries(self):
65 """Create a DistroSeries suitable for copying.
66
67 Creates a distroseries with a DistroArchSeries and nominatedarchindep,
68 which makes it suitable for copying because it will create some builds.
69 """
70 distro_name = "foobuntu"
71 distro = self.factory.makeDistribution(name=distro_name)
72 distroseries_name = "maudlin"
73 distroseries = self.factory.makeDistroSeries(
74 distribution=distro, name=distroseries_name)
75 das = self.factory.makeDistroArchSeries(
76 distroseries=distroseries, architecturetag="i386",
77 processorfamily=ProcessorFamilySet().getByName("x86"),
78 supports_virtualized=True)
79 distroseries.nominatedarchindep = das
80 return distroseries
81
82 def getTargetArchive(self, distribution):
83 """Get a target archive for copying in to."""
84 return self.factory.makeArchive(
85 name="test-copy-archive", purpose=ArchivePurpose.COPY,
86 distribution=distribution)
87
88 def createSourcePublication(self, info, distroseries):
89 """Create a SourcePackagePublishingHistory based on a PackageInfo."""
90 archive = distroseries.distribution.main_archive
91 sources = archive.getPublishedSources(
92 distroseries=distroseries,
93 status=(PackagePublishingStatus.PENDING,
94 PackagePublishingStatus.PUBLISHED),
95 name=info.name, exact_match=True)
96 for src in sources:
97 src.supersede()
98 self.factory.makeSourcePackagePublishingHistory(
99 sourcepackagename=self.factory.getOrMakeSourcePackageName(
100 name=info.name),
101 distroseries=distroseries, component=self.factory.makeComponent(
102 info.component),
103 version=info.version, architecturehintlist='any',
104 archive=archive, status=info.status,
105 pocket=PackagePublishingPocket.RELEASE)
106
107 def createSourcePublications(self, package_infos, distroseries):
108 """Create a source publication for each item in package_infos."""
109 for package_info in package_infos:
110 self.createSourcePublication(package_info, distroseries)
111
112 def makeCopyArchive(self, package_infos, component="main",
113 source_pocket=None, target_pocket=None,
114 proc_families=None):
115 """Make a copy archive based on a new distribution."""
116 distroseries = self.createSourceDistribution(package_infos)
117 copy_archive = self.getTargetArchive(distroseries.distribution)
118 to_component = getUtility(IComponentSet).ensure(component)
119 self.copyArchive(
120 copy_archive, distroseries, from_pocket=source_pocket,
121 to_pocket=target_pocket, to_component=to_component,
122 proc_families=proc_families)
123 return (copy_archive, distroseries)
124
125 def checkBuilds(self, archive, package_infos):
126 """Check the build records pending in an archive.
127
128 Given a set of PackageInfo objects check that each has a build
129 created for it.
130 """
131 expected_builds = list(
132 [(info.name, info.version) for info in package_infos])
133 builds = list(
134 getUtility(IBinaryPackageBuildSet).getBuildsForArchive(
135 archive, status=BuildStatus.NEEDSBUILD))
136 actual_builds = list()
137 for build in builds:
138 naked_build = removeSecurityProxy(build)
139 spr = naked_build.source_package_release
140 actual_builds.append((spr.name, spr.version))
141 self.assertEqual(sorted(expected_builds), sorted(actual_builds))
142
143 def copyArchive(self, to_archive, to_distroseries, from_archive=None,
144 from_distroseries=None, from_pocket=None, to_pocket=None,
145 to_component=None, packagesets=None, proc_families=None):
146 """Use a PackageCloner to copy an archive."""
147 if from_distroseries is None:
148 from_distroseries = to_distroseries
149 if from_archive is None:
150 from_archive = from_distroseries.distribution.main_archive
151 if from_pocket is None:
152 from_pocket = PackagePublishingPocket.RELEASE
153 if to_pocket is None:
154 to_pocket = PackagePublishingPocket.RELEASE
155 if packagesets is None:
156 packagesets = []
157 origin = PackageLocation(
158 from_archive, from_distroseries.distribution, from_distroseries,
159 from_pocket)
160 destination = PackageLocation(
161 to_archive, to_distroseries.distribution, to_distroseries,
162 to_pocket)
163 origin.packagesets = packagesets
164 if to_component is not None:
165 destination.component = to_component
166 cloner = getUtility(IPackageCloner)
167 cloner.clonePackages(
168 origin, destination, distroarchseries_list=None,
169 proc_families=proc_families)
170 return cloner
171
172 def testCopiesPublished(self):
173 """Test that PUBLISHED sources are copied."""
174 package_info = PackageInfo(
175 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
176 copy_archive, distroseries = self.makeCopyArchive([package_info])
177 self.checkCopiedSources(
178 copy_archive, distroseries, [package_info])
179
180 def testCopiesPending(self):
181 """Test that PENDING sources are copied."""
182 package_info = PackageInfo(
183 "bzr", "2.1", status=PackagePublishingStatus.PENDING)
184 copy_archive, distroseries = self.makeCopyArchive([package_info])
185 self.checkCopiedSources(
186 copy_archive, distroseries, [package_info])
187
188 def testDoesntCopySuperseded(self):
189 """Test that SUPERSEDED sources are not copied."""
190 package_info = PackageInfo(
191 "bzr", "2.1", status=PackagePublishingStatus.SUPERSEDED)
192 copy_archive, distroseries = self.makeCopyArchive([package_info])
193 self.checkCopiedSources(
194 copy_archive, distroseries, [])
195
196 def testDoesntCopyDeleted(self):
197 """Test that DELETED sources are not copied."""
198 package_info = PackageInfo(
199 "bzr", "2.1", status=PackagePublishingStatus.DELETED)
200 copy_archive, distroseries = self.makeCopyArchive([package_info])
201 self.checkCopiedSources(
202 copy_archive, distroseries, [])
203
204 def testDoesntCopyObsolete(self):
205 """Test that OBSOLETE sources are not copied."""
206 package_info = PackageInfo(
207 "bzr", "2.1", status=PackagePublishingStatus.OBSOLETE)
208 copy_archive, distroseries = self.makeCopyArchive([package_info])
209 self.checkCopiedSources(
210 copy_archive, distroseries, [])
211
212 def testCopiesAllComponents(self):
213 """Test that packages from all components are copied.
214
215 When copying you specify a component, but that component doesn't
216 limit the packages copied. We create a source in main and one in
217 universe, and then copy with --component main, and expect to see
218 both sources in the copy.
219 """
220 package_infos = [
221 PackageInfo(
222 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED,
223 component="universe"),
224 PackageInfo(
225 "apt", "2.2", status=PackagePublishingStatus.PUBLISHED,
226 component="main")]
227 copy_archive, distroseries = self.makeCopyArchive(package_infos,
228 component="main")
229 self.checkCopiedSources(copy_archive, distroseries, package_infos)
230
231 def testSubsetsBasedOnPackageset(self):
232 """Test that --package-set limits the sources copied."""
233 package_infos = [
234 PackageInfo(
235 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
236 PackageInfo(
237 "apt", "2.2", status=PackagePublishingStatus.PUBLISHED),
238 ]
239 distroseries = self.createSourceDistribution(package_infos)
240 spn = self.factory.getOrMakeSourcePackageName(name="apt")
241 packageset = self.factory.makePackageset(
242 distroseries=distroseries, packages=(spn,))
243 copy_archive = self.getTargetArchive(distroseries.distribution)
244 self.copyArchive(copy_archive, distroseries, packagesets=[packageset])
245 self.checkCopiedSources(
246 copy_archive, distroseries, [package_infos[1]])
247
248 def testUnionsPackagesets(self):
249 """Test that package sets are unioned when copying archives."""
250 package_infos = [
251 PackageInfo(
252 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
253 PackageInfo(
254 "apt", "2.2", status=PackagePublishingStatus.PUBLISHED),
255 PackageInfo(
256 "gcc", "4.5", status=PackagePublishingStatus.PUBLISHED),
257 ]
258 distroseries = self.createSourceDistribution(package_infos)
259 apt_spn = self.factory.getOrMakeSourcePackageName(name="apt")
260 gcc_spn = self.factory.getOrMakeSourcePackageName(name="gcc")
261 apt_packageset = self.factory.makePackageset(
262 distroseries=distroseries, packages=(apt_spn,))
263 gcc_packageset = self.factory.makePackageset(
264 distroseries=distroseries, packages=(gcc_spn,))
265 copy_archive = self.getTargetArchive(distroseries.distribution)
266 self.copyArchive(
267 copy_archive, distroseries,
268 packagesets=[apt_packageset, gcc_packageset])
269 self.checkCopiedSources(
270 copy_archive, distroseries, package_infos[1:])
271
272 def testRecursivelyCopiesPackagesets(self):
273 """Test that package set copies include subsets."""
274 package_infos = [
275 PackageInfo(
276 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
277 PackageInfo(
278 "apt", "2.2", status=PackagePublishingStatus.PUBLISHED),
279 PackageInfo(
280 "gcc", "4.5", status=PackagePublishingStatus.PUBLISHED),
281 ]
282 distroseries = self.createSourceDistribution(package_infos)
283 apt_spn = self.factory.getOrMakeSourcePackageName(name="apt")
284 gcc_spn = self.factory.getOrMakeSourcePackageName(name="gcc")
285 apt_packageset = self.factory.makePackageset(
286 distroseries=distroseries, packages=(apt_spn,))
287 gcc_packageset = self.factory.makePackageset(
288 distroseries=distroseries, packages=(gcc_spn,))
289 apt_packageset.add((gcc_packageset,))
290 copy_archive = self.getTargetArchive(distroseries.distribution)
291 self.copyArchive(
292 copy_archive, distroseries, packagesets=[apt_packageset])
293 self.checkCopiedSources(
294 copy_archive, distroseries, package_infos[1:])
295
296 def testCloneFromPPA(self):
297 """Test we can create a copy archive with a PPA as the source."""
298 distroseries = self.createSourceDistroSeries()
299 ppa = self.factory.makeArchive(
300 purpose=ArchivePurpose.PPA,
301 distribution=distroseries.distribution)
302 package_info = PackageInfo(
303 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED,
304 component="universe")
305 self.factory.makeSourcePackagePublishingHistory(
306 sourcepackagename=self.factory.getOrMakeSourcePackageName(
307 name=package_info.name),
308 distroseries=distroseries, component=self.factory.makeComponent(
309 package_info.component),
310 version=package_info.version, archive=ppa,
311 status=package_info.status, architecturehintlist='any',
312 pocket=PackagePublishingPocket.RELEASE)
313 copy_archive = self.getTargetArchive(distroseries.distribution)
314 self.copyArchive(copy_archive, distroseries, from_archive=ppa)
315 self.checkCopiedSources(
316 copy_archive, distroseries, [package_info])
317
318 def testCreatesNoBuildsWithNoProcFamilies(self):
319 """Test that no builds are created if we specify no proc families."""
320 package_info = PackageInfo(
321 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
322 copy_archive, distroseries = self.makeCopyArchive([package_info])
323 self.checkBuilds(copy_archive, [])
324
325 def testCreatesBuilds(self):
326 """Test that a copy archive creates builds for the copied packages."""
327 package_info = PackageInfo(
328 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
329 # This is the processor family for the DAS that the source has,
330 # so we expect to get builds.
331 proc_families = [ProcessorFamilySet().getByName("x86")]
332 copy_archive, distroseries = self.makeCopyArchive(
333 [package_info], proc_families=proc_families)
334 self.checkBuilds(copy_archive, [package_info])
335
336 def testNoBuildsIfProcFamilyNotInSource(self):
337 """Test that no builds are created for a proc family without a DAS."""
338 package_info = PackageInfo(
339 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
340 # This is a processor family without a DAS in the source, so
341 # we expect no builds.
342 family = self.factory.makeProcessorFamily(name="armel")
343 self.factory.makeProcessor(family=family, name="armel")
344 proc_families = [family]
345 copy_archive, distroseries = self.makeCopyArchive(
346 [package_info], proc_families=proc_families)
347 self.checkBuilds(copy_archive, [])
348
349 def testBuildsOnlyForProcFamiliesInSource(self):
350 """Test that builds are only created for proc families in source."""
351 package_info = PackageInfo(
352 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
353 # One of these processor families has a DAS in the source, so
354 # we expect one set of builds
355 family = self.factory.makeProcessorFamily(name="armel")
356 self.factory.makeProcessor(family=family, name="armel")
357 proc_families = [family, ProcessorFamilySet().getByName("x86")]
358 copy_archive, distroseries = self.makeCopyArchive(
359 [package_info], proc_families=proc_families)
360 self.checkBuilds(copy_archive, [package_info])
361
362 def testCreatesSubsetOfBuilds(self):
363 """Test that builds are only created for requested families."""
364 package_info = PackageInfo(
365 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
366 distroseries = self.createSourceDistribution([package_info])
367 # Create a DAS for a second family
368 self.factory.makeDistroArchSeries(
369 distroseries=distroseries, architecturetag="amd64",
370 processorfamily=ProcessorFamilySet().getByName("amd64"),
371 supports_virtualized=True)
372 # The request builds for only one of the families, so we
373 # expect just one build for each source
374 proc_families = [ProcessorFamilySet().getByName("x86")]
375 copy_archive = self.getTargetArchive(distroseries.distribution)
376 self.copyArchive(
377 copy_archive, distroseries, proc_families=proc_families)
378 self.checkBuilds(copy_archive, [package_info])
379
380 def testCreatesMultipleBuilds(self):
381 """Test that multiple families result in mutiple builds."""
382 package_info = PackageInfo(
383 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
384 distroseries = self.createSourceDistribution([package_info])
385 # Create a DAS for a second family
386 amd64_family = ProcessorFamilySet().getByName("amd64")
387 self.factory.makeDistroArchSeries(
388 distroseries=distroseries, architecturetag="amd64",
389 processorfamily=amd64_family, supports_virtualized=True)
390 # The request builds for both families, so we expect two builds
391 # per source.
392 proc_families = [ProcessorFamilySet().getByName("x86"), amd64_family]
393 copy_archive = self.getTargetArchive(distroseries.distribution)
394 self.copyArchive(
395 copy_archive, distroseries, proc_families=proc_families)
396 self.checkBuilds(copy_archive, [package_info, package_info])
397
398
399 def diffArchives(self, target_archive, target_distroseries,
400 source_archive=None, source_distroseries=None):
401 """Run a packageSetDiff of two archives."""
402 if source_distroseries is None:
403 source_distroseries = target_distroseries
404 if source_archive is None:
405 source_archive = source_distroseries.distribution.main_archive
406 source_location = PackageLocation(
407 source_archive, source_distroseries.distribution,
408 source_distroseries, PackagePublishingPocket.RELEASE)
409 target_location = PackageLocation(
410 target_archive, target_distroseries.distribution,
411 target_distroseries, PackagePublishingPocket.RELEASE)
412 cloner = getUtility(IPackageCloner)
413 return cloner.packageSetDiff(source_location, target_location)
414
415 def checkPackageDiff(self, expected_changed, expected_new, actual,
416 archive):
417 """Check that the diff of two archives is as expected."""
418 actual_changed_keys, actual_new_keys = actual
419 expected_changed_tuples = [(e.name, e.version)
420 for e in expected_changed]
421 expected_new_tuples = [(e.name, e.version) for e in expected_new]
422 def get_tuples(source_keys):
423 tuples = []
424 for source_key in source_keys:
425 source = getUtility(IPublishingSet).getByIdAndArchive(
426 source_key, archive, source=True)
427 self.assertNotEqual(source, None, "Got a non-existant "
428 "source publishing record: %d" % source_key)
429 naked_source = removeSecurityProxy(source)
430 tuples.append(
431 (naked_source.source_package_name,
432 naked_source.source_package_version))
433 return tuples
434 actual_changed_tuples = get_tuples(actual_changed_keys)
435 actual_new_tuples = get_tuples(actual_new_keys)
436 self.assertEqual(expected_changed_tuples, actual_changed_tuples)
437 self.assertEqual(expected_new_tuples, actual_new_tuples)
438
439 def testPackageSetDiffWithNothingNew(self):
440 """Test packageSetDiff."""
441 package_info = PackageInfo(
442 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
443 copy_archive, distroseries = self.makeCopyArchive([package_info])
444 diff = self.diffArchives(copy_archive, distroseries)
445 self.checkPackageDiff(
446 [], [], diff, distroseries.distribution.main_archive)
447
448 def testPackageSetDiffWithNewPackages(self):
449 package_info = PackageInfo(
450 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
451 copy_archive, distroseries = self.makeCopyArchive([package_info])
452 package_infos = [
453 PackageInfo(
454 "apt", "1.2", status=PackagePublishingStatus.PUBLISHED),
455 PackageInfo(
456 "gcc", "4.5", status=PackagePublishingStatus.PENDING),
457 ]
458 self.createSourcePublications(package_infos, distroseries)
459 diff = self.diffArchives(copy_archive, distroseries)
460 self.checkPackageDiff(
461 [], package_infos, diff, distroseries.distribution.main_archive)
462
463 def testPackageSetDiffWithChangedPackages(self):
464 package_infos = [
465 PackageInfo(
466 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
467 PackageInfo(
468 "apt", "1.2", status=PackagePublishingStatus.PUBLISHED),
469 ]
470 copy_archive, distroseries = self.makeCopyArchive(package_infos)
471 package_infos = [
472 PackageInfo(
473 "bzr", "2.2", status=PackagePublishingStatus.PUBLISHED),
474 PackageInfo(
475 "apt", "1.3", status=PackagePublishingStatus.PENDING),
476 ]
477 self.createSourcePublications(package_infos, distroseries)
478 diff = self.diffArchives(copy_archive, distroseries)
479 self.checkPackageDiff(
480 package_infos, [], diff, distroseries.distribution.main_archive)
481
482 def testPackageSetDiffWithBoth(self):
483 package_infos = [
484 PackageInfo(
485 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
486 PackageInfo(
487 "apt", "1.2", status=PackagePublishingStatus.PUBLISHED),
488 ]
489 copy_archive, distroseries = self.makeCopyArchive(package_infos)
490 package_infos = [
491 PackageInfo(
492 "bzr", "2.2", status=PackagePublishingStatus.PUBLISHED),
493 PackageInfo(
494 "gcc", "1.3", status=PackagePublishingStatus.PENDING),
495 ]
496 self.createSourcePublications(package_infos, distroseries)
497 diff = self.diffArchives(copy_archive, distroseries)
498 self.checkPackageDiff(
499 [package_infos[0]], [package_infos[1]], diff,
500 distroseries.distribution.main_archive)
501
502
503 def mergeCopy(self, target_archive, target_distroseries,
504 source_archive=None, source_distroseries=None):
505 if source_distroseries is None:
506 source_distroseries = target_distroseries
507 if source_archive is None:
508 source_archive = source_distroseries.distribution.main_archive
509 source_location = PackageLocation(
510 source_archive, source_distroseries.distribution,
511 source_distroseries, PackagePublishingPocket.RELEASE)
512 target_location = PackageLocation(
513 target_archive, target_distroseries.distribution,
514 target_distroseries, PackagePublishingPocket.RELEASE)
515 cloner = getUtility(IPackageCloner)
516 return cloner.mergeCopy(source_location, target_location)
517
518 def testMergeCopyNoChanges(self):
519 package_info = PackageInfo(
520 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
521 copy_archive, distroseries = self.makeCopyArchive([package_info])
522 self.mergeCopy(copy_archive, distroseries)
523 self.checkCopiedSources(
524 copy_archive, distroseries, [package_info])
525
526 def testMergeCopyWithNewPackages(self):
527 package_info = PackageInfo(
528 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED)
529 copy_archive, distroseries = self.makeCopyArchive([package_info])
530 package_infos = [
531 PackageInfo(
532 "apt", "1.2", status=PackagePublishingStatus.PUBLISHED),
533 PackageInfo(
534 "gcc", "4.5", status=PackagePublishingStatus.PENDING),
535 ]
536 self.createSourcePublications(package_infos, distroseries)
537 self.mergeCopy(copy_archive, distroseries)
538 self.checkCopiedSources(
539 copy_archive, distroseries, [package_info] + package_infos)
540
541 def testMergeCopyWithChangedPackages(self):
542 package_infos = [
543 PackageInfo(
544 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
545 PackageInfo(
546 "apt", "1.2", status=PackagePublishingStatus.PUBLISHED),
547 ]
548 copy_archive, distroseries = self.makeCopyArchive(package_infos)
549 package_infos = [
550 PackageInfo(
551 "bzr", "2.2", status=PackagePublishingStatus.PUBLISHED),
552 PackageInfo(
553 "apt", "1.3", status=PackagePublishingStatus.PENDING),
554 ]
555 self.createSourcePublications(package_infos, distroseries)
556 self.mergeCopy(copy_archive, distroseries)
557 # Critically there is only one record for each info, as the
558 # others have been obsoleted.
559 self.checkCopiedSources(
560 copy_archive, distroseries, package_infos)
561
562 def testMergeCopyWithBoth(self):
563 package_infos = [
564 PackageInfo(
565 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
566 PackageInfo(
567 "apt", "1.2", status=PackagePublishingStatus.PUBLISHED),
568 ]
569 copy_archive, distroseries = self.makeCopyArchive(package_infos)
570 package_infos2 = [
571 PackageInfo(
572 "bzr", "2.2", status=PackagePublishingStatus.PUBLISHED),
573 PackageInfo(
574 "gcc", "1.3", status=PackagePublishingStatus.PENDING),
575 ]
576 self.createSourcePublications(package_infos2, distroseries)
577 self.mergeCopy(copy_archive, distroseries)
578 # Again bzr is obsoleted, gcc is added and apt remains.
579 self.checkCopiedSources(
580 copy_archive, distroseries, [package_infos[1]] + package_infos2)
581
582 def setArchiveArchitectures(self, archive, proc_families):
583 """Associate the archive with the processor families."""
584 aa_set = getUtility(IArchiveArchSet)
585 for proc_family in proc_families:
586 aa_set.new(archive, proc_family)
587
588 def testMergeCopyCreatesBuilds(self):
589 package_infos = [
590 PackageInfo(
591 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
592 PackageInfo(
593 "apt", "1.2", status=PackagePublishingStatus.PUBLISHED),
594 ]
595 proc_families = [ProcessorFamilySet().getByName("x86")]
596 copy_archive, distroseries = self.makeCopyArchive(
597 package_infos, proc_families=proc_families)
598 self.setArchiveArchitectures(copy_archive, proc_families)
599 package_infos2 = [
600 PackageInfo(
601 "bzr", "2.2", status=PackagePublishingStatus.PUBLISHED),
602 PackageInfo(
603 "gcc", "1.3", status=PackagePublishingStatus.PENDING),
604 ]
605 self.createSourcePublications(package_infos2, distroseries)
606 self.mergeCopy(copy_archive, distroseries)
607 # We get all builds, as superseding bzr doesn't cancel the
608 # build
609 self.checkBuilds(copy_archive, package_infos + package_infos2)
610
611 def testMergeCopyCreatesNoBuildsWhenNoArchitectures(self):
612 package_infos = [
613 PackageInfo(
614 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
615 ]
616 # We specify no processor families at creation time
617 copy_archive, distroseries = self.makeCopyArchive(
618 package_infos, proc_families=[])
619 package_infos2 = [
620 PackageInfo(
621 "bzr", "2.2", status=PackagePublishingStatus.PUBLISHED),
622 ]
623 self.createSourcePublications(package_infos2, distroseries)
624 self.mergeCopy(copy_archive, distroseries)
625 # And so we get no builds at merge time
626 self.checkBuilds(copy_archive, [])
627
628 def testMergeCopyCreatesBuildsForMultipleArchitectures(self):
629 package_infos = [
630 PackageInfo(
631 "bzr", "2.1", status=PackagePublishingStatus.PUBLISHED),
632 PackageInfo(
633 "apt", "1.2", status=PackagePublishingStatus.PUBLISHED),
634 ]
635 distroseries = self.createSourceDistribution(package_infos)
636 # Create a DAS for a second family
637 amd64_family = ProcessorFamilySet().getByName("amd64")
638 self.factory.makeDistroArchSeries(
639 distroseries=distroseries, architecturetag="amd64",
640 processorfamily=amd64_family, supports_virtualized=True)
641 # The request builds for both families, so we expect two builds
642 # per source.
643 proc_families = [ProcessorFamilySet().getByName("x86"), amd64_family]
644 copy_archive = self.getTargetArchive(distroseries.distribution)
645 self.setArchiveArchitectures(copy_archive, proc_families)
646 self.copyArchive(
647 copy_archive, distroseries, proc_families=proc_families)
648 package_infos2 = [
649 PackageInfo(
650 "bzr", "2.2", status=PackagePublishingStatus.PUBLISHED),
651 PackageInfo(
652 "gcc", "1.3", status=PackagePublishingStatus.PENDING),
653 ]
654 self.createSourcePublications(package_infos2, distroseries)
655 self.mergeCopy(copy_archive, distroseries)
656 # We get all builds twice, one for each architecture.
657 self.checkBuilds(
658 copy_archive,
659 package_infos + package_infos + package_infos2 + package_infos2)