1
1

usnic: add simple unit testing infrastructure

This commit adds mechanisms for writing and running unit tests in the
usnic BTL.  The short version of how to run the tests is:

1. Configure with `--enable-ompi-btl-usnic-unit-tests`.  This will cause
   the unit testing code and test runner utility to be built.

2. Run the tests by running `ompi_btl_usnic_run_tests`.

See `README.test` for full details.

Reviewed-by: Jeff Squyres <jsquyres@cisco.com>

cmr=v1.7.5:ticket=trac:4253

This commit was SVN r30845.

The following Trac tickets were found above:
  Ticket 4253 --> https://svn.open-mpi.org/trac/ompi/ticket/4253
Этот коммит содержится в:
Dave Goodell 2014-02-26 07:47:50 +00:00
родитель 044a190cac
Коммит 921a29e41f
7 изменённых файлов: 511 добавлений и 2 удалений

Просмотреть файл

@ -11,7 +11,7 @@
# All rights reserved.
# Copyright (c) 2006 Sandia National Laboratories. All rights
# reserved.
# Copyright (c) 2010-2013 Cisco Systems, Inc. All rights reserved.
# Copyright (c) 2010-2014 Cisco Systems, Inc. All rights reserved.
# $COPYRIGHT$
#
# Additional copyrights may follow
@ -22,6 +22,8 @@
AM_CPPFLAGS = $(btl_usnic_CPPFLAGS)
AM_CFLAGS = $(btl_usnic_CFLAGS)
EXTRA_DIST = README.txt README.test
dist_ompidata_DATA = \
help-mpi-btl-usnic.txt
@ -48,7 +50,9 @@ sources = \
btl_usnic_stats.h \
btl_usnic_stats.c \
btl_usnic_util.c \
btl_usnic_util.h
btl_usnic_util.h \
btl_usnic_test.c \
btl_usnic_test.h
if OPAL_HAVE_HWLOC
sources += btl_usnic_hwloc.c
@ -81,3 +85,9 @@ noinst_LTLIBRARIES = $(lib)
libmca_btl_usnic_la_SOURCES = $(lib_sources)
libmca_btl_usnic_la_LDFLAGS= -module -avoid-version $(btl_usnic_LDFLAGS)
libmca_btl_usnic_la_LIBADD = $(btl_usnic_LIBS)
if OMPI_BTL_USNIC_BUILD_UNIT_TESTS
ompi_btl_usnic_run_tests_SOURCES = test/ompi_btl_usnic_run_tests.c
ompi_btl_usnic_run_tests_LDADD = -ldl
bin_PROGRAMS = ompi_btl_usnic_run_tests
endif OMPI_BTL_USNIC_BUILD_UNIT_TESTS

74
ompi/mca/btl/usnic/README.test Обычный файл
Просмотреть файл

