← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~stevenk/launchpad/no-structsub-for-bug-supervisor into lp:launchpad

 

Steve Kowalik has proposed merging lp:~stevenk/launchpad/no-structsub-for-bug-supervisor into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~stevenk/launchpad/no-structsub-for-bug-supervisor/+merge/119831

No longer give bug supervisors an unconditional structural subscription on the target when they are set. Due to this, bug supervisor no longer has to have such a draconian validation method, since setting it will no longer spam them with tonnes of mail.

I have done a bunch of drive-bys in this branch too. Mostly whitespace related.
-- 
https://code.launchpad.net/~stevenk/launchpad/no-structsub-for-bug-supervisor/+merge/119831
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~stevenk/launchpad/no-structsub-for-bug-supervisor into lp:launchpad.
=== modified file 'lib/lp/bugs/browser/bugrole.py'
--- lib/lp/bugs/browser/bugrole.py	2012-02-09 04:28:33 +0000
+++ lib/lp/bugs/browser/bugrole.py	2012-08-16 04:34:22 +0000
@@ -41,47 +41,6 @@
         else:
             return self.OTHER_USER
 
-    def validateBugSupervisor(self, data):
-        """Validates the new bug supervisor.
-
-        Verify that the value is None, the user, or a team he administers,
-        otherwise, set a field error.
-        """
-        field_state = self._getFieldState(
-            self.context.bug_supervisor, 'bug_supervisor', data)
-        if field_state is self.INVALID_PERSON:
-            error = (
-                'You must choose a valid person or team to be the '
-                'bug supervisor for %s.' % self.context.displayname)
-        elif field_state is self.OTHER_TEAM:
-            supervisor = data['bug_supervisor']
-            team_url = canonical_url(
-                supervisor, rootsite='mainsite', view_name='+members')
-            error = structured(
-                'You cannot set %(team)s as the bug supervisor for '
-                '%(target)s because you are not an administrator of that '
-                'team.<br />If you believe that %(team)s should be the '
-                'bug supervisor for %(target)s, notify one of the '
-                '<a href="%(url)s">%(team)s administrators</a>. See '
-                '<a href="https://help.launchpad.net/BugSupervisors";>'
-                'the help wiki</a> for information about setting a bug '
-                'supervisor.',
-                team=supervisor.displayname,
-                target=self.context.displayname,
-                url=team_url)
-        elif field_state is self.OTHER_USER:
-            error = structured(
-                'You cannot set another person as the bug supervisor for '
-                '%(target)s.<br />See '
-                '<a href="https://help.launchpad.net/BugSupervisors";>'
-                'the help wiki</a> for information about setting a bug '
-                'supervisor.',
-                target=self.context.displayname)
-        else:
-            # field_state is self.OK.
-            return
-        self.setFieldError('bug_supervisor', error)
-
     def changeBugSupervisor(self, bug_supervisor):
         if self.context.bug_supervisor != bug_supervisor:
             self.context.setBugSupervisor(bug_supervisor, self.user)

=== modified file 'lib/lp/bugs/browser/bugsupervisor.py'
--- lib/lp/bugs/browser/bugsupervisor.py	2012-02-28 04:24:19 +0000
+++ lib/lp/bugs/browser/bugsupervisor.py	2012-08-16 04:34:22 +0000
@@ -60,10 +60,6 @@
 
     cancel_url = next_url
 
-    def validate(self, data):
-        """See `LaunchpadFormView`."""
-        self.validateBugSupervisor(data)
-
     @action('Change', name='change')
     def change_action(self, action, data):
         """Redirect to the target page with a success message."""
@@ -74,9 +70,5 @@
                 "Successfully cleared the bug supervisor. "
                 "You can set the bug supervisor again at any time.")
         else:
-            message = structured(
-                'A bug mail subscription was created for the bug supervisor. '
-                'You can <a href="%s">edit bug mail</a> '
-                'to change which notifications will be sent.',
-                canonical_url(self.context, view_name='+subscriptions'))
+            message = structured('Bug supervisor privilege granted.')
         self.request.response.addNotification(message)

