summaryrefslogtreecommitdiff
path: root/lib/common_test/src/test_server.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/common_test/src/test_server.erl')
-rw-r--r--lib/common_test/src/test_server.erl153
1 files changed, 130 insertions, 23 deletions
diff --git a/lib/common_test/src/test_server.erl b/lib/common_test/src/test_server.erl
index 1f7c565d09..feb607b46e 100644
--- a/lib/common_test/src/test_server.erl
+++ b/lib/common_test/src/test_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2021. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2022. 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.
@@ -39,10 +39,10 @@
-export([m_out_of_n/3,do_times/4,do_times/2]).
-export([call_crash/3,call_crash/4,call_crash/5]).
-export([temp_name/1]).
--export([start_node/3, stop_node/1, wait_for_node/1, is_release_available/1]).
+-export([start_node/3, stop_node/1, wait_for_node/1, is_release_available/1, find_release/1]).
+-export([peer_name/2, start_peer/3, start_peer/5]).
-export([app_test/1, app_test/2, appup_test/1]).
-export([comment/1, make_priv_dir/0]).
--export([os_type/0]).
-export([run_on_shielded_node/2]).
-export([is_cover/0,is_debug/0,is_commercial/0]).
@@ -347,8 +347,7 @@ stick_all_sticky(Node,Sticky) ->
%% Returns a tuple with the time spent (in seconds) in the test case,
%% the return value from the test case or an {'EXIT',Reason} if the case
%% failed, Loc points out where the test case crashed (if it did). Loc
-%% is either the name of the function, or {<Module>,<Line>} of the last
-%% line executed that had a ?line macro. If the test case did execute
+%% is the name of the function. If the test case did execute
%% erase/0 or similar, it may be empty. Comment is the last comment added
%% by test_server:comment/1, the reason if test_server:fail has been
%% called or the comment given by the return value {comment,Comment} from
@@ -360,7 +359,7 @@ stick_all_sticky(Node,Sticky) ->
%% TimetrapData = {MultiplyTimetrap,ScaleTimetrap}, which indicates a
%% possible extension of all timetraps. Timetraps will be multiplied by
%% MultiplyTimetrap. If it is infinity, no timetraps will be started at all.
-%% ScaleTimetrap indicates if test_server should attemp to automatically
+%% ScaleTimetrap indicates if test_server should attempt to automatically
%% compensate timetraps for runtime delays introduced by e.g. tools like
%% cover.
@@ -1790,7 +1789,7 @@ ts_tc(M, F, A) ->
set_loc(Stk),
case Type of
throw ->
- {failed,{thrown,Reason}};
+ {failed,{thrown,{Reason,Stk}}};
error ->
{'EXIT',{Reason,Stk}};
exit ->
@@ -1889,7 +1888,7 @@ capture_stop() ->
%% Note that since output arrive as messages to the process, it takes
%% a short while from the call to io:format until all output is available
%% by capture_get/0. It is not necessary to call capture_stop/0 before
-%% retreiving the output.
+%% retrieving the output.
capture_get() ->
test_server_sup:capture_get([]).
@@ -1970,6 +1969,9 @@ adjusted_sleep(MSecs) ->
%%
%% Immediately calls exit. Included because test suites are easier
%% to read when using this function, rather than exit directly.
+
+-spec fail(term()) -> no_return().
+
fail(Reason) ->
comment(cast_to_list(Reason)),
try
@@ -1994,6 +1996,9 @@ cast_to_list(X) -> lists:flatten(io_lib:format("~tp", [X])).
%%
%% Immediately calls exit. Included because test suites are easier
%% to read when using this function, rather than exit directly.
+
+-spec fail() -> no_return().
+
fail() ->
try
exit(suite_failed)
@@ -2553,10 +2558,10 @@ m_out_of_n(M,N,Fun) ->
%% Time - integer() in milliseconds.
%% Crash - term()
%%
-%% Spaws a new process that calls MFA. The call is considered
+%% Spawns a new process that calls MFA. The call is considered
%% successful if the call crashes with the given reason (Crash),
%% or any other reason if Crash is not specified.
-%% ** The call must terminate withing the given Time (defaults
+%% ** The call must terminate within the given Time (defaults
%% to infinity), or it is considered a failure (exit with reason
%% 'call_crash_timeout' is generated).
@@ -2575,14 +2580,12 @@ call_crash(Time,Crash,M,F,A) ->
%% Type = slave | peer
%% Options = [{tuple(), term()}]
%%
-%% OptionList is a tuplelist wich may contain one
+%% OptionList is a tuplelist which may contain one
%% or more of these members:
%%
%% Slave and Peer:
%% {remote, true} - Start the node on a remote host. If not specified,
-%% the node will be started on the local host (with
-%% some exceptions, for instance VxWorks,
-%% where all nodes are started on a remote host).
+%% the node will be started on the local host.
%% {args, Arguments} - Arguments passed directly to the node.
%% {cleanup, false} - Nodes started with this option will not be killed
%% by the test server after completion of the test case
@@ -2687,6 +2690,7 @@ wait_for_node(Slave) ->
end,
Result.
+-compile([{nowarn_deprecated_function, [{slave, stop, 1}]}]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% stop_node(Name) -> true|false
@@ -2766,6 +2770,118 @@ is_release_available(Release) ->
{test_server_ctrl,is_release_available,[Release]}},
receive {sync_result,R} -> R end.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% find_release(Release) -> PathToReleaseErlFile | not_available
+%% Release -> string()
+%%
+%% Test if a release (such as "r10b") and if so return the path to the
+%% release's erl file
+
+find_release(Release) ->
+ group_leader() ! {sync_apply,
+ self(),
+ {test_server_ctrl,find_release,[Release]}},
+ receive {sync_result,R} -> R end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% API for starting peer nodes according to Common Test conventions
+peer_name(Module, TestCase) ->
+ peer:random_name(lists:concat([Module, "-", TestCase])).
+
+%% Command line arguments passed
+-spec start_peer([string()] | peer:start_options(), atom() | string(), TestCase :: atom() | string()) ->
+ {ok, gen_statem:server_ref(), node()} | {error, term()}.
+start_peer(Args, Module, TestCase) when is_list(Args) ->
+ start_peer(#{args => Args, name => peer_name(Module, TestCase)}, Module);
+
+%% Full set of options passed
+start_peer(#{name := _Name} = Opts, Module, _TestCase) ->
+ start_peer(Opts, Module);
+start_peer(Opts, Module, TestCase) ->
+ start_peer(Opts#{name => peer_name(Module, TestCase)}, Module).
+
+%% Release compatibility testing
+-spec start_peer([string()] | peer:start_options(), atom() | string(), TestCase :: atom() | string(),
+ Release :: string(), OutDir :: file:filename()) ->
+ {ok, gen_statem:server_ref(), node()} | {error, term()} | not_available.
+start_peer(Args, Module, TestCase, Release, OutDir) when is_list(Args) ->
+ start_peer(#{args => Args}, Module, TestCase, Release, OutDir);
+start_peer(Opts, Module, TestCase, Release, OutDir) ->
+ case find_release(Release) of
+ not_available ->
+ not_available;
+ Erl ->
+ %% remove ERL_AFLAGS, because they may contain "-emu_type debug" which does not exist
+ %% for old releases. Keep ERL_FLAGS, and ERL_ZFLAGS for sometimes you might need it...
+ Env = maps:get(env, Opts, []) ++ [{"ERL_AFLAGS", false}],
+ NewArgs = ["-pa", peer_compile(Erl, code:which(peer), OutDir) | maps:get(args, Opts, [])],
+ start_peer(Opts#{exec => Erl, args => NewArgs,
+ env => Env}, Module, TestCase)
+ end.
+
+%% Internal implementation
+start_peer(#{name := Name} = Opts, Module) ->
+ CrashDir = test_server_sup:crash_dump_dir(),
+ CrashFile = filename:join([CrashDir, lists:concat(["erl_crash_dump.", Name])]),
+ Args = maps:get(args, Opts, []),
+ CookieArg =
+ case lists:member("-setcookie", Args) of
+ false ->
+ ["-setcookie", atom_to_list(erlang:get_cookie())];
+ true ->
+ []
+ end,
+ FullArgs = CookieArg ++ ["-pa", filename:dirname(code:which(Module)),
+ "-env", "ERL_CRASH_DUMP", CrashFile] ++ Args,
+ %% start_cover => false is intentionally undocumented, and is not
+ %% expected to be used by anything but cover_SUITE test.
+ case maps:get(start_cover, Opts, true) andalso test_server:is_cover() of
+ true ->
+ %% when cover is active, node must shut down gracefully, otherwise
+ %% coverage information won't be sent to cover master
+ CoverMain = cover:get_main_node(),
+ %% next line is a way to trick Dialyzer into not complaining over undocumented type
+ Shutdown = binary_to_term(term_to_binary({10000, CoverMain})),
+ case peer:start_link(Opts#{args => FullArgs, shutdown => Shutdown}) of
+ {ok, Peer, Node} ->
+ do_cover_for_node(Node, start),
+ {ok, Peer, Node};
+ Other ->
+ Other
+ end;
+ false ->
+ peer:start_link(Opts#{args => FullArgs})
+ end.
+
+%% When a different release is requested, peer.erl needs to be compiled for
+%% that specific release using the path supplied for 'erl'
+peer_compile(Erl, cover_compiled, OutDir) ->
+ {file, Path} = cover:is_compiled(peer),
+ peer_compile(Erl, Path, OutDir);
+peer_compile(Erl, ModPath, OutDir) ->
+ {ok, ModSrc} = filelib:find_source(ModPath),
+ Erlc = filename:join(filename:dirname(Erl), "erlc"),
+ cmd(Erlc, ["-o", OutDir, ModSrc]),
+ OutDir.
+
+%% This should really be implemented as os:cmd.
+cmd(Exec, Args) ->
+ %% remove all ERL_AFLAGS to drop "-emu_type debug" and similar
+ %% remote ERLC_COMPILE_SERVER because of a bug in pre 25.2 Erlang/OTP
+ Env = [{"ERL_AFLAGS", false},{"ERLC_USE_SERVER",false}],
+ Port = open_port({spawn_executable, Exec}, [{args, Args}, {env, Env},
+ stream, binary, exit_status, stderr_to_stdout]),
+ read_std(Port, lists:join(" ", [Exec|Args]), <<>>).
+
+read_std(Port, Exec, Out) ->
+ receive
+ {Port, {data, More}} ->
+ read_std(Port, Exec, <<Out/binary, More/binary>>);
+ {Port, {exit_status, 0}} ->
+ Out;
+ {Port, {exit_status, Status}} ->
+ erlang:error({exit, Status, Exec, Out})
+ end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% run_on_shielded_node(Fun, CArgs) -> term()
@@ -2911,15 +3027,6 @@ make_priv_dir() ->
tc_supervisor_req(make_priv_dir).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% os_type() -> OsType
-%%
-%% Returns the OsType of the target node. OsType is
-%% the same as returned from os:type()
-os_type() ->
- os:type().
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% is_cover() -> boolean()
%%
%% Returns true if cover is running, else false