Home | History | Annotate | Download | only in tests
      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