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 | |
29 | char executable_path[sizeof(executable_path)]; |
30 | |
31 | |
32 | static 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 | |
39 | const 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 | |
76 | int 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 | |
126 | void 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 | |
163 | int 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 | |
277 | out: |
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 | */ |
352 | int 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 | |
371 | static 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 | |
387 | void 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 | |
416 | void 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 | |