2018-04-04 15:58:44 +10:00
|
|
|
|
/*
|
|
|
|
|
|
* 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.App.ConversationMonitorTest : TestCase {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AccountInformation? account_info = null;
|
|
|
|
|
|
MockAccount? account = null;
|
Convert Geary.FolderRoot to be an actual root, not just a top-level
Instead of each top-level IMAP folder being a FolderRoot object, then
children of that being FolderPath objects, this makes FolderRoot an
"empty" FolderPath, so that both top-level and descendant folders are
plain FolderPath objects. Aside from being more technically correct,
this means that empty namespace roots can now be used interchangably
with non-empty namespace roots (addressing issue #181), and custom
folder implementations no longer need to provide their own trivial,
custom FolderRoot.
To support this, a notion of an IMAP root and a local root have been
added from which all remote and local folder paths are now derived,
existing places that assume top-level == root have been fixed, and
unit tests have been added.
2019-01-14 12:10:48 +11:00
|
|
|
|
FolderRoot? folder_root = null;
|
2018-04-04 15:58:44 +10:00
|
|
|
|
MockFolder? base_folder = null;
|
|
|
|
|
|
MockFolder? other_folder = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public ConversationMonitorTest() {
|
|
|
|
|
|
base("Geary.App.ConversationMonitorTest");
|
|
|
|
|
|
add_test("start_stop_monitoring", start_stop_monitoring);
|
|
|
|
|
|
add_test("open_error", open_error);
|
2020-04-10 14:55:18 +10:00
|
|
|
|
add_test("close_during_open_error", close_during_open_error);
|
|
|
|
|
|
add_test("close_after_open_error", close_after_open_error);
|
2018-04-04 15:58:44 +10:00
|
|
|
|
add_test("load_single_message", load_single_message);
|
|
|
|
|
|
add_test("load_multiple_messages", load_multiple_messages);
|
|
|
|
|
|
add_test("load_related_message", load_related_message);
|
|
|
|
|
|
add_test("base_folder_message_appended", base_folder_message_appended);
|
|
|
|
|
|
add_test("base_folder_message_removed", base_folder_message_removed);
|
|
|
|
|
|
add_test("external_folder_message_appended", external_folder_message_appended);
|
2018-10-10 02:01:56 -07:00
|
|
|
|
add_test("conversation_marked_as_deleted", conversation_marked_as_deleted);
|
2018-04-04 15:58:44 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void set_up() {
|
|
|
|
|
|
this.account_info = new AccountInformation(
|
|
|
|
|
|
"account_01",
|
2018-07-23 13:28:58 +10:00
|
|
|
|
ServiceProvider.OTHER,
|
2018-12-08 13:53:37 +11:00
|
|
|
|
new MockCredentialsMediator(),
|
2018-12-07 10:12:02 +11:00
|
|
|
|
new RFC822.MailboxAddress(null, "test1@example.com")
|
2018-04-04 15:58:44 +10:00
|
|
|
|
);
|
2018-11-18 23:18:21 +11:00
|
|
|
|
this.account = new MockAccount(this.account_info);
|
2019-04-14 20:55:42 +10:00
|
|
|
|
this.folder_root = new FolderRoot("#test", false);
|
2018-04-04 15:58:44 +10:00
|
|
|
|
this.base_folder = new MockFolder(
|
|
|
|
|
|
this.account,
|
|
|
|
|
|
null,
|
Convert Geary.FolderRoot to be an actual root, not just a top-level
Instead of each top-level IMAP folder being a FolderRoot object, then
children of that being FolderPath objects, this makes FolderRoot an
"empty" FolderPath, so that both top-level and descendant folders are
plain FolderPath objects. Aside from being more technically correct,
this means that empty namespace roots can now be used interchangably
with non-empty namespace roots (addressing issue #181), and custom
folder implementations no longer need to provide their own trivial,
custom FolderRoot.
To support this, a notion of an IMAP root and a local root have been
added from which all remote and local folder paths are now derived,
existing places that assume top-level == root have been fixed, and
unit tests have been added.
2019-01-14 12:10:48 +11:00
|
|
|
|
this.folder_root.get_child("base"),
|
2020-03-31 16:08:46 +11:00
|
|
|
|
NONE,
|
2018-04-04 15:58:44 +10:00
|
|
|
|
null
|
|
|
|
|
|
);
|
|
|
|
|
|
this.other_folder = new MockFolder(
|
|
|
|
|
|
this.account,
|
|
|
|
|
|
null,
|
Convert Geary.FolderRoot to be an actual root, not just a top-level
Instead of each top-level IMAP folder being a FolderRoot object, then
children of that being FolderPath objects, this makes FolderRoot an
"empty" FolderPath, so that both top-level and descendant folders are
plain FolderPath objects. Aside from being more technically correct,
this means that empty namespace roots can now be used interchangably
with non-empty namespace roots (addressing issue #181), and custom
folder implementations no longer need to provide their own trivial,
custom FolderRoot.
To support this, a notion of an IMAP root and a local root have been
added from which all remote and local folder paths are now derived,
existing places that assume top-level == root have been fixed, and
unit tests have been added.
2019-01-14 12:10:48 +11:00
|
|
|
|
this.folder_root.get_child("other"),
|
2020-03-31 16:08:46 +11:00
|
|
|
|
NONE,
|
2018-04-04 15:58:44 +10:00
|
|
|
|
null
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Convert Geary.FolderRoot to be an actual root, not just a top-level
Instead of each top-level IMAP folder being a FolderRoot object, then
children of that being FolderPath objects, this makes FolderRoot an
"empty" FolderPath, so that both top-level and descendant folders are
plain FolderPath objects. Aside from being more technically correct,
this means that empty namespace roots can now be used interchangably
with non-empty namespace roots (addressing issue #181), and custom
folder implementations no longer need to provide their own trivial,
custom FolderRoot.
To support this, a notion of an IMAP root and a local root have been
added from which all remote and local folder paths are now derived,
existing places that assume top-level == root have been fixed, and
unit tests have been added.
2019-01-14 12:10:48 +11:00
|
|
|
|
public override void tear_down() {
|
|
|
|
|
|
this.other_folder = null;
|
|
|
|
|
|
this.base_folder = null;
|
|
|
|
|
|
this.folder_root = null;
|
|
|
|
|
|
this.account_info = null;
|
|
|
|
|
|
this.account = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-04-04 15:58:44 +10:00
|
|
|
|
public void start_stop_monitoring() throws Error {
|
|
|
|
|
|
ConversationMonitor monitor = new ConversationMonitor(
|
2019-11-05 09:30:15 +11:00
|
|
|
|
this.base_folder, Email.Field.NONE, 10
|
2018-04-04 15:58:44 +10:00
|
|
|
|
);
|
|
|
|
|
|
Cancellable test_cancellable = new Cancellable();
|
|
|
|
|
|
|
2018-07-08 15:11:44 +10:00
|
|
|
|
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; });
|
|
|
|
|
|
|
2019-11-05 09:30:15 +11:00
|
|
|
|
this.base_folder.expect_call("open_async");
|
2018-04-04 15:58:44 +10:00
|
|
|
|
this.base_folder.expect_call("list_email_by_id_async");
|
|
|
|
|
|
this.base_folder.expect_call("close_async");
|
|
|
|
|
|
|
2019-11-05 09:30:15 +11:00
|
|
|
|
monitor.start_monitoring.begin(
|
2020-04-10 12:58:09 +10:00
|
|
|
|
NONE, test_cancellable, this.async_completion
|
2018-04-04 15:58:44 +10:00
|
|
|
|
);
|
2019-11-05 09:30:15 +11:00
|
|
|
|
monitor.start_monitoring.end(async_result());
|
2018-04-04 15:58:44 +10:00
|
|
|
|
|
2018-07-08 15:11:44 +10:00
|
|
|
|
// Process all of the async tasks arising from the open
|
|
|
|
|
|
while (this.main_loop.pending()) {
|
|
|
|
|
|
this.main_loop.iteration(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-11-05 09:30:15 +11:00
|
|
|
|
monitor.stop_monitoring.begin(
|
2020-04-10 12:58:09 +10:00
|
|
|
|
test_cancellable, this.async_completion
|
2018-04-04 15:58:44 +10:00
|
|
|
|
);
|
2019-11-05 09:30:15 +11:00
|
|
|
|
monitor.stop_monitoring.end(async_result());
|
2018-04-04 15:58:44 +10:00
|
|
|
|
|
2018-07-08 15:11:44 +10:00
|
|
|
|
assert_true(saw_scan_started, "scan_started not fired");
|
|
|
|
|
|
assert_true(saw_scan_completed, "scan_completed not fired");
|
|
|
|
|
|
|
2018-04-04 15:58:44 +10:00
|
|
|
|
this.base_folder.assert_expectations();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void open_error() throws Error {
|
|
|
|
|
|
ConversationMonitor monitor = new ConversationMonitor(
|
2019-11-05 09:30:15 +11:00
|
|
|
|
this.base_folder, Email.Field.NONE, 10
|
2018-04-04 15:58:44 +10:00
|
|
|
|
);
|
|
|
|
|
|
|
2020-05-09 16:04:22 +10:00
|
|
|
|
ValaUnit.ExpectedCall open = this.base_folder
|
2018-04-04 15:58:44 +10:00
|
|
|
|
.expect_call("open_async")
|
|
|
|
|
|
.throws(new EngineError.SERVER_UNAVAILABLE("Mock error"));
|
|
|
|
|
|
|
2019-11-05 09:30:15 +11:00
|
|
|
|
monitor.start_monitoring.begin(
|
2020-04-10 12:58:09 +10:00
|
|
|
|
NONE, null, this.async_completion
|
2018-04-04 15:58:44 +10:00
|
|
|
|
);
|
|
|
|
|
|
try {
|
2019-11-05 09:30:15 +11:00
|
|
|
|
monitor.start_monitoring.end(async_result());
|
2018-04-04 15:58:44 +10:00
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
} catch (Error err) {
|
|
|
|
|
|
assert_error(open.throw_error, err);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-04-10 14:55:18 +10:00
|
|
|
|
assert_false(monitor.is_monitoring, "is monitoring");
|
|
|
|
|
|
|
2018-04-04 15:58:44 +10:00
|
|
|
|
this.base_folder.assert_expectations();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-04-10 14:55:18 +10:00
|
|
|
|
public void close_during_open_error() throws GLib.Error {
|
|
|
|
|
|
ConversationMonitor monitor = new ConversationMonitor(
|
|
|
|
|
|
this.base_folder, Email.Field.NONE, 10
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2020-05-09 16:04:22 +10:00
|
|
|
|
ValaUnit.ExpectedCall open = this.base_folder
|
2020-04-10 14:55:18 +10:00
|
|
|
|
.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"));
|
|
|
|
|
|
|
2020-05-09 16:04:22 +10:00
|
|
|
|
var start_waiter = new ValaUnit.AsyncResultWaiter(this.main_loop);
|
2020-04-10 14:55:18 +10:00
|
|
|
|
monitor.start_monitoring.begin(NONE, null, start_waiter.async_completion);
|
|
|
|
|
|
|
2020-05-09 16:04:22 +10:00
|
|
|
|
var stop_waiter = new ValaUnit.AsyncResultWaiter(this.main_loop);
|
2020-04-10 14:55:18 +10:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// base_folder.close_async should not be called, so should not
|
|
|
|
|
|
// throw an error
|
|
|
|
|
|
monitor.stop_monitoring.end(stop_waiter.async_result());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void close_after_open_error() throws GLib.Error {
|
|
|
|
|
|
ConversationMonitor monitor = new ConversationMonitor(
|
|
|
|
|
|
this.base_folder, Email.Field.NONE, 10
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2020-05-09 16:04:22 +10:00
|
|
|
|
ValaUnit.ExpectedCall open = this.base_folder
|
2020-04-10 14:55:18 +10:00
|
|
|
|
.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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 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.end(async_result());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-04-04 15:58:44 +10:00
|
|
|
|
public void load_single_message() throws Error {
|
|
|
|
|
|
Email e1 = setup_email(1);
|
|
|
|
|
|
|
|
|
|
|
|
Gee.MultiMap<EmailIdentifier,FolderPath> paths =
|
|
|
|
|
|
new Gee.HashMultiMap<EmailIdentifier,FolderPath>();
|
|
|
|
|
|
paths.set(e1.id, this.base_folder.path);
|
|
|
|
|
|
|
|
|
|
|
|
ConversationMonitor monitor = setup_monitor({e1}, paths);
|
|
|
|
|
|
|
2020-05-09 16:04:22 +10:00
|
|
|
|
assert_equal<int?>(monitor.size, 1, "Conversation count");
|
2018-04-04 15:58:44 +10:00
|
|
|
|
assert_non_null(monitor.window_lowest, "Lowest window id");
|
2020-05-09 16:04:22 +10:00
|
|
|
|
assert_equal(monitor.window_lowest, e1.id, "Lowest window id");
|
2018-04-04 15:58:44 +10:00
|
|
|
|
|
2019-11-19 14:20:42 +11:00
|
|
|
|
Conversation c1 = Collection.first(monitor.read_only_view);
|
2018-04-04 15:58:44 +10:00
|
|
|
|
assert_equal(e1, c1.get_email_by_id(e1.id), "Email not present in conversation");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void load_multiple_messages() throws Error {
|
|
|
|
|
|
Email e1 = setup_email(1, null);
|
|
|
|
|
|
Email e2 = setup_email(2, null);
|
|
|
|
|
|
Email e3 = setup_email(3, null);
|
|
|
|
|
|
|
|
|
|
|
|
Gee.MultiMap<EmailIdentifier,FolderPath> paths =
|
|
|
|
|
|
new Gee.HashMultiMap<EmailIdentifier,FolderPath>();
|
|
|
|
|
|
paths.set(e1.id, this.base_folder.path);
|
|
|
|
|
|
paths.set(e2.id, this.base_folder.path);
|
|
|
|
|
|
paths.set(e3.id, this.base_folder.path);
|
|
|
|
|
|
|
|
|
|
|
|
ConversationMonitor monitor = setup_monitor({e3, e2, e1}, paths);
|
|
|
|
|
|
|
2020-05-09 16:04:22 +10:00
|
|
|
|
assert_equal<int?>(monitor.size, 3, "Conversation count");
|
2018-04-04 15:58:44 +10:00
|
|
|
|
assert_non_null(monitor.window_lowest, "Lowest window id");
|
2020-05-09 16:04:22 +10:00
|
|
|
|
assert_equal(monitor.window_lowest, e1.id, "Lowest window id");
|
2018-04-04 15:58:44 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void load_related_message() throws Error {
|
|
|
|
|
|
Email e1 = setup_email(1);
|
|
|
|
|
|
Email e2 = setup_email(2, e1);
|
|
|
|
|
|
|
|
|
|
|
|
Gee.MultiMap<EmailIdentifier,FolderPath> paths =
|
|
|
|
|
|
new Gee.HashMultiMap<EmailIdentifier,FolderPath>();
|
|
|
|
|
|
paths.set(e1.id, this.other_folder.path);
|
|
|
|
|
|
paths.set(e2.id, this.base_folder.path);
|
|
|
|
|
|
|
|
|
|
|
|
Gee.MultiMap<Email,FolderPath> related_paths =
|
|
|
|
|
|
new Gee.HashMultiMap<Email,FolderPath>();
|
|
|
|
|
|
related_paths.set(e1, this.other_folder.path);
|
|
|
|
|
|
related_paths.set(e2, this.base_folder.path);
|
|
|
|
|
|
|
|
|
|
|
|
ConversationMonitor monitor = setup_monitor({e2}, paths, {related_paths});
|
|
|
|
|
|
|
2020-05-09 16:04:22 +10:00
|
|
|
|
assert_equal<int?>(monitor.size, 1, "Conversation count");
|
2018-04-04 15:58:44 +10:00
|
|
|
|
assert_non_null(monitor.window_lowest, "Lowest window id");
|
2020-05-09 16:04:22 +10:00
|
|
|
|
assert_equal(monitor.window_lowest, e2.id, "Lowest window id");
|
2018-04-04 15:58:44 +10:00
|
|
|
|
|
2019-11-19 14:20:42 +11:00
|
|
|
|
Conversation c1 = Collection.first(monitor.read_only_view);
|
2020-05-09 16:04:22 +10:00
|
|
|
|
assert_equal(c1.get_email_by_id(e1.id), e1, "Related email not present in conversation");
|
|
|
|
|
|
assert_equal(c1.get_email_by_id(e2.id), e2, "In folder not present in conversation");
|
2018-04-04 15:58:44 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void base_folder_message_appended() throws Error {
|
|
|
|
|
|
Email e1 = setup_email(1);
|
|
|
|
|
|
|
|
|
|
|
|
Gee.MultiMap<EmailIdentifier,FolderPath> paths =
|
|
|
|
|
|
new Gee.HashMultiMap<EmailIdentifier,FolderPath>();
|
|
|
|
|
|
paths.set(e1.id, this.base_folder.path);
|
|
|
|
|
|
|
|
|
|
|
|
ConversationMonitor monitor = setup_monitor();
|
2020-05-09 16:04:22 +10:00
|
|
|
|
assert_equal<int?>(monitor.size, 0, "Initial conversation count");
|
2018-04-04 15:58:44 +10:00
|
|
|
|
|
|
|
|
|
|
this.base_folder.expect_call("list_email_by_sparse_id_async")
|
|
|
|
|
|
.returns_object(new Gee.ArrayList<Email>.wrap({e1}));
|
|
|
|
|
|
|
|
|
|
|
|
this.account.expect_call("get_special_folder");
|
|
|
|
|
|
this.account.expect_call("get_special_folder");
|
|
|
|
|
|
this.account.expect_call("get_special_folder");
|
|
|
|
|
|
this.account.expect_call("local_search_message_id_async");
|
|
|
|
|
|
|
|
|
|
|
|
this.account.expect_call("get_containing_folders_async")
|
|
|
|
|
|
.returns_object(paths);
|
|
|
|
|
|
|
|
|
|
|
|
this.base_folder.email_appended(new Gee.ArrayList<EmailIdentifier>.wrap({e1.id}));
|
|
|
|
|
|
|
|
|
|
|
|
wait_for_signal(monitor, "conversations-added");
|
|
|
|
|
|
this.base_folder.assert_expectations();
|
|
|
|
|
|
this.account.assert_expectations();
|
|
|
|
|
|
|
2020-05-09 16:04:22 +10:00
|
|
|
|
assert_equal<int?>(monitor.size, 1, "Conversation count");
|
2018-04-04 15:58:44 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void base_folder_message_removed() throws Error {
|
|
|
|
|
|
Email e1 = setup_email(1);
|
|
|
|
|
|
Email e2 = setup_email(2, e1);
|
|
|
|
|
|
Email e3 = setup_email(3);
|
|
|
|
|
|
|
|
|
|
|
|
Gee.MultiMap<EmailIdentifier,FolderPath> paths =
|
|
|
|
|
|
new Gee.HashMultiMap<EmailIdentifier,FolderPath>();
|
|
|
|
|
|
paths.set(e1.id, this.other_folder.path);
|
|
|
|
|
|
paths.set(e2.id, this.base_folder.path);
|
|
|
|
|
|
paths.set(e3.id, this.base_folder.path);
|
|
|
|
|
|
|
|
|
|
|
|
Gee.MultiMap<Email,FolderPath> e2_related_paths =
|
|
|
|
|
|
new Gee.HashMultiMap<Email,FolderPath>();
|
|
|
|
|
|
e2_related_paths.set(e1, this.other_folder.path);
|
|
|
|
|
|
e2_related_paths.set(e2, this.base_folder.path);
|
|
|
|
|
|
|
|
|
|
|
|
ConversationMonitor monitor = setup_monitor(
|
|
|
|
|
|
{e3, e2}, paths, {null, e2_related_paths}
|
|
|
|
|
|
);
|
2020-05-09 16:04:22 +10:00
|
|
|
|
assert_equal<int?>(monitor.size, 2, "Initial conversation count");
|
|
|
|
|
|
assert_equal(monitor.window_lowest, e2.id, "Lowest window id");
|
2018-04-04 15:58:44 +10:00
|
|
|
|
|
|
|
|
|
|
this.base_folder.email_removed(new Gee.ArrayList<EmailIdentifier>.wrap({e2.id}));
|
|
|
|
|
|
wait_for_signal(monitor, "conversations-removed");
|
2020-05-09 16:04:22 +10:00
|
|
|
|
assert_equal<int?>(monitor.size, 1, "Conversation count");
|
|
|
|
|
|
assert_equal(monitor.window_lowest, e3.id, "Lowest window id");
|
2018-04-04 15:58:44 +10:00
|
|
|
|
|
|
|
|
|
|
this.base_folder.email_removed(new Gee.ArrayList<EmailIdentifier>.wrap({e3.id}));
|
|
|
|
|
|
wait_for_signal(monitor, "conversations-removed");
|
2020-05-09 16:04:22 +10:00
|
|
|
|
assert_equal<int?>(monitor.size, 0, "Conversation count");
|
2018-04-04 15:58:44 +10:00
|
|
|
|
assert_null(monitor.window_lowest, "Lowest window id");
|
|
|
|
|
|
|
|
|
|
|
|
// Close the monitor to cancel the final load so it does not
|
|
|
|
|
|
// error out during later tests
|
|
|
|
|
|
this.base_folder.expect_call("close_async");
|
2019-11-05 09:30:15 +11:00
|
|
|
|
monitor.stop_monitoring.begin(
|
2020-04-10 12:58:09 +10:00
|
|
|
|
null, this.async_completion
|
2018-04-04 15:58:44 +10:00
|
|
|
|
);
|
2019-11-05 09:30:15 +11:00
|
|
|
|
monitor.stop_monitoring.end(async_result());
|
2018-04-04 15:58:44 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void external_folder_message_appended() throws Error {
|
|
|
|
|
|
Email e1 = setup_email(1);
|
|
|
|
|
|
Email e2 = setup_email(2, e1);
|
|
|
|
|
|
Email e3 = setup_email(3, e1);
|
|
|
|
|
|
|
|
|
|
|
|
Gee.MultiMap<EmailIdentifier,FolderPath> paths =
|
|
|
|
|
|
new Gee.HashMultiMap<EmailIdentifier,FolderPath>();
|
|
|
|
|
|
paths.set(e1.id, this.base_folder.path);
|
|
|
|
|
|
paths.set(e2.id, this.base_folder.path);
|
|
|
|
|
|
paths.set(e3.id, this.other_folder.path);
|
|
|
|
|
|
|
|
|
|
|
|
Gee.MultiMap<Email,FolderPath> related_paths =
|
|
|
|
|
|
new Gee.HashMultiMap<Email,FolderPath>();
|
|
|
|
|
|
related_paths.set(e1, this.base_folder.path);
|
|
|
|
|
|
related_paths.set(e3, this.other_folder.path);
|
|
|
|
|
|
|
|
|
|
|
|
ConversationMonitor monitor = setup_monitor({e1}, paths);
|
2020-05-09 16:04:22 +10:00
|
|
|
|
assert_equal<int?>(monitor.size, 1, "Initial conversation count");
|
2018-04-04 15:58:44 +10:00
|
|
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
|
this.account.expect_call("get_special_folder");
|
|
|
|
|
|
this.account.expect_call("get_special_folder");
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
|
|
// First call to expand_conversations_async for e3's refs
|
|
|
|
|
|
|
|
|
|
|
|
// LocalSearchOperationAppendOperation's blacklist check
|
|
|
|
|
|
this.account.expect_call("get_special_folder");
|
|
|
|
|
|
this.account.expect_call("get_special_folder");
|
|
|
|
|
|
this.account.expect_call("get_special_folder");
|
|
|
|
|
|
|
|
|
|
|
|
// Search for e1's ref
|
|
|
|
|
|
this.account.expect_call("local_search_message_id_async")
|
|
|
|
|
|
.returns_object(related_paths);
|
|
|
|
|
|
|
|
|
|
|
|
// Search for e2's ref
|
|
|
|
|
|
this.account.expect_call("local_search_message_id_async");
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////
|
|
|
|
|
|
// Second call to expand_conversations_async for e1's refs
|
|
|
|
|
|
|
|
|
|
|
|
this.account.expect_call("get_special_folder");
|
|
|
|
|
|
this.account.expect_call("get_special_folder");
|
|
|
|
|
|
this.account.expect_call("get_special_folder");
|
|
|
|
|
|
this.account.expect_call("local_search_message_id_async");
|
|
|
|
|
|
|
|
|
|
|
|
// Finally, the call to process_email_complete_async
|
|
|
|
|
|
|
|
|
|
|
|
this.account.expect_call("get_containing_folders_async")
|
|
|
|
|
|
.returns_object(paths);
|
|
|
|
|
|
|
|
|
|
|
|
// Should not be added, since it's actually in the base folder
|
|
|
|
|
|
this.account.email_appended(
|
|
|
|
|
|
this.base_folder,
|
|
|
|
|
|
new Gee.ArrayList<EmailIdentifier>.wrap({e2.id})
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// Should be added, since it's an external message
|
|
|
|
|
|
this.account.email_appended(
|
|
|
|
|
|
this.other_folder,
|
|
|
|
|
|
new Gee.ArrayList<EmailIdentifier>.wrap({e3.id})
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
wait_for_signal(monitor, "conversations-added");
|
|
|
|
|
|
this.base_folder.assert_expectations();
|
|
|
|
|
|
this.other_folder.assert_expectations();
|
|
|
|
|
|
this.account.assert_expectations();
|
|
|
|
|
|
|
2020-05-09 16:04:22 +10:00
|
|
|
|
assert_equal<int?>(monitor.size, 1, "Conversation count");
|
2018-04-04 15:58:44 +10:00
|
|
|
|
|
2019-11-19 14:20:42 +11:00
|
|
|
|
Conversation c1 = Collection.first(monitor.read_only_view);
|
2020-05-09 16:04:22 +10:00
|
|
|
|
assert_equal<int?>(c1.get_count(), 2, "Conversation message count");
|
|
|
|
|
|
assert_equal(c1.get_email_by_id(e3.id), e3,
|
2018-04-04 15:58:44 +10:00
|
|
|
|
"Appended email not present in conversation");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-10 02:01:56 -07:00
|
|
|
|
public void conversation_marked_as_deleted() throws Error {
|
|
|
|
|
|
Email e1 = setup_email(1);
|
|
|
|
|
|
|
|
|
|
|
|
Gee.MultiMap<EmailIdentifier,FolderPath> paths =
|
|
|
|
|
|
new Gee.HashMultiMap<EmailIdentifier,FolderPath>();
|
|
|
|
|
|
paths.set(e1.id, this.base_folder.path);
|
|
|
|
|
|
|
|
|
|
|
|
ConversationMonitor monitor = setup_monitor({e1}, paths);
|
2020-05-09 16:04:22 +10:00
|
|
|
|
assert_equal<int?>(monitor.size, 1, "Conversation count");
|
2018-10-10 02:01:56 -07:00
|
|
|
|
|
|
|
|
|
|
// Mark message as deleted
|
|
|
|
|
|
Gee.HashMap<EmailIdentifier,EmailFlags> flags_changed =
|
|
|
|
|
|
new Gee.HashMap<EmailIdentifier,EmailFlags>();
|
|
|
|
|
|
flags_changed.set(e1.id, new EmailFlags.with(EmailFlags.DELETED));
|
|
|
|
|
|
this.account.email_flags_changed(this.base_folder, flags_changed);
|
2019-02-28 09:38:56 +01:00
|
|
|
|
|
2018-10-17 23:51:18 -07:00
|
|
|
|
this.base_folder.expect_call("list_email_by_sparse_id_async");
|
|
|
|
|
|
this.base_folder.expect_call("list_email_by_id_async");
|
2018-10-10 02:01:56 -07:00
|
|
|
|
|
2019-02-28 09:38:56 +01:00
|
|
|
|
wait_for_signal(monitor, "email-flags-changed");
|
2018-10-10 02:01:56 -07:00
|
|
|
|
|
2020-05-09 16:04:22 +10:00
|
|
|
|
assert_equal<int?>(
|
|
|
|
|
|
monitor.size, 0,
|
|
|
|
|
|
"Conversation count should now be zero after being marked deleted."
|
|
|
|
|
|
);
|
2018-10-10 02:01:56 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-04-04 15:58:44 +10:00
|
|
|
|
private Email setup_email(int id, Email? references = null) {
|
|
|
|
|
|
Email email = new Email(new MockEmailIdentifer(id));
|
|
|
|
|
|
DateTime now = new DateTime.now_local();
|
|
|
|
|
|
Geary.RFC822.MessageID mid = new Geary.RFC822.MessageID(
|
|
|
|
|
|
"test%d@localhost".printf(id)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
Geary.RFC822.MessageIDList refs_list = null;
|
|
|
|
|
|
if (references != null) {
|
|
|
|
|
|
refs_list = new Geary.RFC822.MessageIDList.single(
|
|
|
|
|
|
references.message_id
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2020-05-06 11:27:00 +10:00
|
|
|
|
email.set_send_date(new RFC822.Date(now));
|
2018-04-04 15:58:44 +10:00
|
|
|
|
email.set_email_properties(new MockEmailProperties(now));
|
|
|
|
|
|
email.set_full_references(mid, null, refs_list);
|
|
|
|
|
|
return email;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private ConversationMonitor
|
|
|
|
|
|
setup_monitor(Email[] base_folder_email = {},
|
|
|
|
|
|
Gee.MultiMap<EmailIdentifier,FolderPath>? paths = null,
|
|
|
|
|
|
Gee.MultiMap<Email,FolderPath>[] related_paths = {})
|
|
|
|
|
|
throws Error {
|
|
|
|
|
|
ConversationMonitor monitor = new ConversationMonitor(
|
2019-11-05 09:30:15 +11:00
|
|
|
|
this.base_folder, Email.Field.NONE, 10
|
2018-04-04 15:58:44 +10:00
|
|
|
|
);
|
|
|
|
|
|
Cancellable test_cancellable = new Cancellable();
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* The process for loading messages looks roughly like this:
|
|
|
|
|
|
* - load_by_id_async
|
|
|
|
|
|
* - base_folder.list_email_by_id_async
|
|
|
|
|
|
* - process_email_async
|
|
|
|
|
|
* - gets all related messages from listing
|
|
|
|
|
|
* - expand_conversations_async
|
|
|
|
|
|
* - get_search_folder_blacklist (i.e. account.get_special_folder × 3)
|
|
|
|
|
|
* - foreach related: account.local_search_message_id_async
|
|
|
|
|
|
* - process_email_async
|
|
|
|
|
|
* - process_email_complete_async
|
|
|
|
|
|
* - get_containing_folders_async
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
this.base_folder.expect_call("open_async");
|
2020-05-09 16:04:22 +10:00
|
|
|
|
ValaUnit.ExpectedCall list_call = this.base_folder
|
2018-04-04 15:58:44 +10:00
|
|
|
|
.expect_call("list_email_by_id_async")
|
|
|
|
|
|
.returns_object(new Gee.ArrayList<Email>.wrap(base_folder_email));
|
|
|
|
|
|
|
|
|
|
|
|
if (base_folder_email.length > 0) {
|
|
|
|
|
|
// expand_conversations_async calls
|
|
|
|
|
|
// Account:get_special_folder() in
|
|
|
|
|
|
// get_search_folder_blacklist, and the default
|
|
|
|
|
|
// implementation of that calls get_special_folder.
|
|
|
|
|
|
this.account.expect_call("get_special_folder");
|
|
|
|
|
|
this.account.expect_call("get_special_folder");
|
|
|
|
|
|
this.account.expect_call("get_special_folder");
|
|
|
|
|
|
|
|
|
|
|
|
Gee.List<RFC822.MessageID> base_email_ids =
|
|
|
|
|
|
new Gee.ArrayList<RFC822.MessageID>();
|
|
|
|
|
|
foreach (Email base_email in base_folder_email) {
|
|
|
|
|
|
base_email_ids.add(base_email.message_id);
|
|
|
|
|
|
}
|
2019-02-28 09:38:56 +01:00
|
|
|
|
|
2018-04-04 15:58:44 +10:00
|
|
|
|
int base_i = 0;
|
|
|
|
|
|
bool has_related = (
|
|
|
|
|
|
base_folder_email.length == related_paths.length
|
|
|
|
|
|
);
|
|
|
|
|
|
bool found_related = false;
|
|
|
|
|
|
Gee.Set<RFC822.MessageID> seen_ids = new Gee.HashSet<RFC822.MessageID>();
|
|
|
|
|
|
foreach (Email base_email in base_folder_email) {
|
2020-05-09 16:04:22 +10:00
|
|
|
|
ValaUnit.ExpectedCall call =
|
2018-04-04 15:58:44 +10:00
|
|
|
|
this.account.expect_call("local_search_message_id_async");
|
|
|
|
|
|
seen_ids.add(base_email.message_id);
|
|
|
|
|
|
if (has_related && related_paths[base_i] != null) {
|
|
|
|
|
|
call.returns_object(related_paths[base_i++]);
|
|
|
|
|
|
found_related = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach (RFC822.MessageID ancestor in base_email.get_ancestors()) {
|
|
|
|
|
|
if (!seen_ids.contains(ancestor) && !base_email_ids.contains(ancestor)) {
|
|
|
|
|
|
this.account.expect_call("local_search_message_id_async");
|
|
|
|
|
|
seen_ids.add(ancestor);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Second call to expand_conversations_async will be made
|
|
|
|
|
|
// if any related were loaded
|
|
|
|
|
|
if (found_related) {
|
|
|
|
|
|
this.account.expect_call("get_special_folder");
|
|
|
|
|
|
this.account.expect_call("get_special_folder");
|
|
|
|
|
|
this.account.expect_call("get_special_folder");
|
|
|
|
|
|
|
|
|
|
|
|
seen_ids.clear();
|
|
|
|
|
|
foreach (Gee.MultiMap<Email,FolderPath> related in related_paths) {
|
|
|
|
|
|
if (related != null) {
|
|
|
|
|
|
foreach (Email email in related.get_keys()) {
|
|
|
|
|
|
if (!base_email_ids.contains(email.message_id)) {
|
|
|
|
|
|
foreach (RFC822.MessageID ancestor in email.get_ancestors()) {
|
|
|
|
|
|
if (!seen_ids.contains(ancestor)) {
|
|
|
|
|
|
this.account.expect_call("local_search_message_id_async");
|
|
|
|
|
|
seen_ids.add(ancestor);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-05-09 16:04:22 +10:00
|
|
|
|
ValaUnit.ExpectedCall contains =
|
|
|
|
|
|
this.account.expect_call("get_containing_folders_async");
|
2018-04-04 15:58:44 +10:00
|
|
|
|
if (paths != null) {
|
|
|
|
|
|
contains.returns_object(paths);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-11-05 09:30:15 +11:00
|
|
|
|
monitor.start_monitoring.begin(
|
2020-04-10 12:58:09 +10:00
|
|
|
|
NONE, test_cancellable, this.async_completion
|
2018-04-04 15:58:44 +10:00
|
|
|
|
);
|
2019-11-05 09:30:15 +11:00
|
|
|
|
monitor.start_monitoring.end(async_result());
|
2018-04-04 15:58:44 +10:00
|
|
|
|
|
|
|
|
|
|
if (base_folder_email.length == 0) {
|
|
|
|
|
|
wait_for_call(list_call);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
wait_for_signal(monitor, "conversations-added");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.base_folder.assert_expectations();
|
|
|
|
|
|
this.account.assert_expectations();
|
|
|
|
|
|
|
|
|
|
|
|
return monitor;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|