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