diff options
Diffstat (limited to 'lib/reltool/test')
-rw-r--r-- | lib/reltool/test/Makefile | 82 | ||||
-rw-r--r-- | lib/reltool/test/README | 30 | ||||
-rw-r--r-- | lib/reltool/test/reltool.spec | 2 | ||||
-rw-r--r-- | lib/reltool/test/reltool_server_SUITE.erl | 494 | ||||
-rw-r--r-- | lib/reltool/test/reltool_test_lib.erl | 329 | ||||
-rw-r--r-- | lib/reltool/test/reltool_test_lib.hrl | 91 | ||||
-rw-r--r-- | lib/reltool/test/reltool_wx_SUITE.erl | 62 | ||||
-rwxr-xr-x | lib/reltool/test/rtt | 55 | ||||
-rw-r--r-- | lib/reltool/test/rtt.erl | 154 |
9 files changed, 1299 insertions, 0 deletions
diff --git a/lib/reltool/test/Makefile b/lib/reltool/test/Makefile new file mode 100644 index 0000000000..00d2add3e5 --- /dev/null +++ b/lib/reltool/test/Makefile @@ -0,0 +1,82 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2009. 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% + +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- + +MODULES= \ + rtt \ + reltool_wx_SUITE \ + reltool_server_SUITE \ + reltool_test_lib + + +ERL_FILES= $(MODULES:%=%.erl) + +HRL_FILES= reltool_test_lib.hrl + +TARGET_FILES= \ + $(MODULES:%=$(EBIN)/%.$(EMULATOR)) + +INSTALL_PROGS= $(TARGET_FILES) + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/reltool_test + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +#ERL_COMPILE_FLAGS += + +EBIN = . + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +tests debug opt: $(TARGET_FILES) + +clean: + rm -f $(TARGET_FILES) + rm -f core + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + +release_tests_spec: opt + $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DATA) reltool.spec $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) + $(INSTALL_PROGRAM) rtt $(INSTALL_PROGS) $(RELSYSDIR) +# chmod -f -R u+w $(RELSYSDIR) +# @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + +release_docs_spec: + + diff --git a/lib/reltool/test/README b/lib/reltool/test/README new file mode 100644 index 0000000000..031bd2c961 --- /dev/null +++ b/lib/reltool/test/README @@ -0,0 +1,30 @@ + +Testing and running reltool tests. + +Testing gui api/applications can be hard, but we can at least +test that wxerlang behaves as we expected, i.e. that the api +is consistent and that it don't crash. + +The tests are structured as they are because we want you to +be able to run them in three different ways. + - direct via an erlang shell + - via common_test application + - via erlang/OTP inhouse ts tool. + +To run all the tests compile them and on unix +run ./rtt to create an erlang terminal. + +Invoke rtt:t(). in the erlang shell to run all regression tests. +If you want to specific tests invoke rtt:t(Module) +or rtt:t(Module, TestCase). + +To run all tests including the ones that require manual intervention run. +rtt:t(all, [{user,true}]). + +To see every test_case window use +rtt:t(all, [{user,step}]). +This requires that you manually close each window to step to the +next test_case. + +If you want to run specific test_cases use: +rtt:t({Module,TestCase}, [{user,step}]). diff --git a/lib/reltool/test/reltool.spec b/lib/reltool/test/reltool.spec new file mode 100644 index 0000000000..252232e09d --- /dev/null +++ b/lib/reltool/test/reltool.spec @@ -0,0 +1,2 @@ +{topcase, {dir, "../reltool_test"}}. + diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl new file mode 100644 index 0000000000..cf951191a0 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -0,0 +1,494 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009. 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% + +-module(reltool_server_SUITE). + +-export([all/0, init_per_suite/1, end_per_suite/1, + init_per_testcase/2, fin_per_testcase/2, end_per_testcase/2]). + +-compile(export_all). + +-include("reltool_test_lib.hrl"). + +-define(NODE_NAME, '__RELTOOL__TEMPORARY_TEST__NODE__'). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Initialization functions. + +init_per_suite(Config) -> + reltool_test_lib:init_per_suite(Config). + +end_per_suite(Config) -> + reltool_test_lib:end_per_suite(Config). + +init_per_testcase(Func,Config) -> + reltool_test_lib:init_per_testcase(Func,Config). +end_per_testcase(Func,Config) -> + reltool_test_lib:end_per_testcase(Func,Config). +fin_per_testcase(Func,Config) -> %% For test_server + reltool_test_lib:end_per_testcase(Func,Config). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% SUITE specification + +all() -> + all(suite). +all(suite) -> + [ + start_server, + set_config, + create_release, + create_script, + create_target, + create_embedded, + create_standalone, + create_old_target + ]. + +%% The test cases + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Start a server process and check that it does not crash + +start_server(TestInfo) when is_atom(TestInfo) -> + reltool_test_lib:tc_info(TestInfo); +start_server(_Config) -> + {ok, Pid} = ?msym({ok, _}, reltool:start_server([])), + Libs = lists:sort(erl_libs()), + StrippedDefault = + case Libs of + [] -> {sys, []}; + _ -> {sys, [{lib_dirs, Libs}]} + end, + ?m({ok, StrippedDefault}, reltool:get_config(Pid)), + ?m(ok, reltool:stop(Pid)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Start a server process and check that it does not crash + +set_config(TestInfo) when is_atom(TestInfo) -> + reltool_test_lib:tc_info(TestInfo); +set_config(_Config) -> + Libs = lists:sort(erl_libs()), + Default = + {sys, + [ + {mod_cond, all}, + {incl_cond, derived}, + {root_dir, code:root_dir()}, + {lib_dirs, Libs} + ]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Default}])), + StrippedDefault = + case Libs of + [] -> {sys, []}; + _ -> {sys, [{lib_dirs, Libs}]} + end, + ?m({ok, StrippedDefault}, reltool:get_config(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate releases + +create_release(TestInfo) when is_atom(TestInfo) -> + reltool_test_lib:tc_info(TestInfo); +create_release(_Config) -> + %% Configure the server + RelName = "Just testing...", + RelVsn = "1.0", + Config = + {sys, + [ + {lib_dirs, []}, + {boot_rel, RelName}, + {rel, RelName, RelVsn, [kernel, stdlib]} + ]}, + %% Generate release + ErtsVsn = erlang:system_info(version), + Apps = application:loaded_applications(), + {value, {_, _, KernelVsn}} = lists:keysearch(kernel, 1, Apps), + {value, {_, _, StdlibVsn}} = lists:keysearch(stdlib, 1, Apps), + Rel = {release, {RelName, RelVsn}, + {erts, ErtsVsn}, + [{kernel, KernelVsn}, + {stdlib, StdlibVsn}]}, + ?m({ok, Rel}, reltool:get_rel([{config, Config}], RelName)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate boot scripts + +create_script(TestInfo) when is_atom(TestInfo) -> + reltool_test_lib:tc_info(TestInfo); +create_script(_Config) -> + %% Configure the server + RelName = "Just testing", + RelVsn = "1.0", + Config = + {sys, + [ + {lib_dirs, []}, + {boot_rel, RelName}, + {rel, RelName, RelVsn, [stdlib, kernel]} + ]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Config}])), + + %% Generate release file + ErtsVsn = erlang:system_info(version), + Apps = application:loaded_applications(), + {value, {_, _, KernelVsn}} = lists:keysearch(kernel, 1, Apps), + {value, {_, _, StdlibVsn}} = lists:keysearch(stdlib, 1, Apps), + Rel = {release, + {RelName, RelVsn}, + {erts, ErtsVsn}, + [{stdlib, StdlibVsn}, {kernel, KernelVsn}]}, + ?m({ok, Rel}, reltool:get_rel(Pid, RelName)), + RelFile = RelName ++ ".rel", + ?m(ok, file:write_file(RelFile, io_lib:format("~p.\n", [Rel]))), + + %% Generate script file + ?m(ok, systools:make_script(RelName, [])), + ScriptFile = RelName ++ ".script", + {ok, [OrigScript]} = ?msym({ok, [_]}, file:consult(ScriptFile)), + {ok, Script} = ?msym({ok, _}, reltool:get_script(Pid, RelName)), + %% OrigScript2 = sort_script(OrigScript), + %% Script2 = sort_script(Script), + %% ?m(OrigScript2, Script2), + + ?m(equal, diff_script(OrigScript, Script)), + + %% Stop server + ?m(ok, reltool:stop(Pid)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate target system + +create_target(TestInfo) when is_atom(TestInfo) -> + reltool_test_lib:tc_info(TestInfo); +create_target(_Config) -> + %% Configure the server + RelName1 = "Just testing", + RelName2 = "Just testing with SASL", + RelVsn = "1.0", + Config = + {sys, + [ + {root_dir, code:root_dir()}, + {lib_dirs, []}, + {boot_rel, RelName2}, + {rel, RelName1, RelVsn, [stdlib, kernel]}, + {rel, RelName2, RelVsn, [sasl, stdlib, kernel]}, + {app, sasl, [{incl_cond, include}]} + ]}, + + %% Generate target file + TargetDir = "reltool_target_dir_development", + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + + Erl = filename:join([TargetDir, "bin", "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + ?msym(ok, stop_node(Node)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate embedded target system + +create_embedded(TestInfo) when is_atom(TestInfo) -> + reltool_test_lib:tc_info(TestInfo); +create_embedded(_Config) -> + %% Configure the server + RelName1 = "Just testing", + RelName2 = "Just testing with SASL", + RelVsn = "1.0", + Config = + {sys, + [ + {lib_dirs, []}, + {profile, embedded}, + {boot_rel, RelName2}, + {rel, RelName1, RelVsn, [stdlib, kernel]}, + {rel, RelName2, RelVsn, [sasl, stdlib, kernel]}, + {app, sasl, [{incl_cond, include}]} + ]}, + + %% Generate target file + TargetDir = "reltool_target_dir_embedded", + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + + Erl = filename:join([TargetDir, "bin", "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + ?msym(ok, stop_node(Node)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate standalone system + +create_standalone(TestInfo) when is_atom(TestInfo) -> + reltool_test_lib:tc_info(TestInfo); +create_standalone(_Config) -> + %% Configure the server + ExDir = code:lib_dir(reltool, examples), + EscriptName = "display_args", + Escript = filename:join([ExDir, EscriptName]), + Config = + {sys, + [ + {lib_dirs, []}, + {escript, Escript, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + %% Generate target file + TargetDir = "reltool_target_dir_standalone", + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + + BinDir = filename:join([TargetDir, "bin"]), + Erl = filename:join([BinDir, "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + RootDir = ?ignore(rpc:call(Node, code, root_dir, [])), + ?msym(ok, stop_node(Node)), + + Expected = iolist_to_binary(["Root dir: ", RootDir, "\n" + "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n", + "Smp: false\n", + "ExitCode:0"]), + io:format("Expected: ~s\n", [Expected]), + ?m(Expected, run(BinDir, EscriptName ++ " -arg1 arg2 arg3")), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate old type of target system + +create_old_target(TestInfo) when is_atom(TestInfo) -> + reltool_test_lib:tc_info(TestInfo); +create_old_target(_Config) -> + %% Configure the server + RelName1 = "Just testing", + RelName2 = "Just testing with SASL", + RelVsn = "1.0", + Config = + {sys, + [ + {lib_dirs, []}, + {boot_rel, RelName2}, + {rel, RelName1, RelVsn, [stdlib, kernel]}, + {rel, RelName2, RelVsn, [sasl, stdlib, kernel]}, + {relocatable, false}, % Implies explicit old style installation + {app, sasl, [{incl_cond, include}]} + ]}, + + %% Generate target file + TargetDir = "reltool_target_dir_old", + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + + %% io:format("Will fail on Windows (should patch erl.ini)\n", []), + ?m(ok, reltool:install(RelName2, TargetDir)), + + Erl = filename:join([TargetDir, "bin", "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + ?msym(ok, stop_node(Node)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Library functions + +erl_libs() -> + case os:getenv("ERL_LIBS") of + false -> []; + LibStr -> string:tokens(LibStr, ":;") + end. + +diff_script(Script, Script) -> + equal; +diff_script({script, Rel, Commands1}, {script, Rel, Commands2}) -> + diff_cmds(Commands1, Commands2); +diff_script({script, Rel1, _}, {script, Rel2, _}) -> + {error, {Rel1, Rel2}}. + +diff_cmds([Cmd | Commands1], [Cmd | Commands2]) -> + diff_cmds(Commands1, Commands2); +diff_cmds([Cmd1 | _Commands1], [Cmd2 | _Commands2]) -> + {diff, {expected, Cmd1}, {actual, Cmd2}}; +diff_cmds([], []) -> + equal. + +os_cmd(Cmd) when is_list(Cmd) -> + %% Call the plain os:cmd with an echo command appended to print command status + %% io:format("os:cmd(~p).\n", [Cmd]), + case os:cmd(Cmd++";echo \"#$?\"") of + %% There is (as far as I can tell) only one thing that will match this + %% and that is too silly to ever be used, but... + []-> + {99, []}; + Return-> + %% Find the position of the status code wich is last in the string + %% prepended with # + case string:rchr(Return, $#) of + + %% This happens only if the sh command pipe is somehow interrupted + 0-> + {98, Return}; + + Position-> + Result = string:left(Return,Position - 1), + Status = string:substr(Return,Position + 1, length(Return) - Position - 1), + {list_to_integer(Status), Result} + end + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Node handling + +start_node(Name, ErlPath) -> + FullName = full_node_name(Name), + CmdLine = mk_node_cmdline(Name, ErlPath), + io:format("Starting node ~p: ~s~n", [FullName, CmdLine]), + case open_port({spawn, CmdLine}, []) of + Port when is_port(Port) -> + unlink(Port), + erlang:port_close(Port), + case ping_node(FullName, 50) of + ok -> {ok, FullName}; + Other -> exit({failed_to_start_node, FullName, Other}) + end; + Error -> + exit({failed_to_start_node, FullName, Error}) + end. + +stop_node(Node) -> + monitor_node(Node, true), + spawn(Node, fun () -> halt() end), + receive {nodedown, Node} -> ok end. + +mk_node_cmdline(Name) -> + Prog = case catch init:get_argument(progname) of + {ok,[[P]]} -> P; + _ -> exit(no_progname_argument_found) + end, + mk_node_cmdline(Name, Prog). + +mk_node_cmdline(Name, Prog) -> + Static = "-detached -noinput", + Pa = filename:dirname(code:which(?MODULE)), + NameSw = case net_kernel:longnames() of + false -> "-sname "; + true -> "-name "; + _ -> exit(not_distributed_node) + end, + {ok, Pwd} = file:get_cwd(), + NameStr = atom_to_list(Name), + Prog ++ " " + ++ Static ++ " " + ++ NameSw ++ " " ++ NameStr ++ " " + ++ "-pa " ++ Pa ++ " " + ++ "-env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr ++ " " + ++ "-setcookie " ++ atom_to_list(erlang:get_cookie()). + +full_node_name(PreName) -> + HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end, + atom_to_list(node())), + list_to_atom(atom_to_list(PreName) ++ HostSuffix). + +ping_node(_Node, 0) -> + {error, net_adm}; +ping_node(Node, N) when is_integer(N), N > 0 -> + case catch net_adm:ping(Node) of + pong -> + wait_for_process(Node, code_server, 50); + _ -> + timer:sleep(1000), + ping_node(Node, N-1) + end. + +wait_for_process(_Node, Name, 0) -> + {error, Name}; +wait_for_process(Node, Name, N) when is_integer(N), N > 0 -> + case rpc:call(Node, erlang, whereis, [Name]) of + undefined -> + timer:sleep(1000), + wait_for_process(Node, Name, N-1); + {badrpc, _} = Reason -> + erlang:error({Reason, Node}); + Pid when is_pid(Pid) -> + ok + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Run escript + +run(Dir, Cmd0) -> + Cmd = case os:type() of + {win32,_} -> filename:nativename(Dir) ++ "\\" ++ Cmd0; + _ -> Cmd0 + end, + do_run(Dir, Cmd). + +run(Dir, Opts, Cmd0) -> + Cmd = case os:type() of + {win32,_} -> Opts ++ " " ++ filename:nativename(Dir) ++ "\\" ++ Cmd0; + _ -> Opts ++ " " ++ Dir ++ "/" ++ Cmd0 + end, + do_run(Dir, Cmd). + +do_run(Dir, Cmd) -> + io:format("Run: ~p\n", [Cmd]), + Env = [{"PATH",Dir++":"++os:getenv("PATH")}], + Port = open_port({spawn,Cmd}, [exit_status,eof,in,{env,Env}]), + Res = get_data(Port, []), + receive + {Port,{exit_status,ExitCode}} -> + iolist_to_binary([Res,"ExitCode:"++integer_to_list(ExitCode)]) + end. + +get_data(Port, SoFar) -> + receive + {Port,{data,Bytes}} -> + get_data(Port, [SoFar|Bytes]); + {Port,eof} -> + erlang:port_close(Port), + SoFar + end. + +expected_output([data_dir|T], Data) -> + Slash = case os:type() of + {win32,_} -> "\\"; + _ -> "/" + end, + [filename:nativename(Data)++Slash|expected_output(T, Data)]; +expected_output([H|T], Data) -> + [H|expected_output(T, Data)]; +expected_output([], _) -> + []; +expected_output(Bin, _) when is_binary(Bin) -> + Bin. diff --git a/lib/reltool/test/reltool_test_lib.erl b/lib/reltool/test/reltool_test_lib.erl new file mode 100644 index 0000000000..25978294ee --- /dev/null +++ b/lib/reltool/test/reltool_test_lib.erl @@ -0,0 +1,329 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009. 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% + +-module(reltool_test_lib). +-compile(export_all). + +-include("reltool_test_lib.hrl"). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init_per_suite(Config) when is_list(Config)-> + incr_timetrap(Config, 5). + +end_per_suite(Config) when is_list(Config)-> + ok. + +incr_timetrap(Config, Times) -> + Key = tc_timeout, + KeyPos = 1, + NewTime = + case lists:keysearch(Key, KeyPos, Config) of + {value, {Key, OldTime}} -> + (timer:minutes(1) + OldTime) * Times; + false -> + timer:minutes(1) * Times + end, + lists:keystore(Key, KeyPos, Config, {Key, NewTime}). + +set_kill_timer(Config) -> + case init:get_argument(reltool_test_timeout) of + {ok, _} -> + Config; + _ -> + Time = + case lookup_config(tc_timeout, Config) of + [] -> + timer:minutes(5); + ConfigTime when is_integer(ConfigTime) -> + ConfigTime + end, + WatchDog = test_server:timetrap(Time), + [{kill_timer, WatchDog} | Config] + end. + +reset_kill_timer(Config) -> + DogKiller = + case get(reltool_test_server) of + true -> + fun(P) when is_pid(P) -> P ! stop; + (_) -> ok + end; + _ -> + fun(Ref) -> test_server:timetrap_cancel(Ref) end + end, + case lists:keysearch(kill_timer, 1, Config) of + {value, {kill_timer, WatchDog}} -> + DogKiller(WatchDog), + lists:keydelete(kill_timer, 1, Config); + _ -> + Config + end. + +lookup_config(Key,Config) -> + case lists:keysearch(Key, 1, Config) of + {value,{Key,Val}} -> + Val; + _ -> + [] + end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +wx_init_per_suite(Config) -> + {_Pid, Ref} = + spawn_monitor(fun() -> + %% Avoid test case crash if wx master process dies + process_flag(trap_exit, true), + try + case os:type() of + {unix,darwin} -> + exit({skipped, "Can not test on MacOSX"}); + {unix, _} -> + io:format("DISPLAY ~s~n", [os:getenv("DISPLAY")]), + case proplists:get_value(xserver, Config, none) of + none -> ignore; + Server -> os:putenv("DISPLAY", Server) + end; + _ -> + ignore + end, + wx:new(), + wx:destroy() + catch + error:undef -> + exit({skipped, "No wx compiled for this platform"}); + _:Reason -> + exit({skipped, lists:flatten(io_lib:format("Start wx failed: ~p", [Reason]))}) + end, + exit(normal) + end), + receive + {'DOWN', Ref, _, _, normal} -> + init_per_suite(Config); + {'DOWN', Ref, _, _, {skipped, _} = Skipped} -> + Skipped; + {'DOWN', Ref, _, _, Reason} -> + exit({wx_init_per_suite, Reason}) + after timer:minutes(1) -> + exit({wx_init_per_suite, timeout}) + end. + +wx_end_per_suite(Config) -> + end_per_suite(Config). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init_per_testcase(_Func, Config) when is_list(Config) -> + set_kill_timer(Config), + global:register_name(reltool_global_logger, group_leader()), + Config. + +end_per_testcase(_Func, Config) when is_list(Config) -> + global:unregister_name(reltool_global_logger), + reset_kill_timer(Config), + Config. + +%% Backwards compatible with test_server +tc_info(suite) -> []; +tc_info(doc) -> "". + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Use ?log(Format, Args) as wrapper +log(Format, Args, LongFile, Line) -> + File = filename:basename(LongFile), + Format2 = lists:concat([File, "(", Line, ")", ": ", Format]), + log(Format2, Args). + +log(Format, Args) -> + case global:whereis_name(reltool_global_logger) of + undefined -> + io:format(user, Format, Args); + Pid -> + io:format(Pid, Format, Args) + end. + +verbose(Format, Args, File, Line) -> + Arg = reltool_test_verbose, + case get(Arg) of + false -> + ok; + true -> + log(Format, Args, File, Line); + undefined -> + case init:get_argument(Arg) of + {ok, List} when is_list(List) -> + case lists:last(List) of + ["true"] -> + put(Arg, true), + log(Format, Args, File, Line); + _ -> + put(Arg, false), + ok + end; + _ -> + put(Arg, false), + ok + end + end. + +error(Format, Args, File, Line) -> + global:send(reltool_global_logger, {failed, File, Line}), + Fail = {filename:basename(File),Line,Args}, + case global:whereis_name(reltool_test_case_sup) of + undefined -> ignore; + Pid -> Pid ! Fail + %% global:send(reltool_test_case_sup, Fail), + end, + log("<ERROR>~n" ++ Format, Args, File, Line). + + +pick_msg() -> + receive + Message -> Message + after 4000 -> timeout + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Utility functions + +user_available(Config) -> + false /= proplists:get_value(user, Config, false). + + +wx_destroy(Frame, Config) -> + case proplists:get_value(user, Config, false) of + false -> + timer:sleep(100), + ?m(ok, wxFrame:destroy(Frame)), + ?m(ok, wx:destroy()); + true -> + timer:sleep(500), + ?m(ok, wxFrame:destroy(Frame)), + ?m(ok, wx:destroy()); + step -> %% Wait for user to close window + ?m(ok, wxEvtHandler:connect(Frame, close_window, [{skip,true}])), + wait_for_close() + end. + +wait_for_close() -> + receive + #wx{event=#wxClose{}} -> + ?log("Got close~n",[]), + ?m(ok, wx:destroy()); + #wx{obj=Obj, event=Event} -> + try + Name = wxTopLevelWindow:getTitle(Obj), + ?log("~p Event: ~p~n", [Name, Event]) + catch _:_ -> + ?log("Event: ~p~n", [Event]) + end, + wait_for_close(); + Other -> + ?log("Unexpected: ~p~n", [Other]), + wait_for_close() + end. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% A small test server, which can be run standalone in a shell + +run_test(Test = {_,_},Config) -> + run_test([Test],Config); +run_test([{Module, TC} | Rest], Config) -> + log("\n\n=== Eval test suite: ~w ===~n", [Module]), + case catch Module:init_per_suite(Config) of + {skipped, Reason} -> + log("Test suite skipped: ~s~n", [Reason]), + [{skipped, Reason}]; + NewConfig when is_list(NewConfig) -> + Res = + if + TC =:= all -> + [do_run_test(Module, Test, NewConfig) || Test <- Module:all()]; + is_list(TC) -> + [do_run_test(Module, Test, NewConfig) || Test <- TC]; + true -> + [do_run_test(Module, TC, NewConfig)] + end, + Module:end_per_suite(NewConfig), + Res ++ run_test(Rest, NewConfig); + Error -> + ?error("Test suite skipped: ~w~n", [Error]), + [{skipped, Error}] + end; +run_test([], _Config) -> + []. + +do_run_test(Module, all, Config) -> + All = [{Module, Test} || Test <- Module:all()], + run_test(All, Config); +do_run_test(Module, TestCase, Config) -> + log("Eval test case: ~w~n", [{Module, TestCase}]), + Sec = timer:seconds(1) * 1000, + {T, Res} = + timer:tc(?MODULE, eval_test_case, [Module, TestCase, Config]), + log("Tested ~w in ~w sec~n", [TestCase, T div Sec]), + {T div Sec, Res}. + +eval_test_case(Mod, Fun, Config) -> + flush(), + global:register_name(reltool_test_case_sup, self()), + Flag = process_flag(trap_exit, true), + Pid = spawn_link(?MODULE, test_case_evaluator, [Mod, Fun, [Config]]), + R = wait_for_evaluator(Pid, Mod, Fun, Config), + global:unregister_name(reltool_test_case_sup), + process_flag(trap_exit, Flag), + R. + +test_case_evaluator(Mod, Fun, [Config]) -> + NewConfig = Mod:init_per_testcase(Fun, Config), + R = apply(Mod, Fun, [NewConfig]), + Mod:fin_per_testcase(Fun, NewConfig), + exit({test_case_ok, R}). + +wait_for_evaluator(Pid, Mod, Fun, Config) -> + receive + {'EXIT', Pid, {test_case_ok, _PidRes}} -> + Errors = flush(), + Res = + case Errors of + [] -> ok; + Errors -> failed + end, + {Res, {Mod, Fun}, Errors}; + {'EXIT', Pid, {skipped, Reason}} -> + log("<WARNING> Test case ~w skipped, because ~p~n", + [{Mod, Fun}, Reason]), + Mod:fin_per_testcase(Fun, Config), + {skip, {Mod, Fun}, Reason}; + {'EXIT', Pid, Reason} -> + log("<ERROR> Eval process ~w exited, because\n\t~p~n", + [{Mod, Fun}, Reason]), + Mod:fin_per_testcase(Fun, Config), + {crash, {Mod, Fun}, Reason} + end. + +flush() -> + receive Msg -> [Msg | flush()] + after 0 -> [] + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/reltool/test/reltool_test_lib.hrl b/lib/reltool/test/reltool_test_lib.hrl new file mode 100644 index 0000000000..93134144ea --- /dev/null +++ b/lib/reltool/test/reltool_test_lib.hrl @@ -0,0 +1,91 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009. 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% + +-include_lib("wx/include/wx.hrl"). + +-define(log(Format,Args), reltool_test_lib:log(Format,Args,?FILE,?LINE)). +-define(warning(Format,Args), ?log("<WARNING>\n " ++ Format,Args)). +-define(error(Format,Args), reltool_test_lib:error(Format,Args,?FILE,?LINE)). +-define(verbose(Format,Args), reltool_test_lib:verbose(Format,Args,?FILE,?LINE)). + +-define(fatal(Format,Args), + ?error(Format, Args), + exit({test_case_fatal, Format, Args, ?FILE, ?LINE})). + +-define(skip(Format,Args), + ?warning(Format, Args), + exit({skipped, ?flat_format(Format, Args)})). + +-define(ignore(Expr), + fun() -> + AcTuAlReS = (catch (Expr)), + ?verbose("ok: ~p\n",[AcTuAlReS]), + AcTuAlReS + end()). + +-define(msym(ExpectedRes, Expr), + fun() -> + AcTuAlReS = (catch (Expr)), + case AcTuAlReS of + ExpectedRes -> + ?verbose("ok: ~p\n",[AcTuAlReS]), + AcTuAlReS; + _ -> + reltool_test_lib:error("Not matching actual result was:\n ~p \nExpected ~s\n", + [AcTuAlReS, ??ExpectedRes], + ?FILE, ?LINE), + AcTuAlReS + end + end()). + +-define(m(ExpectedRes, Expr), + fun() -> + AcTuAlReS = (catch (Expr)), + case AcTuAlReS of + ExpectedRes -> + ?verbose("ok: ~p\n",[AcTuAlReS]), + AcTuAlReS; + _ -> + reltool_test_lib:error("Not matching actual result was:\n\t~p \nExpected:\n\t~p\n", + [AcTuAlReS, ExpectedRes], + ?FILE, ?LINE), + AcTuAlReS + end + end()). + +-define(m_receive(ExpectedMsg), + ?m(ExpectedMsg,reltool_test_lib:pick_msg())). + +-define(m_multi_receive(ExpectedMsgs), + fun() -> + TmPeXpCtEdMsGs = lists:sort(ExpectedMsgs), + AcTuAlReS = + lists:sort(lists:map(fun(_) -> + reltool_test_lib:pick_msg() + end, TmPeXpCtEdMsGs)), + case AcTuAlReS of + TmPeXpCtEdMsGs -> + ?verbose("ok: ~p\n",[AcTuAlReS]), + AcTuAlReS; + _ -> + reltool_test_lib:error("Not matching actual result was:\n ~p \nExpected ~p\n", + [AcTuAlReS, ExpectedMsgs], + ?FILE, ?LINE), + AcTuAlReS + end + end()). diff --git a/lib/reltool/test/reltool_wx_SUITE.erl b/lib/reltool/test/reltool_wx_SUITE.erl new file mode 100644 index 0000000000..2e2b355e07 --- /dev/null +++ b/lib/reltool/test/reltool_wx_SUITE.erl @@ -0,0 +1,62 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009. 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% + +-module(reltool_wx_SUITE). + +-export([all/0, init_per_suite/1, end_per_suite/1, + init_per_testcase/2, fin_per_testcase/2, end_per_testcase/2]). + +-compile(export_all). + +-include("reltool_test_lib.hrl"). + +%% Initialization functions. +init_per_suite(Config) -> + reltool_test_lib:wx_init_per_suite(Config). + +end_per_suite(Config) -> + reltool_test_lib:wx_end_per_suite(Config). + +init_per_testcase(Func,Config) -> + reltool_test_lib:init_per_testcase(Func,Config). +end_per_testcase(Func,Config) -> + reltool_test_lib:end_per_testcase(Func,Config). +fin_per_testcase(Func,Config) -> %% For test_server + reltool_test_lib:end_per_testcase(Func,Config). + +%% SUITE specification +all() -> + all(suite). +all(suite) -> + [ + start_all_windows + ]. + +%% The test cases + +%% Display all windows and see if something crashes +start_all_windows(TestInfo) when is_atom(TestInfo) -> + reltool_test_lib:tc_info(TestInfo); +start_all_windows(_Config) -> + {ok, SysPid} = ?msym({ok, _}, reltool:start([{trap_exit, false}])), + {ok, AppPid} = ?msym({ok, _}, reltool_sys_win:open_app(SysPid, stdlib)), + ?msym({ok, _}, reltool_app_win:open_mod(AppPid, escript)), + timer:sleep(timer:seconds(10)), + ?m(ok, reltool:stop(SysPid)), + + ok. diff --git a/lib/reltool/test/rtt b/lib/reltool/test/rtt new file mode 100755 index 0000000000..2411195338 --- /dev/null +++ b/lib/reltool/test/rtt @@ -0,0 +1,55 @@ +#! /bin/sh -f +# %CopyrightBegin% +# +# Copyright Ericsson AB 2009. 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% + +# Usage: rtt [-cerl] <args to erlang startup script> + +emu=erl +while [ $# -gt 0 ]; do + case "$1" in + "-cerl") + shift + emu=cerl + ;; + *) + break + ;; + esac +done + +log=test_log_$$ +latest=test_log_latest +args=${1+"$@"} + +erlcmd="$emu -sname test_server -smp -pa ../../reltool/ebin $p $args -reltool_test_verbose true -reltool_test_timeout" + +echo "Give the following command in order to see the outcome:" +echo "" +echo " less $log" + +rm "$latest" 2>/dev/null +ln -s "$log" "$latest" +touch "$log" + +ostype=`uname -s` +if [ "$ostype" = "SunOS" ] ; then + /usr/openwin/bin/xterm -T "Testing reltool" -l -lf "$log" -e $erlcmd & +else + xterm -T "Testing reltool" -e script -f -c "$erlcmd" "$log" & +fi + +tail -f "$log" | egrep 'Eval|<ERROR>|NYI' diff --git a/lib/reltool/test/rtt.erl b/lib/reltool/test/rtt.erl new file mode 100644 index 0000000000..6755b8400f --- /dev/null +++ b/lib/reltool/test/rtt.erl @@ -0,0 +1,154 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009. 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% + +-module(rtt). +-compile(export_all). + +%% Modules or suites can be shortcuts, for example server expands to reltool_server_SUITE. +%% +%% t(Tests) run reltool testcases. +%% Tests can be module, {module, test_case} or [module|{module,test_case}] + +t() -> + t(read_test_case()). +t(Test) -> + t(Test, []). + +t(Mod, TC) when is_atom(Mod), is_atom(TC) -> + t({Mod,TC}, []); +t(all, Config) when is_list(Config) -> + Fs = filelib:wildcard("reltool_*_SUITE.erl"), + t([list_to_atom(filename:rootname(File)) || File <- Fs], Config); +t(Test,Config) when is_list(Config) -> + Tests = resolve(Test), + write_test_case(Test), + Res = reltool_test_lib:run_test(Tests, Config), + append_test_case_info(Test, Res). + +user() -> + user(read_test_case()). +user(Mod) -> + t(Mod, [{user,step}]). +user(Mod,Tc) when is_atom(Tc) -> + t({Mod,Tc}, [{user,step}]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Resolves the name of test suites and test cases +%% according to the alias definitions. Single atoms +%% are assumed to be the name of a test suite. + +resolve(Suite0) when is_atom(Suite0) -> + case alias(Suite0) of + Suite when is_atom(Suite) -> + {Suite, all}; + {Suite, Case} -> + {Suite, Case} + end; +resolve({Suite0, Case}) when is_atom(Suite0), is_atom(Case) -> + case alias(Suite0) of + Suite when is_atom(Suite) -> + {Suite, Case}; + {Suite, Case2} -> + {Suite, Case2} + end; +resolve(List) when is_list(List) -> + [resolve(Case) || Case <- List]. + +alias(Suite) when is_atom(Suite) -> + Str = atom_to_list(Suite), + case {Str, lists:reverse(Str)} of + {"reltool" ++ _, "ETIUS" ++ _} -> + Suite; + _ -> + list_to_atom("reltool_" ++ Str ++ "_SUITE") + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +config_fname() -> + "reltool_test_case_config". + +%% Read default config file +read_config() -> + Fname = config_fname(), + reltool_test_lib:log("Consulting file ~s...~n", [Fname]), + case file:consult(Fname) of + {ok, Config} -> + reltool_test_lib:log("Read config ~w~n", [Config]), + Config; + _Error -> + Config = reltool_test_lib:default_config(), + reltool_test_lib:log("<>WARNING<> Using default config: ~w~n", [Config]), + Config + end. + +%% Write new default config file +write_config(Config) when is_list(Config) -> + Fname = config_fname(), + {ok, Fd} = file:open(Fname, write), + write_list(Fd, Config), + file:close(Fd). + +write_list(Fd, [H | T]) -> + ok = io:format(Fd, "~p.~n",[H]), + write_list(Fd, T); +write_list(_, []) -> + ok. + +test_case_fname() -> + "reltool_test_case_info". + +%% Read name of test case +read_test_case() -> + Fname = test_case_fname(), + case file:open(Fname, [read]) of + {ok, Fd} -> + Res = io:read(Fd, []), + file:close(Fd), + case Res of + {ok, TestCase} -> + reltool_test_lib:log("Using test case ~w from file ~s~n", + [TestCase, Fname]), + TestCase; + {error, _} -> + default_test_case(Fname) + end; + {error, _} -> + default_test_case(Fname) + end. + +default_test_case(Fname) -> + TestCase = all, + reltool_test_lib:log("<>WARNING<> Cannot read file ~s, " + "using default test case: ~w~n", + [Fname, TestCase]), + TestCase. + +write_test_case(TestCase) -> + Fname = test_case_fname(), + {ok, Fd} = file:open(Fname, write), + ok = io:format(Fd, "~p.~n",[TestCase]), + file:close(Fd). + +append_test_case_info(TestCase, TestCaseInfo) -> + Fname = test_case_fname(), + {ok, Fd} = file:open(Fname, [read, write]), + ok = io:format(Fd, "~p.~n",[TestCase]), + ok = io:format(Fd, "~p.~n",[TestCaseInfo]), + file:close(Fd), + TestCaseInfo. |