dnl Copyright © 2021-2022 Nick Bowler dnl dnl License WTFPL2: Do What The Fuck You Want To Public License, version 2. dnl This is free software: you are free to do what the fuck you want to. dnl There is NO WARRANTY, to the extent permitted by law. AT_BANNER([Script tests]) AT_SETUP([gen-options.awk]) AT_KEYWORDS([gen-options awk script scripts]) AT_DATA([options.def], [[--option-only --option-with-val (5) --option-with-flagval (&x, 5) --option-with-arg=ARG some "text" goes here --option-with-optional-arg[=OPTIONAL] hello -a, --option-with-sopt -b, --option-with-sopt-and-arg=SOPTARG -c, --option-with-sopt-and-optional-arg[=SOPTOPTIONAL] --option-with-arg-and-val=ARGVAL (42) --option-with-arg-and-flagval=ARGFLAGVAL (&a[1], 'x') --option-with-optional-arg-and-val[=OPTIONALARGVAL] (54) --option-with-optional-arg-and-flagval[=OPTIONALFLAGVAL] (0, 0) --with-sopt Here is a help string that has a line randomly indented # with a comment @&t@ and a blank line --with-arg=ARG do stuff with ARG --flagval ]]) AT_CHECK([$AWK -f "$builddir/scripts/gen-options.awk" options.h]) AT_DATA([context.h], [[struct option { const char *name; int has_arg; int *flag; int val; }; int x, a[5]; ]]) # test 0: sanity test AT_DATA([test0.c], [[#include "context.h" #include "options.h" static const char sopts[] = SOPT_STRING; static const struct option opts[] = { LOPTS_INITIALIZER, {0} }; int main(void) { return 0; } ]]) AT_CHECK([$CC -o test0$EXEEXT test0.c && ./test0$EXEEXT], [0], [], [ignore]) # test 1: long option names and help text AT_DATA([test1.c], [[#include #include #include "context.h" #include "options.h" static const struct option opts[] = { LOPTS_INITIALIZER }; int main(void) { unsigned i; for (i = 0; i < sizeof opts / sizeof opts[0]; i++) { struct lopt_help help = { "INVALID", "INVALID" }; if (!lopt_get_help(&opts[i], &help)) return EXIT_FAILURE; printf("--%s", opts[i].name); if (opts[i].has_arg) printf("=%s", help.arg); printf("\n%s", help.desc); if (help.desc[0]) putchar('\n'); } return 0; } ]]) # pick out interesting bits from the definitions file sed -n '/^-/s/^.*--\([[^\= @<:@]]*\).*$/\1/p' options.def >options sed -n '/^-/{ s/[[^=]]*\(=[[^@:>@ ]]*\).*$/\1/ s/^[[^=]].*// s/^=// p }' options.def >argnames AS_ECHO(["-"]) | sed -n '1s/^-.*//p 1,/^-/d t clear :clear s/^-.*//p t s/^#.*// s/^ *// t next :next N s/\n-.*// t done s/\n#.*// s/\n */\n/g t next :done s/"/\\\\"/g s/[[^\n]][[^\n]]*/\\"&\\"/g s/^\n*// s/\n*$// s/\n\n*/ /g p ' options.def - >helptext exec 3expout while read opt <&3 && read arg <&4 && read help <&5; do if test ${arg:+y}; then AS_ECHO(["--$opt=$arg"]) >&6 else AS_ECHO(["--$opt"]) >&6 fi eval "set x $help"; shift for arg do AS_ECHO(["$arg"]) >&6 done done exec 3<&- 4<&- 5<&- 6>&- AT_CHECK([$CC -o test1$EXEEXT test1.c && ./test1$EXEEXT], [0], [expout], [ignore]) # test 2: short option string AT_DATA([test2.c], [[#include #include #include "context.h" #include "options.h" int main(void) { struct option lopts[] = {LOPTS_INITIALIZER}; unsigned i, j; for (i = 0; i < sizeof SOPT_STRING - 1; i++) { if (SOPT_STRING[i] != ':') { for (j = 0; j < sizeof lopts / sizeof lopts[0]; j++) { if (lopts[j].val == SOPT_STRING[i]) { printf("--%s ", lopts[j].name); break; } } } putchar(SOPT_STRING[i]); if (SOPT_STRING[i+1] != ':') putchar('\n'); } } ]]) sed -n '/^-/{ s/=.*/:/ s/[[@<:@]]/:/ s/^-\([[^-]]\)[[^:]]*/\1/p s/^-.*//p }' options.def >sopts exec 3expout while read lopt <&3 && read sopt <&4; do if test ${sopt:+y}; then AS_ECHO(["--$lopt $sopt"]) >&5 fi done exec 3<&- 4<&- 5>&- AT_CHECK([$CC -o test2$EXEEXT test2.c && ./test2$EXEEXT], [0], [expout], [ignore]) # Check that all help strings are translatable sed 's/\([[^\\]]\\\)" /\1\\n\\" /g' helptext >help-po exec 3expected.po while read opt <&3 && read arg <&4 && read help <&5; do if test ${arg:+y}; then AS_ECHO(["msgctxt \"$opt\" msgid \"$arg\""]) >&6 fi AS_ECHO(["msgctxt \"$opt\" msgid${help:+ }$help"]) >&6 done exec 3<&- 4<&- 5<&- 6>&- AT_CHECK([xgettext --keyword=PN_:1c,2 options.h test -f messages.po || exit 77]) LC_ALL=C sort expected.po >expout AT_CHECK([sed -n '/^msgctxt/{ t next :next N s/\nmsgstr.*// t done s/\s*""// s/\n/ / t next :done p }' messages.po | LC_ALL=C sort], [0], [expout]) AT_CLEANUP AT_SETUP([gen-strtab.awk]) AT_KEYWORDS([gen-strtab awk script scripts]) AT_DATA([test.def], [[ &a world &b hello world &c hello world &d world\n &e \\not a newline &f \not a newline &g inline continued &h no\ newline\ &i \ leading whitespace &j oneline # with a comment ]]) AT_CHECK([$AWK -f "$builddir/scripts/gen-strtab.awk" test.h]) sed -n 's/^[[&]]\([[^ ]]*\).*/\1/p' test.def >identifiers # test 0: sanity test AT_DATA([test0.c], [[#include "test.h" #include int main(void) { printf("---\n"); ]]) exec 3>test0.c while read ident <&3; do AS_ECHO([' printf("%s\n---\n", '"strtab+$ident);"]) >&4 done AS_ECHO([' return 0;']) >&4 AS_ECHO(['}']) >&4 exec 3<&- 4>&- AT_CHECK([$CC -o test0$EXEEXT test0.c && ./test0$EXEEXT], [0], [--- world --- hello world --- hello world --- world --- \not a newline --- ot a newline --- inline continued --- nonewline --- leading whitespace --- oneline --- ], [ignore]) AT_CLEANUP AT_SETUP([gen-tree.awk]) AT_KEYWORDS([gen-tree awk script scripts]) AT_DATA([tree.def], [[ROOT0 r0a, r0a_OFFSET r0b, r0b_OFFSET r0c r0d r0e, r0e_OFFSET r0f r0g ROOT1 r1a, r1a_OFFSET r1b, r1b_OFFSET r1b r1e r1b r1c, r1c_OFFSET r1d, r1d_OFFSET r1e r1b r1e ]]) AT_CHECK([$AWK -f "$builddir/scripts/gen-tree.awk" tree.h]) AT_DATA([test0.c], [[#include "tree.h" #include struct tree { unsigned id, subtree; }; static const struct tree tree0[] = { ROOT0_INITIALIZER }; static const struct tree tree1[] = { ROOT1_INITIALIZER }; void print_subtree(const struct tree *root, unsigned offset, int depth) { const struct tree *node; for (node = &root[offset]; node->id; node++) { printf("%*s%s", 2*depth, "", &tree_strtab[node->id]); if (node->subtree) { printf(", %s_OFFSET\n", &tree_strtab[node->id]); print_subtree(root, node->subtree, depth+1); } else { putchar('\n'); } } } int main(void) { printf("ROOT0\n"); print_subtree(tree0, 0, 1); printf("ROOT1\n"); print_subtree(tree1, 0, 1); return 0; } ]]) cp tree.def expout AT_CHECK([$CC -o test0$EXEEXT test0.c && ./test0$EXEEXT], [0], [expout]) AT_CLEANUP # Test the gen-tree features that avoid creating string labels for nodes. AT_SETUP([gen-tree.awk @nostrtab option]) AT_KEYWORDS([gen-tree awk script scripts]) AT_DATA([tree.def], [[@nostrtab ROOT a 1, a_OFFSET b 1 c 2 d 2, d_OFFSET e 1 f 2 ]]) AT_CHECK([$AWK -f "$builddir/scripts/gen-tree.awk" tree.h]) AT_DATA([test0.c], [[float tree_strtab = 0; #define a [] #define b [] #define c [] #define e [] #define f [] #include "tree.h" #include static struct { int num, offset; } root[] = { ROOT_INITIALIZER }; int main(void) { unsigned i; for (i = 0; i < sizeof root / sizeof root[0]; i++) { printf("%d, %d\n", root[i].num, root[i].offset); } } ]]) AT_CHECK([$CC -o test0$EXEEXT test0.c && ./test0$EXEEXT], [0], [[1, 3 2, 6 0, 0 1, 0 2, 0 0, 0 1, 0 2, 0 0, 0 ]]) AT_DATA([flat.def], [[FLAT a 1 b 2 c 3 @nostrtab ]]) AT_CHECK([$AWK -f "$builddir/scripts/gen-tree.awk" flat.h]) sed -e 's/tree\.h/flat.h/' -e 's/ROOT/FLAT/' test0.c >test1.c AT_CHECK([$CC -o test1$EXEEXT test1.c && ./test1$EXEEXT], [0], [[1, 0 2, 0 3, 0 0, 0 ]]) AT_CLEANUP AT_SETUP([join.awk]) AT_KEYWORDS([join awk script scripts]) JOIN="$AWK -f $builddir/scripts/join.awk --" AT_DATA([a], [[1 a 3 a1 x 3 a2 x 5 a 6 a 8 a1 x 8 a2 x 9 a1 9 a2 9 a3 ]]) AT_DATA([b], [[2 b 2 b 3 b y 4 b 6 b1 y 6 b2 y 7 b 8 b1 y 8 b2 y ]]) AT_CHECK([$JOIN a b], [0], [[3 a1 x b y 3 a2 x b y 6 a b1 y 6 a b2 y 8 a1 x b1 y 8 a1 x b2 y 8 a2 x b1 y 8 a2 x b2 y ]]) AT_CHECK([$JOIN -v1 a b], [0], [[1 a 5 a 9 a1 9 a2 9 a3 ]]) AT_CHECK([$JOIN -v2 a b], [0], [[2 b 2 b 4 b 7 b ]]) AT_CHECK([$JOIN -v1 -v2 a b], [0], [[1 a 2 b 2 b 4 b 5 a 7 b 9 a1 9 a2 9 a3 ]]) AT_CHECK([$JOIN -a1 a b], [0], [[1 a 3 a1 x b y 3 a2 x b y 5 a 6 a b1 y 6 a b2 y 8 a1 x b1 y 8 a1 x b2 y 8 a2 x b1 y 8 a2 x b2 y 9 a1 9 a2 9 a3 ]]) AT_CHECK([$JOIN -a2 a b], [0], [[2 b 2 b 3 a1 x b y 3 a2 x b y 4 b 6 a b1 y 6 a b2 y 7 b 8 a1 x b1 y 8 a1 x b2 y 8 a2 x b1 y 8 a2 x b2 y ]]) AT_CHECK([$JOIN -a1 -a2 a b], [0], [[1 a 2 b 2 b 3 a1 x b y 3 a2 x b y 4 b 5 a 6 a b1 y 6 a b2 y 7 b 8 a1 x b1 y 8 a1 x b2 y 8 a2 x b1 y 8 a2 x b2 y 9 a1 9 a2 9 a3 ]]) AT_CHECK([$JOIN b a], [0], [[3 b y a1 x 3 b y a2 x 6 b1 y a 6 b2 y a 8 b1 y a1 x 8 b1 y a2 x 8 b2 y a1 x 8 b2 y a2 x ]]) AT_CHECK([$JOIN -v1 b a], [0], [[2 b 2 b 4 b 7 b ]]) AT_CHECK([$JOIN -v2 b a], [0], [[1 a 5 a 9 a1 9 a2 9 a3 ]]) AT_CHECK([$JOIN -v1 -v2 b a], [0], [[1 a 2 b 2 b 4 b 5 a 7 b 9 a1 9 a2 9 a3 ]]) AT_CHECK([$JOIN -a1 b a], [0], [[2 b 2 b 3 b y a1 x 3 b y a2 x 4 b 6 b1 y a 6 b2 y a 7 b 8 b1 y a1 x 8 b1 y a2 x 8 b2 y a1 x 8 b2 y a2 x ]]) AT_CHECK([$JOIN -a2 b a], [0], [[1 a 3 b y a1 x 3 b y a2 x 5 a 6 b1 y a 6 b2 y a 8 b1 y a1 x 8 b1 y a2 x 8 b2 y a1 x 8 b2 y a2 x 9 a1 9 a2 9 a3 ]]) AT_CHECK([$JOIN -a1 -a2 b a], [0], [[1 a 2 b 2 b 3 b y a1 x 3 b y a2 x 4 b 5 a 6 b1 y a 6 b2 y a 7 b 8 b1 y a1 x 8 b1 y a2 x 8 b2 y a1 x 8 b2 y a2 x 9 a1 9 a2 9 a3 ]]) AT_CHECK([echo wat | $JOIN -v1 - /dev/null], [0], [[wat ]]) AT_CLEANUP m4_divert_push([PREPARE_TESTS])dnl test_fix_ltdl () { $PERL -e 'my $x = 42; exit $x;'; test $? = 42 || exit 77 $PERL -f "$srcdir/scripts/fix-ltdl.pl" "$@" } test_fix_gnulib () { $PERL -e 'my $x = 42; exit $x;'; test $? = 42 || exit 77 $PERL -f "$srcdir/scripts/fix-gnulib.pl" "$@" } test_gnulib_mk () { echo; for arg do sed -n -f - "$srcdir/tests/data/gnulib.mk" <test.mk.in AT_CHECK([grep SED_HEADER test.mk.in >expout || exit 99]) AT_CHECK([test_fix_gnulib -i test.mk.in -o test.mk || exit grep SED_HEADER test.mk], [0], [expout]) AT_CLEANUP AT_SETUP([fix-gnulib.pl %reldir% substitution]) test_gnulib_mk sys_types >test.mk.in AT_CHECK([grep '%reldir%' test.mk.in >/dev/null || exit 99]) sed -n -f - test.mk.in >expout <<'EOF' ${G;p;b} /^## begin gnulib/,/^## end gnulib/!b /^#/{p;b} s|(srcdir)|(top_srcdir)| s|%reldir%|lib| s|BUILT_SOURCES|gnulib_core_headers| s|sys[[/_]]|lib/&|g /^MOSTLYCLEANFILES/{h;b} p EOF AT_CHECK([test_fix_gnulib -i test.mk.in -o test.mk || exit sed -n -e '/^## begin gnulib/,/^## end gnulib/p' \ -e '/CLEANFILES/p' test.mk], [0], [expout]) AT_CLEANUP AT_SETUP([fix-gnulib.pl warning removal]) AT_DATA([test.mk.in], [[ ## test begin noinst_LTLIBRARIES += libgnu.la libgnu_la_CFLAGS = $(AM_CFLAGS) $(GL_CFLAG_GNULIB_WARNINGS) noinst_LIBRARIES += libgnu.a libgnu_a_CFLAGS = $(AM_CFLAGS) $(GL_CFLAG_GNULIB_WARNINGS) ## test end ]]) AT_CHECK([test_fix_gnulib -i test.mk.in -o test.mk || exit sed -n '/^## test begin/,/^## test end/p' test.mk], [0], [## test begin EXTRA_LTLIBRARIES += libgnu.la EXTRA_LIBRARIES += libgnu.a ## test end ]) AT_CLEANUP AT_SETUP([fix-gnulib.pl header directory creation]) AT_DATA([extract.sed], [[/AM_V_GEN/b ok /gl_V_at/b ok s/:.*/:/ h b :ok s/'//g x G p n s/[)].*/)/ p ]]) test_gnulib_mk alloca-opt sys_types stddef >test.mk.in AT_CHECK([test_fix_gnulib -i test.mk.in -o test.mk || exit sed -n -f extract.sed test.mk], [0], [[lib/alloca.h: $(AM_V_GEN)$(MKDIR_P) lib $(AM_V_at) lib/sys/types.h: $(AM_V_GEN)$(MKDIR_P) lib/sys $(AM_V_at) lib/stddef.h: $(AM_V_GEN)$(MKDIR_P) lib $(AM_V_at) ]]) AT_CLEANUP dnl TEST_FIND_AUTOMAKE_VER([to-check], [test-action]) dnl dnl For each whitespace-separated version token in to-check, check if we can dnl run the programs automake-VER and aclocal-VER. The special token 'default' dnl also checks the unversioned automake and aclocal (or, if set in the dnl environment, $AUTOMAKE and $ACLOCAL). dnl dnl Then test-action is expanded such that shell variables $ac and $am refer to dnl the aclocal and automake programs, and $amver is the actual version dnl reported by --version. The action should do nothing if the version is dnl acceptable, or "continue" if the version is unacceptable. dnl dnl If an acceptable version is found, the AUTOMAKE and ACLOCAL environment dnl variables are set accordingly. Otherwise, the test group is skipped. m4_define([TEST_FIND_AUTOMAKE], [have_am=false for am in $1; do AS_CASE([$am], [default], [ac=${ACLOCAL-aclocal} am=${AUTOMAKE-automake}], [ac=aclocal-$am; am=automake-$am]) amver=`$am --version | sed -n '1s/.* //p'` acver=`$ac --version | sed -n '1s/.* //p'` set x $amver $acver; shift; test x"$[]#" = x"2" || continue test x"$amver" = x"$acver" || continue $2 have_am=:; break done AT_CHECK([$have_am || exit 77]) AUTOMAKE=$am; export AUTOMAKE ACLOCAL=$ac; export ACLOCAL AT_CHECK([$ACLOCAL --version && $AUTOMAKE --version], [0], [stdout]) ]) m4_define([TEST_LTDL_LIBOBJ_MANGLING], [TEST_CONFIGURE_AC([[AM_INIT_AUTOMAKE([foreign subdir-objects]) AC_PROG_CC LT_INIT AC_SUBST([ltdl_LTLIBOBJS], [libltdl/test.lo]) AC_CONFIG_FILES([Makefile]) ]]) mkdir libltdl AT_DATA([ltdl.mk.in], [[ AM_CPPFLAGS += -DSTRING=\"helloworld\" noinst_LTLIBRARIES = libltdl/libltdl.la libltdl_libltdl_la_SOURCES = libltdl/ltdl.c libltdl_libltdl_la_LIBADD = $(ltdl_LTLIBOBJS) libltdl_libltdl_la_DEPENDENCIES = $(ltdl_LTLIBOBJS) EXTRA_DIST += libltdl/test.c ]]) AT_DATA([Makefile.am], [[AM_CPPFLAGS = include $(top_srcdir)/ltdl.mk AM_LIBTOOLFLAGS = --quiet bin_PROGRAMS = test test_SOURCES = test_LDADD = libltdl/libltdl.la all-local: ; @printf '%s\n' $(AM_CPPFLAGS) ]]) AT_DATA([libltdl/test.c], [[#include int foo(void) { printf("%s\n", STRING); return 0; } ]]) AT_DATA([libltdl/ltdl.c], [[int foo(void); int main(void) { return foo(); } ]]) AT_CHECK([test_fix_ltdl -i ltdl.mk.in -o ltdl.mk]) libtoolize; TEST_AUTORECONF TEST_CONFIGURE([--disable-shared]) AT_CHECK([make -s && ./test], [0], [ helloworld ])]) AT_SETUP([fix-ltdl.pl LIBOBJ mangling (=automake-1.16)]) TEST_FIND_AUTOMAKE([default 1.16 1.17 1.18 1.19], [AS_VERSION_COMPARE(["$amver"], [1.16], [continue])]) TEST_LTDL_LIBOBJ_MANGLING AT_CLEANUP