Implemented test log serialization.
authorTim Janik <timj@src.gnome.org>
Tue, 20 Nov 2007 15:00:32 +0000 (15:00 +0000)
committerTim Janik <timj@src.gnome.org>
Tue, 20 Nov 2007 15:00:32 +0000 (15:00 +0000)
* glib/gtestframework.h: added g_test_log*() API.

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

svn path=/trunk/; revision=5886

glib/gtestframework.c
glib/gtestframework.h

index 803b7ff..34ebc70 100644 (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);
+}
index beec33e..fa400f3 100644 (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;