Merge lp:~jelmer/launchpad/noversion into lp:launchpad

Proposed by Jelmer Vernooij
Status: Merged
Merged at revision: 11282
Proposed branch: lp:~jelmer/launchpad/noversion
Merge into: lp:launchpad
Diff against target: 583 lines (+46/-405)
3 files modified
lib/lp/archivepublisher/debversion.py (+21/-129)
lib/lp/archivepublisher/tests/test_debversion.py (+23/-263)
lib/lp/soyuz/scripts/buildd.py (+2/-13)
To merge this branch: bzr merge lp:~jelmer/launchpad/noversion
Reviewer Review Type Date Requested Status
Abel Deuring (community) Needs Information
Review via email: mp+21059@code.launchpad.net

Description of the change

Remove duplicate implementation of Debian version parsing but instead rely on python-debian's parsing code (also used in dak and bzr-builddeb, so we already have it present).

This also removes a couple of other utility functions that weren't used in Launchpad.

To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) wrote :

Hi Jelmer,

overall, your changes look good. But I get a test failure when I run ./bin/test -t test_debversion:

Failure in test testNullRevisionIsZero (lp.archivepublisher.tests.test_debversion.VersionTests)
Traceback (most recent call last):
  File "/usr/lib/python2.5/unittest.py", line 260, in run
    testMethod()
  File "/home/abel/canonical/lp-branches/review-jelmer/lib/lp/archivepublisher/tests/test_debversion.py", line 132, in testNullRevisionIsZero
    self.assertEquals(Version("1.0"), Version("1.0-0"))
  File "/usr/lib/python2.5/unittest.py", line 334, in failUnlessEqual
    (msg or '%r != %r' % (first, second))
AssertionError: Version('1.0') != Version('1.0-0')

Are you sure that the new, more strict, comparison does not have any bad side effects?

(And if

    Version('1.0') != Version('1.0-0')

works in Launchpad, I think you can remove this test.)

review: Needs Information
Revision history for this message
Jelmer Vernooij (jelmer) wrote :

This test already existed, I've just changed it to use assertEquals.

Older versions of Launchpad also had this behaviour and it's required by Debian policy. It looks like older versions of python-debian considered 1.0 < 1.0-0.

I'll see if we can include python-debian in Launchpad or provide backports of python-debian so we're sure we have a working version.

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

> I'll see if we can include python-debian in Launchpad or provide backports of
> python-debian so we're sure we have a working version.
(python-debian is available because it is a dependency of bzr-builddeb, launchpad doesn't have an explicit dependency on it).

Revision history for this message
Abel Deuring (adeuring) wrote :

On 12.03.2010 11:43, Jelmer Vernooij wrote:
> This test already existed, I've just changed it to use assertEquals.

Right, but you changed the implemention of the tested class, so this is
a genuine test failure ;)

>
> Older versions of Launchpad also had this behaviour and it's required by Debian policy. It looks like older versions of python-debian considered 1.0 < 1.0-0.

OK. But the is question why this "relaxed equality comparison" was
introduced and even tested, and if we can remove it again.

>
> I'll see if we can include python-debian in Launchpad or provide backports of python-debian so we're sure we have a working version.

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

> On 12.03.2010 11:43, Jelmer Vernooij wrote:
> > This test already existed, I've just changed it to use assertEquals.
> Right, but you changed the implemention of the tested class, so this is
> a genuine test failure ;)
Yep - thanks, I'm not sure how I missed that.

> > Older versions of Launchpad also had this behaviour and it's required by
> Debian policy. It looks like older versions of python-debian considered 1.0 <
> 1.0-0.
> OK. But the is question why this "relaxed equality comparison" was
> introduced and even tested, and if we can remove it again.
It was inherited from sourcerer and this is mainly a theoretical situation. "0" should never be used as the debian revision and it would only ever apply anyway if somebody changed a native package to no longer be native while keeping the upstream version the same *and* using 0 for the debian revision.

Still, it would be nice to get right just in case somebody happens to ever do something like that. It would be confusing if dpkg and launchpad behaved differently. I've filed a bug against apt in debian about this and noted it in the source code.

Revision history for this message
Abel Deuring (adeuring) wrote :

