From 4cc709c2a0a0eda016ca344b077a14139ff74b86 Mon Sep 17 00:00:00 2001 From: John Bintz Date: Wed, 28 Feb 2024 12:50:57 -0500 Subject: [PATCH] add unit tests? --- cutest-1.5/AllTests.c | 25 ++ cutest-1.5/CuTest.c | 339 +++++++++++++++++++ cutest-1.5/CuTest.h | 116 +++++++ cutest-1.5/CuTestTest.c | 709 +++++++++++++++++++++++++++++++++++++++ cutest-1.5/README.txt | 211 ++++++++++++ cutest-1.5/index.html | 196 +++++++++++ cutest-1.5/license.txt | 38 +++ cutest-1.5/make-tests.sh | 55 +++ cutest-1.5/style.css | 21 ++ game.c | 85 +---- game.h | 11 +- makefile | 7 +- movement.c | 3 +- spawn.c | 118 +++++++ spawn.h | 26 ++ spawn_test.c | 82 +++++ test | Bin 0 -> 39124 bytes test.c | 20 ++ 18 files changed, 1984 insertions(+), 78 deletions(-) create mode 100644 cutest-1.5/AllTests.c create mode 100644 cutest-1.5/CuTest.c create mode 100644 cutest-1.5/CuTest.h create mode 100644 cutest-1.5/CuTestTest.c create mode 100644 cutest-1.5/README.txt create mode 100644 cutest-1.5/index.html create mode 100644 cutest-1.5/license.txt create mode 100644 cutest-1.5/make-tests.sh create mode 100644 cutest-1.5/style.css create mode 100644 spawn.c create mode 100644 spawn.h create mode 100644 spawn_test.c create mode 100755 test create mode 100644 test.c diff --git a/cutest-1.5/AllTests.c b/cutest-1.5/AllTests.c new file mode 100644 index 0000000..5c849ef --- /dev/null +++ b/cutest-1.5/AllTests.c @@ -0,0 +1,25 @@ +#include + +#include "CuTest.h" + +CuSuite* CuGetSuite(); +CuSuite* CuStringGetSuite(); + +void RunAllTests(void) +{ + CuString *output = CuStringNew(); + CuSuite* suite = CuSuiteNew(); + + CuSuiteAddSuite(suite, CuGetSuite()); + CuSuiteAddSuite(suite, CuStringGetSuite()); + + CuSuiteRun(suite); + CuSuiteSummary(suite, output); + CuSuiteDetails(suite, output); + printf("%s\n", output->buffer); +} + +int main(void) +{ + RunAllTests(); +} diff --git a/cutest-1.5/CuTest.c b/cutest-1.5/CuTest.c new file mode 100644 index 0000000..8f61199 --- /dev/null +++ b/cutest-1.5/CuTest.c @@ -0,0 +1,339 @@ +#include +#include +#include +#include +#include +#include + +#include "CuTest.h" + +/*-------------------------------------------------------------------------* + * CuStr + *-------------------------------------------------------------------------*/ + +char* CuStrAlloc(int size) +{ + char* newStr = (char*) malloc( sizeof(char) * (size) ); + return newStr; +} + +char* CuStrCopy(const char* old) +{ + int len = strlen(old); + char* newStr = CuStrAlloc(len + 1); + strcpy(newStr, old); + return newStr; +} + +/*-------------------------------------------------------------------------* + * CuString + *-------------------------------------------------------------------------*/ + +void CuStringInit(CuString* str) +{ + str->length = 0; + str->size = STRING_MAX; + str->buffer = (char*) malloc(sizeof(char) * str->size); + str->buffer[0] = '\0'; +} + +CuString* CuStringNew(void) +{ + CuString* str = (CuString*) malloc(sizeof(CuString)); + str->length = 0; + str->size = STRING_MAX; + str->buffer = (char*) malloc(sizeof(char) * str->size); + str->buffer[0] = '\0'; + return str; +} + +void CuStringDelete(CuString *str) +{ + if (!str) return; + free(str->buffer); + free(str); +} + +void CuStringResize(CuString* str, int newSize) +{ + str->buffer = (char*) realloc(str->buffer, sizeof(char) * newSize); + str->size = newSize; +} + +void CuStringAppend(CuString* str, const char* text) +{ + int length; + + if (text == NULL) { + text = "NULL"; + } + + length = strlen(text); + if (str->length + length + 1 >= str->size) + CuStringResize(str, str->length + length + 1 + STRING_INC); + str->length += length; + strcat(str->buffer, text); +} + +void CuStringAppendChar(CuString* str, char ch) +{ + char text[2]; + text[0] = ch; + text[1] = '\0'; + CuStringAppend(str, text); +} + +void CuStringAppendFormat(CuString* str, const char* format, ...) +{ + va_list argp; + char buf[HUGE_STRING_LEN]; + va_start(argp, format); + vsprintf(buf, format, argp); + va_end(argp); + CuStringAppend(str, buf); +} + +void CuStringInsert(CuString* str, const char* text, int pos) +{ + int length = strlen(text); + if (pos > str->length) + pos = str->length; + if (str->length + length + 1 >= str->size) + CuStringResize(str, str->length + length + 1 + STRING_INC); + memmove(str->buffer + pos + length, str->buffer + pos, (str->length - pos) + 1); + str->length += length; + memcpy(str->buffer + pos, text, length); +} + +/*-------------------------------------------------------------------------* + * CuTest + *-------------------------------------------------------------------------*/ + +void CuTestInit(CuTest* t, const char* name, TestFunction function) +{ + t->name = CuStrCopy(name); + t->failed = 0; + t->ran = 0; + t->message = NULL; + t->function = function; + t->jumpBuf = NULL; +} + +CuTest* CuTestNew(const char* name, TestFunction function) +{ + CuTest* tc = CU_ALLOC(CuTest); + CuTestInit(tc, name, function); + return tc; +} + +void CuTestDelete(CuTest *t) +{ + if (!t) return; + free(t->name); + free(t); +} + +void CuTestRun(CuTest* tc) +{ + jmp_buf buf; + tc->jumpBuf = &buf; + if (setjmp(buf) == 0) + { + tc->ran = 1; + (tc->function)(tc); + } + tc->jumpBuf = 0; +} + +static void CuFailInternal(CuTest* tc, const char* file, int line, CuString* string) +{ + char buf[HUGE_STRING_LEN]; + + sprintf(buf, "%s:%d: ", file, line); + CuStringInsert(string, buf, 0); + + tc->failed = 1; + tc->message = string->buffer; + if (tc->jumpBuf != 0) longjmp(*(tc->jumpBuf), 0); +} + +void CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message) +{ + CuString string; + + CuStringInit(&string); + if (message2 != NULL) + { + CuStringAppend(&string, message2); + CuStringAppend(&string, ": "); + } + CuStringAppend(&string, message); + CuFailInternal(tc, file, line, &string); +} + +void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, int condition) +{ + if (condition) return; + CuFail_Line(tc, file, line, NULL, message); +} + +void CuAssertStrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, + const char* expected, const char* actual) +{ + CuString string; + if ((expected == NULL && actual == NULL) || + (expected != NULL && actual != NULL && + strcmp(expected, actual) == 0)) + { + return; + } + + CuStringInit(&string); + if (message != NULL) + { + CuStringAppend(&string, message); + CuStringAppend(&string, ": "); + } + CuStringAppend(&string, "expected <"); + CuStringAppend(&string, expected); + CuStringAppend(&string, "> but was <"); + CuStringAppend(&string, actual); + CuStringAppend(&string, ">"); + CuFailInternal(tc, file, line, &string); +} + +void CuAssertIntEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, + int expected, int actual) +{ + char buf[STRING_MAX]; + if (expected == actual) return; + sprintf(buf, "expected <%d> but was <%d>", expected, actual); + CuFail_Line(tc, file, line, message, buf); +} + +void CuAssertDblEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, + double expected, double actual, double delta) +{ + char buf[STRING_MAX]; + if (fabs(expected - actual) <= delta) return; + sprintf(buf, "expected <%f> but was <%f>", expected, actual); + + CuFail_Line(tc, file, line, message, buf); +} + +void CuAssertPtrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, + void* expected, void* actual) +{ + char buf[STRING_MAX]; + if (expected == actual) return; + sprintf(buf, "expected pointer <0x%p> but was <0x%p>", expected, actual); + CuFail_Line(tc, file, line, message, buf); +} + + +/*-------------------------------------------------------------------------* + * CuSuite + *-------------------------------------------------------------------------*/ + +void CuSuiteInit(CuSuite* testSuite) +{ + testSuite->count = 0; + testSuite->failCount = 0; + memset(testSuite->list, 0, sizeof(testSuite->list)); +} + +CuSuite* CuSuiteNew(void) +{ + CuSuite* testSuite = CU_ALLOC(CuSuite); + CuSuiteInit(testSuite); + return testSuite; +} + +void CuSuiteDelete(CuSuite *testSuite) +{ + unsigned int n; + for (n=0; n < MAX_TEST_CASES; n++) + { + if (testSuite->list[n]) + { + CuTestDelete(testSuite->list[n]); + } + } + free(testSuite); + +} + +void CuSuiteAdd(CuSuite* testSuite, CuTest *testCase) +{ + assert(testSuite->count < MAX_TEST_CASES); + testSuite->list[testSuite->count] = testCase; + testSuite->count++; +} + +void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2) +{ + int i; + for (i = 0 ; i < testSuite2->count ; ++i) + { + CuTest* testCase = testSuite2->list[i]; + CuSuiteAdd(testSuite, testCase); + } +} + +void CuSuiteRun(CuSuite* testSuite) +{ + int i; + for (i = 0 ; i < testSuite->count ; ++i) + { + CuTest* testCase = testSuite->list[i]; + CuTestRun(testCase); + if (testCase->failed) { testSuite->failCount += 1; } + } +} + +void CuSuiteSummary(CuSuite* testSuite, CuString* summary) +{ + int i; + for (i = 0 ; i < testSuite->count ; ++i) + { + CuTest* testCase = testSuite->list[i]; + CuStringAppend(summary, testCase->failed ? "F" : "."); + } + CuStringAppend(summary, "\n\n"); +} + +void CuSuiteDetails(CuSuite* testSuite, CuString* details) +{ + int i; + int failCount = 0; + + if (testSuite->failCount == 0) + { + int passCount = testSuite->count - testSuite->failCount; + const char* testWord = passCount == 1 ? "test" : "tests"; + CuStringAppendFormat(details, "OK (%d %s)\n", passCount, testWord); + } + else + { + if (testSuite->failCount == 1) + CuStringAppend(details, "There was 1 failure:\n"); + else + CuStringAppendFormat(details, "There were %d failures:\n", testSuite->failCount); + + for (i = 0 ; i < testSuite->count ; ++i) + { + CuTest* testCase = testSuite->list[i]; + if (testCase->failed) + { + failCount++; + CuStringAppendFormat(details, "%d) %s: %s\n", + failCount, testCase->name, testCase->message); + } + } + CuStringAppend(details, "\n!!!FAILURES!!!\n"); + + CuStringAppendFormat(details, "Runs: %d ", testSuite->count); + CuStringAppendFormat(details, "Passes: %d ", testSuite->count - testSuite->failCount); + CuStringAppendFormat(details, "Fails: %d\n", testSuite->failCount); + } +} diff --git a/cutest-1.5/CuTest.h b/cutest-1.5/CuTest.h new file mode 100644 index 0000000..8b32773 --- /dev/null +++ b/cutest-1.5/CuTest.h @@ -0,0 +1,116 @@ +#ifndef CU_TEST_H +#define CU_TEST_H + +#include +#include + +#define CUTEST_VERSION "CuTest 1.5" + +/* CuString */ + +char* CuStrAlloc(int size); +char* CuStrCopy(const char* old); + +#define CU_ALLOC(TYPE) ((TYPE*) malloc(sizeof(TYPE))) + +#define HUGE_STRING_LEN 8192 +#define STRING_MAX 256 +#define STRING_INC 256 + +typedef struct +{ + int length; + int size; + char* buffer; +} CuString; + +void CuStringInit(CuString* str); +CuString* CuStringNew(void); +void CuStringRead(CuString* str, const char* path); +void CuStringAppend(CuString* str, const char* text); +void CuStringAppendChar(CuString* str, char ch); +void CuStringAppendFormat(CuString* str, const char* format, ...); +void CuStringInsert(CuString* str, const char* text, int pos); +void CuStringResize(CuString* str, int newSize); +void CuStringDelete(CuString* str); + +/* CuTest */ + +typedef struct CuTest CuTest; + +typedef void (*TestFunction)(CuTest *); + +struct CuTest +{ + char* name; + TestFunction function; + int failed; + int ran; + const char* message; + jmp_buf *jumpBuf; +}; + +void CuTestInit(CuTest* t, const char* name, TestFunction function); +CuTest* CuTestNew(const char* name, TestFunction function); +void CuTestRun(CuTest* tc); +void CuTestDelete(CuTest *t); + +/* Internal versions of assert functions -- use the public versions */ +void CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message); +void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, int condition); +void CuAssertStrEquals_LineMsg(CuTest* tc, + const char* file, int line, const char* message, + const char* expected, const char* actual); +void CuAssertIntEquals_LineMsg(CuTest* tc, + const char* file, int line, const char* message, + int expected, int actual); +void CuAssertDblEquals_LineMsg(CuTest* tc, + const char* file, int line, const char* message, + double expected, double actual, double delta); +void CuAssertPtrEquals_LineMsg(CuTest* tc, + const char* file, int line, const char* message, + void* expected, void* actual); + +/* public assert functions */ + +#define CuFail(tc, ms) CuFail_Line( (tc), __FILE__, __LINE__, NULL, (ms)) +#define CuAssert(tc, ms, cond) CuAssert_Line((tc), __FILE__, __LINE__, (ms), (cond)) +#define CuAssertTrue(tc, cond) CuAssert_Line((tc), __FILE__, __LINE__, "assert failed", (cond)) + +#define CuAssertStrEquals(tc,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) +#define CuAssertStrEquals_Msg(tc,ms,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) +#define CuAssertIntEquals(tc,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) +#define CuAssertIntEquals_Msg(tc,ms,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) +#define CuAssertDblEquals(tc,ex,ac,dl) CuAssertDblEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac),(dl)) +#define CuAssertDblEquals_Msg(tc,ms,ex,ac,dl) CuAssertDblEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac),(dl)) +#define CuAssertPtrEquals(tc,ex,ac) CuAssertPtrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) +#define CuAssertPtrEquals_Msg(tc,ms,ex,ac) CuAssertPtrEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) + +#define CuAssertPtrNotNull(tc,p) CuAssert_Line((tc),__FILE__,__LINE__,"null pointer unexpected",(p != NULL)) +#define CuAssertPtrNotNullMsg(tc,msg,p) CuAssert_Line((tc),__FILE__,__LINE__,(msg),(p != NULL)) + +/* CuSuite */ + +#define MAX_TEST_CASES 1024 + +#define SUITE_ADD_TEST(SUITE,TEST) CuSuiteAdd(SUITE, CuTestNew(#TEST, TEST)) + +typedef struct +{ + int count; + CuTest* list[MAX_TEST_CASES]; + int failCount; + +} CuSuite; + + +void CuSuiteInit(CuSuite* testSuite); +CuSuite* CuSuiteNew(void); +void CuSuiteDelete(CuSuite *testSuite); +void CuSuiteAdd(CuSuite* testSuite, CuTest *testCase); +void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2); +void CuSuiteRun(CuSuite* testSuite); +void CuSuiteSummary(CuSuite* testSuite, CuString* summary); +void CuSuiteDetails(CuSuite* testSuite, CuString* details); + +#endif /* CU_TEST_H */ diff --git a/cutest-1.5/CuTestTest.c b/cutest-1.5/CuTestTest.c new file mode 100644 index 0000000..547f119 --- /dev/null +++ b/cutest-1.5/CuTestTest.c @@ -0,0 +1,709 @@ +#include +#include +#include +#include +#include + +#include "CuTest.h" + +/*-------------------------------------------------------------------------* + * Helper functions + *-------------------------------------------------------------------------*/ + +#define CompareAsserts(tc, message, expected, actual) X_CompareAsserts((tc), __FILE__, __LINE__, (message), (expected), (actual)) + +static void X_CompareAsserts(CuTest* tc, const char *file, int line, const char* message, const char* expected, const char* actual) +{ + int mismatch; + if (expected == NULL || actual == NULL) { + mismatch = (expected != NULL || actual != NULL); + } else { + const char *front = __FILE__ ":"; + const size_t frontLen = strlen(front); + const size_t expectedLen = strlen(expected); + + const char *matchStr = actual; + + mismatch = (strncmp(matchStr, front, frontLen) != 0); + if (!mismatch) { + matchStr = strchr(matchStr + frontLen, ':'); + mismatch |= (matchStr == NULL || strncmp(matchStr, ": ", 2)); + if (!mismatch) { + matchStr += 2; + mismatch |= (strncmp(matchStr, expected, expectedLen) != 0); + } + } + } + + CuAssert_Line(tc, file, line, message, !mismatch); +} + +/*-------------------------------------------------------------------------* + * CuString Test + *-------------------------------------------------------------------------*/ + +void TestCuStringNew(CuTest* tc) +{ + CuString* str = CuStringNew(); + CuAssertTrue(tc, 0 == str->length); + CuAssertTrue(tc, 0 != str->size); + CuAssertStrEquals(tc, "", str->buffer); +} + + +void TestCuStringAppend(CuTest* tc) +{ + CuString* str = CuStringNew(); + CuStringAppend(str, "hello"); + CuAssertIntEquals(tc, 5, str->length); + CuAssertStrEquals(tc, "hello", str->buffer); + CuStringAppend(str, " world"); + CuAssertIntEquals(tc, 11, str->length); + CuAssertStrEquals(tc, "hello world", str->buffer); +} + + +void TestCuStringAppendNULL(CuTest* tc) +{ + CuString* str = CuStringNew(); + CuStringAppend(str, NULL); + CuAssertIntEquals(tc, 4, str->length); + CuAssertStrEquals(tc, "NULL", str->buffer); +} + + +void TestCuStringAppendChar(CuTest* tc) +{ + CuString* str = CuStringNew(); + CuStringAppendChar(str, 'a'); + CuStringAppendChar(str, 'b'); + CuStringAppendChar(str, 'c'); + CuStringAppendChar(str, 'd'); + CuAssertIntEquals(tc, 4, str->length); + CuAssertStrEquals(tc, "abcd", str->buffer); +} + + +void TestCuStringInserts(CuTest* tc) +{ + CuString* str = CuStringNew(); + CuStringAppend(str, "world"); + CuAssertIntEquals(tc, 5, str->length); + CuAssertStrEquals(tc, "world", str->buffer); + CuStringInsert(str, "hell", 0); + CuAssertIntEquals(tc, 9, str->length); + CuAssertStrEquals(tc, "hellworld", str->buffer); + CuStringInsert(str, "o ", 4); + CuAssertIntEquals(tc, 11, str->length); + CuAssertStrEquals(tc, "hello world", str->buffer); + CuStringInsert(str, "!", 11); + CuAssertIntEquals(tc, 12, str->length); + CuAssertStrEquals(tc, "hello world!", str->buffer); +} + + +void TestCuStringResizes(CuTest* tc) +{ + CuString* str = CuStringNew(); + int i; + for(i = 0 ; i < STRING_MAX ; ++i) + { + CuStringAppend(str, "aa"); + } + CuAssertTrue(tc, STRING_MAX * 2 == str->length); + CuAssertTrue(tc, STRING_MAX * 2 <= str->size); +} + +CuSuite* CuStringGetSuite(void) +{ + CuSuite* suite = CuSuiteNew(); + + SUITE_ADD_TEST(suite, TestCuStringNew); + SUITE_ADD_TEST(suite, TestCuStringAppend); + SUITE_ADD_TEST(suite, TestCuStringAppendNULL); + SUITE_ADD_TEST(suite, TestCuStringAppendChar); + SUITE_ADD_TEST(suite, TestCuStringInserts); + SUITE_ADD_TEST(suite, TestCuStringResizes); + + return suite; +} + +/*-------------------------------------------------------------------------* + * CuTest Test + *-------------------------------------------------------------------------*/ + +void TestPasses(CuTest* tc) +{ + CuAssert(tc, "test should pass", 1 == 0 + 1); +} + +void zTestFails(CuTest* tc) +{ + CuAssert(tc, "test should fail", 1 == 1 + 1); +} + + +void TestCuTestNew(CuTest* tc) +{ + CuTest* tc2 = CuTestNew("MyTest", TestPasses); + CuAssertStrEquals(tc, "MyTest", tc2->name); + CuAssertTrue(tc, !tc2->failed); + CuAssertTrue(tc, tc2->message == NULL); + CuAssertTrue(tc, tc2->function == TestPasses); + CuAssertTrue(tc, tc2->ran == 0); + CuAssertTrue(tc, tc2->jumpBuf == NULL); +} + + +void TestCuTestInit(CuTest *tc) +{ + CuTest tc2; + CuTestInit(&tc2, "MyTest", TestPasses); + CuAssertStrEquals(tc, "MyTest", tc2.name); + CuAssertTrue(tc, !tc2.failed); + CuAssertTrue(tc, tc2.message == NULL); + CuAssertTrue(tc, tc2.function == TestPasses); + CuAssertTrue(tc, tc2.ran == 0); + CuAssertTrue(tc, tc2.jumpBuf == NULL); +} + +void TestCuAssert(CuTest* tc) +{ + CuTest tc2; + CuTestInit(&tc2, "MyTest", TestPasses); + + CuAssert(&tc2, "test 1", 5 == 4 + 1); + CuAssertTrue(tc, !tc2.failed); + CuAssertTrue(tc, tc2.message == NULL); + + CuAssert(&tc2, "test 2", 0); + CuAssertTrue(tc, tc2.failed); + CompareAsserts(tc, "CuAssert didn't fail", "test 2", tc2.message); + + CuAssert(&tc2, "test 3", 1); + CuAssertTrue(tc, tc2.failed); + CompareAsserts(tc, "CuAssert didn't fail", "test 2", tc2.message); + + CuAssert(&tc2, "test 4", 0); + CuAssertTrue(tc, tc2.failed); + CompareAsserts(tc, "CuAssert didn't fail", "test 4", tc2.message); + +} + +void TestCuAssertPtrEquals_Success(CuTest* tc) +{ + CuTest tc2; + int x; + + CuTestInit(&tc2, "MyTest", TestPasses); + + /* test success case */ + CuAssertPtrEquals(&tc2, &x, &x); + CuAssertTrue(tc, ! tc2.failed); + CuAssertTrue(tc, NULL == tc2.message); +} + +void TestCuAssertPtrEquals_Failure(CuTest* tc) +{ + CuTest tc2; + int x; + int* nullPtr = NULL; + char expected_message[STRING_MAX]; + + CuTestInit(&tc2, "MyTest", TestPasses); + + /* test failing case */ + sprintf(expected_message, "expected pointer <0x%p> but was <0x%p>", nullPtr, &x); + CuAssertPtrEquals(&tc2, NULL, &x); + CuAssertTrue(tc, tc2.failed); + CompareAsserts(tc, "CuAssertPtrEquals failed", expected_message, tc2.message); +} + +void TestCuAssertPtrNotNull_Success(CuTest* tc) +{ + CuTest tc2; + int x; + + CuTestInit(&tc2, "MyTest", TestPasses); + + /* test success case */ + CuAssertPtrNotNull(&tc2, &x); + CuAssertTrue(tc, ! tc2.failed); + CuAssertTrue(tc, NULL == tc2.message); +} + +void TestCuAssertPtrNotNull_Failure(CuTest* tc) +{ + CuTest tc2; + + CuTestInit(&tc2, "MyTest", TestPasses); + + /* test failing case */ + CuAssertPtrNotNull(&tc2, NULL); + CuAssertTrue(tc, tc2.failed); + CompareAsserts(tc, "CuAssertPtrNotNull failed", "null pointer unexpected", tc2.message); +} + +void TestCuTestRun(CuTest* tc) +{ + CuTest tc2; + CuTestInit(&tc2, "MyTest", zTestFails); + CuTestRun(&tc2); + + CuAssertStrEquals(tc, "MyTest", tc2.name); + CuAssertTrue(tc, tc2.failed); + CuAssertTrue(tc, tc2.ran); + CompareAsserts(tc, "TestRun failed", "test should fail", tc2.message); +} + +/*-------------------------------------------------------------------------* + * CuSuite Test + *-------------------------------------------------------------------------*/ + +void TestCuSuiteInit(CuTest* tc) +{ + CuSuite ts; + CuSuiteInit(&ts); + CuAssertTrue(tc, ts.count == 0); + CuAssertTrue(tc, ts.failCount == 0); +} + +void TestCuSuiteNew(CuTest* tc) +{ + CuSuite* ts = CuSuiteNew(); + CuAssertTrue(tc, ts->count == 0); + CuAssertTrue(tc, ts->failCount == 0); +} + +void TestCuSuiteAddTest(CuTest* tc) +{ + CuSuite ts; + CuTest tc2; + + CuSuiteInit(&ts); + CuTestInit(&tc2, "MyTest", zTestFails); + + CuSuiteAdd(&ts, &tc2); + CuAssertTrue(tc, ts.count == 1); + + CuAssertStrEquals(tc, "MyTest", ts.list[0]->name); +} + +void TestCuSuiteAddSuite(CuTest* tc) +{ + CuSuite* ts1 = CuSuiteNew(); + CuSuite* ts2 = CuSuiteNew(); + + CuSuiteAdd(ts1, CuTestNew("TestFails1", zTestFails)); + CuSuiteAdd(ts1, CuTestNew("TestFails2", zTestFails)); + + CuSuiteAdd(ts2, CuTestNew("TestFails3", zTestFails)); + CuSuiteAdd(ts2, CuTestNew("TestFails4", zTestFails)); + + CuSuiteAddSuite(ts1, ts2); + CuAssertIntEquals(tc, 4, ts1->count); + + CuAssertStrEquals(tc, "TestFails1", ts1->list[0]->name); + CuAssertStrEquals(tc, "TestFails2", ts1->list[1]->name); + CuAssertStrEquals(tc, "TestFails3", ts1->list[2]->name); + CuAssertStrEquals(tc, "TestFails4", ts1->list[3]->name); +} + +void TestCuSuiteRun(CuTest* tc) +{ + CuSuite ts; + CuTest tc1, tc2, tc3, tc4; + + CuSuiteInit(&ts); + CuTestInit(&tc1, "TestPasses", TestPasses); + CuTestInit(&tc2, "TestPasses", TestPasses); + CuTestInit(&tc3, "TestFails", zTestFails); + CuTestInit(&tc4, "TestFails", zTestFails); + + CuSuiteAdd(&ts, &tc1); + CuSuiteAdd(&ts, &tc2); + CuSuiteAdd(&ts, &tc3); + CuSuiteAdd(&ts, &tc4); + CuAssertTrue(tc, ts.count == 4); + + CuSuiteRun(&ts); + CuAssertTrue(tc, ts.count - ts.failCount == 2); + CuAssertTrue(tc, ts.failCount == 2); +} + +void TestCuSuiteSummary(CuTest* tc) +{ + CuSuite ts; + CuTest tc1, tc2; + CuString summary; + + CuSuiteInit(&ts); + CuTestInit(&tc1, "TestPasses", TestPasses); + CuTestInit(&tc2, "TestFails", zTestFails); + CuStringInit(&summary); + + CuSuiteAdd(&ts, &tc1); + CuSuiteAdd(&ts, &tc2); + CuSuiteRun(&ts); + + CuSuiteSummary(&ts, &summary); + + CuAssertTrue(tc, ts.count == 2); + CuAssertTrue(tc, ts.failCount == 1); + CuAssertStrEquals(tc, ".F\n\n", summary.buffer); +} + + +void TestCuSuiteDetails_SingleFail(CuTest* tc) +{ + CuSuite ts; + CuTest tc1, tc2; + CuString details; + const char* front; + const char* back; + + CuSuiteInit(&ts); + CuTestInit(&tc1, "TestPasses", TestPasses); + CuTestInit(&tc2, "TestFails", zTestFails); + CuStringInit(&details); + + CuSuiteAdd(&ts, &tc1); + CuSuiteAdd(&ts, &tc2); + CuSuiteRun(&ts); + + CuSuiteDetails(&ts, &details); + + CuAssertTrue(tc, ts.count == 2); + CuAssertTrue(tc, ts.failCount == 1); + + front = "There was 1 failure:\n" + "1) TestFails: "; + back = "test should fail\n" + "\n!!!FAILURES!!!\n" + "Runs: 2 Passes: 1 Fails: 1\n"; + + CuAssertStrEquals(tc, back, details.buffer + strlen(details.buffer) - strlen(back)); + details.buffer[strlen(front)] = 0; + CuAssertStrEquals(tc, front, details.buffer); +} + + +void TestCuSuiteDetails_SinglePass(CuTest* tc) +{ + CuSuite ts; + CuTest tc1; + CuString details; + const char* expected; + + CuSuiteInit(&ts); + CuTestInit(&tc1, "TestPasses", TestPasses); + CuStringInit(&details); + + CuSuiteAdd(&ts, &tc1); + CuSuiteRun(&ts); + + CuSuiteDetails(&ts, &details); + + CuAssertTrue(tc, ts.count == 1); + CuAssertTrue(tc, ts.failCount == 0); + + expected = + "OK (1 test)\n"; + + CuAssertStrEquals(tc, expected, details.buffer); +} + +void TestCuSuiteDetails_MultiplePasses(CuTest* tc) +{ + CuSuite ts; + CuTest tc1, tc2; + CuString details; + const char* expected; + + CuSuiteInit(&ts); + CuTestInit(&tc1, "TestPasses", TestPasses); + CuTestInit(&tc2, "TestPasses", TestPasses); + CuStringInit(&details); + + CuSuiteAdd(&ts, &tc1); + CuSuiteAdd(&ts, &tc2); + CuSuiteRun(&ts); + + CuSuiteDetails(&ts, &details); + + CuAssertTrue(tc, ts.count == 2); + CuAssertTrue(tc, ts.failCount == 0); + + expected = + "OK (2 tests)\n"; + + CuAssertStrEquals(tc, expected, details.buffer); +} + +void TestCuSuiteDetails_MultipleFails(CuTest* tc) +{ + CuSuite ts; + CuTest tc1, tc2; + CuString details; + const char* front; + const char* mid; + const char* back; + + CuSuiteInit(&ts); + CuTestInit(&tc1, "TestFails1", zTestFails); + CuTestInit(&tc2, "TestFails2", zTestFails); + CuStringInit(&details); + + CuSuiteAdd(&ts, &tc1); + CuSuiteAdd(&ts, &tc2); + CuSuiteRun(&ts); + + CuSuiteDetails(&ts, &details); + + CuAssertTrue(tc, ts.count == 2); + CuAssertTrue(tc, ts.failCount == 2); + + front = + "There were 2 failures:\n" + "1) TestFails1: "; + mid = "test should fail\n" + "2) TestFails2: "; + back = "test should fail\n" + "\n!!!FAILURES!!!\n" + "Runs: 2 Passes: 0 Fails: 2\n"; + + CuAssertStrEquals(tc, back, details.buffer + strlen(details.buffer) - strlen(back)); + CuAssert(tc, "Couldn't find middle", strstr(details.buffer, mid) != NULL); + details.buffer[strlen(front)] = 0; + CuAssertStrEquals(tc, front, details.buffer); +} + + +/*-------------------------------------------------------------------------* + * Misc Test + *-------------------------------------------------------------------------*/ + +void TestCuStrCopy(CuTest* tc) +{ + const char* old = "hello world"; + const char* newStr = CuStrCopy(old); + CuAssert(tc, "old is new", strcmp(old, newStr) == 0); +} + + +void TestCuStringAppendFormat(CuTest* tc) +{ + int i; + char* text = CuStrAlloc(301); /* long string */ + CuString* str = CuStringNew(); + for (i = 0 ; i < 300 ; ++i) + text[i] = 'a'; + text[300] = '\0'; + CuStringAppendFormat(str, "%s", text); + + /* buffer limit raised to HUGE_STRING_LEN so no overflow */ + + CuAssert(tc, "length of str->buffer is 300", 300 == strlen(str->buffer)); +} + +void TestFail(CuTest* tc) +{ + jmp_buf buf; + int pointReached = 0; + CuTest* tc2 = CuTestNew("TestFails", zTestFails); + tc2->jumpBuf = &buf; + if (setjmp(buf) == 0) + { + CuFail(tc2, "hello world"); + pointReached = 1; + } + CuAssert(tc, "point was not reached", pointReached == 0); +} + +void TestAssertStrEquals(CuTest* tc) +{ + jmp_buf buf; + CuTest *tc2 = CuTestNew("TestAssertStrEquals", zTestFails); + + const char* expected = "expected but was "; + const char *expectedMsg = "some text: expected but was "; + + tc2->jumpBuf = &buf; + if (setjmp(buf) == 0) + { + CuAssertStrEquals(tc2, "hello", "world"); + } + CuAssertTrue(tc, tc2->failed); + CompareAsserts(tc, "CuAssertStrEquals failed", expected, tc2->message); + if (setjmp(buf) == 0) + { + CuAssertStrEquals_Msg(tc2, "some text", "hello", "world"); + } + CuAssertTrue(tc, tc2->failed); + CompareAsserts(tc, "CuAssertStrEquals failed", expectedMsg, tc2->message); +} + +void TestAssertStrEquals_NULL(CuTest* tc) +{ + jmp_buf buf; + CuTest *tc2 = CuTestNew("TestAssertStrEquals_NULL", zTestFails); + + tc2->jumpBuf = &buf; + if (setjmp(buf) == 0) + { + CuAssertStrEquals(tc2, NULL, NULL); + } + CuAssertTrue(tc, !tc2->failed); + CompareAsserts(tc, "CuAssertStrEquals_NULL failed", NULL, tc2->message); + if (setjmp(buf) == 0) + { + CuAssertStrEquals_Msg(tc2, "some text", NULL, NULL); + } + CuAssertTrue(tc, !tc2->failed); + CompareAsserts(tc, "CuAssertStrEquals_NULL failed", NULL, tc2->message); +} + +void TestAssertStrEquals_FailNULLStr(CuTest* tc) +{ + jmp_buf buf; + CuTest *tc2 = CuTestNew("TestAssertStrEquals_FailNULLStr", zTestFails); + + const char* expected = "expected but was "; + const char *expectedMsg = "some text: expected but was "; + + tc2->jumpBuf = &buf; + if (setjmp(buf) == 0) + { + CuAssertStrEquals(tc2, "hello", NULL); + } + CuAssertTrue(tc, tc2->failed); + CompareAsserts(tc, "CuAssertStrEquals_FailNULLStr failed", expected, tc2->message); + if (setjmp(buf) == 0) + { + CuAssertStrEquals_Msg(tc2, "some text", "hello", NULL); + } + CuAssertTrue(tc, tc2->failed); + CompareAsserts(tc, "CuAssertStrEquals_FailNULLStr failed", expectedMsg, tc2->message); +} + +void TestAssertStrEquals_FailStrNULL(CuTest* tc) +{ + jmp_buf buf; + CuTest *tc2 = CuTestNew("TestAssertStrEquals_FailStrNULL", zTestFails); + + const char* expected = "expected but was "; + const char *expectedMsg = "some text: expected but was "; + + tc2->jumpBuf = &buf; + if (setjmp(buf) == 0) + { + CuAssertStrEquals(tc2, NULL, "hello"); + } + CuAssertTrue(tc, tc2->failed); + CompareAsserts(tc, "CuAssertStrEquals_FailStrNULL failed", expected, tc2->message); + if (setjmp(buf) == 0) + { + CuAssertStrEquals_Msg(tc2, "some text", NULL, "hello"); + } + CuAssertTrue(tc, tc2->failed); + CompareAsserts(tc, "CuAssertStrEquals_FailStrNULL failed", expectedMsg, tc2->message); +} + +void TestAssertIntEquals(CuTest* tc) +{ + jmp_buf buf; + CuTest *tc2 = CuTestNew("TestAssertIntEquals", zTestFails); + const char* expected = "expected <42> but was <32>"; + const char* expectedMsg = "some text: expected <42> but was <32>"; + tc2->jumpBuf = &buf; + if (setjmp(buf) == 0) + { + CuAssertIntEquals(tc2, 42, 32); + } + CuAssertTrue(tc, tc2->failed); + CompareAsserts(tc, "CuAssertIntEquals failed", expected, tc2->message); + if (setjmp(buf) == 0) + { + CuAssertIntEquals_Msg(tc2, "some text", 42, 32); + } + CuAssertTrue(tc, tc2->failed); + CompareAsserts(tc, "CuAssertStrEquals failed", expectedMsg, tc2->message); +} + +void TestAssertDblEquals(CuTest* tc) +{ + jmp_buf buf; + double x = 3.33; + double y = 10.0 / 3.0; + CuTest *tc2 = CuTestNew("TestAssertDblEquals", zTestFails); + char expected[STRING_MAX]; + char expectedMsg[STRING_MAX]; + sprintf(expected, "expected <%lf> but was <%lf>", x, y); + sprintf(expectedMsg, "some text: expected <%lf> but was <%lf>", x, y); + + CuTestInit(tc2, "TestAssertDblEquals", TestPasses); + + CuAssertDblEquals(tc2, x, x, 0.0); + CuAssertTrue(tc, ! tc2->failed); + CuAssertTrue(tc, NULL == tc2->message); + + CuAssertDblEquals(tc2, x, y, 0.01); + CuAssertTrue(tc, ! tc2->failed); + CuAssertTrue(tc, NULL == tc2->message); + + tc2->jumpBuf = &buf; + if (setjmp(buf) == 0) + { + CuAssertDblEquals(tc2, x, y, 0.001); + } + CuAssertTrue(tc, tc2->failed); + CompareAsserts(tc, "CuAssertDblEquals failed", expected, tc2->message); + tc2->jumpBuf = &buf; + if (setjmp(buf) == 0) + { + CuAssertDblEquals_Msg(tc2, "some text", x, y, 0.001); + } + CuAssertTrue(tc, tc2->failed); + CompareAsserts(tc, "CuAssertDblEquals failed", expectedMsg, tc2->message); +} + +/*-------------------------------------------------------------------------* + * main + *-------------------------------------------------------------------------*/ + +CuSuite* CuGetSuite(void) +{ + CuSuite* suite = CuSuiteNew(); + + SUITE_ADD_TEST(suite, TestCuStringAppendFormat); + SUITE_ADD_TEST(suite, TestCuStrCopy); + SUITE_ADD_TEST(suite, TestFail); + SUITE_ADD_TEST(suite, TestAssertStrEquals); + SUITE_ADD_TEST(suite, TestAssertStrEquals_NULL); + SUITE_ADD_TEST(suite, TestAssertStrEquals_FailStrNULL); + SUITE_ADD_TEST(suite, TestAssertStrEquals_FailNULLStr); + SUITE_ADD_TEST(suite, TestAssertIntEquals); + SUITE_ADD_TEST(suite, TestAssertDblEquals); + + SUITE_ADD_TEST(suite, TestCuTestNew); + SUITE_ADD_TEST(suite, TestCuTestInit); + SUITE_ADD_TEST(suite, TestCuAssert); + SUITE_ADD_TEST(suite, TestCuAssertPtrEquals_Success); + SUITE_ADD_TEST(suite, TestCuAssertPtrEquals_Failure); + SUITE_ADD_TEST(suite, TestCuAssertPtrNotNull_Success); + SUITE_ADD_TEST(suite, TestCuAssertPtrNotNull_Failure); + SUITE_ADD_TEST(suite, TestCuTestRun); + + SUITE_ADD_TEST(suite, TestCuSuiteInit); + SUITE_ADD_TEST(suite, TestCuSuiteNew); + SUITE_ADD_TEST(suite, TestCuSuiteAddTest); + SUITE_ADD_TEST(suite, TestCuSuiteAddSuite); + SUITE_ADD_TEST(suite, TestCuSuiteRun); + SUITE_ADD_TEST(suite, TestCuSuiteSummary); + SUITE_ADD_TEST(suite, TestCuSuiteDetails_SingleFail); + SUITE_ADD_TEST(suite, TestCuSuiteDetails_SinglePass); + SUITE_ADD_TEST(suite, TestCuSuiteDetails_MultiplePasses); + SUITE_ADD_TEST(suite, TestCuSuiteDetails_MultipleFails); + + return suite; +} diff --git a/cutest-1.5/README.txt b/cutest-1.5/README.txt new file mode 100644 index 0000000..12b9796 --- /dev/null +++ b/cutest-1.5/README.txt @@ -0,0 +1,211 @@ +HOW TO USE + +You can use CuTest to create unit tests to drive your development +in the style of Extreme Programming. You can also add unit tests to +existing code to ensure that it works as you suspect. + +Your unit tests are an investment. They let you to change your +code and add new features confidently without worrying about +accidentally breaking earlier features. + + +LICENSING + +For details on licensing see license.txt. + + +GETTING STARTED + +To add unit testing to your C code the only files you need are +CuTest.c and CuTest.h. + +CuTestTest.c and AllTests.c have been included to provide an +example of how to write unit tests and then how to aggregate them +into suites and into a single AllTests.c file. Suites allow you +to put group tests into logical sets. AllTests.c combines all the +suites and runs them. + +You should not have to look inside CuTest.c. Looking in +CuTestTest.c and AllTests.c (for example usage) should be +sufficient. + +After downloading the sources, run your compiler to create an +executable called AllTests.exe. For example, if you are using +Windows with the cl.exe compiler you would type: + + cl.exe AllTests.c CuTest.c CuTestTest.c + AllTests.exe + +This will run all the unit tests associated with CuTest and print +the output on the console. You can replace cl.exe with gcc or +your favorite compiler in the command above. + + +DETAILED EXAMPLE + +Here is a more detailed example. We will work through a simple +test first exercise. The goal is to create a library of string +utilities. First, lets write a function that converts a +null-terminated string to all upper case. + +Ensure that CuTest.c and CuTest.h are accessible from your C +project. Next, create a file called StrUtil.c with these +contents: + + #include "CuTest.h" + + char* StrToUpper(char* str) { + return str; + } + + void TestStrToUpper(CuTest *tc) { + char* input = strdup("hello world"); + char* actual = StrToUpper(input); + char* expected = "HELLO WORLD"; + CuAssertStrEquals(tc, expected, actual); + } + + CuSuite* StrUtilGetSuite() { + CuSuite* suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, TestStrToUpper); + return suite; + } + +Create another file called AllTests.c with these contents: + + #include "CuTest.h" + + CuSuite* StrUtilGetSuite(); + + void RunAllTests(void) { + CuString *output = CuStringNew(); + CuSuite* suite = CuSuiteNew(); + + CuSuiteAddSuite(suite, StrUtilGetSuite()); + + CuSuiteRun(suite); + CuSuiteSummary(suite, output); + CuSuiteDetails(suite, output); + printf("%s\n", output->buffer); + } + + int main(void) { + RunAllTests(); + } + +Then type this on the command line: + + gcc AllTests.c CuTest.c StrUtil.c + +to compile. You can replace gcc with your favorite compiler. +CuTest should be portable enough to handle all Windows and Unix +compilers. Then to run the tests type: + + a.out + +This will print an error because we haven't implemented the +StrToUpper function correctly. We are just returning the string +without changing it to upper case. + + char* StrToUpper(char* str) { + return str; + } + +Rewrite this as follows: + + char* StrToUpper(char* str) { + char* p; + for (p = str ; *p ; ++p) *p = toupper(*p); + return str; + } + +Recompile and run the tests again. The test should pass this +time. + + +WHAT TO DO NEXT + +At this point you might want to write more tests for the +StrToUpper function. Here are some ideas: + +TestStrToUpper_EmptyString : pass in "" +TestStrToUpper_UpperCase : pass in "HELLO WORLD" +TestStrToUpper_MixedCase : pass in "HELLO world" +TestStrToUpper_Numbers : pass in "1234 hello" + +As you write each one of these tests add it to StrUtilGetSuite +function. If you don't the tests won't be run. Later as you write +other functions and write tests for them be sure to include those +in StrUtilGetSuite also. The StrUtilGetSuite function should +include all the tests in StrUtil.c + +Over time you will create another file called FunkyStuff.c +containing other functions unrelated to StrUtil. Follow the same +pattern. Create a FunkyStuffGetSuite function in FunkyStuff.c. +And add FunkyStuffGetSuite to AllTests.c. + +The framework is designed in the way it is so that it is easy to +organize a lot of tests. + +THE BIG PICTURE + +Each individual test corresponds to a CuTest. These are grouped +to form a CuSuite. CuSuites can hold CuTests or other CuSuites. +AllTests.c collects all the CuSuites in the program into a single +CuSuite which it then runs as a single CuSuite. + +The project is open source so feel free to take a peek under the +hood at the CuTest.c file to see how it works. CuTestTest.c +contains tests for CuTest.c. So CuTest tests itself. + +Since AllTests.c has a main() you will need to exclude this when +you are building your product. Here is a nicer way to do this if +you want to avoid messing with multiple builds. Remove the main() +in AllTests.c. Note that it just calls RunAllTests(). Instead +we'll call this directly from the main program. + +Now in the main() of the actual program check to see if the +command line option "--test" was passed. If it was then I call +RunAllTests() from AllTests.c. Otherwise run the real program. + +Shipping the tests with the code can be useful. If you customers +complain about a problem you can ask them to run the unit tests +and send you the output. This can help you to quickly isolate the +piece of your system that is malfunctioning in the customer's +environment. + +CuTest offers a rich set of CuAssert functions. Here is a list: + +void CuAssert(CuTest* tc, char* message, int condition); +void CuAssertTrue(CuTest* tc, int condition); +void CuAssertStrEquals(CuTest* tc, char* expected, char* actual); +void CuAssertIntEquals(CuTest* tc, int expected, int actual); +void CuAssertPtrEquals(CuTest* tc, void* expected, void* actual); +void CuAssertPtrNotNull(CuTest* tc, void* pointer); + +The project is open source and so you can add other more powerful +asserts to make your tests easier to write and more concise. +Please feel free to send me changes you make so that I can +incorporate them into future releases. + +If you see any errors in this document please contact me at +asimjalis@peakprogramming.com. + + +AUTOMATING TEST SUITE GENERATION + +make-tests.sh will grep through all the .c files in the current +directory and generate the code to run all the tests contained in +them. Using this script you don't have to worry about writing +AllTests.c or dealing with any of the other suite code. + + +CREDITS + +These people have contributed useful code changes to the CuTest project. +Thanks! + +- [02.23.2003] Dave Glowacki +- [04.17.2009] Tobias Lippert +- [11.13.2009] Eli Bendersky +- [12.14.2009] Andrew Brown diff --git a/cutest-1.5/index.html b/cutest-1.5/index.html new file mode 100644 index 0000000..f8c9a42 --- /dev/null +++ b/cutest-1.5/index.html @@ -0,0 +1,196 @@ + + + + + +CuTest: The Cutest C Unit Testing Framework + + + + + + + +
+
+
+ +[ + Download Now +| Download Statistics +| Browse Source +| SourceForge +| Blog +| asimjalis(nospam)gmail.com +] + +
+ + +

