xref: /libuv/test/runner.c (revision cb5da592)
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 char executable_path[sizeof(executable_path)];
31 
32 
compare_task(const void * va,const void * vb)33 static int compare_task(const void* va, const void* vb) {
34   const task_entry_t* a = va;
35   const task_entry_t* b = vb;
36   return strcmp(a->task_name, b->task_name);
37 }
38 
39 
fmt(char (* buf)[32],double d)40 char* fmt(char (*buf)[32], double d) {
41   uint64_t v;
42   char* p;
43 
44   p = &(*buf)[32];
45   v = (uint64_t) d;
46 
47   *--p = '\0';
48 
49   if (v == 0)
50     *--p = '0';
51 
52   while (v) {
53     if (v) *--p = '0' + (v % 10), v /= 10;
54     if (v) *--p = '0' + (v % 10), v /= 10;
55     if (v) *--p = '0' + (v % 10), v /= 10;
56     if (v) *--p = ',';
57   }
58 
59   return p;
60 }
61 
62 
run_tests(int benchmark_output)63 int run_tests(int benchmark_output) {
64   int actual;
65   int total;
66   int failed;
67   int current;
68   int test_result;
69   int skip;
70   task_entry_t* task;
71 
72   /* Count the number of tests. */
73   actual = 0;
74   total = 0;
75   for (task = TASKS; task->main; task++, actual++) {
76     if (!task->is_helper) {
77       total++;
78     }
79   }
80 
81   /* Keep platform_output first. */
82   skip = (actual > 0 && 0 == strcmp(TASKS[0].task_name, "platform_output"));
83   qsort(TASKS + skip, actual - skip, sizeof(TASKS[0]), compare_task);
84 
85   fprintf(stdout, "1..%d\n", total);
86   fflush(stdout);
87 
88   /* Run all tests. */
89   failed = 0;
90   current = 1;
91   for (task = TASKS; task->main; task++) {
92     if (task->is_helper) {
93       continue;
94     }
95 
96     test_result = run_test(task->task_name, benchmark_output, current);
97     switch (test_result) {
98     case TEST_OK: break;
99     case TEST_SKIP: break;
100     default: failed++;
101     }
102     current++;
103   }
104 
105   return failed;
106 }
107 
108 
log_tap_result(int test_count,const char * test,int status,process_info_t * process)109 void log_tap_result(int test_count,
110                     const char* test,
111                     int status,
112                     process_info_t* process) {
113   const char* result;
114   const char* directive;
115   char reason[1024];
116   int reason_length;
117 
118   switch (status) {
119   case TEST_OK:
120     result = "ok";
121     directive = "";
122     break;
123   case TEST_SKIP:
124     result = "ok";
125     directive = " # SKIP ";
126     break;
127   default:
128     result = "not ok";
129     directive = "";
130   }
131 
132   if (status == TEST_SKIP && process_output_size(process) > 0) {
133     process_read_last_line(process, reason, sizeof reason);
134     reason_length = strlen(reason);
135     if (reason_length > 0 && reason[reason_length - 1] == '\n')
136       reason[reason_length - 1] = '\0';
137   } else {
138     reason[0] = '\0';
139   }
140 
141   fprintf(stdout, "%s %d - %s%s%s\n", result, test_count, test, directive, reason);
142   fflush(stdout);
143 }
144 
145 
run_test(const char * test,int benchmark_output,int test_count)146 int run_test(const char* test,
147              int benchmark_output,
148              int test_count) {
149   char errmsg[1024] = "";
150   process_info_t processes[1024];
151   process_info_t *main_proc;
152   task_entry_t* task;
153   int timeout_multiplier;
154   int process_count;
155   int result;
156   int status;
157   int i;
158 
159   status = 255;
160   main_proc = NULL;
161   process_count = 0;
162 
163 #ifndef _WIN32
164   /* Clean up stale socket from previous run. */
165   remove(TEST_PIPENAME);
166   remove(TEST_PIPENAME_2);
167   remove(TEST_PIPENAME_3);
168 #endif
169 
170   /* If it's a helper the user asks for, start it directly. */
171   for (task = TASKS; task->main; task++) {
172     if (task->is_helper && strcmp(test, task->process_name) == 0) {
173       return task->main();
174     }
175   }
176 
177   /* Start the helpers first. */
178   for (task = TASKS; task->main; task++) {
179     if (strcmp(test, task->task_name) != 0) {
180       continue;
181     }
182 
183     /* Skip the test itself. */
184     if (!task->is_helper) {
185       continue;
186     }
187 
188     if (process_start(task->task_name,
189                       task->process_name,
190                       &processes[process_count],
191                       1 /* is_helper */) == -1) {
192       snprintf(errmsg,
193                sizeof errmsg,
194                "Process `%s` failed to start.",
195                task->process_name);
196       goto out;
197     }
198 
199     process_count++;
200   }
201 
202   /* Now start the test itself. */
203   for (task = TASKS; task->main; task++) {
204     if (strcmp(test, task->task_name) != 0) {
205       continue;
206     }
207 
208     if (task->is_helper) {
209       continue;
210     }
211 
212     if (process_start(task->task_name,
213                       task->process_name,
214                       &processes[process_count],
215                       0 /* !is_helper */) == -1) {
216       snprintf(errmsg,
217                sizeof errmsg,
218                "Process `%s` failed to start.",
219                task->process_name);
220       goto out;
221     }
222 
223     main_proc = &processes[process_count];
224     process_count++;
225     break;
226   }
227 
228   if (main_proc == NULL) {
229     snprintf(errmsg,
230              sizeof errmsg,
231              "No test with that name: %s",
232              test);
233     goto out;
234   }
235 
236   timeout_multiplier = 1;
237 #ifndef _WIN32
238   do {
239     const char* var;
240 
241     var = getenv("UV_TEST_TIMEOUT_MULTIPLIER");
242     if (var == NULL)
243       break;
244 
245     timeout_multiplier = atoi(var);
246     if (timeout_multiplier <= 0)
247       timeout_multiplier = 1;
248   } while (0);
249 #endif
250 
251   result = process_wait(main_proc, 1, task->timeout * timeout_multiplier);
252   if (result == -1) {
253     FATAL("process_wait failed");
254   } else if (result == -2) {
255     /* Don't have to clean up the process, process_wait() has killed it. */
256     snprintf(errmsg,
257              sizeof errmsg,
258              "timeout");
259     goto out;
260   }
261 
262   status = process_reap(main_proc);
263   if (status != TEST_OK) {
264     snprintf(errmsg,
265              sizeof errmsg,
266              "exit code %d",
267              status);
268     goto out;
269   }
270 
271   if (benchmark_output) {
272     /* Give the helpers time to clean up their act. */
273     uv_sleep(1000);
274   }
275 
276 out:
277   /* Reap running processes except the main process, it's already dead. */
278   for (i = 0; i < process_count - 1; i++) {
279     process_terminate(&processes[i]);
280   }
281 
282   if (process_count > 0 &&
283       process_wait(processes, process_count - 1, -1) < 0) {
284     FATAL("process_wait failed");
285   }
286 
287   log_tap_result(test_count, test, status, &processes[i]);
288 
289   /* Show error and output from processes if the test failed. */
290   if ((status != TEST_OK && status != TEST_SKIP) || task->show_output) {
291     if (strlen(errmsg) > 0)
292       fprintf(stdout, "# %s\n", errmsg);
293     fprintf(stdout, "# ");
294     fflush(stdout);
295 
296     for (i = 0; i < process_count; i++) {
297       switch (process_output_size(&processes[i])) {
298        case -1:
299         fprintf(stdout, "Output from process `%s`: (unavailable)\n",
300                 process_get_name(&processes[i]));
301         fflush(stdout);
302         break;
303 
304        case 0:
305         fprintf(stdout, "Output from process `%s`: (no output)\n",
306                 process_get_name(&processes[i]));
307         fflush(stdout);
308         break;
309 
310        default:
311         fprintf(stdout, "Output from process `%s`:\n", process_get_name(&processes[i]));
312         fflush(stdout);
313         process_copy_output(&processes[i], stdout);
314         break;
315       }
316     }
317 
318   /* In benchmark mode show concise output from the main process. */
319   } else if (benchmark_output) {
320     switch (process_output_size(main_proc)) {
321      case -1:
322       fprintf(stdout, "%s: (unavailable)\n", test);
323       fflush(stdout);
324       break;
325 
326      case 0:
327       fprintf(stdout, "%s: (no output)\n", test);
328       fflush(stdout);
329       break;
330 
331      default:
332       for (i = 0; i < process_count; i++) {
333         process_copy_output(&processes[i], stdout);
334       }
335       break;
336     }
337   }
338 
339   /* Clean up all process handles. */
340   for (i = 0; i < process_count; i++) {
341     process_cleanup(&processes[i]);
342   }
343 
344   return status;
345 }
346 
347 
348 /* Returns the status code of the task part
349  * or 255 if no matching task was not found.
350  */
run_test_part(const char * test,const char * part)351 int run_test_part(const char* test, const char* part) {
352   task_entry_t* task;
353   int r;
354 
355   for (task = TASKS; task->main; task++) {
356     if (strcmp(test, task->task_name) == 0 &&
357         strcmp(part, task->process_name) == 0) {
358       r = task->main();
359       return r;
360     }
361   }
362 
363   fprintf(stdout, "No test part with that name: %s:%s\n", test, part);
364   fflush(stdout);
365   return 255;
366 }
367 
368 
369 
find_helpers(const task_entry_t * task,const task_entry_t ** helpers)370 static int find_helpers(const task_entry_t* task,
371                         const task_entry_t** helpers) {
372   const task_entry_t* helper;
373   int n_helpers;
374 
375   for (n_helpers = 0, helper = TASKS; helper->main; helper++) {
376     if (helper->is_helper && strcmp(helper->task_name, task->task_name) == 0) {
377       *helpers++ = helper;
378       n_helpers++;
379     }
380   }
381 
382   return n_helpers;
383 }
384 
385 
print_tests(FILE * stream)386 void print_tests(FILE* stream) {
387   const task_entry_t* helpers[1024];
388   const task_entry_t* task;
389   int n_helpers;
390   int n_tasks;
391   int i;
392 
393   for (n_tasks = 0, task = TASKS; task->main; n_tasks++, task++);
394   qsort(TASKS, n_tasks, sizeof(TASKS[0]), compare_task);
395 
396   for (task = TASKS; task->main; task++) {
397     if (task->is_helper) {
398       continue;
399     }
400 
401     n_helpers = find_helpers(task, helpers);
402     if (n_helpers) {
403       printf("%-25s (helpers:", task->task_name);
404       for (i = 0; i < n_helpers; i++) {
405         printf(" %s", helpers[i]->process_name);
406       }
407       printf(")\n");
408     } else {
409       printf("%s\n", task->task_name);
410     }
411   }
412 }
413 
414 
print_lines(const char * buffer,size_t size,FILE * stream)415 void print_lines(const char* buffer, size_t size, FILE* stream) {
416   const char* start;
417   const char* end;
418 
419   start = buffer;
420   while ((end = memchr(start, '\n', &buffer[size] - start))) {
421     fputs("# ", stream);
422     fwrite(start, 1, (int)(end - start), stream);
423     fputs("\n", stream);
424     fflush(stream);
425     start = end + 1;
426   }
427 
428   end = &buffer[size];
429   if (start < end) {
430     fputs("# ", stream);
431     fwrite(start, 1, (int)(end - start), stream);
432     fputs("\n", stream);
433     fflush(stream);
434   }
435 }
436