[geary/mjog/invert-folder-class-hierarchy: 20/72] client, engine: Update call sites to deal with new Folder API




commit d0fc6c6f7573845b5acb938b29970e6000236c65
Author: Michael Gratton <mike vee net>
Date:   Sat Sep 12 17:45:58 2020 +1000

    client, engine: Update call sites to deal with new Folder API

 po/POTFILES.in                                     |   2 -
 .../application/application-account-context.vala   |   2 +-
 src/client/application/application-controller.vala | 118 +++-----------
 .../application/application-main-window.vala       |   3 -
 .../api/geary-aggregated-folder-properties.vala    |  54 -------
 src/engine/api/geary-folder-properties.vala        |   2 +-
 src/engine/app/app-conversation-monitor.vala       | 147 +++++------------
 src/engine/app/app-draft-manager.vala              |  53 +-----
 src/engine/app/app-email-store.vala                |  54 +------
 .../app-fill-window-operation.vala                 |   4 +-
 .../imap-engine-account-synchronizer.vala          |  99 +++---------
 .../imap-engine/imap-engine-generic-account.vala   |  12 +-
 .../imap-engine/imap-engine-revokable-move.vala    |  29 +---
 .../replay-ops/imap-engine-user-close.vala         |  45 ------
 src/engine/meson.build                             |   2 -
 src/engine/smtp/smtp-client-service.vala           |  41 +----
 test/engine/app/app-conversation-monitor-test.vala | 179 ++++++++++++---------
 test/meson.build                                   |   1 +
 test/mock/mock-remote-folder.vala                  | 147 +++++++++++++++++
 19 files changed, 357 insertions(+), 637 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index c0710e59d..9ac3950de 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -150,7 +150,6 @@ src/console/main.vala
 src/engine/api/geary.vala
 src/engine/api/geary-account-information.vala
 src/engine/api/geary-account.vala
-src/engine/api/geary-aggregated-folder-properties.vala
 src/engine/api/geary-attachment.vala
 src/engine/api/geary-base-object.vala
 src/engine/api/geary-client-service.vala
@@ -312,7 +311,6 @@ src/engine/imap-engine/replay-ops/imap-engine-replay-append.vala
 src/engine/imap-engine/replay-ops/imap-engine-replay-removal.vala
 src/engine/imap-engine/replay-ops/imap-engine-replay-update.vala
 src/engine/imap-engine/replay-ops/imap-engine-server-search-email.vala
-src/engine/imap-engine/replay-ops/imap-engine-user-close.vala
 src/engine/imap-engine/yahoo/imap-engine-yahoo-account.vala
 src/engine/imap-engine/yahoo/imap-engine-yahoo-folder.vala
 src/engine/imap/message/imap-data-format.vala