=== modified file 'lib/lp/bugs/browser/bugtarget.py'
--- lib/lp/bugs/browser/bugtarget.py	2012-08-03 00:30:51 +0000
+++ lib/lp/bugs/browser/bugtarget.py	2012-08-16 04:34:22 +0000
@@ -218,7 +218,6 @@
         """Constrain bug expiration to Launchpad Bugs tracker."""
         super(ProductConfigureBugTrackerView, self).validate(data)
         if check_permission("launchpad.Edit", self.context):
-            self.validateBugSupervisor(data)
             self.validateSecurityContact(data)
         # enable_bug_expiration is disabled by JavaScript when bugtracker
         # is not 'In Launchpad'. The constraint is enforced here in case the

=== modified file 'lib/lp/bugs/browser/tests/test_bugsupervisor.py'
--- lib/lp/bugs/browser/tests/test_bugsupervisor.py	2012-08-14 23:27:07 +0000
+++ lib/lp/bugs/browser/tests/test_bugsupervisor.py	2012-08-16 04:34:22 +0000
@@ -5,7 +5,6 @@
 
 __metaclass__ = type
 
-from zope.app.form.interfaces import ConversionError
 from zope.component import getUtility
 
 from lp.bugs.browser.bugsupervisor import BugSupervisorEditSchema
@@ -14,7 +13,6 @@
 from lp.services.webapp.publisher import canonical_url
 from lp.testing import (
     BrowserTestCase,
-    login,
     login_person,
     TestCaseWithFactory,
     )
@@ -70,10 +68,7 @@
         self.assertEqual(self.product.bug_supervisor, self.owner)
         notifications = view.request.response.notifications
         self.assertEqual(1, len(notifications))
-        expected = (
-            'A bug mail subscription was created for the bug supervisor. '
-            'You can <a href="http://launchpad.dev/boing/+subscriptions";>'
-            'edit bug mail</a> to change which notifications will be sent.')
+        expected = 'Bug supervisor privilege granted.'
         self.assertEqual(expected, notifications.pop().message)
 
     def test_owner_appoint_self_from_another(self):
@@ -115,77 +110,6 @@
         self.assertEqual([], view.errors)
         self.assertEqual(private_team, self.product.bug_supervisor)
 
-    def test_owner_cannot_appoint_another_team(self):
-        team = self.factory.makeTeam(name='smack', displayname='<smack />')
-        form = self._makeForm(team)
-        view = create_initialized_view(
-            self.product, name='+bugsupervisor', form=form)
-        self.assertEqual(1, len(view.errors))
-        expected = (
-            'You cannot set &lt;smack /&gt; as the bug supervisor for '
-            '&lt;boing /&gt; because you are not an administrator of that '
-            'team.<br />If you believe that &lt;smack /&gt; should be the '
-            'bug supervisor for &lt;boing /&gt;, notify one of the '
-            '<a href="http://launchpad.dev/~smack/+members";>&lt;smack /&gt; '
-            'administrators</a>. See '
-            '<a href="https://help.launchpad.net/BugSupervisors";>the '
-            'help wiki</a> for information about setting a bug supervisor.')
-        self.assertEqual(expected, view.errors.pop())
-
-    def test_owner_cannot_appoint_a_nonvalid_user(self):
-        # The vocabulary only accepts valid users.
-        form = self._makeForm(None)
-        form['field.bug_supervisor'] = 'fnord'
-        view = create_initialized_view(
-            self.product, name='+bugsupervisor', form=form)
-        self.assertEqual(2, len(view.errors))
-        expected = (
-            'You must choose a valid person or team to be the bug supervisor '
-            'for &lt;boing /&gt;.')
-        self.assertEqual(expected, view.errors.pop())
-        self.assertTrue(isinstance(view.errors.pop(), ConversionError))
-
-    def test_owner_cannot_appoint_another_user(self):
-        another_user = self.factory.makePerson()
-        form = self._makeForm(another_user)
-        view = create_initialized_view(
-            self.product, name='+bugsupervisor', form=form)
-        self.assertEqual(1, len(view.errors))
-        expected = (
-            'You cannot set another person as the bug supervisor for '
-            '&lt;boing /&gt;.<br />See '
-            '<a href="https://help.launchpad.net/BugSupervisors";>the help '
-            'wiki</a> for information about setting a bug supervisor.')
-        self.assertEqual(expected, view.errors.pop())
-
-    def test_admin_appoint_another_user(self):
-        another_user = self.factory.makePerson()
-        login('admin@xxxxxxxxxxxxx')
-        form = self._makeForm(another_user)
-        view = create_initialized_view(
-            self.product, name='+bugsupervisor', form=form)
-        self.assertEqual([], view.errors)
-        self.assertEqual(another_user, self.product.bug_supervisor)
-
-    def test_admin_appoint_another_team(self):
-        another_team = self.factory.makeTeam()
-        login('admin@xxxxxxxxxxxxx')
-        form = self._makeForm(another_team)
-        view = create_initialized_view(
-            self.product, name='+bugsupervisor', form=form)
-        self.assertEqual([], view.errors)
-        self.assertEqual(another_team, self.product.bug_supervisor)
-
-    def test_admin_appoint_private_team(self):
-        private_team = self.factory.makeTeam(
-            visibility=PersonVisibility.PRIVATE)
-        login('admin@xxxxxxxxxxxxx')
-        form = self._makeForm(private_team)
-        view = create_initialized_view(
-            self.product, name='+bugsupervisor', form=form)
-        self.assertEqual([], view.errors)
-        self.assertEqual(private_team, self.product.bug_supervisor)
-
 
 class TestBugSupervisorLink(BrowserTestCase):
 

=== modified file 'lib/lp/bugs/browser/tests/test_bugtarget_configure.py'
--- lib/lp/bugs/browser/tests/test_bugtarget_configure.py	2012-08-13 19:34:10 +0000
+++ lib/lp/bugs/browser/tests/test_bugtarget_configure.py	2012-08-16 04:34:22 +0000
@@ -92,17 +92,6 @@
             self.product.bug_reported_acknowledgement)
         self.assertFalse(self.product.enable_bugfiling_duplicate_search)
 
-    def test_bug_supervisor_invalid(self):
-        # Verify that invalid bug_supervisor states are reported.
-        # This is a sanity check. The bug_supervisor is rigorously tested
-        # in its own test.
-        other_person = self.factory.makePerson()
-        form = self._makeForm()
-        form['field.bug_supervisor'] = other_person.name
-        view = create_initialized_view(
-            self.product, name='+configure-bugtracker', form=form)
-        self.assertEqual(1, len(view.errors))
-
     def test_security_contact_invalid(self):
         # Verify that invalid security_contact states are reported.
         # This is a sanity check. The security_contact is rigorously tested

=== modified file 'lib/lp/bugs/doc/bugsubscription.txt'
--- lib/lp/bugs/doc/bugsubscription.txt	2012-08-07 13:50:53 +0000
+++ lib/lp/bugs/doc/bugsubscription.txt	2012-08-16 04:34:22 +0000
@@ -199,13 +199,11 @@
 
     >>> print_displayname(linux_source_bug.getIndirectSubscribers())
     No Privileges Person
-    Robert Collins
     Sample Person
     Ubuntu Team
 
     >>> print_displayname(linux_source_bug.getAlsoNotifiedSubscribers())
     No Privileges Person
-    Robert Collins
     Sample Person
     Ubuntu Team
 
@@ -216,13 +214,11 @@
 
     >>> print_displayname(linux_source_bug.getIndirectSubscribers())
     No Privileges Person
-    Robert Collins
     Sample Person
     Ubuntu Team
 
     >>> print_displayname(linux_source_bug.getAlsoNotifiedSubscribers())
     No Privileges Person
-    Robert Collins
     Sample Person
     Ubuntu Team
 
@@ -232,13 +228,11 @@
 
     >>> print_displayname(linux_source_bug.getIndirectSubscribers())
     No Privileges Person
-    Robert Collins
     Sample Person
     Ubuntu Team
 
     >>> print_displayname(linux_source_bug.getAlsoNotifiedSubscribers())
     No Privileges Person
-    Robert Collins
     Sample Person
     Ubuntu Team
 
@@ -267,28 +261,24 @@
     >>> print_displayname(linux_source_bug.getAlsoNotifiedSubscribers(
     ...     level=BugNotificationLevel.COMMENTS))
     No Privileges Person
