1 /*-
2  * Copyright (c) 2010, 2020 Oracle and/or its affiliates.  All rights reserved.
3  *
4  * See the file LICENSE for license information.
5  *
6  * $Id$
7  */
8 
9 #include <assert.h>
10 #include <errno.h>
11 #include <setjmp.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <math.h>
16 
17 #include "CuTest.h"
18 
19 /*-------------------------------------------------------------------------*
20  * Functions added by Berkeley DB, that allow more control over which tests
21  * are run. They assume a populated TestSuite array called g_suites
22  * is available globally.
23  *-------------------------------------------------------------------------*/
24 
25 extern TestSuite g_suites[];
RunAllSuites(void)26 int RunAllSuites(void)
27 {
28 	int failCount, i;
29 	CuString *output;
30 	failCount = 0;
31 	for(i = 0; strlen(g_suites[i].name) != 0; i++) {
32 		printf("Running suite %s\n", g_suites[i].name);
33 		output = CuStringNew();
34 		failCount += g_suites[i].fn(output);
35 		printf("%s\n", output->buffer);
36 		CuStringDelete(output);
37 		printf("Finished suite %s\n", g_suites[i].name);
38 	}
39 	return (failCount);
40 }
41 
RunSuite(const char * suite)42 int RunSuite(const char * suite)
43 {
44 	int failCount, i;
45 	CuString *output;
46 	failCount = 0;
47 	for(i = 0; strlen(g_suites[i].name) != 0; i++)
48 		if (strcmp(g_suites[i].name, suite) == 0) {
49 			output = CuStringNew();
50 			failCount = g_suites[i].fn(output);
51 			printf("%s\n", output->buffer);
52 			CuStringDelete(output);
53 			break;
54 		}
55 
56 	return (failCount);
57 }
58 
RunTest(const char * suite,const char * test)59 int RunTest(const char * suite, const char *test)
60 {
61 	fprintf(stderr, "TODO: Implement RunTest: %s:%s.\n", suite, test);
62 	return (1);
63 }
64 
65 /*-------------------------------------------------------------------------*
66  * CuStr
67  *-------------------------------------------------------------------------*/
68 
CuStrAlloc(int size)69 char* CuStrAlloc(int size)
70 {
71 	char* newStr = (char*) malloc( sizeof(char) * (size) );
72 	return newStr;
73 }
74 
CuStrCopy(const char * old)75 char* CuStrCopy(const char* old)
76 {
77 	int len = (int)strlen(old);
78 	char* newStr = CuStrAlloc(len + 1);
79 	if (newStr != NULL)
80 		strcpy(newStr, old);
81 	else
82 		fprintf(stderr, "%s: malloc in CuStrCopy.\n", CU_FAIL_HEADER);
83 	return newStr;
84 }
85 
86 /*-------------------------------------------------------------------------*
87  * CuString
88  *-------------------------------------------------------------------------*/
89 
CuStringInit(CuString * str)90 void CuStringInit(CuString* str)
91 {
92 	str->length = 0;
93 	str->size = STRING_MAX;
94 	str->buffer = (char*) malloc(sizeof(char) * str->size);
95 	str->buffer[0] = '\0';
96 }
97 
CuStringNew(void)98 CuString* CuStringNew(void)
99 {
100 	CuString* str = (CuString*) malloc(sizeof(CuString));
101 	CuStringInit(str);
102 	return str;
103 }
104 
CuStringDelete(CuString * str)105 void CuStringDelete(CuString *str)
106 {
107         if (!str) return;
108         free(str->buffer);
109         free(str);
110 }
111 
CuStringResize(CuString * str,int newSize)112 int CuStringResize(CuString* str, int newSize)
113 {
114 	char *newStr;
115 	newStr = (char*) realloc(str->buffer, sizeof(char) * newSize);
116 	if (newStr != 0) {
117 		str->size = newSize;
118 		str->buffer = newStr;
119 		return (0);
120 	}
121 	return (ENOMEM);
122 }
123 
CuStringAppend(CuString * str,const char * text,int dump)124 int CuStringAppend(CuString* str, const char* text, int dump)
125 {
126 	int length;
127 
128 	if (text == NULL) {
129 		text = "NULL";
130 	}
131 
132 	length = (int)strlen(text);
133 	if ((str->length + length + 1 >= str->size)  &&
134 	    CuStringResize(str, str->length + length + 1 + STRING_INC) != 0) {
135 		if (dump) {
136 			fprintf(stderr, "%s:%s\n%s\n", CU_FAIL_HEADER,
137 			    "String append in test framework failed due to"
138 			    "malloc failure. Outputting appended text instead.",
139 			    text);
140 		}
141 		return (ENOMEM);
142 	}
143 	str->length += length;
144 	strcat(str->buffer, text);
145 	return (0);
146 }
147 
CuStringAppendChar(CuString * str,char ch,int dump)148 int CuStringAppendChar(CuString* str, char ch, int dump)
149 {
150 	char text[2];
151 	text[0] = ch;
152 	text[1] = '\0';
153 	return (CuStringAppend(str, text, dump));
154 }
155 
CuStringAppendFormat(CuString * str,int dump,const char * format,...)156 int CuStringAppendFormat(CuString* str, int dump, const char* format, ...)
157 {
158 	va_list argp;
159 	char buf[HUGE_STRING_LEN];
160 	va_start(argp, format);
161 	vsprintf(buf, format, argp);
162 	va_end(argp);
163 	return (CuStringAppend(str, buf, dump));
164 }
165 
CuStringInsert(CuString * str,const char * text,int pos,int dump)166 int CuStringInsert(CuString* str, const char* text, int pos, int dump)
167 {
168 	int length = (int)strlen(text);
169 	if (pos > str->length)
170 		pos = str->length;
171 	if ((str->length + length + 1 >= str->size) &&
172 	    CuStringResize(str, str->length + length + 1 + STRING_INC) != 0) {
173 		if (dump) {
174 			fprintf(stderr, "%s:%s\n%s\n", CU_FAIL_HEADER,
175 			    "String append in test framework failed due to"
176 			    "malloc failure. Outputting appended text instead.",
177 			    text);
178 		}
179 		return (ENOMEM);
180 	}
181 	memmove(str->buffer + pos + length, str->buffer + pos,
182 	    (str->length - pos) + 1);
183 	str->length += length;
184 	memcpy(str->buffer + pos, text, length);
185 	return (0);
186 }
187 
188 /*-------------------------------------------------------------------------*
189  * CuTest
190  *-------------------------------------------------------------------------*/
191 
CuTestInit(CuTest * t,const char * name,TestFunction function,TestSetupFunction setup,TestTeardownFunction teardown)192 void CuTestInit(CuTest* t, const char* name, TestFunction function, TestSetupFunction setup, TestTeardownFunction teardown)
193 {
194 	t->name = CuStrCopy(name);
195 	t->failed = 0;
196 	t->ran = 0;
197 	t->message = NULL;
198 	t->function = function;
199 	t->jumpBuf = NULL;
200 	t->TestSetup = setup;
201 	t->TestTeardown = teardown;
202 }
203 
CuTestNew(const char * name,TestFunction function,TestSetupFunction setup,TestTeardownFunction teardown)204 CuTest* CuTestNew(const char* name, TestFunction function, TestSetupFunction setup, TestTeardownFunction teardown)
205 {
206 	CuTest* tc = CU_ALLOC(CuTest);
207 	if (tc != NULL)
208 		CuTestInit(tc, name, function, setup, teardown);
209 	else
210 		fprintf(stderr, "%s: %s%s\n", CU_FAIL_HEADER,
211 		    "Error initializing test case: ", name);
212 	return tc;
213 }
214 
CuTestDelete(CuTest * t)215 void CuTestDelete(CuTest *t)
216 {
217         if (!t) return;
218         free(t->name);
219         free(t);
220 }
221 
CuTestRun(CuTest * tc)222 void CuTestRun(CuTest* tc)
223 {
224 	jmp_buf buf;
225 	if (tc->TestSetup != NULL)
226 		(tc->TestSetup)(tc);
227 	tc->jumpBuf = &buf;
228 	if (setjmp(buf) == 0)
229 	{
230 		tc->ran = 1;
231 		(tc->function)(tc);
232 	}
233 	if (tc->TestTeardown != NULL)
234 		(tc->TestTeardown)(tc);
235 	tc->jumpBuf = 0;
236 }
237 
CuFailInternal(CuTest * tc,const char * file,int line,CuString * string)238 static void CuFailInternal(CuTest* tc, const char* file, int line, CuString* string)
239 {
240 	char buf[HUGE_STRING_LEN];
241 
242 	sprintf(buf, "%s:%d: ", file, line);
243 	(void)CuStringInsert(string, buf, 0, 1);
244 
245 	if (tc == NULL)
246 	{
247 		/*
248 		 * Output the message now, it's come from overriding
249 		 * __db_assert.
250 		 * TODO: It'd be nice to somehow map this onto a CuTest, so the
251 		 *       assert can be effectively trapped. Since Berkeley DB
252 		 *       doesn't necessarily return error after an assert, so
253 		 *       a test case can "pass" even after a __db_assert.
254 		 *       Could trap this output, and map it back to the test
255 		 *       case, but I want to be careful about allowing multi-
256 		 *       threaded use at some stage.
257 		 */
258 		fprintf(stderr, "DB internal assert: %s\n", string->buffer);
259 	} else {
260 		tc->failed = 1;
261 		tc->message = string->buffer;
262 		if (tc->jumpBuf != 0) longjmp(*(tc->jumpBuf), 0);
263 	}
264 }
265 
CuFail_Line(CuTest * tc,const char * file,int line,const char * message2,const char * message)266 void CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message)
267 {
268 	CuString string;
269 
270 	CuStringInit(&string);
271 	if (message2 != NULL)
272 	{
273 		CuStringAppend(&string, message2, 1);
274 		CuStringAppend(&string, ": ", 1);
275 	}
276 	CuStringAppend(&string, message, 1);
277 	CuFailInternal(tc, file, line, &string);
278 }
279 
CuAssert_Line(CuTest * tc,const char * file,int line,const char * message,int condition)280 void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, int condition)
281 {
282 	if (condition) return;
283 	CuFail_Line(tc, file, line, NULL, message);
284 }
285 
CuAssertStrEquals_LineMsg(CuTest * tc,const char * file,int line,const char * message,const char * expected,const char * actual)286 void CuAssertStrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message,
287 	const char* expected, const char* actual)
288 {
289 	CuString string;
290 	if ((expected == NULL && actual == NULL) ||
291 	    (expected != NULL && actual != NULL &&
292 	     strcmp(expected, actual) == 0))
293 	{
294 		return;
295 	}
296 
297 	CuStringInit(&string);
298 	if (message != NULL)
299 	{
300 		CuStringAppend(&string, message, 1);
301 		CuStringAppend(&string, ": ", 1);
302 	}
303 	CuStringAppend(&string, "expected <", 1);
304 	CuStringAppend(&string, expected, 1);
305 	CuStringAppend(&string, "> but was <", 1);
306 	CuStringAppend(&string, actual, 1);
307 	CuStringAppend(&string, ">", 1);
308 	CuFailInternal(tc, file, line, &string);
309 }
310 
CuAssertIntEquals_LineMsg(CuTest * tc,const char * file,int line,const char * message,int expected,int actual)311 void CuAssertIntEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message,
312 	int expected, int actual)
313 {
314 	char buf[STRING_MAX];
315 	if (expected == actual) return;
316 	sprintf(buf, "expected <%d> but was <%d>", expected, actual);
317 	CuFail_Line(tc, file, line, message, buf);
318 }
319 
CuAssertDblEquals_LineMsg(CuTest * tc,const char * file,int line,const char * message,double expected,double actual,double delta)320 void CuAssertDblEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message,
321 	double expected, double actual, double delta)
322 {
323 	char buf[STRING_MAX];
324 	if (fabs(expected - actual) <= delta) return;
325 	sprintf(buf, "expected <%f> but was <%f>", expected, actual);
326 
327 	CuFail_Line(tc, file, line, message, buf);
328 }
329 
CuAssertPtrEquals_LineMsg(CuTest * tc,const char * file,int line,const char * message,void * expected,void * actual)330 void CuAssertPtrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message,
331 	void* expected, void* actual)
332 {
333 	char buf[STRING_MAX];
334 	if (expected == actual) return;
335 	sprintf(buf, "expected pointer <0x%p> but was <0x%p>", expected, actual);
336 	CuFail_Line(tc, file, line, message, buf);
337 }
338 
339 
340 /*-------------------------------------------------------------------------*
341  * CuSuite
342  *-------------------------------------------------------------------------*/
343 
CuSuiteInit(CuSuite * testSuite,const char * name,SuiteSetupFunction setup,SuiteTeardownFunction teardown)344 void CuSuiteInit(CuSuite* testSuite, const char *name,
345     SuiteSetupFunction setup, SuiteTeardownFunction teardown)
346 {
347 	testSuite->name = name;
348 	testSuite->count = 0;
349 	testSuite->failCount = 0;
350 	testSuite->SuiteSetup = setup;
351 	testSuite->SuiteTeardown = teardown;
352 	testSuite->context = NULL;
353         memset(testSuite->list, 0, sizeof(testSuite->list));
354 }
355 
CuSuiteNew(const char * name,SuiteSetupFunction setup,SuiteTeardownFunction teardown)356 CuSuite* CuSuiteNew(const char *name,
357     SuiteSetupFunction setup, SuiteTeardownFunction teardown)
358 {
359 	CuSuite* testSuite = CU_ALLOC(CuSuite);
360 	if (testSuite != NULL)
361 		CuSuiteInit(testSuite, name, setup, teardown);
362 	else
363 		fprintf(stderr, "%s: %s%s\n", CU_FAIL_HEADER,
364 		    "Error initializing test suite: ", name);
365 	return testSuite;
366 }
367 
CuSuiteDelete(CuSuite * testSuite)368 void CuSuiteDelete(CuSuite *testSuite)
369 {
370         unsigned int n;
371         for (n=0; n < MAX_TEST_CASES; n++)
372         {
373                 if (testSuite->list[n])
374                 {
375                         CuTestDelete(testSuite->list[n]);
376                 }
377         }
378         free(testSuite);
379 
380 }
381 
CuSuiteAdd(CuSuite * testSuite,CuTest * testCase)382 void CuSuiteAdd(CuSuite* testSuite, CuTest *testCase)
383 {
384 	assert(testSuite->count < MAX_TEST_CASES);
385 	testSuite->list[testSuite->count] = testCase;
386 	testSuite->count++;
387 	testCase->suite = testSuite;
388 }
389 
CuSuiteAddSuite(CuSuite * testSuite,CuSuite * testSuite2)390 void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2)
391 {
392 	int i;
393 	for (i = 0 ; i < testSuite2->count ; ++i)
394 	{
395 		CuTest* testCase = testSuite2->list[i];
396 		CuSuiteAdd(testSuite, testCase);
397 	}
398 }
399 
CuSuiteRun(CuSuite * testSuite)400 void CuSuiteRun(CuSuite* testSuite)
401 {
402 	int i;
403 	for (i = 0 ; i < testSuite->count ; ++i)
404 	{
405 		CuTest* testCase = testSuite->list[i];
406 		if (testSuite->SuiteSetup != NULL)
407 			(testSuite->SuiteSetup)(testSuite);
408 		CuTestRun(testCase);
409 		if (testSuite->SuiteTeardown != NULL)
410 			(testSuite->SuiteTeardown)(testSuite);
411 		if (testCase->failed) { testSuite->failCount += 1; }
412 	}
413 }
414 
CuSuiteSummary(CuSuite * testSuite,CuString * summary)415 void CuSuiteSummary(CuSuite* testSuite, CuString* summary)
416 {
417 	int i;
418 	for (i = 0 ; i < testSuite->count ; ++i)
419 	{
420 		CuTest* testCase = testSuite->list[i];
421 		CuStringAppend(summary, testCase->failed ? "F" : ".", 1);
422 	}
423 	CuStringAppend(summary, "\n\n", 1);
424 }
425 
CuSuiteDetails(CuSuite * testSuite,CuString * details)426 void CuSuiteDetails(CuSuite* testSuite, CuString* details)
427 {
428 	int i;
429 	int failCount = 0;
430 
431 	if (testSuite->failCount == 0)
432 	{
433 		int passCount = testSuite->count - testSuite->failCount;
434 		const char* testWord = passCount == 1 ? "test" : "tests";
435 		CuStringAppendFormat(details, 1, "OK (%d %s)\n", passCount, testWord);
436 	}
437 	else
438 	{
439 		if (testSuite->failCount == 1)
440 			CuStringAppend(details, "There was 1 failure:\n", 1);
441 		else
442 			CuStringAppendFormat(details, 1, "There were %d failures:\n", testSuite->failCount);
443 
444 		for (i = 0 ; i < testSuite->count ; ++i)
445 		{
446 			CuTest* testCase = testSuite->list[i];
447 			if (testCase->failed)
448 			{
449 				failCount++;
450 				CuStringAppendFormat(details, 1, "%d) %s: %s\n",
451 					failCount, testCase->name, testCase->message);
452 			}
453 		}
454 		CuStringAppend(details, "\n!!!FAILURES!!!\n", 1);
455 
456 		CuStringAppendFormat(details, 1, "Runs: %d ",   testSuite->count);
457 		CuStringAppendFormat(details, 1, "Passes: %d ", testSuite->count - testSuite->failCount);
458 		CuStringAppendFormat(details, 1, "Fails: %d\n",  testSuite->failCount);
459 	}
460 }
461