# 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