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
=== modified file 'lib/lp/registry/model/distroseriesdifference.py'
--- lib/lp/registry/model/distroseriesdifference.py 2011-08-15 14:53:26 +0000
+++ lib/lp/registry/model/distroseriesdifference.py 2011-08-16 15:51:26 +0000
@@ -443,10 +443,8 @@
443 child_version_higher=False, parent_series=None,443 child_version_higher=False, parent_series=None,
444 packagesets=None, changed_by=None):444 packagesets=None, changed_by=None):
445 """See `IDistroSeriesDifferenceSource`."""445 """See `IDistroSeriesDifferenceSource`."""
446 if status is None:446 if isinstance(status, DBItem):
447 status = (DistroSeriesDifferenceStatus.NEEDS_ATTENTION,)447 status = (status,)
448 elif isinstance(status, DBItem):
449 status = (status, )
450 if IPerson.providedBy(changed_by):448 if IPerson.providedBy(changed_by):
451 changed_by = (changed_by,)449 changed_by = (changed_by,)
452450
@@ -461,10 +459,11 @@
461 conditions = [459 conditions = [
462 DSD.derived_series == distro_series,460 DSD.derived_series == distro_series,
463 DSD.source_package_name == SPN.id, # For ordering.461 DSD.source_package_name == SPN.id, # For ordering.
464 DSD.status.is_in(status),
465 ]462 ]
466 if difference_type is not None:463 if difference_type is not None:
467 conditions.append(DSD.difference_type == difference_type)464 conditions.append(DSD.difference_type == difference_type)
465 if status is not None:
466 conditions.append(DSD.status.is_in(tuple(status)))
468467
469 if child_version_higher:468 if child_version_higher:
470 conditions.append(DSD.source_version > DSD.parent_source_version)469 conditions.append(DSD.source_version > DSD.parent_source_version)
471470
=== modified file 'lib/lp/registry/tests/test_distroseriesdifference.py'
--- lib/lp/registry/tests/test_distroseriesdifference.py 2011-08-16 08:55:31 +0000
+++ lib/lp/registry/tests/test_distroseriesdifference.py 2011-08-16 15:51:26 +0000
@@ -955,45 +955,22 @@
955955
956 layer = DatabaseFunctionalLayer956 layer = DatabaseFunctionalLayer
957957
958 def test_implements_interface(self):958 def makeDifferencesForAllDifferenceTypes(self, derived_series):
959 # The implementation implements the interface correctly.959 """Create DSDs of all types for `derived_series`."""
960 dsd_source = getUtility(IDistroSeriesDifferenceSource)960 return dict(
961961 (diff_type, self.factory.makeDistroSeriesDifference(
962 verifyObject(IDistroSeriesDifferenceSource, dsd_source)962 derived_series, difference_type=diff_type))
963963 for diff_type in DistroSeriesDifferenceType.items)
964 def makeDiffsForDistroSeries(self, derived_series, parent_series=None):964
965 # Helper that creates a range of differences for a derived965 def makeDifferencesForAllStatuses(self, derived_series):
966 # series.966 """Create DSDs of all statuses for `derived_series`."""
967 diffs = {967 return dict(
968 'normal': [],968 (status, self.factory.makeDistroSeriesDifference(
969 'unique': [],969 derived_series, status=status))
970 'missing': [],970 for status in DistroSeriesDifferenceStatus.items)
971 'ignored': [],
972 }
973 diffs['normal'].append(
974 self.factory.makeDistroSeriesDifference(
975 derived_series=derived_series, parent_series=parent_series))
976 diffs['unique'].append(
977 self.factory.makeDistroSeriesDifference(
978 derived_series=derived_series,
979 parent_series=parent_series,
980 difference_type=(
981 DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES)))
982 diffs['missing'].append(
983 self.factory.makeDistroSeriesDifference(
984 derived_series=derived_series,
985 parent_series=parent_series,
986 difference_type=(
987 DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES)))
988 diffs['ignored'].append(
989 self.factory.makeDistroSeriesDifference(
990 derived_series=derived_series,
991 parent_series=parent_series,
992 status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT))
993 return diffs
994971
995 def makeDerivedSeries(self, derived_series=None):972 def makeDerivedSeries(self, derived_series=None):
996 # Keep tests DRY.973 """Create a derived `DistroSeries`."""
997 dsp = self.factory.makeDistroSeriesParent(974 dsp = self.factory.makeDistroSeriesParent(
998 derived_series=derived_series)975 derived_series=derived_series)
999 return dsp.derived_series976 return dsp.derived_series
@@ -1026,82 +1003,78 @@
1026 derived_series=derived_series, versions=versions, status=status,1003 derived_series=derived_series, versions=versions, status=status,
1027 set_base_version=True)1004 set_base_version=True)
10281005
1006 def test_implements_interface(self):
1007 self.assertProvides(
1008 getUtility(IDistroSeriesDifferenceSource),
1009 IDistroSeriesDifferenceSource),
1010
1029 def test_getForDistroSeries_default(self):1011 def test_getForDistroSeries_default(self):
1030 # By default all differences needing attention for the given1012 # By default all differences for the given series are returned.
1031 # series are returned.1013 series = self.makeDerivedSeries()
1032 derived_series = self.makeDerivedSeries()1014 dsd = self.factory.makeDistroSeriesDifference(series)
1033 diffs = self.makeDiffsForDistroSeries(derived_series)1015 dsd_source = getUtility(IDistroSeriesDifferenceSource)
10341016 self.assertContentEqual([dsd], dsd_source.getForDistroSeries(series))
1035 result = getUtility(IDistroSeriesDifferenceSource).getForDistroSeries(
1036 derived_series)
1037
1038 self.assertContentEqual(
1039 diffs['normal'] + diffs['unique'] + diffs['missing'], result)
10401017
1041 def test_getForDistroSeries_filters_by_distroseries(self):1018 def test_getForDistroSeries_filters_by_distroseries(self):
1042 # Differences for other series are not included.1019 # Differences for other series are not included.
1043 derived_series = self.makeDerivedSeries()1020 self.factory.makeDistroSeriesDifference()
1044 self.makeDiffsForDistroSeries(derived_series)1021 other_series = self.makeDerivedSeries()
1045 diff_for_other_series = self.factory.makeDistroSeriesDifference()
1046
1047 dsd_source = getUtility(IDistroSeriesDifferenceSource)1022 dsd_source = getUtility(IDistroSeriesDifferenceSource)
1048 results = set(dsd_source.getForDistroSeries(derived_series))1023 self.assertContentEqual(
10491024 [], dsd_source.getForDistroSeries(other_series))
1050 self.assertFalse(diff_for_other_series in results)
10511025
1052 def test_getForDistroSeries_does_not_filter_dsd_type_by_default(self):1026 def test_getForDistroSeries_does_not_filter_dsd_type_by_default(self):
1053 # If no difference_type is given, getForDistroSeries returns1027 # If no difference_type is given, getForDistroSeries returns
1054 # DSDs of all types (missing in derived series, different1028 # DSDs of all types (missing in derived series, different
1055 # versions, or unique to derived series).1029 # versions, or unique to derived series).
1056 derived_series = self.makeDerivedSeries()1030 series = self.makeDerivedSeries()
1057 dsds = [1031 dsds = self.makeDifferencesForAllDifferenceTypes(series)
1058 self.factory.makeDistroSeriesDifference(
1059 derived_series, difference_type=diff_type)
1060 for diff_type in DistroSeriesDifferenceType.items]
1061 dsd_source = getUtility(IDistroSeriesDifferenceSource)1032 dsd_source = getUtility(IDistroSeriesDifferenceSource)
1062 self.assertContentEqual(1033 self.assertContentEqual(
1063 dsds, dsd_source.getForDistroSeries(derived_series))1034 dsds.values(), dsd_source.getForDistroSeries(series))
10641035
1065 def test_getForDistroSeries_filters_by_type(self):1036 def test_getForDistroSeries_filters_by_type(self):
1066 # Only differences for the specified types are returned.1037 # Only differences for the specified types are returned.
1067 derived_series = self.makeDerivedSeries()1038 series = self.makeDerivedSeries()
1068 diffs = self.makeDiffsForDistroSeries(derived_series)1039 dsds = self.makeDifferencesForAllDifferenceTypes(series)
10691040 dsd_source = getUtility(IDistroSeriesDifferenceSource)
1070 result = getUtility(IDistroSeriesDifferenceSource).getForDistroSeries(1041 wanted_type = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
1071 derived_series,1042 self.assertContentEqual(
1072 DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES)1043 [dsds[wanted_type]],
10731044 dsd_source.getForDistroSeries(
1074 self.assertContentEqual(diffs['unique'], result)1045 series, difference_type=wanted_type))
1046
1047 def test_getForDistroSeries_includes_all_statuses_by_default(self):
1048 # If no status is given, getForDistroSeries returns DSDs of all
1049 # statuses.
1050 series = self.makeDerivedSeries()
1051 dsds = self.makeDifferencesForAllStatuses(series)
1052 dsd_source = getUtility(IDistroSeriesDifferenceSource)
1053 self.assertContentEqual(
1054 dsds.values(), dsd_source.getForDistroSeries(series))
10751055
1076 def test_getForDistroSeries_filters_by_status(self):1056 def test_getForDistroSeries_filters_by_status(self):
1077 # A single status can be used to filter results.1057 # A single status can be used to filter results.
1078 derived_series = self.makeDerivedSeries()1058 series = self.makeDerivedSeries()
1079 diffs = self.makeDiffsForDistroSeries(derived_series)1059 dsds = self.makeDifferencesForAllStatuses(series)
10801060 dsd_source = getUtility(IDistroSeriesDifferenceSource)
1081 result = getUtility(IDistroSeriesDifferenceSource).getForDistroSeries(1061 wanted_status = DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT
1082 derived_series,1062 self.assertContentEqual(
1083 status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT)1063 [dsds[wanted_status]],
10841064 dsd_source.getForDistroSeries(series, status=wanted_status))
1085 self.assertContentEqual(diffs['ignored'], result)
10861065
1087 def test_getForDistroSeries_filters_by_multiple_statuses(self):1066 def test_getForDistroSeries_filters_by_multiple_statuses(self):
1088 # Multiple statuses can be passed for filtering.1067 # Multiple statuses can be passed for filtering.
1089 derived_series = self.makeDerivedSeries()1068 series = self.makeDerivedSeries()
1090 for status in DistroSeriesDifferenceStatus.items:1069 dsds = self.makeDifferencesForAllStatuses(series)
1091 self.factory.makeDistroSeriesDifference(1070 wanted_statuses = (
1092 derived_series, status=status)
1093
1094 statuses = (
1095 DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT,1071 DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT,
1096 DistroSeriesDifferenceStatus.NEEDS_ATTENTION,1072 DistroSeriesDifferenceStatus.NEEDS_ATTENTION,
1097 )1073 )
1098
1099 dsd_source = getUtility(IDistroSeriesDifferenceSource)1074 dsd_source = getUtility(IDistroSeriesDifferenceSource)
1100 self.assertContentEqual(1075 self.assertContentEqual(
1101 statuses, [1076 [dsds[status] for status in wanted_statuses],
1102 dsd.status1077 dsd_source.getForDistroSeries(series, status=wanted_statuses))
1103 for dsd in dsd_source.getForDistroSeries(
1104 derived_series, status=statuses)])
11051078
1106 def test_getForDistroSeries_matches_by_package_name(self):1079 def test_getForDistroSeries_matches_by_package_name(self):
1107 dsd = self.factory.makeDistroSeriesDifference()1080 dsd = self.factory.makeDistroSeriesDifference()
@@ -1146,15 +1119,14 @@
11461119
1147 def test_getForDistroSeries_sorted_by_package_name(self):1120 def test_getForDistroSeries_sorted_by_package_name(self):
1148 # The differences are sorted by package name.1121 # The differences are sorted by package name.
1149 derived_series = self.makeDerivedSeries()1122 series = self.makeDerivedSeries()
1150 names = []1123 names = [
1151 for i in range(10):1124 self.factory.makeDistroSeriesDifference(
1152 diff = self.factory.makeDistroSeriesDifference(1125 series).source_package_name.name
1153 derived_series=derived_series)1126 for counter in xrange(10)]
1154 names.append(diff.source_package_name.name)
11551127
1156 results = getUtility(1128 results = getUtility(
1157 IDistroSeriesDifferenceSource).getForDistroSeries(derived_series)1129 IDistroSeriesDifferenceSource).getForDistroSeries(series)
11581130
1159 self.assertContentEqual(1131 self.assertContentEqual(
1160 sorted(names),1132 sorted(names),
11611133
=== modified file 'lib/lp/soyuz/model/packagecopyjob.py'
--- lib/lp/soyuz/model/packagecopyjob.py 2011-07-28 14:26:49 +0000
+++ lib/lp/soyuz/model/packagecopyjob.py 2011-08-16 15:51:26 +0000
@@ -8,8 +8,9 @@
8 "PlainPackageCopyJob",8 "PlainPackageCopyJob",
9 ]9 ]
1010
11import logging
12
11from lazr.delegates import delegates13from lazr.delegates import delegates
12import logging
13import simplejson14import simplejson
14from storm.locals import (15from storm.locals import (
15 And,16 And,
@@ -36,6 +37,7 @@
36 )37 )
37from lp.app.errors import NotFoundError38from lp.app.errors import NotFoundError
38from lp.app.interfaces.launchpad import ILaunchpadCelebrities39from lp.app.interfaces.launchpad import ILaunchpadCelebrities
40from lp.registry.enum import DistroSeriesDifferenceStatus
39from lp.registry.interfaces.distroseriesdifference import (41from lp.registry.interfaces.distroseriesdifference import (
40 IDistroSeriesDifferenceSource,42 IDistroSeriesDifferenceSource,
41 )43 )
@@ -528,7 +530,8 @@
528 dsd_source = getUtility(IDistroSeriesDifferenceSource)530 dsd_source = getUtility(IDistroSeriesDifferenceSource)
529 target_series = self.target_distroseries531 target_series = self.target_distroseries
530 candidates = dsd_source.getForDistroSeries(532 candidates = dsd_source.getForDistroSeries(
531 distro_series=target_series, name_filter=self.package_name)533 distro_series=target_series, name_filter=self.package_name,
534 status=DistroSeriesDifferenceStatus.NEEDS_ATTENTION)
532535
533 # The job doesn't know what distroseries a given package is536 # The job doesn't know what distroseries a given package is
534 # coming from, and the version number in the DSD may have537 # coming from, and the version number in the DSD may have