• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libusb test library helper functions
3  * Copyright © 2012 Toby Gray <toby.gray@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  */
test_result_to_str(libusb_testlib_result result)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 
print_usage(int argc,char ** argv)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 
cleanup_test_output(libusb_testlib_ctx * ctx)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  */
setup_test_output(libusb_testlib_ctx * ctx)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 
libusb_testlib_logf(libusb_testlib_ctx * ctx,const char * fmt,...)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 
libusb_testlib_run_tests(int argc,char ** argv,const libusb_testlib_test * tests)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