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