tests: extract test-template-functions.t from test-command-template.t
authorYuya Nishihara <yuya@tcha.org>
Sat, 16 Jun 2018 13:17:11 +0900
changeset 38434 70f551a3f52e
parent 38433 ddce7bdf7f3c
child 38435 6a8ed5c7e112
tests: extract test-template-functions.t from test-command-template.t I decided to not split filters and functions into two test files since we sometimes reimplement a filter as a function.
contrib/python3-whitelist
tests/test-command-template.t
tests/test-template-functions.t
--- a/contrib/python3-whitelist	Sat Jun 16 12:37:43 2018 +0900
+++ b/contrib/python3-whitelist	Sat Jun 16 13:17:11 2018 +0900
@@ -490,6 +490,7 @@
 test-tag.t
 test-tags.t
 test-template-filters.t
+test-template-functions.t
 test-template-map.t
 test-transplant.t
 test-treemanifest.t
--- a/tests/test-command-template.t	Sat Jun 16 12:37:43 2018 +0900
+++ b/tests/test-command-template.t	Sat Jun 16 13:17:11 2018 +0900
@@ -929,225 +929,12 @@
   p2node--debug: 0000000000000000000000000000000000000000
   p2node--debug: 0000000000000000000000000000000000000000
 
-Filters work:
-
-  $ hg log --template '{author|domain}\n'
-  
-  hostname
-  
-  
-  
-  
-  place
-  place
-  hostname
-
-  $ hg log --template '{author|person}\n'
-  test
-  User Name
-  person
-  person
-  person
-  person
-  other
-  A. N. Other
-  User Name
-
-  $ hg log --template '{author|user}\n'
-  test
-  user
-  person
-  person
-  person
-  person
-  other
-  other
-  user
-
-  $ hg log --template '{date|date}\n'
-  Wed Jan 01 10:01:00 2020 +0000
-  Mon Jan 12 13:46:40 1970 +0000
-  Sun Jan 18 08:40:01 1970 +0000
-  Sun Jan 18 08:40:00 1970 +0000
-  Sat Jan 17 04:53:20 1970 +0000
-  Fri Jan 16 01:06:40 1970 +0000
-  Wed Jan 14 21:20:00 1970 +0000
-  Tue Jan 13 17:33:20 1970 +0000
-  Mon Jan 12 13:46:40 1970 +0000
-
-  $ hg log --template '{date|isodate}\n'
-  2020-01-01 10:01 +0000
-  1970-01-12 13:46 +0000
-  1970-01-18 08:40 +0000
-  1970-01-18 08:40 +0000
-  1970-01-17 04:53 +0000
-  1970-01-16 01:06 +0000
-  1970-01-14 21:20 +0000
-  1970-01-13 17:33 +0000
-  1970-01-12 13:46 +0000
-
-  $ hg log --template '{date|isodatesec}\n'
-  2020-01-01 10:01:00 +0000
-  1970-01-12 13:46:40 +0000
-  1970-01-18 08:40:01 +0000
-  1970-01-18 08:40:00 +0000
-  1970-01-17 04:53:20 +0000
-  1970-01-16 01:06:40 +0000
-  1970-01-14 21:20:00 +0000
-  1970-01-13 17:33:20 +0000
-  1970-01-12 13:46:40 +0000
-
-  $ hg log --template '{date|rfc822date}\n'
-  Wed, 01 Jan 2020 10:01:00 +0000
-  Mon, 12 Jan 1970 13:46:40 +0000
-  Sun, 18 Jan 1970 08:40:01 +0000
-  Sun, 18 Jan 1970 08:40:00 +0000
-  Sat, 17 Jan 1970 04:53:20 +0000
-  Fri, 16 Jan 1970 01:06:40 +0000
-  Wed, 14 Jan 1970 21:20:00 +0000
-  Tue, 13 Jan 1970 17:33:20 +0000
-  Mon, 12 Jan 1970 13:46:40 +0000
-
-  $ hg log --template '{desc|firstline}\n'
-  third
-  second
-  merge
-  new head
-  new branch
-  no user, no domain
-  no person
-  other 1
-  line 1
-
-  $ hg log --template '{node|short}\n'
-  95c24699272e
-  29114dbae42b
-  d41e714fe50d
-  13207e5a10d9
-  bbe44766e73d
-  10e46f2dcbf4
-  97054abb4ab8
-  b608e9d1a3f0
-  1e4e1b8f71e0
-
-  $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
-  <changeset author="test"/>
-  <changeset author="User Name &lt;user@hostname&gt;"/>
-  <changeset author="person"/>
-  <changeset author="person"/>
-  <changeset author="person"/>
-  <changeset author="person"/>
-  <changeset author="other@place"/>
-  <changeset author="A. N. Other &lt;other@place&gt;"/>
-  <changeset author="User Name &lt;user@hostname&gt;"/>
-
-  $ hg log --template '{rev}: {children}\n'
-  8: 
-  7: 8:95c24699272e
-  6: 
-  5: 6:d41e714fe50d
-  4: 6:d41e714fe50d
-  3: 4:bbe44766e73d 5:13207e5a10d9
-  2: 3:10e46f2dcbf4
-  1: 2:97054abb4ab8
-  0: 1:b608e9d1a3f0
-
-Formatnode filter works:
-
-  $ hg -q log -r 0 --template '{node|formatnode}\n'
-  1e4e1b8f71e0
-
-  $ hg log -r 0 --template '{node|formatnode}\n'
-  1e4e1b8f71e0
-
-  $ hg -v log -r 0 --template '{node|formatnode}\n'
-  1e4e1b8f71e0
-
-  $ hg --debug log -r 0 --template '{node|formatnode}\n'
-  1e4e1b8f71e05681d422154f5421e385fec3454f
-
-Age filter:
-
-  $ hg init unstable-hash
-  $ cd unstable-hash
-  $ hg log --template '{date|age}\n' > /dev/null || exit 1
-
-  >>> from __future__ import absolute_import
-  >>> import datetime
-  >>> fp = open('a', 'wb')
-  >>> n = datetime.datetime.now() + datetime.timedelta(366 * 7)
-  >>> fp.write(b'%d-%d-%d 00:00' % (n.year, n.month, n.day)) and None
-  >>> fp.close()
-  $ hg add a
-  $ hg commit -m future -d "`cat a`"
-
-  $ hg log -l1 --template '{date|age}\n'
-  7 years from now
-
-  $ cd ..
-  $ rm -rf unstable-hash
-
-Filename filters:
-
-  $ hg debugtemplate '{"foo/bar"|basename}|{"foo/"|basename}|{"foo"|basename}|\n'
-  bar||foo|
-  $ hg debugtemplate '{"foo/bar"|dirname}|{"foo/"|dirname}|{"foo"|dirname}|\n'
-  foo|foo||
-  $ hg debugtemplate '{"foo/bar"|stripdir}|{"foo/"|stripdir}|{"foo"|stripdir}|\n'
-  foo|foo|foo|
-
 Add a dummy commit to make up for the instability of the above:
 
   $ echo a > a
   $ hg add a
   $ hg ci -m future
 
-Count filter:
-
-  $ hg log -l1 --template '{node|count} {node|short|count}\n'
-  40 12
-
-  $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
-  0 1 4
-
-  $ hg log -G --template '{rev}: children: {children|count}, \
-  > tags: {tags|count}, file_adds: {file_adds|count}, \
-  > ancestors: {revset("ancestors(%s)", rev)|count}'
-  @  9: children: 0, tags: 1, file_adds: 1, ancestors: 3
-  |
-  o  8: children: 1, tags: 0, file_adds: 2, ancestors: 2
-  |
-  o  7: children: 1, tags: 0, file_adds: 1, ancestors: 1
-  
-  o    6: children: 0, tags: 0, file_adds: 0, ancestors: 7
-  |\
-  | o  5: children: 1, tags: 0, file_adds: 1, ancestors: 5
-  | |
-  o |  4: children: 1, tags: 0, file_adds: 0, ancestors: 5
-  |/
-  o  3: children: 2, tags: 0, file_adds: 0, ancestors: 4
-  |
-  o  2: children: 1, tags: 0, file_adds: 1, ancestors: 3
-  |
-  o  1: children: 1, tags: 0, file_adds: 1, ancestors: 2
-  |
-  o  0: children: 1, tags: 0, file_adds: 1, ancestors: 1
-  
-
-  $ hg log -l1 -T '{termwidth|count}\n'
-  hg: parse error: not countable
-  (template filter 'count' is not compatible with keyword 'termwidth')
-  [255]
-
-Upper/lower filters:
-
-  $ hg log -r0 --template '{branch|upper}\n'
-  DEFAULT
-  $ hg log -r0 --template '{author|lower}\n'
-  user name <user@hostname>
-  $ hg log -r0 --template '{date|upper}\n'
-  1000000.00
-
 Add a commit that does all possible modifications at once
 
   $ echo modify >> third
@@ -1266,11 +1053,6 @@
   hg: parse error: unknown function 'foo'
   [255]
 
-Pass generator object created by template function to filter
-
-  $ hg log -l 1 --template '{if(author, author)|user}\n'
-  test
-
 Test index keyword:
 
   $ hg log -l 2 -T '{index + 10}{files % " {index}:{file}"}\n'
@@ -1281,68 +1063,6 @@
   0 default
   1 foo
 
