[folks] Add a performance test for contacts in e-d-s
- From: Travis Reitter <treitter src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [folks] Add a performance test for contacts in e-d-s
- Date: Wed, 20 Mar 2013 23:27:31 +0000 (UTC)
commit c8c1642adb85c7bbe2baf3c0a15f4994bcdb270d
Author: Simon McVittie <simon mcvittie collabora co uk>
Date: Mon Mar 18 20:28:20 2013 +0000
Add a performance test for contacts in e-d-s
tests/eds/Makefile.am | 5 +
tests/eds/perf.vala | 406 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 411 insertions(+), 0 deletions(-)
---
diff --git a/tests/eds/Makefile.am b/tests/eds/Makefile.am
index ef0b130..642e1f3 100644
--- a/tests/eds/Makefile.am
+++ b/tests/eds/Makefile.am
@@ -78,6 +78,7 @@ TESTS = \
anti-linking \
enable-disable-stores \
set-is-favourite \
+ perf \
$(NULL)
RUN_WITH_PRIVATE_BUS = $(top_srcdir)/tests/tools/with-session-bus-eds.sh
@@ -227,6 +228,10 @@ store_removed_SOURCES = \
store-removed.vala \
$(NULL)
+perf_SOURCES = \
+ perf.vala \
+ $(NULL)
+
helper_create_many_contacts_SOURCES = \
helper-create-many-contacts.vala \
$(NULL)
diff --git a/tests/eds/perf.vala b/tests/eds/perf.vala
new file mode 100644
index 0000000..1609bf7
--- /dev/null
+++ b/tests/eds/perf.vala
@@ -0,0 +1,406 @@
+/*
+ * Copyright © 2011 Collabora Ltd.
+ * Copyright © 2013 Intel Corporation
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Raul Gutierrez Segales <raul gutierrez segales collabora co uk>
+ * Simon McVittie <simon mcvittie collabora co uk>
+ */
+
+using Folks;
+
+public class PerfTests : EdsTest.TestCase
+{
+ public PerfTests ()
+ {
+ base ("PerfTests");
+
+ this.add_test ("unprepared", this._test);
+ this.add_test ("again", this._test);
+ this.add_test ("twice", this._test_twice);
+ this.add_test ("pre-prepared", this._test_preprepared);
+
+ this.add_test ("pre-prepared-one-individual", this._test_preprepared_one);
+ this.add_test ("pre-prepared-one-persona",
+ this._test_preprepared_one_persona);
+
+ if (Test.perf () ||
+ Environment.get_variable ("FOLKS_TESTS_SLOW") != null)
+ {
+ this.add_test ("clones", this._test_clones);
+ this.add_test ("clones-one-individual", this._test_clones_one);
+ this.add_test ("clones-one-persona", this._test_clones_one_persona);
+ }
+ else
+ {
+ message ("skipping slow tests, run with -m perf or " +
+ "FOLKS_TESTS_SLOW=1 to enable");
+ }
+ }
+
+ /* Add a bunch of contacts, going behind Folks' back. Do it in a
+ * subprocess, so that we ignore it when profiling this process. */
+ private void _add_500 ()
+ {
+ Test.timer_start ();
+
+ try
+ {
+ Folks.TestUtils.run_test_helper_sync (
+ { "eds/helper-create-many-contacts",
+ "-n", "500",
+ "-u", ((!) this.eds_backend).address_book_uid });
+ }
+ catch (Error e)
+ {
+ error (e.message);
+ }
+
+ message ("%.6f Finished adding contacts", Test.timer_elapsed ());
+ }
+
+ public override void set_up ()
+ {
+ base.set_up ();
+ this._add_500 ();
+ }
+
+ private uint _test_num = 0;
+
+ public override void create_backend ()
+ {
+ /* Use a unique e-d-s book for each test. */
+ this.eds_backend = new EdsTest.Backend ();
+ ((!) this.eds_backend).set_up (true, "test%u".printf (this._test_num++));
+ }
+
+ /* Prepare the individual aggregator and wait for it to quiesce. */
+ private async IndividualAggregator _aggregate () throws Error
+ {
+ Test.timer_start ();
+
+ message ("%.6f Preparing backend store", Test.timer_elapsed ());
+ var store = BackendStore.dup ();
+ yield store.prepare ();
+
+ message ("%.6f Loading backends", Test.timer_elapsed ());
+ yield store.load_backends ();
+
+ var eds = store.dup_backend_by_name ("eds");
+ assert (eds != null);
+
+ message ("%.6f Waiting for EDS backend", Test.timer_elapsed ());
+ yield TestUtils.backend_prepare_and_wait_for_quiescence ((!) eds);
+
+ message ("%.6f Preparing aggregator", Test.timer_elapsed ());
+ var aggregator = new IndividualAggregator ();
+ yield aggregator.prepare ();
+
+ message ("%.6f Waiting for aggregator", Test.timer_elapsed ());
+ yield TestUtils.aggregator_prepare_and_wait_for_quiescence (aggregator);
+
+ return aggregator;
+ }
+
+ private IndividualAggregator _run_aggregate ()
+ {
+ AsyncResult? result = null;
+ var loop = new MainLoop (null, false);
+
+ this._aggregate.begin ((obj, res) =>
+ {
+ result = res;
+ loop.quit ();
+ });
+
+ TestUtils.loop_run_with_timeout (loop, 60);
+ assert (result != null);
+
+ try
+ {
+ return this._aggregate.end ((!) result);
+ }
+ catch (Error e)
+ {
+ error ("%s #%d: %s", e.domain.to_string (), e.code, e.message);
+ }
+ }
+
+ /* Basic test: prepare the aggregator, then iterate through individuals.
+ * Also part of some subsequent tests.
+ *
+ * This represents a Folks client like gnome-contacts or the Empathy
+ * contact list, whose use case is: give me all my contacts as
+ * Individuals. */
+ private void _test ()
+ {
+ var aggregator = this._run_aggregate ();
+
+ var map = aggregator.individuals;
+ var iter = aggregator.individuals.map_iterator ();
+
+ message ("%.6f Aggregated into %d individuals", Test.timer_elapsed (),
+ map.size);
+
+ while (iter.next ())
+ debug ("%s → %s", iter.get_key (), iter.get_value ().full_name);
+
+ var elapsed = Test.timer_elapsed ();
+ Test.minimized_result (elapsed,
+ "%.6f Total time to iterate %d individuals", elapsed, map.size);
+ }
+
+ /* Basis for tests that look for one individual.
+ *
+ * This represents a Folks client that has a stored individual ID
+ * and wants to display that one Individiual. */
+ private async void _test_one_individual (string id) throws Error
+ {
+ var aggregator = yield this._aggregate ();
+ var map = aggregator.individuals;
+ Individual? individual = yield aggregator.look_up_individual (id);
+
+ assert (individual != null);
+
+ message ("%s => %s", id, ((!) individual).full_name);
+
+ var elapsed = Test.timer_elapsed ();
+ Test.minimized_result (elapsed,
+ "%.6f Total time to find one of %d individuals", elapsed, map.size);
+ }
+
+ private void _run_test_one_individual (string id) throws Error
+ {
+ AsyncResult? result = null;
+ var loop = new MainLoop (null, false);
+
+ this._test_one_individual.begin (id, (obj, res) =>
+ {
+ result = res;
+ loop.quit ();
+ });
+
+ TestUtils.loop_run_with_timeout (loop, 60);
+ assert (result != null);
+
+ this._test_one_individual.end ((!) result);
+ }
+
+ /* Basis for tests that look for one Persona.
+ *
+ * This represents a Folks client that has a stored persona UID,
+ * or can derive one from information known to it (e.g. a Telepathy
+ * identifier), and wants to display that Persona and its
+ * parent Individual. */
+ private async void _test_one_persona (string uid) throws Error
+ {
+ var aggregator = yield this._aggregate ();
+ var map = aggregator.individuals;
+
+ message ("%.6f Aggregated into %d individuals", Test.timer_elapsed (),
+ map.size);
+
+ /* FIXME: #696215: there really ought to be convenience API for
+ * some of this. */
+ string backend_name;
+ string persona_store_id;
+ string persona_id;
+ Persona.split_uid (uid, out backend_name, out persona_store_id,
+ out persona_id);
+ var backend_store = BackendStore.dup ();
+ var backend = backend_store.dup_backend_by_name (backend_name);
+ assert (backend != null);
+ var persona_stores = ((!) backend).persona_stores;
+ var persona_store = persona_stores[persona_store_id];
+ assert (persona_store != null);
+ var personas = ((!) persona_store).personas;
+
+ /* personas is keyed by Persona.iid, which is not the same thing as
+ * either Individual.id or Persona.uid.
+ *
+ * FIXME: surely there ought to be a generic way to do this without
+ * iteration? */
+ Persona? persona = null;
+ var iter = personas.map_iterator ();
+ while (iter.next ())
+ {
+ persona = iter.get_value ();
+
+ if (persona.uid == uid)
+ break;
+ }
+
+ assert (persona != null);
+ var individual = persona.individual;
+ assert (individual != null);
+ message ("%s is part of individual %s", uid, persona.individual.id);
+
+ var elapsed = Test.timer_elapsed ();
+ Test.minimized_result (elapsed,
+ "%.6f Total time to find one of %d individuals by persona uid",
+ elapsed, map.size);
+ }
+
+ private void _run_test_one_persona (string id) throws Error
+ {
+ AsyncResult? result = null;
+ var loop = new MainLoop (null, false);
+
+ this._test_one_persona.begin (id, (obj, res) =>
+ {
+ result = res;
+ loop.quit ();
+ });
+
+ TestUtils.loop_run_with_timeout (loop, 60);
+ assert (result != null);
+
+ this._test_one_persona.end ((!) result);
+ }
+
+ /**
+ * Benchmark whether we go any faster when test() is repeated.
+ */
+ private void _test_twice ()
+ {
+ this._test ();
+ this._test ();
+ }
+
+ /**
+ * Benchmark how much slower we go when we have many similar (or in this
+ * case, identical except for unique identifier) contacts in a backend.
+ *
+ * This is really pretty slow, so it's only run if either the test
+ * was run with -m perf, or FOLKS_TESTS_SLOW is set (because setting
+ * environment variables is easier than passing command-line arguments
+ * while using Automake).
+ */
+ private void _test_clones ()
+ {
+ /* Work with 3 copies of basically the same 500 contacts. */
+ this._add_500 ();
+ this._add_500 ();
+ this._test ();
+ }
+
+ /**
+ * Benchmark how much faster we go when another process has already prepared
+ * our cache for us (assuming we ever implement such a cache).
+ */
+ private void _test_preprepared ()
+ {
+ try
+ {
+ Test.timer_start ();
+ Folks.TestUtils.run_test_helper_sync (
+ { "eds/helper-prepare-aggregator" });
+ message ("%.6f Finished pre-preparation", Test.timer_elapsed ());
+ }
+ catch (Error e)
+ {
+ error (e.message);
+ }
+
+ this._test ();
+ }
+
+ /* test_preprepared() x test_one_individual() */
+ private void _test_preprepared_one ()
+ {
+ try
+ {
+ string capture;
+ Test.timer_start ();
+ Folks.TestUtils.run_test_helper_sync (
+ { "eds/helper-prepare-aggregator", "--print-an-individual-id" },
+ out capture);
+ message ("%.6f Finished pre-preparation", Test.timer_elapsed ());
+
+ capture = capture.chomp ();
+ this._run_test_one_individual (capture);
+ }
+ catch (Error e)
+ {
+ error (e.message);
+ }
+ }
+
+ /* test_preprepared_one() x test_clones() */
+ private void _test_clones_one ()
+ {
+ this._add_500 ();
+ this._add_500 ();
+ this._test_preprepared_one ();
+ }
+
+ /* test_preprepared() x test_one_persona() */
+ private void _test_preprepared_one_persona ()
+ {
+ try
+ {
+ string capture;
+ Test.timer_start ();
+ Folks.TestUtils.run_test_helper_sync (
+ { "eds/helper-prepare-aggregator", "--print-a-persona-uid" },
+ out capture);
+ message ("%.6f Finished pre-preparation", Test.timer_elapsed ());
+
+ capture = capture.chomp ();
+ this._run_test_one_persona (capture);
+ }
+ catch (Error e)
+ {
+ error (e.message);
+ }
+ }
+
+ /* test_preprepared_one_persona() x test_clones() */
+ private void _test_clones_one_persona ()
+ {
+ this._add_500 ();
+ this._add_500 ();
+ this._test_preprepared_one_persona ();
+ }
+
+ public override void tear_down ()
+ {
+ try
+ {
+ Folks.TestUtils.run_test_helper_sync (
+ { "eds/helper-delete-contacts",
+ "-u", ((!) this.eds_backend).address_book_uid });
+ }
+ catch (Error e)
+ {
+ error (e.message);
+ }
+
+ base.tear_down ();
+ }
+}
+
+public int main (string[] args)
+{
+ Test.init (ref args);
+
+ var tests = new PerfTests ();
+ tests.register ();
+ Test.run ();
+ tests.final_tear_down ();
+
+ return 0;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]