1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2014, STMicroelectronics International N.V.
4  */
5 
6 /*************************************************************************
7 * 1. Includes
8 *************************************************************************/
9 #include "adbg_int.h"
10 
11 /*************************************************************************
12 * 2. Definition of external constants and variables
13 *************************************************************************/
14 
15 /*************************************************************************
16 * 3. File scope types, constants and variables
17 *************************************************************************/
18 
19 /*************************************************************************
20 * 4. Declaration of file local functions
21 *************************************************************************/
22 
23 /*
24  * Deletes a subcase. Don't call this function before the
25  * subcase is removed from list.
26  */
27 static void ADBG_SubCase_Delete(ADBG_SubCase_t *SubCase);
28 
29 static ADBG_SubCase_t *ADBG_Case_CreateSubCase(ADBG_Case_t *Case_p,
30 					       const char *const Title_p);
31 
32 static ADBG_SubCase_t *ADBG_Case_GetParentSubCase(ADBG_Case_t *Case_p,
33 						  ADBG_SubCase_t *SubCase_p);
34 
35 static const char *ADBG_Case_GetTestID(ADBG_Case_t *Case_p);
36 
37 /*************************************************************************
38 * 5. Definition of external functions
39 *************************************************************************/
ADBG_Case_New(const struct adbg_case_def * case_def)40 ADBG_Case_t *ADBG_Case_New(const struct adbg_case_def *case_def)
41 {
42 	ADBG_Case_t *Case_p = NULL;
43 
44 	Case_p = calloc(1, sizeof(*Case_p));
45 	if (Case_p)
46 		Case_p->case_def = case_def;
47 
48 	return Case_p;
49 }
50 
ADBG_Case_Delete(ADBG_Case_t * Case_p)51 void ADBG_Case_Delete(ADBG_Case_t *Case_p)
52 {
53 	ADBG_SubCase_Delete(Case_p->FirstSubCase_p);
54 	free(Case_p);
55 }
56 
ADBG_Case_SubCaseIsMain(const ADBG_Case_t * const Case_p,const ADBG_SubCase_t * const SubCase_p)57 bool ADBG_Case_SubCaseIsMain(
58 	const ADBG_Case_t *const Case_p,
59 	const ADBG_SubCase_t *const SubCase_p
60 	)
61 {
62 	IDENTIFIER_NOT_USED(Case_p)
63 	return SubCase_p->Parent_p == NULL;
64 }
65 
66 
ADBG_Case_IterateSubCase(ADBG_Case_t * Case_p,ADBG_SubCase_Iterator_t * Iterator_p)67 void ADBG_Case_IterateSubCase(
68 	ADBG_Case_t *Case_p,
69 	ADBG_SubCase_Iterator_t *Iterator_p
70 	)
71 {
72 	Iterator_p->Case_p = Case_p;
73 	Iterator_p->CurrentSubCase_p = NULL;
74 }
75 
ADBG_Case_NextSubCase(ADBG_SubCase_Iterator_t * Iterator_p)76 ADBG_SubCase_t *ADBG_Case_NextSubCase(
77 	ADBG_SubCase_Iterator_t *Iterator_p
78 	)
79 {
80 	ADBG_Case_t *Case_p = Iterator_p->Case_p;
81 	ADBG_SubCase_t *SubCase_p = Iterator_p->CurrentSubCase_p;
82 
83 
84 	/*
85 	 * Traverse the subcases depth first, that is:
86 	 * 1.1.1.1
87 	 * 1.1.1.2
88 	 * 1.1.1
89 	 * 1.1.2.1
90 	 * 1.1.2
91 	 * 1.1
92 	 * 1.2.1
93 	 * 1.2
94 	 * 1
95 	 */
96 	if (SubCase_p == NULL) {
97 		/* Find the first leaf */
98 		SubCase_p = Case_p->FirstSubCase_p;
99 		if (SubCase_p == NULL)
100 			goto CleanupReturn;
101 
102 		while (!TAILQ_EMPTY(&SubCase_p->SubCasesList))
103 			SubCase_p = TAILQ_FIRST(&SubCase_p->SubCasesList);
104 		goto CleanupReturn;
105 	}
106 
107 	/*
108 	 * Look for the next leaf belonging to the parent
109 	 */
110 
111 	if (SubCase_p->Parent_p == NULL) {
112 		/* If parent is NULL this is the top
113 			subcase and we're done */
114 		SubCase_p = NULL;
115 		goto CleanupReturn;
116 	}
117 
118 	if (TAILQ_NEXT(SubCase_p, Link) == NULL) {
119 		/* If this is the last subcase of the
120 			parent move up to parent */
121 		SubCase_p = SubCase_p->Parent_p;
122 		goto CleanupReturn;
123 	}
124 
125 	/*
126 	 * Find next leaf
127 	 */
128 	SubCase_p = TAILQ_NEXT(SubCase_p, Link);
129 	while (!TAILQ_EMPTY(&SubCase_p->SubCasesList))
130 		SubCase_p = TAILQ_FIRST(&SubCase_p->SubCasesList);
131 
132 CleanupReturn:
133 	Iterator_p->CurrentSubCase_p = SubCase_p;
134 	return SubCase_p;
135 }
136 
Do_ADBG_BeginSubCase(ADBG_Case_t * const Case_p,const char * const FormatTitle_p,...)137 void Do_ADBG_BeginSubCase(
138 	ADBG_Case_t *const Case_p,
139 	const char *const FormatTitle_p, ...
140 	)
141 {
142 	ADBG_SubCase_t *SubCase_p = NULL;
143 
144 	if (Case_p == NULL) {
145 		Do_ADBG_Log("Do_ADBG_BeginSubCase: NULL Case_p!");
146 		return;
147 	}
148 
149 	if (FormatTitle_p == NULL) {
150 		Do_ADBG_Log("Do_ADBG_BeginSubCase: NULL FormatTitle_p!");
151 		return;
152 	}
153 
154 	va_list ArgList;
155 	char Title[80] = { };
156 
157 	va_start(ArgList, FormatTitle_p);
158 	vsnprintf(Title, sizeof(Title), FormatTitle_p, ArgList);
159 	va_end(ArgList);
160 
161 	SubCase_p = ADBG_Case_CreateSubCase(Case_p, Title);
162 
163 	if (SubCase_p == NULL) {
164 		Do_ADBG_Log("Do_ADBG_BeginSubCase: HEAP_ALLOC failed");
165 		return;
166 	}
167 
168 
169 	if (ADBG_Case_SubCaseIsMain(Case_p, SubCase_p)) {
170 		/* Main SubCase */
171 		Do_ADBG_Log(" ");
172 		Do_ADBG_Log("* %s %s", SubCase_p->TestID_p, SubCase_p->Title_p);
173 	} else {
174 		Do_ADBG_Log("o %s %s", SubCase_p->TestID_p, SubCase_p->Title_p);
175 	}
176 }
177 
Do_ADBG_EndSubCase(ADBG_Case_t * const Case_p,const char * const FormatTitle_p,...)178 void Do_ADBG_EndSubCase(
179 	ADBG_Case_t *const Case_p,
180 	const char *const FormatTitle_p, ...
181 	)
182 {
183 	va_list ArgList;
184 	char Title[80] = { };
185 	ADBG_SubCase_t *SubCase_p = NULL;
186 
187 	if (Case_p == NULL) {
188 		Do_ADBG_Log("Do_ADBG_EndSubCase: NULL Case_p!");
189 		return;
190 	}
191 
192 	if (FormatTitle_p == NULL) {
193 		strcpy(Title, "NULL");
194 	} else {
195 		va_start(ArgList, FormatTitle_p);
196 		vsnprintf(Title, sizeof(Title), FormatTitle_p, ArgList);
197 		va_end(ArgList);
198 	}
199 
200 
201 	SubCase_p = Case_p->CurrentSubCase_p;
202 
203 	if (SubCase_p == NULL) {
204 		Do_ADBG_Log("Do_ADBG_EndSubCase: "
205 			    "Have no active SubCase, bailing out for title \"%s\"",
206 			    Title);
207 		return;
208 	}
209 
210 	if (FormatTitle_p != NULL && strcmp(SubCase_p->Title_p, Title) != 0) {
211 		Do_ADBG_Log("Do_ADBG_EndSubCase: "
212 			    "Active SubCase \"%s\" doesn't match supplied title \"%s\"",
213 			    SubCase_p->Title_p, Title);
214 		return;
215 	}
216 
217 	if (ADBG_Case_SubCaseIsMain(Case_p, SubCase_p)) {
218 		if (FormatTitle_p == NULL) {
219 			/* To end the main subcase we require
220 				a matching title */
221 			Do_ADBG_Log("Do_ADBG_EndSubCase: "
222 				    "The main SubCase \"%s\" doesn't match supplied title \"%s\"",
223 				    SubCase_p->Title_p, Title);
224 			return;
225 		}
226 		/*
227 		 * Ending the main subcase
228 		 * make a complete copy of the aggregated result.
229 		 */
230 		Case_p->Result = SubCase_p->Result;
231 	} else {
232 		/*
233 		 * Ending a subcase,
234 		 * Aggregate results to parent.
235 		 */
236 		ADBG_SubCase_t *Parent_p = SubCase_p->Parent_p;
237 
238 		Parent_p->Result.NumSubTests += SubCase_p->Result.NumTests +
239 						SubCase_p->Result.NumSubTests;
240 		Parent_p->Result.NumFailedSubTests +=
241 			SubCase_p->Result.NumFailedTests +
242 			SubCase_p->Result.
243 			NumFailedSubTests;
244 		Parent_p->Result.AbortTestSuite =
245 			SubCase_p->Result.AbortTestSuite;
246 		if (SubCase_p->Result.NumTests > 0 ||
247 		    SubCase_p->Result.NumSubTests > 0)
248 			Parent_p->Result.NumFailedSubCases++;
249 	}
250 
251 	/* Print a summary of the subcase result */
252 	if (SubCase_p->Result.NumFailedTests > 0 ||
253 	    SubCase_p->Result.NumFailedSubTests > 0) {
254 		Do_ADBG_Log("  %s FAILED", SubCase_p->TestID_p);
255 	} else {
256 		Do_ADBG_Log("  %s OK", SubCase_p->TestID_p);
257 	}
258 
259 	/* Update current subcase to be the parent of this subcase */
260 	Case_p->CurrentSubCase_p =
261 		ADBG_Case_GetParentSubCase(Case_p, SubCase_p);
262 }
263 
264 /*************************************************************************
265 * 6. Definition of internal functions
266 *************************************************************************/
ADBG_Case_CreateSubCase(ADBG_Case_t * Case_p,const char * const Title_p)267 static ADBG_SubCase_t *ADBG_Case_CreateSubCase(
268 	ADBG_Case_t *Case_p,
269 	const char *const Title_p
270 	)
271 {
272 	ADBG_SubCase_t *SubCase_p = NULL;
273 
274 	SubCase_p = calloc(1, sizeof(*SubCase_p));
275 	if (SubCase_p == NULL)
276 		goto ErrorReturn;
277 
278 	TAILQ_INIT(&SubCase_p->SubCasesList);
279 
280 	SubCase_p->Title_p = strdup(Title_p);
281 	if (SubCase_p->Title_p == NULL)
282 		goto ErrorReturn;
283 
284 	/* Set parent pointer needed "early" below. */
285 	SubCase_p->Parent_p = Case_p->CurrentSubCase_p;
286 
287 	if (SubCase_p->Parent_p == NULL) {
288 		/* Main SubCase */
289 		SubCase_p->TestID_p = strdup(ADBG_Case_GetTestID(Case_p));
290 		if (SubCase_p->TestID_p == NULL)
291 			goto ErrorReturn;
292 
293 		Case_p->FirstSubCase_p = SubCase_p;
294 	} else {
295 		ADBG_SubCase_t *Parent_p = SubCase_p->Parent_p;
296 		char PrefixTitle[80] = { };
297 
298 		/* Update parent SubCase */
299 		Parent_p->Result.NumSubCases++;
300 		snprintf(PrefixTitle, sizeof(PrefixTitle), "%s.%d",
301 			 Parent_p->TestID_p, Parent_p->Result.NumSubCases);
302 		SubCase_p->TestID_p = strdup(PrefixTitle);
303 		if (SubCase_p->TestID_p == NULL)
304 			goto ErrorReturn;
305 
306 		TAILQ_INSERT_TAIL(&Parent_p->SubCasesList, SubCase_p, Link);
307 	}
308 
309 	Case_p->CurrentSubCase_p = SubCase_p;
310 	return SubCase_p;
311 
312 ErrorReturn:
313 	ADBG_SubCase_Delete(SubCase_p);
314 	return NULL;
315 }
316 
ADBG_SubCase_Delete(ADBG_SubCase_t * SubCase_p)317 static void ADBG_SubCase_Delete(
318 	ADBG_SubCase_t *SubCase_p
319 	)
320 {
321 	if (SubCase_p != NULL) {
322 		/*
323 		 * Note that Util_ListDestroy() checks
324 		 * if SubCase_p->SubCasesList_p
325 		 * is NULL.
326 		 */
327 		while (true) {
328 			ADBG_SubCase_t *s =
329 				TAILQ_FIRST(&SubCase_p->SubCasesList);
330 
331 			if (s == NULL)
332 				break;
333 
334 			TAILQ_REMOVE(&SubCase_p->SubCasesList, s, Link);
335 			ADBG_SubCase_Delete(s);
336 		}
337 		free(SubCase_p->TestID_p);
338 		free(SubCase_p->Title_p);
339 		free(SubCase_p);
340 	}
341 }
342 
ADBG_Case_GetParentSubCase(ADBG_Case_t * Case_p,ADBG_SubCase_t * SubCase_p)343 ADBG_SubCase_t *ADBG_Case_GetParentSubCase(
344 	ADBG_Case_t *Case_p,
345 	ADBG_SubCase_t *SubCase_p
346 	)
347 {
348 	IDENTIFIER_NOT_USED(Case_p)
349 	IDENTIFIER_NOT_USED(SubCase_p)
350 	return SubCase_p->Parent_p;
351 }
352 
ADBG_Case_GetTestID(ADBG_Case_t * Case_p)353 static const char *ADBG_Case_GetTestID(ADBG_Case_t *Case_p)
354 {
355 	IDENTIFIER_NOT_USED(Case_p)
356 
357 	return Case_p->case_def->TestID_p;
358 }
359