diff options
author | David Zeuthen <davidz@redhat.com> | 2010-10-04 12:00:38 -0400 |
---|---|---|
committer | David Zeuthen <davidz@redhat.com> | 2010-10-04 12:00:38 -0400 |
commit | 7620487167d851324cadce8775c4e70baa46d3f3 (patch) | |
tree | 05a7d6de0efe6ef9357bea77c2590cdf6ac9a247 | |
parent | 2693adb92a660c78f5935cc79b6e9d5c0308dc51 (diff) |
Monitor configuration files and emit added, remove and changed signals
Signed-off-by: David Zeuthen <davidz@redhat.com>
-rw-r--r-- | stc/stcitem.c | 110 | ||||
-rw-r--r-- | stc/stcmonitor.c | 285 | ||||
-rw-r--r-- | stc/stcprivate.h | 7 | ||||
-rw-r--r-- | stc/test.c | 290 | ||||
-rw-r--r-- | stc/testdata/semi_valid_conf/stc.conf.d/10.conf (renamed from stc/testdata/semi_valid_conf/stc.d/10.conf) | 0 | ||||
-rw-r--r-- | stc/testdata/semi_valid_conf/stc.conf.d/50.conf (renamed from stc/testdata/semi_valid_conf/stc.d/50.conf) | 0 | ||||
-rw-r--r-- | stc/testdata/semi_valid_conf/stc.conf.d/50.conf.wrong-extension (renamed from stc/testdata/semi_valid_conf/stc.d/50.conf.wrong-extension) | 0 | ||||
-rw-r--r-- | stc/testdata/semi_valid_conf/stc.conf.d/90.conf (renamed from stc/testdata/semi_valid_conf/stc.d/90.conf) | 0 | ||||
-rw-r--r-- | stc/testdata/semi_valid_conf/stc.conf.d/91.conf (renamed from stc/testdata/semi_valid_conf/stc.d/91.conf) | 0 |
9 files changed, 645 insertions, 47 deletions
diff --git a/stc/stcitem.c b/stc/stcitem.c index f8fa01e..a736429 100644 --- a/stc/stcitem.c +++ b/stc/stcitem.c @@ -56,6 +56,9 @@ struct _StcItem /* memoized */ gchar **option_keys; + + /* private */ + gchar *sort_key; }; typedef struct _StcItemClass StcItemClass; @@ -63,8 +66,17 @@ typedef struct _StcItemClass StcItemClass; struct _StcItemClass { GObjectClass parent_class; + void (*changed) (StcItem *item); +}; + +enum +{ + CHANGED_SIGNAL, + LAST_SIGNAL }; +static guint signals[LAST_SIGNAL] = { 0 }; + G_DEFINE_TYPE (StcItem, stc_item, G_TYPE_OBJECT); static void @@ -78,6 +90,8 @@ stc_item_finalize (GObject *object) g_hash_table_unref (item->options); g_free (item->option_keys); + g_free (item->sort_key); + if (G_OBJECT_CLASS (stc_item_parent_class)->finalize) G_OBJECT_CLASS (stc_item_parent_class)->finalize (object); } @@ -94,6 +108,22 @@ stc_item_class_init (StcItemClass *klass) gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = stc_item_finalize; + + /** + * StcItem::changed: + * @item: The item that changed. + * + * Emitted when an item changes. + */ + signals[CHANGED_SIGNAL] = g_signal_new ("changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (StcItemClass, changed), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 0); } /** @@ -160,7 +190,8 @@ _stc_item_new (StcItemType type, const gchar *id, const gchar *target, const gchar *nick_name, - GHashTable *options) + GHashTable *options, + const gchar *sort_key) { StcItem *item; @@ -171,10 +202,87 @@ _stc_item_new (StcItemType type, item->nick_name = g_strdup (nick_name); item->type = type; item->options = g_hash_table_ref (options); + item->sort_key = g_strdup (sort_key); return item; } +static gboolean +hash_tables_equal (GHashTable *a, GHashTable *b) +{ + gboolean ret; + GHashTableIter iter; + const gchar *key; + const gchar *value; + const gchar *other_value; + + ret = FALSE; + + g_hash_table_iter_init (&iter, a); + while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) + { + other_value = g_hash_table_lookup (b, key); + if (g_strcmp0 (value, other_value) != 0) + goto out; + } + + g_hash_table_iter_init (&iter, b); + while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) + { + other_value = g_hash_table_lookup (a, key); + if (g_strcmp0 (value, other_value) != 0) + goto out; + } + + ret = TRUE; + + out: + return ret; +} + +gboolean +_stc_item_update (StcItem *item, StcItem *other) +{ + gboolean changed; + + g_return_val_if_fail (STC_IS_ITEM (item), FALSE); + g_return_val_if_fail (STC_IS_ITEM (other), FALSE); + + changed = FALSE; + + g_assert_cmpstr (item->id, ==, other->id); + + if (g_strcmp0 (item->target, other->target) != 0) + { + g_free (item->target); + item->target = g_strdup (other->target); + changed = TRUE; + } + + if (g_strcmp0 (item->nick_name, other->nick_name) != 0) + { + g_free (item->nick_name); + item->nick_name = g_strdup (other->nick_name); + changed = TRUE; + } + + if (!hash_tables_equal (item->options, other->options)) + { + g_hash_table_unref (item->options); + item->options = g_hash_table_ref (other->options); + changed = TRUE; + } + + return changed; +} + +const gchar * +_stc_item_get_sort_key (StcItem *item) +{ + g_return_val_if_fail (STC_IS_ITEM (item), NULL); + return item->sort_key; +} + const gchar* const * stc_item_get_option_keys (StcItem *item) { diff --git a/stc/stcmonitor.c b/stc/stcmonitor.c index 8f0cc70..8fc5bf6 100644 --- a/stc/stcmonitor.c +++ b/stc/stcmonitor.c @@ -58,6 +58,9 @@ struct _StcMonitor GList *items; GHashTable *map_id_to_item; + + GFileMonitor *conf_file_monitor; + GFileMonitor *conf_dir_monitor; }; typedef struct _StcMonitorClass StcMonitorClass; @@ -66,10 +69,12 @@ struct _StcMonitorClass { GObjectClass parent_class; - void (*error) (StcMonitor *monitor, - const gchar *filename, - gint line_no, - GError **error); + void (*item_added) (StcMonitor *monitor, + StcItem *item); + void (*item_removed) (StcMonitor *monitor, + StcItem *item); + void (*item_changed) (StcMonitor *monitor, + StcItem *item); }; enum @@ -80,7 +85,29 @@ enum PROP_ERROR_HANDLER_USER_DATA }; -static GList *stc_monitor_load_all_files (StcMonitor *monitor); +enum +{ + ITEM_ADDED_SIGNAL, + ITEM_REMOVED_SIGNAL, + ITEM_CHANGED_SIGNAL, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void stc_monitor_update (StcMonitor *monitor); + +static void on_conf_file_monitor_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data); + +static void on_conf_dir_monitor_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data); static void stc_monitor_emit_gerror (StcMonitor *monitor, @@ -104,6 +131,16 @@ stc_monitor_finalize (GObject *object) if (monitor->context != NULL) g_main_context_unref (monitor->context); + if (monitor->conf_file_monitor != NULL) + { + g_signal_handlers_disconnect_by_func (monitor->conf_file_monitor, G_CALLBACK (on_conf_file_monitor_changed), monitor); + g_object_unref (monitor->conf_file_monitor); + } + if (monitor->conf_dir_monitor != NULL) + { + g_signal_handlers_disconnect_by_func (monitor->conf_dir_monitor, G_CALLBACK (on_conf_dir_monitor_changed), monitor); + g_object_unref (monitor->conf_dir_monitor); + } g_free (monitor->config_dir); g_list_foreach (monitor->items, (GFunc) g_object_unref, NULL); g_list_free (monitor->items); @@ -181,27 +218,57 @@ stc_monitor_init (StcMonitor *monitor) monitor->map_id_to_item = g_hash_table_new (g_str_hash, g_str_equal); } - static void stc_monitor_constructed (GObject *object) { StcMonitor *monitor = STC_MONITOR (object); - GList *loaded_items; - GList *l; + GFile *file; + GError *error; + gchar *path; g_return_if_fail (STC_IS_MONITOR (monitor)); - loaded_items = stc_monitor_load_all_files (monitor); - - for (l = loaded_items; l != NULL; l = l->next) + path = g_strdup_printf ("%s/stc.conf", monitor->config_dir); + file = g_file_new_for_path (path); + error = NULL; + monitor->conf_file_monitor = g_file_monitor_file (file, G_FILE_MONITOR_SEND_MOVED, NULL, &error); + g_file_monitor_set_rate_limit (monitor->conf_file_monitor, 50); + if (monitor->conf_file_monitor == NULL) { - StcItem *item = STC_ITEM (l->data); - /* TODO: check if we have item already - if so, update existing object */ - monitor->items = g_list_append (monitor->items, g_object_ref (item)); - g_hash_table_insert (monitor->map_id_to_item, (gpointer) stc_item_get_id (item), item); + stc_monitor_emit_gerror (monitor, + path, + -1, + error); + g_error_free (error); } - g_list_foreach (loaded_items, (GFunc) g_object_unref, NULL); - g_list_free (loaded_items); + g_signal_connect (monitor->conf_file_monitor, + "changed", + G_CALLBACK (on_conf_file_monitor_changed), + monitor); + g_object_unref (file); + g_free (path); + + path = g_strdup_printf ("%s/stc.conf.d", monitor->config_dir); + file = g_file_new_for_path (path); + error = NULL; + monitor->conf_dir_monitor = g_file_monitor_directory (file, G_FILE_MONITOR_SEND_MOVED, NULL, &error); + g_file_monitor_set_rate_limit (monitor->conf_dir_monitor, 50); + if (monitor->conf_dir_monitor == NULL) + { + stc_monitor_emit_gerror (monitor, + path, + -1, + error); + g_error_free (error); + } + g_signal_connect (monitor->conf_dir_monitor, + "changed", + G_CALLBACK (on_conf_dir_monitor_changed), + monitor); + g_object_unref (file); + g_free (path); + + stc_monitor_update (monitor); if (G_OBJECT_CLASS (stc_monitor_parent_class)->constructed) G_OBJECT_CLASS (stc_monitor_parent_class)->constructed (object); @@ -263,6 +330,61 @@ stc_monitor_class_init (StcMonitorClass *klass) G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + + /** + * StcMonitor::item-added: + * @monitor: The #StcMonitor emitting the signal. + * @item: The item that was added. + * + * Emitted when an item is added. + */ + signals[ITEM_ADDED_SIGNAL] = g_signal_new ("item-added", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (StcMonitorClass, item_added), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + STC_TYPE_ITEM); + + /** + * StcMonitor::item-removed: + * @monitor: The #StcMonitor emitting the signal. + * @item: The item that was removed. + * + * Emitted when an item is removed. + */ + signals[ITEM_REMOVED_SIGNAL] = g_signal_new ("item-removed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (StcMonitorClass, item_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + STC_TYPE_ITEM); + + /** + * StcMonitor::item-changed: + * @monitor: The #StcMonitor emitting the signal. + * @item: The item that changed. + * + * Emitted when an item changes. + */ + signals[ITEM_CHANGED_SIGNAL] = g_signal_new ("item-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (StcMonitorClass, item_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + STC_TYPE_ITEM); } /** @@ -422,6 +544,7 @@ stc_monitor_load_one_file (StcMonitor *monitor, const gchar *item_id; gchar *target; gchar *nick_name; + gchar *sort_key; gchar *s; gchar **options_strv; StcItem *item; @@ -430,6 +553,7 @@ stc_monitor_load_one_file (StcMonitor *monitor, tokens = NULL; target = NULL; nick_name = NULL; + sort_key = NULL; options = NULL; tokens = g_strsplit (group, " ", 0); @@ -534,13 +658,15 @@ stc_monitor_load_one_file (StcMonitor *monitor, /* NickName is optional */ nick_name = g_key_file_get_string (key_file, group, "NickName", NULL); - item = _stc_item_new (item_type, item_id, target, nick_name, options); + sort_key = g_strdup_printf ("%s:%s", path, item_id); + item = _stc_item_new (item_type, item_id, target, nick_name, options, sort_key); ret = g_list_prepend (ret, item); process_next_item: g_strfreev (tokens); g_free (target); g_free (nick_name); + g_free (sort_key); if (options != NULL) g_hash_table_unref (options); } @@ -619,7 +745,7 @@ stc_monitor_load_all_files (StcMonitor *monitor) paths = g_ptr_array_new (); g_ptr_array_add (paths, g_strdup_printf ("%s/stc.conf", monitor->config_dir)); - dir_path = g_strdup_printf ("%s/stc.d", monitor->config_dir); + dir_path = g_strdup_printf ("%s/stc.conf.d", monitor->config_dir); error = NULL; dir = g_dir_open (dir_path, 0, &error); if (error != NULL) @@ -683,6 +809,127 @@ stc_monitor_load_all_files (StcMonitor *monitor) return ret; } +static gint +_item_cmp_func (gconstpointer a, + gconstpointer b) +{ + StcItem *ia; + StcItem *ib; + + ia = STC_ITEM ((gpointer) a); + ib = STC_ITEM ((gpointer) b); + + return g_strcmp0 (_stc_item_get_sort_key (ia), _stc_item_get_sort_key (ib)); +} + +static void +stc_monitor_update (StcMonitor *monitor) +{ + GList *loaded_items; + GList *l; + GList *ll; + GList *items_changed; + GList *items_added; + GList *items_removed; + GHashTable *loaded_items_ids; + + items_changed = NULL; + items_added = NULL; + items_removed = NULL; + + loaded_items_ids = g_hash_table_new (g_str_hash, g_str_equal); + + /* add/change items */ + loaded_items = stc_monitor_load_all_files (monitor); + for (l = loaded_items; l != NULL; l = l->next) + { + StcItem *item = STC_ITEM (l->data); + StcItem *existing_item; + + existing_item = g_hash_table_lookup (monitor->map_id_to_item, stc_item_get_id (item)); + if (existing_item != NULL) + { + if (_stc_item_update (existing_item, item)) + { + items_changed = g_list_prepend (items_changed, g_object_ref (existing_item)); + } + } + else + { + monitor->items = g_list_append (monitor->items, g_object_ref (item)); + g_hash_table_insert (monitor->map_id_to_item, (gpointer) stc_item_get_id (item), item); + items_added = g_list_prepend (items_added, g_object_ref (item)); + } + + g_hash_table_insert (loaded_items_ids, (gpointer) stc_item_get_id (item), GUINT_TO_POINTER (1)); + } + /* remove items that wasn't loaded this time around */ + for (l = monitor->items; l != NULL; l = ll) + { + StcItem *item = STC_ITEM (l->data); + ll = l->next; + if (g_hash_table_lookup (loaded_items_ids, stc_item_get_id (item)) == NULL) + { + /* item was removed */ + items_removed = g_list_prepend (items_removed, g_object_ref (item)); + monitor->items = g_list_delete_link (monitor->items, l); + g_object_unref (item); + } + } + g_hash_table_unref (loaded_items_ids); + g_list_foreach (loaded_items, (GFunc) g_object_unref, NULL); + g_list_free (loaded_items); + + /* make the items appear in the order they were declared */ + monitor->items = g_list_sort (monitor->items, _item_cmp_func); + + /* emit signals only when our monitor object has been completely updated */ + for (l = items_removed; l != NULL; l = l->next) + { + StcItem *item = STC_ITEM (l->data); + g_signal_emit (monitor, signals[ITEM_REMOVED_SIGNAL], 0, item); + g_object_unref (item); + } + g_list_free (items_removed); + for (l = items_added; l != NULL; l = l->next) + { + StcItem *item = STC_ITEM (l->data); + g_signal_emit (monitor, signals[ITEM_ADDED_SIGNAL], 0, item); + g_object_unref (item); + } + g_list_free (items_added); + for (l = items_changed; l != NULL; l = l->next) + { + StcItem *item = STC_ITEM (l->data); + g_signal_emit (monitor, signals[ITEM_CHANGED_SIGNAL], 0, item); + g_signal_emit_by_name (item, "changed"); + g_object_unref (item); + } + g_list_free (items_changed); +} + +static void +on_conf_file_monitor_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + /* TODO: this is a bit expensive since all config files are reloaded */ + stc_monitor_update (STC_MONITOR (user_data)); +} + +static void +on_conf_dir_monitor_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + /* TODO: this is a bit expensive since all config files are reloaded */ + stc_monitor_update (STC_MONITOR (user_data)); +} + static void stc_monitor_emit_gerror (StcMonitor *monitor, const gchar *filename, diff --git a/stc/stcprivate.h b/stc/stcprivate.h index f7325cb..f56021b 100644 --- a/stc/stcprivate.h +++ b/stc/stcprivate.h @@ -36,7 +36,12 @@ StcItem *_stc_item_new (StcItemType type, const gchar *id, const gchar *target, const gchar *nick_name, - GHashTable *options); + GHashTable *options, + const gchar *sort_key); + +const gchar *_stc_item_get_sort_key (StcItem *item); + +gboolean _stc_item_update (StcItem *item, StcItem *other); G_END_DECLS @@ -20,8 +20,15 @@ #include "config.h" +#include <stdlib.h> +#include <string.h> + +#include <glib/gstdio.h> + #include "stc.h" +static GMainLoop *loop = NULL; + /* ---------------------------------------------------------------------------------------------------- */ static const gchar * @@ -134,8 +141,9 @@ test_stc_no_conf (void) monitor = stc_monitor_new_for_config_dir (SRCDIR "/nonexistant", on_error_append_str, str); g_assert_cmpstr (stc_monitor_get_config_dir (monitor), ==, SRCDIR "/nonexistant"); g_object_unref (monitor); + g_assert_cmpstr (str->str, ==, - SRCDIR "/nonexistant/stc.d: Error opening directory '" SRCDIR "/nonexistant/stc.d': No such file or directory (g-file-error-quark, 4)\n" + SRCDIR "/nonexistant/stc.conf.d: Error opening directory '" SRCDIR "/nonexistant/stc.conf.d': No such file or directory (g-file-error-quark, 4)\n" SRCDIR "/nonexistant/stc.conf: No such file or directory (g-file-error-quark, 4)\n" ); g_string_free (str, TRUE); @@ -159,7 +167,7 @@ test_stc_semi_valid_conf (void) SRCDIR "/testdata/semi_valid_conf/stc.conf: Invalid item identifer `with-dash' in group `Filesystem with-dash'. Only characters in [a-zA-Z0-9_] are supported. (stc-error-quark, 1)\n" SRCDIR "/testdata/semi_valid_conf/stc.conf: Error parsing group name `Filesystem with space' (stc-error-quark, 1)\n" SRCDIR "/testdata/semi_valid_conf/stc.conf: Element 0 of Options, `foo', for item foo of type MDRaid is malformed (no equal sign found). (stc-error-quark, 1)\n" - SRCDIR "/testdata/semi_valid_conf/stc.d/91.conf: Encountered item with duplicate id `multiple_instances_same_id'. Ignoring previous items. (stc-error-quark, 2)\n" + SRCDIR "/testdata/semi_valid_conf/stc.conf.d/91.conf: Encountered item with duplicate id `multiple_instances_same_id'. Ignoring previous items. (stc-error-quark, 2)\n" ); str2 = g_string_new (NULL); items = stc_monitor_get_items (monitor); @@ -169,6 +177,30 @@ test_stc_semi_valid_conf (void) item_append_str (item, str2); } g_assert_cmpstr (str2->str, ==, + "id `in_pri_10'\n" + "target `Device=/dev/sda'\n" + "nick-name `(none)'\n" + "type filesystem\n" + "options (none)\n" + "\n" + "id `in_pri_50'\n" + "target `Device=/dev/sda'\n" + "nick-name `(none)'\n" + "type filesystem\n" + "options (none)\n" + "\n" + "id `in_pri_90'\n" + "target `Device=/dev/sda'\n" + "nick-name `(none)'\n" + "type filesystem\n" + "options (none)\n" + "\n" + "id `multiple_instances_same_id'\n" + "target `Device=/dev/multiple_instances_same_id/second'\n" + "nick-name `(none)'\n" + "type filesystem\n" + "options (none)\n" + "\n" "id `BigStorage'\n" "target `Device=/dev/disk/md-uuid-01234:56789'\n" "nick-name `BigStorage'\n" @@ -193,30 +225,6 @@ test_stc_semi_valid_conf (void) "nick-name `My Secret Stuff (Encrypted)'\n" "type luks\n" "options LUKS:password -> `xyz123'\n" - "\n" - "id `in_pri_10'\n" - "target `Device=/dev/sda'\n" - "nick-name `(none)'\n" - "type filesystem\n" - "options (none)\n" - "\n" - "id `in_pri_50'\n" - "target `Device=/dev/sda'\n" - "nick-name `(none)'\n" - "type filesystem\n" - "options (none)\n" - "\n" - "id `in_pri_90'\n" - "target `Device=/dev/sda'\n" - "nick-name `(none)'\n" - "type filesystem\n" - "options (none)\n" - "\n" - "id `multiple_instances_same_id'\n" - "target `Device=/dev/multiple_instances_same_id/second'\n" - "nick-name `(none)'\n" - "type filesystem\n" - "options (none)\n" "\n"); g_string_free (str2, TRUE); @@ -226,6 +234,231 @@ test_stc_semi_valid_conf (void) /* ---------------------------------------------------------------------------------------------------- */ +static void +cleanup_dir (const gchar *dirname) +{ + gchar *command; + + g_assert (g_str_has_prefix (dirname, "/tmp/stc-monitoring-test-")); + g_assert (strstr (dirname, "\"") == NULL); + command = g_strdup_printf ("rm -rf \"%s\"", dirname); + g_assert_cmpint (system (command), ==, 0); + g_free (command); +} + +static gboolean +on_timeout (gpointer user_data) +{ + g_assert_not_reached (); + return FALSE; +} + +typedef enum +{ + _EVENT_ADDED, + _EVENT_REMOVED, + _EVENT_CHANGED +} _Event; + +static void +check_state (StcMonitor *monitor, + StcItem *item, + gint state, + _Event event) +{ + switch (state) + { + case 0: + g_assert_cmpstr (stc_item_get_id (item), ==, "item1"); + g_assert_cmpint (event, ==, _EVENT_ADDED); + break; + + case 1: + g_assert_cmpstr (stc_item_get_id (item), ==, "item2"); + g_assert_cmpint (event, ==, _EVENT_ADDED); + g_assert_cmpstr (stc_item_get_target (item), ==, "Device=/dev/sdc"); + break; + + case 2: + g_assert_cmpstr (stc_item_get_id (item), ==, "item1"); + g_assert_cmpint (event, ==, _EVENT_REMOVED); + break; + + case 3: + g_assert_cmpstr (stc_item_get_id (item), ==, "item2"); + g_assert_cmpint (event, ==, _EVENT_CHANGED); + g_assert_cmpstr (stc_item_get_target (item), ==, "Device=/dev/sda"); + break; + + case 4: + g_assert_cmpstr (stc_item_get_id (item), ==, "item2"); + g_assert_cmpint (event, ==, _EVENT_REMOVED); + break; + + case 5: + g_assert_cmpstr (stc_item_get_id (item), ==, "item3"); + g_assert_cmpint (event, ==, _EVENT_ADDED); + break; + + default: + g_assert_not_reached (); + break; + } +} + + +static void +on_item_added (StcMonitor *monitor, + StcItem *item, + gpointer user_data) +{ + gint *state = user_data; + check_state (monitor, item, *state, _EVENT_ADDED); + *state += 1; + g_main_loop_quit (loop); +} + +static void +on_item_removed (StcMonitor *monitor, + StcItem *item, + gpointer user_data) +{ + gint *state = user_data; + check_state (monitor, item, *state, _EVENT_REMOVED); + *state += 1; + g_main_loop_quit (loop); +} + +static void +on_item_changed (StcMonitor *monitor, + StcItem *item, + gpointer user_data) +{ + gint *state = user_data; + check_state (monitor, item, *state, _EVENT_CHANGED); + *state += 1; + g_main_loop_quit (loop); +} + +static void +test_stc_monitoring (void) +{ + gchar *dirname; + StcMonitor *monitor; + GError *error; + gboolean rc; + gchar *filename; + guint timeout_id; + gint state; + + dirname = g_strdup ("/tmp/stc-monitoring-test-XXXXXX"); + if (mkdtemp (dirname) == 0) + { + g_warning ("Error creating temp dir with template %s: %m", dirname); + g_assert_not_reached (); + } + + monitor = stc_monitor_new_for_config_dir (dirname, NULL, NULL); + g_assert_cmpstr (stc_monitor_get_config_dir (monitor), ==, dirname); + + /* main timeout */ + timeout_id = g_timeout_add_seconds (30, on_timeout, NULL); + + state = 0; + g_signal_connect (monitor, + "item-added", + G_CALLBACK (on_item_added), + &state); + g_signal_connect (monitor, + "item-removed", + G_CALLBACK (on_item_removed), + &state); + g_signal_connect (monitor, + "item-changed", + G_CALLBACK (on_item_changed), + &state); + + /* 0: check we get the item-added signal */ + filename = g_strdup_printf ("%s/stc.conf", dirname); + error = NULL; + rc = g_file_set_contents (filename, + "[Filesystem item1]\n" + "Device=/dev/sdb\n", + -1, + &error); + g_assert_no_error (error); + g_assert (rc); + g_free (filename); + if (state <= 0) + g_main_loop_run (loop); + + /* 1: check we get the item-added signal (again) */ + filename = g_strdup_printf ("%s/stc.conf", dirname); + error = NULL; + rc = g_file_set_contents (filename, + "[Filesystem item1]\n" + "Device=/dev/sdb\n" + "\n" + "[Filesystem item2]\n" + "Device=/dev/sdc\n", + -1, + &error); + g_assert_no_error (error); + g_assert (rc); + g_free (filename); + if (state <= 1) + g_main_loop_run (loop); + + /* 2: check we get the item-removed signal */ + filename = g_strdup_printf ("%s/stc.conf", dirname); + error = NULL; + rc = g_file_set_contents (filename, + "[Filesystem item2]\n" + "Device=/dev/sdc\n", + -1, + &error); + g_assert_no_error (error); + g_assert (rc); + g_free (filename); + if (state <= 2) + g_main_loop_run (loop); + + /* 3: check we get the item-changed signal */ + filename = g_strdup_printf ("%s/stc.conf", dirname); + error = NULL; + rc = g_file_set_contents (filename, + "[Filesystem item2]\n" + "Device=/dev/sda\n", + -1, + &error); + g_assert_no_error (error); + g_assert (rc); + g_free (filename); + if (state <= 3) + g_main_loop_run (loop); + + /* 4+5: will give item-removed then item-added */ + filename = g_strdup_printf ("%s/stc.conf", dirname); + error = NULL; + rc = g_file_set_contents (filename, + "[Filesystem item3]\n" + "Device=/dev/sda\n", + -1, + &error); + g_assert_no_error (error); + g_assert (rc); + g_free (filename); + if (state <= 5) + g_main_loop_run (loop); + + g_object_unref (monitor); + cleanup_dir (dirname); + + g_source_remove (timeout_id); +} + +/* ---------------------------------------------------------------------------------------------------- */ + int main (int argc, char **argv) @@ -235,11 +468,16 @@ main (int argc, g_type_init (); g_test_init (&argc, &argv, NULL); + loop = g_main_loop_new (NULL, FALSE); + g_test_add_func ("/stc/basic", test_stc_basic); g_test_add_func ("/stc/no_conf", test_stc_no_conf); g_test_add_func ("/stc/semi_valid_conf", test_stc_semi_valid_conf); + g_test_add_func ("/stc/monitoring", test_stc_monitoring); ret = g_test_run(); + g_main_loop_unref (loop); + return ret; } diff --git a/stc/testdata/semi_valid_conf/stc.d/10.conf b/stc/testdata/semi_valid_conf/stc.conf.d/10.conf index 73b0c95..73b0c95 100644 --- a/stc/testdata/semi_valid_conf/stc.d/10.conf +++ b/stc/testdata/semi_valid_conf/stc.conf.d/10.conf diff --git a/stc/testdata/semi_valid_conf/stc.d/50.conf b/stc/testdata/semi_valid_conf/stc.conf.d/50.conf index bad4e88..bad4e88 100644 --- a/stc/testdata/semi_valid_conf/stc.d/50.conf +++ b/stc/testdata/semi_valid_conf/stc.conf.d/50.conf diff --git a/stc/testdata/semi_valid_conf/stc.d/50.conf.wrong-extension b/stc/testdata/semi_valid_conf/stc.conf.d/50.conf.wrong-extension index fae6617..fae6617 100644 --- a/stc/testdata/semi_valid_conf/stc.d/50.conf.wrong-extension +++ b/stc/testdata/semi_valid_conf/stc.conf.d/50.conf.wrong-extension diff --git a/stc/testdata/semi_valid_conf/stc.d/90.conf b/stc/testdata/semi_valid_conf/stc.conf.d/90.conf index 15a3de1..15a3de1 100644 --- a/stc/testdata/semi_valid_conf/stc.d/90.conf +++ b/stc/testdata/semi_valid_conf/stc.conf.d/90.conf diff --git a/stc/testdata/semi_valid_conf/stc.d/91.conf b/stc/testdata/semi_valid_conf/stc.conf.d/91.conf index a97db0f..a97db0f 100644 --- a/stc/testdata/semi_valid_conf/stc.d/91.conf +++ b/stc/testdata/semi_valid_conf/stc.conf.d/91.conf |