mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 06:56:14 +01:00
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:
parent
e3c66f03e0
commit
25053dc518
@ -49,6 +49,8 @@ struct GTestSuite
|
|||||||
/* --- prototypes --- */
|
/* --- prototypes --- */
|
||||||
static void test_run_seed (const gchar *rseed);
|
static void test_run_seed (const gchar *rseed);
|
||||||
static void test_trap_clear (void);
|
static void test_trap_clear (void);
|
||||||
|
static guint8* g_test_log_dump (GTestLogMsg *msg,
|
||||||
|
guint *len);
|
||||||
|
|
||||||
/* --- variables --- */
|
/* --- variables --- */
|
||||||
static int test_stdmsg = 1;
|
static int test_stdmsg = 1;
|
||||||
@ -77,19 +79,8 @@ static char *test_trap_last_stderr = NULL;
|
|||||||
static gboolean test_debug_log = FALSE;
|
static gboolean test_debug_log = FALSE;
|
||||||
|
|
||||||
/* --- functions --- */
|
/* --- 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*
|
static const char*
|
||||||
test_log_bit (GTestLogBit lbit)
|
test_log_bit (GTestLogType lbit)
|
||||||
{
|
{
|
||||||
switch (lbit)
|
switch (lbit)
|
||||||
{
|
{
|
||||||
@ -104,14 +95,47 @@ test_log_bit (GTestLogBit lbit)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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 *string1,
|
||||||
const gchar *string2,
|
const gchar *string2,
|
||||||
guint n_args,
|
guint n_args,
|
||||||
long double *largs)
|
long double *largs)
|
||||||
{
|
{
|
||||||
gboolean fail = lbit == G_TEST_LOG_STOP_CASE && largs[0] != 0;
|
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)
|
switch (lbit)
|
||||||
{
|
{
|
||||||
@ -132,29 +156,16 @@ g_test_log (GTestLogBit lbit,
|
|||||||
default: ;
|
default: ;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (test_debug_log)
|
msg.log_type = lbit;
|
||||||
g_printerr ("{*LOG(%s)", bit);
|
msg.n_strings = (string1 != NULL) + (string1 && string2);
|
||||||
if (test_debug_log && string1)
|
msg.strings = astrings;
|
||||||
g_printerr (":{%s}", string1);
|
astrings[0] = (gchar*) string1;
|
||||||
if (test_debug_log && string2)
|
astrings[1] = astrings[0] ? (gchar*) string2 : NULL;
|
||||||
g_printerr (":{%s}", string2);
|
msg.n_nums = n_args;
|
||||||
if (n_args)
|
msg.nums = largs;
|
||||||
{
|
dbuffer = g_test_log_dump (&msg, &dbufferlen);
|
||||||
guint i;
|
g_test_log_send (dbufferlen, dbuffer);
|
||||||
if (test_debug_log)
|
g_free (dbuffer);
|
||||||
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");
|
|
||||||
|
|
||||||
switch (lbit)
|
switch (lbit)
|
||||||
{
|
{
|
||||||
@ -1063,3 +1074,172 @@ g_test_trap_assertions (const char *domain,
|
|||||||
g_free (msg);
|
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);
|
||||||
|
}
|
||||||
|
@ -167,6 +167,38 @@ void g_test_add_vtable (const char *testpath,
|
|||||||
void (*data_test) (void),
|
void (*data_test) (void),
|
||||||
void (*data_teardown) (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;
|
G_END_DECLS;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user