[geary/wip/713611-conversation-message-delete: 4/10] Make Geary.App.ConversationSet unit testable, add unit tests.



commit 392b0ef45202edaf4670babd43f53929cd376bd7
Author: Michael James Gratton <mike vee net>
Date:   Thu Dec 7 10:25:14 2017 +1100

    Make Geary.App.ConversationSet unit testable, add unit tests.
    
    * src/engine/app/conversation-monitor/app-conversation-set.vala
      (ConversationSet.add_all_emails_async): Replace monitor arg with a
      multiset of email paths, update call site to do the path lookup on the
      caller's side instead. Add unit tests.
      (ConversationSet.merge_conversations_async): Remove monitor arg with a
      multiset of email paths, get paths for emails to be merged from the
      conversations themselves, since they should be up to date.
      (Conversation.remove_all_emails_by_identifier): Add unit tests.

 src/engine/app/app-conversation-monitor.vala       |   17 +-
 src/engine/app/app-conversation.vala               |   13 +-
 .../conversation-monitor/app-conversation-set.vala |  115 +++--
 test/CMakeLists.txt                                |    1 +
 test/engine/app/app-conversation-set-test.vala     |  453 ++++++++++++++++++++
 test/test-engine.vala                              |    1 +
 6 files changed, 548 insertions(+), 52 deletions(-)
