~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/archivepublisher/tests/test_dominator.py

Merged db-devel into replication.

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
from canonical.database.sqlbase import flush_database_updates
16
16
from canonical.testing.layers import ZopelessDatabaseLayer
17
17
from lp.archivepublisher.domination import (
 
18
    ArchSpecificPublicationsCache,
 
19
    contains_arch_indep,
18
20
    Dominator,
 
21
    find_live_binary_versions_pass_1,
 
22
    find_live_binary_versions_pass_2,
 
23
    find_live_source_versions,
19
24
    GeneralizedPublication,
20
25
    STAY_OF_EXECUTION,
21
26
    )
30
35
    StormStatementRecorder,
31
36
    TestCaseWithFactory,
32
37
    )
 
38
from lp.testing.fakemethod import FakeMethod
33
39
from lp.testing.matchers import HasQueryCount
34
40
 
35
41
 
72
78
            is_source=ISourcePackagePublishingHistory.providedBy(dominant))
73
79
        dominator = Dominator(self.logger, self.ubuntutest.main_archive)
74
80
 
75
 
        # The _dominate* test methods require a dictionary where the
76
 
        # package name is the key. The key's value is a list of
77
 
        # source or binary packages representing dominant, the first element
78
 
        # and dominated, the subsequents.
79
 
        pubs = {'foo': [dominant, dominated]}
80
 
 
81
 
        dominator._dominatePublications(pubs, generalization)
 
81
        pubs = [dominant, dominated]
 
82
        live_versions = [generalization.getPackageVersion(dominant)]
 
83
        dominator.dominatePackage(pubs, live_versions, generalization)
82
84
        flush_database_updates()
83
85
 
84
86
        # The dominant version remains correctly published.
158
160
            [foo_10_source] + foo_10_binaries,
159
161
            PackagePublishingStatus.SUPERSEDED)
160
162
 
161
 
    def testEmptyDomination(self):
162
 
        """Domination asserts for not empty input list."""
163
 
        dominator = Dominator(self.logger, self.ubuntutest.main_archive)
164
 
        pubs = {'foo': []}
165
 
        # This isn't a really good exception. It should probably be
166
 
        # something more indicative of bad input.
167
 
        self.assertRaises(
168
 
            AssertionError,
169
 
            dominator._dominatePublications,
170
 
            pubs, GeneralizedPublication(True))
 
163
    def test_dominateBinaries_rejects_empty_publication_list(self):
 
164
        """Domination asserts for non-empty input list."""
 
165
        package = self.factory.makeBinaryPackageName()
 
166
        dominator = Dominator(self.logger, self.ubuntutest.main_archive)
 
167
        dominator._sortPackages = FakeMethod({package.name: []})
 
168
        # This isn't a really good exception. It should probably be
 
169
        # something more indicative of bad input.
 
170
        self.assertRaises(
 
171
            AssertionError,
 
172
            dominator.dominateBinaries,
 
173
            self.factory.makeDistroArchSeries().distroseries,
 
174
            self.factory.getAnyPocket())
 
175
 
 
176
    def test_dominateSources_rejects_empty_publication_list(self):
 
177
        """Domination asserts for non-empty input list."""
 
178
        package = self.factory.makeSourcePackageName()
 
179
        dominator = Dominator(self.logger, self.ubuntutest.main_archive)
 
180
        dominator._sortPackages = FakeMethod({package.name: []})
 
181
        # This isn't a really good exception. It should probably be
 
182
        # something more indicative of bad input.
 
183
        self.assertRaises(
 
184
            AssertionError,
 
185
            dominator.dominateSources,
 
186
            self.factory.makeDistroSeries(), self.factory.getAnyPocket())
 
187
 
 
188
    def test_archall_domination(self):
 
189
        # Arch-all binaries should not be dominated when a new source
 
190
        # version builds an updated arch-all binary, because slower builds
 
191
        # of other architectures will leave the previous version
 
192
        # uninstallable if they depend on the arch-all binary.
 
193
        # See https://bugs.launchpad.net/launchpad/+bug/34086
 
194
 
 
195
        # Set up a source, "foo" which builds "foo-bin" and foo-common
 
196
        # (which is arch-all).
 
197
        foo_10_src = self.getPubSource(
 
198
            sourcename="foo", version="1.0", architecturehintlist="i386",
 
199
            status=PackagePublishingStatus.PUBLISHED)
 
200
        [foo_10_i386_bin] = self.getPubBinaries(
 
201
            binaryname="foo-bin", status=PackagePublishingStatus.PUBLISHED,
 
202
            architecturespecific=True, version="1.0", pub_source=foo_10_src)
 
203
        [build] = foo_10_src.getBuilds()
 
204
        bpr = self.factory.makeBinaryPackageRelease(
 
205
            binarypackagename="foo-common", version="1.0", build=build,
 
206
            architecturespecific=False)
 