CuTest: C Unit Testing Framework

+ + +

Overview

+ +CuTest is a unit testing library for the C language. It can be +used to do Extreme Programming and Test-First Development in the +C language. It's a fun and cute library that will make your +programming fun and productive. + +

Benefits

+ +
    + +
  • Lower Defects. The tests ensure that your code keeps working +as you make small changes in it. + +
  • Faster Debugging. The tests tell you which subroutine is +broken. You avoid spending hours trying to figure out what's +broken. + +
  • Development Speed. You trust your old code and can keep +adding to it without worrying about bad interactions. If there is +a bad interaction the tests will catch it. + +
  • Permanent Bug Fixes. If every time a bug is reported you +write a quick test, you will guarantee that the bug never +reappears again. + +
  • Fun. As your bug count drops you will begin to enjoy +programming like you've never done before. Running the tests +every few minutes and seeing them pass feels good. + +
+ +

Features

+ +
    + +
  • Small. Consists of a single .c and .h file. + +
  • Easy to Deploy. Just drop the two files into your source +tree. + +
  • Highly Portable. Works with all major compilers on Windows +(Microsoft, Borland), Linux, Unix, PalmOS. + +
  • Open Source. You can extend it to add more functionality. +The source can be invaluable if you are trying to trace a test +failure. + +
  • Cuteness. Of all the testing frameworks CuTest has the +cutest name :-) + +
