• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }