%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2018-2021. 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%
%%

%%

-module(ssh_chan_behaviours_SUITE).

-include_lib("common_test/include/ct.hrl").
-include("ssh.hrl").
-include("ssh_test_lib.hrl").

-export([
         suite/0,
         all/0,
         init_per_suite/1,
         end_per_suite/1,
         init_per_testcase/2,
         end_per_testcase/2
        ]).

-export([
         defined_subsystem/1,
         noexist_subsystem/1,
         subsystem_client/1,
         undefined_subsystem/1
        ]).

%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------

suite() ->
    [{ct_hooks,[ts_install_cth]},
     {timetrap,{seconds,60}}].

all() -> 
    [
     noexist_subsystem,
     undefined_subsystem,
     defined_subsystem,
     subsystem_client
    ].

%%--------------------------------------------------------------------
init_per_suite(Config) ->
    ?CHECK_CRYPTO(
       begin
           ssh:start(),
           Config
       end).

end_per_suite(_Config) ->
    {Time,R} = timer:tc(ssh, stop, []),
    ct:log("Stop ssh: ~p ms",[(100*(Time div 1000)) / 100]),
    R.

init_per_testcase(_TC, Config) ->
    SubSystems = [
                  {"bad_cb", {ssh_chan_behaviours_undefined, []}}, % A non-existing file
                  {"ch1",    {ssh_chan_behaviours_server,    [self(),true]}}
                 ],
    {Pid, Host, Port} = ssh_test_lib:std_daemon(Config, [{subsystems,SubSystems}]),
    C = ssh_test_lib:std_connect(Config, Host, Port, []),
    [{connref,C}, {daemon_pid,Pid}| Config].

end_per_testcase(_TC, Config) ->
    {Time,_} = timer:tc(ssh, stop_daemon, [proplists:get_value(daemon_pid,Config)]),
    ct:log("Stop daemon: ~p ms",[(100*(Time div 1000)) / 100]),
    case flush() of
        [] -> ok;
        Msgs -> ct:log("Unhandled messages:~n~p", [Msgs])
    end.
    

-define(EXPECT(What, Bind),
            (fun() ->
                     receive What ->
                             ct:log("~p:~p ~p got ~p",[?MODULE,?LINE,self(),What]),
                             Bind
                     after 5000 ->
                             ct:log("~p:~p ~p Flushed:~n~p",[?MODULE,?LINE,self(),flush()]),
                             ct:fail("Timeout!",[])
                     end
             end)()
        ).

%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
%% Try start a subsystem whose name is not known by the server
noexist_subsystem(Config) ->
    C = proplists:get_value(connref, Config),
    {ok, Ch} = ssh_connection:session_channel(C, infinity),
    failure = ssh_connection:subsystem(C, Ch, "noexist", infinity),
    ok = ssh_connection:close(C, Ch),
    ?EXPECT({ssh_cm,C,{closed,Ch}},[]),
    ok.

%% Try to start a subsystem with a known name, but without any callback file
undefined_subsystem(Config) ->
    C = proplists:get_value(connref, Config),
    {ok, Ch} = ssh_connection:session_channel(C, infinity),
    failure = ssh_connection:subsystem(C, Ch, "bad_cb", infinity),
    ok = ssh_connection:close(C, Ch),
    ?EXPECT({ssh_cm,C,{closed,Ch}},[]), % self() is instead of a proper channel handler
    ok.

%% Try to start and stop a subsystem with known name and defined callback file
defined_subsystem(Config) ->
    C = proplists:get_value(connref, Config),
    {ok, Ch1} = ssh_connection:session_channel(C, infinity),

    success = ssh_connection:subsystem(C, Ch1, "ch1", infinity),
    IDsrv = ?EXPECT({{Csrv,Ch1srv}, {ssh_channel_up,Ch1srv,Csrv}}, {Csrv,Ch1srv}),

    ok = ssh_connection:close(C, Ch1),
    ?EXPECT({IDsrv, {terminate,normal}}, []),
    ?EXPECT({ssh_cm, C, {closed,Ch1}}, []), % self() is instead of a proper channel handler
    ok.

%% Try to start and stop a subsystem from a ssh_client_channel behaviour
subsystem_client(Config) ->
    C = proplists:get_value(connref, Config),

    {ok,ChRef} = ssh_chan_behaviours_client:start_link(C),
    IDclt = ?EXPECT({{C,Ch1clt},     {ssh_channel_up,_C1clt,C}},     {C,Ch1clt}),
    IDsrv = ?EXPECT({{Csrv,_h1srv}, {ssh_channel_up,Ch1srv,Csrv}}, {Csrv,Ch1srv}),

    ok = ssh_chan_behaviours_client:stop(ChRef),
    ?EXPECT({IDclt, {terminate,normal}}, []), % From the proper channel handler
    ?EXPECT({IDsrv, {terminate,normal}}, []),
    ok.

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

flush() -> lists:reverse(flush([])).

flush(Acc) ->
    receive
        M ->
            flush([M|Acc])
    after 0 ->
            Acc
    end.
            
