Core: Fix writing of log lines a single fprintf call
[libusbx.git] / tests / testlib.c
blobe69aa3901f25a3a79a0aa3dd8f5e23872991510d
1 /*
2 * libusbx test library helper functions
3 * Copyright © 2012 Toby Gray <toby.gray@realvnc.com>
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.
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.
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
20 #include "libusbx_testlib.h"
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
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)
53 /**
54 * Converts a test result code into a human readable string.
56 static const char* test_result_to_str(libusbx_testlib_result result)
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";
72 static void print_usage(int argc, char ** argv)
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");
80 static void cleanup_test_output(libusbx_testlib_ctx * ctx)
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;
88 if (ctx->old_stderr != INVALID_FD) {
89 IGNORE_RETVAL(dup2(ctx->old_stderr, STDERR_FILENO));
90 ctx->old_stderr = INVALID_FD;
92 if (ctx->null_fd != INVALID_FD) {
93 close(ctx->null_fd);
94 ctx->null_fd = INVALID_FD;
96 if (ctx->output_file != stdout) {
97 fclose(ctx->output_file);
98 ctx->output_file = stdout;
101 #endif
105 * Setup test output handles
106 * \return zero on success, non-zero on failure
108 static int setup_test_output(libusbx_testlib_ctx * ctx)
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;
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;
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;
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;
140 ctx->output_file = fdopen(ctx->old_stdout, "w");
141 if (!ctx->output_file) {
142 cleanup_test_output(ctx);
143 printf("Failed to open FILE for output handle: %d\n", errno);
144 return 1;
147 #endif
148 return 0;
151 void libusbx_testlib_logf(libusbx_testlib_ctx * ctx,
152 const char* fmt, ...)
154 va_list va;
155 va_start(va, fmt);
156 vfprintf(ctx->output_file, fmt, va);
157 va_end(va);
158 fprintf(ctx->output_file, "\n");
159 fflush(ctx->output_file);
162 int libusbx_testlib_run_tests(int argc,
163 char ** argv,
164 const libusbx_testlib_test * tests)
166 int run_count = 0;
167 int idx = 0;
168 int pass_count = 0;
169 int fail_count = 0;
170 int error_count = 0;
171 int skip_count = 0;
172 int r, j;
173 size_t arglen;
174 libusbx_testlib_result test_result;
175 libusbx_testlib_ctx ctx;
177 /* Setup default mode of operation */
178 ctx.test_names = NULL;
179 ctx.test_count = 0;
180 ctx.list_tests = false;
181 ctx.verbose = false;
182 ctx.old_stdout = INVALID_FD;
183 ctx.old_stderr = INVALID_FD;
184 ctx.output_file = stdout;
185 ctx.null_fd = INVALID_FD;
187 /* Parse command line options */
188 if (argc >= 2) {
189 for (j = 1; j < argc; j++) {
190 arglen = strlen(argv[j]);
191 if ( ((argv[j][0] == '-') || (argv[j][0] == '/')) &&
192 arglen >=2 ) {
193 switch (argv[j][1]) {
194 case 'l':
195 ctx.list_tests = true;
196 break;
197 case 'v':
198 ctx.verbose = true;
199 break;
200 default:
201 printf("Unknown option: '%s'\n", argv[j]);
202 print_usage(argc, argv);
203 return 1;
205 } else {
206 /* End of command line options, remaining must be list of tests to run */
207 ctx.test_names = argv + j;
208 ctx.test_count = argc - j;
209 break;
214 /* Validate command line options */
215 if (ctx.test_names && ctx.list_tests) {
216 printf("List of tests requested but test list provided\n");
217 print_usage(argc, argv);
218 return 1;
221 /* Setup test log output */
222 r = setup_test_output(&ctx);
223 if (r != 0)
224 return r;
226 /* Act on any options not related to running tests */
227 if (ctx.list_tests) {
228 while (tests[idx].function != NULL) {
229 libusbx_testlib_logf(&ctx, tests[idx].name);
230 ++idx;
232 cleanup_test_output(&ctx);
233 return 0;
236 /* Run any requested tests */
237 while (tests[idx].function != NULL) {
238 const libusbx_testlib_test * test = &tests[idx];
239 ++idx;
240 if (ctx.test_count > 0) {
241 /* Filtering tests to run, check if this is one of them */
242 int i;
243 for (i = 0; i < ctx.test_count; ++i) {
244 if (strcmp(ctx.test_names[i], test->name) == 0)
245 /* Matches a requested test name */
246 break;
248 if (i >= ctx.test_count) {
249 /* Failed to find a test match, so do the next loop iteration */
250 continue;
253 libusbx_testlib_logf(&ctx,
254 "Starting test run: %s...", test->name);
255 test_result = test->function(&ctx);
256 libusbx_testlib_logf(&ctx,
257 "%s (%d)",
258 test_result_to_str(test_result), test_result);
259 switch (test_result) {
260 case TEST_STATUS_SUCCESS: pass_count++; break;
261 case TEST_STATUS_FAILURE: fail_count++; break;
262 case TEST_STATUS_ERROR: error_count++; break;
263 case TEST_STATUS_SKIP: skip_count++; break;
265 ++run_count;
267 libusbx_testlib_logf(&ctx, "---");
268 libusbx_testlib_logf(&ctx, "Ran %d tests", run_count);
269 libusbx_testlib_logf(&ctx, "Passed %d tests", pass_count);
270 libusbx_testlib_logf(&ctx, "Failed %d tests", fail_count);
271 libusbx_testlib_logf(&ctx, "Error in %d tests", error_count);
272 libusbx_testlib_logf(&ctx, "Skipped %d tests", skip_count);
274 cleanup_test_output(&ctx);
275 return pass_count != run_count;