-    Robert Collins
     Sample Person
     Ubuntu Team
 
     >>> print_displayname(linux_source_bug.getIndirectSubscribers(
     ...     level=BugNotificationLevel.COMMENTS))
     No Privileges Person
-    Robert Collins
     Sample Person
     Ubuntu Team
 
     >>> print_displayname(linux_source_bug.getAlsoNotifiedSubscribers(
     ...     level=BugNotificationLevel.LIFECYCLE))
     No Privileges Person
-    Robert Collins
     Sample Person
     Ubuntu Team
 
     >>> print_displayname(linux_source_bug.getIndirectSubscribers(
     ...     level=BugNotificationLevel.LIFECYCLE))
     No Privileges Person
-    Robert Collins
     Sample Person
     Ubuntu Team
 
@@ -305,26 +295,22 @@
     >>> print_displayname(linux_source_bug.getAlsoNotifiedSubscribers(
     ...     level=BugNotificationLevel.LIFECYCLE))
     No Privileges Person
-    Robert Collins
     Sample Person
     Ubuntu Team
 
     >>> print_displayname(linux_source_bug.getIndirectSubscribers(
     ...     level=BugNotificationLevel.LIFECYCLE))
     No Privileges Person
-    Robert Collins
     Sample Person
     Ubuntu Team
 
     >>> print_displayname(linux_source_bug.getAlsoNotifiedSubscribers(
     ...     level=BugNotificationLevel.METADATA))
-    Robert Collins
     Sample Person
     Ubuntu Team
 
     >>> print_displayname(linux_source_bug.getIndirectSubscribers(
     ...     level=BugNotificationLevel.METADATA))
-    Robert Collins
     Sample Person
     Ubuntu Team
 
@@ -357,7 +343,6 @@
 
     >>> print_displayname(linux_source_bug.getIndirectSubscribers())
     No Privileges Person
-    Robert Collins
     Sample Person
     Scott James Remnant
     Ubuntu Team
@@ -710,7 +695,7 @@
 recipients list.
 
     >>> getSubscribers(new_bug)
-    ['foo.bar@xxxxxxxxxxxxx', 'mark@xxxxxxxxxxx', 'robertc@xxxxxxxxxxxxxxxxx']
+    ['foo.bar@xxxxxxxxxxxxx']
 
 If we create a bug task on Ubuntu in the same bug, the Ubuntu bug
 supervisor will be subscribed:
@@ -720,8 +705,6 @@
 
     >>> print '\n'.join(getSubscribers(new_bug))
     foo.bar@xxxxxxxxxxxxx
-    mark@xxxxxxxxxxx
-    robertc@xxxxxxxxxxxxxxxxx
     support@xxxxxxxxxx
     test@xxxxxxxxxxxxx
 
@@ -753,8 +736,6 @@
 
     >>> print '\n'.join(getSubscribers(new_bug))
     foo.bar@xxxxxxxxxxxxx
-    mark@xxxxxxxxxxx
-    robertc@xxxxxxxxxxxxxxxxx
 
 The upstream maintainer will be subscribed to security-related private
 bugs, because upstream has no security contact, in this case.
@@ -769,8 +750,6 @@
 
     >>> print '\n'.join(getSubscribers(new_bug))
     foo.bar@xxxxxxxxxxxxx
-    mark@xxxxxxxxxxx
-    robertc@xxxxxxxxxxxxxxxxx
     test@xxxxxxxxxxxxx
 
 Now let's create a bug on a specific package, which has no package bug

=== modified file 'lib/lp/bugs/doc/initial-bug-contacts.txt'
--- lib/lp/bugs/doc/initial-bug-contacts.txt	2012-05-22 12:05:51 +0000
+++ lib/lp/bugs/doc/initial-bug-contacts.txt	2012-08-16 04:34:22 +0000
@@ -305,8 +305,7 @@
 
     >>> mozilla_firefox.setBugSupervisor(foobar, foobar)
 
-Then we'll reassign bug #2 in Ubuntu to be in Firefox, noting that Foo
-Bar gets subscribed to the bug in the process:
+Then we'll reassign bug #2 in Ubuntu to be in Firefox:
 
     >>> bug_two_in_ubuntu = getUtility(IBugTaskSet).get(3)
     >>> print bug_two_in_ubuntu.bug.id
