From 53c99c86cd495e82c3aaf02213a069e7adeccfa3 Mon Sep 17 00:00:00 2001 From: Andy Holmes Date: Mon, 21 Oct 2024 15:06:56 -0700 Subject: [PATCH] test(messages): revive messaging test suite --- REUSE.toml | 2 +- src/libvalent/contacts/valent-contacts.c | 35 +- .../data/sparql/get-message-attachments.rq | 14 +- .../messages/data/sparql/get-message.rq | 15 +- .../data/sparql/get-thread-attachments.rq | 18 +- .../data/sparql/get-thread-messages.rq | 1 + .../messages/data/sparql/get-thread.rq | 17 +- .../messages/data/sparql/get-timestamp.rq | 10 +- .../messages/data/sparql/remove-thread.rq | 10 +- .../messages/data/sparql/search-messages.rq | 2 +- .../messages/valent-message-thread.c | 47 ++- src/libvalent/messages/valent-message.c | 61 ---- src/libvalent/messages/valent-message.h | 3 - .../messages/valent-messages-adapter.c | 312 ++++++------------ .../messages/valent-messages-adapter.h | 13 +- src/libvalent/messages/valent-messages.c | 96 +++--- src/plugins/gnome/valent-messages-window.c | 41 +-- src/plugins/sms/valent-sms-device.c | 153 +++------ src/plugins/sms/valent-sms-plugin.c | 65 +--- tests/extra/lsan.supp | 7 +- tests/extra/tsan.supp | 6 + tests/fixtures/data/graph-messages.turtle | 55 +++ tests/fixtures/data/image.jpg | Bin 0 -> 1231 bytes tests/fixtures/data/plugin-sms.json | 75 ++++- tests/fixtures/libvalent-test.gresource.xml | 2 + tests/fixtures/meson.build | 1 + tests/fixtures/mock-plugin.c | 10 +- tests/fixtures/valent-mock-messages-adapter.c | 281 ++++++++++++++++ tests/fixtures/valent-mock-messages-adapter.h | 15 + tests/libvalent/meson.build | 1 + tests/libvalent/messages/meson.build | 36 ++ tests/libvalent/messages/test-message.c | 98 ++++++ .../messages/test-messages-component.c | 184 +++++++++++ tests/plugins/meson.build | 2 +- tests/plugins/sms/test-sms-common.h | 205 ------------ tests/plugins/sms/test-sms-plugin.c | 122 ++++++- 36 files changed, 1187 insertions(+), 828 deletions(-) create mode 100644 tests/fixtures/data/graph-messages.turtle create mode 100644 tests/fixtures/data/image.jpg create mode 100644 tests/fixtures/valent-mock-messages-adapter.c create mode 100644 tests/fixtures/valent-mock-messages-adapter.h create mode 100644 tests/libvalent/messages/meson.build create mode 100644 tests/libvalent/messages/test-message.c create mode 100644 tests/libvalent/messages/test-messages-component.c delete mode 100644 tests/plugins/sms/test-sms-common.h diff --git a/REUSE.toml b/REUSE.toml index a3b6ab2fd3..1dfc14ed2c 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -10,7 +10,7 @@ SPDX-FileCopyrightText = "Andy Holmes " SPDX-License-Identifier = "GPL-3.0-or-later" [[annotations]] -path = [".github/**", ".gitignore", ".gitmodules", "build-aux/**", "CHANGELOG.md", "CONTRIBUTING.md", "README.md", "**.ontology", "**.png", "**.svg", "**.vcf", "**.wrap"] +path = [".github/**", ".gitignore", ".gitmodules", "build-aux/**", "CHANGELOG.md", "CONTRIBUTING.md", "README.md", "**.jpg", "**.ontology", "**.png", "**.svg", "**.vcf", "**.wrap"] precedence = "aggregate" SPDX-FileCopyrightText = "No rights reserved" SPDX-License-Identifier = "CC0-1.0" diff --git a/src/libvalent/contacts/valent-contacts.c b/src/libvalent/contacts/valent-contacts.c index b4b9615bdb..098cfc76e5 100644 --- a/src/libvalent/contacts/valent-contacts.c +++ b/src/libvalent/contacts/valent-contacts.c @@ -31,10 +31,13 @@ struct _ValentContacts { ValentComponent parent_instance; - GPtrArray *adapters; + /* list model */ + GPtrArray *items; }; -static void g_list_model_iface_init (GListModelInterface *iface); +static void valent_contacts_unbind_extension (ValentComponent *component, + GObject *extension); +static void g_list_model_iface_init (GListModelInterface *iface); G_DEFINE_FINAL_TYPE_WITH_CODE (ValentContacts, valent_contacts, VALENT_TYPE_COMPONENT, G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, g_list_model_iface_init)) @@ -53,10 +56,10 @@ valent_contacts_get_item (GListModel *list, g_assert (VALENT_IS_CONTACTS (self)); - if G_UNLIKELY (position >= self->adapters->len) + if G_UNLIKELY (position >= self->items->len) return NULL; - return g_object_ref (g_ptr_array_index (self->adapters, position)); + return g_object_ref (g_ptr_array_index (self->items, position)); } static GType @@ -72,7 +75,7 @@ valent_contacts_get_n_items (GListModel *list) g_assert (VALENT_IS_CONTACTS (self)); - return self->adapters->len; + return self->items->len; } static void @@ -98,7 +101,7 @@ valent_contacts_bind_extension (ValentComponent *component, g_assert (VALENT_IS_CONTACTS (self)); g_assert (VALENT_IS_CONTACTS_ADAPTER (extension)); - if (g_ptr_array_find (self->adapters, extension, &position)) + if (g_ptr_array_find (self->items, extension, &position)) { g_warning ("Adapter \"%s\" already exported in \"%s\"", G_OBJECT_TYPE_NAME (extension), @@ -106,8 +109,14 @@ valent_contacts_bind_extension (ValentComponent *component, return; } - position = self->adapters->len; - g_ptr_array_add (self->adapters, g_object_ref (extension)); + g_signal_connect_object (extension, + "destroy", + G_CALLBACK (valent_contacts_unbind_extension), + self, + G_CONNECT_SWAPPED); + + position = self->items->len; + g_ptr_array_add (self->items, g_object_ref (extension)); g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1); VALENT_EXIT; @@ -118,6 +127,7 @@ valent_contacts_unbind_extension (ValentComponent *component, GObject *extension) { ValentContacts *self = VALENT_CONTACTS (component); + g_autoptr (ValentExtension) item = NULL; unsigned int position = 0; VALENT_ENTRY; @@ -125,7 +135,7 @@ valent_contacts_unbind_extension (ValentComponent *component, g_assert (VALENT_IS_CONTACTS (self)); g_assert (VALENT_IS_CONTACTS_ADAPTER (extension)); - if (!g_ptr_array_find (self->adapters, extension, &position)) + if (!g_ptr_array_find (self->items, extension, &position)) { g_warning ("Adapter \"%s\" not found in \"%s\"", G_OBJECT_TYPE_NAME (extension), @@ -133,7 +143,8 @@ valent_contacts_unbind_extension (ValentComponent *component, return; } - g_ptr_array_remove (self->adapters, extension); + g_signal_handlers_disconnect_by_func (extension, valent_contacts_unbind_extension, self); + item = g_ptr_array_steal_index (self->items, position); g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0); VALENT_EXIT; @@ -147,7 +158,7 @@ valent_contacts_finalize (GObject *object) { ValentContacts *self = VALENT_CONTACTS (object); - g_clear_pointer (&self->adapters, g_ptr_array_unref); + g_clear_pointer (&self->items, g_ptr_array_unref); G_OBJECT_CLASS (valent_contacts_parent_class)->finalize (object); } @@ -167,7 +178,7 @@ valent_contacts_class_init (ValentContactsClass *klass) static void valent_contacts_init (ValentContacts *self) { - self->adapters = g_ptr_array_new_with_free_func (g_object_unref); + self->items = g_ptr_array_new_with_free_func (g_object_unref); } /** diff --git a/src/libvalent/messages/data/sparql/get-message-attachments.rq b/src/libvalent/messages/data/sparql/get-message-attachments.rq index 4fbedd70aa..58240222b4 100644 --- a/src/libvalent/messages/data/sparql/get-message-attachments.rq +++ b/src/libvalent/messages/data/sparql/get-message-attachments.rq @@ -9,11 +9,13 @@ SELECT ?encodedThumbnail ?fileUri WHERE { - BIND(IRI(xsd:string(~iri)) AS ?message) - - ?message nmo:hasAttachment ?attachment . - OPTIONAL { ?attachment rdf:type nfo:Attachment } - OPTIONAL { ?attachment vmo:encoded_thumbnail ?encodedThumbnail } - OPTIONAL { ?attachment nie:url ?fileUri } + GRAPH { + BIND(IRI(xsd:string(~iri)) AS ?message) + ?message rdf:type vmo:PhoneMessage ; + nmo:hasAttachment ?attachment . + OPTIONAL { ?attachment rdf:type nfo:Attachment } + OPTIONAL { ?attachment vmo:encoded_thumbnail ?encodedThumbnail } + OPTIONAL { ?attachment nie:url ?fileUri } + } } diff --git a/src/libvalent/messages/data/sparql/get-message.rq b/src/libvalent/messages/data/sparql/get-message.rq index e8325f6777..cc93b8344a 100644 --- a/src/libvalent/messages/data/sparql/get-message.rq +++ b/src/libvalent/messages/data/sparql/get-message.rq @@ -21,14 +21,14 @@ SELECT WHERE { BIND(IRI(xsd:string(~iri)) AS ?message) ?message rdf:type vmo:PhoneMessage ; + vmo:phoneMessageBox ?phoneMessageBoxId ; dc:date ?date ; - vmo:phoneMessageBox ?phoneMessageBox ; vmo:phoneMessageId ?messageId ; nmo:isRead ?read ; - vmo:communicationChannel ?communicationChannel ; - vmo:subscriptionId ?subscriptionId . - ?communicationChannel vmo:communicationChannelId ?threadId . + vmo:subscriptionId ?subscriptionId ; + vmo:communicationChannel ?communicationChannel . ?phoneMessageBox vmo:phoneMessageBoxId ?box . + ?communicationChannel vmo:communicationChannelId ?threadId . OPTIONAL { ?message nmo:hasAttachment ?attachment . OPTIONAL { ?attachment rdf:type nfo:Attachment } @@ -36,15 +36,14 @@ WHERE { OPTIONAL { ?attachment nie:url ?fileUri } } OPTIONAL { - ?message nmo:messageSender ?contactMedium . - OPTIONAL { ?contactMedium (nco:phoneNumber|nco:emailAddress) ?sender } + ?message nmo:primaryMessageRecipient/(nco:phoneNumber|nco:emailAddress) ?recipient } OPTIONAL { - ?message nmo:primaryMessageRecipient ?contactMedium . - OPTIONAL { ?contactMedium (nco:phoneNumber|nco:emailAddress) ?recipient } + ?message nmo:messageSender/(nco:phoneNumber|nco:emailAddress) ?sender } OPTIONAL { ?message nmo:plainTextMessageContent ?text } } GROUP BY ?message ?attachment + diff --git a/src/libvalent/messages/data/sparql/get-thread-attachments.rq b/src/libvalent/messages/data/sparql/get-thread-attachments.rq index 3785d43a25..2eacf65e40 100644 --- a/src/libvalent/messages/data/sparql/get-thread-attachments.rq +++ b/src/libvalent/messages/data/sparql/get-thread-attachments.rq @@ -9,12 +9,14 @@ SELECT ?encodedThumbnail ?fileUri WHERE { - BIND(IRI(xsd:string(~iri)) AS ?thread) - - ?message vmo:communicationChannel ?thread ; - nmo:hasAttachment ?attachment . - - OPTIONAL { ?attachment rdf:type nfo:Attachment } - OPTIONAL { ?attachment vmo:encoded_thumbnail ?encodedThumbnail } - OPTIONAL { ?attachment nie:url ?fileUri } + GRAPH { + BIND(IRI(xsd:string(~iri)) AS ?communicationChannel) + ?message rdf:type vmo:PhoneMessage ; + vmo:communicationChannel ?communicationChannel ; + nmo:hasAttachment ?attachment . + OPTIONAL { ?attachment rdf:type nfo:Attachment } + OPTIONAL { ?attachment vmo:encoded_thumbnail ?encodedThumbnail } + OPTIONAL { ?attachment nie:url ?fileUri } + } } + diff --git a/src/libvalent/messages/data/sparql/get-thread-messages.rq b/src/libvalent/messages/data/sparql/get-thread-messages.rq index d81f3a4702..e3076bcc67 100644 --- a/src/libvalent/messages/data/sparql/get-thread-messages.rq +++ b/src/libvalent/messages/data/sparql/get-thread-messages.rq @@ -46,3 +46,4 @@ WHERE { } GROUP BY ?message ?attachment ORDER BY ASC(?date) + diff --git a/src/libvalent/messages/data/sparql/get-thread.rq b/src/libvalent/messages/data/sparql/get-thread.rq index 98397307c8..2ca7466a17 100644 --- a/src/libvalent/messages/data/sparql/get-thread.rq +++ b/src/libvalent/messages/data/sparql/get-thread.rq @@ -3,7 +3,7 @@ # Inputs: iri # Outputs: message, box, date, messageId, read, recipients, sender, subscriptionId, text, threadId -# communicationChannel, +# communicationChannel, participants SELECT ?message ?box @@ -27,26 +27,25 @@ WHERE { } GROUP BY ?communicationChannel } + FILTER(?date = ?latestDate) ?message rdf:type vmo:PhoneMessage ; + vmo:phoneMessageBox/vmo:phoneMessageBoxId ?box ; dc:date ?date ; - vmo:phoneMessageBox ?phoneMessageBox ; vmo:phoneMessageId ?messageId ; nmo:isRead ?read ; - vmo:communicationChannel ?communicationChannel ; - vmo:subscriptionId ?subscriptionId . + vmo:subscriptionId ?subscriptionId ; + vmo:communicationChannel ?communicationChannel . ?communicationChannel rdf:type vmo:CommunicationChannel ; vmo:communicationChannelId ?threadId ; vmo:hasParticipant ?participant . - ?phoneMessageBox vmo:phoneMessageBoxId ?box . OPTIONAL { - ?message nmo:messageSender ?contactMedium . - OPTIONAL { ?contactMedium (nco:phoneNumber|nco:emailAddress) ?sender } + ?message nmo:primaryMessageRecipient/(nco:phoneNumber|nco:emailAddress) ?recipient } OPTIONAL { - ?message nmo:primaryMessageRecipient ?contactMedium . - OPTIONAL { ?contactMedium (nco:phoneNumber|nco:emailAddress) ?recipient } + ?message nmo:messageSender/(nco:phoneNumber|nco:emailAddress) ?sender } OPTIONAL { ?message nmo:plainTextMessageContent ?text } } + diff --git a/src/libvalent/messages/data/sparql/get-timestamp.rq b/src/libvalent/messages/data/sparql/get-timestamp.rq index fa16906340..882f161270 100644 --- a/src/libvalent/messages/data/sparql/get-timestamp.rq +++ b/src/libvalent/messages/data/sparql/get-timestamp.rq @@ -5,9 +5,11 @@ # Outputs: date SELECT (MAX(?date) AS ?latestDate) WHERE { - ?message rdf:type vmo:PhoneMessage ; - vmo:communicationChannel/vmo:communicationChannelId ?threadId ; - dc:date ?date . - FILTER(?threadId = ~threadId^^xsd:integer) + GRAPH { + ?message rdf:type vmo:PhoneMessage ; + vmo:communicationChannel/vmo:communicationChannelId ?threadId ; + dc:date ?date . + FILTER(?threadId = ~threadId^^xsd:integer) + } } diff --git a/src/libvalent/messages/data/sparql/remove-thread.rq b/src/libvalent/messages/data/sparql/remove-thread.rq index d0a0e1569c..2515fdc687 100644 --- a/src/libvalent/messages/data/sparql/remove-thread.rq +++ b/src/libvalent/messages/data/sparql/remove-thread.rq @@ -5,13 +5,13 @@ # Outputs: None DELETE WHERE { - BIND(IRI(xsd:string(~iri)) AS ?channel) - ?channel rdf:type vmo:CommunicationChannel ; - ?channelProperty ?channelValue . + BIND(IRI(xsd:string(~iri)) AS ?communicationChannel) + ?communicationChannel rdf:type vmo:CommunicationChannel ; + ?channelProperty ?channelValue . + OPTIONAL { ?channel ?channelProperty ?channelValue . } ?message rdf:type vmo:PhoneMessage ; - vmo:communicationChannel ?channel ; + vmo:communicationChannel ?communicationChannel ; ?messageProperty ?messageValue . OPTIONAL { ?message ?messageProperty ?messageValue . } - OPTIONAL { ?channel ?channelProperty ?channelValue . } } diff --git a/src/libvalent/messages/data/sparql/search-messages.rq b/src/libvalent/messages/data/sparql/search-messages.rq index 7d82f6c816..b17fcb4443 100644 --- a/src/libvalent/messages/data/sparql/search-messages.rq +++ b/src/libvalent/messages/data/sparql/search-messages.rq @@ -54,7 +54,7 @@ WHERE { ?message nmo:plainTextMessageContent ?text } } -GROUP BY ?message +GROUP BY ?message ?attachment ORDER BY DESC(fts:rank(?message)) LIMIT 5 diff --git a/src/libvalent/messages/valent-message-thread.c b/src/libvalent/messages/valent-message-thread.c index da41bac864..5ea6a90ab6 100644 --- a/src/libvalent/messages/valent-message-thread.c +++ b/src/libvalent/messages/valent-message-thread.c @@ -183,15 +183,10 @@ on_notifier_event (TrackerNotifier *notifier, VALENT_NOTE ("CREATE: %s", urn); // HACK: if the thread hasn't been loaded, assume newer messages sort // last and pick one to update the last-message. - if (self->cancellable == NULL) - { - if (latest_urn == NULL || g_utf8_collate (latest_urn, urn) < 0) - latest_urn = urn; - } - else - { - valent_message_thread_load_message (self, urn); - } + if (self->cancellable != NULL) + valent_message_thread_load_message (self, urn); + else if (latest_urn == NULL || g_utf8_collate (latest_urn, urn) < 0) + latest_urn = urn; break; case TRACKER_NOTIFIER_EVENT_DELETE: @@ -323,6 +318,7 @@ valent_message_thread_load_cb (GObject *object, ValentMessageThread *self = VALENT_MESSAGE_THREAD (object); g_autoptr (GPtrArray) messages = NULL; g_autoptr (GError) error = NULL; + unsigned int position = 0; messages = g_task_propagate_pointer (G_TASK (result), &error); if (messages == NULL) @@ -333,10 +329,11 @@ valent_message_thread_load_cb (GObject *object, return; } + position = g_sequence_get_length (self->items); for (unsigned int i = 0; i < messages->len; i++) g_sequence_append (self->items, g_object_ref (g_ptr_array_index (messages, i))); - g_list_model_items_changed (G_LIST_MODEL (self), 0, 0, messages->len); + g_list_model_items_changed (G_LIST_MODEL (self), position, 0, messages->len); } static void @@ -514,7 +511,7 @@ valent_message_thread_constructed (GObject *object) g_object_get (VALENT_OBJECT (self), "iri", &self->iri, NULL); if (self->connection != NULL) { - g_autoptr (GString) iri_string = NULL; + g_autofree char *iri_pattern = NULL; self->notifier = tracker_sparql_connection_create_notifier (self->connection); g_signal_connect_object (self->notifier, @@ -523,14 +520,8 @@ valent_message_thread_constructed (GObject *object) self, G_CONNECT_DEFAULT); - /* FIXME: this relies on IRIs use the pattern `//`, - * which may not be universally applicable - */ - iri_string = g_string_new (self->iri); - g_string_replace (iri_string, "/", "\\/", 0); - g_string_prepend_c (iri_string, '^'); - g_string_append (iri_string, "\\/([^\\/]+)$"); - self->iri_pattern = g_regex_new (iri_string->str, + iri_pattern = g_strdup_printf ("^%s:([^:]+)$", self->iri); + self->iri_pattern = g_regex_new (iri_pattern, G_REGEX_OPTIMIZE, G_REGEX_MATCH_DEFAULT, NULL); @@ -542,7 +533,17 @@ valent_message_thread_destroy (ValentObject *object) { ValentMessageThread *self = VALENT_MESSAGE_THREAD (object); - g_signal_handlers_disconnect_by_func (self->notifier, on_notifier_event, self); + g_clear_object (&self->get_message_stmt); + g_clear_object (&self->get_thread_messages_stmt); + g_clear_pointer (&self->iri_pattern, g_regex_unref); + g_clear_pointer (&self->iri, g_free); + + if (self->notifier != NULL) + { + g_signal_handlers_disconnect_by_func (self->notifier, on_notifier_event, self); + g_clear_object (&self->notifier); + } + g_clear_object (&self->connection); VALENT_OBJECT_CLASS (valent_message_thread_parent_class)->destroy (object); } @@ -552,14 +553,8 @@ valent_message_thread_finalize (GObject *object) { ValentMessageThread *self = VALENT_MESSAGE_THREAD (object); - g_clear_object (&self->connection); g_clear_object (&self->latest_message); g_clear_pointer (&self->participants, g_strfreev); - g_clear_pointer (&self->iri, g_free); - g_clear_object (&self->notifier); - g_clear_object (&self->get_message_stmt); - g_clear_object (&self->get_thread_messages_stmt); - g_clear_pointer (&self->iri_pattern, g_regex_unref); g_clear_object (&self->cancellable); g_clear_pointer (&self->items, g_sequence_free); diff --git a/src/libvalent/messages/valent-message.c b/src/libvalent/messages/valent-message.c index cf90fd8545..b055d45c62 100644 --- a/src/libvalent/messages/valent-message.c +++ b/src/libvalent/messages/valent-message.c @@ -535,64 +535,3 @@ valent_message_get_thread_id (ValentMessage *message) return message->thread_id; } -/** - * valent_message_update: - * @message: a `ValentMessage` - * @update: (transfer full): a `ValentMessage` - * - * Update @message with data from @update. The `ValentMessage`:id property - * must match on both objects. - * - * This function consumes @update and all its memory, so it should not be used - * after calling this. - * - * Since: 1.0 - */ -void -valent_message_update (ValentMessage *message, - ValentMessage *update) -{ - g_return_if_fail (VALENT_IS_MESSAGE (message)); - g_return_if_fail (VALENT_IS_MESSAGE (update)); - g_return_if_fail (message->id == update->id); - - g_object_freeze_notify (G_OBJECT (message)); - - if (message->box != update->box) - { - message->box = update->box; - g_object_notify_by_pspec (G_OBJECT (message), properties [PROP_BOX]); - } - - if (message->date != update->date) - { - message->date = update->date; - g_object_notify_by_pspec (G_OBJECT (message), properties [PROP_DATE]); - } - - if (message->read != update->read) - { - message->read = update->read; - g_object_notify_by_pspec (G_OBJECT (message), properties [PROP_READ]); - } - - if (message->recipients != update->recipients) - { - g_clear_pointer (&message->recipients, g_strfreev); - message->recipients = g_steal_pointer (&update->recipients); - g_object_notify_by_pspec (G_OBJECT (message), properties [PROP_RECIPIENTS]); - } - - if (g_set_str (&message->sender, update->sender)) - g_object_notify_by_pspec (G_OBJECT (message), properties [PROP_SENDER]); - - if (g_set_str (&message->text, update->text)) - g_object_notify_by_pspec (G_OBJECT (message), properties [PROP_TEXT]); - - if (g_set_object (&message->attachments, update->attachments)) - g_object_notify_by_pspec (G_OBJECT (message), properties [PROP_ATTACHMENTS]); - - g_object_thaw_notify (G_OBJECT (message)); - g_object_unref (update); -} - diff --git a/src/libvalent/messages/valent-message.h b/src/libvalent/messages/valent-message.h index 361a7f1773..1a9178e7c5 100644 --- a/src/libvalent/messages/valent-message.h +++ b/src/libvalent/messages/valent-message.h @@ -69,8 +69,5 @@ VALENT_AVAILABLE_IN_1_0 const char * valent_message_get_text (ValentMessage *message); VALENT_AVAILABLE_IN_1_0 int64_t valent_message_get_thread_id (ValentMessage *message); -VALENT_AVAILABLE_IN_1_0 -void valent_message_update (ValentMessage *message, - ValentMessage *update); G_END_DECLS diff --git a/src/libvalent/messages/valent-messages-adapter.c b/src/libvalent/messages/valent-messages-adapter.c index d9cd3a2da5..1ccca9cbba 100644 --- a/src/libvalent/messages/valent-messages-adapter.c +++ b/src/libvalent/messages/valent-messages-adapter.c @@ -53,17 +53,14 @@ typedef struct GRegex *iri_pattern; GCancellable *cancellable; - /* list */ - GSequence *items; - unsigned int last_position; - GSequenceIter *last_iter; - gboolean last_position_valid; + /* list model */ + GPtrArray *items; } ValentMessagesAdapterPrivate; static void g_list_model_iface_init (GListModelInterface *iface); -static void valent_messages_adapter_load_thread (ValentMessagesAdapter *self, - const char *iri); +static void valent_messages_adapter_load_thread (ValentMessagesAdapter *self, + const char *iri); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ValentMessagesAdapter, valent_messages_adapter, VALENT_TYPE_EXTENSION, G_ADD_PRIVATE (ValentMessagesAdapter) @@ -76,24 +73,6 @@ typedef enum static GParamSpec *properties[PROP_CONNECTION + 1] = { 0, }; -static inline int -valent_messages_adapter_sort_func (gconstpointer a, - gconstpointer b, - gpointer user_data) -{ - g_autoptr (ValentMessage) message1 = NULL; - g_autoptr (ValentMessage) message2 = NULL; - int64_t date1 = 0; - int64_t date2 = 0; - - g_object_get ((GObject *)a, "latest-message", &message1, NULL); - date1 = valent_message_get_date (message1); - g_object_get ((GObject *)b, "latest-message", &message2, NULL); - date2 = valent_message_get_date (message2); - - return (date1 > date2) ? -1 : (date1 < date2); -} - static void valent_messages_adapter_load_thread_cb (GObject *object, GAsyncResult *result, @@ -101,13 +80,12 @@ valent_messages_adapter_load_thread_cb (GObject *object, { ValentMessagesAdapter *self = VALENT_MESSAGES_ADAPTER (object); ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self); - g_autoptr (ValentMessageThread) thread = NULL; - GSequenceIter *it; + g_autoptr (GListModel) list = NULL; unsigned int position; g_autoptr (GError) error = NULL; - thread = g_task_propagate_pointer (G_TASK (result), &error); - if (thread == NULL) + list = g_task_propagate_pointer (G_TASK (result), &error); + if (list == NULL) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { @@ -118,22 +96,18 @@ valent_messages_adapter_load_thread_cb (GObject *object, return; } - it = g_sequence_insert_sorted (priv->items, - g_object_ref (thread), - valent_messages_adapter_sort_func, - NULL); - position = g_sequence_iter_get_position (it); + position = priv->items->len; + g_ptr_array_add (priv->items, g_steal_pointer (&list)); g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1); } -static inline int -valent_messages_adapter_lookup_func (gconstpointer a, - gconstpointer b, - gpointer user_data) +static inline gboolean +valent_messages_adapter_equal_func (gconstpointer a, + gconstpointer b) { g_autofree char *iri = valent_object_dup_iri ((ValentObject *)a); - return g_utf8_collate (iri, (const char *)b); + return g_utf8_collate (iri, (const char *)b) == 0; } static void @@ -141,25 +115,27 @@ valent_messages_adapter_remove_thread (ValentMessagesAdapter *self, const char *iri) { ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self); - GSequenceIter *it; - unsigned int position; + g_autoptr (GListModel) item = NULL; + unsigned int position = 0; g_assert (VALENT_IS_MESSAGES_ADAPTER (self)); - it = g_sequence_lookup (priv->items, - (char *)iri, - valent_messages_adapter_lookup_func, - NULL); - - if (it != NULL) + if (!g_ptr_array_find_with_equal_func (priv->items, + iri, + valent_messages_adapter_equal_func, + &position)) { - position = g_sequence_iter_get_position (it); - g_sequence_remove (it); - g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0); + g_warning ("Resource \"%s\" not found in \"%s\"", + iri, + G_OBJECT_TYPE_NAME (self)); + return; } + + item = g_ptr_array_steal_index (priv->items, position); + g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0); } -gboolean +static inline gboolean valent_messages_adapter_event_is_thread (ValentMessagesAdapter *self, const char *iri) { @@ -219,7 +195,8 @@ valent_messages_adapter_open (ValentMessagesAdapter *self, ValentContext *context = NULL; g_autoptr (GFile) file = NULL; g_autoptr (GFile) ontology = NULL; - g_autoptr (GString) iri_string = NULL; + g_autofree char *iri = NULL; + g_autofree char *iri_pattern = NULL; context = valent_extension_get_context (VALENT_EXTENSION (self)); file = valent_context_get_cache_file (context, "metadata"); @@ -235,14 +212,9 @@ valent_messages_adapter_open (ValentMessagesAdapter *self, if (priv->connection == NULL) return FALSE; - /* FIXME: this relies on IRIs use the pattern `//`, - * which may not be universally applicable - */ - iri_string = g_string_new (valent_context_get_path (context)); - g_string_replace (iri_string, "/", "\\/", 0); - g_string_prepend (iri_string, "^valent:\\/\\/"); - g_string_append (iri_string, "\\/([^\\/]+)$"); - priv->iri_pattern = g_regex_new (iri_string->str, + iri = valent_object_dup_iri (VALENT_OBJECT (self)); + iri_pattern = g_strdup_printf ("^%s:([^:]+)$", iri); + priv->iri_pattern = g_regex_new (iri_pattern, G_REGEX_OPTIMIZE, G_REGEX_MATCH_DEFAULT, NULL); @@ -266,36 +238,18 @@ valent_messages_adapter_get_item (GListModel *list, { ValentMessagesAdapter *self = VALENT_MESSAGES_ADAPTER (list); ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self); - GSequenceIter *it = NULL; g_autofree char *iri = NULL; g_autoptr (ValentMessage) latest_message = NULL; g_auto (GStrv) participants = NULL; g_assert (VALENT_IS_MESSAGES_ADAPTER (self)); - if (priv->last_position_valid) - { - if (position < G_MAXUINT && priv->last_position == position + 1) - it = g_sequence_iter_prev (priv->last_iter); - else if (position > 0 && priv->last_position == position - 1) - it = g_sequence_iter_next (priv->last_iter); - else if (priv->last_position == position) - it = priv->last_iter; - } - - if (it == NULL) - it = g_sequence_get_iter_at_pos (priv->items, position); - - priv->last_iter = it; - priv->last_position = position; - priv->last_position_valid = TRUE; - - if (g_sequence_iter_is_end (it)) + if G_UNLIKELY (position >= priv->items->len) return NULL; - // HACK: return a duplicate thread to avoid accruing memory - // return g_object_ref (g_sequence_get (it)); - g_object_get (g_sequence_get (it), + // FIXME: a duplicate thread is returned to avoid accruing memory + // return g_object_ref (g_ptr_array_index (priv->items, position)); + g_object_get (g_ptr_array_index (priv->items, position), "iri", &iri, "latest-message", &latest_message, "participants", &participants, @@ -312,7 +266,7 @@ valent_messages_adapter_get_item (GListModel *list, static GType valent_messages_adapter_get_item_type (GListModel *list) { - return VALENT_TYPE_MESSAGE_THREAD; + return G_TYPE_LIST_MODEL; } static unsigned int @@ -323,7 +277,7 @@ valent_messages_adapter_get_n_items (GListModel *list) g_assert (VALENT_IS_MESSAGES_ADAPTER (self)); - return g_sequence_get_length (priv->items); + return priv->items->len; } static void @@ -457,66 +411,73 @@ valent_message_from_sparql_cursor (TrackerSparqlCursor *cursor, static ValentMessageThread * valent_message_thread_from_sparql_cursor (TrackerSparqlCursor *cursor) { - ValentMessage *message = NULL; + g_autoptr (ValentMessage) message = NULL; const char *iri = NULL; const char *participants = NULL; g_auto (GStrv) participantv = NULL; - g_autoptr (GListStore) attachments = NULL; - ValentMessageBox box = VALENT_MESSAGE_BOX_ALL; - int64_t date = 0; - g_autoptr (GDateTime) datetime = NULL; - int64_t message_id; - gboolean read = FALSE; - const char *recipients = NULL; - g_auto (GStrv) recipientv = NULL; - const char *sender = NULL; - int64_t subscription_id = -1; - const char *text = NULL; - int64_t thread_id = -1; g_assert (TRACKER_IS_SPARQL_CURSOR (cursor)); - attachments = g_list_store_new (VALENT_TYPE_MESSAGE_ATTACHMENT); - box = tracker_sparql_cursor_get_integer (cursor, CURSOR_MESSAGE_BOX); + /* NOTE: typically there won't be a thread without a message, but this may be + * the case as an implementation detail. + */ + iri = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_IRI, NULL); + if (iri != NULL) + { + g_autoptr (GListStore) attachments = NULL; + ValentMessageBox box = VALENT_MESSAGE_BOX_ALL; + int64_t date = 0; + g_autoptr (GDateTime) datetime = NULL; + int64_t message_id; + gboolean read = FALSE; + const char *recipients = NULL; + g_auto (GStrv) recipientv = NULL; + const char *sender = NULL; + int64_t subscription_id = -1; + const char *text = NULL; + int64_t thread_id = -1; - datetime = tracker_sparql_cursor_get_datetime (cursor, CURSOR_MESSAGE_DATE); - if (datetime != NULL) - date = g_date_time_to_unix_usec (datetime) / 1000; + attachments = g_list_store_new (VALENT_TYPE_MESSAGE_ATTACHMENT); + box = tracker_sparql_cursor_get_integer (cursor, CURSOR_MESSAGE_BOX); - message_id = tracker_sparql_cursor_get_integer (cursor, CURSOR_MESSAGE_ID); - read = tracker_sparql_cursor_get_boolean (cursor, CURSOR_MESSAGE_READ); + datetime = tracker_sparql_cursor_get_datetime (cursor, CURSOR_MESSAGE_DATE); + if (datetime != NULL) + date = g_date_time_to_unix_usec (datetime) / 1000; - recipients = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_RECIPIENTS, NULL); - if (recipients != NULL) - recipientv = g_strsplit (recipients, ",", -1); + message_id = tracker_sparql_cursor_get_integer (cursor, CURSOR_MESSAGE_ID); + read = tracker_sparql_cursor_get_boolean (cursor, CURSOR_MESSAGE_READ); - if (tracker_sparql_cursor_is_bound (cursor, CURSOR_MESSAGE_SENDER)) - sender = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_SENDER, NULL); + recipients = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_RECIPIENTS, NULL); + if (recipients != NULL) + recipientv = g_strsplit (recipients, ",", -1); - if (tracker_sparql_cursor_is_bound (cursor, CURSOR_MESSAGE_SUBSCRIPTION_ID)) - subscription_id = tracker_sparql_cursor_get_integer (cursor, CURSOR_MESSAGE_SUBSCRIPTION_ID); + if (tracker_sparql_cursor_is_bound (cursor, CURSOR_MESSAGE_SENDER)) + sender = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_SENDER, NULL); - if (tracker_sparql_cursor_is_bound (cursor, CURSOR_MESSAGE_TEXT)) - text = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_TEXT, NULL); + if (tracker_sparql_cursor_is_bound (cursor, CURSOR_MESSAGE_SUBSCRIPTION_ID)) + subscription_id = tracker_sparql_cursor_get_integer (cursor, CURSOR_MESSAGE_SUBSCRIPTION_ID); - thread_id = tracker_sparql_cursor_get_integer (cursor, CURSOR_MESSAGE_THREAD_ID); + if (tracker_sparql_cursor_is_bound (cursor, CURSOR_MESSAGE_TEXT)) + text = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_TEXT, NULL); - iri = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_IRI, NULL); - message = g_object_new (VALENT_TYPE_MESSAGE, - "iri", iri, - "box", box, - "date", date, - "id", message_id, - "read", read, - "recipients", recipientv, - "sender", sender, - "subscription-id", subscription_id, - "text", text, - "thread-id", thread_id, - "attachments", attachments, - NULL); + thread_id = tracker_sparql_cursor_get_integer (cursor, CURSOR_MESSAGE_THREAD_ID); - /* Attachment + message = g_object_new (VALENT_TYPE_MESSAGE, + "iri", iri, + "box", box, + "date", date, + "id", message_id, + "read", read, + "recipients", recipientv, + "sender", sender, + "subscription-id", subscription_id, + "text", text, + "thread-id", thread_id, + "attachments", attachments, + NULL); + } + + /* Thread */ if (tracker_sparql_cursor_is_bound (cursor, CURSOR_MESSAGE_THREAD_IRI)) iri = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_THREAD_IRI, NULL); @@ -549,19 +510,15 @@ cursor_get_threads_cb (TrackerSparqlCursor *cursor, thread = valent_message_thread_from_sparql_cursor (cursor); if (thread != NULL) { - GSequenceIter *it; unsigned int position; - it = g_sequence_insert_sorted (priv->items, - g_steal_pointer (&thread), - valent_messages_adapter_sort_func, - NULL); - position = g_sequence_iter_get_position (it); + position = priv->items->len; + g_ptr_array_add (priv->items, g_steal_pointer (&thread)); g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1); } tracker_sparql_cursor_next_async (cursor, - g_task_get_cancellable (G_TASK (result)), + priv->cancellable, (GAsyncReadyCallback) cursor_get_threads_cb, g_object_ref (self)); } @@ -761,22 +718,6 @@ valent_messages_adapter_real_send_message_finish (ValentMessagesAdapter *adapte return g_task_propagate_boolean (G_TASK (result), error); } - -static void -valent_messages_adapter_real_export_adapter (ValentMessagesAdapter *adapter, - ValentMessagesAdapter *object) -{ - g_assert (VALENT_MESSAGES_ADAPTER (adapter)); - g_assert (VALENT_MESSAGES_ADAPTER (object)); -} - -static void -valent_messages_adapter_real_unexport_adapter (ValentMessagesAdapter *adapter, - ValentMessagesAdapter *object) -{ - g_assert (VALENT_MESSAGES_ADAPTER (adapter)); - g_assert (VALENT_MESSAGES_ADAPTER (object)); -} /* LCOV_EXCL_STOP */ /* @@ -788,11 +729,15 @@ valent_messages_adapter_destroy (ValentObject *object) ValentMessagesAdapter *self = VALENT_MESSAGES_ADAPTER (object); ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self); - g_clear_object (&priv->notifier); g_clear_object (&priv->get_thread_stmt); g_clear_object (&priv->get_threads_stmt); g_clear_pointer (&priv->iri_pattern, g_regex_unref); - g_clear_object (&priv->cancellable); + + if (priv->notifier != NULL) + { + g_signal_handlers_disconnect_by_func (priv->notifier, on_notifier_event, self); + g_clear_object (&priv->notifier); + } if (priv->connection != NULL) { @@ -831,7 +776,7 @@ valent_messages_adapter_finalize (GObject *object) ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self); g_clear_object (&priv->cancellable); - g_clear_pointer (&priv->items, g_sequence_free); + g_clear_pointer (&priv->items, g_ptr_array_unref); G_OBJECT_CLASS (valent_messages_adapter_parent_class)->finalize (object); } @@ -892,8 +837,6 @@ valent_messages_adapter_class_init (ValentMessagesAdapterClass *klass) klass->send_message = valent_messages_adapter_real_send_message; klass->send_message_finish = valent_messages_adapter_real_send_message_finish; - klass->export_adapter = valent_messages_adapter_real_export_adapter; - klass->unexport_adapter = valent_messages_adapter_real_unexport_adapter; /** * ValentMessagesAdapter:connection: @@ -916,7 +859,7 @@ valent_messages_adapter_init (ValentMessagesAdapter *self) { ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self); - priv->items = g_sequence_new (g_object_unref); + priv->items = g_ptr_array_new_with_free_func (g_object_unref); } /** @@ -987,56 +930,3 @@ valent_messages_adapter_send_message_finish (ValentMessagesAdapter *adapter, VALENT_RETURN (ret); } -/** - * valent_messages_adapter_export_adapter: (virtual export_adapter) - * @adapter: an `ValentMessagesAdapter` - * @object: a `ValentMessagesAdapter` - * - * Export @object on @adapter. - * - * This method is intended to allow device plugins to expose remote message - * threads to the host system. - * - * Implementations must automatically unexport any threads when destroyed. - * - * Since: 1.0 - */ -void -valent_messages_adapter_export_adapter (ValentMessagesAdapter *adapter, - ValentMessagesAdapter *object) -{ - VALENT_ENTRY; - - g_return_if_fail (VALENT_IS_MESSAGES_ADAPTER (adapter)); - g_return_if_fail (VALENT_IS_MESSAGES_ADAPTER (object)); - - VALENT_MESSAGES_ADAPTER_GET_CLASS (adapter)->export_adapter (adapter, - object); - - VALENT_EXIT; -} - -/** - * valent_messages_adapter_unexport_adapter: (virtual unexport_adapter) - * @adapter: an `ValentMessagesAdapter` - * @object: a `ValentMessagesAdapter` - * - * Unexport @object from @adapter. - * - * Since: 1.0 - */ -void -valent_messages_adapter_unexport_adapter (ValentMessagesAdapter *adapter, - ValentMessagesAdapter *object) -{ - VALENT_ENTRY; - - g_return_if_fail (VALENT_IS_MESSAGES_ADAPTER (adapter)); - g_return_if_fail (VALENT_IS_MESSAGES_ADAPTER (object)); - - VALENT_MESSAGES_ADAPTER_GET_CLASS (adapter)->unexport_adapter (adapter, - object); - - VALENT_EXIT; -} - diff --git a/src/libvalent/messages/valent-messages-adapter.h b/src/libvalent/messages/valent-messages-adapter.h index 9e6da3976e..de08598c5e 100644 --- a/src/libvalent/messages/valent-messages-adapter.h +++ b/src/libvalent/messages/valent-messages-adapter.h @@ -32,6 +32,7 @@ struct _ValentMessagesAdapterClass { ValentExtensionClass parent_class; + /* virtual functions */ void (*send_message) (ValentMessagesAdapter *adapter, ValentMessage *message, GCancellable *cancellable, @@ -41,12 +42,6 @@ struct _ValentMessagesAdapterClass GAsyncResult *result, GError **error); - /* virtual functions */ - void (*export_adapter) (ValentMessagesAdapter *adapter, - ValentMessagesAdapter *object); - void (*unexport_adapter) (ValentMessagesAdapter *adapter, - ValentMessagesAdapter *object); - /*< private >*/ gpointer padding[8]; }; @@ -61,12 +56,6 @@ VALENT_AVAILABLE_IN_1_0 gboolean valent_messages_adapter_send_message_finish (ValentMessagesAdapter *adapter, GAsyncResult *result, GError **error); -VALENT_AVAILABLE_IN_1_0 -void valent_messages_adapter_export_adapter (ValentMessagesAdapter *adapter, - ValentMessagesAdapter *object); -VALENT_AVAILABLE_IN_1_0 -void valent_messages_adapter_unexport_adapter (ValentMessagesAdapter *adapter, - ValentMessagesAdapter *object); G_END_DECLS diff --git a/src/libvalent/messages/valent-messages.c b/src/libvalent/messages/valent-messages.c index 6a28b982c6..4d908b1710 100644 --- a/src/libvalent/messages/valent-messages.c +++ b/src/libvalent/messages/valent-messages.c @@ -32,10 +32,13 @@ struct _ValentMessages { ValentComponent parent_instance; - GPtrArray *adapters; + /* list model */ + GPtrArray *items; }; -static void g_list_model_iface_init (GListModelInterface *iface); +static void valent_messages_unbind_extension (ValentComponent *component, + GObject *extension); +static void g_list_model_iface_init (GListModelInterface *iface); G_DEFINE_FINAL_TYPE_WITH_CODE (ValentMessages, valent_messages, VALENT_TYPE_COMPONENT, G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, g_list_model_iface_init)) @@ -53,10 +56,10 @@ valent_messages_get_item (GListModel *list, g_assert (VALENT_IS_MESSAGES (self)); - if G_UNLIKELY (position >= self->adapters->len) + if G_UNLIKELY (position >= self->items->len) return NULL; - return g_object_ref (g_ptr_array_index (self->adapters, position)); + return g_object_ref (g_ptr_array_index (self->items, position)); } static GType @@ -72,7 +75,7 @@ valent_messages_get_n_items (GListModel *list) g_assert (VALENT_IS_MESSAGES (self)); - return self->adapters->len; + return self->items->len; } static void @@ -91,15 +94,30 @@ valent_messages_bind_extension (ValentComponent *component, GObject *extension) { ValentMessages *self = VALENT_MESSAGES (component); - GListModel *list = G_LIST_MODEL (extension); + unsigned int position = 0; VALENT_ENTRY; g_assert (VALENT_IS_MESSAGES (self)); g_assert (VALENT_IS_MESSAGES_ADAPTER (extension)); - g_ptr_array_add (self->adapters, g_object_ref (extension)); - g_list_model_items_changed (list, self->adapters->len, 0, 1); + if (g_ptr_array_find (self->items, extension, &position)) + { + g_warning ("Adapter \"%s\" already exported in \"%s\"", + G_OBJECT_TYPE_NAME (extension), + G_OBJECT_TYPE_NAME (component)); + return; + } + + g_signal_connect_object (extension, + "destroy", + G_CALLBACK (valent_messages_unbind_extension), + self, + G_CONNECT_SWAPPED); + + position = self->items->len; + g_ptr_array_add (self->items, g_object_ref (extension)); + g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1); VALENT_EXIT; } @@ -109,7 +127,7 @@ valent_messages_unbind_extension (ValentComponent *component, GObject *extension) { ValentMessages *self = VALENT_MESSAGES (component); - GListModel *list = G_LIST_MODEL (extension); + g_autoptr (ValentExtension) item = NULL; unsigned int position = 0; VALENT_ENTRY; @@ -117,16 +135,17 @@ valent_messages_unbind_extension (ValentComponent *component, g_assert (VALENT_IS_MESSAGES (self)); g_assert (VALENT_IS_MESSAGES_ADAPTER (extension)); - if (!g_ptr_array_find (self->adapters, extension, &position)) + if (!g_ptr_array_find (self->items, extension, &position)) { - g_warning ("No such adapter \"%s\" found in \"%s\"", + g_warning ("Adapter \"%s\" found in \"%s\"", G_OBJECT_TYPE_NAME (extension), G_OBJECT_TYPE_NAME (component)); return; } - g_ptr_array_remove (self->adapters, extension); - g_list_model_items_changed (list, position, 1, 0); + g_signal_handlers_disconnect_by_func (extension, valent_messages_unbind_extension, self); + item = g_ptr_array_steal_index (self->items, position); + g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0); VALENT_EXIT; } @@ -139,7 +158,7 @@ valent_messages_finalize (GObject *object) { ValentMessages *self = VALENT_MESSAGES (object); - g_clear_pointer (&self->adapters, g_ptr_array_unref); + g_clear_pointer (&self->items, g_ptr_array_unref); G_OBJECT_CLASS (valent_messages_parent_class)->finalize (object); } @@ -159,7 +178,7 @@ valent_messages_class_init (ValentMessagesClass *klass) static void valent_messages_init (ValentMessages *self) { - self->adapters = g_ptr_array_new_with_free_func (g_object_unref); + self->items = g_ptr_array_new_with_free_func (g_object_unref); } /** @@ -200,33 +219,13 @@ void valent_messages_export_adapter (ValentMessages *messages, ValentMessagesAdapter *object) { - unsigned int position = 0; - VALENT_ENTRY; g_return_if_fail (VALENT_IS_MESSAGES (messages)); g_return_if_fail (VALENT_IS_MESSAGES_ADAPTER (object)); - if (g_ptr_array_find (messages->adapters, object, NULL)) - { - g_critical ("%s(): known export %s", - G_STRFUNC, - G_OBJECT_TYPE_NAME (object)); - VALENT_EXIT; - } - - // Starting at index `1` skips the exports GListModel - for (unsigned int i = 1; i < messages->adapters->len; i++) - { - ValentMessagesAdapter *adapter = NULL; - - adapter = g_ptr_array_index (messages->adapters, i); - valent_messages_adapter_export_adapter (adapter, object); - } - - position = messages->adapters->len; - g_ptr_array_add (messages->adapters, g_object_ref (object)); - g_list_model_items_changed (G_LIST_MODEL (messages), position, 0, 1); + valent_messages_bind_extension (VALENT_COMPONENT (messages), + G_OBJECT (object)); VALENT_EXIT; } @@ -244,32 +243,13 @@ void valent_messages_unexport_adapter (ValentMessages *messages, ValentMessagesAdapter *object) { - unsigned int position = 0; - VALENT_ENTRY; g_return_if_fail (VALENT_IS_MESSAGES (messages)); g_return_if_fail (VALENT_IS_MESSAGES_ADAPTER (object)); - if (!g_ptr_array_find (messages->adapters, object, &position)) - { - g_critical ("%s(): unknown export %s", - G_STRFUNC, - G_OBJECT_TYPE_NAME (object)); - VALENT_EXIT; - } - - for (unsigned int i = 0; i < messages->adapters->len; i++) - { - ValentMessagesAdapter *adapter = NULL; - - adapter = g_ptr_array_index (messages->adapters, i); - if (adapter != object) - valent_messages_adapter_unexport_adapter (adapter, object); - } - - g_ptr_array_remove (messages->adapters, g_object_ref (object)); - g_list_model_items_changed (G_LIST_MODEL (messages), position, 1, 0); + valent_messages_unbind_extension (VALENT_COMPONENT (messages), + G_OBJECT (object)); VALENT_EXIT; } diff --git a/src/plugins/gnome/valent-messages-window.c b/src/plugins/gnome/valent-messages-window.c index 8261f58afb..e0bc2b6a40 100644 --- a/src/plugins/gnome/valent-messages-window.c +++ b/src/plugins/gnome/valent-messages-window.c @@ -472,7 +472,7 @@ sidebar_list_create (gpointer item, ValentMessagesWindow *window = VALENT_MESSAGES_WINDOW (user_data); GListModel *thread = G_LIST_MODEL (item); g_autoptr (ValentMessage) message = NULL; - ValentMessageBox box; + ValentMessageBox box = VALENT_MESSAGE_BOX_ALL; const char * const *recipients = NULL; const char *medium = NULL; GtkWidget *row; @@ -490,7 +490,9 @@ sidebar_list_create (gpointer item, g_object_unref); // TODO: participant-based avatar for sidebar rows - box = valent_message_get_box (message); + if (message != NULL) + box = valent_message_get_box (message); + if (box == VALENT_MESSAGE_BOX_INBOX) { medium = valent_message_get_sender (message); @@ -562,12 +564,13 @@ on_conversation_activated (GtkListBox *box, GtkListBoxRow *row, ValentMessagesWindow *self) { - ValentContext *context; GtkWidget *page; EContact *contact; ValentMessage *message; const char *sender; - g_autofree char *iri = NULL; + int64_t thread_id; + g_autofree char *adapter_urn = NULL; + g_autofree char *thread_urn = NULL; g_assert (VALENT_IS_MESSAGES_WINDOW (self)); g_assert (VALENT_IS_MESSAGE_ROW (row)); @@ -578,12 +581,11 @@ on_conversation_activated (GtkListBox *box, // TODO: use IRI contact = valent_message_row_get_contact (VALENT_MESSAGE_ROW (row)); message = valent_message_row_get_message (VALENT_MESSAGE_ROW (row)); - context = valent_extension_get_context (VALENT_EXTENSION (self->messages_adapter)); - iri = g_strdup_printf ("valent://%s/%"PRId64, - valent_context_get_path (context), - valent_message_get_thread_id (message)); + thread_id = valent_message_get_thread_id (message); + adapter_urn = valent_object_dup_iri (VALENT_OBJECT (self->messages_adapter)); + thread_urn = g_strdup_printf ("%s:%"PRId64, adapter_urn, thread_id); - page = valent_messages_window_ensure_conversation (self, iri); + page = valent_messages_window_ensure_conversation (self, thread_urn); sender = valent_message_get_sender (message); if (sender != NULL && *sender != '\0') { @@ -613,15 +615,6 @@ on_selected_item (GObject *object, if (!g_set_object (&self->messages_adapter, adapter)) return; - // FIXME: adapters need properties - if (g_strcmp0 (G_OBJECT_TYPE_NAME (adapter), "ValentSmsDevice") != 0) - { - g_warning ("%s(): unsupported message source \"%s\"", - G_STRFUNC, - G_OBJECT_TYPE_NAME (adapter)); - return; - } - // HACK: try to find a matching contacts adapter owner = valent_extension_get_object (VALENT_EXTENSION (adapter)); n_items = g_list_model_get_n_items (self->contacts); @@ -839,19 +832,17 @@ valent_messages_window_set_active_message (ValentMessagesWindow *window, { GtkWidget *widget; ValentConversationPage *conversation; - g_autofree char *iri = NULL; - ValentContext *context; int64_t thread_id; + g_autofree char *adapter_urn = NULL; + g_autofree char *thread_urn = NULL; g_return_if_fail (VALENT_IS_MESSAGES_WINDOW (window)); - context = valent_extension_get_context (VALENT_EXTENSION (window->messages_adapter)); thread_id = valent_message_get_thread_id (message); - iri = g_strdup_printf ("valent://%s/%"PRId64, - valent_context_get_path (context), - thread_id); + adapter_urn = valent_object_dup_iri (VALENT_OBJECT (window->messages_adapter)); + thread_urn = g_strdup_printf ("%s:%"PRId64, adapter_urn, thread_id); - widget = valent_messages_window_ensure_conversation (window, iri); + widget = valent_messages_window_ensure_conversation (window, thread_urn); conversation = VALENT_CONVERSATION_PAGE (widget); valent_conversation_page_scroll_to_message (conversation, message); } diff --git a/src/plugins/sms/valent-sms-device.c b/src/plugins/sms/valent-sms-device.c index 0bb8b4e53f..2a13184d97 100644 --- a/src/plugins/sms/valent-sms-device.c +++ b/src/plugins/sms/valent-sms-device.c @@ -42,13 +42,6 @@ static void valent_sms_device_request_conversation (ValentSmsDevice *self, G_DEFINE_FINAL_TYPE (ValentSmsDevice, valent_sms_device, VALENT_TYPE_MESSAGES_ADAPTER) -typedef enum -{ - PROP_DEVICE = 1, -} ValentSmsDeviceProperty; - -static GParamSpec *properties[PROP_DEVICE + 1] = { 0, }; - typedef struct { @@ -70,11 +63,10 @@ attachment_request_free (gpointer data) } static void -attachment_request_next_cb (GFile *file, - GAsyncResult *result, - gpointer user_data) +attachment_request_next_cb (GFile *file, + GAsyncResult *result, + ValentSmsDevice *self) { - g_autoptr (ValentSmsDevice) self = VALENT_SMS_DEVICE (g_steal_pointer (&user_data)); g_autoptr (GFileInfo) info = NULL; g_autoptr (GError) error = NULL; @@ -122,7 +114,7 @@ attachment_request_next (ValentSmsDevice *self) G_PRIORITY_DEFAULT, self->cancellable, (GAsyncReadyCallback) attachment_request_next_cb, - g_object_ref (self)); + self); } } @@ -162,11 +154,10 @@ update_attachment_cb (TrackerSparqlConnection *connection, } static void -attachment_request_query_cb (GFile *file, - GAsyncResult *result, - gpointer user_data) +attachment_request_query_cb (GFile *file, + GAsyncResult *result, + ValentSmsDevice *self) { - g_autoptr (ValentSmsDevice) self = g_steal_pointer (&user_data); const char *iri = NULL; g_autofree char *uri = NULL; g_autoptr (TrackerResource) attachment = NULL; @@ -211,17 +202,16 @@ attachment_request_query_cb (GFile *file, tracker_sparql_connection_update_resource_async (self->connection, VALENT_MESSAGES_GRAPH, attachment, - NULL, + self->cancellable, (GAsyncReadyCallback)update_attachment_cb, NULL); } static void -handle_attachment_file_cb (ValentTransfer *transfer, - GAsyncResult *result, - gpointer user_data) +handle_attachment_file_cb (ValentTransfer *transfer, + GAsyncResult *result, + ValentSmsDevice *self) { - g_autoptr (ValentSmsDevice) self = g_steal_pointer (&user_data); g_autoptr (GError) error = NULL; if (!valent_transfer_execute_finish (transfer, result, &error)) @@ -245,9 +235,9 @@ handle_attachment_file_cb (ValentTransfer *transfer, "standard::*", G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, - NULL, // cancellable, + self->cancellable, (GAsyncReadyCallback)attachment_request_query_cb, - g_object_ref (self)); + self); } if (!g_queue_is_empty (&self->attachment_requests)) @@ -498,7 +488,7 @@ valent_message_resource_from_json (ValentSmsDevice *self, unique_identifier = json_node_get_string (subnode); - rel_iri = g_strdup_printf ("%s/%s", iri, unique_identifier); + rel_iri = g_strdup_printf ("%s:%s", iri, unique_identifier); rel = tracker_resource_new (rel_iri); tracker_resource_set_uri (rel, "rdf:type", "nfo:Attachment"); tracker_resource_set_string (rel, "nfo:fileName", unique_identifier); @@ -532,7 +522,7 @@ execute_add_messages_cb (TrackerBatch *batch, if (!tracker_batch_execute_finish (batch, result, &error) && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - g_debug ("%s(): %s", G_STRFUNC, error->message); + g_warning ("%s(): %s", G_STRFUNC, error->message); } } @@ -541,7 +531,7 @@ valent_sms_device_add_json (ValentSmsDevice *self, JsonNode *messages) { g_autoptr (TrackerBatch) batch = NULL; - TrackerResource *thread = NULL; + g_autoptr (TrackerResource) thread = NULL; g_autofree char *base_urn = NULL; g_autofree char *thread_urn = NULL; int64_t thread_id; @@ -688,7 +678,6 @@ valent_sms_device_request_conversation (ValentSmsDevice *self, NULL); } -#if 0 static inline void valent_sms_device_request_conversations (ValentSmsDevice *self) { @@ -706,7 +695,6 @@ valent_sms_device_request_conversations (ValentSmsDevice *self) (GAsyncReadyCallback) valent_device_send_packet_cb, NULL); } -#endif /* * ValentMessagesAdapter @@ -855,19 +843,20 @@ valent_sms_device_send_message (ValentMessagesAdapter *adapter, g_object_ref (task)); } -/* - * ValentObject - */ static void -valent_sms_device_destroy (ValentObject *object) +on_device_state_changed (ValentDevice *device, + GParamSpec *pspec, + ValentSmsDevice *self) { - ValentSmsDevice *self = VALENT_SMS_DEVICE (object); + ValentDeviceState state = VALENT_DEVICE_STATE_NONE; + gboolean available; - g_clear_object (&self->device); - g_clear_object (&self->connection); - g_clear_object (&self->get_timestamp_stmt); + state = valent_device_get_state (device); + available = (state & VALENT_DEVICE_STATE_CONNECTED) != 0 && + (state & VALENT_DEVICE_STATE_PAIRED) != 0; - VALENT_OBJECT_CLASS (valent_sms_device_parent_class)->destroy (object); + if (available) + valent_sms_device_request_conversations (self); } /* @@ -880,51 +869,28 @@ valent_sms_device_constructed (GObject *object) G_OBJECT_CLASS (valent_sms_device_parent_class)->constructed (object); - g_object_get (self, "connection", &self->connection, NULL); -} - -static void -valent_sms_device_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - ValentSmsDevice *self = VALENT_SMS_DEVICE (object); - - switch ((ValentSmsDeviceProperty)prop_id) - { - case PROP_DEVICE: - g_value_set_object (value, self->device); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } + self->device = valent_extension_get_object (VALENT_EXTENSION (self)); + g_signal_connect_object (self->device, + "notify::state", + G_CALLBACK (on_device_state_changed), + self, + G_CONNECT_DEFAULT); + + g_object_get (self, + "connection", &self->connection, + "cancellable", &self->cancellable, + NULL); } static void -valent_sms_device_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) +valent_sms_device_finalize (GObject *object) { ValentSmsDevice *self = VALENT_SMS_DEVICE (object); - switch ((ValentSmsDeviceProperty)prop_id) - { - case PROP_DEVICE: - self->device = g_value_dup_object (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -valent_sms_device_finalize (GObject *object) -{ - /* ValentSmsDevice *self = VALENT_SMS_DEVICE (object); */ + g_queue_clear_full (&self->attachment_requests, attachment_request_free); + g_clear_pointer (&self->message_requests, g_ptr_array_unref); + g_clear_object (&self->connection); + g_clear_object (&self->get_timestamp_stmt); G_OBJECT_CLASS (valent_sms_device_parent_class)->finalize (object); } @@ -933,32 +899,12 @@ static void valent_sms_device_class_init (ValentSmsDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass); ValentMessagesAdapterClass *adapter_class = VALENT_MESSAGES_ADAPTER_CLASS (klass); object_class->constructed = valent_sms_device_constructed; object_class->finalize = valent_sms_device_finalize; - object_class->get_property = valent_sms_device_get_property; - object_class->set_property = valent_sms_device_set_property; - - vobject_class->destroy = valent_sms_device_destroy; adapter_class->send_message = valent_sms_device_send_message; - - /** - * ValentSmsDevice:device: - * - * The device hosting the message store. - */ - properties [PROP_DEVICE] = - g_param_spec_object ("device", NULL, NULL, - VALENT_TYPE_DEVICE, - (G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_EXPLICIT_NOTIFY | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties); } static void @@ -980,15 +926,18 @@ ValentMessagesAdapter * valent_sms_device_new (ValentDevice *device) { g_autoptr (ValentContext) context = NULL; + g_autofree char *iri = NULL; g_return_val_if_fail (VALENT_IS_DEVICE (device), NULL); context = valent_context_new (valent_device_get_context (device), "plugin", "sms"); + iri = tracker_sparql_escape_uri_printf ("urn:valent:messages:%s", + valent_device_get_id (device)); return g_object_new (VALENT_TYPE_SMS_DEVICE, + "iri", iri, "object", device, - "device", device, "context", context, NULL); } @@ -1190,7 +1139,7 @@ void valent_sms_device_handle_messages (ValentSmsDevice *self, JsonNode *packet) { - ValentContext *context = NULL; + ValentDevice *device = NULL; g_autofree char *thread_iri = NULL; JsonNode *node; JsonObject *body; @@ -1225,11 +1174,11 @@ valent_sms_device_handle_messages (ValentSmsDevice *self, return; } - context = valent_extension_get_context (VALENT_EXTENSION (self)); + device = valent_extension_get_object (VALENT_EXTENSION (self)); thread_id = json_object_get_int_member (json_array_get_object_element (messages, 0), "thread_id"); - thread_iri = g_strdup_printf ("valent://%s/%"PRId64, - valent_context_get_path (context), + thread_iri = g_strdup_printf ("urn:valent:messages:%s:%"PRId64, + valent_device_get_id (device), thread_id); /* Check if there is an active request for this thread @@ -1318,6 +1267,6 @@ valent_sms_device_handle_attachment_file (ValentSmsDevice *self, valent_transfer_execute (transfer, self->cancellable, (GAsyncReadyCallback) handle_attachment_file_cb, - g_object_ref (self)); + self); } diff --git a/src/plugins/sms/valent-sms-plugin.c b/src/plugins/sms/valent-sms-plugin.c index 8ab8d0d631..ab1ce20e4d 100644 --- a/src/plugins/sms/valent-sms-plugin.c +++ b/src/plugins/sms/valent-sms-plugin.c @@ -5,7 +5,6 @@ #include "config.h" -#include #include #include #include @@ -19,8 +18,7 @@ struct _ValentSmsPlugin { ValentDevicePlugin parent_instance; - ValentMessagesAdapter *store; - GCancellable *cancellable; + ValentMessagesAdapter *adapter; }; G_DEFINE_FINAL_TYPE (ValentSmsPlugin, valent_sms_plugin, VALENT_TYPE_DEVICE_PLUGIN) @@ -34,14 +32,11 @@ sync_action (GSimpleAction *action, gpointer user_data) { ValentSmsPlugin *self = VALENT_SMS_PLUGIN (user_data); - g_autoptr (JsonBuilder) builder = NULL; g_autoptr (JsonNode) packet = NULL; g_assert (VALENT_IS_SMS_PLUGIN (self)); - valent_packet_init (&builder, "kdeconnect.sms.request_conversations"); - packet = valent_packet_end (&builder); - + packet = valent_packet_new ("kdeconnect.sms.request_conversations"); valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), packet); } @@ -65,24 +60,6 @@ valent_sms_plugin_update_state (ValentDevicePlugin *plugin, (state & VALENT_DEVICE_STATE_PAIRED) != 0; valent_extension_toggle_actions (VALENT_EXTENSION (plugin), available); - - /* Request summary of messages */ - if (available) - { - if (self->cancellable == NULL) - { - self->cancellable = g_cancellable_new (); - g_action_group_activate_action (G_ACTION_GROUP (self), "sync", NULL); - } - } - else - { - if (self->cancellable != NULL) - { - g_cancellable_cancel (self->cancellable); - g_clear_object (&self->cancellable); - } - } } static void @@ -97,9 +74,9 @@ valent_sms_plugin_handle_packet (ValentDevicePlugin *plugin, g_assert (VALENT_IS_PACKET (packet)); if (g_str_equal (type, "kdeconnect.sms.messages")) - valent_sms_device_handle_messages (VALENT_SMS_DEVICE (self->store), packet); + valent_sms_device_handle_messages (VALENT_SMS_DEVICE (self->adapter), packet); else if (g_str_equal (type, "kdeconnect.sms.attachment_file")) - valent_sms_device_handle_attachment_file (VALENT_SMS_DEVICE (self->store), packet); + valent_sms_device_handle_attachment_file (VALENT_SMS_DEVICE (self->adapter), packet); else g_assert_not_reached (); } @@ -112,11 +89,12 @@ valent_sms_plugin_destroy (ValentObject *object) { ValentSmsPlugin *self = VALENT_SMS_PLUGIN (object); - if (self->store != NULL) + if (self->adapter != NULL) { valent_messages_unexport_adapter (valent_messages_get_default (), - self->store); - g_clear_object (&self->store); + self->adapter); + valent_object_destroy (VALENT_OBJECT (self->adapter)); + g_clear_object (&self->adapter); } VALENT_OBJECT_CLASS (valent_sms_plugin_parent_class)->destroy (object); @@ -129,29 +107,19 @@ static void valent_sms_plugin_constructed (GObject *object) { ValentSmsPlugin *self = VALENT_SMS_PLUGIN (object); - ValentDevicePlugin *plugin = VALENT_DEVICE_PLUGIN (object); ValentDevice *device = NULL; G_OBJECT_CLASS (valent_sms_plugin_parent_class)->constructed (object); - device = valent_extension_get_object (VALENT_EXTENSION (plugin)); - self->store = valent_sms_device_new (device); - valent_messages_export_adapter (valent_messages_get_default (), self->store); + device = valent_extension_get_object (VALENT_EXTENSION (self)); + self->adapter = valent_sms_device_new (device); + valent_messages_export_adapter (valent_messages_get_default (), + self->adapter); - g_action_map_add_action_entries (G_ACTION_MAP (plugin), + g_action_map_add_action_entries (G_ACTION_MAP (self), actions, G_N_ELEMENTS (actions), - plugin); -} - -static void -valent_sms_plugin_finalize (GObject *object) -{ - ValentSmsPlugin *self = VALENT_SMS_PLUGIN (object); - - g_clear_object (&self->store); - - G_OBJECT_CLASS (valent_sms_plugin_parent_class)->finalize (object); + self); } static void @@ -162,12 +130,11 @@ valent_sms_plugin_class_init (ValentSmsPluginClass *klass) ValentDevicePluginClass *plugin_class = VALENT_DEVICE_PLUGIN_CLASS (klass); object_class->constructed = valent_sms_plugin_constructed; - object_class->finalize = valent_sms_plugin_finalize; + + vobject_class->destroy = valent_sms_plugin_destroy; plugin_class->handle_packet = valent_sms_plugin_handle_packet; plugin_class->update_state = valent_sms_plugin_update_state; - - vobject_class->destroy = valent_sms_plugin_destroy; } static void diff --git a/tests/extra/lsan.supp b/tests/extra/lsan.supp index dc567f43e5..619a9a6ebb 100644 --- a/tests/extra/lsan.supp +++ b/tests/extra/lsan.supp @@ -13,9 +13,14 @@ leak:update_custom_image_snapshot # https://gitlab.gnome.org/GNOME/evolution-data-server/-/merge_requests/162 leak:e_contact_get_property -# Unconfirmed leak (valent-notifications-plugin) +# FIXME: Unconfirmed (valent-notifications-plugin) leak:gtk_media_file_extension_init +# FIXME: Unconfirmed (valent-messages-adapter,valent-sms-device) +leak:tracker_sparql_execute_cursor +leak:tracker_sparql_execute_update +leak:translate_RDFLiteral.lto_priv.0 + # False positives caused by G_TEST_OPTION_ISOLATE_DIRS leak:g_content_type_set_mime_dirs leak:g_test_init diff --git a/tests/extra/tsan.supp b/tests/extra/tsan.supp index 0436996f03..db4dffdc6b 100644 --- a/tests/extra/tsan.supp +++ b/tests/extra/tsan.supp @@ -55,6 +55,11 @@ called_from_lib:libphonenumber called_from_lib:libprotobuf called_from_lib:libsqlite3 +# TinySPARQL Dependencies +called_from_lib:libstemmer.so +called_from_lib:libtracker-parser-libicu.so +called_from_lib:libtracker-sparql-3.0 + # GLib called_from_lib:libglib-2 called_from_lib:libgobject-2 @@ -88,4 +93,5 @@ mutex:swrast_dri.so race:libEGL race:libGLX race:radeonsi_dri +race:swrast_dri.so diff --git a/tests/fixtures/data/graph-messages.turtle b/tests/fixtures/data/graph-messages.turtle new file mode 100644 index 0000000000..0b65323fc5 --- /dev/null +++ b/tests/fixtures/data/graph-messages.turtle @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: No rights reserved + +@prefix nmo: . +@prefix nrl: . +@prefix rdfs: . +@prefix fts: . +@prefix tracker: . +@prefix nfo: . +@prefix dc: . +@prefix vmo: . +@prefix nco: . +@prefix nie: . +@prefix xsd: . + + rdf:type nco:PhoneNumber ; + nco:phoneNumber "2345678917" . + + rdf:type nco:PhoneNumber ; + nco:phoneNumber "2345678918" . + + rdf:type nfo:Attachment ; + nie:url "resource://tests/image.jpg" ; + nfo:fileName "image.jpg" ; + vmo:encoded_thumbnail "/9j/4AAQSkZJRgABAQAAAQABAAD/4QBWRXhpZgAATU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAEAAAITAAMAAAABAAEAAAAAAAAAAAABAAAAAQAAAAEAAAAB/9sAhAAKCgoKCgoLDAwLDxAOEA8WFBMTFBYiGBoYGhgiMyAlICAlIDMtNywpLDctUUA4OEBRXk9KT15xZWVxj4iPu7v7AQoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/v/wgARCAIAAgADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/2gAIAQEAAAAAswAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xAAUEAEAAAAAAAAAAAAAAAAAAADA/9oACAEBAAE/AAAH/8QAFBEBAAAAAAAAAAAAAAAAAAAAoP/aAAgBAgEBPwAAH//EABQRAQAAAAAAAAAAAAAAAAAAAKD/2gAIAQMBAT8AAB//2Q==" . + + rdf:type vmo:CommunicationChannel ; + vmo:communicationChannelId 38 ; + vmo:hasParticipant ; + vmo:hasParticipant . + + rdf:type vmo:PhoneMessage ; + vmo:communicationChannel ; + vmo:subscriptionId -1 ; + nmo:plainTextMessageContent "ای بهار من" ; + nmo:messageSender ; + vmo:phoneMessageBox vmo:android-message-type-inbox ; + nmo:messageFrom ; + vmo:phoneMessageId 3314 ; + nmo:receivedDate "2018-11-29T17:34:55.320000-08:00" ; + nmo:hasAttachment ; + nmo:isRead true . + + rdf:type vmo:PhoneMessage ; + vmo:communicationChannel ; + vmo:subscriptionId -1 ; + nmo:plainTextMessageContent "ای نقاش من" ; + nmo:messageSender ; + vmo:phoneMessageBox vmo:android-message-type-sent ; + nmo:primaryMessageRecipient ; + nmo:messageFrom ; + vmo:phoneMessageId 3315 ; + nmo:sentDate "2018-11-29T17:35:55.320000-08:00" ; + nmo:isRead true . + diff --git a/tests/fixtures/data/image.jpg b/tests/fixtures/data/image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..caff6a3d363321da6ef4d929c55ec5e4067526ac GIT binary patch literal 1231 zcmex=@0K0sjv%2y!qnFflMQDlsq# zGBOJ?{y)MX3idwO%`k%f|1Aa%P@pg{Y=()A(jzkjfGI=-k`$2r4^ALV0?0xG#Kz-3 q=E34V0dQ7A3ZMhf48~~Bz`zdjfgrky1&|PC1gVh6qk mock.plugin data/graph-contacts.turtle + data/graph-messages.turtle packetless.plugin @@ -15,6 +16,7 @@ data/contact.vcf data/contact2.vcf data/contact3.vcf + data/image.jpg data/image.png diff --git a/tests/fixtures/meson.build b/tests/fixtures/meson.build index 1e620b92f1..27a70da135 100644 --- a/tests/fixtures/meson.build +++ b/tests/fixtures/meson.build @@ -39,6 +39,7 @@ libvalent_test_public_sources = [ 'valent-mock-input-adapter.c', 'valent-mock-media-adapter.c', 'valent-mock-media-player.c', + 'valent-mock-messages-adapter.c', 'valent-mock-mixer-adapter.c', 'valent-mock-notifications-adapter.c', 'valent-mock-session-adapter.c', diff --git a/tests/fixtures/mock-plugin.c b/tests/fixtures/mock-plugin.c index 02a35da99e..a870d34097 100644 --- a/tests/fixtures/mock-plugin.c +++ b/tests/fixtures/mock-plugin.c @@ -12,6 +12,7 @@ #include "valent-mock-contacts-adapter.h" #include "valent-mock-input-adapter.h" #include "valent-mock-media-adapter.h" +#include "valent-mock-messages-adapter.h" #include "valent-mock-mixer-adapter.h" #include "valent-mock-notifications-adapter.h" #include "valent-mock-session-adapter.h" @@ -36,12 +37,15 @@ valent_mock_plugin_register_types (PeasObjectModule *module) peas_object_module_register_extension_type (module, VALENT_TYPE_INPUT_ADAPTER, VALENT_TYPE_MOCK_INPUT_ADAPTER); - peas_object_module_register_extension_type (module, - VALENT_TYPE_MIXER_ADAPTER, - VALENT_TYPE_MOCK_MIXER_ADAPTER); peas_object_module_register_extension_type (module, VALENT_TYPE_MEDIA_ADAPTER, VALENT_TYPE_MOCK_MEDIA_ADAPTER); + peas_object_module_register_extension_type (module, + VALENT_TYPE_MESSAGES_ADAPTER, + VALENT_TYPE_MOCK_MESSAGES_ADAPTER); + peas_object_module_register_extension_type (module, + VALENT_TYPE_MIXER_ADAPTER, + VALENT_TYPE_MOCK_MIXER_ADAPTER); peas_object_module_register_extension_type (module, VALENT_TYPE_NOTIFICATIONS_ADAPTER, VALENT_TYPE_MOCK_NOTIFICATIONS_ADAPTER); diff --git a/tests/fixtures/valent-mock-messages-adapter.c b/tests/fixtures/valent-mock-messages-adapter.c new file mode 100644 index 0000000000..3e0064cd24 --- /dev/null +++ b/tests/fixtures/valent-mock-messages-adapter.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Andy Holmes + +#define G_LOG_DOMAIN "valent-mock-messages-adapter" + +#include "config.h" + +#include + +#include +#include +#include + +#include "valent-mock-messages-adapter.h" + +struct _ValentMockMessagesAdapter +{ + ValentMessagesAdapter parent_instance; +}; + +G_DEFINE_FINAL_TYPE (ValentMockMessagesAdapter, valent_mock_messages_adapter, VALENT_TYPE_MESSAGES_ADAPTER) + +static void +deserialize_cb (TrackerSparqlConnection *connection, + GAsyncResult *result, + gpointer user_data) +{ + GError *error = NULL; + + tracker_sparql_connection_deserialize_finish (connection, result, &error); + g_assert_no_error (error); +} + +static void +update_cb (TrackerSparqlConnection *connection, + GAsyncResult *result, + gpointer user_data) +{ + GError *error = NULL; + + tracker_sparql_connection_update_finish (connection, result, &error); + g_assert_no_error (error); +} + +static void +action_callback (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + ValentMockMessagesAdapter *self = VALENT_MOCK_MESSAGES_ADAPTER (user_data); + const char *name = g_action_get_name (G_ACTION (action)); + int64_t thread_id, message_id; + g_autoptr (TrackerSparqlConnection) connection = NULL; + + g_variant_get (parameter, "(xx)", &thread_id, &message_id); + g_object_get (self, "connection", &connection, NULL); + if (g_str_equal (name, "add-message")) + { + g_autofree char *sparql = NULL; + + sparql = g_strdup_printf ("INSERT DATA {" + " GRAPH {" + " rdf:type nco:PhoneNumber ;" + " nco:phoneNumber \"7786283857\" ." + "" + " rdf:type vmo:CommunicationChannel ;" + " vmo:communicationChannelId %"PRId64" ;" + " vmo:hasParticipant ." + "" + " rdf:type vmo:PhoneMessage ;" + " vmo:communicationChannel ;" + " vmo:subscriptionId -1 ;" + " nmo:plainTextMessageContent \"Sry mistyped the # 😅\" ;" + " nmo:messageSender ;" + " vmo:phoneMessageBox vmo:android-message-type-inbox ;" + " nmo:messageFrom ;" + " vmo:phoneMessageId %"PRId64" ;" + " nmo:receivedDate \"2018-11-29T17:38:55.320000-08:00\" ;" + " nmo:isRead true ." + " }" + "}", + thread_id, thread_id, + thread_id, message_id, + thread_id, message_id); + tracker_sparql_connection_update_async (connection, + sparql, + NULL, + (GAsyncReadyCallback)update_cb, + NULL); + } + else if (g_str_equal (name, "remove-message")) + { + g_autofree char *sparql = NULL; + + sparql = g_strdup_printf ("DELETE DATA {" + " GRAPH {" + " a vmo:PhoneMessage ;" + " }" + "}", + thread_id, + message_id); + tracker_sparql_connection_update_async (connection, + sparql, + NULL, + (GAsyncReadyCallback)update_cb, + NULL); + } + else if (g_str_equal (name, "remove-list")) + { + g_autofree char *sparql = NULL; + + sparql = g_strdup_printf ("DELETE DATA {" + " GRAPH {" + " vmo:PhoneMessage vmo:communicationChannel ." + " a vmo:CommunicationChannel ." + " }" + "}", + thread_id, + thread_id); + tracker_sparql_connection_update_async (connection, + sparql, + NULL, + (GAsyncReadyCallback)update_cb, + NULL); + } +} + +static const GActionEntry actions[] = { + {"add-message", action_callback, "(xx)", NULL, NULL}, + {"remove-message", action_callback, "(xx)", NULL, NULL}, + {"add-list", action_callback, "(xx)", NULL, NULL}, + {"remove-list", action_callback, "(xx)", NULL, NULL}, +}; + +static void +send_message_cb (TrackerSparqlConnection *connection, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data)); + GError *error = NULL; + + tracker_sparql_connection_update_finish (connection, result, &error); + if (error == NULL) + g_task_return_boolean (task, TRUE); + else + g_task_return_error (task, g_steal_pointer (&error)); +} + +static void +valent_mock_messages_adapter_send_message (ValentMessagesAdapter *adapter, + ValentMessage *message, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr (GTask) task = NULL; + g_autoptr (TrackerSparqlConnection) connection = NULL; + g_autofree char *sparql = NULL; + int64_t message_id = 0; + int64_t thread_id = 0; + + g_assert (VALENT_IS_MESSAGES_ADAPTER (adapter)); + g_assert (VALENT_IS_MESSAGE (message)); + g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (adapter, cancellable, callback, user_data); + g_task_set_source_tag (task, valent_mock_messages_adapter_send_message); + + g_object_get (adapter, "connection", &connection, NULL); + message_id = valent_message_get_id (message); + message_id = valent_message_get_thread_id (message); + sparql = g_strdup_printf ("INSERT DATA {" + " GRAPH {" + " rdf:type nco:PhoneNumber ;" + " nco:phoneNumber \"7786283857\" ." + "" + " rdf:type vmo:CommunicationChannel ;" + " vmo:communicationChannelId %"PRId64" ;" + " vmo:hasParticipant ." + "" + " rdf:type vmo:PhoneMessage ;" + " vmo:communicationChannel ;" + " vmo:subscriptionId -1 ;" + " nmo:plainTextMessageContent \"Sry mistyped the # 😅\" ;" + " nmo:messageSender ;" + " vmo:phoneMessageBox vmo:android-message-type-sent ;" + " nmo:messageFrom ;" + " vmo:phoneMessageId %"PRId64" ;" + " nmo:receivedDate \"2018-11-29T17:34:55.320000-08:00\" ;" + " nmo:isRead true ." + " }" + "}", + thread_id, thread_id, + thread_id, message_id, + thread_id, message_id); + tracker_sparql_connection_update_async (connection, + sparql, + cancellable, + (GAsyncReadyCallback)send_message_cb, + g_steal_pointer (&task)); +} + +static gboolean +valent_mock_messages_adapter_send_message_finish (ValentMessagesAdapter *adapter, + GAsyncResult *result, + GError **error) +{ + g_assert (VALENT_IS_MESSAGES_ADAPTER (adapter)); + g_assert (g_task_is_valid (result, adapter)); + g_assert (error == NULL || *error == NULL); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +/* + * GObject + */ +static void +valent_mock_messages_adapter_destroy (ValentObject *object) +{ + ValentMockMessagesAdapter *self = VALENT_MOCK_MESSAGES_ADAPTER (object); + ValentContext *context; + + VALENT_OBJECT_CLASS (valent_mock_messages_adapter_parent_class)->destroy (object); + + context = valent_extension_get_context (VALENT_EXTENSION (self)); + valent_context_clear_cache (context); +} + +static void +valent_mock_messages_adapter_constructed (GObject *object) +{ + ValentMockMessagesAdapter *self = VALENT_MOCK_MESSAGES_ADAPTER (object); + g_autoptr (TrackerSparqlConnection) connection = NULL; + g_autoptr (GInputStream) graph = NULL; + g_autoptr (GListModel) list = NULL; + + G_OBJECT_CLASS (valent_mock_messages_adapter_parent_class)->constructed (object); + + g_object_get (self, "connection", &connection, NULL); + graph = g_resources_open_stream ("/plugins/mock/graph-messages.turtle", + G_RESOURCE_LOOKUP_FLAGS_NONE, + NULL); + tracker_sparql_connection_deserialize_async (connection, + TRACKER_DESERIALIZE_FLAGS_NONE, + TRACKER_RDF_FORMAT_TURTLE, + VALENT_MESSAGES_GRAPH, + graph, + NULL, + (GAsyncReadyCallback)deserialize_cb, + NULL); + + while (g_list_model_get_n_items (G_LIST_MODEL (self)) == 0) + g_main_context_iteration (NULL, FALSE); +} + +static void +valent_mock_messages_adapter_class_init (ValentMockMessagesAdapterClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass); + ValentMessagesAdapterClass *adapter_class = VALENT_MESSAGES_ADAPTER_CLASS (klass); + + object_class->constructed = valent_mock_messages_adapter_constructed; + + vobject_class->destroy = valent_mock_messages_adapter_destroy; + + adapter_class->send_message = valent_mock_messages_adapter_send_message; + adapter_class->send_message_finish = valent_mock_messages_adapter_send_message_finish; +} + +static void +valent_mock_messages_adapter_init (ValentMockMessagesAdapter *self) +{ + g_action_map_add_action_entries (G_ACTION_MAP (self), + actions, + G_N_ELEMENTS (actions), + self); +} + diff --git a/tests/fixtures/valent-mock-messages-adapter.h b/tests/fixtures/valent-mock-messages-adapter.h new file mode 100644 index 0000000000..dd30036514 --- /dev/null +++ b/tests/fixtures/valent-mock-messages-adapter.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Andy Holmes + +#pragma once + +#include + +G_BEGIN_DECLS + +#define VALENT_TYPE_MOCK_MESSAGES_ADAPTER (valent_mock_messages_adapter_get_type ()) + +G_DECLARE_FINAL_TYPE (ValentMockMessagesAdapter, valent_mock_messages_adapter, VALENT, MOCK_MESSAGES_ADAPTER, ValentMessagesAdapter) + +G_END_DECLS + diff --git a/tests/libvalent/meson.build b/tests/libvalent/meson.build index 5a9b0c2b17..3ba317d6cb 100644 --- a/tests/libvalent/meson.build +++ b/tests/libvalent/meson.build @@ -7,6 +7,7 @@ subdir('contacts') subdir('device') subdir('input') subdir('media') +subdir('messages') subdir('mixer') subdir('notifications') subdir('session') diff --git a/tests/libvalent/messages/meson.build b/tests/libvalent/messages/meson.build new file mode 100644 index 0000000000..57ffdf79f9 --- /dev/null +++ b/tests/libvalent/messages/meson.build @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-FileCopyrightText: Andy Holmes + +# Dependencies +libvalent_messages_test_deps = [ + libvalent_test_dep, +] + +libvalent_messages_tests = [ + 'test-message', + 'test-messages-component', +] + +foreach test : libvalent_messages_tests + test_program = executable(test, '@0@.c'.format(test), + c_args: test_c_args, + dependencies: libvalent_messages_test_deps, + link_args: test_link_args, + link_whole: libvalent_test, + install: get_option('installed_tests'), + install_dir: installed_tests_execdir, + export_dynamic: true, + ) + + test(test, test_program, + args: ['--tap'], + env: tests_env, + is_parallel: false, + protocol: 'tap', + suite: ['libvalent', 'messages'], + ) + + installed_tests_plan += [{ + 'program': test_program, + }] +endforeach diff --git a/tests/libvalent/messages/test-message.c b/tests/libvalent/messages/test-message.c new file mode 100644 index 0000000000..8239a3e954 --- /dev/null +++ b/tests/libvalent/messages/test-message.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Andy Holmes + +#include +#include + +static void +test_message_basic (void) +{ + g_autoptr (ValentMessage) message = NULL; + + g_autoptr (GListStore) attachments = g_list_store_new (VALENT_TYPE_MESSAGE_ATTACHMENT); + ValentMessageBox box = VALENT_MESSAGE_BOX_OUTBOX; + int64_t date = 123456789; + int64_t id = 987654321; + gboolean read = TRUE; + GStrv recipients = (char *[]){ "1-234-567-8911", NULL, }; + const char *sender = "1-234-567-8910"; + int64_t subscription_id = 2; + const char *text = "Test Message"; + int64_t thread_id = 987321654; + + g_autoptr (GListStore) attachments2 = NULL; + ValentMessageBox box2; + int64_t date2; + int64_t id2; + g_autoptr (GVariant) metadata2 = NULL; + gboolean read2; + g_auto (GStrv) recipients2 = NULL; + g_autofree char *sender2 = NULL; + int64_t subscription_id2 = 2; + g_autofree char *text2 = NULL; + int64_t thread_id2; + + VALENT_TEST_CHECK ("Object can be constructed"); + message = g_object_new (VALENT_TYPE_MESSAGE, + "attachments", attachments, + "box", box, + "date", date, + "id", id, + "read", read, + "recipients", recipients, + "sender", sender, + "subscription-id", subscription_id, + "text", text, + "thread-id", thread_id, + NULL); + + VALENT_TEST_CHECK ("GObject properties function correctly"); + g_object_get (message, + "attachments", &attachments2, + "box", &box2, + "date", &date2, + "id", &id2, + "read", &read2, + "recipients", &recipients2, + "sender", &sender2, + "subscription-id", &subscription_id2, + "text", &text2, + "thread-id", &thread_id2, + NULL); + + g_assert_true (attachments == attachments2); + g_assert_cmpuint (box, ==, box2); + g_assert_cmpint (date, ==, date2); + g_assert_cmpint (id, ==, id2); + g_assert_true (read == read2); + g_assert_false (recipients == recipients2); // TODO: strv compare + g_assert_cmpstr (sender, ==, sender2); + g_assert_cmpint (subscription_id, ==, subscription_id2); + g_assert_cmpstr (text, ==, text2); + g_assert_cmpint (thread_id, ==, thread_id2); + + VALENT_TEST_CHECK ("Property getters function correctly"); + g_assert_true (G_LIST_MODEL (attachments) == valent_message_get_attachments (message)); + g_assert_cmpuint (box, ==, valent_message_get_box (message)); + g_assert_cmpint (date, ==, valent_message_get_date (message)); + g_assert_cmpint (id, ==, valent_message_get_id (message)); + g_assert_true (read == valent_message_get_read (message)); + g_assert_false ((const char * const *)recipients == valent_message_get_recipients (message)); // TODO: strv compare + g_assert_cmpstr (sender, ==, valent_message_get_sender (message)); + g_assert_cmpint (subscription_id, ==, valent_message_get_subscription_id (message)); + g_assert_cmpstr (text, ==, valent_message_get_text (message)); + g_assert_cmpint (thread_id, ==, valent_message_get_thread_id (message)); +} + +int +main (int argc, + char *argv[]) +{ + valent_test_init (&argc, &argv, NULL); + + g_test_add_func ("/libvalent/messages/message", + test_message_basic); + + return g_test_run (); +} + diff --git a/tests/libvalent/messages/test-messages-component.c b/tests/libvalent/messages/test-messages-component.c new file mode 100644 index 0000000000..627ea2c19e --- /dev/null +++ b/tests/libvalent/messages/test-messages-component.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Andy Holmes + +#include +#include +#include +#include + + +typedef struct +{ +} MessagesComponentFixture; + +static void +messages_component_fixture_set_up (MessagesComponentFixture *fixture, + gconstpointer user_data) +{ +} + +static void +messages_component_fixture_tear_down (MessagesComponentFixture *fixture, + gconstpointer user_data) +{ +} + +static void +test_messages_component_adapter (MessagesComponentFixture *fixture, + gconstpointer user_data) +{ + PeasEngine *engine; + PeasPluginInfo *plugin_info; + g_autoptr (GObject) adapter = NULL; + g_autoptr (TrackerSparqlConnection) connection = NULL; + unsigned int n_items = 0; + + engine = valent_get_plugin_engine (); + plugin_info = peas_engine_get_plugin_info (engine, "mock"); + + VALENT_TEST_CHECK ("Adapter can be constructed"); + adapter = peas_engine_create_extension (engine, + plugin_info, + VALENT_TYPE_MESSAGES_ADAPTER, + "iri", "urn:valent:messages:mock", + "object", NULL, + NULL); + + VALENT_TEST_CHECK ("GObject properties function correctly"); + g_object_get (adapter, + "connection", &connection, + NULL); + g_assert_true (TRACKER_IS_SPARQL_CONNECTION (connection)); + + VALENT_TEST_CHECK ("Adapter implements GListModel correctly"); + g_assert_true (G_LIST_MODEL (adapter)); + g_assert_true (g_list_model_get_item_type (G_LIST_MODEL (adapter)) == G_TYPE_LIST_MODEL); + + n_items = g_list_model_get_n_items (G_LIST_MODEL (adapter)); + for (unsigned int i = 0; i < n_items; i++) + { + g_autoptr (GListModel) item = g_list_model_get_item (G_LIST_MODEL (adapter), i); + g_assert_true (G_IS_LIST_MODEL (item)); + } + + VALENT_TEST_CHECK ("Adapter detects message lists added to the graph"); + g_action_group_activate_action (G_ACTION_GROUP (adapter), + "add-message", + g_variant_new ("(xx)", 4, 1)); + valent_test_await_signal (adapter, "items-changed"); + g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (adapter)), ==, n_items + 1); + + VALENT_TEST_CHECK ("Adapter detects message lists removed from the graph"); + g_action_group_activate_action (G_ACTION_GROUP (adapter), + "remove-list", + g_variant_new ("(xx)", 4, -1)); + valent_test_await_signal (adapter, "items-changed"); + g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (adapter)), ==, n_items); +} + +static void +test_messages_component_message_list (MessagesComponentFixture *fixture, + gconstpointer user_data) +{ + PeasEngine *engine; + PeasPluginInfo *plugin_info; + g_autoptr (GObject) adapter = NULL; + g_autoptr (GListModel) list = NULL; + g_autoptr (TrackerSparqlConnection) connection = NULL; + unsigned int n_items = 0; + + engine = valent_get_plugin_engine (); + plugin_info = peas_engine_get_plugin_info (engine, "mock"); + adapter = peas_engine_create_extension (engine, + plugin_info, + VALENT_TYPE_MESSAGES_ADAPTER, + "iri", "urn:valent:messages:mock", + "object", NULL, + NULL); + + // HACK: to address the lazy-load hack in ValentMessageThread + list = g_list_model_get_item (G_LIST_MODEL (adapter), 0); + while (g_list_model_get_n_items (G_LIST_MODEL (list)) != 2) + g_main_context_iteration (NULL, FALSE); + + VALENT_TEST_CHECK ("GObject properties function correctly"); + g_object_get (list, + "connection", &connection, + NULL); + g_assert_true (TRACKER_IS_SPARQL_CONNECTION (connection)); + + VALENT_TEST_CHECK ("Message list implements GListModel correctly"); + g_assert_true (G_IS_LIST_MODEL (list)); + g_assert_true (g_list_model_get_item_type (list) == VALENT_TYPE_MESSAGE); + + n_items = g_list_model_get_n_items (list); + for (unsigned int i = 0; i < n_items; i++) + { + g_autoptr (GObject) item = g_list_model_get_item (list, i); + g_assert_true (VALENT_IS_MESSAGE (item)); + } + + VALENT_TEST_CHECK ("Message list detects messages added to the graph"); + g_action_group_activate_action (G_ACTION_GROUP (adapter), + "add-message", + g_variant_new ("(xx)", 38, 3316)); + valent_test_await_signal (list, "items-changed"); + g_assert_cmpuint (g_list_model_get_n_items (list), ==, n_items + 1); + + VALENT_TEST_CHECK ("Message list detects messages removed from the graph"); + g_action_group_activate_action (G_ACTION_GROUP (adapter), + "remove-message", + g_variant_new ("(xx)", 38, 3316)); + valent_test_await_signal (list, "items-changed"); + g_assert_cmpuint (g_list_model_get_n_items (list), ==, n_items); +} + +static void +test_messages_component_self (MessagesComponentFixture *fixture, + gconstpointer user_data) +{ + ValentMessages *messages = valent_messages_get_default (); + unsigned int n_items = 0; + + VALENT_TEST_CHECK ("Component implements GListModel correctly"); + g_assert_true (G_LIST_MODEL (messages)); + g_assert_true (g_list_model_get_item_type (G_LIST_MODEL (messages)) == VALENT_TYPE_MESSAGES_ADAPTER); + + n_items = g_list_model_get_n_items (G_LIST_MODEL (messages)); + for (unsigned int i = 0; i < n_items; i++) + { + g_autoptr (ValentMessagesAdapter) item = NULL; + + item = g_list_model_get_item (G_LIST_MODEL (messages), i); + g_assert_true (VALENT_IS_MESSAGES_ADAPTER (item)); + } + + v_await_finalize_object (messages); +} + +int +main (int argc, + char *argv[]) +{ + valent_test_init (&argc, &argv, NULL); + + g_test_add ("/libvalent/messages/adapter", + MessagesComponentFixture, NULL, + messages_component_fixture_set_up, + test_messages_component_adapter, + messages_component_fixture_tear_down); + + g_test_add ("/libvalent/messages/list", + MessagesComponentFixture, NULL, + messages_component_fixture_set_up, + test_messages_component_message_list, + messages_component_fixture_tear_down); + + g_test_add ("/libvalent/messages/self", + MessagesComponentFixture, NULL, + messages_component_fixture_set_up, + test_messages_component_self, + messages_component_fixture_tear_down); + + return g_test_run (); +} diff --git a/tests/plugins/meson.build b/tests/plugins/meson.build index 73da7f366a..fd1268692b 100644 --- a/tests/plugins/meson.build +++ b/tests/plugins/meson.build @@ -23,7 +23,7 @@ plugin_tests = [ 'runcommand', 'sftp', 'share', - # FIXME: 'sms', + 'sms', 'systemvolume', 'telephony', diff --git a/tests/plugins/sms/test-sms-common.h b/tests/plugins/sms/test-sms-common.h deleted file mode 100644 index 20bfadf66d..0000000000 --- a/tests/plugins/sms/test-sms-common.h +++ /dev/null @@ -1,205 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -// SPDX-FileCopyrightText: Andy Holmes - -#pragma once - -#include -#include -#include - -#include "valent-message-store.h" - -G_BEGIN_DECLS - - -static void -valent_test_message_store_new_cb (ValentMessagesAdapter *store, - GAsyncResult *result, - gboolean *done) -{ - g_autoptr (GError) error = NULL; - - valent_messages_adapter_add_messages_finish (store, result, &error); - g_assert_no_error (error); - - if (done != NULL) - *done = TRUE; -} - -/** - * valent_test_contact1: - * - * Get test contact #1. - * - * Returns: (transfer none): a `EContact` - */ -static inline EContact * -valent_test_contact1 (void) -{ - static EContact *contact = NULL; - - if G_UNLIKELY (contact == NULL) - { - g_autoptr (GBytes) bytes = NULL; - - bytes = g_resources_lookup_data ("/tests/contact.vcf", 0, NULL); - contact = e_contact_new_from_vcard_with_uid (g_bytes_get_data (bytes, NULL), - "4077i252298cf8ded4bfe"); - } - - return contact; -} - -/** - * valent_test_contact2: - * - * Get test contact #2. - * - * Returns: (transfer none): a `EContact` - */ -static inline EContact * -valent_test_contact2 (void) -{ - static EContact *contact = NULL; - - if G_UNLIKELY (contact == NULL) - { - g_autoptr (GBytes) bytes = NULL; - - bytes = g_resources_lookup_data ("/tests/contact2.vcf", 0, NULL); - contact = e_contact_new_from_vcard_with_uid (g_bytes_get_data (bytes, NULL), - "4077i252298cf8ded4bff"); - } - - return contact; -} - -/** - * valent_test_contact3: - * - * Get test contact #3. - * - * Returns: (transfer none): a `EContact` - */ -static inline EContact * -valent_test_contact3 (void) -{ - static EContact *contact = NULL; - - if G_UNLIKELY (contact == NULL) - { - g_autoptr (GBytes) bytes = NULL; - - bytes = g_resources_lookup_data ("/tests/contact3.vcf", 0, NULL); - contact = e_contact_new_from_vcard_with_uid (g_bytes_get_data (bytes, NULL), - "4077i252298cf8ded4bfg"); - } - - return contact; -} - -/** - * valent_test_contact_store_new: - * - * Create a new `ValentContactsAdapter` for testing. - * - * Returns: (transfer full): a `ValentContactsAdapter` - */ -static inline ValentContactsAdapter * -valent_test_contact_store_new (void) -{ - return NULL; -} - -static inline GPtrArray * -valent_test_sms_get_messages (void) -{ - static GPtrArray *messages = NULL; - - if (messages == NULL) - { - ValentMessage *message; - GVariant *metadata; - - messages = g_ptr_array_new_with_free_func (g_object_unref); - - metadata = g_variant_new_parsed ("{'addresses': <[{'address': <'+1-234-567-8912'>},{'address': <'+1-234-567-8910'>}]>}"); - message = g_object_new (VALENT_TYPE_MESSAGE, - "box", VALENT_MESSAGE_BOX_INBOX, - "date", 1, - "id", 1, - "metadata", metadata, - "read", FALSE, - "sender", "+1-234-567-8912", - "text", "Thread 1, Message 1", - "thread-id", 1, - NULL); - g_ptr_array_add (messages, message); - - metadata = g_variant_new_parsed ("{'addresses': <[{'address': <'+1-234-567-8912'>}]>}"); - message = g_object_new (VALENT_TYPE_MESSAGE, - "box", VALENT_MESSAGE_BOX_SENT, - "date", 2, - "id", 2, - "metadata", metadata, - "read", FALSE, - "sender", NULL, - "text", "Thread 1, Message 2", - "thread-id", 1, - NULL); - g_ptr_array_add (messages, message); - - metadata = g_variant_new_parsed ("{'addresses': <[{'address': <'+1-234-567-8914'>}]>}"); - message = g_object_new (VALENT_TYPE_MESSAGE, - "box", VALENT_MESSAGE_BOX_SENT, - "date", 3, - "id", 3, - "metadata", metadata, - "read", FALSE, - "sender", NULL, - "text", "Thread 2, Message 1", - "thread-id", 2, - NULL); - g_ptr_array_add (messages, message); - } - - return g_ptr_array_ref (messages); -} - -/** - * valent_test_message_store_new: - * - * Create a new `ValentMessagesAdapter` for testing. - * - * Returns: (transfer full): a `ValentMessagesAdapter` - */ -static inline ValentMessagesAdapter * -valent_test_message_store_new (void) -{ - g_autoptr (ValentContext) context = NULL; - g_autoptr (ValentMessagesAdapter) store = NULL; - g_autoptr (GPtrArray) messages = NULL; - gboolean done = FALSE; - - /* Prepare Store */ - context = g_object_new (VALENT_TYPE_CONTEXT, - "domain", "device", - "id", "test-device", - NULL); - store = g_object_new (VALENT_TYPE_MESSAGES_ADAPTER, - "parent", context, - NULL); - messages = valent_test_sms_get_messages (); - - /* Add Messages */ - valent_messages_adapter_add_messages (store, - messages, - NULL, - (GAsyncReadyCallback)valent_test_message_store_new_cb, - &done); - valent_test_await_boolean (&done); - - return g_steal_pointer (&store); -} - -G_END_DECLS diff --git a/tests/plugins/sms/test-sms-plugin.c b/tests/plugins/sms/test-sms-plugin.c index 97c5e3804a..843bc77c70 100644 --- a/tests/plugins/sms/test-sms-plugin.c +++ b/tests/plugins/sms/test-sms-plugin.c @@ -5,7 +5,6 @@ #include #include - static void test_sms_plugin_basic (ValentTestFixture *fixture, gconstpointer user_data) @@ -14,36 +13,38 @@ test_sms_plugin_basic (ValentTestFixture *fixture, JsonNode *packet; VALENT_TEST_CHECK ("Plugin has expected actions"); - g_assert_true (g_action_group_has_action (actions, "sms.fetch")); - g_assert_true (g_action_group_has_action (actions, "sms.messaging")); + g_assert_true (g_action_group_has_action (actions, "sms.sync")); valent_test_fixture_connect (fixture, TRUE); - VALENT_TEST_CHECK ("Plugin actions are enabled when connected"); - g_assert_true (g_action_group_get_action_enabled (actions, "sms.fetch")); - g_assert_true (g_action_group_get_action_enabled (actions, "sms.messaging")); - VALENT_TEST_CHECK ("Plugin requests the threads on connect"); packet = valent_test_fixture_expect_packet (fixture); v_assert_packet_type (packet, "kdeconnect.sms.request_conversations"); json_node_unref (packet); - VALENT_TEST_CHECK ("Plugin action `sms.fetch` sends a request for the thread list"); - g_action_group_activate_action (actions, "sms.fetch", NULL); + VALENT_TEST_CHECK ("Plugin actions are enabled when connected"); + g_assert_true (g_action_group_get_action_enabled (actions, "sms.sync")); + + VALENT_TEST_CHECK ("Plugin action `sms.sync` sends a request for the thread list"); + g_action_group_activate_action (actions, "sms.sync", NULL); packet = valent_test_fixture_expect_packet (fixture); v_assert_packet_type (packet, "kdeconnect.sms.request_conversations"); json_node_unref (packet); - - VALENT_TEST_CHECK ("Plugin action `sms.messaging` opens the messaging window"); - g_action_group_activate_action (actions, "sms.messaging", NULL); } static void test_sms_plugin_handle_request (ValentTestFixture *fixture, gconstpointer user_data) { + ValentMessages *messages = valent_messages_get_default (); + g_autoptr (GListModel) adapter = NULL; + g_autoptr (GListModel) list = NULL; + g_autoptr (ValentMessage) message = NULL; JsonNode *packet; + adapter = g_list_model_get_item (G_LIST_MODEL (messages), 1); + g_assert_true (VALENT_IS_MESSAGES_ADAPTER (adapter)); + valent_test_fixture_connect (fixture, TRUE); VALENT_TEST_CHECK ("Plugin requests the threads on connect"); @@ -55,6 +56,12 @@ test_sms_plugin_handle_request (ValentTestFixture *fixture, packet = valent_test_fixture_lookup_packet (fixture, "connect-time-1"); valent_test_fixture_handle_packet (fixture, packet); + valent_test_await_signal (adapter, "items-changed"); + list = g_list_model_get_item (adapter, 0); + g_assert_true (G_IS_LIST_MODEL (list)); + g_assert_cmpuint (g_list_model_get_n_items (list), ==, 0); + valent_test_await_signal (list, "items-changed"); + VALENT_TEST_CHECK ("Plugin requests the thread (1)"); packet = valent_test_fixture_expect_packet (fixture); v_assert_packet_type (packet, "kdeconnect.sms.request_conversation"); @@ -65,10 +72,22 @@ test_sms_plugin_handle_request (ValentTestFixture *fixture, packet = valent_test_fixture_lookup_packet (fixture, "thread-1"); valent_test_fixture_handle_packet (fixture, packet); + valent_test_await_signal (list, "items-changed"); + message = g_list_model_get_item (list, 1); + g_assert_true (VALENT_IS_MESSAGE (message)); + g_clear_object (&list); + g_clear_object (&message); + VALENT_TEST_CHECK ("Plugin handles the latest thread message (2)"); packet = valent_test_fixture_lookup_packet (fixture, "connect-time-2"); valent_test_fixture_handle_packet (fixture, packet); + valent_test_await_signal (adapter, "items-changed"); + list = g_list_model_get_item (adapter, 1); + g_assert_true (G_IS_LIST_MODEL (list)); + g_assert_cmpuint (g_list_model_get_n_items (list), ==, 0); + valent_test_await_signal (list, "items-changed"); + VALENT_TEST_CHECK ("Plugin requests the thread (2)"); packet = valent_test_fixture_expect_packet (fixture); v_assert_packet_type (packet, "kdeconnect.sms.request_conversation"); @@ -78,8 +97,78 @@ test_sms_plugin_handle_request (ValentTestFixture *fixture, VALENT_TEST_CHECK ("Plugin handles the requested thread (2)"); packet = valent_test_fixture_lookup_packet (fixture, "thread-2"); valent_test_fixture_handle_packet (fixture, packet); + + valent_test_await_signal (list, "items-changed"); + message = g_list_model_get_item (list, 1); + g_assert_true (VALENT_IS_MESSAGE (message)); + g_clear_object (&list); + g_clear_object (&message); } +static void +test_sms_plugin_handle_attachment (ValentTestFixture *fixture, + gconstpointer user_data) +{ + ValentMessages *messages = valent_messages_get_default (); + g_autoptr (GListModel) adapter = NULL; + g_autoptr (GListModel) list = NULL; + g_autoptr (ValentMessage) message = NULL; + JsonNode *packet; + g_autoptr (GFile) file = NULL; + GError *error = NULL; + + adapter = g_list_model_get_item (G_LIST_MODEL (messages), 1); + g_assert_true (VALENT_IS_MESSAGES_ADAPTER (adapter)); + + valent_test_fixture_connect (fixture, TRUE); + + VALENT_TEST_CHECK ("Plugin requests the threads on connect"); + packet = valent_test_fixture_expect_packet (fixture); + v_assert_packet_type (packet, "kdeconnect.sms.request_conversations"); + json_node_unref (packet); + + VALENT_TEST_CHECK ("Plugin handles the latest thread message"); + packet = valent_test_fixture_lookup_packet (fixture, "attachment-thread-message"); + valent_test_fixture_handle_packet (fixture, packet); + + valent_test_await_signal (adapter, "items-changed"); + list = g_list_model_get_item (adapter, 0); + g_assert_true (G_IS_LIST_MODEL (list)); + g_assert_cmpuint (g_list_model_get_n_items (list), ==, 0); + valent_test_await_signal (list, "items-changed"); + + VALENT_TEST_CHECK ("Plugin requests the thread"); + packet = valent_test_fixture_expect_packet (fixture); + v_assert_packet_type (packet, "kdeconnect.sms.request_conversation"); + v_assert_packet_cmpint (packet, "threadID", ==, 42); + json_node_unref (packet); + + VALENT_TEST_CHECK ("Plugin handles the requested thread"); + packet = valent_test_fixture_lookup_packet (fixture, "attachment-thread"); + valent_test_fixture_handle_packet (fixture, packet); + + valent_test_await_signal (list, "items-changed"); + message = g_list_model_get_item (list, 0); + g_assert_true (VALENT_IS_MESSAGE (message)); + g_clear_object (&list); + g_clear_object (&message); + + VALENT_TEST_CHECK ("Plugin requests the attachment"); + packet = valent_test_fixture_expect_packet (fixture); + v_assert_packet_type (packet, "kdeconnect.sms.request_attachment"); + v_assert_packet_cmpint (packet, "part_id", ==, 190); + v_assert_packet_cmpstr (packet, "unique_identifier", ==, "image.jpg"); + json_node_unref (packet); + + packet = valent_test_fixture_lookup_packet (fixture, "attachment-thread-payload"); + file = g_file_new_for_uri ("resource:///tests/image.jpg"); + valent_test_fixture_upload (fixture, packet, file, &error); + g_assert_no_error (error); + + valent_test_await_pending (); +} + +#if 0 static const char *schemas[] = { "/tests/kdeconnect.sms.attachment_file.json", /* "/tests/kdeconnect.sms.messages.json", */ @@ -100,6 +189,7 @@ test_sms_plugin_fuzz (ValentTestFixture *fixture, for (size_t s = 0; s < G_N_ELEMENTS (schemas); s++) valent_test_fixture_schema_fuzz (fixture, schemas[s]); } +#endif int main (int argc, @@ -121,11 +211,19 @@ main (int argc, test_sms_plugin_handle_request, valent_test_fixture_clear); + g_test_add ("/plugins/sms/handle-attachment", + ValentTestFixture, path, + valent_test_fixture_init, + test_sms_plugin_handle_attachment, + valent_test_fixture_clear); + +#if 0 g_test_add ("/plugins/sms/fuzz", ValentTestFixture, path, valent_test_fixture_init, test_sms_plugin_fuzz, valent_test_fixture_clear); +#endif return g_test_run (); }