[folks] Add write support for Tracker



commit 8b35f9a6498d48225e31b2dd4a86fd1dc64c664e
Author: Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
Date:   Fri Mar 18 14:45:23 2011 +0000

    Add write support for Tracker
    
    Closes: bgo#645413

 NEWS                                        |    1 +
 backends/tracker/lib/trf-persona-store.vala | 1415 ++++++++++++++++++++++++---
 backends/tracker/lib/trf-persona.vala       |  243 ++++--
 backends/tracker/lib/trf-util.vala          |   18 +
 tests/tracker/Makefile.am                   |   96 ++
 tests/tracker/add-contact.vala              |   19 +-
 tests/tracker/add-persona.vala              |  514 ++++++++++
 tests/tracker/additional-names-updates.vala |   44 +-
 tests/tracker/family-name-updates.vala      |   31 +-
 tests/tracker/given-name-updates.vala       |   19 +-
 tests/tracker/name-details-interface.vala   |   14 +-
 tests/tracker/nickname-updates.vala         |   26 +-
 tests/tracker/prefix-name-updates.vala      |   19 +-
 tests/tracker/remove-persona.vala           |  208 ++++
 tests/tracker/set-alias.vala                |  168 ++++
 tests/tracker/set-avatar.vala               |  145 +++
 tests/tracker/set-birthday.vala             |  154 +++
 tests/tracker/set-emails.vala               |  160 +++
 tests/tracker/set-favourite.vala            |  180 ++++
 tests/tracker/set-full-name.vala            |  144 +++
 tests/tracker/set-gender.vala               |  142 +++
 tests/tracker/set-im-addresses.vala         |  177 ++++
 tests/tracker/set-notes.vala                |  153 +++
 tests/tracker/set-phones.vala               |  160 +++
 tests/tracker/set-postal-addresses.vala     |  174 ++++
 tests/tracker/set-roles.vala                |  153 +++
 tests/tracker/set-structured-name.vala      |  156 +++
 tests/tracker/set-urls.vala                 |  166 ++++
 tests/tracker/suffix-name-updates.vala      |    3 +
 29 files changed, 4643 insertions(+), 259 deletions(-)
---
diff --git a/NEWS b/NEWS
index 718d6f0..d234076 100644
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,7 @@ Bugs fixed:
 * Bug 645475 â?? Linker warnings for Tracker backend tests
 * Bug 645570 â?? Fix checks for empty and equal StructuredNames
 * Bug 645989 â?? Ensure add_persona_from_details handles the basic attribute
+* Bug 645413 â?? Write support for Tracker
 
 API changes:
 * Add equal () to StructuredName
diff --git a/backends/tracker/lib/trf-persona-store.vala b/backends/tracker/lib/trf-persona-store.vala
index ce43a11..930954c 100644
--- a/backends/tracker/lib/trf-persona-store.vala
+++ b/backends/tracker/lib/trf-persona-store.vala
@@ -37,7 +37,7 @@ internal enum Trf.Fields
   ADDITIONAL_NAMES,
   PREFIXES,
   SUFFIXES,
-  NICKNAME,
+  ALIAS,
   BIRTHDAY,
   AVATAR_URL,
   IM_ADDRESSES,
@@ -74,7 +74,8 @@ internal enum Trf.AfflInfoFields
   AFFL_PHONE,
   AFFL_WEBSITE,
   AFFL_BLOG,
-  AFFL_URL
+  AFFL_URL,
+  IM_NICKNAME
 }
 
 internal enum Trf.PostalAddressFields
@@ -111,7 +112,8 @@ internal enum Trf.IMFields
 {
   TRACKER_ID,
   PROTO,
-  ID
+  ID,
+  IM_NICKNAME
 }
 
 internal enum Trf.PhoneFields
@@ -131,6 +133,21 @@ internal enum Trf.TagFields
   TRACKER_ID
 }
 
+private enum Trf.Attrib
+{
+  EMAILS,
+  PHONES,
+  URLS,
+  IM_ADDRESSES,
+  POSTAL_ADDRESSES
+}
+
+private const char _REMOVE_ALL_ATTRIBS = 0x01;
+private const char _REMOVE_PHONES      = 0x02;
+private const char _REMOVE_POSTALS     = 0x04;
+private const char _REMOVE_IM_ADDRS    = 0x08;
+private const char _REMOVE_EMAILS      = 0x10;
+
 /**
  * A persona store.
  * It will create { link Persona}s for each contacts on the main addressbook.
@@ -165,7 +182,8 @@ public class Trf.PersonaStore : Folks.PersonaStore
     "GROUP_CONCAT ( " +
     " fn:concat(tracker:id(?affl),'\t'," +
     " tracker:coalesce(nco:imProtocol(?a),''), " +
-    "'\t', tracker:coalesce(nco:imID(?a),'')),'\\n') " +
+    "'\t', tracker:coalesce(nco:imID(?a),''), '\t'," +
+    " tracker:coalesce(nco:imNickname(?a),'')), '\\n') " +
     "WHERE { ?_contact nco:hasAffiliation ?affl. " +
     " ?affl nco:hasIMAddress ?a } ) " +
 
@@ -248,7 +266,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
     "WHERE { ?_contact nco:hasAffiliation " +
     "?affl . ?affl nco:hasPostalAddress ?postal }) " +
 
-    "{ ?_contact a nco:PersonContact . } " +
+    "{ ?_contact a nco:PersonContact . %s } " +
     "ORDER BY tracker:id(?_contact) ";
 
 
@@ -268,7 +286,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
    */
   public override MaybeBool can_add_personas
     {
-      get { return MaybeBool.FALSE; }
+      get { return MaybeBool.TRUE; }
     }
 
   /**
@@ -304,7 +322,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
    */
   public override MaybeBool can_remove_personas
     {
-      get { return MaybeBool.FALSE; }
+      get { return MaybeBool.TRUE; }
     }
 
   /**
@@ -344,27 +362,438 @@ public class Trf.PersonaStore : Folks.PersonaStore
   /**
    * Add a new { link Persona} to the PersonaStore.
    *
+   * Accepted keys for `details` are:
+   * - PersonaStore.detail_key (PersonaDetail.IM_ADDRESSES)
+   * - PersonaStore.detail_key (PersonaDetail.ALIAS)
+   * - PersonaStore.detail_key (PersonaDetail.FULL_NAME)
+   * - PersonaStore.detail_key (PersonaDetail.FAVOURITE)
+   * - PersonaStore.detail_key (PersonaDetail.STRUCTURED_NAME)
+   * - PersonaStore.detail_key (PersonaDetail.AVATAR)
+   * - PersonaStore.detail_key (PersonaDetail.BIRTHDAY)
+   * - PersonaStore.detail_key (PersonaDetail.GENDER)
+   * - PersonaStore.detail_key (PersonaDetail.EMAIL_ADDRESSES)
+   * - PersonaStore.detail_key (PersonaDetail.IM_ADDRESSES)
+   * - PersonaStore.detail_key (PersonaDetail.NOTES)
+   * - PersonaStore.detail_key (PersonaDetail.PHONE_NUMBERS)
+   * - PersonaStore.detail_key (PersonaDetail.POSTAL_ADDRESSES)
+   * - PersonaStore.detail_key (PersonaDetail.ROLES)
+   * - PersonaStore.detail_key (PersonaDetail.URL)
+   *
    * See { link Folks.PersonaStore.add_persona_from_details}.
    */
   public override async Folks.Persona? add_persona_from_details (
       HashTable<string, Value?> details) throws Folks.PersonaStoreError
     {
-      throw new PersonaStoreError.READ_ONLY (
-          "Personas cannot be added to this store.");
+      var builder = new Tracker.Sparql.Builder.update ();
+      builder.insert_open (null);
+      builder.subject ("_:p");
+      builder.predicate ("a");
+      builder.object ("nco:PersonContact");
+
+      foreach (var k in details.get_keys ())
+        {
+          Value? v = details.lookup (k);
+          if (k == this.detail_key (PersonaDetail.ALIAS))
+            {
+              builder.subject ("_:p");
+              builder.predicate (Trf.OntologyDefs.NCO_NICKNAME);
+              builder.object_string (v.get_string ());
+            }
+          else if (k == this.detail_key (PersonaDetail.FULL_NAME))
+            {
+              builder.subject ("_:p");
+              builder.predicate (Trf.OntologyDefs.NCO_FULLNAME);
+              builder.object_string (v.get_string ());
+            }
+          else if (k == this.detail_key (PersonaDetail.STRUCTURED_NAME))
+            {
+              StructuredName sname = (StructuredName) v.get_object ();
+              builder.subject ("_:p");
+              builder.predicate (Trf.OntologyDefs.NCO_FAMILY);
+              builder.object_string (sname.family_name);
+              builder.predicate (Trf.OntologyDefs.NCO_GIVEN);
+              builder.object_string (sname.given_name);
+              builder.predicate (Trf.OntologyDefs.NCO_ADDITIONAL);
+              builder.object_string (sname.additional_names);
+              builder.predicate (Trf.OntologyDefs.NCO_SUFFIX);
+              builder.object_string (sname.suffixes);
+              builder.predicate (Trf.OntologyDefs.NCO_PREFIX);
+              builder.object_string (sname.prefixes);
+            }
+          else if (k == this.detail_key (PersonaDetail.FAVOURITE))
+            {
+              if (v.get_boolean ())
+                {
+                  builder.subject ("_:p");
+                  builder.predicate (Trf.OntologyDefs.NAO_TAG);
+                  builder.object (Trf.OntologyDefs.NAO_FAVORITE);
+                }
+            }
+          else if (k == this.detail_key (PersonaDetail.AVATAR))
+            {
+              var avatar = (File) v.get_object ();
+              builder.subject ("_:photo");
+              builder.predicate ("a");
+              builder.object ("nfo:Image, nie:DataObject");
+              builder.predicate (Trf.OntologyDefs.NIE_URL);
+              builder.object_string (avatar.get_uri ());
+              builder.subject ("_:p");
+              builder.predicate (Trf.OntologyDefs.NCO_PHOTO);
+              builder.object ("_:photo");
+            }
+          else if (k == this.detail_key (PersonaDetail.BIRTHDAY))
+            {
+              var birthday = (DateTime) v.get_boxed ();
+              builder.subject ("_:p");
+              builder.predicate (Trf.OntologyDefs.NCO_BIRTHDAY);
+              TimeVal tv;
+              birthday.to_timeval (out tv);
+              builder.object_string (tv.to_iso8601 ());
+            }
+          else if (k == this.detail_key (PersonaDetail.GENDER))
+            {
+              var gender = (Gender) v.get_enum ();
+              if (gender != Gender.UNSPECIFIED)
+                {
+                  builder.subject ("_:p");
+                  builder.predicate (Trf.OntologyDefs.NCO_GENDER);
+                  if (gender == Gender.MALE)
+                    builder.object (Trf.OntologyDefs.NCO_MALE);
+                  else
+                    builder.object (Trf.OntologyDefs.NCO_FEMALE);
+                }
+            }
+          else if (k == this.detail_key (PersonaDetail.EMAIL_ADDRESSES))
+            {
+              unowned GLib.List<FieldDetails> email_addresses =
+                (GLib.List<FieldDetails>) v.get_pointer ();
+              int email_cnt = 0;
+              foreach (var e in email_addresses)
+                {
+                  var email_affl = "_:email_affl%d".printf (email_cnt);
+                  var email = "_:email%d".printf (email_cnt);
+                  builder.subject (email);
+                  builder.predicate ("a");
+                  builder.object (Trf.OntologyDefs.NCO_EMAIL);
+                  builder.predicate (Trf.OntologyDefs.NCO_EMAIL_PROP);
+                  builder.object_string (e.value);
+
+                  builder.subject (email_affl);
+                  builder.predicate ("a");
+                  builder.object (Trf.OntologyDefs.NCO_AFFILIATION);
+                  builder.predicate (Trf.OntologyDefs.NCO_HAS_EMAIL);
+                  builder.object (email);
+
+                  builder.subject ("_:p");
+                  builder.predicate (Trf.OntologyDefs.NCO_HAS_AFFILIATION);
+                  builder.object (email_affl);
+
+                  email_cnt++;
+                }
+            }
+          else if (k == this.detail_key (PersonaDetail.IM_ADDRESSES))
+            {
+              var im_addresses =
+                (HashTable<string, LinkedHashSet<string>>) v.get_boxed ();
+
+              int im_cnt = 0;
+              foreach (var proto in im_addresses.get_keys ())
+                {
+                  var addrs_a = im_addresses.lookup (proto);
+
+                  foreach (var addr in addrs_a)
+                    {
+                      var im_affl = "_:im_affl%d".printf (im_cnt);
+                      var im = "_:im%d".printf (im_cnt);
+
+                      builder.subject (im);
+                      builder.predicate ("a");
+                      builder.object (Trf.OntologyDefs.NCO_IMADDRESS);
+                      builder.predicate (Trf.OntologyDefs.NCO_IMID);
+                      builder.object_string (addr);
+                      builder.predicate (Trf.OntologyDefs.NCO_IMPROTOCOL);
+                      builder.object_string (proto);
+
+                      builder.subject (im_affl);
+                      builder.predicate ("a");
+                      builder.object (Trf.OntologyDefs.NCO_AFFILIATION);
+                      builder.predicate (Trf.OntologyDefs.NCO_HAS_IMADDRESS);
+                      builder.object (im);
+
+                      builder.subject ("_:p");
+                      builder.predicate (Trf.OntologyDefs.NCO_HAS_AFFILIATION);
+                      builder.object (im_affl);
+
+                      im_cnt++;
+                    }
+                }
+            }
+          else if (k == this.detail_key (PersonaDetail.NOTES))
+            {
+              var notes = (Gee.HashSet<Note>) v.get_object ();
+              foreach (var n in notes)
+                {
+                  builder.subject ("_:p");
+                  builder.predicate (Trf.OntologyDefs.NCO_NOTE);
+                  builder.object_string (n.content);
+                }
+            }
+          else if (k == this.detail_key (PersonaDetail.PHONE_NUMBERS))
+            {
+              unowned GLib.List<FieldDetails> phone_numbers =
+                (GLib.List<FieldDetails>) v.get_pointer ();
+
+              int phone_cnt = 0;
+              foreach (var p in phone_numbers)
+                {
+                  var phone_affl = "_:phone_affl%d".printf (phone_cnt);
+                  var phone = "_:phone%d".printf (phone_cnt);
+                  builder.subject (phone);
+                  builder.predicate ("a");
+                  builder.object (Trf.OntologyDefs.NCO_PHONE);
+                  builder.predicate (Trf.OntologyDefs.NCO_PHONE_PROP);
+                  builder.object_string (p.value);
+
+                  builder.subject (phone_affl);
+                  builder.predicate ("a");
+                  builder.object (Trf.OntologyDefs.NCO_AFFILIATION);
+                  builder.predicate (Trf.OntologyDefs.NCO_HAS_PHONE);
+                  builder.object (phone);
+
+                  builder.subject ("_:p");
+                  builder.predicate (Trf.OntologyDefs.NCO_HAS_AFFILIATION);
+                  builder.object (phone_affl);
+
+                  phone_cnt++;
+                }
+            }
+          else if (k == this.detail_key (PersonaDetail.ROLES))
+            {
+              var roles = (Gee.HashSet<Role>) v.get_object ();
+
+              int roles_cnt = 0;
+              foreach (var r in roles)
+                {
+                  var role_affl = "_:role_affl%d".printf (roles_cnt);
+
+                  builder.subject (role_affl);
+                  builder.predicate ("a");
+                  builder.object (Trf.OntologyDefs.NCO_AFFILIATION);
+                  builder.predicate (Trf.OntologyDefs.NCO_ROLE);
+                  builder.object_string (r.title);
+                  builder.predicate (Trf.OntologyDefs.NCO_ORG);
+                  builder.object_string (r.organisation_name);
+
+                  builder.subject ("_:p");
+                  builder.predicate (Trf.OntologyDefs.NCO_HAS_AFFILIATION);
+                  builder.object (role_affl);
+
+                  roles_cnt++;
+                }
+            }
+          else if (k == this.detail_key (PersonaDetail.POSTAL_ADDRESSES))
+            {
+              unowned GLib.List<PostalAddress> postal_addresses =
+                (GLib.List<PostalAddress>) v.get_pointer ();
+
+              int postal_cnt = 0;
+              foreach (var pa in postal_addresses)
+                {
+                  var postal_affl = "_:postal_affl%d".printf (postal_cnt);
+                  var postal = "_:postal%d".printf (postal_cnt);
+                  builder.subject (postal);
+                  builder.predicate ("a");
+                  builder.object (Trf.OntologyDefs.NCO_POSTAL_ADDRESS);
+                  builder.predicate (Trf.OntologyDefs.NCO_POBOX);
+                  builder.object_string (pa.po_box);
+                  builder.predicate (Trf.OntologyDefs.NCO_LOCALITY);
+                  builder.object_string (pa.locality);
+                  builder.predicate (Trf.OntologyDefs.NCO_POSTALCODE);
+                  builder.object_string (pa.postal_code);
+                  builder.predicate (Trf.OntologyDefs.NCO_STREET_ADDRESS);
+                  builder.object_string (pa.street);
+                  builder.predicate (Trf.OntologyDefs.NCO_EXTENDED_ADDRESS);
+                  builder.object_string (pa.extension);
+                  builder.predicate (Trf.OntologyDefs.NCO_COUNTRY);
+                  builder.object_string (pa.country);
+                  builder.predicate (Trf.OntologyDefs.NCO_REGION);
+                  builder.object_string (pa.region);
+
+                  builder.subject (postal_affl);
+                  builder.predicate ("a");
+                  builder.object (Trf.OntologyDefs.NCO_AFFILIATION);
+                  builder.predicate (Trf.OntologyDefs.NCO_HAS_POSTAL_ADDRESS);
+                  builder.object (postal);
+
+                  builder.subject ("_:p");
+                  builder.predicate (Trf.OntologyDefs.NCO_HAS_AFFILIATION);
+                  builder.object (postal_affl);
+
+                  postal_cnt++;
+                }
+            }
+          else if (k == this.detail_key (PersonaDetail.URLS))
+            {
+              unowned GLib.List<FieldDetails> urls =
+                (GLib.List<FieldDetails>) v.get_pointer ();
+
+              int url_cnt = 0;
+              foreach (var u in urls)
+                {
+                  var url_affl = "_:url_affl%d".printf (url_cnt);
+
+                  builder.subject (url_affl);
+                  builder.predicate ("a");
+                  builder.object (Trf.OntologyDefs.NCO_AFFILIATION);
+                  builder.predicate (Trf.OntologyDefs.NCO_URL);
+                  builder.object_string (u.value);
+
+                  builder.subject ("_:p");
+                  builder.predicate (Trf.OntologyDefs.NCO_HAS_AFFILIATION);
+                  builder.object (url_affl);
+
+                  url_cnt++;
+                }
+            }
+          else
+            {
+              throw new PersonaStoreError.INVALID_ARGUMENT (
+                  /* Translators: the first parameter identifies the
+                   * persona store and the second the unknown key
+                   * that was received with the details params. */
+                _("Unrecognized paramter %s by the  %s PersonaStore:\n')"),
+                this.type_id, k);
+            }
+        }
+      builder.insert_close ();
+
+      Trf.Persona ret = null;
+      lock (this._personas)
+        {
+          string? contact_urn = yield this._insert_persona (builder.result,
+              "p");
+          if (contact_urn != null)
+            {
+              string filter = " FILTER(?_contact = <%s>) ".printf (contact_urn);
+              string query = this._INITIAL_QUERY.printf (filter);
+              Queue<Persona> ret_personas;
+              ret_personas = yield this._do_add_contacts (query);
+              ret = ret_personas.pop_head ();
+            }
+          else
+            {
+              debug ("Failed to inserting the new persona  into Tracker.");
+            }
+        }
+
+      return ret;
     }
 
   /**
    * Remove a { link Persona} from the PersonaStore.
    *
    * See { link Folks.PersonaStore.remove_persona}.
+   *
    */
   public override async void remove_persona (Folks.Persona persona)
       throws Folks.PersonaStoreError
     {
-      throw new PersonaStoreError.READ_ONLY (
-          "Personas cannot be removed from this store.");
+      var urn = yield this._remove_attributes_from_persona (persona,
+          _REMOVE_ALL_ATTRIBS);
+
+      /* Finally: remove literal properties */
+      var q = " DELETE { " +
+        " %s ?p ?o " +
+        "} " +
+        "WHERE { " +
+        " %s ?p ?o " +
+        "} ";
+      yield this._tracker_update (q.printf (urn, urn), "remove_persona");
+    }
+
+  private async string _remove_attributes_from_persona (Folks.Persona persona,
+      char remove_flag)
+    {
+      var urn = yield this._urn_from_persona (persona);
+      yield this._remove_attributes (urn, remove_flag);
+      return urn;
     }
 
