summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJens Georg <mail@jensge.org>2022-06-19 20:01:45 +0200
committerJens Georg <mail@jensge.org>2022-06-19 20:06:12 +0200
commit67a109cc7e219c5be7a23fd4318db5d0758b05cc (patch)
treeb420bb660dbd8b6461e67647503f8ad519fdc0d3
parent532e46739f8e160bdd9d38f21c46129d7e4d628e (diff)
downloadgupnp-67a109cc7e219c5be7a23fd4318db5d0758b05cc.tar.gz
ContextManager: Handle filter events properly
If the filter is changed, announce all changed to contexts as well This means if the filter is changed, causing a known device not to match anymore, it will signal "context-unavailable" and drop all managed devices attached to that context. Likewise, for filter changes that cause contexts to not being filtered out any more, "context-available" will be signalled. Fixes #36 Fixes #37
-rw-r--r--libgupnp/gupnp-context-manager.c424
-rw-r--r--tests/test-context-manager.c209
2 files changed, 420 insertions, 213 deletions
diff --git a/libgupnp/gupnp-context-manager.c b/libgupnp/gupnp-context-manager.c
index 9501f3d..b9cf511 100644
--- a/libgupnp/gupnp-context-manager.c
+++ b/libgupnp/gupnp-context-manager.c
@@ -47,10 +47,16 @@ struct _GUPnPContextManagerPrivate {
GUPnPContextManager *impl;
- GList *objects; /* control points and root devices */
+ GPtrArray *control_points;
+ GPtrArray *root_devices;
+
GList *filtered; /* Filtered contexts */
+ // map of context -> managed objects, doubles also as a set of seen contexts
+ GHashTable *contexts;
+
GUPnPContextFilter *context_filter;
+ gboolean syntesized_internal;
};
typedef struct _GUPnPContextManagerPrivate GUPnPContextManagerPrivate;
@@ -92,9 +98,10 @@ enum {
static guint signals[SIGNAL_LAST];
-static gint32
-handle_update (GUPnPRootDevice *root_device)
+static void
+handle_update (GUPnPRootDevice *root_device, gpointer user_data)
{
+ gint32 *output = user_data;
gint32 boot_id;
GSSDPResourceGroup *group = NULL;
GSSDPClient *client = NULL;
@@ -104,7 +111,48 @@ handle_update (GUPnPRootDevice *root_device)
g_object_get (G_OBJECT (client), "boot-id", &boot_id, NULL);
gssdp_resource_group_update (group, ++boot_id);
- return boot_id;
+ *output = boot_id;
+}
+
+static gboolean
+context_filtered (GUPnPContextFilter *filter, GUPnPContext *context)
+{
+ return !gupnp_context_filter_is_empty (filter) &&
+ gupnp_context_filter_get_enabled (filter) &&
+ !gupnp_context_filter_check_context (filter, context);
+}
+
+static GPtrArray *
+ensure_context (GHashTable *contexts, GUPnPContext *context)
+{
+ GPtrArray *objects = g_hash_table_lookup (contexts, context);
+ if (objects == NULL) {
+ objects = g_ptr_array_new_with_free_func (g_object_unref);
+ g_hash_table_insert (contexts, g_object_ref (context), objects);
+ }
+
+ return objects;
+}
+
+static void
+do_boot_id_update_for_root_devices (GUPnPContextManager *manager)
+{
+ GUPnPContextManagerPrivate *priv;
+ priv = gupnp_context_manager_get_instance_private (manager);
+
+ // Nothing to do for UDA 1.0. It does not have the boot-id
+ // concept
+ if (priv->uda_version == GSSDP_UDA_VERSION_1_0) {
+ return;
+ }
+
+ gint32 boot_id = -1;
+ g_ptr_array_foreach (priv->root_devices,
+ (GFunc) handle_update,
+ &boot_id);
+ if (boot_id > 1) {
+ priv->boot_id = boot_id;
+ }
}
static void
@@ -112,54 +160,31 @@ on_context_available (GUPnPContextManager *manager,
GUPnPContext *context,
G_GNUC_UNUSED gpointer *user_data)
{
- GUPnPContextFilter *context_filter;
GUPnPContextManagerPrivate *priv;
- gboolean enabled = TRUE;
-
priv = gupnp_context_manager_get_instance_private (manager);
- context_filter = priv->context_filter;
+ if (priv->syntesized_internal)
+ return;
+
+ ensure_context (priv->contexts, context);
- /* Try to catch the notification, only if the context filter
- * is enabled, not empty and the context doesn't match */
- if (!gupnp_context_filter_is_empty (context_filter) &&
- gupnp_context_filter_get_enabled (context_filter) &&
- !gupnp_context_filter_check_context (context_filter, context)) {
+ if (context_filtered (priv->context_filter, context)) {
/* If the context doesn't match, block the notification
* and disable the context */
g_signal_stop_emission_by_name (manager, "context-available");
/* Make sure we don't send anything on now blocked network */
g_object_set (context, "active", FALSE, NULL);
- enabled = FALSE;
/* Save it in case we need to re-enable it */
priv->filtered =
g_list_prepend (priv->filtered, g_object_ref (context));
- }
- /* Ignore the boot-id handling for UDA 1.0 */
- if (priv->uda_version == GSSDP_UDA_VERSION_1_0)
return;
-
- if (enabled) {
- /* We have a new context, so we need to send ssdp:update and
- * re-announce on the old clients */
- GList *l = priv->objects;
- gint32 boot_id = -1;
-
- while (l) {
- if (GUPNP_IS_ROOT_DEVICE (l->data)) {
- boot_id = handle_update (GUPNP_ROOT_DEVICE (l->data));
- }
- l = l->next;
- }
-
- if (boot_id > -1) {
- priv->boot_id = boot_id;
- }
}
+ do_boot_id_update_for_root_devices (manager);
+
/* The new client gets the current boot-id */
gssdp_client_set_boot_id (GSSDP_CLIENT (context), priv->boot_id);
}
@@ -169,199 +194,176 @@ on_context_unavailable (GUPnPContextManager *manager,
GUPnPContext *context,
G_GNUC_UNUSED gpointer *user_data)
{
- GList *l;
- GList *filtered_context;
GUPnPContextManagerPrivate *priv;
-
priv = gupnp_context_manager_get_instance_private (manager);
+ if (priv->syntesized_internal)
+ return;
+
/* Make sure we don't send anything on now unavailable network */
g_object_set (context, "active", FALSE, NULL);
- /* Unref all associated objects */
- l = priv->objects;
-
- while (l) {
- GUPnPContext *obj_context = NULL;
-
- if (GUPNP_IS_CONTROL_POINT (l->data)) {
- GUPnPControlPoint *cp;
-
- cp = GUPNP_CONTROL_POINT (l->data);
- obj_context = gupnp_control_point_get_context (cp);
- } else if (GUPNP_IS_ROOT_DEVICE (l->data)) {
- GUPnPDeviceInfo *info;
-
- info = GUPNP_DEVICE_INFO (l->data);
- obj_context = gupnp_device_info_get_context (info);
- } else {
- g_assert_not_reached ();
- }
-
- if (context == obj_context) {
- GList *next = l->next;
-
- g_object_unref (l->data);
+ GList *ctx = g_list_find (priv->filtered, context);
+ if (ctx != NULL) {
+ g_signal_stop_emission_by_name (manager, "context-unavailable");
- priv->objects = g_list_delete_link (priv->objects,
- l);
- l = next;
- } else {
- l = l->next;
- }
+ priv->filtered = g_list_remove_link (priv->filtered, ctx);
+ g_object_unref (ctx);
}
- filtered_context = g_list_find (priv->filtered, context);
-
- if (filtered_context != NULL) {
- g_signal_stop_emission_by_name (manager, "context-unavailable");
+ g_hash_table_remove (priv->contexts, context);
- g_object_unref (filtered_context->data);
- priv->filtered =
- g_list_delete_link (priv->filtered, filtered_context);
- } else {
- /* When UDA 1.0, ignore boot-id handling */
- if (priv->uda_version == GSSDP_UDA_VERSION_1_0) {
- return;
- }
- /* We have lost a context, so we need to send ssdp:update and
- * re-announce on the old clients */
- GList *l = priv->objects;
- gint32 boot_id = -1;
-
- while (l) {
- if (GUPNP_IS_ROOT_DEVICE (l->data)) {
- boot_id = handle_update (GUPNP_ROOT_DEVICE (l->data));
- }
- l = l->next;
- }
+ // The context was not announced, we can just silenty leave after removing
+ // it from the list of all contexts.
+ if (ctx != NULL)
+ return;
- if (boot_id > -1) {
- gssdp_client_set_boot_id (GSSDP_CLIENT (context), boot_id);
- priv->boot_id = boot_id;
- }
- }
+ // Handle the boot-id changes because of changed contexts
+ do_boot_id_update_for_root_devices (manager);
}
static void
-gupnp_context_manager_filter_context (GUPnPContextFilter *context_filter,
- GUPnPContextManager *manager,
- gboolean check)
+on_context_filter_change_cb (GUPnPContextFilter *context_filter,
+ GParamSpec *pspec,
+ gpointer user_data)
{
- GList *next;
- GList *obj;
- GList *iter;
- gboolean match;
+ GUPnPContextManager *manager = GUPNP_CONTEXT_MANAGER (user_data);
GUPnPContextManagerPrivate *priv;
+ gboolean enabled;
priv = gupnp_context_manager_get_instance_private (manager);
+ enabled = gupnp_context_filter_get_enabled (context_filter);
- obj = priv->objects;
- iter = priv->filtered;
-
- while (obj != NULL) {
- /* If the context filter is empty, treat it as disabled */
- if (check) {
- GUPnPContext *context;
- const char *property = "context";
+ if (!enabled) {
+ // Don't care. Nothing to do
+ return;
+ }
- if (GUPNP_IS_CONTROL_POINT (obj->data)) {
- property = "client";
+ GHashTableIter iter;
+ g_hash_table_iter_init (&iter, priv->contexts);
+ GUPnPContext *key;
+ while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) {
+ GList *filtered = g_list_find (priv->filtered, key);
+
+ if (context_filtered (context_filter, key)) {
+ // This context was already filtered. Nothing to
+ // do
+ if (filtered != NULL) {
+ continue;
}
- g_object_get (G_OBJECT (obj->data),
- property, &context,
- NULL);
+ // This context is now filtered
+ priv->filtered = g_list_prepend (priv->filtered, key);
- match = gupnp_context_filter_check_context (
- context_filter,
- context);
+ // Drop all references to the objects we manage
+ g_hash_table_iter_replace (
+ &iter,
+ g_ptr_array_new_with_free_func (
+ g_object_unref));
- g_object_unref (context);
- } else {
- /* Re-activate all context, if needed */
- match = TRUE;
- }
+ // Synthesize unavailable signal
+ priv->syntesized_internal = TRUE;
+ g_object_set (G_OBJECT (key), "active", FALSE, NULL);
+ g_signal_emit (manager,
+ signals[CONTEXT_UNAVAILABLE],
+ 0,
+ key);
+ priv->syntesized_internal = FALSE;
- if (GUPNP_IS_CONTROL_POINT (obj->data)) {
- GSSDPResourceBrowser *browser;
-
- browser = GSSDP_RESOURCE_BROWSER (obj->data);
- gssdp_resource_browser_set_active (browser, match);
- } else if (GUPNP_IS_ROOT_DEVICE (obj->data)) {
- GSSDPResourceGroup *group;
+ } else {
+ // The context is not filtered
+ if (filtered == NULL) {
+ // The context wasn't filtered before
+ // -> nothing to do
+ continue;
+ }
- group = GSSDP_RESOURCE_GROUP (obj->data);
- gssdp_resource_group_set_available (group, match);
- } else
- g_assert_not_reached ();
+ // Drop the reference from the filter list
+ priv->filtered =
+ g_list_delete_link (priv->filtered, filtered);
- obj = obj->next;
- }
+ g_object_set (G_OBJECT (key), "active", TRUE, NULL);
- while (iter != NULL) {
- /* If the context filter is empty, treat it as disabled */
- if (check)
- /* Filter out context */
- match = gupnp_context_filter_check_context (
- context_filter,
- iter->data);
- else
- /* Re-activate all context, if needed */
- match = TRUE;
-
- if (!match) {
- iter = iter->next;
- continue;
+ priv->syntesized_internal = TRUE;
+ g_signal_emit (manager,
+ signals[CONTEXT_AVAILABLE],
+ 0,
+ key);
+ priv->syntesized_internal = FALSE;
}
-
- next = iter->next;
- g_object_set (iter->data, "active", TRUE, NULL);
-
- g_signal_emit_by_name (manager,
- "context-available",
- iter->data);
-
- g_object_unref (iter->data);
- priv->filtered = g_list_delete_link (priv->filtered, iter);
- iter = next;
}
}
static void
-on_context_filter_change_cb (GUPnPContextFilter *context_filter,
- GParamSpec *pspec,
- gpointer user_data)
-{
- GUPnPContextManager *manager = GUPNP_CONTEXT_MANAGER (user_data);
- gboolean enabled;
- gboolean is_empty;
-
- enabled = gupnp_context_filter_get_enabled (context_filter);
- is_empty = gupnp_context_filter_is_empty (context_filter);
-
- if (enabled)
- gupnp_context_manager_filter_context (context_filter,
- manager,
- !is_empty);
-}
-
-static void
on_context_filter_enabled_cb (GUPnPContextFilter *context_filter,
GParamSpec *pspec,
gpointer user_data)
{
GUPnPContextManager *manager = GUPNP_CONTEXT_MANAGER (user_data);
+ GUPnPContextManagerPrivate *priv;
+
gboolean enabled;
gboolean is_empty;
enabled = gupnp_context_filter_get_enabled (context_filter);
is_empty = gupnp_context_filter_is_empty (context_filter);
+ priv = gupnp_context_manager_get_instance_private (manager);
+
+ // we have switched from enabled to disabled. Flush the filtered
+ // context queue
+ if (!enabled) {
+ while (priv->filtered != NULL) {
+ // This is ok since the filter is disabled. The
+ // callback will not modify the list as well
+
+ // Do not block our handler, that is what we want here
+ g_object_set (G_OBJECT (priv->filtered->data),
+ "active",
+ TRUE,
+ NULL);
+
+ g_signal_emit (manager,
+ signals[CONTEXT_AVAILABLE],
+ 0,
+ priv->filtered->data);
+
+ priv->filtered = g_list_delete_link (priv->filtered,
+ priv->filtered);
+ }
+
+ return;
+ }
+
+ // We have switched from disabled to enabled, but the filter is empty.
+ // Nothing to do.
+ if (enabled && is_empty) {
+ return;
+ }
- if (!is_empty)
- gupnp_context_manager_filter_context (context_filter,
- manager,
- enabled);
+ GHashTableIter iter;
+ g_hash_table_iter_init (&iter, priv->contexts);
+ GUPnPContext *key;
+ while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) {
+ if (context_filtered (context_filter, key)) {
+ // This context is now filtered
+ priv->filtered = g_list_prepend (priv->filtered, key);
+
+ // Drop all references to the objects we manage
+ g_hash_table_iter_replace (
+ &iter,
+ g_ptr_array_new_with_free_func (
+ g_object_unref));
+
+ // Synthesize unavailable signal
+ priv->syntesized_internal = TRUE;
+ g_object_set (G_OBJECT (key), "active", FALSE, NULL);
+ g_signal_emit (manager,
+ signals[CONTEXT_UNAVAILABLE],
+ 0,
+ key);
+ priv->syntesized_internal = FALSE;
+ }
+ }
}
static void
@@ -372,6 +374,13 @@ gupnp_context_manager_init (GUPnPContextManager *manager)
priv = gupnp_context_manager_get_instance_private (manager);
priv->context_filter = g_object_new (GUPNP_TYPE_CONTEXT_FILTER, NULL);
+ priv->contexts =
+ g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ g_object_unref,
+ (GDestroyNotify) g_ptr_array_unref);
+ priv->control_points = g_ptr_array_new ();
+ priv->root_devices = g_ptr_array_new ();
g_signal_connect_after (priv->context_filter,
"notify::entries",
@@ -465,9 +474,12 @@ gupnp_context_manager_dispose (GObject *object)
on_context_filter_change_cb,
NULL);
- g_list_free_full (priv->objects, g_object_unref);
- priv->objects = NULL;
- g_list_free_full (priv->filtered, g_object_unref);
+ g_hash_table_destroy (priv->contexts);
+
+ g_ptr_array_free (priv->control_points, TRUE);
+ g_ptr_array_free (priv->root_devices, TRUE);
+
+ g_list_free (priv->filtered);
priv->filtered = NULL;
g_clear_object (&filter);
@@ -719,23 +731,15 @@ gupnp_context_manager_create_full (GSSDPUDAVersion uda_version, GSocketFamily fa
void
gupnp_context_manager_rescan_control_points (GUPnPContextManager *manager)
{
- GList *l;
GUPnPContextManagerPrivate *priv;
g_return_if_fail (GUPNP_IS_CONTEXT_MANAGER (manager));
priv = gupnp_context_manager_get_instance_private (manager);
- l = priv->objects;
- while (l) {
- if (GUPNP_IS_CONTROL_POINT (l->data)) {
- GSSDPResourceBrowser *browser =
- GSSDP_RESOURCE_BROWSER (l->data);
- gssdp_resource_browser_rescan (browser);
- }
-
- l = l->next;
- }
+ g_ptr_array_foreach (priv->control_points,
+ (GFunc) gssdp_resource_browser_rescan,
+ NULL);
}
/**
@@ -775,8 +779,17 @@ gupnp_context_manager_manage_control_point (GUPnPContextManager *manager,
g_return_if_fail (GUPNP_IS_CONTROL_POINT (control_point));
priv = gupnp_context_manager_get_instance_private (manager);
- priv->objects = g_list_append (priv->objects,
- g_object_ref (control_point));
+
+ GUPnPContext *ctx = GUPNP_CONTEXT (gssdp_resource_browser_get_client (
+ GSSDP_RESOURCE_BROWSER (control_point)));
+
+ GPtrArray *objects = ensure_context (priv->contexts, ctx);
+
+ g_ptr_array_add (objects, g_object_ref (control_point));
+
+ g_object_weak_ref (G_OBJECT (control_point),
+ (GWeakNotify) g_ptr_array_remove_fast,
+ priv->control_points);
}
/**
@@ -818,8 +831,17 @@ gupnp_context_manager_manage_root_device (GUPnPContextManager *manager,
g_return_if_fail (GUPNP_IS_ROOT_DEVICE (root_device));
priv = gupnp_context_manager_get_instance_private (manager);
- priv->objects = g_list_append (priv->objects,
- g_object_ref (root_device));
+
+ GUPnPContext *ctx =
+ gupnp_device_info_get_context (GUPNP_DEVICE_INFO (root_device));
+
+ GPtrArray *objects = ensure_context (priv->contexts, ctx);
+
+ g_ptr_array_add (objects, g_object_ref (root_device));
+
+ g_object_weak_ref (G_OBJECT (root_device),
+ (GWeakNotify) g_ptr_array_remove_fast,
+ priv->root_devices);
}
/**
diff --git a/tests/test-context-manager.c b/tests/test-context-manager.c
index ac42c6a..c10cf65 100644
--- a/tests/test-context-manager.c
+++ b/tests/test-context-manager.c
@@ -35,7 +35,12 @@ test_context_manager_manage ()
GError *error = NULL;
GUPnPContext *ctx = gupnp_context_new ("lo", 0, &error);
- g_assert_null (error);
+ g_assert_no_error (error);
+ g_assert_nonnull (ctx);
+
+ GUPnPContext *ctx2 = gupnp_context_new ("lo", 0, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (ctx2);
GUPnPControlPoint *cp =
gupnp_control_point_new (ctx, "upnp::rootdevice");
@@ -53,6 +58,13 @@ test_context_manager_manage ()
// Check that the context manager has kept a reference on cp
g_assert_nonnull (weak);
+ // Annunce ctx2, which does not have a bound cp
+ g_signal_emit_by_name (cm, "context-unavailable", ctx2, NULL);
+
+ // Check that the context manager dropped the reference if the
+ // context is gone
+ g_assert_nonnull (weak);
+
g_signal_emit_by_name (cm, "context-unavailable", ctx, NULL);
// Check that the context manager dropped the reference if the
@@ -73,20 +85,48 @@ test_context_manager_manage ()
// Check that the context manager has kept a reference on cp
g_assert_nonnull (weak);
+ // Unannunce ctx2, which does not have a bound root device
+ g_signal_emit_by_name (cm, "context-unavailable", ctx2, NULL);
+
+ // Check that the context manager dropped the reference if the
+ // context is gone
+ g_assert_nonnull (weak);
+
g_signal_emit_by_name (cm, "context-unavailable", ctx, NULL);
// Check that the context manager dropped the reference if the
// context is gone
g_assert_null (weak);
+ // Check that tearing down the context manager tears down the managed devices
+ cp = gupnp_control_point_new (ctx, "upnp::rootdevice");
+ rd = gupnp_root_device_new (ctx, "TestDevice.xml", DATA_PATH, &error);
+ gpointer weak_cp = cp;
+ gpointer weak_rd = rd;
+
+ g_object_add_weak_pointer (G_OBJECT (cp), &weak_cp);
+ g_object_add_weak_pointer (G_OBJECT (rd), &weak_rd);
+
+ gupnp_context_manager_manage_control_point (GUPNP_CONTEXT_MANAGER (cm),
+ cp);
+ gupnp_context_manager_manage_root_device (GUPNP_CONTEXT_MANAGER (cm),
+ rd);
+ g_object_unref (cp);
+ g_object_unref (rd);
g_object_unref (cm);
+
+ g_assert_null (weak_cp);
+ g_assert_null (weak_rd);
+
g_object_unref (ctx);
+ g_object_unref (ctx2);
}
typedef struct _EnableDisableTestData {
GUPnPContext *expected_context;
- gboolean triggered;
+ gboolean available_triggered;
+ gboolean unavailable_triggered;
} EnableDisableTestData;
void
@@ -97,7 +137,18 @@ on_context_available (GUPnPContextManager *cm,
EnableDisableTestData *d = user_data;
g_assert (d->expected_context == ctx);
- d->triggered = TRUE;
+ d->available_triggered = TRUE;
+}
+
+void
+on_context_unavailable (GUPnPContextManager *cm,
+ GUPnPContext *ctx,
+ gpointer user_data)
+{
+ EnableDisableTestData *d = user_data;
+
+ g_assert (d->expected_context == ctx);
+ d->unavailable_triggered = TRUE;
}
void
@@ -119,10 +170,10 @@ test_context_manager_filter_enable_disable ()
NULL);
EnableDisableTestData context_available_triggered = {
.expected_context = ctx,
- .triggered = FALSE
+ .available_triggered = FALSE,
+ .unavailable_triggered = FALSE
};
-
const char *iface = gssdp_client_get_interface (GSSDP_CLIENT (ctx));
g_assert_no_error (error);
@@ -141,42 +192,172 @@ test_context_manager_filter_enable_disable ()
"context-available",
G_CALLBACK (on_context_available),
&context_available_triggered);
+ g_signal_connect (cm,
+ "context-unavailable",
+ G_CALLBACK (on_context_unavailable),
+ &context_available_triggered);
g_signal_emit_by_name (cm, "context-available", ctx, NULL);
- g_assert (context_available_triggered.triggered);
+ g_assert (context_available_triggered.available_triggered);
// Enable context filter. Since it is empty, we should still see the event
- context_available_triggered.triggered = FALSE;
+ context_available_triggered.available_triggered = FALSE;
gupnp_context_filter_set_enabled (cf, TRUE);
g_assert (gupnp_context_filter_get_enabled (cf));
g_signal_emit_by_name (cm, "context-available", ctx, NULL);
- g_assert (context_available_triggered.triggered);
+ g_assert (context_available_triggered.available_triggered);
// Enable context filter. Since it is empty, we should still see the event
- context_available_triggered.triggered = FALSE;
+ context_available_triggered.available_triggered = FALSE;
gupnp_context_filter_set_enabled (cf, TRUE);
g_assert (gupnp_context_filter_get_enabled (cf));
g_signal_emit_by_name (cm, "context-available", ctx, NULL);
- g_assert (context_available_triggered.triggered);
+ g_assert (context_available_triggered.available_triggered);
// Filter is enabled and not empty, we should get it since it matches
- context_available_triggered.triggered = FALSE;
+ context_available_triggered.available_triggered = FALSE;
g_assert (gupnp_context_filter_get_enabled (cf));
gupnp_context_filter_add_entry (cf, iface);
g_assert_false (gupnp_context_filter_is_empty (cf));
g_signal_emit_by_name (cm, "context-available", ctx, NULL);
- g_assert (context_available_triggered.triggered);
+ g_assert (context_available_triggered.available_triggered);
+
+ // Filter that does not match the triggered context, but is disabled
+ context_available_triggered.available_triggered = FALSE;
+ gupnp_context_filter_set_enabled (cf, FALSE);
+ gupnp_context_filter_clear (cf);
+
+ g_assert_false (gupnp_context_filter_get_enabled (cf));
+ gupnp_context_filter_add_entry (cf, "wl0ps2");
+ g_assert_false (gupnp_context_filter_is_empty (cf));
+
+ g_signal_emit_by_name (cm, "context-available", ctx, NULL);
+ g_assert (context_available_triggered.available_triggered);
+
+ // Now enable the filter. We should get the context-unavailable signal
+ context_available_triggered.available_triggered = FALSE;
+ gupnp_context_filter_set_enabled (cf, TRUE);
+ g_assert (context_available_triggered.unavailable_triggered);
+ g_assert_false (gssdp_client_get_active (GSSDP_CLIENT (ctx)));
+
+ // Now disable the filter. We should get the context-available signal
+ context_available_triggered.available_triggered = FALSE;
+ context_available_triggered.unavailable_triggered = FALSE;
+ gupnp_context_filter_set_enabled (cf, FALSE);
+ g_assert (context_available_triggered.available_triggered);
+ g_assert_false (context_available_triggered.unavailable_triggered);
+ g_assert (gssdp_client_get_active (GSSDP_CLIENT (ctx)));
g_clear_object (&ctx);
g_clear_error (&error);
}
+void
+test_context_manager_filter_add_remove ()
+{
+ GError *error = NULL;
+
+ GUPnPContext *ctx = g_initable_new (GUPNP_TYPE_CONTEXT,
+ NULL,
+ &error,
+ "host-ip",
+ "127.0.0.1",
+
+ "network",
+ "Free WiFi!",
+
+ "active",
+ FALSE,
+ NULL);
+ EnableDisableTestData context_available_triggered = {
+ .expected_context = ctx,
+ .available_triggered = FALSE,
+ .unavailable_triggered = FALSE
+ };
+
+ const char *iface = gssdp_client_get_interface (GSSDP_CLIENT (ctx));
+
+ g_assert_no_error (error);
+ g_assert_nonnull (ctx);
+
+ TestContextManager *cm =
+ g_object_new (test_context_manager_get_type (), NULL);
+
+ // Check that the context filter is off and empty by default
+ GUPnPContextFilter *cf = gupnp_context_manager_get_context_filter (
+ GUPNP_CONTEXT_MANAGER (cm));
+ g_assert_false (gupnp_context_filter_get_enabled (cf));
+ g_assert (gupnp_context_filter_is_empty (cf));
+
+ g_signal_emit_by_name (cm, "context-available", ctx, NULL);
+
+ g_signal_connect (cm,
+ "context-available",
+ G_CALLBACK (on_context_available),
+ &context_available_triggered);
+ g_signal_connect (cm,
+ "context-unavailable",
+ G_CALLBACK (on_context_unavailable),
+ &context_available_triggered);
+
+ gupnp_context_filter_set_enabled (cf, TRUE);
+ g_assert (gupnp_context_filter_get_enabled (cf));
+ g_assert_false (context_available_triggered.available_triggered);
+ g_assert_false (context_available_triggered.unavailable_triggered);
+
+ context_available_triggered.available_triggered = FALSE;
+ context_available_triggered.unavailable_triggered = FALSE;
+ gupnp_context_filter_add_entry (cf, "wl3ps3");
+ g_assert_false (gupnp_context_filter_is_empty (cf));
+ g_assert_false (context_available_triggered.available_triggered);
+ g_assert (context_available_triggered.unavailable_triggered);
+ g_assert_false (gssdp_client_get_active (GSSDP_CLIENT (ctx)));
+
+ // Adding the same entry should not trigger any event
+ context_available_triggered.available_triggered = FALSE;
+ context_available_triggered.unavailable_triggered = FALSE;
+ gupnp_context_filter_add_entry (cf, "wl3ps3");
+ g_assert_false (gupnp_context_filter_is_empty (cf));
+ g_assert_false (context_available_triggered.available_triggered);
+ g_assert_false (context_available_triggered.unavailable_triggered);
+
+ // Adding an entry that allows the known interface
+ context_available_triggered.available_triggered = FALSE;
+ context_available_triggered.unavailable_triggered = FALSE;
+ gupnp_context_filter_add_entry (cf, iface);
+ g_assert_false (gupnp_context_filter_is_empty (cf));
+ g_assert (context_available_triggered.available_triggered);
+ g_assert_false (context_available_triggered.unavailable_triggered);
+ g_assert (gssdp_client_get_active (GSSDP_CLIENT (ctx)));
+
+ // Check that the manager gives up managed objects if a context
+ // because it disappears from filtering
+ GUPnPControlPoint *cp =
+ gupnp_control_point_new (ctx, "upnp::rootdevice");
+ gpointer weak = cp;
+
+ g_object_add_weak_pointer (G_OBJECT (cp), &weak);
+
+ gupnp_context_manager_manage_control_point (GUPNP_CONTEXT_MANAGER (cm),
+ cp);
+ g_object_unref (cp);
+
+ context_available_triggered.available_triggered = FALSE;
+ context_available_triggered.unavailable_triggered = FALSE;
+ gupnp_context_filter_remove_entry (cf, iface);
+ g_assert_false (gupnp_context_filter_is_empty (cf));
+ g_assert_false (context_available_triggered.available_triggered);
+ g_assert (context_available_triggered.unavailable_triggered);
+ g_assert_false (gssdp_client_get_active (GSSDP_CLIENT (ctx)));
+ g_assert_null (weak);
+}
+
int
main (int argc, char *argv[])
{
@@ -184,8 +365,12 @@ main (int argc, char *argv[])
g_test_add_func ("/context-manager/manage",
test_context_manager_manage);
+
g_test_add_func ("/context_manager/filter/enable_disable",
test_context_manager_filter_enable_disable);
+ g_test_add_func ("/context_manager/filter/add_remove",
+ test_context_manager_filter_add_remove);
+
return g_test_run ();
}