200
216
TestDomination.setUp(self)
201
217
self.ubuntutest['breezy-autotest'].status = (
202
218
SeriesStatus.OBSOLETE)
221
def make_spphs_for_versions(factory, versions):
222
"""Create publication records for each of `versions`.
224
They records are created in the same order in which they are specified.
225
Make the order irregular to prove that version ordering is not a
226
coincidence of object creation order etc.
228
Versions may also be identical; each publication record will still have
229
its own package release.
231
spn = factory.makeSourcePackageName()
232
distroseries = factory.makeDistroSeries()
233
pocket = factory.getAnyPocket()
235
factory.makeSourcePackageRelease(
236
sourcepackagename=spn, version=version)
237
for version in versions]
239
factory.makeSourcePackagePublishingHistory(
240
distroseries=distroseries, pocket=pocket,
241
sourcepackagerelease=spr,
242
status=PackagePublishingStatus.PUBLISHED)
246
def list_source_versions(spphs):
247
"""Extract the versions from `spphs` as a list, in the same order."""
248
return [spph.sourcepackagerelease.version for spph in spphs]
251
def alter_creation_dates(spphs, ages):
252
"""Set `datecreated` on each of `spphs` according to `ages`.
254
:param spphs: Iterable of `SourcePackagePublishingHistory`. Their
255
respective creation dates will be offset by the respective ages found
256
in `ages` (with the two being matched up in the same order).
257
:param ages: Iterable of ages. Must provide the same number of items as
258
`spphs`. Ages are `timedelta` objects that will be subtracted from
259
the creation dates on the respective records in `spph`.
261
for spph, age in zip(spphs, ages):
262
spph.datecreated -= age
265
class TestGeneralizedPublication(TestCaseWithFactory):
266
"""Test publication generalization helpers."""
268
layer = ZopelessDatabaseLayer
270
def test_getPackageVersion_gets_source_version(self):
271
spph = self.factory.makeSourcePackagePublishingHistory()
273
spph.sourcepackagerelease.version,
274
GeneralizedPublication(is_source=True).getPackageVersion(spph))
276
def test_getPackageVersion_gets_binary_version(self):
277
bpph = self.factory.makeBinaryPackagePublishingHistory()
279
bpph.binarypackagerelease.version,
280
GeneralizedPublication(is_source=False).getPackageVersion(bpph))
282
def test_compare_sorts_versions(self):
288
spphs = make_spphs_for_versions(self.factory, versions)
289
sorted_spphs = sorted(spphs, cmp=GeneralizedPublication().compare)
291
sorted(versions), list_source_versions(sorted_spphs))
293
def test_compare_orders_versions_by_debian_rules(self):
300
spphs = make_spphs_for_versions(self.factory, versions)
302
debian_sorted_versions = sorted(versions, cmp=apt_pkg.VersionCompare)
304
# Assumption: in this case, Debian version ordering is not the
305
# same as alphabetical version ordering.
306
self.assertNotEqual(sorted(versions), debian_sorted_versions)
308
# The compare method produces the Debian ordering.
309
sorted_spphs = sorted(spphs, cmp=GeneralizedPublication().compare)
311
sorted(versions, cmp=apt_pkg.VersionCompare),
312
list_source_versions(sorted_spphs))
314
def test_compare_breaks_tie_with_creation_date(self):
315
# When two publications are tied for comparison because they are
316
# for the same package release, they are ordered by creation
318
distroseries = self.factory.makeDistroSeries()
319
pocket = self.factory.getAnyPocket()
320
spr = self.factory.makeSourcePackageRelease()
322
datetime.timedelta(2),
323
datetime.timedelta(1),
324
datetime.timedelta(3),
327
self.factory.makeSourcePackagePublishingHistory(
328
sourcepackagerelease=spr, distroseries=distroseries,
330
for counter in xrange(len(ages))]
331
alter_creation_dates(spphs, ages)
334
[spphs[2], spphs[0], spphs[1]],
335
sorted(spphs, cmp=GeneralizedPublication().compare))
337
def test_compare_breaks_tie_for_releases_with_same_version(self):
338
# When two publications are tied for comparison because they
339
# belong to releases with the same version string, they are
340
# ordered by creation date.
341
version = "1.%d" % self.factory.getUniqueInteger()
343
datetime.timedelta(2),
344
datetime.timedelta(1),
345
datetime.timedelta(3),
347
distroseries = self.factory.makeDistroSeries()
348
pocket = self.factory.getAnyPocket()
350
self.factory.makeSourcePackagePublishingHistory(
351
distroseries=distroseries, pocket=pocket,
352
sourcepackagerelease=self.factory.makeSourcePackageRelease(
354
for counter in xrange(len(ages))]
355
alter_creation_dates(spphs, ages)
358
[spphs[2], spphs[0], spphs[1]],
359
sorted(spphs, cmp=GeneralizedPublication().compare))
362
def jumble(ordered_list):
363
"""Jumble the elements of `ordered_list` into a weird order.
365
Ordering is very important in domination. We jumble some of our lists to
366
insure against "lucky coincidences" that might give our tests the right
367
answers for the wrong reasons.
370
item for offset, item in enumerate(ordered_list) if offset % 2 == 0]
372
item for offset, item in enumerate(ordered_list) if offset % 2 != 0]
373
return list(reversed(odd)) + even
376
class TestDominatorMethods(TestCaseWithFactory):
378
layer = ZopelessDatabaseLayer
380
def makeDominator(self, publications):
381
"""Create a `Dominator` suitable for `publications`."""
382
if len(publications) == 0:
383
archive = self.factory.makeArchive()
385
archive = publications[0].archive
386
return Dominator(DevNullLogger(), archive)
388
def test_dominatePackage_survives_empty_publications_list(self):
389
# Nothing explodes when dominatePackage is called with an empty
391
self.makeDominator([]).dominatePackage(
392
[], [], GeneralizedPublication(True))
393
# The test is that we get here without error.
396
def test_dominatePackage_leaves_live_version_untouched(self):
397
# dominatePackage does not supersede live versions.
398
[pub] = make_spphs_for_versions(self.factory, ['3.1'])
399
self.makeDominator([pub]).dominatePackage(
400
[pub], ['3.1'], GeneralizedPublication(True))
401
self.assertEqual(PackagePublishingStatus.PUBLISHED, pub.status)
403
def test_dominatePackage_deletes_dead_version_without_successor(self):
404
# dominatePackage marks non-live package versions without
405
# superseding versions as deleted.
406
[pub] = make_spphs_for_versions(self.factory, ['1.1'])
407
self.makeDominator([pub]).dominatePackage(
408
[pub], [], GeneralizedPublication(True))
409
self.assertEqual(PackagePublishingStatus.DELETED, pub.status)
411
def test_dominatePackage_supersedes_older_pub_with_newer_live_pub(self):
412
# When marking a package as superseded, dominatePackage
413
# designates a newer live version as the superseding version.
414
pubs = make_spphs_for_versions(self.factory, ['1.0', '1.1'])
415
self.makeDominator(pubs).dominatePackage(
416
pubs, ['1.1'], GeneralizedPublication(True))
417
self.assertEqual(PackagePublishingStatus.SUPERSEDED, pubs[0].status)
418
self.assertEqual(pubs[1].sourcepackagerelease, pubs[0].supersededby)
419
self.assertEqual(PackagePublishingStatus.PUBLISHED, pubs[1].status)
421
def test_dominatePackage_only_supersedes_with_live_pub(self):
422
# When marking a package as superseded, dominatePackage will
423
# only pick a live version as the superseding one.
424
pubs = make_spphs_for_versions(
425
self.factory, ['1.0', '2.0', '3.0', '4.0'])
426
self.makeDominator(pubs).dominatePackage(
427
pubs, ['3.0'], GeneralizedPublication(True))
429
pubs[2].sourcepackagerelease,
430
pubs[2].sourcepackagerelease,
434
[pub.supersededby for pub in pubs])
436
def test_dominatePackage_supersedes_with_oldest_newer_live_pub(self):
437
# When marking a package as superseded, dominatePackage picks
438
# the oldest of the newer, live versions as the superseding one.
439
pubs = make_spphs_for_versions(self.factory, ['2.7', '2.8', '2.9'])
440
self.makeDominator(pubs).dominatePackage(
441
pubs, ['2.8', '2.9'], GeneralizedPublication(True))
442
self.assertEqual(pubs[1].sourcepackagerelease, pubs[0].supersededby)
444
def test_dominatePackage_only_supersedes_with_newer_live_pub(self):
445
# When marking a package as superseded, dominatePackage only
446
# considers a newer version as the superseding one.
447
pubs = make_spphs_for_versions(self.factory, ['0.1', '0.2'])
448
self.makeDominator(pubs).dominatePackage(
449
pubs, ['0.1'], GeneralizedPublication(True))
450
self.assertEqual(None, pubs[1].supersededby)
451
self.assertEqual(PackagePublishingStatus.DELETED, pubs[1].status)
453
def test_dominatePackage_supersedes_replaced_pub_for_live_version(self):
454
# Even if a publication record is for a live version, a newer
455
# one for the same version supersedes it.
456
spr = self.factory.makeSourcePackageRelease()
457
series = self.factory.makeDistroSeries()
458
pocket = PackagePublishingPocket.RELEASE
460
self.factory.makeSourcePackagePublishingHistory(
461
archive=series.main_archive, distroseries=series,
462
pocket=pocket, status=PackagePublishingStatus.PUBLISHED,
463
sourcepackagerelease=spr)
464
for counter in xrange(3)]
465
alter_creation_dates(pubs, [
466
datetime.timedelta(3),
467
datetime.timedelta(2),
468
datetime.timedelta(1),
471
self.makeDominator(pubs).dominatePackage(
472
pubs, [spr.version], GeneralizedPublication(True))
474
PackagePublishingStatus.SUPERSEDED,
475
PackagePublishingStatus.SUPERSEDED,
476
PackagePublishingStatus.PUBLISHED,
478
[pub.status for pub in pubs])
480
[spr, spr, None], [pub.supersededby for pub in pubs])
482
def test_dominatePackage_advanced_scenario(self):
483
# Put dominatePackage through its paces with complex combined
485
# This test should be redundant in theory (which in theory
486
# equates practice but in practice does not). If this fails,
487
# don't just patch up the code or this test. Create unit tests
488
# that specifically cover the difference, then change the code
489
# and/or adapt this test to return to harmony.
490
series = self.factory.makeDistroSeries()
491
package = self.factory.makeSourcePackageName()
492
pocket = PackagePublishingPocket.RELEASE
494
versions = ["1.%d" % number for number in xrange(4)]
496
# We have one package releases for each version.
497
relevant_releases = dict(
498
(version, self.factory.makeSourcePackageRelease(
499
sourcepackagename=package, version=version))
500
for version in jumble(versions))
502
# Each of those releases is subsequently published in
503
# different components.
505
[self.factory.makeComponent() for version in versions])
507
# Map versions to lists of publications for that version, from
508
# oldest to newest. Each re-publishing into a different
509
# component is meant to supersede publication into the previous
511
pubs_by_version = dict(
513
self.factory.makeSourcePackagePublishingHistory(
514
archive=series.main_archive, distroseries=series,
515
pocket=pocket, status=PackagePublishingStatus.PUBLISHED,
516
sourcepackagerelease=relevant_releases[version],
518
for component in components])
519
for version in jumble(versions))
522
[datetime.timedelta(age) for age in xrange(len(versions))])
524
# Actually the "oldest to newest" order on the publications only
525
# applies to their creation dates. Their creation orders are
527
for pubs_list in pubs_by_version.itervalues():
528
alter_creation_dates(pubs_list, ages)
529
pubs_list.sort(key=attrgetter('datecreated'))
531
live_versions = ["1.1", "1.2"]
532
last_version_alive = sorted(live_versions)[-1]
534
all_pubs = sum(pubs_by_version.itervalues(), [])
535
Dominator(DevNullLogger(), series.main_archive).dominatePackage(
536
all_pubs, live_versions, GeneralizedPublication(True))
538
for version in reversed(versions):
539
pubs = pubs_by_version[version]
541
if version in live_versions:
542
# Beware: loop-carried variable. Used locally as well,
543
# but tells later iterations what the highest-versioned
544
# release so far was. This is used in tracking
545
# supersededby links.
546
superseding_release = pubs[-1].sourcepackagerelease
548
if version in live_versions:
549
# The live versions' latest publications are Published,
550
# their older ones Superseded.
552
[PackagePublishingStatus.SUPERSEDED] * (len(pubs) - 1) +
553
[PackagePublishingStatus.PUBLISHED])
554
expected_supersededby = (
555
[superseding_release] * (len(pubs) - 1) + [None])
556
elif version < last_version_alive:
557
# The superseded versions older than the last live
558
# version have all been superseded.
560
[PackagePublishingStatus.SUPERSEDED] * len(pubs))
561
expected_supersededby = [superseding_release] * len(pubs)
563
# Versions that are newer than any live release have
566
[PackagePublishingStatus.DELETED] * len(pubs))
567
expected_supersededby = [None] * len(pubs)
569
self.assertEqual(expected_status, [pub.status for pub in pubs])
571
expected_supersededby, [pub.supersededby for pub in pubs])
573
def test_dominateRemovedSourceVersions_dominates_publications(self):
574
# dominateRemovedSourceVersions finds the publications for a
575
# package and calls dominatePackage on them.
576
pubs = make_spphs_for_versions(self.factory, ['0.1', '0.2', '0.3'])
577
package_name = pubs[0].sourcepackagerelease.sourcepackagename.name
579
self.makeDominator(pubs).dominateRemovedSourceVersions(
580
pubs[0].distroseries, pubs[0].pocket, package_name, ['0.2'])
582
PackagePublishingStatus.SUPERSEDED,
583
PackagePublishingStatus.PUBLISHED,
584
PackagePublishingStatus.DELETED,
586
[pub.status for pub in pubs])
588
[pubs[1].sourcepackagerelease, None, None],
589
[pub.supersededby for pub in pubs])
591
def test_dominateRemovedSourceVersions_ignores_other_pockets(self):
592
# dominateRemovedSourceVersions ignores publications in other
593
# pockets than the one specified.
594
pubs = make_spphs_for_versions(self.factory, ['2.3', '2.4'])
595
package_name = pubs[0].sourcepackagerelease.sourcepackagename.name
596
removeSecurityProxy(pubs[0]).pocket = PackagePublishingPocket.UPDATES
597
removeSecurityProxy(pubs[1]).pocket = PackagePublishingPocket.PROPOSED
598
self.makeDominator(pubs).dominateRemovedSourceVersions(
599
pubs[0].distroseries, pubs[0].pocket, package_name, ['2.3'])
600
self.assertEqual(PackagePublishingStatus.PUBLISHED, pubs[1].status)
602
def test_dominateRemovedSourceVersions_ignores_other_packages(self):
603
pubs = make_spphs_for_versions(self.factory, ['1.0', '1.1'])
604
other_package_name = self.factory.makeSourcePackageName().name
605
self.makeDominator(pubs).dominateRemovedSourceVersions(
606
pubs[0].distroseries, pubs[0].pocket, other_package_name, ['1.1'])
607
self.assertEqual(PackagePublishingStatus.PUBLISHED, pubs[0].status)
609
def test_findPublishedSourcePackageNames_finds_package(self):
610
spph = self.factory.makeSourcePackagePublishingHistory(
611
status=PackagePublishingStatus.PUBLISHED)
612
dominator = self.makeDominator([spph])
613
self.assertContentEqual(
614
[spph.sourcepackagerelease.sourcepackagename.name],
615
dominator.findPublishedSourcePackageNames(
616
spph.distroseries, spph.pocket))
618
def test_findPublishedSourcePackageNames_ignores_other_states(self):
619
series = self.factory.makeDistroSeries()
620
pocket = PackagePublishingPocket.RELEASE
622
(status, self.factory.makeSourcePackagePublishingHistory(
623
distroseries=series, archive=series.main_archive,
624
pocket=pocket, status=status))
625
for status in PackagePublishingStatus.items)
626
published_spph = spphs[PackagePublishingStatus.PUBLISHED]
627
dominator = self.makeDominator(spphs.values())
628
self.assertContentEqual(
629
[published_spph.sourcepackagerelease.sourcepackagename.name],
630
dominator.findPublishedSourcePackageNames(series, pocket))
632
def test_findPublishedSourcePackageNames_ignores_other_archives(self):
633
spph = self.factory.makeSourcePackagePublishingHistory(
634
status=PackagePublishingStatus.PUBLISHED)
635
dominator = self.makeDominator([spph])
636
dominator.archive = self.factory.makeArchive()
637
self.assertContentEqual(
639
dominator.findPublishedSourcePackageNames(
640
spph.distroseries, spph.pocket))
642
def test_findPublishedSourcePackageNames_ignores_other_series(self):
643
spph = self.factory.makeSourcePackagePublishingHistory(
644
status=PackagePublishingStatus.PUBLISHED)
645
distro = spph.distroseries.distribution
646
other_series = self.factory.makeDistroSeries(distribution=distro)
647
dominator = self.makeDominator([spph])
648
self.assertContentEqual(
650
dominator.findPublishedSourcePackageNames(
651
other_series, spph.pocket))
653
def test_findPublishedSourcePackageNames_ignores_other_pockets(self):
654
spph = self.factory.makeSourcePackagePublishingHistory(
655
status=PackagePublishingStatus.PUBLISHED,
656
pocket=PackagePublishingPocket.RELEASE)
657
dominator = self.makeDominator([spph])
658
self.assertContentEqual(
660
dominator.findPublishedSourcePackageNames(
661
spph.distroseries, PackagePublishingPocket.SECURITY))
663
def test_findPublishedSourcePackageNames_does_not_return_duplicates(self):
664
series = self.factory.makeDistroSeries()
665
pocket = PackagePublishingPocket.RELEASE
666
package = self.factory.makeSourcePackageName()
668
self.factory.makeSourcePackagePublishingHistory(
669
distroseries=series, archive=series.main_archive,
670
pocket=pocket, status=PackagePublishingStatus.PUBLISHED,
671
sourcepackagerelease=self.factory.makeSourcePackageRelease(
672
sourcepackagename=package))
673
for counter in xrange(2)]
674
dominator = self.makeDominator(spphs)
677
list(dominator.findPublishedSourcePackageNames(series, pocket)))
679
def test_findPublishedSPPHs_finds_published_SPPH(self):
680
spph = self.factory.makeSourcePackagePublishingHistory(
681
status=PackagePublishingStatus.PUBLISHED)
682
package_name = spph.sourcepackagerelease.sourcepackagename.name
683
dominator = self.makeDominator([spph])
684
self.assertContentEqual(
686
dominator.findPublishedSPPHs(
687
spph.distroseries, spph.pocket, package_name))
689
def test_findPublishedSPPHs_ignores_other_states(self):
690
series = self.factory.makeDistroSeries()
691
package = self.factory.makeSourcePackageName()
692
pocket = PackagePublishingPocket.RELEASE
694
(status, self.factory.makeSourcePackagePublishingHistory(
695
distroseries=series, archive=series.main_archive,
696
pocket=pocket, status=status,
697
sourcepackagerelease=self.factory.makeSourcePackageRelease(
698
sourcepackagename=package)))
699
for status in PackagePublishingStatus.items)
700
dominator = self.makeDominator(spphs.values())
701
self.assertContentEqual(
702
[spphs[PackagePublishingStatus.PUBLISHED]],
703
dominator.findPublishedSPPHs(series, pocket, package.name))
705
def test_findPublishedSPPHs_ignores_other_archives(self):
706
spph = self.factory.makeSourcePackagePublishingHistory(
707
status=PackagePublishingStatus.PUBLISHED)
708
package = spph.sourcepackagerelease.sourcepackagename
709
dominator = self.makeDominator([spph])
710
dominator.archive = self.factory.makeArchive()
711
self.assertContentEqual(
713
dominator.findPublishedSPPHs(
714
spph.distroseries, spph.pocket, package.name))
716
def test_findPublishedSPPHs_ignores_other_series(self):
717
spph = self.factory.makeSourcePackagePublishingHistory(
718
status=PackagePublishingStatus.PUBLISHED)
719
distro = spph.distroseries.distribution
720
package = spph.sourcepackagerelease.sourcepackagename
721
other_series = self.factory.makeDistroSeries(distribution=distro)
722
dominator = self.makeDominator([spph])
723
self.assertContentEqual(
725
dominator.findPublishedSPPHs(
726
other_series, spph.pocket, package.name))
728
def test_findPublishedSPPHs_ignores_other_pockets(self):
729
spph = self.factory.makeSourcePackagePublishingHistory(
730
status=PackagePublishingStatus.PUBLISHED,
731
pocket=PackagePublishingPocket.RELEASE)
732
package = spph.sourcepackagerelease.sourcepackagename
733
dominator = self.makeDominator([spph])
734
self.assertContentEqual(
736
dominator.findPublishedSPPHs(
737
spph.distroseries, PackagePublishingPocket.SECURITY,
740
def test_findPublishedSPPHs_ignores_other_packages(self):
741
spph = self.factory.makeSourcePackagePublishingHistory(
742
status=PackagePublishingStatus.PUBLISHED)
743
other_package = self.factory.makeSourcePackageName()
744
dominator = self.makeDominator([spph])
745
self.assertContentEqual(
747
dominator.findPublishedSPPHs(
748
spph.distroseries, spph.pocket, other_package.name))