+  /*
+   * Garbage collecting related resources:
+   *  - for each related resource we (recursively)
+   *    check to if the deleted nco:Person
+   *    is the only one holding a link, if so we
+   *    remove the resource.
+   */
+  private async void _remove_attributes (string urn, char remove_flag)
+    {
+      Gee.HashSet<string> affiliations =
+       yield this._affiliations_from_persona (urn);
+
+     foreach (var affl in affiliations)
+       {
+         bool got_attrib = false;
+
+         if ((remove_flag & _REMOVE_ALL_ATTRIBS) ==
+             _REMOVE_ALL_ATTRIBS ||
+             (remove_flag & _REMOVE_PHONES) == _REMOVE_PHONES)
+           {
+             Gee.HashSet<string> phones =
+               yield this._phones_from_affiliation (affl);
+
+             foreach (var phone in phones)
+               {
+                 got_attrib = true;
+                 yield this._delete_resource (phone);
+               }
+           }
+
+         if ((remove_flag & _REMOVE_ALL_ATTRIBS) ==
+             _REMOVE_ALL_ATTRIBS ||
+             (remove_flag & _REMOVE_POSTALS) == _REMOVE_POSTALS)
+           {
+             Gee.HashSet<string> postals =
+               yield this._postals_from_affiliation (affl);
+             foreach (var postal in postals)
+               {
+                 got_attrib = true;
+                 yield this._delete_resource (postal);
+               }
+           }
+
+         if ((remove_flag & _REMOVE_ALL_ATTRIBS) ==
+             _REMOVE_ALL_ATTRIBS ||
+             (remove_flag & _REMOVE_IM_ADDRS) == _REMOVE_IM_ADDRS)
+           {
+             Gee.HashSet<string> im_addrs =
+               yield this._imaddrs_from_affiliation (affl);
+             foreach (var im_addr in im_addrs)
+               {
+                 got_attrib = true;
+                 yield this._delete_resource (im_addr);
+               }
+           }
+
+         if ((remove_flag & _REMOVE_ALL_ATTRIBS) ==
+             _REMOVE_ALL_ATTRIBS ||
+             (remove_flag & _REMOVE_EMAILS) == _REMOVE_EMAILS)
+           {
+             Gee.HashSet<string> emails =
+               yield this._emails_from_affiliation (affl);
+               foreach (var email in emails)
+                 {
+                   got_attrib = true;
+                   yield yield this._delete_resource (email);
+                 }
+           }
+
+         if (got_attrib ||
+             (remove_flag & _REMOVE_ALL_ATTRIBS) == _REMOVE_ALL_ATTRIBS)
+           yield this._delete_resource (affl);
+       }
+   }
+
   /**
    * Prepare the PersonaStore for use.
    *
@@ -381,10 +810,11 @@ public class Trf.PersonaStore : Folks.PersonaStore
             {
               try
                 {
-                  this._connection = Tracker.Sparql.Connection.get ();
+                  this._connection =
+                    yield Tracker.Sparql.Connection.get_async ();
 
                   yield this._build_predicates_table ();
-                  yield this._do_add_contacts (this._INITIAL_QUERY);
+                  yield this._do_add_contacts (this._INITIAL_QUERY.printf (""));
 
                   /* Don't add a match rule for all signals from Tracker but
                    * only for GraphUpdated with the specific class we need. We
@@ -402,6 +832,9 @@ public class Trf.PersonaStore : Folks.PersonaStore
                       "GraphUpdated", this._OBJECT_PATH,
                       Trf.OntologyDefs.PERSON_CLASS, GLib.DBusSignalFlags.NONE,
                       this._graph_updated_cb);
+
+                  this._is_prepared = true;
+                  this.notify_property ("is-prepared");
                 }
               catch (GLib.IOError e1)
                 {
@@ -566,53 +999,79 @@ public class Trf.PersonaStore : Folks.PersonaStore
           return;
         }
 
-      var removed_personas = new Queue<Persona> ();
-      var added_personas = new Queue<Persona> ();
+      this._handle_events ((owned) iter_del, (owned) iter_ins);
+    }
+
+  private async void _handle_events
+      (owned VariantIter iter_del, owned VariantIter iter_ins)
+    {
+      yield this._handle_delete_events ((owned) iter_del);
+      yield this._handle_insert_events ((owned) iter_ins);
+    }
 
+  private async void _handle_delete_events (owned VariantIter iter_del)
+    {
+      var removed_personas = new Queue<Persona> ();
       var nco_person_id =
           this._prefix_tracker_id.get (Trf.OntologyDefs.NCO_PERSON);
       var rdf_type_id = this._prefix_tracker_id.get (Trf.OntologyDefs.RDF_TYPE);
-
       Event e = Event ();
 
       while (iter_del.next
           ("(iiii)", &e.graph_id, &e.subject_id, &e.pred_id, &e.object_id))
         {
           var p_id = Trf.Persona.build_iid (this.id, e.subject_id.to_string ());
-          var persona = this._personas.lookup (p_id);
-          if (persona != null)
+          if (e.pred_id == rdf_type_id &&
+              e.object_id == nco_person_id)
             {
-              if (e.pred_id == rdf_type_id &&
-                  e.object_id == nco_person_id)
+              lock (this._personas)
                 {
-                  removed_personas.push_tail (persona);
-                  _personas.remove (persona.iid);
+                  var removed_p = this._personas.lookup (p_id);
+                  if (removed_p != null)
+                    {
+                      removed_personas.push_tail (removed_p);
+                      _personas.remove (removed_p.iid);
+                    }
                 }
-              else
+            }
+          else
+            {
+              var persona = this._personas.lookup (p_id);
+              if (persona != null)
                 {
-                  this._do_update (persona, e, false);
+                  yield this._do_update (persona, e, false);
                 }
             }
         }
 
+      if (removed_personas.length > 0)
+        {
+          this.personas_changed (null, removed_personas.head, null, null, 0);
+        }
+    }
+
+  private async void _handle_insert_events (owned VariantIter iter_ins)
+    {
+      var added_personas = new Queue<Persona> ();
+      Event e = Event ();
+
       while (iter_ins.next
           ("(iiii)", &e.graph_id, &e.subject_id, &e.pred_id, &e.object_id))
         {
           var subject_tracker_id = e.subject_id.to_string ();
           var p_id = Trf.Persona.build_iid (this.id, subject_tracker_id);
-          var persona = this._personas.lookup (p_id);
-          if (persona == null)
+          Trf.Persona persona;
+          lock (this._personas)
             {
-              persona = new Trf.Persona (this, subject_tracker_id);
-              this._personas.insert (persona.iid, persona);
-              added_personas.push_tail (persona);
+              persona = this._personas.lookup (p_id);
+              if (persona == null)
+                {
+                  persona = new Trf.Persona (this, subject_tracker_id);
+                  this._personas.insert (persona.iid, persona);
+                  added_personas.push_tail (persona);
+                }
             }
-          this._do_update (persona, e);
-        }
-
-      if (removed_personas.length > 0)
-        {
-          this.personas_changed (null, removed_personas.head, null, null, 0);
+          yield this._do_update (persona, e);
         }
 
       if (added_personas.length > 0)
@@ -621,10 +1080,11 @@ public class Trf.PersonaStore : Folks.PersonaStore
         }
     }
 
-  private async void _do_add_contacts (string query)
+  private async Queue<Persona> _do_add_contacts (string query)
     {
-      try {
-        var added_personas = new Queue<Persona> ();
+      var added_personas = new Queue<Persona> ();
+
+     try {
         Sparql.Cursor cursor = yield this._connection.query_async (query);
 
         while (cursor.next ())
@@ -647,9 +1107,11 @@ public class Trf.PersonaStore : Folks.PersonaStore
       } catch (GLib.Error e) {
         warning ("Couldn't perform queries: %s %s", query, e.message);
       }
+
+      return added_personas;
     }
 
-  private void _do_update (Persona p, Event e, bool adding = true)
+  private async void _do_update (Persona p, Event e, bool adding = true)
     {
       if (e.pred_id ==
           this._prefix_tracker_id.get (Trf.OntologyDefs.NCO_FULLNAME))
@@ -658,21 +1120,22 @@ public class Trf.PersonaStore : Folks.PersonaStore
           if (adding)
             {
               fullname =
-              this._get_property (e.subject_id, Trf.OntologyDefs.NCO_FULLNAME);
+                yield this._get_property (e.subject_id,
+                    Trf.OntologyDefs.NCO_FULLNAME);
             }
           p._update_full_name (fullname);
         }
       else if (e.pred_id ==
                this._prefix_tracker_id.get (Trf.OntologyDefs.NCO_NICKNAME))
         {
-          string nickname = "";
+          string alias = "";
           if (adding)
             {
-              nickname =
-                  this._get_property (
-                      e.subject_id, Trf.OntologyDefs.NCO_NICKNAME);
+              alias =
+                yield this._get_property (
+                    e.subject_id, Trf.OntologyDefs.NCO_NICKNAME);
             }
-          p._update_nickname (nickname);
+          p._update_alias (alias);
         }
       else if (e.pred_id ==
                this._prefix_tracker_id.get (Trf.OntologyDefs.NCO_FAMILY))
@@ -680,8 +1143,8 @@ public class Trf.PersonaStore : Folks.PersonaStore
           string family_name = "";
           if (adding)
             {
-              family_name = this._get_property
-                  (e.subject_id, Trf.OntologyDefs.NCO_FAMILY);
+              family_name = yield this._get_property (e.subject_id,
+                  Trf.OntologyDefs.NCO_FAMILY);
             }
           p._update_family_name (family_name);
         }
@@ -691,7 +1154,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
           string given_name = "";
           if (adding)
             {
-              given_name = this._get_property (
+              given_name = yield this._get_property (
                   e.subject_id, Trf.OntologyDefs.NCO_GIVEN);
             }
           p._update_given_name (given_name);
@@ -702,7 +1165,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
           string additional_name = "";
           if (adding)
             {
-              additional_name = this._get_property
+              additional_name = yield this._get_property
                   (e.subject_id, Trf.OntologyDefs.NCO_ADDITIONAL);
             }
           p._update_additional_names (additional_name);
@@ -713,7 +1176,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
           string suffix_name = "";
           if (adding)
             {
-              suffix_name = this._get_property
+              suffix_name = yield this._get_property
                   (e.subject_id, Trf.OntologyDefs.NCO_SUFFIX);
             }
           p._update_suffixes (suffix_name);
@@ -724,23 +1187,23 @@ public class Trf.PersonaStore : Folks.PersonaStore
           string prefix_name = "";
           if (adding)
             {
-              prefix_name = this._get_property
+              prefix_name = yield this._get_property
                   (e.subject_id, Trf.OntologyDefs.NCO_PREFIX);
             }
           p._update_prefixes (prefix_name);
         }
       else if (e.pred_id == this._prefix_tracker_id.get
-          ("nao:hasTag"))
+          (Trf.OntologyDefs.NAO_TAG))
         {
           if (e.object_id == this.get_favorite_id ())
             {
               if (adding)
                 {
-                  p.is_favourite = true;
+                  p._set_favourite (true);
                 }
               else
                 {
-                  p.is_favourite = false;
+                  p._set_favourite (false);
                 }
             }
         }
@@ -749,7 +1212,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
         {
           if (adding)
             {
-              var email = this._get_property (
+              var email = yield this._get_property (
                   e.object_id,
                   Trf.OntologyDefs.NCO_EMAIL_PROP,
                   Trf.OntologyDefs.NCO_EMAIL);
@@ -765,7 +1228,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
         {
           if (adding)
             {
-              var phone = this._get_property (
+              var phone = yield this._get_property (
                   e.object_id, Trf.OntologyDefs.NCO_PHONE_PROP,
                   Trf.OntologyDefs.NCO_PHONE);
               p._add_phone (phone, e.object_id.to_string ());
@@ -780,15 +1243,18 @@ public class Trf.PersonaStore : Folks.PersonaStore
         {
           if (adding)
             {
-              var affl_info = this._get_affl_info (e.subject_id.to_string (),
+              var affl_info =
+                yield this._get_affl_info (e.subject_id.to_string (),
                   e.object_id.to_string ());
 
               debug ("affl_info : %s", affl_info.to_string ());
 
               if (affl_info.im_tracker_id != null)
                 {
-                  p._add_im_address (affl_info.affl_tracker_id,
-                      affl_info.im_proto, affl_info.im_account_id);
+                  p._update_nickname (affl_info.im_nickname);
+                  if (affl_info.im_proto != null)
+                    p._add_im_address (affl_info.affl_tracker_id,
+                        affl_info.im_proto, affl_info.im_account_id);
                 }
 
               if (affl_info.affl_tracker_id != null)
@@ -838,7 +1304,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
           string bday = "";
           if (adding)
             {
-              bday = this._get_property (
+              bday = yield this._get_property (
                   e.subject_id, Trf.OntologyDefs.NCO_BIRTHDAY);
             }
           p._set_birthday (bday);
@@ -849,7 +1315,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
           string note = "";
           if (adding)
             {
-              note = this._get_property (
+              note = yield this._get_property (
                   e.subject_id, Trf.OntologyDefs.NCO_NOTE);
             }
           p._set_note (note);
@@ -872,18 +1338,17 @@ public class Trf.PersonaStore : Folks.PersonaStore
           string avatar_url = "";
           if (adding)
             {
-              avatar_url = this._get_property (e.object_id,
+              avatar_url = yield this._get_property (e.object_id,
                   Trf.OntologyDefs.NIE_URL, Trf.OntologyDefs.NFO_IMAGE);
             }
           p._set_avatar (avatar_url);
         }
     }
 
-  private string _get_property
+  private async string _get_property
       (int subject_tracker_id, string property,
        string subject_type = Trf.OntologyDefs.NCO_PERSON)
     {
-      string ret = "";
       const string query_template =
         "SELECT ?property WHERE" +
         " { ?p a %s ; " +
@@ -892,66 +1357,48 @@ public class Trf.PersonaStore : Folks.PersonaStore
 
       string query = query_template.printf (subject_type,
           property, subject_tracker_id);
-
-      try
-        {
-          Sparql.Cursor cursor = this._connection.query (query);
-          while (cursor.next ())
-            {
-              var prop = cursor.get_string (0);
-              if (prop != null)
-                {
-                  ret = prop.dup ();
-                }
-            }
-        }
-      catch (Tracker.Sparql.Error e1)
-        {
-          warning ("Couldn't fetch propery: %s %s", query, e1.message);
-        }
-      catch (GLib.Error e2)
-        {
-          warning ("Couldn't fetch property: %s %s", query, e2.message);
-        }
-
-      return ret;
+      return yield this._single_value_query (query);
     }
 
   /*
    * This should be kept in sync with Trf.AfflInfoFields
    */