207
        foo_10_all_bins = self.publishBinaryInArchive(
 
208
            bpr, self.ubuntutest.main_archive, pocket=foo_10_src.pocket,
 
209
            status=PackagePublishingStatus.PUBLISHED)
 
210
 
 
211
        # Now, make version 1.1 of foo and add a foo-common but not foo-bin
 
212
        # (imagine that it's not finished building yet).
 
213
        foo_11_src = self.getPubSource(
 
214
            sourcename="foo", version="1.1", architecturehintlist="all",
 
215
            status=PackagePublishingStatus.PUBLISHED)
 
216
        # Generate binary publications for architecture "all" (actually,
 
217
        # one such publication per architecture).
 
218
        self.getPubBinaries(
 
219
            binaryname="foo-common", status=PackagePublishingStatus.PUBLISHED,
 
220
            architecturespecific=False, version="1.1", pub_source=foo_11_src)
 
221
 
 
222
        dominator = Dominator(self.logger, self.ubuntutest.main_archive)
 
223
        dominator.judgeAndDominate(
 
224
            foo_10_src.distroseries, foo_10_src.pocket)
 
225
 
 
226
        # The source will be superseded.
 
227
        self.checkPublication(foo_10_src, PackagePublishingStatus.SUPERSEDED)
 
228
        # The arch-specific has no dominant, so it's still published
 
229
        self.checkPublication(
 
230
            foo_10_i386_bin, PackagePublishingStatus.PUBLISHED)
 
231
        # The arch-indep has a dominant but must not be superseded yet
 
232
        # since the arch-specific is still published.
 
233
        self.checkPublications(
 
234
            foo_10_all_bins, PackagePublishingStatus.PUBLISHED)
 
235
 
 
236
        # Now creating a newer foo-bin should see those last two
 
237
        # publications superseded.
 
238
        [build2] = foo_11_src.getBuilds()
 
239
        foo_11_bin = self.factory.makeBinaryPackageRelease(
 
240
            binarypackagename="foo-bin", version="1.1", build=build2,
 
241
            architecturespecific=True)
 
242
        self.publishBinaryInArchive(
 
243
            foo_11_bin, self.ubuntutest.main_archive,
 
244
            pocket=foo_10_src.pocket,
 
245
            status=PackagePublishingStatus.PUBLISHED)
 
246
        dominator.judgeAndDominate(
 
247
            foo_10_src.distroseries, foo_10_src.pocket)
 
248
        self.checkPublication(
 
249
            foo_10_i386_bin, PackagePublishingStatus.SUPERSEDED)
 
250
        self.checkPublications(
 
251
            foo_10_all_bins, PackagePublishingStatus.SUPERSEDED)
 
252
 
 
253
    def test_any_superseded_by_all(self):
 
254
        # Set up a source, foo, which builds an architecture-dependent
 
255
        # binary, foo-bin.
 
256
        foo_10_src = self.getPubSource(
 
257
            sourcename="foo", version="1.0", architecturehintlist="i386",
 
258
            status=PackagePublishingStatus.PUBLISHED)
 
259
        [foo_10_i386_bin] = self.getPubBinaries(
 
260
            binaryname="foo-bin", status=PackagePublishingStatus.PUBLISHED,
 
261
            architecturespecific=True, version="1.0", pub_source=foo_10_src)
 
262
 
 
263
        # Now, make version 1.1 of foo, where foo-bin is now
 
264
        # architecture-independent.
 
265
        foo_11_src = self.getPubSource(
 
266
            sourcename="foo", version="1.1", architecturehintlist="all",
 
267
            status=PackagePublishingStatus.PUBLISHED)
 
268
        [foo_10_all_bin, foo_10_all_bin_2] = self.getPubBinaries(
 
269
            binaryname="foo-bin", status=PackagePublishingStatus.PUBLISHED,
 
270
            architecturespecific=False, version="1.1", pub_source=foo_11_src)
 
271
 
 
272
        dominator = Dominator(self.logger, self.ubuntutest.main_archive)
 
273
        dominator.judgeAndDominate(
 
274
            foo_10_src.distroseries, foo_10_src.pocket)
 
275
 
 
276
        # The source will be superseded.
 
277
        self.checkPublication(foo_10_src, PackagePublishingStatus.SUPERSEDED)
 
278
        # The arch-specific is superseded by the new arch-indep.
 
279
        self.checkPublication(
 
280
            foo_10_i386_bin, PackagePublishingStatus.SUPERSEDED)
 
281
 
 
282
    def test_schitzoid_package(self):
 
283
        # Test domination of a source that produces an arch-indep and an
 
284
        # arch-all, that then switches both on the next version to the
 
285
        # other arch type.
 
286
        foo_10_src = self.getPubSource(
 
287
            sourcename="foo", version="1.0", architecturehintlist="i386",
 
288
            status=PackagePublishingStatus.PUBLISHED)
 
289
        [foo_10_i386_bin] = self.getPubBinaries(
 
290
            binaryname="foo-bin", status=PackagePublishingStatus.PUBLISHED,
 
291
            architecturespecific=True, version="1.0", pub_source=foo_10_src)
 
