/* * This code is based on CuTest by Asim Jalis * http://sourceforge.net/projects/cutest/ * * The license for the original code is * 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. */ #include #include #include #include #include #include #include #include #include "cutest.h" #include "memory.h" #define HUGE_STRING_LEN 8192 #define STRING_MAX 256 #define asprintf_or_die(strp, fmt, args ...) \ if (asprintf(strp, fmt, ## args) == -1) { \ fprintf(stderr, "Fatal error (probably out of memory)\n"); \ abort(); \ } void die_oom(void) { printf("Ran out of memory. Send more\n"); exit(2); } /*-------------------------------------------------------------------------* * CuTest *-------------------------------------------------------------------------*/ void CuTestInit(CuTest* t, const char* name, TestFunction function) { t->name = strdup(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 = NULL; if (ALLOC(tc) < 0) die_oom(); CuTestInit(tc, name, function); return tc; } void CuTestRun(CuTest* tc, TestFunction setup, TestFunction teardown) { jmp_buf buf; if (getenv("CUTEST") && STRNEQ(getenv("CUTEST"), tc->name)) return; tc->jumpBuf = &buf; if (setjmp(buf) == 0) { if (setup) (setup)(tc); tc->ran = 1; (tc->function)(tc); } if (teardown && setjmp(buf) == 0) { (teardown)(tc); } tc->jumpBuf = 0; } static void CuFailInternal(CuTest* tc, const char* file, int line, const char *string) { char *buf = NULL; asprintf_or_die(&buf, "%s:%d: %s", file, line, string); tc->failed = 1; tc->message = buf; if (tc->jumpBuf != 0) longjmp(*(tc->jumpBuf), 0); } void CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message) { char *string = NULL; if (message2 != NULL) { asprintf_or_die(&string, "%s:%s", message2, message); } else { string = strdup(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) { char *string = NULL; if ((expected == NULL && actual == NULL) || (expected != NULL && actual != NULL && strcmp(expected, actual) == 0)) { return; } if (message != NULL) { asprintf_or_die(&string, "%s: expected <%s> but was <%s>", message, expected, actual); } else { asprintf_or_die(&string, "expected <%s> but was <%s>", expected, actual); } CuFailInternal(tc, file, line, string); } void CuAssertStrNotEqual_LineMsg(CuTest* tc, const char* file, int line, const char* message, const char* expected, const char* actual) { char *string = NULL; if (expected != NULL && actual != NULL && strcmp(expected, actual) != 0) return; if (message != NULL) { asprintf_or_die(&string, "%s: expected <%s> but was <%s>", message, expected, actual); } else { asprintf_or_die(&string, "expected <%s> but was <%s>", expected, actual); } 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 <%lf> but was <%lf>", expected, actual); CuFail_Line(tc, file, line, message, buf); } void CuAssertPtrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, const void* expected, const 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); } void CuAssertPtrNotEqual_LineMsg(CuTest* tc, const char* file, int line, const char* message, const void* expected, const void* actual) { char buf[STRING_MAX]; if (expected != actual) return; sprintf(buf, "expected pointer <0x%p> to be different from <0x%p>", expected, actual); CuFail_Line(tc, file, line, message, buf); } /*-------------------------------------------------------------------------* * CuSuite *-------------------------------------------------------------------------*/ void CuSuiteInit(CuSuite* testSuite) { testSuite->count = 0; testSuite->failCount = 0; } CuSuite* CuSuiteNew(void) { CuSuite* testSuite = NULL; if (ALLOC(testSuite) < 0) die_oom(); CuSuiteInit(testSuite); return testSuite; } void CuSuiteSetup(CuSuite *testSuite, TestFunction setup, TestFunction teardown) { testSuite->setup = setup; testSuite->teardown = teardown; } 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, testSuite->setup, testSuite->teardown); if (testCase->failed) { testSuite->failCount += 1; } } } static void string_append(char **s, const char *p) { if (*s == NULL) { *s = strdup(p); } else { int len = strlen(*s) + strlen(p) + 1; *s = realloc(*s, len); if (*s == NULL) die_oom(); strcat(*s, p); } } void CuSuiteSummary(CuSuite* testSuite, char **summary) { int i; for (i = 0 ; i < testSuite->count ; ++i) { CuTest* testCase = testSuite->list[i]; string_append(summary, testCase->failed ? "F" : "."); } string_append(summary, "\n\n"); } void CuSuiteDetails(CuSuite* testSuite, char **details) { int i; int failCount = 0; char *s = NULL; if (testSuite->failCount == 0) { int passCount = testSuite->count - testSuite->failCount; const char* testWord = passCount == 1 ? "test" : "tests"; asprintf_or_die(&s, "OK (%d %s)\n", passCount, testWord); string_append(details, s); free(s); } else { if (testSuite->failCount == 1) string_append(details, "There was 1 failure:\n"); else { asprintf_or_die(&s, "There were %d failures:\n", testSuite->failCount); string_append(details, s); free(s); } for (i = 0 ; i < testSuite->count ; ++i) { CuTest* testCase = testSuite->list[i]; if (testCase->failed) { failCount++; asprintf_or_die(&s, "%d) %s:\n%s\n", failCount, testCase->name, testCase->message); string_append(details, s); free(s); } } string_append(details, "\n!!!FAILURES!!!\n"); asprintf_or_die(&s, "Runs: %d ", testSuite->count); string_append(details, s); free(s); asprintf_or_die(&s, "Passes: %d ", testSuite->count - testSuite->failCount); string_append(details, s); free(s); asprintf_or_die(&s, "Fails: %d\n", testSuite->failCount); string_append(details, s); free(s); } } void CuSuiteFree(CuSuite *suite) { for (int i=0; i < suite->count; i++) { CuTest *test = suite->list[i]; free(test->name); free(test->message); free(test); } free(suite); } /* * Test utilities */ void run(CuTest *tc, const char *format, ...) { char *command; va_list args; int r; va_start(args, format); r = vasprintf(&command, format, args); va_end (args); if (r < 0) CuFail(tc, "Failed to format command (out of memory)"); r = system(command); if (r < 0 || (WIFEXITED(r) && WEXITSTATUS(r) != 0)) { char *msg; r = asprintf(&msg, "Command %s failed with status %d\n", command, WEXITSTATUS(r)); CuFail(tc, msg); free(msg); } free(command); } int should_run(const char *name, int argc, char **argv) { if (argc == 0) return 1; for (int i=0; i < argc; i++) if (STREQ(argv[i], name)) return 1; return 0; } /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */