autoconf-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RFC] improve autotest syntax checks


From: Eric Blake
Subject: [RFC] improve autotest syntax checks
Date: Wed, 5 Mar 2008 21:08:11 +0000 (UTC)
User-agent: Loom/3.14 (http://gmane.org/)

Based on Chris Hulbert's report of less-than-helpful error messages when 
autotest macros are invoked out-of-order [1], I wrote this patch series.  I'll 
wait a couple days before committing, unless I get a favorable review sooner.

[1] http://lists.gnu.org/archive/html/autoconf/2008-02/msg00067.html

A couple of notes on the series:
1. Factor existing code to be more compact, and easier to reuse in patch 4.
2. Turn off the hammer that lets all unquoted AT_ through the output, and 
instead insist that uses of AT_ be quoted with @&address@hidden  This makes it 
easier to 
spot intentional syntax errors, without worrying about a broken test that 
intended to cause a syntax error.
3. M4 1.4.11 (or will it be named 1.5?) is the first implementation of GNU M4 
to implement the POSIX FIFO rules on m4wrap (you can preview this with the 
argv_ref branch of m4.git).  We already scoured the autoconf codebase for any 
code that depended on LIFO ordering, and made it order-agnostic; but I needed 
LIFO ordering after all for a sane error message (with FIFO ordering, a missing 
AT_SETUP triggers the m4sugar check for balanced diversion pops prior to any 
m4_wrap issued in autotest/general.m4, giving the wrong error message).  So I 
added a way to enforce LIFO cleanup by adding an optional parameter to m4_init 
and AS_INIT, and avoiding multiple m4_wrap.
4. The actual patch.  Had this been in place earlier, Chris's original problem 
would have been immediately diagnosed as calling AT_CHECK prior to AT_SETUP, 
rather than a vague AT_line being undefined.

Apologies if gmane munges the wrapping on this series.

2008-03-05  Eric Blake  <address@hidden>

        Improve error messages for common testsuite bugs.
        * lib/autotest/general.m4 (_AT_DEFINE_INIT, _AT_DEFINE_SETUP): New
        macros for defining order-enforced macros.
        (AT_INIT, AT_SETUP, AT_CLEANUP, AT_BANNER, AT_XFAIL_IF)
        (AT_CAPTURE_FILE, AT_DATA, AT_CHECK, AT_CHECK_NOESCAPE): Add error
        messages when order violations are detected.
        * tests/autotest.at (AT_CHECK_AT_SYNTAX): New helper macro.
        (AT_SETUP without AT_INIT, AT_BANNER without AT_INIT)
        (AT_CLEANUP without AT_INIT, Missing AT_CLEANUP)
        (AT_CHECK without AT_SETUP, AT_DATA without AT_SETUP)
        (AT_XFAIL_IF without AT_DATA, AT_KEYWORDS without AT_SETUP,
        (AT_CLEANUP without AT_SETUP, AT_BANNER inside AT_SETUP)
        (AT_SETUP inside AT_SETUP, Multiple AT_INIT)
        (Banner-only test suite): New tests.
        Reported by Christopher Hulbert.

        Allow LIFO autom4te cleanup, regardless of m4_wrap being LIFO or FIFO.
        * doc/autoconf.texi (Diagnostic Macros) <m4_fatal>: Document
        argument.
        (Redefined M4 Macros) <m4_wrap>: Improve documentation.
        (Diversion support) <m4_init>: Add optional parameter.
        (Programming in M4sh) <AS_INIT>: Likewise.
        * lib/m4sugar/m4sugar.m4 (m4_init): Add cleanup parameter.
        * lib/m4sugar/m4sh.m4 (AS_INIT): Likewise.
        (_AS_DETECT_BETTER_SHELL): Use cleanup parameter, rather than
        m4_wrap.
        * lib/autotest/general.m4 (AT_INIT): Likewise.
        * tests/m4sh.at (AS_INIT cleanup): New test.

        Encode nested autotest data.
        * tests/autotest.at (AT_CHECK_AT_PREP): Avoid raw AT_ in output.
        (unusual file names): Likewise.
        (m4_pattern_allow): Remove loophole, to make it easier to catch
        poorly written tests.

        Factor some autotest tests.
        * tests/autotest.at (AT_CHECK_AT_PREP): New macro, to factor out
        common initialization.
        (AT_CHECK_AT, Banners, Keywords and ranges, srcdir propagation)
        (whitespace in absolute testdir, unusual file names): Use it.


-- 

>From bd23d95617088bebbfab2c0e8c8535dc18c1608d Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Wed, 5 Mar 2008 09:20:23 -0700
Subject: [PATCH] Factor some autotest tests.

* tests/autotest.at (AT_CHECK_AT_PREP): New macro, to factor out
common initialization.
(AT_CHECK_AT, Banners, Keywords and ranges, srcdir propagation)
(whitespace in absolute testdir, unusual file names): Use it.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog         |    8 ++++
 tests/autotest.at |  112 +++++++++++++++++++++++------------------------------
 2 files changed, 56 insertions(+), 64 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index cf144bf..9e45ed7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2008-03-05  Eric Blake  <address@hidden>
+
+       Factor some autotest tests.
+       * tests/autotest.at (AT_CHECK_AT_PREP): New macro, to factor out
+       common initialization.
+       (AT_CHECK_AT, Banners, Keywords and ranges, srcdir propagation)
+       (whitespace in absolute testdir, unusual file names): Use it.
+
 2008-03-04  Eric Blake  <address@hidden>
 
        Make AT_CHECK act like a simple command.
diff --git a/tests/autotest.at b/tests/autotest.at
index 78d8a8a..b193c5d 100644
--- a/tests/autotest.at
+++ b/tests/autotest.at
@@ -18,6 +18,37 @@ AT_BANNER([Autotest.])
 # 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_CHECK_AT_PREP(NAME, SUITE-CODE, [STATUS = 0], [STDOUT], [STDERR],
+#                  [DIR = .])
+# --------------------------------------------------------------------
+# Create a new testsuite named NAME that runs a minimal Autotest test suite,
+# SUITE-CODE.  Do not use `testsuite' for NAME, or the log file it generates
+# will overwrite the log that the Autoconf test produces when managing
+# this test case.  STATUS, STDOUT, and STDERR pass directly to the AT_CHECK
+# that compiles the testsuite.  DIR can specify a particular subdirectory
+# where the testsuite should live.
+m4_define([AT_CHECK_AT_PREP],
+[AT_KEYWORDS([autotest])dnl
+dnl overquote AT_dir, to make it easier to use
+m4_pushdef([AT_dir], m4_ifval([$6], [[[$6]]], [.]))dnl
+dnl
+AT_CAPTURE_FILE([$1.log])dnl
+dnl
+AT_DATA(AT_dir[/package.m4],
+[[m4_define([AT_PACKAGE_NAME],      [GNU Nonsense])
+m4_define([AT_PACKAGE_TARNAME],   [nonsense])
+m4_define([AT_PACKAGE_VERSION],   [1.0])
+m4_define([AT_PACKAGE_STRING],    [GNU Nonsense 1.0])
+m4_define([AT_PACKAGE_BUGREPORT], address@hidden)
+]])
+dnl
+AT_DATA(AT_dir[/$1.at], [$2])
+m4_ifval([$6], [(cd AT_dir])
+AT_CHECK_AUTOM4TE([--language=autotest -o $1 $1.at], [$3], [$4], [$5])
+m4_ifval([$6], [)])
+m4_popdef([AT_dir])dnl
+])
+
 # AT_CHECK_AT(TITLE, SUITE-CODE, [XFAIL-CONDITION], [STATUS = 0],
 #             [STDOUT := ignore], STDERR, [POST-TEST-CODE])
 # ---------------------------------------------------------------
@@ -27,28 +58,14 @@ AT_BANNER([Autotest.])
 # used, but it is reserved for future use.  Run POST-TEST-CODE
 # at the top level after the micro-suite has been run.
 m4_define([AT_CHECK_AT],
-[
-AT_SETUP([$1])
-AT_KEYWORDS([autotest])
-AT_CAPTURE_FILE([micro-suite.log])
+[AT_SETUP([$1])
 AT_XFAIL_IF([$3])
+AT_CHECK_AT_PREP([micro-suite], [$2])
 
-AT_DATA([package.m4],[[
-m4_define([AT_PACKAGE_NAME],      [GNU Nonsense])
-m4_define([AT_PACKAGE_TARNAME],   [nonsense])
-m4_define([AT_PACKAGE_VERSION],   [1.0])
-m4_define([AT_PACKAGE_STRING],    [GNU Nonsense 1.0])
-m4_define([AT_PACKAGE_BUGREPORT], address@hidden)
-]])
-
-AT_DATA([mysuite.at], [$2])
-
-# Do not use `testsuite' as the name of the small test suite, or the
-# log file it generates will overwrite the log that the Autoconf test
-# suite produces for this test case.
-AT_CHECK_AUTOM4TE([--language=autotest -o micro-suite mysuite.at])
-AT_CHECK([$CONFIG_SHELL ./micro-suite],       m4_default([$4], 0), [ignore], 
[$6])
-AT_CHECK([$CONFIG_SHELL ./micro-suite -v -x], m4_default([$4], 0), [ignore], 
[$6])
+AT_CHECK([$CONFIG_SHELL ./micro-suite],       m4_default([$4], 0),
+         [ignore], [$6])
+AT_CHECK([$CONFIG_SHELL ./micro-suite -v -x], m4_default([$4], 0),
+         [ignore], [$6])
 $7
 AT_CLEANUP
 ])# AT_CHECK_AT
@@ -470,12 +487,9 @@ AT_CHECK([$CONFIG_SHELL ./micro-suite -C 
 ## Banners. ##
 ## -------- ##
 AT_SETUP([Banners])
-AT_KEYWORDS([autotest])
 
-AT_DATA([b.at],
-[[m4_define([AT_PACKAGE_STRING],[b])
-m4_define([AT_PACKAGE_BUGREPORT],[devnull])
-AT_INIT
+AT_CHECK_AT_PREP([b],
+[[AT_INIT
 AT_SETUP(zero)# 1
 AT_CHECK(:)
 AT_CLEANUP
@@ -488,7 +502,6 @@ AT_SETUP(one b)# 3
 AT_CHECK(:)
 AT_CLEANUP
 
-
 AT_BANNER()
 AT_SETUP(two)# 4
 AT_CHECK(:)
@@ -502,7 +515,6 @@ AT_SETUP(three b)# 6
 AT_CHECK(:)
 AT_CLEANUP
 ]])
-AT_CHECK_AUTOM4TE([--language=autotest -o b b.at])
 
 # AT_CHECK_BANNERS(TESTSUITE-OPTIONS, PATTERN1, COUNT1, PATTERN2, COUNT2)
 m4_define([AT_CHECK_BANNERS],
@@ -536,12 +548,9 @@ AT_CLEANUP
 ## Keywords. ##
 ## --------- ##
 AT_SETUP([Keywords and ranges])
-AT_KEYWORDS([autotest])
 
-AT_DATA([k.at],
-[[m4_define([AT_PACKAGE_STRING],[k])
-m4_define([AT_PACKAGE_BUGREPORT],[devnull])
-AT_INIT
+AT_CHECK_AT_PREP([k],
+[[AT_INIT
 AT_SETUP(none)
 AT_CHECK(:)
 AT_CLEANUP
@@ -559,7 +568,6 @@ AT_KEYWORDS([key1])
 AT_CHECK(:)
 AT_CLEANUP
 ]])
-AT_CHECK_AUTOM4TE([--language=autotest -o k k.at])
 dnl check that AT_KEYWORDS does not duplicate words
 AT_CHECK([grep 'key1.*key1' k], [1])
 dnl check that -k requires an argument
@@ -623,8 +631,6 @@ AT_CLEANUP
 ## ------------------- ##
 
 AT_SETUP([srcdir propagation])
-AT_KEYWORDS([autotest])
-AT_CAPTURE_FILE([suite.log])
 
 mkdir pkg vpath-outside vpath-abs
 mkdir pkg/t pkg/vpath-inside
@@ -641,20 +647,12 @@ cd pkg
 AT_CHECK_AUTOCONF
 cd ..
 
-AT_DATA([pkg/t/suite.at],
-[[m4_define([AT_PACKAGE_NAME],    [GNU Nonsense])
-m4_define([AT_PACKAGE_TARNAME],   [nonsense])
-m4_define([AT_PACKAGE_VERSION],   [1.0])
-m4_define([AT_PACKAGE_STRING],    [GNU Nonsense 1.0])
-m4_define([AT_PACKAGE_BUGREPORT], address@hidden)
-AT_INIT([suite to check srcdir])
+AT_CHECK_AT_PREP([suite],
+[[AT_INIT([suite to check srcdir])
 AT_SETUP([my only test])
 AT_CHECK([test -f "$top_srcdir"/a && test -f "$srcdir"/b])
 AT_CLEANUP
-]])
-cd pkg/t
-AT_CHECK_AUTOM4TE([--language=autotest -o suite suite.at])
-cd ../..
+]], [], [], [], [pkg/t])
 
 # Build directory totally outside source directory.
 cd vpath-outside
@@ -699,7 +697,6 @@ AT_CLEANUP
 ## ------------------------------ ##
 
 AT_SETUP([whitespace in absolute testdir])
-AT_KEYWORDS([autotest])
 
 dir='dir with  whitespace'
 mkdir "$dir"
@@ -707,18 +704,12 @@ cd "$dir"
 wd=`pwd`
 
 AT_DATA([a])
-AT_DATA([suite.at],
-[[m4_define([AT_PACKAGE_NAME],    [GNU Nonsense])
-m4_define([AT_PACKAGE_TARNAME],   [nonsense])
-m4_define([AT_PACKAGE_VERSION],   [1.0])
-m4_define([AT_PACKAGE_STRING],    [GNU Nonsense 1.0])
-m4_define([AT_PACKAGE_BUGREPORT], address@hidden)
-AT_INIT([suite to check srcdir])
+AT_CHECK_AT_PREP([suite],
+[[AT_INIT([suite to check srcdir])
 AT_SETUP([my only test])
 AT_CHECK([test -f "$top_srcdir"/a])
 AT_CLEANUP
 ]])
-AT_CHECK_AUTOM4TE([--language=autotest -o suite suite.at])
 AT_CHECK([top_srcdir=$wd ./suite], [0], [ignore])
 AT_CHECK([top_srcdir=$wd ./suite -d], [0], [ignore])
 AT_CHECK([cd suite.dir/1 && ./run top_srcdir="$wd"], [0], [ignore], [ignore])
@@ -730,7 +721,6 @@ AT_CLEANUP
 ## ------------------ ##
 
 AT_SETUP([unusual file names])
-AT_KEYWORDS([autotest])
 
 AT_DATA_M4SUGAR([d@&address@hidden,
 [[AT_SETUP([test one])
@@ -746,16 +736,10 @@ AT_CHECK([test "]m4_dquote(AT_LINE)[" = "two  
spaces.at:2"])
 AT_CLEANUP
 ]])
 
-AT_DATA([suite.at],
-[[m4_define([AT_PACKAGE_NAME],    [GNU Nonsense])
-m4_define([AT_PACKAGE_TARNAME],   [nonsense])
-m4_define([AT_PACKAGE_VERSION],   [1.0])
-m4_define([AT_PACKAGE_STRING],    [GNU Nonsense 1.0])
-m4_define([AT_PACKAGE_BUGREPORT], address@hidden)
-AT_INIT([suite to check included file names])
+AT_CHECK_AT_PREP([suite],
+[[AT_INIT([suite to check included file names])
 m4@&address@hidden([d][nl.at])
 m4@&address@hidden([sub/two  spaces.at])
 ]])
-AT_CHECK_AUTOM4TE([--language=autotest -o suite suite.at])
 AT_CHECK([$CONFIG_SHELL ./suite], [0], [ignore])
 AT_CLEANUP
-- 
1.5.4


>From d22a6f2f1efedff8116cec0b84e9c6b14d5328c8 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Wed, 5 Mar 2008 09:32:04 -0700
Subject: [PATCH] Encode nested autotest data.

* tests/autotest.at (AT_CHECK_AT_PREP): Avoid raw AT_ in output.
(unusual file names): Likewise.
(m4_pattern_allow): Remove loophole, to make it easier to catch
poorly written tests.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog         |    6 ++++++
 tests/autotest.at |   27 ++++++++++++---------------
 2 files changed, 18 insertions(+), 15 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 9e45ed7..d8d5681 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2008-03-05  Eric Blake  <address@hidden>
 
+       Encode nested autotest data.
+       * tests/autotest.at (AT_CHECK_AT_PREP): Avoid raw AT_ in output.
+       (unusual file names): Likewise.
+       (m4_pattern_allow): Remove loophole, to make it easier to catch
+       poorly written tests.
+
        Factor some autotest tests.
        * tests/autotest.at (AT_CHECK_AT_PREP): New macro, to factor out
        common initialization.
diff --git a/tests/autotest.at b/tests/autotest.at
index b193c5d..885ecca 100644
--- a/tests/autotest.at
+++ b/tests/autotest.at
@@ -35,14 +35,14 @@ dnl
 AT_CAPTURE_FILE([$1.log])dnl
 dnl
 AT_DATA(AT_dir[/package.m4],
-[[m4_define([AT_PACKAGE_NAME],      [GNU Nonsense])
-m4_define([AT_PACKAGE_TARNAME],   [nonsense])
-m4_define([AT_PACKAGE_VERSION],   [1.0])
-m4_define([AT_PACKAGE_STRING],    [GNU Nonsense 1.0])
-m4_define([AT_PACKAGE_BUGREPORT], address@hidden)
+[[m4_define([AT@&address@hidden,      [GNU Nonsense])
+m4_define([AT@&address@hidden,   [nonsense])
+m4_define([AT@&address@hidden,   [1.0])
+m4_define([AT@&address@hidden,    [GNU Nonsense 1.0])
+m4_define([AT@&address@hidden, address@hidden)
 ]])
 dnl
-AT_DATA(AT_dir[/$1.at], [$2])
+AT_DATA(AT_dir[/$1.at], m4_bpatsubst([[$2]], [AT_], [AT@&address@hidden))
 m4_ifval([$6], [(cd AT_dir])
 AT_CHECK_AUTOM4TE([--language=autotest -o $1 $1.at], [$3], [$4], [$5])
 m4_ifval([$6], [)])
@@ -83,9 +83,6 @@ $2
 AT_CLEANUP
 ]], m4_shift2($@))])
 
-# Here documents for these tests contain forbidden macros.
-m4_pattern_allow([^AT_])
-
 # AT_NO_CMDSUBST
 # --------------
 m4_define([AT_NO_CMDSUBST],
@@ -723,17 +720,17 @@ AT_CLEANUP
 AT_SETUP([unusual file names])
 
 AT_DATA_M4SUGAR([d@&address@hidden,
-[[AT_SETUP([test one])
+[[AT@&address@hidden([test one])
 m4_pattern_allow([^dnl$])
-AT_CHECK([test "]m4_dquote(AT_LINE)[" = dn[]l.at:3])
-AT_CLEANUP
+AT@&address@hidden([test "]m4_dquote(AT@&address@hidden)[" = dn[]l.at:3])
+AT@&address@hidden
 ]])
 
 mkdir sub
 AT_DATA_M4SUGAR([sub/"two  spaces".at],
-[[AT_SETUP([test two])
-AT_CHECK([test "]m4_dquote(AT_LINE)[" = "two  spaces.at:2"])
-AT_CLEANUP
+[[AT@&address@hidden([test two])
+AT@&address@hidden([test "]m4_dquote(AT@&address@hidden)[" = "two  
spaces.at:2"])
+AT@&address@hidden
 ]])
 
 AT_CHECK_AT_PREP([suite],
-- 
1.5.4


>From ec4baa0287f0b9a6f8759d8624f001b5ca265466 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Wed, 5 Mar 2008 10:51:26 -0700
Subject: [PATCH] Allow LIFO autom4te cleanup, regardless of m4_wrap being LIFO 
or FIFO.

* doc/autoconf.texi (Diagnostic Macros) <m4_fatal>: Document
argument.
(Redefined M4 Macros) <m4_wrap>: Improve documentation.
(Diversion support) <m4_init>: Add optional parameter.
(Programming in M4sh) <AS_INIT>: Likewise.
* lib/m4sugar/m4sugar.m4 (m4_init): Add cleanup parameter.
* lib/m4sugar/m4sh.m4 (AS_INIT): Likewise.
(_AS_DETECT_BETTER_SHELL): Use cleanup parameter, rather than
m4_wrap.
* lib/autotest/general.m4 (AT_INIT): Likewise.
* tests/m4sh.at (AS_INIT cleanup): New test.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog               |   13 +++++++++++++
 doc/autoconf.texi       |   24 ++++++++++++++----------
 lib/autotest/general.m4 |    4 ++--
 lib/m4sugar/m4sh.m4     |   15 ++++++++-------
 lib/m4sugar/m4sugar.m4  |    8 +++++---
 tests/m4sh.at           |   23 +++++++++++++++++++++--
 6 files changed, 63 insertions(+), 24 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index d8d5681..3fa74ae 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,18 @@
 2008-03-05  Eric Blake  <address@hidden>
 
+       Allow LIFO autom4te cleanup, regardless of m4_wrap being LIFO or FIFO.
+       * doc/autoconf.texi (Diagnostic Macros) <m4_fatal>: Document
+       argument.
+       (Redefined M4 Macros) <m4_wrap>: Improve documentation.
+       (Diversion support) <m4_init>: Add optional parameter.
+       (Programming in M4sh) <AS_INIT>: Likewise.
+       * lib/m4sugar/m4sugar.m4 (m4_init): Add cleanup parameter.
+       * lib/m4sugar/m4sh.m4 (AS_INIT): Likewise.
+       (_AS_DETECT_BETTER_SHELL): Use cleanup parameter, rather than
+       m4_wrap.
+       * lib/autotest/general.m4 (AT_INIT): Likewise.
+       * tests/m4sh.at (AS_INIT cleanup): New test.
+
        Encode nested autotest data.
        * tests/autotest.at (AT_CHECK_AT_PREP): Avoid raw AT_ in output.
        (unusual file names): Likewise.
diff --git a/doc/autoconf.texi b/doc/autoconf.texi
index 83aec6f..8e9743d 100644
--- a/doc/autoconf.texi
+++ b/doc/autoconf.texi
@@ -10341,13 +10341,15 @@ This macro corresponds to @code{m4wrap}.
 
 Posix requires arguments of multiple @code{m4wrap} calls to be
 reprocessed at @acronym{EOF} in the same order as the original calls.
address@hidden M4 versions through 1.4.x, however, reprocess them in
-reverse order.  Your code should not depend on the order.
address@hidden M4 versions through 1.4.10, however, reprocess them in
+reverse order.  Your code should not depend on the order.  However, it
+is often the case that last-in, first-out order is desirable; this can
+be accomplished by avoiding @code{m4_wrap} and instead registering your
+cleanup code via the optional argument of @code{m4_init}.
 
 Also, Posix requires @code{m4wrap} to ignore its second and succeeding
-arguments, but @acronym{GNU} M4 versions through 1.4.x concatenate the
-arguments with intervening spaces.  Your code should not pass more than
-one argument.
+arguments, but @acronym{GNU} M4 versions concatenate the arguments with
+intervening spaces.  Your code should not pass more than one argument.
 
 You are encouraged to end @var{text} with @samp{[]}, to avoid unexpected
 token pasting between consecutive invocations of @code{m4_wrap}, as in:
@@ -10384,7 +10386,7 @@ guaranteed after @var{message}.
 @end defmac
 
 @anchor{m4_fatal}
address@hidden m4_fatal
address@hidden m4_fatal (@var{message})
 @msindex{fatal}
 Report a severe error @var{message} prefixed with the current location,
 and have @command{autom4te} die.
@@ -10527,10 +10529,11 @@ m4_divert_pop(address@hidden)dnl
 @end example
 @end defmac
 
address@hidden m4_init
address@hidden m4_init (@ovar{cleanup})
 @msindex{init}
 Initialize the M4sugar environment, setting up the default named
-diversion to be @code{KILL}.
+diversion to be @code{KILL}.  When input is exhausted, expand
address@hidden prior to any m4sugar cleanup.
 @end defmac
 
 @node Conditional constructs
@@ -11418,12 +11421,13 @@ ensures any required macros of @code{HANDLE_FOO}
 are expanded before the first test.
 @end defmac
 
address@hidden AS_INIT
address@hidden AS_INIT (@ovar{cleanup})
 @asindex{INIT}
 Initialize the M4sh environment.  This macro calls @code{m4_init}, then
 outputs the @code{#! /bin/sh} line, a notice about where the output was
 generated from, and code to sanitize the environment for the rest of the
-script.  Finally, it changes the current diversion to @code{BODY}.
+script.  Finally, it changes the current diversion to @code{BODY}.  When
+input is exhausted, expand @var{cleanup} prior to any M4sh cleanup.
 @end defmac
 
 @defmac AS_MKDIR_P (@var{file-name})
diff --git a/lib/autotest/general.m4 b/lib/autotest/general.m4
index 98cceae..a218ea5 100644
--- a/lib/autotest/general.m4
+++ b/lib/autotest/general.m4
@@ -194,7 +194,7 @@ m4_define([AT_ordinal], 0)
 m4_define([AT_banner_ordinal], 0)
 m4_define([AT_groups_all], [])
 m4_define([AT_help_all], [])
-AS_INIT[]dnl
+AS_INIT([_AT_FINISH])dnl
 m4_divert_push([DEFAULTS])dnl
 AT_COPYRIGHT(
 [Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
@@ -403,7 +403,7 @@ esac]
 # Whether -C is in effect.
 at_change_dir=false
 m4_divert_pop([DEFAULTS])dnl
-m4_wrap([m4_divert_text([DEFAULTS],
+m4_define([_AT_FINISH], [m4_divert_text([DEFAULTS],
 [
 # List of the tested programs.
 at_tested='m4_ifdef([AT_tested],
diff --git a/lib/m4sugar/m4sh.m4 b/lib/m4sugar/m4sh.m4
index 6689e4b..2a85adb 100644
--- a/lib/m4sugar/m4sh.m4
+++ b/lib/m4sugar/m4sh.m4
@@ -2,8 +2,8 @@
 # M4 sugar for common shell constructs.
 # Requires GNU M4 and M4sugar.
 #
-# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free
-# Software Foundation, Inc.
+# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+# Free Software Foundation, Inc.
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -237,7 +237,7 @@ m4_expand_once([m4_append([_AS_DETECT_SUGGESTED_BODY], [
 # <http://lists.gnu.org/archive/html/autoconf-patches/2006-03/msg00081.html>.
 #
 m4_defun_once([_AS_DETECT_BETTER_SHELL],
-[m4_wrap([m4_divert_text([M4SH-SANITIZE], [
+[m4_append([_AS_CLEANUP], [m4_divert_text([M4SH-SANITIZE], [
 AS_REQUIRE([_AS_UNSET_PREPARE])dnl
 if test "x$CONFIG_SHELL" = x; then
   AS_IF([_AS_RUN([_AS_DETECT_REQUIRED_BODY]) 2>/dev/null],
@@ -1643,11 +1643,12 @@ _AS_RUN([_AS_SHELL_FN_WORK]) || {
 ])
 
 
-# AS_INIT
-# -------
-# Initialize m4sh.
+# AS_INIT([CLEANUP])
+# ------------------
+# Initialize m4sh.  Expand CLEANUP prior to any M4sh cleanup.
 m4_define([AS_INIT],
-[m4_init
+[m4_init([_AS_CLEANUP])
+m4_define([_AS_CLEANUP], [$1[]])
 
 # Forbidden tokens and exceptions.
 m4_pattern_forbid([^_?AS_])
diff --git a/lib/m4sugar/m4sugar.m4 b/lib/m4sugar/m4sugar.m4
index cd4a1c6..bc0ee66 100644
--- a/lib/m4sugar/m4sugar.m4
+++ b/lib/m4sugar/m4sugar.m4
@@ -2213,8 +2213,10 @@ m4_if(m4_sysval, [0], [],
 ## ------------------------ ##
 
 
-# m4_init
-# -------
+# m4_init([CLEANUP])
+# ------------------
+# Initialize the m4sugar language.  Expand CLEANUP prior to any m4sugar
+# cleanup.
 m4_define([m4_init],
 [# All the M4sugar macros start with `m4_', except `dnl' kept as is
 # for sake of simplicity.
@@ -2225,7 +2227,7 @@ m4_pattern_forbid([^dnl$])
 m4_divert_push([KILL])
 
 # Check the divert push/pop perfect balance.
-m4_wrap([m4_divert_pop([])
+m4_wrap([$1[]m4_divert_pop([])
         m4_ifdef([_m4_divert_diversion],
           [m4_fatal([$0: unbalanced m4_divert_push:]_m4_divert_n_stack)])[]])
 ])
diff --git a/tests/m4sh.at b/tests/m4sh.at
index 2b55bc9..12391fe 100644
--- a/tests/m4sh.at
+++ b/tests/m4sh.at
@@ -2,8 +2,8 @@
 
 AT_BANNER([M4sh.])
 
-# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software
-# Foundation, Inc.
+# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+# Free Software Foundation, Inc.
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -758,3 +758,22 @@ ok 7
 ]])
 
 AT_CLEANUP
+
+
+## ----------------- ##
+## AS_INIT cleanup.  ##
+## ----------------- ##
+
+AT_SETUP([AS@&address@hidden cleanup])
+
+AT_DATA_M4SH([script.as], [[dnl
+AS_INIT([echo cleanup])
+echo body
+]])
+
+AT_CHECK_M4SH
+AT_CHECK([./script], [], [[body
+cleanup
+]])
+
+AT_CLEANUP
-- 
1.5.4


>From c6d4a37915d5ab448aebea9250d1db7b1f4a9c4b Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Wed, 5 Mar 2008 13:39:23 -0700
Subject: [PATCH] Improve error messages for common testsuite bugs.

* lib/autotest/general.m4 (_AT_DEFINE_INIT, _AT_DEFINE_SETUP): New
macros for defining order-enforced macros.
(AT_INIT, AT_SETUP, AT_CLEANUP, AT_BANNER, AT_XFAIL_IF)
(AT_CAPTURE_FILE, AT_DATA, AT_CHECK, AT_CHECK_NOESCAPE): Add error
messages when order violations are detected.
* tests/autotest.at (AT_CHECK_AT_SYNTAX): New helper macro.
(AT_SETUP without AT_INIT, AT_BANNER without AT_INIT)
(AT_CLEANUP without AT_INIT, Missing AT_CLEANUP)
(AT_CHECK without AT_SETUP, AT_DATA without AT_SETUP)
(AT_XFAIL_IF without AT_DATA, AT_KEYWORDS without AT_SETUP,
(AT_CLEANUP without AT_SETUP, AT_BANNER inside AT_SETUP)
(AT_SETUP inside AT_SETUP, Multiple AT_INIT)
(Banner-only test suite): New tests.
Reported by Christopher Hulbert.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog               |   16 ++++++++
 lib/autotest/general.m4 |   53 ++++++++++++++++++++-------
 tests/autotest.at       |   91 +++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 144 insertions(+), 16 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 3fa74ae..fbdb63a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,21 @@
 2008-03-05  Eric Blake  <address@hidden>
 
+       Improve error messages for common testsuite bugs.
+       * lib/autotest/general.m4 (_AT_DEFINE_INIT, _AT_DEFINE_SETUP): New
+       macros for defining order-enforced macros.
+       (AT_INIT, AT_SETUP, AT_CLEANUP, AT_BANNER, AT_XFAIL_IF)
+       (AT_CAPTURE_FILE, AT_DATA, AT_CHECK, AT_CHECK_NOESCAPE): Add error
+       messages when order violations are detected.
+       * tests/autotest.at (AT_CHECK_AT_SYNTAX): New helper macro.
+       (AT_SETUP without AT_INIT, AT_BANNER without AT_INIT)
+       (AT_CLEANUP without AT_INIT, Missing AT_CLEANUP)
+       (AT_CHECK without AT_SETUP, AT_DATA without AT_SETUP)
+       (AT_XFAIL_IF without AT_DATA, AT_KEYWORDS without AT_SETUP,
+       (AT_CLEANUP without AT_SETUP, AT_BANNER inside AT_SETUP)
+       (AT_SETUP inside AT_SETUP, Multiple AT_INIT)
+       (Banner-only test suite): New tests.
+       Reported by Christopher Hulbert.
+
        Allow LIFO autom4te cleanup, regardless of m4_wrap being LIFO or FIFO.
        * doc/autoconf.texi (Diagnostic Macros) <m4_fatal>: Document
        argument.
diff --git a/lib/autotest/general.m4 b/lib/autotest/general.m4
index a218ea5..90f2a4a 100644
--- a/lib/autotest/general.m4
+++ b/lib/autotest/general.m4
@@ -181,12 +181,29 @@ m4_define([_AT_NORMALIZE_TEST_GROUP_NUMBER],
   done'
 ])
 
+# _AT_DEFINE_INIT(NAME, [DEFINITION])
+# -----------------------------------
+# Define macro NAME to die if invoked prior to AT_INIT, and to DEFINITION
+# after AT_INIT.
+m4_define([_AT_DEFINE_INIT],
+[m4_define($@)m4_pushdef([$1], [m4_fatal([$1: missing AT_INIT detected])])dnl
+m4_append([_AT_DEFINE_INIT_LIST], [[$1]], [,])])
+
+# _AT_DEFINE_SETUP(NAME, [DEFINITION])
+# -----------------------------------
+# Define macro NAME to die if invoked outside AT_SETUP/AT_CLEANUP, and
+# to DEFINITION otherwise.
+m4_define([_AT_DEFINE_SETUP],
+[m4_define([$1], [m4_ifndef([AT_ingroup],
+ [m4_fatal([$1: missing AT_SETUP detected])])$2])])
+
 
 # AT_INIT([TESTSUITE-NAME])
 # -------------------------
 # Begin test suite.
 m4_define([AT_INIT],
-[m4_pattern_forbid([^_?AT_])
+[m4_pushdef([AT_INIT], [m4_fatal([$0: invoked multiple times])])
+m4_pattern_forbid([^_?AT_])
 m4_pattern_allow([^_AT_T_EOF$])
 m4_define([AT_TESTSUITE_NAME],
          m4_defn([AT_PACKAGE_STRING])[ test suite]m4_ifval([$1], [: $1]))
@@ -194,6 +211,7 @@ m4_define([AT_ordinal], 0)
 m4_define([AT_banner_ordinal], 0)
 m4_define([AT_groups_all], [])
 m4_define([AT_help_all], [])
+m4_foreach([AT_name], [_AT_DEFINE_INIT_LIST], [m4_popdef(m4_defn([AT_name]))])
 AS_INIT([_AT_FINISH])dnl
 m4_divert_push([DEFAULTS])dnl
 AT_COPYRIGHT(
@@ -403,7 +421,9 @@ esac]
 # Whether -C is in effect.
 at_change_dir=false
 m4_divert_pop([DEFAULTS])dnl
-m4_define([_AT_FINISH], [m4_divert_text([DEFAULTS],
+m4_define([_AT_FINISH],
+[m4_ifdef([AT_ingroup], [m4_fatal([missing AT_CLEANUP detected])])dnl
+m4_divert_text([DEFAULTS],
 [
 # List of the tested programs.
 at_tested='m4_ifdef([AT_tested],
@@ -1400,8 +1420,10 @@ $1])])# AT_COPYRIGHT
 # ---------------------
 # Start a group of related tests, all to be executed in the same subshell.
 # The group is testing what DESCRIPTION says.
-m4_define([AT_SETUP],
-[m4_ifdef([AT_keywords], [m4_undefine([AT_keywords])])
+_AT_DEFINE_INIT([AT_SETUP],
+[m4_ifdef([AT_ingroup], [m4_fatal([$0: nested AT_SETUP detected])],
+  [m4_define([AT_ingroup])])
+m4_ifdef([AT_keywords], [m4_undefine([AT_keywords])])
 m4_define([AT_capture_files], [])
 m4_define([AT_line], AT_LINE)
 m4_define([AT_xfail], [at_xfail=no])
@@ -1425,7 +1447,7 @@ m4_divert_push([TEST_SCRIPT])dnl
 # -----------------------------
 # Set up the test to be expected to fail if SHELL-EXPRESSION evaluates to
 # true (exitcode = 0).
-m4_define([AT_XFAIL_IF],
+_AT_DEFINE_SETUP([AT_XFAIL_IF],
 [dnl
 dnl Try to limit the amount of conditionals that we emit.
 m4_case([$1],
@@ -1441,7 +1463,7 @@ m4_case([$1],
 # ---------------------
 # Declare a list of keywords associated to the current test group.
 # The list is stored in lower case, since the -k option is case-insensitive.
-m4_define([AT_KEYWORDS],
+_AT_DEFINE_SETUP([AT_KEYWORDS],
 [m4_append_uniq_w([AT_keywords], m4_tolower([[$1]]))])
 
 
@@ -1449,7 +1471,7 @@ m4_define([AT_KEYWORDS],
 # ---------------------
 # If the current test group does not behave as expected, save the contents of
 # FILE in the test suite log.
-m4_define([AT_CAPTURE_FILE],
+_AT_DEFINE_SETUP([AT_CAPTURE_FILE],
 [m4_append_uniq([AT_capture_files], ["$1"], [ \
 ])])
 
@@ -1457,8 +1479,10 @@ m4_define([AT_CAPTURE_FILE],
 # AT_CLEANUP
 # ----------
 # Complete a group of related tests.
-m4_define([AT_CLEANUP],
-[m4_append([AT_help_all],
+_AT_DEFINE_INIT([AT_CLEANUP],
+[m4_ifdef([AT_ingroup], [m4_undefine([AT_ingroup])],
+  [m4_fatal([$0: missing AT_SETUP detected])])dnl
+m4_append([AT_help_all],
 m4_defn([AT_ordinal]);m4_defn([AT_line]);m4_defn([AT_description]);dnl
 m4_ifdef([AT_keywords], [m4_defn([AT_keywords])]);
 )dnl
@@ -1483,8 +1507,9 @@ m4_divert_pop([TEST_GROUPS])dnl Back to KILL.
 # Start a category of related test groups.  If multiple groups are executed,
 # output TEXT as a banner without any shell expansion, prior to any test
 # from the category.  If TEXT is empty, no banner is printed.
-m4_define([AT_BANNER],
-[m4_define([AT_banner_ordinal], m4_incr(AT_banner_ordinal))
+_AT_DEFINE_INIT([AT_BANNER],
+[m4_ifdef([AT_ingroup], [m4_fatal([$0: nested AT_SETUP detected])])dnl
+m4_define([AT_banner_ordinal], m4_incr(AT_banner_ordinal))
 m4_divert_text([BANNERS],
 address@hidden:@ Banner AT_banner_ordinal. AT_LINE
 @%:@ Category starts at test group m4_incr(AT_ordinal).
@@ -1498,7 +1523,7 @@ at_banner_text_[]AT_banner_ordinal="AS_ESCAPE([$1])"])dnl
 # an end of line.
 # This macro is not robust to active symbols in CONTENTS *on purpose*.
 # If you don't want CONTENTS to be evaluated, quote it twice.
-m4_define([AT_DATA],
+_AT_DEFINE_SETUP([AT_DATA],
 [cat >$1 <<'_ATEOF'
 $2[]_ATEOF
 ])
@@ -1544,7 +1569,7 @@ $2[]_ATEOF
 # out, since most shells when tracing include subshell traces in stderr.
 # This may cause spurious failures when the test suite is run with `-x'.
 #
-m4_define([AT_CHECK],
+_AT_DEFINE_SETUP([AT_CHECK],
 [_AT_CHECK([$1],[$2],[$3],[$4],[$5],[$6],1)])
 
 # AT_CHECK_NOESCAPE(COMMANDS, [STATUS = 0], STDOUT, STDERR,
@@ -1552,7 +1577,7 @@ m4_define([AT_CHECK],
 # ---------------------------------------------------------
 # Like AT_CHECK, but do not AS_ESCAPE shell metacharacters in the STDOUT
 # and STDERR arguments before running the comparison.
-m4_define([AT_CHECK_NOESCAPE],
+_AT_DEFINE_SETUP([AT_CHECK_NOESCAPE],
 [_AT_CHECK([$1],[$2],[$3],[$4],[$5],[$6])])
 
 
diff --git a/tests/autotest.at b/tests/autotest.at
index 885ecca..5d0cec8 100644
--- a/tests/autotest.at
+++ b/tests/autotest.at
@@ -81,7 +81,20 @@ AT_INIT([artificial test suite])
 AT_SETUP([my only test])
 $2
 AT_CLEANUP
-]], m4_shift2($@))])
+]], m4_shift2($@))]) # AT_CHECK_AT_TEST
+
+# AT_CHECK_AT_SYNTAX(TITLE, SUITE, MESSAGE)
+# -----------------------------------------
+# Create a test named TITLE that tries compiling SUITE with syntax
+# errors with autotest.  Expect a failed compilation, and grep for
+# MESSAGE occuring within the error output.
+m4_define([AT_CHECK_AT_SYNTAX],
+[AT_SETUP([$1])
+AT_CHECK_AT_PREP([micro-suite], [$2], [1], [], [stderr])
+AT_CHECK([grep '$3' stderr], [0], [ignore])
+AT_CLEANUP
+])# AT_CHECK_AT_SYNTAX
+
 
 # AT_NO_CMDSUBST
 # --------------
@@ -109,12 +122,86 @@ AT_CHECK_AT([Empty test suite],
 [[AT_INIT([empty test suite])
 ]])
 
+AT_CHECK_AT([Banner-only test suite],
+[[AT_INIT([empty test suite])
+AT_BANNER([banner])
+]])
+
 # Next level of emptiness.
 AT_CHECK_AT_TEST([Empty test], [])
 
 # And finally, an empty check should not cause a syntax error.
 AT_CHECK_AT_TEST([Empty check], [AT_CHECK])
 
+# Check for sensible error messages for common bugs.
+AT_CHECK_AT_SYNTAX([AT@&address@hidden without AT@&address@hidden,
+[[AT_SETUP([only test])
+AT_CHECK([:])
+AT_CLEANUP
+]], [AT@&address@hidden: missing AT@&address@hidden detected])
+
+AT_CHECK_AT_SYNTAX([AT@&address@hidden without AT@&address@hidden,
+[[AT_BANNER([just a banner])
+]], [AT@&address@hidden: missing AT@&address@hidden detected])
+
+AT_CHECK_AT_SYNTAX([AT@&address@hidden without AT@&address@hidden,
+[[AT_CLEANUP
+]], [AT@&address@hidden: missing AT@&address@hidden detected])
+
+AT_CHECK_AT_SYNTAX([Missing AT@&address@hidden,
+[[AT_INIT([incomplete test suite])
+AT_SETUP([only test])
+AT_CHECK([:])
+]], [missing AT@&address@hidden detected])
+
+AT_CHECK_AT_SYNTAX([AT@&address@hidden without AT@&address@hidden,
+[[AT_INIT([incomplete test suite])
+AT_CHECK([:])
+]], [AT@&address@hidden: missing AT@&address@hidden detected])
+
+AT_CHECK_AT_SYNTAX([AT@&address@hidden without AT@&address@hidden,
+[[AT_INIT([incomplete test suite])
+AT_DATA([file])
+]], [AT@&address@hidden: missing AT@&address@hidden detected])
+
+AT_CHECK_AT_SYNTAX([AT@&address@hidden without AT@&address@hidden,
+[[AT_INIT([incomplete test suite])
+AT_XFAIL_IF([:])
+]], [AT@&address@hidden: missing AT@&address@hidden detected])
+
+AT_CHECK_AT_SYNTAX([AT@&address@hidden without AT@&address@hidden,
+[[AT_INIT([incomplete test suite])
+AT_KEYWORDS([keyword])
+]], [AT@&address@hidden: missing AT@&address@hidden detected])
+
+AT_CHECK_AT_SYNTAX([AT@&address@hidden without AT@&address@hidden,
+[[AT_INIT([incomplete test suite])
+AT_CLEANUP
+]], [AT@&address@hidden: missing AT@&address@hidden detected])
+
+AT_CHECK_AT_SYNTAX([AT@&address@hidden inside AT@&address@hidden,
+[[AT_INIT([incomplete test suite])
+AT_SETUP([only test])
+AT_BANNER([banner])
+AT_CHECK([:])
+AT_CLEANUP
+]], [AT@&address@hidden: nested AT@&address@hidden detected])
+
+AT_CHECK_AT_SYNTAX([AT@&address@hidden inside AT@&address@hidden,
+[[AT_INIT([incomplete test suite])
+AT_SETUP([only test])
+ AT_SETUP([nested test])
+ AT_CHECK([:])
+ AT_CLEANUP
+AT_CHECK([:])
+AT_CLEANUP
+]], [AT@&address@hidden: nested AT@&address@hidden detected])
+
+AT_CHECK_AT_SYNTAX([Multiple AT@&address@hidden,
+[[AT_INIT([suite, take one])
+AT_INIT([repeat])
+]], [AT@&address@hidden: invoked multiple times])
+
 # Check for tested programs.  autoconf should only appear once.
 AT_CHECK_AT([Tested programs],
 [[AT_INIT([programs test suite])
-- 
1.5.4







reply via email to

[Prev in Thread] Current Thread [Next in Thread]