bug-gnulib
[Top][All Lists]
Advanced

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

gnulib-tool: support for unit test modules


From: Bruno Haible
Subject: gnulib-tool: support for unit test modules
Date: Thu, 25 Aug 2005 14:39:38 +0200
User-agent: KMail/1.5

Hi all,

C code generally doesn't work unless it's tested.

Simon added a test for the 'getpass' module yesterday, but no "make check"
actually runs it. So it resembles dead code. Let's change that!

Nelson Beebe has a zoo of different platforms, and now and then he reports
portability problems about a package that uses gnulib. But we have no
guarantee that all of gnulib actually works on all his platforms.
Let's change that!

I'm adding to gnulib-tool the support for unitary tests. It is activated
when you pass the option --with-tests. Then, the package created by
--create-testdir or --create-megatestdir has a "make check" that will
actually run the available tests.

When you contribute new tests, the test sources go under tests/.
They are accompanied with a metainformation file modules/<module>-tests.
The template for this file is:

  Files:
  [Here you list all test files. m4 files are also possible.]

  Depends-on:
  [The test module may depend on other modules than the particular library
  module. It may even depend on other test modules.]

  configure.ac:
  [Some autoconf tests for use by the test.]

  Makefile.am:
  [Snippet of rules for tests/Makefile.am.]

Everyone, please contribute a test for your modules! Then at some point,
we can actually create a megatestdir and have people like Nelson Beebe
perform a complete portability test on gnulib.

Bruno


        Support for unit test modules.
        * modules/README: Mention tests modules.
        * modules/TEMPLATE-TESTS: New file.
        * gnulib-tool: New options --extract-tests-module, --with-tests and
        --tests-base (unused for the moment).
        (testsbase, inctests): New variables.
        (func_all_modules): Exclude TEMPLATE-TESTS and *-tests.
        (func_verify_module): Exclude TEMPLATE-TESTS.
        (func_verify_nontests_module, func_verify_tests_module): New functions.
        (func_get_dependencies): Add implicit dependency for tests modules.
        (func_get_tests_module): New function.
        (func_modules_transitive_closure): When --with-tests was specified,
        include the unit tests as well, unless explicitly avoided.
        (func_emit_lib_Makefile_am): Ignore the tests modules here.
        (func_emit_tests_Makefile_am): New function.
        (func_create_testdir): When --with-tests was specified, emit a
        tests/ directory.
        * MODULES.html.sh (Future developments): Update.

*** modules/README.bak  2004-10-11 13:28:34.000000000 +0200
--- modules/README      2005-08-25 00:54:40.000000000 +0200
***************
*** 1,9 ****
! This directory contains metainformation about the gnulib modules, one file
! per module. These files are used by gnulib-tool.
  
  All the files in this directory are distributed under the following copyright:
  
!   Copyright (C) 2002-2004 Free Software Foundation, Inc.
    Copying and distribution of this file, with or without modification,
    in any medium, are permitted without royalty provided the copyright
    notice and this notice are preserved.
--- 1,15 ----
! This directory contains metainformation about the gnulib modules, one or two
! files per module. These files are used by gnulib-tool.
! 
! For every module,
!   - the file <module> is the metainformation about the library code of the
!     module,
!   - the file <module>-tests is the metainformation about the unit test of
!     the module (optional but recommended).
  
  All the files in this directory are distributed under the following copyright:
  
!   Copyright (C) 2002-2005 Free Software Foundation, Inc.
    Copying and distribution of this file, with or without modification,
    in any medium, are permitted without royalty provided the copyright
    notice and this notice are preserved.
*** gnulib-tool.bak     2005-08-25 02:14:54.000000000 +0200
--- gnulib-tool 2005-08-25 03:59:41.000000000 +0200
***************
*** 56,61 ****
--- 56,62 ----
         gnulib-tool --extract-include-directive module
         gnulib-tool --extract-license module
         gnulib-tool --extract-maintainer module
+        gnulib-tool --extract-tests-module module
  
  Operation modes:
        --list                print the available module names
