# Hand crafted tests for GNU M4. -*- Autotest -*-
# Copyright (C) 2001, 2006-2010, 2013 Free Software Foundation, Inc.
# This file is part of GNU M4.
#
# GNU M4 is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNU M4 is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
AT_BANNER([Torturing builtins.])
## -------- ##
## __file__ ##
## -------- ##
AT_SETUP([__@&t@file__])
dnl Unfortunately, AT_DATA does not make it easy to create files without
dnl a trailing newline.
[echo $ECHO_N "__line"__:__"file__$ECHO_C"] > nested
AT_DATA([outer],
[[__file__:__line__
include(`nested')
__file__:__line__
]])
dnl Make sure line numbers are consistent, even if include file does not
dnl end with a newline
AT_CHECK_M4([outer], [0],
[[outer:1
1:nested
outer:3
]])
AT_CLEANUP
## -------- ##
## __line__ ##
## -------- ##
AT_SETUP([__@&t@line__])
dnl Unfortunately, AT_DATA does not make it easy to create files without
dnl a trailing newline.
[echo $ECHO_N "__file"__:__"line__$ECHO_C"] > nested
AT_DATA([outer],
[[__file__:__line__
include(`nested')
__file__:__line__
]])
dnl Make sure line numbers are consistent, even if include file does not
dnl end with a newline
AT_CHECK_M4([outer], [0],
[[outer:1
nested:1
outer:3
]])
AT_CLEANUP
## -------------- ##
## __m4_version__ ##
## -------------- ##
AT_SETUP([__m4_@&t@version__])
AT_DATA([in], [[defn(`__m4_version__')
]])
AT_CHECK_M4([--version], [0], [stdout])
AT_CHECK([[$SED -e 's/.*(GNU M4\(.*\)) \([^ ]*\).*/\2\1/;q' < stdout]],
[0], [stdout])
mv stdout expout
AT_CHECK_M4([in], [0], [expout])
dnl Prove that __m4_version__ is unquoted, by making '.' an active character.
AT_DATA([in], [[changesyntax(`A.')define(`.', `errprint(`hi
')undefine(`.').')dnl
__m4_version__
]])
AT_CHECK_M4([in], [0], [expout], [[hi
]])
AT_CLEANUP
## ------- ##
## builtin ##
## ------- ##
AT_SETUP([builtin])
dnl This was a regression in 1.4.10b.
AT_DATA([in.m4],
[[define(`s', `builtin(`shift', $@)')dnl
define(`loop', `ifelse(`$2', `', `-', `$1$2: $0(`$1', s(s($@)))')')dnl
loop(`1')
loop(`1', `2')
loop(`1', `2', `3')
loop(`1', `2', `3', `4')
loop(`1', `2', `3', `4', `5')
]])
AT_CHECK_M4([in.m4], [0],
[[-
12: -
12: 13: -
12: 13: 14: -
12: 13: 14: 15: -
]])
AT_CLEANUP
## ----------- ##
## changequote ##
## ----------- ##
AT_SETUP([changequote])
AT_DATA([in.m4],
[[define(`aaaaaaaaaaaaaaaaaaaa', `A')define(`q', `"$@"')
changequote(`"', `"')
q(q("aaaaaaaaaaaaaaaaaaaa", "a"))
changequote`'define(`echo', `$@')dnl
changequote(`<<<', `>>')dnl
echo(<<>>>)
]])
AT_CHECK_M4([in.m4], [0], [[
A,a
a<<>
]])
AT_CLEANUP
## ----- ##
## debug ##
## ----- ##
AT_SETUP([debug])
AT_DATA([[debug.m4]],
[[define(`countdown', `$1 ifelse(eval($1 > 0), 1, `countdown(decr($1))', `Liftoff')')
debugmode(`aeqc')
traceon(`countdown')
countdown(2)
]])
AT_DATA([[expout]],
[[
2 1 0 Liftoff
]])
AT_DATA([[experr]],
[[m4trace: -1- countdown ... = `$1 ifelse(eval($1 > 0), 1, `countdown(decr($1))', `Liftoff')'
m4trace: -1- countdown(`2') -> `2 ifelse(eval(2 > 0), 1, `countdown(decr(2))', `Liftoff')'
m4trace: -1- countdown ... = `$1 ifelse(eval($1 > 0), 1, `countdown(decr($1))', `Liftoff')'
m4trace: -1- countdown(`1') -> `1 ifelse(eval(1 > 0), 1, `countdown(decr(1))', `Liftoff')'
m4trace: -1- countdown ... = `$1 ifelse(eval($1 > 0), 1, `countdown(decr($1))', `Liftoff')'
m4trace: -1- countdown(`0') -> `0 ifelse(eval(0 > 0), 1, `countdown(decr(0))', `Liftoff')'
]])
AT_CHECK_M4([debug.m4], 0, expout, experr)
dnl Test a regression introduced 2008-05-08, fixed 2008-07-30.
AT_DATA([debug.m4], [[debugmode(`e')traceon(`ifelse')dnl
define(`e', `ifelse(`$1', `$2', `ifelse(`$1', `$2', `e(shift($@))')', `$2')')
e(`1', `1', `a')
]])
AT_CHECK_M4([debug.m4], [0], [[
a
]], [[m4trace: -1- ifelse -> ifelse(`1', `1', `e(shift(`1',`1',`a'))')
m4trace: -1- ifelse -> e(shift(`1',`1',`a'))
m4trace: -1- ifelse -> a
]])
AT_CLEANUP
## ------ ##
## define ##
## ------ ##
AT_SETUP([define])
AT_DATA([[define.m4]],
[[undefine(`macro')dnl
pushdef(`macro', `base value')dnl
pushdef(`macro', `hello, world')dnl
pushdef(`macro', `top value')dnl
define(`macro', `new value')dnl
macro.
popdef(`macro')dnl
macro.
popdef(`macro')dnl
macro.
]])
AT_CHECK_M4([define.m4], 0,
[[new value.
hello, world.
base value.
]], [[m4:define.m4:1: warning: undefine: undefined macro 'macro'
]])
AT_CHECK_M4([--traditional define.m4], 0,
[[new value.
hello, world.
base value.
]], [[m4:define.m4:1: warning: undefine: undefined macro 'macro'
]])
dnl check regression present 2008-02-22 to 2008-04-30.
AT_DATA([in.m4], [[define(`qq', ``$*;$@'')dnl
define(`foo', qq(`a', `b'))dnl
foo
defn(`foo')
]])
AT_CHECK_M4([in.m4], [0], [[a,b;a,b
a,b;`a',`b'
]])
dnl Check hashing performance.
AT_DATA([in.m4], [[include(`forloop3.m4')dnl
forloop(`i', `1', `10000', `define(`m'i, i)')m10000
forloop(`i', `1', `10000', `undefine(`m'i)')m10000
]])
AT_CHECK_M4([-I "$abs_top_srcdir/doc/examples" in.m4], [0], [[10000
m10000
]])
AT_CLEANUP
## ---- ##
## defn ##
## ---- ##
AT_SETUP([defn])
AT_DATA([[in.m4]],
[[define(`e', `$@')define(`q', ``$@'')define(`u', `$*')
define(`cmp', `ifelse($1, $2, `yes', `no')')define(`d', defn(`defn'))
cmp(`defn(`defn')', `defn(`d')')
cmp(`defn(`defn')', ``'')
cmp(`q(defn(`defn'))', `q(defn(`d'))')
cmp(`q(defn(`defn'))', `q(`')')
cmp(`q(defn(`defn'))', ``'')
cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', defn(`d'))')
cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', `')')
cmp(`q(`1', `2', defn(`defn'))', ```1',`2','')
cmp(`q(`1', `2', defn(`defn'))', ```1',`2',`''')
define(`cat', `$1`'ifelse(`$@%:@', `1', `', `$0(shift($@))')')
cat(`define(`foo',', defn(`divnum'), `)foo')
cat(e(`define(`bar',', defn(`divnum'), `)bar'))
m4wrap(`u('q(`cat(`define(`baz','', defn(`divnum'), ``)baz')')`)
')
]])
AT_CHECK_M4([in.m4], [0], [[
yes
no
yes
no
no
yes
no
no
no
0
0
0
]])
AT_CLEANUP
## ------ ##
## divert ##
## ------ ##
AT_SETUP([divert])
AT_DATA([[divert.m4]],
[[divert(1)Text diverted a first time.
divert(0)undivert(1)dnl
divert(1)Text diverted a second time.
divert(0)undivert(1)dnl
]])
AT_CHECK_M4([divert.m4], 0,
[[Text diverted a first time.
Text diverted a second time.
]])
dnl Test second divert argument, added for m4 2.0
AT_DATA([in.m4], [[define(`echo',`$1')dnl
divert(`-1', `discarded without warning')
divert`'dnl
echo(` world'divert(divnum, `hello'))
]])
AT_CHECK_M4([-s in.m4], [0], [[#line 4 "in.m4"
hello world
]])
dnl Test large diversions, which were broken in m4 1.4.8-1.4.10.
dnl Hopefully $SED doesn't choke on the over-long second line.
AT_CHECK([echo 'divert(1)hi
format(%1000000d, 1)' | $M4 | $SED -n 1p], [0], [[hi
]])
AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(`2')f`'dnl
divert(`1')hello
divert(`3')goodbye
]])
dnl Rather than open-code the 1 megabyte expected output, we reduce the
dnl size of testsuite by constructing it.
AT_DATA([expout], [[
]])
cat expout expout > expout2
cat expout2 expout2 > expout
cat expout expout > expout2
cat expout2 expout2 > expout
cat expout expout > expout2
cat expout2 expout2 > expout
cat expout expout > expout2
cat expout2 expout2 > expout
cat expout expout > expout2
cat expout2 expout2 > expout
cat expout expout > expout2
cat expout2 expout2 > expout
cat expout expout > expout2
cat expout2 expout2 > expout
cat expout expout > expout2
cat expout2 expout2 > expout
cat expout expout > expout2
cat expout2 expout2 > expout
cat expout expout > expout2 # 512 kilobytes
echo hello > expout
cat expout2 expout2 >> expout # 1 megabyte
echo goodbye >> expout
rm expout2
AT_CHECK_M4([in.m4], [0], [expout])
dnl Avoid quadratic copying time when transferring diversions; test
dnl both in-memory and diversions spilled to a file.
AT_DATA([in.m4], [[include(`forloop2.m4')dnl
divert(`1')format(`%10000s', `')dnl
forloop(`i', `1', `10000',
`divert(incr(i))undivert(i)')dnl
divert(`9001')format(`%1000000s', `')dnl
forloop(`i', `9001', `10000',
`divert(incr(i))undivert(i)')dnl
divert(`-1')undivert
]])
AT_CHECK_M4([-I "$abs_top_srcdir/doc/examples" in.m4])
AT_CLEANUP
## --- ##
## dnl ##
## --- ##
AT_SETUP([d@&t@nl])
dnl Unfortunately, AT_DATA does not make it easy to create files without
dnl a trailing newline.
[echo $ECHO_N "__file"__:__"line__ d""nl ignored$ECHO_C"] > nested
AT_DATA([outer],
[[__file__:__line__
include(`nested') still ignored
__file__:__line__
define(`foo', `dnl
__file__:__line__ include(`nested') ignored
dnl')dnl
foo ignored
__file__:__line__
]])
dnl Make sure line numbers are consistent, even if include file does not
dnl end with a newline
AT_CHECK_M4([outer], [0],
[[outer:1
nested:1 outer:3
outer:7 nested:1 outer:8
]])
AT_CLEANUP
## ------- ##
## dumpdef ##
## ------- ##
AT_SETUP([dumpdef])
dnl Make sure that stderr and stdout are properly interleaved when directed
dnl to the same file.
AT_DATA([in], [[1dumpdef(`defn')3
]])
AT_CHECK_M4([in], [0], [[13
]], [[defn:
]])
AT_CHECK_M4([in 2>&1], [0], [[1defn:
3
]])
AT_CLEANUP
## -------- ##
## errprint ##
## -------- ##
AT_SETUP([errprint])
dnl Make sure that stderr and stdout are properly interleaved when directed
dnl to the same file.
AT_DATA([in], [[1errprint(`2')3errprint(`
')
]])
AT_CHECK_M4([in], [0], [[13
]], [[2
]])
AT_CHECK_M4([in 2>&1], [0], [[123
]])
AT_CLEANUP
## ------- ##
## esyscmd ##
## ------- ##
AT_SETUP([esyscmd])
AT_DATA([[esyscmd.m4]],
[[# Cannot use real hostname program because test would fail
define(`hostname', esyscmd(`echo www.gnu.org'))dnl
`hostname = >>'hostname`<<'
define(`hostname',
pushdef(`_tmp', `$1')_tmp(translit(esyscmd(`echo www.gnu.org'), `.', `,'))`'popdef(`_tmp'))dnl
`hostname = >>'hostname`<<'
]])
AT_CHECK_M4([esyscmd.m4], 0,
[[# Cannot use real hostname program because test would fail
hostname = >>www.gnu.org
<<
hostname = >>www<<
]])
dnl Ensure that esyscmd does not inherit any unnecessary fds from trace.
AT_DATA([in.m4], [[esyscmd(`echo hi >&3')ifelse(sysval,
`0', `skipping: sh cannot detect closed fds
m4exit(`77')')dnl
]])
AT_CHECK_M4([3>&-], [0], [], [stderr], [in.m4])
mv stderr experr
AT_CHECK_M4([--debugfile=trace -tdnl 3>&-], [0], [], [experr], [in.m4])
AT_CHECK([cat trace], [0], [[m4trace: -1- dnl -> `'
]])
dnl Ensure that esyscmd does not inherit any unnecessary fds from diversions.
AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(`1')f
world
esyscmd(`echo hi >&3')divert
hello
]])
AT_CHECK_M4([3>&-], [0], [stdout-nolog], [experr], [in.m4])
AT_CHECK([$SED -ne '/./p' stdout], [0], [[hello
world
]])
dnl Ensure that esyscmd does not inherit any unnecessary fds from input files.
AT_DATA([in.m4], [[hello esyscmd(`cat <&3')dnl
dnl this line should not be read by cat
world
]])
AT_CHECK_M4([3>&-], [0], [[hello world
]], [stderr], [in.m4])
mv stderr experr
AT_CHECK_M4([in.m4 3>&-], [0], [[hello world
]], [experr])
AT_CLEANUP
## ------ ##
## ifelse ##
## ------ ##
AT_TEST_M4([ifelse],
dnl ensure that comparisons work regardless of reference chains in the middle
[[define(`e', `$@')define(`long', `01234567890123456789')
dnl in isolation
ifelse(long, `01234567890123456789', `yes', `no')
ifelse(`01234567890123456789', long, `yes', `no')
ifelse(long, `01234567890123456789-', `yes', `no')
ifelse(`01234567890123456789-', long, `yes', `no')
dnl through macro expansion
ifelse(e(long), `01234567890123456789', `yes', `no')
ifelse(`01234567890123456789', e(long), `yes', `no')
ifelse(e(long), `01234567890123456789-', `yes', `no')
ifelse(`01234567890123456789-', e(long), `yes', `no')
dnl concatenate macro expansion with unquoted characters
ifelse(-e(long), `-01234567890123456789', `yes', `no')
ifelse(-`01234567890123456789', -e(long), `yes', `no')
ifelse(-e(long), `-01234567890123456789-', `yes', `no')
ifelse(`-01234567890123456789-', -e(long), `yes', `no')
ifelse(-e(long)-, `-01234567890123456789-', `yes', `no')
ifelse(-`01234567890123456789-', -e(long)-, `yes', `no')
ifelse(-e(long)-, `-01234567890123456789', `yes', `no')
ifelse(`-01234567890123456789', -e(long)-, `yes', `no')
dnl concatenate macro expansion with quoted characters
ifelse(`-'e(long), `-01234567890123456789', `yes', `no')
ifelse(-`01234567890123456789', `-'e(long), `yes', `no')
ifelse(`-'e(long), `-01234567890123456789-', `yes', `no')
ifelse(`-01234567890123456789-', `-'e(long), `yes', `no')
ifelse(`-'e(long)`-', `-01234567890123456789-', `yes', `no')
ifelse(-`01234567890123456789-', `-'e(long)`-', `yes', `no')
ifelse(`-'e(long)`-', `-01234567890123456789', `yes', `no')
ifelse(`-01234567890123456789', `-'e(long)`-', `yes', `no')
]], [[
yes
yes
no
no
yes
yes
no
no
yes
yes
no
no
yes
yes
no
no
yes
yes
no
no
yes
yes
no
no
]])
## ------- ##
## include ##
## ------- ##
AT_SETUP([include])
AT_DATA([[include.m4]],
[[Beginning.
include(`NOFILE')
Intermediate
include(`incl-test.m4')
After
include(`NOFILE')
very late
]])
AT_DATA([[incl-test.m4]],
[[dnl noauto
`include test file.'
define()
]])
AT_DATA([[expout]],
[[Beginning.
Intermediate
include test file.
After
very late
]])
AT_DATA([[experr]],
[[m4:include.m4:2: include: cannot open file 'NOFILE': No such file or directory
m4:include.m4:6: include: cannot open file 'NOFILE': No such file or directory
]])
AT_CHECK_M4([include.m4], 1, expout, experr)
dnl make sure files are handled correctly even via builtin
AT_DATA([foo], [[bar
]])
AT_DATA([in], [[builtin(`include', `foo')dnl
]])
AT_CHECK_M4([in], [0], [[bar
]])
AT_CLEANUP
## ----- ##
## index ##
## ----- ##
AT_SETUP([index])
dnl This used to be quadratic, taking millions of comparisons,
dnl but should now operate in linear time with only several thousand checks.
AT_DATA([in], [M4_ONE_MEG_DEFN[dnl
index(substr(f, `0', `500000')-, substr(f, `0', `100000')-)
]])
AT_CHECK_M4([in], [0], [[400000
]])
dnl This validates that index is 8-bit safe.
AT_DATA([in], [[index(`1«2', `»')
index(`1«2', `«')
index(`1«2', `«1')
index(`1«2', `«2')
]])
AT_CHECK_M4([in], [0], [[-1
1
-1
1
]])
AT_CLEANUP
## ----- ##
## indir ##
## ----- ##
AT_SETUP([indir])
AT_DATA([[indir.m4]],
[[define(`%%$$##', `>>>$0<<< cnt $#')
# indir(`%%$$##', nonsense, nonsense)
indir(`%%$$##', nonsense, nonsense)
# indir(`indir', `%%$$##', nonsense)
indir(`indir', `%%$$##', nonsense)
# indir(`indir', `indir', `indir', `indir', `%%$$##')
indir(`indir', `indir', `indir', `indir', `%%$$##')
]])
AT_DATA([[expout]],
[[
# indir(`%%$$##', nonsense, nonsense)
>>>%%$$##<<< cnt 2
# indir(`indir', `%%$$##', nonsense)
>>>%%$$##<<< cnt 1
# indir(`indir', `indir', `indir', `indir', `%%$$##')
>>>%%$$##<<< cnt 0
]])
AT_CHECK_M4([indir.m4], 0, expout)
AT_CLEANUP
## ------ ##
## m4exit ##
## ------ ##
AT_SETUP([m4exit])
dnl Ensure that spilled diversions are gracefully cleaned up
AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(`1')f
m4exit
]])
AT_CHECK([rm -Rf tmpdir && mkdir tmpdir && test -d tmpdir])
TMPDIR=tmpdir
export TMPDIR
AT_CHECK_M4([in.m4], [0])
AT_CHECK([rmdir tmpdir])
AT_CLEANUP
## ------- ##
## mkdtemp ##
## ------- ##
AT_SETUP([mkdtemp])
dnl Check that on error, the expansion is void
AT_DATA([[in]],
[[mkdtemp(`no_such_dir/m4-fooXXXXXX')
]])
AT_CHECK_M4([in], [0], [[
]], [[m4:in:1: warning: mkdtemp: cannot create directory from template 'no_such_dir/m4-fooXXXXXX': No such file or directory
]])
dnl Check that umask has an effect. drws--S--T is okay.
AT_DATA([[in]],
[[translit(substr(esyscmd(`ls -ld 'mkdtemp(`m4-fooXXXXXX')), `0', `10'),
`SsT', `-x-')
]])
AT_CHECK([$M4 < in], [0], [[drwx------
]])
AT_CHECK([umask 700; $M4 < in], [0], [[d---------
]])
AT_CLEANUP
## -------- ##
## maketemp ##
## -------- ##
AT_SETUP([mkstemp])
AT_KEYWORDS([maketemp])
dnl Check that on error, the expansion is void
AT_DATA([[in]],
[[mkstemp(`no_such_dir/m4-fooXXXXXX')
]])
AT_CHECK_M4([in], [0], [[
]], [[m4:in:1: warning: mkstemp: cannot create file from template 'no_such_dir/m4-fooXXXXXX': No such file or directory
]])
dnl Check that extra X are appended, but not trailing NUL
AT_DATA([[in]], [[len(mkstemp(`m4-fooXXXXX'))
]])
AT_CHECK_M4([in], [0], [[12
]])
dnl Check that umask has an effect
AT_DATA([[in]],
[[substr(esyscmd(`ls -ld 'mkstemp(`m4-fooXXXXXX')), `0', `10')
]])
AT_CHECK([$M4 < in], [0], [[-rw-------
]])
AT_CHECK([umask 700; $M4 < in], [0], [[----------
]])
dnl Check for Solaris compatibility of maketemp. Hopefully the pid is
dnl less than 20 decimal digits. Also check that --safer does not affect
dnl traditional behavior of maketemp, which is textual only.
AT_DATA([[in]],
[[maketemp()
maketemp(X)
maketemp(XX)
maketemp(XXXXXXXXXXXXXXXXXXXXX)
maketemp(no_such_dir/XXXXXX)
]])
dnl Abuse our knowledge of AT_CHECK_M4 so that we can get stderr filtering...
AT_CHECK_M4([-G -Q --safer], [0], [stdout], [],
[in& echo $! > pid; wait $!])
pid=`cat pid`
cat >expout <*>*>*>*>!, !<*<*<*<**>*>*>*>foo bar<*<*<*<*<
foo bar
>*>*>*>*>*>*><*<*<*<*<*<*<
dumpdef(>*>*>*>*>foo<*<*<*<*<, >*>*>*>*>bar<*<*<*<*<)dnl
]])
AT_DATA([[expout]],
[[
``traceon''
foo
``FOO''
BAR
foo bar
``FOO'' BAR
*>*>*<*<
]])
AT_DATA([[experr]],
[[m4trace: -1- changequote(`[', `]') -> []
m4trace: -1- dnl -> []
m4trace: -1- changequote([``], ['']) -> ``''
m4trace: -1- dnl -> ``''
m4trace: -1- define(``foo'', ````FOO'''') -> ``''
m4trace: -1- dnl -> ``''
foo: ````FOO''''
m4trace: -1- dumpdef(``foo'') -> ``''
m4trace: -1- dnl -> ``''
m4trace: -1- changequote(``!'', ``!'') -> !!
m4trace: -1- dnl -> !!
m4trace: -1- foo -> !``FOO''!
foo: !``FOO''!
m4trace: -1- dumpdef(!foo!) -> !!
m4trace: -1- dnl -> !!
m4trace: -1- define(!bar!, !BAR!) -> !!
m4trace: -1- bar -> !BAR!
m4trace: -1- changequote(!>*>*>*>*>!, !<*<*<*<* >*>*>*>*><*<*<*<*<
m4trace: -1- dnl -> >*>*>*>*><*<*<*<*<
m4trace: -1- foo -> >*>*>*>*>``FOO''<*<*<*<*<
m4trace: -1- bar -> >*>*>*>*>BAR<*<*<*<*<
bar: >*>*>*>*>BAR<*<*<*<*<
foo: >*>*>*>*>``FOO''<*<*<*<*<
m4trace: -1- dumpdef(>*>*>*>*>foo<*<*<*<*<, >*>*>*>*>bar<*<*<*<*<) -> >*>*>*>*><*<*<*<*<
m4trace: -1- dnl -> >*>*>*>*><*<*<*<*<
]])
AT_CHECK_M4([multiquotes.m4], 0, expout, experr)
AT_CLEANUP
## -------- ##
## patsubst ##
## -------- ##
AT_SETUP([patsubst])
AT_DATA([[patsubst.m4]],
[[# traceon(`patsubst')
patsubst(`GNUs not Unix.', `^', `OBS: ')
patsubst(`GNUs not Unix.', `\<', `OBS: ')
patsubst(`GNUs not Unix.', `\<\w', `\&=')
patsubst(`GNUs not Unix.', `\w*', `(\&)')
patsubst(`GNUs not Unix.', `\w+', `(\&)')
patsubst(`GNUs not Unix.', `\w+')
patsubst(`GNUs not '` Unix.', `[ ]+', ` ')
]])
AT_DATA([[expout]],
[[# traceon(`patsubst')
OBS: GNUs not Unix.
OBS: GNUs OBS: not OBS: Unix.
G=NUs n=ot U=nix.
(GNUs)() (not)() (Unix)().()
(GNUs) (not) (Unix).
.
GNUs not Unix.
]])
AT_CHECK_M4([patsubst.m4], 0, expout)
AT_CLEANUP
## ------ ##
## regexp ##
## ------ ##
AT_SETUP([regexp])
AT_DATA([[regexp.m4]],
[[traceon(`regexp')dnl
regexp(`hej med dig', `.*', `>>\&<<')
regexp(`hej med dig', `\w*', `>>\&<<')
regexp(`hej med dig', `.+', `>>\&<<')
regexp(`hej med dig', `m\w+', `>>\&<<')
regexp(`hej med dig', `m\(.*\)', `>>\&<< >>\1<<')
regexp(`hej med dig', `.*')
regexp(`hej med dig', `\w*')
regexp(`hej med dig', `.+')
regexp(`hej med dig', `m\w+')
regexp(`hej med dig', `m\(.*\)')
]])
AT_DATA([[expout]],
[[>>hej med dig<<
>>hej<<
>>hej med dig<<
>>med<<
>>med dig<< >>ed dig<<
0
0
0
4
4
]])
AT_DATA([[experr]],
[[m4trace: -1- regexp(`hej med dig', `.*', `>>\&<<') -> `>>hej med dig<<'
m4trace: -1- regexp(`hej med dig', `\w*', `>>\&<<') -> `>>hej<<'
m4trace: -1- regexp(`hej med dig', `.+', `>>\&<<') -> `>>hej med dig<<'
m4trace: -1- regexp(`hej med dig', `m\w+', `>>\&<<') -> `>>med<<'
m4trace: -1- regexp(`hej med dig', `m\(.*\)', `>>\&<< >>\1<<') -> `>>med dig<< >>ed dig<<'
m4trace: -1- regexp(`hej med dig', `.*') -> `0'
m4trace: -1- regexp(`hej med dig', `\w*') -> `0'
m4trace: -1- regexp(`hej med dig', `.+') -> `0'
m4trace: -1- regexp(`hej med dig', `m\w+') -> `4'
m4trace: -1- regexp(`hej med dig', `m\(.*\)') -> `4'
]])
AT_CHECK_M4([regexp.m4], 0, expout, experr)
AT_CLEANUP
## ------------ ##
## sync-lines. ##
## ------------ ##
AT_SETUP([sync-lines])
AT_DATA([[in]],
[[syncoutput(on)dnl
# Several input lines, expanding to one
define(`foo', ``foo' line one.
`foo' line two.
`foo' line three.') xyz
foo
# Several input lines, expanding to none
define(`foo', ``foo' line one.
`foo' line two.
`foo' line three.')dnl
# one input line, expanding to several output lines
foo foo
]])
AT_CHECK_M4([[in]], 0,
[[#line 2 "in"
# Several input lines, expanding to one
#line 5
xyz
foo line one.
#line 6
foo line two.
#line 6
foo line three.
# Several input lines, expanding to none
#line 11
# one input line, expanding to several output lines
foo line one.
#line 12
foo line two.
#line 12
foo line three. foo line one.
#line 12
foo line two.
#line 12
foo line three.
]])
AT_CLEANUP
## ------ ##
## syscmd ##
## ------ ##
AT_SETUP([syscmd])
dnl Ensure that syscmd does not inherit any unnecessary fds from trace.
AT_DATA([in.m4], [[syscmd(`echo hi >&3')ifelse(sysval,
`0', `skipping: sh cannot detect closed fds
m4exit(`77')')dnl
]])
AT_CHECK_M4([3>&-], [0], [], [stderr], [in.m4])
mv stderr experr
AT_CHECK_M4([--debugfile=trace -tdnl 3>&-], [0], [], [experr], [in.m4])
AT_CHECK([cat trace], [0], [[m4trace: -1- dnl -> `'
]])
dnl Ensure that syscmd does not inherit any unnecessary fds from diversions.
AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(`1')f
world
syscmd(`echo hi >&3')divert
hello
]])
AT_CHECK_M4([3>&-], [0], [stdout-nolog], [experr], [in.m4])
AT_CHECK([$SED -ne '/./p' stdout], [0], [[hello
world
]])
dnl Ensure that syscmd does not inherit any unnecessary fds from input files.
AT_DATA([in.m4], [[hello syscmd(`cat <&3')dnl
dnl this line should not be read by cat
world
]])
AT_CHECK_M4([3>&-], [0], [[hello world
]], [stderr], [in.m4])
mv stderr experr
AT_CHECK_M4([in.m4 3>&-], [0], [[hello world
]], [experr])
AT_CLEANUP
## -------- ##
## translit ##
## -------- ##
AT_SETUP([translit])
AT_DATA([[translit.m4]],
[[# traceon(`translit')dnl
translit(`GNUs not Unix', `a-z')
translit(`GNUs not Unix', `a-z', `A-Z')
translit(`GNUs not Unix', `A-Z', `a-z')
translit(`GNUs not Unix', `A-Z')
translit(`a-z', `a-')
translit(`A-Z', `A-Z-', `-A-Z')
translit(`GNUs not Unix', `Z-A', `a-z')
]])
AT_CHECK_M4([translit.m4], 0,
[[# traceon(`translit')dnl
GNU U
GNUS NOT UNIX
gnus not unix
s not nix
z
-ZY
tmfs not fnix
]])
dnl This used to be quadratic, taking millions of comparisons,
dnl but should now operate in linear time with only several thousand checks.
AT_DATA([in], [M4_ONE_MEG_DEFN[dnl
define(`a_', translit(substr(f, `0', `50000'), `
', `a'))dnl
define(`b_', translit(substr(f, `0', `50000'), `
', `b'))dnl
define(`d_', translit(substr(f, `0', `50000'), `
', `d'))dnl
define(`c'd_, `pass')dnl
translit(`a'b_, a_`b', `c'd_)
]])
AT_CHECK_M4([in], [0], [[pass
]])
dnl This validates that ranges are built using unsigned chars.
AT_DATA([in], [[translit(`«abc~', `~-»')
]])
AT_CHECK_M4([in], [0], [[abc
]])
dnl Validate short strings, which take a different code path.
AT_DATA([in], [[dnl
translit(`abcdeabcde', `a')
translit(`abcdeabcde', `ab')
translit(`abcdeabcde', `a', `f')
translit(`abcdeabcde', `aa', `fg')
translit(`abcdeabcde', `a', `fg')
translit(`abcdeabcde', `ab', `f')
translit(`abcdeabcde', `ab', `fg')
translit(`abcdeabcde', `ab', `ba')
translit(`abcdeabcde', `e', `f')
translit(`abc', `', `cde')
translit(`', `a', `bc')
]])
AT_CHECK_M4([in], [0], [[bcdebcde
cdecde
fbcdefbcde
fbcdefbcde
fbcdefbcde
fcdefcde
fgcdefgcde
bacdebacde
abcdfabcdf
abc
]])
AT_CLEANUP
## -------- ##
## undivert ##
## -------- ##
AT_SETUP([undivert])
AT_DATA([[undivert.m4]],
[[define(`undiverted', `UNDIVERTED')
# undiverted file.
undivert(`undivert.incl')
# included file.
include(`undivert.incl')
]])
AT_DATA([[undivert.incl]],
[[This is to be undiverted soon.
]])
AT_CHECK_M4([undivert.m4], 0,
[[
# undiverted file.
This is to be undiverted soon.
# included file.
This is to be UNDIVERTED soon.
]])
AT_CLEANUP
## ---- ##
## wrap ##
## ---- ##
AT_SETUP([wrap])
AT_DATA([[wrap.m4]],
[[divert(-1)
m4wrap(`Wrapper no. 1
')
m4wrap(`Wrapper no. 2
m4wrap(`Wrapper no. 3
m4wrap(`Wrapper no. 4
')')')
divert
No. 33: The End.
]])
AT_CHECK_M4([wrap.m4], 0,
[[
No. 33: The End.
Wrapper no. 1
Wrapper no. 2
Wrapper no. 3
Wrapper no. 4
]])
AT_CLEANUP