[evolution-data-server/wip/offline-cache] Add some initial tests for EBookMetaBackend



commit 741202177045319eb0330b18e3a7f0df4c287fa5
Author: Milan Crha <mcrha redhat com>
Date:   Wed May 10 20:01:01 2017 +0200

    Add some initial tests for EBookMetaBackend

 src/addressbook/libebook-contacts/e-vcard.c        |    5 +
 .../libedata-book/e-book-meta-backend.c            |   19 +-
 .../libedata-book/e-book-meta-backend.h            |    4 +-
 tests/libebook/data/vcards/custom-4.vcf            |    2 +-
 tests/libebook/data/vcards/logo-1.vcf              |   21 +
 tests/libebook/data/vcards/photo-1.vcf             |   59 +
 tests/libedata-book/CMakeLists.txt                 |    1 +
 tests/libedata-book/test-book-meta-backend.c       | 1110 ++++++++++++++++++++
 tests/libedata-cal/test-cal-meta-backend.c         |    3 +-
 9 files changed, 1213 insertions(+), 11 deletions(-)
---
diff --git a/src/addressbook/libebook-contacts/e-vcard.c b/src/addressbook/libebook-contacts/e-vcard.c
index b969e41..c3b9493 100644
--- a/src/addressbook/libebook-contacts/e-vcard.c
+++ b/src/addressbook/libebook-contacts/e-vcard.c
@@ -2034,6 +2034,11 @@ e_vcard_attribute_remove_param (EVCardAttribute *attr,
                param = l->data;
                if (g_ascii_strcasecmp (e_vcard_attribute_param_get_name (param),
                                        param_name) == 0) {
+                       if (g_ascii_strcasecmp (param_name, EVC_ENCODING) == 0) {
+                               attr->encoding_set = FALSE;
+                               attr->encoding = EVC_ENCODING_RAW;
+                       }
+
                        attr->params = g_list_delete_link (attr->params, l);
                        e_vcard_attribute_param_free (param);
                        break;
diff --git a/src/addressbook/libedata-book/e-book-meta-backend.c 
b/src/addressbook/libedata-book/e-book-meta-backend.c
index bac3b4a..ea79af8 100644
--- a/src/addressbook/libedata-book/e-book-meta-backend.c
+++ b/src/addressbook/libedata-book/e-book-meta-backend.c
@@ -114,7 +114,7 @@ static gboolean ebmb_save_contact_wrapper_sync (EBookMetaBackend *meta_backend,
                                                EBookCache *book_cache,
                                                gboolean overwrite_existing,
                                                EConflictResolution conflict_resolution,
-                                               const EContact *in_contact,
+                                               /* const */ EContact *in_contact,
                                                const gchar *extra,
                                                const gchar *orig_uid,
                                                gboolean *out_requires_put,
@@ -1219,7 +1219,7 @@ ebmb_save_contact_wrapper_sync (EBookMetaBackend *meta_backend,
                                EBookCache *book_cache,
                                gboolean overwrite_existing,
                                EConflictResolution conflict_resolution,
-                               const EContact *in_contact,
+                               /* const */ EContact *in_contact,
                                const gchar *extra,
                                const gchar *orig_uid,
                                gboolean *out_requires_put,
@@ -1238,7 +1238,7 @@ ebmb_save_contact_wrapper_sync (EBookMetaBackend *meta_backend,
        if (out_new_uid)
                *out_new_uid = NULL;
 
-       contact = e_contact_duplicate ((EContact *) in_contact);
+       contact = e_contact_duplicate (in_contact);
 
        success = e_book_meta_backend_inline_local_photos_sync (meta_backend, contact, cancellable, error);
 
@@ -2897,7 +2897,7 @@ e_book_meta_backend_inline_local_photos_sync (EBookMetaBackend *meta_backend,
 
                values = e_vcard_attribute_get_param (attr, EVC_VALUE);
                if (values && g_ascii_strcasecmp (values->data, "uri") == 0) {
-                       const gchar *url;
+                       gchar *url;
 
                        url = e_vcard_attribute_get_value (attr);
                        if (url && g_str_has_prefix (url, LOCAL_PREFIX)) {
@@ -2937,6 +2937,8 @@ e_book_meta_backend_inline_local_photos_sync (EBookMetaBackend *meta_backend,
                                g_object_unref (file);
                                g_free (basename);
                        }
+
+                       g_free (url);
                }
        }
 
@@ -2951,7 +2953,7 @@ ebmb_create_photo_local_filename (EBookMetaBackend *meta_backend,
                                  const gchar *type)
 {
        EBookCache *book_cache;
-       gchar *local_filename, *cache_path, *checksum, *prefix, *extension;
+       gchar *local_filename, *cache_path, *checksum, *prefix, *extension, *filename;
 
        g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), NULL);
        g_return_val_if_fail (uid != NULL, NULL);
@@ -2969,13 +2971,16 @@ ebmb_create_photo_local_filename (EBookMetaBackend *meta_backend,
        else
                extension = NULL;
 
-       local_filename = g_build_filename (cache_path, prefix, extension ? "." : NULL, extension, NULL);
+       filename = g_strconcat (prefix, extension ? "." : NULL, extension, NULL);
+
+       local_filename = g_build_filename (cache_path, filename, NULL);
 
        g_object_unref (book_cache);
        g_free (cache_path);
        g_free (checksum);
        g_free (prefix);
        g_free (extension);
+       g_free (filename);
 
        return local_filename;
 }
@@ -3475,7 +3480,7 @@ gboolean
 e_book_meta_backend_save_contact_sync (EBookMetaBackend *meta_backend,
                                       gboolean overwrite_existing,
                                       EConflictResolution conflict_resolution,
-                                      const EContact *contact,
+                                      /* const */ EContact *contact,
                                       const gchar *extra,
                                       gchar **out_new_uid,
                                       gchar **out_new_extra,
diff --git a/src/addressbook/libedata-book/e-book-meta-backend.h 
b/src/addressbook/libedata-book/e-book-meta-backend.h
index f21f5e8..740f321 100644
--- a/src/addressbook/libedata-book/e-book-meta-backend.h
+++ b/src/addressbook/libedata-book/e-book-meta-backend.h
@@ -138,7 +138,7 @@ struct _EBookMetaBackendClass {
        gboolean        (* save_contact_sync)   (EBookMetaBackend *meta_backend,
                                                 gboolean overwrite_existing,
                                                 EConflictResolution conflict_resolution,
-                                                const EContact *contact,
+                                                /* const */ EContact *contact,
                                                 const gchar *extra,
                                                 gchar **out_new_uid,
                                                 gchar **out_new_extra,
@@ -242,7 +242,7 @@ gboolean    e_book_meta_backend_save_contact_sync
                                                (EBookMetaBackend *meta_backend,
                                                 gboolean overwrite_existing,
                                                 EConflictResolution conflict_resolution,
-                                                const EContact *contact,
+                                                /* const */ EContact *contact,
                                                 const gchar *extra,
                                                 gchar **out_new_uid,
                                                 gchar **out_new_extra,
diff --git a/tests/libebook/data/vcards/custom-4.vcf b/tests/libebook/data/vcards/custom-4.vcf
index 77e2990..2bee4a8 100644
--- a/tests/libebook/data/vcards/custom-4.vcf
+++ b/tests/libebook/data/vcards/custom-4.vcf
@@ -1,5 +1,5 @@
 BEGIN:VCARD
-UID:Custom-4
+UID:custom-4
 FN:Big Bobby Brown
 TEL;TYPE=work,pref:+9999999
 EMAIL:big bobby brown org
diff --git a/tests/libebook/data/vcards/logo-1.vcf b/tests/libebook/data/vcards/logo-1.vcf
new file mode 100644
index 0000000..eaf812c
--- /dev/null
+++ b/tests/libebook/data/vcards/logo-1.vcf
@@ -0,0 +1,21 @@
+BEGIN:VCARD
+VERSION:3.0
+UID:logo-1
+FN:logo
+N:;logo;;;
+LOGO;TYPE="X-EVOLUTION-UNKNOWN";ENCODING=b:/9j/4AAQSkZJRgABAQEASABIAAD/2wB
+ DABYPEBMQDhYTEhMYFxYaIDYjIB4eIEIvMic2TkVSUU1FTEpWYXxpVlx1XUpMbJNtdYCEi4yLV
+ GiZo5eHonyIi4b/2wBDARcYGCAcID8jIz+GWUxZhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoa
+ GhoaGhoaGhoaGhoaGhoaGhoaGhoaGhob/wgARCABAAEADAREAAhEBAxEB/8QAFgABAQEAAAAAA
+ AAAAAAAAAAAAgEE/8QAFwEBAQEBAAAAAAAAAAAAAAAAAAECBP/aAAwDAQACEAMQAAABXP2RDRD
+ ZAhs2Y2UlENkCGzZjZSUQ2QNFNmNlJRDZA0U2Y2UlENkDRTZjZSUQ2QNFNmNlJRDZA0U2Y2UlE
+ NkDRT//xAAUEAEAAAAAAAAAAAAAAAAAAABg/9oACAEBAAEFAgH/xAAUEQEAAAAAAAAAAAAAAAA
+ AAABg/9oACAEDAQE/AQH/xAAUEQEAAAAAAAAAAAAAAAAAAABg/9oACAECAQE/AQH/xAAUEAEAA
+ AAAAAAAAAAAAAAAAABg/9oACAEBAAY/AgH/xAAWEAADAAAAAAAAAAAAAAAAAAABIFD/2gAIAQE
+ AAT8hQ3P/2gAMAwEAAgADAAAAEIzqtZzqlZzqhZzqhZzqhZyqhZyqhZyqhf/EABQRAQAAAAAAA
+ AAAAAAAAAAAAGD/2gAIAQMBAT8QAf/EABQRAQAAAAAAAAAAAAAAAAAAAGD/2gAIAQIBAT8QAf/
+ EABYQAQEBAAAAAAAAAAAAAAAAAAEAEP/aAAgBAQABPxBmZwzMzMzMzMzMzMzMzMzMzMzMzMzMz
+ MzMzMzMzMzMzf/Z
+REV:2017-05-10T14:36:01Z(2)
+EMAIL;TYPE=WORK:logo@no.where
+END:VCARD
\ No newline at end of file
diff --git a/tests/libebook/data/vcards/photo-1.vcf b/tests/libebook/data/vcards/photo-1.vcf
new file mode 100644
index 0000000..75d1ba4
--- /dev/null
+++ b/tests/libebook/data/vcards/photo-1.vcf
@@ -0,0 +1,59 @@
+BEGIN:VCARD
+VERSION:3.0
+UID:photo-1
+FN:photo
+N:;photo;;;
+PHOTO;TYPE="X-EVOLUTION-UNKNOWN";ENCODING=b:iVBORw0KGgoAAAANSUhEUgAAAEAAAAB
+ ACAIAAAAlC+aJAAAKo0lEQVRo3n1aW7LcuA4j6OzkLujufxOzgu5gPsQHIHdNqpKc03bbEh8gC
+ Ar//98/D74Pvonvk98nvw++mZ8Hf5/8PPk38XnwffLz4Jv5/ZN+Jz5P/n3wOR/q1cSnn/b9U8/
+ /PPlNfP9gb8j8ngU8+X3wyX7yfPd8+Of83DdnnlV9E0QQgQBi/oD9ExlxLiACCJCBiGAE++Pg+
+ TbqQl/a2xgR0Esg55HngYhAUBbB4DxmV7BPqwVn/Xb+zteBehUC8bdWBkYQCNbLIoI4//XiSMz
+ r58GYGyLifCH6+STQ1xjYrR+TEmGbYOz+z/6yHj7WYahtQ/fXNgXLGNRnIxgEOMvDOI+13vJWk
+ DHb6LftdiLo4YAIQmwwtg1E5ImLCJLol8i3O5DYNu+nHStD9kHUM6McgbaSuvdcIDo+y0VAhQh
+ PWHECkDxvqd3vV84KElznjmM3D6AxSIlsRuDYGxUVx3mMDmpaUJZLNLdCdkFW/EGiAbXQE6Zio
+ Fj/5URZXay9mmfbYBIx0L2KiWfFJ91IC7EKwVnZiRAy2CjSWwc8OCsMy7PcKM9KAEhoiek3lSG
+ JUTdhgoUWnpi4t0RiG4nrTfSdKAeIkzdj9clUXzKQtVeu1UKTNxpkCBSMKUYqbtCRsvOv4avAg
+ /rwY0oQDWaGZAh1IGaFjbwRAaZEz946KdObsMisxQJmc4a+b5If5gkAZ59sNEOA4IKSoCfVuI1
+ bgrokiBNCtpTGo4bLdYpiPDf2OqwNwvp1kigdRrLNBRDuvQx0kBiQMMqD8/AIJg54U6orFMIOW
+ LIdsxmDwXsEScYFWmO3jo628zG7xFi/nuI8qWDs2HS2UKmXt4Fm24dewFLP0ny5AjfQOkrDDSz
+ lg78WGcCWZAQpZAZbCaUEHITDoRKahUAHnsDT/AAtZwPVqNAk+33siregPw6rbayblo0gJt2x1
+ QNV5SvPuM5DRELsKbQmoOyBoNC6QGjBONawWC9KAa2ZBR77lCFn62OKgTUwFnhPvnFsilxmVyZ
+ WzgH2N6r0yOtjI4K1UsBf3TuD4L2gOql4Wp63r2vJlXji8A4yN+6JYLHlqXVotkPeUSt5j96Mw
+ igHdKlMQpGi6PkG1rhsaQPGV02XsLcdMidkdO6afK8bMZykw3FyYQy4HIQoWhlGnSFQHFJr4ra
+ 5ErCmEVQmJnmVtCiE1fKtu8PP1uvQzIzoZoSNkrxiWVhjvIHIAmfgunF/YWgCvCMugd4er36q6
+ xU9OwdFsdbjWKjQiHfxa8oq7UaRCzh+nMQocgHrCzQLhs/mMlvxHadBxMu/y6UWm9FbBSU8YSX
+ NP+bAsBpXgp4n9ZbKFxOFxSQjt4/Q9o3UfWnKWruLWgCNuQvIH2syMDi7YCjZ14lGy6piSuvKN
+ 4QgUjj49iXUMiCdlPZ6Bxl46uYw2WMJCGGvoIM1gnMHA1Z0ebcP2ntSuXDZKOubVqwHx+70gnU
+ 55eY1GCQEpeQrQa5yxZEigryaDRBT+PhqmSYS65n5RoZu3GkMY2QHWWcRXqh08ILFlV60EQhrD
+ KraFNO2vgD0duXud9KqjkQjHMW3/G0asOGyQIyWHW0iLwihzfysDp1PNJpqtHf7Jyom5A8HhLH
+ BFXesTbQi24HKqzYoK2Ros3koGOFGfjXaNLubKlB/U00P0SJwdfGjbGydOBx/u7u7366s53TUX
+ KJOCvcxCr4AdYL3LnHjobP7hCwmRFnb5rHKUmlLcEBSXY3anE0nwnBUG4AeelW/S2ZVUwbr3Kb
+ TlW0zkotaV9gsXGN8866mu6XJHhohP2DOoYdQ4MRymLiUvuG5LrJyfEsNIayZtlrhWEFVtFvnC
+ NWi4NR9qAyr0GFQdchnV0N4Q7HqmCmlWFF2gD5/cGMhTwo40KYcyypK6Vy9NUQsgxJQYDwi+Wa
+ CU8ksy3qmXeFUOk6nVjnQHAUTs1bDsOwi0JoFl4xOCDHurjuCKkzwJZuNeIANO4Eyo5cSUlilP
+ 5s4FisQ0JRkxEruA9kuPmy5RThv1458Uobq3rvvAqRGmEVQ2FHePmRuOxd6y6BUHl2Et4/a5u0
+ 8VbQA0djDSAQCP7pG9BtMvyMsFlbBHIlnyJwBMJ35wHg9hffuQokXaRFMkCIPEdRxF/qfastMS
+ jgyYb+7fs9f44Alq1aV9AX4pYEobYTmB0e1QrQ3J8cpxevYDBRTUgcDryb/eMCk4kt2iztkgZW
+ 2MLOYu2SKVhDaWc0HvwjfyXcKf+OoghfBmGVn4cpC74olnE5XpIELJOLdDR0h29qJQX5lKAwHf
+ 2tZVkwAhBh0970J3pXYRH2ZMYBcnApihljKPV99J0ftLNnWG9EefsKnRZQKKsMA5b3Cqcriicu
+ fq8qUFId7HAeFhbu/UDOw40JHDlt3DqptRkHUEZo1A9QhkY0GM+It5ZUg9xLjAF1HiM5milsnC
+ uwapFXoT8iLA8DZ+egiWMn5RBa6O8sLQd513QrhzTDm5WouazxbMgJFeNRyj5Wk2Vr4KIi4h2a
+ kRBuDkZgefhJFtLjl1+Vu/hCiAOoEk+9umss9IGIJJfZWOotp0wd1RLaCCD/TUqKQUHYZFKELW
+ 2tAG0bpFGPaAe5wcuunyBDOeVXnUuLHHmBX7fCMn/jI1+GCka+HBbb3Xx3fTQlc599WD/dkxbp
+ lyECO+6oTU7iUHRqv3SQmtf0cOcAHXJAKrrPSe3iN1eurMNHliLGIHm5gUVcOt+iB3rBuLt0ea
+ MlRly65wyBo5yry76uiSpJeYIBr2jRr6rI+LI+mstkAITrHuSINItWkwrRIF3HoLWmFBX523DJ
+ lHLpnBxWoOu01s2Go3GqaLm69vuR1xiuhwmVnrfFqBgqPW/Ecry1ZuPFMhne4DA5mai1v2UF5n
+ 6UdIcIWlJtx4QYhKh9WVD8kS8W/kempieRzfOzsddpF1kANPu6B9/TTCslhghMlaSm6skgXb53
+ Zj0hRsIsjLeI109IJcLxOUdySk0u/0HBVKRf8QWYQCZftBBD2sNMUkq79LTEi5KSR0kLuzLUBe
+ gVp29WxvjGpq6dB/BClx1RJ13tsPC0jX8xpC6JZ+ujnEPUesOGGcMOA5YhOFLz7xMzq7FAPtJ0
+ aVEj8x0xzM0mEUVxa4NieQlaOaansw0d8XZlvsk/pfHdM3+xYhYmK4QwpHMQ1IQPxRia8lFbYS
+ /V0AR02pKbInA2yGZ3LD/N9nYLZtnxOLV76qs0y5iiZTSzVlMD7gIRqUg2UOqPZFOB1tuoamM6
+ sWrjVQkcOBYdwPRklz0k8oUt2uM5Yj2iw0FnLSDNKoWDHdLRnJnAPXnWsGyILJKhjXZsSj+YLu
+ GANutANHxxiaixsbKnijoHVKINdS0b7NqFmRaEYnTSSkNr+FmT2iOT2KlCzN12b2QGwhzy53by
+ UAVxngsPneTwKt9kSdLKxM5OcirRMkxSpcQ/sanTDz8n5kUoYntGl2vuEoLahnWT06qi8jLe2m
+ gXrLVRMFzBdFuQMMW08JU4F34QdLa+QK0zjNYuZ7pl21nKAcPusPmRndDdBGwnBWS/u4wzYhME
+ ew9N6W3PGHcICYvwdQjbvoh2a3Z/QdZQ6zBy6hOkH5oSWjuOMM9mhSkpI86cUB2IVzAg5ZGVCE
+ F5nVDCI3L0ktGde0ZgyjP0XqSHx2GyNb9QAAAAASUVORK5CYII=
+EMAIL;TYPE=WORK:photo@no.where
+REV:2017-05-10T14:34:50Z(0)
+END:VCARD
diff --git a/tests/libedata-book/CMakeLists.txt b/tests/libedata-book/CMakeLists.txt
index 70ba706..8bcc6c1 100644
--- a/tests/libedata-book/CMakeLists.txt
+++ b/tests/libedata-book/CMakeLists.txt
@@ -107,6 +107,7 @@ set(TESTS
        test-book-cache-cursor-set-sexp
        test-book-cache-cursor-change-locale
        test-book-cache-offline
+       test-book-meta-backend
        test-sqlite-get-contact
        test-sqlite-create-cursor
        test-sqlite-cursor-move-by-posix
diff --git a/tests/libedata-book/test-book-meta-backend.c b/tests/libedata-book/test-book-meta-backend.c
new file mode 100644
index 0000000..d4ca77d
--- /dev/null
+++ b/tests/libedata-book/test-book-meta-backend.c
@@ -0,0 +1,1110 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * 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/>.
+ */
+
+#include "evolution-data-server-config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+
+#include "libebook-contacts/libebook-contacts.h"
+
+#include "test-book-cache-utils.h"
+
+typedef struct _EBookMetaBackendTest {
+       EBookMetaBackend parent;
+
+       GHashTable *contacts;
+
+       gint sync_tag_index;
+       gboolean can_connect;
+       gboolean is_connected;
+       gint connect_count;
+       gint list_count;
+       gint save_count;
+       gint load_count;
+       gint remove_count;
+} EBookMetaBackendTest;
+
+typedef struct _EBookMetaBackendTestClass {
+       EBookMetaBackendClass parent_class;
+} EBookMetaBackendTestClass;
+
+#define E_TYPE_BOOK_META_BACKEND_TEST (e_book_meta_backend_test_get_type ())
+#define E_BOOK_META_BACKEND_TEST(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_BOOK_META_BACKEND_TEST, EBookMetaBackendTest))
+#define E_IS_BOOK_META_BACKEND_TEST(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_BOOK_META_BACKEND_TEST))
+
+GType e_book_meta_backend_test_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (EBookMetaBackendTest, e_book_meta_backend_test, E_TYPE_BOOK_META_BACKEND)
+
+static void
+ebmb_test_add_test_case (EBookMetaBackendTest *test_backend,
+                        const gchar *case_name)
+{
+       EContact *contact;
+
+       g_assert_nonnull (test_backend);
+       g_assert_nonnull (case_name);
+
+       contact = tcu_new_contact_from_test_case (case_name);
+       g_assert_nonnull (contact);
+
+       if (!e_contact_get_const (contact, E_CONTACT_REV))
+               e_contact_set (contact, E_CONTACT_REV, "initial-revision");
+
+       g_hash_table_insert (test_backend->contacts, e_contact_get (contact, E_CONTACT_UID), contact);
+}
+
+static void
+ebmb_test_remove_component (EBookMetaBackendTest *test_backend,
+                           const gchar *uid)
+{
+       g_assert_nonnull (test_backend);
+       g_assert_nonnull (uid);
+
+       g_hash_table_remove (test_backend->contacts, uid);
+}
+
+static GHashTable * /* gchar * ~> NULL */
+ebmb_test_gather_uids (va_list args)
+{
+       GHashTable *expects;
+       const gchar *uid;
+
+       expects = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+       uid = va_arg (args, const gchar *);
+       while (uid) {
+               g_hash_table_insert (expects, g_strdup (uid), NULL);
+               uid = va_arg (args, const gchar *);
+       }
+
+       return expects;
+}
+
+static void
+ebmb_test_hash_contains (GHashTable *contacts, /* gchar *uid ~> EContact * */
+                        gboolean negate,
+                        gboolean exact,
+                        ...) /* uid-s, ended with NULL */
+{
+       va_list args;
+       GHashTable *expects;
+       GHashTableIter iter;
+       gpointer uid;
+       guint ntotal;
+
+       g_return_if_fail (contacts != NULL);
+
+       va_start (args, exact);
+       expects = ebmb_test_gather_uids (args);
+       va_end (args);
+
+       ntotal = g_hash_table_size (expects);
+
+       g_hash_table_iter_init (&iter, contacts);
+       while (g_hash_table_iter_next (&iter, &uid, NULL)) {
+               if (exact) {
+                       if (negate)
+                               g_assert (!g_hash_table_remove (expects, uid));
+                       else
+                               g_assert (g_hash_table_remove (expects, uid));
+               } else {
+                       g_hash_table_remove (expects, uid);
+               }
+       }
+
+       if (negate)
+               g_assert_cmpint (g_hash_table_size (expects), ==, ntotal);
+       else
+               g_assert_cmpint (g_hash_table_size (expects), ==, 0);
+
+       g_hash_table_destroy (expects);
+}
+
+static void
+ebmb_test_cache_contains (EBookCache *book_cache,
+                         gboolean negate,
+                         gboolean exact,
+                         ...) /* uid-s, ended with NULL */
+{
+       va_list args;
+       GHashTable *expects;
+       GHashTableIter iter;
+       ECache *cache;
+       gpointer key;
+       gint found = 0;
+
+       g_return_if_fail (E_IS_BOOK_CACHE (book_cache));
+
+       va_start (args, exact);
+       expects = ebmb_test_gather_uids (args);
+       va_end (args);
+
+       cache = E_CACHE (book_cache);
+
+       g_hash_table_iter_init (&iter, expects);
+       while (g_hash_table_iter_next (&iter, &key, NULL)) {
+               const gchar *uid = key;
+
+               g_assert_nonnull (uid);
+
+               if (e_cache_contains (cache, uid, E_CACHE_EXCLUDE_DELETED))
+                       found++;
+       }
+
+       if (negate)
+               g_assert_cmpint (0, ==, found);
+       else
+               g_assert_cmpint (g_hash_table_size (expects), ==, found);
+
+       g_hash_table_destroy (expects);
+
+       if (exact && !negate)
+               g_assert_cmpint (e_cache_get_count (cache, E_CACHE_EXCLUDE_DELETED, NULL, NULL), ==, found);
+}
+
+static void
+ebmb_test_cache_and_server_equal (EBookCache *book_cache,
+                                 GHashTable *contacts,
+                                 ECacheDeletedFlag deleted_flag)
+{
+       ECache *cache;
+       GHashTableIter iter;
+       gpointer uid;
+
+       g_return_if_fail (E_IS_BOOK_CACHE (book_cache));
+       g_return_if_fail (contacts != NULL);
+
+       cache = E_CACHE (book_cache);
+
+       g_assert_cmpint (e_cache_get_count (cache, deleted_flag, NULL, NULL), ==,
+               g_hash_table_size (contacts));
+
+       g_hash_table_iter_init (&iter, contacts);
+       while (g_hash_table_iter_next (&iter, &uid, NULL)) {
+               g_assert (e_cache_contains (cache, uid, deleted_flag));
+       }
+}
+
+static gchar *
+e_book_meta_backend_test_get_backend_property (EBookBackend *book_backend,
+                                              const gchar *prop_name)
+{
+       g_return_val_if_fail (E_IS_BOOK_META_BACKEND_TEST (book_backend), NULL);
+       g_return_val_if_fail (prop_name != NULL, NULL);
+
+       if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
+               return g_strjoin (",",
+                       e_book_meta_backend_get_capabilities (E_BOOK_META_BACKEND (book_backend)),
+                       "local",
+                       "contact-lists",
+                       NULL);
+       }
+
+       /* Chain up to parent's method. */
+       return E_BOOK_BACKEND_CLASS (e_book_meta_backend_test_parent_class)->get_backend_property 
(book_backend, prop_name);
+}
+
+static gboolean
+e_book_meta_backend_test_connect_sync (EBookMetaBackend *meta_backend,
+                                      const ENamedParameters *credentials,
+                                      ESourceAuthenticationResult *out_auth_result,
+                                      gchar **out_certificate_pem,
+                                      GTlsCertificateFlags *out_certificate_errors,
+                                      GCancellable *cancellable,
+                                      GError **error)
+{
+       EBookMetaBackendTest *test_backend;
+
+       g_return_val_if_fail (E_IS_BOOK_META_BACKEND_TEST (meta_backend), FALSE);
+
+       test_backend = E_BOOK_META_BACKEND_TEST (meta_backend);
+
+       if (test_backend->is_connected)
+               return TRUE;
+
+       test_backend->connect_count++;
+
+       if (test_backend->can_connect) {
+               test_backend->is_connected = TRUE;
+               return TRUE;
+       }
+
+       g_set_error_literal (error, E_CLIENT_ERROR, E_CLIENT_ERROR_REPOSITORY_OFFLINE,
+               e_client_error_to_string (E_CLIENT_ERROR_REPOSITORY_OFFLINE));
+
+       return FALSE;
+}
+
+static gboolean
+e_book_meta_backend_test_disconnect_sync (EBookMetaBackend *meta_backend,
+                                         GCancellable *cancellable,
+                                         GError **error)
+{
+       EBookMetaBackendTest *test_backend;
+
+       g_return_val_if_fail (E_IS_BOOK_META_BACKEND_TEST (meta_backend), FALSE);
+
+       test_backend = E_BOOK_META_BACKEND_TEST (meta_backend);
+       test_backend->is_connected = FALSE;
+
+       return TRUE;
+}
+
+static gboolean
+e_book_meta_backend_test_get_changes_sync (EBookMetaBackend *meta_backend,
+                                          const gchar *last_sync_tag,
+                                          gboolean is_repeat,
+                                          gchar **out_new_sync_tag,
+                                          gboolean *out_repeat,
+                                          GSList **out_created_objects,
+                                          GSList **out_modified_objects,
+                                          GSList **out_removed_objects,
+                                          GCancellable *cancellable,
+                                          GError **error)
+{
+       EBookMetaBackendTest *test_backend;
+
+       g_return_val_if_fail (E_IS_BOOK_META_BACKEND_TEST (meta_backend), FALSE);
+       g_return_val_if_fail (out_new_sync_tag != NULL, FALSE);
+       g_return_val_if_fail (out_repeat != NULL, FALSE);
+
+       test_backend = E_BOOK_META_BACKEND_TEST (meta_backend);
+
+       if (!test_backend->sync_tag_index) {
+               g_assert_null (last_sync_tag);
+       } else {
+               g_assert_nonnull (last_sync_tag);
+               g_assert_cmpint (atoi (last_sync_tag), ==, test_backend->sync_tag_index);
+
+               test_backend->sync_tag_index++;
+               *out_new_sync_tag = g_strdup_printf ("%d", test_backend->sync_tag_index);
+
+               if (test_backend->sync_tag_index == 2)
+                       *out_repeat = TRUE;
+               else if (test_backend->sync_tag_index == 3)
+                       return TRUE;
+       }
+
+       /* Nothing to do here at the moment, left the work to the parent class,
+          which calls list_existing_sync() internally. */
+       return E_BOOK_META_BACKEND_CLASS (e_book_meta_backend_test_parent_class)->get_changes_sync 
(meta_backend,
+               last_sync_tag, is_repeat, out_new_sync_tag, out_repeat, out_created_objects,
+               out_modified_objects, out_removed_objects, cancellable, error);
+}
+
+static gboolean
+e_book_meta_backend_test_list_existing_sync (EBookMetaBackend *meta_backend,
+                                            gchar **out_new_sync_tag,
+                                            GSList **out_existing_objects,
+                                            GCancellable *cancellable,
+                                            GError **error)
+{
+       EBookMetaBackendTest *test_backend;
+       GHashTableIter iter;
+       gpointer key, value;
+
+       g_return_val_if_fail (E_IS_BOOK_META_BACKEND_TEST (meta_backend), FALSE);
+       g_return_val_if_fail (out_new_sync_tag, FALSE);
+       g_return_val_if_fail (out_existing_objects, FALSE);
+
+       test_backend = E_BOOK_META_BACKEND_TEST (meta_backend);
+       test_backend->list_count++;
+
+       g_assert (test_backend->is_connected);
+
+       *out_existing_objects = NULL;
+
+       g_hash_table_iter_init (&iter, test_backend->contacts);
+       while (g_hash_table_iter_next (&iter, &key, &value)) {
+               const gchar *uid;
+               gchar *revision;
+               EBookMetaBackendInfo *nfo;
+
+               uid = key;
+               revision = e_contact_get (value, E_CONTACT_REV);
+
+               nfo = e_book_meta_backend_info_new (uid, revision, NULL, NULL);
+               *out_existing_objects = g_slist_prepend (*out_existing_objects, nfo);
+
+               g_free (revision);
+       }
+
+       return TRUE;
+}
+
+static gboolean
+e_book_meta_backend_test_save_contact_sync (EBookMetaBackend *meta_backend,
+                                           gboolean overwrite_existing,
+                                           EConflictResolution conflict_resolution,
+                                           EContact *contact,
+                                           const gchar *extra,
+                                           gchar **out_new_uid,
+                                           gchar **out_new_extra,
+                                           GCancellable *cancellable,
+                                           GError **error)
+{
+       EBookMetaBackendTest *test_backend;
+       const gchar *uid;
+
+       g_return_val_if_fail (E_IS_BOOK_META_BACKEND_TEST (meta_backend), FALSE);
+       g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
+       g_return_val_if_fail (out_new_uid != NULL, FALSE);
+
+       test_backend = E_BOOK_META_BACKEND_TEST (meta_backend);
+       test_backend->save_count++;
+
+       g_assert (test_backend->is_connected);
+
+       uid = e_contact_get_const (contact, E_CONTACT_UID);
+       g_assert_nonnull (uid);
+
+       if (g_hash_table_contains (test_backend->contacts, uid)) {
+               if (!overwrite_existing) {
+                       g_propagate_error (error, e_data_book_create_error 
(E_DATA_BOOK_STATUS_CONTACTID_ALREADY_EXISTS, NULL));
+                       return FALSE;
+               }
+
+               g_hash_table_remove (test_backend->contacts, uid);
+       }
+
+       /* Intentionally do not add a referenced 'contact', thus any later changes
+          on it are not "propagated" into the test_backend's content. */
+       g_hash_table_insert (test_backend->contacts, g_strdup (uid), e_contact_duplicate (contact));
+
+       *out_new_uid = g_strdup (uid);
+
+       return TRUE;
+}
+
+static gboolean
+e_book_meta_backend_test_load_contact_sync (EBookMetaBackend *meta_backend,
+                                           const gchar *uid,
+                                           const gchar *extra,
+                                           EContact **out_contact,
+                                           gchar **out_extra,
+                                           GCancellable *cancellable,
+                                           GError **error)
+{
+       EBookMetaBackendTest *test_backend;
+
+       g_return_val_if_fail (E_IS_BOOK_META_BACKEND_TEST (meta_backend), FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
+       g_return_val_if_fail (out_contact != NULL, FALSE);
+       g_return_val_if_fail (out_extra != NULL, FALSE);
+
+       test_backend = E_BOOK_META_BACKEND_TEST (meta_backend);
+       test_backend->load_count++;
+
+       g_assert (test_backend->is_connected);
+
+       *out_contact = g_hash_table_lookup (test_backend->contacts, uid);
+
+       if (*out_contact) {
+               *out_contact = e_contact_duplicate (*out_contact);
+               *out_extra = g_strconcat ("extra for ", uid, NULL);
+               return TRUE;
+       } else {
+               g_propagate_error (error, e_data_book_create_error (E_DATA_BOOK_STATUS_CONTACT_NOT_FOUND, 
NULL));
+       }
+
+       return FALSE;
+}
+
+static gboolean
+e_book_meta_backend_test_remove_contact_sync (EBookMetaBackend *meta_backend,
+                                             EConflictResolution conflict_resolution,
+                                             const gchar *uid,
+                                             const gchar *extra,
+                                             const gchar *object,
+                                             GCancellable *cancellable,
+                                             GError **error)
+{
+       EBookMetaBackendTest *test_backend;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (E_IS_BOOK_META_BACKEND_TEST (meta_backend), FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
+       g_return_val_if_fail (extra != NULL, FALSE);
+
+       test_backend = E_BOOK_META_BACKEND_TEST (meta_backend);
+       test_backend->remove_count++;
+
+       g_assert (test_backend->is_connected);
+
+       success = g_hash_table_remove (test_backend->contacts, uid);
+       if (success) {
+               gchar *expected_extra;
+
+               expected_extra = g_strconcat ("extra for ", uid, NULL);
+               g_assert_cmpstr (expected_extra, ==, extra);
+               g_free (expected_extra);
+       } else {
+               g_propagate_error (error, e_data_book_create_error (E_DATA_BOOK_STATUS_CONTACT_NOT_FOUND, 
NULL));
+       }
+
+       return success;
+}
+
+static void
+e_book_meta_backend_test_reset_counters (EBookMetaBackendTest *test_backend)
+{
+       g_return_if_fail (E_IS_BOOK_META_BACKEND_TEST (test_backend));
+
+       test_backend->connect_count = 0;
+       test_backend->list_count = 0;
+       test_backend->save_count = 0;
+       test_backend->load_count = 0;
+       test_backend->remove_count = 0;
+}
+
+static EBookCache *glob_use_cache = NULL;
+
+static void
+e_book_meta_backend_test_constructed (GObject *object)
+{
+       EBookMetaBackendTest *test_backend = E_BOOK_META_BACKEND_TEST (object);
+
+       g_assert_nonnull (glob_use_cache);
+
+       /* Set it before EBookMetaBackend::constucted() creates its own cache */
+       e_book_meta_backend_set_cache (E_BOOK_META_BACKEND (test_backend), glob_use_cache);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_book_meta_backend_test_parent_class)->constructed (object);
+}
+
+static void
+e_book_meta_backend_test_finalize (GObject *object)
+{
+       EBookMetaBackendTest *test_backend = E_BOOK_META_BACKEND_TEST (object);
+
+       g_assert_nonnull (test_backend->contacts);
+
+       g_hash_table_destroy (test_backend->contacts);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_book_meta_backend_test_parent_class)->finalize (object);
+}
+
+static void
+e_book_meta_backend_test_class_init (EBookMetaBackendTestClass *klass)
+{
+       EBookMetaBackendClass *book_meta_backend_class;
+       EBookBackendClass *book_backend_class;
+       GObjectClass *object_class;
+
+       book_meta_backend_class = E_BOOK_META_BACKEND_CLASS (klass);
+       book_meta_backend_class->connect_sync = e_book_meta_backend_test_connect_sync;
+       book_meta_backend_class->disconnect_sync = e_book_meta_backend_test_disconnect_sync;
+       book_meta_backend_class->get_changes_sync = e_book_meta_backend_test_get_changes_sync;
+       book_meta_backend_class->list_existing_sync = e_book_meta_backend_test_list_existing_sync;
+       book_meta_backend_class->save_contact_sync = e_book_meta_backend_test_save_contact_sync;
+       book_meta_backend_class->load_contact_sync = e_book_meta_backend_test_load_contact_sync;
+       book_meta_backend_class->remove_contact_sync = e_book_meta_backend_test_remove_contact_sync;
+
+       book_backend_class = E_BOOK_BACKEND_CLASS (klass);
+       book_backend_class->get_backend_property = e_book_meta_backend_test_get_backend_property;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->constructed = e_book_meta_backend_test_constructed;
+       object_class->finalize = e_book_meta_backend_test_finalize;
+}
+
+static void
+e_book_meta_backend_test_init (EBookMetaBackendTest *test_backend)
+{
+       test_backend->sync_tag_index = 0;
+       test_backend->is_connected = FALSE;
+       test_backend->can_connect = TRUE;
+       test_backend->contacts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+
+       ebmb_test_add_test_case (test_backend, "custom-1");
+       ebmb_test_add_test_case (test_backend, "custom-2");
+       ebmb_test_add_test_case (test_backend, "custom-3");
+       ebmb_test_add_test_case (test_backend, "custom-4");
+       ebmb_test_add_test_case (test_backend, "custom-5");
+
+       e_book_meta_backend_test_reset_counters (test_backend);
+
+       e_backend_set_online (E_BACKEND (test_backend), TRUE);
+       e_book_backend_set_writable (E_BOOK_BACKEND (test_backend), TRUE);
+}
+
+static ESourceRegistry *glob_registry = NULL;
+
+static EBookMetaBackend *
+e_book_meta_backend_test_new (EBookCache *cache)
+{
+       EBookMetaBackend *meta_backend;
+       GHashTableIter iter;
+       ESource *scratch;
+       gpointer contact;
+       gboolean success;
+       GError *error = NULL;
+
+       g_assert (E_IS_BOOK_CACHE (cache));
+
+       g_assert_nonnull (glob_registry);
+       g_assert_null (glob_use_cache);
+
+       glob_use_cache = cache;
+
+       scratch = e_source_new_with_uid ("test-source", NULL, &error);
+       g_assert_no_error (error);
+       g_assert_nonnull (scratch);
+
+       meta_backend = g_object_new (E_TYPE_BOOK_META_BACKEND_TEST,
+               "source", scratch,
+               "registry", glob_registry,
+               NULL);
+       g_assert_nonnull (meta_backend);
+
+       g_assert (glob_use_cache == cache);
+       glob_use_cache = NULL;
+
+       g_object_unref (scratch);
+
+       g_hash_table_iter_init (&iter, E_BOOK_META_BACKEND_TEST (meta_backend)->contacts);
+       while (g_hash_table_iter_next (&iter, NULL, &contact)) {
+               gchar *extra;
+
+               extra = g_strconcat ("extra for ", e_contact_get_const (contact, E_CONTACT_UID), NULL);
+               success = e_book_cache_put_contact (cache, contact, extra, E_CACHE_IS_ONLINE, NULL, &error);
+               g_free (extra);
+
+               g_assert_no_error (error);
+               g_assert (success);
+       }
+
+       return meta_backend;
+}
+
+#if 0
+static void
+e_book_meta_backend_test_change_online (EBookMetaBackend *meta_backend,
+                                       gboolean is_online)
+{
+       EFlag *flag;
+       gulong handler_id;
+
+       if (!is_online) {
+               e_backend_set_online (E_BACKEND (meta_backend), FALSE);
+               return;
+       }
+
+       if (e_backend_get_online (E_BACKEND (meta_backend)))
+               return;
+
+       flag = e_flag_new ();
+
+       handler_id = g_signal_connect_swapped (meta_backend, "refresh-completed",
+               G_CALLBACK (e_flag_set), flag);
+
+       /* Going online triggers refresh, thus wait for it */
+       e_backend_set_online (E_BACKEND (meta_backend), TRUE);
+
+       e_flag_wait (flag);
+       e_flag_free (flag);
+
+       g_signal_handler_disconnect (meta_backend, handler_id);
+}
+#endif
+
+static void
+e_book_meta_backend_test_call_refresh (EBookMetaBackend *meta_backend)
+{
+       EFlag *flag;
+       gulong handler_id;
+       gboolean success;
+       GError *error = NULL;
+
+       if (!e_backend_get_online (E_BACKEND (meta_backend)))
+               return;
+
+       flag = e_flag_new ();
+
+       handler_id = g_signal_connect_swapped (meta_backend, "refresh-completed",
+               G_CALLBACK (e_flag_set), flag);
+
+       success = E_BOOK_BACKEND_GET_CLASS (meta_backend)->refresh_sync (E_BOOK_BACKEND (meta_backend), NULL, 
&error);
+       g_assert_no_error (error);
+       g_assert (success);
+
+       e_flag_wait (flag);
+       e_flag_free (flag);
+
+       g_signal_handler_disconnect (meta_backend, handler_id);
+}
+
+static void
+test_one_photo (EBookMetaBackend *meta_backend,
+               const gchar *test_case,
+               EContactField field)
+{
+       EContact *contact;
+       EContactPhoto *photo;
+       guchar *orig_content;
+       gchar *new_content = NULL;
+       gsize orig_len = 0, new_len = 0;
+       gchar *filename;
+       const gchar *remote_uri = "http://www.gnome.org/logo.jpg";;
+       gboolean success;
+       GError *error = NULL;
+
+       g_assert (E_IS_BOOK_META_BACKEND (meta_backend));
+       g_assert_nonnull (test_case);
+       g_assert (field == E_CONTACT_PHOTO || field == E_CONTACT_LOGO);
+
+       contact = tcu_new_contact_from_test_case (test_case);
+       g_assert_nonnull (contact);
+
+       photo = e_contact_get (contact, field);
+       g_assert_nonnull (photo);
+       g_assert_cmpint (photo->type, ==, E_CONTACT_PHOTO_TYPE_INLINED);
+
+       orig_content = (guchar *) e_contact_photo_get_inlined (photo, &orig_len);
+       g_assert_nonnull (orig_content);
+       g_assert_cmpint (orig_len, >, 0);
+
+       orig_content = g_memdup (orig_content, (guint) orig_len);
+
+       e_contact_photo_free (photo);
+
+       success = e_book_meta_backend_store_inline_photos_sync (meta_backend, contact, NULL, &error);
+       g_assert_no_error (error);
+       g_assert (success);
+
+       photo = e_contact_get (contact, field);
+       g_assert_nonnull (photo);
+       g_assert_cmpint (photo->type, ==, E_CONTACT_PHOTO_TYPE_URI);
+       g_assert_nonnull (e_contact_photo_get_uri (photo));
+
+       filename = g_filename_from_uri (e_contact_photo_get_uri (photo), NULL, &error);
+       g_assert_no_error (error);
+       g_assert_nonnull (filename);
+
+       success = g_file_get_contents (filename, &new_content, &new_len, &error);
+       g_assert_no_error (error);
+       g_assert (success);
+       g_assert_nonnull (new_content);
+       g_assert_cmpmem (orig_content, orig_len, new_content, new_len);
+
+       g_free (new_content);
+       g_free (filename);
+
+       e_contact_photo_free (photo);
+
+       success = e_book_meta_backend_inline_local_photos_sync (meta_backend, contact, NULL, &error);
+       g_assert_no_error (error);
+       g_assert (success);
+
+       photo = e_contact_get (contact, field);
+       g_assert_nonnull (photo);
+       g_assert_cmpint (photo->type, ==, E_CONTACT_PHOTO_TYPE_INLINED);
+
+       new_content = (gchar *) e_contact_photo_get_inlined (photo, &new_len);
+       g_assert_nonnull (new_content);
+       g_assert_cmpmem (orig_content, orig_len, new_content, new_len);
+
+       e_contact_photo_free (photo);
+       g_free (orig_content);
+
+       /* Also try with remote URI, which should be left as is */
+       photo = e_contact_photo_new ();
+       g_assert_nonnull (photo);
+
+       photo->type = E_CONTACT_PHOTO_TYPE_URI;
+       e_contact_photo_set_uri (photo, remote_uri);
+       e_contact_set (contact, field, photo);
+       e_contact_photo_free (photo);
+
+       photo = e_contact_get (contact, field);
+       g_assert_nonnull (photo);
+       g_assert_cmpint (photo->type, ==, E_CONTACT_PHOTO_TYPE_URI);
+       g_assert_cmpstr (e_contact_photo_get_uri (photo), ==, remote_uri);
+       e_contact_photo_free (photo);
+
+       success = e_book_meta_backend_store_inline_photos_sync (meta_backend, contact, NULL, &error);
+       g_assert_no_error (error);
+       g_assert (success);
+
+       photo = e_contact_get (contact, field);
+       g_assert_nonnull (photo);
+       g_assert_cmpint (photo->type, ==, E_CONTACT_PHOTO_TYPE_URI);
+       g_assert_cmpstr (e_contact_photo_get_uri (photo), ==, remote_uri);
+       e_contact_photo_free (photo);
+
+       success = e_book_meta_backend_inline_local_photos_sync (meta_backend, contact, NULL, &error);
+       g_assert_no_error (error);
+       g_assert (success);
+
+       photo = e_contact_get (contact, field);
+       g_assert_nonnull (photo);
+       g_assert_cmpint (photo->type, ==, E_CONTACT_PHOTO_TYPE_URI);
+       g_assert_cmpstr (e_contact_photo_get_uri (photo), ==, remote_uri);
+       e_contact_photo_free (photo);
+
+       g_object_unref (contact);
+}
+
+static void
+test_photos (TCUFixture *fixture,
+            gconstpointer user_data)
+{
+       EBookMetaBackend *meta_backend;
+
+       meta_backend = e_book_meta_backend_test_new (fixture->book_cache);
+       g_assert_nonnull (meta_backend);
+
+       test_one_photo (meta_backend, "photo-1", E_CONTACT_PHOTO);
+       test_one_photo (meta_backend, "logo-1", E_CONTACT_LOGO);
+
+       g_object_unref (meta_backend);
+}
+
+static void
+test_empty_cache (TCUFixture *fixture,
+                 gconstpointer user_data)
+{
+       EBookMetaBackend *meta_backend;
+       EBookMetaBackendTest *test_backend;
+       GSList *uids;
+       gboolean success;
+       GError *error = NULL;
+
+       meta_backend = e_book_meta_backend_test_new (fixture->book_cache);
+       g_assert_nonnull (meta_backend);
+
+       test_backend = E_BOOK_META_BACKEND_TEST (meta_backend);
+       g_assert_nonnull (test_backend);
+
+       uids = NULL;
+       success = e_book_cache_search_uids (fixture->book_cache, NULL, &uids, NULL, &error);
+       g_assert_no_error (error);
+       g_assert (success);
+       g_assert_cmpint (g_slist_length (uids), >, 0);
+       g_slist_free_full (uids, g_free);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->book_cache), E_CACHE_INCLUDE_DELETED, NULL, 
&error), >, 0);
+       g_assert_no_error (error);
+
+       /* Empty the cache */
+       success = e_book_meta_backend_empty_cache_sync (meta_backend, NULL, &error);
+       g_assert_no_error (error);
+       g_assert (success);
+
+       /* Verify the cache is truly empty */
+       uids = NULL;
+       success = e_book_cache_search_uids (fixture->book_cache, NULL, &uids, NULL, &error);
+       g_assert_no_error (error);
+       g_assert (success);
+       g_assert_cmpint (g_slist_length (uids), ==, 0);
+       g_slist_free_full (uids, g_free);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->book_cache), E_CACHE_INCLUDE_DELETED, NULL, 
&error), ==, 0);
+       g_assert_no_error (error);
+
+       g_object_unref (meta_backend);
+}
+
+static void
+test_refresh (EBookMetaBackend *meta_backend)
+{
+       EBookMetaBackendTest *test_backend;
+       EBookCache *book_cache;
+       ECache *cache;
+       guint count;
+       EContact *contact;
+       GError *error = NULL;
+
+       g_assert_nonnull (meta_backend);
+
+       test_backend = E_BOOK_META_BACKEND_TEST (meta_backend);
+       book_cache = e_book_meta_backend_ref_cache (meta_backend);
+       g_assert_nonnull (book_cache);
+
+       cache = E_CACHE (book_cache);
+
+       /* Empty local cache */
+       e_cache_remove_all (cache, NULL, &error);
+       g_assert_no_error (error);
+
+       count = e_cache_get_count (cache, E_CACHE_INCLUDE_DELETED, NULL, &error);
+       g_assert_no_error (error);
+       g_assert_cmpint (count, ==, 0);
+
+       e_book_meta_backend_test_reset_counters (test_backend);
+
+       ebmb_test_remove_component (test_backend, "custom-4");
+       ebmb_test_remove_component (test_backend, "custom-5");
+
+       /* Sync with server content */
+       e_book_meta_backend_test_call_refresh (meta_backend);
+
+       g_assert_cmpint (test_backend->list_count, ==, 1);
+       g_assert_cmpint (test_backend->save_count, ==, 0);
+       g_assert_cmpint (test_backend->load_count, ==, 3);
+       g_assert_cmpint (test_backend->remove_count, ==, 0);
+
+       count = e_cache_get_count (cache, E_CACHE_INCLUDE_DELETED, NULL, &error);
+       g_assert_no_error (error);
+       g_assert_cmpint (count, ==, 3);
+
+       ebmb_test_cache_and_server_equal (book_cache, test_backend->contacts, E_CACHE_INCLUDE_DELETED);
+
+       /* Add new contact */
+       ebmb_test_add_test_case (test_backend, "custom-5");
+
+       /* Sync with server content */
+       e_book_meta_backend_test_call_refresh (meta_backend);
+
+       g_assert_cmpint (test_backend->list_count, ==, 2);
+       g_assert_cmpint (test_backend->save_count, ==, 0);
+       g_assert_cmpint (test_backend->load_count, ==, 4);
+       g_assert_cmpint (test_backend->remove_count, ==, 0);
+
+       count = e_cache_get_count (cache, E_CACHE_INCLUDE_DELETED, NULL, &error);
+       g_assert_no_error (error);
+       g_assert_cmpint (count, ==, 4);
+
+       ebmb_test_hash_contains (test_backend->contacts, FALSE, TRUE,
+               "custom-1",
+               "custom-2",
+               "custom-3",
+               "custom-5",
+               NULL);
+
+       ebmb_test_cache_contains (book_cache, FALSE, TRUE,
+               "custom-1",
+               "custom-2",
+               "custom-3",
+               "custom-5",
+               NULL);
+
+       /* Sync with server content */
+       e_book_meta_backend_test_call_refresh (meta_backend);
+
+       g_assert_cmpint (test_backend->list_count, ==, 3);
+       g_assert_cmpint (test_backend->save_count, ==, 0);
+       g_assert_cmpint (test_backend->load_count, ==, 4);
+       g_assert_cmpint (test_backend->remove_count, ==, 0);
+
+       count = e_cache_get_count (cache, E_CACHE_INCLUDE_DELETED, NULL, &error);
+       g_assert_no_error (error);
+       g_assert_cmpint (count, ==, 4);
+
+       ebmb_test_cache_and_server_equal (book_cache, test_backend->contacts, E_CACHE_INCLUDE_DELETED);
+
+       /* Add some more contacts */
+       ebmb_test_add_test_case (test_backend, "custom-6");
+       ebmb_test_add_test_case (test_backend, "custom-7");
+
+       /* Sync with server content */
+       e_book_meta_backend_test_call_refresh (meta_backend);
+
+       g_assert_cmpint (test_backend->list_count, ==, 4);
+       g_assert_cmpint (test_backend->save_count, ==, 0);
+       g_assert_cmpint (test_backend->load_count, ==, 6);
+       g_assert_cmpint (test_backend->remove_count, ==, 0);
+
+       count = e_cache_get_count (cache, E_CACHE_INCLUDE_DELETED, NULL, &error);
+       g_assert_no_error (error);
+       g_assert_cmpint (count, ==, 6);
+
+       ebmb_test_cache_and_server_equal (book_cache, test_backend->contacts, E_CACHE_INCLUDE_DELETED);
+
+       /* Remove two contacts */
+       ebmb_test_remove_component (test_backend, "custom-2");
+       ebmb_test_remove_component (test_backend, "custom-5");
+
+       /* Sync with server content */
+       e_book_meta_backend_test_call_refresh (meta_backend);
+
+       g_assert_cmpint (test_backend->list_count, ==, 5);
+       g_assert_cmpint (test_backend->save_count, ==, 0);
+       g_assert_cmpint (test_backend->load_count, ==, 6);
+       g_assert_cmpint (test_backend->remove_count, ==, 0);
+
+       count = e_cache_get_count (cache, E_CACHE_INCLUDE_DELETED, NULL, &error);
+       g_assert_no_error (error);
+       g_assert_cmpint (count, ==, 4);
+
+       ebmb_test_cache_and_server_equal (book_cache, test_backend->contacts, E_CACHE_INCLUDE_DELETED);
+
+       /* Mix add/remove/modify */
+       ebmb_test_add_test_case (test_backend, "custom-8");
+
+       ebmb_test_remove_component (test_backend, "custom-3");
+       ebmb_test_remove_component (test_backend, "custom-6");
+
+       contact = g_hash_table_lookup (test_backend->contacts, "custom-1");
+       g_assert_nonnull (contact);
+       e_contact_set (contact, E_CONTACT_REV, "changed");
+
+       contact = g_hash_table_lookup (test_backend->contacts, "custom-7");
+       g_assert_nonnull (contact);
+       e_contact_set (contact, E_CONTACT_REV, "changed");
+
+       /* Sync with server content */
+       e_book_meta_backend_test_call_refresh (meta_backend);
+
+       g_assert_cmpint (test_backend->list_count, ==, 6);
+       g_assert_cmpint (test_backend->save_count, ==, 0);
+       g_assert_cmpint (test_backend->load_count, ==, 9);
+       g_assert_cmpint (test_backend->remove_count, ==, 0);
+
+       count = e_cache_get_count (cache, E_CACHE_INCLUDE_DELETED, NULL, &error);
+       g_assert_no_error (error);
+       g_assert_cmpint (count, ==, 3);
+
+       ebmb_test_cache_and_server_equal (book_cache, test_backend->contacts, E_CACHE_INCLUDE_DELETED);
+
+       g_object_unref (book_cache);
+}
+
+typedef void (* TestWithMainLoopFunc) (EBookMetaBackend *meta_backend);
+
+typedef struct _MainLoopThreadData {
+       TestWithMainLoopFunc func;
+       EBookMetaBackend *meta_backend;
+       GMainLoop *main_loop;
+} MainLoopThreadData;
+
+static gpointer
+test_with_main_loop_thread (gpointer user_data)
+{
+       MainLoopThreadData *mlt = user_data;
+
+       g_assert_nonnull (mlt);
+       g_assert_nonnull (mlt->func);
+       g_assert_nonnull (mlt->meta_backend);
+
+       mlt->func (mlt->meta_backend);
+
+       g_main_loop_quit (mlt->main_loop);
+
+       return NULL;
+}
+
+static gboolean
+quit_test_with_mainloop_cb (gpointer user_data)
+{
+       GMainLoop *main_loop = user_data;
+
+       g_assert_nonnull (main_loop);
+
+       g_main_loop_quit (main_loop);
+
+       g_assert_not_reached ();
+
+       return FALSE;
+}
+
+static gboolean
+test_with_mainloop_run_thread_idle (gpointer user_data)
+{
+       GThread *thread;
+
+       g_assert_nonnull (user_data);
+
+       thread = g_thread_new (NULL, test_with_main_loop_thread, user_data);
+       g_thread_unref (thread);
+
+       return FALSE;
+}
+
+static void
+test_with_main_loop (EBookCache *book_cache,
+                    TestWithMainLoopFunc func)
+{
+       MainLoopThreadData mlt;
+       EBookMetaBackend *meta_backend;
+       guint timeout_id;
+
+       g_assert_nonnull (book_cache);
+       g_assert_nonnull (func);
+
+       meta_backend = e_book_meta_backend_test_new (book_cache);
+       g_assert_nonnull (meta_backend);
+
+       mlt.func = func;
+       mlt.meta_backend = meta_backend;
+       mlt.main_loop = g_main_loop_new (NULL, FALSE);
+
+       g_idle_add (test_with_mainloop_run_thread_idle, &mlt);
+       timeout_id = g_timeout_add_seconds (10, quit_test_with_mainloop_cb, mlt.main_loop);
+
+       g_main_loop_run (mlt.main_loop);
+
+       g_source_remove (timeout_id);
+       g_main_loop_unref (mlt.main_loop);
+       g_clear_object (&mlt.meta_backend);
+}
+
+#define main_loop_wrapper(_func) \
+static void \
+_func ## _tcu (TCUFixture *fixture, \
+              gconstpointer user_data) \
+{ \
+       test_with_main_loop (fixture->book_cache, _func); \
+}
+
+main_loop_wrapper (test_refresh)
+
+#undef main_loop_wrapper
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+       TCUClosure closure = { 0 };
+       gint res;
+       GError *error = NULL;
+
+#if !GLIB_CHECK_VERSION (2, 35, 1)
+       g_type_init ();
+#endif
+       g_test_init (&argc, &argv, NULL);
+
+       /* Ensure that the client and server get the same locale */
+       g_assert (g_setenv ("LC_ALL", "en_US.UTF-8", TRUE));
+       setlocale (LC_ALL, "");
+
+       glob_registry = e_source_registry_new_sync (NULL, &error);
+       g_assert_no_error (error);
+       g_assert_nonnull (glob_registry);
+
+       g_test_add ("/EBookMetaBackend/Photos", TCUFixture, &closure,
+               tcu_fixture_setup, test_photos, tcu_fixture_teardown);
+       g_test_add ("/EBookMetaBackend/EmptyCache", TCUFixture, &closure,
+               tcu_fixture_setup, test_empty_cache, tcu_fixture_teardown);
+       g_test_add ("/EBookMetaBackend/Refresh", TCUFixture, &closure,
+               tcu_fixture_setup, test_refresh_tcu, tcu_fixture_teardown);
+
+       res = g_test_run ();
+
+       g_clear_object (&glob_registry);
+
+       return res;
+}
diff --git a/tests/libedata-cal/test-cal-meta-backend.c b/tests/libedata-cal/test-cal-meta-backend.c
index 63e481d..65fa24e 100644
--- a/tests/libedata-cal/test-cal-meta-backend.c
+++ b/tests/libedata-cal/test-cal-meta-backend.c
@@ -20,7 +20,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <locale.h>
-#include <libecal/libecal.h>
+
+#include "libecal/libecal.h"
 
 #include "test-cal-cache-utils.h"
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]