[evolution-data-server/wip/offline-cache] Add first few tests for ECalCache



commit 2a29e19334fa37240ac5c3fd4d553d6b0fc1f0b1
Author: Milan Crha <mcrha redhat com>
Date:   Fri Feb 17 12:56:16 2017 +0100

    Add first few tests for ECalCache

 src/calendar/libedata-cal/e-cal-backend-sexp.c     |    3 -
 src/calendar/libedata-cal/e-cal-cache.c            |  144 ++-
 src/calendar/libedata-cal/e-cal-cache.h            |   18 +-
 src/libebackend/e-cache.c                          |    2 +-
 tests/libedata-book/CMakeLists.txt                 |   42 +-
 tests/libedata-book/data-test-utils.h              |    4 +
 ...te-cursor.c => test-book-cache-create-cursor.c} |    2 +-
 ...culate.c => test-book-cache-cursor-calculate.c} |    2 +-
 ...le.c => test-book-cache-cursor-change-locale.c} |    2 +-
 ...DE.c => test-book-cache-cursor-move-by-de-DE.c} |    2 +-
 ...US.c => test-book-cache-cursor-move-by-en-US.c} |    2 +-
 ...CA.c => test-book-cache-cursor-move-by-fr-CA.c} |    2 +-
 ...ix.c => test-book-cache-cursor-move-by-posix.c} |    2 +-
 ...et-sexp.c => test-book-cache-cursor-set-sexp.c} |    2 +-
 ...arget.c => test-book-cache-cursor-set-target.c} |    2 +-
 ...get-contact.c => test-book-cache-get-contact.c} |    2 +-
 ...t-cache-offline.c => test-book-cache-offline.c} |    4 +-
 ...{test-cache-utils.c => test-book-cache-utils.c} |    2 +-
 ...{test-cache-utils.h => test-book-cache-utils.h} |   10 +-
 tests/libedata-cal/CMakeLists.txt                  |   67 ++-
 tests/libedata-cal/components/event-1.ics          |   18 +
 tests/libedata-cal/components/event-2.ics          |   12 +
 tests/libedata-cal/components/event-3.ics          |   11 +
 tests/libedata-cal/components/event-4.ics          |   11 +
 tests/libedata-cal/components/event-5.ics          |   18 +
 tests/libedata-cal/test-cal-cache-offline.c        | 1043 ++++++++++++++++++++
 tests/libedata-cal/test-cal-cache-utils.c          |  155 +++
 tests/libedata-cal/test-cal-cache-utils.h          |   46 +
 28 files changed, 1531 insertions(+), 99 deletions(-)
---
diff --git a/src/calendar/libedata-cal/e-cal-backend-sexp.c b/src/calendar/libedata-cal/e-cal-backend-sexp.c
index 4f53ef1..c2c96fe 100644
--- a/src/calendar/libedata-cal/e-cal-backend-sexp.c
+++ b/src/calendar/libedata-cal/e-cal-backend-sexp.c
@@ -1209,9 +1209,6 @@ e_cal_backend_sexp_new (const gchar *text)
        e_sexp_input_text (sexp->priv->search_sexp, text, strlen (text));
 
        if (e_sexp_parse (sexp->priv->search_sexp) == -1) {
-               g_warning (
-                       "%s: Error in parsing: %s",
-                       G_STRFUNC, e_sexp_get_error (sexp->priv->search_sexp));
                g_object_unref (sexp);
                sexp = NULL;
        }
diff --git a/src/calendar/libedata-cal/e-cal-cache.c b/src/calendar/libedata-cal/e-cal-cache.c
index 21a8dd4..cc82c34 100644
--- a/src/calendar/libedata-cal/e-cal-cache.c
+++ b/src/calendar/libedata-cal/e-cal-cache.c
@@ -231,7 +231,7 @@ ecc_check_sexp_func (sqlite3_context *context,
        const gchar *icalstring;
 
        g_return_if_fail (context != NULL);
-       g_return_if_fail (argc != 2);
+       g_return_if_fail (argc == 2);
 
        cal_cache = sqlite3_user_data (context);
        sexp_id = sqlite3_value_int (argv[0]);
@@ -578,6 +578,9 @@ ecc_extract_organizer (ECalComponent *comp)
 
        e_cal_component_get_organizer (comp, &org);
 
+       if (!org.value)
+               return NULL;
+
        value = g_string_new ("");
 
        ecc_encode_mail (value, org.cn, org.value);
@@ -1412,7 +1415,7 @@ ecc_search_foreach_cb (ECache *cache,
                }
        }
 
-       g_return_val_if_fail (ctx->extra_idx == -1, FALSE);
+       g_return_val_if_fail (ctx->extra_idx != -1, FALSE);
 
        g_warn_if_fail (ecc_decode_id_sql (uid, &comp_uid, &comp_rid));
 
@@ -1465,7 +1468,7 @@ ecc_init_aux_tables (ECalCache *cal_cache,
        gboolean success;
 
        stmt = e_cache_sqlite_stmt_printf ("CREATE TABLE IF NOT EXISTS %Q ("
-               "tzid TEXT PRIMARY INDEX, "
+               "tzid TEXT PRIMARY KEY, "
                "zone TEXT)",
                ECC_TABLE_TIMEZONES);
        success = e_cache_sqlite_exec (E_CACHE (cal_cache), stmt, cancellable, error);
@@ -1626,6 +1629,43 @@ e_cal_cache_dup_component_revision (ECalCache *cal_cache,
 }
 
 /**
+ * e_cal_cache_contains:
+ * @cal_cache: an #ECalCache
+ * @uid: component UID
+ * @rid: (nullable): optional component Recurrence-ID or %NULL
+ * @deleted_flag: one of #ECacheDeletedFlag enum
+ *
+ * Checkes whether the @cal_cache contains an object with
+ * the given @uid and @rid. The @rid can be an empty string
+ * or %NULL to search for the master object, otherwise the check
+ * is done for a detached instance, not for a recurrence instance.
+ *
+ * Returns: Whether the the object had been found.
+ *
+ * Since: 3.26
+ **/
+gboolean
+e_cal_cache_contains (ECalCache *cal_cache,
+                     const gchar *uid,
+                     const gchar *rid,
+                     ECacheDeletedFlag deleted_flag)
+{
+       gchar *id;
+       gboolean found;
+
+       g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
+
+       id = ecc_encode_id_sql (uid, rid);
+
+       found = e_cache_contains (E_CACHE (cal_cache), id, deleted_flag);
+
+       g_free (id);
+
+       return found;
+}
+
+/**
  * e_cal_cache_put_component:
  * @cal_cache: an #ECalCache
  * @component: an #ECalComponent to put into the @cal_cache
@@ -2245,6 +2285,27 @@ e_cal_cache_get_components_in_range_as_strings (ECalCache *cal_cache,
 }
 
 static gboolean
+ecc_search_data_cb (ECalCache *cal_cache,
+                   const gchar *uid,
+                   const gchar *rid,
+                   const gchar *revision,
+                   const gchar *object,
+                   const gchar *extra,
+                   EOfflineState offline_state,
+                   gpointer user_data)
+{
+       GSList **out_data = user_data;
+
+       g_return_val_if_fail (out_data != NULL, FALSE);
+       g_return_val_if_fail (object != NULL, FALSE);
+
+       *out_data = g_slist_prepend (*out_data,
+               e_cal_cache_search_data_new (uid, rid, object, extra));
+
+       return TRUE;
+}
+
+static gboolean
 ecc_search_components_cb (ECalCache *cal_cache,
                          const gchar *uid,
                          const gchar *rid,
@@ -2266,26 +2327,6 @@ ecc_search_components_cb (ECalCache *cal_cache,
 }
 
 static gboolean
-ecc_search_icalstrings_cb (ECalCache *cal_cache,
-                          const gchar *uid,
-                          const gchar *rid,
-                          const gchar *revision,
-                          const gchar *object,
-                          const gchar *extra,
-                          EOfflineState offline_state,
-                          gpointer user_data)
-{
-       GSList **out_icalstrings = user_data;
-
-       g_return_val_if_fail (out_icalstrings != NULL, FALSE);
-       g_return_val_if_fail (object != NULL, FALSE);
-
-       *out_icalstrings = g_slist_prepend (*out_icalstrings, g_strdup (object));
-
-       return TRUE;
-}
-
-static gboolean
 ecc_search_ids_cb (ECalCache *cal_cache,
                   const gchar *uid,
                   const gchar *rid,
@@ -2309,15 +2350,16 @@ ecc_search_ids_cb (ECalCache *cal_cache,
  * e_cal_cache_search:
  * @cal_cache: an #ECalCache
  * @sexp: (nullable): search expression; use %NULL or an empty string to list all stored components
- * @out_components: (out) (transfer full) (element-type ECalComponent): stored components satisfied by @sexp
+ * @out_data: (out) (transfer full) (element-type ECalCacheSearchData): stored components, as search data, 
satisfied by @sexp
  * @cancellable: optional #GCancellable object, or %NULL
  * @error: return location for a #GError, or %NULL
  *
  * Searches the @cal_cache with the given @sexp and
  * returns those components which satisfy the search
- * expression. The @out_components should be freed with
- * g_slist_free_full (components, g_object_unref); when
- * no longer needed.
+ * expression as a #GSList of #ECalCacheSearchData.
+ * The @out_data should be freed with
+ * g_slist_free_full (data, e_cal_cache_search_data_free);
+ * when no longer needed.
  *
  * Returns: Whether succeeded.
  *
@@ -2326,41 +2368,41 @@ ecc_search_ids_cb (ECalCache *cal_cache,
 gboolean
 e_cal_cache_search (ECalCache *cal_cache,
                    const gchar *sexp,
-                   GSList **out_components,
+                   GSList **out_data,
                    GCancellable *cancellable,
                    GError **error)
 {
        gboolean success;
 
        g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE);
-       g_return_val_if_fail (out_components != NULL, FALSE);
+       g_return_val_if_fail (out_data != NULL, FALSE);
 
-       *out_components = NULL;
+       *out_data = NULL;
 
-       success = e_cal_cache_search_with_callback (cal_cache, sexp, ecc_search_components_cb,
-               out_components, cancellable, error);
+       success = e_cal_cache_search_with_callback (cal_cache, sexp, ecc_search_data_cb,
+               out_data, cancellable, error);
        if (success) {
-               *out_components = g_slist_reverse (*out_components);
+               *out_data = g_slist_reverse (*out_data);
        } else {
-               g_slist_free_full (*out_components, g_object_unref);
-               *out_components = NULL;
+               g_slist_free_full (*out_data, e_cal_cache_search_data_free);
+               *out_data = NULL;
        }
 
        return success;
 }
 
 /**
- * e_cal_cache_search_as_strings:
+ * e_cal_cache_search_components:
  * @cal_cache: an #ECalCache
  * @sexp: (nullable): search expression; use %NULL or an empty string to list all stored components
- * @out_icalstrings: (out) (transfer full) (element-type utf8): stored components satisfied by @sexp as iCal 
strings
+ * @out_components: (out) (transfer full) (element-type ECalComponent): stored components satisfied by @sexp
  * @cancellable: optional #GCancellable object, or %NULL
  * @error: return location for a #GError, or %NULL
  *
- * Searches the @cal_cache with the given @sexp and returns those
- * components which satisfy the search expression as iCal strings.
- * The @out_icalstrings should be freed with
- * g_slist_free_full (components, g_free); when
+ * Searches the @cal_cache with the given @sexp and
+ * returns those components which satisfy the search
+ * expression. The @out_components should be freed with
+ * g_slist_free_full (components, g_object_unref); when
  * no longer needed.
  *
  * Returns: Whether succeeded.
@@ -2368,26 +2410,26 @@ e_cal_cache_search (ECalCache *cal_cache,
  * Since: 3.26
  **/
 gboolean
-e_cal_cache_search_as_strings (ECalCache *cal_cache,
+e_cal_cache_search_components (ECalCache *cal_cache,
                               const gchar *sexp,
-                              GSList **out_icalstrings,
+                              GSList **out_components,
                               GCancellable *cancellable,
                               GError **error)
 {
        gboolean success;
 
        g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE);
-       g_return_val_if_fail (out_icalstrings != NULL, FALSE);
+       g_return_val_if_fail (out_components != NULL, FALSE);
 
-       *out_icalstrings = NULL;
+       *out_components = NULL;
 
-       success = e_cal_cache_search_with_callback (cal_cache, sexp, ecc_search_icalstrings_cb,
-               out_icalstrings, cancellable, error);
+       success = e_cal_cache_search_with_callback (cal_cache, sexp, ecc_search_components_cb,
+               out_components, cancellable, error);
        if (success) {
-               *out_icalstrings = g_slist_reverse (*out_icalstrings);
+               *out_components = g_slist_reverse (*out_components);
        } else {
-               g_slist_free_full (*out_icalstrings, g_free);
-               *out_icalstrings = NULL;
+               g_slist_free_full (*out_components, g_object_unref);
+               *out_components = NULL;
        }
 
        return success;
@@ -3086,7 +3128,7 @@ e_cal_cache_init (ECalCache *cal_cache)
        cal_cache->priv->loaded_timezones = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
cal_cache_free_zone);
        cal_cache->priv->modified_timezones = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
cal_cache_free_zone);
 
