Test lib updates: updated harness to support filtering, added surface comparer, updated interface to test images; added Render test suite from GSOC project
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
22 #include "SDL_config.h"
31 /* Invalid test name/description message format */
32 const char *SDLTest_InvalidNameFormat = "(Invalid)";
34 /* Log summary message format */
35 const char *SDLTest_LogSummaryFormat = "%s Summary: Total=%d Passed=%d Failed=%d Skipped=%d";
37 /* Final result message format */
38 const char *SDLTest_FinalResultFormat = ">>> %s '%s': %s\n";
40 /*! \brief Timeout for single test case execution */
41 static Uint32 SDLTest_TestCaseTimeout = 3600;
44 * Generates a random run seed string for the harness. The generated seed
45 * will contain alphanumeric characters (0-9A-Z).
47 * Note: The returned string needs to be deallocated by the caller.
49 * \param length The length of the seed string to generate
51 * \returns The generated seed string
54 SDLTest_GenerateRunSeed(const int length)
57 SDLTest_RandomContext randomContext;
62 SDLTest_LogError("The length of the harness seed must be >0.");
66 // Allocate output buffer
67 seed = (char *)SDL_malloc((length + 1) * sizeof(char));
69 SDLTest_LogError("SDL_malloc for run seed output buffer failed.");
73 // Generate a random string of alphanumeric characters
74 SDLTest_RandomInitTime(&randomContext);
75 for (counter = 0; counter < length - 1; ++counter) {
76 unsigned int number = SDLTest_Random(&randomContext);
77 char ch = (char) (number % (91 - 48)) + 48;
78 if (ch >= 58 && ch <= 64) {
89 * Generates an execution key for the fuzzer.
91 * \param runSeed The run seed to use
92 * \param suiteName The name of the test suite
93 * \param testName The name of the test
94 * \param iteration The iteration count
96 * \returns The generated execution key to initialize the fuzzer with.
100 SDLTest_GenerateExecKey(char *runSeed, char *suiteName, char *testName, int iteration)
102 SDLTest_Md5Context md5Context;
104 char iterationString[16];
105 Uint32 runSeedLength;
106 Uint32 suiteNameLength;
107 Uint32 testNameLength;
108 Uint32 iterationStringLength;
109 Uint32 entireStringLength;
112 if (runSeed == NULL || strlen(runSeed)==0) {
113 SDLTest_LogError("Invalid runSeed string.");
117 if (suiteName == NULL || strlen(suiteName)==0) {
118 SDLTest_LogError("Invalid suiteName string.");
122 if (testName == NULL || strlen(testName)==0) {
123 SDLTest_LogError("Invalid testName string.");
127 if (iteration <= 0) {
128 SDLTest_LogError("Invalid iteration count.");
132 // Convert iteration number into a string
133 memset(iterationString, 0, sizeof(iterationString));
134 SDL_snprintf(iterationString, sizeof(iterationString) - 1, "%d", iteration);
136 // Combine the parameters into single string
137 runSeedLength = strlen(runSeed);
138 suiteNameLength = strlen(suiteName);
139 testNameLength = strlen(testName);
140 iterationStringLength = strlen(iterationString);
141 entireStringLength = runSeedLength + suiteNameLength + testNameLength + iterationStringLength + 1;
142 buffer = (char *)SDL_malloc(entireStringLength);
143 if (buffer == NULL) {
144 SDLTest_LogError("SDL_malloc failed to allocate buffer for execKey generation.");
147 SDL_snprintf(buffer, entireStringLength, "%s%s%s%d", runSeed, suiteName, testName, iteration);
149 // Hash string and use half of the digest as 64bit exec key
150 SDLTest_Md5Init(&md5Context);
151 SDLTest_Md5Update(&md5Context, (unsigned char *)buffer, entireStringLength);
152 SDLTest_Md5Final(&md5Context);
154 keys = (Uint64 *)md5Context.digest;
160 * \brief Set timeout handler for test.
162 * Note: SDL_Init(SDL_INIT_TIMER) will be called if it wasn't done so before.
164 * \param timeout Timeout interval in seconds.
165 * \param callback Function that will be called after timeout has elapsed.
167 * \return Timer id or -1 on failure.
170 SDLTest_SetTestTimeout(int timeout, void (*callback)())
172 Uint32 timeoutInMilliseconds;
175 if (callback == NULL) {
176 SDLTest_LogError("Timeout callback can't be NULL");
181 SDLTest_LogError("Timeout value must be bigger than zero.");
185 /* Init SDL timer if not initialized before */
186 if (SDL_WasInit(SDL_INIT_TIMER) == 0) {
187 if (SDL_InitSubSystem(SDL_INIT_TIMER)) {
188 SDLTest_LogError("Failed to init timer subsystem: %s", SDL_GetError());
194 timeoutInMilliseconds = timeout * 1000;
195 timerID = SDL_AddTimer(timeoutInMilliseconds, (SDL_TimerCallback)callback, 0x0);
197 SDLTest_LogError("Creation of SDL timer failed: %s", SDL_GetError());
205 * \brief Timeout handler. Aborts test run and exits harness process.
210 SDLTest_LogError("TestCaseTimeout timer expired. Aborting test run.");
211 exit(TEST_ABORTED); // bail out from the test
215 * \brief Execute a test using the given execution key.
217 * \param testSuite Suite containing the test case.
218 * \param testCase Case to execute.
219 * \param execKey Execution key for the fuzzer.
221 * \returns Test case result.
224 SDLTest_RunTest(SDLTest_TestSuiteReference *testSuite, SDLTest_TestCaseReference *testCase, Uint64 execKey)
226 SDL_TimerID timer = 0;
230 if (testSuite==NULL || testCase==NULL || testSuite->name==NULL || testCase->name==NULL)
232 SDLTest_LogError("Setup failure: testSuite or testCase references NULL");
233 return TEST_RESULT_SETUP_FAILURE;
236 if (!testCase->enabled)
238 SDLTest_Log((char *)SDLTest_FinalResultFormat, "Test", testCase->name, "Skipped (Disabled)");
239 return TEST_RESULT_SKIPPED;
244 SDLTest_FuzzerInit(execKey);
246 // Reset assert tracker
247 SDLTest_ResetAssertSummary();
250 timer = SDLTest_SetTestTimeout(SDLTest_TestCaseTimeout, SDLTest_BailOut);
252 // Maybe run suite initalizer function
253 if (testSuite->testSetUp) {
254 testSuite->testSetUp(0x0);
255 if (SDLTest_AssertSummaryToTestResult() == TEST_RESULT_FAILED) {
256 SDLTest_LogError((char *)SDLTest_FinalResultFormat, "Suite Setup", testSuite->name, "Failed");
257 return TEST_RESULT_SETUP_FAILURE;
261 // Run test case function
262 testCase->testCase(0x0);
263 testResult = SDLTest_AssertSummaryToTestResult();
265 // Maybe run suite cleanup function (ignore failed asserts)
266 if (testSuite->testTearDown) {
267 testSuite->testTearDown(0x0);
270 // Cancel timeout timer
272 SDL_RemoveTimer(timer);
275 // Report on asserts and fuzzer usage
276 fuzzerCount = SDLTest_GetFuzzerInvocationCount();
277 if (fuzzerCount > 0) {
278 SDLTest_Log("Fuzzer invocations: %d", fuzzerCount);
280 SDLTest_LogAssertSummary();
285 /* Prints summary of all suites/tests contained in the given reference */
286 void SDLTest_LogTestSuiteSummary(SDLTest_TestSuiteReference *testSuites)
290 SDLTest_TestSuiteReference *testSuite;
291 SDLTest_TestCaseReference *testCase;
293 // Loop over all suites
295 while(&testSuites[suiteCounter]) {
296 testSuite=&testSuites[suiteCounter];
298 SDLTest_Log("Test Suite %i - %s\n", suiteCounter,
299 (testSuite->name) ? testSuite->name : SDLTest_InvalidNameFormat);
301 // Loop over all test cases
303 while(testSuite->testCases[testCounter])
305 testCase=(SDLTest_TestCaseReference *)testSuite->testCases[testCounter];
307 SDLTest_Log(" Test Case %i - %s: %s", testCounter,
308 (testCase->name) ? testCase->name : SDLTest_InvalidNameFormat,
309 (testCase->description) ? testCase->description : SDLTest_InvalidNameFormat);
314 /* Gets a timer value in seconds */
317 float currentClock = (float)clock();
318 return currentClock / (float)CLOCKS_PER_SEC;
322 * \brief Execute a test suite using the given run seend and execution key.
324 * The filter string is matched to the suite name (full comparison) to select a single suite,
325 * or if no suite matches, it is matched to the test names (full comparison) to select a single test.
327 * \param testSuites Suites containing the test case.
328 * \param userRunSeed Custom run seed provided by user, or NULL to autogenerate one.
329 * \param userExecKey Custom execution key provided by user, or 0 to autogenerate one.
330 * \param filter Filter specification. NULL disables. Case sensitive.
331 * \param testIterations Number of iterations to run each test case.
333 * \returns Test run result; 0 when all tests passed, 1 if any tests failed.
336 SDLTest_RunSuites(SDLTest_TestSuiteReference *testSuites[], char *userRunSeed, Uint64 userExecKey, char *filter, int testIterations)
340 int iterationCounter;
341 SDLTest_TestSuiteReference *testSuite;
342 SDLTest_TestCaseReference *testCase;
343 char *runSeed = NULL;
344 char *currentSuiteName;
345 char *currentTestName;
347 float runStartSeconds;
348 float suiteStartSeconds;
349 float testStartSeconds;
351 float suiteEndSeconds;
352 float testEndSeconds;
355 char *suiteFilterName = NULL;
357 char *testFilterName = NULL;
360 Uint32 totalTestFailedCount = 0;
361 Uint32 totalTestPassedCount = 0;
362 Uint32 totalTestSkippedCount = 0;
363 Uint32 testFailedCount = 0;
364 Uint32 testPassedCount = 0;
365 Uint32 testSkippedCount = 0;
367 char *logFormat = (char *)SDLTest_LogSummaryFormat;
369 // Sanitize test iterations
370 if (testIterations < 1) {
374 // Generate run see if we don't have one already
375 if (userRunSeed == NULL || strlen(userRunSeed) == 0) {
376 runSeed = SDLTest_GenerateRunSeed(16);
377 if (runSeed == NULL) {
378 SDLTest_LogError("Generating a random seed failed");
382 runSeed = userRunSeed;
386 // Reset per-run counters
387 totalTestFailedCount = 0;
388 totalTestPassedCount = 0;
389 totalTestSkippedCount = 0;
391 // Take time - run start
392 runStartSeconds = GetClock();
394 // Log run with fuzzer parameters
395 SDLTest_Log("::::: Test Run /w seed '%s' started\n", runSeed);
397 // Initialize filtering
398 if (filter != NULL && SDL_strlen(filter) > 0) {
399 /* Loop over all suites to check if we have a filter match */
401 while (testSuites[suiteCounter] && suiteFilter == 0) {
402 testSuite=(SDLTest_TestSuiteReference *)testSuites[suiteCounter];
404 if (testSuite->name != NULL && SDL_strcmp(filter, testSuite->name) == 0) {
405 /* Matched a suite name */
407 suiteFilterName = testSuite->name;
408 SDLTest_Log("Filtering: running only suite '%s'", suiteFilterName);
412 /* Within each suite, loop over all test cases to check if we have a filter match */
414 while (testSuite->testCases[testCounter] && testFilter == 0)
416 testCase=(SDLTest_TestCaseReference *)testSuite->testCases[testCounter];
418 if (testCase->name != NULL && SDL_strcmp(filter, testCase->name) == 0) {
419 /* Matched a test name */
421 suiteFilterName = testSuite->name;
423 testFilterName = testCase->name;
424 SDLTest_Log("Filtering: running only test '%s' in suite '%s'", testFilterName, suiteFilterName);
430 if (suiteFilter == 0 && testFilter == 0) {
431 SDLTest_LogError("Filter '%s' did not match any test suite/case.", filter);
432 SDLTest_Log("Exit code: 2");
437 // Loop over all suites
439 while(testSuites[suiteCounter]) {
440 testSuite=(SDLTest_TestSuiteReference *)testSuites[suiteCounter];
441 currentSuiteName = (char *)((testSuite->name) ? testSuite->name : SDLTest_InvalidNameFormat);
444 // Filter suite if flag set and we have a name
445 if (suiteFilter == 1 && suiteFilterName != NULL && testSuite->name != NULL &&
446 SDL_strcmp(suiteFilterName, testSuite->name) != 0) {
448 SDLTest_Log("===== Test Suite %i: '%s' skipped\n",
453 // Reset per-suite counters
456 testSkippedCount = 0;
458 // Take time - suite start
459 suiteStartSeconds = GetClock();
462 SDLTest_Log("===== Test Suite %i: '%s' started\n",
466 // Loop over all test cases
468 while(testSuite->testCases[testCounter])
470 testCase=(SDLTest_TestCaseReference *)testSuite->testCases[testCounter];
471 currentTestName = (char *)((testCase->name) ? testCase->name : SDLTest_InvalidNameFormat);
474 // Filter tests if flag set and we have a name
475 if (testFilter == 1 && testFilterName != NULL && testCase->name != NULL &&
476 SDL_strcmp(testFilterName, testCase->name) != 0) {
478 SDLTest_Log("===== Test Case %i.%i: '%s' skipped\n",
484 // Take time - test start
485 testStartSeconds = GetClock();
488 SDLTest_Log("----- Test Case %i.%i: '%s' started",
492 if (testCase->description != NULL && strlen(testCase->description)>0) {
493 SDLTest_Log("Test Description: '%s'",
494 (testCase->description) ? testCase->description : SDLTest_InvalidNameFormat);
497 // Loop over all iterations
498 iterationCounter = 0;
499 while(iterationCounter < testIterations)
503 if (userExecKey != 0) {
504 execKey = userExecKey;
506 execKey = SDLTest_GenerateExecKey(runSeed, testSuite->name, testCase->name, iterationCounter);
509 SDLTest_Log("Test Iteration %i: execKey %llu", iterationCounter, execKey);
510 testResult = SDLTest_RunTest(testSuite, testCase, execKey);
512 if (testResult == TEST_RESULT_PASSED) {
514 totalTestPassedCount++;
515 } else if (testResult == TEST_RESULT_SKIPPED) {
517 totalTestSkippedCount++;
520 totalTestFailedCount++;
524 // Take time - test end
525 testEndSeconds = GetClock();
526 runtime = testEndSeconds - testStartSeconds;
527 if (runtime < 0.0f) runtime = 0.0f;
529 if (testIterations > 1) {
531 SDLTest_Log("Runtime of %i iterations: %.1f sec", testIterations, runtime);
532 SDLTest_Log("Average Test runtime: %.5f sec", runtime / (float)testIterations);
535 SDLTest_Log("Total Test runtime: %.1f sec", runtime);
538 // Log final test result
539 switch (testResult) {
540 case TEST_RESULT_PASSED:
541 SDLTest_Log((char *)SDLTest_FinalResultFormat, "Test", currentTestName, "Passed");
543 case TEST_RESULT_FAILED:
544 SDLTest_LogError((char *)SDLTest_FinalResultFormat, "Test", currentTestName, "Failed");
546 case TEST_RESULT_NO_ASSERT:
547 SDLTest_LogError((char *)SDLTest_FinalResultFormat,"Test", currentTestName, "No Asserts");
554 // Take time - suite end
555 suiteEndSeconds = GetClock();
556 runtime = suiteEndSeconds - suiteStartSeconds;
557 if (runtime < 0.0f) runtime = 0.0f;
560 SDLTest_Log("Total Suite runtime: %.1f sec", runtime);
562 // Log summary and final Suite result
563 countSum = testPassedCount + testFailedCount + testSkippedCount;
564 if (testFailedCount == 0)
566 SDLTest_Log(logFormat, "Suite", countSum, testPassedCount, testFailedCount, testSkippedCount);
567 SDLTest_Log((char *)SDLTest_FinalResultFormat, "Suite", currentSuiteName, "Passed");
571 SDLTest_LogError(logFormat, "Suite", countSum, testPassedCount, testFailedCount, testSkippedCount);
572 SDLTest_LogError((char *)SDLTest_FinalResultFormat, "Suite", currentSuiteName, "Failed");
578 // Take time - run end
579 runEndSeconds = GetClock();
580 runtime = runEndSeconds - runStartSeconds;
581 if (runtime < 0.0f) runtime = 0.0f;
584 SDLTest_Log("Total Run runtime: %.1f sec", runtime);
586 // Log summary and final run result
587 countSum = totalTestPassedCount + totalTestFailedCount + totalTestSkippedCount;
588 if (totalTestFailedCount == 0)
591 SDLTest_Log(logFormat, "Run", countSum, totalTestPassedCount, totalTestFailedCount, totalTestSkippedCount);
592 SDLTest_Log((char *)SDLTest_FinalResultFormat, "Run /w seed", runSeed, "Passed");
597 SDLTest_LogError(logFormat, "Run", countSum, totalTestPassedCount, totalTestFailedCount, totalTestSkippedCount);
598 SDLTest_LogError((char *)SDLTest_FinalResultFormat, "Run /w seed", runSeed, "Failed");
601 SDLTest_Log("Exit code: %d", runResult);