From 8b35f9a6498d48225e31b2dd4a86fd1dc64c664e Mon Sep 17 00:00:00 2001 From: Raul Gutierrez Segales Date: Fri, 18 Mar 2011 14:45:23 +0000 Subject: [PATCH] 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(-) create mode 100644 tests/tracker/add-persona.vala create mode 100644 tests/tracker/remove-persona.vala create mode 100644 tests/tracker/set-alias.vala create mode 100644 tests/tracker/set-avatar.vala create mode 100644 tests/tracker/set-birthday.vala create mode 100644 tests/tracker/set-emails.vala create mode 100644 tests/tracker/set-favourite.vala create mode 100644 tests/tracker/set-full-name.vala create mode 100644 tests/tracker/set-gender.vala create mode 100644 tests/tracker/set-im-addresses.vala create mode 100644 tests/tracker/set-notes.vala create mode 100644 tests/tracker/set-phones.vala create mode 100644 tests/tracker/set-postal-addresses.vala create mode 100644 tests/tracker/set-roles.vala create mode 100644 tests/tracker/set-structured-name.vala create mode 100644 tests/tracker/set-urls.vala 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 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 email_addresses = + (GLib.List) 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>) 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) 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 phone_numbers = + (GLib.List) 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) 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 postal_addresses = + (GLib.List) 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 urls = + (GLib.List) 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 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 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 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 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 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 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 (); - var added_personas = new Queue (); + 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 (); 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 (); + 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 _do_add_contacts (string query) { - try { - var added_personas = new Queue (); + var added_personas = new Queue (); + + 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 rows = yield this._multi_value_query (query); + foreach (var r in rows) + { + return r; + } + return ""; + } + + private async Gee.HashSet _multi_value_query (string query) + { + Gee.HashSet ret = new Gee.HashSet (); + + 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 phone_numbers) + { + yield this._set_attrib (persona, (owned) phone_numbers, + Trf.Attrib.PHONES); + } + + internal async void _set_emails (Folks.Persona persona, + owned GLib.List emails) + { + yield this._set_attrib (persona, (owned) emails, + Trf.Attrib.EMAILS); + } + + internal async void _set_urls (Folks.Persona persona, + owned GLib.List urls) + { + yield this._set_attrib (persona, (owned) urls, + Trf.Attrib.URLS); + } + + internal async void _set_im_addresses (Folks.Persona persona, + owned HashTable> 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 ims = new GLib.List (); + 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 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 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 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 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 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 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 _affiliations_from_persona (string urn) + { + return yield this._linked_resources (urn, Trf.OntologyDefs.NCO_PERSON, + Trf.OntologyDefs.NCO_HAS_AFFILIATION); + } + + private async Gee.HashSet _phones_from_affiliation (string affl) + { + return yield this._linked_resources (affl, + Trf.OntologyDefs.NCO_AFFILIATION, + Trf.OntologyDefs.NCO_HAS_PHONE); + } + + private async Gee.HashSet _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 _imaddrs_from_affiliation (string affl) + { + return yield this._linked_resources (affl, + Trf.OntologyDefs.NCO_AFFILIATION, + Trf.OntologyDefs.NCO_HAS_IMADDRESS); + } + + private async Gee.HashSet _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 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 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 format) + */ + private async Gee.HashSet _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 _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 phone_numbers { get { return this._phone_numbers; } - private set + public set { - this._phone_numbers = new GLib.List (); - foreach (unowned FieldDetails ps in value) + var _temp = new GLib.List (); + 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 email_addresses { get { return this._email_addresses; } - private set + public set { - this._email_addresses = new GLib.List (); + var _temp = new GLib.List (); 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 _roles = new HashSet ((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 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 urls { get { return this._urls; } - private set + public set { - this._urls = new GLib.List (); - foreach (unowned FieldDetails ps in value) - this._urls.prepend (ps); - this._urls.reverse (); + var _temp = new GLib.List (); + 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 (); - foreach (PostalAddress pa in value) - this._postal_addresses.prepend (pa); - this._postal_addresses.reverse (); - this.notify_property ("postal-addresses"); + var _temp = new GLib.List (); + 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> 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 = " c1 = new Gee.HashMap (); + 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 c1 = new Gee.HashMap (); - 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 . + * + * Authors: Raul Gutierrez Segales + * + */ + +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 _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 types = new GLib.List (); + 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 + (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 details = new HashTable + (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)); + GLib.List emails = + new GLib.List (); + 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>)); + HashTable> im_addrs = + new HashTable> (null, null); + LinkedHashSet proto1 = new LinkedHashSet (); + proto1.add (this._im_addr_1); + im_addrs.insert ("jabber", proto1); + LinkedHashSet proto2 = new LinkedHashSet (); + 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)); + Gee.HashSet notes = new Gee.HashSet (); + 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)); + GLib.List phones = + new GLib.List (); + 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)); + Gee.HashSet roles = new Gee.HashSet (); + 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)); + GLib.List postal_addresses = + new GLib.List (); + + GLib.List types = new GLib.List (); + 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)); + GLib.List urls = + new GLib.List (); + 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? added, + GLib.List? 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 c1 = new Gee.HashMap (); - 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 = ""; 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 (); 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 c1 = new Gee.HashMap (); this._initial_fullname = "persona #1"; - this._initial_nickname = "nickname #1"; this._updated_nickname = "updated nickname #1"; this._contact_urn = ""; 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 = ""; + this._tracker_backend.insert_triplet (im_addr, + "a", Trf.OntologyDefs.NCO_IMADDRESS, + Trf.OntologyDefs.NCO_IM_NICKNAME, this._updated_nickname); + + var 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 . + * + * Authors: Raul Gutierrez Segales + * + */ + +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 details = new HashTable + (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)); + GLib.List emails = + new GLib.List (); + 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? added, + GLib.List? 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 . + * + * Authors: Raul Gutierrez Segales + * + */ + +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 c1 = new Gee.HashMap (); + 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? added, + GLib.List? 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 . + * + * Authors: Raul Gutierrez Segales + * + */ + +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 c1 = new Gee.HashMap (); + 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? added, + GLib.List? 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 . + * + * Authors: Raul Gutierrez Segales + * + */ + +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 c1 = new Gee.HashMap (); + 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? added, + GLib.List? 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 . + * + * Authors: Raul Gutierrez Segales + * + */ + +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 c1 = new Gee.HashMap (); + 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? added, + GLib.List? 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 emails = new GLib.List (); + 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 . + * + * Authors: Raul Gutierrez Segales + * + */ + +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 c1 = new Gee.HashMap (); + Gee.HashMap c2 = new Gee.HashMap (); + 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? added, + GLib.List? 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 . + * + * Authors: Raul Gutierrez Segales + * + */ + +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 c1 = new Gee.HashMap (); + 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? added, + GLib.List? 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 . + * + * Authors: Raul Gutierrez Segales + * + */ + +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 c1 = new Gee.HashMap (); + 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? added, + GLib.List? 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 . + * + * Authors: Raul Gutierrez Segales + * + */ + +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 _addresses = + new GLib.List (); + + 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 c1 = + new Gee.HashMap (); + 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? added, + GLib.List? 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> im_addresses = + new HashTable> + (str_hash, str_equal); + + var addrs_1 = new LinkedHashSet (); + addrs_1.add ("one@example.org"); + addrs_1.add ("two@example.org"); + im_addresses.insert ("aim", + (owned) addrs_1); + + var addrs_2 = new LinkedHashSet (); + 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 . + * + * Authors: Raul Gutierrez Segales + * + */ + +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 c1 = new Gee.HashMap (); + 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? added, + GLib.List? 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 notes = new HashSet + ((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 . + * + * Authors: Raul Gutierrez Segales + * + */ + +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 c1 = new Gee.HashMap (); + 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? added, + GLib.List? 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 phones = new GLib.List (); + 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 . + * + * Authors: Raul Gutierrez Segales + * + */ + +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 c1 = new Gee.HashMap (); + this._persona_fullname = "persona #1"; + + c1.set (Trf.OntologyDefs.NCO_FULLNAME, this._persona_fullname); + this._tracker_backend.add_contact (c1); + + GLib.List types = new GLib.List (); + 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? added, + GLib.List? 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 types = new GLib.List (); + GLib.List addresses = + new GLib.List (); + 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 . + * + * Authors: Raul Gutierrez Segales + * + */ + +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 c1 = new Gee.HashMap (); + 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? added, + GLib.List? 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 roles = new HashSet + ((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 . + * + * Authors: Raul Gutierrez Segales + * + */ + +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 c1 = new Gee.HashMap (); + 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? added, + GLib.List? 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 . + * + * Authors: Raul Gutierrez Segales + * + */ + +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 _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 c1 = new Gee.HashMap (); + this._persona_fullname = "persona #1"; + this._urls = new Gee.HashMap (); + 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? added, + GLib.List? 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 urls = new GLib.List (); + 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 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); -- 2.7.4