From ea34dad66fe4a46a2f7ec51c4358144f6cb3ed67 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 26 Apr 2018 11:59:39 +0100 Subject: [PATCH] [unit-test] Push the new unit test framwork. See doc/unit-test.txt for details. Some bcache tests failing. Probably due to dct changing semantics, will fix in follow up patch. --- .gitignore | 2 + Makefile.in | 7 +- configure | 175 +-------- configure.in | 7 +- doc/unit-tests.txt | 257 +++++++++++++ report-generators/lib/log.rb | 40 -- report-generators/lib/report_templates.rb | 38 -- report-generators/lib/reports.rb | 58 --- report-generators/lib/schedule_file.rb | 56 --- report-generators/lib/string-store.rb | 42 -- report-generators/memcheck.rb | 86 ----- .../templates/boiler_plate.rhtml | 25 -- report-generators/templates/index.rhtml | 17 - report-generators/templates/memcheck.rhtml | 30 -- report-generators/templates/unit_detail.rhtml | 37 -- report-generators/templates/unit_test.rhtml | 23 -- report-generators/test/example.schedule | 4 - .../test/strings/more_strings/test3.txt | 1 - report-generators/test/strings/test1.txt | 1 - report-generators/test/strings/test2 | 3 - report-generators/test/tc_log.rb | 36 -- report-generators/test/tc_schedule_file.rb | 38 -- report-generators/test/tc_string_store.rb | 29 -- report-generators/test/ts.rb | 13 - report-generators/title_page.rb | 42 -- report-generators/unit_test.rb | 56 --- reports/stylesheet.css | 77 ---- test/unit/config_t.c | 152 -------- test/unit/dmstatus_t.c | 72 ---- test/unit/matcher_t.c | 70 ---- test/unit/run.c | 39 -- test/unit/string_t.c | 78 ---- test/unit/units.h | 35 -- {test/unit => unit-test}/Makefile.in | 35 +- {test/unit => unit-test}/bcache_t.c | 363 ++++++++++++------ {test/unit => unit-test}/bitset_t.c | 79 ++-- unit-test/config_t.c | 167 ++++++++ {test/unit => unit-test}/dmlist_t.c | 38 +- unit-test/dmstatus_t.c | 84 ++++ unit-test/framework.c | 66 ++++ unit-test/framework.h | 49 +++ {test/unit => unit-test}/matcher_data.h | 0 unit-test/matcher_t.c | 89 +++++ {test/unit => unit-test}/percent_t.c | 73 ++-- unit-test/run.c | 309 +++++++++++++++ unit-test/string_t.c | 91 +++++ unit-test/units.h | 46 +++ 47 files changed, 1544 insertions(+), 1591 deletions(-) create mode 100644 doc/unit-tests.txt delete mode 100644 report-generators/lib/log.rb delete mode 100644 report-generators/lib/report_templates.rb delete mode 100644 report-generators/lib/reports.rb delete mode 100644 report-generators/lib/schedule_file.rb delete mode 100644 report-generators/lib/string-store.rb delete mode 100644 report-generators/memcheck.rb delete mode 100644 report-generators/templates/boiler_plate.rhtml delete mode 100644 report-generators/templates/index.rhtml delete mode 100644 report-generators/templates/memcheck.rhtml delete mode 100644 report-generators/templates/unit_detail.rhtml delete mode 100644 report-generators/templates/unit_test.rhtml delete mode 100644 report-generators/test/example.schedule delete mode 100644 report-generators/test/strings/more_strings/test3.txt delete mode 100644 report-generators/test/strings/test1.txt delete mode 100644 report-generators/test/strings/test2 delete mode 100644 report-generators/test/tc_log.rb delete mode 100644 report-generators/test/tc_schedule_file.rb delete mode 100644 report-generators/test/tc_string_store.rb delete mode 100644 report-generators/test/ts.rb delete mode 100644 report-generators/title_page.rb delete mode 100644 report-generators/unit_test.rb delete mode 100644 reports/stylesheet.css delete mode 100644 test/unit/config_t.c delete mode 100644 test/unit/dmstatus_t.c delete mode 100644 test/unit/matcher_t.c delete mode 100644 test/unit/run.c delete mode 100644 test/unit/string_t.c delete mode 100644 test/unit/units.h rename {test/unit => unit-test}/Makefile.in (58%) rename {test/unit => unit-test}/bcache_t.c (59%) rename {test/unit => unit-test}/bitset_t.c (62%) create mode 100644 unit-test/config_t.c rename {test/unit => unit-test}/dmlist_t.c (60%) create mode 100644 unit-test/dmstatus_t.c create mode 100644 unit-test/framework.c create mode 100644 unit-test/framework.h rename {test/unit => unit-test}/matcher_data.h (100%) create mode 100644 unit-test/matcher_t.c rename {test/unit => unit-test}/percent_t.c (59%) create mode 100644 unit-test/run.c create mode 100644 unit-test/string_t.c create mode 100644 unit-test/units.h diff --git a/.gitignore b/.gitignore index 7e031bfb6..201cbe51d 100644 --- a/.gitignore +++ b/.gitignore @@ -79,3 +79,5 @@ test/lib/vgrename test/lib/vgs test/lib/vgscan test/lib/vgsplit + +unit-test/unit-test diff --git a/Makefile.in b/Makefile.in index 146ed558a..242026752 100644 --- a/Makefile.in +++ b/Makefile.in @@ -43,8 +43,7 @@ endif ifeq ($(MAKECMDGOALS),distclean) SUBDIRS = conf include man test scripts \ libdaemon lib tools daemons libdm \ - udev po liblvm python \ - unit-tests/datastruct unit-tests/mm unit-tests/regex + udev po liblvm python tools.distclean: test.distclean endif DISTCLEAN_DIRS += lcov_reports* @@ -97,7 +96,7 @@ endif DISTCLEAN_TARGETS += cscope.out CLEAN_DIRS += autom4te.cache -check check_system check_cluster check_local check_lvmetad check_lvmpolld check_lvmlockd_test check_lvmlockd_dlm check_lvmlockd_sanlock unit: all +check check_system check_cluster check_local check_lvmetad check_lvmpolld check_lvmlockd_test check_lvmlockd_dlm check_lvmlockd_sanlock unit-test: all $(MAKE) -C test $(@) conf.generate man.generate: tools @@ -212,7 +211,7 @@ endif endif ifeq ("$(TESTING)", "yes") -include test/unit/Makefile +include unit-test/Makefile endif ifneq ($(shell which ctags),) diff --git a/configure b/configure index 50110897c..63ca48037 100755 --- a/configure +++ b/configure @@ -707,9 +707,7 @@ FSADM ELDFLAGS DM_LIB_PATCHLEVEL DMEVENTD_PATH -AIO_LIBS DL_LIBS -AIO DEVMAPPER DEFAULT_USE_LVMLOCKD DEFAULT_USE_LVMPOLLD @@ -783,8 +781,6 @@ LOCKD_SANLOCK_LIBS LOCKD_SANLOCK_CFLAGS VALGRIND_LIBS VALGRIND_CFLAGS -CUNIT_LIBS -CUNIT_CFLAGS GENPNG GENHTML LCOV @@ -957,7 +953,6 @@ enable_profiling enable_testing enable_valgrind_pool enable_devmapper -enable_aio enable_lvmetad enable_lvmpolld enable_lvmlockd_sanlock @@ -1047,8 +1042,6 @@ DLM_CFLAGS DLM_LIBS SACKPT_CFLAGS SACKPT_LIBS -CUNIT_CFLAGS -CUNIT_LIBS VALGRIND_CFLAGS VALGRIND_LIBS LOCKD_SANLOCK_CFLAGS @@ -1707,7 +1700,6 @@ Optional Features: --enable-testing enable testing targets in the makefile --enable-valgrind-pool enable valgrind awareness of pools --disable-devmapper disable LVM2 device-mapper interaction - --disable-aio disable asynchronous I/O --enable-lvmetad enable the LVM Metadata Daemon --enable-lvmpolld enable the LVM Polling Daemon --enable-lvmlockd-sanlock @@ -1894,9 +1886,6 @@ Some influential environment variables: SACKPT_CFLAGS C compiler flags for SACKPT, overriding pkg-config SACKPT_LIBS linker flags for SACKPT, overriding pkg-config - CUNIT_CFLAGS - C compiler flags for CUNIT, overriding pkg-config - CUNIT_LIBS linker flags for CUNIT, overriding pkg-config VALGRIND_CFLAGS C compiler flags for VALGRIND, overriding pkg-config VALGRIND_LIBS @@ -3195,7 +3184,6 @@ case "$host_os" in LDDEPS="$LDDEPS .export.sym" LIB_SUFFIX=so DEVMAPPER=yes - AIO=yes BUILD_LVMETAD=no BUILD_LVMPOLLD=no LOCKDSANLOCK=no @@ -3215,7 +3203,6 @@ case "$host_os" in CLDNOWHOLEARCHIVE= LIB_SUFFIX=dylib DEVMAPPER=yes - AIO=no ODIRECT=no DM_IOCTLS=no SELINUX=no @@ -11623,101 +11610,6 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $TESTING" >&5 $as_echo "$TESTING" >&6; } -if test "$TESTING" = yes; then - pkg_config_init - -pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for CUNIT" >&5 -$as_echo_n "checking for CUNIT... " >&6; } - -if test -n "$CUNIT_CFLAGS"; then - pkg_cv_CUNIT_CFLAGS="$CUNIT_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"cunit >= 2.0\""; } >&5 - ($PKG_CONFIG --exists --print-errors "cunit >= 2.0") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_CUNIT_CFLAGS=`$PKG_CONFIG --cflags "cunit >= 2.0" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi -if test -n "$CUNIT_LIBS"; then - pkg_cv_CUNIT_LIBS="$CUNIT_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"cunit >= 2.0\""; } >&5 - ($PKG_CONFIG --exists --print-errors "cunit >= 2.0") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_CUNIT_LIBS=`$PKG_CONFIG --libs "cunit >= 2.0" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi - - - -if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then - _pkg_short_errors_supported=yes -else - _pkg_short_errors_supported=no -fi - if test $_pkg_short_errors_supported = yes; then - CUNIT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "cunit >= 2.0" 2>&1` - else - CUNIT_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "cunit >= 2.0" 2>&1` - fi - # Put the nasty error message in config.log where it belongs - echo "$CUNIT_PKG_ERRORS" >&5 - - as_fn_error $? "Package requirements (cunit >= 2.0) were not met: - -$CUNIT_PKG_ERRORS - -Consider adjusting the PKG_CONFIG_PATH environment variable if you -installed software in a non-standard prefix. - -Alternatively, you may set the environment variables CUNIT_CFLAGS -and CUNIT_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details." "$LINENO" 5 -elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it -is in your PATH or set the PKG_CONFIG environment variable to the full -path to pkg-config. - -Alternatively, you may set the environment variables CUNIT_CFLAGS -and CUNIT_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details. - -To get pkg-config, see . -See \`config.log' for more details" "$LINENO" 5; } -else - CUNIT_CFLAGS=$pkg_cv_CUNIT_CFLAGS - CUNIT_LIBS=$pkg_cv_CUNIT_LIBS - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - -fi -fi - ################################################################################ TESTSUITE_DATA='${datarootdir}/lvm2-testsuite' # double eval needed ${datarootdir} -> ${prefix}/share -> real path @@ -11844,67 +11736,6 @@ $as_echo "#define DEVMAPPER_SUPPORT 1" >>confdefs.h fi -################################################################################ -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use asynchronous I/O" >&5 -$as_echo_n "checking whether to asynchronous I/O... " >&6; } -# Check whether --enable-aio was given. -if test "${enable_aio+set}" = set; then : - enableval=$enable_aio; AIO=$enableval -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $AIO" >&5 -$as_echo "$AIO" >&6; } - -if test "$AIO" = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for io_setup in -laio" >&5 -$as_echo_n "checking for io_setup in -laio... " >&6; } -if ${ac_cv_lib_aio_io_setup+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-laio $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char io_setup (); -int -main () -{ -return io_setup (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_aio_io_setup=yes -else - ac_cv_lib_aio_io_setup=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_aio_io_setup" >&5 -$as_echo "$ac_cv_lib_aio_io_setup" >&6; } -if test "x$ac_cv_lib_aio_io_setup" = xyes; then : - -$as_echo "#define AIO_SUPPORT 1" >>confdefs.h - - AIO_LIBS="-laio" - AIO_SUPPORT=yes -else - AIO_LIBS= - AIO_SUPPORT=no -fi - -fi - ################################################################################ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build LVMetaD" >&5 $as_echo_n "checking whether to build LVMetaD... " >&6; } @@ -15854,13 +15685,11 @@ _ACEOF - - ################################################################################ -ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/dmfilemapd/Makefile daemons/lvmdbusd/Makefile daemons/lvmdbusd/lvmdbusd daemons/lvmdbusd/lvmdb.py daemons/lvmdbusd/lvm_shell_proxy.py daemons/lvmdbusd/path.py daemons/lvmetad/Makefile daemons/lvmpolld/Makefile daemons/lvmlockd/Makefile conf/Makefile conf/example.conf conf/lvmlocal.conf conf/command_profile_template.profile conf/metadata_profile_template.profile include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile include/lvm-version.h lib/raid/Makefile lib/snapshot/Makefile lib/thin/Makefile lib/cache_segtype/Makefile libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/com.redhat.lvmdbus1.service scripts/dm_event_systemd_red_hat.service scripts/dm_event_systemd_red_hat.socket scripts/lvm2_cluster_activation_red_hat.sh scripts/lvm2_cluster_activation_systemd_red_hat.service scripts/lvm2_clvmd_systemd_red_hat.service scripts/lvm2_cmirrord_systemd_red_hat.service scripts/lvm2_lvmdbusd_systemd_red_hat.service scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmpolld_init_red_hat scripts/lvm2_lvmpolld_systemd_red_hat.service scripts/lvm2_lvmpolld_systemd_red_hat.socket scripts/lvm2_lvmlockd_systemd_red_hat.service scripts/lvm2_lvmlocking_systemd_red_hat.service scripts/lvm2_monitoring_init_red_hat scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_tmpfiles_red_hat.conf scripts/lvmdump.sh scripts/Makefile test/Makefile test/api/Makefile test/unit/Makefile tools/Makefile udev/Makefile" +ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/dmfilemapd/Makefile daemons/lvmdbusd/Makefile daemons/lvmdbusd/lvmdbusd daemons/lvmdbusd/lvmdb.py daemons/lvmdbusd/lvm_shell_proxy.py daemons/lvmdbusd/path.py daemons/lvmetad/Makefile daemons/lvmpolld/Makefile daemons/lvmlockd/Makefile conf/Makefile conf/example.conf conf/lvmlocal.conf conf/command_profile_template.profile conf/metadata_profile_template.profile include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile include/lvm-version.h lib/raid/Makefile lib/snapshot/Makefile lib/thin/Makefile lib/cache_segtype/Makefile libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/com.redhat.lvmdbus1.service scripts/dm_event_systemd_red_hat.service scripts/dm_event_systemd_red_hat.socket scripts/lvm2_cluster_activation_red_hat.sh scripts/lvm2_cluster_activation_systemd_red_hat.service scripts/lvm2_clvmd_systemd_red_hat.service scripts/lvm2_cmirrord_systemd_red_hat.service scripts/lvm2_lvmdbusd_systemd_red_hat.service scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmpolld_init_red_hat scripts/lvm2_lvmpolld_systemd_red_hat.service scripts/lvm2_lvmpolld_systemd_red_hat.socket scripts/lvm2_lvmlockd_systemd_red_hat.service scripts/lvm2_lvmlocking_systemd_red_hat.service scripts/lvm2_monitoring_init_red_hat scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_tmpfiles_red_hat.conf scripts/lvmdump.sh scripts/Makefile test/Makefile test/api/Makefile unit-test/Makefile tools/Makefile udev/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -16634,7 +16463,7 @@ do "scripts/Makefile") CONFIG_FILES="$CONFIG_FILES scripts/Makefile" ;; "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;; "test/api/Makefile") CONFIG_FILES="$CONFIG_FILES test/api/Makefile" ;; - "test/unit/Makefile") CONFIG_FILES="$CONFIG_FILES test/unit/Makefile" ;; + "unit-test/Makefile") CONFIG_FILES="$CONFIG_FILES unit-test/Makefile" ;; "tools/Makefile") CONFIG_FILES="$CONFIG_FILES tools/Makefile" ;; "udev/Makefile") CONFIG_FILES="$CONFIG_FILES udev/Makefile" ;; diff --git a/configure.in b/configure.in index 0d268e83f..461c21bfa 100644 --- a/configure.in +++ b/configure.in @@ -1076,11 +1076,6 @@ AC_ARG_ENABLE(testing, TESTING=$enableval, TESTING=no) AC_MSG_RESULT($TESTING) -if test "$TESTING" = yes; then - pkg_config_init - PKG_CHECK_MODULES(CUNIT, cunit >= 2.0) -fi - ################################################################################ dnl -- Set LVM2 testsuite data TESTSUITE_DATA='${datarootdir}/lvm2-testsuite' @@ -2251,7 +2246,7 @@ scripts/lvmdump.sh scripts/Makefile test/Makefile test/api/Makefile -test/unit/Makefile +unit-test/Makefile tools/Makefile udev/Makefile ]) diff --git a/doc/unit-tests.txt b/doc/unit-tests.txt new file mode 100644 index 000000000..55bbcebe7 --- /dev/null +++ b/doc/unit-tests.txt @@ -0,0 +1,257 @@ +Building unit tests +=================== + + make unit-unit/unit-test + + +Running unit tests +================== + +The tests leave no artifacts at the moment, so you can just run +unit-test/unit-test from wherever you want. + + ./unit-test [pattern] + +Listing tests +------------- + +Every test has a symbolic path associated with it. Just like file paths they +are split into components separated by '/'s. The 'list' command will show you +a tree of these tests, along with some description text. + + +ejt@devel-vm1:~/lvm2/unit-test/$ ./unit-test list +base + data-struct + bitset + and ................................................. and all bits + equal ............................................... equality + get_next ............................................ get next set bit + list + splice .............................................. joining lists together + string + asprint ............................................. tests asprint + strncpy ............................................. tests string copying + device + bcache + block-size-multiple-page ............................ block size must be a multiple of page size + block-size-positive ................................. block size must be positive + blocks-get-evicted .................................. block get evicted with many reads + cache-blocks-positive ............................... nr cache blocks must be positive + create-destroy ...................................... simple create/destroy + flush-waits ......................................... flush waits for all dirty + get-reads ........................................... bcache_get() triggers read + prefetch-never-waits ................................ too many prefetches does not trigger a wait + prefetch-reads ...................................... prefetch issues a read + read-multiple-files ................................. read from multiple files + reads-cached ........................................ repeated reads are cached + writeback-occurs .................................... dirty data gets written back + zero-flag-dirties ................................... zeroed data counts as dirty + formatting + percent + 0 ................................................... Pretty printing of percentages near 0% + 100 ................................................. Pretty printing of percentages near 100% + regex + fingerprints .......................................... not sure + matching .............................................. test the matcher with a variety of regexes +dm + target + mirror + status .............................................. parsing mirror status +metadata + config + cascade ............................................... cascade + clone ................................................. duplicating a config tree + parse ................................................. parsing various + + +An optional 'pattern' argument may be specified to select subsets of tests. +This pattern is a posix regex and does a substring match, so you will need to +use anchors if you particularly want the match at the beginning or end of the +string. + +ejt@devel-vm1:~/lvm2/unit-test/$ ./unit-test list data-struct +base + data-struct + bitset + and ................................................. and all bits + equal ............................................... equality + get_next ............................................ get next set bit + list + splice .............................................. joining lists together + string + asprint ............................................. tests asprint + strncpy ............................................. tests string copying + +ejt@devel-vm1:~/lvm2/unit-test/$ ./unit-test list s$ +base + device + bcache + flush-waits ......................................... flush waits for all dirty + get-reads ........................................... bcache_get() triggers read + prefetch-never-waits ................................ too many prefetches does not trigger a wait + prefetch-reads ...................................... prefetch issues a read + read-multiple-files ................................. read from multiple files + writeback-occurs .................................... dirty data gets written back + zero-flag-dirties ................................... zeroed data counts as dirty + regex + fingerprints .......................................... not sure +dm + target + mirror + status .............................................. parsing mirror status + + +Running tests +============= + +'make run-unit-test' from the top level will run all unit tests. But I tend to +run it by hand to I can select just the tests I'm working on. + +Use the 'run' command to run the tests. Currently all logging goes to stderr, +so the test runner prints a line at the start of the test and a line +indicating success or failure at the end. + +ejt@devel-vm1:~/lvm2/unit-test/$ ./unit-test run bcache/block-size +[RUN ] /base/device/bcache/block-size-multiple-page +bcache block size must be a multiple of page size +bcache block size must be a multiple of page size +bcache block size must be a multiple of page size +bcache block size must be a multiple of page size +[ OK] /base/device/bcache/block-size-multiple-page + +[RUN ] /base/device/bcache/block-size-positive +bcache must have a non zero block size +[ OK] /base/device/bcache/block-size-positive + + +2/2 tests passed + + +ejt@devel-vm1:~/lvm2/unit-test/$ ./unit-test run data-struct +[RUN ] /base/data-struct/bitset/and +[ OK] /base/data-struct/bitset/and + +[RUN ] /base/data-struct/bitset/equal +[ OK] /base/data-struct/bitset/equal + +[RUN ] /base/data-struct/bitset/get_next +[ OK] /base/data-struct/bitset/get_next + +[RUN ] /base/data-struct/list/splice +[ OK] /base/data-struct/list/splice + +[RUN ] /base/data-struct/string/asprint +[ OK] /base/data-struct/string/asprint + +[RUN ] /base/data-struct/string/strncpy +[ OK] /base/data-struct/string/strncpy + + +6/6 tests passed + + +Writing tests +============= + +[See unit-test/framework.h and unit-test/units.h for the details] + +Tests are grouped together into 'suites', all tests in a suite share a +'fixture'. A fixture is a void * to any object you want; use it to set up any +common environment that you need for the tests to run (eg, creating a dm_pool). + +Test suites have nothing to do with the test paths, you can have tests from +different suites with similar paths, the runner sorts things for you. + +Put your tests in a file in unit-test/, with '_t' at the end of the name +(convention only, nothing relies on this). + +#include "units.h" + +Then write any fixtures you need: + +eg, +static void *_mem_init(void) { + struct dm_pool *mem = dm_pool_create("bitset test", 1024); + if (!mem) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + return mem; +} + +static void _mem_exit(void *mem) +{ + dm_pool_destroy(mem); +} + +Then write your tests, which should take the void * that was returned by your +fixture. Use the T_ASSERT* macros to indicate failure. + +eg, +static void test_equal(void *fixture) +{ + struct dm_pool *mem = fixture; + dm_bitset_t bs1 = dm_bitset_create(mem, NR_BITS); + dm_bitset_t bs2 = dm_bitset_create(mem, NR_BITS); + + int i, j; + for (i = 0, j = 1; i < NR_BITS; i += j, j++) { + dm_bit_set(bs1, i); + dm_bit_set(bs2, i); + } + + T_ASSERT(dm_bitset_equal(bs1, bs2)); + T_ASSERT(dm_bitset_equal(bs2, bs1)); + + for (i = 0; i < NR_BITS; i++) { + bit_flip(bs1, i); + T_ASSERT(!dm_bitset_equal(bs1, bs2)); + T_ASSERT(!dm_bitset_equal(bs2, bs1)); + + T_ASSERT(dm_bitset_equal(bs1, bs1)); /* comparing with self */ + bit_flip(bs1, i); + } +} + +At the end of your test file you should write a function that builds one or +more test suites and adds them to the list of all suites that is passed in. I +tend to write a little macro (T) to save typing the same test path repeatedly. + +eg, +#define T(path, desc, fn) register_test(ts, "/base/data-struct/bitset/" path, desc, fn) + +void bitset_tests(struct dm_list *all_tests) +{ + struct test_suite *ts = test_suite_create(_mem_init, _mem_exit); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("get_next", "get next set bit", test_get_next); + T("equal", "equality", test_equal); + T("and", "and all bits", test_and); + + dm_list_add(all_tests, &ts->list); +} + +Then you need to declare your registration function and call it in units.h. + + +// Declare the function that adds tests suites here ... + ... +void bitset_tests(struct dm_list *suites); + ... + +// ... and call it in here. +static inline void register_all_tests(struct dm_list *suites) +{ + ... + bitset_tests(suites); + ... +} + +Finally add your test file to the Makefile.in and rerun configure. + diff --git a/report-generators/lib/log.rb b/report-generators/lib/log.rb deleted file mode 100644 index cf74fc4bc..000000000 --- a/report-generators/lib/log.rb +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (C) 2010 Red Hat, Inc. All rights reserved. -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions -# of the GNU General Public License v.2. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -# Merely wraps the logger library with a bit of standard policy. -require 'logger' - -module Log - $log = Logger.new(STDERR) - - def init(io_) - $log = Logger.new(io_) - end -end - -def fatal(*args) - $log.fatal(*args) -end - -def error(*args) - $log.error(*args) -end - -def info(*args) - $log.info(*args) -end - -def warning(*args) - $log.warn(*args) -end - -def debug(*args) - $log.debug(*args) -end diff --git a/report-generators/lib/report_templates.rb b/report-generators/lib/report_templates.rb deleted file mode 100644 index 3da29abf9..000000000 --- a/report-generators/lib/report_templates.rb +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (C) 2010 Red Hat, Inc. All rights reserved. -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions -# of the GNU General Public License v.2. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -# Policy for the location of report templates -require 'string-store' - -class TemplateStringStore < StringStore - def initialize() - super(['report-generators/templates']) - end -end - -module ReportTemplates - def generate_report(report, bs, dest_path = nil) - include Reports - reports = ReportRegister.new - template_store = TemplateStringStore.new - report = reports.get_report(report) - erb = ERB.new(template_store.lookup(report.template)) - body = erb.result(bs) - title = report.short_desc - - erb = ERB.new(template_store.lookup("boiler_plate.rhtml")) - txt = erb.result(binding) - - dest_path = dest_path.nil? ? report.path : dest_path - dest_path.open("w") do |out| - out.puts txt - end - end -end diff --git a/report-generators/lib/reports.rb b/report-generators/lib/reports.rb deleted file mode 100644 index 2930f8389..000000000 --- a/report-generators/lib/reports.rb +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (C) 2010 Red Hat, Inc. All rights reserved. -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions -# of the GNU General Public License v.2. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -# Data about the various reports we support -require 'log' -require 'pathname' - -module Reports - Report = Struct.new(:short_desc, :desc, :path, :template) - - class ReportRegister - attr_reader :reports - - private - def add_report(sym, *args) - @reports[sym] = Report.new(*args) - end - - public - def initialize() - @reports = Hash.new - - add_report(:unit_test, - "Unit Tests", - "unit tests", - Pathname.new("reports/unit.html"), - Pathname.new("unit_test.rhtml")) - - add_report(:memcheck, - "Memory Tests", - "unit tests with valgrind memory checking", - Pathname.new("reports/memcheck.html"), - Pathname.new("memcheck.rhtml")) - - add_report(:unit_detail, - "Unit Test Detail", - "unit test detail", - Pathname.new("reports/unit_detail.html"), # FIXME replace this with a lambda - Pathname.new("unit_detail.rhtml")) - end - - def get_report(sym) - raise RuntimeError, "unknown report '#{sym}'" unless @reports.member?(sym) - @reports[sym] - end - - def each(&block) - @reports.each(&block) - end - end -end diff --git a/report-generators/lib/schedule_file.rb b/report-generators/lib/schedule_file.rb deleted file mode 100644 index d695f57c7..000000000 --- a/report-generators/lib/schedule_file.rb +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (C) 2010 Red Hat, Inc. All rights reserved. -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions -# of the GNU General Public License v.2. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -# Parses the simple colon delimited test schedule files. - -ScheduledTest = Struct.new(:desc, :command_line, :status, :output) - -class Schedule - attr_reader :dir, :schedules - - def initialize(dir, ss) - @dir = dir - @schedules = ss - end - - def run - Dir::chdir(@dir.to_s) do - @schedules.each do |s| - reader, writer = IO.pipe - print "#{s.desc} ... " - pid = spawn(s.command_line, [ STDERR, STDOUT ] => writer) - writer.close - _, s.status = Process::waitpid2(pid) - puts (s.status.success? ? "pass" : "fail") - s.output = reader.read - end - end - end - - def self.read(dir, io) - ss = Array.new - - io.readlines.each do |line| - case line.strip - when /^\#.*/ - next - - when /([^:]+):(.*)/ - ss << ScheduledTest.new($1.strip, $2.strip) - - else - raise RuntimeError, "badly formatted schedule line" - end - end - - Schedule.new(dir, ss) - end -end - diff --git a/report-generators/lib/string-store.rb b/report-generators/lib/string-store.rb deleted file mode 100644 index 66d223164..000000000 --- a/report-generators/lib/string-store.rb +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (C) 2010 Red Hat, Inc. All rights reserved. -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions -# of the GNU General Public License v.2. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -# Provides a simple way of accessing the contents of files by a symbol -# name. Useful for erb templates. - -require 'pathname' - -class StringStore - attr_accessor :path - - def initialize(p) - @paths = p.nil? ? Array.new : p # FIXME: do we need to copy p ? - end - - def lookup(sym) - files = expansions(sym) - - @paths.each do |p| - files.each do |f| - pn = Pathname.new("#{p}/#{f}") - if pn.file? - return pn.read - end - end - end - - raise RuntimeError, "unknown string entry: #{sym}" - end - - private - def expansions(sym) - ["#{sym}", "#{sym}.txt"] - end -end diff --git a/report-generators/memcheck.rb b/report-generators/memcheck.rb deleted file mode 100644 index 1dccd215d..000000000 --- a/report-generators/memcheck.rb +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (C) 2010 Red Hat, Inc. All rights reserved. -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions -# of the GNU General Public License v.2. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -# Reads the schedule files given on the command line. Runs them and -# generates the reports. - -# FIXME: a lot of duplication with unit_test.rb - -require 'schedule_file' -require 'pathname' -require 'reports' -require 'erb' -require 'report_templates' - -include ReportTemplates - -schedules = ARGV.map do |f| - p = Pathname.new(f) - Schedule.read(p.dirname, p) -end - -total_passed = 0 -total_failed = 0 - -# We need to make sure the lvm shared libs are in the LD_LIBRARY_PATH -ENV['LD_LIBRARY_PATH'] = `pwd`.strip + "/libdm:" + (ENV['LD_LIBRARY_PATH'] || '') - -ENV['TEST_TOOL'] = "valgrind --leak-check=full --show-reachable=yes" - -schedules.each do |s| - s.run - - s.schedules.each do |t| - if t.status.success? - total_passed += 1 - else - total_failed += 1 - end - end -end - -def mangle(txt) - txt.gsub(/\s+/, '_') -end - -MemcheckStats = Struct.new(:definitely_lost, :indirectly_lost, :possibly_lost, :reachable) - -def format(bytes, blocks) - "#{bytes} bytes, #{blocks} blocks" -end - -# Examines the output for details of leaks -def extract_stats(t) - d = i = p = r = '-' - - t.output.split("\n").each do |l| - case l - when /==\d+== definitely lost: ([0-9,]+) bytes in ([0-9,]+) blocks/ - d = format($1, $2) - when /==\d+== indirectly lost: ([0-9,]+) bytes in ([0-9,]+) blocks/ - i = format($1, $2) - when /==\d+== possibly lost: ([0-9,]+) bytes in ([0-9,]+) blocks/ - p = format($1, $2) - when /==\d+== still reachable: ([0-9,]+) bytes in ([0-9,]+) blocks/ - r = format($1, $2) - end - end - - MemcheckStats.new(d, i, p, r) -end - -generate_report(:memcheck, binding) - -# now we generate a detail report for each schedule -schedules.each do |s| - s.schedules.each do |t| - generate_report(:unit_detail, binding, Pathname.new("reports/memcheck_#{mangle(t.desc)}.html")) - end -end diff --git a/report-generators/templates/boiler_plate.rhtml b/report-generators/templates/boiler_plate.rhtml deleted file mode 100644 index 23f01cbdf..000000000 --- a/report-generators/templates/boiler_plate.rhtml +++ /dev/null @@ -1,25 +0,0 @@ - - - -<%= title %> - - - - - -
- - -
- <%= body %> -
-
- diff --git a/report-generators/templates/index.rhtml b/report-generators/templates/index.rhtml deleted file mode 100644 index 6d72081fb..000000000 --- a/report-generators/templates/index.rhtml +++ /dev/null @@ -1,17 +0,0 @@ - - -<% [:unit_test, :memcheck].each do |sym| %> -<% r = reports.get_report(sym) %> - - - - -<% end %> -
ReportGeneration time
- <% if r.path.file? %> - <%= r.short_desc %> - <% else %> - <%= r.short_desc %> - <% end %> - <%= safe_mtime(r) %>
- diff --git a/report-generators/templates/memcheck.rhtml b/report-generators/templates/memcheck.rhtml deleted file mode 100644 index 75872ed49..000000000 --- a/report-generators/templates/memcheck.rhtml +++ /dev/null @@ -1,30 +0,0 @@ - - - -
Tests passedTests failed
<%= total_passed %>><%= total_failed %>
- -<% schedules.each do |s| %> -

