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