🛠️🐜 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.

532 lines
15 KiB

  1. /* See LICENSE.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 const char*
  47. usage(void)
  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. SDL_zero(userevent);
  77. userevent.type = SDL_USEREVENT;
  78. userevent.code = ACTION_TIMER_EVENT;
  79. userevent.data1 = &current->action;
  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. SDL_zero(userevent);
  100. userevent.type = SDL_USEREVENT;
  101. userevent.code = KILL_TIMER_EVENT;
  102. event.type = SDL_USEREVENT;
  103. event.user = userevent;
  104. SDL_PushEvent(&event);
  105. kill_timer = 0;
  106. return 0;
  107. }
  108. static int
  109. ProcessAction(SDLVisualTest_Action* action, int* sut_running, char* args)
  110. {
  111. if(!action || !sut_running)
  112. return TEST_ERROR;
  113. switch(action->type)
  114. {
  115. case SDL_ACTION_KILL:
  116. SDLTest_Log("Action: Kill SUT");
  117. if(SDL_IsProcessRunning(&pinfo) == 1 &&
  118. !SDL_KillProcess(&pinfo, &sut_exitstatus))
  119. {
  120. SDLTest_LogError("SDL_KillProcess() failed");
  121. return TEST_ERROR;
  122. }
  123. *sut_running = 0;
  124. break;
  125. case SDL_ACTION_QUIT:
  126. SDLTest_Log("Action: Quit SUT");
  127. if(SDL_IsProcessRunning(&pinfo) == 1 &&
  128. !SDL_QuitProcess(&pinfo, &sut_exitstatus))
  129. {
  130. SDLTest_LogError("SDL_QuitProcess() failed");
  131. return TEST_FAILED;
  132. }
  133. *sut_running = 0;
  134. break;
  135. case SDL_ACTION_LAUNCH:
  136. {
  137. char* path;
  138. char* args;
  139. SDL_ProcessInfo action_process;
  140. SDL_ProcessExitStatus ps;
  141. path = action->extra.process.path;
  142. args = action->extra.process.args;
  143. if(args)
  144. {
  145. SDLTest_Log("Action: Launch process: %s with arguments: %s",
  146. path, args);
  147. }
  148. else
  149. SDLTest_Log("Action: Launch process: %s", path);
  150. if(!SDL_LaunchProcess(path, args, &action_process))
  151. {
  152. SDLTest_LogError("SDL_LaunchProcess() failed");
  153. return TEST_ERROR;
  154. }
  155. /* small delay so that the process can do its job */
  156. SDL_Delay(1000);
  157. if(SDL_IsProcessRunning(&action_process) > 0)
  158. {
  159. SDLTest_LogError("Process %s took too long too complete."
  160. " Force killing...", action->extra.process.path);
  161. if(!SDL_KillProcess(&action_process, &ps))
  162. {
  163. SDLTest_LogError("SDL_KillProcess() failed");
  164. return TEST_ERROR;
  165. }
  166. }
  167. }
  168. break;
  169. case SDL_ACTION_SCREENSHOT:
  170. {
  171. char path[MAX_PATH_LEN], hash[33];
  172. SDLTest_Log("Action: Take screenshot");
  173. /* can't take a screenshot if the SUT isn't running */
  174. if(SDL_IsProcessRunning(&pinfo) != 1)
  175. {
  176. SDLTest_LogError("SUT has quit.");
  177. *sut_running = 0;
  178. return TEST_FAILED;
  179. }
  180. /* file name for the screenshot image */
  181. SDLVisualTest_HashString(args, hash);
  182. SDL_snprintf(path, MAX_PATH_LEN, "%s/%s", state.output_dir, hash);
  183. if(!SDLVisualTest_ScreenshotProcess(&pinfo, path))
  184. {
  185. SDLTest_LogError("SDLVisualTest_ScreenshotProcess() failed");
  186. return TEST_ERROR;
  187. }
  188. }
  189. break;
  190. case SDL_ACTION_VERIFY:
  191. {
  192. int ret;
  193. SDLTest_Log("Action: Verify screenshot");
  194. ret = SDLVisualTest_VerifyScreenshots(args, state.output_dir,
  195. state.verify_dir);
  196. if(ret == -1)
  197. {
  198. SDLTest_LogError("SDLVisualTest_VerifyScreenshots() failed");
  199. return TEST_ERROR;
  200. }
  201. else if(ret == 0)
  202. {
  203. SDLTest_Log("Verification failed: Images were not equal.");
  204. return TEST_FAILED;
  205. }
  206. else if(ret == 1)
  207. SDLTest_Log("Verification successful.");
  208. else
  209. {
  210. SDLTest_Log("Verfication skipped.");
  211. return TEST_FAILED;
  212. }
  213. }
  214. break;
  215. default:
  216. SDLTest_LogError("Invalid action type");
  217. return TEST_ERROR;
  218. break;
  219. }
  220. return TEST_PASSED;
  221. }
  222. static int
  223. RunSUTAndTest(char* sutargs, int variation_num)
  224. {
  225. int success, sut_running, return_code;
  226. char hash[33];
  227. SDL_Event event;
  228. return_code = TEST_PASSED;
  229. if(!sutargs)
  230. {
  231. SDLTest_LogError("sutargs argument cannot be NULL");
  232. return_code = TEST_ERROR;
  233. goto runsutandtest_cleanup_generic;
  234. }
  235. SDLVisualTest_HashString(sutargs, hash);
  236. SDLTest_Log("Hash: %s", hash);
  237. success = SDL_LaunchProcess(state.sutapp, sutargs, &pinfo);
  238. if(!success)
  239. {
  240. SDLTest_Log("Could not launch SUT.");
  241. return_code = TEST_ERROR;
  242. goto runsutandtest_cleanup_generic;
  243. }
  244. SDLTest_Log("SUT launch successful.");
  245. SDLTest_Log("Process will be killed in %d milliseconds", state.timeout);
  246. sut_running = 1;
  247. /* launch the timers */
  248. SDLTest_Log("Performing actions..");
  249. current = state.action_queue.front;
  250. action_timer = 0;
  251. kill_timer = 0;
  252. if(current)
  253. {
  254. action_timer = SDL_AddTimer(current->action.time, ActionTimerCallback, NULL);
  255. if(!action_timer)
  256. {
  257. SDLTest_LogError("SDL_AddTimer() failed");
  258. return_code = TEST_ERROR;
  259. goto runsutandtest_cleanup_timer;
  260. }
  261. }
  262. kill_timer = SDL_AddTimer(state.timeout, KillTimerCallback, NULL);
  263. if(!kill_timer)
  264. {
  265. SDLTest_LogError("SDL_AddTimer() failed");
  266. return_code = TEST_ERROR;
  267. goto runsutandtest_cleanup_timer;
  268. }
  269. /* the timer stops running if the actions queue is empty, and the
  270. SUT stops running if it crashes or if we encounter a KILL/QUIT action */
  271. while(sut_running)
  272. {
  273. /* process the actions by using an event queue */
  274. while(SDL_PollEvent(&event))
  275. {
  276. if(event.type == SDL_USEREVENT)
  277. {
  278. if(event.user.code == ACTION_TIMER_EVENT)
  279. {
  280. SDLVisualTest_Action* action;
  281. action = (SDLVisualTest_Action*)event.user.data1;
  282. switch(ProcessAction(action, &sut_running, sutargs))
  283. {
  284. case TEST_PASSED:
  285. break;
  286. case TEST_FAILED:
  287. return_code = TEST_FAILED;
  288. goto runsutandtest_cleanup_timer;
  289. break;
  290. default:
  291. SDLTest_LogError("ProcessAction() failed");
  292. return_code = TEST_ERROR;
  293. goto runsutandtest_cleanup_timer;
  294. }
  295. }
  296. else if(event.user.code == KILL_TIMER_EVENT)
  297. {
  298. SDLTest_LogError("Maximum timeout reached. Force killing..");
  299. return_code = TEST_FAILED;
  300. goto runsutandtest_cleanup_timer;
  301. }
  302. }
  303. else if(event.type == SDL_QUIT)
  304. {
  305. SDLTest_LogError("Received QUIT event. Testharness is quitting..");
  306. return_code = TEST_ERROR;
  307. goto runsutandtest_cleanup_timer;
  308. }
  309. }
  310. SDL_Delay(1000/ACTION_LOOP_FPS);
  311. }
  312. SDLTest_Log("SUT exit code was: %d", sut_exitstatus.exit_status);
  313. if(sut_exitstatus.exit_status == 0)
  314. {
  315. return_code = TEST_PASSED;
  316. goto runsutandtest_cleanup_timer;
  317. }
  318. else
  319. {
  320. return_code = TEST_FAILED;
  321. goto runsutandtest_cleanup_timer;
  322. }
  323. return_code = TEST_ERROR;
  324. goto runsutandtest_cleanup_generic;
  325. runsutandtest_cleanup_timer:
  326. if(action_timer && !SDL_RemoveTimer(action_timer))
  327. {
  328. SDLTest_Log("SDL_RemoveTimer() failed");
  329. return_code = TEST_ERROR;
  330. }
  331. if(kill_timer && !SDL_RemoveTimer(kill_timer))
  332. {
  333. SDLTest_Log("SDL_RemoveTimer() failed");
  334. return_code = TEST_ERROR;
  335. }
  336. /* runsutandtest_cleanup_process: */
  337. if(SDL_IsProcessRunning(&pinfo) && !SDL_KillProcess(&pinfo, &sut_exitstatus))
  338. {
  339. SDLTest_Log("SDL_KillProcess() failed");
  340. return_code = TEST_ERROR;
  341. }
  342. runsutandtest_cleanup_generic:
  343. return return_code;
  344. }
  345. /** Entry point for testharness */
  346. int
  347. main(int argc, char* argv[])
  348. {
  349. int i, passed, return_code, failed;
  350. /* freeing resources, linux style! */
  351. return_code = 0;
  352. if(argc < 2)
  353. {
  354. SDLTest_Log(usage(), argv[0]);
  355. goto cleanup_generic;
  356. }
  357. #if defined(__LINUX__) || defined(__CYGWIN__)
  358. signal(SIGINT, CtrlCHandlerCallback);
  359. #endif
  360. /* parse arguments */
  361. if(!SDLVisualTest_ParseHarnessArgs(argv + 1, &state))
  362. {
  363. SDLTest_Log(usage(), argv[0]);
  364. return_code = 1;
  365. goto cleanup_generic;
  366. }
  367. SDLTest_Log("Parsed harness arguments successfully.");
  368. /* initialize SDL */
  369. if(SDL_Init(SDL_INIT_TIMER) == -1)
  370. {
  371. SDLTest_LogError("SDL_Init() failed.");
  372. SDLVisualTest_FreeHarnessState(&state);
  373. return_code = 1;
  374. goto cleanup_harness_state;
  375. }
  376. /* create an output directory if none exists */
  377. #if defined(__LINUX__) || defined(__CYGWIN__)
  378. mkdir(state.output_dir, 0777);
  379. #elif defined(__WIN32__)
  380. _mkdir(state.output_dir);
  381. #else
  382. #error "Unsupported platform"
  383. #endif
  384. /* test with sutargs */
  385. if(SDL_strlen(state.sutargs))
  386. {
  387. SDLTest_Log("Running: %s %s", state.sutapp, state.sutargs);
  388. if(!state.no_launch)
  389. {
  390. switch(RunSUTAndTest(state.sutargs, 0))
  391. {
  392. case TEST_PASSED:
  393. SDLTest_Log("Status: PASSED");
  394. break;
  395. case TEST_FAILED:
  396. SDLTest_Log("Status: FAILED");
  397. break;
  398. case TEST_ERROR:
  399. SDLTest_LogError("Some error occurred while testing.");
  400. return_code = 1;
  401. goto cleanup_sdl;
  402. break;
  403. }
  404. }
  405. }
  406. if(state.sut_config.num_options > 0)
  407. {
  408. const char* variator_name = (state.variator_type == SDL_VARIATOR_RANDOM) ?
  409. "RANDOM" : "EXHAUSTIVE";
  410. if(state.num_variations > 0)
  411. SDLTest_Log("Testing SUT with variator: %s for %d variations",
  412. variator_name, state.num_variations);
  413. else
  414. SDLTest_Log("Testing SUT with variator: %s and ALL variations",
  415. variator_name);
  416. /* initialize the variator */
  417. if(!SDLVisualTest_InitVariator(&variator, &state.sut_config,
  418. state.variator_type, 0))
  419. {
  420. SDLTest_LogError("Could not initialize variator");
  421. return_code = 1;
  422. goto cleanup_sdl;
  423. }
  424. /* iterate through all the variations */
  425. passed = 0;
  426. failed = 0;
  427. for(i = 0; state.num_variations > 0 ? (i < state.num_variations) : 1; i++)
  428. {
  429. char* args = SDLVisualTest_GetNextVariation(&variator);
  430. if(!args)
  431. break;
  432. SDLTest_Log("\nVariation number: %d\nArguments: %s", i + 1, args);
  433. if(!state.no_launch)
  434. {
  435. switch(RunSUTAndTest(args, i + 1))
  436. {
  437. case TEST_PASSED:
  438. SDLTest_Log("Status: PASSED");
  439. passed++;
  440. break;
  441. case TEST_FAILED:
  442. SDLTest_Log("Status: FAILED");
  443. failed++;
  444. break;
  445. case TEST_ERROR:
  446. SDLTest_LogError("Some error occurred while testing.");
  447. goto cleanup_variator;
  448. break;
  449. }
  450. }
  451. }
  452. if(!state.no_launch)
  453. {
  454. /* report stats */
  455. SDLTest_Log("Testing complete.");
  456. SDLTest_Log("%d/%d tests passed.", passed, passed + failed);
  457. }
  458. goto cleanup_variator;
  459. }
  460. goto cleanup_sdl;
  461. cleanup_variator:
  462. SDLVisualTest_FreeVariator(&variator);
  463. cleanup_sdl:
  464. SDL_Quit();
  465. cleanup_harness_state:
  466. SDLVisualTest_FreeHarnessState(&state);
  467. cleanup_generic:
  468. return return_code;
  469. }