***************
*** 76,81 ****
--- 77,83 ----
        --extract-license            report the license terms of the source 
files
                                     under lib/
        --extract-maintainer         report the maintainer(s) inside gnulib
+       --extract-tests-module       report the unit test module, if it exists
  
  Options:
        --dir=DIRECTORY       specify the target directory
***************
*** 88,95 ****
--- 90,101 ----
                              placed (default \"lib\"), for --import.
        --m4-base=DIRECTORY   Directory relative --dir where *.m4 macros are
                              placed (default \"m4\"), for --import.
+       --tests-base=DIRECTORY
+                             Directory relative --dir where unit tests are
+                             placed (default \"tests\"), for --import.
        --aux-dir=DIRECTORY   Directory relative --dir where auxiliary build
                              tools are placed (default \"build-aux\").
+       --with-tests          Include unit tests for the included modules.
        --avoid=MODULE        Avoid including the given MODULE. Useful if you
                              have code that provides equivalent functionality.
                              This option can be repeated.
***************
*** 183,189 ****
--- 189,197 ----
  # - libname, supplied_libname  from --lib
  # - sourcebase      from --source-base
  # - m4base          from --m4-base
+ # - testsbase       from --tests-base
  # - auxdir          from --aux-dir
+ # - inctests        true if --with-tests was given, blank otherwise
  # - avoidlist       list of modules to avoid, from --avoid
  # - lgpl            true if --lgpl was given, blank otherwise
  # - libtool         true if --libtool was given, blank otherwise
***************
*** 196,202 ****
--- 204,212 ----
    supplied_libname=
    sourcebase=
    m4base=
+   testsbase=
    auxdir=
+   inctests=
    avoidlist=
    lgpl=
    libtool=
***************
*** 271,276 ****
--- 281,296 ----
        --m4-base=* )
          m4base=`echo "X$1" | sed -e 's/^X--m4-base=//'`
          shift ;;
+       --tests-base )
+         shift
+         if test $# = 0; then
+           func_fatal_error "missing argument for --tests-base"
+         fi
+         testsbase=$1
+         shift ;;
+       --tests-base=* )
+         testsbase=`echo "X$1" | sed -e 's/^X--tests-base=//'`
+         shift ;;
        --aux-dir )
          shift
          if test $# = 0; then
***************
*** 281,286 ****
--- 301,309 ----
        --aux-dir=* )
          auxdir=`echo "X$1" | sed -e 's/^X--aux-dir=//'`
          shift ;;
+       --with-tests )
+         inctests=true
+         shift ;;
        --avoid )
          shift
          if test $# = 0; then
***************
*** 356,363 ****
  # func_all_modules
  func_all_modules ()
  {
    (cd "$gnulib_dir/modules" && ls -1) \
!       | sed -e '/^CVS$/d' -e '/^ChangeLog$/d' -e '/^README$/d' -e 
'/^TEMPLATE$/d' -e '/~$/d' \
        | sort
  }
  
--- 379,390 ----
  # func_all_modules
  func_all_modules ()
  {
+   # Filter out metainformation files like README, which are not modules.
+   # Filter out unit test modules; they can be retrieved through
+   # --extract-tests-module if desired.
    (cd "$gnulib_dir/modules" && ls -1) \
!       | sed -e '/^CVS$/d' -e '/^ChangeLog$/d' -e '/^README$/d' -e 
'/^TEMPLATE$/d' -e '/^TEMPLATE-TESTS$/d' -e '/~$/d' \
!       | sed -e '/-tests$/d' \
        | sort
  }
  
***************
*** 369,380 ****
       || test "CVS" = "$module" \
       || test "ChangeLog" = "$module" \
       || test "README" = "$module" \
!      || test "TEMPLATE" = "$module"; then
      echo "gnulib-tool: module $module doesn't exist" 1>&2
      module=
    fi
  }
  
  sed_extract_prog=':[  ]*$/ {
    :a
      n
