blob: baadb419ef8700ff1732b2aecf84e933b8b9adcf [file] [log] [blame]
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/*************************************************************************
* 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;
}