File 1451-ssh-add-cth_events-and-event-verification-for-ssh_pr.patch of Package erlang
From 98fdca2e170585ff40a4224615d9d07354ec385d Mon Sep 17 00:00:00 2001
From: Jakub Witczak <kuba@erlang.org>
Date: Thu, 16 Oct 2025 18:00:30 +0200
Subject: [PATCH] ssh: add cth_events and event verification for
ssh_protocol_SUITE
- add ssh/test/cth_events - hook module for verifying logger
events
- verify logger events for ssh_protocol_SUITE
- use cth_events in other test suites
---
lib/ssh/test/Makefile | 1 +
lib/ssh/test/cth_events.erl | 97 +++++++++++++++++++++++++++
lib/ssh/test/ssh_basic_SUITE.erl | 33 +++------
lib/ssh/test/ssh_connection_SUITE.erl | 22 +++---
lib/ssh/test/ssh_protocol_SUITE.erl | 22 +++++-
lib/ssh/test/ssh_test_lib.erl | 14 ++++
6 files changed, 151 insertions(+), 38 deletions(-)
create mode 100644 lib/ssh/test/cth_events.erl
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index 44b1a82c3c..a92c7d551f 100644
--- a/lib/ssh/test/Makefile
+++ b/lib/ssh/test/Makefile
@@ -28,6 +28,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
# ----------------------------------------------------
MODULES= \
+ cth_events \
ssh_cth \
ssh_algorithms_SUITE \
ssh_options_SUITE \
diff --git a/lib/ssh/test/cth_events.erl b/lib/ssh/test/cth_events.erl
new file mode 100644
index 0000000000..8f3891884e
--- /dev/null
+++ b/lib/ssh/test/cth_events.erl
@@ -0,0 +1,97 @@
+%%
+%% %CopyrightBegin%
+%%
+%% SPDX-License-Identifier: Apache-2.0
+%%
+%% Copyright Ericsson AB 2011-2025. 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(cth_events).
+-moduledoc false.
+
+%%% This module verifies logger events.
+
+%% CTH Callbacks
+-export([id/1, init/2,
+ %% pre_init_per_suite/3, pre_end_per_suite/3, post_end_per_suite/4,
+ %% pre_init_per_group/4, post_init_per_group/5,
+ %% pre_end_per_group/4, post_end_per_group/5,
+ pre_init_per_testcase/4, %post_init_per_testcase/5,
+ %% pre_end_per_testcase/4,
+ post_end_per_testcase/5]).
+
+-behaviour(ct_hooks).
+
+id(_Opts) ->
+ ?MODULE.
+
+init(?MODULE, Opts) ->
+ GetValue =
+ fun(Property, PropList, Default) ->
+ case proplists:get_value(Property, PropList) of
+ undefined -> Default;
+ V -> V
+ end
+ end,
+
+ DefaultVerifyFun =
+ fun(_, 0) ->
+ ok;
+ (_, EventNumber) when EventNumber > 0 ->
+ {fail, lists:flatten(
+ io_lib:format("unexpected event cnt: ~s",
+ [integer_to_list(EventNumber)]))}
+ end,
+ VerifyFun = GetValue(verify_fun, Opts, DefaultVerifyFun),
+ SkipTc = GetValue(skip_tc, Opts, []),
+ ct_util:mark_process(), % ??
+ {ok, #{verify_fun => VerifyFun, skip_tc => SkipTc}}.
+
+%% FIXME in parallel executions (e.g. ssh_basic_SUITE:p_basic group) this setup does not
+%% work log handlers are uniq per testcase, but they all receive same
+%% logger events; so if one testcase fails due to logger events, rest
+%% of group might fail as well
+pre_init_per_testcase(_Suite, TestCase, Config0, State = #{skip_tc := SkipTc}) ->
+ case lists:member(TestCase, SkipTc) of
+ false ->
+ Config = ssh_test_lib:add_log_handler(TestCase, Config0),
+ {Config, State};
+ true ->
+ {Config0, State}
+ end.
+
+post_end_per_testcase(_Suite, TestCase, Config, Result,
+ State = #{skip_tc := SkipTc,
+ verify_fun := VerifyFun}) ->
+ case lists:member(TestCase, SkipTc) of
+ false ->
+ {ok, Events} = ssh_test_lib:get_log_events(
+ proplists:get_value(log_handler_ref, Config)),
+ EventCnt = length(Events),
+ {ok, InterestingEventCnt} = ssh_test_lib:analyze_events(Events, EventCnt),
+ VerificationResult = VerifyFun(TestCase, InterestingEventCnt),
+ ssh_test_lib:rm_log_handler(TestCase),
+ case VerificationResult of
+ ok ->
+ {Result, State};
+ _ ->
+ {VerificationResult, State}
+ end;
+ true ->
+ {Result, State}
+ end.
+
+
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 8027e5b43c..98d5868a7f 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -109,7 +109,9 @@
%%--------------------------------------------------------------------
suite() ->
- [{ct_hooks,[ts_install_cth]},
+ [{ct_hooks,[ts_install_cth,
+ {cth_events,
+ [{verify_fun, fun verify_events/2}]}]},
{timetrap,{seconds,90}}].
all() ->
@@ -190,10 +192,9 @@ init_per_group(_, Config) ->
end_per_group(_, Config) ->
Config.
%%--------------------------------------------------------------------
-init_per_testcase(TestCase, Config0)
+init_per_testcase(TestCase, Config)
when TestCase==shell_no_unicode;
TestCase==shell_unicode_string ->
- Config = ssh_test_lib:add_log_handler(TestCase, Config0),
PrivDir = proplists:get_value(priv_dir, Config),
UserDir = proplists:get_value(priv_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
@@ -210,8 +211,7 @@ init_per_testcase(TestCase, Config0)
ct:log("file:native_name_encoding() = ~p,~nio:getopts() = ~p",
[file:native_name_encoding(),io:getopts()]),
wait_for_erlang_first_line([{io,IO}, {shell,Shell}, {sftpd, Sftpd} | Config]);
-init_per_testcase(TestCase = inet6_option, Config0) ->
- Config = ssh_test_lib:add_log_handler(TestCase, Config0),
+init_per_testcase(inet6_option, Config) ->
case ssh_test_lib:has_inet6_address() of
true ->
init_per_testcase('__default__', Config);
@@ -226,26 +226,13 @@ end_per_testcase(TestCase, Config)
TestCase==shell_unicode_string ->
case proplists:get_value(sftpd, Config) of
{Pid, _, _} ->
- catch ssh:stop_daemon(Pid);
+ catch ssh:stop_daemon(Pid),
+ ok;
_ ->
ok
- end,
- process_events(TestCase, Config);
-end_per_testcase(TestCase, Config) ->
- process_events(TestCase, Config).
-
-%% FIXME in parallel executions (p_basic group) this setup does not
-%% work log handlers are uniq per testcase, but they all receive same
-%% logger events; so if one testcase fails due to logger events, rest
-%% of group might fail as well
-process_events(TestCase, Config) ->
- {ok, Events} = ssh_test_lib:get_log_events(
- proplists:get_value(log_handler_ref, Config)),
- EventCnt = length(Events),
- {ok, InterestingEventCnt} = ssh_test_lib:analyze_events(Events, EventCnt),
- VerificationResult = verify_events(TestCase, InterestingEventCnt),
- ssh_test_lib:rm_log_handler(TestCase),
- VerificationResult.
+ end;
+end_per_testcase(_TestCase, _Config) ->
+ ok.
verify_events(_TestCase, 0) ->
ok;
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
index 21eab4c69f..da1fef6748 100644
--- a/lib/ssh/test/ssh_connection_SUITE.erl
+++ b/lib/ssh/test/ssh_connection_SUITE.erl
@@ -125,7 +125,10 @@
%% [{ct_hooks,[ts_install_cth]}].
suite() ->
- [{timetrap,{seconds,40}}].
+ [{ct_hooks,[ts_install_cth,
+ {cth_events,
+ [{verify_fun, fun verify_events/2}]}]},
+ {timetrap,{seconds,40}}].
all() ->
[
@@ -235,21 +238,14 @@ end_per_group(_, Config) ->
Config.
%%--------------------------------------------------------------------
-init_per_testcase(TestCase, Config) ->
+init_per_testcase(_TestCase, Config) ->
ssh:stop(),
ssh:start(),
ssh_test_lib:verify_sanity_check(Config),
- ssh_test_lib:add_log_handler(TestCase, Config).
-
-end_per_testcase(TestCase, Config) ->
- {ok, Events} = ssh_test_lib:get_log_events(
- proplists:get_value(log_handler_ref, Config)),
- EventCnt = length(Events),
- {ok, InterestingEventCnt} = ssh_test_lib:analyze_events(Events, EventCnt),
- VerificationResult = verify_events(TestCase, InterestingEventCnt),
- ssh_test_lib:rm_log_handler(TestCase),
- ssh:stop(),
- VerificationResult.
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ssh:stop().
verify_events(_TestCase, 0) -> ok;
verify_events(no_sensitive_leak, 1) -> ok;
diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl
index 1d905c2644..1eec7924a3 100644
--- a/lib/ssh/test/ssh_protocol_SUITE.erl
+++ b/lib/ssh/test/ssh_protocol_SUITE.erl
@@ -123,7 +123,25 @@
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
suite() ->
- [{ct_hooks,[ts_install_cth]},
+ VerifyFun =
+ fun(_, 0) ->
+ ok;
+ (client_close_after_hello, 1) ->
+ ok;
+ (extra_ssh_msg_service_request, 1) ->
+ ok;
+ (_, EventNumber) ->
+ {fail, lists:flatten(
+ io_lib:format("unexpected event cnt: ~s",
+ [integer_to_list(EventNumber)]))}
+ end,
+ SkipTc = [kex_strict_negotiated,
+ kex_strict_violation,
+ kex_strict_violation_2],
+ [{ct_hooks,[ts_install_cth,
+ {cth_events,
+ [{verify_fun, VerifyFun},
+ {skip_tc, SkipTc}]}]},
{timetrap,{seconds,40}}].
all() ->
@@ -550,7 +568,7 @@ no_common_alg_client_disconnects(Config) ->
ct:log("ERROR!~nOp = ~p~nExecResult = ~p~nState =~n~s",
[Op,ExecResult,ssh_trpt_test_lib:format_msg(S)]),
{fail, ExecResult};
- X ->
+ {result, Pid, X} ->
ct:log("¤¤¤¤¤"),
ct:fail(X)
after
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 4dfcc3fe87..9c8b01c1c0 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -1426,6 +1426,14 @@ process_event(#{msg := {report,
io_lib:format("[~44s] ~6s ~30s ~20s ~30s ~20s:~10s(~40s)~n",
[io_lib:format("~p", [E]) ||
E <- [Pid, Level, Label, Status, Id, M, F, Args]]);
+process_event(#{msg := {report,
+ #{label := Label,
+ report := [MsgString]}},
+ meta := #{pid := Pid},
+ level := Level}) ->
+ io_lib:format("[~44s] ~6s ~20s ~s~n",
+ [io_lib:format("~p", [E]) ||
+ E <- [Pid, Level, Label]] ++ [MsgString]);
process_event(#{msg := {report,
#{label := Label,
name := Pid,
@@ -1449,6 +1457,12 @@ process_event(#{msg := {Format, Args},
io_lib:format("[~44s] ~6s~n~s~n",
[io_lib:format("~p", [E]) ||
E <- [Pid, Level]] ++ [io_lib:format(Format, Args)]);
+process_event(#{msg := {string, MsgString},
+ meta := #{pid := Pid},
+ level := Level}) when is_list(MsgString) ->
+ io_lib:format("[~44s] ~6s ~s~n",
+ [io_lib:format("~p", [E]) ||
+ E <- [Pid, Level]] ++ [MsgString]);
process_event(#{msg := {report,
#{label := Label,
reason := Reason,
--
2.51.0