[evolution-data-server/wip/offline-cache] Add more EBookCache offline-related tests and fix what didn't work properly



commit e66738f136b207bc92aacf07fb48569928026918
Author: Milan Crha <mcrha redhat com>
Date:   Thu Feb 2 20:16:54 2017 +0100

    Add more EBookCache offline-related tests and fix what didn't work properly

 src/addressbook/libedata-book/e-book-cache.c |   65 +++-
 src/libebackend/e-cache.c                    |   85 ++--
 src/libebackend/e-cache.h                    |    4 +
 tests/libedata-book/test-cache-offline.c     |  725 +++++++++++++++++++++++---
 4 files changed, 769 insertions(+), 110 deletions(-)
---
diff --git a/src/addressbook/libedata-book/e-book-cache.c b/src/addressbook/libedata-book/e-book-cache.c
index 5450a6b..37b08e1 100644
--- a/src/addressbook/libedata-book/e-book-cache.c
+++ b/src/addressbook/libedata-book/e-book-cache.c
@@ -1190,6 +1190,39 @@ ebc_delete_from_aux_tables (ECache *cache,
 }
 
 static gboolean
+ebc_delete_from_aux_tables_offline_deleted (ECache *cache,
+                                           GCancellable *cancellable,
+                                           GError **error)
+{
+       EBookCache *book_cache;
+       gint ii;
+       gboolean success = TRUE;
+
+       g_return_val_if_fail (E_IS_BOOK_CACHE (cache), FALSE);
+
+       book_cache = E_BOOK_CACHE (cache);
+
+       for (ii = 0; ii < book_cache->priv->n_summary_fields && success; ii++) {
+               SummaryField *field = &(book_cache->priv->summary_fields[ii]);
+               gchar *stmt;
+
+               if (field->type != E_TYPE_CONTACT_ATTR_LIST)
+                       continue;
+
+               stmt = e_cache_sqlite_stmt_printf ("DELETE FROM %Q WHERE uid IN ("
+                       "SELECT " E_CACHE_COLUMN_UID " FROM " E_CACHE_TABLE_OBJECTS
+                       " WHERE " E_CACHE_COLUMN_STATE "=%d)",
+                       field->aux_table, E_OFFLINE_STATE_LOCALLY_DELETED);
+
+               success = e_cache_sqlite_exec (cache, stmt, cancellable, error);
+
+               e_cache_sqlite_stmt_free (stmt);
+       }
+
+       return success;
+}
+
+static gboolean
 ebc_empty_aux_tables (ECache *cache,
                      GCancellable *cancellable,
                      GError **error)
@@ -5854,6 +5887,27 @@ e_book_cache_remove_all_locked (ECache *cache,
        return success;
 }
 
+static gboolean
+e_book_cache_clear_offline_changes_locked (ECache *cache,
+                                          GCancellable *cancellable,
+                                          GError **error)
+{
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_BOOK_CACHE (cache), FALSE);
+       g_return_val_if_fail (E_CACHE_CLASS (e_book_cache_parent_class)->clear_offline_changes_locked != 
NULL, FALSE);
+
+       /* First check whether there are any locally deleted objects at all */
+       if (e_cache_count (cache, TRUE, cancellable, error) > e_cache_count (cache, FALSE, cancellable, 
error))
+               success = ebc_delete_from_aux_tables_offline_deleted (cache, cancellable, error);
+       else
+               success = TRUE;
+
+       success = success && E_CACHE_CLASS (e_book_cache_parent_class)->clear_offline_changes_locked (cache, 
cancellable, error);
+
+       return success;
+}
+
 static void
 e_book_cache_get_property (GObject *object,
                           guint property_id,
@@ -5892,21 +5946,22 @@ e_book_cache_finalize (GObject *object)
 }
 
 static void
