aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/test/esock_ttest/esock-ttest
blob: f0d363ab30179da283e0f21c516c3ac69ecdd0e8 (plain) (tree)
1
2
3
4
5
6




                      
                                                        




























































































































































































































































































                                                                                             

                                                       




















                                                                      

                                                          



































                                                                             
#!/usr/bin/env escript

%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2018-2019. All Rights Reserved.
%% 
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%% 
%% %CopyrightEnd%
%%

%% ==========================================================================
%%
%% This is a simple wrapper escript on top of the socket ttest program(s).
%% The idea is to make it simple to run in a normal shell (bash).
%%
%% ==========================================================================

-define(SECS(I), timer:seconds(I)).

-define(CLIENT_MSG_1_MAX_OUTSTANDING, 100).
-define(CLIENT_MSG_2_MAX_OUTSTANDING,  10).
-define(CLIENT_MSG_3_MAX_OUTSTANDING,   1).

main(Args) ->
    State = process_args(Args),
    exec(State),
    ok.

usage(ErrorString) when is_list(ErrorString) ->
    eprint(ErrorString),
    usage(),
    erlang:halt(0).

usage() ->
    io:format("usage: ~s [options]"
              "~n"
              "~n   This erlang script is used to start the (e)socket ttest "
              "~n   units (server or client)."
              "~n"
              "~n   options: "
              "~n     --help                    Display this info and exit. "
              "~n     --server [server-options] Start a server. "
              "~n                               There are no mandatory server options."
              "~n     --client client-options   Start a client"
              "~n                               Some client options are mandatory and"
              "~n                               others optional."
              "~n     --active <active>         boolean() | once."
              "~n                               Valid for both client and server."
              "~n                               Defaults to: false"
              "~n     --transport <transport>   Which transport to use: gen|sock[:plain|msg]"
              "~n                                 gen:  gen_tcp"
              "~n                                 sock: socket"
              "~n                                       plain: recv/send (default)"
              "~n                                       msg:   recvmsg/sendmsg"
              "~n                               Defaults to: sock:plain"
              "~n     --scon <addr>:<port>      Address and port of the server."
              "~n                               The address part is in the standard form:"
              "~n                               \"a.b.c.d\"."
              "~n                               Only valid for client."
              "~n                               Mandatory."
              "~n     --msg-id <1|2|3>          Choose which message to use during the test."
              "~n                               Basically: "
              "~n                                  1: small"
              "~n                                  2: medium"
              "~n                                  3: large"
              "~n                               Defaults to: 1"
              "~n     --max-outstanding <Num>   How many messages to send before waiting for"
              "~n                               a reply."
              "~n                               Valid only for client."
              "~n                               Defaults to: "
              "~n                                  MsgID 1: 100"
              "~n                                  MsgID 2:  10"
              "~n                                  MsgID 3:   1"
              "~n     --runtime <Time>          Time of the test in seconds."
              "~n                               Only valid for client."
              "~n                               Mandatory."
              "~n                               Defaults to: 60 (seconds)"
              "~n"
              "~n"
              "~n", 
              [scriptname()]),
    ok.

process_args(["--help"|_]) ->
    usage();
process_args(["--server"|ServerArgs]) ->
    process_server_args(ServerArgs);
process_args(["--client"|ClientArgs]) ->
    process_client_args(ClientArgs);
process_args(Args) ->
    usage(f("Invalid Args: "
            "~n   ~p", [Args])).


process_server_args(Args) ->
    Defaults = #{role      => server,
                 active    => false,
                 transport => {sock, plain}},
    process_server_args(Args, Defaults).

process_server_args([], State) ->
    State;

