← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~jtv/launchpad/pre-bug-791204-getPackageUploads-name-filter into lp:launchpad

 

Jeroen T. Vermeulen has proposed merging lp:~jtv/launchpad/pre-bug-791204-getPackageUploads-name-filter into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #791204 in Launchpad itself: "Fix the 'queue' script tool to work with copy-type PackageUploads"
  https://bugs.launchpad.net/launchpad/+bug/791204

For more details, see:
https://code.launchpad.net/~jtv/launchpad/pre-bug-791204-getPackageUploads-name-filter/+merge/64345

= Summary =

PackageUploadSet.getAll and its callsite DistroSeries.getPackageUploads are a replacement for the older (and pre-Storm) DistroSeries.getQueueItems, but not a complete one yet.  In order to replace the callsite in the +queue view, it needs a name filter.  (getQueueItems isn't good enough there because, out of the various kinds of PackageUpload, it won't return the new ones that have PackageCopyJobs).


== Proposed fix ==

I'm adding the name filter here, cribbing the functionality off getQueueItems.  I also added unit tests for the function, in the course of which I discovered that the "archive" argument isn't actually used.  There were doctests, but those by nature operate at too high a level to be exhaustive.


== Pre-implementation notes ==

This is part of a series of interacting branches between Julian and myself.  The larger goal is to get the queue script and the +queue page working on PackageUploads with PackageCopyJobs.


== Implementation details ==

The extended search query in getAll left-joins a bunch of tables.  I hope the query won't be too wide to be efficient; practical experience will have to tell.  The code could get quite a bit more complicated if it needed optimizing against that, so I invoke Knuth's Law.

For testing I used what I hope is a sensible pattern for this kind of multi-constrained search: each filter is tested in isolation, by establishing one match and one non-match.  The filtering mechanics for this new filter are complex enough that I need to test it separately against each type of PackageUpload.  This worked well for test-driven development: I laid out most of the requirements as tests, then added code to satisfy each of them in turn.  Then I discovered some of the requirements weren't quite what they seemed (the name match is a prefix match, not a full name match; escaping of the filter might be an issue, custom uploads also needed matching) so I went through a few more "test, implement" iterations.


== Tests ==

{{{
./bin/tests -vvc lp.soyuz.tests.test_packageupload -t UploadSet
}}}


== Demo and Q/A ==

I don't see any callsites yet, so demo and Q/A won't be meaningful until the actual fix for bug 791204 lands.


= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/soyuz/model/queue.py
  lib/lp/soyuz/interfaces/queue.py
  lib/lp/registry/interfaces/distroseries.py
  lib/lp/soyuz/tests/test_packageupload.py
  lib/lp/registry/model/distroseries.py
-- 
https://code.launchpad.net/~jtv/launchpad/pre-bug-791204-getPackageUploads-name-filter/+merge/64345
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jtv/launchpad/pre-bug-791204-getPackageUploads-name-filter into lp:launchpad.
=== modified file 'lib/lp/registry/interfaces/distroseries.py'
--- lib/lp/registry/interfaces/distroseries.py	2011-06-10 15:15:48 +0000
+++ lib/lp/registry/interfaces/distroseries.py	2011-06-12 18:49:26 +0000
@@ -533,8 +533,8 @@
     # Really IPackageUpload, patched in _schema_circular_imports.py
     @operation_returns_collection_of(Interface)
     @export_read_operation()
-    def getPackageUploads(created_since_date, status, archive, pocket,
-                          custom_type):
+    def getPackageUploads(created_since_date=None, status=None, archive=None,
+                          pocket=None, custom_type=None, name_filter=None):
         """Get package upload records for this distribution series.
 
         :param created_since_date: If specified, only returns items uploaded
@@ -543,6 +543,8 @@
         :param archive: Filter results for this `IArchive`
         :param pocket: Filter results by this `PackagePublishingPocket`
         :param custom_type: Filter results by this `PackageUploadCustomFormat`
+        :param name_filter: Filter results by this file name or package name
+            prefix.
         :return: A result set containing `IPackageUpload`
         """
 

=== modified file 'lib/lp/registry/model/distroseries.py'
--- lib/lp/registry/model/distroseries.py	2011-06-10 20:00:08 +0000
+++ lib/lp/registry/model/distroseries.py	2011-06-12 18:49:26 +0000
@@ -90,7 +90,6 @@
     BugTargetBase,
     HasBugHeatMixin,
     )
