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

Reply via email to