##// END OF EJS Templates
tests: use a decorator for hghave checks
Matt Mackall -
r22093:45611a30 default
parent child Browse files
Show More
@@ -1,333 +1,339 b''
1 import os, stat
1 import os, stat
2 import re
2 import re
3 import sys
3 import sys
4 import tempfile
4 import tempfile
5
5
6 tempprefix = 'hg-hghave-'
6 tempprefix = 'hg-hghave-'
7
7
8 checks = {
9 "true": (lambda: True, "yak shaving"),
10 "false": (lambda: False, "nail clipper"),
11 }
12
13 def check(name, desc):
14 def decorator(func):
15 checks[name] = (func, desc)
16 return func
17 return decorator
18
8 def matchoutput(cmd, regexp, ignorestatus=False):
19 def matchoutput(cmd, regexp, ignorestatus=False):
9 """Return True if cmd executes successfully and its output
20 """Return True if cmd executes successfully and its output
10 is matched by the supplied regular expression.
21 is matched by the supplied regular expression.
11 """
22 """
12 r = re.compile(regexp)
23 r = re.compile(regexp)
13 fh = os.popen(cmd)
24 fh = os.popen(cmd)
14 s = fh.read()
25 s = fh.read()
15 try:
26 try:
16 ret = fh.close()
27 ret = fh.close()
17 except IOError:
28 except IOError:
18 # Happen in Windows test environment
29 # Happen in Windows test environment
19 ret = 1
30 ret = 1
20 return (ignorestatus or ret is None) and r.search(s)
31 return (ignorestatus or ret is None) and r.search(s)
21
32
33 @check("baz", "GNU Arch baz client")
22 def has_baz():
34 def has_baz():
23 return matchoutput('baz --version 2>&1', r'baz Bazaar version')
35 return matchoutput('baz --version 2>&1', r'baz Bazaar version')
24
36
37 @check("bzr", "Canonical's Bazaar client")
25 def has_bzr():
38 def has_bzr():
26 try:
39 try:
27 import bzrlib
40 import bzrlib
28 return bzrlib.__doc__ is not None
41 return bzrlib.__doc__ is not None
29 except ImportError:
42 except ImportError:
30 return False
43 return False
31
44
45 @check("bzr114", "Canonical's Bazaar client >= 1.14")
32 def has_bzr114():
46 def has_bzr114():
33 try:
47 try:
34 import bzrlib
48 import bzrlib
35 return (bzrlib.__doc__ is not None
49 return (bzrlib.__doc__ is not None
36 and bzrlib.version_info[:2] >= (1, 14))
50 and bzrlib.version_info[:2] >= (1, 14))
37 except ImportError:
51 except ImportError:
38 return False
52 return False
39
53
54 @check("cvs", "cvs client/server")
40 def has_cvs():
55 def has_cvs():
41 re = r'Concurrent Versions System.*?server'
56 re = r'Concurrent Versions System.*?server'
42 return matchoutput('cvs --version 2>&1', re) and not has_msys()
57 return matchoutput('cvs --version 2>&1', re) and not has_msys()
43
58
59 @check("cvs112", "cvs client/server >= 1.12")
44 def has_cvs112():
60 def has_cvs112():
45 re = r'Concurrent Versions System \(CVS\) 1.12.*?server'
61 re = r'Concurrent Versions System \(CVS\) 1.12.*?server'
46 return matchoutput('cvs --version 2>&1', re) and not has_msys()
62 return matchoutput('cvs --version 2>&1', re) and not has_msys()
47
63
64 @check("darcs", "darcs client")
48 def has_darcs():
65 def has_darcs():
49 return matchoutput('darcs --version', r'2\.[2-9]', True)
66 return matchoutput('darcs --version', r'2\.[2-9]', True)
50
67
68 @check("mtn", "monotone client (>= 1.0)")
51 def has_mtn():
69 def has_mtn():
52 return matchoutput('mtn --version', r'monotone', True) and not matchoutput(
70 return matchoutput('mtn --version', r'monotone', True) and not matchoutput(
53 'mtn --version', r'monotone 0\.', True)
71 'mtn --version', r'monotone 0\.', True)
54
72
73 @check("eol-in-paths", "end-of-lines in paths")
55 def has_eol_in_paths():
74 def has_eol_in_paths():
56 try:
75 try:
57 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
76 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
58 os.close(fd)
77 os.close(fd)
59 os.remove(path)
78 os.remove(path)
60 return True
79 return True
61 except (IOError, OSError):
80 except (IOError, OSError):
62 return False
81 return False
63
82
83 @check("execbit", "executable bit")
64 def has_executablebit():
84 def has_executablebit():
65 try:
85 try:
66 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
86 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
67 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
87 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
68 try:
88 try:
69 os.close(fh)
89 os.close(fh)
70 m = os.stat(fn).st_mode & 0777
90 m = os.stat(fn).st_mode & 0777
71 new_file_has_exec = m & EXECFLAGS
91 new_file_has_exec = m & EXECFLAGS
72 os.chmod(fn, m ^ EXECFLAGS)
92 os.chmod(fn, m ^ EXECFLAGS)
73 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
93 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
74 finally:
94 finally:
75 os.unlink(fn)
95 os.unlink(fn)
76 except (IOError, OSError):
96 except (IOError, OSError):
77 # we don't care, the user probably won't be able to commit anyway
97 # we don't care, the user probably won't be able to commit anyway
78 return False
98 return False
79 return not (new_file_has_exec or exec_flags_cannot_flip)
99 return not (new_file_has_exec or exec_flags_cannot_flip)
80
100
101 @check("icasefs", "case insensitive file system")
81 def has_icasefs():
102 def has_icasefs():
82 # Stolen from mercurial.util
103 # Stolen from mercurial.util
83 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
104 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
84 os.close(fd)
105 os.close(fd)
85 try:
106 try:
86 s1 = os.stat(path)
107 s1 = os.stat(path)
87 d, b = os.path.split(path)
108 d, b = os.path.split(path)
88 p2 = os.path.join(d, b.upper())
109 p2 = os.path.join(d, b.upper())
89 if path == p2:
110 if path == p2:
90 p2 = os.path.join(d, b.lower())
111 p2 = os.path.join(d, b.lower())
91 try:
112 try:
92 s2 = os.stat(p2)
113 s2 = os.stat(p2)
93 return s2 == s1
114 return s2 == s1
94 except OSError:
115 except OSError:
95 return False
116 return False
96 finally:
117 finally:
97 os.remove(path)
118 os.remove(path)
98
119
120 @check("fifo", "named pipes")
99 def has_fifo():
121 def has_fifo():
100 if getattr(os, "mkfifo", None) is None:
122 if getattr(os, "mkfifo", None) is None:
101 return False
123 return False
102 name = tempfile.mktemp(dir='.', prefix=tempprefix)
124 name = tempfile.mktemp(dir='.', prefix=tempprefix)
103 try:
125 try:
104 os.mkfifo(name)
126 os.mkfifo(name)
105 os.unlink(name)
127 os.unlink(name)
106 return True
128 return True
107 except OSError:
129 except OSError:
108 return False
130 return False
109
131
132 @check("killdaemons", 'killdaemons.py support')
110 def has_killdaemons():
133 def has_killdaemons():
111 return True
134 return True
112
135
136 @check("cacheable", "cacheable filesystem")
113 def has_cacheable_fs():
137 def has_cacheable_fs():
114 from mercurial import util
138 from mercurial import util
115
139
116 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
140 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
117 os.close(fd)
141 os.close(fd)
118 try:
142 try:
119 return util.cachestat(path).cacheable()
143 return util.cachestat(path).cacheable()
120 finally:
144 finally:
121 os.remove(path)
145 os.remove(path)
122
146
147 @check("lsprof", "python lsprof module")
123 def has_lsprof():
148 def has_lsprof():
124 try:
149 try:
125 import _lsprof
150 import _lsprof
126 return True
151 return True
127 except ImportError:
152 except ImportError:
128 return False
153 return False
129
154
155 @check("gettext", "GNU Gettext (msgfmt)")
130 def has_gettext():
156 def has_gettext():
131 return matchoutput('msgfmt --version', 'GNU gettext-tools')
157 return matchoutput('msgfmt --version', 'GNU gettext-tools')
132
158
159 @check("git", "git command line client")
133 def has_git():
160 def has_git():
134 return matchoutput('git --version 2>&1', r'^git version')
161 return matchoutput('git --version 2>&1', r'^git version')
135
162
163 @check("docutils", "Docutils text processing library")
136 def has_docutils():
164 def has_docutils():
137 try:
165 try:
138 from docutils.core import publish_cmdline
166 from docutils.core import publish_cmdline
139 return True
167 return True
140 except ImportError:
168 except ImportError:
141 return False
169 return False
142
170
143 def getsvnversion():
171 def getsvnversion():
144 m = matchoutput('svn --version --quiet 2>&1', r'^(\d+)\.(\d+)')
172 m = matchoutput('svn --version --quiet 2>&1', r'^(\d+)\.(\d+)')
145 if not m:
173 if not m:
146 return (0, 0)
174 return (0, 0)
147 return (int(m.group(1)), int(m.group(2)))
175 return (int(m.group(1)), int(m.group(2)))
148
176
177 @check("svn15", "subversion client and admin tools >= 1.5")
149 def has_svn15():
178 def has_svn15():
150 return getsvnversion() >= (1, 5)
179 return getsvnversion() >= (1, 5)
151
180
181 @check("svn13", "subversion client and admin tools >= 1.3")
152 def has_svn13():
182 def has_svn13():
153 return getsvnversion() >= (1, 3)
183 return getsvnversion() >= (1, 3)
154
184
185 @check("svn", "subversion client and admin tools")
155 def has_svn():
186 def has_svn():
156 return matchoutput('svn --version 2>&1', r'^svn, version') and \
187 return matchoutput('svn --version 2>&1', r'^svn, version') and \
157 matchoutput('svnadmin --version 2>&1', r'^svnadmin, version')
188 matchoutput('svnadmin --version 2>&1', r'^svnadmin, version')
158
189
190 @check("svn-bindings", "subversion python bindings")
159 def has_svn_bindings():
191 def has_svn_bindings():
160 try:
192 try:
161 import svn.core
193 import svn.core
162 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
194 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
163 if version < (1, 4):
195 if version < (1, 4):
164 return False
196 return False
165 return True
197 return True
166 except ImportError:
198 except ImportError:
167 return False
199 return False
168
200
201 @check("p4", "Perforce server and client")
169 def has_p4():
202 def has_p4():
170 return (matchoutput('p4 -V', r'Rev\. P4/') and
203 return (matchoutput('p4 -V', r'Rev\. P4/') and
171 matchoutput('p4d -V', r'Rev\. P4D/'))
204 matchoutput('p4d -V', r'Rev\. P4D/'))
172
205
206 @check("symlink", "symbolic links")
173 def has_symlink():
207 def has_symlink():
174 if getattr(os, "symlink", None) is None:
208 if getattr(os, "symlink", None) is None:
175 return False
209 return False
176 name = tempfile.mktemp(dir='.', prefix=tempprefix)
210 name = tempfile.mktemp(dir='.', prefix=tempprefix)
177 try:
211 try:
178 os.symlink(".", name)
212 os.symlink(".", name)
179 os.unlink(name)
213 os.unlink(name)
180 return True
214 return True
181 except (OSError, AttributeError):
215 except (OSError, AttributeError):
182 return False
216 return False
183
217
218 @check("hardlink", "hardlinks")
184 def has_hardlink():
219 def has_hardlink():
185 from mercurial import util
220 from mercurial import util
186 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
221 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
187 os.close(fh)
222 os.close(fh)
188 name = tempfile.mktemp(dir='.', prefix=tempprefix)
223 name = tempfile.mktemp(dir='.', prefix=tempprefix)
189 try:
224 try:
190 try:
225 try:
191 util.oslink(fn, name)
226 util.oslink(fn, name)
192 os.unlink(name)
227 os.unlink(name)
193 return True
228 return True
194 except OSError:
229 except OSError:
195 return False
230 return False
196 finally:
231 finally:
197 os.unlink(fn)
232 os.unlink(fn)
198
233
234 @check("tla", "GNU Arch tla client")
199 def has_tla():
235 def has_tla():
200 return matchoutput('tla --version 2>&1', r'The GNU Arch Revision')
236 return matchoutput('tla --version 2>&1', r'The GNU Arch Revision')
201
237
238 @check("gpg", "gpg client")
202 def has_gpg():
239 def has_gpg():
203 return matchoutput('gpg --version 2>&1', r'GnuPG')
240 return matchoutput('gpg --version 2>&1', r'GnuPG')
204
241
242 @check("unix-permissions", "unix-style permissions")
205 def has_unix_permissions():
243 def has_unix_permissions():
206 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
244 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
207 try:
245 try:
208 fname = os.path.join(d, 'foo')
246 fname = os.path.join(d, 'foo')
209 for umask in (077, 007, 022):
247 for umask in (077, 007, 022):
210 os.umask(umask)
248 os.umask(umask)
211 f = open(fname, 'w')
249 f = open(fname, 'w')
212 f.close()
250 f.close()
213 mode = os.stat(fname).st_mode
251 mode = os.stat(fname).st_mode
214 os.unlink(fname)
252 os.unlink(fname)
215 if mode & 0777 != ~umask & 0666:
253 if mode & 0777 != ~umask & 0666:
216 return False
254 return False
217 return True
255 return True
218 finally:
256 finally:
219 os.rmdir(d)
257 os.rmdir(d)
220
258
259 @check("root", "root permissions")
221 def has_root():
260 def has_root():
222 return getattr(os, 'geteuid', None) and os.geteuid() == 0
261 return getattr(os, 'geteuid', None) and os.geteuid() == 0
223
262
263 @check("pyflakes", "Pyflakes python linter")
224 def has_pyflakes():
264 def has_pyflakes():
225 return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
265 return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
226 r"<stdin>:1: 're' imported but unused",
266 r"<stdin>:1: 're' imported but unused",
227 True)
267 True)
228
268
269 @check("pygments", "Pygments source highlighting library")
229 def has_pygments():
270 def has_pygments():
230 try:
271 try:
231 import pygments
272 import pygments
232 return True
273 return True
233 except ImportError:
274 except ImportError:
234 return False
275 return False
235
276
277 @check("python243", "python >= 2.4.3")
236 def has_python243():
278 def has_python243():
237 return sys.version_info >= (2, 4, 3)
279 return sys.version_info >= (2, 4, 3)
238
280
281 @check("outer-repo", "outer repo")
239 def has_outer_repo():
282 def has_outer_repo():
240 # failing for other reasons than 'no repo' imply that there is a repo
283 # failing for other reasons than 'no repo' imply that there is a repo
241 return not matchoutput('hg root 2>&1',
284 return not matchoutput('hg root 2>&1',
242 r'abort: no repository found', True)
285 r'abort: no repository found', True)
243
286
287 @check("ssl", "python >= 2.6 ssl module and python OpenSSL")
244 def has_ssl():
288 def has_ssl():
245 try:
289 try:
246 import ssl
290 import ssl
247 import OpenSSL
291 import OpenSSL
248 OpenSSL.SSL.Context
292 OpenSSL.SSL.Context
249 return True
293 return True
250 except ImportError:
294 except ImportError:
251 return False
295 return False
252
296
297 @check("windows", "Windows")
253 def has_windows():
298 def has_windows():
254 return os.name == 'nt'
299 return os.name == 'nt'
255
300
301 @check("system-sh", "system() uses sh")
256 def has_system_sh():
302 def has_system_sh():
257 return os.name != 'nt'
303 return os.name != 'nt'
258
304
305 @check("serve", "platform and python can manage 'hg serve -d'")
259 def has_serve():
306 def has_serve():
260 return os.name != 'nt' # gross approximation
307 return os.name != 'nt' # gross approximation
261
308
309 @check("test-repo", "running tests from repository")
262 def has_test_repo():
310 def has_test_repo():
263 t = os.environ["TESTDIR"]
311 t = os.environ["TESTDIR"]
264 return os.path.isdir(os.path.join(t, "..", ".hg"))
312 return os.path.isdir(os.path.join(t, "..", ".hg"))
265
313
314 @check("tic", "terminfo compiler and curses module")
266 def has_tic():
315 def has_tic():
267 try:
316 try:
268 import curses
317 import curses
269 curses.COLOR_BLUE
318 curses.COLOR_BLUE
270 return matchoutput('test -x "`which tic`"', '')
319 return matchoutput('test -x "`which tic`"', '')
271 except ImportError:
320 except ImportError:
272 return False
321 return False
273
322
323 @check("msys", "Windows with MSYS")
274 def has_msys():
324 def has_msys():
275 return os.getenv('MSYSTEM')
325 return os.getenv('MSYSTEM')
276
326
327 @check("aix", "AIX")
277 def has_aix():
328 def has_aix():
278 return sys.platform.startswith("aix")
329 return sys.platform.startswith("aix")
279
330
331 @check("absimport", "absolute_import in __future__")
280 def has_absimport():
332 def has_absimport():
281 import __future__
333 import __future__
282 from mercurial import util
334 from mercurial import util
283 return util.safehasattr(__future__, "absolute_import")
335 return util.safehasattr(__future__, "absolute_import")
284
336
337 @check("py3k", "running with Python 3.x")
285 def has_py3k():
338 def has_py3k():
286 return 3 == sys.version_info[0]
339 return 3 == sys.version_info[0]
287
288 checks = {
289 "true": (lambda: True, "yak shaving"),
290 "false": (lambda: False, "nail clipper"),
291 "baz": (has_baz, "GNU Arch baz client"),
292 "bzr": (has_bzr, "Canonical's Bazaar client"),
293 "bzr114": (has_bzr114, "Canonical's Bazaar client >= 1.14"),
294 "cacheable": (has_cacheable_fs, "cacheable filesystem"),
295 "cvs": (has_cvs, "cvs client/server"),
296 "cvs112": (has_cvs112, "cvs client/server >= 1.12"),
297 "darcs": (has_darcs, "darcs client"),
298 "docutils": (has_docutils, "Docutils text processing library"),
299 "eol-in-paths": (has_eol_in_paths, "end-of-lines in paths"),
300 "execbit": (has_executablebit, "executable bit"),
301 "fifo": (has_fifo, "named pipes"),
302 "gettext": (has_gettext, "GNU Gettext (msgfmt)"),
303 "git": (has_git, "git command line client"),
304 "gpg": (has_gpg, "gpg client"),
305 "hardlink": (has_hardlink, "hardlinks"),
306 "icasefs": (has_icasefs, "case insensitive file system"),
307 "killdaemons": (has_killdaemons, 'killdaemons.py support'),
308 "lsprof": (has_lsprof, "python lsprof module"),
309 "mtn": (has_mtn, "monotone client (>= 1.0)"),
310 "outer-repo": (has_outer_repo, "outer repo"),
311 "p4": (has_p4, "Perforce server and client"),
312 "pyflakes": (has_pyflakes, "Pyflakes python linter"),
313 "pygments": (has_pygments, "Pygments source highlighting library"),
314 "python243": (has_python243, "python >= 2.4.3"),
315 "root": (has_root, "root permissions"),
316 "serve": (has_serve, "platform and python can manage 'hg serve -d'"),
317 "ssl": (has_ssl, "python >= 2.6 ssl module and python OpenSSL"),
318 "svn": (has_svn, "subversion client and admin tools"),
319 "svn13": (has_svn13, "subversion client and admin tools >= 1.3"),
320 "svn15": (has_svn15, "subversion client and admin tools >= 1.5"),
321 "svn-bindings": (has_svn_bindings, "subversion python bindings"),
322 "symlink": (has_symlink, "symbolic links"),
323 "system-sh": (has_system_sh, "system() uses sh"),
324 "test-repo": (has_test_repo, "running tests from repository"),
325 "tic": (has_tic, "terminfo compiler and curses module"),
326 "tla": (has_tla, "GNU Arch tla client"),
327 "unix-permissions": (has_unix_permissions, "unix-style permissions"),
328 "windows": (has_windows, "Windows"),
329 "msys": (has_msys, "Windows with MSYS"),
330 "aix": (has_aix, "AIX"),
331 "absimport": (has_absimport, "absolute_import in __future__"),
332 "py3k": (has_py3k, "running with Python 3.x"),
333 }
General Comments 0
You need to be logged in to leave comments. Login now