[evolution-data-server/cursor-staging: 21/25] Added tests for the low level cursor API



commit d8f82c9597ee2e329a87442280f5a1e53320f8cd
Author: Tristan Van Berkom <tristanvb openismus com>
Date:   Fri Apr 12 15:37:27 2013 +0900

    Added tests for the low level cursor API
    
      o test-sqlite-create-cursor
    
        The new test case verifies that cursor creation is well protected
        (cursor creation is denied for some queries and ordering requests).
    
      o test-sqlite-cursor-move-by
    
        This test case asserts that e_book_backend_sqlite_cursor_move_by()
        works as expected. Consequently, a hand full of additional test
        vcards were added for this.
    
      o test-sqlite-cursor-set-target.c
    
        This test case tests e_book_backend_sqlitedb_cursor_set_target().
    
        The special case of setting a partial state is tested so that when
        a cursor is setup to sort by "family_name, given_name" and the target
        is set to "J", then we recieve results inclusive of the contact with
        the family name "J".
    
      o test-sqlite-cursor-calculate.c
    
        This test verifies that e_book_backend_sqlitedb_cursor_calculate() report
        the right position and total values when the cursor is moved, after the
        target is set, after the addressbook is modified, for filtered and unfiltered
        result sets.
    
      o test-sqlite-cursor-set-sexp.c
    
        This test verifies that invalid queries are rejected, and that a cursor
        position is changed by the new sexp with the correct new filtered values
        after applying a filter (sexp) to the cursor.
    
      o Tests to ensure that localized data is properly migrated:
          - test-sqlite-cursor-posix-initial
          - test-sqlite-cursor-en-US-migrated
          - test-sqlite-cursor-fr-CA-migrated
          - test-sqlite-cursor-de-DE-migrated
          - test-sqlite-cursor-posix-migrated

 configure.ac                                       |    1 +
 tests/Makefile.am                                  |    2 +-
 tests/libebook/Makefile.am                         |   20 +
 tests/libebook/data/vcards/sorted-1.vcf            |    6 +
 tests/libebook/data/vcards/sorted-10.vcf           |    6 +
 tests/libebook/data/vcards/sorted-11.vcf           |    6 +
 tests/libebook/data/vcards/sorted-12.vcf           |    7 +
 tests/libebook/data/vcards/sorted-13.vcf           |    6 +
 tests/libebook/data/vcards/sorted-14.vcf           |    6 +
 tests/libebook/data/vcards/sorted-15.vcf           |    6 +
 tests/libebook/data/vcards/sorted-16.vcf           |    6 +
 tests/libebook/data/vcards/sorted-17.vcf           |    6 +
 tests/libebook/data/vcards/sorted-18.vcf           |    6 +
 tests/libebook/data/vcards/sorted-19.vcf           |    6 +
 tests/libebook/data/vcards/sorted-2.vcf            |    7 +
 tests/libebook/data/vcards/sorted-20.vcf           |    6 +
 tests/libebook/data/vcards/sorted-3.vcf            |    7 +
 tests/libebook/data/vcards/sorted-4.vcf            |    6 +
 tests/libebook/data/vcards/sorted-5.vcf            |    6 +
 tests/libebook/data/vcards/sorted-6.vcf            |    5 +
 tests/libebook/data/vcards/sorted-7.vcf            |    5 +
 tests/libebook/data/vcards/sorted-8.vcf            |    6 +
 tests/libebook/data/vcards/sorted-9.vcf            |    6 +
 tests/libedata-book/Makefile.am                    |  114 +++
 tests/libedata-book/data-test-utils.c              |  756 ++++++++++++++++++++
 tests/libedata-book/data-test-utils.h              |  164 +++++
 tests/libedata-book/test-sqlite-create-cursor.c    |  131 ++++
 tests/libedata-book/test-sqlite-cursor-calculate.c |  647 +++++++++++++++++
 .../test-sqlite-cursor-change-locale.c             |   52 ++
 .../test-sqlite-cursor-de-DE-migrated.c            |   32 +
 .../test-sqlite-cursor-en-US-migrated.c            |   33 +
 .../test-sqlite-cursor-fr-CA-migrated.c            |   32 +
 .../test-sqlite-cursor-move-by-de-DE.c             |   46 ++
 .../test-sqlite-cursor-move-by-en-US.c             |   63 ++
 .../test-sqlite-cursor-move-by-fr-CA.c             |   46 ++
 .../test-sqlite-cursor-move-by-posix.c             |   46 ++
 .../test-sqlite-cursor-posix-initial.c             |   35 +
 .../test-sqlite-cursor-posix-migrated.c            |   32 +
 tests/libedata-book/test-sqlite-cursor-set-sexp.c  |  104 +++
 .../libedata-book/test-sqlite-cursor-set-target.c  |  180 +++++
 tests/libedata-book/test-sqlite-get-contact.c      |   56 ++
 41 files changed, 2712 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 72b52cf..a57a7c2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1789,6 +1789,7 @@ services/evolution-calendar-factory/Makefile
 services/evolution-source-registry/Makefile
 services/evolution-user-prompter/Makefile
 tests/Makefile
+tests/libedata-book/Makefile
 tests/libebook/Makefile
 tests/libebook-contacts/Makefile
 tests/libebook/client/Makefile
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 53b8924..db8304b 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = test-server-utils libedataserver libebook-contacts libebook book-migration libecal libedata-cal
+SUBDIRS = test-server-utils libedataserver libebook-contacts libedata-book libebook book-migration libecal 
libedata-cal
 
 @GNOME_CODE_COVERAGE_RULES@
 
diff --git a/tests/libebook/Makefile.am b/tests/libebook/Makefile.am
index 1e0ba45..dc3663d 100644
--- a/tests/libebook/Makefile.am
+++ b/tests/libebook/Makefile.am
@@ -62,6 +62,26 @@ EXTRA_DIST = \
        $(srcdir)/data/vcards/name-only.vcf     \
        $(srcdir)/data/vcards/simple-1.vcf      \
        $(srcdir)/data/vcards/simple-2.vcf      \
+       $(srcdir)/data/vcards/sorted-1.vcf      \
+       $(srcdir)/data/vcards/sorted-2.vcf      \
+       $(srcdir)/data/vcards/sorted-3.vcf      \
+       $(srcdir)/data/vcards/sorted-4.vcf      \
+       $(srcdir)/data/vcards/sorted-5.vcf      \
+       $(srcdir)/data/vcards/sorted-6.vcf      \
+       $(srcdir)/data/vcards/sorted-7.vcf      \
+       $(srcdir)/data/vcards/sorted-8.vcf      \
+       $(srcdir)/data/vcards/sorted-9.vcf      \
+       $(srcdir)/data/vcards/sorted-10.vcf     \
+       $(srcdir)/data/vcards/sorted-11.vcf     \
+       $(srcdir)/data/vcards/sorted-12.vcf     \
+       $(srcdir)/data/vcards/sorted-13.vcf     \
+       $(srcdir)/data/vcards/sorted-14.vcf     \
+       $(srcdir)/data/vcards/sorted-15.vcf     \
+       $(srcdir)/data/vcards/sorted-16.vcf     \
+       $(srcdir)/data/vcards/sorted-17.vcf     \
+       $(srcdir)/data/vcards/sorted-18.vcf     \
+       $(srcdir)/data/vcards/sorted-19.vcf     \
+       $(srcdir)/data/vcards/sorted-20.vcf     \
        $(srcdir)/data/vcards/custom-1.vcf      \
        $(srcdir)/data/vcards/custom-2.vcf      \
        $(srcdir)/data/vcards/custom-3.vcf      \
