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