diff options
author | Shane Kerr <shane@isc.org> | 2007-11-16 11:04:12 +0000 |
---|---|---|
committer | Shane Kerr <shane@isc.org> | 2007-11-16 11:04:12 +0000 |
commit | 6e999c3c8a96354e088da8a5a35c9ab92f419ec7 (patch) | |
tree | 375ea7f6ca88c7732d4e675d2d52b3f3693e21ec /tests | |
parent | b9ced0286885a375857acb742091a10a787b07c7 (diff) | |
download | isc-dhcp-6e999c3c8a96354e088da8a5a35c9ab92f419ec7.tar.gz |
Unit test framework now added.
See RT ticket #17223 for more.
Diffstat (limited to 'tests')
-rw-r--r-- | tests/HOWTO-unit-test | 153 | ||||
-rw-r--r-- | tests/Makefile.am | 33 | ||||
-rw-r--r-- | tests/Makefile.in | 406 | ||||
-rw-r--r-- | tests/t_api.c | 823 | ||||
-rw-r--r-- | tests/t_api_dhcp.c | 44 | ||||
-rw-r--r-- | tests/unit_test_sample.c | 25 |
6 files changed, 1484 insertions, 0 deletions
diff --git a/tests/HOWTO-unit-test b/tests/HOWTO-unit-test new file mode 100644 index 00000000..b38185c9 --- /dev/null +++ b/tests/HOWTO-unit-test @@ -0,0 +1,153 @@ +Introduction +------------ + +In DHCP, a unit test exercises a particular piece of code in +isolation. There is a separate unit test per module or API. Each unit +test lives in a directory beneath the code it is designed to exercise. +So, we have: + + client/tests/ + common/tests/ + dhcpctl/tests/ + +And so on. + +Ideally each function would be invoked with every possible type of +input, and each branch of every function would be checked. In practice +we try to be a bit more pragmatic, and target the most basic +operations, as well tricky code, and areas we have seen bugs in the +past. + + +Running Unit Tests +------------------ + +In order to run the unit tests for DHCP, use: + +$ make check + +This will run all of the unit tests. + +You can run a single test by going to the appropriate test directory +and invoking the test directly: + +$ cd common/tests +$ make test_alloc +$ ./test_alloc + +There are also a number of options that you can use when running a +test. To see these, use the "-u" flag on the program. + + +Adding a New Unit Test +---------------------- + +To add an additional test to an existing test program, you must create +a function for the new test in the C source file: + +static void +mynewtest(void) { + static const char *test_desc = "describe the test"; + + t_assert("mynewtest", 1, T_REQUIRED, test_desc); + + /* ... test code ... */ + + t_result(T_PASS); +} + +Then add this function to the T_testlist[] array in the file: + +testspec_t T_testlist[] = { + ... + { mynewtest, "some new test" }, + { NULL, NULL } +}; + +Then you should be able to compile and run your new test. + + +Adding a New Unit Test Program +------------------------------ + +To add a new program, such as when a new module is added, you can copy +the "unit_test_sample.c" file (in this directory) to a new name, add +the new file as a target in Makefile.am, and begin adding tests. Do +not forget to add it to CVS via "cvs add". + +If there is no "tests" directory for a given subdirectory, then one +must be created. This can be done by: + +1. Creating the directory: + + $ mkdir $subdir/tests + $ cvs add tests + +2. Adding the subdirectory to the build system: + + Add to $subdir/Makefile.am: + + SUBDIRS = tests + + Add to the AC_OUTPUT macro in configure.ac: + + $subdir/tests/Makefile + +3. Create a Makefile.am in the new directory, something like this: + + AM_CPPFLAGS = -I../.. + + check_PROGRAMS = test_foo + + TESTS = test_foo + + test_foo_SOURCES = test_foo.c + test_foo_LDADD = ../../tests/libt_api.a # plus others... + + +See existing Makefile.am for examples, and the Automake documentation: + + http://www.gnu.org/software/automake/manual/html_node/Tests.html + + +Support Functions +----------------- + +Here are a few of the most useful functions defined in t_api that you +can use in testing: + + void + t_assert(const char *component, int anum, int class, + const char *what, ...); + + The name of this function is slightly misleading. It + actually just prints out an error message in the test + output. + + void + t_info(const char *format, ...); + + Prints out a message in the test output. You should + include "\n" at the end. + + void + t_result(int result); + + Prints out the result in the test output. You should + use one of the constants for this: + + T_PASS + T_FAIL + T_UNRESOLVED + T_UNSUPPORTED + T_UNTESTED + T_THREADONLY + +Additional Testing +------------------ + +Other static or runtime testing is always an option. For instance, you +can use valgrind to check for memory leaks. + + +$Id: HOWTO-unit-test,v 1.2 2007/11/16 11:04:12 shane Exp $ diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 00000000..9c6c650e --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,33 @@ +EXTRA_DIST = failover/dhcp-1.cf failover/dhcp-2.cf failover/new-failover \ + DHCPv6/000-badmsgtype.pl \ + DHCPv6/010-solicit-noclientid.pl \ + DHCPv6/011-solicit-serverid.pl \ + DHCPv6/020-advertise-mcast.pl \ + DHCPv6/030-request-noclientid.pl \ + DHCPv6/031-request-noserverid.pl \ + DHCPv6/032-request-badduid.pl \ + DHCPv6/110-information-request-ia_na.pl \ + DHCPv6/111-information-request-ia_ta.pl \ + DHCPv6/112-badduid.pl \ + DHCPv6/210-solicit-nohost.pl \ + DHCPv6/211-solicit-opt-in-na.pl \ + DHCPv6/212-solicit-opt-in-na-norapidcommit.pl \ + DHCPv6/280-release-nohost.pl \ + DHCPv6/281-release-bad-address.pl \ + DHCPv6/282-release-no-address.pl \ + DHCPv6/283-release.pl \ + DHCPv6/290-decline-nohost.pl \ + DHCPv6/291-decline-bad-address.pl \ + DHCPv6/292-decline-no-address.pl \ + DHCPv6/293-decline.pl \ + DHCPv6/README DHCPv6/dhcp_client.pm \ + DHCPv6/stubcli-opt-in-na.pl DHCPv6/stubcli.pl \ + DHCPv6/test-a.conf DHCPv6/test-b.conf \ + HOWTO-unit-test \ + unit_test_sample.c + +AM_CPPFLAGS = -I.. + +check_LIBRARIES = libt_api.a +libt_api_a_SOURCES = t_api.c t_api_dhcp.c + diff --git a/tests/Makefile.in b/tests/Makefile.in new file mode 100644 index 00000000..af0c0b01 --- /dev/null +++ b/tests/Makefile.in @@ -0,0 +1,406 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = tests +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/includes/config.h +CONFIG_CLEAN_FILES = +AR = ar +ARFLAGS = cru +libt_api_a_AR = $(AR) $(ARFLAGS) +libt_api_a_LIBADD = +am_libt_api_a_OBJECTS = t_api.$(OBJEXT) t_api_dhcp.$(OBJEXT) +libt_api_a_OBJECTS = $(am_libt_api_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/includes +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libt_api_a_SOURCES) +DIST_SOURCES = $(libt_api_a_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +byte_order = @byte_order@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +EXTRA_DIST = failover/dhcp-1.cf failover/dhcp-2.cf failover/new-failover \ + DHCPv6/000-badmsgtype.pl \ + DHCPv6/010-solicit-noclientid.pl \ + DHCPv6/011-solicit-serverid.pl \ + DHCPv6/020-advertise-mcast.pl \ + DHCPv6/030-request-noclientid.pl \ + DHCPv6/031-request-noserverid.pl \ + DHCPv6/032-request-badduid.pl \ + DHCPv6/110-information-request-ia_na.pl \ + DHCPv6/111-information-request-ia_ta.pl \ + DHCPv6/112-badduid.pl \ + DHCPv6/210-solicit-nohost.pl \ + DHCPv6/211-solicit-opt-in-na.pl \ + DHCPv6/212-solicit-opt-in-na-norapidcommit.pl \ + DHCPv6/280-release-nohost.pl \ + DHCPv6/281-release-bad-address.pl \ + DHCPv6/282-release-no-address.pl \ + DHCPv6/283-release.pl \ + DHCPv6/290-decline-nohost.pl \ + DHCPv6/291-decline-bad-address.pl \ + DHCPv6/292-decline-no-address.pl \ + DHCPv6/293-decline.pl \ + DHCPv6/README DHCPv6/dhcp_client.pm \ + DHCPv6/stubcli-opt-in-na.pl DHCPv6/stubcli.pl \ + DHCPv6/test-a.conf DHCPv6/test-b.conf \ + HOWTO-unit-test \ + unit_test_sample.c + +AM_CPPFLAGS = -I.. +check_LIBRARIES = libt_api.a +libt_api_a_SOURCES = t_api.c t_api_dhcp.c +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign tests/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign tests/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-checkLIBRARIES: + -test -z "$(check_LIBRARIES)" || rm -f $(check_LIBRARIES) +libt_api.a: $(libt_api_a_OBJECTS) $(libt_api_a_DEPENDENCIES) + -rm -f libt_api.a + $(libt_api_a_AR) libt_api.a $(libt_api_a_OBJECTS) $(libt_api_a_LIBADD) + $(RANLIB) libt_api.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_api.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_api_dhcp.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + $(mkdir_p) $(distdir)/DHCPv6 $(distdir)/failover + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_LIBRARIES) +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-checkLIBRARIES clean-generic mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am + +.PHONY: CTAGS GTAGS all all-am check check-am clean \ + clean-checkLIBRARIES clean-generic ctags distclean \ + distclean-compile distclean-generic distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/tests/t_api.c b/tests/t_api.c new file mode 100644 index 00000000..6685cec0 --- /dev/null +++ b/tests/t_api.c @@ -0,0 +1,823 @@ +/* + * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1999-2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: t_api.c,v 1.2 2007/11/16 11:04:12 shane Exp $ */ + +/*! \file */ + +/* + * This test API framework is taken from the BIND 9 code. It has been + * modified to remove the DNS-specific parts, and the BIND-specific + * parts. + * + * The DNS-specific parts are now wrapped with the DNS_SUPPORT macro, + * and the BIND-specific parts are now wrapped with the BIND_SUPPORT + * macro. + */ + +#include <config.h> + +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +#include <sys/wait.h> + +#include <isc-dhcp/boolean.h> +#include <isc-dhcp/commandline.h> +#include <isc-dhcp/print.h> +#include <isc-dhcp/string.h> +#include <isc-dhcp/mem.h> + +#ifdef DNS_SUPPORT +#include <dns/compress.h> +#include <dns/result.h> +#endif /* DNS_SUPPORT */ + +#ifndef BIND_SUPPORT +#define isc_commandline_parse getopt +#define isc_commandline_argument optarg +#define isc_commandline_option optopt +#endif /* BIND_SUPPORT */ + +#include "t_api.h" + +static const char *Usage = + "\t-a : run all tests\n" + "\t-b <dir> : chdir to dir before running tests" + "\t-c <config_file> : use specified config file\n" + "\t-d <debug_level> : set debug level to debug_level\n" + "\t-h : print test info\n" + "\t-u : print usage info\n" + "\t-n <test_name> : run specified test name\n" + "\t-t <test_number> : run specified test number\n" + "\t-x : don't execute tests in a subproc\n" + "\t-q <timeout> : use 'timeout' as the timeout value\n"; +/*!< + * -a --> run all tests + * -b dir --> chdir to dir before running tests + * -c config --> use config file 'config' + * -d --> turn on api debugging + * -h --> print out available test names + * -u --> print usage info + * -n name --> run test named name + * -tn --> run test n + * -x --> don't execute testcases in a subproc + * -q timeout --> use 'timeout' as the timeout value + */ + +#define T_MAXTESTS 256 /*% must be 0 mod 8 */ +#define T_MAXENV 256 +#define T_DEFAULT_CONFIG "t_config" +#define T_BUFSIZ 256 +#define T_BIGBUF 4096 + +#define T_TCTOUT 60 + +int T_debug; +int T_timeout; +pid_t T_pid; +static const char * T_config; +static char T_tvec[T_MAXTESTS / 8]; +static char * T_env[T_MAXENV + 1]; +static char T_buf[T_BIGBUF]; +static char * T_dir; + +static int +t_initconf(const char *path); + +static int +t_dumpconf(const char *path); + +static int +t_putinfo(const char *key, const char *info); + +static char * +t_getdate(char *buf, size_t buflen); + +static void +printhelp(void); + +static void +printusage(void); + +static int T_int; + +static void +t_sighandler(int sig) { + T_int = sig; +} + +int +main(int argc, char **argv) { + int c; + int tnum; + int subprocs; + pid_t deadpid; + int status; + int len; + isc_boolean_t first; + testspec_t *pts; + struct sigaction sa; + +#ifdef BIND_SUPPORT + isc_mem_debugging = ISC_MEM_DEBUGRECORD; +#endif /* BIND_SUPPORT */ + first = ISC_TRUE; + subprocs = 1; + T_timeout = T_TCTOUT; + + /* + * -a option is now default. + */ + memset(T_tvec, 0xffff, sizeof(T_tvec)); + + /* + * Parse args. + */ + while ((c = isc_commandline_parse(argc, argv, ":at:c:d:n:huxq:b:")) + != -1) { + if (c == 'a') { + /* + * Flag all tests to be run. + */ + memset(T_tvec, 0xffff, sizeof(T_tvec)); + } + else if (c == 'b') { + T_dir = isc_commandline_argument; + } + else if (c == 't') { + tnum = atoi(isc_commandline_argument); + if ((tnum > 0) && (tnum < T_MAXTESTS)) { + if (first) { + /* + * Turn off effect of -a default + * and allow multiple -t and -n + * options. + */ + memset(T_tvec, 0, sizeof(T_tvec)); + first = ISC_FALSE; + } + /* + * Flag test tnum to be run. + */ + tnum -= 1; + T_tvec[tnum / 8] |= (0x01 << (tnum % 8)); + } + } + else if (c == 'c') { + T_config = isc_commandline_argument; + } + else if (c == 'd') { + T_debug = atoi(isc_commandline_argument); + } + else if (c == 'n') { + pts = &T_testlist[0]; + tnum = 0; + while (pts->pfv != NULL) { + if (! strcmp(pts->func_name, + isc_commandline_argument)) { + if (first) { + memset(T_tvec, 0, + sizeof(T_tvec)); + first = ISC_FALSE; + } + T_tvec[tnum/8] |= (0x01 << (tnum%8)); + break; + } + ++pts; + ++tnum; + } + if (pts->pfv == NULL) { + fprintf(stderr, "no such test %s\n", + isc_commandline_argument); + exit(1); + } + } + else if (c == 'h') { + printhelp(); + exit(0); + } + else if (c == 'u') { + printusage(); + exit(0); + } + else if (c == 'x') { + subprocs = 0; + } + else if (c == 'q') { + T_timeout = atoi(isc_commandline_argument); + } + else if (c == ':') { + fprintf(stderr, "Option -%c requires an argument\n", + isc_commandline_option); + exit(1); + } + else if (c == '?') { + fprintf(stderr, "Unrecognized option -%c\n", + isc_commandline_option); + exit(1); + } + } + + /* + * Set cwd. + */ + + if (T_dir != NULL) + (void) chdir(T_dir); + + /* + * We don't want buffered output. + */ + + (void)setbuf(stdout, NULL); + (void)setbuf(stderr, NULL); + + /* + * Setup signals. + */ + + sa.sa_flags = 0; + sigfillset(&sa.sa_mask); + +#ifdef SIGCHLD + /* + * This is mostly here for NetBSD's pthread implementation, until + * people catch up to the latest unproven-pthread package. + */ + sa.sa_handler = SIG_DFL; + (void)sigaction(SIGCHLD, &sa, NULL); +#endif + + sa.sa_handler = t_sighandler; + (void)sigaction(SIGINT, &sa, NULL); + (void)sigaction(SIGALRM, &sa, NULL); + + /* + * Output start stanza to journal. + */ + + snprintf(T_buf, sizeof(T_buf), "%s:", argv[0]); + len = strlen(T_buf); + (void) t_getdate(T_buf + len, T_BIGBUF - len); + t_putinfo("S", T_buf); + + /* + * Setup the test environment using the config file. + */ + + if (T_config == NULL) + T_config = T_DEFAULT_CONFIG; + + t_initconf(T_config); + if (T_debug) + t_dumpconf(T_config); + + /* + * Now invoke all the test cases. + */ + + tnum = 0; + pts = &T_testlist[0]; + while (*pts->pfv != NULL) { + if (T_tvec[tnum / 8] & (0x01 << (tnum % 8))) { + if (subprocs) { + T_pid = fork(); + if (T_pid == 0) { + (*pts->pfv)(); + exit(0); + } else if (T_pid > 0) { + + T_int = 0; + sa.sa_handler = t_sighandler; + (void)sigaction(SIGALRM, &sa, NULL); + alarm(T_timeout); + + deadpid = (pid_t) -1; + while (deadpid != T_pid) { + deadpid = + waitpid(T_pid, &status, 0); + if (deadpid == T_pid) { + if (WIFSIGNALED(status)) { + if (WTERMSIG(status) == + SIGTERM) + t_info( + "the test case timed out\n"); + else + t_info( + "the test case caused exception %d\n", + WTERMSIG(status)); + t_result(T_UNRESOLVED); + } + } else if ((deadpid == -1) && + (errno == EINTR) && + T_int) { + kill(T_pid, SIGTERM); + T_int = 0; + } + else if ((deadpid == -1) && + ((errno == ECHILD) || + (errno == ESRCH))) + break; + } + + alarm(0); + sa.sa_handler = SIG_IGN; + (void)sigaction(SIGALRM, &sa, NULL); + } else { + t_info("fork failed, errno == %d\n", + errno); + t_result(T_UNRESOLVED); + } + } + else { + (*pts->pfv)(); + } + } + ++pts; + ++tnum; + } + + snprintf(T_buf, sizeof(T_buf), "%s:", argv[0]); + len = strlen(T_buf); + (void) t_getdate(T_buf + len, T_BIGBUF - len); + t_putinfo("E", T_buf); + + return(0); +} + +void +t_assert(const char *component, int anum, int class, const char *what, ...) { + va_list args; + + (void)printf("T:%s:%d:%s\n", component, anum, class == T_REQUIRED ? + "A" : "C"); + + /* + * Format text to a buffer. + */ + va_start(args, what); + (void)vsnprintf(T_buf, sizeof(T_buf), what, args); + va_end(args); + + (void)t_putinfo("A", T_buf); + (void)printf("\n"); +} + +void +t_info(const char *format, ...) { + va_list args; + + va_start(args, format); + (void) vsnprintf(T_buf, sizeof(T_buf), format, args); + va_end(args); + (void) t_putinfo("I", T_buf); +} + +void +t_result(int result) { + const char *p; + + switch (result) { + case T_PASS: + p = "PASS"; + break; + case T_FAIL: + p = "FAIL"; + break; + case T_UNRESOLVED: + p = "UNRESOLVED"; + break; + case T_UNSUPPORTED: + p = "UNSUPPORTED"; + break; + case T_UNTESTED: + p = "UNTESTED"; + break; + case T_THREADONLY: + p = "THREADONLY"; + break; + default: + p = "UNKNOWN"; + break; + } + printf("R:%s\n", p); +} + +char * +t_getenv(const char *name) { + char *n; + char **p; + size_t len; + + n = NULL; + if (name && *name) { + + p = &T_env[0]; + len = strlen(name); + + while (*p != NULL) { + if (strncmp(*p, name, len) == 0) { + if ( *(*p + len) == '=') { + n = *p + len + 1; + break; + } + } + ++p; + } + } + return(n); +} + +/* + * + * Read in the config file at path, initializing T_env. + * + * note: no format checking for now ... + * + */ + +static int +t_initconf(const char *path) { + + int n; + int rval; + char **p; + FILE *fp; + + rval = -1; + + fp = fopen(path, "r"); + if (fp != NULL) { + n = 0; + p = &T_env[0]; + while (n < T_MAXENV) { + *p = t_fgetbs(fp); + if (*p == NULL) + break; + if ((**p == '#') || (strchr(*p, '=') == NULL)) { + /* + * Skip comments and other junk. + */ + (void)free(*p); + continue; + } + ++p; ++n; + } + (void)fclose(fp); + rval = 0; + } + + return (rval); +} + +/* + * + * Dump T_env to stdout. + * + */ + +static int +t_dumpconf(const char *path) { + int rval; + char **p; + FILE *fp; + + rval = -1; + fp = fopen(path, "r"); + if (fp != NULL) { + p = &T_env[0]; + while (*p != NULL) { + printf("C:%s\n", *p); + ++p; + } + (void) fclose(fp); + rval = 0; + } + return(rval); +} + +/* + * + * Read a newline or EOF terminated string from fp. + * On success: + * return a malloc'd buf containing the string with + * the newline converted to a '\0'. + * On error: + * return NULL. + * + * Caller is responsible for freeing buf. + * + */ + +char * +t_fgetbs(FILE *fp) { + int c; + size_t n; + size_t size; + char *buf; + char *p; + + n = 0; + size = T_BUFSIZ; + buf = (char *) malloc(T_BUFSIZ * sizeof(char)); + + if (buf != NULL) { + p = buf; + while ((c = fgetc(fp)) != EOF) { + + if (c == '\n') + break; + + *p++ = c; + ++n; + if ( n >= size ) { + size += T_BUFSIZ; + buf = (char *)realloc(buf, + size * sizeof(char)); + if (buf == NULL) + break; + p = buf + n; + } + } + *p = '\0'; + if (c == EOF && n == 0U) { + free(buf); + return (NULL); + } + return (buf); + } else { + fprintf(stderr, "malloc failed %d", errno); + return(NULL); + } +} + +/* + * + * Put info to log, using key. + * For now, just dump it out. + * Later format into pretty lines. + * + */ + +static int +t_putinfo(const char *key, const char *info) { + int rval; + + /* + * For now. + */ + rval = printf("%s:%s", key, info); + return(rval); +} + +static char * +t_getdate(char *buf, size_t buflen) { + size_t n; + time_t t; + struct tm *p; + + t = time(NULL); + p = localtime(&t); + n = strftime(buf, buflen - 1, "%A %d %B %H:%M:%S %Y\n", p); + return(n != 0U ? buf : NULL); +} + +/* + * Some generally used utilities. + */ +#ifdef DNS_SUPPORT +struct dns_errormap { + isc_result_t result; + const char *text; +} dns_errormap[] = { + { ISC_R_SUCCESS, "ISC_R_SUCCESS" }, + { ISC_R_EXISTS, "ISC_R_EXISTS" }, + { ISC_R_NOTFOUND, "ISC_R_NOTFOUND" }, + { ISC_R_NOSPACE, "ISC_R_NOSPACE" }, + { ISC_R_UNEXPECTED, "ISC_R_UNEXPECTED" }, + { ISC_R_UNEXPECTEDEND, "ISC_R_UNEXPECTEDEND" }, + { ISC_R_RANGE, "ISC_R_RANGE" }, + { DNS_R_LABELTOOLONG, "DNS_R_LABELTOOLONG" }, + { DNS_R_BADESCAPE, "DNS_R_BADESCAPE" }, + /* { DNS_R_BADBITSTRING, "DNS_R_BADBITSTRING" }, */ + /* { DNS_R_BITSTRINGTOOLONG, "DNS_R_BITSTRINGTOOLONG"}, */ + { DNS_R_EMPTYLABEL, "DNS_R_EMPTYLABEL" }, + { DNS_R_BADDOTTEDQUAD, "DNS_R_BADDOTTEDQUAD" }, + { DNS_R_UNKNOWN, "DNS_R_UNKNOWN" }, + { DNS_R_BADLABELTYPE, "DNS_R_BADLABELTYPE" }, + { DNS_R_BADPOINTER, "DNS_R_BADPOINTER" }, + { DNS_R_TOOMANYHOPS, "DNS_R_TOOMANYHOPS" }, + { DNS_R_DISALLOWED, "DNS_R_DISALLOWED" }, + { DNS_R_EXTRATOKEN, "DNS_R_EXTRATOKEN" }, + { DNS_R_EXTRADATA, "DNS_R_EXTRADATA" }, + { DNS_R_TEXTTOOLONG, "DNS_R_TEXTTOOLONG" }, + { DNS_R_SYNTAX, "DNS_R_SYNTAX" }, + { DNS_R_BADCKSUM, "DNS_R_BADCKSUM" }, + { DNS_R_BADAAAA, "DNS_R_BADAAAA" }, + { DNS_R_NOOWNER, "DNS_R_NOOWNER" }, + { DNS_R_NOTTL, "DNS_R_NOTTL" }, + { DNS_R_BADCLASS, "DNS_R_BADCLASS" }, + { DNS_R_PARTIALMATCH, "DNS_R_PARTIALMATCH" }, + { DNS_R_NEWORIGIN, "DNS_R_NEWORIGIN" }, + { DNS_R_UNCHANGED, "DNS_R_UNCHANGED" }, + { DNS_R_BADTTL, "DNS_R_BADTTL" }, + { DNS_R_NOREDATA, "DNS_R_NOREDATA" }, + { DNS_R_CONTINUE, "DNS_R_CONTINUE" }, + { DNS_R_DELEGATION, "DNS_R_DELEGATION" }, + { DNS_R_GLUE, "DNS_R_GLUE" }, + { DNS_R_DNAME, "DNS_R_DNAME" }, + { DNS_R_CNAME, "DNS_R_CNAME" }, + { DNS_R_NXDOMAIN, "DNS_R_NXDOMAIN" }, + { DNS_R_NXRRSET, "DNS_R_NXRRSET" }, + { DNS_R_BADDB, "DNS_R_BADDB" }, + { DNS_R_ZONECUT, "DNS_R_ZONECUT" }, + { DNS_R_NOTZONETOP, "DNS_R_NOTZONETOP" }, + { DNS_R_SEENINCLUDE, "DNS_R_SEENINCLUDE" }, + { DNS_R_SINGLETON, "DNS_R_SINGLETON" }, + { (isc_result_t)0, NULL } +}; + +isc_result_t +t_dns_result_fromtext(char *name) { + + isc_result_t result; + struct dns_errormap *pmap; + + result = ISC_R_UNEXPECTED; + + pmap = dns_errormap; + while (pmap->text != NULL) { + if (strcmp(name, pmap->text) == 0) + break; + ++pmap; + } + + if (pmap->text != NULL) + result = pmap->result; + + return (result); +} + +struct dc_method_map { + unsigned int dc_method; + const char *text; +} dc_method_map[] = { + + { DNS_COMPRESS_NONE, "DNS_COMPRESS_NONE" }, + { DNS_COMPRESS_GLOBAL14, "DNS_COMPRESS_GLOBAL14" }, + { DNS_COMPRESS_ALL, "DNS_COMPRESS_ALL" }, + { 0, NULL } +}; + +unsigned int +t_dc_method_fromtext(char *name) { + unsigned int dc_method; + struct dc_method_map *pmap; + + dc_method = DNS_COMPRESS_NONE; + + pmap = dc_method_map; + while (pmap->text != NULL) { + if (strcmp(name, pmap->text) == 0) + break; + ++pmap; + } + + if (pmap->text != NULL) + dc_method = pmap->dc_method; + + return(dc_method); +} +#endif /* DNS_SUPPORT */ + +int +t_bustline(char *line, char **toks) { + int cnt; + char *p; + + cnt = 0; + if (line && *line) { + while ((p = strtok(line, "\t")) && (cnt < T_MAXTOKS)) { + *toks++ = p; + line = NULL; + ++cnt; + } + } + return(cnt); +} + +static void +printhelp(void) { + int cnt; + testspec_t *pts; + + cnt = 1; + pts = &T_testlist[0]; + + printf("Available tests:\n"); + while (pts->func_name) { + printf("\t%d\t%s\n", cnt, pts->func_name); + ++pts; + ++cnt; + } +} + +static void +printusage(void) { + printf("Usage:\n%s\n", Usage); +} + +int +t_eval(const char *filename, int (*func)(char **), int nargs) { + FILE *fp; + char *p; + int line; + int cnt; + int result; + int nfails; + int nprobs; + int npass; + char *tokens[T_MAXTOKS + 1]; + + npass = 0; + nfails = 0; + nprobs = 0; + + fp = fopen(filename, "r"); + if (fp != NULL) { + line = 0; + while ((p = t_fgetbs(fp)) != NULL) { + + ++line; + + /* + * Skip comment lines. + */ + if ((isspace((unsigned char)*p)) || (*p == '#')) { + (void)free(p); + continue; + } + + cnt = t_bustline(p, tokens); + if (cnt == nargs) { + result = func(tokens); + switch (result) { + case T_PASS: + ++npass; + break; + case T_FAIL: + ++nfails; + break; + case T_UNTESTED: + break; + default: + ++nprobs; + break; + } + } else { + t_info("bad format in %s at line %d\n", + filename, line); + ++nprobs; + } + + (void)free(p); + } + (void)fclose(fp); + } else { + t_info("Missing datafile %s\n", filename); + ++nprobs; + } + + result = T_UNRESOLVED; + + if (nfails == 0 && nprobs == 0 && npass > 0) + result = T_PASS; + else if (nfails > 0) + result = T_FAIL; + else if (npass == 0) + result = T_UNTESTED; + + return (result); +} diff --git a/tests/t_api_dhcp.c b/tests/t_api_dhcp.c new file mode 100644 index 00000000..9ef221a1 --- /dev/null +++ b/tests/t_api_dhcp.c @@ -0,0 +1,44 @@ +/* + * We have to have a number of symbols defined in order to build a + * DHCP program. + */ + +#include <config.h> +#include "dhcpd.h" + +void +bootp(struct packet *packet) { +} + +void +dhcp(struct packet *packet) { +} + +void +dhcpv6(struct packet *packet) { +} + +isc_result_t +dhcp_set_control_state(control_object_state_t old, control_object_state_t new) { + return ISC_R_NOTIMPLEMENTED; +} + +int +check_collection(struct packet *p, struct lease *l, struct collection *c) { + return 0; +} + +void +classify (struct packet *p, struct class *c) { +} + +isc_result_t +find_class(struct class **class, const char *c1, const char *c2, int i) { + return ISC_R_NOTFOUND; +} + +int +parse_allow_deny(struct option_cache **oc, struct parse *p, int i) { + return 0; +} + diff --git a/tests/unit_test_sample.c b/tests/unit_test_sample.c new file mode 100644 index 00000000..86d63365 --- /dev/null +++ b/tests/unit_test_sample.c @@ -0,0 +1,25 @@ +#include "config.h" +#include "t_api.h" + +static void foo(void); + +/* + * T_testlist is a list of tests that are invoked. + */ +testspec_t T_testlist[] = { + { foo, "sample test" }, + { NULL, NULL } +}; + +static void +foo(void) { + static const char *test_desc = + "this is an example test, for no actual module"; + + t_assert("sample", 1, T_REQUIRED, test_desc); + + /* ... */ /* Test code would go here. */ + + t_result(T_PASS); +} + |