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.