Merge lp:~stevenk/launchpad/db-add-bprc into lp:launchpad/db-devel

Proposed by Steve Kowalik
Status: Merged
Approved by: Steve Kowalik
Approved revision: no longer in the source branch.
Merged at revision: 10693
Proposed branch: lp:~stevenk/launchpad/db-add-bprc
Merge into: lp:launchpad/db-devel
Diff against target: 416 lines (+341/-0)
10 files modified
database/schema/comments.sql (+10/-0)
database/schema/patch-2208-76-0.sql (+17/-0)
database/schema/security.cfg (+2/-0)
lib/lp/soyuz/configure.zcml (+34/-0)
lib/lp/soyuz/interfaces/binarypackagepath.py (+36/-0)
lib/lp/soyuz/interfaces/binarypackagereleasecontents.py (+43/-0)
lib/lp/soyuz/model/binarypackagepath.py (+37/-0)
lib/lp/soyuz/model/binarypackagereleasecontents.py (+70/-0)
lib/lp/soyuz/tests/test_binarypackagepath.py (+26/-0)
lib/lp/soyuz/tests/test_binarypackagereleasecontents.py (+66/-0)
To merge this branch: bzr merge lp:~stevenk/launchpad/db-add-bprc
Reviewer Review Type Date Requested Status
William Grant code Approve
Gavin Panella (community) Abstain
Stuart Bishop (community) db Approve
Robert Collins db Pending
Review via email: mp+64783@code.launchpad.net

Commit message

[r=stub,wgrant][bug=796997][incr] Add infrastructure to eventually support contents generation from the database.

Description of the change

(Updated to facilitate code review.)

Add a two new tables, BinaryPackagePath and BinaryPackageReleaseContents, and associated interfaces and models.

Background:

