launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #02229
[Merge] lp:~sinzui/launchpad/search-and-rescue into lp:launchpad
Curtis Hovey has proposed merging lp:~sinzui/launchpad/search-and-rescue into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
#150945 Searching for "Exaile" on Bugs front page turns up empty
https://bugs.launchpad.net/bugs/150945
#404158 'There is no project in Launchpad named "Launchpad".'
https://bugs.launchpad.net/bugs/404158
Case insensitive lookups for pillars and packages.
Launchpad bug:
https://bugs.launchpad.net/bugs/150945
https://bugs.launchpad.net/bugs/404158
https://bugs.launchpad.net/bugs/192615
Pre-implementation: no one
Test command: ./bin/test -vv \
-t regexp
Bug #150945 [Searching for "Exaile" on Bugs front page turns up empty]
https://bugs.launchpad.net/ when searching for the project "Exaile",
I get told that there is no project by that name. /exaile does exist.
Bug #404158 [There is no project in Launchpad named "Launchpad"]
"Also affects other project" and entering "Launchpad"
into the box results in error. /launchpad does exist.
Bug #192615 [Reporting bug on capitalized package name returns an error]
Enter "Xorg" or "fireFox" for the package name. "There is 1 error". There
was no clue whatsoever what the error was or how I should fix it.
xorg and firefox packages do exist.
--------------------------------------------------------------------
RULES
Launchpad name fields are always lowercase. In huge vocabularies that
support getTermByToken(), that method could lower case the user entered
term.
Bug #150945 [Searching for "Exaile" on Bugs front page turns up empty]
* Lowercase the input before searching the product vocab.
Bug #404158 [There is no project in Launchpad named "Launchpad"]
* Lowercase the input before searching the pillar vocabs.
Bug #192615 [Reporting bug on capitalized package name returns an error]
* Lowercase the input before searching the package name vocabs.
QA
Bug #150945 [Searching for "Exaile" on Bugs front page turns up empty]
* Visit https://bugs.launchpad.net/
* Search in the Exaile project
* Verify bugs are listed
Bug #404158 [There is no project in Launchpad named "Launchpad"]
* Visit a non-launchpad bug.
* Choose "Also affects other project" and enter Launchpad
* Verify launchpad is matched.
Bug #192615 [Reporting bug on capitalized package name returns an error]
* Visit https://bugs.qastaging.launchpad.net/ubuntu/+filebug
* Enter Xorg
* Verify the bug is reported.
LINT
lib/lp/registry/vocabularies.py
lib/lp/registry/doc/vocabularies.txt
lib/lp/registry/tests/test_pillar_vocabularies.py
lib/lp/registry/tests/test_product_vocabularies.py
lib/lp/registry/tests/test_sourcepackagename_vocabulary.py
lib/lp/soyuz/model/binaryandsourcepackagename.py
lib/lp/soyuz/tests/test_binaryandsourcepackagename.py
IMPLEMENTATION
Bug #150945 [Searching for "Exaile" on Bugs front page turns up empty]
Updated getTermByToken in the base vocab. Added tests for the pillar
vocabularies.
lib/lp/registry/vocabularies.py
lib/lp/registry/tests/test_pillar_vocabularies.py
Bug #404158 [There is no project in Launchpad named "Launchpad"]
Lowercased the token entered by the user in getTermByToken. Updated test
coverage, and deleted redundant tests.
lib/lp/registry/vocabularies.py
lib/lp/registry/doc/vocabularies.txt
lib/lp/registry/tests/test_product_vocabularies.py
Bug #192615 [Reporting bug on capitalized package name returns an error]
Updated getTermByToken in the two vocabs and added tests.
lib/lp/registry/vocabularies.py
lib/lp/registry/tests/test_sourcepackagename_vocabulary.py
lib/lp/soyuz/model/binaryandsourcepackagename.py
lib/lp/soyuz/tests/test_binaryandsourcepackagename.py
--
https://code.launchpad.net/~sinzui/launchpad/search-and-rescue/+merge/44422
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~sinzui/launchpad/search-and-rescue into lp:launchpad.
=== modified file 'lib/lp/registry/doc/vocabularies.txt'
--- lib/lp/registry/doc/vocabularies.txt 2010-12-03 00:03:15 +0000
+++ lib/lp/registry/doc/vocabularies.txt 2010-12-22 03:33:00 +0000
@@ -544,47 +544,6 @@
>>> flush_database_updates()
-=== ProductVocabulary ===
-
-The list of selectable products. Results are ordered by displayname.
-
- >>> product_vocabulary = get_naked_vocab(None, "Product")
- >>> product_vocabulary.displayname
- 'Select a project'
-
- >>> list(product_vocabulary.search(None))
- []
- >>> gnome_products = product_vocabulary.search("gnome")
- >>> l = [product_term.title for product_term in gnome_products]
- >>> l[:2]
- [u'The Evolution Groupware Application', u'The GNOME Terminal Emulator']
- >>> l[2:]
- [u'The Gnome Panel Applets', u'Gnome Baker']
- >>> arch = product_vocabulary.getTermByToken("arch-mirrors")
- >>> arch.title
- u'Arch archive mirrors'
-
-The ProductVocabulary does not list inactive products.
-
- >>> arch.value in product_vocabulary
- True
-
- >>> arch_product = product_set['arch-mirrors']
- >>> arch_product in product_vocabulary
- True
-
- >>> [t.title for t in product_vocabulary.search('arch and archive')]
- [u'Arch archive mirrors']
- >>> arch_product.active = False
- >>> flush_database_updates()
- >>> arch_product in product_vocabulary
- False
- >>> [t.title for t in product_vocabulary.search('arch and archive')]
- []
- >>> arch_product.active = True
- >>> flush_database_updates()
-
-
=== ProductReleaseVocabulary ===
The list of selectable products releases.
=== added file 'lib/lp/registry/tests/test_pillar_vocabularies.py'
--- lib/lp/registry/tests/test_pillar_vocabularies.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/tests/test_pillar_vocabularies.py 2010-12-22 03:33:00 +0000
@@ -0,0 +1,119 @@
+# Copyright 2010 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Test the pillar vocabularies."""
+
+__metaclass__ = type
+
+from canonical.testing.layers import DatabaseFunctionalLayer
+from lp.registry.vocabularies import (
+ DistributionOrProductVocabulary,
+ DistributionOrProductOrProjectGroupVocabulary,
+ PillarVocabularyBase,
+ )
+from lp.testing import (
+ celebrity_logged_in,
+ TestCaseWithFactory,
+ )
+
+
+class TestPillarVocabularyBase(TestCaseWithFactory):
+ """Test that the ProductVocabulary behaves as expected."""
+ layer = DatabaseFunctionalLayer
+
+ def setUp(self):
+ super(TestPillarVocabularyBase, self).setUp()
+ self.vocabulary = PillarVocabularyBase()
+ self.product = self.factory.makeProduct(name='orchid-snark')
+ self.distribution = self.factory.makeDistribution(name='zebra-snark')
+ self.project_group = self.factory.makeProject(name='apple-snark')
+
+ def test_toTerm(self):
+ # Product terms are composed of title, name, and the object.
+ term = self.vocabulary.toTerm(self.product)
+ title = '%s (Product)' % self.product.title
+ self.assertEqual(title, term.title)
+ self.assertEqual(self.product.name, term.token)
+ self.assertEqual(self.product, term.value)
+
+ def test_getTermByToken(self):
+ # Tokens are case insentive because the product name is lowercase.
+ term = self.vocabulary.getTermByToken('ORCHID-SNARK')
+ self.assertEqual(self.product, term.value)
+
+ def test_getTermByToken_LookupError(self):
+ # getTermByToken() raises a LookupError when no match is found.
+ self.assertRaises(
+ LookupError,
+ self.vocabulary.getTermByToken, 'does-notexist')
+
+ def test_order_by_name(self):
+ # Results are ordered by name.
+ terms = self.vocabulary.searchForTerms('snark')
+ result = [term.value for term in terms]
+ self.assertEqual(
+ [self.project_group, self.product, self.distribution], result)
+
+
+class TestDistributionOrProductVocabulary(TestCaseWithFactory):
+ """Test that the ProductVocabulary behaves as expected."""
+ layer = DatabaseFunctionalLayer
+
+ def setUp(self):
+ super(TestDistributionOrProductVocabulary, self).setUp()
+ self.vocabulary = DistributionOrProductVocabulary()
+ self.product = self.factory.makeProduct(name='orchid-snark')
+ self.distribution = self.factory.makeDistribution(name='zebra-snark')
+
+ def test_inactive_products_are_discluded(self):
+ # Inactive product are not in the vocabulary.
+ with celebrity_logged_in('registry_experts'):
+ self.product.active = False
+ terms = self.vocabulary.searchForTerms('snark')
+ result = [term.value for term in terms]
+ self.assertEqual([self.distribution], result)
+ self.assertFalse(self.product in self.vocabulary)
+
+ def test_project_groups_are_discluded(self):
+ # Project groups are not in the vocabulary.
+ project_group = self.factory.makeProject(name='apple-snark')
+ terms = self.vocabulary.searchForTerms('snark')
+ result = [term.value for term in terms]
+ self.assertEqual([self.product, self.distribution], result)
+ self.assertFalse(project_group in self.vocabulary)
+
+
+class TestDistributionOrProductOrProjectGroupVocabulary(TestCaseWithFactory):
+ """Test for DistributionOrProductOrProjectGroupVocabulary."""
+ layer = DatabaseFunctionalLayer
+
+ def setUp(self):
+ super(TestDistributionOrProductOrProjectGroupVocabulary, self).setUp()
+ self.vocabulary = DistributionOrProductOrProjectGroupVocabulary()
+ self.product = self.factory.makeProduct(name='orchid-snark')
+ self.distribution = self.factory.makeDistribution(name='zebra-snark')
+ self.project_group = self.factory.makeProject(name='apple-snark')
+
+ def test_contains_all_pillars_active(self):
+ # All active products, project groups and distributions are included.
+ self.assertTrue(self.product in self.vocabulary)
+ self.assertTrue(self.distribution in self.vocabulary)
+ self.assertTrue(self.project_group in self.vocabulary)
+
+ def test_inactive_products_are_discluded(self):
+ # Inactive porduct are not in the vocabulary.
+ with celebrity_logged_in('registry_experts'):
+ self.product.active = False
+ terms = self.vocabulary.searchForTerms('snark')
+ result = [term.value for term in terms]
+ self.assertEqual([self.project_group, self.distribution], result)
+ self.assertFalse(self.product in self.vocabulary)
+
+ def test_inactive_product_groups_are_discluded(self):
+ # Inactive porject groups are not in the vocabulary.
+ with celebrity_logged_in('registry_experts'):
+ self.project_group.active = False
+ terms = self.vocabulary.searchForTerms('snark')
+ result = [term.value for term in terms]
+ self.assertEqual([self.product, self.distribution], result)
+ self.assertFalse(self.project_group in self.vocabulary)
=== added file 'lib/lp/registry/tests/test_product_vocabularies.py'
--- lib/lp/registry/tests/test_product_vocabularies.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/tests/test_product_vocabularies.py 2010-12-22 03:33:00 +0000
@@ -0,0 +1,64 @@
+# Copyright 2010 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Test the product vocabularies."""
+
+__metaclass__ = type
+
+from canonical.testing.layers import DatabaseFunctionalLayer
+from lp.registry.vocabularies import ProductVocabulary
+from lp.testing import (
+ celebrity_logged_in,
+ TestCaseWithFactory,
+ )
+
+
+class TestProductVocabulary(TestCaseWithFactory):
+ """Test that the ProductVocabulary behaves as expected."""
+ layer = DatabaseFunctionalLayer
+
+ def setUp(self):
+ super(TestProductVocabulary, self).setUp()
+ self.vocabulary = ProductVocabulary()
+ self.product = self.factory.makeProduct(
+ name='bedbugs', displayname='BedBugs')
+
+ def test_toTerm(self):
+ # Product terms are composed of title, name, and the object.
+ term = self.vocabulary.toTerm(self.product)
+ self.assertEqual(self.product.title, term.title)
+ self.assertEqual(self.product.name, term.token)
+ self.assertEqual(self.product, term.value)
+
+ def test_getTermByToken(self):
+ # Tokens are case insentive because the product name is lowercase.
+ term = self.vocabulary.getTermByToken('BedBUGs')
+ self.assertEqual(self.product, term.value)
+
+ def test_getTermByToken_LookupError(self):
+ # getTermByToken() raises a LookupError when no match is found.
+ self.assertRaises(
+ LookupError,
+ self.vocabulary.getTermByToken, 'does-notexist')
+
+ def test_search_in_any_case(self):
+ # Search is case insensitive and uses stem rules.
+ result = self.vocabulary.search('BEDBUG')
+ self.assertEqual([self.product], list(result))
+
+ def test_order_by_displayname(self):
+ # Results are ordered by displayname.
+ z_product = self.factory.makeProduct(
+ name='mule', displayname='Bed zebra')
+ a_product = self.factory.makeProduct(
+ name='orange', displayname='Bed apple')
+ result = self.vocabulary.search('bed')
+ self.assertEqual(
+ [a_product, z_product, self.product], list(result))
+
+ def test_inactive_products_are_discluded(self):
+ # Inactive products are not in the vocabulary.
+ with celebrity_logged_in('registry_experts'):
+ self.product.active = False
+ result = self.vocabulary.search('bedbugs')
+ self.assertEqual([], list(result))
=== added file 'lib/lp/registry/tests/test_sourcepackagename_vocabulary.py'
--- lib/lp/registry/tests/test_sourcepackagename_vocabulary.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/tests/test_sourcepackagename_vocabulary.py 2010-12-22 03:33:00 +0000
@@ -0,0 +1,38 @@
+# Copyright 2010 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Test the source package name vocabularies."""
+
+__metaclass__ = type
+
+from canonical.testing.layers import DatabaseFunctionalLayer
+from lp.registry.vocabularies import SourcePackageNameVocabulary
+from lp.testing import TestCaseWithFactory
+
+
+class TestSourcePackageNameVocabulary(TestCaseWithFactory):
+ """Test that the ProductVocabulary behaves as expected."""
+ layer = DatabaseFunctionalLayer
+
+ def setUp(self):
+ super(TestSourcePackageNameVocabulary, self).setUp()
+ self.vocabulary = SourcePackageNameVocabulary()
+ self.spn = self.factory.makeSourcePackageName(name='bedbugs')
+
+ def test_toTerm(self):
+ # Source package name terms are composed of name, and the spn.
+ term = self.vocabulary.toTerm(self.spn)
+ self.assertEqual(self.spn.name, term.title)
+ self.assertEqual(self.spn.name, term.token)
+ self.assertEqual(self.spn, term.value)
+
+ def test_getTermByToken(self):
+ # Tokens are case insentive because the name is lowercase.
+ term = self.vocabulary.getTermByToken('BedBUGs')
+ self.assertEqual(self.spn, term.value)
+
+ def test_getTermByToken_LookupError(self):
+ # getTermByToken() raises a LookupError when no match is found.
+ self.assertRaises(
+ LookupError,
+ self.vocabulary.getTermByToken, 'does-notexist')
=== modified file 'lib/lp/registry/vocabularies.py'
--- lib/lp/registry/vocabularies.py 2010-12-02 19:45:50 +0000
+++ lib/lp/registry/vocabularies.py 2010-12-22 03:33:00 +0000
@@ -245,6 +245,8 @@
def getTermByToken(self, token):
"""See `IVocabularyTokenized`."""
+ # Product names are always lowercase.
+ token = token.lower()
product = self._table.selectOneBy(name=token, active=True)
if product is None:
raise LookupError(token)
@@ -1488,6 +1490,12 @@
return SimpleTerm(obj, obj.name, title)
+ def getTermByToken(self, token):
+ """See `IVocabularyTokenized`."""
+ # Pillar names are always lowercase.
+ return super(PillarVocabularyBase, self).getTermByToken(
+ token.lower())
+
def __contains__(self, obj):
raise NotImplementedError
@@ -1568,3 +1576,9 @@
_table = SourcePackageName
_orderBy = 'name'
iterator = SourcePackageNameIterator
+
+ def getTermByToken(self, token):
+ """See `IVocabularyTokenized`."""
+ # package names are always lowercase.
+ return super(SourcePackageNameVocabulary, self).getTermByToken(
+ token.lower())
=== modified file 'lib/lp/soyuz/model/binaryandsourcepackagename.py'
--- lib/lp/soyuz/model/binaryandsourcepackagename.py 2010-10-03 15:30:06 +0000
+++ lib/lp/soyuz/model/binaryandsourcepackagename.py 2010-12-22 03:33:00 +0000
@@ -71,3 +71,9 @@
displayname = 'Select a Package'
_orderBy = 'name'
iterator = BinaryAndSourcePackageNameIterator
+
+ def getTermByToken(self, token):
+ """See `IVocabularyTokenized`."""
+ # package names are always lowercase.
+ super_class = super(BinaryAndSourcePackageNameVocabulary, self)
+ return super_class.getTermByToken(token.lower())
=== added file 'lib/lp/soyuz/tests/test_binaryandsourcepackagename.py'
--- lib/lp/soyuz/tests/test_binaryandsourcepackagename.py 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/tests/test_binaryandsourcepackagename.py 2010-12-22 03:33:00 +0000
@@ -0,0 +1,46 @@
+# Copyright 2010 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Test the binary and source package name vocabularies."""
+
+__metaclass__ = type
+
+from storm.store import Store
+
+from canonical.testing.layers import DatabaseFunctionalLayer
+from lp.soyuz.model.binaryandsourcepackagename import (
+ BinaryAndSourcePackageName,
+ BinaryAndSourcePackageNameVocabulary,
+ )
+from lp.testing import TestCaseWithFactory
+
+
+class TestBinaryAndSourcePackageNameVocabulary(TestCaseWithFactory):
+ """Test that the ProductVocabulary behaves as expected."""
+ layer = DatabaseFunctionalLayer
+
+ def setUp(self):
+ super(TestBinaryAndSourcePackageNameVocabulary, self).setUp()
+ self.vocabulary = BinaryAndSourcePackageNameVocabulary()
+ spn = self.factory.makeSourcePackageName(name='bedbugs')
+ self.bspn = Store.of(spn).find(
+ BinaryAndSourcePackageName, name=spn.name).one()
+
+ def test_toTerm(self):
+ # Binary and source package name terms are composed of name,
+ # and the bspn.
+ term = self.vocabulary.toTerm(self.bspn)
+ self.assertEqual(self.bspn.name, term.title)
+ self.assertEqual(self.bspn.name, term.token)
+ self.assertEqual(self.bspn, term.value)
+
+ def test_getTermByToken(self):
+ # Tokens are case insentive because the name is lowercase.
+ term = self.vocabulary.getTermByToken('BedBUGs')
+ self.assertEqual(self.bspn, term.value)
+
+ def test_getTermByToken_LookupError(self):
+ # getTermByToken() raises a LookupError when no match is found.
+ self.assertRaises(
+ LookupError,
+ self.vocabulary.getTermByToken, 'does-notexist')