launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #06230
[Merge] lp:~adeuring/launchpad/bug-829074 into lp:launchpad
Abel Deuring has proposed merging lp:~adeuring/launchpad/bug-829074 into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~adeuring/launchpad/bug-829074/+merge/91302
This branch is a step to fix bug 829074: Show bugs that are
not known to affect "official" upstream.
Bryce suggests in a comment on the bug to optionally limit the
search to bugtasks targeted to the upstream product.
Daniel points out in another comment that bug 232545 describes
a very similar problem.
On the model side, we can fix both bugs if we allow to optionally
specify an upstream bug target; the search should then return bugs
that have (or do not have) a bug task with the "right properties"
for this target.
An oddity of upstream related searches is that a search with the
parameter pending_bugwatch_elsewhere considers distributions
as a possible upstream target, while the other upstream searches
consider only products as upstream targets.
While I don't see much value in using an entire distribtuion
as the new optional upstream target, I added this option
nevertheless for all upstream related searches, just for the sake
of consistency. (To address bug 232545 completely, I'll add the
targets ISourcePackage and IDistributionSourcePackage in a later
branch. This should make work easier if a Debian package is the
the upstream of an Ubuntu package; it might also help for the new
derived distributions.)
Implementation:
A new property BugTaskSearchParams.upstream_target. This property
is only relevant if at least one of
BugTaskSearchParams.pending_bugwatch_elsewhere,
BugTaskSearchParams.has_no_upstream_bugtask,
BugTaskSearchParams.resolved_usptream or
BugTaskSearchParams.open_upstream is specified.
If BugTaskSearchParams.upstream_target is specified, the WHERE
clause returned by BugTaskSet.buildUpstreamClause() contains an
expression that limits the search to bugtasks for this target.
The file lp/bugs/tests/test_bugtask_search.py allows to write a
single test that is run for all possible bug targets.
A proper setup of an upstream related test can be a bit convoluted
-- see for example the already existing method
test_has_no_upstream_bugtask(). To make life a bit easier, I
limited the new tests to the targets SourcePackage and
DistributionSourcePackage: Searching for bugs on other targets
with an upstream filter together with a specific upstream target
is probably pointless, and I intend to add option "limit the
upstream search to upstream target X" to source packages and
DSPs as the main search target.
tests:
./bin/test bugs -vvt lp.bugs.tests.test_bugtask_search.*test_resolved_upstream
./bin/test bugs -vvt lp.bugs.tests.test_bugtask_search.*test_open_upstream
./bin/test bugs -vvt lp.bugs.tests.test_bugtask_search.*test_has_no_upstream_bugtask
./bin/test bugs -vvt lp.bugs.tests.test_bugtask_search.*test_pending_bugwatch_elsewhere
--
https://code.launchpad.net/~adeuring/launchpad/bug-829074/+merge/91302
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~adeuring/launchpad/bug-829074 into lp:launchpad.
=== modified file 'lib/lp/bugs/interfaces/bugtask.py'
--- lib/lp/bugs/interfaces/bugtask.py 2012-01-10 14:24:19 +0000
+++ lib/lp/bugs/interfaces/bugtask.py 2012-02-02 16:31:40 +0000
@@ -1204,7 +1204,8 @@
hardware_is_linked_to_bug=False,
linked_branches=None, linked_blueprints=None,
structural_subscriber=None, modified_since=None,
- created_since=None, exclude_conjoined_tasks=False, cve=None):
+ created_since=None, exclude_conjoined_tasks=False, cve=None,
+ upstream_target=None):
self.bug = bug
self.searchtext = searchtext
@@ -1254,6 +1255,7 @@
self.created_since = created_since
self.exclude_conjoined_tasks = exclude_conjoined_tasks
self.cve = cve
+ self.upstream_target = upstream_target
def setProduct(self, product):
"""Set the upstream context on which to filter the search."""
=== modified file 'lib/lp/bugs/model/bugtask.py'
--- lib/lp/bugs/model/bugtask.py 2012-01-09 13:25:03 +0000
+++ lib/lp/bugs/model/bugtask.py 2012-02-02 16:31:40 +0000
@@ -1742,19 +1742,35 @@
_ORDERBY_COLUMN = None
_open_resolved_upstream = """
- EXISTS (
- SELECT TRUE FROM BugTask AS RelatedBugTask
- WHERE RelatedBugTask.bug = BugTask.bug
- AND RelatedBugTask.id != BugTask.id
- AND ((
- RelatedBugTask.bugwatch IS NOT NULL AND
- RelatedBugTask.status %s)
- OR (
- RelatedBugTask.product IS NOT NULL AND
- RelatedBugTask.bugwatch IS NULL AND
- RelatedBugTask.status %s))
- )
- """
+ EXISTS (
+ SELECT TRUE FROM BugTask AS RelatedBugTask
+ WHERE RelatedBugTask.bug = BugTask.bug
+ AND RelatedBugTask.id != BugTask.id
+ AND ((
+ RelatedBugTask.bugwatch IS NOT NULL AND
+ RelatedBugTask.status %s)
+ OR (
+ RelatedBugTask.product IS NOT NULL AND
+ RelatedBugTask.bugwatch IS NULL AND
+ RelatedBugTask.status %s))
+ )
+ """
+
+ _open_resolved_upstream_with_target = """
+ EXISTS (
+ SELECT TRUE FROM BugTask AS RelatedBugTask
+ WHERE RelatedBugTask.bug = BugTask.bug
+ AND RelatedBugTask.id != BugTask.id
+ AND ((
+ RelatedBugTask.%(target_column)s = %(target_id)s AND
+ RelatedBugTask.bugwatch IS NOT NULL AND
+ RelatedBugTask.status %(status_with_watch)s)
+ OR (
+ RelatedBugTask.%(target_column)s = %(target_id)s AND
+ RelatedBugTask.bugwatch IS NULL AND
+ RelatedBugTask.status %(status_without_watch)s))
+ )
+ """
title = "A set of bug tasks"
@@ -2411,66 +2427,147 @@
query, clauseTables, decorator, join_tables,
has_duplicate_results, with_clause)
- def buildUpstreamClause(self, params):
- """Return an clause for returning upstream data if the data exists.
-
- This method will handles BugTasks that do not have upstream BugTasks
- as well as thoses that do.
+ def buildPendingBugwatchElsewhereClause(self, params):
+ """Return a clause for BugTaskSearchParams.pending_bugwatch_elsewhere
"""
- params = self._require_params(params)
- upstream_clauses = []
- if params.pending_bugwatch_elsewhere:
- if params.product:
- # Include only bugtasks that do no have bug watches that
- # belong to a product that does not use Malone.
- pending_bugwatch_elsewhere_clause = """
- EXISTS (
- SELECT TRUE
- FROM BugTask AS RelatedBugTask
- LEFT OUTER JOIN Product AS OtherProduct
- ON RelatedBugTask.product = OtherProduct.id
- WHERE RelatedBugTask.bug = BugTask.bug
- AND RelatedBugTask.id = BugTask.id
- AND RelatedBugTask.bugwatch IS NULL
- AND OtherProduct.official_malone IS FALSE
- AND RelatedBugTask.status != %s)
- """ % sqlvalues(BugTaskStatus.INVALID)
+ if params.product:
+ # Include only bugtasks that do no have bug watches that
+ # belong to a product that does not use Malone.
+ return """
+ EXISTS (
+ SELECT TRUE
+ FROM BugTask AS RelatedBugTask
+ LEFT OUTER JOIN Product AS OtherProduct
+ ON RelatedBugTask.product = OtherProduct.id
+ WHERE RelatedBugTask.bug = BugTask.bug
+ AND RelatedBugTask.id = BugTask.id
+ AND RelatedBugTask.bugwatch IS NULL
+ AND OtherProduct.official_malone IS FALSE
+ AND RelatedBugTask.status != %s)
+ """ % sqlvalues(BugTaskStatus.INVALID)
+ elif params.upstream_target is None:
+ # Include only bugtasks that have other bugtasks on targets
+ # not using Malone, which are not Invalid, and have no bug
+ # watch.
+ return """
+ EXISTS (
+ SELECT TRUE
+ FROM BugTask AS RelatedBugTask
+ LEFT OUTER JOIN Distribution AS OtherDistribution
+ ON RelatedBugTask.distribution =
+ OtherDistribution.id
+ LEFT OUTER JOIN Product AS OtherProduct
+ ON RelatedBugTask.product = OtherProduct.id
+ WHERE RelatedBugTask.bug = BugTask.bug
+ AND RelatedBugTask.id != BugTask.id
+ AND RelatedBugTask.bugwatch IS NULL
+ AND (
+ OtherDistribution.official_malone IS FALSE
+ OR OtherProduct.official_malone IS FALSE)
+ AND RelatedBugTask.status != %s)
+ """ % sqlvalues(BugTaskStatus.INVALID)
+ else:
+ # Include only bugtasks that have other bugtasks on
+ # params.upstream_target, but only if this this product
+ # does not use Malone and if the bugtasks are not Invalid,
+ # and have no bug watch.
+ if IProduct.providedBy(params.upstream_target):
+ target_clause = 'RelatedBugTask.product = %s'
+ elif IDistribution.providedBy(params.upstream_target):
+ target_clause = 'RelatedBugTask.distribution = %s'
else:
- # Include only bugtasks that have other bugtasks on targets
- # not using Malone, which are not Invalid, and have no bug
- # watch.
- pending_bugwatch_elsewhere_clause = """
- EXISTS (
- SELECT TRUE
- FROM BugTask AS RelatedBugTask
- LEFT OUTER JOIN Distribution AS OtherDistribution
- ON RelatedBugTask.distribution =
- OtherDistribution.id
- LEFT OUTER JOIN Product AS OtherProduct
- ON RelatedBugTask.product = OtherProduct.id
- WHERE RelatedBugTask.bug = BugTask.bug
- AND RelatedBugTask.id != BugTask.id
- AND RelatedBugTask.bugwatch IS NULL
- AND (
- OtherDistribution.official_malone IS FALSE
- OR OtherProduct.official_malone IS FALSE)
- AND RelatedBugTask.status != %s)
- """ % sqlvalues(BugTaskStatus.INVALID)
-
- upstream_clauses.append(pending_bugwatch_elsewhere_clause)
-
- if params.has_no_upstream_bugtask:
+ raise AssertionError(
+ 'params.upstream_target must be a Distribution or '
+ 'a Product')
+ # There is no point to construct a real sub-select if we
+ # already know that the result will be empty.
+ if params.upstream_target.official_malone:
+ return 'false'
+ target_clause = target_clause % sqlvalues(
+ params.upstream_target.id)
+ return """
+ EXISTS (
+ SELECT TRUE
+ FROM BugTask AS RelatedBugTask
+ WHERE RelatedBugTask.bug = BugTask.bug
+ AND RelatedBugTask.id != BugTask.id
+ AND RelatedBugTask.bugwatch IS NULL
+ AND %s
+ AND RelatedBugTask.status != %s)
+ """ % (target_clause, sqlvalues(BugTaskStatus.INVALID)[0])
+
+ def buildNoUpstreamBugtaskClause(self, params):
+ """Return a clause for BugTaskSearchParams.has_no_upstream_bugtask."""
+ if params.upstream_target is None:
# Find all bugs that has no product bugtask. We limit the
# SELECT by matching against BugTask.bug to make the query
# faster.
- has_no_upstream_bugtask_clause = """
+ return """
NOT EXISTS (SELECT TRUE
FROM BugTask AS OtherBugTask
WHERE OtherBugTask.bug = BugTask.bug
AND OtherBugTask.product IS NOT NULL)
"""
- upstream_clauses.append(has_no_upstream_bugtask_clause)
-
+ elif IProduct.providedBy(params.upstream_target):
+ return """
+ NOT EXISTS (SELECT TRUE
+ FROM BugTask AS OtherBugTask
+ WHERE OtherBugTask.bug = BugTask.bug
+ AND OtherBugTask.product=%s)
+ """ % sqlvalues(params.upstream_target.id)
+ elif IDistribution.providedBy(params.upstream_target):
+ return """
+ NOT EXISTS (SELECT TRUE
+ FROM BugTask AS OtherBugTask
+ WHERE OtherBugTask.bug = BugTask.bug
+ AND OtherBugTask.distribution=%s)
+ """ % sqlvalues(params.upstream_target.id)
+ else:
+ raise AssertionError(
+ 'params.upstream_target must be a Distribution or '
+ 'a Product')
+
+ def buildOpenOrResolvedUpstreamClause(self, params,
+ statuses_for_watch_tasks,
+ statuses_for_upstream_tasks):
+ """Return a clause for BugTaskSearchParams.open_upstream or
+ BugTaskSearchParams.resolved_upstream."""
+ if params.upstream_target is None:
+ return self._open_resolved_upstream % (
+ search_value_to_where_condition(
+ any(*statuses_for_watch_tasks)),
+ search_value_to_where_condition(
+ any(*statuses_for_upstream_tasks)))
+ elif IProduct.providedBy(params.upstream_target):
+ query_values = {'target_column': 'product'}
+ elif IDistribution.providedBy(params.upstream_target):
+ query_values = {'target_column': 'distribution'}
+ else:
+ raise AssertionError(
+ 'params.upstream_target must be a Distribution or '
+ 'a Product')
+ query_values['target_id'] = sqlvalues(params.upstream_target.id)[0]
+ query_values['status_with_watch'] = search_value_to_where_condition(
+ any(*statuses_for_watch_tasks))
+ query_values['status_without_watch'] = search_value_to_where_condition(
+ any(*statuses_for_upstream_tasks))
+ return self._open_resolved_upstream_with_target % query_values
+
+ def buildOpenUpstreamClause(self, params):
+ """Return a clause for BugTaskSearchParams.open_upstream."""
+ statuses_for_open_tasks = [
+ BugTaskStatus.NEW,
+ BugTaskStatus.INCOMPLETE,
+ BugTaskStatusSearch.INCOMPLETE_WITHOUT_RESPONSE,
+ BugTaskStatusSearch.INCOMPLETE_WITH_RESPONSE,
+ BugTaskStatus.CONFIRMED,
+ BugTaskStatus.INPROGRESS,
+ BugTaskStatus.UNKNOWN]
+ return self.buildOpenOrResolvedUpstreamClause(
+ params, statuses_for_open_tasks, statuses_for_open_tasks)
+
+ def buildResolvedUpstreamClause(self, params):
+ """Return a clause for BugTaskSearchParams.open_upstream."""
# Our definition of "resolved upstream" means:
#
# * bugs with bugtasks linked to watches that are invalid,
@@ -2481,36 +2578,34 @@
# This definition of "resolved upstream" should address the use
# cases we gathered at UDS Paris (and followup discussions with
# seb128, sfllaw, et al.)
+ statuses_for_watch_tasks = [
+ BugTaskStatus.INVALID,
+ BugTaskStatus.FIXCOMMITTED,
+ BugTaskStatus.FIXRELEASED]
+ statuses_for_upstream_tasks = [
+ BugTaskStatus.FIXCOMMITTED,
+ BugTaskStatus.FIXRELEASED]
+ return self.buildOpenOrResolvedUpstreamClause(
+ params, statuses_for_watch_tasks, statuses_for_upstream_tasks)
+
+ def buildUpstreamClause(self, params):
+ """Return an clause for returning upstream data if the data exists.
+
+ This method will handles BugTasks that do not have upstream BugTasks
+ as well as thoses that do.
+ """
+ params = self._require_params(params)
+ upstream_clauses = []
+ if params.pending_bugwatch_elsewhere:
+ upstream_clauses.append(
+ self.buildPendingBugwatchElsewhereClause(params))
+ if params.has_no_upstream_bugtask:
+ upstream_clauses.append(
+ self.buildNoUpstreamBugtaskClause(params))
if params.resolved_upstream:
- statuses_for_watch_tasks = [
- BugTaskStatus.INVALID,
- BugTaskStatus.FIXCOMMITTED,
- BugTaskStatus.FIXRELEASED]
- statuses_for_upstream_tasks = [
- BugTaskStatus.FIXCOMMITTED,
- BugTaskStatus.FIXRELEASED]
-
- only_resolved_upstream_clause = self._open_resolved_upstream % (
- search_value_to_where_condition(
- any(*statuses_for_watch_tasks)),
- search_value_to_where_condition(
- any(*statuses_for_upstream_tasks)))
- upstream_clauses.append(only_resolved_upstream_clause)
+ upstream_clauses.append(self.buildResolvedUpstreamClause(params))
if params.open_upstream:
- statuses_for_open_tasks = [
- BugTaskStatus.NEW,
- BugTaskStatus.INCOMPLETE,
- BugTaskStatusSearch.INCOMPLETE_WITHOUT_RESPONSE,
- BugTaskStatusSearch.INCOMPLETE_WITH_RESPONSE,
- BugTaskStatus.CONFIRMED,
- BugTaskStatus.INPROGRESS,
- BugTaskStatus.UNKNOWN]
- only_open_upstream_clause = self._open_resolved_upstream % (
- search_value_to_where_condition(
- any(*statuses_for_open_tasks)),
- search_value_to_where_condition(
- any(*statuses_for_open_tasks)))
- upstream_clauses.append(only_open_upstream_clause)
+ upstream_clauses.append(self.buildOpenUpstreamClause(params))
if upstream_clauses:
upstream_clause = " OR ".join(upstream_clauses)
=== modified file 'lib/lp/bugs/tests/test_bugtask_search.py'
--- lib/lp/bugs/tests/test_bugtask_search.py 2012-01-03 10:57:01 +0000
+++ lib/lp/bugs/tests/test_bugtask_search.py 2012-02-02 16:31:40 +0000
@@ -1228,7 +1228,221 @@
self.assertSearchFinds(params, [self.bugtasks[-1]])
-class SourcePackageTarget(BugTargetTestBase):
+class UpstreamFilterTests:
+ """Tests related to restircted upstream filtering.
+
+ These tests make sense only for the targets SourcePackage
+ DistributionSourcePackage.
+ """
+
+ def setUpUpstreamTests(self, upstream_target):
+ # The default test bugs have two tasks for DistributionSourcePackage
+ # tests: one task for the DSP and another task for a product;
+ # they have three tasks for SourcePackage tests: for a product,
+ # for a DSP and for a sourcepackage.
+ # Tests in this class are about searching bug tasks, where the
+ # bug has a task for any upstream target or for a given upstream
+ # target and where the bug task for the upstream target has certain
+ # properties.
+ with person_logged_in(self.searchtarget.distribution.owner):
+ self.searchtarget.distribution.official_malone = True
+ for existing_task in self.bugtasks:
+ bug = existing_task.bug
+ self.factory.makeBugTask(bug, target=upstream_target)
+
+ def addWatch(self, bug, target=None):
+ # Add a bug watch to the bugtask for the given target. If no
+ # target is specified, the bug watch is added to the default
+ # bugtask, which is a different product for each bug.
+ if target is None:
+ task = bug.bugtasks[0]
+ else:
+ for task in bug.bugtasks:
+ if task.target == target:
+ break
+ with person_logged_in(task.target.owner):
+ watch = self.factory.makeBugWatch(bug=bug)
+ task.bugwatch = watch
+
+ def test_pending_bugwatch_elsewhere__no_upstream_specified(self):
+ # By default, those bugs are returned where
+ # - an upstream task exists
+ # - the upstream product does not use LP for bug tracking
+ # - the bug task has no bug watch.
+ # All test bugs fulfill this condition.
+ upstream_target = self.factory.makeProduct()
+ self.setUpUpstreamTests(upstream_target)
+ params = self.getBugTaskSearchParams(
+ user=None, pending_bugwatch_elsewhere=True)
+ self.assertSearchFinds(params, self.bugtasks)
+ # If a bug watch is added to only one of the product related
+ # bug tasks, the bug is still returned.
+ self.addWatch(self.bugtasks[0].bug)
+ self.addWatch(self.bugtasks[1].bug, target=upstream_target)
+ self.assertSearchFinds(params, self.bugtasks)
+ # If bugwatches are added to the other product related bug task
+ # too, the bugs are not included in the search result.
+ self.addWatch(self.bugtasks[0].bug, target=upstream_target)
+ self.addWatch(self.bugtasks[1].bug)
+ self.assertSearchFinds(params, self.bugtasks[2:])
+
+ def test_pending_bugwatch_elsewhere__upstream_product(self):
+ # If an upstream target using Malone is specified, a search
+ # returns all bugs with a bug task for this target, if the
+ # task does not have a bug watch.
+ upstream_target = self.factory.makeProduct()
+ self.setUpUpstreamTests(upstream_target)
+ # The first bug task of all test bugs is targeted to its
+ # own Product instance.
+ bug = self.bugtasks[0].bug
+ single_bugtask_product = bug.bugtasks[0].target
+ params = self.getBugTaskSearchParams(
+ user=None, pending_bugwatch_elsewhere=True,
+ upstream_target=single_bugtask_product)
+ self.assertSearchFinds(params, self.bugtasks[:1])
+ # If a bug watch is added to this task, the search returns an
+ # empty result set.
+ self.addWatch(self.bugtasks[0].bug)
+ self.assertSearchFinds(params, [])
+
+ def test_pending_bugwatch_elsewhere__upstream_product_uses_lp(self):
+ # If an upstream target not using Malone is specified, a search
+ # alsways returns an empty result set.
+ upstream_target = self.factory.makeProduct()
+ self.setUpUpstreamTests(upstream_target)
+ with person_logged_in(upstream_target.owner):
+ upstream_target.official_malone = True
+ params = self.getBugTaskSearchParams(
+ user=None, pending_bugwatch_elsewhere=True,
+ upstream_target=upstream_target)
+ self.assertSearchFinds(params, [])
+
+ def test_pending_bugwatch_elsewhere__upstream_distribution(self):
+ # If an upstream target not using Malone is specified, a search
+ # alsways returns an empty result set.
+ upstream_target = self.factory.makeDistribution()
+ self.setUpUpstreamTests(upstream_target)
+ params = self.getBugTaskSearchParams(
+ user=None, pending_bugwatch_elsewhere=True,
+ upstream_target=upstream_target)
+ self.assertSearchFinds(params, self.bugtasks)
+
+ def test_has_no_upstream_bugtask__target_specified(self):
+ # The target of the default bugtask of the first test bug
+ # (a product) does not appear in other bugs, thus a search
+ # returns all other bugtasks if we specify the search parameters
+ # has_no_upstream_bugtask and use the target described above
+ # as the upstream_target.
+ bug = self.bugtasks[0].bug
+ upstream_target = bug.bugtasks[0].target
+ params = self.getBugTaskSearchParams(
+ user=None, has_no_upstream_bugtask=True,
+ upstream_target=upstream_target)
+ self.assertSearchFinds(params, self.bugtasks[1:])
+ # If a new distribution is specified as the upstream target,
+ # all bugs are returned, since there are no tasks for this
+ # distribution.
+ upstream_target = self.factory.makeDistribution()
+ params = self.getBugTaskSearchParams(
+ user=None, has_no_upstream_bugtask=True,
+ upstream_target=upstream_target)
+ self.assertSearchFinds(params, self.bugtasks)
+ # When we add bugtasks for this distribution, the search returns
+ # an empty result.
+ self.setUpUpstreamTests(upstream_target)
+ self.assertSearchFinds(params, [])
+
+ def test_open_upstream(self):
+ # It is possible to search for bugs with open upstream bugtasks.
+ bug = self.bugtasks[2].bug
+ upstream_task = bug.bugtasks[0]
+ upstream_owner = upstream_task.target.owner
+ with person_logged_in(upstream_owner):
+ upstream_task.transitionToStatus(
+ BugTaskStatus.FIXRELEASED, upstream_owner)
+ params = self.getBugTaskSearchParams(user=None, open_upstream=True)
+ self.assertSearchFinds(params, self.bugtasks[:2])
+
+ def test_open_upstream__upstream_product_specified(self):
+ # A search for bugs having an open upstream bugtask can be
+ # limited to a specific upstream product.
+ bug = self.bugtasks[2].bug
+ upstream_task = bug.bugtasks[0]
+ upstream_product = upstream_task.target
+ params = self.getBugTaskSearchParams(
+ user=None, open_upstream=True, upstream_target=upstream_product)
+ self.assertSearchFinds(params, self.bugtasks[2:])
+ upstream_owner = upstream_product.owner
+ with person_logged_in(upstream_owner):
+ upstream_task.transitionToStatus(
+ BugTaskStatus.FIXRELEASED, upstream_owner)
+ self.assertSearchFinds(params, [])
+
+ def test_open_upstream__upstream_distribution_specified(self):
+ # A search for bugs having an open upstream bugtask can be
+ # limited to a specific upstream distribution.
+ upstream_distro = self.factory.makeDistribution()
+ params = self.getBugTaskSearchParams(
+ user=None, open_upstream=True, upstream_target=upstream_distro)
+ self.assertSearchFinds(params, [])
+ bug = self.bugtasks[0].bug
+ distro_task = self.factory.makeBugTask(
+ bug=bug, target=upstream_distro)
+ self.assertSearchFinds(params, self.bugtasks[:1])
+ with person_logged_in(upstream_distro.owner):
+ distro_task.transitionToStatus(
+ BugTaskStatus.FIXRELEASED, upstream_distro.owner)
+ self.assertSearchFinds(params, [])
+
+ def test_resolved_upstream(self):
+ # It is possible to search for bugs with resolved upstream bugtasks.
+ bug = self.bugtasks[2].bug
+ upstream_task = bug.bugtasks[0]
+ upstream_owner = upstream_task.target.owner
+ with person_logged_in(upstream_owner):
+ upstream_task.transitionToStatus(
+ BugTaskStatus.FIXRELEASED, upstream_owner)
+ params = self.getBugTaskSearchParams(user=None, resolved_upstream=True)
+ self.assertSearchFinds(params, self.bugtasks[2:])
+
+ def test_resolved_upstream__upstream_product_specified(self):
+ # A search for bugs having a resolved upstream bugtask can be
+ # limited to a specific upstream product.
+ bug = self.bugtasks[2].bug
+ upstream_task = bug.bugtasks[0]
+ upstream_product = upstream_task.target
+ params = self.getBugTaskSearchParams(
+ user=None, resolved_upstream=True,
+ upstream_target=upstream_product)
+ self.assertSearchFinds(params, [])
+ upstream_owner = upstream_product.owner
+ for bug in [task.bug for task in self.bugtasks]:
+ upstream_task = bug.bugtasks[0]
+ upstream_owner = upstream_task.owner
+ with person_logged_in(upstream_owner):
+ upstream_task.transitionToStatus(
+ BugTaskStatus.FIXRELEASED, upstream_owner)
+ self.assertSearchFinds(params, self.bugtasks[2:])
+
+ def test_resolved_upstream__upstream_distribution_specified(self):
+ # A search for bugs having an open upstream bugtask can be
+ # limited to a specific upstream distribution.
+ upstream_distro = self.factory.makeDistribution()
+ params = self.getBugTaskSearchParams(
+ user=None, resolved_upstream=True,
+ upstream_target=upstream_distro)
+ self.assertSearchFinds(params, [])
+ bug = self.bugtasks[0].bug
+ distro_task = self.factory.makeBugTask(
+ bug=bug, target=upstream_distro)
+ self.assertSearchFinds(params, [])
+ with person_logged_in(upstream_distro.owner):
+ distro_task.transitionToStatus(
+ BugTaskStatus.FIXRELEASED, upstream_distro.owner)
+ self.assertSearchFinds(params, self.bugtasks[:1])
+
+
+class SourcePackageTarget(BugTargetTestBase, UpstreamFilterTests):
"""Use a source package as the bug target."""
def setUp(self):
@@ -1275,7 +1489,8 @@
class DistributionSourcePackageTarget(BugTargetTestBase,
- BugTargetWithBugSuperVisor):
+ BugTargetWithBugSuperVisor,
+ UpstreamFilterTests):
"""Use a distribution source package as the bug target."""
def setUp(self):