Add a test for marshalling and endian-swapping
authorSimon McVittie <simon.mcvittie@collabora.co.uk>
Thu, 9 Jun 2011 17:47:04 +0000 (18:47 +0100)
committerSimon McVittie <simon.mcvittie@collabora.co.uk>
Fri, 10 Jun 2011 17:32:43 +0000 (18:32 +0100)
Reviewed-by: Will Thompson <will.thompson@collabora.co.uk>
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=38120

test/.gitignore
test/Makefile.am
test/marshal.c [new file with mode: 0644]

index 77f38e5..1337de6 100644 (file)
@@ -27,3 +27,4 @@ test-names
 test-loopback
 test-relay
 test-dbus-daemon
+test-marshal
index e74a8d3..afa99dc 100644 (file)
@@ -80,6 +80,7 @@ installable_tests = \
        test-corrupt \
        test-dbus-daemon \
        test-loopback \
+       test-marshal \
        test-relay \
        $(NULL)
 
@@ -120,6 +121,13 @@ test_dbus_daemon_LDADD = $(top_builddir)/dbus/libdbus-1.la \
     $(GLIB_LIBS) \
     $(DBUS_GLIB_LIBS)
 
+test_marshal_SOURCES = marshal.c
+test_marshal_CPPFLAGS = $(GLIB_CFLAGS) $(DBUS_GLIB_CFLAGS)
+test_marshal_LDFLAGS = @R_DYNAMIC_LDFLAG@
+test_marshal_LDADD = $(top_builddir)/dbus/libdbus-1.la \
+    $(GLIB_LIBS) \
+    $(DBUS_GLIB_LIBS)
+
 if DBUS_ENABLE_MODULAR_TESTS
 TESTS += $(installable_tests)
 installcheck_tests += $(installable_tests)
