Test: Allow logging to mark messages as repeating

This commit is contained in:
Vicki Pfau 2020-07-21 23:49:26 -07:00
parent f1d90e5f72
commit 18ea9502cd
1 changed files with 102 additions and 62 deletions

View File

@ -94,8 +94,15 @@ DECLARE_VECTOR(ImageList, void*)
DEFINE_VECTOR(ImageList, void*)
struct StringBuilder {
struct StringList out;
struct StringList err;
struct StringList lines;
struct StringList partial;
unsigned repeat;
};
struct CInemaLogStream {
struct StringBuilder err;
struct StringBuilder out;
};
static bool showVersion = false;
@ -115,7 +122,7 @@ static size_t jobIndex = 0;
static Mutex jobMutex;
static Thread jobThreads[MAX_JOBS];
static int jobStatus;
static ThreadLocal stringBuilder;
static ThreadLocal logStream;
static ThreadLocal currentTest;
bool CInemaTestInit(struct CInemaTest*, const char* directory, const char* filename);
@ -126,53 +133,9 @@ void CInemaConfigLoad(struct Table* configTree, const char* testName, struct mCo
static void _log(struct mLogger* log, int category, enum mLogLevel level, const char* format, va_list args);
void CIflush(struct StringList* list, FILE* out);
void CIflush(struct StringBuilder* list, FILE* file);
ATTRIBUTE_FORMAT(printf, 2, 3) void CIlog(int minlevel, const char* format, ...) {
if (verbosity < minlevel) {
return;
}
va_list args;
va_start(args, format);
#ifdef HAVE_VASPRINTF
struct StringBuilder* builder = ThreadLocalGetValue(stringBuilder);
if (!builder) {
vprintf(format, args);
} else {
if (StringListSize(&builder->out) > LOG_THRESHOLD) {
CIflush(&builder->out, stdout);
}
vasprintf(StringListAppend(&builder->out), format, args);
}
#else
vprintf(format, args);
#endif
va_end(args);
}
ATTRIBUTE_FORMAT(printf, 2, 3) void CIerr(int minlevel, const char* format, ...) {
if (verbosity < minlevel) {
return;
}
va_list args;
va_start(args, format);
#ifdef HAVE_VASPRINTF
struct StringBuilder* builder = ThreadLocalGetValue(stringBuilder);
if (!builder) {
vfprintf(stderr, format, args);
} else {
if (StringListSize(&builder->err) > LOG_THRESHOLD) {
CIflush(&builder->err, stderr);
}
vasprintf(StringListAppend(&builder->err), format, args);
}
#else
vfprintf(stderr, format, args);
#endif
va_end(args);
}
void CIflush(struct StringList* list, FILE* out) {
static char* _compileStringList(struct StringList* list) {
size_t len = 0;
size_t i;
for (i = 0; i < StringListSize(list); ++i) {
@ -187,9 +150,80 @@ void CIflush(struct StringList* list, FILE* out) {
free(brick);
cur += portion;
}
StringListClear(list);
return string;
}
static void _logToStream(FILE* file, const char* format, va_list args) {
#ifdef HAVE_VASPRINTF
struct CInemaLogStream* stream = ThreadLocalGetValue(logStream);
if (!stream) {
vfprintf(file, format, args);
} else {
struct StringBuilder* builder = &stream->out;
if (file == stderr) {
builder = &stream->err;
}
if (StringListSize(&builder->lines) > LOG_THRESHOLD) {
CIflush(builder, file);
}
char** line = StringListAppend(&builder->partial);
vasprintf(line, format, args);
size_t len = strlen(*line);
if (len && (*line)[len - 1] == '\n') {
char* string = _compileStringList(&builder->partial);
size_t linecount = StringListSize(&builder->lines);
if (linecount && strcmp(string, *StringListGetPointer(&builder->lines, linecount - 1)) == 0) {
++builder->repeat;
free(string);
} else {
if (builder->repeat > 1) {
asprintf(StringListAppend(&builder->lines), "The previous message was repeated %u times.\n", builder->repeat);
}
*StringListAppend(&builder->lines) = string;
builder->repeat = 1;
}
}
}
#else
vfprintf(file, format, args);
#endif
}
ATTRIBUTE_FORMAT(printf, 2, 3) void CIlog(int minlevel, const char* format, ...) {
if (verbosity < minlevel) {
return;
}
va_list args;
va_start(args, format);
_logToStream(stdout, format, args);
va_end(args);
}
ATTRIBUTE_FORMAT(printf, 2, 3) void CIerr(int minlevel, const char* format, ...) {
if (verbosity < minlevel) {
return;
}
va_list args;
va_start(args, format);
_logToStream(stderr, format, args);
va_end(args);
}
void CIflush(struct StringBuilder* builder, FILE* out) {
if (StringListSize(&builder->partial)) {
*StringListAppend(&builder->lines) = _compileStringList(&builder->partial);
}
#ifdef HAVE_VASPRINTF
if (builder->repeat > 1) {
asprintf(StringListAppend(&builder->lines), "The previous message was repeated %u times.\n", builder->repeat);
}
#endif
char* string = _compileStringList(&builder->lines);
builder->repeat = 0;
fputs(string, out);
free(string);
StringListClear(list);
}
static bool parseCInemaArgs(int argc, char* const* argv) {
@ -989,10 +1023,14 @@ static bool CInemaTask(struct CInemaTestList* tests, size_t i) {
static THREAD_ENTRY CInemaJob(void* context) {
struct CInemaTestList* tests = context;
struct StringBuilder builder;
StringListInit(&builder.out, 0);
StringListInit(&builder.err, 0);
ThreadLocalSetKey(stringBuilder, &builder);
struct CInemaLogStream stream;
StringListInit(&stream.out.lines, 0);
StringListInit(&stream.out.partial, 0);
stream.out.repeat = 0;
StringListInit(&stream.err.lines, 0);
StringListInit(&stream.err.partial, 0);
stream.err.repeat = 0;
ThreadLocalSetKey(logStream, &stream);
bool success = true;
while (true) {
@ -1007,8 +1045,8 @@ static THREAD_ENTRY CInemaJob(void* context) {
if (!CInemaTask(tests, i)) {
success = false;
}
CIflush(&builder.out, stdout);
CIflush(&builder.err, stderr);
CIflush(&stream.out, stdout);
CIflush(&stream.err, stderr);
}
MutexLock(&jobMutex);
if (!success) {
@ -1016,11 +1054,13 @@ static THREAD_ENTRY CInemaJob(void* context) {
}
MutexUnlock(&jobMutex);
CIflush(&builder.out, stdout);
StringListDeinit(&builder.out);
CIflush(&stream.out, stdout);
StringListDeinit(&stream.out.lines);
StringListDeinit(&stream.out.partial);
CIflush(&builder.err, stderr);
StringListDeinit(&builder.err);
CIflush(&stream.err, stderr);
StringListDeinit(&stream.err.lines);
StringListDeinit(&stream.err.partial);
}
void _log(struct mLogger* log, int category, enum mLogLevel level, const char* format, va_list args) {
@ -1055,8 +1095,8 @@ void _log(struct mLogger* log, int category, enum mLogLevel level, const char* f
}
int main(int argc, char** argv) {
ThreadLocalInitKey(&stringBuilder);
ThreadLocalSetKey(stringBuilder, NULL);
ThreadLocalInitKey(&logStream);
ThreadLocalSetKey(logStream, NULL);
int status = 0;
if (!parseCInemaArgs(argc, argv)) {