dbus-daemon test: exercise maximum replies per connection
authorSimon McVittie <simon.mcvittie@collabora.co.uk>
Fri, 1 Jul 2016 12:02:04 +0000 (13:02 +0100)
committerSimon McVittie <smcv@debian.org>
Tue, 16 Aug 2016 16:44:30 +0000 (17:44 +0100)
This time we're doing so deliberately.

Bug: https://bugs.freedesktop.org/show_bug.cgi?id=86442
Signed-off-by: Simon McVittie <simon.mcvittie@collabora.co.uk>
test/Makefile.am
test/data/valid-config-files/max-replies-per-connection.conf.in [new file with mode: 0644]
test/dbus-daemon.c

index 248bb21..602436f 100644 (file)
@@ -349,6 +349,7 @@ in_data = \
        data/valid-config-files/incoming-limit.conf.in \
        data/valid-config-files/max-completed-connections.conf.in \
        data/valid-config-files/max-connections-per-user.conf.in \
+       data/valid-config-files/max-replies-per-connection.conf.in \
        data/valid-config-files/multi-user.conf.in \
        data/valid-config-files/systemd-activation.conf.in \
        data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoExec.service.in \
diff --git a/test/data/valid-config-files/max-replies-per-connection.conf.in b/test/data/valid-config-files/max-replies-per-connection.conf.in
new file mode 100644 (file)
index 0000000..d4615ed
--- /dev/null
@@ -0,0 +1,18 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+  <!-- Our well-known bus type, don't change this -->
+  <type>session</type>
+  <listen>@TEST_LISTEN@</listen>
+
+  <policy context="default">
+    <!-- Allow everything to be sent -->
+    <allow send_destination="*" eavesdrop="true"/>
+    <!-- Allow everything to be received -->
+    <allow eavesdrop="true"/>
+    <!-- Allow anyone to own anything -->
+    <allow own="*"/>
+  </policy>
+
+  <limit name="max_replies_per_connection">3</limit>
+</busconfig>
index c518029..58f8141 100644 (file)
@@ -83,7 +83,9 @@ typedef struct {
     DBusConnection *left_conn;
 
     DBusConnection *right_conn;
+    GQueue held_messages;
     gboolean right_conn_echo;
+    gboolean right_conn_hold;
     gboolean wait_forever_called;
 
     gchar *tmp_runtime_dir;
@@ -121,6 +123,21 @@ echo_filter (DBusConnection *connection,
   return DBUS_HANDLER_RESULT_HANDLED;
 }
 
+static DBusHandlerResult
+hold_filter (DBusConnection *connection,
+    DBusMessage *message,
+    void *user_data)
+{
+  Fixture *f = user_data;
+
+  if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+  g_queue_push_tail (&f->held_messages, dbus_message_ref (message));
+
+  return DBUS_HANDLER_RESULT_HANDLED;
+}
+
 typedef struct {
     const char *bug_ref;
     guint min_messages;
@@ -187,6 +204,15 @@ add_echo_filter (Fixture *f)
 }
 
 static void
+add_hold_filter (Fixture *f)
+{
+  if (!dbus_connection_add_filter (f->right_conn, hold_filter, f, NULL))
+    g_error ("OOM");
+
+  f->right_conn_hold = TRUE;
+}
+
+static void
 pc_count (DBusPendingCall *pc,
     void *data)
 {
@@ -196,6 +222,17 @@ pc_count (DBusPendingCall *pc,
 }
 
 static void
+pc_enqueue (DBusPendingCall *pc,
+            void *data)
+{
+  GQueue *q = data;
+  DBusMessage *m = dbus_pending_call_steal_reply (pc);
+
+  g_test_message ("message of type %d", dbus_message_get_type (m));
+  g_queue_push_tail (q, m);
+}
+
+static void
 test_echo (Fixture *f,
     gconstpointer context)
 {
@@ -687,6 +724,130 @@ test_max_connections (Fixture *f,
 }
 
 static void
+test_max_replies_per_connection (Fixture *f,
+    gconstpointer context)
+{
+  GQueue received = G_QUEUE_INIT;
+  GQueue errors = G_QUEUE_INIT;
+  DBusMessage *m;
+  DBusPendingCall *pc;
+  guint i;
+  DBusError error = DBUS_ERROR_INIT;
+
+  if (f->skip)
+    return;
+
+  add_hold_filter (f);
+
+  /* The configured limit is 3 replies per connection. */
+  for (i = 0; i < 3; i++)
+    {
+      m = dbus_message_new_method_call (
+          dbus_bus_get_unique_name (f->right_conn), "/",
+          "com.example", "Spam");
+
+      if (m == NULL)
+        g_error ("OOM");
+
+      if (!dbus_connection_send_with_reply (f->left_conn, m, &pc,
+                                            DBUS_TIMEOUT_INFINITE) ||
+          pc == NULL)
+        g_error ("OOM");
+
+      if (dbus_pending_call_get_completed (pc))
+        pc_enqueue (pc, &received);
+      else if (!dbus_pending_call_set_notify (pc, pc_enqueue, &received,
+            NULL))
+        g_error ("OOM");
+
+      dbus_pending_call_unref (pc);
+      dbus_message_unref (m);
+    }
+
+  while (g_queue_get_length (&f->held_messages) < 3)
+    test_main_context_iterate (f->ctx, TRUE);
+
+  g_assert_cmpuint (g_queue_get_length (&received), ==, 0);
+  g_assert_cmpuint (g_queue_get_length (&errors), ==, 0);
+
+  /* Go a couple of messages over the limit. */
+  for (i = 0; i < 2; i++)
+    {
+      m = dbus_message_new_method_call (
+          dbus_bus_get_unique_name (f->right_conn), "/",
+          "com.example", "Spam");
+
+      if (m == NULL)
+        g_error ("OOM");
+
+      if (!dbus_connection_send_with_reply (f->left_conn, m, &pc,
+                                            DBUS_TIMEOUT_INFINITE) ||
+          pc == NULL)
+        g_error ("OOM");
+
+      if (dbus_pending_call_get_completed (pc))
+        pc_enqueue (pc, &errors);
+      else if (!dbus_pending_call_set_notify (pc, pc_enqueue, &errors,
+            NULL))
+        g_error ("OOM");
+
+      dbus_pending_call_unref (pc);
+      dbus_message_unref (m);
+    }
+
+  /* Reply to the held messages. */
+  for (m = g_queue_pop_head (&f->held_messages);
+       m != NULL;
+       m = g_queue_pop_head (&f->held_messages))
+    {
+      DBusMessage *reply = dbus_message_new_method_return (m);
+
+      if (reply == NULL)
+        g_error ("OOM");
+
+      if (!dbus_connection_send (f->right_conn, reply, NULL))
+        g_error ("OOM");
+
+      dbus_message_unref (reply);
+    }
+
+  /* Wait for all 5 replies to come in. */
+  while (g_queue_get_length (&received) + g_queue_get_length (&errors) < 5)
+    test_main_context_iterate (f->ctx, TRUE);
+
+  /* The first three succeeded. */
+  for (i = 0; i < 3; i++)
+    {
+      m = g_queue_pop_head (&received);
+      g_assert (m != NULL);
+
+      if (dbus_set_error_from_message (&error, m))
+        g_error ("Unexpected error: %s: %s", error.name, error.message);
+
+      dbus_message_unref (m);
+    }
+
+  /* The last two failed. */
+  for (i = 0; i < 2; i++)
+    {
+      m = g_queue_pop_head (&errors);
+      g_assert (m != NULL);
+
+      if (!dbus_set_error_from_message (&error, m))
+        g_error ("Unexpected success");
+
+      g_assert_cmpstr (error.name, ==, DBUS_ERROR_LIMITS_EXCEEDED);
+      dbus_error_free (&error);
+      dbus_message_unref (m);
+    }
+
+  g_assert_cmpuint (g_queue_get_length (&received), ==, 0);
+  g_assert_cmpuint (g_queue_get_length (&errors), ==, 0);
+  g_queue_clear (&received);
+  g_queue_clear (&errors);
+}
+
+static void
 teardown (Fixture *f,
     gconstpointer context G_GNUC_UNUSED)
 {
@@ -708,6 +869,15 @@ teardown (Fixture *f,
           f->right_conn_echo = FALSE;
         }
 
+      if (f->right_conn_hold)
+        {
+          dbus_connection_remove_filter (f->right_conn, hold_filter, f);
+          f->right_conn_hold = FALSE;
+        }
+
+      g_queue_foreach (&f->held_messages, (GFunc) dbus_message_unref, NULL);
+      g_queue_clear (&f->held_messages);
+
       dbus_connection_close (f->right_conn);
       dbus_connection_unref (f->right_conn);
       f->right_conn = NULL;
@@ -771,6 +941,11 @@ static Config max_connections_per_user_config = {
     SPECIFY_ADDRESS
 };
 
+static Config max_replies_per_connection_config = {
+    NULL, 1, "valid-config-files/max-replies-per-connection.conf",
+    SPECIFY_ADDRESS
+};
+
 int
 main (int argc,
     char **argv)
@@ -794,6 +969,9 @@ main (int argc,
   g_test_add ("/limits/max-connections-per-user", Fixture,
       &max_connections_per_user_config,
       setup, test_max_connections, teardown);
+  g_test_add ("/limits/max-replies-per-connection", Fixture,
+      &max_replies_per_connection_config,
+      setup, test_max_replies_per_connection, teardown);
 #ifdef DBUS_UNIX
   /* We can't test this in loopback.c with the rest of unix:runtime=yes,
    * because dbus_bus_get[_private] is the only way to use the default,