tests/test-pager.t
author Gregory Szorc <gregory.szorc@gmail.com>
Sat, 14 Apr 2018 11:50:19 -0700
changeset 37719 a656cba08a04
parent 37598 7a9c905e51f9
child 38074 5a3feb2bc9dd
permissions -rw-r--r--
wireprotov2: move response handling out of httppeer And fix some bugs while we're here. The code for processing response data from the unified framing protocol is mostly peer agnostic. The peer-specific bits are the configuration of the client reactor and how I/O is performed. I initially implemented things in httppeer for expediency. This commit establishes a module for holding the peer API level code for the framing based protocol. Inside this module we have a class to help coordinate higher-level activities, such as managing response object. The client handler bits could be rolled into clientreactor. However, I want clientreactor to be sans I/O and I want it to only be concerned with protocol-level details, not higher-level concepts like how protocol events are converted into peer API concepts. I want clientreactor to receive a frame and then tell the caller what should probably be done about it. If we start putting things like future resolution into clientreactor, we'll constrain how the protocol can be used (e.g. by requiring futures). The new code is loosely based on what was in httppeer before. I changed things a bit around response handling. We now buffer the entire response "body" and then handle it as one atomic unit. This fixed a bug around decoding CBOR data that spanned multiple frames. I also fixed an off-by-one bug where we failed to read a single byte CBOR value at the end of the stream. That's why tests have changed. The new state of httppeer is much cleaner. It is largely agnostic about framing protocol implementation details. That's how it should be: the framing protocol is designed to be largely transport agnostic. We want peers merely putting bytes on the wire and telling the framing protocol where to read response data from. There's still a bit of work to be done here, especially for representing responses. But at least we're a step closer to having a higher-level peer interface that can be plugged into the SSH peer someday. I initially added this class to wireprotoframing. However, we'll eventually need version 2 specific functions to convert CBOR responses into data structures expected by the code calling commands. This needs to live somewhere. Since that code would be shared across peers, we need a common module. We have wireprotov1peer for the equivalent version 1 code. So I decided to establish wireprotov2peer. Differential Revision: https://phab.mercurial-scm.org/D3379

  $ cat >> fakepager.py <<EOF
  > import sys
  > printed = False
  > for line in sys.stdin:
  >     sys.stdout.write('paged! %r\n' % line)
  >     printed = True
  > if not printed:
  >     sys.stdout.write('paged empty output!\n')
  > EOF

Enable ui.formatted because pager won't fire without it, and set up
pager and tell it to use our fake pager that lets us see when the
pager was running.
  $ cat >> $HGRCPATH <<EOF
  > [ui]
  > formatted = yes
  > color = no
  > [pager]
  > pager = $PYTHON $TESTTMP/fakepager.py
  > EOF

  $ hg init repo
  $ cd repo
  $ echo a >> a
  $ hg add a
  $ hg ci -m 'add a'
  $ for x in `$PYTHON $TESTDIR/seq.py 1 10`; do
  >   echo a $x >> a
  >   hg ci -m "modify a $x"
  > done

By default diff and log are paged, but id is not:

  $ hg diff -c 2 --pager=yes
  paged! 'diff -r f4be7687d414 -r bce265549556 a\n'
  paged! '--- a/a\tThu Jan 01 00:00:00 1970 +0000\n'
  paged! '+++ b/a\tThu Jan 01 00:00:00 1970 +0000\n'
  paged! '@@ -1,2 +1,3 @@\n'
  paged! ' a\n'
  paged! ' a 1\n'
  paged! '+a 2\n'

  $ hg log --limit 2
  paged! 'changeset:   10:46106edeeb38\n'
  paged! 'tag:         tip\n'
  paged! 'user:        test\n'
  paged! 'date:        Thu Jan 01 00:00:00 1970 +0000\n'
  paged! 'summary:     modify a 10\n'
  paged! '\n'
  paged! 'changeset:   9:6dd8ea7dd621\n'
  paged! 'user:        test\n'
  paged! 'date:        Thu Jan 01 00:00:00 1970 +0000\n'
  paged! 'summary:     modify a 9\n'
  paged! '\n'

  $ hg id
  46106edeeb38 tip

