Fixed spaces in source file license comment.
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2013 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;
60 /* Sanity check input */
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 || runSeed[0] == '\0') {
113 SDLTest_LogError("Invalid runSeed string.");
117 if (suiteName == NULL || suiteName[0] == '\0') {
118 SDLTest_LogError("Invalid suiteName string.");
122 if (testName == NULL || testName[0] == '\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 SDL_memset(iterationString, 0, sizeof(iterationString));
134 SDL_snprintf(iterationString, sizeof(iterationString) - 1, "%d", iteration);
136 /* Combine the parameters into single string */
137 runSeedLength = SDL_strlen(runSeed);
138 suiteNameLength = SDL_strlen(suiteName);
139 testNameLength = SDL_strlen(testName);
140 iterationStringLength = SDL_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;
227 int testCaseResult = 0;
231 if (testSuite==NULL || testCase==NULL || testSuite->name==NULL || testCase->name==NULL)
233 SDLTest_LogError("Setup failure: testSuite or testCase references NULL");
234 return TEST_RESULT_SETUP_FAILURE;
237 if (!testCase->enabled)
239 SDLTest_Log((char *)SDLTest_FinalResultFormat, "Test", testCase->name, "Skipped (Disabled)");
240 return TEST_RESULT_SKIPPED;
244 /* Initialize fuzzer */
245 SDLTest_FuzzerInit(execKey);
247 /* Reset assert tracker */
248 SDLTest_ResetAssertSummary();
250 /* Set timeout timer */
251 timer = SDLTest_SetTestTimeout(SDLTest_TestCaseTimeout, SDLTest_BailOut);
253 /* Maybe run suite initalizer function */
254 if (testSuite->testSetUp) {
255 testSuite->testSetUp(0x0);
256 if (SDLTest_AssertSummaryToTestResult() == TEST_RESULT_FAILED) {
257 SDLTest_LogError((char *)SDLTest_FinalResultFormat, "Suite Setup", testSuite->name, "Failed");
258 return TEST_RESULT_SETUP_FAILURE;
262 /* Run test case function */
263 testCaseResult = testCase->testCase(0x0);
265 /* Convert test execution result into harness result */
266 if (testCaseResult == TEST_SKIPPED) {
267 /* Test was programatically skipped */
268 testResult = TEST_RESULT_SKIPPED;
269 } else if (testCaseResult == TEST_STARTED) {
270 /* Test did not return a TEST_COMPLETED value; assume it failed */
271 testResult = TEST_RESULT_FAILED;
272 } else if (testCaseResult == TEST_ABORTED) {
273 /* Test was aborted early; assume it failed */
274 testResult = TEST_RESULT_FAILED;
276 /* Perform failure analysis based on asserts */
277 testResult = SDLTest_AssertSummaryToTestResult();
280 /* Maybe run suite cleanup function (ignore failed asserts) */
281 if (testSuite->testTearDown) {
282 testSuite->testTearDown(0x0);
285 /* Cancel timeout timer */
287 SDL_RemoveTimer(timer);
290 /* Report on asserts and fuzzer usage */
291 fuzzerCount = SDLTest_GetFuzzerInvocationCount();
292 if (fuzzerCount > 0) {
293 SDLTest_Log("Fuzzer invocations: %d", fuzzerCount);
296 /* Final log based on test execution result */
297 if (testCaseResult == TEST_SKIPPED) {
298 /* Test was programatically skipped */
299 SDLTest_Log((char *)SDLTest_FinalResultFormat, "Test", testCase->name, "Skipped (Programmatically)");
300 } else if (testCaseResult == TEST_STARTED) {
301 /* Test did not return a TEST_COMPLETED value; assume it failed */
302 SDLTest_LogError((char *)SDLTest_FinalResultFormat, "Test", testCase->name, "Failed (test started, but did not return TEST_COMPLETED)");
303 } else if (testCaseResult == TEST_ABORTED) {
304 /* Test was aborted early; assume it failed */
305 SDLTest_LogError((char *)SDLTest_FinalResultFormat, "Test", testCase->name, "Failed (Aborted)");
307 SDLTest_LogAssertSummary();
313 /* Prints summary of all suites/tests contained in the given reference */
314 void SDLTest_LogTestSuiteSummary(SDLTest_TestSuiteReference *testSuites)
318 SDLTest_TestSuiteReference *testSuite;
319 SDLTest_TestCaseReference *testCase;
321 /* Loop over all suites */
323 while(&testSuites[suiteCounter]) {
324 testSuite=&testSuites[suiteCounter];
326 SDLTest_Log("Test Suite %i - %s\n", suiteCounter,
327 (testSuite->name) ? testSuite->name : SDLTest_InvalidNameFormat);
329 /* Loop over all test cases */
331 while(testSuite->testCases[testCounter])
333 testCase=(SDLTest_TestCaseReference *)testSuite->testCases[testCounter];
335 SDLTest_Log(" Test Case %i - %s: %s", testCounter,
336 (testCase->name) ? testCase->name : SDLTest_InvalidNameFormat,
337 (testCase->description) ? testCase->description : SDLTest_InvalidNameFormat);
342 /* Gets a timer value in seconds */
345 float currentClock = (float)clock();
346 return currentClock / (float)CLOCKS_PER_SEC;
350 * \brief Execute a test suite using the given run seend and execution key.
352 * The filter string is matched to the suite name (full comparison) to select a single suite,
353 * or if no suite matches, it is matched to the test names (full comparison) to select a single test.
355 * \param testSuites Suites containing the test case.
356 * \param userRunSeed Custom run seed provided by user, or NULL to autogenerate one.
357 * \param userExecKey Custom execution key provided by user, or 0 to autogenerate one.
358 * \param filter Filter specification. NULL disables. Case sensitive.
359 * \param testIterations Number of iterations to run each test case.
361 * \returns Test run result; 0 when all tests passed, 1 if any tests failed.
363 int SDLTest_RunSuites(SDLTest_TestSuiteReference *testSuites[], const char *userRunSeed, Uint64 userExecKey, const char *filter, int testIterations)
367 int iterationCounter;
368 SDLTest_TestSuiteReference *testSuite;
369 SDLTest_TestCaseReference *testCase;
370 const char *runSeed = NULL;
371 char *currentSuiteName;
372 char *currentTestName;
374 float runStartSeconds;
375 float suiteStartSeconds;
376 float testStartSeconds;
378 float suiteEndSeconds;
379 float testEndSeconds;
382 char *suiteFilterName = NULL;
384 char *testFilterName = NULL;
387 Uint32 totalTestFailedCount = 0;
388 Uint32 totalTestPassedCount = 0;
389 Uint32 totalTestSkippedCount = 0;
390 Uint32 testFailedCount = 0;
391 Uint32 testPassedCount = 0;
392 Uint32 testSkippedCount = 0;
394 char *logFormat = (char *)SDLTest_LogSummaryFormat;
396 /* Sanitize test iterations */
397 if (testIterations < 1) {
401 /* Generate run see if we don't have one already */
402 if (userRunSeed == NULL || userRunSeed[0] == '\0') {
403 runSeed = SDLTest_GenerateRunSeed(16);
404 if (runSeed == NULL) {
405 SDLTest_LogError("Generating a random seed failed");
409 runSeed = userRunSeed;
413 /* Reset per-run counters */
414 totalTestFailedCount = 0;
415 totalTestPassedCount = 0;
416 totalTestSkippedCount = 0;
418 /* Take time - run start */
419 runStartSeconds = GetClock();
421 /* Log run with fuzzer parameters */
422 SDLTest_Log("::::: Test Run /w seed '%s' started\n", runSeed);
424 /* Initialize filtering */
425 if (filter != NULL && filter[0] != '\0') {
426 /* Loop over all suites to check if we have a filter match */
428 while (testSuites[suiteCounter] && suiteFilter == 0) {
429 testSuite=(SDLTest_TestSuiteReference *)testSuites[suiteCounter];
431 if (testSuite->name != NULL && SDL_strcmp(filter, testSuite->name) == 0) {
432 /* Matched a suite name */
434 suiteFilterName = testSuite->name;
435 SDLTest_Log("Filtering: running only suite '%s'", suiteFilterName);
439 /* Within each suite, loop over all test cases to check if we have a filter match */
441 while (testSuite->testCases[testCounter] && testFilter == 0)
443 testCase=(SDLTest_TestCaseReference *)testSuite->testCases[testCounter];
445 if (testCase->name != NULL && SDL_strcmp(filter, testCase->name) == 0) {
446 /* Matched a test name */
448 suiteFilterName = testSuite->name;
450 testFilterName = testCase->name;
451 SDLTest_Log("Filtering: running only test '%s' in suite '%s'", testFilterName, suiteFilterName);
457 if (suiteFilter == 0 && testFilter == 0) {
458 SDLTest_LogError("Filter '%s' did not match any test suite/case.", filter);
459 SDLTest_Log("Exit code: 2");
464 /* Loop over all suites */
466 while(testSuites[suiteCounter]) {
467 testSuite=(SDLTest_TestSuiteReference *)testSuites[suiteCounter];
468 currentSuiteName = (char *)((testSuite->name) ? testSuite->name : SDLTest_InvalidNameFormat);
471 /* Filter suite if flag set and we have a name */
472 if (suiteFilter == 1 && suiteFilterName != NULL && testSuite->name != NULL &&
473 SDL_strcmp(suiteFilterName, testSuite->name) != 0) {
475 SDLTest_Log("===== Test Suite %i: '%s' skipped\n",
480 /* Reset per-suite counters */
483 testSkippedCount = 0;
485 /* Take time - suite start */
486 suiteStartSeconds = GetClock();
488 /* Log suite started */
489 SDLTest_Log("===== Test Suite %i: '%s' started\n",
493 /* Loop over all test cases */
495 while(testSuite->testCases[testCounter])
497 testCase=(SDLTest_TestCaseReference *)testSuite->testCases[testCounter];
498 currentTestName = (char *)((testCase->name) ? testCase->name : SDLTest_InvalidNameFormat);
501 /* Filter tests if flag set and we have a name */
502 if (testFilter == 1 && testFilterName != NULL && testCase->name != NULL &&
503 SDL_strcmp(testFilterName, testCase->name) != 0) {
505 SDLTest_Log("===== Test Case %i.%i: '%s' skipped\n",
510 /* Override 'disabled' flag if we specified a test filter (i.e. force run for debugging) */
511 if (testFilter == 1 && !testCase->enabled) {
512 SDLTest_Log("Force run of disabled test since test filter was set");
513 testCase->enabled = 1;
516 /* Take time - test start */
517 testStartSeconds = GetClock();
519 /* Log test started */
520 SDLTest_Log("----- Test Case %i.%i: '%s' started",
524 if (testCase->description != NULL && testCase->description[0] != '\0') {
525 SDLTest_Log("Test Description: '%s'",
526 (testCase->description) ? testCase->description : SDLTest_InvalidNameFormat);
529 /* Loop over all iterations */
530 iterationCounter = 0;
531 while(iterationCounter < testIterations)
535 if (userExecKey != 0) {
536 execKey = userExecKey;
538 execKey = SDLTest_GenerateExecKey((char *)runSeed, testSuite->name, testCase->name, iterationCounter);
541 SDLTest_Log("Test Iteration %i: execKey %llu", iterationCounter, execKey);
542 testResult = SDLTest_RunTest(testSuite, testCase, execKey);
544 if (testResult == TEST_RESULT_PASSED) {
546 totalTestPassedCount++;
547 } else if (testResult == TEST_RESULT_SKIPPED) {
549 totalTestSkippedCount++;
552 totalTestFailedCount++;
556 /* Take time - test end */
557 testEndSeconds = GetClock();
558 runtime = testEndSeconds - testStartSeconds;
559 if (runtime < 0.0f) runtime = 0.0f;
561 if (testIterations > 1) {
562 /* Log test runtime */
563 SDLTest_Log("Runtime of %i iterations: %.1f sec", testIterations, runtime);
564 SDLTest_Log("Average Test runtime: %.5f sec", runtime / (float)testIterations);
566 /* Log test runtime */
567 SDLTest_Log("Total Test runtime: %.1f sec", runtime);
570 /* Log final test result */
571 switch (testResult) {
572 case TEST_RESULT_PASSED:
573 SDLTest_Log((char *)SDLTest_FinalResultFormat, "Test", currentTestName, "Passed");
575 case TEST_RESULT_FAILED:
576 SDLTest_LogError((char *)SDLTest_FinalResultFormat, "Test", currentTestName, "Failed");
578 case TEST_RESULT_NO_ASSERT:
579 SDLTest_LogError((char *)SDLTest_FinalResultFormat,"Test", currentTestName, "No Asserts");
586 /* Take time - suite end */
587 suiteEndSeconds = GetClock();
588 runtime = suiteEndSeconds - suiteStartSeconds;
589 if (runtime < 0.0f) runtime = 0.0f;
591 /* Log suite runtime */
592 SDLTest_Log("Total Suite runtime: %.1f sec", runtime);
594 /* Log summary and final Suite result */
595 countSum = testPassedCount + testFailedCount + testSkippedCount;
596 if (testFailedCount == 0)
598 SDLTest_Log(logFormat, "Suite", countSum, testPassedCount, testFailedCount, testSkippedCount);
599 SDLTest_Log((char *)SDLTest_FinalResultFormat, "Suite", currentSuiteName, "Passed");
603 SDLTest_LogError(logFormat, "Suite", countSum, testPassedCount, testFailedCount, testSkippedCount);
604 SDLTest_LogError((char *)SDLTest_FinalResultFormat, "Suite", currentSuiteName, "Failed");
610 /* Take time - run end */
611 runEndSeconds = GetClock();
612 runtime = runEndSeconds - runStartSeconds;
613 if (runtime < 0.0f) runtime = 0.0f;
615 /* Log total runtime */
616 SDLTest_Log("Total Run runtime: %.1f sec", runtime);
618 /* Log summary and final run result */
619 countSum = totalTestPassedCount + totalTestFailedCount + totalTestSkippedCount;
620 if (totalTestFailedCount == 0)
623 SDLTest_Log(logFormat, "Run", countSum, totalTestPassedCount, totalTestFailedCount, totalTestSkippedCount);
624 SDLTest_Log((char *)SDLTest_FinalResultFormat, "Run /w seed", runSeed, "Passed");
629 SDLTest_LogError(logFormat, "Run", countSum, totalTestPassedCount, totalTestFailedCount, totalTestSkippedCount);
630 SDLTest_LogError((char *)SDLTest_FinalResultFormat, "Run /w seed", runSeed, "Failed");
633 SDLTest_Log("Exit code: %d", runResult);