← Back to team overview

launchpad-reviewers team mailing list archive

[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')