diff --git a/tests/libebook/data/vcards/sorted-1.vcf b/tests/libebook/data/vcards/sorted-1.vcf
new file mode 100644
index 0000000..bd4fdbf
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-1.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+UID:sorted-1
+N:bad;First Name
+TEL;HOME:+1-221-5423789
+EMAIL;TYPE=home,work:micheal jackson com
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-10.vcf b/tests/libebook/data/vcards/sorted-10.vcf
new file mode 100644
index 0000000..3fbb99b
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-10.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+UID:sorted-10
+N:C;First Name
+TEL;HOME:+1-221-5423789
+EMAIL;TYPE=home,work:mister jackson com
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-11.vcf b/tests/libebook/data/vcards/sorted-11.vcf
new file mode 100644
index 0000000..b820cc8
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-11.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+UID:sorted-11
+FN:Ye Nameless One
+TEL;HOME:+1-221-5423789
+EMAIL;TYPE=home,work:name less com
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-12.vcf b/tests/libebook/data/vcards/sorted-12.vcf
new file mode 100644
index 0000000..b600d03
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-12.vcf
@@ -0,0 +1,7 @@
+BEGIN:VCARD
+UID:sorted-12
+N:coté;First Name
+TEL;HOME:514-845-8436
+EMAIL;TYPE=home,work:pink pony com
+END:VCARD
+
diff --git a/tests/libebook/data/vcards/sorted-13.vcf b/tests/libebook/data/vcards/sorted-13.vcf
new file mode 100644
index 0000000..a13875c
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-13.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+UID:sorted-13
+N:côte;First Name
+TEL;HOME:514-845-8436
+EMAIL;TYPE=home,work:pink pony org
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-14.vcf b/tests/libebook/data/vcards/sorted-14.vcf
new file mode 100644
index 0000000..cd803b5
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-14.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+UID:sorted-14
+N:cote;First Name
+TEL;HOME:514-845-8436
+EMAIL;TYPE=home,work:pink pony com
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-15.vcf b/tests/libebook/data/vcards/sorted-15.vcf
new file mode 100644
index 0000000..403bc70
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-15.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+UID:sorted-15
+N:black-bird;First Name
+TEL;HOME:514-845-8436
+EMAIL;TYPE=home,work:pink pony org
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-16.vcf b/tests/libebook/data/vcards/sorted-16.vcf
new file mode 100644
index 0000000..6e845db
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-16.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+UID:sorted-16
+N:blackbird;First Name
+TEL;HOME:514-845-8436
+EMAIL;TYPE=home,work:pink pony com
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-17.vcf b/tests/libebook/data/vcards/sorted-17.vcf
new file mode 100644
index 0000000..5f62f4d
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-17.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+UID:sorted-17
+N:black-birds;First Name
+TEL;HOME:514-845-8436
+EMAIL;TYPE=home,work:pink pony com
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-18.vcf b/tests/libebook/data/vcards/sorted-18.vcf
new file mode 100644
index 0000000..c3c3b69
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-18.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+UID:sorted-18
+N:blackbirds;First Name
+TEL;HOME:514-845-8436
+EMAIL;TYPE=home,work:pink pony com
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-19.vcf b/tests/libebook/data/vcards/sorted-19.vcf
new file mode 100644
index 0000000..2971aa2
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-19.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+UID:sorted-19
+N:Muffler;First Name
+TEL;HOME:514-845-8436
+EMAIL;TYPE=home,work:pink pony net
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-2.vcf b/tests/libebook/data/vcards/sorted-2.vcf
new file mode 100644
index 0000000..9a9b8a2
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-2.vcf
@@ -0,0 +1,7 @@
+BEGIN:VCARD
+UID:sorted-2
+N:Bad;First Name
+TEL;HOME:7654321
+EMAIL:janet jackson com
+EMAIL:janny jackson com
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-20.vcf b/tests/libebook/data/vcards/sorted-20.vcf
new file mode 100644
index 0000000..2d4c8ee
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-20.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+UID:sorted-20
+N:Müller;First Name
+TEL;HOME:514-845-8436
+EMAIL;TYPE=home,work:pink pony net
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-3.vcf b/tests/libebook/data/vcards/sorted-3.vcf
new file mode 100644
index 0000000..5f75eeb
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-3.vcf
@@ -0,0 +1,7 @@
+BEGIN:VCARD
+UID:sorted-3
+N:Bat;First Name
+TEL;HOME:+9999999
+EMAIL;TYPE=work:bobby brown org
+EMAIL;TYPE=home,work:bobby brown com
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-4.vcf b/tests/libebook/data/vcards/sorted-4.vcf
new file mode 100644
index 0000000..888b89c
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-4.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+UID:sorted-4
+N:bat;First Name
+TEL;TYPE=work,pref:+9999999
+EMAIL:big bobby brown org
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-5.vcf b/tests/libebook/data/vcards/sorted-5.vcf
new file mode 100644
index 0000000..bf6c11a
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-5.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+UID:sorted-5
+N:bäd;First Name
+TEL;HOME:+6666666
+EMAIL;TYPE=home,work:james brown com
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-6.vcf b/tests/libebook/data/vcards/sorted-6.vcf
new file mode 100644
index 0000000..2af7b7d
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-6.vcf
@@ -0,0 +1,5 @@
+BEGIN:VCARD
+UID:sorted-6
+N:Bäd;First Name
+TEL;HOME:ask Jenny for Lisa's number
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-7.vcf b/tests/libebook/data/vcards/sorted-7.vcf
new file mode 100644
index 0000000..4a9c3e6
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-7.vcf
@@ -0,0 +1,5 @@
+BEGIN:VCARD
+UID:sorted-7
+N:bät;First Name
+TEL;HOME:+49-89-7888 99
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-8.vcf b/tests/libebook/data/vcards/sorted-8.vcf
new file mode 100644
index 0000000..456181e
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-8.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+UID:sorted-8
+N:Bät;First Name
+TEL;HOME:+31-221-5423789
+EMAIL;TYPE=home,work:purple pony com
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-9.vcf b/tests/libebook/data/vcards/sorted-9.vcf
new file mode 100644
index 0000000..9a825d6
--- /dev/null
+++ b/tests/libebook/data/vcards/sorted-9.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+UID:sorted-9
+N:côté;First Name
+TEL;HOME:514-845-8436
+EMAIL;TYPE=home,work:pink pony com
+END:VCARD
diff --git a/tests/libedata-book/Makefile.am b/tests/libedata-book/Makefile.am
new file mode 100644
index 0000000..75324f4
--- /dev/null
+++ b/tests/libedata-book/Makefile.am
@@ -0,0 +1,114 @@
+
+noinst_LTLIBRARIES = libdata-test-utils.la
+
+libdata_test_utils_la_SOURCES =                                        \
+       data-test-utils.c                                       \
+       data-test-utils.h                                       \
+       $(NULL)
+
+libdata_test_utils_la_CPPFLAGS =                               \
+       $(AM_CPPFLAGS)                                          \
+       -I$(top_srcdir)                                         \
+       -I$(top_builddir)                                       \
+       -I$(top_srcdir)/addressbook                             \
+       -I$(top_builddir)/addressbook                           \
+       -I$(top_srcdir)/calendar                                \
+       -I$(top_builddir)/calendar                              \
+       -I$(top_srcdir)/tests/libedataserver                    \
+       -I$(top_builddir)/tests/libedataserver                  \
+       -I$(top_srcdir)/tests/test-server-utils                 \
+       -I$(top_builddir)/tests/test-server-utils               \
+       -I$(top_srcdir)/private                                 \
+       -I$(top_builddir)/private                               \
+       -DBACKENDDIR=\"$(ebook_backenddir)\"                    \
+       -DDATADIR=\"$(datadir)\"                                \
+       -DSRCDIR=\""$(abs_srcdir)"\"                            \
+       -DBUILDDIR=\""$(abs_topbuilddir)"\"                     \
+       $(EVOLUTION_ADDRESSBOOK_CFLAGS)                         \
+       $(GIO_UNIX_CFLAGS)                                      \
+       $(CAMEL_CFLAGS)                                         \
+       $(NULL)
+
+libdata_test_utils_la_LIBADD =                                 \
+       $(top_builddir)/addressbook/libebook-contacts/libebook-contacts-1.2.la          \
+       $(top_builddir)/addressbook/libedata-book/libedata-book-1.2.la  \
+       $(top_builddir)/addressbook/libebook/libebook-1.2.la    \
+       $(top_builddir)/tests/test-server-utils/libetestserverutils.la  \
+       $(top_builddir)/private/libedbus-private.la             \
+       $(EVOLUTION_ADDRESSBOOK_LIBS)                           \
+       $(GIO_UNIX_LIBS)                                        \
+       $(CAMEL_LIBS)                                           \
+       $(NULL)
+
+# Should be kept ordered approximately from least to most difficult/complex
+#
+# Note that these tests must be run in order:
+#   test-sqlite-cursor-posix-initial,
+#   test-sqlite-cursor-en-US-migrated,
+#   test-sqlite-cursor-posix-migrated
+#
+# This is because each migrated test changes the
+# locale and reloads the same addressbook of the previous test. 
+TESTS =                                                \
+       test-sqlite-get-contact                 \
+       test-sqlite-create-cursor               \
+       test-sqlite-cursor-move-by-posix        \
+       test-sqlite-cursor-move-by-en-US        \
+       test-sqlite-cursor-move-by-fr-CA        \
+       test-sqlite-cursor-move-by-de-DE        \
+       test-sqlite-cursor-set-target           \
+       test-sqlite-cursor-calculate            \
+       test-sqlite-cursor-set-sexp             \
+       test-sqlite-cursor-posix-initial        \
+       test-sqlite-cursor-en-US-migrated       \
+       test-sqlite-cursor-fr-CA-migrated       \
+       test-sqlite-cursor-de-DE-migrated       \
+       test-sqlite-cursor-posix-migrated       \
+       test-sqlite-cursor-change-locale
+
+noinst_PROGRAMS = $(TESTS)
+
+TEST_CPPFLAGS =                                                        \
+       $(libdata_test_utils_la_CPPFLAGS)                       \
+       $(EVOLUTION_ADDRESSBOOK_CPPFLAGS)                       \
+       $(NULL)
+
+TEST_LIBS =                                                    \
+       $(libdata_test_utils_la_LIBADD)                 \
+       libdata-test-utils.la                                   \
+       $(top_builddir)/addressbook/libebook/libebook-1.2.la    \
+       $(EVOLUTION_ADDRESSBOOK_LIBS)                           \
+       $(NULL)
+
+test_sqlite_get_contact_LDADD=$(TEST_LIBS)
+test_sqlite_get_contact_CPPFLAGS=$(TEST_CPPFLAGS)
+test_sqlite_create_cursor_LDADD=$(TEST_LIBS)
+test_sqlite_create_cursor_CPPFLAGS=$(TEST_CPPFLAGS)
+test_sqlite_cursor_move_by_posix_LDADD=$(TEST_LIBS)
+test_sqlite_cursor_move_by_posix_CPPFLAGS=$(TEST_CPPFLAGS)
+test_sqlite_cursor_move_by_en_US_LDADD=$(TEST_LIBS)
+test_sqlite_cursor_move_by_en_US_CPPFLAGS=$(TEST_CPPFLAGS)
+test_sqlite_cursor_move_by_fr_CA_LDADD=$(TEST_LIBS)
+test_sqlite_cursor_move_by_fr_CA_CPPFLAGS=$(TEST_CPPFLAGS)
+test_sqlite_cursor_move_by_de_DE_LDADD=$(TEST_LIBS)
+test_sqlite_cursor_move_by_de_DE_CPPFLAGS=$(TEST_CPPFLAGS)
+test_sqlite_cursor_set_target_LDADD=$(TEST_LIBS)
+test_sqlite_cursor_set_target_CPPFLAGS=$(TEST_CPPFLAGS)
+test_sqlite_cursor_calculate_LDADD=$(TEST_LIBS)
+test_sqlite_cursor_calculate_CPPFLAGS=$(TEST_CPPFLAGS)
+test_sqlite_cursor_set_sexp_LDADD=$(TEST_LIBS)
+test_sqlite_cursor_set_sexp_CPPFLAGS=$(TEST_CPPFLAGS)
+test_sqlite_cursor_posix_initial_LDADD=$(TEST_LIBS)
+test_sqlite_cursor_posix_initial_CPPFLAGS=$(TEST_CPPFLAGS)
+test_sqlite_cursor_en_US_migrated_LDADD=$(TEST_LIBS)
+test_sqlite_cursor_en_US_migrated_CPPFLAGS=$(TEST_CPPFLAGS)
+test_sqlite_cursor_fr_CA_migrated_LDADD=$(TEST_LIBS)
+test_sqlite_cursor_fr_CA_migrated_CPPFLAGS=$(TEST_CPPFLAGS)
+test_sqlite_cursor_de_DE_migrated_LDADD=$(TEST_LIBS)
+test_sqlite_cursor_de_DE_migrated_CPPFLAGS=$(TEST_CPPFLAGS)
+test_sqlite_cursor_posix_migrated_LDADD=$(TEST_LIBS)
+test_sqlite_cursor_posix_migrated_CPPFLAGS=$(TEST_CPPFLAGS)
+test_sqlite_cursor_change_locale_LDADD=$(TEST_LIBS)
+test_sqlite_cursor_change_locale_CPPFLAGS=$(TEST_CPPFLAGS)
+
+-include $(top_srcdir)/git.mk
diff --git a/tests/libedata-book/data-test-utils.c b/tests/libedata-book/data-test-utils.c
new file mode 100644
index 0000000..b5f3ffc
--- /dev/null
+++ b/tests/libedata-book/data-test-utils.c
@@ -0,0 +1,756 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2013, Openismus GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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
+ * 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, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Authors: Tristan Van Berkom <tristanvb openismus com>
+ */
+
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "data-test-utils.h"
+
+/* This forces the GType to be registered in a way that
+ * avoids a "statement with no effect" compiler warning.
+ * FIXME Use g_type_ensure() once we require GLib 2.34. */
+#define REGISTER_TYPE(type) \
+       (g_type_class_unref (g_type_class_ref (type)))
+
+
+#define SQLITEDB_EMAIL_ID    "addressbook localbackend com"
+#define SQLITEDB_FOLDER_NAME "folder"
+
+gchar *
+new_vcard_from_test_case (const gchar *case_name)
+{
+       gchar *filename;
+       gchar *case_filename;
+       GFile * file;
+       GError *error = NULL;
+       gchar *vcard;
+
+       case_filename = g_strdup_printf ("%s.vcf", case_name);
+       filename = g_build_filename (SRCDIR, "..", "libebook", "data", "vcards", case_filename, NULL);
+       file = g_file_new_for_path (filename);
+       if (!g_file_load_contents (file, NULL, &vcard, NULL, NULL, &error))
+               g_error ("failed to read test contact file '%s': %s",
+                        filename, error->message);
+
+       g_free (case_filename);
+       g_free (filename);
+       g_object_unref (file);
+
+       return vcard;
+}
+
+EContact *
+new_contact_from_test_case (const gchar *case_name)
+{
+       gchar *vcard;
+       EContact *contact = NULL;
+
+       vcard = new_vcard_from_test_case (case_name);
+       if (vcard)
+               contact = e_contact_new_from_vcard (vcard);
+       g_free (vcard);
+
+       return contact;
+}
+
+static gboolean
+contacts_are_equal_shallow (EContact *a,
+                            EContact *b)
+{
+       const gchar *uid_a, *uid_b;
+
+        /* Avoid warnings if one or more are NULL, to make this function
+         * "NULL-friendly" */
+       if (!a && !b)
+               return TRUE;
+
+       if (!E_IS_CONTACT (a) || !E_IS_CONTACT (b))
+               return FALSE;
+
+       uid_a = e_contact_get_const (a, E_CONTACT_UID);
+       uid_b = e_contact_get_const (b, E_CONTACT_UID);
+
+       return g_strcmp0 (uid_a, uid_b) == 0;
+}
+
+gboolean
+add_contact_from_test_case_verify (EBookClient *book_client,
+                                   const gchar *case_name,
+                                   EContact **contact)
+{
+       EContact *contact_orig;
+       EContact *contact_final;
+       gchar *uid;
+       GError *error = NULL;
+
+       contact_orig = new_contact_from_test_case (case_name);
+
+       if (!e_book_client_add_contact_sync (book_client, contact_orig, &uid, NULL, &error))
+               g_error ("Failed to add contact: %s", error->message);
+
+       e_contact_set (contact_orig, E_CONTACT_UID, uid);
+
+       if (!e_book_client_get_contact_sync (book_client, uid, &contact_final, NULL, &error))
+               g_error ("Failed to get contact: %s", error->message);
+
+        /* verify the contact was added "successfully" (not thorough) */
+       g_assert (contacts_are_equal_shallow (contact_orig, contact_final));
+
+       if (contact)
+                *contact = contact_final;
+       else
+               g_object_unref (contact_final);
+       g_object_unref (contact_orig);
+       g_free (uid);
+
+       return TRUE;
+}
+
+static gchar *
+get_addressbook_directory (ESourceRegistry *registry,
+                          ESource         *source)
+{
+       ESource *builtin_source;
+       const gchar *user_data_dir;
+       const gchar *uid;
+       gchar *filename = NULL;
+
+       uid = e_source_get_uid (source);
+       g_return_val_if_fail (uid != NULL, NULL);
+
+       user_data_dir = e_get_user_data_dir ();
+
+       builtin_source = e_source_registry_ref_builtin_address_book (registry);
+
+       /* Special case directory for the builtin addressbook source */
+       if (builtin_source != NULL && e_source_equal (source, builtin_source))
+               uid = "system";
+
+       filename = g_build_filename (user_data_dir, "addressbook", uid, NULL);
+
+       if (builtin_source)
+               g_object_unref (builtin_source);
+
+       return filename;
+}
+
+static EBookBackendSqliteDB *
+open_sqlitedb (ESourceRegistry *registry,
+              ESource         *source)
+{
+       EBookBackendSqliteDB *ebsdb;
+       GError *error;
+       gchar *dirname;
+
+       dirname = get_addressbook_directory (registry, source);
+       ebsdb   = e_book_backend_sqlitedb_new (dirname,
+                                              SQLITEDB_EMAIL_ID,
+                                              SQLITEDB_FOLDER_ID,
+                                              SQLITEDB_FOLDER_NAME,
+                                              TRUE, &error);
+
+       if (!ebsdb)
+               g_error ("Failed to open SQLite backend: %s", error->message);
+
+       g_free (dirname);
+
+       return ebsdb;
+}
+
+void
+e_sqlitedb_fixture_setup (ESqliteDBFixture *fixture,
+                         gconstpointer     user_data)
+{
+       EBookClient *book_client;
+
+       e_test_server_utils_setup ((ETestServerFixture *)fixture, user_data);
+
+       book_client = E_TEST_SERVER_UTILS_SERVICE (fixture, EBookClient);
+       fixture->ebsdb = open_sqlitedb (((ETestServerFixture *)fixture)->registry,
+                                       e_client_get_source (E_CLIENT (book_client)));
+}
+
+void
+e_sqlitedb_fixture_teardown (ESqliteDBFixture *fixture,
+                            gconstpointer     user_data)
+{
+       g_object_unref (fixture->ebsdb);
+       e_test_server_utils_teardown ((ETestServerFixture *)fixture, user_data);
+}
+
+void
+e_sqlitedb_cursor_fixture_setup_book (ESource            *scratch,
+                                     ETestServerClosure *closure)
+{
+       ESourceBackendSummarySetup *setup;
+
+       g_type_class_unref (g_type_class_ref (E_TYPE_SOURCE_BACKEND_SUMMARY_SETUP));
+       setup = e_source_get_extension (scratch, E_SOURCE_EXTENSION_BACKEND_SUMMARY_SETUP);
+       e_source_backend_summary_setup_set_summary_fields (setup,
+                                                          E_CONTACT_FAMILY_NAME,
+                                                          E_CONTACT_GIVEN_NAME,
+                                                          E_CONTACT_EMAIL,
+                                                          0);
+       e_source_backend_summary_setup_set_indexed_fields (setup,
+                                                          E_CONTACT_FAMILY_NAME, E_BOOK_INDEX_PREFIX,
+                                                          E_CONTACT_GIVEN_NAME, E_BOOK_INDEX_PREFIX,
+                                                          E_CONTACT_EMAIL, E_BOOK_INDEX_PREFIX,
+                                                          0);
+}
+
+void
+e_sqlitedb_cursor_fixture_setup (EbSdbCursorFixture *fixture,
+                                gconstpointer       user_data)
+{
+       ETestServerFixture *base_fixture = (ETestServerFixture *)fixture;
+       ESqliteDBFixture *ebsdb_fixture = (ESqliteDBFixture *)fixture;
+       EbSdbCursorClosure *data = (EbSdbCursorClosure *)user_data;
+       EContactField sort_fields[] = { E_CONTACT_FAMILY_NAME, E_CONTACT_GIVEN_NAME };
+       EBookCursorSortType sort_types[] = { data->sort_type, data->sort_type };
+       EBookClient *book_client;
+       GSList *contacts = NULL;
+       GError *error = NULL;
+       gint i;
+       gchar *sexp = NULL;
+       const gchar *source_name;
+
+       /* Support the migration tests */
+       source_name = g_getenv ("MIGRATION_TEST_SOURCE_NAME");
+       if (source_name != NULL)
+               base_fixture->source_name = g_strdup (source_name);
+
+       /* Setup the EBookClient, but don't open the EBookBackendSqliteDB until after
+        * we've specified the locale
+        */
+       e_test_server_utils_setup ((ETestServerFixture *)fixture, user_data);
+       book_client = E_TEST_SERVER_UTILS_SERVICE (fixture, EBookClient);
+
+       if (data->locale)
+               e_sqlitedb_cursor_fixture_set_locale (fixture, data->locale);
+       else
+               e_sqlitedb_cursor_fixture_set_locale (fixture, "en_US.UTF-8");
+
+       /* Now open the EBookBackendSqliteDB */
+       ebsdb_fixture->ebsdb = open_sqlitedb (((ETestServerFixture *)fixture)->registry,
+                                             e_client_get_source (E_CLIENT (book_client)));
+
+       for (i = 0; i < N_SORTED_CONTACTS; i++) {
+               gchar *case_name = g_strdup_printf ("sorted-%d", i + 1);
+               gchar *vcard;
+               EContact *contact;
+
+               vcard    = new_vcard_from_test_case (case_name);
+               contact  = e_contact_new_from_vcard (vcard);
+               contacts = g_slist_prepend (contacts, contact);
+               g_free (vcard);
+               g_free (case_name);
+
+               fixture->contacts[i] = contact;
+       }
+
+       if (!e_book_client_add_contacts_sync (book_client, contacts, NULL, NULL, &error)) { 
+
+               /* Dont complain here, we re-use the same addressbook for multiple tests
+                * and we can't add the same contacts twice
+                */
+               if (g_error_matches (error, E_BOOK_CLIENT_ERROR,
+                                    E_BOOK_CLIENT_ERROR_CONTACT_ID_ALREADY_EXISTS))
+                       g_clear_error (&error);
+               else
+                       g_error ("Failed to add test contacts: %s", error->message);
+       }
+
+       g_slist_free (contacts);
+
+       /* Allow a surrounding fixture setup to add a query here */
+       if (fixture->query) {
+               sexp = e_book_query_to_string (fixture->query);
+               e_book_query_unref (fixture->query);
+               fixture->query = NULL;
+       }
+
+       fixture->cursor = e_book_backend_sqlitedb_cursor_new (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                             SQLITEDB_FOLDER_ID,
+                                                             sexp, sort_fields, sort_types, 2, &error);
+
+       g_free (sexp);
+
+       g_assert (fixture->cursor != NULL);
+}
+
+void
+e_sqlitedb_cursor_fixture_filtered_setup (EbSdbCursorFixture *fixture,
+                                         gconstpointer  user_data)
+{
+       fixture->query = e_book_query_field_test (E_CONTACT_EMAIL, E_BOOK_QUERY_ENDS_WITH, ".com");
+
+       e_sqlitedb_cursor_fixture_setup (fixture, user_data);
+}
+
+void
+e_sqlitedb_cursor_fixture_teardown (EbSdbCursorFixture *fixture,
+                                   gconstpointer       user_data)
+{
+       gint i;
+
+       for (i = 0; i < N_SORTED_CONTACTS; i++) {
+               if (fixture->contacts[i])
+                       g_object_unref (fixture->contacts[i]);
+       }
+
+       if (fixture->locale1)
+               g_object_unref (fixture->locale1);
+
+       if (fixture->own_id > 0)
+               g_bus_unown_name (fixture->own_id);
+
+       e_book_backend_sqlitedb_cursor_free (((ESqliteDBFixture *) fixture)->ebsdb, fixture->cursor);
+       e_sqlitedb_fixture_teardown ((ESqliteDBFixture *)fixture, user_data);
+}
+
+typedef struct {
+       EbSdbCursorFixture *fixture;
+       const gchar *locale;
+} ChangeLocaleData;
+
+static void
+book_client_locale_change (EBookClient *book,
+                          GParamSpec  *pspec,
+                          ChangeLocaleData *data)
+{
+       ETestServerFixture *base_fixture = (ETestServerFixture *)data->fixture;
+
+       if (!g_strcmp0 (e_book_client_get_locale (book), data->locale))
+               g_main_loop_quit (base_fixture->loop);
+}
+
+void
+e_sqlitedb_cursor_fixture_set_locale (EbSdbCursorFixture *fixture,
+                                     const gchar        *locale)
+{
+       ETestServerFixture *base_fixture = (ETestServerFixture *)fixture;
+       EBookClient *book_client;
+       ChangeLocaleData data = { fixture, locale };
+       gulong handler_id;
+       gchar *strv[2] = { NULL, NULL };
+
+       book_client = E_TEST_SERVER_UTILS_SERVICE (fixture, EBookClient);
+
+       /* We're already in the right locale */
+       if (g_strcmp0 (locale, e_book_client_get_locale (book_client)) == 0)
+               return;
+
+       if (!fixture->locale1) {
+               GDBusConnection *bus;
+               GError *error = NULL;
+
+               /* We use the 'org.freedesktop.locale1 on the session bus instead
+                * of the system bus only for testing purposes... in real life
+                * this service is on the system bus.
+                */
+               bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+               if (!bus)
+                       g_error ("Failed to get system bus: %s", error->message);
+
+               fixture->locale1 = e_dbus_locale1_skeleton_new ();
+
+               /* Set initial locale before exporting on the bus */
+               strv[0] = g_strdup_printf ("LANG=%s", locale);
+               e_dbus_locale1_set_locale (fixture->locale1, (const gchar * const *)strv);
+               g_free (strv[0]);
+
+               if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (fixture->locale1),
+                                                      bus, "/org/freedesktop/locale1", &error))
+                       g_error ("Failed to export org.freedesktop.locale1: %s", error->message);
+
+               fixture->own_id =
+                       g_bus_own_name_on_connection (bus,
+                                                     "org.freedesktop.locale1",
+                                                     G_BUS_NAME_OWNER_FLAGS_REPLACE,
+                                                     NULL, NULL, NULL, NULL);
+
+               g_object_unref (bus);
+       } else {
+               /* Send locale change message */
+               strv[0] = g_strdup_printf ("LANG=%s", locale);
+               e_dbus_locale1_set_locale (fixture->locale1, (const gchar * const *)strv);
+               g_free (strv[0]);
+       }
+
+       handler_id = g_signal_connect (book_client, "notify::locale",
+                                      G_CALLBACK (book_client_locale_change), &data);
+       g_main_loop_run (base_fixture->loop);
+       g_signal_handler_disconnect (book_client, handler_id);
+}
+
+static gint
+find_contact_data (EbSdbSearchData *data,
+                  const gchar     *uid)
+{
+       return g_strcmp0 (data->uid, uid);
+}
+
+void
+assert_contacts_order_slist (GSList      *results,
+                            GSList      *uids)
+{
+       gint position = -1;
+       GSList *link, *l;
+
+       /* Assert that all passed UIDs are found in the
+        * results, and that those UIDs are in the
+        * specified order.
+        */
+       for (l = uids; l; l = l->next) {
+               const gchar *uid = l->data;
+               gint new_position;
+
+               link = g_slist_find_custom (results, uid, (GCompareFunc)find_contact_data);
+               if (!link)
+                       g_error ("Specified uid '%s' was not found in results", uid);
+
+               new_position = g_slist_position (results, link);
+               g_assert_cmpint (new_position, >, position);
+               position = new_position;
+       }
+
+}
+
+void
+assert_contacts_order (GSList      *results,
+                      const gchar *first_uid,
+                      ...)
+{
+       GSList *uids = NULL;
+       gchar *uid;
+       va_list args;
+
+       g_assert (first_uid);
+
+       uids = g_slist_append (uids, (gpointer)first_uid);
+
+       va_start (args, first_uid);
+       uid = va_arg (args, gchar*);
+       while (uid) {
+               uids = g_slist_append (uids, uid);
+               uid = va_arg (args, gchar*);
+       }
+       va_end (args);
+
+       assert_contacts_order_slist (results, uids);
+       g_slist_free (uids);
+}
+
+void
+print_results (GSList      *results)
+{
+       GSList *l;
+
+       if (g_getenv ("TEST_DEBUG") == NULL)
+               return;
+
+       g_print ("\nPRINTING RESULTS:\n");
+
+       for (l = results; l; l = l->next) {
+               EbSdbSearchData *data = l->data;
+
+               g_print ("\n%s\n", data->vcard);
+       }
+
+       g_print ("\nRESULT LIST_FINISHED\n");
+}
+
+/********************************************
+ *           Move By Test Helpers
+ ********************************************/
+#define DEBUG_FIXTURE        0
+
+static MoveByData *
+move_by_test_new_internal (const gchar *test_path,
+                          const gchar *locale,
+                          gsize        struct_size)
+{
+       MoveByData *data;
+
+       data = g_slice_alloc0 (struct_size);
+       data->parent.parent.type = E_TEST_SERVER_ADDRESS_BOOK;
+       data->parent.parent.customize = e_sqlitedb_cursor_fixture_setup_book;
+       data->parent.locale = g_strdup (locale);
+       data->parent.sort_type = E_BOOK_CURSOR_SORT_ASCENDING;
+       data->path = g_strdup (test_path);
+       data->struct_size = struct_size;
+
+       /* Keep the work dir for migration tests */
+       if (g_getenv ("MIGRATION_TEST_SOURCE_NAME") != NULL)
+           data->parent.parent.keep_work_directory = TRUE;
+
+       return data;
+}
+
+static void
+move_by_test_free (MoveByData *data)
+{
+       g_free (data->path);
+       g_free ((gchar *)data->parent.locale);
+       g_slice_free1 (data->struct_size, data);
+}
+
+MoveByData *
+move_by_test_new (const gchar *test_path,
+                 const gchar *locale)
+{
+       return move_by_test_new_internal (test_path, locale, sizeof (MoveByData));
+}
+
+MoveByData *
+move_by_test_new_full (const gchar         *test_path,
+                      const gchar         *locale,
+                      EBookCursorSortType  sort_type)
+{
+       MoveByData *data;
+
+       data = move_by_test_new_internal (test_path, locale, sizeof (MoveByData));
+       data->parent.sort_type = sort_type;
+
+       return data;
+}
+
+static void
+test_cursor_move_teardown (EbSdbCursorFixture *fixture,
+                          gconstpointer  user_data)
+{
+       MoveByData *data = (MoveByData *)user_data;
+
+       e_sqlitedb_cursor_fixture_teardown (fixture, user_data);
+
+       move_by_test_free (data);
+}
+
+static void
+assert_move_by (EbSdbCursorFixture *fixture,
+               MoveByData *data,
+               gint i,
+               GSList *results)
+{
+       GSList *uids = NULL;
+       gint j, expected = 0;
+
+       /* Count the number of really expected results */
+       for (j = 0; j < ABS (data->counts[i]); j++) {
+               gint index = data->expected[i][j];
+
+               if (index < 0)
+                       break;
+
+               expected++;
+       }
+
+       /* Assert the exact amount of requested results */
+       g_assert_cmpint (g_slist_length (results), ==, expected);
+
+#if DEBUG_FIXTURE
+       g_print ("%s: Constructing expected result list for a fetch of %d: ",
+                data->path, data->counts[i]);
+#endif
+       for (j = 0; j < ABS (data->counts[i]); j++) {
+               gint index = data->expected[i][j];
+               gchar *uid;
+
+               if (index < 0)
+                       break;
+
+               uid = (gchar *)e_contact_get_const (fixture->contacts[index], E_CONTACT_UID);
+               uids = g_slist_append (uids, uid);
+
+#if DEBUG_FIXTURE
+               g_print ("%s ", uid);
+#endif
+
+       }
+#if DEBUG_FIXTURE
+       g_print ("\n");
+#endif
+
+       assert_contacts_order_slist (results, uids);
+       g_slist_free (uids);
+}
+
+static void
+test_move_by (EbSdbCursorFixture *fixture,
+             gconstpointer  user_data)
+{
+       MoveByData *data = (MoveByData *)user_data;
+       GSList *results = NULL;
+       GError *error = NULL;
+       gint i;
+       gint expected_position = 0, position;
+       gint total;
+
+       total = data->filtered ? N_FILTERED_CONTACTS : N_SORTED_CONTACTS;
+
+       for (i = 0; i < MAX_MOVE_BY_COUNTS && data->counts[i] != 0; i++) {
+
+               /* From the 0 position, a negative move starts from the end */
+               if (expected_position == 0 && data->counts[i] < 0)
+                       expected_position = (total + 1) - ABS (data->counts[i]);
+               else
+                       expected_position += data->counts[i];
+
+               if (expected_position > total || expected_position < 1)
+                       expected_position = 0;
+
+               /* Try normal order */
+               if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                            fixture->cursor,
+                                                            EBSDB_CURSOR_ORIGIN_CURRENT,
+                                                            data->counts[i],
+                                                            &results, &error))
+                       g_error ("Error fetching cursor results: %s", error->message);
+
+               print_results (results);
+               assert_move_by (fixture, data, i, results);
+               g_slist_foreach (results, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+               g_slist_free (results);
+               results = NULL;
+
+               if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                              fixture->cursor, NULL, &position, &error))
+                       g_error ("Error calculating cursor: %s", error->message);
+               g_assert_cmpint (expected_position, ==, position);
+
+               /* Try repeat last query */
+               if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                            fixture->cursor,
+                                                            EBSDB_CURSOR_ORIGIN_PREVIOUS,
+                                                            data->counts[i],
+                                                            &results, &error))
+                       g_error ("Error fetching cursor results: %s", error->message);
+
+               print_results (results);
+               assert_move_by (fixture, data, i, results);
+               g_slist_foreach (results, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+               g_slist_free (results);
+               results = NULL;
+
+               if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                              fixture->cursor, NULL, &position, &error))
+                       g_error ("Error calculating cursor: %s", error->message);
+               g_assert_cmpint (expected_position, ==, position);
+       }
+
+       /* One more, test reset API, the first batch from the beginning */
+       if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor,
+                                                    EBSDB_CURSOR_ORIGIN_RESET,
+                                                    data->counts[0],
+                                                    &results, &error))
+               g_error ("Error fetching cursor results: %s", error->message);
+
+       print_results (results);
+       assert_move_by (fixture, data, 0, results);
+       g_slist_foreach (results, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+       g_slist_free (results);
+       results = NULL;
+
+       /* From the 0 position, a negative move starts from the end */
+       if (data->counts[0] < 0)
+               expected_position = (total + 1) - ABS (data->counts[0]);
+       else
+               expected_position = data->counts[0];
+
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, NULL, &position, &error))
+               g_error ("Error calculating cursor: %s", error->message);
+       g_assert_cmpint (expected_position, ==, position);
+}
+
+static void
+move_by_test_add_assertion_va_list (MoveByData *data,
+                                   gint        count,
+                                   va_list     args)
+{
+       gint i, j;
+       gint expected;
+
+       for (i = 0; i < MAX_MOVE_BY_COUNTS; i++) {
+
+               /* Find the next available test slot */
+               if (data->counts[i] == 0) {
+                       data->counts[i] = count;
+
+#if DEBUG_FIXTURE
+                       g_print ("Adding assertion to test %d: %s\n", i + 1, data->path);
+                       g_print ("  Test will move by %d and expect: ", count);
+#endif
+                       for (j = 0; j < ABS (count); j++) {
+                               expected = va_arg (args, gint);
+
+#if DEBUG_FIXTURE
+                               g_print ("%d ", expected);
+#endif
+                               data->expected[i][j] = expected - 1;
+                       }
+#if DEBUG_FIXTURE
+                       g_print ("\n");
+#endif
+
+                       break;
+               }
+       }
+
+       g_assert (i < MAX_MOVE_BY_COUNTS);
+}
+
+/* A positive of negative 'count' value
+ * followed by ABS (count) UID indexes.
+ *
+ * The indexes start at 1 so that they
+ * are easier to match up with the chart
+ * in data-test-utils.h
+ */
+void
+move_by_test_add_assertion (MoveByData *data,
+                           gint        count,
+                           ...)
+{
+
+       va_list args;
+
+       va_start (args, count);
+       move_by_test_add_assertion_va_list (data, count, args);
+       va_end (args);
+}
+
+void
+move_by_test_add (MoveByData  *data,
+                 gboolean     filtered)
+{
+       data->filtered = filtered;
+
+       g_test_add (data->path, EbSdbCursorFixture, data,
+                   filtered ?
+                   e_sqlitedb_cursor_fixture_filtered_setup :
+                   e_sqlitedb_cursor_fixture_setup,
+                   test_move_by,
+                   test_cursor_move_teardown);
+}
+
diff --git a/tests/libedata-book/data-test-utils.h b/tests/libedata-book/data-test-utils.h
new file mode 100644
index 0000000..10108a6
--- /dev/null
+++ b/tests/libedata-book/data-test-utils.h
@@ -0,0 +1,164 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2013, Openismus GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 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
+ * 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, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Authors: Tristan Van Berkom <tristanvb openismus com>
+ */
+
+#ifndef DATA_TEST_UTILS_H
+#define DATA_TEST_UTILS_H
+
+#include <libebook/libebook.h>
+#include <libedata-book/libedata-book.h>
+#include "e-test-server-utils.h"
+#include "e-dbus-localed.h"
+
+/* 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
+ * following legend.
+ *
+ * The Email column indicates whether the contact has a .com email address
+ * (in order to test filtered cursor results) and corresponds to the natural
+ * order in the 'N' column.
+ *
+ * +-----------------------------------------------------------------------------------------------+
+ * | N   | Email | Last Name   | en_US_POSIX    | en_US / de_DE  | fr_CA          | de_DE          |
+ * |     |       |             |                |                |                | (phonebook)    |
+ * +-----------------------------------------------------------------------------------------------+
+ * | 1   | Yes   | bad         |             11 |             11 |             11 |             11 |
+ * | 2   | Yes   | Bad         | Bad         2  | bad         1  | bad         1  | bad         1  |
+ * | 3   | Yes   | Bat         | Bäd         6  | Bad         2  | Bad         2  | Bad         2  |
+ * | 4   | No    | bat         | Bat         3  | bäd         5  | bäd         5  | bäd         5  |
+ * | 5   | Yes   | bäd         | Bät         8  | Bäd         6  | Bäd         6  | Bäd         6  |
+ * | 6   | No    | Bäd         | bad         1  | bat         4  | bat         4  | bät         7  |
+ * | 7   | No    | bät         | bäd         5  | Bat         3  | Bat         3  | Bät         8  |
+ * | 8   | Yes   | Bät         | bat         4  | bät         7  | bät         7  | bat         4  |
+ * | 9   | Yes   | côté        | bät         7  | Bät         8  | Bät         8  | Bat         3  |
+ * | 10  | Yes   | C           | black-bird  15 | black-bird  15 | black-bird  15 | black-bird  15 |
+ * | 11  | Yes   |             | black-birds 17 | black-birds 17 | black-birds 17 | black-birds 17 |
+ * | 12  | Yes   | coté        | blackbird   16 | blackbird   16 | blackbird   16 | blackbird   16 |
+ * | 13  | No    | côte        | blackbirds  18 | blackbirds  18 | blackbirds  18 | blackbirds  18 |
+ * | 14  | Yes   | cote        | C           10 | C           10 | C           10 | C           10 |
+ * | 15  | No    | black-bird  | cote        14 | cote        14 | cote        14 | cote        14 |
+ * | 16  | Yes   | blackbird   | coté        12 | coté        12 | côte        13 | coté        12 | 
+ * | 17  | Yes   | black-birds | côte        13 | côte        13 | coté        12 | côte        13 | 
+ * | 18  | Yes   | blackbirds  | côté        9  | côté        9  | côté        9  | côté        9  | 
+ * | 19  | No    | Muffler     | Muffler     19 | Muffler     19 | Muffler     19 | Müller      20 | 
+ * | 20  | No    | Müller      | Müller      20 | Müller      20 | Müller      20 | Muffler     19 |
+ * +-----------------------------------------------------------------------------------------------+
+ *
+ * See this ICU demo to check additional sort ordering by ICU in various locales:
+ *     http://demo.icu-project.org/icu-bin/locexp?_=en_US&d_=en&x=col
+ */
+
+#define SQLITEDB_FOLDER_ID   "folder_id"
+#define N_SORTED_CONTACTS    20
+#define MAX_MOVE_BY_COUNTS   5
+
+/* 13 contacts in the test data have an email address ending with ".com" */
+#define N_FILTERED_CONTACTS  13
+
+
+typedef struct {
+       ETestServerFixture parent_fixture;
+
+       EBookBackendSqliteDB *ebsdb;
+} ESqliteDBFixture;
+
+typedef struct {
+       ESqliteDBFixture parent_fixture;
+
+       EbSdbCursor     *cursor;
+       EContact        *contacts[N_SORTED_CONTACTS];
+       EBookQuery      *query;
+
+       EDBusLocale1    *locale1;
+       guint            own_id;
+} EbSdbCursorFixture;
+
+typedef struct {
+       ETestServerClosure  parent;
+
+       const gchar        *locale;
+       EBookCursorSortType sort_type;
+} EbSdbCursorClosure;
+
+typedef struct {
+       EbSdbCursorClosure parent;
+       gchar *path;
+
+       /* array of counts to move by, terminated with 0 or MAX_COUNTS */
+       gint counts[MAX_MOVE_BY_COUNTS];
+
+       /* For each move_by() command, an array of 'ABS (counts[i])' expected contacts */
+       gint expected[MAX_MOVE_BY_COUNTS][N_SORTED_CONTACTS];
+
+       /* Whether this is a filtered test */
+       gboolean filtered;
+
+       /* Private detail */
+       gsize struct_size;
+} MoveByData;
+
+void     e_sqlitedb_fixture_setup          (ESqliteDBFixture *fixture,
+                                           gconstpointer     user_data);
+void     e_sqlitedb_fixture_teardown       (ESqliteDBFixture *fixture,
+                                           gconstpointer     user_data);
+
+void     e_sqlitedb_cursor_fixture_setup_book (ESource            *scratch,
+                                              ETestServerClosure *closure);
+void     e_sqlitedb_cursor_fixture_setup    (EbSdbCursorFixture *fixture,
+                                            gconstpointer       user_data);
+void     e_sqlitedb_cursor_fixture_teardown (EbSdbCursorFixture *fixture,
+                                            gconstpointer       user_data);
+void     e_sqlitedb_cursor_fixture_set_locale (EbSdbCursorFixture *fixture,
+                                              const gchar        *locale);
+
+/* Filters contacts with E_CONTACT_EMAIL ending with '.com' */
+void     e_sqlitedb_cursor_fixture_filtered_setup (EbSdbCursorFixture *fixture,
+                                                  gconstpointer  user_data);
+
+
+gchar    *new_vcard_from_test_case         (const gchar *case_name);
+EContact *new_contact_from_test_case       (const gchar *case_name);
+
+gboolean add_contact_from_test_case_verify (EBookClient *book_client,
+                                           const gchar *case_name,
+                                           EContact   **contact);
+
+void     assert_contacts_order_slist       (GSList      *results,
+                                           GSList      *uids);
+void     assert_contacts_order             (GSList      *results,
+                                           const gchar *first_uid,
+                                           ...) G_GNUC_NULL_TERMINATED;
+
+void     print_results                     (GSList      *results);
+
+/*  MoveBy test helpers */
+void        move_by_test_add_assertion     (MoveByData  *data,
+                                           gint         count,
+                                           ...);
+MoveByData *move_by_test_new               (const gchar *test_path,
+                                           const gchar *locale);
+MoveByData *move_by_test_new_full          (const gchar   *test_path,
+                                           const gchar   *locale,
+                                           EBookCursorSortType sort_type);
+void        move_by_test_add               (MoveByData  *data,
+                                           gboolean     filtered);
+
+#endif /* DATA_TEST_UTILS_H */
diff --git a/tests/libedata-book/test-sqlite-create-cursor.c b/tests/libedata-book/test-sqlite-create-cursor.c
new file mode 100644
index 0000000..399ad09
--- /dev/null
+++ b/tests/libedata-book/test-sqlite-create-cursor.c
@@ -0,0 +1,131 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include <stdlib.h>
+#include <locale.h>
+#include <libebook/libebook.h>
+
+#include "data-test-utils.h"
+
+static ETestServerClosure book_closure = { E_TEST_SERVER_ADDRESS_BOOK, NULL, 0 };
+
+static void
+test_create_cursor_empty_query (ESqliteDBFixture *fixture,
+                               gconstpointer     user_data)
+{
+       EbSdbCursor  *cursor;
+       EContactField sort_fields[] = { E_CONTACT_FAMILY_NAME, E_CONTACT_GIVEN_NAME };
+       EBookCursorSortType sort_types[] = { E_BOOK_CURSOR_SORT_ASCENDING, E_BOOK_CURSOR_SORT_ASCENDING };
+       GError       *error = NULL;
+
+       cursor = e_book_backend_sqlitedb_cursor_new (fixture->ebsdb, SQLITEDB_FOLDER_ID, NULL,
+                                                    sort_fields, sort_types, 2, &error);
+
+       g_assert (cursor != NULL);
+       e_book_backend_sqlitedb_cursor_free (fixture->ebsdb, cursor);
+}
+
+static void
+test_create_cursor_valid_query (ESqliteDBFixture *fixture,
+                               gconstpointer     user_data)
+{
+       EbSdbCursor  *cursor;
+       EContactField sort_fields[] = { E_CONTACT_FAMILY_NAME, E_CONTACT_GIVEN_NAME };
+       EBookCursorSortType sort_types[] = { E_BOOK_CURSOR_SORT_ASCENDING, E_BOOK_CURSOR_SORT_ASCENDING };
+       EBookQuery   *query;
+       gchar        *sexp;
+       GError       *error = NULL;
+
+       query = e_book_query_field_test (E_CONTACT_FULL_NAME, E_BOOK_QUERY_IS, "James Brown");
+       sexp  = e_book_query_to_string (query);
+
+       cursor = e_book_backend_sqlitedb_cursor_new (fixture->ebsdb, SQLITEDB_FOLDER_ID, sexp,
+                                                    sort_fields, sort_types, 2, &error);
+
+       g_assert (cursor != NULL);
+       e_book_backend_sqlitedb_cursor_free (fixture->ebsdb, cursor);
+       g_free (sexp);
+       e_book_query_unref (query);
+}
+
+static void
+test_create_cursor_invalid_query (ESqliteDBFixture *fixture,
+                                 gconstpointer     user_data)
+{
+       EbSdbCursor  *cursor;
+       EContactField sort_fields[] = { E_CONTACT_FAMILY_NAME, E_CONTACT_GIVEN_NAME };
+       EBookCursorSortType sort_types[] = { E_BOOK_CURSOR_SORT_ASCENDING, E_BOOK_CURSOR_SORT_ASCENDING };
+       EBookQuery   *query;
+       gchar        *sexp;
+       GError       *error = NULL;
+
+       query = e_book_query_field_test (E_CONTACT_TEL, E_BOOK_QUERY_CONTAINS, "888");
+       sexp  = e_book_query_to_string (query);
+
+       cursor = e_book_backend_sqlitedb_cursor_new (fixture->ebsdb, SQLITEDB_FOLDER_ID, sexp,
+                                                    sort_fields, sort_types, 2, &error);
+
+       g_assert (cursor == NULL);
+       g_assert (error);
+       g_assert (g_error_matches (error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_INVALID_QUERY));
+
+       g_free (sexp);
+       e_book_query_unref (query);
+}
+
+static void
+test_create_cursor_invalid_sort (ESqliteDBFixture *fixture,
+                                gconstpointer     user_data)
+{
+       EbSdbCursor  *cursor;
+       EContactField sort_fields[] = { E_CONTACT_TEL };
+       EBookCursorSortType sort_types[] = { E_BOOK_CURSOR_SORT_ASCENDING };
+       GError       *error = NULL;
+
+       cursor = e_book_backend_sqlitedb_cursor_new (fixture->ebsdb, SQLITEDB_FOLDER_ID, NULL,
+                                                    sort_fields, sort_types, 1, &error);
+
+       g_assert (cursor == NULL);
+       g_assert (error);
+       g_assert (g_error_matches (error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_INVALID_QUERY));
+}
+
+static void
+test_create_cursor_missing_sort (ESqliteDBFixture *fixture,
+                                gconstpointer     user_data)
+{
+       EbSdbCursor  *cursor;
+       GError       *error = NULL;
+
+       cursor = e_book_backend_sqlitedb_cursor_new (fixture->ebsdb, SQLITEDB_FOLDER_ID, NULL, NULL, NULL, 0, 
&error);
+
+       g_assert (cursor == NULL);
+       g_assert (error);
+       g_assert (g_error_matches (error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_INVALID_QUERY));
+}
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+#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 ("/EbSdbCursor/Create/EmptyQuery", ESqliteDBFixture, &book_closure,
+                   e_sqlitedb_fixture_setup, test_create_cursor_empty_query, e_sqlitedb_fixture_teardown);
+       g_test_add ("/EbSdbCursor/Create/ValidQuery", ESqliteDBFixture, &book_closure,
+                   e_sqlitedb_fixture_setup, test_create_cursor_valid_query, e_sqlitedb_fixture_teardown);
+       g_test_add ("/EbSdbCursor/Create/InvalidQuery", ESqliteDBFixture, &book_closure,
+                   e_sqlitedb_fixture_setup, test_create_cursor_invalid_query, e_sqlitedb_fixture_teardown);
+       g_test_add ("/EbSdbCursor/Create/InvalidSort", ESqliteDBFixture, &book_closure,
+                   e_sqlitedb_fixture_setup, test_create_cursor_invalid_sort, e_sqlitedb_fixture_teardown);
+       g_test_add ("/EbSdbCursor/Create/MissingSort", ESqliteDBFixture, &book_closure,
+                   e_sqlitedb_fixture_setup, test_create_cursor_missing_sort, e_sqlitedb_fixture_teardown);
+
+       return e_test_server_utils_run ();
+}
diff --git a/tests/libedata-book/test-sqlite-cursor-calculate.c 
b/tests/libedata-book/test-sqlite-cursor-calculate.c
new file mode 100644
index 0000000..2d1621f
--- /dev/null
+++ b/tests/libedata-book/test-sqlite-cursor-calculate.c
@@ -0,0 +1,647 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include <stdlib.h>
+#include <locale.h>
+#include <libebook/libebook.h>
+
+#include "data-test-utils.h"
+
+static EbSdbCursorClosure ascending_closure = {
+       { E_TEST_SERVER_ADDRESS_BOOK, e_sqlitedb_cursor_fixture_setup_book, 0 },
+       NULL, 
+       E_BOOK_CURSOR_SORT_ASCENDING
+};
+
+static EbSdbCursorClosure descending_closure = {
+       { E_TEST_SERVER_ADDRESS_BOOK, e_sqlitedb_cursor_fixture_setup_book, 0 },
+       NULL, 
+       E_BOOK_CURSOR_SORT_DESCENDING
+};
+
+static void
+test_cursor_calculate_initial (EbSdbCursorFixture *fixture,
+                              gconstpointer  user_data)
+{
+       GError *error = NULL;
+       gint    position = 0, total = 0;
+
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+           g_error ("Error calculating cursor: %s", error->message);
+
+       g_assert_cmpint (position, ==, 0);
+       g_assert_cmpint (total, ==, 20);
+}
+
+static void
+test_cursor_calculate_move_forward (EbSdbCursorFixture *fixture,
+                                   gconstpointer  user_data)
+{
+       GSList *results = NULL;
+       GError *error = NULL;
+       gint    position = 0, total = 0;
+
+       /* Move cursor */
+       if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor,
+                                                    EBSDB_CURSOR_ORIGIN_CURRENT,
+                                                    5,
+                                                    &results, &error))
+               g_error ("Error fetching cursor results: %s", error->message);
+
+       /* Assert the first 5 contacts in en_US order */
+       g_assert_cmpint (g_slist_length (results), ==, 5);
+       assert_contacts_order (results,
+                              "sorted-11",
+                              "sorted-1",
+                              "sorted-2",
+                              "sorted-5",
+                              "sorted-6",
+                              NULL);
+       g_slist_foreach (results, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+       g_slist_free (results);
+
+       /* Check new position */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+           g_error ("Error calculating cursor: %s", error->message);
+
+       /* results 0 + 5 = position 5, result index 4 (results[0, 1, 2, 3, 4]) */
+       g_assert_cmpint (position, ==, 5);
+       g_assert_cmpint (total, ==, 20);
+}
+
+static void
+test_cursor_calculate_move_backwards (EbSdbCursorFixture *fixture,
+                                     gconstpointer  user_data)
+{
+       GSList *results = NULL;
+       GError *error = NULL;
+       gint    position = 0, total = 0;
+
+       /* Move cursor */
+       if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor,
+                                                    EBSDB_CURSOR_ORIGIN_CURRENT,
+                                                    -5,
+                                                    &results, &error))
+               g_error ("Error fetching cursor results: %s", error->message);
+
+       /* Assert the last 5 contacts in en_US order */
+       g_assert_cmpint (g_slist_length (results), ==, 5);
+       assert_contacts_order (results,
+                              "sorted-20",
+                              "sorted-19",
+                              "sorted-9",
+                              "sorted-13",
+                              "sorted-12",
+                              NULL);
+       g_slist_foreach (results, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+       g_slist_free (results);
+
+       /* Check new position */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+           g_error ("Error calculating cursor: %s", error->message);
+
+       /* results 20 - 5 = position 16 result index 15 (results[20, 19, 18, 17, 16]) */
+       g_assert_cmpint (position, ==, 16);
+       g_assert_cmpint (total, ==, 20);
+}
+
+static void
+test_cursor_calculate_back_and_forth (EbSdbCursorFixture *fixture,
+                                     gconstpointer  user_data)
+{
+       GSList *results = NULL;
+       GError *error = NULL;
+       gint    position = 0, total = 0;
+
+       /* Move cursor */
+       if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor, 
+                                                    EBSDB_CURSOR_ORIGIN_CURRENT,
+                                                    7,
+                                                    &results, &error))
+               g_error ("Error fetching cursor results: %s", error->message);
+
+       g_assert_cmpint (g_slist_length (results), ==, 7);
+       g_slist_foreach (results, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+       g_slist_free (results);
+       results = NULL;
+
+       /* Check new position */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+           g_error ("Error calculating cursor: %s", error->message);
+
+       /* results 0 + 7 = position 7 result index 6 (results[0, 1, 2, 3, 4, 5, 6]) */
+       g_assert_cmpint (position, ==, 7);
+       g_assert_cmpint (total, ==, 20);
+
+       /* Move cursor */
+       if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor,
+                                                    EBSDB_CURSOR_ORIGIN_CURRENT,
+                                                    -4,
+                                                    &results, &error))
+               g_error ("Error fetching cursor results: %s", error->message);
+
+       g_assert_cmpint (g_slist_length (results), ==, 4);
+       g_slist_foreach (results, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+       g_slist_free (results);
+       results = NULL;
+
+       /* Check new position */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+           g_error ("Error calculating cursor: %s", error->message);
+
+       /* results 7 - 4 = position 3 result index 2 (results[5, 4, 3, 2]) */
+       g_assert_cmpint (position, ==, 3);
+       g_assert_cmpint (total, ==, 20);
+
+       /* Move cursor */
+       if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor,
+                                                    EBSDB_CURSOR_ORIGIN_CURRENT,
+                                                    5,
+                                                    &results, &error))
+               g_error ("Error fetching cursor results: %s", error->message);
+
+       g_assert_cmpint (g_slist_length (results), ==, 5);
+       g_slist_foreach (results, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+       g_slist_free (results);
+       results = NULL;
+
+       /* Check new position */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+           g_error ("Error calculating cursor: %s", error->message);
+
+       /* results 3 + 5 = position 8 result index 7 (results[3, 4, 5, 6, 7]) */
+       g_assert_cmpint (position, ==, 8);
+       g_assert_cmpint (total, ==, 20);
+}
+
+static void
+test_cursor_calculate_partial_target (EbSdbCursorFixture *fixture,
+                                     gconstpointer  user_data)
+{
+       GError *error = NULL;
+       gint    position = 0, total = 0;
+       ECollator *collator;
+       gint n_labels;
+       const gchar *const *labels;
+
+       /* First verify our test... in en_US locale the label 'C' should exist with the index 3 */
+       collator = e_book_backend_sqlitedb_ref_collator (((ESqliteDBFixture *) fixture)->ebsdb);
+       labels = e_collator_get_index_labels (collator, &n_labels, NULL, NULL, NULL);
+       g_assert_cmpstr (labels[3], ==, "C");
+       e_collator_unref (collator);
+
+       /* Set the cursor at the start of family names beginning with 'C' */
+       e_book_backend_sqlitedb_cursor_set_target_alphabetic_index (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                                   fixture->cursor, 3);
+
+       /* Check new position */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+               g_error ("Error calculating cursor: %s", error->message);
+
+       /* Position is 13, there are 13 contacts before the letter 'C' in en_US locale */
+       g_assert_cmpint (position, ==, 13);
+       g_assert_cmpint (total, ==, 20);
+}
+
+static void
+test_cursor_calculate_after_modification (EbSdbCursorFixture *fixture,
+                                         gconstpointer  user_data)
+{
+       EBookClient  *book_client;
+       GError *error = NULL;
+       gint    position = 0, total = 0;
+
+       book_client = E_TEST_SERVER_UTILS_SERVICE (fixture, EBookClient);
+
+       /* Set the cursor to point exactly 'blackbird' (which is the 12th contact) */
+       if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor,
+                                                    EBSDB_CURSOR_ORIGIN_CURRENT,
+                                                    12, NULL, &error))
+               g_error ("Error fetching cursor results: %s", error->message);
+
+       /* Check new position */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+               g_error ("Error calculating cursor: %s", error->message);
+
+       /* blackbird is at position 12 in en_US locale */
+       g_assert_cmpint (position, ==, 12);
+       g_assert_cmpint (total, ==, 20);
+
+       /* Rename Muffler -> Jacob Appelbaum */
+       e_contact_set (fixture->contacts[19 - 1], E_CONTACT_FAMILY_NAME, "Appelbaum");
+       e_contact_set (fixture->contacts[19 - 1], E_CONTACT_GIVEN_NAME, "Jacob");
+       if (!e_book_client_modify_contact_sync (book_client, fixture->contacts[19 - 1], NULL, &error))
+               g_error ("modify contact sync: %s", error->message);
+
+       /* Rename Müller -> Sade Adu */
+       e_contact_set (fixture->contacts[20 - 1], E_CONTACT_FAMILY_NAME, "Adu");
+       e_contact_set (fixture->contacts[20 - 1], E_CONTACT_GIVEN_NAME, "Sade");
+       if (!e_book_client_modify_contact_sync (book_client, fixture->contacts[20 - 1], NULL, &error))
+               g_error ("modify contact sync: %s", error->message);
+
+       /* Check new position */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+               g_error ("Error calculating cursor: %s", error->message);
+
+       /* blackbird is now at position 14 after moving 2 later contacts to begin with 'A' */
+       g_assert_cmpint (position, ==, 14);
+       g_assert_cmpint (total, ==, 20);
+}
+
+static void
+test_cursor_calculate_filtered_initial (EbSdbCursorFixture *fixture,
+                                       gconstpointer  user_data)
+{
+       GError *error = NULL;
+       gint    position = 0, total = 0;
+
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+           g_error ("Error calculating cursor: %s", error->message);
+
+       g_assert_cmpint (position, ==, 0);
+       g_assert_cmpint (total, ==, 13);
+}
+
+static void
+test_cursor_calculate_filtered_move_forward (EbSdbCursorFixture *fixture,
+                                            gconstpointer  user_data)
+{
+       GSList *results = NULL;
+       GError *error = NULL;
+       gint    position = 0, total = 0;
+
+       /* Move cursor */
+       if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor,
+                                                    EBSDB_CURSOR_ORIGIN_CURRENT,
+                                                    5, &results, &error))
+               g_error ("Error fetching cursor results: %s", error->message);
+
+       g_assert_cmpint (g_slist_length (results), ==, 5);
+       g_slist_foreach (results, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+       g_slist_free (results);
+       results = NULL;
+
+       /* Check new position */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+           g_error ("Error calculating cursor: %s", error->message);
+
+       /* results 0 + 5 = position 5, result index 4 (results[0, 1, 2, 3, 4]) */
+       g_assert_cmpint (position, ==, 5);
+       g_assert_cmpint (total, ==, 13);
+}
+
+static void
+test_cursor_calculate_filtered_move_backwards (EbSdbCursorFixture *fixture,
+                                              gconstpointer  user_data)
+{
+       GSList *results = NULL;
+       GError *error = NULL;
+       gint    position = 0, total = 0;
+
+       /* Move cursor */
+       if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor,
+                                                    EBSDB_CURSOR_ORIGIN_CURRENT,
+                                                    -5,
+                                                    &results, &error))
+               g_error ("Error fetching cursor results: %s", error->message);
+
+       g_assert_cmpint (g_slist_length (results), ==, 5);
+       g_slist_foreach (results, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+       g_slist_free (results);
+       results = NULL;
+
+       /* Check new position */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+           g_error ("Error calculating cursor: %s", error->message);
+
+       /* results 13 - 5 = position 9 (results[13, 12, 11, 10, 9]) */
+       g_assert_cmpint (position, ==, 9);
+       g_assert_cmpint (total, ==, 13);
+}
+
+static void
+test_cursor_calculate_filtered_partial_target (EbSdbCursorFixture *fixture,
+                                              gconstpointer  user_data)
+{
+       GError *error = NULL;
+       gint    position = 0, total = 0;
+       ECollator *collator;
+       gint n_labels;
+       const gchar *const *labels;
+
+       /* First verify our test... in en_US locale the label 'C' should exist with the index 3 */
+       collator = e_book_backend_sqlitedb_ref_collator (((ESqliteDBFixture *) fixture)->ebsdb);
+       labels = e_collator_get_index_labels (collator, &n_labels, NULL, NULL, NULL);
+       g_assert_cmpstr (labels[3], ==, "C");
+       e_collator_unref (collator);
+
+       /* Set the cursor at the start of family names beginning with 'C' */
+       e_book_backend_sqlitedb_cursor_set_target_alphabetic_index (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                                   fixture->cursor, 3);
+
+       /* Check new position */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+               g_error ("Error calculating cursor: %s", error->message);
+
+       /* There are 9 contacts before the letter 'C' in the en_US locale */
+       g_assert_cmpint (position, ==, 9);
+       g_assert_cmpint (total, ==, 13);
+}
+
+static void
+test_cursor_calculate_filtered_after_modification (EbSdbCursorFixture *fixture,
+                                                  gconstpointer  user_data)
+{
+       EBookClient  *book_client;
+       GError *error = NULL;
+       gint    position = 0, total = 0;
+
+       book_client = E_TEST_SERVER_UTILS_SERVICE (fixture, EBookClient);
+
+       /* Set the cursor to point exactly 'blackbird' (which is the 8th contact when filtered) */
+       if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor,
+                                                    EBSDB_CURSOR_ORIGIN_CURRENT,
+                                                    8, NULL, &error))
+               g_error ("Error fetching cursor results: %s", error->message);
+
+       /* 'blackbirds' -> Jacob Appelbaum */
+       e_contact_set (fixture->contacts[18 - 1], E_CONTACT_FAMILY_NAME, "Appelbaum");
+       e_contact_set (fixture->contacts[18 - 1], E_CONTACT_GIVEN_NAME, "Jacob");
+       if (!e_book_client_modify_contact_sync (book_client, fixture->contacts[18 - 1], NULL, &error))
+               g_error ("modify contact sync: %s", error->message);
+
+       /* 'black-birds' -> Sade Adu */
+       e_contact_set (fixture->contacts[17 - 1], E_CONTACT_FAMILY_NAME, "Adu");
+       e_contact_set (fixture->contacts[17 - 1], E_CONTACT_GIVEN_NAME, "Sade");
+       if (!e_book_client_modify_contact_sync (book_client, fixture->contacts[17 - 1], NULL, &error))
+               g_error ("modify contact sync: %s", error->message);
+
+       /* Check new position */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+               g_error ("Error calculating cursor: %s", error->message);
+
+       /* blackbird is now at position 11 after moving 2 later contacts to begin with 'A' */
+       g_assert_cmpint (position, ==, 9);
+       g_assert_cmpint (total, ==, 13);
+}
+
+static void
+test_cursor_calculate_descending_move_forward (EbSdbCursorFixture *fixture,
+                                              gconstpointer  user_data)
+{
+       GSList *results = NULL;
+       GError *error = NULL;
+       gint    position = 0, total = 0;
+
+       /* Move cursor */
+       if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor,
+                                                    EBSDB_CURSOR_ORIGIN_CURRENT,
+                                                    5,
+                                                    &results, &error))
+               g_error ("Error fetching cursor results: %s", error->message);
+
+       /* Assert the first 5 contacts in en_US order */
+       g_assert_cmpint (g_slist_length (results), ==, 5);
+       assert_contacts_order (results,
+                              "sorted-20",
+                              "sorted-19",
+                              "sorted-9",
+                              "sorted-13",
+                              "sorted-12",
+                              NULL);
+       g_slist_foreach (results, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+       g_slist_free (results);
+       results = NULL;
+
+       /* Check new position */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+           g_error ("Error calculating cursor: %s", error->message);
+
+       /* results 0 + 5 = position 5, result index 4 (results[0, 1, 2, 3, 4]) */
+       g_assert_cmpint (position, ==, 5);
+       g_assert_cmpint (total, ==, 20);
+}
+
+static void
+test_cursor_calculate_descending_move_backwards (EbSdbCursorFixture *fixture,
+                                                gconstpointer  user_data)
+{
+       GSList *results = NULL;
+       GError *error = NULL;
+       gint    position = 0, total = 0;
+
+       /* Move cursor */
+       if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor,
+                                                    EBSDB_CURSOR_ORIGIN_CURRENT,
+                                                    -5, &results, &error))
+               g_error ("Error fetching cursor results: %s", error->message);
+
+       /* Assert the last 5 contacts in en_US order */
+       g_assert_cmpint (g_slist_length (results), ==, 5);
+       assert_contacts_order (results,
+                              "sorted-11",
+                              "sorted-1",
+                              "sorted-2",
+                              "sorted-5",
+                              "sorted-6",
+                              NULL);
+       g_slist_foreach (results, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+       g_slist_free (results);
+       results = NULL;
+
+       /* Check new position */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+           g_error ("Error calculating cursor: %s", error->message);
+
+       /* results 20 - 5 = position 16 result index 15 (results[20, 19, 18, 17, 16]) */
+       g_assert_cmpint (position, ==, 16);
+       g_assert_cmpint (total, ==, 20);
+}
+
+static void
+test_cursor_calculate_descending_partial_target (EbSdbCursorFixture *fixture,
+                                                gconstpointer  user_data)
+{
+       GError *error = NULL;
+       gint    position = 0, total = 0;
+       ECollator *collator;
+       gint n_labels;
+       const gchar *const *labels;
+
+       /* First verify our test... in en_US locale the label 'C' should exist with the index 3 */
+       collator = e_book_backend_sqlitedb_ref_collator (((ESqliteDBFixture *) fixture)->ebsdb);
+       labels = e_collator_get_index_labels (collator, &n_labels, NULL, NULL, NULL);
+       g_assert_cmpstr (labels[3], ==, "C");
+       e_collator_unref (collator);
+
+       /* Set the cursor at the start of family names beginning with 'C' */
+       e_book_backend_sqlitedb_cursor_set_target_alphabetic_index (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                                   fixture->cursor, 3);
+
+       /* Check new position */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+               g_error ("Error calculating cursor: %s", error->message);
+
+       /* Position is 7, there are 7 contacts leading up to the last 'C' in en_US locale
+        * (when sorting in descending order) */
+       g_assert_cmpint (position, ==, 7);
+       g_assert_cmpint (total, ==, 20);
+}
+
+static void
+test_cursor_calculate_descending_after_modification (EbSdbCursorFixture *fixture,
+                                                    gconstpointer  user_data)
+{
+       EBookClient  *book_client;
+       GError *error = NULL;
+       gint    position = 0, total = 0;
+
+       book_client = E_TEST_SERVER_UTILS_SERVICE (fixture, EBookClient);
+
+       /* Set the cursor to point exactly 'Bät' (which is the 12th contact in descending order) */
+       if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor,
+                                                    EBSDB_CURSOR_ORIGIN_CURRENT,
+                                                    12, NULL, &error))
+               g_error ("Error fetching cursor results: %s", error->message);
+
+       /* Check new position */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+               g_error ("Error calculating cursor: %s", error->message);
+
+       /* 'Bät' is at position 12 in en_US locale (descending order) */
+       g_assert_cmpint (position, ==, 12);
+       g_assert_cmpint (total, ==, 20);
+
+       /* Rename Muffler -> Jacob Appelbaum */
+       e_contact_set (fixture->contacts[19 - 1], E_CONTACT_FAMILY_NAME, "Appelbaum");
+       e_contact_set (fixture->contacts[19 - 1], E_CONTACT_GIVEN_NAME, "Jacob");
+       if (!e_book_client_modify_contact_sync (book_client, fixture->contacts[19 - 1], NULL, &error))
+               g_error ("modify contact sync: %s", error->message);
+
+       /* Rename Müller -> Sade Adu */
+       e_contact_set (fixture->contacts[20 - 1], E_CONTACT_FAMILY_NAME, "Adu");
+       e_contact_set (fixture->contacts[20 - 1], E_CONTACT_GIVEN_NAME, "Sade");
+       if (!e_book_client_modify_contact_sync (book_client, fixture->contacts[20 - 1], NULL, &error))
+               g_error ("modify contact sync: %s", error->message);
+
+       /* Check new position */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+               g_error ("Error calculating cursor: %s", error->message);
+
+       /* 'Bät' is now at position 10 in descending order after moving 2 contacts to begin with 'A' */
+       g_assert_cmpint (position, ==, 10);
+       g_assert_cmpint (total, ==, 20);
+}
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+#if !GLIB_CHECK_VERSION (2, 35, 1)
+       g_type_init ();
+#endif
+       g_test_init (&argc, &argv, NULL);
+
+       g_test_add ("/EbSdbCursor/Calculate/Initial", EbSdbCursorFixture, &ascending_closure,
+                   e_sqlitedb_cursor_fixture_setup,
+                   test_cursor_calculate_initial,
+                   e_sqlitedb_cursor_fixture_teardown);
+       g_test_add ("/EbSdbCursor/Calculate/MoveForward", EbSdbCursorFixture, &ascending_closure,
+                   e_sqlitedb_cursor_fixture_setup,
+                   test_cursor_calculate_move_forward,
+                   e_sqlitedb_cursor_fixture_teardown);
+       g_test_add ("/EbSdbCursor/Calculate/MoveBackwards", EbSdbCursorFixture, &ascending_closure,
+                   e_sqlitedb_cursor_fixture_setup,
+                   test_cursor_calculate_move_backwards,
+                   e_sqlitedb_cursor_fixture_teardown);
+       g_test_add ("/EbSdbCursor/Calculate/BackAndForth", EbSdbCursorFixture, &ascending_closure,
+                   e_sqlitedb_cursor_fixture_setup,
+                   test_cursor_calculate_back_and_forth,
+                   e_sqlitedb_cursor_fixture_teardown);
+       g_test_add ("/EbSdbCursor/Calculate/AlphabeticTarget", EbSdbCursorFixture, &ascending_closure,
+                   e_sqlitedb_cursor_fixture_setup,
+                   test_cursor_calculate_partial_target,
+                   e_sqlitedb_cursor_fixture_teardown);
+       g_test_add ("/EbSdbCursor/Calculate/AfterModification", EbSdbCursorFixture, &ascending_closure,
+                   e_sqlitedb_cursor_fixture_setup,
+                   test_cursor_calculate_after_modification,
+                   e_sqlitedb_cursor_fixture_teardown);
+
+       g_test_add ("/EbSdbCursor/Calculate/Filtered/Initial", EbSdbCursorFixture, &ascending_closure,
+                   e_sqlitedb_cursor_fixture_filtered_setup,
+                   test_cursor_calculate_filtered_initial,
+                   e_sqlitedb_cursor_fixture_teardown);
+       g_test_add ("/EbSdbCursor/Calculate/Filtered/MoveForward", EbSdbCursorFixture, &ascending_closure,
+                   e_sqlitedb_cursor_fixture_filtered_setup,
+                   test_cursor_calculate_filtered_move_forward,
+                   e_sqlitedb_cursor_fixture_teardown);
+       g_test_add ("/EbSdbCursor/Calculate/Filtered/MoveBackwards", EbSdbCursorFixture, &ascending_closure,
+                   e_sqlitedb_cursor_fixture_filtered_setup,
+                   test_cursor_calculate_filtered_move_backwards,
+                   e_sqlitedb_cursor_fixture_teardown);
+       g_test_add ("/EbSdbCursor/Calculate/Filtered/AlphabeticTarget", EbSdbCursorFixture, 
&ascending_closure,
+                   e_sqlitedb_cursor_fixture_filtered_setup,
+                   test_cursor_calculate_filtered_partial_target,
+                   e_sqlitedb_cursor_fixture_teardown);
+       g_test_add ("/EbSdbCursor/Calculate/Filtered/AfterModification", EbSdbCursorFixture, 
&ascending_closure,
+                   e_sqlitedb_cursor_fixture_filtered_setup,
+                   test_cursor_calculate_filtered_after_modification,
+                   e_sqlitedb_cursor_fixture_teardown);
+
+       g_test_add ("/EbSdbCursor/Calculate/Descending/Initial", EbSdbCursorFixture, &descending_closure,
+                   e_sqlitedb_cursor_fixture_setup,
+                   test_cursor_calculate_initial,
+                   e_sqlitedb_cursor_fixture_teardown);
+       g_test_add ("/EbSdbCursor/Calculate/Descending/MoveForward", EbSdbCursorFixture, &descending_closure,
+                   e_sqlitedb_cursor_fixture_setup,
+                   test_cursor_calculate_descending_move_forward,
+                   e_sqlitedb_cursor_fixture_teardown);
+       g_test_add ("/EbSdbCursor/Calculate/Descending/MoveBackwards", EbSdbCursorFixture, 
&descending_closure,
+                   e_sqlitedb_cursor_fixture_setup,
+                   test_cursor_calculate_descending_move_backwards,
+                   e_sqlitedb_cursor_fixture_teardown);
+       g_test_add ("/EbSdbCursor/Calculate/Descending/BackAndForth", EbSdbCursorFixture, &descending_closure,
+                   e_sqlitedb_cursor_fixture_setup,
+                   test_cursor_calculate_back_and_forth,
+                   e_sqlitedb_cursor_fixture_teardown);
+       g_test_add ("/EbSdbCursor/Calculate/Descending/AlphabeticTarget", EbSdbCursorFixture, 
&descending_closure,
+                   e_sqlitedb_cursor_fixture_setup,
+                   test_cursor_calculate_descending_partial_target,
+                   e_sqlitedb_cursor_fixture_teardown);
+       g_test_add ("/EbSdbCursor/Calculate/Descending/AfterModification", EbSdbCursorFixture, 
&descending_closure,
+                   e_sqlitedb_cursor_fixture_setup,
+                   test_cursor_calculate_descending_after_modification,
+                   e_sqlitedb_cursor_fixture_teardown);
+
+       return e_test_server_utils_run ();
+}
diff --git a/tests/libedata-book/test-sqlite-cursor-change-locale.c 
b/tests/libedata-book/test-sqlite-cursor-change-locale.c
new file mode 100644
index 0000000..fd322ab
--- /dev/null
+++ b/tests/libedata-book/test-sqlite-cursor-change-locale.c
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include <stdlib.h>
+#include <locale.h>
+#include <libebook/libebook.h>
+
+#include "data-test-utils.h"
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+       MoveByData *data;
+
+#if !GLIB_CHECK_VERSION (2, 35, 1)
+       g_type_init ();
+#endif
+       g_test_init (&argc, &argv, NULL);
+
+       g_assert (g_setenv ("MIGRATION_TEST_SOURCE_NAME", "migration-test-source", TRUE));
+
+       data = move_by_test_new ("/EbSdbCursor/ChangeLocale/POSIX", "POSIX");
+       move_by_test_add_assertion (data, 5, 11, 2,  6,  3,  8);
+       move_by_test_add_assertion (data, 5, 1,  5,  4,  7,  15);
+       move_by_test_add_assertion (data, 5, 17, 16, 18, 10, 14);
+       move_by_test_add_assertion (data, 5, 12, 13, 9,  19, 20);
+       move_by_test_add (data, FALSE);
+
+       data = move_by_test_new ("/EbSdbCursor/ChangeLocale/en_US", "en_US.UTF-8");
+       move_by_test_add_assertion (data, 5, 11, 1,  2,  5,  6);
+       move_by_test_add_assertion (data, 5, 4,  3,  7,  8,  15);
+       move_by_test_add_assertion (data, 5, 17, 16, 18, 10, 14);
+       move_by_test_add_assertion (data, 5, 12, 13, 9,  19, 20);
+       move_by_test_add (data, FALSE);
+
+       data = move_by_test_new ("/EbSdbCursor/ChangeLocale/fr_CA", "fr_CA.UTF-8");
+       move_by_test_add_assertion (data, 5, 11, 1,  2,  5,  6);
+       move_by_test_add_assertion (data, 5, 4,  3,  7,  8,  15);
+       move_by_test_add_assertion (data, 5, 17, 16, 18, 10, 14);
+       move_by_test_add_assertion (data, 5, 13, 12, 9,  19, 20);
+       move_by_test_add (data, FALSE);
+
+       data = move_by_test_new ("/EbSdbCursor/ChangeLocale/de_DE", "de_DE.UTF-8");
+       move_by_test_add_assertion (data, 5, 11, 1,  2,  5,  6);
+       move_by_test_add_assertion (data, 5, 7,  8,  4,  3,  15);
+       move_by_test_add_assertion (data, 5, 17, 16, 18, 10, 14);
+       move_by_test_add_assertion (data, 5, 12, 13, 9,  20, 19);
+       move_by_test_add (data, FALSE);
+
+       /* On this case, we want to delete the work directory and start afresh */
+       return e_test_server_utils_run ();
+}
diff --git a/tests/libedata-book/test-sqlite-cursor-de-DE-migrated.c 
b/tests/libedata-book/test-sqlite-cursor-de-DE-migrated.c
new file mode 100644
index 0000000..9944ddc
--- /dev/null
+++ b/tests/libedata-book/test-sqlite-cursor-de-DE-migrated.c
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include <stdlib.h>
+#include <locale.h>
+#include <libebook/libebook.h>
+
+#include "data-test-utils.h"
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+       MoveByData *data;
+
+#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 ("MIGRATION_TEST_SOURCE_NAME", "migration-test-source", TRUE));
+
+       data = move_by_test_new ("/EbSdbCursor/Locale/de_DE/Migrated", "de_DE.UTF-8");
+       move_by_test_add_assertion (data, 5, 11, 1,  2,  5,  6);
+       move_by_test_add_assertion (data, 5, 7,  8,  4,  3,  15);
+       move_by_test_add_assertion (data, 5, 17, 16, 18, 10, 14);
+       move_by_test_add_assertion (data, 5, 12, 13, 9,  20, 19);
+       move_by_test_add (data, FALSE);
+
+       /* On this case, we are using the migrated addressbook, don't delete it first */
+       return e_test_server_utils_run_full (E_TEST_SERVER_KEEP_WORK_DIRECTORY);
+}
diff --git a/tests/libedata-book/test-sqlite-cursor-en-US-migrated.c 
b/tests/libedata-book/test-sqlite-cursor-en-US-migrated.c
new file mode 100644
index 0000000..7e2f969
--- /dev/null
+++ b/tests/libedata-book/test-sqlite-cursor-en-US-migrated.c
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include <stdlib.h>
+#include <locale.h>
+#include <libebook/libebook.h>
+
+#include "data-test-utils.h"
+
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+       MoveByData *data;
+
+#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 ("MIGRATION_TEST_SOURCE_NAME", "migration-test-source", TRUE));
+
+       data = move_by_test_new ("/EbSdbCursor/Locale/en_US/Migrated", "en_US.UTF-8");
+       move_by_test_add_assertion (data, 5, 11, 1,  2,  5,  6);
+       move_by_test_add_assertion (data, 5, 4,  3,  7,  8,  15);
+       move_by_test_add_assertion (data, 5, 17, 16, 18, 10, 14);
+       move_by_test_add_assertion (data, 5, 12, 13, 9,  19, 20);
+       move_by_test_add (data, FALSE);
+
+       /* On this case, we are using the migrated addressbook, don't delete it first */
+       return e_test_server_utils_run_full (E_TEST_SERVER_KEEP_WORK_DIRECTORY);
+}
diff --git a/tests/libedata-book/test-sqlite-cursor-fr-CA-migrated.c 
b/tests/libedata-book/test-sqlite-cursor-fr-CA-migrated.c
new file mode 100644
index 0000000..bc1cc9b
--- /dev/null
+++ b/tests/libedata-book/test-sqlite-cursor-fr-CA-migrated.c
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include <stdlib.h>
+#include <locale.h>
+#include <libebook/libebook.h>
+
+#include "data-test-utils.h"
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+       MoveByData *data;
+
+#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 ("MIGRATION_TEST_SOURCE_NAME", "migration-test-source", TRUE));
+
+       data = move_by_test_new ("/EbSdbCursor/Locale/fr_CA/Migrated", "fr_CA.UTF-8");
+       move_by_test_add_assertion (data, 5, 11, 1,  2,  5,  6);
+       move_by_test_add_assertion (data, 5, 4,  3,  7,  8,  15);
+       move_by_test_add_assertion (data, 5, 17, 16, 18, 10, 14);
+       move_by_test_add_assertion (data, 5, 13, 12, 9,  19, 20);
+       move_by_test_add (data, FALSE);
+
+       /* On this case, we are using the migrated addressbook, don't delete it first */
+       return e_test_server_utils_run_full (E_TEST_SERVER_KEEP_WORK_DIRECTORY);
+}
diff --git a/tests/libedata-book/test-sqlite-cursor-move-by-de-DE.c 
b/tests/libedata-book/test-sqlite-cursor-move-by-de-DE.c
new file mode 100644
index 0000000..d3fac4e
--- /dev/null
+++ b/tests/libedata-book/test-sqlite-cursor-move-by-de-DE.c
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include <stdlib.h>
+#include <locale.h>
+#include <libebook/libebook.h>
+
+#include "data-test-utils.h"
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+       MoveByData *data;
+
+#if !GLIB_CHECK_VERSION (2, 35, 1)
+       g_type_init ();
+#endif
+       g_test_init (&argc, &argv, NULL);
+
+       data = move_by_test_new ("/EbSdbCursor/de_DE/Move/Forward", "de_DE.UTF-8");
+       move_by_test_add_assertion (data, 5, 11, 1, 2, 5, 6);
+       move_by_test_add_assertion (data, 6, 7, 8, 4, 3, 15, 17);
+       move_by_test_add (data, FALSE);
+
+       data = move_by_test_new ("/EbSdbCursor/de_DE/Move/ForwardOnNameless", "de_DE.UTF-8");
+       move_by_test_add_assertion (data, 1, 11);
+       move_by_test_add_assertion (data, 3, 1, 2, 5);
+       move_by_test_add (data, FALSE);
+
+       data = move_by_test_new ("/EbSdbCursor/de_DE/Move/Backwards", "de_DE.UTF-8");
+       move_by_test_add_assertion (data, -5, 19, 20, 9, 13, 12);
+       move_by_test_add_assertion (data, -8, 14, 10, 18, 16, 17, 15, 3, 4);
+       move_by_test_add (data, FALSE);
+
+       data = move_by_test_new ("/EbSdbCursor/de_DE/Filtered/Move/Forward", "de_DE.UTF-8");
+       move_by_test_add_assertion (data, 5, 11, 1, 2, 5, 8);
+       move_by_test_add_assertion (data, 8, 3, 17, 16, 18, 10, 14, 12, 9);
+       move_by_test_add (data, TRUE);
+
+       data = move_by_test_new ("/EbSdbCursor/de_DE/Filtered/Move/Backwards", "de_DE.UTF-8");
+       move_by_test_add_assertion (data, -5, 9, 12, 14, 10, 18);
+       move_by_test_add_assertion (data, -8, 16, 17, 3, 8, 5, 2, 1, 11);
+       move_by_test_add (data, TRUE);
+
+       return e_test_server_utils_run ();
+}
diff --git a/tests/libedata-book/test-sqlite-cursor-move-by-en-US.c 
b/tests/libedata-book/test-sqlite-cursor-move-by-en-US.c
new file mode 100644
index 0000000..2876a16
--- /dev/null
+++ b/tests/libedata-book/test-sqlite-cursor-move-by-en-US.c
@@ -0,0 +1,63 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include <stdlib.h>
+#include <locale.h>
+#include <libebook/libebook.h>
+
+#include "data-test-utils.h"
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+       MoveByData *data;
+
+#if !GLIB_CHECK_VERSION (2, 35, 1)
+       g_type_init ();
+#endif
+       g_test_init (&argc, &argv, NULL);
+
+       data = move_by_test_new ("/EbSdbCursor/en_US/Move/Forward", "en_US.UTF-8");
+       move_by_test_add_assertion (data, 5, 11, 1, 2, 5, 6);
+       move_by_test_add_assertion (data, 6, 4, 3, 7, 8, 15, 17);
+       move_by_test_add (data, FALSE);
+
+       data = move_by_test_new ("/EbSdbCursor/en_US/Move/ForwardOnNameless", "en_US.UTF-8");
+       move_by_test_add_assertion (data, 1, 11);
+       move_by_test_add_assertion (data, 3, 1, 2, 5);
+       move_by_test_add (data, FALSE);
+
+       data = move_by_test_new ("/EbSdbCursor/en_US/Move/Backwards", "en_US.UTF-8");
+       move_by_test_add_assertion (data, -5, 20, 19, 9, 13, 12);
+       move_by_test_add_assertion (data, -8, 14, 10, 18, 16, 17, 15, 8, 7);
+       move_by_test_add (data, FALSE);
+
+       data = move_by_test_new ("/EbSdbCursor/en_US/Filtered/Move/Forward", "en_US.UTF-8");
+       move_by_test_add_assertion (data, 5, 11, 1, 2, 5, 3);
+       move_by_test_add_assertion (data, 8, 8, 17, 16, 18, 10, 14, 12, 9);
+       move_by_test_add (data, TRUE);
+
+       data = move_by_test_new ("/EbSdbCursor/en_US/Filtered/Move/Backwards", "en_US.UTF-8");
+       move_by_test_add_assertion (data, -5, 9, 12, 14, 10, 18);
+       move_by_test_add_assertion (data, -8, 16, 17, 8, 3, 5, 2, 1, 11);
+       move_by_test_add (data, TRUE);
+
+       data = move_by_test_new_full ("/EbSdbCursor/en_US/Move/Descending/Forward", "en_US.UTF-8",
+                                     E_BOOK_CURSOR_SORT_DESCENDING);
+       move_by_test_add_assertion (data, 5, 20, 19, 9,  13, 12);
+       move_by_test_add_assertion (data, 5, 14, 10, 18, 16, 17);
+       move_by_test_add_assertion (data, 5, 15, 8,  7,  3,  4);
+       move_by_test_add_assertion (data, 5, 6,  5,  2,  1,  11);
+       move_by_test_add (data, FALSE);
+
+       data = move_by_test_new_full ("/EbSdbCursor/en_US/Move/Descending/Forward/Loop", "en_US.UTF-8",
+                                     E_BOOK_CURSOR_SORT_DESCENDING);
+       move_by_test_add_assertion (data, 10, 20, 19, 9,  13, 12, 14, 10, 18, 16, 17);
+       move_by_test_add_assertion (data, 11, 15, 8,  7,  3,  4, 6,  5,  2,  1,  11, 0);
+
+       move_by_test_add_assertion (data, 10, 20, 19, 9,  13, 12, 14, 10, 18, 16, 17);
+       move_by_test_add_assertion (data, 10, 15, 8,  7,  3,  4, 6,  5,  2,  1,  11);
+       move_by_test_add (data, FALSE);
+
+       return e_test_server_utils_run ();
+}
diff --git a/tests/libedata-book/test-sqlite-cursor-move-by-fr-CA.c 
b/tests/libedata-book/test-sqlite-cursor-move-by-fr-CA.c
new file mode 100644
index 0000000..4d13e24
--- /dev/null
+++ b/tests/libedata-book/test-sqlite-cursor-move-by-fr-CA.c
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include <stdlib.h>
+#include <locale.h>
+#include <libebook/libebook.h>
+
+#include "data-test-utils.h"
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+       MoveByData *data;
+
+#if !GLIB_CHECK_VERSION (2, 35, 1)
+       g_type_init ();
+#endif
+       g_test_init (&argc, &argv, NULL);
+
+       data = move_by_test_new ("/EbSdbCursor/fr_CA/Move/Forward", "fr_CA.UTF-8");
+       move_by_test_add_assertion (data, 5, 11, 1, 2, 5, 6);
+       move_by_test_add_assertion (data, 6, 4, 3, 7, 8, 15, 17);
+       move_by_test_add (data, FALSE);
+
+       data = move_by_test_new ("/EbSdbCursor/fr_CA/Move/ForwardOnNameless", "fr_CA.UTF-8");
+       move_by_test_add_assertion (data, 1, 11);
+       move_by_test_add_assertion (data, 3, 1, 2, 5);
+       move_by_test_add (data, FALSE);
+
+       data = move_by_test_new ("/EbSdbCursor/fr_CA/Move/Backwards", "fr_CA.UTF-8");
+       move_by_test_add_assertion (data, -5, 20, 19, 9, 12, 13);
+       move_by_test_add_assertion (data, -8, 14, 10, 18, 16, 17, 15, 8, 7);
+       move_by_test_add (data, FALSE);
+
+       data = move_by_test_new ("/EbSdbCursor/fr_CA/Filtered/Move/Forward", "fr_CA.UTF-8");
+       move_by_test_add_assertion (data, 5, 11, 1, 2, 5, 3);
+       move_by_test_add_assertion (data, 8, 8, 17, 16, 18, 10, 14, 12, 9);
+       move_by_test_add (data, TRUE);
+
+       data = move_by_test_new ("/EbSdbCursor/fr_CA/Filtered/Move/Backwards", "fr_CA.UTF-8");
+       move_by_test_add_assertion (data, -5, 9, 12, 14, 10, 18);
+       move_by_test_add_assertion (data, -8, 16, 17, 8, 3, 5, 2, 1, 11);
+       move_by_test_add (data, TRUE);
+
+       return e_test_server_utils_run ();
+}
diff --git a/tests/libedata-book/test-sqlite-cursor-move-by-posix.c 
b/tests/libedata-book/test-sqlite-cursor-move-by-posix.c
new file mode 100644
index 0000000..01eed6a
--- /dev/null
+++ b/tests/libedata-book/test-sqlite-cursor-move-by-posix.c
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include <stdlib.h>
+#include <locale.h>
+#include <libebook/libebook.h>
+
+#include "data-test-utils.h"
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+       MoveByData *data;
+
+#if !GLIB_CHECK_VERSION (2, 35, 1)
+       g_type_init ();
+#endif
+       g_test_init (&argc, &argv, NULL);
+
+       data = move_by_test_new ("/EbSdbCursor/POSIX/Move/Forward", "POSIX");
+       move_by_test_add_assertion (data, 5, 11, 2, 6, 3, 8);
+       move_by_test_add_assertion (data, 6, 1,  5,  4,  7,  15, 17);
+       move_by_test_add (data, FALSE);
+
+       data = move_by_test_new ("/EbSdbCursor/POSIX/Move/ForwardOnNameless", "POSIX");
+       move_by_test_add_assertion (data, 1, 11);
+       move_by_test_add_assertion (data, 3, 2, 6, 3);
+       move_by_test_add (data, FALSE);
+
+       data = move_by_test_new ("/EbSdbCursor/POSIX/Move/Backwards", "POSIX");
+       move_by_test_add_assertion (data, -5, 20, 19, 9, 13, 12);
+       move_by_test_add_assertion (data, -12, 14, 10, 18, 16, 17, 15, 7, 4, 5, 1, 8, 3);
+       move_by_test_add (data, FALSE);
+
+       data = move_by_test_new ("/EbSdbCursor/POSIX/Filtered/Move/Forward", "POSIX");
+       move_by_test_add_assertion (data, 5, 11, 2, 3, 8, 1);
+       move_by_test_add_assertion (data, 8, 5, 17, 16, 18, 10, 14, 12, 9);
+       move_by_test_add (data, TRUE);
+
+       data = move_by_test_new ("/EbSdbCursor/POSIX/Filtered/Move/Backwards", "POSIX");
+       move_by_test_add_assertion (data, -5, 9, 12, 14, 10, 18);
+       move_by_test_add_assertion (data, -8, 16, 17, 5, 1, 8, 3, 2, 11);
+       move_by_test_add (data, TRUE);
+
+       return e_test_server_utils_run ();
+}
diff --git a/tests/libedata-book/test-sqlite-cursor-posix-initial.c 
b/tests/libedata-book/test-sqlite-cursor-posix-initial.c
new file mode 100644
index 0000000..0703fa1
--- /dev/null
+++ b/tests/libedata-book/test-sqlite-cursor-posix-initial.c
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include <stdlib.h>
+#include <locale.h>
+#include <libebook/libebook.h>
+
+#include "data-test-utils.h"
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+       MoveByData *data;
+
+#if !GLIB_CHECK_VERSION (2, 35, 1)
+       g_type_init ();
+#endif
+       g_test_init (&argc, &argv, NULL);
+
+       g_assert (g_setenv ("MIGRATION_TEST_SOURCE_NAME", "migration-test-source", TRUE));
+
+       /* This test actually creates the addressbook, subsequent migration tests dont
+        * recreate the contacts but rely on the addressbook to have migrated the sort keys
+        * into the new locales
+        */
+       data = move_by_test_new ("/EbSdbCursor/Locale/POSIX/Initial", "POSIX");
+       move_by_test_add_assertion (data, 5, 11, 2,  6,  3,  8);
+       move_by_test_add_assertion (data, 5, 1,  5,  4,  7,  15);
+       move_by_test_add_assertion (data, 5, 17, 16, 18, 10, 14);
+       move_by_test_add_assertion (data, 5, 12, 13, 9,  19, 20);
+       move_by_test_add (data, FALSE);
+
+       /* On this case, we want to delete the work directory and start afresh */
+       return e_test_server_utils_run ();
+}
diff --git a/tests/libedata-book/test-sqlite-cursor-posix-migrated.c 
b/tests/libedata-book/test-sqlite-cursor-posix-migrated.c
new file mode 100644
index 0000000..31a5cb9
--- /dev/null
+++ b/tests/libedata-book/test-sqlite-cursor-posix-migrated.c
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include <stdlib.h>
+#include <locale.h>
+#include <libebook/libebook.h>
+
+#include "data-test-utils.h"
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+       MoveByData *data;
+
+#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 ("MIGRATION_TEST_SOURCE_NAME", "migration-test-source", TRUE));
+
+       data = move_by_test_new ("/EbSdbCursor/Locale/POSIX/Migrated", "POSIX");
+       move_by_test_add_assertion (data, 5, 11, 2,  6,  3,  8);
+       move_by_test_add_assertion (data, 5, 1,  5,  4,  7,  15);
+       move_by_test_add_assertion (data, 5, 17, 16, 18, 10, 14);
+       move_by_test_add_assertion (data, 5, 12, 13, 9,  19, 20);
+       move_by_test_add (data, FALSE);
+
+       /* On this case, we are using the migrated addressbook, don't delete it first */
+       return e_test_server_utils_run_full (E_TEST_SERVER_KEEP_WORK_DIRECTORY);
+}
diff --git a/tests/libedata-book/test-sqlite-cursor-set-sexp.c 
b/tests/libedata-book/test-sqlite-cursor-set-sexp.c
new file mode 100644
index 0000000..05dcc7f
--- /dev/null
+++ b/tests/libedata-book/test-sqlite-cursor-set-sexp.c
@@ -0,0 +1,104 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include <stdlib.h>
+#include <locale.h>
+#include <libebook/libebook.h>
+
+#include "data-test-utils.h"
+
+static EbSdbCursorClosure book_closure = { { E_TEST_SERVER_ADDRESS_BOOK, 
e_sqlitedb_cursor_fixture_setup_book, 0 }, FALSE };
+
+static void
+test_cursor_sexp_invalid (EbSdbCursorFixture *fixture,
+                         gconstpointer  user_data)
+{
+       GError *error = NULL;
+       EBookQuery *query;
+       gchar *sexp = NULL;
+
+       query = e_book_query_field_test (E_CONTACT_NICKNAME, E_BOOK_QUERY_BEGINS_WITH, "Kung Fu");
+       sexp = e_book_query_to_string (query);
+       e_book_query_unref (query);
+
+       if (e_book_backend_sqlitedb_cursor_set_sexp (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor, sexp, &error))
+               g_error ("Succeeded in setting non-summarized field in the cursor query expression");
+
+       g_assert (error);
+       g_assert (g_error_matches (error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_INVALID_QUERY));
+}
+
+static void
+test_cursor_sexp_calculate_position (EbSdbCursorFixture *fixture,
+                                    gconstpointer  user_data)
+{
+       GError *error = NULL;
+       EBookQuery *query;
+       gint    position = 0, total = 0;
+       gchar *sexp = NULL;
+       GSList *results = NULL, *node;
+       EbSdbSearchData *data;
+
+       /* Set the cursor to point exactly to 'blackbirds', which is the 12th contact in en_US */
+       if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor,
+                                                    EBSDB_CURSOR_ORIGIN_RESET,
+                                                    12, &results, &error))
+               g_error ("Error fetching cursor results: %s", error->message);
+
+       /* Ensure we moved to the right contact */
+       node = g_slist_last (results);
+       g_assert (node);
+       data = node->data;
+       g_assert_cmpstr (data->uid, ==, "sorted-16");
+       g_slist_foreach (results, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+       g_slist_free (results);
+
+       /* Check position */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+               g_error ("Error calculating cursor: %s", error->message);
+
+       /* blackbird is at position 12 in an unfiltered en_US locale */
+       g_assert_cmpint (position, ==, 12);
+       g_assert_cmpint (total, ==, 20);
+
+       /* Set new sexp, only contacts with .com email addresses */
+       query = e_book_query_field_test (E_CONTACT_EMAIL, E_BOOK_QUERY_ENDS_WITH, ".com");
+       sexp = e_book_query_to_string (query);
+       e_book_query_unref (query);
+
+       if (!e_book_backend_sqlitedb_cursor_set_sexp (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                     fixture->cursor, sexp, &error))
+               g_error ("Failed to set sexp: %s", error->message);
+
+       /* Check new position after modified sexp */
+       if (!e_book_backend_sqlitedb_cursor_calculate (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                      fixture->cursor, &total, &position, &error))
+               g_error ("Error calculating cursor: %s", error->message);
+
+       /* 'blackbird' is now at position 8 out of 13, with a filtered set of contacts in en_US locale */
+       g_assert_cmpint (position, ==, 8);
+       g_assert_cmpint (total, ==, 13);
+}
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+#if !GLIB_CHECK_VERSION (2, 35, 1)
+       g_type_init ();
+#endif
+       g_test_init (&argc, &argv, NULL);
+
+       g_test_add ("/EbSdbCursor/SetSexp/Invalid", EbSdbCursorFixture, &book_closure,
+                   e_sqlitedb_cursor_fixture_setup,
+                   test_cursor_sexp_invalid,
+                   e_sqlitedb_cursor_fixture_teardown);
+       g_test_add ("/EbSdbCursor/SetSexp/CalculatePosition", EbSdbCursorFixture, &book_closure,
+                   e_sqlitedb_cursor_fixture_setup,
+                   test_cursor_sexp_calculate_position,
+                   e_sqlitedb_cursor_fixture_teardown);
+
+       return e_test_server_utils_run ();
+}
diff --git a/tests/libedata-book/test-sqlite-cursor-set-target.c 
b/tests/libedata-book/test-sqlite-cursor-set-target.c
new file mode 100644
index 0000000..69bc986
--- /dev/null
+++ b/tests/libedata-book/test-sqlite-cursor-set-target.c
@@ -0,0 +1,180 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include <stdlib.h>
+#include <locale.h>
+#include <libebook/libebook.h>
+
+#include "data-test-utils.h"
+
+static EbSdbCursorClosure book_closure = { { E_TEST_SERVER_ADDRESS_BOOK, 
e_sqlitedb_cursor_fixture_setup_book, 0 }, FALSE };
+
+/*****************************************************
+ *          Expect the same results twice            *
+ *****************************************************/
+static void
+test_cursor_set_target_reset_cursor (EbSdbCursorFixture *fixture,
+                                    gconstpointer  user_data)
+{
+       GSList *results = NULL;
+       GError *error = NULL;
+
+       /* First batch */
+       if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor,
+                                                    EBSDB_CURSOR_ORIGIN_CURRENT,
+                                                    5, &results, &error))
+               g_error ("Error fetching cursor results: %s", error->message);
+
+       print_results (results);
+
+       /* Assert the first 5 contacts in en_US order */
+       g_assert_cmpint (g_slist_length (results), ==, 5);
+       assert_contacts_order (results,
+                              "sorted-11",
+                              "sorted-1",
+                              "sorted-2",
+                              "sorted-5",
+                              "sorted-6",
+                              NULL);
+
+       g_slist_foreach (results, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+       g_slist_free (results);
+       results = NULL;
+
+       /* Second batch reset (same results) */
+       if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor,
+                                                    EBSDB_CURSOR_ORIGIN_RESET,
+                                                    5, &results, &error))
+               g_error ("Error fetching cursor results: %s", error->message);
+
+       print_results (results);
+
+       /* Assert the first 5 contacts in en_US order again */
+       g_assert_cmpint (g_slist_length (results), ==, 5);
+       assert_contacts_order (results,
+                              "sorted-11",
+                              "sorted-1",
+                              "sorted-2",
+                              "sorted-5",
+                              "sorted-6",
+                              NULL);
+
+       g_slist_foreach (results, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+       g_slist_free (results);
+}
+
+/*****************************************************
+ * Expect results with family name starting with 'C' *
+ *****************************************************/
+static void
+test_cursor_set_target_c_next_results (EbSdbCursorFixture *fixture,
+                                      gconstpointer  user_data)
+{
+       GSList *results = NULL;
+       GError *error = NULL;
+       ECollator *collator;
+       gint n_labels;
+       const gchar *const *labels;
+
+       /* First verify our test... in en_US locale the label 'C' should exist with the index 3 */
+       collator = e_book_backend_sqlitedb_ref_collator (((ESqliteDBFixture *) fixture)->ebsdb);
+       labels = e_collator_get_index_labels (collator, &n_labels, NULL, NULL, NULL);
+       g_assert_cmpstr (labels[3], ==, "C");
+       e_collator_unref (collator);
+
+       /* Set the cursor at the start of family names beginning with 'C' */
+       e_book_backend_sqlitedb_cursor_set_target_alphabetic_index (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                                   fixture->cursor, 3);
+
+       if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor,
+                                                    EBSDB_CURSOR_ORIGIN_CURRENT,
+                                                    5, &results, &error))
+               g_error ("Error fetching cursor results: %s", error->message);
+
+       print_results (results);
+
+       /* Assert that we got the results starting at C */
+       g_assert_cmpint (g_slist_length (results), ==, 5);
+       assert_contacts_order (results,
+                              "sorted-10",
+                              "sorted-14",
+                              "sorted-12",
+                              "sorted-13",
+                              "sorted-9",
+                              NULL);
+
+       g_slist_foreach (results, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+       g_slist_free (results);
+}
+
+/*****************************************************
+ *       Expect results before the letter 'C'        *
+ *****************************************************/
+static void
+test_cursor_set_target_c_prev_results (EbSdbCursorFixture *fixture,
+                                      gconstpointer  user_data)
+{
+       GSList *results = NULL;
+       GError *error = NULL;
+       ECollator *collator;
+       gint n_labels;
+       const gchar *const *labels;
+
+       /* First verify our test... in en_US locale the label 'C' should exist with the index 3 */
+       collator = e_book_backend_sqlitedb_ref_collator (((ESqliteDBFixture *) fixture)->ebsdb);
+       labels = e_collator_get_index_labels (collator, &n_labels, NULL, NULL, NULL);
+       g_assert_cmpstr (labels[3], ==, "C");
+       e_collator_unref (collator);
+
+       /* Set the cursor at the start of family names beginning with 'C' */
+       e_book_backend_sqlitedb_cursor_set_target_alphabetic_index (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                                   fixture->cursor, 3);
+
+       if (!e_book_backend_sqlitedb_cursor_move_by (((ESqliteDBFixture *) fixture)->ebsdb,
+                                                    fixture->cursor, 
+                                                    EBSDB_CURSOR_ORIGIN_CURRENT,
+                                                    -5, &results, &error))
+               g_error ("Error fetching cursor results: %s", error->message);
+
+       print_results (results);
+
+       /* Assert that we got the results before C */
+       g_assert_cmpint (g_slist_length (results), ==, 5);
+       assert_contacts_order (results,
+                              "sorted-18",
+                              "sorted-16",
+                              "sorted-17",
+                              "sorted-15",
+                              "sorted-8",
+                              NULL);
+
+       g_slist_foreach (results, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+       g_slist_free (results);
+}
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+#if !GLIB_CHECK_VERSION (2, 35, 1)
+       g_type_init ();
+#endif
+       g_test_init (&argc, &argv, NULL);
+
+       g_test_add ("/EbSdbCursor/SetTarget/ResetCursor", EbSdbCursorFixture, &book_closure,
+                   e_sqlitedb_cursor_fixture_setup,
+                   test_cursor_set_target_reset_cursor,
+                   e_sqlitedb_cursor_fixture_teardown);
+       g_test_add ("/EbSdbCursor/SetTarget/Alphabetic/C/NextResults", EbSdbCursorFixture, &book_closure,
+                   e_sqlitedb_cursor_fixture_setup,
+                   test_cursor_set_target_c_next_results,
+                   e_sqlitedb_cursor_fixture_teardown);
+       g_test_add ("/EbSdbCursor/SetTarget/Alphabetic/C/PreviousResults", EbSdbCursorFixture, &book_closure,
+                   e_sqlitedb_cursor_fixture_setup,
+                   test_cursor_set_target_c_prev_results,
+                   e_sqlitedb_cursor_fixture_teardown);
+
+       return e_test_server_utils_run ();
+}
diff --git a/tests/libedata-book/test-sqlite-get-contact.c b/tests/libedata-book/test-sqlite-get-contact.c
new file mode 100644
index 0000000..6736567
--- /dev/null
+++ b/tests/libedata-book/test-sqlite-get-contact.c
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include <stdlib.h>
+#include <locale.h>
+#include <libebook/libebook.h>
+
+#include "data-test-utils.h"
+
+static ETestServerClosure book_closure = { E_TEST_SERVER_ADDRESS_BOOK, NULL, 0 };
+
+static void
+test_get_contact (ESqliteDBFixture *fixture,
+                 gconstpointer     user_data)
+{
+       EBookClient *book_client;
+       EContact *contact = NULL;
+       EContact *other;
+       GError *error = NULL;
+
+       book_client = E_TEST_SERVER_UTILS_SERVICE (fixture, EBookClient);
+
+       if (!add_contact_from_test_case_verify (book_client, "simple-1", &contact)) {
+               g_error ("Failed to get contact");
+       }
+
+       other = e_book_backend_sqlitedb_get_contact (fixture->ebsdb, SQLITEDB_FOLDER_ID,
+                                                    (const gchar *)e_contact_get_const (contact, 
E_CONTACT_UID),
+                                                    NULL, NULL, &error);
+
+       if (!other)
+               g_error ("Failed to get contact with uid '%s': %s",
+                        (const gchar *)e_contact_get_const (contact, E_CONTACT_UID),
+                        error->message);
+
+       g_object_unref (contact);
+       g_object_unref (other);
+}
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+#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 ("/EBookBackendSqliteDB/GetContact", ESqliteDBFixture, &book_closure,
+                   e_sqlitedb_fixture_setup, test_get_contact, e_sqlitedb_fixture_teardown);
+
+       return e_test_server_utils_run ();
+}


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