@ -0,0 +1,74 @@
usnic BTL Unit Testing Information
==================================
This document briefly describes the scheme put in place for usnic BTL unit
testing. This system was very quickly tossed together and warrants a proper
revisiting at some point in the future. There are lots of ways to solve these
problems and this is just one of them. Future improvement is welcome.
Goals
-----
* To enable _unit_ testing of isolated functions or sets of functions. This
has all sorts of benefits, including:
- greater confidence that corner cases work as expected
- faster, lower-stress refactoring in the future
- influencing future interfaces to have less implicit coupling/state (unit
testing is harder otherwise)
* To be able to test *static* functions as well as non-static ones.
* To avoid cluttering the normal code base with excessive test-related
macros/code.
* The tests should be easy to run under Valgrind and GDB to facilitate:
- automated leak/memory checking
- easy debugging compared to a parallel MPI environment
Anti-Goals
----------
* Testing the low level networking API (e.g., verbs).
* Testing inter-process interaction, such as ORTE-related functionality.
Constraints
-----------
* our unit tests should never perturb a normal build in terms of performance
or correctness
- also should not affect other non-usNIC developers in any way (don't
break Ralph's `make check`)
* static functions are difficult to test from outside the same source file
Design Notes
------------
* Source files named `X.c` include a header at the end named `test/X_test.h`
- Rationale: gives tests access to the static functions in `X.c`
- Rationale: keeps `X.c` clutter-free
* unit test infrastructure lives in `btl_usnic_test.c` and `btl_usnic_test.h`
* unit test functionality is built and enabled by passing
`--enable-ompi-btl-usnic-unit-tests` to configure
- Rationale: default state disables all unit test logic, achieving our
"non-interference" goals
* The tests are run by a new executable that gets built when unit tests are
enabled: `ompi_btl_usnic_run_tests`.
* Tests are registered at dlopen time via an
`__attribute__((__constructor__))` function that is generated by invocations
of the `USNIC_REGISTER_TEST` macro.
- Rationale: add tests in one spot, no need to centralize the list of
tests to run separately from the tests themselves.
* Tests only use a simple `check()` macro right now that has `assert`-like
semantics.
- this could easily be expanded in the future, using the check docs as
inspiration:
http://check.sourceforge.net/doc/check_html/check_4.html#Convenience-Test-Functions
Worthy Future Goals/Features
----------------------------
* Add some mocking capabilities.
- could use the preprocessor to replace regular function/macro calls with
calls to functions/macros that dispatch to changeable function pointers
- will require some reorganization of some of the existing code...
* Output test results in a format that Jenkins and other tools can understand.
- TAP
- jUnit XML
* Possibly utilize part or all of an existing unit testing framework, e.g.:
- check: http://check.sourceforge.net/ (LGPLed)
- cUnit: http://cunit.sourceforge.net/ (unfortunately GPLed...)
* Re-examine test grouping and numbering. Right now there's no real concept
of "suites" or multiple cases within a single test function. Once the
number of tests grows to a certain point it will probably make sense to
revisit this decision.

Просмотреть файл

@ -73,6 +73,7 @@
#include "btl_usnic_send.h"
#include "btl_usnic_recv.h"
#include "btl_usnic_proc.h"
#include "btl_usnic_test.h"
#define OMPI_BTL_USNIC_NUM_WC 500
#define max(a,b) ((a) > (b) ? (a) : (b))
@ -213,6 +214,11 @@ static int usnic_component_close(void)
mca_btl_usnic_component.vendor_part_ids = NULL;
}
#if OMPI_BTL_USNIC_UNIT_TESTS
/* clean up the unit test infrastructure */
ompi_btl_usnic_cleanup_tests();
#endif
return OMPI_SUCCESS;
}

127
ompi/mca/btl/usnic/btl_usnic_test.c Обычный файл
Просмотреть файл

@ -0,0 +1,127 @@
/*
* Copyright (c) 2014 Cisco Systems, Inc. All rights reserved.
* $COPYRIGHT$
*
* Additional copyrights may follow
*
* $HEADER$
*/
#include <stdio.h>
#include "opal/class/opal_list.h"
#include "btl_usnic.h"
#include "btl_usnic_test.h"
int ompi_btl_usnic_num_tests_run = 0;
int ompi_btl_usnic_num_tests_passed = 0;
int ompi_btl_usnic_num_tests_failed = 0;
int ompi_btl_usnic_num_tests_skipped = 0;
struct test_info {
opal_list_item_t li;
char *name;
ompi_btl_usnic_test_fn_t test_fn;
void *ctx;
};
#if OMPI_BTL_USNIC_UNIT_TESTS
static bool initialized = false;
static opal_list_t all_tests;
void ompi_btl_usnic_cleanup_tests(void)
{
opal_list_item_t *li;
struct test_info *info;
if (initialized) {
while (NULL != (li = opal_list_remove_first(&all_tests))) {
info = container_of(li, struct test_info, li);
free(info);
}
OBJ_DESTRUCT(&all_tests);
}
initialized = false;
}
static void init_test_infra(void)
{
if (!initialized) {
OBJ_CONSTRUCT(&all_tests, opal_list_t);
initialized = true;
}
}
void ompi_btl_usnic_register_test(const char *name,
ompi_btl_usnic_test_fn_t test_fn,
void *ctx)
{
struct test_info *info = malloc(sizeof(*info));
assert(info != NULL);
OBJ_CONSTRUCT(&info->li, opal_list_item_t);
init_test_infra();
info->name = strdup(name);
info->test_fn = test_fn;
info->ctx = ctx;
opal_list_append(&all_tests, &info->li);
}
void ompi_btl_usnic_run_tests(void)
{
struct test_info *info;
enum test_result result;
if (!OMPI_BTL_USNIC_UNIT_TESTS) {
test_out("unit tests disabled in this build, doing nothing!\n");
return;
}
test_out("STARTING TESTS\n");
OPAL_LIST_FOREACH(info, &all_tests, struct test_info) {
test_out("running test '%s'... ", info->name);
result = info->test_fn(info->ctx);
++ompi_btl_usnic_num_tests_run;
switch (result) {
case TEST_PASSED:
++ompi_btl_usnic_num_tests_passed;
test_out("PASSED\n");
break;
case TEST_FAILED:
++ompi_btl_usnic_num_tests_failed;
test_out("FAILED\n");
break;
case TEST_SKIPPED:
++ompi_btl_usnic_num_tests_skipped;
test_out("SKIPPED\n");
break;
}
}
test_out("FINISHED TESTS (%d passed, %d failed, %d skipped)\n",
ompi_btl_usnic_num_tests_passed,
ompi_btl_usnic_num_tests_failed,
ompi_btl_usnic_num_tests_skipped);
}
#else /* !OMPI_BTL_USNIC_UNIT_TESTS */
void ompi_btl_usnic_register_test(const char *name,
ompi_btl_usnic_test_fn_t test_fn,
void *ctx)
{
abort(); /* never should be called */
}
void ompi_btl_usnic_run_tests(void)
{
test_out("unit tests disabled in this build, doing nothing!\n");
return;
}
#endif /* !OMPI_BTL_USNIC_UNIT_TESTS */