292
        [build] = foo_10_src.getBuilds()
 
293
        bpr = self.factory.makeBinaryPackageRelease(
 
294
            binarypackagename="foo-common", version="1.0", build=build,
 
295
            architecturespecific=False)
 
296
        foo_10_all_bins = self.publishBinaryInArchive(
 
297
            bpr, self.ubuntutest.main_archive, pocket=foo_10_src.pocket,
 
298
            status=PackagePublishingStatus.PUBLISHED)
 
299
 
 
300
        foo_11_src = self.getPubSource(
 
301
            sourcename="foo", version="1.1", architecturehintlist="i386",
 
302
            status=PackagePublishingStatus.PUBLISHED)
 
303
        [foo_11_i386_bin] = self.getPubBinaries(
 
304
            binaryname="foo-common", status=PackagePublishingStatus.PUBLISHED,
 
305
            architecturespecific=True, version="1.1", pub_source=foo_11_src)
 
306
        [build] = foo_11_src.getBuilds()
 
307
        bpr = self.factory.makeBinaryPackageRelease(
 
308
            binarypackagename="foo-bin", version="1.1", build=build,
 
309
            architecturespecific=False)
 
310
        # Generate binary publications for architecture "all" (actually,
 
311
        # one such publication per architecture).
 
312
        self.publishBinaryInArchive(
 
313
            bpr, self.ubuntutest.main_archive, pocket=foo_11_src.pocket,
 
314
            status=PackagePublishingStatus.PUBLISHED)
 
315
 
 
316
        dominator = Dominator(self.logger, self.ubuntutest.main_archive)
 
317
        dominator.judgeAndDominate(foo_10_src.distroseries, foo_10_src.pocket)
 
318
 
 
319
        self.checkPublications(foo_10_all_bins + [foo_10_i386_bin],
 
320
                               PackagePublishingStatus.SUPERSEDED)
171
321
 
172
322
 
173
323
class TestDomination(TestNativePublishingBase):
224
374
            SeriesStatus.OBSOLETE)
225
375
 
226
376
 
 
377
def remove_security_proxies(proxied_objects):
 
378
    """Return list of `proxied_objects`, without their proxies.
 
379
 
 
380
    The dominator runs only in scripts, where security proxies don't get
 
381
    in the way.  To test realistically for this environment, strip the
 
382
    proxies wherever necessary and do as you will.
 
383
    """
 
384
    return [removeSecurityProxy(obj) for obj in proxied_objects]
 
385
 
 
386
 
227
387
def make_spphs_for_versions(factory, versions):
228
388
    """Create publication records for each of `versions`.
229
389
 
230
 
    They records are created in the same order in which they are specified.
 
390
    All these publications will be in the same source package, archive,
 
391
    distroseries, and pocket.  They will all be in Published status.
 
392
 
 
393
    The records are created in the same order in which they are specified.
231
394
    Make the order irregular to prove that version ordering is not a
232
395
    coincidence of object creation order etc.
233
396
 
237
400
    spn = factory.makeSourcePackageName()
238
401
    distroseries = factory.makeDistroSeries()
239
402
    pocket = factory.getAnyPocket()
 
403
    archive = distroseries.main_archive
240
404
    sprs = [
241
405
        factory.makeSourcePackageRelease(
242
406
            sourcepackagename=spn, version=version)
244
408
    return [
245
409
        factory.makeSourcePackagePublishingHistory(
246
410
            distroseries=distroseries, pocket=pocket,
247
 
            sourcepackagerelease=spr,
 
411
            sourcepackagerelease=spr, archive=archive,
248
412
            status=PackagePublishingStatus.PUBLISHED)
249
413
        for spr in sprs]
250
414
 
251
415
 
 
416
def make_bpphs_for_versions(factory, versions):
 
417
    """Create publication records for each of `versions`.
 
418
 
 
419
    All these publications will be in the same binary package, source
 
420
    package, archive, distroarchseries, and pocket.  They will all be in
 
421
    Published status.
 
