And added the multithreaded uiQueueMain() tests.

This commit is contained in:
Pietro Gagliardi 2019-04-28 20:45:53 -04:00
parent 6c41fb712e
commit df8eadb980
3 changed files with 126 additions and 18 deletions

View File

@ -63,7 +63,7 @@ testingTest(QueueMain)
uiQueueMain(queued, &flag);
timeout_uiMain(t, 5 * testingNsecPerSec, 0);
if (flag != 1)
testingTErrorf(t, "uiQueueMain didn't set flag properly: got %d, want 1", flag);
testingTErrorf(t, "uiQueueMain() didn't set flag properly: got %d, want 1", flag);
}
#define queueOp(name, expr) \
@ -93,30 +93,77 @@ static const struct {
{ 16, "mul8 -> div3 -> sub2" },
};
static void queueOrder(uint32_t *flag)
{
*flag = 7;
uiQueueMain(sub2, flag);
uiQueueMain(div3, flag);
uiQueueMain(mul8, flag);
uiQueueMain(done, NULL);
}
// TODO somehow funnel the caller's file/line through
static void checkOrder(testingT *t, uint32_t flag)
{
int i;
if (flag == orders[0].result)
return;
for (i = 1; i < 6; i++)
if (flag == orders[i].result) {
testingTErrorf(t, "got %" PRIu32 " (%s), want %" PRIu32 " (%s)", flag, orders[i].order, orders[0].result, orders[0].order);
return;
}
testingTErrorf(t, "got %" PRIu32 " (unknown order), want %" PRIu32 " (%s)", flag, orders[0].result, orders[0].order);
}
testingTest(QueueMain_Sequence)
{
uint32_t flag;
int i;
flag = 7;
uiQueueMain(sub2, &flag);
uiQueueMain(div3, &flag);
uiQueueMain(mul8, &flag);
uiQueueMain(done, NULL);
queueOrder(&flag);
timeout_uiMain(t, 5 * testingNsecPerSec, 0);
if (flag != orders[0].result) {
for (i = 1; i < 6; i++)
if (flag == orders[i].result) {
testingTErrorf(t, "got %" PRIu32 " (%s), want %" PRIu32 " (%s)", flag, orders[i].order, orders[0].result, orders[0].order);
break;
}
if (i >= 6)
testingTErrorf(t, "got %" PRIu32 " (unknown order), want %" PRIu32 " (%s)", flag, orders[0].result, orders[0].order);
}
checkOrder(t, flag);
}
// TODO testingTest(QueueMain_DifferentThread)
// TODO testingTest(QueueMain_DifferentThreadSequence)
static void queueThread(void *data)
{
int *flag = (int *) data;
testingSleep(1250 * testingNsecPerMsec);
uiQueueMain(queued, flag);
}
testingTest(QueueMain_DifferentThread)
{
testingThread *thread;
int flag = 0;
thread = testingNewThread(queueThread, &flag);
timeout_uiMain(t, 5 * testingNsecPerSec, 0);
testingThreadWaitAndFree(thread);
if (flag != 1)
testingTErrorf(t, "uiQueueMain() didn't set flag properly: got %d, want 1", flag);
}
static void queueOrderThread(void *data)
{
uint32_t *flag = (uint32_t *) data;
testingSleep(1250 * testingNsecPerMsec);
queueOrder(flag);
}
testingTest(QueueMain_DifferentThreadSequence)
{
testingThread *thread;
uint32_t flag;
thread = testingNewThread(queueOrderThread, &flag);
timeout_uiMain(t, 5 * testingNsecPerSec, 0);
testingThreadWaitAndFree(thread);
checkOrder(t, flag);
}
#if 0
static void timer(void *data)

View File

@ -67,6 +67,7 @@ extern void testingTDefer(testingT *t, void (*f)(testingT *t, void *data), void
typedef struct testingTimer testingTimer;
#define testingNsecPerUsec ((int64_t) 1000)
#define testingNsecPerMsec ((int64_t) 1000000)
#define testingNsecPerSec ((int64_t) 1000000000)
extern testingTimer *testingNewTimer(void);
@ -81,6 +82,13 @@ extern void testingFreeNsecString(char *s);
#define testingRunWithTimeout(t, timeout, f, data, comment, failNowOnError) \
testingprivRunWithTimeout(t, __FILE__, __LINE__, timeout, f, data, comment, failNowOnError)
extern void testingSleep(int64_t nsec);
// TODO I don't like this threading model, but let's use it for now so I can continue working
typedef struct testingThread testingThread;
extern testingThread *testingNewThread(void (*f)(void *data), void *data);
extern void testingThreadWaitAndFree(testingThread *t);
extern void testingprivRegisterTest(const char *, void (*)(testingT *), const char *, long);
extern void testingprivRegisterTestBefore(const char *, void (*)(testingT *), const char *, long);
extern void testingprivRegisterTestAfter(const char *, void (*)(testingT *), const char *, long);

View File

@ -259,3 +259,56 @@ out:
if (failNowOnError)
testingTFailNow(t);
}
void testingSleep(int64_t nsec)
{
HANDLE timer;
LARGE_INTEGER duration;
// TODO check errors, possibly falling back to Sleep() (although it has lower resolution)
// TODO rename all the other durations that are timeout or timer to duration or nsec, both here and in the Unix/Darwin code
duration.QuadPart = nsec / 100;
duration.QuadPart = -duration.QuadPart;
timer = CreateWaitableTimerW(NULL, TRUE, NULL);
SetWaitableTimer(timer, &duration, 0, NULL, NULL, FALSE);
WaitForSingleObject(timer, INFINITE);
CloseHandle(timer);
}
struct testingThread {
uintptr_t handle;
void (*f)(void *data);
void *data;
};
static unsigned __stdcall threadThreadProc(void *data)
{
testingThread *t = (testingThread *) data;
(*(t->f))(t->data);
return 0;
}
testingThread *testingNewThread(void (*f)(void *data), void *data)
{
testingThread *t;
t = malloc(sizeof (testingThread));
// TODO check error
ZeroMemory(t, sizeof (testingThread));
t->f = f;
t->data = data;
t->handle = _beginthreadex(NULL, 0, threadThreadProc, t, 0, NULL);
// TODO check error
return t;
}
void testingThreadWaitAndFree(testingThread *t)
{
// TODO check errors
WaitForSingleObject((HANDLE) (t->handle), INFINITE);
// TODO end check errors
CloseHandle((HANDLE) (t->handle));
free(t);
}