95
ompi/mca/btl/usnic/btl_usnic_test.h Обычный файл
Просмотреть файл

@ -0,0 +1,95 @@
/*
* Copyright (c) 2014 Cisco Systems, Inc. All rights reserved.
* $COPYRIGHT$
*
* Additional copyrights may follow
*
* $HEADER$
*/
#ifndef BTL_USNIC_TEST_H
#define BTL_USNIC_TEST_H
#include "ompi_config.h"
typedef int (*ompi_btl_usnic_test_fn_t)(void *ctx);
#if OMPI_BTL_USNIC_UNIT_TESTS
# define test_out(...) fprintf(stderr, __VA_ARGS__)
# define check(a) \
do { \
if (!(a)) { \
test_out("%s:%d: check failed, '%s'\n", __func__, __LINE__, #a); \
return TEST_FAILED; \
} \
} while (0)
# define check_str_eq(a,b) \
do { \
const char *a_ = (a); \
const char *b_ = (b); \
if (0 != strcmp(a_,b_)) { \
test_out("%s:%d: check failed, \"%s\" != \"%s\"\n", \
__func__, __LINE__, a_, b_); \
return TEST_FAILED; \
} \
} while (0)
# define check_int_eq(got, expected) \
do { \
if ((got) != (expected)) { \
test_out("%s:%d: check failed, \"%s\" != \"%s\", got %d\n", \
__func__, __LINE__, #got, #expected, (got)); \
return TEST_FAILED; \
} \
} while (0)
/* just use check_int_eq for now, no public error code to string routine
* exists (opal_err2str is static) */
# define check_err_code(got, expected) \
check_int_eq(got, expected)
# define check_msg(a, msg) \
do { \
if (!(a)) { \
test_out("%s:%d: check failed, \"%s\" (%s)\n", \
__func__, __LINE__, #a, (msg)); \
return TEST_FAILED; \
} \
} while (0)
extern int ompi_btl_usnic_num_tests_run;
extern int ompi_btl_usnic_num_tests_passed;
extern int ompi_btl_usnic_num_tests_failed;
extern int ompi_btl_usnic_num_tests_skipped;
enum test_result {
TEST_PASSED = 0,
TEST_FAILED,
TEST_SKIPPED
};
/* let us actually paste __LINE__ with other tokens */
# define USNIC_PASTE(a,b) USNIC_PASTE2(a,b)
# define USNIC_PASTE2(a,b) a ## b
/* A helper macro to de-clutter test registration. */
# define USNIC_REGISTER_TEST(name, test_fn, ctx) \
__attribute__((__constructor__)) \
static void USNIC_PASTE(usnic_reg_ctor_,__LINE__)(void) \
{ \
ompi_btl_usnic_register_test(name, test_fn, ctx); \
} \
#else /* !OMPI_BTL_USNIC_UNIT_TESTS */
# define test_out(...) do {} while(0)
# define USNIC_REGISTER_TEST(name, test_fn, ctx)
#endif
/* Run all registered tests. Typically called by an external utility that
* dlopens the usnic BTL shared object. See run_usnic_tests.c. */
void ompi_btl_usnic_run_tests(void);
void ompi_btl_usnic_register_test(const char *name,
ompi_btl_usnic_test_fn_t test_fn,
void *ctx);
/* should be called once, at component close time */
void ompi_btl_usnic_cleanup_tests(void);
#endif /* BTL_USNIC_TEST_H */

Просмотреть файл

@ -20,6 +20,88 @@
# $HEADER$
#
# OMPI_CHECK_LIBNL3(prefix, [action-if-found], [action-if-not-found])
# --------------------------------------------------------
# check if libnl3 support can be found. sets prefix_{CPPFLAGS,
# LDFLAGS, LIBS} as needed and runs action-if-found if there is
# support, otherwise executes action-if-not-found
#
# libnl3 changed its default header location as of v3.2 (released ca. September
# 2011). It was previously "${prefix}/include/netlink/...". It now is
# "${prefix}/libnl3/include/netlink/...". The logic below only supports
# >=v3.2, under the assumption that it is not widely deployed.
AC_DEFUN([OMPI_CHECK_LIBNL3],[
AC_ARG_WITH([libnl3],
[AC_HELP_STRING([--with-libnl3(=DIR)],
[Build libnl3 support])])
OMPI_CHECK_WITHDIR([libnl3], [$with_libnl3], [include/libnl3/netlink/netlink.h])
AC_ARG_WITH([libnl3-libdir],
[AC_HELP_STRING([--with-libnl3-libdir=DIR],
[Search for libnl3 libraries in DIR])])
OMPI_CHECK_WITHDIR([libnl3-libdir], [$with_libnl3_libdir], [libnl-3.*])
ompi_check_libnl3_$1_save_CPPFLAGS="$CPPFLAGS"
ompi_check_libnl3_$1_save_LDFLAGS="$LDFLAGS"
ompi_check_libnl3_$1_save_LIBS="$LIBS"
ompi_check_libnl3_$1_orig_CPPFLAGS="$$1_CPPFLAGS"
ompi_check_libnl3_$1_orig_LDFLAGS="$$1_LDFLAGS"
ompi_check_libnl3_$1_orig_LIBS="$$1_LIBS"
AS_IF([test "$with_libnl3" != "no"],
[AS_IF([test ! -z "$with_libnl3" -a "$with_libnl3" != "yes"],
[ompi_check_libnl3_dir="$with_libnl3"])
AS_IF([test ! -z "$with_libnl3_libdir" -a "$with_libnl3_libdir" != "yes"],
[ompi_check_libnl3_libdir="$with_libnl3_libdir"])
# OMPI_CHECK_PACKAGE unfortunately can't handle this weird include
# dir layout
AS_IF([test -n "$ompi_check_libnl3_dir"],
[ompi_check_libnl3_includedir="$ompi_check_libnl3_dir/include/libnl3"],
[ompi_check_libnl3_includedir="/usr/include/libnl3"])
$1_CPPFLAGS="$$1_CPPFLAGS -I$ompi_check_libnl3_includedir"
CPPFLAGS="$CPPFLAGS -I$ompi_check_libnl3_includedir"
AC_CHECK_HEADER([netlink/netlink.h],
[# nl_recvmsgs_report appears to be a symbol which
# is present in libnl-3 but not libnl (v1)
_OMPI_CHECK_PACKAGE_LIB([$1],
[nl-3],
[nl_recvmsgs_report],
[],
[$ompi_check_libnl3_dir],
[$ompi_check_libnl3_libdir],
[ompi_check_libnl3_happy="yes"],
[ompi_check_libnl3_happy="no"])],
[ompi_check_libnl3_happy=no])
# make sure that we don't pollute the cache with the results of a
# test performed under different CPPFLAGS
AS_UNSET([ac_cv_header_netlink_netlink_h])],
[ompi_check_libnl3_happy="no"])
# restore global flags
CPPFLAGS="$ompi_check_libnl3_$1_save_CPPFLAGS"
LDFLAGS="$ompi_check_libnl3_$1_save_LDFLAGS"
LIBS="$ompi_check_libnl3_$1_save_LIBS"
AS_IF([test "$ompi_check_libnl3_happy" = "yes"],
[$2],
[AS_IF([test ! -z "$with_libnl3" -a "$with_libnl3" != "no"],
[AC_MSG_ERROR([libnl3 support requested but not found. Aborting])])
# restore prefixed flags on failure
$1_CPPFLAGS="$ompi_check_package_$1_orig_CPPFLAGS"
$1_LDFLAGS="$ompi_check_package_$1_orig_LDFLAGS"
$1_LIBS="$ompi_check_package_$1_orig_LIBS"
$3])
])
# MCA_ompi_btl_usnic_POST_CONFIG([should_build])
# ------------------------------------------
AC_DEFUN([MCA_ompi_btl_usnic_POST_CONFIG], [
AM_CONDITIONAL([OMPI_BTL_USNIC_BUILD_UNIT_TESTS],
[test "$1" -eq 1 && test "X$enable_ompi_btl_usnic_unit_tests" = "Xyes"])
])
# MCA_btl_usnic_CONFIG([action-if-can-compile],
# [action-if-cant-compile])
@ -27,6 +109,20 @@
AC_DEFUN([MCA_ompi_btl_usnic_CONFIG],[
AC_CONFIG_FILES([ompi/mca/btl/usnic/Makefile])
# see README.test for information about this scheme
AC_ARG_ENABLE([ompi-btl-usnic-unit-tests],
[AS_HELP_STRING([--enable-ompi-btl-usnic-unit-tests],
[build unit tests for the usnic BTL,
including the test runner program,
ompi_btl_usnic_run_tests])])
AS_IF([test "X$enable_ompi_btl_usnic_unit_tests" = "Xyes"],
[unit_tests=1
AC_MSG_NOTICE([enabling usnic BTL unit tests])],
[unit_tests=0])
AC_DEFINE_UNQUOTED([OMPI_BTL_USNIC_UNIT_TESTS], [$unit_tests],
[define to 1 if usnic BTL unit tests are enabled, 0 otherwise])
unset unit_tests
OMPI_CHECK_OPENFABRICS([btl_usnic],
[btl_usnic_happy="yes"],
[btl_usnic_happy="no"])

Просмотреть файл

@ -0,0 +1,101 @@
/*
* Copyright (c) 2014 Cisco Systems, Inc. All rights reserved.
* $COPYRIGHT$
*
* Additional copyrights may follow
*
* $HEADER$
*/
/* A simple test runner program for the usnic BTL unit tests. See README.test
* for more information. */
/* for dladdr() */
#define _GNU_SOURCE
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h> /* for dirname() */
#include <mpi.h>
#include <dlfcn.h>
#define MCA_BTL_USNIC_SO "mca_btl_usnic.so"
typedef void (*run_tests_fn_t)(void);
int main(int argc, char **argv)
{
void *mpi_handle;
void *usnic_handle;
void (*run_tests)(void);
int (*init)(int *, char ***);
int (*finalize)(void);
Dl_info info;
char *libmpi_path;
char *path;
char *to;
int path_len;
mpi_handle = dlopen("libmpi.so", RTLD_NOW|RTLD_GLOBAL);
if (mpi_handle == NULL) {
fprintf(stderr, "mpi_handle=NULL dlerror()=%s\n", dlerror());
abort();
}
/* casting awfulness needed for GCC's "-pedantic" option :( */
*(void **)(&init) = dlsym(mpi_handle, "MPI_Init");
if (init == NULL) {
fprintf(stderr, "init=NULL dlerror()=%s\n", dlerror());
abort();
}
/* casting awfulness needed for GCC's "-pedantic" option :( */
*(void **)(&finalize) = dlsym(mpi_handle, "MPI_Finalize");
if (finalize == NULL) {
fprintf(stderr, "finalize=%p dlerror()=%s\n", *(void **)(&finalize), dlerror());
abort();
}
/* call MPI_Init this way to avoid build-time dependency issues */
init(&argc, &argv);
/* figure out where the usnic BTL shared object is relative to libmpi.so */
if (!dladdr(*(void **)(&init), &info)) {
fprintf(stderr, "ERROR: unable to dladdr(init,...)\n");
abort();
}
libmpi_path = strdup(info.dli_fname);
assert(libmpi_path != NULL);
path_len = strlen(libmpi_path) + strlen("/openmpi/") + strlen(MCA_BTL_USNIC_SO);
path = calloc(path_len+1, 1);
to = path;
to = stpcpy(to, dirname(libmpi_path));
to = stpcpy(to, "/openmpi/");
to = stpcpy(to, MCA_BTL_USNIC_SO);
usnic_handle = dlopen(path, RTLD_NOW|RTLD_LOCAL);
if (usnic_handle == NULL) {
fprintf(stderr, "usnic_handle=%p dlerror()=%s\n", (void *)usnic_handle, dlerror());
abort();
}
free(libmpi_path);
free(path);
/* casting awfulness needed for GCC's "-pedantic" option :( */
*(void **)(&run_tests) = dlsym(usnic_handle, "ompi_btl_usnic_run_tests");
if (run_tests == NULL) {
fprintf(stderr, "run_tests=%p dlerror()=%s\n", *(void **)(&run_tests), dlerror());
abort();
}
run_tests();
finalize();
/* deliberately do not dlclose() either handle so that any valgrind stack
* traces are more useful */
return 0;
}