diff --git a/test/marshal.c b/test/marshal.c
new file mode 100644 (file)
index 0000000..d87e440
--- /dev/null
@@ -0,0 +1,220 @@
+/* Simple sanity-check for D-Bus message serialization.
+ *
+ * Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
+ * Copyright © 2010-2011 Nokia Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+typedef struct {
+    DBusError e;
+} Fixture;
+
+static void
+assert_no_error (const DBusError *e)
+{
+  if (G_UNLIKELY (dbus_error_is_set (e)))
+    g_error ("expected success but got error: %s: %s", e->name, e->message);
+}
+
+static void
+setup (Fixture *f,
+    gconstpointer arg G_GNUC_UNUSED)
+{
+  dbus_error_init (&f->e);
+}
+
+/* this is meant to be obviously correct, not efficient! */
+static guint32
+get_uint32 (const gchar *blob,
+    gsize offset,
+    char endian)
+{
+  if (endian == 'l')
+    {
+      return
+        blob[offset] |
+        (blob[offset + 1] << 8) |
+        (blob[offset + 2] << 16) |
+        (blob[offset + 3] << 24);
+    }
+  else if (endian == 'B')
+    {
+      return
+        (blob[offset] << 24) |
+        (blob[offset + 1] << 16) |
+        (blob[offset + 2] << 8) |
+        blob[offset + 3];
+    }
+  else
+    {
+      g_assert_not_reached ();
+    }
+}
+
+#define BLOB_LENGTH (sizeof (le_blob) - 1)
+#define OFFSET_BODY_LENGTH (4)
+#define OFFSET_SERIAL (8)
+
+const gchar le_blob[] =
+    /* byte 0 */
+    /* yyyyuu fixed headers */
+    "l"                     /* little-endian */
+    "\2"                    /* reply (which is the simplest message) */
+    "\2"                    /* no auto-starting */
+    "\1"                    /* D-Bus version = 1 */
+    /* byte 4 */
+    "\4\0\0\0"              /* bytes in body = 4 */
+    /* byte 8 */
+    "\x78\x56\x34\x12"      /* serial number = 0x12345678 */
+    /* byte 12 */
+    /* a(uv) variable headers start here */
+    "\x0f\0\0\0"            /* bytes in array of variable headers = 15 */
+                            /* pad to 8-byte boundary = nothing */
+    /* byte 16 */
+    "\5"                    /* in reply to: */
+        "\1u\0"             /* variant signature = u */
+                            /* pad to 4-byte boundary = nothing */
+        "\x12\xef\xcd\xab"  /* 0xabcdef12 */
+                            /* pad to 8-byte boundary = nothing */
+    /* byte 24 */
+    "\x08"                  /* signature: */
+        "\1g\0"             /* variant signature = g */
+        "\1u\0"             /* 1 byte, u, NUL (no alignment needed) */
+        "\0"                /* pad to 8-byte boundary for body */
+    /* body; byte 32 */
+    "\xef\xbe\xad\xde"      /* 0xdeadbeef */
+    ;
+
+const gchar be_blob[] =
+    /* byte 0 */
+    /* yyyyuu fixed headers */
+    "B"                     /* big-endian */
+    "\2"                    /* reply (which is the simplest message) */
+    "\2"                    /* no auto-starting */
+    "\1"                    /* D-Bus version = 1 */
+    /* byte 4 */
+    "\0\0\0\4"              /* bytes in body = 4 */
+    /* byte 8 */
+    "\x12\x34\x56\x78"      /* serial number = 0x12345678 */
+    /* byte 12 */
+    /* a(uv) variable headers start here */
+    "\0\0\0\x0f"            /* bytes in array of variable headers = 15 */
+                            /* pad to 8-byte boundary = nothing */
+    /* byte 16 */
+    "\5"                    /* in reply to: */
+        "\1u\0"             /* variant signature = u */
+                            /* pad to 4-byte boundary = nothing */
+        "\xab\xcd\xef\x12"  /* 0xabcdef12 */
+                            /* pad to 8-byte boundary = nothing */
+    /* byte 24 */
+    "\x08"                  /* signature: */
+        "\1g\0"             /* variant signature = g */
+        "\1u\0"             /* 1 byte, u, NUL (no alignment needed) */
+        "\0"                /* pad to 8-byte boundary for body */
+    /* body; byte 32 */
+    "\xde\xad\xbe\xef"      /* 0xdeadbeef */
+    ;
+
+static void
+test_endian (Fixture *f,
+    gconstpointer arg)
+{
+  const gchar *blob = arg;
+  const gchar *native_blob;
+  char *output;
+  DBusMessage *m;
+  int len;
+  dbus_uint32_t u;
+  dbus_bool_t ok;
+
+  g_assert_cmpuint ((guint) sizeof (le_blob), ==, (guint) sizeof (be_blob));
+
+  g_assert_cmpuint (get_uint32 (blob, OFFSET_BODY_LENGTH, blob[0]), ==, 4);
+  g_assert_cmpuint (get_uint32 (blob, OFFSET_SERIAL, blob[0]), ==,
+      0x12345678u);
+
+  len = dbus_message_demarshal_bytes_needed (blob, sizeof (le_blob));
+  /* everything in the string except the implicit "\0" at the end is part of
+   * the message */
+  g_assert_cmpint (len, ==, BLOB_LENGTH);
+
+  m = dbus_message_demarshal (blob, sizeof (le_blob), &f->e);
+  assert_no_error (&f->e);
+  g_assert (m != NULL);
+
+  g_assert_cmpuint (dbus_message_get_serial (m), ==, 0x12345678u);
+  g_assert_cmpuint (dbus_message_get_reply_serial (m), ==, 0xabcdef12u);
+  g_assert_cmpstr (dbus_message_get_signature (m), ==, "u");
+
+  /* Implementation detail: appending to the message results in it being
+   * byteswapped into compiler byte order, which exposed a bug in libdbus,
+   * fd.o #38120. (If that changes, this test might not exercise that
+   * particular bug but will still be valid.) */
+  u = 0xdecafbadu;
+  ok = dbus_message_append_args (m,
+      DBUS_TYPE_UINT32, &u,
+      DBUS_TYPE_INVALID);
+  g_assert (ok);
+
+  dbus_message_marshal (m, &output, &len);
+
+  g_assert (output[0] == 'l' || output[0] == 'B');
+  /* the single-byte fields are unaffected, even if the endianness was
+   * swapped */
+  g_assert_cmpint (output[1], ==, blob[1]);
+  g_assert_cmpint (output[2], ==, blob[2]);
+  g_assert_cmpint (output[3], ==, blob[3]);
+  /* the length and serial are in the new endianness, the length has expanded
+   * to 8, and the serial is correct */
+  g_assert_cmpuint (get_uint32 (output, OFFSET_BODY_LENGTH, output[0]), ==, 8);
+  g_assert_cmpuint (get_uint32 (output, OFFSET_SERIAL, output[0]), ==,
+      0x12345678u);
+  /* the second "u" in the signature replaced a padding byte, so only
+   * the length of the body changed */
+  g_assert_cmpint (len, ==, BLOB_LENGTH + 4);
+}
+
+static void
+teardown (Fixture *f,
+    gconstpointer arg G_GNUC_UNUSED)
+{
+  dbus_error_free (&f->e);
+}
+
+int
+main (int argc,
+    char **argv)
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add ("/demarshal/le", Fixture, le_blob, setup, test_endian, teardown);
+  g_test_add ("/demarshal/be", Fixture, be_blob, setup, test_endian, teardown);
+
+  return g_test_run ();
+}