Edit

kc3-lang/SDL/visualtest/src/harness_argparser.c

Branch :

  • Show log

    Commit

  • Author : Sylvain
    Date : 2021-11-22 16:22:39
    Hash : 5699e0fa
    Message : visualtest: use SDL_malloc

  • visualtest/src/harness_argparser.c
  • /* See LICENSE.txt for the full license governing this code. */
    /**
     * \file harness_argparser.c
     *
     * Source file for functions to parse arguments to the test harness.
     */
    
    #include <SDL_test.h>
    #include <stdio.h>
    #include <string.h>
    
    #include "SDL_visualtest_harness_argparser.h"
    #include "SDL_visualtest_rwhelper.h"
    
    /** Maximum length of one line in the config file */
    #define MAX_CONFIG_LINE_LEN 400
    /** Default value for the timeout after which the SUT is forcefully killed */
    #define DEFAULT_SUT_TIMEOUT (60 * 1000)
    
    /* String compare s1 and s2 ignoring leading hyphens */
    static int
    StrCaseCmpIgnoreHyphen(char* s1, char* s2)
    {
        /* treat NULL pointer as empty strings */
        if(!s1)
            s1 = "";
        if(!s2)
            s2 = "";
    
        while(*s1 == '-')
            s1++;
        while(*s2 == '-')
            s2++;
    
        return SDL_strcasecmp(s1, s2);
    }
    
    /* parser an argument, updates the state object and returns the number of
       arguments processed; returns -1 on failure */
    static int
    ParseArg(char** argv, int index, SDLVisualTest_HarnessState* state)
    {
        if(!argv || !argv[index] || !state)
            return 0;
    
        if(StrCaseCmpIgnoreHyphen("sutapp", argv[index]) == 0)
        {
            index++;
            if(!argv[index])
            {
                SDLTest_LogError("Arguments parsing error: Invalid argument for sutapp.");
                return -1;
            }
            SDL_strlcpy(state->sutapp, argv[index], MAX_PATH_LEN);
            SDLTest_Log("SUT Application: %s", state->sutapp);
            return 2;
        }
        else if(StrCaseCmpIgnoreHyphen("output-dir", argv[index]) == 0)
        {
            index++;
            if(!argv[index])
            {
                SDLTest_LogError("Arguments parsing error: Invalid argument for output-dir.");
                return -1;
            }
            SDL_strlcpy(state->output_dir, argv[index], MAX_PATH_LEN);
            SDLTest_Log("Screenshot Output Directory: %s", state->output_dir);
            return 2;
        }
        else if(StrCaseCmpIgnoreHyphen("verify-dir", argv[index]) == 0)
        {
            index++;
            if(!argv[index])
            {
                SDLTest_LogError("Arguments parsing error: Invalid argument for verify-dir.");
                return -1;
            }
            SDL_strlcpy(state->verify_dir, argv[index], MAX_PATH_LEN);
            SDLTest_Log("Screenshot Verification Directory: %s", state->verify_dir);
            return 2;
        }
        else if(StrCaseCmpIgnoreHyphen("sutargs", argv[index]) == 0)
        {
            index++;
            if(!argv[index])
            {
                SDLTest_LogError("Arguments parsing error: Invalid argument for sutargs.");
                return -1;
            }
            SDL_strlcpy(state->sutargs, argv[index], MAX_SUT_ARGS_LEN);
            SDLTest_Log("SUT Arguments: %s", state->sutargs);
            return 2;
        }
        else if(StrCaseCmpIgnoreHyphen("timeout", argv[index]) == 0)
        {
            int hr, min, sec;
            index++;
            if(!argv[index] || SDL_sscanf(argv[index], "%d:%d:%d", &hr, &min, &sec) != 3)
            {
                SDLTest_LogError("Arguments parsing error: Invalid argument for timeout.");
                return -1;
            }
            state->timeout = (((hr * 60) + min) * 60 + sec) * 1000;
            SDLTest_Log("Maximum Timeout for each SUT run: %d milliseconds",
                        state->timeout);
            return 2;
        }
        else if(StrCaseCmpIgnoreHyphen("parameter-config", argv[index]) == 0)
        {
            index++;
            if(!argv[index])
            {
                SDLTest_LogError("Arguments parsing error: Invalid argument for parameter-config.");
                return -1;
            }
            SDLTest_Log("SUT Parameters file: %s", argv[index]);
            SDLVisualTest_FreeSUTConfig(&state->sut_config);
            if(!SDLVisualTest_ParseSUTConfig(argv[index], &state->sut_config))
            {
                SDLTest_LogError("Failed to parse SUT parameters file");
                return -1;
            }
            return 2;
        }
        else if(StrCaseCmpIgnoreHyphen("variator", argv[index]) == 0)
        {
            index++;
            if(!argv[index])
            {
                SDLTest_LogError("Arguments parsing error: Invalid argument for variator.");
                return -1;
            }
            SDLTest_Log("Variator: %s", argv[index]);
            if(SDL_strcasecmp("exhaustive", argv[index]) == 0)
                state->variator_type = SDL_VARIATOR_EXHAUSTIVE;
            else if(SDL_strcasecmp("random", argv[index]) == 0)
                state->variator_type = SDL_VARIATOR_RANDOM;
            else
            {
                SDLTest_LogError("Arguments parsing error: Invalid variator name.");
                return -1;
            }
            return 2;
        }
        else if(StrCaseCmpIgnoreHyphen("num-variations", argv[index]) == 0)
        {
            index++;
            if(!argv[index])
            {
                SDLTest_LogError("Arguments parsing error: Invalid argument for num-variations.");
                return -1;
            }
            state->num_variations = SDL_atoi(argv[index]);
            SDLTest_Log("Number of variations to run: %d", state->num_variations);
            if(state->num_variations <= 0)
            {
                SDLTest_LogError("Arguments parsing error: num-variations must be positive.");
                return -1;
            }
            return 2;
        }
        else if(StrCaseCmpIgnoreHyphen("no-launch", argv[index]) == 0)
        {
            state->no_launch = SDL_TRUE;
            SDLTest_Log("SUT will not be launched.");
            return 1;
        }
        else if(StrCaseCmpIgnoreHyphen("action-config", argv[index]) == 0)
        {
            index++;
            if(!argv[index])
            {
                SDLTest_LogError("Arguments parsing error: invalid argument for action-config");
                return -1;
            }
            SDLTest_Log("Action Config file: %s", argv[index]);
            SDLVisualTest_EmptyActionQueue(&state->action_queue);
            if(!SDLVisualTest_ParseActionConfig(argv[index], &state->action_queue))
            {
                SDLTest_LogError("SDLVisualTest_ParseActionConfig() failed");
                return -1;
            }
            return 2;
        }
        else if(StrCaseCmpIgnoreHyphen("config", argv[index]) == 0)
        {
            index++;
            if(!argv[index])
            {
                SDLTest_LogError("Arguments parsing error: invalid argument for config");
                return -1;
            }
    
            /* do nothing, this option has already been handled */
            return 2;
        }
        return 0;
    }
    
    /* TODO: Trailing/leading spaces and spaces between equals sign not supported. */
    static int
    ParseConfig(char* file, SDLVisualTest_HarnessState* state)
    {
        SDL_RWops* rw;
        SDLVisualTest_RWHelperBuffer buffer;
        char line[MAX_CONFIG_LINE_LEN];
    
        rw = SDL_RWFromFile(file, "r");
        if(!rw)
        {
            SDLTest_LogError("SDL_RWFromFile() failed");
            return 0;
        }
    
        SDLVisualTest_RWHelperResetBuffer(&buffer);
        while(SDLVisualTest_RWHelperReadLine(rw, line, MAX_CONFIG_LINE_LEN,
                                             &buffer, '#'))
        {
            char** argv;
            int i, num_params;
    
            /* count number of parameters and replace the trailing newline with 0 */
            num_params = 1;
            for(i = 0; line[i]; i++)
            {
                if(line[i] == '=')
                {
                    num_params = 2;
                    break;
                }
            }
    
            /* populate argv */
            argv = (char**)SDL_malloc((num_params + 1) * sizeof(char*));
            if(!argv)
            {
                SDLTest_LogError("SDL_malloc() failed.");
                SDL_RWclose(rw);
                return 0;
            }
    
            argv[num_params] = NULL;
            for(i = 0; i < num_params; i++)
            {
                argv[i] = strtok(i == 0 ? line : NULL, "=");
            }
    
            if(ParseArg(argv, 0, state) == -1)
            {
                SDLTest_LogError("ParseArg() failed");
                SDL_free(argv);
                SDL_RWclose(rw);
                return 0;
            }
            SDL_free(argv);
        }
        SDL_RWclose(rw);
    
        if(!state->sutapp[0])
            return 0;
        return 1;
    }
    
    int
    SDLVisualTest_ParseHarnessArgs(char** argv, SDLVisualTest_HarnessState* state)
    {
        int i;
    
        SDLTest_Log("Parsing commandline arguments..");
    
        if(!argv)
        {
            SDLTest_LogError("argv is NULL");
            return 0;
        }
        if(!state)
        {
            SDLTest_LogError("state is NULL");
            return 0;
        }
    
        /* initialize the state object */
        state->sutargs[0] = '\0';
        state->sutapp[0] = '\0';
        state->output_dir[0] = '\0';
        state->verify_dir[0] = '\0';
        state->timeout = DEFAULT_SUT_TIMEOUT;
        SDL_memset(&state->sut_config, 0, sizeof(SDLVisualTest_SUTConfig));
        SDL_memset(&state->action_queue, 0, sizeof(SDLVisualTest_ActionQueue));
        state->variator_type = SDL_VARIATOR_RANDOM;
        state->num_variations = -1;
        state->no_launch = SDL_FALSE;
    
        /* parse config file if passed */
        for(i = 0; argv[i]; i++)
        {
            if(StrCaseCmpIgnoreHyphen("config", argv[i]) == 0)
            {
                if(!argv[i + 1])
                {
                    SDLTest_Log("Arguments parsing error: invalid argument for config.");
                    return 0;
                }
                if(!ParseConfig(argv[i + 1], state))
                {
                    SDLTest_LogError("ParseConfig() failed");
                    return 0;
                }
            }
        }
    
        /* parse the arguments */
        for(i = 0; argv[i];)
        {
            int consumed = ParseArg(argv, i, state);
            if(consumed == -1 || consumed == 0)
            {
                SDLTest_LogError("ParseArg() failed");
                return 0;
            }
            i += consumed;
        }
    
        if(state->variator_type == SDL_VARIATOR_RANDOM && state->num_variations == -1)
            state->num_variations = 1;
    
        /* check to see if required options have been passed */
        if(!state->sutapp[0])
        {
            SDLTest_LogError("sutapp must be passed.");
            return 0;
        }
        if(!state->sutargs[0] && !state->sut_config.options)
        {
            SDLTest_LogError("Either sutargs or parameter-config must be passed.");
            return 0;
        }
        if(!state->output_dir[0])
        {
            SDL_strlcpy(state->output_dir, "./output", MAX_PATH_LEN);
        }
        if(!state->verify_dir[0])
        {
            SDL_strlcpy(state->verify_dir, "./verify", MAX_PATH_LEN);
        }
    
        return 1;
    }
    
    void
    SDLVisualTest_FreeHarnessState(SDLVisualTest_HarnessState* state)
    {
        if(state)
        {
            SDLVisualTest_EmptyActionQueue(&state->action_queue);
            SDLVisualTest_FreeSUTConfig(&state->sut_config);
        }
    }