-  private Trf.AfflInfo _get_affl_info (
+  private async Trf.AfflInfo _get_affl_info (
       string person_id, string affiliation_id)
     {
       Trf.AfflInfo affl_info = new Trf.AfflInfo ();
       const string query_template =
         "SELECT " +
         "tracker:id(?i) " +
-        "nco:imProtocol(?i) " +
-        "nco:imID(?i) " +
+        Trf.OntologyDefs.NCO_IMPROTOCOL  + "(?i) " +
+        Trf.OntologyDefs.NCO_IMID + "(?i) " +
         "tracker:id(?a) " +
-        "nco:role(?a) " +
-        "nco:org(?a) " +
-        "nco:pobox(?postal) " +
-        "nco:district(?postal) " +
-        "nco:county(?postal) " +
-        "nco:locality(?postal) " +
-        "nco:postalcode(?postal) " +
-        "nco:streetAddress(?postal) " +
-        "nco:addressLocation(?postal) " +
-        "nco:extendedAddress(?postal) " +
-        "nco:country(?postal) " +
-        "nco:region(?postal) " +
-        "nco:emailAddress(?e) " +
-        "nco:phoneNumber(?number) " +
-        "nco:websiteUrl(?a) " +
-        "nco:blogUrl(?a) " +
-        "nco:url(?a) " +
-        " WHERE { ?p a nco:PersonContact ; nco:hasAffiliation ?a . " +
-        " OPTIONAL { ?a nco:hasIMAddress ?i } . " +
-        " OPTIONAL { ?a nco:hasPostalAddress ?postal } . " +
-        " OPTIONAL { ?a nco:hasEmailAddress ?e } . " +
-        " OPTIONAL { ?a nco:hasPhoneNumber ?number }  " +
+        Trf.OntologyDefs.NCO_ROLE + "(?a) " +
+        Trf.OntologyDefs.NCO_ORG + "(?a) " +
+        Trf.OntologyDefs.NCO_POBOX + "(?postal) " +
+        Trf.OntologyDefs.NCO_DISTRICT + "(?postal) " +
+        Trf.OntologyDefs.NCO_COUNTY + "(?postal) " +
+        Trf.OntologyDefs.NCO_LOCALITY + "(?postal) " +
+        Trf.OntologyDefs.NCO_POSTALCODE + "(?postal) " +
+        Trf.OntologyDefs.NCO_STREET_ADDRESS + "(?postal) " +
+        Trf.OntologyDefs.NCO_ADDRESS_LOCATION + "(?postal) " +
+        Trf.OntologyDefs.NCO_EXTENDED_ADDRESS + "(?postal) " +
+        Trf.OntologyDefs.NCO_COUNTRY + "(?postal) " +
+        Trf.OntologyDefs.NCO_REGION + "(?postal) " +
+        Trf.OntologyDefs.NCO_EMAIL_PROP + "(?e) " +
+        Trf.OntologyDefs.NCO_PHONE_PROP + "(?number) " +
+        Trf.OntologyDefs.NCO_WEBSITE + "(?a) " +
+        Trf.OntologyDefs.NCO_BLOG + "(?a) " +
+        Trf.OntologyDefs.NCO_URL + "(?a) " +
+        Trf.OntologyDefs.NCO_IM_NICKNAME + "(?i) " +
+        "WHERE { "+
+        " ?p a " + Trf.OntologyDefs.NCO_PERSON  + " ; " +
+        Trf.OntologyDefs.NCO_HAS_AFFILIATION + " ?a . " +
+        " OPTIONAL { ?a " + Trf.OntologyDefs.NCO_HAS_IMADDRESS + " ?i } . " +
+        " OPTIONAL { ?a " + Trf.OntologyDefs.NCO_HAS_POSTAL_ADDRESS +
+        "               ?postal } . " +
+        " OPTIONAL { ?a " + Trf.OntologyDefs.NCO_HAS_EMAIL + " ?e } . " +
+        " OPTIONAL { ?a " + Trf.OntologyDefs.NCO_HAS_PHONE + " ?number }  " +
         " FILTER(tracker:id(?p) = %s" +
         " && tracker:id(?a) = %s" +
         " ) } ";
@@ -962,8 +1409,8 @@ public class Trf.PersonaStore : Folks.PersonaStore
 
       try
         {
-          Sparql.Cursor cursor = this._connection.query (query);
-          while (cursor.next ())
+          Sparql.Cursor cursor = yield this._connection.query_async (query);
+          while (yield cursor.next_async ())
             {
               affl_info.im_tracker_id = cursor.get_string
                   (Trf.AfflInfoFields.IM_TRACKER_ID).dup ();
@@ -971,6 +1418,9 @@ public class Trf.PersonaStore : Folks.PersonaStore
                   (Trf.AfflInfoFields.IM_PROTOCOL).dup ();
               affl_info.im_account_id = cursor.get_string
                   (Trf.AfflInfoFields.IM_ACCOUNT_ID).dup ();
+              affl_info.im_nickname = cursor.get_string
+                  (Trf.AfflInfoFields.IM_NICKNAME).dup ();
+
               affl_info.affl_tracker_id = cursor.get_string
                   (Trf.AfflInfoFields.AFFL_TRACKER_ID).dup ();
               affl_info.title = cursor.get_string
@@ -1025,4 +1475,741 @@ public class Trf.PersonaStore : Folks.PersonaStore
 
       return affl_info;
     }
+
+  private async string? _insert_persona (string query, string persona_var)
+    {
+      GLib.Variant variant;
+      string contact_urn = null;
+
+      try
+        {
+          debug ("_insert_persona: %s", query);
+          variant = yield this._connection.update_blank_async (query);
+
+          VariantIter iter1, iter2, iter3;
+          string anon_var = null;
+          iter1 = variant.iterator ();
+
+          while (iter1.next ("aa{ss}", out iter2))
+            {
+              if (iter2 == null)
+                continue;
+
+              while (iter2.next ("a{ss}", out iter3))
+                {
+                  if (iter3 == null)
+                    continue;
+
+                  while (iter3.next ("{ss}", out anon_var, out contact_urn))
+                    {
+                      /* The dictionary mapping blank node names to
+                       * IRIs doesn't have a fixed order so we need
+                       * check for the anon var corresponding to
+                       * nco:PersonContact.
+                       */
+                      if (anon_var == persona_var)
+                        return contact_urn;
+                    }
+                }
+            }
+        }
+      catch (GLib.Error e)
+        {
+          contact_urn = null;
+          warning ("Couldn't insert nco:PersonContact: %s", e.message);
+        }
+
+      return null;
+    }
+
+  private async string _single_value_query (string query)
+    {
+      Gee.HashSet<string> rows = yield this._multi_value_query (query);
+      foreach (var r in rows)
+        {
+          return r;
+        }
+      return "";
+    }
+
+  private async Gee.HashSet<string> _multi_value_query (string query)
+    {
+      Gee.HashSet<string> ret = new Gee.HashSet<string> ();
+
+      try
+        {
+          Sparql.Cursor cursor = yield this._connection.query_async (query);
+          while (cursor.next ())
+            {
+              var prop = cursor.get_string (0);
+              if (prop != null)
+                ret.add (prop);
+            }
+        }
+      catch (Tracker.Sparql.Error e1)
+        {
+          warning ("Couldn't run query: %s %s", query, e1.message);
+        }
+      catch (GLib.Error e2)
+        {
+          warning ("Couldn't run query: %s %s", query, e2.message);
+        }
+
+      return ret;
+    }
+
+  private async string _urn_from_tracker_id (string tracker_id)
+    {
+      const string query = "SELECT fn:concat('<', tracker:uri(%s), '>') " +
+        "WHERE {}";
+      return yield this._single_value_query (query.printf (tracker_id));
+    }
+
+  internal async void _set_alias (Trf.Persona persona, string alias)
+    {
+      const string query_t = "DELETE { "+
+        " ?p " + Trf.OntologyDefs.NCO_NICKNAME + " ?n  " +
+        "} " +
+        "WHERE { " +
+        " ?p a " + Trf.OntologyDefs.NCO_PERSON + " ; " +
+        Trf.OntologyDefs.NCO_NICKNAME + " ?n . " +
+        " FILTER(tracker:id(?p) = %s) " +
+        "} " +
+        "INSERT { " +
+        " ?p " + Trf.OntologyDefs.NCO_NICKNAME + " '%s' " +
+        "} " +
+        "WHERE { "+
+        " ?p a " + Trf.OntologyDefs.NCO_PERSON + " . " +
+        "FILTER (tracker:id(?p) = %s) " +
+        "} ";
+
+      string query = query_t.printf (persona.tracker_id (), alias,
+          persona.tracker_id ());
+
+      yield this._tracker_update (query, "change_alias");
+    }
+
+  internal async void _set_is_favourite (Folks.Persona persona,
+      bool is_favourite)
+    {
+      const string ins_q = "INSERT { " +
+        " ?p " + Trf.OntologyDefs.NAO_TAG + " " +
+        Trf.OntologyDefs.NAO_FAVORITE +
+        "} " +
+        "WHERE { " +
+        " ?p a " + Trf.OntologyDefs.NCO_PERSON +
+        " FILTER (tracker:id(?p) = %s) " +
+        "} ";
+      const string del_q = "DELETE { " +
+        " ?p " + Trf.OntologyDefs.NAO_TAG + " " +
+        Trf.OntologyDefs.NAO_FAVORITE + " " +
+       "} " +
+        "WHERE { " +
+        " ?p a " + Trf.OntologyDefs.NCO_PERSON +
+        " FILTER (tracker:id(?p) = %s) " +
+        "} ";
+      string query;
+
+      if (is_favourite)
+        {
+          query = ins_q.printf (((Trf.Persona) persona).tracker_id ());
+        }
+      else
+        {
+          query = del_q.printf (((Trf.Persona) persona).tracker_id ());
+        }
+
+      yield this._tracker_update (query, "change_is_favourite");
+    }
+
+  internal async void _set_phones (Folks.Persona persona,
+      owned GLib.List<FieldDetails> phone_numbers)
+    {
+      yield this._set_attrib (persona, (owned) phone_numbers,
+          Trf.Attrib.PHONES);
+    }
+
+  internal async void _set_emails (Folks.Persona persona,
+      owned GLib.List<FieldDetails> emails)
+    {
+      yield this._set_attrib (persona, (owned) emails,
+          Trf.Attrib.EMAILS);
+    }
+
+  internal async void _set_urls (Folks.Persona persona,
+      owned GLib.List<FieldDetails> urls)
+    {
+       yield this._set_attrib (persona, (owned) urls,
+          Trf.Attrib.URLS);
+    }
+
+  internal async void _set_im_addresses (Folks.Persona persona,
+      owned HashTable<string, LinkedHashSet<string>> im_addresses)
+    {
+      /* FIXME:
+       * - this conversion should go away once we've switched to use the
+       *   same data structure for each property that is a list of something.
+       *   See: https://bugzilla.gnome.org/show_bug.cgi?id=646079 */
+      GLib.List <FieldDetails> ims = new GLib.List <FieldDetails> ();
+      foreach (var proto in im_addresses.get_keys ())
+        {
+          var addrs = im_addresses.lookup (proto);
+          foreach (var a in addrs)
+            {
+              var fd = new FieldDetails (a);
+              fd.set_parameter ("proto", proto);
+              ims.prepend ((owned) fd);
+            }
+        }
+
+       yield this._set_attrib (persona, (owned) ims,
+          Trf.Attrib.IM_ADDRESSES);
+    }
+
+  internal async void _set_postal_addresses (Folks.Persona persona,
+      owned GLib.List<PostalAddress> postal_addresses)
+    {
+       yield this._set_attrib (persona, (owned) postal_addresses,
+          Trf.Attrib.POSTAL_ADDRESSES);
+    }
+
+  internal async void _set_roles (Folks.Persona persona,
+      owned Gee.HashSet<Role> roles)
+    {
+      const string del_t = "DELETE { " +
+        " ?p " + Trf.OntologyDefs.NCO_HAS_AFFILIATION + " ?a " +
+        "} " +
+        "WHERE { " +
+        " ?p a " + Trf.OntologyDefs.NCO_PERSON + "; " +
+        " " + Trf.OntologyDefs.NCO_HAS_AFFILIATION + " ?a . " +
+        " OPTIONAL { ?a " + Trf.OntologyDefs.NCO_ORG +  " ?o } . " +
+        " OPTIONAL { ?a " + Trf.OntologyDefs.NCO_ROLE + " ?r } . " +
+        " FILTER(tracker:id(?p) = %s) " +
+        "} ";
+
+      var p_id = ((Trf.Persona) persona).tracker_id ();
+      string del_q = del_t.printf (p_id);
+
+      var builder = new Tracker.Sparql.Builder.update ();
+      builder.insert_open (null);
+
+      int i = 0;
+      foreach (var r in roles)
+        {
+          string affl = "_:a%d".printf (i);
+
+          builder.subject (affl);
+          builder.predicate ("a");
+          builder.object (Trf.OntologyDefs.NCO_AFFILIATION);
+          builder.predicate (Trf.OntologyDefs.NCO_ROLE);
+          builder.object_string (r.title);
+          builder.predicate (Trf.OntologyDefs.NCO_ORG);
+          builder.object_string (r.organisation_name);
+          builder.subject ("?contact");
+          builder.predicate (Trf.OntologyDefs.NCO_HAS_AFFILIATION);
+          builder.object (affl);
+        }
+
+      builder.insert_close ();
+      builder.where_open ();
+      builder.subject ("?contact");
+      builder.predicate ("a");
+      builder.object (Trf.OntologyDefs.NCO_PERSON);
+      string filter = " FILTER(tracker:id(?contact) = %s) ".printf (p_id);
+      builder.append (filter);
+      builder.where_close ();
+
+      yield this._tracker_update (del_q + builder.result, "_set_roles");
+   }
+
+  internal async void _set_notes (Folks.Persona persona,
+      owned Gee.HashSet<Note> notes)
+    {
+      const string del_t = "DELETE { " +
+        "?p " + Trf.OntologyDefs.NCO_NOTE  + " ?n " +
+        "} " +
+        "WHERE {" +
+        " ?p a nco:PersonContact ; " +
+        Trf.OntologyDefs.NCO_NOTE + " ?n . " +
+        " FILTER(tracker:id(?p) = %s)" +
+        "}";
+
+      var p_id = ((Trf.Persona) persona).tracker_id ();
+      string del_q = del_t.printf (p_id);
+
+      var builder = new Tracker.Sparql.Builder.update ();
+      builder.insert_open (null);
+
+      foreach (var n in notes)
+        {
+          builder.subject ("?contact");
+          builder.predicate (Trf.OntologyDefs.NCO_NOTE);
+          builder.object_string (n.content);
+        }
+
+      builder.insert_close ();
+      builder.where_open ();
+      builder.subject ("?contact");
+      builder.predicate ("a");
+      builder.object (Trf.OntologyDefs.NCO_PERSON);
+      string filter = " FILTER(tracker:id(?contact) = %s) ".printf (p_id);
+      builder.append (filter);
+      builder.where_close ();
+
+      yield this._tracker_update (del_q + builder.result, "_set_notes");
+    }
+
+  internal async void _set_birthday (Folks.Persona persona,
+      owned DateTime bday)
+    {
+      const string q_t = "DELETE { " +
+         " ?p " + Trf.OntologyDefs.NCO_BIRTHDAY + " ?b " +
+         "} " +
+         "WHERE { " +
+         " ?p a " + Trf.OntologyDefs.NCO_PERSON + "; " +
+         Trf.OntologyDefs.NCO_BIRTHDAY + " ?b . " +
+         " FILTER (tracker:id(?p) = %s ) " +
+         "} " +
+         "INSERT { " +
+         " ?p " + Trf.OntologyDefs.NCO_BIRTHDAY + " '%s' " +
+         "} " +
+         "WHERE { " +
+         " ?p a " + Trf.OntologyDefs.NCO_PERSON + " . " +
+         " FILTER (tracker:id(?p) = %s) " +
+         "} ";
+
+      var p_id = ((Trf.Persona) persona).tracker_id ();
+      TimeVal tv;
+      bday.to_timeval (out tv);
+      string query = q_t.printf (p_id, tv.to_iso8601 (), p_id);
+
+      yield this._tracker_update (query, "_set_birthday");
+    }
+
+  internal async void _set_gender (Folks.Persona persona,
+      owned Gender gender)
+    {
+      const string del_t = "DELETE { " +
+        " ?p " + Trf.OntologyDefs.NCO_GENDER + " ?g " +
+        "} " +
+        "WHERE { " +
+        " ?p a " + Trf.OntologyDefs.NCO_PERSON + " ; " +
+        Trf.OntologyDefs.NCO_GENDER + " ?g . " +
+        " FILTER (tracker:id(?p) = %s) " +
+        "} ";
+      const string ins_t = "INSERT { " +
+        " ?p " + Trf.OntologyDefs.NCO_GENDER + " %s " +
+        "} " +
+        "WHERE { " +
+        " ?p a " + Trf.OntologyDefs.NCO_PERSON +  " . " +
+        " FILTER (tracker:id(?p) = %s) " +
+        "} ";
+
+      var p_id = ((Trf.Persona) persona).tracker_id ();
+      string query;
+
+      if (gender == Gender.UNSPECIFIED)
+        {
+          query = del_t.printf (p_id);
+        }
+      else
+        {
+          string gender_urn;
+
+          if (gender == Gender.MALE)
+            gender_urn = Trf.OntologyDefs.NCO_URL_PREFIX + "nco#gender-male>";
+          else
+            gender_urn = Trf.OntologyDefs.NCO_URL_PREFIX + "nco#gender-female>";
+
+          query = del_t.printf (p_id) + ins_t.printf (gender_urn, p_id);
+        }
+
+      yield this._tracker_update (query, "_set_gender");
+    }
+
+  internal async void _set_avatar (Folks.Persona persona,
+      File avatar)
+    {
+      const string query_t = "DELETE {" +
+        " ?c " + Trf.OntologyDefs.NCO_PHOTO  + " ?p " +
+        " } " +
+        "WHERE { " +
+        " ?c a " + Trf.OntologyDefs.NCO_PERSON  + " ; " +
+        Trf.OntologyDefs.NCO_PHOTO + " ?p . " +
+        " FILTER(tracker:id(?c) = %s) " +
+        "} " +
+        "INSERT { " +
+        " _:i a " + Trf.OntologyDefs.NFO_IMAGE  + ", " +
+        Trf.OntologyDefs.NIE_DATAOBJECT + " ; " +
+        Trf.OntologyDefs.NIE_URL + " '%s' . " +
+        " ?c " + Trf.OntologyDefs.NCO_PHOTO + " _:i " +
+        "} " +
+        "WHERE { " +
+        " ?c a nco:PersonContact . " +
+        " FILTER(tracker:id(?c) = %s) " +
+        "}";
+
+      var p_id = ((Trf.Persona) persona).tracker_id ();
+
+      var image_urn = yield this._get_property (int.parse (p_id),
+          Trf.OntologyDefs.NCO_PHOTO);
+      if (image_urn != "")
+        this._delete_resource ("<%s>".printf (image_urn));
+
+      string query = query_t.printf (p_id, avatar.get_uri (), p_id);
+      yield this._tracker_update (query, "_set_avatar");
+    }
+
+  internal async void _set_structured_name (Folks.Persona persona,
+      StructuredName sname)
+    {
+      const string query_t = "DELETE { " +
+        " ?p " + Trf.OntologyDefs.NCO_FAMILY + " ?family . " +
+        " ?p " + Trf.OntologyDefs.NCO_GIVEN + " ?given . " +
+        " ?p " + Trf.OntologyDefs.NCO_ADDITIONAL + " ?adi . " +
+        " ?p " + Trf.OntologyDefs.NCO_PREFIX + " ?prefix . " +
+        " ?p " + Trf.OntologyDefs.NCO_SUFFIX + " ?suffix " +
+        "} " +
+        "WHERE { " +
+        " ?p a " + Trf.OntologyDefs.NCO_PERSON + " .  " +
+        " OPTIONAL { ?p " + Trf.OntologyDefs.NCO_FAMILY + " ?family } . " +
+        " OPTIONAL { ?p " + Trf.OntologyDefs.NCO_GIVEN + " ?given } . " +
+        " OPTIONAL { ?p " + Trf.OntologyDefs.NCO_ADDITIONAL + " ?adi } . " +
+        " OPTIONAL { ?p " + Trf.OntologyDefs.NCO_PREFIX + " ?prefix } . " +
+        " OPTIONAL { ?p " + Trf.OntologyDefs.NCO_SUFFIX + " ?suffix } . " +
+        " FILTER (tracker:id(?p) = %s) " +
+        "} " +
+        "INSERT { " +
+        " ?p " + Trf.OntologyDefs.NCO_FAMILY + " '%s'; " +
+        " " + Trf.OntologyDefs.NCO_GIVEN + " '%s'; " +
+        " " + Trf.OntologyDefs.NCO_ADDITIONAL + " '%s'; " +
+        " " + Trf.OntologyDefs.NCO_PREFIX + " '%s'; " +
+        " " + Trf.OntologyDefs.NCO_SUFFIX + " '%s' " +
+        " } " +
+        "WHERE { " +
+        " ?p a " + Trf.OntologyDefs.NCO_PERSON + " . " +
+        " FILTER (tracker:id(?p) = %s) " +
+        "} ";
+
+      var p_id = ((Trf.Persona) persona).tracker_id ();
+      string query = query_t.printf (p_id, sname.family_name, sname.given_name,
+          sname.additional_names, sname.prefixes, sname.suffixes, p_id);
+      yield this._tracker_update (query, "_set_structured_name");
+    }
+
+  internal async void _set_full_name  (Folks.Persona persona,
+      string full_name)
+    {
+      const string query_t = "DELETE { " +
+        " ?p " + Trf.OntologyDefs.NCO_FULLNAME + " ?fn " +
+        "} " +
+        "WHERE { " +
+        " ?p a " + Trf.OntologyDefs.NCO_PERSON + " .  " +
+        " OPTIONAL { ?p " + Trf.OntologyDefs.NCO_FULLNAME + " ?fn } . " +
+        " FILTER (tracker:id(?p) = %s) " +
+        "} " +
+        "INSERT { " +
+        " ?p " + Trf.OntologyDefs.NCO_FULLNAME + " '%s' " +
+        "} " +
+        "WHERE { " +
+        " ?p a " + Trf.OntologyDefs.NCO_PERSON + " . " +
+        " FILTER (tracker:id(?p) = %s) " +
+        "} ";
+
+      var p_id = ((Trf.Persona) persona).tracker_id ();
+      string query = query_t.printf (p_id, full_name, p_id);
+      yield this._tracker_update (query, "_set_full_name");
+    }
+
+  /* NOTE:
+   * - first we nuke old attribs
+   * - we create new affls with the new attribs
+   */
+  private async void _set_attrib (Folks.Persona persona,
+      owned GLib.List<Object> attribs, Trf.Attrib what)
+    {
+      var p_id = ((Trf.Persona) persona).tracker_id ();
+
+      unowned string? related_attrib = null;
+      unowned string? related_prop = null;
+      unowned string? related_prop_2 = null;
+      unowned string? related_connection = null;
+
+      switch (what)
+        {
+          case Trf.Attrib.PHONES:
+            related_attrib = Trf.OntologyDefs.NCO_PHONE;
+            related_prop = Trf.OntologyDefs.NCO_PHONE_PROP;
+            related_connection = Trf.OntologyDefs.NCO_HAS_PHONE;
+            yield this._remove_attributes_from_persona (persona,
+                _REMOVE_PHONES);
+            break;
+          case Trf.Attrib.EMAILS:
+            related_attrib = Trf.OntologyDefs.NCO_EMAIL;
+            related_prop = Trf.OntologyDefs.NCO_EMAIL_PROP;
+            related_connection = Trf.OntologyDefs.NCO_HAS_EMAIL;
+            yield this._remove_attributes_from_persona (persona,
+                _REMOVE_EMAILS);
+            break;
+          case Trf.Attrib.URLS:
+            related_attrib = Trf.OntologyDefs.NCO_URL;
+            related_connection = Trf.OntologyDefs.NCO_URL;
+            break;
+          case Trf.Attrib.IM_ADDRESSES:
+            related_attrib = Trf.OntologyDefs.NCO_IMADDRESS;
+            related_prop = Trf.OntologyDefs.NCO_IMID;
+            related_prop_2 = Trf.OntologyDefs.NCO_IMPROTOCOL;
+            related_connection = Trf.OntologyDefs.NCO_HAS_IMADDRESS;
+            yield this._remove_attributes_from_persona (persona,
+                _REMOVE_IM_ADDRS);
+            break;
+          case Trf.Attrib.POSTAL_ADDRESSES:
+            related_attrib = Trf.OntologyDefs.NCO_POSTAL_ADDRESS;
+            related_connection = Trf.OntologyDefs.NCO_HAS_POSTAL_ADDRESS;
+            yield this._remove_attributes_from_persona (persona,
+                _REMOVE_POSTALS);
+            break;
+        }
+
+      var builder = new Tracker.Sparql.Builder.update ();
+      builder.insert_open (null);
+      int i = 0;
+      foreach (var p in attribs)
+        {
+          FieldDetails fd = null;
+          PostalAddress pa = null;
+
+          string affl = "_:a%d".printf (i);
+          string attr;
+
+          if (what == Trf.Attrib.POSTAL_ADDRESSES)
+            {
+              pa = (PostalAddress) p;
+              attr = "_:p%d".printf (i);
+              builder.subject (attr);
+              builder.predicate ("a");
+              builder.object (related_attrib);
+              builder.predicate (Trf.OntologyDefs.NCO_POBOX);
+              builder.object_string (pa.po_box);
+              builder.predicate (Trf.OntologyDefs.NCO_LOCALITY);
+              builder.object_string (pa.locality);
+              builder.predicate (Trf.OntologyDefs.NCO_POSTALCODE);
+              builder.object_string (pa.postal_code);
+              builder.predicate (Trf.OntologyDefs.NCO_STREET_ADDRESS);
+              builder.object_string (pa.street);
+              builder.predicate (Trf.OntologyDefs.NCO_EXTENDED_ADDRESS);
+              builder.object_string (pa.extension);
+              builder.predicate (Trf.OntologyDefs.NCO_COUNTRY);
+              builder.object_string (pa.country);
+              builder.predicate (Trf.OntologyDefs.NCO_REGION);
+              builder.object_string (pa.region);
+            }
+          else if (what == Trf.Attrib.URLS)
+            {
+              fd = (FieldDetails) p;
+              unowned List<string> type_p = fd.get_parameter_values ("type");
+              if (type_p.length () > 0)
+                {
+                  if (type_p.nth_data (0) == "blog")
+                    {
+                      related_connection = Trf.OntologyDefs.NCO_BLOG;
+                    }
+                  else if (type_p.nth_data (0) == "website")
+                    {
+                      related_connection = Trf.OntologyDefs.NCO_WEBSITE;
+                    }
+                }
+              attr = "'%s'".printf (fd.value);
+            }
+          else
+            {
+              fd = (FieldDetails) p;
+              attr = "_:p%d".printf (i);
+              builder.subject (attr);
+              builder.predicate ("a");
+              builder.object (related_attrib);
+              builder.predicate (related_prop);
+              builder.object_string (fd.value);
+
+              if (what == Trf.Attrib.IM_ADDRESSES)
+                {
+                  builder.predicate (related_prop_2);
+                  unowned List<string> im_params =
+                      fd.get_parameter_values ("proto");
+                  builder.object_string (im_params.nth_data (0));
+                }
+            }
+
+          builder.subject (affl);
+          builder.predicate ("a");
+          builder.object (Trf.OntologyDefs.NCO_AFFILIATION);
+          builder.predicate (related_connection);
+          builder.object (attr);
+          builder.subject ("?contact");
+          builder.predicate (Trf.OntologyDefs.NCO_HAS_AFFILIATION);
+          builder.object (affl);
+
+          i++;
+        }
+      builder.insert_close ();
+      builder.where_open ();
+      builder.subject ("?contact");
+      builder.predicate ("a");
+      builder.object (Trf.OntologyDefs.NCO_PERSON);
+      string filter = " FILTER(tracker:id(?contact) = %s) ".printf (p_id);
+      builder.append (filter);
+      builder.where_close ();
+
+      yield this._tracker_update (builder.result, "set_attrib");
+    }
+
+  private async bool _tracker_update (string query, string caller)
+    {
+      bool ret = false;
+
+      debug ("%s: %s", caller, query);
+
+      try
+        {
+          yield this._connection.update_async (query);
+          ret = true;
+        }
+      catch (Tracker.Sparql.Error e1)
+        {
+          warning ("[%s] SPARQL syntax error: %s. Query: %s",
+              caller, e1.message, query);
+        }
+      catch (GLib.IOError e2)
+        {
+          warning ("[%s] IO error: %s",
+              caller, e2.message);
+        }
+      catch (GLib.DBusError e3)
+        {
+          warning ("[%s] DBus error: %s",
+              caller, e3.message);
+        }
+
+      return ret;
+    }
+
+  private async Gee.HashSet<string> _affiliations_from_persona (string urn)
+    {
+      return yield this._linked_resources (urn, Trf.OntologyDefs.NCO_PERSON,
+          Trf.OntologyDefs.NCO_HAS_AFFILIATION);
+    }
+
+  private async Gee.HashSet<string> _phones_from_affiliation (string affl)
+    {
+      return yield this._linked_resources (affl,
+          Trf.OntologyDefs.NCO_AFFILIATION,
+          Trf.OntologyDefs.NCO_HAS_PHONE);
+    }
+
+  private async Gee.HashSet<string>  _postals_from_affiliation (string affl)
+    {
+      return yield this._linked_resources (affl,
+          Trf.OntologyDefs.NCO_AFFILIATION,
+          Trf.OntologyDefs.NCO_HAS_POSTAL_ADDRESS);
+    }
+
+  private async Gee.HashSet<string> _imaddrs_from_affiliation  (string affl)
+    {
+      return yield this._linked_resources (affl,
+          Trf.OntologyDefs.NCO_AFFILIATION,
+          Trf.OntologyDefs.NCO_HAS_IMADDRESS);
+    }
+
+  private async Gee.HashSet<string> _emails_from_affiliation (string affl)
+    {
+      return yield this._linked_resources (affl,
+          Trf.OntologyDefs.NCO_AFFILIATION,
+          Trf.OntologyDefs.NCO_HAS_EMAIL);
+    }
+
+  /**
+   * Retrieve the list of linked resources of a given subject
+   *
+   * @param resource          the urn of the resource in <urn> format
+   * @return number of resources linking to this resource
+   */
+  private async int _resource_usage_count (string resource)
+    {
+      const string query_t = "SELECT " +
+        " count(?s) " +
+        "WHERE { " +
+        " %s a rdfs:Resource . " +
+        " ?s ?p %s } ";
+
+      var query = query_t.printf (resource, resource);
+      var result = yield this._single_value_query (query);
+      return int.parse (result);
+    }
+
+  /*
+   * NOTE:
+   *
+   * We asume that the caller is holding a link to the resource,
+   * so if _resource_usage_count () == 1 it means no one else
+   * (beside the caller) is linking to the resource.
+   *
+   * This means that _delete_resource shold be called before
+   * removing the resources that hold a link to it (which also
+   * makes sense from the signaling perspective).
+   */
+  private async bool _delete_resource (string resource_urn,
+      bool check_count = true)
+    {
+      bool deleted = false;
+      var query_t = " DELETE { " +
+        " %s a rdfs:Resource " +
+        "} " +
+        "WHERE { " +
+        " %s a rdfs:Resource " +
+        "} ";
+
+      var query = query_t.printf (resource_urn, resource_urn);
+      if (check_count)
+        {
+          int count = yield this._resource_usage_count (resource_urn);
+          if (count == 1)
+            {
+              deleted = yield this._tracker_update (query, "_delete_resource");
+            }
+        }
+      else
+        {
+          deleted = yield this._tracker_update (query, "_delete_resource");
+        }
+
+      return deleted;
+    }
+
+  /**
+   * Retrieve the list of linked resources of a given subject
+   *
+   * @param urn               the urn of the subject in <urn> format
+   * @param subject_type      i.e: nco:Person, nco:Affiliation, etc
+   * @param linking_predicate i.e.: nco:hasAffiliation
+   * @return a list of linked resources (in <urn> format)
+   */
+  private async Gee.HashSet<string> _linked_resources (string urn,
+      string subject_type, string linking_predicate)
+    {
+      string query_t = "SELECT " +
+        " fn:concat('<',?linkedr,'>')  " +
+        "WHERE { " +
+        " %s a %s; " +
+        " %s ?linkedr " +
+        "} ";
+
+      var query = query_t.printf (urn, subject_type, linking_predicate);
+      return yield this._multi_value_query (query);
+    }
+
+  private async string _urn_from_persona (Folks.Persona persona)
+    {
+      var id = ((Trf.Persona) persona).tracker_id ();
+      return yield this._urn_from_tracker_id (id);
+    }
 }
diff --git a/backends/tracker/lib/trf-persona.vala b/backends/tracker/lib/trf-persona.vala
index 47b5a2d..29effd8 100644
--- a/backends/tracker/lib/trf-persona.vala
+++ b/backends/tracker/lib/trf-persona.vala
@@ -30,6 +30,7 @@ using Tracker.Sparql;
  * A persona subclass which represents a single nco:Contact.
  */
 public class Trf.Persona : Folks.Persona,
+    AliasDetails,
     AvatarDetails,
     BirthdayDetails,
     EmailDetails,
@@ -43,6 +44,8 @@ public class Trf.Persona : Folks.Persona,
     RoleDetails,
     UrlDetails
 {
+  private string _alias;
+  private bool _is_favourite;
   private const string[] _linkable_properties =
       {"im-addresses", "email-addresses"};
   private GLib.List<FieldDetails> _phone_numbers;
@@ -51,19 +54,41 @@ public class Trf.Persona : Folks.Persona,
   private string _tracker_id;
 
   /**
+   * An alias for the Persona.
+   *
+   * See { link Folks.AliasDetails.alias}.
+   */
+  public string alias
+    {
+      get { return this._alias; }
+
+      set
+        {
+          if (this._alias == value)
+            return;
+          this._alias = value;
+          this.notify_property ("alias");
+          ((Trf.PersonaStore) this.store)._set_alias (this, value);
+        }
+    }
+
+  /**
    * { inheritDoc}
    */
   public GLib.List<FieldDetails> phone_numbers
     {
       get { return this._phone_numbers; }
-      private set
+      public set
         {
-          this._phone_numbers = new GLib.List<FieldDetails> ();
-          foreach (unowned FieldDetails ps in value)
+          var _temp = new GLib.List<FieldDetails> ();
+          foreach (unowned FieldDetails e in value)
             {
-              this._phone_numbers.prepend (ps);
+              _temp.prepend (e);
             }
-          this._phone_numbers.reverse ();
+          _temp.reverse ();
+
+          ((Trf.PersonaStore) this.store)._set_phones (this,
+              (owned) _temp);
         }
     }
 
@@ -73,14 +98,17 @@ public class Trf.Persona : Folks.Persona,
   public GLib.List<FieldDetails> email_addresses
     {
       get { return this._email_addresses; }
-      private set
+      public set
         {
-          this._email_addresses = new GLib.List<FieldDetails> ();
+          var _temp = new GLib.List<FieldDetails> ();
           foreach (unowned FieldDetails e in value)
             {
-              this._email_addresses.prepend (e);
+              _temp.prepend (e);
             }
-          this._email_addresses.reverse ();
+          _temp.reverse ();
+
+          ((Trf.PersonaStore) this.store)._set_emails (this,
+              (owned) _temp);
         }
     }
 
@@ -92,22 +120,46 @@ public class Trf.Persona : Folks.Persona,
       get { return this._linkable_properties; }
     }
 
+  private File _avatar;
   /**
    * An avatar for the Persona.
    *
    * See { link Folks.Avatar.avatar}.
    */
-  public File avatar { get; set; }
+  public File avatar
+    {
+      get { return this._avatar; }
+      public set
+        {
+          ((Trf.PersonaStore) this.store)._set_avatar (this, value);
+        }
+    }
 
+  private StructuredName _structured_name;
   /**
    * { inheritDoc}
    */
-  public StructuredName structured_name { get; private set; }
+  public StructuredName structured_name
+    {
+      get { return this._structured_name; }
+      public set
+        {
+          ((Trf.PersonaStore) this.store)._set_structured_name (this, value);
+        }
+    }
 
+  private string _full_name;
   /**
    * { inheritDoc}
    */
-  public string full_name { get; private set; }
+  public string full_name
+    {
+      get { return this._full_name; }
+      public set
+        {
+          ((Trf.PersonaStore) this.store)._set_full_name (this, value);
+        }
+    }
 
   private string _nickname;
   /**
@@ -115,16 +167,36 @@ public class Trf.Persona : Folks.Persona,
    */
   public string nickname { get { return this._nickname; } }
 
+  private Gender _gender;
   /**
    * { inheritDoc}
    */
-  public Gender gender { get; private set; }
+  public Gender gender
+    {
+      get { return this._gender; }
+      public set
+        {
+          ((Trf.PersonaStore) this.store)._set_gender (this, value);
+        }
+    }
 
   private HashSet<Role> _roles =
       new HashSet<Role> ((GLib.HashFunc) Role.hash,
       (GLib.EqualFunc) Role.equal);
 
-  public DateTime birthday { get; set; }
+
+  private DateTime _birthday;
+  /**
+   * { inheritDoc}
+   */
+  public DateTime birthday
+    {
+      get { return this._birthday; }
+      public set
+        {
+          ((Trf.PersonaStore) this.store)._set_birthday (this, value);
+        }
+    }
 
   public string calendar_event_id { get; set; }
 
@@ -134,10 +206,9 @@ public class Trf.Persona : Folks.Persona,
   public HashSet<Role> roles
     {
       get { return this._roles; }
-      private set
+      public set
         {
-          this._roles = value;
-          this.notify_property ("roles");
+          ((Trf.PersonaStore) this.store)._set_roles (this, value);
         }
     }
 
@@ -153,8 +224,7 @@ public class Trf.Persona : Folks.Persona,
       get { return this._notes; }
       private set
         {
-          this._notes = notes;
-          this.notify_property ("notes");
+          ((Trf.PersonaStore) this.store)._set_notes (this, value);
         }
     }
 
@@ -165,12 +235,15 @@ public class Trf.Persona : Folks.Persona,
   public GLib.List<FieldDetails> urls
     {
       get { return this._urls; }
-      private set
+      public set
         {
-          this._urls = new GLib.List<FieldDetails> ();
-          foreach (unowned FieldDetails ps in value)
-            this._urls.prepend (ps);
-          this._urls.reverse ();
+          var _temp = new GLib.List<FieldDetails> ();
+          foreach (unowned FieldDetails e in value)
+              _temp.prepend (e);
+          _temp.reverse ();
+
+          ((Trf.PersonaStore) this.store)._set_urls (this,
+              (owned) _temp);
         }
     }
 
@@ -184,11 +257,11 @@ public class Trf.Persona : Folks.Persona,
       get { return this._postal_addresses; }
       private set
         {
-          this._postal_addresses = new GLib.List<PostalAddress> ();
-          foreach (PostalAddress pa in value)
-            this._postal_addresses.prepend (pa);
-          this._postal_addresses.reverse ();
-          this.notify_property ("postal-addresses");
+          var _temp = new GLib.List<PostalAddress> ();
+          foreach (unowned PostalAddress e in value)
+              _temp.prepend (e);
+          ((Trf.PersonaStore) this.store)._set_postal_addresses (this,
+              (owned) _temp);
         }
     }
 