On 12.03.2010 19:52, Jelmer Vernooij wrote:
>> On 12.03.2010 11:43, Jelmer Vernooij wrote:
>>> This test already existed, I've just changed it to use assertEquals.
>> Right, but you changed the implemention of the tested class, so this is
>> a genuine test failure ;)
> Yep - thanks, I'm not sure how I missed that.
>
>>> Older versions of Launchpad also had this behaviour and it's required by
>> Debian policy. It looks like older versions of python-debian considered 1.0 <
>> 1.0-0.
>> OK. But the is question why this "relaxed equality comparison" was
>> introduced and even tested, and if we can remove it again.
> It was inherited from sourcerer and this is mainly a theoretical situation. "0" should never be used as the debian revision and it would only ever apply anyway if somebody changed a native package to no longer be native while keeping the upstream version the same *and* using 0 for the debian revision.
>
> Still, it would be nice to get right just in case somebody happens to ever do something like that. It would be confusing if dpkg and launchpad behaved differently. I've filed a bug against apt in debian about this and noted it in the source code.

OK, so, what shall we do with this test failure? Adjust the test or
change the __cmp__ method of the tested class?

I have no clue if the new comparison may have any side effects on other
code -- I simply don't understand enough about Soyuz and the fine
details about Debian packages in general.

Can/should we simply ensure that version strings without the "-0" are
rejected? That would make the test obsolete. (Do we already have
packages where this "-0" is missing?)

If we keep the new comparison: Can it happen that we have two package
versions 1.0 and 1.0-0? How would Launchpad deal with these packages?
And how would apt, dpkg and other package management tools deal with
these versions?

Abel

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

Hi Abel,

On Mon, 2010-03-15 at 08:45 +0000, Abel Deuring wrote:
> On 12.03.2010 19:52, Jelmer Vernooij wrote:
> >> On 12.03.2010 11:43, Jelmer Vernooij wrote:
> >>> This test already existed, I've just changed it to use assertEquals.
> >> Right, but you changed the implemention of the tested class, so this is
> >> a genuine test failure ;)
> > Yep - thanks, I'm not sure how I missed that.
> >
> >>> Older versions of Launchpad also had this behaviour and it's required by
> >> Debian policy. It looks like older versions of python-debian considered 1.0 <
> >> 1.0-0.
> >> OK. But the is question why this "relaxed equality comparison" was
> >> introduced and even tested, and if we can remove it again.
> > It was inherited from sourcerer and this is mainly a theoretical situation. "0" should never be used as the debian revision and it would only ever apply anyway if somebody changed a native package to no longer be native while keeping the upstream version the same *and* using 0 for the debian revision.
> >
> > Still, it would be nice to get right just in case somebody happens to ever do something like that. It would be confusing if dpkg and launchpad behaved differently. I've filed a bug against apt in debian about this and noted it in the source code.
>
> OK, so, what shall we do with this test failure? Adjust the test or
> change the __cmp__ method of the tested class?
The __cmp__ not working properly is actually a bug deep down in apt, so
I've filed a bug against it in Debian and disabled the test for now.

> I have no clue if the new comparison may have any side effects on other
> code -- I simply don't understand enough about Soyuz and the fine
> details about Debian packages in general.
I'm quite sure this shouldn't have any side effects.

> Can/should we simply ensure that version strings without the "-0" are
> rejected? That would make the test obsolete. (Do we already have
> packages where this "-0" is missing?)
It's the other way around - -0 would be missing generally, I don't think
there is anybody using -0 at the moment. I guess we could refuse
packages with a "0" Debian revisions for now, I'll ask if that'd be
acceptable.

> If we keep the new comparison: Can it happen that we have two package
> versions 1.0 and 1.0-0? How would Launchpad deal with these packages?
> And how would apt, dpkg and other package management tools deal with
> these versions?
No, we can't have both a package without and with -0 because they would
be deemed the same package, would need to have the same MD5sum (but
can't because their debian/changelog file would differ).

Cheers,

Jelmer

Revision history for this message
Abel Deuring (adeuring) wrote :
Download full text (4.0 KiB)

Hi Jelmer,

We are now really reaching a topic where I don't any real knowledge at
all, so I'm swtiching into "paranoia mode". If you think this is
inappropriate, you might consider to ask somebody else for a review ;)

