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:
Pietro Gagliardi 2019-04-22 23:01:55 -04:00
parent 23591eeefa
commit bd1ca240e3
4 changed files with 201 additions and 1 deletions

View File

@ -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'

View File

@ -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);
}

View File

@ -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);

47
test/testing_darwin.c Normal file
View File

@ -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;
}