e-test-server-utils: Add support for testing Direct Read Access books.
[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 gboolean
50 test_installed_services (void)
51 {
52         static gint use_installed_services = -1;
53
54         if (use_installed_services < 0) {
55                 if (g_getenv ("TEST_INSTALLED_SERVICES") != NULL)
56                         use_installed_services = 1;
57                 else
58                         use_installed_services = 0;
59         }
60         return use_installed_services;
61 }
62
63 static void
64 setup_environment (void)
65 {
66         g_assert (g_setenv ("XDG_DATA_HOME", EDS_TEST_WORK_DIR, TRUE));
67         g_assert (g_setenv ("XDG_CACHE_HOME", EDS_TEST_WORK_DIR, TRUE));
68         g_assert (g_setenv ("XDG_CONFIG_HOME", EDS_TEST_WORK_DIR, TRUE));
69         g_assert (g_setenv ("GSETTINGS_SCHEMA_DIR", EDS_TEST_SCHEMA_DIR, TRUE));
70         g_assert (g_setenv ("EDS_CALENDAR_MODULES", EDS_TEST_CALENDAR_DIR, TRUE));
71         g_assert (g_setenv ("EDS_ADDRESS_BOOK_MODULES", EDS_TEST_ADDRESS_BOOK_DIR, TRUE));
72         g_assert (g_setenv ("GIO_USE_VFS", "local", TRUE));
73 }
74
75 static void
76 delete_work_directory (void)
77 {
78         /* XXX Instead of complex error checking here, we should ideally use
79          * a recursive GDir / g_unlink() function.
80          *
81          * We cannot use GFile and the recursive delete function without
82          * corrupting our contained D-Bus environment with service files
83          * from the OS.
84          */
85         const gchar *argv[] = { "/bin/rm", "-rf", EDS_TEST_WORK_DIR, NULL };
86         gboolean spawn_succeeded;
87         gint exit_status;
88
89         spawn_succeeded = g_spawn_sync (
90                 NULL, (gchar **) argv, NULL, 0, NULL, NULL,
91                                         NULL, NULL, &exit_status, NULL);
92
93         g_assert (spawn_succeeded);
94         g_assert (WIFEXITED (exit_status));
95         g_assert_cmpint (WEXITSTATUS (exit_status), ==, 0);
96 }
97
98 static gboolean
99 e_test_server_utils_bootstrap_timeout (FixturePair *pair)
100 {
101         g_error ("Timed out while waiting for ESource creation from the registry");
102
103         pair->fixture->timeout_source_id = 0;
104         return FALSE;
105 }
106
107 static void
108 e_test_server_utils_source_added (ESourceRegistry *registry,
109                                   ESource         *source,
110                                   FixturePair     *pair)
111 {
112         GError  *error = NULL;
113
114         switch (pair->closure->type) {
115         case E_TEST_SERVER_ADDRESS_BOOK:
116         case E_TEST_SERVER_DIRECT_ADDRESS_BOOK:
117                 if (g_strcmp0 (e_source_get_uid (source), ADDRESS_BOOK_SOURCE_UID) != 0)
118                         return;
119
120                 if (pair->closure->type == E_TEST_SERVER_DIRECT_ADDRESS_BOOK)
121                         pair->fixture->service.book_client = (EBookClient *)
122                                 e_book_client_connect_direct_sync (pair->fixture->registry, source, NULL, &error);
123                 else
124                         pair->fixture->service.book_client = (EBookClient *)
125                                 e_book_client_connect_sync (source, NULL, &error);
126
127                 if (!pair->fixture->service.book_client)
128                         g_error ("Unable to create the test book: %s", error->message);
129
130                 break;
131
132         case E_TEST_SERVER_DEPRECATED_ADDRESS_BOOK:
133                 if (g_strcmp0 (e_source_get_uid (source), ADDRESS_BOOK_SOURCE_UID) != 0)
134                         return;
135
136                 pair->fixture->service.book = e_book_new (source, &error);
137                 if (!pair->fixture->service.book)
138                         g_error ("Unable to create the test book: %s", error->message);
139
140                 if (!e_book_open (pair->fixture->service.book, FALSE, &error))
141                         g_error ("Unable to open book: %s", error->message);
142
143                 break;
144
145         case E_TEST_SERVER_CALENDAR:
146                 if (g_strcmp0 (e_source_get_uid (source), CALENDAR_SOURCE_UID) != 0)
147                         return;
148
149                 pair->fixture->service.calendar_client = (ECalClient *)
150                         e_cal_client_connect_sync (source,
151                                                    pair->closure->calendar_source_type, NULL, &error);
152                 if (!pair->fixture->service.calendar_client)
153                         g_error ("Unable to create the test calendar: %s", error->message);
154
155                 break;
156
157         case E_TEST_SERVER_DEPRECATED_CALENDAR:
158                 if (g_strcmp0 (e_source_get_uid (source), CALENDAR_SOURCE_UID) != 0)
159                         return;
160
161                 pair->fixture->service.calendar = e_cal_new (source, pair->closure->calendar_source_type);
162                 if (!pair->fixture->service.calendar)
163                         g_error ("Unable to create the test calendar");
164
165                 if (!e_cal_open (pair->fixture->service.calendar, FALSE, &error))
166                         g_error ("Unable to open calendar: %s", error->message);
167
168                 break;
169
170         case E_TEST_SERVER_NONE:
171                 return;
172         }
173
174         g_main_loop_quit (pair->fixture->loop);
175 }
176
177 static gboolean
178 e_test_server_utils_bootstrap_idle (FixturePair *pair)
179 {
180         ESourceBackend *backend = NULL;
181         ESource *scratch = NULL;
182         GError  *error = NULL;
183
184         pair->fixture->registry = e_source_registry_new_sync (NULL, &error);
185
186         if (!pair->fixture->registry)
187                 g_error ("Unable to create the test registry: %s", error->message);
188
189         g_signal_connect (pair->fixture->registry, "source-added",
190                           G_CALLBACK (e_test_server_utils_source_added), pair);
191
192         /* Create an address book */
193         switch (pair->closure->type) {
194         case E_TEST_SERVER_ADDRESS_BOOK:
195         case E_TEST_SERVER_DIRECT_ADDRESS_BOOK:
196         case E_TEST_SERVER_DEPRECATED_ADDRESS_BOOK:
197
198                 scratch = e_source_new_with_uid (ADDRESS_BOOK_SOURCE_UID, NULL, &error);
199                 if (!scratch)
200                         g_error ("Failed to create scratch source for an addressbook: %s", error->message);
201
202                 /* Ensure Book type */
203                 backend = e_source_get_extension (scratch, E_SOURCE_EXTENSION_ADDRESS_BOOK);
204                 e_source_backend_set_backend_name (backend, "local");
205
206                 break;
207         case E_TEST_SERVER_CALENDAR:
208         case E_TEST_SERVER_DEPRECATED_CALENDAR:
209
210                 scratch = e_source_new_with_uid (CALENDAR_SOURCE_UID, NULL, &error);
211                 if (!scratch)
212                         g_error ("Failed to create scratch source for a calendar: %s", error->message);
213
214                 /* Ensure Calendar type source (how to specify the backend here ?? */
215                 backend = e_source_get_extension (scratch, E_SOURCE_EXTENSION_CALENDAR);
216                 e_source_backend_set_backend_name (backend, "local");
217
218                 break;
219
220         case E_TEST_SERVER_NONE:
221                 break;
222         }
223
224         if (scratch) {
225                 if (pair->closure->customize)
226                         pair->closure->customize (scratch, pair->closure);
227
228                 if (!e_source_registry_commit_source_sync (pair->fixture->registry, scratch, NULL, &error))
229                         g_error ("Unable to add new addressbook source to the registry: %s", error->message);
230
231                 g_object_unref (scratch);
232         }
233
234         if (pair->closure->type != E_TEST_SERVER_NONE)
235                 pair->fixture->timeout_source_id =
236                         g_timeout_add (20 * 1000, (GSourceFunc) e_test_server_utils_bootstrap_timeout, pair);
237         else
238                 g_main_loop_quit (pair->fixture->loop);
239
240         return FALSE;
241 }
242
243 /**
244  * e_test_server_utils_setup:
245  * @fixture: A #ETestServerFixture
246  * @user_data: A #ETestServerClosure or derived structure provided by the test.
247  *
248  * A setup function for the #ETestServerFixture fixture
249  */
250 void
251 e_test_server_utils_setup (ETestServerFixture *fixture,
252                            gconstpointer user_data)
253 {
254         ETestServerClosure *closure = (ETestServerClosure *) user_data;
255         FixturePair         pair    = { fixture, closure };
256
257         /* Create work directory */
258         g_assert (g_mkdir_with_parents (EDS_TEST_WORK_DIR, 0755) == 0);
259
260         fixture->loop = g_main_loop_new (NULL, FALSE);
261
262         if (!test_installed_services ()) {
263 #if !GLOBAL_DBUS_DAEMON
264                 /* Create the global dbus-daemon for this test suite */
265                 fixture->dbus = g_test_dbus_new (G_TEST_DBUS_NONE);
266
267                 /* Add the private directory with our in-tree service files */
268                 g_test_dbus_add_service_dir (fixture->dbus, EDS_TEST_DBUS_SERVICE_DIR);
269
270                 /* Start the private D-Bus daemon */
271                 g_test_dbus_up (fixture->dbus);
272 #else
273                 fixture->dbus = global_test_dbus;
274 #endif
275         }
276
277         g_idle_add ((GSourceFunc) e_test_server_utils_bootstrap_idle, &pair);
278         g_main_loop_run (fixture->loop);
279
280         /* This needs to be explicitly removed, otherwise the timeout source
281          * stays in the default GMainContext and after running tests for 20 seconds
282          * in the same test suite... the tests bail out.
283          */
284         if (fixture->timeout_source_id) {
285                 g_source_remove (fixture->timeout_source_id);
286                 fixture->timeout_source_id = 0;
287         }
288
289         g_signal_handlers_disconnect_by_func (fixture->registry, e_test_server_utils_source_added, &pair);
290 }
291
292 /**
293  * e_test_server_utils_teardown:
294  * @fixture: A #ETestServerFixture
295  * @user_data: A #ETestServerClosure or derived structure provided by the test.
296  *
297  * A teardown function for the #ETestServerFixture fixture
298  */
299 void
300 e_test_server_utils_teardown (ETestServerFixture *fixture,
301                               gconstpointer user_data)
302 {
303         ETestServerClosure *closure = (ETestServerClosure *) user_data;
304         GError             *error = NULL;
305
306         switch (closure->type) {
307         case E_TEST_SERVER_ADDRESS_BOOK:
308         case E_TEST_SERVER_DIRECT_ADDRESS_BOOK:
309                 if (!e_client_remove_sync (E_CLIENT (fixture->service.book_client), NULL, &error)) {
310                         g_message ("Failed to remove test book: %s (ignoring)", error->message);
311                         g_clear_error (&error);
312                 }
313                 g_object_unref (fixture->service.book_client);
314                 fixture->service.book_client = NULL;
315                 break;
316
317         case E_TEST_SERVER_DEPRECATED_ADDRESS_BOOK:
318                 if (!e_book_remove (fixture->service.book, &error)) {
319                         g_message ("Failed to remove test book: %s (ignoring)", error->message);
320                         g_clear_error (&error);
321                 }
322                 g_object_unref (fixture->service.book);
323                 fixture->service.book = NULL;
324                 break;
325
326         case E_TEST_SERVER_CALENDAR:
327                 if (!e_client_remove_sync (E_CLIENT (fixture->service.calendar_client), NULL, &error)) {
328                         g_message ("Failed to remove test calendar: %s (ignoring)", error->message);
329                         g_clear_error (&error);
330                 }
331                 g_object_unref (fixture->service.calendar_client);
332                 fixture->service.calendar_client = NULL;
333                 break;
334
335         case E_TEST_SERVER_DEPRECATED_CALENDAR:
336                 if (!e_cal_remove (fixture->service.calendar, &error)) {
337                         g_message ("Failed to remove test calendar: %s (ignoring)", error->message);
338                         g_clear_error (&error);
339                 }
340                 g_object_unref (fixture->service.calendar);
341                 fixture->service.calendar = NULL;
342
343         case E_TEST_SERVER_NONE:
344                 break;
345         }
346
347         g_object_run_dispose (G_OBJECT (fixture->registry));
348         g_object_unref (fixture->registry);
349         fixture->registry = NULL;
350
351         g_main_loop_unref (fixture->loop);
352         fixture->loop = NULL;
353
354         if (!test_installed_services ()) {
355 #if !GLOBAL_DBUS_DAEMON
356                 /* Teardown the D-Bus Daemon
357                  *
358                  * Note that we intentionally leak the TestDBus daemon
359                  * in this case, presumably this is due to some leaked
360                  * GDBusConnection reference counting
361                  */
362                 g_test_dbus_down (fixture->dbus);
363                 g_object_unref (fixture->dbus);
364                 fixture->dbus = NULL;
365 #else
366                 fixture->dbus = NULL;
367 #endif
368         }
369
370         /* Cleanup work directory */
371         if (!closure->keep_work_directory && !test_installed_services ())
372                 delete_work_directory ();
373
374         /* Destroy dynamically allocated closure */
375         if (closure->destroy_closure_func)
376                 closure->destroy_closure_func (closure);
377 }
378
379 gint
380 e_test_server_utils_run (void)
381 {
382         gint tests_ret;
383
384         /* Cleanup work directory */
385         delete_work_directory ();
386
387         setup_environment ();
388
389 #if GLOBAL_DBUS_DAEMON
390         if (!test_installed_services ()) {
391                 /* Create the global dbus-daemon for this test suite */
392                 global_test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE);
393
394                 /* Add the private directory with our in-tree service files */
395                 g_test_dbus_add_service_dir (global_test_dbus, EDS_TEST_DBUS_SERVICE_DIR);
396
397                 /* Start the private D-Bus daemon */
398                 g_test_dbus_up (global_test_dbus);
399         }
400 #endif
401
402         /* Run the GTest suite */
403         tests_ret = g_test_run ();
404
405 #if GLOBAL_DBUS_DAEMON
406         if (!test_installed_services ()) {
407                 /* Teardown the D-Bus Daemon
408                  *
409                  * Note that we intentionally leak the TestDBus daemon
410                  * in this case, presumably this is due to some leaked
411                  * GDBusConnection reference counting
412                  */
413                 g_test_dbus_stop (global_test_dbus);
414                 /* g_object_unref (global_test_dbus); */
415                 global_test_dbus = NULL;
416         }
417 #endif
418
419         return tests_ret;
420 }