xref: /libuv/test/runner.c (revision a94f2ad2)
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