Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang
erlang
3883-ct-ct_hooks_order-option.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 3883-ct-ct_hooks_order-option.patch of Package erlang
From c4e0179da6b1d26dfba7c187e5ac3d1e0c938b27 Mon Sep 17 00:00:00 2001 From: Jakub Witczak <kuba@erlang.org> Date: Wed, 12 Jul 2023 14:21:06 +0200 Subject: [PATCH 3/6] ct: ct_hooks_order option --- lib/common_test/src/ct.erl | 4 +- lib/common_test/src/ct_framework.erl | 2 + lib/common_test/src/ct_hooks.erl | 148 +++++++++++++++++++++------ lib/common_test/src/ct_run.erl | 29 +++++- lib/common_test/src/ct_testspec.erl | 4 + lib/common_test/src/ct_util.hrl | 3 +- 6 files changed, 151 insertions(+), 39 deletions(-) diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl index 012bb5c740..0c4166783e 100644 --- a/lib/common_test/src/ct.erl +++ b/lib/common_test/src/ct.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2022. All Rights Reserved. +%% Copyright Ericsson AB 2003-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. @@ -170,6 +170,7 @@ run(TestDirs) -> | {esc_chars, boolean()} | {keep_logs,KeepSpec} | {ct_hooks, CTHs} + | {ct_hooks_order, CTHsOrder} | {enable_builtin_hooks, boolean()} | {release_shell, boolean()}, TestDirs :: [string()] | string(), @@ -211,6 +212,7 @@ run(TestDirs) -> Category :: atom(), KeepSpec :: all | pos_integer(), CTHs :: [CTHModule | {CTHModule, CTHInitArgs}], + CTHsOrder :: atom(), CTHModule :: atom(), CTHInitArgs :: term(), Result :: {Ok, Failed, {UserSkipped, AutoSkipped}} | TestRunnerPid | {error, Reason}, diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl index ad01da29f6..7c704e36be 100644 --- a/lib/common_test/src/ct_framework.erl +++ b/lib/common_test/src/ct_framework.erl @@ -606,6 +606,8 @@ configure([{timetrap,Time}|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) -> configure(Rest,Info,SuiteInfo,Scope,PostInitHook1,Config); configure([{ct_hooks,Hook}|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) -> configure(Rest,Info,SuiteInfo,Scope,PostInitHook,[{ct_hooks,Hook}|Config]); +configure([{ct_hooks_order,Order}|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) -> + configure(Rest,Info,SuiteInfo,Scope,PostInitHook,[{ct_hooks_order,Order}|Config]); configure([_|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) -> configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config); configure([],_,_,_,PostInitHook,Config) -> diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl index 23fadcbc2c..6144b2458c 100644 --- a/lib/common_test/src/ct_hooks.erl +++ b/lib/common_test/src/ct_hooks.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2021. All Rights Reserved. +%% Copyright Ericsson AB 2004-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. @@ -31,7 +31,8 @@ -export([on_tc_fail/2]). %% If you change this, remember to update ct_util:look -> stop clause as well. --define(config_name, ct_hooks). +-define(hooks_name, ct_hooks). +-define(hooks_order_name, ct_hooks_order). %% All of the hooks which are to be started by default. Remove by issuing %% -enable_builtin_hooks false to when starting common test. @@ -49,6 +50,7 @@ -spec init(State :: term()) -> ok | {fail, Reason :: term()}. init(Opts) -> + process_hooks_order(?FUNCTION_NAME, Opts), call(get_builtin_hooks(Opts) ++ get_new_hooks(Opts, undefined), ok, init, []). @@ -56,16 +58,16 @@ init(Opts) -> groups(Mod, Groups) -> Info = try proplists:get_value(ct_hooks, Mod:suite(), []) of CTHooks when is_list(CTHooks) -> - [{?config_name,CTHooks}]; + [{?hooks_name,CTHooks}]; CTHook when is_atom(CTHook) -> - [{?config_name,[CTHook]}] + [{?hooks_name,[CTHook]}] catch _:_ -> %% since this might be the first time Mod:suite() %% is called, and it might just fail or return %% something bad, we allow any failure here - it %% will be caught later if there is something %% really wrong. - [{?config_name,[]}] + [{?hooks_name,[]}] end, case call(fun call_generic/3, Info ++ [{'$ct_groups',Groups}], [post_groups, Mod]) of [{'$ct_groups',NewGroups}] -> @@ -78,13 +80,13 @@ groups(Mod, Groups) -> all(Mod, Tests) -> Info = try proplists:get_value(ct_hooks, Mod:suite(), []) of CTHooks when is_list(CTHooks) -> - [{?config_name,CTHooks}]; + [{?hooks_name,CTHooks}]; CTHook when is_atom(CTHook) -> - [{?config_name,[CTHook]}] + [{?hooks_name,[CTHook]}] catch _:_ -> %% just allow any failure here - it will be caught %% later if there is something really wrong. - [{?config_name,[]}] + [{?hooks_name,[]}] end, case call(fun call_generic/3, Info ++ [{'$ct_all',Tests}], [post_all, Mod]) of [{'$ct_all',NewTests}] -> @@ -118,11 +120,11 @@ terminate(Hooks) -> init_tc(Mod, init_per_suite, Config) -> Info = try proplists:get_value(ct_hooks, Mod:suite(),[]) of List when is_list(List) -> - [{?config_name,List}]; + [{?hooks_name,List}]; CTHook when is_atom(CTHook) -> - [{?config_name,[CTHook]}] + [{?hooks_name,[CTHook]}] catch error:undef -> - [{?config_name,[]}] + [{?hooks_name,[]}] end, call(fun call_generic/3, Config ++ Info, [pre_init_per_suite, Mod]); @@ -158,13 +160,15 @@ init_tc(Mod, TC = error_in_suite, Config) -> {fail, Reason :: term()} | ok | '$ct_no_change'. -end_tc(Mod, init_per_suite, Config, _Result, Return) -> +end_tc(Mod, CFunc = init_per_suite, Config, _Result, Return) -> + process_hooks_order(CFunc, Return), call(fun call_generic/3, Return, [post_init_per_suite, Mod, Config], '$ct_no_change'); end_tc(Mod, end_per_suite, Config, Result, _Return) -> call(fun call_generic/3, Result, [post_end_per_suite, Mod, Config], '$ct_no_change'); -end_tc(Mod, {init_per_group, GroupName, _}, Config, _Result, Return) -> +end_tc(Mod, {CFunc = init_per_group, GroupName, _}, Config, _Result, Return) -> + process_hooks_order(CFunc, Return), call(fun call_generic_fallback/3, Return, [post_init_per_group, Mod, GroupName, Config], '$ct_no_change'); end_tc(Mod, {end_per_group, GroupName, Properties}, Config, Result, _Return) -> @@ -249,13 +253,15 @@ do_call_generic(#ct_hook_config{ module = Mod, state = State} = Hook, {NewValue, Hook#ct_hook_config{ state = NewState } }. %% Generic call function -call(Fun, Config, Meta) -> +call(Fun, Config, [CFunc | _] = Meta) -> maybe_lock(), Hooks = get_hooks(), Calls = get_new_hooks(Config, Fun) ++ [{HookId,Fun} || #ct_hook_config{id = HookId} <- Hooks], - Res = call(resort(Calls,Hooks,Meta), - remove(?config_name,Config), Meta, Hooks), + Order = process_hooks_order(CFunc, Config), + Res = call(resort(Calls,Hooks,Meta, Order), + remove([?hooks_name, ?hooks_order_name], Config), + Meta, Hooks), maybe_unlock(), Res. @@ -264,7 +270,6 @@ call(Fun, Config, Meta, NoChangeRet) when is_function(Fun) -> Config -> NoChangeRet; NewReturn -> NewReturn end; - call([{Hook, call_id, NextFun} | Rest], Config, Meta, Hooks) -> try {Config, #ct_hook_config{ id = NewId } = NewHook} = @@ -286,7 +291,9 @@ call([{Hook, call_id, NextFun} | Rest], Config, Meta, Hooks) -> {Hooks ++ [NewHook], Rest ++ [{NewId, call_init}, {NewId,NextFun}]} end, - call(resort(NewRest,NewHooks,Meta), Config, Meta, NewHooks) + {_, Order} = get_hooks_order(), + call(resort(NewRest, NewHooks, Meta, Order), Config, Meta, + NewHooks) catch Error:Reason:Trace -> ct_logs:log("Suite Hook","Failed to start a CTH: ~tp:~tp", [Error,{Reason,Trace}]), @@ -301,8 +308,12 @@ call([{HookId, Fun} | Rest], Config, Meta, Hooks) -> {NewConf, NewHook} = Fun(Hook, Config, Meta), NewCalls = get_new_hooks(NewConf, Fun), NewHooks = lists:keyreplace(HookId, #ct_hook_config.id, Hooks, NewHook), - call(resort(NewCalls ++ Rest,NewHooks,Meta), %% Resort if call_init changed prio - remove(?config_name, NewConf), Meta, + %% FIXME - not needed, but maybe logical? + %% process_hooks_order(NewConf), + {_, Order} = get_hooks_order(), + call(resort(NewCalls ++ Rest, NewHooks, + Meta, Order), %% Resort if call_init changed prio + remove([?hooks_name, ?hooks_order_name], NewConf), Meta, terminate_if_scope_ends(HookId, Meta, NewHooks)) catch throw:{error_in_cth_call,Reason} -> call(Rest, {fail, Reason}, Meta, @@ -310,8 +321,14 @@ call([{HookId, Fun} | Rest], Config, Meta, Hooks) -> end; call([], Config, _Meta, Hooks) -> save_suite_data_async(Hooks), + %% process_hooks_order([{?hooks_order_name, HooksOrder}]), Config. +remove([], List) when is_list(List) -> + List; +remove([Key|T], List) when is_list(List) -> + NewList = remove(Key, List), + remove(T, NewList); remove(Key,List) when is_list(List) -> [Conf || Conf <- List, is_tuple(Conf) =:= false orelse element(1, Conf) =/= Key]; @@ -392,9 +409,9 @@ get_new_hooks(Config, Fun) -> end, get_new_hooks(Config)). get_new_hooks(Config) when is_list(Config) -> - lists:flatmap(fun({?config_name, HookConfigs}) when is_list(HookConfigs) -> + lists:flatmap(fun({?hooks_name, HookConfigs}) when is_list(HookConfigs) -> HookConfigs; - ({?config_name, HookConfig}) when is_atom(HookConfig) -> + ({?hooks_name, HookConfig}) when is_atom(HookConfig) -> [HookConfig]; (_) -> [] @@ -411,10 +428,10 @@ get_builtin_hooks(Opts) -> end. save_suite_data_async(Hooks) -> - ct_util:save_suite_data_async(?config_name, Hooks). + ct_util:save_suite_data_async(?hooks_name, Hooks). get_hooks() -> - lists:keysort(#ct_hook_config.prio,ct_util:read_suite_data(?config_name)). + lists:keysort(#ct_hook_config.prio,ct_util:read_suite_data(?hooks_name)). %% Sort all calls in this order: %% call_id < call_init < ctfirst < Priority 1 < .. < Priority N < ctlast @@ -423,17 +440,38 @@ get_hooks() -> %% If we are doing a cleanup call i.e. {post,pre}_end_per_*, all priorities %% are reversed. Probably want to make this sorting algorithm pluginable %% as some point... -resort(Calls,Hooks,[F|_R]) when F == pre_end_per_testcase; - F == post_end_per_testcase; - F == pre_end_per_group; - F == post_end_per_group; - F == pre_end_per_suite; - F == post_end_per_suite -> - lists:reverse(resort(Calls,Hooks)); - -resort(Calls,Hooks,_Meta) -> +resort(Calls, Hooks, [CFunc|_R], HooksOrder) -> + Resorted = resort(Calls, Hooks), + ReversedHooks = + case HooksOrder of + config -> + %% reversed order for all post hooks (config centric order) + %% ct_hooks_order is 'config' + [post_init_per_testcase, + post_end_per_testcase, + post_init_per_group, + post_end_per_group, + post_init_per_suite, + post_end_per_suite]; + _ -> + %% reversed order for all end hooks (testcase centric order) + %% default or when ct_hooks_order is 'test' + [pre_end_per_testcase, + post_end_per_testcase, + pre_end_per_group, + post_end_per_group, + pre_end_per_suite, + post_end_per_suite] + end, + case lists:member(CFunc, ReversedHooks) of + true -> + lists:reverse(Resorted); + _ -> + Resorted + end; +resort(Calls,Hooks,_Meta, _HooksOrder) -> resort(Calls,Hooks). - + resort(Calls, Hooks) -> lists:sort( fun({_,_,_},_) -> @@ -498,6 +536,48 @@ catch_apply(M,F,A) -> [M,F,length(A)]))}) end. +process_hooks_order(Stage = init, Return) when is_list(Return) -> + maybe_save_hooks_order(Stage, Return); +process_hooks_order(Stage, Return) when is_list(Return) -> + {StoredStage, StoredOrder0} = get_hooks_order(), + DeleteConditions = + [{pre_end_per_suite, init_per_group}, + {pre_end_per_suite, pre_init_per_group}, + {pre_end_per_group, pre_init_per_testcase}], + StoredOrder = + case lists:member({Stage, StoredStage}, DeleteConditions) of + true-> + ct_util:delete_suite_data(?hooks_order_name), + undefined; + _ -> + StoredOrder0 + end, + case StoredOrder of + undefined -> + maybe_save_hooks_order(Stage, Return); + _ -> + StoredOrder + end; +process_hooks_order(_Stage, _) -> + nothing_to_save. + +get_hooks_order() -> + Value = ct_util:read_suite_data(?hooks_order_name), + case Value of + undefined -> + {undefined, undefined}; + {_, _} -> + Value + end. + +maybe_save_hooks_order(Stage, Return) -> + case proplists:get_value(?hooks_order_name, Return) of + Order when Order == config -> + ct_util:save_suite_data_async(?hooks_order_name, {Stage, Order}), + Order; + _ -> + test + end. %% We need to lock around the state for parallel groups only. This is because %% we will get several processes reading and writing the state for a single diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl index fa72f4e68a..3507f3513d 100644 --- a/lib/common_test/src/ct_run.erl +++ b/lib/common_test/src/ct_run.erl @@ -59,6 +59,7 @@ config = [], event_handlers = [], ct_hooks = [], + ct_hooks_order, enable_builtin_hooks, include = [], auto_compile, @@ -248,6 +249,10 @@ script_start1(Parent, Args) -> end, Args), EvHandlers = event_handler_args2opts(Args), CTHooks = ct_hooks_args2opts(Args), + CTHooksOrder = get_start_opt(ct_hooks_order, + fun([CTHO]) -> list_to_atom(CTHO); + ([]) -> undefined + end, undefined, Args), EnableBuiltinHooks = get_start_opt(enable_builtin_hooks, fun([CT]) -> list_to_atom(CT); ([]) -> undefined @@ -352,6 +357,7 @@ script_start1(Parent, Args) -> verbosity = Verbosity, event_handlers = EvHandlers, ct_hooks = CTHooks, + ct_hooks_order = CTHooksOrder, enable_builtin_hooks = EnableBuiltinHooks, auto_compile = AutoCompile, abort_if_missing_suites = AbortIfMissing, @@ -539,6 +545,10 @@ combine_test_opts(TS, Specs, Opts) -> [Opts#opts.ct_hooks, TSOpts#opts.ct_hooks]), + AllCTHooksOrder = + choose_val(Opts#opts.ct_hooks_order, + TSOpts#opts.ct_hooks_order), + EnableBuiltinHooks = choose_val( Opts#opts.enable_builtin_hooks, @@ -603,6 +613,7 @@ combine_test_opts(TS, Specs, Opts) -> config = TSOpts#opts.config, event_handlers = AllEvHs, ct_hooks = AllCTHooks, + ct_hooks_order = AllCTHooksOrder, enable_builtin_hooks = EnableBuiltinHooks, stylesheet = Stylesheet, auto_compile = AutoCompile, @@ -614,14 +625,16 @@ combine_test_opts(TS, Specs, Opts) -> check_and_install_configfiles( Configs, LogDir, #opts{ - event_handlers = EvHandlers, - ct_hooks = CTHooks, - enable_builtin_hooks = EnableBuiltinHooks} ) -> + event_handlers = EvHandlers, + ct_hooks = CTHooks, + ct_hooks_order = CTHooksOrder, + enable_builtin_hooks = EnableBuiltinHooks} ) -> case ct_config:check_config_files(Configs) of false -> install([{config,Configs}, {event_handler,EvHandlers}, {ct_hooks,CTHooks}, + {ct_hooks_order,CTHooksOrder}, {enable_builtin_hooks,EnableBuiltinHooks}], LogDir); {value,{error,{nofile,File}}} -> {error,{cant_read_config_file,File}}; @@ -957,6 +970,11 @@ run_test2(StartOpts) -> %% CT Hooks CTHooks = get_start_opt(ct_hooks, value, [], StartOpts), + CTHooksOrder = get_start_opt(ct_hooks_order, + fun(CHO) when CHO == test; + CHO == config -> + CHO + end, undefined, StartOpts), EnableBuiltinHooks = get_start_opt(enable_builtin_hooks, fun(EBH) when EBH == true; EBH == false -> @@ -1073,6 +1091,7 @@ run_test2(StartOpts) -> verbosity = Verbosity, event_handlers = EvHandlers, ct_hooks = CTHooks, + ct_hooks_order = CTHooksOrder, enable_builtin_hooks = EnableBuiltinHooks, auto_compile = AutoCompile, abort_if_missing_suites = AbortIfMissing, @@ -1200,6 +1219,7 @@ run_dir(Opts = #opts{logdir = LogDir, config = CfgFiles, event_handlers = EvHandlers, ct_hooks = CTHook, + ct_hooks_order = CTHooksOrder, enable_builtin_hooks = EnableBuiltinHooks}, StartOpts) -> LogDir1 = which(logdir, LogDir), @@ -1226,6 +1246,7 @@ run_dir(Opts = #opts{logdir = LogDir, case install([{config,AbsCfgFiles}, {event_handler,EvHandlers}, {ct_hooks, CTHook}, + {ct_hooks_order, CTHooksOrder}, {enable_builtin_hooks,EnableBuiltinHooks}], LogDir1) of ok -> ok; {error,_IReason} = IError -> exit(IError) @@ -1417,6 +1438,7 @@ get_data_for_node(#testspec{label = Labels, userconfig = UsrCfgs, event_handler = EvHs, ct_hooks = CTHooks, + ct_hooks_order = CTHooksOrder, enable_builtin_hooks = EnableBuiltinHooks, auto_compile = ACs, abort_if_missing_suites = AiMSs, @@ -1471,6 +1493,7 @@ get_data_for_node(#testspec{label = Labels, config = ConfigFiles, event_handlers = EvHandlers, ct_hooks = FiltCTHooks, + ct_hooks_order = CTHooksOrder, enable_builtin_hooks = EnableBuiltinHooks, auto_compile = AutoCompile, abort_if_missing_suites = AbortIfMissing, diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl index 9b63c0d60b..1b9fc8ee15 100644 --- a/lib/common_test/src/ct_testspec.erl +++ b/lib/common_test/src/ct_testspec.erl @@ -981,6 +981,9 @@ add_tests([{event_handler,Node,HOrHs,Args}|Ts],Spec) -> add_tests([{enable_builtin_hooks,Bool}|Ts],Spec) -> add_tests(Ts, Spec#testspec{enable_builtin_hooks = Bool}); +add_tests([{ct_hooks_order,Order}|Ts],Spec) -> + add_tests(Ts, Spec#testspec{ct_hooks_order = Order}); + add_tests([{release_shell,Bool}|Ts],Spec) -> add_tests(Ts, Spec#testspec{release_shell = Bool}); @@ -1592,6 +1595,7 @@ valid_terms() -> {event_handler,4}, {ct_hooks,2}, {ct_hooks,3}, + {ct_hooks_order,2}, {enable_builtin_hooks,2}, {release_shell,2}, {multiply_timetraps,2}, diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl index 7eba02d148..e0145b0588 100644 --- a/lib/common_test/src/ct_util.hrl +++ b/lib/common_test/src/ct_util.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2020. All Rights Reserved. +%% Copyright Ericsson AB 2003-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. @@ -47,6 +47,7 @@ userconfig=[], event_handler=[], ct_hooks=[], + ct_hooks_order, enable_builtin_hooks=true, release_shell=false, include=[], -- 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