| # 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([Module support.]) |
| |
| |
| ## --------- ## |
| ## modfreeze ## |
| ## --------- ## |
| |
| AT_SETUP([Freezing modules]) |
| AT_KEYWORDS([frozen]) |
| AT_CHECK_DYNAMIC_MODULE |
| |
| AT_DATA([[frozen.m4]], |
| [[divert(1)dnl |
| define(`test', `local::`test'')dnl |
| define(`test1', defn(`test'))dnl |
| ->test |
| include(`modtest') |
| define(`test2', defn(`test'))dnl |
| ->test |
| include(`shadow') |
| define(`test3', defn(`test'))dnl |
| ->test |
| ]]) |
| |
| AT_DATA([[unfrozen.m4]], |
| [[undivert(1)dnl |
| test1 |
| test2 |
| test3 |
| ]]) |
| |
| # First generate the `expout' ouput by running over the sources before |
| # freezing. |
| AT_CHECK_M4([-I "$abs_builddir" load frozen.m4 unfrozen.m4], |
| [0], [stdout], [stderr]) |
| |
| mv stdout expout |
| mv stderr experr |
| |
| # Now freeze the first source file. |
| AT_CHECK_M4([-I "$abs_builddir" load -F frozen.m4f frozen.m4], |
| [0], [], [ignore]) |
| |
| # Now rerun the original sequence, but using the frozen file. |
| AT_CHECK_M4([-I "$abs_builddir" -R frozen.m4f unfrozen.m4], |
| [0], [expout], [experr]) |
| |
| AT_CLEANUP([frozen.m4f]) |
| |
| |
| ## ------------------ ## |
| ## module test macros ## |
| ## ------------------ ## |
| |
| AT_SETUP([module test macros]) |
| AT_CHECK_DYNAMIC_MODULE |
| AT_CHECK_GMP |
| |
| AT_DATA([in], [[include(`mpeval') |
| -__load__-__mpeval__- |
| unload(`mpeval') |
| -__load__-__mpeval__- |
| unload(`load') |
| -__load__-__mpeval__- |
| ]]) |
| |
| AT_CHECK_M4([load in], [0], [[ |
| --- |
| |
| --__mpeval__- |
| |
| -__load__-__mpeval__- |
| ]]) |
| |
| AT_CLEANUP |
| |
| |
| ## ---------------------------- ## |
| ## Exercising the test module. ## |
| ## ---------------------------- ## |
| |
| # AT_CHECK_M4_MODTEST(TITLE, ENV-VARS, M4-OPTIONS) |
| # ------------------------------------------------ |
| # Add a test named TITLE, running m4 with either ENV-VARS in the environment |
| # or M4-OPTIONS set to pick up test modules. |
| m4_define([AT_CHECK_M4_MODTEST], |
| [AT_SETUP([$1]) |
| AT_CHECK_DYNAMIC_MODULE |
| |
| AT_DATA([input.m4], |
| [[include(`modtest') |
| test |
| Dumpdef: dumpdef(`test'). |
| unload(`modtest') |
| test |
| Dumpdef: dumpdef(`test'). |
| ]]) |
| |
| dnl Fortunately, all tests within AT_SETUP are in the same subshell, so |
| dnl setting the environment now will impact the AT_CHECK_M4, but not |
| dnl carry over to the next AT_SETUP. |
| m4_ifval([$2], [$2 |
| export m4_substr([$2], [0], m4_index([$2], [=]))]) |
| |
| AT_CHECK_M4([load $3 input.m4], [0], |
| [[ |
| Test module called. |
| Dumpdef: . |
| |
| test |
| Dumpdef: . |
| ]], |
| [[Test module loaded. |
| test: <test> |
| Test module unloaded. |
| m4:input.m4:6: Warning: dumpdef: undefined macro `test' |
| ]]) |
| |
| AT_CLEANUP |
| ]) |
| |
| |
| |
| AT_CHECK_M4_MODTEST([--include: absolute path], |
| [], [-I "$abs_builddir"]) |
| |
| AT_CHECK_M4_MODTEST([--include: relative path], |
| [], [-I "$top_build_prefix/tests"]) |
| |
| AT_CHECK_M4_MODTEST([M4PATH: absolute path], |
| [M4PATH="$abs_builddir"], []) |
| |
| AT_CHECK_M4_MODTEST([M4PATH: relative path], |
| [M4PATH="$top_build_prefix/tests"], []) |
| |
| |
| |
| ## ------ ## |
| ## shadow ## |
| ## ------ ## |
| |
| AT_SETUP([modules: shadow]) |
| AT_CHECK_DYNAMIC_MODULE |
| |
| AT_DATA([[input.m4]], |
| [[# no modules loaded yet |
| test |
| shadow |
| |
| # define our own macros for `test' and `shadow' |
| define(`test', `local::`test'') |
| define(`shadow', `local::`shadow'') |
| test |
| shadow |
| |
| # module Shadow defines `shadow' and `test' macros |
| include(`shadow') |
| dumpdef(`test') |
| dumpdef(`shadow') |
| test |
| shadow |
| |
| # save the definition of `test' from the Shadow module |
| define(`Shadow::test', defn(`test')) |
| |
| # module Modtest also defines a `test' macro |
| include(`modtest') |
| dumpdef(`test') |
| dumpdef(`shadow') |
| test |
| shadow |
| |
| # Reloading Shadow shouldn't affect anything |
| include(`shadow') |
| dumpdef(`test') |
| dumpdef(`shadow') |
| test |
| shadow |
| |
| # Unloading Modtest will unshadow the test definition in Shadow |
| unload(`modtest') |
| dumpdef(`test') |
| dumpdef(`shadow') |
| test |
| shadow |
| |
| # Unloading Shadow once has no effect (we loaded it twice) |
| unload(`shadow') |
| dumpdef(`test') |
| dumpdef(`shadow') |
| test |
| shadow |
| |
| # Unloading Shadow again will revert to copying `test' and the local |
| # `shadow' macro. |
| unload(`shadow') |
| test |
| shadow |
| ]]) |
| |
| AT_DATA([[expout]], |
| [[# no modules loaded yet |
| test |
| shadow |
| |
| # define our own macros for `test' and `shadow' |
| |
| |
| local::test |
| local::shadow |
| |
| # module Shadow defines `shadow' and `test' macros |
| Shadow module loaded. |
| |
| |
| Shadow::test called. |
| Shadow::shadow called. |
| |
| # save the definition of `test' from the Shadow module |
| |
| |
| # module Modtest also defines a `test' macro |
| |
| |
| |
| Test module called. |
| Shadow::shadow called. |
| |
| # Reloading Shadow shouldn't affect anything |
| |
| |
| |
| Test module called. |
| Shadow::shadow called. |
| |
| # Unloading Modtest will unshadow the test definition in Shadow |
| |
| |
| |
| Shadow::test called. |
| Shadow::shadow called. |
| |
| # Unloading Shadow once has no effect (we loaded it twice) |
| |
| |
| |
| Shadow::test called. |
| Shadow::shadow called. |
| |
| # Unloading Shadow again will revert to copying `test' and the local |
| # `shadow' macro. |
| |
| local::test |
| local::shadow |
| ]]) |
| |
| AT_DATA([[experr]], |
| [[test: <test> |
| shadow: <shadow> |
| Test module loaded. |
| test: <test> |
| shadow: <shadow> |
| test: <test> |
| shadow: <shadow> |
| Test module unloaded. |
| test: <test> |
| shadow: <shadow> |
| test: <test> |
| shadow: <shadow> |
| ]]) |
| |
| AT_CHECK_M4([-I "$abs_builddir" load input.m4], [0], |
| [expout], [experr]) |
| |
| AT_CLEANUP |
| |
| |
| |
| ## ------ ## |
| ## unload ## |
| ## ------ ## |
| |
| AT_SETUP([modules: unload]) |
| AT_CHECK_DYNAMIC_MODULE |
| |
| AT_DATA([[input.m4]], |
| [[test |
| __test__ |
| include(`modtest') |
| test |
| __test__ |
| include(`shadow') |
| test |
| __test__ |
| unload(`modtest') |
| test |
| __test__ |
| include(`modtest') |
| test |
| __test__ |
| unload(`modtest') |
| test |
| __test__ |
| unload(`shadow') |
| test |
| __test__ |
| ]]) |
| |
| AT_DATA([[expout]], |
| [[test |
| __test__ |
| |
| Test module called. |
| modtest |
| Shadow module loaded. |
| Shadow::test called. |
| shadow |
| |
| Shadow::test called. |
| shadow |
| |
| Test module called. |
| modtest |
| |
| Shadow::test called. |
| shadow |
| |
| test |
| __test__ |
| ]]) |
| |
| AT_DATA([[experr]], |
| [[Test module loaded. |
| Test module unloaded. |
| Test module loaded. |
| Test module unloaded. |
| ]]) |
| |
| |
| AT_CHECK_M4([-I "$abs_builddir" load input.m4], |
| [0], [expout], [experr]) |
| |
| AT_CLEANUP |
| |
| |
| |
| ## ----------------------- ## |
| ## module symbol importing ## |
| ## ----------------------- ## |
| |
| # Importing a symbol from a not yet loaded module |
| |
| # This test is ugly, because we need to canonicalize strerror strings |
| # to match our test. So we save STDERR to a file, and run another check |
| # which edits that file and compares it to the canonical STDERR output |
| # from the first command: |
| |
| AT_SETUP([modules: importing]) |
| AT_CHECK_DYNAMIC_MODULE |
| |
| AT_DATA([[input.m4]], |
| [[import |
| include(`import') |
| import |
| unload(`modtest') |
| import |
| symbol_fail |
| module_fail |
| ]]) |
| |
| AT_DATA([[expout]], |
| [[import |
| |
| import::import called. |
| |
| import::import called. |
| import::symbol_fail called. |
| ]]) |
| |
| AT_DATA([[experr]], |
| [[Test module loaded. |
| |
| TRUE |
| Test module unloaded. |
| Test module loaded. |
| |
| TRUE |
| m4:input.m4:6: cannot load symbol `no_such' from module `modtest' |
| m4:input.m4:7: cannot open module `no_such' |
| ]]) |
| |
| ls "$abs_builddir" |
| |
| AT_CHECK_M4([-I "$abs_builddir" load input.m4], |
| [1], [expout], [experr]) |
| |
| AT_CLEANUP |
| |
| |
| |
| ## ------------------- ## |
| ## text module symbols ## |
| ## ------------------- ## |
| |
| # Support text macros with requested numbers of parameters. |
| |
| AT_SETUP([modules: text]) |
| AT_CHECK_DYNAMIC_MODULE |
| |
| AT_DATA([input.m4], |
| [[__test__ |
| __test__(1) |
| __test__(1,2) |
| onearg |
| onearg(1) |
| onearg(1,2) |
| manyargs |
| manyargs(1) |
| manyargs(1,2) |
| ]]) |
| |
| AT_CHECK_M4([-I "$abs_builddir" modtest input.m4], [0], |
| [[modtest |
| modtest |
| modtest |
| |
| 1 |
| 1 |
| |
| 1 |
| 1,2 |
| ]], [[Test module loaded. |
| m4:input.m4:2: Warning: __test__: extra arguments ignored: 1 > 0 |
| m4:input.m4:3: Warning: __test__: extra arguments ignored: 2 > 0 |
| m4:input.m4:4: Warning: onearg: too few arguments: 0 < 1 |
| m4:input.m4:6: Warning: onearg: extra arguments ignored: 2 > 1 |
| Test module unloaded. |
| ]]) |
| |
| AT_CLEANUP |
| |
| |
| ## -------------------- ## |
| ## trace module symbols ## |
| ## -------------------- ## |
| |
| # The trace bit should not be lost if a builtin is unloaded from |
| # memory and then redefined by a subsequent load. |
| |
| AT_SETUP([modules: trace]) |
| AT_CHECK_DYNAMIC_MODULE |
| |
| AT_DATA([[input.m4]], |
| [[test |
| include(`shadow') |
| test |
| unload(`shadow') |
| test |
| include(`shadow') |
| test |
| ]]) |
| |
| AT_DATA([[expout]], |
| [[test |
| Shadow module loaded. |
| Shadow::test called. |
| |
| test |
| Shadow module loaded. |
| Shadow::test called. |
| ]]) |
| |
| AT_DATA([[experr]], |
| [[m4trace: -1- test -> `Shadow::`test' called.' |
| m4trace: -1- test -> `Shadow::`test' called.' |
| ]]) |
| |
| |
| AT_CHECK_M4([-I "$abs_builddir" load -t test input.m4], |
| [0], [expout], [experr]) |
| |
| AT_CLEANUP |
| |
| |
| ## ----------------- ## |
| ## unload gnu module ## |
| ## ----------------- ## |
| |
| AT_SETUP([unload gnu module]) |
| AT_CHECK_DYNAMIC_MODULE |
| |
| dnl Ensure that the gnu module does not leak memory. I don't know how |
| dnl to portably artificially limit the heap to cause an out-of-memory |
| dnl condition in the case of a leak, but examining the run of this test |
| dnl in a debugger can show whether it is leaking. |
| AT_DATA([input.m4], [[divert(-1) |
| 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')')') |
| forloop(`i', `1', `5000', `unload(`gnu')include(`gnu')regexp(`123', `\(4\)?2')') |
| ]]) |
| |
| AT_CHECK_M4([load input.m4], [0]) |
| |
| AT_CLEANUP |
| |
| |
| ## ------------------ ## |
| ## unload load module ## |
| ## ------------------ ## |
| |
| AT_SETUP([unload load module]) |
| AT_CHECK_DYNAMIC_MODULE |
| |
| dnl Ensure that the load module can be unloaded and reloaded (obviously, |
| dnl it can't reload itself; the reload occurs on the command line). Since |
| dnl the module must be resident (after all, the `unload' builtin had better |
| dnl not unmap the memory for the code it is currently executing), make sure |
| dnl that resident modules are handled gracefully. |
| AT_DATA([in1.m4], [[__load__ 1 |
| unload(`load') 2 |
| __load__ 3 |
| ]]) |
| |
| AT_DATA([in2.m4], [[__load__ 4 |
| include(`load') 5 |
| __load__ 6 |
| unload(`load') 7 |
| __load__ 8 |
| unload(`load') 9 |
| __load__ 10 |
| ]]) |
| |
| AT_CHECK_M4([load in1.m4 load in2.m4], [0], |
| [[ 1 |
| 2 |
| __load__ 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| __load__ 10 |
| ]]) |
| |
| AT_CLEANUP |