--- 396,428 ----
       || test "CVS" = "$module" \
       || test "ChangeLog" = "$module" \
       || test "README" = "$module" \
!      || test "TEMPLATE" = "$module" \
!      || test "TEMPLATE-TESTS" = "$module"; then
      echo "gnulib-tool: module $module doesn't exist" 1>&2
      module=
    fi
  }
  
+ # func_verify_nontests_module
+ # verifies a module name, excluding tests modules
+ func_verify_nontests_module ()
+ {
+   case "$module" in
+     *-tests ) module= ;;
+     * ) func_verify_module ;;
+   esac
+ }
+ 
+ # func_verify_tests_module
+ # verifies a module name, considering only tests modules
+ func_verify_tests_module ()
+ {
+   case "$module" in
+     *-tests ) func_verify_module ;;
+     * ) module= ;;
+   esac
+ }
+ 
  sed_extract_prog=':[  ]*$/ {
    :a
      n
***************
*** 409,414 ****
--- 457,465 ----
  # func_get_dependencies module
  func_get_dependencies ()
  {
+   # ${module}-tests always implicitly depends on ${module}.
+   echo "$1" | sed -n -e 's/-tests//p'
+   # Then the explicit dependencies listed in the module description.
    sed -n -e "/^Depends-on$sed_extract_prog" < "$gnulib_dir/modules/$1"
  }
  
***************
*** 443,448 ****
--- 494,508 ----
    sed -n -e "/^Maintainer$sed_extract_prog" < "$gnulib_dir/modules/$1"
  }
  
+ # func_get_tests_module module
+ func_get_tests_module ()
+ {
+   # The naming convention for tests modules is hardwired: ${module}-tests.
+   if test -f modules/"$1"-tests; then
+     echo "$1"-tests
+   fi
+ }
+ 
  # func_acceptable module
  # tests whether a module is acceptable.
  # Input:
***************
*** 460,465 ****
--- 520,526 ----
  # func_modules_transitive_closure
  # Input:
  # - modules         list of specified modules
+ # - inctests        true if tests should be included, blank otherwise
  # - avoidlist       list of modules to avoid
  # Output:
  # - modules         list of modules, including dependencies
***************
*** 482,487 ****
--- 543,561 ----
                xmodules="$xmodules $depmodule"
              fi
            done
+           if test -n "$inctests"; then
+             testsmodule=`func_get_tests_module $module`
+             if test -n "$testsmodule"; then
+               if func_acceptable $testsmodule; then
+                 xmodules="$xmodules $testsmodule"
+                 for depmodule in `func_get_dependencies $testsmodule`; do
+                   if func_acceptable $depmodule; then
+                     xmodules="$xmodules $depmodule"
+                   fi
+                 done
+               fi
+             fi
+           fi
          fi
        fi
      done
***************
*** 560,566 ****
    echo "MAINTAINERCLEANFILES ="
    echo
    for module in $modules; do
