tests: Refactor testlib to simplify and fix Cygwin build
authorChris Dickens <christopher.a.dickens@gmail.com>
Sun, 13 Sep 2020 22:30:04 +0000 (15:30 -0700)
committerChris Dickens <christopher.a.dickens@gmail.com>
Sun, 8 Nov 2020 05:11:33 +0000 (21:11 -0800)
Signed-off-by: Chris Dickens <christopher.a.dickens@gmail.com>
libusb/os/windows_common.c
libusb/version_nano.h
tests/libusb_testlib.h
tests/stress.c
tests/testlib.c

index f91075a..8dc0e0a 100644 (file)
@@ -297,7 +297,7 @@ void windows_force_sync_completion(struct usbi_transfer *itransfer, ULONG size)
        struct windows_transfer_priv *transfer_priv = usbi_get_transfer_priv(itransfer);
        OVERLAPPED *overlapped = &transfer_priv->overlapped;
 
-       usbi_dbg("transfer %p, length %lu", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer), size);
+       usbi_dbg("transfer %p, length %lu", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer), ULONG_CAST(size));
 
        overlapped->Internal = (ULONG_PTR)STATUS_SUCCESS;
        overlapped->InternalHigh = (ULONG_PTR)size;
index 8a2be53..4e73d5c 100644 (file)
@@ -1 +1 @@
-#define LIBUSB_NANO 11570
+#define LIBUSB_NANO 11571
index 6c987d3..b3dbaae 100644 (file)
 #ifndef LIBUSB_TESTLIB_H
 #define LIBUSB_TESTLIB_H
 
