]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/test/test_version.py
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         (errors, extras) = cross_check({"foo": ("unparseable", "")}, [("foo", ("1.0", "", None))])
77         self.failUnlessEqual(extras, [])
78         self.failUnlessEqual(len(errors), 1)
79         self.failUnlessIn("by pkg_resources could not be parsed", errors[0])
80
81         (errors, extras) = cross_check({"foo": ("1.0", "")}, [("foo", ("unparseable", "", None))])
82         self.failUnlessEqual(extras, [])
83         self.failUnlessEqual(len(errors), 1)
84         self.failUnlessIn(") could not be parsed", errors[0])
85
86     def test_cross_check(self):
87         res = cross_check({}, [])
88         self.failUnlessEqual(res, ([], []))
89
90         res = cross_check({}, [("allmydata-tahoe", ("1.0", "", "blah"))])
91         self.failUnlessEqual(res, ([], []))
92
93         res = cross_check({"foo": ("unparseable", "")}, [])
94         self.failUnlessEqual(res, ([], [("foo", ("unparseable", "", "according to pkg_resources"))]))
95
96         res = cross_check({"argparse": ("unparseable", "")}, [])
97         self.failUnlessEqual(res, ([], []))
98
99         (errors, extras) = cross_check({}, [("foo", ("unparseable", "", None))])
100         self.failUnlessEqual(extras, [])
101         self.failUnlessEqual(len(errors), 1)
102         self.failUnlessIn("was not found by pkg_resources", errors[0])
103
104         res = cross_check({"distribute": ("1.0", "/somewhere")}, [("setuptools", ("2.0", "/somewhere", "distribute"))])
105         self.failUnlessEqual(res, ([], []))
106
107         (errors, extras) = cross_check({"distribute": ("1.0", "/somewhere")}, [("setuptools", ("2.0", "/somewhere", None))])
108         self.failUnlessEqual(extras, [])
109         self.failUnlessEqual(len(errors), 1)
110         self.failUnlessIn("location mismatch", errors[0])
111
112         (errors, extras) = cross_check({"distribute": ("1.0", "/somewhere")}, [("setuptools", ("2.0", "/somewhere_different", None))])
113         self.failUnlessEqual(extras, [])
114         self.failUnlessEqual(len(errors), 1)
115         self.failUnlessIn("location mismatch", errors[0])
116
117         res = cross_check({"zope.interface": ("1.0", "")}, [("zope.interface", ("unknown", "", None))])
118         self.failUnlessEqual(res, ([], []))
119
120         (errors, extras) = cross_check({"foo": ("1.0", "")}, [("foo", ("unknown", "", None))])
121         self.failUnlessEqual(extras, [])
122         self.failUnlessEqual(len(errors), 1)
123         self.failUnlessIn("could not find a version number", errors[0])
124
125         # When pkg_resources and import both find a package, there is only a warning if both
126         # the version and the path fail to match.
127
128         res = cross_check({"foo": ("1.0", "/somewhere")}, [("foo", ("2.0", "/somewhere", None))])
129         self.failUnlessEqual(res, ([], []))
130
131         res = cross_check({"foo": ("1.0", "/somewhere")}, [("foo", ("1.0", "/somewhere_different", None))])
132         self.failUnlessEqual(res, ([], []))
133
134         res = cross_check({"foo": ("1.0-r123", "/somewhere")}, [("foo", ("1.0.post123", "/somewhere_different", None))])
135         self.failUnlessEqual(res, ([], []))
136
137         (errors, extras) = cross_check({"foo": ("1.0", "/somewhere")}, [("foo", ("2.0", "/somewhere_different", None))])
138         self.failUnlessEqual(extras, [])
139         self.failUnlessEqual(len(errors), 1)
140         self.failUnlessIn("but version '2.0'", errors[0])
141
142     def test_extract_openssl_version(self):
143         self.failUnlessEqual(extract_openssl_version(MockSSL("")),
144                                                      ("", None, None))
145         self.failUnlessEqual(extract_openssl_version(MockSSL("NotOpenSSL a.b.c foo")),
146                                                      ("NotOpenSSL", None, "a.b.c foo"))
147         self.failUnlessEqual(extract_openssl_version(MockSSL("OpenSSL a.b.c")),
148                                                      ("a.b.c", None, None))
149         self.failUnlessEqual(extract_openssl_version(MockSSL("OpenSSL 1.0.1e 11 Feb 2013")),
150                                                      ("1.0.1e", None, "11 Feb 2013"))
151         self.failUnlessEqual(extract_openssl_version(MockSSL("OpenSSL 1.0.1e 11 Feb 2013", compiled_without_heartbeats=True)),
152                                                      ("1.0.1e", None, "11 Feb 2013, no heartbeats"))
153
154
155 # based on https://bitbucket.org/tarek/distutilsversion/src/17df9a7d96ef/test_verlib.py
156
157 class VersionTestCase(unittest.TestCase):
158     versions = ((V('1.0'), '1.0'),
159                 (V('1.1'), '1.1'),
160                 (V('1.2.3'), '1.2.3'),
161                 (V('1.2'), '1.2'),
162                 (V('1.2.3a4'), '1.2.3a4'),
163                 (V('1.2c4'), '1.2c4'),
164                 (V('1.2.3.4'), '1.2.3.4'),
165                 (V('1.2.3.4.0b3'), '1.2.3.4b3'),
166                 (V('1.2.0.0.0'), '1.2'),
167                 (V('1.0.dev345'), '1.0.dev345'),
168                 (V('1.0.post456.dev623'), '1.0.post456.dev623'))
169
170     def test_basic_versions(self):
171         for v, s in self.versions:
172             self.failUnlessEqual(str(v), s)
173
174     def test_from_parts(self):
175         for v, s in self.versions:
176             parts = v.parts
177             v2 = V.from_parts(*parts)
178             self.failUnlessEqual(v, v2)
179             self.failUnlessEqual(str(v), str(v2))
180
181     def test_irrational_versions(self):
182         irrational = ('1', '1.2a', '1.2.3b', '1.02', '1.2a03',
183                       '1.2a3.04', '1.2.dev.2', '1.2dev', '1.2.dev',
184                       '1.2.dev2.post2', '1.2.post2.dev3.post4')
185
186         for s in irrational:
187             self.failUnlessRaises(IrrationalVersionError, V, s)
188
189     def test_comparison(self):
190         self.failUnlessRaises(TypeError, lambda: V('1.2.0') == '1.2')
191
192         self.failUnlessEqual(V('1.2.0'), V('1.2'))
193         self.failIfEqual(V('1.2.0'), V('1.2.3'))
194         self.failUnless(V('1.2.0') < V('1.2.3'))
195         self.failUnless(V('1.0') > V('1.0b2'))
196         self.failUnless(V('1.0') > V('1.0c2') > V('1.0c1') > V('1.0b2') > V('1.0b1')
197                         > V('1.0a2') > V('1.0a1'))
198         self.failUnless(V('1.0.0') > V('1.0.0c2') > V('1.0.0c1') > V('1.0.0b2') > V('1.0.0b1')
199                         > V('1.0.0a2') > V('1.0.0a1'))
200
201         self.failUnless(V('1.0') < V('1.0.post456.dev623'))
202         self.failUnless(V('1.0.post456.dev623') < V('1.0.post456')  < V('1.0.post1234'))
203
204         self.failUnless(V('1.0a1')
205                         < V('1.0a2.dev456')
206                         < V('1.0a2')
207                         < V('1.0a2.1.dev456')  # e.g. need to do a quick post release on 1.0a2
208                         < V('1.0a2.1')
209                         < V('1.0b1.dev456')
210                         < V('1.0b2')
211                         < V('1.0c1')
212                         < V('1.0c2.dev456')
213                         < V('1.0c2')
214                         < V('1.0.dev7')
215                         < V('1.0.dev18')
216                         < V('1.0.dev456')
217                         < V('1.0.dev1234')
218                         < V('1.0')
219                         < V('1.0.post456.dev623')  # development version of a post release
220                         < V('1.0.post456'))
221
222     def test_suggest_normalized_version(self):
223         self.failUnlessEqual(suggest('1.0'), '1.0')
224         self.failUnlessEqual(suggest('1.0-alpha1'), '1.0a1')
225         self.failUnlessEqual(suggest('1.0c2'), '1.0c2')
226         self.failUnlessEqual(suggest('walla walla washington'), None)
227         self.failUnlessEqual(suggest('2.4c1'), '2.4c1')
228
229         # from setuptools
230         self.failUnlessEqual(suggest('0.4a1.r10'), '0.4a1.post10')
231         self.failUnlessEqual(suggest('0.7a1dev-r66608'), '0.7a1.dev66608')
232         self.failUnlessEqual(suggest('0.6a9.dev-r41475'), '0.6a9.dev41475')
233         self.failUnlessEqual(suggest('2.4preview1'), '2.4c1')
234         self.failUnlessEqual(suggest('2.4pre1') , '2.4c1')
235         self.failUnlessEqual(suggest('2.1-rc2'), '2.1c2')
236
237         # from pypi
238         self.failUnlessEqual(suggest('0.1dev'), '0.1.dev0')
239         self.failUnlessEqual(suggest('0.1.dev'), '0.1.dev0')
240
241         # we want to be able to parse Twisted
242         # development versions are like post releases in Twisted
243         self.failUnlessEqual(suggest('9.0.0+r2363'), '9.0.0.post2363')
244
245         # pre-releases are using markers like "pre1"
246         self.failUnlessEqual(suggest('9.0.0pre1'), '9.0.0c1')
247
248         # we want to be able to parse Tcl-TK
249         # they use "p1" "p2" for post releases
250         self.failUnlessEqual(suggest('1.4p1'), '1.4.post1')
251
252         # from darcsver
253         self.failUnlessEqual(suggest('1.8.1-r4956'), '1.8.1.post4956')
254
255         # zetuptoolz
256         self.failUnlessEqual(suggest('0.6c16dev3'), '0.6c16.dev3')