Currently, Contents generation (for example, the file at http://archive.ubuntu.com/ubuntu/dists/natty/Contents-amd64.gz) is generated every day, where it stops the publisher from running, and takes an hour or slightly more.

These two tables completes the first step of moving contents generation into the database, and lets it do the heavy lifting, rather than cocoplum's disk. This also gives us the opportunity to perform Contents generation for PPAs.

The next step of the plan is to have a garbo script that slowly populates these tables (but not yet, since they will be large).

The end goal is to move Contents generation into the publisher itself, where it will query the table by a list of BPRs and get back every single filename that the BPRs contain.

IBinaryPackagePath{,Source}:

The idea behind these two interfaces and database table is so that a filename that is in the contents of a Binary Package Release (such as, 'bin/true'), is only mentioned once.

IBinaryPackageReleaseContents{,Set}:

These two interfaces and database table are to link a given Binary Package Release (BPR) to its contents, where the filenames are joined from BinaryPackagePath. Note that a given BPR will have multiple rows in BPRC, one for each file, but each BPR can only link to a given file once.

Tests: All new, all shiny.

To post a comment you must log in.
Revision history for this message
Stuart Bishop (stub) wrote :

BinaryPackagePath.path should be UNIQUE NOT NULL.

Shouldn't this table be called BinaryPackageReleasePath to match BinaryPackageReleaseContents?

Both columns in BinaryPackageReleaseContents should be NOT NULL.

patch-2208-76-0.sql

You likely will want this construct to ensure a path exists in the BinaryPackageReleasePaths table. This will avoid race conditions in scripts (not running in serializable isolation):

insert into binarypackagepaths (path) select 'foo' where not exists (select * from binarypackagepaths where path='foo');

review: Approve (db)
Revision history for this message
Gavin Panella (allenap) :
review: Abstain
Revision history for this message
William Grant (wgrant) wrote :
Download full text (3.3 KiB)

82 + <class
83 + class="lp.soyuz.model.binarypackagepath.BinaryPackagePath">
84 + <allow
85 + interface="lp.soyuz.interfaces.binarypackagepath.IBinaryPackagePathSource"/>
86 + </class>

No need for this. You already grant it to the securedutility.

104 + <class
105 + class="lp.soyuz.model.binarypackagereleasecontents.BinaryPackageReleaseContents">
106 + <allow
107 + interface="lp.soyuz.interfaces.binarypackagereleasecontents.IBinaryPackageReleaseContentsSet"/>
108 + </class>

Same here.

You also have one Source and one Set. Please make them the same.

+ path = TextLine(title=_('Full path name'), required=True, readonly=True)

s/Full path name/Absolute path/, perhaps?

158 + def getOrCreate(path):
159 + """Fetch the ID of the given path name, or create it.
160 +
161 + :param: path: The full path name to query or create.
162 +
163 + :return: An ID.
164 + """

Why an ID? Why not the object itself?

191 + """The contents of a binary package release.
192 +
193 + A binary package release is a representation of a binary package in
194 + one or more releases. This class models the files contained within
195 + the binary package.
196 + """

In one or more releases? I think replacing all this with a one-liner would be good. "A file contained by an `IBinaryPackageRelease`." or so.

250 + def getOrCreate(self, path):
251 + """See `IBinaryPackagePathSource`."""
252 + store = IMasterStore(BinaryPackagePath)
253 + bpp = store.find(BinaryPackagePath, BinaryPackagePath.path == path)
254 + if bpp.count():
255 + return bpp[0]
256 + else:
257 + bpp = BinaryPackagePath()
258 + bpp.path = path
259 + return store.add(bpp)

Try something like this:

    bpp = store.find(BinaryPackagePath, BinaryPackagePath.path == path).one()
    if bpp is None:
        bpp = BinaryPackagePath(path)
        store.add(bpp)
    return bpp

314 + with TempDir() as tmp_dir:

Can you use a TemporaryFile instead? Or even a NamedTemporaryFile, if that doesn't work. Or does DebPackage really want a filename, and care about what it is? Also, might as well break out of the with block as soon as you're done with DebPackage.

324 + bpp = getUtility(IBinaryPackagePathSource).getOrCreate(
325 + unicode(filename))

Objection! unicode() on potentially non-ASCII data == wrong. Probably best to treat it as UTF-8, probably skipping if it's undecodable.

Also, this is going to be pretty slow. You may need to think about optimising it down to a single query eventually.

334 + results = store.find(
335 + BinaryPackageReleaseContents,
336 + BinaryPackageReleaseContents.binarypackagerelease == bpr.id)
337 + for bprc in results:
338 + store.remove(bprc)

You can't just call remove() on the resultset?

362 + def test_get_or_create(self):

Method name style should be preserved here. So, test_getOrCreate.

405 + deb = open(datadir('pmount_0.9.7-2ubuntu2_amd64.deb'), 'r')
406 + lfa = self.factory.makeLibraryFileAlias(...

Read more...

review: Needs Fixing (code)
Revision history for this message
William Grant (wgrant) wrote :

IBPP's docstring is a lie (seems copied from IBPRC).

Also, try using NamedTemporaryFile as a context manager. Bit clearer and shorter.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'database/schema/comments.sql'
2--- database/schema/comments.sql 2011-06-10 12:53:23 +0000
3+++ database/schema/comments.sql 2011-06-21 01:46:48 +0000
4@@ -1625,6 +1625,11 @@
5 COMMENT ON COLUMN BinaryPackageRelease.user_defined_fields IS 'A JSON struct containing a sequence of key-value pairs with user defined fields in the control file.';
6 COMMENT ON COLUMN BinaryPackageRelease.homepage IS 'Upstream project homepage URL, not checked for validity.';
7
8+-- BinaryPackageReleaseContents
9+
10+COMMENT ON TABLE BinaryPackageReleaseContents IS 'BinaryPackageReleaseContents: Mapping table that maps from BinaryPackageReleases to path names.';
11+COMMENT ON COLUMN BinaryPackageReleaseContents.binarypackagerelease IS 'The BinaryPackageRelease that contains the path name.';
12+COMMENT ON COLUMN BinaryPackageReleaseContents.binarypackagepath IS 'The path name, via the BinaryPackagePath table.';
13
14 -- BinaryPackageFile
15
16@@ -1637,6 +1642,11 @@
17
18 COMMENT ON TABLE BinaryPackageName IS 'BinaryPackageName: A soyuz binary package name.';
19
20+-- BinaryPackagePath
21+
22+COMMENT ON TABLE BinaryPackagePath IS 'BinaryPackagePath: A table of filenames shipped in binary packages.';
23+COMMENT ON COLUMN BinaryPackagePath.path IS 'The full path of the file.';
24+
25 -- Distribution
26
27 COMMENT ON TABLE Distribution IS 'Distribution: A soyuz distribution. A distribution is a collection of DistroSeries. Distributions often group together policy and may be referred to by a name such as "Ubuntu" or "Debian"';
28
29=== added file 'database/schema/patch-2208-76-0.sql'
30--- database/schema/patch-2208-76-0.sql 1970-01-01 00:00:00 +0000
31+++ database/schema/patch-2208-76-0.sql 2011-06-21 01:46:48 +0000
32@@ -0,0 +1,17 @@
33+-- Copyright 2011 Canonical Ltd. This software is licensed under the
34+-- GNU Affero General Public License version 3 (see the file LICENSE).
35+
36+SET client_min_messages=ERROR;
37+
38+CREATE TABLE BinaryPackagePath (
39+ id serial PRIMARY KEY,
40+ path bytea UNIQUE NOT NULL
41+);
42+
43+CREATE TABLE BinaryPackageReleaseContents (
44+ binarypackagerelease integer NOT NULL REFERENCES BinaryPackageRelease,
45+ binarypackagepath integer NOT NULL REFERENCES BinaryPackagePath,
46+ PRIMARY KEY (binarypackagerelease, binarypackagepath)
47+);
48+
49+INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 76, 0);
50
51=== modified file 'database/schema/security.cfg'
52--- database/schema/security.cfg 2011-06-17 17:32:37 +0000
53+++ database/schema/security.cfg 2011-06-21 01:46:48 +0000
54@@ -118,7 +118,9 @@
55 public.archivesubscriber = SELECT, INSERT, UPDATE
56 public.authtoken = SELECT, INSERT, UPDATE, DELETE
57 public.binaryandsourcepackagenameview = SELECT
58+public.binarypackagepath = SELECT, INSERT, DELETE
59 public.binarypackagepublishinghistory = SELECT
60+public.binarypackagereleasecontents = SELECT, INSERT, DELETE
61 public.binarypackagereleasedownloadcount= SELECT, INSERT, UPDATE
62 public.bountysubscription = SELECT, INSERT, UPDATE, DELETE
63 public.branch = SELECT, INSERT, UPDATE, DELETE
64
65=== modified file 'lib/lp/soyuz/configure.zcml'
66--- lib/lp/soyuz/configure.zcml 2011-06-15 02:41:34 +0000
67+++ lib/lp/soyuz/configure.zcml 2011-06-21 01:46:48 +0000
68@@ -982,6 +982,40 @@
69 <allow interface="lp.soyuz.adapters.overrides.IOverridePolicy" />
70 </class>
71
72+ <!-- BinaryPackagePath -->
73+
74+ <class
75+ class="lp.soyuz.model.binarypackagepath.BinaryPackagePath">
76+ <allow
77+ interface="lp.soyuz.interfaces.binarypackagepath.IBinaryPackagePath"/>
78+ </class>
79+
80+ <!-- BinaryPackagePathSet -->
81+
82+ <securedutility
83+ class="lp.soyuz.model.binarypackagepath.BinaryPackagePath"
84+ provides="lp.soyuz.interfaces.binarypackagepath.IBinaryPackagePathSet">
85+ <allow
86+ interface="lp.soyuz.interfaces.binarypackagepath.IBinaryPackagePathSet"/>
87+ </securedutility>
88+
89+ <!-- BinaryPackageReleaseContents -->
90+
91+ <class
92+ class="lp.soyuz.model.binarypackagereleasecontents.BinaryPackageReleaseContents">
93+ <allow
94+ interface="lp.soyuz.interfaces.binarypackagereleasecontents.IBinaryPackageReleaseContents"/>
95+ </class>
96+
97+ <!-- BinaryPackageReleaseContentsSet -->
98+
99+ <securedutility
100+ class="lp.soyuz.model.binarypackagereleasecontents.BinaryPackageReleaseContents"
101+ provides="lp.soyuz.interfaces.binarypackagereleasecontents.IBinaryPackageReleaseContentsSet">
102+ <allow
103+ interface="lp.soyuz.interfaces.binarypackagereleasecontents.IBinaryPackageReleaseContentsSet"/>
104+ </securedutility>
105+
106 <webservice:register module="lp.soyuz.interfaces.webservice" />
107
108 </configure>
109
110=== added file 'lib/lp/soyuz/interfaces/binarypackagepath.py'
111--- lib/lp/soyuz/interfaces/binarypackagepath.py 1970-01-01 00:00:00 +0000
112+++ lib/lp/soyuz/interfaces/binarypackagepath.py 2011-06-21 01:46:48 +0000
113@@ -0,0 +1,36 @@
114+# Copyright 2011 Canonical Ltd. This software is licensed under the
115+# GNU Affero General Public License version 3 (see the file LICENSE).
116+
117+"""Binary Package Path interface."""
118+
119+__metaclass__ = type
120+
121+__all__ = [
122+ 'IBinaryPackagePath',
123+ 'IBinaryPackagePathSet',
124+ ]
125+
126+from zope.interface import Interface
127+from zope.schema import (
128+ Int,
129+ TextLine,
130+ )
131+
132+from canonical.launchpad import _
133+
134+
135+class IBinaryPackagePath(Interface):
136+ """The path of a file contained in a binary package."""
137+ id = Int(title=_('ID'), required=True, readonly=True)
138+ path = TextLine(title=_('Full path name'), required=True, readonly=True)
139+
140+
141+class IBinaryPackagePathSet(Interface):
142+
143+ def getOrCreate(path):
144+ """Fetch the ID of the given path name, or create it.
145+
146+ :param: path: The full path name to query or create.
147+
148+ :return: A `IBinaryPackagePath`.
149+ """
150
151=== added file 'lib/lp/soyuz/interfaces/binarypackagereleasecontents.py'
152--- lib/lp/soyuz/interfaces/binarypackagereleasecontents.py 1970-01-01 00:00:00 +0000
153+++ lib/lp/soyuz/interfaces/binarypackagereleasecontents.py 2011-06-21 01:46:48 +0000
154@@ -0,0 +1,43 @@
155+# Copyright 2011 Canonical Ltd. This software is licensed under the
156+# GNU Affero General Public License version 3 (see the file LICENSE).
157+
158+"""Binary Package Release Contents interface."""
159+
160+__metaclass__ = type
161+
162+__all__ = [
163+ 'IBinaryPackageReleaseContents',
164+ 'IBinaryPackageReleaseContentsSet',
165+ ]
166+
167+from lazr.restful.fields import Reference
168+from zope.interface import Interface
169+
170+from canonical.launchpad import _
171+from lp.soyuz.interfaces.binarypackagepath import IBinaryPackagePath
172+from lp.soyuz.interfaces.binarypackagerelease import IBinaryPackageRelease
173+
174+
175+class IBinaryPackageReleaseContents(Interface):
176+ """A file contained by an `IBinaryPackageRelease`."""
177+ binarypackagerelease = Reference(
178+ IBinaryPackageRelease, title=_('Binary Package Release'),
179+ required=True, readonly=True)
180+ binarypackagepath = Reference(
181+ IBinaryPackagePath, title=_('Binary Package Pathname'),
182+ required=True, readonly=True)
183+
184+
185+class IBinaryPackageReleaseContentsSet(Interface):
186+
187+ def add(bpr):
188+ """Add the contents of the given binary package release.
189+
190+ :param: bpr: The `IBinaryPackageRelease` to add.
191+ """
192+
193+ def remove(bpr):
194+ """Remove the contents of the given binary package release.
195+
196+ :param: bpr: The `IBinaryPackageRelease` to remove.
197+ """
198
199=== added file 'lib/lp/soyuz/model/binarypackagepath.py'
200--- lib/lp/soyuz/model/binarypackagepath.py 1970-01-01 00:00:00 +0000
201+++ lib/lp/soyuz/model/binarypackagepath.py 2011-06-21 01:46:48 +0000
202@@ -0,0 +1,37 @@
203+# Copyright 2009 Canonical Ltd. This software is licensed under the
204+# GNU Affero General Public License version 3 (see the file LICENSE).
205+
206+__metaclass__ = type
207+
208+__all__ = [
209+ 'BinaryPackagePath',
210+ ]
211+
212+from storm.locals import (
213+ Int,
214+ RawStr,
215+ Storm,
216+ )
217+from zope.interface import implements
218+
219+from canonical.launchpad.interfaces.lpstorm import IMasterStore
220+from lp.soyuz.interfaces.binarypackagepath import IBinaryPackagePath
221+
222+
223+class BinaryPackagePath(Storm):
224+ """See `IBinaryPackagePath`."""
225+ implements(IBinaryPackagePath)
226+ __storm_table__ = 'BinaryPackagePath'
227+ id = Int(primary=True)
228+ path = RawStr(name='path', allow_none=False)
229+
230+ def getOrCreate(self, path):
231+ """See `IBinaryPackagePathSet`."""
232+ store = IMasterStore(BinaryPackagePath)
233+ bpp = store.find(
234+ BinaryPackagePath, BinaryPackagePath.path == path).one()
235+ if bpp is None:
236+ bpp = BinaryPackagePath()
237+ bpp.path = path
238+ store.add(bpp)
239+ return bpp
240
241=== added file 'lib/lp/soyuz/model/binarypackagereleasecontents.py'
242--- lib/lp/soyuz/model/binarypackagereleasecontents.py 1970-01-01 00:00:00 +0000
243+++ lib/lp/soyuz/model/binarypackagereleasecontents.py 2011-06-21 01:46:48 +0000
244@@ -0,0 +1,70 @@
245+# Copyright 2011 Canonical Ltd. This software is licensed under the
246+# GNU Affero General Public License version 3 (see the file LICENSE).
247+
248+__metaclass__ = type
249+
250+__all__ = [
251+ 'BinaryPackageReleaseContents',
252+ ]
253+
254+import tempfile
255+
256+from apt.debfile import DebPackage
257+from bzrlib.osutils import pumpfile
258+from storm.locals import (
259+ Int,
260+ Reference,
261+ Storm,
262+ )
263+from zope.component import getUtility
264+from zope.interface import implements
265+
266+from canonical.launchpad.interfaces.lpstorm import IMasterStore
267+from lp.soyuz.interfaces.binarypackagepath import IBinaryPackagePathSet
268+from lp.soyuz.interfaces.binarypackagereleasecontents import (
269+ IBinaryPackageReleaseContents,
270+ )
271+
272+
273+class BinaryPackageReleaseContents(Storm):
274+ """See `IBinaryPackageReleaseContents`."""
275+ implements(IBinaryPackageReleaseContents)
276+ __storm_table__ = 'BinaryPackageReleaseContents'
277+ __storm_primary__ = ("binarypackagerelease_id", "binaypackagepath_id")
278+
279+ binarypackagerelease_id = Int(
280+ name='binarypackagerelease', allow_none=False)
281+ binarypackagerelease = Reference(
282+ binarypackagerelease_id, 'BinaryPackageRelease.id')
283+
284+ binaypackagepath_id = Int(name='binarypackagepath', allow_none=False)
285+ binarypackagepath = Reference(
286+ binaypackagepath_id, 'BinaryPackagePath.id')
287+
288+ def add(self, bpr):
289+ """See `IBinaryPackageReleaseContentsSet`."""
290+ if not bpr.files:
291+ return None
292+ store = IMasterStore(BinaryPackageReleaseContents)
293+ with tempfile.NamedTemporaryFile() as dest_file:
294+ bpr.files[0].libraryfile.open()
295+ pumpfile(bpr.files[0].libraryfile, dest_file)
296+ dest_file.seek(0)
297+ deb = DebPackage(filename=dest_file.name)
298+ # Filter out directories.
299+ filelist = filter(lambda x: not x.endswith('/'), deb.filelist)
300+ for filename in filelist:
301+ bpp = getUtility(IBinaryPackagePathSet).getOrCreate(
302+ filename)
303+ bprc = BinaryPackageReleaseContents()
304+ bprc.binarypackagerelease = bpr
305+ bprc.binarypackagepath = bpp
306+ store.add(bprc)
307+
308+ def remove(self, bpr):
309+ """See `IBinaryPackageReleaseContentsSet`."""
310+ store = IMasterStore(BinaryPackageReleaseContents)
311+ results = store.find(
312+ BinaryPackageReleaseContents,
313+ BinaryPackageReleaseContents.binarypackagerelease == bpr.id)
314+ results.remove()
315
316=== added file 'lib/lp/soyuz/tests/test_binarypackagepath.py'
317--- lib/lp/soyuz/tests/test_binarypackagepath.py 1970-01-01 00:00:00 +0000
318+++ lib/lp/soyuz/tests/test_binarypackagepath.py 2011-06-21 01:46:48 +0000
319@@ -0,0 +1,26 @@
320+# Copyright 2011 Canonical Ltd. This software is licensed under the
321+# GNU Affero General Public License version 3 (see the file LICENSE).
322+
323+"""Test the Binary Package Path model."""
324+
325+__metaclass__ = type
326+
327+from zope.component import getUtility
328+
329+from canonical.testing.layers import DatabaseFunctionalLayer
330+from lp.soyuz.interfaces.binarypackagepath import IBinaryPackagePathSet
331+from lp.testing import TestCaseWithFactory
332+
333+
334+class TestBinaryPackagePath(TestCaseWithFactory):
335+
336+ layer = DatabaseFunctionalLayer
337+
338+ def test_getOrCreate(self):
339+ bpp = getUtility(IBinaryPackagePathSet).getOrCreate('bin/bash')
340+ self.assertEqual('bin/bash', bpp.path)
341+
342+ def test_getOrCreate_existing(self):
343+ orig_bpp = getUtility(IBinaryPackagePathSet).getOrCreate('bin/bash')
344+ bpp = getUtility(IBinaryPackagePathSet).getOrCreate('bin/bash')
345+ self.assertEqual(orig_bpp.id, bpp.id)
346
347=== added file 'lib/lp/soyuz/tests/test_binarypackagereleasecontents.py'
348--- lib/lp/soyuz/tests/test_binarypackagereleasecontents.py 1970-01-01 00:00:00 +0000
349+++ lib/lp/soyuz/tests/test_binarypackagereleasecontents.py 2011-06-21 01:46:48 +0000
350@@ -0,0 +1,66 @@
351+# Copyright 2011 Canonical Ltd. This software is licensed under the
352+# GNU Affero General Public License version 3 (see the file LICENSE).
353+
354+"""Test the Binary Package Release Contents model."""
355+
356+__metaclass__ = type
357+
358+import transaction
359+
360+from zope.component import getUtility
361+
362+from canonical.launchpad.interfaces.lpstorm import IStore
363+from canonical.testing.layers import LaunchpadFunctionalLayer
364+from lp.archiveuploader.tests import datadir
365+from lp.soyuz.interfaces.binarypackagereleasecontents import (
366+ IBinaryPackageReleaseContentsSet,
367+ )
368+from lp.soyuz.model.binarypackagereleasecontents import (
369+ BinaryPackageReleaseContents,
370+ )
371+from lp.testing import TestCaseWithFactory
372+
373+
374+class TestBinaryPackagePath(TestCaseWithFactory):
375+
376+ layer = LaunchpadFunctionalLayer
377+
378+ def create_bpr(self):
379+ bpr = self.factory.makeBinaryPackageRelease()
380+ deb = open(datadir('pmount_0.9.7-2ubuntu2_amd64.deb'), 'r')
381+ lfa = self.factory.makeLibraryFileAlias(
382+ filename='pmount_0.9.7-2ubuntu2_amd64.deb', content=deb.read())
383+ deb.close()
384+ transaction.commit()
385+ bpr.addFile(lfa)
386+ return bpr
387+
388+ def test_add(self):
389+ bpr = self.create_bpr()
390+ getUtility(IBinaryPackageReleaseContentsSet).add(bpr)
391+ store = IStore(BinaryPackageReleaseContents)
392+ results = store.find(
393+ BinaryPackageReleaseContents,
394+ BinaryPackageReleaseContents.binarypackagerelease == bpr.id)
395+ paths = map(lambda x: x.binarypackagepath.path, results)
396+ expected_paths = [
397+ 'etc/pmount.allow', 'usr/bin/pumount', 'usr/bin/pmount-hal',
398+ 'usr/bin/pmount', 'usr/share/doc/pmount/TODO',
399+ 'usr/share/doc/pmount/README.Debian',
400+ 'usr/share/doc/pmount/AUTHORS', 'usr/share/doc/pmount/copyright',
401+ 'usr/share/doc/pmount/changelog.gz',
402+ 'usr/share/doc/pmount/changelog.Debian.gz',
403+ 'usr/share/man/man1/pmount-hal.1.gz',
404+ 'usr/share/man/man1/pmount.1.gz',
405+ 'usr/share/man/man1/pumount.1.gz']
406+ self.assertContentEqual(expected_paths, paths)
407+
408+ def test_remove(self):
409+ bpr = self.create_bpr()
410+ getUtility(IBinaryPackageReleaseContentsSet).add(bpr)
411+ getUtility(IBinaryPackageReleaseContentsSet).remove(bpr)
412+ store = IStore(BinaryPackageReleaseContents)
413+ results = store.find(
414+ BinaryPackageReleaseContents,
415+ BinaryPackageReleaseContents.binarypackagerelease == bpr.id)
416+ self.assertEqual(0, results.count())

Subscribers

People subscribed via source and target branches

to status/vote changes: