/** @file | |
UnitTestLib APIs to run unit tests using cmocka | |
Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdarg.h> | |
#include <stddef.h> | |
#include <setjmp.h> | |
#include <cmocka.h> | |
#include <Uefi.h> | |
#include <UnitTestFrameworkTypes.h> | |
#include <Library/UnitTestLib.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/DebugLib.h> | |
STATIC UNIT_TEST_FRAMEWORK_HANDLE mFrameworkHandle = NULL; | |
UNIT_TEST_FRAMEWORK_HANDLE | |
GetActiveFrameworkHandle ( | |
VOID | |
) | |
{ | |
return mFrameworkHandle; | |
} | |
// | |
// The currently active test suite | |
// | |
UNIT_TEST_SUITE *mActiveUnitTestSuite = NULL; | |
void | |
CmockaUnitTestFunctionRunner ( | |
void **state | |
) | |
{ | |
UNIT_TEST *UnitTest; | |
UNIT_TEST_SUITE *Suite; | |
UNIT_TEST_FRAMEWORK *Framework; | |
UnitTest = (UNIT_TEST *)(*state); | |
Suite = (UNIT_TEST_SUITE *)(UnitTest->ParentSuite); | |
Framework = (UNIT_TEST_FRAMEWORK *)(Suite->ParentFramework); | |
if (UnitTest->RunTest == NULL) { | |
UnitTest->Result = UNIT_TEST_SKIPPED; | |
} else { | |
UnitTest->Result = UNIT_TEST_RUNNING; | |
Framework->CurrentTest = UnitTest; | |
UnitTest->Result = UnitTest->RunTest (UnitTest->Context); | |
Framework->CurrentTest = NULL; | |
} | |
} | |
int | |
CmockaUnitTestSetupFunctionRunner ( | |
void **state | |
) | |
{ | |
UNIT_TEST *UnitTest; | |
UNIT_TEST_SUITE *Suite; | |
UNIT_TEST_FRAMEWORK *Framework; | |
UNIT_TEST_STATUS Result; | |
UnitTest = (UNIT_TEST *)(*state); | |
Suite = (UNIT_TEST_SUITE *)(UnitTest->ParentSuite); | |
Framework = (UNIT_TEST_FRAMEWORK *)(Suite->ParentFramework); | |
if (UnitTest->Prerequisite == NULL) { | |
return 0; | |
} | |
Framework->CurrentTest = UnitTest; | |
Result = UnitTest->Prerequisite (UnitTest->Context); | |
Framework->CurrentTest = NULL; | |
// | |
// Return 0 for success. Non-zero for error. | |
// | |
return (int)Result; | |
} | |
int | |
CmockaUnitTestTeardownFunctionRunner ( | |
void **state | |
) | |
{ | |
UNIT_TEST *UnitTest; | |
UNIT_TEST_SUITE *Suite; | |
UNIT_TEST_FRAMEWORK *Framework; | |
UnitTest = (UNIT_TEST *)(*state); | |
Suite = (UNIT_TEST_SUITE *)(UnitTest->ParentSuite); | |
Framework = (UNIT_TEST_FRAMEWORK *)(Suite->ParentFramework); | |
if (UnitTest->CleanUp != NULL) { | |
Framework->CurrentTest = UnitTest; | |
UnitTest->CleanUp (UnitTest->Context); | |
Framework->CurrentTest = NULL; | |
} | |
// | |
// Print out the log messages - This is a partial solution as it | |
// does not get the log into the XML. Need cmocka changes to support | |
// stdout and stderr in their xml format | |
// | |
if (UnitTest->Log != NULL) { | |
print_message ("UnitTest: %s - %s\n", UnitTest->Name, UnitTest->Description); | |
print_message ("Log Output Start\n"); | |
print_message ("%s", UnitTest->Log); | |
print_message ("Log Output End\n"); | |
} | |
// | |
// Return 0 for success. Non-zero for error. | |
// | |
return 0; | |
} | |
int | |
CmockaUnitTestSuiteSetupFunctionRunner ( | |
void **state | |
) | |
{ | |
if (mActiveUnitTestSuite == NULL) { | |
return -1; | |
} | |
if (mActiveUnitTestSuite->Setup == NULL) { | |
return 0; | |
} | |
mActiveUnitTestSuite->Setup (); | |
// | |
// Always succeed | |
// | |
return 0; | |
} | |
int | |
CmockaUnitTestSuiteTeardownFunctionRunner ( | |
void **state | |
) | |
{ | |
if (mActiveUnitTestSuite == NULL) { | |
return -1; | |
} | |
if (mActiveUnitTestSuite->Teardown == NULL) { | |
return 0; | |
} | |
mActiveUnitTestSuite->Teardown (); | |
// | |
// Always succeed | |
// | |
return 0; | |
} | |
STATIC | |
EFI_STATUS | |
RunTestSuite ( | |
IN UNIT_TEST_SUITE *Suite | |
) | |
{ | |
UNIT_TEST_LIST_ENTRY *TestEntry; | |
UNIT_TEST *UnitTest; | |
struct CMUnitTest *Tests; | |
UINTN Index; | |
TestEntry = NULL; | |
if (Suite == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
DEBUG ((DEBUG_VERBOSE, "---------------------------------------------------------\n")); | |
DEBUG ((DEBUG_VERBOSE, "RUNNING TEST SUITE: %a\n", Suite->Title)); | |
DEBUG ((DEBUG_VERBOSE, "---------------------------------------------------------\n")); | |
// | |
// Allocate buffer of CMUnitTest entries | |
// | |
Tests = AllocateZeroPool (Suite->NumTests * sizeof (struct CMUnitTest)); | |
ASSERT (Tests != NULL); | |
// | |
// Populate buffer of CMUnitTest entries | |
// | |
Index = 0; | |
for (TestEntry = (UNIT_TEST_LIST_ENTRY *)GetFirstNode (&(Suite->TestCaseList)); | |
(LIST_ENTRY *)TestEntry != &(Suite->TestCaseList); | |
TestEntry = (UNIT_TEST_LIST_ENTRY *)GetNextNode (&(Suite->TestCaseList), (LIST_ENTRY *)TestEntry)) | |
{ | |
UnitTest = &TestEntry->UT; | |
Tests[Index].name = UnitTest->Description; | |
Tests[Index].test_func = CmockaUnitTestFunctionRunner; | |
Tests[Index].setup_func = CmockaUnitTestSetupFunctionRunner; | |
Tests[Index].teardown_func = CmockaUnitTestTeardownFunctionRunner; | |
Tests[Index].initial_state = UnitTest; | |
Index++; | |
} | |
ASSERT (Index == Suite->NumTests); | |
// | |
// Run all unit tests in a test suite | |
// | |
mActiveUnitTestSuite = Suite; | |
_cmocka_run_group_tests ( | |
Suite->Title, | |
Tests, | |
Suite->NumTests, | |
CmockaUnitTestSuiteSetupFunctionRunner, | |
CmockaUnitTestSuiteTeardownFunctionRunner | |
); | |
mActiveUnitTestSuite = NULL; | |
FreePool (Tests); | |
return EFI_SUCCESS; | |
} | |
/** | |
Execute all unit test cases in all unit test suites added to a Framework. | |
Once a unit test framework is initialized and all unit test suites and unit | |
test cases are registered, this function will cause the unit test framework to | |
dispatch all unit test cases in sequence and record the results for reporting. | |
@param[in] FrameworkHandle A handle to the current running framework that | |
dispatched the test. Necessary for recording | |
certain test events with the framework. | |
@retval EFI_SUCCESS All test cases were dispatched. | |
@retval EFI_INVALID_PARAMETER FrameworkHandle is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
RunAllTestSuites ( | |
IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle | |
) | |
{ | |
UNIT_TEST_FRAMEWORK *Framework; | |
UNIT_TEST_SUITE_LIST_ENTRY *Suite; | |
EFI_STATUS Status; | |
Framework = (UNIT_TEST_FRAMEWORK *)FrameworkHandle; | |
Suite = NULL; | |
if (Framework == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
DEBUG ((DEBUG_VERBOSE, "---------------------------------------------------------\n")); | |
DEBUG ((DEBUG_VERBOSE, "------------ RUNNING ALL TEST SUITES --------------\n")); | |
DEBUG ((DEBUG_VERBOSE, "---------------------------------------------------------\n")); | |
mFrameworkHandle = FrameworkHandle; | |
// | |
// Iterate all suites | |
// | |
for (Suite = (UNIT_TEST_SUITE_LIST_ENTRY *)GetFirstNode (&Framework->TestSuiteList); | |
(LIST_ENTRY *)Suite != &Framework->TestSuiteList; | |
Suite = (UNIT_TEST_SUITE_LIST_ENTRY *)GetNextNode (&Framework->TestSuiteList, (LIST_ENTRY *)Suite)) | |
{ | |
Status = RunTestSuite (&(Suite->UTS)); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "Test Suite Failed with Error. %r\n", Status)); | |
} | |
} | |
mFrameworkHandle = NULL; | |
return EFI_SUCCESS; | |
} |