|
|
- /* See LICENSE.txt for the full license governing this code. */
- /**
- * \file testharness.c
- *
- * Source file for the test harness.
- */
-
- #include <stdlib.h>
- #include <SDL_test.h>
- #include <SDL.h>
- #include <SDL_assert.h>
- #include "SDL_visualtest_harness_argparser.h"
- #include "SDL_visualtest_process.h"
- #include "SDL_visualtest_variators.h"
- #include "SDL_visualtest_screenshot.h"
- #include "SDL_visualtest_mischelper.h"
-
- #if defined(__WIN32__) && !defined(__CYGWIN__)
- #include <direct.h>
- #elif defined(__WIN32__) && defined(__CYGWIN__)
- #include <signal.h>
- #elif defined(__LINUX__)
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <signal.h>
- #else
- #error "Unsupported platform"
- #endif
-
- /** Code for the user event triggered when a new action is to be executed */
- #define ACTION_TIMER_EVENT 0
- /** Code for the user event triggered when the maximum timeout is reached */
- #define KILL_TIMER_EVENT 1
- /** FPS value used for delays in the action loop */
- #define ACTION_LOOP_FPS 10
-
- /** Value returned by RunSUTAndTest() when the test has passed */
- #define TEST_PASSED 1
- /** Value returned by RunSUTAndTest() when the test has failed */
- #define TEST_FAILED 0
- /** Value returned by RunSUTAndTest() on a fatal error */
- #define TEST_ERROR -1
-
- static SDL_ProcessInfo pinfo;
- static SDL_ProcessExitStatus sut_exitstatus;
- static SDLVisualTest_HarnessState state;
- static SDLVisualTest_Variator variator;
- static SDLVisualTest_ActionNode* current; /* the current action being performed */
- static SDL_TimerID action_timer, kill_timer;
-
- /* returns a char* to be passed as the format argument of a printf-style function. */
- static const char*
- usage(void)
- {
- return "Usage: \n%s --sutapp xyz"
- " [--sutargs abc | --parameter-config xyz.parameters"
- " [--variator exhaustive|random]"
- " [--num-variations N] [--no-launch]] [--timeout hh:mm:ss]"
- " [--action-config xyz.actions]"
- " [--output-dir /path/to/output]"
- " [--verify-dir /path/to/verify]"
- " or --config app.config";
- }
-
- /* register Ctrl+C handlers */
- #if defined(__LINUX__) || defined(__CYGWIN__)
- static void
- CtrlCHandlerCallback(int signum)
- {
- SDL_Event event;
- SDLTest_Log("Ctrl+C received");
- event.type = SDL_QUIT;
- SDL_PushEvent(&event);
- }
- #endif
-
- static Uint32
- ActionTimerCallback(Uint32 interval, void* param)
- {
- SDL_Event event;
- SDL_UserEvent userevent;
- Uint32 next_action_time;
-
- /* push an event to handle the action */
- SDL_zero(userevent);
- userevent.type = SDL_USEREVENT;
- userevent.code = ACTION_TIMER_EVENT;
- userevent.data1 = ¤t->action;
-
- event.type = SDL_USEREVENT;
- event.user = userevent;
- SDL_PushEvent(&event);
-
- /* calculate the new interval and return it */
- if(current->next)
- next_action_time = current->next->action.time - current->action.time;
- else
- {
- next_action_time = 0;
- action_timer = 0;
- }
-
- current = current->next;
- return next_action_time;
- }
-
- static Uint32
- KillTimerCallback(Uint32 interval, void* param)
- {
- SDL_Event event;
- SDL_UserEvent userevent;
-
- SDL_zero(userevent);
- userevent.type = SDL_USEREVENT;
- userevent.code = KILL_TIMER_EVENT;
-
- event.type = SDL_USEREVENT;
- event.user = userevent;
- SDL_PushEvent(&event);
-
- kill_timer = 0;
- return 0;
- }
-
- static int
- ProcessAction(SDLVisualTest_Action* action, int* sut_running, char* args)
- {
- if(!action || !sut_running)
- return TEST_ERROR;
-
- switch(action->type)
- {
- case SDL_ACTION_KILL:
- SDLTest_Log("Action: Kill SUT");
- if(SDL_IsProcessRunning(&pinfo) == 1 &&
- !SDL_KillProcess(&pinfo, &sut_exitstatus))
- {
- SDLTest_LogError("SDL_KillProcess() failed");
- return TEST_ERROR;
- }
- *sut_running = 0;
- break;
-
- case SDL_ACTION_QUIT:
- SDLTest_Log("Action: Quit SUT");
- if(SDL_IsProcessRunning(&pinfo) == 1 &&
- !SDL_QuitProcess(&pinfo, &sut_exitstatus))
- {
- SDLTest_LogError("SDL_QuitProcess() failed");
- return TEST_FAILED;
- }
- *sut_running = 0;
- break;
-
- case SDL_ACTION_LAUNCH:
- {
- char* path;
- char* args;
- SDL_ProcessInfo action_process;
- SDL_ProcessExitStatus ps;
-
- path = action->extra.process.path;
- args = action->extra.process.args;
- if(args)
- {
- SDLTest_Log("Action: Launch process: %s with arguments: %s",
- path, args);
- }
- else
- SDLTest_Log("Action: Launch process: %s", path);
- if(!SDL_LaunchProcess(path, args, &action_process))
- {
- SDLTest_LogError("SDL_LaunchProcess() failed");
- return TEST_ERROR;
- }
-
- /* small delay so that the process can do its job */
- SDL_Delay(1000);
-
- if(SDL_IsProcessRunning(&action_process) > 0)
- {
- SDLTest_LogError("Process %s took too long too complete."
- " Force killing...", action->extra.process.path);
- if(!SDL_KillProcess(&action_process, &ps))
- {
- SDLTest_LogError("SDL_KillProcess() failed");
- return TEST_ERROR;
- }
- }
- }
- break;
-
- case SDL_ACTION_SCREENSHOT:
- {
- char path[MAX_PATH_LEN], hash[33];
-
- SDLTest_Log("Action: Take screenshot");
- /* can't take a screenshot if the SUT isn't running */
- if(SDL_IsProcessRunning(&pinfo) != 1)
- {
- SDLTest_LogError("SUT has quit.");
- *sut_running = 0;
- return TEST_FAILED;
- }
-
- /* file name for the screenshot image */
- SDLVisualTest_HashString(args, hash);
- SDL_snprintf(path, MAX_PATH_LEN, "%s/%s", state.output_dir, hash);
- if(!SDLVisualTest_ScreenshotProcess(&pinfo, path))
- {
- SDLTest_LogError("SDLVisualTest_ScreenshotProcess() failed");
- return TEST_ERROR;
- }
- }
- break;
-
- case SDL_ACTION_VERIFY:
- {
- int ret;
-
- SDLTest_Log("Action: Verify screenshot");
- ret = SDLVisualTest_VerifyScreenshots(args, state.output_dir,
- state.verify_dir);
-
- if(ret == -1)
- {
- SDLTest_LogError("SDLVisualTest_VerifyScreenshots() failed");
- return TEST_ERROR;
- }
- else if(ret == 0)
- {
- SDLTest_Log("Verification failed: Images were not equal.");
- return TEST_FAILED;
- }
- else if(ret == 1)
- SDLTest_Log("Verification successful.");
- else
- {
- SDLTest_Log("Verfication skipped.");
- return TEST_FAILED;
- }
- }
- break;
-
- default:
- SDLTest_LogError("Invalid action type");
- return TEST_ERROR;
- break;
- }
-
- return TEST_PASSED;
- }
-
- static int
- RunSUTAndTest(char* sutargs, int variation_num)
- {
- int success, sut_running, return_code;
- char hash[33];
- SDL_Event event;
-
- return_code = TEST_PASSED;
-
- if(!sutargs)
- {
- SDLTest_LogError("sutargs argument cannot be NULL");
- return_code = TEST_ERROR;
- goto runsutandtest_cleanup_generic;
- }
-
- SDLVisualTest_HashString(sutargs, hash);
- SDLTest_Log("Hash: %s", hash);
-
- success = SDL_LaunchProcess(state.sutapp, sutargs, &pinfo);
- if(!success)
- {
- SDLTest_Log("Could not launch SUT.");
- return_code = TEST_ERROR;
- goto runsutandtest_cleanup_generic;
- }
- SDLTest_Log("SUT launch successful.");
- SDLTest_Log("Process will be killed in %d milliseconds", state.timeout);
- sut_running = 1;
-
- /* launch the timers */
- SDLTest_Log("Performing actions..");
- current = state.action_queue.front;
- action_timer = 0;
- kill_timer = 0;
- if(current)
- {
- action_timer = SDL_AddTimer(current->action.time, ActionTimerCallback, NULL);
- if(!action_timer)
- {
- SDLTest_LogError("SDL_AddTimer() failed");
- return_code = TEST_ERROR;
- goto runsutandtest_cleanup_timer;
- }
- }
- kill_timer = SDL_AddTimer(state.timeout, KillTimerCallback, NULL);
- if(!kill_timer)
- {
- SDLTest_LogError("SDL_AddTimer() failed");
- return_code = TEST_ERROR;
- goto runsutandtest_cleanup_timer;
- }
-
- /* the timer stops running if the actions queue is empty, and the
- SUT stops running if it crashes or if we encounter a KILL/QUIT action */
- while(sut_running)
- {
- /* process the actions by using an event queue */
- while(SDL_PollEvent(&event))
- {
- if(event.type == SDL_USEREVENT)
- {
- if(event.user.code == ACTION_TIMER_EVENT)
- {
- SDLVisualTest_Action* action;
-
- action = (SDLVisualTest_Action*)event.user.data1;
-
- switch(ProcessAction(action, &sut_running, sutargs))
- {
- case TEST_PASSED:
- break;
-
- case TEST_FAILED:
- return_code = TEST_FAILED;
- goto runsutandtest_cleanup_timer;
- break;
-
- default:
- SDLTest_LogError("ProcessAction() failed");
- return_code = TEST_ERROR;
- goto runsutandtest_cleanup_timer;
- }
- }
- else if(event.user.code == KILL_TIMER_EVENT)
- {
- SDLTest_LogError("Maximum timeout reached. Force killing..");
- return_code = TEST_FAILED;
- goto runsutandtest_cleanup_timer;
- }
- }
- else if(event.type == SDL_QUIT)
- {
- SDLTest_LogError("Received QUIT event. Testharness is quitting..");
- return_code = TEST_ERROR;
- goto runsutandtest_cleanup_timer;
- }
- }
- SDL_Delay(1000/ACTION_LOOP_FPS);
- }
-
- SDLTest_Log("SUT exit code was: %d", sut_exitstatus.exit_status);
- if(sut_exitstatus.exit_status == 0)
- {
- return_code = TEST_PASSED;
- goto runsutandtest_cleanup_timer;
- }
- else
- {
- return_code = TEST_FAILED;
- goto runsutandtest_cleanup_timer;
- }
-
- return_code = TEST_ERROR;
- goto runsutandtest_cleanup_generic;
-
- runsutandtest_cleanup_timer:
- if(action_timer && !SDL_RemoveTimer(action_timer))
- {
- SDLTest_Log("SDL_RemoveTimer() failed");
- return_code = TEST_ERROR;
- }
-
- if(kill_timer && !SDL_RemoveTimer(kill_timer))
- {
- SDLTest_Log("SDL_RemoveTimer() failed");
- return_code = TEST_ERROR;
- }
- /* runsutandtest_cleanup_process: */
- if(SDL_IsProcessRunning(&pinfo) && !SDL_KillProcess(&pinfo, &sut_exitstatus))
- {
- SDLTest_Log("SDL_KillProcess() failed");
- return_code = TEST_ERROR;
- }
- runsutandtest_cleanup_generic:
- return return_code;
- }
-
- /** Entry point for testharness */
- int
- main(int argc, char* argv[])
- {
- int i, passed, return_code, failed;
-
- /* freeing resources, linux style! */
- return_code = 0;
-
- if(argc < 2)
- {
- SDLTest_Log(usage(), argv[0]);
- goto cleanup_generic;
- }
-
- #if defined(__LINUX__) || defined(__CYGWIN__)
- signal(SIGINT, CtrlCHandlerCallback);
- #endif
-
- /* parse arguments */
- if(!SDLVisualTest_ParseHarnessArgs(argv + 1, &state))
- {
- SDLTest_Log(usage(), argv[0]);
- return_code = 1;
- goto cleanup_generic;
- }
- SDLTest_Log("Parsed harness arguments successfully.");
-
- /* initialize SDL */
- if(SDL_Init(SDL_INIT_TIMER) == -1)
- {
- SDLTest_LogError("SDL_Init() failed.");
- SDLVisualTest_FreeHarnessState(&state);
- return_code = 1;
- goto cleanup_harness_state;
- }
-
- /* create an output directory if none exists */
- #if defined(__LINUX__) || defined(__CYGWIN__)
- mkdir(state.output_dir, 0777);
- #elif defined(__WIN32__)
- _mkdir(state.output_dir);
- #else
- #error "Unsupported platform"
- #endif
-
- /* test with sutargs */
- if(SDL_strlen(state.sutargs))
- {
- SDLTest_Log("Running: %s %s", state.sutapp, state.sutargs);
- if(!state.no_launch)
- {
- switch(RunSUTAndTest(state.sutargs, 0))
- {
- case TEST_PASSED:
- SDLTest_Log("Status: PASSED");
- break;
-
- case TEST_FAILED:
- SDLTest_Log("Status: FAILED");
- break;
-
- case TEST_ERROR:
- SDLTest_LogError("Some error occurred while testing.");
- return_code = 1;
- goto cleanup_sdl;
- break;
- }
- }
- }
-
- if(state.sut_config.num_options > 0)
- {
- const char* variator_name = (state.variator_type == SDL_VARIATOR_RANDOM) ?
- "RANDOM" : "EXHAUSTIVE";
- if(state.num_variations > 0)
- SDLTest_Log("Testing SUT with variator: %s for %d variations",
- variator_name, state.num_variations);
- else
- SDLTest_Log("Testing SUT with variator: %s and ALL variations",
- variator_name);
- /* initialize the variator */
- if(!SDLVisualTest_InitVariator(&variator, &state.sut_config,
- state.variator_type, 0))
- {
- SDLTest_LogError("Could not initialize variator");
- return_code = 1;
- goto cleanup_sdl;
- }
-
- /* iterate through all the variations */
- passed = 0;
- failed = 0;
- for(i = 0; state.num_variations > 0 ? (i < state.num_variations) : 1; i++)
- {
- char* args = SDLVisualTest_GetNextVariation(&variator);
- if(!args)
- break;
- SDLTest_Log("\nVariation number: %d\nArguments: %s", i + 1, args);
-
- if(!state.no_launch)
- {
- switch(RunSUTAndTest(args, i + 1))
- {
- case TEST_PASSED:
- SDLTest_Log("Status: PASSED");
- passed++;
- break;
-
- case TEST_FAILED:
- SDLTest_Log("Status: FAILED");
- failed++;
- break;
-
- case TEST_ERROR:
- SDLTest_LogError("Some error occurred while testing.");
- goto cleanup_variator;
- break;
- }
- }
- }
- if(!state.no_launch)
- {
- /* report stats */
- SDLTest_Log("Testing complete.");
- SDLTest_Log("%d/%d tests passed.", passed, passed + failed);
- }
- goto cleanup_variator;
- }
-
- goto cleanup_sdl;
-
- cleanup_variator:
- SDLVisualTest_FreeVariator(&variator);
- cleanup_sdl:
- SDL_Quit();
- cleanup_harness_state:
- SDLVisualTest_FreeHarnessState(&state);
- cleanup_generic:
- return return_code;
- }
|