Merge lp:~edwin-grubbs/launchpad/bug-638924-testfix into lp:launchpad
| Status: | Merged | ||||
|---|---|---|---|---|---|
| Approved by: | Edwin Grubbs on 2010-12-16 | ||||
| Approved revision: | no longer in the source branch. | ||||
| Merged at revision: | 12090 | ||||
| Proposed branch: | lp:~edwin-grubbs/launchpad/bug-638924-testfix | ||||
| Merge into: | lp:launchpad | ||||
| Prerequisite: | lp:~edwin-grubbs/launchpad/bug-638924-milestone-page-eager-loading | ||||
| Diff against target: |
1228 lines (+865/-128) 8 files modified
lib/lp/bugs/interfaces/bugtask.py (+9/-1) lib/lp/bugs/model/bugtask.py (+116/-8) lib/lp/bugs/tests/test_bugsearch_conjoined.py (+340/-0) lib/lp/registry/browser/milestone.py (+5/-19) lib/lp/registry/browser/tests/test_milestone.py (+212/-4) lib/lp/registry/interfaces/person.py (+18/-0) lib/lp/registry/model/person.py (+137/-94) lib/lp/registry/tests/test_person.py (+28/-2) |
||||
| To merge this branch: | bzr merge lp:~edwin-grubbs/launchpad/bug-638924-testfix | ||||
| Related bugs: |
|
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Jelmer Vernooij (community) | code | 2010-12-15 | Approve on 2010-12-16 |
|
Review via email:
|
|||
Commit Message
[r=jelmer]
Description of the Change
Summary
-------
Most of this branch was already landed and then rolled back. I am
including a smaller diff of just the changes that I have made since the
first time it was reviewed.
The query was excluding all the bugtasks for a bug, if the bug had even
one conjoined master. However, the conjoined master bugtask should only
exclude the conjoined slave. The conjoined master is the bugtask on the
project's development focus series or the distro's currentseries. The
conjoined slave is the bugtask on the corresponding project or distro.
In the Launchpad web UI, the milestone for the project or distro bugtask
will be hidden if there is a conjoined master bugtask, but in the
database, the project or distro bugtask is always updated with the same
milestone as the conjoined master bugtask.
Tests
-----
./bin/test -vv -t 'test_bugsearch
Demo and Q/A
------------
These three pages should not timeout.
* https:/
* https:/
* https:/
Diff
----
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -39,6 +39,7 @@
In,
Join,
LeftJoin,
+ Not,
Or,
Select,
SQL,
@@ -1601,6 +1602,78 @@
raise AssertionError(
+ def _buildExcludeCo
+ """Exclude bugtasks with a conjoined master.
+
+ This search option only makes sense when searching for bugtasks
+ for a milestone. Only bugtasks for a project or a distribution
+ can have a conjoined master bugtask, which is a bugtask on the
+ project's development focus series or the distribution's
+ currentseries. The project bugtask or the distribution bugtask
+ will always have the same milestone set as its conjoined master
+ bugtask, if it exists on the bug. Therefore, this prevents a lot
+ of bugs having two bugtasks listed in the results. However, it
+ is ok if a bug has multiple bugtasks in the results as long as
+ those other bugtasks are on other series.
+ """
+ # XXX: EdwinGrubbs 2010-12-15 bug=682989
+ # (ConjoinedMaste
+ # (ConjoinedMaste
+ # all foreign keys on the ClassAlias.
+
+ # Perform a LEFT JOIN to the conjoined master bugtask. If the
+ # conjoined master is not null, it gets filtered out.
+ ConjoinedMaster = ClassAlias(BugTask, 'ConjoinedMaster')
+ extra_clauses = ["ConjoinedMast
+ if milestone.
+ current_series = milestone.
+ join = LeftJoin(
+ ConjoinedMaster,
+ And(ConjoinedMa
+ BugTask.
+ ConjoinedMaster
+ Not(ConjoinedMa
+ BugTask.
+ join_tables = [(ConjoinedMaster, join)]
+ else:
+ # Prevent import loop.
+ from lp.registry.
+ from lp.registry.
+ if IProjectGroupMi
+ # Since an IProjectGroupMi
+ # bugtasks on two different projects, the project
+ # bugtask is only excluded by a development focus series
+ # bugtask on the same project.
+ joins = [
+ Join(Milestone, BugTask.milestone == Milestone.id),
+ LeftJoin(Product, BugTask.product == Product.id),
+ LeftJoin(
+ ConjoinedMaster,
+ And(ConjoinedMa
+ ConjoinedMaster
+ == Product.
+ Not(ConjoinedMa
+ BugTask.
+ ]
+ # join.right is the table name.
+ join_tables = [(join.right, join) for join in joins]
+ elif milestone.product is not None:
+ dev_focus_id = (
+ milestone.
+ join = LeftJoin(
+ ConjoinedMaster,
+ And(ConjoinedMa
+ BugTask.productID == milestone.
+ ConjoinedMaster
+ Not(ConjoinedMa
+ BugTask.
+ join_tables = [(ConjoinedMaster, join)]
+ else:
+ raise AssertionError(
+ "A milestone must always have either a project, "
+ "project group, or distribution")
+ return (join_tables, extra_clauses)
+
def buildQuery(self, params):
"""Build and return an SQL query with the given parameters.
@@ -1671,41 +1744,10 @@
if params.
- # Perform a LEFT JOIN to the conjoined master bugtask.
- # If the conjoined master is not null, it gets filtered
- # out.
- ConjoinedMaster = ClassAlias(BugTask, 'ConjoinedMaster')
- extra_clauses.
- if params.
- current_series = (
- params.
- join = LeftJoin(
- ConjoinedMaster,
- And(ConjoinedMa
- ConjoinedMaster
- ))
- join_tables.
- else:
- # Prevent import loop.
- from lp.registry.
- if IProjectGroupMi
- dev_focus_ids = list(
- IStore(
- Product.
- Product.project == params.
- elif params.
- dev_focus_ids = [
- params.
- else:
- raise AssertionError(
- "A milestone must always have either a project, "
- "project group, or distribution")
- join = LeftJoin(
- ConjoinedMaster,
- And(ConjoinedMa
- ConjoinedMaster
- dev_focus_ids)))
- join_tables.
+ tables, clauses = self._buildExcl
+ params.milestone)
+ join_tables += tables
+ extra_clauses += clauses
elif params.
raise ValueError(
=== added file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -0,0 +1,340 @@
+# Copyright 2010 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Test for the exclude_
+
+__metaclass__ = type
+
+__all__ = []
+
+from storm.store import Store
+from testtools.matchers import Equals
+from zope.component import getUtility
+
+from canonical.
+from canonical.
+from lp.bugs.
+ BugTaskSearchPa
+ BugTaskStatus,
+ IBugTaskSet,
+ )
+from lp.registry.
+from lp.testing import (
+ person_logged_in,
+ StormStatementR
+ TestCaseWithFac
+ )
+from lp.testing.matchers import HasQueryCount
+
+
+class TestSearchBase(
+ """Tests of exclude_
+
+ def makeBug(self, milestone):
+ bug = self.factory.
+ product=
+ with person_
+ bug.default_
+ milestone, milestone.
+ return bug
+
+
+class TestProjectExcl
+ """Tests of exclude_
+
+ layer = DatabaseFunctio
+
+ def setUp(self):
+ super(TestProje
+ self.bugtask_set = getUtility(
+ self.product = self.factory.
+ self.milestone = self.factory.
+ product=
+ self.bug_count = 2
+ self.bugs = [
+ self.makeBug(
+ for i in range(self.
+ self.params = BugTaskSearchPa
+ user=None, milestone=
+
+ def test_search_
+ # Verify number of results with no conjoined masters.
+ self.assertEqual(
+ self.bug_count,
+ self.bugtask_
+
+ def test_search_
+ # Verify query count.
+ Store.of(
+ with StormStatementR
+ list(self.
+ self.assertThat
+
+ def test_search_
+ # Test with zero conjoined masters and bugtasks targeted to
+ # productseries that are not the development focus.
+ productseries = self.factory.
+ extra_bugtasks = 0
+ for bug in self.bugs:
+ extra_bugtasks += 1
+ bugtask = self.factory.
+ with person_
+ bugtask.
+ self.milestone, self.product.owner)
+ self.assertEqual(
+ self.bug_count + extra_bugtasks,
+ self.bugtask_
+
+ def test_search_
+ # Test with increasing numbers of conjoined masters.
+ # The conjoined masters will exclude the conjoined slaves from
+ # the results.
+ tasks = list(self.
+ for bug in self.bugs:
+ # The product bugtask is in the results before the conjoined
+ # master is added.
+ self.assertIn(
+ (bug.id, self.product),
+ [(task.bug.id, task.product) for task in tasks])
+ bugtask = self.factory.
+ bug=bug, target=
+ tasks = list(self.
+ # The product bugtask is excluded from the results.
+ self.assertEqua
+ self.assertNotIn(
+ (bug.id, self.product),
+ [(task.bug.id, task.product) for task in tasks])
+
+ def test_search_
+ # Test that conjoined master bugtasks in the WONTFIX status
+ # don't cause the bug to be excluded.
+ masters = [
+ self.factory.
+ bug=bug, target=
+ for bug in self.bugs]
+ tasks = list(self.
+ wontfix_
+ for bugtask in masters:
+ wontfix_
+ self.assertNotIn(
+ (bugtask.bug.id, self.product),
+ [(task.bug.id, task.product) for task in tasks])
+ with person_
+ bugtask.
+ BugTaskStatus.
+ tasks = list(self.
+ self.assertEqua
+ len(tasks))
+ self.assertIn(
+ (bugtask.bug.id, self.product),
+ [(task.bug.id, task.product) for task in tasks])
+
+
+class TestProjectGrou
+ """Tests of exclude_
+
+ layer = DatabaseFunctio
+
+ def setUp(self):
+ super(TestProje
+ self.bugtask_set = getUtility(
+ self.projectgroup = self.factory.
+ self.bug_count = 2
+ self.bug_products = {}
+ for i in range(self.
+ product = self.factory.
+ product_milestone = self.factory.
+ product=product, name='foo')
+ bug = self.makeBug(
+ self.bug_
+ self.milestone = self.projectgro
+ self.params = BugTaskSearchPa
+ user=None, milestone=
+
+ def test_search_
+ # Verify number of results with no conjoined masters.
+ self.assertEqual(
+ self.bug_count,
+ self.bugtask_
+
+ def test_search_
+ # Verify query count.
+ Store.of(
+ with StormStatementR
+ list(self.
+ self.assertThat
+
+ def test_search_
+ # Test with zero conjoined masters and bugtasks targeted to
+ # productseries that are not the development focus.
+ extra_bugtasks = 0
+ for bug, product in self.bug_
+ extra_bugtasks += 1
+ productseries = self.factory.
+ bugtask = self.factory.
+ with person_
+ bugtask.
+ product.
+ self.assertEqual(
+ self.bug_count + extra_bugtasks,
+ self.bugtask_
+
+ def test_search_
+ # Test with increasing numbers of conjoined masters.
+ tasks = list(self.
+ for bug, product in self.bug_
+ self.assertIn(
+ (bug.id, product),
+ [(task.bug.id, task.product) for task in tasks])
+ self.factory.
+ bug=bug, target=
+ tasks = list(self.
+ self.assertEqual(
+ self.bug_count,
+ self.bugtask_
+ self.assertNotIn(
+ (bug.id, product),
+ [(task.bug.id, task.product) for task in tasks])
+
+ def test_search_
+ # Verify that a conjoined master in one project of the project
+ # group doesn't cause a bugtask on another project in the group
+ # to be excluded from the project group milestone's bugs.
+ extra_bugtasks = 0
+ for bug, product in self.bug_
+ extra_bugtasks += 1
+ other_product = self.factory.
+ project=
+ # Create a new milestone with the same name.
+ other_product_
+ product=
+ name=bug.
+ # Add bugtask on the new product and select the milestone.
+ other_product_
+ bug=bug, target=
+ with person_
+ other_product_
+ other_product_
+ # Add conjoined master for the milestone on the new product.
+ self.factory.
+ bug=bug, target=
+ # The bug count should not change, since we are just adding
+ # bugtasks on existing bugs.
+ self.assertEqual(
+ self.bug_count + extra_bugtasks,
+ self.bugtask_
+
+ def test_search_
+ # Test that conjoined master bugtasks in the WONTFIX status
+ # don't cause the bug to be excluded.
+ masters = [
+ self.factory.
+ bug=bug, target=
+ for bug, product in self.bug_
+ unexcluded_count = 0
+ for bugtask in masters:
+ unexcluded_count += 1
+ with person_
+ bugtask.
+ BugTaskStatus.
+ self.assertEqual(
+ self.bug_count + unexcluded_count,
+ self.bugtask_
+
+
+class TestDistributio
+ """Tests of exclude_
+
+ layer = DatabaseFunctio
+
+ def setUp(self):
+ super(TestDistr
+ self.bugtask_set = getUtility(
+ self.distro = getUtility(
+ self.milestone = self.factory.
+ distribution=
+ self.bug_count = 2
+ self.bugs = [
+ self.makeBug(
+ for i in range(self.
+ self.params = BugTaskSearchPa
+ user=None, milestone=
+
+ def test_search_
+ # Verify number of results with no conjoined masters.
+ self.assertEqual(
+ self.bug_count,
+ self.bugtask_
+
+ def test_search_
+ # Verify query count.
+ # 1. Query all the distroseries to determine the distro's
+ # currentseries.
+ # 2. Query the bugtasks.
+ Store.of(
+ with StormStatementR
+ list(self.
+ self.assertThat
+
+ def test_search_
+ # Test with zero conjoined masters and bugtasks targeted to
+ # productseries that are not the development focus.
+ distroseries = self.factory.
+ distribution=
+ extra_bugtasks = 0
+ for bug in self.bugs:
+ extra_bugtasks += 1
+ bugtask = self.factory.
+ with person_
+ bugtask.
+ self.milestone, self.distro.owner)
+ self.assertEqual(
+ self.bug_count + extra_bugtasks,
+ self.bugtask_
+
+ def test_search_
+ # Test with increasing numbers of conjoined masters.
+ tasks = list(self.
+ for bug in self.bugs:
+ # The distro bugtask is in the results before the conjoined
+ # master is added.
+ self.assertIn(
+ (bug.id, self.distro),
+ [(task.bug.id, task.distribution) for task in tasks])
+ self.factory.
+ bug=bug, target=
+ tasks = list(self.
+ # The product bugtask is excluded from the results.
+ self.assertEqua
+ self.assertNotIn(
+ (bug.id, self.distro),
+ [(task.bug.id, task.distribution) for task in tasks])
+
+ def test_search_
+ # Test that conjoined master bugtasks in the WONTFIX status
+ # don't cause the bug to be excluded.
+ masters = [
+ self.factory.
+ bug=bug, target=
+ for bug in self.bugs]
+ wontfix_
+ tasks = list(self.
+ for bugtask in masters:
+ wontfix_
+ # The distro bugtask is still excluded by the conjoined
+ # master.
+ self.assertNotIn(
+ (bugtask.bug.id, self.distro),
+ [(task.bug.id, task.distribution) for task in tasks])
+ with person_
+ bugtask.
+ BugTaskStatus.
+ tasks = list(self.
+ self.assertEqual(
+ self.bug_count + wontfix_
+ self.bugtask_
+ # The distro bugtask is no longer excluded by the conjoined
+ # master, since its status is WONTFIX.
+ self.assertIn(
+ (bugtask.bug.id, self.distro),
+ [(task.bug.id, task.distribution) for task in tasks])

Sorry this took so long. My SQL is rusty and I wasn't really familiar with the problem space.
Great to see it's very well tested.