// SPDX-License-Identifier: GPL-2.0
|
/*
|
* Copyright (c) 2014, STMicroelectronics International N.V.
|
*/
|
|
/*************************************************************************
|
* 1. Includes
|
*************************************************************************/
|
#include "adbg_int.h"
|
|
/*************************************************************************
|
* 2. Definition of external constants and variables
|
*************************************************************************/
|
|
/*************************************************************************
|
* 3. File scope types, constants and variables
|
*************************************************************************/
|
|
/*************************************************************************
|
* 4. Declaration of file local functions
|
*************************************************************************/
|
|
/*
|
* Deletes a subcase. Don't call this function before the
|
* subcase is removed from list.
|
*/
|
static void ADBG_SubCase_Delete(ADBG_SubCase_t *SubCase);
|
|
static ADBG_SubCase_t *ADBG_Case_CreateSubCase(ADBG_Case_t *Case_p,
|
const char *const Title_p);
|
|
static ADBG_SubCase_t *ADBG_Case_GetParentSubCase(ADBG_Case_t *Case_p,
|
ADBG_SubCase_t *SubCase_p);
|
|
static const char *ADBG_Case_GetTestID(ADBG_Case_t *Case_p);
|
|
/*************************************************************************
|
* 5. Definition of external functions
|
*************************************************************************/
|
ADBG_Case_t *ADBG_Case_New(const struct adbg_case_def *case_def)
|
{
|
ADBG_Case_t *Case_p = NULL;
|
|
Case_p = calloc(1, sizeof(*Case_p));
|
if (Case_p)
|
Case_p->case_def = case_def;
|
|
return Case_p;
|
}
|
|
void ADBG_Case_Delete(ADBG_Case_t *Case_p)
|
{
|
ADBG_SubCase_Delete(Case_p->FirstSubCase_p);
|
free(Case_p);
|
}
|
|
bool ADBG_Case_SubCaseIsMain(
|
const ADBG_Case_t *const Case_p,
|
const ADBG_SubCase_t *const SubCase_p
|
)
|
{
|
IDENTIFIER_NOT_USED(Case_p)
|
return SubCase_p->Parent_p == NULL;
|
}
|
|
|
void ADBG_Case_IterateSubCase(
|
ADBG_Case_t *Case_p,
|
ADBG_SubCase_Iterator_t *Iterator_p
|
)
|
{
|
Iterator_p->Case_p = Case_p;
|
Iterator_p->CurrentSubCase_p = NULL;
|
}
|
|
ADBG_SubCase_t *ADBG_Case_NextSubCase(
|
ADBG_SubCase_Iterator_t *Iterator_p
|
)
|
{
|
ADBG_Case_t *Case_p = Iterator_p->Case_p;
|
ADBG_SubCase_t *SubCase_p = Iterator_p->CurrentSubCase_p;
|
|
|
/*
|
* Traverse the subcases depth first, that is:
|
* 1.1.1.1
|
* 1.1.1.2
|
* 1.1.1
|
* 1.1.2.1
|
* 1.1.2
|
* 1.1
|
* 1.2.1
|
* 1.2
|
* 1
|
*/
|
if (SubCase_p == NULL) {
|
/* Find the first leaf */
|
SubCase_p = Case_p->FirstSubCase_p;
|
if (SubCase_p == NULL)
|
goto CleanupReturn;
|
|
while (!TAILQ_EMPTY(&SubCase_p->SubCasesList))
|
SubCase_p = TAILQ_FIRST(&SubCase_p->SubCasesList);
|
goto CleanupReturn;
|
}
|
|
/*
|
* Look for the next leaf belonging to the parent
|
*/
|
|
if (SubCase_p->Parent_p == NULL) {
|
/* If parent is NULL this is the top
|
subcase and we're done */
|
SubCase_p = NULL;
|
goto CleanupReturn;
|
}
|
|
if (TAILQ_NEXT(SubCase_p, Link) == NULL) {
|
/* If this is the last subcase of the
|
parent move up to parent */
|
SubCase_p = SubCase_p->Parent_p;
|
goto CleanupReturn;
|
}
|
|
/*
|
* Find next leaf
|
*/
|
SubCase_p = TAILQ_NEXT(SubCase_p, Link);
|
while (!TAILQ_EMPTY(&SubCase_p->SubCasesList))
|
SubCase_p = TAILQ_FIRST(&SubCase_p->SubCasesList);
|
|
CleanupReturn:
|
Iterator_p->CurrentSubCase_p = SubCase_p;
|
return SubCase_p;
|
}
|
|
void Do_ADBG_BeginSubCase(
|
ADBG_Case_t *const Case_p,
|
const char *const FormatTitle_p, ...
|
)
|
{
|
ADBG_SubCase_t *SubCase_p = NULL;
|
|
if (Case_p == NULL) {
|
Do_ADBG_Log("Do_ADBG_BeginSubCase: NULL Case_p!");
|
return;
|
}
|
|
if (FormatTitle_p == NULL) {
|
Do_ADBG_Log("Do_ADBG_BeginSubCase: NULL FormatTitle_p!");
|
return;
|
}
|
|
va_list ArgList;
|
char Title[80] = { };
|
|
va_start(ArgList, FormatTitle_p);
|
vsnprintf(Title, sizeof(Title), FormatTitle_p, ArgList);
|
va_end(ArgList);
|
|
SubCase_p = ADBG_Case_CreateSubCase(Case_p, Title);
|
|
if (SubCase_p == NULL) {
|
Do_ADBG_Log("Do_ADBG_BeginSubCase: HEAP_ALLOC failed");
|
return;
|
}
|
|
|
if (ADBG_Case_SubCaseIsMain(Case_p, SubCase_p)) {
|
/* Main SubCase */
|
Do_ADBG_Log(" ");
|
Do_ADBG_Log("* %s %s", SubCase_p->TestID_p, SubCase_p->Title_p);
|
} else {
|
Do_ADBG_Log("o %s %s", SubCase_p->TestID_p, SubCase_p->Title_p);
|
}
|
}
|
|
void Do_ADBG_EndSubCase(
|
ADBG_Case_t *const Case_p,
|
const char *const FormatTitle_p, ...
|
)
|
{
|
va_list ArgList;
|
char Title[80] = { };
|
ADBG_SubCase_t *SubCase_p = NULL;
|
|
if (Case_p == NULL) {
|
Do_ADBG_Log("Do_ADBG_EndSubCase: NULL Case_p!");
|
return;
|
}
|
|
if (FormatTitle_p == NULL) {
|
strcpy(Title, "NULL");
|
} else {
|
va_start(ArgList, FormatTitle_p);
|
vsnprintf(Title, sizeof(Title), FormatTitle_p, ArgList);
|
va_end(ArgList);
|
}
|
|
|
SubCase_p = Case_p->CurrentSubCase_p;
|
|
if (SubCase_p == NULL) {
|
Do_ADBG_Log("Do_ADBG_EndSubCase: "
|
"Have no active SubCase, bailing out for title \"%s\"",
|
Title);
|
return;
|
}
|
|
if (FormatTitle_p != NULL && strcmp(SubCase_p->Title_p, Title) != 0) {
|
Do_ADBG_Log("Do_ADBG_EndSubCase: "
|
"Active SubCase \"%s\" doesn't match supplied title \"%s\"",
|
SubCase_p->Title_p, Title);
|
return;
|
}
|
|
if (ADBG_Case_SubCaseIsMain(Case_p, SubCase_p)) {
|
if (FormatTitle_p == NULL) {
|
/* To end the main subcase we require
|
a matching title */
|
Do_ADBG_Log("Do_ADBG_EndSubCase: "
|
"The main SubCase \"%s\" doesn't match supplied title \"%s\"",
|
SubCase_p->Title_p, Title);
|
return;
|
}
|
/*
|
* Ending the main subcase
|
* make a complete copy of the aggregated result.
|
*/
|
Case_p->Result = SubCase_p->Result;
|
} else {
|
/*
|
* Ending a subcase,
|
* Aggregate results to parent.
|
*/
|
ADBG_SubCase_t *Parent_p = SubCase_p->Parent_p;
|
|
Parent_p->Result.NumSubTests += SubCase_p->Result.NumTests +
|
SubCase_p->Result.NumSubTests;
|
Parent_p->Result.NumFailedSubTests +=
|
SubCase_p->Result.NumFailedTests +
|
SubCase_p->Result.
|
NumFailedSubTests;
|
Parent_p->Result.AbortTestSuite =
|
SubCase_p->Result.AbortTestSuite;
|
if (SubCase_p->Result.NumTests > 0 ||
|
SubCase_p->Result.NumSubTests > 0)
|
Parent_p->Result.NumFailedSubCases++;
|
}
|
|
/* Print a summary of the subcase result */
|
if (SubCase_p->Result.NumFailedTests > 0 ||
|
SubCase_p->Result.NumFailedSubTests > 0) {
|
Do_ADBG_Log(" %s FAILED", SubCase_p->TestID_p);
|
} else {
|
Do_ADBG_Log(" %s OK", SubCase_p->TestID_p);
|
}
|
|
/* Update current subcase to be the parent of this subcase */
|
Case_p->CurrentSubCase_p =
|
ADBG_Case_GetParentSubCase(Case_p, SubCase_p);
|
}
|
|
/*************************************************************************
|
* 6. Definition of internal functions
|
*************************************************************************/
|
static ADBG_SubCase_t *ADBG_Case_CreateSubCase(
|
ADBG_Case_t *Case_p,
|
const char *const Title_p
|
)
|
{
|
ADBG_SubCase_t *SubCase_p = NULL;
|
|
SubCase_p = calloc(1, sizeof(*SubCase_p));
|
if (SubCase_p == NULL)
|
goto ErrorReturn;
|
|
TAILQ_INIT(&SubCase_p->SubCasesList);
|
|
SubCase_p->Title_p = strdup(Title_p);
|
if (SubCase_p->Title_p == NULL)
|
goto ErrorReturn;
|
|
/* Set parent pointer needed "early" below. */
|
SubCase_p->Parent_p = Case_p->CurrentSubCase_p;
|
|
if (SubCase_p->Parent_p == NULL) {
|
/* Main SubCase */
|
SubCase_p->TestID_p = strdup(ADBG_Case_GetTestID(Case_p));
|
if (SubCase_p->TestID_p == NULL)
|
goto ErrorReturn;
|
|
Case_p->FirstSubCase_p = SubCase_p;
|
} else {
|
ADBG_SubCase_t *Parent_p = SubCase_p->Parent_p;
|
char PrefixTitle[80] = { };
|
|
/* Update parent SubCase */
|
Parent_p->Result.NumSubCases++;
|
snprintf(PrefixTitle, sizeof(PrefixTitle), "%s.%d",
|
Parent_p->TestID_p, Parent_p->Result.NumSubCases);
|
SubCase_p->TestID_p = strdup(PrefixTitle);
|
if (SubCase_p->TestID_p == NULL)
|
goto ErrorReturn;
|
|
TAILQ_INSERT_TAIL(&Parent_p->SubCasesList, SubCase_p, Link);
|
}
|
|
Case_p->CurrentSubCase_p = SubCase_p;
|
return SubCase_p;
|
|
ErrorReturn:
|
ADBG_SubCase_Delete(SubCase_p);
|
return NULL;
|
}
|
|
static void ADBG_SubCase_Delete(
|
ADBG_SubCase_t *SubCase_p
|
)
|
{
|
if (SubCase_p != NULL) {
|
/*
|
* Note that Util_ListDestroy() checks
|
* if SubCase_p->SubCasesList_p
|
* is NULL.
|
*/
|
while (true) {
|
ADBG_SubCase_t *s =
|
TAILQ_FIRST(&SubCase_p->SubCasesList);
|
|
if (s == NULL)
|
break;
|
|
TAILQ_REMOVE(&SubCase_p->SubCasesList, s, Link);
|
ADBG_SubCase_Delete(s);
|
}
|
free(SubCase_p->TestID_p);
|
free(SubCase_p->Title_p);
|
free(SubCase_p);
|
}
|
}
|
|
ADBG_SubCase_t *ADBG_Case_GetParentSubCase(
|
ADBG_Case_t *Case_p,
|
ADBG_SubCase_t *SubCase_p
|
)
|
{
|
IDENTIFIER_NOT_USED(Case_p)
|
IDENTIFIER_NOT_USED(SubCase_p)
|
return SubCase_p->Parent_p;
|
}
|
|
static const char *ADBG_Case_GetTestID(ADBG_Case_t *Case_p)
|
{
|
IDENTIFIER_NOT_USED(Case_p)
|
|
return Case_p->case_def->TestID_p;
|
}
|