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