harness_argparser.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. /* See COPYING.txt for the full license governing this code. */
  2. /**
  3. * \file harness_argparser.c
  4. *
  5. * Source file for functions to parse arguments to the test harness.
  6. */
  7. #include <SDL_test.h>
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include "SDL_visualtest_harness_argparser.h"
  11. #include "SDL_visualtest_rwhelper.h"
  12. /** Maximum length of one line in the config file */
  13. #define MAX_CONFIG_LINE_LEN 400
  14. /** Default value for the timeout after which the SUT is forcefully killed */
  15. #define DEFAULT_SUT_TIMEOUT (60 * 1000)
  16. /* String compare s1 and s2 ignoring leading hyphens */
  17. static int
  18. StrCaseCmpIgnoreHyphen(char* s1, char* s2)
  19. {
  20. /* treat NULL pointer as empty strings */
  21. if(!s1)
  22. s1 = "";
  23. if(!s2)
  24. s2 = "";
  25. while(*s1 == '-')
  26. s1++;
  27. while(*s2 == '-')
  28. s2++;
  29. return SDL_strcasecmp(s1, s2);
  30. }
  31. /* parser an argument, updates the state object and returns the number of
  32. arguments processed; returns -1 on failure */
  33. static int
  34. ParseArg(char** argv, int index, SDLVisualTest_HarnessState* state)
  35. {
  36. if(!argv || !argv[index] || !state)
  37. return 0;
  38. if(StrCaseCmpIgnoreHyphen("sutapp", argv[index]) == 0)
  39. {
  40. index++;
  41. if(!argv[index])
  42. {
  43. SDLTest_LogError("Arguments parsing error: Invalid argument for sutapp.");
  44. return -1;
  45. }
  46. SDL_strlcpy(state->sutapp, argv[index], MAX_PATH_LEN);
  47. SDLTest_Log("SUT Application: %s", state->sutapp);
  48. return 2;
  49. }
  50. else if(StrCaseCmpIgnoreHyphen("output-dir", argv[index]) == 0)
  51. {
  52. index++;
  53. if(!argv[index])
  54. {
  55. SDLTest_LogError("Arguments parsing error: Invalid argument for output-dir.");
  56. return -1;
  57. }
  58. SDL_strlcpy(state->output_dir, argv[index], MAX_PATH_LEN);
  59. SDLTest_Log("Screenshot Output Directory: %s", state->output_dir);
  60. return 2;
  61. }
  62. else if(StrCaseCmpIgnoreHyphen("verify-dir", argv[index]) == 0)
  63. {
  64. index++;
  65. if(!argv[index])
  66. {
  67. SDLTest_LogError("Arguments parsing error: Invalid argument for verify-dir.");
  68. return -1;
  69. }
  70. SDL_strlcpy(state->verify_dir, argv[index], MAX_PATH_LEN);
  71. SDLTest_Log("Screenshot Verification Directory: %s", state->verify_dir);
  72. return 2;
  73. }
  74. else if(StrCaseCmpIgnoreHyphen("sutargs", argv[index]) == 0)
  75. {
  76. index++;
  77. if(!argv[index])
  78. {
  79. SDLTest_LogError("Arguments parsing error: Invalid argument for sutargs.");
  80. return -1;
  81. }
  82. SDL_strlcpy(state->sutargs, argv[index], MAX_SUT_ARGS_LEN);
  83. SDLTest_Log("SUT Arguments: %s", state->sutargs);
  84. return 2;
  85. }
  86. else if(StrCaseCmpIgnoreHyphen("timeout", argv[index]) == 0)
  87. {
  88. int hr, min, sec;
  89. index++;
  90. if(!argv[index] || SDL_sscanf(argv[index], "%d:%d:%d", &hr, &min, &sec) != 3)
  91. {
  92. SDLTest_LogError("Arguments parsing error: Invalid argument for timeout.");
  93. return -1;
  94. }
  95. state->timeout = (((hr * 60) + min) * 60 + sec) * 1000;
  96. SDLTest_Log("Maximum Timeout for each SUT run: %d milliseconds",
  97. state->timeout);
  98. return 2;
  99. }
  100. else if(StrCaseCmpIgnoreHyphen("parameter-config", argv[index]) == 0)
  101. {
  102. index++;
  103. if(!argv[index])
  104. {
  105. SDLTest_LogError("Arguments parsing error: Invalid argument for parameter-config.");
  106. return -1;
  107. }
  108. SDLTest_Log("SUT Parameters file: %s", argv[index]);
  109. SDLVisualTest_FreeSUTConfig(&state->sut_config);
  110. if(!SDLVisualTest_ParseSUTConfig(argv[index], &state->sut_config))
  111. {
  112. SDLTest_LogError("Failed to parse SUT parameters file");
  113. return -1;
  114. }
  115. return 2;
  116. }
  117. else if(StrCaseCmpIgnoreHyphen("variator", argv[index]) == 0)
  118. {
  119. index++;
  120. if(!argv[index])
  121. {
  122. SDLTest_LogError("Arguments parsing error: Invalid argument for variator.");
  123. return -1;
  124. }
  125. SDLTest_Log("Variator: %s", argv[index]);
  126. if(SDL_strcasecmp("exhaustive", argv[index]) == 0)
  127. state->variator_type = SDL_VARIATOR_EXHAUSTIVE;
  128. else if(SDL_strcasecmp("random", argv[index]) == 0)
  129. state->variator_type = SDL_VARIATOR_RANDOM;
  130. else
  131. {
  132. SDLTest_LogError("Arguments parsing error: Invalid variator name.");
  133. return -1;
  134. }
  135. return 2;
  136. }
  137. else if(StrCaseCmpIgnoreHyphen("num-variations", argv[index]) == 0)
  138. {
  139. index++;
  140. if(!argv[index])
  141. {
  142. SDLTest_LogError("Arguments parsing error: Invalid argument for num-variations.");
  143. return -1;
  144. }
  145. state->num_variations = SDL_atoi(argv[index]);
  146. SDLTest_Log("Number of variations to run: %d", state->num_variations);
  147. if(state->num_variations <= 0)
  148. {
  149. SDLTest_LogError("Arguments parsing error: num-variations must be positive.");
  150. return -1;
  151. }
  152. return 2;
  153. }
  154. else if(StrCaseCmpIgnoreHyphen("no-launch", argv[index]) == 0)
  155. {
  156. state->no_launch = SDL_TRUE;
  157. SDLTest_Log("SUT will not be launched.");
  158. return 1;
  159. }
  160. else if(StrCaseCmpIgnoreHyphen("action-config", argv[index]) == 0)
  161. {
  162. index++;
  163. if(!argv[index])
  164. {
  165. SDLTest_LogError("Arguments parsing error: invalid argument for action-config");
  166. return -1;
  167. }
  168. SDLTest_Log("Action Config file: %s", argv[index]);
  169. SDLVisualTest_EmptyActionQueue(&state->action_queue);
  170. if(!SDLVisualTest_ParseActionConfig(argv[index], &state->action_queue))
  171. {
  172. SDLTest_LogError("SDLVisualTest_ParseActionConfig() failed");
  173. return -1;
  174. }
  175. return 2;
  176. }
  177. else if(StrCaseCmpIgnoreHyphen("config", argv[index]) == 0)
  178. {
  179. index++;
  180. if(!argv[index])
  181. {
  182. SDLTest_LogError("Arguments parsing error: invalid argument for config");
  183. return -1;
  184. }
  185. /* do nothing, this option has already been handled */
  186. return 2;
  187. }
  188. return 0;
  189. }
  190. /* TODO: Trailing/leading spaces and spaces between equals sign not supported. */
  191. static int
  192. ParseConfig(char* file, SDLVisualTest_HarnessState* state)
  193. {
  194. SDL_RWops* rw;
  195. SDLVisualTest_RWHelperBuffer buffer;
  196. char line[MAX_CONFIG_LINE_LEN];
  197. rw = SDL_RWFromFile(file, "r");
  198. if(!rw)
  199. {
  200. SDLTest_LogError("SDL_RWFromFile() failed");
  201. return 0;
  202. }
  203. SDLVisualTest_RWHelperResetBuffer(&buffer);
  204. while(SDLVisualTest_RWHelperReadLine(rw, line, MAX_CONFIG_LINE_LEN,
  205. &buffer, '#'))
  206. {
  207. char** argv;
  208. int i, num_params;
  209. /* count number of parameters and replace the trailing newline with 0 */
  210. num_params = 1;
  211. for(i = 0; line[i]; i++)
  212. {
  213. if(line[i] == '=')
  214. {
  215. num_params = 2;
  216. break;
  217. }
  218. }
  219. /* populate argv */
  220. argv = (char**)SDL_malloc((num_params + 1) * sizeof(char*));
  221. if(!argv)
  222. {
  223. SDLTest_LogError("malloc() failed.");
  224. SDL_RWclose(rw);
  225. return 0;
  226. }
  227. argv[num_params] = NULL;
  228. for(i = 0; i < num_params; i++)
  229. {
  230. argv[i] = strtok(i == 0 ? line : NULL, "=");
  231. }
  232. if(ParseArg(argv, 0, state) == -1)
  233. {
  234. SDLTest_LogError("ParseArg() failed");
  235. SDL_free(argv);
  236. SDL_RWclose(rw);
  237. return 0;
  238. }
  239. SDL_free(argv);
  240. }
  241. SDL_RWclose(rw);
  242. if(!state->sutapp[0])
  243. return 0;
  244. return 1;
  245. }
  246. int
  247. SDLVisualTest_ParseHarnessArgs(char** argv, SDLVisualTest_HarnessState* state)
  248. {
  249. int i;
  250. SDLTest_Log("Parsing commandline arguments..");
  251. if(!argv)
  252. {
  253. SDLTest_LogError("argv is NULL");
  254. return 0;
  255. }
  256. if(!state)
  257. {
  258. SDLTest_LogError("state is NULL");
  259. return 0;
  260. }
  261. /* initialize the state object */
  262. state->sutargs[0] = '\0';
  263. state->sutapp[0] = '\0';
  264. state->output_dir[0] = '\0';
  265. state->verify_dir[0] = '\0';
  266. state->timeout = DEFAULT_SUT_TIMEOUT;
  267. SDL_memset(&state->sut_config, 0, sizeof(SDLVisualTest_SUTConfig));
  268. SDL_memset(&state->action_queue, 0, sizeof(SDLVisualTest_ActionQueue));
  269. state->variator_type = SDL_VARIATOR_RANDOM;
  270. state->num_variations = -1;
  271. state->no_launch = SDL_FALSE;
  272. /* parse config file if passed */
  273. for(i = 0; argv[i]; i++)
  274. {
  275. if(StrCaseCmpIgnoreHyphen("config", argv[i]) == 0)
  276. {
  277. if(!argv[i + 1])
  278. {
  279. SDLTest_Log("Arguments parsing error: invalid argument for config.");
  280. return 0;
  281. }
  282. if(!ParseConfig(argv[i + 1], state))
  283. {
  284. SDLTest_LogError("ParseConfig() failed");
  285. return 0;
  286. }
  287. }
  288. }
  289. /* parse the arguments */
  290. for(i = 0; argv[i];)
  291. {
  292. int consumed = ParseArg(argv, i, state);
  293. if(consumed == -1 || consumed == 0)
  294. {
  295. SDLTest_LogError("ParseArg() failed");
  296. return 0;
  297. }
  298. i += consumed;
  299. }
  300. if(state->variator_type == SDL_VARIATOR_RANDOM && state->num_variations == -1)
  301. state->num_variations = 1;
  302. /* check to see if required options have been passed */
  303. if(!state->sutapp[0])
  304. {
  305. SDLTest_LogError("sutapp must be passed.");
  306. return 0;
  307. }
  308. if(!state->sutargs[0] && !state->sut_config.options)
  309. {
  310. SDLTest_LogError("Either sutargs or parameter-config must be passed.");
  311. return 0;
  312. }
  313. if(!state->output_dir[0])
  314. {
  315. SDL_strlcpy(state->output_dir, "./output", MAX_PATH_LEN);
  316. }
  317. if(!state->verify_dir[0])
  318. {
  319. SDL_strlcpy(state->verify_dir, "./verify", MAX_PATH_LEN);
  320. }
  321. return 1;
  322. }
  323. void
  324. SDLVisualTest_FreeHarnessState(SDLVisualTest_HarnessState* state)
  325. {
  326. if(state)
  327. {
  328. SDLVisualTest_EmptyActionQueue(&state->action_queue);
  329. SDLVisualTest_FreeSUTConfig(&state->sut_config);
  330. }
  331. }