| # 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 <http://www.gnu.org/licenses/>. |
| |
| 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', <define>) -> `' |
| m4trace: -1- define(`my_define', <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', <define>) -> `' |
| m4trace: -1- define(`my_define', <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 |