mirror of https://github.com/nirenjan/pinelog.git
Add test framework
parent
490a2e3faa
commit
10941defb2
22
Makefile.am
22
Makefile.am
|
@ -18,3 +18,25 @@ libpinelog_la_SOURCES = pinelog.c
|
|||
libpinelog_la_CFLAGS = @PINELOG_CFLAGS@ $(WARN_CFLAGS)
|
||||
libpinelog_la_LDFLAGS = $(WARN_LDFLAGS)
|
||||
|
||||
|
||||
test_SRCFILES = test_pinelog.c $(libpinelog_la_SOURCES)
|
||||
test_CFLAGS = \
|
||||
-DPINELOG_FATAL_STR='"F"' \
|
||||
-DPINELOG_ERROR_STR='"E"' \
|
||||
-DPINELOG_WARNING_STR='"W"' \
|
||||
-DPINELOG_INFO_STR='"I"' \
|
||||
-DPINELOG_DEBUG_STR='"D"' \
|
||||
-DPINELOG_TRACE_STR='"T"' \
|
||||
-DPINELOG_DEFAULT_LEVEL=PINELOG_LVL_TRACE \
|
||||
-DPINELOG_DEFAULT_STREAM=stderr \
|
||||
-DPINELOG_TEST
|
||||
|
||||
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/tap-driver.sh
|
||||
TESTS = \
|
||||
test_ts_lvl_tr
|
||||
|
||||
check_PROGRAMS = $(TESTS)
|
||||
test_ts_lvl_tr_SOURCES = $(test_SRCFILES)
|
||||
test_ts_lvl_tr_CFLAGS = $(WARN_CFLAGS) $(test_CFLAGS) \
|
||||
-DPINELOG_SHOW_DATE=1 -DPINELOG_SHOW_LEVEL=1 -DPINELOG_SHOW_BACKTRACE=1
|
||||
test_ts_lvl_tr_LDFLAGS = $(WARN_LDFLAGS)
|
||||
|
|
|
@ -10,6 +10,7 @@ AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
|
|||
AC_REQUIRE_AUX_FILE([tap-driver.sh])
|
||||
AC_PROG_CC
|
||||
AC_PROG_CC_STDC
|
||||
AC_PROG_AWK
|
||||
AM_PROG_AR
|
||||
LT_INIT
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
|
64
pinelog.c
64
pinelog.c
|
@ -43,28 +43,28 @@
|
|||
/**********************************************************************
|
||||
* Configure level strings
|
||||
*********************************************************************/
|
||||
#ifndef PINELOG_FATAL
|
||||
#define PINELOG_FATAL "FATAL"
|
||||
#ifndef PINELOG_FATAL_STR
|
||||
#define PINELOG_FATAL_STR "FATAL"
|
||||
#endif
|
||||
|
||||
#ifndef PINELOG_ERROR
|
||||
#define PINELOG_ERROR "ERROR"
|
||||
#ifndef PINELOG_ERROR_STR
|
||||
#define PINELOG_ERROR_STR "ERROR"
|
||||
#endif
|
||||
|
||||
#ifndef PINELOG_WARNING
|
||||
#define PINELOG_WARNING "WARNING"
|
||||
#ifndef PINELOG_WARNING_STR
|
||||
#define PINELOG_WARNING_STR "WARNING"
|
||||
#endif
|
||||
|
||||
#ifndef PINELOG_INFO
|
||||
#define PINELOG_INFO "INFO"
|
||||
#ifndef PINELOG_INFO_STR
|
||||
#define PINELOG_INFO_STR "INFO"
|
||||
#endif
|
||||
|
||||
#ifndef PINELOG_DEBUG
|
||||
#define PINELOG_DEBUG "DEBUG"
|
||||
#ifndef PINELOG_DEBUG_STR
|
||||
#define PINELOG_DEBUG_STR "DEBUG"
|
||||
#endif
|
||||
|
||||
#ifndef PINELOG_TRACE
|
||||
#define PINELOG_TRACE "TRACE"
|
||||
#ifndef PINELOG_TRACE_STR
|
||||
#define PINELOG_TRACE_STR "TRACE"
|
||||
#endif
|
||||
|
||||
/**********************************************************************
|
||||
|
@ -103,6 +103,13 @@ int pinelog_set_output_stream(FILE *stream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef PINELOG_TEST
|
||||
FILE * pinelog_get_output_stream(void)
|
||||
{
|
||||
return output_stream;
|
||||
}
|
||||
#endif
|
||||
|
||||
int pinelog_set_output_file(const char *file)
|
||||
{
|
||||
FILE *stream;
|
||||
|
@ -151,41 +158,46 @@ void pinelog_log_message(int level, const char *file, int line, const char *fmt,
|
|||
level = PINELOG_LVL_TRACE;
|
||||
}
|
||||
|
||||
/* Validate and set output stream */
|
||||
#if !HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR
|
||||
/*
|
||||
* Validate and set output stream. Only necessary if the compiler doesn't
|
||||
* support the constructor attribute
|
||||
*/
|
||||
if (output_stream == NULL) {
|
||||
output_stream = stdout;
|
||||
output_stream = PINELOG_DEFAULT_STREAM;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PINELOG_SHOW_DATE
|
||||
do {
|
||||
time_t t;
|
||||
struct tm *tmp;
|
||||
char date_string[20];
|
||||
char date_string[30];
|
||||
t = time(NULL);
|
||||
tmp = localtime(&t);
|
||||
strftime(date_string, sizeof(date_string), "%F %T ", tmp);
|
||||
fputs(date_string, out_stream);
|
||||
fputs(date_string, output_stream);
|
||||
} while (0);
|
||||
#endif
|
||||
|
||||
#if PINELOG_SHOW_LEVEL
|
||||
do {
|
||||
static const char *level_strings[] = {
|
||||
PINELOG_FATAL,
|
||||
PINELOG_ERROR,
|
||||
PINELOG_WARNING,
|
||||
PINELOG_INFO,
|
||||
PINELOG_DEBUG,
|
||||
PINELOG_TRACE,
|
||||
PINELOG_FATAL_STR,
|
||||
PINELOG_ERROR_STR,
|
||||
PINELOG_WARNING_STR,
|
||||
PINELOG_INFO_STR,
|
||||
PINELOG_DEBUG_STR,
|
||||
PINELOG_TRACE_STR,
|
||||
};
|
||||
|
||||
fputs(level_strings[level], out_stream);
|
||||
fputs(": ", out_stream);
|
||||
} while (0)
|
||||
fputs(level_strings[level], output_stream);
|
||||
fputs(": ", output_stream);
|
||||
} while (0);
|
||||
#endif
|
||||
|
||||
#if PINELOG_SHOW_BACKTRACE
|
||||
fprintf(out_stream, "%s:%d ", file, line);
|
||||
fprintf(output_stream, "%s:%d ", file, line);
|
||||
#endif
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
|
18
pinelog.h
18
pinelog.h
|
@ -20,7 +20,9 @@
|
|||
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#ifndef PINELOG_TEST
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -60,6 +62,15 @@ enum {
|
|||
*/
|
||||
void pinelog_set_defaults(void);
|
||||
|
||||
#ifdef PINELOG_TEST
|
||||
/**
|
||||
* @brief Get the pointer to the output stream. Only used in test harness.
|
||||
*
|
||||
* @returns FILE pointer to output stream
|
||||
*/
|
||||
FILE * pinelog_get_output_stream(void);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Set the output stream. Must be a FILE pointer.
|
||||
*
|
||||
|
@ -113,11 +124,16 @@ __attribute__((format(printf, 4, 5)))
|
|||
|
||||
void pinelog_log_message(int level, const char *file, int line, const char *fmt, ...);
|
||||
|
||||
// Test harness will redefine pinelog_exit
|
||||
#ifndef PINELOG_TEST
|
||||
#define pinelog_exit exit
|
||||
#endif
|
||||
|
||||
#define PINELOG_FATAL(fmt, ...) do { \
|
||||
if (PINELOG_LVL_FATAL <= pinelog_get_level()) { \
|
||||
pinelog_log_message(PINELOG_LVL_FATAL, __FILE__, __LINE__, fmt, ##__VA_ARGS__); \
|
||||
} \
|
||||
exit(1); \
|
||||
pinelog_exit(1); \
|
||||
} while (0)
|
||||
|
||||
#define PINELOG_ERROR(fmt, ...) do { \
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Pinelog lightweight logging library - test harness
|
||||
*
|
||||
* Copyright (C) 2021 Nirenjan Krishnan (nirenjan@nirenjan.org)
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "pinelog.h"
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/**********************************************************************
|
||||
* Global variables
|
||||
*********************************************************************/
|
||||
// Test ID (of current test case)
|
||||
static unsigned int test_id;
|
||||
|
||||
// Observed output stream
|
||||
static FILE *observed_stream_w;
|
||||
static FILE *observed_stream_r;
|
||||
|
||||
// Temporary pipe for observed data
|
||||
static char observed_fifo[NAME_MAX];
|
||||
|
||||
// Buffer for expected output
|
||||
static char expected_output[1024];
|
||||
static size_t expected_len;
|
||||
|
||||
static void test_case(const char *desc, bool test)
|
||||
{
|
||||
test_id++;
|
||||
if (test) {
|
||||
printf("ok %u %s\n", test_id, desc);
|
||||
} else {
|
||||
printf("not ok %u %s\n", test_id, desc);
|
||||
}
|
||||
}
|
||||
|
||||
static void pinelog_exit(int status)
|
||||
{
|
||||
fprintf(observed_stream_w, "EXIT(%d)\n", status);
|
||||
expected_len += snprintf(&expected_output[expected_len],
|
||||
sizeof(expected_output) - expected_len,
|
||||
"EXIT(%d)\n", status);
|
||||
}
|
||||
|
||||
static void dump_data(const char *type, size_t len, char *data)
|
||||
{
|
||||
char *line;
|
||||
printf("# %s (%lu bytes):\n", type, len);
|
||||
line = strtok(data, "\n");
|
||||
while (line != NULL) {
|
||||
printf("#\t%s\n", line);
|
||||
line = strtok(NULL, "\n");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void test_setup(int level, int filter, const char *file, int line, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
expected_len = 0;
|
||||
memset(expected_output, 0, sizeof(expected_output));
|
||||
|
||||
if (level <= filter) {
|
||||
if (PINELOG_SHOW_DATE) {
|
||||
time_t t;
|
||||
struct tm *tmp;
|
||||
|
||||
t = time(NULL);
|
||||
tmp = localtime(&t);
|
||||
expected_len += strftime(&expected_output[expected_len],
|
||||
sizeof(expected_output) - expected_len,
|
||||
"%F %T ", tmp);
|
||||
}
|
||||
|
||||
if (PINELOG_SHOW_LEVEL) {
|
||||
const char * level_string[] = {
|
||||
PINELOG_FATAL_STR,
|
||||
PINELOG_ERROR_STR,
|
||||
PINELOG_WARNING_STR,
|
||||
PINELOG_INFO_STR,
|
||||
PINELOG_DEBUG_STR,
|
||||
PINELOG_TRACE_STR,
|
||||
};
|
||||
expected_len += snprintf(&expected_output[expected_len],
|
||||
sizeof(expected_output) - expected_len,
|
||||
"%s: ", level_string[level]);
|
||||
}
|
||||
|
||||
if (PINELOG_SHOW_BACKTRACE) {
|
||||
expected_len += snprintf(&expected_output[expected_len],
|
||||
sizeof(expected_output) - expected_len,
|
||||
"%s:%d ", file, line);
|
||||
}
|
||||
|
||||
va_start(ap, fmt);
|
||||
expected_len += vsnprintf(&expected_output[expected_len],
|
||||
sizeof(expected_output) - expected_len,
|
||||
fmt, ap);
|
||||
va_end(ap);
|
||||
expected_output[expected_len] = '\n';
|
||||
expected_len++;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_teardown(const char *desc)
|
||||
{
|
||||
// Compare the output
|
||||
static char observed[1024];
|
||||
size_t observed_len;
|
||||
int result;
|
||||
|
||||
observed_len = fread(observed, 1, sizeof(observed), observed_stream_r);
|
||||
|
||||
result = ((expected_len == observed_len) &&
|
||||
(memcmp(expected_output, observed, expected_len) == 0));
|
||||
test_case(desc, result);
|
||||
if (!result) {
|
||||
dump_data("expected", expected_len, expected_output);
|
||||
dump_data("observed", observed_len, observed);
|
||||
}
|
||||
}
|
||||
|
||||
static void verify_defaults(void)
|
||||
{
|
||||
test_case("Get default output stream",
|
||||
pinelog_get_output_stream() == PINELOG_DEFAULT_STREAM);
|
||||
test_case("Get default logging level",
|
||||
pinelog_get_level() == PINELOG_DEFAULT_LEVEL);
|
||||
}
|
||||
|
||||
#define PINELOG_WARNING PINELOG_WARN
|
||||
|
||||
#define TEST_LOG(lvl, filter, fmt, ...) do { \
|
||||
test_setup(PINELOG_LVL_ ## lvl, PINELOG_LVL_ ## filter, \
|
||||
__FILE__, __LINE__, fmt, ##__VA_ARGS__); \
|
||||
PINELOG_ ## lvl (fmt, ##__VA_ARGS__); \
|
||||
test_teardown("Log " #lvl " filter " #filter); \
|
||||
} while(0)
|
||||
|
||||
#define TEST(filter, fmt, ...) do { \
|
||||
pinelog_set_level(PINELOG_LVL_ ## filter); \
|
||||
TEST_LOG(TRACE, filter, fmt, ##__VA_ARGS__); \
|
||||
TEST_LOG(DEBUG, filter, fmt, ##__VA_ARGS__); \
|
||||
TEST_LOG(INFO, filter, fmt, ##__VA_ARGS__); \
|
||||
TEST_LOG(WARNING, filter, fmt, ##__VA_ARGS__); \
|
||||
TEST_LOG(ERROR, filter, fmt, ##__VA_ARGS__); \
|
||||
TEST_LOG(FATAL, filter, fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int fifo_fd_r, fifo_fd_w;
|
||||
snprintf(observed_fifo, sizeof(observed_fifo), "%s.fifo", argv[0]);
|
||||
mkfifo(observed_fifo, 0777);
|
||||
|
||||
fifo_fd_r = open(observed_fifo, O_RDONLY | O_NONBLOCK);
|
||||
fifo_fd_w = open(observed_fifo, O_WRONLY | O_NONBLOCK);
|
||||
observed_stream_r = fdopen(fifo_fd_r, "r");
|
||||
observed_stream_w = fdopen(fifo_fd_w, "w");
|
||||
|
||||
verify_defaults();
|
||||
|
||||
pinelog_set_output_stream(observed_stream_w);
|
||||
TEST(TRACE, "testing %s... %d, %f, %u", "testing", -1, 0.0, 1);
|
||||
TEST(DEBUG, "testing %s... %d, %f, %u", "testing", -1, 0.0, 1);
|
||||
TEST(INFO, "testing %s... %d, %f, %u", "testing", -1, 0.0, 1);
|
||||
TEST(WARNING, "testing %s... %d, %f, %u", "testing", -1, 0.0, 1);
|
||||
TEST(ERROR, "testing %s... %d, %f, %u", "testing", -1, 0.0, 1);
|
||||
TEST(FATAL, "testing %s... %d, %f, %u", "testing", -1, 0.0, 1);
|
||||
TEST(NONE, "testing %s... %d, %f, %u", "testing", -1, 0.0, 1);
|
||||
|
||||
printf("1..%u\n", test_id);
|
||||
|
||||
fclose(observed_stream_w);
|
||||
fclose(observed_stream_r);
|
||||
close(fifo_fd_w);
|
||||
close(fifo_fd_r);
|
||||
unlink(observed_fifo);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue