Merge lp:~james-w/launchpad/more-matchers into lp:launchpad

Proposed by James Westby
Status: Work in progress
Proposed branch: lp:~james-w/launchpad/more-matchers
Merge into: lp:launchpad
Prerequisite: lp:~james-w/launchpad/improve-makeDistroArchSeries
Diff against target: 745 lines (+559/-31)
7 files modified
lib/lp/soyuz/testing/matchers.py (+137/-0)
lib/lp/soyuz/testing/tests/test_matchers.py (+244/-0)
lib/lp/soyuz/tests/test_publishing.py (+7/-24)
lib/lp/testing/factory.py (+1/-1)
lib/lp/testing/matchers.py (+61/-3)
lib/lp/testing/tests/test_factory.py (+10/-0)
lib/lp/testing/tests/test_matchers.py (+99/-3)
To merge this branch: bzr merge lp:~james-w/launchpad/more-matchers
Reviewer Review Type Date Requested Status
Robert Collins (community) Approve
Review via email: mp+32057@code.launchpad.net

Commit message

[r=lifeless][no-qa] Add some more matchers, particularly soyuz-specific ones.

Description of the change

Hi,

This ports some Soyuz custom assertion methods to matchers.

I did this so that tests can make use of them without having to
subclass what is a fairly heavyweight test class.

I could have added a new TestCase subclass with the custom assertion
methods only and slotted it in to the existing hierarchy, but matchers
are better.

Lint:

./lib/lp/testing/factory.py
      32: redefinition of unused 'os' from line 31

Thanks,

James

To post a comment you must log in.
lp:~james-w/launchpad/more-matchers updated
11332. By James Westby

Dummy revision to force diff regeneration.

11333. By James Westby

Fix the docstrings given that it's not publications that superseded publications.

Revision history for this message
Robert Collins (lifeless) wrote :

PublishedStateIsNot is a little weird to export, being a mismatch - might want to think about the clarity for users there.

PublishedStateIs would add more debug value if it included all the failing elements, not just the first.

Typo 'supecseded'

This could be a regular method, not inline.
+ def spr_title(spr):

DateIsInPast would be good to do upstream - its not launchpad domain specific at all.

Please consider these tweaks and recommendations.

review: Approve
lp:~james-w/launchpad/more-matchers updated
11334. By James Westby

Rename PublishedStateIsNot to PublishedStateIsWrong as the first sounds like a matcher.

11335. By James Westby

Use a normal function, not an inline one for title_of.

11336. By James Westby

Fix matcher import.

11337. By James Westby

Allow publishing records in the superseded check.

Revision history for this message
Graham Binns (gmb) wrote :

Trying to run this through EC2 I got a mass of conflicts:

Warning: criss-cross merge encountered. See bzr help criss-cross.
[...]
Text conflict in lib/lp/archivepublisher/tests/test_dominator.py
Text conflict in lib/lp/archiveuploader/tests/nascentupload-ddebs.txt
Text conflict in lib/lp/soyuz/doc/build-files.txt
Text conflict in lib/lp/soyuz/doc/publishing.txt
Text conflict in lib/lp/soyuz/doc/sourcepackagerelease.txt
Text conflict in lib/lp/soyuz/scripts/tests/test_changeoverride.py
Text conflict in lib/lp/soyuz/scripts/tests/test_copypackage.py
Text conflict in lib/lp/soyuz/tests/test_publish_archive_indexes.py
Text conflict in lib/lp/soyuz/tests/test_publishing.py
Text conflict in lib/lp/testing/factory.py
Text conflict in lib/lp/testing/matchers.py
Text conflict in lib/lp/testing/tests/test_factory.py
Text conflict in lib/lp/testing/tests/test_matchers.py

Please resolve these and then ping me and I'll be happy to re-try the landing.

Revision history for this message
Robert Collins (lifeless) wrote :

