From faf7a2bdfdb0685eaeac3b6a8bf8ce84c87f7aa2 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Mon, 14 Jan 2019 11:01:03 +1100 Subject: [PATCH] Add some unit tests for Geary.ImapDb.Account folder management --- src/engine/imap-db/imap-db-account.vala | 36 +-- .../imap-engine-generic-account.vala | 2 +- test/engine/imap-db/imap-db-account-test.vala | 305 ++++++++++++++++++ test/meson.build | 1 + test/test-case.vala | 23 ++ test/test-engine.vala | 1 + 6 files changed, 344 insertions(+), 24 deletions(-) create mode 100644 test/engine/imap-db/imap-db-account-test.vala diff --git a/src/engine/imap-db/imap-db-account.vala b/src/engine/imap-db/imap-db-account.vala index 6b1db5e6..0353fd2d 100644 --- a/src/engine/imap-db/imap-db-account.vala +++ b/src/engine/imap-db/imap-db-account.vala @@ -326,18 +326,7 @@ private class Geary.ImapDB.Account : BaseObject { throw err; } - - Geary.Account account; - try { - account = Geary.Engine.instance.get_account_instance(account_information); - } catch (Error e) { - // If they're opening an account, the engine should already be - // open, and there should be no reason for this to fail. Thus, if - // we get here, it's a programmer error. - - error("Error finding account from its information: %s", e.message); - } - + background_cancellable = new Cancellable(); // Kick off a background update of the search table, but since the database is getting @@ -419,21 +408,23 @@ private class Geary.ImapDB.Account : BaseObject { return yield fetch_folder_async(path, cancellable); } - public async void delete_folder_async(Geary.Folder folder, Cancellable? cancellable) - throws Error { + public async void delete_folder_async(Geary.FolderPath path, + GLib.Cancellable? cancellable) + throws GLib.Error { check_open(); - - Geary.FolderPath path = folder.path; - yield db.exec_transaction_async(Db.TransactionType.RW, (cx) => { int64 folder_id; do_fetch_folder_id(cx, path, false, out folder_id, cancellable); - if (folder_id == Db.INVALID_ROWID) - return Db.TransactionOutcome.ROLLBACK; - + if (folder_id == Db.INVALID_ROWID) { + throw new EngineError.NOT_FOUND( + "Folder not found: %s", path.to_string() + ); + } + if (do_has_children(cx, folder_id, cancellable)) { - debug("Can't delete folder %s because it has children", folder.to_string()); - return Db.TransactionOutcome.ROLLBACK; + throw new ImapError.NOT_SUPPORTED( + "Folder has children: %s", path.to_string() + ); } do_delete_folder(cx, folder_id, cancellable); @@ -441,7 +432,6 @@ private class Geary.ImapDB.Account : BaseObject { return Db.TransactionOutcome.COMMIT; }, cancellable); - } private void initialize_contacts(Cancellable? cancellable = null) throws Error { diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala b/src/engine/imap-engine/imap-engine-generic-account.vala index 08cc810a..7276ca9f 100644 --- a/src/engine/imap-engine/imap-engine-generic-account.vala +++ b/src/engine/imap-engine/imap-engine-generic-account.vala @@ -1268,7 +1268,7 @@ internal class Geary.ImapEngine.UpdateRemoteFolders : AccountOperation { foreach (Geary.Folder folder in removed) { try { debug("Locally deleting removed folder %s", folder.to_string()); - yield local.delete_folder_async(folder, cancellable); + yield local.delete_folder_async(folder.path, cancellable); } catch (Error e) { debug("Unable to locally delete removed folder %s: %s", folder.to_string(), e.message); } diff --git a/test/engine/imap-db/imap-db-account-test.vala b/test/engine/imap-db/imap-db-account-test.vala new file mode 100644 index 00000000..146d7127 --- /dev/null +++ b/test/engine/imap-db/imap-db-account-test.vala @@ -0,0 +1,305 @@ +/* + * Copyright 2019 Michael Gratton + * + * 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.AccountTest : TestCase { + + + private GLib.File? tmp_dir = null; + private Geary.AccountInformation? config = null; + private Account? account = null; + + + public AccountTest() { + base("Geary.ImapDB.AccountTest"); + add_test("create_base_folder", create_base_folder); + add_test("create_child_folder", create_child_folder); + add_test("list_folders", list_folders); + add_test("delete_folder", delete_folder); + add_test("delete_folder_with_child", delete_folder_with_child); + add_test("delete_nonexistent_folder", delete_nonexistent_folder); + add_test("fetch_base_folder", fetch_base_folder); + add_test("fetch_child_folder", fetch_child_folder); + add_test("fetch_nonexistent_folder", fetch_nonexistent_folder); + } + + public override void set_up() throws GLib.Error { + this.tmp_dir = GLib.File.new_for_path( + GLib.DirUtils.make_tmp("geary-db-database-test-XXXXXX") + ); + + this.config = new Geary.AccountInformation( + "test", + ServiceProvider.OTHER, + new MockCredentialsMediator(), + new Geary.RFC822.MailboxAddress(null, "test@example.com") + ); + + this.account = new Account(config); + this.account.open_async.begin( + this.tmp_dir, + GLib.File.new_for_path(_SOURCE_ROOT_DIR).get_child("sql"), + null, + (obj, ret) => { async_complete(ret); } + ); + this.account.open_async.end(async_result()); + } + + public override void tear_down() throws GLib.Error { + this.account.close_async.begin( + null, + (obj, ret) => { async_complete(ret); } + ); + this.account.close_async.end(async_result()); + + delete_file(this.tmp_dir); + this.tmp_dir = null; + } + + public void create_base_folder() throws GLib.Error { + Imap.Folder folder = new Imap.Folder( + new Imap.FolderRoot("test"), + new Imap.FolderProperties.selectable( + new Imap.MailboxAttributes( + Gee.Collection.empty() + ), + new Imap.StatusData( + new Imap.MailboxSpecifier("test"), + 10, // total + 9, // recent + new Imap.UID(8), + new Imap.UIDValidity(7), + 6 //unseen + ), + new Imap.Capabilities(1) + ) + ); + + this.account.clone_folder_async.begin( + folder, + null, + (obj, ret) => { async_complete(ret); } + ); + this.account.clone_folder_async.end(async_result()); + + Geary.Db.Result result = this.account.db.query( + "SELECT * FROM FolderTable;" + ); + assert_false(result.finished, "Folder not created"); + assert_string("test", result.string_for("name"), "Folder name"); + assert_true(result.is_null_for("parent_id"), "Folder parent"); + assert_false(result.next(), "Multiple rows inserted"); + } + + public void create_child_folder() throws GLib.Error { + this.account.db.exec( + "INSERT INTO FolderTable (id, name) VALUES (1, 'test');" + ); + + Imap.Folder folder = new Imap.Folder( + new Imap.FolderRoot("test").get_child("child"), + new Imap.FolderProperties.selectable( + new Imap.MailboxAttributes( + Gee.Collection.empty() + ), + new Imap.StatusData( + new Imap.MailboxSpecifier("test>child"), + 10, // total + 9, // recent + new Imap.UID(8), + new Imap.UIDValidity(7), + 6 //unseen + ), + new Imap.Capabilities(1) + ) + ); + + this.account.clone_folder_async.begin( + folder, + null, + (obj, ret) => { async_complete(ret); } + ); + this.account.clone_folder_async.end(async_result()); + + Geary.Db.Result result = this.account.db.query( + "SELECT * FROM FolderTable WHERE id != 1;" + ); + assert_false(result.finished, "Folder not created"); + assert_string("child", result.string_for("name"), "Folder name"); + assert_int(1, result.int_for("parent_id"), "Folder parent"); + assert_false(result.next(), "Multiple rows inserted"); + } + + public void list_folders() throws GLib.Error { + this.account.db.exec(""" + INSERT INTO FolderTable (id, name, parent_id) + VALUES + (1, 'test1', null), + (2, 'test2', 1), + (3, 'test3', 2); + """); + + this.account.list_folders_async.begin( + null, + null, + (obj, ret) => { async_complete(ret); } + ); + Gee.Collection result = + this.account.list_folders_async.end(async_result()); + + Folder test1 = traverse(result).first(); + assert_int(1, result.size, "Base folder not listed"); + assert_string("test1", test1.get_path().basename, "Base folder name"); + + this.account.list_folders_async.begin( + test1.get_path(), + null, + (obj, ret) => { async_complete(ret); } + ); + result = this.account.list_folders_async.end(async_result()); + + Folder test2 = traverse(result).first(); + assert_int(1, result.size, "Child folder not listed"); + assert_string("test2", test2.get_path().basename, "Child folder name"); + + this.account.list_folders_async.begin( + test2.get_path(), + null, + (obj, ret) => { async_complete(ret); } + ); + result = this.account.list_folders_async.end(async_result()); + + Folder test3 = traverse(result).first(); + assert_int(1, result.size, "Grandchild folder not listed"); + assert_string("test3", test3.get_path().basename, "Grandchild folder name"); + } + + public void delete_folder() throws GLib.Error { + this.account.db.exec(""" + INSERT INTO FolderTable (id, name, parent_id) + VALUES + (1, 'test1', null), + (2, 'test2', 1); + """); + + this.account.delete_folder_async.begin( + new Imap.FolderRoot("test1").get_child("test2"), + null, + (obj, ret) => { async_complete(ret); } + ); + this.account.delete_folder_async.end(async_result()); + + this.account.delete_folder_async.begin( + new Imap.FolderRoot("test1"), + null, + (obj, ret) => { async_complete(ret); } + ); + this.account.delete_folder_async.end(async_result()); + } + + public void delete_folder_with_child() throws GLib.Error { + this.account.db.exec(""" + INSERT INTO FolderTable (id, name, parent_id) + VALUES + (1, 'test1', null), + (2, 'test2', 1); + """); + + this.account.delete_folder_async.begin( + new Imap.FolderRoot("test1"), + null, + (obj, ret) => { async_complete(ret); } + ); + try { + this.account.delete_folder_async.end(async_result()); + assert_not_reached(); + } catch (GLib.Error err) { + assert_error(new ImapError.NOT_SUPPORTED(""), err); + } + } + + public void delete_nonexistent_folder() throws GLib.Error { + this.account.db.exec(""" + INSERT INTO FolderTable (id, name, parent_id) + VALUES + (1, 'test1', null), + (2, 'test2', 1); + """); + + this.account.delete_folder_async.begin( + new Imap.FolderRoot("test3"), + null, + (obj, ret) => { async_complete(ret); } + ); + try { + this.account.delete_folder_async.end(async_result()); + assert_not_reached(); + } catch (GLib.Error err) { + assert_error(new EngineError.NOT_FOUND(""), err); + } + } + + public void fetch_base_folder() throws GLib.Error { + this.account.db.exec(""" + INSERT INTO FolderTable (id, name, parent_id) + VALUES + (1, 'test1', null), + (2, 'test2', 1); + """); + + this.account.fetch_folder_async.begin( + new Imap.FolderRoot("test1"), + null, + (obj, ret) => { async_complete(ret); } + ); + + Folder? result = this.account.fetch_folder_async.end(async_result()); + assert_non_null(result); + assert_string("test1", result.get_path().basename); + } + + public void fetch_child_folder() throws GLib.Error { + this.account.db.exec(""" + INSERT INTO FolderTable (id, name, parent_id) + VALUES + (1, 'test1', null), + (2, 'test2', 1); + """); + + this.account.fetch_folder_async.begin( + new Imap.FolderRoot("test1").get_child("test2"), + null, + (obj, ret) => { async_complete(ret); } + ); + + Folder? result = this.account.fetch_folder_async.end(async_result()); + assert_non_null(result); + assert_string("test2", result.get_path().basename); + } + + public void fetch_nonexistent_folder() throws GLib.Error { + this.account.db.exec(""" + INSERT INTO FolderTable (id, name, parent_id) + VALUES + (1, 'test1', null), + (2, 'test2', 1); + """); + + this.account.fetch_folder_async.begin( + new Imap.FolderRoot("test3"), + null, + (obj, ret) => { async_complete(ret); } + ); + try { + this.account.fetch_folder_async.end(async_result()); + assert_not_reached(); + } catch (GLib.Error err) { + assert_error(new EngineError.NOT_FOUND(""), err); + } + } + +} diff --git a/test/meson.build b/test/meson.build index 26e588ba..c8f45530 100644 --- a/test/meson.build +++ b/test/meson.build @@ -36,6 +36,7 @@ geary_test_engine_sources = [ 'engine/imap/parameter/imap-list-parameter-test.vala', 'engine/imap/response/imap-namespace-response-test.vala', 'engine/imap/transport/imap-deserializer-test.vala', + 'engine/imap-db/imap-db-account-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', diff --git a/test/test-case.vala b/test/test-case.vala index 166bf324..b90de697 100644 --- a/test/test-case.vala +++ b/test/test-case.vala @@ -152,6 +152,27 @@ private inline void print_assert(string message, string? context) { GLib.stderr.putc('\n'); } +public void delete_file(File parent) throws GLib.Error { + FileInfo info = parent.query_info( + "standard::*", + FileQueryInfoFlags.NOFOLLOW_SYMLINKS + ); + + if (info.get_file_type () == FileType.DIRECTORY) { + FileEnumerator enumerator = parent.enumerate_children( + "standard::*", + FileQueryInfoFlags.NOFOLLOW_SYMLINKS + ); + + info = null; + while (((info = enumerator.next_file()) != null)) { + delete_file(parent.get_child(info.get_name())); + } + } + + parent.delete(); +} + public abstract class TestCase : Object { @@ -304,5 +325,7 @@ public abstract class TestCase : Object { assert_no_error(err); } } + } + } diff --git a/test/test-engine.vala b/test/test-engine.vala index d5f1e546..3c2309d8 100644 --- a/test/test-engine.vala +++ b/test/test-engine.vala @@ -45,6 +45,7 @@ int main(string[] args) { engine.add_suite(new Geary.Imap.ListParameterTest().get_suite()); engine.add_suite(new Geary.Imap.MailboxSpecifierTest().get_suite()); engine.add_suite(new Geary.Imap.NamespaceResponseTest().get_suite()); + engine.add_suite(new Geary.ImapDB.AccountTest().get_suite()); engine.add_suite(new Geary.ImapDB.AttachmentTest().get_suite()); engine.add_suite(new Geary.ImapDB.AttachmentIoTest().get_suite()); engine.add_suite(new Geary.ImapDB.DatabaseTest().get_suite());