##// END OF EJS Templates
tests: use jshint when available to check .js files
av6 -
r35034:5d436907 default
parent child Browse files
Show More
@@ -0,0 +1,12
1 #require test-repo jshint hg10
2
3 $ . "$TESTDIR/helpers-testrepo.sh"
4
5 run jshint on all tracked files ending in .js except vendored dependencies
6
7 $ cd "`dirname "$TESTDIR"`"
8
9 $ testrepohg locate 'set:**.js' \
10 > -X mercurial/templates/static/excanvas.js \
11 > 2>/dev/null \
12 > | xargs jshint
@@ -1,687 +1,691
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 import errno
3 import errno
4 import os
4 import os
5 import re
5 import re
6 import socket
6 import socket
7 import stat
7 import stat
8 import subprocess
8 import subprocess
9 import sys
9 import sys
10 import tempfile
10 import tempfile
11
11
12 tempprefix = 'hg-hghave-'
12 tempprefix = 'hg-hghave-'
13
13
14 checks = {
14 checks = {
15 "true": (lambda: True, "yak shaving"),
15 "true": (lambda: True, "yak shaving"),
16 "false": (lambda: False, "nail clipper"),
16 "false": (lambda: False, "nail clipper"),
17 }
17 }
18
18
19 def check(name, desc):
19 def check(name, desc):
20 """Registers a check function for a feature."""
20 """Registers a check function for a feature."""
21 def decorator(func):
21 def decorator(func):
22 checks[name] = (func, desc)
22 checks[name] = (func, desc)
23 return func
23 return func
24 return decorator
24 return decorator
25
25
26 def checkvers(name, desc, vers):
26 def checkvers(name, desc, vers):
27 """Registers a check function for each of a series of versions.
27 """Registers a check function for each of a series of versions.
28
28
29 vers can be a list or an iterator"""
29 vers can be a list or an iterator"""
30 def decorator(func):
30 def decorator(func):
31 def funcv(v):
31 def funcv(v):
32 def f():
32 def f():
33 return func(v)
33 return func(v)
34 return f
34 return f
35 for v in vers:
35 for v in vers:
36 v = str(v)
36 v = str(v)
37 f = funcv(v)
37 f = funcv(v)
38 checks['%s%s' % (name, v.replace('.', ''))] = (f, desc % v)
38 checks['%s%s' % (name, v.replace('.', ''))] = (f, desc % v)
39 return func
39 return func
40 return decorator
40 return decorator
41
41
42 def checkfeatures(features):
42 def checkfeatures(features):
43 result = {
43 result = {
44 'error': [],
44 'error': [],
45 'missing': [],
45 'missing': [],
46 'skipped': [],
46 'skipped': [],
47 }
47 }
48
48
49 for feature in features:
49 for feature in features:
50 negate = feature.startswith('no-')
50 negate = feature.startswith('no-')
51 if negate:
51 if negate:
52 feature = feature[3:]
52 feature = feature[3:]
53
53
54 if feature not in checks:
54 if feature not in checks:
55 result['missing'].append(feature)
55 result['missing'].append(feature)
56 continue
56 continue
57
57
58 check, desc = checks[feature]
58 check, desc = checks[feature]
59 try:
59 try:
60 available = check()
60 available = check()
61 except Exception:
61 except Exception:
62 result['error'].append('hghave check failed: %s' % feature)
62 result['error'].append('hghave check failed: %s' % feature)
63 continue
63 continue
64
64
65 if not negate and not available:
65 if not negate and not available:
66 result['skipped'].append('missing feature: %s' % desc)
66 result['skipped'].append('missing feature: %s' % desc)
67 elif negate and available:
67 elif negate and available:
68 result['skipped'].append('system supports %s' % desc)
68 result['skipped'].append('system supports %s' % desc)
69
69
70 return result
70 return result
71
71
72 def require(features):
72 def require(features):
73 """Require that features are available, exiting if not."""
73 """Require that features are available, exiting if not."""
74 result = checkfeatures(features)
74 result = checkfeatures(features)
75
75
76 for missing in result['missing']:
76 for missing in result['missing']:
77 sys.stderr.write('skipped: unknown feature: %s\n' % missing)
77 sys.stderr.write('skipped: unknown feature: %s\n' % missing)
78 for msg in result['skipped']:
78 for msg in result['skipped']:
79 sys.stderr.write('skipped: %s\n' % msg)
79 sys.stderr.write('skipped: %s\n' % msg)
80 for msg in result['error']:
80 for msg in result['error']:
81 sys.stderr.write('%s\n' % msg)
81 sys.stderr.write('%s\n' % msg)
82
82
83 if result['missing']:
83 if result['missing']:
84 sys.exit(2)
84 sys.exit(2)
85
85
86 if result['skipped'] or result['error']:
86 if result['skipped'] or result['error']:
87 sys.exit(1)
87 sys.exit(1)
88
88
89 def matchoutput(cmd, regexp, ignorestatus=False):
89 def matchoutput(cmd, regexp, ignorestatus=False):
90 """Return the match object if cmd executes successfully and its output
90 """Return the match object if cmd executes successfully and its output
91 is matched by the supplied regular expression.
91 is matched by the supplied regular expression.
92 """
92 """
93 r = re.compile(regexp)
93 r = re.compile(regexp)
94 try:
94 try:
95 p = subprocess.Popen(
95 p = subprocess.Popen(
96 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
96 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
97 except OSError as e:
97 except OSError as e:
98 if e.errno != errno.ENOENT:
98 if e.errno != errno.ENOENT:
99 raise
99 raise
100 ret = -1
100 ret = -1
101 ret = p.wait()
101 ret = p.wait()
102 s = p.stdout.read()
102 s = p.stdout.read()
103 return (ignorestatus or not ret) and r.search(s)
103 return (ignorestatus or not ret) and r.search(s)
104
104
105 @check("baz", "GNU Arch baz client")
105 @check("baz", "GNU Arch baz client")
106 def has_baz():
106 def has_baz():
107 return matchoutput('baz --version 2>&1', br'baz Bazaar version')
107 return matchoutput('baz --version 2>&1', br'baz Bazaar version')
108
108
109 @check("bzr", "Canonical's Bazaar client")
109 @check("bzr", "Canonical's Bazaar client")
110 def has_bzr():
110 def has_bzr():
111 try:
111 try:
112 import bzrlib
112 import bzrlib
113 import bzrlib.bzrdir
113 import bzrlib.bzrdir
114 import bzrlib.errors
114 import bzrlib.errors
115 import bzrlib.revision
115 import bzrlib.revision
116 import bzrlib.revisionspec
116 import bzrlib.revisionspec
117 bzrlib.revisionspec.RevisionSpec
117 bzrlib.revisionspec.RevisionSpec
118 return bzrlib.__doc__ is not None
118 return bzrlib.__doc__ is not None
119 except (AttributeError, ImportError):
119 except (AttributeError, ImportError):
120 return False
120 return False
121
121
122 @checkvers("bzr", "Canonical's Bazaar client >= %s", (1.14,))
122 @checkvers("bzr", "Canonical's Bazaar client >= %s", (1.14,))
123 def has_bzr_range(v):
123 def has_bzr_range(v):
124 major, minor = v.split('.')[0:2]
124 major, minor = v.split('.')[0:2]
125 try:
125 try:
126 import bzrlib
126 import bzrlib
127 return (bzrlib.__doc__ is not None
127 return (bzrlib.__doc__ is not None
128 and bzrlib.version_info[:2] >= (int(major), int(minor)))
128 and bzrlib.version_info[:2] >= (int(major), int(minor)))
129 except ImportError:
129 except ImportError:
130 return False
130 return False
131
131
132 @check("chg", "running with chg")
132 @check("chg", "running with chg")
133 def has_chg():
133 def has_chg():
134 return 'CHGHG' in os.environ
134 return 'CHGHG' in os.environ
135
135
136 @check("cvs", "cvs client/server")
136 @check("cvs", "cvs client/server")
137 def has_cvs():
137 def has_cvs():
138 re = br'Concurrent Versions System.*?server'
138 re = br'Concurrent Versions System.*?server'
139 return matchoutput('cvs --version 2>&1', re) and not has_msys()
139 return matchoutput('cvs --version 2>&1', re) and not has_msys()
140
140
141 @check("cvs112", "cvs client/server 1.12.* (not cvsnt)")
141 @check("cvs112", "cvs client/server 1.12.* (not cvsnt)")
142 def has_cvs112():
142 def has_cvs112():
143 re = br'Concurrent Versions System \(CVS\) 1.12.*?server'
143 re = br'Concurrent Versions System \(CVS\) 1.12.*?server'
144 return matchoutput('cvs --version 2>&1', re) and not has_msys()
144 return matchoutput('cvs --version 2>&1', re) and not has_msys()
145
145
146 @check("cvsnt", "cvsnt client/server")
146 @check("cvsnt", "cvsnt client/server")
147 def has_cvsnt():
147 def has_cvsnt():
148 re = br'Concurrent Versions System \(CVSNT\) (\d+).(\d+).*\(client/server\)'
148 re = br'Concurrent Versions System \(CVSNT\) (\d+).(\d+).*\(client/server\)'
149 return matchoutput('cvsnt --version 2>&1', re)
149 return matchoutput('cvsnt --version 2>&1', re)
150
150
151 @check("darcs", "darcs client")
151 @check("darcs", "darcs client")
152 def has_darcs():
152 def has_darcs():
153 return matchoutput('darcs --version', br'\b2\.([2-9]|\d{2})', True)
153 return matchoutput('darcs --version', br'\b2\.([2-9]|\d{2})', True)
154
154
155 @check("mtn", "monotone client (>= 1.0)")
155 @check("mtn", "monotone client (>= 1.0)")
156 def has_mtn():
156 def has_mtn():
157 return matchoutput('mtn --version', br'monotone', True) and not matchoutput(
157 return matchoutput('mtn --version', br'monotone', True) and not matchoutput(
158 'mtn --version', br'monotone 0\.', True)
158 'mtn --version', br'monotone 0\.', True)
159
159
160 @check("eol-in-paths", "end-of-lines in paths")
160 @check("eol-in-paths", "end-of-lines in paths")
161 def has_eol_in_paths():
161 def has_eol_in_paths():
162 try:
162 try:
163 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
163 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
164 os.close(fd)
164 os.close(fd)
165 os.remove(path)
165 os.remove(path)
166 return True
166 return True
167 except (IOError, OSError):
167 except (IOError, OSError):
168 return False
168 return False
169
169
170 @check("execbit", "executable bit")
170 @check("execbit", "executable bit")
171 def has_executablebit():
171 def has_executablebit():
172 try:
172 try:
173 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
173 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
174 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
174 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
175 try:
175 try:
176 os.close(fh)
176 os.close(fh)
177 m = os.stat(fn).st_mode & 0o777
177 m = os.stat(fn).st_mode & 0o777
178 new_file_has_exec = m & EXECFLAGS
178 new_file_has_exec = m & EXECFLAGS
179 os.chmod(fn, m ^ EXECFLAGS)
179 os.chmod(fn, m ^ EXECFLAGS)
180 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0o777) == m)
180 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0o777) == m)
181 finally:
181 finally:
182 os.unlink(fn)
182 os.unlink(fn)
183 except (IOError, OSError):
183 except (IOError, OSError):
184 # we don't care, the user probably won't be able to commit anyway
184 # we don't care, the user probably won't be able to commit anyway
185 return False
185 return False
186 return not (new_file_has_exec or exec_flags_cannot_flip)
186 return not (new_file_has_exec or exec_flags_cannot_flip)
187
187
188 @check("icasefs", "case insensitive file system")
188 @check("icasefs", "case insensitive file system")
189 def has_icasefs():
189 def has_icasefs():
190 # Stolen from mercurial.util
190 # Stolen from mercurial.util
191 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
191 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
192 os.close(fd)
192 os.close(fd)
193 try:
193 try:
194 s1 = os.stat(path)
194 s1 = os.stat(path)
195 d, b = os.path.split(path)
195 d, b = os.path.split(path)
196 p2 = os.path.join(d, b.upper())
196 p2 = os.path.join(d, b.upper())
197 if path == p2:
197 if path == p2:
198 p2 = os.path.join(d, b.lower())
198 p2 = os.path.join(d, b.lower())
199 try:
199 try:
200 s2 = os.stat(p2)
200 s2 = os.stat(p2)
201 return s2 == s1
201 return s2 == s1
202 except OSError:
202 except OSError:
203 return False
203 return False
204 finally:
204 finally:
205 os.remove(path)
205 os.remove(path)
206
206
207 @check("fifo", "named pipes")
207 @check("fifo", "named pipes")
208 def has_fifo():
208 def has_fifo():
209 if getattr(os, "mkfifo", None) is None:
209 if getattr(os, "mkfifo", None) is None:
210 return False
210 return False
211 name = tempfile.mktemp(dir='.', prefix=tempprefix)
211 name = tempfile.mktemp(dir='.', prefix=tempprefix)
212 try:
212 try:
213 os.mkfifo(name)
213 os.mkfifo(name)
214 os.unlink(name)
214 os.unlink(name)
215 return True
215 return True
216 except OSError:
216 except OSError:
217 return False
217 return False
218
218
219 @check("killdaemons", 'killdaemons.py support')
219 @check("killdaemons", 'killdaemons.py support')
220 def has_killdaemons():
220 def has_killdaemons():
221 return True
221 return True
222
222
223 @check("cacheable", "cacheable filesystem")
223 @check("cacheable", "cacheable filesystem")
224 def has_cacheable_fs():
224 def has_cacheable_fs():
225 from mercurial import util
225 from mercurial import util
226
226
227 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
227 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
228 os.close(fd)
228 os.close(fd)
229 try:
229 try:
230 return util.cachestat(path).cacheable()
230 return util.cachestat(path).cacheable()
231 finally:
231 finally:
232 os.remove(path)
232 os.remove(path)
233
233
234 @check("lsprof", "python lsprof module")
234 @check("lsprof", "python lsprof module")
235 def has_lsprof():
235 def has_lsprof():
236 try:
236 try:
237 import _lsprof
237 import _lsprof
238 _lsprof.Profiler # silence unused import warning
238 _lsprof.Profiler # silence unused import warning
239 return True
239 return True
240 except ImportError:
240 except ImportError:
241 return False
241 return False
242
242
243 def gethgversion():
243 def gethgversion():
244 m = matchoutput('hg --version --quiet 2>&1', br'(\d+)\.(\d+)')
244 m = matchoutput('hg --version --quiet 2>&1', br'(\d+)\.(\d+)')
245 if not m:
245 if not m:
246 return (0, 0)
246 return (0, 0)
247 return (int(m.group(1)), int(m.group(2)))
247 return (int(m.group(1)), int(m.group(2)))
248
248
249 @checkvers("hg", "Mercurial >= %s",
249 @checkvers("hg", "Mercurial >= %s",
250 list([(1.0 * x) / 10 for x in range(9, 99)]))
250 list([(1.0 * x) / 10 for x in range(9, 99)]))
251 def has_hg_range(v):
251 def has_hg_range(v):
252 major, minor = v.split('.')[0:2]
252 major, minor = v.split('.')[0:2]
253 return gethgversion() >= (int(major), int(minor))
253 return gethgversion() >= (int(major), int(minor))
254
254
255 @check("hg08", "Mercurial >= 0.8")
255 @check("hg08", "Mercurial >= 0.8")
256 def has_hg08():
256 def has_hg08():
257 if checks["hg09"][0]():
257 if checks["hg09"][0]():
258 return True
258 return True
259 return matchoutput('hg help annotate 2>&1', '--date')
259 return matchoutput('hg help annotate 2>&1', '--date')
260
260
261 @check("hg07", "Mercurial >= 0.7")
261 @check("hg07", "Mercurial >= 0.7")
262 def has_hg07():
262 def has_hg07():
263 if checks["hg08"][0]():
263 if checks["hg08"][0]():
264 return True
264 return True
265 return matchoutput('hg --version --quiet 2>&1', 'Mercurial Distributed SCM')
265 return matchoutput('hg --version --quiet 2>&1', 'Mercurial Distributed SCM')
266
266
267 @check("hg06", "Mercurial >= 0.6")
267 @check("hg06", "Mercurial >= 0.6")
268 def has_hg06():
268 def has_hg06():
269 if checks["hg07"][0]():
269 if checks["hg07"][0]():
270 return True
270 return True
271 return matchoutput('hg --version --quiet 2>&1', 'Mercurial version')
271 return matchoutput('hg --version --quiet 2>&1', 'Mercurial version')
272
272
273 @check("gettext", "GNU Gettext (msgfmt)")
273 @check("gettext", "GNU Gettext (msgfmt)")
274 def has_gettext():
274 def has_gettext():
275 return matchoutput('msgfmt --version', br'GNU gettext-tools')
275 return matchoutput('msgfmt --version', br'GNU gettext-tools')
276
276
277 @check("git", "git command line client")
277 @check("git", "git command line client")
278 def has_git():
278 def has_git():
279 return matchoutput('git --version 2>&1', br'^git version')
279 return matchoutput('git --version 2>&1', br'^git version')
280
280
281 def getgitversion():
281 def getgitversion():
282 m = matchoutput('git --version 2>&1', br'git version (\d+)\.(\d+)')
282 m = matchoutput('git --version 2>&1', br'git version (\d+)\.(\d+)')
283 if not m:
283 if not m:
284 return (0, 0)
284 return (0, 0)
285 return (int(m.group(1)), int(m.group(2)))
285 return (int(m.group(1)), int(m.group(2)))
286
286
287 @checkvers("git", "git client (with ext::sh support) version >= %s", (1.9,))
287 @checkvers("git", "git client (with ext::sh support) version >= %s", (1.9,))
288 def has_git_range(v):
288 def has_git_range(v):
289 major, minor = v.split('.')[0:2]
289 major, minor = v.split('.')[0:2]
290 return getgitversion() >= (int(major), int(minor))
290 return getgitversion() >= (int(major), int(minor))
291
291
292 @check("docutils", "Docutils text processing library")
292 @check("docutils", "Docutils text processing library")
293 def has_docutils():
293 def has_docutils():
294 try:
294 try:
295 import docutils.core
295 import docutils.core
296 docutils.core.publish_cmdline # silence unused import
296 docutils.core.publish_cmdline # silence unused import
297 return True
297 return True
298 except ImportError:
298 except ImportError:
299 return False
299 return False
300
300
301 def getsvnversion():
301 def getsvnversion():
302 m = matchoutput('svn --version --quiet 2>&1', br'^(\d+)\.(\d+)')
302 m = matchoutput('svn --version --quiet 2>&1', br'^(\d+)\.(\d+)')
303 if not m:
303 if not m:
304 return (0, 0)
304 return (0, 0)
305 return (int(m.group(1)), int(m.group(2)))
305 return (int(m.group(1)), int(m.group(2)))
306
306
307 @checkvers("svn", "subversion client and admin tools >= %s", (1.3, 1.5))
307 @checkvers("svn", "subversion client and admin tools >= %s", (1.3, 1.5))
308 def has_svn_range(v):
308 def has_svn_range(v):
309 major, minor = v.split('.')[0:2]
309 major, minor = v.split('.')[0:2]
310 return getsvnversion() >= (int(major), int(minor))
310 return getsvnversion() >= (int(major), int(minor))
311
311
312 @check("svn", "subversion client and admin tools")
312 @check("svn", "subversion client and admin tools")
313 def has_svn():
313 def has_svn():
314 return matchoutput('svn --version 2>&1', br'^svn, version') and \
314 return matchoutput('svn --version 2>&1', br'^svn, version') and \
315 matchoutput('svnadmin --version 2>&1', br'^svnadmin, version')
315 matchoutput('svnadmin --version 2>&1', br'^svnadmin, version')
316
316
317 @check("svn-bindings", "subversion python bindings")
317 @check("svn-bindings", "subversion python bindings")
318 def has_svn_bindings():
318 def has_svn_bindings():
319 try:
319 try:
320 import svn.core
320 import svn.core
321 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
321 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
322 if version < (1, 4):
322 if version < (1, 4):
323 return False
323 return False
324 return True
324 return True
325 except ImportError:
325 except ImportError:
326 return False
326 return False
327
327
328 @check("p4", "Perforce server and client")
328 @check("p4", "Perforce server and client")
329 def has_p4():
329 def has_p4():
330 return (matchoutput('p4 -V', br'Rev\. P4/') and
330 return (matchoutput('p4 -V', br'Rev\. P4/') and
331 matchoutput('p4d -V', br'Rev\. P4D/'))
331 matchoutput('p4d -V', br'Rev\. P4D/'))
332
332
333 @check("symlink", "symbolic links")
333 @check("symlink", "symbolic links")
334 def has_symlink():
334 def has_symlink():
335 if getattr(os, "symlink", None) is None:
335 if getattr(os, "symlink", None) is None:
336 return False
336 return False
337 name = tempfile.mktemp(dir='.', prefix=tempprefix)
337 name = tempfile.mktemp(dir='.', prefix=tempprefix)
338 try:
338 try:
339 os.symlink(".", name)
339 os.symlink(".", name)
340 os.unlink(name)
340 os.unlink(name)
341 return True
341 return True
342 except (OSError, AttributeError):
342 except (OSError, AttributeError):
343 return False
343 return False
344
344
345 @check("hardlink", "hardlinks")
345 @check("hardlink", "hardlinks")
346 def has_hardlink():
346 def has_hardlink():
347 from mercurial import util
347 from mercurial import util
348 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
348 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
349 os.close(fh)
349 os.close(fh)
350 name = tempfile.mktemp(dir='.', prefix=tempprefix)
350 name = tempfile.mktemp(dir='.', prefix=tempprefix)
351 try:
351 try:
352 util.oslink(fn, name)
352 util.oslink(fn, name)
353 os.unlink(name)
353 os.unlink(name)
354 return True
354 return True
355 except OSError:
355 except OSError:
356 return False
356 return False
357 finally:
357 finally:
358 os.unlink(fn)
358 os.unlink(fn)
359
359
360 @check("hardlink-whitelisted", "hardlinks on whitelisted filesystems")
360 @check("hardlink-whitelisted", "hardlinks on whitelisted filesystems")
361 def has_hardlink_whitelisted():
361 def has_hardlink_whitelisted():
362 from mercurial import util
362 from mercurial import util
363 try:
363 try:
364 fstype = util.getfstype('.')
364 fstype = util.getfstype('.')
365 except OSError:
365 except OSError:
366 return False
366 return False
367 return fstype in util._hardlinkfswhitelist
367 return fstype in util._hardlinkfswhitelist
368
368
369 @check("rmcwd", "can remove current working directory")
369 @check("rmcwd", "can remove current working directory")
370 def has_rmcwd():
370 def has_rmcwd():
371 ocwd = os.getcwd()
371 ocwd = os.getcwd()
372 temp = tempfile.mkdtemp(dir='.', prefix=tempprefix)
372 temp = tempfile.mkdtemp(dir='.', prefix=tempprefix)
373 try:
373 try:
374 os.chdir(temp)
374 os.chdir(temp)
375 # On Linux, 'rmdir .' isn't allowed, but the other names are okay.
375 # On Linux, 'rmdir .' isn't allowed, but the other names are okay.
376 # On Solaris and Windows, the cwd can't be removed by any names.
376 # On Solaris and Windows, the cwd can't be removed by any names.
377 os.rmdir(os.getcwd())
377 os.rmdir(os.getcwd())
378 return True
378 return True
379 except OSError:
379 except OSError:
380 return False
380 return False
381 finally:
381 finally:
382 os.chdir(ocwd)
382 os.chdir(ocwd)
383 # clean up temp dir on platforms where cwd can't be removed
383 # clean up temp dir on platforms where cwd can't be removed
384 try:
384 try:
385 os.rmdir(temp)
385 os.rmdir(temp)
386 except OSError:
386 except OSError:
387 pass
387 pass
388
388
389 @check("tla", "GNU Arch tla client")
389 @check("tla", "GNU Arch tla client")
390 def has_tla():
390 def has_tla():
391 return matchoutput('tla --version 2>&1', br'The GNU Arch Revision')
391 return matchoutput('tla --version 2>&1', br'The GNU Arch Revision')
392
392
393 @check("gpg", "gpg client")
393 @check("gpg", "gpg client")
394 def has_gpg():
394 def has_gpg():
395 return matchoutput('gpg --version 2>&1', br'GnuPG')
395 return matchoutput('gpg --version 2>&1', br'GnuPG')
396
396
397 @check("gpg2", "gpg client v2")
397 @check("gpg2", "gpg client v2")
398 def has_gpg2():
398 def has_gpg2():
399 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.')
399 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.')
400
400
401 @check("gpg21", "gpg client v2.1+")
401 @check("gpg21", "gpg client v2.1+")
402 def has_gpg21():
402 def has_gpg21():
403 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.(?!0)')
403 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.(?!0)')
404
404
405 @check("unix-permissions", "unix-style permissions")
405 @check("unix-permissions", "unix-style permissions")
406 def has_unix_permissions():
406 def has_unix_permissions():
407 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
407 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
408 try:
408 try:
409 fname = os.path.join(d, 'foo')
409 fname = os.path.join(d, 'foo')
410 for umask in (0o77, 0o07, 0o22):
410 for umask in (0o77, 0o07, 0o22):
411 os.umask(umask)
411 os.umask(umask)
412 f = open(fname, 'w')
412 f = open(fname, 'w')
413 f.close()
413 f.close()
414 mode = os.stat(fname).st_mode
414 mode = os.stat(fname).st_mode
415 os.unlink(fname)
415 os.unlink(fname)
416 if mode & 0o777 != ~umask & 0o666:
416 if mode & 0o777 != ~umask & 0o666:
417 return False
417 return False
418 return True
418 return True
419 finally:
419 finally:
420 os.rmdir(d)
420 os.rmdir(d)
421
421
422 @check("unix-socket", "AF_UNIX socket family")
422 @check("unix-socket", "AF_UNIX socket family")
423 def has_unix_socket():
423 def has_unix_socket():
424 return getattr(socket, 'AF_UNIX', None) is not None
424 return getattr(socket, 'AF_UNIX', None) is not None
425
425
426 @check("root", "root permissions")
426 @check("root", "root permissions")
427 def has_root():
427 def has_root():
428 return getattr(os, 'geteuid', None) and os.geteuid() == 0
428 return getattr(os, 'geteuid', None) and os.geteuid() == 0
429
429
430 @check("pyflakes", "Pyflakes python linter")
430 @check("pyflakes", "Pyflakes python linter")
431 def has_pyflakes():
431 def has_pyflakes():
432 return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
432 return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
433 br"<stdin>:1: 're' imported but unused",
433 br"<stdin>:1: 're' imported but unused",
434 True)
434 True)
435
435
436 @check("pylint", "Pylint python linter")
436 @check("pylint", "Pylint python linter")
437 def has_pylint():
437 def has_pylint():
438 return matchoutput("pylint --help",
438 return matchoutput("pylint --help",
439 br"Usage: pylint",
439 br"Usage: pylint",
440 True)
440 True)
441
441
442 @check("clang-format", "clang-format C code formatter")
442 @check("clang-format", "clang-format C code formatter")
443 def has_clang_format():
443 def has_clang_format():
444 return matchoutput("clang-format --help",
444 return matchoutput("clang-format --help",
445 br"^OVERVIEW: A tool to format C/C\+\+[^ ]+ code.")
445 br"^OVERVIEW: A tool to format C/C\+\+[^ ]+ code.")
446
446
447 @check("jshint", "JSHint static code analysis tool")
448 def has_jshint():
449 return matchoutput("jshint --version 2>&1", br"jshint v")
450
447 @check("pygments", "Pygments source highlighting library")
451 @check("pygments", "Pygments source highlighting library")
448 def has_pygments():
452 def has_pygments():
449 try:
453 try:
450 import pygments
454 import pygments
451 pygments.highlight # silence unused import warning
455 pygments.highlight # silence unused import warning
452 return True
456 return True
453 except ImportError:
457 except ImportError:
454 return False
458 return False
455
459
456 @check("outer-repo", "outer repo")
460 @check("outer-repo", "outer repo")
457 def has_outer_repo():
461 def has_outer_repo():
458 # failing for other reasons than 'no repo' imply that there is a repo
462 # failing for other reasons than 'no repo' imply that there is a repo
459 return not matchoutput('hg root 2>&1',
463 return not matchoutput('hg root 2>&1',
460 br'abort: no repository found', True)
464 br'abort: no repository found', True)
461
465
462 @check("ssl", "ssl module available")
466 @check("ssl", "ssl module available")
463 def has_ssl():
467 def has_ssl():
464 try:
468 try:
465 import ssl
469 import ssl
466 ssl.CERT_NONE
470 ssl.CERT_NONE
467 return True
471 return True
468 except ImportError:
472 except ImportError:
469 return False
473 return False
470
474
471 @check("sslcontext", "python >= 2.7.9 ssl")
475 @check("sslcontext", "python >= 2.7.9 ssl")
472 def has_sslcontext():
476 def has_sslcontext():
473 try:
477 try:
474 import ssl
478 import ssl
475 ssl.SSLContext
479 ssl.SSLContext
476 return True
480 return True
477 except (ImportError, AttributeError):
481 except (ImportError, AttributeError):
478 return False
482 return False
479
483
480 @check("defaultcacerts", "can verify SSL certs by system's CA certs store")
484 @check("defaultcacerts", "can verify SSL certs by system's CA certs store")
481 def has_defaultcacerts():
485 def has_defaultcacerts():
482 from mercurial import sslutil, ui as uimod
486 from mercurial import sslutil, ui as uimod
483 ui = uimod.ui.load()
487 ui = uimod.ui.load()
484 return sslutil._defaultcacerts(ui) or sslutil._canloaddefaultcerts
488 return sslutil._defaultcacerts(ui) or sslutil._canloaddefaultcerts
485
489
486 @check("defaultcacertsloaded", "detected presence of loaded system CA certs")
490 @check("defaultcacertsloaded", "detected presence of loaded system CA certs")
487 def has_defaultcacertsloaded():
491 def has_defaultcacertsloaded():
488 import ssl
492 import ssl
489 from mercurial import sslutil, ui as uimod
493 from mercurial import sslutil, ui as uimod
490
494
491 if not has_defaultcacerts():
495 if not has_defaultcacerts():
492 return False
496 return False
493 if not has_sslcontext():
497 if not has_sslcontext():
494 return False
498 return False
495
499
496 ui = uimod.ui.load()
500 ui = uimod.ui.load()
497 cafile = sslutil._defaultcacerts(ui)
501 cafile = sslutil._defaultcacerts(ui)
498 ctx = ssl.create_default_context()
502 ctx = ssl.create_default_context()
499 if cafile:
503 if cafile:
500 ctx.load_verify_locations(cafile=cafile)
504 ctx.load_verify_locations(cafile=cafile)
501 else:
505 else:
502 ctx.load_default_certs()
506 ctx.load_default_certs()
503
507
504 return len(ctx.get_ca_certs()) > 0
508 return len(ctx.get_ca_certs()) > 0
505
509
506 @check("tls1.2", "TLS 1.2 protocol support")
510 @check("tls1.2", "TLS 1.2 protocol support")
507 def has_tls1_2():
511 def has_tls1_2():
508 from mercurial import sslutil
512 from mercurial import sslutil
509 return 'tls1.2' in sslutil.supportedprotocols
513 return 'tls1.2' in sslutil.supportedprotocols
510
514
511 @check("windows", "Windows")
515 @check("windows", "Windows")
512 def has_windows():
516 def has_windows():
513 return os.name == 'nt'
517 return os.name == 'nt'
514
518
515 @check("system-sh", "system() uses sh")
519 @check("system-sh", "system() uses sh")
516 def has_system_sh():
520 def has_system_sh():
517 return os.name != 'nt'
521 return os.name != 'nt'
518
522
519 @check("serve", "platform and python can manage 'hg serve -d'")
523 @check("serve", "platform and python can manage 'hg serve -d'")
520 def has_serve():
524 def has_serve():
521 return True
525 return True
522
526
523 @check("test-repo", "running tests from repository")
527 @check("test-repo", "running tests from repository")
524 def has_test_repo():
528 def has_test_repo():
525 t = os.environ["TESTDIR"]
529 t = os.environ["TESTDIR"]
526 return os.path.isdir(os.path.join(t, "..", ".hg"))
530 return os.path.isdir(os.path.join(t, "..", ".hg"))
527
531
528 @check("tic", "terminfo compiler and curses module")
532 @check("tic", "terminfo compiler and curses module")
529 def has_tic():
533 def has_tic():
530 try:
534 try:
531 import curses
535 import curses
532 curses.COLOR_BLUE
536 curses.COLOR_BLUE
533 return matchoutput('test -x "`which tic`"', br'')
537 return matchoutput('test -x "`which tic`"', br'')
534 except ImportError:
538 except ImportError:
535 return False
539 return False
536
540
537 @check("msys", "Windows with MSYS")
541 @check("msys", "Windows with MSYS")
538 def has_msys():
542 def has_msys():
539 return os.getenv('MSYSTEM')
543 return os.getenv('MSYSTEM')
540
544
541 @check("aix", "AIX")
545 @check("aix", "AIX")
542 def has_aix():
546 def has_aix():
543 return sys.platform.startswith("aix")
547 return sys.platform.startswith("aix")
544
548
545 @check("osx", "OS X")
549 @check("osx", "OS X")
546 def has_osx():
550 def has_osx():
547 return sys.platform == 'darwin'
551 return sys.platform == 'darwin'
548
552
549 @check("osxpackaging", "OS X packaging tools")
553 @check("osxpackaging", "OS X packaging tools")
550 def has_osxpackaging():
554 def has_osxpackaging():
551 try:
555 try:
552 return (matchoutput('pkgbuild', br'Usage: pkgbuild ', ignorestatus=1)
556 return (matchoutput('pkgbuild', br'Usage: pkgbuild ', ignorestatus=1)
553 and matchoutput(
557 and matchoutput(
554 'productbuild', br'Usage: productbuild ',
558 'productbuild', br'Usage: productbuild ',
555 ignorestatus=1)
559 ignorestatus=1)
556 and matchoutput('lsbom', br'Usage: lsbom', ignorestatus=1)
560 and matchoutput('lsbom', br'Usage: lsbom', ignorestatus=1)
557 and matchoutput(
561 and matchoutput(
558 'xar --help', br'Usage: xar', ignorestatus=1))
562 'xar --help', br'Usage: xar', ignorestatus=1))
559 except ImportError:
563 except ImportError:
560 return False
564 return False
561
565
562 @check('linuxormacos', 'Linux or MacOS')
566 @check('linuxormacos', 'Linux or MacOS')
563 def has_linuxormacos():
567 def has_linuxormacos():
564 # This isn't a perfect test for MacOS. But it is sufficient for our needs.
568 # This isn't a perfect test for MacOS. But it is sufficient for our needs.
565 return sys.platform.startswith(('linux', 'darwin'))
569 return sys.platform.startswith(('linux', 'darwin'))
566
570
567 @check("docker", "docker support")
571 @check("docker", "docker support")
568 def has_docker():
572 def has_docker():
569 pat = br'A self-sufficient runtime for'
573 pat = br'A self-sufficient runtime for'
570 if matchoutput('docker --help', pat):
574 if matchoutput('docker --help', pat):
571 if 'linux' not in sys.platform:
575 if 'linux' not in sys.platform:
572 # TODO: in theory we should be able to test docker-based
576 # TODO: in theory we should be able to test docker-based
573 # package creation on non-linux using boot2docker, but in
577 # package creation on non-linux using boot2docker, but in
574 # practice that requires extra coordination to make sure
578 # practice that requires extra coordination to make sure
575 # $TESTTEMP is going to be visible at the same path to the
579 # $TESTTEMP is going to be visible at the same path to the
576 # boot2docker VM. If we figure out how to verify that, we
580 # boot2docker VM. If we figure out how to verify that, we
577 # can use the following instead of just saying False:
581 # can use the following instead of just saying False:
578 # return 'DOCKER_HOST' in os.environ
582 # return 'DOCKER_HOST' in os.environ
579 return False
583 return False
580
584
581 return True
585 return True
582 return False
586 return False
583
587
584 @check("debhelper", "debian packaging tools")
588 @check("debhelper", "debian packaging tools")
585 def has_debhelper():
589 def has_debhelper():
586 # Some versions of dpkg say `dpkg', some say 'dpkg' (` vs ' on the first
590 # Some versions of dpkg say `dpkg', some say 'dpkg' (` vs ' on the first
587 # quote), so just accept anything in that spot.
591 # quote), so just accept anything in that spot.
588 dpkg = matchoutput('dpkg --version',
592 dpkg = matchoutput('dpkg --version',
589 br"Debian .dpkg' package management program")
593 br"Debian .dpkg' package management program")
590 dh = matchoutput('dh --help',
594 dh = matchoutput('dh --help',
591 br'dh is a part of debhelper.', ignorestatus=True)
595 br'dh is a part of debhelper.', ignorestatus=True)
592 dh_py2 = matchoutput('dh_python2 --help',
596 dh_py2 = matchoutput('dh_python2 --help',
593 br'other supported Python versions')
597 br'other supported Python versions')
594 # debuild comes from the 'devscripts' package, though you might want
598 # debuild comes from the 'devscripts' package, though you might want
595 # the 'build-debs' package instead, which has a dependency on devscripts.
599 # the 'build-debs' package instead, which has a dependency on devscripts.
596 debuild = matchoutput('debuild --help',
600 debuild = matchoutput('debuild --help',
597 br'to run debian/rules with given parameter')
601 br'to run debian/rules with given parameter')
598 return dpkg and dh and dh_py2 and debuild
602 return dpkg and dh and dh_py2 and debuild
599
603
600 @check("debdeps",
604 @check("debdeps",
601 "debian build dependencies (run dpkg-checkbuilddeps in contrib/)")
605 "debian build dependencies (run dpkg-checkbuilddeps in contrib/)")
602 def has_debdeps():
606 def has_debdeps():
603 # just check exit status (ignoring output)
607 # just check exit status (ignoring output)
604 path = '%s/../contrib/debian/control' % os.environ['TESTDIR']
608 path = '%s/../contrib/debian/control' % os.environ['TESTDIR']
605 return matchoutput('dpkg-checkbuilddeps %s' % path, br'')
609 return matchoutput('dpkg-checkbuilddeps %s' % path, br'')
606
610
607 @check("demandimport", "demandimport enabled")
611 @check("demandimport", "demandimport enabled")
608 def has_demandimport():
612 def has_demandimport():
609 # chg disables demandimport intentionally for performance wins.
613 # chg disables demandimport intentionally for performance wins.
610 return ((not has_chg()) and os.environ.get('HGDEMANDIMPORT') != 'disable')
614 return ((not has_chg()) and os.environ.get('HGDEMANDIMPORT') != 'disable')
611
615
612 @check("py3k", "running with Python 3.x")
616 @check("py3k", "running with Python 3.x")
613 def has_py3k():
617 def has_py3k():
614 return 3 == sys.version_info[0]
618 return 3 == sys.version_info[0]
615
619
616 @check("py3exe", "a Python 3.x interpreter is available")
620 @check("py3exe", "a Python 3.x interpreter is available")
617 def has_python3exe():
621 def has_python3exe():
618 return 'PYTHON3' in os.environ
622 return 'PYTHON3' in os.environ
619
623
620 @check("py3pygments", "Pygments available on Python 3.x")
624 @check("py3pygments", "Pygments available on Python 3.x")
621 def has_py3pygments():
625 def has_py3pygments():
622 if has_py3k():
626 if has_py3k():
623 return has_pygments()
627 return has_pygments()
624 elif has_python3exe():
628 elif has_python3exe():
625 # just check exit status (ignoring output)
629 # just check exit status (ignoring output)
626 py3 = os.environ['PYTHON3']
630 py3 = os.environ['PYTHON3']
627 return matchoutput('%s -c "import pygments"' % py3, br'')
631 return matchoutput('%s -c "import pygments"' % py3, br'')
628 return False
632 return False
629
633
630 @check("pure", "running with pure Python code")
634 @check("pure", "running with pure Python code")
631 def has_pure():
635 def has_pure():
632 return any([
636 return any([
633 os.environ.get("HGMODULEPOLICY") == "py",
637 os.environ.get("HGMODULEPOLICY") == "py",
634 os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure",
638 os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure",
635 ])
639 ])
636
640
637 @check("slow", "allow slow tests (use --allow-slow-tests)")
641 @check("slow", "allow slow tests (use --allow-slow-tests)")
638 def has_slow():
642 def has_slow():
639 return os.environ.get('HGTEST_SLOW') == 'slow'
643 return os.environ.get('HGTEST_SLOW') == 'slow'
640
644
641 @check("hypothesis", "Hypothesis automated test generation")
645 @check("hypothesis", "Hypothesis automated test generation")
642 def has_hypothesis():
646 def has_hypothesis():
643 try:
647 try:
644 import hypothesis
648 import hypothesis
645 hypothesis.given
649 hypothesis.given
646 return True
650 return True
647 except ImportError:
651 except ImportError:
648 return False
652 return False
649
653
650 @check("unziplinks", "unzip(1) understands and extracts symlinks")
654 @check("unziplinks", "unzip(1) understands and extracts symlinks")
651 def unzip_understands_symlinks():
655 def unzip_understands_symlinks():
652 return matchoutput('unzip --help', br'Info-ZIP')
656 return matchoutput('unzip --help', br'Info-ZIP')
653
657
654 @check("zstd", "zstd Python module available")
658 @check("zstd", "zstd Python module available")
655 def has_zstd():
659 def has_zstd():
656 try:
660 try:
657 import mercurial.zstd
661 import mercurial.zstd
658 mercurial.zstd.__version__
662 mercurial.zstd.__version__
659 return True
663 return True
660 except ImportError:
664 except ImportError:
661 return False
665 return False
662
666
663 @check("devfull", "/dev/full special file")
667 @check("devfull", "/dev/full special file")
664 def has_dev_full():
668 def has_dev_full():
665 return os.path.exists('/dev/full')
669 return os.path.exists('/dev/full')
666
670
667 @check("virtualenv", "Python virtualenv support")
671 @check("virtualenv", "Python virtualenv support")
668 def has_virtualenv():
672 def has_virtualenv():
669 try:
673 try:
670 import virtualenv
674 import virtualenv
671 virtualenv.ACTIVATE_SH
675 virtualenv.ACTIVATE_SH
672 return True
676 return True
673 except ImportError:
677 except ImportError:
674 return False
678 return False
675
679
676 @check("fsmonitor", "running tests with fsmonitor")
680 @check("fsmonitor", "running tests with fsmonitor")
677 def has_fsmonitor():
681 def has_fsmonitor():
678 return 'HGFSMONITOR_TESTS' in os.environ
682 return 'HGFSMONITOR_TESTS' in os.environ
679
683
680 @check("fuzzywuzzy", "Fuzzy string matching library")
684 @check("fuzzywuzzy", "Fuzzy string matching library")
681 def has_fuzzywuzzy():
685 def has_fuzzywuzzy():
682 try:
686 try:
683 import fuzzywuzzy
687 import fuzzywuzzy
684 fuzzywuzzy.__version__
688 fuzzywuzzy.__version__
685 return True
689 return True
686 except ImportError:
690 except ImportError:
687 return False
691 return False
General Comments 0
You need to be logged in to leave comments. Login now