-       cal_cache->priv->sexps = g_hash_table_new_full (g_int_hash, g_int_equal, NULL, g_object_unref);
+       cal_cache->priv->sexps = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
 
        g_rec_mutex_init (&cal_cache->priv->timezones_lock);
        g_mutex_init (&cal_cache->priv->sexps_lock);
diff --git a/src/calendar/libedata-cal/e-cal-cache.h b/src/calendar/libedata-cal/e-cal-cache.h
index 64ea910..661da19 100644
--- a/src/calendar/libedata-cal/e-cal-cache.h
+++ b/src/calendar/libedata-cal/e-cal-cache.h
@@ -52,10 +52,10 @@ typedef struct _ECalCachePrivate ECalCachePrivate;
 
 /**
  * ECalCacheSearchData:
- * @uid: The UID of this component
- * @rid: (nullable): The Recurrence-ID of this component
- * @object: The component string
- * @extra: Any extra data associated with the component
+ * @uid: the UID of this component
+ * @rid: (nullable): the Recurrence-ID of this component
+ * @object: the component string
+ * @extra: any extra data associated with the component
  *
  * This structure is used to represent components returned
  * by the #ECalCache from various functions
@@ -158,6 +158,10 @@ ECalCache *        e_cal_cache_new                 (const gchar *filename,
 gchar *                e_cal_cache_dup_component_revision
                                                (ECalCache *cal_cache,
                                                 ECalComponent *component);
+gboolean       e_cal_cache_contains            (ECalCache *cal_cache,
+                                                const gchar *uid,
+                                                const gchar *rid,
+                                                ECacheDeletedFlag deleted_flag);
 gboolean       e_cal_cache_put_component       (ECalCache *cal_cache,
                                                 ECalComponent *component,
                                                 const gchar *extra,
@@ -234,12 +238,12 @@ gboolean  e_cal_cache_get_components_in_range_as_strings
                                                 GError **error);
 gboolean       e_cal_cache_search              (ECalCache *cal_cache,
                                                 const gchar *sexp,
-                                                GSList **out_components, /* ECalComponent * */
+                                                GSList **out_data, /* ECalCacheSearchData * * */
                                                 GCancellable *cancellable,
                                                 GError **error);
