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