Patchwork D9151: test: add test-transaction-rollback-on-sigpipe.t demonstrating py3 regression

login
register
mail settings
Submitter phabricator
Date Oct. 5, 2020, 10:37 p.m.
Message ID <differential-rev-PHID-DREV-fr4ubzrsscb565gvjzl5-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/47380/
State Superseded
Headers show

Comments

phabricator - Oct. 5, 2020, 10:37 p.m.
mplamann created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  When an hg push is interrupted with C-c, the remote [hg serve] command
  receives SIGPIPE.
  
  If a pretxnchangegroup hook fails, the remote hg then tries to
  rollback the transaction. It begins by printing "transaction
  abort!\n". This returns EPIPE, but ui.py ignores that error.
  
  In python3 (but not python2), this "transaction abort!\n" message
  stays in a buffer, so future flushes of stderr will try to print the
  message again, and so those flushes will also hit EPIPE.
  
  This test demonstrates such a case where this EPIPE causes the
  transaction rollback to fail, leaving behind an abandoned transaction.

REPOSITORY
  rHG Mercurial

BRANCH
  stable

REVISION DETAIL
  https://phab.mercurial-scm.org/D9151

AFFECTED FILES
  tests/test-transaction-rollback-on-sigpipe.t

CHANGE DETAILS




To: mplamann, #hg-reviewers
Cc: mercurial-patches, mercurial-devel

Patch

diff --git a/tests/test-transaction-rollback-on-sigpipe.t b/tests/test-transaction-rollback-on-sigpipe.t
new file mode 100644
--- /dev/null
+++ b/tests/test-transaction-rollback-on-sigpipe.t
@@ -0,0 +1,69 @@ 
+Test that, when an hg push is interrupted and the remote side recieves SIGPIPE,
+the remote hg is able to successfully roll back the transaction.
+
+  $ hg=$(which hg)
+
+  $ $hg init -q remote
+  $ $hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" -q ssh://user@dummy/`pwd`/remote local
+
+  $ check_for_abandoned_transaction() {
+  >     [[ -f $TESTTMP/remote/.hg/store/journal ]] && echo "Abandoned transaction!"
+  > }
+
+  $ pidfile=`pwd`/pidfile
+  $ >$pidfile
+
+  $ script() {
+  >     cat >"$1"
+  >     chmod +x "$1"
+  > }
+
+On the remote end, run hg, piping stdout and stderr through processes that we
+know the PIDs of. We will later kill these to simulate an ssh client
+disconnecting.
+
+  $ killable_pipe=`pwd`/killable_pipe.sh
+  $ script $killable_pipe <<EOF
+  > #!/bin/bash
+  > echo \$\$ >> $pidfile
+  > exec cat
+  > EOF
+
+  $ remotecmd=`pwd`/remotecmd.sh
+  $ script $remotecmd <<EOF
+  > #!/bin/bash
+  > $hg "\$@" 1> >($killable_pipe) 2> >($killable_pipe >&2)
+  > EOF
+
+In the pretxnchangegroup hook, kill the PIDs recorded above to simulate ssh
+disconnecting. Then exit nonzero, to force a transaction rollback.
+
+  $ hook_script=`pwd`/pretxnchangegroup.sh
+  $ script $hook_script <<EOF
+  > #!/bin/bash
+  > for pid in \$(cat $pidfile) ; do
+  >   kill \$pid
+  >   while kill -0 \$pid 2>/dev/null ; do
+  >     sleep 0.1
+  >   done
+  > done
+  > exit 1
+  > EOF
+
+  $ cat >remote/.hg/hgrc <<EOF
+  > [hooks]
+  > pretxnchangegroup.break-things=$hook_script
+  > EOF
+
+  $ cd local
+  $ echo foo > foo ; $hg commit -qAm "commit"
+  $ $hg push -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" --remotecmd $remotecmd 2>&1 | grep -v $killable_pipe
+  pushing to ssh://user@dummy/$TESTTMP/remote
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  abort: stream ended unexpectedly (got 0 bytes, expected 4)
+
+  $ check_for_abandoned_transaction
+  Abandoned transaction!