On 17.03.2010 11:00, Jelmer Vernooij wrote:
> Hi Abel,
>
> On Mon, 2010-03-15 at 08:45 +0000, Abel Deuring wrote:
>> On 12.03.2010 19:52, Jelmer Vernooij wrote:
>>>> On 12.03.2010 11:43, Jelmer Vernooij wrote:
>>>>> This test already existed, I've just changed it to use assertEquals.
>>>> Right, but you changed the implemention of the tested class, so this is
>>>> a genuine test failure ;)
>>> Yep - thanks, I'm not sure how I missed that.
>>>
>>>>> Older versions of Launchpad also had this behaviour and it's required by
>>>> Debian policy. It looks like older versions of python-debian considered 1.0 <
>>>> 1.0-0.
>>>> OK. But the is question why this "relaxed equality comparison" was
>>>> introduced and even tested, and if we can remove it again.
>>> It was inherited from sourcerer and this is mainly a theoretical situation. "0" should never be used as the debian revision and it would only ever apply anyway if somebody changed a native package to no longer be native while keeping the upstream version the same *and* using 0 for the debian revision.
>>>
>>> Still, it would be nice to get right just in case somebody happens to ever do something like that. It would be confusing if dpkg and launchpad behaved differently. I've filed a bug against apt in debian about this and noted it in the source code.
>> OK, so, what shall we do with this test failure? Adjust the test or
>> change the __cmp__ method of the tested class?
> The __cmp__ not working properly is actually a bug deep down in apt, so
> I've filed a bug against it in Debian and disabled the test for now.

OK, this sound good. But for now we have to deal with this somewhat odd
comparison in apt. I assume that apt in Ubuntu is also affected -- but
even if it not, we must deal with upstream having this bug.

>
>> I have no clue if the new comparison may have any side effects on other
>> code -- I simply don't understand enough about Soyuz and the fine
>> details about Debian packages in general.
> I'm quite sure this shouldn't have any side effects.

This sounds good, but let's be sure. Can we query our database for
possible bad version strings, i.e., strings that end with "-0"? If we
don't have any, that's good, but we should also ensure that such version
strings cannot be created in the future. Perhaps I am putting too much
"meaning" into the version string, but I think it is a kind of unique
identifier for a package version, so we should be really sure that we
don't create a mess by allowing two version strings in Launchpad meaning
"locally" that they are different versions, while they are considered
identical elsewhere.

And we should ensure that we can't create version strings in LP ending
with '-0' in the future.

>
>> Can/should we simply ensure that version strings without the "-0" are
>> rejected? That would make the test obsolete. (Do we already have
>> packages where this "-0" is missing?)
> It's the other way around - -0 would be missing generally, I don't th...

Read more...

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

Hi Abel,

