🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

533 lines
15 KiB

  1. /* See COPYING.txt for the full license governing this code. */
  2. /**
  3. * \file testharness.c
  4. *
  5. * Source file for the test harness.
  6. */
  7. #include <stdlib.h>
  8. #include <SDL_test.h>
  9. #include <SDL.h>
  10. #include <SDL_assert.h>
  11. #include "SDL_visualtest_harness_argparser.h"
  12. #include "SDL_visualtest_process.h"
  13. #include "SDL_visualtest_variators.h"
  14. #include "SDL_visualtest_screenshot.h"
  15. #include "SDL_visualtest_mischelper.h"
  16. #if defined(__WIN32__) && !defined(__CYGWIN__)
  17. #include <direct.h>
  18. #elif defined(__WIN32__) && defined(__CYGWIN__)
  19. #include <signal.h>
  20. #elif defined(__LINUX__)
  21. #include <sys/stat.h>
  22. #include <sys/types.h>
  23. #include <signal.h>
  24. #else
  25. #error "Unsupported platform"
  26. #endif
  27. /** Code for the user event triggered when a new action is to be executed */
  28. #define ACTION_TIMER_EVENT 0
  29. /** Code for the user event triggered when the maximum timeout is reached */
  30. #define KILL_TIMER_EVENT 1
  31. /** FPS value used for delays in the action loop */
  32. #define ACTION_LOOP_FPS 10
  33. /** Value returned by RunSUTAndTest() when the test has passed */
  34. #define TEST_PASSED 1
  35. /** Value returned by RunSUTAndTest() when the test has failed */
  36. #define TEST_FAILED 0
  37. /** Value returned by RunSUTAndTest() on a fatal error */
  38. #define TEST_ERROR -1
  39. static SDL_ProcessInfo pinfo;
  40. static SDL_ProcessExitStatus sut_exitstatus;
  41. static SDLVisualTest_HarnessState state;
  42. static SDLVisualTest_Variator variator;
  43. static SDLVisualTest_ActionNode* current; /* the current action being performed */
  44. static SDL_TimerID action_timer, kill_timer;
  45. /* returns a char* to be passed as the format argument of a printf-style function. */
  46. static char*
  47. usage()
  48. {
  49. return "Usage: \n%s --sutapp xyz"
  50. " [--sutargs abc | --parameter-config xyz.parameters"
  51. " [--variator exhaustive|random]"
  52. " [--num-variations N] [--no-launch]] [--timeout hh:mm:ss]"
  53. " [--action-config xyz.actions]"
  54. " [--output-dir /path/to/output]"
  55. " [--verify-dir /path/to/verify]"
  56. " or --config app.config";
  57. }
  58. /* register Ctrl+C handlers */
  59. #if defined(__LINUX__) || defined(__CYGWIN__)
  60. static void
  61. CtrlCHandlerCallback(int signum)
  62. {
  63. SDL_Event event;
  64. SDLTest_Log("Ctrl+C received");
  65. event.type = SDL_QUIT;
  66. SDL_PushEvent(&event);
  67. }
  68. #endif
  69. static Uint32
  70. ActionTimerCallback(Uint32 interval, void* param)
  71. {
  72. SDL_Event event;
  73. SDL_UserEvent userevent;
  74. Uint32 next_action_time;
  75. /* push an event to handle the action */
  76. userevent.type = SDL_USEREVENT;
  77. userevent.code = ACTION_TIMER_EVENT;
  78. userevent.data1 = &current->action;
  79. userevent.data2 = NULL;
  80. event.type = SDL_USEREVENT;
  81. event.user = userevent;
  82. SDL_PushEvent(&event);
  83. /* calculate the new interval and return it */
  84. if(current->next)
  85. next_action_time = current->next->action.time - current->action.time;
  86. else
  87. {
  88. next_action_time = 0;
  89. action_timer = 0;
  90. }
  91. current = current->next;
  92. return next_action_time;
  93. }
  94. static Uint32
  95. KillTimerCallback(Uint32 interval, void* param)
  96. {
  97. SDL_Event event;
  98. SDL_UserEvent userevent;
  99. userevent.type = SDL_USEREVENT;
  100. userevent.code = KILL_TIMER_EVENT;
  101. userevent.data1 = NULL;
  102. userevent.data2 = NULL;
  103. event.type = SDL_USEREVENT;
  104. event.user = userevent;
  105. SDL_PushEvent(&event);
  106. kill_timer = 0;
  107. return 0;
  108. }
  109. static int
  110. ProcessAction(SDLVisualTest_Action* action, int* sut_running, char* args)
  111. {
  112. if(!action || !sut_running)
  113. return TEST_ERROR;
  114. switch(action->type)
  115. {
  116. case SDL_ACTION_KILL:
  117. SDLTest_Log("Action: Kill SUT");
  118. if(SDL_IsProcessRunning(&pinfo) == 1 &&
  119. !SDL_KillProcess(&pinfo, &sut_exitstatus))
  120. {
  121. SDLTest_LogError("SDL_KillProcess() failed");
  122. return TEST_ERROR;
  123. }
  124. *sut_running = 0;
  125. break;
  126. case SDL_ACTION_QUIT:
  127. SDLTest_Log("Action: Quit SUT");
  128. if(SDL_IsProcessRunning(&pinfo) == 1 &&
  129. !SDL_QuitProcess(&pinfo, &sut_exitstatus))
  130. {
  131. SDLTest_LogError("SDL_QuitProcess() failed");
  132. return TEST_FAILED;
  133. }
  134. *sut_running = 0;
  135. break;
  136. case SDL_ACTION_LAUNCH:
  137. {
  138. char* path;
  139. char* args;
  140. SDL_ProcessInfo action_process;
  141. SDL_ProcessExitStatus ps;
  142. path = action->extra.process.path;
  143. args = action->extra.process.args;
  144. if(args)
  145. {
  146. SDLTest_Log("Action: Launch process: %s with arguments: %s",
  147. path, args);
  148. }
  149. else
  150. SDLTest_Log("Action: Launch process: %s", path);
  151. if(!SDL_LaunchProcess(path, args, &action_process))
  152. {
  153. SDLTest_LogError("SDL_LaunchProcess() failed");
  154. return TEST_ERROR;
  155. }
  156. /* small delay so that the process can do its job */
  157. SDL_Delay(1000);
  158. if(SDL_IsProcessRunning(&action_process) > 0)
  159. {
  160. SDLTest_LogError("Process %s took too long too complete."
  161. " Force killing...", action->extra);
  162. if(!SDL_KillProcess(&action_process, &ps))
  163. {
  164. SDLTest_LogError("SDL_KillProcess() failed");
  165. return TEST_ERROR;
  166. }
  167. }
  168. }
  169. break;
  170. case SDL_ACTION_SCREENSHOT:
  171. {
  172. char path[MAX_PATH_LEN], hash[33];
  173. SDLTest_Log("Action: Take screenshot");
  174. /* can't take a screenshot if the SUT isn't running */
  175. if(SDL_IsProcessRunning(&pinfo) != 1)
  176. {
  177. SDLTest_LogError("SUT has quit.");
  178. *sut_running = 0;
  179. return TEST_FAILED;
  180. }
  181. /* file name for the screenshot image */
  182. SDLVisualTest_HashString(args, hash);
  183. SDL_snprintf(path, MAX_PATH_LEN, "%s/%s", state.output_dir, hash);
  184. if(!SDLVisualTest_ScreenshotProcess(&pinfo, path))
  185. {
  186. SDLTest_LogError("SDLVisualTest_ScreenshotProcess() failed");
  187. return TEST_ERROR;
  188. }
  189. }
  190. break;
  191. case SDL_ACTION_VERIFY:
  192. {
  193. int ret;
  194. SDLTest_Log("Action: Verify screenshot");
  195. ret = SDLVisualTest_VerifyScreenshots(args, state.output_dir,
  196. state.verify_dir);
  197. if(ret == -1)
  198. {
  199. SDLTest_LogError("SDLVisualTest_VerifyScreenshots() failed");
  200. return TEST_ERROR;
  201. }
  202. else if(ret == 0)
  203. {
  204. SDLTest_Log("Verification failed: Images were not equal.");
  205. return TEST_FAILED;
  206. }
  207. else if(ret == 1)
  208. SDLTest_Log("Verification successful.");
  209. else
  210. {
  211. SDLTest_Log("Verfication skipped.");
  212. return TEST_FAILED;
  213. }
  214. }
  215. break;
  216. default:
  217. SDLTest_LogError("Invalid action type");
  218. return TEST_ERROR;
  219. break;
  220. }
  221. return TEST_PASSED;
  222. }
  223. static int
  224. RunSUTAndTest(char* sutargs, int variation_num)
  225. {
  226. int success, sut_running, return_code;
  227. char hash[33];
  228. SDL_Event event;
  229. return_code = TEST_PASSED;
  230. if(!sutargs)
  231. {
  232. SDLTest_LogError("sutargs argument cannot be NULL");
  233. return_code = TEST_ERROR;
  234. goto runsutandtest_cleanup_generic;
  235. }
  236. SDLVisualTest_HashString(sutargs, hash);
  237. SDLTest_Log("Hash: %s", hash);
  238. success = SDL_LaunchProcess(state.sutapp, sutargs, &pinfo);
  239. if(!success)
  240. {
  241. SDLTest_Log("Could not launch SUT.");
  242. return_code = TEST_ERROR;
  243. goto runsutandtest_cleanup_generic;
  244. }
  245. SDLTest_Log("SUT launch successful.");
  246. SDLTest_Log("Process will be killed in %d milliseconds", state.timeout);
  247. sut_running = 1;
  248. /* launch the timers */
  249. SDLTest_Log("Performing actions..");
  250. current = state.action_queue.front;
  251. action_timer = 0;
  252. kill_timer = 0;
  253. if(current)
  254. {
  255. action_timer = SDL_AddTimer(current->action.time, ActionTimerCallback, NULL);
  256. if(!action_timer)
  257. {
  258. SDLTest_LogError("SDL_AddTimer() failed");
  259. return_code = TEST_ERROR;
  260. goto runsutandtest_cleanup_timer;
  261. }
  262. }
  263. kill_timer = SDL_AddTimer(state.timeout, KillTimerCallback, NULL);
  264. if(!kill_timer)
  265. {
  266. SDLTest_LogError("SDL_AddTimer() failed");
  267. return_code = TEST_ERROR;
  268. goto runsutandtest_cleanup_timer;
  269. }
  270. /* the timer stops running if the actions queue is empty, and the
  271. SUT stops running if it crashes or if we encounter a KILL/QUIT action */
  272. while(sut_running)
  273. {
  274. /* process the actions by using an event queue */
  275. while(SDL_PollEvent(&event))
  276. {
  277. if(event.type == SDL_USEREVENT)
  278. {
  279. if(event.user.code == ACTION_TIMER_EVENT)
  280. {
  281. SDLVisualTest_Action* action;
  282. action = (SDLVisualTest_Action*)event.user.data1;
  283. switch(ProcessAction(action, &sut_running, sutargs))
  284. {
  285. case TEST_PASSED:
  286. break;
  287. case TEST_FAILED:
  288. return_code = TEST_FAILED;
  289. goto runsutandtest_cleanup_timer;
  290. break;
  291. default:
  292. SDLTest_LogError("ProcessAction() failed");
  293. return_code = TEST_ERROR;
  294. goto runsutandtest_cleanup_timer;
  295. }
  296. }
  297. else if(event.user.code == KILL_TIMER_EVENT)
  298. {
  299. SDLTest_LogError("Maximum timeout reached. Force killing..");
  300. return_code = TEST_FAILED;
  301. goto runsutandtest_cleanup_timer;
  302. }
  303. }
  304. else if(event.type == SDL_QUIT)
  305. {
  306. SDLTest_LogError("Received QUIT event. Testharness is quitting..");
  307. return_code = TEST_ERROR;
  308. goto runsutandtest_cleanup_timer;
  309. }
  310. }
  311. SDL_Delay(1000/ACTION_LOOP_FPS);
  312. }
  313. SDLTest_Log("SUT exit code was: %d", sut_exitstatus.exit_status);
  314. if(sut_exitstatus.exit_status == 0)
  315. {
  316. return_code = TEST_PASSED;
  317. goto runsutandtest_cleanup_timer;
  318. }
  319. else
  320. {
  321. return_code = TEST_FAILED;
  322. goto runsutandtest_cleanup_timer;
  323. }
  324. return_code = TEST_ERROR;
  325. goto runsutandtest_cleanup_generic;
  326. runsutandtest_cleanup_timer:
  327. if(action_timer && !SDL_RemoveTimer(action_timer))
  328. {
  329. SDLTest_Log("SDL_RemoveTimer() failed");
  330. return_code = TEST_ERROR;
  331. }
  332. if(kill_timer && !SDL_RemoveTimer(kill_timer))
  333. {
  334. SDLTest_Log("SDL_RemoveTimer() failed");
  335. return_code = TEST_ERROR;
  336. }
  337. /* runsutandtest_cleanup_process: */
  338. if(SDL_IsProcessRunning(&pinfo) && !SDL_KillProcess(&pinfo, &sut_exitstatus))
  339. {
  340. SDLTest_Log("SDL_KillProcess() failed");
  341. return_code = TEST_ERROR;
  342. }
  343. runsutandtest_cleanup_generic:
  344. return return_code;
  345. }
  346. /** Entry point for testharness */
  347. int
  348. main(int argc, char* argv[])
  349. {
  350. int i, passed, return_code, failed;
  351. /* freeing resources, linux style! */
  352. return_code = 0;
  353. if(argc < 2)
  354. {
  355. SDLTest_Log(usage(), argv[0]);
  356. goto cleanup_generic;
  357. }
  358. #if defined(__LINUX__) || defined(__CYGWIN__)
  359. signal(SIGINT, CtrlCHandlerCallback);
  360. #endif
  361. /* parse arguments */
  362. if(!SDLVisualTest_ParseHarnessArgs(argv + 1, &state))
  363. {
  364. SDLTest_Log(usage(), argv[0]);
  365. return_code = 1;
  366. goto cleanup_generic;
  367. }
  368. SDLTest_Log("Parsed harness arguments successfully.");
  369. /* initialize SDL */
  370. if(SDL_Init(SDL_INIT_TIMER) == -1)
  371. {
  372. SDLTest_LogError("SDL_Init() failed.");
  373. SDLVisualTest_FreeHarnessState(&state);
  374. return_code = 1;
  375. goto cleanup_harness_state;
  376. }
  377. /* create an output directory if none exists */
  378. #if defined(__LINUX__) || defined(__CYGWIN__)
  379. mkdir(state.output_dir, 0777);
  380. #elif defined(__WIN32__)
  381. _mkdir(state.output_dir);
  382. #else
  383. #error "Unsupported platform"
  384. #endif
  385. /* test with sutargs */
  386. if(SDL_strlen(state.sutargs))
  387. {
  388. SDLTest_Log("Running: %s %s", state.sutapp, state.sutargs);
  389. if(!state.no_launch)
  390. {
  391. switch(RunSUTAndTest(state.sutargs, 0))
  392. {
  393. case TEST_PASSED:
  394. SDLTest_Log("Status: PASSED");
  395. break;
  396. case TEST_FAILED:
  397. SDLTest_Log("Status: FAILED");
  398. break;
  399. case TEST_ERROR:
  400. SDLTest_LogError("Some error occurred while testing.");
  401. return_code = 1;
  402. goto cleanup_sdl;
  403. break;
  404. }
  405. }
  406. }
  407. if(state.sut_config.num_options > 0)
  408. {
  409. char* variator_name = state.variator_type == SDL_VARIATOR_RANDOM ?
  410. "RANDOM" : "EXHAUSTIVE";
  411. if(state.num_variations > 0)
  412. SDLTest_Log("Testing SUT with variator: %s for %d variations",
  413. variator_name, state.num_variations);
  414. else
  415. SDLTest_Log("Testing SUT with variator: %s and ALL variations",
  416. variator_name);
  417. /* initialize the variator */
  418. if(!SDLVisualTest_InitVariator(&variator, &state.sut_config,
  419. state.variator_type, 0))
  420. {
  421. SDLTest_LogError("Could not initialize variator");
  422. return_code = 1;
  423. goto cleanup_sdl;
  424. }
  425. /* iterate through all the variations */
  426. passed = 0;
  427. failed = 0;
  428. for(i = 0; state.num_variations > 0 ? (i < state.num_variations) : 1; i++)
  429. {
  430. char* args = SDLVisualTest_GetNextVariation(&variator);
  431. if(!args)
  432. break;
  433. SDLTest_Log("\nVariation number: %d\nArguments: %s", i + 1, args);
  434. if(!state.no_launch)
  435. {
  436. switch(RunSUTAndTest(args, i + 1))
  437. {
  438. case TEST_PASSED:
  439. SDLTest_Log("Status: PASSED");
  440. passed++;
  441. break;
  442. case TEST_FAILED:
  443. SDLTest_Log("Status: FAILED");
  444. failed++;
  445. break;
  446. case TEST_ERROR:
  447. SDLTest_LogError("Some error occurred while testing.");
  448. goto cleanup_variator;
  449. break;
  450. }
  451. }
  452. }
  453. if(!state.no_launch)
  454. {
  455. /* report stats */
  456. SDLTest_Log("Testing complete.");
  457. SDLTest_Log("%d/%d tests passed.", passed, passed + failed);
  458. }
  459. goto cleanup_variator;
  460. }
  461. goto cleanup_sdl;
  462. cleanup_variator:
  463. SDLVisualTest_FreeVariator(&variator);
  464. cleanup_sdl:
  465. SDL_Quit();
  466. cleanup_harness_state:
  467. SDLVisualTest_FreeHarnessState(&state);
  468. cleanup_generic:
  469. return return_code;
  470. }