1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "runner.h"
27 #include "task.h"
28 #include "uv.h"
29
30 /* Refs: https://github.com/libuv/libuv/issues/4369 */
31 #if defined(__ANDROID__)
32 #include <android/fdsan.h>
33 #endif
34
35 char executable_path[sizeof(executable_path)];
36
37
compare_task(const void * va,const void * vb)38 static int compare_task(const void* va, const void* vb) {
39 const task_entry_t* a = va;
40 const task_entry_t* b = vb;
41 return strcmp(a->task_name, b->task_name);
42 }
43
44
fmt(char (* buf)[32],double d)45 char* fmt(char (*buf)[32], double d) {
46 uint64_t v;
47 char* p;
48
49 p = &(*buf)[32];
50 v = (uint64_t) d;
51
52 *--p = '\0';
53
54 if (v == 0)
55 *--p = '0';
56
57 while (v) {
58 if (v) *--p = '0' + (v % 10), v /= 10;
59 if (v) *--p = '0' + (v % 10), v /= 10;
60 if (v) *--p = '0' + (v % 10), v /= 10;
61 if (v) *--p = ',';
62 }
63
64 return p;
65 }
66
67
run_tests(int benchmark_output)68 int run_tests(int benchmark_output) {
69 int actual;
70 int total;
71 int failed;
72 int current;
73 int test_result;
74 int skip;
75 task_entry_t* task;
76
77 /* Count the number of tests. */
78 actual = 0;
79 total = 0;
80 for (task = TASKS; task->main; task++, actual++) {
81 if (!task->is_helper) {
82 total++;
83 }
84 }
85
86 /* Keep platform_output first. */
87 skip = (actual > 0 && 0 == strcmp(TASKS[0].task_name, "platform_output"));
88 qsort(TASKS + skip, actual - skip, sizeof(TASKS[0]), compare_task);
89
90 fprintf(stdout, "1..%d\n", total);
91 fflush(stdout);
92
93 /* Run all tests. */
94 failed = 0;
95 current = 1;
96 for (task = TASKS; task->main; task++) {
97 if (task->is_helper) {
98 continue;
99 }
100
101 test_result = run_test(task->task_name, benchmark_output, current);
102 switch (test_result) {
103 case TEST_OK: break;
104 case TEST_SKIP: break;
105 default: failed++;
106 }
107 current++;
108 }
109
110 return failed;
111 }
112
113
log_tap_result(int test_count,const char * test,int status,process_info_t * process)114 void log_tap_result(int test_count,
115 const char* test,
116 int status,
117 process_info_t* process) {
118 const char* result;
119 const char* directive;
120 char reason[1024];
121 int reason_length;
122
123 switch (status) {
124 case TEST_OK:
125 result = "ok";
126 directive = "";
127 break;
128 case TEST_SKIP:
129 result = "ok";
130 directive = " # SKIP ";
131 break;
132 default:
133 result = "not ok";
134 directive = "";
135 }
136
137 if (status == TEST_SKIP && process_output_size(process) > 0) {
138 process_read_last_line(process, reason, sizeof reason);
139 reason_length = strlen(reason);
140 if (reason_length > 0 && reason[reason_length - 1] == '\n')
141 reason[reason_length - 1] = '\0';
142 } else {
143 reason[0] = '\0';
144 }
145
146 fprintf(stdout, "%s %d - %s%s%s\n", result, test_count, test, directive, reason);
147 fflush(stdout);
148 }
149
enable_fdsan(void)150 void enable_fdsan(void) {
151 /* Refs: https://github.com/libuv/libuv/issues/4369 */
152 #if defined(__ANDROID__)
153 android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ALWAYS);
154 #endif
155 }
156
157
run_test(const char * test,int benchmark_output,int test_count)158 int run_test(const char* test,
159 int benchmark_output,
160 int test_count) {
161 char errmsg[1024] = "";
162 process_info_t processes[1024];
163 process_info_t *main_proc;
164 task_entry_t* task;
165 int timeout_multiplier;
166 int process_count;
167 int result;
168 int status;
169 int i;
170
171 status = 255;
172 main_proc = NULL;
173 process_count = 0;
174
175 enable_fdsan();
176
177 #ifndef _WIN32
178 /* Clean up stale socket from previous run. */
179 remove(TEST_PIPENAME);
180 remove(TEST_PIPENAME_2);
181 remove(TEST_PIPENAME_3);
182 #endif
183
184 /* If it's a helper the user asks for, start it directly. */
185 for (task = TASKS; task->main; task++) {
186 if (task->is_helper && strcmp(test, task->process_name) == 0) {
187 return task->main();
188 }
189 }
190
191 /* Start the helpers first. */
192 for (task = TASKS; task->main; task++) {
193 if (strcmp(test, task->task_name) != 0) {
194 continue;
195 }
196
197 /* Skip the test itself. */
198 if (!task->is_helper) {
199 continue;
200 }
201
202 if (process_start(task->task_name,
203 task->process_name,
204 &processes[process_count],
205 1 /* is_helper */) == -1) {
206 snprintf(errmsg,
207 sizeof errmsg,
208 "Process `%s` failed to start.",
209 task->process_name);
210 goto out;
211 }
212
213 process_count++;
214 }
215
216 /* Now start the test itself. */
217 for (task = TASKS; task->main; task++) {
218 if (strcmp(test, task->task_name) != 0) {
219 continue;
220 }
221
222 if (task->is_helper) {
223 continue;
224 }
225
226 if (process_start(task->task_name,
227 task->process_name,
228 &processes[process_count],
229 0 /* !is_helper */) == -1) {
230 snprintf(errmsg,
231 sizeof errmsg,
232 "Process `%s` failed to start.",
233 task->process_name);
234 goto out;
235 }
236
237 main_proc = &processes[process_count];
238 process_count++;
239 break;
240 }
241
242 if (main_proc == NULL) {
243 snprintf(errmsg,
244 sizeof errmsg,
245 "No test with that name: %s",
246 test);
247 goto out;
248 }
249
250 timeout_multiplier = 1;
251 #ifndef _WIN32
252 do {
253 const char* var;
254
255 var = getenv("UV_TEST_TIMEOUT_MULTIPLIER");
256 if (var == NULL)
257 break;
258
259 timeout_multiplier = atoi(var);
260 if (timeout_multiplier <= 0)
261 timeout_multiplier = 1;
262 } while (0);
263 #endif
264
265 result = process_wait(main_proc, 1, task->timeout * timeout_multiplier);
266 if (result == -1) {
267 FATAL("process_wait failed");
268 } else if (result == -2) {
269 /* Don't have to clean up the process, process_wait() has killed it. */
270 snprintf(errmsg,
271 sizeof errmsg,
272 "timeout");
273 goto out;
274 }
275
276 status = process_reap(main_proc);
277 if (status != TEST_OK) {
278 snprintf(errmsg,
279 sizeof errmsg,
280 "exit code %d",
281 status);
282 goto out;
283 }
284
285 if (benchmark_output) {
286 /* Give the helpers time to clean up their act. */
287 uv_sleep(1000);
288 }
289
290 out:
291 /* Reap running processes except the main process, it's already dead. */
292 for (i = 0; i < process_count - 1; i++) {
293 process_terminate(&processes[i]);
294 }
295
296 if (process_count > 0 &&
297 process_wait(processes, process_count - 1, -1) < 0) {
298 FATAL("process_wait failed");
299 }
300
301 log_tap_result(test_count, test, status, &processes[i]);
302
303 /* Show error and output from processes if the test failed. */
304 if ((status != TEST_OK && status != TEST_SKIP) || task->show_output) {
305 if (strlen(errmsg) > 0)
306 fprintf(stdout, "# %s\n", errmsg);
307 fprintf(stdout, "# ");
308 fflush(stdout);
309
310 for (i = 0; i < process_count; i++) {
311 switch (process_output_size(&processes[i])) {
312 case -1:
313 fprintf(stdout, "Output from process `%s`: (unavailable)\n",
314 process_get_name(&processes[i]));
315 fflush(stdout);
316 break;
317
318 case 0:
319 fprintf(stdout, "Output from process `%s`: (no output)\n",
320 process_get_name(&processes[i]));
321 fflush(stdout);
322 break;
323
324 default:
325 fprintf(stdout, "Output from process `%s`:\n", process_get_name(&processes[i]));
326 fflush(stdout);
327 process_copy_output(&processes[i], stdout);
328 break;
329 }
330 }
331
332 /* In benchmark mode show concise output from the main process. */
333 } else if (benchmark_output) {
334 switch (process_output_size(main_proc)) {
335 case -1:
336 fprintf(stdout, "%s: (unavailable)\n", test);
337 fflush(stdout);
338 break;
339
340 case 0:
341 fprintf(stdout, "%s: (no output)\n", test);
342 fflush(stdout);
343 break;
344
345 default:
346 for (i = 0; i < process_count; i++) {
347 process_copy_output(&processes[i], stdout);
348 }
349 break;
350 }
351 }
352
353 /* Clean up all process handles. */
354 for (i = 0; i < process_count; i++) {
355 process_cleanup(&processes[i]);
356 }
357
358 return status;
359 }
360
361
362 /* Returns the status code of the task part
363 * or 255 if no matching task was not found.
364 */
run_test_part(const char * test,const char * part)365 int run_test_part(const char* test, const char* part) {
366 task_entry_t* task;
367 int r;
368
369 for (task = TASKS; task->main; task++) {
370 if (strcmp(test, task->task_name) == 0 &&
371 strcmp(part, task->process_name) == 0) {
372 r = task->main();
373 return r;
374 }
375 }
376
377 fprintf(stdout, "No test part with that name: %s:%s\n", test, part);
378 fflush(stdout);
379 return 255;
380 }
381
382
383
find_helpers(const task_entry_t * task,const task_entry_t ** helpers)384 static int find_helpers(const task_entry_t* task,
385 const task_entry_t** helpers) {
386 const task_entry_t* helper;
387 int n_helpers;
388
389 for (n_helpers = 0, helper = TASKS; helper->main; helper++) {
390 if (helper->is_helper && strcmp(helper->task_name, task->task_name) == 0) {
391 *helpers++ = helper;
392 n_helpers++;
393 }
394 }
395
396 return n_helpers;
397 }
398
399
print_tests(FILE * stream)400 void print_tests(FILE* stream) {
401 const task_entry_t* helpers[1024];
402 const task_entry_t* task;
403 int n_helpers;
404 int n_tasks;
405 int i;
406
407 for (n_tasks = 0, task = TASKS; task->main; n_tasks++, task++);
408 qsort(TASKS, n_tasks, sizeof(TASKS[0]), compare_task);
409
410 for (task = TASKS; task->main; task++) {
411 if (task->is_helper) {
412 continue;
413 }
414
415 n_helpers = find_helpers(task, helpers);
416 if (n_helpers) {
417 printf("%-25s (helpers:", task->task_name);
418 for (i = 0; i < n_helpers; i++) {
419 printf(" %s", helpers[i]->process_name);
420 }
421 printf(")\n");
422 } else {
423 printf("%s\n", task->task_name);
424 }
425 }
426 }
427
428
print_lines(const char * buffer,size_t size,FILE * stream)429 void print_lines(const char* buffer, size_t size, FILE* stream) {
430 const char* start;
431 const char* end;
432
433 start = buffer;
434 while ((end = memchr(start, '\n', &buffer[size] - start))) {
435 fputs("# ", stream);
436 fwrite(start, 1, (int)(end - start), stream);
437 fputs("\n", stream);
438 fflush(stream);
439 start = end + 1;
440 }
441
442 end = &buffer[size];
443 if (start < end) {
444 fputs("# ", stream);
445 fwrite(start, 1, (int)(end - start), stream);
446 fputs("\n", stream);
447 fflush(stream);
448 }
449 }
450