2 * Copyright (C) 2011 Collabora Ltd.
4 * This library is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 2.1 of the License, or
7 * (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library. If not, see <http://www.gnu.org/licenses/>.
17 * Authors: Raul Gutierrez Segales <raul.gutierrez.segales@collabora.co.uk>
28 WEB_SERVICE_ADDRESSES,
33 public class LinkPersonasTests : Folks.TestCase
35 private GLib.MainLoop _main_loop;
36 private EdsTest.Backend? _eds_backend;
37 private IndividualAggregator _aggregator;
38 private string _persona_fullname_1;
39 private string _persona_fullname_2;
40 private string _im_address_1 = "someone-1@jabber.example.org";
41 private string _im_address_2 = "someone-2@jabber.example.org";
42 private string _auto_linkable_email = "the.cool.dude@gmail.tld";
43 private bool _linking_fired;
44 private bool _persona_found_1;
45 private bool _persona_found_2;
46 private string _persona_iid_1;
47 private string _persona_iid_2;
48 private HashSet<Persona> _personas;
49 private Gee.HashMap<string, string> _linking_props;
50 private LinkingMethod _linking_method;
51 private int _test_num = -1;
53 public LinkPersonasTests ()
55 base ("LinkPersonasTests");
57 this.add_test ("test linking personas via IM addresses",
58 this.test_linking_personas_via_im_addresses);
59 this.add_test ("test linking personas via local IDs",
60 this.test_linking_personas_via_local_ids);
61 this.add_test ("test linking personas via web service addresses",
62 this.test_linking_personas_via_web_service_addresses);
63 this.add_test ("test auto linking via e-mail address as IM address",
64 this.test_linking_via_email_as_im_address);
67 public override void set_up ()
69 this._eds_backend = new EdsTest.Backend ();
71 /* Create a new backend (by name) each set up to guarantee we don't
72 * inherit state from the last test.
73 * FIXME: bgo#690830 */
75 this._eds_backend.set_up (false, @"test$_test_num");
77 /* We configure eds as the primary store */
78 var config_val = "eds:%s".printf (this._eds_backend.address_book_uid);
79 Environment.set_variable ("FOLKS_PRIMARY_STORE", config_val, true);
82 public override void tear_down ()
84 this._eds_backend.tear_down ();
86 Environment.unset_variable ("FOLKS_PRIMARY_STORE");
88 this._eds_backend = null;
91 public void test_linking_personas_via_im_addresses ()
93 this._linking_method = LinkingMethod.IM_ADDRESSES;
94 this._test_linking_personas ();
97 public void test_linking_personas_via_local_ids ()
99 this._linking_method = LinkingMethod.LOCAL_IDS;
100 this._test_linking_personas ();
103 public void test_linking_personas_via_web_service_addresses ()
105 this._linking_method = LinkingMethod.WEB_SERVICE_ADDRESSES;
106 this._test_linking_personas ();
109 public void test_linking_via_email_as_im_address ()
111 this._linking_method = LinkingMethod.EMAIL_AS_IM_ADDRESS;
112 this._test_linking_personas ();
115 private void _test_linking_personas ()
117 this._main_loop = new GLib.MainLoop (null, false);
118 this._persona_fullname_1 = "persona #1";
119 this._persona_fullname_2 = "persona #2";
120 this._personas = new HashSet<Persona> ();
121 this._persona_found_1 = false;
122 this._persona_found_2 = false;
123 this._linking_fired = false;
124 this._persona_iid_1 = "";
125 this._persona_iid_2 = "";
127 this._linking_props = new Gee.HashMap<string, string> ();
128 if (this._linking_method == LinkingMethod.IM_ADDRESSES ||
129 this._linking_method == LinkingMethod.WEB_SERVICE_ADDRESSES)
131 this._linking_props.set ("prop1", this._im_address_1);
132 this._linking_props.set ("prop2", this._im_address_2);
134 else if (this._linking_method == LinkingMethod.EMAIL_AS_IM_ADDRESS)
136 this._linking_props.set ("prop1", this._auto_linkable_email);
137 this._linking_props.set ("prop2", this._auto_linkable_email);
140 this._test_linking_personas_async.begin ();
142 var timer_id = Timeout.add_seconds (8, () =>
144 // Let the main loop run out of events before we quit (yes, this is a HACK)
145 Timeout.add_seconds (5, () =>
147 this._main_loop.quit ();
150 assert_not_reached ();
153 this._main_loop.run ();
155 GLib.Source.remove (timer_id);
156 this._aggregator = null;
157 this._main_loop = null;
160 private async void _test_linking_personas_async ()
162 var store = BackendStore.dup ();
163 yield store.prepare ();
164 this._aggregator = new IndividualAggregator ();
165 this._aggregator.individuals_changed_detailed.connect
166 (this._individuals_changed_cb);
169 yield this._aggregator.prepare ();
171 var pstore = this._get_store (store,
172 this._eds_backend.address_book_uid);
173 assert (pstore != null);
175 pstore.notify["is-prepared"].connect (this._notify_pstore_cb);
176 if (pstore.is_prepared)
177 yield this._add_personas (pstore, pstore);
181 GLib.warning ("Error when calling prepare: %s\n", e.message);
185 private void _notify_pstore_cb (Object _pstore, ParamSpec ps)
187 var pstore = (PersonaStore) _pstore;
188 this._add_personas.begin (pstore, pstore);
191 private PersonaStore? _get_store (BackendStore store, string store_id)
193 PersonaStore? pstore = null;
194 foreach (var backend in store.enabled_backends.values)
196 pstore = backend.persona_stores.get (store_id);
203 /* Here is how this test is expected to work:
204 * - we start by adding 2 personas
205 * - this should trigger individuals-changed with 2 new individuals
206 * - we ask the IndividualAggregator to link the 2 personas coming
207 * from those individuals
208 * - we wait for a new Individual which contains the linkable
209 * attributes of these 2 personas
211 * @param pstore1 the {@link PersonaStore} in which to add the 1st Persona
212 * @param pstore2 the {@link PersonaStore} in which to add the 1st Persona
214 private async void _add_personas (PersonaStore pstore1, PersonaStore pstore2)
216 HashTable<string, Value?> details1 = new HashTable<string, Value?>
217 (str_hash, str_equal);
220 Folks.PersonaStore.detail_key (PersonaDetail.WEB_SERVICE_ADDRESSES);
222 if (this._linking_method == LinkingMethod.IM_ADDRESSES ||
223 this._linking_method == LinkingMethod.EMAIL_AS_IM_ADDRESS)
225 v1 = Value (typeof (MultiMap));
226 var im_addrs1 = new HashMultiMap<string, ImFieldDetails> (
228 (Gee.HashDataFunc) AbstractFieldDetails<string>.hash_static,
229 (Gee.EqualDataFunc) AbstractFieldDetails<string>.equal_static);
230 if (this._linking_method == LinkingMethod.EMAIL_AS_IM_ADDRESS)
231 im_addrs1.set ("jabber",
232 new ImFieldDetails (this._auto_linkable_email));
234 im_addrs1.set ("jabber", new ImFieldDetails (this._im_address_1));
235 v1.set_object (im_addrs1);
236 details1.insert ("im-addresses", (owned) v1);
238 else if (this._linking_method == LinkingMethod.WEB_SERVICE_ADDRESSES)
240 v1 = Value (typeof (MultiMap));
241 var wsa1 = new HashMultiMap<string, WebServiceFieldDetails> (
243 (Gee.HashDataFunc) AbstractFieldDetails<string>.hash_static,
244 (Gee.EqualDataFunc) AbstractFieldDetails<string>.equal_static);
245 wsa1.set ("twitter", new WebServiceFieldDetails (this._im_address_1));
246 v1.set_object (wsa1);
247 details1.insert (wsk, (owned) v1);
250 Value? v2 = Value (typeof (string));
251 v2.set_string (this._persona_fullname_1);
252 details1.insert ("full-name", (owned) v2);
254 HashTable<string, Value?> details2 = new HashTable<string, Value?>
255 (str_hash, str_equal);
258 if (this._linking_method == LinkingMethod.IM_ADDRESSES)
260 v3 = Value (typeof (MultiMap));
261 var im_addrs2 = new HashMultiMap<string, ImFieldDetails> (
263 (Gee.HashDataFunc) AbstractFieldDetails<string>.hash_static,
264 (Gee.EqualDataFunc) AbstractFieldDetails<string>.equal_static);
265 im_addrs2.set ("yahoo", new ImFieldDetails (this._im_address_2));
266 v3.set_object (im_addrs2);
267 details2.insert ("im-addresses", (owned) v3);
269 else if (this._linking_method == LinkingMethod.WEB_SERVICE_ADDRESSES)
271 v3 = Value (typeof (MultiMap));
272 var wsa2 = new HashMultiMap<string, WebServiceFieldDetails> (
274 (Gee.HashDataFunc) AbstractFieldDetails<string>.hash_static,
275 (Gee.EqualDataFunc) AbstractFieldDetails<string>.equal_static);
276 wsa2.set ("lastfm", new WebServiceFieldDetails (this._im_address_2));
277 v3.set_object (wsa2);
278 details2.insert (wsk, (owned) v3);
280 else if (this._linking_method == LinkingMethod.EMAIL_AS_IM_ADDRESS)
282 v3 = Value (typeof (Set));
283 var emails = new HashSet<EmailFieldDetails> (
284 AbstractFieldDetails<string>.hash_static,
285 AbstractFieldDetails<string>.equal_static);
286 var email_1 = new EmailFieldDetails (this._auto_linkable_email);
287 emails.add (email_1);
288 v3.set_object (emails);
290 Folks.PersonaStore.detail_key (PersonaDetail.EMAIL_ADDRESSES),
294 Value? v4 = Value (typeof (string));
295 v4.set_string (this._persona_fullname_2);
296 details2.insert ("full-name", (owned)v4);
300 yield this._aggregator.add_persona_from_details (null,
303 yield this._aggregator.add_persona_from_details (null,
306 catch (Folks.IndividualAggregatorError e)
308 GLib.warning ("[AddPersonaError] add_persona_from_details: %s\n",
313 private void _individuals_changed_cb (
314 MultiMap<Individual?, Individual?> changes)
316 this._individuals_changed_async (changes);
319 private void _individuals_changed_async (
320 MultiMap<Individual?, Individual?> changes)
322 var added = changes.get_values ();
324 foreach (var i in added)
331 if (this._linking_method == LinkingMethod.EMAIL_AS_IM_ADDRESS)
332 this._check_auto_linked_personas.begin (i);
334 this._check_personas.begin (i);
338 /* As mentioned in _add_personas here we actually check
339 * for the following events
341 * - spot the 2 individuals corresponding to the 2 personas we've added
342 * - when we've spotted these 2, we pack them in a list and feed that to
343 * IndividualAggregator#link_personas
344 * - this should fire a new individuals-changed event with a new individual
345 * which should be the linked individual if it contains the linking
346 * properties of the 2 linked personas.
348 private async void _check_personas (Individual i)
350 if (this._linking_props.size == 0 &&
357 Persona first_persona = null;
358 foreach (var p in i.personas)
364 if (i.full_name == this._persona_fullname_1 &&
365 this._persona_iid_1 == "")
367 this._persona_iid_1 = first_persona.iid;
368 this._personas.add (first_persona);
369 if (this._linking_method == LinkingMethod.LOCAL_IDS)
371 var contact_id1 = ((Edsf.Persona) first_persona).iid;
372 this._linking_props.set ("prop1", contact_id1);
375 else if (i.full_name == this._persona_fullname_2 &&
376 this._persona_iid_2 == "")
378 this._persona_iid_2 = first_persona.iid;
379 this._personas.add (first_persona);
380 if (this._linking_method == LinkingMethod.LOCAL_IDS)
382 var contact_id2 = ((Edsf.Persona) first_persona).iid;
383 this._linking_props.set ("prop2", contact_id2);
386 else if (i.personas.size > 1)
388 /* Lets check if it contains all the linking properties */
389 if (this._linking_method == LinkingMethod.IM_ADDRESSES)
391 foreach (var proto in i.im_addresses.get_keys ())
393 var im_fds = i.im_addresses.get (proto);
394 foreach (var im_fd in im_fds)
396 if (im_fd.value == this._linking_props.get ("prop1"))
398 this._linking_props.unset ("prop1");
400 else if (im_fd.value == this._linking_props.get ("prop2"))
402 this._linking_props.unset ("prop2");
407 else if (this._linking_method == LinkingMethod.LOCAL_IDS)
409 foreach (var local_id in i.local_ids)
411 if (local_id == this._linking_props.get ("prop1"))
413 this._linking_props.unset ("prop1");
415 else if (local_id == this._linking_props.get ("prop2"))
417 this._linking_props.unset ("prop2");
421 else if (this._linking_method == LinkingMethod.WEB_SERVICE_ADDRESSES)
423 foreach (var service in i.web_service_addresses.get_keys ())
425 var ws_fds = i.web_service_addresses.get (service);
426 foreach (var ws_fd in ws_fds)
428 var prop1 = this._linking_props.get ("prop1");
429 var prop2 = this._linking_props.get ("prop2");
431 ws_fd.equal (new WebServiceFieldDetails (prop1)))
433 this._linking_props.unset ("prop1");
435 else if (prop2 != null &&
436 ws_fd.equal (new WebServiceFieldDetails (prop2)))
438 this._linking_props.unset ("prop2");
444 if (this._linking_props.size == 0)
446 Timeout.add_seconds (5, () =>
448 this._main_loop.quit ();
454 /* We can try linking the personas only once we've got the
455 * 2 initially created personas. */
456 if (this._personas.size == 2 &&
457 this._linking_fired == false)
459 this._linking_fired = true;
462 yield this._aggregator.link_personas (this._personas);
466 GLib.warning ("link_personas: %s\n", e.message);
471 /* Certain e-mail addresses (i.e.: gmail, msn) will be added
472 * as IM addresses to their Persona so auto linking should
475 * Hence, no need to call link_personas () here.
477 private async void _check_auto_linked_personas (Individual i)
479 if (this._linking_props.size == 0)
481 /* Don't even bother. */
485 if (i.personas.size > 1)
487 foreach (var email in i.email_addresses)
489 if (email.value == this._auto_linkable_email)
491 this._linking_props.unset ("prop1");
495 foreach (var proto1 in i.im_addresses.get_keys ())
497 var im_fds1 = i.im_addresses.get (proto1);
498 foreach (var im_fd1 in im_fds1)
500 if (im_fd1.value == this._auto_linkable_email)
502 this._linking_props.unset ("prop2");
508 if (this._linking_props.size == 0)
510 this._main_loop.quit ();
515 public int main (string[] args)
517 Test.init (ref args);
519 TestSuite root = TestSuite.get_root ();
520 root.add_suite (new LinkPersonasTests ().get_suite ());