# Hand crafted tests for GNU M4. -*- Autotest -*-
# Copyright (C) 2006-2010, 2013-2014 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_TEST_FREEZE([title], [text1], [text2])
# -----------------------------------------
# Create a test TITLE, which checks that freezing TEXT1, then reloading
# it with TEXT2, produces the same results as running TEXT1 and TEXT2 in
# a single run.
m4_define([AT_TEST_FREEZE],
[AT_SETUP([$1])
AT_KEYWORDS([frozen])
AT_DATA([frozen.m4], [$2])
AT_DATA([unfrozen.m4], [$3])
# First generate the `expout' output by running over the sources before
# freezing.
AT_CHECK_M4([frozen.m4 unfrozen.m4], [0], [stdout-nolog], [stderr])
mv stdout expout
mv stderr experr
# Now freeze the first source file.
AT_CHECK_M4([-F frozen.m4f frozen.m4], [0], [stdout-nolog])
mv stdout out1
# Now rerun the original sequence, but using the frozen file.
AT_CHECK_M4([-R frozen.m4f unfrozen.m4],
[0], [stdout-nolog], [experr], [], [ ])
AT_CHECK([cat out1 stdout], [0], [expout])
AT_CLEANUP
])
AT_BANNER([Freezing state.])
## ---------------- ##
## freezing failure ##
## ---------------- ##
AT_SETUP([freezing failure])
AT_KEYWORDS([frozen])
AT_CHECK_M4([-F /none/such], [1], [],
[[m4: cannot open '/none/such': No such file or directory
]])
if test -w /dev/full && test -c /dev/full ; then
AT_CHECK_M4([-F /dev/full], [1], [],
[[m4: unable to create frozen state: No space left on device
]])
fi
AT_CLEANUP
## --------------- ##
## large diversion ##
## --------------- ##
# Check that large diversions are handled across freeze boundaries.
# Also check for escape character handling.
AT_TEST_FREEZE([large diversion],
[M4_ONE_MEG_DEFN[divert(2)f
divert(1)hi
a\nb
]],
[[divert(3)bye
]])
## ---------------- ##
## loading format 1 ##
## ---------------- ##
AT_SETUP([loading format 1])
AT_KEYWORDS([frozen])
m4_if([
Note: frozen.m4f was obtained by deleting unneeded lines from the output of
a version of m4 1.4.5 with changeword support. Deleting lines is in effect
equivalent to using undefine(name) in the input. This test ensures we
behave well with the old format, including \ parsing, disappearing builtins
(okay so long as the input does not try to use them), and restoring sane
defaults for features that were only added in version 2 frozen format.
$ m4 --version | head -n1
GNU M4 1.4.5
$ cat frozen.m4
divert(`-1')
define(`foo', `\n\
FOO')
pushdef(`foo', `bar${1}')
define(`my_define', defn(`define'))
define(`my_changeword', defn(`changeword'))
pushdef(`my_define', `define')
pushdef(`my_define', defn(`define'))
divert(`1')dnl
foo
divert`'dnl
changequote([,])dnl
changecom([/*], [*/])dnl
dnl Implied sequence of undefine(`name') due to hand-edits
$ m4 -F frozen.m4f frozen.m4
$
])
AT_DATA([frozen.m4f],
[[# This is a frozen state file generated by GNU M4 1.4.5
V1
Q1,1
[]
C2,2
/**/
F6,6
popdefpopdef
F13,10
my_changewordchangeword
F9,6
my_definedefine
T9,6
my_definedefine
F9,6
my_definedefine
T3,7
foo\n\
FOO
T3,7
foobar${1}
F3,3
dnldnl
D1,8
bar${1}
D0,0
# End of frozen state file
]])
AT_DATA([input.m4],
[[foo([2]) /* foo */ popdef([foo])foo
my_define([bar], [1])[]popdef([my_define]) bar
my_define([bar], [2])[]popdef([my_define]) bar
my_define([bar], [3])[]popdef([my_define]) bar
my_define([bar], [4])[]popdef([my_define]) bar
]])
AT_CHECK_M4([-R frozen.m4f input.m4], [0],
[[bar${1} /* foo */ \n\
FOO
1
define 1
3
my_define(bar, 4) 3
bar${1}
]],
[[m4:input.m4:5: warning: popdef: undefined macro 'my_define'
]])
dnl Test rejection of v2 features in a v1 frozen file
AT_DATA([bogus.m4f], [[V1
M2
m4
]])
AT_CHECK_M4([-R bogus.m4f], [1], [],
[[m4:bogus.m4f:2: ill-formed frozen file, version 2 directive `M' encountered
]])
AT_CLEANUP
## ---------------- ##
## loading format 2 ##
## ---------------- ##
AT_SETUP([loading format 2])
AT_KEYWORDS([frozen])
AT_DATA([frozen.m4f],
[[# Handcrafted file, obeying the version 2 spec
V2
# missing close quote should be supplied
Q1,0
>
# missing close comment should be supplied
C1,0
<
M2
m4
M3
gnu
F7,7,3
builtin
builtin
gnu
# Text to negative diversion must not crash. Catches a regression
# introduced 2007-05-28 and fixed 2007-05-31.
D-1,5
12345
# Check line continuations.
D1,3
a\n\
b
# Zero can be implied
D,
# Testing escape sequences
T4,6
blah
-\t\477\040\X5C
# Long macro definition. Catches a regression introduced on 2007-01-20
# and patched 2007-02-25.
T4,122
long
01234567890123456789012345678901234567890123456789
01234567890123456789012345678901234567890123456789
01234567890123456789
]])
AT_DATA([input.m4],
[[< comment: builtin()
builtin(>define', foo, bar)
foo
blah
]])
AT_CHECK_M4([-R frozen.m4f input.m4], [0],
[[< comment: builtin()
bar
- '7 \
a
b]])
dnl We don't support anything larger than format 2; make sure of that...
AT_DATA([bogus.m4f], [[# comments aren't continued\
V3
]])
AT_CHECK_M4([-R bogus.m4f], [63], [],
[[m4:bogus.m4f:2: frozen file version 3 greater than max supported of 2
]])
dnl Check that V appears.
AT_DATA([bogus.m4f], [[# not really a frozen file
oops
]])
AT_CHECK_M4([-R bogus.m4f], [1], [],
[[m4:bogus.m4f:2: expecting character `V' in frozen file
]])
dnl M4_DIVNUM_TEST(number, [out-of-bounds])
dnl Check for diversion number corner case handling. Simulate freezing with
dnl number as the active diversion, then reload and check that number. If
dnl OUT-OF-BOUNDS, expect reloading to reject the frozen file.
m4_define([M4_DIVNUM_TEST], [
AT_DATA([frozen.m4f], [[V2
M2
m4
M3
gnu
T1,5
a
\n\n\n\n\n
F6,6,2
divnum\
divnum
\
m4\
F6,6,2
divert
divert
m4
F6,6,2
define
define
m4
D]$1[,3
hi
]])
AT_CHECK_M4([-R frozen.m4f in.m4], m4_ifval([$2], [1], [0]),
m4_ifval([$2], [], [m4_bpatsubst([$1], [^0*])
m4_if(m4_substr([$1], [0], [1]), [-], [], [[hi
]])]), m4_ifval([$2], [[m4:frozen.m4f:24: integer overflow in frozen file
]]))
])
AT_DATA([in.m4], [[define(d,divnum)divert(0)d
]])
M4_DIVNUM_TEST([02147483647])
M4_DIVNUM_TEST([02147483648], [:])
M4_DIVNUM_TEST([-2147483648])
M4_DIVNUM_TEST([-2147483649], [:])
AT_CLEANUP
## --------- ##
## changecom ##
## --------- ##
# Check that changecom/changequote are maintained across freeze boundaries.
AT_TEST_FREEZE([reloading changecom],
[[changecom`'changequote(<,>)dnl
]],
[[define(, )
foo # foo
]])
## ------------ ##
## changesyntax ##
## ------------ ##
# Check that changesyntax is maintained across freeze boundaries.
AT_TEST_FREEZE([reloading changesyntax],
[[changesyntax(`W+.', `({', `)}')dnl
define{`a.b', `hello $1'}dnl
]],
[[a.b{world}
]])
## --------- ##
## debugmode ##
## --------- ##
# Check that debugmode can be preserved, and how it interacts with -d
AT_SETUP([reloading debugmode])
AT_KEYWORDS([frozen])
AT_DATA([frozen.m4],
[[debugmode(`fl')dnl
]])
AT_DATA([unfrozen.m4],
[[traceon(`len')len(`a')
]])
AT_CHECK_M4([-F frozen.m4f -d-V frozen.m4], [0])
dnl With no -d option, use the frozen file
AT_CHECK_M4([-R frozen.m4f unfrozen.m4], [0], [[1
]], [[m4trace:unfrozen.m4:1: -1- len
]], [], [ ])
dnl With plain -d before -R, use the frozen file
AT_CHECK_M4([-R frozen.m4f unfrozen.m4], [0], [[1
]], [[m4trace:unfrozen.m4:1: -1- len
]])
dnl With plain -d after -R, add +adeq to the frozen file
AT_CHECK_M4([-R frozen.m4f -d unfrozen.m4], [0], [[1
]], [[m4trace:unfrozen.m4:1: -1- len(`a') -> `1'
]], [], [ ])
dnl With explicit -d option, override frozen file
AT_CHECK_M4([-R frozen.m4f -de unfrozen.m4], [0], [[1
]], [[m4trace: -1- len -> 1
]])
AT_CLEANUP
## --------- ##
## nul bytes ##
## --------- ##
# Check that NUL can be transparently preserved over freezing.
AT_SETUP([reloading nul])
AT_KEYWORDS([frozen])
dnl AT_DATA can't generate NUL bytes (at least, not in all shells).
# Skip the test if printf(1) is insufficient.
AT_CHECK([printf 'define(-\0-,\0-\0)changequote([,\0])changecom(--\0)dnl
divert(1)undivert(null.out)' || exit 77],
[0], [stdout], [ignore])
mv stdout frozen.m4
printf 'divert(0)[divnum\0] @%:@-- len(indir(-\0-))\n' > unfrozen.m4
# First generate the `expout' output by running over the sources before
# freezing.
AT_CHECK_M4([-I "$abs_srcdir" frozen.m4 unfrozen.m4], [0], [stdout], [stderr])
mv stdout expout
mv stderr experr
# Now freeze the first source file.
AT_CHECK_M4([-F frozen.m4f -I "$abs_srcdir" frozen.m4], [0], [stdout])
mv stdout out1
# Now rerun the original sequence, but using the frozen file.
AT_CHECK_M4([-R frozen.m4f unfrozen.m4], [0], [stdout], [experr], [], [ ])
AT_CHECK([cat out1 stdout], [0], [expout])
dnl Check that unexpected embedded NULs are recognized.
printf '# bogus frozen file\nV2\nR4\ngnu\0\n' > bogus.m4f
AT_CHECK_M4([-R bogus.m4f], [1], [],
[[m4:bogus.m4f:4: bad syntax-spec 'gnu\0'
]])
dnl Reject escape sequences that expand to unexpected NUL
AT_DATA([bogus.m4f],
[[# bogus frozen file
V2
F3,4
len
len\0
]])
AT_CHECK_M4([-R bogus.m4f], [1], [],
[[m4:bogus.m4f:5: ill-formed frozen file, invalid builtin 'len\0' encountered
]])
AT_CLEANUP
])
## ------- ##
## pushdef ##
## ------- ##
# Check for pushdef stacks; broken 2001-09-01, fixed 2008-05-15.
AT_TEST_FREEZE([reloading pushdef stack],
[[pushdef(`foo', `1')
pushdef(`foo', defn(`len'))
pushdef(`foo', `3')
]],
[[foo(`abc')popdef(`foo')
foo(`ab')popdef(`foo')
foo(`a')popdef(`foo')
foo
]])
## ------------- ##
## regexp syntax ##
## ------------- ##
# Check that regular expression syntax is maintained across freeze boundaries.
AT_TEST_FREEZE([reloading regexp syntax],
[[changeresyntax(`POSIX_EXTENDED')dnl
]],
[[regexp(`GNUs not Unix', `\w(\w*)$')
regexp(`GNUs not Unix', `\w\(\w*\)$', `GNU_M4')
]])
## ----- ##
## trace ##
## ----- ##
# Check for macro tracing, both single and global.
AT_TEST_FREEZE([reloading traced macros],
[[define(`text', `hello world')dnl
define(`foo', `bar')dnl
traceon(`blah', `divnum', `text')dnl
traceon
]],
[[foo
traceoff
foo
text
divnum
ifdef(`blah', `', `define(`blah', `finally')')dnl
blah
]])
## ---------------- ##
## unknown builtins ##
## ---------------- ##
AT_SETUP([reloading unknown builtin])
AT_KEYWORDS([frozen])
AT_DATA([[empty.m4]])
# Freeze default state. Also check for bug fixed 18 Oct, 2007.
AT_CHECK_M4([-F frozen.m4f -t undefined empty.m4])
# Add an unknown builtin.
echo 'F1,1' >> frozen.m4f
echo 'a' >> frozen.m4f
echo 'b' >> frozen.m4f
AT_DATA([[input.m4]],
[[dnl The macro is defined; checking this is safe
ifdef(`a', `yes', `no')
dnl Grabbing the definition must warn; and the copy is the empty string
define(`c', defn(`a'))
dnl Invoking the macro directly must warn
a
dnl Invoking it indirectly must warn
indir(`a')
dnl Since it is a placeholder, builtin must reject it
builtin(`b')
dnl The copy is a text string, not a placeholder
c
dnl Since it is defined, it must have a definition
dumpdef(`a', `c')
dnl Deleting it is safe
popdef(`a')
a
]])
AT_CHECK_M4([-R frozen.m4f input.m4], 0,
[[yes
a
]],
[[m4:input.m4:4: warning: defn: a: builtin 'b' requested by frozen file not found
m4:input.m4:6: warning: a: builtin 'b' requested by frozen file not found
m4:input.m4:8: warning: a: builtin 'b' requested by frozen file not found
m4:input.m4:10: warning: builtin: undefined builtin 'b'
a: <>
c: `'
]], [], [ ])
AT_CLEANUP