-#include <stdio.h>
-
-#if !defined(bool)
-#define bool int
-#endif
-#if !defined(true)
-#define true (1 == 1)
-#endif
-#if !defined(false)
-#define false (!true)
-#endif
-
 /** Values returned from a test function to indicate test result */
 typedef enum {
        /** Indicates that the test ran successfully. */
@@ -41,53 +29,33 @@ typedef enum {
        /** Indicates that an unexpected error occurred. */
        TEST_STATUS_ERROR,
        /** Indicates that the test can't be run. For example this may be
-       * due to no suitable device being connected to perform the tests.*/
+        * due to no suitable device being connected to perform the tests. */
        TEST_STATUS_SKIP
 } libusb_testlib_result;
 
 /**
- * Context for test library functions
- */
-typedef struct {
-       char ** test_names;
-       int test_count;
-       bool list_tests;
-       bool verbose;
-       int old_stdout;
-       int old_stderr;
-       FILE* output_file;
-       int null_fd;
-} libusb_testlib_ctx;
-
-/**
  * Logs some test information or state
  */
-void libusb_testlib_logf(libusb_testlib_ctx * ctx, 
-                          const char* fmt, ...);
-
-/**
- * Function pointer for a libusb test function.
- *
- * Should return TEST_STATUS_SUCCESS on success or another TEST_STATUS value.
- */
-typedef libusb_testlib_result
-(*libusb_testlib_test_function)(libusb_testlib_ctx * ctx);
+void libusb_testlib_logf(const char *fmt, ...);
 
 /**
  * Structure holding a test description.
  */
 typedef struct {
        /** Human readable name of the test. */
-       const char * name;
-       /** The test library will call this function to run the test. */
-       libusb_testlib_test_function function;
+       const char *name;
+       /** The test library will call this function to run the test.
+        *
+        * Should return TEST_STATUS_SUCCESS on success or another TEST_STATUS value.
+        */
+       libusb_testlib_result (*function)(void);
 } libusb_testlib_test;
 
 /**
  * Value to use at the end of a test array to indicate the last
  * element.
  */
-#define LIBUSB_NULL_TEST {NULL, NULL}
+#define LIBUSB_NULL_TEST { NULL, NULL }
 
 /**
  * Runs the tests provided.
@@ -100,8 +68,7 @@ typedef struct {
  * \param tests A NULL_TEST terminated array of tests
  * \return 0 on success, non-zero on failure
  */
-int libusb_testlib_run_tests(int argc,
-                              char ** argv,
-                              const libusb_testlib_test * tests);
+int libusb_testlib_run_tests(int argc, char *argv[],
+       const libusb_testlib_test *tests);
 
 #endif //LIBUSB_TESTLIB_H
index 1602ee9..5c8c315 100644 (file)
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include <stdio.h>
 #include <string.h>
-#include <memory.h>
 
 #include "libusb.h"
 #include "libusb_testlib.h"
 
 /** Test that creates and destroys a single concurrent context
  * 10000 times. */
-static libusb_testlib_result test_init_and_exit(libusb_testlib_ctx * tctx)
+static libusb_testlib_result test_init_and_exit(void)
 {
-       libusb_context * ctx = NULL;
-       int i;
-       for (i = 0; i < 10000; ++i) {
-               int r = libusb_init(&ctx);
+       for (int i = 0; i < 10000; ++i) {
+               libusb_context *ctx = NULL;
+               int r;
+
+               r = libusb_init(&ctx);
                if (r != LIBUSB_SUCCESS) {
-                       libusb_testlib_logf(tctx,
+                       libusb_testlib_logf(
                                "Failed to init libusb on iteration %d: %d",
                                i, r);
                        return TEST_STATUS_FAILURE;
                }
                libusb_exit(ctx);
-               ctx = NULL;
        }
 
        return TEST_STATUS_SUCCESS;
 }
 
 /** Tests that devices can be listed 1000 times. */
-static libusb_testlib_result test_get_device_list(libusb_testlib_ctx * tctx)
+static libusb_testlib_result test_get_device_list(void)
 {
-       libusb_context * ctx = NULL;
-       int r, i;
+       libusb_context *ctx;
+       int r;
+
        r = libusb_init(&ctx);
        if (r != LIBUSB_SUCCESS) {
-               libusb_testlib_logf(tctx, "Failed to init libusb: %d", r);
+               libusb_testlib_logf("Failed to init libusb: %d", r);
                return TEST_STATUS_FAILURE;
        }
-       for (i = 0; i < 1000; ++i) {
-               libusb_device ** device_list;
+
+       for (int i = 0; i < 1000; ++i) {
+               libusb_device **device_list = NULL;
                ssize_t list_size = libusb_get_device_list(ctx, &device_list);
-               if (list_size < 0 || device_list == NULL) {
-                       libusb_testlib_logf(tctx,
+               if (list_size < 0 || !device_list) {
+                       libusb_testlib_logf(
                                "Failed to get device list on iteration %d: %ld (%p)",
                                i, (long)-list_size, device_list);
+                       libusb_exit(ctx);
                        return TEST_STATUS_FAILURE;
                }
                libusb_free_device_list(device_list, 1);
        }
+
        libusb_exit(ctx);
        return TEST_STATUS_SUCCESS;
 }
 
 /** Tests that 100 concurrent device lists can be open at a time. */
-static libusb_testlib_result test_many_device_lists(libusb_testlib_ctx * tctx)
+static libusb_testlib_result test_many_device_lists(void)
 {
 #define LIST_COUNT 100
-       libusb_context * ctx = NULL;
-       libusb_device ** device_lists[LIST_COUNT];
-       int r, i;
-       memset(device_lists, 0, sizeof(device_lists));
+       libusb_testlib_result result = TEST_STATUS_SUCCESS;
+       libusb_context *ctx = NULL;
+       libusb_device **device_lists[LIST_COUNT];
+       int r;
 
        r = libusb_init(&ctx);
        if (r != LIBUSB_SUCCESS) {
-               libusb_testlib_logf(tctx, "Failed to init libusb: %d", r);
+               libusb_testlib_logf("Failed to init libusb: %d", r);
                return TEST_STATUS_FAILURE;
        }
 
+       memset(device_lists, 0, sizeof(device_lists));
+
        /* Create the 100 device lists. */
-       for (i = 0; i < LIST_COUNT; ++i) {
-               ssize_t list_size = libusb_get_device_list(ctx, &(device_lists[i]));
-               if (list_size < 0 || device_lists[i] == NULL) {
-                       libusb_testlib_logf(tctx,
-                               "Failed to get device list on iteration %d: %d (%p)",
-                               i, -list_size, device_lists[i]);
-                       return TEST_STATUS_FAILURE;
+       for (int i = 0; i < LIST_COUNT; ++i) {
+               ssize_t list_size = libusb_get_device_list(ctx, &device_lists[i]);
+               if (list_size < 0 || !device_lists[i]) {
+                       libusb_testlib_logf(
+                               "Failed to get device list on iteration %d: %ld (%p)",
+                               i, (long)-list_size, device_lists[i]);
+                       result = TEST_STATUS_FAILURE;
+                       break;
                }
        }
 
        /* Destroy the 100 device lists. */
-       for (i = 0; i < LIST_COUNT; ++i) {
-               if (device_lists[i]) {
+       for (int i = 0; i < LIST_COUNT; ++i) {
+               if (device_lists[i])
                        libusb_free_device_list(device_lists[i], 1);
-                       device_lists[i] = NULL;
-               }
        }
 
        libusb_exit(ctx);
-       return TEST_STATUS_SUCCESS;
+       return result;
 #undef LIST_COUNT
 }
 
 /** Tests that the default context (used for various things including
  * logging) works correctly when the first context created in a
  * process is destroyed. */
-static libusb_testlib_result test_default_context_change(libusb_testlib_ctx * tctx)
+static libusb_testlib_result test_default_context_change(void)
 {
-       libusb_context * ctx = NULL;
-       int r, i;
+       for (int i = 0; i < 100; ++i) {
+               libusb_context *ctx = NULL;
+               int r;
 
-       for (i = 0; i < 100; ++i) {
                /* First create a new context */
                r = libusb_init(&ctx);
                if (r != LIBUSB_SUCCESS) {
-                       libusb_testlib_logf(tctx, "Failed to init libusb: %d", r);
+                       libusb_testlib_logf("Failed to init libusb: %d", r);
                        return TEST_STATUS_FAILURE;
                }
 
@@ -132,7 +135,8 @@ static libusb_testlib_result test_default_context_change(libusb_testlib_ctx * tc
                /* Now create a reference to the default context */
                r = libusb_init(NULL);
                if (r != LIBUSB_SUCCESS) {
-                       libusb_testlib_logf(tctx, "Failed to init libusb: %d", r);
+                       libusb_testlib_logf("Failed to init libusb: %d", r);
+                       libusb_exit(ctx);
                        return TEST_STATUS_FAILURE;
                }
 
@@ -147,14 +151,14 @@ static libusb_testlib_result test_default_context_change(libusb_testlib_ctx * tc
 
 /* Fill in the list of tests. */
 static const libusb_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},
+       { "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 },
        LIBUSB_NULL_TEST
 };
 
-int main (int argc, char ** argv)
+int main(int argc, char *argv[])
 {
        return libusb_testlib_run_tests(argc, argv, tests);
 }
index 95c9c0a..fb4fee6 100644 (file)
 
 #include "libusb_testlib.h"
 
-#include <stdio.h>
+#include <errno.h>
 #include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
 #include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
 
 #if defined(_WIN32)
-#include <io.h>
-#define dup _dup
-#define dup2 _dup2
-#define open _open
-#define close _close
-#define fdopen _fdopen
 #define NULL_PATH "nul"
-#define STDOUT_FILENO 1
-#define STDERR_FILENO 2
 #else
-#include <unistd.h>
 #define NULL_PATH "/dev/null"
 #endif
-#define INVALID_FD -1
-#define IGNORE_RETVAL(expr) do { (void)(expr); } while(0)
 
 /**
  * Converts a test result code into a human readable string.
  */
-static const chartest_result_to_str(libusb_testlib_result result)
+static const char *test_result_to_str(libusb_testlib_result result)
 {
        switch (result) {
        case TEST_STATUS_SUCCESS:
@@ -63,96 +50,27 @@ static const char* test_result_to_str(libusb_testlib_result result)
        }
 }
 
-static void print_usage(int argc, char ** argv)
+static void print_usage(const char *progname)
 {
-       printf("Usage: %s [-l] [-v] [<test_name> ...]\n",
-               argc > 0 ? argv[0] : "test_*");
+       printf("Usage: %s [-l] [-v] [<test_name> ...]\n", progname);
        printf("   -l   List available tests\n");
-       printf("   -v   Don't redirect STDERR/STDOUT during tests\n");
+       printf("   -v   Don't redirect STDERR before running tests\n");
+       printf("   -h   Display this help and exit\n");
 }
 
-static void cleanup_test_output(libusb_testlib_ctx * ctx)
-{
-       if (!ctx->verbose) {
-               if (ctx->old_stdout != INVALID_FD) {
-                       IGNORE_RETVAL(dup2(ctx->old_stdout, STDOUT_FILENO));
-                       ctx->old_stdout = INVALID_FD;
-               }
-               if (ctx->old_stderr != INVALID_FD) {
-                       IGNORE_RETVAL(dup2(ctx->old_stderr, STDERR_FILENO));
-                       ctx->old_stderr = INVALID_FD;
-               }
-               if (ctx->null_fd != INVALID_FD) {
-                       close(ctx->null_fd);
-                       ctx->null_fd = INVALID_FD;
-               }
-               if (ctx->output_file != stdout) {
-                       fclose(ctx->output_file);
-                       ctx->output_file = stdout;
-               }
-       }
-}
-
-/**
- * Setup test output handles
- * \return zero on success, non-zero on failure
- */
-static int setup_test_output(libusb_testlib_ctx * ctx)
-{
-       /* Stop output to stdout and stderr from being displayed if using non-verbose output */
-       if (!ctx->verbose) {
-               /* Keep a copy of STDOUT and STDERR */
-               ctx->old_stdout = dup(STDOUT_FILENO);
-               if (ctx->old_stdout < 0) {
-                       ctx->old_stdout = INVALID_FD;
-                       printf("Failed to duplicate stdout handle: %d\n", errno);
-                       return 1;
-               }
-               ctx->old_stderr = dup(STDERR_FILENO);
-               if (ctx->old_stderr < 0) {
-                       ctx->old_stderr = INVALID_FD;
-                       cleanup_test_output(ctx);
-                       printf("Failed to duplicate stderr handle: %d\n", errno);
-                       return 1;
-               }
-               /* Redirect STDOUT_FILENO and STDERR_FILENO to /dev/null or "nul"*/
-               ctx->null_fd = open(NULL_PATH, O_WRONLY);
-               if (ctx->null_fd < 0) {
-                       ctx->null_fd = INVALID_FD;
-                       cleanup_test_output(ctx);
-                       printf("Failed to open null handle: %d\n", errno);
-                       return 1;
-               }
-               if ((dup2(ctx->null_fd, STDOUT_FILENO) < 0) ||
-                       (dup2(ctx->null_fd, STDERR_FILENO) < 0)) {
-                               cleanup_test_output(ctx);
-                               return 1;
-               }
-               ctx->output_file = fdopen(ctx->old_stdout, "w");
-               if (!ctx->output_file) {
-                       ctx->output_file = stdout;
-                       cleanup_test_output(ctx);
-                       printf("Failed to open FILE for output handle: %d\n", errno);
-                       return 1;
-               }
-       }
-       return 0;
-}
-
-void libusb_testlib_logf(libusb_testlib_ctx * ctx,
-       const char* fmt, ...)
+void libusb_testlib_logf(const char *fmt, ...)
 {
        va_list va;
+
        va_start(va, fmt);
-       vfprintf(ctx->output_file, fmt, va);
+       vfprintf(stdout, fmt, va);
        va_end(va);
-       fprintf(ctx->output_file, "\n");
-       fflush(ctx->output_file);
+       fputc('\n', stdout);
+       fflush(stdout);
 }
 
-int libusb_testlib_run_tests(int argc,
-       char ** argv,
-       const libusb_testlib_test * tests)
+int libusb_testlib_run_tests(int argc, char *argv[],
+       const libusb_testlib_test *tests)
 {
        int run_count = 0;
        int idx = 0;
@@ -160,108 +78,105 @@ int libusb_testlib_run_tests(int argc,
        int fail_count = 0;
        int error_count = 0;
        int skip_count = 0;
-       int r, j;
-       size_t arglen;
-       libusb_testlib_result test_result;
-       libusb_testlib_ctx ctx;
 
        /* Setup default mode of operation */
-       ctx.test_names = NULL;
-       ctx.test_count = 0;
-       ctx.list_tests = false;
-       ctx.verbose = false;
-       ctx.old_stdout = INVALID_FD;
-       ctx.old_stderr = INVALID_FD;
-       ctx.output_file = stdout;
-       ctx.null_fd = INVALID_FD;
+       char **test_names = NULL;
+       int test_count = 0;
+       bool list_tests = false;
+       bool verbose = false;
 
        /* Parse command line options */
        if (argc >= 2) {
-               for (j = 1; j < argc; j++) {
-                       arglen = strlen(argv[j]);
-                       if ( ((argv[j][0] == '-') || (argv[j][0] == '/')) &&
-                               arglen >=2 ) {
-                                       switch (argv[j][1]) {
+               for (int j = 1; j < argc; j++) {
+                       const char *argstr = argv[j];
+                       size_t arglen = strlen(argstr);
+
+                       if (argstr[0] == '-' || argstr[0] == '/') {
+                               if (arglen == 2) {
+                                       switch (argstr[1]) {
                                        case 'l':
-                                               ctx.list_tests = true;
-                                               break;
+                                               list_tests = true;
+                                               continue;
                                        case 'v':
-                                               ctx.verbose = true;
-                                               break;
-                                       default:
-                                               printf("Unknown option: '%s'\n", argv[j]);
-                                               print_usage(argc, argv);
-                                               return 1;
+                                               verbose = true;
+                                               continue;
+                                       case 'h':
+                                               print_usage(argv[0]);
+                                               return 0;
                                        }
+                               }
+
+                               fprintf(stderr, "Unknown option: '%s'\n", argstr);
+                               print_usage(argv[0]);
+                               return 1;
                        } else {
                                /* End of command line options, remaining must be list of tests to run */
-                               ctx.test_names = argv + j;
-                               ctx.test_count = argc - j;
+                               test_names = argv + j;
+                               test_count = argc - j;
                                break;
                        }
                }
        }
 
        /* Validate command line options */
-       if (ctx.test_names && ctx.list_tests) {
-               printf("List of tests requested but test list provided\n");
-               print_usage(argc, argv);
+       if (test_names && list_tests) {
+               fprintf(stderr, "List of tests requested but test list provided\n");
+               print_usage(argv[0]);
                return 1;
        }
 
        /* Setup test log output */
-       r = setup_test_output(&ctx);
-       if (r != 0)
-               return r;  
+       if (!verbose) {
+               if (!freopen(NULL_PATH, "w", stderr)) {
+                       printf("Failed to open null handle: %d\n", errno);
+                       return 1;
+               }
+       }
 
        /* Act on any options not related to running tests */
-       if (ctx.list_tests) {
-               while (tests[idx].function != NULL) {
-                       libusb_testlib_logf(&ctx, tests[idx].name);
-                       ++idx;
-               }
-               cleanup_test_output(&ctx);
+       if (list_tests) {
+               while (tests[idx].function)
+                       libusb_testlib_logf("%s", tests[idx++].name);
                return 0;
        }
 
        /* Run any requested tests */
-       while (tests[idx].function != NULL) {
-               const libusb_testlib_test * test = &tests[idx];
-               ++idx;
-               if (ctx.test_count > 0) {
+       while (tests[idx].function) {
+               const libusb_testlib_test *test = &tests[idx++];
+               libusb_testlib_result test_result;
+
+               if (test_count > 0) {
                        /* Filtering tests to run, check if this is one of them */
                        int i;
-                       for (i = 0; i < ctx.test_count; ++i) {
-                               if (strcmp(ctx.test_names[i], test->name) == 0)
+
+                       for (i = 0; i < test_count; i++) {
+                               if (!strcmp(test_names[i], test->name))
                                        /* Matches a requested test name */
                                        break;
                        }
-                       if (i >= ctx.test_count) {
+                       if (i == test_count) {
                                /* Failed to find a test match, so do the next loop iteration */
                                continue;
                        }
                }
-               libusb_testlib_logf(&ctx,
-                       "Starting test run: %s...", test->name);
-               test_result = test->function(&ctx);
-               libusb_testlib_logf(&ctx,
-                       "%s (%d)",
-                       test_result_to_str(test_result), test_result);
+               libusb_testlib_logf("Starting test run: %s...", test->name);
+               test_result = test->function();
+               libusb_testlib_logf("%s (%d)", test_result_to_str(test_result), test_result);
                switch (test_result) {
                case TEST_STATUS_SUCCESS: pass_count++; break;
                case TEST_STATUS_FAILURE: fail_count++; break;
                case TEST_STATUS_ERROR: error_count++; break;
                case TEST_STATUS_SKIP: skip_count++; break;
                }
-               ++run_count;
+               run_count++;
        }
-       libusb_testlib_logf(&ctx, "---");
-       libusb_testlib_logf(&ctx, "Ran %d tests", run_count);
-       libusb_testlib_logf(&ctx, "Passed %d tests", pass_count);
-       libusb_testlib_logf(&ctx, "Failed %d tests", fail_count);
-       libusb_testlib_logf(&ctx, "Error in %d tests", error_count);
-       libusb_testlib_logf(&ctx, "Skipped %d tests", skip_count);
 
-       cleanup_test_output(&ctx);
+       libusb_testlib_logf("---");
+       libusb_testlib_logf("Ran %d tests", run_count);
+       libusb_testlib_logf("Passed %d tests", pass_count);
+       libusb_testlib_logf("Failed %d tests", fail_count);
+       libusb_testlib_logf("Error in %d tests", error_count);
+       libusb_testlib_logf("Skipped %d tests", skip_count);
+
        return pass_count != run_count;
 }