@@ -330,46 +329,6 @@
     >>> notify(product_changed)
     >>> transaction.commit()
 
-With the product changed, we can see that Foo Bar is now subscribed:
-
-    >>> print '\n'.join(subscriber_names(bug_two_in_ubuntu.bug))
-    Foo Bar
-    Sample Person
-    Steve Alexander
-    Ubuntu Team
-
-And Foo Bar gets an email containing complete information about the bug
-report. It states that he received the email because he is the bug
-supervisor for Mozilla Firefox.
-
-    >>> test_emails = list(stub.test_emails)
-    >>> test_emails.sort(by_to_addrs)
-
-    >>> len(test_emails)
-    1
-
-    >>> from_addr, to_addr, raw_message = test_emails.pop()
-    >>> print from_addr
-    bounces@xxxxxxxxxxxxx
-
-    >>> print to_addr
-    ['foo.bar@xxxxxxxxxxxxx']
-
-    >>> msg = email.message_from_string(raw_message)
-    >>> msg['Subject']
-    '[Bug 2] [NEW] Blackhole Trash folder'
-
-    >>> print msg.get_payload(decode=True)
-    You have been subscribed to a public bug:
-    ...
-    ** Affects: firefox
-    ...
-    --
-    Blackhole Trash folder
-    http://bugs.launchpad.dev/bugs/2
-    You received this bug notification because you
-    are subscribed to Mozilla Firefox.
-
 
 Teams as bug supervisors
 ------------------------

=== modified file 'lib/lp/bugs/interfaces/bugtask.py'
--- lib/lp/bugs/interfaces/bugtask.py	2012-08-07 03:47:15 +0000
+++ lib/lp/bugs/interfaces/bugtask.py	2012-08-16 04:34:22 +0000
@@ -785,7 +785,7 @@
         """
 
     def userHasDriverPrivileges(user):
-        """Does the user have driver privledges on the current bugtask?
+        """Does the user have driver privileges on the current bugtask?
 
         :return: A boolean.
         """

=== modified file 'lib/lp/bugs/model/tests/test_bugsubscriptioninfo.py'
--- lib/lp/bugs/model/tests/test_bugsubscriptioninfo.py	2012-08-08 07:22:51 +0000
+++ lib/lp/bugs/model/tests/test_bugsubscriptioninfo.py	2012-08-16 04:34:22 +0000
@@ -392,8 +392,7 @@
         # the assignee, supervisor and structural subscriber do.
         found_subscribers = self.getInfo().also_notified_subscribers
         self.assertContentEqual(
-            [assignee, supervisor, structural_subscriber],
-            found_subscribers)
+            [assignee, structural_subscriber], found_subscribers)
 
     def test_also_notified_subscribers_muted(self):
         # If someone is muted, they are not listed in the
@@ -405,13 +404,10 @@
         # when they are not muted.
         found_subscribers = self.getInfo().also_notified_subscribers
         self.assertContentEqual(
-            [assignee, supervisor, structural_subscriber],
-            found_subscribers)
+            [assignee, structural_subscriber], found_subscribers)
         # Now we mute all of the subscribers.
         with person_logged_in(assignee):
             self.bug.mute(assignee, assignee)
-        with person_logged_in(supervisor):
-            self.bug.mute(supervisor, supervisor)
         with person_logged_in(structural_subscriber):
             self.bug.mute(structural_subscriber, structural_subscriber)
         # Now we don't see them.

