|
160
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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 */
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
429 int print_lines(const char* buffer, size_t size, FILE* stream, int partial) {
|
|
|
430 const char* start;
|
|
|
431 const char* end;
|
|
|
432
|
|
|
433 start = buffer;
|
|
|
434 while ((end = memchr(start, '\n', &buffer[size] - start))) {
|
|
|
435 if (partial == 0)
|
|
|
436 fputs("# ", stream);
|
|
|
437 else
|
|
|
438 partial = 0;
|
|
|
439
|
|
|
440 fwrite(start, 1, (int)(end - start), stream);
|
|
|
441 fputs("\n", stream);
|
|
|
442 fflush(stream);
|
|
|
443 start = end + 1;
|
|
|
444 }
|
|
|
445
|
|
|
446 end = &buffer[size];
|
|
|
447 if (start < end) {
|
|
|
448 if (partial == 0)
|
|
|
449 fputs("# ", stream);
|
|
|
450
|
|
|
451 fwrite(start, 1, (int)(end - start), stream);
|
|
|
452 fflush(stream);
|
|
|
453 return 1;
|
|
|
454 }
|
|
|
455
|
|
|
456 return 0;
|
|
|
457 }
|