422
    """
 
423
    bpn = factory.makeBinaryPackageName()
 
424
    spn = factory.makeSourcePackageName()
 
425
    das = factory.makeDistroArchSeries()
 
426
    archive = das.distroseries.main_archive
 
427
    pocket = factory.getAnyPocket()
 
428
    bprs = [
 
429
        factory.makeBinaryPackageRelease(
 
430
            binarypackagename=bpn, version=version)
 
431
        for version in versions]
 
432
    return remove_security_proxies([
 
433
        factory.makeBinaryPackagePublishingHistory(
 
434
            binarypackagerelease=bpr, binarypackagename=bpn,
 
435
            distroarchseries=das, pocket=pocket, archive=archive,
 
436
            sourcepackagename=spn, status=PackagePublishingStatus.PUBLISHED)
 
437
        for bpr in bprs])
 
438
 
 
439
 
252
440
def list_source_versions(spphs):
253
441
    """Extract the versions from `spphs` as a list, in the same order."""
254
442
    return [spph.sourcepackagerelease.version for spph in spphs]
430
618
    def test_dominatePackage_supersedes_older_pub_with_newer_live_pub(self):
431
619
        # When marking a package as superseded, dominatePackage
432
620
        # designates a newer live version as the superseding version.
 
621
        generalization = GeneralizedPublication(True)
433
622
        pubs = make_spphs_for_versions(self.factory, ['1.0', '1.1'])
434
623
        self.makeDominator(pubs).dominatePackage(
435
 
            pubs, ['1.1'], GeneralizedPublication(True))
 
624
            generalization.sortPublications(pubs), ['1.1'], generalization)
436
625
        self.assertEqual(PackagePublishingStatus.SUPERSEDED, pubs[0].status)
437
626
        self.assertEqual(pubs[1].sourcepackagerelease, pubs[0].supersededby)
438
627
        self.assertEqual(PackagePublishingStatus.PUBLISHED, pubs[1].status)
440
629
    def test_dominatePackage_only_supersedes_with_live_pub(self):
441
630
        # When marking a package as superseded, dominatePackage will
442
631
        # only pick a live version as the superseding one.
 
632
        generalization = GeneralizedPublication(True)
443
633
        pubs = make_spphs_for_versions(
444
634
            self.factory, ['1.0', '2.0', '3.0', '4.0'])
445
635
        self.makeDominator(pubs).dominatePackage(
446
 
            pubs, ['3.0'], GeneralizedPublication(True))
 
636
            generalization.sortPublications(pubs), ['3.0'], generalization)
447
637
        self.assertEqual([
448
638
                pubs[2].sourcepackagerelease,
449
639
                pubs[2].sourcepackagerelease,
455
645
    def test_dominatePackage_supersedes_with_oldest_newer_live_pub(self):
456
646
        # When marking a package as superseded, dominatePackage picks
457
647
        # the oldest of the newer, live versions as the superseding one.
 
648
        generalization = GeneralizedPublication(True)
458
649
        pubs = make_spphs_for_versions(self.factory, ['2.7', '2.8', '2.9'])
459
650
        self.makeDominator(pubs).dominatePackage(
460
 
            pubs, ['2.8', '2.9'], GeneralizedPublication(True))
 
651
            generalization.sortPublications(pubs), ['2.8', '2.9'],
 
652
            generalization)
461
653
        self.assertEqual(pubs[1].sourcepackagerelease, pubs[0].supersededby)
462
654
 
463
655
    def test_dominatePackage_only_supersedes_with_newer_live_pub(self):
464
656
        # When marking a package as superseded, dominatePackage only
465
657
        # considers a newer version as the superseding one.
 
658
        generalization = GeneralizedPublication(True)
466
659
        pubs = make_spphs_for_versions(self.factory, ['0.1', '0.2'])
467
660
        self.makeDominator(pubs).dominatePackage(
468
 
            pubs, ['0.1'], GeneralizedPublication(True))
 
661
            generalization.sortPublications(pubs), ['0.1'], generalization)
469
662
        self.assertEqual(None, pubs[1].supersededby)
470
663
        self.assertEqual(PackagePublishingStatus.DELETED, pubs[1].status)
471
664
 
472
665
    def test_dominatePackage_supersedes_replaced_pub_for_live_version(self):
473
666
        # Even if a publication record is for a live version, a newer
474
667
        # one for the same version supersedes it.
 
668
        generalization = GeneralizedPublication(True)
475
669
        spr = self.factory.makeSourcePackageRelease()
476
670
        series = self.factory.makeDistroSeries()
477
671
        pocket = PackagePublishingPocket.RELEASE
488
682
            ])
489
683
 
490
684
        self.makeDominator(pubs).dominatePackage(
491
 
            pubs, [spr.version], GeneralizedPublication(True))
 
685
            generalization.sortPublications(pubs), [spr.version],
 
686
            generalization)
492
687
        self.assertEqual([
493
688
            PackagePublishingStatus.SUPERSEDED,
494
689
            PackagePublishingStatus.SUPERSEDED,
500
695
 
501
696
    def test_dominatePackage_is_efficient(self):
502
697
        # dominatePackage avoids issuing too many queries.
 
698
        generalization = GeneralizedPublication(True)
503
699
        versions = ["1.%s" % revision for revision in xrange(5)]
504
700
        pubs = make_spphs_for_versions(self.factory, versions)
505
701
        with StormStatementRecorder() as recorder:
506
702
            self.makeDominator(pubs).dominatePackage(
507
 
                pubs, versions[2:-1],
508
 
                GeneralizedPublication(True))
 
703
                generalization.sortPublications(pubs), versions[2:-1],
 
704
                generalization)
509
705
        self.assertThat(recorder, HasQueryCount(LessThan(5)))
510
706
 
511
707
    def test_dominatePackage_advanced_scenario(self):
516
712
        # don't just patch up the code or this test.  Create unit tests
517
713
        # that specifically cover the difference, then change the code
518
714
        # and/or adapt this test to return to harmony.
 
715
        generalization = GeneralizedPublication(True)
519
716
        series = self.factory.makeDistroSeries()
520
717
        package = self.factory.makeSourcePackageName()
521
718
        pocket = PackagePublishingPocket.RELEASE
562
759
 
563
760
        all_pubs = sum(pubs_by_version.itervalues(), [])
564
761
        Dominator(DevNullLogger(), series.main_archive).dominatePackage(
565
 
            all_pubs, live_versions, GeneralizedPublication(True))
 
762
            generalization.sortPublications(all_pubs), live_versions,
 
763
            generalization)
566
764
 
567
765
        for version in reversed(versions):
568
766
            pubs = pubs_by_version[version]
787
985
            [],
788
986
            dominator.findPublishedSPPHs(
789
987
                spph.distroseries, spph.pocket, other_package.name))
 
988
 
 
989
    def test_findBinariesForDomination_finds_published_publications(self):
 
990
        bpphs = make_bpphs_for_versions(self.factory, ['1.0', '1.1'])
 
991
        dominator = self.makeDominator(bpphs)
 
992
        self.assertContentEqual(
 
993
            bpphs, dominator.findBinariesForDomination(
 
994
                bpphs[0].distroarchseries, bpphs[0].pocket))
 
995
 
 
996
    def test_findBinariesForDomination_skips_single_pub_packages(self):
 
997
        # The domination algorithm that uses findBinariesForDomination
 
998
        # always keeps the latest version live.  Thus, a single
 
999
        # publication isn't worth dominating.  findBinariesForDomination
 
1000
        # won't return it.
 
1001
        bpphs = make_bpphs_for_versions(self.factory, ['1.0'])
 
1002
        dominator = self.makeDominator(bpphs)
 
1003
        self.assertContentEqual(
 
1004
            [], dominator.findBinariesForDomination(
 
1005
                bpphs[0].distroarchseries, bpphs[0].pocket))
 
1006
 
 
1007
    def test_findBinariesForDomination_ignores_other_distroseries(self):
 
1008
        bpphs = make_bpphs_for_versions(self.factory, ['1.0', '1.1'])
 
1009
        dominator = self.makeDominator(bpphs)
 
1010
        das = bpphs[0].distroarchseries
 
1011
        other_series = self.factory.makeDistroSeries(
 
1012
            distribution=das.distroseries.distribution)
 
1013
        other_das = self.factory.makeDistroArchSeries(
 
1014
            distroseries=other_series, architecturetag=das.architecturetag,
 
1015
            processorfamily=das.processorfamily)
 
1016
        self.assertContentEqual(
 
1017
            [], dominator.findBinariesForDomination(
 
1018
                other_das, bpphs[0].pocket))
 
1019
 
 
1020
    def test_findBinariesForDomination_ignores_other_architectures(self):
 
1021
        bpphs = make_bpphs_for_versions(self.factory, ['1.0', '1.1'])
 
1022
        dominator = self.makeDominator(bpphs)
 
1023
        other_das = self.factory.makeDistroArchSeries(
 
1024
            distroseries=bpphs[0].distroseries)
 
1025
        self.assertContentEqual(
 
1026
            [], dominator.findBinariesForDomination(
 
1027
                other_das, bpphs[0].pocket))
 
1028
 
 
1029
    def test_findBinariesForDomination_ignores_other_archive(self):
 
1030
        bpphs = make_bpphs_for_versions(self.factory, ['1.0', '1.1'])
 
1031
        dominator = self.makeDominator(bpphs)
 
1032
        dominator.archive = self.factory.makeArchive()
 
1033
        self.assertContentEqual(
 
1034
            [], dominator.findBinariesForDomination(
 
1035
                bpphs[0].distroarchseries, bpphs[0].pocket))
 
1036
 
 
1037
    def test_findBinariesForDomination_ignores_other_pocket(self):
 
1038
        bpphs = make_bpphs_for_versions(self.factory, ['1.0', '1.1'])
 
1039
        dominator = self.makeDominator(bpphs)
 
1040
        for bpph in bpphs:
 
1041
            removeSecurityProxy(bpph).pocket = PackagePublishingPocket.UPDATES
 
1042
        self.assertContentEqual(
 
1043
            [], dominator.findBinariesForDomination(
 
1044
                bpphs[0].distroarchseries, PackagePublishingPocket.SECURITY))
 
1045
 
 
1046
    def test_findBinariesForDomination_ignores_other_status(self):
 
1047
        # If we have one BPPH for each possible status, plus one
 
1048
        # Published one to stop findBinariesForDomination from skipping
 
1049
        # the package, findBinariesForDomination returns only the
 
1050
        # Published ones.
 
1051
        versions = [
 
1052
            '1.%d' % self.factory.getUniqueInteger()
 
1053
            for status in PackagePublishingStatus.items] + ['0.9']
 
1054
        bpphs = make_bpphs_for_versions(self.factory, versions)
 
1055
        dominator = self.makeDominator(bpphs)
 
1056
 
 
1057
        for bpph, status in zip(bpphs, PackagePublishingStatus.items):
 
1058
            bpph.status = status
 
1059
 
 
1060
        # These are the Published publications.  The other ones will all
 
1061
        # be ignored.
 
1062
        published_bpphs = [
 
1063
            bpph
 
1064
            for bpph in bpphs
 
1065
                if bpph.status == PackagePublishingStatus.PUBLISHED]
 
1066
 
 
1067
        self.assertContentEqual(
 
1068
            published_bpphs,
 
1069
            dominator.findBinariesForDomination(
 
1070
                bpphs[0].distroarchseries, bpphs[0].pocket))
 
1071
 
 
1072
    def test_findSourcesForDomination_finds_published_publications(self):
 
1073
        spphs = make_spphs_for_versions(self.factory, ['2.0', '2.1'])
 
1074
        dominator = self.makeDominator(spphs)
 
1075
        self.assertContentEqual(
 
1076
            spphs, dominator.findSourcesForDomination(
 
1077
                spphs[0].distroseries, spphs[0].pocket))
 
1078
 
 
1079
    def test_findSourcesForDomination_skips_single_pub_packages(self):
 
1080
        # The domination algorithm that uses findSourcesForDomination
 
1081
        # always keeps the latest version live.  Thus, a single
 
1082
        # publication isn't worth dominating.  findSourcesForDomination
 
1083
        # won't return it.
 
1084
        spphs = make_spphs_for_versions(self.factory, ['2.0'])
 
1085
        dominator = self.makeDominator(spphs)
 
1086
        self.assertContentEqual(
 
1087
            [], dominator.findSourcesForDomination(
 
1088
                spphs[0].distroseries, spphs[0].pocket))
 
1089
 
 
1090
    def test_findSourcesForDomination_ignores_other_distroseries(self):
 
1091
        spphs = make_spphs_for_versions(self.factory, ['2.0', '2.1'])
 
1092
        dominator = self.makeDominator(spphs)
 
1093
        other_series = self.factory.makeDistroSeries(
 
1094
            distribution=spphs[0].distroseries.distribution)
 
1095
        self.assertContentEqual(
 
1096
            [], dominator.findSourcesForDomination(
 
1097
                other_series, spphs[0].pocket))
 
1098
 
 
1099
    def test_findSourcesForDomination_ignores_other_pocket(self):
 
1100
        spphs = make_spphs_for_versions(self.factory, ['2.0', '2.1'])
 
1101
        dominator = self.makeDominator(spphs)
 
1102
        for spph in spphs:
 
1103
            removeSecurityProxy(spph).pocket = PackagePublishingPocket.UPDATES
 
1104
        self.assertContentEqual(
 
1105
            [], dominator.findSourcesForDomination(
 
1106
                spphs[0].distroseries, PackagePublishingPocket.SECURITY))
 
1107
 
 
1108
    def test_findSourcesForDomination_ignores_other_status(self):
 
1109
        versions = [
 
1110
            '1.%d' % self.factory.getUniqueInteger()
 
1111
            for status in PackagePublishingStatus.items] + ['0.9']
 
1112
        spphs = make_spphs_for_versions(self.factory, versions)
 
1113
        dominator = self.makeDominator(spphs)
 
1114
 
 
1115
        for spph, status in zip(spphs, PackagePublishingStatus.items):
 
1116
            spph.status = status
 
1117
 
 
1118
        # These are the Published publications.  The other ones will all
 
1119
        # be ignored.
 
1120
        published_spphs = [
 
1121
            spph
 
1122
            for spph in spphs
 
1123
                if spph.status == PackagePublishingStatus.PUBLISHED]
 
1124
 
 
1125
        self.assertContentEqual(
 
1126
            published_spphs,
 
1127
            dominator.findSourcesForDomination(
 
1128
                spphs[0].distroseries, spphs[0].pocket))
 
1129
 
 
1130
 
 
1131
def make_publications_arch_specific(pubs, arch_specific=True):
 
1132
    """Set the `architecturespecific` attribute for given SPPHs.
 
