##// END OF EJS Templates
tests: add hghave rule 'setprocname' to check if osutil.setprocname and use it...
Pulkit Goyal -
r45514:e4d2efb7 default draft
parent child Browse files
Show More
@@ -1,1052 +1,1063 b''
1 1 from __future__ import absolute_import, print_function
2 2
3 3 import distutils.version
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 try:
20 20 import msvcrt
21 21
22 22 msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
23 23 msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
24 24 except ImportError:
25 25 pass
26 26
27 27 stdout = getattr(sys.stdout, 'buffer', sys.stdout)
28 28 stderr = getattr(sys.stderr, 'buffer', sys.stderr)
29 29
30 30 if sys.version_info[0] >= 3:
31 31
32 32 def _sys2bytes(p):
33 33 if p is None:
34 34 return p
35 35 return p.encode('utf-8')
36 36
37 37 def _bytes2sys(p):
38 38 if p is None:
39 39 return p
40 40 return p.decode('utf-8')
41 41
42 42
43 43 else:
44 44
45 45 def _sys2bytes(p):
46 46 return p
47 47
48 48 _bytes2sys = _sys2bytes
49 49
50 50
51 51 def check(name, desc):
52 52 """Registers a check function for a feature."""
53 53
54 54 def decorator(func):
55 55 checks[name] = (func, desc)
56 56 return func
57 57
58 58 return decorator
59 59
60 60
61 61 def checkvers(name, desc, vers):
62 62 """Registers a check function for each of a series of versions.
63 63
64 64 vers can be a list or an iterator.
65 65
66 66 Produces a series of feature checks that have the form <name><vers> without
67 67 any punctuation (even if there's punctuation in 'vers'; i.e. this produces
68 68 'py38', not 'py3.8' or 'py-38')."""
69 69
70 70 def decorator(func):
71 71 def funcv(v):
72 72 def f():
73 73 return func(v)
74 74
75 75 return f
76 76
77 77 for v in vers:
78 78 v = str(v)
79 79 f = funcv(v)
80 80 checks['%s%s' % (name, v.replace('.', ''))] = (f, desc % v)
81 81 return func
82 82
83 83 return decorator
84 84
85 85
86 86 def checkfeatures(features):
87 87 result = {
88 88 'error': [],
89 89 'missing': [],
90 90 'skipped': [],
91 91 }
92 92
93 93 for feature in features:
94 94 negate = feature.startswith('no-')
95 95 if negate:
96 96 feature = feature[3:]
97 97
98 98 if feature not in checks:
99 99 result['missing'].append(feature)
100 100 continue
101 101
102 102 check, desc = checks[feature]
103 103 try:
104 104 available = check()
105 105 except Exception:
106 106 result['error'].append('hghave check failed: %s' % feature)
107 107 continue
108 108
109 109 if not negate and not available:
110 110 result['skipped'].append('missing feature: %s' % desc)
111 111 elif negate and available:
112 112 result['skipped'].append('system supports %s' % desc)
113 113
114 114 return result
115 115
116 116
117 117 def require(features):
118 118 """Require that features are available, exiting if not."""
119 119 result = checkfeatures(features)
120 120
121 121 for missing in result['missing']:
122 122 stderr.write(
123 123 ('skipped: unknown feature: %s\n' % missing).encode('utf-8')
124 124 )
125 125 for msg in result['skipped']:
126 126 stderr.write(('skipped: %s\n' % msg).encode('utf-8'))
127 127 for msg in result['error']:
128 128 stderr.write(('%s\n' % msg).encode('utf-8'))
129 129
130 130 if result['missing']:
131 131 sys.exit(2)
132 132
133 133 if result['skipped'] or result['error']:
134 134 sys.exit(1)
135 135
136 136
137 137 def matchoutput(cmd, regexp, ignorestatus=False):
138 138 """Return the match object if cmd executes successfully and its output
139 139 is matched by the supplied regular expression.
140 140 """
141 141 r = re.compile(regexp)
142 142 p = subprocess.Popen(
143 143 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
144 144 )
145 145 s = p.communicate()[0]
146 146 ret = p.returncode
147 147 return (ignorestatus or not ret) and r.search(s)
148 148
149 149
150 150 @check("baz", "GNU Arch baz client")
151 151 def has_baz():
152 152 return matchoutput('baz --version 2>&1', br'baz Bazaar version')
153 153
154 154
155 155 @check("bzr", "Canonical's Bazaar client")
156 156 def has_bzr():
157 157 try:
158 158 import bzrlib
159 159 import bzrlib.bzrdir
160 160 import bzrlib.errors
161 161 import bzrlib.revision
162 162 import bzrlib.revisionspec
163 163
164 164 bzrlib.revisionspec.RevisionSpec
165 165 return bzrlib.__doc__ is not None
166 166 except (AttributeError, ImportError):
167 167 return False
168 168
169 169
170 170 @checkvers("bzr", "Canonical's Bazaar client >= %s", (1.14,))
171 171 def has_bzr_range(v):
172 172 major, minor = v.split('rc')[0].split('.')[0:2]
173 173 try:
174 174 import bzrlib
175 175
176 176 return bzrlib.__doc__ is not None and bzrlib.version_info[:2] >= (
177 177 int(major),
178 178 int(minor),
179 179 )
180 180 except ImportError:
181 181 return False
182 182
183 183
184 184 @check("chg", "running with chg")
185 185 def has_chg():
186 186 return 'CHGHG' in os.environ
187 187
188 188
189 189 @check("cvs", "cvs client/server")
190 190 def has_cvs():
191 191 re = br'Concurrent Versions System.*?server'
192 192 return matchoutput('cvs --version 2>&1', re) and not has_msys()
193 193
194 194
195 195 @check("cvs112", "cvs client/server 1.12.* (not cvsnt)")
196 196 def has_cvs112():
197 197 re = br'Concurrent Versions System \(CVS\) 1.12.*?server'
198 198 return matchoutput('cvs --version 2>&1', re) and not has_msys()
199 199
200 200
201 201 @check("cvsnt", "cvsnt client/server")
202 202 def has_cvsnt():
203 203 re = br'Concurrent Versions System \(CVSNT\) (\d+).(\d+).*\(client/server\)'
204 204 return matchoutput('cvsnt --version 2>&1', re)
205 205
206 206
207 207 @check("darcs", "darcs client")
208 208 def has_darcs():
209 209 return matchoutput('darcs --version', br'\b2\.([2-9]|\d{2})', True)
210 210
211 211
212 212 @check("mtn", "monotone client (>= 1.0)")
213 213 def has_mtn():
214 214 return matchoutput('mtn --version', br'monotone', True) and not matchoutput(
215 215 'mtn --version', br'monotone 0\.', True
216 216 )
217 217
218 218
219 219 @check("eol-in-paths", "end-of-lines in paths")
220 220 def has_eol_in_paths():
221 221 try:
222 222 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
223 223 os.close(fd)
224 224 os.remove(path)
225 225 return True
226 226 except (IOError, OSError):
227 227 return False
228 228
229 229
230 230 @check("execbit", "executable bit")
231 231 def has_executablebit():
232 232 try:
233 233 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
234 234 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
235 235 try:
236 236 os.close(fh)
237 237 m = os.stat(fn).st_mode & 0o777
238 238 new_file_has_exec = m & EXECFLAGS
239 239 os.chmod(fn, m ^ EXECFLAGS)
240 240 exec_flags_cannot_flip = (os.stat(fn).st_mode & 0o777) == m
241 241 finally:
242 242 os.unlink(fn)
243 243 except (IOError, OSError):
244 244 # we don't care, the user probably won't be able to commit anyway
245 245 return False
246 246 return not (new_file_has_exec or exec_flags_cannot_flip)
247 247
248 248
249 249 @check("icasefs", "case insensitive file system")
250 250 def has_icasefs():
251 251 # Stolen from mercurial.util
252 252 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
253 253 os.close(fd)
254 254 try:
255 255 s1 = os.stat(path)
256 256 d, b = os.path.split(path)
257 257 p2 = os.path.join(d, b.upper())
258 258 if path == p2:
259 259 p2 = os.path.join(d, b.lower())
260 260 try:
261 261 s2 = os.stat(p2)
262 262 return s2 == s1
263 263 except OSError:
264 264 return False
265 265 finally:
266 266 os.remove(path)
267 267
268 268
269 269 @check("fifo", "named pipes")
270 270 def has_fifo():
271 271 if getattr(os, "mkfifo", None) is None:
272 272 return False
273 273 name = tempfile.mktemp(dir='.', prefix=tempprefix)
274 274 try:
275 275 os.mkfifo(name)
276 276 os.unlink(name)
277 277 return True
278 278 except OSError:
279 279 return False
280 280
281 281
282 282 @check("killdaemons", 'killdaemons.py support')
283 283 def has_killdaemons():
284 284 return True
285 285
286 286
287 287 @check("cacheable", "cacheable filesystem")
288 288 def has_cacheable_fs():
289 289 from mercurial import util
290 290
291 291 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
292 292 os.close(fd)
293 293 try:
294 294 return util.cachestat(path).cacheable()
295 295 finally:
296 296 os.remove(path)
297 297
298 298
299 299 @check("lsprof", "python lsprof module")
300 300 def has_lsprof():
301 301 try:
302 302 import _lsprof
303 303
304 304 _lsprof.Profiler # silence unused import warning
305 305 return True
306 306 except ImportError:
307 307 return False
308 308
309 309
310 310 def _gethgversion():
311 311 m = matchoutput('hg --version --quiet 2>&1', br'(\d+)\.(\d+)')
312 312 if not m:
313 313 return (0, 0)
314 314 return (int(m.group(1)), int(m.group(2)))
315 315
316 316
317 317 _hgversion = None
318 318
319 319
320 320 def gethgversion():
321 321 global _hgversion
322 322 if _hgversion is None:
323 323 _hgversion = _gethgversion()
324 324 return _hgversion
325 325
326 326
327 327 @checkvers(
328 328 "hg", "Mercurial >= %s", list([(1.0 * x) / 10 for x in range(9, 99)])
329 329 )
330 330 def has_hg_range(v):
331 331 major, minor = v.split('.')[0:2]
332 332 return gethgversion() >= (int(major), int(minor))
333 333
334 334
335 335 @check("rust", "Using the Rust extensions")
336 336 def has_rust():
337 337 """Check is the mercurial currently running is using some rust code"""
338 338 cmd = 'hg debuginstall --quiet 2>&1'
339 339 match = br'checking module policy \(([^)]+)\)'
340 340 policy = matchoutput(cmd, match)
341 341 if not policy:
342 342 return False
343 343 return b'rust' in policy.group(1)
344 344
345 345
346 346 @check("hg08", "Mercurial >= 0.8")
347 347 def has_hg08():
348 348 if checks["hg09"][0]():
349 349 return True
350 350 return matchoutput('hg help annotate 2>&1', '--date')
351 351
352 352
353 353 @check("hg07", "Mercurial >= 0.7")
354 354 def has_hg07():
355 355 if checks["hg08"][0]():
356 356 return True
357 357 return matchoutput('hg --version --quiet 2>&1', 'Mercurial Distributed SCM')
358 358
359 359
360 360 @check("hg06", "Mercurial >= 0.6")
361 361 def has_hg06():
362 362 if checks["hg07"][0]():
363 363 return True
364 364 return matchoutput('hg --version --quiet 2>&1', 'Mercurial version')
365 365
366 366
367 367 @check("gettext", "GNU Gettext (msgfmt)")
368 368 def has_gettext():
369 369 return matchoutput('msgfmt --version', br'GNU gettext-tools')
370 370
371 371
372 372 @check("git", "git command line client")
373 373 def has_git():
374 374 return matchoutput('git --version 2>&1', br'^git version')
375 375
376 376
377 377 def getgitversion():
378 378 m = matchoutput('git --version 2>&1', br'git version (\d+)\.(\d+)')
379 379 if not m:
380 380 return (0, 0)
381 381 return (int(m.group(1)), int(m.group(2)))
382 382
383 383
384 384 @check("pygit2", "pygit2 Python library")
385 385 def has_git():
386 386 try:
387 387 import pygit2
388 388
389 389 pygit2.Oid # silence unused import
390 390 return True
391 391 except ImportError:
392 392 return False
393 393
394 394
395 395 # https://github.com/git-lfs/lfs-test-server
396 396 @check("lfs-test-server", "git-lfs test server")
397 397 def has_lfsserver():
398 398 exe = 'lfs-test-server'
399 399 if has_windows():
400 400 exe = 'lfs-test-server.exe'
401 401 return any(
402 402 os.access(os.path.join(path, exe), os.X_OK)
403 403 for path in os.environ["PATH"].split(os.pathsep)
404 404 )
405 405
406 406
407 407 @checkvers("git", "git client (with ext::sh support) version >= %s", (1.9,))
408 408 def has_git_range(v):
409 409 major, minor = v.split('.')[0:2]
410 410 return getgitversion() >= (int(major), int(minor))
411 411
412 412
413 413 @check("docutils", "Docutils text processing library")
414 414 def has_docutils():
415 415 try:
416 416 import docutils.core
417 417
418 418 docutils.core.publish_cmdline # silence unused import
419 419 return True
420 420 except ImportError:
421 421 return False
422 422
423 423
424 424 def getsvnversion():
425 425 m = matchoutput('svn --version --quiet 2>&1', br'^(\d+)\.(\d+)')
426 426 if not m:
427 427 return (0, 0)
428 428 return (int(m.group(1)), int(m.group(2)))
429 429
430 430
431 431 @checkvers("svn", "subversion client and admin tools >= %s", (1.3, 1.5))
432 432 def has_svn_range(v):
433 433 major, minor = v.split('.')[0:2]
434 434 return getsvnversion() >= (int(major), int(minor))
435 435
436 436
437 437 @check("svn", "subversion client and admin tools")
438 438 def has_svn():
439 439 return matchoutput('svn --version 2>&1', br'^svn, version') and matchoutput(
440 440 'svnadmin --version 2>&1', br'^svnadmin, version'
441 441 )
442 442
443 443
444 444 @check("svn-bindings", "subversion python bindings")
445 445 def has_svn_bindings():
446 446 try:
447 447 import svn.core
448 448
449 449 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
450 450 if version < (1, 4):
451 451 return False
452 452 return True
453 453 except ImportError:
454 454 return False
455 455
456 456
457 457 @check("p4", "Perforce server and client")
458 458 def has_p4():
459 459 return matchoutput('p4 -V', br'Rev\. P4/') and matchoutput(
460 460 'p4d -V', br'Rev\. P4D/'
461 461 )
462 462
463 463
464 464 @check("symlink", "symbolic links")
465 465 def has_symlink():
466 466 # mercurial.windows.checklink() is a hard 'no' at the moment
467 467 if os.name == 'nt' or getattr(os, "symlink", None) is None:
468 468 return False
469 469 name = tempfile.mktemp(dir='.', prefix=tempprefix)
470 470 try:
471 471 os.symlink(".", name)
472 472 os.unlink(name)
473 473 return True
474 474 except (OSError, AttributeError):
475 475 return False
476 476
477 477
478 478 @check("hardlink", "hardlinks")
479 479 def has_hardlink():
480 480 from mercurial import util
481 481
482 482 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
483 483 os.close(fh)
484 484 name = tempfile.mktemp(dir='.', prefix=tempprefix)
485 485 try:
486 486 util.oslink(_sys2bytes(fn), _sys2bytes(name))
487 487 os.unlink(name)
488 488 return True
489 489 except OSError:
490 490 return False
491 491 finally:
492 492 os.unlink(fn)
493 493
494 494
495 495 @check("hardlink-whitelisted", "hardlinks on whitelisted filesystems")
496 496 def has_hardlink_whitelisted():
497 497 from mercurial import util
498 498
499 499 try:
500 500 fstype = util.getfstype(b'.')
501 501 except OSError:
502 502 return False
503 503 return fstype in util._hardlinkfswhitelist
504 504
505 505
506 506 @check("rmcwd", "can remove current working directory")
507 507 def has_rmcwd():
508 508 ocwd = os.getcwd()
509 509 temp = tempfile.mkdtemp(dir='.', prefix=tempprefix)
510 510 try:
511 511 os.chdir(temp)
512 512 # On Linux, 'rmdir .' isn't allowed, but the other names are okay.
513 513 # On Solaris and Windows, the cwd can't be removed by any names.
514 514 os.rmdir(os.getcwd())
515 515 return True
516 516 except OSError:
517 517 return False
518 518 finally:
519 519 os.chdir(ocwd)
520 520 # clean up temp dir on platforms where cwd can't be removed
521 521 try:
522 522 os.rmdir(temp)
523 523 except OSError:
524 524 pass
525 525
526 526
527 527 @check("tla", "GNU Arch tla client")
528 528 def has_tla():
529 529 return matchoutput('tla --version 2>&1', br'The GNU Arch Revision')
530 530
531 531
532 532 @check("gpg", "gpg client")
533 533 def has_gpg():
534 534 return matchoutput('gpg --version 2>&1', br'GnuPG')
535 535
536 536
537 537 @check("gpg2", "gpg client v2")
538 538 def has_gpg2():
539 539 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.')
540 540
541 541
542 542 @check("gpg21", "gpg client v2.1+")
543 543 def has_gpg21():
544 544 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.(?!0)')
545 545
546 546
547 547 @check("unix-permissions", "unix-style permissions")
548 548 def has_unix_permissions():
549 549 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
550 550 try:
551 551 fname = os.path.join(d, 'foo')
552 552 for umask in (0o77, 0o07, 0o22):
553 553 os.umask(umask)
554 554 f = open(fname, 'w')
555 555 f.close()
556 556 mode = os.stat(fname).st_mode
557 557 os.unlink(fname)
558 558 if mode & 0o777 != ~umask & 0o666:
559 559 return False
560 560 return True
561 561 finally:
562 562 os.rmdir(d)
563 563
564 564
565 565 @check("unix-socket", "AF_UNIX socket family")
566 566 def has_unix_socket():
567 567 return getattr(socket, 'AF_UNIX', None) is not None
568 568
569 569
570 570 @check("root", "root permissions")
571 571 def has_root():
572 572 return getattr(os, 'geteuid', None) and os.geteuid() == 0
573 573
574 574
575 575 @check("pyflakes", "Pyflakes python linter")
576 576 def has_pyflakes():
577 577 try:
578 578 import pyflakes
579 579
580 580 pyflakes.__version__
581 581 except ImportError:
582 582 return False
583 583 else:
584 584 return True
585 585
586 586
587 587 @check("pylint", "Pylint python linter")
588 588 def has_pylint():
589 589 return matchoutput("pylint --help", br"Usage: pylint", True)
590 590
591 591
592 592 @check("clang-format", "clang-format C code formatter")
593 593 def has_clang_format():
594 594 m = matchoutput('clang-format --version', br'clang-format version (\d)')
595 595 # style changed somewhere between 4.x and 6.x
596 596 return m and int(m.group(1)) >= 6
597 597
598 598
599 599 @check("jshint", "JSHint static code analysis tool")
600 600 def has_jshint():
601 601 return matchoutput("jshint --version 2>&1", br"jshint v")
602 602
603 603
604 604 @check("pygments", "Pygments source highlighting library")
605 605 def has_pygments():
606 606 try:
607 607 import pygments
608 608
609 609 pygments.highlight # silence unused import warning
610 610 return True
611 611 except ImportError:
612 612 return False
613 613
614 614
615 615 @check("pygments25", "Pygments version >= 2.5")
616 616 def pygments25():
617 617 try:
618 618 import pygments
619 619
620 620 v = pygments.__version__
621 621 except ImportError:
622 622 return False
623 623
624 624 parts = v.split(".")
625 625 major = int(parts[0])
626 626 minor = int(parts[1])
627 627
628 628 return (major, minor) >= (2, 5)
629 629
630 630
631 631 @check("outer-repo", "outer repo")
632 632 def has_outer_repo():
633 633 # failing for other reasons than 'no repo' imply that there is a repo
634 634 return not matchoutput('hg root 2>&1', br'abort: no repository found', True)
635 635
636 636
637 637 @check("ssl", "ssl module available")
638 638 def has_ssl():
639 639 try:
640 640 import ssl
641 641
642 642 ssl.CERT_NONE
643 643 return True
644 644 except ImportError:
645 645 return False
646 646
647 647
648 648 @check("defaultcacertsloaded", "detected presence of loaded system CA certs")
649 649 def has_defaultcacertsloaded():
650 650 import ssl
651 651 from mercurial import sslutil, ui as uimod
652 652
653 653 ui = uimod.ui.load()
654 654 cafile = sslutil._defaultcacerts(ui)
655 655 ctx = ssl.create_default_context()
656 656 if cafile:
657 657 ctx.load_verify_locations(cafile=cafile)
658 658 else:
659 659 ctx.load_default_certs()
660 660
661 661 return len(ctx.get_ca_certs()) > 0
662 662
663 663
664 664 @check("tls1.2", "TLS 1.2 protocol support")
665 665 def has_tls1_2():
666 666 from mercurial import sslutil
667 667
668 668 return b'tls1.2' in sslutil.supportedprotocols
669 669
670 670
671 671 @check("windows", "Windows")
672 672 def has_windows():
673 673 return os.name == 'nt'
674 674
675 675
676 676 @check("system-sh", "system() uses sh")
677 677 def has_system_sh():
678 678 return os.name != 'nt'
679 679
680 680
681 681 @check("serve", "platform and python can manage 'hg serve -d'")
682 682 def has_serve():
683 683 return True
684 684
685 685
686 @check("setprocname", "whether osutil.setprocname is available or not")
687 def has_setprocname():
688 try:
689 from mercurial.utils import procutil
690
691 procutil.setprocname
692 return True
693 except AttributeError:
694 return False
695
696
686 697 @check("test-repo", "running tests from repository")
687 698 def has_test_repo():
688 699 t = os.environ["TESTDIR"]
689 700 return os.path.isdir(os.path.join(t, "..", ".hg"))
690 701
691 702
692 703 @check("tic", "terminfo compiler and curses module")
693 704 def has_tic():
694 705 try:
695 706 import curses
696 707
697 708 curses.COLOR_BLUE
698 709 return matchoutput('test -x "`which tic`"', br'')
699 710 except (ImportError, AttributeError):
700 711 return False
701 712
702 713
703 714 @check("xz", "xz compression utility")
704 715 def has_xz():
705 716 # When Windows invokes a subprocess in shell mode, it uses `cmd.exe`, which
706 717 # only knows `where`, not `which`. So invoke MSYS shell explicitly.
707 718 return matchoutput("sh -c 'test -x \"`which xz`\"'", b'')
708 719
709 720
710 721 @check("msys", "Windows with MSYS")
711 722 def has_msys():
712 723 return os.getenv('MSYSTEM')
713 724
714 725
715 726 @check("aix", "AIX")
716 727 def has_aix():
717 728 return sys.platform.startswith("aix")
718 729
719 730
720 731 @check("osx", "OS X")
721 732 def has_osx():
722 733 return sys.platform == 'darwin'
723 734
724 735
725 736 @check("osxpackaging", "OS X packaging tools")
726 737 def has_osxpackaging():
727 738 try:
728 739 return (
729 740 matchoutput('pkgbuild', br'Usage: pkgbuild ', ignorestatus=1)
730 741 and matchoutput(
731 742 'productbuild', br'Usage: productbuild ', ignorestatus=1
732 743 )
733 744 and matchoutput('lsbom', br'Usage: lsbom', ignorestatus=1)
734 745 and matchoutput('xar --help', br'Usage: xar', ignorestatus=1)
735 746 )
736 747 except ImportError:
737 748 return False
738 749
739 750
740 751 @check('linuxormacos', 'Linux or MacOS')
741 752 def has_linuxormacos():
742 753 # This isn't a perfect test for MacOS. But it is sufficient for our needs.
743 754 return sys.platform.startswith(('linux', 'darwin'))
744 755
745 756
746 757 @check("docker", "docker support")
747 758 def has_docker():
748 759 pat = br'A self-sufficient runtime for'
749 760 if matchoutput('docker --help', pat):
750 761 if 'linux' not in sys.platform:
751 762 # TODO: in theory we should be able to test docker-based
752 763 # package creation on non-linux using boot2docker, but in
753 764 # practice that requires extra coordination to make sure
754 765 # $TESTTEMP is going to be visible at the same path to the
755 766 # boot2docker VM. If we figure out how to verify that, we
756 767 # can use the following instead of just saying False:
757 768 # return 'DOCKER_HOST' in os.environ
758 769 return False
759 770
760 771 return True
761 772 return False
762 773
763 774
764 775 @check("debhelper", "debian packaging tools")
765 776 def has_debhelper():
766 777 # Some versions of dpkg say `dpkg', some say 'dpkg' (` vs ' on the first
767 778 # quote), so just accept anything in that spot.
768 779 dpkg = matchoutput(
769 780 'dpkg --version', br"Debian .dpkg' package management program"
770 781 )
771 782 dh = matchoutput(
772 783 'dh --help', br'dh is a part of debhelper.', ignorestatus=True
773 784 )
774 785 dh_py2 = matchoutput(
775 786 'dh_python2 --help', br'other supported Python versions'
776 787 )
777 788 # debuild comes from the 'devscripts' package, though you might want
778 789 # the 'build-debs' package instead, which has a dependency on devscripts.
779 790 debuild = matchoutput(
780 791 'debuild --help', br'to run debian/rules with given parameter'
781 792 )
782 793 return dpkg and dh and dh_py2 and debuild
783 794
784 795
785 796 @check(
786 797 "debdeps", "debian build dependencies (run dpkg-checkbuilddeps in contrib/)"
787 798 )
788 799 def has_debdeps():
789 800 # just check exit status (ignoring output)
790 801 path = '%s/../contrib/packaging/debian/control' % os.environ['TESTDIR']
791 802 return matchoutput('dpkg-checkbuilddeps %s' % path, br'')
792 803
793 804
794 805 @check("demandimport", "demandimport enabled")
795 806 def has_demandimport():
796 807 # chg disables demandimport intentionally for performance wins.
797 808 return (not has_chg()) and os.environ.get('HGDEMANDIMPORT') != 'disable'
798 809
799 810
800 811 # Add "py27", "py35", ... as possible feature checks. Note that there's no
801 812 # punctuation here.
802 813 @checkvers("py", "Python >= %s", (2.7, 3.5, 3.6, 3.7, 3.8, 3.9))
803 814 def has_python_range(v):
804 815 major, minor = v.split('.')[0:2]
805 816 py_major, py_minor = sys.version_info.major, sys.version_info.minor
806 817
807 818 return (py_major, py_minor) >= (int(major), int(minor))
808 819
809 820
810 821 @check("py3", "running with Python 3.x")
811 822 def has_py3():
812 823 return 3 == sys.version_info[0]
813 824
814 825
815 826 @check("py3exe", "a Python 3.x interpreter is available")
816 827 def has_python3exe():
817 828 return matchoutput('python3 -V', br'^Python 3.(5|6|7|8|9)')
818 829
819 830
820 831 @check("pure", "running with pure Python code")
821 832 def has_pure():
822 833 return any(
823 834 [
824 835 os.environ.get("HGMODULEPOLICY") == "py",
825 836 os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure",
826 837 ]
827 838 )
828 839
829 840
830 841 @check("slow", "allow slow tests (use --allow-slow-tests)")
831 842 def has_slow():
832 843 return os.environ.get('HGTEST_SLOW') == 'slow'
833 844
834 845
835 846 @check("hypothesis", "Hypothesis automated test generation")
836 847 def has_hypothesis():
837 848 try:
838 849 import hypothesis
839 850
840 851 hypothesis.given
841 852 return True
842 853 except ImportError:
843 854 return False
844 855
845 856
846 857 @check("unziplinks", "unzip(1) understands and extracts symlinks")
847 858 def unzip_understands_symlinks():
848 859 return matchoutput('unzip --help', br'Info-ZIP')
849 860
850 861
851 862 @check("zstd", "zstd Python module available")
852 863 def has_zstd():
853 864 try:
854 865 import mercurial.zstd
855 866
856 867 mercurial.zstd.__version__
857 868 return True
858 869 except ImportError:
859 870 return False
860 871
861 872
862 873 @check("devfull", "/dev/full special file")
863 874 def has_dev_full():
864 875 return os.path.exists('/dev/full')
865 876
866 877
867 878 @check("ensurepip", "ensurepip module")
868 879 def has_ensurepip():
869 880 try:
870 881 import ensurepip
871 882
872 883 ensurepip.bootstrap
873 884 return True
874 885 except ImportError:
875 886 return False
876 887
877 888
878 889 @check("virtualenv", "Python virtualenv support")
879 890 def has_virtualenv():
880 891 try:
881 892 import virtualenv
882 893
883 894 virtualenv.ACTIVATE_SH
884 895 return True
885 896 except ImportError:
886 897 return False
887 898
888 899
889 900 @check("fsmonitor", "running tests with fsmonitor")
890 901 def has_fsmonitor():
891 902 return 'HGFSMONITOR_TESTS' in os.environ
892 903
893 904
894 905 @check("fuzzywuzzy", "Fuzzy string matching library")
895 906 def has_fuzzywuzzy():
896 907 try:
897 908 import fuzzywuzzy
898 909
899 910 fuzzywuzzy.__version__
900 911 return True
901 912 except ImportError:
902 913 return False
903 914
904 915
905 916 @check("clang-libfuzzer", "clang new enough to include libfuzzer")
906 917 def has_clang_libfuzzer():
907 918 mat = matchoutput('clang --version', br'clang version (\d)')
908 919 if mat:
909 920 # libfuzzer is new in clang 6
910 921 return int(mat.group(1)) > 5
911 922 return False
912 923
913 924
914 925 @check("clang-6.0", "clang 6.0 with version suffix (libfuzzer included)")
915 926 def has_clang60():
916 927 return matchoutput('clang-6.0 --version', br'clang version 6\.')
917 928
918 929
919 930 @check("xdiff", "xdiff algorithm")
920 931 def has_xdiff():
921 932 try:
922 933 from mercurial import policy
923 934
924 935 bdiff = policy.importmod('bdiff')
925 936 return bdiff.xdiffblocks(b'', b'') == [(0, 0, 0, 0)]
926 937 except (ImportError, AttributeError):
927 938 return False
928 939
929 940
930 941 @check('extraextensions', 'whether tests are running with extra extensions')
931 942 def has_extraextensions():
932 943 return 'HGTESTEXTRAEXTENSIONS' in os.environ
933 944
934 945
935 946 def getrepofeatures():
936 947 """Obtain set of repository features in use.
937 948
938 949 HGREPOFEATURES can be used to define or remove features. It contains
939 950 a space-delimited list of feature strings. Strings beginning with ``-``
940 951 mean to remove.
941 952 """
942 953 # Default list provided by core.
943 954 features = {
944 955 'bundlerepo',
945 956 'revlogstore',
946 957 'fncache',
947 958 }
948 959
949 960 # Features that imply other features.
950 961 implies = {
951 962 'simplestore': ['-revlogstore', '-bundlerepo', '-fncache'],
952 963 }
953 964
954 965 for override in os.environ.get('HGREPOFEATURES', '').split(' '):
955 966 if not override:
956 967 continue
957 968
958 969 if override.startswith('-'):
959 970 if override[1:] in features:
960 971 features.remove(override[1:])
961 972 else:
962 973 features.add(override)
963 974
964 975 for imply in implies.get(override, []):
965 976 if imply.startswith('-'):
966 977 if imply[1:] in features:
967 978 features.remove(imply[1:])
968 979 else:
969 980 features.add(imply)
970 981
971 982 return features
972 983
973 984
974 985 @check('reporevlogstore', 'repository using the default revlog store')
975 986 def has_reporevlogstore():
976 987 return 'revlogstore' in getrepofeatures()
977 988
978 989
979 990 @check('reposimplestore', 'repository using simple storage extension')
980 991 def has_reposimplestore():
981 992 return 'simplestore' in getrepofeatures()
982 993
983 994
984 995 @check('repobundlerepo', 'whether we can open bundle files as repos')
985 996 def has_repobundlerepo():
986 997 return 'bundlerepo' in getrepofeatures()
987 998
988 999
989 1000 @check('repofncache', 'repository has an fncache')
990 1001 def has_repofncache():
991 1002 return 'fncache' in getrepofeatures()
992 1003
993 1004
994 1005 @check('sqlite', 'sqlite3 module is available')
995 1006 def has_sqlite():
996 1007 try:
997 1008 import sqlite3
998 1009
999 1010 version = sqlite3.sqlite_version_info
1000 1011 except ImportError:
1001 1012 return False
1002 1013
1003 1014 if version < (3, 8, 3):
1004 1015 # WITH clause not supported
1005 1016 return False
1006 1017
1007 1018 return matchoutput('sqlite3 -version', br'^3\.\d+')
1008 1019
1009 1020
1010 1021 @check('vcr', 'vcr http mocking library')
1011 1022 def has_vcr():
1012 1023 try:
1013 1024 import vcr
1014 1025
1015 1026 vcr.VCR
1016 1027 return True
1017 1028 except (ImportError, AttributeError):
1018 1029 pass
1019 1030 return False
1020 1031
1021 1032
1022 1033 @check('emacs', 'GNU Emacs')
1023 1034 def has_emacs():
1024 1035 # Our emacs lisp uses `with-eval-after-load` which is new in emacs
1025 1036 # 24.4, so we allow emacs 24.4, 24.5, and 25+ (24.5 was the last
1026 1037 # 24 release)
1027 1038 return matchoutput('emacs --version', b'GNU Emacs 2(4.4|4.5|5|6|7|8|9)')
1028 1039
1029 1040
1030 1041 @check('black', 'the black formatter for python')
1031 1042 def has_black():
1032 1043 blackcmd = 'black --version'
1033 1044 version_regex = b'black, version ([0-9a-b.]+)'
1034 1045 version = matchoutput(blackcmd, version_regex)
1035 1046 sv = distutils.version.StrictVersion
1036 1047 return version and sv(_bytes2sys(version.group(1))) >= sv('19.10b0')
1037 1048
1038 1049
1039 1050 @check('pytype', 'the pytype type checker')
1040 1051 def has_pytype():
1041 1052 pytypecmd = 'pytype --version'
1042 1053 version = matchoutput(pytypecmd, b'[0-9a-b.]+')
1043 1054 sv = distutils.version.StrictVersion
1044 1055 return version and sv(_bytes2sys(version.group(0))) >= sv('2019.10.17')
1045 1056
1046 1057
1047 1058 @check("rustfmt", "rustfmt tool")
1048 1059 def has_rustfmt():
1049 1060 # We use Nightly's rustfmt due to current unstable config options.
1050 1061 return matchoutput(
1051 1062 '`rustup which --toolchain nightly rustfmt` --version', b'rustfmt'
1052 1063 )
@@ -1,418 +1,418 b''
1 1 #require chg
2 2
3 3 $ mkdir log
4 4 $ cp $HGRCPATH $HGRCPATH.unconfigured
5 5 $ cat <<'EOF' >> $HGRCPATH
6 6 > [cmdserver]
7 7 > log = $TESTTMP/log/server.log
8 8 > max-log-files = 1
9 9 > max-log-size = 10 kB
10 10 > EOF
11 11 $ cp $HGRCPATH $HGRCPATH.orig
12 12
13 13 $ filterlog () {
14 14 > sed -e 's!^[0-9/]* [0-9:]* ([0-9]*)>!YYYY/MM/DD HH:MM:SS (PID)>!' \
15 15 > -e 's!\(setprocname\|received fds\|setenv\): .*!\1: ...!' \
16 16 > -e 's!\(confighash\|mtimehash\) = [0-9a-f]*!\1 = ...!g' \
17 17 > -e 's!\(in \)[0-9.]*s\b!\1 ...s!g' \
18 18 > -e 's!\(pid\)=[0-9]*!\1=...!g' \
19 19 > -e 's!\(/server-\)[0-9a-f]*!\1...!g'
20 20 > }
21 21
22 22 init repo
23 23
24 24 $ chg init foo
25 25 $ cd foo
26 26
27 27 ill-formed config
28 28
29 29 $ chg status
30 30 $ echo '=brokenconfig' >> $HGRCPATH
31 31 $ chg status
32 32 hg: parse error at * (glob)
33 33 [255]
34 34
35 35 $ cp $HGRCPATH.orig $HGRCPATH
36 36
37 37 long socket path
38 38
39 39 $ sockpath=$TESTTMP/this/path/should/be/longer/than/one-hundred-and-seven/characters/where/107/is/the/typical/size/limit/of/unix-domain-socket
40 40 $ mkdir -p $sockpath
41 41 $ bakchgsockname=$CHGSOCKNAME
42 42 $ CHGSOCKNAME=$sockpath/server
43 43 $ export CHGSOCKNAME
44 44 $ chg root
45 45 $TESTTMP/foo
46 46 $ rm -rf $sockpath
47 47 $ CHGSOCKNAME=$bakchgsockname
48 48 $ export CHGSOCKNAME
49 49
50 50 $ cd ..
51 51
52 52 editor
53 53 ------
54 54
55 55 $ cat >> pushbuffer.py <<EOF
56 56 > def reposetup(ui, repo):
57 57 > repo.ui.pushbuffer(subproc=True)
58 58 > EOF
59 59
60 60 $ chg init editor
61 61 $ cd editor
62 62
63 63 by default, system() should be redirected to the client:
64 64
65 65 $ touch foo
66 66 $ CHGDEBUG= HGEDITOR=cat chg ci -Am channeled --edit 2>&1 \
67 67 > | egrep "HG:|run 'cat"
68 68 chg: debug: * run 'cat "*"' at '$TESTTMP/editor' (glob)
69 69 HG: Enter commit message. Lines beginning with 'HG:' are removed.
70 70 HG: Leave message empty to abort commit.
71 71 HG: --
72 72 HG: user: test
73 73 HG: branch 'default'
74 74 HG: added foo
75 75
76 76 but no redirection should be made if output is captured:
77 77
78 78 $ touch bar
79 79 $ CHGDEBUG= HGEDITOR=cat chg ci -Am bufferred --edit \
80 80 > --config extensions.pushbuffer="$TESTTMP/pushbuffer.py" 2>&1 \
81 81 > | egrep "HG:|run 'cat"
82 82 [1]
83 83
84 84 check that commit commands succeeded:
85 85
86 86 $ hg log -T '{rev}:{desc}\n'
87 87 1:bufferred
88 88 0:channeled
89 89
90 90 $ cd ..
91 91
92 92 pager
93 93 -----
94 94
95 95 $ cat >> fakepager.py <<EOF
96 96 > import sys
97 97 > for line in sys.stdin:
98 98 > sys.stdout.write('paged! %r\n' % line)
99 99 > EOF
100 100
101 101 enable pager extension globally, but spawns the master server with no tty:
102 102
103 103 $ chg init pager
104 104 $ cd pager
105 105 $ cat >> $HGRCPATH <<EOF
106 106 > [extensions]
107 107 > pager =
108 108 > [pager]
109 109 > pager = "$PYTHON" $TESTTMP/fakepager.py
110 110 > EOF
111 111 $ chg version > /dev/null
112 112 $ touch foo
113 113 $ chg ci -qAm foo
114 114
115 115 pager should be enabled if the attached client has a tty:
116 116
117 117 $ chg log -l1 -q --config ui.formatted=True
118 118 paged! '0:1f7b0de80e11\n'
119 119 $ chg log -l1 -q --config ui.formatted=False
120 120 0:1f7b0de80e11
121 121
122 122 chg waits for pager if runcommand raises
123 123
124 124 $ cat > $TESTTMP/crash.py <<EOF
125 125 > from mercurial import registrar
126 126 > cmdtable = {}
127 127 > command = registrar.command(cmdtable)
128 128 > @command(b'crash')
129 129 > def pagercrash(ui, repo, *pats, **opts):
130 130 > ui.write(b'going to crash\n')
131 131 > raise Exception('.')
132 132 > EOF
133 133
134 134 $ cat > $TESTTMP/fakepager.py <<EOF
135 135 > from __future__ import absolute_import
136 136 > import sys
137 137 > import time
138 138 > for line in iter(sys.stdin.readline, ''):
139 139 > if 'crash' in line: # only interested in lines containing 'crash'
140 140 > # if chg exits when pager is sleeping (incorrectly), the output
141 141 > # will be captured by the next test case
142 142 > time.sleep(1)
143 143 > sys.stdout.write('crash-pager: %s' % line)
144 144 > EOF
145 145
146 146 $ cat >> .hg/hgrc <<EOF
147 147 > [extensions]
148 148 > crash = $TESTTMP/crash.py
149 149 > EOF
150 150
151 151 $ chg crash --pager=on --config ui.formatted=True 2>/dev/null
152 152 crash-pager: going to crash
153 153 [255]
154 154
155 155 $ cd ..
156 156
157 157 server lifecycle
158 158 ----------------
159 159
160 160 chg server should be restarted on code change, and old server will shut down
161 161 automatically. In this test, we use the following time parameters:
162 162
163 163 - "sleep 1" to make mtime different
164 164 - "sleep 2" to notice mtime change (polling interval is 1 sec)
165 165
166 166 set up repository with an extension:
167 167
168 168 $ chg init extreload
169 169 $ cd extreload
170 170 $ touch dummyext.py
171 171 $ cat <<EOF >> .hg/hgrc
172 172 > [extensions]
173 173 > dummyext = dummyext.py
174 174 > EOF
175 175
176 176 isolate socket directory for stable result:
177 177
178 178 $ OLDCHGSOCKNAME=$CHGSOCKNAME
179 179 $ mkdir chgsock
180 180 $ CHGSOCKNAME=`pwd`/chgsock/server
181 181
182 182 warm up server:
183 183
184 184 $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
185 185 chg: debug: * start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
186 186
187 187 new server should be started if extension modified:
188 188
189 189 $ sleep 1
190 190 $ touch dummyext.py
191 191 $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
192 192 chg: debug: * instruction: unlink $TESTTMP/extreload/chgsock/server-* (glob)
193 193 chg: debug: * instruction: reconnect (glob)
194 194 chg: debug: * start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
195 195
196 196 old server will shut down, while new server should still be reachable:
197 197
198 198 $ sleep 2
199 199 $ CHGDEBUG= chg log 2>&1 | (egrep 'instruction|start' || true)
200 200
201 201 socket file should never be unlinked by old server:
202 202 (simulates unowned socket by updating mtime, which makes sure server exits
203 203 at polling cycle)
204 204
205 205 $ ls chgsock/server-*
206 206 chgsock/server-* (glob)
207 207 $ touch chgsock/server-*
208 208 $ sleep 2
209 209 $ ls chgsock/server-*
210 210 chgsock/server-* (glob)
211 211
212 212 since no server is reachable from socket file, new server should be started:
213 213 (this test makes sure that old server shut down automatically)
214 214
215 215 $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
216 216 chg: debug: * start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
217 217
218 218 shut down servers and restore environment:
219 219
220 220 $ rm -R chgsock
221 221 $ sleep 2
222 222 $ CHGSOCKNAME=$OLDCHGSOCKNAME
223 223 $ cd ..
224 224
225 225 check that server events are recorded:
226 226
227 227 $ ls log
228 228 server.log
229 229 server.log.1
230 230
231 231 print only the last 10 lines, since we aren't sure how many records are
232 preserved (since setprocname isn't available on py3, the 10th-most-recent line
233 is different when using py3):
232 preserved (since setprocname isn't available on py3 and pure version,
233 the 10th-most-recent line is different when using py3):
234 234
235 235 $ cat log/server.log.1 log/server.log | tail -10 | filterlog
236 YYYY/MM/DD HH:MM:SS (PID)> confighash = ... mtimehash = ... (py3 !)
236 YYYY/MM/DD HH:MM:SS (PID)> confighash = ... mtimehash = ... (no-setprocname !)
237 237 YYYY/MM/DD HH:MM:SS (PID)> forked worker process (pid=...)
238 YYYY/MM/DD HH:MM:SS (PID)> setprocname: ... (no-py3 !)
238 YYYY/MM/DD HH:MM:SS (PID)> setprocname: ... (setprocname !)
239 239 YYYY/MM/DD HH:MM:SS (PID)> received fds: ...
240 240 YYYY/MM/DD HH:MM:SS (PID)> chdir to '$TESTTMP/extreload'
241 241 YYYY/MM/DD HH:MM:SS (PID)> setumask 18
242 242 YYYY/MM/DD HH:MM:SS (PID)> setenv: ...
243 243 YYYY/MM/DD HH:MM:SS (PID)> confighash = ... mtimehash = ...
244 244 YYYY/MM/DD HH:MM:SS (PID)> validate: []
245 245 YYYY/MM/DD HH:MM:SS (PID)> worker process exited (pid=...)
246 246 YYYY/MM/DD HH:MM:SS (PID)> $TESTTMP/extreload/chgsock/server-... is not owned, exiting.
247 247
248 248 global data mutated by schems
249 249 -----------------------------
250 250
251 251 $ hg init schemes
252 252 $ cd schemes
253 253
254 254 initial state
255 255
256 256 $ cat > .hg/hgrc <<'EOF'
257 257 > [extensions]
258 258 > schemes =
259 259 > [schemes]
260 260 > foo = https://foo.example.org/
261 261 > EOF
262 262 $ hg debugexpandscheme foo://expanded
263 263 https://foo.example.org/expanded
264 264 $ hg debugexpandscheme bar://unexpanded
265 265 bar://unexpanded
266 266
267 267 add bar
268 268
269 269 $ cat > .hg/hgrc <<'EOF'
270 270 > [extensions]
271 271 > schemes =
272 272 > [schemes]
273 273 > foo = https://foo.example.org/
274 274 > bar = https://bar.example.org/
275 275 > EOF
276 276 $ hg debugexpandscheme foo://expanded
277 277 https://foo.example.org/expanded
278 278 $ hg debugexpandscheme bar://expanded
279 279 https://bar.example.org/expanded
280 280
281 281 remove foo
282 282
283 283 $ cat > .hg/hgrc <<'EOF'
284 284 > [extensions]
285 285 > schemes =
286 286 > [schemes]
287 287 > bar = https://bar.example.org/
288 288 > EOF
289 289 $ hg debugexpandscheme foo://unexpanded
290 290 foo://unexpanded
291 291 $ hg debugexpandscheme bar://expanded
292 292 https://bar.example.org/expanded
293 293
294 294 $ cd ..
295 295
296 296 repository cache
297 297 ----------------
298 298
299 299 $ rm log/server.log*
300 300 $ cp $HGRCPATH.unconfigured $HGRCPATH
301 301 $ cat <<'EOF' >> $HGRCPATH
302 302 > [cmdserver]
303 303 > log = $TESTTMP/log/server.log
304 304 > max-repo-cache = 1
305 305 > track-log = command, repocache
306 306 > EOF
307 307
308 308 isolate socket directory for stable result:
309 309
310 310 $ OLDCHGSOCKNAME=$CHGSOCKNAME
311 311 $ mkdir chgsock
312 312 $ CHGSOCKNAME=`pwd`/chgsock/server
313 313
314 314 create empty repo and cache it:
315 315
316 316 $ hg init cached
317 317 $ hg id -R cached
318 318 000000000000 tip
319 319 $ sleep 1
320 320
321 321 modify repo (and cache will be invalidated):
322 322
323 323 $ touch cached/a
324 324 $ hg ci -R cached -Am 'add a'
325 325 adding a
326 326 $ sleep 1
327 327
328 328 read cached repo:
329 329
330 330 $ hg log -R cached
331 331 changeset: 0:ac82d8b1f7c4
332 332 tag: tip
333 333 user: test
334 334 date: Thu Jan 01 00:00:00 1970 +0000
335 335 summary: add a
336 336
337 337 $ sleep 1
338 338
339 339 discard cached from LRU cache:
340 340
341 341 $ hg clone cached cached2
342 342 updating to branch default
343 343 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
344 344 $ hg id -R cached2
345 345 ac82d8b1f7c4 tip
346 346 $ sleep 1
347 347
348 348 read uncached repo:
349 349
350 350 $ hg log -R cached
351 351 changeset: 0:ac82d8b1f7c4
352 352 tag: tip
353 353 user: test
354 354 date: Thu Jan 01 00:00:00 1970 +0000
355 355 summary: add a
356 356
357 357 $ sleep 1
358 358
359 359 shut down servers and restore environment:
360 360
361 361 $ rm -R chgsock
362 362 $ sleep 2
363 363 $ CHGSOCKNAME=$OLDCHGSOCKNAME
364 364
365 365 check server log:
366 366
367 367 $ cat log/server.log | filterlog
368 368 YYYY/MM/DD HH:MM:SS (PID)> worker process exited (pid=...)
369 369 YYYY/MM/DD HH:MM:SS (PID)> worker process exited (pid=...)
370 370 YYYY/MM/DD HH:MM:SS (PID)> init cached
371 371 YYYY/MM/DD HH:MM:SS (PID)> id -R cached
372 372 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in ...s)
373 373 YYYY/MM/DD HH:MM:SS (PID)> repo from cache: $TESTTMP/cached
374 374 YYYY/MM/DD HH:MM:SS (PID)> ci -R cached -Am 'add a'
375 375 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in ...s)
376 376 YYYY/MM/DD HH:MM:SS (PID)> repo from cache: $TESTTMP/cached
377 377 YYYY/MM/DD HH:MM:SS (PID)> log -R cached
378 378 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in ...s)
379 379 YYYY/MM/DD HH:MM:SS (PID)> clone cached cached2
380 380 YYYY/MM/DD HH:MM:SS (PID)> id -R cached2
381 381 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached2 (in ...s)
382 382 YYYY/MM/DD HH:MM:SS (PID)> log -R cached
383 383 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in ...s)
384 384
385 385 Test that chg works (sets to the user's actual LC_CTYPE) even when python
386 386 "coerces" the locale (py3.7+)
387 387
388 388 $ cat > $TESTTMP/debugenv.py <<EOF
389 389 > from mercurial import encoding
390 390 > from mercurial import registrar
391 391 > cmdtable = {}
392 392 > command = registrar.command(cmdtable)
393 393 > @command(b'debugenv', [], b'', norepo=True)
394 394 > def debugenv(ui):
395 395 > for k in [b'LC_ALL', b'LC_CTYPE', b'LANG']:
396 396 > v = encoding.environ.get(k)
397 397 > if v is not None:
398 398 > ui.write(b'%s=%s\n' % (k, encoding.environ[k]))
399 399 > EOF
400 400 (hg keeps python's modified LC_CTYPE, chg doesn't)
401 401 $ (unset LC_ALL; unset LANG; LC_CTYPE= "$CHGHG" \
402 402 > --config extensions.debugenv=$TESTTMP/debugenv.py debugenv)
403 403 LC_CTYPE=C.UTF-8 (py37 !)
404 404 LC_CTYPE= (no-py37 !)
405 405 $ (unset LC_ALL; unset LANG; LC_CTYPE= chg \
406 406 > --config extensions.debugenv=$TESTTMP/debugenv.py debugenv)
407 407 LC_CTYPE=
408 408 $ (unset LC_ALL; unset LANG; LC_CTYPE=unsupported_value chg \
409 409 > --config extensions.debugenv=$TESTTMP/debugenv.py debugenv)
410 410 LC_CTYPE=unsupported_value
411 411 $ (unset LC_ALL; unset LANG; LC_CTYPE= chg \
412 412 > --config extensions.debugenv=$TESTTMP/debugenv.py debugenv)
413 413 LC_CTYPE=
414 414 $ LANG= LC_ALL= LC_CTYPE= chg \
415 415 > --config extensions.debugenv=$TESTTMP/debugenv.py debugenv
416 416 LC_ALL=
417 417 LC_CTYPE=
418 418 LANG=
General Comments 0
You need to be logged in to leave comments. Login now