=== removed file 'lib/lp/bugs/tests/has-bug-supervisor.txt'
--- lib/lp/bugs/tests/has-bug-supervisor.txt	2010-10-14 18:42:19 +0000
+++ lib/lp/bugs/tests/has-bug-supervisor.txt	1970-01-01 00:00:00 +0000
@@ -1,66 +0,0 @@
-= IHasBugSupervisor =
-
-Bug supervisors are people or teams who are responsible for dealing with
-bugs pertaining to a target. All objects with a bug supervisor are also
-structural subscription targets. When the bug supervisor for such an object
-is set, a new bug subscription is created as well.
-
-    >>> list(target.bug_subscriptions)
-    []
-
-    >>> print target.bug_supervisor
-    None
-
-    >>> from lp.registry.interfaces.person import IPersonSet
-    >>> personset = getUtility(IPersonSet)
-    >>> no_priv = personset.getByEmail("no-priv@xxxxxxxxxxxxx")
-    >>> foo_bar = personset.getByEmail("foo.bar@xxxxxxxxxxxxx")
-
-    >>> login("foo.bar@xxxxxxxxxxxxx")
-
-We set no_priv as the bug supervisor for the target.
-
-    >>> target.setBugSupervisor(no_priv, foo_bar)
-
-The bug supervisor can be retrieved using the `bug_supervisor` property.
-
-    >>> print target.bug_supervisor.preferredemail.email
-    no-priv@xxxxxxxxxxxxx
-
-But we can't use it to set the bug supervisor.
-
-    >>> target.bug_supervisor = None
-    Traceback (most recent call last):
-    ...
-    ForbiddenAttribute: ('bug_supervisor', <...>)
-
-After setting no_priv as the bug supervisor, a bug subscription is created
-for the target.
-
-    >>> for bug_subscription in target.bug_subscriptions:
-    ...     print bug_subscription.subscriber.preferredemail.email
-    no-priv@xxxxxxxxxxxxx
-
-And now no_priv is an indirect subscriber to any bug filed on
-the target.
-
-    >>> bug = filebug(target, 'test bug')
-    >>> no_priv in bug.getIndirectSubscribers()
-    True
-
-If no_priv unsubscribes, he is still set as the bug supervisor.
-
-    >>> target.removeBugSubscription(no_priv, no_priv)
-    >>> print target.bug_supervisor.preferredemail.email
-    no-priv@xxxxxxxxxxxxx
-    >>> no_priv in bug.getIndirectSubscribers()
-    False
-
-But we can remove the bug supervisor by setting it to None.
-
-    >>> target.setBugSupervisor(None, None)
-    >>> print target.bug_supervisor
-    None
-
-Note that removing a person as a bug contact supervisor not remove their
-subscription to the target, if it exists.

=== renamed file 'lib/lp/bugs/tests/test_bugcontact.py' => 'lib/lp/bugs/tests/test_bugsupervisor.py'
--- lib/lp/bugs/tests/test_bugcontact.py	2012-06-06 16:04:34 +0000
+++ lib/lp/bugs/tests/test_bugsupervisor.py	2012-08-16 04:34:22 +0000
@@ -1,40 +1,45 @@
 # Copyright 2009 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