+ + +

Licensing

+ +CuTest is distributed under the zlib/libpng +license. See license.txt in the distribution for text of license. The +intent of the license is to: + +
    + +
  • Keep the license as simple as possible +
  • Encourage the use of CuTest in both free and commercial applications and libraries +
  • Keep the source code together +
  • Give credit to the CuTest contributors for their work + +
+ +If you find CuTest useful we would like to hear about it. + + +

Getting Started

+ +

For a detailed tutorial see README in the distribution. This shows you how +to organize your tests and how to autogenerate the AllTests.c file from your +source files. + +

To add unit testing to your C code the only files you need +are CuTest.c and CuTest.h. + +

CuTestTest.c and AllTests.c have been included to provide an +example of how to write unit tests and then how to aggregate them +into suites and into a single AllTests.c file. Suites allow you +to put unit tests for different parts of your code in different +files. AllTests.c combines all the suites and runs them. + +

You should not have to look inside CuTest.c. Looking in +CuTestTest.c (for example usage) should be sufficient. + +

After downloading the sources, run your compiler to create an +executable called AllTests.exe. For example, if you are using +Windows you would type: + +

+cl AllTests.c CuTest.c CuTestTest.c
+AllTests.exe
+
+ +

This will run all the unit tests associated with CuTest and +print the output on the console. + +