We can control the pager from the config

  $ hg log --limit 1 --config 'ui.paginate=False'
  changeset:   10:46106edeeb38
  tag:         tip
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     modify a 10
  
  $ hg log --limit 1 --config 'ui.paginate=0'
  changeset:   10:46106edeeb38
  tag:         tip
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     modify a 10
  
  $ hg log --limit 1 --config 'ui.paginate=1'
  paged! 'changeset:   10:46106edeeb38\n'
  paged! 'tag:         tip\n'
  paged! 'user:        test\n'
  paged! 'date:        Thu Jan 01 00:00:00 1970 +0000\n'
  paged! 'summary:     modify a 10\n'
  paged! '\n'

explicit --pager=on should take precedence over other configurations
(issue5580)

  $ cat >> $HGRCPATH <<EOF
  > [ui]
  > paginate = false
  > EOF
  $ hg log --limit 1 --pager=on
  paged! 'changeset:   10:46106edeeb38\n'
  paged! 'tag:         tip\n'
  paged! 'user:        test\n'
  paged! 'date:        Thu Jan 01 00:00:00 1970 +0000\n'
  paged! 'summary:     modify a 10\n'
  paged! '\n'

  $ cat >> $HGRCPATH <<EOF
  > [ui]
  > # true is default value of ui.paginate
  > paginate = true
  > EOF
  $ hg log --limit 1 --pager=off
  changeset:   10:46106edeeb38
  tag:         tip
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     modify a 10
  

We can enable the pager on id:

BROKEN: should be paged
  $ hg --config pager.attend-id=yes id
  46106edeeb38 tip

Setting attend-$COMMAND to a false value works, even with pager in
core:
  $ hg --config pager.attend-diff=no diff -c 2
  diff -r f4be7687d414 -r bce265549556 a
  --- a/a	Thu Jan 01 00:00:00 1970 +0000
  +++ b/a	Thu Jan 01 00:00:00 1970 +0000
  @@ -1,2 +1,3 @@
   a
   a 1
  +a 2

Command aliases should have same behavior as main command

  $ hg history --limit 2
  paged! 'changeset:   10:46106edeeb38\n'
  paged! 'tag:         tip\n'
  paged! 'user:        test\n'
  paged! 'date:        Thu Jan 01 00:00:00 1970 +0000\n'
  paged! 'summary:     modify a 10\n'
  paged! '\n'
  paged! 'changeset:   9:6dd8ea7dd621\n'
  paged! 'user:        test\n'
  paged! 'date:        Thu Jan 01 00:00:00 1970 +0000\n'
  paged! 'summary:     modify a 9\n'
  paged! '\n'

Abbreviated command alias should also be paged

  $ hg hist -l 1
  paged! 'changeset:   10:46106edeeb38\n'
  paged! 'tag:         tip\n'
  paged! 'user:        test\n'
  paged! 'date:        Thu Jan 01 00:00:00 1970 +0000\n'
  paged! 'summary:     modify a 10\n'
  paged! '\n'

Attend for an abbreviated command does not work

  $ hg --config pager.attend-ident=true ident
  46106edeeb38 tip

  $ hg --config extensions.pager= --config pager.attend-ident=true ident
  46106edeeb38 tip

Pager should not start if stdout is not a tty.

  $ hg log -l1 -q --config ui.formatted=False
  10:46106edeeb38

Pager should be disabled if pager.pager is empty (otherwise the output would
be silently lost.)

  $ hg log -l1 -q --config pager.pager=
  10:46106edeeb38

