[folks/648811-dummy-backend-non-dbus] dummy: UNFINISHED work to document backend and port tests to it
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [folks/648811-dummy-backend-non-dbus] dummy: UNFINISHED work to document backend and port tests to it
- Date: Wed, 22 May 2013 22:57:56 +0000 (UTC)
commit 747bc2741ac513fd1866238012e052e988780cac
Author: Philip Withnall <philip tecnocode co uk>
Date: Wed May 22 23:57:15 2013 +0100
dummy: UNFINISHED work to document backend and port tests to it
backends/dummy/dummy-backend-factory.vala | 2 +-
backends/dummy/lib/dummy-backend.vala | 77 ++++++++-
backends/dummy/lib/dummy-fat-persona.vala | 233 +++++++++++++++++++++++++--
backends/dummy/lib/dummy-persona-store.vala | 232 ++++++++++++++++++++++++---
backends/dummy/lib/dummy-persona.vala | 8 +
configure.ac | 1 +
tests/data/Makefile.am | 1 +
tests/data/backend-store-all.ini | 3 +
tests/folks/Makefile.am | 10 +-
tests/folks/aggregation.vala | 125 +++++----------
tests/lib/Makefile.am | 8 +-
tests/lib/test-case.vala | 36 ++++-
tests/lib/test-utils.vala | 70 ++++++++
13 files changed, 674 insertions(+), 132 deletions(-)
---
diff --git a/backends/dummy/dummy-backend-factory.vala b/backends/dummy/dummy-backend-factory.vala
index 9b7a6f6..08df1cd 100644
--- a/backends/dummy/dummy-backend-factory.vala
+++ b/backends/dummy/dummy-backend-factory.vala
@@ -43,5 +43,5 @@ public void module_init (BackendStore backend_store)
*/
public void module_finalize (BackendStore backend_store)
{
- /* TODO */
+ /* TODO: No backend_store.remove_backend() API exists. */
}
diff --git a/backends/dummy/lib/dummy-backend.vala b/backends/dummy/lib/dummy-backend.vala
index 6646649..d76463c 100644
--- a/backends/dummy/lib/dummy-backend.vala
+++ b/backends/dummy/lib/dummy-backend.vala
@@ -25,8 +25,23 @@ using Folks;
extern const string BACKEND_NAME;
/**
- * A backend which connects to EDS and creates a { link PersonaStore}
- * for each service. TODO
+ * A backend which allows { link Dummyf.PersonaStore}s and
+ * { link Dummyf.Persona}s to be programmatically created and manipulated, for
+ * the purposes of testing the core of libfolks itself.
+ *
+ * This backend is not meant to be enabled in production use. The methods on
+ * { link Dummyf.Backend} (and other classes) for programmatically manipulating
+ * the backend's state are considered internal to libfolks and are not stable.
+ *
+ * This backend maintains two sets of persona stores: the set of all persona
+ * stores, and the set of enabled persona stores (which must be a subset of the
+ * former). { link Dummyf.Backend.register_persona_stores} adds persona stores
+ * to the set of all stores. Optionally it also enables them, adding them to the
+ * set of enabled stores. The set of persona stores advertised by the backend as
+ * { link Backend.persona_stores} is the set of enabled stores. libfolks may
+ * internally enable or disable stores using
+ * { link Backend.enable_persona_store}, { link Backend.disable_persona_store}
+ * and { link Backend.set_persona_stores}.
*
* @since UNRELEASED
*/
@@ -278,13 +293,42 @@ public class Dummyf.Backend : Folks.Backend
}
}
+
+ /*
+ * All the functions below here are to be used by testing code rather than by
+ * libfolks clients. They form the interface which would normally be between
+ * the Backend and a web service or backing store of some kind.
+ */
+
+
/**
- * TODO
+ * Register and enable some { link Dummyf.PersonaStore}s.
*
+ * For each of the persona stores in ``stores``, register it with this
+ * backend. If ``enable_stores`` is ``true``, added stores will also be
+ * enabled, emitting { link Backend.persona_store_added} for each
+ * newly-enabled store. After all addition signals are emitted, a change
+ * notification for { link Backend.persona_stores} will be emitted (but only
+ * if at least one addition signal is emitted).
+ *
+ * Persona stores are identified by their { link PersonaStore.id}; if a store
+ * in ``stores`` has the same ID as a store previously registered through this
+ * method, the duplicate will be ignored (so
+ * { link Backend.persona_store_added} won't be emitted for that store).
+ *
+ * Persona stores must be instances of { link Dummyf.PersonaStore} or
+ * subclasses of it, allowing for different persona store implementations to
+ * be tested.
+ *
+ * @param stores set of persona stores to register
+ * @param enable_stores whether to automatically enable the stores
* @since UNRELEASED
*/
- public void register_persona_stores (Set<PersonaStore> stores)
+ public void register_persona_stores (Set<PersonaStore> stores,
+ bool enable_stores = true)
{
+ this.freeze_notify ();
+
foreach (var store in stores)
{
if (this._all_persona_stores.has_key (store.id))
@@ -293,17 +337,36 @@ public class Dummyf.Backend : Folks.Backend
}
this._all_persona_stores.set (store.id, store);
- this._enable_persona_store (store);
+
+ if (enable_stores == true)
+ {
+ this._enable_persona_store (store);
+ }
}
+
+ this.thaw_notify ();
}
/**
- * TODO
+ * Disable and unregister some { link Dummyf.PersonaStores}.
+ *
+ * For each of the persona stores in ``stores``, disable it (if it was
+ * enabled) and unregister it from the backend so that it cannot be re-enabled
+ * using { link Backend.enable_persona_store} or
+ * { link Backend.set_persona_stores}.
+ *
+ * { link Backend.persona_store_removed} will be emitted for all persona
+ * stores in ``stores`` which were previously enabled. After all removal
+ * signals are emitted, a change notification for
+ * { link Backend.persona_stores} will be emitted (but only if at least one
+ * removal signal is emitted).
*
* @since UNRELEASED
*/
public void unregister_persona_stores (Set<PersonaStore> stores)
{
+ this.freeze_notify ();
+
foreach (var store in stores)
{
if (!this._all_persona_stores.has_key (store.id))
@@ -314,5 +377,7 @@ public class Dummyf.Backend : Folks.Backend
this._disable_persona_store (store);
this._all_persona_stores.unset (store.id);
}
+
+ this.thaw_notify ();
}
}
diff --git a/backends/dummy/lib/dummy-fat-persona.vala b/backends/dummy/lib/dummy-fat-persona.vala
index 643e5c8..c157734 100644
--- a/backends/dummy/lib/dummy-fat-persona.vala
+++ b/backends/dummy/lib/dummy-fat-persona.vala
@@ -16,7 +16,9 @@
*
* Authors:
* Philip Withnall <philip tecnocode co uk>
- * TODO
+ * Travis Reitter <travis reitter collabora co uk>
+ * Marco Barisione <marco barisione collabora co uk>
+ * Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
*/
using Folks;
@@ -668,10 +670,14 @@ public class Dummyf.FatPersona : Dummyf.Persona,
});
}
- /**
- * TODO All the functions below here are...
+
+ /*
+ * All the functions below here are to be used by testing code rather than by
+ * libfolks clients. They form the interface which would normally be between
+ * the Persona and a web service or backing store of some kind.
*/
+
private HashSet<T> _dup_to_hash_set<T> (Set<T> input_set)
{
var output_set = new HashSet<T> ();
@@ -696,6 +702,17 @@ public class Dummyf.FatPersona : Dummyf.Persona,
return output_multi_map;
}
+ /**
+ * Update persona's gender.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link GenderDetails.gender} property. It is intended to be used for
+ * testing code which consumes this property. If the property value changes,
+ * this results in a property change notification on the persona.
+ *
+ * @param gender persona's new gender
+ * @since UNRELEASED
+ */
public void update_gender (Gender gender)
{
if (this._gender != gender)
@@ -705,6 +722,17 @@ public class Dummyf.FatPersona : Dummyf.Persona,
}
}
+ /**
+ * Update persona's birthday calendar event ID.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link BirthdayDetails.calendar_event_id} property. It is intended to be
+ * used for testing code which consumes this property. If the property value
+ * changes, this results in a property change notification on the persona.
+ *
+ * @param calendar_event_id persona's new birthday calendar event ID
+ * @since UNRELEASED
+ */
public void update_calendar_event_id (string? calendar_event_id)
{
if (calendar_event_id != this._calendar_event_id)
@@ -714,6 +742,17 @@ public class Dummyf.FatPersona : Dummyf.Persona,
}
}
+ /**
+ * Update persona's birthday.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link BirthdayDetails.birthday} property. It is intended to be used for
+ * testing code which consumes this property. If the property value changes,
+ * this results in a property change notification on the persona.
+ *
+ * @param birthday persona's new birthday
+ * @since UNRELEASED
+ */
public void update_birthday (DateTime? birthday)
{
if ((this._birthday == null) != (birthday == null) ||
@@ -725,6 +764,17 @@ public class Dummyf.FatPersona : Dummyf.Persona,
}
}
+ /**
+ * Update persona's roles.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link RoleDetails.roles} property. It is intended to be used for
+ * testing code which consumes this property. If the property value changes,
+ * this results in a property change notification on the persona.
+ *
+ * @param roles persona's new roles
+ * @since UNRELEASED
+ */
public void update_roles (Set<RoleFieldDetails> roles)
{
if (!Folks.Internal.equal_sets<RoleFieldDetails> (roles, this._roles))
@@ -735,6 +785,17 @@ public class Dummyf.FatPersona : Dummyf.Persona,
}
}
+ /**
+ * Update persona's groups.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link GroupDetails.groups} property. It is intended to be used for
+ * testing code which consumes this property. If the property value changes,
+ * this results in a property change notification on the persona.
+ *
+ * @param groups persona's new groups
+ * @since UNRELEASED
+ */
public void update_groups (Set<string> groups)
{
if (!Folks.Internal.equal_sets<string> (groups, this._groups))
@@ -745,6 +806,18 @@ public class Dummyf.FatPersona : Dummyf.Persona,
}
}
+ /**
+ * Update persona's web service addresses.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link WebServiceDetails.web_service_addresses} property. It is intended to
+ * be used for testing code which consumes this property. If the property
+ * value changes, this results in a property change notification on the
+ * persona.
+ *
+ * @param web_service_addresses persona's new web service addresses
+ * @since UNRELEASED
+ */
public void update_web_service_addresses (
MultiMap<string, WebServiceFieldDetails> web_service_addresses)
{
@@ -758,6 +831,17 @@ public class Dummyf.FatPersona : Dummyf.Persona,
}
}
+ /**
+ * Update persona's e-mail addresses.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link EmailDetails.email_addresses} property. It is intended to be used
+ * for testing code which consumes this property. If the property value
+ * changes, this results in a property change notification on the persona.
+ *
+ * @param email_addresses persona's new e-mail addresses
+ * @since UNRELEASED
+ */
public void update_email_addresses (Set<EmailFieldDetails> email_addresses)
{
if (!Folks.Internal.equal_sets<EmailFieldDetails> (email_addresses,
@@ -770,6 +854,17 @@ public class Dummyf.FatPersona : Dummyf.Persona,
}
}
+ /**
+ * Update persona's notes.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link NoteDetails.notes} property. It is intended to be used for
+ * testing code which consumes this property. If the property value changes,
+ * this results in a property change notification on the persona.
+ *
+ * @param notes persona's new notes
+ * @since UNRELEASED
+ */
public void update_notes (Set<NoteFieldDetails> notes)
{
if (!Folks.Internal.equal_sets<NoteFieldDetails> (notes, this._notes))
@@ -780,6 +875,17 @@ public class Dummyf.FatPersona : Dummyf.Persona,
}
}
+ /**
+ * Update persona's full name.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link NameDetails.full_name} property. It is intended to be used for
+ * testing code which consumes this property. If the property value changes,
+ * this results in a property change notification on the persona.
+ *
+ * @param full_name persona's new full name
+ * @since UNRELEASED
+ */
public void update_full_name (string full_name)
{
if (this._full_name != full_name)
@@ -789,6 +895,17 @@ public class Dummyf.FatPersona : Dummyf.Persona,
}
}
+ /**
+ * Update persona's nickname.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link NameDetails.nickname} property. It is intended to be used for
+ * testing code which consumes this property. If the property value changes,
+ * this results in a property change notification on the persona.
+ *
+ * @param nickname persona's new nickname
+ * @since UNRELEASED
+ */
public void update_nickname (string nickname)
{
if (this._nickname != nickname)
@@ -798,6 +915,17 @@ public class Dummyf.FatPersona : Dummyf.Persona,
}
}
+ /**
+ * Update persona's structured name.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link NameDetails.structured_name} property. It is intended to be used for
+ * testing code which consumes this property. If the property value changes,
+ * this results in a property change notification on the persona.
+ *
+ * @param structured_name persona's new structured name
+ * @since UNRELEASED
+ */
public void update_structured_name (StructuredName? structured_name)
{
if (structured_name != null && !((!) structured_name).is_empty ())
@@ -812,6 +940,17 @@ public class Dummyf.FatPersona : Dummyf.Persona,
}
}
+ /**
+ * Update persona's avatar.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link AvatarDetails.avatar} property. It is intended to be used for
+ * testing code which consumes this property. If the property value changes,
+ * this results in a property change notification on the persona.
+ *
+ * @param avatar persona's new avatar
+ * @since UNRELEASED
+ */
public void update_avatar (LoadableIcon? avatar)
{
if ((this._avatar == null) != (avatar == null) ||
@@ -823,6 +962,17 @@ public class Dummyf.FatPersona : Dummyf.Persona,
}
}
+ /**
+ * Update persona's URIs.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link UrlDetails.urls} property. It is intended to be used for
+ * testing code which consumes this property. If the property value changes,
+ * this results in a property change notification on the persona.
+ *
+ * @param urls persona's new URIs
+ * @since UNRELEASED
+ */
public void update_urls (Set<UrlFieldDetails> urls)
{
if (!Utils.set_afd_equal (urls, this._urls))
@@ -833,6 +983,17 @@ public class Dummyf.FatPersona : Dummyf.Persona,
}
}
+ /**
+ * Update persona's IM addresses.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link ImDetails.im_addresses} property. It is intended to be used for
+ * testing code which consumes this property. If the property value changes,
+ * this results in a property change notification on the persona.
+ *
+ * @param im_addresses persona's new IM addresses
+ * @since UNRELEASED
+ */
public void update_im_addresses (
MultiMap<string, ImFieldDetails> im_addresses)
{
@@ -846,16 +1007,17 @@ public class Dummyf.FatPersona : Dummyf.Persona,
}
}
- public void _update_groups (Set<string> groups)
- {
- if (!Folks.Internal.equal_sets<string> (groups, this._groups))
- {
- this._groups = this._dup_to_hash_set<string> (groups);
- this._groups_ro = this._groups.read_only_view;
- this.notify_property ("groups");
- }
- }
-
+ /**
+ * Update persona's phone numbers.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link PhoneDetails.phone_numbers} property. It is intended to be used for
+ * testing code which consumes this property. If the property value changes,
+ * this results in a property change notification on the persona.
+ *
+ * @param phone_numbers persona's new phone numbers
+ * @since UNRELEASED
+ */
public void update_phone_numbers (Set<PhoneFieldDetails> phone_numbers)
{
if (!Folks.Internal.equal_sets<PhoneFieldDetails> (phone_numbers,
@@ -868,6 +1030,18 @@ public class Dummyf.FatPersona : Dummyf.Persona,
}
}
+ /**
+ * Update persona's postal addresses.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link PostalAddressDetails.postal_addresses} property. It is intended to
+ * be used for testing code which consumes this property. If the property
+ * value changes, this results in a property change notification on the
+ * persona.
+ *
+ * @param postal_addresses persona's new postal addresses
+ * @since UNRELEASED
+ */
public void update_postal_addresses (
Set<PostalAddressFieldDetails> postal_addresses)
{
@@ -882,6 +1056,17 @@ public class Dummyf.FatPersona : Dummyf.Persona,
}
}
+ /**
+ * Update persona's local IDs.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link LocalIdDetails.local_ids} property. It is intended to be used for
+ * testing code which consumes this property. If the property value changes,
+ * this results in a property change notification on the persona.
+ *
+ * @param local_ids persona's new local IDs
+ * @since UNRELEASED
+ */
public void update_local_ids (Set<string> local_ids)
{
/* Make sure it includes our local ID. */
@@ -895,6 +1080,17 @@ public class Dummyf.FatPersona : Dummyf.Persona,
}
}
+ /**
+ * Update persona's status as a favourite.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link FavouriteDetails.is_favourite} property. It is intended to be used
+ * for testing code which consumes this property. If the property value
+ * changes, this results in a property change notification on the persona.
+ *
+ * @param is_favourite persona's new status as a favourite
+ * @since UNRELEASED
+ */
public void update_is_favourite (bool is_favourite)
{
if (is_favourite != this._is_favourite)
@@ -904,6 +1100,17 @@ public class Dummyf.FatPersona : Dummyf.Persona,
}
}
+ /**
+ * Update persona's anti-links.
+ *
+ * This simulates a backing-store-side update of the persona's
+ * { link AntiLinkable.anti_links} property. It is intended to be used for
+ * testing code which consumes this property. If the property value changes,
+ * this results in a property change notification on the persona.
+ *
+ * @param anti_links persona's new anti-links
+ * @since UNRELEASED
+ */
public void update_anti_links (Set<string> anti_links)
{
if (!Folks.Internal.equal_sets<string> (anti_links, this._anti_links))
diff --git a/backends/dummy/lib/dummy-persona-store.vala b/backends/dummy/lib/dummy-persona-store.vala
index e2b6f2d..f9e9c6e 100644
--- a/backends/dummy/lib/dummy-persona-store.vala
+++ b/backends/dummy/lib/dummy-persona-store.vala
@@ -23,10 +23,14 @@ using Gee;
using GLib;
/**
- * A persona store representing a single EDS address book. TODO
+ * A persona store which allows { link Dummyf.Persona}s to be programmatically
+ * created and manipulated, for the purposes of testing the core of libfolks
+ * itself.
*
- * The persona store will contain { link Dummy.Persona}s for each contact in the
- * address book it represents.
+ * TODO: Mock functions
+ * TODO: Unstable API
+ *
+ * TODO
*
* TODO: trust_level and is_user_set_default can be set as normal properties
*
@@ -192,12 +196,12 @@ public class Dummyf.PersonaStore : Folks.PersonaStore
}
/**
- * Create a new PersonaStore.
+ * Create a new persona store.
*
- * Create a new persona store to store the { link Persona}s for the contacts
- * in ``s``. Passing a re-used source registry to the constructor (compared to
- * the old { link Dummy.PersonaStore} constructor) saves a lot of time and
- * D-Bus round trips. TODO
+ * This store will have no personas to begin with; use
+ * { link Dummyf.PersonaStore.register_personas} to add some, then call
+ * { link Dummyf.PersonaStore.reach_quiescence} to signal the store reaching
+ * quiescence.
*
* @param id The new store's ID.
* @param display_name The new store's display name.
@@ -222,6 +226,43 @@ public class Dummyf.PersonaStore : Folks.PersonaStore
}
/**
+ * Type of a mock function for { link PersonaStore.add_persona_from_details}.
+ *
+ * See { link Dummyf.PersonaStore.add_persona_from_details_mock}.
+ *
+ * @param persona the persona being added to the store, as constructed from
+ * the details passed to { link PersonaStore.add_persona_from_details}.
+ * @throws PersonaStoreError to be thrown from
+ * { link PersonaStore.add_persona_from_details}
+ * @since UNRELEASED
+ */
+ public delegate void AddPersonaFromDetailsMock (Persona persona)
+ throws PersonaStoreError;
+
+ /**
+ * Mock function for { link PersonaStore.add_persona_from_details}.
+ *
+ * This function is called whenever this store's
+ * { link PersonaStore.add_persona_from_details} method is called. It allows
+ * the caller to determine whether adding the given persona should fail, by
+ * throwing an error from this mock function. If no error is thrown from this
+ * function, adding the given persona will succeed. This is useful for testing
+ * error handling of calls to { link PersoneStore.add_persona_from_details}.
+ *
+ * If this is ``null``, all calls to
+ * { link PersonaStore.add_persona_from_details} will succeed.
+ *
+ * This mock function may be changed at any time; changes will take effect for
+ * the next call to { link PersonaStore.add_persona_from_details}.
+ *
+ * @since UNRELEASED
+ */
+ public unowned AddPersonaFromDetailsMock? add_persona_from_details_mock
+ {
+ get; set; default = null;
+ }
+
+ /**
* Add a new { link Persona} to the PersonaStore.
*
* Accepted keys for ``details`` are:
@@ -251,7 +292,7 @@ public class Dummyf.PersonaStore : Folks.PersonaStore
* @since UNRELEASED
*/
public override async Folks.Persona? add_persona_from_details (
- HashTable<string, Value?> details) throws Folks.PersonaStoreError
+ HashTable<string, Value?> details) throws PersonaStoreError
{
// We have to have called prepare() beforehand.
if (!this._is_prepared)
@@ -260,8 +301,20 @@ public class Dummyf.PersonaStore : Folks.PersonaStore
"Persona store has not yet been prepared.");
}
- /* TODO: Allow overriding the class used. */
- var persona = new Dummyf.Persona (this, "TODO", false, {});
+ /* Allow overriding the class used. */
+ var contact_id = "TODO";
+ var uid = Folks.Persona.build_uid (BACKEND_NAME, this.id, contact_id);
+ var iid = this.id + ":" + contact_id;
+
+ var persona = Object.new (this._persona_type,
+ "display-id", contact_id,
+ "uid", uid,
+ "iid", iid,
+ "store", this,
+ "is-user", false,
+ null) as Dummyf.Persona;
+ assert (persona != null);
+ persona.update_writeable_properties (this.always_writeable_properties);
var iter = HashTableIter<string, Value?> (details);
unowned string k;
@@ -388,7 +441,14 @@ public class Dummyf.PersonaStore : Folks.PersonaStore
}*/
}
- /* TODO: How to simulate failure? */
+ /* Allow the caller to inject failures into add_persona_from_details()
+ * by providing a mock function which can throw errors as appropriate. */
+ if (this.add_persona_from_details_mock != null)
+ {
+ this.add_persona_from_details_mock (persona);
+ }
+
+ /* No simulated failure: continue adding the persona. */
this._personas.set (persona.iid, persona);
/* Notify of the new persona. */
@@ -400,6 +460,42 @@ public class Dummyf.PersonaStore : Folks.PersonaStore
}
/**
+ * Type of a mock function for { link PersonaStore.remove_persona}.
+ *
+ * See { link Dummyf.PersonaStore.remove_persona_mock}.
+ *
+ * @param persona the persona being removed from the store
+ * @throws PersonaStoreError to be thrown from
+ * { link PersonaStore.remove_persona}
+ * @since UNRELEASED
+ */
+ public delegate void RemovePersonaMock (Persona persona)
+ throws PersonaStoreError;
+
+ /**
+ * Mock function for { link PersonaStore.remove_persona}.
+ *
+ * This function is called whenever this store's
+ * { link PersonaStore.remove_persona} method is called. It allows
+ * the caller to determine whether removing the given persona should fail, by
+ * throwing an error from this mock function. If no error is thrown from this
+ * function, removing the given persona will succeed. This is useful for
+ * testing error handling of calls to { link PersoneStore.remove_persona}.
+ *
+ * If this is ``null``, all calls to { link PersonaStore.remove_persona} will
+ * succeed.
+ *
+ * This mock function may be changed at any time; changes will take effect for
+ * the next call to { link PersonaStore.remove_persona}.
+ *
+ * @since UNRELEASED
+ */
+ public unowned RemovePersonaMock? remove_persona_mock
+ {
+ get; set; default = null;
+ }
+
+ /**
* Remove a { link Persona} from the PersonaStore.
*
* See { link Folks.PersonaStore.remove_persona}.
@@ -416,7 +512,8 @@ public class Dummyf.PersonaStore : Folks.PersonaStore
* @since UNRELEASED
*/
public override async void remove_persona (Folks.Persona persona)
- throws Folks.PersonaStoreError
+ throws PersonaStoreError
+ requires (persona is Dummyf.Persona)
{
// We have to have called prepare() beforehand.
if (!this._is_prepared)
@@ -425,9 +522,15 @@ public class Dummyf.PersonaStore : Folks.PersonaStore
"Persona store has not yet been prepared.");
}
- /* TODO: How to simulate failure? */
+ /* Allow the caller to inject failures into remove_persona()
+ * by providing a mock function which can throw errors as appropriate. */
+ if (this.remove_persona_mock != null)
+ {
+ this.remove_persona_mock ((Dummyf.Persona) persona);
+ }
+
Persona? _persona = this._personas.get (persona.iid);
- if (persona != null)
+ if (_persona != null)
{
this._personas.unset (persona.iid);
@@ -446,15 +549,50 @@ public class Dummyf.PersonaStore : Folks.PersonaStore
}
/**
+ * Type of a mock function for { link PersonaStore.prepare}.
+ *
+ * See { link Dummyf.PersonaStore.prepare_mock}.
+ *
+ * @throws PersonaStoreError to be thrown from { link PersonaStore.prepare}
+ * @since UNRELEASED
+ */
+ public delegate void PrepareMock () throws PersonaStoreError;
+
+ /**
+ * Mock function for { link PersonaStore.prepare}.
+ *
+ * This function is called whenever this store's
+ * { link PersonaStore.prepare} method is called on an unprepared store. It
+ * allows the caller to determine whether preparing the store should fail, by
+ * throwing an error from this mock function. If no error is thrown from this
+ * function, preparing the store will succeed (and all future calls to
+ * { link PersonaStore.prepare} will return immediately without calling this
+ * mock function). This is useful for testing error handling of calls to
+ * { link PersoneStore.prepare}.
+ *
+ * If this is ``null``, all calls to { link PersonaStore.prepare} will
+ * succeed.
+ *
+ * This mock function may be changed at any time; changes will take effect for
+ * the next call to { link PersonaStore.prepare}.
+ *
+ * @since UNRELEASED
+ */
+ public unowned PrepareMock? prepare_mock
+ {
+ get; set; default = null;
+ }
+
+ /**
* Prepare the PersonaStore for use.
*
* See { link Folks.PersonaStore.prepare}.
*
- * @throws Folks.PersonaStoreError.STORE_OFFLINE if the EDS store is offline
+ * @throws Folks.PersonaStoreError.STORE_OFFLINE if the store is offline
* @throws Folks.PersonaStoreError.PERMISSION_DENIED if permission was denied
- * to open the EDS store
+ * to open the store
* @throws Folks.PersonaStoreError.INVALID_ARGUMENT if any other error
- * occurred in the EDS store
+ * occurred in the store
*
* @since UNRELEASED
*/
@@ -472,6 +610,13 @@ public class Dummyf.PersonaStore : Folks.PersonaStore
{
this._prepare_pending = true;
+ /* Allow the caller to inject failures into prepare() by providing a
+ * mock function which can throw errors as appropriate. */
+ if (this.prepare_mock != null)
+ {
+ this.prepare_mock ();
+ }
+
this._is_prepared = true;
this.notify_property ("is-prepared");
@@ -490,12 +635,55 @@ public class Dummyf.PersonaStore : Folks.PersonaStore
Internal.profiling_end ("preparing Dummy.PersonaStore");
}
+
+ /*
+ * All the functions below here are to be used by testing code rather than by
+ * libfolks clients. They form the interface which would normally be between
+ * the PersonaStore and a web service or backing store of some kind.
+ */
+
+
+ private Type _persona_type = typeof (Dummyf.Persona);
+
/**
- * TODO
+ * Type of programmatically created personas.
+ *
+ * This is the type used to create new personas when
+ * { link PersonaStore.add_persona_from_details} is called. It must be a
+ * subtype of { link Dummyf.Persona}.
*
- * @param can_add_personas TODO
- * @param can_alias_personas TODO
- * @param can_remove_personas TODO
+ * This may be modified at any time, with modifications taking effect for the
+ * next call to { link PersonaStore.add_persona_from_details}.
+ *
+ * @since UNRELEASED
+ */
+ public Type persona_type
+ {
+ get { return this._persona_type; }
+ set
+ {
+ assert (value.is_a (typeof (Dummyf.Persona)));
+ if (this._persona_type != value)
+ {
+ this._persona_type = value;
+ this.notify_property ("persona-type");
+ }
+ }
+ }
+
+ /**
+ * Set capabilities of the persona store.
+ *
+ * This sets the capabilities of the store, as if they were changed on a
+ * backing store somewhere. This is intended to be used for testing code which
+ * depends on the values of { link PersonaStore.can_add_personas},
+ * { link PersonaStore.can_alias_personas} and
+ * { link PersonaStore.can_remove_personas}.
+ *
+ * @param can_add_personas whether the store can handle adding personas
+ * @param can_alias_personas whether the store can handle and update
+ * user-specified persona aliases
+ * @param can_remove_personas whether the store can handle removing personas
* @since UNRELEASED
*/
public void set_capabilities (MaybeBool can_add_personas,
diff --git a/backends/dummy/lib/dummy-persona.vala b/backends/dummy/lib/dummy-persona.vala
index 8b5f467..9634347 100644
--- a/backends/dummy/lib/dummy-persona.vala
+++ b/backends/dummy/lib/dummy-persona.vala
@@ -153,6 +153,14 @@ public class Dummyf.Persona : Folks.Persona
}
}
+
+ /*
+ * All the functions below here are to be used by testing code rather than by
+ * libfolks clients. They form the interface which would normally be between
+ * the Persona and a web service or backing store of some kind.
+ */
+
+
/**
* TODO
*
diff --git a/configure.ac b/configure.ac
index 3ec081f..a3522bb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -663,6 +663,7 @@ AC_CONFIG_FILES([
tests/tracker/Makefile
tests/lib/Makefile
tests/lib/folks-test-uninstalled.pc
+ tests/lib/dummy/Makefile
tests/lib/eds/Makefile
tests/lib/key-file/Makefile
tests/lib/libsocialweb/Makefile
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index c8884b4..017b570 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -1,6 +1,7 @@
EXTRA_DIST = \
avatar-01.jpg \
backend-store-all.ini \
+ backend-store-dummy-only.ini \
$(NULL)
-include $(top_srcdir)/git.mk
diff --git a/tests/data/backend-store-all.ini b/tests/data/backend-store-all.ini
index a24f714..bc019c7 100644
--- a/tests/data/backend-store-all.ini
+++ b/tests/data/backend-store-all.ini
@@ -5,5 +5,8 @@ enabled=true
[telepathy]
enabled=true
+[dummy]
+enabled=true
+
[all-others]
enabled=false
diff --git a/tests/folks/Makefile.am b/tests/folks/Makefile.am
index bfc362c..5b79a84 100644
--- a/tests/folks/Makefile.am
+++ b/tests/folks/Makefile.am
@@ -3,7 +3,9 @@ AM_CPPFLAGS = \
$(GEE_CFLAGS) \
$(TP_GLIB_CFLAGS) \
-I$(top_srcdir)/folks \
+ -I$(top_srcdir)/backends/dummy/lib \
-I$(top_srcdir)/tests/lib \
+ -I$(top_srcdir)/tests/lib/dummy \
-I$(top_srcdir)/tests/lib/key-file \
-I$(top_srcdir)/tests/lib/telepathy/contactlist \
-include $(CONFIG_HEADER) \
@@ -11,8 +13,10 @@ AM_CPPFLAGS = \
LDADD = \
$(top_builddir)/tests/lib/libfolks-test.la \
+ $(top_builddir)/tests/lib/dummy/libdummy-test.la \
$(top_builddir)/tests/lib/key-file/libkf-test.la \
$(top_builddir)/tests/lib/telepathy/contactlist/libtp-test-contactlist.la \
+ $(top_builddir)/backends/dummy/lib/libfolks-dummy.la \
$(top_builddir)/folks/libfolks.la \
$(GLIB_LIBS) \
$(GEE_LIBS) \
@@ -29,7 +33,9 @@ AM_VALAFLAGS = \
--disable-warnings \
--vapidir=. \
--vapidir=$(top_srcdir)/folks \
+ --vapidir=$(top_srcdir)/backends/dummy/lib \
--vapidir=$(top_srcdir)/tests/lib \
+ --vapidir=$(top_srcdir)/tests/lib/dummy \
--vapidir=$(top_srcdir)/tests/lib/key-file \
--vapidir=$(top_builddir)/tests/lib/telepathy/contactlist/ \
--pkg gobject-2.0 \
@@ -37,8 +43,10 @@ AM_VALAFLAGS = \
--pkg gee-1.0 \
--pkg folks \
--pkg folks-test \
+ --pkg dummy-test \
--pkg kf-test \
--pkg tp-test-contactlist \
+ --pkg folks-dummy \
-g \
$(NULL)
@@ -55,7 +63,7 @@ noinst_PROGRAMS = \
$(NULL)
SESSION_CONF = $(top_builddir)/tests/lib/telepathy/contactlist/session.conf
-backend_store_key_file=$(top_srcdir)/tests/data/backend-store-all.ini
+backend_store_key_file=$(top_srcdir)/tests/data/backend-store-dummy-only.ini # TODO
avatar_file= abs_top_srcdir@/tests/data/avatar-01.jpg
TESTS_ENVIRONMENT = \
FOLKS_BACKEND_PATH=$(BACKEND_UNINST_PATH) \
diff --git a/tests/folks/aggregation.vala b/tests/folks/aggregation.vala
index e555998..006e98d 100644
--- a/tests/folks/aggregation.vala
+++ b/tests/folks/aggregation.vala
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2011 Collabora Ltd.
+ * Copyright (C) 2013 Philip Withnall
*
* 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
@@ -15,16 +16,18 @@
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Travis Reitter <travis reitter collabora co uk>
+ * Philip Withnall <philip tecnocode co uk>
*/
using Gee;
using Folks;
using TpTests;
-public class AggregationTests : Folks.TestCase
+public class AggregationTests : Folks.NormalTestCase
{
private KfTest.Backend _kf_backend;
private TpTests.Backend _tp_backend;
+ private DummyTest.Backend _dummy_backend;
private HashSet<string> _default_personas;
private int _test_timeout = 3;
@@ -34,6 +37,7 @@ public class AggregationTests : Folks.TestCase
this._kf_backend = new KfTest.Backend ();
this._tp_backend = new TpTests.Backend ();
+ this._dummy_backend = new DummyTest.Backend ();
/* Create a set of the individuals we expect to see */
this._default_personas = new HashSet<string> (str_hash, str_equal);
@@ -67,14 +71,18 @@ public class AggregationTests : Folks.TestCase
this._test_timeout = 10;
}
- public override void set_up ()
+ public override async void set_up ()
{
+ yield base.set_up ();
+ this._dummy_backend.set_up (this._backend_store);
this._tp_backend.set_up ();
}
- public override void tear_down ()
+ public override async void tear_down ()
{
this._tp_backend.tear_down ();
+ this._dummy_backend.tear_down ();
+ yield base.tear_down ();
}
/* Test that personas are aggregated if their IIDs match (e.g. with the
@@ -88,108 +96,59 @@ public class AggregationTests : Folks.TestCase
{
var main_loop = new GLib.MainLoop (null, false);
- this._kf_backend.set_up ("");
-
- void* account1_handle = this._tp_backend.add_account ("protocol",
- "me example com", "cm", "account");
- void* account2_handle = this._tp_backend.add_account ("protocol",
- "me2 example com", "cm", "account2");
-
- /* IDs of the individuals we expect to see.
- * These are externally opaque, but internally are SHA-1 hashes of the
- * concatenated UIDs of the Personas in the Individual. In these cases,
- * each default_individual contains two Personas with the same IID.
- * e.g.
- * telepathy:/org/freedesktop/Telepathy/Account/cm/protocol/account2:sjoerd example com
- * and
- * telepathy:/org/freedesktop/Telepathy/Account/cm/protocol/account:sjoerd example com
- * in a single Individual. */
- var default_individuals = new HashSet<string> ();
-
- /* guillaume example com */
- default_individuals.add ("6380b17dc511b21a1defd4811f1add97b278f92c");
- /* sjoerd example com */
- default_individuals.add ("6b08188cb2ef8cbaca140b277780069b5af8add6");
- /* travis example com */
- default_individuals.add ("60c91326018f6a60604f8d260fc24a60a5b8512c");
- /* olivier example com */
- default_individuals.add ("0e46c5e74f61908f49550d241f2a1651892a1695");
- /* christian example com */
- default_individuals.add ("07b913b8977c04d2f2011e26a46ea3e3dcfe3e3d");
- /* geraldine example com */
- default_individuals.add ("f948d4d2af79085ab860f0ef67bf0c201c4602d4");
- /* helen example com */
- default_individuals.add ("f34529a442577b840a75271b464e90666c38c464");
- /* wim example com */
- default_individuals.add ("467d13f955e62bf30ebf9620fa052aaee2160260");
-
- /* Work on a copy of the set of individuals so we can mangle it. We keep
- * one copy of the set for the individuals_changed signal, and one for
- * the individuals_changed_detailed signal so that we can compare their
- * behaviour. */
- HashSet<string> expected_individuals = new HashSet<string> ();
- var expected_individuals_detailed = new HashSet<string> ();
- foreach (var id in default_individuals)
- {
- expected_individuals.add (id);
- expected_individuals_detailed.add (id);
- }
-
- /* Set up the aggregator */
- var aggregator = new IndividualAggregator ();
- aggregator.individuals_changed_detailed.connect ((changes) =>
- {
- var removed = changes.get_keys ();
- var added = changes.get_values ();
-
- this._test_iid_individuals_changed (true, added, removed,
- default_individuals, expected_individuals_detailed);
- });
- aggregator.individuals_changed.connect ((added, removed, m, a, r) =>
- {
- this._test_iid_individuals_changed (false, added, removed,
- default_individuals, expected_individuals);
- });
-
/* Kill the main loop after a few seconds. If there are still individuals
* in the set of expected individuals, the aggregator has either failed or
* been too slow (which we can consider to be failure). */
Timeout.add_seconds (this._test_timeout, () =>
{
+ warning ("Killing main loop after %u seconds.", this._test_timeout);
main_loop.quit ();
return false;
});
Idle.add (() =>
{
- aggregator.prepare.begin ((s,r) =>
+ this.test_iid_async.begin ((s, r) =>
{
- try
- {
- aggregator.prepare.end (r);
- }
- catch (GLib.Error e1)
- {
- GLib.critical ("Failed to prepare aggregator: %s",
- e1.message);
- assert_not_reached ();
- }
+ this.test_iid_async.end (r);
+ main_loop.quit ();
});
return false;
});
main_loop.run ();
+ }
+
+ public async void test_iid_async ()
+ {
+ var store1 = this._dummy_backend.add_persona_store ("store1", {},
+ (store) =>
+ {
+ return { new Dummyf.Persona (store, "iid1") };
+ });
+
+ var store2 = this._dummy_backend.add_persona_store ("store2", {},
+ (store) =>
+ {
+ return { new Dummyf.Persona (store, "iid1") };
+ });
+
+ this._kf_backend.set_up ("");
- /* We should have enumerated exactly the individuals in the set */
- assert (expected_individuals.size == 0);
- assert (expected_individuals_detailed.size == 0);
+ /* Prepare the aggregator. */
+ var individuals = yield this.individual_aggregator_prepare ();
+
+ /* Check the individuals. */
+ TestUtils.individuals_map_equals (individuals,
+ {
+ "store1:iid1,store2:iid1"
+ });
/* Clean up for the next test */
- this._tp_backend.remove_account (account2_handle);
- this._tp_backend.remove_account (account1_handle);
+ this._dummy_backend.remove_persona_store (store2);
+ this._dummy_backend.remove_persona_store (store1);
this._kf_backend.tear_down ();
- aggregator = null;
}
private void _test_iid_individuals_changed (bool detailed,
diff --git a/tests/lib/Makefile.am b/tests/lib/Makefile.am
index bcaf81d..a0a53f8 100644
--- a/tests/lib/Makefile.am
+++ b/tests/lib/Makefile.am
@@ -1,4 +1,5 @@
SUBDIRS = \
+ dummy \
key-file \
$(NULL)
@@ -19,6 +20,7 @@ SUBDIRS += eds
endif
DIST_SUBDIRS = \
+ dummy \
key-file \
telepathy \
eds \
@@ -28,7 +30,11 @@ DIST_SUBDIRS = \
noinst_LTLIBRARIES = libfolks-test.la
-libfolks_test_la_SOURCES = test-case.vala test-utils.vala
+libfolks_test_la_SOURCES = \
+ normal-test-case.vala \
+ test-case.vala \
+ test-utils.vala \
+ $(NULL)
libfolks_test_la_CFLAGS = \
$(AM_CFLAGS) \
diff --git a/tests/lib/test-case.vala b/tests/lib/test-case.vala
index a0715a1..05ca1bb 100644
--- a/tests/lib/test-case.vala
+++ b/tests/lib/test-case.vala
@@ -43,11 +43,11 @@ public abstract class Folks.TestCase : Object
adaptor.name, adaptor.set_up, adaptor.run, adaptor.tear_down));
}
- public virtual void set_up ()
+ public virtual async void set_up ()
{
}
- public virtual void tear_down ()
+ public virtual async void tear_down ()
{
}
@@ -56,7 +56,7 @@ public abstract class Folks.TestCase : Object
return this._suite;
}
- private class Adaptor
+ private class Adaptor
{
public string name { get; private set; }
private unowned TestMethod _test;
@@ -74,7 +74,20 @@ public abstract class Folks.TestCase : Object
GLib.set_printerr_handler (Adaptor._printerr_func_stack_trace);
Log.set_default_handler (this._log_func_stack_trace);
- this._test_case.set_up ();
+ var main_loop = new MainLoop ();
+
+ Idle.add (() =>
+ {
+ this._test_case.set_up.begin ((s, r) =>
+ {
+ this._test_case.set_up.end (r);
+ main_loop.quit ();
+ });
+
+ return false;
+ });
+
+ main_loop.run ();
}
private static void _printerr_func_stack_trace (string? text)
@@ -112,7 +125,20 @@ public abstract class Folks.TestCase : Object
public void tear_down (void* fixture)
{
- this._test_case.tear_down ();
+ var main_loop = new MainLoop ();
+
+ Idle.add (() =>
+ {
+ this._test_case.tear_down.begin ((s, r) =>
+ {
+ this._test_case.tear_down.end (r);
+ main_loop.quit ();
+ });
+
+ return false;
+ });
+
+ main_loop.run ();
}
}
}
diff --git a/tests/lib/test-utils.vala b/tests/lib/test-utils.vala
index f6e97cd..8c3cfc9 100644
--- a/tests/lib/test-utils.vala
+++ b/tests/lib/test-utils.vala
@@ -22,6 +22,7 @@
using Folks;
using GLib;
+using Gee;
public class Folks.TestUtils
{
@@ -135,4 +136,73 @@ public class Folks.TestUtils
assert (aggregator.is_quiescent == true);
}
}
+
+ /**
+ * TODO
+ *
+ * @since UNRELEASED
+ */
+ public static bool personas_set_equals (Set<Persona> actual_personas,
+ string[] expected_personas)
+ {
+ if (actual_personas.size != expected_personas.length)
+ {
+ return false;
+ }
+
+ foreach (var p in actual_personas)
+ {
+ if (!(p.uid in expected_personas))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * TODO
+ *
+ * @since UNRELEASED
+ */
+ public static bool individuals_map_equals (
+ Map<string, Individual> actual_individuals, string[] expected_individuals)
+ {
+ if (actual_individuals.size != expected_individuals.length)
+ {
+ return false;
+ }
+
+ var _actual_individuals = new HashSet<Individual> ();
+ _actual_individuals.add_all (actual_individuals.values);
+ assert (_actual_individuals.size == actual_individuals.size);
+
+ var _expected_individuals = new HashSet<string> ();
+ foreach (var i in expected_individuals)
+ {
+ _expected_individuals.add (i);
+ }
+
+ foreach (var i in _actual_individuals)
+ {
+ var actual_personas = i.personas;
+
+ foreach (var _expected_personas in _expected_individuals)
+ {
+ var expected_personas = _expected_personas.split (",");
+
+ if (TestUtils.personas_set_equals (actual_personas,
+ expected_personas) == true)
+ {
+ _expected_individuals.remove (_expected_personas);
+ break;
+ }
+ }
+
+ return false;
+ }
+
+ return true;
+ }
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]