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