Added timers to the testing framework, test timing (so no more of those pesky TODOs), and the OS X implementation of test timing.
This commit is contained in:
parent
23591eeefa
commit
bd1ca240e3
|
@ -6,6 +6,10 @@ libui_test_sources = [
|
|||
'testing.c',
|
||||
]
|
||||
|
||||
if libui_OS == 'darwin'
|
||||
libui_test_sources += ['testing_darwin.c']
|
||||
endif
|
||||
|
||||
if libui_OS == 'windows'
|
||||
libui_test_manifest = 'test.manifest'
|
||||
if libui_mode == 'static'
|
||||
|
|
138
test/testing.c
138
test/testing.c
|
@ -128,12 +128,17 @@ static void testsetRun(struct testset *set, int *anyFailed)
|
|||
size_t i;
|
||||
testingT *t;
|
||||
const char *status;
|
||||
testingTimer *timer;
|
||||
char *timerstr;
|
||||
|
||||
t = set->tests;
|
||||
timer = testingNewTimer();
|
||||
for (i = 0; i < set->len; i++) {
|
||||
printf("=== RUN %s\n", t->name);
|
||||
testingTimerStart(timer);
|
||||
if (setjmp(t->returnNowBuf) == 0)
|
||||
(*(t->f))(t);
|
||||
testingTimerEnd(timer);
|
||||
t->returned = 1;
|
||||
runDefers(t);
|
||||
status = "PASS";
|
||||
|
@ -143,9 +148,12 @@ static void testsetRun(struct testset *set, int *anyFailed)
|
|||
} else if (t->skipped)
|
||||
// note that failed overrides skipped
|
||||
status = "SKIP";
|
||||
printf("--- %s: %s (%s)\n", status, t->name, "TODO");
|
||||
timerstr = testingTimerString(timer);
|
||||
printf("--- %s: %s (%s)\n", status, t->name, timerstr);
|
||||
testingFreeTimerString(timerstr);
|
||||
t++;
|
||||
}
|
||||
testingFreeTimer(timer);
|
||||
}
|
||||
|
||||
int testingMain(void)
|
||||
|
@ -236,3 +244,131 @@ void testingTDefer(testingT *t, void (*f)(testingT *t, void *data), void *data)
|
|||
d->next = t->defers;
|
||||
t->defers = d;
|
||||
}
|
||||
|
||||
// This is based on the algorithm that Go uses for time.Duration.
|
||||
// Of course, we're not expressing it the same way...
|
||||
struct timerStringPart {
|
||||
char suffix;
|
||||
char suffix2;
|
||||
int mode;
|
||||
uint32_t maxOrMod;
|
||||
int precision;
|
||||
};
|
||||
|
||||
enum {
|
||||
modeMaxAndStop,
|
||||
modeFracModContinue,
|
||||
};
|
||||
|
||||
static const struct timerStringPart parts[] = {
|
||||
{ 'n', 's', modeMaxAndStop, 1000, 0 },
|
||||
{ 'u', 's', modeMaxAndStop, 1000000, 3 },
|
||||
{ 'm', 's', modeMaxAndStop, 1000000000, 6 },
|
||||
{ 's', 0, modeFracModContinue, 60, 9 },
|
||||
{ 'm', 0, modeFracModContinue, 60, 0 },
|
||||
{ 'h', 0, modeFracModContinue, 60, 0 },
|
||||
{ 0, 0, 0, 0, 0 },
|
||||
};
|
||||
|
||||
static void fillFracPart(char *s, int precision, int *start, uint64_t *unsec)
|
||||
{
|
||||
int i;
|
||||
int print;
|
||||
uint64_t digit;
|
||||
|
||||
print = 0;
|
||||
for (i = 0; i < precision; i++) {
|
||||
digit = *unsec % 10;
|
||||
print = print || (digit == 0);
|
||||
if (print) {
|
||||
s[*start - 1] = "0123456789"[digit];
|
||||
(*start)--;
|
||||
}
|
||||
*unsec /= 10;
|
||||
}
|
||||
if (print) {
|
||||
s[*start - 1] = '.';
|
||||
(*start)--;
|
||||
}
|
||||
}
|
||||
|
||||
static void fillIntPart(char *s, int *start, uint64_t unsec)
|
||||
{
|
||||
if (unsec == 0) {
|
||||
s[*start - 1] = '0';
|
||||
(*start)--;
|
||||
return;
|
||||
}
|
||||
while (unsec != 0) {
|
||||
s[*start - 1] = "0123456789"[unsec % 10];
|
||||
(*start)--;
|
||||
unsec /= 10;
|
||||
}
|
||||
}
|
||||
|
||||
char *testingTimerString(testingTimer *t)
|
||||
{
|
||||
int64_t nsec;
|
||||
uint64_t unsec;
|
||||
int neg;
|
||||
char *s;
|
||||
int start;
|
||||
const struct timerStringPart *p;
|
||||
|
||||
// The Go algorithm says 32 should be enough.
|
||||
s = (char *) malloc(33 * sizeof (char));
|
||||
// TODO handle failure
|
||||
memset(s, 0, 33 * sizeof (char));
|
||||
start = 32;
|
||||
|
||||
nsec = testingTimerNsec(t);
|
||||
if (nsec == 0) {
|
||||
s[0] = '0';
|
||||
s[1] = 's';
|
||||
return s;
|
||||
}
|
||||
unsec = (uint64_t) nsec;
|
||||
neg = 0;
|
||||
if (nsec < 0) {
|
||||
unsec = -unsec;
|
||||
neg = 1;
|
||||
}
|
||||
|
||||
for (p = parts; p->suffix != 0; p++) {
|
||||
if (p->mode == modeMaxAndStop && unsec < p->maxOrMod) {
|
||||
if (p->suffix2 != 0) {
|
||||
s[start - 1] = p->suffix2;
|
||||
start--;
|
||||
}
|
||||
s[start - 1] = p->suffix;
|
||||
start--;
|
||||
fillFracPart(s, p->precision, &start, &unsec);
|
||||
fillIntPart(s, &start, unsec);
|
||||
break;
|
||||
}
|
||||
if (p->mode == modeFracModContinue && unsec != 0) {
|
||||
if (p->suffix2 != 0) {
|
||||
s[start - 1] = p->suffix2;
|
||||
start--;
|
||||
}
|
||||
s[start - 1] = p->suffix;
|
||||
start--;
|
||||
fillFracPart(s, p->precision, &start, &unsec);
|
||||
fillIntPart(s, &start, unsec % p->maxOrMod);
|
||||
unsec /= p->maxOrMod;
|
||||
// and move on to the next one
|
||||
}
|
||||
}
|
||||
|
||||
if (neg) {
|
||||
s[start - 1] = '-';
|
||||
start--;
|
||||
}
|
||||
memmove(s, s + start, 33 - start);
|
||||
return s;
|
||||
}
|
||||
|
||||
void testingFreeTimerString(char *s)
|
||||
{
|
||||
free(s);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// 27 february 2018
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define testingprivImplName(basename) testingprivImpl ## basename
|
||||
|
||||
|
@ -63,6 +64,18 @@ extern void testingTFailNow(testingT *t);
|
|||
extern void testingTSkipNow(testingT *t);
|
||||
extern void testingTDefer(testingT *t, void (*f)(testingT *t, void *data), void *data);
|
||||
|
||||
typedef struct testingTimer testingTimer;
|
||||
|
||||
#define testingTimerNsecPerSec ((int64_t) 1000000000)
|
||||
|
||||
extern testingTimer *testingNewTimer(void);
|
||||
extern void testingFreeTimer(testingTimer *t);
|
||||
extern void testingTimerStart(testingTimer *t);
|
||||
extern void testingTimerEnd(testingTimer *t);
|
||||
extern int64_t testingTimerNsec(testingTimer *t);
|
||||
extern char *testingTimerString(testingTimer *t);
|
||||
extern void testingFreeTimerString(char *s);
|
||||
|
||||
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);
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
// 22 april 2019
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
#include "testing.h"
|
||||
|
||||
struct testingTimer {
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
};
|
||||
|
||||
testingTimer *testingNewTimer(void)
|
||||
{
|
||||
testingTimer *t;
|
||||
|
||||
t = (testingTimer *) malloc(sizeof (testingTimer));
|
||||
// TODO handle failure
|
||||
memset(t, 0, sizeof (testingTimer));
|
||||
return t;
|
||||
}
|
||||
|
||||
void testingFreeTimer(testingTimer *t)
|
||||
{
|
||||
free(t);
|
||||
}
|
||||
|
||||
void testingTimerStart(testingTimer *t)
|
||||
{
|
||||
t->start = mach_absolute_time();
|
||||
}
|
||||
|
||||
void testingTimerEnd(testingTimer *t)
|
||||
{
|
||||
t->end = mach_absolute_time();
|
||||
}
|
||||
|
||||
int64_t testingTimerNsec(testingTimer *t)
|
||||
{
|
||||
mach_timebase_info_data_t mt;
|
||||
uint64_t c;
|
||||
|
||||
mach_timebase_info(&mt);
|
||||
c = t->end - t->start;
|
||||
c = c * mt.numer / mt.denom;
|
||||
return (int64_t) c;
|
||||
}
|
Loading…
Reference in New Issue