diff --git a/src/client/application/application-account-context.vala 
b/src/client/application/application-account-context.vala
index d24b1e672..5b1b719a5 100644
--- a/src/client/application/application-account-context.vala
+++ b/src/client/application/application-account-context.vala
@@ -15,7 +15,7 @@ public class Application.AccountContext : Geary.BaseObject {
     public Geary.Account account { get; private set; }
 
     /** The account's Inbox folder */
-    public Geary.Folder? inbox = null;
+    public Geary.RemoteFolder? inbox = null;
 
     /** The account's search folder */
     public Geary.App.SearchFolder search = null;
diff --git a/src/client/application/application-controller.vala 
b/src/client/application/application-controller.vala
index e28da2f6d..ccb8cf2b1 100644
--- a/src/client/application/application-controller.vala
+++ b/src/client/application/application-controller.vala
@@ -1101,13 +1101,9 @@ internal class Application.Controller :
             context.cancellable.cancel();
 
             // Explicitly close the inbox since we explicitly open it
-            Geary.Folder? inbox = context.inbox;
+            Geary.RemoteFolder? inbox = context.inbox;
             if (inbox != null) {
-                try {
-                    yield inbox.close_async(null);
-                } catch (Error close_inbox_err) {
-                    debug("Unable to close monitored inbox: %s", close_inbox_err.message);
-                }
+                inbox.stop_monitoring();
                 context.inbox = null;
             }
 
@@ -1366,12 +1362,11 @@ internal class Application.Controller :
             foreach (var folder in available) {
                 if (Controller.should_add_folder(available, folder)) {
                     if (folder.used_as == INBOX) {
-                        if (account_context.inbox == null) {
-                            account_context.inbox = folder;
+                        if (account_context.inbox != null) {
+                            account_context.inbox.stop_monitoring();
                         }
-                        folder.open_async.begin(
-                            NO_DELAY, account_context.cancellable
-                        );
+                        account_context.inbox = folder as Geary.RemoteFolder;
+                        account_context.inbox.start_monitoring();
                     }
 
                     var folder_context = new FolderContext(folder);
@@ -2136,26 +2131,11 @@ private class Application.MoveEmailCommand : RevokableCommand {
     protected override async Geary.Revokable
         execute_impl(GLib.Cancellable cancellable)
         throws GLib.Error {
-        bool open = false;
-        try {
-            yield this.source.open_async(
-                Geary.Folder.OpenFlags.NO_DELAY, cancellable
-            );
-            open = true;
-            return yield this.source.move_email_async(
-                this.email,
-                this.destination.path,
-                cancellable
-            );
-        } finally {
-            if (open) {
-                try {
-                    yield this.source.close_async(null);
-                } catch (GLib.Error err) {
-                    // ignored
-                }
-            }
-        }
+        return yield this.source.move_email_async(
+            this.email,
+            this.destination.path,
+            cancellable
+        );
     }
 
 }
@@ -2231,24 +2211,9 @@ private class Application.ArchiveEmailCommand : RevokableCommand {
     protected override async Geary.Revokable
         execute_impl(GLib.Cancellable cancellable)
         throws GLib.Error {
-        bool open = false;
-        try {
-            yield this.source.open_async(
-                Geary.Folder.OpenFlags.NO_DELAY, cancellable
-            );
-            open = true;
-            return yield this.source.archive_email_async(
-                this.email, cancellable
-            );
-        } finally {
-            if (open) {
-                try {
-                    yield this.source.close_async(null);
-                } catch (GLib.Error err) {
-                    // ignored
-                }
-            }
-        }
+        return yield this.source.archive_email_async(
+            this.email, cancellable
+        );
     }
 
 }
@@ -2282,24 +2247,9 @@ private class Application.CopyEmailCommand : EmailCommand {
 
     public override async void execute(GLib.Cancellable? cancellable)
         throws GLib.Error {
-        bool open = false;
-        try {
-            yield this.source.open_async(
-                Geary.Folder.OpenFlags.NO_DELAY, cancellable
-            );
-            open = true;
-            yield this.source.copy_email_async(
-                this.email, this.destination.path, cancellable
-            );
-        } finally {
-            if (open) {
-                try {
-                    yield this.source.close_async(null);
-                } catch (GLib.Error err) {
-                    // ignored
-                }
-            }
-        }
+        yield this.source.copy_email_async(
+            this.email, this.destination.path, cancellable
+        );
     }
 
     public override async void undo(GLib.Cancellable? cancellable)
@@ -2356,22 +2306,7 @@ private class Application.DeleteEmailCommand : EmailCommand {
 
     public override async void execute(GLib.Cancellable? cancellable)
         throws GLib.Error {
-        bool open = false;
-        try {
-            yield this.target.open_async(
-                Geary.Folder.OpenFlags.NO_DELAY, cancellable
-            );
-            open = true;
-            yield this.target.remove_email_async(this.email, cancellable);
-        } finally {
-            if (open) {
-                try {
-                    yield this.target.close_async(null);
-                } catch (GLib.Error err) {
-                    // ignored
-                }
-            }
-        }
+        yield this.target.remove_email_async(this.email, cancellable);
     }
 
     public override async void undo(GLib.Cancellable? cancellable)
@@ -2401,22 +2336,7 @@ private class Application.EmptyFolderCommand : Command {
 
     public override async void execute(GLib.Cancellable? cancellable)
         throws GLib.Error {
-        bool open = false;
-        try {
-            yield this.target.open_async(
-                Geary.Folder.OpenFlags.NO_DELAY, cancellable
-            );
-            open = true;
-            yield this.target.empty_folder_async(cancellable);
-        } finally {
-            if (open) {
-                try {
-                    yield this.target.close_async(null);
-                } catch (GLib.Error err) {
-                    // ignored
-                }
-            }
-        }
+        yield this.target.empty_folder_async(cancellable);
     }
 
     public override async void undo(GLib.Cancellable? cancellable)
diff --git a/src/client/application/application-main-window.vala 
b/src/client/application/application-main-window.vala
index 0e1353260..5c8af4b32 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -719,7 +719,6 @@ public class Application.MainWindow :
                     this.selected_folder, true
                 );
 
-                this.progress_monitor.remove(this.selected_folder.opening_monitor);
                 this.selected_folder.properties.notify.disconnect(update_headerbar);
                 this.selected_folder = null;
             }
@@ -774,7 +773,6 @@ public class Application.MainWindow :
             // loading conversations.
 
             if (to_select != null) {
-                this.progress_monitor.add(to_select.opening_monitor);
                 to_select.properties.notify.connect(update_headerbar);
 
                 this.conversations = new Geary.App.ConversationMonitor(
@@ -1605,7 +1603,6 @@ public class Application.MainWindow :
         to_open.conversations_removed.connect(on_conversation_count_changed);
 
         to_open.start_monitoring.begin(
-            NO_DELAY,
             cancellable,
             (obj, res) => {
                 try {
diff --git a/src/engine/api/geary-folder-properties.vala b/src/engine/api/geary-folder-properties.vala
index 9015f37a1..d4bfbdace 100644
--- a/src/engine/api/geary-folder-properties.vala
+++ b/src/engine/api/geary-folder-properties.vala
@@ -39,7 +39,7 @@ public abstract class Geary.FolderProperties : BaseObject {
     public Trillian supports_children { get; protected set; }
 
     /**
-     * Returns a {@link Trillian} indicating if {@link Folder.open_async} can succeed remotely.
+     * Returns a {@link Trillian} indicating if a folder can be opened remotely.
      */
     public Trillian is_openable { get; protected set; }
 
diff --git a/src/engine/app/app-conversation-monitor.vala b/src/engine/app/app-conversation-monitor.vala
index 98d1a1a05..796b95fd9 100644
--- a/src/engine/app/app-conversation-monitor.vala
+++ b/src/engine/app/app-conversation-monitor.vala
@@ -303,12 +303,8 @@ public class Geary.App.ConversationMonitor : BaseObject, Logging.Source {
      * folder, but not subsequently when scanning for new messages. To
      * cancel any such operations, simply close the monitor via {@link
      * stop_monitoring}.
-     *
-     * @param open_flags See {@link Geary.Folder}
-     * @param cancellable Passed to the folder open operation
      */
-    public async bool start_monitoring(Folder.OpenFlags open_flags,
-                                       GLib.Cancellable? cancellable)
+    public async bool start_monitoring(GLib.Cancellable? cancellable)
         throws GLib.Error {
         if (this.is_monitoring)
             return false;
@@ -320,7 +316,6 @@ public class Geary.App.ConversationMonitor : BaseObject, Logging.Source {
         this.base_folder.email_appended.connect(on_folder_email_appended);
         this.base_folder.email_inserted.connect(on_folder_email_inserted);
         this.base_folder.email_removed.connect(on_folder_email_removed);
-        this.base_folder.opened.connect(on_folder_opened);
         this.base_folder.account.email_appended_to_folder.connect(on_account_email_appended);
         this.base_folder.account.email_inserted_into_folder.connect(on_account_email_inserted);
         this.base_folder.account.email_removed_from_folder.connect(on_account_email_removed);
@@ -333,31 +328,15 @@ public class Geary.App.ConversationMonitor : BaseObject, Logging.Source {
         // monitor is closed while it is opening, the folder open is
         // also cancelled
         GLib.Cancellable opening = new GLib.Cancellable();
+        this.operation_cancellable.cancelled.connect(() => opening.cancel());
         if (cancellable != null) {
             cancellable.cancelled.connect(() => opening.cancel());
         }
-        this.operation_cancellable.cancelled.connect(() => opening.cancel());
 
-        try {
-            yield this.base_folder.open_async(open_flags, opening);
+        var remote = this.base_folder as RemoteFolder;
+        if (remote != null && !remote.is_monitoring) {
+            remote.start_monitoring();
             this.base_was_opened = true;
-        } catch (GLib.Error err) {
-            // This check is needed since ::stop_monitoring may have
-            // been called while this call is waiting for the folder
-            // to finish opening. If so, we're already disconnected
-            // and don't need to do it again.
-            if (this.is_monitoring) {
-                try {
-                    yield stop_monitoring_internal(null);
-                } catch (GLib.Error stop_error) {
-                    warning(
-                        "Error cleaning up after folder open error: %s", err.message
-                    );
-                }
-            }
-
-            this.is_monitoring = false;
-            throw err;
         }
 
         // Now the folder is open, start the queue running. The here
@@ -372,12 +351,12 @@ public class Geary.App.ConversationMonitor : BaseObject, Logging.Source {
     /**
      * Stops monitoring for new messages and closes the base folder.
      *
-     * Returns a result code that is semantically identical to the
-     * result of {@link Geary.Folder.close_async}.
-     *
      * The //cancellable// parameter will be used when waiting for
      * internal monitor operations to complete, but will not prevent
      * attempts to close the base folder.
+     *
+     * Returns true if the monitor was actively monitoring, else
+     * false.
      */
     public async bool stop_monitoring(GLib.Cancellable? cancellable)
         throws GLib.Error {
@@ -385,7 +364,8 @@ public class Geary.App.ConversationMonitor : BaseObject, Logging.Source {
         if (this.is_monitoring) {
             // Set now to prevent reentrancy during yield or signal
             this.is_monitoring = false;
-            is_closing = yield stop_monitoring_internal(cancellable);
+            yield stop_monitoring_internal(cancellable);
+            is_closing = true;
         }
         return is_closing;
     }
@@ -551,56 +531,36 @@ public class Geary.App.ConversationMonitor : BaseObject, Logging.Source {
     internal async void external_load_by_sparse_id(Folder folder,
                                                    Gee.Collection<EmailIdentifier> ids,
                                                    Folder.ListFlags flags)
-        throws Error {
-        bool opened = false;
-
-        Gee.List<Geary.Email>? emails = null;
-        try {
-            yield folder.open_async(
-                Geary.Folder.OpenFlags.NONE, this.operation_cancellable
-            );
-            opened = true;
-
-            // First just get the bare minimum we need to determine if we even
-            // care about the messages.
-            emails = yield folder.list_email_by_sparse_id_async(
+        throws GLib.Error {
+        // First just get the bare minimum we need to determine if we even
+        // care about the messages.
+        Gee.List<Geary.Email>? emails =
+            yield folder.list_email_by_sparse_id_async(
                 ids, Geary.Email.Field.REFERENCES, flags, this.operation_cancellable
             );
-            if (emails != null) {
-                Gee.HashSet<Geary.EmailIdentifier> relevant_ids =
-                    new Gee.HashSet<Geary.EmailIdentifier>();
-                foreach (Geary.Email email in emails) {
-                    Gee.Set<RFC822.MessageID>? ancestors = email.get_ancestors();
-                    if (ancestors != null &&
-                        Geary.traverse<RFC822.MessageID>(ancestors).any(id => 
conversations.has_message_id(id)))
-                        relevant_ids.add(email.id);
-                }
-
-                // List the relevant messages again with the full set of fields, to
-                // make sure when we load them from the database we have all the
-                // data we need.
-                if (!relevant_ids.is_empty) {
-                    emails = yield folder.list_email_by_sparse_id_async(
-                        relevant_ids, required_fields, flags, this.operation_cancellable
-                    );
-                } else {
-                    emails = null;
+        if (emails != null) {
+            var relevant_ids = new Gee.HashSet<Geary.EmailIdentifier>();
+            foreach (var email in emails) {
+                Gee.Set<RFC822.MessageID>? ancestors = email.get_ancestors();
+                if (ancestors != null &&
+                    Geary.traverse<RFC822.MessageID>(ancestors).any(id => conversations.has_message_id(id))) 
{
+                    relevant_ids.add(email.id);
                 }
             }
 
-            yield folder.close_async(null);
-            opened = false;
-        } catch (Error err) {
-            if (opened) {
-                // Always try to close the opened folder
-                try {
-                    yield folder.close_async(null);
-                } catch (Error close_err) {
-                    warning("Error closing folder %s: %s",
-                            folder.to_string(), close_err.message);
-                }
+            // List the relevant messages again with the full set of
+            // fields, to make sure when we load them from the
+            // database we have all the data we need.
+            if (!relevant_ids.is_empty) {
+                emails = yield folder.list_email_by_sparse_id_async(
+                    relevant_ids,
+                    required_fields,
+                    flags,
+                    this.operation_cancellable
+                );
+            } else {
+                emails = null;
             }
-            throw err;
         }
 
         if (emails != null && !emails.is_empty) {
@@ -661,12 +621,11 @@ public class Geary.App.ConversationMonitor : BaseObject, Logging.Source {
         email_flags_changed(conversation, email);
     }
 
-    private async bool stop_monitoring_internal(GLib.Cancellable? cancellable)
-        throws Error {
+    private async void stop_monitoring_internal(GLib.Cancellable? cancellable)
+        throws GLib.Error {
         this.base_folder.email_appended.disconnect(on_folder_email_appended);
         this.base_folder.email_inserted.disconnect(on_folder_email_inserted);
         this.base_folder.email_removed.disconnect(on_folder_email_removed);
-        this.base_folder.opened.disconnect(on_folder_opened);
         this.base_folder.account.email_appended_to_folder.disconnect(on_account_email_appended);
         this.base_folder.account.email_inserted_into_folder.disconnect(on_account_email_inserted);
         this.base_folder.account.email_removed_from_folder.disconnect(on_account_email_removed);
@@ -675,35 +634,12 @@ public class Geary.App.ConversationMonitor : BaseObject, Logging.Source {
         // Cancel outstanding ops so they don't block the queue closing
         this.operation_cancellable.cancel();
 
-        // Keep track of errors stopping the queue but continue, since
-        // the cleanup below needs to occur.
-        Error? close_err = null;
-        try {
-            yield this.queue.stop_processing_async(cancellable);
-        } catch (Error err) {
-            close_err = err;
-        }
-
-        bool closing = false;
         if (this.base_was_opened) {
-            try {
-                closing = yield this.base_folder.close_async(null);
-            } catch (GLib.Error err) {
-                if (close_err == null) {
-                    close_err = err;
-                } else {
-                    warning(
-                        "Unable to close monitored folder %s: %s",
-                        this.base_folder.to_string(), err.message
-                    );
-                }
-            }
+            ((Geary.RemoteFolder) this.base_folder).stop_monitoring();
+            this.base_was_opened = false;
         }
 
-        if (close_err != null)
-            throw close_err;
-
-        return closing;
+        yield this.queue.stop_processing_async(cancellable);
     }
 
     private async void process_email_async(Gee.Collection<Geary.Email>? emails,
@@ -823,11 +759,6 @@ public class Geary.App.ConversationMonitor : BaseObject, Logging.Source {
               needed_message_ids.size, needed_messages.size);
     }
 
-    private void on_folder_opened(Geary.Folder.OpenState state, int count) {
-        if (state == Geary.Folder.OpenState.REMOTE)
-            this.queue.add(new ReseedOperation(this));
-    }
-
     private void on_folder_email_appended(Gee.Collection<EmailIdentifier> appended) {
         this.queue.add(new AppendOperation(this, appended));
     }
diff --git a/src/engine/app/app-draft-manager.vala b/src/engine/app/app-draft-manager.vala
index 9d5a31b1b..65ebc4d9f 100644
--- a/src/engine/app/app-draft-manager.vala
+++ b/src/engine/app/app-draft-manager.vala
@@ -215,43 +215,19 @@ public class Geary.App.DraftManager : BaseObject {
         this.create_support =  (FolderSupport.Create) save_to;
         this.remove_support = (FolderSupport.Remove) save_to;
 
-        this.drafts_folder.closed.connect(on_folder_closed);
-
-        yield drafts_folder.open_async(Folder.OpenFlags.NO_DELAY, cancellable);
-
-        // if drafts folder doesn't return the identifier of newly
-        // created emails, then this object can't do it's work
-        // ... wait until open to check for this, to be absolutely
-        // sure
-        //
-        // Since open_async returns before a remote connection is
-        // made, need to wait for it here to ensure
-        var engine = this.drafts_folder as ImapEngine.MinimalFolder;
-        if (engine != null) {
-            yield engine.claim_remote_session(cancellable);
-        }
+        // if drafts folder doesn't return the identifier of newly created emails, then this object
+        // can't do it's work ... wait until open to check for this, to be absolutely sure
         if (drafts_folder.properties.create_never_returns_id) {
-            try {
-                yield drafts_folder.close_async();
-            } catch (Error err) {
-                // ignore
-            }
-
-            throw new EngineError.UNSUPPORTED("%s: Drafts folder %s does not return created mail ID",
-                to_string(), drafts_folder.to_string());
+            throw new EngineError.UNSUPPORTED(
+                "%s: Drafts folder %s does not return created mail ID",
+                to_string(), drafts_folder.to_string()
+            );
         }
 
         // start the operation message loop, which ensures commands are handled in orderly fashion
         operation_loop_async.begin();
     }
 
-    private void on_folder_closed(Folder.CloseReason reason) {
-        if (reason == Folder.CloseReason.FOLDER_CLOSED) {
-            fatal(new EngineError.SERVER_UNAVAILABLE("%s: Unexpected drafts folder closed (%s)",
-                to_string(), reason.to_string()));
-        }
-    }
-
     /**
      * Flush pending operations and close the {@link DraftManager}.
      *
@@ -282,11 +258,6 @@ public class Geary.App.DraftManager : BaseObject {
                 // fall through
             }
         }
-
-        // Disconnect before closing, as signal handler is for unexpected closes
-        drafts_folder.closed.disconnect(on_folder_closed);
-
-        yield drafts_folder.close_async(cancellable);
     }
 
     private void check_open() throws EngineError {
@@ -376,18 +347,6 @@ public class Geary.App.DraftManager : BaseObject {
         if (op.op_type == OperationType.CLOSE)
             return false;
 
-        // make sure there's a folder to work with
-        if (this.drafts_folder == null ||
-            this.drafts_folder.get_open_state() == CLOSED) {
-            fatal(
-                new EngineError.SERVER_UNAVAILABLE(
-                    "%s: premature drafts folder close", to_string()
-                )
-            );
-
-            return false;
-        }
-
         // at this point, only operation left is PUSH
         assert(op.op_type == OperationType.PUSH);
 
diff --git a/src/engine/app/app-email-store.vala b/src/engine/app/app-email-store.vala
index 103fbf05a..ad9f041e4 100644
--- a/src/engine/app/app-email-store.vala
+++ b/src/engine/app/app-email-store.vala
@@ -112,33 +112,18 @@ public class Geary.App.EmailStore : BaseObject {
         next_folder_for_operation(AsyncFolderOperation operation,
                                   Gee.MultiMap<FolderPath,EmailIdentifier> folders_to_ids)
         throws GLib.Error {
-        bool best_is_open = false;
-        int best_count = 0;
         Geary.FolderPath? best = null;
+        int best_count = 0;
         foreach (Geary.FolderPath path in folders_to_ids.get_keys()) {
             Folder folder = this.account.get_folder(path);
-            if (!folder.get_type().is_a(operation.folder_type))
-                continue;
-
-            int count = folders_to_ids.get(path).size;
-            if (count == 0)
-                continue;
-
-            if (folder.get_open_state() == Geary.Folder.OpenState.REMOTE) {
-                if (!best_is_open) {
-                    best_is_open = true;
-                    best_count = 0;
+            if (folder.get_type().is_a(operation.folder_type)) {
+                int count = folders_to_ids.get(path).size;
+                if (count > best_count) {
+                    best_count = count;
+                    best = path;
                 }
-            } else if (best_is_open) {
-                continue;
-            }
-
-            if (count > best_count) {
-                best_count = count;
-                best = path;
             }
         }
-
         return best;
     }
 
@@ -163,31 +148,8 @@ public class Geary.App.EmailStore : BaseObject {
             Gee.Collection<Geary.EmailIdentifier> ids = folders_to_ids.get(path);
             assert(ids.size > 0);
 
-            bool open = false;
-            Gee.Collection<Geary.EmailIdentifier>? used_ids = null;
-            GLib.Error? op_error = null;
-            try {
-                yield folder.open_async(Folder.OpenFlags.NONE, cancellable);
-                open = true;
-                used_ids = yield operation.execute_async(folder, ids, cancellable);
-            } catch (GLib.Error err) {
-                op_error = err;
-            }
-
-            if (open) {
-                try {
-                    // Don't use the cancellable here, if it's been opened
-                    // we need to try to close it.
-                    yield folder.close_async(null);
-                } catch (Error e) {
-                    warning("Error closing folder %s: %s",
-                            folder.to_string(), e.message);
-                }
-            }
-
-            if (op_error != null) {
-                throw op_error;
-            }
+            Gee.Collection<Geary.EmailIdentifier>? used_ids =
+                yield operation.execute_async(folder, ids, cancellable);
 
             // We don't want to operate on any mails twice.
             if (used_ids != null) {
diff --git a/src/engine/app/conversation-monitor/app-fill-window-operation.vala 
b/src/engine/app/conversation-monitor/app-fill-window-operation.vala
index a7ca8b8cb..86685f5f5 100644
--- a/src/engine/app/conversation-monitor/app-fill-window-operation.vala
+++ b/src/engine/app/conversation-monitor/app-fill-window-operation.vala
@@ -55,9 +55,11 @@ private class Geary.App.FillWindowOperation : ConversationOperation {
             this.monitor.base_folder.properties.email_total
         );
 
+        var remote = this.monitor.base_folder as RemoteFolder;
         if (loaded < num_to_load &&
             this.monitor.can_load_more &&
-            this.monitor.base_folder.get_open_state() == REMOTE) {
+            remote != null &&
+            remote.is_monitoring) {
             // Not enough were loaded locally, but the remote seems to
             // be online and it looks like there and there might be
             // some more on the remote, so go see if there are any.
diff --git a/src/engine/imap-engine/imap-engine-account-synchronizer.vala 
b/src/engine/imap-engine/imap-engine-account-synchronizer.vala
index 3342fabd8..f57b8a634 100644
--- a/src/engine/imap-engine/imap-engine-account-synchronizer.vala
+++ b/src/engine/imap-engine/imap-engine-account-synchronizer.vala
@@ -162,64 +162,34 @@ private abstract class Geary.ImapEngine.FolderSync : FolderOperation {
 
     protected GLib.DateTime sync_max_epoch { get; private set; }
 
-    private Folder.OpenFlags open_flags;
-    private GLib.Cancellable? closed_cancellable = null;
-
 
     internal FolderSync(GenericAccount account,
                         MinimalFolder folder,
-                        GLib.DateTime sync_max_epoch,
-                        Folder.OpenFlags open_flags) {
+                        GLib.DateTime sync_max_epoch) {
         base(account, folder);
         this.sync_max_epoch = sync_max_epoch;
-        this.open_flags = open_flags;
-        this.folder.closed.connect(on_folder_close);
-    }
-
-    ~FolderSync() {
-        weak Geary.Folder? folder = this.folder;
-        if (folder != null) {
-            this.folder.closed.disconnect(on_folder_close);
-        }
     }
 
     public override async void execute(GLib.Cancellable cancellable)
         throws GLib.Error {
-        // Stash the cancellable so the op can cancel the sync if the
-        // folder closes.
-        this.closed_cancellable = cancellable;
+        debug("Synchronising");
 
-        bool was_opened = false;
-        MinimalFolder minimal = (MinimalFolder) this.folder;
-        try {
-            yield minimal.open_async(this.open_flags, cancellable);
-            was_opened = true;
-
-            debug("Synchronising");
-            // Determine the earliest date we should be synchronising
-            // back to
-            DateTime actual_max_epoch;
-            if (this.account.information.prefetch_period_days >= 0) {
-                actual_max_epoch = new DateTime.now_local();
-                actual_max_epoch = actual_max_epoch.add_days(
-                    0 - account.information.prefetch_period_days
-                );
-            } else {
-                actual_max_epoch = this.sync_max_epoch;
-            }
+        // Determine the earliest date we should be synchronising
+        // back to
+        DateTime actual_max_epoch;
+        if (this.account.information.prefetch_period_days >= 0) {
+            actual_max_epoch = new DateTime.now_local();
+            actual_max_epoch = actual_max_epoch.add_days(
+                0 - account.information.prefetch_period_days
+            );
+        } else {
+            actual_max_epoch = this.sync_max_epoch;
+        }
 
+        try {
             yield sync_folder(actual_max_epoch, cancellable);
         } catch (GLib.IOError.CANCELLED err) {
             // All good
-        } catch (EngineError.ALREADY_CLOSED err) {
-            // Failed to open the folder, which could be because the
-            // network went away, or because the remote folder went
-            // away. Either way don't bother reporting it.
-            debug(
-                "Folder failed to open %s: %s",
-                minimal.to_string(),
-                err.message
-            );
         } catch (GLib.Error err) {
             this.account.report_problem(
                 new ServiceProblemReport(
@@ -229,46 +199,12 @@ private abstract class Geary.ImapEngine.FolderSync : FolderOperation {
                 )
             );
         }
-
-        // Clear this now so that the wait for close below doesn't get
-        // cancelled as the folder closes.
-        this.closed_cancellable = null;
-
-        if (was_opened) {
-            try {
-                // don't pass in the Cancellable; really need this
-                // to complete in all cases
-                if (yield this.folder.close_async(null)) {
-                    // The folder was actually closing, so wait
-                    // for it here to completely close so that its
-                    // session has a chance to exit IMAP Selected
-                    // state when released, allowing the next sync
-                    // op to reuse the same session. Here we
-                    // definitely want to use the cancellable so
-                    // the wait can be interrupted.
-                    yield this.folder.wait_for_close_async(cancellable);
-                }
-            } catch (Error err) {
-                debug(
-                    "%s: Error closing folder %s: %s",
-                    this.account.to_string(),
-                    this.folder.to_string(),
-                    err.message
-                    );
-            }
-        }
     }
 
     protected abstract async void sync_folder(GLib.DateTime max_epoch,
                                               GLib.Cancellable cancellable)
         throws GLib.Error;
 
-    private void on_folder_close() {
-        if (this.closed_cancellable != null) {
-            this.closed_cancellable.cancel();
-        }
-    }
-
 }
 
 
@@ -285,13 +221,14 @@ private class Geary.ImapEngine.RefreshFolderSync : FolderSync {
     internal RefreshFolderSync(GenericAccount account,
                                MinimalFolder folder,
                                GLib.DateTime sync_max_epoch) {
-        base(account, folder, sync_max_epoch, NO_DELAY);
+        base(account, folder, sync_max_epoch);
     }
 
     protected override async void sync_folder(GLib.DateTime max_epoch,
                                               GLib.Cancellable cancellable)
         throws GLib.Error {
-        yield this.folder.synchronise_remote(cancellable);
+        var remote = (RemoteFolder) this.folder as RemoteFolder;
+        yield remote.synchronise(cancellable);
     }
 
 }
@@ -489,7 +426,7 @@ private class Geary.ImapEngine.TruncateToEpochFolderSync : FolderSync {
                                        MinimalFolder folder,
                                        DateTime sync_max_epoch,
                                        IdleGarbageCollection? post_idle_detach_op) {
-        base(account, folder, sync_max_epoch, NONE);
+        base(account, folder, sync_max_epoch);
         this.post_idle_detach_op = post_idle_detach_op;
     }
 
diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala 
b/src/engine/imap-engine/imap-engine-generic-account.vala
index ca569283a..58b8c7d27 100644
--- a/src/engine/imap-engine/imap-engine-generic-account.vala
+++ b/src/engine/imap-engine/imap-engine-generic-account.vala
@@ -92,7 +92,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
         imap.set_logging_parent(this);
         this.imap = imap;
 
-        smtp.outbox = new Outbox.Folder(this, local_folder_root, local);
+        smtp.outbox = new Outbox.Folder(this, local_folder_root, local.db);
         smtp.report_problem.connect(notify_report_problem);
         smtp.set_logging_parent(this);
         this.smtp = smtp;
@@ -211,7 +211,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
         folders_available_unavailable(null, remotes);
         foreach (var folder in remotes) {
             debug("Waiting for remote to close: %s", folder.to_string());
-            yield folder.wait_for_close_async();
+            yield ((MinimalFolder) folder).close();
         }
 
         // Close IMAP service manager now that folders are closed
@@ -690,7 +690,6 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
             FolderPath path = db_folder.get_path();
             if (!this.remote_folders.has_key(path)) {
                 MinimalFolder folder = new_folder(db_folder);
-                folder.report_problem.connect(notify_report_problem);
                 if (folder.used_as == NONE) {
                     var use = this.information.get_folder_use_for_path(path);
                     if (use != NONE) {
@@ -1461,8 +1460,9 @@ internal class Geary.ImapEngine.RefreshFolderUnseen : FolderOperation {
     }
 
     public override async void execute(GLib.Cancellable cancellable) throws GLib.Error {
-        GenericAccount account = (GenericAccount) this.account;
-        if (this.folder.get_open_state() == Geary.Folder.OpenState.CLOSED) {
+        var account = (GenericAccount) this.account;
+        var folder = (MinimalFolder) this.folder;
+        if (!folder.is_monitoring) {
             Imap.AccountSession? remote = yield account.claim_account_session(
                 cancellable
             );
@@ -1477,7 +1477,7 @@ internal class Geary.ImapEngine.RefreshFolderUnseen : FolderOperation {
                 // local_folder since we are only using its properties,
                 // and the properties were loaded when the folder was
                 // first instantiated.
-                ImapDB.Folder local_folder = ((MinimalFolder) this.folder).local_folder;
+                ImapDB.Folder local_folder = folder.local_folder;
 
                 if (remote_folder.properties.have_contents_changed(
                         local_folder.get_properties(),
diff --git a/src/engine/imap-engine/imap-engine-revokable-move.vala 
b/src/engine/imap-engine/imap-engine-revokable-move.vala
index 9490191c0..0c3ea40ee 100644
--- a/src/engine/imap-engine/imap-engine-revokable-move.vala
+++ b/src/engine/imap-engine/imap-engine-revokable-move.vala
@@ -33,17 +33,15 @@ private class Geary.ImapEngine.RevokableMove : Revokable {
         account.folders_available_unavailable.connect(on_folders_available_unavailable);
         source.email_removed.connect(on_source_email_removed);
         source.marked_email_removed.connect(on_source_email_removed);
-        source.closing.connect(on_source_closing);
     }
 
     ~RevokableMove() {
         account.folders_available_unavailable.disconnect(on_folders_available_unavailable);
         source.email_removed.disconnect(on_source_email_removed);
         source.marked_email_removed.disconnect(on_source_email_removed);
-        source.closing.disconnect(on_source_closing);
 
         // if still valid, schedule operation so its executed
-        if (valid && source.get_open_state() != Folder.OpenState.CLOSED) {
+        if (valid && source.is_remote_open) {
             debug("Freeing revokable, scheduling move %d emails from %s to %s", move_ids.size,
                 source.path.to_string(), destination.to_string());
 
@@ -55,7 +53,7 @@ private class Geary.ImapEngine.RevokableMove : Revokable {
             }
         } else if (valid) {
             debug("Not scheduling freed move revokable for %s, open_state=%s",
-                source.path.to_string(), source.get_open_state().to_string());
+                  source.path.to_string(), source.is_remote_open.to_string());
         }
     }
 
@@ -117,27 +115,4 @@ private class Geary.ImapEngine.RevokableMove : Revokable {
             set_invalid();
     }
 
-    private void on_source_closing(Gee.List<ReplayOperation> final_ops) {
-        if (!valid)
-            return;
-
-        MoveEmailCommit op = new MoveEmailCommit(
-            source, move_ids, destination.path, null
-        );
-        final_ops.add(op);
-        set_invalid();
-
-        // Capture these for the closure below, since once it gets
-        // invoked, this instance may no longer exist.
-        GenericAccount account = this.account;
-        Geary.Folder destination = this.destination;
-        op.wait_for_ready_async.begin(null, (obj, res) => {
-                try {
-                    op.wait_for_ready_async.end(res);
-                    account.update_folder(destination);
-                } catch (Error err) {
-                    // Oh well
-                }
-            });
-    }
 }
diff --git a/src/engine/meson.build b/src/engine/meson.build
index a5e86fd6c..f539752ba 100644
--- a/src/engine/meson.build
+++ b/src/engine/meson.build
@@ -3,7 +3,6 @@ engine_vala_sources = files(
   'api/geary.vala',
   'api/geary-account.vala',
   'api/geary-account-information.vala',
-  'api/geary-aggregated-folder-properties.vala',
   'api/geary-attachment.vala',
   'api/geary-base-object.vala',
   'api/geary-client-service.vala',
@@ -222,7 +221,6 @@ engine_vala_sources = files(
   'imap-engine/replay-ops/imap-engine-replay-removal.vala',
   'imap-engine/replay-ops/imap-engine-replay-update.vala',
   'imap-engine/replay-ops/imap-engine-server-search-email.vala',
-  'imap-engine/replay-ops/imap-engine-user-close.vala',
   'imap-engine/yahoo/imap-engine-yahoo-account.vala',
   'imap-engine/yahoo/imap-engine-yahoo-folder.vala',
 
diff --git a/src/engine/smtp/smtp-client-service.vala b/src/engine/smtp/smtp-client-service.vala
index 49c09cfa1..6204148e9 100644
--- a/src/engine/smtp/smtp-client-service.vala
+++ b/src/engine/smtp/smtp-client-service.vala
@@ -71,7 +71,6 @@ public class Geary.Smtp.ClientService : Geary.ClientService {
      */
     public override async void start(GLib.Cancellable? cancellable = null)
         throws GLib.Error {
-        yield this.outbox.open_async(Folder.OpenFlags.NONE, cancellable);
         yield this.fill_outbox_queue(cancellable);
         notify_started();
     }
@@ -90,7 +89,6 @@ public class Geary.Smtp.ClientService : Geary.ClientService {
             GLib.Idle.add(this.stop.callback);
             yield;
         }
-        yield this.outbox.close_async(cancellable);
     }
 
     /**
@@ -385,44 +383,17 @@ public class Geary.Smtp.ClientService : Geary.ClientService {
         }
 
         RFC822.Message raw = message.get_message();
-        bool open = false;
-        try {
-            yield create.open_async(NO_DELAY, cancellable);
-            open = true;
-            yield create.create_email_async(raw, null, null, cancellable);
-            yield wait_for_message(create, message, cancellable);
-        } finally {
-            if (open) {
-                try {
-                    yield create.close_async(null);
-                } catch (Error e) {
-                    debug("Error closing folder %s: %s", create.to_string(), e.message);
-                }
-            }
-        }
+        yield create.create_email_async(raw, null, null, cancellable);
+        yield wait_for_message(create, message, cancellable);
     }
 
     private async void sync_sent_mail(Geary.Email message,
                                       GLib.Cancellable? cancellable)
         throws GLib.Error {
-        Geary.Folder sent = this.owner.get_special_folder(SENT);
-        if (sent != null) {
-            bool open = false;
-            try {
-                yield sent.open_async(NO_DELAY, cancellable);
-                open = true;
-                yield sent.synchronise_remote(cancellable);
-                yield wait_for_message(sent, message, cancellable);
-            } finally {
-                if (open) {
-                    try {
-                        yield sent.close_async(null);
-                    } catch (Error e) {
-                        debug("Error closing folder %s: %s",
-                              sent.to_string(), e.message);
-                    }
-                }
-            }
+        var remote_sent = this.owner.get_special_folder(SENT) as RemoteFolder;
+        if (remote_sent != null) {
+            yield remote_sent.synchronise(cancellable);
+            yield wait_for_message(remote_sent, message, cancellable);
         }
     }
 
diff --git a/test/engine/app/app-conversation-monitor-test.vala 
b/test/engine/app/app-conversation-monitor-test.vala
index 22fd76285..f9c6b5a86 100644
--- a/test/engine/app/app-conversation-monitor-test.vala
+++ b/test/engine/app/app-conversation-monitor-test.vala
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 Michael Gratton <mike vee net>
+ * Copyright © 2018-2020 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.
@@ -12,16 +12,24 @@ class Geary.App.ConversationMonitorTest : TestCase {
     AccountInformation? account_info = null;
     Mock.Account? account = null;
     FolderRoot? folder_root = null;
-    Mock.Folder? base_folder = null;
+    Mock.RemoteFolder? base_folder = null;
     Mock.Folder? other_folder = null;
 
 
     public ConversationMonitorTest() {
         base("Geary.App.ConversationMonitorTest");
-        add_test("start_stop_monitoring", start_stop_monitoring);
-        add_test("open_error", open_error);
-        add_test("close_during_open_error", close_during_open_error);
-        add_test("close_after_open_error", close_after_open_error);
+        add_test(
+            "start_stop_monitoring_remote_not_started",
+            start_stop_monitoring_remote_not_started
+        );
+        add_test(
+            "start_stop_monitoring_remote_already_started",
+            start_stop_monitoring_remote_already_started
+        );
+        add_test(
+            "start_stop_monitoring_local",
+            start_stop_monitoring_local
+        );
         add_test("load_single_message", load_single_message);
         add_test("load_multiple_messages", load_multiple_messages);
         add_test("load_related_message", load_related_message);
@@ -40,12 +48,14 @@ class Geary.App.ConversationMonitorTest : TestCase {
         );
         this.account = new Mock.Account(this.account_info);
         this.folder_root = new FolderRoot("#test", false);
-        this.base_folder = new Mock.Folder(
+        this.base_folder = new Mock.RemoteFolder(
             this.account,
             null,
             this.folder_root.get_child("base"),
             NONE,
-            null
+            null,
+            false,
+            false
         );
         this.other_folder = new Mock.Folder(
             this.account,
@@ -64,9 +74,18 @@ class Geary.App.ConversationMonitorTest : TestCase {
         this.account = null;
     }
 
-    public void start_stop_monitoring() throws Error {
+    public void start_stop_monitoring_remote_not_started() throws GLib.Error {
+        var test_article = new Mock.RemoteFolder(
+            this.account,
+            null,
+            this.folder_root.get_child("base"),
+            NONE,
+            null,
+            false,
+            false
+        );
         ConversationMonitor monitor = new ConversationMonitor(
-            this.base_folder, Email.Field.NONE, 10
+            test_article, Email.Field.NONE, 10
         );
         Cancellable test_cancellable = new Cancellable();
 
@@ -75,12 +94,12 @@ class Geary.App.ConversationMonitorTest : TestCase {
         monitor.scan_started.connect(() => { saw_scan_started = true; });
         monitor.scan_completed.connect(() => { saw_scan_completed = true; });
 
-        this.base_folder.expect_call("open_async");
-        this.base_folder.expect_call("list_email_by_id_async");
-        this.base_folder.expect_call("close_async");
+        test_article.expect_call("start_monitoring");
+        test_article.expect_call("list_email_by_id_async");
+        test_article.expect_call("stop_monitoring");
 
         monitor.start_monitoring.begin(
-            NONE, test_cancellable, this.async_completion
+            test_cancellable, this.async_completion
         );
         monitor.start_monitoring.end(async_result());
 
@@ -97,89 +116,93 @@ class Geary.App.ConversationMonitorTest : TestCase {
         assert_true(saw_scan_started, "scan_started not fired");
         assert_true(saw_scan_completed, "scan_completed not fired");
 
-        this.base_folder.assert_expectations();
+        test_article.assert_expectations();
     }
 
-    public void open_error() throws Error {
+    public void start_stop_monitoring_remote_already_started()
+        throws GLib.Error {
+        var test_article = new Mock.RemoteFolder(
+            this.account,
+            null,
+            this.folder_root.get_child("base"),
+            NONE,
+            null,
+            true,
+            false
+        );
         ConversationMonitor monitor = new ConversationMonitor(
-            this.base_folder, Email.Field.NONE, 10
+            test_article, Email.Field.NONE, 10
         );
+        Cancellable test_cancellable = new Cancellable();
+
+        bool saw_scan_started = false;
+        bool saw_scan_completed = false;
+        monitor.scan_started.connect(() => { saw_scan_started = true; });
+        monitor.scan_completed.connect(() => { saw_scan_completed = true; });
 
-        ValaUnit.ExpectedCall open = this.base_folder
-            .expect_call("open_async")
-            .throws(new EngineError.SERVER_UNAVAILABLE("Mock error"));
+        test_article.expect_call("list_email_by_id_async");
 
         monitor.start_monitoring.begin(
-            NONE, null, this.async_completion
+            test_cancellable, this.async_completion
         );
-        try {
-            monitor.start_monitoring.end(async_result());
-            assert_not_reached();
-        } catch (Error err) {
-            assert_error(open.throw_error, err);
+        monitor.start_monitoring.end(async_result());
+
+        // Process all of the async tasks arising from the open
+        while (this.main_loop.pending()) {
+            this.main_loop.iteration(true);
         }
 
-        assert_false(monitor.is_monitoring, "is monitoring");
+        monitor.stop_monitoring.begin(
+            test_cancellable, this.async_completion
+        );
+        monitor.stop_monitoring.end(async_result());
+
+        assert_true(saw_scan_started, "scan_started not fired");
+        assert_true(saw_scan_completed, "scan_completed not fired");
 
-        this.base_folder.assert_expectations();
+        test_article.assert_expectations();
     }
 
-    public void close_during_open_error() throws GLib.Error {
+    public void start_stop_monitoring_local()
+        throws GLib.Error {
+        var test_article = new Mock.Folder(
+            this.account,
+            null,
+            this.folder_root.get_child("base"),
+            NONE,
+            null
+        );
         ConversationMonitor monitor = new ConversationMonitor(
-            this.base_folder, Email.Field.NONE, 10
+            test_article, Email.Field.NONE, 10
         );
+        Cancellable test_cancellable = new Cancellable();
 
-        ValaUnit.ExpectedCall open = this.base_folder
-            .expect_call("open_async")
-            .async_call(PAUSE)
-            .throws(new GLib.IOError.CANCELLED("Mock error"));
-        this.base_folder
-            .expect_call("close_async")
-            .throws(new EngineError.ALREADY_CLOSED("Mock error"));
-
-        var start_waiter = new ValaUnit.AsyncResultWaiter(this.main_loop);
-        monitor.start_monitoring.begin(NONE, null, start_waiter.async_completion);
-
-        var stop_waiter = new ValaUnit.AsyncResultWaiter(this.main_loop);
-        monitor.stop_monitoring.begin(null, stop_waiter.async_completion);
-
-        open.async_resume();
-        try {
-            monitor.start_monitoring.end(start_waiter.async_result());
-            assert_not_reached();
-        } catch (GLib.Error err) {
-            assert_error(open.throw_error, err);
-        }
+        bool saw_scan_started = false;
+        bool saw_scan_completed = false;
+        monitor.scan_started.connect(() => { saw_scan_started = true; });
+        monitor.scan_completed.connect(() => { saw_scan_completed = true; });
 
-        // base_folder.close_async should not be called, so should not
-        // throw an error
-        monitor.stop_monitoring.end(stop_waiter.async_result());
-    }
+        test_article.expect_call("list_email_by_id_async");
 
-    public void close_after_open_error() throws GLib.Error {
-        ConversationMonitor monitor = new ConversationMonitor(
-            this.base_folder, Email.Field.NONE, 10
+        monitor.start_monitoring.begin(
+            test_cancellable, this.async_completion
         );
+        monitor.start_monitoring.end(async_result());
 
-        ValaUnit.ExpectedCall open = this.base_folder
-            .expect_call("open_async")
-            .throws(new EngineError.SERVER_UNAVAILABLE("Mock error"));
-        this.base_folder
-            .expect_call("close_async")
-            .throws(new EngineError.ALREADY_CLOSED("Mock error"));
-
-        monitor.start_monitoring.begin(NONE, null, this.async_completion);
-        try {
-            monitor.start_monitoring.end(async_result());
-            assert_not_reached();
-        } catch (GLib.Error err) {
-            assert_error(open.throw_error, err);
+        // Process all of the async tasks arising from the open
+        while (this.main_loop.pending()) {
+            this.main_loop.iteration(true);
         }
 
-        // base_folder.close_async should not be called, so should not
-        // throw an error
-        monitor.stop_monitoring.begin(null, this.async_completion);
+        monitor.stop_monitoring.begin(
+            test_cancellable, this.async_completion
+        );
         monitor.stop_monitoring.end(async_result());
+
+        assert_true(saw_scan_started, "scan_started not fired");
+        assert_true(saw_scan_completed, "scan_completed not fired");
+
+        test_article.assert_expectations();
     }
 
     public void load_single_message() throws Error {
@@ -306,7 +329,7 @@ class Geary.App.ConversationMonitorTest : TestCase {
 
         // Close the monitor to cancel the final load so it does not
         // error out during later tests
-        this.base_folder.expect_call("close_async");
+        this.base_folder.expect_call("stop_monitoring");
         monitor.stop_monitoring.begin(
             null, this.async_completion
         );
@@ -332,12 +355,10 @@ class Geary.App.ConversationMonitorTest : TestCase {
         ConversationMonitor monitor = setup_monitor({e1}, paths);
         assert_equal<int?>(monitor.size, 1, "Initial conversation count");
 
-        this.other_folder.expect_call("open_async");
         this.other_folder.expect_call("list_email_by_sparse_id_async")
             .returns_object(new Gee.ArrayList<Email>.wrap({e3}));
         this.other_folder.expect_call("list_email_by_sparse_id_async")
             .returns_object(new Gee.ArrayList<Email>.wrap({e3}));
-        this.other_folder.expect_call("close_async");
 
         // ExternalAppendOperation's blacklist check
         this.account.expect_call("get_special_folder");
@@ -467,7 +488,7 @@ class Geary.App.ConversationMonitorTest : TestCase {
          *           - get_containing_folders_async
          */
 
-        this.base_folder.expect_call("open_async");
+        this.base_folder.expect_call("start_monitoring");
         ValaUnit.ExpectedCall list_call = this.base_folder
             .expect_call("list_email_by_id_async")
             .returns_object(new Gee.ArrayList<Email>.wrap(base_folder_email));
@@ -542,7 +563,7 @@ class Geary.App.ConversationMonitorTest : TestCase {
         }
 
         monitor.start_monitoring.begin(
-            NONE, test_cancellable, this.async_completion
+            test_cancellable, this.async_completion
         );
         monitor.start_monitoring.end(async_result());
 
diff --git a/test/meson.build b/test/meson.build
index a4fe2c292..de8adf0b9 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -14,6 +14,7 @@ libmock_sources = [
   'mock/mock-email-properties.vala',
   'mock/mock-folder.vala',
   'mock/mock-folder-properties.vala',
+  'mock/mock-remote-folder.vala',
   'mock/mock-search-query.vala',
 ]
 
diff --git a/test/mock/mock-remote-folder.vala b/test/mock/mock-remote-folder.vala
new file mode 100644
index 000000000..75cf54a8d
--- /dev/null
+++ b/test/mock/mock-remote-folder.vala
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+public class Mock.RemoteFolder : Geary.RemoteFolder,
+    ValaUnit.TestAssertions,
+    ValaUnit.MockObject {
+
+
+    public override Geary.Account account {
+        get { return this._account; }
+    }
+
+    public override Geary.FolderProperties properties {
+        get { return this._properties; }
+    }
+
+    public override Geary.FolderPath path {
+        get { return this._path; }
+    }
+
+    public override Geary.Folder.SpecialUse used_as {
+        get { return this._used_as; }
+    }
+
+    public override bool is_monitoring {
+        get { return this._is_monitoring; }
+    }
+    private bool _is_monitoring = false;
+
+    public override bool is_fully_expanded {
+        get { return this._is_fully_expanded; }
+    }
+    private bool _is_fully_expanded = false;
+
+    protected Gee.Queue<ValaUnit.ExpectedCall> expected {
+        get; set; default = new Gee.LinkedList<ValaUnit.ExpectedCall>();
+    }
+
+
+    private Geary.Account _account;
+    private Geary.FolderProperties _properties;
+    private Geary.FolderPath _path;
+    private Geary.Folder.SpecialUse _used_as;
+    private Geary.ProgressMonitor _opening_monitor;
+
+
+    public RemoteFolder(Geary.Account? account,
+                        Geary.FolderProperties? properties,
+                        Geary.FolderPath? path,
+                        Geary.Folder.SpecialUse used_as,
+                        Geary.ProgressMonitor? monitor,
+                        bool is_monitoring,
+                        bool is_fully_expanded) {
+        this._account = account;
+        this._properties = properties ?? new FolderPoperties();
+        this._path = path;
+        this._used_as = used_as;
+        this._opening_monitor = monitor;
+        this._is_monitoring = is_monitoring;
+        this._is_fully_expanded = is_fully_expanded;
+    }
+
+    public override async Gee.Collection<Geary.EmailIdentifier> contains_identifiers(
+        Gee.Collection<Geary.EmailIdentifier> ids,
+        GLib.Cancellable? cancellable = null)
+    throws GLib.Error {
+        return yield object_call_async<Gee.Collection<Geary.EmailIdentifier>>(
+            "contains_identifiers",
+            {ids, cancellable},
+            new Gee.LinkedList<Geary.EmailIdentifier>()
+        );
+    }
+
+    public override async Gee.List<Geary.Email>?
+        list_email_by_id_async(Geary.EmailIdentifier? initial_id,
+                               int count,
+                               Geary.Email.Field required_fields,
+                               Geary.Folder.ListFlags flags,
+                               GLib.Cancellable? cancellable = null)
+        throws GLib.Error {
+        return yield object_call_async<Gee.List<Geary.Email>?>(
+            "list_email_by_id_async",
+            {initial_id, int_arg(count), box_arg(required_fields), box_arg(flags), cancellable},
+            null
+        );
+    }
+
+    public override async Gee.List<Geary.Email>?
+        list_email_by_sparse_id_async(Gee.Collection<Geary.EmailIdentifier> ids,
+                                      Geary.Email.Field required_fields,
+                                      Geary.Folder.ListFlags flags,
+                                      GLib.Cancellable? cancellable = null)
+        throws GLib.Error {
+        return yield object_call_async<Gee.List<Geary.Email>?>(
+            "list_email_by_sparse_id_async",
+            {ids, box_arg(required_fields), box_arg(flags), cancellable},
+            null
+        );
+    }
+
+    public override async Geary.Email
+        fetch_email_async(Geary.EmailIdentifier email_id,
+                          Geary.Email.Field required_fields,
+                          Geary.Folder.ListFlags flags,
+                          GLib.Cancellable? cancellable = null)
+    throws GLib.Error {
+        throw new Geary.EngineError.UNSUPPORTED("Mock method");
+    }
+
+    public override void set_used_as_custom(bool enabled)
+        throws Geary.EngineError.UNSUPPORTED {
+        throw new Geary.EngineError.UNSUPPORTED("Mock method");
+    }
+
+    public override void start_monitoring() {
+        try {
+            void_call("start_monitoring", {});
+            this._is_monitoring = true;
+        } catch (GLib.Error err) {
+            // nooop
+        }
+    }
+
+    public override void stop_monitoring() {
+        try {
+            void_call("stop_monitoring", {});
+            this._is_monitoring = false;
+        } catch (GLib.Error err) {
+            // noop
+        }
+    }
+
+    public override async void synchronise(GLib.Cancellable? cancellable)
+        throws GLib.Error {
+        yield void_call_async("synchronise", { cancellable });
+    }
+
+    public override async void expand_vector(GLib.Cancellable? cancellable)
+        throws GLib.Error {
+        yield void_call_async("expand_vector", { cancellable });
+    }
+
+}


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