Merge lp:~wgrant/launchpad/rework-dominator-tests into lp:launchpad

Proposed by William Grant
Status: Merged
Approved by: Robert Collins
Approved revision: no longer in the source branch.
Merged at revision: 11205
Proposed branch: lp:~wgrant/launchpad/rework-dominator-tests
Merge into: lp:launchpad
Prerequisite: lp:~wgrant/launchpad/refactor-_dominateBinary
Diff against target: 740 lines (+272/-342)
3 files modified
lib/lp/archivepublisher/tests/test_dominator.py (+70/-296)
lib/lp/archivepublisher/tests/test_publisher.py (+1/-0)
lib/lp/soyuz/tests/test_publishing.py (+201/-46)
To merge this branch: bzr merge lp:~wgrant/launchpad/rework-dominator-tests
Reviewer Review Type Date Requested Status
Robert Collins (community) Approve
Review via email: mp+29668@code.launchpad.net

Commit message

Replace most of the domination tests with more thorough model unit tests.

Description of the change

The prerequisite branch moved most of Dominator's logic into the model. This branch is a follow-up, replacing the coarse tests with smaller, more thorough unit tests over the model.

There is no lint.

To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

Nice.

raising AssertionError in production code sucks, but I realise you're just defining the contract. Please consider adding a 'this is a bad contract' comment there and in the place raising it - otherwise future programmers may believe that the current behaviour is your /intent/.

review: Approve
Revision history for this message
William Grant (wgrant) wrote :

