1#require no-rhg no-chg
2
3XXX-RHG this test hangs if `hg` is really `rhg`. This was hidden by the use of
4`alias hg=rhg` by run-tests.py. With such alias removed, this test is revealed
5buggy. This need to be resolved sooner than later.
6
7XXX-CHG this test hangs if `hg` is really `chg`. This was hidden by the use of
8`alias hg=chg` by run-tests.py. With such alias removed, this test is revealed
9buggy. This need to be resolved sooner than later.
10
11#if windows
12  $ PYTHONPATH="$TESTDIR/../contrib;$PYTHONPATH"
13#else
14  $ PYTHONPATH="$TESTDIR/../contrib:$PYTHONPATH"
15#endif
16  $ export PYTHONPATH
17
18typical client does not want echo-back messages, so test without it:
19
20  $ grep -v '^promptecho ' < $HGRCPATH >> $HGRCPATH.new
21  $ mv $HGRCPATH.new $HGRCPATH
22
23  $ hg init repo
24  $ cd repo
25
26  >>> from __future__ import absolute_import
27  >>> import os
28  >>> import sys
29  >>> from hgclient import bprint, check, readchannel, runcommand
30  >>> @check
31  ... def hellomessage(server):
32  ...     ch, data = readchannel(server)
33  ...     bprint(b'%c, %r' % (ch, data))
34  ...     # run an arbitrary command to make sure the next thing the server
35  ...     # sends isn't part of the hello message
36  ...     runcommand(server, [b'id'])
37  o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
38  *** runcommand id
39  000000000000 tip
40
41  >>> from hgclient import check
42  >>> @check
43  ... def unknowncommand(server):
44  ...     server.stdin.write(b'unknowncommand\n')
45  abort: unknown command unknowncommand
46
47  >>> from hgclient import check, readchannel, runcommand
48  >>> @check
49  ... def checkruncommand(server):
50  ...     # hello block
51  ...     readchannel(server)
52  ...
53  ...     # no args
54  ...     runcommand(server, [])
55  ...
56  ...     # global options
57  ...     runcommand(server, [b'id', b'--quiet'])
58  ...
59  ...     # make sure global options don't stick through requests
60  ...     runcommand(server, [b'id'])
61  ...
62  ...     # --config
63  ...     runcommand(server, [b'id', b'--config', b'ui.quiet=True'])
64  ...
65  ...     # make sure --config doesn't stick
66  ...     runcommand(server, [b'id'])
67  ...
68  ...     # negative return code should be masked
69  ...     runcommand(server, [b'id', b'-runknown'])
70  *** runcommand
71  Mercurial Distributed SCM
72
73  basic commands:
74
75   add           add the specified files on the next commit
76   annotate      show changeset information by line for each file
77   clone         make a copy of an existing repository
78   commit        commit the specified files or all outstanding changes
79   diff          diff repository (or selected files)
80   export        dump the header and diffs for one or more changesets
81   forget        forget the specified files on the next commit
82   init          create a new repository in the given directory
83   log           show revision history of entire repository or files
84   merge         merge another revision into working directory
85   pull          pull changes from the specified source
86   push          push changes to the specified destination
87   remove        remove the specified files on the next commit
88   serve         start stand-alone webserver
89   status        show changed files in the working directory
90   summary       summarize working directory state
91   update        update working directory (or switch revisions)
92
93  (use 'hg help' for the full list of commands or 'hg -v' for details)
94  *** runcommand id --quiet
95  000000000000
96  *** runcommand id
97  000000000000 tip
98  *** runcommand id --config ui.quiet=True
99  000000000000
100  *** runcommand id
101  000000000000 tip
102  *** runcommand id -runknown
103  abort: unknown revision 'unknown'
104   [10]
105
106  >>> from hgclient import bprint, check, readchannel
107  >>> @check
108  ... def inputeof(server):
109  ...     readchannel(server)
110  ...     server.stdin.write(b'runcommand\n')
111  ...     # close stdin while server is waiting for input
112  ...     server.stdin.close()
113  ...
114  ...     # server exits with 1 if the pipe closed while reading the command
115  ...     bprint(b'server exit code =', b'%d' % server.wait())
116  server exit code = 1
117
118  >>> from hgclient import check, readchannel, runcommand, stringio
119  >>> @check
120  ... def serverinput(server):
121  ...     readchannel(server)
122  ...
123  ...     patch = b"""
124  ... # HG changeset patch
125  ... # User test
126  ... # Date 0 0
127  ... # Node ID c103a3dec114d882c98382d684d8af798d09d857
128  ... # Parent  0000000000000000000000000000000000000000
129  ... 1
130  ...
131  ... diff -r 000000000000 -r c103a3dec114 a
132  ... --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
133  ... +++ b/a	Thu Jan 01 00:00:00 1970 +0000
134  ... @@ -0,0 +1,1 @@
135  ... +1
136  ... """
137  ...
138  ...     runcommand(server, [b'import', b'-'], input=stringio(patch))
139  ...     runcommand(server, [b'log'])
140  *** runcommand import -
141  applying patch from stdin
142  *** runcommand log
143  changeset:   0:eff892de26ec
144  tag:         tip
145  user:        test
146  date:        Thu Jan 01 00:00:00 1970 +0000
147  summary:     1
148
149
150check strict parsing of early options:
151
152  >>> import os
153  >>> from hgclient import check, readchannel, runcommand
154  >>> os.environ['HGPLAIN'] = '+strictflags'
155  >>> @check
156  ... def cwd(server):
157  ...     readchannel(server)
158  ...     runcommand(server, [b'log', b'-b', b'--config=alias.log=!echo pwned',
159  ...                         b'default'])
160  *** runcommand log -b --config=alias.log=!echo pwned default
161  abort: unknown revision '--config=alias.log=!echo pwned'
162   [255]
163
164check that "histedit --commands=-" can read rules from the input channel:
165
166  >>> from hgclient import check, readchannel, runcommand, stringio
167  >>> @check
168  ... def serverinput(server):
169  ...     readchannel(server)
170  ...     rules = b'pick eff892de26ec\n'
171  ...     runcommand(server, [b'histedit', b'0', b'--commands=-',
172  ...                         b'--config', b'extensions.histedit='],
173  ...                input=stringio(rules))
174  *** runcommand histedit 0 --commands=- --config extensions.histedit=
175
176check that --cwd doesn't persist between requests:
177
178  $ mkdir foo
179  $ touch foo/bar
180  >>> from hgclient import check, readchannel, runcommand
181  >>> @check
182  ... def cwd(server):
183  ...     readchannel(server)
184  ...     runcommand(server, [b'--cwd', b'foo', b'st', b'bar'])
185  ...     runcommand(server, [b'st', b'foo/bar'])
186  *** runcommand --cwd foo st bar
187  ? bar
188  *** runcommand st foo/bar
189  ? foo/bar
190
191  $ rm foo/bar
192
193
194check that local configs for the cached repo aren't inherited when -R is used:
195
196  $ cat <<EOF >> .hg/hgrc
197  > [ui]
198  > foo = bar
199  > EOF
200
201#if no-extraextensions
202
203  >>> from hgclient import check, readchannel, runcommand, sep
204  >>> @check
205  ... def localhgrc(server):
206  ...     readchannel(server)
207  ...
208  ...     # the cached repo local hgrc contains ui.foo=bar, so showconfig should
209  ...     # show it
210  ...     runcommand(server, [b'showconfig'], outfilter=sep)
211  ...
212  ...     # but not for this repo
213  ...     runcommand(server, [b'init', b'foo'])
214  ...     runcommand(server, [b'-R', b'foo', b'showconfig', b'ui', b'defaults'])
215  *** runcommand showconfig
216  bundle.mainreporoot=$TESTTMP/repo
217  chgserver.idletimeout=60
218  devel.all-warnings=true
219  devel.default-date=0 0
220  extensions.fsmonitor= (fsmonitor !)
221  format.exp-rc-dirstate-v2=1 (dirstate-v2 !)
222  largefiles.usercache=$TESTTMP/.cache/largefiles
223  lfs.usercache=$TESTTMP/.cache/lfs
224  ui.slash=True
225  ui.interactive=False
226  ui.detailed-exit-code=True
227  ui.merge=internal:merge
228  ui.mergemarkers=detailed
229  ui.ssh=* (glob)
230  ui.timeout.warn=15
231  ui.foo=bar
232  ui.nontty=true
233  web.address=localhost
234  web\.ipv6=(?:True|False) (re)
235  web.server-header=testing stub value
236  *** runcommand init foo
237  *** runcommand -R foo showconfig ui defaults
238  ui.slash=True
239  ui.interactive=False
240  ui.detailed-exit-code=True
241  ui.merge=internal:merge
242  ui.mergemarkers=detailed
243  ui.ssh=* (glob)
244  ui.timeout.warn=15
245  ui.nontty=true
246#endif
247
248  $ rm -R foo
249
250#if windows
251  $ PYTHONPATH="$TESTTMP/repo;$PYTHONPATH"
252#else
253  $ PYTHONPATH="$TESTTMP/repo:$PYTHONPATH"
254#endif
255
256  $ cat <<EOF > hook.py
257  > import sys
258  > from hgclient import bprint
259  > def hook(**args):
260  >     bprint(b'hook talking')
261  >     bprint(b'now try to read something: %r' % sys.stdin.read())
262  > EOF
263
264  >>> from hgclient import check, readchannel, runcommand, stringio
265  >>> @check
266  ... def hookoutput(server):
267  ...     readchannel(server)
268  ...     runcommand(server, [b'--config',
269  ...                         b'hooks.pre-identify=python:hook.hook',
270  ...                         b'id'],
271  ...                input=stringio(b'some input'))
272  *** runcommand --config hooks.pre-identify=python:hook.hook id
273  eff892de26ec tip
274  hook talking
275  now try to read something: ''
276
277Clean hook cached version
278  $ rm hook.py*
279  $ rm -Rf __pycache__
280
281  $ echo a >> a
282  >>> import os
283  >>> from hgclient import check, readchannel, runcommand
284  >>> @check
285  ... def outsidechanges(server):
286  ...     readchannel(server)
287  ...     runcommand(server, [b'status'])
288  ...     os.system('hg ci -Am2')
289  ...     runcommand(server, [b'tip'])
290  ...     runcommand(server, [b'status'])
291  *** runcommand status
292  M a
293  *** runcommand tip
294  changeset:   1:d3a0a68be6de
295  tag:         tip
296  user:        test
297  date:        Thu Jan 01 00:00:00 1970 +0000
298  summary:     2
299
300  *** runcommand status
301
302  >>> import os
303  >>> from hgclient import bprint, check, readchannel, runcommand
304  >>> @check
305  ... def bookmarks(server):
306  ...     readchannel(server)
307  ...     runcommand(server, [b'bookmarks'])
308  ...
309  ...     # changes .hg/bookmarks
310  ...     os.system('hg bookmark -i bm1')
311  ...     os.system('hg bookmark -i bm2')
312  ...     runcommand(server, [b'bookmarks'])
313  ...
314  ...     # changes .hg/bookmarks.current
315  ...     os.system('hg upd bm1 -q')
316  ...     runcommand(server, [b'bookmarks'])
317  ...
318  ...     runcommand(server, [b'bookmarks', b'bm3'])
319  ...     f = open('a', 'ab')
320  ...     f.write(b'a\n') and None
321  ...     f.close()
322  ...     runcommand(server, [b'commit', b'-Amm'])
323  ...     runcommand(server, [b'bookmarks'])
324  ...     bprint(b'')
325  *** runcommand bookmarks
326  no bookmarks set
327  *** runcommand bookmarks
328     bm1                       1:d3a0a68be6de
329     bm2                       1:d3a0a68be6de
330  *** runcommand bookmarks
331   * bm1                       1:d3a0a68be6de
332     bm2                       1:d3a0a68be6de
333  *** runcommand bookmarks bm3
334  *** runcommand commit -Amm
335  *** runcommand bookmarks
336     bm1                       1:d3a0a68be6de
337     bm2                       1:d3a0a68be6de
338   * bm3                       2:aef17e88f5f0
339
340
341  >>> import os
342  >>> from hgclient import check, readchannel, runcommand
343  >>> @check
344  ... def tagscache(server):
345  ...     readchannel(server)
346  ...     runcommand(server, [b'id', b'-t', b'-r', b'0'])
347  ...     os.system('hg tag -r 0 foo')
348  ...     runcommand(server, [b'id', b'-t', b'-r', b'0'])
349  *** runcommand id -t -r 0
350
351  *** runcommand id -t -r 0
352  foo
353
354  >>> import os
355  >>> from hgclient import check, readchannel, runcommand
356  >>> @check
357  ... def setphase(server):
358  ...     readchannel(server)
359  ...     runcommand(server, [b'phase', b'-r', b'.'])
360  ...     os.system('hg phase -r . -p')
361  ...     runcommand(server, [b'phase', b'-r', b'.'])
362  *** runcommand phase -r .
363  3: draft
364  *** runcommand phase -r .
365  3: public
366
367  $ echo a >> a
368  >>> from hgclient import bprint, check, readchannel, runcommand
369  >>> @check
370  ... def rollback(server):
371  ...     readchannel(server)
372  ...     runcommand(server, [b'phase', b'-r', b'.', b'-p'])
373  ...     runcommand(server, [b'commit', b'-Am.'])
374  ...     runcommand(server, [b'rollback'])
375  ...     runcommand(server, [b'phase', b'-r', b'.'])
376  ...     bprint(b'')
377  *** runcommand phase -r . -p
378  no phases changed
379  *** runcommand commit -Am.
380  *** runcommand rollback
381  repository tip rolled back to revision 3 (undo commit)
382  working directory now based on revision 3
383  *** runcommand phase -r .
384  3: public
385
386
387  >>> import os
388  >>> from hgclient import check, readchannel, runcommand
389  >>> @check
390  ... def branch(server):
391  ...     readchannel(server)
392  ...     runcommand(server, [b'branch'])
393  ...     os.system('hg branch foo')
394  ...     runcommand(server, [b'branch'])
395  ...     os.system('hg branch default')
396  *** runcommand branch
397  default
398  marked working directory as branch foo
399  (branches are permanent and global, did you want a bookmark?)
400  *** runcommand branch
401  foo
402  marked working directory as branch default
403  (branches are permanent and global, did you want a bookmark?)
404
405  $ touch .hgignore
406  >>> import os
407  >>> from hgclient import bprint, check, readchannel, runcommand
408  >>> @check
409  ... def hgignore(server):
410  ...     readchannel(server)
411  ...     runcommand(server, [b'commit', b'-Am.'])
412  ...     f = open('ignored-file', 'ab')
413  ...     f.write(b'') and None
414  ...     f.close()
415  ...     f = open('.hgignore', 'ab')
416  ...     f.write(b'ignored-file')
417  ...     f.close()
418  ...     runcommand(server, [b'status', b'-i', b'-u'])
419  ...     bprint(b'')
420  *** runcommand commit -Am.
421  adding .hgignore
422  *** runcommand status -i -u
423  I ignored-file
424
425
426cache of non-public revisions should be invalidated on repository change
427(issue4855):
428
429  >>> import os
430  >>> from hgclient import bprint, check, readchannel, runcommand
431  >>> @check
432  ... def phasesetscacheaftercommit(server):
433  ...     readchannel(server)
434  ...     # load _phasecache._phaserevs and _phasesets
435  ...     runcommand(server, [b'log', b'-qr', b'draft()'])
436  ...     # create draft commits by another process
437  ...     for i in range(5, 7):
438  ...         f = open('a', 'ab')
439  ...         f.seek(0, os.SEEK_END)
440  ...         f.write(b'a\n') and None
441  ...         f.close()
442  ...         os.system('hg commit -Aqm%d' % i)
443  ...     # new commits should be listed as draft revisions
444  ...     runcommand(server, [b'log', b'-qr', b'draft()'])
445  ...     bprint(b'')
446  *** runcommand log -qr draft()
447  4:7966c8e3734d
448  *** runcommand log -qr draft()
449  4:7966c8e3734d
450  5:41f6602d1c4f
451  6:10501e202c35
452
453
454  >>> import os
455  >>> from hgclient import bprint, check, readchannel, runcommand
456  >>> @check
457  ... def phasesetscacheafterstrip(server):
458  ...     readchannel(server)
459  ...     # load _phasecache._phaserevs and _phasesets
460  ...     runcommand(server, [b'log', b'-qr', b'draft()'])
461  ...     # strip cached revisions by another process
462  ...     os.system('hg --config extensions.strip= strip -q 5')
463  ...     # shouldn't abort by "unknown revision '6'"
464  ...     runcommand(server, [b'log', b'-qr', b'draft()'])
465  ...     bprint(b'')
466  *** runcommand log -qr draft()
467  4:7966c8e3734d
468  5:41f6602d1c4f
469  6:10501e202c35
470  *** runcommand log -qr draft()
471  4:7966c8e3734d
472
473
474cache of phase roots should be invalidated on strip (issue3827):
475
476  >>> import os
477  >>> from hgclient import check, readchannel, runcommand, sep
478  >>> @check
479  ... def phasecacheafterstrip(server):
480  ...     readchannel(server)
481  ...
482  ...     # create new head, 5:731265503d86
483  ...     runcommand(server, [b'update', b'-C', b'0'])
484  ...     f = open('a', 'ab')
485  ...     f.write(b'a\n') and None
486  ...     f.close()
487  ...     runcommand(server, [b'commit', b'-Am.', b'a'])
488  ...     runcommand(server, [b'log', b'-Gq'])
489  ...
490  ...     # make it public; draft marker moves to 4:7966c8e3734d
491  ...     runcommand(server, [b'phase', b'-p', b'.'])
492  ...     # load _phasecache.phaseroots
493  ...     runcommand(server, [b'phase', b'.'], outfilter=sep)
494  ...
495  ...     # strip 1::4 outside server
496  ...     os.system('hg -q --config extensions.mq= strip 1')
497  ...
498  ...     # shouldn't raise "7966c8e3734d: no node!"
499  ...     runcommand(server, [b'branches'])
500  *** runcommand update -C 0
501  1 files updated, 0 files merged, 2 files removed, 0 files unresolved
502  (leaving bookmark bm3)
503  *** runcommand commit -Am. a
504  created new head
505  *** runcommand log -Gq
506  @  5:731265503d86
507  |
508  | o  4:7966c8e3734d
509  | |
510  | o  3:b9b85890c400
511  | |
512  | o  2:aef17e88f5f0
513  | |
514  | o  1:d3a0a68be6de
515  |/
516  o  0:eff892de26ec
517
518  *** runcommand phase -p .
519  *** runcommand phase .
520  5: public
521  *** runcommand branches
522  default                        1:731265503d86
523
524in-memory cache must be reloaded if transaction is aborted. otherwise
525changelog and manifest would have invalid node:
526
527  $ echo a >> a
528  >>> from hgclient import check, readchannel, runcommand
529  >>> @check
530  ... def txabort(server):
531  ...     readchannel(server)
532  ...     runcommand(server, [b'commit', b'--config', b'hooks.pretxncommit=false',
533  ...                         b'-mfoo'])
534  ...     runcommand(server, [b'verify'])
535  *** runcommand commit --config hooks.pretxncommit=false -mfoo
536  transaction abort!
537  rollback completed
538  abort: pretxncommit hook exited with status 1
539   [40]
540  *** runcommand verify
541  checking changesets
542  checking manifests
543  crosschecking files in changesets and manifests
544  checking files
545  checked 2 changesets with 2 changes to 1 files
546  $ hg revert --no-backup -aq
547
548  $ cat >> .hg/hgrc << EOF
549  > [experimental]
550  > evolution.createmarkers=True
551  > EOF
552
553  >>> import os
554  >>> from hgclient import check, readchannel, runcommand
555  >>> @check
556  ... def obsolete(server):
557  ...     readchannel(server)
558  ...
559  ...     runcommand(server, [b'up', b'null'])
560  ...     runcommand(server, [b'phase', b'-df', b'tip'])
561  ...     cmd = 'hg debugobsolete `hg log -r tip --template {node}`'
562  ...     if os.name == 'nt':
563  ...         cmd = 'sh -c "%s"' % cmd # run in sh, not cmd.exe
564  ...     os.system(cmd)
565  ...     runcommand(server, [b'log', b'--hidden'])
566  ...     runcommand(server, [b'log'])
567  *** runcommand up null
568  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
569  *** runcommand phase -df tip
570  1 new obsolescence markers
571  obsoleted 1 changesets
572  *** runcommand log --hidden
573  changeset:   1:731265503d86
574  tag:         tip
575  user:        test
576  date:        Thu Jan 01 00:00:00 1970 +0000
577  obsolete:    pruned
578  summary:     .
579
580  changeset:   0:eff892de26ec
581  bookmark:    bm1
582  bookmark:    bm2
583  bookmark:    bm3
584  user:        test
585  date:        Thu Jan 01 00:00:00 1970 +0000
586  summary:     1
587
588  *** runcommand log
589  changeset:   0:eff892de26ec
590  bookmark:    bm1
591  bookmark:    bm2
592  bookmark:    bm3
593  tag:         tip
594  user:        test
595  date:        Thu Jan 01 00:00:00 1970 +0000
596  summary:     1
597
598
599  $ cat <<EOF >> .hg/hgrc
600  > [extensions]
601  > mq =
602  > EOF
603
604  >>> import os
605  >>> from hgclient import check, readchannel, runcommand
606  >>> @check
607  ... def mqoutsidechanges(server):
608  ...     readchannel(server)
609  ...
610  ...     # load repo.mq
611  ...     runcommand(server, [b'qapplied'])
612  ...     os.system('hg qnew 0.diff')
613  ...     # repo.mq should be invalidated
614  ...     runcommand(server, [b'qapplied'])
615  ...
616  ...     runcommand(server, [b'qpop', b'--all'])
617  ...     os.system('hg qqueue --create foo')
618  ...     # repo.mq should be recreated to point to new queue
619  ...     runcommand(server, [b'qqueue', b'--active'])
620  *** runcommand qapplied
621  *** runcommand qapplied
622  0.diff
623  *** runcommand qpop --all
624  popping 0.diff
625  patch queue now empty
626  *** runcommand qqueue --active
627  foo
628
629  $ cat <<'EOF' > ../dbgui.py
630  > import os
631  > import sys
632  > from mercurial import commands, registrar
633  > cmdtable = {}
634  > command = registrar.command(cmdtable)
635  > @command(b"debuggetpass", norepo=True)
636  > def debuggetpass(ui):
637  >     ui.write(b"%s\n" % ui.getpass())
638  > @command(b"debugprompt", norepo=True)
639  > def debugprompt(ui):
640  >     ui.write(b"%s\n" % ui.prompt(b"prompt:"))
641  > @command(b"debugpromptchoice", norepo=True)
642  > def debugpromptchoice(ui):
643  >     msg = b"promptchoice (y/n)? $$ &Yes $$ &No"
644  >     ui.write(b"%d\n" % ui.promptchoice(msg))
645  > @command(b"debugreadstdin", norepo=True)
646  > def debugreadstdin(ui):
647  >     ui.write(b"read: %r\n" % sys.stdin.read(1))
648  > @command(b"debugwritestdout", norepo=True)
649  > def debugwritestdout(ui):
650  >     os.write(1, b"low-level stdout fd and\n")
651  >     sys.stdout.write("stdout should be redirected to stderr\n")
652  >     sys.stdout.flush()
653  > EOF
654  $ cat <<EOF >> .hg/hgrc
655  > [extensions]
656  > dbgui = ../dbgui.py
657  > EOF
658
659  >>> from hgclient import check, readchannel, runcommand, stringio
660  >>> @check
661  ... def getpass(server):
662  ...     readchannel(server)
663  ...     runcommand(server, [b'debuggetpass', b'--config',
664  ...                         b'ui.interactive=True'],
665  ...                input=stringio(b'1234\n'))
666  ...     runcommand(server, [b'debuggetpass', b'--config',
667  ...                         b'ui.interactive=True'],
668  ...                input=stringio(b'\n'))
669  ...     runcommand(server, [b'debuggetpass', b'--config',
670  ...                         b'ui.interactive=True'],
671  ...                input=stringio(b''))
672  ...     runcommand(server, [b'debugprompt', b'--config',
673  ...                         b'ui.interactive=True'],
674  ...                input=stringio(b'5678\n'))
675  ...     runcommand(server, [b'debugprompt', b'--config',
676  ...                         b'ui.interactive=True'],
677  ...                input=stringio(b'\nremainder\nshould\nnot\nbe\nread\n'))
678  ...     runcommand(server, [b'debugreadstdin'])
679  ...     runcommand(server, [b'debugwritestdout'])
680  *** runcommand debuggetpass --config ui.interactive=True
681  password: 1234
682  *** runcommand debuggetpass --config ui.interactive=True
683  password:
684  *** runcommand debuggetpass --config ui.interactive=True
685  password: abort: response expected
686   [255]
687  *** runcommand debugprompt --config ui.interactive=True
688  prompt: 5678
689  *** runcommand debugprompt --config ui.interactive=True
690  prompt: y
691  *** runcommand debugreadstdin
692  read: ''
693  *** runcommand debugwritestdout
694  low-level stdout fd and
695  stdout should be redirected to stderr
696
697
698run commandserver in commandserver, which is silly but should work:
699
700  >>> from hgclient import bprint, check, readchannel, runcommand, stringio
701  >>> @check
702  ... def nested(server):
703  ...     bprint(b'%c, %r' % readchannel(server))
704  ...     class nestedserver(object):
705  ...         stdin = stringio(b'getencoding\n')
706  ...         stdout = stringio()
707  ...     runcommand(server, [b'serve', b'--cmdserver', b'pipe'],
708  ...                output=nestedserver.stdout, input=nestedserver.stdin)
709  ...     nestedserver.stdout.seek(0)
710  ...     bprint(b'%c, %r' % readchannel(nestedserver))  # hello
711  ...     bprint(b'%c, %r' % readchannel(nestedserver))  # getencoding
712  o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
713  *** runcommand serve --cmdserver pipe
714  o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
715  r, '*' (glob)
716
717
718start without repository:
719
720  $ cd ..
721
722  >>> from hgclient import bprint, check, readchannel, runcommand
723  >>> @check
724  ... def hellomessage(server):
725  ...     ch, data = readchannel(server)
726  ...     bprint(b'%c, %r' % (ch, data))
727  ...     # run an arbitrary command to make sure the next thing the server
728  ...     # sends isn't part of the hello message
729  ...     runcommand(server, [b'id'])
730  o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
731  *** runcommand id
732  abort: there is no Mercurial repository here (.hg not found)
733   [10]
734
735  >>> from hgclient import check, readchannel, runcommand
736  >>> @check
737  ... def startwithoutrepo(server):
738  ...     readchannel(server)
739  ...     runcommand(server, [b'init', b'repo2'])
740  ...     runcommand(server, [b'id', b'-R', b'repo2'])
741  *** runcommand init repo2
742  *** runcommand id -R repo2
743  000000000000 tip
744
745
746don't fall back to cwd if invalid -R path is specified (issue4805):
747
748  $ cd repo
749  $ hg serve --cmdserver pipe -R ../nonexistent
750  abort: repository ../nonexistent not found
751  [255]
752  $ cd ..
753
754
755#if no-windows
756
757option to not shutdown on SIGINT:
758
759  $ cat <<'EOF' > dbgint.py
760  > import os
761  > import signal
762  > import time
763  > from mercurial import commands, registrar
764  > cmdtable = {}
765  > command = registrar.command(cmdtable)
766  > @command(b"debugsleep", norepo=True)
767  > def debugsleep(ui):
768  >     time.sleep(1)
769  > @command(b"debugsuicide", norepo=True)
770  > def debugsuicide(ui):
771  >     os.kill(os.getpid(), signal.SIGINT)
772  >     time.sleep(1)
773  > EOF
774
775  >>> import signal
776  >>> import time
777  >>> from hgclient import checkwith, readchannel, runcommand
778  >>> @checkwith(extraargs=[b'--config', b'cmdserver.shutdown-on-interrupt=False',
779  ...                       b'--config', b'extensions.dbgint=dbgint.py'])
780  ... def nointr(server):
781  ...     readchannel(server)
782  ...     server.send_signal(signal.SIGINT)  # server won't be terminated
783  ...     time.sleep(1)
784  ...     runcommand(server, [b'debugsleep'])
785  ...     server.send_signal(signal.SIGINT)  # server won't be terminated
786  ...     runcommand(server, [b'debugsleep'])
787  ...     runcommand(server, [b'debugsuicide'])  # command can be interrupted
788  ...     server.send_signal(signal.SIGTERM)  # server will be terminated
789  ...     time.sleep(1)
790  *** runcommand debugsleep
791  *** runcommand debugsleep
792  *** runcommand debugsuicide
793  interrupted!
794  killed!
795   [255]
796
797#endif
798
799
800structured message channel:
801
802  $ cat <<'EOF' >> repo2/.hg/hgrc
803  > [ui]
804  > # server --config should precede repository option
805  > message-output = stdio
806  > EOF
807
808  >>> from hgclient import bprint, checkwith, readchannel, runcommand
809  >>> @checkwith(extraargs=[b'--config', b'ui.message-output=channel',
810  ...                       b'--config', b'cmdserver.message-encodings=foo cbor'])
811  ... def verify(server):
812  ...     _ch, data = readchannel(server)
813  ...     bprint(data)
814  ...     runcommand(server, [b'-R', b'repo2', b'verify'])
815  capabilities: getencoding runcommand
816  encoding: ascii
817  message-encoding: cbor
818  pid: * (glob)
819  pgid: * (glob) (no-windows !)
820  *** runcommand -R repo2 verify
821  message: '\xa2DdataTchecking changesets\nDtypeFstatus'
822  message: '\xa6Ditem@Cpos\xf6EtopicHcheckingEtotal\xf6DtypeHprogressDunit@'
823  message: '\xa2DdataSchecking manifests\nDtypeFstatus'
824  message: '\xa6Ditem@Cpos\xf6EtopicHcheckingEtotal\xf6DtypeHprogressDunit@'
825  message: '\xa2DdataX0crosschecking files in changesets and manifests\nDtypeFstatus'
826  message: '\xa6Ditem@Cpos\xf6EtopicMcrosscheckingEtotal\xf6DtypeHprogressDunit@'
827  message: '\xa2DdataOchecking files\nDtypeFstatus'
828  message: '\xa6Ditem@Cpos\xf6EtopicHcheckingEtotal\xf6DtypeHprogressDunit@'
829  message: '\xa2DdataX/checked 0 changesets with 0 changes to 0 files\nDtypeFstatus'
830
831  >>> from hgclient import checkwith, readchannel, runcommand, stringio
832  >>> @checkwith(extraargs=[b'--config', b'ui.message-output=channel',
833  ...                       b'--config', b'cmdserver.message-encodings=cbor',
834  ...                       b'--config', b'extensions.dbgui=dbgui.py'])
835  ... def prompt(server):
836  ...     readchannel(server)
837  ...     interactive = [b'--config', b'ui.interactive=True']
838  ...     runcommand(server, [b'debuggetpass'] + interactive,
839  ...                input=stringio(b'1234\n'))
840  ...     runcommand(server, [b'debugprompt'] + interactive,
841  ...                input=stringio(b'5678\n'))
842  ...     runcommand(server, [b'debugpromptchoice'] + interactive,
843  ...                input=stringio(b'n\n'))
844  *** runcommand debuggetpass --config ui.interactive=True
845  message: '\xa3DdataJpassword: Hpassword\xf5DtypeFprompt'
846  1234
847  *** runcommand debugprompt --config ui.interactive=True
848  message: '\xa3DdataGprompt:GdefaultAyDtypeFprompt'
849   5678
850  *** runcommand debugpromptchoice --config ui.interactive=True
851  message: '\xa4Gchoices\x82\x82AyCYes\x82AnBNoDdataTpromptchoice (y/n)? GdefaultAyDtypeFprompt'
852   1
853
854bad message encoding:
855
856  $ hg serve --cmdserver pipe --config ui.message-output=channel
857  abort: no supported message encodings:
858  [255]
859  $ hg serve --cmdserver pipe --config ui.message-output=channel \
860  > --config cmdserver.message-encodings='foo bar'
861  abort: no supported message encodings: foo bar
862  [255]
863
864unix domain socket:
865
866  $ cd repo
867  $ hg update -q
868
869#if unix-socket unix-permissions
870
871  >>> from hgclient import bprint, check, readchannel, runcommand, stringio, unixserver
872  >>> server = unixserver(b'.hg/server.sock', b'.hg/server.log')
873  >>> def hellomessage(conn):
874  ...     ch, data = readchannel(conn)
875  ...     bprint(b'%c, %r' % (ch, data))
876  ...     runcommand(conn, [b'id'])
877  >>> check(hellomessage, server.connect)
878  o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
879  *** runcommand id
880  eff892de26ec tip bm1/bm2/bm3
881  >>> def unknowncommand(conn):
882  ...     readchannel(conn)
883  ...     conn.stdin.write(b'unknowncommand\n')
884  >>> check(unknowncommand, server.connect)  # error sent to server.log
885  >>> def serverinput(conn):
886  ...     readchannel(conn)
887  ...     patch = b"""
888  ... # HG changeset patch
889  ... # User test
890  ... # Date 0 0
891  ... 2
892  ...
893  ... diff -r eff892de26ec -r 1ed24be7e7a0 a
894  ... --- a/a
895  ... +++ b/a
896  ... @@ -1,1 +1,2 @@
897  ...  1
898  ... +2
899  ... """
900  ...     runcommand(conn, [b'import', b'-'], input=stringio(patch))
901  ...     runcommand(conn, [b'log', b'-rtip', b'-q'])
902  >>> check(serverinput, server.connect)
903  *** runcommand import -
904  applying patch from stdin
905  *** runcommand log -rtip -q
906  2:1ed24be7e7a0
907  >>> server.shutdown()
908
909  $ cat .hg/server.log
910  listening at .hg/server.sock
911  abort: unknown command unknowncommand
912  killed!
913  $ rm .hg/server.log
914
915 if server crashed before hello, traceback will be sent to 'e' channel as
916 last ditch:
917
918  $ cat <<'EOF' > ../earlycrasher.py
919  > from mercurial import commandserver, extensions
920  > def _serverequest(orig, ui, repo, conn, createcmdserver, prereposetups):
921  >     def createcmdserver(*args, **kwargs):
922  >         raise Exception('crash')
923  >     return orig(ui, repo, conn, createcmdserver, prereposetups)
924  > def extsetup(ui):
925  >     extensions.wrapfunction(commandserver, b'_serverequest', _serverequest)
926  > EOF
927  $ cat <<EOF >> .hg/hgrc
928  > [extensions]
929  > earlycrasher = ../earlycrasher.py
930  > EOF
931  >>> from hgclient import bprint, check, readchannel, unixserver
932  >>> server = unixserver(b'.hg/server.sock', b'.hg/server.log')
933  >>> def earlycrash(conn):
934  ...     while True:
935  ...         try:
936  ...             ch, data = readchannel(conn)
937  ...             for l in data.splitlines(True):
938  ...                 if not l.startswith(b'  '):
939  ...                     bprint(b'%c, %r' % (ch, l))
940  ...         except EOFError:
941  ...             break
942  >>> check(earlycrash, server.connect)
943  e, 'Traceback (most recent call last):\n'
944  e, 'Exception: crash\n'
945  >>> server.shutdown()
946
947  $ cat .hg/server.log | grep -v '^  '
948  listening at .hg/server.sock
949  Traceback (most recent call last):
950  Exception: crash
951  killed!
952#endif
953#if no-unix-socket
954
955  $ hg serve --cmdserver unix -a .hg/server.sock
956  abort: unsupported platform
957  [255]
958
959#endif
960
961  $ cd ..
962
963Test that accessing to invalid changelog cache is avoided at
964subsequent operations even if repo object is reused even after failure
965of transaction (see 0a7610758c42 also)
966
967"hg log" after failure of transaction is needed to detect invalid
968cache in repoview: this can't detect by "hg verify" only.
969
970Combination of "finalization" and "empty-ness of changelog" (2 x 2 =
9714) are tested, because '00changelog.i' are differently changed in each
972cases.
973
974  $ cat > $TESTTMP/failafterfinalize.py <<EOF
975  > # extension to abort transaction after finalization forcibly
976  > from mercurial import commands, error, extensions, lock as lockmod
977  > from mercurial import registrar
978  > cmdtable = {}
979  > command = registrar.command(cmdtable)
980  > configtable = {}
981  > configitem = registrar.configitem(configtable)
982  > configitem(b'failafterfinalize', b'fail',
983  >     default=None,
984  > )
985  > def fail(tr):
986  >     raise error.Abort(b'fail after finalization')
987  > def reposetup(ui, repo):
988  >     class failrepo(repo.__class__):
989  >         def commitctx(self, ctx, error=False, origctx=None):
990  >             if self.ui.configbool(b'failafterfinalize', b'fail'):
991  >                 # 'sorted()' by ASCII code on category names causes
992  >                 # invoking 'fail' after finalization of changelog
993  >                 # using "'cl-%i' % id(self)" as category name
994  >                 self.currenttransaction().addfinalize(b'zzzzzzzz', fail)
995  >             return super(failrepo, self).commitctx(ctx, error, origctx)
996  >     repo.__class__ = failrepo
997  > EOF
998
999  $ hg init repo3
1000  $ cd repo3
1001
1002  $ cat <<EOF >> $HGRCPATH
1003  > [command-templates]
1004  > log = {rev} {desc|firstline} ({files})\n
1005  >
1006  > [extensions]
1007  > failafterfinalize = $TESTTMP/failafterfinalize.py
1008  > EOF
1009
1010- test failure with "empty changelog"
1011
1012  $ echo foo > foo
1013  $ hg add foo
1014
1015(failure before finalization)
1016
1017  >>> from hgclient import check, readchannel, runcommand
1018  >>> @check
1019  ... def abort(server):
1020  ...     readchannel(server)
1021  ...     runcommand(server, [b'commit',
1022  ...                         b'--config', b'hooks.pretxncommit=false',
1023  ...                         b'-mfoo'])
1024  ...     runcommand(server, [b'log'])
1025  ...     runcommand(server, [b'verify', b'-q'])
1026  *** runcommand commit --config hooks.pretxncommit=false -mfoo
1027  transaction abort!
1028  rollback completed
1029  abort: pretxncommit hook exited with status 1
1030   [40]
1031  *** runcommand log
1032  *** runcommand verify -q
1033
1034(failure after finalization)
1035
1036  >>> from hgclient import check, readchannel, runcommand
1037  >>> @check
1038  ... def abort(server):
1039  ...     readchannel(server)
1040  ...     runcommand(server, [b'commit',
1041  ...                         b'--config', b'failafterfinalize.fail=true',
1042  ...                         b'-mfoo'])
1043  ...     runcommand(server, [b'log'])
1044  ...     runcommand(server, [b'verify', b'-q'])
1045  *** runcommand commit --config failafterfinalize.fail=true -mfoo
1046  transaction abort!
1047  rollback completed
1048  abort: fail after finalization
1049   [255]
1050  *** runcommand log
1051  *** runcommand verify -q
1052
1053- test failure with "not-empty changelog"
1054
1055  $ echo bar > bar
1056  $ hg add bar
1057  $ hg commit -mbar bar
1058
1059(failure before finalization)
1060
1061  >>> from hgclient import check, readchannel, runcommand
1062  >>> @check
1063  ... def abort(server):
1064  ...     readchannel(server)
1065  ...     runcommand(server, [b'commit',
1066  ...                         b'--config', b'hooks.pretxncommit=false',
1067  ...                         b'-mfoo', b'foo'])
1068  ...     runcommand(server, [b'log'])
1069  ...     runcommand(server, [b'verify', b'-q'])
1070  *** runcommand commit --config hooks.pretxncommit=false -mfoo foo
1071  transaction abort!
1072  rollback completed
1073  abort: pretxncommit hook exited with status 1
1074   [40]
1075  *** runcommand log
1076  0 bar (bar)
1077  *** runcommand verify -q
1078
1079(failure after finalization)
1080
1081  >>> from hgclient import check, readchannel, runcommand
1082  >>> @check
1083  ... def abort(server):
1084  ...     readchannel(server)
1085  ...     runcommand(server, [b'commit',
1086  ...                         b'--config', b'failafterfinalize.fail=true',
1087  ...                         b'-mfoo', b'foo'])
1088  ...     runcommand(server, [b'log'])
1089  ...     runcommand(server, [b'verify', b'-q'])
1090  *** runcommand commit --config failafterfinalize.fail=true -mfoo foo
1091  transaction abort!
1092  rollback completed
1093  abort: fail after finalization
1094   [255]
1095  *** runcommand log
1096  0 bar (bar)
1097  *** runcommand verify -q
1098
1099  $ cd ..
1100
1101Test symlink traversal over cached audited paths:
1102-------------------------------------------------
1103
1104#if symlink
1105
1106set up symlink hell
1107
1108  $ mkdir merge-symlink-out
1109  $ hg init merge-symlink
1110  $ cd merge-symlink
1111  $ touch base
1112  $ hg commit -qAm base
1113  $ ln -s ../merge-symlink-out a
1114  $ hg commit -qAm 'symlink a -> ../merge-symlink-out'
1115  $ hg up -q 0
1116  $ mkdir a
1117  $ touch a/poisoned
1118  $ hg commit -qAm 'file a/poisoned'
1119  $ hg log -G -T '{rev}: {desc}\n'
1120  @  2: file a/poisoned
1121  |
1122  | o  1: symlink a -> ../merge-symlink-out
1123  |/
1124  o  0: base
1125
1126
1127try trivial merge after update: cache of audited paths should be discarded,
1128and the merge should fail (issue5628)
1129
1130  $ hg up -q null
1131  >>> from hgclient import check, readchannel, runcommand
1132  >>> @check
1133  ... def merge(server):
1134  ...     readchannel(server)
1135  ...     # audit a/poisoned as a good path
1136  ...     runcommand(server, [b'up', b'-qC', b'2'])
1137  ...     runcommand(server, [b'up', b'-qC', b'1'])
1138  ...     # here a is a symlink, so a/poisoned is bad
1139  ...     runcommand(server, [b'merge', b'2'])
1140  *** runcommand up -qC 2
1141  *** runcommand up -qC 1
1142  *** runcommand merge 2
1143  abort: path 'a/poisoned' traverses symbolic link 'a'
1144   [255]
1145  $ ls ../merge-symlink-out
1146
1147cache of repo.auditor should be discarded, so matcher would never traverse
1148symlinks:
1149
1150  $ hg up -qC 0
1151  $ touch ../merge-symlink-out/poisoned
1152  >>> from hgclient import check, readchannel, runcommand
1153  >>> @check
1154  ... def files(server):
1155  ...     readchannel(server)
1156  ...     runcommand(server, [b'up', b'-qC', b'2'])
1157  ...     # audit a/poisoned as a good path
1158  ...     runcommand(server, [b'files', b'a/poisoned'])
1159  ...     runcommand(server, [b'up', b'-qC', b'0'])
1160  ...     runcommand(server, [b'up', b'-qC', b'1'])
1161  ...     # here 'a' is a symlink, so a/poisoned should be warned
1162  ...     runcommand(server, [b'files', b'a/poisoned'])
1163  *** runcommand up -qC 2
1164  *** runcommand files a/poisoned
1165  a/poisoned
1166  *** runcommand up -qC 0
1167  *** runcommand up -qC 1
1168  *** runcommand files a/poisoned
1169  abort: path 'a/poisoned' traverses symbolic link 'a'
1170   [255]
1171
1172  $ cd ..
1173
1174#endif
1175