Pager with color enabled allows colors to come through by default,
even though stdout is no longer a tty.
  $ cat >> $HGRCPATH <<EOF
  > [ui]
  > color = always
  > [color]
  > mode = ansi
  > EOF
  $ hg log --limit 3
  paged! '\x1b[0;33mchangeset:   10:46106edeeb38\x1b[0m\n'
  paged! 'tag:         tip\n'
  paged! 'user:        test\n'
  paged! 'date:        Thu Jan 01 00:00:00 1970 +0000\n'
  paged! 'summary:     modify a 10\n'
  paged! '\n'
  paged! '\x1b[0;33mchangeset:   9:6dd8ea7dd621\x1b[0m\n'
  paged! 'user:        test\n'
  paged! 'date:        Thu Jan 01 00:00:00 1970 +0000\n'
  paged! 'summary:     modify a 9\n'
  paged! '\n'
  paged! '\x1b[0;33mchangeset:   8:cff05a6312fe\x1b[0m\n'
  paged! 'user:        test\n'
  paged! 'date:        Thu Jan 01 00:00:00 1970 +0000\n'
  paged! 'summary:     modify a 8\n'
  paged! '\n'

#if no-chg
An invalid pager command name is reported sensibly if we don't have to
use shell=True in the subprocess call:
  $ hg log --limit 3 --config pager.pager=this-command-better-never-exist
  missing pager command 'this-command-better-never-exist', skipping pager
  \x1b[0;33mchangeset:   10:46106edeeb38\x1b[0m (esc)
  tag:         tip
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     modify a 10
  
  \x1b[0;33mchangeset:   9:6dd8ea7dd621\x1b[0m (esc)
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     modify a 9
  
  \x1b[0;33mchangeset:   8:cff05a6312fe\x1b[0m (esc)
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     modify a 8
  
#endif

A complicated pager command gets worse behavior. Bonus points if you can
improve this.
  $ hg log --limit 3 \
  >   --config pager.pager='this-command-better-never-exist --seriously' \
  >  2>/dev/null || true

Pager works with shell aliases.

  $ cat >> $HGRCPATH <<EOF
  > [alias]
  > echoa = !echo a
  > EOF

  $ hg echoa
  a
BROKEN: should be paged
  $ hg --config pager.attend-echoa=yes echoa
  a

Pager works with hg aliases including environment variables.

  $ cat >> $HGRCPATH <<'EOF'
  > [alias]
  > printa = log -T "$A\n" -r 0
  > EOF

  $ A=1 hg --config pager.attend-printa=yes printa
  paged! '1\n'
  $ A=2 hg --config pager.attend-printa=yes printa
  paged! '2\n'

Something that's explicitly attended is still not paginated if the
pager is globally set to off using a flag:
  $ A=2 hg --config pager.attend-printa=yes printa --pager=no
  2

Pager should not override the exit code of other commands

  $ cat >> $TESTTMP/fortytwo.py <<'EOF'
  > from mercurial import commands, registrar
  > cmdtable = {}
  > command = registrar.command(cmdtable)
  > @command(b'fortytwo', [], 'fortytwo', norepo=True)
  > def fortytwo(ui, *opts):
  >     ui.write('42\n')
  >     return 42
  > EOF

  $ cat >> $HGRCPATH <<'EOF'
  > [extensions]
  > fortytwo = $TESTTMP/fortytwo.py
  > EOF

  $ hg fortytwo --pager=on
  paged! '42\n'
  [42]

A command that asks for paging using ui.pager() directly works:
  $ hg blame a
  paged! ' 0: a\n'
  paged! ' 1: a 1\n'
  paged! ' 2: a 2\n'
  paged! ' 3: a 3\n'
  paged! ' 4: a 4\n'
  paged! ' 5: a 5\n'
  paged! ' 6: a 6\n'
  paged! ' 7: a 7\n'
  paged! ' 8: a 8\n'
  paged! ' 9: a 9\n'
  paged! '10: a 10\n'
but not with HGPLAIN
  $ HGPLAIN=1 hg blame a
   0: a
   1: a 1
   2: a 2
   3: a 3
   4: a 4
   5: a 5
   6: a 6
   7: a 7
   8: a 8
   9: a 9
  10: a 10
explicit flags work too:
  $ hg blame --pager=no a
   0: a
   1: a 1
   2: a 2
   3: a 3
   4: a 4
   5: a 5
   6: a 6
   7: a 7
   8: a 8
   9: a 9
  10: a 10

A command with --output option:

  $ hg cat -r0 a
  paged! 'a\n'
  $ hg cat -r0 a --output=-
  paged! 'a\n'
  $ hg cat -r0 a --output=out

  $ hg export -r0
  paged! '# HG changeset patch\n'
  paged! '# User test\n'
  paged! '# Date 0 0\n'
  paged! '#      Thu Jan 01 00:00:00 1970 +0000\n'
  paged! '# Node ID 1f0dee641bb7258c56bd60e93edfa2405381c41e\n'
  paged! '# Parent  0000000000000000000000000000000000000000\n'
  paged! 'add a\n'
  paged! '\n'
  paged! '\x1b[0;1mdiff -r 000000000000 -r 1f0dee641bb7 a\x1b[0m\n'
  paged! '\x1b[0;31;1m--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\x1b[0m\n'
  paged! '\x1b[0;32;1m+++ b/a\tThu Jan 01 00:00:00 1970 +0000\x1b[0m\n'
  paged! '\x1b[0;35m@@ -0,0 +1,1 @@\x1b[0m\n'
  paged! '\x1b[0;32m+a\x1b[0m\n'
  $ hg export -r0 -o -
  paged! '# HG changeset patch\n'
  paged! '# User test\n'
  paged! '# Date 0 0\n'
  paged! '#      Thu Jan 01 00:00:00 1970 +0000\n'
  paged! '# Node ID 1f0dee641bb7258c56bd60e93edfa2405381c41e\n'
  paged! '# Parent  0000000000000000000000000000000000000000\n'
  paged! 'add a\n'
  paged! '\n'
  paged! '\x1b[0;1mdiff -r 000000000000 -r 1f0dee641bb7 a\x1b[0m\n'
  paged! '\x1b[0;31;1m--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\x1b[0m\n'
  paged! '\x1b[0;32;1m+++ b/a\tThu Jan 01 00:00:00 1970 +0000\x1b[0m\n'
  paged! '\x1b[0;35m@@ -0,0 +1,1 @@\x1b[0m\n'
  paged! '\x1b[0;32m+a\x1b[0m\n'
  $ hg export -r0 -o out

  $ rm out

Put annotate in the ignore list for pager:
  $ cat >> $HGRCPATH <<EOF
  > [pager]
  > ignore = annotate
  > EOF
  $ hg blame a
   0: a
   1: a 1
   2: a 2
   3: a 3
   4: a 4
   5: a 5
   6: a 6
   7: a 7
   8: a 8
   9: a 9
  10: a 10

During pushbuffer, pager should not start:
  $ cat > $TESTTMP/pushbufferpager.py <<EOF
  > def uisetup(ui):
  >     ui.pushbuffer()
  >     ui.pager('mycmd')
  >     ui.write('content\n')
  >     ui.write(ui.popbuffer())
  > EOF

  $ echo append >> a
  $ hg --config extensions.pushbuffer=$TESTTMP/pushbufferpager.py status --color=off
  content
  paged! 'M a\n'

Environment variables like LESS and LV are set automatically:
  $ cat > $TESTTMP/printlesslv.py <<EOF
  > from __future__ import absolute_import
  > import os
  > import sys
  > sys.stdin.read()
  > for name in ['LESS', 'LV']:
  >     sys.stdout.write(('%s=%s\n') % (name, os.environ.get(name, '-')))
  > sys.stdout.flush()
  > EOF

  $ cat >> $HGRCPATH <<EOF
  > [alias]
  > noop = log -r 0 -T ''
  > [ui]
  > formatted=1
  > [pager]
  > pager = $PYTHON $TESTTMP/printlesslv.py
  > EOF
  $ unset LESS
  $ unset LV
  $ hg noop --pager=on
  LESS=FRX
  LV=-c
  $ LESS=EFGH hg noop --pager=on
  LESS=EFGH
  LV=-c