From 25053dc5184932660015d1ce6ecbf7dd8677a3c4 Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Tue, 20 Nov 2007 15:00:32 +0000 Subject: [PATCH] Implemented test log serialization. * glib/gtestframework.h: added g_test_log*() API. * glib/gtestframework.c: implement test log serialization. svn path=/trunk/; revision=5886 --- glib/gtestframework.c | 254 ++++++++++++++++++++++++++++++++++++------ glib/gtestframework.h | 32 ++++++ 2 files changed, 249 insertions(+), 37 deletions(-) diff --git a/glib/gtestframework.c b/glib/gtestframework.c index 803b7ffc2..34ebc70e3 100644 --- a/glib/gtestframework.c +++ b/glib/gtestframework.c @@ -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); +} diff --git a/glib/gtestframework.h b/glib/gtestframework.h index beec33e34..fa400f3e5 100644 --- a/glib/gtestframework.h +++ b/glib/gtestframework.h @@ -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;