[folks/next] Port to telepathy-glib-1



commit af26addebbc4d20783c29f5b8d57cfc9183ba395
Author: Xavier Claessens <xavier claessens collabora co uk>
Date:   Thu Sep 26 15:03:35 2013 -0400

    Port to telepathy-glib-1
    
    Note the evil hack in Makefiles when generating vapi files. I have
    to use sed to change the namespace from "Tp." to "TelepathyGLib.".
    It seems vapigen is confused between telepathy-glib's gir namespace
    and its vapi namespace. I don't know why.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=708871

 backends/telepathy/Makefile.am                     |    2 +-
 backends/telepathy/lib/Makefile.am                 |    8 +-
 .../lib/folks-telepathy-uninstalled.pc.in          |    2 +-
 backends/telepathy/lib/folks-telepathy.deps        |    2 +-
 backends/telepathy/lib/folks-telepathy.pc.in       |    2 +-
 backends/telepathy/lib/tp-lowlevel.c               |    1 +
 backends/telepathy/lib/tpf-persona-store.vala      |   38 +-
 backends/telepathy/lib/tpf-persona.vala            |   10 +-
 backends/telepathy/tp-backend.vala                 |   18 +-
 configure.ac                                       |   11 +-
 docs/Makefile.am                                   |    2 +-
 folks/folks-uninstalled.pc.in                      |    2 +-
 tests/folks/aggregation.vala                       |    2 +-
 tests/lib/telepathy/contactlist/Makefile.am        |    8 +-
 tests/lib/telepathy/contactlist/backend.c          |   15 +-
 .../contactlist/broken-client-types-conn.c         |   65 ++
 .../contactlist/broken-client-types-conn.h         |   50 ++
 tests/lib/telepathy/contactlist/bug-19101-conn.c   |   74 ++
 tests/lib/telepathy/contactlist/bug-19101-conn.h   |   51 ++
 .../telepathy/contactlist/contact-list-manager.c   |  140 ++--
 .../telepathy/contactlist/contact-list-manager.h   |    2 +-
 tests/lib/telepathy/contactlist/contacts-conn.c    |  472 ++----------
 tests/lib/telepathy/contactlist/contacts-conn.h    |   76 +--
 tests/lib/telepathy/contactlist/dbus-tube-chan.c   |  455 ++++++++++++
 tests/lib/telepathy/contactlist/dbus-tube-chan.h   |  127 ++++
 tests/lib/telepathy/contactlist/echo-chan.c        |  223 ++++++
 tests/lib/telepathy/contactlist/echo-chan.h        |   56 ++
 tests/lib/telepathy/contactlist/echo-conn.c        |  165 +++++
 tests/lib/telepathy/contactlist/echo-conn.h        |   56 ++
 tests/lib/telepathy/contactlist/echo-im-manager.c  |  382 ++++++++++
 tests/lib/telepathy/contactlist/echo-im-manager.h  |   54 ++
 .../lib/telepathy/contactlist/file-transfer-chan.c |  763 ++++++++++++++++++++
 .../lib/telepathy/contactlist/file-transfer-chan.h |   63 ++
 tests/lib/telepathy/contactlist/my-conn-proxy.c    |  364 ++++++++++
 tests/lib/telepathy/contactlist/my-conn-proxy.h    |  124 ++++
 tests/lib/telepathy/contactlist/myassert.h         |   16 +
 tests/lib/telepathy/contactlist/room-list-chan.c   |    3 +-
 tests/lib/telepathy/contactlist/room-list-chan.h   |    2 +-
 .../telepathy/contactlist/simple-account-manager.c |   67 +-
 .../telepathy/contactlist/simple-account-manager.h |    4 +-
 tests/lib/telepathy/contactlist/simple-account.c   |   84 ++-
 tests/lib/telepathy/contactlist/simple-account.h   |    6 +-
 .../simple-channel-dispatch-operation.c            |  303 ++++++++
 .../simple-channel-dispatch-operation.h            |   73 ++
 .../contactlist/simple-channel-dispatcher.c        |  394 ++++++++++
 .../contactlist/simple-channel-dispatcher.h        |   66 ++
 .../telepathy/contactlist/simple-channel-manager.c |   95 +++
 .../telepathy/contactlist/simple-channel-manager.h |   54 ++
 .../telepathy/contactlist/simple-channel-request.c |  508 +++++++++++++
 .../telepathy/contactlist/simple-channel-request.h |   70 ++
 tests/lib/telepathy/contactlist/simple-client.c    |  250 +++++++
 tests/lib/telepathy/contactlist/simple-client.h    |   64 ++
 tests/lib/telepathy/contactlist/simple-conn.c      |  132 ++---
 tests/lib/telepathy/contactlist/simple-conn.h      |    8 +-
 tests/lib/telepathy/contactlist/stream-tube-chan.c |  650 +++++++++++++++++
 tests/lib/telepathy/contactlist/stream-tube-chan.h |  129 ++++
 tests/lib/telepathy/contactlist/stub-object.c      |   68 ++
 tests/lib/telepathy/contactlist/stub-object.h      |   11 +
 tests/lib/telepathy/contactlist/textchan-group.c   |  329 +++++++++
 tests/lib/telepathy/contactlist/textchan-group.h   |   72 ++
 tests/lib/telepathy/contactlist/textchan-null.c    |  579 ---------------
 tests/lib/telepathy/contactlist/textchan-null.h    |  137 ----
 tests/lib/telepathy/contactlist/tls-certificate.c  |  350 +++++++++
 tests/lib/telepathy/contactlist/tls-certificate.h  |   68 ++
 .../telepathy/contactlist/tp-test-contactlist.deps |    2 +-
 tests/lib/telepathy/contactlist/util.c             |  207 +++++-
 tests/lib/telepathy/contactlist/util.h             |   38 +-
 tests/lib/telepathy/tpf-test.deps                  |    2 +-
 tests/telepathy/Makefile.am                        |    2 +-
 tests/telepathy/individual-properties.vala         |    2 +-
 tests/telepathy/individual-retrieval.vala          |    2 +-
 tests/tools/manager-file.py                        |    4 +-
 tools/import-pidgin.vala                           |    2 +-
 73 files changed, 7235 insertions(+), 1503 deletions(-)
---
diff --git a/backends/telepathy/Makefile.am b/backends/telepathy/Makefile.am
index d22d969..6e036d1 100644
--- a/backends/telepathy/Makefile.am
+++ b/backends/telepathy/Makefile.am
@@ -17,7 +17,7 @@ telepathy_la_VALAFLAGS = \
        --pkg gee-0.8 \
        --pkg gio-2.0 \
        --pkg gobject-2.0 \
-       --pkg telepathy-glib \
+       --pkg telepathy-glib-1 \
        $(NULL)
 
 backenddir = $(BACKEND_DIR)/telepathy
diff --git a/backends/telepathy/lib/Makefile.am b/backends/telepathy/lib/Makefile.am
index 0d32f81..480d270 100644
--- a/backends/telepathy/lib/Makefile.am
+++ b/backends/telepathy/lib/Makefile.am
@@ -78,8 +78,8 @@ tp-lowlevel.vapi: TpLowlevel-$(API_VERSION_DOT).gir
        $(AM_V_GEN)$(VAPIGEN) $(VAPIGENFLAGS) \
                --library tp-lowlevel \
                --pkg gio-2.0 \
-               --pkg telepathy-glib \
-               TpLowlevel-$(API_VERSION_DOT).gir
+               --pkg telepathy-glib-1 \
+               TpLowlevel-$(API_VERSION_DOT).gir && sed -i "s/Tp\./TelepathyGLib\./g" $@
        touch $@
 endif
 
@@ -156,7 +156,7 @@ libtp_zeitgeist_la_VALAFLAGS = \
        --pkg gio-2.0 \
        --pkg gee-0.8 \
        --pkg zeitgeist-2.0 \
-       --pkg telepathy-glib \
+       --pkg telepathy-glib-1 \
        --pkg folks \
        --pkg build-conf \
        --library tp-zeitgeist \
@@ -226,7 +226,7 @@ libfolks_telepathy_la_VALAFLAGS = \
        --pkg gobject-2.0 \
        --pkg gio-2.0 \
        --pkg gee-0.8 \
-       --pkg telepathy-glib \
+       --pkg telepathy-glib-1 \
        --includedir folks \
        --library folks-telepathy \
        --vapi folks-telepathy.vapi \
diff --git a/backends/telepathy/lib/folks-telepathy-uninstalled.pc.in 
b/backends/telepathy/lib/folks-telepathy-uninstalled.pc.in
index c3a70c3..81dcdeb 100644
--- a/backends/telepathy/lib/folks-telepathy-uninstalled.pc.in
+++ b/backends/telepathy/lib/folks-telepathy-uninstalled.pc.in
@@ -7,6 +7,6 @@ vapidir= abs_top_srcdir@/folks
 Name: Folks Telepathy support library (uninstalled copy)
 Description: Telepathy support library for the Folks meta-contacts library
 Version: @VERSION@
-Requires: folks glib-2.0 gobject-2.0 gee-0.8 telepathy-glib >= 0.11.11
+Requires: folks glib-2.0 gobject-2.0 gee-0.8 telepathy-glib-1 >= 0.99.1
 Libs: ${abs_top_builddir}/backends/telepathy/libfolks-telepathy.la
 Cflags: -I${abs_top_srcdir} -I${abs_top_srcdir}/backends/telepathy -I${abs_top_builddir}
diff --git a/backends/telepathy/lib/folks-telepathy.deps b/backends/telepathy/lib/folks-telepathy.deps
index dec8d50..8ecb088 100644
--- a/backends/telepathy/lib/folks-telepathy.deps
+++ b/backends/telepathy/lib/folks-telepathy.deps
@@ -1,4 +1,4 @@
 glib-2.0
 gobject-2.0
 folks
-telepathy-glib
+telepathy-glib-1
diff --git a/backends/telepathy/lib/folks-telepathy.pc.in b/backends/telepathy/lib/folks-telepathy.pc.in
index 3e56916..1e5d899 100644
--- a/backends/telepathy/lib/folks-telepathy.pc.in
+++ b/backends/telepathy/lib/folks-telepathy.pc.in
@@ -10,6 +10,6 @@ vapidir= datadir@/vala/vapi
 Name: Folks Telepathy support library
 Description: Telepathy support library for the Folks meta-contacts library
 Version: @VERSION@
-Requires: folks glib-2.0 gobject-2.0 gee-0.8 telepathy-glib >= 0.11.11
+Requires: folks glib-2.0 gobject-2.0 gee-0.8 telepathy-glib-1 >= 0.99.1
 Libs: -L${libdir} -lfolks-telepathy
 Cflags: -I${includedir}
diff --git a/backends/telepathy/lib/tp-lowlevel.c b/backends/telepathy/lib/tp-lowlevel.c
index da5052c..d803e0f 100644
--- a/backends/telepathy/lib/tp-lowlevel.c
+++ b/backends/telepathy/lib/tp-lowlevel.c
@@ -27,6 +27,7 @@
 #include <glib/gi18n.h>
 #include <gio/gio.h>
 #include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
 
 #include "tp-lowlevel.h"
 
diff --git a/backends/telepathy/lib/tpf-persona-store.vala b/backends/telepathy/lib/tpf-persona-store.vala
index b5b0ac6..63a5b00 100644
--- a/backends/telepathy/lib/tpf-persona-store.vala
+++ b/backends/telepathy/lib/tpf-persona-store.vala
@@ -395,8 +395,8 @@ public class Tpf.PersonaStore : Folks.PersonaStore
 
       /* We do not trust local-xmpp or IRC at all, since Persona UIDs can be
        * faked by just changing hostname/username or nickname. */
-      if (account.get_protocol () == "local-xmpp" ||
-          account.get_protocol () == "irc")
+      if (account.get_protocol_name () == "local_xmpp" ||
+          account.get_protocol_name () == "irc")
         this.trust_level = PersonaStoreTrust.NONE;
       else
         this.trust_level = PersonaStoreTrust.PARTIAL;
@@ -487,14 +487,14 @@ public class Tpf.PersonaStore : Folks.PersonaStore
            * contacts. */
           var factory = this._account_manager.get_factory ();
           factory.add_contact_features ({
-              ContactFeature.ALIAS,
-              ContactFeature.AVATAR_DATA,
-              ContactFeature.AVATAR_TOKEN,
-              ContactFeature.CAPABILITIES,
-              ContactFeature.CLIENT_TYPES,
-              ContactFeature.PRESENCE,
-              ContactFeature.CONTACT_INFO,
-              ContactFeature.CONTACT_GROUPS
+              Contact.get_feature_quark_alias (),
+              Contact.get_feature_quark_avatar_data (),
+              Contact.get_feature_quark_avatar_token (),
+              Contact.get_feature_quark_capabilities (),
+              Contact.get_feature_quark_client_types (),
+              Contact.get_feature_quark_presence (),
+              Contact.get_feature_quark_contact_info (),
+              Contact.get_feature_quark_contact_groups (),
           });
 
           this._account_manager.invalidated.connect (
@@ -511,7 +511,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
                   this._remove_store (this._persona_set);
                 }
             });
-          this._account_manager.account_validity_changed.connect (
+          this._account_manager.account_usability_changed.connect (
               (a, valid) =>
                 {
                   if (!valid && this.account == a)
@@ -762,11 +762,11 @@ public class Tpf.PersonaStore : Folks.PersonaStore
           0
       });
 
-      if (!this.account.connection.has_interface_by_id (
-          iface_quark_connection_interface_contact_list ()))
+      if (!this.account.connection.is_prepared (
+          TelepathyGLib.Connection.get_feature_quark_contact_list ()))
         {
           debug ("Connection does not implement ContactList iface; " +
-              "legacy CMs are not supported any more.");
+              "skipping store with no contact list.");
 
           this._remove_store (this._persona_set);
 
@@ -851,7 +851,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
           if ((ci_flags & ContactInfoFlags.CAN_SET) != 0)
             {
               var field_specs =
-                connection.get_contact_info_supported_fields ();
+                connection.dup_contact_info_supported_fields ();
               foreach (var field_spec in field_specs)
                 {
                   /* XXX: we ignore the maximum count for each type of
@@ -876,12 +876,12 @@ public class Tpf.PersonaStore : Folks.PersonaStore
   private async void _load_cache (HashSet<Persona>? old_personas)
     {
       /* Only load from the cache if the account is enabled and valid. */
-      if (this.account.enabled == false || this.account.valid == false)
+      if (this.account.enabled == false || this.account.usable == false)
         {
           debug ("Skipping loading cache for Tpf.PersonaStore %p ('%s'): " +
               "enabled: %s, valid: %s.", this, this.id,
               this.account.enabled ? "yes" : "no",
-              this.account.valid ? "yes" : "no");
+              this.account.usable ? "yes" : "no");
 
           return;
         }
@@ -973,7 +973,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
       /* Only store/load the cache if the account is enabled and valid;
        * otherwise, the PersonaStore will get removed and the cache
        * deleted later anyway. */
-      if (!this.account.enabled || !this.account.valid)
+      if (!this.account.enabled || !this.account.usable)
         {
           debug ("Skipping storing cache for Tpf.PersonaStore %p (‘%s’) as " +
               "its TpAccount is disabled or invalid.", this, this.id);
@@ -1680,7 +1680,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
   private async void _populate_counters ()
     {
       this._zg_controller = new FolksTpZeitgeist.Controller (this,
-          this.account.protocol, (p, dt) =>
+          this.account.protocol_name, (p, dt) =>
         {
           var persona = p as Tpf.Persona;
           assert (persona != null);
diff --git a/backends/telepathy/lib/tpf-persona.vala b/backends/telepathy/lib/tpf-persona.vala
index 6a3cf55..4d9572e 100644
--- a/backends/telepathy/lib/tpf-persona.vala
+++ b/backends/telepathy/lib/tpf-persona.vala
@@ -765,10 +765,10 @@ public class Tpf.Persona : Folks.Persona,
                * interface along with the code in
                * Kf.Persona.linkable_property_to_links(), but that depends on
                * bgo#624842 being fixed. */
-              iid: account.get_protocol () + ":" + id,
+              iid: account.get_protocol_name () + ":" + id,
               uid: uid,
               store: store,
-              is_user: contact.handle == connection.self_handle);
+              is_user: contact == connection.self_contact);
 
       debug ("Created new Tpf.Persona '%s' for service-specific UID '%s': %p",
           uid, id, this);
@@ -815,9 +815,9 @@ public class Tpf.Persona : Folks.Persona,
       try
         {
           var im_addr = ImDetails.normalise_im_address (this.display_id,
-              account.get_protocol ());
+              account.get_protocol_name ());
           var im_fd = new ImFieldDetails (im_addr);
-          this._im_addresses.set (account.get_protocol (), im_fd);
+          this._im_addresses.set (account.get_protocol_name (), im_fd);
         }
       catch (ImDetailsError e)
         {
@@ -974,7 +974,7 @@ public class Tpf.Persona : Folks.Persona,
            AbstractFieldDetails<string>.hash_static,
            AbstractFieldDetails<string>.equal_static);
 
-      var contact_info = contact.get_contact_info ();
+      var contact_info = contact.dup_contact_info ();
       foreach (var info in contact_info)
         {
           if (info.field_name == "") {}
diff --git a/backends/telepathy/tp-backend.vala b/backends/telepathy/tp-backend.vala
index f593604..b119db1 100644
--- a/backends/telepathy/tp-backend.vala
+++ b/backends/telepathy/tp-backend.vala
@@ -84,8 +84,8 @@ public class Folks.Backends.Tp.Backend : Folks.Backend
       PersonaStore[] removed_stores = {};
 
       /* First handle adding any missing persona stores. */
-      GLib.List<unowned Account> accounts =
-        this._account_manager.get_valid_accounts ();
+      GLib.List<Account> accounts =
+          this._account_manager.dup_usable_accounts ();
       foreach (Account account in accounts)
         {
           string id = account.get_object_path ();
@@ -170,11 +170,11 @@ public class Folks.Backends.Tp.Backend : Folks.Backend
           yield this._account_manager.prepare_async (null);
           this._account_manager.account_enabled.connect (
               this._account_enabled_cb);
-          this._account_manager.account_validity_changed.connect (
-              this._account_validity_changed_cb);
+          this._account_manager.account_usability_changed.connect (
+              this._account_usability_changed_cb);
 
-          GLib.List<unowned Account> accounts =
-          this._account_manager.get_valid_accounts ();
+          GLib.List<Account> accounts =
+              this._account_manager.dup_usable_accounts ();
           foreach (Account account in accounts)
             {
               this._account_enabled_cb (account);
@@ -210,8 +210,8 @@ public class Folks.Backends.Tp.Backend : Folks.Backend
 
           this._account_manager.account_enabled.disconnect (
               this._account_enabled_cb);
-          this._account_manager.account_validity_changed.disconnect (
-              this._account_validity_changed_cb);
+          this._account_manager.account_usability_changed.disconnect (
+              this._account_usability_changed_cb);
           this._account_manager = null;
 
           this._is_quiescent = false;
@@ -226,7 +226,7 @@ public class Folks.Backends.Tp.Backend : Folks.Backend
         }
     }
 
-  private void _account_validity_changed_cb (Account account, bool valid)
+  private void _account_usability_changed_cb (Account account, bool valid)
     {
       if (valid)
         this._account_enabled_cb (account);
diff --git a/configure.ac b/configure.ac
index d00dc94..0fa0395 100644
--- a/configure.ac
+++ b/configure.ac
@@ -159,7 +159,7 @@ AS_IF([test "x$enable_libsocialweb_backend" != "xno"], [
       have_libsocialweb_backend="yes", have_libsocialweb_backend="no")
   AS_IF([test "x$have_libsocialweb_backend" = "xyes" -a \
           "x$enable_vala" = "xyes"], [
-    VALA_CHECK_PACKAGES([telepathy-glib
+    VALA_CHECK_PACKAGES([telepathy-glib-1
                          gio-2.0
                          gee-0.8
                          libsocialweb-client],
@@ -209,10 +209,13 @@ PKG_CHECK_MODULES([DBUS_GLIB], [dbus-glib-1 dbus-1])
 
 PKG_CHECK_MODULES([GEE], [gee-0.8 >= $GEE_REQUIRED])
 
-TP_GLIB_REQUIRED=0.19.0
+TP_GLIB_REQUIRED=0.99.1
 
 AS_IF([test x$enable_telepathy_backend = xyes], [
-        PKG_CHECK_MODULES([TP_GLIB], [telepathy-glib >= $TP_GLIB_REQUIRED])])
+        PKG_CHECK_MODULES([TP_GLIB], [
+            telepathy-glib-1 >= $TP_GLIB_REQUIRED
+            telepathy-glib-1-dbus >= $TP_GLIB_REQUIRED
+            ])])
 case "x$enable_zeitgeist" in
   xyes)
     AS_IF([test x$enable_telepathy_backend = xyes],
@@ -341,7 +344,7 @@ AS_IF([test "x$enable_vala" = "xyes"], [
                              gee-0.8])
 
         AS_IF([test x$enable_telepathy_backend = xyes], [
-          VALA_CHECK_PACKAGES([telepathy-glib])
+          VALA_CHECK_PACKAGES([telepathy-glib-1])
          AS_IF([test "x$have_zeitgeist" == "xyes"], [
              VALA_CHECK_PACKAGES([zeitgeist-2.0])
          ])
diff --git a/docs/Makefile.am b/docs/Makefile.am
index d94b21a..56ece33 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -135,7 +135,7 @@ folks_telepathy_doc_deps = \
        gmodule-2.0 \
        gio-2.0 \
        gee-0.8 \
-       telepathy-glib \
+       telepathy-glib-1 \
        zeitgeist-2.0 \
        tp-lowlevel \
        build-conf \
diff --git a/folks/folks-uninstalled.pc.in b/folks/folks-uninstalled.pc.in
index 642d801..a4db1a7 100644
--- a/folks/folks-uninstalled.pc.in
+++ b/folks/folks-uninstalled.pc.in
@@ -7,6 +7,6 @@ vapidir= abs_top_srcdir@/folks
 Name: Folks (uninstalled copy)
 Description: The Folks meta-contacts library
 Version: @VERSION@
-Requires: glib-2.0 gobject-2.0 gee-0.8 telepathy-glib >= 0.11.11
+Requires: glib-2.0 gobject-2.0 gee-0.8 telepathy-glib-1 >= 0.99.1
 Libs: ${abs_top_builddir}/folks/libfolks.la
 Cflags: -I${abs_top_srcdir} -I${abs_top_builddir}
diff --git a/tests/folks/aggregation.vala b/tests/folks/aggregation.vala
index 22946bb..410d8f7 100644
--- a/tests/folks/aggregation.vala
+++ b/tests/folks/aggregation.vala
@@ -26,7 +26,7 @@ public class AggregationTests : TpfTest.MixedTestCase
   private HashSet<string> _default_personas;
 
   private static string iid_prefix =
-      "telepathy:/org/freedesktop/Telepathy/Account/cm/protocol/account:";
+      "telepathy:/im/telepathy1/Account/cm/protocol/account:";
   private string olivier_sha1 = Checksum.compute_for_string (ChecksumType.SHA1,
       iid_prefix + "olivier example com");
 
diff --git a/tests/lib/telepathy/contactlist/Makefile.am b/tests/lib/telepathy/contactlist/Makefile.am
index 05d23f6..b9b6910 100644
--- a/tests/lib/telepathy/contactlist/Makefile.am
+++ b/tests/lib/telepathy/contactlist/Makefile.am
@@ -11,7 +11,7 @@ VAPIGENFLAGS += \
        --pkg gee-0.8 \
        --pkg gmodule-2.0 \
        --pkg dbus-glib-1 \
-       --pkg telepathy-glib \
+       --pkg telepathy-glib-1 \
        $(NULL)
 
 noinst_LTLIBRARIES = libtp-test-contactlist.la
@@ -36,8 +36,8 @@ libtp_test_contactlist_la_SOURCES = \
         simple-account-manager.h \
         simple-conn.c \
         simple-conn.h \
-        textchan-null.c \
-        textchan-null.h \
+        echo-chan.c \
+        echo-chan.h \
         util.c \
         util.h \
        $(NULL)
@@ -84,7 +84,7 @@ tp-test-contactlist.vapi: tp-test-contactlist.gir tp-test-contactlist.deps
        @test x$(srcdir) = x. || cp $(srcdir)/tp-test-contactlist.deps .
        $(MAKE) $(AM_MAKEFLAGS) tp-test-contactlist.gir
        $(AM_V_GEN)$(VAPIGEN) $(VAPIGENFLAGS) --library tp-test-contactlist \
-               tp-test-contactlist.gir
+               tp-test-contactlist.gir && sed -i "s/Tp\./TelepathyGLib\./g" $@
        touch $@
 endif
 
diff --git a/tests/lib/telepathy/contactlist/backend.c b/tests/lib/telepathy/contactlist/backend.c
index b7193dc..243b4a6 100644
--- a/tests/lib/telepathy/contactlist/backend.c
+++ b/tests/lib/telepathy/contactlist/backend.c
@@ -20,9 +20,8 @@
 
 #include <config.h>
 #include <glib.h>
-#include <telepathy-glib/base-connection.h>
-#include <telepathy-glib/dbus.h>
-#include <telepathy-glib/svc-account.h>
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
 
 #include "simple-account.h"
 #include "simple-account-manager.h"
@@ -133,7 +132,7 @@ void
 tp_tests_backend_set_up (TpTestsBackend *self)
 {
   TpTestsBackendPrivate *priv = self->priv;
-  TpSimpleClientFactory *factory;
+  TpClientFactory *factory;
   GError *error = NULL;
 
   /* Override the handler set in the general Folks.TestCase class */
@@ -160,7 +159,7 @@ tp_tests_backend_set_up (TpTestsBackend *self)
 
   priv->client_am = tp_account_manager_dup ();
   factory = tp_proxy_get_factory (priv->client_am);
-  tp_simple_client_factory_add_contact_features_varargs (factory,
+  tp_client_factory_add_contact_features_varargs (factory,
       TP_CONTACT_FEATURE_ALIAS,
       TP_CONTACT_FEATURE_AVATAR_DATA,
       TP_CONTACT_FEATURE_AVATAR_TOKEN,
@@ -169,7 +168,7 @@ tp_tests_backend_set_up (TpTestsBackend *self)
       TP_CONTACT_FEATURE_PRESENCE,
       TP_CONTACT_FEATURE_CONTACT_INFO,
       TP_CONTACT_FEATURE_CONTACT_GROUPS,
-      TP_CONTACT_FEATURE_INVALID);
+      0);
 }
 
 static void
@@ -268,7 +267,7 @@ tp_tests_backend_add_account (TpTestsBackend *self,
     const gchar *account)
 {
   TpTestsBackendPrivate *priv = self->priv;
-  TpSimpleClientFactory *factory;
+  TpClientFactory *factory;
   AccountData *data;
   gchar *conn_path;
   GError *error = NULL;
@@ -286,7 +285,7 @@ tp_tests_backend_add_account (TpTestsBackend *self,
   g_assert_no_error (error);
 
   factory = tp_proxy_get_factory (priv->client_am);
-  data->client_conn = tp_simple_client_factory_ensure_connection (factory,
+  data->client_conn = tp_client_factory_ensure_connection (factory,
       conn_path, NULL, &error);
   g_assert_no_error (error);
 
diff --git a/tests/lib/telepathy/contactlist/broken-client-types-conn.c 
b/tests/lib/telepathy/contactlist/broken-client-types-conn.c
new file mode 100644
index 0000000..0ffd992
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/broken-client-types-conn.c
@@ -0,0 +1,65 @@
+/*
+ * broken-client-types-conn.c - a connection with a broken client
+ *   types implementation which inexplicably returns presence information!
+ *
+ * Copyright © 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "config.h"
+
+#include "broken-client-types-conn.h"
+
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
+
+G_DEFINE_TYPE_WITH_CODE (TpTestsBrokenClientTypesConnection,
+    tp_tests_broken_client_types_connection,
+    TP_TESTS_TYPE_CONTACTS_CONNECTION,
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CLIENT_TYPES,
+        NULL);
+    );
+
+static void
+tp_tests_broken_client_types_connection_init (
+    TpTestsBrokenClientTypesConnection *self)
+{
+}
+
+static void
+broken_fill_client_types (
+    GObject *object,
+    const GArray *contacts,
+    GHashTable *attributes)
+{
+  guint i;
+
+  for (i = 0; i < contacts->len; i++)
+    {
+      TpHandle handle = g_array_index (contacts, guint, i);
+      /* Muahaha. Actually we add Presence information. */
+      GValueArray *presence = tp_value_array_build (3,
+          G_TYPE_UINT, TP_CONNECTION_PRESENCE_TYPE_AVAILABLE,
+          G_TYPE_STRING, "available",
+          G_TYPE_STRING, "hi mum!",
+          G_TYPE_INVALID);
+
+      tp_contacts_mixin_set_contact_attribute (attributes,
+          handle,
+          TP_TOKEN_CONNECTION_INTERFACE_PRESENCE_PRESENCE,
+          tp_g_value_slice_new_take_boxed (G_TYPE_VALUE_ARRAY, presence));
+    }
+}
+
+static void
+tp_tests_broken_client_types_connection_class_init (
+    TpTestsBrokenClientTypesConnectionClass *klass)
+{
+  TpTestsContactsConnectionClass *cc_class =
+      TP_TESTS_CONTACTS_CONNECTION_CLASS (klass);
+
+  cc_class->fill_client_types = broken_fill_client_types;
+}
diff --git a/tests/lib/telepathy/contactlist/broken-client-types-conn.h 
b/tests/lib/telepathy/contactlist/broken-client-types-conn.h
new file mode 100644
index 0000000..ad36d7d
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/broken-client-types-conn.h
@@ -0,0 +1,50 @@
+/*
+ * broken-client-types-conn.h - header for a connection with a broken client
+ *   types implementation which inexplicably returns presence information!
+ *
+ * Copyright © 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef TP_TESTS_BROKEN_CLIENT_TYPES_CONN_H
+#define TP_TESTS_BROKEN_CLIENT_TYPES_CONN_H
+
+#include "contacts-conn.h"
+
+typedef struct _TpTestsBrokenClientTypesConnection TpTestsBrokenClientTypesConnection;
+typedef struct _TpTestsBrokenClientTypesConnectionClass TpTestsBrokenClientTypesConnectionClass;
+typedef struct _TpTestsBrokenClientTypesConnectionPrivate TpTestsBrokenClientTypesConnectionPrivate;
+
+struct _TpTestsBrokenClientTypesConnectionClass {
+    TpTestsContactsConnectionClass parent_class;
+};
+
+struct _TpTestsBrokenClientTypesConnection {
+    TpTestsContactsConnection parent;
+
+    TpTestsBrokenClientTypesConnectionPrivate *priv;
+};
+
+GType tp_tests_broken_client_types_connection_get_type (void);
+
+/* HI MUM */
+#define TP_TESTS_TYPE_BROKEN_CLIENT_TYPES_CONNECTION \
+  (tp_tests_broken_client_types_connection_get_type ())
+#define TP_TESTS_BROKEN_CLIENT_TYPES_CONNECTION(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_BROKEN_CLIENT_TYPES_CONNECTION, \
+                              TpTestsBrokenClientTypesConnection))
+#define TP_TESTS_BROKEN_CLIENT_TYPES_CONNECTION_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_BROKEN_CLIENT_TYPES_CONNECTION, \
+                           TpTestsBrokenClientTypesConnectionClass))
+#define TP_TESTS_IS_BROKEN_CLIENT_TYPES_CONNECTION(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_BROKEN_CLIENT_TYPES_CONNECTION))
+#define TP_TESTS_IS_BROKEN_CLIENT_TYPES_CONNECTION_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_BROKEN_CLIENT_TYPES_CONNECTION))
+#define TP_TESTS_BROKEN_CLIENT_TYPES_CONNECTION_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_BROKEN_CLIENT_TYPES_CONNECTION, \
+                              TpTestsBrokenClientTypesConnectionClass))
+
+#endif // TP_TESTS_BROKEN_CLIENT_TYPES_CONN_H
diff --git a/tests/lib/telepathy/contactlist/bug-19101-conn.c 
b/tests/lib/telepathy/contactlist/bug-19101-conn.c
new file mode 100644
index 0000000..52a4b68
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/bug-19101-conn.c
@@ -0,0 +1,74 @@
+/*
+ * bug-19101-conn.c - a broken connection to reproduce bug #19101
+ *
+ * Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "config.h"
+
+#include "bug-19101-conn.h"
+
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
+
+#include "debug.h"
+
+static void contacts_iface_init (gpointer g_iface, gpointer iface_data);
+
+G_DEFINE_TYPE_WITH_CODE (TpTestsBug19101Connection,
+    tp_tests_bug19101_connection, TP_TESTS_TYPE_CONTACTS_CONNECTION,
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACTS,
+        contacts_iface_init);
+    )
+
+static void
+tp_tests_bug19101_connection_init (TpTestsBug19101Connection *self)
+{
+}
+
+static void
+tp_tests_bug19101_connection_class_init (TpTestsBug19101ConnectionClass *klass)
+{
+}
+
+/* A broken implementation of GetContactByID, which returns an empty dict
+ * of attributes for each id.
+ */
+static void
+tp_tests_bug19101_connection_get_contact_by_id (
+    TpSvcConnectionInterfaceContacts *iface,
+    const gchar *id,
+    const char **interfaces,
+    DBusGMethodInvocation *context)
+{
+  TpBaseConnection *base_conn = TP_BASE_CONNECTION (iface);
+  TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
+      base_conn, TP_HANDLE_TYPE_CONTACT);
+  TpHandle handle;
+  GHashTable *table;
+
+  handle = tp_handle_ensure (contact_repo, id, NULL, NULL);
+  table = g_hash_table_new (NULL, NULL);
+
+  tp_svc_connection_interface_contacts_return_from_get_contact_by_id (
+      context, handle, table);
+
+  g_hash_table_unref (table);
+}
+
+static void
+contacts_iface_init (gpointer g_iface, gpointer iface_data)
+{
+  TpSvcConnectionInterfaceContactsClass *klass =
+    (TpSvcConnectionInterfaceContactsClass *) g_iface;
+
+#define IMPLEMENT(x) tp_svc_connection_interface_contacts_implement_##x ( \
+    klass, tp_tests_bug19101_connection_##x)
+  IMPLEMENT(get_contact_by_id);
+#undef IMPLEMENT
+}
diff --git a/tests/lib/telepathy/contactlist/bug-19101-conn.h 
b/tests/lib/telepathy/contactlist/bug-19101-conn.h
new file mode 100644
index 0000000..23b670e
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/bug-19101-conn.h
@@ -0,0 +1,51 @@
+/*
+ * bug-19101-conn.h - header for a broken connection to reproduce bug #19101
+ *
+ * Copyright (C) 2007-2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2007-2008 Nokia Corporation
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef __TP_TESTS_BUG19101_CONN_H__
+#define __TP_TESTS_BUG19101_CONN_H__
+
+#include "contacts-conn.h"
+
+G_BEGIN_DECLS
+
+typedef struct _TpTestsBug19101Connection TpTestsBug19101Connection;
+typedef struct _TpTestsBug19101ConnectionClass TpTestsBug19101ConnectionClass;
+
+struct _TpTestsBug19101ConnectionClass {
+    TpTestsContactsConnectionClass parent_class;
+};
+
+struct _TpTestsBug19101Connection {
+    TpTestsContactsConnection parent;
+};
+
+GType tp_tests_bug19101_connection_get_type (void);
+
+/* TYPE MACROS */
+#define TP_TESTS_TYPE_BUG19101_CONNECTION \
+  (tp_tests_bug19101_connection_get_type ())
+#define BUG_19101_CONNECTION(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_BUG19101_CONNECTION, \
+                              TpTestsBug19101Connection))
+#define BUG_19101_CONNECTION_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_BUG19101_CONNECTION, \
+                           TpTestsBug19101ConnectionClass))
+#define BUG_19101_IS_CONNECTION(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_BUG19101_CONNECTION))
+#define BUG_19101_IS_CONNECTION_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_BUG19101_CONNECTION))
+#define BUG_19101_CONNECTION_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_BUG19101_CONNECTION, \
+                              TpTestsBug19101ConnectionClass))
+
+G_END_DECLS
+
+#endif /* #ifndef __TP_TESTS_BUG19101_CONN_H__ */
diff --git a/tests/lib/telepathy/contactlist/contact-list-manager.c 
b/tests/lib/telepathy/contactlist/contact-list-manager.c
index b816673..bce8e21 100644
--- a/tests/lib/telepathy/contactlist/contact-list-manager.c
+++ b/tests/lib/telepathy/contactlist/contact-list-manager.c
@@ -26,8 +26,7 @@ struct _TpTestsContactListManagerPrivate
   GHashTable *contact_details;
 
   TpHandleRepoIface *contact_repo;
-  TpHandleRepoIface *group_repo;
-  TpHandleSet *groups;
+  GHashTable *groups;
 };
 
 static void contact_groups_iface_init (TpContactGroupListInterface *iface);
@@ -49,7 +48,7 @@ typedef struct {
   TpSubscriptionState subscribe;
   TpSubscriptionState publish;
   gchar *publish_request;
-  TpHandleSet *groups;
+  GHashTable *groups;
 
   TpHandle handle;
   TpHandleRepoIface *contact_repo;
@@ -61,7 +60,7 @@ contact_detail_destroy (gpointer p)
   ContactDetails *d = p;
 
   g_free (d->publish_request);
-  tp_handle_set_destroy (d->groups);
+  g_hash_table_unref (d->groups);
 
   g_slice_free (ContactDetails, d);
 }
@@ -86,7 +85,7 @@ ensure_contact (TpTestsContactListManager *self,
       d->subscribe = TP_SUBSCRIPTION_STATE_NO;
       d->publish = TP_SUBSCRIPTION_STATE_NO;
       d->publish_request = NULL;
-      d->groups = tp_handle_set_new (self->priv->group_repo);
+      d->groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
       d->handle = handle;
       d->contact_repo = self->priv->contact_repo;
 
@@ -117,7 +116,7 @@ close_all (TpTestsContactListManager *self)
       self->priv->status_changed_id = 0;
     }
   tp_clear_pointer (&self->priv->contact_details, g_hash_table_unref);
-  tp_clear_pointer (&self->priv->groups, tp_handle_set_destroy);
+  tp_clear_pointer (&self->priv->groups, g_hash_table_unref);
 }
 
 static void
@@ -197,16 +196,15 @@ contact_list_dup_groups (TpBaseContactList *base)
 
   if (self->priv->groups != NULL)
     {
-      TpIntsetFastIter iter;
-      TpHandle group;
+      GHashTableIter iter;
+      gpointer name;
 
-      ret = g_ptr_array_sized_new (tp_handle_set_size (self->priv->groups) + 1);
+      ret = g_ptr_array_sized_new (g_hash_table_size (self->priv->groups) + 1);
 
-      tp_intset_fast_iter_init (&iter, tp_handle_set_peek (self->priv->groups));
-      while (tp_intset_fast_iter_next (&iter, &group))
+      g_hash_table_iter_init (&iter, self->priv->groups);
+      while (g_hash_table_iter_next (&iter, &name, NULL))
         {
-          g_ptr_array_add (ret, g_strdup (tp_handle_inspect (
-              self->priv->group_repo, group)));
+          g_ptr_array_add (ret, g_strdup (name));
         }
     }
   else
@@ -229,16 +227,15 @@ contact_list_dup_contact_groups (TpBaseContactList *base,
 
   if (d != NULL && d->groups != NULL)
     {
-      TpIntsetFastIter iter;
-      TpHandle group;
+      GHashTableIter iter;
+      gpointer name;
 
-      ret = g_ptr_array_sized_new (tp_handle_set_size (d->groups) + 1);
+      ret = g_ptr_array_sized_new (g_hash_table_size (d->groups) + 1);
 
-      tp_intset_fast_iter_init (&iter, tp_handle_set_peek (d->groups));
-      while (tp_intset_fast_iter_next (&iter, &group))
+      g_hash_table_iter_init (&iter, d->groups);
+      while (g_hash_table_iter_next (&iter, &name, NULL))
         {
-          g_ptr_array_add (ret, g_strdup (tp_handle_inspect (
-              self->priv->group_repo, group)));
+          g_ptr_array_add (ret, g_strdup (name));
         }
     }
   else
@@ -256,14 +253,13 @@ contact_list_dup_group_members (TpBaseContactList *base,
     const gchar *group)
 {
   TpTestsContactListManager *self = TP_TESTS_CONTACT_LIST_MANAGER (base);
-  TpHandleSet *set;
-  TpHandle group_handle;
   GHashTableIter iter;
   gpointer k, v;
+  TpHandleSet *set;
 
   set = tp_handle_set_new (self->priv->contact_repo);
-  group_handle = tp_handle_lookup (self->priv->group_repo, group, NULL, NULL);
-  if (G_UNLIKELY (group_handle == 0))
+
+  if (G_UNLIKELY (g_hash_table_lookup (self->priv->groups, group) == NULL))
     {
       /* clearly it doesn't have members */
       return set;
@@ -275,13 +271,31 @@ contact_list_dup_group_members (TpBaseContactList *base,
       ContactDetails *d = v;
 
       if (d->groups != NULL &&
-          tp_handle_set_is_member (d->groups, group_handle))
+          g_hash_table_lookup (d->groups, group) != NULL)
         tp_handle_set_add (set, GPOINTER_TO_UINT (k));
     }
 
   return set;
 }
 
+static GPtrArray *
+group_difference (GHashTable *left,
+    GHashTable *right)
+{
+  GHashTableIter iter;
+  GPtrArray *set = g_ptr_array_sized_new (g_hash_table_size (left));
+  gpointer name;
+
+  g_hash_table_iter_init (&iter, left);
+  while (g_hash_table_iter_next (&iter, &name, NULL))
+    {
+      if (g_hash_table_lookup (right, name) == NULL)
+        g_ptr_array_add (set, name);
+    }
+
+  return set;
+}
+
 static void
 contact_list_set_contact_groups_async (TpBaseContactList *base,
     TpHandle contact,
@@ -292,25 +306,24 @@ contact_list_set_contact_groups_async (TpBaseContactList *base,
 {
   TpTestsContactListManager *self = TP_TESTS_CONTACT_LIST_MANAGER (base);
   ContactDetails *d;
-  TpIntset *set, *added_set, *removed_set;
-  GPtrArray *added_names, *removed_names;
+  GHashTable *tmp;
+  GPtrArray *added, *removed;
   GPtrArray *new_groups;
-  TpIntsetFastIter iter;
-  TpHandle group_handle;
   guint i;
 
   d = ensure_contact (self, contact);
 
   new_groups = g_ptr_array_new ();
-  set = tp_intset_new ();
+  /* make a hash table so we only have one difference function */
+  tmp = g_hash_table_new (g_str_hash, g_str_equal);
   for (i = 0; i < n; i++)
     {
-      group_handle = tp_handle_ensure (self->priv->group_repo, names[i], NULL, NULL);
-      tp_intset_add (set, group_handle);
+      g_hash_table_insert (tmp, (gpointer) names[i], GUINT_TO_POINTER (1));
 
-      if (!tp_handle_set_is_member (self->priv->groups, group_handle))
+      if (g_hash_table_lookup (self->priv->groups, names[i]) == NULL)
         {
-          tp_handle_set_add (self->priv->groups, group_handle);
+          g_hash_table_insert (self->priv->groups, g_strdup (names[i]),
+              GUINT_TO_POINTER (1));
           g_ptr_array_add (new_groups, (gchar *) names[i]);
         }
     }
@@ -321,42 +334,30 @@ contact_list_set_contact_groups_async (TpBaseContactList *base,
           (const gchar * const *) new_groups->pdata, new_groups->len);
     }
 
-  added_set = tp_intset_difference (set, tp_handle_set_peek (d->groups));
-  added_names = g_ptr_array_sized_new (tp_intset_size (added_set));
-  tp_intset_fast_iter_init (&iter, added_set);
-  while (tp_intset_fast_iter_next (&iter, &group_handle))
-    {
-      g_ptr_array_add (added_names, (gchar *) tp_handle_inspect (
-          self->priv->group_repo, group_handle));
-    }
-  tp_intset_destroy (added_set);
+  /* see which groups were added and which were removed */
+  added = group_difference (tmp, d->groups);
+  removed = group_difference (d->groups, tmp);
 
-  removed_set = tp_intset_difference (tp_handle_set_peek (d->groups), set);
-  removed_names = g_ptr_array_sized_new (tp_intset_size (removed_set));
-  tp_intset_fast_iter_init (&iter, removed_set);
-  while (tp_intset_fast_iter_next (&iter, &group_handle))
-    {
-      g_ptr_array_add (removed_names, (gchar *) tp_handle_inspect (
-          self->priv->group_repo, group_handle));
-    }
-  tp_intset_destroy (removed_set);
+  g_hash_table_unref (tmp);
 
-  tp_handle_set_destroy (d->groups);
-  d->groups = tp_handle_set_new_from_intset (self->priv->group_repo, set);
-  tp_intset_destroy (set);
+  /* update the list of groups the contact thinks it has */
+  g_hash_table_remove_all (d->groups);
+  for (i = 0; i < n; i++)
+    g_hash_table_insert (d->groups, g_strdup (names[i]), GUINT_TO_POINTER (1));
 
-  if (added_names->len > 0 || removed_names->len > 0)
+  /* signal the change */
+  if (added->len > 0 || removed->len > 0)
     {
       tp_base_contact_list_one_contact_groups_changed (base, contact,
-          (const gchar * const *) added_names->pdata, added_names->len,
-          (const gchar * const *) removed_names->pdata, removed_names->len);
+          (const gchar * const *) added->pdata, added->len,
+          (const gchar * const *) removed->pdata, removed->len);
     }
 
   tp_simple_async_report_success_in_idle ((GObject *) self, callback,
       user_data, contact_list_set_contact_groups_async);
 
-  g_ptr_array_unref (added_names);
-  g_ptr_array_unref (removed_names);
+  g_ptr_array_unref (added);
+  g_ptr_array_unref (removed);
   g_ptr_array_unref (new_groups);
 }
 
@@ -551,9 +552,8 @@ constructed (GObject *object)
 
   self->priv->contact_repo = tp_base_connection_get_handles (self->priv->conn,
       TP_HANDLE_TYPE_CONTACT);
-  self->priv->group_repo = tp_base_connection_get_handles (self->priv->conn,
-      TP_HANDLE_TYPE_GROUP);
-  self->priv->groups = tp_handle_set_new (self->priv->group_repo);
+  self->priv->groups = g_hash_table_new_full (g_str_hash, g_str_equal,
+      g_free, NULL);
 }
 
 static void
@@ -606,19 +606,17 @@ tp_tests_contact_list_manager_add_to_group (TpTestsContactListManager *self,
 {
   TpBaseContactList *base = TP_BASE_CONTACT_LIST (self);
   ContactDetails *d = ensure_contact (self, member);
-  TpHandle group_handle;
 
-  group_handle = tp_handle_ensure (self->priv->group_repo,
-      group_name, NULL, NULL);
+  g_hash_table_insert (d->groups, g_strdup (group_name), GUINT_TO_POINTER (1));
 
-  if (!tp_handle_set_is_member (self->priv->groups, group_handle))
+  if (g_hash_table_lookup (self->priv->groups, group_name) == NULL)
     {
-      tp_handle_set_add (self->priv->groups, group_handle);
+      g_hash_table_insert (self->priv->groups, g_strdup (group_name),
+          GUINT_TO_POINTER (1));
       tp_base_contact_list_groups_created ((TpBaseContactList *) self,
           &group_name, 1);
     }
 
-  tp_handle_set_add (d->groups, group_handle);
   tp_base_contact_list_one_contact_groups_changed (base, member,
       &group_name, 1, NULL, 0);
 }
@@ -629,14 +627,12 @@ tp_tests_contact_list_manager_remove_from_group (TpTestsContactListManager *self
 {
   TpBaseContactList *base = TP_BASE_CONTACT_LIST (self);
   ContactDetails *d = lookup_contact (self, member);
-  TpHandle group_handle;
 
   if (d == NULL)
     return;
 
-  group_handle = tp_handle_ensure (self->priv->group_repo, group_name, NULL, NULL);
+  g_hash_table_remove (d->groups, group_name);
 
-  tp_handle_set_remove (d->groups, group_handle);
   tp_base_contact_list_one_contact_groups_changed (base, member,
       NULL, 0, &group_name, 1);
 }
diff --git a/tests/lib/telepathy/contactlist/contact-list-manager.h 
b/tests/lib/telepathy/contactlist/contact-list-manager.h
index 8be787c..bc44863 100644
--- a/tests/lib/telepathy/contactlist/contact-list-manager.h
+++ b/tests/lib/telepathy/contactlist/contact-list-manager.h
@@ -12,7 +12,7 @@
 #ifndef __TP_TESTS_CONTACT_LIST_MANAGER_H__
 #define __TP_TESTS_CONTACT_LIST_MANAGER_H__
 
-#include <telepathy-glib/base-contact-list.h>
+#include <telepathy-glib/telepathy-glib.h>
 
 G_BEGIN_DECLS
 
diff --git a/tests/lib/telepathy/contactlist/contacts-conn.c b/tests/lib/telepathy/contactlist/contacts-conn.c
index 6f30880..3ba50a2 100644
--- a/tests/lib/telepathy/contactlist/contacts-conn.c
+++ b/tests/lib/telepathy/contactlist/contacts-conn.c
@@ -15,19 +15,13 @@
 
 #include <dbus/dbus-glib.h>
 
-#include <telepathy-glib/interfaces.h>
-#include <telepathy-glib/dbus.h>
-#include <telepathy-glib/errors.h>
-#include <telepathy-glib/gtypes.h>
-#include <telepathy-glib/handle-repo-dynamic.h>
-#include <telepathy-glib/util.h>
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
 
 #include "debug.h"
 
 static void init_aliasing (gpointer, gpointer);
 static void init_avatars (gpointer, gpointer);
-static void init_location (gpointer, gpointer);
-static void init_contact_caps (gpointer, gpointer);
 static void init_contact_info (gpointer, gpointer);
 static void conn_avatars_properties_getter (GObject *object, GQuark interface,
     GQuark name, GValue *value, gpointer getter_data);
@@ -41,13 +35,9 @@ G_DEFINE_TYPE_WITH_CODE (TpTestsContactsConnection,
       init_avatars);
     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_PRESENCE,
       tp_presence_mixin_iface_init);
-    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
-      tp_presence_mixin_simple_presence_iface_init)
-    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_LOCATION,
-      init_location)
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_LOCATION, NULL)
     G_IMPLEMENT_INTERFACE (
-      TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_CAPABILITIES,
-      init_contact_caps)
+      TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_CAPABILITIES, NULL)
     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_INFO,
       init_contact_info)
     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACTS,
@@ -402,11 +392,17 @@ constructed (GObject *object)
   if (parent_impl != NULL)
     parent_impl (object);
 
+  self->priv->list_manager = g_object_new (TP_TESTS_TYPE_CONTACT_LIST_MANAGER,
+      "connection", self, NULL);
+
   tp_contacts_mixin_init (object,
       G_STRUCT_OFFSET (TpTestsContactsConnection, contacts_mixin));
   tp_base_connection_register_with_contacts_mixin (base);
   if (self->priv->list_manager)
-    tp_base_contact_list_mixin_register_with_contacts_mixin (base);
+    {
+      tp_base_contact_list_mixin_register_with_contacts_mixin (
+          TP_BASE_CONTACT_LIST (self->priv->list_manager), base);
+    }
   tp_contacts_mixin_add_contact_attributes_iface (object,
       TP_IFACE_CONNECTION_INTERFACE_ALIASING,
       aliasing_fill_contact_attributes);
@@ -428,7 +424,7 @@ constructed (GObject *object)
 
   tp_presence_mixin_init (object,
       G_STRUCT_OFFSET (TpTestsContactsConnection, presence_mixin));
-  tp_presence_mixin_simple_presence_register_with_contacts_mixin (object);
+  tp_presence_mixin_register_with_contacts_mixin (object);
 }
 
 static const TpPresenceStatusOptionalArgumentSpec can_have_message[] = {
@@ -459,8 +455,7 @@ my_status_available (GObject *object,
 
 static GHashTable *
 my_get_contact_statuses (GObject *object,
-                         const GArray *contacts,
-                         GError **error)
+                         const GArray *contacts)
 {
   TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object);
   GHashTable *result = g_hash_table_new_full (g_direct_hash, g_direct_equal,
@@ -529,24 +524,13 @@ my_get_maximum_status_message_length_cb (GObject *obj)
 static GPtrArray *
 create_channel_managers (TpBaseConnection *conn)
 {
-  TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (conn);
-  GPtrArray *ret = g_ptr_array_sized_new (1);
-
-  self->priv->list_manager = g_object_new (TP_TESTS_TYPE_CONTACT_LIST_MANAGER,
-      "connection", conn, NULL);
-
-  g_ptr_array_add (ret, self->priv->list_manager);
-
-  return ret;
+  return g_ptr_array_new ();
 }
 
-static void
-tp_tests_contacts_connection_class_init (TpTestsContactsConnectionClass *klass)
+static GPtrArray *
+tp_tests_contacts_get_interfaces_always_present (TpBaseConnection *base)
 {
-  TpBaseConnectionClass *base_class =
-      (TpBaseConnectionClass *) klass;
-  GObjectClass *object_class = (GObjectClass *) klass;
-  TpPresenceMixinClass *mixin_class;
+  GPtrArray *interfaces;
   static const gchar *interfaces_always_present[] = {
       TP_IFACE_CONNECTION_INTERFACE_ALIASING,
       TP_IFACE_CONNECTION_INTERFACE_AVATARS,
@@ -554,13 +538,56 @@ tp_tests_contacts_connection_class_init (TpTestsContactsConnectionClass *klass)
       TP_IFACE_CONNECTION_INTERFACE_CONTACT_LIST,
       TP_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS,
       TP_IFACE_CONNECTION_INTERFACE_PRESENCE,
-      TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
       TP_IFACE_CONNECTION_INTERFACE_LOCATION,
       TP_IFACE_CONNECTION_INTERFACE_CLIENT_TYPES,
       TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES,
       TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO,
-      TP_IFACE_CONNECTION_INTERFACE_REQUESTS,
       NULL };
+  guint i;
+
+  interfaces = TP_BASE_CONNECTION_CLASS (
+      tp_tests_contacts_connection_parent_class)->get_interfaces_always_present (base);
+
+  for (i = 0; interfaces_always_present[i] != NULL; i++)
+    g_ptr_array_add (interfaces, (gchar *) interfaces_always_present[i]);
+
+  return interfaces;
+}
+
+enum
+{
+  ALIASING_DP_ALIAS_FLAGS,
+};
+
+static void
+aliasing_get_dbus_property (GObject *object,
+    GQuark interface,
+    GQuark name,
+    GValue *value,
+    gpointer user_data)
+{
+  switch (GPOINTER_TO_UINT (user_data))
+    {
+    case ALIASING_DP_ALIAS_FLAGS:
+      g_value_set_uint (value, TP_CONNECTION_ALIAS_FLAG_USER_SET);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static void
+tp_tests_contacts_connection_class_init (TpTestsContactsConnectionClass *klass)
+{
+  TpBaseConnectionClass *base_class =
+      (TpBaseConnectionClass *) klass;
+  GObjectClass *object_class = (GObjectClass *) klass;
+  TpPresenceMixinClass *mixin_class;
+  static TpDBusPropertiesMixinPropImpl aliasing_props[] = {
+    { "AliasFlags", GUINT_TO_POINTER (ALIASING_DP_ALIAS_FLAGS), NULL },
+    { NULL }
+  };
   static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
         { TP_IFACE_CONNECTION_INTERFACE_AVATARS,
           conn_avatars_properties_getter,
@@ -572,6 +599,11 @@ tp_tests_contacts_connection_class_init (TpTestsContactsConnectionClass *klass)
           NULL,
           conn_contact_info_properties,
         },
+        { TP_IFACE_CONNECTION_INTERFACE_ALIASING,
+          aliasing_get_dbus_property,
+          NULL,
+          aliasing_props,
+        },
         { NULL }
   };
 
@@ -579,7 +611,7 @@ tp_tests_contacts_connection_class_init (TpTestsContactsConnectionClass *klass)
   object_class->finalize = finalize;
   g_type_class_add_private (klass, sizeof (TpTestsContactsConnectionPrivate));
 
-  base_class->interfaces_always_present = interfaces_always_present;
+  base_class->get_interfaces_always_present = tp_tests_contacts_get_interfaces_always_present;
   base_class->create_channel_managers = create_channel_managers;
 
   tp_contacts_mixin_class_init (object_class,
@@ -593,7 +625,7 @@ tp_tests_contacts_connection_class_init (TpTestsContactsConnectionClass *klass)
   mixin_class->get_maximum_status_message_length =
       my_get_maximum_status_message_length_cb;
 
-  tp_presence_mixin_simple_presence_init_dbus_properties (object_class);
+  tp_presence_mixin_init_dbus_properties (object_class);
 
   klass->properties_class.interfaces = prop_interfaces;
   tp_dbus_properties_mixin_class_init (object_class,
@@ -623,34 +655,23 @@ tp_tests_contacts_connection_change_aliases (TpTestsContactsConnection *self,
                                     const TpHandle *handles,
                                     const gchar * const *aliases)
 {
-  GPtrArray *structs = g_ptr_array_sized_new (n);
+  GHashTable *changes = g_hash_table_new (NULL, NULL);
   guint i;
 
   for (i = 0; i < n; i++)
     {
-      GValueArray *pair = g_value_array_new (2);
-
       DEBUG ("contact#%u -> %s", handles[i], aliases[i]);
 
       g_hash_table_insert (self->priv->aliases,
           GUINT_TO_POINTER (handles[i]), g_strdup (aliases[i]));
 
-      g_value_array_append (pair, NULL);
-      g_value_init (pair->values + 0, G_TYPE_UINT);
-      g_value_set_uint (pair->values + 0, handles[i]);
-
-      g_value_array_append (pair, NULL);
-      g_value_init (pair->values + 1, G_TYPE_STRING);
-      g_value_set_string (pair->values + 1, aliases[i]);
-
-      g_ptr_array_add (structs, pair);
+      g_hash_table_insert (changes,
+          GUINT_TO_POINTER (handles[i]), (gchar *) aliases[i]);
     }
 
-  tp_svc_connection_interface_aliasing_emit_aliases_changed (self,
-      structs);
+  tp_svc_connection_interface_aliasing_emit_aliases_changed (self, changes);
 
-  g_ptr_array_foreach (structs, (GFunc) g_value_array_free, NULL);
-  g_ptr_array_unref (structs);
+  g_hash_table_unref (changes);
 }
 
 void
@@ -793,60 +814,6 @@ tp_tests_contacts_connection_set_default_contact_info (
 }
 
 static void
-my_get_alias_flags (TpSvcConnectionInterfaceAliasing *aliasing,
-                    DBusGMethodInvocation *context)
-{
-  TpBaseConnection *base = TP_BASE_CONNECTION (aliasing);
-
-  TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
-  tp_svc_connection_interface_aliasing_return_from_get_alias_flags (context,
-      TP_CONNECTION_ALIAS_FLAG_USER_SET);
-}
-
-static void
-my_get_aliases (TpSvcConnectionInterfaceAliasing *aliasing,
-                const GArray *contacts,
-                DBusGMethodInvocation *context)
-{
-  TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (aliasing);
-  TpBaseConnection *base = TP_BASE_CONNECTION (aliasing);
-  TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
-      TP_HANDLE_TYPE_CONTACT);
-  GHashTable *result;
-  GError *error = NULL;
-  guint i;
-
-  TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
-
-  if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error))
-    {
-      dbus_g_method_return_error (context, error);
-      g_error_free (error);
-      return;
-    }
-
-  result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
-
-  for (i = 0; i < contacts->len; i++)
-    {
-      TpHandle handle = g_array_index (contacts, TpHandle, i);
-      const gchar *alias = g_hash_table_lookup (self->priv->aliases,
-          GUINT_TO_POINTER (handle));
-
-      if (alias == NULL)
-        g_hash_table_insert (result, GUINT_TO_POINTER (handle),
-            (gchar *) tp_handle_inspect (contact_repo, handle));
-      else
-        g_hash_table_insert (result, GUINT_TO_POINTER (handle),
-            (gchar *) alias);
-    }
-
-  tp_svc_connection_interface_aliasing_return_from_get_aliases (context,
-      result);
-  g_hash_table_unref (result);
-}
-
-static void
 my_request_aliases (TpSvcConnectionInterfaceAliasing *aliasing,
                     const GArray *contacts,
                     DBusGMethodInvocation *context)
@@ -950,105 +917,12 @@ init_aliasing (gpointer g_iface,
 
 #define IMPLEMENT(x) tp_svc_connection_interface_aliasing_implement_##x (\
     klass, my_##x)
-  IMPLEMENT(get_alias_flags);
   IMPLEMENT(request_aliases);
-  IMPLEMENT(get_aliases);
   IMPLEMENT(set_aliases);
 #undef IMPLEMENT
 }
 
 static void
-my_get_avatar_tokens (TpSvcConnectionInterfaceAvatars *avatars,
-                      const GArray *contacts,
-                      DBusGMethodInvocation *context)
-{
-  TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (avatars);
-  TpBaseConnection *base = TP_BASE_CONNECTION (avatars);
-  TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
-      TP_HANDLE_TYPE_CONTACT);
-  GError *error = NULL;
-  GHashTable *result;
-  guint i;
-
-  TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
-
-  if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error))
-    {
-      dbus_g_method_return_error (context, error);
-      g_error_free (error);
-      return;
-    }
-
-  result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
-
-  for (i = 0; i < contacts->len; i++)
-    {
-      TpHandle handle = g_array_index (contacts, TpHandle, i);
-      AvatarData *a = g_hash_table_lookup (self->priv->avatars,
-          GUINT_TO_POINTER (handle));
-
-      if (a == NULL || a->token == NULL)
-        {
-          /* we're expected to do a round-trip to the server to find out
-           * their token, so we have to give some sort of result. Assume
-           * no avatar, here */
-          a = avatar_data_new (NULL, NULL, "");
-          g_hash_table_insert (self->priv->avatars,
-              GUINT_TO_POINTER (handle), a);
-          tp_svc_connection_interface_avatars_emit_avatar_updated (self,
-              handle, a->token);
-        }
-
-      g_hash_table_insert (result, GUINT_TO_POINTER (handle),
-          a->token);
-    }
-
-  tp_svc_connection_interface_avatars_return_from_get_known_avatar_tokens (
-      context, result);
-  g_hash_table_unref (result);
-}
-
-static void
-my_get_known_avatar_tokens (TpSvcConnectionInterfaceAvatars *avatars,
-                            const GArray *contacts,
-                            DBusGMethodInvocation *context)
-{
-  TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (avatars);
-  TpBaseConnection *base = TP_BASE_CONNECTION (avatars);
-  TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
-      TP_HANDLE_TYPE_CONTACT);
-  GError *error = NULL;
-  GHashTable *result;
-  guint i;
-
-  TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
-
-  if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error))
-    {
-      dbus_g_method_return_error (context, error);
-      g_error_free (error);
-      return;
-    }
-
-  result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
-
-  for (i = 0; i < contacts->len; i++)
-    {
-      TpHandle handle = g_array_index (contacts, TpHandle, i);
-      AvatarData *a = g_hash_table_lookup (self->priv->avatars,
-          GUINT_TO_POINTER (handle));
-      const gchar *token = a ? a->token : NULL;
-
-      g_hash_table_insert (result, GUINT_TO_POINTER (handle),
-          (gchar *) (token != NULL ? token : ""));
-    }
-
-  tp_svc_connection_interface_avatars_return_from_get_known_avatar_tokens (
-      context, result);
-  g_hash_table_unref (result);
-}
-
-static void
 my_request_avatars (TpSvcConnectionInterfaceAvatars *avatars,
     const GArray *contacts,
     DBusGMethodInvocation *context)
@@ -1112,8 +986,6 @@ init_avatars (gpointer g_iface,
 #define IMPLEMENT(x) tp_svc_connection_interface_avatars_implement_##x (\
     klass, my_##x)
   /* IMPLEMENT(get_avatar_requirements); */
-  IMPLEMENT(get_avatar_tokens);
-  IMPLEMENT(get_known_avatar_tokens);
   /* IMPLEMENT(request_avatar); */
   IMPLEMENT(request_avatars);
   /* IMPLEMENT(set_avatar); */
@@ -1121,113 +993,6 @@ init_avatars (gpointer g_iface,
 #undef IMPLEMENT
 }
 
-static void
-my_get_locations (TpSvcConnectionInterfaceLocation *avatars,
-    const GArray *contacts,
-    DBusGMethodInvocation *context)
-{
-  TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (avatars);
-  TpBaseConnection *base = TP_BASE_CONNECTION (avatars);
-  TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
-      TP_HANDLE_TYPE_CONTACT);
-  GError *error = NULL;
-  GHashTable *result;
-  guint i;
-
-  TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
-
-  if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error))
-    {
-      dbus_g_method_return_error (context, error);
-      g_error_free (error);
-      return;
-    }
-
-  result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
-
-  for (i = 0; i < contacts->len; i++)
-    {
-      TpHandle handle = g_array_index (contacts, TpHandle, i);
-      GHashTable *location = g_hash_table_lookup (self->priv->locations,
-          GUINT_TO_POINTER (handle));
-
-      if (location != NULL)
-        {
-          g_hash_table_insert (result, GUINT_TO_POINTER (handle), location);
-        }
-    }
-
-  tp_svc_connection_interface_location_return_from_get_locations (
-      context, result);
-  g_hash_table_unref (result);
-}
-
-static void
-init_location (gpointer g_iface,
-    gpointer iface_data)
-{
-  TpSvcConnectionInterfaceLocationClass *klass = g_iface;
-
-#define IMPLEMENT(x) tp_svc_connection_interface_location_implement_##x (\
-    klass, my_##x)
-  IMPLEMENT(get_locations);
-#undef IMPLEMENT
-}
-
-static void
-my_get_contact_capabilities (TpSvcConnectionInterfaceContactCapabilities *obj,
-    const GArray *contacts,
-    DBusGMethodInvocation *context)
-{
-  TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (obj);
-  TpBaseConnection *base = TP_BASE_CONNECTION (obj);
-  TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base,
-      TP_HANDLE_TYPE_CONTACT);
-  GError *error = NULL;
-  GHashTable *result;
-  guint i;
-
-  TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
-
-  if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error))
-    {
-      dbus_g_method_return_error (context, error);
-      g_error_free (error);
-      return;
-    }
-
-  result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
-
-  for (i = 0; i < contacts->len; i++)
-    {
-      TpHandle handle = g_array_index (contacts, TpHandle, i);
-      GPtrArray *arr = g_hash_table_lookup (self->priv->capabilities,
-          GUINT_TO_POINTER (handle));
-
-      if (arr != NULL)
-        {
-          g_hash_table_insert (result, GUINT_TO_POINTER (handle), arr);
-        }
-    }
-
-  tp_svc_connection_interface_contact_capabilities_return_from_get_contact_capabilities (
-      context, result);
-
-  g_hash_table_unref (result);
-}
-
-static void
-init_contact_caps (gpointer g_iface,
-    gpointer iface_data)
-{
-  TpSvcConnectionInterfaceContactCapabilitiesClass *klass = g_iface;
-
-#define IMPLEMENT(x) tp_svc_connection_interface_contact_capabilities_implement_##x (\
-    klass, my_##x)
-  IMPLEMENT(get_contact_capabilities);
-#undef IMPLEMENT
-}
-
 static GPtrArray *
 lookup_contact_info (TpTestsContactsConnection *self,
     TpHandle handle)
@@ -1351,92 +1116,3 @@ init_contact_info (gpointer g_iface,
   IMPLEMENT (set_contact_info);
 #undef IMPLEMENT
 }
-
-/* =============== Legacy version (no Contacts interface) ================= */
-
-G_DEFINE_TYPE (TpTestsLegacyContactsConnection,
-    tp_tests_legacy_contacts_connection, TP_TESTS_TYPE_CONTACTS_CONNECTION);
-
-enum
-{
-    LEGACY_PROP_HAS_IMMORTAL_HANDLES = 1
-};
-
-static void
-legacy_contacts_connection_get_property (GObject *object,
-    guint property_id,
-    GValue *value,
-    GParamSpec *pspec)
-{
-  switch (property_id)
-    {
-    case LEGACY_PROP_HAS_IMMORTAL_HANDLES:
-      /* Pretend we don't. */
-      g_value_set_boolean (value, FALSE);
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-      break;
-    }
-}
-
-static void
-tp_tests_legacy_contacts_connection_init (TpTestsLegacyContactsConnection *self)
-{
-}
-
-static void
-tp_tests_legacy_contacts_connection_class_init (
-    TpTestsLegacyContactsConnectionClass *klass)
-{
-  /* Leave Contacts out of the interfaces we say are present, so clients
-   * won't use it */
-  static const gchar *interfaces_always_present[] = {
-      TP_IFACE_CONNECTION_INTERFACE_ALIASING,
-      TP_IFACE_CONNECTION_INTERFACE_AVATARS,
-      TP_IFACE_CONNECTION_INTERFACE_PRESENCE,
-      TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
-      TP_IFACE_CONNECTION_INTERFACE_LOCATION,
-      TP_IFACE_CONNECTION_INTERFACE_REQUESTS,
-      NULL };
-  TpBaseConnectionClass *base_class =
-      (TpBaseConnectionClass *) klass;
-  GObjectClass *object_class = (GObjectClass *) klass;
-
-  object_class->get_property = legacy_contacts_connection_get_property;
-
-  base_class->interfaces_always_present = interfaces_always_present;
-
-  g_object_class_override_property (object_class,
-      LEGACY_PROP_HAS_IMMORTAL_HANDLES, "has-immortal-handles");
-}
-
-/* =============== No Requests and no ContactCapabilities ================= */
-
-G_DEFINE_TYPE (TpTestsNoRequestsConnection, tp_tests_no_requests_connection,
-    TP_TESTS_TYPE_CONTACTS_CONNECTION);
-
-static void
-tp_tests_no_requests_connection_init (TpTestsNoRequestsConnection *self)
-{
-}
-
-static void
-tp_tests_no_requests_connection_class_init (
-    TpTestsNoRequestsConnectionClass *klass)
-{
-  static const gchar *interfaces_always_present[] = {
-      TP_IFACE_CONNECTION_INTERFACE_ALIASING,
-      TP_IFACE_CONNECTION_INTERFACE_AVATARS,
-      TP_IFACE_CONNECTION_INTERFACE_CONTACTS,
-      TP_IFACE_CONNECTION_INTERFACE_PRESENCE,
-      TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
-      TP_IFACE_CONNECTION_INTERFACE_LOCATION,
-      NULL };
-  TpBaseConnectionClass *base_class =
-      (TpBaseConnectionClass *) klass;
-
-  base_class->interfaces_always_present = interfaces_always_present;
-  base_class->create_channel_managers = NULL;
-}
diff --git a/tests/lib/telepathy/contactlist/contacts-conn.h b/tests/lib/telepathy/contactlist/contacts-conn.h
index 64056e0..679b0eb 100644
--- a/tests/lib/telepathy/contactlist/contacts-conn.h
+++ b/tests/lib/telepathy/contactlist/contacts-conn.h
@@ -13,9 +13,7 @@
 #define __TP_TESTS_CONTACTS_CONN_H__
 
 #include <glib-object.h>
-#include <telepathy-glib/base-connection.h>
-#include <telepathy-glib/contacts-mixin.h>
-#include <telepathy-glib/presence-mixin.h>
+#include <telepathy-glib/telepathy-glib.h>
 
 #include "simple-conn.h"
 #include "contact-list-manager.h"
@@ -113,78 +111,6 @@ void tp_tests_contacts_connection_set_default_contact_info (
     TpTestsContactsConnection *self,
     GPtrArray *info);
 
-/* Legacy version (no Contacts interface, and no immortal handles) */
-
-typedef struct _TpTestsLegacyContactsConnection TpTestsLegacyContactsConnection;
-typedef struct _TpTestsLegacyContactsConnectionClass TpTestsLegacyContactsConnectionClass;
-typedef struct _TpTestsLegacyContactsConnectionPrivate
-  TpTestsLegacyContactsConnectionPrivate;
-
-struct _TpTestsLegacyContactsConnectionClass {
-    TpTestsContactsConnectionClass parent_class;
-};
-
-struct _TpTestsLegacyContactsConnection {
-    TpTestsContactsConnection parent;
-
-    TpTestsLegacyContactsConnectionPrivate *priv;
-};
-
-GType tp_tests_legacy_contacts_connection_get_type (void);
-
-/* TYPE MACROS */
-#define TP_TESTS_TYPE_LEGACY_CONTACTS_CONNECTION \
-  (tp_tests_legacy_contacts_connection_get_type ())
-#define LEGACY_TP_TESTS_CONTACTS_CONNECTION(obj) \
-  (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_LEGACY_CONTACTS_CONNECTION, \
-                              TpTestsLegacyContactsConnection))
-#define LEGACY_TP_TESTS_CONTACTS_CONNECTION_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_LEGACY_CONTACTS_CONNECTION, \
-                           TpTestsLegacyContactsConnectionClass))
-#define TP_TESTS_LEGACY_CONTACTS_IS_CONNECTION(obj) \
-  (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_LEGACY_CONTACTS_CONNECTION))
-#define TP_TESTS_LEGACY_CONTACTS_IS_CONNECTION_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_LEGACY_CONTACTS_CONNECTION))
-#define LEGACY_TP_TESTS_CONTACTS_CONNECTION_GET_CLASS(obj) \
-  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_LEGACY_CONTACTS_CONNECTION, \
-                              TpTestsLegacyContactsConnectionClass))
-
-/* No Requests version */
-
-typedef struct _TpTestsNoRequestsConnection TpTestsNoRequestsConnection;
-typedef struct _TpTestsNoRequestsConnectionClass TpTestsNoRequestsConnectionClass;
-typedef struct _TpTestsNoRequestsConnectionPrivate
-  TpTestsNoRequestsConnectionPrivate;
-
-struct _TpTestsNoRequestsConnectionClass {
-    TpTestsContactsConnectionClass parent_class;
-};
-
-struct _TpTestsNoRequestsConnection {
-    TpTestsContactsConnection parent;
-
-    TpTestsNoRequestsConnectionPrivate *priv;
-};
-
-GType tp_tests_no_requests_connection_get_type (void);
-
-/* TYPE MACROS */
-#define TP_TESTS_TYPE_NO_REQUESTS_CONNECTION \
-  (tp_tests_no_requests_connection_get_type ())
-#define TP_TESTS_NO_REQUESTS_CONNECTION(obj) \
-  (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_NO_REQUESTS_CONNECTION, \
-                              TpTestsNoRequestsConnection))
-#define TP_TESTS_NO_REQUESTS_CONNECTION_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_NO_REQUESTS_CONNECTION, \
-                           TpTestsNoRequestsConnectionClass))
-#define TP_TESTS_NO_REQUESTS_IS_CONNECTION(obj) \
-  (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_NO_REQUESTS_CONNECTION))
-#define TP_TESTS_NO_REQUESTS_IS_CONNECTION_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_NO_REQUESTS_CONNECTION))
-#define TP_TESTS_NO_REQUESTS_CONNECTION_GET_CLASS(obj) \
-  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_NO_REQUESTS_CONNECTION, \
-                              TpTestsNoRequestsConnectionClass))
-
 G_END_DECLS
 
 #endif /* ifndef __TP_TESTS_CONTACTS_CONN_H__ */
diff --git a/tests/lib/telepathy/contactlist/dbus-tube-chan.c 
b/tests/lib/telepathy/contactlist/dbus-tube-chan.c
new file mode 100644
index 0000000..28e34b5
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/dbus-tube-chan.c
@@ -0,0 +1,455 @@
+/*
+ * dbus-tube-chan.c - Simple dbus tube channel
+ *
+ * Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "config.h"
+
+#include "dbus-tube-chan.h"
+
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
+#include <glib/gstdio.h>
+
+#if defined(G_OS_UNIX)
+#   define LISTEN_ADDRESS "unix:tmpdir=/tmp"
+#else
+#   define LISTEN_ADDRESS "tcp:host=127.0.0.1"
+#endif
+
+enum
+{
+  PROP_SERVICE_NAME = 1,
+  PROP_DBUS_NAMES,
+  PROP_SUPPORTED_ACCESS_CONTROLS,
+  PROP_PARAMETERS,
+  PROP_STATE,
+};
+
+enum
+{
+  SIG_NEW_CONNECTION,
+  LAST_SIGNAL
+};
+
+static guint _signals[LAST_SIGNAL] = { 0, };
+
+struct _TpTestsDBusTubeChannelPrivate {
+    /* Controls whether the channel should become open before returning from
+     * Open/Accept, after returning, or never.
+     */
+    TpTestsDBusTubeChannelOpenMode open_mode;
+    TpTubeChannelState state;
+
+    /* TpHandle -> gchar * */
+    GHashTable *dbus_names;
+
+    GDBusServer *dbus_server;
+};
+
+static void
+tp_tests_dbus_tube_channel_get_property (GObject *object,
+    guint property_id,
+    GValue *value,
+    GParamSpec *pspec)
+{
+  TpTestsDBusTubeChannel *self = (TpTestsDBusTubeChannel *) object;
+
+  switch (property_id)
+    {
+      case PROP_SERVICE_NAME:
+        g_value_set_string (value, "com.test.Test");
+        break;
+
+      case PROP_DBUS_NAMES:
+        g_value_set_boxed (value, self->priv->dbus_names);
+        break;
+
+      case PROP_SUPPORTED_ACCESS_CONTROLS:
+        {
+          GArray *array;
+          TpSocketAccessControl a;
+
+          array = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
+
+          a = TP_SOCKET_ACCESS_CONTROL_LOCALHOST;
+          g_array_append_val (array, a);
+
+          g_value_set_boxed (value, array);
+
+          g_array_unref (array);
+        }
+        break;
+
+      case PROP_PARAMETERS:
+        g_value_take_boxed (value, tp_asv_new (
+              "badger", G_TYPE_UINT, 42,
+              NULL));
+        break;
+
+      case PROP_STATE:
+        g_value_set_uint (value, self->priv->state);
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void dbus_tube_iface_init (gpointer iface, gpointer data);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (TpTestsDBusTubeChannel,
+    tp_tests_dbus_tube_channel,
+    TP_TYPE_BASE_CHANNEL,
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_DBUS_TUBE,
+      dbus_tube_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_TUBE,
+      NULL);
+    )
+
+/* type definition stuff */
+
+static GPtrArray *
+tp_tests_dbus_tube_channel_get_interfaces (TpBaseChannel *self)
+{
+  GPtrArray *interfaces;
+
+  interfaces = TP_BASE_CHANNEL_CLASS (
+      tp_tests_dbus_tube_channel_parent_class)->get_interfaces (self);
+
+  g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_TUBE);
+  return interfaces;
+};
+
+static void
+tp_tests_dbus_tube_channel_init (TpTestsDBusTubeChannel *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
+      TP_TESTS_TYPE_DBUS_TUBE_CHANNEL, TpTestsDBusTubeChannelPrivate);
+
+  self->priv->open_mode = TP_TESTS_DBUS_TUBE_CHANNEL_OPEN_FIRST;
+  self->priv->dbus_names = g_hash_table_new_full (g_direct_hash,
+      g_direct_equal, NULL, g_free);
+}
+
+static GObject *
+constructor (GType type,
+             guint n_props,
+             GObjectConstructParam *props)
+{
+  GObject *object =
+      G_OBJECT_CLASS (tp_tests_dbus_tube_channel_parent_class)->constructor (
+          type, n_props, props);
+  TpTestsDBusTubeChannel *self = TP_TESTS_DBUS_TUBE_CHANNEL (object);
+
+  if (tp_base_channel_is_requested (TP_BASE_CHANNEL (self)))
+    self->priv->state = TP_TUBE_CHANNEL_STATE_NOT_OFFERED;
+  else
+    self->priv->state = TP_TUBE_CHANNEL_STATE_LOCAL_PENDING;
+
+  tp_base_channel_register (TP_BASE_CHANNEL (self));
+
+  return object;
+}
+
+static void
+dispose (GObject *object)
+{
+  TpTestsDBusTubeChannel *self = (TpTestsDBusTubeChannel *) object;
+
+  tp_clear_pointer (&self->priv->dbus_names, g_hash_table_unref);
+
+  if (self->priv->dbus_server != NULL)
+    {
+      /* FIXME: this is pretty stupid but apparently unless you start and then
+       * stop the server before freeing it, it doesn't stop listening. Calling
+       * _start() twice is a no-op.
+       *
+       * https://bugzilla.gnome.org/show_bug.cgi?id=673372
+      */
+      g_dbus_server_start (self->priv->dbus_server);
+
+      g_dbus_server_stop (self->priv->dbus_server);
+      g_clear_object (&self->priv->dbus_server);
+    }
+
+  ((GObjectClass *) tp_tests_dbus_tube_channel_parent_class)->dispose (
+    object);
+}
+
+static void
+channel_close (TpBaseChannel *channel)
+{
+  tp_base_channel_destroyed (channel);
+}
+
+static void
+fill_immutable_properties (TpBaseChannel *chan,
+    GHashTable *properties)
+{
+  TpBaseChannelClass *klass = TP_BASE_CHANNEL_CLASS (
+      tp_tests_dbus_tube_channel_parent_class);
+
+  klass->fill_immutable_properties (chan, properties);
+
+  tp_dbus_properties_mixin_fill_properties_hash (
+      G_OBJECT (chan), properties,
+      TP_IFACE_CHANNEL_TYPE_DBUS_TUBE, "ServiceName",
+      TP_IFACE_CHANNEL_TYPE_DBUS_TUBE, "SupportedAccessControls",
+      NULL);
+
+  if (!tp_base_channel_is_requested (chan))
+    {
+      /* Parameters is immutable only for incoming tubes */
+      tp_dbus_properties_mixin_fill_properties_hash (
+          G_OBJECT (chan), properties,
+          TP_IFACE_CHANNEL_INTERFACE_TUBE, "Parameters",
+          NULL);
+    }
+}
+
+static void
+tp_tests_dbus_tube_channel_class_init (TpTestsDBusTubeChannelClass *klass)
+{
+  GObjectClass *object_class = (GObjectClass *) klass;
+  TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (klass);
+  GParamSpec *param_spec;
+  static TpDBusPropertiesMixinPropImpl dbus_tube_props[] = {
+      { "ServiceName", "service-name", NULL, },
+      { "DBusNames", "dbus-names", NULL, },
+      { "SupportedAccessControls", "supported-access-controls", NULL, },
+      { NULL }
+  };
+  static TpDBusPropertiesMixinPropImpl tube_props[] = {
+      { "Parameters", "parameters", NULL, },
+      { "State", "state", NULL, },
+      { NULL }
+  };
+
+  object_class->constructor = constructor;
+  object_class->get_property = tp_tests_dbus_tube_channel_get_property;
+  object_class->dispose = dispose;
+
+  base_class->channel_type = TP_IFACE_CHANNEL_TYPE_DBUS_TUBE;
+  base_class->get_interfaces = tp_tests_dbus_tube_channel_get_interfaces;
+  base_class->close = channel_close;
+  base_class->fill_immutable_properties = fill_immutable_properties;
+
+  /* base_class->target_handle_type is defined in subclasses */
+
+  param_spec = g_param_spec_string ("service-name", "Service Name",
+      "the service name associated with this tube object.",
+       "",
+       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_SERVICE_NAME, param_spec);
+
+  param_spec = g_param_spec_boxed ("dbus-names", "DBus Names",
+      "DBusTube.DBusNames",
+      TP_HASH_TYPE_DBUS_TUBE_PARTICIPANTS,
+       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_DBUS_NAMES, param_spec);
+
+  param_spec = g_param_spec_boxed ("supported-access-controls",
+      "Supported access-controls",
+      "GArray containing supported access controls.",
+      DBUS_TYPE_G_UINT_ARRAY,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class,
+      PROP_SUPPORTED_ACCESS_CONTROLS, param_spec);
+
+  param_spec = g_param_spec_boxed (
+      "parameters", "Parameters",
+      "parameters of the tube",
+      TP_HASH_TYPE_STRING_VARIANT_MAP,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_PARAMETERS,
+      param_spec);
+
+  param_spec = g_param_spec_uint (
+      "state", "TpTubeState",
+      "state of the tube",
+      0, TP_NUM_TUBE_CHANNEL_STATES - 1, 0,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_STATE,
+      param_spec);
+
+  _signals[SIG_NEW_CONNECTION] = g_signal_new ("new-connection",
+      G_OBJECT_CLASS_TYPE (klass),
+      G_SIGNAL_RUN_LAST,
+      0,
+      g_signal_accumulator_true_handled, NULL,
+      NULL,
+      G_TYPE_BOOLEAN,
+      1, G_TYPE_DBUS_CONNECTION);
+
+  tp_dbus_properties_mixin_implement_interface (object_class,
+      TP_IFACE_QUARK_CHANNEL_TYPE_DBUS_TUBE,
+      tp_dbus_properties_mixin_getter_gobject_properties, NULL,
+      dbus_tube_props);
+
+  tp_dbus_properties_mixin_implement_interface (object_class,
+      TP_IFACE_QUARK_CHANNEL_INTERFACE_TUBE,
+      tp_dbus_properties_mixin_getter_gobject_properties, NULL,
+      tube_props);
+
+  g_type_class_add_private (object_class,
+      sizeof (TpTestsDBusTubeChannelPrivate));
+}
+
+static void
+change_state (TpTestsDBusTubeChannel *self,
+  TpTubeChannelState state)
+{
+  self->priv->state = state;
+
+  tp_svc_channel_interface_tube_emit_tube_channel_state_changed (self, state);
+}
+
+static gboolean
+dbus_new_connection_cb (GDBusServer *server,
+    GDBusConnection *connection,
+    gpointer user_data)
+{
+  TpTestsDBusTubeChannel *self = user_data;
+  gboolean ret = FALSE;
+
+  g_signal_emit (self, _signals[SIG_NEW_CONNECTION], 0, connection, &ret);
+  return ret;
+}
+
+static void
+open_tube (TpTestsDBusTubeChannel *self)
+{
+  GError *error = NULL;
+  gchar *guid;
+
+  guid = g_dbus_generate_guid ();
+
+  self->priv->dbus_server = g_dbus_server_new_sync (LISTEN_ADDRESS,
+      G_DBUS_SERVER_FLAGS_NONE, guid, NULL, NULL, &error);
+  g_assert_no_error (error);
+
+  g_free (guid);
+
+  g_signal_connect (self->priv->dbus_server, "new-connection",
+      G_CALLBACK (dbus_new_connection_cb), self);
+}
+
+static void
+really_open_tube (TpTestsDBusTubeChannel *self)
+{
+  g_dbus_server_start (self->priv->dbus_server);
+
+  change_state (self, TP_TUBE_CHANNEL_STATE_OPEN);
+}
+
+static void
+dbus_tube_offer (TpSvcChannelTypeDBusTube *chan,
+    GHashTable *parameters,
+    guint access_control,
+    DBusGMethodInvocation *context)
+{
+  TpTestsDBusTubeChannel *self = (TpTestsDBusTubeChannel *) chan;
+
+  open_tube (self);
+
+  if (self->priv->open_mode == TP_TESTS_DBUS_TUBE_CHANNEL_OPEN_FIRST)
+    really_open_tube (self);
+
+  tp_svc_channel_type_dbus_tube_return_from_offer (context,
+      g_dbus_server_get_client_address (self->priv->dbus_server));
+
+  if (self->priv->open_mode == TP_TESTS_DBUS_TUBE_CHANNEL_OPEN_SECOND)
+    really_open_tube (self);
+  else if (self->priv->open_mode == TP_TESTS_DBUS_TUBE_CHANNEL_NEVER_OPEN)
+    tp_base_channel_close (TP_BASE_CHANNEL (self));
+}
+
+static void
+dbus_tube_accept (TpSvcChannelTypeDBusTube *chan,
+    guint access_control,
+    DBusGMethodInvocation *context)
+{
+  TpTestsDBusTubeChannel *self = (TpTestsDBusTubeChannel *) chan;
+
+  open_tube (self);
+
+  if (self->priv->open_mode == TP_TESTS_DBUS_TUBE_CHANNEL_OPEN_FIRST)
+    really_open_tube (self);
+
+  tp_svc_channel_type_dbus_tube_return_from_accept (context,
+      g_dbus_server_get_client_address (self->priv->dbus_server));
+
+  if (self->priv->open_mode == TP_TESTS_DBUS_TUBE_CHANNEL_OPEN_SECOND)
+    really_open_tube (self);
+  else if (self->priv->open_mode == TP_TESTS_DBUS_TUBE_CHANNEL_NEVER_OPEN)
+    tp_base_channel_close (TP_BASE_CHANNEL (self));
+}
+
+void
+tp_tests_dbus_tube_channel_set_open_mode (
+    TpTestsDBusTubeChannel *self,
+    TpTestsDBusTubeChannelOpenMode open_mode)
+{
+  self->priv->open_mode = open_mode;
+}
+
+static void
+dbus_tube_iface_init (gpointer iface,
+    gpointer data)
+{
+  TpSvcChannelTypeDBusTubeClass *klass = iface;
+
+#define IMPLEMENT(x) tp_svc_channel_type_dbus_tube_implement_##x (klass, dbus_tube_##x)
+  IMPLEMENT (offer);
+  IMPLEMENT (accept);
+#undef IMPLEMENT
+}
+
+/* Contact DBus Tube */
+
+G_DEFINE_TYPE (TpTestsContactDBusTubeChannel,
+    tp_tests_contact_dbus_tube_channel,
+    TP_TESTS_TYPE_DBUS_TUBE_CHANNEL)
+
+static void
+tp_tests_contact_dbus_tube_channel_init (
+    TpTestsContactDBusTubeChannel *self)
+{
+}
+
+static void
+tp_tests_contact_dbus_tube_channel_class_init (
+    TpTestsContactDBusTubeChannelClass *klass)
+{
+  TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (klass);
+
+  base_class->target_handle_type = TP_HANDLE_TYPE_CONTACT;
+}
+
+/* Room DBus Tube */
+
+G_DEFINE_TYPE (TpTestsRoomDBusTubeChannel,
+    tp_tests_room_dbus_tube_channel,
+    TP_TESTS_TYPE_DBUS_TUBE_CHANNEL)
+
+static void
+tp_tests_room_dbus_tube_channel_init (
+    TpTestsRoomDBusTubeChannel *self)
+{
+}
+
+static void
+tp_tests_room_dbus_tube_channel_class_init (
+    TpTestsRoomDBusTubeChannelClass *klass)
+{
+  TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (klass);
+
+  base_class->target_handle_type = TP_HANDLE_TYPE_ROOM;
+}
diff --git a/tests/lib/telepathy/contactlist/dbus-tube-chan.h 
b/tests/lib/telepathy/contactlist/dbus-tube-chan.h
new file mode 100644
index 0000000..7180097
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/dbus-tube-chan.h
@@ -0,0 +1,127 @@
+/*
+ * dbus-tube-chan.h - Simple dbus tube channel
+ *
+ * Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef __TP_DBUS_TUBE_CHAN_H__
+#define __TP_DBUS_TUBE_CHAN_H__
+
+#include <glib-object.h>
+#include <telepathy-glib/telepathy-glib.h>
+
+G_BEGIN_DECLS
+
+/* Base Class */
+typedef struct _TpTestsDBusTubeChannel TpTestsDBusTubeChannel;
+typedef struct _TpTestsDBusTubeChannelClass TpTestsDBusTubeChannelClass;
+typedef struct _TpTestsDBusTubeChannelPrivate TpTestsDBusTubeChannelPrivate;
+
+GType tp_tests_dbus_tube_channel_get_type (void);
+
+#define TP_TESTS_TYPE_DBUS_TUBE_CHANNEL \
+  (tp_tests_dbus_tube_channel_get_type ())
+#define TP_TESTS_DBUS_TUBE_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), TP_TESTS_TYPE_DBUS_TUBE_CHANNEL, \
+                               TpTestsDBusTubeChannel))
+#define TP_TESTS_DBUS_TUBE_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), TP_TESTS_TYPE_DBUS_TUBE_CHANNEL, \
+                            TpTestsDBusTubeChannelClass))
+#define TP_TESTS_IS_DBUS_TUBE_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TP_TESTS_TYPE_DBUS_TUBE_CHANNEL))
+#define TP_TESTS_IS_DBUS_TUBE_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), TP_TESTS_TYPE_DBUS_TUBE_CHANNEL))
+#define TP_TESTS_DBUS_TUBE_CHANNEL_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_DBUS_TUBE_CHANNEL, \
+                              TpTestsDBusTubeChannelClass))
+
+struct _TpTestsDBusTubeChannelClass {
+    TpBaseChannelClass parent_class;
+    TpDBusPropertiesMixinClass dbus_properties_class;
+};
+
+struct _TpTestsDBusTubeChannel {
+    TpBaseChannel parent;
+
+    TpTestsDBusTubeChannelPrivate *priv;
+};
+
+typedef enum {
+    TP_TESTS_DBUS_TUBE_CHANNEL_OPEN_FIRST,
+    TP_TESTS_DBUS_TUBE_CHANNEL_OPEN_SECOND,
+    TP_TESTS_DBUS_TUBE_CHANNEL_NEVER_OPEN
+} TpTestsDBusTubeChannelOpenMode;
+
+void tp_tests_dbus_tube_channel_set_open_mode (
+    TpTestsDBusTubeChannel *self,
+    TpTestsDBusTubeChannelOpenMode open_mode);
+
+/* Contact DBus Tube */
+
+typedef struct _TpTestsContactDBusTubeChannel TpTestsContactDBusTubeChannel;
+typedef struct _TpTestsContactDBusTubeChannelClass TpTestsContactDBusTubeChannelClass;
+
+GType tp_tests_contact_dbus_tube_channel_get_type (void);
+
+#define TP_TESTS_TYPE_CONTACT_DBUS_TUBE_CHANNEL \
+  (tp_tests_contact_dbus_tube_channel_get_type ())
+#define TP_TESTS_CONTACT_DBUS_TUBE_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), TP_TESTS_TYPE_CONTACT_DBUS_TUBE_CHANNEL, \
+                               TpTestsContactDBusTubeChannel))
+#define TP_TESTS_CONTACT_DBUS_TUBE_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), TP_TESTS_TYPE_CONTACT_DBUS_TUBE_CHANNEL, \
+                            TpTestsContactDBusTubeChannelClass))
+#define TP_TESTS_IS_CONTACT_DBUS_TUBE_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TP_TESTS_TYPE_CONTACT_DBUS_TUBE_CHANNEL))
+#define TP_TESTS_IS_CONTACT_DBUS_TUBE_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), TP_TESTS_TYPE_CONTACT_DBUS_TUBE_CHANNEL))
+#define TP_TESTS_CONTACT_DBUS_TUBE_CHANNEL_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_CONTACT_DBUS_TUBE_CHANNEL, \
+                              TpTestsContactDBusTubeChannelClass))
+
+struct _TpTestsContactDBusTubeChannelClass {
+    TpTestsDBusTubeChannelClass parent_class;
+};
+
+struct _TpTestsContactDBusTubeChannel {
+    TpTestsDBusTubeChannel parent;
+};
+
+/* Room DBus Tube */
+
+typedef struct _TpTestsRoomDBusTubeChannel TpTestsRoomDBusTubeChannel;
+typedef struct _TpTestsRoomDBusTubeChannelClass TpTestsRoomDBusTubeChannelClass;
+
+GType tp_tests_room_dbus_tube_channel_get_type (void);
+
+#define TP_TESTS_TYPE_ROOM_DBUS_TUBE_CHANNEL \
+  (tp_tests_room_dbus_tube_channel_get_type ())
+#define TP_TESTS_ROOM_DBUS_TUBE_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), TP_TESTS_TYPE_ROOM_DBUS_TUBE_CHANNEL, \
+                               TpTestsRoomDBusTubeChannel))
+#define TP_TESTS_ROOM_DBUS_TUBE_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), TP_TESTS_TYPE_ROOM_DBUS_TUBE_CHANNEL, \
+                            TpTestsRoomDBusTubeChannelClass))
+#define TP_TESTS_IS_ROOM_DBUS_TUBE_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TP_TESTS_TYPE_ROOM_DBUS_TUBE_CHANNEL))
+#define TP_TESTS_IS_ROOM_DBUS_TUBE_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), TP_TESTS_TYPE_ROOM_DBUS_TUBE_CHANNEL))
+#define TP_TESTS_ROOM_DBUS_TUBE_CHANNEL_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_ROOM_DBUS_TUBE_CHANNEL, \
+                              TpTestsRoomDBusTubeChannelClass))
+
+struct _TpTestsRoomDBusTubeChannelClass {
+    TpTestsDBusTubeChannelClass parent_class;
+};
+
+struct _TpTestsRoomDBusTubeChannel {
+    TpTestsDBusTubeChannel parent;
+};
+
+G_END_DECLS
+
+#endif /* #ifndef __TP_DBUS_TUBE_CHAN_H__ */
diff --git a/tests/lib/telepathy/contactlist/echo-chan.c b/tests/lib/telepathy/contactlist/echo-chan.c
new file mode 100644
index 0000000..868fbcf
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/echo-chan.c
@@ -0,0 +1,223 @@
+/*
+ * chan.c - an example text channel talking to a particular
+ * contact. Similar code is used for 1-1 IM channels in many protocols
+ * (IRC private messages ("/query"), XMPP IM etc.)
+ *
+ * Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "config.h"
+
+#include "echo-chan.h"
+
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
+
+static void destroyable_iface_init (gpointer iface, gpointer data);
+
+G_DEFINE_TYPE_WITH_CODE (TpTestsEchoChannel,
+    tp_tests_echo_channel,
+    TP_TYPE_BASE_CHANNEL,
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_TEXT,
+      tp_message_mixin_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_DESTROYABLE,
+      destroyable_iface_init);
+    )
+
+/* type definition stuff */
+
+static GPtrArray *
+tp_tests_echo_channel_get_interfaces (TpBaseChannel *self)
+{
+  GPtrArray *interfaces;
+
+  interfaces = TP_BASE_CHANNEL_CLASS (tp_tests_echo_channel_parent_class)->
+    get_interfaces (self);
+
+  g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_DESTROYABLE);
+  return interfaces;
+};
+
+static void
+tp_tests_echo_channel_init (TpTestsEchoChannel *self)
+{
+}
+
+static void text_send (GObject *object, TpMessage *message,
+    TpMessageSendingFlags flags);
+
+static void
+constructed (GObject *object)
+{
+  TpTestsEchoChannel *self = TP_TESTS_ECHO_CHANNEL (object);
+  TpBaseConnection *conn = tp_base_channel_get_connection (TP_BASE_CHANNEL (self));
+  const TpChannelTextMessageType types[] = {
+      TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
+      TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION,
+      TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE,
+  };
+  const gchar * supported_content_types[] = {
+      "text/plain",
+      NULL
+  };
+  g_assert (conn != NULL);
+
+  G_OBJECT_CLASS (tp_tests_echo_channel_parent_class)->constructed (object);
+
+  tp_base_channel_register (TP_BASE_CHANNEL (self));
+
+  tp_message_mixin_init (object,
+      G_STRUCT_OFFSET (TpTestsEchoChannel, message),
+      conn);
+  tp_message_mixin_implement_sending (object,
+      text_send, G_N_ELEMENTS (types), types, 0, 0,
+      supported_content_types);
+}
+
+static void
+finalize (GObject *object)
+{
+  tp_message_mixin_finalize (object);
+
+  ((GObjectClass *) tp_tests_echo_channel_parent_class)->finalize (object);
+}
+
+static void
+tp_tests_echo_channel_close (TpTestsEchoChannel *self)
+{
+  GObject *object = (GObject *) self;
+  gboolean closed = tp_base_channel_is_destroyed (TP_BASE_CHANNEL (self));
+
+  if (!closed)
+    {
+      TpHandle first_sender;
+
+      /* The manager wants to be able to respawn the channel if it has pending
+       * messages. When respawned, the channel must have the initiator set
+       * to the contact who sent us those messages (if it isn't already),
+       * and the messages must be marked as having been rescued so they
+       * don't get logged twice. */
+      if (tp_message_mixin_has_pending_messages (object, &first_sender))
+        {
+          tp_base_channel_reopened (TP_BASE_CHANNEL (self), first_sender);
+          tp_message_mixin_set_rescued (object);
+        }
+      else
+        {
+          tp_base_channel_destroyed (TP_BASE_CHANNEL (self));
+        }
+    }
+}
+
+static void
+channel_close (TpBaseChannel *channel)
+{
+  TpTestsEchoChannel *self = TP_TESTS_ECHO_CHANNEL (channel);
+
+  tp_tests_echo_channel_close (self);
+}
+
+static void
+tp_tests_echo_channel_class_init (TpTestsEchoChannelClass *klass)
+{
+  GObjectClass *object_class = (GObjectClass *) klass;
+  TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (klass);
+
+  object_class->constructed = constructed;
+  object_class->finalize = finalize;
+
+  base_class->channel_type = TP_IFACE_CHANNEL_TYPE_TEXT;
+  base_class->target_handle_type = TP_HANDLE_TYPE_CONTACT;
+  base_class->get_interfaces = tp_tests_echo_channel_get_interfaces;
+  base_class->close = channel_close;
+
+  tp_message_mixin_init_dbus_properties (object_class);
+}
+
+
+static void
+text_send (GObject *object,
+    TpMessage *message,
+    TpMessageSendingFlags flags)
+{
+  TpTestsEchoChannel *self = TP_TESTS_ECHO_CHANNEL (object);
+  TpChannelTextMessageType type = tp_message_get_message_type (message);
+  TpChannelTextMessageType echo_type = type;
+  TpHandle target = tp_base_channel_get_target_handle (TP_BASE_CHANNEL (self));
+  gchar *echo;
+  gint64 now = time (NULL);
+  const GHashTable *part;
+  const gchar *text;
+  TpMessage *msg;
+
+  /* Pretend that the remote contact has replied. Normally, you'd
+   * call tp_text_mixin_receive or tp_text_mixin_receive_with_flags
+   * in response to network events */
+
+  part = tp_message_peek (message, 1);
+  text = tp_asv_get_string (part, "content");
+
+  switch (type)
+    {
+    case TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL:
+      echo = g_strdup_printf ("You said: %s", text);
+      break;
+    case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION:
+      echo = g_strdup_printf ("notices that the user %s", text);
+      break;
+    case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE:
+      echo = g_strdup_printf ("You sent a notice: %s", text);
+      break;
+    default:
+      echo = g_strdup_printf ("You sent some weird message type, %u: \"%s\"",
+          type, text);
+      echo_type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
+    }
+
+  tp_message_mixin_sent (object, message, 0, "", NULL);
+
+  msg = tp_cm_message_new (
+      tp_base_channel_get_connection (TP_BASE_CHANNEL (self)),
+      2);
+
+  tp_cm_message_set_sender (msg, target);
+  tp_message_set_uint32 (msg, 0, "message-type", echo_type);
+  tp_message_set_int64 (msg, 0, "message-sent", now);
+  tp_message_set_int64 (msg, 0, "message-received", now);
+
+  tp_message_set_string (msg, 1, "content-type", "text/plain");
+  tp_message_set_string (msg, 1, "content", echo);
+
+  tp_message_mixin_take_received (object, msg);
+
+  g_free (echo);
+}
+
+static void
+destroyable_destroy (TpSvcChannelInterfaceDestroyable *iface,
+                     DBusGMethodInvocation *context)
+{
+  TpTestsEchoChannel *self = TP_TESTS_ECHO_CHANNEL (iface);
+
+  tp_message_mixin_clear ((GObject *) self);
+  tp_base_channel_destroyed (TP_BASE_CHANNEL (self));
+
+  tp_svc_channel_interface_destroyable_return_from_destroy (context);
+}
+
+static void
+destroyable_iface_init (gpointer iface,
+                        gpointer data)
+{
+  TpSvcChannelInterfaceDestroyableClass *klass = iface;
+
+#define IMPLEMENT(x) \
+  tp_svc_channel_interface_destroyable_implement_##x (klass, destroyable_##x)
+  IMPLEMENT (destroy);
+#undef IMPLEMENT
+}
diff --git a/tests/lib/telepathy/contactlist/echo-chan.h b/tests/lib/telepathy/contactlist/echo-chan.h
new file mode 100644
index 0000000..5fbe6ee
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/echo-chan.h
@@ -0,0 +1,56 @@
+/*
+ * chan.h - header for an example channel
+ *
+ * Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef __TP_TESTS_CHAN_H__
+#define __TP_TESTS_CHAN_H__
+
+#include <glib-object.h>
+#include <telepathy-glib/telepathy-glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _TpTestsEchoChannel TpTestsEchoChannel;
+typedef struct _TpTestsEchoChannelClass TpTestsEchoChannelClass;
+typedef struct _TpTestsEchoChannelPrivate TpTestsEchoChannelPrivate;
+
+GType tp_tests_echo_channel_get_type (void);
+
+#define TP_TESTS_TYPE_ECHO_CHANNEL \
+  (tp_tests_echo_channel_get_type ())
+#define TP_TESTS_ECHO_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), TP_TESTS_TYPE_ECHO_CHANNEL, \
+                               TpTestsEchoChannel))
+#define TP_TESTS_ECHO_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), TP_TESTS_TYPE_ECHO_CHANNEL, \
+                            TpTestsEchoChannelClass))
+#define TP_TESTS_IS_ECHO_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TP_TESTS_TYPE_ECHO_CHANNEL))
+#define TP_TESTS_IS_ECHO_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), TP_TESTS_TYPE_ECHO_CHANNEL))
+#define TP_TESTS_ECHO_CHANNEL_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_ECHO_CHANNEL, \
+                              TpTestsEchoChannelClass))
+
+struct _TpTestsEchoChannelClass {
+    TpBaseChannelClass parent_class;
+    TpDBusPropertiesMixinClass dbus_properties_class;
+};
+
+struct _TpTestsEchoChannel {
+    TpBaseChannel parent;
+    TpMessageMixin message;
+
+    TpTestsEchoChannelPrivate *priv;
+};
+
+G_END_DECLS
+
+#endif /* #ifndef __TP_TESTS_CHAN_H__ */
diff --git a/tests/lib/telepathy/contactlist/echo-conn.c b/tests/lib/telepathy/contactlist/echo-conn.c
new file mode 100644
index 0000000..e985ec6
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/echo-conn.c
@@ -0,0 +1,165 @@
+/*
+ * conn.c - an example connection
+ *
+ * Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "config.h"
+
+#include "echo-conn.h"
+
+#include <dbus/dbus-glib.h>
+
+#include <telepathy-glib/telepathy-glib.h>
+
+#include "echo-im-manager.h"
+#include "simple-channel-manager.h"
+
+G_DEFINE_TYPE (TpTestsEchoConnection,
+    tp_tests_echo_connection,
+    TP_TESTS_TYPE_CONTACTS_CONNECTION)
+
+/* type definition stuff */
+
+enum
+{
+  PROP_CHANNEL_MANAGER = 1,
+  N_PROPS
+};
+
+struct _TpTestsEchoConnectionPrivate
+{
+  TpTestsSimpleChannelManager *channel_manager;
+};
+
+
+static void
+tp_tests_echo_connection_init (TpTestsEchoConnection *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TESTS_TYPE_ECHO_CONNECTION,
+      TpTestsEchoConnectionPrivate);
+}
+
+/* Returns the same id given in but in lowercase. If '#' is present,
+ * the normalized contact will be the lhs of it. For example:
+ *
+ * LOL -> lol
+ * Lol#foo -> lol
+ */
+static gchar *
+tp_tests_echo_normalize_contact (TpHandleRepoIface *repo,
+                           const gchar *id,
+                           gpointer context,
+                           GError **error)
+{
+  gchar *hash;
+
+  if (id[0] == '\0')
+    {
+      g_set_error (error, TP_ERROR, TP_ERROR_INVALID_HANDLE,
+          "ID must not be empty");
+      return NULL;
+    }
+
+  hash = g_utf8_strchr (id, -1, '#');
+
+  return g_utf8_strdown (id, hash != NULL ? (hash - id) : -1);
+}
+
+static void
+create_handle_repos (TpBaseConnection *conn,
+                     TpHandleRepoIface *repos[TP_NUM_HANDLE_TYPES])
+{
+  ((TpBaseConnectionClass *)
+      tp_tests_echo_connection_parent_class)->create_handle_repos (conn, repos);
+
+  /* Replace the contacts handle repo with our own, for special normalization */
+  g_assert (repos[TP_HANDLE_TYPE_CONTACT] != NULL);
+  g_object_unref (repos[TP_HANDLE_TYPE_CONTACT]);
+  repos[TP_HANDLE_TYPE_CONTACT] = tp_dynamic_handle_repo_new
+      (TP_HANDLE_TYPE_CONTACT, tp_tests_echo_normalize_contact, NULL);
+}
+
+static GPtrArray *
+create_channel_managers (TpBaseConnection *conn)
+{
+  TpTestsEchoConnection *self = TP_TESTS_ECHO_CONNECTION (conn);
+  GPtrArray *ret;
+
+  ret = ((TpBaseConnectionClass *)
+      tp_tests_echo_connection_parent_class)->create_channel_managers (conn);
+
+  if (self->priv->channel_manager == NULL)
+    {
+      self->priv->channel_manager = g_object_new (TP_TESTS_TYPE_ECHO_IM_MANAGER,
+          "connection", conn,
+          NULL);
+    }
+
+  /* tp-glib will free this for us so we don't need to worry about
+     doing it ourselves. */
+  g_ptr_array_add (ret, self->priv->channel_manager);
+
+  return ret;
+}
+
+static void
+get_property (GObject *object,
+    guint property_id,
+    GValue *value,
+    GParamSpec *spec)
+{
+  TpTestsEchoConnection *self = TP_TESTS_ECHO_CONNECTION (object);
+
+  switch (property_id) {
+    case PROP_CHANNEL_MANAGER:
+      g_assert (self->priv->channel_manager == NULL); /* construct-only */
+      g_value_set_object (value, self->priv->channel_manager);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
+  }
+}
+
+static void
+set_property (GObject *object,
+    guint property_id,
+    const GValue *value,
+    GParamSpec *spec)
+{
+  TpTestsEchoConnection *self = TP_TESTS_ECHO_CONNECTION (object);
+
+  switch (property_id) {
+    case PROP_CHANNEL_MANAGER:
+      self->priv->channel_manager = g_value_dup_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
+  }
+}
+
+static void
+tp_tests_echo_connection_class_init (TpTestsEchoConnectionClass *klass)
+{
+  TpBaseConnectionClass *base_class =
+      (TpBaseConnectionClass *) klass;
+  GObjectClass *object_class = (GObjectClass *) klass;
+  GParamSpec *param_spec;
+
+  object_class->get_property = get_property;
+  object_class->set_property = set_property;
+  g_type_class_add_private (klass, sizeof (TpTestsEchoConnectionPrivate));
+
+  base_class->create_handle_repos = create_handle_repos;
+  base_class->create_channel_managers = create_channel_managers;
+
+  param_spec = g_param_spec_object ("channel-manager", "Channel manager",
+      "The channel manager", TP_TESTS_TYPE_SIMPLE_CHANNEL_MANAGER,
+      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_CHANNEL_MANAGER, param_spec);
+}
diff --git a/tests/lib/telepathy/contactlist/echo-conn.h b/tests/lib/telepathy/contactlist/echo-conn.h
new file mode 100644
index 0000000..ffc8f4d
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/echo-conn.h
@@ -0,0 +1,56 @@
+/*
+ * conn.h - header for an example connection
+ *
+ * Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef __TP_TESTS_ECHO_CONN_H__
+#define __TP_TESTS_ECHO_CONN_H__
+
+#include <glib-object.h>
+
+#include "contacts-conn.h"
+
+G_BEGIN_DECLS
+
+typedef struct _TpTestsEchoConnection TpTestsEchoConnection;
+typedef struct _TpTestsEchoConnectionClass TpTestsEchoConnectionClass;
+typedef struct _TpTestsEchoConnectionPrivate TpTestsEchoConnectionPrivate;
+
+struct _TpTestsEchoConnectionClass {
+    TpTestsContactsConnectionClass parent_class;
+};
+
+struct _TpTestsEchoConnection {
+    TpTestsContactsConnection parent;
+
+    TpTestsEchoConnectionPrivate *priv;
+};
+
+GType tp_tests_echo_connection_get_type (void);
+
+/* TYPE MACROS */
+#define TP_TESTS_TYPE_ECHO_CONNECTION \
+  (tp_tests_echo_connection_get_type ())
+#define TP_TESTS_ECHO_CONNECTION(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_ECHO_CONNECTION, \
+                              TpTestsEchoConnection))
+#define TP_TESTS_ECHO_CONNECTION_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_ECHO_CONNECTION, \
+                           TpTestsEchoConnectionClass))
+#define TP_TESTS_IS_ECHO_CONNECTION(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_ECHO_CONNECTION))
+#define TP_TESTS_IS_ECHO_CONNECTION_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_ECHO_CONNECTION))
+#define TP_TESTS_ECHO_CONNECTION_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_ECHO_CONNECTION, \
+                              TpTestsEchoConnectionClass))
+
+G_END_DECLS
+
+#endif
diff --git a/tests/lib/telepathy/contactlist/echo-im-manager.c 
b/tests/lib/telepathy/contactlist/echo-im-manager.c
new file mode 100644
index 0000000..8c38186
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/echo-im-manager.c
@@ -0,0 +1,382 @@
+/*
+ * im-manager.c - an example channel manager for channels talking to a
+ * particular contact. Similar code is used for 1-1 IM channels in many
+ * protocols (IRC private messages ("/query"), XMPP IM etc.)
+ *
+ * Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "config.h"
+
+#include "echo-im-manager.h"
+
+#include <dbus/dbus-glib.h>
+
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
+
+#include "echo-chan.h"
+
+static void channel_manager_iface_init (gpointer, gpointer);
+
+G_DEFINE_TYPE_WITH_CODE (TpTestsEchoImManager,
+    tp_tests_echo_im_manager,
+    G_TYPE_OBJECT,
+    G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER,
+      channel_manager_iface_init))
+
+/* type definition stuff */
+
+enum
+{
+  PROP_CONNECTION = 1,
+  N_PROPS
+};
+
+struct _TpTestsEchoImManagerPrivate
+{
+  TpBaseConnection *conn;
+
+  /* GUINT_TO_POINTER (handle) => TpTestsEchoChannel */
+  GHashTable *channels;
+  gulong status_changed_id;
+};
+
+static void
+tp_tests_echo_im_manager_init (TpTestsEchoImManager *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TESTS_TYPE_ECHO_IM_MANAGER,
+      TpTestsEchoImManagerPrivate);
+
+  self->priv->channels = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+      NULL, g_object_unref);
+}
+
+static void tp_tests_echo_im_manager_close_all (TpTestsEchoImManager *self);
+
+static void
+dispose (GObject *object)
+{
+  TpTestsEchoImManager *self = TP_TESTS_ECHO_IM_MANAGER (object);
+
+  tp_tests_echo_im_manager_close_all (self);
+  g_assert (self->priv->channels == NULL);
+
+  ((GObjectClass *) tp_tests_echo_im_manager_parent_class)->dispose (object);
+}
+
+static void
+get_property (GObject *object,
+              guint property_id,
+              GValue *value,
+              GParamSpec *pspec)
+{
+  TpTestsEchoImManager *self = TP_TESTS_ECHO_IM_MANAGER (object);
+
+  switch (property_id)
+    {
+    case PROP_CONNECTION:
+      g_value_set_object (value, self->priv->conn);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+set_property (GObject *object,
+              guint property_id,
+              const GValue *value,
+              GParamSpec *pspec)
+{
+  TpTestsEchoImManager *self = TP_TESTS_ECHO_IM_MANAGER (object);
+
+  switch (property_id)
+    {
+    case PROP_CONNECTION:
+      /* We don't ref the connection, because it owns a reference to the
+       * channel manager, and it guarantees that the manager's lifetime is
+       * less than its lifetime */
+      self->priv->conn = g_value_get_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+status_changed_cb (TpBaseConnection *conn,
+                   guint status,
+                   guint reason,
+                   TpTestsEchoImManager *self)
+{
+  if (status == TP_CONNECTION_STATUS_DISCONNECTED)
+    tp_tests_echo_im_manager_close_all (self);
+}
+
+static void
+constructed (GObject *object)
+{
+  TpTestsEchoImManager *self = TP_TESTS_ECHO_IM_MANAGER (object);
+  void (*chain_up) (GObject *) =
+      ((GObjectClass *) tp_tests_echo_im_manager_parent_class)->constructed;
+
+  if (chain_up != NULL)
+    {
+      chain_up (object);
+    }
+
+  self->priv->status_changed_id = g_signal_connect (self->priv->conn,
+      "status-changed", (GCallback) status_changed_cb, self);
+}
+
+static void
+tp_tests_echo_im_manager_class_init (TpTestsEchoImManagerClass *klass)
+{
+  GParamSpec *param_spec;
+  GObjectClass *object_class = (GObjectClass *) klass;
+
+  object_class->constructed = constructed;
+  object_class->dispose = dispose;
+  object_class->get_property = get_property;
+  object_class->set_property = set_property;
+
+  param_spec = g_param_spec_object ("connection", "Connection object",
+      "The connection that owns this channel manager",
+      TP_TYPE_BASE_CONNECTION,
+      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+  g_type_class_add_private (klass, sizeof (TpTestsEchoImManagerPrivate));
+}
+
+static void
+tp_tests_echo_im_manager_close_all (TpTestsEchoImManager *self)
+{
+  if (self->priv->channels != NULL)
+    {
+      GHashTable *tmp = self->priv->channels;
+
+      self->priv->channels = NULL;
+      g_hash_table_unref (tmp);
+    }
+
+  if (self->priv->status_changed_id != 0)
+    {
+      g_signal_handler_disconnect (self->priv->conn,
+          self->priv->status_changed_id);
+      self->priv->status_changed_id = 0;
+    }
+}
+
+static void
+tp_tests_echo_im_manager_foreach_channel (TpChannelManager *iface,
+                                         TpExportableChannelFunc callback,
+                                         gpointer user_data)
+{
+  TpTestsEchoImManager *self = TP_TESTS_ECHO_IM_MANAGER (iface);
+  GHashTableIter iter;
+  gpointer handle, channel;
+
+  g_hash_table_iter_init (&iter, self->priv->channels);
+
+  while (g_hash_table_iter_next (&iter, &handle, &channel))
+    {
+      callback (TP_EXPORTABLE_CHANNEL (channel), user_data);
+    }
+}
+
+static void
+channel_closed_cb (TpTestsEchoChannel *chan,
+                   TpTestsEchoImManager *self)
+{
+  tp_channel_manager_emit_channel_closed_for_object (self,
+      TP_EXPORTABLE_CHANNEL (chan));
+
+  if (self->priv->channels != NULL)
+    {
+      TpHandle handle;
+      gboolean really_destroyed;
+
+      g_object_get (chan,
+          "handle", &handle,
+          "channel-destroyed", &really_destroyed,
+          NULL);
+
+      /* Re-announce the channel if it's not yet ready to go away (pending
+       * messages) */
+      if (really_destroyed)
+        {
+          g_hash_table_remove (self->priv->channels,
+              GUINT_TO_POINTER (handle));
+        }
+      else
+        {
+          tp_channel_manager_emit_new_channel (self,
+              TP_EXPORTABLE_CHANNEL (chan), NULL);
+        }
+    }
+}
+
+static void
+new_channel (TpTestsEchoImManager *self,
+             TpHandle handle,
+             TpHandle initiator,
+             gpointer request_token)
+{
+  TpTestsEchoChannel *chan;
+  gchar *object_path;
+  GSList *requests = NULL;
+
+  object_path = g_strdup_printf ("%s/EchoChannel%u",
+      tp_base_connection_get_object_path (self->priv->conn), handle);
+
+  chan = g_object_new (TP_TESTS_TYPE_ECHO_CHANNEL,
+      "connection", self->priv->conn,
+      "object-path", object_path,
+      "handle", handle,
+      "initiator-handle", initiator,
+      NULL);
+
+  g_free (object_path);
+
+  g_signal_connect (chan, "closed", (GCallback) channel_closed_cb, self);
+
+  /* self->priv->channels takes ownership of 'chan' */
+  g_hash_table_insert (self->priv->channels, GUINT_TO_POINTER (handle), chan);
+
+  if (request_token != NULL)
+    requests = g_slist_prepend (requests, request_token);
+
+  tp_channel_manager_emit_new_channel (self, TP_EXPORTABLE_CHANNEL (chan),
+      requests);
+  g_slist_free (requests);
+}
+
+static const gchar * const fixed_properties[] = {
+    TP_PROP_CHANNEL_CHANNEL_TYPE,
+    TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
+    NULL
+};
+
+static const gchar * const allowed_properties[] = {
+    TP_PROP_CHANNEL_TARGET_HANDLE,
+    TP_PROP_CHANNEL_TARGET_ID,
+    NULL
+};
+
+static void
+tp_tests_echo_im_manager_foreach_channel_class (TpChannelManager *manager,
+    TpChannelManagerChannelClassFunc func,
+    gpointer user_data)
+{
+  GHashTable *table = tp_asv_new (
+      TP_PROP_CHANNEL_CHANNEL_TYPE,
+          G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_TEXT,
+      TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
+      NULL);
+
+
+  func (manager, table, allowed_properties, user_data);
+
+  g_hash_table_unref (table);
+}
+
+static gboolean
+tp_tests_echo_im_manager_request (TpTestsEchoImManager *self,
+                                 gpointer request_token,
+                                 GHashTable *request_properties,
+                                 gboolean require_new)
+{
+  TpHandle handle;
+  TpTestsEchoChannel *chan;
+  GError *error = NULL;
+
+  if (tp_strdiff (tp_asv_get_string (request_properties,
+          TP_PROP_CHANNEL_CHANNEL_TYPE),
+      TP_IFACE_CHANNEL_TYPE_TEXT))
+    {
+      return FALSE;
+    }
+
+  if (tp_asv_get_uint32 (request_properties,
+      TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL) != TP_HANDLE_TYPE_CONTACT)
+    {
+      return FALSE;
+    }
+
+  handle = tp_asv_get_uint32 (request_properties,
+      TP_PROP_CHANNEL_TARGET_HANDLE, NULL);
+  g_assert (handle != 0);
+
+  if (tp_channel_manager_asv_has_unknown_properties (request_properties,
+        fixed_properties, allowed_properties, &error))
+    {
+      goto error;
+    }
+
+  chan = g_hash_table_lookup (self->priv->channels, GUINT_TO_POINTER (handle));
+
+  if (chan == NULL)
+    {
+      new_channel (self, handle,
+          tp_base_connection_get_self_handle (self->priv->conn),
+          request_token);
+    }
+  else if (require_new)
+    {
+      g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
+          "An echo channel to contact #%u already exists", handle);
+      goto error;
+    }
+  else
+    {
+      tp_channel_manager_emit_request_already_satisfied (self,
+          request_token, TP_EXPORTABLE_CHANNEL (chan));
+    }
+
+  return TRUE;
+
+error:
+  tp_channel_manager_emit_request_failed (self, request_token,
+      error->domain, error->code, error->message);
+  g_error_free (error);
+  return TRUE;
+}
+
+static gboolean
+tp_tests_echo_im_manager_create_channel (TpChannelManager *manager,
+                                        gpointer request_token,
+                                        GHashTable *request_properties)
+{
+    return tp_tests_echo_im_manager_request (TP_TESTS_ECHO_IM_MANAGER (manager),
+        request_token, request_properties, TRUE);
+}
+
+static gboolean
+tp_tests_echo_im_manager_ensure_channel (TpChannelManager *manager,
+                                        gpointer request_token,
+                                        GHashTable *request_properties)
+{
+    return tp_tests_echo_im_manager_request (TP_TESTS_ECHO_IM_MANAGER (manager),
+        request_token, request_properties, FALSE);
+}
+
+static void
+channel_manager_iface_init (gpointer g_iface,
+                            gpointer iface_data G_GNUC_UNUSED)
+{
+  TpChannelManagerIface *iface = g_iface;
+
+  iface->foreach_channel = tp_tests_echo_im_manager_foreach_channel;
+  iface->foreach_channel_class = tp_tests_echo_im_manager_foreach_channel_class;
+  iface->create_channel = tp_tests_echo_im_manager_create_channel;
+  iface->ensure_channel = tp_tests_echo_im_manager_ensure_channel;
+  /* In this channel manager, Request has the same semantics as Ensure */
+  iface->request_channel = tp_tests_echo_im_manager_ensure_channel;
+}
diff --git a/tests/lib/telepathy/contactlist/echo-im-manager.h 
b/tests/lib/telepathy/contactlist/echo-im-manager.h
new file mode 100644
index 0000000..a33b83e
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/echo-im-manager.h
@@ -0,0 +1,54 @@
+/*
+ * im-manager.h - header for an example channel manager
+ *
+ * Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef __TP_TESTS_ECHO_IM_MANAGER_H__
+#define __TP_TESTS_ECHO_IM_MANAGER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _TpTestsEchoImManager TpTestsEchoImManager;
+typedef struct _TpTestsEchoImManagerClass TpTestsEchoImManagerClass;
+typedef struct _TpTestsEchoImManagerPrivate TpTestsEchoImManagerPrivate;
+
+struct _TpTestsEchoImManagerClass {
+    GObjectClass parent_class;
+};
+
+struct _TpTestsEchoImManager {
+    GObject parent;
+
+    TpTestsEchoImManagerPrivate *priv;
+};
+
+GType tp_tests_echo_im_manager_get_type (void);
+
+/* TYPE MACROS */
+#define TP_TESTS_TYPE_ECHO_IM_MANAGER \
+  (tp_tests_echo_im_manager_get_type ())
+#define TP_TESTS_ECHO_IM_MANAGER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_ECHO_IM_MANAGER, \
+                              TpTestsEchoImManager))
+#define TP_TESTS_ECHO_IM_MANAGER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_ECHO_IM_MANAGER, \
+                           TpTestsEchoImManagerClass))
+#define TP_TESTS_IS_ECHO_IM_MANAGER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_ECHO_IM_MANAGER))
+#define TP_TESTS_IS_ECHO_IM_MANAGER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_ECHO_IM_MANAGER))
+#define TP_TESTS_ECHO_IM_MANAGER_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_ECHO_IM_MANAGER, \
+                              TpTestsEchoImManagerClass))
+
+G_END_DECLS
+
+#endif
diff --git a/tests/lib/telepathy/contactlist/file-transfer-chan.c 
b/tests/lib/telepathy/contactlist/file-transfer-chan.c
new file mode 100644
index 0000000..92520f4
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/file-transfer-chan.c
@@ -0,0 +1,763 @@
+/*
+ * file-transfer-chan.c - Simple file transfer channel
+ *
+ * Copyright (C) 2010-2011 Morten Mjelva <morten mjelva gmail com>
+ * Copyright (C) 2010-2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "config.h"
+
+#include "file-transfer-chan.h"
+#include "util.h"
+#include "debug.h"
+
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
+
+#include <glib/gstdio.h>
+
+static void file_transfer_iface_init (gpointer iface, gpointer data);
+
+G_DEFINE_TYPE_WITH_CODE (TpTestsFileTransferChannel,
+    tp_tests_file_transfer_channel,
+    TP_TYPE_BASE_CHANNEL,
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_FILE_TRANSFER,
+      file_transfer_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA,
+      NULL);
+    )
+
+enum /* properties */
+{
+  PROP_AVAILABLE_SOCKET_TYPES = 1,
+  PROP_CONTENT_TYPE,
+  PROP_CONTENT_HASH,
+  PROP_CONTENT_HASH_TYPE,
+  PROP_DATE,
+  PROP_DESCRIPTION,
+  PROP_FILENAME,
+  PROP_INITIAL_OFFSET,
+  PROP_SIZE,
+  PROP_STATE,
+  PROP_TRANSFERRED_BYTES,
+  PROP_URI,
+  PROP_SERVICE_NAME,
+  PROP_METADATA,
+  N_PROPS,
+};
+
+struct _TpTestsFileTransferChannelPrivate {
+    /* Exposed properties */
+    gchar *content_type;
+    guint64 date;
+    gchar *description;
+    gchar *filename;
+    guint64 size;
+    TpFileTransferState state;
+    guint64 transferred_bytes;
+    gchar *uri;
+    gchar *service_name;
+    GHashTable *metadata;
+
+    /* Hidden properties */
+    TpFileHashType content_hash_type;
+    gchar *content_hash;
+    GHashTable *available_socket_types;
+    gint64 initial_offset;
+
+    /* Accepting side */
+    GSocketService *service;
+    GValue *access_control_param;
+
+    /* Offering side */
+    TpSocketAddressType address_type;
+    GValue *address;
+    gchar *unix_address;
+    gchar *unix_tmpdir;
+    guint connection_id;
+    TpSocketAccessControl access_control;
+
+    guint timer_id;
+};
+
+static void
+tp_tests_file_transfer_channel_init (TpTestsFileTransferChannel *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
+      TP_TESTS_TYPE_FILE_TRANSFER_CHANNEL, TpTestsFileTransferChannelPrivate);
+}
+
+static void
+create_available_socket_types (TpTestsFileTransferChannel *self)
+{
+  TpSocketAccessControl access_control;
+  GArray *unix_tab;
+
+  g_assert (self->priv->available_socket_types == NULL);
+  self->priv->available_socket_types = g_hash_table_new_full (NULL, NULL,
+      NULL, _tp_destroy_socket_control_list);
+
+  /* SocketAddressTypeUnix */
+  unix_tab = g_array_sized_new (FALSE, FALSE, sizeof (TpSocketAccessControl),
+      1);
+  access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST;
+  g_array_append_val (unix_tab, access_control);
+
+  g_hash_table_insert (self->priv->available_socket_types,
+      GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_UNIX), unix_tab);
+}
+
+static GObject *
+constructor (GType type,
+    guint n_props,
+    GObjectConstructParam *props)
+{
+  GObject *object =
+    G_OBJECT_CLASS (tp_tests_file_transfer_channel_parent_class)->constructor
+    (type, n_props, props);
+  TpTestsFileTransferChannel *self = TP_TESTS_FILE_TRANSFER_CHANNEL (object);
+
+  self->priv->state = TP_FILE_TRANSFER_STATE_PENDING;
+
+  if (self->priv->available_socket_types == NULL)
+    create_available_socket_types (self);
+
+  tp_base_channel_register (TP_BASE_CHANNEL (self));
+
+  return object;
+}
+
+static void
+dispose (GObject *object)
+{
+  TpTestsFileTransferChannel *self = TP_TESTS_FILE_TRANSFER_CHANNEL (object);
+
+  if (self->priv->timer_id != 0)
+    {
+      g_source_remove (self->priv->timer_id);
+      self->priv->timer_id = 0;
+    }
+
+  g_free (self->priv->content_hash);
+  g_free (self->priv->content_type);
+  g_free (self->priv->description);
+  g_free (self->priv->filename);
+  g_free (self->priv->uri);
+  g_free (self->priv->service_name);
+
+  tp_clear_pointer (&self->priv->address, tp_g_value_slice_free);
+  tp_clear_pointer (&self->priv->available_socket_types, g_hash_table_unref);
+  tp_clear_pointer (&self->priv->access_control_param, tp_g_value_slice_free);
+  tp_clear_pointer (&self->priv->metadata, g_hash_table_unref);
+
+  if (self->priv->unix_address != NULL)
+    g_unlink (self->priv->unix_address);
+
+  tp_clear_pointer (&self->priv->unix_address, g_free);
+
+  if (self->priv->unix_tmpdir != NULL)
+    g_rmdir (self->priv->unix_tmpdir);
+
+  tp_clear_pointer (&self->priv->unix_tmpdir, g_free);
+
+  ((GObjectClass *) tp_tests_file_transfer_channel_parent_class)->dispose (
+      object);
+}
+
+static void
+get_property (GObject *object,
+    guint property_id,
+    GValue *value,
+    GParamSpec *pspec)
+{
+  TpTestsFileTransferChannel *self = (TpTestsFileTransferChannel *) object;
+
+  switch (property_id)
+    {
+      case PROP_AVAILABLE_SOCKET_TYPES:
+        g_value_set_boxed (value, self->priv->available_socket_types);
+        break;
+
+      case PROP_CONTENT_HASH:
+        g_value_set_string (value, self->priv->content_hash);
+
+      case PROP_CONTENT_HASH_TYPE:
+        g_value_set_uint (value, self->priv->content_hash_type);
+        break;
+
+      case PROP_CONTENT_TYPE:
+        g_value_set_string (value, self->priv->content_type);
+        break;
+
+      case PROP_DATE:
+        g_value_set_uint64 (value, self->priv->date);
+        break;
+
+      case PROP_DESCRIPTION:
+        g_value_set_string (value, self->priv->description);
+        break;
+
+      case PROP_FILENAME:
+        g_value_set_string (value, self->priv->filename);
+        break;
+
+      case PROP_INITIAL_OFFSET:
+        g_value_set_uint64 (value, self->priv->initial_offset);
+        break;
+
+      case PROP_SIZE:
+        g_value_set_uint64 (value, self->priv->size);
+        break;
+
+      case PROP_STATE:
+        g_value_set_uint (value, self->priv->state);
+        break;
+
+      case PROP_TRANSFERRED_BYTES:
+        g_value_set_uint64 (value, self->priv->transferred_bytes);
+        break;
+
+      case PROP_URI:
+        g_value_set_string (value, self->priv->uri);
+        break;
+
+      case PROP_SERVICE_NAME:
+        g_value_set_string (value, self->priv->service_name);
+        break;
+
+      case PROP_METADATA:
+        g_value_set_boxed (value, self->priv->metadata);
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
+set_property (GObject *object,
+    guint property_id,
+    const GValue *value,
+    GParamSpec *pspec)
+{
+  TpTestsFileTransferChannel *self = (TpTestsFileTransferChannel *) object;
+
+  switch (property_id)
+    {
+      case PROP_AVAILABLE_SOCKET_TYPES:
+        self->priv->available_socket_types = g_value_dup_boxed (value);
+        break;
+
+      case PROP_CONTENT_HASH:
+        self->priv->content_hash = g_value_dup_string (value);
+        break;
+
+      case PROP_CONTENT_HASH_TYPE:
+        break;
+
+      case PROP_CONTENT_TYPE:
+        self->priv->content_type = g_value_dup_string (value);
+        break;
+
+      case PROP_DATE:
+        self->priv->date = g_value_get_uint64 (value);
+        break;
+
+      case PROP_DESCRIPTION:
+        self->priv->description = g_value_dup_string (value);
+        break;
+
+      case PROP_FILENAME:
+        self->priv->filename = g_value_dup_string (value);
+        break;
+
+      case PROP_INITIAL_OFFSET:
+        self->priv->initial_offset = g_value_get_uint64 (value);
+        break;
+
+      case PROP_SIZE:
+        self->priv->size = g_value_get_uint64 (value);
+        break;
+
+      case PROP_STATE:
+        self->priv->state = g_value_get_uint (value);
+        break;
+
+      case PROP_TRANSFERRED_BYTES:
+        self->priv->transferred_bytes = g_value_get_uint64 (value);
+        break;
+
+      case PROP_URI:
+        self->priv->uri = g_value_dup_string (value);
+        break;
+
+      case PROP_SERVICE_NAME:
+        self->priv->service_name = g_value_dup_string (value);
+        break;
+
+      case PROP_METADATA:
+        self->priv->metadata = g_value_dup_boxed (value);
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
+channel_close (TpBaseChannel *self)
+{
+  g_print ("entered channel_close");
+  tp_base_channel_destroyed (self);
+}
+
+static void
+fill_immutable_properties (TpBaseChannel *self,
+    GHashTable *properties)
+{
+  TpBaseChannelClass *klass = TP_BASE_CHANNEL_CLASS (
+      tp_tests_file_transfer_channel_parent_class);
+
+  klass->fill_immutable_properties (self, properties);
+
+  tp_dbus_properties_mixin_fill_properties_hash (
+      G_OBJECT (self), properties,
+      TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "AvailableSocketTypes",
+      TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentType",
+      TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Filename",
+      TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Size",
+      TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Description",
+      TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Date",
+      TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA, "ServiceName",
+      TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA, "Metadata",
+      NULL);
+
+  /* URI is immutable only for outgoing transfers */
+  if (tp_base_channel_is_requested (self))
+    {
+      tp_dbus_properties_mixin_fill_properties_hash (G_OBJECT (self),
+          properties,
+          TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "URI", NULL);
+    }
+}
+
+static void
+change_state (TpTestsFileTransferChannel *self,
+    TpFileTransferState state,
+    TpFileTransferStateChangeReason reason)
+{
+  self->priv->state = state;
+
+  tp_svc_channel_type_file_transfer_emit_file_transfer_state_changed (self,
+      state, reason);
+}
+
+/* This function imitates the beginning of a filetransfer. It sets the state
+ * to open, and connects to the "incoming" signal of the GSocketService.
+ */
+static gboolean
+start_file_transfer (gpointer data)
+{
+  TpTestsFileTransferChannel *self = (TpTestsFileTransferChannel *) data;
+
+  DEBUG ("Setting TP_FILE_TRANSFER_STATE_OPEN");
+  change_state (self, TP_FILE_TRANSFER_STATE_OPEN,
+      TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED);
+
+  g_object_notify ((GObject *) data, "state");
+  DEBUG ("Fired state signal");
+
+//  g_signal_connect (self->priv->service, "incoming", G_CALLBACK
+//      (incoming_file_transfer_cb));
+
+  self->priv->timer_id = 0;
+  return FALSE;
+}
+
+static gboolean
+check_address_type (TpTestsFileTransferChannel *self,
+    TpSocketAddressType address_type,
+    TpSocketAccessControl access_control)
+{
+  GArray *arr;
+  guint i;
+
+  arr = g_hash_table_lookup (self->priv->available_socket_types,
+      GUINT_TO_POINTER (address_type));
+  if (arr == NULL)
+    return FALSE;
+
+  for (i = 0; i < arr->len; i++)
+    {
+      if (g_array_index (arr, TpSocketAccessControl, i) == access_control)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+service_incoming_cb (GSocketService *service,
+    GSocketConnection *connection,
+    GObject *source_object,
+    gpointer user_data)
+{
+  TpTestsFileTransferChannel *self = user_data;
+  GError *error = NULL;
+
+  DEBUG ("Servicing incoming connection");
+  if (self->priv->access_control == TP_SOCKET_ACCESS_CONTROL_CREDENTIALS)
+    {
+      GCredentials *creds;
+      guchar byte;
+
+      /* TODO: Async version */
+      creds = tp_unix_connection_receive_credentials_with_byte (
+          connection, &byte, NULL, &error);
+      g_assert_no_error (error);
+
+      g_assert_cmpuint (byte, ==,
+          g_value_get_uchar (self->priv->access_control_param));
+      g_object_unref (creds);
+    }
+  else if (self->priv->access_control == TP_SOCKET_ACCESS_CONTROL_PORT)
+    {
+      GSocketAddress *addr;
+      guint16 port;
+
+      addr = g_socket_connection_get_remote_address (connection, &error);
+      g_assert_no_error (error);
+
+      port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
+
+      g_assert_cmpuint (port, ==,
+          g_value_get_uint (self->priv->access_control_param));
+
+      g_object_unref (addr);
+    }
+}
+
+static void
+file_transfer_provide_file (TpSvcChannelTypeFileTransfer *iface,
+    TpSocketAddressType address_type,
+    TpSocketAccessControl access_control,
+    const GValue *access_control_param,
+    DBusGMethodInvocation *context)
+{
+  TpTestsFileTransferChannel *self = (TpTestsFileTransferChannel *) iface;
+  TpBaseChannel *base_chan = (TpBaseChannel *) iface;
+  GError *error = NULL;
+
+  if (tp_base_channel_is_requested (base_chan) != TRUE)
+    {
+      g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+          "File transfer is not outgoing. Cannot offer file");
+      goto fail;
+    }
+
+  if (self->priv->state != TP_FILE_TRANSFER_STATE_PENDING &&
+      self->priv->state != TP_FILE_TRANSFER_STATE_ACCEPTED)
+    {
+      g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+          "File transfer is not pending or accepted. Cannot offer file");
+      goto fail;
+    }
+
+  if (self->priv->address != NULL)
+    {
+      g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
+          "ProvideFile has already been called for this channel");
+      goto fail;
+    }
+
+  if (!check_address_type (self, address_type, access_control))
+    {
+      g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+          "Address type %i is not supported with access control %i",
+          address_type, access_control);
+      goto fail;
+    }
+
+  self->priv->address = _tp_create_local_socket (address_type, access_control,
+      &self->priv->service, &self->priv->unix_address,
+      &self->priv->unix_tmpdir, &error);
+
+  if (self->priv->address == NULL)
+      {
+        g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
+            "Could not set up local socket");
+        goto fail;
+      }
+
+  self->priv->address_type = address_type;
+  self->priv->access_control = access_control;
+
+  DEBUG ("Waiting 500ms and setting state to OPEN");
+  self->priv->timer_id = g_timeout_add (500, start_file_transfer, self);
+
+  // connect to self->priv->service incoming signal
+  // when the signal returns, add x bytes per n seconds using timeout
+  // then close the socket
+  // g_output_stream_write_async
+
+  tp_svc_channel_type_file_transfer_return_from_provide_file (context,
+      self->priv->address);
+
+  return;
+
+fail:
+  dbus_g_method_return_error (context, error);
+  g_error_free (error);
+}
+
+static void
+file_transfer_accept_file (TpSvcChannelTypeFileTransfer *iface,
+    TpSocketAddressType address_type,
+    TpSocketAccessControl access_control,
+    const GValue *access_control_param,
+    guint64 offset,
+    DBusGMethodInvocation *context)
+{
+  TpTestsFileTransferChannel *self = (TpTestsFileTransferChannel *) iface;
+  TpBaseChannel *base_chan = (TpBaseChannel *) iface;
+  GError *error = NULL;
+  GValue *address;
+
+  if (tp_base_channel_is_requested (base_chan) == TRUE)
+    {
+      g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+          "File transfer is not incoming. Cannot accept file");
+      goto fail;
+    }
+
+  if (self->priv->state != TP_FILE_TRANSFER_STATE_PENDING)
+    {
+      g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+          "File transfer is not in the pending state");
+      goto fail;
+    }
+
+  if (!check_address_type (self, address_type, access_control))
+    {
+      g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+          "Address type %i is not supported with access control %i",
+          address_type, access_control);
+      goto fail;
+    }
+
+  address = _tp_create_local_socket (address_type, access_control,
+      &self->priv->service, &self->priv->unix_address,
+      &self->priv->unix_tmpdir, &error);
+
+  tp_g_signal_connect_object (self->priv->service, "incoming",
+      G_CALLBACK (service_incoming_cb), self, 0);
+
+  self->priv->access_control = access_control;
+  self->priv->access_control_param = tp_g_value_slice_dup (
+      access_control_param);
+
+  DEBUG ("Setting TP_FILE_TRANSFER_STATE_ACCEPTED");
+  change_state (self, TP_FILE_TRANSFER_STATE_ACCEPTED,
+      TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED);
+
+  DEBUG ("Waiting 500ms and setting state to OPEN");
+  self->priv->timer_id = g_timeout_add (500, start_file_transfer, self);
+
+  tp_svc_channel_type_file_transfer_return_from_accept_file (context,
+      address);
+
+  tp_clear_pointer (&address, tp_g_value_slice_free);
+
+  return;
+
+fail:
+  dbus_g_method_return_error (context, error);
+  g_error_free (error);
+}
+
+static void
+tp_tests_file_transfer_channel_class_init (
+    TpTestsFileTransferChannelClass *klass)
+{
+  GObjectClass *object_class = (GObjectClass *) klass;
+  TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (klass);
+  GParamSpec *param_spec;
+
+  static TpDBusPropertiesMixinPropImpl file_transfer_props[] = {
+      { "AvailableSocketTypes", "available-socket-types", NULL },
+      { "ContentType", "content-type", NULL },
+      { "Date", "date", NULL },
+      { "Description", "description", NULL },
+      { "Filename", "filename", NULL },
+      { "Size", "size", NULL },
+      { "State", "state", NULL },
+      { "TransferredBytes", "transferred-bytes", NULL },
+      { "URI", "uri", NULL },
+      { NULL }
+  };
+
+  static TpDBusPropertiesMixinPropImpl metadata_props[] = {
+      { "ServiceName", "service-name", NULL },
+      { "Metadata", "metadata", NULL },
+      { NULL }
+  };
+
+  object_class->constructor = constructor;
+  object_class->get_property = get_property;
+  object_class->set_property = set_property;
+  object_class->dispose = dispose;
+
+  base_class->channel_type = TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER;
+  base_class->target_handle_type = TP_HANDLE_TYPE_CONTACT;
+
+  base_class->close = channel_close;
+  base_class->fill_immutable_properties = fill_immutable_properties;
+
+  param_spec = g_param_spec_boxed ("available-socket-types",
+      "AvailableSocketTypes",
+      "The AvailableSocketTypes property of this channel",
+      TP_HASH_TYPE_SUPPORTED_SOCKET_MAP,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_AVAILABLE_SOCKET_TYPES,
+      param_spec);
+
+  param_spec = g_param_spec_string ("content-type",
+      "ContentType",
+      "The ContentType property of this channel",
+      NULL,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_CONTENT_TYPE,
+      param_spec);
+
+  param_spec = g_param_spec_uint64 ("date",
+      "Date",
+      "The Date property of this channel",
+      0, G_MAXUINT64, 0,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_DATE,
+      param_spec);
+
+  param_spec = g_param_spec_string ("description",
+      "Description",
+      "The Description property of this channel",
+      NULL,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_DESCRIPTION,
+      param_spec);
+
+  param_spec = g_param_spec_string ("filename",
+      "Filename",
+      "The Filename property of this channel",
+      NULL,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_FILENAME,
+      param_spec);
+
+  param_spec = g_param_spec_uint64 ("initial-offset",
+      "InitialOffset",
+      "The InitialOffset property of this channel",
+      0, G_MAXUINT64, 0,
+      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_INITIAL_OFFSET,
+      param_spec);
+
+  param_spec = g_param_spec_uint64 ("size",
+      "Size",
+      "The Size property of this channel",
+      0, G_MAXUINT64, 0,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_SIZE,
+      param_spec);
+
+  param_spec = g_param_spec_uint ("state",
+      "State",
+      "The State property of this channel",
+      0, TP_NUM_FILE_TRANSFER_STATES, TP_FILE_TRANSFER_STATE_NONE,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_STATE,
+      param_spec);
+
+  param_spec = g_param_spec_uint64 ("transferred-bytes",
+      "TransferredBytes",
+      "The TransferredBytes property of this channel",
+      0, G_MAXUINT64, 0,
+      G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_TRANSFERRED_BYTES,
+      param_spec);
+
+  param_spec = g_param_spec_string ("uri",
+      "URI",
+      "The URI property of this channel",
+      NULL,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_URI,
+      param_spec);
+
+  param_spec = g_param_spec_string ("service-name",
+      "ServiceName",
+      "The Metadata.ServiceName property of this channel",
+      "",
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_SERVICE_NAME,
+      param_spec);
+
+  param_spec = g_param_spec_boxed ("metadata",
+      "Metadata",
+      "The Metadata.Metadata property of this channel",
+      TP_HASH_TYPE_METADATA,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_METADATA,
+      param_spec);
+
+  tp_dbus_properties_mixin_implement_interface (object_class,
+      TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER,
+      tp_dbus_properties_mixin_getter_gobject_properties, NULL,
+      file_transfer_props);
+
+  tp_dbus_properties_mixin_implement_interface (object_class,
+      TP_IFACE_QUARK_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA,
+      tp_dbus_properties_mixin_getter_gobject_properties, NULL,
+      metadata_props);
+
+  g_type_class_add_private (object_class,
+      sizeof (TpTestsFileTransferChannelPrivate));
+}
+
+static void
+file_transfer_iface_init (gpointer iface, gpointer data)
+{
+  TpSvcChannelTypeFileTransferClass *klass = iface;
+
+#define IMPLEMENT(x) tp_svc_channel_type_file_transfer_implement_##x (klass, \
+    file_transfer_##x)
+  IMPLEMENT(accept_file);
+  IMPLEMENT(provide_file);
+#undef IMPLEMENT
+}
+
+/* Return the address of the file transfer's socket */
+GSocketAddress *
+tp_tests_file_transfer_channel_get_server_address (
+    TpTestsFileTransferChannel *self)
+{
+  GSocketAddress *address;
+  GError *error = NULL;
+
+  g_assert (self->priv->address != NULL);
+
+  address = tp_g_socket_address_from_variant (self->priv->address_type,
+      self->priv->address, &error);
+
+  if (error != NULL)
+    {
+      g_printf ("%s\n", error->message);
+    }
+
+  return address;
+}
diff --git a/tests/lib/telepathy/contactlist/file-transfer-chan.h 
b/tests/lib/telepathy/contactlist/file-transfer-chan.h
new file mode 100644
index 0000000..19d6c98
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/file-transfer-chan.h
@@ -0,0 +1,63 @@
+/*
+ * stream-tube-chan.h - Simple stream tube channel
+ *
+ * Copyright (C) 2010-2011 Morten Mjelva <morten mjelva gmail com>
+ * Copyright (C) 2010-2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef __TP_FILE_TRANSFER_CHAN_H__
+#define __TP_FILE_TRANSFER_CHAN_H__
+
+#include <glib-object.h>
+#include <telepathy-glib/telepathy-glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _TpTestsFileTransferChannel TpTestsFileTransferChannel;
+typedef struct _TpTestsFileTransferChannelClass TpTestsFileTransferChannelClass;
+typedef struct _TpTestsFileTransferChannelPrivate TpTestsFileTransferChannelPrivate;
+
+GType tp_tests_file_transfer_channel_get_type (void);
+
+#define TP_TESTS_TYPE_FILE_TRANSFER_CHANNEL \
+    (tp_tests_file_transfer_channel_get_type ())
+#define TP_TESTS_FILE_TRANSFER_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), TP_TESTS_TYPE_FILE_TRANSFER_CHANNEL, \
+                               TpTestsFileTransferChannel))
+#define TP_TESTS_FILE_TRANSFER_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), TP_TESTS_TYPE_FILE_TRANSFER_CHANNEL, \
+                            TpTestsFileTransferChannelClass))
+#define TP_TESTS_IS_FILE_TRANSFER_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TP_TESTS_TYPE_FILE_TRANSFER_CHANNEL))
+#define TP_TESTS_IS_FILE_TRANSFER_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), TP_TESTS_TYPE_FILE_TRANSFER_CHANNEL))
+#define TP_TESTS_FILE_TRANSFER_CHANNEL_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_FILE_TRANSFER_CHANNEL, \
+                              TpTestsFileTransferChannelClass))
+
+struct _TpTestsFileTransferChannelClass {
+    TpBaseChannelClass parent_class;
+    TpDBusPropertiesMixinClass dbus_properties_class;
+};
+
+struct _TpTestsFileTransferChannel {
+    TpBaseChannel parent;
+
+    TpTestsFileTransferChannelPrivate *priv;
+};
+
+void tp_tests_file_transfer_channel_close (TpTestsFileTransferChannel *self);
+
+GHashTable * tp_tests_file_transfer_channel_get_props (
+        TpTestsFileTransferChannel *self);
+
+GSocketAddress * tp_tests_file_transfer_channel_get_server_address (
+        TpTestsFileTransferChannel *self);
+
+G_END_DECLS
+
+#endif
diff --git a/tests/lib/telepathy/contactlist/my-conn-proxy.c b/tests/lib/telepathy/contactlist/my-conn-proxy.c
new file mode 100644
index 0000000..0d7723c
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/my-conn-proxy.c
@@ -0,0 +1,364 @@
+/*
+ * my-conn-proxy.c - a simple subclass of TpConnection
+ *
+ * Copyright (C) 2010-2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "config.h"
+
+#include "my-conn-proxy.h"
+
+#include <telepathy-glib/telepathy-glib.h>
+
+G_DEFINE_TYPE  (TpTestsMyConnProxy, tp_tests_my_conn_proxy,
+    TP_TYPE_CONNECTION)
+
+static void
+tp_tests_my_conn_proxy_init (TpTestsMyConnProxy *self)
+{
+}
+
+enum {
+    FEAT_CORE,
+    FEAT_A,
+    FEAT_B,
+    FEAT_WRONG_IFACE,
+    FEAT_BAD_DEP,
+    FEAT_FAIL,
+    FEAT_FAIL_DEP,
+    FEAT_RETRY,
+    FEAT_RETRY_DEP,
+    FEAT_BEFORE_CONNECTED,
+    FEAT_INTERFACE_LATER,
+    N_FEAT
+};
+
+static void
+prepare_core_async (TpProxy *proxy,
+    const TpProxyFeature *feature,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  GSimpleAsyncResult *result;
+
+  /* superclass core features are prepared first */
+  g_assert (tp_proxy_is_prepared (proxy, TP_CONNECTION_FEATURE_CORE));
+
+  result = g_simple_async_result_new ((GObject *) proxy, callback, user_data,
+      prepare_core_async);
+
+  g_simple_async_result_complete_in_idle (result);
+  g_object_unref (result);
+}
+
+static void
+prepare_a_async (TpProxy *proxy,
+    const TpProxyFeature *feature,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  GSimpleAsyncResult *result;
+
+  g_assert (tp_proxy_is_prepared (proxy, TP_TESTS_MY_CONN_PROXY_FEATURE_CORE));
+
+  result = g_simple_async_result_new ((GObject *) proxy, callback, user_data,
+      prepare_a_async);
+
+  g_simple_async_result_complete_in_idle (result);
+  g_object_unref (result);
+}
+
+static void
+prepare_b_async (TpProxy *proxy,
+    const TpProxyFeature *feature,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  GSimpleAsyncResult *result;
+
+  g_assert (tp_proxy_is_prepared (proxy, TP_TESTS_MY_CONN_PROXY_FEATURE_CORE));
+  g_assert (tp_proxy_is_prepared (proxy, TP_TESTS_MY_CONN_PROXY_FEATURE_A));
+
+  result = g_simple_async_result_new ((GObject *) proxy, callback, user_data,
+      prepare_b_async);
+
+  g_simple_async_result_complete_in_idle (result);
+  g_object_unref (result);
+}
+
+static void
+cannot_be_prepared_async (TpProxy *proxy,
+    const TpProxyFeature *feature,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  g_assert_not_reached ();
+}
+
+static void
+prepare_fail_async (TpProxy *proxy,
+    const TpProxyFeature *feature,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  GSimpleAsyncResult *result;
+
+  g_assert (tp_proxy_is_prepared (proxy, TP_TESTS_MY_CONN_PROXY_FEATURE_CORE));
+
+  result = g_simple_async_result_new_error ((GObject *) proxy, callback,
+      user_data, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
+      "No feature for you!");
+
+  g_simple_async_result_complete_in_idle (result);
+  g_object_unref (result);
+}
+
+static void
+prepare_retry_async (TpProxy *proxy,
+    const TpProxyFeature *feature,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  TpTestsMyConnProxy *self = (TpTestsMyConnProxy *) proxy;
+  GSimpleAsyncResult *result;
+
+  result = g_simple_async_result_new ((GObject *) proxy, callback, user_data,
+      prepare_retry_async);
+
+  if (!self->retry_feature_success)
+    {
+      /* Fail the first time we try to prepare the feature */
+      g_simple_async_result_set_error (result, TP_ERROR,
+          TP_ERROR_NOT_YET, "Nah");
+    }
+
+  g_simple_async_result_complete_in_idle (result);
+  g_object_unref (result);
+}
+
+static void
+prepare_retry_dep_async (TpProxy *proxy,
+    const TpProxyFeature *feature,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  GSimpleAsyncResult *result;
+
+  g_assert (tp_proxy_is_prepared (proxy, TP_TESTS_MY_CONN_PROXY_FEATURE_CORE));
+
+  result = g_simple_async_result_new ((GObject *) proxy, callback, user_data,
+      prepare_retry_dep_async);
+
+  g_simple_async_result_complete_in_idle (result);
+  g_object_unref (result);
+}
+
+static void
+prepare_before_connected_async (TpProxy *proxy,
+    const TpProxyFeature *feature,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  TpTestsMyConnProxy *self = (TpTestsMyConnProxy *) proxy;
+  GSimpleAsyncResult *result;
+
+  g_assert (tp_proxy_is_prepared (proxy, TP_TESTS_MY_CONN_PROXY_FEATURE_CORE));
+
+  result = g_simple_async_result_new ((GObject *) proxy, callback, user_data,
+      prepare_before_connected_async);
+
+  if (tp_connection_get_status (TP_CONNECTION (self), NULL) ==
+        TP_CONNECTION_STATUS_CONNECTED)
+    self->before_connected_state = BEFORE_CONNECTED_STATE_CONNECTED;
+  else
+    self->before_connected_state = BEFORE_CONNECTED_STATE_NOT_CONNECTED;
+
+  g_simple_async_result_complete_in_idle (result);
+  g_object_unref (result);
+}
+
+static void
+prepare_before_connected_before_async (TpProxy *proxy,
+    const TpProxyFeature *feature,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  TpTestsMyConnProxy *self = (TpTestsMyConnProxy *) proxy;
+  GSimpleAsyncResult *result;
+
+  g_assert (tp_proxy_is_prepared (proxy, TP_TESTS_MY_CONN_PROXY_FEATURE_CORE));
+
+  g_assert_cmpuint (tp_connection_get_status (TP_CONNECTION (proxy), NULL), ==,
+      TP_CONNECTION_STATUS_CONNECTING);
+
+  result = g_simple_async_result_new ((GObject *) proxy, callback, user_data,
+      prepare_before_connected_before_async);
+
+  self->before_connected_state = BEFORE_CONNECTED_STATE_CONNECTED;
+
+  g_simple_async_result_complete_in_idle (result);
+  g_object_unref (result);
+}
+
+static void
+prepare_interface_later_async (TpProxy *proxy,
+    const TpProxyFeature *feature,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  GSimpleAsyncResult *result;
+
+  result = g_simple_async_result_new ((GObject *) proxy, callback, user_data,
+      prepare_interface_later_async);
+
+  g_simple_async_result_complete_in_idle (result);
+  g_object_unref (result);
+}
+
+static const TpProxyFeature *
+list_features (TpProxyClass *cls G_GNUC_UNUSED)
+{
+  static TpProxyFeature features[N_FEAT + 1] = { { 0 } };
+  static GQuark need_a[2] = {0, 0};
+  static GQuark need_channel_core[2] = {0, 0};
+  static GQuark need_wrong_iface[2] = {0, 0};
+  static GQuark need_fail[2] = {0, 0};
+  static GQuark need_retry[2] = {0, 0};
+  static GQuark need_iface_later[2] = {0, 0};
+
+  if (G_LIKELY (features[0].name != 0))
+    return features;
+
+  features[FEAT_CORE].name = TP_TESTS_MY_CONN_PROXY_FEATURE_CORE;
+  features[FEAT_CORE].core = TRUE;
+  features[FEAT_CORE].prepare_async = prepare_core_async;
+
+  features[FEAT_A].name = TP_TESTS_MY_CONN_PROXY_FEATURE_A;
+  features[FEAT_A].prepare_async = prepare_a_async;
+
+  features[FEAT_B].name = TP_TESTS_MY_CONN_PROXY_FEATURE_B;
+  features[FEAT_B].prepare_async = prepare_b_async;
+  need_a[0] = TP_TESTS_MY_CONN_PROXY_FEATURE_A;
+  features[FEAT_B].depends_on = need_a;
+
+  features[FEAT_WRONG_IFACE].name = TP_TESTS_MY_CONN_PROXY_FEATURE_WRONG_IFACE;
+  features[FEAT_WRONG_IFACE].prepare_async = cannot_be_prepared_async;
+  need_channel_core[0] = TP_CHANNEL_FEATURE_CORE;
+  features[FEAT_WRONG_IFACE].interfaces_needed = need_channel_core;
+
+  features[FEAT_BAD_DEP].name = TP_TESTS_MY_CONN_PROXY_FEATURE_BAD_DEP;
+  features[FEAT_BAD_DEP].prepare_async = cannot_be_prepared_async;
+  need_wrong_iface[0] = TP_TESTS_MY_CONN_PROXY_FEATURE_WRONG_IFACE;
+  features[FEAT_BAD_DEP].depends_on = need_wrong_iface;
+
+  features[FEAT_FAIL].name = TP_TESTS_MY_CONN_PROXY_FEATURE_FAIL;
+  features[FEAT_FAIL].prepare_async = prepare_fail_async;
+
+  features[FEAT_FAIL_DEP].name = TP_TESTS_MY_CONN_PROXY_FEATURE_FAIL_DEP;
+  features[FEAT_FAIL_DEP].prepare_async = cannot_be_prepared_async;
+  need_fail[0] = TP_TESTS_MY_CONN_PROXY_FEATURE_FAIL;
+  features[FEAT_FAIL_DEP].depends_on = need_fail;
+
+  features[FEAT_RETRY].name = TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY;
+  features[FEAT_RETRY].prepare_async = prepare_retry_async;
+  features[FEAT_RETRY].can_retry = TRUE;
+
+  features[FEAT_RETRY_DEP].name = TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY_DEP;
+  need_retry[0] = TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY;
+  features[FEAT_RETRY_DEP].prepare_async = prepare_retry_dep_async;
+  features[FEAT_RETRY_DEP].depends_on = need_retry;
+
+  features[FEAT_BEFORE_CONNECTED].name =
+    TP_TESTS_MY_CONN_PROXY_FEATURE_BEFORE_CONNECTED;
+  features[FEAT_BEFORE_CONNECTED].prepare_async =
+    prepare_before_connected_async;
+  features[FEAT_BEFORE_CONNECTED].prepare_before_signalling_connected_async =
+    prepare_before_connected_before_async;
+
+  features[FEAT_INTERFACE_LATER].name =
+    TP_TESTS_MY_CONN_PROXY_FEATURE_INTERFACE_LATER;
+  features[FEAT_INTERFACE_LATER].prepare_async = prepare_interface_later_async;
+  need_iface_later[0] = g_quark_from_static_string (
+      TP_TESTS_MY_CONN_PROXY_IFACE_LATER);
+  features[FEAT_INTERFACE_LATER].interfaces_needed = need_iface_later;
+
+  return features;
+}
+
+static void
+tp_tests_my_conn_proxy_class_init (TpTestsMyConnProxyClass *klass)
+{
+  TpProxyClass *proxy_class = (TpProxyClass *) klass;
+
+  proxy_class->list_features = list_features;
+}
+
+GQuark
+tp_tests_my_conn_proxy_get_feature_quark_core (void)
+{
+  return g_quark_from_static_string ("tp-my-conn-proxy-feature-core");
+}
+
+GQuark
+tp_tests_my_conn_proxy_get_feature_quark_a (void)
+{
+  return g_quark_from_static_string ("tp-my-conn-proxy-feature-a");
+}
+
+GQuark
+tp_tests_my_conn_proxy_get_feature_quark_b (void)
+{
+  return g_quark_from_static_string ("tp-my-conn-proxy-feature-b");
+}
+
+GQuark
+tp_tests_my_conn_proxy_get_feature_quark_wrong_iface (void)
+{
+  return g_quark_from_static_string ("tp-my-conn-proxy-feature-wrong_iface");
+}
+
+GQuark
+tp_tests_my_conn_proxy_get_feature_quark_bad_dep (void)
+{
+  return g_quark_from_static_string ("tp-my-conn-proxy-feature-bad-dep");
+}
+
+GQuark
+tp_tests_my_conn_proxy_get_feature_quark_fail (void)
+{
+  return g_quark_from_static_string ("tp-my-conn-proxy-feature-fail");
+}
+
+GQuark
+tp_tests_my_conn_proxy_get_feature_quark_fail_dep (void)
+{
+  return g_quark_from_static_string ("tp-my-conn-proxy-feature-fail-dep");
+}
+
+GQuark
+tp_tests_my_conn_proxy_get_feature_quark_retry (void)
+{
+  return g_quark_from_static_string ("tp-my-conn-proxy-feature-retry");
+}
+
+GQuark
+tp_tests_my_conn_proxy_get_feature_quark_retry_dep (void)
+{
+  return g_quark_from_static_string ("tp-my-conn-proxy-feature-retry-dep");
+}
+
+GQuark
+tp_tests_my_conn_proxy_get_feature_quark_before_connected (void)
+{
+  return g_quark_from_static_string ("tp-my-conn-proxy-feature-before-connected");
+}
+
+GQuark
+tp_tests_my_conn_proxy_get_feature_quark_interface_later (void)
+{
+  return g_quark_from_static_string ("tp-my-conn-proxy-feature-interface-later");
+}
diff --git a/tests/lib/telepathy/contactlist/my-conn-proxy.h b/tests/lib/telepathy/contactlist/my-conn-proxy.h
new file mode 100644
index 0000000..08be18b
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/my-conn-proxy.h
@@ -0,0 +1,124 @@
+/*
+ * my-conn-proxy.h - header for a simple subclass of TpConnection
+ *
+ * Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef __TP_TESTS_MY_CONN_PROXY_H__
+#define __TP_TESTS_MY_CONN_PROXY_H__
+
+#include <glib-object.h>
+#include <telepathy-glib/telepathy-glib.h>
+
+
+G_BEGIN_DECLS
+
+typedef struct _TpTestsMyConnProxy TpTestsMyConnProxy;
+typedef struct _TpTestsMyConnProxyClass TpTestsMyConnProxyClass;
+typedef struct _TpTestsMyConnProxyPrivate TpTestsMyConnProxyPrivate;
+
+struct _TpTestsMyConnProxyClass {
+    TpConnectionClass parent_class;
+};
+
+typedef enum
+{
+  BEFORE_CONNECTED_STATE_UNPREPARED = 0,
+  BEFORE_CONNECTED_STATE_NOT_CONNECTED,
+  BEFORE_CONNECTED_STATE_CONNECTED,
+} TpTestsMyConnProxyBeforeConnectedState;
+
+
+struct _TpTestsMyConnProxy {
+    TpConnection parent;
+
+    gboolean retry_feature_success;
+    TpTestsMyConnProxyBeforeConnectedState before_connected_state;
+};
+
+GType tp_tests_my_conn_proxy_get_type (void);
+
+/* TYPE MACROS */
+#define TP_TESTS_TYPE_MY_CONN_PROXY \
+  (tp_tests_my_conn_proxy_get_type ())
+#define TP_TESTS_MY_CONN_PROXY(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_MY_CONN_PROXY, \
+                              TpTestsMyConnProxy))
+#define TP_TESTS_MY_CONN_PROXY_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_MY_CONN_PROXY, \
+                           TpTestsMyConnProxyClass))
+#define TP_TESTS_SIMPLE_IS_MY_CONN_PROXY(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_MY_CONN_PROXY))
+#define TP_TESTS_SIMPLE_IS_MY_CONN_PROXY_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_MY_CONN_PROXY))
+#define TP_TESTS_MY_CONN_PROXY_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_MY_CONN_PROXY, \
+                              TpTestsMyConnProxyClass))
+
+/* Core feature */
+#define TP_TESTS_MY_CONN_PROXY_FEATURE_CORE \
+  (tp_tests_my_conn_proxy_get_feature_quark_core ())
+GQuark tp_tests_my_conn_proxy_get_feature_quark_core (void) G_GNUC_CONST;
+
+/* No depends */
+#define TP_TESTS_MY_CONN_PROXY_FEATURE_A \
+  (tp_tests_my_conn_proxy_get_feature_quark_a ())
+GQuark tp_tests_my_conn_proxy_get_feature_quark_a (void) G_GNUC_CONST;
+
+/* Depends on A */
+#define TP_TESTS_MY_CONN_PROXY_FEATURE_B \
+  (tp_tests_my_conn_proxy_get_feature_quark_b ())
+GQuark tp_tests_my_conn_proxy_get_feature_quark_b (void) G_GNUC_CONST;
+
+/* Depends on an unimplemented iface */
+#define TP_TESTS_MY_CONN_PROXY_FEATURE_WRONG_IFACE \
+  (tp_tests_my_conn_proxy_get_feature_quark_wrong_iface ())
+GQuark tp_tests_my_conn_proxy_get_feature_quark_wrong_iface (void) G_GNUC_CONST;
+
+/* Depends on WRONG_IFACE */
+#define TP_TESTS_MY_CONN_PROXY_FEATURE_BAD_DEP \
+  (tp_tests_my_conn_proxy_get_feature_quark_bad_dep ())
+GQuark tp_tests_my_conn_proxy_get_feature_quark_bad_dep (void) G_GNUC_CONST;
+
+/* Fail during preparation */
+#define TP_TESTS_MY_CONN_PROXY_FEATURE_FAIL \
+  (tp_tests_my_conn_proxy_get_feature_quark_fail ())
+GQuark tp_tests_my_conn_proxy_get_feature_quark_fail (void) G_GNUC_CONST;
+
+/* Depends on FAIL */
+#define TP_TESTS_MY_CONN_PROXY_FEATURE_FAIL_DEP \
+  (tp_tests_my_conn_proxy_get_feature_quark_fail_dep ())
+GQuark tp_tests_my_conn_proxy_get_feature_quark_fail_dep (void) G_GNUC_CONST;
+
+/* Fail at first attempt but succeed after */
+#define TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY \
+  (tp_tests_my_conn_proxy_get_feature_quark_retry ())
+GQuark tp_tests_my_conn_proxy_get_feature_quark_retry (void) G_GNUC_CONST;
+
+/* Depends on FEATURE_RETRY */
+#define TP_TESTS_MY_CONN_PROXY_FEATURE_RETRY_DEP \
+  (tp_tests_my_conn_proxy_get_feature_quark_retry_dep ())
+GQuark tp_tests_my_conn_proxy_get_feature_quark_retry_dep (void) G_GNUC_CONST;
+
+/* Can be prepared before the connection is connected and block announcing the
+ * connected state */
+#define TP_TESTS_MY_CONN_PROXY_FEATURE_BEFORE_CONNECTED \
+  (tp_tests_my_conn_proxy_get_feature_quark_before_connected ())
+GQuark tp_tests_my_conn_proxy_get_feature_quark_before_connected (void) G_GNUC_CONST;
+
+#define TP_TESTS_MY_CONN_PROXY_IFACE_LATER "org.freedesktop.Telepathy.Conncetion.Interface.Test.Later"
+
+/* Need the interface TP_TESTS_MY_CONN_PROXY_IFACE_LATER to be prepared but
+ * this interface is not in the initial set of interfaces of the connection.
+ * It is added when the connection is connected. */
+#define TP_TESTS_MY_CONN_PROXY_FEATURE_INTERFACE_LATER \
+  (tp_tests_my_conn_proxy_get_feature_quark_interface_later ())
+GQuark tp_tests_my_conn_proxy_get_feature_quark_interface_later (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* #ifndef __TP_TESTS_MY_CONN_PROXY_H__ */
diff --git a/tests/lib/telepathy/contactlist/myassert.h b/tests/lib/telepathy/contactlist/myassert.h
new file mode 100644
index 0000000..1fd6fd3
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/myassert.h
@@ -0,0 +1,16 @@
+#ifndef TP_TESTS_MYASSERT_H
+#define TP_TESTS_MYASSERT_H
+
+#include <glib.h>
+#include <telepathy-glib/telepathy-glib.h>
+
+#define MYASSERT(assertion, extra_format, ...)\
+  G_STMT_START {\
+      if (!(assertion))\
+        {\
+          g_error ("\n%s:%d: Assertion failed: %s" extra_format,\
+            __FILE__, __LINE__, #assertion, ##__VA_ARGS__);\
+        }\
+  } G_STMT_END
+
+#endif
diff --git a/tests/lib/telepathy/contactlist/room-list-chan.c 
b/tests/lib/telepathy/contactlist/room-list-chan.c
index 49ed291..f4db067 100644
--- a/tests/lib/telepathy/contactlist/room-list-chan.c
+++ b/tests/lib/telepathy/contactlist/room-list-chan.c
@@ -4,8 +4,7 @@
 #include "room-list-chan.h"
 
 #include <telepathy-glib/telepathy-glib.h>
-#include <telepathy-glib/channel-iface.h>
-#include <telepathy-glib/svc-channel.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
 
 static void room_list_iface_init (gpointer iface,
     gpointer data);
diff --git a/tests/lib/telepathy/contactlist/room-list-chan.h 
b/tests/lib/telepathy/contactlist/room-list-chan.h
index b41be27..52dbd2e 100644
--- a/tests/lib/telepathy/contactlist/room-list-chan.h
+++ b/tests/lib/telepathy/contactlist/room-list-chan.h
@@ -3,7 +3,7 @@
 #define __TP_TESTS_ROOM_LIST_CHAN_H__
 
 #include <glib-object.h>
-#include <telepathy-glib/base-channel.h>
+#include <telepathy-glib/telepathy-glib.h>
 
 G_BEGIN_DECLS
 
diff --git a/tests/lib/telepathy/contactlist/simple-account-manager.c 
b/tests/lib/telepathy/contactlist/simple-account-manager.c
index e1fec67..e5bddbc 100644
--- a/tests/lib/telepathy/contactlist/simple-account-manager.c
+++ b/tests/lib/telepathy/contactlist/simple-account-manager.c
@@ -13,12 +13,8 @@
 
 #include "simple-account-manager.h"
 
-#include <telepathy-glib/account.h>
-#include <telepathy-glib/gtypes.h>
-#include <telepathy-glib/interfaces.h>
-#include <telepathy-glib/svc-generic.h>
-#include <telepathy-glib/svc-account-manager.h>
-#include <telepathy-glib/util.h>
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
 
 static void account_manager_iface_init (gpointer, gpointer);
 
@@ -39,14 +35,14 @@ enum
 {
   PROP_0,
   PROP_INTERFACES,
-  PROP_VALID_ACCOUNTS,
-  PROP_INVALID_ACCOUNTS,
+  PROP_USABLE_ACCOUNTS,
+  PROP_UNUSABLE_ACCOUNTS,
 };
 
 struct _TpTestsSimpleAccountManagerPrivate
 {
-  GPtrArray *valid_accounts;
-  GPtrArray *invalid_accounts;
+  GPtrArray *usable_accounts;
+  GPtrArray *unusable_accounts;
 };
 
 static void
@@ -95,8 +91,8 @@ tp_tests_simple_account_manager_init (TpTestsSimpleAccountManager *self)
   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
       TP_TESTS_TYPE_SIMPLE_ACCOUNT_MANAGER, TpTestsSimpleAccountManagerPrivate);
 
-  self->priv->valid_accounts = g_ptr_array_new_with_free_func (g_free);
-  self->priv->invalid_accounts = g_ptr_array_new_with_free_func (g_free);
+  self->priv->usable_accounts = g_ptr_array_new_with_free_func (g_free);
+  self->priv->unusable_accounts = g_ptr_array_new_with_free_func (g_free);
 }
 
 static void
@@ -112,12 +108,12 @@ tp_tests_simple_account_manager_get_property (GObject *object,
       g_value_set_boxed (value, ACCOUNT_MANAGER_INTERFACES);
       break;
 
-    case PROP_VALID_ACCOUNTS:
-      g_value_set_boxed (value, self->priv->valid_accounts);
+    case PROP_USABLE_ACCOUNTS:
+      g_value_set_boxed (value, self->priv->usable_accounts);
       break;
 
-    case PROP_INVALID_ACCOUNTS:
-      g_value_set_boxed (value, self->priv->invalid_accounts);
+    case PROP_UNUSABLE_ACCOUNTS:
+      g_value_set_boxed (value, self->priv->unusable_accounts);
       break;
 
     default:
@@ -131,8 +127,8 @@ tp_tests_simple_account_manager_finalize (GObject *object)
 {
   TpTestsSimpleAccountManager *self = SIMPLE_ACCOUNT_MANAGER (object);
 
-  g_ptr_array_unref (self->priv->valid_accounts);
-  g_ptr_array_unref (self->priv->invalid_accounts);
+  g_ptr_array_unref (self->priv->usable_accounts);
+  g_ptr_array_unref (self->priv->unusable_accounts);
 
   tp_clear_pointer (&self->create_cm, g_free);
   tp_clear_pointer (&self->create_protocol, g_free);
@@ -160,8 +156,8 @@ tp_tests_simple_account_manager_class_init (
 
   static TpDBusPropertiesMixinPropImpl am_props[] = {
         { "Interfaces", "interfaces", NULL },
-        { "ValidAccounts", "valid-accounts", NULL },
-        { "InvalidAccounts", "invalid-accounts", NULL },
+        { "UsableAccounts", "usable-accounts", NULL },
+        { "UnusableAccounts", "unusable-accounts", NULL },
         /*
         { "SupportedAccountProperties", "supported-account-properties", NULL },
         */
@@ -186,16 +182,16 @@ tp_tests_simple_account_manager_class_init (
       G_TYPE_STRV,
       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
   g_object_class_install_property (object_class, PROP_INTERFACES, param_spec);
-  param_spec = g_param_spec_boxed ("valid-accounts", "Valid accounts",
-      "The accounts which are valid on this account. This may be a lie.",
+  param_spec = g_param_spec_boxed ("usable-accounts", "Usable accounts",
+      "The accounts which are usable on this account manager. This may be a lie.",
       TP_ARRAY_TYPE_OBJECT_PATH_LIST,
       G_PARAM_READABLE);
-  g_object_class_install_property (object_class, PROP_VALID_ACCOUNTS, param_spec);
-  param_spec = g_param_spec_boxed ("invalid-accounts", "Invalid accounts",
-      "The accounts which are invalid on this account. This may be a lie.",
+  g_object_class_install_property (object_class, PROP_USABLE_ACCOUNTS, param_spec);
+  param_spec = g_param_spec_boxed ("unusable-accounts", "Unusable accounts",
+      "The accounts which are unusable on this account manager. This may be a lie.",
       TP_ARRAY_TYPE_OBJECT_PATH_LIST,
       G_PARAM_READABLE);
-  g_object_class_install_property (object_class, PROP_INVALID_ACCOUNTS, param_spec);
+  g_object_class_install_property (object_class, PROP_UNUSABLE_ACCOUNTS, param_spec);
 
   klass->dbus_props_class.interfaces = prop_interfaces;
   tp_dbus_properties_mixin_class_init (object_class,
@@ -219,17 +215,18 @@ void
 tp_tests_simple_account_manager_add_account (
     TpTestsSimpleAccountManager *self,
     const gchar *object_path,
-    gboolean valid)
+    gboolean usable)
 {
-  remove_from_array (self->priv->valid_accounts, object_path);
-  remove_from_array (self->priv->valid_accounts, object_path);
+  remove_from_array (self->priv->usable_accounts, object_path);
+  remove_from_array (self->priv->unusable_accounts, object_path);
 
-  if (valid)
-    g_ptr_array_add (self->priv->valid_accounts, g_strdup (object_path));
+  if (usable)
+    g_ptr_array_add (self->priv->usable_accounts, g_strdup (object_path));
   else
-    g_ptr_array_add (self->priv->invalid_accounts, g_strdup (object_path));
+    g_ptr_array_add (self->priv->unusable_accounts, g_strdup (object_path));
 
-  tp_svc_account_manager_emit_account_validity_changed (self, object_path, valid);
+  tp_svc_account_manager_emit_account_usability_changed (self, object_path,
+      usable);
 }
 
 void
@@ -237,8 +234,8 @@ tp_tests_simple_account_manager_remove_account (
     TpTestsSimpleAccountManager *self,
     const gchar *object_path)
 {
-  remove_from_array (self->priv->valid_accounts, object_path);
-  remove_from_array (self->priv->valid_accounts, object_path);
+  remove_from_array (self->priv->usable_accounts, object_path);
+  remove_from_array (self->priv->unusable_accounts, object_path);
 
   tp_svc_account_manager_emit_account_removed (self, object_path);
 }
diff --git a/tests/lib/telepathy/contactlist/simple-account-manager.h 
b/tests/lib/telepathy/contactlist/simple-account-manager.h
index d1608d5..cc65f09 100644
--- a/tests/lib/telepathy/contactlist/simple-account-manager.h
+++ b/tests/lib/telepathy/contactlist/simple-account-manager.h
@@ -13,7 +13,7 @@
 #define __TP_TESTS_SIMPLE_ACCOUNT_MANAGER_H__
 
 #include <glib-object.h>
-#include <telepathy-glib/dbus-properties-mixin.h>
+#include <telepathy-glib/telepathy-glib.h>
 
 
 G_BEGIN_DECLS
@@ -61,7 +61,7 @@ GType tp_tests_simple_account_manager_get_type (void);
 void tp_tests_simple_account_manager_add_account (
     TpTestsSimpleAccountManager *self,
     const gchar *object_path,
-    gboolean valid);
+    gboolean usable);
 
 void tp_tests_simple_account_manager_remove_account (
     TpTestsSimpleAccountManager *self,
diff --git a/tests/lib/telepathy/contactlist/simple-account.c 
b/tests/lib/telepathy/contactlist/simple-account.c
index 6279d75..8b13268 100644
--- a/tests/lib/telepathy/contactlist/simple-account.c
+++ b/tests/lib/telepathy/contactlist/simple-account.c
@@ -12,14 +12,8 @@
 
 #include "simple-account.h"
 
-#include <telepathy-glib/dbus.h>
-#include <telepathy-glib/defs.h>
-#include <telepathy-glib/enums.h>
-#include <telepathy-glib/gtypes.h>
-#include <telepathy-glib/interfaces.h>
-#include <telepathy-glib/util.h>
-#include <telepathy-glib/svc-generic.h>
-#include <telepathy-glib/svc-account.h>
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
 
 static void account_iface_init (gpointer, gpointer);
 
@@ -50,7 +44,7 @@ enum
   PROP_INTERFACES,
   PROP_DISPLAY_NAME,
   PROP_ICON,
-  PROP_VALID,
+  PROP_USABLE,
   PROP_ENABLED,
   PROP_NICKNAME,
   PROP_PARAMETERS,
@@ -80,6 +74,7 @@ struct _TpTestsSimpleAccountPrivate
   gchar *presence_msg;
   gchar *connection_path;
   gboolean enabled;
+  GPtrArray *uri_schemes;
 };
 
 static void
@@ -122,10 +117,14 @@ account_iface_init (gpointer klass,
 #undef IMPLEMENT
 }
 
+/* you may have noticed this is not entirely realistic */
+static const gchar * const uri_schemes[] = { "about", "telnet", NULL };
 
 static void
 tp_tests_simple_account_init (TpTestsSimpleAccount *self)
 {
+  guint i;
+
   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TESTS_TYPE_SIMPLE_ACCOUNT,
       TpTestsSimpleAccountPrivate);
 
@@ -134,10 +133,11 @@ tp_tests_simple_account_init (TpTestsSimpleAccount *self)
   self->priv->presence_msg = g_strdup ("this is my CurrentPresence");
   self->priv->connection_path = g_strdup ("/");
   self->priv->enabled = TRUE;
-}
 
-/* you may have noticed this is not entirely realistic */
-static const gchar * const uri_schemes[] = { "about", "telnet", NULL };
+  self->priv->uri_schemes = g_ptr_array_new_with_free_func (g_free);
+  for (i = 0; uri_schemes[i] != NULL; i++)
+    g_ptr_array_add (self->priv->uri_schemes, g_strdup (uri_schemes[i]));
+}
 
 static void
 tp_tests_simple_account_get_property (GObject *object,
@@ -161,7 +161,7 @@ tp_tests_simple_account_get_property (GObject *object,
     case PROP_ICON:
       g_value_set_string (value, "");
       break;
-    case PROP_VALID:
+    case PROP_USABLE:
       g_value_set_boolean (value, TRUE);
       break;
     case PROP_ENABLED:
@@ -213,7 +213,7 @@ tp_tests_simple_account_get_property (GObject *object,
       g_value_set_boolean (value, TRUE);
       break;
     case PROP_STORAGE_PROVIDER:
-      g_value_set_string (value, "org.freedesktop.Telepathy.glib.test");
+      g_value_set_string (value, "im.telepathy1.glib.test");
       break;
     case PROP_STORAGE_IDENTIFIER:
       g_value_set_boxed (value, &identifier);
@@ -231,7 +231,19 @@ tp_tests_simple_account_get_property (GObject *object,
           TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS);
       break;
     case PROP_URI_SCHEMES:
-      g_value_set_boxed (value, uri_schemes);
+        {
+          GPtrArray *arr;
+          guint i;
+
+          arr = g_ptr_array_sized_new (self->priv->uri_schemes->len + 1);
+          for (i = 0; i < self->priv->uri_schemes->len; i++)
+              g_ptr_array_add (arr,
+                  g_ptr_array_index (self->priv->uri_schemes, i));
+            g_ptr_array_add (arr, NULL);
+
+          g_value_set_boxed (value, arr->pdata);
+          g_ptr_array_unref (arr);
+        }
       break;
     case PROP_AVATAR:
         {
@@ -274,6 +286,8 @@ tp_tests_simple_account_finalize (GObject *object)
   g_free (self->priv->presence_msg);
   g_free (self->priv->connection_path);
 
+  g_ptr_array_unref (self->priv->uri_schemes);
+
   G_OBJECT_CLASS (tp_tests_simple_account_parent_class)->finalize (object);
 }
 
@@ -292,7 +306,7 @@ tp_tests_simple_account_class_init (TpTestsSimpleAccountClass *klass)
         { "Interfaces", "interfaces", NULL },
         { "DisplayName", "display-name", NULL },
         { "Icon", "icon", NULL },
-        { "Valid", "valid", NULL },
+        { "Usable", "usable", NULL },
         { "Enabled", "enabled", NULL },
         { "Nickname", "nickname", NULL },
         { "Parameters", "parameters", NULL },
@@ -375,11 +389,11 @@ tp_tests_simple_account_class_init (TpTestsSimpleAccountClass *klass)
       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
   g_object_class_install_property (object_class, PROP_ICON, param_spec);
 
-  param_spec = g_param_spec_boolean ("valid", "valid",
-      "Valid property",
+  param_spec = g_param_spec_boolean ("usable", "usable",
+      "Usable property",
       FALSE,
       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
-  g_object_class_install_property (object_class, PROP_VALID, param_spec);
+  g_object_class_install_property (object_class, PROP_USABLE, param_spec);
 
   param_spec = g_param_spec_boolean ("enabled", "enabled",
       "Enabled property",
@@ -401,7 +415,7 @@ tp_tests_simple_account_class_init (TpTestsSimpleAccountClass *klass)
 
   param_spec = g_param_spec_boxed ("automatic-presence", "automatic presence",
       "AutomaticPresence property",
-      TP_STRUCT_TYPE_SIMPLE_PRESENCE,
+      TP_STRUCT_TYPE_PRESENCE,
       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
   g_object_class_install_property (object_class, PROP_AUTOMATIC_PRESENCE,
       param_spec);
@@ -435,14 +449,14 @@ tp_tests_simple_account_class_init (TpTestsSimpleAccountClass *klass)
 
   param_spec = g_param_spec_boxed ("current-presence", "current presence",
       "CurrentPresence property",
-      TP_STRUCT_TYPE_SIMPLE_PRESENCE,
+      TP_STRUCT_TYPE_PRESENCE,
       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
   g_object_class_install_property (object_class, PROP_CURRENT_PRESENCE,
       param_spec);
 
   param_spec = g_param_spec_boxed ("requested-presence", "requested presence",
       "RequestedPresence property",
-      TP_STRUCT_TYPE_SIMPLE_PRESENCE,
+      TP_STRUCT_TYPE_PRESENCE,
       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
   g_object_class_install_property (object_class, PROP_REQUESTED_PRESENCE,
       param_spec);
@@ -533,12 +547,12 @@ tp_tests_simple_account_set_presence (TpTestsSimpleAccount *self,
   g_object_get (self, "current-presence", &v, NULL);
 
   props = tp_asv_new (
-      "CurrentPresence", TP_STRUCT_TYPE_SIMPLE_PRESENCE, v,
+      "CurrentPresence", TP_STRUCT_TYPE_PRESENCE, v,
       NULL);
 
   tp_svc_account_emit_account_property_changed (self, props);
 
-  g_boxed_free (TP_STRUCT_TYPE_SIMPLE_PRESENCE, v);
+  g_boxed_free (TP_STRUCT_TYPE_PRESENCE, v);
 }
 
 void
@@ -578,3 +592,25 @@ tp_tests_simple_account_set_enabled (TpTestsSimpleAccount *self,
   tp_svc_account_emit_account_property_changed (self, change);
   g_hash_table_unref (change);
 }
+
+void
+tp_tests_simple_account_add_uri_scheme (TpTestsSimpleAccount *self,
+    const gchar *uri_scheme)
+{
+  GHashTable *changed;
+  GStrv schemes;
+
+  g_ptr_array_add (self->priv->uri_schemes, g_strdup (uri_scheme));
+
+  g_object_get (self, "uri-schemes", &schemes, NULL);
+
+  changed = tp_asv_new (
+      "URISchemes", G_TYPE_STRV, schemes,
+      NULL);
+
+  tp_svc_dbus_properties_emit_properties_changed (self,
+      TP_IFACE_ACCOUNT_INTERFACE_ADDRESSING, changed, NULL);
+
+  g_strfreev (schemes);
+  g_hash_table_unref (changed);
+}
diff --git a/tests/lib/telepathy/contactlist/simple-account.h 
b/tests/lib/telepathy/contactlist/simple-account.h
index 2ce3efd..351c6cc 100644
--- a/tests/lib/telepathy/contactlist/simple-account.h
+++ b/tests/lib/telepathy/contactlist/simple-account.h
@@ -13,8 +13,7 @@
 
 #include <glib-object.h>
 
-#include <telepathy-glib/connection.h>
-#include <telepathy-glib/dbus-properties-mixin.h>
+#include <telepathy-glib/telepathy-glib.h>
 
 G_BEGIN_DECLS
 
@@ -64,6 +63,9 @@ void tp_tests_simple_account_removed (TpTestsSimpleAccount *self);
 void tp_tests_simple_account_set_enabled (TpTestsSimpleAccount *self,
     gboolean enabled);
 
+void tp_tests_simple_account_add_uri_scheme (TpTestsSimpleAccount *self,
+    const gchar * uri_scheme);
+
 G_END_DECLS
 
 #endif /* #ifndef __TP_TESTS_SIMPLE_ACCOUNT_H__ */
diff --git a/tests/lib/telepathy/contactlist/simple-channel-dispatch-operation.c 
b/tests/lib/telepathy/contactlist/simple-channel-dispatch-operation.c
new file mode 100644
index 0000000..82b3819
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/simple-channel-dispatch-operation.c
@@ -0,0 +1,303 @@
+/*
+ * simple-channel-dispatch-operation.c - a simple channel dispatch operation
+ * service.
+ *
+ * Copyright © 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "config.h"
+
+#include "simple-channel-dispatch-operation.h"
+
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
+
+static void channel_dispatch_operation_iface_init (gpointer, gpointer);
+
+G_DEFINE_TYPE_WITH_CODE (TpTestsSimpleChannelDispatchOperation,
+    tp_tests_simple_channel_dispatch_operation,
+    G_TYPE_OBJECT,
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_DISPATCH_OPERATION,
+        channel_dispatch_operation_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
+        tp_dbus_properties_mixin_iface_init)
+    )
+
+/* TP_IFACE_CHANNEL_DISPATCH_OPERATION is implied */
+static const char *CHANNEL_DISPATCH_OPERATION_INTERFACES[] = { NULL };
+
+static const gchar *CHANNEL_DISPATCH_OPERATION_POSSIBLE_HANDLERS[] = {
+    TP_CLIENT_BUS_NAME_BASE ".Badger", NULL, };
+
+enum
+{
+  PROP_0,
+  PROP_INTERFACES,
+  PROP_CONNECTION,
+  PROP_ACCOUNT,
+  PROP_CHANNELS,
+  PROP_POSSIBLE_HANDLERS,
+};
+
+struct _SimpleChannelDispatchOperationPrivate
+{
+  gchar *conn_path;
+  gchar *account_path;
+  /* Array of TpChannel */
+  GPtrArray *channels;
+};
+
+static void
+tp_tests_simple_channel_dispatch_operation_handle_with (
+    TpSvcChannelDispatchOperation *iface,
+    const gchar *handler,
+    DBusGMethodInvocation *context)
+{
+  if (!tp_strdiff (handler, "FAIL"))
+    {
+      GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Nope" };
+
+      dbus_g_method_return_error (context, &error);
+      return;
+    }
+
+  dbus_g_method_return (context);
+}
+
+static void
+tp_tests_simple_channel_dispatch_operation_claim (
+    TpSvcChannelDispatchOperation *iface,
+    DBusGMethodInvocation *context)
+{
+  tp_svc_channel_dispatch_operation_emit_finished (iface);
+
+  dbus_g_method_return (context);
+}
+
+static void
+tp_tests_simple_channel_dispatch_operation_handle_with_time (
+    TpSvcChannelDispatchOperation *iface,
+    const gchar *handler,
+    gint64 user_action_timestamp,
+    DBusGMethodInvocation *context)
+{
+  dbus_g_method_return (context);
+}
+
+static void
+channel_dispatch_operation_iface_init (gpointer klass,
+    gpointer unused G_GNUC_UNUSED)
+{
+#define IMPLEMENT(x) tp_svc_channel_dispatch_operation_implement_##x (\
+  klass, tp_tests_simple_channel_dispatch_operation_##x)
+  IMPLEMENT(handle_with);
+  IMPLEMENT(claim);
+  IMPLEMENT(handle_with_time);
+#undef IMPLEMENT
+}
+
+
+static void
+tp_tests_simple_channel_dispatch_operation_init (TpTestsSimpleChannelDispatchOperation *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+      TP_TESTS_TYPE_SIMPLE_CHANNEL_DISPATCH_OPERATION,
+      TpTestsSimpleChannelDispatchOperationPrivate);
+
+  self->priv->channels = g_ptr_array_new_with_free_func (
+      (GDestroyNotify) g_object_unref);
+}
+
+static void
+tp_tests_simple_channel_dispatch_operation_get_property (GObject *object,
+              guint property_id,
+              GValue *value,
+              GParamSpec *spec)
+{
+  TpTestsSimpleChannelDispatchOperation *self =
+    TP_TESTS_SIMPLE_CHANNEL_DISPATCH_OPERATION (object);
+
+  switch (property_id) {
+    case PROP_INTERFACES:
+      g_value_set_boxed (value, CHANNEL_DISPATCH_OPERATION_INTERFACES);
+      break;
+
+    case PROP_ACCOUNT:
+      g_value_set_boxed (value, self->priv->account_path);
+      break;
+
+    case PROP_CONNECTION:
+      g_value_set_boxed (value, self->priv->conn_path);
+      break;
+
+    case PROP_CHANNELS:
+      {
+        GPtrArray *arr = g_ptr_array_new ();
+        guint i;
+
+        for (i = 0; i < self->priv->channels->len; i++)
+          {
+            TpChannel *channel = g_ptr_array_index (self->priv->channels, i);
+            GValue props_value = G_VALUE_INIT;
+            GVariant *props_variant;
+
+            /* Yay, double conversion! But this is for tests corner case */
+            props_variant = tp_channel_dup_immutable_properties (channel);
+            dbus_g_value_parse_g_variant (props_variant, &props_value);
+
+            g_ptr_array_add (arr,
+                tp_value_array_build (2,
+                  DBUS_TYPE_G_OBJECT_PATH, tp_proxy_get_object_path (channel),
+                  TP_HASH_TYPE_STRING_VARIANT_MAP,
+                      g_value_get_boxed (&props_value),
+                  G_TYPE_INVALID));
+
+            g_variant_unref (props_variant);
+            g_value_unset (&props_value);
+          }
+
+        g_value_take_boxed (value, arr);
+      }
+      break;
+
+    case PROP_POSSIBLE_HANDLERS:
+      g_value_set_boxed (value, CHANNEL_DISPATCH_OPERATION_POSSIBLE_HANDLERS);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
+      break;
+  }
+}
+
+static void
+tp_tests_simple_channel_dispatch_operation_finalize (GObject *object)
+{
+  TpTestsSimpleChannelDispatchOperation *self =
+    TP_TESTS_SIMPLE_CHANNEL_DISPATCH_OPERATION (object);
+  void (*finalize) (GObject *) =
+    G_OBJECT_CLASS (tp_tests_simple_channel_dispatch_operation_parent_class)->finalize;
+
+  g_free (self->priv->conn_path);
+  g_free (self->priv->account_path);
+  g_ptr_array_unref (self->priv->channels);
+
+  if (finalize != NULL)
+    finalize (object);
+}
+
+/**
+  * This class currently only provides the minimum for
+  * tp_channel_dispatch_operation_prepare to succeed. This turns out to be only a working
+  * Properties.GetAll().
+  */
+static void
+tp_tests_simple_channel_dispatch_operation_class_init (TpTestsSimpleChannelDispatchOperationClass *klass)
+{
+  GObjectClass *object_class = (GObjectClass *) klass;
+  GParamSpec *param_spec;
+
+  static TpDBusPropertiesMixinPropImpl a_props[] = {
+        { "Interfaces", "interfaces", NULL },
+        { "Connection", "connection", NULL },
+        { "Account", "account", NULL },
+        { "Channels", "channels", NULL },
+        { "PossibleHandlers", "possible-handlers", NULL },
+        { NULL }
+  };
+
+  static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
+        { TP_IFACE_CHANNEL_DISPATCH_OPERATION,
+          tp_dbus_properties_mixin_getter_gobject_properties,
+          NULL,
+          a_props
+        },
+        { NULL },
+  };
+
+  g_type_class_add_private (klass, sizeof (TpTestsSimpleChannelDispatchOperationPrivate));
+  object_class->get_property = tp_tests_simple_channel_dispatch_operation_get_property;
+  object_class->finalize = tp_tests_simple_channel_dispatch_operation_finalize;
+
+  param_spec = g_param_spec_boxed ("interfaces", "Extra D-Bus interfaces",
+      "In this case we only implement ChannelDispatchOperation, so none.",
+      G_TYPE_STRV,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_INTERFACES, param_spec);
+
+  param_spec = g_param_spec_boxed ("connection", "connection path",
+      "Connection path",
+      DBUS_TYPE_G_OBJECT_PATH,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+  param_spec = g_param_spec_boxed ("account", "account path",
+      "Account path",
+      DBUS_TYPE_G_OBJECT_PATH,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_ACCOUNT, param_spec);
+
+  param_spec = g_param_spec_boxed ("channels", "channel paths",
+      "Channel paths",
+      TP_ARRAY_TYPE_CHANNEL_DETAILS_LIST,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_CHANNELS, param_spec);
+
+  param_spec = g_param_spec_boxed ("possible-handlers", "possible handlers",
+      "possible handles",
+      G_TYPE_STRV,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_POSSIBLE_HANDLERS,
+      param_spec);
+
+  klass->dbus_props_class.interfaces = prop_interfaces;
+  tp_dbus_properties_mixin_class_init (object_class,
+      G_STRUCT_OFFSET (TpTestsSimpleChannelDispatchOperationClass, dbus_props_class));
+}
+
+void
+tp_tests_simple_channel_dispatch_operation_set_conn_path (
+    TpTestsSimpleChannelDispatchOperation *self,
+    const gchar *conn_path)
+{
+  self->priv->conn_path = g_strdup (conn_path);
+}
+
+void
+tp_tests_simple_channel_dispatch_operation_add_channel (
+    TpTestsSimpleChannelDispatchOperation *self,
+    TpChannel *chan)
+{
+  g_ptr_array_add (self->priv->channels, g_object_ref (chan));
+}
+
+void
+tp_tests_simple_channel_dispatch_operation_lost_channel (
+    TpTestsSimpleChannelDispatchOperation *self,
+    TpChannel *chan)
+{
+  const gchar *path = tp_proxy_get_object_path (chan);
+
+  g_ptr_array_remove (self->priv->channels, chan);
+
+  tp_svc_channel_dispatch_operation_emit_channel_lost (self, path,
+      TP_ERROR_STR_NOT_AVAILABLE, "Badger");
+
+  if (self->priv->channels->len == 0)
+    {
+      /* We removed the last channel; fire Finished */
+      tp_svc_channel_dispatch_operation_emit_finished (self);
+    }
+}
+
+void
+tp_tests_simple_channel_dispatch_operation_set_account_path (
+    TpTestsSimpleChannelDispatchOperation *self,
+    const gchar *account_path)
+{
+  self->priv->account_path = g_strdup (account_path);
+}
diff --git a/tests/lib/telepathy/contactlist/simple-channel-dispatch-operation.h 
b/tests/lib/telepathy/contactlist/simple-channel-dispatch-operation.h
new file mode 100644
index 0000000..3e9ee93
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/simple-channel-dispatch-operation.h
@@ -0,0 +1,73 @@
+/*
+ * simple-channel-dispatch-operation.h - a simple channel dispatch operation
+ * service.
+ *
+ * Copyright © 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef __TP_TESTS_SIMPLE_CHANNEL_DISPATCH_OPERATION_H__
+#define __TP_TESTS_SIMPLE_CHANNEL_DISPATCH_OPERATION_H__
+
+#include <glib-object.h>
+
+#include <telepathy-glib/telepathy-glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _SimpleChannelDispatchOperation TpTestsSimpleChannelDispatchOperation;
+typedef struct _SimpleChannelDispatchOperationClass TpTestsSimpleChannelDispatchOperationClass;
+typedef struct _SimpleChannelDispatchOperationPrivate TpTestsSimpleChannelDispatchOperationPrivate;
+
+struct _SimpleChannelDispatchOperationClass {
+    GObjectClass parent_class;
+    TpDBusPropertiesMixinClass dbus_props_class;
+};
+
+struct _SimpleChannelDispatchOperation {
+    GObject parent;
+
+    TpTestsSimpleChannelDispatchOperationPrivate *priv;
+};
+
+GType tp_tests_simple_channel_dispatch_operation_get_type (void);
+
+/* TYPE MACROS */
+#define TP_TESTS_TYPE_SIMPLE_CHANNEL_DISPATCH_OPERATION \
+  (tp_tests_simple_channel_dispatch_operation_get_type ())
+#define TP_TESTS_SIMPLE_CHANNEL_DISPATCH_OPERATION(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_SIMPLE_CHANNEL_DISPATCH_OPERATION, \
+                              TpTestsSimpleChannelDispatchOperation))
+#define TP_TESTS_SIMPLE_CHANNEL_DISPATCH_OPERATION_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_SIMPLE_CHANNEL_DISPATCH_OPERATION, \
+                           TpTestsSimpleChannelDispatchOperationClass))
+#define SIMPLE_IS_CHANNEL_DISPATCH_OPERATION(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_SIMPLE_CHANNEL_DISPATCH_OPERATION))
+#define SIMPLE_IS_CHANNEL_DISPATCH_OPERATION_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_SIMPLE_CHANNEL_DISPATCH_OPERATION))
+#define TP_TESTS_SIMPLE_CHANNEL_DISPATCH_OPERATION_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_SIMPLE_CHANNEL_DISPATCH_OPERATION, \
+                              TpTestsSimpleChannelDispatchOperationClass))
+
+void tp_tests_simple_channel_dispatch_operation_set_conn_path (
+    TpTestsSimpleChannelDispatchOperation *self,
+    const gchar *conn_path);
+
+void tp_tests_simple_channel_dispatch_operation_add_channel (
+    TpTestsSimpleChannelDispatchOperation *self,
+    TpChannel *chan);
+
+void tp_tests_simple_channel_dispatch_operation_lost_channel (
+    TpTestsSimpleChannelDispatchOperation *self,
+    TpChannel *chan);
+
+void tp_tests_simple_channel_dispatch_operation_set_account_path (
+    TpTestsSimpleChannelDispatchOperation *self,
+    const gchar *account_path);
+
+G_END_DECLS
+
+#endif /* #ifndef __TP_TESTS_SIMPLE_CHANNEL_DISPATCH_OPERATION_H__ */
diff --git a/tests/lib/telepathy/contactlist/simple-channel-dispatcher.c 
b/tests/lib/telepathy/contactlist/simple-channel-dispatcher.c
new file mode 100644
index 0000000..775f915
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/simple-channel-dispatcher.c
@@ -0,0 +1,394 @@
+/*
+ * simple-channel-dispatcher.c - simple channel dispatcher service.
+ *
+ * Copyright © 2010 Collabora Ltd.
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "config.h"
+
+#include "simple-channel-dispatcher.h"
+
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
+
+#include "simple-channel-request.h"
+#include "simple-conn.h"
+
+static void channel_dispatcher_iface_init (gpointer, gpointer);
+
+G_DEFINE_TYPE_WITH_CODE (TpTestsSimpleChannelDispatcher,
+    tp_tests_simple_channel_dispatcher,
+    G_TYPE_OBJECT,
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_DISPATCHER,
+        channel_dispatcher_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
+        tp_dbus_properties_mixin_iface_init)
+    )
+
+/* signals */
+enum {
+  CHANNEL_REQUEST_CREATED,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+/* TP_IFACE_CHANNEL_DISPATCHER is implied */
+static const char *CHANNEL_DISPATCHER_INTERFACES[] = { NULL };
+
+enum
+{
+  PROP_0,
+  PROP_INTERFACES,
+  PROP_CONNECTION,
+};
+
+struct _TpTestsSimpleChannelDispatcherPrivate
+{
+  /* To keep things simpler, this CD can only create channels using one
+   * connection */
+  TpTestsSimpleConnection *conn;
+
+  /* List of reffed TpTestsSimpleChannelRequest */
+  GSList *requests;
+
+  /* Used when ensuring a channel to store its handler.
+   * If this is set we fake that the channel already exist and re-call
+   * HandleChannels() on the handler rather than creating a new channel.
+   * This is pretty stupid but good enough for our tests. */
+  gchar *old_handler;
+};
+
+static gchar *
+create_channel_request (TpTestsSimpleChannelDispatcher *self,
+    const gchar *account,
+    GHashTable *request,
+    gint64 user_action_time,
+    const gchar *preferred_handler,
+    GHashTable *hints_,
+    GHashTable **out_immutable_properties)
+{
+  TpTestsSimpleChannelRequest *chan_request;
+  GPtrArray *requests;
+  static guint count = 0;
+  gchar *path;
+  TpDBusDaemon *dbus;
+  GHashTable *hints;
+
+  requests = g_ptr_array_sized_new (1);
+  g_ptr_array_add (requests, request);
+
+  path = g_strdup_printf ("/Request%u", count++);
+
+  if (hints_ == NULL)
+    hints = g_hash_table_new (NULL, NULL);
+  else
+    hints = g_hash_table_ref (hints_);
+
+  chan_request = tp_tests_simple_channel_request_new (path,
+      self->priv->conn, account, user_action_time, preferred_handler, requests,
+      hints);
+
+  g_hash_table_unref (hints);
+
+  self->priv->requests = g_slist_append (self->priv->requests, chan_request);
+
+  g_ptr_array_unref (requests);
+
+  dbus = tp_dbus_daemon_dup (NULL);
+  g_assert (dbus != NULL);
+
+  tp_dbus_daemon_register_object (dbus, path, chan_request);
+
+  g_object_unref (dbus);
+
+  g_signal_emit (self, signals[CHANNEL_REQUEST_CREATED], 0, chan_request);
+
+  *out_immutable_properties =
+      tp_tests_simple_channel_request_dup_immutable_props (chan_request);
+
+  return path;
+}
+
+static void
+tp_tests_simple_channel_dispatcher_create_channel (
+    TpSvcChannelDispatcher *dispatcher,
+    const gchar *account,
+    GHashTable *request,
+    gint64 user_action_time,
+    const gchar *preferred_handler,
+    GHashTable *hints,
+    DBusGMethodInvocation *context)
+{
+  TpTestsSimpleChannelDispatcher *self = SIMPLE_CHANNEL_DISPATCHER (dispatcher);
+  gchar *path;
+  GHashTable *immutable_properties;
+
+  tp_clear_pointer (&self->last_request, g_hash_table_unref);
+  self->last_request = g_boxed_copy (TP_HASH_TYPE_STRING_VARIANT_MAP, request);
+  tp_clear_pointer (&self->last_hints, g_hash_table_unref);
+  self->last_hints = g_boxed_copy (TP_HASH_TYPE_STRING_VARIANT_MAP, request);
+  self->last_user_action_time = user_action_time;
+  g_free (self->last_account);
+  self->last_account = g_strdup (account);
+  g_free (self->last_preferred_handler);
+  self->last_preferred_handler = g_strdup (preferred_handler);
+
+  if (tp_asv_get_boolean (request, "CreateChannelFail", NULL))
+    {
+      /* Fail to create the channel */
+      GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+          "Computer says no" };
+
+      dbus_g_method_return_error (context, &error);
+      return;
+    }
+
+  path = create_channel_request (self, account, request, user_action_time,
+      preferred_handler, hints, &immutable_properties);
+
+  if (path == NULL)
+    return;
+
+  tp_svc_channel_dispatcher_return_from_create_channel (context,
+      path, immutable_properties);
+
+  g_free (path);
+  g_hash_table_unref (immutable_properties);
+}
+
+static void
+tp_tests_simple_channel_dispatcher_ensure_channel (
+    TpSvcChannelDispatcher *dispatcher,
+    const gchar *account,
+    GHashTable *request,
+    gint64 user_action_time,
+    const gchar *preferred_handler,
+    GHashTable *hints,
+    DBusGMethodInvocation *context)
+{
+  TpTestsSimpleChannelDispatcher *self = SIMPLE_CHANNEL_DISPATCHER (dispatcher);
+  gchar *path;
+  GHashTable *immutable_properties;
+
+  tp_clear_pointer (&self->last_request, g_hash_table_unref);
+  self->last_request = g_boxed_copy (TP_HASH_TYPE_STRING_VARIANT_MAP, request);
+  tp_clear_pointer (&self->last_hints, g_hash_table_unref);
+  self->last_hints = g_boxed_copy (TP_HASH_TYPE_STRING_VARIANT_MAP, request);
+  self->last_user_action_time = user_action_time;
+  g_free (self->last_account);
+  self->last_account = g_strdup (account);
+  g_free (self->last_preferred_handler);
+  self->last_preferred_handler = g_strdup (preferred_handler);
+
+  if (self->priv->old_handler != NULL)
+    {
+      /* Pretend that the channel already exists */
+      path = create_channel_request (self, account, request, user_action_time,
+          self->priv->old_handler, hints, &immutable_properties);
+    }
+  else
+    {
+      self->priv->old_handler = g_strdup (preferred_handler);
+
+      path = create_channel_request (self, account, request, user_action_time,
+          preferred_handler, hints, &immutable_properties);
+    }
+
+  tp_svc_channel_dispatcher_return_from_ensure_channel (context,
+      path, immutable_properties);
+
+  g_free (path);
+  g_hash_table_unref (immutable_properties);
+}
+
+static void
+free_not_delegated_error (gpointer data)
+{
+    g_boxed_free (TP_STRUCT_TYPE_NOT_DELEGATED_ERROR, data);
+}
+
+
+static void
+tp_tests_simple_channel_dispatcher_delegate_channels (
+    TpSvcChannelDispatcher *dispatcher,
+    const GPtrArray *channels,
+    gint64 user_action_time,
+    const gchar *preferred_handler,
+    DBusGMethodInvocation *context)
+{
+  TpTestsSimpleChannelDispatcher *self = (TpTestsSimpleChannelDispatcher *)
+    dispatcher;
+  GPtrArray *delegated;
+  GHashTable *not_delegated;
+  guint i;
+
+  delegated = g_ptr_array_new ();
+  not_delegated = g_hash_table_new_full (g_str_hash, g_str_equal,
+      NULL, free_not_delegated_error);
+
+  for (i = 0; i < channels->len; i++)
+    {
+      gpointer chan_path = g_ptr_array_index (channels, i);
+      GValueArray *v;
+
+      if (!self->refuse_delegate)
+        {
+          g_ptr_array_add (delegated, chan_path);
+          continue;
+        }
+
+      v = tp_value_array_build (2,
+        G_TYPE_STRING, TP_ERROR_STR_BUSY,
+        G_TYPE_STRING, "Nah!",
+        G_TYPE_INVALID);
+
+      g_hash_table_insert (not_delegated, chan_path, v);
+    }
+
+  tp_svc_channel_dispatcher_return_from_delegate_channels (context, delegated,
+      not_delegated);
+
+  g_ptr_array_unref  (delegated);
+  g_hash_table_unref (not_delegated);
+}
+
+static void
+tp_tests_simple_channel_dispatcher_present_channel (
+    TpSvcChannelDispatcher *dispatcher,
+    const gchar *channel,
+    gint64 user_action_time,
+    DBusGMethodInvocation *context)
+{
+  tp_svc_channel_dispatcher_return_from_present_channel (context);
+}
+
+static void
+channel_dispatcher_iface_init (gpointer klass,
+    gpointer unused G_GNUC_UNUSED)
+{
+#define IMPLEMENT(x) tp_svc_channel_dispatcher_implement_##x (\
+  klass, tp_tests_simple_channel_dispatcher_##x)
+  IMPLEMENT (create_channel);
+  IMPLEMENT (ensure_channel);
+  IMPLEMENT (delegate_channels);
+  IMPLEMENT (present_channel);
+#undef IMPLEMENT
+}
+
+
+static void
+tp_tests_simple_channel_dispatcher_init (TpTestsSimpleChannelDispatcher *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+      TP_TESTS_TYPE_SIMPLE_CHANNEL_DISPATCHER,
+      TpTestsSimpleChannelDispatcherPrivate);
+}
+
+static void
+tp_tests_simple_channel_dispatcher_get_property (GObject *object,
+              guint property_id,
+              GValue *value,
+              GParamSpec *spec)
+{
+  switch (property_id) {
+    case PROP_INTERFACES:
+      g_value_set_boxed (value, CHANNEL_DISPATCHER_INTERFACES);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
+      break;
+  }
+}
+
+static void
+tp_tests_simple_channel_dispatcher_set_property (GObject *object,
+              guint property_id,
+              const GValue *value,
+              GParamSpec *spec)
+{
+  TpTestsSimpleChannelDispatcher *self = SIMPLE_CHANNEL_DISPATCHER (object);
+
+  switch (property_id) {
+    case PROP_CONNECTION:
+      self->priv->conn = g_value_dup_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
+      break;
+  }
+}
+
+static void
+tp_tests_simple_channel_dispatcher_dispose (GObject *object)
+{
+  TpTestsSimpleChannelDispatcher *self = SIMPLE_CHANNEL_DISPATCHER (object);
+
+  tp_clear_object (&self->priv->conn);
+
+  g_slist_foreach (self->priv->requests, (GFunc) g_object_unref, NULL);
+  g_slist_free (self->priv->requests);
+
+  g_free (self->priv->old_handler);
+
+  if (G_OBJECT_CLASS (tp_tests_simple_channel_dispatcher_parent_class)->dispose != NULL)
+    G_OBJECT_CLASS (tp_tests_simple_channel_dispatcher_parent_class)->dispose (object);
+}
+
+static void
+tp_tests_simple_channel_dispatcher_class_init (
+    TpTestsSimpleChannelDispatcherClass *klass)
+{
+  GObjectClass *object_class = (GObjectClass *) klass;
+  GParamSpec *param_spec;
+
+  static TpDBusPropertiesMixinPropImpl am_props[] = {
+        { "Interfaces", "interfaces", NULL },
+        { NULL }
+  };
+
+  static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
+        { TP_IFACE_CHANNEL_DISPATCHER,
+          tp_dbus_properties_mixin_getter_gobject_properties,
+          NULL,
+          am_props
+        },
+        { NULL },
+  };
+
+  g_type_class_add_private (klass, sizeof (TpTestsSimpleChannelDispatcherPrivate));
+  object_class->get_property = tp_tests_simple_channel_dispatcher_get_property;
+  object_class->set_property = tp_tests_simple_channel_dispatcher_set_property;
+
+  object_class->dispose = tp_tests_simple_channel_dispatcher_dispose;
+
+  param_spec = g_param_spec_boxed ("interfaces", "Extra D-Bus interfaces",
+      "In this case we only implement ChannelDispatcher, so none.",
+      G_TYPE_STRV,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_INTERFACES, param_spec);
+
+  param_spec = g_param_spec_object ("connection", "TpTestsSimpleConnection",
+      "connection to use when creating channels",
+      TP_TESTS_TYPE_SIMPLE_CONNECTION,
+      G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+  /* Fired when we create a new channel request object. This can be used in
+   * test to track the progression of a request. */
+  signals[CHANNEL_REQUEST_CREATED] = g_signal_new ("channel-request-created",
+      G_TYPE_FROM_CLASS (object_class),
+      G_SIGNAL_RUN_LAST,
+      0, NULL, NULL, NULL,
+      G_TYPE_NONE, 1, TP_TESTS_TYPE_SIMPLE_CHANNEL_REQUEST);
+
+  klass->dbus_props_class.interfaces = prop_interfaces;
+  tp_dbus_properties_mixin_class_init (object_class,
+      G_STRUCT_OFFSET (TpTestsSimpleChannelDispatcherClass, dbus_props_class));
+}
diff --git a/tests/lib/telepathy/contactlist/simple-channel-dispatcher.h 
b/tests/lib/telepathy/contactlist/simple-channel-dispatcher.h
new file mode 100644
index 0000000..da21406
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/simple-channel-dispatcher.h
@@ -0,0 +1,66 @@
+/*
+ * simple-channel-dispatcher.h - header for a simple channel dispatcher service.
+ *
+ * Copyright © 2010 Collabora Ltd.
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef __TP_TESTS_SIMPLE_CHANNEL_DISPATCHER_H__
+#define __TP_TESTS_SIMPLE_CHANNEL_DISPATCHER_H__
+
+#include <glib-object.h>
+#include <telepathy-glib/telepathy-glib.h>
+
+
+G_BEGIN_DECLS
+
+typedef struct _TpTestsSimpleChannelDispatcher TpTestsSimpleChannelDispatcher;
+typedef struct _TpTestsSimpleChannelDispatcherClass TpTestsSimpleChannelDispatcherClass;
+typedef struct _TpTestsSimpleChannelDispatcherPrivate TpTestsSimpleChannelDispatcherPrivate;
+
+struct _TpTestsSimpleChannelDispatcherClass {
+    GObjectClass parent_class;
+    TpDBusPropertiesMixinClass dbus_props_class;
+};
+
+struct _TpTestsSimpleChannelDispatcher {
+    GObject parent;
+
+    /* so regression tests can verify what was asked for */
+    GHashTable *last_request;
+    gchar *last_account;
+    gint64 last_user_action_time;
+    gchar *last_preferred_handler;
+    GHashTable *last_hints;
+
+    TpTestsSimpleChannelDispatcherPrivate *priv;
+
+    gboolean refuse_delegate;
+};
+
+GType tp_tests_simple_channel_dispatcher_get_type (void);
+
+/* TYPE MACROS */
+#define TP_TESTS_TYPE_SIMPLE_CHANNEL_DISPATCHER \
+  (tp_tests_simple_channel_dispatcher_get_type ())
+#define SIMPLE_CHANNEL_DISPATCHER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_SIMPLE_CHANNEL_DISPATCHER, \
+                              TpTestsSimpleChannelDispatcher))
+#define SIMPLE_CHANNEL_DISPATCHER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_SIMPLE_CHANNEL_DISPATCHER, \
+                           TpTestsSimpleChannelDispatcherClass))
+#define SIMPLE_IS_CHANNEL_DISPATCHER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_SIMPLE_CHANNEL_DISPATCHER))
+#define SIMPLE_IS_CHANNEL_DISPATCHER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_SIMPLE_CHANNEL_DISPATCHER))
+#define SIMPLE_CHANNEL_DISPATCHER_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_SIMPLE_CHANNEL_DISPATCHER, \
+                              TpTestsSimpleChannelDispatcherClass))
+
+
+G_END_DECLS
+
+#endif /* #ifndef __TP_TESTS_SIMPLE_CHANNEL_DISPATCHER_H__ */
diff --git a/tests/lib/telepathy/contactlist/simple-channel-manager.c 
b/tests/lib/telepathy/contactlist/simple-channel-manager.c
new file mode 100644
index 0000000..ba60a9d
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/simple-channel-manager.c
@@ -0,0 +1,95 @@
+/*
+ * simple-channel-manager.c
+ *
+ * Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "config.h"
+
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
+
+#include "simple-channel-manager.h"
+#include "util.h"
+#include "echo-chan.h"
+
+static void channel_manager_iface_init (gpointer, gpointer);
+
+G_DEFINE_TYPE_WITH_CODE (TpTestsSimpleChannelManager,
+    tp_tests_simple_channel_manager, G_TYPE_OBJECT,
+    G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, channel_manager_iface_init);
+    )
+
+/* signals */
+enum {
+  REQUEST,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+static void
+tp_tests_simple_channel_manager_class_init (TpTestsSimpleChannelManagerClass *klass)
+{
+  signals[REQUEST] = g_signal_new ("request",
+      G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST,
+      0, NULL, NULL, NULL,
+      G_TYPE_NONE, 1, G_TYPE_HASH_TABLE);
+}
+
+static void
+tp_tests_simple_channel_manager_init (TpTestsSimpleChannelManager *self)
+{
+}
+
+static gboolean
+tp_tests_simple_channel_manager_request (TpChannelManager *manager,
+    gpointer request_token,
+    GHashTable *request_properties)
+{
+  TpTestsSimpleChannelManager *self =
+    TP_TESTS_SIMPLE_CHANNEL_MANAGER (manager);
+  GSList *tokens;
+  TpExportableChannel *channel;
+  TpHandle handle = tp_asv_get_uint32 (request_properties,
+      TP_PROP_CHANNEL_TARGET_HANDLE, NULL);
+  gchar *path;
+
+  g_signal_emit (manager, signals[REQUEST], 0, request_properties);
+
+  tokens = g_slist_append (NULL, request_token);
+
+  path = g_strdup_printf ("%s/Channel",
+      tp_base_connection_get_object_path (self->conn));
+
+  channel = tp_tests_object_new_static_class (
+      TP_TESTS_TYPE_ECHO_CHANNEL,
+      "connection", self->conn,
+      "object-path", path,
+      "handle", handle,
+      NULL);
+
+  tp_channel_manager_emit_new_channel (manager, channel, tokens);
+
+  g_free (path);
+  g_slist_free (tokens);
+  g_object_unref (channel);
+
+  return TRUE;
+}
+
+static void
+channel_manager_iface_init (gpointer g_iface,
+    gpointer giface_data G_GNUC_UNUSED)
+{
+  TpChannelManagerIface *iface = g_iface;
+
+  iface->create_channel = tp_tests_simple_channel_manager_request;
+  iface->ensure_channel = tp_tests_simple_channel_manager_request;
+  iface->request_channel = tp_tests_simple_channel_manager_request;
+}
diff --git a/tests/lib/telepathy/contactlist/simple-channel-manager.h 
b/tests/lib/telepathy/contactlist/simple-channel-manager.h
new file mode 100644
index 0000000..ad711cd
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/simple-channel-manager.h
@@ -0,0 +1,54 @@
+/*
+ * simple-channel-manager.h
+ *
+ * Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef __TP_TESTS_SIMPLE_CHANNEL_MANAGER_H__
+#define __TP_TESTS_SIMPLE_CHANNEL_MANAGER_H__
+
+#include <glib-object.h>
+
+#include <telepathy-glib/telepathy-glib.h>
+
+typedef struct _TpTestsSimpleChannelManager TpTestsSimpleChannelManager;
+typedef struct _TpTestsSimpleChannelManagerClass TpTestsSimpleChannelManagerClass;
+
+struct _TpTestsSimpleChannelManager
+{
+  GObject parent;
+
+  TpBaseConnection *conn;
+};
+
+struct _TpTestsSimpleChannelManagerClass
+{
+  GObjectClass parent_class;
+};
+
+GType tp_tests_simple_channel_manager_get_type (void);
+
+/* TYPE MACROS */
+#define TP_TESTS_TYPE_SIMPLE_CHANNEL_MANAGER \
+  (tp_tests_simple_channel_manager_get_type ())
+#define TP_TESTS_SIMPLE_CHANNEL_MANAGER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_SIMPLE_CHANNEL_MANAGER, \
+                              TpTestsSimpleChannelManager))
+#define TP_TESTS_SIMPLE_CHANNEL_MANAGER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_SIMPLE_CHANNEL_MANAGER, \
+                           TpTestsSimpleChannelManagerClass))
+#define TP_TESTS_IS_SIMPLE_CHANNEL_MANAGER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_SIMPLE_CHANNEL_MANAGER))
+#define TP_TESTS_IS_SIMPLE_CHANNEL_MANAGER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_SIMPLE_CHANNEL_MANAGER))
+#define TP_TESTS_SIMPLE_CHANNEL_MANAGER_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_SIMPLE_CHANNEL_MANAGER, \
+                              TpTestsSimpleChannelManagerClass))
+
+G_END_DECLS
+
+#endif /* #ifndef __TP_TESTS_SIMPLE_CHANNEL_MANAGER_H__ */
diff --git a/tests/lib/telepathy/contactlist/simple-channel-request.c 
b/tests/lib/telepathy/contactlist/simple-channel-request.c
new file mode 100644
index 0000000..4884cb0
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/simple-channel-request.c
@@ -0,0 +1,508 @@
+/*
+ * simple-channel-request.c - simple channel request service.
+ *
+ * Copyright © 2010 Collabora Ltd.
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "config.h"
+
+#include "simple-channel-request.h"
+
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
+#include <telepathy-glib/proxy-subclass.h>
+
+#include "tests/lib/util.h"
+
+static void channel_request_iface_init (gpointer, gpointer);
+
+G_DEFINE_TYPE_WITH_CODE (TpTestsSimpleChannelRequest,
+    tp_tests_simple_channel_request,
+    G_TYPE_OBJECT,
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_REQUEST,
+        channel_request_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
+        tp_dbus_properties_mixin_iface_init)
+    )
+
+
+/* TP_IFACE_CHANNEL_REQUEST is implied */
+static const char *CHANNEL_REQUEST_INTERFACES[] = { NULL };
+
+enum
+{
+  PROP_0,
+  PROP_PATH,
+  PROP_ACCOUNT,
+  PROP_USER_ACTION_TIME,
+  PROP_PREFERRED_HANDLER,
+  PROP_REQUESTS,
+  PROP_CONNECTION,
+  PROP_INTERFACES,
+  PROP_HINTS,
+};
+
+struct _TpTestsSimpleChannelRequestPrivate
+{
+  /* D-Bus properties */
+  gchar *account_path;
+  gint64 user_action_time;
+  gchar *preferred_handler;
+  GPtrArray *requests;
+  GHashTable *hints;
+
+  /* Our own path */
+  gchar *path;
+
+  /* connection used to create channels */
+  TpTestsSimpleConnection *conn;
+};
+
+static void
+handle_channels_cb (TpClient *client,
+    const GError *error,
+    gpointer user_data,
+    GObject *weak_object)
+{
+  TpTestsSimpleChannelRequest *self = SIMPLE_CHANNEL_REQUEST (weak_object);
+  TpBaseConnection *base_conn = TP_BASE_CONNECTION (self->priv->conn);
+  const gchar *chan_path = user_data;
+  GHashTable *props = tp_asv_new (NULL, NULL);
+
+  if (error != NULL)
+    {
+      tp_svc_channel_request_emit_failed (self,
+          tp_error_get_dbus_name (error->code), error->message);
+      return;
+    }
+
+  tp_svc_channel_request_emit_succeeded (self,
+      tp_base_connection_get_object_path (base_conn), props, chan_path, props);
+
+  g_hash_table_unref (props);
+}
+
+static gchar *
+add_channel (TpTestsSimpleChannelRequest *self,
+    GPtrArray *channels)
+{
+  const gchar *target_id;
+  gchar *chan_path;
+  GHashTable *request;
+  GHashTable *props;
+  const char *chan_type;
+
+  request = g_ptr_array_index (self->priv->requests, 0);
+  chan_type = tp_asv_get_string (request, TP_PROP_CHANNEL_CHANNEL_TYPE);
+
+  if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_TEXT))
+    {
+      target_id = tp_asv_get_string (request, TP_PROP_CHANNEL_TARGET_ID);
+      g_assert (target_id != NULL);
+
+      chan_path = tp_tests_simple_connection_ensure_text_chan (self->priv->conn,
+          target_id, &props);
+    }
+  else if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_ROOM_LIST))
+    {
+      chan_path = tp_tests_simple_connection_ensure_room_list_chan (
+          self->priv->conn, tp_asv_get_string (request,
+            TP_PROP_CHANNEL_TYPE_ROOM_LIST_SERVER), &props);
+    }
+  else
+    {
+      g_assert_not_reached ();
+    }
+
+  g_ptr_array_add (channels, tp_value_array_build (2,
+      DBUS_TYPE_G_OBJECT_PATH, chan_path,
+      TP_HASH_TYPE_STRING_VARIANT_MAP, props,
+      G_TYPE_INVALID));
+
+  g_hash_table_unref (props);
+
+  return chan_path;
+}
+
+static void
+free_channel_details (gpointer data,
+    gpointer user_data)
+{
+  g_boxed_free (TP_STRUCT_TYPE_CHANNEL_DETAILS, data);
+}
+
+GHashTable *
+tp_tests_simple_channel_request_dup_immutable_props (
+    TpTestsSimpleChannelRequest *self)
+{
+  return tp_asv_new (
+      TP_PROP_CHANNEL_REQUEST_ACCOUNT, DBUS_TYPE_G_OBJECT_PATH,
+        self->priv->account_path,
+      TP_PROP_CHANNEL_REQUEST_USER_ACTION_TIME, G_TYPE_INT64,
+        self->priv->user_action_time,
+      TP_PROP_CHANNEL_REQUEST_PREFERRED_HANDLER, G_TYPE_STRING,
+        self->priv->preferred_handler,
+      TP_PROP_CHANNEL_REQUEST_REQUESTS, TP_ARRAY_TYPE_CHANNEL_CLASS_LIST,
+        self->priv->requests,
+      TP_PROP_CHANNEL_REQUEST_INTERFACES, G_TYPE_STRV, NULL,
+      TP_PROP_CHANNEL_REQUEST_HINTS, TP_HASH_TYPE_STRING_VARIANT_MAP,
+        self->priv->hints,
+      NULL);
+}
+
+static void
+tp_tests_simple_channel_request_proceed (TpSvcChannelRequest *request,
+    DBusGMethodInvocation *context)
+{
+  TpTestsSimpleChannelRequest *self = SIMPLE_CHANNEL_REQUEST (request);
+  TpClient *client;
+  TpDBusDaemon *dbus;
+  gchar *client_path;
+  GPtrArray *channels;
+  GPtrArray *satisfied;
+  GHashTable *info;
+  TpBaseConnection *base_conn = (TpBaseConnection *) self->priv->conn;
+  GHashTable *req;
+  GHashTable *request_props;
+  gchar *chan_path;
+
+  req = g_ptr_array_index (self->priv->requests, 0);
+  g_assert (req != NULL);
+  if (tp_asv_get_boolean (req, "ProceedFail", NULL))
+    {
+      /* We have been asked to fail */
+     GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+          "Computer says no" };
+
+      dbus_g_method_return_error (context, &error);
+      return;
+    }
+
+  tp_svc_channel_request_return_from_proceed (context);
+
+  if (tp_asv_get_boolean (req, "FireFailed", NULL))
+    {
+      /* We have been asked to fire the 'Failed' signal */
+      tp_svc_channel_request_emit_failed (self, TP_ERROR_STR_INVALID_ARGUMENT,
+          "Let's fail!");
+      return;
+    }
+
+  /* We just support handling request having a preferred handler */
+  if (self->priv->preferred_handler == NULL ||
+      !tp_strdiff (self->priv->preferred_handler, ""))
+    {
+      tp_svc_channel_request_emit_failed (self, TP_ERROR_STR_NOT_AVAILABLE,
+          "ChannelRequest doesn't have a preferred handler");
+      return;
+    }
+
+  if (!tp_strdiff (self->priv->preferred_handler, "Fake"))
+    {
+      /* Pretend that the channel has been handled */
+      GHashTable *props;
+      props = g_hash_table_new (NULL, NULL);
+
+      tp_svc_channel_request_emit_succeeded (self,
+          tp_base_connection_get_object_path (base_conn),
+          props, "/chan", props);
+
+      g_hash_table_unref (props);
+      return;
+    }
+
+  /* Call HandleChannels() on the preferred handler */
+  client_path = g_strdelimit (g_strdup_printf ("/%s",
+        self->priv->preferred_handler), ".", '/');
+
+  dbus = tp_dbus_daemon_dup (NULL);
+  g_assert (dbus != NULL);
+
+  client = tp_tests_object_new_static_class (TP_TYPE_CLIENT,
+          "dbus-daemon", dbus,
+          "bus-name", self->priv->preferred_handler,
+          "object-path", client_path,
+          NULL);
+
+  tp_proxy_add_interface_by_id (TP_PROXY (client), TP_IFACE_QUARK_CLIENT);
+  tp_proxy_add_interface_by_id (TP_PROXY (client),
+      TP_IFACE_QUARK_CLIENT_HANDLER);
+
+  channels = g_ptr_array_sized_new (1);
+  chan_path = add_channel (self, channels);
+
+  satisfied = g_ptr_array_sized_new (1);
+  g_ptr_array_add (satisfied, self->priv->path);
+
+  request_props = g_hash_table_new_full (g_str_hash, g_str_equal,
+      g_free, (GDestroyNotify) g_hash_table_unref);
+
+  g_hash_table_insert (request_props, g_strdup (self->priv->path),
+      tp_tests_simple_channel_request_dup_immutable_props (self));
+
+  info = tp_asv_new (
+      "request-properties", TP_HASH_TYPE_OBJECT_IMMUTABLE_PROPERTIES_MAP,
+        request_props,
+      NULL);
+
+  tp_cli_client_handler_call_handle_channels (client, -1,
+      self->priv->account_path,
+      tp_base_connection_get_object_path (base_conn), channels,
+      satisfied, self->priv->user_action_time, info, handle_channels_cb,
+      g_strdup (chan_path), g_free, G_OBJECT (self));
+
+  g_free (chan_path);
+  g_free (client_path);
+  g_ptr_array_foreach (channels, free_channel_details, NULL);
+  g_ptr_array_unref (channels);
+  g_ptr_array_unref (satisfied);
+  g_hash_table_unref (info);
+  g_hash_table_unref (request_props);
+  g_object_unref (dbus);
+  g_object_unref (client);
+}
+
+static void
+tp_tests_simple_channel_request_cancel (TpSvcChannelRequest *request,
+    DBusGMethodInvocation *context)
+{
+  tp_svc_channel_request_emit_failed (request, TP_ERROR_STR_CANCELLED,
+      "ChannelRequest has been cancelled");
+
+  tp_svc_channel_request_return_from_cancel (context);
+}
+
+static void
+channel_request_iface_init (gpointer klass,
+    gpointer unused G_GNUC_UNUSED)
+{
+#define IMPLEMENT(x) tp_svc_channel_request_implement_##x (\
+  klass, tp_tests_simple_channel_request_##x)
+  IMPLEMENT (proceed);
+  IMPLEMENT (cancel);
+#undef IMPLEMENT
+}
+
+static void
+tp_tests_simple_channel_request_init (TpTestsSimpleChannelRequest *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+      TP_TESTS_TYPE_SIMPLE_CHANNEL_REQUEST,
+      TpTestsSimpleChannelRequestPrivate);
+}
+
+static void
+tp_tests_simple_channel_request_get_property (GObject *object,
+              guint property_id,
+              GValue *value,
+              GParamSpec *spec)
+{
+  TpTestsSimpleChannelRequest *self = SIMPLE_CHANNEL_REQUEST (object);
+
+  switch (property_id) {
+    case PROP_PATH:
+      g_value_set_string (value, self->priv->path);
+      break;
+
+    case PROP_ACCOUNT:
+      g_value_set_string (value, self->priv->account_path);
+      break;
+
+    case PROP_USER_ACTION_TIME:
+      g_value_set_int64 (value, self->priv->user_action_time);
+      break;
+
+    case PROP_PREFERRED_HANDLER:
+      g_value_set_string (value, self->priv->preferred_handler);
+      break;
+
+    case PROP_REQUESTS:
+      g_value_set_boxed (value, self->priv->requests);
+      break;
+
+    case PROP_INTERFACES:
+      g_value_set_boxed (value, CHANNEL_REQUEST_INTERFACES);
+      break;
+
+    case PROP_HINTS:
+      g_value_set_boxed (value, self->priv->hints);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
+      break;
+  }
+}
+
+static void
+tp_tests_simple_channel_request_set_property (GObject *object,
+              guint property_id,
+              const GValue *value,
+              GParamSpec *spec)
+{
+  TpTestsSimpleChannelRequest *self = SIMPLE_CHANNEL_REQUEST (object);
+
+  switch (property_id) {
+    case PROP_PATH:
+      self->priv->path = g_value_dup_string (value);
+      break;
+
+    case PROP_ACCOUNT:
+      self->priv->account_path = g_value_dup_string (value);
+      break;
+
+    case PROP_USER_ACTION_TIME:
+      self->priv->user_action_time = g_value_get_int64 (value);
+      break;
+
+    case PROP_PREFERRED_HANDLER:
+      self->priv->preferred_handler = g_value_dup_string (value);
+      break;
+
+    case PROP_REQUESTS:
+      self->priv->requests = g_value_dup_boxed (value);
+      break;
+
+    case PROP_CONNECTION:
+      self->priv->conn = g_value_dup_object (value);
+      break;
+
+    case PROP_HINTS:
+      self->priv->hints = g_value_dup_boxed (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
+      break;
+  }
+}
+
+static void
+tp_tests_simple_channel_request_dispose (GObject *object)
+{
+  TpTestsSimpleChannelRequest *self = SIMPLE_CHANNEL_REQUEST (object);
+
+  g_free (self->priv->path);
+  g_free (self->priv->account_path);
+  g_free (self->priv->preferred_handler);
+
+  if (self->priv->requests != NULL)
+    {
+      g_boxed_free (TP_ARRAY_TYPE_CHANNEL_CLASS_LIST, self->priv->requests);
+      self->priv->requests = NULL;
+    }
+
+  tp_clear_object (&self->priv->conn);
+  tp_clear_pointer (&self->priv->hints, g_hash_table_unref);
+
+  if (G_OBJECT_CLASS (tp_tests_simple_channel_request_parent_class)->dispose != NULL)
+    G_OBJECT_CLASS (tp_tests_simple_channel_request_parent_class)->dispose (object);
+}
+
+static void
+tp_tests_simple_channel_request_class_init (
+    TpTestsSimpleChannelRequestClass *klass)
+{
+  GObjectClass *object_class = (GObjectClass *) klass;
+  GParamSpec *param_spec;
+
+  static TpDBusPropertiesMixinPropImpl am_props[] = {
+        { "Interfaces", "interfaces", NULL },
+        { "Hints", "hints", NULL },
+        { NULL }
+  };
+
+  static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
+        { TP_IFACE_CHANNEL_REQUEST,
+          tp_dbus_properties_mixin_getter_gobject_properties,
+          NULL,
+          am_props
+        },
+        { NULL },
+  };
+
+  g_type_class_add_private (klass, sizeof (TpTestsSimpleChannelRequestPrivate));
+  object_class->get_property = tp_tests_simple_channel_request_get_property;
+  object_class->set_property = tp_tests_simple_channel_request_set_property;
+
+  object_class->dispose = tp_tests_simple_channel_request_dispose;
+
+  param_spec = g_param_spec_string ("path", "Path",
+      "Path of this ChannelRequest",
+      NULL,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_PATH, param_spec);
+
+  param_spec = g_param_spec_string ("account", "Account",
+      "Path of the Account",
+      NULL,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_ACCOUNT, param_spec);
+
+  param_spec = g_param_spec_int64 ("user-action-time", "UserActionTime",
+      "UserActionTime",
+      G_MININT64, G_MAXINT64, 0,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_USER_ACTION_TIME,
+      param_spec);
+
+  param_spec = g_param_spec_string ("preferred-handler", "PreferredHandler",
+      "PreferredHandler",
+      NULL,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_PREFERRED_HANDLER,
+      param_spec);
+
+  param_spec = g_param_spec_boxed ("requests", "Requests",
+      "Requests",
+      TP_ARRAY_TYPE_CHANNEL_CLASS_LIST,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_REQUESTS, param_spec);
+
+  param_spec = g_param_spec_boxed ("interfaces", "Extra D-Bus interfaces",
+      "In this case we only implement ChannelRequest, so none.",
+      G_TYPE_STRV,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_INTERFACES, param_spec);
+
+  param_spec = g_param_spec_object ("connection", "TpBaseConnection",
+      "connection to use when creating channels",
+      TP_TYPE_BASE_CONNECTION,
+      G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+  param_spec = g_param_spec_boxed ("hints", "Hints",
+      "Metadata provided by the channel's requester, if any",
+      TP_HASH_TYPE_STRING_VARIANT_MAP,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_HINTS, param_spec);
+
+  klass->dbus_props_class.interfaces = prop_interfaces;
+  tp_dbus_properties_mixin_class_init (object_class,
+      G_STRUCT_OFFSET (TpTestsSimpleChannelRequestClass, dbus_props_class));
+}
+
+TpTestsSimpleChannelRequest *
+tp_tests_simple_channel_request_new (const gchar *path,
+    TpTestsSimpleConnection *conn,
+    const gchar *account_path,
+    gint64 user_action_time,
+    const gchar *preferred_handler,
+    GPtrArray *requests,
+    GHashTable *hints)
+{
+  return tp_tests_object_new_static_class (
+      TP_TESTS_TYPE_SIMPLE_CHANNEL_REQUEST,
+      "path", path,
+      "connection", conn,
+      "account", account_path,
+      "user-action-time", user_action_time,
+      "preferred-handler", preferred_handler,
+      "requests", requests,
+      "hints", hints,
+      NULL);
+}
diff --git a/tests/lib/telepathy/contactlist/simple-channel-request.h 
b/tests/lib/telepathy/contactlist/simple-channel-request.h
new file mode 100644
index 0000000..60ea3be
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/simple-channel-request.h
@@ -0,0 +1,70 @@
+/*
+ * simple-channel-request.h - header for a simple channel request service.
+ *
+ * Copyright © 2010 Collabora Ltd.
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef __TP_TESTS_SIMPLE_CHANNEL_REQUEST_H__
+#define __TP_TESTS_SIMPLE_CHANNEL_REQUEST_H__
+
+#include <glib-object.h>
+
+#include <telepathy-glib/telepathy-glib.h>
+
+#include "simple-conn.h"
+
+G_BEGIN_DECLS
+
+typedef struct _TpTestsSimpleChannelRequest TpTestsSimpleChannelRequest;
+typedef struct _TpTestsSimpleChannelRequestClass TpTestsSimpleChannelRequestClass;
+typedef struct _TpTestsSimpleChannelRequestPrivate TpTestsSimpleChannelRequestPrivate;
+
+struct _TpTestsSimpleChannelRequestClass {
+    GObjectClass parent_class;
+    TpDBusPropertiesMixinClass dbus_props_class;
+};
+
+struct _TpTestsSimpleChannelRequest {
+    GObject parent;
+
+    TpTestsSimpleChannelRequestPrivate *priv;
+};
+
+GType tp_tests_simple_channel_request_get_type (void);
+
+/* TYPE MACROS */
+#define TP_TESTS_TYPE_SIMPLE_CHANNEL_REQUEST \
+  (tp_tests_simple_channel_request_get_type ())
+#define SIMPLE_CHANNEL_REQUEST(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_SIMPLE_CHANNEL_REQUEST, \
+                              TpTestsSimpleChannelRequest))
+#define SIMPLE_CHANNEL_REQUEST_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_SIMPLE_CHANNEL_REQUEST, \
+                           TpTestsSimpleChannelRequestClass))
+#define SIMPLE_IS_CHANNEL_REQUEST(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_SIMPLE_CHANNEL_REQUEST))
+#define SIMPLE_IS_CHANNEL_REQUEST_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_SIMPLE_CHANNEL_REQUEST))
+#define SIMPLE_CHANNEL_REQUEST_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_SIMPLE_CHANNEL_REQUEST, \
+                              TpTestsSimpleChannelRequestClass))
+
+TpTestsSimpleChannelRequest *
+tp_tests_simple_channel_request_new (const gchar *path,
+    TpTestsSimpleConnection *conn,
+    const gchar *account_path,
+    gint64 user_action_time,
+    const gchar *preferred_handler,
+    GPtrArray *requests,
+    GHashTable *hints);
+
+GHashTable * tp_tests_simple_channel_request_dup_immutable_props (
+    TpTestsSimpleChannelRequest *self);
+
+G_END_DECLS
+
+#endif /* #ifndef __TP_TESTS_SIMPLE_CHANNEL_REQUEST_H__ */
diff --git a/tests/lib/telepathy/contactlist/simple-client.c b/tests/lib/telepathy/contactlist/simple-client.c
new file mode 100644
index 0000000..c743164
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/simple-client.c
@@ -0,0 +1,250 @@
+/*
+ * simple-client.c - a simple client
+ *
+ * Copyright © 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "config.h"
+
+#include "simple-client.h"
+
+#include <string.h>
+
+#include <dbus/dbus-glib.h>
+
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
+
+#include "tests/lib/util.h"
+
+G_DEFINE_TYPE (TpTestsSimpleClient, tp_tests_simple_client, TP_TYPE_BASE_CLIENT)
+
+static void
+simple_observe_channels (
+    TpBaseClient *client,
+    TpAccount *account,
+    TpConnection *connection,
+    GList *channels,
+    TpChannelDispatchOperation *dispatch_operation,
+    GList *requests,
+    TpObserveChannelsContext *context)
+{
+  TpTestsSimpleClient *self = TP_TESTS_SIMPLE_CLIENT (client);
+  GHashTable *info;
+  gboolean fail;
+  GList *l;
+
+  /* Fail if caller set the fake "FAIL" info */
+  g_object_get (context, "observer-info", &info, NULL);
+  fail = tp_asv_get_boolean (info, "FAIL", NULL);
+  g_hash_table_unref (info);
+
+  if (self->observe_ctx != NULL)
+    {
+      g_object_unref (self->observe_ctx);
+      self->observe_ctx = NULL;
+    }
+
+  if (fail)
+    {
+      GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+          "No observation for you!" };
+
+      tp_observe_channels_context_fail (context, &error);
+      return;
+    }
+
+  g_assert (TP_IS_ACCOUNT (account));
+  g_assert (tp_proxy_is_prepared (account, TP_ACCOUNT_FEATURE_CORE));
+
+  g_assert (TP_IS_CONNECTION (connection));
+  g_assert (tp_proxy_is_prepared (connection, TP_CONNECTION_FEATURE_CORE));
+
+  g_assert_cmpuint (g_list_length (channels), >, 0);
+  for (l = channels; l != NULL; l = g_list_next (l))
+    {
+      TpChannel *channel = l->data;
+
+      g_assert (TP_IS_CHANNEL (channel));
+      g_assert (tp_proxy_is_prepared (channel, TP_CHANNEL_FEATURE_CORE) ||
+          tp_proxy_get_invalidated (channel) != NULL);
+    }
+
+  if (dispatch_operation != NULL)
+    g_assert (TP_IS_CHANNEL_DISPATCH_OPERATION (dispatch_operation));
+
+  for (l = requests; l != NULL; l = g_list_next (l))
+    {
+      TpChannelRequest *request = l->data;
+
+      g_assert (TP_IS_CHANNEL_REQUEST (request));
+    }
+
+  self->observe_ctx = g_object_ref (context);
+  tp_observe_channels_context_accept (context);
+}
+
+static void
+simple_add_dispatch_operation (
+    TpBaseClient *client,
+    TpAccount *account,
+    TpConnection *connection,
+    GList *channels,
+    TpChannelDispatchOperation *dispatch_operation,
+    TpAddDispatchOperationContext *context)
+{
+  TpTestsSimpleClient *self = TP_TESTS_SIMPLE_CLIENT (client);
+  GList *l;
+
+  g_assert (TP_IS_ACCOUNT (account));
+  g_assert (tp_proxy_is_prepared (account, TP_ACCOUNT_FEATURE_CORE));
+
+  g_assert (TP_IS_CONNECTION (connection));
+  g_assert (tp_proxy_is_prepared (connection, TP_CONNECTION_FEATURE_CORE));
+
+  g_assert (TP_IS_CHANNEL_DISPATCH_OPERATION (dispatch_operation));
+  g_assert (tp_proxy_is_prepared (dispatch_operation,
+        TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE) ||
+      tp_proxy_get_invalidated (dispatch_operation) != NULL);
+
+  if (self->add_dispatch_ctx != NULL)
+    {
+      g_object_unref (self->add_dispatch_ctx);
+      self->add_dispatch_ctx = NULL;
+    }
+
+  g_assert_cmpuint (g_list_length (channels), >, 0);
+  for (l = channels; l != NULL; l = g_list_next (l))
+    {
+      TpChannel *channel = l->data;
+
+      g_assert (TP_IS_CHANNEL (channel));
+      g_assert (tp_proxy_is_prepared (channel, TP_CHANNEL_FEATURE_CORE) ||
+          tp_proxy_get_invalidated (channel) != NULL);
+    }
+
+  self->add_dispatch_ctx = g_object_ref (context);
+  tp_add_dispatch_operation_context_accept (context);
+}
+
+static void
+simple_handle_channels (TpBaseClient *client,
+    TpAccount *account,
+    TpConnection *connection,
+    GList *channels,
+    GList *requests_satisfied,
+    gint64 user_action_time,
+    TpHandleChannelsContext *context)
+{
+  TpTestsSimpleClient *self = TP_TESTS_SIMPLE_CLIENT (client);
+  GList *l;
+
+  if (self->handle_channels_ctx != NULL)
+    {
+      g_object_unref (self->handle_channels_ctx);
+      self->handle_channels_ctx = NULL;
+    }
+
+  g_assert (TP_IS_ACCOUNT (account));
+  g_assert (tp_proxy_is_prepared (account, TP_ACCOUNT_FEATURE_CORE));
+
+  g_assert (TP_IS_CONNECTION (connection));
+  g_assert (tp_proxy_is_prepared (connection, TP_CONNECTION_FEATURE_CORE));
+
+  g_assert_cmpuint (g_list_length (channels), >, 0);
+  for (l = channels; l != NULL; l = g_list_next (l))
+    {
+      TpChannel *channel = l->data;
+
+      g_assert (TP_IS_CHANNEL (channel));
+      g_assert (tp_proxy_is_prepared (channel, TP_CHANNEL_FEATURE_CORE) ||
+          tp_proxy_get_invalidated (channel) != NULL);
+    }
+
+  for (l = requests_satisfied; l != NULL; l = g_list_next (l))
+    {
+      TpChannelRequest *request = l->data;
+
+      g_assert (TP_IS_CHANNEL_REQUEST (request));
+    }
+
+  self->handle_channels_ctx = g_object_ref (context);
+  tp_handle_channels_context_accept (context);
+}
+
+static void
+tp_tests_simple_client_init (TpTestsSimpleClient *self)
+{
+}
+
+static void
+tp_tests_simple_client_dispose (GObject *object)
+{
+  TpTestsSimpleClient *self = TP_TESTS_SIMPLE_CLIENT (object);
+  void (*dispose) (GObject *) =
+    G_OBJECT_CLASS (tp_tests_simple_client_parent_class)->dispose;
+
+  if (self->observe_ctx != NULL)
+    {
+      g_object_unref (self->observe_ctx);
+      self->observe_ctx = NULL;
+    }
+
+  if (self->add_dispatch_ctx != NULL)
+    {
+      g_object_unref (self->add_dispatch_ctx);
+      self->add_dispatch_ctx = NULL;
+    }
+
+  if (self->handle_channels_ctx != NULL)
+    {
+      g_object_unref (self->handle_channels_ctx);
+      self->handle_channels_ctx = NULL;
+    }
+
+  if (dispose != NULL)
+    dispose (object);
+}
+
+static void
+tp_tests_simple_client_class_init (TpTestsSimpleClientClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  TpBaseClientClass *base_class = TP_BASE_CLIENT_CLASS (klass);
+
+  object_class->dispose = tp_tests_simple_client_dispose;
+
+  tp_base_client_implement_observe_channels (base_class,
+      simple_observe_channels);
+
+  tp_base_client_implement_add_dispatch_operation (base_class,
+      simple_add_dispatch_operation);
+
+  tp_base_client_implement_handle_channels (base_class,
+      simple_handle_channels);
+}
+
+TpTestsSimpleClient *
+tp_tests_simple_client_new (TpClientFactory *factory,
+    const gchar *name,
+    gboolean uniquify_name)
+{
+  return tp_tests_object_new_static_class (TP_TESTS_TYPE_SIMPLE_CLIENT,
+      "factory", factory,
+      "name", name,
+      "uniquify-name", uniquify_name,
+      NULL);
+}
+
+TpTestsSimpleClient *
+tp_tests_simple_client_new_with_am (TpAccountManager *account_mgr,
+    const gchar *name,
+    gboolean uniquify_name)
+{
+  return tp_tests_simple_client_new (tp_proxy_get_factory (account_mgr),
+      name, uniquify_name);
+}
diff --git a/tests/lib/telepathy/contactlist/simple-client.h b/tests/lib/telepathy/contactlist/simple-client.h
new file mode 100644
index 0000000..b6f7b5f
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/simple-client.h
@@ -0,0 +1,64 @@
+/*
+ * simple-client.h - header for a simple client
+ *
+ * Copyright © 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef __TP_TESTS_SIMPLE_CLIENT_H__
+#define __TP_TESTS_SIMPLE_CLIENT_H__
+
+#include <glib-object.h>
+#include <telepathy-glib/telepathy-glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _TpTestsSimpleClient TpTestsSimpleClient;
+typedef struct _TpTestsSimpleClientClass TpTestsSimpleClientClass;
+
+struct _TpTestsSimpleClientClass {
+    TpBaseClientClass parent_class;
+};
+
+struct _TpTestsSimpleClient {
+    TpBaseClient parent;
+
+    TpObserveChannelsContext *observe_ctx;
+    TpAddDispatchOperationContext *add_dispatch_ctx;
+    TpHandleChannelsContext *handle_channels_ctx;
+};
+
+GType tp_tests_simple_client_get_type (void);
+
+/* TYPE MACROS */
+#define TP_TESTS_TYPE_SIMPLE_CLIENT \
+  (tp_tests_simple_client_get_type ())
+#define TP_TESTS_SIMPLE_CLIENT(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_SIMPLE_CLIENT, \
+                              TpTestsSimpleClient))
+#define TP_TESTS_SIMPLE_CLIENT_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_SIMPLE_CLIENT, \
+                           TpTestsSimpleClientClass))
+#define SIMPLE_IS_CLIENT(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_SIMPLE_CLIENT))
+#define SIMPLE_IS_CLIENT_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_SIMPLE_CLIENT))
+#define TP_TESTS_SIMPLE_CLIENT_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_SIMPLE_CLIENT, \
+                              TpTestsSimpleClientClass))
+
+TpTestsSimpleClient * tp_tests_simple_client_new (TpClientFactory *factory,
+    const gchar *name,
+    gboolean uniquify_name);
+
+TpTestsSimpleClient * tp_tests_simple_client_new_with_am (
+   TpAccountManager *account_mgr,
+    const gchar *name,
+    gboolean uniquify_name);
+
+G_END_DECLS
+
+#endif /* #ifndef __TP_TESTS_SIMPLE_CONN_H__ */
diff --git a/tests/lib/telepathy/contactlist/simple-conn.c b/tests/lib/telepathy/contactlist/simple-conn.c
index e6ee214..fa7bfda 100644
--- a/tests/lib/telepathy/contactlist/simple-conn.c
+++ b/tests/lib/telepathy/contactlist/simple-conn.c
@@ -17,36 +17,32 @@
 
 #include <dbus/dbus-glib.h>
 
-#include <telepathy-glib/dbus.h>
-#include <telepathy-glib/errors.h>
-#include <telepathy-glib/gtypes.h>
-#include <telepathy-glib/handle-repo-dynamic.h>
-#include <telepathy-glib/interfaces.h>
-#include <telepathy-glib/util.h>
-
-#include "textchan-null.h"
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
+
+#include "echo-chan.h"
 #include "room-list-chan.h"
 #include "util.h"
 
-static void conn_iface_init (TpSvcConnectionClass *);
+static void props_iface_init (TpSvcDBusPropertiesClass *);
 
 G_DEFINE_TYPE_WITH_CODE (TpTestsSimpleConnection, tp_tests_simple_connection,
     TP_TYPE_BASE_CONNECTION,
-    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION, conn_iface_init))
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, props_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION, NULL))
 
 /* type definition stuff */
 
 enum
 {
   PROP_ACCOUNT = 1,
-  PROP_BREAK_PROPS = 2,
-  PROP_DBUS_STATUS = 3,
+  PROP_DBUS_STATUS,
   N_PROPS
 };
 
 enum
 {
-  SIGNAL_GOT_SELF_HANDLE,
+  SIGNAL_GOT_ALL,
   N_SIGNALS
 };
 
@@ -57,13 +53,10 @@ struct _TpTestsSimpleConnectionPrivate
   gchar *account;
   guint connect_source;
   guint disconnect_source;
-  gboolean break_fastpath_props;
 
   /* TpHandle => reffed TpTestsTextChannelNull */
   GHashTable *text_channels;
   TpTestsRoomListChan *room_list_chan;
-
-  GError *get_self_handle_error /* initially NULL */ ;
 };
 
 static void
@@ -88,16 +81,7 @@ get_property (GObject *object,
     case PROP_ACCOUNT:
       g_value_set_string (value, self->priv->account);
       break;
-    case PROP_BREAK_PROPS:
-      g_value_set_boolean (value, self->priv->break_fastpath_props);
-      break;
     case PROP_DBUS_STATUS:
-      if (self->priv->break_fastpath_props)
-        {
-          g_debug ("returning broken value for Connection.Status");
-          g_value_set_uint (value, 0xdeadbeefU);
-        }
-      else
         {
           g_value_set_uint (value,
               tp_base_connection_get_status (TP_BASE_CONNECTION (self)));
@@ -121,9 +105,6 @@ set_property (GObject *object,
       g_free (self->priv->account);
       self->priv->account = g_utf8_strdown (g_value_get_string (value), -1);
       break;
-    case PROP_BREAK_PROPS:
-      self->priv->break_fastpath_props = g_value_get_boolean (value);
-      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
   }
@@ -155,7 +136,6 @@ finalize (GObject *object)
       g_source_remove (self->priv->disconnect_source);
     }
 
-  g_clear_error (&self->priv->get_self_handle_error);
   g_free (self->priv->account);
 
   G_OBJECT_CLASS (tp_tests_simple_connection_parent_class)->finalize (object);
@@ -203,7 +183,7 @@ create_handle_repos (TpBaseConnection *conn,
 }
 
 static GPtrArray *
-create_channel_factories (TpBaseConnection *conn)
+create_channel_managers (TpBaseConnection *conn)
 {
   return g_ptr_array_sized_new (0);
 }
@@ -284,6 +264,19 @@ shut_down (TpBaseConnection *conn)
       conn);
 }
 
+static GPtrArray *
+get_interfaces_always_present (TpBaseConnection *base)
+{
+  GPtrArray *interfaces;
+
+  interfaces = TP_BASE_CONNECTION_CLASS (
+      tp_tests_simple_connection_parent_class)->get_interfaces_always_present (base);
+
+  g_ptr_array_add (interfaces, TP_IFACE_CONNECTION_INTERFACE_REQUESTS);
+
+  return interfaces;
+}
+
 static void
 tp_tests_simple_connection_class_init (TpTestsSimpleConnectionClass *klass)
 {
@@ -291,8 +284,6 @@ tp_tests_simple_connection_class_init (TpTestsSimpleConnectionClass *klass)
       (TpBaseConnectionClass *) klass;
   GObjectClass *object_class = (GObjectClass *) klass;
   GParamSpec *param_spec;
-  static const gchar *interfaces_always_present[] = {
-      TP_IFACE_CONNECTION_INTERFACE_REQUESTS, NULL };
 
   object_class->get_property = get_property;
   object_class->set_property = set_property;
@@ -302,23 +293,17 @@ tp_tests_simple_connection_class_init (TpTestsSimpleConnectionClass *klass)
 
   base_class->create_handle_repos = create_handle_repos;
   base_class->get_unique_connection_name = get_unique_connection_name;
-  base_class->create_channel_factories = create_channel_factories;
+  base_class->create_channel_managers = create_channel_managers;
   base_class->start_connecting = start_connecting;
   base_class->shut_down = shut_down;
 
-  base_class->interfaces_always_present = interfaces_always_present;
+  base_class->get_interfaces_always_present = get_interfaces_always_present;
 
   param_spec = g_param_spec_string ("account", "Account name",
       "The username of this user", NULL,
       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
   g_object_class_install_property (object_class, PROP_ACCOUNT, param_spec);
 
-  param_spec = g_param_spec_boolean ("break-0192-properties",
-      "Break 0.19.2 properties",
-      "Break Connection D-Bus properties introduced in spec 0.19.2", FALSE,
-      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-  g_object_class_install_property (object_class, PROP_BREAK_PROPS, param_spec);
-
   param_spec = g_param_spec_uint ("dbus-status",
       "Connection.Status",
       "The connection status as visible on D-Bus (overridden so can break it)",
@@ -327,7 +312,7 @@ tp_tests_simple_connection_class_init (TpTestsSimpleConnectionClass *klass)
       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
   g_object_class_install_property (object_class, PROP_DBUS_STATUS, param_spec);
 
-  signals[SIGNAL_GOT_SELF_HANDLE] = g_signal_new ("got-self-handle",
+  signals[SIGNAL_GOT_ALL] = g_signal_new ("got-all",
       G_OBJECT_CLASS_TYPE (klass),
       G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       0,
@@ -366,11 +351,10 @@ tp_tests_simple_connection_ensure_text_chan (TpTestsSimpleConnection *self,
     const gchar *target_id,
     GHashTable **props)
 {
-  TpTestsTextChannelNull *chan;
+  TpTestsEchoChannel *chan;
   gchar *chan_path;
   TpHandleRepoIface *contact_repo;
   TpHandle handle;
-  static guint count = 0;
   TpBaseConnection *base_conn = (TpBaseConnection *) self;
 
   /* Get contact handle */
@@ -382,21 +366,12 @@ tp_tests_simple_connection_ensure_text_chan (TpTestsSimpleConnection *self,
 
   chan = g_hash_table_lookup (self->priv->text_channels,
       GUINT_TO_POINTER (handle));
-  if (chan != NULL)
+  if (chan == NULL)
     {
-      /* Channel already exist, reuse it */
-      g_object_get (chan, "object-path", &chan_path, NULL);
-    }
-  else
-    {
-      chan_path = g_strdup_printf ("%s/Channel%u",
-          tp_base_connection_get_object_path (base_conn), count++);
-
-       chan = TP_TESTS_TEXT_CHANNEL_NULL (
+       chan = TP_TESTS_ECHO_CHANNEL (
           tp_tests_object_new_static_class (
-            TP_TESTS_TYPE_TEXT_CHANNEL_NULL,
+            TP_TESTS_TYPE_ECHO_CHANNEL,
             "connection", self,
-            "object-path", chan_path,
             "handle", handle,
             NULL));
 
@@ -404,8 +379,10 @@ tp_tests_simple_connection_ensure_text_chan (TpTestsSimpleConnection *self,
           chan);
     }
 
+  g_object_get (chan, "object-path", &chan_path, NULL);
+
   if (props != NULL)
-    *props = tp_tests_text_channel_get_props (chan);
+    g_object_get (chan, "channel-properties", props, NULL);
 
   return chan_path;
 }
@@ -455,44 +432,25 @@ tp_tests_simple_connection_ensure_room_list_chan (TpTestsSimpleConnection *self,
   return chan_path;
 }
 
-void
-tp_tests_simple_connection_set_get_self_handle_error (
-    TpTestsSimpleConnection *self,
-    GQuark domain,
-    gint code,
-    const gchar *message)
-{
-  self->priv->get_self_handle_error = g_error_new_literal (domain, code,
-      message);
-}
-
 static void
-get_self_handle (TpSvcConnection *iface,
+get_all (TpSvcDBusProperties *iface,
+    const gchar *interface_name,
     DBusGMethodInvocation *context)
 {
-  TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (iface);
-  TpBaseConnection *base = TP_BASE_CONNECTION (iface);
-
-  g_assert (TP_IS_BASE_CONNECTION (base));
-
-  TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
-
-  if (self->priv->get_self_handle_error != NULL)
-    {
-      dbus_g_method_return_error (context, self->priv->get_self_handle_error);
-      return;
-    }
+  GHashTable *values = tp_dbus_properties_mixin_dup_all (G_OBJECT (iface),
+      interface_name);
 
-  tp_svc_connection_return_from_get_self_handle (context,
-      tp_base_connection_get_self_handle (base));
-  g_signal_emit (self, signals[SIGNAL_GOT_SELF_HANDLE], 0);
+  tp_svc_dbus_properties_return_from_get_all (context, values);
+  g_hash_table_unref (values);
+  g_signal_emit (iface, signals[SIGNAL_GOT_ALL],
+      g_quark_from_string (interface_name));
 }
 
 static void
-conn_iface_init (TpSvcConnectionClass *iface)
+props_iface_init (TpSvcDBusPropertiesClass *iface)
 {
-#define IMPLEMENT(prefix,x) \
-  tp_svc_connection_implement_##x (iface, prefix##x)
-  IMPLEMENT(,get_self_handle);
+#define IMPLEMENT(x) \
+  tp_svc_dbus_properties_implement_##x (iface, x)
+  IMPLEMENT (get_all);
 #undef IMPLEMENT
 }
diff --git a/tests/lib/telepathy/contactlist/simple-conn.h b/tests/lib/telepathy/contactlist/simple-conn.h
index 837400b..ffe5778 100644
--- a/tests/lib/telepathy/contactlist/simple-conn.h
+++ b/tests/lib/telepathy/contactlist/simple-conn.h
@@ -13,7 +13,7 @@
 #define __TP_TESTS_SIMPLE_CONN_H__
 
 #include <glib-object.h>
-#include <telepathy-glib/base-connection.h>
+#include <telepathy-glib/telepathy-glib.h>
 
 G_BEGIN_DECLS
 
@@ -66,12 +66,6 @@ gchar * tp_tests_simple_connection_ensure_text_chan (
     const gchar *target_id,
     GHashTable **props);
 
-void tp_tests_simple_connection_set_get_self_handle_error (
-    TpTestsSimpleConnection *self,
-    GQuark domain,
-    gint code,
-    const gchar *message);
-
 gchar * tp_tests_simple_connection_ensure_room_list_chan (
     TpTestsSimpleConnection *self,
     const gchar *server,
diff --git a/tests/lib/telepathy/contactlist/stream-tube-chan.c 
b/tests/lib/telepathy/contactlist/stream-tube-chan.c
new file mode 100644
index 0000000..e1184af
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/stream-tube-chan.c
@@ -0,0 +1,650 @@
+/*
+ * stream-tube-chan.c - Simple stream tube channel
+ *
+ * Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "config.h"
+
+#include "config.h"
+
+#include "stream-tube-chan.h"
+#include "util.h"
+
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
+
+#ifdef HAVE_GIO_UNIX
+#include <gio/gunixsocketaddress.h>
+#include <gio/gunixconnection.h>
+#endif
+
+#include <glib/gstdio.h>
+
+enum
+{
+  PROP_SERVICE = 1,
+  PROP_SUPPORTED_SOCKET_TYPES,
+  PROP_PARAMETERS,
+  PROP_STATE,
+};
+
+enum
+{
+  SIG_INCOMING_CONNECTION,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = {0, };
+
+
+struct _TpTestsStreamTubeChannelPrivate {
+    TpTubeChannelState state;
+    GHashTable *supported_socket_types;
+
+    /* Accepting side */
+    GSocketService *service;
+    GValue *access_control_param;
+
+    /* Offering side */
+    TpSocketAddressType address_type;
+    GValue *address;
+    gchar *unix_address;
+    gchar *unix_tmpdir;
+    guint connection_id;
+
+    TpSocketAccessControl access_control;
+};
+
+static void
+create_supported_socket_types (TpTestsStreamTubeChannel *self)
+{
+  TpSocketAccessControl access_control;
+  GArray *unix_tab;
+
+  g_assert (self->priv->supported_socket_types == NULL);
+  self->priv->supported_socket_types = g_hash_table_new_full (NULL, NULL,
+      NULL, _tp_destroy_socket_control_list);
+
+  /* Socket_Address_Type_Unix */
+  unix_tab = g_array_sized_new (FALSE, FALSE, sizeof (TpSocketAccessControl),
+      1);
+  access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST;
+  g_array_append_val (unix_tab, access_control);
+
+  g_hash_table_insert (self->priv->supported_socket_types,
+      GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_UNIX), unix_tab);
+}
+
+static void
+tp_tests_stream_tube_channel_get_property (GObject *object,
+    guint property_id,
+    GValue *value,
+    GParamSpec *pspec)
+{
+  TpTestsStreamTubeChannel *self = (TpTestsStreamTubeChannel *) object;
+
+  switch (property_id)
+    {
+      case PROP_SERVICE:
+        g_value_set_string (value, "test-service");
+        break;
+
+      case PROP_SUPPORTED_SOCKET_TYPES:
+        g_value_set_boxed (value,
+            self->priv->supported_socket_types);
+        break;
+
+      case PROP_PARAMETERS:
+        g_value_take_boxed (value, tp_asv_new (
+              "badger", G_TYPE_UINT, 42,
+              NULL));
+        break;
+
+      case PROP_STATE:
+        g_value_set_uint (value, self->priv->state);
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
+tp_tests_stream_tube_channel_set_property (GObject *object,
+    guint property_id,
+    const GValue *value,
+    GParamSpec *pspec)
+{
+  TpTestsStreamTubeChannel *self = (TpTestsStreamTubeChannel *) object;
+
+  switch (property_id)
+    {
+      case PROP_SUPPORTED_SOCKET_TYPES:
+        self->priv->supported_socket_types = g_value_dup_boxed (value);
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void stream_tube_iface_init (gpointer iface, gpointer data);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (TpTestsStreamTubeChannel,
+    tp_tests_stream_tube_channel,
+    TP_TYPE_BASE_CHANNEL,
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_STREAM_TUBE,
+      stream_tube_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_TUBE,
+      NULL);
+    )
+
+/* type definition stuff */
+
+static GPtrArray *
+tp_tests_stream_tube_channel_get_interfaces (TpBaseChannel *self)
+{
+  GPtrArray *interfaces;
+
+  interfaces = TP_BASE_CHANNEL_CLASS (
+      tp_tests_stream_tube_channel_parent_class)->get_interfaces (self);
+
+  g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_TUBE);
+  return interfaces;
+};
+
+static void
+tp_tests_stream_tube_channel_init (TpTestsStreamTubeChannel *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
+      TP_TESTS_TYPE_STREAM_TUBE_CHANNEL, TpTestsStreamTubeChannelPrivate);
+}
+
+static GObject *
+constructor (GType type,
+             guint n_props,
+             GObjectConstructParam *props)
+{
+  GObject *object =
+      G_OBJECT_CLASS (tp_tests_stream_tube_channel_parent_class)->constructor (
+          type, n_props, props);
+  TpTestsStreamTubeChannel *self = TP_TESTS_STREAM_TUBE_CHANNEL (object);
+
+  if (tp_base_channel_is_requested (TP_BASE_CHANNEL (self)))
+    self->priv->state = TP_TUBE_CHANNEL_STATE_NOT_OFFERED;
+  else
+    self->priv->state = TP_TUBE_CHANNEL_STATE_LOCAL_PENDING;
+
+  if (self->priv->supported_socket_types == NULL)
+    create_supported_socket_types (self);
+
+  tp_base_channel_register (TP_BASE_CHANNEL (self));
+
+  return object;
+}
+
+static void
+dispose (GObject *object)
+{
+  TpTestsStreamTubeChannel *self = (TpTestsStreamTubeChannel *) object;
+
+  if (self->priv->service != NULL)
+    {
+      g_socket_service_stop (self->priv->service);
+      tp_clear_object (&self->priv->service);
+    }
+
+  tp_clear_pointer (&self->priv->address, tp_g_value_slice_free);
+  tp_clear_pointer (&self->priv->supported_socket_types, g_hash_table_unref);
+  tp_clear_pointer (&self->priv->access_control_param, tp_g_value_slice_free);
+
+  if (self->priv->unix_address != NULL)
+    g_unlink (self->priv->unix_address);
+
+  tp_clear_pointer (&self->priv->unix_address, g_free);
+
+  if (self->priv->unix_tmpdir != NULL)
+    g_rmdir (self->priv->unix_tmpdir);
+
+  tp_clear_pointer (&self->priv->unix_tmpdir, g_free);
+
+  ((GObjectClass *) tp_tests_stream_tube_channel_parent_class)->dispose (
+    object);
+}
+
+static void
+channel_close (TpBaseChannel *channel)
+{
+  tp_base_channel_destroyed (channel);
+}
+
+static void
+fill_immutable_properties (TpBaseChannel *chan,
+    GHashTable *properties)
+{
+  TpBaseChannelClass *klass = TP_BASE_CHANNEL_CLASS (
+      tp_tests_stream_tube_channel_parent_class);
+
+  klass->fill_immutable_properties (chan, properties);
+
+  tp_dbus_properties_mixin_fill_properties_hash (
+      G_OBJECT (chan), properties,
+      TP_IFACE_CHANNEL_TYPE_STREAM_TUBE, "Service",
+      TP_IFACE_CHANNEL_TYPE_STREAM_TUBE, "SupportedSocketTypes",
+      NULL);
+
+  if (!tp_base_channel_is_requested (chan))
+    {
+      /* Parameters is immutable only for incoming tubes */
+      tp_dbus_properties_mixin_fill_properties_hash (
+          G_OBJECT (chan), properties,
+          TP_IFACE_CHANNEL_INTERFACE_TUBE, "Parameters",
+          NULL);
+    }
+}
+
+static void
+tp_tests_stream_tube_channel_class_init (TpTestsStreamTubeChannelClass *klass)
+{
+  GObjectClass *object_class = (GObjectClass *) klass;
+  TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (klass);
+  GParamSpec *param_spec;
+  static TpDBusPropertiesMixinPropImpl stream_tube_props[] = {
+      { "Service", "service", NULL, },
+      { "SupportedSocketTypes", "supported-socket-types", NULL },
+      { NULL }
+  };
+  static TpDBusPropertiesMixinPropImpl tube_props[] = {
+      { "Parameters", "parameters", NULL, },
+      { "State", "state", NULL, },
+      { NULL }
+  };
+
+  object_class->constructor = constructor;
+  object_class->get_property = tp_tests_stream_tube_channel_get_property;
+  object_class->set_property = tp_tests_stream_tube_channel_set_property;
+  object_class->dispose = dispose;
+
+  base_class->channel_type = TP_IFACE_CHANNEL_TYPE_STREAM_TUBE;
+  base_class->get_interfaces = tp_tests_stream_tube_channel_get_interfaces;
+  base_class->close = channel_close;
+  base_class->fill_immutable_properties = fill_immutable_properties;
+
+  /* base_class->target_handle_type is defined in subclasses */
+
+  param_spec = g_param_spec_string ("service", "service name",
+      "the service associated with this tube object.",
+       "",
+       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_SERVICE, param_spec);
+
+  param_spec = g_param_spec_boxed (
+      "supported-socket-types", "Supported socket types",
+      "GHashTable containing supported socket types.",
+      TP_HASH_TYPE_SUPPORTED_SOCKET_MAP,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_SUPPORTED_SOCKET_TYPES,
+      param_spec);
+
+  param_spec = g_param_spec_boxed (
+      "parameters", "Parameters",
+      "parameters of the tube",
+      TP_HASH_TYPE_STRING_VARIANT_MAP,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_PARAMETERS,
+      param_spec);
+
+  param_spec = g_param_spec_uint (
+      "state", "TpTubeState",
+      "state of the tube",
+      0, TP_NUM_TUBE_CHANNEL_STATES - 1, 0,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_STATE,
+      param_spec);
+
+  signals[SIG_INCOMING_CONNECTION] = g_signal_new ("incoming-connection",
+      G_OBJECT_CLASS_TYPE (klass),
+      G_SIGNAL_RUN_LAST,
+      0, NULL, NULL, NULL,
+      G_TYPE_NONE,
+      1, G_TYPE_IO_STREAM);
+
+  tp_dbus_properties_mixin_implement_interface (object_class,
+      TP_IFACE_QUARK_CHANNEL_TYPE_STREAM_TUBE,
+      tp_dbus_properties_mixin_getter_gobject_properties, NULL,
+      stream_tube_props);
+
+  tp_dbus_properties_mixin_implement_interface (object_class,
+      TP_IFACE_QUARK_CHANNEL_INTERFACE_TUBE,
+      tp_dbus_properties_mixin_getter_gobject_properties, NULL,
+      tube_props);
+
+  g_type_class_add_private (object_class,
+      sizeof (TpTestsStreamTubeChannelPrivate));
+}
+
+static void
+change_state (TpTestsStreamTubeChannel *self,
+  TpTubeChannelState state)
+{
+  self->priv->state = state;
+
+  tp_svc_channel_interface_tube_emit_tube_channel_state_changed (self, state);
+}
+
+/* Return the address of the socket which has been shared over the tube */
+GSocketAddress *
+tp_tests_stream_tube_channel_get_server_address (TpTestsStreamTubeChannel *self)
+{
+  return tp_g_socket_address_from_variant (self->priv->address_type,
+      self->priv->address, NULL);
+}
+
+static gboolean
+check_address_type (TpTestsStreamTubeChannel *self,
+    TpSocketAddressType address_type,
+    TpSocketAccessControl access_control)
+{
+  GArray *arr;
+  guint i;
+
+  arr = g_hash_table_lookup (self->priv->supported_socket_types,
+      GUINT_TO_POINTER (address_type));
+  if (arr == NULL)
+    return FALSE;
+
+  for (i = 0; i < arr->len; i++)
+    {
+      if (g_array_index (arr, TpSocketAccessControl, i) == access_control)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+stream_tube_offer (TpSvcChannelTypeStreamTube *iface,
+    guint address_type,
+    const GValue *address,
+    guint access_control,
+    GHashTable *parameters,
+    DBusGMethodInvocation *context)
+{
+  TpTestsStreamTubeChannel *self = (TpTestsStreamTubeChannel *) iface;
+  GError *error = NULL;
+
+  if (self->priv->state != TP_TUBE_CHANNEL_STATE_NOT_OFFERED)
+    {
+      g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+          "Tube is not in the not offered state");
+      goto fail;
+    }
+
+  if (!check_address_type (self, address_type, access_control))
+    {
+      g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+          "Address type not supported with this access control");
+      goto fail;
+    }
+
+  self->priv->address_type = address_type;
+  self->priv->address = tp_g_value_slice_dup (address);
+  self->priv->access_control = access_control;
+
+  change_state (self, TP_TUBE_CHANNEL_STATE_REMOTE_PENDING);
+
+  tp_svc_channel_type_stream_tube_return_from_offer (context);
+  return;
+
+fail:
+  dbus_g_method_return_error (context, error);
+  g_error_free (error);
+}
+
+static void
+service_incoming_cb (GSocketService *service,
+    GSocketConnection *connection,
+    GObject *source_object,
+    gpointer user_data)
+{
+  TpTestsStreamTubeChannel *self = user_data;
+  GError *error = NULL;
+
+  if (self->priv->access_control == TP_SOCKET_ACCESS_CONTROL_CREDENTIALS)
+    {
+#ifdef HAVE_GIO_UNIX
+      GCredentials *creds;
+      guchar byte;
+
+      /* FIXME: we should an async version of this API (bgo #629503) */
+      creds = tp_unix_connection_receive_credentials_with_byte (
+              connection, &byte, NULL, &error);
+      g_assert_no_error (error);
+
+      g_assert_cmpuint (byte, ==,
+          g_value_get_uchar (self->priv->access_control_param));
+      g_object_unref (creds);
+#else
+      /* Tests shouldn't use this if not supported */
+      g_assert_not_reached ();
+#endif
+    }
+  else if (self->priv->access_control == TP_SOCKET_ACCESS_CONTROL_PORT)
+    {
+      GSocketAddress *addr;
+      guint16 port;
+
+      addr = g_socket_connection_get_remote_address (connection, &error);
+      g_assert_no_error (error);
+
+      port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
+
+      g_assert_cmpuint (port, ==,
+          g_value_get_uint (self->priv->access_control_param));
+
+      g_object_unref (addr);
+    }
+
+  tp_svc_channel_type_stream_tube_emit_new_local_connection (self,
+      self->priv->connection_id);
+
+  self->priv->connection_id++;
+
+  g_signal_emit (self, signals[SIG_INCOMING_CONNECTION], 0, connection);
+}
+
+static void
+stream_tube_accept (TpSvcChannelTypeStreamTube *iface,
+    TpSocketAddressType address_type,
+    TpSocketAccessControl access_control,
+    const GValue *access_control_param,
+    DBusGMethodInvocation *context)
+{
+  TpTestsStreamTubeChannel *self = (TpTestsStreamTubeChannel *) iface;
+  GError *error = NULL;
+  GValue *address;
+
+  if (self->priv->state != TP_TUBE_CHANNEL_STATE_LOCAL_PENDING)
+    {
+      g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+          "Tube is not in the local pending state");
+      goto fail;
+    }
+
+  if (!check_address_type (self, address_type, access_control))
+    {
+      g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+          "Address type not supported with this access control");
+      goto fail;
+    }
+
+  address = _tp_create_local_socket (address_type, access_control,
+      &self->priv->service, &self->priv->unix_address,
+      &self->priv->unix_tmpdir, &error);
+  tp_g_signal_connect_object (self->priv->service, "incoming",
+      G_CALLBACK (service_incoming_cb), self, 0);
+
+  self->priv->access_control = access_control;
+  self->priv->access_control_param = tp_g_value_slice_dup (
+      access_control_param);
+
+  change_state (self, TP_TUBE_CHANNEL_STATE_OPEN);
+
+  tp_svc_channel_type_stream_tube_return_from_accept (context, address);
+
+  tp_g_value_slice_free (address);
+  return;
+
+fail:
+  dbus_g_method_return_error (context, error);
+  g_error_free (error);
+}
+
+static void
+stream_tube_iface_init (gpointer iface,
+    gpointer data)
+{
+  TpSvcChannelTypeStreamTubeClass *klass = iface;
+
+#define IMPLEMENT(x) tp_svc_channel_type_stream_tube_implement_##x (klass, stream_tube_##x)
+  IMPLEMENT(offer);
+  IMPLEMENT(accept);
+#undef IMPLEMENT
+}
+
+/* Called to emulate a peer connecting to an offered tube */
+void
+tp_tests_stream_tube_channel_peer_connected (TpTestsStreamTubeChannel *self,
+    GIOStream *stream,
+    TpHandle handle)
+{
+  GValue *connection_param;
+  TpBaseChannel *base = (TpBaseChannel *) self;
+  TpBaseConnection *conn = tp_base_channel_get_connection (base);
+  TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn,
+      TP_HANDLE_TYPE_CONTACT);
+
+  if (self->priv->state == TP_TUBE_CHANNEL_STATE_REMOTE_PENDING)
+    change_state (self, TP_TUBE_CHANNEL_STATE_OPEN);
+
+  g_assert (self->priv->state == TP_TUBE_CHANNEL_STATE_OPEN);
+
+  switch (self->priv->access_control)
+    {
+      case TP_SOCKET_ACCESS_CONTROL_LOCALHOST:
+        connection_param = tp_g_value_slice_new_static_string ("dummy");
+        break;
+
+      case TP_SOCKET_ACCESS_CONTROL_CREDENTIALS:
+        {
+#ifdef HAVE_GIO_UNIX
+          GError *error = NULL;
+          guchar byte = g_random_int_range (0, G_MAXUINT8);
+
+          /* FIXME: we should an async version of this API (bgo #629503) */
+          tp_unix_connection_send_credentials_with_byte (
+              G_SOCKET_CONNECTION (stream), byte, NULL, &error);
+          g_assert_no_error (error);
+
+          connection_param = tp_g_value_slice_new_byte (byte);
+#else
+          /* Tests shouldn't use this if not supported */
+          g_assert_not_reached ();
+#endif
+        }
+        break;
+
+      case TP_SOCKET_ACCESS_CONTROL_PORT:
+        {
+          GSocketAddress *addr;
+          GError *error = NULL;
+
+          addr = g_socket_connection_get_local_address (
+              G_SOCKET_CONNECTION (stream), &error);
+          g_assert_no_error (error);
+
+          connection_param = tp_g_value_slice_new_take_boxed (
+              TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV4,
+              dbus_g_type_specialized_construct (
+                TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV4));
+
+          dbus_g_type_struct_set (connection_param,
+              0, "badger",
+              1, g_inet_socket_address_get_port (
+                G_INET_SOCKET_ADDRESS (addr)),
+              G_MAXUINT);
+
+          g_object_unref (addr);
+        }
+        break;
+
+      default:
+        g_assert_not_reached ();
+    }
+
+  tp_svc_channel_type_stream_tube_emit_new_remote_connection (self, handle,
+      tp_handle_inspect (contact_repo, handle), connection_param,
+      self->priv->connection_id);
+
+  self->priv->connection_id++;
+
+  tp_g_value_slice_free (connection_param);
+}
+
+void
+tp_tests_stream_tube_channel_last_connection_disconnected (
+    TpTestsStreamTubeChannel *self,
+    const gchar *error)
+{
+  tp_svc_channel_type_stream_tube_emit_connection_closed (self,
+      self->priv->connection_id - 1, error, "kaboum");
+}
+
+/* Contact Stream Tube */
+
+G_DEFINE_TYPE (TpTestsContactStreamTubeChannel,
+    tp_tests_contact_stream_tube_channel,
+    TP_TESTS_TYPE_STREAM_TUBE_CHANNEL)
+
+static void
+tp_tests_contact_stream_tube_channel_init (
+    TpTestsContactStreamTubeChannel *self)
+{
+}
+
+static void
+tp_tests_contact_stream_tube_channel_class_init (
+    TpTestsContactStreamTubeChannelClass *klass)
+{
+  TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (klass);
+
+  base_class->target_handle_type = TP_HANDLE_TYPE_CONTACT;
+}
+
+/* Room Stream Tube */
+
+G_DEFINE_TYPE (TpTestsRoomStreamTubeChannel,
+    tp_tests_room_stream_tube_channel,
+    TP_TESTS_TYPE_STREAM_TUBE_CHANNEL)
+
+static void
+tp_tests_room_stream_tube_channel_init (
+    TpTestsRoomStreamTubeChannel *self)
+{
+}
+
+static void
+tp_tests_room_stream_tube_channel_class_init (
+    TpTestsRoomStreamTubeChannelClass *klass)
+{
+  TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (klass);
+
+  base_class->target_handle_type = TP_HANDLE_TYPE_ROOM;
+}
diff --git a/tests/lib/telepathy/contactlist/stream-tube-chan.h 
b/tests/lib/telepathy/contactlist/stream-tube-chan.h
new file mode 100644
index 0000000..2672912
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/stream-tube-chan.h
@@ -0,0 +1,129 @@
+/*
+ * stream-tube-chan.h - Simple stream tube channel
+ *
+ * Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef __TP_STREAM_TUBE_CHAN_H__
+#define __TP_STREAM_TUBE_CHAN_H__
+
+#include <glib-object.h>
+#include <telepathy-glib/telepathy-glib.h>
+
+G_BEGIN_DECLS
+
+/* Base Class */
+typedef struct _TpTestsStreamTubeChannel TpTestsStreamTubeChannel;
+typedef struct _TpTestsStreamTubeChannelClass TpTestsStreamTubeChannelClass;
+typedef struct _TpTestsStreamTubeChannelPrivate TpTestsStreamTubeChannelPrivate;
+
+GType tp_tests_stream_tube_channel_get_type (void);
+
+#define TP_TESTS_TYPE_STREAM_TUBE_CHANNEL \
+  (tp_tests_stream_tube_channel_get_type ())
+#define TP_TESTS_STREAM_TUBE_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), TP_TESTS_TYPE_STREAM_TUBE_CHANNEL, \
+                               TpTestsStreamTubeChannel))
+#define TP_TESTS_STREAM_TUBE_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), TP_TESTS_TYPE_STREAM_TUBE_CHANNEL, \
+                            TpTestsStreamTubeChannelClass))
+#define TP_TESTS_IS_STREAM_TUBE_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TP_TESTS_TYPE_STREAM_TUBE_CHANNEL))
+#define TP_TESTS_IS_STREAM_TUBE_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), TP_TESTS_TYPE_STREAM_TUBE_CHANNEL))
+#define TP_TESTS_STREAM_TUBE_CHANNEL_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_STREAM_TUBE_CHANNEL, \
+                              TpTestsStreamTubeChannelClass))
+
+struct _TpTestsStreamTubeChannelClass {
+    TpBaseChannelClass parent_class;
+    TpDBusPropertiesMixinClass dbus_properties_class;
+};
+
+struct _TpTestsStreamTubeChannel {
+    TpBaseChannel parent;
+
+    TpTestsStreamTubeChannelPrivate *priv;
+};
+
+GSocketAddress * tp_tests_stream_tube_channel_get_server_address (
+    TpTestsStreamTubeChannel *self);
+
+void tp_tests_stream_tube_channel_peer_connected (
+    TpTestsStreamTubeChannel *self,
+    GIOStream *stream,
+    TpHandle handle);
+
+void tp_tests_stream_tube_channel_last_connection_disconnected (
+    TpTestsStreamTubeChannel *self,
+    const gchar *error);
+
+/* Contact Stream Tube */
+
+typedef struct _TpTestsContactStreamTubeChannel TpTestsContactStreamTubeChannel;
+typedef struct _TpTestsContactStreamTubeChannelClass TpTestsContactStreamTubeChannelClass;
+
+GType tp_tests_contact_stream_tube_channel_get_type (void);
+
+#define TP_TESTS_TYPE_CONTACT_STREAM_TUBE_CHANNEL \
+  (tp_tests_contact_stream_tube_channel_get_type ())
+#define TP_TESTS_CONTACT_STREAM_TUBE_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), TP_TESTS_TYPE_CONTACT_STREAM_TUBE_CHANNEL, \
+                               TpTestsContactStreamTubeChannel))
+#define TP_TESTS_CONTACT_STREAM_TUBE_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), TP_TESTS_TYPE_CONTACT_STREAM_TUBE_CHANNEL, \
+                            TpTestsContactStreamTubeChannelClass))
+#define TP_TESTS_IS_CONTACT_STREAM_TUBE_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TP_TESTS_TYPE_CONTACT_STREAM_TUBE_CHANNEL))
+#define TP_TESTS_IS_CONTACT_STREAM_TUBE_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), TP_TESTS_TYPE_CONTACT_STREAM_TUBE_CHANNEL))
+#define TP_TESTS_CONTACT_STREAM_TUBE_CHANNEL_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_CONTACT_STREAM_TUBE_CHANNEL, \
+                              TpTestsContactStreamTubeChannelClass))
+
+struct _TpTestsContactStreamTubeChannelClass {
+    TpTestsStreamTubeChannelClass parent_class;
+};
+
+struct _TpTestsContactStreamTubeChannel {
+    TpTestsStreamTubeChannel parent;
+};
+
+/* Room Stream Tube */
+
+typedef struct _TpTestsRoomStreamTubeChannel TpTestsRoomStreamTubeChannel;
+typedef struct _TpTestsRoomStreamTubeChannelClass TpTestsRoomStreamTubeChannelClass;
+
+GType tp_tests_room_stream_tube_channel_get_type (void);
+
+#define TP_TESTS_TYPE_ROOM_STREAM_TUBE_CHANNEL \
+  (tp_tests_room_stream_tube_channel_get_type ())
+#define TP_TESTS_ROOM_STREAM_TUBE_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), TP_TESTS_TYPE_ROOM_STREAM_TUBE_CHANNEL, \
+                               TpTestsRoomStreamTubeChannel))
+#define TP_TESTS_ROOM_STREAM_TUBE_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), TP_TESTS_TYPE_ROOM_STREAM_TUBE_CHANNEL, \
+                            TpTestsRoomStreamTubeChannelClass))
+#define TP_TESTS_IS_ROOM_STREAM_TUBE_CHANNEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TP_TESTS_TYPE_ROOM_STREAM_TUBE_CHANNEL))
+#define TP_TESTS_IS_ROOM_STREAM_TUBE_CHANNEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), TP_TESTS_TYPE_ROOM_STREAM_TUBE_CHANNEL))
+#define TP_TESTS_ROOM_STREAM_TUBE_CHANNEL_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_ROOM_STREAM_TUBE_CHANNEL, \
+                              TpTestsRoomStreamTubeChannelClass))
+
+struct _TpTestsRoomStreamTubeChannelClass {
+    TpTestsStreamTubeChannelClass parent_class;
+};
+
+struct _TpTestsRoomStreamTubeChannel {
+    TpTestsStreamTubeChannel parent;
+};
+
+G_END_DECLS
+
+#endif /* #ifndef __TP_STREAM_TUBE_CHAN_H__ */
diff --git a/tests/lib/telepathy/contactlist/stub-object.c b/tests/lib/telepathy/contactlist/stub-object.c
new file mode 100644
index 0000000..1482876
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/stub-object.c
@@ -0,0 +1,68 @@
+#include "config.h"
+
+#include "stub-object.h"
+
+G_DEFINE_TYPE (TpTestsStubObject, tp_tests_stub_object, G_TYPE_OBJECT)
+
+enum {
+    PROP_0,
+    PROP_NAME,
+    N_PROPS
+};
+
+static void
+stub_object_get_property (GObject *object,
+    guint prop_id,
+    GValue *value,
+    GParamSpec *param_spec)
+{
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      g_value_set_string (value, "Bruce");
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, param_spec);
+      break;
+    }
+}
+
+static void
+stub_object_set_property (GObject *object,
+    guint prop_id,
+    const GValue *value,
+    GParamSpec *param_spec)
+{
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      /* do nothing */
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, param_spec);
+      break;
+    }
+}
+
+static void
+tp_tests_stub_object_class_init (TpTestsStubObjectClass *klass)
+{
+  GObjectClass *object_class = (GObjectClass *) klass;
+
+  object_class->get_property = stub_object_get_property;
+  object_class->set_property = stub_object_set_property;
+
+  g_object_class_install_property (object_class, PROP_NAME,
+      g_param_spec_string ("name",
+          "Name",
+          "The name of the stub object",
+          "Bruce",
+          G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
+}
+
+static void
+tp_tests_stub_object_init (TpTestsStubObject *self)
+{
+}
diff --git a/tests/lib/telepathy/contactlist/stub-object.h b/tests/lib/telepathy/contactlist/stub-object.h
new file mode 100644
index 0000000..ccdb094
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/stub-object.h
@@ -0,0 +1,11 @@
+#ifndef __TP_TESTS_STUB_OBJECT_H__
+#define __TP_TESTS_STUB_OBJECT_H__
+
+#include <glib-object.h>
+
+typedef struct { GObject p; } TpTestsStubObject;
+typedef struct { GObjectClass p; } TpTestsStubObjectClass;
+
+GType tp_tests_stub_object_get_type (void);
+
+#endif /* #ifndef __TP_TESTS_STUB_OBJECT_H__ */
diff --git a/tests/lib/telepathy/contactlist/textchan-group.c 
b/tests/lib/telepathy/contactlist/textchan-group.c
new file mode 100644
index 0000000..3b9b179
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/textchan-group.c
@@ -0,0 +1,329 @@
+/*
+ * a stub anonymous MUC
+ *
+ * Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include "config.h"
+
+#include "textchan-group.h"
+
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
+
+static void password_iface_init (gpointer iface, gpointer data);
+
+G_DEFINE_TYPE_WITH_CODE (TpTestsTextChannelGroup,
+    tp_tests_text_channel_group, TP_TYPE_BASE_CHANNEL,
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_TEXT,
+        tp_message_mixin_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP,
+      tp_group_mixin_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_PASSWORD,
+      password_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
+      tp_dbus_properties_mixin_iface_init))
+
+static GPtrArray *
+text_channel_group_get_interfaces (TpBaseChannel *self)
+{
+  GPtrArray *interfaces;
+
+  interfaces = TP_BASE_CHANNEL_CLASS (
+      tp_tests_text_channel_group_parent_class)->get_interfaces (self);
+
+  g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_GROUP);
+  g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_PASSWORD);
+  return interfaces;
+};
+
+/* type definition stuff */
+
+struct _TpTestsTextChannelGroupPrivate
+{
+  gboolean closed;
+  gboolean disposed;
+
+  gchar *password;
+};
+
+
+static gboolean
+add_member (GObject *obj,
+            TpHandle handle,
+            const gchar *message,
+            GError **error)
+{
+  TpTestsTextChannelGroup *self = TP_TESTS_TEXT_CHANNEL_GROUP (obj);
+  TpIntset *add = tp_intset_new ();
+  GHashTable *details = tp_asv_new (
+      "actor", G_TYPE_UINT, tp_base_connection_get_self_handle (self->conn),
+      "change-reason", G_TYPE_UINT, TP_CHANNEL_GROUP_CHANGE_REASON_NONE,
+      "message", G_TYPE_STRING, message,
+      NULL);
+
+  tp_intset_add (add, handle);
+  tp_group_mixin_change_members (obj, add, NULL, NULL, NULL, details);
+  tp_intset_destroy (add);
+
+  g_hash_table_unref (details);
+
+  return TRUE;
+}
+
+static gboolean
+remove_with_reason (GObject *obj,
+    TpHandle handle,
+    const gchar *message,
+    guint reason,
+    GError **error)
+{
+  TpTestsTextChannelGroup *self = TP_TESTS_TEXT_CHANNEL_GROUP (obj);
+  TpGroupMixin *group = TP_GROUP_MIXIN (self);
+
+  tp_clear_pointer (&self->removed_message, g_free);
+
+  self->removed_handle = handle;
+  self->removed_message = g_strdup (message);
+  self->removed_reason = reason;
+
+  if (handle == group->self_handle)
+    {
+      /* User wants to leave */
+      if (!self->priv->closed)
+        {
+          self->priv->closed = TRUE;
+          tp_svc_channel_emit_closed (self);
+        }
+
+      return TRUE;
+    }
+
+  return TRUE;
+}
+
+static void
+tp_tests_text_channel_group_init (TpTestsTextChannelGroup *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+      TP_TESTS_TYPE_TEXT_CHANNEL_GROUP, TpTestsTextChannelGroupPrivate);
+}
+
+static void
+text_send (GObject *object,
+           TpMessage *message,
+           TpMessageSendingFlags flags)
+{
+  /* silently swallow the message */
+  tp_message_mixin_sent (object, message, 0, "", NULL);
+}
+
+static void
+constructed (GObject *object)
+{
+  TpTestsTextChannelGroup *self = TP_TESTS_TEXT_CHANNEL_GROUP (object);
+  TpHandleRepoIface *contact_repo;
+  TpChannelGroupFlags flags = 0;
+  TpBaseChannel *base = TP_BASE_CHANNEL (self);
+  const TpChannelTextMessageType types[] = {
+      TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
+      TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION,
+      TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE,
+  };
+  const gchar * supported_content_types[] = {
+      "text/plain",
+      NULL
+  };
+
+  G_OBJECT_CLASS (tp_tests_text_channel_group_parent_class)->constructed (
+      object);
+
+  self->conn = tp_base_channel_get_connection (base);
+
+  contact_repo = tp_base_connection_get_handles (self->conn,
+      TP_HANDLE_TYPE_CONTACT);
+
+  tp_base_channel_register (base);
+
+  tp_message_mixin_init (object,
+      G_STRUCT_OFFSET (TpTestsTextChannelGroup, message),
+      self->conn);
+
+  tp_message_mixin_implement_sending (object,
+      text_send, G_N_ELEMENTS (types), types, 0, 0,
+      supported_content_types);
+
+  flags |= TP_CHANNEL_GROUP_FLAG_CAN_ADD;
+
+  tp_group_mixin_init (object, G_STRUCT_OFFSET (TpTestsTextChannelGroup, group),
+      contact_repo,
+      tp_base_connection_get_self_handle (self->conn));
+
+  tp_group_mixin_change_flags (object, flags, 0);
+}
+
+static void
+dispose (GObject *object)
+{
+  TpTestsTextChannelGroup *self = TP_TESTS_TEXT_CHANNEL_GROUP (object);
+
+  if (self->priv->disposed)
+    return;
+
+  self->priv->disposed = TRUE;
+
+  if (!self->priv->closed)
+    {
+      tp_svc_channel_emit_closed (self);
+    }
+
+  ((GObjectClass *) tp_tests_text_channel_group_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+  TpTestsTextChannelGroup *self = TP_TESTS_TEXT_CHANNEL_GROUP (object);
+
+  tp_message_mixin_finalize (object);
+  tp_group_mixin_finalize (object);
+
+  tp_clear_pointer (&self->priv->password, g_free);
+
+  ((GObjectClass *) tp_tests_text_channel_group_parent_class)->finalize (object);
+}
+
+static void
+channel_close (TpBaseChannel *base)
+{
+  TpTestsTextChannelGroup *self = TP_TESTS_TEXT_CHANNEL_GROUP (base);
+
+  if (!self->priv->closed)
+    {
+      self->priv->closed = TRUE;
+      tp_svc_channel_emit_closed (self);
+    }
+}
+
+static void
+tp_tests_text_channel_group_class_init (TpTestsTextChannelGroupClass *klass)
+{
+  GObjectClass *object_class = (GObjectClass *) klass;
+  TpBaseChannelClass *base_class = (TpBaseChannelClass *) klass;
+
+  g_type_class_add_private (klass, sizeof (TpTestsTextChannelGroupPrivate));
+
+  object_class->constructed = constructed;
+  object_class->dispose = dispose;
+  object_class->finalize = finalize;
+
+  base_class->channel_type = TP_IFACE_CHANNEL_TYPE_TEXT;
+  base_class->target_handle_type = TP_HANDLE_TYPE_NONE;
+  base_class->get_interfaces = text_channel_group_get_interfaces;
+  base_class->close = channel_close;
+
+  tp_group_mixin_class_init (object_class,
+      G_STRUCT_OFFSET (TpTestsTextChannelGroupClass, group_class), add_member,
+      NULL);
+
+  tp_group_mixin_class_set_remove_with_reason_func (object_class,
+      remove_with_reason);
+
+  tp_group_mixin_class_allow_self_removal (object_class);
+
+  tp_dbus_properties_mixin_class_init (object_class,
+      G_STRUCT_OFFSET (TpTestsTextChannelGroupClass, dbus_properties_class));
+
+  tp_group_mixin_init_dbus_properties (object_class);
+
+  tp_message_mixin_init_dbus_properties (object_class);
+}
+
+void
+tp_tests_text_channel_group_join (TpTestsTextChannelGroup *self)
+{
+  TpIntset *add, *empty;
+  GHashTable *details = tp_asv_new (
+      "actor", G_TYPE_UINT, 0,
+      "change-reason", G_TYPE_UINT, 0,
+      "message", G_TYPE_STRING, "",
+      NULL);
+
+ /* Add ourself as a member */
+  add = tp_intset_new_containing (
+      tp_base_connection_get_self_handle (self->conn));
+  empty = tp_intset_new ();
+
+  tp_group_mixin_change_members ((GObject *) self, add, empty,
+      empty, empty, details);
+
+  tp_intset_destroy (add);
+  tp_intset_destroy (empty);
+  g_hash_table_unref (details);
+}
+
+void
+tp_tests_text_channel_set_password (TpTestsTextChannelGroup *self,
+    const gchar *password)
+{
+  gboolean pass_was_needed, pass_needed;
+
+  pass_was_needed = (self->priv->password != NULL);
+
+  tp_clear_pointer (&self->priv->password, g_free);
+
+  self->priv->password = g_strdup (password);
+
+  pass_needed = (self->priv->password != NULL);
+
+  if (pass_needed == pass_was_needed)
+    return;
+
+  if (pass_needed)
+    tp_svc_channel_interface_password_emit_password_flags_changed (self,
+        TP_CHANNEL_PASSWORD_FLAG_PROVIDE, 0);
+  else
+    tp_svc_channel_interface_password_emit_password_flags_changed (self,
+        0, TP_CHANNEL_PASSWORD_FLAG_PROVIDE);
+}
+
+static void
+password_get_password_flags (TpSvcChannelInterfacePassword *chan,
+    DBusGMethodInvocation *context)
+{
+  TpTestsTextChannelGroup *self = (TpTestsTextChannelGroup *) chan;
+  TpChannelPasswordFlags flags = 0;
+
+  if (self->priv->password != NULL)
+    flags |= TP_CHANNEL_PASSWORD_FLAG_PROVIDE;
+
+  tp_svc_channel_interface_password_return_from_get_password_flags (context,
+      flags);
+}
+
+static void
+password_provide_password (TpSvcChannelInterfacePassword *chan,
+    const gchar *password,
+    DBusGMethodInvocation *context)
+{
+  TpTestsTextChannelGroup *self = (TpTestsTextChannelGroup *) chan;
+
+  tp_svc_channel_interface_password_return_from_provide_password (context,
+      !tp_strdiff (password, self->priv->password));
+}
+
+static void
+password_iface_init (gpointer iface, gpointer data)
+{
+  TpSvcChannelInterfacePasswordClass *klass = iface;
+
+#define IMPLEMENT(x) tp_svc_channel_interface_password_implement_##x (klass, password_##x)
+  IMPLEMENT (get_password_flags);
+  IMPLEMENT (provide_password);
+#undef IMPLEMENT
+}
diff --git a/tests/lib/telepathy/contactlist/textchan-group.h 
b/tests/lib/telepathy/contactlist/textchan-group.h
new file mode 100644
index 0000000..810e004
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/textchan-group.h
@@ -0,0 +1,72 @@
+/*
+ * a stub anonymous MUC
+ *
+ * Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#ifndef __TEST_TEXT_CHANNEL_GROUP_H__
+#define __TEST_TEXT_CHANNEL_GROUP_H__
+
+#include <glib-object.h>
+#include <telepathy-glib/telepathy-glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _TpTestsTextChannelGroup TpTestsTextChannelGroup;
+typedef struct _TpTestsTextChannelGroupClass TpTestsTextChannelGroupClass;
+typedef struct _TpTestsTextChannelGroupPrivate TpTestsTextChannelGroupPrivate;
+
+GType tp_tests_text_channel_group_get_type (void);
+
+#define TP_TESTS_TYPE_TEXT_CHANNEL_GROUP \
+  (tp_tests_text_channel_group_get_type ())
+#define TP_TESTS_TEXT_CHANNEL_GROUP(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), TP_TESTS_TYPE_TEXT_CHANNEL_GROUP, \
+                               TpTestsTextChannelGroup))
+#define TP_TESTS_TEXT_CHANNEL_GROUP_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), TP_TESTS_TYPE_TEXT_CHANNEL_GROUP, \
+                            TpTestsTextChannelGroupClass))
+#define TEST_IS_TEXT_CHANNEL_GROUP(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TP_TESTS_TYPE_TEXT_CHANNEL_GROUP))
+#define TEST_IS_TEXT_CHANNEL_GROUP_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), TP_TESTS_TYPE_TEXT_CHANNEL_GROUP))
+#define TP_TESTS_TEXT_CHANNEL_GROUP_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_TEXT_CHANNEL_GROUP, \
+                              TpTestsTextChannelGroupClass))
+
+struct _TpTestsTextChannelGroupClass {
+    TpBaseChannelClass parent_class;
+
+    TpGroupMixinClass group_class;
+    TpDBusPropertiesMixinClass dbus_properties_class;
+};
+
+struct _TpTestsTextChannelGroup {
+    TpBaseChannel parent;
+
+    TpBaseConnection *conn;
+
+    TpMessageMixin message;
+    TpGroupMixin group;
+
+    TpHandle removed_handle;
+    gchar *removed_message;
+    TpChannelGroupChangeReason removed_reason;
+
+    TpTestsTextChannelGroupPrivate *priv;
+};
+
+
+void tp_tests_text_channel_group_join (TpTestsTextChannelGroup *self);
+
+void tp_tests_text_channel_set_password (TpTestsTextChannelGroup *self,
+    const gchar *password);
+
+G_END_DECLS
+
+#endif /* #ifndef __TEST_TEXT_CHANNEL_GROUP_H__ */
diff --git a/tests/lib/telepathy/contactlist/tls-certificate.c 
b/tests/lib/telepathy/contactlist/tls-certificate.c
new file mode 100644
index 0000000..0da1645
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/tls-certificate.c
@@ -0,0 +1,350 @@
+/*
+ * tls-certificate.c - Source for TpTestsTLSCertificate
+ * Copyright (C) 2010 Collabora Ltd.
+ * @author Cosimo Cecchi <cosimo cecchi collabora co uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "config.h"
+#include "tls-certificate.h"
+
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
+
+#define DEBUG_FLAG TP_TESTS_DEBUG_TLS
+#include "debug.h"
+
+static void
+tls_certificate_iface_init (gpointer g_iface, gpointer iface_data);
+
+G_DEFINE_TYPE_WITH_CODE (TpTestsTLSCertificate,
+    tp_tests_tls_certificate,
+    G_TYPE_OBJECT,
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_AUTHENTICATION_TLS_CERTIFICATE,
+        tls_certificate_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
+        tp_dbus_properties_mixin_iface_init);)
+
+struct _TpTestsTLSCertificatePrivate {
+  gchar *object_path;
+
+  gchar *cert_type;
+  TpTLSCertificateState cert_state;
+
+  GPtrArray *rejections;
+  GPtrArray *cert_data;
+
+  TpDBusDaemon *daemon;
+
+  gboolean dispose_has_run;
+};
+
+enum {
+  PROP_OBJECT_PATH = 1,
+  PROP_STATE,
+  PROP_REJECTIONS,
+  PROP_CERTIFICATE_TYPE,
+  PROP_CERTIFICATE_CHAIN_DATA,
+
+  /* not exported */
+  PROP_DBUS_DAEMON,
+
+  NUM_PROPERTIES
+};
+
+static void
+tp_tests_tls_certificate_get_property (GObject *object,
+    guint property_id,
+    GValue *value,
+    GParamSpec *pspec)
+{
+  TpTestsTLSCertificate *self = TP_TESTS_TLS_CERTIFICATE (object);
+
+  switch (property_id)
+    {
+    case PROP_OBJECT_PATH:
+      g_value_set_string (value, self->priv->object_path);
+      break;
+    case PROP_STATE:
+      g_value_set_uint (value, self->priv->cert_state);
+      break;
+    case PROP_REJECTIONS:
+      g_value_set_boxed (value, self->priv->rejections);
+      break;
+    case PROP_CERTIFICATE_TYPE:
+      g_value_set_string (value, self->priv->cert_type);
+      break;
+    case PROP_CERTIFICATE_CHAIN_DATA:
+      g_value_set_boxed (value, self->priv->cert_data);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+  }
+}
+
+static void
+tp_tests_tls_certificate_set_property (GObject *object,
+    guint property_id,
+    const GValue *value,
+    GParamSpec *pspec)
+{
+  TpTestsTLSCertificate *self = TP_TESTS_TLS_CERTIFICATE (object);
+
+  switch (property_id)
+    {
+    case PROP_OBJECT_PATH:
+      self->priv->object_path = g_value_dup_string (value);
+      break;
+    case PROP_CERTIFICATE_TYPE:
+      self->priv->cert_type = g_value_dup_string (value);
+      break;
+    case PROP_CERTIFICATE_CHAIN_DATA:
+      self->priv->cert_data = g_value_dup_boxed (value);
+      break;
+    case PROP_DBUS_DAEMON:
+      self->priv->daemon = g_value_dup_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, value);
+      break;
+    }
+}
+
+static void
+tp_tests_tls_certificate_finalize (GObject *object)
+{
+  TpTestsTLSCertificate *self = TP_TESTS_TLS_CERTIFICATE (object);
+
+  tp_clear_boxed (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST,
+      &self->priv->rejections);
+
+  g_free (self->priv->object_path);
+  g_free (self->priv->cert_type);
+  g_ptr_array_unref (self->priv->cert_data);
+
+  G_OBJECT_CLASS (tp_tests_tls_certificate_parent_class)->finalize (object);
+}
+
+static void
+tp_tests_tls_certificate_dispose (GObject *object)
+{
+  TpTestsTLSCertificate *self = TP_TESTS_TLS_CERTIFICATE (object);
+
+  if (self->priv->dispose_has_run)
+    return;
+
+  self->priv->dispose_has_run = TRUE;
+
+  tp_clear_object (&self->priv->daemon);
+
+  G_OBJECT_CLASS (tp_tests_tls_certificate_parent_class)->dispose (object);
+}
+
+static void
+tp_tests_tls_certificate_constructed (GObject *object)
+{
+  TpTestsTLSCertificate *self = TP_TESTS_TLS_CERTIFICATE (object);
+  void (*chain_up) (GObject *) =
+    G_OBJECT_CLASS (tp_tests_tls_certificate_parent_class)->constructed;
+
+  if (chain_up != NULL)
+    chain_up (object);
+
+  /* register the certificate on the bus */
+  tp_dbus_daemon_register_object (self->priv->daemon,
+      self->priv->object_path, self);
+}
+
+static void
+tp_tests_tls_certificate_init (TpTestsTLSCertificate *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+      TP_TESTS_TYPE_TLS_CERTIFICATE, TpTestsTLSCertificatePrivate);
+  self->priv->rejections = g_ptr_array_new ();
+}
+
+static void
+tp_tests_tls_certificate_class_init (TpTestsTLSCertificateClass *klass)
+{
+  static TpDBusPropertiesMixinPropImpl object_props[] = {
+    { "State", "state", NULL },
+    { "Rejections", "rejections", NULL },
+    { "CertificateType", "certificate-type", NULL },
+    { "CertificateChainData", "certificate-chain-data", NULL },
+    { NULL }
+  };
+  static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
+    { TP_IFACE_AUTHENTICATION_TLS_CERTIFICATE,
+      tp_dbus_properties_mixin_getter_gobject_properties,
+      NULL,
+      object_props,
+    },
+    { NULL }
+  };
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+  GParamSpec *pspec;
+
+  g_type_class_add_private (klass, sizeof (TpTestsTLSCertificatePrivate));
+
+  oclass->finalize = tp_tests_tls_certificate_finalize;
+  oclass->dispose = tp_tests_tls_certificate_dispose;
+  oclass->set_property = tp_tests_tls_certificate_set_property;
+  oclass->get_property = tp_tests_tls_certificate_get_property;
+  oclass->constructed = tp_tests_tls_certificate_constructed;
+
+  pspec = g_param_spec_string ("object-path",
+      "D-Bus object path",
+      "The D-Bus object path used for this object on the bus.",
+      NULL,
+      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (oclass, PROP_OBJECT_PATH, pspec);
+
+  pspec = g_param_spec_uint ("state",
+      "State of this certificate",
+      "The state of this TLS certificate.",
+      0, TP_NUM_TLS_CERTIFICATE_STATES - 1,
+      TP_TLS_CERTIFICATE_STATE_PENDING,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (oclass, PROP_STATE, pspec);
+
+  pspec = g_param_spec_boxed ("rejections",
+      "The reject reasons",
+      "The reasons why this TLS certificate has been rejected",
+      TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (oclass, PROP_REJECTIONS, pspec);
+
+  pspec = g_param_spec_string ("certificate-type",
+      "The certificate type",
+      "The type of this certificate.",
+      NULL,
+      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (oclass, PROP_CERTIFICATE_TYPE, pspec);
+
+  pspec = g_param_spec_boxed ("certificate-chain-data",
+      "The certificate chain data",
+      "The raw PEM-encoded trust chain of this certificate.",
+      TP_ARRAY_TYPE_UCHAR_ARRAY_LIST,
+      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (oclass, PROP_CERTIFICATE_CHAIN_DATA, pspec);
+
+  pspec = g_param_spec_object ("dbus-daemon",
+      "The DBus daemon connection",
+      "The connection to the DBus daemon owning the CM",
+      TP_TYPE_DBUS_DAEMON,
+      G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (oclass, PROP_DBUS_DAEMON, pspec);
+
+  klass->dbus_props_class.interfaces = prop_interfaces;
+  tp_dbus_properties_mixin_class_init (oclass,
+      G_STRUCT_OFFSET (TpTestsTLSCertificateClass, dbus_props_class));
+}
+
+static void
+tp_tests_tls_certificate_accept (TpSvcAuthenticationTLSCertificate *cert,
+    DBusGMethodInvocation *context)
+{
+  TpTestsTLSCertificate *self = TP_TESTS_TLS_CERTIFICATE (cert);
+
+  DEBUG ("Accept() called on the TLS certificate; current state %u",
+      self->priv->cert_state);
+
+  if (self->priv->cert_state != TP_TLS_CERTIFICATE_STATE_PENDING)
+    {
+      GError error =
+        { TP_ERROR,
+          TP_ERROR_INVALID_ARGUMENT,
+          "Calling Accept() on a certificate with state != PENDING "
+          "doesn't make sense."
+        };
+
+      dbus_g_method_return_error (context, &error);
+      return;
+    }
+
+  self->priv->cert_state = TP_TLS_CERTIFICATE_STATE_ACCEPTED;
+  tp_svc_authentication_tls_certificate_emit_accepted (self);
+
+  tp_svc_authentication_tls_certificate_return_from_accept (context);
+}
+
+static void
+tp_tests_tls_certificate_reject (TpSvcAuthenticationTLSCertificate *cert,
+    const GPtrArray *rejections,
+    DBusGMethodInvocation *context)
+{
+  TpTestsTLSCertificate *self = TP_TESTS_TLS_CERTIFICATE (cert);
+
+  DEBUG ("Reject() called on the TLS certificate with rejections %p, "
+      "length %u; current state %u", rejections, rejections->len,
+      self->priv->cert_state);
+
+  if (rejections->len < 1)
+    {
+      GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
+          "Calling Reject() with a zero-length rejection list." };
+
+      dbus_g_method_return_error (context, &error);
+      return;
+    }
+
+  if (self->priv->cert_state != TP_TLS_CERTIFICATE_STATE_PENDING)
+    {
+      GError error =
+        { TP_ERROR,
+          TP_ERROR_INVALID_ARGUMENT,
+          "Calling Reject() on a certificate with state != PENDING "
+          "doesn't make sense."
+        };
+
+      dbus_g_method_return_error (context, &error);
+      return;
+    }
+
+  tp_clear_boxed (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST,
+      &self->priv->rejections);
+
+  self->priv->rejections =
+    g_boxed_copy (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST,
+        rejections);
+  self->priv->cert_state = TP_TLS_CERTIFICATE_STATE_REJECTED;
+
+  tp_svc_authentication_tls_certificate_emit_rejected (
+      self, self->priv->rejections);
+
+  tp_svc_authentication_tls_certificate_return_from_reject (context);
+}
+
+static void
+tls_certificate_iface_init (gpointer g_iface,
+    gpointer iface_data)
+{
+  TpSvcAuthenticationTLSCertificateClass *klass = g_iface;
+
+#define IMPLEMENT(x) \
+  tp_svc_authentication_tls_certificate_implement_##x ( \
+      klass, tp_tests_tls_certificate_##x)
+  IMPLEMENT (accept);
+  IMPLEMENT (reject);
+#undef IMPLEMENT
+}
+
+void
+tp_tests_tls_certificate_clear_rejection (TpTestsTLSCertificate *self)
+{
+  g_ptr_array_set_size (self->priv->rejections, 0);
+}
diff --git a/tests/lib/telepathy/contactlist/tls-certificate.h 
b/tests/lib/telepathy/contactlist/tls-certificate.h
new file mode 100644
index 0000000..1de3192
--- /dev/null
+++ b/tests/lib/telepathy/contactlist/tls-certificate.h
@@ -0,0 +1,68 @@
+/*
+ * tls-certificate.h - Header for TpTestsTLSCertificate
+ * Copyright (C) 2010 Collabora Ltd.
+ * @author Cosimo Cecchi <cosimo cecchi collabora co uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __TP_TESTS_TLS_CERTIFICATE_H__
+#define __TP_TESTS_TLS_CERTIFICATE_H__
+
+#include <glib-object.h>
+
+#include <telepathy-glib/telepathy-glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _TpTestsTLSCertificate TpTestsTLSCertificate;
+typedef struct _TpTestsTLSCertificateClass TpTestsTLSCertificateClass;
+typedef struct _TpTestsTLSCertificatePrivate TpTestsTLSCertificatePrivate;
+
+struct _TpTestsTLSCertificateClass {
+  GObjectClass parent_class;
+
+  TpDBusPropertiesMixinClass dbus_props_class;
+};
+
+struct _TpTestsTLSCertificate {
+  GObject parent;
+
+  TpTestsTLSCertificatePrivate *priv;
+};
+
+GType tp_tests_tls_certificate_get_type (void);
+
+#define TP_TESTS_TYPE_TLS_CERTIFICATE \
+  (tp_tests_tls_certificate_get_type ())
+#define TP_TESTS_TLS_CERTIFICATE(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_TLS_CERTIFICATE, \
+      TpTestsTLSCertificate))
+#define TP_TESTS_TLS_CERTIFICATE_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_TLS_CERTIFICATE, \
+      TpTestsTLSCertificateClass))
+#define TP_TESTS_IS_TLS_CERTIFICATE(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_TLS_CERTIFICATE))
+#define TP_TESTS_IS_TLS_CERTIFICATE_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_TLS_CERTIFICATE))
+#define TP_TESTS_TLS_CERTIFICATE_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_TLS_CERTIFICATE, \
+      TpTestsTLSCertificateClass))
+
+void tp_tests_tls_certificate_clear_rejection (TpTestsTLSCertificate *self);
+
+G_END_DECLS
+
+#endif /* #ifndef __TP_TESTS_TLS_CERTIFICATE_H__*/
diff --git a/tests/lib/telepathy/contactlist/tp-test-contactlist.deps 
b/tests/lib/telepathy/contactlist/tp-test-contactlist.deps
index 0d850c2..6e998ce 100644
--- a/tests/lib/telepathy/contactlist/tp-test-contactlist.deps
+++ b/tests/lib/telepathy/contactlist/tp-test-contactlist.deps
@@ -2,4 +2,4 @@ gio-2.0
 dbus-glib-1
 gobject-2.0
 gio-2.0
-telepathy-glib
+telepathy-glib-1
diff --git a/tests/lib/telepathy/contactlist/util.c b/tests/lib/telepathy/contactlist/util.c
index 21d3855..792afb9 100644
--- a/tests/lib/telepathy/contactlist/util.c
+++ b/tests/lib/telepathy/contactlist/util.c
@@ -12,7 +12,8 @@
 
 #include "util.h"
 
-#include <telepathy-glib/connection.h>
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
 
 #include <glib/gstdio.h>
 #include <string.h>
@@ -176,6 +177,26 @@ _tp_tests_assert_strv_equals (const char *file,
 }
 
 void
+_tp_tests_assert_bytes_equal (const gchar *file, int line,
+  GBytes *actual, gconstpointer expected_data,
+  gsize expected_length)
+{
+  if (expected_length != g_bytes_get_size (actual))
+    {
+      g_error ("%s:%d: assertion failed: expected %"G_GSIZE_FORMAT
+         " bytes, got %"G_GSIZE_FORMAT,
+        file, line, expected_length, g_bytes_get_size (actual));
+    }
+  else if (memcmp (g_bytes_get_data (actual, NULL),
+      expected_data, expected_length) != 0)
+    {
+      g_error (
+        "%s:%d: assertion failed: expected data didn't match the actual data",
+        file, line);
+    }
+}
+
+void
 tp_tests_create_conn (GType conn_type,
     const gchar *account,
     gboolean connect,
@@ -183,7 +204,6 @@ tp_tests_create_conn (GType conn_type,
     TpConnection **client_conn)
 {
   TpDBusDaemon *dbus;
-  TpSimpleClientFactory *factory;
   gchar *name;
   gchar *conn_path;
   GError *error = NULL;
@@ -192,7 +212,6 @@ tp_tests_create_conn (GType conn_type,
   g_assert (client_conn != NULL);
 
   dbus = tp_tests_dbus_daemon_dup_or_die ();
-  factory = (TpSimpleClientFactory *) tp_automatic_client_factory_new (dbus);
 
   *service_conn = tp_tests_object_new_static_class (
         conn_type,
@@ -205,8 +224,7 @@ tp_tests_create_conn (GType conn_type,
         &name, &conn_path, &error));
   g_assert_no_error (error);
 
-  *client_conn = tp_simple_client_factory_ensure_connection (factory,
-      conn_path, NULL, &error);
+  *client_conn = tp_tests_connection_new (dbus, NULL, conn_path, &error);
   g_assert (*client_conn != NULL);
   g_assert_no_error (error);
 
@@ -222,7 +240,6 @@ tp_tests_create_conn (GType conn_type,
   g_free (conn_path);
 
   g_object_unref (dbus);
-  g_object_unref (factory);
 }
 
 void
@@ -318,6 +335,7 @@ _tp_create_local_socket (TpSocketAddressType address_type,
     TpSocketAccessControl access_control,
     GSocketService **service,
     gchar **unix_address,
+    gchar **unix_tmpdir,
     GError **error)
 {
   gboolean success;
@@ -343,7 +361,20 @@ _tp_create_local_socket (TpSocketAddressType address_type,
 #ifdef HAVE_GIO_UNIX
       case TP_SOCKET_ADDRESS_TYPE_UNIX:
         {
-          address = g_unix_socket_address_new (tmpnam (NULL));
+          GError *e = NULL;
+          gchar *dir = g_dir_make_tmp ("tp-glib-tests.XXXXXX", &e);
+          gchar *name;
+
+          g_assert_no_error (e);
+
+          name = g_build_filename (dir, "s", NULL);
+          address = g_unix_socket_address_new (name);
+          g_free (name);
+
+          if (unix_tmpdir != NULL)
+            *unix_tmpdir = dir;
+          else
+            g_free (dir);
           break;
         }
 #endif
@@ -450,12 +481,11 @@ one_contact_cb (GObject *object,
 TpContact *
 tp_tests_connection_run_until_contact_by_id (TpConnection *connection,
     const gchar *id,
-    guint n_features,
-    const TpContactFeature *features)
+    const GQuark *features)
 {
   TpContact *contact = NULL;
 
-  tp_connection_dup_contact_by_id_async (connection, id, n_features, features,
+  tp_connection_dup_contact_by_id_async (connection, id, features,
       one_contact_cb, &contact);
 
   while (contact == NULL)
@@ -463,3 +493,160 @@ tp_tests_connection_run_until_contact_by_id (TpConnection *connection,
 
   return contact;
 }
+
+void
+tp_tests_channel_assert_expect_members (TpChannel *channel,
+    TpIntset *expected_members)
+{
+  GPtrArray *contacts;
+  TpIntset *members;
+  guint i;
+
+  members = tp_intset_new ();
+  contacts = tp_channel_group_dup_members (channel);
+  if (contacts != NULL)
+    {
+      for (i = 0; i < contacts->len; i++)
+        {
+          TpContact *contact = g_ptr_array_index (contacts, i);
+          tp_intset_add (members, tp_contact_get_handle (contact));
+        }
+    }
+
+  g_assert (tp_intset_is_equal (members, expected_members));
+
+  g_ptr_array_unref (contacts);
+  tp_intset_destroy (members);
+}
+
+TpConnection *
+tp_tests_connection_new (TpDBusDaemon *dbus,
+    const gchar *bus_name,
+    const gchar *object_path,
+    GError **error)
+{
+  TpClientFactory *factory;
+  gchar *dup_path = NULL;
+  TpConnection *ret = NULL;
+
+  g_return_val_if_fail (TP_IS_DBUS_DAEMON (dbus), NULL);
+  g_return_val_if_fail (object_path != NULL ||
+                        (bus_name != NULL && bus_name[0] != ':'), NULL);
+
+  if (object_path == NULL)
+    {
+      dup_path = g_strdelimit (g_strdup_printf ("/%s", bus_name), ".", '/');
+      object_path = dup_path;
+    }
+
+  if (!tp_dbus_check_valid_object_path (object_path, error))
+    goto finally;
+
+  factory = tp_automatic_client_factory_new (dbus);
+  ret = tp_client_factory_ensure_connection (factory,
+      object_path, NULL, error);
+  g_object_unref (factory);
+
+finally:
+  g_free (dup_path);
+
+  return ret;
+}
+
+TpAccount *
+tp_tests_account_new (TpDBusDaemon *dbus,
+    const gchar *object_path,
+    GError **error)
+{
+  TpClientFactory *factory;
+  TpAccount *ret;
+
+  if (!tp_dbus_check_valid_object_path (object_path, error))
+    return NULL;
+
+  factory = tp_automatic_client_factory_new (dbus);
+  ret = tp_client_factory_ensure_account (factory,
+      object_path, NULL, error);
+  g_object_unref (factory);
+
+  return ret;
+}
+
+TpChannel *
+tp_tests_channel_new (TpConnection *conn,
+    const gchar *object_path,
+    const gchar *optional_channel_type,
+    TpHandleType optional_handle_type,
+    TpHandle optional_handle,
+    GError **error)
+{
+  TpChannel *ret;
+  GHashTable *asv;
+
+  asv = tp_asv_new (NULL, NULL);
+
+  if (optional_channel_type != NULL)
+    {
+      tp_asv_set_string (asv,
+          TP_PROP_CHANNEL_CHANNEL_TYPE, optional_channel_type);
+    }
+  if (optional_handle_type != TP_HANDLE_TYPE_NONE)
+    {
+      tp_asv_set_uint32 (asv,
+          TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, optional_handle_type);
+    }
+  if (optional_handle != 0)
+    {
+      tp_asv_set_uint32 (asv,
+          TP_PROP_CHANNEL_TARGET_HANDLE, optional_handle);
+    }
+
+  ret = tp_tests_channel_new_from_properties (conn, object_path, asv, error);
+
+  g_hash_table_unref (asv);
+
+  return ret;
+}
+
+TpChannel *
+tp_tests_channel_new_from_properties (TpConnection *conn,
+    const gchar *object_path,
+    const GHashTable *immutable_properties,
+    GError **error)
+{
+  TpClientFactory *factory;
+
+  if (!tp_dbus_check_valid_object_path (object_path, error))
+    return NULL;
+
+  factory = tp_proxy_get_factory (conn);
+  return tp_client_factory_ensure_channel (factory, conn,
+      object_path, immutable_properties, error);
+}
+
+void
+tp_tests_add_channel_to_ptr_array (GPtrArray *arr,
+    TpChannel *channel)
+{
+  GValueArray *tmp;
+  GVariant *variant;
+  GValue v = G_VALUE_INIT;
+  GHashTable *asv;
+
+  g_assert (arr != NULL);
+  g_assert (channel != NULL);
+
+  variant = tp_channel_dup_immutable_properties (channel);
+  dbus_g_value_parse_g_variant (variant, &v);
+  asv = g_value_get_boxed (&v);
+
+  tmp = tp_value_array_build (2,
+      DBUS_TYPE_G_OBJECT_PATH, tp_proxy_get_object_path (channel),
+      TP_HASH_TYPE_STRING_VARIANT_MAP, asv,
+      G_TYPE_INVALID);
+
+  g_ptr_array_add (arr, tmp);
+  g_variant_unref (variant);
+  g_value_unset (&v);
+}
+
diff --git a/tests/lib/telepathy/contactlist/util.h b/tests/lib/telepathy/contactlist/util.h
index d58138e..3797f85 100644
--- a/tests/lib/telepathy/contactlist/util.h
+++ b/tests/lib/telepathy/contactlist/util.h
@@ -12,7 +12,6 @@
 #define __TP_TESTS_LIB_UTIL_H__
 
 #include <telepathy-glib/telepathy-glib.h>
-#include <telepathy-glib/base-connection.h>
 
 TpDBusDaemon *tp_tests_dbus_daemon_dup_or_die (void);
 
@@ -36,6 +35,12 @@ void _tp_tests_assert_strv_equals (const char *file, int line,
   const char *actual_desc, gconstpointer actual_strv,
   const char *expected_desc, gconstpointer expected_strv);
 
+#define tp_tests_assert_bytes_equals(actual, expected, expected_length) \
+  _tp_tests_assert_bytes_equal (__FILE__, __LINE__, \
+      actual, expected, expected_length)
+void _tp_tests_assert_bytes_equal (const gchar *file, int line,
+  GBytes *actual, gconstpointer expected_data, gsize expected_length);
+
 void tp_tests_create_conn (GType conn_type,
     const gchar *account,
     gboolean connect,
@@ -63,6 +68,7 @@ GValue *_tp_create_local_socket (TpSocketAddressType address_type,
     TpSocketAccessControl access_control,
     GSocketService **service,
     gchar **unix_address,
+    gchar **unix_tmpdir,
     GError **error);
 
 void _tp_destroy_socket_control_list (gpointer data);
@@ -72,7 +78,33 @@ void tp_tests_connection_assert_disconnect_succeeds (TpConnection *connection);
 TpContact *tp_tests_connection_run_until_contact_by_id (
     TpConnection *connection,
     const gchar *id,
-    guint n_features,
-    const TpContactFeature *features);
+    const GQuark *features);
+
+void tp_tests_channel_assert_expect_members (TpChannel *channel,
+    TpIntset *expected_members);
+
+TpConnection *tp_tests_connection_new (TpDBusDaemon *dbus,
+    const gchar *bus_name,
+    const gchar *object_path,
+    GError **error);
+
+TpAccount *tp_tests_account_new (TpDBusDaemon *dbus,
+    const gchar *object_path,
+    GError **error);
+
+TpChannel *tp_tests_channel_new (TpConnection *conn,
+    const gchar *object_path,
+    const gchar *optional_channel_type,
+    TpHandleType optional_handle_type,
+    TpHandle optional_handle,
+    GError **error);
+
+TpChannel *tp_tests_channel_new_from_properties (TpConnection *conn,
+    const gchar *object_path,
+    const GHashTable *immutable_properties,
+    GError **error);
+
+void tp_tests_add_channel_to_ptr_array (GPtrArray *arr,
+    TpChannel *channel);
 
 #endif /* #ifndef __TP_TESTS_LIB_UTIL_H__ */
diff --git a/tests/lib/telepathy/tpf-test.deps b/tests/lib/telepathy/tpf-test.deps
index 1bce19e..87e45fc 100644
--- a/tests/lib/telepathy/tpf-test.deps
+++ b/tests/lib/telepathy/tpf-test.deps
@@ -3,5 +3,5 @@ gobject-2.0
 gio-2.0
 gee-0.8
 folks
-telepathy-glib
+telepathy-glib-1
 tp-test-contactlist
diff --git a/tests/telepathy/Makefile.am b/tests/telepathy/Makefile.am
index 5eda26d..f7abebe 100644
--- a/tests/telepathy/Makefile.am
+++ b/tests/telepathy/Makefile.am
@@ -52,7 +52,7 @@ AM_VALAFLAGS += \
        --pkg gio-2.0 \
        --pkg gee-0.8 \
        --pkg gmodule-2.0 \
-       --pkg telepathy-glib \
+       --pkg telepathy-glib-1 \
        --pkg folks \
        --pkg folks-telepathy \
        --pkg folks-test \
diff --git a/tests/telepathy/individual-properties.vala b/tests/telepathy/individual-properties.vala
index 5351824..2d8215a 100644
--- a/tests/telepathy/individual-properties.vala
+++ b/tests/telepathy/individual-properties.vala
@@ -29,7 +29,7 @@ public class IndividualPropertiesTests : TpfTest.TestCase
   private HashSet<string>? _changes_pending = null;
 
   private static string iid_prefix =
-      "telepathy:/org/freedesktop/Telepathy/Account/cm/protocol/account:";
+      "telepathy:/im/telepathy1/Account/cm/protocol/account:";
   private string olivier_sha1 = Checksum.compute_for_string (ChecksumType.SHA1,
       iid_prefix + "olivier example com");
 
diff --git a/tests/telepathy/individual-retrieval.vala b/tests/telepathy/individual-retrieval.vala
index 5908c05..8a629a5 100644
--- a/tests/telepathy/individual-retrieval.vala
+++ b/tests/telepathy/individual-retrieval.vala
@@ -29,7 +29,7 @@ public class IndividualRetrievalTests : TpfTest.TestCase
   private HashSet<string> default_individuals;
 
   private static string iid_prefix =
-      "telepathy:/org/freedesktop/Telepathy/Account/cm/protocol/account:";
+      "telepathy:/im/telepathy1/Account/cm/protocol/account:";
 
   public IndividualRetrievalTests ()
     {
diff --git a/tests/tools/manager-file.py b/tests/tools/manager-file.py
index 45f6404..a97a166 100755
--- a/tests/tools/manager-file.py
+++ b/tests/tools/manager-file.py
@@ -88,8 +88,8 @@ gflags = {
 def write_manager(f, manager, protos):
     # pointless backwards compat section
     print >> f, '[ConnectionManager]'
-    print >> f, 'BusName=org.freedesktop.Telepathy.ConnectionManager.' + manager
-    print >> f, 'ObjectPath=/org/freedesktop/Telepathy/ConnectionManager/' + manager
+    print >> f, 'BusName=im.telepathy1.ConnectionManager.' + manager
+    print >> f, 'ObjectPath=/im/telepathy1/ConnectionManager/' + manager
 
     # protocols
     for proto, params in protos.iteritems():
diff --git a/tools/import-pidgin.vala b/tools/import-pidgin.vala
index f09e693..cbb25a2 100644
--- a/tools/import-pidgin.vala
+++ b/tools/import-pidgin.vala
@@ -273,7 +273,7 @@ public class Folks.Importers.Pidgin : Folks.Importer
        * http://telepathy.freedesktop.org/spec/Connection_Manager.html#Protocol
        * and http://developer.pidgin.im/wiki/prpl_id. */
       if (tp_protocol == "bonjour")
-        tp_protocol = "local-xmpp";
+        tp_protocol = "local_xmpp";
       else if (tp_protocol == "novell")
         tp_protocol = "groupwise";
       else if (tp_protocol == "gg")



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