Merge branch 'mjog/imap-connection-fixes' into 'mainline'
IMAP connection fixes See merge request GNOME/geary!479
This commit is contained in:
commit
297a59ca80
28 changed files with 1440 additions and 757 deletions
|
|
@ -84,7 +84,7 @@ class Geary.ImapDB.AccountTest : TestCase {
|
|||
new Imap.UIDValidity(7),
|
||||
6 //unseen
|
||||
),
|
||||
new Imap.Capabilities(1)
|
||||
new Imap.Capabilities.empty(0)
|
||||
)
|
||||
);
|
||||
|
||||
|
|
@ -123,7 +123,7 @@ class Geary.ImapDB.AccountTest : TestCase {
|
|||
new Imap.UIDValidity(7),
|
||||
6 //unseen
|
||||
),
|
||||
new Imap.Capabilities(1)
|
||||
new Imap.Capabilities.empty(0)
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
|||
160
test/engine/imap/transport/imap-client-connection-test.vala
Normal file
160
test/engine/imap/transport/imap-client-connection-test.vala
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright 2019 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.Imap.ClientConnectionTest : TestCase {
|
||||
|
||||
|
||||
private class TestCommand : Command {
|
||||
|
||||
public TestCommand() {
|
||||
base("TEST");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private TestServer? server = null;
|
||||
|
||||
|
||||
public ClientConnectionTest() {
|
||||
base("Geary.Imap.ClientConnectionTest");
|
||||
add_test("connect_disconnect", connect_disconnect);
|
||||
if (GLib.Test.slow()) {
|
||||
add_test("idle", idle);
|
||||
add_test("command_timeout", command_timeout);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void set_up() throws GLib.Error {
|
||||
this.server = new TestServer();
|
||||
}
|
||||
|
||||
protected override void tear_down() {
|
||||
this.server.stop();
|
||||
this.server = null;
|
||||
}
|
||||
|
||||
public void connect_disconnect() throws GLib.Error {
|
||||
var test_article = new ClientConnection(new_endpoint());
|
||||
|
||||
test_article.connect_async.begin(null, this.async_complete_full);
|
||||
test_article.connect_async.end(async_result());
|
||||
|
||||
assert_non_null(test_article.get_remote_address());
|
||||
assert_non_null(test_article.get_local_address());
|
||||
|
||||
test_article.disconnect_async.begin(null, this.async_complete_full);
|
||||
test_article.disconnect_async.end(async_result());
|
||||
|
||||
assert_null(test_article.get_remote_address());
|
||||
assert_null(test_article.get_local_address());
|
||||
|
||||
TestServer.Result result = this.server.wait_for_script(this.main_loop);
|
||||
assert(result.succeeded);
|
||||
}
|
||||
|
||||
public void idle() throws GLib.Error {
|
||||
this.server.add_script_line(RECEIVE_LINE, "a001 IDLE");
|
||||
this.server.add_script_line(SEND_LINE, "+ idling");
|
||||
this.server.add_script_line(RECEIVE_LINE, "DONE");
|
||||
this.server.add_script_line(SEND_LINE, "a001 OK Completed");
|
||||
this.server.add_script_line(RECEIVE_LINE, "a002 TEST");
|
||||
this.server.add_script_line(SEND_LINE, "a002 OK Looks good");
|
||||
this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
|
||||
|
||||
const int COMMAND_TIMEOUT = 1;
|
||||
const int IDLE_TIMEOUT = 1;
|
||||
|
||||
var test_article = new ClientConnection(
|
||||
new_endpoint(), COMMAND_TIMEOUT, IDLE_TIMEOUT
|
||||
);
|
||||
test_article.connect_async.begin(null, this.async_complete_full);
|
||||
test_article.connect_async.end(async_result());
|
||||
|
||||
assert_false(test_article.is_in_idle(), "Initial idle state");
|
||||
test_article.enable_idle_when_quiet(true);
|
||||
assert_false(test_article.is_in_idle(), "Post-enabled idle state");
|
||||
|
||||
// Wait for idle to kick in
|
||||
GLib.Timer timer = new GLib.Timer();
|
||||
timer.start();
|
||||
while (!test_article.is_in_idle() &&
|
||||
timer.elapsed() < IDLE_TIMEOUT * 2) {
|
||||
this.main_loop.iteration(false);
|
||||
}
|
||||
|
||||
assert_true(test_article.is_in_idle(), "Entered idle");
|
||||
|
||||
// Ensure idle outlives command timeout
|
||||
timer.start();
|
||||
while (timer.elapsed() < COMMAND_TIMEOUT * 2) {
|
||||
this.main_loop.iteration(false);
|
||||
}
|
||||
|
||||
assert_true(test_article.is_in_idle(), "Post idle command timeout");
|
||||
|
||||
var command = new TestCommand();
|
||||
test_article.send_command(command);
|
||||
command.wait_until_complete.begin(null, this.async_complete_full);
|
||||
command.wait_until_complete.end(async_result());
|
||||
|
||||
assert_false(test_article.is_in_idle(), "Post test command");
|
||||
|
||||
test_article.disconnect_async.begin(null, this.async_complete_full);
|
||||
test_article.disconnect_async.end(async_result());
|
||||
|
||||
TestServer.Result result = this.server.wait_for_script(this.main_loop);
|
||||
assert(result.succeeded);
|
||||
}
|
||||
|
||||
public void command_timeout() throws GLib.Error {
|
||||
this.server.add_script_line(
|
||||
SEND_LINE, "* OK localhost test server ready"
|
||||
);
|
||||
this.server.add_script_line(RECEIVE_LINE, "a001 TEST");
|
||||
this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
|
||||
|
||||
const int TIMEOUT = 2;
|
||||
|
||||
bool sent = false;
|
||||
bool recv_fail = false;
|
||||
bool timed_out = false;
|
||||
|
||||
var test_article = new ClientConnection(new_endpoint(), TIMEOUT);
|
||||
test_article.sent_command.connect(() => { sent = true; });
|
||||
test_article.receive_failure.connect(() => { recv_fail = true; });
|
||||
test_article.connect_async.begin(null, this.async_complete_full);
|
||||
test_article.connect_async.end(async_result());
|
||||
|
||||
var command = new TestCommand();
|
||||
command.response_timed_out.connect(() => { timed_out = true; });
|
||||
|
||||
test_article.send_command(command);
|
||||
|
||||
GLib.Timer timer = new GLib.Timer();
|
||||
timer.start();
|
||||
while (!timed_out && timer.elapsed() < TIMEOUT * 2) {
|
||||
this.main_loop.iteration(false);
|
||||
}
|
||||
|
||||
test_article.disconnect_async.begin(null, this.async_complete_full);
|
||||
test_article.disconnect_async.end(async_result());
|
||||
|
||||
assert_true(sent, "connection.sent_command");
|
||||
assert_true(recv_fail, "command.receive_failure");
|
||||
assert_true(timed_out, "command.response_timed_out");
|
||||
|
||||
debug("Waiting for server...");
|
||||
|
||||
TestServer.Result result = this.server.wait_for_script(this.main_loop);
|
||||
assert_true(result.succeeded);
|
||||
}
|
||||
|
||||
protected Endpoint new_endpoint() {
|
||||
return new Endpoint(this.server.get_client_address(), NONE, 10);
|
||||
}
|
||||
|
||||
}
|
||||
415
test/engine/imap/transport/imap-client-session-test.vala
Normal file
415
test/engine/imap/transport/imap-client-session-test.vala
Normal file
|
|
@ -0,0 +1,415 @@
|
|||
/*
|
||||
* Copyright 2019 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.Imap.ClientSessionTest : TestCase {
|
||||
|
||||
private const uint CONNECT_TIMEOUT = 2;
|
||||
|
||||
private TestServer? server = null;
|
||||
|
||||
|
||||
public ClientSessionTest() {
|
||||
base("Geary.Imap.ClientSessionTest");
|
||||
add_test("connect_disconnect", connect_disconnect);
|
||||
add_test("connect_with_capabilities", connect_with_capabilities);
|
||||
if (GLib.Test.slow()) {
|
||||
add_test("connect_timeout", connect_timeout);
|
||||
}
|
||||
add_test("login", login);
|
||||
add_test("login_with_capabilities", login_with_capabilities);
|
||||
add_test("logout", logout);
|
||||
add_test("login_logout", login_logout);
|
||||
add_test("initiate_request_capabilities", initiate_request_capabilities);
|
||||
add_test("initiate_implicit_capabilities", initiate_implicit_capabilities);
|
||||
add_test("initiate_namespace", initiate_namespace);
|
||||
}
|
||||
|
||||
protected override void set_up() throws GLib.Error {
|
||||
this.server = new TestServer();
|
||||
}
|
||||
|
||||
protected override void tear_down() {
|
||||
this.server.stop();
|
||||
this.server = null;
|
||||
}
|
||||
|
||||
public void connect_disconnect() throws GLib.Error {
|
||||
this.server.add_script_line(
|
||||
SEND_LINE, "* OK localhost test server ready"
|
||||
);
|
||||
this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
|
||||
|
||||
var test_article = new ClientSession(new_endpoint());
|
||||
assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
|
||||
|
||||
test_article.connect_async.begin(
|
||||
CONNECT_TIMEOUT, null, this.async_complete_full
|
||||
);
|
||||
test_article.connect_async.end(async_result());
|
||||
assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
|
||||
|
||||
test_article.disconnect_async.begin(null, this.async_complete_full);
|
||||
test_article.disconnect_async.end(async_result());
|
||||
assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
|
||||
|
||||
TestServer.Result result = this.server.wait_for_script(this.main_loop);
|
||||
assert_true(
|
||||
result.succeeded,
|
||||
result.error != null ? result.error.message : "Server result failed"
|
||||
);
|
||||
}
|
||||
|
||||
public void connect_with_capabilities() throws GLib.Error {
|
||||
this.server.add_script_line(
|
||||
SEND_LINE, "* OK [CAPABILITY IMAP4rev1] localhost test server ready"
|
||||
);
|
||||
this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
|
||||
|
||||
var test_article = new ClientSession(new_endpoint());
|
||||
test_article.connect_async.begin(
|
||||
CONNECT_TIMEOUT, null, this.async_complete_full
|
||||
);
|
||||
test_article.connect_async.end(async_result());
|
||||
|
||||
assert_true(test_article.capabilities.supports_imap4rev1());
|
||||
|
||||
test_article.disconnect_async.begin(null, this.async_complete_full);
|
||||
test_article.disconnect_async.end(async_result());
|
||||
|
||||
TestServer.Result result = this.server.wait_for_script(this.main_loop);
|
||||
assert_true(result.succeeded);
|
||||
}
|
||||
|
||||
public void connect_timeout() throws GLib.Error {
|
||||
this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
|
||||
|
||||
var test_article = new ClientSession(new_endpoint());
|
||||
|
||||
GLib.Timer timer = new GLib.Timer();
|
||||
timer.start();
|
||||
test_article.connect_async.begin(
|
||||
CONNECT_TIMEOUT, null, this.async_complete_full
|
||||
);
|
||||
try {
|
||||
test_article.connect_async.end(async_result());
|
||||
assert_not_reached();
|
||||
} catch (GLib.IOError.TIMED_OUT err) {
|
||||
assert_double(timer.elapsed(), CONNECT_TIMEOUT, CONNECT_TIMEOUT * 0.5);
|
||||
}
|
||||
|
||||
TestServer.Result result = this.server.wait_for_script(this.main_loop);
|
||||
assert_true(result.succeeded);
|
||||
}
|
||||
|
||||
public void login_with_capabilities() throws GLib.Error {
|
||||
this.server.add_script_line(
|
||||
SEND_LINE, "* OK localhost test server ready"
|
||||
);
|
||||
this.server.add_script_line(RECEIVE_LINE, "a001 login test password");
|
||||
this.server.add_script_line(
|
||||
SEND_LINE, "a001 OK [CAPABILITY IMAP4rev1] ohhai"
|
||||
);
|
||||
this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
|
||||
|
||||
var test_article = new ClientSession(new_endpoint());
|
||||
test_article.connect_async.begin(
|
||||
CONNECT_TIMEOUT, null, this.async_complete_full
|
||||
);
|
||||
test_article.connect_async.end(async_result());
|
||||
test_article.login_async.begin(
|
||||
new Credentials(PASSWORD, "test", "password"),
|
||||
null,
|
||||
this.async_complete_full
|
||||
);
|
||||
test_article.login_async.end(async_result());
|
||||
|
||||
assert_true(test_article.capabilities.supports_imap4rev1());
|
||||
|
||||
test_article.disconnect_async.begin(null, this.async_complete_full);
|
||||
test_article.disconnect_async.end(async_result());
|
||||
|
||||
TestServer.Result result = this.server.wait_for_script(this.main_loop);
|
||||
assert_true(
|
||||
result.succeeded,
|
||||
result.error != null ? result.error.message : "Server result failed"
|
||||
);
|
||||
}
|
||||
|
||||
public void login() throws GLib.Error {
|
||||
this.server.add_script_line(
|
||||
SEND_LINE, "* OK localhost test server ready"
|
||||
);
|
||||
this.server.add_script_line(RECEIVE_LINE, "a001 login test password");
|
||||
this.server.add_script_line(SEND_LINE, "a001 OK ohhai");
|
||||
this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
|
||||
|
||||
var test_article = new ClientSession(new_endpoint());
|
||||
assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
|
||||
|
||||
test_article.connect_async.begin(
|
||||
CONNECT_TIMEOUT, null, this.async_complete_full
|
||||
);
|
||||
test_article.connect_async.end(async_result());
|
||||
assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
|
||||
|
||||
test_article.login_async.begin(
|
||||
new Credentials(PASSWORD, "test", "password"),
|
||||
null,
|
||||
this.async_complete_full
|
||||
);
|
||||
test_article.login_async.end(async_result());
|
||||
assert_true(test_article.get_protocol_state() == AUTHORIZED);
|
||||
|
||||
test_article.disconnect_async.begin(null, this.async_complete_full);
|
||||
test_article.disconnect_async.end(async_result());
|
||||
assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
|
||||
|
||||
TestServer.Result result = this.server.wait_for_script(this.main_loop);
|
||||
assert_true(
|
||||
result.succeeded,
|
||||
result.error != null ? result.error.message : "Server result failed"
|
||||
);
|
||||
}
|
||||
|
||||
public void logout() throws GLib.Error {
|
||||
this.server.add_script_line(
|
||||
SEND_LINE, "* OK localhost test server ready"
|
||||
);
|
||||
this.server.add_script_line(RECEIVE_LINE, "a001 logout");
|
||||
this.server.add_script_line(SEND_LINE, "* BYE fine");
|
||||
this.server.add_script_line(SEND_LINE, "a001 OK laters");
|
||||
this.server.add_script_line(DISCONNECT, "");
|
||||
|
||||
var test_article = new ClientSession(new_endpoint());
|
||||
assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
|
||||
|
||||
test_article.connect_async.begin(
|
||||
CONNECT_TIMEOUT, null, this.async_complete_full
|
||||
);
|
||||
test_article.connect_async.end(async_result());
|
||||
assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
|
||||
|
||||
test_article.logout_async.begin(null, this.async_complete_full);
|
||||
test_article.logout_async.end(async_result());
|
||||
assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
|
||||
|
||||
TestServer.Result result = this.server.wait_for_script(this.main_loop);
|
||||
assert_true(
|
||||
result.succeeded,
|
||||
result.error != null ? result.error.message : "Server result failed"
|
||||
);
|
||||
}
|
||||
|
||||
public void login_logout() throws GLib.Error {
|
||||
this.server.add_script_line(
|
||||
SEND_LINE, "* OK localhost test server ready"
|
||||
);
|
||||
this.server.add_script_line(RECEIVE_LINE, "a001 login test password");
|
||||
this.server.add_script_line(SEND_LINE, "a001 OK ohhai");
|
||||
this.server.add_script_line(RECEIVE_LINE, "a002 logout");
|
||||
this.server.add_script_line(SEND_LINE, "* BYE fine");
|
||||
this.server.add_script_line(SEND_LINE, "a002 OK laters");
|
||||
this.server.add_script_line(DISCONNECT, "");
|
||||
|
||||
var test_article = new ClientSession(new_endpoint());
|
||||
assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
|
||||
|
||||
test_article.connect_async.begin(
|
||||
CONNECT_TIMEOUT, null, this.async_complete_full
|
||||
);
|
||||
test_article.connect_async.end(async_result());
|
||||
assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
|
||||
|
||||
test_article.login_async.begin(
|
||||
new Credentials(PASSWORD, "test", "password"),
|
||||
null,
|
||||
this.async_complete_full
|
||||
);
|
||||
test_article.login_async.end(async_result());
|
||||
assert_true(test_article.get_protocol_state() == AUTHORIZED);
|
||||
|
||||
test_article.logout_async.begin(null, this.async_complete_full);
|
||||
test_article.logout_async.end(async_result());
|
||||
assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
|
||||
|
||||
TestServer.Result result = this.server.wait_for_script(this.main_loop);
|
||||
assert_true(
|
||||
result.succeeded,
|
||||
result.error != null ? result.error.message : "Server result failed"
|
||||
);
|
||||
}
|
||||
|
||||
public void initiate_request_capabilities() throws GLib.Error {
|
||||
this.server.add_script_line(
|
||||
SEND_LINE, "* OK localhost test server ready"
|
||||
);
|
||||
this.server.add_script_line(RECEIVE_LINE, "a001 capability");
|
||||
this.server.add_script_line(SEND_LINE, "* CAPABILITY IMAP4rev1 LOGIN");
|
||||
this.server.add_script_line(SEND_LINE, "a001 OK enjoy");
|
||||
this.server.add_script_line(RECEIVE_LINE, "a002 login test password");
|
||||
this.server.add_script_line(SEND_LINE, "a002 OK ohhai");
|
||||
this.server.add_script_line(RECEIVE_LINE, "a003 capability");
|
||||
this.server.add_script_line(SEND_LINE, "* CAPABILITY IMAP4rev1");
|
||||
this.server.add_script_line(SEND_LINE, "a003 OK thanks");
|
||||
this.server.add_script_line(RECEIVE_LINE, "a004 LIST \"\" INBOX");
|
||||
this.server.add_script_line(SEND_LINE, "* LIST (\\HasChildren) \".\" Inbox");
|
||||
this.server.add_script_line(SEND_LINE, "a004 OK there");
|
||||
this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
|
||||
|
||||
var test_article = new ClientSession(new_endpoint());
|
||||
assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
|
||||
|
||||
test_article.connect_async.begin(
|
||||
CONNECT_TIMEOUT, null, this.async_complete_full
|
||||
);
|
||||
test_article.connect_async.end(async_result());
|
||||
assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
|
||||
|
||||
test_article.initiate_session_async.begin(
|
||||
new Credentials(PASSWORD, "test", "password"),
|
||||
null,
|
||||
this.async_complete_full
|
||||
);
|
||||
test_article.initiate_session_async.end(async_result());
|
||||
|
||||
assert_true(test_article.capabilities.supports_imap4rev1());
|
||||
assert_false(test_article.capabilities.has_capability("AUTH"));
|
||||
assert_int(2, test_article.capabilities.revision);
|
||||
|
||||
assert_string("Inbox", test_article.inbox.mailbox.name);
|
||||
assert_true(test_article.inbox.mailbox.is_inbox);
|
||||
|
||||
test_article.disconnect_async.begin(null, this.async_complete_full);
|
||||
test_article.disconnect_async.end(async_result());
|
||||
|
||||
TestServer.Result result = this.server.wait_for_script(this.main_loop);
|
||||
assert_true(
|
||||
result.succeeded,
|
||||
result.error != null ? result.error.message : "Server result failed"
|
||||
);
|
||||
}
|
||||
|
||||
public void initiate_implicit_capabilities() throws GLib.Error {
|
||||
this.server.add_script_line(
|
||||
SEND_LINE, "* OK [CAPABILITY IMAP4rev1 LOGIN] localhost test server ready"
|
||||
);
|
||||
this.server.add_script_line(RECEIVE_LINE, "a001 login test password");
|
||||
this.server.add_script_line(SEND_LINE, "a001 OK [CAPABILITY IMAP4rev1] ohhai");
|
||||
this.server.add_script_line(RECEIVE_LINE, "a002 LIST \"\" INBOX");
|
||||
this.server.add_script_line(SEND_LINE, "* LIST (\\HasChildren) \".\" Inbox");
|
||||
this.server.add_script_line(SEND_LINE, "a002 OK there");
|
||||
this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
|
||||
|
||||
var test_article = new ClientSession(new_endpoint());
|
||||
assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
|
||||
|
||||
test_article.connect_async.begin(
|
||||
CONNECT_TIMEOUT, null, this.async_complete_full
|
||||
);
|
||||
test_article.connect_async.end(async_result());
|
||||
assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
|
||||
|
||||
test_article.initiate_session_async.begin(
|
||||
new Credentials(PASSWORD, "test", "password"),
|
||||
null,
|
||||
this.async_complete_full
|
||||
);
|
||||
test_article.initiate_session_async.end(async_result());
|
||||
|
||||
assert_true(test_article.capabilities.supports_imap4rev1());
|
||||
assert_false(test_article.capabilities.has_capability("AUTH"));
|
||||
assert_int(2, test_article.capabilities.revision);
|
||||
|
||||
assert_string("Inbox", test_article.inbox.mailbox.name);
|
||||
assert_true(test_article.inbox.mailbox.is_inbox);
|
||||
|
||||
test_article.disconnect_async.begin(null, this.async_complete_full);
|
||||
test_article.disconnect_async.end(async_result());
|
||||
|
||||
TestServer.Result result = this.server.wait_for_script(this.main_loop);
|
||||
assert_true(
|
||||
result.succeeded,
|
||||
result.error != null ? result.error.message : "Server result failed"
|
||||
);
|
||||
}
|
||||
|
||||
public void initiate_namespace() throws GLib.Error {
|
||||
this.server.add_script_line(
|
||||
SEND_LINE,
|
||||
"* OK [CAPABILITY IMAP4rev1 LOGIN] localhost test server ready"
|
||||
);
|
||||
this.server.add_script_line(
|
||||
RECEIVE_LINE, "a001 login test password"
|
||||
);
|
||||
this.server.add_script_line(
|
||||
SEND_LINE, "a001 OK [CAPABILITY IMAP4rev1 NAMESPACE] ohhai"
|
||||
);
|
||||
this.server.add_script_line(
|
||||
RECEIVE_LINE, "a002 LIST \"\" INBOX"
|
||||
);
|
||||
this.server.add_script_line(
|
||||
SEND_LINE, "* LIST (\\HasChildren) \".\" Inbox"
|
||||
);
|
||||
this.server.add_script_line(
|
||||
SEND_LINE, "a002 OK there"
|
||||
);
|
||||
this.server.add_script_line(
|
||||
RECEIVE_LINE, "a003 NAMESPACE"
|
||||
);
|
||||
this.server.add_script_line(
|
||||
SEND_LINE,
|
||||
"""* NAMESPACE (("INBOX." ".")) (("user." ".")) (("shared." "."))"""
|
||||
);
|
||||
this.server.add_script_line(SEND_LINE, "a003 OK there");
|
||||
this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
|
||||
|
||||
var test_article = new ClientSession(new_endpoint());
|
||||
assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
|
||||
|
||||
test_article.connect_async.begin(
|
||||
CONNECT_TIMEOUT, null, this.async_complete_full
|
||||
);
|
||||
test_article.connect_async.end(async_result());
|
||||
assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
|
||||
|
||||
test_article.initiate_session_async.begin(
|
||||
new Credentials(PASSWORD, "test", "password"),
|
||||
null,
|
||||
this.async_complete_full
|
||||
);
|
||||
test_article.initiate_session_async.end(async_result());
|
||||
|
||||
assert_int(1, test_article.get_personal_namespaces().size);
|
||||
assert_string(
|
||||
"INBOX.", test_article.get_personal_namespaces()[0].prefix
|
||||
);
|
||||
|
||||
assert_int(1, test_article.get_shared_namespaces().size);
|
||||
assert_string(
|
||||
"shared.", test_article.get_shared_namespaces()[0].prefix
|
||||
);
|
||||
|
||||
assert_int(1, test_article.get_other_users_namespaces().size);
|
||||
assert_string(
|
||||
"user.", test_article.get_other_users_namespaces()[0].prefix
|
||||
);
|
||||
|
||||
test_article.disconnect_async.begin(null, this.async_complete_full);
|
||||
test_article.disconnect_async.end(async_result());
|
||||
|
||||
TestServer.Result result = this.server.wait_for_script(this.main_loop);
|
||||
assert_true(
|
||||
result.succeeded,
|
||||
result.error != null ? result.error.message : "Server result failed"
|
||||
);
|
||||
}
|
||||
|
||||
protected Endpoint new_endpoint() {
|
||||
return new Endpoint(this.server.get_client_address(), NONE, 10);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -265,7 +265,7 @@ class Geary.Imap.DeserializerTest : TestCase {
|
|||
this.stream.add_data(bye.data);
|
||||
|
||||
bool eos = false;
|
||||
this.deser.eos.connect(() => { eos = true; });
|
||||
this.deser.end_of_stream.connect(() => { eos = true; });
|
||||
|
||||
this.process.begin(Expect.MESSAGE, (obj, ret) => { async_complete(ret); });
|
||||
RootParameters? message = this.process.end(async_result());
|
||||
|
|
@ -283,7 +283,7 @@ class Geary.Imap.DeserializerTest : TestCase {
|
|||
|
||||
this.deser.parameters_ready.connect((param) => { message = param; });
|
||||
this.deser.bytes_received.connect((count) => { bytes_received += count; });
|
||||
this.deser.eos.connect((param) => { eos = true; });
|
||||
this.deser.end_of_stream.connect((param) => { eos = true; });
|
||||
this.deser.deserialize_failure.connect(() => { deserialize_failure = true; });
|
||||
this.deser.receive_failure.connect((err) => { receive_failure = true;});
|
||||
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ class Geary.TimeoutManagerTest : TestCase {
|
|||
this.main_loop.iteration(true);
|
||||
}
|
||||
|
||||
assert_epsilon(timer.elapsed(), 1.0, SECONDS_EPSILON);
|
||||
assert_double(timer.elapsed(), 1.0, SECONDS_EPSILON);
|
||||
}
|
||||
|
||||
public void milliseconds() throws Error {
|
||||
|
|
@ -101,7 +101,7 @@ class Geary.TimeoutManagerTest : TestCase {
|
|||
this.main_loop.iteration(true);
|
||||
}
|
||||
|
||||
assert_epsilon(timer.elapsed(), 0.1, MILLISECONDS_EPSILON);
|
||||
assert_double(timer.elapsed(), 0.1, MILLISECONDS_EPSILON);
|
||||
}
|
||||
|
||||
public void repeat_forever() throws Error {
|
||||
|
|
@ -118,11 +118,7 @@ class Geary.TimeoutManagerTest : TestCase {
|
|||
}
|
||||
timer.stop();
|
||||
|
||||
assert_epsilon(timer.elapsed(), 2.0, SECONDS_EPSILON * 2);
|
||||
}
|
||||
|
||||
private inline void assert_epsilon(double actual, double expected, double epsilon) {
|
||||
assert(actual + epsilon >= expected && actual - epsilon <= expected);
|
||||
assert_double(timer.elapsed(), 2.0, SECONDS_EPSILON * 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class Integration.Imap.ClientSession : TestCase {
|
|||
}
|
||||
|
||||
public override void tear_down() throws GLib.Error {
|
||||
if (this.session.get_protocol_state(null) != NOT_CONNECTED) {
|
||||
if (this.session.get_protocol_state() != NOT_CONNECTED) {
|
||||
this.session.disconnect_async.begin(null, async_complete_full);
|
||||
this.session.disconnect_async.end(async_result());
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ class Integration.Imap.ClientSession : TestCase {
|
|||
}
|
||||
|
||||
public void session_connect() throws GLib.Error {
|
||||
this.session.connect_async.begin(null, async_complete_full);
|
||||
this.session.connect_async.begin(2, null, async_complete_full);
|
||||
this.session.connect_async.end(async_result());
|
||||
|
||||
this.session.disconnect_async.begin(null, async_complete_full);
|
||||
|
|
@ -98,7 +98,7 @@ class Integration.Imap.ClientSession : TestCase {
|
|||
}
|
||||
|
||||
private void do_connect() throws GLib.Error {
|
||||
this.session.connect_async.begin(null, async_complete_full);
|
||||
this.session.connect_async.begin(5, null, async_complete_full);
|
||||
this.session.connect_async.end(async_result());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ subdir('data')
|
|||
geary_test_lib_sources = [
|
||||
'mock-object.vala',
|
||||
'test-case.vala',
|
||||
'test-server.vala',
|
||||
]
|
||||
|
||||
geary_test_engine_sources = [
|
||||
|
|
@ -41,6 +42,8 @@ geary_test_engine_sources = [
|
|||
'engine/imap/message/imap-mailbox-specifier-test.vala',
|
||||
'engine/imap/parameter/imap-list-parameter-test.vala',
|
||||
'engine/imap/response/imap-namespace-response-test.vala',
|
||||
'engine/imap/transport/imap-client-connection-test.vala',
|
||||
'engine/imap/transport/imap-client-session-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',
|
||||
|
|
|
|||
|
|
@ -96,6 +96,10 @@ public void assert_int64(int64 expected, int64 actual, string? context = null)
|
|||
}
|
||||
}
|
||||
|
||||
public void assert_double(double actual, double expected, double epsilon) {
|
||||
assert(actual + epsilon >= expected && actual - epsilon <= expected);
|
||||
}
|
||||
|
||||
public void assert_uint(uint expected, uint actual, string? context = null)
|
||||
throws GLib.Error {
|
||||
if (expected != actual) {
|
||||
|
|
|
|||
|
|
@ -46,20 +46,28 @@ int main(string[] args) {
|
|||
engine.add_suite(new Geary.Db.DatabaseTest().get_suite());
|
||||
engine.add_suite(new Geary.Db.VersionedDatabaseTest().get_suite());
|
||||
engine.add_suite(new Geary.HTML.UtilTest().get_suite());
|
||||
// Other IMAP tests rely on DataFormat working, so test that first
|
||||
|
||||
// Other IMAP tests rely on these working, so test them first
|
||||
engine.add_suite(new Geary.Imap.DataFormatTest().get_suite());
|
||||
|
||||
engine.add_suite(new Geary.Imap.CreateCommandTest().get_suite());
|
||||
engine.add_suite(new Geary.Imap.DeserializerTest().get_suite());
|
||||
engine.add_suite(new Geary.Imap.FetchCommandTest().get_suite());
|
||||
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());
|
||||
|
||||
// Depends on IMAP commands working
|
||||
engine.add_suite(new Geary.Imap.DeserializerTest().get_suite());
|
||||
engine.add_suite(new Geary.Imap.ClientConnectionTest().get_suite());
|
||||
engine.add_suite(new Geary.Imap.ClientSessionTest().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());
|
||||
engine.add_suite(new Geary.ImapDB.EmailIdentifierTest().get_suite());
|
||||
engine.add_suite(new Geary.ImapDB.FolderTest().get_suite());
|
||||
|
||||
engine.add_suite(new Geary.ImapEngine.AccountProcessorTest().get_suite());
|
||||
engine.add_suite(new Geary.ImapEngine.GenericAccountTest().get_suite());
|
||||
|
||||
|
|
|
|||
222
test/test-server.vala
Normal file
222
test/test-server.vala
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* Copyright 2019 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A simple mock server for testing network connections.
|
||||
*
|
||||
* To use it, unit tests should construct an instance as a fixture in
|
||||
* set up, specify a test script by adding lines and then check the
|
||||
* result, before stopping the server in tear down.
|
||||
*/
|
||||
public class TestServer : GLib.Object {
|
||||
|
||||
|
||||
/** Possible actions a script may take. */
|
||||
public enum Action {
|
||||
/**
|
||||
* The implicit first action.
|
||||
*
|
||||
* This does not need to be specified as a script action, it
|
||||
* will always be taken when a client connects.
|
||||
*/
|
||||
CONNECTED,
|
||||
|
||||
/** Send a line to the client. */
|
||||
SEND_LINE,
|
||||
|
||||
/** Receive a line from the client. */
|
||||
RECEIVE_LINE,
|
||||
|
||||
/** Wait for the client to disconnect. */
|
||||
WAIT_FOR_DISCONNECT,
|
||||
|
||||
/** Disconnect immediately. */
|
||||
DISCONNECT;
|
||||
}
|
||||
|
||||
|
||||
/** A line of the server's script. */
|
||||
public struct Line {
|
||||
|
||||
/** The action to take for this line. */
|
||||
public Action action;
|
||||
|
||||
/**
|
||||
* The value for the action.
|
||||
*
|
||||
* If sending, this string will be sent. If receiving, the
|
||||
* expected line.
|
||||
*/
|
||||
public string value;
|
||||
|
||||
}
|
||||
|
||||
/** The result of executing a script line. */
|
||||
public struct Result {
|
||||
|
||||
/** The expected action. */
|
||||
public Line line;
|
||||
|
||||
/** Was the expected action successful. */
|
||||
public bool succeeded;
|
||||
|
||||
/** The actual string sent by a client when not as expected. */
|
||||
public string? actual;
|
||||
|
||||
/** In case of an error being thrown, the error itself. */
|
||||
public GLib.Error? error;
|
||||
|
||||
}
|
||||
|
||||
|
||||
private GLib.DataStreamNewlineType line_ending;
|
||||
private uint16 port;
|
||||
private GLib.ThreadedSocketService service =
|
||||
new GLib.ThreadedSocketService(10);
|
||||
private GLib.Cancellable running = new GLib.Cancellable();
|
||||
private Gee.List<Line?> script = new Gee.ArrayList<Line?>();
|
||||
private GLib.AsyncQueue<Result?> completion_queue =
|
||||
new GLib.AsyncQueue<Result?>();
|
||||
|
||||
|
||||
public TestServer(GLib.DataStreamNewlineType line_ending = CR_LF)
|
||||
throws GLib.Error {
|
||||
this.line_ending = line_ending;
|
||||
this.port = this.service.add_any_inet_port(null);
|
||||
this.service.run.connect((conn) => {
|
||||
handle_connection(conn);
|
||||
return true;
|
||||
});
|
||||
this.service.start();
|
||||
}
|
||||
|
||||
public GLib.SocketConnectable get_client_address() {
|
||||
return new GLib.NetworkAddress("localhost", this.port);
|
||||
}
|
||||
|
||||
public void add_script_line(Action action, string value) {
|
||||
this.script.add({ action, value });
|
||||
}
|
||||
|
||||
public Result wait_for_script(GLib.MainContext loop) {
|
||||
Result? result = null;
|
||||
while (result == null) {
|
||||
loop.iteration(false);
|
||||
result = this.completion_queue.try_pop();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
this.service.stop();
|
||||
this.running.cancel();
|
||||
}
|
||||
|
||||
private void handle_connection(GLib.SocketConnection connection) {
|
||||
debug("Connected");
|
||||
var input = new GLib.DataInputStream(
|
||||
connection.input_stream
|
||||
);
|
||||
input.set_newline_type(this.line_ending);
|
||||
|
||||
var output = new GLib.DataOutputStream(
|
||||
connection.output_stream
|
||||
);
|
||||
|
||||
Line connected_line = { CONNECTED, "" };
|
||||
Result result = { connected_line, true, null, null };
|
||||
foreach (var line in this.script) {
|
||||
result.line = line;
|
||||
switch (line.action) {
|
||||
case SEND_LINE:
|
||||
debug("Sending: %s", line.value);
|
||||
try {
|
||||
output.put_string(line.value);
|
||||
switch (this.line_ending) {
|
||||
case CR:
|
||||
output.put_byte('\r');
|
||||
break;
|
||||
case LF:
|
||||
output.put_byte('\n');
|
||||
break;
|
||||
default:
|
||||
output.put_byte('\r');
|
||||
output.put_byte('\n');
|
||||
break;
|
||||
}
|
||||
} catch (GLib.Error err) {
|
||||
result.succeeded = false;
|
||||
result.error = err;
|
||||
}
|
||||
break;
|
||||
|
||||
case RECEIVE_LINE:
|
||||
debug("Waiting for: %s", line.value);
|
||||
try {
|
||||
size_t len;
|
||||
string? received = input.read_line(out len, this.running);
|
||||
if (received == null || received != line.value) {
|
||||
result.succeeded = false;
|
||||
result.actual = received;
|
||||
}
|
||||
} catch (GLib.Error err) {
|
||||
result.succeeded = false;
|
||||
result.error = err;
|
||||
}
|
||||
break;
|
||||
|
||||
case WAIT_FOR_DISCONNECT:
|
||||
debug("Waiting for disconnect");
|
||||
var socket = connection.get_socket();
|
||||
try {
|
||||
uint8 buffer[4096];
|
||||
while (socket.receive_with_blocking(buffer, true) > 0) { }
|
||||
} catch (GLib.Error err) {
|
||||
result.succeeded = false;
|
||||
result.error = err;
|
||||
}
|
||||
break;
|
||||
|
||||
case DISCONNECT:
|
||||
debug("Disconnecting");
|
||||
try {
|
||||
connection.close(this.running);
|
||||
} catch (GLib.Error err) {
|
||||
result.succeeded = false;
|
||||
result.error = err;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!result.succeeded) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result.succeeded) {
|
||||
debug("Done");
|
||||
} else if (result.error != null) {
|
||||
warning("Error: %s", result.error.message);
|
||||
} else if (result.line.action == RECEIVE_LINE) {
|
||||
warning("Received unexpected line: %s", result.actual ?? "(null)");
|
||||
} else {
|
||||
warning("Failed for unknown reason");
|
||||
}
|
||||
|
||||
if (connection.is_connected()) {
|
||||
try {
|
||||
connection.close(this.running);
|
||||
} catch (GLib.Error err) {
|
||||
warning(
|
||||
"Error closing test server connection: %s", err.message
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.completion_queue.push(result);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue