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