Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:25
erlang
4571-ct-cte_track-for-tracking-CT-events.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 4571-ct-cte_track-for-tracking-CT-events.patch of Package erlang
From 856b4bd4e338cb23d954939c3ecfafa4e5da9fa6 Mon Sep 17 00:00:00 2001 From: Jakub Witczak <kuba@erlang.org> Date: Sat, 18 Nov 2023 21:24:44 +0100 Subject: [PATCH] ct: cte_track for tracking CT events - event handling module for tracking CT execution progress - displays additional terminal summary at the end of test run - optionally displays execution progress on terminal - its output can be redirected to file --- lib/common_test/src/Makefile | 1 + lib/common_test/src/common_test.app.src | 1 + lib/common_test/src/cte_track.erl | 294 ++++++++++++++++++++++++ lib/inets/test/inets.spec | 5 +- lib/ssh/test/ssh.spec | 12 +- lib/ssl/test/ssl_gh.spec | 3 + 6 files changed, 308 insertions(+), 8 deletions(-) create mode 100644 lib/common_test/src/cte_track.erl diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile index 6e84601c18..ce730de17f 100644 --- a/lib/common_test/src/Makefile +++ b/lib/common_test/src/Makefile @@ -71,6 +71,7 @@ MODULES= \ ct_config_xml \ ct_slave \ ct_hooks_lock\ + cte_track\ cth_log_redirect\ cth_surefire \ ct_netconfc \ diff --git a/lib/common_test/src/common_test.app.src b/lib/common_test/src/common_test.app.src index fd7025937f..dcc1ccf7fe 100644 --- a/lib/common_test/src/common_test.app.src +++ b/lib/common_test/src/common_test.app.src @@ -54,6 +54,7 @@ ct_config_plain, ct_config_xml, ct_slave, + cte_track, cth_log_redirect, cth_conn_log, cth_surefire, diff --git a/lib/common_test/src/cte_track.erl b/lib/common_test/src/cte_track.erl new file mode 100644 index 0000000000..2e64bc1ea4 --- /dev/null +++ b/lib/common_test/src/cte_track.erl @@ -0,0 +1,294 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2023. 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(cte_track). +%% module for tracking CT execution progress +%% test spec addition examples: +%% {event_handler, {cte_track, []}}. +%% {event_handler, {cte_track, [{file, "/tmp/log.txt"}]}}. + +-behaviour(gen_event). + +%% gen_event callbacks +-export([init/1, handle_event/2, handle_call/2, + handle_info/2, terminate/2, code_change/3]). +-include_lib("common_test/include/ct_event.hrl"). + +%%==================================================================== +%% gen_event callbacks +%%==================================================================== +init(InitArgs) -> + Device = + case proplists:get_value(file, InitArgs) of + undefined -> + user; + Path when is_list(Path) -> + {ok, D} = file:open(Path, [write]), + D + end, + [begin + dbg:start(), + dbg:tracer(), + dbg:tp(?MODULE, handle_event, 2, []), + dbg:p(all,c), + print(Device, "DEBUG started ~n") + end || lists:member(debug, InitArgs)], + {ok, #{device => Device, + suite => undefined, group => undefined, + test_stats => {0, 0, {0, 0}}, + test => #{ok => [], skipped => [], failed => [], auto_skipped => []}, + conf => #{ok => [], skipped => [], failed => [], auto_skipped => []}, + start_time => get_seconds()}}. + +handle_event(#event{name = test_stats, data = TestStats}, State) -> + {ok, maps:put(test_stats, TestStats, State)}; +handle_event(#event{name = tc_start, + data = {Suite, init_per_suite}}, + State0 = #{device := D, + test_stats := TestStats}) -> + print(D, "~n~p: ", [Suite]), + State1 = maps:put(suite, Suite, State0), + State = maps:put(suite_stats0, TestStats, State1), + {ok, State}; +handle_event(#event{name = tc_start, + data = {_Suite, {init_per_group, GroupName, _}}}, + State) -> + {ok, maps:put(group, GroupName, State)}; +handle_event(#event{name = Name, + data = {Suite, end_per_suite, Result}}, + State0 = #{device := Device, group := Group, + suite_stats0 := {SOk, SFailed, {SUserSkip, SAutoSkip}}, + test_stats := {Ok, Failed, {UserSkip, AutoSkip}}}) + when Name == tc_done; Name == tc_user_skip; Name == tc_auto_skip-> + State1 = maps:put(suite, undefined, State0), + State2 = maps:put(group, undefined, State1), + State = + case Name of + tc_done -> + handle_result(Suite, Group, end_per_suite, Result, undefined, + State2); + Skip when Skip == tc_user_skip; Skip == tc_auto_skip -> + handle_result(Suite, Group, end_per_suite, skipped, Result, + State2) + end, + print(Device, " | ~p ok, ~p failed, ~pU/~pA skipped", + [Ok - SOk, Failed - SFailed, UserSkip - SUserSkip, AutoSkip - SAutoSkip]), + {ok, State}; +handle_event(#event{name = tc_done, + data = {Suite, Case, ok}}, + State = #{group := Group}) + when is_atom(Case) -> + {ok, handle_result(Suite, Group, Case, ok, undefined, State)}; +handle_event(#event{name = tc_done, + data = {_Suite, Case, {SkipOrFailed, Reason}}}, + State = #{suite := Suite, group := Group}) + when is_atom(Case) -> + {ok, handle_result(Suite, Group, Case, SkipOrFailed, Reason, State)}; +handle_event(#event{name = tc_done, + data = {_Suite, {Case, _, _}, {skipped, Reason}}}, + State = #{suite := Suite, group := Group}) + when Case == init_per_group; Case == end_per_group -> + {ok, handle_result(Suite, Group, Case, skipped, Reason, State)}; +handle_event(#event{name = tc_done, + data = {_Suite, {Case, _, _}, ok}}, + State) + when Case == init_per_group; Case == end_per_group-> + % FIXME - not storing conf function result + {ok, State}; +handle_event(#event{name = tc_done, + data = Data}, + State = #{device := D}) -> + print(D, "~n~nUnhandled interesting event:~nName = tc_done~nData = ~p~n~n", + [Data]), + {ok, State}; +handle_event(#event{name = Name, data = {Suite, {Case, Group}, Comment}}, + State) when Name == tc_user_skip; Name == tc_auto_skip -> + {ok, handle_result(Suite, Group, Case, skipped, Comment, State)}; +handle_event(#event{name = Name, data = {Suite, Case, Comment}}, + State) when Name == tc_user_skip; Name == tc_auto_skip -> + {ok, handle_result(Suite, undefined, Case, skipped, Comment, State)}; +handle_event(#event{name = test_done}, + State = #{test := Test, + conf := Conf, + start_time := StartTime, + device := D, + test_stats := {Ok, Failed, {UserSkip, AutoSkip}}}) -> + print_line(D), + PrintCategory = + fun(Result, Results) -> + List = maps:get(Result, Results), + [begin + print_entry(D, Result, I) + end || I <- lists:reverse(List)] + end, + print(D, "# CONF SUMMARY~n"), + PrintCategory(auto_skipped, Conf), + PrintCategory(failed, Conf), + print(D, "# TEST SUMMARY~n"), + PrintCategory(auto_skipped, Test), + PrintCategory(failed, Test), + print(D, "~n"), + GetCount = fun(Result) -> + length(maps:get(Result, Conf)) + end, + print(D, + "# CONF TOTALS ~p ok, ~p failed, ~pU/~pA skipped~n" ++ + "# TEST TOTALS ~p ok, ~p failed, ~pU/~pA skipped~n" ++ + "# TIME ELAPSED " ++ + get_nice_time(get_seconds() - StartTime) ++ "~n", + [GetCount(ok), GetCount(failed), GetCount(skipped), + GetCount(auto_skipped)] ++ + [Ok, Failed, UserSkip, AutoSkip]), + print_line(D), + print(D, "~n"), + {ok,State}; +handle_event(#event{name = Name, data = Data}, State = #{device := D}) -> + NotInteresting = [start_logging, start_write_file, finished_write_file, + test_stats, start_make, finished_make, tc_logfile, + test_start, start_info, tc_start, stop_logging], + case lists:member(Name, NotInteresting) of + false -> + print(D, "~n~nUnhandled interesting event:~nName = ~p~nData = ~p~n~n", + [Name, Data]); + _ -> + ok + end, + {ok,State}. + +handle_call(_Req, State) -> + Reply = ok, + {ok, Reply, State}. + +handle_info(_Info, State) -> + {ok, State}. + +terminate(stop, #{device := user}) -> + ok; +terminate(stop, #{device := Device}) -> + file:close(Device), + ok; +terminate(Reason, #{device := D}) -> + print(D, "~n > Interesting terminate reason = ~p~n", [Reason]), + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%-------------------------------------------------------------------- +print(Device, Fmt) -> + print(Device, Fmt, []). + +print(Device, Fmt, Args) -> + io:fwrite(Device, Fmt, Args). + +print_line(Device) -> + print(Device, "~n======================================================================~n"). + +% FIXME - split handle_result into: store_event and present_event +handle_result(Suite, _Group, all, skipped, _Reason, + State0 = #{device := Device}) -> + % FIXME store this conf fun result + print(Device, "~n~p: suite skipped", [Suite]), + State0; +handle_result(Suite, Group, Case, Result, Reason0, + State0 = #{device := Device}) -> + Type = type(Case), + TypeResults = maps:get(Type, State0), + TypeResultAcc = maps:get(Result, TypeResults, []), + Reason = + case lists:keyfind(Reason0, 4, TypeResultAcc) of + false -> + Reason0; + _ -> + saved + end, + Entry = {Suite, Group, Case, Reason}, + [print_entry(Device, Result, Entry) || + is_test(Case)], + maps:put(Type, + maps:put(Result, [Entry | TypeResultAcc], TypeResults), + State0). + +print_entry(Device, Result, _Entry) when Result == ok; Result == skipped -> + print(Device, "~s", [result_short(Result)]); +print_entry(Device, Result, Entry) when is_atom(Result) -> + print_entry(Device, result_long(Result), Entry); +print_entry(Device, Result, {Suite, undefined, Case, Reason}) -> + print(Device, "~n~s ~p:~p ~s~n", + [Result, Suite, Case, format_reason(Reason)]); +print_entry(Device, Result, {Suite, Group, Case, Reason}) -> + print(Device, "~n~s ~p@~p:~p ~s~n", + [Result, Suite, Group, Case, format_reason(Reason)]). + +%result_short(failed) -> "F"; +result_short(ok) -> "."; +result_short(R) when R == skipped; R == auto_skipped -> "S"; +result_short(_R) -> "?". + +result_long(R) when R == skipped; R == auto_skipped; R == ok; R == failed -> + io_lib:format("[~s]", [atom_to_list(R)]); +result_long(_) -> "[?]". + +format_reason(saved) -> + ""; +format_reason(Reason) when is_list(Reason) -> + io_lib:format("Reason: ~p", [lists:flatten(Reason)]); +format_reason({Reason, SubReason}) -> + io_lib:format("Reason: ~p (~p)", [Reason, SubReason]); +format_reason({Reason, A, B, C}) -> + io_lib:format("Reason: ~p (~p ~p ~p)", [Reason, A, B, C]); +format_reason(undefined) -> + ""; +format_reason(Reason) -> + io_lib:format("~n > Not recognized reason FIXME! ~p", [Reason]). + +get_nice_time(Seconds) when is_integer(Seconds) -> + case Seconds < 60 of + true -> + io_lib:format("~ws", [Seconds]); + _ -> + io_lib:format("~wm", [round(Seconds/60)]) + end. + +get_seconds() -> + erlang:system_time(second). + +%% get_stats(#{ok := Ok, skipped := Skipped, auto_skipped := AutoSkipped, +%% failed := Failed}) -> +%% OkLength = length(Ok), +%% SkippedLength = length(Skipped) + length(AutoSkipped), +%% FailedLength = length(Failed), +%% Total = OkLength + SkippedLength + FailedLength, +%% [OkLength, SkippedLength, FailedLength, Total]. + +type(Case) -> + case is_test(Case) of + true -> test; + _ -> conf + end. + +is_test(Case) -> + not is_config(Case). + +is_config(Case) -> + % FIXME add 'all'? + lists:member(Case, [init_per_suite, end_per_suite, + init_per_group, end_per_group, + init_per_testcase, end_per_testcase]). diff --git a/lib/inets/test/inets.spec b/lib/inets/test/inets.spec index 6cb3d6526c..027b0f1630 100644 --- a/lib/inets/test/inets.spec +++ b/lib/inets/test/inets.spec @@ -1,3 +1,6 @@ {suites,"../inets_test", all}. -{skip_suites, "../inets_test", [httpd_bench_SUITE], +{skip_suites, "../inets_test", [httpd_bench_SUITE], "Benchmarks run separately"}. +{event_handler, {cte_track, []}}. +{enable_builtin_hooks, false}. +{ct_hooks, [{cth_log_redirect, [{mode, replace}]}]}. diff --git a/lib/ssh/test/ssh.spec b/lib/ssh/test/ssh.spec index b4e3d36072..b2b4f849c2 100644 --- a/lib/ssh/test/ssh.spec +++ b/lib/ssh/test/ssh.spec @@ -1,8 +1,6 @@ {suites,"../ssh_test",all}. - -{skip_suites, "../ssh_test", [ssh_bench_SUITE, - ssh_upgrade_SUITE - ], - "Benchmarks run separately"}. - - +{skip_suites, "../ssh_test", + [ssh_bench_SUITE, ssh_upgrade_SUITE], "Benchmarks run separately"}. +{event_handler, {cte_track, []}}. +{enable_builtin_hooks, false}. +{ct_hooks, [{cth_log_redirect, [{mode, replace}]}]}. diff --git a/lib/ssl/test/ssl_gh.spec b/lib/ssl/test/ssl_gh.spec index 1df89d2d01..2daea55774 100644 --- a/lib/ssl/test/ssl_gh.spec +++ b/lib/ssl/test/ssl_gh.spec @@ -12,3 +12,6 @@ {skip_groups,dir,ssl_dist_bench_SUITE,throughput,"Benchmarks run separately"}. {skip_groups,dir,ssl_dist_bench_SUITE,sched_utilization,"Benchmarks run separately"}. +{event_handler, {cte_track, []}}. +{enable_builtin_hooks, false}. +{ct_hooks, [{cth_log_redirect, [{mode, replace}]}]}. -- 2.35.3
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor