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