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