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