summaryrefslogtreecommitdiff
path: root/tests/libedata-book/test-book-cache-utils.c
diff options
context:
space:
mode:
authorMilan Crha <mcrha@redhat.com>2017-05-17 15:32:39 +0200
committerMilan Crha <mcrha@redhat.com>2017-05-17 15:32:39 +0200
commit3abbcce2eadfef5a9de866b0aef43b8945b419b1 (patch)
treec71911cec3bbb864818b9fbaf0eee9e127bd85d0 /tests/libedata-book/test-book-cache-utils.c
parentdf820249c353fd3c28798340fc90cd783f410424 (diff)
downloadevolution-data-server-3abbcce2eadfef5a9de866b0aef43b8945b419b1.tar.gz
Merge offline-cache changes into master
Diffstat (limited to 'tests/libedata-book/test-book-cache-utils.c')
-rw-r--r--tests/libedata-book/test-book-cache-utils.c695
1 files changed, 695 insertions, 0 deletions
diff --git a/tests/libedata-book/test-book-cache-utils.c b/tests/libedata-book/test-book-cache-utils.c
new file mode 100644
index 000000000..5db16841e
--- /dev/null
+++ b/tests/libedata-book/test-book-cache-utils.c
@@ -0,0 +1,695 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2013, Openismus GmbH
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Tristan Van Berkom <tristanvb@openismus.com>
+ */
+
+#include "evolution-data-server-config.h"
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "test-book-cache-utils.h"
+
+gchar *
+tcu_new_vcard_from_test_case (const gchar *case_name)
+{
+ gchar *filename;
+ gchar *case_filename;
+ GFile * file;
+ GError *error = NULL;
+ gchar *vcard;
+
+ case_filename = g_strdup_printf ("%s.vcf", case_name);
+
+ /* In the case of installed tests, they run in ${pkglibexecdir}/installed-tests
+ * and the vcards are installed in ${pkglibexecdir}/installed-tests/vcards
+ */
+ if (g_getenv ("TEST_INSTALLED_SERVICES") != NULL)
+ filename = g_build_filename (INSTALLED_TEST_DIR, "vcards", case_filename, NULL);
+ else
+ filename = g_build_filename (SRCDIR, "..", "libebook", "data", "vcards", case_filename, NULL);
+
+ file = g_file_new_for_path (filename);
+ if (!g_file_load_contents (file, NULL, &vcard, NULL, NULL, &error))
+ g_error (
+ "failed to read test contact file '%s': %s",
+ filename, error->message);
+
+ g_free (case_filename);
+ g_free (filename);
+ g_object_unref (file);
+
+ return vcard;
+}
+
+EContact *
+tcu_new_contact_from_test_case (const gchar *case_name)
+{
+ gchar *vcard;
+ EContact *contact = NULL;
+
+ vcard = tcu_new_vcard_from_test_case (case_name);
+ if (vcard)
+ contact = e_contact_new_from_vcard (vcard);
+ g_free (vcard);
+
+ if (!contact)
+ g_error (
+ "failed to construct contact from test case '%s'",
+ case_name);
+
+ return contact;
+}
+
+void
+tcu_add_contact_from_test_case (TCUFixture *fixture,
+ const gchar *case_name,
+ EContact **ret_contact)
+{
+ EContact *contact;
+ GError *error = NULL;
+
+ contact = tcu_new_contact_from_test_case (case_name);
+
+ if (!e_book_cache_put_contact (fixture->book_cache, contact, case_name, E_CACHE_IS_ONLINE, NULL, &error))
+ g_error ("Failed to add contact: %s", error->message);
+
+ if (ret_contact)
+ *ret_contact = g_object_ref (contact);
+
+ g_clear_object (&contact);
+}
+
+static void
+delete_work_directory (const gchar *filename)
+{
+ /* XXX Instead of complex error checking here, we should ideally use
+ * a recursive GDir / g_unlink() function.
+ *
+ * We cannot use GFile and the recursive delete function without
+ * corrupting our contained D-Bus environment with service files
+ * from the OS.
+ */
+ const gchar *argv[] = { "/bin/rm", "-rf", filename, NULL };
+ gboolean spawn_succeeded;
+ gint exit_status;
+
+ spawn_succeeded = g_spawn_sync (
+ NULL, (gchar **) argv, NULL, 0, NULL, NULL,
+ NULL, NULL, &exit_status, NULL);
+
+ g_assert (spawn_succeeded);
+ #ifndef G_OS_WIN32
+ g_assert (WIFEXITED (exit_status));
+ g_assert_cmpint (WEXITSTATUS (exit_status), ==, 0);
+ #else
+ g_assert_cmpint (exit_status, ==, 0);
+ #endif
+}
+
+ESourceBackendSummarySetup *
+tcu_setup_empty_book (void)
+{
+ ESourceBackendSummarySetup *setup;
+ ESource *scratch;
+ GError *error = NULL;
+
+ scratch = e_source_new_with_uid ("test-source", NULL, &error);
+ if (!scratch)
+ g_error ("Error creating scratch source: %s", error ? error->message : "Unknown error");
+
+ /* This is a bit of a cheat */
+ setup = g_object_new (E_TYPE_SOURCE_BACKEND_SUMMARY_SETUP, "source", scratch, NULL);
+ e_source_backend_summary_setup_set_summary_fields (
+ setup,
+ /* We don't use this field in our tests anyway */
+ E_CONTACT_FILE_AS,
+ 0);
+
+ g_object_unref (scratch);
+
+ return setup;
+}
+
+static void
+e164_changed_cb (EBookCache *book_cache,
+ EContact *contact,
+ gboolean is_replace,
+ gpointer user_data)
+{
+ TCUFixture *fixture = user_data;
+
+ if (is_replace)
+ fixture->n_locale_changes++;
+ else
+ fixture->n_add_changes++;
+}
+
+void
+tcu_fixture_setup (TCUFixture *fixture,
+ gconstpointer user_data)
+{
+ TCUClosure *closure = (TCUClosure *) user_data;
+ ESourceBackendSummarySetup *setup = NULL;
+ gchar *filename, *directory;
+ GError *error = NULL;
+
+ if (!g_file_test (CAMEL_PROVIDERDIR, G_FILE_TEST_IS_DIR | G_FILE_TEST_EXISTS)) {
+ if (g_mkdir_with_parents (CAMEL_PROVIDERDIR, 0700) == -1)
+ g_warning ("%s: Failed to create folder '%s': %s\n", G_STRFUNC, CAMEL_PROVIDERDIR, g_strerror (errno));
+ }
+
+ /* Cleanup from last test */
+ directory = g_build_filename (g_get_tmp_dir (), "test-book-cache", NULL);
+ delete_work_directory (directory);
+ g_free (directory);
+ filename = g_build_filename (g_get_tmp_dir (), "test-book-cache", "cache.db", NULL);
+
+ if (closure->setup_summary)
+ setup = closure->setup_summary ();
+
+ fixture->book_cache = e_book_cache_new_full (filename, NULL, setup, NULL, &error);
+
+ g_clear_object (&setup);
+
+ if (!fixture->book_cache)
+ g_error ("Failed to create the EBookCache: %s", error->message);
+
+ g_free (filename);
+
+ g_signal_connect (fixture->book_cache, "e164-changed",
+ G_CALLBACK (e164_changed_cb), fixture);
+}
+
+void
+tcu_fixture_teardown (TCUFixture *fixture,
+ gconstpointer user_data)
+{
+ g_object_unref (fixture->book_cache);
+}
+
+void
+tcu_cursor_fixture_setup (TCUCursorFixture *fixture,
+ gconstpointer user_data)
+{
+ TCUFixture *base_fixture = (TCUFixture *) fixture;
+ TCUCursorClosure *data = (TCUCursorClosure *) user_data;
+ EContactField sort_fields[] = { E_CONTACT_FAMILY_NAME, E_CONTACT_GIVEN_NAME };
+ EBookCursorSortType sort_types[] = { data->sort_type, data->sort_type };
+ GSList *contacts = NULL;
+ GSList *extra_list = NULL;
+ GError *error = NULL;
+ gint ii;
+ gchar *sexp = NULL;
+
+ tcu_fixture_setup (base_fixture, user_data);
+
+ if (data->locale)
+ tcu_cursor_fixture_set_locale (fixture, data->locale);
+ else
+ tcu_cursor_fixture_set_locale (fixture, "en_US.UTF-8");
+
+ for (ii = 0; ii < N_SORTED_CONTACTS; ii++) {
+ gchar *case_name = g_strdup_printf ("sorted-%d", ii + 1);
+ gchar *vcard;
+ EContact *contact;
+
+ vcard = tcu_new_vcard_from_test_case (case_name);
+ contact = e_contact_new_from_vcard (vcard);
+ contacts = g_slist_prepend (contacts, contact);
+ extra_list = g_slist_prepend (extra_list, case_name);
+
+ g_free (vcard);
+
+ fixture->contacts[ii] = g_object_ref (contact);
+ }
+
+ if (!e_book_cache_put_contacts (base_fixture->book_cache, contacts, extra_list, E_CACHE_IS_ONLINE, NULL, &error)) {
+ /* Dont complain here, we re-use the same addressbook for multiple tests
+ * and we can't add the same contacts twice
+ */
+ if (g_error_matches (error, E_CACHE_ERROR, E_CACHE_ERROR_CONSTRAINT))
+ g_clear_error (&error);
+ else
+ g_error ("Failed to add test contacts: %s", error->message);
+ }
+
+ g_slist_free_full (contacts, g_object_unref);
+ g_slist_free_full (extra_list, g_free);
+
+ /* Allow a surrounding fixture setup to add a query here */
+ if (fixture->query) {
+ sexp = e_book_query_to_string (fixture->query);
+ e_book_query_unref (fixture->query);
+ fixture->query = NULL;
+ }
+
+ fixture->cursor = e_book_cache_cursor_new (
+ base_fixture->book_cache, sexp,
+ sort_fields, sort_types, 2, &error);
+
+ if (!fixture->cursor)
+ g_error ("Failed to create cursor: %s\n", error->message);
+
+ g_free (sexp);
+}
+
+void
+tcu_cursor_fixture_filtered_setup (TCUCursorFixture *fixture,
+ gconstpointer user_data)
+{
+ fixture->query = e_book_query_field_test (E_CONTACT_EMAIL, E_BOOK_QUERY_ENDS_WITH, ".com");
+
+ tcu_cursor_fixture_setup (fixture, user_data);
+}
+
+void
+tcu_cursor_fixture_teardown (TCUCursorFixture *fixture,
+ gconstpointer user_data)
+{
+ TCUFixture *base_fixture = (TCUFixture *) fixture;
+ gint ii;
+
+ for (ii = 0; ii < N_SORTED_CONTACTS; ii++) {
+ if (fixture->contacts[ii])
+ g_object_unref (fixture->contacts[ii]);
+ }
+
+ e_book_cache_cursor_free (base_fixture->book_cache, fixture->cursor);
+ tcu_fixture_teardown (base_fixture, user_data);
+}
+
+void
+tcu_cursor_fixture_set_locale (TCUCursorFixture *fixture,
+ const gchar *locale)
+{
+ TCUFixture *base_fixture = (TCUFixture *) fixture;
+ GError *error = NULL;
+
+ if (!e_book_cache_set_locale (base_fixture->book_cache, locale, NULL, &error))
+ g_error ("Failed to set locale: %s", error->message);
+}
+
+static gint
+find_contact_data (EBookCacheSearchData *data,
+ const gchar *uid)
+{
+ return g_strcmp0 (data->uid, uid);
+}
+
+void
+tcu_assert_contacts_order_slist (GSList *results,
+ GSList *uids)
+{
+ gint position = -1;
+ GSList *link, *l;
+
+ /* Assert that all passed UIDs are found in the
+ * results, and that those UIDs are in the
+ * specified order.
+ */
+ for (l = uids; l; l = l->next) {
+ const gchar *uid = l->data;
+ gint new_position;
+
+ link = g_slist_find_custom (results, uid, (GCompareFunc) find_contact_data);
+ if (!link)
+ g_error ("Specified uid '%s' was not found in results", uid);
+
+ new_position = g_slist_position (results, link);
+ g_assert_cmpint (new_position, >, position);
+ position = new_position;
+ }
+}
+
+void
+tcu_assert_contacts_order (GSList *results,
+ const gchar *first_uid,
+ ...)
+{
+ GSList *uids = NULL;
+ gchar *uid;
+ va_list args;
+
+ g_assert (first_uid);
+
+ uids = g_slist_append (uids, (gpointer) first_uid);
+
+ va_start (args, first_uid);
+ uid = va_arg (args, gchar *);
+ while (uid) {
+ uids = g_slist_append (uids, uid);
+ uid = va_arg (args, gchar *);
+ }
+ va_end (args);
+
+ tcu_assert_contacts_order_slist (results, uids);
+ g_slist_free (uids);
+}
+
+void
+tcu_print_results (const GSList *results)
+{
+ const GSList *link;
+
+ if (g_getenv ("TEST_DEBUG") == NULL)
+ return;
+
+ g_print ("\nPRINTING RESULTS:\n");
+
+ for (link = results; link; link = link->next) {
+ EBookCacheSearchData *data = link->data;
+
+ g_print ("\n%s\n", data->vcard);
+ }
+
+ g_print ("\nRESULT LIST_FINISHED\n");
+}
+
+/********************************************
+ * Move By Test Helpers
+ ********************************************/
+#define DEBUG_FIXTURE 0
+
+static TCUStepData *
+step_test_new_internal (const gchar *test_path,
+ const gchar *locale,
+ gboolean empty_book)
+{
+ TCUStepData *data;
+
+ data = g_slice_new0 (TCUStepData);
+
+ data->parent.locale = g_strdup (locale);
+ data->parent.sort_type = E_BOOK_CURSOR_SORT_ASCENDING;
+
+ if (empty_book)
+ data->parent.parent.setup_summary = tcu_setup_empty_book;
+
+ data->path = g_strdup (test_path);
+
+ return data;
+}
+
+static void
+step_test_free (TCUStepData *data)
+{
+ GList *l;
+
+ g_free (data->path);
+ g_free ((gchar *) data->parent.locale);
+
+ for (l = data->assertions; l; l = l->next) {
+ TCUStepAssertion *assertion = l->data;
+
+ g_free (assertion->locale);
+ g_slice_free (TCUStepAssertion, assertion);
+ }
+
+ g_list_free (data->assertions);
+
+ g_slice_free (TCUStepData, data);
+}
+
+TCUStepData *
+tcu_step_test_new (const gchar *test_prefix,
+ const gchar *test_path,
+ const gchar *locale,
+ gboolean empty_book)
+{
+ TCUStepData *data;
+ gchar *path;
+
+ path = g_strconcat (test_prefix, test_path, NULL);
+ data = step_test_new_internal (path, locale, empty_book);
+ g_free (path);
+
+ return data;
+}
+
+TCUStepData *
+tcu_step_test_new_full (const gchar *test_prefix,
+ const gchar *test_path,
+ const gchar *locale,
+ gboolean empty_book,
+ EBookCursorSortType sort_type)
+{
+ TCUStepData *data;
+ gchar *path;
+
+ path = g_strconcat (test_prefix, test_path, NULL);
+ data = step_test_new_internal (path, locale, empty_book);
+ data->parent.sort_type = sort_type;
+ g_free (path);
+
+ return data;
+}
+
+static void
+test_cursor_move_teardown (TCUCursorFixture *fixture,
+ gconstpointer user_data)
+{
+ TCUStepData *data = (TCUStepData *) user_data;
+
+ tcu_cursor_fixture_teardown (fixture, user_data);
+ step_test_free (data);
+}
+
+static void
+assert_step (TCUCursorFixture *fixture,
+ TCUStepData *data,
+ TCUStepAssertion *assertion,
+ GSList *results,
+ gint n_results,
+ gboolean expect_results)
+{
+ GSList *uids = NULL;
+ gint ii, expected = 0;
+
+ /* Count the number of really expected results */
+ for (ii = 0; ii < ABS (assertion->count); ii++) {
+ gint index = assertion->expected[ii];
+
+ if (index < 0)
+ break;
+
+ expected++;
+ }
+
+ g_assert_cmpint (n_results, ==, expected);
+ if (!expect_results) {
+ g_assert_cmpint (g_slist_length (results), ==, 0);
+ return;
+ }
+
+ /* Assert the exact amount of requested results */
+ g_assert_cmpint (g_slist_length (results), ==, expected);
+
+#if DEBUG_FIXTURE
+ g_print (
+ "%s: Constructing expected result list for a fetch of %d: ",
+ data->path, assertion->count);
+#endif
+ for (ii = 0; ii < ABS (assertion->count); ii++) {
+ gint index = assertion->expected[ii];
+ gchar *uid;
+
+ if (index < 0)
+ break;
+
+ uid = (gchar *) e_contact_get_const (fixture->contacts[index], E_CONTACT_UID);
+ uids = g_slist_append (uids, uid);
+
+#if DEBUG_FIXTURE
+ g_print ("%s ", uid);
+#endif
+
+ }
+#if DEBUG_FIXTURE
+ g_print ("\n");
+#endif
+
+ tcu_assert_contacts_order_slist (results, uids);
+ g_slist_free (uids);
+}
+
+static void
+test_step (TCUCursorFixture *fixture,
+ gconstpointer user_data)
+{
+ TCUFixture *base_fixture = (TCUFixture *) fixture;
+ TCUStepData *data = (TCUStepData *) user_data;
+ GSList *results = NULL;
+ GError *error = NULL;
+ gint n_results;
+ EBookCacheCursorOrigin origin;
+ GList *l;
+ gboolean reset = TRUE;
+
+ for (l = data->assertions; l; l = l->next) {
+ TCUStepAssertion *assertion = l->data;
+
+ if (assertion->locale) {
+ gint n_locale_changes = base_fixture->n_locale_changes;
+
+ if (!e_book_cache_set_locale (base_fixture->book_cache, assertion->locale, NULL, &error))
+ g_error ("Failed to set locale: %s", error->message);
+
+ n_locale_changes = (base_fixture->n_locale_changes - n_locale_changes);
+
+ /* Only check for contact changes is phone numbers are supported,
+ * contact changes only happen because of e164 number interpretations.
+ */
+ if (e_phone_number_is_supported () &&
+ assertion->count != n_locale_changes)
+ g_error ("Expected %d e164 numbers to change, %d actually changed.",
+ assertion->count, n_locale_changes);
+
+ reset = TRUE;
+ continue;
+ }
+
+ /* For the first call to e_book_cache_cursor_step(),
+ * or the first reset after locale change, set the origin accordingly.
+ */
+ if (reset) {
+ if (assertion->count < 0)
+ origin = E_BOOK_CACHE_CURSOR_ORIGIN_END;
+ else
+ origin = E_BOOK_CACHE_CURSOR_ORIGIN_BEGIN;
+
+ reset = FALSE;
+ } else {
+ origin = E_BOOK_CACHE_CURSOR_ORIGIN_CURRENT;
+ }
+
+ /* Try only fetching the contacts but not moving the cursor */
+ n_results = e_book_cache_cursor_step (
+ base_fixture->book_cache,
+ fixture->cursor,
+ E_BOOK_CACHE_CURSOR_STEP_FETCH,
+ origin,
+ assertion->count,
+ &results,
+ NULL, &error);
+ if (n_results < 0)
+ g_error ("Error fetching cursor results: %s", error->message);
+
+ tcu_print_results (results);
+ assert_step (fixture, data, assertion, results, n_results, TRUE);
+ g_slist_free_full (results, e_book_cache_search_data_free);
+ results = NULL;
+
+ /* Do it again, this time only moving the cursor */
+ n_results = e_book_cache_cursor_step (
+ base_fixture->book_cache,
+ fixture->cursor,
+ E_BOOK_CACHE_CURSOR_STEP_MOVE,
+ origin,
+ assertion->count,
+ &results,
+ NULL, &error);
+ if (n_results < 0)
+ g_error ("Error fetching cursor results: %s", error->message);
+
+ tcu_print_results (results);
+ assert_step (fixture, data, assertion, results, n_results, FALSE);
+ g_slist_free_full (results, e_book_cache_search_data_free);
+ results = NULL;
+ }
+}
+
+static void
+step_test_add_assertion_va_list (TCUStepData *data,
+ gint count,
+ va_list args)
+{
+ TCUStepAssertion *assertion = g_slice_new0 (TCUStepAssertion);
+ gint expected, ii = 0;
+
+ assertion->count = count;
+
+#if DEBUG_FIXTURE
+ g_print ("Adding assertion to test %d: %s\n", ii + 1, data->path);
+ g_print (" Test will move by %d and expect: ", count);
+#endif
+ for (ii = 0; ii < ABS (count); ii++) {
+ expected = va_arg (args, gint);
+
+#if DEBUG_FIXTURE
+ g_print ("%d ", expected);
+#endif
+ assertion->expected[ii] = expected - 1;
+ }
+#if DEBUG_FIXTURE
+ g_print ("\n");
+#endif
+
+ data->assertions = g_list_append (data->assertions, assertion);
+}
+
+/* A positive of negative 'count' value
+ * followed by ABS (count) UID indexes.
+ *
+ * The indexes start at 1 so that they
+ * are easier to match up with the chart
+ * in data-test-utils.h
+ */
+void
+tcu_step_test_add_assertion (TCUStepData *data,
+ gint count,
+ ...)
+{
+ va_list args;
+
+ va_start (args, count);
+ step_test_add_assertion_va_list (data, count, args);
+ va_end (args);
+}
+
+void
+tcu_step_test_change_locale (TCUStepData *data,
+ const gchar *locale,
+ gint expected_changes)
+{
+ TCUStepAssertion *assertion = g_slice_new0 (TCUStepAssertion);
+
+ assertion->locale = g_strdup (locale);
+ assertion->count = expected_changes;
+ data->assertions = g_list_append (data->assertions, assertion);
+}
+
+void
+tcu_step_test_add (TCUStepData *data,
+ gboolean filtered)
+{
+ data->filtered = filtered;
+
+ g_test_add (
+ data->path, TCUCursorFixture, data,
+ filtered ?
+ tcu_cursor_fixture_filtered_setup :
+ tcu_cursor_fixture_setup,
+ test_step,
+ test_cursor_move_teardown);
+}