-gboolean       e_cal_cache_search_as_strings   (ECalCache *cal_cache,
+gboolean       e_cal_cache_search_components   (ECalCache *cal_cache,
                                                 const gchar *sexp,
-                                                GSList **out_icalstrings, /* gchar * */
+                                                GSList **out_components, /* ECalComponent * */
                                                 GCancellable *cancellable,
                                                 GError **error);
 gboolean       e_cal_cache_search_ids          (ECalCache *cal_cache,
diff --git a/src/libebackend/e-cache.c b/src/libebackend/e-cache.c
index 9238b1b..20d26bd 100644
--- a/src/libebackend/e-cache.c
+++ b/src/libebackend/e-cache.c
@@ -1115,7 +1115,7 @@ e_cache_count_rows_cb (ECache *cache,
  * Checkes whether the @cache contains an object with
  * the given @uid.
  *
- * Returns: Whether the cache contains an object with the given @uid.
+ * Returns: Whether the the object had been found.
  *
  * Since: 3.26
  **/
diff --git a/tests/libedata-book/CMakeLists.txt b/tests/libedata-book/CMakeLists.txt
index e120f27..d97096e 100644
--- a/tests/libedata-book/CMakeLists.txt
+++ b/tests/libedata-book/CMakeLists.txt
@@ -37,31 +37,31 @@ set(extra_ldflags
 set(SOURCES
        data-test-utils.c
        data-test-utils.h
-       test-cache-utils.c
-       test-cache-utils.h
+       test-book-cache-utils.c
+       test-book-cache-utils.h
 )
 
-add_library(data-test-utils STATIC
+add_library(data-book-test-utils STATIC
        ${SOURCES}
 )
 
-add_dependencies(data-test-utils
+add_dependencies(data-book-test-utils
        edataserver
        ${extra_deps}
 )
 
-target_compile_definitions(data-test-utils PRIVATE
-       -DG_LOG_DOMAIN=\"data-test-utils\"
+target_compile_definitions(data-book-test-utils PRIVATE
+       -DG_LOG_DOMAIN=\"data-book-test-utils\"
        ${extra_defines}
 )
 
-target_compile_options(data-test-utils PUBLIC
+target_compile_options(data-book-test-utils PUBLIC
        ${BACKEND_CFLAGS}
        ${DATA_SERVER_CFLAGS}
        ${extra_cflags}
 )
 
-target_include_directories(data-test-utils PUBLIC
+target_include_directories(data-book-test-utils PUBLIC
        ${CMAKE_BINARY_DIR}
        ${CMAKE_BINARY_DIR}/src
        ${CMAKE_SOURCE_DIR}/src
@@ -70,7 +70,7 @@ target_include_directories(data-test-utils PUBLIC
        ${extra_incdirs}
 )
 
-target_link_libraries(data-test-utils
+target_link_libraries(data-book-test-utils
        edataserver
        ${extra_deps}
        ${BACKEND_LDFLAGS}
@@ -81,7 +81,7 @@ target_link_libraries(data-test-utils
 set(extra_deps
        ebook
        ebook-contacts
-       data-test-utils
+       data-book-test-utils
 )
 
 set(extra_defines)
@@ -96,17 +96,17 @@ set(extra_defines)
 # This is because each migrated test changes the
 # locale and reloads the same addressbook of the previous test.
 set(TESTS
-       test-cache-get-contact
-       test-cache-create-cursor
-       test-cache-cursor-move-by-posix
-       test-cache-cursor-move-by-en-US
-       test-cache-cursor-move-by-fr-CA
-       test-cache-cursor-move-by-de-DE
-       test-cache-cursor-set-target
-       test-cache-cursor-calculate
-       test-cache-cursor-set-sexp
-       test-cache-cursor-change-locale
-       test-cache-offline
+       test-book-cache-get-contact
+       test-book-cache-create-cursor
+       test-book-cache-cursor-move-by-posix
+       test-book-cache-cursor-move-by-en-US
+       test-book-cache-cursor-move-by-fr-CA
+       test-book-cache-cursor-move-by-de-DE
+       test-book-cache-cursor-set-target
+       test-book-cache-cursor-calculate
+       test-book-cache-cursor-set-sexp
+       test-book-cache-cursor-change-locale
+       test-book-cache-offline
        test-sqlite-get-contact
        test-sqlite-create-cursor
        test-sqlite-cursor-move-by-posix
diff --git a/tests/libedata-book/data-test-utils.h b/tests/libedata-book/data-test-utils.h
index b72822e..e415101 100644
--- a/tests/libedata-book/data-test-utils.h
+++ b/tests/libedata-book/data-test-utils.h
@@ -22,6 +22,8 @@
 
 #include <libedata-book/libedata-book.h>
 
+G_BEGIN_DECLS
+
 /* This legend shows the add order, and various sort order of the sorted
  * vcards. The UIDs of these contacts are formed as 'sorted-1', 'sorted-2' etc
  * and the numbering of the contacts is according to the 'N' column in the
@@ -174,4 +176,6 @@ StepData *step_test_new_full               (const gchar         *test_prefix,
 void      step_test_add                    (StepData    *data,
                                            gboolean     filtered);
 
+G_END_DECLS
+
 #endif /* DATA_TEST_UTILS_H */
diff --git a/tests/libedata-book/test-cache-create-cursor.c 
b/tests/libedata-book/test-book-cache-create-cursor.c
similarity index 99%
rename from tests/libedata-book/test-cache-create-cursor.c
rename to tests/libedata-book/test-book-cache-create-cursor.c
index 5f75771..c4cc6e1 100644
--- a/tests/libedata-book/test-cache-create-cursor.c
+++ b/tests/libedata-book/test-book-cache-create-cursor.c
@@ -18,7 +18,7 @@
 #include <locale.h>
 #include <libebook/libebook.h>
 
-#include "test-cache-utils.h"
+#include "test-book-cache-utils.h"
 
 static TCUClosure closure = { NULL };
 
diff --git a/tests/libedata-book/test-cache-cursor-calculate.c 
b/tests/libedata-book/test-book-cache-cursor-calculate.c
similarity index 99%
rename from tests/libedata-book/test-cache-cursor-calculate.c
rename to tests/libedata-book/test-book-cache-cursor-calculate.c
index 6bb1407..522ca6f 100644
--- a/tests/libedata-book/test-cache-cursor-calculate.c
+++ b/tests/libedata-book/test-book-cache-cursor-calculate.c
@@ -18,7 +18,7 @@
 #include <locale.h>
 #include <libebook/libebook.h>
 
-#include "test-cache-utils.h"
+#include "test-book-cache-utils.h"
 
 static TCUCursorClosure ascending_closure = {
        { NULL },
diff --git a/tests/libedata-book/test-cache-cursor-change-locale.c 
b/tests/libedata-book/test-book-cache-cursor-change-locale.c
similarity index 99%
rename from tests/libedata-book/test-cache-cursor-change-locale.c
rename to tests/libedata-book/test-book-cache-cursor-change-locale.c
index 2445652..f95a901 100644
--- a/tests/libedata-book/test-cache-cursor-change-locale.c
+++ b/tests/libedata-book/test-book-cache-cursor-change-locale.c
@@ -18,7 +18,7 @@
 #include <locale.h>
 #include <libebook/libebook.h>
 
-#include "test-cache-utils.h"
+#include "test-book-cache-utils.h"
 
 struct {
        gboolean empty_book;
diff --git a/tests/libedata-book/test-cache-cursor-move-by-de-DE.c 
b/tests/libedata-book/test-book-cache-cursor-move-by-de-DE.c
similarity index 98%
rename from tests/libedata-book/test-cache-cursor-move-by-de-DE.c
rename to tests/libedata-book/test-book-cache-cursor-move-by-de-DE.c
index b4bfb2c..8432f03 100644
--- a/tests/libedata-book/test-cache-cursor-move-by-de-DE.c
+++ b/tests/libedata-book/test-book-cache-cursor-move-by-de-DE.c
@@ -18,7 +18,7 @@
 #include <locale.h>
 #include <libebook/libebook.h>
 
-#include "test-cache-utils.h"
+#include "test-book-cache-utils.h"
 
 struct {
        gboolean empty_book;
diff --git a/tests/libedata-book/test-cache-cursor-move-by-en-US.c 
b/tests/libedata-book/test-book-cache-cursor-move-by-en-US.c
similarity index 98%
rename from tests/libedata-book/test-cache-cursor-move-by-en-US.c
rename to tests/libedata-book/test-book-cache-cursor-move-by-en-US.c
index 6f969e7..94955d5 100644
--- a/tests/libedata-book/test-cache-cursor-move-by-en-US.c
+++ b/tests/libedata-book/test-book-cache-cursor-move-by-en-US.c
@@ -18,7 +18,7 @@
 #include <locale.h>
 #include <libebook/libebook.h>
 
-#include "test-cache-utils.h"
+#include "test-book-cache-utils.h"
 
 struct {
        gboolean empty_book;
diff --git a/tests/libedata-book/test-cache-cursor-move-by-fr-CA.c 
b/tests/libedata-book/test-book-cache-cursor-move-by-fr-CA.c
similarity index 98%
rename from tests/libedata-book/test-cache-cursor-move-by-fr-CA.c
rename to tests/libedata-book/test-book-cache-cursor-move-by-fr-CA.c
index 7bfb435..8d71e27 100644
--- a/tests/libedata-book/test-cache-cursor-move-by-fr-CA.c
+++ b/tests/libedata-book/test-book-cache-cursor-move-by-fr-CA.c
@@ -18,7 +18,7 @@
 #include <locale.h>
 #include <libebook/libebook.h>
 
-#include "test-cache-utils.h"
+#include "test-book-cache-utils.h"
 
 struct {
        gboolean empty_book;
diff --git a/tests/libedata-book/test-cache-cursor-move-by-posix.c 
b/tests/libedata-book/test-book-cache-cursor-move-by-posix.c
similarity index 98%
rename from tests/libedata-book/test-cache-cursor-move-by-posix.c
rename to tests/libedata-book/test-book-cache-cursor-move-by-posix.c
index 93d4e05..110cd6c 100644
--- a/tests/libedata-book/test-cache-cursor-move-by-posix.c
+++ b/tests/libedata-book/test-book-cache-cursor-move-by-posix.c
@@ -18,7 +18,7 @@
 #include <locale.h>
 #include <libebook/libebook.h>
 
-#include "test-cache-utils.h"
+#include "test-book-cache-utils.h"
 
 struct {
        gboolean empty_book;
diff --git a/tests/libedata-book/test-cache-cursor-set-sexp.c 
b/tests/libedata-book/test-book-cache-cursor-set-sexp.c
similarity index 99%
rename from tests/libedata-book/test-cache-cursor-set-sexp.c
rename to tests/libedata-book/test-book-cache-cursor-set-sexp.c
index e717264..88da7de 100644
--- a/tests/libedata-book/test-cache-cursor-set-sexp.c
+++ b/tests/libedata-book/test-book-cache-cursor-set-sexp.c
@@ -18,7 +18,7 @@
 #include <locale.h>
 #include <libebook/libebook.h>
 
-#include "test-cache-utils.h"
+#include "test-book-cache-utils.h"
 
 static TCUCursorClosure book_closure = { { NULL }, NULL, E_BOOK_CURSOR_SORT_ASCENDING };
 
diff --git a/tests/libedata-book/test-cache-cursor-set-target.c 
b/tests/libedata-book/test-book-cache-cursor-set-target.c
similarity index 99%
rename from tests/libedata-book/test-cache-cursor-set-target.c
rename to tests/libedata-book/test-book-cache-cursor-set-target.c
index ef092e5..f230634 100644
--- a/tests/libedata-book/test-cache-cursor-set-target.c
+++ b/tests/libedata-book/test-book-cache-cursor-set-target.c
@@ -18,7 +18,7 @@
 #include <locale.h>
 #include <libebook/libebook.h>
 
-#include "test-cache-utils.h"
+#include "test-book-cache-utils.h"
 
 /*****************************************************
  *          Expect the same results twice            *
diff --git a/tests/libedata-book/test-cache-get-contact.c b/tests/libedata-book/test-book-cache-get-contact.c
similarity index 98%
rename from tests/libedata-book/test-cache-get-contact.c
rename to tests/libedata-book/test-book-cache-get-contact.c
index 58fe3b0..c214d4f 100644
--- a/tests/libedata-book/test-cache-get-contact.c
+++ b/tests/libedata-book/test-book-cache-get-contact.c
@@ -18,7 +18,7 @@
 #include <locale.h>
 #include <libebook/libebook.h>
 
-#include "test-cache-utils.h"
+#include "test-book-cache-utils.h"
 
 static void
 test_get_contact (TCUFixture *fixture,
diff --git a/tests/libedata-book/test-cache-offline.c b/tests/libedata-book/test-book-cache-offline.c
similarity index 99%
rename from tests/libedata-book/test-cache-offline.c
rename to tests/libedata-book/test-book-cache-offline.c
index e02943d..b6d4f1e 100644
--- a/tests/libedata-book/test-cache-offline.c
+++ b/tests/libedata-book/test-book-cache-offline.c
@@ -18,7 +18,7 @@
 #include <locale.h>
 #include <libebook/libebook.h>
 
-#include "test-cache-utils.h"
+#include "test-book-cache-utils.h"
 
 static void
 test_fill_cache (TCUFixture *fixture,
@@ -260,10 +260,12 @@ test_basic_search (TCUFixture *fixture,
        /* Invalid expression */
        g_assert (!e_book_cache_search (fixture->book_cache, "invalid expression here", TRUE, &list, NULL, 
&error));
        g_assert_error (error, E_CACHE_ERROR, E_CACHE_ERROR_INVALID_QUERY);
+       g_assert_null (list);
        g_clear_error (&error);
 
        g_assert (!e_book_cache_search_uids (fixture->book_cache, "invalid expression here", &list, NULL, 
&error));
        g_assert_error (error, E_CACHE_ERROR, E_CACHE_ERROR_INVALID_QUERY);
+       g_assert_null (list);
        g_clear_error (&error);
 }
 
diff --git a/tests/libedata-book/test-cache-utils.c b/tests/libedata-book/test-book-cache-utils.c
similarity index 99%
rename from tests/libedata-book/test-cache-utils.c
rename to tests/libedata-book/test-book-cache-utils.c
index 8759df7..5db1684 100644
--- a/tests/libedata-book/test-cache-utils.c
+++ b/tests/libedata-book/test-book-cache-utils.c
@@ -28,7 +28,7 @@
 #include <stdlib.h>
 #include <errno.h>
 
-#include "test-cache-utils.h"
+#include "test-book-cache-utils.h"
 
 gchar *
 tcu_new_vcard_from_test_case (const gchar *case_name)
diff --git a/tests/libedata-book/test-cache-utils.h b/tests/libedata-book/test-book-cache-utils.h
similarity index 98%
rename from tests/libedata-book/test-cache-utils.h
rename to tests/libedata-book/test-book-cache-utils.h
index 4b379c0..4718665 100644
--- a/tests/libedata-book/test-cache-utils.h
+++ b/tests/libedata-book/test-book-cache-utils.h
@@ -17,11 +17,13 @@
  * Authors: Tristan Van Berkom <tristanvb openismus com>
  */
 
-#ifndef TEST_CACHE_UTILS_H
-#define TEST_CACHE_UTILS_H
+#ifndef TEST_BOOK_CACHE_UTILS_H
+#define TEST_BOOK_CACHE_UTILS_H
 
 #include <libedata-book/libedata-book.h>
 
+G_BEGIN_DECLS
+
 /* This legend shows the add order, and various sort order of the sorted
  * vcards. The UIDs of these contacts are formed as 'sorted-1', 'sorted-2' etc
  * and the numbering of the contacts is according to the 'N' column in the
@@ -171,4 +173,6 @@ TCUStepData *       tcu_step_test_new_full                  (const gchar *test_prefix,
 void           tcu_step_test_add                       (TCUStepData *data,
                                                         gboolean filtered);
 
-#endif /* TEST_CACHE_UTILS_H */
+G_END_DECLS
+
+#endif /* TEST_BOOK_CACHE_UTILS_H */
diff --git a/tests/libedata-cal/CMakeLists.txt b/tests/libedata-cal/CMakeLists.txt
index d42a64b..06659d4 100644
--- a/tests/libedata-cal/CMakeLists.txt
+++ b/tests/libedata-cal/CMakeLists.txt
@@ -4,7 +4,14 @@ set(extra_deps
        edata-cal
 )
 
-set(extra_defines)
+set(extra_defines
+       -DSRCDIR=\"${CMAKE_CURRENT_SOURCE_DIR}\"
+       -DINSTALLED_TEST_DIR=\"${INSTALLED_TESTS_EXEC_DIR}\"
+       -DBACKENDDIR=\"${ecal_backenddir}\"
+       -DDATADIR=\"${SHARE_INSTALL_PREFIX}\"
+       -DBUILDDIR=\"${CAMKE_BINARY_DIR}\"
+       -DCAMEL_PROVIDERDIR=\"${camel_providerdir}\"
+)
 
 set(extra_cflags
        ${CALENDAR_CFLAGS}
@@ -18,10 +25,60 @@ set(extra_ldflags
        ${CALENDAR_LDFLAGS}
 )
 
+set(SOURCES
+       test-cal-cache-utils.c
+       test-cal-cache-utils.h
+)
+
+add_library(data-cal-test-utils STATIC
+       ${SOURCES}
+)
+
+add_dependencies(data-cal-test-utils
+       edataserver
+       ${extra_deps}
+)
+target_compile_definitions(data-cal-test-utils PRIVATE
+       -DG_LOG_DOMAIN=\"data-cal-test-utils\"
+       ${extra_defines}
+)
+
+target_compile_options(data-cal-test-utils PUBLIC
+       ${BACKEND_CFLAGS}
+       ${DATA_SERVER_CFLAGS}
+       ${extra_cflags}
+)
+
+target_include_directories(data-cal-test-utils PUBLIC
+       ${CMAKE_BINARY_DIR}
+       ${CMAKE_BINARY_DIR}/src
+       ${CMAKE_SOURCE_DIR}/src
+       ${BACKEND_INCLUDE_DIRS}
+       ${DATA_SERVER_INCLUDE_DIRS}
+       ${extra_incdirs}
+)
+
+target_link_libraries(data-cal-test-utils
+       edataserver
+       ${extra_deps}
+       ${BACKEND_LDFLAGS}
+       ${DATA_SERVER_LDFLAGS}
+       ${extra_ldflags}
+)
+
+set(extra_deps
+       ecal
+       edata-cal
+       data-cal-test-utils
+)
+
+set(extra_defines)
+
 # Should be kept ordered approximately from least to most difficult/complex
 set(TESTS
        test-cal-backend-sexp
        test-intervaltree
+       test-cal-cache-offline
 )
 
 foreach(_test ${TESTS})
@@ -38,3 +95,11 @@ foreach(_test ${TESTS})
                "TEST_INSTALLED_SERVICES=1"
        )
 endforeach(_test)
+
+if(ENABLE_INSTALLED_TESTS)
+       file(GLOB COMPONENTS ${CMAKE_CURRENT_SOURCE_DIR}/components/*.ics)
+
+       install(FILES ${COMPONENTS}
+               DESTINATION ${INSTALLED_TESTS_EXEC_DIR}/components
+       )
+endif(ENABLE_INSTALLED_TESTS)
diff --git a/tests/libedata-cal/components/event-1.ics b/tests/libedata-cal/components/event-1.ics
new file mode 100644
index 0000000..39eaa58
--- /dev/null
+++ b/tests/libedata-cal/components/event-1.ics
@@ -0,0 +1,18 @@
+BEGIN:VEVENT
+UID:event-1
+DTSTAMP:20170130T000000Z
+CREATED:20170216T155507Z
+LAST-MODIFIED:20170216T155543Z
+SEQUENCE:1
+DTSTART:20170209T013000Z
+DTEND:20170209T030000Z
+SUMMARY:Alarmed
+DESCRIPTION:Event with alarm
+CLASS:PUBLIC
+TRANSP:OPACHE
+BEGIN:VALARM
+TRIGGER:-PT30M
+ACTION:DISPLAY
+DESCRIPTION:Reminder
+END:VALARM
+END:VEVENT
diff --git a/tests/libedata-cal/components/event-2.ics b/tests/libedata-cal/components/event-2.ics
new file mode 100644
index 0000000..3b6efef
--- /dev/null
+++ b/tests/libedata-cal/components/event-2.ics
@@ -0,0 +1,12 @@
+BEGIN:VEVENT
+UID:event-2
+DTSTAMP:20170103T070000Z
+CREATED:20170216T155507Z
+LAST-MODIFIED:20170216T155543Z
+SEQUENCE:1
+DTSTART:20170103T080000Z
+DTEND:20170103T083000Z
+SUMMARY:First working day
+DESCRIPTION:Multiline\n
+ description text
+END:VEVENT
diff --git a/tests/libedata-cal/components/event-3.ics b/tests/libedata-cal/components/event-3.ics
new file mode 100644
index 0000000..bbbaac5
--- /dev/null
+++ b/tests/libedata-cal/components/event-3.ics
@@ -0,0 +1,11 @@
+BEGIN:VEVENT
+UID:event-3
+DTSTAMP:20170103T070000Z
+CREATED:20170216T155507Z
+LAST-MODIFIED:20170216T155543Z
+SEQUENCE:3
+DTSTART:20170104T100000Z
+DTEND:20170110T120000Z
+SUMMARY:Lunch prepare
+LOCATION:Kitchen
+END:VEVENT
diff --git a/tests/libedata-cal/components/event-4.ics b/tests/libedata-cal/components/event-4.ics
new file mode 100644
index 0000000..fd83a62
--- /dev/null
+++ b/tests/libedata-cal/components/event-4.ics
@@ -0,0 +1,11 @@
+BEGIN:VEVENT
+UID:event-4
+DTSTAMP:20170102T000000Z
+CREATED:20170216T155507Z
+LAST-MODIFIED:20170216T155543Z
+SEQUENCE:3
+DTSTART:20170102T100000Z
+DTEND:20170102T180000Z
+SUMMARY:After-party clean up
+LOCATION:All around
+END:VEVENT
diff --git a/tests/libedata-cal/components/event-5.ics b/tests/libedata-cal/components/event-5.ics
new file mode 100644
index 0000000..a12a488
--- /dev/null
+++ b/tests/libedata-cal/components/event-5.ics
@@ -0,0 +1,18 @@
+BEGIN:VEVENT
+UID:event-5
+DTSTAMP:20101231T000000Z
+CREATED:20170216T155507Z
+LAST-MODIFIED:20170216T155543Z
+SEQUENCE:1
+DTSTART:20091231T000000Z
+DTEND:20100101T235959Z
+SUMMARY:New Year Party
+LOCATION:All around
+CATEGORIES:Holiday,International
+RRULE:FREQ=YEARLY
+BEGIN:VALARM
+ACTION:DISPLAY
+TRIGGER;RELATED=START:-P1D
+DESCRIPTION:New Year Party
+END:VALARM
+END:VEVENT
diff --git a/tests/libedata-cal/test-cal-cache-offline.c b/tests/libedata-cal/test-cal-cache-offline.c
new file mode 100644
index 0000000..1d0d183
--- /dev/null
+++ b/tests/libedata-cal/test-cal-cache-offline.c
@@ -0,0 +1,1043 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdlib.h>
+#include <locale.h>
+#include <libecal/libecal.h>
+
+#include "test-cal-cache-utils.h"
+
+static void
+test_fill_cache (TCUFixture *fixture,
+                ECalComponent **out_component)
+{
+       tcu_add_component_from_test_case (fixture, "event-1", out_component);
+       tcu_add_component_from_test_case (fixture, "event-2", NULL);
+       tcu_add_component_from_test_case (fixture, "event-5", NULL);
+}
+
+enum {
+       EXPECT_DEFAULT          = (0),
+       EXPECT_EVENT_1          = (1 << 0),
+       EXPECT_EVENT_2          = (1 << 1),
+       EXPECT_EVENT_3          = (1 << 2),
+       EXPECT_EVENT_4          = (1 << 3),
+       HAS_SEARCH_DATA         = (1 << 4),
+       SKIP_COMPONENT_PUT      = (1 << 5)
+};
+
+static void
+test_check_search_result (const GSList *list,
+                         guint32 flags)
+{
+       gboolean expect_event_1 = (flags & EXPECT_EVENT_1) != 0;
+       gboolean expect_event_2 = (flags & EXPECT_EVENT_2) != 0;
+       gboolean expect_event_3 = (flags & EXPECT_EVENT_3) != 0;
+       gboolean expect_event_4 = (flags & EXPECT_EVENT_4) != 0;
+       gboolean has_search_data = (flags & HAS_SEARCH_DATA) != 0;
+       gboolean have_event_1 = FALSE;
+       gboolean have_event_2 = FALSE;
+       gboolean have_event_3 = FALSE;
+       gboolean have_event_4 = FALSE;
+       gboolean have_event_5 = FALSE;
+       const GSList *link;
+
+       for (link = list; link; link = g_slist_next (link)) {
+               const gchar *uid;
+
+               if (has_search_data) {
+                       ECalCacheSearchData *sd = link->data;
+                       ECalComponent *component;
+
+                       g_assert (sd != NULL);
+                       g_assert (sd->uid != NULL);
+                       g_assert (sd->object != NULL);
+
+                       uid = sd->uid;
+
+                       component = e_cal_component_new_from_string (sd->object);
+                       g_assert (E_IS_CAL_COMPONENT (component));
+                       g_assert_cmpstr (uid, ==, icalcomponent_get_uid (e_cal_component_get_icalcomponent 
(component)));
+                       g_assert_nonnull (icalcomponent_get_summary (e_cal_component_get_icalcomponent 
(component)));
+
+                       g_clear_object (&component);
+               } else {
+                       const ECalComponentId *id = link->data;
+
+                       g_assert (id != NULL);
+                       g_assert (id->uid != NULL);
+
+                       uid = id->uid;
+               }
+
+               g_assert_nonnull (uid);
+
+               if (g_str_equal (uid, "event-1")) {
+                       g_assert (expect_event_1);
+                       g_assert (!have_event_1);
+                       have_event_1 = TRUE;
+               } else if (g_str_equal (uid, "event-2")) {
+                       g_assert (!have_event_2);
+                       have_event_2 = TRUE;
+               } else if (g_str_equal (uid, "event-3")) {
+                       g_assert (expect_event_3);
+                       g_assert (!have_event_3);
+                       have_event_3 = TRUE;
+               } else if (g_str_equal (uid, "event-4")) {
+                       g_assert (expect_event_4);
+                       g_assert (!have_event_4);
+                       have_event_4 = TRUE;
+               } else if (g_str_equal (uid, "event-5")) {
+                       g_assert (!have_event_5);
+                       have_event_5 = TRUE;
+               } else {
+                       /* It's not supposed to be NULL, but it will print the value of 'uid' */
+                       g_assert_cmpstr (uid, ==, NULL);
+               }
+       }
+
+       g_assert ((expect_event_1 && have_event_1) || (!expect_event_1 && !have_event_1));
+       g_assert ((expect_event_2 && have_event_2) || (!expect_event_2 && !have_event_2));
+       g_assert ((expect_event_3 && have_event_3) || (!expect_event_3 && !have_event_3));
+       g_assert ((expect_event_4 && have_event_4) || (!expect_event_4 && !have_event_4));
+       g_assert (have_event_5);
+}
+
+static void
+test_basic_search (TCUFixture *fixture,
+                  guint32 flags)
+{
+       GSList *list = NULL;
+       const gchar *sexp;
+       gint expect_total;
+       GError *error = NULL;
+
+       expect_total = 2 +
+               ((flags & EXPECT_EVENT_1) != 0 ? 1 : 0) +
+               ((flags & EXPECT_EVENT_3) != 0 ? 1 : 0) +
+               ((flags & EXPECT_EVENT_4) != 0 ? 1 : 0);
+
+       /* All components first */
+       g_assert (e_cal_cache_search (fixture->cal_cache, NULL, &list, NULL, &error));
+       g_assert_no_error (error);
+       g_assert_cmpint (g_slist_length (list), ==, expect_total);
+       test_check_search_result (list, flags | EXPECT_EVENT_2 | HAS_SEARCH_DATA);
+       g_slist_free_full (list, e_cal_cache_search_data_free);
+       list = NULL;
+
+       g_assert (e_cal_cache_search_ids (fixture->cal_cache, NULL, &list, NULL, &error));
+       g_assert_no_error (error);
+       g_assert_cmpint (g_slist_length (list), ==, expect_total);
+       test_check_search_result (list, flags | EXPECT_EVENT_2);
+       g_slist_free_full (list, (GDestroyNotify) e_cal_component_free_id);
+       list = NULL;
+
+       /* Only Party, aka event-5, as an in-summary query */
+       sexp = "(has-categories? \"Holiday\")";
+
+       g_assert (e_cal_cache_search (fixture->cal_cache, sexp, &list, NULL, &error));
+       g_assert_no_error (error);
+       g_assert_cmpint (g_slist_length (list), ==, 1);
+       test_check_search_result (list, HAS_SEARCH_DATA);
+       g_slist_free_full (list, e_cal_cache_search_data_free);
+       list = NULL;
+
+       g_assert (e_cal_cache_search_ids (fixture->cal_cache, sexp, &list, NULL, &error));
+       g_assert_no_error (error);
+       g_assert_cmpint (g_slist_length (list), ==, 1);
+       test_check_search_result (list, EXPECT_DEFAULT);
+       g_slist_free_full (list, (GDestroyNotify) e_cal_component_free_id);
+       list = NULL;
+
+       /* Only Party, aka event-5, as a non-summarised query */
+       sexp = "(has-alarms-in-range? (make-time \"20091229T230000Z\") (make-time \"20091231T010000Z\"))";
+
+       g_assert (e_cal_cache_search (fixture->cal_cache, sexp, &list, NULL, &error));
+       g_assert_no_error (error);
+       g_assert_cmpint (g_slist_length (list), ==, 1);
+       test_check_search_result (list, HAS_SEARCH_DATA);
+       g_slist_free_full (list, e_cal_cache_search_data_free);
+       list = NULL;
+
+       g_assert (e_cal_cache_search_ids (fixture->cal_cache, sexp, &list, NULL, &error));
+       g_assert_no_error (error);
+       g_assert_cmpint (g_slist_length (list), ==, 1);
+       test_check_search_result (list, EXPECT_DEFAULT);
+       g_slist_free_full (list, (GDestroyNotify) e_cal_component_free_id);
+       list = NULL;
+
+       /* Invalid expression */
+       g_assert (!e_cal_cache_search (fixture->cal_cache, "invalid expression here", &list, NULL, &error));
+       g_assert_error (error, E_CACHE_ERROR, E_CACHE_ERROR_INVALID_QUERY);
+       g_assert_null (list);
+       g_clear_error (&error);
+
+       g_assert (!e_cal_cache_search_ids (fixture->cal_cache, "invalid expression here", &list, NULL, 
&error));
+       g_assert_error (error, E_CACHE_ERROR, E_CACHE_ERROR_INVALID_QUERY);
+       g_assert_null (list);
+       g_clear_error (&error);
+}
+
+/* Expects pairs of UID (gchar *) and EOfflineState (gint), terminated by NULL */
+static void
+test_check_offline_changes (TCUFixture *fixture,
+                           ...) G_GNUC_NULL_TERMINATED;
+
+static void
+test_check_offline_changes (TCUFixture *fixture,
+                           ...)
+{
+       GSList *changes, *link;
+       va_list args;
+       GHashTable *expects;
+       const gchar *uid;
+       GError *error = NULL;
+
+       changes = e_cache_get_offline_changes (E_CACHE (fixture->cal_cache), NULL, &error);
+
+       g_assert_no_error (error);
+
+       expects = g_hash_table_new (g_str_hash, g_str_equal);
+
+       va_start (args, fixture);
+       uid = va_arg (args, const gchar *);
+       while (uid) {
+               gint state = va_arg (args, gint);
+
+               g_hash_table_insert (expects, (gpointer) uid, GINT_TO_POINTER (state));
+               uid = va_arg (args, const gchar *);
+       }
+       va_end (args);
+
+       g_assert_cmpint (g_slist_length (changes), ==, g_hash_table_size (expects));
+
+       for (link = changes; link; link = g_slist_next (link)) {
+               ECacheOfflineChange *change = link->data;
+               gint expect_state;
+
+               g_assert_nonnull (change);
+               g_assert (g_hash_table_contains (expects, change->uid));
+
+               expect_state = GPOINTER_TO_INT (g_hash_table_lookup (expects, change->uid));
+               g_assert_cmpint (expect_state, ==, change->state);
+       }
+
+       g_slist_free_full (changes, e_cache_offline_change_free);
+       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->cal_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 *summ_value)
+{
+       ECalComponent *component = NULL;
+       GError *error = NULL;
+
+       g_assert (e_cal_cache_get_component (fixture->cal_cache, uid, NULL, &component, NULL, &error));
+       g_assert_no_error (error);
+       g_assert_nonnull (component);
+       g_assert_cmpstr (icalcomponent_get_summary (e_cal_component_get_icalcomponent (component)), ==, 
summ_value);
+
+       g_clear_object (&component);
+}
+
+static void
+test_verify_storage (TCUFixture *fixture,
+                    const gchar *uid,
+                    const gchar *expect_summ,
+                    const gchar *expect_extra,
+                    EOfflineState expect_offline_state)
+{
+       ECalComponent *component = NULL;
+       EOfflineState offline_state;
+       gchar *saved_extra = NULL;
+       GError *error = NULL;
+
+       if (expect_offline_state == E_OFFLINE_STATE_LOCALLY_DELETED ||
+           expect_offline_state == E_OFFLINE_STATE_UNKNOWN) {
+               g_assert (!e_cal_cache_get_component (fixture->cal_cache, uid, NULL, &component, NULL, 
&error));
+               g_assert_error (error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND);
+               g_assert_null (component);
+
+               g_clear_error (&error);
+       } else {
+               g_assert (e_cal_cache_get_component (fixture->cal_cache, uid, NULL, &component, NULL, 
&error));
+               g_assert_no_error (error);
+               g_assert_nonnull (component);
+       }
+
+       offline_state = test_check_offline_state (fixture, uid, expect_offline_state);
+
+       if (offline_state == E_OFFLINE_STATE_UNKNOWN) {
+               g_assert (!e_cal_cache_contains (fixture->cal_cache, uid, NULL, E_CACHE_EXCLUDE_DELETED));
+               g_assert (!e_cal_cache_contains (fixture->cal_cache, uid, NULL, E_CACHE_INCLUDE_DELETED));
+               test_check_offline_changes (fixture, NULL);
+               return;
+       }
+
+       g_assert (e_cal_cache_get_component_extra (fixture->cal_cache, uid, NULL, &saved_extra, NULL, 
&error));
+       g_assert_no_error (error);
+
+       g_assert_cmpstr (saved_extra, ==, expect_extra);
+       g_assert_cmpstr (icalcomponent_get_summary (e_cal_component_get_icalcomponent (component)), ==, 
expect_summ);
+
+       g_clear_object (&component);
+       g_free (saved_extra);
+
+       if (expect_offline_state == E_OFFLINE_STATE_SYNCED)
+               test_check_offline_changes (fixture, NULL);
+       else
+               test_check_offline_changes (fixture, uid, expect_offline_state, NULL);
+}
+
+static void
+test_offline_basics (TCUFixture *fixture,
+                    gconstpointer user_data)
+{
+       EOfflineState states[] = {
+               E_OFFLINE_STATE_LOCALLY_CREATED,
+               E_OFFLINE_STATE_LOCALLY_MODIFIED,
+               E_OFFLINE_STATE_LOCALLY_DELETED,
+               E_OFFLINE_STATE_SYNCED
+       };
+       ECalComponent *component = NULL;
+       gint ii;
+       const gchar *uid;
+       gchar *saved_extra = NULL, *tmp;
+       GError *error = NULL;
+
+       /* Basic ECache stuff */
+       e_cache_set_version (E_CACHE (fixture->cal_cache), 123);
+       g_assert_cmpint (e_cache_get_version (E_CACHE (fixture->cal_cache)), ==, 123);
+
+       e_cache_set_revision (E_CACHE (fixture->cal_cache), "rev-321");
+       tmp = e_cache_dup_revision (E_CACHE (fixture->cal_cache));
+       g_assert_cmpstr ("rev-321", ==, tmp);
+       g_free (tmp);
+
+       g_assert (e_cache_set_key (E_CACHE (fixture->cal_cache), "my-key-str", "key-str-value", &error));
+       g_assert_no_error (error);
+
+       tmp = e_cache_dup_key (E_CACHE (fixture->cal_cache), "my-key-str", &error);
+       g_assert_no_error (error);
+       g_assert_cmpstr ("key-str-value", ==, tmp);
+       g_free (tmp);
+
+       g_assert (e_cache_set_key_int (E_CACHE (fixture->cal_cache), "version", 567, &error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_get_key_int (E_CACHE (fixture->cal_cache), "version", &error), ==, 567);
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_get_version (E_CACHE (fixture->cal_cache)), ==, 123);
+
+       /* Add in online */
+       test_fill_cache (fixture, &component);
+       g_assert_nonnull (component);
+
+       uid = icalcomponent_get_uid (e_cal_component_get_icalcomponent (component));
+       g_assert_nonnull (uid);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, 3);
+       g_assert_no_error (error);
+
+       g_assert (e_cal_cache_set_component_extra (fixture->cal_cache, uid, NULL, "extra-0", NULL, &error));
+       g_assert_no_error (error);
+
+       g_assert (e_cal_cache_get_component_extra (fixture->cal_cache, uid, NULL, &saved_extra, NULL, 
&error));
+       g_assert_no_error (error);
+       g_assert_cmpstr (saved_extra, ==, "extra-0");
+
+       g_free (saved_extra);
+       saved_extra = NULL;
+
+       icalcomponent_set_summary (e_cal_component_get_icalcomponent (component), "summ-0");
+
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_SYNCED);
+
+       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->cal_cache), uid, states[ii], NULL, 
&error));
+               g_assert_no_error (error);
+
+               test_check_offline_state (fixture, uid, states[ii]);
+
+               if (states[ii] != E_OFFLINE_STATE_SYNCED)
+                       test_check_offline_changes (fixture, uid, states[ii], NULL);
+
+               if (states[ii] == E_OFFLINE_STATE_LOCALLY_DELETED) {
+                       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), 
E_CACHE_EXCLUDE_DELETED, NULL, &error), ==, 2);
+                       g_assert_no_error (error);
+
+                       g_assert (!e_cal_cache_contains (fixture->cal_cache, uid, NULL, 
E_CACHE_EXCLUDE_DELETED));
+
+                       g_assert (e_cal_cache_set_component_extra (fixture->cal_cache, uid, NULL, "extra-1", 
NULL, &error));
+                       g_assert_no_error (error);
+
+                       g_assert (e_cal_cache_get_component_extra (fixture->cal_cache, uid, NULL, 
&saved_extra, NULL, &error));
+                       g_assert_no_error (error);
+                       g_assert_cmpstr (saved_extra, ==, "extra-1");
+
+                       g_free (saved_extra);
+                       saved_extra = NULL;
+
+                       /* Search when locally deleted */
+                       test_basic_search (fixture, EXPECT_DEFAULT);
+               } else {
+                       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), 
E_CACHE_EXCLUDE_DELETED, NULL, &error), ==, 3);
+                       g_assert_no_error (error);
+
+                       g_assert (e_cal_cache_contains (fixture->cal_cache, uid, NULL, 
E_CACHE_EXCLUDE_DELETED));
+
+                       /* Search when locally available */
+                       test_basic_search (fixture, EXPECT_EVENT_1);
+               }
+
+               g_assert (e_cal_cache_contains (fixture->cal_cache, uid, NULL, E_CACHE_INCLUDE_DELETED));
+
+               g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_INCLUDE_DELETED, 
NULL, &error), ==, 3);
+               g_assert_no_error (error);
+       }
+
+       test_check_offline_changes (fixture, NULL);
+
+       /* Edit in online */
+       icalcomponent_set_summary (e_cal_component_get_icalcomponent (component), "summ-1");
+
+       g_assert (e_cal_cache_put_component (fixture->cal_cache, component, NULL, E_CACHE_IS_ONLINE, NULL, 
&error));
+       g_assert_no_error (error);
+
+       test_verify_storage (fixture, uid, "summ-1", NULL, E_OFFLINE_STATE_SYNCED);
+       test_check_offline_changes (fixture, NULL);
+
+       icalcomponent_set_summary (e_cal_component_get_icalcomponent (component), "summ-2");
+
+       g_assert (e_cal_cache_put_component (fixture->cal_cache, component, "extra-2", E_CACHE_IS_ONLINE, 
NULL, &error));
+       g_assert_no_error (error);
+
+       test_verify_storage (fixture, uid, "summ-2", "extra-2", E_OFFLINE_STATE_SYNCED);
+       test_check_offline_changes (fixture, NULL);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, 3);
+       g_assert_no_error (error);
+
+       /* Search before delete */
+       test_basic_search (fixture, EXPECT_EVENT_1);
+
+       /* Delete in online */
+       g_assert (e_cal_cache_remove_component (fixture->cal_cache, uid, NULL, E_CACHE_IS_ONLINE, NULL, 
&error));
+       g_assert_no_error (error);
+
+       g_assert (!e_cache_set_offline_state (E_CACHE (fixture->cal_cache), uid, 
E_OFFLINE_STATE_LOCALLY_MODIFIED, NULL, &error));
+       g_assert_error (error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND);
+       g_clear_error (&error);
+
+       test_verify_storage (fixture, uid, NULL, NULL, E_OFFLINE_STATE_UNKNOWN);
+       test_check_offline_changes (fixture, NULL);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, 2);
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_INCLUDE_DELETED, NULL, 
&error), ==, 2);
+       g_assert_no_error (error);
+
+       g_assert (!e_cal_cache_set_component_extra (fixture->cal_cache, uid, NULL, "extra-3", NULL, &error));
+       g_assert_error (error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND);
+       g_clear_error (&error);
+
+       g_assert (!e_cal_cache_get_component_extra (fixture->cal_cache, uid, NULL, &saved_extra, NULL, 
&error));
+       g_assert_error (error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND);
+       g_assert_null (saved_extra);
+       g_clear_error (&error);
+
+       g_clear_object (&component);
+
+       /* Search after delete */
+       test_basic_search (fixture, EXPECT_DEFAULT);
+}
+
+static void
+test_offline_add_one (TCUFixture *fixture,
+                     const gchar *case_name,
+                     gint expect_total,
+                     guint32 flags,
+                     ECalComponent **out_component)
+{
+       ECalComponent *component = NULL;
+       const gchar *uid;
+       GError *error = NULL;
+
+       if (!(flags & SKIP_COMPONENT_PUT)) {
+               component = tcu_new_component_from_test_case (case_name);
+               g_assert_nonnull (component);
+
+               uid = icalcomponent_get_uid (e_cal_component_get_icalcomponent (component));
+               g_assert_nonnull (uid);
+
+               test_check_offline_state (fixture, uid, E_OFFLINE_STATE_UNKNOWN);
+
+               /* Add a component in offline */
+               g_assert (e_cal_cache_put_component (fixture->cal_cache, component, NULL, E_CACHE_IS_OFFLINE, 
NULL, &error));
+               g_assert_no_error (error);
+       } else {
+               uid = case_name;
+       }
+
+       if ((flags & EXPECT_EVENT_3) != 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_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, expect_total);
+       g_assert_no_error (error);
+
+       test_basic_search (fixture, flags);
+
+       if (out_component)
+               *out_component = component;
+       else
+               g_clear_object (&component);
+}
+
+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_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, 3);
+       g_assert_no_error (error);
+
+       test_check_offline_changes (fixture, NULL);
+
+       /* Add the first in offline */
+       test_offline_add_one (fixture, "event-3", 4, EXPECT_EVENT_3 | EXPECT_EVENT_1, NULL);
+
+       test_check_offline_changes (fixture,
+               "event-3", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+
+       /* Add the second in offline */
+       test_offline_add_one (fixture, "event-4", 5, EXPECT_EVENT_3 | EXPECT_EVENT_4 | EXPECT_EVENT_1, NULL);
+
+       test_check_offline_changes (fixture,
+               "event-3", E_OFFLINE_STATE_LOCALLY_CREATED,
+               "event-4", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+}
+
+static void
+test_offline_add_edit (TCUFixture *fixture,
+                      gconstpointer user_data)
+{
+       ECalComponent *component = NULL;
+       GError *error = NULL;
+
+       /* Add in online */
+       test_fill_cache (fixture, NULL);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, 3);
+       g_assert_no_error (error);
+
+       test_check_offline_changes (fixture, NULL);
+
+       /* Add in offline */
+       test_offline_add_one (fixture, "event-3", 4, EXPECT_EVENT_3 | EXPECT_EVENT_1, &component);
+       g_assert_nonnull (component);
+
+       test_check_offline_changes (fixture,
+               "event-3", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+
+       /* Modify added in offline */
+       icalcomponent_set_summary (e_cal_component_get_icalcomponent (component), "summ-2");
+
+       g_assert (e_cal_cache_put_component (fixture->cal_cache, component, NULL, E_CACHE_IS_OFFLINE, NULL, 
&error));
+       g_assert_no_error (error);
+
+       test_offline_add_one (fixture, "event-3", 4, EXPECT_EVENT_3 | EXPECT_EVENT_1 | SKIP_COMPONENT_PUT, 
NULL);
+
+       test_check_offline_changes (fixture,
+               "event-3", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+
+       test_check_edit_saved (fixture, "event-3", "summ-2");
+
+       g_clear_object (&component);
+}
+
+static void
+test_offline_add_delete (TCUFixture *fixture,
+                        gconstpointer user_data)
+{
+       ECalComponent *component = NULL;
+       const gchar *uid;
+       GError *error = NULL;
+
+       /* Add in online */
+       test_fill_cache (fixture, NULL);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, 3);
+       g_assert_no_error (error);
+
+       test_check_offline_changes (fixture, NULL);
+
+       /* Add in offline */
+       test_offline_add_one (fixture, "event-3", 4, EXPECT_EVENT_3 | EXPECT_EVENT_1, &component);
+       g_assert_nonnull (component);
+
+       test_check_offline_changes (fixture,
+               "event-3", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+
+       uid = icalcomponent_get_uid (e_cal_component_get_icalcomponent (component));
+       g_assert_nonnull (uid);
+
+       /* Delete added in offline */
+
+       g_assert (e_cal_cache_remove_component (fixture->cal_cache, uid, NULL, E_CACHE_IS_OFFLINE, NULL, 
&error));
+       g_assert_no_error (error);
+
+       test_offline_add_one (fixture, "event-3", 3, EXPECT_EVENT_1 | SKIP_COMPONENT_PUT, NULL);
+
+       test_check_offline_changes (fixture, NULL);
+
+       g_clear_object (&component);
+}
+
+static void
+test_offline_add_delete_add (TCUFixture *fixture,
+                            gconstpointer user_data)
+{
+       ECalComponent *component = NULL;
+       const gchar *uid;
+       GError *error = NULL;
+
+       /* Add in online */
+       test_fill_cache (fixture, NULL);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, 3);
+       g_assert_no_error (error);
+
+       test_check_offline_changes (fixture, NULL);
+
+       /* Add in offline */
+       test_offline_add_one (fixture, "event-3", 4, EXPECT_EVENT_3 | EXPECT_EVENT_1, &component);
+       g_assert_nonnull (component);
+
+       test_check_offline_changes (fixture,
+               "event-3", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+
+       uid = icalcomponent_get_uid (e_cal_component_get_icalcomponent (component));
+       g_assert_nonnull (uid);
+
+       /* Delete added in offline */
+       g_assert (e_cal_cache_remove_component (fixture->cal_cache, uid, NULL, E_CACHE_IS_OFFLINE, NULL, 
&error));
+       g_assert_no_error (error);
+
+       test_offline_add_one (fixture, "event-3", 3, EXPECT_EVENT_1 | SKIP_COMPONENT_PUT, NULL);
+
+       test_check_offline_changes (fixture, NULL);
+
+       g_clear_object (&component);
+
+       /* Add in offline again */
+       test_offline_add_one (fixture, "event-3", 4, EXPECT_EVENT_3 | EXPECT_EVENT_1, NULL);
+
+       test_check_offline_changes (fixture,
+               "event-3", 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_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, 3);
+       g_assert_no_error (error);
+
+       test_check_offline_changes (fixture, NULL);
+
+       /* Add in offline */
+       test_offline_add_one (fixture, "event-3", 4, EXPECT_EVENT_3 | EXPECT_EVENT_1, NULL);
+
+       test_check_offline_changes (fixture,
+               "event-3", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+
+       /* Resync all offline changes */
+       g_assert (e_cache_clear_offline_changes (E_CACHE (fixture->cal_cache), NULL, &error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, 4);
+       g_assert_no_error (error);
+
+       test_basic_search (fixture, EXPECT_EVENT_3 | EXPECT_EVENT_1);
+       test_check_offline_changes (fixture, NULL);
+       test_check_offline_state (fixture, "event-3", E_OFFLINE_STATE_SYNCED);
+}
+
+static void
+test_offline_edit_common (TCUFixture *fixture,
+                         gchar **out_uid)
+{
+       ECalComponent *component = NULL;
+       const gchar *uid;
+       GError *error = NULL;
+
+       /* Add in online */
+       test_fill_cache (fixture, &component);
+       g_assert_nonnull (component);
+
+       uid = icalcomponent_get_uid (e_cal_component_get_icalcomponent (component));
+       g_assert_nonnull (uid);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, 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 */
+       icalcomponent_set_summary (e_cal_component_get_icalcomponent (component), "summ-2");
+
+       g_assert (e_cal_cache_put_component (fixture->cal_cache, component, NULL, E_CACHE_IS_OFFLINE, NULL, 
&error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, 3);
+       g_assert_no_error (error);
+
+       test_check_edit_saved (fixture, uid, "summ-2");
+
+       test_basic_search (fixture, EXPECT_EVENT_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 (&component);
+}
+
+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)
+{
+       ECalComponent *component = NULL;
+       gchar *uid = NULL;
+       GError *error = NULL;
+
+       test_offline_edit_common (fixture, &uid);
+
+       /* Delete the modified component in offline */
+       g_assert (e_cal_cache_remove_component (fixture->cal_cache, uid, NULL, E_CACHE_IS_OFFLINE, NULL, 
&error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, 2);
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_INCLUDE_DELETED, 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_cal_cache_get_component (fixture->cal_cache, uid, FALSE, &component, NULL, &error));
+       g_assert_error (error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND);
+       g_assert_null (component);
+
+       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->cal_cache), NULL, &error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, 3);
+       g_assert_no_error (error);
+
+       test_basic_search (fixture, EXPECT_EVENT_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)
+{
+       ECalComponent *component = NULL;
+       const gchar *uid;
+       GError *error = NULL;
+
+       /* Add in online */
+       test_fill_cache (fixture, &component);
+       g_assert_nonnull (component);
+
+       uid = icalcomponent_get_uid (e_cal_component_get_icalcomponent (component));
+       g_assert_nonnull (uid);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, 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_cal_cache_remove_component (fixture->cal_cache, uid, NULL, E_CACHE_IS_OFFLINE, NULL, 
&error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, 2);
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_INCLUDE_DELETED, 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 (&component);
+}
+
+static void
+test_offline_delete_add (TCUFixture *fixture,
+                        gconstpointer user_data)
+{
+       ECalComponent *component = NULL;
+       const gchar *uid;
+       GError *error = NULL;
+
+       /* Add in online */
+       test_fill_cache (fixture, &component);
+       g_assert_nonnull (component);
+
+       uid = icalcomponent_get_uid (e_cal_component_get_icalcomponent (component));
+       g_assert_nonnull (uid);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, 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, "event-3", 4, EXPECT_EVENT_3 | EXPECT_EVENT_1, NULL);
+       g_assert (e_cal_cache_remove_component (fixture->cal_cache, "event-3", NULL, E_CACHE_IS_OFFLINE, 
NULL, &error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, 3);
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_INCLUDE_DELETED, NULL, 
&error), ==, 3);
+       g_assert_no_error (error);
+
+       test_basic_search (fixture, EXPECT_EVENT_1);
+       test_check_offline_changes (fixture, NULL);
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_SYNCED);
+       test_check_offline_state (fixture, "event-3", E_OFFLINE_STATE_UNKNOWN);
+
+       /* Delete synced in offline */
+       g_assert (e_cal_cache_remove_component (fixture->cal_cache, uid, NULL, E_CACHE_IS_OFFLINE, NULL, 
&error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, 2);
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_INCLUDE_DELETED, 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, "event-3", 3, EXPECT_EVENT_3, NULL);
+
+       test_check_offline_changes (fixture,
+               uid, E_OFFLINE_STATE_LOCALLY_DELETED,
+               "event-3", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_LOCALLY_DELETED);
+       test_check_offline_state (fixture, "event-3", E_OFFLINE_STATE_LOCALLY_CREATED);
+
+       /* Modify the previous component and add it again */
+       icalcomponent_set_summary (e_cal_component_get_icalcomponent (component), "summ-3");
+
+       g_assert (e_cal_cache_put_component (fixture->cal_cache, component, NULL, E_CACHE_IS_OFFLINE, NULL, 
&error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, 4);
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_INCLUDE_DELETED, NULL, 
&error), ==, 4);
+       g_assert_no_error (error);
+
+       test_check_edit_saved (fixture, uid, "summ-3");
+
+       test_basic_search (fixture, EXPECT_EVENT_1 | EXPECT_EVENT_3);
+       test_check_offline_changes (fixture,
+               uid, E_OFFLINE_STATE_LOCALLY_MODIFIED,
+               "event-3", E_OFFLINE_STATE_LOCALLY_CREATED,
+               NULL);
+       test_check_offline_state (fixture, uid, E_OFFLINE_STATE_LOCALLY_MODIFIED);
+       test_check_offline_state (fixture, "event-3", E_OFFLINE_STATE_LOCALLY_CREATED);
+
+       g_clear_object (&component);
+}
+
+static void
+test_offline_delete_resync (TCUFixture *fixture,
+                           gconstpointer user_data)
+{
+       ECalComponent *component = NULL;
+       const gchar *uid;
+       GError *error = NULL;
+
+       /* Add in online */
+       test_fill_cache (fixture, &component);
+       g_assert_nonnull (component);
+
+       uid = icalcomponent_get_uid (e_cal_component_get_icalcomponent (component));
+       g_assert_nonnull (uid);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, 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_cal_cache_remove_component (fixture->cal_cache, uid, NULL, E_CACHE_IS_OFFLINE, NULL, 
&error));
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, 2);
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_INCLUDE_DELETED, 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->cal_cache), NULL, &error);
+       g_assert_no_error (error);
+
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_EXCLUDE_DELETED, NULL, 
&error), ==, 2);
+       g_assert_cmpint (e_cache_get_count (E_CACHE (fixture->cal_cache), E_CACHE_INCLUDE_DELETED, 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 (&component);
+}
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+       TCUClosure closure = { 0 };
+
+#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, "");
+
+       g_test_add ("/ECalCache/Offline/Basics", TCUFixture, &closure,
+               tcu_fixture_setup, test_offline_basics, tcu_fixture_teardown);
+       g_test_add ("/ECalCache/Offline/Add", TCUFixture, &closure,
+               tcu_fixture_setup, test_offline_add, tcu_fixture_teardown);
+       g_test_add ("/ECalCache/Offline/AddEdit", TCUFixture, &closure,
+               tcu_fixture_setup, test_offline_add_edit, tcu_fixture_teardown);
+       g_test_add ("/ECalCache/Offline/AddDelete", TCUFixture, &closure,
+               tcu_fixture_setup, test_offline_add_delete, tcu_fixture_teardown);
+       g_test_add ("/ECalCache/Offline/AddDeleteAdd", TCUFixture, &closure,
+               tcu_fixture_setup, test_offline_add_delete_add, tcu_fixture_teardown);
+       g_test_add ("/ECalCache/Offline/AddResync", TCUFixture, &closure,
+               tcu_fixture_setup, test_offline_add_resync, tcu_fixture_teardown);
+       g_test_add ("/ECalCache/Offline/Edit", TCUFixture, &closure,
+               tcu_fixture_setup, test_offline_edit, tcu_fixture_teardown);
+       g_test_add ("/ECalCache/Offline/EditDelete", TCUFixture, &closure,
+               tcu_fixture_setup, test_offline_edit_delete, tcu_fixture_teardown);
+       g_test_add ("/ECalCache/Offline/EditResync", TCUFixture, &closure,
+               tcu_fixture_setup, test_offline_edit_resync, tcu_fixture_teardown);
+       g_test_add ("/ECalCache/Offline/Delete", TCUFixture, &closure,
+               tcu_fixture_setup, test_offline_delete, tcu_fixture_teardown);
+       g_test_add ("/ECalCache/Offline/DeleteAdd", TCUFixture, &closure,
+               tcu_fixture_setup, test_offline_delete_add, tcu_fixture_teardown);
+       g_test_add ("/ECalCache/Offline/DeleteResync", TCUFixture, &closure,
+               tcu_fixture_setup, test_offline_delete_resync, tcu_fixture_teardown);
+
+       return g_test_run ();
+}
diff --git a/tests/libedata-cal/test-cal-cache-utils.c b/tests/libedata-cal/test-cal-cache-utils.c
new file mode 100644
index 0000000..b14e3a4
--- /dev/null
+++ b/tests/libedata-cal/test-cal-cache-utils.c
@@ -0,0 +1,155 @@
+/* -*- 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 <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "test-cal-cache-utils.h"
+
+static void
+delete_work_directory (const gchar *filename)
+{
+       /* XXX Instead of complex error checking here, we should ideally use
+        * a recursive GDir / g_unlink() function.
+        *
+        * We cannot use GFile and the recursive delete function without
+        * corrupting our contained D-Bus environment with service files
+        * from the OS.
+        */
+       const gchar *argv[] = { "/bin/rm", "-rf", filename, NULL };
+       gboolean spawn_succeeded;
+       gint exit_status;
+
+       spawn_succeeded = g_spawn_sync (
+               NULL, (gchar **) argv, NULL, 0, NULL, NULL,
+                                       NULL, NULL, &exit_status, NULL);
+
+       g_assert (spawn_succeeded);
+       #ifndef G_OS_WIN32
+       g_assert (WIFEXITED (exit_status));
+       g_assert_cmpint (WEXITSTATUS (exit_status), ==, 0);
+       #else
+       g_assert_cmpint (exit_status, ==, 0);
+       #endif
+}
+
+void
+tcu_fixture_setup (TCUFixture *fixture,
+                  gconstpointer user_data)
+{
+       /* TCUClosure *closure = (TCUClosure *) user_data; */
+       gchar *filename, *directory;
+       GError *error = NULL;
+
+       if (!g_file_test (CAMEL_PROVIDERDIR, G_FILE_TEST_IS_DIR | G_FILE_TEST_EXISTS)) {
+               if (g_mkdir_with_parents (CAMEL_PROVIDERDIR, 0700) == -1)
+                       g_warning ("%s: Failed to create folder '%s': %s\n", G_STRFUNC, CAMEL_PROVIDERDIR, 
g_strerror (errno));
+       }
+
+       /* Cleanup from last test */
+       directory = g_build_filename (g_get_tmp_dir (), "test-cal-cache", NULL);
+       delete_work_directory (directory);
+       g_free (directory);
+       filename = g_build_filename (g_get_tmp_dir (), "test-cal-cache", "cache.db", NULL);
+
+       fixture->cal_cache = e_cal_cache_new (filename, NULL, &error);
+
+       if (!fixture->cal_cache)
+               g_error ("Failed to create the ECalCache: %s", error->message);
+
+       g_free (filename);
+}
+
+void
+tcu_fixture_teardown (TCUFixture *fixture,
+                     gconstpointer user_data)
+{
+       g_object_unref (fixture->cal_cache);
+}
+
+gchar *
+tcu_new_icalstring_from_test_case (const gchar *case_name)
+{
+       gchar *filename;
+       gchar *case_filename;
+       GFile * file;
+       GError *error = NULL;
+       gchar *icalstring = NULL;
+
+       case_filename = g_strdup_printf ("%s.ics", case_name);
+
+       /* In the case of installed tests, they run in ${pkglibexecdir}/installed-tests
+        * and the components are installed in ${pkglibexecdir}/installed-tests/components
+        */
+       if (g_getenv ("TEST_INSTALLED_SERVICES") != NULL)
+               filename = g_build_filename (INSTALLED_TEST_DIR, "components", case_filename, NULL);
+       else
+               filename = g_build_filename (SRCDIR, "..", "libedata-cal", "components", case_filename, NULL);
+
+       file = g_file_new_for_path (filename);
+       if (!g_file_load_contents (file, NULL, &icalstring, NULL, NULL, &error))
+               g_error (
+                       "Failed to read test iCal string file '%s': %s",
+                       filename, error->message);
+
+       g_free (case_filename);
+       g_free (filename);
+       g_object_unref (file);
+
+       return icalstring;
+}
+
+ECalComponent *
+tcu_new_component_from_test_case (const gchar *case_name)
+{
+       gchar *icalstring;
+       ECalComponent *component = NULL;
+
+       icalstring = tcu_new_icalstring_from_test_case (case_name);
+       if (icalstring)
+               component = e_cal_component_new_from_string (icalstring);
+       g_free (icalstring);
+
+       if (!component)
+               g_error (
+                       "Failed to construct component from test case '%s'",
+                       case_name);
+
+       return component;
+}
+
+void
+tcu_add_component_from_test_case (TCUFixture *fixture,
+                                 const gchar *case_name,
+                                 ECalComponent **out_component)
+{
+       ECalComponent *component;
+       GError *error = NULL;
+
+       component = tcu_new_component_from_test_case (case_name);
+
+       if (!e_cal_cache_put_component (fixture->cal_cache, component, case_name, E_CACHE_IS_ONLINE, NULL, 
&error))
+               g_error ("Failed to add component: %s", error->message);
+
+       if (out_component)
+               *out_component = g_object_ref (component);
+
+       g_clear_object (&component);
+}
diff --git a/tests/libedata-cal/test-cal-cache-utils.h b/tests/libedata-cal/test-cal-cache-utils.h
new file mode 100644
index 0000000..373f93e
--- /dev/null
+++ b/tests/libedata-cal/test-cal-cache-utils.h
@@ -0,0 +1,46 @@
+/* -*- 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/>.
+ */
+
+#ifndef TEST_CACHE_UTILS_H
+#define TEST_CACHE_UTILS_H
+
+#include <libedata-cal/libedata-cal.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+       ECalCache *cal_cache;
+} TCUFixture;
+
+typedef struct {
+       gint dummy;
+} TCUClosure;
+
+void           tcu_fixture_setup                       (TCUFixture *fixture,
+                                                        gconstpointer user_data);
+void           tcu_fixture_teardown                    (TCUFixture *fixture,
+                                                        gconstpointer user_data);
+
+gchar *                tcu_new_icalstring_from_test_case       (const gchar *case_name);
+ECalComponent *        tcu_new_component_from_test_case        (const gchar *case_name);
+void           tcu_add_component_from_test_case        (TCUFixture *fixture,
+                                                        const gchar *case_name,
+                                                        ECalComponent **out_component);
+
+G_END_DECLS
+
+#endif /* TEST_CACHE_UTILS_H */


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