]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/test/test_version.py
fc64277724c0d3ae258feddfd3149169c9dcdb9e
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / test / test_version.py
1
2 from pkg_resources import Requirement
3
4 from twisted.trial import unittest
5
6 from allmydata import check_requirement, cross_check, PackagingError
7 from allmydata.util.verlib import NormalizedVersion as V, \
8                                   IrrationalVersionError, \
9                                   suggest_normalized_version as suggest
10
11
12 class CheckRequirement(unittest.TestCase):
13     def test_check_requirement(self):
14         self._check_success("setuptools >= 0.6c6", {"setuptools": ("0.6", "", None)})
15         self._check_success("setuptools >= 0.6c6", {"setuptools": ("0.6", "", "distribute")})
16         self._check_success("pycrypto >= 2.1.0, != 2.2, != 2.4", {"pycrypto": ("2.1.0", "", None)})
17         self._check_success("pycrypto >= 2.1.0, != 2.2, != 2.4", {"pycrypto": ("2.3.0", "", None)})
18         self._check_success("pycrypto >= 2.1.0, != 2.2, != 2.4", {"pycrypto": ("2.4.1", "", None)})
19         self._check_success("Twisted >= 11.0.0, <= 12.2.0", {"Twisted": ("11.0.0", "", None)})
20         self._check_success("Twisted >= 11.0.0, <= 12.2.0", {"Twisted": ("12.2.0", "", None)})
21
22         self._check_success("zope.interface", {"zope.interface": ("unknown", "", None)})
23         self._check_success("mock", {"mock": ("0.6.0", "", None)})
24         self._check_success("foo >= 1.0", {"foo": ("1.0", "", None), "bar": ("2.0", "", None)})
25
26         self._check_success("foolscap[secure_connections] >= 0.6.0", {"foolscap": ("0.7.0", "", None)})
27
28         try:
29             self._check_success("foolscap[secure_connections] >= 0.6.0", {"foolscap": ("0.6.1+", "", None)})
30             # succeeding is ok
31         except PackagingError, e:
32             self.failUnlessIn("could not parse", str(e))
33
34         self._check_failure("foolscap[secure_connections] >= 0.6.0", {"foolscap": ("0.5.1", "", None)})
35         self._check_failure("pycrypto >= 2.1.0, != 2.2, != 2.4", {"pycrypto": ("2.2.0", "", None)})
36         self._check_failure("pycrypto >= 2.1.0, != 2.2, != 2.4", {"pycrypto": ("2.0.0", "", None)})
37         self._check_failure("Twisted >= 11.0.0, <= 12.2.0", {"Twisted": ("10.2.0", "", None)})
38         self._check_failure("Twisted >= 11.0.0, <= 12.2.0", {"Twisted": ("13.0.0", "", None)})
39         self._check_failure("foo >= 1.0", {})
40         self._check_failure("foo >= 1.0", {"foo": ("irrational", "", None)})
41
42         self.failUnlessRaises(ImportError, check_requirement,
43                               "foo >= 1.0", {"foo": (None, None, "foomodule")})
44
45     def _check_success(self, req, vers_and_locs):
46         check_requirement(req, vers_and_locs)
47
48         for pkg, ver in vers_and_locs.items():
49             self.failUnless(ver[0] in Requirement.parse(req), str((ver, req)))
50
51     def _check_failure(self, req, vers_and_locs):
52         self.failUnlessRaises(PackagingError, check_requirement, req, vers_and_locs)
53
54         for pkg, ver in vers_and_locs.items():
55             self.failIf(ver[0] in Requirement.parse(req), str((ver, req)))
56
57     def test_cross_check_ticket_1355(self):
58         # The bug in #1355 is triggered when a version string from either pkg_resources or import
59         # is not parseable at all by normalized_version.
60
61         res = cross_check({"foo": ("unparseable", "")}, [("foo", ("1.0", "", None))])
62         self.failUnlessEqual(len(res), 1)
63         self.failUnlessIn("by pkg_resources could not be parsed", res[0])
64
65         res = cross_check({"foo": ("1.0", "")}, [("foo", ("unparseable", "", None))])
66         self.failUnlessEqual(len(res), 1)
67         self.failUnlessIn(") could not be parsed", res[0])
68
69     def test_cross_check(self):
70         res = cross_check({}, [])
71         self.failUnlessEqual(res, [])
72
73         res = cross_check({}, [("allmydata-tahoe", ("1.0", "", "blah"))])
74         self.failUnlessEqual(res, [])
75
76         res = cross_check({"foo": ("unparseable", "")}, [])
77         self.failUnlessEqual(len(res), 1)
78         self.failUnlessIn("not found by import", res[0])
79
80         res = cross_check({"argparse": ("unparseable", "")}, [])
81         self.failUnlessEqual(len(res), 0)
82
83         res = cross_check({}, [("foo", ("unparseable", "", None))])
84         self.failUnlessEqual(len(res), 1)
85         self.failUnlessIn("not found by pkg_resources", res[0])
86
87         res = cross_check({"distribute": ("1.0", "/somewhere")}, [("setuptools", ("2.0", "/somewhere", "distribute"))])
88         self.failUnlessEqual(len(res), 0)
89
90         res = cross_check({"distribute": ("1.0", "/somewhere")}, [("setuptools", ("2.0", "/somewhere", None))])
91         self.failUnlessEqual(len(res), 1)
92         self.failUnlessIn("location mismatch", res[0])
93
94         res = cross_check({"distribute": ("1.0", "/somewhere")}, [("setuptools", ("2.0", "/somewhere_different", None))])
95         self.failUnlessEqual(len(res), 1)
96         self.failUnlessIn("location mismatch", res[0])
97
98         res = cross_check({"zope.interface": ("1.0", "")}, [("zope.interface", ("unknown", "", None))])
99         self.failUnlessEqual(len(res), 0)
100
101         res = cross_check({"foo": ("1.0", "")}, [("foo", ("unknown", "", None))])
102         self.failUnlessEqual(len(res), 1)
103         self.failUnlessIn("could not find a version number", res[0])
104
105         # When pkg_resources and import both find a package, there is only a warning if both
106         # the version and the path fail to match.
107
108         res = cross_check({"foo": ("1.0", "/somewhere")}, [("foo", ("2.0", "/somewhere", None))])
109         self.failUnlessEqual(len(res), 0)
110
111         res = cross_check({"foo": ("1.0", "/somewhere")}, [("foo", ("1.0", "/somewhere_different", None))])
112         self.failUnlessEqual(len(res), 0)
113
114         res = cross_check({"foo": ("1.0-r123", "/somewhere")}, [("foo", ("1.0.post123", "/somewhere_different", None))])
115         self.failUnlessEqual(len(res), 0)
116
117         res = cross_check({"foo": ("1.0", "/somewhere")}, [("foo", ("2.0", "/somewhere_different", None))])
118         self.failUnlessEqual(len(res), 1)
119         self.failUnlessIn("but version '2.0'", res[0])
120
121
122 # based on https://bitbucket.org/tarek/distutilsversion/src/17df9a7d96ef/test_verlib.py
123
124 class VersionTestCase(unittest.TestCase):
125     versions = ((V('1.0'), '1.0'),
126                 (V('1.1'), '1.1'),
127                 (V('1.2.3'), '1.2.3'),
128                 (V('1.2'), '1.2'),
129                 (V('1.2.3a4'), '1.2.3a4'),
130                 (V('1.2c4'), '1.2c4'),
131                 (V('1.2.3.4'), '1.2.3.4'),
132                 (V('1.2.3.4.0b3'), '1.2.3.4b3'),
133                 (V('1.2.0.0.0'), '1.2'),
134                 (V('1.0.dev345'), '1.0.dev345'),
135                 (V('1.0.post456.dev623'), '1.0.post456.dev623'))
136
137     def test_basic_versions(self):
138         for v, s in self.versions:
139             self.failUnlessEqual(str(v), s)
140
141     def test_from_parts(self):
142         for v, s in self.versions:
143             parts = v.parts
144             v2 = V.from_parts(*parts)
145             self.failUnlessEqual(v, v2)
146             self.failUnlessEqual(str(v), str(v2))
147
148     def test_irrational_versions(self):
149         irrational = ('1', '1.2a', '1.2.3b', '1.02', '1.2a03',
150                       '1.2a3.04', '1.2.dev.2', '1.2dev', '1.2.dev',
151                       '1.2.dev2.post2', '1.2.post2.dev3.post4')
152
153         for s in irrational:
154             self.failUnlessRaises(IrrationalVersionError, V, s)
155
156     def test_comparison(self):
157         self.failUnlessRaises(TypeError, lambda: V('1.2.0') == '1.2')
158
159         self.failUnlessEqual(V('1.2.0'), V('1.2'))
160         self.failIfEqual(V('1.2.0'), V('1.2.3'))
161         self.failUnless(V('1.2.0') < V('1.2.3'))
162         self.failUnless(V('1.0') > V('1.0b2'))
163         self.failUnless(V('1.0') > V('1.0c2') > V('1.0c1') > V('1.0b2') > V('1.0b1')
164                         > V('1.0a2') > V('1.0a1'))
165         self.failUnless(V('1.0.0') > V('1.0.0c2') > V('1.0.0c1') > V('1.0.0b2') > V('1.0.0b1')
166                         > V('1.0.0a2') > V('1.0.0a1'))
167
168         self.failUnless(V('1.0') < V('1.0.post456.dev623'))
169         self.failUnless(V('1.0.post456.dev623') < V('1.0.post456')  < V('1.0.post1234'))
170
171         self.failUnless(V('1.0a1')
172                         < V('1.0a2.dev456')
173                         < V('1.0a2')
174                         < V('1.0a2.1.dev456')  # e.g. need to do a quick post release on 1.0a2
175                         < V('1.0a2.1')
176                         < V('1.0b1.dev456')
177                         < V('1.0b2')
178                         < V('1.0c1')
179                         < V('1.0c2.dev456')
180                         < V('1.0c2')
181                         < V('1.0.dev7')
182                         < V('1.0.dev18')
183                         < V('1.0.dev456')
184                         < V('1.0.dev1234')
185                         < V('1.0')
186                         < V('1.0.post456.dev623')  # development version of a post release
187                         < V('1.0.post456'))
188
189     def test_suggest_normalized_version(self):
190         self.failUnlessEqual(suggest('1.0'), '1.0')
191         self.failUnlessEqual(suggest('1.0-alpha1'), '1.0a1')
192         self.failUnlessEqual(suggest('1.0c2'), '1.0c2')
193         self.failUnlessEqual(suggest('walla walla washington'), None)
194         self.failUnlessEqual(suggest('2.4c1'), '2.4c1')
195
196         # from setuptools
197         self.failUnlessEqual(suggest('0.4a1.r10'), '0.4a1.post10')
198         self.failUnlessEqual(suggest('0.7a1dev-r66608'), '0.7a1.dev66608')
199         self.failUnlessEqual(suggest('0.6a9.dev-r41475'), '0.6a9.dev41475')
200         self.failUnlessEqual(suggest('2.4preview1'), '2.4c1')
201         self.failUnlessEqual(suggest('2.4pre1') , '2.4c1')
202         self.failUnlessEqual(suggest('2.1-rc2'), '2.1c2')
203
204         # from pypi
205         self.failUnlessEqual(suggest('0.1dev'), '0.1.dev0')
206         self.failUnlessEqual(suggest('0.1.dev'), '0.1.dev0')
207
208         # we want to be able to parse Twisted
209         # development versions are like post releases in Twisted
210         self.failUnlessEqual(suggest('9.0.0+r2363'), '9.0.0.post2363')
211
212         # pre-releases are using markers like "pre1"
213         self.failUnlessEqual(suggest('9.0.0pre1'), '9.0.0c1')
214
215         # we want to be able to parse Tcl-TK
216         # they use "p1" "p2" for post releases
217         self.failUnlessEqual(suggest('1.4p1'), '1.4.post1')
218
219         # from darcsver
220         self.failUnlessEqual(suggest('1.8.1-r4956'), '1.8.1.post4956')
221
222         # zetuptoolz
223         self.failUnlessEqual(suggest('0.6c16dev3'), '0.6c16.dev3')