Converted testing.c to buffer test output. Also reordered test logs to be after the status line, like in Go. Now we can add verbosity control, and then maybe later even subtests!

This commit is contained in:
Pietro Gagliardi 2019-05-05 14:26:38 -04:00
parent 8b70e6d247
commit b7f06074fb
1 changed files with 120 additions and 72 deletions

View File

@ -36,39 +36,142 @@ static void *mustmalloc(size_t n, const char *what)
#define new(T) ((T *) mustmalloc(sizeof (T), #T)) #define new(T) ((T *) mustmalloc(sizeof (T), #T))
#define newArray(T, n) ((T *) mustmalloc(n * sizeof (T), #T "[" #n "]")) #define newArray(T, n) ((T *) mustmalloc(n * sizeof (T), #T "[" #n "]"))
static void *mustrealloc(void *x, size_t n, const char *what) static void *mustrealloc(void *x, size_t prevN, size_t n, const char *what)
{ {
void *y; void *y;
y = realloc(x, n); // don't use realloc() because we want to clear the new memory
y = malloc(n);
if (y == NULL) if (y == NULL)
internalError("memory exhausted reallocating %s", what); internalError("memory exhausted reallocating %s", what);
memset(y, 0, n);
memmove(y, x, prevN);
free(x);
return y; return y;
} }
#define resizeArray(x, T, n) ((T *) mustrealloc(x, n * sizeof (T), #T "[" #n "]")) #define resizeArray(x, T, prevN, n) ((T *) mustrealloc(x, prevN * sizeof (T), n * sizeof (T), #T "[" #n "]"))
static int mustvsnprintf(char *s, size_t n, const char *fmt, va_list ap) static int mustvsnprintf(char *s, size_t n, const char *format, va_list ap)
{ {
int ret; int ret;
ret = vsnprintf(s, n, fmt, ap); ret = vsnprintf(s, n, format, ap);
if (ret < 0) if (ret < 0)
internalError("encoding error in vsnprintf(); this likely means your call to testingTLogf() and the like is invalid"); internalError("encoding error in vsnprintf(); this likely means your call to testingTLogf() and the like is invalid");
return ret; return ret;
} }
static int mustsnprintf(char *s, size_t n, const char *fmt, ...) static int mustsnprintf(char *s, size_t n, const char *format, ...)
{ {
va_list ap; va_list ap;
int ret; int ret;
va_start(ap, fmt); va_start(ap, format);
ret = mustvsnprintf(s, n, fmt, ap); ret = mustvsnprintf(s, n, format, ap);
va_end(ap); va_end(ap);
return ret; return ret;
} }
// a struct outbuf of NULL writes directly to stdout
struct outbuf {
char *buf;
size_t len;
size_t cap;
};
#define nOutbufGrow 32
static void outbufCopyStr(struct outbuf *o, const char *str, size_t len)
{
size_t grow;
if (len == 0)
return;
if (o == NULL) {
printf("%s", str);
return;
}
grow = len + 1;
if (grow < nOutbufGrow)
grow = nOutbufGrow;
if ((o->len + grow) >= o->cap) {
size_t prevcap;
prevcap = o->cap;
o->cap += grow;
o->buf = resizeArray(o->buf, char, prevcap, o->cap);
}
memmove(o->buf + o->len, str, len * sizeof (char));
o->len += len;
}
#define outbufAddNewline(o) outbufCopyStr(o, "\n", 1)
static void outbufAddIndent(struct outbuf *o, int n)
{
for (; n != 0; n--)
outbufCopyStr(o, " ", 4);
}
static void outbufVprintf(struct outbuf *o, int indent, const char *format, va_list ap)
{
va_list ap2;
char *buf;
int n;
char *lineStart, *lineEnd;
int firstLine = 1;
va_copy(ap2, ap);
n = mustvsnprintf(NULL, 0, format, ap2);
// TODO handle n < 0 case
va_end(ap2);
buf = newArray(char, n + 1);
mustvsnprintf(buf, n + 1, format, ap);
// strip trailing blank lines
while (buf[n - 1] == '\n')
n--;
buf[n] = '\0';
lineStart = buf;
for (;;) {
lineEnd = strchr(lineStart, '\n');
if (lineEnd == NULL) // last line
break;
*lineEnd = '\0';
outbufAddIndent(o, indent);
outbufCopyStr(o, lineStart, lineEnd - lineStart);
outbufAddNewline(o);
lineStart = lineEnd + 1;
if (firstLine) {
// subsequent lines are indented twice
indent++;
firstLine = 0;
}
}
// print the last line
outbufAddIndent(o, indent);
outbufCopyStr(o, lineStart, strlen(lineStart));
outbufAddNewline(o);
free(buf);
}
static void outbufPrintf(struct outbuf *o, int indent, const char *format, ...)
{
va_list ap;
va_start(ap, format);
outbufVprintf(o, indent, format, ap);
va_end(ap);
}
static void outbufCopy(struct outbuf *o, const struct outbuf *from)
{
outbufCopyStr(o, from->buf, from->len);
}
struct defer { struct defer {
void (*f)(testingT *, void *); void (*f)(testingT *, void *);
void *data; void *data;
@ -102,9 +205,7 @@ struct testingT {
// output // output
int indent; int indent;
int verbose; int verbose;
char *output; struct outbuf output;
size_t outputLen;
size_t outputCap;
}; };
#ifdef _MSC_VER #ifdef _MSC_VER
@ -135,8 +236,11 @@ static struct testset testsAfter = { NULL, 0, 0 };
static void testsetAdd(struct testset *set, const char *name, void (*f)(testingT *), const char *file, long line) static void testsetAdd(struct testset *set, const char *name, void (*f)(testingT *), const char *file, long line)
{ {
if (set->len == set->cap) { if (set->len == set->cap) {
size_t prevcap;
prevcap = set->cap;
set->cap += nGrow; set->cap += nGrow;
set->tests = resizeArray(set->tests, testingT, set->cap); set->tests = resizeArray(set->tests, testingT, prevcap, set->cap);
} }
initTest(set->tests + set->len, name, f, file, line); initTest(set->tests + set->len, name, f, file, line);
set->len++; set->len++;
@ -178,63 +282,6 @@ static void testsetSort(struct testset *set)
qsort(set->tests, set->len, sizeof (testingT), testcmp); qsort(set->tests, set->len, sizeof (testingT), testcmp);
} }
static void printIndent(int n)
{
for (; n != 0; n--)
printf(" ");
}
static void vprintfIndented(int indent, const char *format, va_list ap)
{
va_list ap2;
char *buf;
int n;
char *lineStart, *lineEnd;
int firstLine = 1;
va_copy(ap2, ap);
n = mustvsnprintf(NULL, 0, format, ap2);
// TODO handle n < 0 case
va_end(ap2);
buf = newArray(char, n + 1);
mustvsnprintf(buf, n + 1, format, ap);
// strip trailing blank lines
while (buf[n - 1] == '\n')
n--;
buf[n] = '\0';
lineStart = buf;
for (;;) {
lineEnd = strchr(lineStart, '\n');
if (lineEnd == NULL) // last line
break;
*lineEnd = '\0';
printIndent(indent);
printf("%s\n", lineStart);
lineStart = lineEnd + 1;
if (firstLine) {
// subsequent lines are indented twice
indent++;
firstLine = 0;
}
}
// print the last line
printIndent(indent);
printf("%s\n", lineStart);
free(buf);
}
static void printfIndented(int indent, const char *format, ...)
{
va_list ap;
va_start(ap, format);
vprintfIndented(indent, format, ap);
va_end(ap);
}
static void runDefers(testingT *t) static void runDefers(testingT *t)
{ {
struct defer *d; struct defer *d;
@ -256,7 +303,7 @@ static void testsetRun(struct testset *set, int indent, int *anyFailed)
t = set->tests; t = set->tests;
for (i = 0; i < set->len; i++) { for (i = 0; i < set->len; i++) {
printfIndented(indent, "=== RUN %s\n", t->name); outbufPrintf(NULL, indent, "=== RUN %s", t->name);
t->indent = indent + 1; t->indent = indent + 1;
start = timerMonotonicNow(); start = timerMonotonicNow();
if (setjmp(t->returnNowBuf) == 0) if (setjmp(t->returnNowBuf) == 0)
@ -272,7 +319,8 @@ static void testsetRun(struct testset *set, int indent, int *anyFailed)
// note that failed overrides skipped // note that failed overrides skipped
status = "SKIP"; status = "SKIP";
timerDurationString(timerTimeSub(end, start), timerstr); timerDurationString(timerTimeSub(end, start), timerstr);
printfIndented(indent, "--- %s: %s (%s)\n", status, t->name, timerstr); outbufPrintf(NULL, indent, "--- %s: %s (%s)", status, t->name, timerstr);
outbufCopy(NULL, &(t->output));
t++; t++;
} }
} }
@ -333,7 +381,7 @@ void testingprivTLogvfFull(testingT *t, const char *file, long line, const char
buf = newArray(char, n + n2 + 1); buf = newArray(char, n + n2 + 1);
mustsnprintf(buf, n + 1, "%s:%ld: ", file, line); mustsnprintf(buf, n + 1, "%s:%ld: ", file, line);
mustvsnprintf(buf + n, n2 + 1, format, ap); mustvsnprintf(buf + n, n2 + 1, format, ap);
printfIndented(t->indent, "%s", buf); outbufPrintf(&(t->output), t->indent, "%s", buf);
free(buf); free(buf);
} }