-"""Test harness for running tests against IHasBugcontact
-implementations.
-"""
-
-import unittest
-
-from lp.bugs.tests.test_structuralsubscriptiontarget import (
-    distributionSetUp,
-    productSetUp,
+"""Run tests against IHasBugSupervisor implementations."""
+
+from zope.security.interfaces import ForbiddenAttribute
+
+from lp.testing import (
+    person_logged_in,
+    TestCaseWithFactory,
     )
 from lp.testing.layers import DatabaseFunctionalLayer
-from lp.testing.systemdocs import (
-    LayeredDocFileSuite,
-    tearDown,
-    )
-
-
-def test_suite():
-    """Return the `IHasBugSupervisor` TestSuite."""
-    suite = unittest.TestSuite()
-
-    setUpMethods = [
-        productSetUp,
-        distributionSetUp,
-        ]
-
-    testname = 'has-bug-supervisor.txt'
-    for setUpMethod in setUpMethods:
-        id_ext = "%s-%s" % (testname, setUpMethod.func_name)
-        test = LayeredDocFileSuite(
-            testname,
-            id_extensions=[id_ext],
-            setUp=setUpMethod, tearDown=tearDown,
-            layer=DatabaseFunctionalLayer)
-        suite.addTest(test)
-
-    return suite
+
+
+class BugSupervisorMixin:
+
+    def test_cannot_be_set_directly(self):
+        # The bug_supervisor attribute can not be directly set.
+        self.assertRaises(
+            ForbiddenAttribute, setattr, self.target, 'bug_supervisor', None)
+
+    def test_set_bug_supervisor(self):
+        # The bug_supervisor attribute can be set by calling .setBugSupervisor.
+        person = self.factory.makePerson()
+        with person_logged_in(self.target.owner):
+            self.target.setBugSupervisor(person, None)
+        self.assertEqual(person, self.target.bug_supervisor)
+
+
+class TestProductBugSupervisor(TestCaseWithFactory, BugSupervisorMixin):
+
+    layer = DatabaseFunctionalLayer
+
+    def setUp(self):
+        super(TestProductBugSupervisor, self).setUp()
+        self.target = self.factory.makeProduct()
+
+
+class TestDistributionBugSupervisor(TestCaseWithFactory, BugSupervisorMixin):
+
+    layer = DatabaseFunctionalLayer
+
+    def setUp(self):
+        super(TestDistributionBugSupervisor, self).setUp()
+        self.target = self.factory.makeDistribution()

=== modified file 'lib/lp/registry/browser/product.py'
--- lib/lp/registry/browser/product.py	2012-08-11 02:18:44 +0000
+++ lib/lp/registry/browser/product.py	2012-08-16 04:34:22 +0000
@@ -114,7 +114,6 @@
 from lp.app.widgets.itemswidgets import (
     CheckBoxMatrixWidget,
     LaunchpadRadioWidget,
-    LaunchpadRadioWidgetWithDescription,
     )
 from lp.app.widgets.popup import PersonPickerWidget
 from lp.app.widgets.product import (
@@ -612,8 +611,7 @@
         return Link('+branchvisibility', text, icon='edit')
 
 
-class ProductBugsMenu(PillarBugsMenu,
-                      ProductEditLinksMixin):
+class ProductBugsMenu(PillarBugsMenu, ProductEditLinksMixin):
 
     usedfor = IProduct
     facet = 'bugs'
@@ -1543,10 +1541,7 @@
         else:
             return canonical_url(getUtility(IProductSet))
 
-    @property
-    def cancel_url(self):
-        """See `LaunchpadFormView`."""
-        return self.next_url
+    cancel_url = next_url
 
 
 class ProductValidationMixin:

=== modified file 'lib/lp/registry/model/distribution.py'
--- lib/lp/registry/model/distribution.py	2012-08-08 05:36:44 +0000
+++ lib/lp/registry/model/distribution.py	2012-08-16 04:34:22 +0000
@@ -81,7 +81,6 @@
     DB_UNRESOLVED_BUGTASK_STATUSES,
     )
 from lp.bugs.interfaces.bugtaskfilter import OrderedBugTask
-from lp.bugs.model.bug import BugSet
 from lp.bugs.model.bugtarget import (
     BugTargetBase,
     OfficialBugTagTargetMixin,
@@ -1581,8 +1580,6 @@
     def setBugSupervisor(self, bug_supervisor, user):
         """See `IHasBugSupervisor`."""
         self.bug_supervisor = bug_supervisor
-        if bug_supervisor is not None:
-            self.addBugSubscription(bug_supervisor, user)
 
     def getAllowedBugInformationTypes(self):
         """See `IDistribution.`"""

=== modified file 'lib/lp/registry/model/product.py'
--- lib/lp/registry/model/product.py	2012-08-10 06:39:58 +0000
+++ lib/lp/registry/model/product.py	2012-08-16 04:34:22 +0000
@@ -92,7 +92,6 @@
 from lp.bugs.interfaces.bugsummary import IBugSummaryDimension
 from lp.bugs.interfaces.bugsupervisor import IHasBugSupervisor
 from lp.bugs.interfaces.bugtaskfilter import OrderedBugTask
-from lp.bugs.model.bug import BugSet
 from lp.bugs.model.bugtarget import (
     BugTargetBase,
     OfficialBugTagTargetMixin,
@@ -1387,8 +1386,6 @@
     def setBugSupervisor(self, bug_supervisor, user):
         """See `IHasBugSupervisor`."""
         self.bug_supervisor = bug_supervisor
-        if bug_supervisor is not None:
-            self.addBugSubscription(bug_supervisor, user)
 
     def composeCustomLanguageCodeMatch(self):
         """See `HasCustomLanguageCodesMixin`."""


Follow ups