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
30char executable_path[sizeof(executable_path)];
31
32
33static 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
40const 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
77int 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
127void 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
164int 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
294out:
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 */
369int 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
388static 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
404void 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
433void 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