launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #04632
[Merge] lp:~sinzui/launchpad/private-bug-1 into lp:launchpad
Curtis Hovey has proposed merging lp:~sinzui/launchpad/private-bug-1 into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~sinzui/launchpad/private-bug-1/+merge/71767
Replace doctests with unittests before working on bug 797697.
This branch removes inline and file doctests of classes I am changing in my next branch. These was one minor change test__normalizePath_distros was duplicated in the doctest. I think this was a typo because there was no test__normalizePath_products which I added.
LINT
lib/lp/bugs/mail/commands.py
lib/lp/bugs/mail/tests/test_commands.py
TEST
./bin/test -vv lp.bugs.mail.tests.test_commands
IMPLEMENTATION
Extracted doctests from classmethods.
lib/lp/bugs/mail/commands.py
lib/lp/bugs/mail/tests/test_commands.py
Replaces doctest for getBugTarget with unittest.
lib/lp/bugs/doc/bugs-email-affects-path.txt
lib/lp/bugs/mail/tests/test_commands.py
--
https://code.launchpad.net/~sinzui/launchpad/private-bug-1/+merge/71767
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~sinzui/launchpad/private-bug-1 into lp:launchpad.
=== removed file 'lib/lp/bugs/doc/bugs-email-affects-path.txt'
--- lib/lp/bugs/doc/bugs-email-affects-path.txt 2011-08-11 21:17:02 +0000
+++ lib/lp/bugs/doc/bugs-email-affects-path.txt 1970-01-01 00:00:00 +0000
@@ -1,221 +0,0 @@
-The 'affects' path in the Bugs e-mail interface
-===============================================
-
-When choosing which bugtask certain e-mail commands should modify, the
-'affects' command is used. It accepts a single argument, the name of the
-bug target the bugtask should target. The conversion of a name to an
-IBugTarget is done by the AffectsEmailCommand.getBugTarget method.
-
- >>> from lp.bugs.mail.commands import AffectsEmailCommand
-
-It accepts a single parameter, the name/path of the bugtarget. If a
-non-existant name/path is given an exception is raised with an
-appropriate error message
-
- >>> AffectsEmailCommand.getBugTarget('non-existant')
- Traceback (most recent call last):
- ...
- BugTargetNotFound: There is no project named 'non-existant'
- registered in Launchpad.
-
-The same exception is raised also when the name contains slashes:
-
- >>> AffectsEmailCommand.getBugTarget('non-existant/dapper/evolution')
- Traceback (most recent call last):
- ...
- BugTargetNotFound: There is no project named 'non-existant'
- registered in Launchpad.
-
-Even though a ProjectGroup is an IBugTarget, it's not possible to file bugs
-on it, so an exception with an appropriate error message is raised.
-
- >>> AffectsEmailCommand.getBugTarget('gnome')
- Traceback (most recent call last):
- ...
- BugTargetNotFound: gnome is a group of projects. To report a bug,
- you need to specify which of these projects the
- bug applies to: evolution, gnome-terminal, applets,
- netapplet, gnomebaker
-
-
-Getting a product
------------------
-
-Getting a product is done by specifying the name of the product.
-
- >>> from lp.registry.interfaces.product import IProduct
- >>> evolution = AffectsEmailCommand.getBugTarget('evolution')
- >>> IProduct.providedBy(evolution)
- True
- >>> evolution.name
- u'evolution'
-
-Inactive products are treated like non-existant ones.
-
- >>> from lp.registry.interfaces.product import IProductSet
- >>> unassigned = getUtility(IProductSet).getByName('unassigned')
- >>> unassigned.active
- False
- >>> AffectsEmailCommand.getBugTarget(unassigned.name)
- Traceback (most recent call last):
- ...
- BugTargetNotFound: There is no project named 'unassigned'
- registered in Launchpad.
-
-
-Getting a product series
-------------------------
-
-A product series is a part of a product, so both the product and series
-name have to be given, separated with a slash.
-
- >>> from lp.registry.interfaces.productseries import IProductSeries
- >>> evolution_trunk = AffectsEmailCommand.getBugTarget('evolution/trunk')
- >>> IProductSeries.providedBy(evolution_trunk)
- True
- >>> evolution_trunk.product.name
- u'evolution'
- >>> evolution_trunk.name
- u'trunk'
-
-If a non-existant product series is given, an exception is raised with
-an appropriate error message.
-
- >>> AffectsEmailCommand.getBugTarget('evolution/non-existant')
- Traceback (most recent call last):
- ...
- BugTargetNotFound: Evolution doesn't have a series named 'non-existant'.
-
-If some additional path component is included after the series, an
-exception with an appropriate error is raised.
-
- >>> AffectsEmailCommand.getBugTarget('evolution/trunk/foo')
- Traceback (most recent call last):
- ...
- BugTargetNotFound: Unexpected path components: foo
-
-
-Getting a distribution
-----------------------
-
-Getting a distribution is done by specifying the name of the
-distribution.
-
- >>> from lp.registry.interfaces.distribution import IDistribution
- >>> ubuntu = AffectsEmailCommand.getBugTarget('ubuntu')
- >>> IDistribution.providedBy(ubuntu)
- True
- >>> ubuntu.name
- u'ubuntu'
-
-
-Getting a source package in a distribution
-..........................................
-
-A distribution source package is a part of a distribution, so both the
-distribution and package name has to be specified when getting such a
-package.
-
- >>> from lp.registry.interfaces.distributionsourcepackage import (
- ... IDistributionSourcePackage)
- >>> ubuntu_evolution = AffectsEmailCommand.getBugTarget(
- ... 'ubuntu/evolution')
- >>> IDistributionSourcePackage.providedBy(ubuntu_evolution)
- True
- >>> ubuntu_evolution.name
- u'evolution'
- >>> ubuntu_evolution.distribution.name
- u'ubuntu'
-
-If a non-existant source package is given, an exception is raised with
-an appropriate error message.
-
- >>> AffectsEmailCommand.getBugTarget('ubuntu/non-existant')
- Traceback (most recent call last):
- ...
- BugTargetNotFound: Ubuntu doesn't have a series or source package
- named 'non-existant'.
-
-If some additional path component is included after the package, an
-exception with an appropriate error is raised.
-
- >>> AffectsEmailCommand.getBugTarget('ubuntu/evolution/foo')
- Traceback (most recent call last):
- ...
- BugTargetNotFound: Unexpected path components: foo
-
-
-Getting a distribution series
------------------------------
-
-A distribution series is a part of a distribution, so both the
-distribution and series has to be specified.
-
- >>> from lp.registry.interfaces.distroseries import IDistroSeries
- >>> ubuntu_warty = AffectsEmailCommand.getBugTarget('ubuntu/warty')
- >>> IDistroSeries.providedBy(ubuntu_warty)
- True
- >>> ubuntu_warty.name
- u'warty'
- >>> ubuntu_warty.distribution.name
- u'ubuntu'
-
-The path structure for a distribution series is the same as for a
-distribution source package, so the same error handling is used for
-both.
-
-
-Getting a source package in a distribution series
--------------------------------------------------
-
-When getting a source package within a distribution series, the source
-package is given after the distribution series.
-
- >>> from lp.registry.interfaces.sourcepackage import ISourcePackage
- >>> warty_evolution = AffectsEmailCommand.getBugTarget(
- ... 'ubuntu/warty/evolution')
- >>> ISourcePackage.providedBy(warty_evolution)
- True
- >>> warty_evolution.name
- u'evolution'
- >>> warty_evolution.distroseries.name
- u'warty'
- >>> warty_evolution.distroseries.distribution.name
- u'ubuntu'
-
-If a non-existant source package is given, an exception is raised with
-an appropriate error message.
-
- >>> AffectsEmailCommand.getBugTarget('ubuntu/warty/non-existant')
- Traceback (most recent call last):
- ...
- BugTargetNotFound: Warty doesn't have a series or source package
- named 'non-existant'.
-
-If some additional path component is included after the package, an
-exception with an appropriate error is raised.
-
- >>> AffectsEmailCommand.getBugTarget('ubuntu/warty/evolution/foo')
- Traceback (most recent call last):
- ...
- BugTargetNotFound: Unexpected path components: foo
-
-
-Error correction
-----------------
-
-Previously the affects path was resolved in the same way, except that
-either /distros/ or /products/ was needed in the front. Since people are
-used to this syntax, we accept it as well.
-
- >>> ubuntu = AffectsEmailCommand.getBugTarget('/distros/ubuntu')
- >>> IDistribution.providedBy(ubuntu)
- True
- >>> ubuntu.name
- u'ubuntu'
-
- >>> evolution = AffectsEmailCommand.getBugTarget('/products/evolution')
- >>> IProduct.providedBy(evolution)
- True
- >>> evolution.name
- u'evolution'
=== modified file 'lib/lp/bugs/mail/commands.py'
--- lib/lp/bugs/mail/commands.py 2011-08-12 14:39:51 +0000
+++ lib/lp/bugs/mail/commands.py 2011-08-16 19:26:24 +0000
@@ -428,15 +428,7 @@
"""Split the path part into two.
The first part is the part before any slash, and the other is
- the part behind the slash:
-
- >>> AffectsEmailCommand._splitPath('foo/bar/baz')
- ('foo', 'bar/baz')
-
- If No slash is in the path, the other part will be empty.
-
- >>> AffectsEmailCommand._splitPath('foo')
- ('foo', '')
+ the part behind the slash.
"""
if '/' not in path:
return path, ''
@@ -450,16 +442,6 @@
Previously the path had to start with either /distros/ or
/products/. Simply remove any such prefixes to stay backward
compatible.
-
- >>> AffectsEmailCommand._normalizePath('/distros/foo/bar')
- 'foo/bar'
- >>> AffectsEmailCommand._normalizePath('/distros/foo/bar')
- 'foo/bar'
-
- Also remove a starting slash, since that's a common mistake.
-
- >>> AffectsEmailCommand._normalizePath('/foo/bar')
- 'foo/bar'
"""
for prefix in ['/distros/', '/products/', '/']:
if path.startswith(prefix):
=== modified file 'lib/lp/bugs/mail/tests/test_commands.py'
--- lib/lp/bugs/mail/tests/test_commands.py 2011-08-12 14:36:25 +0000
+++ lib/lp/bugs/mail/tests/test_commands.py 2011-08-16 19:26:24 +0000
@@ -1,8 +1,145 @@
# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
-from doctest import DocTestSuite
-
-
-def test_suite():
- return DocTestSuite('lp.bugs.mail.commands')
+from canonical.testing.layers import DatabaseFunctionalLayer
+from lp.bugs.mail.commands import (
+ AffectsEmailCommand,
+ )
+from lp.services.mail.interfaces import BugTargetNotFound
+from lp.testing import (
+ login_celebrity,
+ login_person,
+ TestCaseWithFactory,
+ )
+
+
+class AffectsEmailCommandTestCase(TestCaseWithFactory):
+
+ layer = DatabaseFunctionalLayer
+
+ def test__splitPath_with_slashes(self):
+ self.assertEqual(
+ ('foo', 'bar/baz'), AffectsEmailCommand._splitPath('foo/bar/baz'))
+
+ def test__splitPath_no_slashes(self):
+ self.assertEqual(
+ ('foo', ''), AffectsEmailCommand._splitPath('foo'))
+
+ def test__normalizePath_leading_slash(self):
+ self.assertEqual(
+ 'foo/bar', AffectsEmailCommand._normalizePath('/foo/bar'))
+
+ def test__normalizePath_distros(self):
+ self.assertEqual(
+ 'foo/bar', AffectsEmailCommand._normalizePath('/distros/foo/bar'))
+
+ def test__normalizePath_products(self):
+ self.assertEqual(
+ 'foo/bar',
+ AffectsEmailCommand._normalizePath('/products/foo/bar'))
+
+ def test_getBugTarget_no_pillar_error(self):
+ message = "There is no project named 'fnord' registered in Launchpad."
+ self.assertRaisesWithContent(
+ BugTargetNotFound, message,
+ AffectsEmailCommand.getBugTarget, 'fnord')
+
+ def test_getBugTarget_project_group_error(self):
+ owner = self.factory.makePerson()
+ login_person(owner)
+ project_group = self.factory.makeProject(name='fnord', owner=owner)
+ project_1 = self.factory.makeProduct(name='pting', owner=owner)
+ project_1.project = project_group
+ project_2 = self.factory.makeProduct(name='snarf', owner=owner)
+ project_2.project = project_group
+ message = (
+ "fnord is a group of projects. To report a bug, you need to "
+ "specify which of these projects the bug applies to: "
+ "pting, snarf")
+ self.assertRaisesWithContent(
+ BugTargetNotFound, message,
+ AffectsEmailCommand.getBugTarget, 'fnord')
+
+ def test_getBugTarget_deactivated_project_error(self):
+ project = self.factory.makeProduct(name='fnord')
+ login_celebrity('admin')
+ project.active = False
+ message = "There is no project named 'fnord' registered in Launchpad."
+ self.assertRaisesWithContent(
+ BugTargetNotFound, message,
+ AffectsEmailCommand.getBugTarget, 'fnord')
+
+ def test_getBugTarget_project(self):
+ project = self.factory.makeProduct(name='fnord')
+ self.assertEqual(project, AffectsEmailCommand.getBugTarget('fnord'))
+
+ def test_getBugTarget_no_project_series_error(self):
+ self.factory.makeProduct(name='fnord')
+ message = "Fnord doesn't have a series named 'pting'."
+ self.assertRaisesWithContent(
+ BugTargetNotFound, message,
+ AffectsEmailCommand.getBugTarget, 'fnord/pting')
+
+ def test_getBugTarget_project_series(self):
+ project = self.factory.makeProduct(name='fnord')
+ series = self.factory.makeProductSeries(name='pting', product=project)
+ self.assertEqual(
+ series, AffectsEmailCommand.getBugTarget('fnord/pting'))
+
+ def test_getBugTarget_product_extra_path_error(self):
+ product = self.factory.makeProduct(name='fnord')
+ self.factory.makeProductSeries(name='pting', product=product)
+ message = "Unexpected path components: snarf"
+ self.assertRaisesWithContent(
+ BugTargetNotFound, message,
+ AffectsEmailCommand.getBugTarget, 'fnord/pting/snarf')
+
+ def test_getBugTarget_no_series_or_package_error(self):
+ self.factory.makeDistribution(name='fnord')
+ message = (
+ "Fnord doesn't have a series or source package named 'pting'.")
+ self.assertRaisesWithContent(
+ BugTargetNotFound, message,
+ AffectsEmailCommand.getBugTarget, 'fnord/pting')
+
+ def test_getBugTarget_distribution(self):
+ distribution = self.factory.makeDistribution(name='fnord')
+ self.assertEqual(
+ distribution, AffectsEmailCommand.getBugTarget('fnord'))
+
+ def test_getBugTarget_distroseries(self):
+ distribution = self.factory.makeDistribution(name='fnord')
+ series = self.factory.makeDistroSeries(
+ name='pting', distribution=distribution)
+ self.assertEqual(
+ series, AffectsEmailCommand.getBugTarget('fnord/pting'))
+
+ def test_getBugTarget_source_package(self):
+ distribution = self.factory.makeDistribution(name='fnord')
+ series = self.factory.makeDistroSeries(
+ name='pting', distribution=distribution)
+ package = self.factory.makeSourcePackage(
+ sourcepackagename='snarf', distroseries=series, publish=True)
+ self.assertEqual(
+ package, AffectsEmailCommand.getBugTarget('fnord/pting/snarf'))
+
+ def test_getBugTarget_distribution_source_package(self):
+ distribution = self.factory.makeDistribution(name='fnord')
+ series = self.factory.makeDistroSeries(
+ name='pting', distribution=distribution)
+ package = self.factory.makeSourcePackage(
+ sourcepackagename='snarf', distroseries=series, publish=True)
+ dsp = distribution.getSourcePackage(package.name)
+ self.assertEqual(
+ dsp, AffectsEmailCommand.getBugTarget('fnord/snarf'))
+
+ def test_getBugTarget_distribution_extra_path_error(self):
+ distribution = self.factory.makeDistribution(name='fnord')
+ series = self.factory.makeDistroSeries(
+ name='pting', distribution=distribution)
+ self.factory.makeSourcePackage(
+ sourcepackagename='snarf', distroseries=series, publish=True)
+ message = "Unexpected path components: thrup"
+ self.assertRaisesWithContent(
+ BugTargetNotFound, message,
+ AffectsEmailCommand.getBugTarget, 'fnord/pting/snarf/thrup')