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