This clearly needs more work to be usable :(.

Revision history for this message
Brad Crittenden (bac) wrote :

This branch depends on two pre-requisite branches that have been marked as 'Work In Progress', so this one cannot progress until they do. I am therefore marking this branch WIP too.

Unmerged revisions

11337. By James Westby

Allow publishing records in the superseded check.

11336. By James Westby

Fix matcher import.

11335. By James Westby

Use a normal function, not an inline one for title_of.

11334. By James Westby

Rename PublishedStateIsNot to PublishedStateIsWrong as the first sounds like a matcher.

11333. By James Westby

Fix the docstrings given that it's not publications that superseded publications.

11332. By James Westby

Dummy revision to force diff regeneration.

11331. By James Westby

Clean up some lint.

11330. By James Westby

Replace checkSuperseded with the new matcher.

11329. By James Westby

Add an IsSupersededBy matcher.

11328. By James Westby

Have test_publishing use the new matcher.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'lib/lp/soyuz/testing'
=== added file 'lib/lp/soyuz/testing/__init__.py'
=== added file 'lib/lp/soyuz/testing/matchers.py'
--- lib/lp/soyuz/testing/matchers.py 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/testing/matchers.py 2010-08-09 15:59:49 +0000
@@ -0,0 +1,137 @@
1# Copyright 2010 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
6__all__ = [
7 'IsSupersededBy',
8 'PublishedStateIs',
9 'PublishedStateIsWrong',
10]
11
12from zope.security.proxy import isinstance
13
14from testtools.matchers import Annotate, Equals, Matcher, Mismatch
15
16from lp.soyuz.interfaces.publishing import PackagePublishingStatus
17from lp.soyuz.model.publishing import (
18 BinaryPackagePublishingHistory, SourcePackagePublishingHistory)
19from lp.testing.matchers import DateIsInPast
20
21
22class PublishedStateIsWrong(Mismatch):
23
24 def __init__(self, publication, status):
25 """Create a PublishedStateIsWrong Mismatch.
26
27 :param publication: the publication that has the wrong state.
28 :param status: the status that the publication should have had.
29 """
30 self.publication = publication
31 self.status = status
32
33 def describe(self):
34 return "Publication '%s' was '%s' instead of '%s'." % (
35 self.publication.displayname, self.publication.status.name,
36 self.status.name)
37
38
39class PublishedStateIs(Matcher):
40 """Check the state of publication(s) is a certain value."""
41
42 def __init__(self, status):
43 """Create a PublishedState matcher.
44
45 :param status: the status the publication(s) should have.
46 """
47 self.status = status
48
49 def __str__(self):
50 return "Published state is %s" % (self.status.name, )
51
52 def match(self, publications):
53 """Match the status of publication(s) against the expected.
54
55 :param publications: a publication or iterable of publications
56 of which to check the status.
57 :return: An instance of `PublishedStateIsNot` if any of the
58 publications have a different status, otherwise None if they
59 all match.
60 """
61 try:
62 list(publications)
63 except TypeError:
64 publications = [publications]
65 for publication in publications:
66 if publication.status != self.status:
67 return PublishedStateIsWrong(publication, self.status)
68 return None
69
70
71def title_of(obj):
72 if obj is None:
73 return None
74 else:
75 return obj.title
76
77
78class IsSupersededBy(Matcher):
79 """Check that superseded publishing record(s) have correct values.
80
81 A superseded publishing record should have:
82 status = PackagePublishingStatus.SUPERSEDED
83 datesuperseded in the past
84 superseded_by a particular object or None
85 """
86
87 def __init__(self, superseded_by):
88 """Create an IsSupersededBy Matcher.
89
90 :param superseded_by: the object (`SourcePackageRelease` for
91 source publications, or `PackageBuild` for binary publications)
92 that should have superseded the checked publications, or
93 None if they shouldn't have been superseded by another
94 object. You can also pass a `SourcePackagePublishingHistory`
95 or `BinaryPackagePublishingHistory` to have their associated
96 `SourcePackageRelease` or `PackageBuild` used instead.
97 """
98 self.superseded_by = superseded_by
99
100 def __str__(self):
101 by = None
102 if self.superseded_by is not None:
103 by = "'%s'" % self.superseded_by.displayname
104 return "Is correctly superseded (by %s)." % by
105
106 def match(self, publications):
107 mismatch = PublishedStateIs(
108 PackagePublishingStatus.SUPERSEDED).match(publications)
109 if mismatch is not None:
110 return mismatch
111 try:
112 list(publications)
113 except TypeError:
114 publications = [publications]
115 for publication in publications:
116 matcher = Annotate(
117 "'%s' has a datesuperseded in the future." % (
118 publication.displayname, ),
119 DateIsInPast())
120 mismatch = matcher.match(publication.datesuperseded)
121 if mismatch is not None:
122 return mismatch
123 superseded_by = self.superseded_by
124 if superseded_by is not None:
125 if isinstance(superseded_by, BinaryPackagePublishingHistory):
126 superseded_by = superseded_by.binarypackagerelease.build
127 elif isinstance(superseded_by, SourcePackagePublishingHistory):
128 superseded_by = superseded_by.sourcepackagerelease
129 matcher = Annotate(
130 "'%s' has the wrong supersededby, expected '%s', got '%s'"
131 % (publication.displayname, title_of(superseded_by),
132 title_of(publication.supersededby)),
133 Equals(superseded_by))
134 mismatch = matcher.match(publication.supersededby)
135 if mismatch is not None:
136 return mismatch
137 return None
0138
=== added directory 'lib/lp/soyuz/testing/tests'
=== added file 'lib/lp/soyuz/testing/tests/__init__.py'
=== added file 'lib/lp/soyuz/testing/tests/test_matchers.py'
--- lib/lp/soyuz/testing/tests/test_matchers.py 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/testing/tests/test_matchers.py 2010-08-09 15:59:49 +0000
@@ -0,0 +1,244 @@
1# Copyright 2010 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 datetime import datetime, timedelta
7
8import pytz
9
10from testtools.matchers import AnnotatedMismatch
11
12from canonical.testing.layers import DatabaseFunctionalLayer
13from lp.soyuz.interfaces.publishing import PackagePublishingStatus
14from lp.soyuz.testing.matchers import (
15 IsSupersededBy, PublishedStateIs, PublishedStateIsWrong)
16from lp.testing import celebrity_logged_in, TestCaseWithFactory
17from lp.testing.matchers import DateIsNotInPast
18
19
20class PublishedStateIsNotTests(TestCaseWithFactory):
21
22 layer = DatabaseFunctionalLayer
23
24 def test_describe_binary(self):
25 bpph = self.factory.makeBinaryPackagePublishingHistory(
26 status = PackagePublishingStatus.PENDING)
27 status = PackagePublishingStatus.PUBLISHED
28 mismatch = PublishedStateIsWrong(bpph, status)
29 self.assertEqual(
30 "Publication '%s' was '%s' instead of '%s'." % (
31 bpph.displayname, bpph.status.name, status.name),
32 mismatch.describe())
33
34 def test_describe_source(self):
35 spph = self.factory.makeSourcePackagePublishingHistory(
36 status = PackagePublishingStatus.PENDING)
37 status = PackagePublishingStatus.PUBLISHED
38 mismatch = PublishedStateIsWrong(spph, status)
39 self.assertEqual(
40 "Publication '%s' was '%s' instead of '%s'." % (
41 spph.displayname, spph.status.name, status.name),
42 mismatch.describe())
43
44
45class PublishedStateIsTests(TestCaseWithFactory):
46
47 layer = DatabaseFunctionalLayer
48
49 def test_str(self):
50 status = PackagePublishingStatus.PUBLISHED
51 matcher = PublishedStateIs(status)
52 self.assertEqual(
53 "Published state is %s" % (status.name, ),
54 str(matcher))
55
56 def getMatcherResultSingle(self, status):
57 spph = self.factory.makeSourcePackagePublishingHistory(
58 status=PackagePublishingStatus.PENDING)
59 matcher = PublishedStateIs(status)
60 return matcher.match(spph), spph
61
62 def getMatcherResultList(self, status):
63 pubs = [
64 self.factory.makeSourcePackagePublishingHistory(
65 status=status),
66 self.factory.makeSourcePackagePublishingHistory(
67 status=PackagePublishingStatus.PENDING),
68 ]
69 matcher = PublishedStateIs(status)
70 return matcher.match(pubs), pubs
71
72 def test_match_single(self):
73 mismatch, publication = self.getMatcherResultSingle(
74 PackagePublishingStatus.PENDING)
75 self.assertIs(None, mismatch)
76
77 def test_match_list(self):
78 mismatch, publications = self.getMatcherResultList(
79 PackagePublishingStatus.PENDING)
80 self.assertIs(None, mismatch)
81
82 def test_mismatch_single(self):
83 mismatch, publication = self.getMatcherResultSingle(
84 PackagePublishingStatus.PUBLISHED)
85 self.assertIsInstance(mismatch, PublishedStateIsWrong)
86
87 def test_mismatch_list(self):
88 mismatch, publications = self.getMatcherResultList(
89 PackagePublishingStatus.PUBLISHED)
90 self.assertIsInstance(mismatch, PublishedStateIsWrong)
91
92 def test_mismatch_sets_publication_single(self):
93 mismatch, publication = self.getMatcherResultSingle(
94 PackagePublishingStatus.PUBLISHED)
95 self.assertEqual(publication, mismatch.publication)
96
97 def test_mismatch_sets_publication_list(self):
98 mismatch, publications = self.getMatcherResultList(
99 PackagePublishingStatus.PUBLISHED)
100 self.assertIsInstance(mismatch, PublishedStateIsWrong)
101 self.assertEqual(publications[1], mismatch.publication)
102
103 def test_mismatch_sets_status(self):
104 status = PackagePublishingStatus.PUBLISHED
105 mismatch, publication = self.getMatcherResultSingle(status)
106 self.assertEqual(status, mismatch.status)
107
108
109class IsSupersededByTests(TestCaseWithFactory):
110
111 layer = DatabaseFunctionalLayer
112
113 def test_str_with_superseded_by(self):
114 pub = self.factory.makeSourcePackagePublishingHistory()
115 matcher = IsSupersededBy(pub)
116 self.assertEqual(
117 "Is correctly superseded (by '%s')." % pub.displayname,
118 str(matcher))
119
120 def test_str_with_None(self):
121 matcher = IsSupersededBy(None)
122 self.assertEqual(
123 "Is correctly superseded (by None).", str(matcher))
124
125 def makeSupersededPublishing(self, superseded_by=None,
126 datesuperseded=None):
127 pub = self.factory.makeSourcePackagePublishingHistory(
128 status=PackagePublishingStatus.PUBLISHED)
129 pub.supersede(dominant=superseded_by)
130 if datesuperseded is not None:
131 with celebrity_logged_in('admin'):
132 pub.datesuperseded = datesuperseded
133 return pub
134
135 def test_match_single_superseded_by_not_None(self):
136 superseded_by = self.factory.makeSourcePackagePublishingHistory()
137 matcher = IsSupersededBy(superseded_by.sourcepackagerelease)
138 past = datetime.now(pytz.UTC) - timedelta(days=4)
139 pub = self.makeSupersededPublishing(
140 superseded_by=superseded_by, datesuperseded=past)
141 mismatch = matcher.match(pub)
142 self.assertIs(None, mismatch)
143
144 def test_match_single_superseded_by_None(self):
145 matcher = IsSupersededBy(None)
146 past = datetime.now(pytz.UTC) - timedelta(days=4)
147 pub = self.makeSupersededPublishing(
148 superseded_by=None, datesuperseded=past)
149 mismatch = matcher.match(pub)
150 self.assertIs(None, mismatch)
151
152 def test_match_list_superseded_by_not_None(self):
153 superseded_by = self.factory.makeSourcePackagePublishingHistory()
154 matcher = IsSupersededBy(superseded_by.sourcepackagerelease)
155 past = datetime.now(pytz.UTC) - timedelta(days=4)
156 pubs = [
157 self.makeSupersededPublishing(
158 superseded_by=superseded_by, datesuperseded=past),
159 self.makeSupersededPublishing(
160 superseded_by=superseded_by, datesuperseded=past),
161 ]
162 mismatch = matcher.match(pubs)
163 self.assertIs(None, mismatch)
164
165 def test_match_list_superseded_by_None(self):
166 matcher = IsSupersededBy(None)
167 past = datetime.now(pytz.UTC) - timedelta(days=4)
168 pubs = [
169 self.makeSupersededPublishing(
170 superseded_by=None, datesuperseded=past),
171 self.makeSupersededPublishing(
172 superseded_by=None, datesuperseded=past),
173 ]
174 mismatch = matcher.match(pubs)
175 self.assertIs(None, mismatch)
176
177 def test_mismatch_single_wrong_status(self):
178 matcher = IsSupersededBy(None)
179 past = datetime.now(pytz.UTC) - timedelta(days=4)
180 pub = self.makeSupersededPublishing(
181 superseded_by=None, datesuperseded=past)
182 with celebrity_logged_in('admin'):
183 pub.status = PackagePublishingStatus.PUBLISHED
184 mismatch = matcher.match(pub)
185 self.assertIsInstance(mismatch, PublishedStateIsWrong)
186
187 def test_mismatch_list_wrong_status(self):
188 matcher = IsSupersededBy(None)
189 past = datetime.now(pytz.UTC) - timedelta(days=4)
190 pubs = [
191 self.makeSupersededPublishing(
192 superseded_by=None, datesuperseded=past),
193 self.makeSupersededPublishing(
194 superseded_by=None, datesuperseded=past),
195 ]
196 with celebrity_logged_in('admin'):
197 pubs[1].status = PackagePublishingStatus.PUBLISHED
198 mismatch = matcher.match(pubs)
199 self.assertIsInstance(mismatch, PublishedStateIsWrong)
200
201 def test_mismatch_single_wrong_date(self):
202 matcher = IsSupersededBy(None)
203 future = datetime.now(pytz.UTC) + timedelta(days=4)
204 pub = self.makeSupersededPublishing(
205 superseded_by=None, datesuperseded=future)
206 mismatch = matcher.match(pub)
207 self.assertIsInstance(mismatch, AnnotatedMismatch)
208 self.assertIsInstance(mismatch.mismatch, DateIsNotInPast)
209
210 def test_mismatch_list_wrong_date(self):
211 matcher = IsSupersededBy(None)
212 past = datetime.now(pytz.UTC) - timedelta(days=4)
213 future = datetime.now(pytz.UTC) + timedelta(days=4)
214 pubs = [
215 self.makeSupersededPublishing(
216 superseded_by=None, datesuperseded=past),
217 self.makeSupersededPublishing(
218 superseded_by=None, datesuperseded=future),
219 ]
220 mismatch = matcher.match(pubs)
221 self.assertIsInstance(mismatch, AnnotatedMismatch)
222 self.assertIsInstance(mismatch.mismatch, DateIsNotInPast)
223
224 def test_mismatch_single_wrong_superseded_by(self):
225 superseded_by = self.factory.makeSourcePackagePublishingHistory()
226 matcher = IsSupersededBy(None)
227 past = datetime.now(pytz.UTC) - timedelta(days=4)
228 pub = self.makeSupersededPublishing(
229 superseded_by=superseded_by, datesuperseded=past)
230 mismatch = matcher.match(pub)
231 self.assertIsInstance(mismatch, AnnotatedMismatch)
232
233 def test_mismatch_list_wrong_superseded_by(self):
234 superseded_by = self.factory.makeSourcePackagePublishingHistory()
235 matcher = IsSupersededBy(None)
236 past = datetime.now(pytz.UTC) - timedelta(days=4)
237 pubs = [
238 self.makeSupersededPublishing(
239 superseded_by=None, datesuperseded=past),
240 self.makeSupersededPublishing(
241 superseded_by=superseded_by, datesuperseded=past),
242 ]
243 mismatch = matcher.match(pubs)
244 self.assertIsInstance(mismatch, AnnotatedMismatch)
0245
=== modified file 'lib/lp/soyuz/tests/test_publishing.py'
--- lib/lp/soyuz/tests/test_publishing.py 2010-08-09 15:59:47 +0000
+++ lib/lp/soyuz/tests/test_publishing.py 2010-08-09 15:59:49 +0000
@@ -12,7 +12,7 @@
1212
13import pytz13import pytz
14from zope.component import getUtility14from zope.component import getUtility
15from zope.security.proxy import isinstance, removeSecurityProxy15from zope.security.proxy import removeSecurityProxy
1616
17from canonical.config import config17from canonical.config import config
18from canonical.database.constants import UTC_NOW18from canonical.database.constants import UTC_NOW
@@ -29,8 +29,6 @@
29from lp.registry.interfaces.sourcepackage import SourcePackageUrgency29from lp.registry.interfaces.sourcepackage import SourcePackageUrgency
30from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet30from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
31from lp.soyuz.model.processor import ProcessorFamily31from lp.soyuz.model.processor import ProcessorFamily
32from lp.soyuz.model.publishing import (
33 BinaryPackagePublishingHistory)
34from lp.soyuz.interfaces.archive import ArchivePurpose32from lp.soyuz.interfaces.archive import ArchivePurpose
35from lp.soyuz.interfaces.archivearch import IArchiveArchSet33from lp.soyuz.interfaces.archivearch import IArchiveArchSet
36from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFormat34from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFormat
@@ -38,8 +36,10 @@
38from lp.soyuz.interfaces.publishing import (36from lp.soyuz.interfaces.publishing import (
39 IPublishingSet, PackagePublishingPriority, PackagePublishingStatus)37 IPublishingSet, PackagePublishingPriority, PackagePublishingStatus)
40from lp.soyuz.interfaces.queue import PackageUploadStatus38from lp.soyuz.interfaces.queue import PackageUploadStatus
39from lp.soyuz.testing.matchers import IsSupersededBy, PublishedStateIs
41from canonical.launchpad.scripts import FakeLogger40from canonical.launchpad.scripts import FakeLogger
42from lp.testing import TestCaseWithFactory41from lp.testing import TestCaseWithFactory
42from lp.testing.matchers import DateIsInPast
43from lp.testing.factory import LaunchpadObjectFactory43from lp.testing.factory import LaunchpadObjectFactory
44from lp.testing.sampledata import UBUNTU_DEVELOPER_ADMIN_NAME44from lp.testing.sampledata import UBUNTU_DEVELOPER_ADMIN_NAME
45from lp.testing.fakemethod import FakeMethod45from lp.testing.fakemethod import FakeMethod
@@ -499,17 +499,14 @@
499499
500 def checkPublication(self, pub, status):500 def checkPublication(self, pub, status):
501 """Assert the publication has the given status."""501 """Assert the publication has the given status."""
502 self.assertEqual(502 self.checkPublications(pub, status)
503 pub.status, status, "%s is not %s (%s)" % (
504 pub.displayname, status.name, pub.status.name))
505503
506 def checkPublications(self, pubs, status):504 def checkPublications(self, pubs, status):
507 """Assert the given publications have the given status.505 """Assert the given publications have the given status.
508506
509 See `checkPublication`.507 See `checkPublication`.
510 """508 """
511 for pub in pubs:509 self.assertThat(pubs, PublishedStateIs(status))
512 self.checkPublication(pub, status)
513510
514 def checkPastDate(self, date, lag=None):511 def checkPastDate(self, date, lag=None):
515 """Assert given date is older than 'now'.512 """Assert given date is older than 'now'.
@@ -517,24 +514,10 @@
517 Optionally the user can pass a 'lag' which will be added to 'now'514 Optionally the user can pass a 'lag' which will be added to 'now'
518 before comparing.515 before comparing.
519 """516 """
520 UTC = pytz.timezone("UTC")517 self.assertThat(date, DateIsInPast(lag=lag))
521 limit = datetime.datetime.now(UTC)
522 if lag is not None:
523 limit = limit + lag
524 self.assertTrue(date < limit, "%s >= %s" % (date, limit))
525518
526 def checkSuperseded(self, pubs, supersededby=None):519 def checkSuperseded(self, pubs, supersededby=None):
527 self.checkPublications(pubs, PackagePublishingStatus.SUPERSEDED)520 self.assertThat(pubs, IsSupersededBy(supersededby))
528 for pub in pubs:
529 self.checkPastDate(pub.datesuperseded)
530 if supersededby is not None:
531 if isinstance(pub, BinaryPackagePublishingHistory):
532 dominant = supersededby.binarypackagerelease.build
533 else:
534 dominant = supersededby.sourcepackagerelease
535 self.assertEquals(dominant, pub.supersededby)
536 else:
537 self.assertIs(None, pub.supersededby)
538521
539522
540class TestNativePublishing(TestNativePublishingBase):523class TestNativePublishing(TestNativePublishingBase):
541524
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2010-08-09 15:59:47 +0000
+++ lib/lp/testing/factory.py 2010-08-09 15:59:49 +0000
@@ -2243,7 +2243,7 @@
2243 def makeSourcePackageName(self, name=None):2243 def makeSourcePackageName(self, name=None):
2244 """Make an `ISourcePackageName`."""2244 """Make an `ISourcePackageName`."""
2245 if name is None:2245 if name is None:
2246 name = self.getUniqueString()2246 name = self.getUniqueString('sourcepackagename')
2247 return getUtility(ISourcePackageNameSet).new(name)2247 return getUtility(ISourcePackageNameSet).new(name)
22482248
2249 def getOrMakeSourcePackageName(self, name=None):2249 def getOrMakeSourcePackageName(self, name=None):
22502250
=== modified file 'lib/lp/testing/matchers.py'
--- lib/lp/testing/matchers.py 2010-08-09 15:59:47 +0000
+++ lib/lp/testing/matchers.py 2010-08-09 15:59:49 +0000
@@ -3,6 +3,8 @@
33
4__metaclass__ = type4__metaclass__ = type
5__all__ = [5__all__ = [
6 'DateIsInPast',
7 'DateIsNotInPast',
6 'DoesNotCorrectlyProvide',8 'DoesNotCorrectlyProvide',
7 'DoesNotProvide',9 'DoesNotProvide',
8 'DoesNotStartWith',10 'DoesNotStartWith',
@@ -14,6 +16,9 @@
14 'StartsWith',16 'StartsWith',
15 ]17 ]
1618
19from datetime import datetime
20
21import pytz
17from zope.interface.verify import verifyObject22from zope.interface.verify import verifyObject
18from zope.interface.exceptions import (23from zope.interface.exceptions import (
19 BrokenImplementation, BrokenMethodImplementation, DoesNotImplement)24 BrokenImplementation, BrokenMethodImplementation, DoesNotImplement)
@@ -123,15 +128,15 @@
123 self.query_collector = query_collector128 self.query_collector = query_collector
124129
125 def describe(self):130 def describe(self):
126 return "queries do not match: %s" % (self.count_mismatch.describe(),)131 return "queries do not match: %s" % (self.count_mismatch.describe(), )
127132
128 def get_details(self):133 def get_details(self):
129 result = []134 result = []
130 for query in self.query_collector.queries:135 for query in self.query_collector.queries:
131 result.append(unicode(query).encode('utf8'))136 result.append(unicode(query).encode('utf8'))
132 return {'queries': Content(ContentType('text', 'plain',137 return {'queries': Content(ContentType('text', 'plain',
133 {'charset': 'utf8'}), lambda:['\n'.join(result)])}138 {'charset': 'utf8'}), lambda: ['\n'.join(result)])}
134 139
135140
136class IsNotProxied(Mismatch):141class IsNotProxied(Mismatch):
137 """An object is not proxied."""142 """An object is not proxied."""
@@ -213,3 +218,56 @@
213 if not matchee.startswith(self.expected):218 if not matchee.startswith(self.expected):
214 return DoesNotStartWith(matchee, self.expected)219 return DoesNotStartWith(matchee, self.expected)
215 return None220 return None
221
222
223class DateIsNotInPast(Mismatch):
224
225 def __init__(self, date, limit):
226 """Create a DateIsNotInPast Mismatch.
227
228 :param date: the date that was checked.
229 :param limit: the date that it should have been before.
230 """
231 self.date = date
232 self.limit = limit
233
234 def describe(self):
235 return "%s >= %s" % (self.date, self.limit)
236
237
238class DateIsInPast(Matcher):
239 """A matcher that checks a datetime is in the past."""
240
241 def __init__(self, current_date=None, lag=None):
242 """Create a DateIsInPast Matcher.
243
244 :param current_date: the `datetime.datetime` that the date should be
245 before, or None for the current time when the check is done.
246 :param lag: a `datetime.timedelta` to add to the current time
247 before comparing, or None for no change.
248 """
249 self.current_date = current_date
250 self.lag = lag
251
252 def __str__(self):
253 if self.current_date is None:
254 start_str = 'UTC_NOW'
255 if self.lag is not None:
256 start_str += ' plus %s' % self.lag
257 else:
258 start = self.current_date
259 if self.lag is not None:
260 start += self.lag
261 start_str = str(start)
262 return "Date is before %s." % start_str
263
264 def match(self, date):
265 if self.current_date is None:
266 limit = datetime.now(pytz.UTC)
267 else:
268 limit = self.current_date
269 if self.lag is not None:
270 limit += self.lag
271 if date >= limit:
272 return DateIsNotInPast(date, limit)
273 return None
216274
=== modified file 'lib/lp/testing/tests/test_factory.py'
--- lib/lp/testing/tests/test_factory.py 2010-08-09 15:59:47 +0000
+++ lib/lp/testing/tests/test_factory.py 2010-08-09 15:59:49 +0000
@@ -20,6 +20,7 @@
20from lp.registry.interfaces.distribution import IDistribution20from lp.registry.interfaces.distribution import IDistribution
21from lp.registry.interfaces.distroseries import IDistroSeries21from lp.registry.interfaces.distroseries import IDistroSeries
22from lp.registry.interfaces.sourcepackage import SourcePackageFileType22from lp.registry.interfaces.sourcepackage import SourcePackageFileType
23from lp.registry.interfaces.sourcepackagename import ISourcePackageName
23from lp.registry.interfaces.suitesourcepackage import ISuiteSourcePackage24from lp.registry.interfaces.suitesourcepackage import ISuiteSourcePackage
24from lp.services.worlddata.interfaces.language import ILanguage25from lp.services.worlddata.interfaces.language import ILanguage
25from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuild26from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuild
@@ -401,6 +402,15 @@
401 # And name is constructed from code as 'Language %(code)s'.402 # And name is constructed from code as 'Language %(code)s'.
402 self.assertEquals('Test language', language.englishname)403 self.assertEquals('Test language', language.englishname)
403404
405 # makeSourcePackageName
406 def test_makeSourcePackageName_returns_proxied_ISPN(self):
407 spn = self.factory.makeSourcePackageName()
408 self.assertThat(spn, ProvidesAndIsProxied(ISourcePackageName))
409
410 def test_makeSourcePackageName_created_has_useful_prefix(self):
411 spn = self.factory.makeSourcePackageName()
412 self.assertThat(spn.name, StartsWith("sourcepackagename"))
413
404 # makeSourcePackagePublishingHistory414 # makeSourcePackagePublishingHistory
405 def test_makeSourcePackagePublishingHistory_returns_ISPPH(self):415 def test_makeSourcePackagePublishingHistory_returns_ISPPH(self):
406 spph = self.factory.makeSourcePackagePublishingHistory()416 spph = self.factory.makeSourcePackagePublishingHistory()
407417
=== modified file 'lib/lp/testing/tests/test_matchers.py'
--- lib/lp/testing/tests/test_matchers.py 2010-08-09 15:59:47 +0000
+++ lib/lp/testing/tests/test_matchers.py 2010-08-09 15:59:49 +0000
@@ -3,6 +3,9 @@
33
4__metaclass__ = type4__metaclass__ = type
55
6from datetime import datetime, timedelta
7
8import pytz
6from zope.interface import implements, Interface9from zope.interface import implements, Interface
7from zope.interface.verify import verifyObject10from zope.interface.verify import verifyObject
8from zope.interface.exceptions import BrokenImplementation11from zope.interface.exceptions import BrokenImplementation
@@ -11,8 +14,9 @@
1114
12from lp.testing import TestCase15from lp.testing import TestCase
13from lp.testing.matchers import (16from lp.testing.matchers import (
14 DoesNotCorrectlyProvide, DoesNotProvide, DoesNotStartWith, HasQueryCount,17 DateIsInPast, DateIsNotInPast, DoesNotCorrectlyProvide, DoesNotProvide,
15 IsNotProxied, IsProxied, Provides, ProvidesAndIsProxied, StartsWith)18 DoesNotStartWith, HasQueryCount, IsNotProxied, IsProxied, Provides,
19 ProvidesAndIsProxied, StartsWith)
16from lp.testing._webservice import QueryCollector20from lp.testing._webservice import QueryCollector
1721
18from testtools.matchers import Is, Not, LessThan22from testtools.matchers import Is, Not, LessThan
@@ -208,7 +212,7 @@
208 self.assertEqual(["('foo', 'bar')\n('baaz', 'quux')"],212 self.assertEqual(["('foo', 'bar')\n('baaz', 'quux')"],
209 lines)213 lines)
210 self.assertEqual(214 self.assertEqual(
211 "queries do not match: %s" % (LessThan(2).match(2).describe(),),215 "queries do not match: %s" % (LessThan(2).match(2).describe(), ),
212 mismatch.describe())216 mismatch.describe())
213217
214218
@@ -243,3 +247,95 @@
243 matcher = StartsWith("bar")247 matcher = StartsWith("bar")
244 mismatch = matcher.match("foo")248 mismatch = matcher.match("foo")
245 self.assertEqual("bar", mismatch.expected)249 self.assertEqual("bar", mismatch.expected)
250
251
252class DateIsNotInPastTests(TestCase):
253
254 def test_describe(self):
255 date = datetime(2008, 01, 01, 01, 01, 01, 01, pytz.UTC)
256 limit = datetime(2000, 02, 02, 02, 02, 02, 02, pytz.UTC)
257 mismatch = DateIsNotInPast(date, limit)
258 self.assertEqual("%s >= %s" % (date, limit), mismatch.describe())
259
260
261class DateIsInPastTests(TestCase):
262
263 def test_str_now_no_lag(self):
264 matcher = DateIsInPast()
265 self.assertEqual("Date is before UTC_NOW.", str(matcher))
266
267 def test_str_now_with_lag(self):
268 lag = timedelta(seconds=60)
269 matcher = DateIsInPast(lag=lag)
270 self.assertEqual(
271 "Date is before UTC_NOW plus %s." % lag, str(matcher))
272
273 def test_str_current_date_no_lag(self):
274 date = datetime(2008, 01, 01, 01, 01, 01, 01, pytz.UTC)
275 matcher = DateIsInPast(current_date=date)
276 self.assertEqual("Date is before %s." % date, str(matcher))
277
278 def test_str_current_date_with_lag(self):
279 date = datetime(2008, 01, 01, 01, 01, 01, 01, pytz.UTC)
280 lag = timedelta(seconds=60)
281 matcher = DateIsInPast(current_date=date, lag=lag)
282 self.assertEqual("Date is before %s." % (date + lag, ), str(matcher))
283
284 def test_match_now_no_lag(self):
285 date = datetime.now(pytz.UTC) - timedelta(days=3)
286 matcher = DateIsInPast()
287 self.assertEqual(None, matcher.match(date))
288
289 def test_match_now_with_lag(self):
290 date = datetime.now(pytz.UTC) + timedelta(days=3)
291 lag = timedelta(days=4)
292 matcher = DateIsInPast(lag=lag)
293 self.assertEqual(None, matcher.match(date))
294
295 def test_current_date_no_lag(self):
296 current_date = datetime(2008, 01, 01, 01, 01, 01, 01, pytz.UTC)
297 date = current_date - timedelta(days=3)
298 matcher = DateIsInPast(current_date=current_date)
299 self.assertEqual(None, matcher.match(date))
300
301 def test_current_date_with_lag(self):
302 current_date = datetime(2008, 01, 01, 01, 01, 01, 01, pytz.UTC)
303 date = current_date + timedelta(days=3)
304 lag = timedelta(days=4)
305 matcher = DateIsInPast(current_date=current_date, lag=lag)
306 self.assertEqual(None, matcher.match(date))
307
308 def test_mismatch_now_no_lag(self):
309 date = datetime.now(pytz.UTC) + timedelta(days=3)
310 matcher = DateIsInPast()
311 self.assertIsInstance(matcher.match(date), DateIsNotInPast)
312
313 def test_mismatch_now_with_lag(self):
314 date = datetime.now(pytz.UTC) + timedelta(days=3)
315 lag = timedelta(days=2)
316 matcher = DateIsInPast(lag=lag)
317 self.assertIsInstance(matcher.match(date), DateIsNotInPast)
318
319 def test_mismatch_current_date_no_lag(self):
320 current_date = datetime(2008, 01, 01, 01, 01, 01, 01, pytz.UTC)
321 date = current_date + timedelta(days=3)
322 matcher = DateIsInPast(current_date=current_date)
323 self.assertIsInstance(matcher.match(date), DateIsNotInPast)
324
325 def test_mismatch_current_date_with_lag(self):
326 current_date = datetime(2008, 01, 01, 01, 01, 01, 01, pytz.UTC)
327 date = current_date + timedelta(days=3)
328 lag = timedelta(days=2)
329 matcher = DateIsInPast(current_date=current_date, lag=lag)
330 self.assertIsInstance(matcher.match(date), DateIsNotInPast)
331
332 def test_mismatch_sets_date(self):
333 date = datetime.now(pytz.UTC) + timedelta(days=3)
334 matcher = DateIsInPast()
335 self.assertEqual(date, matcher.match(date).date)
336
337 def test_mismatch_sets_limit(self):
338 current_date = datetime.now(pytz.UTC)
339 date = current_date + timedelta(days=3)
340 matcher = DateIsInPast(current_date=current_date)
341 self.assertEqual(current_date, matcher.match(date).limit)