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
[email protected]
https://lists.sourceforge.net/lists/listinfo/libusbx-devel