1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-21 13:34:40 +03:00

[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.
This commit is contained in:
Joe Thornber 2018-04-26 11:59:39 +01:00
parent c7fdacbc50
commit ea34dad66f
47 changed files with 1544 additions and 1591 deletions

2
.gitignore vendored
View File

@ -79,3 +79,5 @@ test/lib/vgrename
test/lib/vgs
test/lib/vgscan
test/lib/vgsplit
unit-test/unit-test

View File

@ -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),)

175
configure vendored
View File

@ -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 <http://pkg-config.freedesktop.org/>.
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" ;;

View File

@ -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
])

257
doc/unit-tests.txt Normal file
View File

@ -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 <list|run> [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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,25 +0,0 @@
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title><%= title %></title>
<link title="Style" type="text/css" rel="stylesheet" href="stylesheet.css">
</head>
<body>
<div id="banner">
<h2><%= title %></h2>
</div>
<div id="main">
<div id="controls">
<table>
<tr><td><a href="index.html">Generation times</a></td></tr>
<tr><td><a href="unit.html">Unit tests</a></td></tr>
<tr><td><a href="memcheck.html">Memory tests</a></td></tr>
</table>
</div>
<div id="body">
<%= body %>
</div>
</div>
</body>

View File

@ -1,17 +0,0 @@
<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
<tr><th>Report</th><th>Generation time</th></tr>
<% [:unit_test, :memcheck].each do |sym| %>
<% r = reports.get_report(sym) %>
<tr>
<td>
<% if r.path.file? %>
<a href="<%= r.path.to_s.gsub(/^reports\//, '') %>"><%= r.short_desc %></a>
<% else %>
<%= r.short_desc %>
<% end %>
</td>
<td><%= safe_mtime(r) %></td>
</tr>
<% end %>
</table>

View File

@ -1,30 +0,0 @@
<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
<tr><th>Tests passed</th><th>Tests failed</th></tr>
<tr><td class="pass"><%= total_passed %></td><td <%= total_failed == 0 ? "" : "class=\"fail\""%>><%= total_failed %></td></tr>
</table>
<% schedules.each do |s| %>
<h3><%= s.dir.sub('./unit-tests/', '') %></h3>
<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
<tr><th>Test</th><th>Result</th><th>Definitely lost</th><th>indirectly lost</th><th>possibly lost</th><th>still reachable</th><tr>
<% s.schedules.each do |t| %>
<tr>
<td>
<a href="memcheck_<%= mangle(t.desc) %>.html"><%= t.desc %></a>
</td>
<% if t.status.success? %>
<td class="pass">pass</td>
<% else %>
<td class="fail">fail</td>
<% end %>
<% stats = extract_stats(t) %>
<td><%= stats.definitely_lost %></td>
<td><%= stats.indirectly_lost %></td>
<td><%= stats.possibly_lost %></td>
<td><%= stats.reachable %></td>
</tr>
<% end %>
</table>
<% end %>

View File

@ -1,37 +0,0 @@
<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
<tr><th>Test</th><th>Result</th></tr>
<tr>
<td>
<%= t.desc %>
</td>
<% if t.status.success? %>
<td class="pass">pass</td>
<% else %>
<td class="fail">fail</td>
<% end %>
</tr>
</table>
<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
<tr><th>Command line</th></tr>
<tr>
<td>
<pre>
<%= t.command_line %>
</pre>
</td>
</tr>
</table>
<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
<tr><th>Output</th></tr>
<tr>
<td>
<pre>
<%= t.output %>
</pre>
</td>
</tr>
</table>

View File

@ -1,23 +0,0 @@
<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
<tr><th>Tests passed</th><th>Tests failed</th></tr>
<tr><td class="pass"><%= total_passed %></td><td <%= total_failed == 0 ? "" : "class=\"fail\""%>><%= total_failed %></td></tr>
</table>
<% schedules.each do |s| %>
<h3><%= s.dir.sub('./unit-tests/', '') %></h3>
<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
<tr><th>Test</th><th>Result</th></tr>
<% s.schedules.each do |t| %>
<tr>
<td>
<a href="detail_<%= mangle(t.desc) %>.html"><%= t.desc %></a>
</td>
<% if t.status.success? %>
<td class="pass">pass</td>
<% else %>
<td class="fail">fail</td>
<% end %>
</tr>
<% end %>
</table>
<% end %>

View File

@ -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

View File

@ -1 +0,0 @@
Hello, world!

View File

@ -1,3 +0,0 @@
one
two
three

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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
};

View File

@ -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
};

View File

@ -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
};

View File

@ -1,39 +0,0 @@
#include "units.h"
#include <CUnit/Basic.h>
#include <stdio.h>
#include <stdlib.h>
/* 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);
}

View File

@ -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 <stdio.h>
#include <string.h>
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
};

View File

@ -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 <CUnit/CUnit.h>
#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

View File

@ -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)

View File

@ -12,40 +12,17 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <setjmp.h>
#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);
}

View File

@ -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
};

167
unit-test/config_t.c Normal file
View File

@ -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);
};

View File

@ -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);
}

84
unit-test/dmstatus_t.c Normal file
View File

@ -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);
}

66
unit-test/framework.c Normal file
View File

@ -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;
}
//-----------------------------------------------------------------

49
unit-test/framework.h Normal file
View File

@ -0,0 +1,49 @@
#ifndef TEST_UNIT_FRAMEWORK_H
#define TEST_UNIT_FRAMEWORK_H
#include "libdevmapper.h"
#include <stdbool.h>
#include <stdint.h>
#include <setjmp.h>
//-----------------------------------------------------------------
// 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

89
unit-test/matcher_t.c Normal file
View File

@ -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);
}

View File

@ -13,21 +13,12 @@
*/
#include "units.h"
#include "libdevmapper.h"
#include <stdio.h>
#include <string.h>
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);
}

309
unit-test/run.c Normal file
View File

@ -0,0 +1,309 @@
#include "units.h"
#include <getopt.h>
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <unistd.h>
//-----------------------------------------------------------------
#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 <list|run> [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;
}
//-----------------------------------------------------------------

91
unit-test/string_t.c Normal file
View File

@ -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 <stdio.h>
#include <string.h>
#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);
}

46
unit-test/units.h Normal file
View File

@ -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