view tests/test-chg.t @ 42618:c17e6a3e7356

phabricator: handle local:commits time being string or int When setting local:commits arcanist has different behaviour depending on whether the repo is git or hg. With hg it sets the time as a number, since it calls PHP's strtotime on the value, but with git it sets it as a string. Normally this wouldn't be an issue since phabread wouldn't be interacting with Phabricator Revisions for git repos, but Mozilla has a secondary workflow for git users that uses the git-cinnabar tool to interact with their hg repos. When a git-cinnabar user uses the moz-phab tool to submit patches for mozilla-central it makes use of Mozilla's fork of arcanist, which works with their local git version of m-c, and thus sets the local:commit time as a string, and then translates the commit hashes. Currently when encountering such DREVS phabread dies with "TypeError: %d format: a number is required, not str". phabsend also used to set it as a string but wouldn't have encountered the issue with its own DREVs since it would read hg:meta first. Differential Revision: https://phab.mercurial-scm.org/D6650
author Ian Moody <moz-ian@perix.co.uk>
date Tue, 16 Jul 2019 19:18:16 +0100
parents dcac24ec935b
children f05d10ef42e3
line wrap: on
line source

#require chg

  $ mkdir log
  $ cp $HGRCPATH $HGRCPATH.unconfigured
  $ cat <<'EOF' >> $HGRCPATH
  > [cmdserver]
  > log = $TESTTMP/log/server.log
  > max-log-files = 1
  > max-log-size = 10 kB
  > EOF
  $ cp $HGRCPATH $HGRCPATH.orig

  $ filterlog () {
  >   sed -e 's!^[0-9/]* [0-9:]* ([0-9]*)>!YYYY/MM/DD HH:MM:SS (PID)>!' \
  >       -e 's!\(setprocname\|received fds\|setenv\): .*!\1: ...!' \
  >       -e 's!\(confighash\|mtimehash\) = [0-9a-f]*!\1 = ...!g' \
  >       -e 's!\(in \)[0-9.]*s\b!\1 ...s!g' \
  >       -e 's!\(pid\)=[0-9]*!\1=...!g' \
  >       -e 's!\(/server-\)[0-9a-f]*!\1...!g'
  > }

init repo

  $ chg init foo
  $ cd foo

ill-formed config

  $ chg status
  $ echo '=brokenconfig' >> $HGRCPATH
  $ chg status
  hg: parse error at * (glob)
  [255]

  $ cp $HGRCPATH.orig $HGRCPATH

long socket path

  $ sockpath=$TESTTMP/this/path/should/be/longer/than/one-hundred-and-seven/characters/where/107/is/the/typical/size/limit/of/unix-domain-socket
  $ mkdir -p $sockpath
  $ bakchgsockname=$CHGSOCKNAME
  $ CHGSOCKNAME=$sockpath/server
  $ export CHGSOCKNAME
  $ chg root
  $TESTTMP/foo
  $ rm -rf $sockpath
  $ CHGSOCKNAME=$bakchgsockname
  $ export CHGSOCKNAME

  $ cd ..

editor
------

  $ cat >> pushbuffer.py <<EOF
  > def reposetup(ui, repo):
  >     repo.ui.pushbuffer(subproc=True)
  > EOF

  $ chg init editor
  $ cd editor

by default, system() should be redirected to the client:

  $ touch foo
  $ CHGDEBUG= HGEDITOR=cat chg ci -Am channeled --edit 2>&1 \
  > | egrep "HG:|run 'cat"
  chg: debug: * run 'cat "*"' at '$TESTTMP/editor' (glob)
  HG: Enter commit message.  Lines beginning with 'HG:' are removed.
  HG: Leave message empty to abort commit.
  HG: --
  HG: user: test
  HG: branch 'default'
  HG: added foo

but no redirection should be made if output is captured:

  $ touch bar
  $ CHGDEBUG= HGEDITOR=cat chg ci -Am bufferred --edit \
  > --config extensions.pushbuffer="$TESTTMP/pushbuffer.py" 2>&1 \
  > | egrep "HG:|run 'cat"
  [1]

check that commit commands succeeded:

  $ hg log -T '{rev}:{desc}\n'
  1:bufferred
  0:channeled

  $ cd ..

pager
-----

  $ cat >> fakepager.py <<EOF
  > import sys
  > for line in sys.stdin:
  >     sys.stdout.write('paged! %r\n' % line)
  > EOF

enable pager extension globally, but spawns the master server with no tty:

  $ chg init pager
  $ cd pager
  $ cat >> $HGRCPATH <<EOF
  > [extensions]
  > pager =
  > [pager]
  > pager = "$PYTHON" $TESTTMP/fakepager.py
  > EOF
  $ chg version > /dev/null
  $ touch foo
  $ chg ci -qAm foo

pager should be enabled if the attached client has a tty:

  $ chg log -l1 -q --config ui.formatted=True
  paged! '0:1f7b0de80e11\n'
  $ chg log -l1 -q --config ui.formatted=False
  0:1f7b0de80e11

chg waits for pager if runcommand raises

  $ cat > $TESTTMP/crash.py <<EOF
  > from mercurial import registrar
  > cmdtable = {}
  > command = registrar.command(cmdtable)
  > @command(b'crash')
  > def pagercrash(ui, repo, *pats, **opts):
  >     ui.write('going to crash\n')
  >     raise Exception('.')
  > EOF

  $ cat > $TESTTMP/fakepager.py <<EOF
  > from __future__ import absolute_import
  > import sys
  > import time
  > for line in iter(sys.stdin.readline, ''):
  >     if 'crash' in line: # only interested in lines containing 'crash'
  >         # if chg exits when pager is sleeping (incorrectly), the output
  >         # will be captured by the next test case
  >         time.sleep(1)
  >         sys.stdout.write('crash-pager: %s' % line)
  > EOF

  $ cat >> .hg/hgrc <<EOF
  > [extensions]
  > crash = $TESTTMP/crash.py
  > EOF

  $ chg crash --pager=on --config ui.formatted=True 2>/dev/null
  crash-pager: going to crash
  [255]

  $ cd ..

