Make ETestServerFixture more reliable.
[platform/upstream/evolution-data-server.git] / tests / test-server-utils / e-test-server-utils.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* e-test-server-utils.c - Test scaffolding to run tests with in-tree data server.
4  *
5  * Copyright (C) 2012 Intel Corporation
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) version 3.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with the program; if not, see <http://www.gnu.org/licenses/>
19  *
20  * Authors: Tristan Van Berkom <tristanvb@openismus.com>
21  */
22
23 #include "e-test-server-utils.h"
24
25 #define ADDRESS_BOOK_SOURCE_UID "test-address-book"
26 #define CALENDAR_SOURCE_UID     "test-calendar"
27
28 /* FIXME, currently we are unable to achieve server activation
29  * twice in a single test case, so we're using one D-Bus server
30  * throughout an entire test suite.
31  *
32  * When this is fixed we can migrate the D-Bus initialization
33  * and teardown from e_test_server_utils_run() to
34  * e_test_server_utils_setup() and e_test_server_utils_teardown()
35  * and this will transparantly change the way tests run using
36  * this test framework.
37  */
38 #define GLOBAL_DBUS_DAEMON 1
39
40 #if GLOBAL_DBUS_DAEMON
41 static GTestDBus *global_test_dbus = NULL;
42 #endif
43
44 typedef struct {
45         ETestServerFixture *fixture;
46         ETestServerClosure *closure;
47 } FixturePair;
48
49 static void
50 setup_environment (void)
51 {
52         g_assert (g_setenv ("XDG_DATA_HOME", EDS_TEST_WORK_DIR, TRUE));
53         g_assert (g_setenv ("XDG_CACHE_HOME", EDS_TEST_WORK_DIR, TRUE));
54         g_assert (g_setenv ("XDG_CONFIG_HOME", EDS_TEST_WORK_DIR, TRUE));
55         g_assert (g_setenv ("GSETTINGS_SCHEMA_DIR", EDS_TEST_SCHEMA_DIR, TRUE));
56         g_assert (g_setenv ("EDS_CALENDAR_MODULES", EDS_TEST_CALENDAR_DIR, TRUE));
57         g_assert (g_setenv ("EDS_ADDRESS_BOOK_MODULES", EDS_TEST_ADDRESS_BOOK_DIR, TRUE));
58 }
59
60 static void
61 delete_work_directory (void)
62 {
63         /* XXX Instead of complex error checking here, we should ideally use
64          * a recursive GDir / g_unlink() function.
65          *
66          * We cannot use GFile and the recursive delete function without
67          * corrupting our contained D-Bus environment with service files
68          * from the OS.
69          */
70         const gchar *argv[] = { "/bin/rm", "-rf", EDS_TEST_WORK_DIR, NULL };
71         gboolean spawn_succeeded;
72         gint exit_status;
73
74         spawn_succeeded = g_spawn_sync (
75                 NULL, (gchar **) argv, NULL, 0, NULL, NULL,
76                                         NULL, NULL, &exit_status, NULL);
77
78         g_assert (spawn_succeeded);
79         g_assert (WIFEXITED (exit_status));
80         g_assert_cmpint (WEXITSTATUS (exit_status), ==, 0);
81 }
82
83 static gboolean
84 e_test_server_utils_bootstrap_timeout (FixturePair *pair)
85 {
86         g_error ("Timed out while waiting for ESource creation from the registry");
87         return FALSE;
88 }
89
90 static void
91 e_test_server_utils_source_added (ESourceRegistry *registry,
92                                   ESource         *source,
93                                   FixturePair     *pair)
94 {
95         GError  *error = NULL;
96
97         switch (pair->closure->type) {
98         case E_TEST_SERVER_ADDRESS_BOOK:
99                 if (g_strcmp0 (e_source_get_uid (source), ADDRESS_BOOK_SOURCE_UID) != 0)
100                         return;
101
102                 pair->fixture->service.book_client = e_book_client_new (source, &error);
103                 if (!pair->fixture->service.book_client)
104                         g_error ("Unable to create the test book: %s", error->message);
105
106                 if (!e_client_open_sync (E_CLIENT (pair->fixture->service.book_client), FALSE, NULL, &error))
107                         g_error ("Unable to open book client: %s", error->message);
108
109                 break;
110
111         case E_TEST_SERVER_DEPRECATED_ADDRESS_BOOK:
112                 if (g_strcmp0 (e_source_get_uid (source), ADDRESS_BOOK_SOURCE_UID) != 0)
113                         return;
114
115                 pair->fixture->service.book = e_book_new (source, &error);
116                 if (!pair->fixture->service.book)
117                         g_error ("Unable to create the test book: %s", error->message);
118
119                 if (!e_book_open (pair->fixture->service.book, FALSE, &error))
120                         g_error ("Unable to open book: %s", error->message);
121
122                 break;
123
124         case E_TEST_SERVER_CALENDAR:
125                 if (g_strcmp0 (e_source_get_uid (source), CALENDAR_SOURCE_UID) != 0)
126                         return;
127
128                 pair->fixture->service.calendar_client = e_cal_client_new (source,
129                         pair->closure->calendar_source_type,
130                         &error);
131                 if (!pair->fixture->service.calendar_client)
132                         g_error ("Unable to create the test calendar: %s", error->message);
133
134                 if (!e_client_open_sync (E_CLIENT (pair->fixture->service.calendar_client), FALSE, NULL, &error))
135                         g_error ("Unable to open calendar client: %s", error->message);
136
137                 break;
138
139         case E_TEST_SERVER_DEPRECATED_CALENDAR:
140                 if (g_strcmp0 (e_source_get_uid (source), CALENDAR_SOURCE_UID) != 0)
141                         return;
142
143                 pair->fixture->service.calendar = e_cal_new (source, pair->closure->calendar_source_type);
144                 if (!pair->fixture->service.calendar)
145                         g_error ("Unable to create the test calendar");
146
147                 if (!e_cal_open (pair->fixture->service.calendar, FALSE, &error))
148                         g_error ("Unable to open calendar: %s", error->message);
149
150                 break;
151
152         case E_TEST_SERVER_NONE:
153                 return;
154         }
155
156         g_main_loop_quit (pair->fixture->loop);
157 }
158
159 static gboolean
160 e_test_server_utils_bootstrap_idle (FixturePair *pair)
161 {
162         ESourceBackend *backend = NULL;
163         ESource *scratch = NULL;
164         GError  *error = NULL;
165
166         pair->fixture->registry = e_source_registry_new_sync (NULL, &error);
167
168         if (!pair->fixture->registry)
169                 g_error ("Unable to create the test registry: %s", error->message);
170
171         g_signal_connect (pair->fixture->registry, "source-added",
172                           G_CALLBACK (e_test_server_utils_source_added), pair);
173
174         /* Create an address book */
175         switch (pair->closure->type) {
176         case E_TEST_SERVER_ADDRESS_BOOK:
177         case E_TEST_SERVER_DEPRECATED_ADDRESS_BOOK:
178
179                 scratch = e_source_new_with_uid (ADDRESS_BOOK_SOURCE_UID, NULL, &error);
180                 if (!scratch)
181                         g_error ("Failed to create scratch source for an addressbook: %s", error->message);
182
183                 /* Ensure Book type */
184                 backend = e_source_get_extension (scratch, E_SOURCE_EXTENSION_ADDRESS_BOOK);
185                 e_source_backend_set_backend_name (backend, "local");
186
187                 break;
188         case E_TEST_SERVER_CALENDAR:
189         case E_TEST_SERVER_DEPRECATED_CALENDAR:
190
191                 scratch = e_source_new_with_uid (CALENDAR_SOURCE_UID, NULL, &error);
192                 if (!scratch)
193                         g_error ("Failed to create scratch source for a calendar: %s", error->message);
194
195                 /* Ensure Calendar type source (how to specify the backend here ?? */
196                 backend = e_source_get_extension (scratch, E_SOURCE_EXTENSION_CALENDAR);
197                 e_source_backend_set_backend_name (backend, "local");
198
199                 break;
200
201         case E_TEST_SERVER_NONE:
202                 break;
203         }
204
205         if (scratch) {
206                 if (pair->closure->customize)
207                         pair->closure->customize (scratch, pair->closure);
208
209                 if (!e_source_registry_commit_source_sync (pair->fixture->registry, scratch, NULL, &error))
210                         g_error ("Unable to add new addressbook source to the registry: %s", error->message);
211
212                 g_object_unref (scratch);
213         }
214
215         if (pair->closure->type != E_TEST_SERVER_NONE)
216                 g_timeout_add (20 * 1000, (GSourceFunc) e_test_server_utils_bootstrap_timeout, pair);
217         else
218                 g_main_loop_quit (pair->fixture->loop);
219
220         return FALSE;
221 }
222
223 /**
224  * e_test_server_utils_setup:
225  * @fixture: A #ETestServerFixture
226  * @user_data: A #ETestServerClosure or derived structure provided by the test.
227  *
228  * A setup function for the #ETestServerFixture fixture
229  */
230 void
231 e_test_server_utils_setup (ETestServerFixture *fixture,
232                            gconstpointer user_data)
233 {
234         ETestServerClosure *closure = (ETestServerClosure *) user_data;
235         FixturePair         pair    = { fixture, closure };
236
237         /* Create work directory */
238         g_assert (g_mkdir_with_parents (EDS_TEST_WORK_DIR, 0755) == 0);
239
240         fixture->loop = g_main_loop_new (NULL, FALSE);
241
242 #if !GLOBAL_DBUS_DAEMON
243         /* Create the global dbus-daemon for this test suite */
244         fixture->dbus = g_test_dbus_new (G_TEST_DBUS_NONE);
245
246         /* Add the private directory with our in-tree service files */
247         g_test_dbus_add_service_dir (fixture->dbus, EDS_TEST_DBUS_SERVICE_DIR);
248
249         /* Start the private D-Bus daemon */
250         g_test_dbus_up (fixture->dbus);
251 #else
252         fixture->dbus = global_test_dbus;
253 #endif
254
255         g_idle_add ((GSourceFunc) e_test_server_utils_bootstrap_idle, &pair);
256         g_main_loop_run (fixture->loop);
257         g_signal_handlers_disconnect_by_func (fixture->registry, e_test_server_utils_source_added, &pair);
258 }
259
260 /**
261  * e_test_server_utils_teardown:
262  * @fixture: A #ETestServerFixture
263  * @user_data: A #ETestServerClosure or derived structure provided by the test.
264  *
265  * A teardown function for the #ETestServerFixture fixture
266  */
267 void
268 e_test_server_utils_teardown (ETestServerFixture *fixture,
269                               gconstpointer user_data)
270 {
271         ETestServerClosure *closure = (ETestServerClosure *) user_data;
272         GError             *error = NULL;
273
274         switch (closure->type) {
275         case E_TEST_SERVER_ADDRESS_BOOK:
276                 if (!e_client_remove_sync (E_CLIENT (fixture->service.book_client), NULL, &error)) {
277                         g_message ("Failed to remove test book: %s (ignoring)", error->message);
278                         g_clear_error (&error);
279                 }
280                 g_object_unref (fixture->service.book_client);
281                 fixture->service.book_client = NULL;
282                 break;
283
284         case E_TEST_SERVER_DEPRECATED_ADDRESS_BOOK:
285                 if (!e_book_remove (fixture->service.book, &error)) {
286                         g_message ("Failed to remove test book: %s (ignoring)", error->message);
287                         g_clear_error (&error);
288                 }
289                 g_object_unref (fixture->service.book);
290                 fixture->service.book = NULL;
291                 break;
292
293         case E_TEST_SERVER_CALENDAR:
294                 if (!e_client_remove_sync (E_CLIENT (fixture->service.calendar_client), NULL, &error)) {
295                         g_message ("Failed to remove test calendar: %s (ignoring)", error->message);
296                         g_clear_error (&error);
297                 }
298                 g_object_unref (fixture->service.calendar_client);
299                 fixture->service.calendar_client = NULL;
300                 break;
301
302         case E_TEST_SERVER_DEPRECATED_CALENDAR:
303                 if (!e_cal_remove (fixture->service.calendar, &error)) {
304                         g_message ("Failed to remove test calendar: %s (ignoring)", error->message);
305                         g_clear_error (&error);
306                 }
307                 g_object_unref (fixture->service.calendar);
308                 fixture->service.calendar = NULL;
309
310         case E_TEST_SERVER_NONE:
311                 break;
312         }
313
314         g_object_run_dispose (G_OBJECT (fixture->registry));
315         g_object_unref (fixture->registry);
316         fixture->registry = NULL;
317
318         g_main_loop_unref (fixture->loop);
319         fixture->loop = NULL;
320
321 #if !GLOBAL_DBUS_DAEMON
322         /* Teardown the D-Bus Daemon
323          *
324          * Note that we intentionally leak the TestDBus daemon
325          * in this case, presumably this is due to some leaked
326          * GDBusConnection reference counting
327          */
328         g_test_dbus_down (fixture->dbus);
329         g_object_unref (fixture->dbus);
330         fixture->dbus = NULL;
331 #else
332         fixture->dbus = NULL;
333 #endif
334
335         /* Cleanup work directory */
336         if (!closure->keep_work_directory)
337                 delete_work_directory ();
338 }
339
340 gint
341 e_test_server_utils_run (void)
342 {
343         gint tests_ret;
344
345         /* Cleanup work directory */
346         delete_work_directory ();
347
348         setup_environment ();
349
350 #if GLOBAL_DBUS_DAEMON
351
352         /* Create the global dbus-daemon for this test suite */
353         global_test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE);
354
355         /* Add the private directory with our in-tree service files */
356         g_test_dbus_add_service_dir (global_test_dbus, EDS_TEST_DBUS_SERVICE_DIR);
357
358         /* Start the private D-Bus daemon */
359         g_test_dbus_up (global_test_dbus);
360 #endif
361
362         /* Run the GTest suite */
363         tests_ret = g_test_run ();
364
365 #if GLOBAL_DBUS_DAEMON
366         /* Teardown the D-Bus Daemon
367          *
368          * Note that we intentionally leak the TestDBus daemon
369          * in this case, presumably this is due to some leaked
370          * GDBusConnection reference counting
371          */
372         g_test_dbus_stop (global_test_dbus);
373         /* g_object_unref (global_test_dbus); */
374         global_test_dbus = NULL;
375 #endif
376
377   return tests_ret;
378 }