!     func_verify_module
      if test -n "$module"; then
        {
          func_get_automake_snippet "$module" |
--- 634,640 ----
    echo "MAINTAINERCLEANFILES ="
    echo
    for module in $modules; do
!     func_verify_nontests_module
      if test -n "$module"; then
        {
          func_get_automake_snippet "$module" |
***************
*** 584,589 ****
--- 658,732 ----
    echo "# Makefile.am ends here"
  }
  
+ # func_emit_tests_Makefile_am
+ # emits the contents of tests/Makefile.am to standard output.
+ # Input:
+ # - modules         list of modules, including dependencies
+ # - libname         library name
+ # - libtool         true if libtool will be used, blank otherwise
+ # - sourcebase      relative directory containing lib source code
+ func_emit_tests_Makefile_am ()
+ {
+   if test -n "$libtool"; then
+     libext=la
+   else
+     libext=a
+   fi
+   echo "## Process this file with automake to produce Makefile.in."
+   echo "# Copyright (C) 2004-2005 Free Software Foundation, Inc."
+   echo "#"
+   echo "# This file is free software, distributed under the terms of the GNU"
+   echo "# General Public License.  As a special exception to the GNU General"
+   echo "# Public License, this file may be distributed as part of a program"
+   echo "# that contains a configuration script generated by Automake, under"
+   echo "# the same distribution terms as the rest of that program."
+   echo "#"
+   echo "# Generated by gnulib-tool."
+   echo
+   # Generate dependencies here, since it eases the debugging of test failures.
+   echo "AUTOMAKE_OPTIONS = 1.5 foreign"
+   echo
+   echo "ACLOCAL_AMFLAGS = -I ../m4"
+   echo
+   echo "TESTS ="
+   echo "noinst_PROGRAMS ="
+   echo "EXTRA_DIST ="
+   echo "BUILT_SOURCES ="
+   echo "SUFFIXES ="
+   echo "MOSTLYCLEANFILES ="
+   echo "CLEANFILES ="
+   echo "DISTCLEANFILES ="
+   echo "MAINTAINERCLEANFILES ="
+   echo
+   echo "AM_CPPFLAGS = \\"
+   echo "  -I. -I\$(srcdir) \\"
+   echo "  -I.. -I\$(srcdir)/.. \\"
+   echo "  -I../${sourcebase-lib} -I\$(srcdir)/../${sourcebase-lib}"
+   echo
+   echo "LDADD = ../${sourcebase-lib}/${libname}.${libext}"
+   echo
+   for module in $modules; do
+     func_verify_tests_module
+     if test -n "$module"; then
+       func_get_automake_snippet "$module" > amsnippet.tmp
+       # Skip the contents if its entirely empty.
+       if grep '[^     ]' amsnippet.tmp > /dev/null ; then
+         echo "## begin gnulib module $module"
+         echo
+         cat amsnippet.tmp
+         echo "## end   gnulib module $module"
+         echo
+       fi
+       rm -f amsnippet.tmp
+     fi
+   done
+   echo "# Clean up after Solaris cc."
+   echo "clean-local:"
+   echo "      rm -rf SunWS_cache"
+   echo
+   echo "# Makefile.am ends here"
+ }
+ 
  # func_import modules
  # Uses also the variables
  # - destdir         target directory
***************
*** 811,816 ****
--- 954,960 ----
    ) > "$testdir/m4/Makefile.am"
  
    subdirs="lib m4"
+   subdirs_with_configure_ac=""
  
    if test -f "$testdir"/m4/gettext.m4; then
      # Avoid stupid error message from automake:
***************
*** 821,826 ****
--- 965,1022 ----
      subdirs="$subdirs po"
    fi
  