---
diff --git a/src/engine/app/app-conversation-monitor.vala b/src/engine/app/app-conversation-monitor.vala
index 5f4da5c..0a12f80 100644
--- a/src/engine/app/app-conversation-monitor.vala
+++ b/src/engine/app/app-conversation-monitor.vala
@@ -567,8 +567,21 @@ public class Geary.App.ConversationMonitor : BaseObject {
         Gee.MultiMap<Geary.App.Conversation, Geary.Email>? appended = null;
         Gee.Collection<Conversation>? removed_due_to_merge = null;
         try {
-            yield conversations.add_all_emails_async(job.emails.values, this, folder.path, out added, out 
appended,
-                out removed_due_to_merge, null);
+            // Get known paths for all emails
+            Gee.MultiMap<Geary.EmailIdentifier, Geary.FolderPath>? email_paths =
+                yield this.folder.account.get_containing_folders_async(
+                    job.emails.keys, null
+                );
+
+            // Add them to the conversation set
+            yield this.conversations.add_all_emails_async(
+                job.emails.values,
+                email_paths,
+                this.folder,
+                out added,
+                out appended,
+                out removed_due_to_merge,
+                null);
         } catch (Error err) {
             debug("Unable to add emails to conversation: %s", err.message);
             
diff --git a/src/engine/app/app-conversation.vala b/src/engine/app/app-conversation.vala
index 56a79d0..b298ff9 100644
--- a/src/engine/app/app-conversation.vala
+++ b/src/engine/app/app-conversation.vala
@@ -43,6 +43,13 @@ public class Geary.App.Conversation : BaseObject {
     /** Folder from which the conversation originated. */
     public Folder base_folder { get; private set; }
 
+    /** Cache of paths associated with each email */
+    internal Gee.HashMultiMap<Geary.EmailIdentifier,Geary.FolderPath> path_map {
+        get;
+        private set;
+        default = new Gee.HashMultiMap< Geary.EmailIdentifier,Geary.FolderPath>();
+    }
+
     private Gee.HashMultiSet<RFC822.MessageID> message_ids = new Gee.HashMultiSet<RFC822.MessageID>();
 
     private int convnum;
@@ -59,11 +66,7 @@ public class Geary.App.Conversation : BaseObject {
         Geary.Email.compare_recv_date_ascending);
     private Gee.SortedSet<Email> recv_date_descending = new Gee.TreeSet<Email>(
         Geary.Email.compare_recv_date_descending);
-    
-    // by storing all paths for each EmailIdentifier, can lookup without blocking
-    private Gee.HashMultiMap<Geary.EmailIdentifier, Geary.FolderPath> path_map = new Gee.HashMultiMap<
-        Geary.EmailIdentifier, Geary.FolderPath>();
-    
+
     /**
      * Fired when email has been added to this conversation.
      */
diff --git a/src/engine/app/conversation-monitor/app-conversation-set.vala 
b/src/engine/app/conversation-monitor/app-conversation-set.vala
index 16c6afc..0c279b6 100644
--- a/src/engine/app/conversation-monitor/app-conversation-set.vala
+++ b/src/engine/app/conversation-monitor/app-conversation-set.vala
@@ -84,8 +84,9 @@ private class Geary.App.ConversationSet : BaseObject {
      * NOTE: Do not call this method if get_associated_conversations() returns a Collection with
      * a size greater than one.  That indicates the Conversations *must* be merged before adding.
      */
-    private Conversation? add_email(Geary.Email email, ConversationMonitor monitor,
-        Geary.FolderPath? preferred_folder_path, Gee.Collection<Geary.FolderPath>? known_paths,
+    private Conversation? add_email(Geary.Email email,
+                                    Folder base_folder,
+                                    Gee.Collection<Geary.FolderPath>? known_paths,
         out bool added_conversation) {
         added_conversation = false;
         
@@ -100,7 +101,7 @@ private class Geary.App.ConversationSet : BaseObject {
             conversation = Collection.get_first<Conversation>(associated);
         
         if (conversation == null) {
-            conversation = new Conversation(monitor.folder);
+            conversation = new Conversation(base_folder);
             _conversations.add(conversation);
             
             added_conversation = true;
@@ -126,25 +127,38 @@ private class Geary.App.ConversationSet : BaseObject {
                 logical_message_id_map.set(ancestor, conversation);
         }
     }
-    
-    public async void add_all_emails_async(Gee.Collection<Geary.Email> emails,
-        ConversationMonitor monitor, Geary.FolderPath? preferred_folder_path,
+
+    /**
+     * Adds a set of emails to conversations in this set.
+     *
+     * This method will create and/or merge conversations as
+     * needed. The collection `emails` contains the messages to be
+     * added, and for each email in the collection, there should be an
+     * entry in `id_to_paths` that indicates the folders each message
+     * is known to belong to. The folder `base_folder` is the base
+     * folder for the conversation monitor that owns this set.
+     *
+     * The three collections returned include any conversation that
+     * were created, any that had email appended to them (and the
+     * messages that were appended), and any that were removed due to
+     * being merged into another.
+     */
+    public async void add_all_emails_async(
+        Gee.Collection<Geary.Email> emails,
+        Gee.MultiMap<Geary.EmailIdentifier, Geary.FolderPath>? id_to_paths,
+        Folder base_folder,
         out Gee.Collection<Conversation> added,
         out Gee.MultiMap<Conversation, Geary.Email> appended,
         out Gee.Collection<Conversation> removed_due_to_merge,
-        Cancellable? cancellable) throws Error {
-        // Get known paths for all emails
-        Gee.Map<Geary.EmailIdentifier, Geary.Email>? id_map = Email.emails_to_map(emails);
-        Gee.MultiMap<Geary.EmailIdentifier, Geary.FolderPath>? id_to_paths = null;
-        if (id_map != null) {
-            id_to_paths = yield monitor.folder.account.get_containing_folders_async(id_map.keys,
-                cancellable);
-        }
-        
-        Gee.HashSet<Conversation> _added = new Gee.HashSet<Conversation>();
-        Gee.HashMultiMap<Conversation, Geary.Email> _appended
-            = new Gee.HashMultiMap<Conversation, Geary.Email>();
-        Gee.HashSet<Conversation> _removed_due_to_merge = new Gee.HashSet<Conversation>();
+        Cancellable? cancellable)
+    throws Error {
+        Gee.HashSet<Conversation> _added =
+            new Gee.HashSet<Conversation>();
+        Gee.HashMultiMap<Conversation, Geary.Email> _appended =
+            new Gee.HashMultiMap<Conversation, Geary.Email>();
+        Gee.HashSet<Conversation> _removed_due_to_merge =
+            new Gee.HashSet<Conversation>();
+
         foreach (Geary.Email email in emails) {
             Gee.Set<Conversation> associated = get_associated_conversations(email);
             if (associated.size > 1) {
@@ -157,14 +171,15 @@ private class Geary.App.ConversationSet : BaseObject {
                 // By doing this first, it prevents ConversationSet getting itself into a bad state
                 // where more than one Conversation thinks it "owns" a Message-ID
                 debug("Merging %d conversations due new email associating with all...", associated.size);
-                
+
                 // Note that this call will modify the List so it only holds the to-be-axed
                 // Conversations
                 Gee.Set<Geary.Email> moved_email = new Gee.HashSet<Geary.Email>();
-                Conversation dest = yield merge_conversations_async(monitor, associated, moved_email,
-                    cancellable);
+                Conversation dest = yield merge_conversations_async(
+                    associated, moved_email, cancellable
+                );
                 assert(!associated.contains(dest));
-                
+
                 // remove the remaining conversations from the added/appended Collections
                 _added.remove_all(associated);
                 foreach (Conversation removed_conversation in associated)
@@ -182,13 +197,15 @@ private class Geary.App.ConversationSet : BaseObject {
                 // Nasty ol' Email won't cause problems now -- but let's check anyway!
                 assert(get_associated_conversations(email).size <= 1);
             }
-            
+
             bool added_conversation;
             Conversation? conversation = add_email(
-                email, monitor, preferred_folder_path,
+                email,
+                base_folder,
                 (id_to_paths != null) ? id_to_paths.get(email.id) : null,
-                out added_conversation);
-            
+                out added_conversation
+            );
+
             if (conversation == null)
                 continue;
             
@@ -199,19 +216,21 @@ private class Geary.App.ConversationSet : BaseObject {
                     _appended.set(conversation, email);
             }
         }
-        
+
         added = _added;
         appended = _appended;
         removed_due_to_merge = _removed_due_to_merge;
     }
-    
+
     // This method will remove the destination (merged) Conversation from the List and return it
     // as the result, along with a Collection of email that must be merged into it
-    private async Conversation merge_conversations_async(ConversationMonitor monitor,
-        Gee.Set<Conversation> conversations, Gee.Set<Geary.Email> moved_email,
-        Cancellable? cancellable) throws Error {
+    private async Conversation
+        merge_conversations_async(Gee.Set<Conversation> conversations,
+                                  Gee.Set<Geary.Email> moved_email,
+                                  Cancellable? cancellable)
+        throws Error {
         assert(conversations.size > 0);
-        
+
         // find the largest conversation and merge the others into it
         Conversation? dest = null;
         foreach (Conversation conversation in conversations) {
@@ -223,10 +242,19 @@ private class Geary.App.ConversationSet : BaseObject {
         // conversations merged into it
         bool removed = conversations.remove(dest);
         assert(removed);
-        
-        foreach (Conversation conversation in conversations)
-            moved_email.add_all(conversation.get_emails(Conversation.Ordering.NONE));
-        
+
+        Gee.MultiMap<Geary.EmailIdentifier,Geary.FolderPath>? id_to_paths =
+            new Gee.HashMultiMap<Geary.EmailIdentifier,Geary.FolderPath>();
+
+        foreach (Conversation conversation in conversations) {
+            foreach (EmailIdentifier id in conversation.path_map.get_keys()) {
+                moved_email.add(conversation.get_email_by_id(id));
+                foreach (FolderPath path in conversation.path_map.get(id)) {
+                    id_to_paths.set(id, path);
+                }
+            }
+        }
+
         // convert total sum of Emails to move into map of ID -> Email
         Gee.Map<Geary.EmailIdentifier, Geary.Email>? id_map = Geary.Email.emails_to_map(moved_email);
         // there better be some Email here, otherwise things are really hosed
@@ -243,15 +271,12 @@ private class Geary.App.ConversationSet : BaseObject {
         assert(removed_conversations.size == conversations.size);
         foreach (Conversation conversation in conversations)
             assert(removed_conversations.contains(conversation));
-        
-        // Get known paths for all emails being moved
-        Gee.MultiMap<Geary.EmailIdentifier, Geary.FolderPath>? id_to_paths =
-            yield monitor.folder.account.get_containing_folders_async(id_map.keys, cancellable);
-        
+
         // now add all that email back to the destination Conversation
-        foreach (Geary.Email moved in moved_email)
-            add_email_to_conversation(dest, moved, (id_to_paths != null) ? id_to_paths.get(moved.id) : null);
-        
+        foreach (Geary.Email moved in moved_email) {
+            add_email_to_conversation(dest, moved, id_to_paths.get(moved.id));
+        }
+
         return dest;
     }
     
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 77ae73d..0d8d33a 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -12,6 +12,7 @@ set(TEST_ENGINE_SRC
   engine/api/geary-folder-test.vala
   engine/api/geary-folder-path-test.vala
   engine/app/app-conversation-test.vala
+  engine/app/app-conversation-set-test.vala
   engine/imap/command/imap-create-command-test.vala
   engine/imap/response/imap-namespace-response-test.vala
   engine/imap/transport/imap-deserializer-test.vala
diff --git a/test/engine/app/app-conversation-set-test.vala b/test/engine/app/app-conversation-set-test.vala
new file mode 100644
index 0000000..792dcf9
--- /dev/null
+++ b/test/engine/app/app-conversation-set-test.vala
@@ -0,0 +1,453 @@
+/*
+ * Copyright 2017 Michael Gratton <mike vee net>
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+class Geary.App.ConversationSetTest : Gee.TestCase {
+
+
+    ConversationSet? test = null;
+    Folder? base_folder = null;
+
+    public ConversationSetTest() {
+        base("Geary.App.ConversationSetTest");
+        add_test("add_all_basic", add_all_basic);
+        add_test("add_all_duplicate", add_all_duplicate);
+        add_test("add_all_append_descendants", add_all_append_descendants);
+        add_test("add_all_append_ancestors", add_all_append_ancestors);
+        add_test("add_all_merge", add_all_merge);
+        add_test("remove_all_removed", remove_all_removed);
+        add_test("remove_all_trimmed", remove_all_trimmed);
+    }
+
+    public override void set_up() {
+        this.test = new ConversationSet();
+        this.base_folder = new MockFolder(
+            null,
+            null,
+            new MockFolderRoot("test"),
+            SpecialFolderType.NONE,
+            null
+        );
+    }
+
+    public void add_all_basic() {
+        Email e1 = new Email(new MockEmailIdentifer(1));
+        Email e2 = new Email(new MockEmailIdentifer(2));
+
+        Gee.LinkedList<Email> email = new Gee.LinkedList<Email>();
+        email.add(e1);
+        email.add(e2);
+
+        Gee.MultiMap<Geary.EmailIdentifier, Geary.FolderPath> email_paths =
+            new Gee.HashMultiMap<Geary.EmailIdentifier, Geary.FolderPath>();
+        email_paths.set(e1.id, this.base_folder.path);
+        email_paths.set(e2.id, this.base_folder.path);
+
+        Gee.Collection<Conversation>? added = null;
+        Gee.MultiMap<Conversation,Email>? appended = null;
+        Gee.Collection<Conversation>? removed = null;
+
+        this.test.add_all_emails_async.begin(
+            email,
+            email_paths,
+            this.base_folder,
+            null,
+            (obj, ret) => { async_complete(ret); }
+        );
+        try {
+            this.test.add_all_emails_async.end(
+                async_result(), out added, out appended, out removed
+            );
+        } catch (Error error) {
+            assert_not_reached();
+        }
+
+        assert(this.test.size == 2);
+        assert(this.test.get_email_count() == 2);
+
+        assert(added != null);
+        assert(appended != null);
+        assert(removed != null);
+
+        // Work out which collection in the collection corresponds to which
+        assert(added.size == 2);
+        Conversation? c1 = null;
+        Conversation? c2 = null;
+        foreach (Conversation convo in added) {
+            if (convo.get_email_by_id(e1.id) != null) {
+                c1 = convo;
+            } else if (convo.get_email_by_id(e2.id) != null) {
+                c2 = convo;
+            }
+        }
+        assert(c1 != null);
+        assert(c2 != null);
+
+        assert(appended.size == 0);
+        assert(removed.is_empty);
+    }
+
+    public void add_all_duplicate() {
+        Email e1 = setup_email(1);
+
+        Gee.LinkedList<Email> email = new Gee.LinkedList<Email>();
+        email.add(e1);
+
+        Gee.MultiMap<Geary.EmailIdentifier, Geary.FolderPath> email_paths =
+            new Gee.HashMultiMap<Geary.EmailIdentifier, Geary.FolderPath>();
+        email_paths.set(e1.id, this.base_folder.path);
+
+        // Pass 1: Duplicate in same input collection
+
+        Gee.Collection<Conversation>? added = null;
+        Gee.MultiMap<Conversation,Email>? appended = null;
+        Gee.Collection<Conversation>? removed = null;
+        this.test.add_all_emails_async.begin(
+            email,
+            email_paths,
+            this.base_folder,
+            null,
+            (obj, ret) => { async_complete(ret); }
+        );
+        try {
+            this.test.add_all_emails_async.end(
+                async_result(), out added, out appended, out removed
+            );
+        } catch (Error error) {
+            assert_not_reached();
+        }
+
+        assert(this.test.size == 1);
+        assert(this.test.get_email_count() == 1);
+
+        assert(added.size == 1);
+        assert(Geary.Collection.get_first(added).get_email_by_id(e1.id) == e1);
+
+        assert(appended.size == 0);
+        assert(removed.is_empty);
+
+        // Pass 2: Duplicate being re-added
+
+        added = null;
+        appended = null;
+        removed = null;
+        this.test.add_all_emails_async.begin(
+            email,
+            email_paths,
+            this.base_folder,
+            null,
+            (obj, ret) => { async_complete(ret); }
+        );
+        try {
+            this.test.add_all_emails_async.end(
+                async_result(), out added, out appended, out removed
+            );
+        } catch (Error error) {
+            assert_not_reached();
+        }
+
+        assert(this.test.size == 1);
+        assert(this.test.get_email_count() == 1);
+
+        assert(added.is_empty);
+        assert(appended.size == 0);
+        assert(removed.is_empty);
+    }
+
+    public void add_all_append_descendants() {
+        Email e1 = setup_email(1);
+        Email e2 = setup_email(2, e1);
+
+        // Pass 1: Append two at the same time
+
+        Gee.LinkedList<Email> emails = new Gee.LinkedList<Email>();
+        emails.add(e1);
+        emails.add(e2);
+
+        Gee.MultiMap<Geary.EmailIdentifier, Geary.FolderPath> email_paths =
+            new Gee.HashMultiMap<Geary.EmailIdentifier, Geary.FolderPath>();
+        email_paths.set(e1.id, this.base_folder.path);
+        email_paths.set(e2.id, new MockFolderRoot("other"));
+
+        Gee.Collection<Conversation>? added = null;
+        Gee.MultiMap<Conversation,Email>? appended = null;
+        Gee.Collection<Conversation>? removed = null;
+
+        this.test.add_all_emails_async.begin(
+            emails,
+            email_paths,
+            this.base_folder,
+            null,
+            (obj, ret) => { async_complete(ret); }
+        );
+        try {
+            this.test.add_all_emails_async.end(
+                async_result(), out added, out appended, out removed
+            );
+        } catch (Error error) {
+            assert_not_reached();
+        }
+
+        assert(this.test.size == 1);
+        assert(this.test.get_email_count() == 2);
+
+        assert(added.size == 1);
+
+        Conversation convo1 = Geary.Collection.get_first(added);
+        assert(convo1.get_email_by_id(e1.id) == e1);
+        assert(convo1.get_email_by_id(e2.id) == e2);
+
+        assert(appended.size == 0);
+        assert(removed.is_empty);
+
+        // Pass 2: Append one to an existing convo
+
+        Email e3 = setup_email(3, e1);
+
+        emails.clear();
+        email_paths.clear();
+
+        emails.add(e3);
+        email_paths.set(e3.id, this.base_folder.path);
+
+        added = null;
+        appended = null;
+        removed = null;
+        this.test.add_all_emails_async.begin(
+            emails,
+            email_paths,
+            this.base_folder,
+            null,
+            (obj, ret) => { async_complete(ret); }
+        );
+        try {
+            this.test.add_all_emails_async.end(
+                async_result(), out added, out appended, out removed
+            );
+        } catch (Error error) {
+            assert_not_reached();
+        }
+
+        assert(this.test.size == 1);
+        assert(this.test.get_email_count() == 3);
+
+        assert(added.is_empty);
+
+        assert(appended.size == 1);
+        Conversation convo2 = Geary.Collection.get_first(appended.get_keys());
+        assert(convo2.get_email_by_id(e1.id) == e1);
+        assert(convo2.get_email_by_id(e2.id) == e2);
+        assert(convo2.get_email_by_id(e3.id) == e3);
+
+        assert(appended.contains(convo2) == true);
+        assert(appended.get(convo2).contains(e1) != true);
+        assert(appended.get(convo2).contains(e2) != true);
+        assert(appended.get(convo2).contains(e3) == true);
+
+        assert(removed.is_empty);
+    }
+
+    public void add_all_append_ancestors() {
+        Email e1 = setup_email(1);
+        Email e2 = setup_email(2, e1);
+
+        add_email_to_test_set(e2);
+
+        Gee.LinkedList<Email> emails = new Gee.LinkedList<Email>();
+        emails.add(e1);
+
+        Gee.MultiMap<Geary.EmailIdentifier, Geary.FolderPath> email_paths =
+            new Gee.HashMultiMap<Geary.EmailIdentifier, Geary.FolderPath>();
+        email_paths.set(e1.id, this.base_folder.path);
+
+        Gee.Collection<Conversation>? added = null;
+        Gee.MultiMap<Conversation,Email>? appended = null;
+        Gee.Collection<Conversation>? removed = null;
+        this.test.add_all_emails_async.begin(
+            emails,
+            email_paths,
+            this.base_folder,
+            null,
+            (obj, ret) => { async_complete(ret); }
+        );
+        try {
+            this.test.add_all_emails_async.end(
+                async_result(), out added, out appended, out removed
+            );
+        } catch (Error error) {
+            assert_not_reached();
+        }
+
+        assert(this.test.size == 1);
+        assert(this.test.get_email_count() == 2);
+
+        assert(added.is_empty);
+
+        assert(appended.size == 1);
+        Conversation convo = Geary.Collection.get_first(appended.get_keys());
+        assert(convo.get_email_by_id(e1.id) == e1);
+        assert(convo.get_email_by_id(e2.id) == e2);
+
+        assert(appended.contains(convo) == true);
+        assert(appended.get(convo).contains(e1) == true);
+
+        assert(removed.is_empty);
+    }
+
+    public void add_all_merge() {
+        Email e1 = setup_email(1);
+        add_email_to_test_set(e1);
+
+        Email e2 = setup_email(2, e1);
+
+        Email e3 = setup_email(3, e2);
+        add_email_to_test_set(e3);
+
+        assert(this.test.size == 2);
+        assert(this.test.get_email_count() == 2);
+
+        Gee.LinkedList<Email> emails = new Gee.LinkedList<Email>();
+        emails.add(e2);
+
+        Gee.MultiMap<Geary.EmailIdentifier, Geary.FolderPath> email_paths =
+            new Gee.HashMultiMap<Geary.EmailIdentifier, Geary.FolderPath>();
+        email_paths.set(e2.id, this.base_folder.path);
+
+        Gee.Collection<Conversation>? added = null;
+        Gee.MultiMap<Conversation,Email>? appended = null;
+        Gee.Collection<Conversation>? removed = null;
+        this.test.add_all_emails_async.begin(
+            emails,
+            email_paths,
+            this.base_folder,
+            null,
+            (obj, ret) => { async_complete(ret); }
+        );
+        try {
+            this.test.add_all_emails_async.end(
+                async_result(), out added, out appended, out removed
+            );
+        } catch (Error error) {
+            assert_not_reached();
+        }
+
+        assert(this.test.size == 1);
+        assert(this.test.get_email_count() == 3);
+
+        Conversation convo = this.test.get_by_email_identifier(e1.id);
+        assert(convo.get_email_by_id(e1.id) == e1);
+        assert(convo.get_email_by_id(e2.id) == e2);
+        assert(convo.get_email_by_id(e3.id) == e3);
+
+        assert(appended.size == 2);
+        assert(appended.get(convo) != null);
+        assert(appended.get(convo).contains(e2) == true);
+        assert(appended.get(convo).contains(e3) == true);
+
+        assert(added.is_empty);
+        assert(removed.size == 1);
+    }
+
+    public void remove_all_removed() {
+        Email e1 = setup_email(1);
+        add_email_to_test_set(e1);
+
+        Conversation convo = this.test.get_by_email_identifier(e1.id);
+
+        Gee.LinkedList<EmailIdentifier> ids =
+            new Gee.LinkedList<EmailIdentifier>();
+        ids.add(e1.id);
+
+        Gee.Collection<Conversation>? removed = null;
+        Gee.MultiMap<Conversation,Email>? trimmed = null;
+        this.test.remove_all_emails_by_identifier(
+            ids, out removed, out trimmed
+        );
+
+        assert(this.test.size == 0);
+        assert(this.test.get_email_count() == 0);
+
+        assert(removed != null);
+        assert(trimmed != null);
+
+        assert(removed.contains(convo) == true);
+        assert(trimmed.size == 0);
+    }
+
+    public void remove_all_trimmed() {
+        Email e1 = setup_email(1);
+        add_email_to_test_set(e1);
+
+        Email e2 = setup_email(2, e1);
+        add_email_to_test_set(e2);
+
+        Conversation convo = this.test.get_by_email_identifier(e1.id);
+
+        Gee.LinkedList<EmailIdentifier> ids =
+            new Gee.LinkedList<EmailIdentifier>();
+        ids.add(e1.id);
+
+        Gee.Collection<Conversation>? removed = null;
+        Gee.MultiMap<Conversation,Email>? trimmed = null;
+        this.test.remove_all_emails_by_identifier(
+            ids, out removed, out trimmed
+        );
+
+        assert(this.test.size == 1);
+        assert(this.test.get_email_count() == 1);
+
+        assert(removed != null);
+        assert(trimmed != null);
+
+        assert(removed.is_empty == true);
+        assert(trimmed.contains(convo) == true);
+        assert(trimmed.get(convo).contains(e1) == true);
+    }
+
+    private Email setup_email(int id, Email? references = null) {
+        Email email = new Email(new MockEmailIdentifer(id));
+        Geary.RFC822.MessageID mid = new Geary.RFC822.MessageID(
+            "test%d@localhost".printf(id)
+        );
+
+        Geary.RFC822.MessageIDList refs_list = null;
+        if (references != null) {
+            refs_list = new Geary.RFC822.MessageIDList.single(
+                references.message_id
+            );
+        }
+        email.set_full_references(mid, null, refs_list);
+        return email;
+    }
+
+    private void add_email_to_test_set(Email to_add) {
+        Gee.LinkedList<Email> emails = new Gee.LinkedList<Email>();
+        emails.add(to_add);
+
+        Gee.MultiMap<Geary.EmailIdentifier, Geary.FolderPath> email_paths =
+            new Gee.HashMultiMap<Geary.EmailIdentifier, Geary.FolderPath>();
+        email_paths.set(to_add.id, this.base_folder.path);
+
+        Gee.Collection<Conversation>? added = null;
+        Gee.MultiMap<Conversation,Email>? appended = null;
+        Gee.Collection<Conversation>? removed = null;
+        this.test.add_all_emails_async.begin(
+            emails,
+            email_paths,
+            this.base_folder,
+            null,
+            (obj, ret) => { async_complete(ret); }
+        );
+        try {
+            this.test.add_all_emails_async.end(
+                async_result(), out added, out appended, out removed
+            );
+        } catch (Error error) {
+            assert_not_reached();
+        }
+    }
+
+}
diff --git a/test/test-engine.vala b/test/test-engine.vala
index 28122f8..63d56e2 100644
--- a/test/test-engine.vala
+++ b/test/test-engine.vala
@@ -27,6 +27,7 @@ int main(string[] args) {
     engine.add_suite(new Geary.IdleManagerTest().get_suite());
     engine.add_suite(new Geary.TimeoutManagerTest().get_suite());
     engine.add_suite(new Geary.App.ConversationTest().get_suite());
+    engine.add_suite(new Geary.App.ConversationSetTest().get_suite());
     engine.add_suite(new Geary.HTML.UtilTest().get_suite());
     engine.add_suite(new Geary.Imap.DeserializerTest().get_suite());
     engine.add_suite(new Geary.Imap.CreateCommandTest().get_suite());


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]