%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%%------------------------------------------------------------------
%%% Test Server self test.
%%%------------------------------------------------------------------
-module(test_server_SUITE).
-include_lib("test_server/include/test_server.hrl").
-include_lib("test_server/include/test_server_line.hrl").
-include_lib("kernel/include/file.hrl").
-export([all/1]).
-export([init_per_suite/1, end_per_suite/1]).
-export([init_per_testcase/2, end_per_testcase/2, fin_per_testcase/2]).
-export([config/1, comment/1, timetrap/1, timetrap_cancel/1, multiply_timetrap/1,
init_per_s/1, init_per_tc/1, end_per_tc/1,
timeconv/1, msgs/1, capture/1, timecall/1,
do_times/1, do_times_mfa/1, do_times_fun/1,
skip_cases/1, skip_case1/1, skip_case2/1, skip_case3/1,
skip_case4/1, skip_case5/1, skip_case6/1, skip_case7/1,
skip_case8/1, skip_case9/1, undefined_functions/1,
conf_init/1, check_new_conf/1, conf_cleanup/1,
check_old_conf/1, conf_init_fail/1, start_stop_node/1,
cleanup_nodes_init/1, check_survive_nodes/1, cleanup_nodes_fin/1,
commercial/1]).
-export([dummy_function/0,dummy_function/1,doer/1]).
all(doc) -> ["Test Server self test"];
all(suite) ->
[config, comment, timetrap, timetrap_cancel, multiply_timetrap,
init_per_s, init_per_tc, end_per_tc,
timeconv, msgs, capture, timecall, do_times, skip_cases,
undefined_functions, commercial,
{conf, conf_init, [check_new_conf], conf_cleanup},
check_old_conf,
{conf, conf_init_fail,[conf_member_skip],conf_cleanup_skip},
start_stop_node,
{conf, cleanup_nodes_init,[check_survive_nodes],cleanup_nodes_fin},
config
].
init_per_suite(Config) ->
[{init_per_suite_var,ok}|Config].
end_per_suite(_Config) ->
ok.
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
Dog = ?t:timetrap(?t:minutes(2)),
Config1 = [{watchdog, Dog}|Config],
case Func of
init_per_tc ->
[{strange_var, 1}|Config1];
skip_case8 ->
{skipped, "This case should be noted as `Skipped'"};
skip_case9 ->
{skip, "This case should be noted as `Skipped'"};
_ ->
Config1
end;
init_per_testcase(Func, Config) ->
io:format("Func:~p",[Func]),
io:format("Config:~p",[Config]),
?t:fail("Arguments to init_per_testcase not correct").
end_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
Dog=?config(watchdog, Config),
?t:timetrap_cancel(Dog),
case Func of
end_per_tc -> io:format("CLEANUP => this test case is ok\n");
_Other -> ok
end;
end_per_testcase(Func, Config) ->
io:format("Func:~p",[Func]),
io:format("Config:~p",[Config]),
?t:fail("Arguments to end_per_testcase not correct").
fin_per_testcase(Func, Config) ->
io:format("Func:~p",[Func]),
io:format("Config:~p",[Config]),
?t:fail("fin_per_testcase/2 called, should have called end_per_testcase/2").
config(suite) -> [];
config(doc) -> ["Test that the Config variable is decent, ",
"and that the std config variables are correct ",
"(check that data/priv dir exists)."
"Also check that ?config macro works."];
config(Config) when is_list(Config) ->
is_tuplelist(Config),
{value,{data_dir,Dd}}=lists:keysearch(data_dir,1,Config),
{value,{priv_dir,Dp}}=lists:keysearch(priv_dir,1,Config),
true=is_dir(Dd),
{ok, _Bin}=file:read_file(filename:join(Dd, "dummy_file")),
true=is_dir(Dp),
Dd = ?config(data_dir,Config),
Dp = ?config(priv_dir,Config),
ok;
config(_Config) ->
?t:fail("Config variable is not a list.").
is_tuplelist([]) ->
true;
is_tuplelist([{_A,_B}|Rest]) ->
is_tuplelist(Rest);
is_tuplelist(_) ->
false.
is_dir(Dir) ->
case file:read_file_info(Dir) of
{ok, #file_info{type=directory}} ->
true;
_ ->
false
end.
comment(suite) -> [];
comment(doc) -> ["Print a comment in the HTML log"];
comment(Config) when is_list(Config) ->
?t:comment("This comment should not occur in the HTML log because a later"
" comment shall overwrite it"),
?t:comment("This comment is printed with the comment/1 function."
" It should occur in the HTML log").
timetrap(suite) -> [];
timetrap(doc) -> ["Test that timetrap works."];
timetrap(Config) when is_list(Config) ->
TrapAfter = 3000,
Dog=?t:timetrap(TrapAfter),
process_flag(trap_exit, true),
TimeOut = TrapAfter * test_server:timetrap_scale_factor() + 1000,
receive
{'EXIT', Dog, {timetrap_timeout, _, _}} ->
ok;
{'EXIT', _OtherPid, {timetrap_timeout, _, _}} ->
?t:fail("EXIT signal from wrong process")
after
TimeOut ->
?t:fail("Timetrap is not working.")
end,
?t:timetrap_cancel(Dog),
ok.
timetrap_cancel(suite) -> [];
timetrap_cancel(doc) -> ["Test that timetrap_cancel works."];
timetrap_cancel(Config) when is_list(Config) ->
Dog=?t:timetrap(1000),
receive
after
500 ->
ok
end,
?t:timetrap_cancel(Dog),
receive
after 1000 ->
ok
end,
ok.
multiply_timetrap(suite) -> [];
multiply_timetrap(doc) -> ["Test multiply timetrap"];
multiply_timetrap(Config) when is_list(Config) ->
%% This simulates the call to test_server_ctrl:multiply_timetraps/1:
put(test_server_multiply_timetraps,{2,true}),
Dog = ?t:timetrap(500),
timer:sleep(800),
?t:timetrap_cancel(Dog),
%% Reset
put(test_server_multiply_timetraps,1),
ok.
init_per_s(suite) -> [];
init_per_s(doc) -> ["Test that a Config that is altered in ",
"init_per_suite gets through to the testcases."];
init_per_s(Config) ->
%% Check that the config var sent from init_per_suite
%% really exists.
{value, {init_per_suite_var, ok}} =
lists:keysearch(init_per_suite_var,1,Config),
%% Check that the other variables still exist.
{value,{data_dir,_Dd}}=lists:keysearch(data_dir,1,Config),
{value,{priv_dir,_Dp}}=lists:keysearch(priv_dir,1,Config),
ok.
init_per_tc(suite) -> [];
init_per_tc(doc) -> ["Test that a Config that is altered in ",
"init_per_testcase gets through to the ",
"actual testcase."];
init_per_tc(Config) ->
%% Check that the config var sent from init_per_testcase
%% really exists.
{value, {strange_var, 1}} = lists:keysearch(strange_var,1,Config),
%% Check that the other variables still exist.
{value,{data_dir,_Dd}}=lists:keysearch(data_dir,1,Config),
{value,{priv_dir,_Dp}}=lists:keysearch(priv_dir,1,Config),
ok.
end_per_tc(suite) -> [];
end_per_tc(doc) -> ["Test that end_per_testcase/2 is called even if"
" test case fails"];
end_per_tc(Config) when is_list(Config) ->
?t:fail("This case should fail! Check that \"CLEANUP\" is"
" printed in the minor log file.").
timeconv(suite) -> [];
timeconv(doc) -> ["Test that the time unit conversion functions ",
"works."];
timeconv(Config) when is_list(Config) ->
Val=2,
Secs=Val*1000,
Mins=Secs*60,
Hrs=Mins*60,
Secs=?t:seconds(2),
Mins=?t:minutes(2),
Hrs=?t:hours(2),
ok.
msgs(suite) -> [];
msgs(doc) -> ["Tests the messages_get function."];
msgs(Config) when is_list(Config) ->
self() ! {hej, du},
self() ! {lite, "data"},
self() ! en_atom,
[{hej, du}, {lite, "data"}, en_atom] = ?t:messages_get(),
ok.
capture(suite) -> [];
capture(doc) -> ["Test that the capture functions work properly."];
capture(Config) when is_list(Config) ->
String1="abcedfghjiklmnopqrstuvwxyz",
String2="0123456789",
?t:capture_start(),
io:format(String1),
[String1]=?t:capture_get(),
io:format(String2),
[String2]=?t:capture_get(),
?t:capture_stop(),
[]=?t:capture_get(),
io:format(String2),
[]=?t:capture_get(),
ok.
timecall(suite) -> [];
timecall(doc) -> ["Tests that timed calls work."];
timecall(Config) when is_list(Config) ->
{_Time1, liten_apa_e_oxo_farlig} = ?t:timecall(?MODULE, dummy_function, []),
{Time2, jag_ar_en_gorilla} = ?t:timecall(?MODULE, dummy_function, [gorilla]),
DTime=round(Time2),
if
DTime<1 ->
?t:fail("Timecall reported a too low time.");
DTime==1 ->
ok;
DTime>1 ->
?t:fail("Timecall reported a too high time.")
end,
ok.
dummy_function() ->
liten_apa_e_oxo_farlig.
dummy_function(gorilla) ->
receive after 1000 -> ok end,
jag_ar_en_gorilla.
do_times(suite) -> [do_times_mfa, do_times_fun];
do_times(doc) -> ["Test the do_times function."].
do_times_mfa(suite) -> [];
do_times_mfa(doc) -> ["Test the do_times function with M,F,A given."];
do_times_mfa(Config) when is_list(Config) ->
?t:do_times(100, ?MODULE, doer, [self()]),
100=length(?t:messages_get()),
ok.
do_times_fun(suite) -> [];
do_times_fun(doc) -> ["Test the do_times function with fun given."];
do_times_fun(Config) when is_list(Config) ->
Self = self(),
?t:do_times(100, fun() -> doer(Self) end),
100=length(?t:messages_get()),
ok.
doer(From) ->
From ! a,
ok.
skip_cases(doc) -> ["Test all possible ways to skip a test case."];
skip_cases(suite) -> [skip_case1, skip_case2, skip_case3, skip_case4,
skip_case5, skip_case6, skip_case7, skip_case8,
skip_case9].
skip_case1(suite) -> [];
skip_case1(doc) -> ["Test that you can return {skipped, Reason},"
" and that Reason is in the comment field in the HTML log"];
skip_case1(Config) when is_list(Config) ->
%% If this comment shows, the case failed!!
?t:comment("ERROR: This case should have been noted as `Skipped'"),
%% The Reason in {skipped, Reason} should overwrite a 'comment'
{skipped, "This case should be noted as `Skipped'"}.
skip_case2(suite) -> [];
skip_case2(doc) -> ["Test that you can return {skipped, Reason},"
" and that Reason is in the comment field in the HTML log"];
skip_case2(Config) when is_list(Config) ->
%% If this comment shows, the case failed!!
?t:comment("ERROR: This case should have been noted as `Skipped'"),
%% The Reason in {skipped, Reason} should overwrite a 'comment'
exit({skipped, "This case should be noted as `Skipped'"}).
skip_case3(suite) -> [];
skip_case3(doc) -> ["Test that you can return {skip, Reason},"
" and that Reason is in the comment field in the HTML log"];
skip_case3(Config) when is_list(Config) ->
%% If this comment shows, the case failed!!
?t:comment("ERROR: This case should have been noted as `Skipped'"),
%% The Reason in {skip, Reason} should overwrite a 'comment'
{skip, "This case should be noted as `Skipped'"}.
skip_case4(suite) -> [];
skip_case4(doc) -> ["Test that you can return {skip, Reason},"
" and that Reason is in the comment field in the HTML log"];
skip_case4(Config) when is_list(Config) ->
%% If this comment shows, the case failed!!
?t:comment("ERROR: This case should have been noted as `Skipped'"),
%% The Reason in {skip, Reason} should overwrite a 'comment'
exit({skip, "This case should be noted as `Skipped'"}).
skip_case5(suite) -> {skipped, "This case should be noted as `Skipped'"};
skip_case5(doc) -> ["Test that you can return {skipped, Reason}"
" from the specification clause"].
skip_case6(suite) -> {skip, "This case should be noted as `Skipped'"};
skip_case6(doc) -> ["Test that you can return {skip, Reason}"
" from the specification clause"].
skip_case7(suite) -> [];
skip_case7(doc) -> ["Test that skip works from a test specification file"];
skip_case7(Config) when is_list(Config) ->
%% This case shall be skipped by adding
%% {skip, {test_server_SUITE, skip_case7, Reason}}.
%% to the test specification file.
?t:fail("This case should have been Skipped by the .spec file").
skip_case8(suite) -> [];
skip_case8(doc) -> ["Test that {skipped, Reason} works from"
" init_per_testcase/2"];
skip_case8(Config) when is_list(Config) ->
%% This case shall be skipped by adding a specific clause to
%% returning {skipped, Reason} from init_per_testcase/2 for this case.
?t:fail("This case should have been Skipped by init_per_testcase/2").
skip_case9(suite) -> [];
skip_case9(doc) -> ["Test that {skip, Reason} works from a init_per_testcase/2"];
skip_case9(Config) when is_list(Config) ->
%% This case shall be skipped by adding a specific clause to
%% returning {skip, Reason} from init_per_testcase/2 for this case.
?t:fail("This case should have been Skipped by init_per_testcase/2").
undefined_functions(suite) -> [];
undefined_functions(doc) -> ["Check for calls to undefined functions in"
" test_server."
"Skip if cover is running"];
undefined_functions(Config) when is_list(Config) ->
case whereis(cover_server) of
Pid when is_pid(Pid) ->
{skip,"Cover is running"};
undefined ->
undefined_functions()
end.
undefined_functions() ->
TestServerDir = filename:dirname(code:which(test_server)),
Res = xref:d(TestServerDir),
{value,{unused,Unused}} = lists:keysearch(unused, 1, Res),
case Unused of
[] -> ok;
_ ->
lists:foreach(fun (MFA) ->
io:format("~s unused", [format_mfa(MFA)])
end, Unused)
end,
{value,{undefined,Undef0}} = lists:keysearch(undefined, 1, Res),
Undef = [U || U <- Undef0, not unresolved(U)],
case Undef of
[] -> ok;
_ ->
lists:foreach(fun ({MFA1,MFA2}) ->
io:format("~s calls undefined ~s",
[format_mfa(MFA1),format_mfa(MFA2)])
end, Undef),
?t:fail({length(Undef),undefined_functions_in_otp})
end,
ok.
unresolved({_,{_,'$F_EXPR',_}}) -> true;
unresolved(_) -> false.
format_mfa({M,F,A}) ->
lists:flatten(io_lib:format("~s:~s/~p", [M,F,A])).
conf_init(doc) -> ["Test successful conf case: Change Config parameter"];
conf_init(Config) when is_list(Config) ->
[{conf_init_var,1389}|Config].
check_new_conf(suite) -> [];
check_new_conf(doc) -> ["Check that Config parameter changed by"
" conf_init is used"];
check_new_conf(Config) when is_list(Config) ->
1389 = ?config(conf_init_var,Config),
ok.
conf_cleanup(doc) -> ["Test successful conf case: Restore Config parameter"];
conf_cleanup(Config) when is_list(Config) ->
lists:keydelete(conf_init_var,1,Config).
check_old_conf(suite) -> [];
check_old_conf(doc) -> ["Test that the restored Config is used after a"
" conf cleanup"];
check_old_conf(Config) when is_list(Config) ->
undefined = ?config(conf_init_var,Config),
ok.
conf_init_fail(doc) -> ["Test that config members are skipped if"
" conf init function fails."];
conf_init_fail(Config) when is_list(Config) ->
?t:fail("This case should fail! Check that conf_member_skip and"
" conf_cleanup_skip are skipped.").
start_stop_node(suite) -> [];
start_stop_node(doc) -> ["Test start and stop of slave and peer nodes"];
start_stop_node(Config) when is_list(Config) ->
{ok,Node2} = ?t:start_node(node2,peer,[]),
{error, _} = ?t:start_node(node2,peer,[{fail_on_error,false}]),
true = lists:member(Node2,nodes()),
{ok,Node3} = ?t:start_node(node3,slave,[]),
{error, _} = ?t:start_node(node3,slave,[]),
true = lists:member(Node3,nodes()),
{ok,Node4} = ?t:start_node(node4,peer,[{wait,false}]),
case lists:member(Node4,nodes()) of
true ->
?t:comment("WARNING: Node started with {wait,false}"
" is up faster than expected...");
false ->
wait_for_node(Node4,0),
true = lists:member(Node4,nodes())
end,
true = ?t:stop_node(Node2),
false = lists:member(Node2,nodes()),
true = ?t:stop_node(Node3),
false = lists:member(Node3,nodes()),
true = ?t:stop_node(Node4),
false = lists:member(Node4,nodes()),
timer:sleep(2000),
false = ?t:stop_node(Node4),
ok.
wait_for_node(Node,Acc) ->
case net_adm:ping(Node) of
pang ->
timer:sleep(100),
wait_for_node(Node,Acc+100);
pong ->
Acc
end.
cleanup_nodes_init(doc) -> ["Test that nodes are terminated when test case"
" is finished unless {cleanup,false} is given."];
cleanup_nodes_init(Config) when is_list(Config) ->
{ok,DieSlave} = ?t:start_node(die_slave, slave, []),
{ok,SurviveSlave} = ?t:start_node(survive_slave, slave, [{cleanup,false}]),
{ok,DiePeer} = ?t:start_node(die_peer, peer, []),
{ok,SurvivePeer} = ?t:start_node(survive_peer, peer, [{cleanup,false}]),
[{die_slave,DieSlave},
{survive_slave,SurviveSlave},
{die_peer,DiePeer},
{survive_peer,SurvivePeer} | Config].
check_survive_nodes(suite) -> [];
check_survive_nodes(doc) -> ["Test that nodes with {cleanup,false} survived"];
check_survive_nodes(Config) when is_list(Config) ->
timer:sleep(1000),
false = lists:member(?config(die_slave,Config),nodes()),
true = lists:member(?config(survive_slave,Config),nodes()),
false = lists:member(?config(die_peer,Config),nodes()),
true = lists:member(?config(survive_peer,Config),nodes()),
ok.
cleanup_nodes_fin(doc) -> ["Test that nodes started with {cleanup,false}"
" can be stopped"];
cleanup_nodes_fin(Config) when is_list(Config) ->
Slave = ?config(survive_slave,Config),
Peer = ?config(survive_peer,Config),
true = ?t:stop_node(Slave),
false = lists:member(Slave,nodes()),
true = ?t:stop_node(Peer),
false = lists:member(Peer,nodes()),
C1 = lists:keydelete(die_slave,1,Config),
C2 = lists:keydelete(survive_slave,1,C1),
C3 = lists:keydelete(die_peer,1,C2),
lists:keydelete(survive_peer,1,C3).
commercial(Config) when is_list(Config) ->
case ?t:is_commercial() of
false -> {comment,"Open-source build"};
true -> {comment,"Commercial build"}
end.