diff options
author | Niels De Graef <nielsdegraef@gmail.com> | 2021-01-11 19:22:17 +0100 |
---|---|---|
committer | Niels De Graef <nielsdegraef@gmail.com> | 2022-08-06 12:45:49 +0200 |
commit | fcbc87c40406b322513c209844d3430bc4108b13 (patch) | |
tree | 74efc305e00d893a72b3c8fb22017435b6792111 /tests | |
parent | f37176d5e04b3dc7cc2ad8cb9843d7d7fc35485e (diff) | |
download | gnome-contacts-fcbc87c40406b322513c209844d3430bc4108b13.tar.gz |
Enable importing & exporting VCards
This commit adds the experimental functionality in Contacts to import
VCard (*.vcf) files.
Since importing a contact means we have to take in untrusted/unvalidated
input, let's give a high-level view of what happens:
* Contacts starts a native file chooser dialog so the user can choose
which file to import
* According to the chosen file, Contacts will launch a subprocess to do
the actual parsing using a `Contacts.Io.Parser`. At this point, we
only have a single subclass, which allows importing VCards.
* The helper process serializes the result to a `GLib.Variant`, and
sends it to the main process, which will receive the result and
parses it again.
* After the parsing operation is done, we can then start up a
`ImportOperation`, which will import the contacts using libfolks' API.
Exporting contacts is quite a bit easier, since we don't have to deal
with untrusted input: we serialize the list of selected contacts and
asynchronously write each to the given output stream. In the app, that's
a user chosen file; in tests, that can be a string.
Fixes: https://gitlab.gnome.org/GNOME/gnome-contacts/-/issues/1
Fixes: https://gitlab.gnome.org/GNOME/gnome-contacts/-/issues/38
Diffstat (limited to 'tests')
-rw-r--r-- | tests/io/internal/meson.build | 29 | ||||
-rw-r--r-- | tests/io/internal/test-serialise-birthday.vala | 54 | ||||
-rw-r--r-- | tests/io/internal/test-serialise-common.vala | 66 | ||||
-rw-r--r-- | tests/io/internal/test-serialise-emails.vala | 41 | ||||
-rw-r--r-- | tests/io/internal/test-serialise-full-name.vala | 42 | ||||
-rw-r--r-- | tests/io/internal/test-serialise-nickname.vala | 40 | ||||
-rw-r--r-- | tests/io/internal/test-serialise-structured-name.vala | 45 | ||||
-rw-r--r-- | tests/io/internal/test-serialise-urls.vala | 41 | ||||
-rw-r--r-- | tests/io/meson.build | 2 | ||||
-rw-r--r-- | tests/io/vcard/meson.build | 32 | ||||
-rw-r--r-- | tests/io/vcard/minimal.vcf | 4 | ||||
-rw-r--r-- | tests/io/vcard/test-vcard-minimal-import.vala | 61 | ||||
-rw-r--r-- | tests/meson.build | 6 |
13 files changed, 461 insertions, 2 deletions
diff --git a/tests/io/internal/meson.build b/tests/io/internal/meson.build new file mode 100644 index 0000000..82590ef --- /dev/null +++ b/tests/io/internal/meson.build @@ -0,0 +1,29 @@ +io_internal_testlib = library('io-internal-testlib', + files('test-serialise-common.vala'), + dependencies: libcontacts_dep, +) + +io_internal_testlib_dep = declare_dependency( + link_with: io_internal_testlib, + include_directories: include_directories('.'), +) + +io_internal_test_names = [ + 'serialise-full-name', + 'serialise-structured-name', + 'serialise-nickname', + 'serialise-birthday', + 'serialise-emails', + 'serialise-urls', +] + +foreach _test : io_internal_test_names + test_bin = executable(_test, + files('test-'+_test+'.vala'), + dependencies: [ libcontacts_dep, io_internal_testlib_dep ], + ) + + test(_test, test_bin, + suite: 'io-internal', + ) +endforeach diff --git a/tests/io/internal/test-serialise-birthday.vala b/tests/io/internal/test-serialise-birthday.vala new file mode 100644 index 0000000..46beef2 --- /dev/null +++ b/tests/io/internal/test-serialise-birthday.vala @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2021 Niels De Graef <nielsdegraef@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +using Folks; + +void main (string[] args) { + Test.init (ref args); + Test.add_func ("/io/serialize_birthday", + Contacts.Tests.Io.test_serialize_birthday); + Test.add_func ("/io/serialize_birthday_pre_epoch", + Contacts.Tests.Io.test_serialize_birthday_pre_epoch); + Test.run (); +} + +namespace Contacts.Tests.Io { + + private void test_serialize_birthday () { + unowned var bd_key = PersonaStore.detail_key (PersonaDetail.BIRTHDAY); + + DateTime old_bd = new GLib.DateTime.utc (1992, 8, 1, 0, 0, 0); + var old_bd_val = Value (typeof (DateTime)); + old_bd_val.set_boxed (old_bd); + + var new_bd_val = _transform_single_value (bd_key, old_bd_val); + assert_true (new_bd_val.type () == typeof (DateTime)); + assert_true (old_bd.equal ((DateTime) new_bd_val.get_boxed ())); + } + + private void test_serialize_birthday_pre_epoch () { + unowned var bd_key = PersonaStore.detail_key (PersonaDetail.BIRTHDAY); + + DateTime old_bd = new GLib.DateTime.utc (1961, 7, 3, 0, 0, 0); + var old_bd_val = Value (typeof (DateTime)); + old_bd_val.set_boxed (old_bd); + + var new_bd_val = _transform_single_value (bd_key, old_bd_val); + assert_true (new_bd_val.type () == typeof (DateTime)); + assert_true (old_bd.equal ((DateTime) new_bd_val.get_boxed ())); + } +} diff --git a/tests/io/internal/test-serialise-common.vala b/tests/io/internal/test-serialise-common.vala new file mode 100644 index 0000000..8407e2c --- /dev/null +++ b/tests/io/internal/test-serialise-common.vala @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2021 Niels De Graef <nielsdegraef@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +using Folks; + +namespace Contacts.Tests.Io { + + // Helper to serialize and deserialize an AbstractFieldDetails + public T _transform_single_afd<T> (string prop_key, T afd) { + Gee.Set<T> afd_set = new Gee.HashSet<T> (); + afd_set.add (afd); + + Value val = Value (typeof (Gee.Set)); + val.set_object (afd_set); + + Value emails_value = _transform_single_value (prop_key, val); + var emails_set = emails_value.get_object () as Gee.Set<T>; + if (emails_set == null) + error ("GValue has null value"); + if (emails_set.size != 1) + error ("Expected %d elements but got %d", 1, emails_set.size); + + var deserialized_fd = Utils.get_first<T> (emails_set); + assert_nonnull (deserialized_fd); + + return deserialized_fd; + } + + // Helper to serialize and deserialize a single property with a GLib.Value + public GLib.Value _transform_single_value (string prop_key, GLib.Value val) { + var details = new HashTable<string, Value?> (GLib.str_hash, GLib.str_equal); + details.insert (prop_key, val); + + // Serialize + Variant serialized = Contacts.Io.serialize_to_gvariant_single (details); + if (serialized == null) + error ("Couldn't serialize single-value table for property %s", prop_key); + + // Deserialize + var details_deserialized = Contacts.Io.deserialize_gvariant_single (serialized); + if (details_deserialized == null) + error ("Couldn't deserialize details for property %s", prop_key); + + if (!details_deserialized.contains (prop_key)) + error ("Deserialized details doesn't contain value for property %s", prop_key); + Value? val_deserialized = details_deserialized.lookup (prop_key); + if (val_deserialized.type() == GLib.Type.NONE) + error ("Deserialized Value is unset"); + + return val_deserialized; + } +} diff --git a/tests/io/internal/test-serialise-emails.vala b/tests/io/internal/test-serialise-emails.vala new file mode 100644 index 0000000..27b15ac --- /dev/null +++ b/tests/io/internal/test-serialise-emails.vala @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 Niels De Graef <nielsdegraef@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +using Folks; + +void main (string[] args) { + Test.init (ref args); + Test.add_func ("/io/serialize_emails", + Contacts.Tests.Io.test_serialize_emails); + Test.run (); +} + +namespace Contacts.Tests.Io { + + private void test_serialize_emails () { + unowned var emails_key = PersonaStore.detail_key (PersonaDetail.EMAIL_ADDRESSES); + + var old_fd = new EmailFieldDetails ("nielsdegraef@gmail.com"); + var new_fd = _transform_single_afd<EmailFieldDetails> (emails_key, old_fd); + + if (!(new_fd is EmailFieldDetails)) + error ("Expected EmailFieldDetails but got %s", new_fd.get_type ().name ()); + + if (old_fd.value != new_fd.value) + error ("Expected '%s' but got '%s'", old_fd.value, new_fd.value); + } +} diff --git a/tests/io/internal/test-serialise-full-name.vala b/tests/io/internal/test-serialise-full-name.vala new file mode 100644 index 0000000..9da8319 --- /dev/null +++ b/tests/io/internal/test-serialise-full-name.vala @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2021 Niels De Graef <nielsdegraef@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +using Folks; + +void main (string[] args) { + Test.init (ref args); + Test.add_func ("/io/serialize_full_name_simple", + Contacts.Tests.Io.test_serialize_full_name_simple); + Test.run (); +} + +namespace Contacts.Tests.Io { + + private void test_serialize_full_name_simple () { + unowned var fn_key = PersonaStore.detail_key (PersonaDetail.FULL_NAME); + + string old_fn = "Niels De Graef"; + Value old_fn_val = Value (typeof (string)); + old_fn_val.set_string (old_fn); + + var new_fn_val = _transform_single_value (fn_key, old_fn_val); + if (new_fn_val.type () != typeof (string)) + error ("Expected G_TYPE_STRING but got %s", new_fn_val.type ().name ()); + if (old_fn != new_fn_val.get_string ()) + error ("Expected '%s' but got '%s'", old_fn, new_fn_val.get_string ()); + } +} diff --git a/tests/io/internal/test-serialise-nickname.vala b/tests/io/internal/test-serialise-nickname.vala new file mode 100644 index 0000000..649b638 --- /dev/null +++ b/tests/io/internal/test-serialise-nickname.vala @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 Niels De Graef <nielsdegraef@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +using Folks; + +void main (string[] args) { + Test.init (ref args); + Test.add_func ("/io/serialize_nickame", + Contacts.Tests.Io.test_serialize_nickname); + Test.run (); +} + +namespace Contacts.Tests.Io { + + private void test_serialize_nickname () { + unowned var nick_key = PersonaStore.detail_key (PersonaDetail.NICKNAME); + + string old_nick = "nielsdg"; + var old_nick_val = Value (typeof (string)); + old_nick_val.set_string (old_nick); + + var new_nick_val = _transform_single_value (nick_key, old_nick_val); + assert_true (new_nick_val.type () == typeof (string)); + assert_true (old_nick == new_nick_val.get_string ()); + } +} diff --git a/tests/io/internal/test-serialise-structured-name.vala b/tests/io/internal/test-serialise-structured-name.vala new file mode 100644 index 0000000..45f2093 --- /dev/null +++ b/tests/io/internal/test-serialise-structured-name.vala @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2021 Niels De Graef <nielsdegraef@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +using Folks; + +void main (string[] args) { + Test.init (ref args); + Test.add_func ("/io/serialize_structured_name_simple", + Contacts.Tests.Io.test_serialize_structured_name_simple); + Test.run (); +} + +namespace Contacts.Tests.Io { + + private void test_serialize_structured_name_simple () { + unowned var sn_key = PersonaStore.detail_key (PersonaDetail.STRUCTURED_NAME); + + var old_sn = new StructuredName.simple ("Niels", "De Graef"); + Value old_sn_val = Value (typeof (StructuredName)); + old_sn_val.set_object (old_sn); + + var new_sn_val = _transform_single_value (sn_key, old_sn_val); + + if (new_sn_val.type () != typeof (StructuredName)) + error ("Expected FOLKS_TYPE_STRUCTURED_NAME but got %s", new_sn_val.type ().name ()); + + var new_sn = new_sn_val.get_object () as StructuredName; + if (!old_sn.equal (new_sn)) + error ("Expected '%s' but got '%s'", old_sn.to_string (), new_sn.to_string ()); + } +} diff --git a/tests/io/internal/test-serialise-urls.vala b/tests/io/internal/test-serialise-urls.vala new file mode 100644 index 0000000..cf4cdf9 --- /dev/null +++ b/tests/io/internal/test-serialise-urls.vala @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 Niels De Graef <nielsdegraef@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +using Folks; + +void main (string[] args) { + Test.init (ref args); + Test.add_func ("/io/serialize_urls_single", + Contacts.Tests.Io.test_serialize_urls_single); + Test.run (); +} + +namespace Contacts.Tests.Io { + + private void test_serialize_urls_single () { + unowned var urls_key = PersonaStore.detail_key (PersonaDetail.URLS); + + var old_fd = new UrlFieldDetails ("http://www.islinuxaboutchoice.com/"); + var new_fd = _transform_single_afd<UrlFieldDetails> (urls_key, old_fd); + + if (!(new_fd is UrlFieldDetails)) + error ("Expected UrlFieldDetails but got %s", new_fd.get_type ().name ()); + + if (old_fd.value != new_fd.value) + error ("Expected '%s' but got '%s'", old_fd.value, new_fd.value); + } +} diff --git a/tests/io/meson.build b/tests/io/meson.build new file mode 100644 index 0000000..2f34960 --- /dev/null +++ b/tests/io/meson.build @@ -0,0 +1,2 @@ +subdir('internal') +subdir('vcard') diff --git a/tests/io/vcard/meson.build b/tests/io/vcard/meson.build new file mode 100644 index 0000000..9967815 --- /dev/null +++ b/tests/io/vcard/meson.build @@ -0,0 +1,32 @@ +io_vcard_files = [ + 'minimal', +] + +test_deps = [ + gee, + folks, + libebook, +] + +foreach vcard_name : io_vcard_files + vcf_file = meson.current_source_dir() / vcard_name + '.vcf' + + # Ideally we'd do this using a preprocessor symbol or something + vcf_test_env = environment() + vcf_test_env.append('_VCF_FILE', vcf_file) + + test_sources = [ + contacts_io_sources, + 'test-vcard-'+vcard_name+'-import.vala', + ] + + test_bin = executable(vcard_name, + test_sources, + dependencies: test_deps, + ) + + test(vcard_name, test_bin, + suite: 'io-vcard', + env: vcf_test_env, + ) +endforeach diff --git a/tests/io/vcard/minimal.vcf b/tests/io/vcard/minimal.vcf new file mode 100644 index 0000000..b360c5a --- /dev/null +++ b/tests/io/vcard/minimal.vcf @@ -0,0 +1,4 @@ +BEGIN:VCARD +VERSION:3.0 +FN:Niels De Graef +END:VCARD diff --git a/tests/io/vcard/test-vcard-minimal-import.vala b/tests/io/vcard/test-vcard-minimal-import.vala new file mode 100644 index 0000000..bef6596 --- /dev/null +++ b/tests/io/vcard/test-vcard-minimal-import.vala @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2021 Niels De Graef <nielsdegraef@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +using Folks; + +void main (string[] args) { + Test.init (ref args); + Test.add_func ("/io/test_vcard_minimal", + Contacts.Tests.Io.test_vcard_minimal); + Test.run (); +} + +namespace Contacts.Tests.Io { + private void test_vcard_minimal () { + unowned var vcf_path = Environment.get_variable ("_VCF_FILE"); + if (vcf_path == null || vcf_path == "") + error ("No .vcf file set as envvar. Please use the meson test suite"); + + var file = GLib.File.new_for_path (vcf_path); + if (!file.query_exists ()) + error (".vcf file that is used as test input doesn't exist"); + + var parser = new Contacts.Io.VCardParser (); + HashTable<string, Value?>[] details_list = null; + try { + details_list = parser.parse (file.read (null)); + } catch (Error err) { + error ("Error while importing: %s", err.message); + } + if (details_list == null) + error ("VCardParser returned null"); + + if (details_list.length != 1) + error ("VCardParser parsed %u elements instead of 1", details_list.length); + + unowned var details = details_list[0]; + + unowned var fn_key = PersonaStore.detail_key (PersonaDetail.FULL_NAME); + if (!details.contains (fn_key)) + error ("No FN value"); + + var fn_value = details.lookup (fn_key); + unowned var fn = fn_value as string; + if (fn != "Niels De Graef") + error ("Expected '%s' but got '%s'", "Niels De Graef", fn); + } +} diff --git a/tests/meson.build b/tests/meson.build index 92c3586..6dcfcf1 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,14 +1,16 @@ +subdir('io') + test_names = [ 'basic-test', ] foreach _test : test_names test_bin = executable(_test, - '@0@.vala'.format(_test), + files('@0@.vala'.format(_test)), dependencies: libcontacts_dep, ) test(_test, test_bin, - suite: 'gnome-contacts', + suite: 'src', ) endforeach |