@@ -203,13 +276,32 @@ public class Trf.Persona : Folks.Persona,
   public HashTable<string, LinkedHashSet<string>> im_addresses
     {
       get { return this._im_addresses; }
-      private set {}
+      public set
+        {
+          ((Trf.PersonaStore) this.store)._set_im_addresses (this,
+              value);
+        }
     }
 
   /**
    * Whether this contact is a user-defined favourite.
    */
-  public bool is_favourite { get; set; }
+  public bool is_favourite
+      {
+        get { return this._is_favourite; }
+
+        set
+          {
+            if (this._is_favourite == value)
+              return;
+
+            /* Note:
+             * this property will be set (and notified)
+             * once we receive a notification event from Tracker
+             */
+            ((Trf.PersonaStore) this.store)._set_is_favourite (this, value);
+          }
+      }
 
   /**
    * Build a IID.
@@ -249,18 +341,18 @@ public class Trf.Persona : Folks.Persona,
             }
         }
 
-      debug ("Creating new Trf.Persona with id '%s'", fullname);
+      debug ("Creating new Trf.Persona with iid '%s'", iid);
 
       Object (display_id: fullname,
               uid: uid,
               iid: iid,
               store: store,
-              gender: Gender.UNSPECIFIED,
               is_user: is_user);
 
-      this.full_name = fullname;
+      this._gender = Gender.UNSPECIFIED;
+      this._full_name = fullname;
       this._tracker_id = tracker_id;
-      this.structured_name = new StructuredName (null, null, null, null, null);
+      this._structured_name = new StructuredName (null, null, null, null, null);
 
       if (cursor != null)
         {
@@ -269,6 +361,11 @@ public class Trf.Persona : Folks.Persona,
         }
     }
 
+  internal string tracker_id ()
+    {
+      return this._tracker_id;
+    }
+
   ~Persona ()
     {
       debug ("Destroying Trf.Persona '%s': %p", this.uid, this);
@@ -278,7 +375,8 @@ public class Trf.Persona : Folks.Persona,
     {
       if (fn != null && this.full_name != fn)
         {
-          this.full_name = fn;
+          this._full_name = fn;
+          this.notify_property ("full-name");
         }
     }
 
@@ -291,11 +389,21 @@ public class Trf.Persona : Folks.Persona,
         }
     }
 
+  internal void _update_alias (string? alias)
+    {
+      if (alias != null && this._alias != alias)
+        {
+          this._alias = alias;
+          this.notify_property ("alias");
+        }
+    }
+
   internal void _update_family_name (string? family_name)
     {
       if (family_name != null)
         {
-          this.structured_name.family_name = family_name;
+          this._structured_name.family_name = family_name;
+          this.notify_property ("structured-name");
         }
     }
 
@@ -303,7 +411,8 @@ public class Trf.Persona : Folks.Persona,
     {
       if (given_name != null)
         {
-          this.structured_name.given_name = given_name;
+          this._structured_name.given_name = given_name;
+          this.notify_property ("structured-name");
         }
     }
 
@@ -311,7 +420,8 @@ public class Trf.Persona : Folks.Persona,
     {
       if (additional_names != null)
         {
-          this.structured_name.additional_names = additional_names;
+          this._structured_name.additional_names = additional_names;
+          this.notify_property ("structured-name");
         }
     }
 
@@ -319,7 +429,8 @@ public class Trf.Persona : Folks.Persona,
     {
       if (prefixes != null)
         {
-          this.structured_name.prefixes = prefixes;
+          this._structured_name.prefixes = prefixes;
+          this.notify_property ("structured-name");
         }
     }
 
@@ -327,7 +438,8 @@ public class Trf.Persona : Folks.Persona,
     {
       if (suffixes != null)
         {
-          this.structured_name.suffixes = suffixes;
+          this._structured_name.suffixes = suffixes;
+          this.notify_property ("structured-name");
         }
     }
 
@@ -394,7 +506,7 @@ public class Trf.Persona : Folks.Persona,
         }
 
       postal_addresses.reverse ();
-      this.postal_addresses = (owned) postal_addresses;
+      this._postal_addresses = (owned) postal_addresses;
     }
 
   internal bool _add_postal_address (PostalAddress postal_address)
@@ -443,7 +555,7 @@ public class Trf.Persona : Folks.Persona,
     {
       if (gender_id == 0)
         {
-          this.gender = Gender.UNSPECIFIED;
+          this._gender = Gender.UNSPECIFIED;
         }
       else
         {
@@ -451,13 +563,15 @@ public class Trf.Persona : Folks.Persona,
 
           if (gender_id == trf_store.get_gender_male_id ())
             {
-              this.gender = Gender.MALE;
+              this._gender = Gender.MALE;
             }
           else if (gender_id == trf_store.get_gender_female_id ())
             {
-              this.gender = Gender.FEMALE;
+              this._gender = Gender.FEMALE;
             }
         }
+
+      this.notify_property ("gender");
     }
 
   private void _update_note ()
@@ -492,13 +606,15 @@ public class Trf.Persona : Folks.Persona,
         {
           TimeVal t = TimeVal ();
           t.from_iso8601 (birthday);
-          this.birthday = new DateTime.from_timeval_utc (t);
+          this._birthday = new DateTime.from_timeval_utc (t);
+          this.notify_property ("birthday");
         }
       else
         {
-          if (this.birthday != null)
+          if (this._birthday != null)
             {
-              this.birthday = null;
+              this._birthday = null;
+              this.notify_property ("birthday");
             }
         }
     }
@@ -564,8 +680,8 @@ public class Trf.Persona : Folks.Persona,
       string fullname = this._cursor.get_string (Trf.Fields.FULL_NAME).dup ();
       this._update_full_name (fullname);
 
-      string nickname = this._cursor.get_string (Trf.Fields.NICKNAME).dup ();
-      this._update_nickname (nickname);
+      string alias = this._cursor.get_string (Trf.Fields.ALIAS).dup ();
+      this._update_alias (alias);
 
       string family_name = this._cursor.get_string (
           Trf.Fields.FAMILY_NAME).dup ();
@@ -600,7 +716,8 @@ public class Trf.Persona : Folks.Persona,
         {
           _avatar = File.new_for_uri (avatar_url);
         }
-      this.avatar = _avatar;
+      this._avatar = _avatar;
+      this.notify_property ("avatar");
       return true;
     }
 
@@ -624,7 +741,9 @@ public class Trf.Persona : Folks.Persona,
           var tracker_id = addr_info[Trf.IMFields.TRACKER_ID];
           var proto = addr_info[Trf.IMFields.PROTO];
           var account_id = addr_info[Trf.IMFields.ID];
+          var nickname = addr_info[Trf.IMFields.IM_NICKNAME];
 
+          this._update_nickname (nickname);
           this._add_im_address (tracker_id, proto, account_id, false);
         }
 
@@ -737,7 +856,7 @@ public class Trf.Persona : Folks.Persona,
         }
 
       phones.reverse ();
-      this.phone_numbers = phones;
+      this._phone_numbers = (owned) phones;
     }
 
   internal bool _add_phone (string phone, string tracker_id)
@@ -865,7 +984,7 @@ public class Trf.Persona : Folks.Persona,
         }
 
       email_addresses.reverse ();
-      this.email_addresses = email_addresses;
+      this._email_addresses = (owned) email_addresses;
     }
 
   private void _update_urls ()
@@ -910,7 +1029,7 @@ public class Trf.Persona : Folks.Persona,
         }
 
       urls.reverse ();
-      this.urls = urls;
+      this._urls = (owned) urls;
     }
 
   internal bool _add_url (string url, string tracker_id, string type = "")
@@ -964,7 +1083,7 @@ public class Trf.Persona : Folks.Persona,
     {
       var favourite = this._cursor.get_string (Trf.Fields.FAVOURITE).dup ();
 
-      this.is_favourite = false;
+      this._is_favourite = false;
 
       if (favourite != null)
         {
@@ -974,9 +1093,21 @@ public class Trf.Persona : Folks.Persona,
             {
               if (int.parse (tag) == favorite_tracker_id)
                 {
-                  this.is_favourite = true;
+                  this._is_favourite = true;
                 }
             }
         }
     }
+
+  /**
+   * This method sets the is_favourite attribute internally.
+   * That is, it should be used as a result of an event fired by
+   * Tracker since this method doesn't propagate changes back
+   * to Tracker again.
+   */
+  internal void _set_favourite (bool is_fav)
+    {
+      this._is_favourite = is_fav;
+      this.notify_property ("is-favourite");
+    }
 }
diff --git a/backends/tracker/lib/trf-util.vala b/backends/tracker/lib/trf-util.vala
index 5e93687..f000231 100644
--- a/backends/tracker/lib/trf-util.vala
+++ b/backends/tracker/lib/trf-util.vala
@@ -46,6 +46,8 @@ internal class Trf.AfflInfo : Object
 
   public string im_account_id { get; set; }
 
+  public string im_nickname { get; set; }
+
   public string affl_tracker_id  { get; set; }
 
   public string title { get; set; }
@@ -105,6 +107,7 @@ public class Trf.OntologyDefs : Object
   public static const string NCO_NICKNAME = "nco:nickname";
   public static const string RDF_TYPE = "ns:type";
   public static const string NCO_PERSON = "nco:PersonContact";
+  public static const string NCO_URL = "nco:url";
   public static const string NCO_WEBSITE = "nco:websiteUrl";
   public static const string NCO_BLOG = "nco:blogUrl";
   public static const string NAO_FAVORITE = "nao:predefined-tag-favorite";
@@ -125,11 +128,26 @@ public class Trf.OntologyDefs : Object
   public static const string NCO_PHOTO = "nco:photo";
   public static const string NIE_URL = "nie:url";
   public static const string NFO_IMAGE = "nfo:Image";
+  public static const string NIE_DATAOBJECT = "nie:DataObject";
   public static const string NCO_IMADDRESS = "nco:IMAddress";
   public static const string NCO_HAS_IMADDRESS = "nco:hasIMAddress";
   public static const string NCO_IMPROTOCOL = "nco:imProtocol";
   public static const string NCO_IMID = "nco:imID";
+  public static const string NCO_IM_NICKNAME = "nco:imNickname";
   public static const string NCO_POSTAL_ADDRESS = "nco:PostalAddress";
+  public static const string NCO_HAS_POSTAL_ADDRESS = "nco:hasPostalAddress";
+  public static const string NCO_POBOX = "nco:pobox";
+  public static const string NCO_DISTRICT = "nco:district";
+  public static const string NCO_COUNTY = "nco:county";
+  public static const string NCO_LOCALITY = "nco:locality";
+  public static const string NCO_POSTALCODE = "nco:postalcode";
+  public static const string NCO_STREET_ADDRESS = "nco:streetAddress";
+  public static const string NCO_ADDRESS_LOCATION = "nco:addressLocation";
+  public static const string NCO_EXTENDED_ADDRESS = "nco:extendedAddress";
+  public static const string NCO_COUNTRY = "nco:country";
+  public static const string NCO_REGION = "nco:region";
+  public static const string NCO_ROLE = "nco:role";
+  public static const string NCO_ORG = "nco:org";
   public static const string NCO_URL_PREFIX =
       "<http://www.semanticdesktop.org/ontologies/2007/03/22/";;
   public static const string NAO_URL_PREFIX =
diff --git a/tests/tracker/Makefile.am b/tests/tracker/Makefile.am
index 5793a8d..5d11af8 100644
--- a/tests/tracker/Makefile.am
+++ b/tests/tracker/Makefile.am
@@ -70,6 +70,22 @@ noinst_PROGRAMS = \
 	gender-details-interface  \
 	postal-address-details-interface  \
 	avatar-updates  \
+	add-persona  \
+	remove-persona  \
+	set-alias  \
+	set-favourite  \
+	set-phones  \
+	set-emails  \
+	set-avatar  \
+	set-structured-name  \
+	set-full-name  \
+	set-urls  \
+	set-im-addresses  \
+	set-postal-addresses  \
+	set-roles  \
+	set-notes  \
+	set-birthday  \
+	set-gender  \
 	$(NULL)
 
 backend_store_key_file=$(srcdir)/data/backend-tracker-only.ini
@@ -204,6 +220,70 @@ avatar_updates_SOURCES = \
 	avatar-updates.vala \
 	$(NULL)
 
+add_persona_SOURCES = \
+	add-persona.vala \
+	$(NULL)
+
+remove_persona_SOURCES = \
+	remove-persona.vala \
+	$(NULL)
+
+set_alias_SOURCES = \
+	set-alias.vala \
+	$(NULL)
+
+set_favourite_SOURCES = \
+	set-favourite.vala \
+	$(NULL)
+
+set_phones_SOURCES = \
+	set-phones.vala \
+	$(NULL)
+
+set_emails_SOURCES = \
+	set-emails.vala \
+	$(NULL)
+
+set_avatar_SOURCES = \
+	set-avatar.vala \
+	$(NULL)
+
+set_structured_name_SOURCES = \
+	set-structured-name.vala \
+	$(NULL)
+
+set_full_name_SOURCES = \
+	set-full-name.vala \
+	$(NULL)
+
+set_urls_SOURCES = \
+	set-urls.vala \
+	$(NULL)
+
+set_im_addresses_SOURCES = \
+	set-im-addresses.vala \
+	$(NULL)
+
+set_postal_addresses_SOURCES = \
+	set-postal-addresses.vala \
+	$(NULL)
+
+set_roles_SOURCES = \
+	set-roles.vala \
+	$(NULL)
+
+set_notes_SOURCES = \
+	set-notes.vala \
+	$(NULL)
+
+set_birthday_SOURCES = \
+	set-birthday.vala \
+	$(NULL)
+
+set_gender_SOURCES = \
+	set-gender.vala \
+	$(NULL)
+
 CLEANFILES = \
         *.pid \
         *.address \
@@ -242,6 +322,22 @@ MAINTAINERCLEANFILES = \
         gender_details_interface_vala.stamp \
         postal_address_details_interface_vala.stamp \
         avatar_updates_vala.stamp \
+        add_persona_vala.stamp \
+        remove_persona_vala.stamp \
+        set_alias_vala.stamp \
+        set_favourite_vala.stamp \
+        set_phones_vala.stamp \
+        set_emails_vala.stamp \
+        set_avatar_vala.stamp \
+        set_structured_name_vala.stamp \
+        set_full_name_vala.stamp \
+        set_urls_vala.stamp \
+        set_im_addresses_vala.stamp \
+        set_postal_addresses_vala.stamp \
+        set_roles_vala.stamp \
+        set_notes_vala.stamp \
+        set_birthday_vala.stamp \
+        set_gender_vala.stamp \
         $(NULL)
 
 EXTRA_DIST = \
diff --git a/tests/tracker/add-contact.vala b/tests/tracker/add-contact.vala
index 79ed324..7ce6c87 100644
--- a/tests/tracker/add-contact.vala
+++ b/tests/tracker/add-contact.vala
@@ -54,12 +54,12 @@ public class AddContactTests : Folks.TestCase
       this._persona_fullname = "persona #1";
       this._contact_added = false;
 
-      var store = BackendStore.dup ();
+      Gee.HashMap<string, string> c1 = new Gee.HashMap<string, string> ();
+      c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._persona_fullname);
+      this._tracker_backend.add_contact (c1);
+      this._tracker_backend.set_up ();
 