1133
 
 
1134
    :param pubs: An iterable of `BinaryPackagePublishingHistory`.
 
1135
    :param arch_specific: Whether the binary package releases published
 
1136
        by `pubs` are to be architecture-specific.  If not, they will be
 
1137
        treated as being for the "all" architecture.
 
1138
    """
 
1139
    for pub in pubs:
 
1140
        bpr = removeSecurityProxy(pub).binarypackagerelease
 
1141
        bpr.architecturespecific = arch_specific
 
1142
 
 
1143
 
 
1144
class TestLivenessFunctions(TestCaseWithFactory):
 
1145
    """Tests for the functions that say which versions are live."""
 
1146
 
 
1147
    layer = ZopelessDatabaseLayer
 
1148
 
 
1149
    def test_find_live_source_versions_blesses_latest(self):
 
1150
        # find_live_source_versions, assuming that you passed it
 
1151
        # publications sorted from most current to least current
 
1152
        # version, simply returns the most current version.
 
1153
        spphs = make_spphs_for_versions(self.factory, ['1.2', '1.1', '1.0'])
 
1154
        self.assertEqual(['1.2'], find_live_source_versions(spphs))
 
1155
 
 
1156
    def test_find_live_binary_versions_pass_1_blesses_latest(self):
 
1157
        # find_live_binary_versions_pass_1 always includes the latest
 
1158
        # version among the input publications in its result.
 
1159
        bpphs = make_bpphs_for_versions(self.factory, ['1.2', '1.1', '1.0'])
 
1160
        make_publications_arch_specific(bpphs)
 
1161
        self.assertEqual(['1.2'], find_live_binary_versions_pass_1(bpphs))
 
1162
 
 
1163
    def test_find_live_binary_versions_pass_1_blesses_arch_all(self):
 
1164
        # find_live_binary_versions_pass_1 includes any
 
1165
        # architecture-independent publications among the input in its
 
1166
        # result.
 
1167
        versions = list(reversed(['1.%d' % version for version in range(3)]))
 
1168
        bpphs = make_bpphs_for_versions(self.factory, versions)
 
1169
 
 
1170
        # All of these publications are architecture-specific, except
 
1171
        # the last one.  This would happen if the binary package had
 
1172
        # just changed from being architecture-specific to being
 
1173
        # architecture-independent.
 
1174
        make_publications_arch_specific(bpphs, True)
 
1175
        make_publications_arch_specific(bpphs[-1:], False)
 
1176
        self.assertEqual(
 
1177
            versions[:1] + versions[-1:],
 
1178
            find_live_binary_versions_pass_1(bpphs))
 
1179
 
 
1180
    def test_find_live_binary_versions_pass_2_blesses_latest(self):
 
1181
        # find_live_binary_versions_pass_2 always includes the latest
 
1182
        # version among the input publications in its result.
 
1183
        bpphs = make_bpphs_for_versions(self.factory, ['1.2', '1.1', '1.0'])
 
1184
        make_publications_arch_specific(bpphs, False)
 
1185
        cache = ArchSpecificPublicationsCache()
 
1186
        self.assertEqual(
 
1187
            ['1.2'], find_live_binary_versions_pass_2(bpphs, cache))
 
1188
 
 
1189
    def test_find_live_binary_versions_pass_2_blesses_arch_specific(self):
 
1190
        # find_live_binary_versions_pass_2 includes any
 
1191
        # architecture-specific publications among the input in its
 
1192
        # result.
 
1193
        versions = list(reversed(['1.%d' % version for version in range(3)]))
 
1194
        bpphs = make_bpphs_for_versions(self.factory, versions)
 
1195
        make_publications_arch_specific(bpphs)
 
1196
        cache = ArchSpecificPublicationsCache()
 
1197
        self.assertEqual(
 
1198
            versions, find_live_binary_versions_pass_2(bpphs, cache))
 
1199
 
 
1200
    def test_find_live_binary_versions_pass_2_reprieves_arch_all(self):
 
1201
        # An arch-all BPPH for a BPR built by an SPR that also still has
 
1202
        # active arch-dependent BPPHs gets a reprieve: it can't be
 
1203
        # superseded until those arch-dependent BPPHs have been
 
1204
        # superseded.
 
1205
        bpphs = make_bpphs_for_versions(self.factory, ['1.2', '1.1', '1.0'])
 
1206
        make_publications_arch_specific(bpphs, False)
 
1207
        dependent = self.factory.makeBinaryPackagePublishingHistory(
 
1208
            binarypackagerelease=bpphs[1].binarypackagerelease)
 
1209
        make_publications_arch_specific([dependent], True)
 
1210
        cache = ArchSpecificPublicationsCache()
 
1211
        self.assertEqual(
 
1212
            ['1.2', '1.1'], find_live_binary_versions_pass_2(bpphs, cache))
 
1213
 
 
1214
 
 
1215
class TestDominationHelpers(TestCaseWithFactory):
 
1216
    """Test lightweight helpers for the `Dominator`."""
 
1217
 
 
1218
    layer = ZopelessDatabaseLayer
 
1219
 
 
1220
    def test_contains_arch_indep_says_True_for_arch_indep(self):
 
1221
        bpphs = [self.factory.makeBinaryPackagePublishingHistory()]
 
1222
        make_publications_arch_specific(bpphs, False)
 
1223
        self.assertTrue(contains_arch_indep(bpphs))
 
1224
 
 
1225
    def test_contains_arch_indep_says_False_for_arch_specific(self):
 
1226
        bpphs = [self.factory.makeBinaryPackagePublishingHistory()]
 
1227
        make_publications_arch_specific(bpphs, True)
 
1228
        self.assertFalse(contains_arch_indep(bpphs))
 
1229
 
 
1230
    def test_contains_arch_indep_says_True_for_combination(self):
 
1231
        bpphs = make_bpphs_for_versions(self.factory, ['1.1', '1.0'])
 
1232
        make_publications_arch_specific(bpphs[:1], True)
 
1233
        make_publications_arch_specific(bpphs[1:], False)
 
1234
        self.assertTrue(contains_arch_indep(bpphs))
 
1235
 
 
1236
    def test_contains_arch_indep_says_False_for_empty_list(self):
 
1237
        self.assertFalse(contains_arch_indep([]))
 
1238
 
 
1239
 
 
1240
class TestArchSpecificPublicationsCache(TestCaseWithFactory):
 
1241
    """Tests for `ArchSpecificPublicationsCache`."""
 
1242
 
 
1243
    layer = ZopelessDatabaseLayer
 
1244
 
 
1245
    def makeCache(self):
 
1246
        """Shorthand: create a ArchSpecificPublicationsCache."""
 
1247
        return ArchSpecificPublicationsCache()
 
1248
 
 
1249
    def makeSPR(self):
 
1250
        """Create a `BinaryPackageRelease`."""
 
1251
        # Return an un-proxied SPR.  This is script code, so it won't be
 
1252
        # running into them in real life.
 
1253
        return removeSecurityProxy(self.factory.makeSourcePackageRelease())
 
1254
 
 
1255
    def makeBPPH(self, spr=None, arch_specific=True, archive=None,
 
1256
                 distroseries=None):
 
1257
        """Create a `BinaryPackagePublishingHistory`."""
 
1258
        if spr is None:
 
1259
            spr = self.makeSPR()
 
1260
        bpb = self.factory.makeBinaryPackageBuild(source_package_release=spr)
 
1261
        bpr = self.factory.makeBinaryPackageRelease(
 
1262
            build=bpb, architecturespecific=arch_specific)
 
1263
        das = self.factory.makeDistroArchSeries(distroseries=distroseries)
 
1264
        return removeSecurityProxy(
 
1265
            self.factory.makeBinaryPackagePublishingHistory(
 
1266
                binarypackagerelease=bpr, archive=archive,
 
1267
                distroarchseries=das, pocket=PackagePublishingPocket.UPDATES,
 
1268
                status=PackagePublishingStatus.PUBLISHED))
 
1269
 
 
1270
    def test_getKey_is_consistent_and_distinguishing(self):
 
1271
        # getKey consistently returns the same key for the same BPPH,
 
1272
        # but different keys for non-matching BPPHs.
 
1273
        bpphs = [
 
1274
            self.factory.makeBinaryPackagePublishingHistory()
 
1275
            for counter in range(2)]
 
1276
        cache = self.makeCache()
 
1277
        self.assertContentEqual(
 
1278
            [cache.getKey(bpph) for bpph in bpphs],
 
1279
            set(cache.getKey(bpph) for bpph in bpphs * 2))
 
1280
 
 
1281
    def test_hasArchSpecificPublications_is_consistent_and_correct(self):
 
1282
        # hasArchSpecificPublications consistently, repeatably returns
 
1283
        # the same result for the same key.  Naturally, different keys
 
1284
        # can still produce different results.
 
1285
        spr = self.makeSPR()
 
1286
        dependent = self.makeBPPH(spr, arch_specific=True)
 
1287
        bpph1 = self.makeBPPH(
 
1288
            spr, arch_specific=False, archive=dependent.archive,
 
1289
            distroseries=dependent.distroseries)
 
1290
        bpph2 = self.makeBPPH(arch_specific=False)
 
1291
        cache = self.makeCache()
 
1292
        self.assertEqual(
 
1293
            [True, True, False, False],
 
1294
            [
 
1295
                cache.hasArchSpecificPublications(bpph1),
 
1296
                cache.hasArchSpecificPublications(bpph1),
 
1297
                cache.hasArchSpecificPublications(bpph2),
 
1298
                cache.hasArchSpecificPublications(bpph2),
 
1299
            ])
 
1300
 
 
1301
    def test_hasArchSpecificPublications_caches_results(self):
 
1302
        # Results are cached, so once the presence of archive-specific
 
1303
        # publications has been looked up in the database, the query is
 
1304
        # not performed again for the same inputs.
 
1305
        spr = self.makeSPR()
 
1306
        self.makeBPPH(spr, arch_specific=True)
 
1307
        bpph = self.makeBPPH(spr, arch_specific=False)
 
1308
        cache = self.makeCache()
 
1309
        cache.hasArchSpecificPublications(bpph)
 
1310
        spr.getActiveArchSpecificPublications = FakeMethod()
 
1311
        cache.hasArchSpecificPublications(bpph)
 
1312
        self.assertEqual(0, spr.getActiveArchSpecificPublications.call_count)