Add setup and teardown hooks per test suite

This commit is contained in:
Ueli Niederer 2020-03-07 14:34:10 +01:00
parent 0fe17b4c0f
commit 02898b90c3
4 changed files with 342 additions and 7 deletions

View File

@ -4,6 +4,7 @@
CuSuite* CuGetSuite(void); CuSuite* CuGetSuite(void);
CuSuite* CuStringGetSuite(void); CuSuite* CuStringGetSuite(void);
CuSuite* CuSuiteFrameGetSuite(void);
int RunAllTests(void) int RunAllTests(void)
{ {
@ -12,6 +13,7 @@ int RunAllTests(void)
CuSuiteAddSuite(suite, CuGetSuite()); CuSuiteAddSuite(suite, CuGetSuite());
CuSuiteAddSuite(suite, CuStringGetSuite()); CuSuiteAddSuite(suite, CuStringGetSuite());
CuSuiteAddSuite(suite, CuSuiteFrameGetSuite());
CuSuiteRun(suite); CuSuiteRun(suite);
CuSuiteSummary(suite, output); CuSuiteSummary(suite, output);

View File

@ -134,18 +134,22 @@ void CuTestDelete(CuTest *t)
free(t); free(t);
} }
void CuTestRun(CuTest* tc) static void TestFunctionRun(CuTest *tc, TestFunction function) {
{
jmp_buf buf; jmp_buf buf;
tc->jumpBuf = &buf; tc->jumpBuf = &buf;
if (setjmp(buf) == 0) if (setjmp(buf) == 0)
{ {
tc->ran = 1; tc->ran = 1;
(tc->function)(tc); function(tc);
} }
tc->jumpBuf = 0; tc->jumpBuf = 0;
} }
void CuTestRun(CuTest* tc)
{
TestFunctionRun(tc, tc->function);
}
static void CuFailInternal(CuTest* tc, const char* file, int line, CuString* string) static void CuFailInternal(CuTest* tc, const char* file, int line, CuString* string)
{ {
char buf[HUGE_STRING_LEN]; char buf[HUGE_STRING_LEN];
@ -233,22 +237,52 @@ void CuAssertPtrEquals_LineMsg(CuTest* tc, const char* file, int line, const cha
CuFail_Line(tc, file, line, message, buf); CuFail_Line(tc, file, line, message, buf);
} }
void *CuTestContextGet(CuTest *tc) {
return tc->context;
}
void CuTestContextSet(CuTest *tc, void *context) {
tc->context = context;
}
/*-------------------------------------------------------------------------* /*-------------------------------------------------------------------------*
* CuSuite * CuSuite
*-------------------------------------------------------------------------*/ *-------------------------------------------------------------------------*/
static void EmptySetup(CuTest *tc) {
(void)tc;
}
static void EmptyTeardown(CuTest *tc) {
(void)tc;
}
static const CuTestFrame EmptyFrame = {
.setup = EmptySetup,
.teardown = EmptyTeardown,
};
void CuSuiteInit(CuSuite* testSuite) void CuSuiteInit(CuSuite* testSuite)
{
CuSuiteInitWithFrame(testSuite, &EmptyFrame, NULL);
}
void CuSuiteInitWithFrame(CuSuite* testSuite, const CuTestFrame *frame, void *frameContext)
{ {
testSuite->count = 0; testSuite->count = 0;
testSuite->failCount = 0; testSuite->failCount = 0;
memset(testSuite->list, 0, sizeof(testSuite->list)); memset(testSuite->list, 0, sizeof(testSuite->list));
testSuite->frame = frame;
testSuite->frameContext = frameContext;
} }
CuSuite* CuSuiteNew(void) CuSuite* CuSuiteNew(void)
{ {
return CuSuiteNewWithFrame(&EmptyFrame, NULL);
}
CuSuite* CuSuiteNewWithFrame(const CuTestFrame *frame, void *frameContext) {
CuSuite* testSuite = CU_ALLOC(CuSuite); CuSuite* testSuite = CU_ALLOC(CuSuite);
CuSuiteInit(testSuite); CuSuiteInitWithFrame(testSuite, frame, frameContext);
return testSuite; return testSuite;
} }
@ -285,11 +319,20 @@ void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2)
void CuSuiteRun(CuSuite* testSuite) void CuSuiteRun(CuSuite* testSuite)
{ {
const CuTestFrame * const frame = testSuite->frame;
int i; int i;
for (i = 0 ; i < testSuite->count ; ++i) for (i = 0 ; i < testSuite->count ; ++i)
{ {
CuTest* testCase = testSuite->list[i]; CuTest* testCase = testSuite->list[i];
CuTestRun(testCase); testCase->context = testSuite->frameContext;
TestFunctionRun(testCase, frame->setup);
if (!testCase->failed) {
CuTestRun(testCase);
TestFunctionRun(testCase, frame->teardown);
}
testSuite->frameContext = testCase->context;
if (testCase->failed) { testSuite->failCount += 1; } if (testCase->failed) { testSuite->failCount += 1; }
} }
} }

