| # Hand crafted tests for GNU M4. -*- Autotest -*- |
| # Copyright (C) 2001, 2006, 2007, 2008 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([Composite macros and other tests.]) |
| |
| |
| ## ---------- ## |
| ## capitalize ## |
| ## ---------- ## |
| |
| AT_TEST_M4([Capitalize], |
| [[dnl |
| dnl convert to upper- resp. lowercase |
| define(`upcase', `translit(`$*', `a-z', `A-Z')') |
| define(`downcase', `translit(`$*', `A-Z', `a-z')') |
| upcase(`Convert to upper case') |
| downcase(`Convert To LOWER Case') |
| dnl |
| dnl capitalize a single word |
| define(`capitalize1', `regexp(`$1', `^\(\w\)\(\w*\)', `upcase(`\1')`'downcase(`\2')')') |
| define(`capitalize', `patsubst(`$1', `\w+', ``'capitalize1(`\&')')') |
| capitalize(`This sentence should be capitalized') |
| ]], |
| [[ |
| |
| CONVERT TO UPPER CASE |
| convert to lower case |
| |
| |
| This Sentence Should Be Capitalized |
| ]]) |
| |
| |
| |
| ## -------- ## |
| ## comments ## |
| ## -------- ## |
| |
| AT_TEST_M4([Comments], |
| [[# An ordinary comment |
| define(`foo', # A comment in a macro |
| `Macro `foo' expansion') |
| foo |
| define(`comment', `*** Macro `comment' expansion ***') |
| changecom(`@', `@') |
| foo |
| ]], |
| [[# An ordinary comment |
| |
| # A comment in a macro |
| Macro foo expansion |
| |
| |
| # A *** Macro comment expansion *** in a macro |
| Macro foo expansion |
| ]]) |
| |
| |
| ## --------- ## |
| ## countdown ## |
| ## --------- ## |
| |
| AT_SETUP([countdown]) |
| |
| AT_DATA([[exp.m4]], |
| [[define(`countdown', `$1 |
| ifelse(eval($1 > 0), 1, `countdown(decr($1))', `Done')')dnl |
| countdown(7) |
| ]]) |
| |
| AT_CHECK_M4([exp.m4], 0, |
| [[7 |
| 6 |
| 5 |
| 4 |
| 3 |
| 2 |
| 1 |
| 0 |
| Done |
| ]]) |
| |
| AT_CLEANUP |
| |
| |
| ## ------- ## |
| ## foreach ## |
| ## ------- ## |
| |
| AT_SETUP([foreach]) |
| |
| AT_DATA([[foreach.m4]], |
| [[divert(-1) |
| # foreach(x, (item_1, item_2, ..., item_n), stmt) |
| define(`foreach', `pushdef(`$1', `')_foreach($@)popdef(`$1')') |
| define(`_arg1', ``$1'') |
| define(`_foreach', |
| `ifelse($2, `()', , |
| `define(`$1', `_arg1$2')$3`'_foreach(`$1', `(shift$2)', `$3')')') |
| |
| # traceon(`define', `foreach', `_foreach', `ifelse') |
| |
| define(a, 1) |
| define(b, 2) |
| define(c, 3) |
| divert |
| foreach(`x', `(foo, bar, foobar)', `Word was: x |
| ') |
| |
| # Quote torture from Akim Demaille <akim@epita.fr> |
| foreach(`x', `(`a', `(b', `c)')', `Word was: x |
| ') |
| |
| # Something more complex, from Pierre Gaumond <gaumondp@ere.umontreal.ca>. |
| define(`case', ` $1) |
| $2=" -$1";; |
| ')dnl |
| define(`_cat', `$1$2')dnl |
| `case' "$1" in |
| foreach(`x', ((a, vara), (b, varb), (c, varc)), `_cat(`case', x)')dnl |
| esac |
| ]]) |
| |
| AT_CHECK_M4([foreach.m4], 0, |
| [[ |
| Word was: foo |
| Word was: bar |
| Word was: foobar |
| |
| |
| # Quote torture from Akim Demaille <akim@epita.fr> |
| Word was: a |
| Word was: (b |
| Word was: c) |
| |
| |
| # Something more complex, from Pierre Gaumond <gaumondp@ere.umontreal.ca>. |
| case "$1" in |
| 1) |
| vara=" -1";; |
| 2) |
| varb=" -2";; |
| 3) |
| varc=" -3";; |
| esac |
| ]]) |
| |
| AT_CLEANUP |
| |
| |
| |
| ## ------- ## |
| ## forloop ## |
| ## ------- ## |
| |
| AT_SETUP([forloop]) |
| |
| AT_DATA([[forloop.m4]], |
| [[divert(-1) |
| # forloop(i, from, to, stmt) |
| |
| define(`forloop', `pushdef(`$1', `$2')_forloop(`$1', `$2', `$3', `$4')popdef(`$1')') |
| define(`_forloop', |
| `$4`'ifelse($1, `$3', , |
| `define(`$1', incr($1))_forloop(`$1', `$2', `$3', `$4')')') |
| divert |
| forloop(`x', 1, 10, `2**x = eval(2**x) |
| ') |
| ]]) |
| |
| AT_CHECK_M4([forloop.m4], 0, |
| [[ |
| 2**1 = 2 |
| 2**2 = 4 |
| 2**3 = 8 |
| 2**4 = 16 |
| 2**5 = 32 |
| 2**6 = 64 |
| 2**7 = 128 |
| 2**8 = 256 |
| 2**9 = 512 |
| 2**10 = 1024 |
| |
| ]]) |
| |
| AT_CLEANUP |
| |
| |
| |
| ## ----- ## |
| ## fstab ## |
| ## ----- ## |
| |
| AT_SETUP([fstab]) |
| |
| AT_DATA([[fstab.m4]], |
| [[define(`concat', `translit(``$*'', ` ')') |
| define(`fsent', `format(`%-25s %-16s nfs %-16s 0 0', `$1:$2', `$3', concat$4)') |
| |
| fsent(freja, /home/gevn, /home/gevn, (rw, soft, bg, grpid)) |
| fsent(freja, /home/freja, /home/freja, (rw, soft, grpid)) |
| fsent(rimfaxe, /home/rimfaxe, /home/rimfaxe, (rw, soft, bg)) |
| |
| ]]) |
| |
| AT_CHECK_M4([fstab.m4], 0, |
| [[ |
| |
| |
| freja:/home/gevn /home/gevn nfs rw,soft,bg,grpid 0 0 |
| freja:/home/freja /home/freja nfs rw,soft,grpid 0 0 |
| rimfaxe:/home/rimfaxe /home/rimfaxe nfs rw,soft,bg 0 0 |
| |
| ]]) |
| |
| AT_CLEANUP |
| |
| |
| |
| ## ----- ## |
| ## hanoi ## |
| ## ----- ## |
| |
| AT_SETUP([hanoi]) |
| |
| AT_DATA([[hanoi.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)') |
| |
| # traceon(`move', `_hanoi', `decr') |
| divert`'dnl |
| |
| hanoi(3) |
| ]]) |
| |
| AT_CHECK_M4([hanoi.m4], 0, |
| [[ |
| Move one disk from source to destination. |
| Move one disk from source to auxilliary. |
| Move one disk from destination to auxilliary. |
| Move one disk from source to destination. |
| Move one disk from auxilliary to source. |
| Move one disk from auxilliary to destination. |
| Move one disk from source to destination. |
| |
| ]]) |
| |
| AT_CLEANUP |
| |
| |
| ## ------ ## |
| ## ifndef ## |
| ## ------ ## |
| |
| AT_SETUP([ifndef]) |
| |
| dnl This catches a bug added 2008-03-13, fixed 2008-04-10. |
| AT_DATA([in.m4], |
| [[define(`ifndef', `ifdef(`$1', `$3', `$2')')dnl |
| define(`a_really_long_name', `1')dnl |
| ifdef(`divnum', `yes', `no') |
| ifndef(`divnum', `yes', `no') |
| ifdef(`ifndef', `yes', `no') |
| ifndef(`ifndef', `yes', `no') |
| ifdef(`a_really_long_name', `yes', `no') |
| ifndef(`a_really_long_name', `yes', `no') |
| ifdef(`no_such', `yes', `no') |
| ifndef(`no_such', `yes', `no') |
| ]]) |
| |
| AT_CHECK_M4([in.m4], [0], |
| [[yes |
| no |
| yes |
| no |
| yes |
| no |
| no |
| yes |
| ]]) |
| |
| AT_CLEANUP |
| |
| |
| ## ------- ## |
| ## iso8859 ## |
| ## ------- ## |
| |
| AT_SETUP([iso8859]) |
| |
| # Eh eh eh... |
| # We can't embed iso8859.m4 in here since it includes a NUL character, |
| # and we can't yet rely on autom4te being NUL-clean (even though this |
| # test shows that M4 is trying to be NUL-clean). |
| |
| AT_DATA([[expout]], |
| [[# Testing quotes |
| DEFINE # eol |
| CHANGEQUOTE(«,») # eol |
| 0 TEST # TEST |
| 1 test # test |
| 2 «test» # «test» |
| 3 ««test»» # ««test»» |
| CHANGEQUOTE(«««,»»») # eol |
| 0 TEST # TEST |
| 1 «TEST» # «TEST» |
| 2 ««TEST»» # ««TEST»» |
| 3 test # test |
| # Test use of all iso8859 characters except ^Z (win32 EOF) and NUL ` ' |
| Length of string is: 254 |
| Comparing strings: MATCH |
| # NUL passes through now! |
| 42 |
| ]]) |
| |
| AT_CHECK_M4(["$abs_srcdir/iso8859.m4"], 0, expout) |
| |
| AT_CLEANUP |
| |
| |
| ## ------------- ## |
| ## nul character ## |
| ## ------------- ## |
| |
| AT_SETUP([nul character]) |
| |
| # We don't embed null.* in here, since it is harder to guarantee the |
| # behavior of NUL through autom4te. |
| cp "$abs_srcdir/null.out" expout |
| cp "$abs_srcdir/null.err" experr |
| |
| dnl all but m4exit |
| AT_CHECK_M4([-Dm4exit "$abs_srcdir/null.m4"], [0], [expout], [experr]) |
| |
| dnl just m4exit |
| AT_CHECK_M4(["$abs_srcdir/null.m4"], [2], |
| [[# This file tests m4 behavior on NUL bytes. |
| ]]) |
| |
| AT_CLEANUP |
| |
| |
| ## --------- ## |
| ## recursion ## |
| ## --------- ## |
| |
| AT_SETUP([recursion]) |
| |
| dnl This input exploits contents of loop.m4 to print out the final value |
| dnl of the recursion. |
| AT_DATA([in.m4], |
| [[define(`debug', `define(`popdef', `divert`'i')')dnl |
| include(`loop.m4')dnl |
| ]]) |
| |
| dnl boxed recursion |
| AT_CHECK_M4([-I "$top_srcdir/examples" -Dlimit=10 -Dverbose loop.m4], [0], |
| [[ 1 2 3 4 5 6 7 8 9 10 |
| ]]) |
| AT_CHECK_M4([-I "$top_srcdir/examples" -Dlimit=2500 loop.m4], [0]) |
| AT_CHECK_M4([-I "$top_srcdir/examples" -Dlimit=10000 in.m4], [0], [[10000 |
| ]]) |
| |
| dnl unboxed recursion |
| AT_CHECK_M4([-I "$top_srcdir/examples" -Dlimit=10 -Dverbose -Dalt loop.m4], [0], |
| [[ 1 2 3 4 5 6 7 8 9 10 |
| ]]) |
| AT_CHECK_M4([-I "$top_srcdir/examples" -Dlimit=2500 -Dalt loop.m4], [0]) |
| AT_CHECK_M4([-I "$top_srcdir/examples" -Dlimit=10000 -Dalt in.m4], [0], [[10000 |
| ]]) |
| |
| AT_CLEANUP |
| |
| |
| ## ------- ## |
| ## reverse ## |
| ## ------- ## |
| |
| AT_SETUP([reverse]) |
| |
| AT_DATA([[reverse.m4]], |
| [[define(`reverse', `ifelse(eval($# > 1), 1, `reverse(shift($@)), `$1'', ``$1'')') |
| ``'' => reverse. |
| ``hej'' => reverse(hej). |
| ``hej, med, dig'' => reverse(hej, med, dig). |
| ]]) |
| |
| AT_CHECK_M4([reverse.m4], 0, |
| [[ |
| `' => . |
| `hej' => hej. |
| `hej, med, dig' => dig, med, hej. |
| ]]) |
| |
| AT_CLEANUP |
| |
| |
| ## ------------- ## |
| ## stderr closed ## |
| ## ------------- ## |
| |
| AT_SETUP([stderr closed]) |
| |
| dnl no error when stderr is not used |
| AT_CHECK_M4([2>&-], [0]) |
| |
| dnl no error when stderr is not used |
| AT_DATA([in.m4], [[hello world |
| ]]) |
| AT_CHECK_M4([2>&-], [0], [[hello world |
| ]], [], [in.m4]) |
| |
| dnl must exit nonzero when error issued |
| AT_CHECK_M4([--unknown 2>&-], [1]) |
| |
| dnl must exit nonzero if stderr used |
| AT_DATA([in.m4], [[errprint(`hello world |
| ')dnl |
| ]]) |
| AT_CHECK_M4([2>&-], [1], [], [], [in.m4]) |
| |
| dnl must exit nonzero if stderr used |
| AT_DATA([in.m4], [[hello |
| dnl(`world') |
| ]]) |
| AT_CHECK_M4([2>&-], [1], [[hello |
| ]], [], [in.m4]) |
| |
| dnl must exit nonzero on error, in spite of m4exit requesting 0 |
| AT_DATA([in.m4], [[errprint(`hello world |
| ')m4exit(`0') |
| ]]) |
| AT_CHECK_M4([2>&-], [1], [], [], [in.m4]) |
| |
| dnl preserve m4exit's failure value |
| AT_DATA([in.m4], [[errprint(`hello world |
| ')m4exit(`2') |
| ]]) |
| AT_CHECK_M4([2>&-], [2], [], [], [in.m4]) |
| |
| dnl trace file must not collide with closed stderr |
| AT_DATA([in.m4], [[errprint(`hello world |
| ')dnl |
| ]]) |
| AT_CHECK_M4([--debugfile=trace -terrprint 2>&-], [1], [], [], [in.m4]) |
| AT_CHECK([cat trace], [0], [[m4trace: -1- errprint(`hello world |
| ') -> `' |
| ]]) |
| |
| dnl spilled diversion file must not collide with closed stderr |
| AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(1)f |
| and`'dnl(not) |
| divert |
| hello`'dnl(world) |
| undivert |
| goodbye |
| ]]) |
| AT_CHECK_M4([2>&-], [1], [stdout], [], [in.m4]) |
| AT_CHECK([sed -ne '/./p' stdout], [0], |
| [[hello |
| and |
| goodbye |
| ]]) |
| |
| dnl command line input file must not collide with closed stderr |
| AT_DATA([in.m4], [[syscmd(`cat <&2')sysval |
| dnl this line should not be read by cat |
| ]]) |
| AT_CHECK_M4([2>&-], [0], [[1 |
| ]], [], [in.m4]) |
| AT_CHECK_M4([in.m4 2>&-], [0], [[1 |
| ]]) |
| |
| AT_CLEANUP |
| |
| |
| ## ------------ ## |
| ## stdin closed ## |
| ## ------------ ## |
| |
| AT_SETUP([stdin closed]) |
| |
| dnl no error when stdin is not used due to early exit |
| AT_CHECK_M4([--version], [0], [ignore], [], [-]) |
| |
| dnl no error when stdin is not used due to supplied file |
| AT_DATA([in.m4], [[hello world |
| ]]) |
| AT_CHECK_M4([in.m4], [0], [[hello world |
| ]], [], [-]) |
| |
| dnl error when stdin must be read |
| AT_CHECK_M4([], [1], [], |
| [[m4:stdin:1: error reading file `stdin' |
| m4: error closing file: Bad file descriptor |
| ]], [-]) |
| |
| dnl error when stdin must be read |
| AT_CHECK_M4([-], [1], [], |
| [[m4:stdin:1: error reading file `stdin' |
| m4: error closing file: Bad file descriptor |
| ]], [-]) |
| |
| dnl error once per command-line attempt to read stdin |
| AT_CHECK_M4([- in.m4 -], [1], [[hello world |
| ]], [[m4:stdin:1: error reading file `stdin' |
| m4:stdin:1: error reading file `stdin' |
| m4: error closing file: Bad file descriptor |
| ]], [-]) |
| |
| dnl command line and trace file must not collide with stdin |
| AT_CHECK([cat <&- && { echo "skipping: can't detect closed stdin"; exit 77; }], |
| [1], [], [stderr]) |
| mv stderr experr |
| AT_DATA([in.m4], [[syscmd(`cat')dnl |
| ]]) |
| AT_CHECK_M4([--debugfile=trace -tdnl in.m4], [0], [], [experr], [-]) |
| AT_CHECK([cat trace], [0], [[m4trace: -1- dnl -> `' |
| ]]) |
| |
| dnl diversions must not collide with stdin |
| AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(`1')f |
| syscmd(`cat')dnl |
| divert(`-1')undivert |
| ]]) |
| AT_CHECK_M4([in.m4], [0], [], [experr], [-]) |
| |
| dnl diversions must not collide with stdin |
| AT_DATA([in.m4], [M4_ONE_MEG_DEFN[hello divert(`1')f |
| ]]) |
| AT_DATA([in2.m4], [[divert(`-1')undivert |
| divert`'world |
| ]]) |
| AT_CHECK_M4([in.m4 - in2.m4], [1], [[hello world |
| ]], [[m4:stdin:1: error reading file `stdin' |
| m4: error closing file: Bad file descriptor |
| ]], [-]) |
| |
| AT_CLEANUP |
| |
| ## -------------- ## |
| ## stdin seekable ## |
| ## -------------- ## |
| |
| AT_SETUP([stdin seekable]) |
| |
| dnl POSIX requires that if stdin is seekable, m4 must seek to the location |
| dnl of unprocessed data for the benefit of other copies of the fd. |
| |
| dnl Check internal follow-on process. |
| AT_DATA([in.m4], [[syscmd(`cat')m4exit(15) |
| ]]) |
| AT_CHECK_M4([], [0], [[m4exit(15) |
| ]], [], [in.m4]) |
| |
| dnl Check external follow-on process, after m4exit. |
| AT_DATA([in.m4], [[m4exit( |
| 0)trailing data |
| ]]) |
| AT_CHECK([($M4; cat) < in.m4], [0], [[trailing data |
| ]]) |
| |
| dnl Check external follow-on process, after fatal error. |
| dnl We can't use AT_CHECK_M4, so we must post-process stderr ourselves. |
| AT_DATA([in.m4], [[dnl( |
| 0)trailing data |
| ]]) |
| AT_CHECK([($M4 -EE; cat) < in.m4], [0], [[trailing data |
| ]], [stderr]) |
| AT_CHECK([[sed 's/^[^:]*[lt-]*m4[.ex]*:/m4:/' stderr]], [0], |
| [[m4:stdin:1: Warning: dnl: extra arguments ignored: 1 > 0 |
| ]]) |
| |
| dnl Not all sed and libc combinations get the remaining tests right (for |
| dnl example, sed 4.1.4 on glibc, or cygwin 1.5.22 and earlier). |
| AT_CHECK([(sed -ne 1q; cat) < in.m4], [0], [stdout]) |
| AT_CHECK([test "x`cat stdout`" = "x0)trailing data" || \ |
| { echo "skipping: sed is too greedy on seekable stdin"; exit 77; }]) |
| |
| dnl Ensure that esyscmd resumes parsing where the child process left off. |
| AT_DATA([in.m4], [[define(`foo', `FOO')m4 foo |
| esyscmd(`sed -e "s/foo/bar/;q"')sed foo |
| m4 foo |
| ]]) |
| AT_CHECK_M4([], [0], [[m4 FOO |
| sed bar |
| m4 FOO |
| ]], [], [in.m4]) |
| |
| dnl Ensure that syscmd resumes parsing where the child process left off. |
| AT_DATA([in.m4], [[define(`foo', `FOO')m4 foo |
| syscmd(`sed -e "s/foo/bar/;q"')sed foo |
| m4 foo |
| ]]) |
| AT_CHECK_M4([], [0], [[m4 FOO |
| sed bar |
| m4 FOO |
| ]], [], [in.m4]) |
| |
| AT_CLEANUP |
| |
| ## ------------- ## |
| ## stdout closed ## |
| ## ------------- ## |
| |
| AT_SETUP([stdout closed]) |
| |
| dnl error when stdout must be used |
| AT_CHECK_M4([--version >&-], [1], [], |
| [[m4: write error: Bad file descriptor |
| ]]) |
| |
| dnl no error when stdout is not used |
| AT_CHECK_M4([>&-], [0]) |
| |
| dnl no error when stdout is not used |
| AT_DATA([in.m4], [[errprint(`hello world |
| ')dnl |
| ]]) |
| AT_CHECK_M4([>&-], [0], [], [[hello world |
| ]], [in.m4]) |
| |
| dnl error when stdout must be used |
| AT_CHECK_M4([-P >&-], [1], [], |
| [[m4: write error: Bad file descriptor |
| ]], [in.m4]) |
| |
| dnl error must occur in spite of m4exit requesting 0 |
| AT_DATA([in.m4], [[hello world |
| m4exit(`0') |
| ]]) |
| AT_CHECK_M4([>&-], [1], [], |
| [[m4:stdin:2: write error: Bad file descriptor |
| ]], [in.m4]) |
| |
| dnl preserve m4exit's failure value |
| AT_DATA([in.m4], [[hello world |
| m4exit(`2') |
| ]]) |
| AT_CHECK_M4([>&-], [2], [], |
| [[m4:stdin:2: write error: Bad file descriptor |
| ]], [in.m4]) |
| |
| dnl trace file must not collide with closed stdout |
| AT_DATA([in.m4], [[hello world |
| dnl |
| ]]) |
| AT_CHECK_M4([--debugfile=trace -tdnl >&-], [1], [], |
| [[m4: write error: Bad file descriptor |
| ]], [in.m4]) |
| AT_CHECK([cat trace], [0], [[m4trace: -1- dnl -> `' |
| ]]) |
| |
| dnl esyscmd always has valid stdout |
| AT_DATA([in.m4], [[errprint(esyscmd(`echo hello'))dnl |
| ]]) |
| AT_CHECK_M4([>&-], [0], [], [[hello |
| ]], [in.m4]) |
| |
| dnl syscmd inherits closed stdout |
| AT_DATA([hi], [[hi |
| ]]) |
| AT_CHECK([cat hi >&- && { echo "skipping: can't detect closed stdout"; exit 77; }], |
| [1], [], [stderr]) |
| mv stderr experr |
| AT_DATA([in.m4], [[syscmd(`cat hi')dnl |
| ]]) |
| AT_CHECK_M4([>&-], [0], [], [experr], [in.m4]) |
| |
| dnl spilled diversion file must not collide with closed stdout |
| AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(1)f |
| syscmd(`cat hi') |
| divert(`-1')undivert |
| ]]) |
| AT_CHECK_M4([>&-], [0], [], [experr], [in.m4]) |
| |
| dnl command line input file must not collide with closed stdout |
| AT_DATA([in.m4], [[syscmd(`cat <&1 >&2')dnl |
| dnl this line should not be read by cat |
| ]]) |
| AT_CHECK_M4([in.m4 >&-], [0], [], [stderr]) |
| AT_CHECK([sed -e 's/.*\(Bad file descriptor\)$/\1/' stderr], [0], |
| [[Bad file descriptor |
| ]]) |
| |
| AT_CLEANUP |
| |
| |
| ## ----------- ## |
| ## stdout full ## |
| ## ----------- ## |
| |
| AT_SETUP([stdout full]) |
| AT_CHECK([test -w /dev/full && test -c /dev/full || { |
| echo "Skipping: no /dev/full support"; |
| exit 77 |
| }]) |
| |
| dnl Be careful when modifying these tests. Writes that exceed stdio buffering |
| dnl limits trigger different but legal behavior where errno is lost. Tests |
| dnl that currently require "No space left on device" may fail if the amount of |
| dnl output changes. The --help test shows how to handle this. |
| |
| dnl detect write failures on --help |
| AT_CHECK_M4([--help >/dev/full], [1], [], [stderr]) |
| AT_CHECK([grep '^m4: write error' stderr], [0], [ignore]) |
| |
| dnl detect write failures on --version |
| AT_CHECK_M4([--version >/dev/full], [1], [], |
| [[m4: write error: No space left on device |
| ]]) |
| |
| dnl detect ordinary write failures |
| AT_DATA([in.m4], [[hello world |
| ]]) |
| AT_CHECK_M4([in.m4 >/dev/full], [1], [], |
| [[m4: write error: No space left on device |
| ]]) |
| |
| dnl detect stderr write failures |
| AT_DATA([in.m4], [[dnl(hello world) |
| ]]) |
| AT_CHECK_M4([in.m4 2>/dev/full], [1]) |
| |
| dnl detect trace write failures |
| AT_DATA([in.m4], [[dnl |
| ]]) |
| AT_CHECK_M4([-tdnl in.m4 2>/dev/full], [1]) |
| |
| dnl detect trace write failures |
| AT_DATA([in.m4], [[dnl |
| ]]) |
| AT_CHECK_M4([--debugfile=/dev/full -tdnl in.m4], [1], [], |
| [[m4: error writing to debug stream: No space left on device |
| ]]) |
| |
| dnl too hard to test for spilled diversion failures, without requiring the |
| dnl user to have a nearly full partition that we can assign to $TMPDIR. |
| |
| dnl write failures must override m4exit requesting 0 |
| AT_DATA([in.m4], [[hello world m4exit(`0') |
| ]]) |
| AT_CHECK_M4([in.m4 >/dev/full], [1], [], |
| [[m4:in.m4:1: write error: No space left on device |
| ]]) |
| |
| dnl preserve m4exit's failure value |
| AT_DATA([in.m4], [[hello world m4exit(`2') |
| ]]) |
| AT_CHECK_M4([in.m4 >/dev/full], [2], [], |
| [[m4:in.m4:1: write error: No space left on device |
| ]]) |
| |
| AT_CLEANUP |
| |
| |
| ## --------- ## |
| ## sysv-args ## |
| ## --------- ## |
| |
| AT_SETUP([sysv-args]) |
| |
| AT_DATA([[sysv-args.m4]], |
| [[divert(-1) |
| define(`nargs', `$#') |
| define(`concat', `ifelse(1, $#, `$1', `$1` 'concat(shift($@))')') |
| traceon(`concat', `nargs') |
| divert |
| |
| nargs |
| nargs() |
| nargs(1,2,3,4,5,6) |
| |
| concat() |
| concat(`hej', `med', `dig') |
| concat(`hej', `med', `dig', `en gang igen') |
| concat(an, awful, lot, of, argument, at, least, more, that, ten, silly, arguments) |
| ]]) |
| |
| AT_DATA([[expout]], |
| [[ |
| |
| 0 |
| 1 |
| 6 |
| |
| |
| hej med dig |
| hej med dig en gang igen |
| an awful lot of argument at least more that ten silly arguments |
| ]]) |
| |
| AT_DATA([[experr]], |
| [[m4trace: -1- nargs -> `0' |
| m4trace: -1- nargs(`') -> `1' |
| m4trace: -1- nargs(`1', `2', `3', `4', `5', `6') -> `6' |
| m4trace: -1- concat(`') -> `ifelse(1, 1, `', `` 'concat(shift(`'))')' |
| m4trace: -1- concat(`hej', `med', `dig') -> `ifelse(1, 3, `hej', `hej` 'concat(shift(`hej',`med',`dig'))')' |
| m4trace: -1- concat(`med', `dig') -> `ifelse(1, 2, `med', `med` 'concat(shift(`med',`dig'))')' |
| m4trace: -1- concat(`dig') -> `ifelse(1, 1, `dig', `dig` 'concat(shift(`dig'))')' |
| m4trace: -1- concat(`hej', `med', `dig', `en gang igen') -> `ifelse(1, 4, `hej', `hej` 'concat(shift(`hej',`med',`dig',`en gang igen'))')' |
| m4trace: -1- concat(`med', `dig', `en gang igen') -> `ifelse(1, 3, `med', `med` 'concat(shift(`med',`dig',`en gang igen'))')' |
| m4trace: -1- concat(`dig', `en gang igen') -> `ifelse(1, 2, `dig', `dig` 'concat(shift(`dig',`en gang igen'))')' |
| m4trace: -1- concat(`en gang igen') -> `ifelse(1, 1, `en gang igen', `en gang igen` 'concat(shift(`en gang igen'))')' |
| m4trace: -1- concat(`an', `awful', `lot', `of', `argument', `at', `least', `more', `that', `ten', `silly', `arguments') -> `ifelse(1, 12, `an', `an` 'concat(shift(`an',`awful',`lot',`of',`argument',`at',`least',`more',`that',`ten',`silly',`arguments'))')' |
| m4trace: -1- concat(`awful', `lot', `of', `argument', `at', `least', `more', `that', `ten', `silly', `arguments') -> `ifelse(1, 11, `awful', `awful` 'concat(shift(`awful',`lot',`of',`argument',`at',`least',`more',`that',`ten',`silly',`arguments'))')' |
| m4trace: -1- concat(`lot', `of', `argument', `at', `least', `more', `that', `ten', `silly', `arguments') -> `ifelse(1, 10, `lot', `lot` 'concat(shift(`lot',`of',`argument',`at',`least',`more',`that',`ten',`silly',`arguments'))')' |
| m4trace: -1- concat(`of', `argument', `at', `least', `more', `that', `ten', `silly', `arguments') -> `ifelse(1, 9, `of', `of` 'concat(shift(`of',`argument',`at',`least',`more',`that',`ten',`silly',`arguments'))')' |
| m4trace: -1- concat(`argument', `at', `least', `more', `that', `ten', `silly', `arguments') -> `ifelse(1, 8, `argument', `argument` 'concat(shift(`argument',`at',`least',`more',`that',`ten',`silly',`arguments'))')' |
| m4trace: -1- concat(`at', `least', `more', `that', `ten', `silly', `arguments') -> `ifelse(1, 7, `at', `at` 'concat(shift(`at',`least',`more',`that',`ten',`silly',`arguments'))')' |
| m4trace: -1- concat(`least', `more', `that', `ten', `silly', `arguments') -> `ifelse(1, 6, `least', `least` 'concat(shift(`least',`more',`that',`ten',`silly',`arguments'))')' |
| m4trace: -1- concat(`more', `that', `ten', `silly', `arguments') -> `ifelse(1, 5, `more', `more` 'concat(shift(`more',`that',`ten',`silly',`arguments'))')' |
| m4trace: -1- concat(`that', `ten', `silly', `arguments') -> `ifelse(1, 4, `that', `that` 'concat(shift(`that',`ten',`silly',`arguments'))')' |
| m4trace: -1- concat(`ten', `silly', `arguments') -> `ifelse(1, 3, `ten', `ten` 'concat(shift(`ten',`silly',`arguments'))')' |
| m4trace: -1- concat(`silly', `arguments') -> `ifelse(1, 2, `silly', `silly` 'concat(shift(`silly',`arguments'))')' |
| m4trace: -1- concat(`arguments') -> `ifelse(1, 1, `arguments', `arguments` 'concat(shift(`arguments'))')' |
| ]]) |
| |
| AT_CHECK_M4([sysv-args.m4], 0, [expout], [experr]) |
| |
| AT_CLEANUP |