<%= s.dir.sub('./unit-tests/', '') %>

- - - -<% s.schedules.each do |t| %> - - - <% if t.status.success? %> - - <% else %> - - <% end %> - - <% stats = extract_stats(t) %> - - - - - -<% end %> -
TestResultDefinitely lostindirectly lostpossibly loststill reachable
- <%= t.desc %> - passfail<%= stats.definitely_lost %><%= stats.indirectly_lost %><%= stats.possibly_lost %><%= stats.reachable %>
-<% end %> diff --git a/report-generators/templates/unit_detail.rhtml b/report-generators/templates/unit_detail.rhtml deleted file mode 100644 index 5324f07cc..000000000 --- a/report-generators/templates/unit_detail.rhtml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - <% if t.status.success? %> - - <% else %> - - <% end %> - -
TestResult
- <%= t.desc %> - passfail
- - - - - - -
Command line
-
-<%= t.command_line %>
-  
-
- - - - - - - -
Output
-
-<%= t.output %>
-  
-
- diff --git a/report-generators/templates/unit_test.rhtml b/report-generators/templates/unit_test.rhtml deleted file mode 100644 index 3137abdd8..000000000 --- a/report-generators/templates/unit_test.rhtml +++ /dev/null @@ -1,23 +0,0 @@ - - - -
Tests passedTests failed
<%= total_passed %>><%= total_failed %>
- -<% schedules.each do |s| %> -