-Test diff function:
-
-  $ hg diff -c 8
-  diff -r 29114dbae42b -r 95c24699272e fourth
-  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
-  +++ b/fourth	Wed Jan 01 10:01:00 2020 +0000
-  @@ -0,0 +1,1 @@
-  +second
-  diff -r 29114dbae42b -r 95c24699272e second
-  --- a/second	Mon Jan 12 13:46:40 1970 +0000
-  +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
-  @@ -1,1 +0,0 @@
-  -second
-  diff -r 29114dbae42b -r 95c24699272e third
-  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
-  +++ b/third	Wed Jan 01 10:01:00 2020 +0000
-  @@ -0,0 +1,1 @@
-  +third
-
-  $ hg log -r 8 -T "{diff()}"
-  diff -r 29114dbae42b -r 95c24699272e fourth
-  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
-  +++ b/fourth	Wed Jan 01 10:01:00 2020 +0000
-  @@ -0,0 +1,1 @@
-  +second
-  diff -r 29114dbae42b -r 95c24699272e second
-  --- a/second	Mon Jan 12 13:46:40 1970 +0000
-  +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
-  @@ -1,1 +0,0 @@
-  -second
-  diff -r 29114dbae42b -r 95c24699272e third
-  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
-  +++ b/third	Wed Jan 01 10:01:00 2020 +0000
-  @@ -0,0 +1,1 @@
-  +third
-
-  $ hg log -r 8 -T "{diff('glob:f*')}"
-  diff -r 29114dbae42b -r 95c24699272e fourth
-  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
-  +++ b/fourth	Wed Jan 01 10:01:00 2020 +0000
-  @@ -0,0 +1,1 @@
-  +second
-
-  $ hg log -r 8 -T "{diff('', 'glob:f*')}"
-  diff -r 29114dbae42b -r 95c24699272e second
-  --- a/second	Mon Jan 12 13:46:40 1970 +0000
-  +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
-  @@ -1,1 +0,0 @@
-  -second
-  diff -r 29114dbae42b -r 95c24699272e third
-  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
-  +++ b/third	Wed Jan 01 10:01:00 2020 +0000
-  @@ -0,0 +1,1 @@
-  +third
-
-  $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
-  diff -r 29114dbae42b -r 95c24699272e fourth
-  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
-  +++ b/fourth	Wed Jan 01 10:01:00 2020 +0000
-  @@ -0,0 +1,1 @@
-  +second
-
 ui verbosity:
 
   $ hg log -l1 -T '{verbosity}\n'
@@ -1545,32 +1265,6 @@
   o  0: null+1,1
   
 
-  $ hg log -G --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
-  @  11: t3, C: 9, D: 8
-  |
-  o  10: t3, C: 8, D: 7
-  |
-  o  9: t3, C: 7, D: 6
-  |
-  o  8: t3, C: 6, D: 5
-  |
-  o  7: t3, C: 5, D: 4
-  |
-  o  6: t3, C: 4, D: 3
-  |
-  o    5: t3, C: 3, D: 2
-  |\
-  | o  4: t3, C: 1, D: 1
-  | |
-  | o  3: t3, C: 0, D: 0
-  | |
-  o |  2: t1, C: 1, D: 1
-  |/
-  o  1: t1, C: 0, D: 0
-  |
-  o  0: null, C: 1, D: 1
-  
-
   $ cd ..
 
 Test new-style inline templating:
@@ -1611,122 +1305,6 @@
   $ hg log -R latesttag -l1 -T '{max(revset("0:9")) % "{rev}:{node|short}\n"}'
   9:fbc7cd862e9c
 
-Test manifest/get() can be join()-ed as string, though it's silly:
-
-  $ hg log -R latesttag -r tip -T '{join(manifest, ".")}\n'
-  1.1.:.2.b.c.6.e.9.0.0.6.c.e.2
-  $ hg log -R latesttag -r tip -T '{join(get(extras, "branch"), ".")}\n'
-  d.e.f.a.u.l.t
-
-Test join() over string
-
-  $ hg log -R latesttag -r tip -T '{join(rev|stringify, ".")}\n'
-  1.1
-
-Test join() over uniterable
-
-  $ hg log -R latesttag -r tip -T '{join(rev, "")}\n'
-  hg: parse error: 11 is not iterable
-  [255]
-
-Test min/max of integers
-
-  $ hg log -R latesttag -l1 -T '{min(revset("9:10"))}\n'
-  9
-  $ hg log -R latesttag -l1 -T '{max(revset("9:10"))}\n'
-  10
-
-Test min/max over map operation:
-
-  $ hg log -R latesttag -r3 -T '{min(tags % "{tag}")}\n'
-  at3
-  $ hg log -R latesttag -r3 -T '{max(tags % "{tag}")}\n'
-  t3
-
-Test min/max of strings:
-
-  $ hg log -R latesttag -l1 -T '{min(desc)}\n'
-  3
-  $ hg log -R latesttag -l1 -T '{max(desc)}\n'
-  t
-
-Test min/max of non-iterable:
-
-  $ hg debugtemplate '{min(1)}'
-  hg: parse error: 1 is not iterable
-  (min first argument should be an iterable)
-  [255]
-  $ hg debugtemplate '{max(2)}'
-  hg: parse error: 2 is not iterable
-  (max first argument should be an iterable)
-  [255]
-
-  $ hg log -R latesttag -l1 -T '{min(date)}'
-  hg: parse error: date is not iterable
-  (min first argument should be an iterable)
-  [255]
-  $ hg log -R latesttag -l1 -T '{max(date)}'
-  hg: parse error: date is not iterable
-  (max first argument should be an iterable)
-  [255]
-
-Test min/max of empty sequence:
-
-  $ hg debugtemplate '{min("")}'
-  hg: parse error: empty string
-  (min first argument should be an iterable)
-  [255]
-  $ hg debugtemplate '{max("")}'
-  hg: parse error: empty string
-  (max first argument should be an iterable)
-  [255]
-  $ hg debugtemplate '{min(dict())}'
-  hg: parse error: empty sequence
-  (min first argument should be an iterable)
-  [255]
-  $ hg debugtemplate '{max(dict())}'
-  hg: parse error: empty sequence
-  (max first argument should be an iterable)
-  [255]
-  $ hg debugtemplate '{min(dict() % "")}'
-  hg: parse error: empty sequence
-  (min first argument should be an iterable)
-  [255]
-  $ hg debugtemplate '{max(dict() % "")}'
-  hg: parse error: empty sequence
-  (max first argument should be an iterable)
-  [255]
-
-Test min/max of if() result
-
-  $ cd latesttag
-  $ hg log -l1 -T '{min(if(true, revset("9:10"), ""))}\n'
-  9
-  $ hg log -l1 -T '{max(if(false, "", revset("9:10")))}\n'
-  10
-  $ hg log -l1 -T '{min(ifcontains("a", "aa", revset("9:10"), ""))}\n'
-  9
-  $ hg log -l1 -T '{max(ifcontains("a", "bb", "", revset("9:10")))}\n'
-  10
-  $ hg log -l1 -T '{min(ifeq(0, 0, revset("9:10"), ""))}\n'
-  9
-  $ hg log -l1 -T '{max(ifeq(0, 1, "", revset("9:10")))}\n'
-  10
-  $ cd ..
-
-Test laziness of if() then/else clause
-
-  $ hg debugtemplate '{count(0)}'
-  hg: parse error: not countable
-  (incompatible use of template filter 'count')
-  [255]
-  $ hg debugtemplate '{if(true, "", count(0))}'
-  $ hg debugtemplate '{if(false, count(0), "")}'
-  $ hg debugtemplate '{ifcontains("a", "aa", "", count(0))}'
-  $ hg debugtemplate '{ifcontains("a", "bb", count(0), "")}'
-  $ hg debugtemplate '{ifeq(0, 0, "", count(0))}'
-  $ hg debugtemplate '{ifeq(0, 1, count(0), "")}'
-
 Test dot operator precedence:
 
   $ hg debugtemplate -R latesttag -r0 -v '{manifest.node|short}\n'
@@ -1781,70 +1359,6 @@
   hg: parse error: 'a' is not a dictionary
   [255]
 
