This improves the testlib used in libusbx to allow tests to either return the test status or to call a function which never returns.
This leads to simpler test code as nested functions do not have to return test status up the calling chain. --- tests/libusbx_testlib.h | 54 ++++++++++++++++++++++++ tests/stress.c | 8 ++-- tests/testlib.c | 105 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 160 insertions(+), 7 deletions(-) diff --git a/tests/libusbx_testlib.h b/tests/libusbx_testlib.h index 06dbc8e..8b109a2 100644 --- a/tests/libusbx_testlib.h +++ b/tests/libusbx_testlib.h @@ -21,6 +21,8 @@ #define LIBUSBX_TESTLIB_H #include <stdio.h> +#include <setjmp.h> +#include "libusb.h" #if !defined(bool) #define bool int @@ -32,6 +34,15 @@ #define false (!true) #endif +/** + * Helper macro used to concatinate two values + */ +#define LIBUSBX_CONCAT_(a, b) a ## b +/** + * Used to concatinate two strings in the preprocessor + */ +#define LIBUSBX_CONCAT(a, b) LIBUSBX_CONCAT_(a, b) + /** Values returned from a test function to indicate test result */ typedef enum { /** Indicates that the test ran successfully. */ @@ -57,6 +68,10 @@ typedef struct { int old_stderr; FILE* output_file; int null_fd; + char * test_device; + int test_device_bus; + int test_device_address; + jmp_buf test_return_buf; } libusbx_testlib_ctx; /** @@ -90,6 +105,12 @@ typedef struct { #define LIBUSBX_NULL_TEST {NULL, NULL} /** + * Value to use in a test array for a test with the provided name + * defined in a function named with a test_ prefix. + */ +#define LIBUSBX_NAMED_TEST(name) {#name, &LIBUSBX_CONCAT(test_, name)} + +/** * Runs the tests provided. * * Before running any tests argc and argv will be processed @@ -104,4 +125,37 @@ int libusbx_testlib_run_tests(int argc, char ** argv, const libusbx_testlib_test * tests); +/** + * Exits the current test with the provided test status. + * + * This function makes use of longjmp so never returns. + */ +void libusbx_testlib_finish_current_test(libusbx_testlib_ctx * tctx, + libusbx_testlib_result result); + +/** + * Skips the current test if no test device has been provided. + * + * This function makes use of libusbx_testlib_finish_current_test + * so may not return. + */ +void libusbx_testlib_skip_if_no_device(libusbx_testlib_ctx * tctx); + +/** + * Initialise and return a libusb context or immediately fail the test. + * + * This function makes use of libusbx_testlib_finish_current_test + * so may not return. + */ +libusb_context * libusbx_testlib_init_ctx_or_fail(libusbx_testlib_ctx * tctx); + +/** + * Open and return a device handle for the test device or fail the test. + * + * This function makes use of libusbx_testlib_finish_current_test + * so may not return. + */ +libusb_device_handle * libusbx_testlib_open_test_device( + libusbx_testlib_ctx * tctx, libusb_context * ctx); + #endif //LIBUSBX_TESTLIB_H diff --git a/tests/stress.c b/tests/stress.c index e53f415..f64f4b7 100644 --- a/tests/stress.c +++ b/tests/stress.c @@ -146,10 +146,10 @@ static libusbx_testlib_result test_default_context_change(libusbx_testlib_ctx * /* Fill in the list of tests. */ static const libusbx_testlib_test tests[] = { - {"init_and_exit", &test_init_and_exit}, - {"get_device_list", &test_get_device_list}, - {"many_device_lists", &test_many_device_lists}, - {"default_context_change", &test_default_context_change}, + LIBUSBX_NAMED_TEST(init_and_exit), + LIBUSBX_NAMED_TEST(get_device_list), + LIBUSBX_NAMED_TEST(many_device_lists), + LIBUSBX_NAMED_TEST(default_context_change), LIBUSBX_NULL_TEST }; diff --git a/tests/testlib.c b/tests/testlib.c index e69aa39..414695c 100644 --- a/tests/testlib.c +++ b/tests/testlib.c @@ -20,6 +20,7 @@ #include "libusbx_testlib.h" #include <stdio.h> +#include <stdlib.h> #include <stdarg.h> #include <string.h> #include <errno.h> @@ -51,6 +52,12 @@ #define IGNORE_RETVAL(expr) do { (void)(expr); } while(0) /** + * As setjmp and longjmp require non-zero values, this + * value is added to results before calling longjmp. + */ +#define TEST_JMP_RESULT_OFFSET 0x100 + +/** * Converts a test result code into a human readable string. */ static const char* test_result_to_str(libusbx_testlib_result result) @@ -71,9 +78,10 @@ static const char* test_result_to_str(libusbx_testlib_result result) static void print_usage(int argc, char ** argv) { - printf("Usage: %s [-l] [-v] [<test_name> ...]\n", + printf("Usage: %s [-l] [-s <bus>:<addr>] [-v] [<test_name> ...]\n", argc > 0 ? argv[0] : "test_*"); printf(" -l List available tests\n"); + printf(" -s Use the device at the provided bus and address\n"); printf(" -v Don't redirect STDERR/STDOUT during tests\n"); } @@ -183,6 +191,9 @@ int libusbx_testlib_run_tests(int argc, ctx.old_stderr = INVALID_FD; ctx.output_file = stdout; ctx.null_fd = INVALID_FD; + ctx.test_device = NULL; + ctx.test_device_bus = 0; + ctx.test_device_address = 0; /* Parse command line options */ if (argc >= 2) { @@ -197,6 +208,15 @@ int libusbx_testlib_run_tests(int argc, case 'v': ctx.verbose = true; break; + case 's': + ++j; + if (j >= argc) { + printf("Option -s requires a parameter\n"); + print_usage(argc, argv); + return 1; + } + ctx.test_device = argv[j]; + break; default: printf("Unknown option: '%s'\n", argv[j]); print_usage(argc, argv); @@ -217,11 +237,23 @@ int libusbx_testlib_run_tests(int argc, print_usage(argc, argv); return 1; } + if (ctx.test_device) { + char* separator = strstr(ctx.test_device, ":"); + if (!separator) { + printf("Please specify the device to use as <bus>:<address>.\n"); + printf("E.g. \"-s 1:4\"\n"); + return 1; + } + ctx.test_device_bus = strtol(ctx.test_device, NULL, 10); + ctx.test_device_address = strtol(separator + 1, NULL, 10); + printf("Using device at %d:%d for tests which use IO\n", + ctx.test_device_bus, ctx.test_device_address); + } /* Setup test log output */ r = setup_test_output(&ctx); if (r != 0) - return r; + return r; /* Act on any options not related to running tests */ if (ctx.list_tests) { @@ -236,6 +268,7 @@ int libusbx_testlib_run_tests(int argc, /* Run any requested tests */ while (tests[idx].function != NULL) { const libusbx_testlib_test * test = &tests[idx]; + int jmp_ret = 0; ++idx; if (ctx.test_count > 0) { /* Filtering tests to run, check if this is one of them */ @@ -252,7 +285,14 @@ int libusbx_testlib_run_tests(int argc, } libusbx_testlib_logf(&ctx, "Starting test run: %s...", test->name); - test_result = test->function(&ctx); + jmp_ret = setjmp(ctx.test_return_buf); + if (!jmp_ret) { + test_result = test->function(&ctx); + } else { + /* Just returned from a longjmp, so use the value as the + * test result. */ + test_result = jmp_ret - TEST_JMP_RESULT_OFFSET; + } libusbx_testlib_logf(&ctx, "%s (%d)", test_result_to_str(test_result), test_result); @@ -274,3 +314,62 @@ int libusbx_testlib_run_tests(int argc, cleanup_test_output(&ctx); return pass_count != run_count; } + +void libusbx_testlib_finish_current_test(libusbx_testlib_ctx * tctx, + libusbx_testlib_result result) +{ + longjmp(tctx->test_return_buf, result + TEST_JMP_RESULT_OFFSET); +} + +void libusbx_testlib_skip_if_no_device(libusbx_testlib_ctx * tctx) +{ + if (!tctx->test_device) { + libusbx_testlib_logf(tctx, "Skipping test as no test device provided"); + libusbx_testlib_finish_current_test(tctx, TEST_STATUS_SKIP); + } +} + +libusb_context * libusbx_testlib_init_ctx_or_fail(libusbx_testlib_ctx * tctx) +{ + libusb_context * ret = NULL; + int r = libusb_init(&ret); + if (r != LIBUSB_SUCCESS) { + libusbx_testlib_logf(tctx, "Failed to init libusb: %d", r); + libusbx_testlib_finish_current_test(tctx, TEST_STATUS_FAILURE); + } + return ret; +} + +libusb_device_handle * libusbx_testlib_open_test_device( + libusbx_testlib_ctx * tctx, + libusb_context * ctx) +{ + libusb_device ** device_list; + libusb_device_handle * dev = NULL; + int i; + ssize_t list_size = libusb_get_device_list((ctx), &device_list); + if (list_size < 0 || device_list == NULL) { + libusbx_testlib_logf(tctx, + "Failed to get device list: %d (%p)", + -list_size, device_list); + libusbx_testlib_finish_current_test(tctx, TEST_STATUS_FAILURE); + } + for (i = 0; i < list_size; ++i) { + const uint8_t bus = libusb_get_bus_number(device_list[i]); + const uint8_t addr = libusb_get_device_address(device_list[i]); + if (bus == tctx->test_device_bus && + addr == tctx->test_device_address) { + int r = libusb_open(device_list[i], &(dev)); + if (r != LIBUSB_SUCCESS) { + libusbx_testlib_logf(tctx, "Failed to open device: %d", r); + libusbx_testlib_finish_current_test(tctx, TEST_STATUS_FAILURE); + } + } + } + libusb_free_device_list(device_list, 1); + if (dev == NULL) { + libusbx_testlib_logf(tctx, "Failed to find a matching test device"); + libusbx_testlib_finish_current_test(tctx, TEST_STATUS_FAILURE); + } + return dev; +} -- 1.7.9.5 ------------------------------------------------------------------------------ This SF.net email is sponsored by Windows: Build for Windows Store. http://p.sf.net/sfu/windows-dev2dev _______________________________________________ libusbx-devel mailing list libusbx-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/libusbx-devel