diff --git a/apps/kolab_guam/test/kolab_guam_session_SUITE.erl b/apps/kolab_guam/test/kolab_guam_session_SUITE.erl index e28b499..e5a874c 100644 --- a/apps/kolab_guam/test/kolab_guam_session_SUITE.erl +++ b/apps/kolab_guam/test/kolab_guam_session_SUITE.erl @@ -1,154 +1,185 @@ %% Copyright 2015 Kolab Systems AG (http://www.kolabsys.com) %% %% Christian Mollekopf (Kolab Systems) %% %% This program is free software: you can redistribute it and/or modify %% it under the terms of the GNU General Public License as published by %% the Free Software Foundation, either version 3 of the License, or %% (at your option) any later version. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program. If not, see . -module(kolab_guam_session_SUITE). % easier than exporting by name -compile(export_all). % required for common_test to work -include_lib("common_test/include/ct.hrl"). -include("../src/kolab_guam_session.hrl"). %%%%%%%%%%%%%%%%%%%%%%%%%%% %% common test callbacks %% %%%%%%%%%%%%%%%%%%%%%%%%%%% % Specify a list of all unit test functions all() -> [ kolab_guam_session_test ]. % required, but can just return Config. this is a suite level setup function. init_per_suite(Config) -> Config. % required, but can just return Config. this is a suite level tear down function. end_per_suite(Config) -> Config. % optional, can do function level setup for all functions, % or for individual functions by matching on TestCase. init_per_testcase(_TestCase, Config) -> Config. % optional, can do function level tear down for all functions, % or for individual functions by matching on TestCase. end_per_testcase(_TestCase, Config) -> Config. kolab_guam_session_test(_TestConfig) -> %% setup boilerplate ServerConfig = kolab_guam_sup:default_imap_server_config(), { ok, ImapSession } = eimap:start_link(ServerConfig), { ok, Socket } = gen_tcp:listen(9964, [ { reuseaddr, true }, {active, false}, inet6 ]), lager:start(), lager:set_loglevel(lager_console_backend, debug), ActiveRules = [{ kolab_guam_rule_filter_groupware, kolab_guam_rule_filter_groupware:new({}) }], State = #state{ socket = Socket, server_config = ServerConfig, imap_session = ImapSession }, % Run without rules {noreply, #state{ server_config = ServerConfig, buffered_client_data = <<>>, current_command_split = undefined, command_split_reset_trigger = [] %FIXME ? }} = kolab_guam_session:handle_info({tcp, Socket, <<"y1 ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\ny3 LIST \"\" \"%\" RETURN (SUBSCRIBED))\r\n">>}, State#state{ rules_deciding = [] }), % Activate filtering {noreply, #state{ server_config = ServerConfig, buffered_client_data = <<>>, current_command_split = {<<"y1">>,<<"ID">>,<<"(\"name\" \"Test\")">>}, command_split_reset_trigger = reset_for_next_client_command, rules_deciding = [], rules_active = [{kolab_guam_rule_filter_groupware, _}] }} = kolab_guam_session:handle_info({tcp, Socket, <<"y1 ID (\"name\" \"Test\")\r\n">>}, State#state{ rules_deciding = ActiveRules }), % Don't activate filtering {noreply, #state{ server_config = ServerConfig, buffered_client_data = <<>>, current_command_split = {<<"y1">>,<<"ID">>,<<"(\"name\" \"Test/KOLAB\")">>}, command_split_reset_trigger = reset_for_next_client_command, rules_deciding = [], rules_active = [] }} = kolab_guam_session:handle_info({tcp, Socket, <<"y1 ID (\"name\" \"Test/KOLAB\")\r\n">>}, State#state{ rules_deciding = ActiveRules }), % Lone tag in a packet. Can/Could happen with iPhone apparently. (See commit 89f9dc93757c68032ed17f42838858bdfaefa408) {noreply, #state{ buffered_client_data = <<"y1">>, rules_active = [] } = IntermediateState1} = kolab_guam_session:handle_info({tcp, Socket, <<"y1">>}, State#state{ rules_deciding = ActiveRules }), {noreply, #state{ server_config = ServerConfig, buffered_client_data = <<>>, current_command_split = {<<"y2">>,<<"ENABLE">>,<<"QRESYNC">>}, command_split_reset_trigger = reset_for_next_client_command, rules_deciding = [], rules_active = [{kolab_guam_rule_filter_groupware, _}] }} = kolab_guam_session:handle_info({tcp, Socket, <<" ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\n">>}, IntermediateState1), % Lone tag in a packet, but with a \r\n before. See guam-0.9.2-stalling-client-buffer-and-split-command-handling.patch (This triggers the odd List buffering case) {noreply, #state{ buffered_client_data = <<"y1 ">>, rules_active = [] } = IntermediateState2} = kolab_guam_session:handle_info({tcp, Socket, <<"\r\ny1 ">>}, State#state{ rules_deciding = ActiveRules }), {noreply, #state{ server_config = ServerConfig, buffered_client_data = <<>>, current_command_split = {<<"y2">>,<<"ENABLE">>,<<"QRESYNC">>}, command_split_reset_trigger = reset_for_next_client_command, rules_deciding = [], rules_active = [{kolab_guam_rule_filter_groupware, _}] }} = kolab_guam_session:handle_info({tcp, Socket, <<"ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\n">>}, IntermediateState2), % Empty line (should be considered a complete command and should not be buffered) {noreply, #state{ buffered_client_data = <<>>, rules_active = [] } = IntermediateState3} = kolab_guam_session:handle_info({tcp, Socket, <<"\r\n">>}, State#state{ rules_deciding = ActiveRules }), {noreply, #state{ server_config = ServerConfig, buffered_client_data = <<>>, current_command_split = {<<"y1">>,<<"ID">>,<<"(\"name\" \"Test\")">>}, command_split_reset_trigger = reset_for_next_client_command, rules_deciding = [], rules_active = [{kolab_guam_rule_filter_groupware, _}] }} = kolab_guam_session:handle_info({tcp, Socket, <<"y1 ID (\"name\" \"Test\")\r\n">>}, IntermediateState3), % Activate with multi-line packet {noreply, #state{ server_config = ServerConfig, buffered_client_data = <<>>, - % current_command_split = undefined, + current_command_split = {<<"y3">>,<<"LIST">>,<<"\"\" \"%\" RETURN (SUBSCRIBED))">>}, command_split_reset_trigger = reset_for_next_client_command, rules_deciding = [], % Make sure that we have processed y3 in here (and don't stop processing at y1) rules_active = [{kolab_guam_rule_filter_groupware, {state,undefined,<<"y3">>,true,<<>>,[<<"LIST">>,<<"list">>,<<"XLIST">>,<<"xlist">>,<<"LSUB">>,<<"lsub">>]}}] % rules_active = [{kolab_guam_rule_filter_groupware, _}] }} = kolab_guam_session:handle_info({tcp, Socket, <<"y1 ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\ny3 LIST \"\" \"%\" RETURN (SUBSCRIBED))\r\n">>}, State#state{ rules_deciding = ActiveRules }), + + % Test various packet splits + PacketsSplits = [ + [<<"y1 ID (\"name\" \"Test\")\r\n">>, <<"y2 ENABLE QRESYNC\r\n">>, <<"y3 LIST \"\" \"%\" RETURN (SUBSCRIBED))\r\n">>] + ], + + lists:foldl(fun(Packets, _) -> + try_packets(Packets, Socket, ServerConfig, State#state{ rules_deciding = ActiveRules }) + end, + undefined, + PacketsSplits), + + lager:info("Done"). + +try_packets(Packets, Socket, ServerConfig, State) -> + #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, + % FIXME this is not what we should be getting. + current_command_split = {<<"y1">>,<<"ID">>,<<"(\"name\" \"Test\")">>}, + command_split_reset_trigger = reset_for_next_client_command, + rules_deciding = [], + % TODO Make sure that we have processed y3 in here (and don't stop processing at y1) + % rules_active = [{kolab_guam_rule_filter_groupware, {state,undefined,<<"y3">>,true,<<>>,[<<"LIST">>,<<"list">>,<<"XLIST">>,<<"xlist">>,<<"LSUB">>,<<"lsub">>]}}] + rules_active = [{kolab_guam_rule_filter_groupware, _}] + } = lists:foldl(fun(Packet, _State) -> + {_, NewState} = kolab_guam_session:handle_info({tcp, Socket, Packet}, _State), + NewState + end, + State, + Packets).