-      this._test_add_contact_async (store, (o, r) =>
-        {
-          this._test_add_contact_async.end (r);
-        });
+      this._test_add_contact_async ();
 
       Timeout.add_seconds (5, () =>
           {
@@ -72,10 +72,10 @@ public class AddContactTests : Folks.TestCase
       this._tracker_backend.tear_down ();
     }
 
-  private async void _test_add_contact_async (BackendStore store)
+  private async void _test_add_contact_async ()
     {
+      var store = BackendStore.dup ();
       yield store.prepare ();
-
       this._aggregator = new IndividualAggregator ();
       this._aggregator.individuals_changed.connect
           (this._individuals_changed_cb);
@@ -83,11 +83,6 @@ public class AddContactTests : Folks.TestCase
       try
         {
           yield this._aggregator.prepare ();
-
-          Gee.HashMap<string, string> c1 = new Gee.HashMap<string, string> ();
-          c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._persona_fullname);
-          this._tracker_backend.add_contact (c1);
-          this._tracker_backend.set_up ();
         }
       catch (GLib.Error e)
         {
diff --git a/tests/tracker/add-persona.vala b/tests/tracker/add-persona.vala
new file mode 100644
index 0000000..1606f37
--- /dev/null
+++ b/tests/tracker/add-persona.vala
@@ -0,0 +1,514 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
+ *
+ */
+
+using Tracker.Sparql;
+using TrackerTest;
+using Folks;
+using Gee;
+
+public class AddPersonaTests : Folks.TestCase
+{
+  private GLib.MainLoop _main_loop;
+  private TrackerTest.Backend _tracker_backend;
+  private IndividualAggregator _aggregator;
+  private string _persona_fullname;
+  private string _persona_alias;
+  private string _family_name;
+  private string _given_name;
+  private HashTable<string, bool> _properties_found;
+  private string _persona_iid;
+  private string _file_uri;
+  private string _birthday;
+  private DateTime _bday;
+  private string _email_1;
+  private string _email_2;
+  private string _im_addr_1;
+  private string _im_addr_2;
+  private string _note_1;
+  private string _phone_1;
+  private string _phone_2;
+  private string _title_1;
+  private string _organisation_1;
+  private PostalAddress _address;
+  private string _po_box = "12345";
+  private string _locality = "locality";
+  private string _postal_code = "code";
+  private string _street = "some street";
+  private string _extension = "some extension";
+  private string _country = "some country";
+  private string _region = "some region";
+  private string _url_1 = "http://www-1.example.org";;
+  private string _url_2 = "http://www-1.example.org";;
+  private Trf.PersonaStore _pstore;
+  private bool _added_persona = false;
+
+  public AddPersonaTests ()
+    {
+      base ("AddPersonaTests");
+
+      this._tracker_backend = new TrackerTest.Backend ();
+
+      this.add_test ("test adding personas to Tracker ", this.test_add_persona);
+    }
+
+  public override void set_up ()
+    {
+    }
+
+  public override void tear_down ()
+    {
+    }
+
+  public void test_add_persona ()
+    {
+      this._main_loop = new GLib.MainLoop (null, false);
+      this._persona_fullname = "persona #1";
+      this._persona_alias = "alias";
+      this._family_name = "family";
+      this._given_name = "given";
+      this._persona_iid = "";
+      this._file_uri = "file:///tmp/some-avatar.jpg";
+      this._birthday = "2001-10-26T20:32:52Z";
+      this._email_1 = "someone-1 example org";
+      this._email_2 = "someone-2 example org";
+      this._im_addr_1 = "someone-1 jabber example org";
+      this._im_addr_2 = "someone-2 jabber example org";
+      this._note_1 = "this is a note";
+      this._phone_1 = "12345";
+      this._phone_2 = "54321";
+      this._title_1 = "CFO";
+      this._organisation_1 = "Example Inc.";
+
+      GLib.List<string> types =  new GLib.List<string> ();
+      this._address = new PostalAddress (this._po_box,
+          this._extension, this._street, this._locality, this._region,
+          this._postal_code, this._country, null, types, null);
+
+      TimeVal t = TimeVal ();
+      t.from_iso8601 (this._birthday);
+      this._bday = new  DateTime.from_timeval_utc (t);
+
+      this._properties_found = new HashTable<string, bool>
+          (str_hash, str_equal);
+      this._properties_found.insert ("full_name", false);
+      this._properties_found.insert ("alias", false);
+      this._properties_found.insert ("is_favourite", false);
+      this._properties_found.insert ("structured_name", false);
+      this._properties_found.insert ("avatar", false);
+      this._properties_found.insert ("birthday", false);
+      this._properties_found.insert ("gender", false);
+      this._properties_found.insert ("email-1", false);
+      this._properties_found.insert ("email-2", false);
+      this._properties_found.insert ("im-addr-1", false);
+      this._properties_found.insert ("im-addr-2", false);
+      this._properties_found.insert ("note-1", false);
+      this._properties_found.insert ("phone-1", false);
+      this._properties_found.insert ("phone-2", false);
+      this._properties_found.insert ("role-1", false);
+      this._properties_found.insert ("postal-address-1", false);
+      this._properties_found.insert ("url-1", false);
+      this._properties_found.insert ("url-2", false);
+
+      this._test_add_persona_async ();
+
+      Timeout.add_seconds (5, () =>
+        {
+          this._main_loop.quit ();
+          assert_not_reached ();
+        });
+
+      this._main_loop.run ();
+
+      foreach (var k in this._properties_found.get_values ())
+        {
+          assert (k);
+        }
+
+      this._tracker_backend.tear_down ();
+    }
+
+  private async void _test_add_persona_async ()
+    {
+      var store = BackendStore.dup ();
+      yield store.prepare ();
+      this._aggregator = new IndividualAggregator ();
+      this._aggregator.individuals_changed.connect
+          (this._individuals_changed_cb);
+      try
+        {
+          yield this._aggregator.prepare ();
+
+          this._pstore = null;
+          foreach (var backend in store.enabled_backends)
+            {
+              this._pstore =
+                (Trf.PersonaStore) backend.persona_stores.lookup ("tracker");
+              if (this._pstore != null)
+                break;
+            }
+          assert (this._pstore != null);
+          this._pstore.notify["is-prepared"].connect (this._notify_pstore_cb);
+          this._try_to_add ();
+        }
+      catch (GLib.Error e)
+        {
+          GLib.warning ("Error when calling prepare: %s\n", e.message);
+        }
+    }
+
+  private async void _add_persona ()
+    {
+      HashTable<string, Value?> details = new HashTable<string, Value?>
+          (str_hash, str_equal);
+
+      Value? v1 = Value (typeof (string));
+      v1.set_string (this._persona_fullname);
+      details.insert (this._pstore.detail_key (PersonaDetail.FULL_NAME),
+          (owned)v1);
+
+      Value? v2 = Value (typeof (string));
+      v2.set_string (this._persona_alias);
+      details.insert (this._pstore.detail_key (PersonaDetail.ALIAS), (owned)v2);
+
+      Value? v3 = Value (typeof (bool));
+      v3.set_boolean (true);
+      details.insert (this._pstore.detail_key (PersonaDetail.FAVOURITE),
+          (owned) v3);
+
+      Value? v4 = Value (typeof (StructuredName));
+      StructuredName sname = new StructuredName (this._family_name,
+          this._given_name, null, null, null);
+      v4.set_object (sname);
+      details.insert (this._pstore.detail_key (PersonaDetail.STRUCTURED_NAME),
+          (owned) v4);
+
+      Value? v5 = Value (typeof (File));
+      File avatar = File.new_for_uri (this._file_uri);
+      v5.set_object (avatar);
+      details.insert (this._pstore.detail_key (PersonaDetail.AVATAR),
+          (owned) v5);
+
+      Value? v6 = Value (typeof (DateTime));
+      TimeVal t = TimeVal ();
+      t.from_iso8601 (this._birthday);
+      DateTime dobj = new  DateTime.from_timeval_utc (t);
+      v6.set_boxed (dobj);
+      details.insert (this._pstore.detail_key (PersonaDetail.BIRTHDAY),
+          (owned) v6);
+
+      Value? v7 = Value (typeof (Folks.Gender));
+      v7.set_enum (Folks.Gender.MALE);
+      details.insert (this._pstore.detail_key (PersonaDetail.GENDER),
+          (owned) v7);
+
+      Value? v8 = Value (typeof (GLib.List<FieldDetails>));
+      GLib.List<FieldDetails> emails =
+        new GLib.List<FieldDetails> ();
+      var email_1 = new FieldDetails (this._email_1);
+      emails.prepend ((owned) email_1);
+      var email_2 = new FieldDetails (this._email_2);
+      emails.prepend ((owned) email_2);
+      v8.set_pointer (emails);
+      details.insert (this._pstore.detail_key (PersonaDetail.EMAIL_ADDRESSES),
+          (owned) v8);
+
+      Value? v9 = Value (typeof (HashTable<string, LinkedHashSet<string>>));
+      HashTable<string, LinkedHashSet<string>> im_addrs =
+        new HashTable<string, LinkedHashSet<string>> (null, null);
+      LinkedHashSet<string> proto1 = new LinkedHashSet<string> ();
+      proto1.add (this._im_addr_1);
+      im_addrs.insert ("jabber", proto1);
+      LinkedHashSet<string> proto2 = new LinkedHashSet<string> ();
+      proto2.add (this._im_addr_2);
+      im_addrs.insert ("yahoo", proto2);
+      v9.set_boxed (im_addrs);
+      details.insert (this._pstore.detail_key (PersonaDetail.IM_ADDRESSES), v9);
+
+      Value? v10 = Value (typeof (Gee.HashSet<Note>));
+      Gee.HashSet<Note> notes = new Gee.HashSet<Note> ();
+      Note n1 = new Note (this._note_1);
+      notes.add (n1);
+      v10.set_object (notes);
+      details.insert (this._pstore.detail_key (PersonaDetail.NOTES),
+          (owned) v10);
+
+      Value? v11 = Value (typeof (GLib.List<FieldDetails>));
+      GLib.List<FieldDetails> phones =
+        new GLib.List<FieldDetails> ();
+      var phone_1 = new FieldDetails (this._phone_1);
+      phones.prepend ((owned) phone_1);
+      var phone_2 = new FieldDetails (this._phone_2);
+      phones.prepend ((owned) phone_2);
+      v11.set_pointer (phones);
+      details.insert (this._pstore.detail_key (PersonaDetail.PHONE_NUMBERS),
+          (owned) v11);
+
+      Value? v12 = Value (typeof (Gee.HashSet<Role>));
+      Gee.HashSet<Role> roles = new Gee.HashSet<Role> ();
+      Role r1 = new Role (this._title_1, this._organisation_1);
+      roles.add (r1);
+      v12.set_object (roles);
+      details.insert (this._pstore.detail_key (PersonaDetail.ROLES),
+          (owned) v12);
+
+      Value? v13 = Value (typeof (GLib.List<PostalAddress>));
+      GLib.List<PostalAddress> postal_addresses =
+        new GLib.List<PostalAddress> ();
+
+      GLib.List<string> types =  new GLib.List<string> ();
+      PostalAddress postal_a = new PostalAddress (this._po_box,
+          this._extension, this._street, this._locality, this._region,
+          this._postal_code, this._country, null, types, null);
+      postal_addresses.prepend ((owned) postal_a);
+      v13.set_pointer (postal_addresses);
+      details.insert (this._pstore.detail_key (PersonaDetail.POSTAL_ADDRESSES),
+          (owned) v13);
+
+      Value? v14 = Value (typeof (GLib.List<FieldDetails>));
+      GLib.List<FieldDetails> urls =
+        new GLib.List<FieldDetails> ();
+      var url_1 = new FieldDetails (this._url_1);
+      urls.prepend ((owned) url_1);
+      var url_2 = new FieldDetails (this._url_2);
+      urls.prepend ((owned) url_2);
+      v14.set_pointer (urls);
+      details.insert (this._pstore.detail_key (PersonaDetail.URLS), (owned)v14);
+
+      try
+        {
+          Trf.Persona persona = (Trf. Persona)
+              yield this._aggregator.add_persona_from_details
+                (null, this._pstore, details);
+          this._persona_iid = persona.iid;
+        }
+      catch (Folks.IndividualAggregatorError e)
+        {
+          GLib.warning ("[AddPersonaError] add_persona_from_details: %s\n",
+              e.message);
+        }
+    }
+
+  private void _individuals_changed_cb
+      (GLib.List<Individual>? added,
+       GLib.List<Individual>? removed,
+       string? message,
+       Persona? actor,
+       GroupDetails.ChangeReason reason)
+    {
+      foreach (unowned Individual i in added)
+        {
+          if (i.is_user == false)
+            {
+              /* NOTE:
+               *   we also listen to the Trf.Persona's structured-name
+               *   because if only one of its property is updated
+               *   Individual won't fire a notification.
+               */
+              unowned Trf.Persona p = (Trf.Persona) i.personas.nth_data (0);
+              if (p.structured_name != null)
+                {
+                  p.notify["structured-name"].connect
+                    (this._notify_persona_sname);
+                }
+
+              i.notify["full-name"].connect (this._notify_cb);
+              i.notify["alias"].connect (this._notify_cb);
+              i.notify["avatar"].connect (this._notify_cb);
+              i.notify["is-favourite"].connect (this._notify_cb);
+              i.notify["structured-name"].connect (this._notify_cb);
+              i.notify["family-name"].connect (this._notify_cb);
+              i.notify["given-name"].connect (this._notify_cb);
+              i.notify["avatar"].connect (this._notify_cb);
+              i.notify["birthday"].connect (this._notify_cb);
+              i.notify["gender"].connect (this._notify_cb);
+              i.notify["email-addresses"].connect (this._notify_cb);
+              i.notify["im-addresses"].connect (this._notify_cb);
+              i.notify["notes"].connect (this._notify_cb);
+              i.notify["phone-numbers"].connect (this._notify_cb);
+              i.notify["roles"].connect (this._notify_cb);
+              i.notify["postal-addresses"].connect (this._notify_cb);
+              i.notify["urls"].connect (this._notify_cb);
+
+              this._check_properties (i);
+            }
+        }
+
+      assert (removed == null);
+    }
+
+  private void _notify_cb (Object individual_obj, ParamSpec ps)
+    {
+      Folks.Individual i = (Folks.Individual) individual_obj;
+      this._check_properties (i);
+    }
+
+  private void _notify_persona_sname (Object persona_p, ParamSpec ps)
+    {
+      Trf.Persona persona = (Trf.Persona) persona_p;
+      this._check_sname (persona.structured_name);
+      this._exit_if_all_properties_found ();
+    }
+
+  private void _notify_pstore_cb (Object _pstore, ParamSpec ps)
+    {
+      this._try_to_add ();
+    }
+
+  private void _try_to_add ()
+    {
+      lock (this._added_persona)
+        {
+          if (this._pstore.is_prepared &&
+              this._added_persona == false)
+            {
+              this._added_persona = true;
+              this._add_persona ();
+            }
+        }
+    }
+
+  private void _check_properties (Individual i)
+    {
+      if (i.full_name == this._persona_fullname)
+        this._properties_found.replace ("full_name", true);
+
+      if (i.alias == this._persona_alias)
+        this._properties_found.replace ("alias", true);
+
+      if (i.is_favourite)
+        this._properties_found.replace ("is_favourite", true);
+
+      if (i.structured_name != null)
+        {
+          this._check_sname (i.structured_name);
+        }
+
+      if (i.avatar != null &&
+          i.avatar.get_uri () == this._file_uri)
+        this._properties_found.replace ("avatar", true);
+
+      if (i.birthday != null &&
+          i.birthday.compare (this._bday) == 0)
+        this._properties_found.replace ("birthday", true);
+
+      if (i.gender == Gender.MALE)
+        this._properties_found.replace ("gender", true);
+
+      foreach (unowned FieldDetails e in i.email_addresses)
+        {
+          if (e.value == this._email_1)
+            {
+              this._properties_found.replace ("email-1", true);
+            }
+          else if (e.value == this._email_2)
+            {
+              this._properties_found.replace ("email-2", true);
+            }
+        }
+
+      foreach (var proto in i.im_addresses.get_keys ())
+        {
+          var addrs = i.im_addresses.lookup (proto);
+          foreach (var a in addrs)
+            {
+              if (a == this._im_addr_1)
+                this._properties_found.replace ("im-addr-1", true);
+              else if (a == this._im_addr_2)
+                this._properties_found.replace ("im-addr-2", true);
+            }
+        }
+
+      foreach (var n in i.notes)
+        {
+          if (n.content == this._note_1)
+            {
+              this._properties_found.replace ("note-1", true);
+            }
+        }
+
+      foreach (unowned FieldDetails e in i.phone_numbers)
+        {
+          if (e.value == this._phone_1)
+            {
+              this._properties_found.replace ("phone-1", true);
+            }
+          else if (e.value == this._phone_2)
+            {
+              this._properties_found.replace ("phone-2", true);
+            }
+        }
+
+      foreach (var r in i.roles)
+        {
+          if (r.title == this._title_1 &&
+              r.organisation_name == this._organisation_1)
+            {
+              this._properties_found.replace ("role-1", true);
+            }
+        }
+
+      foreach (var pa in i.postal_addresses)
+        {
+          this._address.uid = pa.uid;
+          if (pa.equal (this._address))
+            this._properties_found.replace ("postal-address-1", true);
+        }
+
+      foreach (var u in i.urls)
+        {
+          if (u.value == this._url_1)
+            this._properties_found.replace ("url-1", true);
+          if (u.value == this._url_2)
+            this._properties_found.replace ("url-2", true);
+        }
+
+      this._exit_if_all_properties_found ();
+    }
+
+  private void _exit_if_all_properties_found ()
+    {
+      foreach (var k in this._properties_found.get_keys ())
+        {
+          var v = this._properties_found.lookup (k);
+          if (v == false)
+            return;
+        }
+      this._main_loop.quit ();
+    }
+
+  private void _check_sname (StructuredName sname)
+    {
+      if (sname.family_name == this._family_name &&
+          sname.given_name == this._given_name)
+        this._properties_found.replace ("structured_name", true);
+    }
+}
+
+public int main (string[] args)
+{
+  Test.init (ref args);
+
+  TestSuite root = TestSuite.get_root ();
+  root.add_suite (new AddPersonaTests ().get_suite ());
+
+  Test.run ();
+
+  return 0;
+}
diff --git a/tests/tracker/additional-names-updates.vala b/tests/tracker/additional-names-updates.vala
index 8a0ac9a..fa28286 100644
--- a/tests/tracker/additional-names-updates.vala
+++ b/tests/tracker/additional-names-updates.vala
@@ -34,6 +34,7 @@ public class AdditionalNamesUpdatesTests : Folks.TestCase
   private bool _initial_additional_names_found;
   private string _contact_urn;
   private string _initial_additional_names;
+  private string _initial_fullname;
 
   public AdditionalNamesUpdatesTests ()
     {
@@ -58,13 +59,13 @@ public class AdditionalNamesUpdatesTests : Folks.TestCase
     {
       this._main_loop = new GLib.MainLoop (null, false);
       Gee.HashMap<string, string> c1 = new Gee.HashMap<string, string> ();
-      string initial_fullname = "persona #1";
+      this._initial_fullname = "persona #1";
       this._initial_additional_names = "additional name #1";
       this._updated_additional_names = "updated additional name #1";
       this._contact_urn = "<urn:contact001>";
 
       c1.set (TrackerTest.Backend.URN, this._contact_urn);
-      c1.set (Trf.OntologyDefs.NCO_FULLNAME, initial_fullname);
+      c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._initial_fullname);
       c1.set (Trf.OntologyDefs.NCO_ADDITIONAL,
           this._initial_additional_names);
       this._tracker_backend.add_contact (c1);
@@ -116,31 +117,30 @@ public class AdditionalNamesUpdatesTests : Folks.TestCase
        GroupDetails.ChangeReason reason)
     {
       foreach (unowned Individual i in added)
-      {
-        i.structured_name.notify["additional-names"].connect
-        (this._notify_additional_names_cb);
-        var additional_names = i.structured_name.additional_names;
-        if (additional_names == this._initial_additional_names)
-          {
-            this._individual_id = i.id;
-            this._initial_additional_names_found = true;
-            this._tracker_backend.update_contact (this._contact_urn,
-                Trf.OntologyDefs.NCO_ADDITIONAL,
-                this._updated_additional_names);
-          }
-      }
+        {
+          if (this._initial_fullname == i.full_name)
+            {
+              var additional_names = i.structured_name.additional_names;
+              if (additional_names == this._initial_additional_names)
+                {
+                  i.structured_name.notify["additional-names"].connect
+                    (this._notify_additional_names_cb);
+                  this._individual_id = i.id;
+                  this._initial_additional_names_found = true;
+                  this._tracker_backend.update_contact (this._contact_urn,
+                      Trf.OntologyDefs.NCO_ADDITIONAL,
+                      this._updated_additional_names);
+                }
+            }
+        }
 
       assert (removed == null);
     }
 
-  private void _notify_additional_names_cb ()
+  private void _notify_additional_names_cb (Object sname_obj, ParamSpec ps)
     {
-      var i = this._aggregator.individuals.lookup (this._individual_id);
-
-      if (i == null)
-        return;
-
-      var additional_names = i.structured_name.additional_names;
+      Folks.StructuredName sname = (Folks.StructuredName) sname_obj;
+      var additional_names = sname.additional_names;
 
       if (additional_names == this._updated_additional_names)
         {
diff --git a/tests/tracker/family-name-updates.vala b/tests/tracker/family-name-updates.vala
index dbcb7ee..9801863 100644
--- a/tests/tracker/family-name-updates.vala
+++ b/tests/tracker/family-name-updates.vala
@@ -117,28 +117,27 @@ public class FamilyNameUpdatesTests : Folks.TestCase
     {
       foreach (unowned Individual i in added)
         {
-          i.structured_name.notify["family-name"].connect
-              (this._notify_family_name_cb);
-          var family_name = i.structured_name.family_name;
-          if (family_name == this._initial_family_name)
+          if (this._initial_fullname == i.full_name)
             {
-              this._individual_id = i.id;
-              this._initial_family_name_found = true;
-              this._tracker_backend.update_contact (this._contact_urn,
-                  Trf.OntologyDefs.NCO_FAMILY, this._updated_family_name);
+              i.structured_name.notify["family-name"].connect
+                (this._notify_family_name_cb);
+              var family_name = i.structured_name.family_name;
+              if (family_name == this._initial_family_name)
+                {
+                  this._individual_id = i.id;
+                  this._initial_family_name_found = true;
+                  this._tracker_backend.update_contact (this._contact_urn,
+                      Trf.OntologyDefs.NCO_FAMILY, this._updated_family_name);
+                }
             }
         }
-
-        assert (removed == null);
+      assert (removed == null);
     }
 
-  private void _notify_family_name_cb ()
+  private void _notify_family_name_cb (Object individual_obj, ParamSpec ps)
     {
-      var i = this._aggregator.individuals.lookup (this._individual_id);
-      if (i == null)
-        return;
-
-      var family_name = i.structured_name.family_name;
+      Folks.StructuredName sname = (Folks.StructuredName) individual_obj;
+      var family_name = sname.family_name;
       if (family_name == this._updated_family_name)
         {
           this._updated_family_name_found = true;
diff --git a/tests/tracker/given-name-updates.vala b/tests/tracker/given-name-updates.vala
index 463fb0d..9ed9d27 100644
--- a/tests/tracker/given-name-updates.vala
+++ b/tests/tracker/given-name-updates.vala
@@ -116,15 +116,18 @@ public class GivenNameUpdatesTests : Folks.TestCase
     {
       foreach (unowned Individual i in added)
         {
-          i.structured_name.notify["given-name"].connect
-              (this._notify_given_name_cb);
-          var given_name = i.structured_name.given_name;
-          if (given_name == this._initial_given_name)
+          if (this._initial_fullname == i.full_name)
             {
-              this._individual_id = i.id;
-              this._initial_given_name_found = true;
-              this._tracker_backend.update_contact (this._contact_urn,
-                  Trf.OntologyDefs.NCO_GIVEN, this._updated_given_name);
+              i.structured_name.notify["given-name"].connect
+                 (this._notify_given_name_cb);
+              var given_name = i.structured_name.given_name;
+              if (given_name == this._initial_given_name)
+                {
+                  this._individual_id = i.id;
+                  this._initial_given_name_found = true;
+                  this._tracker_backend.update_contact (this._contact_urn,
+                      Trf.OntologyDefs.NCO_GIVEN, this._updated_given_name);
+                }
             }
         }
 
diff --git a/tests/tracker/name-details-interface.vala b/tests/tracker/name-details-interface.vala
index 0aa6eb5..54d2530 100644
--- a/tests/tracker/name-details-interface.vala
+++ b/tests/tracker/name-details-interface.vala
@@ -56,7 +56,6 @@ public class NameDetailsInterfaceTests : Folks.TestCase
       this._c2 = new Gee.HashMap<string, string> ();
 
       this._c1.set (Trf.OntologyDefs.NCO_FULLNAME, "persona #1");
-      this._c1.set (Trf.OntologyDefs.NCO_NICKNAME, "p #1");
       this._c1.set (Trf.OntologyDefs.NCO_FAMILY, "p #1 Family");
       this._c1.set (Trf.OntologyDefs.NCO_GIVEN, "p #1 Given");
       this._c1.set (Trf.OntologyDefs.NCO_ADDITIONAL, "p #1 Additional");
@@ -65,7 +64,6 @@ public class NameDetailsInterfaceTests : Folks.TestCase
       this._tracker_backend.add_contact (this._c1);
 
       this._c2.set (Trf.OntologyDefs.NCO_FULLNAME, "persona #2");
-      this._c2.set (Trf.OntologyDefs.NCO_NICKNAME, "p #2");
       this._tracker_backend.add_contact (this._c2);
 
       this._tracker_backend.set_up ();
@@ -122,11 +120,6 @@ public class NameDetailsInterfaceTests : Folks.TestCase
                  {
                    this._c1.unset (Trf.OntologyDefs.NCO_FULLNAME);
 
-                   string nickname = ((Folks.NameDetails) i).nickname;
-                   assert (this._c1.get (Trf.OntologyDefs.NCO_NICKNAME) ==
-                       nickname);
-                   this._c1.unset (Trf.OntologyDefs.NCO_NICKNAME);
-
                    string family = sname.family_name ;
                    assert (this._c1.get (Trf.OntologyDefs.NCO_FAMILY) ==
                        family);
@@ -157,12 +150,7 @@ public class NameDetailsInterfaceTests : Folks.TestCase
                  {
                    this._c2.unset (Trf.OntologyDefs.NCO_FULLNAME);
 
-                   string nickname = ((Folks.NameDetails) i).nickname;
-                   assert (this._c2.get (Trf.OntologyDefs.NCO_NICKNAME) ==
-                       nickname);
-                   this._c2.unset (Trf.OntologyDefs.NCO_NICKNAME);
-
-                   assert (sname.is_empty () == true);
+                   assert (sname == null || sname.is_empty () == true);
                  }
             }
         }
diff --git a/tests/tracker/nickname-updates.vala b/tests/tracker/nickname-updates.vala
index e3e39d4..deaafa9 100644
--- a/tests/tracker/nickname-updates.vala
+++ b/tests/tracker/nickname-updates.vala
@@ -28,12 +28,10 @@ public class NicknameUpdatesTests : Folks.TestCase
   private TrackerTest.Backend _tracker_backend;
   private IndividualAggregator _aggregator;
   private bool _updated_nickname_found;
-  private bool _initial_nickname_found = false;
   private string _updated_nickname;
   private string _individual_id;
   private GLib.MainLoop _main_loop;
   private string _initial_fullname;
-  private string _initial_nickname;
   private string _contact_urn;
 
   public NicknameUpdatesTests ()
@@ -59,18 +57,15 @@ public class NicknameUpdatesTests : Folks.TestCase
       this._main_loop = new GLib.MainLoop (null, false);
       Gee.HashMap<string, string> c1 = new Gee.HashMap<string, string> ();
       this._initial_fullname = "persona #1";
-      this._initial_nickname = "nickname #1";
       this._updated_nickname = "updated nickname #1";
       this._contact_urn = "<urn:contact001>";
 
       c1.set (TrackerTest.Backend.URN, this._contact_urn);
       c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._initial_fullname);
-      c1.set (Trf.OntologyDefs.NCO_NICKNAME, this._initial_nickname);
       this._tracker_backend.add_contact (c1);
 
       this._tracker_backend.set_up ();
 
-      this._initial_nickname_found = false;
       this._updated_nickname_found = false;
       this._individual_id = "";
 
@@ -84,7 +79,6 @@ public class NicknameUpdatesTests : Folks.TestCase
 
       this._main_loop.run ();
 
-      assert (this._initial_nickname_found == true);
       assert (this._updated_nickname_found == true);
 
       this._tracker_backend.tear_down ();
@@ -116,13 +110,25 @@ public class NicknameUpdatesTests : Folks.TestCase
     {
       foreach (unowned Individual i in added)
         {
-          if (i.nickname == this._initial_nickname)
+          if (i.full_name == this._initial_fullname)
             {
               i.notify["nickname"].connect (this._notify_nickname_cb);
               this._individual_id = i.id;
-              this._initial_nickname_found = true;
-              this._tracker_backend.update_contact (this._contact_urn,
-                  Trf.OntologyDefs.NCO_NICKNAME, this._updated_nickname);
+
+              var im_addr = "<urn:im-address>";
+              this._tracker_backend.insert_triplet (im_addr,
+                  "a", Trf.OntologyDefs.NCO_IMADDRESS,
+                  Trf.OntologyDefs.NCO_IM_NICKNAME, this._updated_nickname);
+
+              var affl = "<urn:im-affl>";
+              this._tracker_backend.insert_triplet (affl,
+                  "a", Trf.OntologyDefs.NCO_AFFILIATION);
+
+               this._tracker_backend.insert_triplet (affl,
+                 Trf.OntologyDefs.NCO_HAS_IMADDRESS, im_addr);
+
+              this._tracker_backend.insert_triplet (this._contact_urn,
+                  Trf.OntologyDefs.NCO_HAS_AFFILIATION, affl);
             }
         }
 
diff --git a/tests/tracker/prefix-name-updates.vala b/tests/tracker/prefix-name-updates.vala
index c547c94..82e655a 100644
--- a/tests/tracker/prefix-name-updates.vala
+++ b/tests/tracker/prefix-name-updates.vala
@@ -116,15 +116,18 @@ public class PrefixNameUpdatesTests : Folks.TestCase
     {
       foreach (unowned Individual i in added)
         {
-          var prefix_name = i.structured_name.prefixes;
-          if (prefix_name == this._initial_prefix_name)
+          if (this._initial_fullname == i.full_name)
             {
-              i.structured_name.notify["prefixes"].connect
-                   (this._notify_prefix_name_cb);
-              this._individual_id = i.id;
-              this._initial_prefix_name_found = true;
-              this._tracker_backend.update_contact (this._contact_urn,
-                  Trf.OntologyDefs.NCO_PREFIX, this._updated_prefix_name);
+              var prefix_name = i.structured_name.prefixes;
+              if (prefix_name == this._initial_prefix_name)
+                {
+                  i.structured_name.notify["prefixes"].connect
+                  (this._notify_prefix_name_cb);
+                  this._individual_id = i.id;
+                  this._initial_prefix_name_found = true;
+                  this._tracker_backend.update_contact (this._contact_urn,
+                      Trf.OntologyDefs.NCO_PREFIX, this._updated_prefix_name);
+                }
             }
         }
       assert (removed == null);
diff --git a/tests/tracker/remove-persona.vala b/tests/tracker/remove-persona.vala
new file mode 100644
index 0000000..e83d0fd
--- /dev/null
+++ b/tests/tracker/remove-persona.vala
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
+ *
+ */
+
+using Tracker.Sparql;
+using TrackerTest;
+using Folks;
+using Gee;
+
+public class RemovePersonaTests : Folks.TestCase
+{
+  private GLib.MainLoop _main_loop;
+  private TrackerTest.Backend _tracker_backend;
+  private IndividualAggregator _aggregator;
+  private string _persona_fullname;
+  private bool _persona_removed;
+  private bool _individual_removed;
+  private string _individual_id;
+  private PersonaStore _pstore;
+  private string _persona_id;
+  private Individual _individual;
+  private bool _added_persona = false;
+
+  public RemovePersonaTests ()
+    {
+      base ("RemovePersonaTests");
+
+      this._tracker_backend = new TrackerTest.Backend ();
+
+      this.add_test ("test adding personas to Tracker ", this.test_remove_persona);
+    }
+
+  public override void set_up ()
+    {
+    }
+
+  public override void tear_down ()
+    {
+    }
+
+  public void test_remove_persona ()
+    {
+      this._main_loop = new GLib.MainLoop (null, false);
+      this._persona_fullname = "persona #1";
+
+      this._persona_removed = false;
+      this._individual_removed = false;
+
+      this._test_remove_persona_async ();
+
+      Timeout.add_seconds (5, () =>
+        {
+          this._main_loop.quit ();
+          assert_not_reached ();
+        });
+
+      this._main_loop.run ();
+
+      assert (this._persona_removed == true);
+      assert (this._individual_removed == true);
+
+      this._tracker_backend.tear_down ();
+    }
+
+  private async void _test_remove_persona_async ()
+    {
+      var store = BackendStore.dup ();
+      yield store.prepare ();
+      this._aggregator = new IndividualAggregator ();
+      this._aggregator.individuals_changed.connect
+          (this._individuals_changed_cb);
+      try
+        {
+          yield this._aggregator.prepare ();
+
+          this._pstore = null;
+          foreach (var backend in store.enabled_backends)
+            {
+              this._pstore = backend.persona_stores.lookup ("tracker");
+              if (this._pstore != null)
+                break;
+            }
+          assert (this._pstore != null);
+
+          this._pstore.notify["is-prepared"].connect (this._notify_pstore_cb);
+          this._try_to_add ();
+        }
+      catch (GLib.Error e)
+        {
+          GLib.warning ("Error when calling prepare: %s\n", e.message);
+        }
+    }
+
+  private void _notify_pstore_cb (Object _pstore, ParamSpec ps)
+    {
+      this._try_to_add ();
+    }
+
+  private void _try_to_add ()
+    {
+      if (this._pstore.is_prepared &&
+          this._added_persona == false)
+        {
+          this._added_persona = true;
+          this._add_persona ();
+        }
+    }
+
+  private async void _add_persona ()
+    {
+      HashTable<string, Value?> details = new HashTable<string, Value?>
+          (str_hash, str_equal);
+      Value? v1 = Value (typeof (string));
+      v1.set_string (this._persona_fullname);
+      details.insert (this._pstore.detail_key (PersonaDetail.FULL_NAME),
+          (owned) v1);
+
+      Value? v2 = Value (typeof (GLib.List<FieldDetails>));
+      GLib.List<FieldDetails> emails =
+        new GLib.List<FieldDetails> ();
+      var email_1 = new FieldDetails ("test-1 example org");
+      emails.prepend ((owned) email_1);
+      var email_2 = new FieldDetails ("test-2 example org");
+      emails.prepend ((owned) email_2);
+      v2.set_pointer (emails);
+      details.insert (this._pstore.detail_key (PersonaDetail.EMAIL_ADDRESSES),
+          (owned) v2);
+
+      try
+        {
+          yield this._aggregator.add_persona_from_details
+              (null, this._pstore, details);
+        }
+      catch (Folks.IndividualAggregatorError e)
+        {
+          GLib.warning ("[RemovePersonaError] add_persona_from_details: %s\n",
+              e.message);
+        }
+    }
+
+  private void _individuals_changed_cb
+      (GLib.List<Individual>? added,
+       GLib.List<Individual>? removed,
+       string? message,
+       Persona? actor,
+       GroupDetails.ChangeReason reason)
+    {
+      foreach (unowned Individual i in added)
+        {
+          if (i.full_name == this._persona_fullname)
+            {
+              this._individual_id = i.id;
+              this._persona_id = i.personas.nth_data (0).iid;
+              this._individual = i;
+              if (this._pstore.personas.lookup (this._persona_id) != null)
+                {
+                  this._pstore.personas_changed.connect (this._personas_cb);
+                  this._aggregator.remove_individual (this._individual);
+                }
+            }
+        }
+
+      foreach (unowned Individual i in removed)
+        {
+          if (i.id == this._individual_id)
+            {
+              this._individual_removed = true;
+            }
+        }
+    }
+
+  private void _personas_cb ()
+    {
+      if (this._pstore.personas.lookup (this._persona_id) == null)
+        {
+          this._persona_removed = true;
+          this._main_loop.quit ();
+        }
+    }
+}
+
+public int main (string[] args)
+{
+  Test.init (ref args);
+
+  TestSuite root = TestSuite.get_root ();
+  root.add_suite (new RemovePersonaTests ().get_suite ());
+
+  Test.run ();
+
+  return 0;
+}
diff --git a/tests/tracker/set-alias.vala b/tests/tracker/set-alias.vala
new file mode 100644
index 0000000..a7bb252
--- /dev/null
+++ b/tests/tracker/set-alias.vala
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
+ *
+ */
+
+using Tracker.Sparql;
+using TrackerTest;
+using Folks;
+using Gee;
+
+public class SetAliasTests : Folks.TestCase
+{
+  private GLib.MainLoop _main_loop;
+  private TrackerTest.Backend _tracker_backend;
+  private IndividualAggregator _aggregator;
+  private string _persona_fullname;
+  private string _initial_alias;
+  private string _modified_alias;
+  private bool _initial_alias_found;
+  private bool _modified_alias_found;
+
+  public SetAliasTests ()
+    {
+      base ("SetAliasTests");
+
+      this._tracker_backend = new TrackerTest.Backend ();
+
+      this.add_test ("test setting alias ", this.test_set_alias);
+    }
+
+  public override void set_up ()
+    {
+    }
+
+  public override void tear_down ()
+    {
+    }
+
+  public void test_set_alias ()
+    {
+      this._main_loop = new GLib.MainLoop (null, false);
+      Gee.HashMap<string, string> c1 = new Gee.HashMap<string, string> ();
+      this._persona_fullname = "persona #1";
+      this._initial_alias = "initial alias";
+      this._modified_alias = "modified alias";
+
+      c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._persona_fullname);
+
+      /* Note:
+       *
+       * we treat the nco:nickname associated to an nco:PersonContact
+       * as the alias, and the nco:nickname(s) associated to IM accounts
+       * as possible nicknames. */
+      c1.set (Trf.OntologyDefs.NCO_NICKNAME, this._initial_alias);
+      this._tracker_backend.add_contact (c1);
+
+      this._tracker_backend.set_up ();
+
+      this._initial_alias_found = false;
+      this._modified_alias_found = false;
+
+      this._test_set_alias_async ();
+
+      Timeout.add_seconds (5, () =>
+        {
+          this._main_loop.quit ();
+          assert_not_reached ();
+        });
+
+      this._main_loop.run ();
+
+      assert (this._initial_alias_found == true);
+      assert (this._modified_alias_found == true);
+
+      this._tracker_backend.tear_down ();
+    }
+
+  private async void _test_set_alias_async ()
+    {
+      var store = BackendStore.dup ();
+      yield store.prepare ();
+      this._aggregator = new IndividualAggregator ();
+      this._aggregator.individuals_changed.connect
+          (this._individuals_changed_cb);
+      try
+        {
+          yield this._aggregator.prepare ();
+        }
+      catch (GLib.Error e)
+        {
+          GLib.warning ("Error when calling prepare: %s\n", e.message);
+        }
+    }
+
+ private void _individuals_changed_cb
+      (GLib.List<Individual>? added,
+       GLib.List<Individual>? removed,
+       string? message,
+       Persona? actor,
+       GroupDetails.ChangeReason reason)
+    {
+      foreach (unowned Individual i in added)
+        {
+          if (i.full_name == this._persona_fullname)
+            {
+              if (i.alias == this._initial_alias)
+                {
+                  this._initial_alias_found = true;
+
+                  Trf.Persona p = (Trf.Persona)i.personas.nth_data (0);
+
+                  /*
+                   * We connect to the Persona's handler because
+                   * Individual won't forward the notification to us
+                   * unless it comes from a writeable store.
+                   */
+                  p.notify["alias"].connect (this._notify_alias_cb);
+
+                  /* FIXME:
+                   * it would be nice if we could just do:
+                   *    i.alias = "foobar"
+                   * but we depend on:
+                   * https://bugzilla.gnome.org/show_bug.cgi?id=645441 */
+                  p.alias = this._modified_alias;
+                }
+            }
+        }
+
+      assert (removed == null);
+    }
+
+  private void _notify_alias_cb (Object persona, ParamSpec ps)
+    {
+      Trf.Persona p = (Trf.Persona) persona;
+      if (p.alias == this._modified_alias)
+        {
+          this._modified_alias_found = true;
+          this._main_loop.quit ();
+        }
+    }
+}
+
+public int main (string[] args)
+{
+  Test.init (ref args);
+
+  TestSuite root = TestSuite.get_root ();
+  root.add_suite (new SetAliasTests ().get_suite ());
+
+  Test.run ();
+
+  return 0;
+}
diff --git a/tests/tracker/set-avatar.vala b/tests/tracker/set-avatar.vala
new file mode 100644
index 0000000..3cac3c3
--- /dev/null
+++ b/tests/tracker/set-avatar.vala
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
+ *
+ */
+
+using Tracker.Sparql;
+using TrackerTest;
+using Folks;
+using Gee;
+
+public class SetAvatarTests : Folks.TestCase
+{
+  private GLib.MainLoop _main_loop;
+  private TrackerTest.Backend _tracker_backend;
+  private IndividualAggregator _aggregator;
+  private string _persona_fullname;
+  private string _avatar_uri;
+  private File _avatar;
+  private bool _avatar_found;
+
+  public SetAvatarTests ()
+    {
+      base ("SetAvatarTests");
+
+      this._tracker_backend = new TrackerTest.Backend ();
+
+      this.add_test ("test setting avatar ", this.test_set_avatar);
+    }
+
+  public override void set_up ()
+    {
+    }
+
+  public override void tear_down ()
+    {
+    }
+
+  public void test_set_avatar ()
+    {
+      this._main_loop = new GLib.MainLoop (null, false);
+      Gee.HashMap<string, string> c1 = new Gee.HashMap<string, string> ();
+      this._persona_fullname = "persona #1";
+      this._avatar_uri = "file:///tmp/some-avatar.jpg";
+      this._avatar = File.new_for_uri (this._avatar_uri);
+
+      c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._persona_fullname);
+      this._tracker_backend.add_contact (c1);
+
+      this._tracker_backend.set_up ();
+
+      this._avatar_found = false;
+
+      this._test_set_avatar_async ();
+
+      Timeout.add_seconds (5, () =>
+        {
+          this._main_loop.quit ();
+          assert_not_reached ();
+        });
+
+      this._main_loop.run ();
+
+      assert (this._avatar_found);
+
+     this._tracker_backend.tear_down ();
+    }
+
+  private async void _test_set_avatar_async ()
+    {
+      var store = BackendStore.dup ();
+      yield store.prepare ();
+      this._aggregator = new IndividualAggregator ();
+      this._aggregator.individuals_changed.connect
+          (this._individuals_changed_cb);
+      try
+        {
+          yield this._aggregator.prepare ();
+        }
+      catch (GLib.Error e)
+        {
+          GLib.warning ("Error when calling prepare: %s\n", e.message);
+        }
+    }
+
+  private void _individuals_changed_cb
+      (GLib.List<Individual>? added,
+       GLib.List<Individual>? removed,
+       string? message,
+       Persona? actor,
+       GroupDetails.ChangeReason reason)
+    {
+      foreach (unowned Individual i in added)
+        {
+          if (i.full_name == this._persona_fullname)
+            {
+              i.notify["avatar"].connect (this._notify_avatar_cb);
+
+              Trf.Persona p = (Trf.Persona)i.personas.nth_data (0);
+              p.avatar = this._avatar;
+            }
+        }
+
+      assert (removed == null);
+    }
+
+  private void _notify_avatar_cb (Object individual_obj, ParamSpec ps)
+    {
+      Folks.Individual i = (Folks.Individual) individual_obj;
+      if (i.full_name == this._persona_fullname)
+        {
+          if (i.avatar.get_uri () == this._avatar_uri)
+            {
+              this._avatar_found = true;
+              this._main_loop.quit ();
+            }
+        }
+   }
+}
+
+public int main (string[] args)
+{
+  Test.init (ref args);
+
+  TestSuite root = TestSuite.get_root ();
+  root.add_suite (new SetAvatarTests ().get_suite ());
+
+  Test.run ();
+
+  return 0;
+}
diff --git a/tests/tracker/set-birthday.vala b/tests/tracker/set-birthday.vala
new file mode 100644
index 0000000..5e8332e
--- /dev/null
+++ b/tests/tracker/set-birthday.vala
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
+ *
+ */
+
+using Tracker.Sparql;
+using TrackerTest;
+using Folks;
+using Gee;
+
+public class SetBirthdayTests : Folks.TestCase
+{
+  private GLib.MainLoop _main_loop;
+  private TrackerTest.Backend _tracker_backend;
+  private IndividualAggregator _aggregator;
+  private string _persona_fullname;
+  private bool _bday_found;
+  private DateTime _bday;
+  private string _birthday;
+
+  public SetBirthdayTests ()
+    {
+      base ("SetBirthdayTests");
+
+      this._tracker_backend = new TrackerTest.Backend ();
+
+      this.add_test ("test setting bithday ",
+          this.test_set_bday);
+    }
+
+  public override void set_up ()
+    {
+    }
+
+  public override void tear_down ()
+    {
+    }
+
+  public void test_set_bday ()
+    {
+      this._main_loop = new GLib.MainLoop (null, false);
+      Gee.HashMap<string, string> c1 = new Gee.HashMap<string, string> ();
+      this._persona_fullname = "persona #1";
+
+      c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._persona_fullname);
+      this._tracker_backend.add_contact (c1);
+
+      this._birthday = "2001-10-26T20:32:52Z";
+      TimeVal t = TimeVal ();
+      t.from_iso8601 (this._birthday);
+      this._bday = new  DateTime.from_timeval_utc (t);
+
+      this._tracker_backend.set_up ();
+
+      this._bday_found = false;
+
+      this._test_set_bday_async ();
+
+      Timeout.add_seconds (5, () =>
+        {
+          this._main_loop.quit ();
+          assert_not_reached ();
+        });
+
+      this._main_loop.run ();
+
+      assert (this._bday_found);
+
+     this._tracker_backend.tear_down ();
+    }
+
+  private async void _test_set_bday_async ()
+    {
+      var store = BackendStore.dup ();
+      yield store.prepare ();
+      this._aggregator = new IndividualAggregator ();
+      this._aggregator.individuals_changed.connect
+          (this._individuals_changed_cb);
+      try
+        {
+          yield this._aggregator.prepare ();
+        }
+      catch (GLib.Error e)
+        {
+          GLib.warning ("Error when calling prepare: %s\n", e.message);
+        }
+    }
+
+ private void _individuals_changed_cb
+      (GLib.List<Individual>? added,
+       GLib.List<Individual>? removed,
+       string? message,
+       Persona? actor,
+       GroupDetails.ChangeReason reason)
+    {
+      foreach (unowned Individual i in added)
+        {
+          if (i.full_name == this._persona_fullname)
+            {
+              i.notify["birthday"].connect (this._notify_bday_cb);
+
+              TimeVal t = TimeVal ();
+              t.from_iso8601 (this._birthday);
+              DateTime bday = new  DateTime.from_timeval_utc (t);
+
+              Trf.Persona p = (Trf.Persona)i.personas.nth_data (0);
+              p.birthday = (owned) bday;
+            }
+        }
+
+      assert (removed == null);
+    }
+
+  private void _notify_bday_cb (Object individual_obj, ParamSpec ps)
+    {
+      Folks.Individual i = (Folks.Individual) individual_obj;
+      if (i.full_name == this._persona_fullname)
+        {
+          if (i.birthday != null &&
+              i.birthday.compare (this._bday) == 0)
+            {
+              this._bday_found = true;
+              this._main_loop.quit ();
+            }
+        }
+    }
+}
+
+public int main (string[] args)
+{
+  Test.init (ref args);
+
+  TestSuite root = TestSuite.get_root ();
+  root.add_suite (new SetBirthdayTests ().get_suite ());
+
+  Test.run ();
+
+  return 0;
+}
diff --git a/tests/tracker/set-emails.vala b/tests/tracker/set-emails.vala
new file mode 100644
index 0000000..2275f38
--- /dev/null
+++ b/tests/tracker/set-emails.vala
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
+ *
+ */
+
+using Tracker.Sparql;
+using TrackerTest;
+using Folks;
+using Gee;
+
+public class SetEmailsTests : Folks.TestCase
+{
+  private GLib.MainLoop _main_loop;
+  private TrackerTest.Backend _tracker_backend;
+  private IndividualAggregator _aggregator;
+  private string _persona_fullname;
+  private string _email_1;
+  private string _email_2;
+  private bool _email_1_found;
+  private bool _email_2_found;
+
+  public SetEmailsTests ()
+    {
+      base ("SetEmailsTests");
+
+      this._tracker_backend = new TrackerTest.Backend ();
+
+      this.add_test ("test setting emails ", this.test_set_emails);
+    }
+
+  public override void set_up ()
+    {
+    }
+
+  public override void tear_down ()
+    {
+    }
+
+  public void test_set_emails ()
+    {
+      this._main_loop = new GLib.MainLoop (null, false);
+      Gee.HashMap<string, string> c1 = new Gee.HashMap<string, string> ();
+      this._persona_fullname = "persona #1";
+      this._email_1 = "email-1 example org";
+      this._email_2 = "email-2 example org";
+
+      c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._persona_fullname);
+      this._tracker_backend.add_contact (c1);
+
+      this._tracker_backend.set_up ();
+
+      this._email_1_found = false;
+      this._email_2_found = false;
+
+      this._test_set_emails_async ();
+
+      Timeout.add_seconds (5, () =>
+        {
+          this._main_loop.quit ();
+          assert_not_reached ();
+        });
+
+      this._main_loop.run ();
+
+      assert (this._email_1_found);
+      assert (this._email_2_found);
+
+     this._tracker_backend.tear_down ();
+    }
+
+  private async void _test_set_emails_async ()
+    {
+      var store = BackendStore.dup ();
+      yield store.prepare ();
+      this._aggregator = new IndividualAggregator ();
+      this._aggregator.individuals_changed.connect
+          (this._individuals_changed_cb);
+      try
+        {
+          yield this._aggregator.prepare ();
+        }
+      catch (GLib.Error e)
+        {
+          GLib.warning ("Error when calling prepare: %s\n", e.message);
+        }
+    }
+
+ private void _individuals_changed_cb
+      (GLib.List<Individual>? added,
+       GLib.List<Individual>? removed,
+       string? message,
+       Persona? actor,
+       GroupDetails.ChangeReason reason)
+    {
+      foreach (unowned Individual i in added)
+        {
+          if (i.full_name == this._persona_fullname)
+            {
+              i.notify["email-addresses"].connect (this._notify_emails_cb);
+
+              GLib.List<FieldDetails> emails = new GLib.List<FieldDetails> ();
+              var p1 = new FieldDetails (this._email_1);
+              emails.prepend ((owned) p1);
+              var p2 = new FieldDetails (this._email_2);
+              emails.prepend ((owned) p2);
+              Trf.Persona p = (Trf.Persona)i.personas.nth_data (0);
+              p.email_addresses = (owned) emails;
+            }
+        }
+
+      assert (removed == null);
+    }
+
+  private void _notify_emails_cb (Object individual_obj, ParamSpec ps)
+    {
+      Folks.Individual i = (Folks.Individual) individual_obj;
+      if (i.full_name == this._persona_fullname)
+        {
+          foreach (unowned FieldDetails p in i.email_addresses)
+            {
+              if (p.value == this._email_1)
+                this._email_1_found = true;
+              else if (p.value == this._email_2)
+                this._email_2_found = true;
+            }
+        }
+
+      if (this._email_1_found && this._email_2_found)
+        {
+          this._main_loop.quit ();
+        }
+    }
+}
+
+public int main (string[] args)
+{
+  Test.init (ref args);
+
+  TestSuite root = TestSuite.get_root ();
+  root.add_suite (new SetEmailsTests ().get_suite ());
+
+  Test.run ();
+
+  return 0;
+}
diff --git a/tests/tracker/set-favourite.vala b/tests/tracker/set-favourite.vala
new file mode 100644
index 0000000..4ded8ba
--- /dev/null
+++ b/tests/tracker/set-favourite.vala
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
+ *
+ */
+
+using Tracker.Sparql;
+using TrackerTest;
+using Folks;
+using Gee;
+
+public class SetFavouriteTests : Folks.TestCase
+{
+  private GLib.MainLoop _main_loop;
+  private TrackerTest.Backend _tracker_backend;
+  private IndividualAggregator _aggregator;
+  private string _initial_fullname_1 = "persona #1";
+  private string _initial_fullname_2 = "persona #2";
+  private bool _c1_initially_not_favourite;
+  private bool _c1_finally_favourite;
+  private bool _c2_initially_favourite;
+  private bool _c2_finally_not_favourite;
+
+  public SetFavouriteTests ()
+    {
+      base ("SetFavouriteTests");
+
+      this._tracker_backend = new TrackerTest.Backend ();
+
+      this.add_test ("test setting favourite ", this.test_set_alias);
+    }
+
+  public override void set_up ()
+    {
+    }
+
+  public override void tear_down ()
+    {
+    }
+
+  public void test_set_alias ()
+    {
+      this._main_loop = new GLib.MainLoop (null, false);
+      Gee.HashMap<string, string> c1 = new Gee.HashMap<string, string> ();
+      Gee.HashMap<string, string> c2 = new Gee.HashMap<string, string> ();
+      this._initial_fullname_1 = "persona #1";
+      this._initial_fullname_2 = "persona #2";
+
+      c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._initial_fullname_1);
+      this._tracker_backend.add_contact (c1);
+
+      c2.set (Trf.OntologyDefs.NCO_FULLNAME, this._initial_fullname_2);
+      c2.set (Trf.OntologyDefs.NAO_TAG, "");
+      this._tracker_backend.add_contact (c2);
+
+      this._tracker_backend.set_up ();
+
+      this._c1_initially_not_favourite = false;
+      this._c1_finally_favourite = false;
+      this._c2_initially_favourite = false;
+      this._c2_finally_not_favourite = false;
+
+      this._test_set_alias_async ();
+
+      Timeout.add_seconds (5, () =>
+        {
+          this._main_loop.quit ();
+          assert_not_reached ();
+        });
+
+      this._main_loop.run ();
+
+      /* Note:
+       *  the is-favourite property is notified as a
+       *  consequence of a value changed event fired by
+       *  Tracker
+       */
+      assert (this._c1_initially_not_favourite);
+      assert (this._c1_finally_favourite);
+      assert (this._c2_initially_favourite);
+      assert (this._c2_finally_not_favourite);
+
+      this._tracker_backend.tear_down ();
+    }
+
+  private async void _test_set_alias_async ()
+    {
+      var store = BackendStore.dup ();
+      yield store.prepare ();
+      this._aggregator = new IndividualAggregator ();
+      this._aggregator.individuals_changed.connect
+          (this._individuals_changed_cb);
+      try
+        {
+          yield this._aggregator.prepare ();
+        }
+      catch (GLib.Error e)
+        {
+          GLib.warning ("Error when calling prepare: %s\n", e.message);
+        }
+    }
+
+ private void _individuals_changed_cb
+      (GLib.List<Individual>? added,
+       GLib.List<Individual>? removed,
+       string? message,
+       Persona? actor,
+       GroupDetails.ChangeReason reason)
+    {
+      foreach (unowned Individual i in added)
+        {
+          i.notify["is-favourite"].connect (this._notify_favourite_cb);
+          if (i.full_name == this._initial_fullname_1)
+            {
+              if (i.is_favourite == false)
+                {
+                  this._c1_initially_not_favourite = true;
+                  Trf.Persona p = (Trf.Persona)i.personas.nth_data (0);
+                  p.is_favourite  = true;
+                }
+            }
+         else if (i.full_name == this._initial_fullname_2)
+           {
+             if (i.is_favourite == true)
+               {
+                 this._c2_initially_favourite = true;
+                 Trf.Persona p = (Trf.Persona)i.personas.nth_data (0);
+                 p.is_favourite  = false;
+               }
+           }
+        }
+
+      assert (removed == null);
+    }
+
+  private void _notify_favourite_cb (Object individual_obj, ParamSpec ps)
+    {
+      Folks.Individual i = (Folks.Individual) individual_obj;
+      if (i.full_name == this._initial_fullname_1)
+        {
+          if (i.is_favourite == true)
+            this._c1_finally_favourite = true;
+        }
+      else if (i.full_name == this._initial_fullname_2)
+        {
+          if (i.is_favourite == false)
+            this._c2_finally_not_favourite = true;
+        }
+
+      if (this._c1_finally_favourite &&
+          this._c2_finally_not_favourite)
+        this._main_loop.quit ();
+    }
+}
+
+public int main (string[] args)
+{
+  Test.init (ref args);
+
+  TestSuite root = TestSuite.get_root ();
+  root.add_suite (new SetFavouriteTests ().get_suite ());
+
+  Test.run ();
+
+  return 0;
+}
diff --git a/tests/tracker/set-full-name.vala b/tests/tracker/set-full-name.vala
new file mode 100644
index 0000000..40c954f
--- /dev/null
+++ b/tests/tracker/set-full-name.vala
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
+ *
+ */
+
+using Tracker.Sparql;
+using TrackerTest;
+using Folks;
+using Gee;
+
+public class SetFullNameTests : Folks.TestCase
+{
+  private GLib.MainLoop _main_loop;
+  private TrackerTest.Backend _tracker_backend;
+  private IndividualAggregator _aggregator;
+  private string _persona_fullname;
+  private bool _found_changed_full_name ;
+  private string _individual_id;
+  private string _modified_fullname;
+
+  public SetFullNameTests ()
+    {
+      base ("SetFullNameTests");
+
+      this._tracker_backend = new TrackerTest.Backend ();
+
+      this.add_test ("test setting structured name ",
+          this.test_set_full_name);
+    }
+
+  public override void set_up ()
+    {
+    }
+
+  public override void tear_down ()
+    {
+    }
+
+  public void test_set_full_name ()
+    {
+      this._main_loop = new GLib.MainLoop (null, false);
+      Gee.HashMap<string, string> c1 = new Gee.HashMap<string, string> ();
+      this._persona_fullname = "persona #1";
+      this._individual_id = "";
+      this._modified_fullname = "modified - persona #1";
+
+      c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._persona_fullname);
+      this._tracker_backend.add_contact (c1);
+
+      this._tracker_backend.set_up ();
+
+      this._found_changed_full_name = false;
+
+      this._test_set_full_name_async ();
+
+      Timeout.add_seconds (5, () =>
+        {
+          this._main_loop.quit ();
+          assert_not_reached ();
+        });
+
+      this._main_loop.run ();
+
+      assert (this._found_changed_full_name);
+
+      this._tracker_backend.tear_down ();
+    }
+
+  private async void _test_set_full_name_async ()
+    {
+      var store = BackendStore.dup ();
+      yield store.prepare ();
+      this._aggregator = new IndividualAggregator ();
+      this._aggregator.individuals_changed.connect
+          (this._individuals_changed_cb);
+      try
+        {
+          yield this._aggregator.prepare ();
+        }
+      catch (GLib.Error e)
+        {
+          GLib.warning ("Error when calling prepare: %s\n", e.message);
+        }
+    }
+
+ private void _individuals_changed_cb
+      (GLib.List<Individual>? added,
+       GLib.List<Individual>? removed,
+       string? message,
+       Persona? actor,
+       GroupDetails.ChangeReason reason)
+    {
+      foreach (unowned Individual i in added)
+        {
+          if (i.full_name == this._persona_fullname)
+            {
+              this._individual_id = i.id;
+              Trf.Persona p = (Trf.Persona)i.personas.nth_data (0);
+              i.notify["full-name"].connect (this._notify_full_name_cb);
+              p.full_name = this._modified_fullname;
+            }
+        }
+
+      assert (removed == null);
+    }
+
+  private void _notify_full_name_cb (Object individual, ParamSpec ps)
+    {
+      Folks.Individual i = (Folks.Individual) individual;
+      if (i.id == this._individual_id &&
+          i.full_name == this._modified_fullname)
+        {
+          this._found_changed_full_name = true;
+          this._main_loop.quit ();
+        }
+    }
+}
+
+public int main (string[] args)
+{
+  Test.init (ref args);
+
+  TestSuite root = TestSuite.get_root ();
+  root.add_suite (new SetFullNameTests ().get_suite ());
+
+  Test.run ();
+
+  return 0;
+}
diff --git a/tests/tracker/set-gender.vala b/tests/tracker/set-gender.vala
new file mode 100644
index 0000000..268baf9
--- /dev/null
+++ b/tests/tracker/set-gender.vala
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
+ *
+ */
+
+using Tracker.Sparql;
+using TrackerTest;
+using Folks;
+using Gee;
+
+public class SetGenderTests : Folks.TestCase
+{
+  private GLib.MainLoop _main_loop;
+  private TrackerTest.Backend _tracker_backend;
+  private IndividualAggregator _aggregator;
+  private string _persona_fullname;
+  private bool _gender_found;
+
+  public SetGenderTests ()
+    {
+      base ("SetGenderTests");
+
+      this._tracker_backend = new TrackerTest.Backend ();
+
+      this.add_test ("test setting gender ",
+          this.test_set_gender);
+    }
+
+  public override void set_up ()
+    {
+    }
+
+  public override void tear_down ()
+    {
+    }
+
+  public void test_set_gender ()
+    {
+      this._main_loop = new GLib.MainLoop (null, false);
+      Gee.HashMap<string, string> c1 = new Gee.HashMap<string, string> ();
+      this._persona_fullname = "persona #1";
+
+      c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._persona_fullname);
+      this._tracker_backend.add_contact (c1);
+
+      this._tracker_backend.set_up ();
+
+      this._gender_found = false;
+
+      this._test_set_gender_async ();
+
+      Timeout.add_seconds (5, () =>
+        {
+          this._main_loop.quit ();
+          assert_not_reached ();
+        });
+
+      this._main_loop.run ();
+
+      assert (this._gender_found);
+
+     this._tracker_backend.tear_down ();
+    }
+
+  private async void _test_set_gender_async ()
+    {
+      var store = BackendStore.dup ();
+      yield store.prepare ();
+      this._aggregator = new IndividualAggregator ();
+      this._aggregator.individuals_changed.connect
+          (this._individuals_changed_cb);
+      try
+        {
+          yield this._aggregator.prepare ();
+        }
+      catch (GLib.Error e)
+        {
+          GLib.warning ("Error when calling prepare: %s\n", e.message);
+        }
+    }
+
+ private void _individuals_changed_cb
+      (GLib.List<Individual>? added,
+       GLib.List<Individual>? removed,
+       string? message,
+       Persona? actor,
+       GroupDetails.ChangeReason reason)
+    {
+      foreach (unowned Individual i in added)
+        {
+          if (i.full_name == this._persona_fullname)
+            {
+              i.notify["gender"].connect (this._notify_gender_cb);
+
+              Trf.Persona p = (Trf.Persona)i.personas.nth_data (0);
+              p.gender = Gender.MALE;
+            }
+        }
+
+      assert (removed == null);
+    }
+
+  private void _notify_gender_cb (Object individual_obj, ParamSpec ps)
+    {
+      Folks.Individual i = (Folks.Individual) individual_obj;
+      if (i.full_name == this._persona_fullname)
+        {
+          if (i.gender == Gender.MALE)
+            {
+              this._gender_found = true;
+              this._main_loop.quit ();
+            }
+        }
+    }
+}
+
+public int main (string[] args)
+{
+  Test.init (ref args);
+
+  TestSuite root = TestSuite.get_root ();
+  root.add_suite (new SetGenderTests ().get_suite ());
+
+  Test.run ();
+
+  return 0;
+}
diff --git a/tests/tracker/set-im-addresses.vala b/tests/tracker/set-im-addresses.vala
new file mode 100644
index 0000000..1f13a4f
--- /dev/null
+++ b/tests/tracker/set-im-addresses.vala
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
+ *
+ */
+
+using Tracker.Sparql;
+using TrackerTest;
+using Folks;
+using Gee;
+
+public class SetIMAddressesTests : Folks.TestCase
+{
+  private GLib.MainLoop _main_loop;
+  private TrackerTest.Backend _tracker_backend;
+  private IndividualAggregator _aggregator;
+  private string _persona_fullname;
+  private GLib.List<string> _addresses =
+    new GLib.List<string> ();
+
+  public SetIMAddressesTests ()
+    {
+      base ("SetIMAddressesTests");
+
+      this._tracker_backend = new TrackerTest.Backend ();
+
+      this.add_test ("test setting im_addresses ", this.test_set_im_addresses);
+    }
+
+  public override void set_up ()
+    {
+    }
+
+  public override void tear_down ()
+    {
+    }
+
+  public void test_set_im_addresses ()
+    {
+      this._main_loop = new GLib.MainLoop (null, false);
+      Gee.HashMap<string, string> c1 =
+        new Gee.HashMap<string, string> ();
+      this._persona_fullname = "persona #1";
+
+      c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._persona_fullname);
+      this._tracker_backend.add_contact (c1);
+
+      this._addresses.prepend ("one example org");
+      this._addresses.prepend ("two example org");
+      this._addresses.prepend ("three example org");
+      this._addresses.prepend ("four example org");
+
+      this._tracker_backend.set_up ();
+
+      this._test_set_im_addresses_async ();
+
+      Timeout.add_seconds (5, () =>
+        {
+          this._main_loop.quit ();
+          assert_not_reached ();
+        });
+
+      this._main_loop.run ();
+
+      assert (this._addresses.length () == 0);
+
+      this._tracker_backend.tear_down ();
+    }
+
+  private async void _test_set_im_addresses_async ()
+    {
+      var store = BackendStore.dup ();
+      yield store.prepare ();
+      this._aggregator = new IndividualAggregator ();
+      this._aggregator.individuals_changed.connect
+          (this._individuals_changed_cb);
+      try
+        {
+          yield this._aggregator.prepare ();
+        }
+        catch (GLib.Error e)
+        {
+          GLib.warning ("Error when calling prepare: %s\n", e.message);
+        }
+    }
+
+ private void _individuals_changed_cb
+      (GLib.List<Individual>? added,
+       GLib.List<Individual>? removed,
+       string? message,
+       Persona? actor,
+       GroupDetails.ChangeReason reason)
+    {
+      foreach (unowned Individual i in added)
+        {
+          if (i.full_name == this._persona_fullname)
+            {
+              i.notify["im-addresses"].connect (this._notify_im_addresses_cb);
+
+              HashTable<string, LinkedHashSet<string>> im_addresses =
+                new HashTable<string, LinkedHashSet<string>>
+                  (str_hash, str_equal);
+
+              var addrs_1 = new LinkedHashSet<string> ();
+              addrs_1.add ("one example org");
+              addrs_1.add ("two example org");
+              im_addresses.insert ("aim",
+                  (owned) addrs_1);
+
+              var addrs_2 = new LinkedHashSet<string> ();
+              addrs_2.add ("three example org");
+              addrs_2.add ("four example org");
+              im_addresses.insert ("yahoo",
+                  (owned) addrs_2);
+
+              Trf.Persona p = (Trf.Persona)i.personas.nth_data (0);
+              p.im_addresses = (owned) im_addresses;
+            }
+        }
+
+      assert (removed == null);
+    }
+
+  private void _notify_im_addresses_cb (Object individual_obj, ParamSpec ps)
+    {
+      Folks.Individual i = (Folks.Individual) individual_obj;
+      if (i.full_name == this._persona_fullname)
+        {
+          foreach (var proto in i.im_addresses.get_keys ())
+            {
+              var addrs = i.im_addresses.lookup (proto);
+              foreach (var a in addrs)
+                {
+                  foreach (unowned string my_a in this._addresses)
+                    {
+                      if (my_a == a)
+                        {
+                          this._addresses.remove (my_a);
+                          break;
+                        }
+                    }
+                }
+            }
+
+            if (this._addresses.length () == 0)
+            {
+              this._main_loop.quit ();
+            }
+        }
+    }
+}
+
+public int main (string[] args)
+{
+  Test.init (ref args);
+
+  TestSuite root = TestSuite.get_root ();
+  root.add_suite (new SetIMAddressesTests ().get_suite ());
+
+  Test.run ();
+
+  return 0;
+}
diff --git a/tests/tracker/set-notes.vala b/tests/tracker/set-notes.vala
new file mode 100644
index 0000000..7ff3667
--- /dev/null
+++ b/tests/tracker/set-notes.vala
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
+ *
+ */
+
+using Tracker.Sparql;
+using TrackerTest;
+using Folks;
+using Gee;
+
+public class SetNotesTests : Folks.TestCase
+{
+  private GLib.MainLoop _main_loop;
+  private TrackerTest.Backend _tracker_backend;
+  private IndividualAggregator _aggregator;
+  private string _persona_fullname;
+  private bool _note_found;
+  private Note _note;
+
+  public SetNotesTests ()
+    {
+      base ("SetNotesTests");
+
+      this._tracker_backend = new TrackerTest.Backend ();
+
+      this.add_test ("test setting notes ",
+          this.test_set_notes);
+    }
+
+  public override void set_up ()
+    {
+    }
+
+  public override void tear_down ()
+    {
+    }
+
+  public void test_set_notes ()
+    {
+      this._main_loop = new GLib.MainLoop (null, false);
+      Gee.HashMap<string, string> c1 = new Gee.HashMap<string, string> ();
+      this._persona_fullname = "persona #1";
+
+      c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._persona_fullname);
+      this._tracker_backend.add_contact (c1);
+
+      this._note = new Note ("some note");
+
+      this._tracker_backend.set_up ();
+
+      this._note_found = false;
+
+      this._test_set_notes_async ();
+
+      Timeout.add_seconds (5, () =>
+        {
+          this._main_loop.quit ();
+          assert_not_reached ();
+        });
+
+      this._main_loop.run ();
+
+      assert (this._note_found);
+
+     this._tracker_backend.tear_down ();
+    }
+
+  private async void _test_set_notes_async ()
+    {
+      var store = BackendStore.dup ();
+      yield store.prepare ();
+      this._aggregator = new IndividualAggregator ();
+      this._aggregator.individuals_changed.connect
+          (this._individuals_changed_cb);
+      try
+        {
+          yield this._aggregator.prepare ();
+        }
+      catch (GLib.Error e)
+        {
+          GLib.warning ("Error when calling prepare: %s\n", e.message);
+        }
+    }
+
+ private void _individuals_changed_cb
+      (GLib.List<Individual>? added,
+       GLib.List<Individual>? removed,
+       string? message,
+       Persona? actor,
+       GroupDetails.ChangeReason reason)
+    {
+      foreach (unowned Individual i in added)
+        {
+          if (i.full_name == this._persona_fullname)
+            {
+              i.notify["notes"].connect (this._notify_notes_cb);
+
+              Gee.HashSet<Note> notes = new HashSet<Note>
+                  ((GLib.HashFunc) Note.hash, (GLib.EqualFunc) Note.equal);
+              var n = new Note ("some note");
+              notes.add ((owned) n);
+
+              Trf.Persona p = (Trf.Persona)i.personas.nth_data (0);
+              p.notes = (owned) notes;
+            }
+        }
+
+      assert (removed == null);
+    }
+
+  private void _notify_notes_cb (Object individual_obj, ParamSpec ps)
+    {
+      Folks.Individual i = (Folks.Individual) individual_obj;
+      if (i.full_name == this._persona_fullname)
+        {
+          foreach (var n in i.notes)
+            {
+              if (Note.equal (n, this._note))
+                {
+                  this._note_found = true;
+                  this._main_loop.quit ();
+                }
+            }
+        }
+    }
+}
+
+public int main (string[] args)
+{
+  Test.init (ref args);
+
+  TestSuite root = TestSuite.get_root ();
+  root.add_suite (new SetNotesTests ().get_suite ());
+
+  Test.run ();
+
+  return 0;
+}
diff --git a/tests/tracker/set-phones.vala b/tests/tracker/set-phones.vala
new file mode 100644
index 0000000..00409d8
--- /dev/null
+++ b/tests/tracker/set-phones.vala
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
+ *
+ */
+
+using Tracker.Sparql;
+using TrackerTest;
+using Folks;
+using Gee;
+
+public class SetPhonesTests : Folks.TestCase
+{
+  private GLib.MainLoop _main_loop;
+  private TrackerTest.Backend _tracker_backend;
+  private IndividualAggregator _aggregator;
+  private string _persona_fullname;
+  private string _phone_1;
+  private string _phone_2;
+  private bool _phone_1_found;
+  private bool _phone_2_found;
+
+  public SetPhonesTests ()
+    {
+      base ("SetPhonesTests");
+
+      this._tracker_backend = new TrackerTest.Backend ();
+
+      this.add_test ("test setting phones ", this.test_set_phones);
+    }
+
+  public override void set_up ()
+    {
+    }
+
+  public override void tear_down ()
+    {
+    }
+
+  public void test_set_phones ()
+    {
+      this._main_loop = new GLib.MainLoop (null, false);
+      Gee.HashMap<string, string> c1 = new Gee.HashMap<string, string> ();
+      this._persona_fullname = "persona #1";
+      this._phone_1 = "12345";
+      this._phone_2 = "54321";
+
+      c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._persona_fullname);
+      this._tracker_backend.add_contact (c1);
+
+      this._tracker_backend.set_up ();
+
+      this._phone_1_found = false;
+      this._phone_2_found = false;
+
+      this._test_set_phones_async ();
+
+      Timeout.add_seconds (5, () =>
+        {
+          this._main_loop.quit ();
+          assert_not_reached ();
+        });
+
+      this._main_loop.run ();
+
+      assert (this._phone_1_found);
+      assert (this._phone_2_found);
+
+     this._tracker_backend.tear_down ();
+    }
+
+  private async void _test_set_phones_async ()
+    {
+      var store = BackendStore.dup ();
+      yield store.prepare ();
+      this._aggregator = new IndividualAggregator ();
+      this._aggregator.individuals_changed.connect
+          (this._individuals_changed_cb);
+      try
+        {
+          yield this._aggregator.prepare ();
+        }
+      catch (GLib.Error e)
+        {
+          GLib.warning ("Error when calling prepare: %s\n", e.message);
+        }
+    }
+
+ private void _individuals_changed_cb
+      (GLib.List<Individual>? added,
+       GLib.List<Individual>? removed,
+       string? message,
+       Persona? actor,
+       GroupDetails.ChangeReason reason)
+    {
+      foreach (unowned Individual i in added)
+        {
+          if (i.full_name == this._persona_fullname)
+            {
+              i.notify["phone-numbers"].connect (this._notify_phones_cb);
+
+              GLib.List<FieldDetails> phones = new GLib.List<FieldDetails> ();
+              var p1 = new FieldDetails (this._phone_1);
+              phones.prepend ((owned) p1);
+              var p2 = new FieldDetails (this._phone_2);
+              phones.prepend ((owned) p2);
+              Trf.Persona p = (Trf.Persona)i.personas.nth_data (0);
+              p.phone_numbers = (owned) phones;
+            }
+        }
+
+      assert (removed == null);
+    }
+
+  private void _notify_phones_cb (Object individual_obj, ParamSpec ps)
+    {
+      Folks.Individual i = (Folks.Individual) individual_obj;
+      if (i.full_name == this._persona_fullname)
+        {
+          foreach (unowned FieldDetails p in i.phone_numbers)
+            {
+              if (p.value == this._phone_1)
+                this._phone_1_found = true;
+              else if (p.value == this._phone_2)
+                this._phone_2_found = true;
+            }
+        }
+
+      if (this._phone_1_found && this._phone_2_found)
+        {
+          this._main_loop.quit ();
+        }
+    }
+}
+
+public int main (string[] args)
+{
+  Test.init (ref args);
+
+  TestSuite root = TestSuite.get_root ();
+  root.add_suite (new SetPhonesTests ().get_suite ());
+
+  Test.run ();
+
+  return 0;
+}
diff --git a/tests/tracker/set-postal-addresses.vala b/tests/tracker/set-postal-addresses.vala
new file mode 100644
index 0000000..f887b1a
--- /dev/null
+++ b/tests/tracker/set-postal-addresses.vala
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
+ *
+ */
+
+using Tracker.Sparql;
+using TrackerTest;
+using Folks;
+using Gee;
+
+public class SetPostalAddressesTests : Folks.TestCase
+{
+  private GLib.MainLoop _main_loop;
+  private TrackerTest.Backend _tracker_backend;
+  private IndividualAggregator _aggregator;
+  private string _persona_fullname;
+  private bool _postal_address_found;
+  private PostalAddress _address;
+
+  public SetPostalAddressesTests ()
+    {
+      base ("SetPostalAddressesTests");
+
+      this._tracker_backend = new TrackerTest.Backend ();
+
+      this.add_test ("test setting postal addresses ",
+          this.test_set_postal_addresses);
+    }
+
+  public override void set_up ()
+    {
+    }
+
+  public override void tear_down ()
+    {
+    }
+
+  public void test_set_postal_addresses ()
+    {
+      this._main_loop = new GLib.MainLoop (null, false);
+      Gee.HashMap<string, string> c1 = new Gee.HashMap<string, string> ();
+      this._persona_fullname = "persona #1";
+
+      c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._persona_fullname);
+      this._tracker_backend.add_contact (c1);
+
+      GLib.List<string> types =  new GLib.List<string> ();
+      this._address = new PostalAddress (null, null, null, null, null,
+          null, null, null, types, null);
+      this._address.po_box = "12345";
+      this._address.locality = "locality";
+      this._address.postal_code = "code";
+      this._address.street = "some street";
+      this._address.extension = "some extension";
+      this._address.country = "some country";
+      this._address.region = "some region";
+
+      this._tracker_backend.set_up ();
+
+      this._postal_address_found = false;
+
+      this._test_set_postal_addresses_async ();
+
+      Timeout.add_seconds (5, () =>
+        {
+          this._main_loop.quit ();
+          assert_not_reached ();
+        });
+
+      this._main_loop.run ();
+
+      assert (this._postal_address_found);
+
+     this._tracker_backend.tear_down ();
+    }
+
+  private async void _test_set_postal_addresses_async ()
+    {
+      var store = BackendStore.dup ();
+      yield store.prepare ();
+      this._aggregator = new IndividualAggregator ();
+      this._aggregator.individuals_changed.connect
+          (this._individuals_changed_cb);
+      try
+        {
+          yield this._aggregator.prepare ();
+        }
+      catch (GLib.Error e)
+        {
+          GLib.warning ("Error when calling prepare: %s\n", e.message);
+        }
+    }
+
+ private void _individuals_changed_cb
+      (GLib.List<Individual>? added,
+       GLib.List<Individual>? removed,
+       string? message,
+       Persona? actor,
+       GroupDetails.ChangeReason reason)
+    {
+      foreach (unowned Individual i in added)
+        {
+          if (i.full_name == this._persona_fullname)
+            {
+              i.notify["postal-addresses"].connect (this._notify_postal_cb);
+
+              GLib.List<string> types =  new GLib.List<string> ();
+              GLib.List<PostalAddress> addresses =
+                new GLib.List<PostalAddress> ();
+              var pa = new Folks.PostalAddress (null, null, null, null, null,
+                null, null, null, types, null);
+              pa.po_box = this._address.po_box;
+              pa.locality = this._address.locality;
+              pa.postal_code =this._address.postal_code;
+              pa.street = this._address.street;
+              pa.extension = this._address.extension;
+              pa.country = this._address.country;
+              pa.region  = this._address.region;
+
+              addresses.prepend ((owned) pa);
+
+              Trf.Persona p = (Trf.Persona)i.personas.nth_data (0);
+              p.postal_addresses = (owned) addresses;
+            }
+        }
+
+      assert (removed == null);
+    }
+
+  private void _notify_postal_cb (Object individual_obj, ParamSpec ps)
+    {
+      Folks.Individual i = (Folks.Individual) individual_obj;
+      if (i.full_name == this._persona_fullname)
+        {
+          foreach (unowned PostalAddress p in i.postal_addresses)
+            {
+              /* we don't care if UIDs differ for this test */
+              this._address.uid = p.uid;
+              if (p.equal (this._address))
+                {
+                  this._postal_address_found = true;
+                  this._main_loop.quit ();
+                }
+            }
+        }
+    }
+}
+
+public int main (string[] args)
+{
+  Test.init (ref args);
+
+  TestSuite root = TestSuite.get_root ();
+  root.add_suite (new SetPostalAddressesTests ().get_suite ());
+
+  Test.run ();
+
+  return 0;
+}
diff --git a/tests/tracker/set-roles.vala b/tests/tracker/set-roles.vala
new file mode 100644
index 0000000..3924de2
--- /dev/null
+++ b/tests/tracker/set-roles.vala
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
+ *
+ */
+
+using Tracker.Sparql;
+using TrackerTest;
+using Folks;
+using Gee;
+
+public class SetRolesTests : Folks.TestCase
+{
+  private GLib.MainLoop _main_loop;
+  private TrackerTest.Backend _tracker_backend;
+  private IndividualAggregator _aggregator;
+  private string _persona_fullname;
+  private bool _role_found;
+  private Role _role;
+
+  public SetRolesTests ()
+    {
+      base ("SetRolesTests");
+
+      this._tracker_backend = new TrackerTest.Backend ();
+
+      this.add_test ("test setting roles ",
+          this.test_set_roles);
+    }
+
+  public override void set_up ()
+    {
+    }
+
+  public override void tear_down ()
+    {
+    }
+
+  public void test_set_roles ()
+    {
+      this._main_loop = new GLib.MainLoop (null, false);
+      Gee.HashMap<string, string> c1 = new Gee.HashMap<string, string> ();
+      this._persona_fullname = "persona #1";
+
+      c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._persona_fullname);
+      this._tracker_backend.add_contact (c1);
+
+      this._role = new Role ("some title", "some organisation");
+
+      this._tracker_backend.set_up ();
+
+      this._role_found = false;
+
+      this._test_set_roles_async ();
+
+      Timeout.add_seconds (5, () =>
+        {
+          this._main_loop.quit ();
+          assert_not_reached ();
+        });
+
+      this._main_loop.run ();
+
+      assert (this._role_found);
+
+     this._tracker_backend.tear_down ();
+    }
+
+  private async void _test_set_roles_async ()
+    {
+      var store = BackendStore.dup ();
+      yield store.prepare ();
+      this._aggregator = new IndividualAggregator ();
+      this._aggregator.individuals_changed.connect
+          (this._individuals_changed_cb);
+      try
+        {
+          yield this._aggregator.prepare ();
+        }
+      catch (GLib.Error e)
+        {
+          GLib.warning ("Error when calling prepare: %s\n", e.message);
+        }
+    }
+
+ private void _individuals_changed_cb
+      (GLib.List<Individual>? added,
+       GLib.List<Individual>? removed,
+       string? message,
+       Persona? actor,
+       GroupDetails.ChangeReason reason)
+    {
+      foreach (unowned Individual i in added)
+        {
+          if (i.full_name == this._persona_fullname)
+            {
+              i.notify["roles"].connect (this._notify_roles_cb);
+
+              Gee.HashSet<Role> roles = new HashSet<Role>
+                  ((GLib.HashFunc) Role.hash, (GLib.EqualFunc) Role.equal);
+              var r = new Role ("some title", "some organisation");
+              roles.add ((owned) r);
+
+              Trf.Persona p = (Trf.Persona)i.personas.nth_data (0);
+              p.roles = (owned) roles;
+            }
+        }
+
+      assert (removed == null);
+    }
+
+  private void _notify_roles_cb (Object individual_obj, ParamSpec ps)
+    {
+      Folks.Individual i = (Folks.Individual) individual_obj;
+      if (i.full_name == this._persona_fullname)
+        {
+          foreach (var r in i.roles)
+            {
+              if (Role.equal (r, this._role))
+                {
+                  this._role_found = true;
+                  this._main_loop.quit ();
+                }
+            }
+        }
+    }
+}
+
+public int main (string[] args)
+{
+  Test.init (ref args);
+
+  TestSuite root = TestSuite.get_root ();
+  root.add_suite (new SetRolesTests ().get_suite ());
+
+  Test.run ();
+
+  return 0;
+}
diff --git a/tests/tracker/set-structured-name.vala b/tests/tracker/set-structured-name.vala
new file mode 100644
index 0000000..b2a3d30
--- /dev/null
+++ b/tests/tracker/set-structured-name.vala
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
+ *
+ */
+
+using Tracker.Sparql;
+using TrackerTest;
+using Folks;
+using Gee;
+
+public class SetStructuredNameTests : Folks.TestCase
+{
+  private GLib.MainLoop _main_loop;
+  private TrackerTest.Backend _tracker_backend;
+  private IndividualAggregator _aggregator;
+  private string _persona_fullname;
+  private bool _sname_found;
+  private StructuredName _sname;
+  private string _family_name;
+  private string _given_name;
+  private string _additional_names;
+  private string _prefixes;
+  private string _suffixes;
+
+  public SetStructuredNameTests ()
+    {
+      base ("SetStructuredNameTests");
+
+      this._tracker_backend = new TrackerTest.Backend ();
+
+      this.add_test ("test setting structured name ",
+          this.test_set_structured_name);
+    }
+
+  public override void set_up ()
+    {
+    }
+
+  public override void tear_down ()
+    {
+    }
+
+  public void test_set_structured_name ()
+    {
+      this._main_loop = new GLib.MainLoop (null, false);
+      Gee.HashMap<string, string> c1 = new Gee.HashMap<string, string> ();
+      this._persona_fullname = "persona #1";
+      this._family_name = "family name";
+      this._given_name = "given name";
+      this._additional_names = "additional name";
+      this._prefixes = "prefixes";
+      this._suffixes = "suffixes";
+
+      this._sname = new StructuredName (this._family_name, this._given_name,
+          this._additional_names, this._prefixes, this._suffixes);
+
+      c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._persona_fullname);
+      this._tracker_backend.add_contact (c1);
+
+      this._tracker_backend.set_up ();
+
+      this._sname_found = false;
+
+      this._test_set_structured_name_async ();
+
+      Timeout.add_seconds (5, () =>
+        {
+          this._main_loop.quit ();
+          assert_not_reached ();
+        });
+
+      this._main_loop.run ();
+
+      assert (this._sname_found);
+
+      this._tracker_backend.tear_down ();
+    }
+
+  private async void _test_set_structured_name_async ()
+    {
+      var store = BackendStore.dup ();
+      yield store.prepare ();
+      this._aggregator = new IndividualAggregator ();
+      this._aggregator.individuals_changed.connect
+          (this._individuals_changed_cb);
+      try
+        {
+          yield this._aggregator.prepare ();
+        }
+      catch (GLib.Error e)
+        {
+          GLib.warning ("Error when calling prepare: %s\n", e.message);
+        }
+    }
+
+ private void _individuals_changed_cb
+      (GLib.List<Individual>? added,
+       GLib.List<Individual>? removed,
+       string? message,
+       Persona? actor,
+       GroupDetails.ChangeReason reason)
+    {
+      foreach (unowned Individual i in added)
+        {
+          if (i.full_name == this._persona_fullname)
+            {
+              Trf.Persona p = (Trf.Persona)i.personas.nth_data (0);
+              p.notify["structured-name"].connect (this._notify_sname_cb);
+              p.structured_name = this._sname;
+            }
+        }
+
+      assert (removed == null);
+    }
+
+  private void _notify_sname_cb (Object persona, ParamSpec ps)
+    {
+      Trf.Persona p = (Trf.Persona) persona;
+      if (p.full_name == this._persona_fullname)
+        {
+          if (p.structured_name.is_empty () == false &&
+              p.structured_name.equal (this._sname) == true)
+            {
+              this._sname_found = true;
+              this._main_loop.quit ();
+            }
+        }
+    }
+}
+
+public int main (string[] args)
+{
+  Test.init (ref args);
+
+  TestSuite root = TestSuite.get_root ();
+  root.add_suite (new SetStructuredNameTests ().get_suite ());
+
+  Test.run ();
+
+  return 0;
+}
diff --git a/tests/tracker/set-urls.vala b/tests/tracker/set-urls.vala
new file mode 100644
index 0000000..d7e56e7
--- /dev/null
+++ b/tests/tracker/set-urls.vala
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
+ *
+ */
+
+using Tracker.Sparql;
+using TrackerTest;
+using Folks;
+using Gee;
+
+public class SetURLsTests : Folks.TestCase
+{
+  private GLib.MainLoop _main_loop;
+  private TrackerTest.Backend _tracker_backend;
+  private IndividualAggregator _aggregator;
+  private string _persona_fullname;
+  Gee.HashMap<string, string> _urls;
+
+  public SetURLsTests ()
+    {
+      base ("SetURLsTests");
+
+      this._tracker_backend = new TrackerTest.Backend ();
+
+      this.add_test ("test setting urls ", this.test_set_urls);
+    }
+
+  public override void set_up ()
+    {
+    }
+
+  public override void tear_down ()
+    {
+    }
+
+  public void test_set_urls ()
+    {
+      this._main_loop = new GLib.MainLoop (null, false);
+      Gee.HashMap<string, string> c1 = new Gee.HashMap<string, string> ();
+      this._persona_fullname = "persona #1";
+      this._urls = new Gee.HashMap<string, string> ();
+      this._urls.set ("blog", "http://one.example.org";);
+      this._urls.set ("website", "http://two.example.org";);
+      this._urls.set ("url", "http://three.example.org";);
+
+      c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._persona_fullname);
+      this._tracker_backend.add_contact (c1);
+
+      this._tracker_backend.set_up ();
+
+      this._test_set_urls_async ();
+
+      Timeout.add_seconds (5, () =>
+        {
+          this._main_loop.quit ();
+          assert_not_reached ();
+        });
+
+      this._main_loop.run ();
+
+      assert (this._urls.size == 0);
+
+     this._tracker_backend.tear_down ();
+    }
+
+  private async void _test_set_urls_async ()
+    {
+      var store = BackendStore.dup ();
+      yield store.prepare ();
+      this._aggregator = new IndividualAggregator ();
+      this._aggregator.individuals_changed.connect
+          (this._individuals_changed_cb);
+      try
+        {
+          yield this._aggregator.prepare ();
+        }
+      catch (GLib.Error e)
+        {
+          GLib.warning ("Error when calling prepare: %s\n", e.message);
+        }
+    }
+
+ private void _individuals_changed_cb
+      (GLib.List<Individual>? added,
+       GLib.List<Individual>? removed,
+       string? message,
+       Persona? actor,
+       GroupDetails.ChangeReason reason)
+    {
+      foreach (unowned Individual i in added)
+        {
+          if (i.full_name == this._persona_fullname)
+            {
+              i.notify["urls"].connect (this._notify_urls_cb);
+
+              GLib.List<FieldDetails> urls = new GLib.List<FieldDetails> ();
+              var p1 = new FieldDetails (this._urls.get ("blog"));
+              p1.set_parameter ("type", "blog");
+              urls.prepend ((owned) p1);
+              var p2 = new FieldDetails (this._urls.get ("website"));
+              p2.set_parameter ("type", "website");
+              urls.prepend ((owned) p2);
+              var p3 = new FieldDetails (this._urls.get ("url"));
+              p3.set_parameter ("type", "url");
+              urls.prepend ((owned) p3);
+
+              Trf.Persona p = (Trf.Persona)i.personas.nth_data (0);
+              p.urls = (owned) urls;
+            }
+        }
+
+      assert (removed == null);
+    }
+
+  private void _notify_urls_cb (Object individual_obj, ParamSpec ps)
+    {
+      Folks.Individual i = (Folks.Individual) individual_obj;
+      if (i.full_name == this._persona_fullname)
+        {
+          foreach (unowned FieldDetails p in i.urls)
+            {
+              unowned GLib.List<string> type_p =
+                p.get_parameter_values ("type");
+              string type = type_p.nth_data (0);
+
+              if (type == "blog" && p.value == this._urls.get ("blog"))
+                this._urls.unset ("blog");
+              else if (type == "website" &&
+                  p.value == this._urls.get ("website"))
+                this._urls.unset ("website");
+              else if (type == "url" && p.value == this._urls.get ("url"))
+                this._urls.unset ("url");
+            }
+        }
+
+      if (this._urls.size == 0)
+        this._main_loop.quit ();
+    }
+}
+
+public int main (string[] args)
+{
+  Test.init (ref args);
+
+  TestSuite root = TestSuite.get_root ();
+  root.add_suite (new SetURLsTests ().get_suite ());
+
+  Test.run ();
+
+  return 0;
+}
diff --git a/tests/tracker/suffix-name-updates.vala b/tests/tracker/suffix-name-updates.vala
index 0696567..aded7bb 100644
--- a/tests/tracker/suffix-name-updates.vala
+++ b/tests/tracker/suffix-name-updates.vala
@@ -118,6 +118,8 @@ public class SuffixNameUpdatesTests : Folks.TestCase
     {
       foreach (unowned Individual i in added)
         {
+          if (this._initial_fullname == i.full_name)
+            {
               var suffix_name = i.structured_name.suffixes;
               if (suffix_name == this._initial_suffix_name)
                 {
@@ -128,6 +130,7 @@ public class SuffixNameUpdatesTests : Folks.TestCase
                   this._tracker_backend.update_contact (this._contact_urn,
                       Trf.OntologyDefs.NCO_SUFFIX, this._updated_suffix_name);
                 }
+            }
         }
 
       assert (removed == null);



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