process_server_args(["--active", Active|Args], State) 
  when ((Active =:= "false") orelse
        (Active =:= "once") orelse
        (Active =:= "true")) ->
    process_server_args(Args, State#{active => list_to_atom(Active)});

process_server_args(["--transport", "gen" | Args], State) ->
    process_server_args(Args, State#{transport => gen});
process_server_args(["--transport", "sock" | Args], State) ->
    process_server_args(Args, State#{transport => {sock, plain}});
process_server_args(["--transport", "sock:plain" | Args], State) ->
    process_server_args(Args, State#{transport => {sock, plain}});
process_server_args(["--transport", "sock:msg" | Args], State) ->
    process_server_args(Args, State#{transport => {sock, msg}});

process_server_args([Arg|_], _State) ->
    usage(f("Invalid Server arg: ~s", [Arg])).


process_client_args(Args) ->
    Defaults = #{role            => client,
                 active          => false,
                 transport       => {sock, plain},
                 %% Will cause error if not provided
                 %% Should be "addr:port"
                 server          => undefined,
                 msg_id          => 1,
                 %% Will be filled in based on msg_id if not provided
                 max_outstanding => undefined,
                 runtime         => ?SECS(60)},
    process_client_args(Args, Defaults).

process_client_args([], State) ->
    process_client_args_ensure_max_outstanding(State);

process_client_args(["--active", Active|Args], State) 
  when ((Active =:= "false") orelse
        (Active =:= "once") orelse
        (Active =:= "true")) ->
    process_client_args(Args, State#{active => list_to_atom(Active)});

process_client_args(["--transport", "gen" | Args], State) ->
    process_client_args(Args, State#{transport => gen});
process_client_args(["--transport", "sock" | Args], State) ->
    process_client_args(Args, State#{transport => {sock, plain}});
process_client_args(["--transport", "sock:plain" | Args], State) ->
    process_client_args(Args, State#{transport => {sock, plain}});
process_client_args(["--transport", "sock:msg" | Args], State) ->
    process_client_args(Args, State#{transport => {sock, msg}});

process_client_args(["--msg-id", MsgID|Args], State)
  when ((MsgID =:= "1") orelse
        (MsgID =:= "2") orelse
        (MsgID =:= "3")) ->
    process_client_args(Args, State#{msg_id => list_to_integer(MsgID)});

process_client_args(["--max-outstanding", Max|Args], State) ->
    try list_to_integer(Max) of
        I when (I > 0) ->
            process_client_args(Args, State#{max_outstanding => I});
        _ ->
            usage(f("Invalid Max Outstanding: ~s", [Max]))
    catch
        _:_:_ ->
            usage(f("Invalid Max Outstanding: ~s", [Max]))
    end;

process_client_args(["--scon", Server|Args], State) ->
    case string:tokens(Server, [$:]) of
        [AddrStr,PortStr] ->
            Addr = case inet:parse_address(AddrStr) of
                       {ok, A} ->
                           A;
                       {error, _} ->
                           usage(f("Invalid Server Address: ~s", [AddrStr]))
                   end,
            Port = try list_to_integer(PortStr) of
                       I when (I > 0) ->
                           I;
                       _ ->
                           usage(f("Invalid Server Port: ~s", [PortStr]))
                   catch
                       _:_:_ ->
                           usage(f("Invalid Server Port: ~s", [PortStr]))
                   end,
            process_client_args(Args, State#{server => {Addr, Port}});
        _ ->
            usage(f("Invalid Server: ~s", [Server]))
    end;

process_client_args(["--runtime", T|Args], State) ->
    try list_to_integer(T) of
        I when (I > 0) ->
            process_client_args(Args, State#{runtime => ?SECS(I)});
        _ ->
            usage(f("Invalid Run Time: ~s", [T]))
    catch
        _:_:_ ->
            usage(f("Invalid Run Time: ~s", [T]))
    end;

process_client_args([Arg|_], _State) ->
    usage(f("Invalid Client arg: ~s", [Arg])).


process_client_args_ensure_max_outstanding(
  #{msg_id          := 1,
    max_outstanding := undefined} = State) ->
    State#{max_outstanding => ?CLIENT_MSG_1_MAX_OUTSTANDING};
process_client_args_ensure_max_outstanding(
  #{msg_id          := 2,
    max_outstanding := undefined} = State) ->
    State#{max_outstanding => ?CLIENT_MSG_2_MAX_OUTSTANDING};
process_client_args_ensure_max_outstanding(
  #{msg_id          := 3,
    max_outstanding := undefined} = State) ->
    State#{max_outstanding => ?CLIENT_MSG_3_MAX_OUTSTANDING};
process_client_args_ensure_max_outstanding(
  #{msg_id          := MsgID,
    max_outstanding := MaxOutstanding} = State)
  when ((MsgID =:= 1) orelse
        (MsgID =:= 2) orelse
        (MsgID =:= 3)) andalso 
       (is_integer(MaxOutstanding) andalso (MaxOutstanding > 0)) ->
    State;
process_client_args_ensure_max_outstanding(
  #{msg_id          := MsgID,
    max_outstanding := MaxOutstanding}) ->
    usage(f("Invalid Msg ID (~w) and Max Outstanding (~w)", 
            [MsgID, MaxOutstanding])).



%% ==========================================================================

exec(#{role      := server,
       active    := Active,
       transport := gen}) ->
    case socket_test_ttest_tcp_server_gen:start(Active) of
        {ok, {Pid, _}} ->
            MRef = erlang:monitor(process, Pid),
            receive
                {'DOWN', MRef, process, Pid, Info} ->
                    Info
            end;
        {error, Reason} ->
            eprint(f("Failed starting server: "
                     "~n   ~p", [Reason])),
            error
    end;
exec(#{role      := server,
       active    := Active,
       transport := {sock, Method}}) ->
    case socket_test_ttest_tcp_server_socket:start(Method, Active) of
        {ok, {Pid, _}} ->
            MRef = erlang:monitor(process, Pid),
            receive
                {'DOWN', MRef, process, Pid, Info} ->
                    Info
            end;
        {error, Reason} ->
            eprint(f("Failed starting server: "
                     "~n   ~p", [Reason])),
            error
    end;

exec(#{role   := client,
       server := undefined}) ->
    usage("Mandatory option 'server' not provided");
exec(#{role            := client,
       server          := {Addr, Port},
       active          := Active,
       transport       := gen,
       msg_id          := MsgID,
       max_outstanding := MaxOutstanding,
       runtime         := RunTime}) ->
    case socket_test_ttest_tcp_client_gen:start(true,
                                                Active,
                                                Addr, Port,
                                                MsgID, MaxOutstanding,
                                                RunTime) of
        {ok, Pid} ->
            MRef = erlang:monitor(process, Pid),
            receive
                {'DOWN', MRef, process, Pid, Info} ->
                    Info
            end;
        {error, Reason} ->
            eprint(f("Failed starting server: "
                     "~n   ~p", [Reason])),
            error
    end;
exec(#{role            := client,
       server          := {Addr, Port},
       active          := Active,
       transport       := {sock, Method},
       msg_id          := MsgID,
       max_outstanding := MaxOutstanding,
       runtime         := RunTime}) ->
    case socket_test_ttest_tcp_client_socket:start(true,
                                                   Method,
                                                   Active,
                                                   Addr, Port,
                                                   MsgID, MaxOutstanding,
                                                   RunTime) of
        {ok, Pid} ->
            MRef = erlang:monitor(process, Pid),
            receive
                {'DOWN', MRef, process, Pid, Info} ->
                    Info
            end;
        {error, Reason} ->
            eprint(f("Failed starting server: "
                     "~n   ~p", [Reason])),
            error
    end;
exec(_) ->
    usage("Unexpected option combo"),
    ok.



%% ==========================================================================
    
f(F, A) ->
    socket_test_ttest_lib:format(F, A).

eprint(ErrorString) when is_list(ErrorString) ->
    print("<ERROR> " ++ ErrorString ++ "~n", []).

print(F, A) ->
    io:format(F ++ "~n", A).

scriptname() ->
    FullName = escript:script_name(),
    filename:basename(FullName).