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.