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