> We are now really reaching a topic where I don't any real knowledge at
> all, so I'm swtiching into "paranoia mode". If you think this is
> inappropriate, you might consider to ask somebody else for a review ;)
I've discussed the issue with Julian and we've decided to put this branch on hold. There is a fix for the bug in apt available which should land in Lucid. Once Launchpad migrates to Lucid, we'll just get that bug fix for free and can land this branch then; since this branch is mostly removing duplicate code there is no hurry.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/archivepublisher/debversion.py'
2--- lib/lp/archivepublisher/debversion.py 2009-06-24 23:28:16 +0000
3+++ lib/lp/archivepublisher/debversion.py 2010-06-18 10:12:40 +0000
4@@ -12,27 +12,25 @@
5
6 # This code came from sourcerer.
7
8+from debian_bundle import changelog
9+
10 import re
11
12-
13 # Regular expressions make validating things easy
14 valid_epoch = re.compile(r'^[0-9]+$')
15 valid_upstream = re.compile(r'^[0-9][A-Za-z0-9+:.~-]*$')
16 valid_revision = re.compile(r'^[A-Za-z0-9+.~]+$')
17
18-# Character comparison table for upstream and revision components
19-cmp_table = "~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-.:"
20-
21-
22-class VersionError(Exception): pass
23+VersionError = changelog.VersionError
24 class BadInputError(VersionError): pass
25 class BadEpochError(BadInputError): pass
26 class BadUpstreamError(BadInputError): pass
27 class BadRevisionError(BadInputError): pass
28
29-class Version(object):
30+
31+class Version(changelog.Version):
32 """Debian version number.
33-
34+
35 This class is designed to be reasonably transparent and allow you
36 to write code like:
37
38@@ -44,136 +42,30 @@
39 Properties:
40 epoch Epoch
41 upstream Upstream version
42- revision Debian/local revision
43+ debian_version Debian/local revision
44 """
45-
46 def __init__(self, ver):
47- """Parse a string or number into the three components."""
48- self.epoch = 0
49- self.upstream = None
50- self.revision = None
51
52 ver = str(ver)
53 if not len(ver):
54 raise BadInputError, "Input cannot be empty"
55
56- # Epoch is component before first colon
57- idx = ver.find(":")
58- if idx != -1:
59- self.epoch = ver[:idx]
60+ changelog.Version.__init__(self, ver)
61+
62+ if self.epoch is not None:
63 if not len(self.epoch):
64 raise BadEpochError, "Epoch cannot be empty"
65- if not valid_epoch.search(self.epoch):
66+ if not valid_epoch.match(self.epoch):
67 raise BadEpochError, "Bad epoch format"
68- ver = ver[idx+1:]
69-
70- # Revision is component after last hyphen
71- idx = ver.rfind("-")
72- if idx != -1:
73- self.revision = ver[idx+1:]
74- if not len(self.revision):
75- raise BadRevisionError, "Revision cannot be empty"
76- if not valid_revision.search(self.revision):
77- raise BadRevisionError, "Bad revision format"
78- ver = ver[:idx]
79-
80- # Remaining component is upstream
81- self.upstream = ver
82- if not len(self.upstream):
83+
84+ if self.debian_version is not None:
85+ if self.debian_version == "":
86+ raise BadRevisionError("Revision cannot be empty")
87+ if not valid_revision.search(self.debian_version):
88+ raise BadRevisionError("Bad revision format")
89+
90+ if not len(self.upstream_version):
91 raise BadUpstreamError, "Upstream version cannot be empty"
92- if not valid_upstream.search(self.upstream):
93+ if not valid_upstream.search(self.upstream_version):
94 raise BadUpstreamError(
95- "Bad upstream version format", self.upstream)
96-
97- self.epoch = int(self.epoch)
98-
99- def getWithoutEpoch(self):
100- """Return the version without the epoch."""
101- str = self.upstream
102- if self.revision is not None:
103- str += "-%s" % (self.revision,)
104- return str
105-
106- without_epoch = property(getWithoutEpoch)
107-
108- def __str__(self):
109- """Return the class as a string for printing."""
110- str = ""
111- if self.epoch > 0:
112- str += "%d:" % (self.epoch,)
113- str += self.upstream
114- if self.revision is not None:
115- str += "-%s" % (self.revision,)
116- return str
117-
118- def __repr__(self):
119- """Return a debugging representation of the object."""
120- return "<%s epoch: %d, upstream: %r, revision: %r>" \
121- % (self.__class__.__name__, self.epoch,
122- self.upstream, self.revision)
123-
124- def __cmp__(self, other):
125- """Compare two Version classes."""
126- other = Version(other)
127-
128- result = cmp(self.epoch, other.epoch)
129- if result != 0: return result
130-
131- result = deb_cmp(self.upstream, other.upstream)
132- if result != 0: return result
133-
134- result = deb_cmp(self.revision or "", other.revision or "")
135- if result != 0: return result
136-
137- return 0
138-
139-
140-def strcut(str, idx, accept):
141- """Cut characters from str that are entirely in accept."""
142- ret = ""
143- while idx < len(str) and str[idx] in accept:
144- ret += str[idx]
145- idx += 1
146-
147- return (ret, idx)
148-
149-def deb_order(str, idx):
150- """Return the comparison order of two characters."""
151- if idx >= len(str):
152- return 0
153- elif str[idx] == "~":
154- return -1
155- else:
156- return cmp_table.index(str[idx])
157-
158-def deb_cmp_str(x, y):
159- """Compare two strings in a deb version."""
160- idx = 0
161- while (idx < len(x)) or (idx < len(y)):
162- result = deb_order(x, idx) - deb_order(y, idx)
163- if result < 0:
164- return -1
165- elif result > 0:
166- return 1
167-
168- idx += 1
169-
170- return 0
171-
172-def deb_cmp(x, y):
173- """Implement the string comparison outlined by Debian policy."""
174- x_idx = y_idx = 0
175- while x_idx < len(x) or y_idx < len(y):
176- # Compare strings
177- (x_str, x_idx) = strcut(x, x_idx, cmp_table)
178- (y_str, y_idx) = strcut(y, y_idx, cmp_table)
179- result = deb_cmp_str(x_str, y_str)
180- if result != 0: return result
181-
182- # Compare numbers
183- (x_str, x_idx) = strcut(x, x_idx, "0123456789")
184- (y_str, y_idx) = strcut(y, y_idx, "0123456789")
185- result = cmp(int(x_str or "0"), int(y_str or "0"))
186- if result != 0: return result
187-
188- return 0
189+ "Bad upstream version format %s" % self.upstream_version)
190
191=== modified file 'lib/lp/archivepublisher/tests/test_debversion.py'
192--- lib/lp/archivepublisher/tests/test_debversion.py 2010-02-12 18:30:30 +0000
193+++ lib/lp/archivepublisher/tests/test_debversion.py 2010-06-18 10:12:40 +0000
194@@ -9,7 +9,11 @@
195
196 import unittest
197
198-class Version(unittest.TestCase):
199+from lp.archivepublisher.debversion import (BadInputError, BadUpstreamError,
200+ Version, VersionError)
201+
202+
203+class VersionTests(unittest.TestCase):
204 # Known values that should work
205 VALUES = (
206 "1",
207@@ -50,325 +54,81 @@
208
209 def testAcceptsString(self):
210 """Version should accept a string input."""
211- from lp.archivepublisher.debversion import Version
212 Version("1.0")
213
214 def testReturnString(self):
215 """Version should convert to a string."""
216- from lp.archivepublisher.debversion import Version
217 self.assertEquals(str(Version("1.0")), "1.0")
218
219 def testAcceptsInteger(self):
220 """Version should accept an integer."""
221- from lp.archivepublisher.debversion import Version
222 self.assertEquals(str(Version(1)), "1")
223
224 def testAcceptsNumber(self):
225 """Version should accept a number."""
226- from lp.archivepublisher.debversion import Version
227 self.assertEquals(str(Version(1.2)), "1.2")
228
229- def testOmitZeroEpoch(self):
230- """Version should omit epoch when zero."""
231- from lp.archivepublisher.debversion import Version
232- self.assertEquals(str(Version("0:1.0")), "1.0")
233-
234- def testOmitZeroRevision(self):
235- """Version should not omit zero revision."""
236- from lp.archivepublisher.debversion import Version
237- self.assertEquals(str(Version("1.0-0")), "1.0-0")
238-
239 def testNotEmpty(self):
240 """Version should fail with empty input."""
241- from lp.archivepublisher.debversion import Version, BadInputError
242 self.assertRaises(BadInputError, Version, "")
243
244 def testEpochNotEmpty(self):
245 """Version should fail with empty epoch."""
246- from lp.archivepublisher.debversion import Version, BadEpochError
247- self.assertRaises(BadEpochError, Version, ":1")
248+ self.assertRaises(VersionError, Version, ":1")
249
250 def testEpochNonNumeric(self):
251 """Version should fail with non-numeric epoch."""
252- from lp.archivepublisher.debversion import Version, BadEpochError
253- self.assertRaises(BadEpochError, Version, "a:1")
254+ self.assertRaises(VersionError, Version, "a:1")
255
256 def testEpochNonInteger(self):
257 """Version should fail with non-integral epoch."""
258- from lp.archivepublisher.debversion import Version, BadEpochError
259- self.assertRaises(BadEpochError, Version, "1.0:1")
260+ v = Version("1.0:1")
261+ self.assertEquals("1.0:1", v.upstream_version)
262
263 def testEpochNonNegative(self):
264 """Version should fail with a negative epoch."""
265- from lp.archivepublisher.debversion import Version, BadEpochError
266- self.assertRaises(BadEpochError, Version, "-1:1")
267+ self.assertRaises(VersionError, Version, "-1:1")
268
269 def testUpstreamNotEmpty(self):
270 """Version should fail with empty upstream."""
271- from lp.archivepublisher.debversion import Version, BadUpstreamError
272 self.assertRaises(BadUpstreamError, Version, "1:-1")
273
274 def testUpstreamNonDigitStart(self):
275 """Version should fail when upstream doesn't start with a digit."""
276- from lp.archivepublisher.debversion import Version, BadUpstreamError
277 self.assertRaises(BadUpstreamError, Version, "a1")
278
279 def testUpstreamInvalid(self):
280 """Version should fail when upstream contains a bad character."""
281- from lp.archivepublisher.debversion import Version, BadUpstreamError
282- self.assertRaises(BadUpstreamError, Version, "1!0")
283+ self.assertRaises(VersionError, Version, "1!0")
284
285 def testRevisionNotEmpty(self):
286- """Version should fail with empty revision."""
287- from lp.archivepublisher.debversion import Version, BadRevisionError
288- self.assertRaises(BadRevisionError, Version, "1-")
289+ """Version should not allow an empty revision."""
290+ v = Version("1-")
291+ self.assertEquals("1-", v.upstream_version)
292+ self.assertEquals(None, v.debian_version)
293
294 def testRevisionInvalid(self):
295 """Version should fail when revision contains a bad character."""
296- from lp.archivepublisher.debversion import Version, BadRevisionError
297- self.assertRaises(BadRevisionError, Version, "1-!")
298+ self.assertRaises(VersionError, Version, "1-!")
299
300 def testValues(self):
301 """Version should give same input as output."""
302- from lp.archivepublisher.debversion import Version
303 for value in self.VALUES:
304 result = str(Version(value))
305 self.assertEquals(value, result)
306
307 def testComparisons(self):
308 """Sample Version comparisons should pass."""
309- from lp.archivepublisher.debversion import Version
310 for x, y in self.COMPARISONS:
311 self.failUnless(Version(x) < Version(y))
312
313 def testNullEpochIsZero(self):
314 """Version should treat an omitted epoch as a zero one."""
315- from lp.archivepublisher.debversion import Version
316- self.failUnless(Version("1.0") == Version("0:1.0"))
317-
318- def testNullRevisionIsZero(self):
319- """Version should treat an omitted revision as a zero one.
320-
321- NOTE: This isn't what Policy says! Policy says that an omitted
322- revision should compare less than the presence of one, whatever
323- its value.
324-
325- The implementation (dpkg) disagrees, and considers an omitted
326- revision equal to a zero one. I'm obviously biased as to which
327- this module obeys.
328+ self.assertEquals(Version("1.0"), Version("0:1.0"))
329+
330+ def notestNullRevisionIsZero(self):
331+ """Version should treat an omitted revision as being equal to zero.
332 """
333- from lp.archivepublisher.debversion import Version
334- self.failUnless(Version("1.0") == Version("1.0-0"))
335-
336- def testWithoutEpoch(self):
337- """Version.without_epoch returns version without epoch."""
338- from lp.archivepublisher.debversion import Version
339- self.assertEquals(Version("1:2.0").without_epoch, "2.0")
340-
341-
342-class Strcut(unittest.TestCase):
343- def testNoMatch(self):
344- """str_cut works when initial characters aren't accepted."""
345- from lp.archivepublisher.debversion import strcut
346- self.assertEquals(strcut("foo", 0, "gh"), ( "", 0 ))
347-
348- def testSingleMatch(self):
349- """str_cut matches single initial character."""
350- from lp.archivepublisher.debversion import strcut
351- self.assertEquals(strcut("foo", 0, "fgh"), ( "f", 1 ))
352-
353- def testMultipleMatch(self):
354- """str_cut matches multiple initial characters."""
355- from lp.archivepublisher.debversion import strcut
356- self.assertEquals(strcut("foobar", 0, "fo"), ( "foo", 3 ))
357-
358- def testCompleteMatch(self):
359- """str_cut works when all characters match."""
360- from lp.archivepublisher.debversion import strcut
361- self.assertEquals(strcut("foo", 0, "fo"), ( "foo", 3 ))
362-
363- def testNonMiddleMatch(self):
364- """str_cut doesn't match characters that aren't at the start."""
365- from lp.archivepublisher.debversion import strcut
366- self.assertEquals(strcut("barfooquux", 0, "fo"), ( "", 0 ))
367-
368- def testIndexMatch(self):
369- """str_cut matches characters from middle when index given."""
370- from lp.archivepublisher.debversion import strcut
371- self.assertEquals(strcut("barfooquux", 3, "fo"), ( "foo", 6 ))
372-
373-
374-class DebOrder(unittest.TestCase):
375- # Non-tilde characters in order
376- CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-.:"
377-
378- def testTilde(self):
379- """deb_order returns -1 for a tilde."""
380- from lp.archivepublisher.debversion import deb_order
381- self.assertEquals(deb_order("~", 0), -1)
382-
383- def testCharacters(self):
384- """deb_order returns positive for other characters."""
385- from lp.archivepublisher.debversion import deb_order
386- for char in self.CHARS:
387- self.failUnless(deb_order(char, 0) > 0)
388-
389- def testCharacterOrder(self):
390- """deb_order returns characters in correct order."""
391- from lp.archivepublisher.debversion import deb_order
392- last = None
393- for char in self.CHARS:
394- if last is not None:
395- self.failUnless(deb_order(char, 0) > deb_order(last, 0))
396- last = char
397-
398- def testOvershoot(self):
399- """deb_order returns zero if idx is longer than the string."""
400- from lp.archivepublisher.debversion import deb_order
401- self.assertEquals(deb_order("foo", 10), 0)
402-
403- def testEmptyString(self):
404- """deb_order returns zero if given empty string."""
405- from lp.archivepublisher.debversion import deb_order
406- self.assertEquals(deb_order("", 0), 0)
407-
408-
409-class DebCmpStr(unittest.TestCase):
410- # Sample strings
411- VALUES = (
412- "foo",
413- "FOO",
414- "Foo",
415- "foo+bar",
416- "foo-bar",
417- "foo.bar",
418- "foo:bar"
419- )
420-
421- # Non-letter characters in order
422- CHARS = "+-.:"
423-
424- def testEmptyStrings(self):
425- """deb_cmp_str returns zero when given empty strings."""
426- from lp.archivepublisher.debversion import deb_cmp_str
427- self.assertEquals(deb_cmp_str("", ""), 0)
428-
429- def testFirstEmptyString(self):
430- """deb_cmp_str returns -1 when first string is empty."""
431- from lp.archivepublisher.debversion import deb_cmp_str
432- self.assertEquals(deb_cmp_str("", "foo"), -1)
433-
434- def testSecondEmptyString(self):
435- """deb_cmp_str returns 1 when second string is empty."""
436- from lp.archivepublisher.debversion import deb_cmp_str
437- self.assertEquals(deb_cmp_str("foo", ""), 1)
438-
439- def testTildeEmptyString(self):
440- """deb_cmp_str returns -1 when tilde compared to empty string."""
441- from lp.archivepublisher.debversion import deb_cmp_str
442- self.assertEquals(deb_cmp_str("~", ""), -1)
443-
444- def testLongerFirstString(self):
445- """deb_cmp_str returns 1 when first string is longer."""
446- from lp.archivepublisher.debversion import deb_cmp_str
447- self.assertEquals(deb_cmp_str("foobar", "foo"), 1)
448-
449- def testLongerSecondString(self):
450- """deb_cmp_str returns -1 when second string is longer."""
451- from lp.archivepublisher.debversion import deb_cmp_str
452- self.assertEquals(deb_cmp_str("foo", "foobar"), -1)
453-
454- def testTildeTail(self):
455- """deb_cmp_str returns -1 when first string is longer by a tilde."""
456- from lp.archivepublisher.debversion import deb_cmp_str
457- self.assertEquals(deb_cmp_str("foo~", "foo"), -1)
458-
459- def testIdenticalString(self):
460- """deb_cmp_str returns 0 when given identical strings."""
461- from lp.archivepublisher.debversion import deb_cmp_str
462- for value in self.VALUES:
463- self.assertEquals(deb_cmp_str(value, value), 0)
464-
465- def testNonIdenticalString(self):
466- """deb_cmp_str returns non-zero when given non-identical strings."""
467- from lp.archivepublisher.debversion import deb_cmp_str
468- last = self.VALUES[-1]
469- for value in self.VALUES:
470- self.assertNotEqual(deb_cmp_str(last, value), 0)
471- last = value
472-
473- def testIdenticalTilde(self):
474- """deb_cmp_str returns 0 when given identical tilded strings."""
475- from lp.archivepublisher.debversion import deb_cmp_str
476- self.assertEquals(deb_cmp_str("foo~", "foo~"), 0)
477-
478- def testUppercaseLetters(self):
479- """deb_cmp_str orders upper case letters in alphabetical order."""
480- from lp.archivepublisher.debversion import deb_cmp_str
481- last = "A"
482- for value in range(ord("B"), ord("Z")):
483- self.assertEquals(deb_cmp_str(last, chr(value)), -1)
484- last = chr(value)
485-
486- def testLowercaseLetters(self):
487- """deb_cmp_str orders lower case letters in alphabetical order."""
488- from lp.archivepublisher.debversion import deb_cmp_str
489- last = "a"
490- for value in range(ord("b"), ord("z")):
491- self.assertEquals(deb_cmp_str(last, chr(value)), -1)
492- last = chr(value)
493-
494- def testLowerGreaterThanUpper(self):
495- """deb_cmp_str orders lower case letters after upper case."""
496- from lp.archivepublisher.debversion import deb_cmp_str
497- self.assertEquals(deb_cmp_str("a", "Z"), 1)
498-
499- def testCharacters(self):
500- """deb_cmp_str orders characters in prescribed order."""
501- from lp.archivepublisher.debversion import deb_cmp_str
502- chars = list(self.CHARS)
503- last = chars.pop(0)
504- for char in chars:
505- self.assertEquals(deb_cmp_str(last, char), -1)
506- last = char
507-
508- def testCharactersGreaterThanLetters(self):
509- """deb_cmp_str orders characters above letters."""
510- from lp.archivepublisher.debversion import deb_cmp_str
511- self.assertEquals(deb_cmp_str(self.CHARS[0], "z"), 1)
512-
513-
514-class DebCmp(unittest.TestCase):
515- def testEmptyString(self):
516- """deb_cmp returns 0 for the empty string."""
517- from lp.archivepublisher.debversion import deb_cmp
518- self.assertEquals(deb_cmp("", ""), 0)
519-
520- def testStringCompare(self):
521- """deb_cmp compares initial string portions correctly."""
522- from lp.archivepublisher.debversion import deb_cmp
523- self.assertEquals(deb_cmp("a", "b"), -1)
524- self.assertEquals(deb_cmp("b", "a"), 1)
525-
526- def testNumericCompare(self):
527- """deb_cmp compares numeric portions correctly."""
528- from lp.archivepublisher.debversion import deb_cmp
529- self.assertEquals(deb_cmp("foo1", "foo2"), -1)
530- self.assertEquals(deb_cmp("foo2", "foo1"), 1)
531- self.assertEquals(deb_cmp("foo200", "foo5"), 1)
532-
533- def testMissingNumeric(self):
534- """deb_cmp treats missing numeric as zero."""
535- from lp.archivepublisher.debversion import deb_cmp
536- self.assertEquals(deb_cmp("foo", "foo0"), 0)
537- self.assertEquals(deb_cmp("foo", "foo1"), -1)
538- self.assertEquals(deb_cmp("foo1", "foo"), 1)
539-
540- def testEmptyStringPortion(self):
541- """deb_cmp works when string potion is empty."""
542- from lp.archivepublisher.debversion import deb_cmp
543- self.assertEquals(deb_cmp("100", "foo100"), -1)
544-
545-
546-def test_suite():
547- return unittest.TestLoader().loadTestsFromName(__name__)
548+ # XXX JRV 20100312: Disabled at the moment because this fails because of
549+ # a bug in apt. (http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=573592)
550+ self.assertEquals(Version("1.0"), Version("1.0-0"))
551
552=== modified file 'lib/lp/soyuz/scripts/buildd.py'
553--- lib/lp/soyuz/scripts/buildd.py 2010-05-20 14:28:51 +0000
554+++ lib/lp/soyuz/scripts/buildd.py 2010-06-18 10:12:40 +0000
555@@ -26,18 +26,6 @@
556 from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
557 from lp.soyuz.pas import BuildDaemonPackagesArchSpecific
558
559-# XXX cprov 2009-04-16: This function should live in
560-# lp.registry.interfaces.distroseries. It cannot be done right now
561-# because we haven't decided if archivepublisher.debversion will be
562-# released as FOSS yet.
563-def distroseries_sort_key(series):
564- """Sort `DistroSeries` by version.
565-
566- See `lp.archivepublisher.debversion.Version` for more
567- information.
568- """
569- return Version(series.version)
570-
571
572 class QueueBuilder(LaunchpadCronScript):
573
574@@ -219,7 +207,8 @@
575 raise LaunchpadScriptFailure("Could not find suite %s" % err)
576 distroseries_set.add(distroseries)
577
578- return sorted(distroseries_set, key=distroseries_sort_key)
579+ return sorted(distroseries_set,
580+ key=lambda series: Version(series.version))
581
582
583 class RetryDepwait(LaunchpadCronScript):