Merge lp:~allenap/launchpad/revert-subscribe-to-tag-bug-151129 into lp:launchpad
- revert-subscribe-to-tag-bug-151129
- Merge into devel
Proposed by
Gavin Panella
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Gavin Panella | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 11985 | ||||
Proposed branch: | lp:~allenap/launchpad/revert-subscribe-to-tag-bug-151129 | ||||
Merge into: | lp:launchpad | ||||
Diff against target: |
888 lines (+176/-500) 2 files modified
lib/lp/registry/model/structuralsubscription.py (+47/-252) lib/lp/registry/tests/test_structuralsubscriptiontarget.py (+129/-248) |
||||
To merge this branch: | bzr merge lp:~allenap/launchpad/revert-subscribe-to-tag-bug-151129 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Francis J. Lacoste (community) | Approve | ||
Gavin Panella (community) | Approve | ||
Review via email:
|
Commit message
[r=allenap]
Description of the change
Subscribing to tags has performance problems. See the linked bug for more information. This branch reverts the change.
To post a comment you must log in.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Gavin Panella (allenap) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/lp/registry/model/structuralsubscription.py' | |||
2 | --- lib/lp/registry/model/structuralsubscription.py 2010-11-19 14:33:01 +0000 | |||
3 | +++ lib/lp/registry/model/structuralsubscription.py 2010-11-25 21:03:11 +0000 | |||
4 | @@ -2,26 +2,14 @@ | |||
5 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
6 | 3 | 3 | ||
7 | 4 | __metaclass__ = type | 4 | __metaclass__ = type |
12 | 5 | __all__ = [ | 5 | __all__ = ['StructuralSubscription', |
13 | 6 | 'StructuralSubscription', | 6 | 'StructuralSubscriptionTargetMixin'] |
10 | 7 | 'StructuralSubscriptionTargetMixin', | ||
11 | 8 | ] | ||
14 | 9 | 7 | ||
15 | 10 | from sqlobject import ForeignKey | 8 | from sqlobject import ForeignKey |
16 | 11 | from storm.expr import ( | 9 | from storm.expr import ( |
17 | 12 | Alias, | ||
18 | 13 | And, | 10 | And, |
19 | 14 | CompoundOper, | ||
20 | 15 | Except, | ||
21 | 16 | In, | ||
22 | 17 | Intersect, | ||
23 | 18 | LeftJoin, | 11 | LeftJoin, |
24 | 19 | NamedFunc, | ||
25 | 20 | Not, | ||
26 | 21 | Or, | 12 | Or, |
27 | 22 | Select, | ||
28 | 23 | SQL, | ||
29 | 24 | Union, | ||
30 | 25 | ) | 13 | ) |
31 | 26 | from storm.store import Store | 14 | from storm.store import Store |
32 | 27 | from zope.component import ( | 15 | from zope.component import ( |
33 | @@ -46,7 +34,6 @@ | |||
34 | 46 | from lp.bugs.model.bugsubscriptionfilterstatus import ( | 34 | from lp.bugs.model.bugsubscriptionfilterstatus import ( |
35 | 47 | BugSubscriptionFilterStatus, | 35 | BugSubscriptionFilterStatus, |
36 | 48 | ) | 36 | ) |
37 | 49 | from lp.bugs.model.bugsubscriptionfiltertag import BugSubscriptionFilterTag | ||
38 | 50 | from lp.registry.enum import BugNotificationLevel | 37 | from lp.registry.enum import BugNotificationLevel |
39 | 51 | from lp.registry.errors import ( | 38 | from lp.registry.errors import ( |
40 | 52 | DeleteSubscriptionError, | 39 | DeleteSubscriptionError, |
41 | @@ -484,242 +471,50 @@ | |||
42 | 484 | 471 | ||
43 | 485 | def getSubscriptionsForBugTask(self, bugtask, level): | 472 | def getSubscriptionsForBugTask(self, bugtask, level): |
44 | 486 | """See `IStructuralSubscriptionTarget`.""" | 473 | """See `IStructuralSubscriptionTarget`.""" |
82 | 487 | set_builder = BugFilterSetBuilder( | 474 | origin = [ |
83 | 488 | bugtask, level, self.__helper.join) | 475 | StructuralSubscription, |
84 | 489 | return Store.of(self.__helper.pillar).find( | 476 | LeftJoin( |
85 | 490 | StructuralSubscription, In( | 477 | BugSubscriptionFilter, |
86 | 491 | StructuralSubscription.id, | 478 | BugSubscriptionFilter.structural_subscription_id == ( |
87 | 492 | set_builder.subscriptions)) | 479 | StructuralSubscription.id)), |
88 | 493 | 480 | LeftJoin( | |
89 | 494 | 481 | BugSubscriptionFilterStatus, | |
90 | 495 | class ArrayAgg(NamedFunc): | 482 | BugSubscriptionFilterStatus.filter_id == ( |
91 | 496 | __slots__ = () | 483 | BugSubscriptionFilter.id)), |
92 | 497 | name = "ARRAY_AGG" | 484 | LeftJoin( |
93 | 498 | 485 | BugSubscriptionFilterImportance, | |
94 | 499 | 486 | BugSubscriptionFilterImportance.filter_id == ( | |
95 | 500 | class ArrayContains(CompoundOper): | 487 | BugSubscriptionFilter.id)), |
96 | 501 | __slots__ = () | 488 | ] |
97 | 502 | oper = "@>" | 489 | |
98 | 503 | 490 | if len(bugtask.bug.tags) == 0: | |
99 | 504 | 491 | tag_conditions = [ | |
100 | 505 | class BugFilterSetBuilder: | 492 | BugSubscriptionFilter.include_any_tags == False, |
101 | 506 | """A convenience class to build queries for getSubscriptionsForBugTask.""" | 493 | ] |
102 | 507 | 494 | else: | |
103 | 508 | def __init__(self, bugtask, level, join_condition): | 495 | tag_conditions = [ |
104 | 509 | """Initialize a new set builder for bug filters. | 496 | BugSubscriptionFilter.exclude_any_tags == False, |
105 | 510 | 497 | ] | |
106 | 511 | :param bugtask: The `IBugTask` to match against. | 498 | |
107 | 512 | :param level: A member of `BugNotificationLevel`. | 499 | conditions = [ |
71 | 513 | :param join_condition: A condition for selecting structural | ||
72 | 514 | subscriptions. Generally this should limit the subscriptions to a | ||
73 | 515 | particular target (i.e. project or distribution). | ||
74 | 516 | """ | ||
75 | 517 | self.status = bugtask.status | ||
76 | 518 | self.importance = bugtask.importance | ||
77 | 519 | # The list() gets around some weirdness with security proxies; Storm | ||
78 | 520 | # does not know how to compile an expression with a proxied list. | ||
79 | 521 | self.tags = list(bugtask.bug.tags) | ||
80 | 522 | # Set up common conditions. | ||
81 | 523 | self.base_conditions = And( | ||
108 | 524 | StructuralSubscription.bug_notification_level >= level, | 500 | StructuralSubscription.bug_notification_level >= level, |
136 | 525 | join_condition) | 501 | Or( |
137 | 526 | # Set up common filter conditions. | 502 | # There's no filter or ... |
111 | 527 | if len(self.tags) == 0: | ||
112 | 528 | self.filter_conditions = And( | ||
113 | 529 | # When the bug has no tags, filters with include_any_tags set | ||
114 | 530 | # can never match. | ||
115 | 531 | Not(BugSubscriptionFilter.include_any_tags), | ||
116 | 532 | self.base_conditions) | ||
117 | 533 | else: | ||
118 | 534 | self.filter_conditions = And( | ||
119 | 535 | # When the bug has tags, filters with exclude_any_tags set can | ||
120 | 536 | # never match. | ||
121 | 537 | Not(BugSubscriptionFilter.exclude_any_tags), | ||
122 | 538 | self.base_conditions) | ||
123 | 539 | |||
124 | 540 | @property | ||
125 | 541 | def subscriptions_without_filters(self): | ||
126 | 542 | """Subscriptions without filters.""" | ||
127 | 543 | return Select( | ||
128 | 544 | StructuralSubscription.id, | ||
129 | 545 | tables=( | ||
130 | 546 | StructuralSubscription, | ||
131 | 547 | LeftJoin( | ||
132 | 548 | BugSubscriptionFilter, | ||
133 | 549 | BugSubscriptionFilter.structural_subscription_id == ( | ||
134 | 550 | StructuralSubscription.id))), | ||
135 | 551 | where=And( | ||
138 | 552 | BugSubscriptionFilter.id == None, | 503 | BugSubscriptionFilter.id == None, |
312 | 553 | self.base_conditions)) | 504 | # There is a filter and ... |
313 | 554 | 505 | And( | |
314 | 555 | def _filters_matching_x(self, join, where_condition, **extra): | 506 | # There's no status filter, or there is a status filter |
315 | 556 | """Return an expression yielding `(subscription_id, filter_id)` rows. | 507 | # and and it matches. |
316 | 557 | 508 | Or(BugSubscriptionFilterStatus.id == None, | |
317 | 558 | The expressions returned by this function are used in set (union, | 509 | BugSubscriptionFilterStatus.status == bugtask.status), |
318 | 559 | intersect, except) operations at the *filter* level. However, the | 510 | # There's no importance filter, or there is an importance |
319 | 560 | interesting result of these set operations is the structural | 511 | # filter and it matches. |
320 | 561 | subscription, hence both columns are included in the expressions | 512 | Or(BugSubscriptionFilterImportance.id == None, |
321 | 562 | generated. Since a structural subscription can have zero or more | 513 | BugSubscriptionFilterImportance.importance == ( |
322 | 563 | filters, and a filter can never be associated with more than one | 514 | bugtask.importance)), |
323 | 564 | subscription, the set operations are unaffected. | 515 | # Any number of conditions relating to tags. |
324 | 565 | """ | 516 | *tag_conditions)), |
325 | 566 | return Select( | 517 | ] |
326 | 567 | columns=( | 518 | |
327 | 568 | # Alias this column so it can be selected in | 519 | return Store.of(self.__helper.pillar).using(*origin).find( |
328 | 569 | # subscriptions_matching. | 520 | StructuralSubscription, self.__helper.join, *conditions) |
156 | 570 | Alias( | ||
157 | 571 | BugSubscriptionFilter.structural_subscription_id, | ||
158 | 572 | "structural_subscription_id"), | ||
159 | 573 | BugSubscriptionFilter.id), | ||
160 | 574 | tables=( | ||
161 | 575 | StructuralSubscription, BugSubscriptionFilter, join), | ||
162 | 576 | where=And( | ||
163 | 577 | BugSubscriptionFilter.structural_subscription_id == ( | ||
164 | 578 | StructuralSubscription.id), | ||
165 | 579 | self.filter_conditions, | ||
166 | 580 | where_condition), | ||
167 | 581 | **extra) | ||
168 | 582 | |||
169 | 583 | @property | ||
170 | 584 | def filters_matching_status(self): | ||
171 | 585 | """Filters with the given bugtask's status.""" | ||
172 | 586 | join = LeftJoin( | ||
173 | 587 | BugSubscriptionFilterStatus, | ||
174 | 588 | BugSubscriptionFilterStatus.filter_id == ( | ||
175 | 589 | BugSubscriptionFilter.id)) | ||
176 | 590 | condition = Or( | ||
177 | 591 | BugSubscriptionFilterStatus.id == None, | ||
178 | 592 | BugSubscriptionFilterStatus.status == self.status) | ||
179 | 593 | return self._filters_matching_x(join, condition) | ||
180 | 594 | |||
181 | 595 | @property | ||
182 | 596 | def filters_matching_importance(self): | ||
183 | 597 | """Filters with the given bugtask's importance.""" | ||
184 | 598 | join = LeftJoin( | ||
185 | 599 | BugSubscriptionFilterImportance, | ||
186 | 600 | BugSubscriptionFilterImportance.filter_id == ( | ||
187 | 601 | BugSubscriptionFilter.id)) | ||
188 | 602 | condition = Or( | ||
189 | 603 | BugSubscriptionFilterImportance.id == None, | ||
190 | 604 | BugSubscriptionFilterImportance.importance == self.importance) | ||
191 | 605 | return self._filters_matching_x(join, condition) | ||
192 | 606 | |||
193 | 607 | @property | ||
194 | 608 | def filters_without_include_tags(self): | ||
195 | 609 | """Filters with no tags required.""" | ||
196 | 610 | join = LeftJoin( | ||
197 | 611 | BugSubscriptionFilterTag, | ||
198 | 612 | And(BugSubscriptionFilterTag.filter_id == ( | ||
199 | 613 | BugSubscriptionFilter.id), | ||
200 | 614 | BugSubscriptionFilterTag.include)) | ||
201 | 615 | return self._filters_matching_x( | ||
202 | 616 | join, BugSubscriptionFilterTag.id == None) | ||
203 | 617 | |||
204 | 618 | @property | ||
205 | 619 | def filters_matching_any_include_tags(self): | ||
206 | 620 | """Filters including any of the bug's tags.""" | ||
207 | 621 | condition = And( | ||
208 | 622 | BugSubscriptionFilterTag.filter_id == ( | ||
209 | 623 | BugSubscriptionFilter.id), | ||
210 | 624 | BugSubscriptionFilterTag.include, | ||
211 | 625 | Not(BugSubscriptionFilter.find_all_tags), | ||
212 | 626 | In(BugSubscriptionFilterTag.tag, self.tags)) | ||
213 | 627 | return self._filters_matching_x( | ||
214 | 628 | BugSubscriptionFilterTag, condition) | ||
215 | 629 | |||
216 | 630 | @property | ||
217 | 631 | def filters_matching_any_exclude_tags(self): | ||
218 | 632 | """Filters excluding any of the bug's tags.""" | ||
219 | 633 | condition = And( | ||
220 | 634 | BugSubscriptionFilterTag.filter_id == ( | ||
221 | 635 | BugSubscriptionFilter.id), | ||
222 | 636 | Not(BugSubscriptionFilterTag.include), | ||
223 | 637 | Not(BugSubscriptionFilter.find_all_tags), | ||
224 | 638 | In(BugSubscriptionFilterTag.tag, self.tags)) | ||
225 | 639 | return self._filters_matching_x( | ||
226 | 640 | BugSubscriptionFilterTag, condition) | ||
227 | 641 | |||
228 | 642 | def _filters_matching_all_x_tags(self, where_condition): | ||
229 | 643 | """Return an expression yielding `(subscription_id, filter_id)` rows. | ||
230 | 644 | |||
231 | 645 | This joins to `BugSubscriptionFilterTag` and calls up to | ||
232 | 646 | `_filters_matching_x`, and groups by filter. Conditions are added to | ||
233 | 647 | ensure that all rows in each group are a subset of the bug's tags. | ||
234 | 648 | """ | ||
235 | 649 | tags_array = "ARRAY[%s]::TEXT[]" % ",".join( | ||
236 | 650 | quote(tag) for tag in self.tags) | ||
237 | 651 | return self._filters_matching_x( | ||
238 | 652 | BugSubscriptionFilterTag, | ||
239 | 653 | And( | ||
240 | 654 | BugSubscriptionFilterTag.filter_id == ( | ||
241 | 655 | BugSubscriptionFilter.id), | ||
242 | 656 | BugSubscriptionFilter.find_all_tags, | ||
243 | 657 | self.filter_conditions, | ||
244 | 658 | where_condition), | ||
245 | 659 | group_by=( | ||
246 | 660 | BugSubscriptionFilter.structural_subscription_id, | ||
247 | 661 | BugSubscriptionFilter.id), | ||
248 | 662 | having=ArrayContains( | ||
249 | 663 | SQL(tags_array), ArrayAgg( | ||
250 | 664 | BugSubscriptionFilterTag.tag))) | ||
251 | 665 | |||
252 | 666 | @property | ||
253 | 667 | def filters_matching_all_include_tags(self): | ||
254 | 668 | """Filters including the bug's tags.""" | ||
255 | 669 | return self._filters_matching_all_x_tags( | ||
256 | 670 | BugSubscriptionFilterTag.include) | ||
257 | 671 | |||
258 | 672 | @property | ||
259 | 673 | def filters_matching_all_exclude_tags(self): | ||
260 | 674 | """Filters excluding the bug's tags.""" | ||
261 | 675 | return self._filters_matching_all_x_tags( | ||
262 | 676 | Not(BugSubscriptionFilterTag.include)) | ||
263 | 677 | |||
264 | 678 | @property | ||
265 | 679 | def filters_matching_include_tags(self): | ||
266 | 680 | """Filters with tag filters including the bug.""" | ||
267 | 681 | return Union( | ||
268 | 682 | self.filters_matching_any_include_tags, | ||
269 | 683 | self.filters_matching_all_include_tags) | ||
270 | 684 | |||
271 | 685 | @property | ||
272 | 686 | def filters_matching_exclude_tags(self): | ||
273 | 687 | """Filters with tag filters excluding the bug.""" | ||
274 | 688 | return Union( | ||
275 | 689 | self.filters_matching_any_exclude_tags, | ||
276 | 690 | self.filters_matching_all_exclude_tags) | ||
277 | 691 | |||
278 | 692 | @property | ||
279 | 693 | def filters_matching_tags(self): | ||
280 | 694 | """Filters with tag filters matching the bug.""" | ||
281 | 695 | if len(self.tags) == 0: | ||
282 | 696 | # The filter's required tags must be an empty set. The filter's | ||
283 | 697 | # excluded tags can be anything so no condition is needed. | ||
284 | 698 | return self.filters_without_include_tags | ||
285 | 699 | else: | ||
286 | 700 | return Except( | ||
287 | 701 | Union(self.filters_without_include_tags, | ||
288 | 702 | self.filters_matching_include_tags), | ||
289 | 703 | self.filters_matching_exclude_tags) | ||
290 | 704 | |||
291 | 705 | @property | ||
292 | 706 | def filters_matching(self): | ||
293 | 707 | """Filters matching the bug.""" | ||
294 | 708 | return Intersect( | ||
295 | 709 | self.filters_matching_status, | ||
296 | 710 | self.filters_matching_importance, | ||
297 | 711 | self.filters_matching_tags) | ||
298 | 712 | |||
299 | 713 | @property | ||
300 | 714 | def subscriptions_with_matching_filters(self): | ||
301 | 715 | """Subscriptions with one or more filters matching the bug.""" | ||
302 | 716 | return Select( | ||
303 | 717 | # I don't know of a more Storm-like way of doing this. | ||
304 | 718 | SQL("filters_matching.structural_subscription_id"), | ||
305 | 719 | tables=Alias(self.filters_matching, "filters_matching")) | ||
306 | 720 | |||
307 | 721 | @property | ||
308 | 722 | def subscriptions(self): | ||
309 | 723 | return Union( | ||
310 | 724 | self.subscriptions_without_filters, | ||
311 | 725 | self.subscriptions_with_matching_filters) | ||
329 | 726 | 521 | ||
330 | === modified file 'lib/lp/registry/tests/test_structuralsubscriptiontarget.py' | |||
331 | --- lib/lp/registry/tests/test_structuralsubscriptiontarget.py 2010-11-18 21:29:44 +0000 | |||
332 | +++ lib/lp/registry/tests/test_structuralsubscriptiontarget.py 2010-11-25 21:03:11 +0000 | |||
333 | @@ -32,6 +32,7 @@ | |||
334 | 32 | BugTaskImportance, | 32 | BugTaskImportance, |
335 | 33 | BugTaskStatus, | 33 | BugTaskStatus, |
336 | 34 | ) | 34 | ) |
337 | 35 | from lp.bugs.model.bugsubscriptionfilter import BugSubscriptionFilter | ||
338 | 35 | from lp.bugs.tests.test_bugtarget import bugtarget_filebug | 36 | from lp.bugs.tests.test_bugtarget import bugtarget_filebug |
339 | 36 | from lp.registry.enum import BugNotificationLevel | 37 | from lp.registry.enum import BugNotificationLevel |
340 | 37 | from lp.registry.errors import ( | 38 | from lp.registry.errors import ( |
341 | @@ -56,11 +57,10 @@ | |||
342 | 56 | from lp.testing.matchers import Provides | 57 | from lp.testing.matchers import Provides |
343 | 57 | 58 | ||
344 | 58 | 59 | ||
347 | 59 | class RestrictedStructuralSubscriptionTestBase: | 60 | class StructuralSubscriptionTestBase: |
346 | 60 | """Tests suitable for a target that restricts structural subscriptions.""" | ||
348 | 61 | 61 | ||
349 | 62 | def setUp(self): | 62 | def setUp(self): |
351 | 63 | super(RestrictedStructuralSubscriptionTestBase, self).setUp() | 63 | super(StructuralSubscriptionTestBase, self).setUp() |
352 | 64 | self.ordinary_subscriber = self.factory.makePerson() | 64 | self.ordinary_subscriber = self.factory.makePerson() |
353 | 65 | self.bug_supervisor_subscriber = self.factory.makePerson() | 65 | self.bug_supervisor_subscriber = self.factory.makePerson() |
354 | 66 | self.team_owner = self.factory.makePerson() | 66 | self.team_owner = self.factory.makePerson() |
355 | @@ -124,12 +124,7 @@ | |||
356 | 124 | self.ordinary_subscriber, self.ordinary_subscriber) | 124 | self.ordinary_subscriber, self.ordinary_subscriber) |
357 | 125 | 125 | ||
358 | 126 | 126 | ||
365 | 127 | class UnrestrictedStructuralSubscriptionTestBase( | 127 | class UnrestrictedStructuralSubscription(StructuralSubscriptionTestBase): |
360 | 128 | RestrictedStructuralSubscriptionTestBase): | ||
361 | 129 | """ | ||
362 | 130 | Tests suitable for a target that does not restrict structural | ||
363 | 131 | subscriptions. | ||
364 | 132 | """ | ||
366 | 133 | 128 | ||
367 | 134 | def test_structural_subscription_by_ordinary_user(self): | 129 | def test_structural_subscription_by_ordinary_user(self): |
368 | 135 | # ordinary users can subscribe themselves | 130 | # ordinary users can subscribe themselves |
369 | @@ -172,276 +167,198 @@ | |||
370 | 172 | None) | 167 | None) |
371 | 173 | 168 | ||
372 | 174 | 169 | ||
374 | 175 | class FilteredStructuralSubscriptionTestBase: | 170 | class FilteredStructuralSubscriptionTestBase(StructuralSubscriptionTestBase): |
375 | 176 | """Tests for filtered structural subscriptions.""" | 171 | """Tests for filtered structural subscriptions.""" |
376 | 177 | 172 | ||
377 | 178 | layer = LaunchpadFunctionalLayer | ||
378 | 179 | |||
379 | 180 | def makeTarget(self): | ||
380 | 181 | raise NotImplementedError(self.makeTarget) | ||
381 | 182 | |||
382 | 183 | def makeBugTask(self): | 173 | def makeBugTask(self): |
383 | 184 | return self.factory.makeBugTask(target=self.target) | 174 | return self.factory.makeBugTask(target=self.target) |
384 | 185 | 175 | ||
385 | 186 | def setUp(self): | ||
386 | 187 | super(FilteredStructuralSubscriptionTestBase, self).setUp() | ||
387 | 188 | self.ordinary_subscriber = self.factory.makePerson() | ||
388 | 189 | login_person(self.ordinary_subscriber) | ||
389 | 190 | self.target = self.makeTarget() | ||
390 | 191 | self.bugtask = self.makeBugTask() | ||
391 | 192 | self.bug = self.bugtask.bug | ||
392 | 193 | self.subscription = self.target.addSubscription( | ||
393 | 194 | self.ordinary_subscriber, self.ordinary_subscriber) | ||
394 | 195 | self.subscription.bug_notification_level = ( | ||
395 | 196 | BugNotificationLevel.COMMENTS) | ||
396 | 197 | |||
397 | 198 | def assertSubscriptions( | ||
398 | 199 | self, expected_subscriptions, level=BugNotificationLevel.NOTHING): | ||
399 | 200 | observed_subscriptions = list( | ||
400 | 201 | self.target.getSubscriptionsForBugTask(self.bugtask, level)) | ||
401 | 202 | self.assertEqual(expected_subscriptions, observed_subscriptions) | ||
402 | 203 | |||
403 | 204 | def test_getSubscriptionsForBugTask(self): | 176 | def test_getSubscriptionsForBugTask(self): |
404 | 205 | # If no one has a filtered subscription for the given bug, the result | 177 | # If no one has a filtered subscription for the given bug, the result |
405 | 206 | # of getSubscriptionsForBugTask() is the same as for | 178 | # of getSubscriptionsForBugTask() is the same as for |
406 | 207 | # getSubscriptions(). | 179 | # getSubscriptions(). |
407 | 180 | bugtask = self.makeBugTask() | ||
408 | 208 | subscriptions = self.target.getSubscriptions( | 181 | subscriptions = self.target.getSubscriptions( |
409 | 209 | min_bug_notification_level=BugNotificationLevel.NOTHING) | 182 | min_bug_notification_level=BugNotificationLevel.NOTHING) |
411 | 210 | self.assertSubscriptions(list(subscriptions)) | 183 | subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
412 | 184 | bugtask, BugNotificationLevel.NOTHING) | ||
413 | 185 | self.assertEqual(list(subscriptions), list(subscriptions_for_bugtask)) | ||
414 | 211 | 186 | ||
415 | 212 | def test_getSubscriptionsForBugTask_with_filter_on_status(self): | 187 | def test_getSubscriptionsForBugTask_with_filter_on_status(self): |
416 | 213 | # If a status filter exists for a subscription, the result of | 188 | # If a status filter exists for a subscription, the result of |
417 | 214 | # getSubscriptionsForBugTask() may be a subset of getSubscriptions(). | 189 | # getSubscriptionsForBugTask() may be a subset of getSubscriptions(). |
418 | 190 | bugtask = self.makeBugTask() | ||
419 | 191 | |||
420 | 192 | # Create a new subscription on self.target. | ||
421 | 193 | login_person(self.ordinary_subscriber) | ||
422 | 194 | subscription = self.target.addSubscription( | ||
423 | 195 | self.ordinary_subscriber, self.ordinary_subscriber) | ||
424 | 196 | subscription.bug_notification_level = BugNotificationLevel.COMMENTS | ||
425 | 215 | 197 | ||
426 | 216 | # Without any filters the subscription is found. | 198 | # Without any filters the subscription is found. |
428 | 217 | self.assertSubscriptions([self.subscription]) | 199 | subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
429 | 200 | bugtask, BugNotificationLevel.NOTHING) | ||
430 | 201 | self.assertEqual([subscription], list(subscriptions_for_bugtask)) | ||
431 | 218 | 202 | ||
432 | 219 | # Filter the subscription to bugs in the CONFIRMED state. | 203 | # Filter the subscription to bugs in the CONFIRMED state. |
434 | 220 | subscription_filter = self.subscription.newBugFilter() | 204 | subscription_filter = BugSubscriptionFilter() |
435 | 205 | subscription_filter.structural_subscription = subscription | ||
436 | 221 | subscription_filter.statuses = [BugTaskStatus.CONFIRMED] | 206 | subscription_filter.statuses = [BugTaskStatus.CONFIRMED] |
437 | 222 | 207 | ||
438 | 223 | # With the filter the subscription is not found. | 208 | # With the filter the subscription is not found. |
440 | 224 | self.assertSubscriptions([]) | 209 | subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
441 | 210 | bugtask, BugNotificationLevel.NOTHING) | ||
442 | 211 | self.assertEqual([], list(subscriptions_for_bugtask)) | ||
443 | 225 | 212 | ||
444 | 226 | # If the filter is adjusted, the subscription is found again. | 213 | # If the filter is adjusted, the subscription is found again. |
447 | 227 | subscription_filter.statuses = [self.bugtask.status] | 214 | subscription_filter.statuses = [bugtask.status] |
448 | 228 | self.assertSubscriptions([self.subscription]) | 215 | subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
449 | 216 | bugtask, BugNotificationLevel.NOTHING) | ||
450 | 217 | self.assertEqual([subscription], list(subscriptions_for_bugtask)) | ||
451 | 229 | 218 | ||
452 | 230 | def test_getSubscriptionsForBugTask_with_filter_on_importance(self): | 219 | def test_getSubscriptionsForBugTask_with_filter_on_importance(self): |
453 | 231 | # If an importance filter exists for a subscription, the result of | 220 | # If an importance filter exists for a subscription, the result of |
454 | 232 | # getSubscriptionsForBugTask() may be a subset of getSubscriptions(). | 221 | # getSubscriptionsForBugTask() may be a subset of getSubscriptions(). |
455 | 222 | bugtask = self.makeBugTask() | ||
456 | 223 | |||
457 | 224 | # Create a new subscription on self.target. | ||
458 | 225 | login_person(self.ordinary_subscriber) | ||
459 | 226 | subscription = self.target.addSubscription( | ||
460 | 227 | self.ordinary_subscriber, self.ordinary_subscriber) | ||
461 | 228 | subscription.bug_notification_level = BugNotificationLevel.COMMENTS | ||
462 | 233 | 229 | ||
463 | 234 | # Without any filters the subscription is found. | 230 | # Without any filters the subscription is found. |
465 | 235 | self.assertSubscriptions([self.subscription]) | 231 | subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
466 | 232 | bugtask, BugNotificationLevel.NOTHING) | ||
467 | 233 | self.assertEqual([subscription], list(subscriptions_for_bugtask)) | ||
468 | 236 | 234 | ||
469 | 237 | # Filter the subscription to bugs in the CRITICAL state. | 235 | # Filter the subscription to bugs in the CRITICAL state. |
471 | 238 | subscription_filter = self.subscription.newBugFilter() | 236 | subscription_filter = BugSubscriptionFilter() |
472 | 237 | subscription_filter.structural_subscription = subscription | ||
473 | 239 | subscription_filter.importances = [BugTaskImportance.CRITICAL] | 238 | subscription_filter.importances = [BugTaskImportance.CRITICAL] |
474 | 240 | 239 | ||
475 | 241 | # With the filter the subscription is not found. | 240 | # With the filter the subscription is not found. |
477 | 242 | self.assertSubscriptions([]) | 241 | subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
478 | 242 | bugtask, BugNotificationLevel.NOTHING) | ||
479 | 243 | self.assertEqual([], list(subscriptions_for_bugtask)) | ||
480 | 243 | 244 | ||
481 | 244 | # If the filter is adjusted, the subscription is found again. | 245 | # If the filter is adjusted, the subscription is found again. |
484 | 245 | subscription_filter.importances = [self.bugtask.importance] | 246 | subscription_filter.importances = [bugtask.importance] |
485 | 246 | self.assertSubscriptions([self.subscription]) | 247 | subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
486 | 248 | bugtask, BugNotificationLevel.NOTHING) | ||
487 | 249 | self.assertEqual([subscription], list(subscriptions_for_bugtask)) | ||
488 | 247 | 250 | ||
489 | 248 | def test_getSubscriptionsForBugTask_with_filter_on_level(self): | 251 | def test_getSubscriptionsForBugTask_with_filter_on_level(self): |
490 | 249 | # All structural subscriptions have a level for bug notifications | 252 | # All structural subscriptions have a level for bug notifications |
491 | 250 | # which getSubscriptionsForBugTask() observes. | 253 | # which getSubscriptionsForBugTask() observes. |
492 | 254 | bugtask = self.makeBugTask() | ||
493 | 251 | 255 | ||
497 | 252 | # Adjust the subscription level to METADATA. | 256 | # Create a new METADATA level subscription on self.target. |
498 | 253 | self.subscription.bug_notification_level = ( | 257 | login_person(self.ordinary_subscriber) |
499 | 254 | BugNotificationLevel.METADATA) | 258 | subscription = self.target.addSubscription( |
500 | 259 | self.ordinary_subscriber, self.ordinary_subscriber) | ||
501 | 260 | subscription.bug_notification_level = BugNotificationLevel.METADATA | ||
502 | 255 | 261 | ||
503 | 256 | # The subscription is found when looking for NOTHING or above. | 262 | # The subscription is found when looking for NOTHING or above. |
506 | 257 | self.assertSubscriptions( | 263 | subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
507 | 258 | [self.subscription], BugNotificationLevel.NOTHING) | 264 | bugtask, BugNotificationLevel.NOTHING) |
508 | 265 | self.assertEqual([subscription], list(subscriptions_for_bugtask)) | ||
509 | 259 | # The subscription is found when looking for METADATA or above. | 266 | # The subscription is found when looking for METADATA or above. |
512 | 260 | self.assertSubscriptions( | 267 | subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
513 | 261 | [self.subscription], BugNotificationLevel.METADATA) | 268 | bugtask, BugNotificationLevel.METADATA) |
514 | 269 | self.assertEqual([subscription], list(subscriptions_for_bugtask)) | ||
515 | 262 | # The subscription is not found when looking for COMMENTS or above. | 270 | # The subscription is not found when looking for COMMENTS or above. |
518 | 263 | self.assertSubscriptions( | 271 | subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
519 | 264 | [], BugNotificationLevel.COMMENTS) | 272 | bugtask, BugNotificationLevel.COMMENTS) |
520 | 273 | self.assertEqual([], list(subscriptions_for_bugtask)) | ||
521 | 265 | 274 | ||
522 | 266 | def test_getSubscriptionsForBugTask_with_filter_include_any_tags(self): | 275 | def test_getSubscriptionsForBugTask_with_filter_include_any_tags(self): |
523 | 267 | # If a subscription filter has include_any_tags, a bug with one or | 276 | # If a subscription filter has include_any_tags, a bug with one or |
524 | 268 | # more tags is matched. | 277 | # more tags is matched. |
525 | 278 | bugtask = self.makeBugTask() | ||
526 | 269 | 279 | ||
528 | 270 | subscription_filter = self.subscription.newBugFilter() | 280 | # Create a new subscription on self.target. |
529 | 281 | login_person(self.ordinary_subscriber) | ||
530 | 282 | subscription = self.target.addSubscription( | ||
531 | 283 | self.ordinary_subscriber, self.ordinary_subscriber) | ||
532 | 284 | subscription.bug_notification_level = BugNotificationLevel.COMMENTS | ||
533 | 285 | subscription_filter = subscription.newBugFilter() | ||
534 | 271 | subscription_filter.include_any_tags = True | 286 | subscription_filter.include_any_tags = True |
535 | 272 | 287 | ||
536 | 273 | # Without any tags the subscription is not found. | 288 | # Without any tags the subscription is not found. |
538 | 274 | self.assertSubscriptions([]) | 289 | subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
539 | 290 | bugtask, BugNotificationLevel.NOTHING) | ||
540 | 291 | self.assertEqual([], list(subscriptions_for_bugtask)) | ||
541 | 275 | 292 | ||
542 | 276 | # With any tag the subscription is found. | 293 | # With any tag the subscription is found. |
545 | 277 | self.bug.tags = ["foo"] | 294 | bugtask.bug.tags = ["foo"] |
546 | 278 | self.assertSubscriptions([self.subscription]) | 295 | subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
547 | 296 | bugtask, BugNotificationLevel.NOTHING) | ||
548 | 297 | self.assertEqual([subscription], list(subscriptions_for_bugtask)) | ||
549 | 279 | 298 | ||
550 | 280 | def test_getSubscriptionsForBugTask_with_filter_exclude_any_tags(self): | 299 | def test_getSubscriptionsForBugTask_with_filter_exclude_any_tags(self): |
551 | 281 | # If a subscription filter has exclude_any_tags, only bugs with no | 300 | # If a subscription filter has exclude_any_tags, only bugs with no |
552 | 282 | # tags are matched. | 301 | # tags are matched. |
553 | 302 | bugtask = self.makeBugTask() | ||
554 | 283 | 303 | ||
556 | 284 | subscription_filter = self.subscription.newBugFilter() | 304 | # Create a new subscription on self.target. |
557 | 305 | login_person(self.ordinary_subscriber) | ||
558 | 306 | subscription = self.target.addSubscription( | ||
559 | 307 | self.ordinary_subscriber, self.ordinary_subscriber) | ||
560 | 308 | subscription.bug_notification_level = BugNotificationLevel.COMMENTS | ||
561 | 309 | subscription_filter = subscription.newBugFilter() | ||
562 | 285 | subscription_filter.exclude_any_tags = True | 310 | subscription_filter.exclude_any_tags = True |
563 | 286 | 311 | ||
564 | 287 | # Without any tags the subscription is found. | 312 | # Without any tags the subscription is found. |
566 | 288 | self.assertSubscriptions([self.subscription]) | 313 | subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
567 | 314 | bugtask, BugNotificationLevel.NOTHING) | ||
568 | 315 | self.assertEqual([subscription], list(subscriptions_for_bugtask)) | ||
569 | 289 | 316 | ||
570 | 290 | # With any tag the subscription is not found. | 317 | # With any tag the subscription is not found. |
647 | 291 | self.bug.tags = ["foo"] | 318 | bugtask.bug.tags = ["foo"] |
648 | 292 | self.assertSubscriptions([]) | 319 | subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
649 | 293 | 320 | bugtask, BugNotificationLevel.NOTHING) | |
650 | 294 | def test_getSubscriptionsForBugTask_with_filter_for_any_tag(self): | 321 | self.assertEqual([], list(subscriptions_for_bugtask)) |
575 | 295 | # If a subscription filter specifies that any of one or more specific | ||
576 | 296 | # tags must be present, bugs with any of those tags are matched. | ||
577 | 297 | |||
578 | 298 | # Looking for either the "foo" or the "bar" tag. | ||
579 | 299 | subscription_filter = self.subscription.newBugFilter() | ||
580 | 300 | subscription_filter.tags = [u"foo", u"bar"] | ||
581 | 301 | subscription_filter.find_all_tags = False | ||
582 | 302 | |||
583 | 303 | # Without either tag the subscription is not found. | ||
584 | 304 | self.assertSubscriptions([]) | ||
585 | 305 | |||
586 | 306 | # With either tag the subscription is found. | ||
587 | 307 | self.bug.tags = ["bar", "baz"] | ||
588 | 308 | self.assertSubscriptions([self.subscription]) | ||
589 | 309 | |||
590 | 310 | def test_getSubscriptionsForBugTask_with_filter_for_all_tags(self): | ||
591 | 311 | # If a subscription filter specifies that all of one or more specific | ||
592 | 312 | # tags must be present, bugs with all of those tags are matched. | ||
593 | 313 | |||
594 | 314 | # Looking for both the "foo" and the "bar" tag. | ||
595 | 315 | subscription_filter = self.subscription.newBugFilter() | ||
596 | 316 | subscription_filter.tags = [u"foo", u"bar"] | ||
597 | 317 | subscription_filter.find_all_tags = True | ||
598 | 318 | |||
599 | 319 | # Without either tag the subscription is not found. | ||
600 | 320 | self.assertSubscriptions([]) | ||
601 | 321 | |||
602 | 322 | # Without only one of the required tags the subscription is not found. | ||
603 | 323 | self.bug.tags = ["foo"] | ||
604 | 324 | self.assertSubscriptions([]) | ||
605 | 325 | |||
606 | 326 | # With both required tags the subscription is found. | ||
607 | 327 | self.bug.tags = ["foo", "bar"] | ||
608 | 328 | self.assertSubscriptions([self.subscription]) | ||
609 | 329 | |||
610 | 330 | def test_getSubscriptionsForBugTask_with_filter_for_not_any_tag(self): | ||
611 | 331 | # If a subscription filter specifies that any of one or more specific | ||
612 | 332 | # tags must not be present, bugs without any of those tags are | ||
613 | 333 | # matched. | ||
614 | 334 | |||
615 | 335 | # Looking to exclude the "foo" or "bar" tags. | ||
616 | 336 | subscription_filter = self.subscription.newBugFilter() | ||
617 | 337 | subscription_filter.tags = [u"-foo", u"-bar"] | ||
618 | 338 | subscription_filter.find_all_tags = False | ||
619 | 339 | |||
620 | 340 | # Without either tag the subscription is found. | ||
621 | 341 | self.assertSubscriptions([self.subscription]) | ||
622 | 342 | |||
623 | 343 | # With either tag the subscription is no longer found. | ||
624 | 344 | self.bug.tags = ["foo"] | ||
625 | 345 | self.assertSubscriptions([]) | ||
626 | 346 | |||
627 | 347 | def test_getSubscriptionsForBugTask_with_filter_for_not_all_tags(self): | ||
628 | 348 | # If a subscription filter specifies that all of one or more specific | ||
629 | 349 | # tags must not be present, bugs without all of those tags are | ||
630 | 350 | # matched. | ||
631 | 351 | |||
632 | 352 | # Looking to exclude the "foo" and "bar" tags. | ||
633 | 353 | subscription_filter = self.subscription.newBugFilter() | ||
634 | 354 | subscription_filter.tags = [u"-foo", u"-bar"] | ||
635 | 355 | subscription_filter.find_all_tags = True | ||
636 | 356 | |||
637 | 357 | # Without either tag the subscription is found. | ||
638 | 358 | self.assertSubscriptions([self.subscription]) | ||
639 | 359 | |||
640 | 360 | # With only one of the excluded tags the subscription is found. | ||
641 | 361 | self.bug.tags = ["foo"] | ||
642 | 362 | self.assertSubscriptions([self.subscription]) | ||
643 | 363 | |||
644 | 364 | # With both tags the subscription is no longer found. | ||
645 | 365 | self.bug.tags = ["foo", "bar"] | ||
646 | 366 | self.assertSubscriptions([]) | ||
651 | 367 | 322 | ||
652 | 368 | def test_getSubscriptionsForBugTask_with_multiple_filters(self): | 323 | def test_getSubscriptionsForBugTask_with_multiple_filters(self): |
653 | 369 | # If multiple filters exist for a subscription, all filters must | 324 | # If multiple filters exist for a subscription, all filters must |
654 | 370 | # match. | 325 | # match. |
655 | 326 | bugtask = self.makeBugTask() | ||
656 | 371 | 327 | ||
659 | 372 | # Add the "foo" tag to the bug. | 328 | # Create a new subscription on self.target. |
660 | 373 | self.bug.tags = ["foo"] | 329 | login_person(self.ordinary_subscriber) |
661 | 330 | subscription = self.target.addSubscription( | ||
662 | 331 | self.ordinary_subscriber, self.ordinary_subscriber) | ||
663 | 332 | subscription.bug_notification_level = BugNotificationLevel.COMMENTS | ||
664 | 374 | 333 | ||
665 | 375 | # Filter the subscription to bugs in the CRITICAL state. | 334 | # Filter the subscription to bugs in the CRITICAL state. |
667 | 376 | subscription_filter = self.subscription.newBugFilter() | 335 | subscription_filter = BugSubscriptionFilter() |
668 | 336 | subscription_filter.structural_subscription = subscription | ||
669 | 377 | subscription_filter.statuses = [BugTaskStatus.CONFIRMED] | 337 | subscription_filter.statuses = [BugTaskStatus.CONFIRMED] |
670 | 378 | subscription_filter.importances = [BugTaskImportance.CRITICAL] | 338 | subscription_filter.importances = [BugTaskImportance.CRITICAL] |
671 | 379 | 339 | ||
672 | 380 | # With the filter the subscription is not found. | 340 | # With the filter the subscription is not found. |
674 | 381 | self.assertSubscriptions([]) | 341 | subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
675 | 342 | bugtask, BugNotificationLevel.NOTHING) | ||
676 | 343 | self.assertEqual([], list(subscriptions_for_bugtask)) | ||
677 | 382 | 344 | ||
678 | 383 | # If the filter is adjusted to match status but not importance, the | 345 | # If the filter is adjusted to match status but not importance, the |
679 | 384 | # subscription is still not found. | 346 | # subscription is still not found. |
682 | 385 | subscription_filter.statuses = [self.bugtask.status] | 347 | subscription_filter.statuses = [bugtask.status] |
683 | 386 | self.assertSubscriptions([]) | 348 | subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
684 | 349 | bugtask, BugNotificationLevel.NOTHING) | ||
685 | 350 | self.assertEqual([], list(subscriptions_for_bugtask)) | ||
686 | 387 | 351 | ||
687 | 388 | # If the filter is adjusted to also match importance, the subscription | 352 | # If the filter is adjusted to also match importance, the subscription |
688 | 389 | # is found again. | 353 | # is found again. |
740 | 390 | subscription_filter.importances = [self.bugtask.importance] | 354 | subscription_filter.importances = [bugtask.importance] |
741 | 391 | self.assertSubscriptions([self.subscription]) | 355 | subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
742 | 392 | 356 | bugtask, BugNotificationLevel.NOTHING) | |
743 | 393 | # If the filter is given some tag criteria, the subscription is not | 357 | self.assertEqual([subscription], list(subscriptions_for_bugtask)) |
693 | 394 | # found. | ||
694 | 395 | subscription_filter.tags = [u"-foo", u"bar", u"baz"] | ||
695 | 396 | subscription_filter.find_all_tags = False | ||
696 | 397 | self.assertSubscriptions([]) | ||
697 | 398 | |||
698 | 399 | # After removing the "foo" tag and adding the "bar" tag, the | ||
699 | 400 | # subscription is found. | ||
700 | 401 | self.bug.tags = ["bar"] | ||
701 | 402 | self.assertSubscriptions([self.subscription]) | ||
702 | 403 | |||
703 | 404 | # Requiring that all tag criteria are fulfilled causes the | ||
704 | 405 | # subscription to no longer be found. | ||
705 | 406 | subscription_filter.find_all_tags = True | ||
706 | 407 | self.assertSubscriptions([]) | ||
707 | 408 | |||
708 | 409 | # After adding the "baz" tag, the subscription is found again. | ||
709 | 410 | self.bug.tags = ["bar", "baz"] | ||
710 | 411 | self.assertSubscriptions([self.subscription]) | ||
711 | 412 | |||
712 | 413 | def test_getSubscriptionsForBugTask_any_filter_is_a_match(self): | ||
713 | 414 | # If a subscription has multiple filters, the subscription is selected | ||
714 | 415 | # when any filter is found to match. Put another way, the filters are | ||
715 | 416 | # ORed together. | ||
716 | 417 | subscription_filter1 = self.subscription.newBugFilter() | ||
717 | 418 | subscription_filter1.statuses = [BugTaskStatus.CONFIRMED] | ||
718 | 419 | subscription_filter2 = self.subscription.newBugFilter() | ||
719 | 420 | subscription_filter2.tags = [u"foo"] | ||
720 | 421 | |||
721 | 422 | # With the filter the subscription is not found. | ||
722 | 423 | self.assertSubscriptions([]) | ||
723 | 424 | |||
724 | 425 | # If the bugtask is adjusted to match the criteria of the first filter | ||
725 | 426 | # but not those of the second, the subscription is found. | ||
726 | 427 | self.bugtask.transitionToStatus( | ||
727 | 428 | BugTaskStatus.CONFIRMED, self.ordinary_subscriber) | ||
728 | 429 | self.assertSubscriptions([self.subscription]) | ||
729 | 430 | |||
730 | 431 | # If the filter is adjusted to also match the criteria of the second | ||
731 | 432 | # filter, the subscription is still found. | ||
732 | 433 | self.bugtask.bug.tags = [u"foo"] | ||
733 | 434 | self.assertSubscriptions([self.subscription]) | ||
734 | 435 | |||
735 | 436 | # If the bugtask is adjusted to no longer match the criteria of the | ||
736 | 437 | # first filter, the subscription is still found. | ||
737 | 438 | self.bugtask.transitionToStatus( | ||
738 | 439 | BugTaskStatus.INPROGRESS, self.ordinary_subscriber) | ||
739 | 440 | self.assertSubscriptions([self.subscription]) | ||
744 | 441 | 358 | ||
745 | 442 | 359 | ||
746 | 443 | class TestStructuralSubscriptionForDistro( | 360 | class TestStructuralSubscriptionForDistro( |
748 | 444 | RestrictedStructuralSubscriptionTestBase, TestCaseWithFactory): | 361 | FilteredStructuralSubscriptionTestBase, TestCaseWithFactory): |
749 | 445 | 362 | ||
750 | 446 | layer = LaunchpadFunctionalLayer | 363 | layer = LaunchpadFunctionalLayer |
751 | 447 | 364 | ||
752 | @@ -504,15 +421,10 @@ | |||
753 | 504 | StructuralSubscription) | 421 | StructuralSubscription) |
754 | 505 | 422 | ||
755 | 506 | 423 | ||
756 | 507 | class TestStructuralSubscriptionFiltersForDistro( | ||
757 | 508 | FilteredStructuralSubscriptionTestBase, TestCaseWithFactory): | ||
758 | 509 | |||
759 | 510 | def makeTarget(self): | ||
760 | 511 | return self.factory.makeDistribution() | ||
761 | 512 | |||
762 | 513 | |||
763 | 514 | class TestStructuralSubscriptionForProduct( | 424 | class TestStructuralSubscriptionForProduct( |
765 | 515 | UnrestrictedStructuralSubscriptionTestBase, TestCaseWithFactory): | 425 | UnrestrictedStructuralSubscription, |
766 | 426 | FilteredStructuralSubscriptionTestBase, | ||
767 | 427 | TestCaseWithFactory): | ||
768 | 516 | 428 | ||
769 | 517 | layer = LaunchpadFunctionalLayer | 429 | layer = LaunchpadFunctionalLayer |
770 | 518 | 430 | ||
771 | @@ -521,15 +433,10 @@ | |||
772 | 521 | self.target = self.factory.makeProduct() | 433 | self.target = self.factory.makeProduct() |
773 | 522 | 434 | ||
774 | 523 | 435 | ||
775 | 524 | class TestStructuralSubscriptionFiltersForProduct( | ||
776 | 525 | FilteredStructuralSubscriptionTestBase, TestCaseWithFactory): | ||
777 | 526 | |||
778 | 527 | def makeTarget(self): | ||
779 | 528 | return self.factory.makeProduct() | ||
780 | 529 | |||
781 | 530 | |||
782 | 531 | class TestStructuralSubscriptionForDistroSourcePackage( | 436 | class TestStructuralSubscriptionForDistroSourcePackage( |
784 | 532 | UnrestrictedStructuralSubscriptionTestBase, TestCaseWithFactory): | 437 | UnrestrictedStructuralSubscription, |
785 | 438 | FilteredStructuralSubscriptionTestBase, | ||
786 | 439 | TestCaseWithFactory): | ||
787 | 533 | 440 | ||
788 | 534 | layer = LaunchpadFunctionalLayer | 441 | layer = LaunchpadFunctionalLayer |
789 | 535 | 442 | ||
790 | @@ -539,15 +446,10 @@ | |||
791 | 539 | self.target = ProxyFactory(self.target) | 446 | self.target = ProxyFactory(self.target) |
792 | 540 | 447 | ||
793 | 541 | 448 | ||
794 | 542 | class TestStructuralSubscriptionFiltersForDistroSourcePackage( | ||
795 | 543 | FilteredStructuralSubscriptionTestBase, TestCaseWithFactory): | ||
796 | 544 | |||
797 | 545 | def makeTarget(self): | ||
798 | 546 | return self.factory.makeDistributionSourcePackage() | ||
799 | 547 | |||
800 | 548 | |||
801 | 549 | class TestStructuralSubscriptionForMilestone( | 449 | class TestStructuralSubscriptionForMilestone( |
803 | 550 | UnrestrictedStructuralSubscriptionTestBase, TestCaseWithFactory): | 450 | UnrestrictedStructuralSubscription, |
804 | 451 | FilteredStructuralSubscriptionTestBase, | ||
805 | 452 | TestCaseWithFactory): | ||
806 | 551 | 453 | ||
807 | 552 | layer = LaunchpadFunctionalLayer | 454 | layer = LaunchpadFunctionalLayer |
808 | 553 | 455 | ||
809 | @@ -556,19 +458,15 @@ | |||
810 | 556 | self.target = self.factory.makeMilestone() | 458 | self.target = self.factory.makeMilestone() |
811 | 557 | self.target = ProxyFactory(self.target) | 459 | self.target = ProxyFactory(self.target) |
812 | 558 | 460 | ||
813 | 559 | |||
814 | 560 | class TestStructuralSubscriptionFiltersForMilestone( | ||
815 | 561 | FilteredStructuralSubscriptionTestBase, TestCaseWithFactory): | ||
816 | 562 | |||
817 | 563 | def makeTarget(self): | ||
818 | 564 | return self.factory.makeMilestone() | ||
819 | 565 | |||
820 | 566 | def makeBugTask(self): | 461 | def makeBugTask(self): |
821 | 462 | # XXX Should test with target *and* series_target. | ||
822 | 567 | return self.factory.makeBugTask(target=self.target.series_target) | 463 | return self.factory.makeBugTask(target=self.target.series_target) |
823 | 568 | 464 | ||
824 | 569 | 465 | ||
825 | 570 | class TestStructuralSubscriptionForDistroSeries( | 466 | class TestStructuralSubscriptionForDistroSeries( |
827 | 571 | UnrestrictedStructuralSubscriptionTestBase, TestCaseWithFactory): | 467 | UnrestrictedStructuralSubscription, |
828 | 468 | FilteredStructuralSubscriptionTestBase, | ||
829 | 469 | TestCaseWithFactory): | ||
830 | 572 | 470 | ||
831 | 573 | layer = LaunchpadFunctionalLayer | 471 | layer = LaunchpadFunctionalLayer |
832 | 574 | 472 | ||
833 | @@ -578,15 +476,10 @@ | |||
834 | 578 | self.target = ProxyFactory(self.target) | 476 | self.target = ProxyFactory(self.target) |
835 | 579 | 477 | ||
836 | 580 | 478 | ||
837 | 581 | class TestStructuralSubscriptionFiltersForDistroSeries( | ||
838 | 582 | FilteredStructuralSubscriptionTestBase, TestCaseWithFactory): | ||
839 | 583 | |||
840 | 584 | def makeTarget(self): | ||
841 | 585 | return self.factory.makeDistroSeries() | ||
842 | 586 | |||
843 | 587 | |||
844 | 588 | class TestStructuralSubscriptionForProjectGroup( | 479 | class TestStructuralSubscriptionForProjectGroup( |
846 | 589 | UnrestrictedStructuralSubscriptionTestBase, TestCaseWithFactory): | 480 | UnrestrictedStructuralSubscription, |
847 | 481 | FilteredStructuralSubscriptionTestBase, | ||
848 | 482 | TestCaseWithFactory): | ||
849 | 590 | 483 | ||
850 | 591 | layer = LaunchpadFunctionalLayer | 484 | layer = LaunchpadFunctionalLayer |
851 | 592 | 485 | ||
852 | @@ -595,20 +488,15 @@ | |||
853 | 595 | self.target = self.factory.makeProject() | 488 | self.target = self.factory.makeProject() |
854 | 596 | self.target = ProxyFactory(self.target) | 489 | self.target = ProxyFactory(self.target) |
855 | 597 | 490 | ||
856 | 598 | |||
857 | 599 | class TestStructuralSubscriptionFiltersForProjectGroup( | ||
858 | 600 | FilteredStructuralSubscriptionTestBase, TestCaseWithFactory): | ||
859 | 601 | |||
860 | 602 | def makeTarget(self): | ||
861 | 603 | return self.factory.makeProject() | ||
862 | 604 | |||
863 | 605 | def makeBugTask(self): | 491 | def makeBugTask(self): |
864 | 606 | return self.factory.makeBugTask( | 492 | return self.factory.makeBugTask( |
865 | 607 | target=self.factory.makeProduct(project=self.target)) | 493 | target=self.factory.makeProduct(project=self.target)) |
866 | 608 | 494 | ||
867 | 609 | 495 | ||
868 | 610 | class TestStructuralSubscriptionForProductSeries( | 496 | class TestStructuralSubscriptionForProductSeries( |
870 | 611 | UnrestrictedStructuralSubscriptionTestBase, TestCaseWithFactory): | 497 | UnrestrictedStructuralSubscription, |
871 | 498 | FilteredStructuralSubscriptionTestBase, | ||
872 | 499 | TestCaseWithFactory): | ||
873 | 612 | 500 | ||
874 | 613 | layer = LaunchpadFunctionalLayer | 501 | layer = LaunchpadFunctionalLayer |
875 | 614 | 502 | ||
876 | @@ -618,13 +506,6 @@ | |||
877 | 618 | self.target = ProxyFactory(self.target) | 506 | self.target = ProxyFactory(self.target) |
878 | 619 | 507 | ||
879 | 620 | 508 | ||
880 | 621 | class TestStructuralSubscriptionFiltersForProductSeries( | ||
881 | 622 | FilteredStructuralSubscriptionTestBase, TestCaseWithFactory): | ||
882 | 623 | |||
883 | 624 | def makeTarget(self): | ||
884 | 625 | return self.factory.makeProductSeries() | ||
885 | 626 | |||
886 | 627 | |||
887 | 628 | class TestStructuralSubscriptionTargetHelper(TestCaseWithFactory): | 509 | class TestStructuralSubscriptionTargetHelper(TestCaseWithFactory): |
888 | 629 | """Tests for implementations of `IStructuralSubscriptionTargetHelper`.""" | 510 | """Tests for implementations of `IStructuralSubscriptionTargetHelper`.""" |
889 | 630 | 511 |
Trivial.