View File

@ -48,12 +48,15 @@ struct CuTest
int ran; int ran;
CuString *message; CuString *message;
jmp_buf *jumpBuf; jmp_buf *jumpBuf;
void *context;
}; };
void CuTestInit(CuTest* t, const char* name, TestFunction function); void CuTestInit(CuTest* t, const char* name, TestFunction function);
CuTest* CuTestNew(const char* name, TestFunction function); CuTest* CuTestNew(const char* name, TestFunction function);
void CuTestRun(CuTest* tc); void CuTestRun(CuTest* tc);
void CuTestDelete(CuTest *t); void CuTestDelete(CuTest *t);
void *CuTestContextGet(CuTest *tc);
void CuTestContextSet(CuTest *tc, void *context);
/* Internal versions of assert functions -- use the public versions */ /* 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 CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message);
@ -95,17 +98,25 @@ void CuAssertPtrEquals_LineMsg(CuTest* tc,
#define SUITE_ADD_TEST(SUITE,TEST) CuSuiteAdd(SUITE, CuTestNew(#TEST, TEST)) #define SUITE_ADD_TEST(SUITE,TEST) CuSuiteAdd(SUITE, CuTestNew(#TEST, TEST))
typedef struct CuTestFrame {
void (*setup)(CuTest *tc);
void (*teardown)(CuTest *tc);
}CuTestFrame;
typedef struct typedef struct
{ {
int count; int count;
CuTest* list[MAX_TEST_CASES]; CuTest* list[MAX_TEST_CASES];
int failCount; int failCount;
const CuTestFrame *frame;
void *frameContext;
} CuSuite; } CuSuite;
void CuSuiteInit(CuSuite* testSuite); void CuSuiteInit(CuSuite* testSuite);
void CuSuiteInitWithFrame(CuSuite* testSuite, const CuTestFrame *frame, void *frameContext);
CuSuite* CuSuiteNew(void); CuSuite* CuSuiteNew(void);
CuSuite* CuSuiteNewWithFrame(const CuTestFrame *frame, void *frameContext);
void CuSuiteDelete(CuSuite *testSuite); void CuSuiteDelete(CuSuite *testSuite);
void CuSuiteAdd(CuSuite* testSuite, CuTest *testCase); void CuSuiteAdd(CuSuite* testSuite, CuTest *testCase);
void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2); void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2);

View File

@ -3,6 +3,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdbool.h>
#include "CuTest.h" #include "CuTest.h"
@ -707,3 +708,281 @@ CuSuite* CuGetSuite(void)
return suite; return suite;
} }
typedef enum TestPhase {
UNKNOWN = 0,
SETUP,
TEST,
TEARDOWN
}TestPhase;
#define TEST_PHASE_RECORDER_LEN 50
typedef struct TestPhaseRecorder {
TestPhase TestSequence[TEST_PHASE_RECORDER_LEN];
size_t pos;
}TestPhaseTracker;
static void TestPhaseRecord(TestPhaseTracker *tracker, TestPhase phase) {
if (tracker->pos < TEST_PHASE_RECORDER_LEN) {
tracker->TestSequence[tracker->pos++] = phase;
}
}
static void TestPhaseNoMoreEventsFrom(CuTest *tc, TestPhaseTracker *tracker, size_t pos) {
for (size_t i = pos;i < TEST_PHASE_RECORDER_LEN;i++) {
CuAssertIntEquals(tc, UNKNOWN, tracker->TestSequence[i]);
}
}
static struct TestMockData {
TestPhaseTracker tracker;
void *ContextPassed;
bool setupShallFail;
bool teardownShallFail;
bool testShallFail;
bool setupContinuedAfterAssert;
bool testContinuedAfterAssert;
bool teardownContinuedAfterAssert;
}TestMockData;
static void TestMockDataInit(void) {
memset(&TestMockData, 0, sizeof(TestMockData));
}
static void FrameMockSetup(CuTest *tc) {
bool fail = TestMockData.setupShallFail;
TestMockData.setupShallFail = false;
TestPhaseRecord(&TestMockData.tracker, SETUP);
CuAssert(tc, "Have to fail", !fail);
TestMockData.setupContinuedAfterAssert = true;
}
static void FrameMockTest(CuTest *tc) {
bool fail = TestMockData.testShallFail;
TestMockData.testShallFail = false;
TestPhaseRecord(&TestMockData.tracker, TEST);
TestMockData.ContextPassed = CuTestContextGet(tc);
CuAssert(tc, "Have to fail", !fail);
TestMockData.testContinuedAfterAssert = true;
}
static void FrameMockTearDown(CuTest *tc) {
bool fail = TestMockData.teardownShallFail;
TestMockData.teardownShallFail = false;
TestPhaseRecord(&TestMockData.tracker, TEARDOWN);
CuAssert(tc, "Have to fail", !fail);
TestMockData.teardownContinuedAfterAssert = true;
}
static const CuTestFrame FrameMock = {
.setup = FrameMockSetup,
.teardown = FrameMockTearDown,
};
static void TestSuiteWithFrameInit(CuTest *tc) {
CuSuite ts;
int context = 0;
CuSuiteInitWithFrame(&ts, &FrameMock, &context);
CuAssertPtrEquals(tc, (void *)&FrameMock, (void *)ts.frame);
CuAssertPtrEquals(tc, &context, ts.frameContext);
}
static void TestSuiteWithFrameNew(CuTest *tc) {
int context = 0;
CuSuite* ts = CuSuiteNewWithFrame(&FrameMock, &context);
CuAssertPtrEquals(tc, (void *)&FrameMock, (void *)ts->frame);
CuAssertPtrEquals(tc, &context, ts->frameContext);
}
static void TestSuiteRunsSetupTestTeardown(CuTest *tc) {
int context = 0;
TestMockDataInit();
CuSuite* uut = CuSuiteNewWithFrame(&FrameMock, &context);
SUITE_ADD_TEST(uut, FrameMockTest);
CuSuiteRun(uut);
TestPhaseTracker *tracker = &TestMockData.tracker;
CuAssertIntEquals(tc, SETUP, tracker->TestSequence[0]);
CuAssertIntEquals(tc, TEST, tracker->TestSequence[1]);
CuAssertIntEquals(tc, TEARDOWN, tracker->TestSequence[2]);
TestPhaseNoMoreEventsFrom(tc, tracker, 3);
}
static void TestSuiteTestFailsIfSetupFails(CuTest *tc) {
int context = 0;
TestMockDataInit();
CuSuite* uut = CuSuiteNewWithFrame(&FrameMock, &context);
SUITE_ADD_TEST(uut, FrameMockTest);
SUITE_ADD_TEST(uut, TestPasses);
TestMockData.setupShallFail = true;
CuSuiteRun(uut);
CuAssertIntEquals(tc, 2, uut->count);
CuAssertIntEquals(tc, 1, uut->failCount);
TestPhaseTracker *tracker = &TestMockData.tracker;
CuAssertIntEquals(tc, SETUP, tracker->TestSequence[0]);
CuAssertIntEquals(tc, SETUP, tracker->TestSequence[1]);
CuAssertIntEquals(tc, TEARDOWN, tracker->TestSequence[2]);
TestPhaseNoMoreEventsFrom(tc, tracker, 3);
}
static void TestSuiteTestFailsIfTeardownFails(CuTest *tc) {
int context = 0;
TestMockDataInit();
CuSuite* uut = CuSuiteNewWithFrame(&FrameMock, &context);
SUITE_ADD_TEST(uut, FrameMockTest);
TestMockData.teardownShallFail = true;
CuSuiteRun(uut);
CuAssertIntEquals(tc, 1, uut->count);
CuAssertIntEquals(tc, 1, uut->failCount);
}
static void TestSuiteTeardownIsExecutedIfTestFails(CuTest *tc) {
int context = 0;
TestMockDataInit();
CuSuite* uut = CuSuiteNewWithFrame(&FrameMock, &context);
SUITE_ADD_TEST(uut, FrameMockTest);
TestMockData.testShallFail = true;
CuSuiteRun(uut);
CuAssertIntEquals(tc, 1, uut->count);
CuAssertIntEquals(tc, 1, uut->failCount);
TestPhaseTracker *tracker = &TestMockData.tracker;
CuAssertIntEquals(tc, SETUP, tracker->TestSequence[0]);
CuAssertIntEquals(tc, TEST, tracker->TestSequence[1]);
CuAssertIntEquals(tc, TEARDOWN, tracker->TestSequence[2]);
TestPhaseNoMoreEventsFrom(tc, tracker, 3);
}
static void TestSuiteContextPassedToCuTest(CuTest *tc) {
int context = 0;
TestMockDataInit();
CuSuite* uut = CuSuiteNewWithFrame(&FrameMock, &context);
SUITE_ADD_TEST(uut, FrameMockTest);
CuSuiteRun(uut);
CuAssertPtrEquals(tc, &context, TestMockData.ContextPassed);
}
static void TestSuiteSetupInterruptsUponFailedAssert(CuTest *tc) {
int context = 0;
TestMockDataInit();
CuSuite* uut = CuSuiteNewWithFrame(&FrameMock, &context);
TestMockData.setupShallFail = true;
SUITE_ADD_TEST(uut, FrameMockTest);
CuSuiteRun(uut);
CuAssert(tc, "Setup did continue", !TestMockData.setupContinuedAfterAssert);
}
static void TestSuiteTestInterruptsUponFailedAssert(CuTest *tc) {
int context = 0;
TestMockDataInit();
CuSuite* uut = CuSuiteNewWithFrame(&FrameMock, &context);
TestMockData.testShallFail = true;
SUITE_ADD_TEST(uut, FrameMockTest);
CuSuiteRun(uut);
CuAssert(tc, "Test did continue", !TestMockData.testContinuedAfterAssert);
}
static void TestSuiteTeardownInterruptsUponFailedAssert(CuTest *tc) {
int context = 0;
TestMockDataInit();
CuSuite* uut = CuSuiteNewWithFrame(&FrameMock, &context);
TestMockData.teardownShallFail = true;
SUITE_ADD_TEST(uut, FrameMockTest);
CuSuiteRun(uut);
CuAssert(tc, "Teardown did continue", !TestMockData.teardownContinuedAfterAssert);
}
static void *TheSetupContext;
static void *TheTestContext;
static void *TheTeardownContext;
static void FrameContextSetup(CuTest *tc) {
TheSetupContext = CuTestContextGet(tc);
CuTestContextSet(tc, &TheSetupContext);
}
static void FrameContextTest(CuTest *tc) {
TheTestContext = CuTestContextGet(tc);
CuTestContextSet(tc, &TheTestContext);
}
static void FrameContextTearDown(CuTest *tc) {
TheTeardownContext = CuTestContextGet(tc);
CuTestContextSet(tc, &TheTeardownContext);
}
static const CuTestFrame FrameContextMock = {
.setup = FrameContextSetup,
.teardown = FrameContextTearDown,
};
static void TestSuiteSetupTestTeardownWithContext(CuTest *tc) {
int context = 0;
TheSetupContext = NULL;
TheTestContext = NULL;
TheTeardownContext = NULL;
CuSuite* uut = CuSuiteNewWithFrame(&FrameContextMock, &context);
SUITE_ADD_TEST(uut, FrameContextTest);
CuSuiteRun(uut);
CuAssertPtrEquals(tc, &context, TheSetupContext);
CuAssertPtrEquals(tc, &TheSetupContext, TheTestContext);
CuAssertPtrEquals(tc, &TheTestContext, TheTeardownContext);
CuAssertPtrEquals(tc, &TheTeardownContext, uut->frameContext);
}
CuSuite* CuSuiteFrameGetSuite(void) {
CuSuite* suite = CuSuiteNew();
SUITE_ADD_TEST(suite, TestSuiteWithFrameInit);
SUITE_ADD_TEST(suite, TestSuiteWithFrameNew);
SUITE_ADD_TEST(suite, TestSuiteRunsSetupTestTeardown);
SUITE_ADD_TEST(suite, TestSuiteTestFailsIfSetupFails);
SUITE_ADD_TEST(suite, TestSuiteTestFailsIfTeardownFails);
SUITE_ADD_TEST(suite, TestSuiteTeardownIsExecutedIfTestFails);
SUITE_ADD_TEST(suite, TestSuiteContextPassedToCuTest);
SUITE_ADD_TEST(suite, TestSuiteSetupInterruptsUponFailedAssert);
SUITE_ADD_TEST(suite, TestSuiteTestInterruptsUponFailedAssert);
SUITE_ADD_TEST(suite, TestSuiteTeardownInterruptsUponFailedAssert);
SUITE_ADD_TEST(suite, TestSuiteSetupTestTeardownWithContext);
return suite;
}