Merge lp:~brian-murray/launchpad/bug-546078 into lp:launchpad
- bug-546078
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Deryck Hodge |
Approved revision: | no longer in the source branch. |
Merged at revision: | 10959 |
Proposed branch: | lp:~brian-murray/launchpad/bug-546078 |
Merge into: | lp:launchpad |
Diff against target: |
687 lines (+357/-35) 11 files modified
lib/lp/bugs/browser/tests/test_bugtarget_patches_view.py (+3/-3) lib/lp/bugs/doc/bugtask-search.txt (+170/-3) lib/lp/bugs/interfaces/bug.py (+1/-1) lib/lp/bugs/interfaces/bugtarget.py (+3/-1) lib/lp/bugs/interfaces/bugtask.py (+6/-3) lib/lp/bugs/model/bugtarget.py (+1/-0) lib/lp/bugs/model/bugtask.py (+45/-2) lib/lp/bugs/stories/patches-view/patches-view.txt (+101/-7) lib/lp/bugs/stories/structural-subscriptions/xx-bug-subscriptions.txt (+1/-1) lib/lp/bugs/stories/webservice/xx-bug.txt (+6/-5) lib/lp/registry/tests/test_person.py (+20/-9) |
To merge this branch: | bzr merge lp:~brian-murray/launchpad/bug-546078 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Eleanor Berger (community) | Approve | ||
Review via email: mp+25740@code.launchpad.net |
Commit message
Description of the change
This branch modifies the +patches view so that bug tasks that a person, or team, are structurally subscribed to appear in the +patches view in addition to those that already appear. This fixes bug 546078.
Additionally, some drive by fixes, for issues I found when reading code, were made:
adding the new expired status to the test_bugtarget_
wording change of expired definition in lib/lp/
wording change of doc string for get_related_
typo fix for distribution drivers in lib/lp/
Tests modified:
lib/lp/
lib/lp/
Eleanor Berger (intellectronica) wrote : | # |
Eleanor Berger (intellectronica) wrote : | # |
As we discussed on IRC, this branch now looks very good. We had one concern, over duplicate results because of the UNION of a subscription to a target and its parent target (like Product and ProjectGroup), so you're going to verify that this isn't the case and fix that if necessary. After that you can land.
Preview Diff
1 | === modified file 'lib/lp/bugs/browser/tests/test_bugtarget_patches_view.py' |
2 | --- lib/lp/bugs/browser/tests/test_bugtarget_patches_view.py 2010-03-11 22:14:51 +0000 |
3 | +++ lib/lp/bugs/browser/tests/test_bugtarget_patches_view.py 2010-06-07 18:13:25 +0000 |
4 | @@ -15,7 +15,6 @@ |
5 | from lp.bugs.interfaces.bugtask import BugTaskStatus |
6 | from lp.testing import TestCaseWithFactory |
7 | |
8 | - |
9 | DISPLAY_BUG_STATUS_FOR_PATCHES = { |
10 | BugTaskStatus.NEW: True, |
11 | BugTaskStatus.INCOMPLETE: True, |
12 | @@ -27,6 +26,7 @@ |
13 | BugTaskStatus.FIXCOMMITTED: True, |
14 | BugTaskStatus.FIXRELEASED: False, |
15 | BugTaskStatus.UNKNOWN: False, |
16 | + BugTaskStatus.EXPIRED: False |
17 | } |
18 | |
19 | |
20 | @@ -54,8 +54,8 @@ |
21 | |
22 | def test_status_of_bugs_with_patches_shown(self): |
23 | # Bugs with patches that have the status FIXRELEASED, INVALID, |
24 | - # WONTFIX, UNKNOWN are not shown in the +patches view; all other |
25 | - # bugs are shown. |
26 | + # WONTFIX, UNKNOWN, EXPIRED are not shown in the +patches view; all |
27 | + # other bugs are shown. |
28 | number_of_bugs_shown = 0 |
29 | for bugtask_status in DISPLAY_BUG_STATUS_FOR_PATCHES: |
30 | if DISPLAY_BUG_STATUS_FOR_PATCHES[bugtask_status]: |
31 | |
32 | === modified file 'lib/lp/bugs/doc/bugtask-search.txt' |
33 | --- lib/lp/bugs/doc/bugtask-search.txt 2010-04-12 07:53:48 +0000 |
34 | +++ lib/lp/bugs/doc/bugtask-search.txt 2010-06-07 18:13:25 +0000 |
35 | @@ -92,8 +92,8 @@ |
36 | |
37 | === Product bug supervisor === |
38 | |
39 | -If No Privileges is specified as Firefox' bug supervisor, searching for his |
40 | -bugs return all of Firefox' bugs. |
41 | +If No Privileges is specified as Firefox's bug supervisor, searching for his |
42 | +bugs return all of Firefox's bugs. |
43 | |
44 | >>> login('foo.bar@canonical.com') |
45 | >>> from canonical.launchpad.ftests import syncUpdate |
46 | @@ -989,7 +989,7 @@ |
47 | 4 2 |
48 | 5 1 |
49 | |
50 | -Similary, we can search for bugs that do not have any linked branches. |
51 | +Similarly, we can search for bugs that do not have any linked branches. |
52 | |
53 | >>> from lp.bugs.interfaces.bugtask import BugBranchSearch |
54 | >>> search_params = BugTaskSearchParams( |
55 | @@ -1197,3 +1197,170 @@ |
56 | ... importance=[BugTaskImportance.LOW, BugTaskImportance.MEDIUM])) |
57 | 4 Mozilla Firefox Reflow problems with complex page layouts NEW MEDIUM |
58 | 1 Mozilla Firefox Firefox does not support SVG NEW LOW |
59 | + |
60 | + |
61 | +== Searching by structural subscriber == |
62 | + |
63 | +The 'structural_subscriber' search parameter allows one to search all the bug |
64 | +tasks to which a person is structurally subscribed. A person can be a |
65 | +structural subscriber for a product, a product series, a project, a milestone, |
66 | +a distribution, a distribution series and a distribution source package. No |
67 | +Privileges Person isn't a structural subscriber, so no bug tasks are found: |
68 | + |
69 | + >>> from canonical.launchpad.interfaces import IPersonSet |
70 | + >>> no_priv = getUtility(IPersonSet).getByName('no-priv') |
71 | + >>> no_priv_struct_sub = BugTaskSearchParams( |
72 | + ... user=None, structural_subscriber=no_priv) |
73 | + >>> found_bugtasks = bugtask_set.search(no_priv_struct_sub) |
74 | + >>> found_bugtasks.count() |
75 | + 0 |
76 | + |
77 | +Create a new person and make them a subscriber to all Firefox (product) bug |
78 | +reports. Subsequently, we confirm that they are subscribed to all of the |
79 | +Firefox bug tasks. |
80 | + |
81 | + >>> product_struct_subber = factory.makePerson( |
82 | + ... name='product-struct-subber') |
83 | + >>> firefox.addBugSubscription(product_struct_subber, |
84 | + ... product_struct_subber) |
85 | + <StructuralSubscription at ...> |
86 | + >>> product_struct_sub_search = BugTaskSearchParams( |
87 | + ... user=None, structural_subscriber=product_struct_subber) |
88 | + >>> found_bugtasks = bugtask_set.search(product_struct_sub_search) |
89 | + >>> found_bugtasks.count() |
90 | + 7 |
91 | + |
92 | +Create a new person and subscribe them to all of the bug tasks for a product |
93 | +series. We then test to see that they are subscribed to all of the bug tasks |
94 | +for the product series in which they are interested. |
95 | + |
96 | + >>> product_series = firefox.getSeries('1.0') |
97 | + >>> all_targeted = BugTaskSearchParams(user=None, omit_targeted=False) |
98 | + >>> series_tasks = product_series.searchTasks(all_targeted) |
99 | + >>> series_struct_subber = factory.makePerson( |
100 | + ... name='series-struct-subber') |
101 | + >>> product_series.addBugSubscription(series_struct_subber, |
102 | + ... series_struct_subber) |
103 | + <StructuralSubscription at ...> |
104 | + >>> series_struct_sub_search = BugTaskSearchParams( |
105 | + ... user=None, structural_subscriber=series_struct_subber) |
106 | + >>> found_bugtasks = bugtask_set.search(series_struct_sub_search) |
107 | + >>> found_bugtasks.count() |
108 | + 2 |
109 | + |
110 | +Create a new product which will be a part of a project group. A bug is |
111 | +created for the product which should then show up for structural subscribers |
112 | +of the project group. Then a new person is created who is subscribed to all |
113 | +of the bug reports about the project. Search for bug tasks that this new |
114 | +person is subscribed. |
115 | + |
116 | + >>> product = factory.makeProduct() |
117 | + >>> bug = factory.makeBug(product=product) |
118 | + >>> project = factory.makeProject() |
119 | + >>> product.project = project |
120 | + >>> project_struct_subber = factory.makePerson( |
121 | + ... name='project-struct-subber') |
122 | + >>> project.addBugSubscription(project_struct_subber, |
123 | + ... project_struct_subber) |
124 | + <StructuralSubscription at ...> |
125 | + >>> project_struct_sub_search = BugTaskSearchParams( |
126 | + ... user=None, structural_subscriber=project_struct_subber) |
127 | + >>> found_bugtasks = bugtask_set.search(project_struct_sub_search) |
128 | + >>> found_bugtasks.count() |
129 | + 1 |
130 | + |
131 | +We will also subscribe this project subscriber to a product that is a part of |
132 | +the project and ensure that duplicate bug tasks do not appear in the search |
133 | +results. |
134 | + |
135 | + >>> product2 = factory.makeProduct() |
136 | + >>> bug = factory.makeBug(product=product2) |
137 | + >>> product2.project = project |
138 | + >>> product2.addBugSubscription(project_struct_subber, |
139 | + ... project_struct_subber) |
140 | + <StructuralSubscription at ...> |
141 | + >>> project_struct_sub_search = BugTaskSearchParams( |
142 | + ... user=None, structural_subscriber=project_struct_subber) |
143 | + >>> found_bugtasks = bugtask_set.search(project_struct_sub_search) |
144 | + >>> found_bugtasks.count() |
145 | + 2 |
146 | + |
147 | +Create a new person and subscribe them to all of bug tasks targeted to a |
148 | +milestone. We then test to see that they are subscribed to all of the |
149 | +bug tasks for the milestone in which they are interested. |
150 | + |
151 | + >>> milestone_struct_subber = factory.makePerson( |
152 | + ... name='milestone-struct-subber') |
153 | + >>> product_milestone.addBugSubscription(milestone_struct_subber, |
154 | + ... milestone_struct_subber) |
155 | + <StructuralSubscription at ...> |
156 | + >>> milestone_struct_sub_search = BugTaskSearchParams( |
157 | + ... user=None, structural_subscriber=milestone_struct_subber) |
158 | + >>> found_bugtasks = bugtask_set.search(milestone_struct_sub_search) |
159 | + >>> found_bugtasks.count() |
160 | + 2 |
161 | + |
162 | +Create another new person and subscribe them to all the Ubuntu bug reports - |
163 | +crazy I know. Then test to see that this poor person is subscribed to all |
164 | +bugs with an Ubuntu bug task. |
165 | + |
166 | + >>> distro_struct_subber = factory.makePerson( |
167 | + ... name='distro-struct-subber') |
168 | + >>> ubuntu.addBugSubscription(distro_struct_subber, |
169 | + ... distro_struct_subber) |
170 | + <StructuralSubscription at ...> |
171 | + >>> distro_struct_sub_search = BugTaskSearchParams( |
172 | + ... user=None, structural_subscriber=distro_struct_subber) |
173 | + >>> found_bugtasks = bugtask_set.search(distro_struct_sub_search) |
174 | + >>> found_bugtasks.count() |
175 | + 5 |
176 | + |
177 | +Create a new person who will only be subscribed to an Ubuntu series, which is |
178 | +something more reasonable than all of Ubuntu. Test to ensure that this person |
179 | +is subscribed to all of the bug tasks about that distro series. |
180 | + |
181 | + >>> ubuntu = getUtility(IDistributionSet).getByName('ubuntu') |
182 | + >>> hoary = ubuntu.getSeries('hoary') |
183 | + >>> distro_series_struct_subber = factory.makePerson( |
184 | + ... name='distro-series-struct-subber') |
185 | + >>> hoary.addBugSubscription(distro_series_struct_subber, |
186 | + ... distro_series_struct_subber) |
187 | + <StructuralSubscription at ...> |
188 | + >>> distro_series_struct_sub_search = BugTaskSearchParams( |
189 | + ... user=None, structural_subscriber=distro_series_struct_subber) |
190 | + >>> found_bugtasks = bugtask_set.search(distro_series_struct_sub_search) |
191 | + >>> all_targeted = BugTaskSearchParams(user=None, omit_targeted=False) |
192 | + >>> hoary_bugtasks = hoary.searchTasks(all_targeted) |
193 | + >>> found_bugtasks.count() == hoary_bugtasks.count() |
194 | + True |
195 | + |
196 | +Create a new person and make them a subscriber to all Ubuntu Firefox (a |
197 | +distribution source package) bug reports. Test to see that the new person is |
198 | +subscribed to all of the Ubuntu Firefox bug tasks. |
199 | + |
200 | + >>> package_struct_subber = factory.makePerson( |
201 | + ... name='package-struct-subber') |
202 | + >>> ubuntu_firefox.addBugSubscription(package_struct_subber, |
203 | + ... package_struct_subber) |
204 | + <StructuralSubscription at ...> |
205 | + >>> package_struct_sub_search = BugTaskSearchParams( |
206 | + ... user=None, structural_subscriber=package_struct_subber) |
207 | + >>> found_bugtasks = bugtask_set.search(package_struct_sub_search) |
208 | + >>> found_bugtasks.count() |
209 | + 1 |
210 | + |
211 | +We'll also subscribe the person who is currently subscribed to a package's bug |
212 | +reports, package_struct_subber, to the bug reports of a product series to |
213 | +ensure that the structural_subscriber search is returning the set of both bug |
214 | +tasks. |
215 | + |
216 | + >>> product_series.addBugSubscription(package_struct_subber, |
217 | + ... package_struct_subber) |
218 | + <StructuralSubscription at ...> |
219 | + >>> package_struct_sub_search = BugTaskSearchParams( |
220 | + ... user=None, structural_subscriber=package_struct_subber) |
221 | + >>> found_bugtasks = bugtask_set.search(package_struct_sub_search) |
222 | + >>> combined_bugtasks_count = (ubuntu_firefox_bugs.count () + |
223 | + ... series_tasks.count()) |
224 | + >>> found_bugtasks.count() == combined_bugtasks_count |
225 | + True |
226 | |
227 | === modified file 'lib/lp/bugs/interfaces/bug.py' |
228 | --- lib/lp/bugs/interfaces/bug.py 2010-06-04 02:29:33 +0000 |
229 | +++ lib/lp/bugs/interfaces/bug.py 2010-06-07 18:13:25 +0000 |
230 | @@ -97,7 +97,7 @@ |
231 | False in a boolean context, or an AssertionError will be raised. |
232 | |
233 | If distribution is specified, sourcepackagename may optionally |
234 | - be provided. product must evaluate to False in a boolean |
235 | + be provided. Product must evaluate to False in a boolean |
236 | context, or an AssertionError will be raised. |
237 | """ |
238 | assert product or distribution, ( |
239 | |
240 | === modified file 'lib/lp/bugs/interfaces/bugtarget.py' |
241 | --- lib/lp/bugs/interfaces/bugtarget.py 2010-05-06 01:46:55 +0000 |
242 | +++ lib/lp/bugs/interfaces/bugtarget.py 2010-06-07 18:13:25 +0000 |
243 | @@ -76,6 +76,7 @@ |
244 | bug_supervisor=Reference(schema=Interface), |
245 | bug_commenter=Reference(schema=Interface), |
246 | bug_subscriber=Reference(schema=Interface), |
247 | + structural_subscriber=Reference(schema=Interface), |
248 | owner=Reference(schema=Interface), |
249 | affected_user=Reference(schema=Interface), |
250 | has_patch=copy_field(IBugTaskSearch['has_patch']), |
251 | @@ -184,7 +185,8 @@ |
252 | hardware_owner_is_bug_reporter=None, |
253 | hardware_owner_is_affected_by_bug=False, |
254 | hardware_owner_is_subscribed_to_bug=False, |
255 | - hardware_is_linked_to_bug=False, linked_branches=None): |
256 | + hardware_is_linked_to_bug=False, linked_branches=None, |
257 | + structural_subscriber=None): |
258 | """Search the IBugTasks reported on this entity. |
259 | |
260 | :search_params: a BugTaskSearchParams object |
261 | |
262 | === modified file 'lib/lp/bugs/interfaces/bugtask.py' |
263 | --- lib/lp/bugs/interfaces/bugtask.py 2010-05-25 16:45:26 +0000 |
264 | +++ lib/lp/bugs/interfaces/bugtask.py 2010-06-07 18:13:25 +0000 |
265 | @@ -175,7 +175,7 @@ |
266 | EXPIRED = DBItem(19, """ |
267 | Expired |
268 | |
269 | - This bug is expired. There was no activity since a longer time. |
270 | + This bug is expired. There was no activity for a long time. |
271 | """) |
272 | |
273 | CONFIRMED = DBItem(20, """ |
274 | @@ -1075,7 +1075,7 @@ |
275 | hardware_owner_is_affected_by_bug=False, |
276 | hardware_owner_is_subscribed_to_bug=False, |
277 | hardware_is_linked_to_bug=False, |
278 | - linked_branches=None |
279 | + linked_branches=None, structural_subscriber=None |
280 | ): |
281 | |
282 | self.bug = bug |
283 | @@ -1120,6 +1120,7 @@ |
284 | hardware_owner_is_subscribed_to_bug) |
285 | self.hardware_is_linked_to_bug = hardware_is_linked_to_bug |
286 | self.linked_branches = linked_branches |
287 | + self.structural_subscriber = structural_subscriber |
288 | |
289 | def setProduct(self, product): |
290 | """Set the upstream context on which to filter the search.""" |
291 | @@ -1192,7 +1193,8 @@ |
292 | hardware_owner_is_bug_reporter=None, |
293 | hardware_owner_is_affected_by_bug=False, |
294 | hardware_owner_is_subscribed_to_bug=False, |
295 | - hardware_is_linked_to_bug=False, linked_branches=None): |
296 | + hardware_is_linked_to_bug=False, linked_branches=None, |
297 | + structural_subscriber=None): |
298 | """Create and return a new instance using the parameter list.""" |
299 | search_params = cls(user=user, orderby=order_by) |
300 | |
301 | @@ -1260,6 +1262,7 @@ |
302 | search_params.hardware_is_linked_to_bug = ( |
303 | hardware_is_linked_to_bug) |
304 | search_params.linked_branches=linked_branches |
305 | + search_params.structural_subscriber = structural_subscriber |
306 | |
307 | return search_params |
308 | |
309 | |
310 | === modified file 'lib/lp/bugs/model/bugtarget.py' |
311 | --- lib/lp/bugs/model/bugtarget.py 2010-04-19 08:31:01 +0000 |
312 | +++ lib/lp/bugs/model/bugtarget.py 2010-06-07 18:13:25 +0000 |
313 | @@ -51,6 +51,7 @@ |
314 | importance=None, |
315 | assignee=None, bug_reporter=None, bug_supervisor=None, |
316 | bug_commenter=None, bug_subscriber=None, owner=None, |
317 | + structural_subscriber=None, |
318 | affected_user=None, affects_me=False, |
319 | has_patch=None, has_cve=None, distribution=None, |
320 | tags=None, tags_combinator=BugTagsSearchCombinator.ALL, |
321 | |
322 | === modified file 'lib/lp/bugs/model/bugtask.py' |
323 | --- lib/lp/bugs/model/bugtask.py 2010-05-27 13:51:06 +0000 |
324 | +++ lib/lp/bugs/model/bugtask.py 2010-06-07 18:13:25 +0000 |
325 | @@ -149,7 +149,7 @@ |
326 | search for all tasks related to a user given by `context`. |
327 | |
328 | Which tasks are related to a user? |
329 | - * the user has to be either assignee or owner of this tasks |
330 | + * the user has to be either assignee or owner of this task |
331 | OR |
332 | * the user has to be subscriber or commenter to the underlying bug |
333 | OR |
334 | @@ -158,7 +158,8 @@ |
335 | always get one task owned by the bug reporter |
336 | """ |
337 | assert IPerson.providedBy(context), "Context argument needs to be IPerson" |
338 | - relevant_fields = ('assignee', 'bug_subscriber', 'owner', 'bug_commenter') |
339 | + relevant_fields = ('assignee', 'bug_subscriber', 'owner', 'bug_commenter', |
340 | + 'structural_subscriber') |
341 | search_params = [] |
342 | for key in relevant_fields: |
343 | # all these parameter default to None |
344 | @@ -1641,6 +1642,48 @@ |
345 | BugSubscription.person = %(personid)s""" % |
346 | sqlvalues(personid=params.subscriber.id)) |
347 | |
348 | + if params.structural_subscriber is not None: |
349 | + structural_subscriber_clause = ( """BugTask.id IN ( |
350 | + SELECT BugTask.id FROM BugTask, StructuralSubscription |
351 | + WHERE BugTask.product = StructuralSubscription.product |
352 | + AND StructuralSubscription.subscriber = %(personid)s |
353 | + UNION ALL |
354 | + SELECT BugTask.id FROM BugTask, StructuralSubscription |
355 | + WHERE |
356 | + BugTask.distribution = StructuralSubscription.distribution |
357 | + AND BugTask.sourcepackagename = |
358 | + StructuralSubscription.sourcepackagename |
359 | + AND StructuralSubscription.subscriber = %(personid)s |
360 | + UNION ALL |
361 | + SELECT BugTask.id FROM BugTask, StructuralSubscription |
362 | + WHERE |
363 | + BugTask.distroseries = StructuralSubscription.distroseries |
364 | + AND StructuralSubscription.subscriber = %(personid)s |
365 | + UNION ALL |
366 | + SELECT BugTask.id FROM BugTask, StructuralSubscription |
367 | + WHERE |
368 | + BugTask.milestone = StructuralSubscription.milestone |
369 | + AND StructuralSubscription.subscriber = %(personid)s |
370 | + UNION ALL |
371 | + SELECT BugTask.id FROM BugTask, StructuralSubscription |
372 | + WHERE |
373 | + BugTask.productseries = StructuralSubscription.productseries |
374 | + AND StructuralSubscription.subscriber = %(personid)s |
375 | + UNION ALL |
376 | + SELECT BugTask.id FROM BugTask, StructuralSubscription, Product |
377 | + WHERE |
378 | + BugTask.product = Product.id |
379 | + AND Product.project = StructuralSubscription.project |
380 | + AND StructuralSubscription.subscriber = %(personid)s |
381 | + UNION ALL |
382 | + SELECT BugTask.id FROM BugTask, StructuralSubscription |
383 | + WHERE |
384 | + BugTask.distribution = StructuralSubscription.distribution |
385 | + AND StructuralSubscription.sourcepackagename is NULL |
386 | + AND StructuralSubscription.subscriber = %(personid)s)""" % |
387 | + sqlvalues(personid=params.structural_subscriber)) |
388 | + extra_clauses.append(structural_subscriber_clause) |
389 | + |
390 | if params.component: |
391 | clauseTables += ["SourcePackagePublishingHistory", |
392 | "SourcePackageRelease"] |
393 | |
394 | === modified file 'lib/lp/bugs/stories/patches-view/patches-view.txt' |
395 | --- lib/lp/bugs/stories/patches-view/patches-view.txt 2010-04-16 13:01:51 +0000 |
396 | +++ lib/lp/bugs/stories/patches-view/patches-view.txt 2010-06-07 18:13:25 +0000 |
397 | @@ -431,6 +431,100 @@ |
398 | Tabs: ... |
399 | Main heading: Patch attachments for Patchy Person |
400 | |
401 | +The patches view for a person or team will also show patches to which a person |
402 | +or team is structurally subscribed. Structural subscription searching is |
403 | +thoroughly tested in bugtask-search.txt so here a single structural |
404 | +subscription will be tested. |
405 | + |
406 | + >>> from canonical.launchpad.ftests import login, logout |
407 | + >>> project_subscriber = factory.doAsUser( |
408 | + ... 'foo.bar@canonical.com', factory.makePerson, |
409 | + ... name="project-subscriber", displayname="Project Subscriber") |
410 | + >>> login('foo.bar@canonical.com') |
411 | + >>> patchy_product.addBugSubscription(project_subscriber, |
412 | + ... project_subscriber) |
413 | + <StructuralSubscription at ...> |
414 | + >>> logout() |
415 | + >>> from zope.security.proxy import removeSecurityProxy |
416 | + >>> subscriber_name = removeSecurityProxy(project_subscriber).name |
417 | + >>> anon_browser.open( |
418 | + ... 'http://bugs.launchpad.dev/~%s/+patches' % subscriber_name) |
419 | + >>> show_patches_view(anon_browser.contents) |
420 | + Bug Importance Status Project Patch Age |
421 | + Bug #18: bug_c title Wishlist Fix Committed patchy-product-1 ...second... |
422 | + From: Patchy Person |
423 | + Link: patch_f.diff |
424 | + description of patch f |
425 | + Bug #17: bug_b title Critical Confirmed patchy-product-1 ...second... |
426 | + From: Patchy Person |
427 | + Link: patch_c.diff |
428 | + description of patch c |
429 | + Bug #16: bug_a title Undecided New patchy-product-1 ...second... |
430 | + From: Patchy Person |
431 | + Link: patch_a.diff |
432 | + description of patch a |
433 | + |
434 | +To ensure that all bugs with patches are shown for every structural |
435 | +subscription a new bug and is made to affect the evolution source package for |
436 | +Ubuntu. Additionally, a patch is added to that bug report so that it will |
437 | +show up in the +patches view. Project-subscriber is then subscribed to the |
438 | +evolution source package for Ubuntu and it is tested that both groups of |
439 | +patches are shown. |
440 | + |
441 | + >>> hacky_product = factory.doAsUser( |
442 | + ... 'foo.bar@canonical.com', factory.makeProduct, |
443 | + ... name='hacky-product', displayname="Hacky Product") |
444 | + >>> transaction.commit() |
445 | + >>> bug_e = factory.doAsUser( |
446 | + ... 'foo.bar@canonical.com', make_bug, |
447 | + ... title="bug_e is for evolution", product=hacky_product, |
448 | + ... importance=BugTaskImportance.WISHLIST, |
449 | + ... status=BugTaskStatus.NEW) |
450 | + >>> factory.doAsUser( |
451 | + ... 'foo.bar@canonical.com', factory.makeBugAttachment, |
452 | + ... comment="comment about patch h", |
453 | + ... filename="patch_h.diff", owner=patch_submitter, |
454 | + ... description="patch h is for helping", bug=bug_e, is_patch=True) |
455 | + <BugAttachment at... |
456 | + >>> factory.doAsUser( |
457 | + ... 'foo.bar@canonical.com', make_bugtask, bug=bug_e, |
458 | + ... target='evolution', target_is_spkg_name=True, |
459 | + ... importance=BugTaskImportance.MEDIUM, |
460 | + ... status=BugTaskStatus.TRIAGED) |
461 | + >>> transaction.commit() |
462 | + >>> from canonical.launchpad.ftests import login, logout |
463 | + >>> from zope.component import getUtility |
464 | + >>> from lp.registry.interfaces.distribution import IDistributionSet |
465 | + >>> login('foo.bar@canonical.com') |
466 | + >>> ubuntu = getUtility(IDistributionSet).getByName('ubuntu') |
467 | + >>> ubuntu_evolution = ubuntu.getSourcePackage("evolution") |
468 | + >>> ubuntu_evolution.addBugSubscription(project_subscriber, |
469 | + ... project_subscriber) |
470 | + <StructuralSubscription at ...> |
471 | + >>> logout() |
472 | + >>> from zope.security.proxy import removeSecurityProxy |
473 | + >>> subscriber_name = removeSecurityProxy(project_subscriber).name |
474 | + >>> anon_browser.open( |
475 | + ... 'http://bugs.launchpad.dev/~%s/+patches' % subscriber_name) |
476 | + >>> show_patches_view(anon_browser.contents) |
477 | + Bug Importance Status Project Patch Age |
478 | + Bug #20: bug_e is... Medium Triaged evolution ...second... |
479 | + From: Patchy Person |
480 | + Link: patch_h.diff |
481 | + patch h is for helping |
482 | + Bug #18: bug_c title Wishlist Fix Committed patchy-product-1 ...second... |
483 | + From: Patchy Person |
484 | + Link: patch_f.diff |
485 | + description of patch f |
486 | + Bug #17: bug_b title Critical Confirmed patchy-product-1 ...second... |
487 | + From: Patchy Person |
488 | + Link: patch_c.diff |
489 | + description of patch c |
490 | + Bug #16: bug_a title Undecided New patchy-product-1 ...second... |
491 | + From: Patchy Person |
492 | + Link: patch_a.diff |
493 | + description of patch a |
494 | + |
495 | Reaching the Patches View |
496 | ------------------------- |
497 | |
498 | @@ -496,24 +590,28 @@ |
499 | |
500 | >>> print_bugfilters_portlet_filled(anon_browser, 'ubuntu') |
501 | 6 New bugs |
502 | - 9 Open bugs |
503 | + 10 Open bugs |
504 | 0 In-progress bugs |
505 | 0 Critical bugs |
506 | 1 High importance bug |
507 | 0 Incomplete bugs (can expire) |
508 | <BLANKLINE> |
509 | - 5 Bugs with patches |
510 | + 6 Bugs with patches |
511 | 2 Bugs fixed elsewhere |
512 | 2 Open CVE bugs - CVE reports |
513 | |
514 | The number of bugs with patches shown in the bugfilter stats portlet |
515 | might be lower than the number of bugs (bugtasks) listed on the |
516 | corresponding patches view page, because the latter shows resolved |
517 | -bugs too. |
518 | +bugs too. N.B. only 5 bug tasks will appear at a time. |
519 | |
520 | >>> anon_browser.open('http://bugs.launchpad.dev/ubuntu/+patches') |
521 | >>> show_patches_view(anon_browser.contents) |
522 | Bug Importance Status Package Patch Age |
523 | + Bug #20: bug_e... Medium Triaged evolution ...second... |
524 | + From: Patchy Person |
525 | + Link: patch_h.diff |
526 | + patch h is for helping |
527 | Bug #18: bug_c title High Triaged a52dec ...second... |
528 | From: Patchy Person |
529 | Link: patch_f.diff |
530 | @@ -530,7 +628,3 @@ |
531 | From: Patchy Person |
532 | Link: patch_a.diff |
533 | description of patch a |
534 | - Bug #16: bug_a title Undecided New ubuntu ...second... |
535 | - From: Patchy Person |
536 | - Link: patch_a.diff |
537 | - description of patch a |
538 | |
539 | === modified file 'lib/lp/bugs/stories/structural-subscriptions/xx-bug-subscriptions.txt' |
540 | --- lib/lp/bugs/stories/structural-subscriptions/xx-bug-subscriptions.txt 2009-10-28 18:00:36 +0000 |
541 | +++ lib/lp/bugs/stories/structural-subscriptions/xx-bug-subscriptions.txt 2010-06-07 18:13:25 +0000 |
542 | @@ -64,7 +64,7 @@ |
543 | Landscape Developers |
544 | |
545 | |
546 | -== Additional options for distribuion drivers == |
547 | +== Additional options for distribution drivers == |
548 | |
549 | When editing the subscriptions for a package, a distribution driver |
550 | can subscribe any Launchpad user. |
551 | |
552 | === modified file 'lib/lp/bugs/stories/webservice/xx-bug.txt' |
553 | --- lib/lp/bugs/stories/webservice/xx-bug.txt 2010-05-18 12:38:19 +0000 |
554 | +++ lib/lp/bugs/stories/webservice/xx-bug.txt 2010-06-07 18:13:25 +0000 |
555 | @@ -1660,7 +1660,7 @@ |
556 | owner_link: u'http://api.launchpad.dev/beta/~testuser1' |
557 | ... |
558 | |
559 | -`testuser2` is subscribed to `testbugs2`, so this bug is related to this |
560 | +`testuser2` is subscribed to `testbug2`, so this bug is related to this |
561 | user: |
562 | |
563 | >>> related = webservice.named_get( |
564 | @@ -1682,15 +1682,16 @@ |
565 | total_size: 0 |
566 | --- |
567 | |
568 | -You are not allowed to overwrite all user related parameter in the same |
569 | -query, because in this case this bug will no be related to the person |
570 | -anymore. In this case a `400 Bad Request`-Error will be returned |
571 | +You are not allowed to overwrite all user related parameters in the same |
572 | +query, because this bug will not be related to the person anymore. In this |
573 | +case a `400 Bad Request`-Error will be returned. |
574 | |
575 | >>> name12 = webservice.get("/~name12").jsonBody() |
576 | >>> print webservice.named_get( |
577 | ... '/~name16', 'searchTasks', assignee=name12['self_link'], |
578 | ... owner=name12['self_link'], bug_subscriber=name12['self_link'], |
579 | - ... bug_commenter=name12['self_link'] |
580 | + ... bug_commenter=name12['self_link'], |
581 | + ... structural_subscriber=name12['self_link'] |
582 | ... ) |
583 | HTTP/1.1 400 Bad Request... |
584 | |
585 | |
586 | === modified file 'lib/lp/registry/tests/test_person.py' |
587 | --- lib/lp/registry/tests/test_person.py 2010-05-07 19:07:28 +0000 |
588 | +++ lib/lp/registry/tests/test_person.py 2010-06-07 18:13:25 +0000 |
589 | @@ -526,7 +526,8 @@ |
590 | |
591 | def checkUserFields( |
592 | self, params, assignee=None, bug_subscriber=None, |
593 | - owner=None, bug_commenter=None, bug_reporter=None): |
594 | + owner=None, bug_commenter=None, bug_reporter=None, |
595 | + structural_subscriber=None): |
596 | self.failUnlessEqual(assignee, params.assignee) |
597 | # fromSearchForm() takes a bug_subscriber parameter, but saves |
598 | # it as subscriber on the parameter object. |
599 | @@ -534,14 +535,15 @@ |
600 | self.failUnlessEqual(owner, params.owner) |
601 | self.failUnlessEqual(bug_commenter, params.bug_commenter) |
602 | self.failUnlessEqual(bug_reporter, params.bug_reporter) |
603 | + self.failUnlessEqual(structural_subscriber, params.structural_subscriber) |
604 | |
605 | def test_get_related_bugtasks_search_params(self): |
606 | # With no specified options, get_related_bugtasks_search_params() |
607 | - # returns 4 BugTaskSearchParams objects, each with a different |
608 | + # returns 5 BugTaskSearchParams objects, each with a different |
609 | # user field set. |
610 | search_params = get_related_bugtasks_search_params( |
611 | self.user, self.context) |
612 | - self.assertEqual(len(search_params), 4) |
613 | + self.assertEqual(len(search_params), 5) |
614 | self.checkUserFields( |
615 | search_params[0], assignee=self.context) |
616 | self.checkUserFields( |
617 | @@ -550,13 +552,15 @@ |
618 | search_params[2], owner=self.context, bug_reporter=self.context) |
619 | self.checkUserFields( |
620 | search_params[3], bug_commenter=self.context) |
621 | + self.checkUserFields( |
622 | + search_params[4], structural_subscriber=self.context) |
623 | |
624 | def test_get_related_bugtasks_search_params_with_assignee(self): |
625 | # With assignee specified, get_related_bugtasks_search_params() |
626 | - # returns 3 BugTaskSearchParams objects. |
627 | + # returns 4 BugTaskSearchParams objects. |
628 | search_params = get_related_bugtasks_search_params( |
629 | self.user, self.context, assignee=self.user) |
630 | - self.assertEqual(len(search_params), 3) |
631 | + self.assertEqual(len(search_params), 4) |
632 | self.checkUserFields( |
633 | search_params[0], assignee=self.user, bug_subscriber=self.context) |
634 | self.checkUserFields( |
635 | @@ -564,19 +568,23 @@ |
636 | bug_reporter=self.context) |
637 | self.checkUserFields( |
638 | search_params[2], assignee=self.user, bug_commenter=self.context) |
639 | + self.checkUserFields( |
640 | + search_params[3], assignee=self.user, structural_subscriber=self.context) |
641 | |
642 | def test_get_related_bugtasks_search_params_with_owner(self): |
643 | # With owner specified, get_related_bugtasks_search_params() returns |
644 | - # 3 BugTaskSearchParams objects. |
645 | + # 4 BugTaskSearchParams objects. |
646 | search_params = get_related_bugtasks_search_params( |
647 | self.user, self.context, owner=self.user) |
648 | - self.assertEqual(len(search_params), 3) |
649 | + self.assertEqual(len(search_params), 4) |
650 | self.checkUserFields( |
651 | search_params[0], owner=self.user, assignee=self.context) |
652 | self.checkUserFields( |
653 | search_params[1], owner=self.user, bug_subscriber=self.context) |
654 | self.checkUserFields( |
655 | search_params[2], owner=self.user, bug_commenter=self.context) |
656 | + self.checkUserFields( |
657 | + search_params[3], owner=self.user, structural_subscriber=self.context) |
658 | |
659 | def test_get_related_bugtasks_search_params_with_bug_reporter(self): |
660 | # With bug reporter specified, get_related_bugtasks_search_params() |
661 | @@ -584,7 +592,7 @@ |
662 | # is overwritten in one instance. |
663 | search_params = get_related_bugtasks_search_params( |
664 | self.user, self.context, bug_reporter=self.user) |
665 | - self.assertEqual(len(search_params), 4) |
666 | + self.assertEqual(len(search_params), 5) |
667 | self.checkUserFields( |
668 | search_params[0], bug_reporter=self.user, |
669 | assignee=self.context) |
670 | @@ -599,13 +607,16 @@ |
671 | self.checkUserFields( |
672 | search_params[3], bug_reporter=self.user, |
673 | bug_commenter=self.context) |
674 | + self.checkUserFields( |
675 | + search_params[4], bug_reporter=self.user, |
676 | + structural_subscriber=self.context) |
677 | |
678 | def test_get_related_bugtasks_search_params_illegal(self): |
679 | self.assertRaises( |
680 | IllegalRelatedBugTasksParams, |
681 | get_related_bugtasks_search_params, self.user, self.context, |
682 | assignee=self.user, owner=self.user, bug_commenter=self.user, |
683 | - bug_subscriber=self.user) |
684 | + bug_subscriber=self.user, structural_subscriber=self.user) |
685 | |
686 | def test_get_related_bugtasks_search_params_illegal_context(self): |
687 | # in case the `context` argument is not of type IPerson an |
Very nice, everything looks pretty good, but there's, I think, one problem. If I understand the code correctly, this works only for subscriptions to products, not to other subscription targets (like packages, project groups, etc). When or before fixing this, it's worth extending the test to cover those other subscription targets too.