# Hand crafted tests for GNU M4. -*- Autotest -*- # Copyright (C) 2001, 2006-2008, 2010, 2013-2014, 2017 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([Macro definitions.]) # Checking everything related to macro definitions: the expansion of # user macros, the propagation of various bits (tracing, number of # arguments and so on). ## ---------------- ## ## Arity and defn. ## ## ---------------- ## AT_SETUP([Arity and defn]) # Check that the arity checking of define is correctly propagated. AT_DATA([[input.m4]], [[define(`defun', defn(`define')) define define(`foo') define(`foo', `bar') define(`foo', `bar', `baz') defun defun(`foo') defun(`foo', `bar') defun(`foo', `bar', `baz') ]]) AT_DATA([[expout]], [[ define defun ]]) AT_CHECK_M4([input.m4], 0, [expout], [[m4:input.m4:5: warning: define: extra arguments ignored: 3 > 2 m4:input.m4:10: warning: defun: extra arguments ignored: 3 > 2 ]]) AT_CLEANUP ## ------------------------- ## ## Arity, defn, and freeze. ## ## ------------------------- ## AT_SETUP([Arity, defn, and freeze]) AT_KEYWORDS([frozen]) AT_DATA([[freezeme.m4]], [[define(`defun', defn(`define'))dnl undefine(`define')dnl ]]) AT_CHECK_M4([--freeze-state=frozen.m4f freezeme.m4], 0) AT_DATA([[input.m4]], [[defun defun(`foo') defun(`foo', `bar') defun(`foo', `bar', `baz') ]]) AT_DATA([[expout]], [[defun ]]) AT_CHECK_M4([--reload-state=frozen.m4f input.m4], 0, expout, [[m4:input.m4:4: warning: defun: extra arguments ignored: 3 > 2 ]]) AT_CLEANUP(freezeme.m4 frozen.m4f) ## ------------------- ## ## Command line define ## ## ------------------- ## AT_SETUP([Command line define]) dnl Test that -D after last file still affects m4wrap'd text. AT_DATA([in1], [[m4wrap(`foo ')foo ]]) AT_DATA([in2], [[foo ]]) AT_CHECK_M4([-Dfoo=1 in1 -Dfoo=2 in2 -Dfoo=3], [0], [[1 2 3 ]]) dnl Test that -D only affects top-most definition. AT_DATA([in1], [[define(`foo', `1')pushdef(`foo', `2')dnl ]]) AT_DATA([in2], [[foo popdef(`foo')foo popdef(`foo')foo ]]) AT_CHECK_M4([in1 -Dfoo=3 in2], [0], [[3 1 foo ]]) dnl Test that -D and -U interact in correct order AT_DATA([in], [[foo ]]) AT_CHECK_M4([-Dfoo=bar in -Ufoo in], [0], [[bar foo ]]) AT_CHECK_M4([-Ufoo in -Dfoo=bar in], [0], [[foo bar ]]) dnl Test macro arguments defined via -D AT_DATA([in], [[-foo-foo(1)-foo(1,2)- -bar-bar(1)-bar(1,2)- ]]) AT_CHECK_M4([-Dfoo -Dbar='$@' in], [0], [[---- --1-1,2- ]]) AT_CLEANUP ## -------------------- ## ## Command line pushdef ## ## -------------------- ## AT_SETUP([Command line pushdef]) dnl Test that -p after last file still affects m4wrap'd text. AT_DATA([in1], [[m4wrap(`foo ')foo ]]) AT_DATA([in2], [[foo ]]) AT_CHECK_M4([-pfoo=1 in1 -pfoo=2 in2 -pfoo=3], [0], [[1 2 3 ]]) dnl Test that -p adds a definition. AT_DATA([in1], [[define(`foo', `1')pushdef(`foo', `2')dnl ]]) AT_DATA([in2], [[foo popdef(`foo')foo popdef(`foo')foo ]]) AT_CHECK_M4([in1 -pfoo=3 in2], [0], [[3 2 1 ]]) dnl Test that --pushdef and --popdef interact in correct order AT_DATA([in], [[foo ]]) AT_CHECK_M4([-Dfoo=1 --pushdef=foo=2 in --popdef=foo in], [0], [[2 1 ]]) AT_CHECK_M4([--popdef=foo in --pushdef=foo=1 in], [0], [[foo 1 ]]) AT_CLEANUP ## ---------------- ## ## pushdef/popdef. ## ## ---------------- ## AT_SETUP([pushdef/popdef]) AT_DATA([[pushpop.m4]], [[divert(-1) pushdef(`hej', `def 1.') dumpdef(`hej') pushdef(`hej', `def 2.') dumpdef(`hej') pushdef(`hej', `def 3.') dumpdef(`hej') pushdef(`hej', `def 4.') dumpdef(`hej') popdef(`hej') dumpdef(`hej') popdef(`hej') dumpdef(`hej') popdef(`hej') dumpdef(`hej') popdef(`hej') dumpdef(`hej') dumpdef(`mac2') popdef(`mac2') ]]) AT_CHECK_M4([pushpop.m4], 0, [], [[hej: `def 1.' hej: `def 2.' hej: `def 3.' hej: `def 4.' hej: `def 3.' hej: `def 2.' hej: `def 1.' m4:pushpop.m4:18: warning: dumpdef: undefined macro 'hej' m4:pushpop.m4:20: warning: dumpdef: undefined macro 'mac2' m4:pushpop.m4:21: warning: popdef: undefined macro 'mac2' ]]) AT_CLEANUP ## ---------------------- ## ## Tracing Hanoi Towers. ## ## ---------------------- ## AT_SETUP([Tracing Hanoi Towers]) AT_DATA([[trace.m4]], [[divert(-1) # move(from, to) define(`move', `Move one disk from `$1' to `$2'. ') # _hanoi (cnt, from, to, aux) define(`_hanoi', `ifelse(eval(`$1'<=1), 1, `move($2, $3)', `_hanoi(decr($1), $2, $4, $3)move($2, $3)_hanoi(decr($1), $4, $3, $2)')') # hanoi (cnt) define(`hanoi', `_hanoi(`$1', source, destination, auxilliary)') divert`'dnl # Debugmode t debugmode(`t') hanoi(2) # Debugmode taeq debugmode(`taeq') hanoi(2) # Debugmode OFF debugmode hanoi(2) # Debugmode ae debugmode(`ae') traceon(`move', `_hanoi') hanoi(2) ]]) AT_DATA([[expout]], [[ # Debugmode t Move one disk from source to auxilliary. Move one disk from source to destination. Move one disk from auxilliary to destination. # Debugmode taeq Move one disk from source to auxilliary. Move one disk from source to destination. Move one disk from auxilliary to destination. # Debugmode OFF Move one disk from source to auxilliary. Move one disk from source to destination. Move one disk from auxilliary to destination. # Debugmode ae Move one disk from source to auxilliary. Move one disk from source to destination. Move one disk from auxilliary to destination. ]]) AT_DATA([[experr]], [[m4trace: -1- hanoi m4trace: -1- _hanoi m4trace: -2- eval m4trace: -1- ifelse m4trace: -2- decr m4trace: -1- _hanoi m4trace: -2- eval m4trace: -1- ifelse m4trace: -1- move m4trace: -1- move m4trace: -2- decr m4trace: -1- _hanoi m4trace: -2- eval m4trace: -1- ifelse m4trace: -1- move m4trace: -1- debugmode m4trace: -1- hanoi(`2') -> `_hanoi(`2', source, destination, auxilliary)' m4trace: -1- _hanoi(`2', `source', `destination', `auxilliary') -> `ifelse(eval(`2'<=1), 1, `move(source, destination)', `_hanoi(decr(2), source, auxilliary, destination)move(source, destination)_hanoi(decr(2), auxilliary, destination, source)')' m4trace: -2- eval(`2<=1') -> `0' m4trace: -1- ifelse(`0', `1', `move(source, destination)', `_hanoi(decr(2), source, auxilliary, destination)move(source, destination)_hanoi(decr(2), auxilliary, destination, source)') -> `_hanoi(decr(2), source, auxilliary, destination)move(source, destination)_hanoi(decr(2), auxilliary, destination, source)' m4trace: -2- decr(`2') -> `1' m4trace: -1- _hanoi(`1', `source', `auxilliary', `destination') -> `ifelse(eval(`1'<=1), 1, `move(source, auxilliary)', `_hanoi(decr(1), source, destination, auxilliary)move(source, auxilliary)_hanoi(decr(1), destination, auxilliary, source)')' m4trace: -2- eval(`1<=1') -> `1' m4trace: -1- ifelse(`1', `1', `move(source, auxilliary)', `_hanoi(decr(1), source, destination, auxilliary)move(source, auxilliary)_hanoi(decr(1), destination, auxilliary, source)') -> `move(source, auxilliary)' m4trace: -1- move(`source', `auxilliary') -> `Move one disk from `source' to `auxilliary'. ' m4trace: -1- move(`source', `destination') -> `Move one disk from `source' to `destination'. ' m4trace: -2- decr(`2') -> `1' m4trace: -1- _hanoi(`1', `auxilliary', `destination', `source') -> `ifelse(eval(`1'<=1), 1, `move(auxilliary, destination)', `_hanoi(decr(1), auxilliary, source, destination)move(auxilliary, destination)_hanoi(decr(1), source, destination, auxilliary)')' m4trace: -2- eval(`1<=1') -> `1' m4trace: -1- ifelse(`1', `1', `move(auxilliary, destination)', `_hanoi(decr(1), auxilliary, source, destination)move(auxilliary, destination)_hanoi(decr(1), source, destination, auxilliary)') -> `move(auxilliary, destination)' m4trace: -1- move(`auxilliary', `destination') -> `Move one disk from `auxilliary' to `destination'. ' m4trace: -1- debugmode -> `' m4trace: -1- _hanoi(2, source, destination, auxilliary) -> ifelse(eval(`2'<=1), 1, `move(source, destination)', `_hanoi(decr(2), source, auxilliary, destination)move(source, destination)_hanoi(decr(2), auxilliary, destination, source)') m4trace: -1- _hanoi(1, source, auxilliary, destination) -> ifelse(eval(`1'<=1), 1, `move(source, auxilliary)', `_hanoi(decr(1), source, destination, auxilliary)move(source, auxilliary)_hanoi(decr(1), destination, auxilliary, source)') m4trace: -1- move(source, auxilliary) -> Move one disk from `source' to `auxilliary'. m4trace: -1- move(source, destination) -> Move one disk from `source' to `destination'. m4trace: -1- _hanoi(1, auxilliary, destination, source) -> ifelse(eval(`1'<=1), 1, `move(auxilliary, destination)', `_hanoi(decr(1), auxilliary, source, destination)move(auxilliary, destination)_hanoi(decr(1), source, destination, auxilliary)') m4trace: -1- move(auxilliary, destination) -> Move one disk from `auxilliary' to `destination'. ]]) AT_CHECK_M4([trace.m4], 0, expout, experr) AT_CLEANUP ## ------------------------------- ## ## Propagation of trace requests. ## ## ------------------------------- ## AT_SETUP([Propagation of traceon]) AT_DATA([[trace2.m4]], [[traceon(`define') debugmode(`aeq') # copy the `define' builtin definition to another symbol define(`my_define', defn(`define')) # delete the original undefine(`define') # Does it work? my_define(`foo', `bar') # Use the new definition to redefine the original symbol my_define(`define', defn(`my_define')) # Torture the flag propogation undefine(`my_define') define(`my_define', defn(`define')) # There are now 2 symbols pointing to the same builtin function my_define(`foo', `bar') define(`foo', `bar') ]]) AT_DATA([[expout]], [[ # copy the `define' builtin definition to another symbol # delete the original # Does it work? # Use the new definition to redefine the original symbol # Torture the flag propogation # There are now 2 symbols pointing to the same builtin function ]]) AT_DATA([[experr]], [[m4trace: -1- define(`my_define', ) -> `' m4trace: -1- define(`my_define', ) -> `' m4trace: -1- define(`foo', `bar') -> `' ]]) AT_CHECK_M4([trace2.m4], 0, expout, experr) AT_CLEANUP ## ------------------------ ## ## Propagation of --trace. ## ## ------------------------ ## AT_SETUP([Propagation of --trace]) AT_DATA([[trace3.m4]], [[# copy the `define' builtin definition to another symbol define(`my_define', defn(`define')) # delete the original undefine(`define') # Does it work? my_define(`foo', `bar') # Use the new definition to redefine the original symbol my_define(`define', defn(`my_define')) # Torture the flag propogation undefine(`my_define') define(`my_define', defn(`define')) # There are now 2 symbols pointing to the same builtin function my_define(`foo', `bar') define(`foo', `bar') ]]) AT_DATA([[expout]], [[# copy the `define' builtin definition to another symbol # delete the original # Does it work? # Use the new definition to redefine the original symbol # Torture the flag propogation # There are now 2 symbols pointing to the same builtin function ]]) AT_DATA([[experr]], [[m4trace: -1- define(`my_define', ) -> `' m4trace: -1- define(`my_define', ) -> `' m4trace: -1- define(`foo', `bar') -> `' ]]) AT_CHECK_M4([-t define -daeq trace3.m4], 0, expout, experr) AT_CLEANUP ## --------------------- ## ## Renamesyms collisions ## ## --------------------- ## AT_SETUP([Renamesyms collisions]) dnl FIXME - We should gracefully detect rename collisions, rather than dnl violating the invariants of the symbol table. AT_XFAIL_IF([:]) AT_DATA([in], [[define(`bar', `1')define(`baz', `2')dnl renamesyms(`^ba.$', `baa') ]]) AT_CHECK_M4([in], [0], [[ ]], [ignore]) AT_CLEANUP ## ----------------- ## ## Rescanning macros ## ## ----------------- ## AT_SETUP([Rescanning macros]) dnl This is a series of tests that used to be included as undocumented tests dnl in the branch m4.texinfo. They exercise rescanning issues not stressed dnl anywhere else in the suite, but which are used by autoconf. AT_DATA([in], [[define(`x1', `len(`$1'')dnl define(`y1', ``$1')')dnl x1(`01234567890123456789')y1(`98765432109876543210') ]]) AT_CHECK_M4([in], [0], [[40 ]]) AT_DATA([in], [[define(`echo', `$@')dnl define(`foo', echo(`01234567890123456789')echo(`98765432109876543210'))dnl foo ]]) AT_CHECK_M4([in], [0], [[0123456789012345678998765432109876543210 ]]) AT_DATA([in], [[define(`a', `A')define(`echo', `$@')define(`join', `$1$2')dnl define(`abcdefghijklmnopqrstuvwxyz', `Z')dnl join(`a', `bcdefghijklmnopqrstuvwxyz') join(`a', echo(`bcdefghijklmnopqrstuvwxyz')) ]]) AT_CHECK_M4([in], [0], [[Z Z ]]) AT_DATA([in], [[define(`echo', `$@')dnl echo(echo(`01234567890123456789', `01234567890123456789') echo(`98765432109876543210', `98765432109876543210')) len((echo(`01234567890123456789', `01234567890123456789')echo(`98765432109876543210', `98765432109876543210'))) indir(`echo', indir(`echo', `01234567890123456789', `01234567890123456789') indir(`echo', `98765432109876543210', `98765432109876543210')) define(`argn', `$#')dnl define(`echo1', `-$@-')define(`echo2', `,$@,')dnl echo1(`1', `2', `3') argn(echo1(`1', `2', `3')) echo2(`1', `2', `3') argn(echo2(`1', `2', `3')) ]]) AT_CHECK_M4([in], [0], [[01234567890123456789,01234567890123456789 98765432109876543210,98765432109876543210 84 01234567890123456789,01234567890123456789 98765432109876543210,98765432109876543210 -1,2,3- 3 ,1,2,3, 5 ]]) AT_CLEANUP