Merge lp:~jtv/launchpad/pre-827347 into lp:launchpad

Proposed by Jeroen T. Vermeulen
Status: Merged
Approved by: Graham Binns
Approved revision: no longer in the source branch.
Merged at revision: 13706
Proposed branch: lp:~jtv/launchpad/pre-827347
Merge into: lp:launchpad
Diff against target: 277 lines (+75/-101)
3 files modified
lib/lp/registry/model/distroseriesdifference.py (+4/-5)
lib/lp/registry/tests/test_distroseriesdifference.py (+66/-94)
lib/lp/soyuz/model/packagecopyjob.py (+5/-2)
To merge this branch: bzr merge lp:~jtv/launchpad/pre-827347
Reviewer Review Type Date Requested Status
Graham Binns (community) code Approve
Review via email: mp+71724@code.launchpad.net

Commit message

[r=gmb][bug=827347] Sanitize default/None status arg for IDistroSeriesDifferenceSource.getForDistroSeries.

Description of the change

= Summary =

IDistroSeriesDifferenceSource.getForDistroSeries accepts a filter argument for status. It defaults to None. But surprisingly, it does not interpret None as "any status." Instead, None is read as DistroSeriesDifferenceStatus.NEEDS_ATTENTION. This causes no end of confusion, and also makes it harder to search for DSDs without regard for status. The weirdness was ossified into a test, but what documentation there is suggests that None is supposed to mean any status.

== Proposed fix ==

Make None mean "any status." No documentation changes needed. Some test cleanups though.

== Pre-implementation notes ==

DistroSeriesDifferencesVocabulary did not pass a status, but its documentation suggested that there would be no filtering by status. Gavin worked on this recently, and so was able to tell me that it was fooled by the unclear interface. The change in this branch should make it work as was always intended.

== Implementation details ==

The actual code change is tiny and appears at the top of the diff.

Only in packagecopyjob did I see a case where the status argument was left out but the caller clearly meant to get NEEDS_ATTENTION DSDs. There are a few tests elsewhere that do not pass status but are just as valid with "any status" as they are with the previous default. I kept those unchanged.

I also rewrote a whole bunch of getForDistroSeries tests to get rid of the complex test data that enabled or compounded this situation.

I'm still running tests to find out whether I missed anything. Since DSDs are a new feature, I'll count on the test suite to expose any problems with the change.

== Tests ==

{{{
./bin/test -vvc lp.registry.tests.test_distroseriesdifference
./bin/test -vvc lp.registry -t DistroSeriesDifferencesVocabulary
./bin/test -vvc lp.registry.browser.tests -t distroseries
}}}

== Demo and Q/A ==

The DistroSeries:+localpackagediffs page is a good place to exercise the effects on the DSD views. Go through the various statuses available in the search radio buttons, searching for each, and see that they all still select the appropriate differences.

Also verify the links from the page for a derived distroseries to the missing / changed / unique packages. Their counts may be affected. I'll be changing these links in my next branch for the same bug, since we want them to behave differently.

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/registry/tests/test_distroseriesdifference.py
  lib/lp/soyuz/model/packagecopyjob.py
  lib/lp/registry/model/distroseriesdifference.py