-Test the sub function of templating for expansion:
-
-  $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
-  xx
-
-  $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
-  hg: parse error: sub got an invalid pattern: [
-  [255]
-  $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
-  hg: parse error: sub got an invalid replacement: \1
-  [255]
-
-Test the strip function with chars specified:
-
-  $ hg log -R latesttag --template '{desc}\n'
-  at3
-  t5
-  t4
-  t3
-  t2
-  t1
-  merge
-  h2e
-  h2d
-  h1c
-  b
-  a
-
-  $ hg log -R latesttag --template '{strip(desc, "te")}\n'
-  at3
-  5
-  4
-  3
-  2
-  1
-  merg
-  h2
-  h2d
-  h1c
-  b
-  a
-
-Test date format:
-
-  $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
-  date: 70 01 01 10 +0000
-  date: 70 01 01 09 +0000
-  date: 70 01 01 04 +0000
-  date: 70 01 01 08 +0000
-  date: 70 01 01 07 +0000
-  date: 70 01 01 06 +0000
-  date: 70 01 01 05 +0100
-  date: 70 01 01 04 +0000
-  date: 70 01 01 03 +0000
-  date: 70 01 01 02 +0000
-  date: 70 01 01 01 +0000
-  date: 70 01 01 00 +0000
-
-Test invalid date:
-
-  $ hg log -R latesttag -T '{date(rev)}\n'
-  hg: parse error: date expects a date information
-  [255]
-
 Test integer literal:
 
   $ hg debugtemplate -v '{(0)}\n'
@@ -2234,498 +1748,10 @@
   $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
   \x1b[0;32mtext\x1b[0m (esc)
 
-color effect can be specified without quoting:
-
-  $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
-  \x1b[0;31mtext\x1b[0m (esc)
-
-color effects can be nested (issue5413)
-
-  $ hg debugtemplate --color=always \
-  > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n'
-  \x1b[0;31mred\x1b[0;35mma\x1b[0;36mcyan\x1b[0m\x1b[0;31m\x1b[0;35m\x1b[0;33myellow\x1b[0m\x1b[0;31m\x1b[0;35mgenta\x1b[0m (esc)
-
-pad() should interact well with color codes (issue5416)
-
-  $ hg debugtemplate --color=always \
-  > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n'
-  \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc)
-
-label should be no-op if color is disabled:
-
-  $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
-  text
-  $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
-  text
-
-Test branches inside if statement:
-
-  $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
-  no
-
-Test dict constructor:
-
-  $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n'
-  y=f7769ec2ab97 x=0
-  $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}'
-  x=0
-  y=f7769ec2ab97
-  $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n'
-  {"x": 0, "y": "f7769ec2ab97"}
-  $ hg log -r 0 -T '{dict()|json}\n'
-  {}
-
-  $ hg log -r 0 -T '{dict(rev, node=node|short)}\n'
-  rev=0 node=f7769ec2ab97
-  $ hg log -r 0 -T '{dict(rev, node|short)}\n'
-  rev=0 node=f7769ec2ab97
-
-  $ hg log -r 0 -T '{dict(rev, rev=rev)}\n'
-  hg: parse error: duplicated dict key 'rev' inferred
-  [255]
-  $ hg log -r 0 -T '{dict(node, node|short)}\n'
-  hg: parse error: duplicated dict key 'node' inferred
-  [255]
-  $ hg log -r 0 -T '{dict(1 + 2)}'
-  hg: parse error: dict key cannot be inferred
-  [255]
-
-  $ hg log -r 0 -T '{dict(x=rev, x=node)}'
-  hg: parse error: dict got multiple values for keyword argument 'x'
-  [255]
-
-Test get function:
-
-  $ hg log -r 0 --template '{get(extras, "branch")}\n'
-  default
-  $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
-  default
-  $ hg log -r 0 --template '{get(files, "should_fail")}\n'
-  hg: parse error: not a dictionary
-  (get() expects a dict as first argument)
-  [255]
-
-Test json filter applied to wrapped object:
-
-  $ hg log -r0 -T '{files|json}\n'
-  ["a"]
-  $ hg log -r0 -T '{extras|json}\n'
-  {"branch": "default"}
-  $ hg log -r0 -T '{date|json}\n'
-  [0, 0]
-
-Test json filter applied to map result:
-
-  $ hg log -r0 -T '{json(extras % "{key}")}\n'
-  ["branch"]
-
-Test localdate(date, tz) function:
-
-  $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
-  1970-01-01 09:00 +0900
-  $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
-  1970-01-01 00:00 +0000
-  $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n'
-  hg: parse error: localdate expects a timezone
-  [255]
-  $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
-  1970-01-01 02:00 +0200
-  $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
-  1970-01-01 00:00 +0000
-  $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
-  1970-01-01 00:00 +0000
-  $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
-  hg: parse error: localdate expects a timezone
-  [255]
-  $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
-  hg: parse error: localdate expects a timezone
-  [255]
-
-Test shortest(node) function:
+Just one more commit:
 
   $ echo b > b
   $ hg ci -qAm b
-  $ hg log --template '{shortest(node)}\n'
-  e777
-  bcc7
-  f776
-  $ hg log --template '{shortest(node, 10)}\n'
-  e777603221
-  bcc7ff960b
-  f7769ec2ab
-  $ hg log --template '{node|shortest}\n' -l1
-  e777
-
-  $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
-  f7769ec2ab
-  $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
-  hg: parse error: shortest() expects an integer minlength
-  [255]
-
-  $ hg log -r 'wdir()' -T '{node|shortest}\n'
-  ffff
-
-  $ hg log --template '{shortest("f")}\n' -l1
-  f
-
-  $ hg log --template '{shortest("0123456789012345678901234567890123456789")}\n' -l1
-  0123456789012345678901234567890123456789
-
-  $ hg log --template '{shortest("01234567890123456789012345678901234567890123456789")}\n' -l1
-  01234567890123456789012345678901234567890123456789
-
-  $ hg log --template '{shortest("not a hex string")}\n' -l1
-  not a hex string
-
-  $ hg log --template '{shortest("not a hex string, but it'\''s 40 bytes long")}\n' -l1
-  not a hex string, but it's 40 bytes long
-
-  $ hg log --template '{shortest("ffffffffffffffffffffffffffffffffffffffff")}\n' -l1
-  ffff
-
-  $ hg log --template '{shortest("fffffff")}\n' -l1
-  ffff
-
-  $ hg log --template '{shortest("ff")}\n' -l1
-  ffff
-
-  $ cd ..
-
-Test shortest(node) with the repo having short hash collision:
-
-  $ hg init hashcollision
-  $ cd hashcollision
-  $ cat <<EOF >> .hg/hgrc
-  > [experimental]
-  > evolution.createmarkers=True
-  > EOF
-  $ echo 0 > a
-  $ hg ci -qAm 0
-  $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do
-  >   hg up -q 0
-  >   echo $i > a
-  >   hg ci -qm $i
-  > done
-  $ hg up -q null
-  $ hg log -r0: -T '{rev}:{node}\n'
-  0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
-  1:11424df6dc1dd4ea255eae2b58eaca7831973bbc
-  2:11407b3f1b9c3e76a79c1ec5373924df096f0499
-  3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4
-  4:10776689e627b465361ad5c296a20a487e153ca4
-  5:a00be79088084cb3aff086ab799f8790e01a976b
-  6:a0b0acd79b4498d0052993d35a6a748dd51d13e6
-  7:a0457b3450b8e1b778f1163b31a435802987fe5d
-  8:c56256a09cd28e5764f32e8e2810d0f01e2e357a
-  9:c5623987d205cd6d9d8389bfc40fff9dbb670b48
-  10:c562ddd9c94164376c20b86b0b4991636a3bf84f
-  $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b
-  obsoleted 1 changesets
-  $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48
-  obsoleted 1 changesets
-  $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f
-  obsoleted 1 changesets
-
- nodes starting with '11' (we don't have the revision number '11' though)
-
-  $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n'
-  1:1142
-  2:1140
-  3:11d
-
- '5:a00' is hidden, but still we have two nodes starting with 'a0'
-
-  $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n'
-  6:a0b
-  7:a04
-
- node '10' conflicts with the revision number '10' even if it is hidden
- (we could exclude hidden revision numbers, but currently we don't)
-
-  $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n'
-  4:107
-  $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
-  4:107
-
- node 'c562' should be unique if the other 'c562' nodes are hidden
- (but we don't try the slow path to filter out hidden nodes for now)
-
-  $ hg log -r 8 -T '{rev}:{node|shortest}\n'
-  8:c5625
-  $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden
-  8:c5625
-  9:c5623
-  10:c562d
-
-  $ cd ..
-
-Test pad function
-
-  $ cd r
-
-  $ hg log --template '{pad(rev, 20)} {author|user}\n'
-  2                    test
-  1                    {node|short}
-  0                    test
-
-  $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
-                     2 test
-                     1 {node|short}
-                     0 test
-
-  $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
-  2------------------- test
-  1------------------- {node|short}
-  0------------------- test
-
-Test template string in pad function
-
-  $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
-  {0}        test
-
-  $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
-  \{rev}     test
-
-Test width argument passed to pad function
-
-  $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
-  0          test
-  $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
-  hg: parse error: pad() expects an integer width
-  [255]
-
-Test invalid fillchar passed to pad function
-
-  $ hg log -r 0 -T '{pad(rev, 10, "")}\n'
-  hg: parse error: pad() expects a single fill character
-  [255]
-  $ hg log -r 0 -T '{pad(rev, 10, "--")}\n'
-  hg: parse error: pad() expects a single fill character
-  [255]
-
-Test boolean argument passed to pad function
-
- no crash
-
-  $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
-  ---------0
-
- string/literal
-
-  $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
-  ---------0
-  $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
-  0---------
-  $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
-  0---------
-
- unknown keyword is evaluated to ''
-
-  $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
-  0---------
-
-Test separate function
-
-  $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
-  a-b-c
-  $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n'
-  0:f7769ec2ab97 test default
-  $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
-  a \x1b[0;31mb\x1b[0m c d (esc)
-
-Test boolean expression/literal passed to if function
-
-  $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
-  rev 0 is True
-  $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
-  literal 0 is True as well
-  $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
-  empty string is False
-  $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
-  empty list is False
-  $ hg log -r 0 -T '{if(revset(r"0"), "non-empty list is True")}\n'
-  non-empty list is True
-  $ hg log -r 0 -T '{if(revset(r"0") % "", "list of empty strings is True")}\n'
-  list of empty strings is True
-  $ hg log -r 0 -T '{if(true, "true is True")}\n'
-  true is True
-  $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
-  false is False
-  $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
-  non-empty string is True
-
-Test ifcontains function
-
-  $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
-  2 is in the string
-  1 is not
-  0 is in the string
-
-  $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
-  2 is in the string
-  1 is not
-  0 is in the string
-
-  $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
-  2 did not add a
-  1 did not add a
-  0 added a
-
-  $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
-  2 is parent of 1
-  1
-  0
-
-  $ hg log -l1 -T '{ifcontains("branch", extras, "t", "f")}\n'
-  t
-  $ hg log -l1 -T '{ifcontains("branch", extras % "{key}", "t", "f")}\n'
-  t
-  $ hg log -l1 -T '{ifcontains("branc", extras % "{key}", "t", "f")}\n'
-  f
-  $ hg log -l1 -T '{ifcontains("branc", stringify(extras % "{key}"), "t", "f")}\n'
-  t
-
-Test revset function
-
-  $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
-  2 current rev
-  1 not current rev
-  0 not current rev
-
-  $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
-  2 match rev
-  1 match rev
-  0 not match rev
-
-  $ hg log -T '{ifcontains(desc, revset(":"), "", "type not match")}\n' -l1
-  type not match
-
-  $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
-  2 Parents: 1
-  1 Parents: 0
-  0 Parents: 
-
-  $ cat >> .hg/hgrc <<EOF
-  > [revsetalias]
-  > myparents(\$1) = parents(\$1)
-  > EOF
-  $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
-  2 Parents: 1
-  1 Parents: 0
-  0 Parents: 
-
-  $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
-  Rev: 2
-  Ancestor: 0
-  Ancestor: 1
-  Ancestor: 2
-  
-  Rev: 1
-  Ancestor: 0
-  Ancestor: 1
-  
-  Rev: 0
-  Ancestor: 0
-  
-  $ hg log --template '{revset("TIP"|lower)}\n' -l1
-  2
-
-  $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
-  2
-
- a list template is evaluated for each item of revset/parents
-
-  $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
-  2 p: 1:bcc7ff960b8e
-  1 p: 0:f7769ec2ab97
-  0 p: 
-
-  $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
-  2 p: 1:bcc7ff960b8e -1:000000000000
-  1 p: 0:f7769ec2ab97 -1:000000000000
-  0 p: -1:000000000000 -1:000000000000
-
- therefore, 'revcache' should be recreated for each rev
-
-  $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
-  2 aa b
-  p 
-  1 
-  p a
-  0 a
-  p 
-
-  $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
-  2 aa b
-  p 
-  1 
-  p a
-  0 a
-  p 
-
-a revset item must be evaluated as an integer revision, not an offset from tip
-
-  $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
-  -1:000000000000
-  $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
-  -1:000000000000
-
-join() should pick '{rev}' from revset items:
-
-  $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6
-  4, 5
-
-on the other hand, parents are formatted as '{rev}:{node|formatnode}' by
-default. join() should agree with the default formatting:
-
-  $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6
-  5:13207e5a10d9, 4:bbe44766e73d
-
-  $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug
-  5:13207e5a10d9fd28ec424934298e176197f2c67f,
-  4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
-
-Invalid arguments passed to revset()
-
-  $ hg log -T '{revset("%whatever", 0)}\n'
-  hg: parse error: unexpected revspec format character w
-  [255]
-  $ hg log -T '{revset("%lwhatever", files)}\n'
-  hg: parse error: unexpected revspec format character w
-  [255]
-  $ hg log -T '{revset("%s %s", 0)}\n'
-  hg: parse error: missing argument for revspec
-  [255]
-  $ hg log -T '{revset("", 0)}\n'
-  hg: parse error: too many revspec arguments specified
-  [255]
-  $ hg log -T '{revset("%s", 0, 1)}\n'
-  hg: parse error: too many revspec arguments specified
-  [255]
-  $ hg log -T '{revset("%", 0)}\n'
-  hg: parse error: incomplete revspec format character
-  [255]
-  $ hg log -T '{revset("%l", 0)}\n'
-  hg: parse error: incomplete revspec format character
-  [255]
-  $ hg log -T '{revset("%d", 'foo')}\n'
-  hg: parse error: invalid argument for revspec
-  [255]
-  $ hg log -T '{revset("%ld", files)}\n'
-  hg: parse error: invalid argument for revspec
-  [255]
-  $ hg log -T '{revset("%ls", 0)}\n'
-  hg: parse error: invalid argument for revspec
-  [255]
-  $ hg log -T '{revset("%b", 'foo')}\n'
-  hg: parse error: invalid argument for revspec
-  [255]
-  $ hg log -T '{revset("%lb", files)}\n'
-  hg: parse error: invalid argument for revspec
-  [255]
-  $ hg log -T '{revset("%r", 0)}\n'
-  hg: parse error: invalid argument for revspec
-  [255]
 
 Test 'originalnode'
 
@@ -2734,41 +1760,6 @@
   $ hg log -r 0 -T '{manifest % "{node} {originalnode}"}\n'
   a0c8bcbbb45c63b90b70ad007bf38961f64f2af0 f7769ec2ab975ad19684098ad1ffd9b81ecc71a1
 
-Test files function
-
-  $ hg log -T "{rev}\n{join(files('*'), '\n')}\n"
-  2
-  a
-  aa
-  b
-  1
-  a
-  0
-  a
-
-  $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n"
-  2
-  aa
-  1
-  
-  0
-  
-  $ hg rm a
-  $ hg log -r "wdir()" -T "{rev}\n{join(files('*'), '\n')}\n"
-  2147483647
-  aa
-  b
-  $ hg revert a
-
-Test relpath function
-
-  $ hg log -r0 -T '{files % "{file|relpath}\n"}'
-  a
-  $ cd ..
-  $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}'
-  r/a
-  $ cd r
-
 Test active bookmark templating
 
   $ hg book foo
@@ -2848,75 +1839,7 @@
   bar
   foo
 
-Test stringify on sub expressions
-
   $ cd ..
-  $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
-  fourth, second, third
-  $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
-  abc
-
-Test splitlines
-
-  $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
-  @  foo Modify, add, remove, rename
-  |
-  o  foo future
-  |
-  o  foo third
-  |
-  o  foo second
-  
-  o    foo merge
-  |\
-  | o  foo new head
-  | |
-  o |  foo new branch
-  |/
-  o  foo no user, no domain
-  |
-  o  foo no person
-  |
-  o  foo other 1
-  |  foo other 2
-  |  foo
-  |  foo other 3
-  o  foo line 1
-     foo line 2
-
-  $ hg log -R a -r0 -T '{desc|splitlines}\n'
-  line 1 line 2
-  $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n'
-  line 1|line 2
-
-Test startswith
-  $ hg log -Gv -R a --template "{startswith(desc)}"
-  hg: parse error: startswith expects two arguments
-  [255]
-
-  $ hg log -Gv -R a --template "{startswith('line', desc)}"
-  @
-  |
-  o
-  |
-  o
-  |
-  o
-  
-  o
-  |\
-  | o
-  | |
-  o |
-  |/
-  o
-  |
-  o
-  |
-  o
-  |
-  o  line 1
-     line 2
 
 Test bad template with better error message
 
@@ -2924,102 +1847,6 @@
   hg: parse error: expected a symbol, got 'func'
   [255]
 
-Test word function (including index out of bounds graceful failure)
-
-  $ hg log -Gv -R a --template "{word('1', desc)}"
-  @  add,
-  |
-  o
-  |
-  o
-  |
-  o
-  
-  o
-  |\
-  | o  head
-  | |
-  o |  branch
-  |/
-  o  user,
-  |
-  o  person
-  |
-  o  1
-  |
-  o  1
-  
-
-Test word third parameter used as splitter
-
-  $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
-  @  M
-  |
-  o  future
-  |
-  o  third
-  |
-  o  sec
-  
-  o    merge
-  |\
-  | o  new head
-  | |
-  o |  new branch
-  |/
-  o  n
-  |
-  o  n
-  |
-  o
-  |
-  o  line 1
-     line 2
-
-Test word error messages for not enough and too many arguments
-
-  $ hg log -Gv -R a --template "{word('0')}"
-  hg: parse error: word expects two or three arguments, got 1
-  [255]
-
-  $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
-  hg: parse error: word expects two or three arguments, got 7
-  [255]
-
-Test word for integer literal
-
-  $ hg log -R a --template "{word(2, desc)}\n" -r0
-  line
-
-Test word for invalid numbers
-
-  $ hg log -Gv -R a --template "{word('a', desc)}"
-  hg: parse error: word expects an integer index
-  [255]
-
-Test word for out of range
-
-  $ hg log -R a --template "{word(10000, desc)}"
-  $ hg log -R a --template "{word(-10000, desc)}"
-
-Test indent and not adding to empty lines
-
-  $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
-  -----
-   > line 1
-  >> line 2
-  -----
-   > other 1
-  >> other 2
-  
-  >> other 3
-
-Test with non-strings like dates
-
-  $ hg log -T "{indent(date, '   ')}\n" -r 2:3 -R a
-     1200000.00
-     1300000.00
-
 Test broken string escapes:
 
   $ hg log -T "bogus\\" -R a
@@ -3029,11 +1856,6 @@
   hg: parse error: invalid \x escape* (glob)
   [255]
 
-json filter should escape HTML tags so that the output can be embedded in hgweb:
-
-  $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
-  "\u003cfoo@example.org\u003e"
-
 Templater supports aliases of symbol and func() styles:
 
   $ hg clone -q a aliases
@@ -3162,57 +1984,6 @@
 
   $ cd ..
 
-Set up repository for non-ascii encoding tests:
-
-  $ hg init nonascii
-  $ cd nonascii
-  $ $PYTHON <<EOF
-  > open('latin1', 'wb').write(b'\xe9')
-  > open('utf-8', 'wb').write(b'\xc3\xa9')
-  > EOF
-  $ HGENCODING=utf-8 hg branch -q `cat utf-8`
-  $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
-
-json filter should try round-trip conversion to utf-8:
-
-  $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
-  "\u00e9"
-  $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
-  "non-ascii branch: \u00e9"
-
-json filter should take input as utf-8 if it was converted from utf-8:
-
-  $ HGENCODING=latin-1 hg log -T "{branch|json}\n" -r0
-  "\u00e9"
-  $ HGENCODING=latin-1 hg log -T "{desc|json}\n" -r0
-  "non-ascii branch: \u00e9"
-
-json filter takes input as utf-8b:
-
-  $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
-  "\u00e9"
-  $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
-  "\udce9"
-
-utf8 filter:
-
-  $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
-  round-trip: c3a9
-  $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
-  decoded: c3a9
-  $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
-  abort: decoding near * (glob)
-  [255]
-  $ hg log -T "coerced to string: {rev|utf8}\n" -r0
-  coerced to string: 0
-
-pad width:
-
-  $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n"
-  \xc3\xa9- (esc)
-
-  $ cd ..
-
 Test that template function in extension is registered as expected
 
   $ cd a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-template-functions.t	Sat Jun 16 13:17:11 2018 +0900
@@ -0,0 +1,1350 @@
+Test template filters and functions
+===================================
+
+  $ hg init a
+  $ cd a
+  $ echo a > a
+  $ hg add a
+  $ echo line 1 > b
+  $ echo line 2 >> b
+  $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
+
+  $ hg add b
+  $ echo other 1 > c
+  $ echo other 2 >> c
+  $ echo >> c
+  $ echo other 3 >> c
+  $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
+
+  $ hg add c
+  $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
+  $ echo c >> c
+  $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
+
+  $ echo foo > .hg/branch
+  $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
+
+  $ hg co -q 3
+  $ echo other 4 >> d
+  $ hg add d
+  $ hg commit -m 'new head' -d '1500000 0' -u 'person'
+
+  $ hg merge -q foo
+  $ hg commit -m 'merge' -d '1500001 0' -u 'person'
+
+Second branch starting at nullrev:
+
+  $ hg update null
+  0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+  $ echo second > second
+  $ hg add second
+  $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
+  created new head
+
+  $ echo third > third
+  $ hg add third
+  $ hg mv second fourth
+  $ hg commit -m third -d "2020-01-01 10:01"
+
+  $ hg phase -r 5 --public
+  $ hg phase -r 7 --secret --force
+
+Filters work:
+
+  $ hg log --template '{author|domain}\n'
+  
+  hostname
+  
+  
+  
+  
+  place
+  place
+  hostname
+
+  $ hg log --template '{author|person}\n'
+  test
+  User Name
+  person
+  person
+  person
+  person
+  other
+  A. N. Other
+  User Name
+
+  $ hg log --template '{author|user}\n'
+  test
+  user
+  person
+  person
+  person
+  person
+  other
+  other
+  user
+
+  $ hg log --template '{date|date}\n'
+  Wed Jan 01 10:01:00 2020 +0000
+  Mon Jan 12 13:46:40 1970 +0000
+  Sun Jan 18 08:40:01 1970 +0000
+  Sun Jan 18 08:40:00 1970 +0000
+  Sat Jan 17 04:53:20 1970 +0000
+  Fri Jan 16 01:06:40 1970 +0000
+  Wed Jan 14 21:20:00 1970 +0000
+  Tue Jan 13 17:33:20 1970 +0000
+  Mon Jan 12 13:46:40 1970 +0000
+
+  $ hg log --template '{date|isodate}\n'
+  2020-01-01 10:01 +0000
+  1970-01-12 13:46 +0000
+  1970-01-18 08:40 +0000
+  1970-01-18 08:40 +0000
+  1970-01-17 04:53 +0000
+  1970-01-16 01:06 +0000
+  1970-01-14 21:20 +0000
+  1970-01-13 17:33 +0000
+  1970-01-12 13:46 +0000
+
+  $ hg log --template '{date|isodatesec}\n'
+  2020-01-01 10:01:00 +0000
+  1970-01-12 13:46:40 +0000
+  1970-01-18 08:40:01 +0000
+  1970-01-18 08:40:00 +0000
+  1970-01-17 04:53:20 +0000
+  1970-01-16 01:06:40 +0000
+  1970-01-14 21:20:00 +0000
+  1970-01-13 17:33:20 +0000
+  1970-01-12 13:46:40 +0000
+
+  $ hg log --template '{date|rfc822date}\n'
+  Wed, 01 Jan 2020 10:01:00 +0000
+  Mon, 12 Jan 1970 13:46:40 +0000
+  Sun, 18 Jan 1970 08:40:01 +0000
+  Sun, 18 Jan 1970 08:40:00 +0000
+  Sat, 17 Jan 1970 04:53:20 +0000
+  Fri, 16 Jan 1970 01:06:40 +0000
+  Wed, 14 Jan 1970 21:20:00 +0000
+  Tue, 13 Jan 1970 17:33:20 +0000
+  Mon, 12 Jan 1970 13:46:40 +0000
+
+  $ hg log --template '{desc|firstline}\n'
+  third
+  second
+  merge
+  new head
+  new branch
+  no user, no domain
+  no person
+  other 1
+  line 1
+
+  $ hg log --template '{node|short}\n'
+  95c24699272e
+  29114dbae42b
+  d41e714fe50d
+  13207e5a10d9
+  bbe44766e73d
+  10e46f2dcbf4
+  97054abb4ab8
+  b608e9d1a3f0
+  1e4e1b8f71e0
+
+  $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
+  <changeset author="test"/>
+  <changeset author="User Name &lt;user@hostname&gt;"/>
+  <changeset author="person"/>
+  <changeset author="person"/>
+  <changeset author="person"/>
+  <changeset author="person"/>
+  <changeset author="other@place"/>
+  <changeset author="A. N. Other &lt;other@place&gt;"/>
+  <changeset author="User Name &lt;user@hostname&gt;"/>
+
+  $ hg log --template '{rev}: {children}\n'
+  8: 
+  7: 8:95c24699272e
+  6: 
+  5: 6:d41e714fe50d
+  4: 6:d41e714fe50d
+  3: 4:bbe44766e73d 5:13207e5a10d9
+  2: 3:10e46f2dcbf4
+  1: 2:97054abb4ab8
+  0: 1:b608e9d1a3f0
+
+Formatnode filter works:
+
+  $ hg -q log -r 0 --template '{node|formatnode}\n'
+  1e4e1b8f71e0
+
+  $ hg log -r 0 --template '{node|formatnode}\n'
+  1e4e1b8f71e0
+
+  $ hg -v log -r 0 --template '{node|formatnode}\n'
+  1e4e1b8f71e0
+
+  $ hg --debug log -r 0 --template '{node|formatnode}\n'
+  1e4e1b8f71e05681d422154f5421e385fec3454f
+
+Age filter:
+
+  $ hg init unstable-hash
+  $ cd unstable-hash
+  $ hg log --template '{date|age}\n' > /dev/null || exit 1
+
+  >>> from __future__ import absolute_import
+  >>> import datetime
+  >>> fp = open('a', 'wb')
+  >>> n = datetime.datetime.now() + datetime.timedelta(366 * 7)
+  >>> fp.write(b'%d-%d-%d 00:00' % (n.year, n.month, n.day)) and None
+  >>> fp.close()
+  $ hg add a
+  $ hg commit -m future -d "`cat a`"
+
+  $ hg log -l1 --template '{date|age}\n'
+  7 years from now
+
+  $ cd ..
+  $ rm -rf unstable-hash
+
+Filename filters:
+
+  $ hg debugtemplate '{"foo/bar"|basename}|{"foo/"|basename}|{"foo"|basename}|\n'
+  bar||foo|
+  $ hg debugtemplate '{"foo/bar"|dirname}|{"foo/"|dirname}|{"foo"|dirname}|\n'
+  foo|foo||
+  $ hg debugtemplate '{"foo/bar"|stripdir}|{"foo/"|stripdir}|{"foo"|stripdir}|\n'
+  foo|foo|foo|
+
+Add a dummy commit to make up for the instability of the above:
+
+  $ echo a > a
+  $ hg add a
+  $ hg ci -m future
+
+Count filter:
+
+  $ hg log -l1 --template '{node|count} {node|short|count}\n'
+  40 12
+
+  $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
+  0 1 4
+
+  $ hg log -G --template '{rev}: children: {children|count}, \
+  > tags: {tags|count}, file_adds: {file_adds|count}, \
+  > ancestors: {revset("ancestors(%s)", rev)|count}'
+  @  9: children: 0, tags: 1, file_adds: 1, ancestors: 3
+  |
+  o  8: children: 1, tags: 0, file_adds: 2, ancestors: 2
+  |
+  o  7: children: 1, tags: 0, file_adds: 1, ancestors: 1
+  
+  o    6: children: 0, tags: 0, file_adds: 0, ancestors: 7
+  |\
+  | o  5: children: 1, tags: 0, file_adds: 1, ancestors: 5
+  | |
+  o |  4: children: 1, tags: 0, file_adds: 0, ancestors: 5
+  |/
+  o  3: children: 2, tags: 0, file_adds: 0, ancestors: 4
+  |
+  o  2: children: 1, tags: 0, file_adds: 1, ancestors: 3
+  |
+  o  1: children: 1, tags: 0, file_adds: 1, ancestors: 2
+  |
+  o  0: children: 1, tags: 0, file_adds: 1, ancestors: 1
+  
+
+  $ hg log -l1 -T '{termwidth|count}\n'
+  hg: parse error: not countable
+  (template filter 'count' is not compatible with keyword 'termwidth')
+  [255]
+
+Upper/lower filters:
+
+  $ hg log -r0 --template '{branch|upper}\n'
+  DEFAULT
+  $ hg log -r0 --template '{author|lower}\n'
+  user name <user@hostname>
+  $ hg log -r0 --template '{date|upper}\n'
+  1000000.00
+
+Add a commit that does all possible modifications at once
+
+  $ echo modify >> third
+  $ touch b
+  $ hg add b
+  $ hg mv fourth fifth
+  $ hg rm a
+  $ hg ci -m "Modify, add, remove, rename"
+
+Pass generator object created by template function to filter
+
+  $ hg log -l 1 --template '{if(author, author)|user}\n'
+  test
+
+Test diff function:
+
+  $ hg diff -c 8
+  diff -r 29114dbae42b -r 95c24699272e fourth
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/fourth	Wed Jan 01 10:01:00 2020 +0000
+  @@ -0,0 +1,1 @@
+  +second
+  diff -r 29114dbae42b -r 95c24699272e second
+  --- a/second	Mon Jan 12 13:46:40 1970 +0000
+  +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,1 +0,0 @@
+  -second
+  diff -r 29114dbae42b -r 95c24699272e third
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/third	Wed Jan 01 10:01:00 2020 +0000
+  @@ -0,0 +1,1 @@
+  +third
+
+  $ hg log -r 8 -T "{diff()}"
+  diff -r 29114dbae42b -r 95c24699272e fourth
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/fourth	Wed Jan 01 10:01:00 2020 +0000
+  @@ -0,0 +1,1 @@
+  +second
+  diff -r 29114dbae42b -r 95c24699272e second
+  --- a/second	Mon Jan 12 13:46:40 1970 +0000
+  +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,1 +0,0 @@
+  -second
+  diff -r 29114dbae42b -r 95c24699272e third
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/third	Wed Jan 01 10:01:00 2020 +0000
+  @@ -0,0 +1,1 @@
+  +third
+
+  $ hg log -r 8 -T "{diff('glob:f*')}"
+  diff -r 29114dbae42b -r 95c24699272e fourth
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/fourth	Wed Jan 01 10:01:00 2020 +0000
+  @@ -0,0 +1,1 @@
+  +second
+
+  $ hg log -r 8 -T "{diff('', 'glob:f*')}"
+  diff -r 29114dbae42b -r 95c24699272e second
+  --- a/second	Mon Jan 12 13:46:40 1970 +0000
+  +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,1 +0,0 @@
+  -second
+  diff -r 29114dbae42b -r 95c24699272e third
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/third	Wed Jan 01 10:01:00 2020 +0000
+  @@ -0,0 +1,1 @@
+  +third
+
+  $ hg log -r 8 -T "{diff('FOURTH'|lower)}"
+  diff -r 29114dbae42b -r 95c24699272e fourth
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/fourth	Wed Jan 01 10:01:00 2020 +0000
+  @@ -0,0 +1,1 @@
+  +second
+
+  $ cd ..
+
+latesttag() function:
+
+  $ hg init latesttag
+  $ cd latesttag
+
+  $ echo a > file
+  $ hg ci -Am a -d '0 0'
+  adding file
+
+  $ echo b >> file
+  $ hg ci -m b -d '1 0'
+
+  $ echo c >> head1
+  $ hg ci -Am h1c -d '2 0'
+  adding head1
+
+  $ hg update -q 1
+  $ echo d >> head2
+  $ hg ci -Am h2d -d '3 0'
+  adding head2
+  created new head
+
+  $ echo e >> head2
+  $ hg ci -m h2e -d '4 0'
+
+  $ hg merge -q
+  $ hg ci -m merge -d '5 -3600'
+
+  $ hg tag -r 1 -m t1 -d '6 0' t1
+  $ hg tag -r 2 -m t2 -d '7 0' t2
+  $ hg tag -r 3 -m t3 -d '8 0' t3
+  $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter
+  $ hg tag -r 5 -m t5 -d '9 0' t5
+  $ hg tag -r 3 -m at3 -d '10 0' at3
+
+  $ hg log -G --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n"
+  @  11: t3, C: 9, D: 8
+  |
+  o  10: t3, C: 8, D: 7
+  |
+  o  9: t3, C: 7, D: 6
+  |
+  o  8: t3, C: 6, D: 5
+  |
+  o  7: t3, C: 5, D: 4
+  |
+  o  6: t3, C: 4, D: 3
+  |
+  o    5: t3, C: 3, D: 2
+  |\
+  | o  4: t3, C: 1, D: 1
+  | |
+  | o  3: t3, C: 0, D: 0
+  | |
+  o |  2: t1, C: 1, D: 1
+  |/
+  o  1: t1, C: 0, D: 0
+  |
+  o  0: null, C: 1, D: 1
+  
+
+  $ cd ..
+
+Test manifest/get() can be join()-ed as string, though it's silly:
+
+  $ hg log -R latesttag -r tip -T '{join(manifest, ".")}\n'
+  1.1.:.2.b.c.6.e.9.0.0.6.c.e.2
+  $ hg log -R latesttag -r tip -T '{join(get(extras, "branch"), ".")}\n'
+  d.e.f.a.u.l.t
+
+Test join() over string
+
+  $ hg log -R latesttag -r tip -T '{join(rev|stringify, ".")}\n'
+  1.1
+
+Test join() over uniterable
+
+  $ hg log -R latesttag -r tip -T '{join(rev, "")}\n'
+  hg: parse error: 11 is not iterable
+  [255]
+
+Test min/max of integers
+
+  $ hg log -R latesttag -l1 -T '{min(revset("9:10"))}\n'
+  9
+  $ hg log -R latesttag -l1 -T '{max(revset("9:10"))}\n'
+  10
+
+Test min/max over map operation:
+
+  $ hg log -R latesttag -r3 -T '{min(tags % "{tag}")}\n'
+  at3
+  $ hg log -R latesttag -r3 -T '{max(tags % "{tag}")}\n'
+  t3
+
+Test min/max of strings:
+
+  $ hg log -R latesttag -l1 -T '{min(desc)}\n'
+  3
+  $ hg log -R latesttag -l1 -T '{max(desc)}\n'
+  t
+
+Test min/max of non-iterable:
+
+  $ hg debugtemplate '{min(1)}'
+  hg: parse error: 1 is not iterable
+  (min first argument should be an iterable)
+  [255]
+  $ hg debugtemplate '{max(2)}'
+  hg: parse error: 2 is not iterable
+  (max first argument should be an iterable)
+  [255]
+
+  $ hg log -R latesttag -l1 -T '{min(date)}'
+  hg: parse error: date is not iterable
+  (min first argument should be an iterable)
+  [255]
+  $ hg log -R latesttag -l1 -T '{max(date)}'
+  hg: parse error: date is not iterable
+  (max first argument should be an iterable)
+  [255]
+
+Test min/max of empty sequence:
+
+  $ hg debugtemplate '{min("")}'
+  hg: parse error: empty string
+  (min first argument should be an iterable)
+  [255]
+  $ hg debugtemplate '{max("")}'
+  hg: parse error: empty string
+  (max first argument should be an iterable)
+  [255]
+  $ hg debugtemplate '{min(dict())}'
+  hg: parse error: empty sequence
+  (min first argument should be an iterable)
+  [255]
+  $ hg debugtemplate '{max(dict())}'
+  hg: parse error: empty sequence
+  (max first argument should be an iterable)
+  [255]
+  $ hg debugtemplate '{min(dict() % "")}'
+  hg: parse error: empty sequence
+  (min first argument should be an iterable)
+  [255]
+  $ hg debugtemplate '{max(dict() % "")}'
+  hg: parse error: empty sequence
+  (max first argument should be an iterable)
+  [255]
+
+Test min/max of if() result
+
+  $ cd latesttag
+  $ hg log -l1 -T '{min(if(true, revset("9:10"), ""))}\n'
+  9
+  $ hg log -l1 -T '{max(if(false, "", revset("9:10")))}\n'
+  10
+  $ hg log -l1 -T '{min(ifcontains("a", "aa", revset("9:10"), ""))}\n'
+  9
+  $ hg log -l1 -T '{max(ifcontains("a", "bb", "", revset("9:10")))}\n'
+  10
+  $ hg log -l1 -T '{min(ifeq(0, 0, revset("9:10"), ""))}\n'
+  9
+  $ hg log -l1 -T '{max(ifeq(0, 1, "", revset("9:10")))}\n'
+  10
+  $ cd ..
+
+Test laziness of if() then/else clause
+
+  $ hg debugtemplate '{count(0)}'
+  hg: parse error: not countable
+  (incompatible use of template filter 'count')
+  [255]
+  $ hg debugtemplate '{if(true, "", count(0))}'
+  $ hg debugtemplate '{if(false, count(0), "")}'
+  $ hg debugtemplate '{ifcontains("a", "aa", "", count(0))}'
+  $ hg debugtemplate '{ifcontains("a", "bb", count(0), "")}'
+  $ hg debugtemplate '{ifeq(0, 0, "", count(0))}'
+  $ hg debugtemplate '{ifeq(0, 1, count(0), "")}'
+
+Test the sub function of templating for expansion:
+
+  $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
+  xx
+
+  $ hg log -R latesttag -r 10 -T '{sub("[", "x", rev)}\n'
+  hg: parse error: sub got an invalid pattern: [
+  [255]
+  $ hg log -R latesttag -r 10 -T '{sub("[0-9]", r"\1", rev)}\n'
+  hg: parse error: sub got an invalid replacement: \1
+  [255]
+
+Test the strip function with chars specified:
+
+  $ hg log -R latesttag --template '{desc}\n'
+  at3
+  t5
+  t4
+  t3
+  t2
+  t1
+  merge
+  h2e
+  h2d
+  h1c
+  b
+  a
+
+  $ hg log -R latesttag --template '{strip(desc, "te")}\n'
+  at3
+  5
+  4
+  3
+  2
+  1
+  merg
+  h2
+  h2d
+  h1c
+  b
+  a
+
+Test date format:
+
+  $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
+  date: 70 01 01 10 +0000
+  date: 70 01 01 09 +0000
+  date: 70 01 01 04 +0000
+  date: 70 01 01 08 +0000
+  date: 70 01 01 07 +0000
+  date: 70 01 01 06 +0000
+  date: 70 01 01 05 +0100
+  date: 70 01 01 04 +0000
+  date: 70 01 01 03 +0000
+  date: 70 01 01 02 +0000
+  date: 70 01 01 01 +0000
+  date: 70 01 01 00 +0000
+
+Test invalid date:
+
+  $ hg log -R latesttag -T '{date(rev)}\n'
+  hg: parse error: date expects a date information
+  [255]
+
+Set up repository containing template fragments in commit metadata:
+
+  $ hg init r
+  $ cd r
+  $ echo a > a
+  $ hg ci -Am '{rev}'
+  adding a
+
+  $ hg branch -q 'text.{rev}'
+  $ echo aa >> aa
+  $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
+
+color effect can be specified without quoting:
+
+  $ hg log --color=always -l 1 --template '{label(red, "text\n")}'
+  \x1b[0;31mtext\x1b[0m (esc)
+
+color effects can be nested (issue5413)
+
+  $ hg debugtemplate --color=always \
+  > '{label(red, "red{label(magenta, "ma{label(cyan, "cyan")}{label(yellow, "yellow")}genta")}")}\n'
+  \x1b[0;31mred\x1b[0;35mma\x1b[0;36mcyan\x1b[0m\x1b[0;31m\x1b[0;35m\x1b[0;33myellow\x1b[0m\x1b[0;31m\x1b[0;35mgenta\x1b[0m (esc)
+
+pad() should interact well with color codes (issue5416)
+
+  $ hg debugtemplate --color=always \
+  > '{pad(label(red, "red"), 5, label(cyan, "-"))}\n'
+  \x1b[0;31mred\x1b[0m\x1b[0;36m-\x1b[0m\x1b[0;36m-\x1b[0m (esc)
+
+label should be no-op if color is disabled:
+
+  $ hg log --color=never -l 1 --template '{label(red, "text\n")}'
+  text
+  $ hg log --config extensions.color=! -l 1 --template '{label(red, "text\n")}'
+  text
+
+Test branches inside if statement:
+
+  $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
+  no
+
+Test dict constructor:
+
+  $ hg log -r 0 -T '{dict(y=node|short, x=rev)}\n'
+  y=f7769ec2ab97 x=0
+  $ hg log -r 0 -T '{dict(x=rev, y=node|short) % "{key}={value}\n"}'
+  x=0
+  y=f7769ec2ab97
+  $ hg log -r 0 -T '{dict(x=rev, y=node|short)|json}\n'
+  {"x": 0, "y": "f7769ec2ab97"}
+  $ hg log -r 0 -T '{dict()|json}\n'
+  {}
+
+  $ hg log -r 0 -T '{dict(rev, node=node|short)}\n'
+  rev=0 node=f7769ec2ab97
+  $ hg log -r 0 -T '{dict(rev, node|short)}\n'
+  rev=0 node=f7769ec2ab97
+
+  $ hg log -r 0 -T '{dict(rev, rev=rev)}\n'
+  hg: parse error: duplicated dict key 'rev' inferred
+  [255]
+  $ hg log -r 0 -T '{dict(node, node|short)}\n'
+  hg: parse error: duplicated dict key 'node' inferred
+  [255]
+  $ hg log -r 0 -T '{dict(1 + 2)}'
+  hg: parse error: dict key cannot be inferred
+  [255]
+
+  $ hg log -r 0 -T '{dict(x=rev, x=node)}'
+  hg: parse error: dict got multiple values for keyword argument 'x'
+  [255]
+
+Test get function:
+
+  $ hg log -r 0 --template '{get(extras, "branch")}\n'
+  default
+  $ hg log -r 0 --template '{get(extras, "br{"anch"}")}\n'
+  default
+  $ hg log -r 0 --template '{get(files, "should_fail")}\n'
+  hg: parse error: not a dictionary
+  (get() expects a dict as first argument)
+  [255]
+
+Test json filter applied to wrapped object:
+
+  $ hg log -r0 -T '{files|json}\n'
+  ["a"]
+  $ hg log -r0 -T '{extras|json}\n'
+  {"branch": "default"}
+  $ hg log -r0 -T '{date|json}\n'
+  [0, 0]
+
+Test json filter applied to map result:
+
+  $ hg log -r0 -T '{json(extras % "{key}")}\n'
+  ["branch"]
+
+Test localdate(date, tz) function:
+
+  $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n'
+  1970-01-01 09:00 +0900
+  $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n'
+  1970-01-01 00:00 +0000
+  $ TZ=JST-09 hg log -r0 -T '{localdate(date, "blahUTC")|isodate}\n'
+  hg: parse error: localdate expects a timezone
+  [255]
+  $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n'
+  1970-01-01 02:00 +0200
+  $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n'
+  1970-01-01 00:00 +0000
+  $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n'
+  1970-01-01 00:00 +0000
+  $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n'
+  hg: parse error: localdate expects a timezone
+  [255]
+  $ hg log -r0 -T '{localdate(date, date)|isodate}\n'
+  hg: parse error: localdate expects a timezone
+  [255]
+
+Test shortest(node) function:
+
+  $ echo b > b
+  $ hg ci -qAm b
+  $ hg log --template '{shortest(node)}\n'
+  e777
+  bcc7
+  f776
+  $ hg log --template '{shortest(node, 10)}\n'
+  e777603221
+  bcc7ff960b
+  f7769ec2ab
+  $ hg log --template '{node|shortest}\n' -l1
+  e777
+
+  $ hg log -r 0 -T '{shortest(node, "1{"0"}")}\n'
+  f7769ec2ab
+  $ hg log -r 0 -T '{shortest(node, "not an int")}\n'
+  hg: parse error: shortest() expects an integer minlength
+  [255]
+
+  $ hg log -r 'wdir()' -T '{node|shortest}\n'
+  ffff
+
+  $ hg log --template '{shortest("f")}\n' -l1
+  f
+
+  $ hg log --template '{shortest("0123456789012345678901234567890123456789")}\n' -l1
+  0123456789012345678901234567890123456789
+
+  $ hg log --template '{shortest("01234567890123456789012345678901234567890123456789")}\n' -l1
+  01234567890123456789012345678901234567890123456789
+
+  $ hg log --template '{shortest("not a hex string")}\n' -l1
+  not a hex string
+
+  $ hg log --template '{shortest("not a hex string, but it'\''s 40 bytes long")}\n' -l1
+  not a hex string, but it's 40 bytes long
+
+  $ hg log --template '{shortest("ffffffffffffffffffffffffffffffffffffffff")}\n' -l1
+  ffff
+
+  $ hg log --template '{shortest("fffffff")}\n' -l1
+  ffff
+
+  $ hg log --template '{shortest("ff")}\n' -l1
+  ffff
+
+  $ cd ..
+
+Test shortest(node) with the repo having short hash collision:
+
+  $ hg init hashcollision
+  $ cd hashcollision
+  $ cat <<EOF >> .hg/hgrc
+  > [experimental]
+  > evolution.createmarkers=True
+  > EOF
+  $ echo 0 > a
+  $ hg ci -qAm 0
+  $ for i in 17 129 248 242 480 580 617 1057 2857 4025; do
+  >   hg up -q 0
+  >   echo $i > a
+  >   hg ci -qm $i
+  > done
+  $ hg up -q null
+  $ hg log -r0: -T '{rev}:{node}\n'
+  0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
+  1:11424df6dc1dd4ea255eae2b58eaca7831973bbc
+  2:11407b3f1b9c3e76a79c1ec5373924df096f0499
+  3:11dd92fe0f39dfdaacdaa5f3997edc533875cfc4
+  4:10776689e627b465361ad5c296a20a487e153ca4
+  5:a00be79088084cb3aff086ab799f8790e01a976b
+  6:a0b0acd79b4498d0052993d35a6a748dd51d13e6
+  7:a0457b3450b8e1b778f1163b31a435802987fe5d
+  8:c56256a09cd28e5764f32e8e2810d0f01e2e357a
+  9:c5623987d205cd6d9d8389bfc40fff9dbb670b48
+  10:c562ddd9c94164376c20b86b0b4991636a3bf84f
+  $ hg debugobsolete a00be79088084cb3aff086ab799f8790e01a976b
+  obsoleted 1 changesets
+  $ hg debugobsolete c5623987d205cd6d9d8389bfc40fff9dbb670b48
+  obsoleted 1 changesets
+  $ hg debugobsolete c562ddd9c94164376c20b86b0b4991636a3bf84f
+  obsoleted 1 changesets
+
+ nodes starting with '11' (we don't have the revision number '11' though)
+
+  $ hg log -r 1:3 -T '{rev}:{shortest(node, 0)}\n'
+  1:1142
+  2:1140
+  3:11d
+
+ '5:a00' is hidden, but still we have two nodes starting with 'a0'
+
+  $ hg log -r 6:7 -T '{rev}:{shortest(node, 0)}\n'
+  6:a0b
+  7:a04
+
+ node '10' conflicts with the revision number '10' even if it is hidden
+ (we could exclude hidden revision numbers, but currently we don't)
+
+  $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n'
+  4:107
+  $ hg log -r 4 -T '{rev}:{shortest(node, 0)}\n' --hidden
+  4:107
+
+ node 'c562' should be unique if the other 'c562' nodes are hidden
+ (but we don't try the slow path to filter out hidden nodes for now)
+
+  $ hg log -r 8 -T '{rev}:{node|shortest}\n'
+  8:c5625
+  $ hg log -r 8:10 -T '{rev}:{node|shortest}\n' --hidden
+  8:c5625
+  9:c5623
+  10:c562d
+
+  $ cd ..
+
+Test pad function
+
+  $ cd r
+
+  $ hg log --template '{pad(rev, 20)} {author|user}\n'
+  2                    test
+  1                    {node|short}
+  0                    test
+
+  $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
+                     2 test
+                     1 {node|short}
+                     0 test
+
+  $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
+  2------------------- test
+  1------------------- {node|short}
+  0------------------- test
+
+Test template string in pad function
+
+  $ hg log -r 0 -T '{pad("\{{rev}}", 10)} {author|user}\n'
+  {0}        test
+
+  $ hg log -r 0 -T '{pad(r"\{rev}", 10)} {author|user}\n'
+  \{rev}     test
+
+Test width argument passed to pad function
+
+  $ hg log -r 0 -T '{pad(rev, "1{"0"}")} {author|user}\n'
+  0          test
+  $ hg log -r 0 -T '{pad(rev, "not an int")}\n'
+  hg: parse error: pad() expects an integer width
+  [255]
+
+Test invalid fillchar passed to pad function
+
+  $ hg log -r 0 -T '{pad(rev, 10, "")}\n'
+  hg: parse error: pad() expects a single fill character
+  [255]
+  $ hg log -r 0 -T '{pad(rev, 10, "--")}\n'
+  hg: parse error: pad() expects a single fill character
+  [255]
+
+Test boolean argument passed to pad function
+
+ no crash
+
+  $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
+  ---------0
+
+ string/literal
+
+  $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
+  ---------0
+  $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
+  0---------
+  $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
+  0---------
+
+ unknown keyword is evaluated to ''
+
+  $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
+  0---------
+
+Test separate function
+
+  $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
+  a-b-c
+  $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n'
+  0:f7769ec2ab97 test default
+  $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
+  a \x1b[0;31mb\x1b[0m c d (esc)
+
+Test boolean expression/literal passed to if function
+
+  $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
+  rev 0 is True
+  $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
+  literal 0 is True as well
+  $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
+  empty string is False
+  $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
+  empty list is False
+  $ hg log -r 0 -T '{if(revset(r"0"), "non-empty list is True")}\n'
+  non-empty list is True
+  $ hg log -r 0 -T '{if(revset(r"0") % "", "list of empty strings is True")}\n'
+  list of empty strings is True
+  $ hg log -r 0 -T '{if(true, "true is True")}\n'
+  true is True
+  $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
+  false is False
+  $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
+  non-empty string is True
+
+Test ifcontains function
+
+  $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
+  2 is in the string
+  1 is not
+  0 is in the string
+
+  $ hg log -T '{rev} {ifcontains(rev, "2 two{" 0"}", "is in the string", "is not")}\n'
+  2 is in the string
+  1 is not
+  0 is in the string
+
+  $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
+  2 did not add a
+  1 did not add a
+  0 added a
+
+  $ hg log --debug -T '{rev}{ifcontains(1, parents, " is parent of 1")}\n'
+  2 is parent of 1
+  1
+  0
+
+  $ hg log -l1 -T '{ifcontains("branch", extras, "t", "f")}\n'
+  t
+  $ hg log -l1 -T '{ifcontains("branch", extras % "{key}", "t", "f")}\n'
+  t
+  $ hg log -l1 -T '{ifcontains("branc", extras % "{key}", "t", "f")}\n'
+  f
+  $ hg log -l1 -T '{ifcontains("branc", stringify(extras % "{key}"), "t", "f")}\n'
+  t
+
+Test revset function
+
+  $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
+  2 current rev
+  1 not current rev
+  0 not current rev
+
+  $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
+  2 match rev
+  1 match rev
+  0 not match rev
+
+  $ hg log -T '{ifcontains(desc, revset(":"), "", "type not match")}\n' -l1
+  type not match
+
+  $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
+  2 Parents: 1
+  1 Parents: 0
+  0 Parents: 
+
+  $ cat >> .hg/hgrc <<EOF
+  > [revsetalias]
+  > myparents(\$1) = parents(\$1)
+  > EOF
+  $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
+  2 Parents: 1
+  1 Parents: 0
+  0 Parents: 
+
+  $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
+  Rev: 2
+  Ancestor: 0
+  Ancestor: 1
+  Ancestor: 2
+  
+  Rev: 1
+  Ancestor: 0
+  Ancestor: 1
+  
+  Rev: 0
+  Ancestor: 0
+  
+  $ hg log --template '{revset("TIP"|lower)}\n' -l1
+  2
+
+  $ hg log -T '{revset("%s", "t{"ip"}")}\n' -l1
+  2
+
+ a list template is evaluated for each item of revset/parents
+
+  $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n'
+  2 p: 1:bcc7ff960b8e
+  1 p: 0:f7769ec2ab97
+  0 p: 
+
+  $ hg log --debug -T '{rev} p:{parents % " {rev}:{node|short}"}\n'
+  2 p: 1:bcc7ff960b8e -1:000000000000
+  1 p: 0:f7769ec2ab97 -1:000000000000
+  0 p: -1:000000000000 -1:000000000000
+
+ therefore, 'revcache' should be recreated for each rev
+
+  $ hg log -T '{rev} {file_adds}\np {revset("p1(%s)", rev) % "{file_adds}"}\n'
+  2 aa b
+  p 
+  1 
+  p a
+  0 a
+  p 
+
+  $ hg log --debug -T '{rev} {file_adds}\np {parents % "{file_adds}"}\n'
+  2 aa b
+  p 
+  1 
+  p a
+  0 a
+  p 
+
+a revset item must be evaluated as an integer revision, not an offset from tip
+
+  $ hg log -l 1 -T '{revset("null") % "{rev}:{node|short}"}\n'
+  -1:000000000000
+  $ hg log -l 1 -T '{revset("%s", "null") % "{rev}:{node|short}"}\n'
+  -1:000000000000
+
+join() should pick '{rev}' from revset items:
+
+  $ hg log -R ../a -T '{join(revset("parents(%d)", rev), ", ")}\n' -r6
+  4, 5
+
+on the other hand, parents are formatted as '{rev}:{node|formatnode}' by
+default. join() should agree with the default formatting:
+
+  $ hg log -R ../a -T '{join(parents, ", ")}\n' -r6
+  5:13207e5a10d9, 4:bbe44766e73d
+
+  $ hg log -R ../a -T '{join(parents, ",\n")}\n' -r6 --debug
+  5:13207e5a10d9fd28ec424934298e176197f2c67f,
+  4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
+
+Invalid arguments passed to revset()
+
+  $ hg log -T '{revset("%whatever", 0)}\n'
+  hg: parse error: unexpected revspec format character w
+  [255]
+  $ hg log -T '{revset("%lwhatever", files)}\n'
+  hg: parse error: unexpected revspec format character w
+  [255]
+  $ hg log -T '{revset("%s %s", 0)}\n'
+  hg: parse error: missing argument for revspec
+  [255]
+  $ hg log -T '{revset("", 0)}\n'
+  hg: parse error: too many revspec arguments specified
+  [255]
+  $ hg log -T '{revset("%s", 0, 1)}\n'
+  hg: parse error: too many revspec arguments specified
+  [255]
+  $ hg log -T '{revset("%", 0)}\n'
+  hg: parse error: incomplete revspec format character
+  [255]
+  $ hg log -T '{revset("%l", 0)}\n'
+  hg: parse error: incomplete revspec format character
+  [255]
+  $ hg log -T '{revset("%d", 'foo')}\n'
+  hg: parse error: invalid argument for revspec
+  [255]
+  $ hg log -T '{revset("%ld", files)}\n'
+  hg: parse error: invalid argument for revspec
+  [255]
+  $ hg log -T '{revset("%ls", 0)}\n'
+  hg: parse error: invalid argument for revspec
+  [255]
+  $ hg log -T '{revset("%b", 'foo')}\n'
+  hg: parse error: invalid argument for revspec
+  [255]
+  $ hg log -T '{revset("%lb", files)}\n'
+  hg: parse error: invalid argument for revspec
+  [255]
+  $ hg log -T '{revset("%r", 0)}\n'
+  hg: parse error: invalid argument for revspec
+  [255]
+
+Test files function
+
+  $ hg log -T "{rev}\n{join(files('*'), '\n')}\n"
+  2
+  a
+  aa
+  b
+  1
+  a
+  0
+  a
+
+  $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n"
+  2
+  aa
+  1
+  
+  0
+  
+  $ hg rm a
+  $ hg log -r "wdir()" -T "{rev}\n{join(files('*'), '\n')}\n"
+  2147483647
+  aa
+  b
+  $ hg revert a
+
+Test relpath function
+
+  $ hg log -r0 -T '{files % "{file|relpath}\n"}'
+  a
+  $ cd ..
+  $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}'
+  r/a
+
+Test stringify on sub expressions
+
+  $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
+  fourth, second, third
+  $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
+  abc
+
+Test splitlines
+
+  $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
+  @  foo Modify, add, remove, rename
+  |
+  o  foo future
+  |
+  o  foo third
+  |
+  o  foo second
+  
+  o    foo merge
+  |\
+  | o  foo new head
+  | |
+  o |  foo new branch
+  |/
+  o  foo no user, no domain
+  |
+  o  foo no person
+  |
+  o  foo other 1
+  |  foo other 2
+  |  foo
+  |  foo other 3
+  o  foo line 1
+     foo line 2
+
+  $ hg log -R a -r0 -T '{desc|splitlines}\n'
+  line 1 line 2
+  $ hg log -R a -r0 -T '{join(desc|splitlines, "|")}\n'
+  line 1|line 2
+
+Test startswith
+  $ hg log -Gv -R a --template "{startswith(desc)}"
+  hg: parse error: startswith expects two arguments
+  [255]
+
+  $ hg log -Gv -R a --template "{startswith('line', desc)}"
+  @
+  |
+  o
+  |
+  o
+  |
+  o
+  
+  o
+  |\
+  | o
+  | |
+  o |
+  |/
+  o
+  |
+  o
+  |
+  o
+  |
+  o  line 1
+     line 2
+
+Test word function (including index out of bounds graceful failure)
+
+  $ hg log -Gv -R a --template "{word('1', desc)}"
+  @  add,
+  |
+  o
+  |
+  o
+  |
+  o
+  
+  o
+  |\
+  | o  head
+  | |
+  o |  branch
+  |/
+  o  user,
+  |
+  o  person
+  |
+  o  1
+  |
+  o  1
+  
+
+Test word third parameter used as splitter
+
+  $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
+  @  M
+  |
+  o  future
+  |
+  o  third
+  |
+  o  sec
+  
+  o    merge
+  |\
+  | o  new head
+  | |
+  o |  new branch
+  |/
+  o  n
+  |
+  o  n
+  |
+  o
+  |
+  o  line 1
+     line 2
+
+Test word error messages for not enough and too many arguments
+
+  $ hg log -Gv -R a --template "{word('0')}"
+  hg: parse error: word expects two or three arguments, got 1
+  [255]
+
+  $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
+  hg: parse error: word expects two or three arguments, got 7
+  [255]
+
+Test word for integer literal
+
+  $ hg log -R a --template "{word(2, desc)}\n" -r0
+  line
+
+Test word for invalid numbers
+
+  $ hg log -Gv -R a --template "{word('a', desc)}"
+  hg: parse error: word expects an integer index
+  [255]
+
+Test word for out of range
+
+  $ hg log -R a --template "{word(10000, desc)}"
+  $ hg log -R a --template "{word(-10000, desc)}"
+
+Test indent and not adding to empty lines
+
+  $ hg log -T "-----\n{indent(desc, '>> ', ' > ')}\n" -r 0:1 -R a
+  -----
+   > line 1
+  >> line 2
+  -----
+   > other 1
+  >> other 2
+  
+  >> other 3
+
+Test with non-strings like dates
+
+  $ hg log -T "{indent(date, '   ')}\n" -r 2:3 -R a
+     1200000.00
+     1300000.00
+
+json filter should escape HTML tags so that the output can be embedded in hgweb:
+
+  $ hg log -T "{'<foo@example.org>'|json}\n" -R a -l1
+  "\u003cfoo@example.org\u003e"
+
+Set up repository for non-ascii encoding tests:
+
+  $ hg init nonascii
+  $ cd nonascii
+  $ $PYTHON <<EOF
+  > open('latin1', 'wb').write(b'\xe9')
+  > open('utf-8', 'wb').write(b'\xc3\xa9')
+  > EOF
+  $ HGENCODING=utf-8 hg branch -q `cat utf-8`
+  $ HGENCODING=utf-8 hg ci -qAm "non-ascii branch: `cat utf-8`" utf-8
+
+json filter should try round-trip conversion to utf-8:
+
+  $ HGENCODING=ascii hg log -T "{branch|json}\n" -r0
+  "\u00e9"
+  $ HGENCODING=ascii hg log -T "{desc|json}\n" -r0
+  "non-ascii branch: \u00e9"
+
+json filter should take input as utf-8 if it was converted from utf-8:
+
+  $ HGENCODING=latin-1 hg log -T "{branch|json}\n" -r0
+  "\u00e9"
+  $ HGENCODING=latin-1 hg log -T "{desc|json}\n" -r0
+  "non-ascii branch: \u00e9"
+
+json filter takes input as utf-8b:
+
+  $ HGENCODING=ascii hg log -T "{'`cat utf-8`'|json}\n" -l1
+  "\u00e9"
+  $ HGENCODING=ascii hg log -T "{'`cat latin1`'|json}\n" -l1
+  "\udce9"
+
+utf8 filter:
+
+  $ HGENCODING=ascii hg log -T "round-trip: {branch|utf8|hex}\n" -r0
+  round-trip: c3a9
+  $ HGENCODING=latin1 hg log -T "decoded: {'`cat latin1`'|utf8|hex}\n" -l1
+  decoded: c3a9
+  $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
+  abort: decoding near * (glob)
+  [255]
+  $ hg log -T "coerced to string: {rev|utf8}\n" -r0
+  coerced to string: 0
+
+pad width:
+
+  $ HGENCODING=utf-8 hg debugtemplate "{pad('`cat utf-8`', 2, '-')}\n"
+  \xc3\xa9- (esc)
+
+  $ cd ..