1/*
2 * Copyright (c) 2003 Asim Jalis
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty. In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 * claim that you wrote the original software. If you use this software in
14 * a product, an acknowledgment in the product documentation would be
15 * appreciated but is not required.
16 *
17 * 2. Altered source versions must be plainly marked as such, and must not
18 * be misrepresented as being the original software.
19 *
20 * 3. This notice may not be removed or altered from any source
21 * distribution.
22 */
23
24#include <assert.h>
25#include <setjmp.h>
26#include <stdlib.h>
27#include <stdio.h>
28#include <string.h>
29#include <math.h>
30
31#include "CuTest.h"
32
33/*-------------------------------------------------------------------------*
34 * CuStr
35 *-------------------------------------------------------------------------*/
36
37char* CuStrAlloc(int size)
38{
39	char* newStr = (char*) malloc( sizeof(char) * (size) );
40	return newStr;
41}
42
43char* CuStrCopy(const char* old)
44{
45	int len = strlen(old);
46	char* newStr = CuStrAlloc(len + 1);
47	strcpy(newStr, old);
48	return newStr;
49}
50
51/*-------------------------------------------------------------------------*
52 * CuString
53 *-------------------------------------------------------------------------*/
54
55void CuStringInit(CuString* str)
56{
57	str->length = 0;
58	str->size = STRING_MAX;
59	str->buffer = (char*) malloc(sizeof(char) * str->size);
60	str->buffer[0] = '\0';
61}
62
63CuString* CuStringNew(void)
64{
65	CuString* str = (CuString*) malloc(sizeof(CuString));
66	str->length = 0;
67	str->size = STRING_MAX;
68	str->buffer = (char*) malloc(sizeof(char) * str->size);
69	str->buffer[0] = '\0';
70	return str;
71}
72
73void CuStringDelete(CuString *str)
74{
75        if (!str) return;
76        free(str->buffer);
77        free(str);
78}
79
80void CuStringResize(CuString* str, int newSize)
81{
82	str->buffer = (char*) realloc(str->buffer, sizeof(char) * newSize);
83	str->size = newSize;
84}
85
86void CuStringAppend(CuString* str, const char* text)
87{
88	int length;
89
90	if (text == NULL) {
91		text = "NULL";
92	}
93
94	length = strlen(text);
95	if (str->length + length + 1 >= str->size)
96		CuStringResize(str, str->length + length + 1 + STRING_INC);
97	str->length += length;
98	strcat(str->buffer, text);
99}
100
101void CuStringAppendChar(CuString* str, char ch)
102{
103	char text[2];
104	text[0] = ch;
105	text[1] = '\0';
106	CuStringAppend(str, text);
107}
108
109__attribute__ ((format (printf, 2, 3))) void CuStringAppendFormat(CuString* str, const char* format, ...)
110{
111	va_list argp;
112	char buf[HUGE_STRING_LEN];
113	va_start(argp, format);
114	vsprintf(buf, format, argp);
115	va_end(argp);
116	CuStringAppend(str, buf);
117}
118
119void CuStringInsert(CuString* str, const char* text, int pos)
120{
121	int length = strlen(text);
122	if (pos > str->length)
123		pos = str->length;
124	if (str->length + length + 1 >= str->size)
125		CuStringResize(str, str->length + length + 1 + STRING_INC);
126	memmove(str->buffer + pos + length, str->buffer + pos, (str->length - pos) + 1);
127	str->length += length;
128	memcpy(str->buffer + pos, text, length);
129}
130
131/*-------------------------------------------------------------------------*
132 * CuTest
133 *-------------------------------------------------------------------------*/
134
135void CuTestInit(CuTest* t, const char* name, TestFunction function)
136{
137	t->name = CuStrCopy(name);
138	t->failed = 0;
139	t->ran = 0;
140	t->message = NULL;
141	t->function = function;
142	t->jumpBuf = NULL;
143}
144
145CuTest* CuTestNew(const char* name, TestFunction function)
146{
147	CuTest* tc = CU_ALLOC(CuTest);
148	CuTestInit(tc, name, function);
149	return tc;
150}
151
152void CuTestDelete(CuTest *t)
153{
154        if (!t) return;
155        free(t->name);
156        free(t);
157}
158
159void CuTestRun(CuTest* tc)
160{
161	jmp_buf buf;
162	tc->jumpBuf = &buf;
163	if (setjmp(buf) == 0)
164	{
165		tc->ran = 1;
166		(tc->function)(tc);
167	}
168	tc->jumpBuf = 0;
169}
170
171static void CuFailInternal(CuTest* tc, const char* file, int line, CuString* string)
172{
173	char buf[HUGE_STRING_LEN];
174
175	sprintf(buf, "%s:%d: ", file, line);
176	CuStringInsert(string, buf, 0);
177
178	tc->failed = 1;
179	tc->message = string->buffer;
180	if (tc->jumpBuf != 0) longjmp(*(tc->jumpBuf), 0);
181}
182
183void CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message)
184{
185	CuString string;
186
187	CuStringInit(&string);
188	if (message2 != NULL)
189	{
190		CuStringAppend(&string, message2);
191		CuStringAppend(&string, ": ");
192	}
193	CuStringAppend(&string, message);
194	CuFailInternal(tc, file, line, &string);
195}
196
197void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, int condition)
198{
199	if (condition) return;
200	CuFail_Line(tc, file, line, NULL, message);
201}
202
203void CuAssertStrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message,
204	const char* expected, const char* actual)
205{
206	CuString string;
207	if ((expected == NULL && actual == NULL) ||
208	    (expected != NULL && actual != NULL &&
209	     strcmp(expected, actual) == 0))
210	{
211		return;
212	}
213
214	CuStringInit(&string);
215	if (message != NULL)
216	{
217		CuStringAppend(&string, message);
218		CuStringAppend(&string, ": ");
219	}
220	CuStringAppend(&string, "expected <");
221	CuStringAppend(&string, expected);
222	CuStringAppend(&string, "> but was <");
223	CuStringAppend(&string, actual);
224	CuStringAppend(&string, ">");
225	CuFailInternal(tc, file, line, &string);
226}
227
228void CuAssertIntEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message,
229	int expected, int actual)
230{
231	char buf[STRING_MAX];
232	if (expected == actual) return;
233	sprintf(buf, "expected <%d> but was <%d>", expected, actual);
234	CuFail_Line(tc, file, line, message, buf);
235}
236
237void CuAssertDblEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message,
238	double expected, double actual, double delta)
239{
240	char buf[STRING_MAX];
241	if (fabs(expected - actual) <= delta) return;
242	sprintf(buf, "expected <%f> but was <%f>", expected, actual);
243
244	CuFail_Line(tc, file, line, message, buf);
245}
246
247void CuAssertPtrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message,
248	void* expected, void* actual)
249{
250	char buf[STRING_MAX];
251	if (expected == actual) return;
252	sprintf(buf, "expected pointer <0x%p> but was <0x%p>", expected, actual);
253	CuFail_Line(tc, file, line, message, buf);
254}
255
256
257/*-------------------------------------------------------------------------*
258 * CuSuite
259 *-------------------------------------------------------------------------*/
260
261void CuSuiteInit(CuSuite* testSuite)
262{
263	testSuite->count = 0;
264	testSuite->failCount = 0;
265        memset(testSuite->list, 0, sizeof(testSuite->list));
266}
267
268CuSuite* CuSuiteNew(void)
269{
270	CuSuite* testSuite = CU_ALLOC(CuSuite);
271	CuSuiteInit(testSuite);
272	return testSuite;
273}
274
275void CuSuiteDelete(CuSuite *testSuite)
276{
277        unsigned int n;
278        for (n=0; n < MAX_TEST_CASES; n++)
279        {
280                if (testSuite->list[n])
281                {
282                        CuTestDelete(testSuite->list[n]);
283                }
284        }
285        free(testSuite);
286
287}
288
289void CuSuiteAdd(CuSuite* testSuite, CuTest *testCase)
290{
291	assert(testSuite->count < MAX_TEST_CASES);
292	testSuite->list[testSuite->count] = testCase;
293	testSuite->count++;
294}
295
296void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2)
297{
298	int i;
299	for (i = 0 ; i < testSuite2->count ; ++i)
300	{
301		CuTest* testCase = testSuite2->list[i];
302		CuSuiteAdd(testSuite, testCase);
303	}
304}
305
306void CuSuiteRun(CuSuite* testSuite)
307{
308	int i;
309	for (i = 0 ; i < testSuite->count ; ++i)
310	{
311		CuTest* testCase = testSuite->list[i];
312		CuTestRun(testCase);
313		if (testCase->failed) { testSuite->failCount += 1; }
314	}
315}
316
317void CuSuiteSummary(CuSuite* testSuite, CuString* summary)
318{
319	int i;
320	for (i = 0 ; i < testSuite->count ; ++i)
321	{
322		CuTest* testCase = testSuite->list[i];
323		CuStringAppend(summary, testCase->failed ? "F" : ".");
324	}
325	CuStringAppend(summary, "\n\n");
326}
327
328void CuSuiteDetails(CuSuite* testSuite, CuString* details)
329{
330	int i;
331	int failCount = 0;
332
333	if (testSuite->failCount == 0)
334	{
335		int passCount = testSuite->count - testSuite->failCount;
336		const char* testWord = passCount == 1 ? "test" : "tests";
337		CuStringAppendFormat(details, "OK (%d %s)\n", passCount, testWord);
338	}
339	else
340	{
341		if (testSuite->failCount == 1)
342			CuStringAppend(details, "There was 1 failure:\n");
343		else
344			CuStringAppendFormat(details, "There were %d failures:\n", testSuite->failCount);
345
346		for (i = 0 ; i < testSuite->count ; ++i)
347		{
348			CuTest* testCase = testSuite->list[i];
349			if (testCase->failed)
350			{
351				failCount++;
352				CuStringAppendFormat(details, "%d) %s: %s\n",
353					failCount, testCase->name, testCase->message);
354			}
355		}
356		CuStringAppend(details, "\n!!!FAILURES!!!\n");
357
358		CuStringAppendFormat(details, "Runs: %d ",   testSuite->count);
359		CuStringAppendFormat(details, "Passes: %d ", testSuite->count - testSuite->failCount);
360		CuStringAppendFormat(details, "Fails: %d\n",  testSuite->failCount);
361	}
362}
363