1: %%% Textgroup server.
    2: %%%
    3: %%% Copyright (c) 2022 Holger Weiss <holger@zedat.fu-berlin.de>.
    4: %%% All rights reserved.
    5: %%%
    6: %%% Licensed under the Apache License, Version 2.0 (the "License");
    7: %%% you may not use this file except in compliance with the License.
    8: %%% You may obtain a copy of the License at
    9: %%%
   10: %%%     http://www.apache.org/licenses/LICENSE-2.0
   11: %%%
   12: %%% Unless required by applicable law or agreed to in writing, software
   13: %%% distributed under the License is distributed on an "AS IS" BASIS,
   14: %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   15: %%% See the License for the specific language governing permissions and
   16: %%% limitations under the License.
   17: 
   18: -module(textgroup_SUITE).
   19: -compile(export_all).
   20: -include_lib("common_test/include/ct.hrl").
   21: 
   22: -type info() :: ct_suite:ct_info().
   23: -type config() :: ct_suite:ct_config().
   24: -type test_def() :: ct_suite:ct_test_def().
   25: -type test_name() :: ct_suite:ct_testname().
   26: -type group_def() :: ct_suite:ct_group_def().
   27: -type group_name() :: ct_suite:ct_groupname().
   28: 
   29: %% API.
   30: 
   31: -spec suite() -> [info()].
   32: suite() ->
   33:     [{require, {server, [addr, port]}},
   34:      {timetrap, {seconds, 30}}].
   35: 
   36: -spec init_per_suite(config()) -> config().
   37: init_per_suite(Config) ->
   38:     Server = ct:get_config(server),
   39:     Addr = proplists:get_value(addr, Server),
   40:     Port = proplists:get_value(port, Server),
   41:     [{addr, Addr}, {port, Port} | Config].
   42: 
   43: -spec end_per_suite(config()) -> ok.
   44: end_per_suite(_Config) ->
   45:     ok.
   46: 
   47: -spec init_per_group(group_name(), config()) -> config().
   48: init_per_group(_GroupName, Config) ->
   49:     Config.
   50: 
   51: -spec end_per_group(group_name(), config()) -> ok.
   52: end_per_group(_GroupName, _Config) ->
   53:     ok.
   54: 
   55: -spec init_per_testcase(test_name(), config()) -> config().
   56: init_per_testcase(_TestCase, Config) ->
   57:     Config.
   58: 
   59: -spec end_per_testcase(test_name(), config()) -> ok.
   60: end_per_testcase(_TestCase, _Config) ->
   61:     ok.
   62: 
   63: -spec groups() -> [group_def()].
   64: groups() ->
   65:     [].
   66: 
   67: -spec all() -> [test_def()] | {skip, term()}.
   68: all() ->
   69:     [start_server,
   70:      peers,
   71:      stats,
   72:      help,
   73:      get_state,
   74:      config_change,
   75:      stop_server].
   76: 
   77: %% Test cases.
   78: 
   79: -spec start_server(config()) -> any().
   80: start_server(Config) ->
   81:     ct:pal("Starting Textgroup server"),
   82:     Port = ?config(port, Config),
   83:     Opts = [{persistent, true}],
   84:     ok = application:set_env(textgroup, port, Port, Opts),
   85:     ok = application:set_env(textgroup, tcp_queue_size, 2, Opts),
   86:     {ok, _Apps} = application:ensure_all_started(textgroup).
   87: 
   88: -spec peers(config()) -> any().
   89: peers(Config) ->
   90:     ok = query(Config, <<"peers">>, <<"127.0.0.1">>).
   91: 
   92: -spec stats(config()) -> any().
   93: stats(Config) ->
   94:     ok = query(Config, <<"stats">>, <<"Messages rcvd: 1">>).
   95: 
   96: -spec help(config()) -> any().
   97: help(Config) ->
   98:     ok = query(Config, <<"help">>, <<"quit">>).
   99: 
  100: -spec get_state(config()) -> any().
  101: get_state(_Config) ->
  102:     {ok, Size} = application:get_env(textgroup, pool_size),
  103:     ct:pal("Requesting state of the ~B acceptor processes", [Size]),
  104:     ok = lists:foreach(fun({_, PID, _, [textgroup_acceptor]}) ->
  105:                                {acceptor_state, _, _} = sys:get_state(PID)
  106:                        end, supervisor:which_children(textgroup_acceptor_sup)).
  107: 
  108: -spec config_change(config()) -> any().
  109: config_change(_Config) ->
  110:     Opts = [{persistent, true}],
  111:     Size = 10,
  112:     ok = application:set_env(textgroup, pool_size, Size, Opts),
  113:     ok = textgroup_app:config_change([{pool_size, Size}], [], []).
  114: 
  115: -spec stop_server(config()) -> any().
  116: stop_server(_Config) ->
  117:     ct:pal("Stopping Textgroup server"),
  118:     ok = application:stop(textgroup).
  119: 
  120: %% Internal functions.
  121: 
  122: -spec connect(config()) -> gen_tcp:socket().
  123: connect(Config) ->
  124:     Addr = ?config(addr, Config),
  125:     Port = ?config(port, Config),
  126:     {ok, Socket} = gen_tcp:connect(Addr, Port,
  127:                                    [binary,
  128:                                     {nodelay, true},
  129:                                     {active, false},
  130:                                     {packet, line}],
  131:                                    timer:seconds(5)),
  132:     Socket.
  133: 
  134: -spec query(config(), binary(), binary()) -> ok.
  135: query(Config, Query, Expected) ->
  136:     ct:pal("Opening first Textgroup session"),
  137:     Socket1 = connect(Config),
  138:     ct:pal("Opening second Textgroup session"),
  139:     Socket2 = connect(Config),
  140:     ok = recv_until(Socket1, <<"Peers may query your IP address">>),
  141:     ok = recv_until(Socket2, <<"Peers may query your IP address">>),
  142:     ct:pal("Sending messages over both sessions"),
  143:     ok = gen_tcp:send(Socket1, [<<"foo">>, $\n]),
  144:     ok = gen_tcp:send(Socket2, [<<"bar">>, $\n]),
  145:     ct:pal("Receiving messages over both sessions"),
  146:     ok = recv_until(Socket1, <<"bar">>),
  147:     ok = recv_until(Socket2, <<"foo">>),
  148:     ct:pal("Querying ~s from Textgroup server", [Query]),
  149:     ok = gen_tcp:send(Socket1, [Query, $\n]),
  150:     ok = recv_until(Socket1, Expected),
  151:     ct:pal("Closing first Textgroup session"),
  152:     ok = gen_tcp:send(Socket1, <<"quit", $\n>>),
  153:     ok = recv_until(Socket1, <<"Thanks for using Textgroup">>),
  154:     ok = gen_tcp:close(Socket1),
  155:     ct:pal("Closing second Textgroup session"),
  156:     ok = gen_tcp:send(Socket2, <<"quit", $\n>>),
  157:     ok = recv_until(Socket2, <<"Thanks for using Textgroup">>),
  158:     ok = gen_tcp:close(Socket2).
  159: 
  160: -spec recv_until(gen_tcp:socket(), binary()) -> ok.
  161: recv_until(Socket, Expected) ->
  162:     ct:pal("Expecting response: ~s", [Expected]),
  163:     {ok, Data} = gen_tcp:recv(Socket, 0, timer:seconds(5)),
  164:     Line = string:trim(Data),
  165:     case string:prefix(Line, Expected) of
  166:         nomatch ->
  167:             ct:pal("Didn't get expected response (yet): ~s", [Line]),
  168:             recv_until(Socket, Expected);
  169:         _Rest ->
  170:             ct:pal("Got expected response: ~s", [Line]),
  171:             ok
  172:     end.