-e_book_cache_class_init (EBookCacheClass *class)
+e_book_cache_class_init (EBookCacheClass *klass)
 {
        GObjectClass *object_class;
        ECacheClass *cache_class;
 
-       g_type_class_add_private (class, sizeof (EBookCachePrivate));
+       g_type_class_add_private (klass, sizeof (EBookCachePrivate));
 
-       object_class = G_OBJECT_CLASS (class);
+       object_class = G_OBJECT_CLASS (klass);
        object_class->get_property = e_book_cache_get_property;
        object_class->finalize = e_book_cache_finalize;
 
-       cache_class = E_CACHE_CLASS (class);
+       cache_class = E_CACHE_CLASS (klass);
        cache_class->put_locked = e_book_cache_put_locked;
        cache_class->remove_locked = e_book_cache_remove_locked;
        cache_class->remove_all_locked = e_book_cache_remove_all_locked;
+       cache_class->clear_offline_changes_locked = e_book_cache_clear_offline_changes_locked;
 
        g_object_class_install_property (
                object_class,
@@ -5921,7 +5976,7 @@ e_book_cache_class_init (EBookCacheClass *class)
 
        signals[E164_CHANGED] = g_signal_new (
                "e164-changed",
-               G_OBJECT_CLASS_TYPE (class),
+               G_OBJECT_CLASS_TYPE (klass),
                G_SIGNAL_RUN_LAST,
                G_STRUCT_OFFSET (EBookCacheClass, e164_changed),
                NULL,
diff --git a/src/libebackend/e-cache.c b/src/libebackend/e-cache.c
index 9fdd771..7b22b7c 100644
--- a/src/libebackend/e-cache.c
+++ b/src/libebackend/e-cache.c
@@ -789,18 +789,18 @@ e_cache_set_revision (ECache *cache,
 void
 e_cache_erase (ECache *cache)
 {
-       ECacheClass *class;
+       ECacheClass *klass;
 
        g_return_if_fail (E_IS_CACHE (cache));
 
        if (!cache->priv->db)
                return;
 
-       class = E_CACHE_GET_CLASS (cache);
-       g_return_if_fail (class != NULL);
+       klass = E_CACHE_GET_CLASS (cache);
+       g_return_if_fail (klass != NULL);
 
-       if (class->erase)
-               class->erase (cache);
+       if (klass->erase)
+               klass->erase (cache);
 
        sqlite3_close (cache->priv->db);
        cache->priv->db = NULL;
@@ -1005,13 +1005,13 @@ e_cache_put_locked (ECache *cache,
                       &success);
 
        if (success) {
-               ECacheClass *class;
+               ECacheClass *klass;
 
-               class = E_CACHE_GET_CLASS (cache);
-               g_return_val_if_fail (class != NULL, FALSE);
-               g_return_val_if_fail (class->put_locked != NULL, FALSE);
+               klass = E_CACHE_GET_CLASS (cache);
+               g_return_val_if_fail (klass != NULL, FALSE);
+               g_return_val_if_fail (klass->put_locked != NULL, FALSE);
 
-               success = class->put_locked (cache, uid, revision, object, other_columns, offline_state, 
is_replace, cancellable, error);
+               success = klass->put_locked (cache, uid, revision, object, other_columns, offline_state, 
is_replace, cancellable, error);
        }
 
        if (my_other_columns)
@@ -1745,7 +1745,7 @@ e_cache_foreach_update (ECache *cache,
  * e_cache_put_offline:
  * @cache: an #ECache
  * @uid: a unique identifier of an object
- * @revision: a revision of the object
+ * @revision: (nullable): a revision of the object
  * @object: the object itself
  * @other_columns: (nullable) (element-type utf8 utf8): what other columns to set; can be %NULL
  * @cancellable: optional #GCancellable object, or %NULL
@@ -1773,7 +1773,6 @@ e_cache_put_offline (ECache *cache,
 
        g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
        g_return_val_if_fail (uid != NULL, FALSE);
-       g_return_val_if_fail (revision != NULL, FALSE);
        g_return_val_if_fail (object != NULL, FALSE);
 
        g_rec_mutex_lock (&cache->priv->lock);
@@ -2011,24 +2010,20 @@ e_cache_clear_offline_changes (ECache *cache,
                               GCancellable *cancellable,
                               GError **error)
 {
+       ECacheClass *klass;
        gboolean success;
 
        g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
 
-       g_rec_mutex_lock (&cache->priv->lock);
+       klass = E_CACHE_GET_CLASS (cache);
+       g_return_val_if_fail (klass != NULL, FALSE);
+       g_return_val_if_fail (klass->clear_offline_changes_locked != NULL, FALSE);
 
-       success = e_cache_sqlite_exec_printf (cache,
-               "DELETE FROM " E_CACHE_TABLE_OBJECTS " WHERE " E_CACHE_COLUMN_STATE "=%d",
-               NULL, NULL, cancellable, error,
-               E_OFFLINE_STATE_LOCALLY_DELETED);
+       e_cache_lock (cache, E_CACHE_LOCK_WRITE);
 
-       success = success && e_cache_sqlite_exec_printf (cache,
-               "UPDATE " E_CACHE_TABLE_OBJECTS " SET " E_CACHE_COLUMN_STATE "=%d"
-               " WHERE " E_CACHE_COLUMN_STATE "!=%d",
-               NULL, NULL, cancellable, error,
-               E_OFFLINE_STATE_SYNCED, E_OFFLINE_STATE_SYNCED);
+       success = klass->clear_offline_changes_locked (cache, cancellable, error);
 
-       g_rec_mutex_unlock (&cache->priv->lock);
+       e_cache_unlock (cache, success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
 
        return success;
 }
@@ -2557,6 +2552,29 @@ e_cache_remove_all_locked_default (ECache *cache,
 }
 
 static gboolean
+e_cache_clear_offline_changes_locked_default (ECache *cache,
+                                             GCancellable *cancellable,
+                                             GError **error)
+{
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
+
+       success = e_cache_sqlite_exec_printf (cache,
+               "DELETE FROM " E_CACHE_TABLE_OBJECTS " WHERE " E_CACHE_COLUMN_STATE "=%d",
+               NULL, NULL, cancellable, error,
+               E_OFFLINE_STATE_LOCALLY_DELETED);
+
+       success = success && e_cache_sqlite_exec_printf (cache,
+               "UPDATE " E_CACHE_TABLE_OBJECTS " SET " E_CACHE_COLUMN_STATE "=%d"
+               " WHERE " E_CACHE_COLUMN_STATE "!=%d",
+               NULL, NULL, cancellable, error,
+               E_OFFLINE_STATE_SYNCED, E_OFFLINE_STATE_SYNCED);
+
+       return success;
+}
+
+static gboolean
 e_cache_signals_accumulator (GSignalInvocationHint *ihint,
                             GValue *return_accu,
                             const GValue *handler_return,
@@ -2615,24 +2633,25 @@ e_cache_finalize (GObject *object)
 }
 
 static void
-e_cache_class_init (ECacheClass *class)
+e_cache_class_init (ECacheClass *klass)
 {
        GObjectClass *object_class;
 
-       g_type_class_add_private (class, sizeof (ECachePrivate));
+       g_type_class_add_private (klass, sizeof (ECachePrivate));
 
-       object_class = G_OBJECT_CLASS (class);
+       object_class = G_OBJECT_CLASS (klass);
        object_class->finalize = e_cache_finalize;
 
-       class->put_locked = e_cache_put_locked_default;
-       class->remove_locked = e_cache_remove_locked_default;
-       class->remove_all_locked = e_cache_remove_all_locked_default;
-       class->before_put = e_cache_before_put_default;
-       class->before_remove = e_cache_before_remove_default;
+       klass->put_locked = e_cache_put_locked_default;
+       klass->remove_locked = e_cache_remove_locked_default;
+       klass->remove_all_locked = e_cache_remove_all_locked_default;
+       klass->clear_offline_changes_locked = e_cache_clear_offline_changes_locked_default;
+       klass->before_put = e_cache_before_put_default;
+       klass->before_remove = e_cache_before_remove_default;
 
        signals[BEFORE_PUT] = g_signal_new (
                "before-put",
-               G_OBJECT_CLASS_TYPE (class),
+               G_OBJECT_CLASS_TYPE (klass),
                G_SIGNAL_RUN_LAST,
                G_STRUCT_OFFSET (ECacheClass, before_put),
                e_cache_signals_accumulator,
@@ -2649,7 +2668,7 @@ e_cache_class_init (ECacheClass *class)
 
        signals[BEFORE_REMOVE] = g_signal_new (
                "before-remove",
-               G_OBJECT_CLASS_TYPE (class),
+               G_OBJECT_CLASS_TYPE (klass),
                G_SIGNAL_RUN_LAST,
                G_STRUCT_OFFSET (ECacheClass, before_remove),
                e_cache_signals_accumulator,
diff --git a/src/libebackend/e-cache.h b/src/libebackend/e-cache.h
index 5cb1128..14a1f50 100644
--- a/src/libebackend/e-cache.h
+++ b/src/libebackend/e-cache.h
@@ -280,6 +280,10 @@ struct _ECacheClass {
                                                 const GSList *uids,
                                                 GCancellable *cancellable,
                                                 GError **error);
+       gboolean        (* clear_offline_changes_locked)
+                                               (ECache *cache,
+                                                GCancellable *cancellable,
+                                                GError **error);
        void            (* erase)               (ECache *cache);
 
        /* Signals */
diff --git a/tests/libedata-book/test-cache-offline.c b/tests/libedata-book/test-cache-offline.c
index 7947816..f3fff61 100644
--- a/tests/libedata-book/test-cache-offline.c
+++ b/tests/libedata-book/test-cache-offline.c
@@ -29,19 +29,38 @@ test_fill_cache (TCUFixture *fixture,
        tcu_add_contact_from_test_case (fixture, "custom-9", NULL);
 }
 
+enum {
+       EXPECT_DEFAULT          = (0),
+       EXPECT_CUSTOM_1         = (1 << 0),
+       EXPECT_CUSTOM_9         = (1 << 1),
+       EXPECT_SIMPLE_1         = (1 << 2),
+       EXPECT_SIMPLE_2         = (1 << 3),
+       HAS_SEARCH_DATA         = (1 << 4),
+       HAS_META_CONTACTS       = (1 << 5),
+       SKIP_CONTACT_PUT        = (1 << 6)
+};
+
 static void
 test_check_search_result (const GSList *list,
-                         gboolean expect_custom_1,
-                         gboolean expect_custom_9,
-                         gboolean search_data,
-                         gboolean meta_contacts)
+                         guint32 flags)
 {
-       gboolean have_custom_1 = FALSE, have_custom_3 = FALSE, have_custom_9 = FALSE;
+       gboolean expect_custom_1 = (flags & EXPECT_CUSTOM_1) != 0;
+       gboolean expect_custom_9 = (flags & EXPECT_CUSTOM_9) != 0;
+       gboolean expect_simple_1 = (flags & EXPECT_SIMPLE_1) != 0;
+       gboolean expect_simple_2 = (flags & EXPECT_SIMPLE_2) != 0;
+       gboolean has_search_data = (flags & HAS_SEARCH_DATA) != 0;
+       gboolean has_meta_contacts = (flags & HAS_META_CONTACTS) != 0;
+       gboolean have_custom_1 = FALSE;
+       gboolean have_custom_3 = FALSE;
+       gboolean have_custom_9 = FALSE;
+       gboolean have_simple_1 = FALSE;
+       gboolean have_simple_2 = FALSE;
        const GSList *link;
 
        for (link = list; link; link = g_slist_next (link)) {
                const gchar *uid;
-               if (search_data) {
+
+               if (has_search_data) {
                        EBookCacheSearchData *sd = link->data;
                        EContact *contact;
 
@@ -55,7 +74,7 @@ test_check_search_result (const GSList *list,
                        g_assert (E_IS_CONTACT (contact));
                        g_assert_cmpstr (uid, ==, e_contact_get_const (contact, E_CONTACT_UID));
 
-                       if (meta_contacts) {
+                       if (has_meta_contacts) {
                                g_assert_nonnull (e_contact_get_const (contact, E_CONTACT_REV));
                                g_assert_null (e_contact_get_const (contact, E_CONTACT_EMAIL_1));
                        } else {
@@ -80,6 +99,14 @@ test_check_search_result (const GSList *list,
                        g_assert (expect_custom_9);
                        g_assert (!have_custom_9);
                        have_custom_9 = TRUE;
+               } else if (g_str_equal (uid, "simple-1")) {
+                       g_assert (expect_simple_1);
+                       g_assert (!have_simple_1);
+                       have_simple_1 = TRUE;
+               } else if (g_str_equal (uid, "simple-2")) {
+                       g_assert (expect_simple_2);
+                       g_assert (!have_simple_2);
+                       have_simple_2 = TRUE;
                } else {
                        /* It's not supposed to be NULL, but it will print the value of 'uid' */
                        g_assert_cmpstr (uid, ==, NULL);
@@ -88,29 +115,36 @@ test_check_search_result (const GSList *list,
 
        g_assert ((expect_custom_1 && have_custom_1) || (!expect_custom_1 && !have_custom_1));
        g_assert ((expect_custom_9 && have_custom_9) || (!expect_custom_9 && !have_custom_9));
+       g_assert ((expect_simple_1 && have_simple_1) || (!expect_simple_1 && !have_simple_1));
+       g_assert ((expect_simple_2 && have_simple_2) || (!expect_simple_2 && !have_simple_2));
        g_assert (have_custom_3);
 }
 
 static void
 test_basic_cursor (TCUFixture *fixture,
-                  gboolean expect_custom_1,
-                  gboolean expect_custom_9,
+                  guint32 flags,
                   const gchar *sexp)
 {
        EContactField sort_fields[] = { E_CONTACT_FAMILY_NAME, E_CONTACT_GIVEN_NAME };
        EBookCursorSortType sort_types[] = { E_BOOK_CURSOR_SORT_ASCENDING, E_BOOK_CURSOR_SORT_ASCENDING };
        EBookCacheCursor *cursor;
-       gint total = -1, position = -1;
+       gint total = -1, position = -1, expect_total;
        GSList *list;
        GError *error = NULL;
 
+       expect_total = 1 +
+               (((flags & EXPECT_CUSTOM_1) != 0) ? 1 : 0) +
+               (((flags & EXPECT_CUSTOM_9) != 0) ? 1 : 0) +
+               (((flags & EXPECT_SIMPLE_1) != 0) ? 1 : 0) +
+               (((flags & EXPECT_SIMPLE_2) != 0) ? 1 : 0);
+
        cursor = e_book_cache_cursor_new (fixture->book_cache, sexp, sort_fields, sort_types, 2, &error);
        g_assert_no_error (error);
        g_assert_nonnull (cursor);
 
        g_assert (e_book_cache_cursor_calculate (fixture->book_cache, cursor, &total, &position, NULL, 
&error));
        g_assert_no_error (error);
-       g_assert_cmpint (total, ==, 1 + (expect_custom_1 ? 1 : 0) + (expect_custom_9 ? 1 : 0));
+       g_assert_cmpint (total, ==, expect_total);
        g_assert_cmpint (position, ==, 0);
 
        g_assert_cmpint (e_book_cache_cursor_step (fixture->book_cache, cursor, 
E_BOOK_CACHE_CURSOR_STEP_FETCH,
@@ -118,7 +152,7 @@ test_basic_cursor (TCUFixture *fixture,
        g_assert_no_error (error);
        g_assert_cmpint (g_slist_length (list), ==, total);
 
-       test_check_search_result (list, expect_custom_1, expect_custom_9, TRUE, FALSE);
+       test_check_search_result (list, flags | HAS_SEARCH_DATA);
 
        g_slist_free_full (list, e_book_cache_search_data_free);
        e_book_cache_cursor_free (fixture->book_cache, cursor);
@@ -126,36 +160,42 @@ test_basic_cursor (TCUFixture *fixture,
 
 static void
 test_basic_search (TCUFixture *fixture,
-                  gboolean expect_custom_1)
+                  guint32 flags)
 {
        EBookQuery *query;
        GSList *list = NULL;
        gchar *sexp;
+       gint expect_total;
        GError *error = NULL;
 
+       expect_total = 2 +
+               ((flags & EXPECT_CUSTOM_1) != 0 ? 1 : 0) +
+               ((flags & EXPECT_SIMPLE_1) != 0 ? 1 : 0) +
+               ((flags & EXPECT_SIMPLE_2) != 0 ? 1 : 0);
+
        /* All contacts first */
        g_assert (e_book_cache_search (fixture->book_cache, NULL, FALSE, &list, NULL, &error));
        g_assert_no_error (error);
-       g_assert_cmpint (g_slist_length (list), ==, expect_custom_1 ? 3 : 2);
-       test_check_search_result (list, expect_custom_1, TRUE, TRUE, FALSE);
+       g_assert_cmpint (g_slist_length (list), ==, expect_total);
+       test_check_search_result (list, flags | EXPECT_CUSTOM_9 | HAS_SEARCH_DATA);
        g_slist_free_full (list, e_book_cache_search_data_free);
        list = NULL;
 
        g_assert (e_book_cache_search (fixture->book_cache, NULL, TRUE, &list, NULL, &error));
        g_assert_no_error (error);
-       g_assert_cmpint (g_slist_length (list), ==, expect_custom_1 ? 3 : 2);
-       test_check_search_result (list, expect_custom_1, TRUE, TRUE, TRUE);
+       g_assert_cmpint (g_slist_length (list), ==, expect_total);
+       test_check_search_result (list, flags | EXPECT_CUSTOM_9 | HAS_SEARCH_DATA | HAS_META_CONTACTS);
        g_slist_free_full (list, e_book_cache_search_data_free);
        list = NULL;
 
        g_assert (e_book_cache_search_uids (fixture->book_cache, NULL, &list, NULL, &error));
        g_assert_no_error (error);
-       g_assert_cmpint (g_slist_length (list), ==, expect_custom_1 ? 3 : 2);
-       test_check_search_result (list, expect_custom_1, TRUE, FALSE, FALSE);
+       g_assert_cmpint (g_slist_length (list), ==, expect_total);
+       test_check_search_result (list, flags | EXPECT_CUSTOM_9);
        g_slist_free_full (list, g_free);
        list = NULL;
 
-       test_basic_cursor (fixture, expect_custom_1, TRUE, NULL);
+       test_basic_cursor (fixture, flags | EXPECT_CUSTOM_9, NULL);
 
        /* Only Brown, aka custom-3, as an autocomplete query */
        query = e_book_query_field_test (E_CONTACT_FULL_NAME, E_BOOK_QUERY_CONTAINS, "Brown");
@@ -165,25 +205,25 @@ test_basic_search (TCUFixture *fixture,
        g_assert (e_book_cache_search (fixture->book_cache, sexp, FALSE, &list, NULL, &error));
        g_assert_no_error (error);
        g_assert_cmpint (g_slist_length (list), ==, 1);
-       test_check_search_result (list, FALSE, FALSE, TRUE, FALSE);
+       test_check_search_result (list, HAS_SEARCH_DATA);
        g_slist_free_full (list, e_book_cache_search_data_free);
        list = NULL;
 
        g_assert (e_book_cache_search (fixture->book_cache, sexp, TRUE, &list, NULL, &error));
        g_assert_no_error (error);
        g_assert_cmpint (g_slist_length (list), ==, 1);
-       test_check_search_result (list, FALSE, FALSE, TRUE, TRUE);
+       test_check_search_result (list, HAS_SEARCH_DATA | HAS_META_CONTACTS);
        g_slist_free_full (list, e_book_cache_search_data_free);
        list = NULL;
 
        g_assert (e_book_cache_search_uids (fixture->book_cache, sexp, &list, NULL, &error));
        g_assert_no_error (error);
        g_assert_cmpint (g_slist_length (list), ==, 1);
-       test_check_search_result (list, FALSE, FALSE, FALSE, FALSE);
+       test_check_search_result (list, EXPECT_DEFAULT);
        g_slist_free_full (list, g_free);
        list = NULL;
 
-       test_basic_cursor (fixture, FALSE, FALSE, sexp);
+       test_basic_cursor (fixture, EXPECT_DEFAULT, sexp);
 
        g_free (sexp);
 
@@ -195,25 +235,25 @@ test_basic_search (TCUFixture *fixture,
        g_assert (e_book_cache_search (fixture->book_cache, sexp, FALSE, &list, NULL, &error));
        g_assert_no_error (error);
        g_assert_cmpint (g_slist_length (list), ==, 1);
-       test_check_search_result (list, FALSE, FALSE, TRUE, FALSE);
+       test_check_search_result (list, HAS_SEARCH_DATA);
        g_slist_free_full (list, e_book_cache_search_data_free);
        list = NULL;
 
        g_assert (e_book_cache_search (fixture->book_cache, sexp, TRUE, &list, NULL, &error));
        g_assert_no_error (error);
        g_assert_cmpint (g_slist_length (list), ==, 1);
-       test_check_search_result (list, FALSE, FALSE, TRUE, TRUE);
+       test_check_search_result (list, HAS_SEARCH_DATA | HAS_META_CONTACTS);
        g_slist_free_full (list, e_book_cache_search_data_free);
        list = NULL;
 
        g_assert (e_book_cache_search_uids (fixture->book_cache, sexp, &list, NULL, &error));
        g_assert_no_error (error);
        g_assert_cmpint (g_slist_length (list), ==, 1);
-       test_check_search_result (list, FALSE, FALSE, FALSE, FALSE);
+       test_check_search_result (list, EXPECT_DEFAULT);
        g_slist_free_full (list, g_free);
        list = NULL;
 
-       test_basic_cursor (fixture, FALSE, FALSE, sexp);
+       test_basic_cursor (fixture, EXPECT_DEFAULT, sexp);
 
        g_free (sexp);
 
@@ -229,12 +269,12 @@ test_basic_search (TCUFixture *fixture,
 
 /* Expects pairs of UID (gchar *) and EOfflineState (gint), terminated by NULL */
 static void
-test_check_offline_states (TCUFixture *fixture,
-                          ...) G_GNUC_NULL_TERMINATED;
+test_check_offline_changes (TCUFixture *fixture,
+                           ...) G_GNUC_NULL_TERMINATED;
 
 static void
-test_check_offline_states (TCUFixture *fixture,
-                          ...)
+test_check_offline_changes (TCUFixture *fixture,
+                           ...)
 {
        GSList *changes, *link;
        va_list args;
@@ -275,6 +315,50 @@ test_check_offline_states (TCUFixture *fixture,
        g_hash_table_destroy (expects);
 }
 
+static EOfflineState
+test_check_offline_state (TCUFixture *fixture,
+                         const gchar *uid,
+                         EOfflineState expect_offline_state)
+{
+       EOfflineState offline_state;
+       GError *error = NULL;
+
+       offline_state = e_cache_get_offline_state (E_CACHE (fixture->book_cache), uid, NULL, &error);
+       g_assert_cmpint (offline_state, ==, expect_offline_state);
+
+       if (offline_state == E_OFFLINE_STATE_UNKNOWN) {
+               g_assert_error (error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND);
+               g_clear_error (&error);
+       } else {
+               g_assert_no_error (error);
+       }
+
+       return offline_state;
+}
+
+static void
+test_check_edit_saved (TCUFixture *fixture,
+                      const gchar *uid,
+                      const gchar *rev_value)
+{
+       EContact *contact = NULL;
+       GError *error = NULL;
+
+       g_assert (e_book_cache_get_contact (fixture->book_cache, uid, FALSE, &contact, NULL, &error));
+       g_assert_no_error (error);
+       g_assert_nonnull (contact);
+       g_assert_cmpstr (e_contact_get_const (contact, E_CONTACT_REV), ==, rev_value);
+
+       g_clear_object (&contact);
+
+       g_assert (e_book_cache_get_contact (fixture->book_cache, uid, TRUE, &contact, NULL, &error));
+       g_assert_no_error (error);
+       g_assert_nonnull (contact);
+       g_assert_cmpstr (e_contact_get_const (contact, E_CONTACT_REV), ==, rev_value);
+
+       g_clear_object (&contact);
+}
+
 static void
 test_verify_storage (TCUFixture *fixture,
                     const gchar *uid,
@@ -300,19 +384,12 @@ test_verify_storage (TCUFixture *fixture,
                g_assert_nonnull (contact);
        }
 
-       offline_state = e_cache_get_offline_state (E_CACHE (fixture->book_cache), uid, NULL, &error);
-       if (offline_state == E_OFFLINE_STATE_UNKNOWN) {
-               g_assert_error (error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND);
-               g_clear_error (&error);
-       } else {
-               g_assert_no_error (error);
-       }
-       g_assert_cmpint (offline_state, ==, expect_offline_state);
+       offline_state = test_check_offline_state (fixture, uid, expect_offline_state);
 
        if (offline_state == E_OFFLINE_STATE_UNKNOWN) {
                g_assert (!e_cache_contains (E_CACHE (fixture->book_cache), uid, TRUE));
                g_assert (!e_cache_contains (E_CACHE (fixture->book_cache), uid, FALSE));
-               test_check_offline_states (fixture, NULL);
+               test_check_offline_changes (fixture, NULL);
                return;
        }
 
@@ -336,9 +413,9 @@ test_verify_storage (TCUFixture *fixture,
        g_free (saved_extra);
 
        if (expect_offline_state == E_OFFLINE_STATE_SYNCED)
-               test_check_offline_states (fixture, NULL);
+               test_check_offline_changes (fixture, NULL);
        else
-               test_check_offline_states (fixture, uid, expect_offline_state, NULL);
+               test_check_offline_changes (fixture, uid, expect_offline_state, NULL);
 }
 
 static void
@@ -351,7 +428,6 @@ test_offline_basics (TCUFixture *fixture,
                E_OFFLINE_STATE_LOCALLY_DELETED,
                E_OFFLINE_STATE_SYNCED
        };
-       EOfflineState offline_state;
        EContact *contact = NULL;
        gint ii;
        const gchar *uid;
@@ -405,23 +481,19 @@ test_offline_basics (TCUFixture *fixture,
 
        e_contact_set (contact, E_CONTACT_REV, "rev-0");
 
-       offline_state = e_cache_get_offline_state (E_CACHE (fixture->book_cache), uid, NULL, &error);
-       g_assert_no_error (error);
-       g_assert (offline_state == E_OFFLINE_STATE_SYNCED);
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_SYNCED);
 
-       test_check_offline_states (fixture, NULL);
+       test_check_offline_changes (fixture, NULL);
 
        /* Try change status */
        for (ii = 0; ii < G_N_ELEMENTS (states); ii++) {
                g_assert (e_cache_set_offline_state (E_CACHE (fixture->book_cache), uid, states[ii], NULL, 
&error));
                g_assert_no_error (error);
 
-               offline_state = e_cache_get_offline_state (E_CACHE (fixture->book_cache), uid, NULL, &error);
-               g_assert_no_error (error);
-               g_assert (offline_state == states[ii]);
+               test_check_offline_state (fixture, uid, states[ii]);
 
                if (states[ii] != E_OFFLINE_STATE_SYNCED)
-                       test_check_offline_states (fixture, uid, states[ii], NULL);
+                       test_check_offline_changes (fixture, uid, states[ii], NULL);
 
                if (states[ii] == E_OFFLINE_STATE_LOCALLY_DELETED) {
                        g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), 
==, 2);
@@ -440,7 +512,7 @@ test_offline_basics (TCUFixture *fixture,
                        saved_extra = NULL;
 
                        /* Search when locally deleted */
-                       test_basic_search (fixture, FALSE);
+                       test_basic_search (fixture, EXPECT_DEFAULT);
                } else {
                        g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), 
==, 3);
                        g_assert_no_error (error);
@@ -448,7 +520,7 @@ test_offline_basics (TCUFixture *fixture,
                        g_assert (e_cache_contains (E_CACHE (fixture->book_cache), uid, FALSE));
 
                        /* Search when locally available */
-                       test_basic_search (fixture, TRUE);
+                       test_basic_search (fixture, EXPECT_CUSTOM_1);
                }
 
                g_assert (e_cache_contains (E_CACHE (fixture->book_cache), uid, TRUE));
@@ -457,7 +529,7 @@ test_offline_basics (TCUFixture *fixture,
                g_assert_no_error (error);
        }
 
-       test_check_offline_states (fixture, NULL);
+       test_check_offline_changes (fixture, NULL);
 
        /* Edit in online */
        e_contact_set (contact, E_CONTACT_REV, "rev-1");
@@ -466,7 +538,7 @@ test_offline_basics (TCUFixture *fixture,
        g_assert_no_error (error);
 
        test_verify_storage (fixture, uid, "rev-1", NULL, E_OFFLINE_STATE_SYNCED);
-       test_check_offline_states (fixture, NULL);
+       test_check_offline_changes (fixture, NULL);
 
        e_contact_set (contact, E_CONTACT_REV, "rev-2");
 
@@ -474,13 +546,13 @@ test_offline_basics (TCUFixture *fixture,
        g_assert_no_error (error);
 
        test_verify_storage (fixture, uid, "rev-2", "extra-2", E_OFFLINE_STATE_SYNCED);
-       test_check_offline_states (fixture, NULL);
+       test_check_offline_changes (fixture, NULL);
 
        g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 3);
        g_assert_no_error (error);
 
        /* Search before delete */
-       test_basic_search (fixture, TRUE);
+       test_basic_search (fixture, EXPECT_CUSTOM_1);
 
        /* Delete in online */
        g_assert (e_book_cache_remove_contact (fixture->book_cache, uid, FALSE, NULL, &error));
@@ -490,12 +562,13 @@ test_offline_basics (TCUFixture *fixture,
        g_assert_error (error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND);
 
        test_verify_storage (fixture, uid, NULL, NULL, E_OFFLINE_STATE_UNKNOWN);
-       test_check_offline_states (fixture, NULL);
+       test_check_offline_changes (fixture, NULL);
 
        g_clear_object (&contact);
        g_clear_error (&error);
 
        g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 2);
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), TRUE, NULL, &error), ==, 2);
        g_assert_no_error (error);
 
        g_assert (!e_book_cache_set_contact_extra (fixture->book_cache, uid, "extra-3", NULL, &error));
@@ -508,7 +581,515 @@ test_offline_basics (TCUFixture *fixture,
        g_clear_error (&error);
 
        /* Search after delete */
-       test_basic_search (fixture, FALSE);
+       test_basic_search (fixture, EXPECT_DEFAULT);
+}
+
+static void
+test_offline_add_one (TCUFixture *fixture,
+                     const gchar *case_name,
+                     gint expect_total,
+                     guint32 flags,
+                     EContact **out_contact)
+{
+       EContact *contact = NULL;
+       const gchar *uid;
+       GError *error = NULL;
+
+       if (!(flags & SKIP_CONTACT_PUT)) {
+               contact = tcu_new_contact_from_test_case (case_name);
+               g_assert_nonnull (contact);
+
+               uid = e_contact_get_const (contact, E_CONTACT_UID);
+               g_assert_nonnull (uid);
+
+               test_check_offline_state (fixture, uid, E_OFFLINE_STATE_UNKNOWN);
+
+               /* Add a contact in offline */
+               g_assert (e_book_cache_put_contact (fixture->book_cache, contact, NULL, TRUE, NULL, &error));
+               g_assert_no_error (error);
+       } else {
+               uid = case_name;
+       }
+
+       if ((flags & EXPECT_SIMPLE_1) != 0) {
+               test_check_offline_state (fixture, uid, E_OFFLINE_STATE_LOCALLY_CREATED);
+       } else {
+               test_check_offline_state (fixture, uid, E_OFFLINE_STATE_UNKNOWN);
+       }
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 
expect_total);
+       g_assert_no_error (error);
+
+       test_basic_search (fixture, flags);
+
+       if (out_contact)
+               *out_contact = contact;
+       else
+               g_clear_object (&contact);
+}
+
+static void
+test_offline_add (TCUFixture *fixture,
+                 gconstpointer user_data)
+{
+       GError *error = NULL;
+
+       /* Add in online */
+       test_fill_cache (fixture, NULL);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 3);
+       g_assert_no_error (error);
+
+       test_check_offline_changes (fixture, NULL);
+
+       /* Add the first in offline */
+       test_offline_add_one (fixture, "simple-1", 4, EXPECT_SIMPLE_1 | EXPECT_CUSTOM_1, NULL);
+
+       test_check_offline_changes (fixture,
+               "simple-1", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+
+       /* Add the second in offline */
+       test_offline_add_one (fixture, "simple-2", 5, EXPECT_SIMPLE_1 | EXPECT_SIMPLE_2 | EXPECT_CUSTOM_1, 
NULL);
+
+       test_check_offline_changes (fixture,
+               "simple-1", E_OFFLINE_STATE_LOCALLY_CREATED,
+               "simple-2", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+}
+
+static void
+test_offline_add_edit (TCUFixture *fixture,
+                      gconstpointer user_data)
+{
+       EContact *contact = NULL;
+       GError *error = NULL;
+
+       /* Add in online */
+       test_fill_cache (fixture, NULL);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 3);
+       g_assert_no_error (error);
+
+       test_check_offline_changes (fixture, NULL);
+
+       /* Add in offline */
+       test_offline_add_one (fixture, "simple-1", 4, EXPECT_SIMPLE_1 | EXPECT_CUSTOM_1, &contact);
+       g_assert_nonnull (contact);
+
+       test_check_offline_changes (fixture,
+               "simple-1", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+
+       /* Modify added in offline */
+       e_contact_set (contact, E_CONTACT_REV, "rev-2");
+
+       g_assert (e_book_cache_put_contact (fixture->book_cache, contact, NULL, TRUE, NULL, &error));
+       g_assert_no_error (error);
+
+       test_offline_add_one (fixture, "simple-1", 4, EXPECT_SIMPLE_1 | EXPECT_CUSTOM_1 | SKIP_CONTACT_PUT, 
NULL);
+
+       test_check_offline_changes (fixture,
+               "simple-1", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+
+       test_check_edit_saved (fixture, "simple-1", "rev-2");
+
+       g_clear_object (&contact);
+}
+
+static void
+test_offline_add_delete (TCUFixture *fixture,
+                        gconstpointer user_data)
+{
+       EContact *contact = NULL;
+       const gchar *uid;
+       GError *error = NULL;
+
+       /* Add in online */
+       test_fill_cache (fixture, NULL);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 3);
+       g_assert_no_error (error);
+
+       test_check_offline_changes (fixture, NULL);
+
+       /* Add in offline */
+       test_offline_add_one (fixture, "simple-1", 4, EXPECT_SIMPLE_1 | EXPECT_CUSTOM_1, &contact);
+       g_assert_nonnull (contact);
+
+       test_check_offline_changes (fixture,
+               "simple-1", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+
+       uid = e_contact_get_const (contact, E_CONTACT_UID);
+       g_assert_nonnull (uid);
+
+       /* Delete added in offline */
+
+       g_assert (e_book_cache_remove_contact (fixture->book_cache, uid, TRUE, NULL, &error));
+       g_assert_no_error (error);
+
+       test_offline_add_one (fixture, "simple-1", 3, EXPECT_CUSTOM_1 | SKIP_CONTACT_PUT, NULL);
+
+       test_check_offline_changes (fixture, NULL);
+
+       g_clear_object (&contact);
+}
+
+static void
+test_offline_add_delete_add (TCUFixture *fixture,
+                            gconstpointer user_data)
+{
+       EContact *contact = NULL;
+       const gchar *uid;
+       GError *error = NULL;
+
+       /* Add in online */
+       test_fill_cache (fixture, NULL);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 3);
+       g_assert_no_error (error);
+
+       test_check_offline_changes (fixture, NULL);
+
+       /* Add in offline */
+       test_offline_add_one (fixture, "simple-1", 4, EXPECT_SIMPLE_1 | EXPECT_CUSTOM_1, &contact);
+       g_assert_nonnull (contact);
+
+       test_check_offline_changes (fixture,
+               "simple-1", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+
+       uid = e_contact_get_const (contact, E_CONTACT_UID);
+       g_assert_nonnull (uid);
+
+       /* Delete added in offline */
+       g_assert (e_book_cache_remove_contact (fixture->book_cache, uid, TRUE, NULL, &error));
+       g_assert_no_error (error);
+
+       test_offline_add_one (fixture, "simple-1", 3, EXPECT_CUSTOM_1 | SKIP_CONTACT_PUT, NULL);
+
+       test_check_offline_changes (fixture, NULL);
+
+       g_clear_object (&contact);
+
+       /* Add in offline again */
+       test_offline_add_one (fixture, "simple-1", 4, EXPECT_SIMPLE_1 | EXPECT_CUSTOM_1, NULL);
+
+       test_check_offline_changes (fixture,
+               "simple-1", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+}
+
+static void
+test_offline_add_resync (TCUFixture *fixture,
+                        gconstpointer user_data)
+{
+       GError *error = NULL;
+
+       /* Add in online */
+       test_fill_cache (fixture, NULL);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 3);
+       g_assert_no_error (error);
+
+       test_check_offline_changes (fixture, NULL);
+
+       /* Add in offline */
+       test_offline_add_one (fixture, "simple-1", 4, EXPECT_SIMPLE_1 | EXPECT_CUSTOM_1, NULL);
+
+       test_check_offline_changes (fixture,
+               "simple-1", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+
+       /* Resync all offline changes */
+       g_assert (e_cache_clear_offline_changes (E_CACHE (fixture->book_cache), NULL, &error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 4);
+       g_assert_no_error (error);
+
+       test_basic_search (fixture, EXPECT_SIMPLE_1 | EXPECT_CUSTOM_1);
+       test_check_offline_changes (fixture, NULL);
+       test_check_offline_state (fixture, "simple-1", E_OFFLINE_STATE_SYNCED);
+}
+
+static void
+test_offline_edit_common (TCUFixture *fixture,
+                         gchar **out_uid)
+{
+       EContact *contact = NULL;
+       const gchar *uid;
+       GError *error = NULL;
+
+       /* Add in online */
+       test_fill_cache (fixture, &contact);
+       g_assert_nonnull (contact);
+
+       uid = e_contact_get_const (contact, E_CONTACT_UID);
+       g_assert_nonnull (uid);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 3);
+       g_assert_no_error (error);
+
+       test_check_offline_changes (fixture, NULL);
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_SYNCED);
+
+       /* Modify in offline */
+       e_contact_set (contact, E_CONTACT_REV, "rev-2");
+
+       g_assert (e_book_cache_put_contact (fixture->book_cache, contact, NULL, TRUE, NULL, &error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 3);
+       g_assert_no_error (error);
+
+       test_check_edit_saved (fixture, uid, "rev-2");
+
+       test_basic_search (fixture, EXPECT_CUSTOM_1);
+       test_check_offline_changes (fixture,
+               uid, E_OFFLINE_STATE_LOCALLY_MODIFIED,
+               NULL);
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_LOCALLY_MODIFIED);
+
+       if (out_uid)
+               *out_uid = g_strdup (uid);
+
+       g_clear_object (&contact);
+}
+
+static void
+test_offline_edit (TCUFixture *fixture,
+                  gconstpointer user_data)
+{
+       test_offline_edit_common (fixture, NULL);
+}
+
+static void
+test_offline_edit_delete (TCUFixture *fixture,
+                         gconstpointer user_data)
+{
+       EContact *contact = NULL;
+       gchar *uid = NULL;
+       GError *error = NULL;
+
+       test_offline_edit_common (fixture, &uid);
+
+       /* Delete the modified contact in offline */
+       g_assert (e_book_cache_remove_contact (fixture->book_cache, uid, TRUE, NULL, &error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 2);
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), TRUE, NULL, &error), ==, 3);
+       g_assert_no_error (error);
+
+       test_basic_search (fixture, EXPECT_DEFAULT);
+       test_check_offline_changes (fixture,
+               uid, E_OFFLINE_STATE_LOCALLY_DELETED,
+               NULL);
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_LOCALLY_DELETED);
+
+       g_assert (!e_book_cache_get_contact (fixture->book_cache, uid, FALSE, &contact, NULL, &error));
+       g_assert_error (error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND);
+       g_assert_null (contact);
+
+       g_clear_error (&error);
+       g_free (uid);
+}
+
+static void
+test_offline_edit_resync (TCUFixture *fixture,
+                         gconstpointer user_data)
+{
+       gchar *uid = NULL;
+       GError *error = NULL;
+
+       test_offline_edit_common (fixture, &uid);
+
+       /* Resync all offline changes */
+       g_assert (e_cache_clear_offline_changes (E_CACHE (fixture->book_cache), NULL, &error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 3);
+       g_assert_no_error (error);
+
+       test_basic_search (fixture, EXPECT_CUSTOM_1);
+       test_check_offline_changes (fixture, NULL);
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_SYNCED);
+
+       g_free (uid);
+}
+
+static void
+test_offline_delete (TCUFixture *fixture,
+                    gconstpointer user_data)
+{
+       EContact *contact = NULL;
+       const gchar *uid;
+       GError *error = NULL;
+
+       /* Add in online */
+       test_fill_cache (fixture, &contact);
+       g_assert_nonnull (contact);
+
+       uid = e_contact_get_const (contact, E_CONTACT_UID);
+       g_assert_nonnull (uid);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 3);
+       g_assert_no_error (error);
+
+       test_check_offline_changes (fixture, NULL);
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_SYNCED);
+
+       /* Delete in offline */
+       g_assert (e_book_cache_remove_contact (fixture->book_cache, uid, TRUE, NULL, &error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 2);
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), TRUE, NULL, &error), ==, 3);
+       g_assert_no_error (error);
+
+       test_basic_search (fixture, EXPECT_DEFAULT);
+       test_check_offline_changes (fixture,
+               uid, E_OFFLINE_STATE_LOCALLY_DELETED,
+               NULL);
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_LOCALLY_DELETED);
+
+       g_clear_object (&contact);
+}
+
+static void
+test_offline_delete_add (TCUFixture *fixture,
+                        gconstpointer user_data)
+{
+       EContact *contact = NULL;
+       const gchar *uid;
+       GError *error = NULL;
+
+       /* Add in online */
+       test_fill_cache (fixture, &contact);
+       g_assert_nonnull (contact);
+
+       uid = e_contact_get_const (contact, E_CONTACT_UID);
+       g_assert_nonnull (uid);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 3);
+       g_assert_no_error (error);
+
+       test_check_offline_changes (fixture, NULL);
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_SYNCED);
+
+       /* Delete locally created in offline */
+       test_offline_add_one (fixture, "simple-1", 4, EXPECT_SIMPLE_1 | EXPECT_CUSTOM_1, NULL);
+       g_assert (e_book_cache_remove_contact (fixture->book_cache, "simple-1", TRUE, NULL, &error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 3);
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), TRUE, NULL, &error), ==, 3);
+       g_assert_no_error (error);
+
+       test_basic_search (fixture, EXPECT_CUSTOM_1);
+       test_check_offline_changes (fixture, NULL);
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_SYNCED);
+       test_check_offline_state (fixture, "simple-1", E_OFFLINE_STATE_UNKNOWN);
+
+       /* Delete synced in offline */
+       g_assert (e_book_cache_remove_contact (fixture->book_cache, uid, TRUE, NULL, &error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 2);
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), TRUE, NULL, &error), ==, 3);
+       g_assert_no_error (error);
+
+       test_basic_search (fixture, EXPECT_DEFAULT);
+       test_check_offline_changes (fixture,
+               uid, E_OFFLINE_STATE_LOCALLY_DELETED,
+               NULL);
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_LOCALLY_DELETED);
+
+       /* Add one in offline */
+       test_offline_add_one (fixture, "simple-1", 3, EXPECT_SIMPLE_1, NULL);
+
+       test_check_offline_changes (fixture,
+               uid, E_OFFLINE_STATE_LOCALLY_DELETED,
+               "simple-1", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_LOCALLY_DELETED);
+       test_check_offline_state (fixture, "simple-1", E_OFFLINE_STATE_LOCALLY_CREATED);
+
+       /* Modify the previous contact and add it again */
+       e_contact_set (contact, E_CONTACT_REV, "rev-3");
+
+       g_assert (e_book_cache_put_contact (fixture->book_cache, contact, NULL, TRUE, NULL, &error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 4);
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), TRUE, NULL, &error), ==, 4);
+       g_assert_no_error (error);
+
+       test_check_edit_saved (fixture, uid, "rev-3");
+
+       test_basic_search (fixture, EXPECT_CUSTOM_1 | EXPECT_SIMPLE_1);
+       test_check_offline_changes (fixture,
+               uid, E_OFFLINE_STATE_LOCALLY_MODIFIED,
+               "simple-1", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_LOCALLY_MODIFIED);
+       test_check_offline_state (fixture, "simple-1", E_OFFLINE_STATE_LOCALLY_CREATED);
+
+       g_clear_object (&contact);
+}
+
+static void
+test_offline_delete_resync (TCUFixture *fixture,
+                           gconstpointer user_data)
+{
+       EContact *contact = NULL;
+       const gchar *uid;
+       GError *error = NULL;
+
+       /* Add in online */
+       test_fill_cache (fixture, &contact);
+       g_assert_nonnull (contact);
+
+       uid = e_contact_get_const (contact, E_CONTACT_UID);
+       g_assert_nonnull (uid);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 3);
+       g_assert_no_error (error);
+
+       test_check_offline_changes (fixture, NULL);
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_SYNCED);
+
+       /* Delete in offline */
+       g_assert (e_book_cache_remove_contact (fixture->book_cache, uid, TRUE, NULL, &error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 2);
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), TRUE, NULL, &error), ==, 3);
+       g_assert_no_error (error);
+
+       test_basic_search (fixture, EXPECT_DEFAULT);
+       test_check_offline_changes (fixture,
+               uid, E_OFFLINE_STATE_LOCALLY_DELETED,
+               NULL);
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_LOCALLY_DELETED);
+
+       /* Resync all offline changes */
+       e_cache_clear_offline_changes (E_CACHE (fixture->book_cache), NULL, &error);
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), FALSE, NULL, &error), ==, 2);
+       g_assert_cmpint (e_cache_count (E_CACHE (fixture->book_cache), TRUE, NULL, &error), ==, 2);
+       g_assert_no_error (error);
+
+       test_basic_search (fixture, EXPECT_DEFAULT);
+       test_check_offline_changes (fixture, NULL);
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_UNKNOWN);
+
+       g_clear_object (&contact);
 }
 
 gint