server lifecycle
----------------

chg server should be restarted on code change, and old server will shut down
automatically. In this test, we use the following time parameters:

 - "sleep 1" to make mtime different
 - "sleep 2" to notice mtime change (polling interval is 1 sec)

set up repository with an extension:

  $ chg init extreload
  $ cd extreload
  $ touch dummyext.py
  $ cat <<EOF >> .hg/hgrc
  > [extensions]
  > dummyext = dummyext.py
  > EOF

isolate socket directory for stable result:

  $ OLDCHGSOCKNAME=$CHGSOCKNAME
  $ mkdir chgsock
  $ CHGSOCKNAME=`pwd`/chgsock/server

warm up server:

  $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
  chg: debug: * start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)

new server should be started if extension modified:

  $ sleep 1
  $ touch dummyext.py
  $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
  chg: debug: * instruction: unlink $TESTTMP/extreload/chgsock/server-* (glob)
  chg: debug: * instruction: reconnect (glob)
  chg: debug: * start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)

old server will shut down, while new server should still be reachable:

  $ sleep 2
  $ CHGDEBUG= chg log 2>&1 | (egrep 'instruction|start' || true)

socket file should never be unlinked by old server:
(simulates unowned socket by updating mtime, which makes sure server exits
at polling cycle)

  $ ls chgsock/server-*
  chgsock/server-* (glob)
  $ touch chgsock/server-*
  $ sleep 2
  $ ls chgsock/server-*
  chgsock/server-* (glob)

since no server is reachable from socket file, new server should be started:
(this test makes sure that old server shut down automatically)

  $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
  chg: debug: * start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)

shut down servers and restore environment:

  $ rm -R chgsock
  $ sleep 2
  $ CHGSOCKNAME=$OLDCHGSOCKNAME
  $ cd ..

check that server events are recorded:

  $ ls log
  server.log
  server.log.1

print only the last 10 lines, since we aren't sure how many records are
preserved:

  $ cat log/server.log.1 log/server.log | tail -10 | filterlog
  YYYY/MM/DD HH:MM:SS (PID)> forked worker process (pid=...)
  YYYY/MM/DD HH:MM:SS (PID)> setprocname: ...
  YYYY/MM/DD HH:MM:SS (PID)> received fds: ...
  YYYY/MM/DD HH:MM:SS (PID)> chdir to '$TESTTMP/extreload'
  YYYY/MM/DD HH:MM:SS (PID)> setumask 18
  YYYY/MM/DD HH:MM:SS (PID)> setenv: ...
  YYYY/MM/DD HH:MM:SS (PID)> confighash = ... mtimehash = ...
  YYYY/MM/DD HH:MM:SS (PID)> validate: []
  YYYY/MM/DD HH:MM:SS (PID)> worker process exited (pid=...)
  YYYY/MM/DD HH:MM:SS (PID)> $TESTTMP/extreload/chgsock/server-... is not owned, exiting.

repository cache
----------------

  $ rm log/server.log*
  $ cp $HGRCPATH.unconfigured $HGRCPATH
  $ cat <<'EOF' >> $HGRCPATH
  > [cmdserver]
  > log = $TESTTMP/log/server.log
  > max-repo-cache = 1
  > track-log = command, repocache
  > EOF

isolate socket directory for stable result:

  $ OLDCHGSOCKNAME=$CHGSOCKNAME
  $ mkdir chgsock
  $ CHGSOCKNAME=`pwd`/chgsock/server

create empty repo and cache it:

  $ hg init cached
  $ hg id -R cached
  000000000000 tip
  $ sleep 1

modify repo (and cache will be invalidated):

  $ touch cached/a
  $ hg ci -R cached -Am 'add a'
  adding a
  $ sleep 1

read cached repo:

  $ hg log -R cached
  changeset:   0:ac82d8b1f7c4
  tag:         tip
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     add a
  
  $ sleep 1

discard cached from LRU cache:

  $ hg clone cached cached2
  updating to branch default
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ hg id -R cached2
  ac82d8b1f7c4 tip
  $ sleep 1

read uncached repo:

  $ hg log -R cached
  changeset:   0:ac82d8b1f7c4
  tag:         tip
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     add a
  
  $ sleep 1

shut down servers and restore environment:

  $ rm -R chgsock
  $ sleep 2
  $ CHGSOCKNAME=$OLDCHGSOCKNAME

check server log:

  $ cat log/server.log | filterlog
  YYYY/MM/DD HH:MM:SS (PID)> init cached
  YYYY/MM/DD HH:MM:SS (PID)> id -R cached
  YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in  ...s)
  YYYY/MM/DD HH:MM:SS (PID)> repo from cache: $TESTTMP/cached
  YYYY/MM/DD HH:MM:SS (PID)> ci -R cached -Am 'add a'
  YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in  ...s)
  YYYY/MM/DD HH:MM:SS (PID)> repo from cache: $TESTTMP/cached
  YYYY/MM/DD HH:MM:SS (PID)> log -R cached
  YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in  ...s)
  YYYY/MM/DD HH:MM:SS (PID)> clone cached cached2
  YYYY/MM/DD HH:MM:SS (PID)> id -R cached2
  YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached2 (in  ...s)
  YYYY/MM/DD HH:MM:SS (PID)> log -R cached
  YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in  ...s)