What's wrong with raising it? It's not valid to call the method with an empty list -- there's no dominant publication.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/archivepublisher/tests/test_dominator.py'
2--- lib/lp/archivepublisher/tests/test_dominator.py 2010-07-21 09:41:48 +0000
3+++ lib/lp/archivepublisher/tests/test_dominator.py 2010-07-22 12:03:10 +0000
4@@ -6,17 +6,11 @@
5 __metaclass__ = type
6
7 import datetime
8-import pytz
9-
10-from zope.component import getUtility
11
12 from lp.archivepublisher.domination import Dominator
13 from lp.archivepublisher.publishing import Publisher
14 from canonical.database.sqlbase import flush_database_updates
15 from lp.registry.interfaces.series import SeriesStatus
16-from lp.soyuz.interfaces.component import IComponentSet
17-from lp.registry.interfaces.person import IPersonSet
18-from lp.registry.interfaces.pocket import PackagePublishingPocket
19 from lp.soyuz.interfaces.publishing import PackagePublishingStatus
20 from lp.soyuz.tests.test_publishing import TestNativePublishingBase
21
22@@ -24,6 +18,16 @@
23 class TestDominator(TestNativePublishingBase):
24 """Test Dominator class."""
25
26+ def createSourceAndBinaries(self, version):
27+ """Create a source and binaries with the given version."""
28+ source = self.getPubSource(
29+ version=version,
30+ status=PackagePublishingStatus.PUBLISHED)
31+ binaries = self.getPubBinaries(
32+ pub_source=source,
33+ status=PackagePublishingStatus.PUBLISHED)
34+ return (source, binaries)
35+
36 def createSimpleDominationContext(self):
37 """Create simple domination context.
38
39@@ -39,308 +43,78 @@
40
41 Note that as an optimization the binaries list is already unpacked.
42 """
43- foo_10_source = self.getPubSource(
44- version='1.0', architecturehintlist='i386',
45- status=PackagePublishingStatus.PUBLISHED)
46- foo_10_binaries = self.getPubBinaries(
47- pub_source=foo_10_source,
48- status=PackagePublishingStatus.PUBLISHED)
49-
50- foo_11_source = self.getPubSource(
51- version='1.1', architecturehintlist='i386',
52- status=PackagePublishingStatus.PUBLISHED)
53- foo_11_binaries = self.getPubBinaries(
54- pub_source=foo_11_source,
55- status=PackagePublishingStatus.PUBLISHED)
56-
57- dominant_source = foo_11_source
58- dominant_binaries = [pub for pub in foo_11_binaries]
59-
60- dominated_source = foo_10_source
61- dominated_binaries = [pub for pub in foo_10_binaries]
62-
63- return (dominant_source, dominant_binaries[0],
64- dominated_source, dominated_binaries[0])
65-
66- def testSourceDomination(self):
67- """Test source domination procedure."""
68+ foo_10_source, foo_10_binaries = self.createSourceAndBinaries('1.0')
69+ foo_11_source, foo_11_binaries = self.createSourceAndBinaries('1.1')
70+ return (foo_11_source, foo_11_binaries[0],
71+ foo_10_source, foo_10_binaries[0])
72+
73+ def dominateAndCheck(self, dominant, dominated, supersededby):
74 dominator = Dominator(self.logger, self.ubuntutest.main_archive)
75
76- [dominant_source, dominant_binary, dominated_source,
77- dominated_binary] = self.createSimpleDominationContext()
78-
79- # The _dominate* test methods require a dictionary where the source
80+ # The _dominate* test methods require a dictionary where the
81 # package name is the key. The key's value is a list of
82 # source or binary packages representing dominant, the first element
83 # and dominated, the subsequents.
84- source_input = {'foo': [dominant_source, dominated_source]}
85+ pubs = {'foo': [dominant, dominated]}
86
87- dominator._dominatePublications(source_input)
88+ dominator._dominatePublications(pubs)
89 flush_database_updates()
90
91 # The dominant version remains correctly published.
92- dominant = self.checkSourcePublication(
93- dominant_source, PackagePublishingStatus.PUBLISHED)
94+ self.checkPublication(dominant, PackagePublishingStatus.PUBLISHED)
95 self.assertTrue(dominant.supersededby is None)
96 self.assertTrue(dominant.datesuperseded is None)
97
98 # The dominated version is correctly dominated.
99- dominated = self.checkSourcePublication(
100- dominated_source, PackagePublishingStatus.SUPERSEDED)
101- self.assertEqual(
102- dominated.supersededby, dominant.sourcepackagerelease)
103- self.checkPastDate(dominated.datesuperseded)
104-
105- def testEmptySourceDomination(self):
106- """Source domination asserts for not empty input list."""
107- dominator = Dominator(self.logger, self.ubuntutest.main_archive)
108- source_input = {'foo': []}
109- self.assertRaises(
110- AssertionError, dominator._dominatePublications, source_input)
111-
112- def testBinariesDomination(self):
113- """Test overall binary domination procedure."""
114- dominator = Dominator(self.logger, self.ubuntutest.main_archive)
115-
116- [dominant_source, dominant, dominated_source,
117- dominated] = self.createSimpleDominationContext()
118-
119- # See comment about domination input format and ordering above.
120- binary_input = {'foo-bin': [dominant, dominated]}
121-
122- dominator._dominatePublications(binary_input)
123- flush_database_updates()
124-
125- # Dominant version remains correctly published.
126- dominant = self.checkBinaryPublication(
127- dominant, PackagePublishingStatus.PUBLISHED)
128- self.assertTrue(dominant.supersededby is None)
129- self.assertTrue(dominant.datesuperseded is None)
130-
131- # Dominated version is correctly dominated.
132- dominated = self.checkBinaryPublication(
133- dominated, PackagePublishingStatus.SUPERSEDED)
134- self.assertEqual(
135- dominated.supersededby, dominant.binarypackagerelease.build)
136- self.checkPastDate(dominated.datesuperseded)
137-
138- def testEmptyBinaryDomination(self):
139- """Binaries domination asserts not empty input list."""
140- dominator = Dominator(self.logger, self.ubuntutest.main_archive)
141- binary_input = {'foo-bin': []}
142- self.assertRaises(
143- AssertionError, dominator._dominatePublications, binary_input)
144-
145- def testBinaryDomination(self):
146- """Test binary domination unit procedure."""
147- dominator = Dominator(self.logger, self.ubuntutest.main_archive)
148-
149- [dominant_source, dominant, dominated_source,
150- dominated] = self.createSimpleDominationContext()
151-
152- dominated.supersede(dominant)
153- flush_database_updates()
154-
155- dominated = self.checkBinaryPublication(
156- dominated, PackagePublishingStatus.SUPERSEDED)
157- self.assertEqual(
158- dominated.supersededby, dominant.binarypackagerelease.build)
159- self.checkPastDate(dominated.datesuperseded)
160-
161- def testBinaryDominationAssertsPendingOrPublished(self):
162- """Test binary domination asserts coherent dominated status.
163-
164- Normally supersede() only accepts domination candidates in
165- PUBLISHED or PENDING status, a exception is opened for architecture
166- independent binaries because during the iteration they might have
167- been already SUPERSEDED with its first publication, when it happens
168- the candidate is skipped, i.e. it's not dominated again.
169-
170- (remembering the architecture independent binaries get superseded
171- atomically)
172- """
173- dominator = Dominator(self.logger, self.ubuntutest.main_archive)
174-
175- [dominant_source, dominant, dominated_source,
176- dominated] = self.createSimpleDominationContext()
177-
178- # Let's modify the domination candidate, so it will look wrong to
179- # supersede() which will raise because it's a architecture
180- # specific binary publication not in PENDING or PUBLISHED state.
181- dominated.status = PackagePublishingStatus.SUPERSEDED
182- manual_domination_date = datetime.datetime(
183- 2006, 12, 25, tzinfo=pytz.timezone("UTC"))
184- dominated.datesuperseded = manual_domination_date
185- flush_database_updates()
186-
187- # An error like that in production clearly indicates that something
188- # is wrong in the Dominator look-up methods.
189- self.assertRaises(
190- AssertionError, dominated.supersede, dominant)
191-
192- # The refused publishing record remains the same.
193- dominated = self.checkBinaryPublication(
194- dominated, PackagePublishingStatus.SUPERSEDED)
195- self.assertEqual(dominated.datesuperseded, manual_domination_date)
196-
197- # Let's make it a architecture independent binary, so the domination
198- # can be executed, but the record will be skipped.
199- dominated.binarypackagerelease.architecturespecific = False
200- flush_database_updates()
201-
202- dominated.supersede(dominant)
203- flush_database_updates()
204- dominated = self.checkBinaryPublication(
205- dominated, PackagePublishingStatus.SUPERSEDED)
206- self.assertEqual(dominated.datesuperseded, manual_domination_date)
207-
208- def testOtherBinaryPublications(self):
209- """Check the basis of architecture independent binary domination.
210-
211- We use _getOtherPublications to identify other publications of the
212- same binarypackagerelease in other architectures (architecture
213- independent binaries), they will be dominated during a single step.
214-
215- See overall details in `testDominationOfOldArchIndepBinaries`.
216- """
217- # Create architecture independent publications for foo-bin_1.0
218- # in i386 & hppa.
219- pub_source_archindep = self.getPubSource(
220- version='1.0', status=PackagePublishingStatus.PUBLISHED,
221- architecturehintlist='all')
222- pub_binaries_archindep = self.getPubBinaries(
223- pub_source=pub_source_archindep,
224- status=PackagePublishingStatus.PUBLISHED)
225- [hppa_pub, i386_pub] = pub_binaries_archindep
226-
227- # We will also copy the binary publications to a PPA archive
228- # to check if the lookup is indeed restricted to the dominated
229- # archive. See bug #237845 for further information.
230- cprov = getUtility(IPersonSet).getByName('cprov')
231- i386_pub.copyTo(
232- self.breezy_autotest,
233- PackagePublishingPocket.RELEASE,
234- cprov.archive)
235- cprov_foo_binaries = cprov.archive.getAllPublishedBinaries(name='foo')
236- self.assertEqual(cprov_foo_binaries.count(), 2)
237-
238- # Manually supersede the hppa binary.
239- hppa_pub.status = PackagePublishingStatus.SUPERSEDED
240- flush_database_updates()
241-
242- # Check if we can reach the i386 publication using
243- # _getOtherBinaryPublications over the hppa binary.
244- [found] = list(hppa_pub._getOtherPublications())
245- self.assertEqual(i386_pub, found)
246-
247- # Create architecture specific publications for foo-bin_1.1 in
248- # i386 & hppa.
249- pub_source_archdep = self.getPubSource(
250- version='1.1', status=PackagePublishingStatus.PUBLISHED,
251- architecturehintlist='any')
252- pub_binaries_archdep = self.getPubBinaries(
253- pub_source=pub_source_archdep)
254- [hppa_pub, i386_pub] = pub_binaries_archdep
255-
256- # Manually supersede the hppa publication.
257- hppa_pub.status = PackagePublishingStatus.SUPERSEDED
258- flush_database_updates()
259-
260- # Check if there is no other publication of the hppa binary package
261- # release.
262- self.assertEqual(
263- hppa_pub._getOtherPublications().count(),
264- 0)
265-
266- def testDominationOfOldArchIndepBinaries(self):
267- """Check domination of architecture independent binaries.
268-
269- When a architecture independent binary is dominated it should also
270- 'carry' the same publications in other architectures independently
271- of whether or not the new binary was successfully built to a specific
272- architecture.
273-
274- See bug #48760 for further information about this aspect.
275- """
276- publisher = Publisher(
277- self.logger, self.config, self.disk_pool,
278- self.ubuntutest.main_archive)
279-
280- # Create published archindep context.
281- pub_source_archindep = self.getPubSource(
282- version='1.0', status=PackagePublishingStatus.PUBLISHED,
283- architecturehintlist='all')
284- pub_binaries_archindep = self.getPubBinaries(
285- pub_source=pub_source_archindep,
286- status=PackagePublishingStatus.PUBLISHED)
287-
288- # Emulated new publication of a archdep binary only on i386.
289- pub_source_archdep = self.getPubSource(
290- version='1.1', architecturehintlist='i386')
291- pub_binaries_archdep = self.getPubBinaries(
292- pub_source=pub_source_archdep)
293-
294- publisher.A_publish(False)
295- publisher.B_dominate(False)
296-
297- # The latest architecture specific source and binary pair is
298- # PUBLISHED.
299+ self.checkPublication(dominated, PackagePublishingStatus.SUPERSEDED)
300+ self.assertEqual(dominated.supersededby, supersededby)
301+ self.checkPastDate(dominated.datesuperseded)
302+
303+ def testManualSourceDomination(self):
304+ """Test source domination procedure."""
305+ [dominant_source, dominant_binary, dominated_source,
306+ dominated_binary] = self.createSimpleDominationContext()
307+
308+ self.dominateAndCheck(
309+ dominant_source, dominated_source,
310+ dominant_source.sourcepackagerelease)
311+
312+ def testManualBinaryDomination(self):
313+ """Test binary domination procedure."""
314+ [dominant_source, dominant, dominated_source,
315+ dominated] = self.createSimpleDominationContext()
316+
317+ self.dominateAndCheck(
318+ dominant, dominated, dominant.binarypackagerelease.build)
319+
320+ def testJudgeAndDominate(self):
321+ """Verify that judgeAndDominate correctly dominates everything."""
322+ foo_10_source, foo_10_binaries = self.createSourceAndBinaries('1.0')
323+ foo_11_source, foo_11_binaries = self.createSourceAndBinaries('1.1')
324+ foo_12_source, foo_12_binaries = self.createSourceAndBinaries('1.2')
325+
326+ dominator = Dominator(self.logger, foo_10_source.archive)
327+ dominator.judgeAndDominate(
328+ foo_10_source.distroseries, foo_10_source.pocket, self.config)
329+
330 self.checkPublications(
331- pub_source_archdep, pub_binaries_archdep,
332+ [foo_12_source] + foo_12_binaries,
333 PackagePublishingStatus.PUBLISHED)
334-
335- # The oldest architecture independent source & binaries should
336- # be SUPERSEDED, i.e., the fact that new source version wasn't
337- # built for hppa should not hold the condemned architecture
338- # independent binary.
339- self.checkPublications(
340- pub_source_archindep, pub_binaries_archindep,
341- PackagePublishingStatus.SUPERSEDED)
342-
343- def testDominationOnArchIndependentBinaryOverrides(self):
344- """Check domination of architecture-independent overridden binaries.
345-
346- Due to the mechanism for performing atomic domination of arch-indep
347- binaries (bug #48760) we were erroneously dominating binary override
348- attempts (new pending publications of the same binary in different
349- component/section). See bug #178102 for further information.
350- """
351- publisher = Publisher(
352- self.logger, self.config, self.disk_pool,
353- self.ubuntutest.main_archive)
354-
355- # Create published archindep context.
356- pub_source = self.getPubSource(
357- version='1.0', status=PackagePublishingStatus.PUBLISHED,
358- architecturehintlist='all')
359- overridden_binaries = self.getPubBinaries(
360- pub_source=pub_source, status=PackagePublishingStatus.PUBLISHED)
361-
362- # Committing the transaction here is required to guarantee sane
363- # order in publisher queries.
364- self.layer.commit()
365-
366- # Perform the binary override.
367- universe = getUtility(IComponentSet)['universe']
368- pub_binaries = []
369- for pub in overridden_binaries:
370- pub_binaries.append(pub.changeOverride(new_component=universe))
371-
372- # Overrides are in DB.
373- self.checkBinaryPublications(
374- pub_binaries, PackagePublishingStatus.PENDING)
375-
376- # Publish and dominate them.
377- publisher.A_publish(False)
378- publisher.B_dominate(False)
379-
380- # The original binary publications are marked as SUPERSEDED and
381- # the just-create overrides as preserved as PUBLISHED.
382- self.checkBinaryPublications(
383- overridden_binaries, PackagePublishingStatus.SUPERSEDED)
384-
385- self.checkBinaryPublications(
386- pub_binaries, PackagePublishingStatus.PUBLISHED)
387+ self.checkPublications(
388+ [foo_11_source] + foo_11_binaries,
389+ PackagePublishingStatus.SUPERSEDED)
390+ self.checkPublications(
391+ [foo_10_source] + foo_10_binaries,
392+ PackagePublishingStatus.SUPERSEDED)
393+
394+ def testEmptyDomination(self):
395+ """Domination asserts for not empty input list."""
396+ dominator = Dominator(self.logger, self.ubuntutest.main_archive)
397+ pubs = {'foo': []}
398+ # This isn't a really good exception. It should probably be
399+ # something more indicative of bad input.
400+ self.assertRaises(
401+ AssertionError, dominator._dominatePublications, pubs)
402
403
404 class TestDomination(TestNativePublishingBase):
405@@ -377,15 +151,15 @@
406 # DELETED and OBSOLETED publications are set to be deleted
407 # immediately, whereas SUPERSEDED ones get a stay of execution
408 # according to the configuration.
409- deleted_source = self.checkSourcePublication(
410+ self.checkPublication(
411 deleted_source, PackagePublishingStatus.DELETED)
412 self.checkPastDate(deleted_source.scheduleddeletiondate)
413
414- obsoleted_source = self.checkSourcePublication(
415+ self.checkPublication(
416 obsoleted_source, PackagePublishingStatus.OBSOLETE)
417 self.checkPastDate(deleted_source.scheduleddeletiondate)
418
419- superseded_source = self.checkSourcePublication(
420+ self.checkPublication(
421 superseded_source, PackagePublishingStatus.SUPERSEDED)
422 self.checkPastDate(
423 superseded_source.scheduleddeletiondate,
424
425=== modified file 'lib/lp/archivepublisher/tests/test_publisher.py'
426--- lib/lp/archivepublisher/tests/test_publisher.py 2010-07-21 14:14:54 +0000
427+++ lib/lp/archivepublisher/tests/test_publisher.py 2010-07-22 12:03:10 +0000
428@@ -1113,6 +1113,7 @@
429
430 def tearDown(self):
431 """Purge the archive root location. """
432+ super(TestPublisherRepositorySignatures, self).tearDown()
433 if self.archive_publisher is not None:
434 shutil.rmtree(self.archive_publisher._config.distsroot)
435
436
437=== modified file 'lib/lp/soyuz/tests/test_publishing.py'
438--- lib/lp/soyuz/tests/test_publishing.py 2010-07-21 14:14:54 +0000
439+++ lib/lp/soyuz/tests/test_publishing.py 2010-07-22 12:03:10 +0000
440@@ -9,7 +9,6 @@
441 import shutil
442 from StringIO import StringIO
443 import tempfile
444-import unittest
445
446 import pytz
447 from zope.component import getUtility
448@@ -457,16 +456,17 @@
449 return source
450
451
452-class TestNativePublishingBase(unittest.TestCase, SoyuzTestPublisher):
453+class TestNativePublishingBase(TestCaseWithFactory, SoyuzTestPublisher):
454 layer = LaunchpadZopelessLayer
455 dbuser = config.archivepublisher.dbuser
456
457 def __init__(self, methodName='runTest'):
458- unittest.TestCase.__init__(self, methodName=methodName)
459+ super(TestNativePublishingBase, self).__init__(methodName=methodName)
460 SoyuzTestPublisher.__init__(self)
461
462 def setUp(self):
463 """Setup a pool dir, the librarian, and instantiate the DiskPool."""
464+ super(TestNativePublishingBase, self).setUp()
465 self.layer.switchDbUser(config.archivepublisher.dbuser)
466 self.prepareBreezyAutotest()
467 self.config = Config(self.ubuntutest)
468@@ -480,6 +480,7 @@
469
470 def tearDown(self):
471 """Tear down blows the pool dir away."""
472+ super(TestNativePublishingBase, self).tearDown()
473 shutil.rmtree(self.config.distroroot)
474
475 def getPubSource(self, *args, **kwargs):
476@@ -502,48 +503,19 @@
477 self.layer.commit()
478 return binaries
479
480- def checkSourcePublication(self, source, status):
481- """Assert the source publications has the given status.
482-
483- Retrieve an up-to-date record corresponding to the given publication,
484- check and return it.
485- """
486- fresh_source = SourcePackagePublishingHistory.get(source.id)
487- self.assertEqual(
488- fresh_source.status, status, "%s is not %s (%s)" % (
489- fresh_source.displayname, status.name, source.status.name))
490- return fresh_source
491-
492- def checkBinaryPublication(self, binary, status):
493- """Assert the binary publication has the given status.
494-
495- Retrieve an up-to-date record corresponding to the given publication,
496- check and return it.
497- """
498- fresh_binary = BinaryPackagePublishingHistory.get(binary.id)
499- self.assertEqual(
500- fresh_binary.status, status, "%s is not %s (%s)" % (
501- fresh_binary.displayname, status.name, fresh_binary.status.name))
502- return fresh_binary
503-
504- def checkBinaryPublications(self, binaries, status):
505- """Assert the binary publications have the given status.
506-
507- See `checkBinaryPublication`.
508- """
509- fresh_binaries = []
510- for bin in binaries:
511- bin = self.checkBinaryPublication(bin, status)
512- fresh_binaries.append(bin)
513- return fresh_binaries
514-
515- def checkPublications(self, source, binaries, status):
516- """Assert source and binary publications have in the given status.
517-
518- See `checkSourcePublication` and `checkBinaryPublications`.
519- """
520- self.checkSourcePublication(source, status)
521- self.checkBinaryPublications(binaries, status)
522+ def checkPublication(self, pub, status):
523+ """Assert the publication has the given status."""
524+ self.assertEqual(
525+ pub.status, status, "%s is not %s (%s)" % (
526+ pub.displayname, status.name, pub.status.name))
527+
528+ def checkPublications(self, pubs, status):
529+ """Assert the given publications have the given status.
530+
531+ See `checkPublication`.
532+ """
533+ for pub in pubs:
534+ self.checkPublication(pub, status)
535
536 def checkPastDate(self, date, lag=None):
537 """Assert given date is older than 'now'.
538@@ -557,6 +529,19 @@
539 limit = limit + lag
540 self.assertTrue(date < limit, "%s >= %s" % (date, limit))
541
542+ def checkSuperseded(self, pubs, supersededby=None):
543+ self.checkPublications(pubs, PackagePublishingStatus.SUPERSEDED)
544+ for pub in pubs:
545+ self.checkPastDate(pub.datesuperseded)
546+ if supersededby is not None:
547+ if isinstance(pub, BinaryPackagePublishingHistory):
548+ dominant = supersededby.binarypackagerelease.build
549+ else:
550+ dominant = supersededby.sourcepackagerelease
551+ self.assertEquals(dominant, pub.supersededby)
552+ else:
553+ self.assertIs(None, pub.supersededby)
554+
555
556 class TestNativePublishing(TestNativePublishingBase):
557
558@@ -815,7 +800,6 @@
559
560 # Adjust the binary package release original component.
561 universe = getUtility(IComponentSet)['universe']
562- from zope.security.proxy import removeSecurityProxy
563 removeSecurityProxy(binary.binarypackagerelease).component = universe
564
565 self.copyAndCheck(
566@@ -1025,3 +1009,174 @@
567 record = self.publishing_set.getByIdAndArchive(
568 binary_publishing.id, wrong_archive, source=False)
569 self.assertEqual(None, record)
570+
571+
572+class TestSourceDomination(TestNativePublishingBase):
573+ """Test SourcePackagePublishingHistory.supersede() operates correctly."""
574+
575+ def testSupersede(self):
576+ """Check that supersede() without arguments works."""
577+ source = self.getPubSource()
578+ source.supersede()
579+ self.checkSuperseded([source])
580+
581+ def testSupersedeWithDominant(self):
582+ """Check that supersede() with a dominant publication works."""
583+ source = self.getPubSource()
584+ super_source = self.getPubSource()
585+ source.supersede(super_source)
586+ self.checkSuperseded([source], super_source)
587+
588+ def testSupersedingSupersededSourceFails(self):
589+ """Check that supersede() fails with a superseded source.
590+
591+ Sources should not be superseded twice. If a second attempt is made,
592+ the Dominator's lookups are buggy.
593+ """
594+ source = self.getPubSource()
595+ super_source = self.getPubSource()
596+ source.supersede(super_source)
597+ self.checkSuperseded([source], super_source)
598+
599+ # Manually set a date in the past, so we can confirm that
600+ # the second supersede() fails properly.
601+ source.datesuperseded = datetime.datetime(
602+ 2006, 12, 25, tzinfo=pytz.timezone("UTC"))
603+ super_date = source.datesuperseded
604+
605+ self.assertRaises(AssertionError, source.supersede, super_source)
606+ self.checkSuperseded([source], super_source)
607+ self.assertEquals(super_date, source.datesuperseded)
608+
609+
610+class TestBinaryDomination(TestNativePublishingBase):
611+ """Test BinaryPackagePublishingHistory.supersede() operates correctly."""
612+
613+ def testSupersede(self):
614+ """Check that supersede() without arguments works."""
615+ bins = self.getPubBinaries(architecturespecific=True)
616+ bins[0].supersede()
617+ self.checkSuperseded([bins[0]])
618+ self.checkPublication(bins[1], PackagePublishingStatus.PENDING)
619+
620+ def testSupersedeWithDominant(self):
621+ """Check that supersede() with a dominant publication works."""
622+ bins = self.getPubBinaries(architecturespecific=True)
623+ super_bins = self.getPubBinaries(architecturespecific=True)
624+ bins[0].supersede(super_bins[0])
625+ self.checkSuperseded([bins[0]], super_bins[0])
626+ self.checkPublication(bins[1], PackagePublishingStatus.PENDING)
627+
628+ def testSupersedesArchIndepBinariesAtomically(self):
629+ """Check that supersede() supersedes arch-indep binaries atomically.
630+
631+ Architecture-independent binaries should be removed from all
632+ architectures when they are superseded on at least one (bug #48760).
633+ """
634+ bins = self.getPubBinaries(architecturespecific=False)
635+ super_bins = self.getPubBinaries(architecturespecific=False)
636+ bins[0].supersede(super_bins[0])
637+ self.checkSuperseded(bins, super_bins[0])
638+
639+ def testAtomicDominationRespectsOverrides(self):
640+ """Check that atomic domination only covers identical overrides.
641+
642+ This is important, as otherwise newly-overridden arch-indep binaries
643+ will supersede themselves, and vanish entirely (bug #178102).
644+ """
645+ bins = self.getPubBinaries(architecturespecific=False)
646+
647+ universe = getUtility(IComponentSet)['universe']
648+ super_bins = []
649+ for bin in bins:
650+ super_bins.append(bin.changeOverride(new_component=universe))
651+
652+ bins[0].supersede(super_bins[0])
653+ self.checkSuperseded(bins, super_bins[0])
654+ self.checkPublications(super_bins, PackagePublishingStatus.PENDING)
655+
656+ def testSupersedingSupersededArchSpecificBinaryFails(self):
657+ """Check that supersede() fails with a superseded arch-dep binary.
658+
659+ Architecture-specific binaries should not normally be superseded
660+ twice. If a second attempt is made, the Dominator's lookups are buggy.
661+ """
662+ bin = self.getPubBinaries(architecturespecific=True)[0]
663+ super_bin = self.getPubBinaries(architecturespecific=True)[0]
664+ bin.supersede(super_bin)
665+
666+ # Manually set a date in the past, so we can confirm that
667+ # the second supersede() fails properly.
668+ bin.datesuperseded = datetime.datetime(
669+ 2006, 12, 25, tzinfo=pytz.timezone("UTC"))
670+ super_date = bin.datesuperseded
671+
672+ self.assertRaises(AssertionError, bin.supersede, super_bin)
673+ self.checkSuperseded([bin], super_bin)
674+ self.assertEquals(super_date, bin.datesuperseded)
675+
676+ def testSkipsSupersededArchIndependentBinary(self):
677+ """Check that supersede() skips a superseded arch-indep binary.
678+
679+ Since all publications of an architecture-independent binary are
680+ superseded atomically, they may be superseded again later. In that
681+ case, we skip the domination, leaving the old date unchanged.
682+ """
683+ bin = self.getPubBinaries(architecturespecific=False)[0]
684+ super_bin = self.getPubBinaries(architecturespecific=False)[0]
685+ bin.supersede(super_bin)
686+ self.checkSuperseded([bin], super_bin)
687+
688+ # Manually set a date in the past, so we can confirm that
689+ # the second supersede() skips properly.
690+ bin.datesuperseded = datetime.datetime(
691+ 2006, 12, 25, tzinfo=pytz.timezone("UTC"))
692+ super_date = bin.datesuperseded
693+
694+ bin.supersede(super_bin)
695+ self.checkSuperseded([bin], super_bin)
696+ self.assertEquals(super_date, bin.datesuperseded)
697+
698+
699+class TestBinaryGetOtherPublications(TestNativePublishingBase):
700+ """Test BinaryPackagePublishingHistory._getOtherPublications() works."""
701+
702+ def checkOtherPublications(self, this, others):
703+ self.assertEquals(
704+ set(removeSecurityProxy(this)._getOtherPublications()),
705+ set(others))
706+
707+ def testFindsOtherArchIndepPublications(self):
708+ """Arch-indep publications with the same overrides should be found."""
709+ bins = self.getPubBinaries(architecturespecific=False)
710+ self.checkOtherPublications(bins[0], bins)
711+
712+ def testDoesntFindArchSpecificPublications(self):
713+ """Arch-dep publications shouldn't be found."""
714+ bins = self.getPubBinaries(architecturespecific=True)
715+ self.checkOtherPublications(bins[0], [bins[0]])
716+
717+ def testDoesntFindPublicationsInOtherArchives(self):
718+ """Publications in other archives shouldn't be found."""
719+ bins = self.getPubBinaries(architecturespecific=False)
720+ foreign_bins = bins[0].copyTo(
721+ bins[0].distroarchseries.distroseries, bins[0].pocket,
722+ self.factory.makeArchive())
723+ self.checkOtherPublications(bins[0], bins)
724+ self.checkOtherPublications(foreign_bins[0], foreign_bins)
725+
726+ def testDoesntFindPublicationsWithDifferentOverrides(self):
727+ """Publications with different overrides shouldn't be found."""
728+ bins = self.getPubBinaries(architecturespecific=False)
729+ universe = getUtility(IComponentSet)['universe']
730+ foreign_bin = bins[0].changeOverride(new_component=universe)
731+ self.checkOtherPublications(bins[0], bins)
732+ self.checkOtherPublications(foreign_bin, [foreign_bin])
733+
734+ def testDoesntFindSupersededPublications(self):
735+ """Superseded publications shouldn't be found."""
736+ bins = self.getPubBinaries(architecturespecific=False)
737+ self.checkOtherPublications(bins[0], bins)
738+ # This will supersede both atomically.
739+ bins[0].supersede()
740+ self.checkOtherPublications(bins[0], [])