# Hand crafted tests for GNU M4. -*- Autotest -*-
# Copyright (C) 2001, 2006, 2007 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([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
load(`modtest')
define(`test2', defn(`test'))dnl
->test
load(`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([-M "$abs_builddir" -m load frozen.m4 unfrozen.m4],
[0], [stdout], [stderr])
mv stdout expout
mv stderr experr
# Now freeze the first source file.
AT_CHECK_M4([-M "$abs_builddir" -m load -F frozen.m4f frozen.m4],
[0], [], [ignore])
# Now rerun the original sequence, but using the frozen file.
AT_CHECK_M4([-M "$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], [[load(`mpeval')
-__load__-__mpeval__-
unload(`mpeval')
-__load__-__mpeval__-
unload(`load')
-__load__-__mpeval__-
]])
AT_CHECK_M4([-m 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],
[[load(`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([-m load $3 input.m4], [0],
[[
Test module called.
Dumpdef: .
test
Dumpdef: .
]],
[[Test module loaded.
test:
Test module unloaded.
m4:input.m4:6: Warning: dumpdef: undefined macro `test'
]])
AT_CLEANUP
])
AT_CHECK_M4_MODTEST([--module-directory: absolute path],
[], [-M "$abs_builddir"])
AT_CHECK_M4_MODTEST([--module-directory: relative path],
[], [-M "$top_build_prefix/tests"])
AT_CHECK_M4_MODTEST([M4MODPATH: absolute path],
[M4MODPATH="$abs_builddir"], [])
AT_CHECK_M4_MODTEST([M4MODPATH: relative path],
[M4MODPATH="$top_build_prefix/tests"], [])
AT_CHECK_M4_MODTEST([LTDL_LIBRARY_PATH: absolute path],
[LTDL_LIBRARY_PATH="$abs_builddir"], [])
AT_CHECK_M4_MODTEST([LTDL_LIBRARY_PATH: relative path],
[LTDL_LIBRARY_PATH="$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
load(`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
load(`modtest')
dumpdef(`test')
dumpdef(`shadow')
test
shadow
# Reloading Shadow shouldn't affect anything
load(`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:
shadow:
Test module loaded.
test:
shadow:
test:
shadow:
Test module unloaded.
test:
shadow:
test:
shadow:
]])
AT_CHECK_M4([-M "$abs_builddir" -m load input.m4], [0],
[expout], [experr])
AT_CLEANUP
## ------ ##
## unload ##
## ------ ##
AT_SETUP([modules: unload])
AT_CHECK_DYNAMIC_MODULE
AT_DATA([[input.m4]],
[[test
__test__
load(`modtest')
test
__test__
load(`shadow')
test
__test__
unload(`modtest')
test
__test__
load(`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([-M "$abs_builddir" -m 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
load(`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'
]])
AT_CHECK_M4([-M "$abs_builddir" -m load input.m4],
[1], [expout], [experr])
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
load(`shadow')
test
unload(`shadow')
test
load(`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([-M "$abs_builddir" -m 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')load(`gnu')regexp(`123', `\(4\)?2')')
]])
AT_CHECK_M4([-m 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
load(`load') 5
__load__ 6
unload(`load') 7
__load__ 8
unload(`load') 9
__load__ 10
]])
AT_CHECK_M4([-m load in1.m4 -m load in2.m4], [0],
[[ 1
2
__load__ 3
4
5
6
7
8
9
__load__ 10
]])
AT_CLEANUP