Merge ~andrey-fedoseev/launchpad:bug-task-channel into launchpad:master
- Git
- lp:~andrey-fedoseev/launchpad
- bug-task-channel
- Merge into master
Proposed by
Andrey Fedoseev
Status: | Needs review |
---|---|
Proposed branch: | ~andrey-fedoseev/launchpad:bug-task-channel |
Merge into: | launchpad:master |
Diff against target: |
1067 lines (+328/-71) 20 files modified
lib/lp/bugs/configure.zcml (+2/-0) lib/lp/bugs/interfaces/bugsummary.py (+3/-1) lib/lp/bugs/interfaces/bugtask.py (+1/-0) lib/lp/bugs/model/bugsummary.py (+12/-1) lib/lp/bugs/model/bugtask.py (+27/-1) lib/lp/bugs/model/bugtaskflat.py (+2/-1) lib/lp/bugs/model/tests/test_bugtask.py (+24/-0) lib/lp/bugs/scripts/bugsummaryrebuild.py (+36/-20) lib/lp/bugs/scripts/bugtasktargetnamecaches.py (+2/-2) lib/lp/registry/interfaces/distroseries.py (+1/-1) lib/lp/registry/interfaces/sourcepackage.py (+11/-1) lib/lp/registry/model/distroseries.py (+3/-2) lib/lp/registry/model/sourcepackage.py (+64/-16) lib/lp/registry/stories/webservice/xx-source-package.rst (+1/-0) lib/lp/registry/tests/test_distributionsourcepackage.py (+1/-1) lib/lp/registry/tests/test_distroseries.py (+6/-0) lib/lp/registry/tests/test_sourcepackage.py (+60/-0) lib/lp/soyuz/tests/test_binarypackagebuild.py (+9/-5) lib/lp/soyuz/tests/test_hasbuildrecords.py (+50/-17) lib/lp/testing/factory.py (+13/-2) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Launchpad code reviewers | Pending | ||
Review via email: mp+434693@code.launchpad.net |
Commit message
WIP: Add `channel` field to `BugTask` and `SourcePackage`
This should also make a `SourcePackage` with a channel a valid target for a `BugTask`, and some work has been done in that direction, but it may not be 100% complete.
Description of the change
Currently, it is confirmed to pass all tests in `lp.{bugs,
To post a comment you must log in.
Unmerged commits
- c6e1a93... by Andrey Fedoseev
-
WIP: Add `channel` field to `BugTask` and `SourcePackage`
This should also make a `SourcePackage` with a channel a valid target for a `BugTask`, and some work has been done in that direction, but it may not be 100% complete.
-
docs:0 (build) lint:0 (build) mypy:0 (build) 1 → 3 of 3 results First • Previous • Next • Last
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/lib/lp/bugs/configure.zcml b/lib/lp/bugs/configure.zcml | |||
2 | index 6eaa58c..12bfdd7 100644 | |||
3 | --- a/lib/lp/bugs/configure.zcml | |||
4 | +++ b/lib/lp/bugs/configure.zcml | |||
5 | @@ -211,6 +211,8 @@ | |||
6 | 211 | distribution | 211 | distribution |
7 | 212 | distroseries | 212 | distroseries |
8 | 213 | milestone | 213 | milestone |
9 | 214 | _channel | ||
10 | 215 | channel | ||
11 | 214 | _status | 216 | _status |
12 | 215 | status | 217 | status |
13 | 216 | status_explanation | 218 | status_explanation |
14 | diff --git a/lib/lp/bugs/interfaces/bugsummary.py b/lib/lp/bugs/interfaces/bugsummary.py | |||
15 | index 7cb55ef..be83775 100644 | |||
16 | --- a/lib/lp/bugs/interfaces/bugsummary.py | |||
17 | +++ b/lib/lp/bugs/interfaces/bugsummary.py | |||
18 | @@ -9,7 +9,7 @@ __all__ = [ | |||
19 | 9 | ] | 9 | ] |
20 | 10 | 10 | ||
21 | 11 | from zope.interface import Interface | 11 | from zope.interface import Interface |
23 | 12 | from zope.schema import Bool, Choice, Int, Object, Text | 12 | from zope.schema import Bool, Choice, Int, Object, Text, TextLine |
24 | 13 | 13 | ||
25 | 14 | from lp import _ | 14 | from lp import _ |
26 | 15 | from lp.bugs.interfaces.bugtask import BugTaskImportance, BugTaskStatusSearch | 15 | from lp.bugs.interfaces.bugtask import BugTaskImportance, BugTaskStatusSearch |
27 | @@ -51,6 +51,8 @@ class IBugSummary(Interface): | |||
28 | 51 | ociproject_id = Int(readonly=True) | 51 | ociproject_id = Int(readonly=True) |
29 | 52 | ociproject = Object(IOCIProject, readonly=True) | 52 | ociproject = Object(IOCIProject, readonly=True) |
30 | 53 | 53 | ||
31 | 54 | channel = TextLine(readonly=True) | ||
32 | 55 | |||
33 | 54 | milestone_id = Int(readonly=True) | 56 | milestone_id = Int(readonly=True) |
34 | 55 | milestone = Object(IMilestone, readonly=True) | 57 | milestone = Object(IMilestone, readonly=True) |
35 | 56 | 58 | ||
36 | diff --git a/lib/lp/bugs/interfaces/bugtask.py b/lib/lp/bugs/interfaces/bugtask.py | |||
37 | index a8c4e45..d2586f7 100644 | |||
38 | --- a/lib/lp/bugs/interfaces/bugtask.py | |||
39 | +++ b/lib/lp/bugs/interfaces/bugtask.py | |||
40 | @@ -475,6 +475,7 @@ class IBugTask(IHasBug, IBugTaskDelete): | |||
41 | 475 | title=_("Series"), required=False, vocabulary="DistroSeries" | 475 | title=_("Series"), required=False, vocabulary="DistroSeries" |
42 | 476 | ) | 476 | ) |
43 | 477 | distroseries_id = Attribute("The distroseries ID") | 477 | distroseries_id = Attribute("The distroseries ID") |
44 | 478 | channel = TextLine(title=_("Channel"), required=False) | ||
45 | 478 | milestone = exported( | 479 | milestone = exported( |
46 | 479 | ReferenceChoice( | 480 | ReferenceChoice( |
47 | 480 | title=_("Milestone"), | 481 | title=_("Milestone"), |
48 | diff --git a/lib/lp/bugs/model/bugsummary.py b/lib/lp/bugs/model/bugsummary.py | |||
49 | index 9084e0e..2caabf6 100644 | |||
50 | --- a/lib/lp/bugs/model/bugsummary.py | |||
51 | +++ b/lib/lp/bugs/model/bugsummary.py | |||
52 | @@ -9,6 +9,8 @@ __all__ = [ | |||
53 | 9 | "get_bugsummary_filter_for_user", | 9 | "get_bugsummary_filter_for_user", |
54 | 10 | ] | 10 | ] |
55 | 11 | 11 | ||
56 | 12 | from typing import Optional | ||
57 | 13 | |||
58 | 12 | from storm.base import Storm | 14 | from storm.base import Storm |
59 | 13 | from storm.expr import SQL, And, Or, Select | 15 | from storm.expr import SQL, And, Or, Select |
60 | 14 | from storm.properties import Bool, Int, Unicode | 16 | from storm.properties import Bool, Int, Unicode |
61 | @@ -32,9 +34,10 @@ from lp.registry.model.product import Product | |||
62 | 32 | from lp.registry.model.productseries import ProductSeries | 34 | from lp.registry.model.productseries import ProductSeries |
63 | 33 | from lp.registry.model.sourcepackagename import SourcePackageName | 35 | from lp.registry.model.sourcepackagename import SourcePackageName |
64 | 34 | from lp.registry.model.teammembership import TeamParticipation | 36 | from lp.registry.model.teammembership import TeamParticipation |
65 | 37 | from lp.services.channels import channel_list_to_string | ||
66 | 35 | from lp.services.database.enumcol import DBEnum | 38 | from lp.services.database.enumcol import DBEnum |
67 | 36 | from lp.services.database.interfaces import IStore | 39 | from lp.services.database.interfaces import IStore |
69 | 37 | from lp.services.database.stormexpr import WithMaterialized | 40 | from lp.services.database.stormexpr import ImmutablePgJSON, WithMaterialized |
70 | 38 | 41 | ||
71 | 39 | 42 | ||
72 | 40 | @implementer(IBugSummary) | 43 | @implementer(IBugSummary) |
73 | @@ -64,6 +67,8 @@ class BugSummary(Storm): | |||
74 | 64 | ociproject_id = Int(name="ociproject") | 67 | ociproject_id = Int(name="ociproject") |
75 | 65 | ociproject = Reference(ociproject_id, "OCIProject.id") | 68 | ociproject = Reference(ociproject_id, "OCIProject.id") |
76 | 66 | 69 | ||
77 | 70 | _channel = ImmutablePgJSON(name="channel") | ||
78 | 71 | |||
79 | 67 | milestone_id = Int(name="milestone") | 72 | milestone_id = Int(name="milestone") |
80 | 68 | milestone = Reference(milestone_id, Milestone.id) | 73 | milestone = Reference(milestone_id, Milestone.id) |
81 | 69 | 74 | ||
82 | @@ -80,6 +85,12 @@ class BugSummary(Storm): | |||
83 | 80 | 85 | ||
84 | 81 | has_patch = Bool() | 86 | has_patch = Bool() |
85 | 82 | 87 | ||
86 | 88 | @property | ||
87 | 89 | def channel(self) -> Optional[str]: | ||
88 | 90 | if self._channel is None: | ||
89 | 91 | return None | ||
90 | 92 | return channel_list_to_string(*self._channel) | ||
91 | 93 | |||
92 | 83 | 94 | ||
93 | 84 | @implementer(IBugSummaryDimension) | 95 | @implementer(IBugSummaryDimension) |
94 | 85 | class CombineBugSummaryConstraint: | 96 | class CombineBugSummaryConstraint: |
95 | diff --git a/lib/lp/bugs/model/bugtask.py b/lib/lp/bugs/model/bugtask.py | |||
96 | index c006618..8955720 100644 | |||
97 | --- a/lib/lp/bugs/model/bugtask.py | |||
98 | +++ b/lib/lp/bugs/model/bugtask.py | |||
99 | @@ -21,6 +21,7 @@ import re | |||
100 | 21 | from collections import defaultdict | 21 | from collections import defaultdict |
101 | 22 | from itertools import chain, repeat | 22 | from itertools import chain, repeat |
102 | 23 | from operator import attrgetter, itemgetter | 23 | from operator import attrgetter, itemgetter |
103 | 24 | from typing import Optional | ||
104 | 24 | 25 | ||
105 | 25 | import pytz | 26 | import pytz |
106 | 26 | from lazr.lifecycle.event import ObjectDeletedEvent | 27 | from lazr.lifecycle.event import ObjectDeletedEvent |
107 | @@ -98,6 +99,7 @@ from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet | |||
108 | 98 | from lp.registry.model.pillar import pillar_sort_key | 99 | from lp.registry.model.pillar import pillar_sort_key |
109 | 99 | from lp.registry.model.sourcepackagename import SourcePackageName | 100 | from lp.registry.model.sourcepackagename import SourcePackageName |
110 | 100 | from lp.services import features | 101 | from lp.services import features |
111 | 102 | from lp.services.channels import channel_list_to_string, channel_string_to_list | ||
112 | 101 | from lp.services.database.bulk import create, load, load_related | 103 | from lp.services.database.bulk import create, load, load_related |
113 | 102 | from lp.services.database.constants import UTC_NOW | 104 | from lp.services.database.constants import UTC_NOW |
114 | 103 | from lp.services.database.decoratedresultset import DecoratedResultSet | 105 | from lp.services.database.decoratedresultset import DecoratedResultSet |
115 | @@ -111,6 +113,7 @@ from lp.services.database.sqlbase import ( | |||
116 | 111 | sqlvalues, | 113 | sqlvalues, |
117 | 112 | ) | 114 | ) |
118 | 113 | from lp.services.database.stormbase import StormBase | 115 | from lp.services.database.stormbase import StormBase |
119 | 116 | from lp.services.database.stormexpr import ImmutablePgJSON | ||
120 | 114 | from lp.services.helpers import shortlist | 117 | from lp.services.helpers import shortlist |
121 | 115 | from lp.services.propertycache import get_property_cache | 118 | from lp.services.propertycache import get_property_cache |
122 | 116 | from lp.services.searchbuilder import any | 119 | from lp.services.searchbuilder import any |
123 | @@ -171,6 +174,7 @@ def bug_target_from_key( | |||
124 | 171 | distroseries, | 174 | distroseries, |
125 | 172 | sourcepackagename, | 175 | sourcepackagename, |
126 | 173 | ociproject, | 176 | ociproject, |
127 | 177 | channel, | ||
128 | 174 | ): | 178 | ): |
129 | 175 | """Returns the IBugTarget defined by the given DB column values.""" | 179 | """Returns the IBugTarget defined by the given DB column values.""" |
130 | 176 | if ociproject: | 180 | if ociproject: |
131 | @@ -189,7 +193,7 @@ def bug_target_from_key( | |||
132 | 189 | return distribution | 193 | return distribution |
133 | 190 | elif distroseries: | 194 | elif distroseries: |
134 | 191 | if sourcepackagename: | 195 | if sourcepackagename: |
136 | 192 | return distroseries.getSourcePackage(sourcepackagename) | 196 | return distroseries.getSourcePackage(sourcepackagename, channel) |
137 | 193 | else: | 197 | else: |
138 | 194 | return distroseries | 198 | return distroseries |
139 | 195 | else: | 199 | else: |
140 | @@ -205,6 +209,7 @@ def bug_target_to_key(target): | |||
141 | 205 | distroseries=None, | 209 | distroseries=None, |
142 | 206 | sourcepackagename=None, | 210 | sourcepackagename=None, |
143 | 207 | ociproject=None, | 211 | ociproject=None, |
144 | 212 | channel=None, | ||
145 | 208 | ) | 213 | ) |
146 | 209 | if IProduct.providedBy(target): | 214 | if IProduct.providedBy(target): |
147 | 210 | values["product"] = target | 215 | values["product"] = target |
148 | @@ -220,6 +225,7 @@ def bug_target_to_key(target): | |||
149 | 220 | elif ISourcePackage.providedBy(target): | 225 | elif ISourcePackage.providedBy(target): |
150 | 221 | values["distroseries"] = target.distroseries | 226 | values["distroseries"] = target.distroseries |
151 | 222 | values["sourcepackagename"] = target.sourcepackagename | 227 | values["sourcepackagename"] = target.sourcepackagename |
152 | 228 | values["channel"] = target.channel | ||
153 | 223 | elif IOCIProject.providedBy(target): | 229 | elif IOCIProject.providedBy(target): |
154 | 224 | # De-normalize the ociproject, including also the ociproject's | 230 | # De-normalize the ociproject, including also the ociproject's |
155 | 225 | # pillar (distribution or product). | 231 | # pillar (distribution or product). |
156 | @@ -499,6 +505,8 @@ class BugTask(StormBase): | |||
157 | 499 | distroseries_id = Int(name="distroseries", allow_none=True) | 505 | distroseries_id = Int(name="distroseries", allow_none=True) |
158 | 500 | distroseries = Reference(distroseries_id, "DistroSeries.id") | 506 | distroseries = Reference(distroseries_id, "DistroSeries.id") |
159 | 501 | 507 | ||
160 | 508 | _channel = ImmutablePgJSON(name="channel", allow_none=True) | ||
161 | 509 | |||
162 | 502 | milestone_id = Int( | 510 | milestone_id = Int( |
163 | 503 | name="milestone", | 511 | name="milestone", |
164 | 504 | allow_none=True, | 512 | allow_none=True, |
165 | @@ -613,6 +621,19 @@ class BugTask(StormBase): | |||
166 | 613 | ) | 621 | ) |
167 | 614 | 622 | ||
168 | 615 | @property | 623 | @property |
169 | 624 | def channel(self) -> Optional[str]: | ||
170 | 625 | if self._channel is None: | ||
171 | 626 | return None | ||
172 | 627 | return channel_list_to_string(*self._channel) | ||
173 | 628 | |||
174 | 629 | @channel.setter | ||
175 | 630 | def channel(self, value: str) -> None: | ||
176 | 631 | if value is None: | ||
177 | 632 | self._channel = None | ||
178 | 633 | else: | ||
179 | 634 | self._channel = channel_string_to_list(value) | ||
180 | 635 | |||
181 | 636 | @property | ||
182 | 616 | def status(self): | 637 | def status(self): |
183 | 617 | if self._status in DB_INCOMPLETE_BUGTASK_STATUSES: | 638 | if self._status in DB_INCOMPLETE_BUGTASK_STATUSES: |
184 | 618 | return BugTaskStatus.INCOMPLETE | 639 | return BugTaskStatus.INCOMPLETE |
185 | @@ -652,6 +673,7 @@ class BugTask(StormBase): | |||
186 | 652 | self.distroseries, | 673 | self.distroseries, |
187 | 653 | self.sourcepackagename, | 674 | self.sourcepackagename, |
188 | 654 | self.ociproject, | 675 | self.ociproject, |
189 | 676 | self.channel, | ||
190 | 655 | ) | 677 | ) |
191 | 656 | 678 | ||
192 | 657 | @property | 679 | @property |
193 | @@ -1863,6 +1885,9 @@ class BugTaskSet: | |||
194 | 1863 | key["distroseries"], | 1885 | key["distroseries"], |
195 | 1864 | key["sourcepackagename"], | 1886 | key["sourcepackagename"], |
196 | 1865 | key["ociproject"], | 1887 | key["ociproject"], |
197 | 1888 | channel_string_to_list(key["channel"]) | ||
198 | 1889 | if key["channel"] | ||
199 | 1890 | else None, | ||
200 | 1866 | status, | 1891 | status, |
201 | 1867 | importance, | 1892 | importance, |
202 | 1868 | assignee, | 1893 | assignee, |
203 | @@ -1880,6 +1905,7 @@ class BugTaskSet: | |||
204 | 1880 | BugTask.distroseries, | 1905 | BugTask.distroseries, |
205 | 1881 | BugTask.sourcepackagename, | 1906 | BugTask.sourcepackagename, |
206 | 1882 | BugTask.ociproject, | 1907 | BugTask.ociproject, |
207 | 1908 | BugTask._channel, | ||
208 | 1883 | BugTask._status, | 1909 | BugTask._status, |
209 | 1884 | BugTask.importance, | 1910 | BugTask.importance, |
210 | 1885 | BugTask.assignee, | 1911 | BugTask.assignee, |
211 | diff --git a/lib/lp/bugs/model/bugtaskflat.py b/lib/lp/bugs/model/bugtaskflat.py | |||
212 | index b25a440..b636ecf 100644 | |||
213 | --- a/lib/lp/bugs/model/bugtaskflat.py | |||
214 | +++ b/lib/lp/bugs/model/bugtaskflat.py | |||
215 | @@ -1,6 +1,5 @@ | |||
216 | 1 | # Copyright 2012-2020 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2012-2020 Canonical Ltd. This software is licensed under the |
217 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
218 | 3 | |||
219 | 4 | from storm.locals import Bool, DateTime, Int, List, Reference, Storm | 3 | from storm.locals import Bool, DateTime, Int, List, Reference, Storm |
220 | 5 | 4 | ||
221 | 6 | from lp.app.enums import InformationType | 5 | from lp.app.enums import InformationType |
222 | @@ -10,6 +9,7 @@ from lp.bugs.interfaces.bugtask import ( | |||
223 | 10 | BugTaskStatusSearch, | 9 | BugTaskStatusSearch, |
224 | 11 | ) | 10 | ) |
225 | 12 | from lp.services.database.enumcol import DBEnum | 11 | from lp.services.database.enumcol import DBEnum |
226 | 12 | from lp.services.database.stormexpr import ImmutablePgJSON | ||
227 | 13 | 13 | ||
228 | 14 | 14 | ||
229 | 15 | class BugTaskFlat(Storm): | 15 | class BugTaskFlat(Storm): |
230 | @@ -42,6 +42,7 @@ class BugTaskFlat(Storm): | |||
231 | 42 | sourcepackagename = Reference(sourcepackagename_id, "SourcePackageName.id") | 42 | sourcepackagename = Reference(sourcepackagename_id, "SourcePackageName.id") |
232 | 43 | ociproject_id = Int(name="ociproject") | 43 | ociproject_id = Int(name="ociproject") |
233 | 44 | ociproject = Reference(ociproject_id, "OCIProject.id") | 44 | ociproject = Reference(ociproject_id, "OCIProject.id") |
234 | 45 | channel = ImmutablePgJSON() | ||
235 | 45 | status = DBEnum(enum=(BugTaskStatus, BugTaskStatusSearch)) | 46 | status = DBEnum(enum=(BugTaskStatus, BugTaskStatusSearch)) |
236 | 46 | importance = DBEnum(enum=BugTaskImportance) | 47 | importance = DBEnum(enum=BugTaskImportance) |
237 | 47 | assignee_id = Int(name="assignee") | 48 | assignee_id = Int(name="assignee") |
238 | diff --git a/lib/lp/bugs/model/tests/test_bugtask.py b/lib/lp/bugs/model/tests/test_bugtask.py | |||
239 | index c3eb248..ecb258f 100644 | |||
240 | --- a/lib/lp/bugs/model/tests/test_bugtask.py | |||
241 | +++ b/lib/lp/bugs/model/tests/test_bugtask.py | |||
242 | @@ -3145,6 +3145,7 @@ class TestBugTargetKeys(TestCaseWithFactory): | |||
243 | 3145 | distroseries=None, | 3145 | distroseries=None, |
244 | 3146 | sourcepackagename=None, | 3146 | sourcepackagename=None, |
245 | 3147 | ociproject=None, | 3147 | ociproject=None, |
246 | 3148 | channel=None, | ||
247 | 3148 | ), | 3149 | ), |
248 | 3149 | ) | 3150 | ) |
249 | 3150 | 3151 | ||
250 | @@ -3159,6 +3160,7 @@ class TestBugTargetKeys(TestCaseWithFactory): | |||
251 | 3159 | distroseries=None, | 3160 | distroseries=None, |
252 | 3160 | sourcepackagename=None, | 3161 | sourcepackagename=None, |
253 | 3161 | ociproject=None, | 3162 | ociproject=None, |
254 | 3163 | channel=None, | ||
255 | 3162 | ), | 3164 | ), |
256 | 3163 | ) | 3165 | ) |
257 | 3164 | 3166 | ||
258 | @@ -3173,6 +3175,7 @@ class TestBugTargetKeys(TestCaseWithFactory): | |||
259 | 3173 | distroseries=None, | 3175 | distroseries=None, |
260 | 3174 | sourcepackagename=None, | 3176 | sourcepackagename=None, |
261 | 3175 | ociproject=None, | 3177 | ociproject=None, |
262 | 3178 | channel=None, | ||
263 | 3176 | ), | 3179 | ), |
264 | 3177 | ) | 3180 | ) |
265 | 3178 | 3181 | ||
266 | @@ -3187,6 +3190,7 @@ class TestBugTargetKeys(TestCaseWithFactory): | |||
267 | 3187 | distroseries=distroseries, | 3190 | distroseries=distroseries, |
268 | 3188 | sourcepackagename=None, | 3191 | sourcepackagename=None, |
269 | 3189 | ociproject=None, | 3192 | ociproject=None, |
270 | 3193 | channel=None, | ||
271 | 3190 | ), | 3194 | ), |
272 | 3191 | ) | 3195 | ) |
273 | 3192 | 3196 | ||
274 | @@ -3201,6 +3205,7 @@ class TestBugTargetKeys(TestCaseWithFactory): | |||
275 | 3201 | distroseries=None, | 3205 | distroseries=None, |
276 | 3202 | sourcepackagename=dsp.sourcepackagename, | 3206 | sourcepackagename=dsp.sourcepackagename, |
277 | 3203 | ociproject=None, | 3207 | ociproject=None, |
278 | 3208 | channel=None, | ||
279 | 3204 | ), | 3209 | ), |
280 | 3205 | ) | 3210 | ) |
281 | 3206 | 3211 | ||
282 | @@ -3215,6 +3220,22 @@ class TestBugTargetKeys(TestCaseWithFactory): | |||
283 | 3215 | distroseries=sp.distroseries, | 3220 | distroseries=sp.distroseries, |
284 | 3216 | sourcepackagename=sp.sourcepackagename, | 3221 | sourcepackagename=sp.sourcepackagename, |
285 | 3217 | ociproject=None, | 3222 | ociproject=None, |
286 | 3223 | channel=None, | ||
287 | 3224 | ), | ||
288 | 3225 | ) | ||
289 | 3226 | |||
290 | 3227 | def test_sourcepackage_with_channel(self): | ||
291 | 3228 | sp = self.factory.makeSourcePackage(channel="stable") | ||
292 | 3229 | self.assertTargetKeyWorks( | ||
293 | 3230 | sp, | ||
294 | 3231 | dict( | ||
295 | 3232 | product=None, | ||
296 | 3233 | productseries=None, | ||
297 | 3234 | distribution=None, | ||
298 | 3235 | distroseries=sp.distroseries, | ||
299 | 3236 | sourcepackagename=sp.sourcepackagename, | ||
300 | 3237 | ociproject=None, | ||
301 | 3238 | channel="stable", | ||
302 | 3218 | ), | 3239 | ), |
303 | 3219 | ) | 3240 | ) |
304 | 3220 | 3241 | ||
305 | @@ -3230,6 +3251,7 @@ class TestBugTargetKeys(TestCaseWithFactory): | |||
306 | 3230 | distroseries=None, | 3251 | distroseries=None, |
307 | 3231 | sourcepackagename=None, | 3252 | sourcepackagename=None, |
308 | 3232 | ociproject=ociproject, | 3253 | ociproject=ociproject, |
309 | 3254 | channel=None, | ||
310 | 3233 | ), | 3255 | ), |
311 | 3234 | ) | 3256 | ) |
312 | 3235 | 3257 | ||
313 | @@ -3245,6 +3267,7 @@ class TestBugTargetKeys(TestCaseWithFactory): | |||
314 | 3245 | distroseries=None, | 3267 | distroseries=None, |
315 | 3246 | sourcepackagename=None, | 3268 | sourcepackagename=None, |
316 | 3247 | ociproject=ociproject, | 3269 | ociproject=ociproject, |
317 | 3270 | channel=None, | ||
318 | 3248 | ), | 3271 | ), |
319 | 3249 | ) | 3272 | ) |
320 | 3250 | 3273 | ||
321 | @@ -3263,6 +3286,7 @@ class TestBugTargetKeys(TestCaseWithFactory): | |||
322 | 3263 | None, | 3286 | None, |
323 | 3264 | None, | 3287 | None, |
324 | 3265 | None, | 3288 | None, |
325 | 3289 | None, | ||
326 | 3266 | ) | 3290 | ) |
327 | 3267 | 3291 | ||
328 | 3268 | 3292 | ||
329 | diff --git a/lib/lp/bugs/scripts/bugsummaryrebuild.py b/lib/lp/bugs/scripts/bugsummaryrebuild.py | |||
330 | index 21003b4..cf9ae8a 100644 | |||
331 | --- a/lib/lp/bugs/scripts/bugsummaryrebuild.py | |||
332 | +++ b/lib/lp/bugs/scripts/bugsummaryrebuild.py | |||
333 | @@ -24,6 +24,7 @@ from lp.registry.model.ociproject import OCIProject | |||
334 | 24 | from lp.registry.model.product import Product | 24 | from lp.registry.model.product import Product |
335 | 25 | from lp.registry.model.productseries import ProductSeries | 25 | from lp.registry.model.productseries import ProductSeries |
336 | 26 | from lp.registry.model.sourcepackagename import SourcePackageName | 26 | from lp.registry.model.sourcepackagename import SourcePackageName |
337 | 27 | from lp.services.channels import channel_string_to_list | ||
338 | 27 | from lp.services.database.bulk import create | 28 | from lp.services.database.bulk import create |
339 | 28 | from lp.services.database.interfaces import IStore | 29 | from lp.services.database.interfaces import IStore |
340 | 29 | from lp.services.database.stormexpr import Unnest | 30 | from lp.services.database.stormexpr import Unnest |
341 | @@ -109,7 +110,7 @@ def load_target(pid, psid, did, dsid, spnid, ociproject_id): | |||
342 | 109 | (pid, psid, did, dsid, spnid, ociproject_id), | 110 | (pid, psid, did, dsid, spnid, ociproject_id), |
343 | 110 | ), | 111 | ), |
344 | 111 | ) | 112 | ) |
346 | 112 | return bug_target_from_key(p, ps, d, ds, spn, ociproject) | 113 | return bug_target_from_key(p, ps, d, ds, spn, ociproject, None) |
347 | 113 | 114 | ||
348 | 114 | 115 | ||
349 | 115 | def format_target(target): | 116 | def format_target(target): |
350 | @@ -130,7 +131,15 @@ def format_target(target): | |||
351 | 130 | def _get_bugsummary_constraint_bits(target): | 131 | def _get_bugsummary_constraint_bits(target): |
352 | 131 | raw_key = bug_target_to_key(target) | 132 | raw_key = bug_target_to_key(target) |
353 | 132 | # Map to ID columns to work around Storm bug #682989. | 133 | # Map to ID columns to work around Storm bug #682989. |
355 | 133 | return {"%s_id" % k: v.id if v else None for (k, v) in raw_key.items()} | 134 | constraint_bits = {} |
356 | 135 | for name, value in raw_key.items(): | ||
357 | 136 | if name == "channel": | ||
358 | 137 | constraint_bits["_channel"] = ( | ||
359 | 138 | channel_string_to_list(value) if value else None | ||
360 | 139 | ) | ||
361 | 140 | else: | ||
362 | 141 | constraint_bits["{}_id".format(name)] = value.id if value else None | ||
363 | 142 | return constraint_bits | ||
364 | 134 | 143 | ||
365 | 135 | 144 | ||
366 | 136 | def get_bugsummary_constraint(target, cls=RawBugSummary): | 145 | def get_bugsummary_constraint(target, cls=RawBugSummary): |
367 | @@ -154,10 +163,19 @@ def get_bugtaskflat_constraint(target): | |||
368 | 154 | if IProduct.providedBy(target): | 163 | if IProduct.providedBy(target): |
369 | 155 | del raw_key["ociproject"] | 164 | del raw_key["ociproject"] |
370 | 156 | # Map to ID columns to work around Storm bug #682989. | 165 | # Map to ID columns to work around Storm bug #682989. |
375 | 157 | return [ | 166 | constraint_bits = [] |
376 | 158 | getattr(BugTaskFlat, "%s_id" % k) == (v.id if v else None) | 167 | for name, value in raw_key.items(): |
377 | 159 | for (k, v) in raw_key.items() | 168 | if name == "channel": |
378 | 160 | ] | 169 | constraint_bits.append( |
379 | 170 | getattr(BugTaskFlat, name) | ||
380 | 171 | == (channel_string_to_list(value) if value else None) | ||
381 | 172 | ) | ||
382 | 173 | else: | ||
383 | 174 | constraint_bits.append( | ||
384 | 175 | getattr(BugTaskFlat, "{}_id".format(name)) | ||
385 | 176 | == (value.id if value else None) | ||
386 | 177 | ) | ||
387 | 178 | return constraint_bits | ||
388 | 161 | 179 | ||
389 | 162 | 180 | ||
390 | 163 | def get_bugsummary_rows(target): | 181 | def get_bugsummary_rows(target): |
391 | @@ -167,6 +185,7 @@ def get_bugsummary_rows(target): | |||
392 | 167 | with BugSummary which is actually combinedbugsummary, a view over | 185 | with BugSummary which is actually combinedbugsummary, a view over |
393 | 168 | bugsummary and bugsummaryjournal. | 186 | bugsummary and bugsummaryjournal. |
394 | 169 | """ | 187 | """ |
395 | 188 | constraint = get_bugsummary_constraint(target) | ||
396 | 170 | return IStore(RawBugSummary).find( | 189 | return IStore(RawBugSummary).find( |
397 | 171 | ( | 190 | ( |
398 | 172 | RawBugSummary.status, | 191 | RawBugSummary.status, |
399 | @@ -178,7 +197,7 @@ def get_bugsummary_rows(target): | |||
400 | 178 | RawBugSummary.access_policy_id, | 197 | RawBugSummary.access_policy_id, |
401 | 179 | RawBugSummary.count, | 198 | RawBugSummary.count, |
402 | 180 | ), | 199 | ), |
404 | 181 | *get_bugsummary_constraint(target), | 200 | *constraint, |
405 | 182 | ) | 201 | ) |
406 | 183 | 202 | ||
407 | 184 | 203 | ||
408 | @@ -191,7 +210,7 @@ def get_bugsummaryjournal_rows(target): | |||
409 | 191 | 210 | ||
410 | 192 | 211 | ||
411 | 193 | def calculate_bugsummary_changes(old, new): | 212 | def calculate_bugsummary_changes(old, new): |
413 | 194 | """Calculate the changes between between the new and old dicts. | 213 | """Calculate the changes between the new and old dicts. |
414 | 195 | 214 | ||
415 | 196 | Takes {key: int} dicts, returns items from the new dict that differ | 215 | Takes {key: int} dicts, returns items from the new dict that differ |
416 | 197 | from the old one. | 216 | from the old one. |
417 | @@ -219,18 +238,14 @@ def calculate_bugsummary_changes(old, new): | |||
418 | 219 | def apply_bugsummary_changes(target, added, updated, removed): | 238 | def apply_bugsummary_changes(target, added, updated, removed): |
419 | 220 | """Apply a set of BugSummary changes to the DB.""" | 239 | """Apply a set of BugSummary changes to the DB.""" |
420 | 221 | bits = _get_bugsummary_constraint_bits(target) | 240 | bits = _get_bugsummary_constraint_bits(target) |
433 | 222 | target_key = tuple( | 241 | target_key = ( |
434 | 223 | map( | 242 | bits["product_id"], |
435 | 224 | bits.get, | 243 | bits["productseries_id"], |
436 | 225 | ( | 244 | bits["distribution_id"], |
437 | 226 | "product_id", | 245 | bits["distroseries_id"], |
438 | 227 | "productseries_id", | 246 | bits["sourcepackagename_id"], |
439 | 228 | "distribution_id", | 247 | bits["ociproject_id"], |
440 | 229 | "distroseries_id", | 248 | bits["_channel"], |
429 | 230 | "sourcepackagename_id", | ||
430 | 231 | "ociproject_id", | ||
431 | 232 | ), | ||
432 | 233 | ) | ||
441 | 234 | ) | 249 | ) |
442 | 235 | target_cols = ( | 250 | target_cols = ( |
443 | 236 | RawBugSummary.product_id, | 251 | RawBugSummary.product_id, |
444 | @@ -239,6 +254,7 @@ def apply_bugsummary_changes(target, added, updated, removed): | |||
445 | 239 | RawBugSummary.distroseries_id, | 254 | RawBugSummary.distroseries_id, |
446 | 240 | RawBugSummary.sourcepackagename_id, | 255 | RawBugSummary.sourcepackagename_id, |
447 | 241 | RawBugSummary.ociproject_id, | 256 | RawBugSummary.ociproject_id, |
448 | 257 | RawBugSummary._channel, | ||
449 | 242 | ) | 258 | ) |
450 | 243 | key_cols = ( | 259 | key_cols = ( |
451 | 244 | RawBugSummary.status, | 260 | RawBugSummary.status, |
452 | diff --git a/lib/lp/bugs/scripts/bugtasktargetnamecaches.py b/lib/lp/bugs/scripts/bugtasktargetnamecaches.py | |||
453 | index 70a82d4..dcbf424 100644 | |||
454 | --- a/lib/lp/bugs/scripts/bugtasktargetnamecaches.py | |||
455 | +++ b/lib/lp/bugs/scripts/bugtasktargetnamecaches.py | |||
456 | @@ -103,10 +103,10 @@ class BugTaskTargetNameCachesTunableLoop: | |||
457 | 103 | self.offset += 1 | 103 | self.offset += 1 |
458 | 104 | # Resolve the IDs to objects, and get the actual IBugTarget. | 104 | # Resolve the IDs to objects, and get the actual IBugTarget. |
459 | 105 | # If the ID is None, don't even try to get an object. | 105 | # If the ID is None, don't even try to get an object. |
461 | 106 | target_objects = ( | 106 | target_objects = [ |
462 | 107 | (store.get(cls, id) if id is not None else None) | 107 | (store.get(cls, id) if id is not None else None) |
463 | 108 | for cls, id in zip(target_classes, target_bits) | 108 | for cls, id in zip(target_classes, target_bits) |
465 | 109 | ) | 109 | ] + [None] |
466 | 110 | target = bug_target_from_key(*target_objects) | 110 | target = bug_target_from_key(*target_objects) |
467 | 111 | new_name = target.bugtargetdisplayname | 111 | new_name = target.bugtargetdisplayname |
468 | 112 | cached_names.discard(new_name) | 112 | cached_names.discard(new_name) |
469 | diff --git a/lib/lp/registry/interfaces/distroseries.py b/lib/lp/registry/interfaces/distroseries.py | |||
470 | index 03e6d68..5a718c2 100644 | |||
471 | --- a/lib/lp/registry/interfaces/distroseries.py | |||
472 | +++ b/lib/lp/registry/interfaces/distroseries.py | |||
473 | @@ -667,7 +667,7 @@ class IDistroSeriesPublic( | |||
474 | 667 | @operation_returns_entry(ISourcePackage) | 667 | @operation_returns_entry(ISourcePackage) |
475 | 668 | @export_read_operation() | 668 | @export_read_operation() |
476 | 669 | @operation_for_version("beta") | 669 | @operation_for_version("beta") |
478 | 670 | def getSourcePackage(name): | 670 | def getSourcePackage(name, channel=None): |
479 | 671 | """Return a source package in this distro series by name. | 671 | """Return a source package in this distro series by name. |
480 | 672 | 672 | ||
481 | 673 | The name given may be a string or an ISourcePackageName-providing | 673 | The name given may be a string or an ISourcePackageName-providing |
482 | diff --git a/lib/lp/registry/interfaces/sourcepackage.py b/lib/lp/registry/interfaces/sourcepackage.py | |||
483 | index d8f776e..a4505c0 100644 | |||
484 | --- a/lib/lp/registry/interfaces/sourcepackage.py | |||
485 | +++ b/lib/lp/registry/interfaces/sourcepackage.py | |||
486 | @@ -130,6 +130,15 @@ class ISourcePackagePublic( | |||
487 | 130 | 130 | ||
488 | 131 | sourcepackagename = Attribute("SourcePackageName") | 131 | sourcepackagename = Attribute("SourcePackageName") |
489 | 132 | 132 | ||
490 | 133 | channel = exported( | ||
491 | 134 | TextLine( | ||
492 | 135 | title=_("Channel"), | ||
493 | 136 | required=False, | ||
494 | 137 | readonly=True, | ||
495 | 138 | description=_("The channel for this source package."), | ||
496 | 139 | ), | ||
497 | 140 | ) | ||
498 | 141 | |||
499 | 133 | # This is really a reference to an IProductSeries. | 142 | # This is really a reference to an IProductSeries. |
500 | 134 | productseries = exported( | 143 | productseries = exported( |
501 | 135 | ReferenceChoice( | 144 | ReferenceChoice( |
502 | @@ -362,11 +371,12 @@ class ISourcePackage(ISourcePackagePublic, ISourcePackageEdit): | |||
503 | 362 | class ISourcePackageFactory(Interface): | 371 | class ISourcePackageFactory(Interface): |
504 | 363 | """A creator of source packages.""" | 372 | """A creator of source packages.""" |
505 | 364 | 373 | ||
507 | 365 | def new(sourcepackagename, distroseries): | 374 | def new(sourcepackagename, distroseries, channel=None): |
508 | 366 | """Create a new `ISourcePackage`. | 375 | """Create a new `ISourcePackage`. |
509 | 367 | 376 | ||
510 | 368 | :param sourcepackagename: An `ISourcePackageName`. | 377 | :param sourcepackagename: An `ISourcePackageName`. |
511 | 369 | :param distroseries: An `IDistroSeries`. | 378 | :param distroseries: An `IDistroSeries`. |
512 | 379 | :param channel: A channel name or None. | ||
513 | 370 | :return: `ISourcePackage`. | 380 | :return: `ISourcePackage`. |
514 | 371 | """ | 381 | """ |
515 | 372 | 382 | ||
516 | diff --git a/lib/lp/registry/model/distroseries.py b/lib/lp/registry/model/distroseries.py | |||
517 | index 7844249..a128ed5 100644 | |||
518 | --- a/lib/lp/registry/model/distroseries.py | |||
519 | +++ b/lib/lp/registry/model/distroseries.py | |||
520 | @@ -13,6 +13,7 @@ __all__ = [ | |||
521 | 13 | import collections | 13 | import collections |
522 | 14 | from io import BytesIO | 14 | from io import BytesIO |
523 | 15 | from operator import itemgetter | 15 | from operator import itemgetter |
524 | 16 | from typing import Optional | ||
525 | 16 | 17 | ||
526 | 17 | import apt_pkg | 18 | import apt_pkg |
527 | 18 | from lazr.delegates import delegate_to | 19 | from lazr.delegates import delegate_to |
528 | @@ -1036,7 +1037,7 @@ class DistroSeries( | |||
529 | 1036 | self.messagecount = messagecount | 1037 | self.messagecount = messagecount |
530 | 1037 | ztm.commit() | 1038 | ztm.commit() |
531 | 1038 | 1039 | ||
533 | 1039 | def getSourcePackage(self, name): | 1040 | def getSourcePackage(self, name, channel: Optional[str] = None): |
534 | 1040 | """See `IDistroSeries`.""" | 1041 | """See `IDistroSeries`.""" |
535 | 1041 | if not ISourcePackageName.providedBy(name): | 1042 | if not ISourcePackageName.providedBy(name): |
536 | 1042 | try: | 1043 | try: |
537 | @@ -1044,7 +1045,7 @@ class DistroSeries( | |||
538 | 1044 | except SQLObjectNotFound: | 1045 | except SQLObjectNotFound: |
539 | 1045 | return None | 1046 | return None |
540 | 1046 | return getUtility(ISourcePackageFactory).new( | 1047 | return getUtility(ISourcePackageFactory).new( |
542 | 1047 | sourcepackagename=name, distroseries=self | 1048 | sourcepackagename=name, distroseries=self, channel=channel |
543 | 1048 | ) | 1049 | ) |
544 | 1049 | 1050 | ||
545 | 1050 | def getBinaryPackage(self, name): | 1051 | def getBinaryPackage(self, name): |
546 | diff --git a/lib/lp/registry/model/sourcepackage.py b/lib/lp/registry/model/sourcepackage.py | |||
547 | index 8a0280b..5aff937 100644 | |||
548 | --- a/lib/lp/registry/model/sourcepackage.py | |||
549 | +++ b/lib/lp/registry/model/sourcepackage.py | |||
550 | @@ -8,7 +8,9 @@ __all__ = [ | |||
551 | 8 | "SourcePackageQuestionTargetMixin", | 8 | "SourcePackageQuestionTargetMixin", |
552 | 9 | ] | 9 | ] |
553 | 10 | 10 | ||
554 | 11 | import json | ||
555 | 11 | from operator import attrgetter, itemgetter | 12 | from operator import attrgetter, itemgetter |
556 | 13 | from typing import Optional | ||
557 | 12 | 14 | ||
558 | 13 | from storm.locals import And, Desc, Join, Store | 15 | from storm.locals import And, Desc, Join, Store |
559 | 14 | from zope.component import getUtility | 16 | from zope.component import getUtility |
560 | @@ -42,6 +44,7 @@ from lp.registry.interfaces.sourcepackage import ( | |||
561 | 42 | from lp.registry.model.hasdrivers import HasDriversMixin | 44 | from lp.registry.model.hasdrivers import HasDriversMixin |
562 | 43 | from lp.registry.model.packaging import Packaging | 45 | from lp.registry.model.packaging import Packaging |
563 | 44 | from lp.registry.model.suitesourcepackage import SuiteSourcePackage | 46 | from lp.registry.model.suitesourcepackage import SuiteSourcePackage |
564 | 47 | from lp.services.channels import channel_string_to_list | ||
565 | 45 | from lp.services.database.decoratedresultset import DecoratedResultSet | 48 | from lp.services.database.decoratedresultset import DecoratedResultSet |
566 | 46 | from lp.services.database.interfaces import IStore | 49 | from lp.services.database.interfaces import IStore |
567 | 47 | from lp.services.database.sqlbase import flush_database_updates, sqlvalues | 50 | from lp.services.database.sqlbase import flush_database_updates, sqlvalues |
568 | @@ -204,7 +207,9 @@ class SourcePackage( | |||
569 | 204 | to the relevant database objects. | 207 | to the relevant database objects. |
570 | 205 | """ | 208 | """ |
571 | 206 | 209 | ||
573 | 207 | def __init__(self, sourcepackagename, distroseries): | 210 | def __init__( |
574 | 211 | self, sourcepackagename, distroseries, channel: Optional[str] = None | ||
575 | 212 | ): | ||
576 | 208 | # We store the ID of the sourcepackagename and distroseries | 213 | # We store the ID of the sourcepackagename and distroseries |
577 | 209 | # simply because Storm can break when accessing them | 214 | # simply because Storm can break when accessing them |
578 | 210 | # with implicit flush is blocked (like in a permission check when | 215 | # with implicit flush is blocked (like in a permission check when |
579 | @@ -213,11 +218,14 @@ class SourcePackage( | |||
580 | 213 | self.sourcepackagename = sourcepackagename | 218 | self.sourcepackagename = sourcepackagename |
581 | 214 | self.distroseries = distroseries | 219 | self.distroseries = distroseries |
582 | 215 | self.distroseriesID = distroseries.id | 220 | self.distroseriesID = distroseries.id |
583 | 221 | self.channel = channel | ||
584 | 216 | 222 | ||
585 | 217 | @classmethod | 223 | @classmethod |
587 | 218 | def new(cls, sourcepackagename, distroseries): | 224 | def new( |
588 | 225 | cls, sourcepackagename, distroseries, channel: Optional[str] = None | ||
589 | 226 | ): | ||
590 | 219 | """See `ISourcePackageFactory`.""" | 227 | """See `ISourcePackageFactory`.""" |
592 | 220 | return cls(sourcepackagename, distroseries) | 228 | return cls(sourcepackagename, distroseries, channel=channel) |
593 | 221 | 229 | ||
594 | 222 | def __repr__(self): | 230 | def __repr__(self): |
595 | 223 | return "<%s %r %r %r>" % ( | 231 | return "<%s %r %r %r>" % ( |
596 | @@ -242,6 +250,12 @@ class SourcePackage( | |||
597 | 242 | == self.sourcepackagename, | 250 | == self.sourcepackagename, |
598 | 243 | SourcePackagePublishingHistory.distroseries | 251 | SourcePackagePublishingHistory.distroseries |
599 | 244 | == self.distroseries, | 252 | == self.distroseries, |
600 | 253 | SourcePackagePublishingHistory._channel | ||
601 | 254 | == ( | ||
602 | 255 | None | ||
603 | 256 | if self.channel is None | ||
604 | 257 | else channel_string_to_list(self.channel) | ||
605 | 258 | ), | ||
606 | 245 | SourcePackagePublishingHistory.archiveID.is_in( | 259 | SourcePackagePublishingHistory.archiveID.is_in( |
607 | 246 | self.distribution.all_distro_archive_ids | 260 | self.distribution.all_distro_archive_ids |
608 | 247 | ), | 261 | ), |
609 | @@ -296,24 +310,27 @@ class SourcePackage( | |||
610 | 296 | ) | 310 | ) |
611 | 297 | 311 | ||
612 | 298 | @property | 312 | @property |
613 | 313 | def series_name(self): | ||
614 | 314 | series_name = self.distroseries.fullseriesname | ||
615 | 315 | if self.channel is not None: | ||
616 | 316 | series_name = "%s, %s" % (series_name, self.channel) | ||
617 | 317 | return series_name | ||
618 | 318 | |||
619 | 319 | @property | ||
620 | 299 | def display_name(self): | 320 | def display_name(self): |
626 | 300 | return "%s in %s %s" % ( | 321 | return "%s in %s" % (self.sourcepackagename.name, self.series_name) |
622 | 301 | self.sourcepackagename.name, | ||
623 | 302 | self.distribution.displayname, | ||
624 | 303 | self.distroseries.displayname, | ||
625 | 304 | ) | ||
627 | 305 | 322 | ||
628 | 306 | displayname = display_name | 323 | displayname = display_name |
629 | 307 | 324 | ||
630 | 308 | @property | 325 | @property |
631 | 309 | def bugtargetdisplayname(self): | 326 | def bugtargetdisplayname(self): |
632 | 310 | """See IBugTarget.""" | 327 | """See IBugTarget.""" |
634 | 311 | return "%s (%s)" % (self.name, self.distroseries.fullseriesname) | 328 | return "%s (%s)" % (self.name, self.series_name) |
635 | 312 | 329 | ||
636 | 313 | @property | 330 | @property |
637 | 314 | def bugtargetname(self): | 331 | def bugtargetname(self): |
638 | 315 | """See `IBugTarget`.""" | 332 | """See `IBugTarget`.""" |
640 | 316 | return "%s (%s)" % (self.name, self.distroseries.fullseriesname) | 333 | return "%s (%s)" % (self.name, self.series_name) |
641 | 317 | 334 | ||
642 | 318 | @property | 335 | @property |
643 | 319 | def bugtarget_parent(self): | 336 | def bugtarget_parent(self): |
644 | @@ -551,6 +568,12 @@ class SourcePackage( | |||
645 | 551 | return And( | 568 | return And( |
646 | 552 | BugSummary.distroseries == self.distroseries, | 569 | BugSummary.distroseries == self.distroseries, |
647 | 553 | BugSummary.sourcepackagename == self.sourcepackagename, | 570 | BugSummary.sourcepackagename == self.sourcepackagename, |
648 | 571 | BugSummary._channel | ||
649 | 572 | == ( | ||
650 | 573 | None | ||
651 | 574 | if self.channel is None | ||
652 | 575 | else channel_string_to_list(self.channel) | ||
653 | 576 | ), | ||
654 | 554 | ) | 577 | ) |
655 | 555 | 578 | ||
656 | 556 | def setPackaging(self, productseries, owner): | 579 | def setPackaging(self, productseries, owner): |
657 | @@ -616,7 +639,11 @@ class SourcePackage( | |||
658 | 616 | 639 | ||
659 | 617 | def __hash__(self): | 640 | def __hash__(self): |
660 | 618 | """See `ISourcePackage`.""" | 641 | """See `ISourcePackage`.""" |
662 | 619 | return hash(self.distroseriesID) ^ hash(self.sourcepackagenameID) | 642 | return ( |
663 | 643 | hash(self.distroseriesID) | ||
664 | 644 | ^ hash(self.sourcepackagenameID) | ||
665 | 645 | ^ hash(self.channel) | ||
666 | 646 | ) | ||
667 | 620 | 647 | ||
668 | 621 | def __eq__(self, other): | 648 | def __eq__(self, other): |
669 | 622 | """See `ISourcePackage`.""" | 649 | """See `ISourcePackage`.""" |
670 | @@ -624,6 +651,7 @@ class SourcePackage( | |||
671 | 624 | (ISourcePackage.providedBy(other)) | 651 | (ISourcePackage.providedBy(other)) |
672 | 625 | and (self.distroseries.id == other.distroseries.id) | 652 | and (self.distroseries.id == other.distroseries.id) |
673 | 626 | and (self.sourcepackagename.id == other.sourcepackagename.id) | 653 | and (self.sourcepackagename.id == other.sourcepackagename.id) |
674 | 654 | and (self.channel == other.channel) | ||
675 | 627 | ) | 655 | ) |
676 | 628 | 656 | ||
677 | 629 | def __ne__(self, other): | 657 | def __ne__(self, other): |
678 | @@ -672,6 +700,16 @@ class SourcePackage( | |||
679 | 672 | ) | 700 | ) |
680 | 673 | ] | 701 | ] |
681 | 674 | 702 | ||
682 | 703 | if self.channel is None: | ||
683 | 704 | condition_clauses.append( | ||
684 | 705 | "SourcePackagePublishingHistory.channel IS NULL" | ||
685 | 706 | ) | ||
686 | 707 | else: | ||
687 | 708 | condition_clauses.append( | ||
688 | 709 | "SourcePackagePublishingHistory.channel = '%s'::jsonb" | ||
689 | 710 | % json.dumps(channel_string_to_list(self.channel)) | ||
690 | 711 | ) | ||
691 | 712 | |||
692 | 675 | # We re-use the optional-parameter handling provided by BuildSet | 713 | # We re-use the optional-parameter handling provided by BuildSet |
693 | 676 | # here, but pass None for the name argument as we've already | 714 | # here, but pass None for the name argument as we've already |
694 | 677 | # matched on exact source package name. | 715 | # matched on exact source package name. |
695 | @@ -882,16 +920,26 @@ class SourcePackage( | |||
696 | 882 | 920 | ||
697 | 883 | def weight_function(bugtask): | 921 | def weight_function(bugtask): |
698 | 884 | if bugtask.sourcepackagename_id == sourcepackagenameID: | 922 | if bugtask.sourcepackagename_id == sourcepackagenameID: |
700 | 885 | if bugtask.distroseries_id == seriesID: | 923 | if ( |
701 | 924 | bugtask.distroseries_id == seriesID | ||
702 | 925 | and bugtask.channel == self.channel | ||
703 | 926 | ): | ||
704 | 886 | return OrderedBugTask(1, bugtask.id, bugtask) | 927 | return OrderedBugTask(1, bugtask.id, bugtask) |
706 | 887 | elif bugtask.distribution_id == distributionID: | 928 | elif bugtask.distroseries_id == seriesID: |
707 | 888 | return OrderedBugTask(2, bugtask.id, bugtask) | 929 | return OrderedBugTask(2, bugtask.id, bugtask) |
708 | 930 | elif bugtask.distribution_id == distributionID: | ||
709 | 931 | return OrderedBugTask(3, bugtask.id, bugtask) | ||
710 | 932 | elif ( | ||
711 | 933 | bugtask.distroseries_id == seriesID | ||
712 | 934 | and bugtask.channel == self.channel | ||
713 | 935 | ): | ||
714 | 936 | return OrderedBugTask(4, bugtask.id, bugtask) | ||
715 | 889 | elif bugtask.distroseries_id == seriesID: | 937 | elif bugtask.distroseries_id == seriesID: |
717 | 890 | return OrderedBugTask(3, bugtask.id, bugtask) | 938 | return OrderedBugTask(5, bugtask.id, bugtask) |
718 | 891 | elif bugtask.distribution_id == distributionID: | 939 | elif bugtask.distribution_id == distributionID: |
720 | 892 | return OrderedBugTask(4, bugtask.id, bugtask) | 940 | return OrderedBugTask(6, bugtask.id, bugtask) |
721 | 893 | # Catch the default case, and where there is a task for the same | 941 | # Catch the default case, and where there is a task for the same |
722 | 894 | # sourcepackage on a different distro. | 942 | # sourcepackage on a different distro. |
724 | 895 | return OrderedBugTask(5, bugtask.id, bugtask) | 943 | return OrderedBugTask(7, bugtask.id, bugtask) |
725 | 896 | 944 | ||
726 | 897 | return weight_function | 945 | return weight_function |
727 | diff --git a/lib/lp/registry/stories/webservice/xx-source-package.rst b/lib/lp/registry/stories/webservice/xx-source-package.rst | |||
728 | index d09785d..b5c17bc 100644 | |||
729 | --- a/lib/lp/registry/stories/webservice/xx-source-package.rst | |||
730 | +++ b/lib/lp/registry/stories/webservice/xx-source-package.rst | |||
731 | @@ -32,6 +32,7 @@ distribution series. | |||
732 | 32 | >>> pprint_entry(evolution) | 32 | >>> pprint_entry(evolution) |
733 | 33 | bug_reported_acknowledgement: None | 33 | bug_reported_acknowledgement: None |
734 | 34 | bug_reporting_guidelines: None | 34 | bug_reporting_guidelines: None |
735 | 35 | channel: None | ||
736 | 35 | displayname: 'evolution in My-distro My-series' | 36 | displayname: 'evolution in My-distro My-series' |
737 | 36 | distribution_link: 'http://.../my-distro' | 37 | distribution_link: 'http://.../my-distro' |
738 | 37 | distroseries_link: 'http://.../my-distro/my-series' | 38 | distroseries_link: 'http://.../my-distro/my-series' |
739 | diff --git a/lib/lp/registry/tests/test_distributionsourcepackage.py b/lib/lp/registry/tests/test_distributionsourcepackage.py | |||
740 | index d8dffd9..6262cb4 100644 | |||
741 | --- a/lib/lp/registry/tests/test_distributionsourcepackage.py | |||
742 | +++ b/lib/lp/registry/tests/test_distributionsourcepackage.py | |||
743 | @@ -49,7 +49,7 @@ class TestDistributionSourcePackage(TestCaseWithFactory): | |||
744 | 49 | registrant=self.factory.makePerson(), | 49 | registrant=self.factory.makePerson(), |
745 | 50 | ) | 50 | ) |
746 | 51 | naked_distribution = removeSecurityProxy(distribution) | 51 | naked_distribution = removeSecurityProxy(distribution) |
748 | 52 | self.factory.makeSourcePackage(distroseries=distribution) | 52 | self.factory.makeDistributionSourcePackage(distribution=distribution) |
749 | 53 | dsp = naked_distribution.getSourcePackage(name="pmount") | 53 | dsp = naked_distribution.getSourcePackage(name="pmount") |
750 | 54 | self.assertEqual(None, dsp.summary) | 54 | self.assertEqual(None, dsp.summary) |
751 | 55 | 55 | ||
752 | diff --git a/lib/lp/registry/tests/test_distroseries.py b/lib/lp/registry/tests/test_distroseries.py | |||
753 | index ab3c6bf..168386f 100644 | |||
754 | --- a/lib/lp/registry/tests/test_distroseries.py | |||
755 | +++ b/lib/lp/registry/tests/test_distroseries.py | |||
756 | @@ -449,6 +449,12 @@ class TestDistroSeries(TestCaseWithFactory): | |||
757 | 449 | ] | 449 | ] |
758 | 450 | ) | 450 | ) |
759 | 451 | 451 | ||
760 | 452 | def test_getSourcePackage_channel(self): | ||
761 | 453 | distroseries = self.factory.makeDistroSeries() | ||
762 | 454 | spn = self.factory.makeSourcePackageName() | ||
763 | 455 | source_package = distroseries.getSourcePackage(spn, channel="stable") | ||
764 | 456 | self.assertEqual("stable", source_package.channel) | ||
765 | 457 | |||
766 | 452 | 458 | ||
767 | 453 | class TestDistroSeriesPackaging(TestCaseWithFactory): | 459 | class TestDistroSeriesPackaging(TestCaseWithFactory): |
768 | 454 | 460 | ||
769 | diff --git a/lib/lp/registry/tests/test_sourcepackage.py b/lib/lp/registry/tests/test_sourcepackage.py | |||
770 | index b839a18..a74f23a 100644 | |||
771 | --- a/lib/lp/registry/tests/test_sourcepackage.py | |||
772 | +++ b/lib/lp/registry/tests/test_sourcepackage.py | |||
773 | @@ -506,6 +506,66 @@ class TestSourcePackage(TestCaseWithFactory): | |||
774 | 506 | sourcepackage.personHasDriverRights(distroseries.owner) | 506 | sourcepackage.personHasDriverRights(distroseries.owner) |
775 | 507 | ) | 507 | ) |
776 | 508 | 508 | ||
777 | 509 | def test_channel(self): | ||
778 | 510 | source_package = self.factory.makeSourcePackage(channel="stable") | ||
779 | 511 | self.assertEqual("stable", source_package.channel) | ||
780 | 512 | |||
781 | 513 | def test_hash(self): | ||
782 | 514 | spn = self.factory.makeSourcePackageName() | ||
783 | 515 | distroseries = self.factory.makeDistroSeries() | ||
784 | 516 | self.assertEqual( | ||
785 | 517 | hash( | ||
786 | 518 | self.factory.makeSourcePackage( | ||
787 | 519 | sourcepackagename=spn, distroseries=distroseries | ||
788 | 520 | ) | ||
789 | 521 | ), | ||
790 | 522 | hash( | ||
791 | 523 | self.factory.makeSourcePackage( | ||
792 | 524 | sourcepackagename=spn, distroseries=distroseries | ||
793 | 525 | ) | ||
794 | 526 | ), | ||
795 | 527 | ) | ||
796 | 528 | self.assertNotEqual( | ||
797 | 529 | hash( | ||
798 | 530 | self.factory.makeSourcePackage( | ||
799 | 531 | sourcepackagename=spn, | ||
800 | 532 | distroseries=distroseries, | ||
801 | 533 | channel="stable", | ||
802 | 534 | ) | ||
803 | 535 | ), | ||
804 | 536 | hash( | ||
805 | 537 | self.factory.makeSourcePackage( | ||
806 | 538 | sourcepackagename=spn, | ||
807 | 539 | distroseries=distroseries, | ||
808 | 540 | channel="beta", | ||
809 | 541 | ) | ||
810 | 542 | ), | ||
811 | 543 | ) | ||
812 | 544 | |||
813 | 545 | def test_eq(self): | ||
814 | 546 | spn = self.factory.makeSourcePackageName() | ||
815 | 547 | distroseries = self.factory.makeDistroSeries() | ||
816 | 548 | self.assertEqual( | ||
817 | 549 | self.factory.makeSourcePackage( | ||
818 | 550 | sourcepackagename=spn, distroseries=distroseries | ||
819 | 551 | ), | ||
820 | 552 | self.factory.makeSourcePackage( | ||
821 | 553 | sourcepackagename=spn, distroseries=distroseries | ||
822 | 554 | ), | ||
823 | 555 | ) | ||
824 | 556 | self.assertNotEqual( | ||
825 | 557 | self.factory.makeSourcePackage( | ||
826 | 558 | sourcepackagename=spn, | ||
827 | 559 | distroseries=distroseries, | ||
828 | 560 | channel="stable", | ||
829 | 561 | ), | ||
830 | 562 | self.factory.makeSourcePackage( | ||
831 | 563 | sourcepackagename=spn, | ||
832 | 564 | distroseries=distroseries, | ||
833 | 565 | channel="beta", | ||
834 | 566 | ), | ||
835 | 567 | ) | ||
836 | 568 | |||
837 | 509 | 569 | ||
838 | 510 | class TestSourcePackageWebService(WebServiceTestCase): | 570 | class TestSourcePackageWebService(WebServiceTestCase): |
839 | 511 | def test_setPackaging(self): | 571 | def test_setPackaging(self): |
840 | diff --git a/lib/lp/soyuz/tests/test_binarypackagebuild.py b/lib/lp/soyuz/tests/test_binarypackagebuild.py | |||
841 | index ef513d3..939e6c1 100644 | |||
842 | --- a/lib/lp/soyuz/tests/test_binarypackagebuild.py | |||
843 | +++ b/lib/lp/soyuz/tests/test_binarypackagebuild.py | |||
844 | @@ -433,13 +433,19 @@ class BaseTestCaseWithThreeBuilds(TestCaseWithFactory): | |||
845 | 433 | """Publish some builds for the test archive.""" | 433 | """Publish some builds for the test archive.""" |
846 | 434 | super().setUp() | 434 | super().setUp() |
847 | 435 | self.ds = self.factory.makeDistroSeries() | 435 | self.ds = self.factory.makeDistroSeries() |
848 | 436 | self.builds = self.makeBuilds() | ||
849 | 437 | self.sources = [ | ||
850 | 438 | build.current_source_publication for build in self.builds | ||
851 | 439 | ] | ||
852 | 440 | |||
853 | 441 | def makeBuilds(self): | ||
854 | 436 | i386_das = self.factory.makeDistroArchSeries( | 442 | i386_das = self.factory.makeDistroArchSeries( |
855 | 437 | distroseries=self.ds, architecturetag="i386" | 443 | distroseries=self.ds, architecturetag="i386" |
856 | 438 | ) | 444 | ) |
857 | 439 | hppa_das = self.factory.makeDistroArchSeries( | 445 | hppa_das = self.factory.makeDistroArchSeries( |
858 | 440 | distroseries=self.ds, architecturetag="hppa" | 446 | distroseries=self.ds, architecturetag="hppa" |
859 | 441 | ) | 447 | ) |
861 | 442 | self.builds = [ | 448 | return [ |
862 | 443 | self.factory.makeBinaryPackageBuild( | 449 | self.factory.makeBinaryPackageBuild( |
863 | 444 | archive=self.ds.main_archive, distroarchseries=i386_das | 450 | archive=self.ds.main_archive, distroarchseries=i386_das |
864 | 445 | ), | 451 | ), |
865 | @@ -449,12 +455,10 @@ class BaseTestCaseWithThreeBuilds(TestCaseWithFactory): | |||
866 | 449 | pocket=PackagePublishingPocket.PROPOSED, | 455 | pocket=PackagePublishingPocket.PROPOSED, |
867 | 450 | ), | 456 | ), |
868 | 451 | self.factory.makeBinaryPackageBuild( | 457 | self.factory.makeBinaryPackageBuild( |
870 | 452 | archive=self.ds.main_archive, distroarchseries=hppa_das | 458 | archive=self.ds.main_archive, |
871 | 459 | distroarchseries=hppa_das, | ||
872 | 453 | ), | 460 | ), |
873 | 454 | ] | 461 | ] |
874 | 455 | self.sources = [ | ||
875 | 456 | build.current_source_publication for build in self.builds | ||
876 | 457 | ] | ||
877 | 458 | 462 | ||
878 | 459 | 463 | ||
879 | 460 | class TestBuildSet(TestCaseWithFactory): | 464 | class TestBuildSet(TestCaseWithFactory): |
880 | diff --git a/lib/lp/soyuz/tests/test_hasbuildrecords.py b/lib/lp/soyuz/tests/test_hasbuildrecords.py | |||
881 | index b0b8e8c..07c5c26 100644 | |||
882 | --- a/lib/lp/soyuz/tests/test_hasbuildrecords.py | |||
883 | +++ b/lib/lp/soyuz/tests/test_hasbuildrecords.py | |||
884 | @@ -2,7 +2,7 @@ | |||
885 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
886 | 3 | 3 | ||
887 | 4 | """Test implementations of the IHasBuildRecords interface.""" | 4 | """Test implementations of the IHasBuildRecords interface.""" |
889 | 5 | 5 | from testscenarios import WithScenarios | |
890 | 6 | from zope.component import getUtility | 6 | from zope.component import getUtility |
891 | 7 | from zope.security.proxy import removeSecurityProxy | 7 | from zope.security.proxy import removeSecurityProxy |
892 | 8 | 8 | ||
893 | @@ -14,12 +14,11 @@ from lp.buildmaster.interfaces.buildfarmjob import ( | |||
894 | 14 | ) | 14 | ) |
895 | 15 | from lp.registry.interfaces.person import IPersonSet | 15 | from lp.registry.interfaces.person import IPersonSet |
896 | 16 | from lp.registry.interfaces.pocket import PackagePublishingPocket | 16 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
897 | 17 | from lp.registry.interfaces.sourcepackage import SourcePackageType | ||
898 | 17 | from lp.registry.model.sourcepackage import SourcePackage | 18 | from lp.registry.model.sourcepackage import SourcePackage |
899 | 18 | from lp.services.database.interfaces import IStore | ||
900 | 19 | from lp.soyuz.enums import ArchivePurpose | 19 | from lp.soyuz.enums import ArchivePurpose |
901 | 20 | from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuild | 20 | from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuild |
902 | 21 | from lp.soyuz.interfaces.buildrecords import IHasBuildRecords | 21 | from lp.soyuz.interfaces.buildrecords import IHasBuildRecords |
903 | 22 | from lp.soyuz.model.publishing import SourcePackagePublishingHistory | ||
904 | 23 | from lp.soyuz.tests.test_binarypackagebuild import BaseTestCaseWithThreeBuilds | 22 | from lp.soyuz.tests.test_binarypackagebuild import BaseTestCaseWithThreeBuilds |
905 | 24 | from lp.soyuz.tests.test_publishing import SoyuzTestPublisher | 23 | from lp.soyuz.tests.test_publishing import SoyuzTestPublisher |
906 | 25 | from lp.testing import TestCaseWithFactory, person_logged_in | 24 | from lp.testing import TestCaseWithFactory, person_logged_in |
907 | @@ -256,30 +255,64 @@ class TestBuilderHasBuildRecords(TestHasBuildRecordsInterface): | |||
908 | 256 | ) | 255 | ) |
909 | 257 | 256 | ||
910 | 258 | 257 | ||
912 | 259 | class TestSourcePackageHasBuildRecords(TestHasBuildRecordsInterface): | 258 | class TestSourcePackageHasBuildRecords( |
913 | 259 | WithScenarios, TestHasBuildRecordsInterface | ||
914 | 260 | ): | ||
915 | 260 | """Test the SourcePackage implementation of IHasBuildRecords.""" | 261 | """Test the SourcePackage implementation of IHasBuildRecords.""" |
916 | 261 | 262 | ||
917 | 263 | scenarios = [ | ||
918 | 264 | ( | ||
919 | 265 | "channel", | ||
920 | 266 | {"channel": "stable", "format": SourcePackageType.CI_BUILD}, | ||
921 | 267 | ), | ||
922 | 268 | ("no_channel", {"channel": None, "format": None}), | ||
923 | 269 | ] | ||
924 | 270 | |||
925 | 262 | def setUp(self): | 271 | def setUp(self): |
926 | 263 | super().setUp() | 272 | super().setUp() |
927 | 264 | gedit_name = self.builds[0].source_package_release.sourcepackagename | 273 | gedit_name = self.builds[0].source_package_release.sourcepackagename |
928 | 265 | self.context = SourcePackage( | 274 | self.context = SourcePackage( |
930 | 266 | gedit_name, self.builds[0].distro_arch_series.distroseries | 275 | gedit_name, |
931 | 276 | self.builds[0].distro_arch_series.distroseries, | ||
932 | 277 | channel=self.channel, | ||
933 | 267 | ) | 278 | ) |
934 | 268 | 279 | ||
945 | 269 | # Convert the other two builds to be builds of | 280 | def makeBuilds(self): |
946 | 270 | # gedit as well so that the one source package (gedit) will have | 281 | i386_das = self.factory.makeDistroArchSeries( |
947 | 271 | # three builds. | 282 | distroseries=self.ds, architecturetag="i386" |
948 | 272 | for build in self.builds[1:3]: | 283 | ) |
949 | 273 | spr = build.source_package_release | 284 | hppa_das = self.factory.makeDistroArchSeries( |
950 | 274 | removeSecurityProxy(spr).sourcepackagename = gedit_name | 285 | distroseries=self.ds, architecturetag="hppa" |
951 | 275 | IStore(SourcePackagePublishingHistory).find( | 286 | ) |
952 | 276 | SourcePackagePublishingHistory, sourcepackagerelease=spr | 287 | spn = self.factory.makeSourcePackageName() |
953 | 277 | ).set(sourcepackagenameID=gedit_name.id) | 288 | builds = [ |
954 | 278 | 289 | self.factory.makeBinaryPackageBuild( | |
955 | 290 | sourcepackagename=spn, | ||
956 | 291 | archive=self.ds.main_archive, | ||
957 | 292 | distroarchseries=i386_das, | ||
958 | 293 | channel=self.channel, | ||
959 | 294 | format=self.format, | ||
960 | 295 | ), | ||
961 | 296 | self.factory.makeBinaryPackageBuild( | ||
962 | 297 | sourcepackagename=spn, | ||
963 | 298 | archive=self.ds.main_archive, | ||
964 | 299 | distroarchseries=i386_das, | ||
965 | 300 | channel=self.channel, | ||
966 | 301 | format=self.format, | ||
967 | 302 | ), | ||
968 | 303 | self.factory.makeBinaryPackageBuild( | ||
969 | 304 | sourcepackagename=spn, | ||
970 | 305 | archive=self.ds.main_archive, | ||
971 | 306 | distroarchseries=hppa_das, | ||
972 | 307 | channel=self.channel, | ||
973 | 308 | format=self.format, | ||
974 | 309 | ), | ||
975 | 310 | ] | ||
976 | 279 | # Set them as successfully built | 311 | # Set them as successfully built |
978 | 280 | for build in self.builds: | 312 | for build in builds: |
979 | 281 | build.updateStatus(BuildStatus.BUILDING) | 313 | build.updateStatus(BuildStatus.BUILDING) |
980 | 282 | build.updateStatus(BuildStatus.FULLYBUILT) | 314 | build.updateStatus(BuildStatus.FULLYBUILT) |
981 | 315 | return builds | ||
982 | 283 | 316 | ||
983 | 284 | def test_get_build_records(self): | 317 | def test_get_build_records(self): |
984 | 285 | # We can fetch builds records from a SourcePackage. | 318 | # We can fetch builds records from a SourcePackage. |
985 | @@ -290,7 +323,7 @@ class TestSourcePackageHasBuildRecords(TestHasBuildRecordsInterface): | |||
986 | 290 | builds = self.context.getBuildRecords( | 323 | builds = self.context.getBuildRecords( |
987 | 291 | pocket=PackagePublishingPocket.RELEASE | 324 | pocket=PackagePublishingPocket.RELEASE |
988 | 292 | ).count() | 325 | ).count() |
990 | 293 | self.assertEqual(2, builds) | 326 | self.assertEqual(3, builds) |
991 | 294 | builds = self.context.getBuildRecords( | 327 | builds = self.context.getBuildRecords( |
992 | 295 | pocket=PackagePublishingPocket.UPDATES | 328 | pocket=PackagePublishingPocket.UPDATES |
993 | 296 | ).count() | 329 | ).count() |
994 | diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py | |||
995 | index 40a959c..b83a946 100644 | |||
996 | --- a/lib/lp/testing/factory.py | |||
997 | +++ b/lib/lp/testing/factory.py | |||
998 | @@ -31,6 +31,7 @@ from functools import wraps | |||
999 | 31 | from io import BytesIO | 31 | from io import BytesIO |
1000 | 32 | from itertools import count | 32 | from itertools import count |
1001 | 33 | from textwrap import dedent | 33 | from textwrap import dedent |
1002 | 34 | from typing import Optional | ||
1003 | 34 | 35 | ||
1004 | 35 | import pytz | 36 | import pytz |
1005 | 36 | import six | 37 | import six |
1006 | @@ -2385,6 +2386,7 @@ class LaunchpadObjectFactory(ObjectFactory): | |||
1007 | 2385 | self.makeSourcePackagePublishingHistory( | 2386 | self.makeSourcePackagePublishingHistory( |
1008 | 2386 | distroseries=target.distroseries, | 2387 | distroseries=target.distroseries, |
1009 | 2387 | sourcepackagename=target.sourcepackagename, | 2388 | sourcepackagename=target.sourcepackagename, |
1010 | 2389 | channel=target.channel, | ||
1011 | 2388 | ) | 2390 | ) |
1012 | 2389 | if IDistributionSourcePackage.providedBy(target): | 2391 | if IDistributionSourcePackage.providedBy(target): |
1013 | 2390 | if publish: | 2392 | if publish: |
1014 | @@ -4575,6 +4577,7 @@ class LaunchpadObjectFactory(ObjectFactory): | |||
1015 | 4575 | distroseries=None, | 4577 | distroseries=None, |
1016 | 4576 | publish=False, | 4578 | publish=False, |
1017 | 4577 | owner=None, | 4579 | owner=None, |
1018 | 4580 | channel: Optional[str] = None, | ||
1019 | 4578 | ): | 4581 | ): |
1020 | 4579 | """Make an `ISourcePackage`. | 4582 | """Make an `ISourcePackage`. |
1021 | 4580 | 4583 | ||
1022 | @@ -4590,7 +4593,9 @@ class LaunchpadObjectFactory(ObjectFactory): | |||
1023 | 4590 | distroseries = self.makeDistroSeries(owner=owner) | 4593 | distroseries = self.makeDistroSeries(owner=owner) |
1024 | 4591 | if publish: | 4594 | if publish: |
1025 | 4592 | self.makeSourcePackagePublishingHistory( | 4595 | self.makeSourcePackagePublishingHistory( |
1027 | 4593 | distroseries=distroseries, sourcepackagename=sourcepackagename | 4596 | distroseries=distroseries, |
1028 | 4597 | sourcepackagename=sourcepackagename, | ||
1029 | 4598 | channel=channel, | ||
1030 | 4594 | ) | 4599 | ) |
1031 | 4595 | with dbuser("statistician"): | 4600 | with dbuser("statistician"): |
1032 | 4596 | DistributionSourcePackageCache( | 4601 | DistributionSourcePackageCache( |
1033 | @@ -4599,7 +4604,9 @@ class LaunchpadObjectFactory(ObjectFactory): | |||
1034 | 4599 | archive=distroseries.main_archive, | 4604 | archive=distroseries.main_archive, |
1035 | 4600 | name=sourcepackagename.name, | 4605 | name=sourcepackagename.name, |
1036 | 4601 | ) | 4606 | ) |
1038 | 4602 | return distroseries.getSourcePackage(sourcepackagename) | 4607 | return distroseries.getSourcePackage( |
1039 | 4608 | sourcepackagename, channel=channel | ||
1040 | 4609 | ) | ||
1041 | 4603 | 4610 | ||
1042 | 4604 | def getAnySourcePackageUrgency(self): | 4611 | def getAnySourcePackageUrgency(self): |
1043 | 4605 | return SourcePackageUrgency.MEDIUM | 4612 | return SourcePackageUrgency.MEDIUM |
1044 | @@ -4892,6 +4899,8 @@ class LaunchpadObjectFactory(ObjectFactory): | |||
1045 | 4892 | processor=None, | 4899 | processor=None, |
1046 | 4893 | sourcepackagename=None, | 4900 | sourcepackagename=None, |
1047 | 4894 | arch_indep=None, | 4901 | arch_indep=None, |
1048 | 4902 | channel=None, | ||
1049 | 4903 | format=None, | ||
1050 | 4895 | ): | 4904 | ): |
1051 | 4896 | """Create a BinaryPackageBuild. | 4905 | """Create a BinaryPackageBuild. |
1052 | 4897 | 4906 | ||
1053 | @@ -4950,12 +4959,14 @@ class LaunchpadObjectFactory(ObjectFactory): | |||
1054 | 4950 | component=multiverse, | 4959 | component=multiverse, |
1055 | 4951 | distroseries=distroarchseries.distroseries, | 4960 | distroseries=distroarchseries.distroseries, |
1056 | 4952 | sourcepackagename=sourcepackagename, | 4961 | sourcepackagename=sourcepackagename, |
1057 | 4962 | format=format, | ||
1058 | 4953 | ) | 4963 | ) |
1059 | 4954 | self.makeSourcePackagePublishingHistory( | 4964 | self.makeSourcePackagePublishingHistory( |
1060 | 4955 | distroseries=distroarchseries.distroseries, | 4965 | distroseries=distroarchseries.distroseries, |
1061 | 4956 | archive=archive, | 4966 | archive=archive, |
1062 | 4957 | sourcepackagerelease=source_package_release, | 4967 | sourcepackagerelease=source_package_release, |
1063 | 4958 | pocket=pocket, | 4968 | pocket=pocket, |
1064 | 4969 | channel=channel, | ||
1065 | 4959 | ) | 4970 | ) |
1066 | 4960 | if status is None: | 4971 | if status is None: |
1067 | 4961 | status = BuildStatus.NEEDSBUILD | 4972 | status = BuildStatus.NEEDSBUILD |