Implemented test log serialization.

* glib/gtestframework.h: added g_test_log*() API.

* glib/gtestframework.c: implement test log serialization.

svn path=/trunk/; revision=5886
This commit is contained in:
Tim Janik 2007-11-20 15:00:32 +00:00
parent e3c66f03e0
commit 25053dc518
2 changed files with 249 additions and 37 deletions

View File

@ -49,6 +49,8 @@ struct GTestSuite
/* --- prototypes --- */
static void test_run_seed (const gchar *rseed);
static void test_trap_clear (void);
static guint8* g_test_log_dump (GTestLogMsg *msg,
guint *len);
/* --- variables --- */
static int test_stdmsg = 1;
@ -77,19 +79,8 @@ static char *test_trap_last_stderr = NULL;
static gboolean test_debug_log = FALSE;
/* --- functions --- */
typedef enum {
G_TEST_LOG_NONE,
G_TEST_LOG_ERROR, // s:msg
G_TEST_LOG_START_BINARY, // s:binaryname s:seed
G_TEST_LOG_LIST_CASE, // s:testpath
G_TEST_LOG_START_CASE, // s:testpath
G_TEST_LOG_STOP_CASE, // d:status d:nforks d:elapsed
G_TEST_LOG_MIN_RESULT, // s:blurb d:result
G_TEST_LOG_MAX_RESULT, // s:blurb d:result
} GTestLogBit;
static const char*
test_log_bit (GTestLogBit lbit)
test_log_bit (GTestLogType lbit)
{
switch (lbit)
{
@ -104,14 +95,47 @@ test_log_bit (GTestLogBit lbit)
}
static void
g_test_log (GTestLogBit lbit,
g_test_log_send (guint n_bytes,
const guint8 *buffer)
{
if (test_debug_log)
{
GTestLogBuffer *lbuffer = g_test_log_buffer_new();
GTestLogMsg *msg;
guint ui;
g_test_log_buffer_push (lbuffer, n_bytes, buffer);
msg = g_test_log_buffer_pop (lbuffer);
g_assert (msg != NULL); // FIXME: should be g_awrn_if_fail
g_assert (lbuffer->data->len == 0); // FIXME: should be g_awrn_if_fail
g_test_log_buffer_free (lbuffer);
/* print message */
g_printerr ("{*LOG(%s)", test_log_bit (msg->log_type));
for (ui = 0; ui < msg->n_strings; ui++)
g_printerr (":{%s}", msg->strings[ui]);
if (msg->n_nums)
{
g_printerr (":(");
for (ui = 0; ui < msg->n_nums; ui++)
g_printerr ("%s%.16Lg", ui ? ";" : "", msg->nums[ui]);
g_printerr (")");
}
g_printerr (":LOG*}\n");
g_test_log_msg_free (msg);
}
}
static void
g_test_log (GTestLogType lbit,
const gchar *string1,
const gchar *string2,
guint n_args,
long double *largs)
{
gboolean fail = lbit == G_TEST_LOG_STOP_CASE && largs[0] != 0;
const char *bit = test_log_bit (lbit);
GTestLogMsg msg;
gchar *astrings[3] = { NULL, NULL, NULL };
guint8 *dbuffer;
guint32 dbufferlen;
switch (lbit)
{
@ -132,29 +156,16 @@ g_test_log (GTestLogBit lbit,
default: ;
}
if (test_debug_log)
g_printerr ("{*LOG(%s)", bit);
if (test_debug_log && string1)
g_printerr (":{%s}", string1);
if (test_debug_log && string2)
g_printerr (":{%s}", string2);
if (n_args)
{
guint i;
if (test_debug_log)
g_printerr (":(");
for (i = 0; i < n_args; i++)
{
if (i && test_debug_log)
g_printerr (";");
if (test_debug_log)
g_printerr ("%.16Lg", largs[i]);
}
if (test_debug_log)
g_printerr (")");
}
if (test_debug_log)
g_printerr (":LOG*}\n");
msg.log_type = lbit;
msg.n_strings = (string1 != NULL) + (string1 && string2);
msg.strings = astrings;
astrings[0] = (gchar*) string1;
astrings[1] = astrings[0] ? (gchar*) string2 : NULL;
msg.n_nums = n_args;
msg.nums = largs;
dbuffer = g_test_log_dump (&msg, &dbufferlen);
g_test_log_send (dbufferlen, dbuffer);
g_free (dbuffer);
switch (lbit)
{
@ -1063,3 +1074,172 @@ g_test_trap_assertions (const char *domain,
g_free (msg);
}
}
static void
gstring_overwrite_int (GString *gstring,
guint pos,
guint32 vuint)
{
vuint = g_htonl (vuint);
g_string_overwrite_len (gstring, pos, (const gchar*) &vuint, 4);
}
static void
gstring_append_int (GString *gstring,
guint32 vuint)
{
vuint = g_htonl (vuint);
g_string_append_len (gstring, (const gchar*) &vuint, 4);
}
static void
gstring_append_double (GString *gstring,
double vdouble)
{
union { double vdouble; guint64 vuint64; } u;
u.vdouble = vdouble;
u.vuint64 = GUINT64_TO_BE (u.vuint64);
g_string_append_len (gstring, (const gchar*) &u.vuint64, 8);
}
static guint8*
g_test_log_dump (GTestLogMsg *msg,
guint *len)
{
GString *gstring = g_string_sized_new (1024);
guint ui;
gstring_append_int (gstring, 0); /* message length */
gstring_append_int (gstring, msg->log_type);
gstring_append_int (gstring, msg->n_strings);
gstring_append_int (gstring, msg->n_nums);
gstring_append_int (gstring, 0); /* reserved */
for (ui = 0; ui < msg->n_strings; ui++)
{
guint l = strlen (msg->strings[ui]);
gstring_append_int (gstring, l);
g_string_append_len (gstring, msg->strings[ui], l);
}
for (ui = 0; ui < msg->n_nums; ui++)
gstring_append_double (gstring, msg->nums[ui]);
*len = gstring->len;
gstring_overwrite_int (gstring, 0, *len); /* message length */
return (guint8*) g_string_free (gstring, FALSE);
}
static inline long double
net_double (const gchar **ipointer)
{
union { guint64 vuint64; double vdouble; } u;
guint64 aligned_int64;
memcpy (&aligned_int64, *ipointer, 8);
*ipointer += 8;
u.vuint64 = GUINT64_FROM_BE (aligned_int64);
return u.vdouble;
}
static inline guint32
net_int (const gchar **ipointer)
{
guint32 aligned_int;
memcpy (&aligned_int, *ipointer, 4);
*ipointer += 4;
return g_ntohl (aligned_int);
}
static gboolean
g_test_log_extract (GTestLogBuffer *tbuffer)
{
const gchar *p = tbuffer->data->str;
GTestLogMsg msg;
guint mlength;
if (tbuffer->data->len < 4 * 5)
return FALSE;
mlength = net_int (&p);
if (tbuffer->data->len < mlength)
return FALSE;
msg.log_type = net_int (&p);
msg.n_strings = net_int (&p);
msg.n_nums = net_int (&p);
if (net_int (&p) == 0)
{
guint ui;
msg.strings = g_new0 (gchar*, msg.n_strings + 1);
msg.nums = g_new0 (long double, msg.n_nums);
for (ui = 0; ui < msg.n_strings; ui++)
{
guint sl = net_int (&p);
msg.strings[ui] = g_strndup (p, sl);
p += sl;
}
for (ui = 0; ui < msg.n_nums; ui++)
msg.nums[ui] = net_double (&p);
if (p <= tbuffer->data->str + mlength)
{
g_string_erase (tbuffer->data, 0, mlength);
tbuffer->msgs = g_slist_prepend (tbuffer->msgs, g_memdup (&msg, sizeof (msg)));
return TRUE;
}
}
g_free (msg.nums);
g_strfreev (msg.strings);
g_error ("corrupt log stream from test program");
return FALSE;
}
GTestLogBuffer*
g_test_log_buffer_new (void)
{
GTestLogBuffer *tb = g_new0 (GTestLogBuffer, 1);
tb->data = g_string_sized_new (1024);
return tb;
}
void
g_test_log_buffer_free (GTestLogBuffer *tbuffer)
{
g_return_if_fail (tbuffer != NULL);
while (tbuffer->msgs)
g_test_log_msg_free (g_test_log_buffer_pop (tbuffer));
g_string_free (tbuffer->data, TRUE);
g_free (tbuffer);
}
void
g_test_log_buffer_push (GTestLogBuffer *tbuffer,
guint n_bytes,
const guint8 *bytes)
{
g_return_if_fail (tbuffer != NULL);
if (n_bytes)
{
gboolean more_messages;
g_return_if_fail (bytes != NULL);
g_string_append_len (tbuffer->data, (const gchar*) bytes, n_bytes);
do
more_messages = g_test_log_extract (tbuffer);
while (more_messages);
}
}
GTestLogMsg*
g_test_log_buffer_pop (GTestLogBuffer *tbuffer)
{
GTestLogMsg *msg = NULL;
g_return_val_if_fail (tbuffer != NULL, NULL);
if (tbuffer->msgs)
{
GSList *slist = g_slist_last (tbuffer->msgs);
msg = slist->data;
tbuffer->msgs = g_slist_delete_link (tbuffer->msgs, slist);
}
return msg;
}
void
g_test_log_msg_free (GTestLogMsg *tmsg)
{
g_return_if_fail (tmsg != NULL);
g_strfreev (tmsg->strings);
g_free (tmsg->nums);
g_free (tmsg);
}

View File

@ -167,6 +167,38 @@ void g_test_add_vtable (const char *testpath,
void (*data_test) (void),
void (*data_teardown) (void));
/* internal logging API */
typedef enum {
G_TEST_LOG_NONE,
G_TEST_LOG_ERROR, // s:msg
G_TEST_LOG_START_BINARY, // s:binaryname s:seed
G_TEST_LOG_LIST_CASE, // s:testpath
G_TEST_LOG_START_CASE, // s:testpath
G_TEST_LOG_STOP_CASE, // d:status d:nforks d:elapsed
G_TEST_LOG_MIN_RESULT, // s:blurb d:result
G_TEST_LOG_MAX_RESULT, // s:blurb d:result
} GTestLogType;
typedef struct {
GTestLogType log_type;
guint n_strings;
gchar **strings; // NULL terminated
guint n_nums;
long double *nums;
} GTestLogMsg;
typedef struct {
/*< private >*/
GString *data;
GSList *msgs;
} GTestLogBuffer;
GTestLogBuffer* g_test_log_buffer_new (void);
void g_test_log_buffer_free (GTestLogBuffer *tbuffer);
void g_test_log_buffer_push (GTestLogBuffer *tbuffer,
guint n_bytes,
const guint8 *bytes);
GTestLogMsg* g_test_log_buffer_pop (GTestLogBuffer *tbuffer);
void g_test_log_msg_free (GTestLogMsg *tmsg);
G_END_DECLS;