<%= s.dir.sub('./unit-tests/', '') %>

- - -<% s.schedules.each do |t| %> - - - <% if t.status.success? %> - - <% else %> - - <% end %> - -<% end %> -
TestResult
- <%= t.desc %> - passfail
-<% end %> diff --git a/report-generators/test/example.schedule b/report-generators/test/example.schedule deleted file mode 100644 index f617187a1..000000000 --- a/report-generators/test/example.schedule +++ /dev/null @@ -1,4 +0,0 @@ -# This is a comment -description number 1:$TEST_TOOL ls -foo bar: $TEST_TOOL du -hs . - this comment is prefixed with whitespace: $TEST_TOOL date \ No newline at end of file diff --git a/report-generators/test/strings/more_strings/test3.txt b/report-generators/test/strings/more_strings/test3.txt deleted file mode 100644 index 3e9ffe066..000000000 --- a/report-generators/test/strings/more_strings/test3.txt +++ /dev/null @@ -1 +0,0 @@ -lorem diff --git a/report-generators/test/strings/test1.txt b/report-generators/test/strings/test1.txt deleted file mode 100644 index af5626b4a..000000000 --- a/report-generators/test/strings/test1.txt +++ /dev/null @@ -1 +0,0 @@ -Hello, world! diff --git a/report-generators/test/strings/test2 b/report-generators/test/strings/test2 deleted file mode 100644 index 54d55bf0b..000000000 --- a/report-generators/test/strings/test2 +++ /dev/null @@ -1,3 +0,0 @@ -one -two -three \ No newline at end of file diff --git a/report-generators/test/tc_log.rb b/report-generators/test/tc_log.rb deleted file mode 100644 index 8ed339670..000000000 --- a/report-generators/test/tc_log.rb +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (C) 2010 Red Hat, Inc. All rights reserved. -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions -# of the GNU General Public License v.2. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -require 'test/unit' -require 'stringio' -require 'log' - -class TestLog < Test::Unit::TestCase - include Log - - private - def remove_timestamps(l) - l.gsub(/\[[^\]]*\]/, '') - end - - public - def test_log - StringIO.open do |out| - init(out) - - info("msg1") - warning("msg2") - debug("msg3") - - assert_equal("I, INFO -- : msg1\nW, WARN -- : msg2\nD, DEBUG -- : msg3\n", - remove_timestamps(out.string)) - end - end -end diff --git a/report-generators/test/tc_schedule_file.rb b/report-generators/test/tc_schedule_file.rb deleted file mode 100644 index 00f9ec34a..000000000 --- a/report-generators/test/tc_schedule_file.rb +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (C) 2010 Red Hat, Inc. All rights reserved. -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions -# of the GNU General Public License v.2. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -require 'test/unit' -require 'pathname' -require 'schedule_file' - -class TestScheduleFile < Test::Unit::TestCase - def test_reading - p = Pathname.new("report-generators/test/example.schedule") - p.open do |f| - s = Schedule.read(p.dirname, f) - - assert_equal(3, s.schedules.size) - assert_equal(s.schedules[2].desc, "this comment is prefixed with whitespace") - assert_equal(s.schedules[0].command_line, "$TEST_TOOL ls") - end - end - - def test_running - p = Pathname.new("report-generators/test/example.schedule") - p.open do |f| - s = Schedule.read(p.dirname, f) - s.run - - s.schedules.each do |t| - assert(t.status.success?) - end - end - end -end diff --git a/report-generators/test/tc_string_store.rb b/report-generators/test/tc_string_store.rb deleted file mode 100644 index 127e0a832..000000000 --- a/report-generators/test/tc_string_store.rb +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (C) 2010 Red Hat, Inc. All rights reserved. -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions -# of the GNU General Public License v.2. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -require 'string-store' -require 'test/unit' - -class TestStringStore < Test::Unit::TestCase - def setup - @ss = StringStore.new(['report-generators/test/strings', - 'report-generators/test/strings/more_strings']) - end - - def test_lookup - assert_equal("Hello, world!\n", @ss.lookup(:test1)) - assert_equal("one\ntwo\nthree", @ss.lookup(:test2)) - assert_equal("lorem\n", @ss.lookup(:test3)) - - assert_raises(RuntimeError) do - @ss.lookup(:unlikely_name) - end - end -end diff --git a/report-generators/test/ts.rb b/report-generators/test/ts.rb deleted file mode 100644 index 0501780f4..000000000 --- a/report-generators/test/ts.rb +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (C) 2010 Red Hat, Inc. All rights reserved. -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions -# of the GNU General Public License v.2. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -require 'tc_log' -require 'tc_string_store' -require 'tc_schedule_file' diff --git a/report-generators/title_page.rb b/report-generators/title_page.rb deleted file mode 100644 index 66e5b03d1..000000000 --- a/report-generators/title_page.rb +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (C) 2010 Red Hat, Inc. All rights reserved. -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions -# of the GNU General Public License v.2. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -# This generates the index for the reports, including generation -# times. - -require 'log' -require 'string-store' -require 'reports' -require 'erb' -require 'report_templates' - -include Reports - -reports = ReportRegister.new - -def safe_mtime(r) - r.path.file? ? r.path.mtime.to_s : "not generated" -end - -template_store = TemplateStringStore.new - -# FIXME: use generate_report() method -erb = ERB.new(template_store.lookup("index.rhtml")) -body = erb.result(binding) -title = "Generation times" - -erb = ERB.new(template_store.lookup("boiler_plate.rhtml")) -txt = erb.result(binding) - -Pathname.new("reports/index.html").open("w") do |f| - f.puts txt -end - - diff --git a/report-generators/unit_test.rb b/report-generators/unit_test.rb deleted file mode 100644 index 1e5c8954f..000000000 --- a/report-generators/unit_test.rb +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (C) 2010 Red Hat, Inc. All rights reserved. -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions -# of the GNU General Public License v.2. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -# Reads the schedule files given on the command line. Runs them and -# generates the reports. - -require 'schedule_file' -require 'pathname' -require 'reports' -require 'erb' -require 'report_templates' - -include ReportTemplates - -schedules = ARGV.map do |f| - p = Pathname.new(f) - Schedule.read(p.dirname, p) -end - -total_passed = 0 -total_failed = 0 - -# We need to make sure the lvm shared libs are in the LD_LIBRARY_PATH -ENV['LD_LIBRARY_PATH'] = `pwd`.strip + "/libdm:" + (ENV['LD_LIBRARY_PATH'] || '') - -schedules.each do |s| - s.run - - s.schedules.each do |t| - if t.status.success? - total_passed += 1 - else - total_failed += 1 - end - end -end - -def mangle(txt) - txt.gsub(/\s+/, '_') -end - -generate_report(:unit_test, binding) - -# now we generate a detail report for each schedule -schedules.each do |s| - s.schedules.each do |t| - generate_report(:unit_detail, binding, Pathname.new("reports/detail_#{mangle(t.desc)}.html")) - end -end diff --git a/reports/stylesheet.css b/reports/stylesheet.css deleted file mode 100644 index 3d41926af..000000000 --- a/reports/stylesheet.css +++ /dev/null @@ -1,77 +0,0 @@ -/* Styles for main page */ -#banner { - background: #9c9; - padding-top: 5px; - padding-bottom: 5px; - border-bottom: 2px solid; - font: small-caps 20px/20px "Times New Roman", serif; - color: #282; - text-align: center; -} - -#banner img { - float: left; -} - -#main { - margin-left: 0em; - padding-top: 4ex; - padding-left: 2em; - background: white; -} - -h1 { - font: 150% sans-serif; - color: #226; - border-bottom: 3px dotted #77d; -} - -body { - font: normal 75% verdana,arial,helvetica; - color:#000000; -} - -table tr td, table tr th { - font-size: 75%; -} - -table.stripes tr th { - font-weight: bold; - text-align: left; - background: #a0a0a0; -} - -table.stripes tr td { - background: #ccccc0; -} - -td.pass { - color: green; -} - -td.fail { - color: red; - font-weight: bold; -} - -#main { - padding-left: 0em; -} - -#controls { - float: left; - padding-top: 1em; - padding-left: 1em; - padding-right: 1em; - padding-bottom: 1em; - width: 14em; - border-right: 2px solid; -} - -#body { - margin-left: 16em; - padding-top: 4ex; - padding-left: 2em; - background: white; - border-left: 2px solid; -} diff --git a/test/unit/config_t.c b/test/unit/config_t.c deleted file mode 100644 index 342667ecd..000000000 --- a/test/unit/config_t.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2010 Red Hat, Inc. All rights reserved. - * - * This file is part of LVM2. - * - * This copyrighted material is made available to anyone wishing to use, - * modify, copy, or redistribute it subject to the terms and conditions - * of the GNU General Public License v.2. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "units.h" - -static struct dm_pool *mem; - -int config_init(void) { - mem = dm_pool_create("config test", 1024); - return mem == NULL; -} - -int config_fini(void) { - dm_pool_destroy(mem); - return 0; -} - -static const char *conf = - "id = \"yada-yada\"\n" - "seqno = 15\n" - "status = [\"READ\", \"WRITE\"]\n" - "flags = []\n" - "extent_size = 8192\n" - "physical_volumes {\n" - " pv0 {\n" - " id = \"abcd-efgh\"\n" - " }\n" - " pv1 {\n" - " id = \"bbcd-efgh\"\n" - " }\n" - " pv2 {\n" - " id = \"cbcd-efgh\"\n" - " }\n" - "}\n"; - -static const char *overlay = - "id = \"yoda-soda\"\n" - "flags = [\"FOO\"]\n" - "physical_volumes {\n" - " pv1 {\n" - " id = \"hgfe-dcba\"\n" - " }\n" - " pv3 {\n" - " id = \"dbcd-efgh\"\n" - " }\n" - "}\n"; - -static void test_parse(void) -{ - struct dm_config_tree *tree = dm_config_from_string(conf); - const struct dm_config_value *value; - - CU_ASSERT((long) tree); - CU_ASSERT(dm_config_has_node(tree->root, "id")); - CU_ASSERT(dm_config_has_node(tree->root, "physical_volumes")); - CU_ASSERT(dm_config_has_node(tree->root, "physical_volumes/pv0")); - CU_ASSERT(dm_config_has_node(tree->root, "physical_volumes/pv0/id")); - - CU_ASSERT(!strcmp(dm_config_find_str(tree->root, "id", "foo"), "yada-yada")); - CU_ASSERT(!strcmp(dm_config_find_str(tree->root, "idt", "foo"), "foo")); - - CU_ASSERT(!strcmp(dm_config_find_str(tree->root, "physical_volumes/pv0/bb", "foo"), "foo")); - CU_ASSERT(!strcmp(dm_config_find_str(tree->root, "physical_volumes/pv0/id", "foo"), "abcd-efgh")); - - CU_ASSERT(!dm_config_get_uint32(tree->root, "id", NULL)); - CU_ASSERT(dm_config_get_uint32(tree->root, "extent_size", NULL)); - - /* FIXME: Currently everything parses as a list, even if it's not */ - // CU_ASSERT(!dm_config_get_list(tree->root, "id", NULL)); - // CU_ASSERT(!dm_config_get_list(tree->root, "extent_size", NULL)); - - CU_ASSERT(dm_config_get_list(tree->root, "flags", &value)); - CU_ASSERT(value->next == NULL); /* an empty list */ - CU_ASSERT(dm_config_get_list(tree->root, "status", &value)); - CU_ASSERT(value->next != NULL); /* a non-empty list */ - - dm_config_destroy(tree); -} - -static void test_clone(void) -{ - struct dm_config_tree *tree = dm_config_from_string(conf); - struct dm_config_node *n = dm_config_clone_node(tree, tree->root, 1); - const struct dm_config_value *value; - - /* Check that the nodes are actually distinct. */ - CU_ASSERT(n != tree->root); - CU_ASSERT(n->sib != tree->root->sib); - CU_ASSERT(dm_config_find_node(n, "physical_volumes") != NULL); - CU_ASSERT(dm_config_find_node(tree->root, "physical_volumes") != NULL); - CU_ASSERT(dm_config_find_node(n, "physical_volumes") != dm_config_find_node(tree->root, "physical_volumes")); - - CU_ASSERT(dm_config_has_node(n, "id")); - CU_ASSERT(dm_config_has_node(n, "physical_volumes")); - CU_ASSERT(dm_config_has_node(n, "physical_volumes/pv0")); - CU_ASSERT(dm_config_has_node(n, "physical_volumes/pv0/id")); - - CU_ASSERT(!strcmp(dm_config_find_str(n, "id", "foo"), "yada-yada")); - CU_ASSERT(!strcmp(dm_config_find_str(n, "idt", "foo"), "foo")); - - CU_ASSERT(!strcmp(dm_config_find_str(n, "physical_volumes/pv0/bb", "foo"), "foo")); - CU_ASSERT(!strcmp(dm_config_find_str(n, "physical_volumes/pv0/id", "foo"), "abcd-efgh")); - - CU_ASSERT(!dm_config_get_uint32(n, "id", NULL)); - CU_ASSERT(dm_config_get_uint32(n, "extent_size", NULL)); - - /* FIXME: Currently everything parses as a list, even if it's not */ - // CU_ASSERT(!dm_config_get_list(tree->root, "id", NULL)); - // CU_ASSERT(!dm_config_get_list(tree->root, "extent_size", NULL)); - - CU_ASSERT(dm_config_get_list(n, "flags", &value)); - CU_ASSERT(value->next == NULL); /* an empty list */ - CU_ASSERT(dm_config_get_list(n, "status", &value)); - CU_ASSERT(value->next != NULL); /* a non-empty list */ - - dm_config_destroy(tree); -} - -static void test_cascade(void) -{ - struct dm_config_tree *t1 = dm_config_from_string(conf), - *t2 = dm_config_from_string(overlay), - *tree = dm_config_insert_cascaded_tree(t2, t1); - - CU_ASSERT(!strcmp(dm_config_tree_find_str(tree, "id", "foo"), "yoda-soda")); - CU_ASSERT(!strcmp(dm_config_tree_find_str(tree, "idt", "foo"), "foo")); - - CU_ASSERT(!strcmp(dm_config_tree_find_str(tree, "physical_volumes/pv0/bb", "foo"), "foo")); - CU_ASSERT(!strcmp(dm_config_tree_find_str(tree, "physical_volumes/pv1/id", "foo"), "hgfe-dcba")); - CU_ASSERT(!strcmp(dm_config_tree_find_str(tree, "physical_volumes/pv3/id", "foo"), "dbcd-efgh")); - - dm_config_destroy(t1); - dm_config_destroy(t2); -} - -CU_TestInfo config_list[] = { - { (char*)"parse", test_parse }, - { (char*)"clone", test_clone }, - { (char*)"cascade", test_cascade }, - CU_TEST_INFO_NULL -}; diff --git a/test/unit/dmstatus_t.c b/test/unit/dmstatus_t.c deleted file mode 100644 index 00af56dcd..000000000 --- a/test/unit/dmstatus_t.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2015 Red Hat, Inc. All rights reserved. - * - * This file is part of LVM2. - * - * This copyrighted material is made available to anyone wishing to use, - * modify, copy, or redistribute it subject to the terms and conditions - * of the GNU General Public License v.2. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "units.h" - -static struct dm_pool *_mem; - -int dmstatus_init(void) -{ - _mem = dm_pool_create("dmstatus test", 1024); - return (_mem == NULL); -} - -int dmstatus_fini(void) -{ - dm_pool_destroy(_mem); - return 0; -} - -static void _test_mirror_status(void) -{ - struct dm_status_mirror *s = NULL; - - CU_ASSERT(dm_get_status_mirror(_mem, - "2 253:1 253:2 80/81 1 AD 3 disk 253:0 A", - &s)); - if (s) { - CU_ASSERT_EQUAL(s->total_regions, 81); - CU_ASSERT_EQUAL(s->insync_regions, 80); - CU_ASSERT_EQUAL(s->dev_count, 2); - CU_ASSERT_EQUAL(s->devs[0].health, 'A'); - CU_ASSERT_EQUAL(s->devs[0].major, 253); - CU_ASSERT_EQUAL(s->devs[0].minor, 1); - CU_ASSERT_EQUAL(s->devs[1].health, 'D'); - CU_ASSERT_EQUAL(s->devs[1].major, 253); - CU_ASSERT_EQUAL(s->devs[1].minor, 2); - CU_ASSERT_EQUAL(s->log_count, 1); - CU_ASSERT_EQUAL(s->logs[0].major, 253); - CU_ASSERT_EQUAL(s->logs[0].minor, 0); - CU_ASSERT_EQUAL(s->logs[0].health, 'A'); - CU_ASSERT(!strcmp(s->log_type, "disk")); - } - - CU_ASSERT(dm_get_status_mirror(_mem, - "4 253:1 253:2 253:3 253:4 10/10 1 ADFF 1 core", - &s)); - if (s) { - CU_ASSERT_EQUAL(s->total_regions, 10); - CU_ASSERT_EQUAL(s->insync_regions, 10); - CU_ASSERT_EQUAL(s->dev_count, 4); - CU_ASSERT_EQUAL(s->devs[3].minor, 4); - CU_ASSERT_EQUAL(s->devs[3].health, 'F'); - CU_ASSERT_EQUAL(s->log_count, 0); - CU_ASSERT(!strcmp(s->log_type, "core")); - } -} - -CU_TestInfo dmstatus_list[] = { - { (char*)"mirror_status", _test_mirror_status }, - CU_TEST_INFO_NULL -}; diff --git a/test/unit/matcher_t.c b/test/unit/matcher_t.c deleted file mode 100644 index f7fac975e..000000000 --- a/test/unit/matcher_t.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. - * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved. - * - * This file is part of LVM2. - * - * This copyrighted material is made available to anyone wishing to use, - * modify, copy, or redistribute it subject to the terms and conditions - * of the GNU General Public License v.2. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "units.h" - -#include "matcher_data.h" - -static struct dm_pool *mem = NULL; - -int regex_init(void) { - mem = dm_pool_create("bitset test", 1024); - return mem == NULL; -} - -int regex_fini(void) { - dm_pool_destroy(mem); - return 0; -} - -static struct dm_regex *make_scanner(const char **rx) -{ - struct dm_regex *scanner; - int nrx = 0; - for (; rx[nrx]; ++nrx); - - scanner = dm_regex_create(mem, rx, nrx); - CU_ASSERT_FATAL(scanner != NULL); - return scanner; -} - -static void test_fingerprints(void) { - struct dm_regex *scanner; - - scanner = make_scanner(dev_patterns); - CU_ASSERT_EQUAL(dm_regex_fingerprint(scanner), 0x7f556c09); - - scanner = make_scanner(random_patterns); - CU_ASSERT_EQUAL(dm_regex_fingerprint(scanner), 0x9f11076c); -} - -static void test_matching(void) { - struct dm_regex *scanner; - int i; - - scanner = make_scanner(dev_patterns); - for (i = 0; devices[i].str; ++i) - CU_ASSERT_EQUAL(dm_regex_match(scanner, devices[i].str), devices[i].expected - 1); - - scanner = make_scanner(nonprint_patterns); - for (i = 0; nonprint[i].str; ++i) - CU_ASSERT_EQUAL(dm_regex_match(scanner, nonprint[i].str), nonprint[i].expected - 1); -} - -CU_TestInfo regex_list[] = { - { (char*)"fingerprints", test_fingerprints }, - { (char*)"matching", test_matching }, - CU_TEST_INFO_NULL -}; diff --git a/test/unit/run.c b/test/unit/run.c deleted file mode 100644 index 82090ba55..000000000 --- a/test/unit/run.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "units.h" -#include - -#include -#include - -/* Setup SuiteInfo struct in a compatible way across different CUnit versions */ -/* old version of CUnit has used char* for .pName, so using cast here */ -#define USE(n) { \ - .pName = (char*) #n, \ - .pInitFunc = n##_init, \ - .pCleanupFunc = n##_fini, \ - .pTests = n##_list } - -CU_SuiteInfo suites[] = { - USE(bcache), - USE(bitset), - USE(config), - USE(dmlist), - USE(dmstatus), - USE(regex), - USE(percent), - USE(string), - CU_SUITE_INFO_NULL -}; - -int main(int argc, char **argv) { - if (CU_initialize_registry() != CUE_SUCCESS) { - printf("Initialization of Test Registry failed.\n"); - return CU_get_error(); - } - - CU_register_suites(suites); - CU_basic_set_mode(CU_BRM_VERBOSE); - CU_basic_run_tests(); - CU_cleanup_registry(); - - return (CU_get_number_of_failures() != 0); -} diff --git a/test/unit/string_t.c b/test/unit/string_t.c deleted file mode 100644 index 68e39c69a..000000000 --- a/test/unit/string_t.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2012 Red Hat, Inc. All rights reserved. - * - * This file is part of LVM2. - * - * This copyrighted material is made available to anyone wishing to use, - * modify, copy, or redistribute it subject to the terms and conditions - * of the GNU General Public License v.2. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "units.h" - -#include -#include - -static struct dm_pool *mem = NULL; - -int string_init(void) -{ - mem = dm_pool_create("string test", 1024); - - return (mem == NULL); -} - -int string_fini(void) -{ - dm_pool_destroy(mem); - - return 0; -} - -/* TODO: Add more string unit tests here */ - -static void test_strncpy(void) -{ - const char st[] = "1234567890"; - char buf[sizeof(st)]; - - CU_ASSERT_EQUAL(dm_strncpy(buf, st, sizeof(buf)), 1); - CU_ASSERT_EQUAL(strcmp(buf, st), 0); - - CU_ASSERT_EQUAL(dm_strncpy(buf, st, sizeof(buf) - 1), 0); - CU_ASSERT_EQUAL(strlen(buf) + 1, sizeof(buf) - 1); -} - -static void test_asprint(void) -{ - const char st0[] = ""; - const char st1[] = "12345678901"; - const char st2[] = "1234567890123456789012345678901234567890123456789012345678901234567"; - char *buf; - int a; - - a = dm_asprintf(&buf, "%s", st0); - CU_ASSERT_EQUAL(strcmp(buf, st0), 0); - CU_ASSERT_EQUAL(a, sizeof(st0)); - free(buf); - - a = dm_asprintf(&buf, "%s", st1); - CU_ASSERT_EQUAL(strcmp(buf, st1), 0); - CU_ASSERT_EQUAL(a, sizeof(st1)); - free(buf); - - a = dm_asprintf(&buf, "%s", st2); - CU_ASSERT_EQUAL(a, sizeof(st2)); - CU_ASSERT_EQUAL(strcmp(buf, st2), 0); - free(buf); -} - -CU_TestInfo string_list[] = { - { (char*)"asprint", test_asprint }, - { (char*)"strncpy", test_strncpy }, - CU_TEST_INFO_NULL -}; diff --git a/test/unit/units.h b/test/unit/units.h deleted file mode 100644 index 319e7ceb9..000000000 --- a/test/unit/units.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2015-2017 Red Hat, Inc. All rights reserved. - * - * This file is part of LVM2. - * - * This copyrighted material is made available to anyone wishing to use, - * modify, copy, or redistribute it subject to the terms and conditions - * of the GNU General Public License v.2. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _UNITS_H -#define _UNITS_H - -#include "libdevmapper.h" -#include - -#define DECL(n) \ - extern CU_TestInfo n ## _list[];\ - int n ## _init(void); \ - int n ## _fini(void); - -DECL(bcache); -DECL(bitset); -DECL(config); -DECL(dmlist); -DECL(dmstatus); -DECL(regex); -DECL(percent); -DECL(string); - -#endif diff --git a/test/unit/Makefile.in b/unit-test/Makefile.in similarity index 58% rename from test/unit/Makefile.in rename to unit-test/Makefile.in index a070329ac..557770654 100644 --- a/test/unit/Makefile.in +++ b/unit-test/Makefile.in @@ -11,29 +11,30 @@ # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA UNIT_SOURCE=\ - test/unit/bcache_t.c \ - - -# test/unit/run.c - -# test/unit/bitset_t.c\ -# test/unit/config_t.c\ -# test/unit/dmlist_t.c\ -# test/unit/dmstatus_t.c\ -# test/unit/matcher_t.c\ -# test/unit/percent_t.c\ -# test/unit/string_t.c\ + unit-test/bcache_t.c \ + unit-test/bitset_t.c \ + unit-test/config_t.c \ + unit-test/dmlist_t.c \ + unit-test/dmstatus_t.c \ + unit-test/matcher_t.c \ + unit-test/framework.c \ + unit-test/percent_t.c \ + unit-test/run.c \ + unit-test/string_t.c +UNIT_DEPENDS=$(subst .c,.d,$(UNIT_SOURCE)) UNIT_OBJECTS=$(UNIT_SOURCE:%.c=%.o) - +CLEAN_TARGETS+=$(UNIT_DEPENDS) $(UNIT_OBJECTS) UNIT_LDLIBS += $(LVMINTERNAL_LIBS) -ldevmapper -laio -test/unit/run: $(UNIT_OBJECTS) libdm/libdevmapper.$(LIB_SUFFIX) lib/liblvm-internal.a +unit-test/unit-test: $(UNIT_OBJECTS) libdm/libdevmapper.$(LIB_SUFFIX) lib/liblvm-internal.a @echo " [LD] $@" $(Q) $(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) -L$(top_builddir)/libdm \ -o $@ $(UNIT_OBJECTS) $(UNIT_LDLIBS) -.PHONEY: unit-test -unit-test: test/unit/run +.PHONEY: run-unit-test +run-unit-test: unit-test/unit-test @echo Running unit tests - LD_LIBRARY_PATH=libdm test/unit/run + LD_LIBRARY_PATH=libdm unit-test/unit-test run + +-include $(UNIT_DEPENDS) diff --git a/test/unit/bcache_t.c b/unit-test/bcache_t.c similarity index 59% rename from test/unit/bcache_t.c rename to unit-test/bcache_t.c index 07a45fe22..4fe913b6a 100644 --- a/test/unit/bcache_t.c +++ b/unit-test/bcache_t.c @@ -12,40 +12,17 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include #include #include -#include #include "bcache.h" +#include "framework.h" +#include "units.h" #define SHOW_MOCK_CALLS 0 -/*---------------------------------------------------------------- - * Assertions - *--------------------------------------------------------------*/ - -static jmp_buf _test_k; -#define TEST_FAILED 1 - -static void _fail(const char *fmt, ...) - __attribute__((format (printf, 1, 2))); - - -static void _fail(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - fprintf(stderr, "\n"); - - longjmp(_test_k, TEST_FAILED); -} - -#define T_ASSERT(e) if (!(e)) {_fail("assertion failed: '%s'", # e);} - /*---------------------------------------------------------------- * Mock engine *--------------------------------------------------------------*/ @@ -72,6 +49,8 @@ struct mock_call { enum dir d; int fd; block_address b; + bool issue_r; + bool wait_r; }; struct mock_io { @@ -81,6 +60,7 @@ struct mock_io { sector_t se; void *data; void *context; + bool r; }; static const char *_show_method(enum method m) @@ -115,6 +95,8 @@ static void _expect_read(struct mock_engine *e, int fd, block_address b) mc->d = DIR_READ; mc->fd = fd; mc->b = b; + mc->issue_r = true; + mc->wait_r = true; dm_list_add(&e->expected_calls, &mc->list); } @@ -126,6 +108,60 @@ static void _expect_write(struct mock_engine *e, int fd, block_address b) mc->d = DIR_WRITE; mc->fd = fd; mc->b = b; + mc->issue_r = true; + mc->wait_r = true; + dm_list_add(&e->expected_calls, &mc->list); +} + +static void _expect_read_bad_issue(struct mock_engine *e, int fd, block_address b) +{ + struct mock_call *mc = malloc(sizeof(*mc)); + mc->m = E_ISSUE; + mc->match_args = true; + mc->d = DIR_READ; + mc->fd = fd; + mc->b = b; + mc->issue_r = false; + mc->wait_r = true; + dm_list_add(&e->expected_calls, &mc->list); +} + +static void _expect_write_bad_issue(struct mock_engine *e, int fd, block_address b) +{ + struct mock_call *mc = malloc(sizeof(*mc)); + mc->m = E_ISSUE; + mc->match_args = true; + mc->d = DIR_WRITE; + mc->fd = fd; + mc->b = b; + mc->issue_r = false; + mc->wait_r = true; + dm_list_add(&e->expected_calls, &mc->list); +} + +static void _expect_read_bad_wait(struct mock_engine *e, int fd, block_address b) +{ + struct mock_call *mc = malloc(sizeof(*mc)); + mc->m = E_ISSUE; + mc->match_args = true; + mc->d = DIR_READ; + mc->fd = fd; + mc->b = b; + mc->issue_r = true; + mc->wait_r = false; + dm_list_add(&e->expected_calls, &mc->list); +} + +static void _expect_write_bad_wait(struct mock_engine *e, int fd, block_address b) +{ + struct mock_call *mc = malloc(sizeof(*mc)); + mc->m = E_ISSUE; + mc->match_args = true; + mc->d = DIR_WRITE; + mc->fd = fd; + mc->b = b; + mc->issue_r = true; + mc->wait_r = false; dm_list_add(&e->expected_calls, &mc->list); } @@ -135,13 +171,13 @@ static struct mock_call *_match_pop(struct mock_engine *e, enum method m) struct mock_call *mc; if (dm_list_empty(&e->expected_calls)) - _fail("unexpected call to method %s\n", _show_method(m)); + test_fail("unexpected call to method %s\n", _show_method(m)); mc = dm_list_item(e->expected_calls.n, struct mock_call); dm_list_del(&mc->list); if (mc->m != m) - _fail("expected %s, but got %s\n", _show_method(mc->m), _show_method(m)); + test_fail("expected %s, but got %s\n", _show_method(mc->m), _show_method(m)); #if SHOW_MOCK_CALLS else fprintf(stderr, "%s called (expected)\n", _show_method(m)); @@ -185,6 +221,7 @@ static void _mock_destroy(struct io_engine *e) static bool _mock_issue(struct io_engine *e, enum dir d, int fd, sector_t sb, sector_t se, void *data, void *context) { + bool r, wait_r; struct mock_io *io; struct mock_call *mc; struct mock_engine *me = _to_mock(e); @@ -196,20 +233,26 @@ static bool _mock_issue(struct io_engine *e, enum dir d, int fd, T_ASSERT(sb == mc->b * me->block_size); T_ASSERT(se == (mc->b + 1) * me->block_size); } + r = mc->issue_r; + wait_r = mc->wait_r; free(mc); - io = malloc(sizeof(*io)); - if (!io) - abort(); + if (r) { + io = malloc(sizeof(*io)); + if (!io) + abort(); - io->fd = fd; - io->sb = sb; - io->se = se; - io->data = data; - io->context = context; + io->fd = fd; + io->sb = sb; + io->se = se; + io->data = data; + io->context = context; + io->r = wait_r; - dm_list_add(&me->issued_io, &io->list); - return true; + dm_list_add(&me->issued_io, &io->list); + } + + return r; } static bool _mock_wait(struct io_engine *e, io_complete_fn fn) @@ -223,7 +266,9 @@ static bool _mock_wait(struct io_engine *e, io_complete_fn fn) T_ASSERT(!dm_list_empty(&me->issued_io)); io = dm_list_item(me->issued_io.n, struct mock_io); dm_list_del(&io->list); - fn(io->context, 0); + fn(io->context, io->r ? 0 : -EIO); + free(io); + return true; } @@ -363,6 +408,7 @@ static void test_block_size_must_be_multiple_of_page_size(void *fixture) static void test_get_triggers_read(void *context) { + int err; struct fixture *f = context; int fd = 17; // arbitrary key @@ -370,12 +416,13 @@ static void test_get_triggers_read(void *context) _expect_read(f->me, fd, 0); _expect(f->me, E_WAIT); - T_ASSERT(bcache_get(f->cache, fd, 0, 0, &b)); + T_ASSERT(bcache_get(f->cache, fd, 0, 0, &b, &err)); bcache_put(b); } static void test_repeated_reads_are_cached(void *context) { + int err; struct fixture *f = context; int fd = 17; // arbitrary key @@ -385,7 +432,7 @@ static void test_repeated_reads_are_cached(void *context) _expect_read(f->me, fd, 0); _expect(f->me, E_WAIT); for (i = 0; i < 100; i++) { - T_ASSERT(bcache_get(f->cache, fd, 0, 0, &b)); + T_ASSERT(bcache_get(f->cache, fd, 0, 0, &b, &err)); bcache_put(b); } } @@ -394,6 +441,7 @@ static void test_block_gets_evicted_with_many_reads(void *context) { struct fixture *f = context; + int err; struct mock_engine *me = f->me; struct bcache *cache = f->cache; const unsigned nr_cache_blocks = 16; @@ -405,14 +453,14 @@ static void test_block_gets_evicted_with_many_reads(void *context) for (i = 0; i < nr_cache_blocks; i++) { _expect_read(me, fd, i); _expect(me, E_WAIT); - T_ASSERT(bcache_get(cache, fd, i, 0, &b)); + T_ASSERT(bcache_get(cache, fd, i, 0, &b, &err)); bcache_put(b); } // Not enough cache blocks to hold this one _expect_read(me, fd, nr_cache_blocks); _expect(me, E_WAIT); - T_ASSERT(bcache_get(cache, fd, nr_cache_blocks, 0, &b)); + T_ASSERT(bcache_get(cache, fd, nr_cache_blocks, 0, &b, &err)); bcache_put(b); // Now if we run through we should find one block has been @@ -421,7 +469,7 @@ static void test_block_gets_evicted_with_many_reads(void *context) _expect(me, E_ISSUE); _expect(me, E_WAIT); for (i = nr_cache_blocks; i; i--) { - T_ASSERT(bcache_get(cache, fd, i - 1, 0, &b)); + T_ASSERT(bcache_get(cache, fd, i - 1, 0, &b, &err)); bcache_put(b); } } @@ -433,6 +481,7 @@ static void test_prefetch_issues_a_read(void *context) struct bcache *cache = f->cache; const unsigned nr_cache_blocks = 16; + int err; int fd = 17; // arbitrary key unsigned i; struct block *b; @@ -446,7 +495,7 @@ static void test_prefetch_issues_a_read(void *context) for (i = 0; i < nr_cache_blocks; i++) { _expect(me, E_WAIT); - T_ASSERT(bcache_get(cache, fd, i, 0, &b)); + T_ASSERT(bcache_get(cache, fd, i, 0, &b, &err)); bcache_put(b); } } @@ -479,13 +528,14 @@ static void test_dirty_data_gets_written_back(void *context) struct mock_engine *me = f->me; struct bcache *cache = f->cache; + int err; int fd = 17; // arbitrary key struct block *b; // Expect the read _expect_read(me, fd, 0); _expect(me, E_WAIT); - T_ASSERT(bcache_get(cache, fd, 0, GF_DIRTY, &b)); + T_ASSERT(bcache_get(cache, fd, 0, GF_DIRTY, &b, &err)); bcache_put(b); // Expect the write @@ -499,11 +549,12 @@ static void test_zeroed_data_counts_as_dirty(void *context) struct mock_engine *me = f->me; struct bcache *cache = f->cache; + int err; int fd = 17; // arbitrary key struct block *b; // No read - T_ASSERT(bcache_get(cache, fd, 0, GF_ZERO, &b)); + T_ASSERT(bcache_get(cache, fd, 0, GF_ZERO, &b, &err)); bcache_put(b); // Expect the write @@ -518,17 +569,18 @@ static void test_flush_waits_for_all_dirty(void *context) struct bcache *cache = f->cache; const unsigned count = 16; + int err; int fd = 17; // arbitrary key unsigned i; struct block *b; for (i = 0; i < count; i++) { if (i % 2) { - T_ASSERT(bcache_get(cache, fd, i, GF_ZERO, &b)); + T_ASSERT(bcache_get(cache, fd, i, GF_ZERO, &b, &err)); } else { _expect_read(me, fd, i); _expect(me, E_WAIT); - T_ASSERT(bcache_get(cache, fd, i, 0, &b)); + T_ASSERT(bcache_get(cache, fd, i, 0, &b, &err)); } bcache_put(b); } @@ -547,10 +599,11 @@ static void test_flush_waits_for_all_dirty(void *context) _no_outstanding_expectations(me); } -static void test_multiple_files(void * context) +static void test_multiple_files(void *context) { static int _fds[] = {1, 128, 345, 678, 890}; + int err; struct fixture *f = context; struct mock_engine *me = f->me; struct bcache *cache = f->cache; @@ -561,76 +614,170 @@ static void test_multiple_files(void * context) _expect_read(me, _fds[i], 0); _expect(me, E_WAIT); - T_ASSERT(bcache_get(cache, _fds[i], 0, 0, &b)); + T_ASSERT(bcache_get(cache, _fds[i], 0, 0, &b, &err)); bcache_put(b); } } +static void test_read_bad_issue(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + int err; + + _expect_read_bad_issue(me, 17, 0); + T_ASSERT(!bcache_get(cache, 17, 0, 0, &b, &err)); +} + +static void test_read_bad_issue_intermittent(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + int fd = 17; + int err; + + _expect_read_bad_issue(me, fd, 0); + T_ASSERT(!bcache_get(cache, fd, 0, 0, &b, &err)); + + _expect_read(me, fd, 0); + _expect(me, E_WAIT); + T_ASSERT(bcache_get(cache, fd, 0, 0, &b, &err)); + bcache_put(b); +} + +static void test_read_bad_wait(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + int fd = 17; + int err; + + _expect_read_bad_wait(me, fd, 0); + _expect(me, E_WAIT); + T_ASSERT(!bcache_get(cache, fd, 0, 0, &b, &err)); +} + +static void test_read_bad_wait_intermittent(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + int fd = 17; + int err; + + _expect_read_bad_wait(me, fd, 0); + _expect(me, E_WAIT); + T_ASSERT(!bcache_get(cache, fd, 0, 0, &b, &err)); + + _expect_read(me, fd, 0); + _expect(me, E_WAIT); + T_ASSERT(bcache_get(cache, fd, 0, 0, &b, &err)); + bcache_put(b); +} + +static void test_write_bad_issue_stops_flush(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + int fd = 17; + int err; + + T_ASSERT(bcache_get(cache, fd, 0, GF_ZERO, &b, &err)); + _expect_write_bad_issue(me, fd, 0); + bcache_put(b); + T_ASSERT(!bcache_flush(cache)); + + // we'll let it succeed the second time + _expect_write(me, fd, 0); + _expect(me, E_WAIT); + T_ASSERT(bcache_flush(cache)); +} + +static void test_write_bad_io_stops_flush(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + int fd = 17; + int err; + + T_ASSERT(bcache_get(cache, fd, 0, GF_ZERO, &b, &err)); + _expect_write_bad_wait(me, fd, 0); + _expect(me, E_WAIT); + bcache_put(b); + T_ASSERT(!bcache_flush(cache)); + + // we'll let it succeed the second time + _expect_write(me, fd, 0); + _expect(me, E_WAIT); + T_ASSERT(bcache_flush(cache)); +} + // Tests to be written -// Open multiple files and prove the blocks are coming from the correct file // show invalidate works // show invalidate_fd works // show writeback is working -// check zeroing -struct test_details { - const char *path; - const char *desc; - void (*fn)(void *); - void *(*fixture_init)(void); - void (*fixture_exit)(void *); -}; +/*---------------------------------------------------------------- + * Top level + *--------------------------------------------------------------*/ +#define T(path, desc, fn) register_test(ts, "/base/device/bcache/" path, desc, fn) -#define PATH "device/bcache/" -#define TEST(path, name, fn) {PATH path, name, fn, NULL, NULL} -#define TEST_S(path, name, fn) {PATH path, name, fn, _small_fixture_init, _small_fixture_exit} -#define TEST_L(path, name, fn) {PATH path, name, fn, _large_fixture_init, _large_fixture_exit} - -int main(int argc, char **argv) +static struct test_suite *_small_tests(void) { - static struct test_details _tests[] = { - TEST("create-destroy", "simple create/destroy", test_create), - TEST("cache-blocks-positive", "nr cache blocks must be positive", test_nr_cache_blocks_must_be_positive), - TEST("block-size-positive", "block size must be positive", test_block_size_must_be_positive), - TEST("block-size-multiple-page", "block size must be a multiple of page size", test_block_size_must_be_multiple_of_page_size), - TEST_S("get-reads", "bcache_get() triggers read", test_get_triggers_read), - TEST_S("reads-cached", "repeated reads are cached", test_repeated_reads_are_cached), - TEST_S("blocks-get-evicted", "block get evicted with many reads", test_block_gets_evicted_with_many_reads), - TEST_S("prefetch-reads", "prefetch issues a read", test_prefetch_issues_a_read), - TEST_S("prefetch-never-waits", "too many prefetches does not trigger a wait", test_too_many_prefetches_does_not_trigger_a_wait), - TEST_S("writeback-occurs", "dirty data gets written back", test_dirty_data_gets_written_back), - TEST_S("zero-flag-dirties", "zeroed data counts as dirty", test_zeroed_data_counts_as_dirty), - TEST_L("flush waits for all dirty", "flush waits for all dirty", test_flush_waits_for_all_dirty), - TEST_S("read-multiple-files", "read from multiple files", test_multiple_files), - }; - - // We have to declare these as volatile because of the setjmp() - volatile unsigned i = 0, passed = 0; - - for (i = 0; i < DM_ARRAY_SIZE(_tests); i++) { - void *fixture; - struct test_details *t = _tests + i; - fprintf(stderr, "[RUN ] %s\n", t->path); - - if (setjmp(_test_k)) - fprintf(stderr, "[ FAIL] %s\n", t->path); - else { - if (t->fixture_init) - fixture = t->fixture_init(); - else - fixture = NULL; - - t->fn(fixture); - - if (t->fixture_exit) - t->fixture_exit(fixture); - - passed++; - fprintf(stderr, "[ OK] %s\n", t->path); - } + struct test_suite *ts = test_suite_create(_small_fixture_init, _small_fixture_exit); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); } - fprintf(stderr, "\n%u/%lu tests passed\n", passed, DM_ARRAY_SIZE(_tests)); + T("create-destroy", "simple create/destroy", test_create); + T("cache-blocks-positive", "nr cache blocks must be positive", test_nr_cache_blocks_must_be_positive); + T("block-size-positive", "block size must be positive", test_block_size_must_be_positive); + T("block-size-multiple-page", "block size must be a multiple of page size", test_block_size_must_be_multiple_of_page_size); + T("get-reads", "bcache_get() triggers read", test_get_triggers_read); + T("reads-cached", "repeated reads are cached", test_repeated_reads_are_cached); + T("blocks-get-evicted", "block get evicted with many reads", test_block_gets_evicted_with_many_reads); + T("prefetch-reads", "prefetch issues a read", test_prefetch_issues_a_read); + T("prefetch-never-waits", "too many prefetches does not trigger a wait", test_too_many_prefetches_does_not_trigger_a_wait); + T("writeback-occurs", "dirty data gets written back", test_dirty_data_gets_written_back); + T("zero-flag-dirties", "zeroed data counts as dirty", test_zeroed_data_counts_as_dirty); + T("read-multiple-files", "read from multiple files", test_multiple_files); + T("read-bad-issue", "read fails if io engine unable to issue", test_read_bad_issue); + T("read-bad-issue-intermittent", "failed issue, followed by succes", test_read_bad_issue_intermittent); + T("read-bad-io", "read issued ok, but io fails", test_read_bad_wait); + T("read-bad-io-intermittent", "failed io, followed by success", test_read_bad_wait_intermittent); + T("write-bad-issue-stops-flush", "flush fails temporarily if any block fails to write", test_write_bad_issue_stops_flush); + T("write-bad-io-stops-flush", "flush fails temporarily if any block fails to write", test_write_bad_io_stops_flush); - return 0; + return ts; +} + +static struct test_suite *_large_tests(void) +{ + struct test_suite *ts = test_suite_create(_large_fixture_init, _large_fixture_exit); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("flush-waits", "flush waits for all dirty", test_flush_waits_for_all_dirty); + + return ts; +} + +void bcache_tests(struct dm_list *all_tests) +{ + dm_list_add(all_tests, &_small_tests()->list); + dm_list_add(all_tests, &_large_tests()->list); } diff --git a/test/unit/bitset_t.c b/unit-test/bitset_t.c similarity index 62% rename from test/unit/bitset_t.c rename to unit-test/bitset_t.c index dadb9ec9d..106f60f6c 100644 --- a/test/unit/bitset_t.c +++ b/unit-test/bitset_t.c @@ -13,30 +13,36 @@ */ #include "units.h" +#include "libdevmapper.h" enum { NR_BITS = 137 }; -static struct dm_pool *mem; +static void *_mem_init(void) { + struct dm_pool *mem = dm_pool_create("bitset test", 1024); + if (!mem) { + fprintf(stderr, "out of memory\n"); + exit(1); + } -int bitset_init(void) { - mem = dm_pool_create("bitset test", 1024); - return mem == NULL; + return mem; } -int bitset_fini(void) { - dm_pool_destroy(mem); - return 0; -} - -static void test_get_next(void) +static void _mem_exit(void *mem) { + dm_pool_destroy(mem); +} + +static void test_get_next(void *fixture) +{ + struct dm_pool *mem = fixture; + int i, j, last = 0, first; dm_bitset_t bs = dm_bitset_create(mem, NR_BITS); for (i = 0; i < NR_BITS; i++) - CU_ASSERT(!dm_bit(bs, i)); + T_ASSERT(!dm_bit(bs, i)); for (i = 0, j = 1; i < NR_BITS; i += j, j++) dm_bit_set(bs, i); @@ -49,10 +55,10 @@ static void test_get_next(void) } else last = dm_bit_get_next(bs, last); - CU_ASSERT(last == i); + T_ASSERT(last == i); } - CU_ASSERT(dm_bit_get_next(bs, last) == -1); + T_ASSERT(dm_bit_get_next(bs, last) == -1); } static void bit_flip(dm_bitset_t bs, int bit) @@ -64,8 +70,9 @@ static void bit_flip(dm_bitset_t bs, int bit) dm_bit_set(bs, bit); } -static void test_equal(void) +static void test_equal(void *fixture) { + struct dm_pool *mem = fixture; dm_bitset_t bs1 = dm_bitset_create(mem, NR_BITS); dm_bitset_t bs2 = dm_bitset_create(mem, NR_BITS); @@ -75,21 +82,22 @@ static void test_equal(void) dm_bit_set(bs2, i); } - CU_ASSERT(dm_bitset_equal(bs1, bs2)); - CU_ASSERT(dm_bitset_equal(bs2, bs1)); + T_ASSERT(dm_bitset_equal(bs1, bs2)); + T_ASSERT(dm_bitset_equal(bs2, bs1)); for (i = 0; i < NR_BITS; i++) { bit_flip(bs1, i); - CU_ASSERT(!dm_bitset_equal(bs1, bs2)); - CU_ASSERT(!dm_bitset_equal(bs2, bs1)); + T_ASSERT(!dm_bitset_equal(bs1, bs2)); + T_ASSERT(!dm_bitset_equal(bs2, bs1)); - CU_ASSERT(dm_bitset_equal(bs1, bs1)); /* comparing with self */ + T_ASSERT(dm_bitset_equal(bs1, bs1)); /* comparing with self */ bit_flip(bs1, i); } } -static void test_and(void) +static void test_and(void *fixture) { + struct dm_pool *mem = fixture; dm_bitset_t bs1 = dm_bitset_create(mem, NR_BITS); dm_bitset_t bs2 = dm_bitset_create(mem, NR_BITS); dm_bitset_t bs3 = dm_bitset_create(mem, NR_BITS); @@ -102,9 +110,9 @@ static void test_and(void) dm_bit_and(bs3, bs1, bs2); - CU_ASSERT(dm_bitset_equal(bs1, bs2)); - CU_ASSERT(dm_bitset_equal(bs1, bs3)); - CU_ASSERT(dm_bitset_equal(bs2, bs3)); + T_ASSERT(dm_bitset_equal(bs1, bs2)); + T_ASSERT(dm_bitset_equal(bs1, bs3)); + T_ASSERT(dm_bitset_equal(bs2, bs3)); dm_bit_clear_all(bs1); dm_bit_clear_all(bs2); @@ -118,12 +126,23 @@ static void test_and(void) dm_bit_and(bs3, bs1, bs2); for (i = 0; i < NR_BITS; i++) - CU_ASSERT(!dm_bit(bs3, i)); + T_ASSERT(!dm_bit(bs3, i)); +} + +#define T(path, desc, fn) register_test(ts, "/base/data-struct/bitset/" path, desc, fn) + +void bitset_tests(struct dm_list *all_tests) +{ + struct test_suite *ts = test_suite_create(_mem_init, _mem_exit); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("get_next", "get next set bit", test_get_next); + T("equal", "equality", test_equal); + T("and", "and all bits", test_and); + + dm_list_add(all_tests, &ts->list); } -CU_TestInfo bitset_list[] = { - { (char*)"get_next", test_get_next }, - { (char*)"equal", test_equal }, - { (char*)"and", test_and }, - CU_TEST_INFO_NULL -}; diff --git a/unit-test/config_t.c b/unit-test/config_t.c new file mode 100644 index 000000000..5331f7933 --- /dev/null +++ b/unit-test/config_t.c @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2010 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "units.h" +#include "libdevmapper.h" + +static void *_mem_init(void) +{ + struct dm_pool *mem = dm_pool_create("config test", 1024); + if (!mem) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + return mem; +} + +static void _mem_exit(void *mem) +{ + dm_pool_destroy(mem); +} + +static const char *conf = + "id = \"yada-yada\"\n" + "seqno = 15\n" + "status = [\"READ\", \"WRITE\"]\n" + "flags = []\n" + "extent_size = 8192\n" + "physical_volumes {\n" + " pv0 {\n" + " id = \"abcd-efgh\"\n" + " }\n" + " pv1 {\n" + " id = \"bbcd-efgh\"\n" + " }\n" + " pv2 {\n" + " id = \"cbcd-efgh\"\n" + " }\n" + "}\n"; + +static const char *overlay = + "id = \"yoda-soda\"\n" + "flags = [\"FOO\"]\n" + "physical_volumes {\n" + " pv1 {\n" + " id = \"hgfe-dcba\"\n" + " }\n" + " pv3 {\n" + " id = \"dbcd-efgh\"\n" + " }\n" + "}\n"; + +static void test_parse(void *fixture) +{ + struct dm_config_tree *tree = dm_config_from_string(conf); + const struct dm_config_value *value; + + T_ASSERT((long) tree); + T_ASSERT(dm_config_has_node(tree->root, "id")); + T_ASSERT(dm_config_has_node(tree->root, "physical_volumes")); + T_ASSERT(dm_config_has_node(tree->root, "physical_volumes/pv0")); + T_ASSERT(dm_config_has_node(tree->root, "physical_volumes/pv0/id")); + + T_ASSERT(!strcmp(dm_config_find_str(tree->root, "id", "foo"), "yada-yada")); + T_ASSERT(!strcmp(dm_config_find_str(tree->root, "idt", "foo"), "foo")); + + T_ASSERT(!strcmp(dm_config_find_str(tree->root, "physical_volumes/pv0/bb", "foo"), "foo")); + T_ASSERT(!strcmp(dm_config_find_str(tree->root, "physical_volumes/pv0/id", "foo"), "abcd-efgh")); + + T_ASSERT(!dm_config_get_uint32(tree->root, "id", NULL)); + T_ASSERT(dm_config_get_uint32(tree->root, "extent_size", NULL)); + + /* FIXME: Currently everything parses as a list, even if it's not */ + // T_ASSERT(!dm_config_get_list(tree->root, "id", NULL)); + // T_ASSERT(!dm_config_get_list(tree->root, "extent_size", NULL)); + + T_ASSERT(dm_config_get_list(tree->root, "flags", &value)); + T_ASSERT(value->next == NULL); /* an empty list */ + T_ASSERT(dm_config_get_list(tree->root, "status", &value)); + T_ASSERT(value->next != NULL); /* a non-empty list */ + + dm_config_destroy(tree); +} + +static void test_clone(void *fixture) +{ + struct dm_config_tree *tree = dm_config_from_string(conf); + struct dm_config_node *n = dm_config_clone_node(tree, tree->root, 1); + const struct dm_config_value *value; + + /* Check that the nodes are actually distinct. */ + T_ASSERT(n != tree->root); + T_ASSERT(n->sib != tree->root->sib); + T_ASSERT(dm_config_find_node(n, "physical_volumes") != NULL); + T_ASSERT(dm_config_find_node(tree->root, "physical_volumes") != NULL); + T_ASSERT(dm_config_find_node(n, "physical_volumes") != dm_config_find_node(tree->root, "physical_volumes")); + + T_ASSERT(dm_config_has_node(n, "id")); + T_ASSERT(dm_config_has_node(n, "physical_volumes")); + T_ASSERT(dm_config_has_node(n, "physical_volumes/pv0")); + T_ASSERT(dm_config_has_node(n, "physical_volumes/pv0/id")); + + T_ASSERT(!strcmp(dm_config_find_str(n, "id", "foo"), "yada-yada")); + T_ASSERT(!strcmp(dm_config_find_str(n, "idt", "foo"), "foo")); + + T_ASSERT(!strcmp(dm_config_find_str(n, "physical_volumes/pv0/bb", "foo"), "foo")); + T_ASSERT(!strcmp(dm_config_find_str(n, "physical_volumes/pv0/id", "foo"), "abcd-efgh")); + + T_ASSERT(!dm_config_get_uint32(n, "id", NULL)); + T_ASSERT(dm_config_get_uint32(n, "extent_size", NULL)); + + /* FIXME: Currently everything parses as a list, even if it's not */ + // T_ASSERT(!dm_config_get_list(tree->root, "id", NULL)); + // T_ASSERT(!dm_config_get_list(tree->root, "extent_size", NULL)); + + T_ASSERT(dm_config_get_list(n, "flags", &value)); + T_ASSERT(value->next == NULL); /* an empty list */ + T_ASSERT(dm_config_get_list(n, "status", &value)); + T_ASSERT(value->next != NULL); /* a non-empty list */ + + dm_config_destroy(tree); +} + +static void test_cascade(void *fixture) +{ + struct dm_config_tree *t1 = dm_config_from_string(conf), + *t2 = dm_config_from_string(overlay), + *tree = dm_config_insert_cascaded_tree(t2, t1); + + T_ASSERT(!strcmp(dm_config_tree_find_str(tree, "id", "foo"), "yoda-soda")); + T_ASSERT(!strcmp(dm_config_tree_find_str(tree, "idt", "foo"), "foo")); + + T_ASSERT(!strcmp(dm_config_tree_find_str(tree, "physical_volumes/pv0/bb", "foo"), "foo")); + T_ASSERT(!strcmp(dm_config_tree_find_str(tree, "physical_volumes/pv1/id", "foo"), "hgfe-dcba")); + T_ASSERT(!strcmp(dm_config_tree_find_str(tree, "physical_volumes/pv3/id", "foo"), "dbcd-efgh")); + + dm_config_destroy(t1); + dm_config_destroy(t2); +} + +#define T(path, desc, fn) register_test(ts, "/metadata/config/" path, desc, fn) + +void config_tests(struct dm_list *all_tests) +{ + struct test_suite *ts = test_suite_create(_mem_init, _mem_exit); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("parse", "parsing various", test_parse); + T("clone", "duplicating a config tree", test_clone); + T("cascade", "cascade", test_cascade); + + dm_list_add(all_tests, &ts->list); +}; diff --git a/test/unit/dmlist_t.c b/unit-test/dmlist_t.c similarity index 60% rename from test/unit/dmlist_t.c rename to unit-test/dmlist_t.c index f582e83a1..82789ba17 100644 --- a/test/unit/dmlist_t.c +++ b/unit-test/dmlist_t.c @@ -13,23 +13,14 @@ */ #include "units.h" +#include "libdevmapper.h" -int dmlist_init(void) -{ - return 0; -} - -int dmlist_fini(void) -{ - return 0; -} - -static void test_dmlist_splice(void) +static void test_dmlist_splice(void *fixture) { + unsigned i; struct dm_list a[10]; struct dm_list list1; struct dm_list list2; - unsigned i; dm_list_init(&list1); dm_list_init(&list2); @@ -38,12 +29,21 @@ static void test_dmlist_splice(void) dm_list_add(&list1, &a[i]); dm_list_splice(&list2, &list1); - CU_ASSERT_EQUAL(dm_list_size(&list1), 0); - CU_ASSERT_EQUAL(dm_list_size(&list2), 10); + T_ASSERT(dm_list_size(&list1) == 0); + T_ASSERT(dm_list_size(&list2) == 10); } -CU_TestInfo dmlist_list[] = { - { (char*)"dmlist_splice", test_dmlist_splice }, - //{ (char*)"dmlist", test_strncpy }, - CU_TEST_INFO_NULL -}; +#define T(path, desc, fn) register_test(ts, "/base/data-struct/list/" path, desc, fn) + +void dm_list_tests(struct dm_list *all_tests) +{ + struct test_suite *ts = test_suite_create(NULL, NULL); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("splice", "joining lists together", test_dmlist_splice); + + dm_list_add(all_tests, &ts->list); +} diff --git a/unit-test/dmstatus_t.c b/unit-test/dmstatus_t.c new file mode 100644 index 000000000..4b57f29e4 --- /dev/null +++ b/unit-test/dmstatus_t.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "units.h" +#include "libdevmapper.h" + +static void *_mem_init(void) +{ + struct dm_pool *mem = dm_pool_create("dmstatus test", 1024); + if (!mem) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + return mem; +} + +static void _mem_exit(void *mem) +{ + dm_pool_destroy(mem); +} + +static void _test_mirror_status(void *fixture) +{ + struct dm_pool *mem = fixture; + struct dm_status_mirror *s = NULL; + + T_ASSERT(dm_get_status_mirror(mem, + "2 253:1 253:2 80/81 1 AD 3 disk 253:0 A", + &s)); + if (s) { + T_ASSERT_EQUAL(s->total_regions, 81); + T_ASSERT_EQUAL(s->insync_regions, 80); + T_ASSERT_EQUAL(s->dev_count, 2); + T_ASSERT_EQUAL(s->devs[0].health, 'A'); + T_ASSERT_EQUAL(s->devs[0].major, 253); + T_ASSERT_EQUAL(s->devs[0].minor, 1); + T_ASSERT_EQUAL(s->devs[1].health, 'D'); + T_ASSERT_EQUAL(s->devs[1].major, 253); + T_ASSERT_EQUAL(s->devs[1].minor, 2); + T_ASSERT_EQUAL(s->log_count, 1); + T_ASSERT_EQUAL(s->logs[0].major, 253); + T_ASSERT_EQUAL(s->logs[0].minor, 0); + T_ASSERT_EQUAL(s->logs[0].health, 'A'); + T_ASSERT(!strcmp(s->log_type, "disk")); + } + + T_ASSERT(dm_get_status_mirror(mem, + "4 253:1 253:2 253:3 253:4 10/10 1 ADFF 1 core", + &s)); + if (s) { + T_ASSERT_EQUAL(s->total_regions, 10); + T_ASSERT_EQUAL(s->insync_regions, 10); + T_ASSERT_EQUAL(s->dev_count, 4); + T_ASSERT_EQUAL(s->devs[3].minor, 4); + T_ASSERT_EQUAL(s->devs[3].health, 'F'); + T_ASSERT_EQUAL(s->log_count, 0); + T_ASSERT(!strcmp(s->log_type, "core")); + } +} + +void dm_status_tests(struct dm_list *all_tests) +{ + struct test_suite *ts = test_suite_create(_mem_init, _mem_exit); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + register_test(ts, "/dm/target/mirror/status", "parsing mirror status", _test_mirror_status); + dm_list_add(all_tests, &ts->list); +} + diff --git a/unit-test/framework.c b/unit-test/framework.c new file mode 100644 index 000000000..de9a8b1a0 --- /dev/null +++ b/unit-test/framework.c @@ -0,0 +1,66 @@ +#include "framework.h" + +/*---------------------------------------------------------------- + * Assertions + *--------------------------------------------------------------*/ + +jmp_buf test_k; +#define TEST_FAILED 1 + +void test_fail(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + + longjmp(test_k, TEST_FAILED); +} + +struct test_suite *test_suite_create(void *(*fixture_init)(void), + void (*fixture_exit)(void *)) +{ + struct test_suite *ts = malloc(sizeof(*ts)); + if (ts) { + ts->fixture_init = fixture_init; + ts->fixture_exit = fixture_exit; + dm_list_init(&ts->tests); + } + + return ts; +} + +void test_suite_destroy(struct test_suite *ts) +{ + struct test_details *td, *tmp; + + dm_list_iterate_items_safe (td, tmp, &ts->tests) { + dm_list_del(&td->list); + free(td); + } + + free(ts); +} + +bool register_test(struct test_suite *ts, + const char *path, const char *desc, + void (*fn)(void *)) +{ + struct test_details *t = malloc(sizeof(*t)); + if (!t) { + fprintf(stderr, "out of memory\n"); + return false; + } + + t->parent = ts; + t->path = path; + t->desc = desc; + t->fn = fn; + dm_list_add(&ts->tests, &t->list); + + return true; +} + +//----------------------------------------------------------------- diff --git a/unit-test/framework.h b/unit-test/framework.h new file mode 100644 index 000000000..5a33ca671 --- /dev/null +++ b/unit-test/framework.h @@ -0,0 +1,49 @@ +#ifndef TEST_UNIT_FRAMEWORK_H +#define TEST_UNIT_FRAMEWORK_H + +#include "libdevmapper.h" + +#include +#include +#include + +//----------------------------------------------------------------- + +// A test suite gathers a set of tests with a common fixture together. +struct test_suite { + struct dm_list list; + + void *(*fixture_init)(void); + void (*fixture_exit)(void *); + struct dm_list tests; +}; + +struct test_details { + struct test_suite *parent; + struct dm_list list; + + const char *path; + const char *desc; + void (*fn)(void *); +}; + +struct test_suite *test_suite_create(void *(*fixture_init)(void), + void (*fixture_exit)(void *)); +void test_suite_destroy(struct test_suite *ts); + +bool register_test(struct test_suite *ts, + const char *path, const char *desc, void (*fn)(void *)); + +void test_fail(const char *fmt, ...) + __attribute__((noreturn, format (printf, 1, 2))); + +#define T_ASSERT(e) do {if (!(e)) {test_fail("assertion failed: '%s'", # e);} } while(0) +#define T_ASSERT_EQUAL(x, y) T_ASSERT((x) == (y)) +#define T_ASSERT_NOT_EQUAL(x, y) T_ASSERT((x) != (y)) + +extern jmp_buf test_k; +#define TEST_FAILED 1 + +//----------------------------------------------------------------- + +#endif diff --git a/test/unit/matcher_data.h b/unit-test/matcher_data.h similarity index 100% rename from test/unit/matcher_data.h rename to unit-test/matcher_data.h diff --git a/unit-test/matcher_t.c b/unit-test/matcher_t.c new file mode 100644 index 000000000..a5eb5f9c3 --- /dev/null +++ b/unit-test/matcher_t.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. + * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "units.h" +#include "libdevmapper.h" + +#include "matcher_data.h" + +static void *_mem_init(void) +{ + struct dm_pool *mem = dm_pool_create("bitset test", 1024); + if (!mem) { + fprintf(stderr, "out of memory"); + exit(1); + } + + return mem; +} + +static void _mem_exit(void *mem) +{ + dm_pool_destroy(mem); +} + +static struct dm_regex *make_scanner(struct dm_pool *mem, const char **rx) +{ + struct dm_regex *scanner; + int nrx = 0; + for (; rx[nrx]; ++nrx); + + scanner = dm_regex_create(mem, rx, nrx); + T_ASSERT(scanner != NULL); + return scanner; +} + +static void test_fingerprints(void *fixture) +{ + struct dm_pool *mem = fixture; + struct dm_regex *scanner; + + scanner = make_scanner(mem, dev_patterns); + T_ASSERT_EQUAL(dm_regex_fingerprint(scanner), 0x7f556c09); + + scanner = make_scanner(mem, random_patterns); + T_ASSERT_EQUAL(dm_regex_fingerprint(scanner), 0x9f11076c); +} + +static void test_matching(void *fixture) +{ + struct dm_pool *mem = fixture; + struct dm_regex *scanner; + int i; + + scanner = make_scanner(mem, dev_patterns); + for (i = 0; devices[i].str; ++i) + T_ASSERT_EQUAL(dm_regex_match(scanner, devices[i].str), devices[i].expected - 1); + + scanner = make_scanner(mem, nonprint_patterns); + for (i = 0; nonprint[i].str; ++i) + T_ASSERT_EQUAL(dm_regex_match(scanner, nonprint[i].str), nonprint[i].expected - 1); +} + +#define T(path, desc, fn) register_test(ts, "/base/regex/" path, desc, fn) + +void regex_tests(struct dm_list *all_tests) +{ + struct test_suite *ts = test_suite_create(_mem_init, _mem_exit); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("fingerprints", "not sure", test_fingerprints); + T("matching", "test the matcher with a variety of regexes", test_matching); + + dm_list_add(all_tests, &ts->list); +} diff --git a/test/unit/percent_t.c b/unit-test/percent_t.c similarity index 59% rename from test/unit/percent_t.c rename to unit-test/percent_t.c index 650f38122..84dd3bdf6 100644 --- a/test/unit/percent_t.c +++ b/unit-test/percent_t.c @@ -13,21 +13,12 @@ */ #include "units.h" +#include "libdevmapper.h" #include #include -int percent_init(void) -{ - return 0; -} - -int percent_fini(void) -{ - return 0; -} - -static void test_percent_100(void) +static void test_percent_100(void *fixture) { char buf[32]; @@ -36,33 +27,33 @@ static void test_percent_100(void) dm_percent_t p1_100 = dm_make_percent(100000, 100000); dm_percent_t n_100 = dm_make_percent(999999, 1000000); - CU_ASSERT_EQUAL(p_100, DM_PERCENT_100); - CU_ASSERT_EQUAL(p1_100, DM_PERCENT_100); - CU_ASSERT_NOT_EQUAL(n_100, DM_PERCENT_100); + T_ASSERT_EQUAL(p_100, DM_PERCENT_100); + T_ASSERT_EQUAL(p1_100, DM_PERCENT_100); + T_ASSERT_NOT_EQUAL(n_100, DM_PERCENT_100); dm_snprintf(buf, sizeof(buf), "%.2f", dm_percent_to_float(p_100)); - CU_ASSERT_EQUAL(strcmp(buf, "100.00"), 0); + T_ASSERT_EQUAL(strcmp(buf, "100.00"), 0); dm_snprintf(buf, sizeof(buf), "%.2f", dm_percent_to_float(p1_100)); - CU_ASSERT_EQUAL(strcmp(buf, "100.00"), 0); + T_ASSERT_EQUAL(strcmp(buf, "100.00"), 0); dm_snprintf(buf, sizeof(buf), "%.2f", dm_percent_to_float(n_100)); - CU_ASSERT_NOT_EQUAL(strcmp(buf, "99.99"), 0); /* Would like to gett */ + T_ASSERT_NOT_EQUAL(strcmp(buf, "99.99"), 0); /* Would like to gett */ dm_snprintf(buf, sizeof(buf), "%.2f", dm_percent_to_round_float(n_100, 2)); - CU_ASSERT_EQUAL(strcmp(buf, "99.99"), 0); + T_ASSERT_EQUAL(strcmp(buf, "99.99"), 0); dm_snprintf(buf, sizeof(buf), "%.3f", dm_percent_to_round_float(n_100, 3)); - CU_ASSERT_EQUAL(strcmp(buf, "99.999"), 0); + T_ASSERT_EQUAL(strcmp(buf, "99.999"), 0); dm_snprintf(buf, sizeof(buf), "%.4f", dm_percent_to_round_float(n_100, 4)); - CU_ASSERT_EQUAL(strcmp(buf, "99.9999"), 0); + T_ASSERT_EQUAL(strcmp(buf, "99.9999"), 0); dm_snprintf(buf, sizeof(buf), "%d", (int)dm_percent_to_round_float(n_100, 0)); - CU_ASSERT_EQUAL(strcmp(buf, "99"), 0); + T_ASSERT_EQUAL(strcmp(buf, "99"), 0); } -static void test_percent_0(void) +static void test_percent_0(void *fixture) { char buf[32]; @@ -71,31 +62,41 @@ static void test_percent_0(void) dm_percent_t p1_0 = dm_make_percent(0, 100000); dm_percent_t n_0 = dm_make_percent(1, 1000000); - CU_ASSERT_EQUAL(p_0, DM_PERCENT_0); - CU_ASSERT_EQUAL(p1_0, DM_PERCENT_0); - CU_ASSERT_NOT_EQUAL(n_0, DM_PERCENT_0); + T_ASSERT_EQUAL(p_0, DM_PERCENT_0); + T_ASSERT_EQUAL(p1_0, DM_PERCENT_0); + T_ASSERT_NOT_EQUAL(n_0, DM_PERCENT_0); dm_snprintf(buf, sizeof(buf), "%.2f", dm_percent_to_float(p_0)); - CU_ASSERT_EQUAL(strcmp(buf, "0.00"), 0); + T_ASSERT_EQUAL(strcmp(buf, "0.00"), 0); dm_snprintf(buf, sizeof(buf), "%.2f", dm_percent_to_float(p1_0)); - CU_ASSERT_EQUAL(strcmp(buf, "0.00"), 0); + T_ASSERT_EQUAL(strcmp(buf, "0.00"), 0); dm_snprintf(buf, sizeof(buf), "%.2f", dm_percent_to_float(n_0)); - CU_ASSERT_NOT_EQUAL(strcmp(buf, "0.01"), 0); + T_ASSERT_NOT_EQUAL(strcmp(buf, "0.01"), 0); dm_snprintf(buf, sizeof(buf), "%.2f", dm_percent_to_round_float(n_0, 2)); - CU_ASSERT_EQUAL(strcmp(buf, "0.01"), 0); + T_ASSERT_EQUAL(strcmp(buf, "0.01"), 0); dm_snprintf(buf, sizeof(buf), "%.3f", dm_percent_to_round_float(n_0, 3)); - CU_ASSERT_EQUAL(strcmp(buf, "0.001"), 0); + T_ASSERT_EQUAL(strcmp(buf, "0.001"), 0); dm_snprintf(buf, sizeof(buf), "%d", (int)dm_percent_to_round_float(n_0, 0)); - CU_ASSERT_EQUAL(strcmp(buf, "1"), 0); + T_ASSERT_EQUAL(strcmp(buf, "1"), 0); } -CU_TestInfo percent_list[] = { - { (char*)"percent_100", test_percent_100 }, - { (char*)"percent_0", test_percent_0 }, - CU_TEST_INFO_NULL -}; +#define T(path, desc, fn) register_test(ts, "/base/formatting/percent/" path, desc, fn) + +void percent_tests(struct dm_list *all_tests) +{ + struct test_suite *ts = test_suite_create(NULL, NULL); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("100", "Pretty printing of percentages near 100%", test_percent_100); + T("0", "Pretty printing of percentages near 0%", test_percent_0); + + dm_list_add(all_tests, &ts->list); +} diff --git a/unit-test/run.c b/unit-test/run.c new file mode 100644 index 000000000..9cbb60506 --- /dev/null +++ b/unit-test/run.c @@ -0,0 +1,309 @@ +#include "units.h" + +#include +#include +#include +#include +#include +#include + +//----------------------------------------------------------------- + +#define MAX_COMPONENTS 16 + +struct token { + const char *b, *e; +}; + +static bool _pop_component(const char *path, struct token *result) +{ + const char *b, *e; + + while (*path && *path == '/') + path++; + + b = path; + while (*path && (*path != '/')) + path++; + e = path; + + if (b == e) + return false; + + result->b = b; + result->e = e; + return true; +} + +static unsigned _split_components(const char *path, struct token *toks, unsigned len) +{ + unsigned count = 0; + struct token tok; + tok.e = path; + + while (len && _pop_component(tok.e, &tok)) { + *toks = tok; + toks++; + count++; + len--; + } + + return count; +} + +static void _indent(FILE *stream, unsigned count) +{ + unsigned i; + + for (i = 0; i < count; i++) + fprintf(stream, " "); +} + +static void _print_token(FILE *stream, struct token *t) +{ + const char *ptr; + + for (ptr = t->b; ptr != t->e; ptr++) + fprintf(stream, "%c", *ptr); +} + +static int _char_cmp(char l, char r) +{ + if (l < r) + return -1; + + else if (r < l) + return 1; + + else + return 0; +} + +static int _tok_cmp(struct token *lhs, struct token *rhs) +{ + const char *l = lhs->b, *le = lhs->e; + const char *r = rhs->b, *re = rhs->e; + + while ((l != le) && (r != re) && (*l == *r)) { + l++; + r++; + } + + if ((l != le) && (r != re)) + return _char_cmp(*l, *r); + + else if (r != re) + return -1; + + else if (l != le) + return 1; + + else + return 0; +} + +static void _print_path_delta(FILE *stream, + struct token *old, unsigned old_len, + struct token *new, unsigned new_len, + const char *desc) +{ + unsigned i, common_prefix = 0, len, d; + unsigned max_prefix = old_len < new_len ? old_len : new_len; + + for (i = 0; i < max_prefix; i++) { + if (_tok_cmp(old + i, new + i)) + break; + else + common_prefix++; + } + + for (; i < new_len; i++) { + _indent(stream, common_prefix); + _print_token(stream, new + i); + common_prefix++; + if (i < new_len - 1) + fprintf(stream, "\n"); + } + + len = common_prefix * 2 + (new[new_len - 1].e - new[new_len - 1].b); + fprintf(stream, " "); + for (d = len; d < 60; d++) + fprintf(stream, "."); + fprintf(stream, " "); + fprintf(stream, "%s", desc); + fprintf(stream, "\n"); +} + +typedef struct token comp_t[MAX_COMPONENTS]; + +static void _list_tests(struct test_details **tests, unsigned nr) +{ + unsigned i, current = 0, current_len, last_len = 0; + + comp_t components[2]; + + for (i = 0; i < nr; i++) { + struct test_details *t = tests[i]; + current_len = _split_components(t->path, components[current], MAX_COMPONENTS); + _print_path_delta(stderr, components[!current], last_len, + components[current], current_len, t->desc); + + last_len = current_len; + current = !current; + } +} + +static void _destroy_tests(struct dm_list *suites) +{ + struct test_suite *ts, *tmp; + + dm_list_iterate_items_safe (ts, tmp, suites) + test_suite_destroy(ts); +} + +static const char *red(bool c) +{ + return c ? "\x1B[31m" : ""; +} + +static const char *green(bool c) +{ + return c ? "\x1B[32m" : ""; +} + +static const char *normal(bool c) +{ + return c ? "\x1B[0m" : ""; +} + +static void _run_test(struct test_details *t, bool use_colour, unsigned *passed, unsigned *total) +{ + void *fixture; + struct test_suite *ts = t->parent; + fprintf(stderr, "[RUN ] %s\n", t->path); + + (*total)++; + if (setjmp(test_k)) + fprintf(stderr, "%s[ FAIL]%s %s\n", red(use_colour), normal(use_colour), t->path); + else { + if (ts->fixture_init) + fixture = ts->fixture_init(); + else + fixture = NULL; + + t->fn(fixture); + + if (ts->fixture_exit) + ts->fixture_exit(fixture); + + (*passed)++; + fprintf(stderr, "%s[ OK]%s\n", green(use_colour), normal(use_colour)); + } +} + +static bool _run_tests(struct test_details **tests, unsigned nr) +{ + bool use_colour = isatty(fileno(stderr)); + unsigned i, passed = 0, total = 0; + + for (i = 0; i < nr; i++) + _run_test(tests[i], use_colour, &passed, &total); + + fprintf(stderr, "\n%u/%u tests passed\n", passed, total); + + return passed == total; +} + +static void _usage(void) +{ + fprintf(stderr, "Usage: unit-test [pattern]\n"); +} + +static int _cmp_paths(const void *lhs, const void *rhs) +{ + struct test_details *l = *((struct test_details **) lhs); + struct test_details *r = *((struct test_details **) rhs); + + return strcmp(l->path, r->path); +} + +static unsigned _filter(const char *pattern, struct test_details **tests, unsigned nr) +{ + unsigned i, found = 0; + regex_t rx; + + if (regcomp(&rx, pattern, 0)) { + fprintf(stderr, "couldn't compile regex '%s'\n", pattern); + exit(1); + } + + for (i = 0; i < nr; i++) + if (!regexec(&rx, tests[i]->path, 0, NULL, 0)) + tests[found++] = tests[i]; + + regfree(&rx); + + return found; +} + +int main(int argc, char **argv) +{ + int r; + unsigned i, nr_tests; + struct test_suite *ts; + struct test_details *t, **t_array; + struct dm_list suites; + + dm_list_init(&suites); + register_all_tests(&suites); + + // count all tests + nr_tests = 0; + dm_list_iterate_items (ts, &suites) + dm_list_iterate_items (t, &ts->tests) + nr_tests++; + + // stick them in an array + t_array = malloc(sizeof(*t_array) * nr_tests); + if (!t_array) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + i = 0; + dm_list_iterate_items (ts, &suites) + dm_list_iterate_items (t, &ts->tests) + t_array[i++] = t; + + // filter + if (argc == 3) + nr_tests = _filter(argv[2], t_array, nr_tests); + + // sort + qsort(t_array, nr_tests, sizeof(*t_array), _cmp_paths); + + // run or list them + if (argc == 1) + r = !_run_tests(t_array, nr_tests); + else { + const char *cmd = argv[1]; + if (!strcmp(cmd, "run")) + r = !_run_tests(t_array, nr_tests); + + else if (!strcmp(cmd, "list")) { + _list_tests(t_array, nr_tests); + r = 0; + + } else { + _usage(); + r = 1; + } + } + + free(t_array); + _destroy_tests(&suites); + + return r; +} + +//----------------------------------------------------------------- diff --git a/unit-test/string_t.c b/unit-test/string_t.c new file mode 100644 index 000000000..2af0f3741 --- /dev/null +++ b/unit-test/string_t.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "units.h" +#include "libdevmapper.h" + +#include +#include + +#if 0 +static int _mem_init(void) +{ + struct dm_pool *mem = dm_pool_create("string test", 1024); + if (!mem) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + return mem; +} + +static void _mem_exit(void *mem) +{ + dm_pool_destroy(mem); +} + +/* TODO: Add more string unit tests here */ +#endif + +static void test_strncpy(void *fixture) +{ + const char st[] = "1234567890"; + char buf[sizeof(st)]; + + T_ASSERT_EQUAL(dm_strncpy(buf, st, sizeof(buf)), 1); + T_ASSERT_EQUAL(strcmp(buf, st), 0); + + T_ASSERT_EQUAL(dm_strncpy(buf, st, sizeof(buf) - 1), 0); + T_ASSERT_EQUAL(strlen(buf) + 1, sizeof(buf) - 1); +} + +static void test_asprint(void *fixture) +{ + const char st0[] = ""; + const char st1[] = "12345678901"; + const char st2[] = "1234567890123456789012345678901234567890123456789012345678901234567"; + char *buf; + int a; + + a = dm_asprintf(&buf, "%s", st0); + T_ASSERT_EQUAL(strcmp(buf, st0), 0); + T_ASSERT_EQUAL(a, sizeof(st0)); + free(buf); + + a = dm_asprintf(&buf, "%s", st1); + T_ASSERT_EQUAL(strcmp(buf, st1), 0); + T_ASSERT_EQUAL(a, sizeof(st1)); + free(buf); + + a = dm_asprintf(&buf, "%s", st2); + T_ASSERT_EQUAL(a, sizeof(st2)); + T_ASSERT_EQUAL(strcmp(buf, st2), 0); + free(buf); +} + +#define T(path, desc, fn) register_test(ts, "/base/data-struct/string/" path, desc, fn) + +void string_tests(struct dm_list *all_tests) +{ + struct test_suite *ts = test_suite_create(NULL, NULL); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("asprint", "tests asprint", test_asprint); + T("strncpy", "tests string copying", test_strncpy); + + dm_list_add(all_tests, &ts->list); +} diff --git a/unit-test/units.h b/unit-test/units.h new file mode 100644 index 000000000..47f04c236 --- /dev/null +++ b/unit-test/units.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef TEST_UNIT_UNITS_H +#define TEST_UNIT_UNITS_H + +#include "framework.h" + +//----------------------------------------------------------------- + +// Declare the function that adds tests suites here ... +void bcache_tests(struct dm_list *suites); +void bitset_tests(struct dm_list *suites); +void config_tests(struct dm_list *suites); +void dm_list_tests(struct dm_list *suites); +void dm_status_tests(struct dm_list *suites); +void regex_tests(struct dm_list *suites); +void percent_tests(struct dm_list *suites); +void string_tests(struct dm_list *suites); + +// ... and call it in here. +static inline void register_all_tests(struct dm_list *suites) +{ + bcache_tests(suites); + bitset_tests(suites); + config_tests(suites); + dm_list_tests(suites); + dm_status_tests(suites); + regex_tests(suites); + percent_tests(suites); + string_tests(suites); +} + +//----------------------------------------------------------------- +#endif