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