+   if test -n "$inctests"; then
+     test -d "$testdir/tests" || mkdir "$testdir/tests"
+     # Create tests/Makefile.am.
+     sourcebase=lib
+     func_emit_tests_Makefile_am > "$testdir/tests/Makefile.am"
+     # Create tests/configure.ac.
+     (echo "# Process this file with autoconf to produce a configure script."
+      echo "AC_INIT([dummy], [0])"
+      echo "AC_CONFIG_AUX_DIR([../$auxdir])"
+      echo "AM_INIT_AUTOMAKE"
+      echo
+      echo "AM_CONFIG_HEADER([config.h])"
+      echo
+      echo "AC_PROG_CC"
+      echo "AC_PROG_INSTALL"
+      echo "AC_PROG_MAKE_SET"
+      echo "AC_PROG_RANLIB"
+      echo
+      if grep AC_GNU_SOURCE "$testdir"/m4/*.m4 > /dev/null; then
+        echo "AC_GNU_SOURCE"
+        echo
+      fi
+      if grep gl_USE_SYSTEM_EXTENSIONS "$testdir"/m4/*.m4 > /dev/null; then
+        echo "gl_USE_SYSTEM_EXTENSIONS"
+        echo
+      fi
+      # We don't have explicit ordering constraints between the various
+      # autoconf snippets. It's cleanest to put those of the library before
+      # those of the tests.
+      for module in $modules; do
+        func_verify_nontests_module
+        if test -n "$module"; then
+          func_get_autoconf_snippet "$module"
+        fi
+      done
+      for module in $modules; do
+        func_verify_tests_module
+        if test -n "$module"; then
+          func_get_autoconf_snippet "$module"
+        fi
+      done
+      echo
+      # Usually tests/config.h will be a superset of config.h. Verify this by
+      # "merging" config.h into tests/config.h; look out for gcc warnings.
+      echo "AH_TOP([#include \"../config.h\"])"
+      echo
+      echo "AC_OUTPUT([Makefile])"
+     ) > "$testdir/tests/configure.ac"
+     subdirs="$subdirs tests"
+     subdirs_with_configure_ac="$subdirs_with_configure_ac tests"
+   fi
+ 
    # Create Makefile.am.
    (echo "## Process this file with automake to produce Makefile.in."
     echo
***************
*** 855,869 ****
       echo
     fi
     for module in $modules; do
!      func_verify_module
       if test -n "$module"; then
         func_get_autoconf_snippet "$module"
       fi
     done
     echo
     makefiles="Makefile"
     for d in $subdirs; do
!      makefiles="$makefiles $d/Makefile"
     done
     echo "AC_OUTPUT([$makefiles])"
    ) > "$testdir/configure.ac"
--- 1051,1073 ----
       echo
     fi
     for module in $modules; do
!      func_verify_nontests_module
       if test -n "$module"; then
         func_get_autoconf_snippet "$module"
       fi
     done
     echo
+    if test -n "$subdirs_with_configure_ac"; then
+      echo "AC_CONFIG_SUBDIRS(["`echo $subdirs_with_configure_ac`"])"
+    fi
     makefiles="Makefile"
     for d in $subdirs; do
!      # For subdirs that have a configure.ac by their own, it's the subdir's
!      # configure.ac which creates the subdir's Makefile.am, not this one.
!      case " $subdirs_with_configure_ac " in
!        *" $d "*) ;;
!        *) makefiles="$makefiles $d/Makefile" ;;
!      esac
     done
     echo "AC_OUTPUT([$makefiles])"
    ) > "$testdir/configure.ac"
***************
*** 1177,1182 ****
--- 1381,1396 ----
      done
      ;;
  
+   extract-tests-module )
+     for module
+     do
+       func_verify_module
+       if test -n "$module"; then
+         func_get_tests_module "$module"
+       fi
+     done
+     ;;
+ 
    * )
      func_fatal_error "unknown operation mode --$mode" ;;
  esac
*** MODULES.html.sh.bak 2005-08-17 16:05:58.000000000 +0200
--- MODULES.html.sh     2005-08-25 01:11:41.000000000 +0200
***************
*** 2098,2106 ****
  func_echo '<LI>One or more implementation files: lib/<VAR>module</VAR>.c et 
al.'
  func_echo '<LI>One or more autoconf macro files: m4/<VAR>module</VAR>.m4 et 
al.'
  func_echo '<LI>A configure.ac fragment, Makefile.am fragment, dependency 
list: modules/<VAR>module</VAR>'
  func_echo '<LI>Some documentation'
  func_echo '<LI>A POT file and some PO files'
- func_echo '<LI>A testsuite'
  func_end UL
  
  func_echo '<HR>'
--- 2098,2106 ----
  func_echo '<LI>One or more implementation files: lib/<VAR>module</VAR>.c et 
al.'
  func_echo '<LI>One or more autoconf macro files: m4/<VAR>module</VAR>.m4 et 
al.'
  func_echo '<LI>A configure.ac fragment, Makefile.am fragment, dependency 
list: modules/<VAR>module</VAR>'
+ func_echo '<LI>A testsuite: source files in tests/ and metainformation (a 
configure.ac fragment, Makefile.am fragment, dependency list) in 
modules/<VAR>module</VAR>-tests'
  func_echo '<LI>Some documentation'
  func_echo '<LI>A POT file and some PO files'
  func_end UL
  
  func_echo '<HR>'





reply via email to

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