-from lp.bugs.model.bugtask import BugTask
 from lp.bugs.model.structuralsubscription import (
     StructuralSubscriptionTargetMixin,
     )
@@ -847,7 +846,8 @@
         """See `IHasBugs`."""
         return get_bug_tags("BugTask.distroseries = %s" % sqlvalues(self))
 
-    def getUsedBugTagsWithOpenCounts(self, user, tag_limit=0, include_tags=None):
+    def getUsedBugTagsWithOpenCounts(self, user, tag_limit=0,
+                                     include_tags=None):
         """See IBugTarget."""
         # Circular fail.
         from lp.bugs.model.bugsummary import BugSummary
@@ -1648,10 +1648,12 @@
         return PackageUploadQueue(self, state)
 
     def getPackageUploads(self, created_since_date=None, status=None,
-                          archive=None, pocket=None, custom_type=None):
+                          archive=None, pocket=None, custom_type=None,
+                          name_filter=None):
         """See `IDistroSeries`."""
         return getUtility(IPackageUploadSet).getAll(
-            self, created_since_date, status, archive, pocket, custom_type)
+            self, created_since_date, status, archive, pocket, custom_type,
+            name_filter=name_filter)
 
     def getQueueItems(self, status=None, name=None, version=None,
                       exact_match=False, pocket=None, archive=None):

=== modified file 'lib/lp/soyuz/interfaces/queue.py'
--- lib/lp/soyuz/interfaces/queue.py	2011-06-06 11:49:08 +0000
+++ lib/lp/soyuz/interfaces/queue.py	2011-06-12 18:49:26 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 # pylint: disable-msg=E0211,E0213
@@ -356,7 +356,6 @@
             title=_("ID"), required=True, readonly=True,
             )
 
-
     packageupload = Int(
             title=_("PackageUpload"), required=True,
             readonly=False,
@@ -393,7 +392,6 @@
             title=_("ID"), required=True, readonly=True,
             )
 