To post a comment you must log in.
Revision history for this message
Graham Binns (gmb) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/model/distroseriesdifference.py'
2--- lib/lp/registry/model/distroseriesdifference.py 2011-08-15 14:53:26 +0000
3+++ lib/lp/registry/model/distroseriesdifference.py 2011-08-16 15:51:26 +0000
4@@ -443,10 +443,8 @@
5 child_version_higher=False, parent_series=None,
6 packagesets=None, changed_by=None):
7 """See `IDistroSeriesDifferenceSource`."""
8- if status is None:
9- status = (DistroSeriesDifferenceStatus.NEEDS_ATTENTION,)
10- elif isinstance(status, DBItem):
11- status = (status, )
12+ if isinstance(status, DBItem):
13+ status = (status,)
14 if IPerson.providedBy(changed_by):
15 changed_by = (changed_by,)
16
17@@ -461,10 +459,11 @@
18 conditions = [
19 DSD.derived_series == distro_series,
20 DSD.source_package_name == SPN.id, # For ordering.
21- DSD.status.is_in(status),
22 ]
23 if difference_type is not None:
24 conditions.append(DSD.difference_type == difference_type)
25+ if status is not None:
26+ conditions.append(DSD.status.is_in(tuple(status)))
27
28 if child_version_higher:
29 conditions.append(DSD.source_version > DSD.parent_source_version)
30
31=== modified file 'lib/lp/registry/tests/test_distroseriesdifference.py'
32--- lib/lp/registry/tests/test_distroseriesdifference.py 2011-08-16 08:55:31 +0000
33+++ lib/lp/registry/tests/test_distroseriesdifference.py 2011-08-16 15:51:26 +0000
34@@ -955,45 +955,22 @@
35
36 layer = DatabaseFunctionalLayer
37
38- def test_implements_interface(self):
39- # The implementation implements the interface correctly.
40- dsd_source = getUtility(IDistroSeriesDifferenceSource)
41-
42- verifyObject(IDistroSeriesDifferenceSource, dsd_source)
43-
44- def makeDiffsForDistroSeries(self, derived_series, parent_series=None):
45- # Helper that creates a range of differences for a derived
46- # series.
47- diffs = {
48- 'normal': [],
49- 'unique': [],
50- 'missing': [],
51- 'ignored': [],
52- }
53- diffs['normal'].append(
54- self.factory.makeDistroSeriesDifference(
55- derived_series=derived_series, parent_series=parent_series))
56- diffs['unique'].append(
57- self.factory.makeDistroSeriesDifference(
58- derived_series=derived_series,
59- parent_series=parent_series,
60- difference_type=(
61- DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES)))
62- diffs['missing'].append(
63- self.factory.makeDistroSeriesDifference(
64- derived_series=derived_series,
65- parent_series=parent_series,
66- difference_type=(
67- DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES)))
68- diffs['ignored'].append(
69- self.factory.makeDistroSeriesDifference(
70- derived_series=derived_series,
71- parent_series=parent_series,
72- status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT))
73- return diffs
74+ def makeDifferencesForAllDifferenceTypes(self, derived_series):
75+ """Create DSDs of all types for `derived_series`."""
76+ return dict(
77+ (diff_type, self.factory.makeDistroSeriesDifference(
78+ derived_series, difference_type=diff_type))
79+ for diff_type in DistroSeriesDifferenceType.items)
80+
81+ def makeDifferencesForAllStatuses(self, derived_series):
82+ """Create DSDs of all statuses for `derived_series`."""
83+ return dict(
84+ (status, self.factory.makeDistroSeriesDifference(
85+ derived_series, status=status))
86+ for status in DistroSeriesDifferenceStatus.items)
87
88 def makeDerivedSeries(self, derived_series=None):
89- # Keep tests DRY.
90+ """Create a derived `DistroSeries`."""
91 dsp = self.factory.makeDistroSeriesParent(
92 derived_series=derived_series)
93 return dsp.derived_series
94@@ -1026,82 +1003,78 @@
95 derived_series=derived_series, versions=versions, status=status,
96 set_base_version=True)
97
98+ def test_implements_interface(self):
99+ self.assertProvides(
100+ getUtility(IDistroSeriesDifferenceSource),
101+ IDistroSeriesDifferenceSource),
102+
103 def test_getForDistroSeries_default(self):
104- # By default all differences needing attention for the given
105- # series are returned.
106- derived_series = self.makeDerivedSeries()
107- diffs = self.makeDiffsForDistroSeries(derived_series)
108-
109- result = getUtility(IDistroSeriesDifferenceSource).getForDistroSeries(
110- derived_series)
111-
112- self.assertContentEqual(
113- diffs['normal'] + diffs['unique'] + diffs['missing'], result)
114+ # By default all differences for the given series are returned.
115+ series = self.makeDerivedSeries()
116+ dsd = self.factory.makeDistroSeriesDifference(series)
117+ dsd_source = getUtility(IDistroSeriesDifferenceSource)
118+ self.assertContentEqual([dsd], dsd_source.getForDistroSeries(series))
119
120 def test_getForDistroSeries_filters_by_distroseries(self):
121 # Differences for other series are not included.
122- derived_series = self.makeDerivedSeries()
123- self.makeDiffsForDistroSeries(derived_series)
124- diff_for_other_series = self.factory.makeDistroSeriesDifference()
125-
126+ self.factory.makeDistroSeriesDifference()
127+ other_series = self.makeDerivedSeries()
128 dsd_source = getUtility(IDistroSeriesDifferenceSource)
129- results = set(dsd_source.getForDistroSeries(derived_series))
130-
131- self.assertFalse(diff_for_other_series in results)
132+ self.assertContentEqual(
133+ [], dsd_source.getForDistroSeries(other_series))
134
135 def test_getForDistroSeries_does_not_filter_dsd_type_by_default(self):
136 # If no difference_type is given, getForDistroSeries returns
137 # DSDs of all types (missing in derived series, different
138 # versions, or unique to derived series).
139- derived_series = self.makeDerivedSeries()
140- dsds = [
141- self.factory.makeDistroSeriesDifference(
142- derived_series, difference_type=diff_type)
143- for diff_type in DistroSeriesDifferenceType.items]
144+ series = self.makeDerivedSeries()
145+ dsds = self.makeDifferencesForAllDifferenceTypes(series)
146 dsd_source = getUtility(IDistroSeriesDifferenceSource)
147 self.assertContentEqual(
148- dsds, dsd_source.getForDistroSeries(derived_series))
149+ dsds.values(), dsd_source.getForDistroSeries(series))
150
151 def test_getForDistroSeries_filters_by_type(self):
152 # Only differences for the specified types are returned.
153- derived_series = self.makeDerivedSeries()
154- diffs = self.makeDiffsForDistroSeries(derived_series)
155-
156- result = getUtility(IDistroSeriesDifferenceSource).getForDistroSeries(
157- derived_series,
158- DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES)
159-
160- self.assertContentEqual(diffs['unique'], result)
161+ series = self.makeDerivedSeries()
162+ dsds = self.makeDifferencesForAllDifferenceTypes(series)
163+ dsd_source = getUtility(IDistroSeriesDifferenceSource)
164+ wanted_type = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
165+ self.assertContentEqual(
166+ [dsds[wanted_type]],
167+ dsd_source.getForDistroSeries(
168+ series, difference_type=wanted_type))
169+
170+ def test_getForDistroSeries_includes_all_statuses_by_default(self):
171+ # If no status is given, getForDistroSeries returns DSDs of all
172+ # statuses.
173+ series = self.makeDerivedSeries()
174+ dsds = self.makeDifferencesForAllStatuses(series)
175+ dsd_source = getUtility(IDistroSeriesDifferenceSource)
176+ self.assertContentEqual(
177+ dsds.values(), dsd_source.getForDistroSeries(series))
178
179 def test_getForDistroSeries_filters_by_status(self):
180 # A single status can be used to filter results.
181- derived_series = self.makeDerivedSeries()
182- diffs = self.makeDiffsForDistroSeries(derived_series)
183-
184- result = getUtility(IDistroSeriesDifferenceSource).getForDistroSeries(
185- derived_series,
186- status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT)
187-
188- self.assertContentEqual(diffs['ignored'], result)
189+ series = self.makeDerivedSeries()
190+ dsds = self.makeDifferencesForAllStatuses(series)
191+ dsd_source = getUtility(IDistroSeriesDifferenceSource)
192+ wanted_status = DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT
193+ self.assertContentEqual(
194+ [dsds[wanted_status]],
195+ dsd_source.getForDistroSeries(series, status=wanted_status))
196
197 def test_getForDistroSeries_filters_by_multiple_statuses(self):
198 # Multiple statuses can be passed for filtering.
199- derived_series = self.makeDerivedSeries()
200- for status in DistroSeriesDifferenceStatus.items:
201- self.factory.makeDistroSeriesDifference(
202- derived_series, status=status)
203-
204- statuses = (
205+ series = self.makeDerivedSeries()
206+ dsds = self.makeDifferencesForAllStatuses(series)
207+ wanted_statuses = (
208 DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT,
209 DistroSeriesDifferenceStatus.NEEDS_ATTENTION,
210 )
211-
212 dsd_source = getUtility(IDistroSeriesDifferenceSource)
213 self.assertContentEqual(
214- statuses, [
215- dsd.status
216- for dsd in dsd_source.getForDistroSeries(
217- derived_series, status=statuses)])
218+ [dsds[status] for status in wanted_statuses],
219+ dsd_source.getForDistroSeries(series, status=wanted_statuses))
220
221 def test_getForDistroSeries_matches_by_package_name(self):
222 dsd = self.factory.makeDistroSeriesDifference()
223@@ -1146,15 +1119,14 @@
224
225 def test_getForDistroSeries_sorted_by_package_name(self):
226 # The differences are sorted by package name.
227- derived_series = self.makeDerivedSeries()
228- names = []
229- for i in range(10):
230- diff = self.factory.makeDistroSeriesDifference(
231- derived_series=derived_series)
232- names.append(diff.source_package_name.name)
233+ series = self.makeDerivedSeries()
234+ names = [
235+ self.factory.makeDistroSeriesDifference(
236+ series).source_package_name.name
237+ for counter in xrange(10)]
238
239 results = getUtility(
240- IDistroSeriesDifferenceSource).getForDistroSeries(derived_series)
241+ IDistroSeriesDifferenceSource).getForDistroSeries(series)
242
243 self.assertContentEqual(
244 sorted(names),
245
246=== modified file 'lib/lp/soyuz/model/packagecopyjob.py'
247--- lib/lp/soyuz/model/packagecopyjob.py 2011-07-28 14:26:49 +0000
248+++ lib/lp/soyuz/model/packagecopyjob.py 2011-08-16 15:51:26 +0000
249@@ -8,8 +8,9 @@
250 "PlainPackageCopyJob",
251 ]
252
253+import logging
254+
255 from lazr.delegates import delegates
256-import logging
257 import simplejson
258 from storm.locals import (
259 And,
260@@ -36,6 +37,7 @@
261 )
262 from lp.app.errors import NotFoundError
263 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
264+from lp.registry.enum import DistroSeriesDifferenceStatus
265 from lp.registry.interfaces.distroseriesdifference import (
266 IDistroSeriesDifferenceSource,
267 )
268@@ -528,7 +530,8 @@
269 dsd_source = getUtility(IDistroSeriesDifferenceSource)
270 target_series = self.target_distroseries
271 candidates = dsd_source.getForDistroSeries(
272- distro_series=target_series, name_filter=self.package_name)
273+ distro_series=target_series, name_filter=self.package_name,
274+ status=DistroSeriesDifferenceStatus.NEEDS_ATTENTION)
275
276 # The job doesn't know what distroseries a given package is
277 # coming from, and the version number in the DSD may have