From 6e85b2bec3ee5511d596e71438e228ce343c663a Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Mon, 11 Nov 2013 13:44:14 +0000 Subject: bluez: Add a test suite This adds a test suite for the BlueZ backend, using a python-dbusmock mock up of the BlueZ and OBEX D-Bus services. This requires the latest version of python-dbusmock, plus up-to-date versions of GLib and Vala for binding updates. The use of the second and third arguments to AM_PROG_VALAC in configure.ac also necessitates use of automake 1.12 or newer. Only a few test cases have been added so far, covering vCard parsing and general set up of PersonaStores. Using python-dbusmock it should be easy to add more tests covering advanced Bluetooth device appearance/disappearance situations in future. https://bugzilla.gnome.org/show_bug.cgi?id=712274 --- autogen.sh | 13 -- configure.ac | 25 ++- tests/Makefile.am | 7 + tests/bluez/Makefile.am | 48 ++++++ tests/bluez/device-properties.vala | 305 +++++++++++++++++++++++++++++++++ tests/bluez/individual-retrieval.vala | 232 ++++++++++++++++++++++++++ tests/bluez/vcard-parsing.vala | 255 ++++++++++++++++++++++++++++ tests/lib/Makefile.am | 7 + tests/lib/bluez/Makefile.am | 51 ++++++ tests/lib/bluez/backend.vala | 306 ++++++++++++++++++++++++++++++++++ tests/lib/bluez/test-case.vala | 127 ++++++++++++++ tests/lib/test-case.vala | 3 + 12 files changed, 1365 insertions(+), 14 deletions(-) create mode 100644 tests/bluez/Makefile.am create mode 100644 tests/bluez/device-properties.vala create mode 100644 tests/bluez/individual-retrieval.vala create mode 100644 tests/bluez/vcard-parsing.vala create mode 100644 tests/lib/bluez/Makefile.am create mode 100644 tests/lib/bluez/backend.vala create mode 100644 tests/lib/bluez/test-case.vala diff --git a/autogen.sh b/autogen.sh index b3b63b11..a38c8cb8 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,19 +1,6 @@ #!/bin/sh set -e -if test -n "$AUTOMAKE"; then - : # don't override an explicit user request -elif automake-1.11 --version >/dev/null 2>/dev/null && \ - aclocal-1.11 --version >/dev/null 2>/dev/null; then - # If we have automake-1.11, use it. This is the oldest version (=> least - # likely to introduce undeclared dependencies) that will give us - # --enable-silent-rules support. - AUTOMAKE=automake-1.11 - export AUTOMAKE - ACLOCAL=aclocal-1.11 - export ACLOCAL -fi - autoreconf -i -f intltoolize --force --copy --automake diff --git a/configure.ac b/configure.ac index 4ea9298c..729e9719 100644 --- a/configure.ac +++ b/configure.ac @@ -79,7 +79,7 @@ AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([Makefile.am]) AC_CONFIG_HEADERS(config.h) AC_CONFIG_SRCDIR([configure.ac]) -AM_INIT_AUTOMAKE([1.11 dist-xz no-define +AM_INIT_AUTOMAKE([1.12 dist-xz no-define no-dist-gzip tar-ustar -Wno-portability color-tests parallel-tests]) AM_MAINTAINER_MODE([enable]) @@ -336,8 +336,29 @@ AS_IF([test x$enable_ofono_backend = xyes], [ AS_IF([test x$enable_bluez_backend = xyes], [ PKG_CHECK_MODULES([EBOOK], [libebook-1.2 >= $EBOOK_REQUIRED]) + + # Dependencies for the BlueZ tests + PKG_CHECK_MODULES([GLIB_2_39_2], [glib-2.0 >= 2.39.2], + [have_glib_2_39_2=yes], [have_glib_2_39_2=no]) + AM_PATH_PYTHON([3.0], [have_python=yes], [have_python=no]) + + AC_MSG_CHECKING([for python-dbusmock]) + AS_IF([! $PYTHON -c 'import dbusmock' > /dev/null 2>&1], + [have_dbusmock=no], [have_dbusmock=yes]) + AC_MSG_RESULT([$have_dbusmock]) + + AM_PROG_VALAC([0.22.0.45-383d-dirty], + [have_valac_0_22_2=yes], [have_valac_0_22_2=no]) ]) +# The BlueZ tests are conditional on several bleeding-edge dependencies. +# FIXME: Remove this once things have stabilised a bit. +AM_CONDITIONAL([HAVE_BLUEZ_TESTS], + [test "x$have_glib_2_39_2" = "xyes" -a \ + "x$have_python" = "xyes" -a \ + "x$have_dbusmock" = "xyes" -a \ + "x$have_valac_0_22_2" = "xyes"]) + # # Vala building options -- allows tarball builds without installing Vala # @@ -751,6 +772,7 @@ AC_CONFIG_FILES([ docs/Makefile po/Makefile.in tests/Makefile + tests/bluez/Makefile tests/data/Makefile tests/eds/Makefile tests/folks/Makefile @@ -761,6 +783,7 @@ AC_CONFIG_FILES([ tests/tracker/Makefile tests/lib/Makefile tests/lib/folks-test-uninstalled.pc + tests/lib/bluez/Makefile tests/lib/eds/Makefile tests/lib/dummy/Makefile tests/lib/key-file/Makefile diff --git a/tests/Makefile.am b/tests/Makefile.am index 2fad8ce4..38561942 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -8,6 +8,12 @@ SUBDIRS = \ tools \ $(NULL) +if ENABLE_BLUEZ +if HAVE_BLUEZ_TESTS +SUBDIRS += bluez +endif +endif + if ENABLE_TELEPATHY SUBDIRS += folks telepathy endif @@ -34,6 +40,7 @@ DIST_SUBDIRS = \ dummy \ eds \ key-file \ + bluez \ telepathy \ libsocialweb \ tracker \ diff --git a/tests/bluez/Makefile.am b/tests/bluez/Makefile.am new file mode 100644 index 00000000..46661fbf --- /dev/null +++ b/tests/bluez/Makefile.am @@ -0,0 +1,48 @@ +include $(top_srcdir)/tests/test.mk + +AM_VALAFLAGS = \ + $(test_valaflags) \ + --vapidir=$(top_srcdir)/tests/lib/bluez \ + --pkg bluez-test \ + --pkg folks-generics \ + $(NULL) + +AM_CPPFLAGS = \ + $(test_cppflags) \ + -I$(top_srcdir)/tests/lib/bluez \ + $(NULL) + +AM_CFLAGS = \ + $(test_cflags) \ + $(NULL) + +LDADD = \ + $(AM_LDADD) \ + $(test_ldadd) \ + $(top_builddir)/tests/lib/bluez/libbluez-test.la \ + $(NULL) + +# in order from least to most complex +noinst_PROGRAMS = \ + device-properties \ + individual-retrieval \ + vcard-parsing \ + $(NULL) + +TESTS = $(noinst_PROGRAMS) + +device_properties_SOURCES = \ + device-properties.vala \ + $(NULL) + +individual_retrieval_SOURCES = \ + individual-retrieval.vala \ + $(NULL) + +vcard_parsing_SOURCES = \ + vcard-parsing.vala \ + $(NULL) + +-include $(top_srcdir)/git.mk +-include $(top_srcdir)/valgrind.mk +-include $(top_srcdir)/check.mk diff --git a/tests/bluez/device-properties.vala b/tests/bluez/device-properties.vala new file mode 100644 index 00000000..19d806a5 --- /dev/null +++ b/tests/bluez/device-properties.vala @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2013 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: Philip Withnall + */ + +using Gee; +using Folks; +using BluezTest; + +public class DevicePropertiesTests : BluezTest.TestCase +{ + public DevicePropertiesTests () + { + base ("DeviceProperties"); + + this.add_test ("device pairing", this.test_device_pairing); + this.add_test ("blocked device", this.test_blocked_device); + this.add_test ("device alias", this.test_device_alias); + } + + /* Start with an unpaired Bluetooth device, and check that it’s not turned + * into a PersonaStore. Then pair the device, and check that it is added as + * a store. */ + public void test_device_pairing () + { + var main_loop = new GLib.MainLoop (null, false); + + /* Set up a simple unpaired device. */ + try + { + this.bluez_backend.mock_bluez.add_adapter ("hci0", "Test System"); + this.bluez_backend.mock_bluez.add_device ("hci0", + this.bluez_backend.primary_device_address, "My Phone"); + } + catch (IOError e1) + { + error ("Error setting up mock BlueZ device: %s", e1.message); + } + + /* Set up its vCard in preparation. */ + this.bluez_backend.set_simple_device_vcard ( + "BEGIN:VCARD\n" + + "VERSION:3.0\n" + + "N:Jones;Pam;Mrs.\n" + + "FN:Pam Jones\n" + + "TEL:0123456789\n" + + "END:VCARD\n"); + + /* Set up the aggregator and wait until either quiescence, or the test + * times out and fails. Unset the primary store to prevent a warning. */ + Environment.set_variable ("FOLKS_PRIMARY_STORE", "", true); + + var aggregator = IndividualAggregator.dup (); + TestUtils.aggregator_prepare_and_wait_for_quiescence.begin (aggregator, + (o, r) => + { + try + { + TestUtils.aggregator_prepare_and_wait_for_quiescence.end (r); + } + catch (GLib.Error e2) + { + error ("Error preparing aggregator: %s", e2.message); + } + + main_loop.quit (); + }); + + TestUtils.loop_run_with_timeout (main_loop); + + var real_backend = + aggregator.backend_store.dup_backend_by_name ("bluez"); + + /* Check there are no individuals and no persona stores. */ + assert (aggregator.individuals.size == 0); + assert (real_backend.persona_stores.size == 0); + + /* Wait for a signal about an added persona store. */ + TestUtils.aggregator_wait_for_individuals.begin (aggregator, + {"Pam Jones"}, {}, (o, r) => + { + TestUtils.aggregator_wait_for_individuals.end (r); + main_loop.quit (); + }); + + /* Pair the device. */ + try + { + this.bluez_backend.mock_bluez.pair_device ("hci0", + this.bluez_backend.primary_device_address); + } + catch (IOError e4) + { + error ("Error pairing mock BlueZ device: %s", e4.message); + } + + TestUtils.loop_run_with_timeout (main_loop); + } + + /* Start with a blocked Bluetooth device, and check it’s not made into a + * PersonaStore. Then unblock the device and check a PersonaStore is created. + * Then block the device again and check the PersonaStore is removed. */ + public void test_blocked_device () + { + var main_loop = new GLib.MainLoop (null, false); + + /* Set up a simple paired but blocked device. */ + this.bluez_backend.create_simple_device_with_vcard ( + "BEGIN:VCARD\n" + + "VERSION:3.0\n" + + "N:Jones;Pam;Mrs.\n" + + "FN:Pam Jones\n" + + "TEL:0123456789\n" + + "END:VCARD\n"); + + try + { + this.bluez_backend.mock_bluez.block_device ("hci0", + this.bluez_backend.primary_device_address); + } + catch (IOError e1) + { + error ("Error blocking device: %s", e1.message); + } + + /* Set up the aggregator and wait until either quiescence, or the test + * times out and fails. Unset the primary store to prevent a warning. */ + Environment.set_variable ("FOLKS_PRIMARY_STORE", "", true); + + var aggregator = IndividualAggregator.dup (); + TestUtils.aggregator_prepare_and_wait_for_quiescence.begin (aggregator, + (o, r) => + { + try + { + TestUtils.aggregator_prepare_and_wait_for_quiescence.end (r); + } + catch (GLib.Error e2) + { + error ("Error preparing aggregator: %s", e2.message); + } + + main_loop.quit (); + }); + + TestUtils.loop_run_with_timeout (main_loop); + + var real_backend = + aggregator.backend_store.dup_backend_by_name ("bluez"); + + /* Check there are no individuals and no persona stores. */ + assert (aggregator.individuals.size == 0); + assert (real_backend.persona_stores.size == 0); + + /* Wait for a signal about an added persona store. */ + TestUtils.aggregator_wait_for_individuals.begin (aggregator, + {"Pam Jones"}, {}, (o, r) => + { + TestUtils.aggregator_wait_for_individuals.end (r); + main_loop.quit (); + }); + + /* Unblock the device. */ + try + { + this.bluez_backend.mock_bluez.pair_device ("hci0", + this.bluez_backend.primary_device_address); + } + catch (IOError e4) + { + error ("Error blocking device: %s", e4.message); + } + + TestUtils.loop_run_with_timeout (main_loop); + + /* Wait for a signal about a removed persona store. */ + TestUtils.aggregator_wait_for_individuals.begin (aggregator, + {}, {"Pam Jones"}, (o, r) => + { + TestUtils.aggregator_wait_for_individuals.end (r); + main_loop.quit (); + }); + + /* Block the device again. */ + try + { + this.bluez_backend.mock_bluez.block_device ("hci0", + this.bluez_backend.primary_device_address); + } + catch (IOError e5) + { + error ("Error blocking device again: %s", e5.message); + } + + TestUtils.loop_run_with_timeout (main_loop); + + /* Check there are no individuals and no persona stores. */ + assert (aggregator.individuals.size == 0); + assert (real_backend.persona_stores.size == 0); + } + + /* Test that changes of a device’s Alias property result in the PersonaStore’s + * display-name being updated. */ + public void test_device_alias () + { + /* Set up the backend. */ + string device_path = ""; + this.bluez_backend.create_simple_device_with_vcard ( + "BEGIN:VCARD\n" + + "VERSION:3.0\n" + + "N:Jones;Pam;Mrs.\n" + + "FN:Pam Jones\n" + + "TEL:0123456789\n" + + "END:VCARD\n", + null, out device_path); + + var aggregator = IndividualAggregator.dup (); + TestUtils.aggregator_prepare_and_wait_for_individuals_sync_with_timeout ( + aggregator, {"Pam Jones"}); + + /* Check the PersonaStore’s alias. */ + var real_backend = + aggregator.backend_store.dup_backend_by_name ("bluez"); + assert (real_backend.persona_stores.size == 1); + + var real_store = + real_backend.persona_stores.get ( + this.bluez_backend.primary_device_address); + + /* FIXME: Have to get the display-name this way because + * Folks.PersonaStore.display_name is not declared as abstract. */ + string display_name = ""; + real_store.get ("display-name", out display_name); + assert (display_name == "My Phone"); + + /* Change the device’s Alias and see if the display-name changes. */ + var main_loop = new GLib.MainLoop (null, false); + real_store.notify["display-name"].connect ((p) => + { + real_store.get ("display-name", out display_name); + assert (display_name == "New Alias!"); + main_loop.quit (); + }); + + try + { + Device mock_device = + Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", device_path); + org.freedesktop.DBus.Mock mock = + Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", device_path); + + var props = new HashTable (str_hash, str_equal); + props.insert ("Alias", "New Alias!"); + + mock_device.alias = "New Alias!"; + mock.emit_signal ("org.freedesktop.DBus.Properties", + "PropertiesChanged", "sa{sv}as", + { + "org.bluez.Device1", + props, + new Variant.array (VariantType.STRING, {}) + }); + } + catch (IOError e1) + { + error ("Error setting device alias: %s", e1.message); + } + + TestUtils.loop_run_with_timeout (main_loop); + } +} + +/* Mini-copy of the org-bluez.vala file in the BlueZ backend. */ +[DBus (name = "org.bluez.Device1")] +public interface Device : Object + { + [DBus (name = "Alias")] + public abstract string alias { owned get; set; } + } + +public int main (string[] args) +{ + Test.init (ref args); + + var tests = new DevicePropertiesTests (); + tests.register (); + Test.run (); + tests.final_tear_down (); + + return 0; +} diff --git a/tests/bluez/individual-retrieval.vala b/tests/bluez/individual-retrieval.vala new file mode 100644 index 00000000..033821ed --- /dev/null +++ b/tests/bluez/individual-retrieval.vala @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2013 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: Philip Withnall + */ + +using Gee; +using Folks; +using BluezTest; + +public class IndividualRetrievalTests : BluezTest.TestCase +{ + public IndividualRetrievalTests () + { + base ("IndividualRetrieval"); + + this.add_test ("singleton individuals", this.test_singleton_individuals); + this.add_test ("empty address book", this.test_empty_address_book); + this.add_test ("photos downloaded later", + this.test_photos_downloaded_later); + } + + /* Test that personas on a pre-existing Bluetooth device are successfully + * downloaded and presented as singleton individuals by the aggregator. */ + public void test_singleton_individuals () + { + var main_loop = new GLib.MainLoop (null, false); + + /* Set up the backend. */ + this.bluez_backend.create_simple_device_with_vcard ( + "BEGIN:VCARD\n" + + "VERSION:3.0\n" + + "N:Gump;Forrest;Mr.\n" + + "FN:Forrest Gump\n" + + "NICKNAME:Fir\n" + + "TEL;TYPE=WORK,VOICE:(111) 555-1212\n" + + "TEL;TYPE=HOME,VOICE:(404) 555-1212\n" + + "EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com\n" + + "URL;TYPE=HOME:http://example.com/\n" + + "END:VCARD\n" + + "\n" + + "BEGIN:VCARD\n" + + "VERSION:3.0\n" + + "N:Jones;Pam;Mrs.\n" + + "FN:Pam Jones\n" + + "TEL:0123456789\n" + + "END:VCARD\n"); + + /* Set up the aggregator and wait until either the expected persona are + * seen, or the test times out and fails. */ + var aggregator = IndividualAggregator.dup (); + TestUtils.aggregator_prepare_and_wait_for_individuals.begin (aggregator, + {"Forrest Gump", "Pam Jones"}, (o, r) => + { + try + { + TestUtils.aggregator_prepare_and_wait_for_individuals.end (r); + } + catch (GLib.Error e1) + { + error ("Error preparing aggregator: %s", e1.message); + } + + main_loop.quit (); + }); + + TestUtils.loop_run_with_timeout (main_loop); + } + + /* Test that an empty address book is handled correctly. */ + public void test_empty_address_book () + { + var main_loop = new GLib.MainLoop (null, false); + + /* Set up the backend with *no* contacts. */ + this.bluez_backend.create_simple_device_with_vcard (""); + + /* Set up the aggregator and wait until either quiescence, or the test + * times out and fails. */ + var aggregator = IndividualAggregator.dup (); + TestUtils.aggregator_prepare_and_wait_for_quiescence.begin (aggregator, + (o, r) => + { + try + { + TestUtils.aggregator_prepare_and_wait_for_quiescence.end (r); + } + catch (GLib.Error e1) + { + error ("Error preparing aggregator: %s", e1.message); + } + + main_loop.quit (); + }); + + TestUtils.loop_run_with_timeout (main_loop); + + /* Check there are no individuals. */ + assert (aggregator.individuals.size == 0); + } + + /* Test that photos are downloaded in a second sweep of the address book. */ + public void test_photos_downloaded_later () + { + var main_loop = new GLib.MainLoop (null, false); + + /* Set up the backend, at first with a vCard without a photo. */ + var vcard_signal_id = this.bluez_backend.create_simple_device_with_vcard ( + "BEGIN:VCARD\n" + + "VERSION:3.0\n" + + "N:Gump;Forrest;Mr.\n" + + "FN:Forrest Gump\n" + + "NICKNAME:Fir\n" + + "TEL;TYPE=WORK,VOICE:(111) 555-1212\n" + + "TEL;TYPE=HOME,VOICE:(404) 555-1212\n" + + "EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com\n" + + "URL;TYPE=HOME:http://example.com/\n" + + "END:VCARD\n"); + + /* Set up the aggregator and wait until either the expected persona are + * seen, or the test times out and fails. */ + var aggregator = IndividualAggregator.dup (); + TestUtils.aggregator_prepare_and_wait_for_individuals.begin (aggregator, + {"Forrest Gump"}, (o, r) => + { + try + { + TestUtils.aggregator_prepare_and_wait_for_individuals.end (r); + } + catch (GLib.Error e1) + { + error ("Error preparing aggregator: %s", e1.message); + } + + main_loop.quit (); + }); + + TestUtils.loop_run_with_timeout (main_loop); + + /* Re-set the backend to now return a vCard with a photo (and nothing + * else). */ + this.bluez_backend.mock_obex.disconnect (vcard_signal_id); + this.bluez_backend.set_simple_device_vcard ( + "BEGIN:VCARD\n" + + "VERSION:3.0\n" + + "N:Gump;Forrest;Mr.\n" + + "FN:Forrest Gump\n" + + "NICKNAME:Fir\n" + + "TEL;TYPE=WORK,VOICE:(111) 555-1212\n" + + "TEL;TYPE=HOME,VOICE:(404) 555-1212\n" + + "EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com\n" + + "URL;TYPE=HOME:http://example.com/\n" + + "PHOTO;TYPE=jpeg;ENCODING=b:" + + "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\n" + + "END:VCARD\n"); + + /* The individual should not have a photo to begin with; wait until one + * appears. */ + assert (aggregator.individuals.size == 1); + var iter = aggregator.individuals.map_iterator (); + while (iter.next () == true) + { + var individual = iter.get_value (); + assert (individual.avatar == null); + + /* Wait for it to change. Assert that only the avatar changes. */ + individual.notify.connect ((pspec) => + { + assert (pspec.name == "avatar"); + assert (individual.avatar != null); + main_loop.quit (); + }); + } + + /* There’s normally a 5s wait between poll attempts in the backend, but + * we set the FOLKS_BLUEZ_TIMEOUT_DIVISOR in the TestCase to reduce + * this. */ + TestUtils.loop_run_with_timeout (main_loop); + } +} + +public int main (string[] args) +{ + Test.init (ref args); + + var tests = new IndividualRetrievalTests (); + tests.register (); + Test.run (); + tests.final_tear_down (); + + return 0; +} diff --git a/tests/bluez/vcard-parsing.vala b/tests/bluez/vcard-parsing.vala new file mode 100644 index 00000000..f9e91002 --- /dev/null +++ b/tests/bluez/vcard-parsing.vala @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2013 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: Philip Withnall + */ + +using Gee; +using Folks; +using BluezTest; + +public class VcardParsingTests : BluezTest.TestCase +{ + public VcardParsingTests () + { + base ("VcardParsing"); + + this.add_test ("multiple attributes", this.test_multiple_attributes); + this.add_test ("name components", this.test_name_components); + this.add_test ("encoding", this.test_encoding); + } + + /* Test that vCards containing multiple attributes with the same name (e.g. + * multiple phone numbers or e-mail addresses) are parsed correctly. */ + public void test_multiple_attributes () + { + /* Set up the backend. */ + this.bluez_backend.create_simple_device_with_vcard ( + "BEGIN:VCARD\n" + + "VERSION:3.0\n" + + "FN:Forrest Gump\n" + + "TEL;TYPE=WORK,VOICE:(111) 555-1212\n" + + "TEL;TYPE=HOME,VOICE:(404) 555-1212\n" + + "EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com\n" + + "EMAIL:test@example.com\n" + + "URL;TYPE=HOME:http://example.com/\n" + + "URL:http://forest.com/\n" + + "URL:https://test.com/\n" + + "END:VCARD\n"); + + /* Set up the aggregator and wait until either the expected persona are + * seen, or the test times out and fails. */ + var aggregator = IndividualAggregator.dup (); + TestUtils.aggregator_prepare_and_wait_for_individuals_sync_with_timeout ( + aggregator, {"Forrest Gump"}); + + /* Check the properties of our friend Forrest. */ + var ind = TestUtils.get_individual_by_name (aggregator, "Forrest Gump"); + + var expected_phone_numbers = new SmallSet ( + AbstractFieldDetails.hash_static, + AbstractFieldDetails.equal_static); + + var expected_phone_fd = new PhoneFieldDetails ("(111) 555-1212"); + expected_phone_fd.add_parameter ("type", "work"); + expected_phone_fd.add_parameter ("type", "voice"); + expected_phone_numbers.add (expected_phone_fd); + + expected_phone_fd = new PhoneFieldDetails ("(404) 555-1212"); + expected_phone_fd.add_parameter ("type", "home"); + expected_phone_fd.add_parameter ("type", "voice"); + expected_phone_numbers.add (expected_phone_fd); + + var expected_email_addresses = new SmallSet ( + AbstractFieldDetails.hash_static, + AbstractFieldDetails.equal_static); + + var expected_email_fd = new EmailFieldDetails ("forrestgump@example.com"); + expected_email_fd.add_parameter ("type", "pref"); + expected_email_fd.add_parameter ("type", "internet"); + expected_email_addresses.add (expected_email_fd); + + expected_email_fd = new EmailFieldDetails ("test@example.com"); + expected_email_addresses.add (expected_email_fd); + + var expected_uris = new SmallSet ( + AbstractFieldDetails.hash_static, + AbstractFieldDetails.equal_static); + + var expected_uri_fd = new UrlFieldDetails ("http://example.com/"); + expected_uri_fd.add_parameter ("type", "home"); + expected_uris.add (expected_uri_fd); + + expected_uri_fd = new UrlFieldDetails ("http://forest.com/"); + expected_uris.add (expected_uri_fd); + + expected_uri_fd = new UrlFieldDetails ("https://test.com/"); + expected_uris.add (expected_uri_fd); + + assert (Utils.set_afd_equal (ind.phone_numbers, expected_phone_numbers)); + assert (Utils.set_afd_equal (ind.email_addresses, + expected_email_addresses)); + assert (Utils.set_afd_equal (ind.urls, expected_uris)); + } + + /* Test that vCards with different numbers of values for their N (structured + * name) attribute are parsed correctly. */ + public void test_name_components () + { + /* Set up the backend. */ + this.bluez_backend.create_simple_device_with_vcard ( + /* Valid N attributes. */ + "BEGIN:VCARD\n" + + "VERSION:3.0\n" + + "FN:John Public\n" + + "N:Public;John;Quinlan;Mr.;Esq.\n" + + "END:VCARD\n" + + "\n" + + "BEGIN:VCARD\n" + + "VERSION:3.0\n" + + "FN:John Stevenson\n" + + "N:Stevenson;John;Philip,Paul;Dr.;Jr.,M.D.,A.C.P.\n" + + "END:VCARD\n" + + "\n" + + "BEGIN:VCARD\n" + + "VERSION:3.0\n" + + "FN:Franco Dianno\n" + + "N:Dianno;Franco;;;\n" + + "END:VCARD\n" + + "\n" + + /* Invalid N attributes (but we should handle them anyway). */ + "BEGIN:VCARD\n" + + "VERSION:3.0\n" + + "FN:Amelia Smith\n" + + "N:Smith;Amelia;David;Dr.\n" + + "END:VCARD\n" + + "\n" + + "BEGIN:VCARD\n" + + "VERSION:3.0\n" + + "FN:Sadie Jones\n" + + "N:Jones;Sadie;M.\n" + + "END:VCARD\n" + + "\n" + + "BEGIN:VCARD\n" + + "VERSION:3.0\n" + + "FN:Alex Lawson\n" + + "N:Lawson;Alex\n" + + "END:VCARD\n"); + + /* Set up the aggregator and wait until either the expected persona are + * seen, or the test times out and fails. */ + var aggregator = IndividualAggregator.dup (); + TestUtils.aggregator_prepare_and_wait_for_individuals_sync_with_timeout ( + aggregator, + { + "John Public", + "John Stevenson", + "Franco Dianno", + "Amelia Smith", + "Sadie Jones", + "Alex Lawson" + }); + + /* Check the properties of our individuals. */ + var ind = TestUtils.get_individual_by_name (aggregator, "John Public"); + var expected_name = + new StructuredName ("Public", "John", "Quinlan", "Mr.", "Esq."); + assert (ind.structured_name.equal (expected_name)); + + ind = TestUtils.get_individual_by_name (aggregator, "John Stevenson"); + expected_name = + new StructuredName ("Stevenson", "John", "Philip,Paul", "Dr.", + "Jr.,M.D.,A.C.P."); + assert (ind.structured_name.equal (expected_name)); + + ind = TestUtils.get_individual_by_name (aggregator, "Franco Dianno"); + expected_name = new StructuredName ("Dianno", "Franco", null, null, null); + assert (ind.structured_name.equal (expected_name)); + + ind = TestUtils.get_individual_by_name (aggregator, "Amelia Smith"); + expected_name = + new StructuredName ("Smith", "Amelia", "David", "Dr.", null); + assert (ind.structured_name.equal (expected_name)); + + ind = TestUtils.get_individual_by_name (aggregator, "Sadie Jones"); + expected_name = new StructuredName ("Jones", "Sadie", "M.", null, null); + assert (ind.structured_name.equal (expected_name)); + + ind = TestUtils.get_individual_by_name (aggregator, "Alex Lawson"); + expected_name = new StructuredName ("Lawson", "Alex", null, null, null); + assert (ind.structured_name.equal (expected_name)); + } + + /* Test that vCards with weird encodings are parsed correctly. */ + public void test_encoding () + { + /* Set up the backend. */ + this.bluez_backend.create_simple_device_with_vcard ( + /* From https://bugs.kde.org/show_bug.cgi?id=98790 */ + "BEGIN:VCARD\n" + + "VERSION:2.1\n" + + "FN:Test 1\n" + + "N;CHARSET=UTF-8:溌剌;元気\n" + + "END:VCARD\n" + + "\n" + + /* From https://git.gnome.org/browse/evolution-data-server/tree/tests/ + * libebook-contacts/test-vcard-parsing.c#n360 */ + "BEGIN:VCARD\n" + + "VERSION:2.1\n" + + "FN;ENCODING=quoted-printable:ActualValue=20=C4=9B=C5=A1" + + "=C4=8D=C5=99=C5=BE=C3=BD=C3=A1=C3=AD=C3=A9=C3=BA=C5=AF=C3" + + "=B3=C3=B6=C4=9A=C5=A0=C4=8C=C5=98=C5=BD=C3=9D=C3=81=C3=8D" + + "=C3=89=C3=9A=C5=AE=C3=93=C3=96=C2=A7=201234567890=2012345" + + "67890=201234567890=201234567890=201234567890\n" + + "END:VCARD\n"); + + /* Set up the aggregator and wait until either the expected persona are + * seen, or the test times out and fails. */ + var aggregator = IndividualAggregator.dup (); + TestUtils.aggregator_prepare_and_wait_for_individuals_sync_with_timeout ( + aggregator, + { + "Test 1", + "ActualValue ěščřžýáíéúůóöĚŠČŘŽÝÁÍÉÚŮÓÖ§ " + + "1234567890 1234567890 1234567890 1234567890 1234567890" + }); + + /* Check the properties of our individuals. */ + var ind = TestUtils.get_individual_by_name (aggregator, "Test 1"); + var expected_name = + new StructuredName ("溌剌", "元気", null, null, null); + assert (ind.structured_name.equal (expected_name)); + + ind = + TestUtils.get_individual_by_name (aggregator, + "ActualValue ěščřžýáíéúůóöĚŠČŘŽÝÁÍÉÚŮÓÖ§ " + + "1234567890 1234567890 1234567890 1234567890 1234567890"); + assert (ind.full_name == "ActualValue ěščřžýáíéúůóöĚŠČŘŽÝÁÍÉÚŮÓÖ§ " + + "1234567890 1234567890 1234567890 1234567890 1234567890"); + } +} + +public int main (string[] args) +{ + Test.init (ref args); + + var tests = new VcardParsingTests (); + tests.register (); + Test.run (); + tests.final_tear_down (); + + return 0; +} diff --git a/tests/lib/Makefile.am b/tests/lib/Makefile.am index 4486e433..a6e958f0 100644 --- a/tests/lib/Makefile.am +++ b/tests/lib/Makefile.am @@ -5,6 +5,12 @@ SUBDIRS = \ key-file \ $(NULL) +if ENABLE_BLUEZ +if HAVE_BLUEZ_TESTS +SUBDIRS += bluez +endif +endif + if ENABLE_TELEPATHY # Build the contactlist first because autotools fails to recognize the # dependencies implicitly. There may be a better way to fix this, but then I'd @@ -30,6 +36,7 @@ endif DIST_SUBDIRS = \ dummy \ key-file \ + bluez \ telepathy \ eds \ libsocialweb \ diff --git a/tests/lib/bluez/Makefile.am b/tests/lib/bluez/Makefile.am new file mode 100644 index 00000000..a2a382dd --- /dev/null +++ b/tests/lib/bluez/Makefile.am @@ -0,0 +1,51 @@ +noinst_LTLIBRARIES = libbluez-test.la + +libbluez_test_la_VALAFLAGS = \ + $(AM_VALAFLAGS) \ + $(TARGET_VALAFLAGS) \ + $(ERROR_VALAFLAGS) \ + --library bluez-test \ + --vapi bluez-test.vapi \ + --header bluez-test.h \ + --vapidir=$(abs_srcdir) \ + --vapidir=$(abs_builddir) \ + --vapidir=$(abs_top_srcdir)/folks \ + --vapidir=$(abs_top_builddir)/folks \ + --vapidir=$(abs_top_srcdir)/tests/lib \ + --vapidir=$(abs_top_builddir)/tests/lib \ + --pkg folks-test \ + --pkg folks-test-dbus \ + -g \ + $(NULL) + +libbluez_test_la_SOURCES = \ + backend.vala \ + test-case.vala \ + $(NULL) + +libbluez_test_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -include $(top_srcdir)/folks/warnings.h \ + $(NULL) + +libbluez_test_la_CFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/tests/lib \ + $(AM_CFLAGS) \ + $(ERROR_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(GEE_CFLAGS) \ + $(NULL) + +libbluez_test_la_LIBADD = \ + $(top_builddir)/tests/lib/libfolks-test.la \ + $(GLIB_LIBS) \ + $(GEE_LIBS) \ + $(NULL) + +EXTRA_DIST = \ + bluez-test.vapi \ + bluez-test.h \ + $(NULL) + +-include $(top_srcdir)/git.mk diff --git a/tests/lib/bluez/backend.vala b/tests/lib/bluez/backend.vala new file mode 100644 index 00000000..1ba34a2a --- /dev/null +++ b/tests/lib/bluez/backend.vala @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2013 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: + * Philip Withnall + */ + +using GLib; + +/* Specific mock interfaces for the BlueZ and OBEX services. */ +namespace org + { + namespace bluez + { + /* Interface for the bluez5 python-dbusmock template. */ + [DBus (name = "org.bluez.Mock")] + public interface Mock : Object + { + [DBus (name = "AddAdapter")] + public abstract string add_adapter (string device_name, + string system_name) throws IOError; + + [DBus (name = "AddDevice")] + public abstract string add_device (string adapter_device_name, + string device_address, string alias) throws IOError; + + [DBus (name = "PairDevice")] + public abstract void pair_device (string adapter_device_name, + string device_address) throws IOError; + + [DBus (name = "BlockDevice")] + public abstract void block_device (string adapter_device_name, + string device_address) throws IOError; + } + + namespace obex + { + /* Interface for the bluez5-obex python-dbusmock template. */ + [DBus (name = "org.bluez.obex.Mock")] + public interface Mock : Object + { + [DBus (name = "TransferCreated")] + public abstract signal void transfer_created (string path, + HashTable filters, + string transfer_filename); + } + + namespace transfer1 + { + [DBus (name = "org.bluez.obex.transfer1.Mock")] + public interface Mock : Object + { + [DBus (name = "UpdateStatus")] + public abstract void update_status (bool is_complete) + throws IOError; + } + } + } + } + } + +/** + * Controller for a mock BlueZ backend. + * + * This contains control methods to instantiate and manipulate a mock BlueZ + * service over D-Bus, for the purposes of testing the folks BlueZ backend. + * + * The mock service uses python-dbusmock, with control messages being sent to + * ``*.Mock`` interfaces on the D-Bus objects. Those control interfaces are + * exposed as {@link Backend.mock_bluez}, {@link Backend.mock_bluez_base}, + * {@link Backend.mock_obex} and {@link Backend.mock_obex_base}. + * + * @since UNRELEASED + */ +public class BluezTest.Backend +{ + private org.bluez.Mock? _mock_bluez = null; + private org.freedesktop.DBus.Mock? _mock_bluez_base = null; + private org.bluez.obex.Mock? _mock_obex = null; + private org.freedesktop.DBus.Mock? _mock_obex_base = null; + + /** + * D-Bus proxy for the BlueZ-specific mock interface on the org.bluez object. + * + * @since UNRELEASED + */ + public org.bluez.Mock? mock_bluez + { + get { return this._mock_bluez; } + } + + /** + * D-Bus proxy for the dbusmock mock interface on the org.bluez object. + * + * @since UNRELEASED + */ + public org.freedesktop.DBus.Mock? mock_bluez_base + { + get { return this._mock_bluez_base; } + } + + /** + * D-Bus proxy for the BlueZ-specific mock interface on the org.bluez.obex + * object. + * + * @since UNRELEASED + */ + public org.bluez.obex.Mock? mock_obex + { + get { return this._mock_obex; } + } + + /** + * D-Bus proxy for the dbusmock mock interface on the org.bluez.obex object. + * + * @since UNRELEASED + */ + public org.freedesktop.DBus.Mock? mock_obex_base + { + get { return this._mock_obex_base; } + } + + /** + * Default Bluetooth address used for the primary adapter. + * + * This is the address used for the primary Bluetooth adapter (``hci0``) + * unless otherwise specified. + * + * @since UNRELEASED + */ + public string primary_device_address + { + get { return "00:00:00:00:00:00"; } + } + + /** + * Set up the mock D-Bus interfaces. + * + * This must be called before every different unit test. It creates D-Bus + * proxies for the dbusmock objects, auto-launching python-dbusmock if + * necessary. + * + * The required D-Bus service files must previously have been set up with the + * buses which are in use. This is done in + * {@link TestCase.create_transient_dir}. + * + * @since UNRELEASED + */ + public void set_up () + { + /* Create proxies for the client code to use. This auto-starts the + * services. Their service files are created in TestCase. */ + try + { + this._mock_bluez = + Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", "/"); + this._mock_bluez_base = + Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", "/"); + + this._mock_obex = + Bus.get_proxy_sync (BusType.SESSION, "org.bluez.obex", "/"); + this._mock_obex_base = + Bus.get_proxy_sync (BusType.SESSION, "org.bluez.obex", "/"); + } + catch (GLib.Error e1) + { + /* Tidy up. */ + this.tear_down (); + + error ("Error connecting to mock object: %s", e1.message); + } + } + + /** + * Tear down the mock D-Bus interfaces. + * + * This must be called after every different unit test. It undoes + * {@link Backend.set_up}, although the python-dbusmock processes are kept + * around and reset, rather than being killed. + * + * @since UNRELEASED + */ + public void tear_down () + { + /* Reset the python-dbusmock state. */ + try + { + this._mock_obex_base.reset (); + this._mock_bluez_base.reset (); + } + catch (IOError e1) + { + error ("Error resetting python-dbusmock state: %s", e1.message); + } + + /* Remove the D-Bus proxies. The python-dbusmock instances will close by + * themselves when the mock D-Bus buses are destroyed in + * final_tear_down(). */ + this._mock_obex_base = null; + this._mock_bluez_base = null; + this._mock_obex = null; + this._mock_bluez = null; + } + + /** + * Create a simple Bluetooth device with the given vCard. + * + * Create a new Bluetooth adapter (``hci0``) and a new Bluetooth device (with + * address {@link Backend.primary_device_address}). Pair with the Bluetooth + * device and simulate it having the given ``vcard`` (potentially containing + * multiple whitespace-separated entries) as its address book. + * + * On error this function will abort the test. + * + * @param vcard series of vCards for the device’s address book + * @param adapter_path optional return location for the adapter’s D-Bus object + * path + * @param device_path optional return location for the device’s D-Bus object + * path + * @return ID of the signal returning the vCard, as per + * {@link Backend.set_simple_device_vcard} + * + * @since UNRELEASED + */ + public ulong create_simple_device_with_vcard (string vcard, + out string? adapter_path = null, out string? device_path = null) + { + try + { + /* Set up a Bluetooth adapter and a single persona store. */ + adapter_path = this.mock_bluez.add_adapter ("hci0", "Test System"); + device_path = + this.mock_bluez.add_device ("hci0", this.primary_device_address, + "My Phone"); + + /* Pair with the phone. */ + this.mock_bluez.pair_device ("hci0", this.primary_device_address); + + /* Set the vCard to be returned for all transfers. */ + return this.set_simple_device_vcard (vcard); + } + catch (IOError e1) + { + error ("Error setting up mock BlueZ device: %s", e1.message); + } + } + + /** + * Set the vCard to be returned by a simple Bluetooth device. + * + * This sets the vCard which will be returned indefinitely. It returns a + * signal ID which may be disconnected with: + * {{{ + * this.mock_obex.disconnect (signal_id); + * }}} + * to prevent the vCard being returned in future. + * + * @param vcard series of vCards for the device’s address book + * @return ID of the signal returning the vCard + * + * @since UNRELEASED + */ + public ulong set_simple_device_vcard (string vcard) + { + /* Wait for a transfer to be created. Skip activating it and go + * straight to completion. */ + return this.mock_obex.transfer_created.connect ((p, f, v) => + { + org.bluez.obex.transfer1.Mock proxy; + + try + { + FileUtils.set_contents (v, vcard); + } + catch (FileError e1) + { + error ("Error writing vCard transfer file ‘%s’: %s", + v, e1.message); + } + + try + { + proxy = + Bus.get_proxy_sync (BusType.SESSION, "org.bluez.obex", p); + proxy.update_status (true); + } + catch (GLib.Error e1) + { + error ("Error activating transfer: %s", e1.message); + } + }); + } +} diff --git a/tests/lib/bluez/test-case.vala b/tests/lib/bluez/test-case.vala new file mode 100644 index 00000000..accaf0b1 --- /dev/null +++ b/tests/lib/bluez/test-case.vala @@ -0,0 +1,127 @@ +/* + * Copyright © 2013 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Philip Withnall + */ + +/** + * A test case for the BlueZ backend, whose private D-Bus session contains the + * necessary python-dbusmock instance to mock up BlueZ. + * + * @since UNRELEASED + */ +public class BluezTest.TestCase : Folks.TestCase +{ + /** + * A BlueZ backend, normally non-null between set_up() and tear_down(). + * + * If this is non-null, the subclass is expected to have called + * its set_up() method at some point before tear_down() is reached. + * This usually happens in create_backend(). + */ + public BluezTest.Backend? bluez_backend = null; + + public TestCase (string name) + { + base (name); + + this.bluez_backend = new BluezTest.Backend (); + + Environment.set_variable ("FOLKS_BACKENDS_ALLOWED", "bluez", true); + Environment.set_variable ("FOLKS_PRIMARY_STORE", "bluez", true); + Environment.set_variable ("FOLKS_BLUEZ_TIMEOUT_DIVISOR", "100", true); + } + + /** + * {@inheritDoc} + * + * @since UNRELEASED + */ + public override void set_up () + { + base.set_up (); + this.create_backend (); + this.configure_primary_store (); + } + + /** + * {@inheritDoc} + * + * @since UNRELEASED + */ + public override void private_bus_up () + { + /* Set up service files for the python-dbusmock services. */ + this.create_dbusmock_service (BusType.SYSTEM, "org.bluez", "bluez5"); + this.create_dbusmock_service (BusType.SESSION, "org.bluez.obex", + "bluez5-obex"); + + base.private_bus_up (); + } + + /** + * Virtual method to create and set up the BlueZ backend. + * + * Called from set_up(); may be overridden to not create the backend, + * or to create it but not set it up. + * + * Subclasses may chain up, but are not required to so. + * + * @since UNRELEASED + */ + public virtual void create_backend () + { + this.bluez_backend = new BluezTest.Backend (); + ((!) this.bluez_backend).set_up (); + } + + /** + * Virtual method to configure ``FOLKS_PRIMARY_STORE`` to point to + * our //bluez_backend//. + * + * Subclasses may chain up, but are not required to so. + * + * @since UNRELEASED + */ + public virtual void configure_primary_store () + { + /* By default, configure BlueZ as the primary store. */ + assert (this.bluez_backend != null); + var config_val = + "bluez:" + ((!) this.bluez_backend).primary_device_address; + Environment.set_variable ("FOLKS_PRIMARY_STORE", config_val, true); + } + + /** + * {@inheritDoc} + * + * @since UNRELEASED + */ + public override void tear_down () + { + if (this.bluez_backend != null) + { + ((!) this.bluez_backend).tear_down (); + this.bluez_backend = null; + } + + Environment.unset_variable ("FOLKS_PRIMARY_STORE"); + + base.tear_down (); + } +} diff --git a/tests/lib/test-case.vala b/tests/lib/test-case.vala index 11673ba7..02d8c23f 100644 --- a/tests/lib/test-case.vala +++ b/tests/lib/test-case.vala @@ -79,6 +79,9 @@ public abstract class Folks.TestCase : Object if (Folks.BuildConf.HAVE_TRACKER) locations += Folks.BuildConf.ABS_TOP_BUILDDIR + "/backends/tracker/.libs/tracker.so"; + if (Folks.BuildConf.HAVE_BLUEZ) + locations += Folks.BuildConf.ABS_TOP_BUILDDIR + "/backends/bluez/.libs/bluez.so"; + Environment.set_variable ("FOLKS_BACKEND_PATH", string.joinv (":", locations), true); } -- cgit v1.2.3