For more details on how to use the library look at the README file included +with the distribution. + +

Contribute

+ +

We hope you CuTest saves you time and helps you produce high quality +software. + +

If you find CuTest useful, let us know. Tell us what platform you are using +it on (Windows, Linux, etc), and what kinds of applications you are using it +with. + +

If you would like to contribute documentation or tutorials to this project +please send e-mail. + +
+
+ +


+ +Copyright © 2002-2003, Asim Jalis (asimjalis(nospam)gmail.com). +All rights reserved. + + + + +

+SourceForge.net Logo + + + diff --git a/cutest-1.5/license.txt b/cutest-1.5/license.txt new file mode 100644 index 0000000..5f053ba --- /dev/null +++ b/cutest-1.5/license.txt @@ -0,0 +1,38 @@ +NOTE + +The license is based on the zlib/libpng license. For more details see +http://www.opensource.org/licenses/zlib-license.html. The intent of the +license is to: + +- keep the license as simple as possible +- encourage the use of CuTest in both free and commercial applications + and libraries +- keep the source code together +- give credit to the CuTest contributors for their work + +If you ship CuTest in source form with your source distribution, the +following license document must be included with it in unaltered form. +If you find CuTest useful we would like to hear about it. + +LICENSE + +Copyright (c) 2003 Asim Jalis + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software in +a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not +be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. diff --git a/cutest-1.5/make-tests.sh b/cutest-1.5/make-tests.sh new file mode 100644 index 0000000..3988c5e --- /dev/null +++ b/cutest-1.5/make-tests.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +# Auto generate single AllTests file for CuTest. +# Searches through all *.c files in the current directory. +# Prints to stdout. +# Author: Asim Jalis +# Date: 01/08/2003 + +if test $# -eq 0 ; then FILES=*.c ; else FILES=$* ; fi + +echo ' + +/* This is auto-generated code. Edit at your own peril. */ +#include +#include + +#include "CuTest.h" + +' + +cat $FILES | grep '^void Test' | + sed -e 's/(.*$//' \ + -e 's/$/(CuTest*);/' \ + -e 's/^/extern /' + +echo \ +' + +void RunAllTests(void) +{ + CuString *output = CuStringNew(); + CuSuite* suite = CuSuiteNew(); + +' +cat $FILES | grep '^void Test' | + sed -e 's/^void //' \ + -e 's/(.*$//' \ + -e 's/^/ SUITE_ADD_TEST(suite, /' \ + -e 's/$/);/' + +echo \ +' + CuSuiteRun(suite); + CuSuiteSummary(suite, output); + CuSuiteDetails(suite, output); + printf("%s\n", output->buffer); + CuStringDelete(output); + CuSuiteDelete(suite); +} + +int main(void) +{ + RunAllTests(); +} +' diff --git a/cutest-1.5/style.css b/cutest-1.5/style.css new file mode 100644 index 0000000..7759d5a --- /dev/null +++ b/cutest-1.5/style.css @@ -0,0 +1,21 @@ +BODY { + font-family: "Verdana,Arial,sans-serif"; + font-size: 10pt; +} +PRE.LIST { + background: #CFD9FF; + border: 1ex solid #AAAAE6; + padding: 1ex; +} + +PRE.SNIP { + background: #CFD9FF; + padding: 1ex; +} + +PRE.CONSOLE { + color: #CCCCCC; + background: #000000; + font-family: "Courier New,Courier"; + padding: 1ex; +} diff --git a/game.c b/game.c index 469228c..e3e602c 100644 --- a/game.c +++ b/game.c @@ -17,6 +17,7 @@ #include "movement.h" #include "combat.h" #include "game.h" +#include "spawn.h" // TODO: centralize these outside of game.c struct CompiledSpriteRender rabbit, @@ -49,17 +50,6 @@ struct BulletPosition enemyBulletPosition[ENEMY_BULLET_LIMIT]; struct RabbitWeaponry rabbitWeaponry; struct PlayerPowerup playerPowerup; -struct SpawnPointRange spawnPointRanges[4] = { - // top - { .left = TILE_SIZE * 4, .width = TILE_SIZE * 2, .top = TILE_SIZE - 8, .height = TILE_SIZE }, - // right - { .left = (ARENA_WIDTH_TILES - 1) * TILE_SIZE - 8, .width = TILE_SIZE, .top = TILE_SIZE * 4, .height = TILE_SIZE * 2 }, - // bottom - { .left = TILE_SIZE * 4, .width = TILE_SIZE * 2, .top = (ARENA_HEIGHT_TILES - 1) * TILE_SIZE - 8, .height = TILE_SIZE }, - // left - { .left = TILE_SIZE - 8, .width = TILE_SIZE, .top = TILE_SIZE * 4, .height = TILE_SIZE * 2 }, -}; - void setupRabbitBullets() { int i; @@ -99,8 +89,11 @@ void setupEnemies() { } } -int spawnCooldown = 0; -int difficulty = 0; +struct GlobalGameState globalGameState = { + .spawnCooldown = 0, + .difficulty = 0 +}; + int kills = 0; int health = RABBIT_HEALTH_MAX; @@ -131,69 +124,19 @@ void handleEnemyKills() { playerPowerup.y = TILE_SIZE + rand() % ((ARENA_HEIGHT_TILES - 2) * TILE_SIZE); playerPowerup.isActive = 1; - playerPowerup.cooldown = POWERUP_RESPAWN_COOLDOWN_PER_LEVEL * difficulty + - rand() % (POWERUP_RESPAWN_COOLDOWN_PER_LEVEL * difficulty); + playerPowerup.cooldown = POWERUP_RESPAWN_COOLDOWN_PER_LEVEL * globalGameState.difficulty + + rand() % (POWERUP_RESPAWN_COOLDOWN_PER_LEVEL * globalGameState.difficulty); } if (hadKill) { for (i = 0; i < 10; ++i) { if (kills > difficultyBands[i]) { - difficulty = i + 1; + globalGameState.difficulty = i + 1; } } } } -void maybeSpawnEnemy() { - char canSpawn; - int i, gate, availableEnemy, gatePlayerIsFacing; - int spawnX, spawnY, spawnTry; - - char buffer[20]; - - if (spawnCooldown-- > 0) return; - - for (i = 0; i < ENEMY_MAX_COUNT; ++i) { - if (!enemyPosition[i].isActive) { - availableEnemy = i; - canSpawn = 1; - break; - } - } - - if (!canSpawn) return; - - // determine which gate the player is at and reduce the likelihood of - // spawning from that gate - // * 8 chances to spawn [0, 1] for each gate - // * if the gate the player is looking at gets [0], reroll - // * try three times max to limit cost of calculating - for (spawnTry = 0; spawnTry < 3; ++spawnTry) { - i = rand() % 12; - gate = i / 3; - - if (gatePlayerIsFacing == gate && i % 3 != 0) continue; - } - - spawnX = spawnPointRanges[gate].left + rand() % spawnPointRanges[gate].width; - spawnY = spawnPointRanges[gate].top + rand() % spawnPointRanges[gate].height; - - enemyPosition[availableEnemy].isActive = 1; - enemyPosition[availableEnemy].willBeInactive = 0; - enemyPosition[availableEnemy].wasKilled = 0; - enemyPosition[availableEnemy].hitPoints = 1 + difficulty / ENEMY_HIT_POINT_DIFFICULTY_INCREASE_EVERY; - enemyPosition[availableEnemy].hasLeftGate = 0; - enemyPosition[availableEnemy].gateExitedFrom = gate; - enemyPosition[availableEnemy].enemyMoveDelayStep = 0; - enemyPosition[availableEnemy].enemyFireDelayStep = ENEMY_FIRE_MIN_DELAY + rand() % ENEMY_FIRE_VARIABLE_DELAY; - enemyPosition[availableEnemy].enemyPosition[0] = spawnX; - enemyPosition[availableEnemy].enemyPosition[1] = spawnY; - enemyPosition[availableEnemy].oldEnemyPosition[0] = spawnX; - enemyPosition[availableEnemy].oldEnemyPosition[1] = spawnY; - - spawnCooldown = (BASE_ENEMY_SPAWN_COOLDOWN - (difficulty * DIFFICULTY_SPAWN_COOLDOWN_REDUCTION)) + MINIMUM_ENEMY_SPAWN_COOLDOWN + rand() % VARIABLE_ENEMY_SPAWN_COOLDOWN; -} - void setupEnemySprites() { buildCompiledSprite( &sprite_enemy, @@ -495,7 +438,7 @@ void handleCombat() { enemyPosition, enemyBulletPosition, &rabbitPosition, - difficulty + globalGameState.difficulty ); advanceRabbitBullets( @@ -541,7 +484,7 @@ void handleCombat() { if (handleRabbitToPowerupCollision(&rabbitPosition, &playerPowerup)) { playerPowerup.willBeInactive = 1; rabbitWeaponry.currentWeapon = WEAPON_TYPE_SPREAD_SHOT_GUN; - rabbitWeaponry.currentWeaponRemainingRounds = (difficulty + 1) * SHOTGUN_ROUNDS_PER_LEVEL; + rabbitWeaponry.currentWeaponRemainingRounds = (globalGameState.difficulty + 1) * SHOTGUN_ROUNDS_PER_LEVEL; } } @@ -585,7 +528,11 @@ int main(void) { readMouse(&mouseStatus); populateKeyboardKeydownState(); - maybeSpawnEnemy(); + maybeSpawnEnemy( + &globalGameState, + enemyPosition, + &rabbitPosition + ); handleMovement(); handleRedraw(); diff --git a/game.h b/game.h index 17d8cc5..9beb271 100644 --- a/game.h +++ b/game.h @@ -3,13 +3,12 @@ #include "system/vga.h" -struct SpawnPointRange { - int left, width; - int top, height; -}; - extern struct CompiledSpriteRender rabbit, mouse, bullet, enemy, enemyBullet, shotgun, shieldKiller; extern struct SpriteBounds bounds; -extern struct SpawnPointRange spawnPointRanges[]; + +struct GlobalGameState { + int difficulty; + int spawnCooldown; +}; #endif diff --git a/makefile b/makefile index 7f40282..cb1e5b3 100644 --- a/makefile +++ b/makefile @@ -1,6 +1,6 @@ -obj = sprites.o game.o bmpload.o arena.o movement.o combat.o +obj = sprites.o game.o bmpload.o arena.o movement.o combat.o spawn.o -all: system/system.lib game.exe .SYMBOLIC +all: system/system.lib game.exe test .SYMBOLIC system/system.lib: .SYMBOLIC cd system @@ -19,6 +19,9 @@ sprites.asm: sprtsht.bmp spritesheet.yml game.exe: $(obj) system/system.lib wcl386 -q -fe=game -bt=dos -l=dos4g $(obj) system/system.lib +test: test.c spawn_test.c spawn.c .SYMBOLIC + wcl386 -fe=test -bt=dos -l=dos4g test.c spawn.c spawn_test.c cutest-1.5/CuTest.c + clean: .SYMBOLIC rm *.o rm system/*.o diff --git a/movement.c b/movement.c index db2a833..66bb54f 100644 --- a/movement.c +++ b/movement.c @@ -4,8 +4,9 @@ #include "game.h" #include "const.h" #include "movement.h" -#include "system/mouse_io.h" +#include "spawn.h" +#include "system/mouse_io.h" #include "system/pc_stuff.h" void captureAndLimitMousePosition( diff --git a/spawn.c b/spawn.c new file mode 100644 index 0000000..22eff90 --- /dev/null +++ b/spawn.c @@ -0,0 +1,118 @@ +#include +#include + +#include "game.h" +#include "movement.h" +#include "const.h" +#include "spawn.h" + +struct SpawnPointRange spawnPointRanges[4] = { + // top + { .left = TILE_SIZE * 4, .width = TILE_SIZE * 2, .top = TILE_SIZE - 8, .height = TILE_SIZE }, + // right + { .left = (ARENA_WIDTH_TILES - 1) * TILE_SIZE - 8, .width = TILE_SIZE, .top = TILE_SIZE * 4, .height = TILE_SIZE * 2 }, + // bottom + { .left = TILE_SIZE * 4, .width = TILE_SIZE * 2, .top = (ARENA_HEIGHT_TILES - 1) * TILE_SIZE - 8, .height = TILE_SIZE }, + // left + { .left = TILE_SIZE - 8, .width = TILE_SIZE, .top = TILE_SIZE * 4, .height = TILE_SIZE * 2 }, +}; + +int HIT_POINT_RANGES[10][4] = { + { 1, 1, 1, 1 }, + { 1, 1, 1, 1 }, + { 1, 1, 1, 2 }, + { 1, 1, 1, 2 }, + { 1, 1, 2, 2 }, + { 1, 2, 2, 2 }, + { 1, 2, 2, 2 }, + { 1, 2, 2, 3 }, + { 1, 2, 2, 3 }, + { 1, 2, 3, 3 } +}; + +int determineEnemyHitPointCalculation(int difficulty, int roll) { + return HIT_POINT_RANGES[difficulty][roll]; +} + +void selectEnemySpawnLocation( + struct EnemySpawningDetails *spawnDetails, + struct RabbitPosition *rabbitPosition +) { + int spawnTry, gate, i; + int gatePlayerIsFacing = rabbitPosition->mouseAngle / 90; + + // determine which gate the player is at and reduce the likelihood of + // spawning from that gate + // * 8 chances to spawn [0, 1] for each gate + // * if the gate the player is looking at gets [0], reroll + // * try three times max to limit cost of calculating + for (spawnTry = 0; spawnTry < 3; ++spawnTry) { + i = rand() % 12; + gate = i / 3; + + if (gatePlayerIsFacing == gate && i % 3 != 0) continue; + } + + spawnDetails->gate = gate; + spawnDetails->spawnX = spawnPointRanges[gate].left + rand() % spawnPointRanges[gate].width; + spawnDetails->spawnY = spawnPointRanges[gate].top + rand() % spawnPointRanges[gate].height; +} + +void spawnEnemy( + struct EnemySpawningDetails *spawnDetails, + struct GlobalGameState *globalGameState +) { + spawnDetails->enemyPosition->isActive = 1; + spawnDetails->enemyPosition->willBeInactive = 0; + spawnDetails->enemyPosition->wasKilled = 0; + spawnDetails->enemyPosition->hitPoints = determineEnemyHitPointCalculation( + globalGameState->difficulty, + rand() % 4 + ); + spawnDetails->enemyPosition->hasLeftGate = 0; + spawnDetails->enemyPosition->gateExitedFrom = spawnDetails->gate; + spawnDetails->enemyPosition->enemyMoveDelayStep = 0; + spawnDetails->enemyPosition->enemyFireDelayStep = ENEMY_FIRE_MIN_DELAY + rand() % ENEMY_FIRE_VARIABLE_DELAY; + spawnDetails->enemyPosition->enemyPosition[0] = spawnDetails->spawnX; + spawnDetails->enemyPosition->enemyPosition[1] = spawnDetails->spawnY; + spawnDetails->enemyPosition->oldEnemyPosition[0] = spawnDetails->spawnX; + spawnDetails->enemyPosition->oldEnemyPosition[1] = spawnDetails->spawnY; +} + +void maybeSpawnEnemy( + struct GlobalGameState *globalGameState, + struct EnemyPosition enemyPosition[], + struct RabbitPosition *rabbitPosition +) { + char canSpawn; + int i, availableEnemy; + + struct EnemySpawningDetails spawnDetails; + + if (globalGameState->spawnCooldown-- > 0) return; + + for (i = 0; i < ENEMY_MAX_COUNT; ++i) { + if (!enemyPosition[i].isActive) { + availableEnemy = i; + canSpawn = 1; + break; + } + } + + if (!canSpawn) return; + + spawnDetails.enemyPosition = &enemyPosition[availableEnemy]; + spawnDetails.difficulty = globalGameState->difficulty; + selectEnemySpawnLocation( + &spawnDetails, + rabbitPosition + ); + spawnEnemy( + &spawnDetails, + globalGameState + ); + + globalGameState->spawnCooldown = ( + BASE_ENEMY_SPAWN_COOLDOWN - (globalGameState->difficulty * DIFFICULTY_SPAWN_COOLDOWN_REDUCTION)) + + MINIMUM_ENEMY_SPAWN_COOLDOWN + rand() % VARIABLE_ENEMY_SPAWN_COOLDOWN; +} diff --git a/spawn.h b/spawn.h new file mode 100644 index 0000000..3b79d7b --- /dev/null +++ b/spawn.h @@ -0,0 +1,26 @@ +#ifndef __SPAWN_H__ +#define __SPAWN_H__ + +#include "game.h" +#include "movement.h" + +void maybeSpawnEnemy( + struct GlobalGameState*, struct EnemyPosition[], struct RabbitPosition* +); + +struct EnemySpawningDetails { + struct EnemyPosition *enemyPosition; + int gate; + int difficulty; + int spawnX; + int spawnY; +}; + +struct SpawnPointRange { + int left, width; + int top, height; +}; + +extern struct SpawnPointRange spawnPointRanges[]; + +#endif diff --git a/spawn_test.c b/spawn_test.c new file mode 100644 index 0000000..754982b --- /dev/null +++ b/spawn_test.c @@ -0,0 +1,82 @@ +#include +#include "cutest-1.5/CuTest.h" + +#include "movement.h" +#include "game.h" +#include "spawn.h" +#include "movement.h" + +// testing these private methods, i know +void selectEnemySpawnLocation( + struct EnemySpawningDetails *spawnDetails, + struct RabbitPosition *rabbitPosition +); +int determineEnemyHitPointCalculation(int difficulty, int pcnt); +int spawnEnemy(struct EnemySpawningDetails*, struct GlobalGameState*); + +struct EnemySpawningDetails spawnDetails; +struct RabbitPosition rabbitPosition; +struct GlobalGameState globalGameState; +struct EnemyPosition enemyPosition; + +void TestSelectEnemySpawnLocation(CuTest *tc) { + srand(1); + + spawnDetails.gate = -1; + spawnDetails.spawnX = -1; + spawnDetails.spawnY = -1; + + rabbitPosition.mouseAngle = 30; + + selectEnemySpawnLocation( + &spawnDetails, + &rabbitPosition + ); + + // built from setting seed to 1 + CuAssertIntEquals(tc, 3, spawnDetails.gate); + CuAssertIntEquals(tc, 27, spawnDetails.spawnX); + CuAssertIntEquals(tc, 91, spawnDetails.spawnY); +} + +void TestDetermineEnemyHitPointCalculation(CuTest *tc) { + srand(1); + + CuAssertIntEquals(tc, 1, determineEnemyHitPointCalculation(0, 0)); + CuAssertIntEquals(tc, 1, determineEnemyHitPointCalculation(0, 3)); + + CuAssertIntEquals(tc, 1, determineEnemyHitPointCalculation(2, 0)); + CuAssertIntEquals(tc, 1, determineEnemyHitPointCalculation(2, 2)); + CuAssertIntEquals(tc, 2, determineEnemyHitPointCalculation(2, 3)); +} + +void TestSpawnEnemy(CuTest *tc) { + srand(1); + + spawnDetails.gate = 1; + spawnDetails.spawnX = 100; + spawnDetails.spawnY = 50; + spawnDetails.enemyPosition = &enemyPosition; + globalGameState.difficulty = 4; + + spawnEnemy(&spawnDetails, &globalGameState); + + // randomized values + CuAssertIntEquals(tc, 2, enemyPosition.hitPoints); + CuAssertIntEquals(tc, 103, enemyPosition.enemyFireDelayStep); + + // from spawndetails + CuAssertIntEquals(tc, 1, enemyPosition.gateExitedFrom); + CuAssertIntEquals(tc, 100, enemyPosition.enemyPosition[0]); + CuAssertIntEquals(tc, 50, enemyPosition.enemyPosition[1]); + CuAssertIntEquals(tc, 100, enemyPosition.oldEnemyPosition[0]); + CuAssertIntEquals(tc, 50, enemyPosition.oldEnemyPosition[1]); +} + +CuSuite *SpawnGetSuite() { + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, TestSelectEnemySpawnLocation); + SUITE_ADD_TEST(suite, TestDetermineEnemyHitPointCalculation); + SUITE_ADD_TEST(suite, TestSpawnEnemy); + return suite; +} diff --git a/test b/test new file mode 100755 index 0000000000000000000000000000000000000000..887576f7f3d82e8a2ebe648b5ea277a653a20f44 GIT binary patch literal 39124 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 literal 0 HcmV?d00001 diff --git a/test.c b/test.c new file mode 100644 index 0000000..dee4f63 --- /dev/null +++ b/test.c @@ -0,0 +1,20 @@ +#include "cutest-1.5/CuTest.h" +#include + +CuSuite *SpawnGetSuite(); + +void RunAllTests(void) { + CuString *output = CuStringNew(); + CuSuite *suite = CuSuiteNew(); + + CuSuiteAddSuite(suite, SpawnGetSuite()); + + CuSuiteRun(suite); + CuSuiteSummary(suite, output); + CuSuiteDetails(suite, output); + printf("%s\n", output->buffer); +} + +int main(void) { + RunAllTests(); +}