[geary/wip/726281-text-attachment-crlf: 6/13] Move attachment related code from ImapDB.Folder to Attachment.



commit 15d8789685fc118e3307a8b9a84be8e663f62b86
Author: Michael James Gratton <mike vee net>
Date:   Sat Apr 28 20:56:09 2018 +1000

    Move attachment related code from ImapDB.Folder to Attachment.

 src/engine/imap-db/imap-db-account.vala          |    6 +-
 src/engine/imap-db/imap-db-attachment.vala       |  219 +++++++++++++++++++
 src/engine/imap-db/imap-db-database.vala         |   10 +-
 src/engine/imap-db/imap-db-folder.vala           |  250 ++--------------------
 test/CMakeLists.txt                              |    1 +
 test/engine/imap-db/imap-db-attachment-test.vala |   89 ++++++++
 test/meson.build                                 |    1 +
 test/test-engine.vala                            |    1 +
 8 files changed, 337 insertions(+), 240 deletions(-)
---
diff --git a/src/engine/imap-db/imap-db-account.vala b/src/engine/imap-db/imap-db-account.vala
index b142dab..41b86da 100644
--- a/src/engine/imap-db/imap-db-account.vala
+++ b/src/engine/imap-db/imap-db-account.vala
@@ -728,7 +728,7 @@ private class Geary.ImapDB.Account : BaseObject {
                 // Ignore any messages that don't have the required fields.
                 if (partial_ok || row.fields.fulfills(requested_fields)) {
                     Geary.Email email = row.to_email(new Geary.ImapDB.EmailIdentifier(id, null));
-                    Geary.ImapDB.Folder.do_add_attachments(
+                    Attachment.do_add_attachments(
                         cx, this.db.attachments_path, email, id, cancellable
                     );
 
@@ -1393,7 +1393,7 @@ private class Geary.ImapDB.Account : BaseObject {
                     email_id.to_string(), row.fields, required_fields);
 
             email = row.to_email(email_id);
-            Geary.ImapDB.Folder.do_add_attachments(
+            Attachment.do_add_attachments(
                 cx, this.db.attachments_path, email, email_id.message_id, cancellable
             );
 
@@ -1556,7 +1556,7 @@ private class Geary.ImapDB.Account : BaseObject {
                     MessageRow row = Geary.ImapDB.Folder.do_fetch_message_row(
                         cx, message_id, search_fields, out db_fields, cancellable);
                     Geary.Email email = row.to_email(new Geary.ImapDB.EmailIdentifier(message_id, null));
-                    Geary.ImapDB.Folder.do_add_attachments(
+                    Attachment.do_add_attachments(
                         cx, this.db.attachments_path, email, message_id, cancellable
                     );
 
diff --git a/src/engine/imap-db/imap-db-attachment.vala b/src/engine/imap-db/imap-db-attachment.vala
index c7752d1..19f5b0d 100644
--- a/src/engine/imap-db/imap-db-attachment.vala
+++ b/src/engine/imap-db/imap-db-attachment.vala
@@ -40,4 +40,223 @@ private class Geary.ImapDB.Attachment : Geary.Attachment {
             .get_child(filename ?? NULL_FILE_NAME);
     }
 
+    internal static void do_save_attachments(Db.Connection cx,
+                                             GLib.File attachments_path,
+                                             int64 message_id,
+                                             Gee.List<GMime.Part>? attachments,
+                                             Cancellable? cancellable)
+        throws Error {
+        // nothing to do if no attachments
+        if (attachments == null || attachments.size == 0)
+            return;
+
+        foreach (GMime.Part attachment in attachments) {
+            GMime.ContentType? content_type = attachment.get_content_type();
+            string mime_type = (content_type != null)
+                ? content_type.to_string()
+                : Mime.ContentType.DEFAULT_CONTENT_TYPE;
+            string? disposition = attachment.get_disposition();
+            string? content_id = attachment.get_content_id();
+            string? description = attachment.get_content_description();
+            string? filename = RFC822.Utils.get_clean_attachment_filename(attachment);
+
+            // Convert the attachment content into a usable ByteArray.
+            GMime.DataWrapper? attachment_data = attachment.get_content_object();
+            ByteArray byte_array = new ByteArray();
+            GMime.StreamMem stream = new GMime.StreamMem.with_byte_array(byte_array);
+            stream.set_owner(false);
+            if (attachment_data != null)
+                attachment_data.write_to_stream(stream); // data is null if it's 0 bytes
+            uint filesize = byte_array.len;
+
+            // convert into DispositionType enum, which is stored as int
+            // (legacy code stored UNSPECIFIED as NULL, which is zero, which is ATTACHMENT, so preserve
+            // this behavior)
+            Mime.DispositionType disposition_type = Mime.DispositionType.deserialize(disposition,
+                null);
+            if (disposition_type == Mime.DispositionType.UNSPECIFIED)
+                disposition_type = Mime.DispositionType.ATTACHMENT;
+
+            // Insert it into the database.
+            Db.Statement stmt = cx.prepare("""
+                INSERT INTO MessageAttachmentTable (message_id, filename, mime_type, filesize, disposition, 
content_id, description)
+                VALUES (?, ?, ?, ?, ?, ?, ?)
+                """);
+            stmt.bind_rowid(0, message_id);
+            stmt.bind_string(1, filename);
+            stmt.bind_string(2, mime_type);
+            stmt.bind_uint(3, filesize);
+            stmt.bind_int(4, disposition_type);
+            stmt.bind_string(5, content_id);
+            stmt.bind_string(6, description);
+
+            int64 attachment_id = stmt.exec_insert(cancellable);
+            File saved_file = ImapDB.Attachment.generate_file(
+                attachments_path, message_id, attachment_id, filename
+            );
+
+            // On the off-chance this is marked for deletion, unmark it
+            try {
+                stmt = cx.prepare("""
+                    DELETE FROM DeleteAttachmentFileTable
+                    WHERE filename = ?
+                """);
+                stmt.bind_string(0, saved_file.get_path());
+
+                stmt.exec(cancellable);
+            } catch (Error err) {
+                debug("Unable to delete from DeleteAttachmentFileTable: %s", err.message);
+
+                // not a deal-breaker, fall through
+            }
+
+            debug("Saving attachment to %s", saved_file.get_path());
+
+            try {
+                // create directory, but don't throw exception if already exists
+                try {
+                    saved_file.get_parent().make_directory_with_parents(cancellable);
+                } catch (IOError ioe) {
+                    // fall through if already exists
+                    if (!(ioe is IOError.EXISTS))
+                        throw ioe;
+                }
+
+                // REPLACE_DESTINATION doesn't seem to work as advertised all the time ... just
+                // play it safe here
+                if (saved_file.query_exists(cancellable))
+                    saved_file.delete(cancellable);
+
+                // Create the file where the attachment will be saved and get the output stream.
+                FileOutputStream saved_stream = saved_file.create(FileCreateFlags.REPLACE_DESTINATION,
+                    cancellable);
+
+                // Save the data to disk and flush it.
+                size_t written;
+                if (filesize != 0)
+                    saved_stream.write_all(byte_array.data[0:filesize], out written, cancellable);
+
+                saved_stream.flush(cancellable);
+            } catch (Error error) {
+                // An error occurred while saving the attachment, so lets remove the attachment from
+                // the database and delete the file (in case it's partially written)
+                debug("Failed to save attachment %s: %s", saved_file.get_path(), error.message);
+
+                try {
+                    saved_file.delete();
+                } catch (Error delete_error) {
+                    debug("Error attempting to delete partial attachment %s: %s", saved_file.get_path(),
+                        delete_error.message);
+                }
+
+                try {
+                    Db.Statement remove_stmt = cx.prepare(
+                        "DELETE FROM MessageAttachmentTable WHERE id=?");
+                    remove_stmt.bind_rowid(0, attachment_id);
+
+                    remove_stmt.exec();
+                } catch (Error remove_error) {
+                    debug("Error attempting to remove added attachment row for %s: %s",
+                        saved_file.get_path(), remove_error.message);
+                }
+
+                throw error;
+            }
+        }
+    }
+
+    internal static void do_delete_attachments(Db.Connection cx,
+                                               GLib.File attachments_path,
+                                               int64 message_id)
+        throws Error {
+        Gee.List<Geary.Attachment>? attachments = do_list_attachments(
+            cx, attachments_path, message_id, null
+        );
+        if (attachments == null || attachments.size == 0)
+            return;
+
+        // delete all files
+        foreach (Geary.Attachment attachment in attachments) {
+            try {
+                attachment.file.delete(null);
+            } catch (Error err) {
+                debug("Unable to delete file %s: %s", attachment.file.get_path(), err.message);
+            }
+        }
+
+        // remove all from attachment table
+        Db.Statement stmt = new Db.Statement(cx, """
+            DELETE FROM MessageAttachmentTable WHERE message_id = ?
+        """);
+        stmt.bind_rowid(0, message_id);
+
+        stmt.exec();
+    }
+
+    internal static Geary.Email do_add_attachments(Db.Connection cx,
+                                                   GLib.File attachments_path,
+                                                   Geary.Email email,
+                                                   int64 message_id,
+                                                   Cancellable? cancellable = null)
+        throws Error {
+        // Add attachments if available
+        if (email.fields.fulfills(ImapDB.Attachment.REQUIRED_FIELDS)) {
+            Gee.List<Geary.Attachment>? attachments = do_list_attachments(
+                cx, attachments_path, message_id, cancellable
+            );
+            if (attachments != null)
+                email.add_attachments(attachments);
+        }
+
+        return email;
+    }
+
+    private static Gee.List<Geary.Attachment>?
+        do_list_attachments(Db.Connection cx,
+                            GLib.File attachments_path,
+                            int64 message_id,
+                            Cancellable? cancellable)
+        throws Error {
+        Db.Statement stmt = cx.prepare("""
+            SELECT id, filename, mime_type, filesize, disposition, content_id, description
+            FROM MessageAttachmentTable
+            WHERE message_id = ?
+            ORDER BY id
+            """);
+        stmt.bind_rowid(0, message_id);
+
+        Db.Result results = stmt.exec(cancellable);
+        if (results.finished)
+            return null;
+
+        Gee.List<Geary.Attachment> list = new Gee.ArrayList<Geary.Attachment>();
+        do {
+            string? content_filename = results.string_at(1);
+            if (content_filename == ImapDB.Attachment.NULL_FILE_NAME) {
+                // Prior to 0.12, Geary would store the untranslated
+                // string "none" as the filename when none was
+                // specified by the MIME content disposition. Check
+                // for that and clean it up.
+                content_filename = null;
+            }
+            Mime.ContentDisposition disposition = new Mime.ContentDisposition.simple(
+                Mime.DispositionType.from_int(results.int_at(4)));
+            list.add(
+                new ImapDB.Attachment(
+                    message_id,
+                    results.rowid_at(0),
+                    Mime.ContentType.deserialize(results.nonnull_string_at(2)),
+                    results.string_at(5),
+                    results.string_at(6),
+                    disposition,
+                    content_filename,
+                    attachments_path,
+                    results.int64_at(3)
+                )
+            );
+        } while (results.next(cancellable));
+
+        return list;
+    }
+
 }
diff --git a/src/engine/imap-db/imap-db-database.vala b/src/engine/imap-db/imap-db-database.vala
index bced663..8028865 100644
--- a/src/engine/imap-db/imap-db-database.vala
+++ b/src/engine/imap-db/imap-db-database.vala
@@ -336,11 +336,11 @@ private class Geary.ImapDB.Database : Geary.Db.VersionedDatabase {
                         Mime.DispositionType target_disposition = Mime.DispositionType.UNSPECIFIED;
                         if (message.get_sub_messages().is_empty)
                             target_disposition = Mime.DispositionType.INLINE;
-                        Geary.ImapDB.Folder.do_save_attachments_db(
+                        Attachment.do_save_attachments(
                             cx,
+                            this.attachments_path,
                             id,
                             message.get_attachments(target_disposition),
-                            this.attachments_path,
                             null
                         );
                     } catch (Error e) {
@@ -500,7 +500,7 @@ private class Geary.ImapDB.Database : Geary.Db.VersionedDatabase {
                     message.get_attachments();
 
                     try {
-                        Geary.ImapDB.Folder.do_delete_attachments(
+                        Attachment.do_delete_attachments(
                             cx, this.attachments_path, message_id
                         );
                     } catch (Error err) {
@@ -511,11 +511,11 @@ private class Geary.ImapDB.Database : Geary.Db.VersionedDatabase {
 
                     // rebuild all
                     try {
-                        Geary.ImapDB.Folder.do_save_attachments_db(
+                        Attachment.do_save_attachments(
                             cx,
+                            this.attachments_path,
                             message_id,
                             msg_attachments,
-                            this.attachments_path,
                             null
                         );
                     } catch (Error err) {
diff --git a/src/engine/imap-db/imap-db-folder.vala b/src/engine/imap-db/imap-db-folder.vala
index f7aa264..0bb5085 100644
--- a/src/engine/imap-db/imap-db-folder.vala
+++ b/src/engine/imap-db/imap-db-folder.vala
@@ -1452,8 +1452,15 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
             // write out attachments, if any
             // TODO: Because this involves saving files, it potentially means holding up access to the
             // database while they're being written; may want to do this outside of transaction.
-            if (email.fields.fulfills(Attachment.REQUIRED_FIELDS))
-                do_save_attachments(cx, message_id, email.get_message().get_attachments(), cancellable);
+            if (email.fields.fulfills(Attachment.REQUIRED_FIELDS)) {
+                Attachment.do_save_attachments(
+                    cx,
+                    this.attachments_path,
+                    message_id,
+                    email.get_message().get_attachments(),
+                    cancellable
+                );
+            }
 
             do_add_email_to_search_table(cx, message_id, email, cancellable);
 
@@ -1597,29 +1604,11 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
         
         Geary.Email email = row.to_email(location.email_id);
 
-        return do_add_attachments(
+        return Attachment.do_add_attachments(
             cx, this.attachments_path, email, location.message_id, cancellable
         );
     }
 
-    internal static Geary.Email do_add_attachments(Db.Connection cx,
-                                                   GLib.File attachments_path,
-                                                   Geary.Email email,
-                                                   int64 message_id,
-                                                   Cancellable? cancellable = null)
-        throws Error {
-        // Add attachments if available
-        if (email.fields.fulfills(ImapDB.Attachment.REQUIRED_FIELDS)) {
-            Gee.List<Geary.Attachment>? attachments = do_list_attachments(
-                cx, attachments_path, message_id, cancellable
-            );
-            if (attachments != null)
-                email.add_attachments(attachments);
-        }
-        
-        return email;
-    }
-    
     private static string fields_to_columns(Geary.Email.Field fields) {
         // always pull the rowid and fields of the message
         StringBuilder builder = new StringBuilder("id, fields");
@@ -2045,13 +2034,18 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
             // Update attachments if not already in the database
             if (!fetched_fields.fulfills(Attachment.REQUIRED_FIELDS)
                 && combined_email.fields.fulfills(Attachment.REQUIRED_FIELDS)) {
-                do_save_attachments(cx, location.message_id, combined_email.get_message().get_attachments(),
-                    cancellable);
+                Attachment.do_save_attachments(
+                    cx,
+                    this.attachments_path,
+                    location.message_id,
+                    combined_email.get_message().get_attachments(),
+                    cancellable
+                );
             }
 
             // Must add attachments to the email object after they're saved to
             // the database.
-            do_add_attachments(
+            Attachment.do_add_attachments(
                 cx,
                 this.attachments_path,
                 combined_email,
@@ -2078,214 +2072,6 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
         unread_count_change += new_unread_count;
     }
 
-    private static Gee.List<Geary.Attachment>?
-        do_list_attachments(Db.Connection cx,
-                            GLib.File attachments_path,
-                            int64 message_id,
-                            Cancellable? cancellable)
-        throws Error {
-        Db.Statement stmt = cx.prepare("""
-            SELECT id, filename, mime_type, filesize, disposition, content_id, description
-            FROM MessageAttachmentTable
-            WHERE message_id = ?
-            ORDER BY id
-            """);
-        stmt.bind_rowid(0, message_id);
-        
-        Db.Result results = stmt.exec(cancellable);
-        if (results.finished)
-            return null;
-
-        Gee.List<Geary.Attachment> list = new Gee.ArrayList<Geary.Attachment>();
-        do {
-            string? content_filename = results.string_at(1);
-            if (content_filename == ImapDB.Attachment.NULL_FILE_NAME) {
-                // Prior to 0.12, Geary would store the untranslated
-                // string "none" as the filename when none was
-                // specified by the MIME content disposition. Check
-                // for that and clean it up.
-                content_filename = null;
-            }
-            Mime.ContentDisposition disposition = new Mime.ContentDisposition.simple(
-                Mime.DispositionType.from_int(results.int_at(4)));
-            list.add(
-                new ImapDB.Attachment(
-                    message_id,
-                    results.rowid_at(0),
-                    Mime.ContentType.deserialize(results.nonnull_string_at(2)),
-                    results.string_at(5),
-                    results.string_at(6),
-                    disposition,
-                    content_filename,
-                    attachments_path,
-                    results.int64_at(3)
-                )
-            );
-        } while (results.next(cancellable));
-
-        return list;
-    }
-
-    private void do_save_attachments(Db.Connection cx, int64 message_id,
-        Gee.List<GMime.Part>? attachments, Cancellable? cancellable) throws Error {
-        do_save_attachments_db(
-            cx, message_id, attachments, this.attachments_path, cancellable
-        );
-    }
-
-    public static void do_save_attachments_db(Db.Connection cx,
-                                              int64 message_id,
-                                              Gee.List<GMime.Part>? attachments,
-                                              GLib.File attachments_path,
-                                              Cancellable? cancellable)
-        throws Error {
-        // nothing to do if no attachments
-        if (attachments == null || attachments.size == 0)
-            return;
-        
-        foreach (GMime.Part attachment in attachments) {
-            GMime.ContentType? content_type = attachment.get_content_type();
-            string mime_type = (content_type != null)
-                ? content_type.to_string()
-                : Mime.ContentType.DEFAULT_CONTENT_TYPE;
-            string? disposition = attachment.get_disposition();
-            string? content_id = attachment.get_content_id();
-            string? description = attachment.get_content_description();
-            string? filename = RFC822.Utils.get_clean_attachment_filename(attachment);
-
-            // Convert the attachment content into a usable ByteArray.
-            GMime.DataWrapper? attachment_data = attachment.get_content_object();
-            ByteArray byte_array = new ByteArray();
-            GMime.StreamMem stream = new GMime.StreamMem.with_byte_array(byte_array);
-            stream.set_owner(false);
-            if (attachment_data != null)
-                attachment_data.write_to_stream(stream); // data is null if it's 0 bytes
-            uint filesize = byte_array.len;
-            
-            // convert into DispositionType enum, which is stored as int
-            // (legacy code stored UNSPECIFIED as NULL, which is zero, which is ATTACHMENT, so preserve
-            // this behavior)
-            Mime.DispositionType disposition_type = Mime.DispositionType.deserialize(disposition,
-                null);
-            if (disposition_type == Mime.DispositionType.UNSPECIFIED)
-                disposition_type = Mime.DispositionType.ATTACHMENT;
-            
-            // Insert it into the database.
-            Db.Statement stmt = cx.prepare("""
-                INSERT INTO MessageAttachmentTable (message_id, filename, mime_type, filesize, disposition, 
content_id, description)
-                VALUES (?, ?, ?, ?, ?, ?, ?)
-                """);
-            stmt.bind_rowid(0, message_id);
-            stmt.bind_string(1, filename);
-            stmt.bind_string(2, mime_type);
-            stmt.bind_uint(3, filesize);
-            stmt.bind_int(4, disposition_type);
-            stmt.bind_string(5, content_id);
-            stmt.bind_string(6, description);
-
-            int64 attachment_id = stmt.exec_insert(cancellable);
-            File saved_file = ImapDB.Attachment.generate_file(
-                attachments_path, message_id, attachment_id, filename
-            );
-
-            // On the off-chance this is marked for deletion, unmark it
-            try {
-                stmt = cx.prepare("""
-                    DELETE FROM DeleteAttachmentFileTable
-                    WHERE filename = ?
-                """);
-                stmt.bind_string(0, saved_file.get_path());
-                
-                stmt.exec(cancellable);
-            } catch (Error err) {
-                debug("Unable to delete from DeleteAttachmentFileTable: %s", err.message);
-                
-                // not a deal-breaker, fall through
-            }
-            
-            debug("Saving attachment to %s", saved_file.get_path());
-            
-            try {
-                // create directory, but don't throw exception if already exists
-                try {
-                    saved_file.get_parent().make_directory_with_parents(cancellable);
-                } catch (IOError ioe) {
-                    // fall through if already exists
-                    if (!(ioe is IOError.EXISTS))
-                        throw ioe;
-                }
-                
-                // REPLACE_DESTINATION doesn't seem to work as advertised all the time ... just
-                // play it safe here
-                if (saved_file.query_exists(cancellable))
-                    saved_file.delete(cancellable);
-                
-                // Create the file where the attachment will be saved and get the output stream.
-                FileOutputStream saved_stream = saved_file.create(FileCreateFlags.REPLACE_DESTINATION,
-                    cancellable);
-                
-                // Save the data to disk and flush it.
-                size_t written;
-                if (filesize != 0)
-                    saved_stream.write_all(byte_array.data[0:filesize], out written, cancellable);
-                
-                saved_stream.flush(cancellable);
-            } catch (Error error) {
-                // An error occurred while saving the attachment, so lets remove the attachment from
-                // the database and delete the file (in case it's partially written)
-                debug("Failed to save attachment %s: %s", saved_file.get_path(), error.message);
-                
-                try {
-                    saved_file.delete();
-                } catch (Error delete_error) {
-                    debug("Error attempting to delete partial attachment %s: %s", saved_file.get_path(),
-                        delete_error.message);
-                }
-                
-                try {
-                    Db.Statement remove_stmt = cx.prepare(
-                        "DELETE FROM MessageAttachmentTable WHERE id=?");
-                    remove_stmt.bind_rowid(0, attachment_id);
-                    
-                    remove_stmt.exec();
-                } catch (Error remove_error) {
-                    debug("Error attempting to remove added attachment row for %s: %s",
-                        saved_file.get_path(), remove_error.message);
-                }
-                
-                throw error;
-            }
-        }
-    }
-
-    public static void do_delete_attachments(Db.Connection cx,
-                                             GLib.File attachments_path,
-                                             int64 message_id)
-        throws Error {
-        Gee.List<Geary.Attachment>? attachments = do_list_attachments(
-            cx, attachments_path, message_id, null
-        );
-        if (attachments == null || attachments.size == 0)
-            return;
-
-        // delete all files
-        foreach (Geary.Attachment attachment in attachments) {
-            try {
-                attachment.file.delete(null);
-            } catch (Error err) {
-                debug("Unable to delete file %s: %s", attachment.file.get_path(), err.message);
-            }
-        }
-        
-        // remove all from attachment table
-        Db.Statement stmt = new Db.Statement(cx, """
-            DELETE FROM MessageAttachmentTable WHERE message_id = ?
-        """);
-        stmt.bind_rowid(0, message_id);
-        
-        stmt.exec();
-    }
-    
     /**
      * Adds a value to the unread count.  If this makes the unread count negative, it will be
      * set to zero.
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index cd96c03..2841073 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -31,6 +31,7 @@ set(TEST_ENGINE_SRC
   engine/imap/command/imap-create-command-test.vala
   engine/imap/response/imap-namespace-response-test.vala
   engine/imap/transport/imap-deserializer-test.vala
+  engine/imap-db/imap-db-attachment-test.vala
   engine/imap-db/imap-db-database-test.vala
   engine/imap-engine/account-processor-test.vala
   engine/mime-content-type-test.vala
diff --git a/test/engine/imap-db/imap-db-attachment-test.vala 
b/test/engine/imap-db/imap-db-attachment-test.vala
new file mode 100644
index 0000000..7600f75
--- /dev/null
+++ b/test/engine/imap-db/imap-db-attachment-test.vala
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2018 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.ImapDB.AttachmentTest : TestCase {
+
+
+    private Geary.Db.Database? db;
+
+    public AttachmentTest() {
+        base("Geary.ImapDB.FolderTest");
+        add_test("save_minimal_attachment", save_minimal_attachment);
+    }
+
+    public override void set_up() throws Error {
+        this.db = new Geary.Db.Database.transient();
+        this.db.open.begin(
+            Geary.Db.DatabaseFlags.NONE, null, null,
+            (obj, res) => { async_complete(res); }
+        );
+        this.db.open.end(async_result());
+        this.db.exec("""
+CREATE TABLE MessageTable (
+    id INTEGER PRIMARY KEY
+);
+""");
+        this.db.exec("INSERT INTO MessageTable VALUES (1);");
+
+        this.db.exec("""
+CREATE TABLE MessageAttachmentTable (
+    id INTEGER PRIMARY KEY,
+    message_id INTEGER REFERENCES MessageTable ON DELETE CASCADE,
+    filename TEXT,
+    mime_type TEXT,
+    filesize INTEGER,
+    disposition INTEGER,
+    content_id TEXT DEFAULT NULL,
+    description TEXT DEFAULT NULL
+);
+""");
+
+    }
+
+    public override void tear_down() throws Error {
+        this.db.close();
+        this.db = null;
+    }
+
+    public void save_minimal_attachment() throws Error {
+        GLib.File tmp_dir = GLib.File.new_for_path(
+            GLib.DirUtils.make_tmp("geary-impadb-foldertest-XXXXXX")
+        );
+
+        GMime.DataWrapper body = new GMime.DataWrapper.with_stream(
+            new GMime.StreamMem.with_buffer(TEXT_ATTACHMENT.data),
+            GMime.ContentEncoding.DEFAULT
+        );
+        GMime.Part attachment = new GMime.Part.with_type("text", "plain");
+        attachment.set_content_object(body);
+        attachment.encode(GMime.EncodingConstraint.7BIT);
+
+        Gee.List<GMime.Part> attachments = new Gee.LinkedList<GMime.Part>();
+        attachments.add(attachment);
+
+        Geary.ImapDB.Attachment.do_save_attachments(
+            this.db.get_master_connection(),
+            tmp_dir,
+            1,
+            attachments,
+            null
+        );
+
+        Geary.Db.Result result = this.db.query(
+            "SELECT * FROM MessageAttachmentTable;"
+        );
+        assert_false(result.finished, "Row not inserted");
+        assert_int(1, result.int_for("message_id"), "Message id");
+        assert_false(result.next(), "Multiple rows inserted");
+
+        Geary.Files.recursive_delete_async.begin(tmp_dir);
+    }
+
+
+    private const string TEXT_ATTACHMENT = "This is an attachment.\n";
+
+}
diff --git a/test/meson.build b/test/meson.build
index f2e5f44..6749136 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -27,6 +27,7 @@ geary_test_engine_sources = [
   'engine/imap/command/imap-create-command-test.vala',
   'engine/imap/response/imap-namespace-response-test.vala',
   'engine/imap/transport/imap-deserializer-test.vala',
+  'engine/imap-db/imap-db-attachment-test.vala',
   'engine/imap-db/imap-db-database-test.vala',
   'engine/imap-engine/account-processor-test.vala',
   'engine/mime-content-type-test.vala',
diff --git a/test/test-engine.vala b/test/test-engine.vala
index 071f781..89bf5d1 100644
--- a/test/test-engine.vala
+++ b/test/test-engine.vala
@@ -36,6 +36,7 @@ int main(string[] args) {
     engine.add_suite(new Geary.Imap.DeserializerTest().get_suite());
     engine.add_suite(new Geary.Imap.CreateCommandTest().get_suite());
     engine.add_suite(new Geary.Imap.NamespaceResponseTest().get_suite());
+    engine.add_suite(new Geary.ImapDB.AttachmentTest().get_suite());
     engine.add_suite(new Geary.ImapDB.DatabaseTest().get_suite());
     engine.add_suite(new Geary.ImapEngine.AccountProcessorTest().get_suite());
     engine.add_suite(new Geary.Inet.Test().get_suite());


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