@@ -528,28 +1109,28 @@ main (gint argc,
 
        g_test_add ("/EBookCache/Offline/Basics", TCUFixture, &closure,
                tcu_fixture_setup, test_offline_basics, tcu_fixture_teardown);
-       /*g_test_add ("/EBookCache/Offline/Add", TCUFixture, closure,
+       g_test_add ("/EBookCache/Offline/Add", TCUFixture, &closure,
                tcu_fixture_setup, test_offline_add, tcu_fixture_teardown);
-       g_test_add ("/EBookCache/Offline/AddEdit", TCUFixture, closure,
+       g_test_add ("/EBookCache/Offline/AddEdit", TCUFixture, &closure,
                tcu_fixture_setup, test_offline_add_edit, tcu_fixture_teardown);
-       g_test_add ("/EBookCache/Offline/AddDelete", TCUFixture, closure,
+       g_test_add ("/EBookCache/Offline/AddDelete", TCUFixture, &closure,
                tcu_fixture_setup, test_offline_add_delete, tcu_fixture_teardown);
-       g_test_add ("/EBookCache/Offline/AddDeleteAdd", TCUFixture, closure,
+       g_test_add ("/EBookCache/Offline/AddDeleteAdd", TCUFixture, &closure,
                tcu_fixture_setup, test_offline_add_delete_add, tcu_fixture_teardown);
-       g_test_add ("/EBookCache/Offline/AddResync", TCUFixture, closure,
+       g_test_add ("/EBookCache/Offline/AddResync", TCUFixture, &closure,
                tcu_fixture_setup, test_offline_add_resync, tcu_fixture_teardown);
-       g_test_add ("/EBookCache/Offline/Edit", TCUFixture, closure,
+       g_test_add ("/EBookCache/Offline/Edit", TCUFixture, &closure,
                tcu_fixture_setup, test_offline_edit, tcu_fixture_teardown);
-       g_test_add ("/EBookCache/Offline/EditDelete", TCUFixture, closure,
+       g_test_add ("/EBookCache/Offline/EditDelete", TCUFixture, &closure,
                tcu_fixture_setup, test_offline_edit_delete, tcu_fixture_teardown);
-       g_test_add ("/EBookCache/Offline/EditResync", TCUFixture, closure,
+       g_test_add ("/EBookCache/Offline/EditResync", TCUFixture, &closure,
                tcu_fixture_setup, test_offline_edit_resync, tcu_fixture_teardown);
-       g_test_add ("/EBookCache/Offline/Delete", TCUFixture, closure,
+       g_test_add ("/EBookCache/Offline/Delete", TCUFixture, &closure,
                tcu_fixture_setup, test_offline_delete, tcu_fixture_teardown);
-       g_test_add ("/EBookCache/Offline/DeleteAdd", TCUFixture, closure,
+       g_test_add ("/EBookCache/Offline/DeleteAdd", TCUFixture, &closure,
                tcu_fixture_setup, test_offline_delete_add, tcu_fixture_teardown);
-       g_test_add ("/EBookCache/Offline/DeleteResync", TCUFixture, closure,
-               tcu_fixture_setup, test_offline_delete_resync, tcu_fixture_teardown);*/
+       g_test_add ("/EBookCache/Offline/DeleteResync", TCUFixture, &closure,
+               tcu_fixture_setup, test_offline_delete_resync, tcu_fixture_teardown);
 
        return g_test_run ();
 }



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