1 #include <dazzle.h>
2 #include <glib/gstdio.h>
3 
4 static const gchar *layer1[] = { "a", "b", "c", "d", "e", "f", "g", "h", "i", NULL };
5 static const gchar *layer2[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", NULL };
6 
7 enum {
8   MODE_CREATE,
9   MODE_DELETE,
10 };
11 
12 typedef struct
13 {
14   GMainLoop               *main_loop;
15   DzlRecursiveFileMonitor *monitor;
16   GHashTable              *created;
17   GHashTable              *deleted;
18   GQueue                   dirs;
19   GList                   *iter;
20   guint                    mode : 1;
21   guint                    did_action : 1;
22 } BasicState;
23 
24 static gboolean
failed_timeout(gpointer state)25 failed_timeout (gpointer state)
26 {
27   /* timed out */
28   g_assert_not_reached ();
29   return G_SOURCE_REMOVE;
30 }
31 
32 G_GNUC_NULL_TERMINATED
33 static GFile *
file_new_build_filename(const gchar * first,...)34 file_new_build_filename (const gchar *first, ...)
35 {
36   g_autoptr(GPtrArray) parts = g_ptr_array_new ();
37   g_autofree gchar *path = NULL;
38   va_list args;
39 
40   va_start (args, first);
41   g_ptr_array_add (parts, (gchar *)first);
42   while ((first = va_arg (args, const gchar *)))
43     g_ptr_array_add (parts, (gchar *)first);
44   g_ptr_array_add (parts, NULL);
45 
46   path = g_build_filenamev ((gchar **)(gpointer)parts->pdata);
47 
48   return g_file_new_for_path (path);
49 }
50 
51 static gboolean
begin_test_basic(gpointer data)52 begin_test_basic (gpointer data)
53 {
54   g_autoptr(GError) error = NULL;
55   BasicState *state = data;
56   GFile *current;
57   gboolean r;
58 
59   g_assert (state != NULL);
60   g_assert (state->iter != NULL);
61   g_assert (state->iter->data != NULL);
62   g_assert (G_IS_FILE (state->iter->data));
63 
64   current = state->iter->data;
65 
66   if (state->mode == MODE_CREATE)
67     {
68       if (g_hash_table_contains (state->created, current))
69         {
70           state->iter = state->iter->next;
71           state->did_action = FALSE;
72 
73           if (state->iter == NULL)
74             {
75               state->mode = MODE_DELETE;
76               state->iter = state->dirs.tail;
77             }
78         }
79       else if (!state->did_action)
80         {
81           state->did_action = TRUE;
82           r = g_file_make_directory (current, NULL, &error);
83           g_assert_no_error (error);
84           g_assert_cmpint (r, ==, TRUE);
85         }
86     }
87   else if (state->mode == MODE_DELETE)
88     {
89       if (g_hash_table_contains (state->deleted, current))
90         {
91           state->iter = state->iter->prev;
92           state->did_action = FALSE;
93 
94           if (state->iter == NULL)
95             {
96               g_main_loop_quit (state->main_loop);
97               return G_SOURCE_REMOVE;
98             }
99         }
100       else if (!state->did_action)
101         {
102           state->did_action = TRUE;
103           g_file_delete (current, NULL, &error);
104           g_assert_no_error (error);
105         }
106     }
107   else
108     g_assert_not_reached ();
109 
110   return G_SOURCE_CONTINUE;
111 }
112 
113 static void
monitor_changed_cb(DzlRecursiveFileMonitor * monitor,GFile * file,GFile * other_file,GFileMonitorEvent event,gpointer data)114 monitor_changed_cb (DzlRecursiveFileMonitor *monitor,
115                     GFile                   *file,
116                     GFile                   *other_file,
117                     GFileMonitorEvent        event,
118                     gpointer                 data)
119 {
120   BasicState *state = data;
121 
122   if (event == G_FILE_MONITOR_EVENT_CREATED)
123     g_hash_table_insert (state->created, g_object_ref (file), NULL);
124   else if (event == G_FILE_MONITOR_EVENT_DELETED)
125     g_hash_table_insert (state->deleted, g_object_ref (file), NULL);
126 }
127 
128 static void
started_cb(GObject * object,GAsyncResult * result,gpointer user_data)129 started_cb (GObject      *object,
130             GAsyncResult *result,
131             gpointer      user_data)
132 {
133   DzlRecursiveFileMonitor *monitor = (DzlRecursiveFileMonitor *)object;
134   BasicState *state = user_data;
135   g_autoptr(GError) error = NULL;
136   gboolean r;
137 
138   g_assert (DZL_IS_RECURSIVE_FILE_MONITOR (monitor));
139 
140   r = dzl_recursive_file_monitor_start_finish (monitor, result, &error);
141   g_assert_no_error (error);
142   g_assert_cmpint (r, ==, TRUE);
143 
144   /*
145    * Now start the async test processing. We use a very
146    * low priority idle to ensure other things process before us.
147    */
148 
149   state->iter = state->dirs.head;
150   state->did_action = FALSE;
151   state->mode = MODE_CREATE;
152   g_idle_add_full (G_MAXINT, begin_test_basic, state, NULL);
153 }
154 
155 static void
test_basic(void)156 test_basic (void)
157 {
158   g_autoptr(GFile) dir = g_file_new_for_path ("recursive-dir");
159   BasicState state = { 0 };
160   gint r;
161 
162   state.main_loop = g_main_loop_new (NULL, FALSE);
163   g_queue_init (&state.dirs);
164   state.created = g_hash_table_new_full (g_file_hash,
165                                          (GEqualFunc) g_file_equal,
166                                          g_object_unref,
167                                          NULL);
168   state.deleted = g_hash_table_new_full (g_file_hash,
169                                          (GEqualFunc) g_file_equal,
170                                          g_object_unref,
171                                          NULL);
172 
173   /* Cleanup any previously failed run */
174   if (g_file_test ("recursive-dir", G_FILE_TEST_EXISTS))
175     {
176       g_autoptr(DzlDirectoryReaper) reaper = dzl_directory_reaper_new ();
177 
178       dzl_directory_reaper_add_directory (reaper, dir, 0);
179       dzl_directory_reaper_execute (reaper, NULL, NULL);
180 
181       r = g_rmdir ("recursive-dir");
182       g_assert_cmpint (r, ==, 0);
183     }
184 
185   /* Create our root directory to use */
186   r = g_mkdir ("recursive-dir", 0750);
187   g_assert_cmpint (r, ==, 0);
188 
189   /* Build our list of directories to create/test */
190   for (guint i = 0; layer1[i]; i++)
191     {
192       g_autoptr(GFile) file1 = file_new_build_filename ("recursive-dir", layer1[i], NULL);
193 
194       g_queue_push_tail (&state.dirs, g_object_ref (file1));
195 
196       for (guint j = 0; layer2[j]; j++)
197         {
198           g_autoptr(GFile) file2 = g_file_get_child (file1, layer2[j]);
199           g_queue_push_tail (&state.dirs, g_steal_pointer (&file2));
200         }
201     }
202 
203   state.monitor = dzl_recursive_file_monitor_new (dir);
204   g_signal_connect (state.monitor, "changed", G_CALLBACK (monitor_changed_cb), &state);
205 
206   /* Add a timeout to avoid infinite running */
207   g_timeout_add_seconds (3, failed_timeout, &state);
208 
209   dzl_recursive_file_monitor_start_async (state.monitor, NULL, started_cb, &state);
210 
211   g_main_loop_run (state.main_loop);
212 
213   dzl_recursive_file_monitor_cancel (state.monitor);
214 
215   g_clear_pointer (&state.main_loop, g_main_loop_unref);
216   g_clear_pointer (&state.created, g_hash_table_unref);
217   g_clear_pointer (&state.deleted, g_hash_table_unref);
218   g_queue_foreach (&state.dirs, (GFunc)g_object_unref, NULL);
219   g_queue_clear (&state.dirs);
220   g_clear_object (&state.monitor);
221 }
222 
223 gint
main(gint argc,gchar * argv[])224 main (gint argc,
225       gchar *argv[])
226 {
227   g_test_init (&argc, &argv, NULL);
228   g_test_add_func ("/Dazzle/RecursiveFileMonitor/basic", test_basic);
229   return g_test_run ();
230 }
231