-
     packageupload = Int(
             title=_("PackageUpload"), required=True,
             readonly=False,
@@ -604,7 +602,8 @@
         """
 
     def getAll(distroseries, created_since_date=None, status=None,
-               archive=None, pocket=None, custom_type=None):
+               archive=None, pocket=None, custom_type=None,
+               name_filter=None):
         """Get package upload records for a series with optional filtering.
 
         :param created_since_date: If specified, only returns items uploaded
@@ -613,6 +612,10 @@
         :param archive: Filter results for this `IArchive`
         :param pocket: Filter results by this `PackagePublishingPocket`
         :param custom_type: Filter results by this `PackageUploadCustomFormat`
+        :param name_filter: Filter results by this package or file name
+            prefix.  Passing 'a' will pass a source upload for source package
+            'ax', a build upload for binary package 'aardvark', a custom
+            upload of file 'app', and so on.
         :return: A result set containing `IPackageUpload`s
         """
 

=== modified file 'lib/lp/soyuz/model/queue.py'
--- lib/lp/soyuz/model/queue.py	2011-06-10 11:52:07 +0000
+++ lib/lp/soyuz/model/queue.py	2011-06-12 18:49:26 +0000
@@ -23,7 +23,12 @@
     SQLMultipleJoin,
     SQLObjectNotFound,
     )
+from storm.expr import (
+    Coalesce,
+    LeftJoin,
+    )
 from storm.locals import (
+    And,
     Desc,
     Int,
     Join,
@@ -44,6 +49,7 @@
     SQLBase,
     sqlvalues,
     )
+from canonical.launchpad.database.librarian import LibraryFileAlias
 from canonical.launchpad.interfaces.lpstorm import (
     IMasterStore,
     IStore,
@@ -58,6 +64,7 @@
 from lp.archivepublisher.customupload import CustomUploadError
 from lp.archiveuploader.tagfiles import parse_tagfile_content
 from lp.registry.interfaces.pocket import PackagePublishingPocket
+from lp.registry.model.sourcepackagename import SourcePackageName
 from lp.services.mail.signedmessage import strip_pgp_signature
 from lp.services.propertycache import cachedproperty
 from lp.soyuz.adapters.notification import notify
@@ -83,6 +90,8 @@
     QueueSourceAcceptError,
     QueueStateWriteProtectedError,
     )
+from lp.soyuz.model.binarypackagename import BinaryPackageName
+from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
 from lp.soyuz.pas import BuildDaemonPackagesArchSpecific
 from lp.soyuz.scripts.processaccepted import close_bugs_for_queue_item
 
@@ -1259,7 +1268,6 @@
         """See `IPackageUploadSet`."""
         # Avoiding circular imports.
         from lp.registry.model.distroseries import DistroSeries
-        from lp.registry.model.sourcepackagename import SourcePackageName
         from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
 
         store = IMasterStore(PackageUpload)
@@ -1307,7 +1315,7 @@
         return PackageUpload.select(query).count()
 
     def getAll(self, distroseries, created_since_date=None, status=None,
-               archive=None, pocket=None, custom_type=None):
+               archive=None, pocket=None, custom_type=None, name_filter=None):
         """See `IPackageUploadSet`."""
         # XXX Julian 2009-07-02 bug=394645
         # This method is an incremental deprecation of
@@ -1315,6 +1323,14 @@
         # using Storm queries instead of SQLObject, but not everything
         # is implemented yet.  When it is, this comment and the old
         # method can be removed and call sites updated to use this one.
+
+        # XXX 2011-06-11 JeroenVermeulen bug=795651: The "archive"
+        # argument is currently ignored.  Not sure why.
+
+        # Avoid circular imports.
+        from lp.soyuz.model.packagecopyjob import PackageCopyJob
+        from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
+
         store = Store.of(distroseries)
 
         def dbitem_tuple(item_or_list):
@@ -1323,38 +1339,94 @@
             else:
                 return tuple(item_or_list)
 
-        timestamp_query_clause = ()
+        def compose_name_match(column):
+            """Match a query column to `name_filter`."""
+            return column.startswith(unicode(name_filter))
+
+        joins = [PackageUpload]
+        clauses = []
         if created_since_date is not None:
-            timestamp_query_clause = (
-                PackageUpload.date_created > created_since_date,)
+            clauses.append(PackageUpload.date_created > created_since_date)
 
-        status_query_clause = ()
         if status is not None:
             status = dbitem_tuple(status)
-            status_query_clause = (PackageUpload.status.is_in(status),)
+            clauses.append(PackageUpload.status.is_in(status))
 
         archives = distroseries.distribution.getArchiveIDList(archive)
-        archive_query_clause = (PackageUpload.archiveID.is_in(archives),)
+        clauses.append(PackageUpload.archiveID.is_in(archives))
 
-        pocket_query_clause = ()
         if pocket is not None:
             pocket = dbitem_tuple(pocket)
-            pocket_query_clause = (PackageUpload.pocket.is_in(pocket),)
+            clauses.append(PackageUpload.pocket.is_in(pocket))
 
-        custom_type_query_clause = ()
         if custom_type is not None:
             custom_type = dbitem_tuple(custom_type)
-            custom_type_query_clause = (
+            joins.append(Join(PackageUploadCustom, And(
                 PackageUpload.id == PackageUploadCustom.packageuploadID,
-                PackageUploadCustom.customformat.is_in(custom_type))
-
-        return store.find(
+                PackageUploadCustom.customformat.is_in(custom_type))))
+
+        if name_filter is not None and name_filter != '':
+            # Join in any attached PackageCopyJob with the right
+            # package name.
+            joins.append(LeftJoin(
+                PackageCopyJob, And(
+                    PackageCopyJob.id == PackageUpload.package_copy_job_id,
+                    compose_name_match(PackageCopyJob.package_name))))
+
+            # Join in any attached PackageUploadSource with attached
+            # SourcePackageRelease with the right SourcePackageName.
+            joins.append(LeftJoin(
+                SourcePackageName,
+                compose_name_match(SourcePackageName.name)))
+            joins.append(LeftJoin(
+                PackageUploadSource,
+                PackageUploadSource.packageuploadID == PackageUpload.id))
+            joins.append(LeftJoin(
+                SourcePackageRelease, And(
+                    SourcePackageRelease.id ==
+                        PackageUploadSource.sourcepackagereleaseID,
+                    SourcePackageRelease.sourcepackagenameID ==
+                        SourcePackageName.id)))
+
+            # Join in any attached PackageUploadBuild with attached
+            # BinaryPackageRelease with the right BinaryPackageName.
+            joins.append(LeftJoin(
+                BinaryPackageName,
+                compose_name_match(BinaryPackageName.name)))
+            joins.append(LeftJoin(
+                PackageUploadBuild,
+                PackageUploadBuild.packageuploadID == PackageUpload.id))
+            joins.append(LeftJoin(
+                BinaryPackageRelease, And(
+                    BinaryPackageRelease.buildID ==
+                        PackageUploadBuild.buildID,
+                    BinaryPackageRelease.binarypackagenameID ==
+                        BinaryPackageName.id)))
+
+            # Join in any attached PackageUploadCustom with attached
+            # LibraryFileAlias with the right filename.
+            joins.append(LeftJoin(
+                PackageUploadCustom,
+                PackageUploadCustom.packageuploadID == PackageUpload.id))
+            joins.append(LeftJoin(
+                LibraryFileAlias, And(
+                    LibraryFileAlias.id ==
+                        PackageUploadCustom.libraryfilealiasID,
+                    compose_name_match(LibraryFileAlias.filename))))
+
+            # One of these attached items (for that package we're
+            # looking for) must exist.
+            clauses.append(
+                Coalesce(
+                    PackageCopyJob.id, SourcePackageRelease.id,
+                    BinaryPackageRelease.id, LibraryFileAlias.id) != None)
+
+        query = store.using(*joins).find(
             PackageUpload,
             PackageUpload.distroseries == distroseries,
-            *(status_query_clause + archive_query_clause +
-              pocket_query_clause + timestamp_query_clause +
-              custom_type_query_clause)).order_by(
-                  Desc(PackageUpload.id)).config(distinct=True)
+            *clauses)
+        query = query.order_by(Desc(PackageUpload.id))
+        return query.config(distinct=True)
 
     def getBuildByBuildIDs(self, build_ids):
         """See `IPackageUploadSet`."""
@@ -1380,4 +1452,3 @@
         return IStore(PackageUpload).find(
             PackageUpload,
             PackageUpload.package_copy_job_id.is_in(pcj_ids))
-

=== modified file 'lib/lp/soyuz/tests/test_packageupload.py'
--- lib/lp/soyuz/tests/test_packageupload.py	2011-06-06 11:49:08 +0000
+++ lib/lp/soyuz/tests/test_packageupload.py	2011-06-12 18:49:26 +0000
@@ -1,8 +1,9 @@
-# Copyright 2009-2010 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Test Build features."""
 
+from datetime import timedelta
 from email import message_from_string
 import os
 import shutil
@@ -13,6 +14,7 @@
 from zope.security.proxy import removeSecurityProxy
 
 from canonical.config import config
+from canonical.launchpad.webapp.testing import verifyObject
 from canonical.testing.layers import LaunchpadZopelessLayer
 from lp.archiveuploader.tests import datadir
 from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfigSet
@@ -21,7 +23,10 @@
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.interfaces.series import SeriesStatus
 from lp.services.log.logger import BufferLogger
-from lp.services.job.interfaces.job import JobStatus
+from lp.services.job.interfaces.job import (
+    JobStatus,
+    SuspendJobException,
+    )
 from lp.services.mail import stub
 from lp.soyuz.enums import (
     ArchivePurpose,
@@ -352,6 +357,243 @@
         self.assertEqual("partner", pub.archive.name)
 
 
+class TestPackageUploadSet(TestCaseWithFactory):
+    """Unit tests for `PackageUploadSet`."""
+
+    layer = LaunchpadZopelessLayer
+
+    def makeSourcePackageUpload(self, distroseries, sourcepackagename=None):
+        upload = self.factory.makePackageUpload(
+            distroseries=distroseries, archive=distroseries.main_archive)
+        upload.addSource(self.factory.makeSourcePackageRelease(
+            sourcepackagename=sourcepackagename))
+        return upload
+
+    def makeBuildPackageUpload(self, distroseries, binarypackagename=None):
+        upload = self.factory.makePackageUpload(
+            distroseries=distroseries, archive=distroseries.main_archive)
+        build = self.factory.makeBinaryPackageBuild()
+        upload.addBuild(build)
+        self.factory.makeBinaryPackageRelease(
+            binarypackagename=binarypackagename, build=build)
+        return upload
+
+    def makeCustomPackageUpload(self, distroseries, custom_type=None,
+                                filename=None):
+        if custom_type is None:
+            custom_type = PackageUploadCustomFormat.DEBIAN_INSTALLER
+        upload = self.factory.makePackageUpload(
+            distroseries=distroseries, archive=distroseries.main_archive)
+        file_alias = self.factory.makeLibraryFileAlias(filename=filename)
+        upload.addCustom(file_alias, custom_type)
+        return upload
+
+    def makeCopyJobPackageUpload(self, distroseries, sourcepackagename=None):
+        spph = self.factory.makeSourcePackagePublishingHistory(
+            sourcepackagename=sourcepackagename)
+        spr = spph.sourcepackagerelease
+        job = self.factory.makePlainPackageCopyJob(
+            package_name=spr.sourcepackagename.name,
+            package_version=spr.version,
+            source_archive=spph.archive,
+            target_archive=distroseries.main_archive,
+            target_distroseries=distroseries)
+        self.layer.txn.commit()
+        try:
+            job.run()
+        except SuspendJobException:
+            # Expected exception.
+            job.suspend()
+        upload_set = getUtility(IPackageUploadSet)
+        return upload_set.getByPackageCopyJobIDs([job.id]).one()
+
+    def test_PackageUploadSet_implements_IPackageUploadSet(self):
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertTrue(verifyObject(IPackageUploadSet, upload_set))
+
+    def test_getAll_returns_source_upload(self):
+        distroseries = self.factory.makeDistroSeries()
+        upload = self.makeSourcePackageUpload(distroseries)
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual([upload], upload_set.getAll(distroseries))
+
+    def test_getAll_returns_build_upload(self):
+        distroseries = self.factory.makeDistroSeries()
+        upload = self.makeBuildPackageUpload(distroseries)
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual([upload], upload_set.getAll(distroseries))
+
+    def test_getAll_returns_custom_upload(self):
+        distroseries = self.factory.makeDistroSeries()
+        upload = self.makeCustomPackageUpload(distroseries)
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual([upload], upload_set.getAll(distroseries))
+
+    def test_getAll_returns_copy_job_upload(self):
+        distroseries = self.factory.makeDistroSeries()
+        upload = self.makeCopyJobPackageUpload(distroseries)
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual([upload], upload_set.getAll(distroseries))
+
+    def test_getAll_matches_created_since_date(self):
+        distroseries = self.factory.makeDistroSeries()
+        upload = self.makeSourcePackageUpload(distroseries)
+        yesterday = upload.date_created - timedelta(1)
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual(
+            [upload],
+            upload_set.getAll(distroseries, created_since_date=yesterday))
+
+    def test_getAll_filters_by_created_since_date(self):
+        distroseries = self.factory.makeDistroSeries()
+        upload = self.makeSourcePackageUpload(distroseries)
+        tomorrow = upload.date_created + timedelta(1)
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual(
+            [], upload_set.getAll(distroseries, created_since_date=tomorrow))
+
+    def test_getAll_matches_status(self):
+        distroseries = self.factory.makeDistroSeries()
+        upload = self.makeSourcePackageUpload(distroseries)
+        status = upload.status
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual(
+            [upload], upload_set.getAll(distroseries, status=status))
+
+    def test_getAll_filters_by_status(self):
+        distroseries = self.factory.makeDistroSeries()
+        self.makeSourcePackageUpload(distroseries)
+        status = PackageUploadStatus.DONE
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual(
+            [], upload_set.getAll(distroseries, status=status))
+
+    def test_getAll_matches_pocket(self):
+        distroseries = self.factory.makeDistroSeries()
+        upload = self.makeSourcePackageUpload(distroseries)
+        pocket = upload.pocket
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual(
+            [upload], upload_set.getAll(distroseries, pocket=pocket))
+
+    def test_getAll_filters_by_pocket(self):
+        def find_different_pocket_than(pocket):
+            for other_pocket in PackagePublishingPocket.items:
+                if other_pocket != pocket:
+                    return other_pocket
+
+        distroseries = self.factory.makeDistroSeries()
+        upload = self.makeSourcePackageUpload(distroseries)
+        pocket = find_different_pocket_than(upload.pocket)
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual(
+            [], upload_set.getAll(distroseries, pocket=pocket))
+
+    def test_getAll_matches_custom_type(self):
+        distroseries = self.factory.makeDistroSeries()
+        custom_type = PackageUploadCustomFormat.DDTP_TARBALL
+        upload = self.makeCustomPackageUpload(
+            distroseries, custom_type=custom_type)
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual(
+            [upload],
+            upload_set.getAll(distroseries, custom_type=custom_type))
+
+    def test_getAll_filters_by_custom_type(self):
+        distroseries = self.factory.makeDistroSeries()
+        one_type = PackageUploadCustomFormat.DIST_UPGRADER
+        other_type = PackageUploadCustomFormat.ROSETTA_TRANSLATIONS
+        self.makeCustomPackageUpload(distroseries, custom_type=one_type)
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual(
+            [], upload_set.getAll(distroseries, custom_type=other_type))
+
+    def test_getAll_matches_source_upload_by_package_name(self):
+        distroseries = self.factory.makeDistroSeries()
+        spn = self.factory.makeSourcePackageName()
+        upload = self.makeSourcePackageUpload(
+            distroseries, sourcepackagename=spn)
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual(
+            [upload], upload_set.getAll(distroseries, name_filter=spn.name))
+
+    def test_getAll_filters_source_upload_by_package_name(self):
+        distroseries = self.factory.makeDistroSeries()
+        self.makeSourcePackageUpload(distroseries)
+        other_name = self.factory.makeSourcePackageName().name
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual(
+            [], upload_set.getAll(distroseries, name_filter=other_name))
+
+    def test_getAll_matches_build_upload_by_package_name(self):
+        distroseries = self.factory.makeDistroSeries()
+        bpn = self.factory.makeBinaryPackageName()
+        upload = self.makeBuildPackageUpload(
+            distroseries, binarypackagename=bpn)
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual(
+            [upload], upload_set.getAll(distroseries, name_filter=bpn.name))
+
+    def test_getAll_filters_build_upload_by_package_name(self):
+        distroseries = self.factory.makeDistroSeries()
+        self.makeBuildPackageUpload(distroseries)
+        other_name = self.factory.makeBinaryPackageName().name
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual(
+            [], upload_set.getAll(distroseries, name_filter=other_name))
+
+    def test_getAll_matches_custom_upload_by_file_name(self):
+        distroseries = self.factory.makeDistroSeries()
+        filename = self.factory.getUniqueString()
+        upload = self.makeCustomPackageUpload(distroseries, filename=filename)
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual(
+            [upload], upload_set.getAll(distroseries, name_filter=filename))
+
+    def test_getAll_filters_custom_upload_by_file_name(self):
+        distroseries = self.factory.makeDistroSeries()
+        filename = self.factory.getUniqueString()
+        self.makeCustomPackageUpload(distroseries, filename=filename)
+        other_name = self.factory.getUniqueString()
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual(
+            [], upload_set.getAll(distroseries, name_filter=other_name))
+
+    def test_getAll_matches_copy_job_upload_by_package_name(self):
+        distroseries = self.factory.makeDistroSeries()
+        spn = self.factory.makeSourcePackageName()
+        upload = self.makeCopyJobPackageUpload(
+            distroseries, sourcepackagename=spn)
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual(
+            [upload], upload_set.getAll(distroseries, name_filter=spn.name))
+
+    def test_getAll_filters_copy_job_upload_by_package_name(self):
+        distroseries = self.factory.makeDistroSeries()
+        self.makeCopyJobPackageUpload(distroseries)
+        other_name = self.factory.makeSourcePackageName().name
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual(
+            [], upload_set.getAll(distroseries, name_filter=other_name))
+
+    def test_getAll_matches_name_by_prefix(self):
+        distroseries = self.factory.makeDistroSeries()
+        spn = self.factory.makeSourcePackageName()
+        upload = self.makeSourcePackageUpload(
+            distroseries, sourcepackagename=spn)
+        prefix_name = spn.name[:-1]
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual(
+            [upload],
+            upload_set.getAll(distroseries, name_filter=prefix_name))
+
+    def test_getAll_escapes_name_filter(self):
+        distroseries = self.factory.makeDistroSeries()
+        upload_set = getUtility(IPackageUploadSet)
+        self.assertContentEqual(
+            [], upload_set.getAll(distroseries, name_filter="'"))
+
+
 class TestPackageUploadWithPackageCopyJob(TestCaseWithFactory):
 
     layer = LaunchpadZopelessLayer
@@ -370,7 +612,8 @@
         pcj = removeSecurityProxy(
             self.factory.makePlainPackageCopyJob()).context
         pu = self.factory.makePackageUpload(package_copy_job=pcj)
-        result = getUtility(IPackageUploadSet).getByPackageCopyJobIDs([pcj.id])
+        result = getUtility(IPackageUploadSet).getByPackageCopyJobIDs(
+            [pcj.id])
         self.assertEqual(pu, result.one())
 
     def test_overrideSource_with_copy_job(self):


Follow ups