Merge lp:~sinzui/launchpad/bug-subselect-timeout into lp:launchpad
- bug-subselect-timeout
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Curtis Hovey |
Approved revision: | no longer in the source branch. |
Merged at revision: | 16058 |
Proposed branch: | lp:~sinzui/launchpad/bug-subselect-timeout |
Merge into: | lp:launchpad |
Diff against target: |
1373 lines (+119/-1038) 3 files modified
lib/lp/bugs/doc/bugtask-search.txt (+34/-953) lib/lp/bugs/model/bugtasksearch.py (+62/-85) lib/lp/bugs/model/tests/test_bugtasksearch.py (+23/-0) |
To merge this branch: | bzr merge lp:~sinzui/launchpad/bug-subselect-timeout |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
William Grant | code | Approve | |
j.c.sackett (community) | Approve | ||
Review via email: mp+126566@code.launchpad.net |
Commit message
Only match the context when searching for structures a person is subscribed to.
Description of the change
The bug query clause for structural subscribers times out. This is
possibly caused by the superfluous joins against bugtaskflat.
-------
RULES
Pre-
* Structure the query to be fast. Try In() and Row() to match a CTE table
of just the relevant structural subscriptions.
* Do not match impossible conditions...when looking for distro or
distro packages, do not match on product or product series.
QA
* Visit https:/
* Verify it does not timeout
LINT
lib/
TEST
./bin/test -vvc -t structural lp.bugs.
IMPLEMENTATION
Replaced the LEFT JOINS with subselects using In() and Row() as needed. I
tried a single Row() that matched the SS columns to BugTaskFlat columns,
but the tests failed -- I suspect that NULLs in Row() did not match the NULLs
in the SELECT. NILL != NULL. Maybe I botched this. Note the odd rule that
matches DSP SSs to DS SP bugs. This looks historical, but may also be
convenience because SPs can be short-lived and probably causes users to
not ask why they stopped getting email when a new series started. After I
got the tests to pass, I changed the code to build only the subqueries that
make sense for the bug params being used.
lib/
William Grant (wgrant) wrote : | # |
There's a general issue here that your preconditions for applying the restrictions are a little too strict. I'd suggest perhaps maintaining a map of {target type: [potentially relevant subscription targets]}, or just examining all subscription targets unconditionally if that performs reasonably.
I tried doing this in a single ROW ... IN clause as I suggested on the call, but didn't get very far before it tried to strangle me, mostly due to the fact that (distro, spn) tasks must be found by any (distro, NULL) subscription. It'd be possible with a bit of UNIONing, but I'm not sure it's worth it at this stage.
Specific comments inline below.
105 + Select(
106 + SS.distributionID,
107 + tables=[SS],
108 + where=(
Given how long the structsub code is, it might be worth dropping the newline in the middle there, and similarly compressing the other blocks. Even cutting just a few lines makes the whole thing seem a lot more manageable.
109 + ss_clauses.
110 + Row(BugTaskFlat
111 + BugTaskFlat.
112 + Select(
113 + ((SS.distributi
114 + SS.sourcepackag
115 + tables=[SS])))
I don't think you need the double bracketing in the Select. Removing that also lets you get it onto one line. Same in a couple of other places.
125 + if params.distroseries is not None:
126 + distroseries_id = params.
127 + parent_distro_id = params.
128 + else:
129 + distroseries_id = 0
130 + parent_distro_id = 0
I've never understood this bit. It looks like it will just ignore DSP structsubs for SP tasks unless queried in a distroseries context?
141 + if is_person_search or params.product is not None:
142 + ss_clauses.
143 + BugTaskFlat.
144 + Select(
145 + SS.productID,
146 + tables=[SS])))
What about project groups? They show multiple products, but this will ignore all of the product subscriptions. Also, the entire Select() expression can be one line.
153 + if is_person_search or params.project is not None:
This will break in one obscure case: if searching in a product, it won't return any bugs if the only structural subscription is through the project group.
163 + if is_person_search or params.milestone is not None:
This needs to apply in basically all cases. Milestone subscriptions affect all targets.
183 + # Remove bugtasks from deactivated products.
184 + # This is needed for searches where people are the context.
185 + if is_person_search:
This also breaks in the case of project group searches.
Curtis Hovey (sinzui) wrote : | # |
Thank you for the review William.
I simplified the rules for appending ss clauses. Milestones are always present as you suggested. The two if-statements
check if the query is constrained to product or distro related bugs. This allowed me to remove the is_person_search variable that you pointed out was also applying to project groups. I reduced the number of vertical lines. I also revised the comment about why we search for DSP's when we know the user has constrained the search to a distroseries.
William Grant (wgrant) wrote : | # |
That turned out much nicer than I thought it could, thanks! One last nitpick: "if params.product is None and params.
Preview Diff
1 | === modified file 'lib/lp/bugs/doc/bugtask-search.txt' |
2 | --- lib/lp/bugs/doc/bugtask-search.txt 2012-08-16 05:18:54 +0000 |
3 | +++ lib/lp/bugs/doc/bugtask-search.txt 2012-09-28 22:06:42 +0000 |
4 | @@ -18,114 +18,6 @@ |
5 | True |
6 | |
7 | |
8 | -== Searching by bug supervisor == |
9 | - |
10 | -The 'bug_supervisor' parameter allows you to search bugtasks that a certain |
11 | -person is responsible for. A person can be a bug supervisor for a product, |
12 | -a distribution, or a distribution source package. No Privileges Person |
13 | -isn't a bug supervisor, so no bugs are found for him: |
14 | - |
15 | - >>> from lp.registry.interfaces.person import IPersonSet |
16 | - >>> no_priv = getUtility(IPersonSet).getByName('no-priv') |
17 | - >>> no_priv_bug_supervisor = BugTaskSearchParams( |
18 | - ... user=None, bug_supervisor=no_priv) |
19 | - >>> found_bugtasks = bugtask_set.search(no_priv_bug_supervisor) |
20 | - >>> found_bugtasks.count() |
21 | - 0 |
22 | - |
23 | -== Product bugs == |
24 | - |
25 | -Firefox has a few bugs: |
26 | - |
27 | - >>> from lp.registry.interfaces.product import IProductSet |
28 | - >>> firefox = getUtility(IProductSet).getByName('firefox') |
29 | - >>> firefox_bugs = firefox.searchTasks(all_public) |
30 | - >>> firefox_public_bugs = firefox_bugs.count() |
31 | - >>> firefox_public_bugs > 0 |
32 | - True |
33 | - |
34 | -== Distribution and package bugs == |
35 | - |
36 | -Ubuntu does too: |
37 | - |
38 | - >>> from lp.registry.interfaces.distribution import IDistributionSet |
39 | - >>> ubuntu = getUtility(IDistributionSet).getByName("ubuntu") |
40 | - >>> all_public = BugTaskSearchParams(user=None) |
41 | - >>> ubuntu_bugs = ubuntu.searchTasks(all_public) |
42 | - >>> ubuntu_bugs.count() > 0 |
43 | - True |
44 | - |
45 | -and in particular, mozilla-firefox in Ubuntu has 'em: |
46 | - |
47 | - >>> ubuntu_firefox = ubuntu.getSourcePackage("mozilla-firefox") |
48 | - >>> ubuntu_firefox_bugs = ubuntu_firefox.searchTasks(all_public) |
49 | - >>> ubuntu_firefox_bugs.count() > 0 |
50 | - True |
51 | - |
52 | -== Person bugs == |
53 | - |
54 | -To get all related tasks to a person call searchTasks() on the person |
55 | -object: |
56 | - |
57 | - >>> from lp.registry.interfaces.person import IPersonSet |
58 | - >>> user = getUtility(IPersonSet).getByName('name16') |
59 | - >>> user_bugs = user.searchTasks(None, user=None) |
60 | - >>> user_bugs.count() > 0 |
61 | - True |
62 | - |
63 | -== Dupes and Conjoined tasks == |
64 | - |
65 | -You can set flags to omit duplicates: |
66 | - |
67 | - >>> no_dupes = BugTaskSearchParams(user=None, omit_dupes=True) |
68 | - >>> firefox = getUtility(IProductSet).getByName('firefox') |
69 | - >>> sans_dupes = firefox.searchTasks(no_dupes) |
70 | - >>> sans_dupes.count() < firefox_public_bugs |
71 | - True |
72 | - |
73 | -and also series-targeted bugs: |
74 | - |
75 | - >>> no_targeted = BugTaskSearchParams(user=None, omit_targeted=True) |
76 | - >>> sans_targeted = ubuntu.searchTasks(no_targeted) |
77 | - >>> sans_targeted.count() < ubuntu_bugs |
78 | - True |
79 | - |
80 | -=== Product bug supervisor === |
81 | - |
82 | -If No Privileges is specified as Firefox's bug supervisor, searching for his |
83 | -bugs return all of Firefox's bugs. |
84 | - |
85 | - >>> login('foo.bar@canonical.com') |
86 | - >>> firefox.bug_supervisor = no_priv |
87 | - |
88 | - >>> found_bugtasks = bugtask_set.search(no_priv_bug_supervisor) |
89 | - >>> found_bugtasks.count() == firefox_bugs.count() |
90 | - True |
91 | - |
92 | - >>> found_targets = set( |
93 | - ... bugtask.target.bugtargetdisplayname for bugtask in found_bugtasks) |
94 | - >>> for target_name in sorted(found_targets): |
95 | - ... print target_name |
96 | - Mozilla Firefox |
97 | - |
98 | - |
99 | -=== Distribution bug supervisor === |
100 | - |
101 | -If someone is bug supervisor for Firefox, Firefox in Ubuntu, and Ubuntu, |
102 | -all bugs in Firefox and Ubuntu are returned. Bugs in the Ubuntu Firefox |
103 | -package are included in the Ubuntu bugs, so they won't be returned |
104 | -twice. |
105 | - |
106 | - >>> all_public = BugTaskSearchParams(user=None) |
107 | - >>> ubuntu_bugs = ubuntu.searchTasks(all_public) |
108 | - >>> ubuntu_bugs.count() > 0 |
109 | - True |
110 | - |
111 | - >>> ubuntu.bug_supervisor = no_priv |
112 | - >>> found_bugtasks = bugtask_set.search(no_priv_bug_supervisor) |
113 | - >>> found_bugtasks.count() == firefox_bugs.count() + ubuntu_bugs.count() |
114 | - True |
115 | - |
116 | == Searching using bug full-text index == |
117 | |
118 | The searchtext parameter does an extensive and expensive search (it |
119 | @@ -136,6 +28,8 @@ |
120 | |
121 | For example, there are no bugs with the word 'Fnord' in Firefox. |
122 | |
123 | + >>> from lp.registry.interfaces.product import IProductSet |
124 | + >>> firefox = getUtility(IProductSet).getByName('firefox') |
125 | >>> text_search = BugTaskSearchParams(user=None, searchtext=u'Fnord') |
126 | >>> found_bugtasks = firefox.searchTasks(text_search) |
127 | >>> found_bugtasks.count() |
128 | @@ -144,6 +38,7 @@ |
129 | But if we put that word in the bug #4 description, it will be found. |
130 | |
131 | >>> from lp.bugs.interfaces.bug import IBugSet |
132 | + >>> login('foo.bar@canonical.com') |
133 | >>> bug_four = getUtility(IBugSet).get(4) |
134 | >>> bug_four.description += ( |
135 | ... '\nThat happens pretty often with the Fnord Highlighter ' |
136 | @@ -212,322 +107,6 @@ |
137 | >>> transaction.abort() |
138 | |
139 | |
140 | -== Searching by bug reporter == |
141 | - |
142 | -The 'bug_reporter' parameter allows you to search for bugs reported by a |
143 | -certain person. |
144 | - |
145 | - >>> foo_bar = getUtility(IPersonSet).getByEmail('foo.bar@canonical.com') |
146 | - >>> reported_by_foo_bar = BugTaskSearchParams( |
147 | - ... user=None, bug_reporter=foo_bar) |
148 | - >>> reported_by_foo_bar.setDistribution(ubuntu) |
149 | - >>> found_bugtasks = bugtask_set.search(reported_by_foo_bar) |
150 | - >>> for bugtask in found_bugtasks: |
151 | - ... print "#%s in %s reported by %s" % ( |
152 | - ... bugtask.bug.id, bugtask.bugtargetname, |
153 | - ... bugtask.bug.owner.displayname) |
154 | - #9 in thunderbird (Ubuntu) reported by Foo Bar |
155 | - #10 in linux-source-2.6.15 (Ubuntu) reported by Foo Bar |
156 | - |
157 | - |
158 | -== Searching for nominated bugs == |
159 | - |
160 | -We can search for bugs nominated to a distribution series by using the |
161 | -nominated_for parameter. |
162 | - |
163 | - >>> ubuntu = getUtility(IDistributionSet).getByName('ubuntu') |
164 | - >>> warty = ubuntu.getSeries('warty') |
165 | - |
166 | - >>> from lp.bugs.model.bugnomination import BugNomination |
167 | - >>> print list(BugNomination.selectBy(distroseries=warty)) |
168 | - [] |
169 | - |
170 | - >>> from lp.bugs.interfaces.bug import CreateBugParams |
171 | - >>> nominated_for_warty = BugTaskSearchParams( |
172 | - ... user=None, nominated_for=warty) |
173 | - >>> list(ubuntu.searchTasks(nominated_for_warty)) |
174 | - [] |
175 | - |
176 | - >>> nominated_bug = ubuntu.createBug( |
177 | - ... CreateBugParams(owner=no_priv, title='Test nominated bug', |
178 | - ... comment='Something')) |
179 | - >>> BugNomination( |
180 | - ... owner=no_priv, distroseries=warty, bug=nominated_bug) |
181 | - <BugNomination at ...> |
182 | - |
183 | - >>> for bugtask in ubuntu.searchTasks(nominated_for_warty): |
184 | - ... print bugtask.bug.title |
185 | - Test nominated bug |
186 | - |
187 | -The same parameter is used to search for bugs nominated to a product |
188 | -series. |
189 | - |
190 | - >>> firefox = getUtility(IProductSet).getByName('firefox') |
191 | - >>> firefox_trunk = firefox.getSeries('trunk') |
192 | - >>> print list(BugNomination.selectBy(productseries=firefox_trunk)) |
193 | - [] |
194 | - >>> nominated_for_trunk = BugTaskSearchParams( |
195 | - ... user=None, nominated_for=firefox_trunk) |
196 | - >>> list(firefox.searchTasks(nominated_for_trunk)) |
197 | - [] |
198 | - |
199 | - >>> nominated_bug = firefox.createBug( |
200 | - ... CreateBugParams(owner=no_priv, title='Bug to be fixed in trunk', |
201 | - ... comment='Something')) |
202 | - >>> BugNomination( |
203 | - ... owner=no_priv, productseries=firefox_trunk, bug=nominated_bug) |
204 | - <BugNomination at ...> |
205 | - |
206 | - >>> for bugtask in firefox.searchTasks(nominated_for_trunk): |
207 | - ... print bugtask.bug.title |
208 | - Bug to be fixed in trunk |
209 | - |
210 | -== Filter by Upstream Status == |
211 | - |
212 | -Add an Ubuntu bugtask for a bug that is confirmed upstream. |
213 | - |
214 | - >>> from lp.bugs.interfaces.bugtask import ( |
215 | - ... BugTaskImportance, |
216 | - ... BugTaskStatus, |
217 | - ... ) |
218 | - >>> from lp.bugs.model.tests.test_bugtask import ( |
219 | - ... BugTaskSearchBugsElsewhereTest) |
220 | - >>> def bugTaskInfo(bugtask): |
221 | - ... return '%i %i %s %s' % ( |
222 | - ... bugtask.id, bugtask.bug.id, bugtask.bugtargetdisplayname, |
223 | - ... bugtask.bug.title) |
224 | - >>> test_helper = BugTaskSearchBugsElsewhereTest(helper_only=True) |
225 | - >>> bug_twelve = getUtility(IBugSet).get(12) |
226 | - >>> task_open_upstream = bugtask_set.createTask( |
227 | - ... bug_twelve, foo_bar, ubuntu, |
228 | - ... status=BugTaskStatus.NEW, importance=BugTaskImportance.MEDIUM) |
229 | - >>> test_helper.assertBugTaskIsOpenUpstream(task_open_upstream) |
230 | - |
231 | -Pass the resolved_upstream flag to include only bugtasks linked to |
232 | -watches that are rejected, fixed committed or fix released, or bugtasks |
233 | -related to upstream bugtasks (i.e. filed on the same bug) that are fix |
234 | -committed or fix released. |
235 | - |
236 | - >>> test_helper.setUpBugsResolvedUpstreamTests() |
237 | - >>> params = BugTaskSearchParams( |
238 | - ... resolved_upstream=True, orderby='id', user=None) |
239 | - >>> closed_elsewhere_tasks = ubuntu.searchTasks(params) |
240 | - >>> for bugtask in closed_elsewhere_tasks: |
241 | - ... test_helper.assertBugTaskIsResolvedUpstream(bugtask) |
242 | - ... print bugTaskInfo(bugtask) |
243 | - 17 1 mozilla-firefox (Ubuntu) Firefox does not support SVG |
244 | - 26 2 Ubuntu Blackhole Trash folder |
245 | - 23 9 thunderbird (Ubuntu) Thunderbird crashes |
246 | - |
247 | - |
248 | -Pass the open_upstream flag to include only bugtasks linked to those |
249 | -watches or those upstream bugtasks that have the status "unconfirmed", |
250 | -"needs info", "confirmed", "in progress" or "unknown". Note that a bug |
251 | -may be associated with three or more bugtasks. If one upstream task |
252 | -has a state associated with "open upstream", and another upstream task |
253 | -has a state associated with "resolved upstream", the bug is included |
254 | -in the results of the "open upstream" filter as well as the "resolved |
255 | -upstream" filter. |
256 | - |
257 | -(In the examples below, the last bugtask is ellipsized because its ID |
258 | - is generated here and therefore sampledata-dependent.) |
259 | - |
260 | - >>> params = BugTaskSearchParams( |
261 | - ... open_upstream=True, orderby='id', user=None) |
262 | - >>> open_elsewhere_tasks = ubuntu.searchTasks(params) |
263 | - >>> for bugtask in open_elsewhere_tasks: |
264 | - ... test_helper.assertBugTaskIsOpenUpstream(bugtask) |
265 | - ... print bugTaskInfo(bugtask) |
266 | - 17 1 mozilla-firefox (Ubuntu) Firefox does not support SVG |
267 | - 26 2 Ubuntu Blackhole Trash folder |
268 | - ... ... Ubuntu Copy, Cut and Delete operations should work on selections |
269 | - |
270 | - |
271 | -We can also filter our search to include only bugs that are not known to |
272 | -affect upstream, i.e., bugs that don't have an IUpstreamBugTask. |
273 | - |
274 | - >>> params = BugTaskSearchParams( |
275 | - ... has_no_upstream_bugtask=True, orderby='id', user=None) |
276 | - >>> tasks_with_no_upstreams = ubuntu.searchTasks(params) |
277 | - >>> for bugtask in tasks_with_no_upstreams: |
278 | - ... test_helper.assertShouldBeShownOnNoUpstreamTaskSearch(bugtask) |
279 | - ... print bugTaskInfo(bugtask) |
280 | - 25 10 linux-source-2.6.15 (Ubuntu) another test bug |
281 | - ... ... Ubuntu Test nominated bug |
282 | - |
283 | -If we combine upstream-related filters, we get the union of the results |
284 | -of the single filters. |
285 | - |
286 | - >>> params = BugTaskSearchParams( |
287 | - ... has_no_upstream_bugtask=True, resolved_upstream=True, |
288 | - ... orderby='id', user=None) |
289 | - >>> tasks_with_no_upstreams = ubuntu.searchTasks(params) |
290 | - >>> for bugtask in tasks_with_no_upstreams: |
291 | - ... print bugTaskInfo(bugtask) |
292 | - 17 1 mozilla-firefox (Ubuntu) Firefox does not support SVG |
293 | - 26 2 Ubuntu Blackhole Trash folder |
294 | - 23 9 thunderbird (Ubuntu) Thunderbird crashes |
295 | - 25 10 linux-source-2.6.15 (Ubuntu) another test bug |
296 | - ... ... Ubuntu Test nominated bug |
297 | - |
298 | - |
299 | - >>> test_helper.tearDownBugsElsewhereTests() |
300 | - |
301 | -The search filter can also return bugs that are related to CVE reports: |
302 | - |
303 | - >>> from lp.bugs.interfaces.cve import ICveSet |
304 | - >>> def getCves(bugtask): |
305 | - ... bug, cve = getUtility(ICveSet).getBugCvesForBugTasks([bugtask])[0] |
306 | - ... return cve.sequence |
307 | - >>> params = BugTaskSearchParams( |
308 | - ... has_cve=True, orderby='id', user=None) |
309 | - >>> tasks_with_cves = ubuntu.searchTasks(params) |
310 | - >>> for bugtask in tasks_with_cves: |
311 | - ... print bugTaskInfo(bugtask), getCves(bugtask) |
312 | - 17 1 mozilla-firefox (Ubuntu) Firefox does not support SVG 1999-8979 |
313 | - 26 2 Ubuntu Blackhole Trash folder 1999-2345 |
314 | - |
315 | - |
316 | -== Searching by bug commenter == |
317 | - |
318 | -The 'bug_commenter' parameter allows you to search bugtasks on which a certain |
319 | -person has commented. No Privileges Person hasn't commented on any bugs, so no |
320 | -bugs are found for him: |
321 | - |
322 | - >>> from lp.registry.interfaces.person import IPersonSet |
323 | - >>> transaction.abort() |
324 | - |
325 | - >>> no_priv = getUtility(IPersonSet).getByName('no-priv') |
326 | - >>> no_priv_bug_commenter = BugTaskSearchParams( |
327 | - ... user=None, bug_commenter=no_priv) |
328 | - >>> found_bugtasks = bugtask_set.search(no_priv_bug_commenter) |
329 | - >>> found_bugtasks.count() |
330 | - 0 |
331 | - |
332 | -If No Privileges Person comments on some bugs, those bugs can then be found by |
333 | -a bug commenter search. There will be one bug task instance returned for each |
334 | -bug target that the task is registered against, so in the test below three |
335 | -comments will produce eight found bug tasks (three for bug 1, five for bug 2). |
336 | - |
337 | - >>> bug_one = getUtility(IBugSet).get(1) |
338 | - >>> bug_one.newMessage(no_priv, 'No subject', 'some comment') |
339 | - <Message at ...> |
340 | - |
341 | - >>> bug_two = getUtility(IBugSet).get(2) |
342 | - >>> bug_two.newMessage(no_priv, 'No subject', 'another comment') |
343 | - <Message at ...> |
344 | - |
345 | - >>> bug_two.newMessage(no_priv, 'No subject', 'yet another comment') |
346 | - <Message at ...> |
347 | - |
348 | - >>> for (bug_id, target) in sorted((bugtask.bug.id, bugtask.bugtargetname) |
349 | - ... for bugtask in found_bugtasks): |
350 | - ... print bug_id, target |
351 | - 1 firefox |
352 | - 1 mozilla-firefox (Debian) |
353 | - 1 mozilla-firefox (Ubuntu) |
354 | - 2 Ubuntu Hoary |
355 | - 2 mozilla-firefox (Debian Woody) |
356 | - 2 mozilla-firefox (Debian) |
357 | - 2 tomcat |
358 | - 2 ubuntu |
359 | - |
360 | -If No Privileges Person reports a bug and does not comment on it, that bug |
361 | -will not be included in the results returned by the bug commenter search. |
362 | - |
363 | - >>> from lp.bugs.interfaces.bug import CreateBugParams |
364 | - >>> from lp.registry.interfaces.product import IProductSet |
365 | - |
366 | - >>> firefox = getUtility(IProductSet).getByName('firefox') |
367 | - >>> firefox.createBug( |
368 | - ... CreateBugParams(no_priv, "Some bug", "Some comment")) |
369 | - <Bug at ...> |
370 | - |
371 | - >>> for (bug_id, target) in sorted((bugtask.bug.id, bugtask.bugtargetname) |
372 | - ... for bugtask in found_bugtasks): |
373 | - ... print bug_id, target |
374 | - 1 firefox |
375 | - 1 mozilla-firefox (Debian) |
376 | - 1 mozilla-firefox (Ubuntu) |
377 | - 2 Ubuntu Hoary |
378 | - 2 mozilla-firefox (Debian Woody) |
379 | - 2 mozilla-firefox (Debian) |
380 | - 2 tomcat |
381 | - 2 ubuntu |
382 | - |
383 | - |
384 | -== Search for BugTasks assigned to milestones == |
385 | - |
386 | -BugTaskSet.search() can return bugtasks associated with milestones. |
387 | -No BugTask is associated yet with firefox milestone 1.0. |
388 | - |
389 | - >>> product_milestone = firefox.getMilestone('1.0') |
390 | - >>> params = BugTaskSearchParams(milestone=product_milestone, user=None) |
391 | - >>> milestone_tasks = bugtask_set.search(params) |
392 | - >>> print milestone_tasks.count() |
393 | - 0 |
394 | - |
395 | -Similary, no BugTasks are associated with the project firexfox belongs to. |
396 | - |
397 | - >>> mozilla = firefox.project |
398 | - >>> project_milestone = mozilla.getMilestone('1.0') |
399 | - >>> params = BugTaskSearchParams(milestone=project_milestone, user=None) |
400 | - >>> milestone_tasks = bugtask_set.search(params) |
401 | - >>> print milestone_tasks.count() |
402 | - 0 |
403 | - |
404 | -When a BugTask is associated with a milestone, it is returned in a search |
405 | -for bugs of this milestone. |
406 | - |
407 | - >>> bugtask = firefox.searchTasks(BugTaskSearchParams(user=None))[0] |
408 | - >>> print bugTaskInfo(bugtask) |
409 | - 2 1 Mozilla Firefox Firefox does not support SVG |
410 | - >>> bugtask.milestone = product_milestone |
411 | - >>> params = BugTaskSearchParams(milestone=product_milestone, user=None) |
412 | - >>> milestone_tasks = bugtask_set.search(params) |
413 | - >>> for bugtask in milestone_tasks: |
414 | - ... print bugTaskInfo(bugtask) |
415 | - 2 1 Mozilla Firefox Firefox does not support SVG |
416 | - |
417 | -This BugTask is also a BugTask of the milestone of the mozilla project. |
418 | - |
419 | - >>> params = BugTaskSearchParams(milestone=project_milestone, user=None) |
420 | - >>> milestone_tasks = bugtask_set.search(params) |
421 | - >>> for bugtask in milestone_tasks: |
422 | - ... print bugTaskInfo(bugtask) |
423 | - 2 1 Mozilla Firefox Firefox does not support SVG |
424 | - |
425 | -If a bug has one bugtask associated with a product and another bugtask |
426 | -associated with a product series, and if both tasks are assigned to the |
427 | -same milestone... |
428 | - |
429 | - >>> firefox_1_0 = firefox.getSeries("1.0") |
430 | - >>> productseries_task = bugtask_set.createTask( |
431 | - ... bug_one, no_priv, firefox_1_0) |
432 | - >>> productseries_task.milestone = product_milestone |
433 | - >>> print bugTaskInfo(productseries_task) |
434 | - 40 1 Mozilla Firefox 1.0 Firefox does not support SVG |
435 | - |
436 | -...both of them are returned, by a search for bugs associated with the |
437 | -product milestone... |
438 | - |
439 | - >>> params = BugTaskSearchParams(milestone=product_milestone, user=None) |
440 | - >>> milestone_tasks = bugtask_set.search(params) |
441 | - >>> for bugtask in milestone_tasks: |
442 | - ... print bugTaskInfo(bugtask) |
443 | - 2 1 Mozilla Firefox Firefox does not support SVG |
444 | - 40 1 Mozilla Firefox 1.0 Firefox does not support SVG |
445 | - |
446 | -...as well as by a search for bugs associated with the project milestone. |
447 | - |
448 | - >>> params = BugTaskSearchParams(milestone=project_milestone, user=None) |
449 | - >>> milestone_tasks = bugtask_set.search(params) |
450 | - >>> for bugtask in milestone_tasks: |
451 | - ... print bugTaskInfo(bugtask) |
452 | - 2 1 Mozilla Firefox Firefox does not support SVG |
453 | - 40 1 Mozilla Firefox 1.0 Firefox does not support SVG |
454 | - |
455 | - |
456 | == Bugs with partner packages == |
457 | |
458 | Bugs may also be targeted to partner packages. First turn "cdrkit" into |
459 | @@ -535,6 +114,8 @@ |
460 | |
461 | >>> from zope.security.proxy import removeSecurityProxy |
462 | >>> from lp.soyuz.interfaces.component import IComponentSet |
463 | + >>> from lp.registry.interfaces.distribution import IDistributionSet |
464 | + >>> ubuntu = getUtility(IDistributionSet).getByName("ubuntu") |
465 | >>> proxied_cdrkit = ubuntu.getSourcePackage("cdrkit") |
466 | >>> cdrkit = removeSecurityProxy(proxied_cdrkit) |
467 | >>> cdrkit.component = getUtility(IComponentSet)['partner'] |
468 | @@ -550,6 +131,8 @@ |
469 | We can file a bug against it and see that show up in a search: |
470 | |
471 | >>> from lp.bugs.interfaces.bug import CreateBugParams |
472 | + >>> from lp.registry.interfaces.person import IPersonSet |
473 | + >>> no_priv = getUtility(IPersonSet).getByName('no-priv') |
474 | >>> bug = cdrkit.createBug( |
475 | ... CreateBugParams(owner=no_priv, title='Bug to be fixed in trunk', |
476 | ... comment='Something')) |
477 | @@ -558,239 +141,6 @@ |
478 | 1 |
479 | |
480 | |
481 | -== Searching by tags == |
482 | - |
483 | -It is possible to search for bugs by their tags. Tags in the search |
484 | -parameters can be combined using either ''any'' or ''all''. |
485 | - |
486 | -First, we create some test bugs. |
487 | - |
488 | - >>> firefox = getUtility(IProductSet).get(4) |
489 | - >>> foobar = getUtility(IPersonSet).get(16) |
490 | - |
491 | -The first bug is tagged with both 'test-tag-1' and 'test-tag-2'. |
492 | - |
493 | - >>> params = CreateBugParams( |
494 | - ... title="test bug a", comment="test bug a", owner=foobar) |
495 | - >>> test_bug_a = firefox.createBug(params) |
496 | - >>> test_bug_a.tags = ['test-tag-1', 'test-tag-2'] |
497 | - |
498 | -The second bug is tagged with only 'test-tag-1'. |
499 | - |
500 | - >>> params = CreateBugParams( |
501 | - ... title="test bug b", comment="test bug b", owner=foobar) |
502 | - >>> test_bug_b = firefox.createBug(params) |
503 | - >>> test_bug_b.tags = ['test-tag-1'] |
504 | - |
505 | -Searching for bugs with any of the tags returns both of them. |
506 | - |
507 | - >>> from operator import attrgetter |
508 | - >>> from lp.services.searchbuilder import all, any |
509 | - |
510 | - >>> def search_tasks_and_print_bugs(user=None, **args): |
511 | - ... params = BugTaskSearchParams(user=user, **args) |
512 | - ... tasks = firefox.searchTasks(params) |
513 | - ... bugs = (task.bug for task in tasks) |
514 | - ... bugs = sorted(bugs, key=attrgetter('id')) |
515 | - ... for bug in bugs: |
516 | - ... print "%s [%s]" % (bug.title, ", ".join(bug.tags)) |
517 | - |
518 | - >>> search_tasks_and_print_bugs( |
519 | - ... tag=any('test-tag-1', 'test-tag-2')) |
520 | - test bug a [test-tag-1, test-tag-2] |
521 | - test bug b [test-tag-1] |
522 | - |
523 | -Searching for bugs with all of the tags returns only test bug a. |
524 | - |
525 | - >>> search_tasks_and_print_bugs( |
526 | - ... tag=all('test-tag-1', 'test-tag-2')) |
527 | - test bug a [test-tag-1, test-tag-2] |
528 | - |
529 | -Search for the absence of a tag is possible by prefixing the tag name |
530 | -with a minus. |
531 | - |
532 | - >>> search_tasks_and_print_bugs(tag=any('-test-tag-2')) |
533 | - Firefox does not support SVG [] |
534 | - Reflow problems with complex page layouts [layout-test] |
535 | - Firefox install instructions should be complete [doc] |
536 | - Firefox crashes when Save As dialog for a nonexistent window is closed [] |
537 | - Some bug [] |
538 | - test bug b [test-tag-1] |
539 | - |
540 | -The any() and all() search combinators are taken into consideration |
541 | -when searching for the absence of tags too. The following search says |
542 | -"give me bugs that don't have the test-tag-2 tag set *OR* that don't |
543 | -have the layout-test tag set". Only test-bug-1 is elimininated because |
544 | -it has no tags other than those requested in the search. |
545 | - |
546 | - >>> search_tasks_and_print_bugs( |
547 | - ... tag=any('-test-tag-1', '-test-tag-2')) |
548 | - Firefox does not support SVG [] |
549 | - Reflow problems with complex page layouts [layout-test] |
550 | - Firefox install instructions should be complete [doc] |
551 | - Firefox crashes when Save As dialog for a nonexistent window is closed [] |
552 | - Some bug [] |
553 | - test bug b [test-tag-1] |
554 | - |
555 | -Whereas the following search says "give me bugs that don't have the |
556 | -test-tag-2 tag set *AND* that don't have the layout-test tag set". |
557 | - |
558 | - >>> search_tasks_and_print_bugs( |
559 | - ... tag=all('-test-tag-2', '-layout-test')) |
560 | - Firefox does not support SVG [] |
561 | - Firefox install instructions should be complete [doc] |
562 | - Firefox crashes when Save As dialog for a nonexistent window is closed [] |
563 | - Some bug [] |
564 | - test bug b [test-tag-1] |
565 | - |
566 | -Searching for the presence of any tags at all is also possible using a |
567 | -wildcard. If prefixed with a minus it searches for the absence of |
568 | -tags. |
569 | - |
570 | - >>> search_tasks_and_print_bugs(tag=all('*')) |
571 | - Reflow problems with complex page layouts [layout-test] |
572 | - Firefox install instructions should be complete [doc] |
573 | - test bug a [test-tag-1, test-tag-2] |
574 | - test bug b [test-tag-1] |
575 | - |
576 | - >>> search_tasks_and_print_bugs(tag=all('-*')) |
577 | - Firefox does not support SVG [] |
578 | - Firefox crashes when Save As dialog for a nonexistent window is closed [] |
579 | - Some bug [] |
580 | - |
581 | -Searching for the presence and absence of tags finds no |
582 | -matches. Unsurprisingly. |
583 | - |
584 | - >>> search_tasks_and_print_bugs(tag=all('*', '-*')) |
585 | - |
586 | -Wildcards can be combined with non-wildcard tags. The following finds |
587 | -all bugs with tags, but without test-tag-1: |
588 | - |
589 | - >>> search_tasks_and_print_bugs(tag=all('*', '-test-tag-1')) |
590 | - Reflow problems with complex page layouts [layout-test] |
591 | - Firefox install instructions should be complete [doc] |
592 | - |
593 | -The following is very similar; it finds all bugs with tags, *or* |
594 | -without test-tag-1: |
595 | - |
596 | - >>> search_tasks_and_print_bugs(tag=any('*', '-test-tag-1')) |
597 | - Firefox does not support SVG [] |
598 | - Reflow problems with complex page layouts [layout-test] |
599 | - Firefox install instructions should be complete [doc] |
600 | - Firefox crashes when Save As dialog for a nonexistent window is closed [] |
601 | - Some bug [] |
602 | - test bug a [test-tag-1, test-tag-2] |
603 | - test bug b [test-tag-1] |
604 | - |
605 | -The following finds all untagged bugs and bugs with the doc tag. |
606 | - |
607 | - >>> search_tasks_and_print_bugs(tag=any('-*', 'doc')) |
608 | - Firefox does not support SVG [] |
609 | - Firefox install instructions should be complete [doc] |
610 | - Firefox crashes when Save As dialog for a nonexistent window is closed [] |
611 | - Some bug [] |
612 | - |
613 | - |
614 | -== Searching by date_closed == |
615 | - |
616 | -It's possible to limit the search by date_closed, to get only bugs |
617 | -closed after a certain date. greater_than is used to search for bugs |
618 | -closed after a certain date. |
619 | - |
620 | - >>> import pytz |
621 | - >>> from datetime import datetime, timedelta |
622 | - >>> from lp.services.searchbuilder import greater_than |
623 | - >>> product = factory.makeProduct() |
624 | - >>> utc_now = datetime(2008, 9, 4, 12, 0, 0, tzinfo=pytz.timezone('UTC')) |
625 | - >>> not_closed_bug = factory.makeBug(target=product, title="Not closed") |
626 | - >>> bug_closed_a_day_ago = factory.makeBug( |
627 | - ... target=product, date_closed=utc_now-timedelta(days=1), |
628 | - ... title="Closed a day ago") |
629 | - >>> bug_closed_a_week_ago = factory.makeBug( |
630 | - ... target=product, date_closed=utc_now-timedelta(days=7), |
631 | - ... title="Closed a week ago") |
632 | - |
633 | - >>> search_params = BugTaskSearchParams( |
634 | - ... user=None, orderby="-date_closed", |
635 | - ... date_closed=greater_than(utc_now)) |
636 | - >>> list(product.searchTasks(search_params)) |
637 | - [] |
638 | - |
639 | - >>> search_params.date_closed = greater_than(utc_now - timedelta(days=2)) |
640 | - >>> for bug_task in product.searchTasks(search_params): |
641 | - ... print bug_task.bug.title |
642 | - Closed a day ago |
643 | - |
644 | - |
645 | -== Searching for bug with attachments == |
646 | - |
647 | -It's possible to search for bugs with an attachment of a certain type. |
648 | - |
649 | - >>> from StringIO import StringIO |
650 | - >>> from lp.services.librarian.interfaces import ILibraryFileAliasSet |
651 | - >>> from lp.services.messages.interfaces.message import IMessageSet |
652 | - >>> from lp.bugs.interfaces.bugattachment import ( |
653 | - ... BugAttachmentType, |
654 | - ... IBugAttachmentSet, |
655 | - ... ) |
656 | - >>> product = factory.makeProduct() |
657 | - >>> patch_bug = factory.makeBug(target=product) |
658 | - >>> filecontent = 'Some diff data' |
659 | - >>> filealias = getUtility(ILibraryFileAliasSet).create( |
660 | - ... name='patch.diff', size=len(filecontent), |
661 | - ... file=StringIO(filecontent), contentType='text/plain') |
662 | - >>> message = getUtility(IMessageSet).fromText( |
663 | - ... subject="title", content="added a patch.") |
664 | - >>> attachmentset = getUtility(IBugAttachmentSet) |
665 | - >>> attachment = attachmentset.create( |
666 | - ... bug=patch_bug, filealias=filealias, title='Patch', |
667 | - ... message=message, attach_type=BugAttachmentType.PATCH) |
668 | - >>> patch_bug.attachments.count() |
669 | - 1 |
670 | - |
671 | -We've added an attachment to our new bug with an attachment type |
672 | -PATCH. Searching for bugs with that attachment type we get one result. |
673 | - |
674 | - >>> search_params = BugTaskSearchParams( |
675 | - ... user=None, attachmenttype=BugAttachmentType.PATCH) |
676 | - >>> product.searchTasks(search_params).count() |
677 | - 1 |
678 | - |
679 | - >>> filecontent = 'Some more diff data' |
680 | - >>> filealias = getUtility(ILibraryFileAliasSet).create( |
681 | - ... name='patch.diff', size=len(filecontent), |
682 | - ... file=StringIO(filecontent), contentType='text/plain') |
683 | - >>> message = getUtility(IMessageSet).fromText( |
684 | - ... subject="title", content="added another patch.") |
685 | - >>> attachmentset = getUtility(IBugAttachmentSet) |
686 | - >>> attachment = attachmentset.create( |
687 | - ... bug=patch_bug, filealias=filealias, title='Patch 2', |
688 | - ... message=message, attach_type=BugAttachmentType.PATCH) |
689 | - >>> patch_bug.attachments.count() |
690 | - 2 |
691 | - |
692 | -We've added another patch to the bug. Searching for bugs with patch |
693 | -attachments still returns a single result, since even though a new |
694 | -attachment was added, there is still only one bug with attachments. |
695 | - |
696 | - >>> product.searchTasks(search_params).count() |
697 | - 1 |
698 | - |
699 | -== Searching for bugs affecting a user == |
700 | - |
701 | -We can search for bugs which a user marked as affecting them. |
702 | - |
703 | - >>> affecting_bug = factory.makeBug(title='A bug affecting a user') |
704 | - >>> affected_user = factory.makePerson(name='affected-user') |
705 | - >>> affecting_bug.markUserAffected(affected_user) |
706 | - >>> target = affecting_bug.bugtasks[0].target |
707 | - >>> affecting_tasks = target.searchTasks( |
708 | - ... None, user=None, affected_user=affected_user) |
709 | - >>> for task in affecting_tasks: |
710 | - ... print task.bug.title |
711 | - A bug affecting a user |
712 | - |
713 | - |
714 | == Searching for bugs related to hardware == |
715 | |
716 | We can search for bugs which are related to a given hardware device or |
717 | @@ -825,7 +175,7 @@ |
718 | 9 Foo Bar |
719 | 10 Foo Bar |
720 | 2 Sample Person |
721 | - 19 No Privileges Person |
722 | + 16 No Privileges Person |
723 | |
724 | Similary, we can search for device drivers appearing in HWDB submissions |
725 | of a bug reporter. |
726 | @@ -871,6 +221,8 @@ |
727 | The PCI device (0x10de, 0x0455) is not controlled in any HWDB submission |
728 | by the sd driver, so we'll get an empty result set for this query. |
729 | |
730 | + >>> from lp.registry.interfaces.product import IProductSet |
731 | + >>> firefox = getUtility(IProductSet).getByName('firefox') |
732 | >>> search_params = BugTaskSearchParams( |
733 | ... user=None, hardware_bus=HWBus.PCI, hardware_vendor_id='0x10de', |
734 | ... hardware_product_id='0x0455', hardware_driver_name='sd', |
735 | @@ -880,7 +232,8 @@ |
736 | |
737 | We can also search for device owners which are subscribed to a bug. |
738 | |
739 | - >>> sample_person = getUtility(IPersonSet).getByEmail('test@canonical.com') |
740 | + >>> sample_person = getUtility(IPersonSet).getByEmail( |
741 | + ... 'test@canonical.com') |
742 | >>> search_params = BugTaskSearchParams( |
743 | ... user=None, hardware_bus=HWBus.PCI, hardware_vendor_id='0x10de', |
744 | ... hardware_product_id='0x0455', |
745 | @@ -909,14 +262,14 @@ |
746 | >>> from lp.hardwaredb.interfaces.hwdb import IHWSubmissionSet |
747 | >>> hw_submission = getUtility(IHWSubmissionSet).getBySubmissionKey( |
748 | ... 'sample-submission') |
749 | - >>> bug_19 = getUtility(IBugSet).get(19) |
750 | - >>> bug_19.linkHWSubmission(hw_submission) |
751 | + >>> bug_16 = getUtility(IBugSet).get(16) |
752 | + >>> bug_16.linkHWSubmission(hw_submission) |
753 | >>> search_params = BugTaskSearchParams( |
754 | ... user=None, hardware_bus=HWBus.PCI, hardware_vendor_id='0x10de', |
755 | ... hardware_product_id='0x0455', hardware_is_linked_to_bug=True) |
756 | >>> for bugtask in ubuntu.searchTasks(search_params): |
757 | ... print bugtask.bug.id |
758 | - 19 |
759 | + 16 |
760 | |
761 | If a device appears in a private submission, related bugs are shown |
762 | only if the user running the request is the owner of the submission |
763 | @@ -930,14 +283,15 @@ |
764 | ... hardware_is_linked_to_bug=True) |
765 | >>> for bugtask in ubuntu.searchTasks(search_params): |
766 | ... print bugtask.bug.id |
767 | - 19 |
768 | + 16 |
769 | |
770 | + >>> foo_bar = getUtility(IPersonSet).getByEmail('foo.bar@canonical.com') |
771 | >>> search_params = BugTaskSearchParams( |
772 | ... user=foo_bar, hardware_bus=HWBus.PCI, hardware_vendor_id='0x10de', |
773 | ... hardware_product_id='0x0455', hardware_is_linked_to_bug=True) |
774 | >>> for bugtask in ubuntu.searchTasks(search_params): |
775 | ... print bugtask.bug.id |
776 | - 19 |
777 | + 16 |
778 | |
779 | Other users cannot see that a bug is related to a device from a |
780 | private submission. |
781 | @@ -954,55 +308,6 @@ |
782 | >>> ubuntu.searchTasks(search_params).count() |
783 | 0 |
784 | |
785 | -== Searching for bugs affecting me == |
786 | - |
787 | -The user searching for bugs can search for bugs affecting him. |
788 | - |
789 | -We search for bugs affecting foo_bar, then check that all the results |
790 | -return True for isUserAffected(foo_bar). |
791 | - |
792 | - >>> search_params = BugTaskSearchParams( |
793 | - ... user=foo_bar, affects_me=True) |
794 | - >>> print reduce( |
795 | - ... lambda x, y: x and y, |
796 | - ... [task.bug.isUserAffected(foo_bar) |
797 | - ... for task in firefox.searchTasks(search_params)]) |
798 | - True |
799 | - |
800 | - |
801 | -== Searching for bugs linked to branches == |
802 | - |
803 | -We can search for bugs having branches linked to them. |
804 | - |
805 | - >>> from lp.bugs.interfaces.bugtasksearch import BugBranchSearch |
806 | - >>> search_params = BugTaskSearchParams( |
807 | - ... user=None, linked_branches=BugBranchSearch.BUGS_WITH_BRANCHES) |
808 | - >>> for task in firefox.searchTasks(search_params): |
809 | - ... print task.bug.id, task.bug.linked_branches.count() |
810 | - 4 2 |
811 | - 5 1 |
812 | - |
813 | -Similarly, we can search for bugs that do not have any linked branches. |
814 | - |
815 | - >>> search_params = BugTaskSearchParams( |
816 | - ... user=None, linked_branches=BugBranchSearch.BUGS_WITHOUT_BRANCHES) |
817 | - >>> for task in firefox.searchTasks(search_params): |
818 | - ... print task.bug.id, task.bug.linked_branches.count() |
819 | - 1 0 |
820 | - 6 0 |
821 | - 18 0 |
822 | - 20 0 |
823 | - 21 0 |
824 | - |
825 | -And we can search for bugs linked to a specific branch. |
826 | - |
827 | - >>> search_params = BugTaskSearchParams( |
828 | - ... user=None, linked_branches=1) |
829 | - >>> for task in firefox.searchTasks(search_params): |
830 | - ... print task.bug.id |
831 | - 4 |
832 | - 5 |
833 | - |
834 | |
835 | == Ordering search results == |
836 | |
837 | @@ -1015,6 +320,11 @@ |
838 | |
839 | Here is the list of bugs for Ubuntu. |
840 | |
841 | + >>> def bugTaskInfo(bugtask): |
842 | + ... return '%i %i %s %s' % ( |
843 | + ... bugtask.id, bugtask.bug.id, bugtask.bugtargetdisplayname, |
844 | + ... bugtask.bug.title) |
845 | + |
846 | >>> params = BugTaskSearchParams( |
847 | ... orderby='-number_of_duplicates', user=None) |
848 | >>> ubuntu_tasks = ubuntu.searchTasks(params) |
849 | @@ -1024,7 +334,7 @@ |
850 | 23 9 thunderbird (Ubuntu) Thunderbird crashes |
851 | 25 10 linux-source-2.6.15 (Ubuntu) another test bug |
852 | 26 2 Ubuntu Blackhole Trash folder |
853 | - 41 19 cdrkit (Ubuntu) Bug to be fixed in trunk |
854 | + 36 16 cdrkit (Ubuntu) Bug to be fixed in trunk |
855 | |
856 | None of these bugs have any duplicates. |
857 | |
858 | @@ -1041,7 +351,8 @@ |
859 | >>> bug_ten.markAsDuplicate(bug_nine) |
860 | >>> flush_database_updates() |
861 | |
862 | -Searching again reveals bug #9 at the top of the list, since it now has a duplicate. |
863 | +Searching again reveals bug #9 at the top of the list, since it now has |
864 | +a duplicate. |
865 | |
866 | >>> ubuntu_tasks = ubuntu.searchTasks(params) |
867 | >>> for bugtask in ubuntu_tasks: |
868 | @@ -1050,7 +361,7 @@ |
869 | 17 1 mozilla-firefox (Ubuntu) Firefox does not support SVG |
870 | 25 10 linux-source-2.6.15 (Ubuntu) another test bug |
871 | 26 2 Ubuntu Blackhole Trash folder |
872 | - 41 19 cdrkit (Ubuntu) Bug to be fixed in trunk |
873 | + 36 16 cdrkit (Ubuntu) Bug to be fixed in trunk |
874 | |
875 | |
876 | === Ordering by number of comments === |
877 | @@ -1066,11 +377,11 @@ |
878 | ... bug = bugtask.bug |
879 | ... print '%s %s [%s comments]' % ( |
880 | ... bug.id, bug.title, bug.message_count) |
881 | - 2 Blackhole Trash folder [5 comments] |
882 | - 1 Firefox does not support SVG [3 comments] |
883 | + 2 Blackhole Trash folder [3 comments] |
884 | + 1 Firefox does not support SVG [2 comments] |
885 | 10 another test bug [2 comments] |
886 | 9 Thunderbird crashes [1 comments] |
887 | - 19 Bug to be fixed in trunk [1 comments] |
888 | + 16 Bug to be fixed in trunk [1 comments] |
889 | |
890 | |
891 | === Ordering by bug heat === |
892 | @@ -1090,7 +401,7 @@ |
893 | ... bug = bugtask.bug |
894 | ... print '%s %s [heat: %s]' % ( |
895 | ... bug.id, bug.title, bug.heat) |
896 | - 19 Bug to be fixed in trunk [heat: 19] |
897 | + 16 Bug to be fixed in trunk [heat: 16] |
898 | 10 another test bug [heat: 10] |
899 | 9 Thunderbird crashes [heat: 9] |
900 | 2 Blackhole Trash folder [heat: 2] |
901 | @@ -1111,7 +422,7 @@ |
902 | >>> ubuntu_tasks = ubuntu.searchTasks(params) |
903 | >>> for bugtask in ubuntu_tasks: |
904 | ... print bugTaskInfo(bugtask) |
905 | - 41 19 cdrkit (Ubuntu) Bug to be fixed in trunk |
906 | + 36 16 cdrkit (Ubuntu) Bug to be fixed in trunk |
907 | 26 2 Ubuntu Blackhole Trash folder |
908 | 25 10 linux-source-2.6.15 (Ubuntu) another test bug |
909 | 23 9 thunderbird (Ubuntu) Thunderbird crashes |
910 | @@ -1119,6 +430,7 @@ |
911 | |
912 | If we add a patch attachment to bug 2 and bug 10, they are listed first. |
913 | |
914 | + >>> bug_two = getUtility(IBugSet).get(2) |
915 | >>> patch_attachment_bug_2 = factory.makeBugAttachment( |
916 | ... bug=bug_two, is_patch=True) |
917 | >>> transaction.commit() |
918 | @@ -1131,237 +443,6 @@ |
919 | ... print bugTaskInfo(bugtask) |
920 | 26 2 Ubuntu Blackhole Trash folder |
921 | 25 10 linux-source-2.6.15 (Ubuntu) another test bug |
922 | - 41 19 cdrkit (Ubuntu) Bug to be fixed in trunk |
923 | + 36 16 cdrkit (Ubuntu) Bug to be fixed in trunk |
924 | 23 9 thunderbird (Ubuntu) Thunderbird crashes |
925 | 17 1 mozilla-firefox (Ubuntu) Firefox does not support SVG |
926 | - |
927 | - |
928 | -== Searching using a flat interface == |
929 | - |
930 | -An alternative, simplified interface for searching bug tasks is available |
931 | -by passing all search parameters as function arguments to searchTasks. |
932 | -This interface corresponds to the search options available from the web |
933 | -search form and the public API. |
934 | - |
935 | - >>> def print_bugtasks(bugtasks): |
936 | - ... for bugtask in bugtasks: |
937 | - ... print '%s %s %s %s %s' % ( |
938 | - ... bugtask.bug.id, bugtask.bugtargetdisplayname, |
939 | - ... bugtask.bug.title, bugtask.status.title.upper(), |
940 | - ... bugtask.importance.title.upper()) |
941 | - |
942 | - |
943 | -When we call the method on Ubuntu, without any parameters, the result is |
944 | -identical to calling searchTasks. |
945 | - |
946 | - >>> print_bugtasks(ubuntu.searchTasks(None, user=None)) |
947 | - 1 mozilla-firefox (Ubuntu) Firefox does not support SVG NEW MEDIUM |
948 | - 9 thunderbird (Ubuntu) Thunderbird crashes CONFIRMED MEDIUM |
949 | - 2 Ubuntu Blackhole Trash folder NEW MEDIUM |
950 | - 19 cdrkit (Ubuntu) Bug to be fixed in trunk NEW UNDECIDED |
951 | - |
952 | -If we want to restrict the search using certain parameters pass them to |
953 | -the function directly. Here we search Ubuntu again, but only bugs for |
954 | -the firefox package. |
955 | - |
956 | - >>> from lp.registry.interfaces.sourcepackagename import ( |
957 | - ... ISourcePackageNameSet) |
958 | - >>> source_package_name_set = getUtility(ISourcePackageNameSet) |
959 | - >>> firefox_source_package = source_package_name_set['mozilla-firefox'] |
960 | - >>> print_bugtasks(ubuntu.searchTasks( |
961 | - ... None, user=None, sourcepackagename=firefox_source_package)) |
962 | - 1 mozilla-firefox (Ubuntu) Firefox does not support SVG NEW MEDIUM |
963 | - |
964 | -Or search for bugs on a distribution source package directly. |
965 | - |
966 | - >>> print_bugtasks(ubuntu_firefox.searchTasks(None, user=None)) |
967 | - 1 mozilla-firefox (Ubuntu) Firefox does not support SVG NEW MEDIUM |
968 | - |
969 | -Or we can search a certain milestone (only getting bugs targeted to it). |
970 | - |
971 | - >>> firefox_milestone_1 = firefox.getMilestone('1.0') |
972 | - >>> print_bugtasks(firefox_milestone_1.searchTasks(None, user=None)) |
973 | - 1 Mozilla Firefox Firefox does not support SVG NEW LOW |
974 | - 1 Mozilla Firefox 1.0 Firefox does not support SVG NEW UNDECIDED |
975 | - |
976 | -We can restrict our search for firefox bugs with a text search. |
977 | - |
978 | - >>> print_bugtasks(firefox.searchTasks( |
979 | - ... None, user=None, search_text=u'instructions')) |
980 | - 5 Mozilla Firefox Firefox install instructions should be complete NEW CRITICAL |
981 | - |
982 | -Or restrict our search (over the mozilla project this time) to a list of |
983 | -relevant importance values. |
984 | - |
985 | - >>> print_bugtasks(mozilla.searchTasks( |
986 | - ... None, user=None, |
987 | - ... importance=[BugTaskImportance.LOW, BugTaskImportance.MEDIUM])) |
988 | - 4 Mozilla Firefox Reflow problems with complex page layouts NEW MEDIUM |
989 | - 1 Mozilla Firefox Firefox does not support SVG NEW LOW |
990 | - |
991 | - |
992 | -== Searching by structural subscriber == |
993 | - |
994 | -The 'structural_subscriber' search parameter allows one to search all the bug |
995 | -tasks to which a person is structurally subscribed. A person can be a |
996 | -structural subscriber for a product, a product series, a project, a milestone, |
997 | -a distribution, a distribution series and a distribution source package. No |
998 | -Privileges Person isn't a structural subscriber, so no bug tasks are found: |
999 | - |
1000 | - >>> from lp.registry.interfaces.person import IPersonSet |
1001 | - >>> no_priv = getUtility(IPersonSet).getByName('no-priv') |
1002 | - >>> no_priv_struct_sub = BugTaskSearchParams( |
1003 | - ... user=None, structural_subscriber=no_priv) |
1004 | - >>> found_bugtasks = bugtask_set.search(no_priv_struct_sub) |
1005 | - >>> found_bugtasks.count() |
1006 | - 0 |
1007 | - |
1008 | -Create a new person and make them a subscriber to all Firefox (product) bug |
1009 | -reports. Subsequently, we confirm that they are subscribed to all of the |
1010 | -Firefox bug tasks. |
1011 | - |
1012 | - >>> product_struct_subber = factory.makePerson( |
1013 | - ... name='product-struct-subber') |
1014 | - >>> firefox.addBugSubscription(product_struct_subber, |
1015 | - ... product_struct_subber) |
1016 | - <...StructuralSubscription object at ...> |
1017 | - >>> product_struct_sub_search = BugTaskSearchParams( |
1018 | - ... user=None, structural_subscriber=product_struct_subber) |
1019 | - >>> found_bugtasks = bugtask_set.search(product_struct_sub_search) |
1020 | - >>> found_bugtasks.count() |
1021 | - 7 |
1022 | - |
1023 | -Create a new person and subscribe them to all of the bug tasks for a product |
1024 | -series. We then test to see that they are subscribed to all of the bug tasks |
1025 | -for the product series in which they are interested. |
1026 | - |
1027 | - >>> product_series = firefox.getSeries('1.0') |
1028 | - >>> all_targeted = BugTaskSearchParams(user=None, omit_targeted=False) |
1029 | - >>> series_tasks = product_series.searchTasks(all_targeted) |
1030 | - >>> series_struct_subber = factory.makePerson( |
1031 | - ... name='series-struct-subber') |
1032 | - >>> product_series.addBugSubscription(series_struct_subber, |
1033 | - ... series_struct_subber) |
1034 | - <...StructuralSubscription object at ...> |
1035 | - >>> series_struct_sub_search = BugTaskSearchParams( |
1036 | - ... user=None, structural_subscriber=series_struct_subber) |
1037 | - >>> found_bugtasks = bugtask_set.search(series_struct_sub_search) |
1038 | - >>> found_bugtasks.count() |
1039 | - 2 |
1040 | - |
1041 | -Create a new product which will be a part of a project group. A bug is |
1042 | -created for the product which should then show up for structural subscribers |
1043 | -of the project group. Then a new person is created who is subscribed to all |
1044 | -of the bug reports about the project. Search for bug tasks that this new |
1045 | -person is subscribed. |
1046 | - |
1047 | - >>> product = factory.makeProduct() |
1048 | - >>> bug = factory.makeBug(target=product) |
1049 | - >>> project = factory.makeProject() |
1050 | - >>> product.project = project |
1051 | - >>> project_struct_subber = factory.makePerson( |
1052 | - ... name='project-struct-subber') |
1053 | - >>> project.addBugSubscription(project_struct_subber, |
1054 | - ... project_struct_subber) |
1055 | - <...StructuralSubscription object at ...> |
1056 | - >>> project_struct_sub_search = BugTaskSearchParams( |
1057 | - ... user=None, structural_subscriber=project_struct_subber) |
1058 | - >>> found_bugtasks = bugtask_set.search(project_struct_sub_search) |
1059 | - >>> found_bugtasks.count() |
1060 | - 1 |
1061 | - |
1062 | -We will also subscribe this project subscriber to a product that is a part of |
1063 | -the project and ensure that duplicate bug tasks do not appear in the search |
1064 | -results. |
1065 | - |
1066 | - >>> product2 = factory.makeProduct() |
1067 | - >>> bug = factory.makeBug(target=product2) |
1068 | - >>> product2.project = project |
1069 | - >>> product2.addBugSubscription(project_struct_subber, |
1070 | - ... project_struct_subber) |
1071 | - <...StructuralSubscription object at ...> |
1072 | - >>> project_struct_sub_search = BugTaskSearchParams( |
1073 | - ... user=None, structural_subscriber=project_struct_subber) |
1074 | - >>> found_bugtasks = bugtask_set.search(project_struct_sub_search) |
1075 | - >>> found_bugtasks.count() |
1076 | - 2 |
1077 | - |
1078 | -Create a new person and subscribe them to all of bug tasks targeted to a |
1079 | -milestone. We then test to see that they are subscribed to all of the |
1080 | -bug tasks for the milestone in which they are interested. |
1081 | - |
1082 | - >>> milestone_struct_subber = factory.makePerson( |
1083 | - ... name='milestone-struct-subber') |
1084 | - >>> product_milestone.addBugSubscription(milestone_struct_subber, |
1085 | - ... milestone_struct_subber) |
1086 | - <...StructuralSubscription object at ...> |
1087 | - >>> milestone_struct_sub_search = BugTaskSearchParams( |
1088 | - ... user=None, structural_subscriber=milestone_struct_subber) |
1089 | - >>> found_bugtasks = bugtask_set.search(milestone_struct_sub_search) |
1090 | - >>> found_bugtasks.count() |
1091 | - 2 |
1092 | - |
1093 | -Create another new person and subscribe them to all the Ubuntu bug reports - |
1094 | -crazy I know. Then test to see that this poor person is subscribed to all |
1095 | -bugs with an Ubuntu bug task. |
1096 | - |
1097 | - >>> distro_struct_subber = factory.makePerson( |
1098 | - ... name='distro-struct-subber') |
1099 | - >>> ubuntu.addBugSubscription(distro_struct_subber, |
1100 | - ... distro_struct_subber) |
1101 | - <...StructuralSubscription object at ...> |
1102 | - >>> distro_struct_sub_search = BugTaskSearchParams( |
1103 | - ... user=None, structural_subscriber=distro_struct_subber) |
1104 | - >>> found_bugtasks = bugtask_set.search(distro_struct_sub_search) |
1105 | - >>> found_bugtasks.count() |
1106 | - 5 |
1107 | - |
1108 | -Create a new person who will only be subscribed to an Ubuntu series, which is |
1109 | -something more reasonable than all of Ubuntu. Test to ensure that this person |
1110 | -is subscribed to all of the bug tasks about that distro series. |
1111 | - |
1112 | - >>> ubuntu = getUtility(IDistributionSet).getByName('ubuntu') |
1113 | - >>> hoary = ubuntu.getSeries('hoary') |
1114 | - >>> distro_series_struct_subber = factory.makePerson( |
1115 | - ... name='distro-series-struct-subber') |
1116 | - >>> hoary.addBugSubscription(distro_series_struct_subber, |
1117 | - ... distro_series_struct_subber) |
1118 | - <...StructuralSubscription object at ...> |
1119 | - >>> distro_series_struct_sub_search = BugTaskSearchParams( |
1120 | - ... user=None, structural_subscriber=distro_series_struct_subber) |
1121 | - >>> found_bugtasks = bugtask_set.search(distro_series_struct_sub_search) |
1122 | - >>> all_targeted = BugTaskSearchParams(user=None, omit_targeted=False) |
1123 | - >>> hoary_bugtasks = hoary.searchTasks(all_targeted) |
1124 | - >>> found_bugtasks.count() == hoary_bugtasks.count() |
1125 | - True |
1126 | - |
1127 | -Create a new person and make them a subscriber to all Ubuntu Firefox (a |
1128 | -distribution source package) bug reports. Test to see that the new person is |
1129 | -subscribed to all of the Ubuntu Firefox bug tasks. |
1130 | - |
1131 | - >>> package_struct_subber = factory.makePerson( |
1132 | - ... name='package-struct-subber') |
1133 | - >>> ubuntu_firefox.addBugSubscription(package_struct_subber, |
1134 | - ... package_struct_subber) |
1135 | - <...StructuralSubscription object at ...> |
1136 | - >>> package_struct_sub_search = BugTaskSearchParams( |
1137 | - ... user=None, structural_subscriber=package_struct_subber) |
1138 | - >>> found_bugtasks = bugtask_set.search(package_struct_sub_search) |
1139 | - >>> found_bugtasks.count() |
1140 | - 1 |
1141 | - |
1142 | -We'll also subscribe the person who is currently subscribed to a package's bug |
1143 | -reports, package_struct_subber, to the bug reports of a product series to |
1144 | -ensure that the structural_subscriber search is returning the set of both bug |
1145 | -tasks. |
1146 | - |
1147 | - >>> product_series.addBugSubscription(package_struct_subber, |
1148 | - ... package_struct_subber) |
1149 | - <...StructuralSubscription object at ...> |
1150 | - >>> package_struct_sub_search = BugTaskSearchParams( |
1151 | - ... user=None, structural_subscriber=package_struct_subber) |
1152 | - >>> found_bugtasks = bugtask_set.search(package_struct_sub_search) |
1153 | - >>> combined_bugtasks_count = (ubuntu_firefox_bugs.count () + |
1154 | - ... series_tasks.count()) |
1155 | - >>> found_bugtasks.count() == combined_bugtasks_count |
1156 | - True |
1157 | |
1158 | === modified file 'lib/lp/bugs/model/bugtasksearch.py' |
1159 | --- lib/lp/bugs/model/bugtasksearch.py 2012-09-24 05:17:00 +0000 |
1160 | +++ lib/lp/bugs/model/bugtasksearch.py 2012-09-28 22:06:42 +0000 |
1161 | @@ -87,11 +87,9 @@ |
1162 | from lp.services.database.lpstorm import IStore |
1163 | from lp.services.database.sqlbase import sqlvalues |
1164 | from lp.services.database.stormexpr import ( |
1165 | - Array, |
1166 | ArrayAgg, |
1167 | ArrayIntersects, |
1168 | get_where_for_reference, |
1169 | - NullCount, |
1170 | Unnest, |
1171 | ) |
1172 | from lp.services.propertycache import get_property_cache |
1173 | @@ -478,8 +476,6 @@ |
1174 | BugSubscription.person == params.subscriber)) |
1175 | |
1176 | if params.structural_subscriber is not None: |
1177 | - # See bug 787294 for the story that led to the query elements |
1178 | - # below. Please change with care. |
1179 | with_clauses.append( |
1180 | '''ss as (SELECT * from StructuralSubscription |
1181 | WHERE StructuralSubscription.subscriber = %s)''' |
1182 | @@ -488,79 +484,65 @@ |
1183 | class StructuralSubscriptionCTE(StructuralSubscription): |
1184 | __storm_table__ = 'ss' |
1185 | |
1186 | - join_tables.append( |
1187 | - (Product, LeftJoin(Product, And( |
1188 | - BugTaskFlat.product_id == Product.id, |
1189 | - Product.active)))) |
1190 | - ProductSub = ClassAlias(StructuralSubscriptionCTE) |
1191 | - join_tables.append(( |
1192 | - ProductSub, |
1193 | - LeftJoin( |
1194 | - ProductSub, |
1195 | - BugTaskFlat.product_id == ProductSub.productID))) |
1196 | - ProductSeriesSub = ClassAlias(StructuralSubscriptionCTE) |
1197 | - join_tables.append(( |
1198 | - ProductSeriesSub, |
1199 | - LeftJoin( |
1200 | - ProductSeriesSub, |
1201 | - BugTaskFlat.productseries_id == |
1202 | - ProductSeriesSub.productseriesID))) |
1203 | - ProjectSub = ClassAlias(StructuralSubscriptionCTE) |
1204 | - join_tables.append(( |
1205 | - ProjectSub, |
1206 | - LeftJoin( |
1207 | - ProjectSub, |
1208 | - Product.projectID == ProjectSub.projectID))) |
1209 | - DistributionSub = ClassAlias(StructuralSubscriptionCTE) |
1210 | - join_tables.append(( |
1211 | - DistributionSub, |
1212 | - LeftJoin( |
1213 | - DistributionSub, |
1214 | - And(BugTaskFlat.distribution_id == |
1215 | - DistributionSub.distributionID, |
1216 | - Or( |
1217 | - DistributionSub.sourcepackagenameID == |
1218 | - BugTaskFlat.sourcepackagename_id, |
1219 | - DistributionSub.sourcepackagenameID == None))))) |
1220 | - if params.distroseries is not None: |
1221 | - parent_distro_id = params.distroseries.distributionID |
1222 | - else: |
1223 | - parent_distro_id = 0 |
1224 | - DistroSeriesSub = ClassAlias(StructuralSubscriptionCTE) |
1225 | - join_tables.append(( |
1226 | - DistroSeriesSub, |
1227 | - LeftJoin( |
1228 | - DistroSeriesSub, |
1229 | - Or(BugTaskFlat.distroseries_id == |
1230 | - DistroSeriesSub.distroseriesID, |
1231 | - # There is a mismatch between BugTask and |
1232 | - # StructuralSubscription. SS does not support |
1233 | - # distroseries. This clause works because other |
1234 | - # joins ensure the match bugtask is the right |
1235 | - # series. |
1236 | - And(parent_distro_id == DistroSeriesSub.distributionID, |
1237 | - BugTaskFlat.sourcepackagename_id == |
1238 | - DistroSeriesSub.sourcepackagenameID))))) |
1239 | - MilestoneSub = ClassAlias(StructuralSubscriptionCTE) |
1240 | - join_tables.append(( |
1241 | - MilestoneSub, |
1242 | - LeftJoin( |
1243 | - MilestoneSub, |
1244 | - BugTaskFlat.milestone_id == MilestoneSub.milestoneID))) |
1245 | - extra_clauses.append( |
1246 | - NullCount(Array( |
1247 | - ProductSub.id, ProductSeriesSub.id, ProjectSub.id, |
1248 | - DistributionSub.id, DistroSeriesSub.id, MilestoneSub.id)) < 6) |
1249 | - has_duplicate_results = True |
1250 | + SS = ClassAlias(StructuralSubscriptionCTE) |
1251 | + # Milestones apply to all structural subscription searches. |
1252 | + ss_clauses = [ |
1253 | + In(BugTaskFlat.milestone_id, Select(SS.milestoneID, tables=[SS]))] |
1254 | + if (params.project is None |
1255 | + and params.product is None and params.productseries is None): |
1256 | + # This search is *not* contrained to project related bugs, so |
1257 | + # include distro, distroseries, DSP and SP subscriptions. |
1258 | + ss_clauses.append(In( |
1259 | + BugTaskFlat.distribution_id, |
1260 | + Select(SS.distributionID, tables=[SS], |
1261 | + where=(SS.sourcepackagenameID == None)))) |
1262 | + ss_clauses.append(In( |
1263 | + Row(BugTaskFlat.distribution_id, |
1264 | + BugTaskFlat.sourcepackagename_id), |
1265 | + Select((SS.distributionID, SS.sourcepackagenameID), |
1266 | + tables=[SS]))) |
1267 | + ss_clauses.append(In( |
1268 | + BugTaskFlat.distroseries_id, |
1269 | + Select(SS.distroseriesID, tables=[SS], |
1270 | + where=(SS.sourcepackagenameID == None)))) |
1271 | + # Users expect to find their DSP subscriptions when searching |
1272 | + # distroseries. We only include these when we need to. |
1273 | + if params.distroseries is not None: |
1274 | + distroseries_id = params.distroseries.id |
1275 | + parent_distro_id = params.distroseries.distributionID |
1276 | + else: |
1277 | + distroseries_id = 0 |
1278 | + parent_distro_id = 0 |
1279 | + ss_clauses.append(In( |
1280 | + Row(BugTaskFlat.distroseries_id, |
1281 | + BugTaskFlat.sourcepackagename_id), |
1282 | + Select((distroseries_id, SS.sourcepackagenameID), tables=[SS], |
1283 | + where=And( |
1284 | + SS.distributionID == parent_distro_id, |
1285 | + SS.sourcepackagenameID != None)))) |
1286 | + if params.distribution is None and params.distroseries is None: |
1287 | + # This search is *not* contrained to distro related bugs so |
1288 | + # include products, productseries, and project group subscriptions. |
1289 | + project_match = True |
1290 | + if params.project is not None: |
1291 | + project_match = Product.project == params.project |
1292 | + ss_clauses.append(In( |
1293 | + BugTaskFlat.product_id, |
1294 | + Select(SS.productID, tables=[SS]))) |
1295 | + ss_clauses.append(In( |
1296 | + BugTaskFlat.productseries_id, |
1297 | + Select(SS.productseriesID, tables=[SS]))) |
1298 | + ss_clauses.append(In( |
1299 | + BugTaskFlat.product_id, |
1300 | + Select(Product.id, tables=[SS, Product], |
1301 | + where=And( |
1302 | + SS.projectID == Product.projectID, |
1303 | + project_match, |
1304 | + Product.active)))) |
1305 | + extra_clauses.append(Or(*ss_clauses)) |
1306 | |
1307 | - # Remove bugtasks from deactivated products, if necessary. |
1308 | - # We don't have to do this if |
1309 | - # 1) We're searching on bugtasks for a specific product |
1310 | - # 2) We're searching on bugtasks for a specific productseries |
1311 | - # 3) We're searching on bugtasks for a distribution |
1312 | - # 4) We're searching for bugtasks for a distroseries |
1313 | - # because in those instances we don't have arbitrary products which |
1314 | - # may be deactivated showing up in our search. |
1315 | + # Remove bugtasks from deactivated products. This is needed for searches |
1316 | + # where people or project groups are the context. |
1317 | if (params.product is None and |
1318 | params.distribution is None and |
1319 | params.productseries is None and |
1320 | @@ -618,15 +600,10 @@ |
1321 | if tag_clause is not None: |
1322 | extra_clauses.append(tag_clause) |
1323 | |
1324 | - # XXX Tom Berger 2008-02-14: |
1325 | - # We use StructuralSubscription to determine |
1326 | - # the bug supervisor relation for distribution source |
1327 | - # packages, following a conversion to use this object. |
1328 | - # We know that the behaviour remains the same, but we |
1329 | - # should change the terminology, or re-instate |
1330 | - # PackageBugSupervisor, since the use of this relation here |
1331 | - # is not for subscription to notifications. |
1332 | - # See bug #191809 |
1333 | + # XXX sinzui 2012-09-26: |
1334 | + # This uses StructuralSubscription to assume a bug supervisor relationship |
1335 | + # for distribution source packages to preserve historical behaviour. |
1336 | + # This also duplicates params.structural_subscriber code and behaviour. |
1337 | if params.bug_supervisor: |
1338 | extra_clauses.append(Or( |
1339 | In( |
1340 | |
1341 | === modified file 'lib/lp/bugs/model/tests/test_bugtasksearch.py' |
1342 | --- lib/lp/bugs/model/tests/test_bugtasksearch.py 2012-09-21 02:03:56 +0000 |
1343 | +++ lib/lp/bugs/model/tests/test_bugtasksearch.py 2012-09-28 22:06:42 +0000 |
1344 | @@ -2377,6 +2377,29 @@ |
1345 | self.assertContentEqual(bug1.bugtasks, tasks) |
1346 | |
1347 | |
1348 | +class TargetLessTestCase(TestCaseWithFactory): |
1349 | + """Test that do not call setTarget() in the BugTaskSearchParams.""" |
1350 | + |
1351 | + layer = DatabaseFunctionalLayer |
1352 | + |
1353 | + def test_project_group_structural_subscription(self): |
1354 | + # Search results can be limited to bugs without a bug target to which |
1355 | + # a given person has a structural subscription. |
1356 | + subscriber = self.factory.makePerson() |
1357 | + product = self.factory.makeProduct() |
1358 | + self.factory.makeBug(target=product) |
1359 | + with person_logged_in(product.owner): |
1360 | + project_group = self.factory.makeProject(owner=product.owner) |
1361 | + product.project = project_group |
1362 | + with person_logged_in(subscriber): |
1363 | + project_group.addBugSubscription(subscriber, subscriber) |
1364 | + params = BugTaskSearchParams( |
1365 | + user=None, structural_subscriber=subscriber) |
1366 | + bugtask_set = getUtility(IBugTaskSet) |
1367 | + found_bugtasks = bugtask_set.search(params) |
1368 | + self.assertEqual(1, found_bugtasks.count()) |
1369 | + |
1370 | + |
1371 | class BaseGetBugPrivacyFilterTermsTests: |
1372 | |
1373 | layer = DatabaseFunctionalLayer |
This looks alright to me, but I would wait for William's input as the peculiarities of bug search performance is definitely not something I'm up on.