/test/runner.c

http://github.com/joyent/libuv · C · 360 lines · 257 code · 66 blank · 37 comment · 74 complexity · da8329315c4439fe91235290ec2e31cc MD5 · raw file

  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. #include <stdio.h>
  22. #include <string.h>
  23. #include "runner.h"
  24. #include "task.h"
  25. #include "uv.h"
  26. char executable_path[PATHMAX] = { '\0' };
  27. static void log_progress(int total, int passed, int failed, const char* name) {
  28. if (total == 0)
  29. total = 1;
  30. LOGF("[%% %3d|+ %3d|- %3d]: %s", (int) ((passed + failed) / ((double) total) * 100.0),
  31. passed, failed, name);
  32. }
  33. const char* fmt(double d) {
  34. uint64_t v;
  35. char* p;
  36. p = (char *) calloc(1, 32) + 31; /* leaks memory */
  37. v = (uint64_t) d;
  38. #if 0 /* works but we don't care about fractional precision */
  39. if (d - v >= 0.01) {
  40. *--p = '0' + (uint64_t) (d * 100) % 10;
  41. *--p = '0' + (uint64_t) (d * 10) % 10;
  42. *--p = '.';
  43. }
  44. #endif
  45. if (v == 0)
  46. *--p = '0';
  47. while (v) {
  48. if (v) *--p = '0' + (v % 10), v /= 10;
  49. if (v) *--p = '0' + (v % 10), v /= 10;
  50. if (v) *--p = '0' + (v % 10), v /= 10;
  51. if (v) *--p = ',';
  52. }
  53. return p;
  54. }
  55. int run_tests(int timeout, int benchmark_output) {
  56. int total, passed, failed;
  57. task_entry_t* task;
  58. /* Count the number of tests. */
  59. total = 0;
  60. for (task = TASKS; task->main; task++) {
  61. if (!task->is_helper) {
  62. total++;
  63. }
  64. }
  65. /* Run all tests. */
  66. passed = 0;
  67. failed = 0;
  68. for (task = TASKS; task->main; task++) {
  69. if (task->is_helper) {
  70. continue;
  71. }
  72. rewind_cursor();
  73. if (!benchmark_output) {
  74. log_progress(total, passed, failed, task->task_name);
  75. }
  76. if (run_test(task->task_name, timeout, benchmark_output) == 0) {
  77. passed++;
  78. } else {
  79. failed++;
  80. }
  81. }
  82. rewind_cursor();
  83. if (!benchmark_output) {
  84. log_progress(total, passed, failed, "Done.\n");
  85. }
  86. return failed;
  87. }
  88. int run_test(const char* test, int timeout, int benchmark_output) {
  89. char errmsg[1024] = "no error";
  90. process_info_t processes[1024];
  91. process_info_t *main_proc;
  92. task_entry_t* task;
  93. int process_count;
  94. int result;
  95. int status;
  96. int i;
  97. status = 255;
  98. main_proc = NULL;
  99. process_count = 0;
  100. #ifndef _WIN32
  101. /* Clean up stale socket from previous run. */
  102. remove(TEST_PIPENAME);
  103. #endif
  104. /* If it's a helper the user asks for, start it directly. */
  105. for (task = TASKS; task->main; task++) {
  106. if (task->is_helper && strcmp(test, task->process_name) == 0) {
  107. return task->main();
  108. }
  109. }
  110. /* Start the helpers first. */
  111. for (task = TASKS; task->main; task++) {
  112. if (strcmp(test, task->task_name) != 0) {
  113. continue;
  114. }
  115. /* Skip the test itself. */
  116. if (!task->is_helper) {
  117. continue;
  118. }
  119. if (process_start(task->task_name,
  120. task->process_name,
  121. &processes[process_count],
  122. 1 /* is_helper */) == -1) {
  123. snprintf(errmsg,
  124. sizeof errmsg,
  125. "Process `%s` failed to start.",
  126. task->process_name);
  127. goto out;
  128. }
  129. process_count++;
  130. }
  131. /* Give the helpers time to settle. Race-y, fix this. */
  132. uv_sleep(250);
  133. /* Now start the test itself. */
  134. for (task = TASKS; task->main; task++) {
  135. if (strcmp(test, task->task_name) != 0) {
  136. continue;
  137. }
  138. if (task->is_helper) {
  139. continue;
  140. }
  141. if (process_start(task->task_name,
  142. task->process_name,
  143. &processes[process_count],
  144. 0 /* !is_helper */) == -1) {
  145. snprintf(errmsg,
  146. sizeof errmsg,
  147. "Process `%s` failed to start.",
  148. task->process_name);
  149. goto out;
  150. }
  151. main_proc = &processes[process_count];
  152. process_count++;
  153. break;
  154. }
  155. if (main_proc == NULL) {
  156. snprintf(errmsg,
  157. sizeof errmsg,
  158. "No test with that name: %s",
  159. test);
  160. goto out;
  161. }
  162. result = process_wait(main_proc, 1, timeout);
  163. if (result == -1) {
  164. FATAL("process_wait failed");
  165. } else if (result == -2) {
  166. /* Don't have to clean up the process, process_wait() has killed it. */
  167. snprintf(errmsg,
  168. sizeof errmsg,
  169. "timeout");
  170. goto out;
  171. }
  172. status = process_reap(main_proc);
  173. if (status != 0) {
  174. snprintf(errmsg,
  175. sizeof errmsg,
  176. "exit code %d",
  177. status);
  178. goto out;
  179. }
  180. if (benchmark_output) {
  181. /* Give the helpers time to clean up their act. */
  182. uv_sleep(1000);
  183. }
  184. out:
  185. /* Reap running processes except the main process, it's already dead. */
  186. for (i = 0; i < process_count - 1; i++) {
  187. process_terminate(&processes[i]);
  188. }
  189. if (process_count > 0 &&
  190. process_wait(processes, process_count - 1, -1) < 0) {
  191. FATAL("process_wait failed");
  192. }
  193. /* Show error and output from processes if the test failed. */
  194. if (status != 0 || task->show_output) {
  195. if (status != 0) {
  196. LOGF("\n`%s` failed: %s\n", test, errmsg);
  197. } else {
  198. LOGF("\n");
  199. }
  200. for (i = 0; i < process_count; i++) {
  201. switch (process_output_size(&processes[i])) {
  202. case -1:
  203. LOGF("Output from process `%s`: (unavailable)\n",
  204. process_get_name(&processes[i]));
  205. break;
  206. case 0:
  207. LOGF("Output from process `%s`: (no output)\n",
  208. process_get_name(&processes[i]));
  209. break;
  210. default:
  211. LOGF("Output from process `%s`:\n", process_get_name(&processes[i]));
  212. process_copy_output(&processes[i], fileno(stderr));
  213. break;
  214. }
  215. }
  216. LOG("=============================================================\n");
  217. /* In benchmark mode show concise output from the main process. */
  218. } else if (benchmark_output) {
  219. switch (process_output_size(main_proc)) {
  220. case -1:
  221. LOGF("%s: (unavailable)\n", test);
  222. break;
  223. case 0:
  224. LOGF("%s: (no output)\n", test);
  225. break;
  226. default:
  227. for (i = 0; i < process_count; i++) {
  228. process_copy_output(&processes[i], fileno(stderr));
  229. }
  230. break;
  231. }
  232. }
  233. /* Clean up all process handles. */
  234. for (i = 0; i < process_count; i++) {
  235. process_cleanup(&processes[i]);
  236. }
  237. return status;
  238. }
  239. /* Returns the status code of the task part
  240. * or 255 if no matching task was not found.
  241. */
  242. int run_test_part(const char* test, const char* part) {
  243. task_entry_t* task;
  244. int r;
  245. for (task = TASKS; task->main; task++) {
  246. if (strcmp(test, task->task_name) == 0 &&
  247. strcmp(part, task->process_name) == 0) {
  248. r = task->main();
  249. return r;
  250. }
  251. }
  252. LOGF("No test part with that name: %s:%s\n", test, part);
  253. return 255;
  254. }
  255. static int compare_task(const void* va, const void* vb) {
  256. const task_entry_t* a = va;
  257. const task_entry_t* b = vb;
  258. return strcmp(a->task_name, b->task_name);
  259. }
  260. static int find_helpers(const task_entry_t* task, const task_entry_t** helpers) {
  261. const task_entry_t* helper;
  262. int n_helpers;
  263. for (n_helpers = 0, helper = TASKS; helper->main; helper++) {
  264. if (helper->is_helper && strcmp(helper->task_name, task->task_name) == 0) {
  265. *helpers++ = helper;
  266. n_helpers++;
  267. }
  268. }
  269. return n_helpers;
  270. }
  271. void print_tests(FILE* stream) {
  272. const task_entry_t* helpers[1024];
  273. const task_entry_t* task;
  274. int n_helpers;
  275. int n_tasks;
  276. int i;
  277. for (n_tasks = 0, task = TASKS; task->main; n_tasks++, task++);
  278. qsort(TASKS, n_tasks, sizeof(TASKS[0]), compare_task);
  279. for (task = TASKS; task->main; task++) {
  280. if (task->is_helper) {
  281. continue;
  282. }
  283. n_helpers = find_helpers(task, helpers);
  284. if (n_helpers) {
  285. printf("%-25s (helpers:", task->task_name);
  286. for (i = 0; i < n_helpers; i++) {
  287. printf(" %s", helpers[i]->process_name);
  288. }
  289. printf(")\n");
  290. } else {
  291. printf("%s\n", task->task_name);
  292. }
  293. }
  294. }