1 /* 2 * libusb test library helper functions 3 * Copyright 2012 Toby Gray <toby.gray (at) realvnc.com> 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 20 #include "libusb_testlib.h" 21 22 #include <stdio.h> 23 #include <stdarg.h> 24 #include <string.h> 25 #include <errno.h> 26 #if !defined(_WIN32_WCE) 27 #include <sys/types.h> 28 #include <sys/stat.h> 29 #include <fcntl.h> 30 #endif 31 32 #if defined(_WIN32_WCE) 33 // No support for selective redirection of STDOUT on WinCE. 34 #define DISABLE_STDOUT_REDIRECTION 35 #define STDOUT_FILENO 1 36 #elif defined(_WIN32) 37 #include <io.h> 38 #define dup _dup 39 #define dup2 _dup2 40 #define open _open 41 #define close _close 42 #define fdopen _fdopen 43 #define NULL_PATH "nul" 44 #define STDOUT_FILENO 1 45 #define STDERR_FILENO 2 46 #else 47 #include <unistd.h> 48 #define NULL_PATH "/dev/null" 49 #endif 50 #define INVALID_FD -1 51 #define IGNORE_RETVAL(expr) do { (void)(expr); } while(0) 52 53 /** 54 * Converts a test result code into a human readable string. 55 */ 56 static const char* test_result_to_str(libusb_testlib_result result) 57 { 58 switch (result) { 59 case TEST_STATUS_SUCCESS: 60 return "Success"; 61 case TEST_STATUS_FAILURE: 62 return "Failure"; 63 case TEST_STATUS_ERROR: 64 return "Error"; 65 case TEST_STATUS_SKIP: 66 return "Skip"; 67 default: 68 return "Unknown"; 69 } 70 } 71 72 static void print_usage(int argc, char ** argv) 73 { 74 printf("Usage: %s [-l] [-v] [<test_name> ...]\n", 75 argc > 0 ? argv[0] : "test_*"); 76 printf(" -l List available tests\n"); 77 printf(" -v Don't redirect STDERR/STDOUT during tests\n"); 78 } 79 80 static void cleanup_test_output(libusb_testlib_ctx * ctx) 81 { 82 #ifndef DISABLE_STDOUT_REDIRECTION 83 if (!ctx->verbose) { 84 if (ctx->old_stdout != INVALID_FD) { 85 IGNORE_RETVAL(dup2(ctx->old_stdout, STDOUT_FILENO)); 86 ctx->old_stdout = INVALID_FD; 87 } 88 if (ctx->old_stderr != INVALID_FD) { 89 IGNORE_RETVAL(dup2(ctx->old_stderr, STDERR_FILENO)); 90 ctx->old_stderr = INVALID_FD; 91 } 92 if (ctx->null_fd != INVALID_FD) { 93 close(ctx->null_fd); 94 ctx->null_fd = INVALID_FD; 95 } 96 if (ctx->output_file != stdout) { 97 fclose(ctx->output_file); 98 ctx->output_file = stdout; 99 } 100 } 101 #endif 102 } 103 104 /** 105 * Setup test output handles 106 * \return zero on success, non-zero on failure 107 */ 108 static int setup_test_output(libusb_testlib_ctx * ctx) 109 { 110 #ifndef DISABLE_STDOUT_REDIRECTION 111 /* Stop output to stdout and stderr from being displayed if using non-verbose output */ 112 if (!ctx->verbose) { 113 /* Keep a copy of STDOUT and STDERR */ 114 ctx->old_stdout = dup(STDOUT_FILENO); 115 if (ctx->old_stdout < 0) { 116 ctx->old_stdout = INVALID_FD; 117 printf("Failed to duplicate stdout handle: %d\n", errno); 118 return 1; 119 } 120 ctx->old_stderr = dup(STDERR_FILENO); 121 if (ctx->old_stderr < 0) { 122 ctx->old_stderr = INVALID_FD; 123 cleanup_test_output(ctx); 124 printf("Failed to duplicate stderr handle: %d\n", errno); 125 return 1; 126 } 127 /* Redirect STDOUT_FILENO and STDERR_FILENO to /dev/null or "nul"*/ 128 ctx->null_fd = open(NULL_PATH, O_WRONLY); 129 if (ctx->null_fd < 0) { 130 ctx->null_fd = INVALID_FD; 131 cleanup_test_output(ctx); 132 printf("Failed to open null handle: %d\n", errno); 133 return 1; 134 } 135 if ((dup2(ctx->null_fd, STDOUT_FILENO) < 0) || 136 (dup2(ctx->null_fd, STDERR_FILENO) < 0)) { 137 cleanup_test_output(ctx); 138 return 1; 139 } 140 ctx->output_file = fdopen(ctx->old_stdout, "w"); 141 if (!ctx->output_file) { 142 ctx->output_file = stdout; 143 cleanup_test_output(ctx); 144 printf("Failed to open FILE for output handle: %d\n", errno); 145 return 1; 146 } 147 } 148 #endif 149 return 0; 150 } 151 152 void libusb_testlib_logf(libusb_testlib_ctx * ctx, 153 const char* fmt, ...) 154 { 155 va_list va; 156 va_start(va, fmt); 157 vfprintf(ctx->output_file, fmt, va); 158 va_end(va); 159 fprintf(ctx->output_file, "\n"); 160 fflush(ctx->output_file); 161 } 162 163 int libusb_testlib_run_tests(int argc, 164 char ** argv, 165 const libusb_testlib_test * tests) 166 { 167 int run_count = 0; 168 int idx = 0; 169 int pass_count = 0; 170 int fail_count = 0; 171 int error_count = 0; 172 int skip_count = 0; 173 int r, j; 174 size_t arglen; 175 libusb_testlib_result test_result; 176 libusb_testlib_ctx ctx; 177 178 /* Setup default mode of operation */ 179 ctx.test_names = NULL; 180 ctx.test_count = 0; 181 ctx.list_tests = false; 182 ctx.verbose = false; 183 ctx.old_stdout = INVALID_FD; 184 ctx.old_stderr = INVALID_FD; 185 ctx.output_file = stdout; 186 ctx.null_fd = INVALID_FD; 187 188 /* Parse command line options */ 189 if (argc >= 2) { 190 for (j = 1; j < argc; j++) { 191 arglen = strlen(argv[j]); 192 if ( ((argv[j][0] == '-') || (argv[j][0] == '/')) && 193 arglen >=2 ) { 194 switch (argv[j][1]) { 195 case 'l': 196 ctx.list_tests = true; 197 break; 198 case 'v': 199 ctx.verbose = true; 200 break; 201 default: 202 printf("Unknown option: '%s'\n", argv[j]); 203 print_usage(argc, argv); 204 return 1; 205 } 206 } else { 207 /* End of command line options, remaining must be list of tests to run */ 208 ctx.test_names = argv + j; 209 ctx.test_count = argc - j; 210 break; 211 } 212 } 213 } 214 215 /* Validate command line options */ 216 if (ctx.test_names && ctx.list_tests) { 217 printf("List of tests requested but test list provided\n"); 218 print_usage(argc, argv); 219 return 1; 220 } 221 222 /* Setup test log output */ 223 r = setup_test_output(&ctx); 224 if (r != 0) 225 return r; 226 227 /* Act on any options not related to running tests */ 228 if (ctx.list_tests) { 229 while (tests[idx].function != NULL) { 230 libusb_testlib_logf(&ctx, tests[idx].name); 231 ++idx; 232 } 233 cleanup_test_output(&ctx); 234 return 0; 235 } 236 237 /* Run any requested tests */ 238 while (tests[idx].function != NULL) { 239 const libusb_testlib_test * test = &tests[idx]; 240 ++idx; 241 if (ctx.test_count > 0) { 242 /* Filtering tests to run, check if this is one of them */ 243 int i; 244 for (i = 0; i < ctx.test_count; ++i) { 245 if (strcmp(ctx.test_names[i], test->name) == 0) 246 /* Matches a requested test name */ 247 break; 248 } 249 if (i >= ctx.test_count) { 250 /* Failed to find a test match, so do the next loop iteration */ 251 continue; 252 } 253 } 254 libusb_testlib_logf(&ctx, 255 "Starting test run: %s...", test->name); 256 test_result = test->function(&ctx); 257 libusb_testlib_logf(&ctx, 258 "%s (%d)", 259 test_result_to_str(test_result), test_result); 260 switch (test_result) { 261 case TEST_STATUS_SUCCESS: pass_count++; break; 262 case TEST_STATUS_FAILURE: fail_count++; break; 263 case TEST_STATUS_ERROR: error_count++; break; 264 case TEST_STATUS_SKIP: skip_count++; break; 265 } 266 ++run_count; 267 } 268 libusb_testlib_logf(&ctx, "---"); 269 libusb_testlib_logf(&ctx, "Ran %d tests", run_count); 270 libusb_testlib_logf(&ctx, "Passed %d tests", pass_count); 271 libusb_testlib_logf(&ctx, "Failed %d tests", fail_count); 272 libusb_testlib_logf(&ctx, "Error in %d tests", error_count); 273 libusb_testlib_logf(&ctx, "Skipped %d tests", skip_count); 274 275 cleanup_test_output(&ctx); 276 return pass_count != run_count; 277 } 278