diff options
author | Siri Hansen <[email protected]> | 2010-08-17 13:34:35 +0200 |
---|---|---|
committer | Siri Hansen <[email protected]> | 2011-05-17 11:32:33 +0200 |
commit | 87fce5499baa1824eebe822cc62608c5bb58d3cf (patch) | |
tree | ee51c2c3b900ab1fea891ae3e9b7403e5173af52 /lib/sasl/test | |
parent | c1e2d6d62e9932b3d438fb9466ab716c81b252c2 (diff) | |
download | otp-87fce5499baa1824eebe822cc62608c5bb58d3cf.tar.gz otp-87fce5499baa1824eebe822cc62608c5bb58d3cf.tar.bz2 otp-87fce5499baa1824eebe822cc62608c5bb58d3cf.zip |
Add SASL test suite
Diffstat (limited to 'lib/sasl/test')
127 files changed, 7842 insertions, 0 deletions
diff --git a/lib/sasl/test/.gitignore b/lib/sasl/test/.gitignore new file mode 100644 index 0000000000..76e706b874 --- /dev/null +++ b/lib/sasl/test/.gitignore @@ -0,0 +1,5 @@ +# +# Don't ignore *.beam files in any sub-directory. +# + +!*.beam diff --git a/lib/sasl/test/Makefile b/lib/sasl/test/Makefile new file mode 100644 index 0000000000..ad08c8136b --- /dev/null +++ b/lib/sasl/test/Makefile @@ -0,0 +1,91 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2011. 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= \ + sasl_SUITE \ + alarm_handler_SUITE \ + installer \ + release_handler_SUITE \ + systools_SUITE \ + systools_rc_SUITE \ + overload_SUITE \ + rb_SUITE + +ERL_FILES= $(MODULES:%=%.erl) + +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +INSTALL_PROGS= $(TARGET_FILES) + +EMAKEFILE=Emakefile + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/sasl_test + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +ERL_MAKE_FLAGS += +ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include \ + -I$(ERL_TOP)/lib/sasl/src + +EBIN = . + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +make_emakefile: + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make'\ + > $(EMAKEFILE) + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES)\ + >> $(EMAKEFILE) + +tests debug opt: make_emakefile + erl $(ERL_MAKE_FLAGS) -make + +clean: + rm -f $(EMAKEFILE) + rm -f $(TARGET_FILES) + rm -f core + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + +release_tests_spec: make_emakefile + $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR) + $(INSTALL_DATA) sasl.spec sasl.cover $(EMAKEFILE) $(RELSYSDIR) + chmod -f -R u+w $(RELSYSDIR) + @tar cfh - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + +release_docs_spec: diff --git a/lib/sasl/test/alarm_handler_SUITE.erl b/lib/sasl/test/alarm_handler_SUITE.erl new file mode 100644 index 0000000000..a98e8c9c67 --- /dev/null +++ b/lib/sasl/test/alarm_handler_SUITE.erl @@ -0,0 +1,179 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. 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(alarm_handler_SUITE). + +-include_lib("test_server/include/test_server.hrl"). + +%%----------------------------------------------------------------- +%% We will add an own alarm handler in order to verify that the +%% alarm_handler deliver the expected events. +%%----------------------------------------------------------------- + +-export([init_per_suite/1, end_per_suite/1, all/0,groups/0, + init_per_group/2,end_per_group/2, + set_alarm/1, clear_alarm/1, swap/1]). + +-export([init/1, handle_event/2, handle_call/2, handle_info/2, + terminate/2]). + + +init_per_suite(Config) -> + application:start(sasl), + Config. + +end_per_suite(_Config) -> + ok. + +all() -> + [set_alarm, clear_alarm, swap]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + + +%%----------------------------------------------------------------- + +set_alarm(suite) -> []; +set_alarm(Config) when is_list(Config) -> + ?line gen_event:add_handler(alarm_handler, ?MODULE, self()), + Alarm1 = {alarm1, "this is the alarm"}, + Alarm2 = {"alarm2", this_is_the_alarm}, + Alarm3 = {{alarm3}, {this_is,"the_alarm"}}, + ?line ok = alarm_handler:set_alarm(Alarm1), + reported(set_alarm, Alarm1), + ?line ok = alarm_handler:set_alarm(Alarm2), + reported(set_alarm, Alarm2), + ?line ok = alarm_handler:set_alarm(Alarm3), + reported(set_alarm, Alarm3), + + ?line [Alarm3,Alarm2,Alarm1] = alarm_handler:get_alarms(), + alarm_handler:clear_alarm(alarm1), + alarm_handler:clear_alarm("alarm2"), + alarm_handler:clear_alarm({alarm3}), + ?line [] = alarm_handler:get_alarms(), + + test_server:messages_get(), + ?line my_yes = gen_event:delete_handler(alarm_handler, ?MODULE, []), + ok. + +%%----------------------------------------------------------------- + +clear_alarm(suite) -> []; +clear_alarm(Config) when is_list(Config) -> + ?line gen_event:add_handler(alarm_handler, ?MODULE, self()), + Alarm1 = {alarm1, "this is the alarm"}, + Alarm2 = {"alarm2", this_is_the_alarm}, + Alarm3 = {{alarm3}, {this_is,"the_alarm"}}, + alarm_handler:set_alarm(Alarm1), + alarm_handler:set_alarm(Alarm2), + alarm_handler:set_alarm(Alarm3), + test_server:messages_get(), + + ?line ok = alarm_handler:clear_alarm(alarm1), + reported(clear_alarm, alarm1), + ?line ok = alarm_handler:clear_alarm("alarm2"), + reported(clear_alarm, "alarm2"), + ?line ok = alarm_handler:clear_alarm({alarm3}), + reported(clear_alarm, {alarm3}), + ?line [] = alarm_handler:get_alarms(), + + ?line my_yes = gen_event:delete_handler(alarm_handler, ?MODULE, []), + ok. + +%%----------------------------------------------------------------- + +swap(suite) -> []; +swap(Config) when is_list(Config) -> + ?line Alarm1 = {alarm1, "this is the alarm"}, + ?line Alarm2 = {"alarm2", this_is_the_alarm}, + ?line Alarm3 = {{alarm3}, {this_is,"the_alarm"}}, + ?line alarm_handler:set_alarm(Alarm1), + ?line alarm_handler:set_alarm(Alarm2), + ?line alarm_handler:set_alarm(Alarm3), + + ?line foo, + case gen_event:which_handlers(alarm_handler) of + [alarm_handler] -> + ?line ok = gen_event:swap_handler(alarm_handler, + {alarm_handler, swap}, + {?MODULE, self()}), + ?line [?MODULE] = gen_event:which_handlers(alarm_handler), + Alarms = [Alarm3, Alarm2, Alarm1], + reported(swap_alarms, Alarms), + + %% get_alarms is only valid with the default handler installed. + ?line {error, _} = alarm_handler:get_alarms(), + + ?line my_yes = gen_event:delete_handler(alarm_handler, + ?MODULE, []), + ?line gen_event:add_handler(alarm_handler, alarm_handler, []), + ok; + _ -> + alarm_handler:clear_alarm(alarm1), + alarm_handler:clear_alarm("alarm2"), + alarm_handler:clear_alarm({alarm3}), + ok + end. + +%%----------------------------------------------------------------- +%% Check that the alarm has been received. +%%----------------------------------------------------------------- +reported(Tag, Data) -> + receive + {Tag, Data} -> + test_server:messages_get(), + ok + after 1000 -> + test_server:fail(no_alarm_received) + end. + +%%----------------------------------------------------------------- +%% The error_logger handler (gen_event behaviour). +%% Sends a notification to the Tester process about the events +%% generated by the Tester process. +%%----------------------------------------------------------------- +init(Tester) when is_pid(Tester) -> + {ok, Tester}; +init({Tester, {alarm_handler,Alarms}}) -> % Swap from default handler. + Tester ! {swap_alarms, Alarms}, + {ok, Tester}. + +handle_event({set_alarm, Alarm}, Tester) -> + Tester ! {set_alarm, Alarm}, + {ok, Tester}; +handle_event({clear_alarm, AlarmId}, Tester) -> + Tester ! {clear_alarm, AlarmId}, + {ok, Tester}; +handle_event(_Event, Tester) -> + {ok, Tester}. + +handle_info(_, Tester) -> + {ok, Tester}. + +handle_call(_Query, Tester) -> {ok, {error, bad_query}, Tester}. + +terminate(_Reason, _Tester) -> + my_yes. diff --git a/lib/sasl/test/installer.erl b/lib/sasl/test/installer.erl new file mode 100644 index 0000000000..a114c4b5c9 --- /dev/null +++ b/lib/sasl/test/installer.erl @@ -0,0 +1,778 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. 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(installer). + +%%-compile(export_all). +-export([install_1/2]). +-export([install_2/1]). +-export([install_3/2]). +-export([install_3a/1]). +-export([install_4/1]). +-export([install_5/1]). +-export([install_5a/1]). +-export([install_6/1]). +-export([install_7/1]). +-export([install_8/1]). +-export([install_9/1]). +-export([install_10/1]). +-export([install_11/1]). +-export([client1_1/4]). +-export([client2/3]). +-export([stop/1]). +-export([unpack_p1h/2]). +-export([permanent_p1h/1]). +-export([reg_proc/1]). +-export([registered_loop/1]). + +-define(print(List), {rh_print, TestNode} ! {print, {?MODULE, ?LINE}, List}). +-define(print_line(Line,List), {rh_print, TestNode} ! {print, {?MODULE, Line}, List}). +-define(fail(Term), exit({?MODULE, ?LINE, Term})). +-define(fail_line(Line,Term), exit({?MODULE, Line, Term})). + +-define(check_release(Vsn,Status,Apps), + check_release(TestNode,node(),Vsn,Status,Apps,?LINE)). +-define(check_release_client(Node,Vsn,Status,Apps), + check_release(TestNode,Node,Vsn,Status,Apps,?LINE)). + +-define(check_running_app(App,Vsn), + check_running_app(TestNode,node(),App,Vsn,?LINE)). +-define(check_running_app_client(Node,App,Vsn), + check_running_app(TestNode,Node,App,Vsn,?LINE)). + + +install_1(TestNode,PrivDir) -> + ?print([TestNode]), + ?print(["install_1 start"]), + + % Unpack and install P1H + {ok, "P1H"} = unpack_release(PrivDir,"rel1"), + ?print(["unpack_release P1H ok"]), + ?check_release("P1H",unpacked,["a-1.0"]), + {ok,"P1G",[new_appl]} = release_handler:install_release("P1H"), + ?print(["install_release P1H ok"]), + ?check_release("P1H",current,["a-1.0"]), + ?check_running_app(a,"1.0"), + X = a:a(), + ?print(["X", X]), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + ?print(["install_1 end OK"]), + ok. + % release_handler_SUITE will reboot this node now! + +install_2(TestNode) -> + ?print(["install_2 start"]), + + % Check that P1H is still unpacked, install it and make_permanent + ?check_release("P1H",unpacked,["a-1.0"]), + ?print(["install_2 P1H unpacked"]), + {ok,"P1G",[new_appl]} = release_handler:install_release("P1H"), + ?print(["install_2 install_release ok"]), + ?check_release("P1H",current,["a-1.0"]), + ?check_running_app(a,"1.0"), + ok = release_handler:make_permanent("P1H"). + % release_handler_SUITE will reboot this node now! + +install_3(TestNode,PrivDir) -> + ?print(["install_3 start"]), + + % Check that P1H is permanent + ?check_release("P1H",permanent,["a-1.0"]), + X = a:a(), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + % Unpack and install P1I + {ok, "P1I"} = unpack_release(PrivDir,"rel2"), + ?print(["install_3 unpack_release P1I ok"]), + ?check_release("P1I",unpacked,["a-1.1"]), + {ok,"P1H",[{extra, gott}]} = release_handler:check_install_release("P1I"), + {error,_} = release_handler:check_install_release("P1J"), + {ok,"P1H",[{extra, gott}]} = release_handler:install_release("P1I"), + ?print(["install_3 install_release P1I ok"]), + ?check_release("P1I",current,["a-1.1"]), + ?check_running_app(a,"1.1"), + X2 = a:a(), + {key2, newval2} = lists:keyfind(key2, 1, X2), + {key1, val1} = lists:keyfind(key1, 1, X2), + {ok, bval} = a:b(), + + % Unpack and install P2A + {ok, "P2A"} = unpack_release(PrivDir,"rel3"), + ?print(["install_3 unpack_release P2A ok"]), + ?check_release("P2A",unpacked,["a-1.1"]), + {ok, "P1I", [new_emu]} = release_handler:check_install_release("P2A"), + ok = release_handler:make_permanent("P1I"), + ?print(["install_3 make_permanent P1I ok"]), + ?check_release("P1I",permanent,["a-1.1"]), + ok. + +install_3a(TestNode) -> + {ok, "P1I", [new_emu]} = release_handler:install_release("P2A"), + %% Node is rebooted by the release_handler:install_release + %% (init:reboot) because P2A includes a new erts vsn and the relup + %% file contains a 'restart_new_emulator' instruction. + ?print(["install_3 P2A installed"]), + ok. + + + +install_4(TestNode) -> + ?print(["install_4 start"]), + + % Check that P2A is in use. + ?check_release("P2A",current,["a-1.1"]), + ?check_running_app(a,"1.1"), + X = a:a(), + {key2, newval2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + {ok, bval} = a:b(), + ok. + % release_handler_SUITE will reboot this node now! + +install_5(TestNode) -> + ?print(["install_5 start"]), + + % Check that P1I is used + {ok, "P1I", [new_emu]} = release_handler:check_install_release("P2A"), + ok. + +install_5a(TestNode) -> + % Install P2A again + {ok, "P1I", [new_emu]} = release_handler:install_release("P2A"), + %% Node is rebooted by the release_handler:install_release + %% (init:reboot) because P2A includes a new erts vsn and the relup + %% file contains a 'restart_new_emulator' instruction. + ?print(["install_5 P2A installed"]), + ok. + +install_6(TestNode) -> + ?print(["install_6 start"]), + + % Check that P2A is used + ?check_release("P2A",current,["a-1.1"]), + ?check_running_app(a,"1.1"), + X = a:a(), + {key2, newval2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + {ok, bval} = a:b(), + ok = release_handler:make_permanent("P2A"). + % release_handler_SUITE will reboot this node now! + + +install_7(TestNode) -> + ?print(["install_7 start"]), + + % Check that P2A is used + ?check_release("P2A",permanent,["a-1.1"]), + + % Install old P1H + ok = release_handler:reboot_old_release("P1H"), + ok. + +install_8(TestNode) -> + ?print(["install_8 start"]), + + % Check that P1H is permanent + ?check_release("P1H",permanent,["a-1.0"]), + X = a:a(), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + %% Remove P1I and P2A and check that a-1.1 and erts-<latest> are removed + ok = release_handler:remove_release("P2A"), + ok = release_handler:remove_release("P1I"), + {ok, Libs} = file:list_dir(code:lib_dir()), + {_,_,StdlibVsn} = lists:keyfind(stdlib,1,application:which_applications()), + true = lists:member("stdlib-"++StdlibVsn, Libs), + true = lists:member("a-1.0", Libs), + false = lists:member("a-1.1", Libs), + {ok, Dirs} = file:list_dir(code:root_dir()), + ["erts-4.4"] = lists:filter(fun(Dir) -> lists:prefix("erts-",Dir) end, Dirs), + ok. + % release_handler_SUITE will reboot this node now! + +install_9(TestNode) -> + ?print(["install_9 start"]), + + % Check that P1H is permanent + ?check_release("P1H",permanent,["a-1.0"]), + X = a:a(), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + % Install old P1G + ok = release_handler:reboot_old_release("P1G"), + ok. + +install_10(TestNode) -> + ?print(["install_10 start"]), + + % Check that P1G is permanent + ?check_release("P1G",permanent,[]), + ?check_release("P1H",old,["a-1.0"]), + + %% Remove P1H and check that both versions of application a is removed + ok = release_handler:remove_release("P1H"), + {ok, Libs} = file:list_dir(code:lib_dir()), + {_,_,StdlibVsn} = lists:keyfind(stdlib,1,application:which_applications()), + true = lists:member("stdlib-"++StdlibVsn, Libs), + false = lists:member("a-1.0", Libs), + false = lists:member("a-1.1", Libs), + ok. + % release_handler_SUITE will reboot this node now! + +install_11(TestNode) -> + ?print(["install_11 start"]), + + % Check that P1G is permanent + ?check_release("P1G",permanent,[]), + ok. + + + +%%----------------------------------------------------------------- +%% This test starts a client node which uses this node as master +%% for the release_handler. +%% The client node runs all tests as in installer/1 test case. +%% Thus, the client node will be rebooted several times. +%% The to_erl /tmp/NODENAME@HOSTNAME/ command can be used to connect +%% to the client node. +%% run_erl logs for the client can be found in the directory: +%% code:root_dir() ++ "/clients/type1/NODENAME@HOSTNAME/log +%%----------------------------------------------------------------- + + +client1_1(TestNode,PrivDir,MasterDir,ClientSname) -> + TestHost = test_host(), + ?print(["client1_1 start"]), + + {ok,IP} = inet:getaddr(TestHost,inet), + erl_boot_server:start([IP]), + + ok = net_kernel:monitor_nodes(true), + Node = start_client(TestNode,ClientSname), + trace_disallowed_calls(Node), + + %% Check env var for SASL on client node + SaslEnv = rpc:call(Node, application, get_all_env, [sasl]), + {_,CliDir} = lists:keyfind(client_directory,1,SaslEnv), + {_,[Master]} = lists:keyfind(masters,1,SaslEnv), + {_,StartCli} = lists:keyfind(start_prg,1,SaslEnv), + Root = code:root_dir(), + true = (CliDir =:= filename:join([Root,"clients","type1",Node])), + true = (StartCli =:= filename:join([CliDir,"bin","start"])), + true = (Master =:= node()), + + %% Unpack P1H on master + {ok, "P1H"} = unpack_release(PrivDir,"rel1"), + + %% Unpack and install P1H on client + P1HDir = filename:join([Root, "releases", "P1H"]), + + %% The AppDirs argument (last arg to set_unpacked) below is really + %% not necessary, it could just be [] since the path is the same + %% as default. But it is given here in order to force hitting the + %% release_handler:check_path function so it can be checked that + %% it does not use file:read_file_info on the client node, see + %% trace_disallowed_calls/1 and check_disallowed_calls/0 below. + %% (OTP-9142) + {ok, "P1H"} = rpc:call(Node, release_handler, set_unpacked, + [filename:join(P1HDir, "rel1.rel"), + [{a,"1.0",filename:join(MasterDir,lib)}]]), + + ?check_release_client(Node,"P1H",unpacked,["a-1.0"]), + + ok = rpc:call(Node, release_handler, install_file, + ["P1H", filename:join(P1HDir, "start.boot")]), + ok = rpc:call(Node, release_handler, install_file, + ["P1H", filename:join(P1HDir, "sys.config")]), + ok = rpc:call(Node, release_handler, install_file, + ["P1H", filename:join(P1HDir, "relup")]), + ?print([{release_handler_state, Node}, + rpc:call(Node, sys, get_status, [release_handler])]), + + {ok,"P1G",[new_appl]} = + rpc:call(Node, release_handler, check_install_release, ["P1H"]), + + {ok,"P1G",[new_appl]} = + rpc:call(Node, release_handler, install_release, ["P1H"]), + + Apps = rpc:call(Node, application, which_applications, []), + {a,"A CXC 138 11","1.0"} = lists:keyfind(a, 1, Apps), + X = rpc:call(Node, a, a, []), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_2(TestNode,PrivDir,Node). + +client1_2(TestNode,PrivDir,Node) -> + ?print(["client1_2 start"]), + + %% Check that P1H is still unpacked, install it and make_permanent + ?check_release_client(Node,"P1H",unpacked,["a-1.0"]), + + {ok,"P1G",[new_appl]} = + rpc:call(Node, release_handler, install_release, ["P1H"]), + ?check_release_client(Node,"P1H",current,["a-1.0"]), + ?check_running_app_client(Node,a,"1.0"), + + ok = rpc:call(Node, release_handler, make_permanent, ["P1H"]), + + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_3(TestNode,PrivDir,Node). + +client1_3(TestNode,PrivDir,Node) -> + ?print(["client1_3 start"]), + + %% Check that P1H is permanent + ?check_release_client(Node,"P1H",permanent,["a-1.0"]), + X = rpc:call(Node, a, a, []), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + %% Unpack P1I on master + {ok, "P1I"} = unpack_release(PrivDir,"rel2"), + + MasterRoot = code:root_dir(), + + %% Unpack and install P1I on client + P1IDir = filename:join([MasterRoot, "releases", "P1I"]), + {ok, "P1I"} = rpc:call(Node, release_handler, set_unpacked, + [filename:join(P1IDir, "rel2.rel"),[]]), + + ?check_release_client(Node,"P1I",unpacked,["a-1.1"]), + + ok = rpc:call(Node, release_handler, install_file, + ["P1I", filename:join(P1IDir, "start.boot")]), + ok = rpc:call(Node, release_handler, install_file, + ["P1I", filename:join(P1IDir, "sys.config")]), + ok = rpc:call(Node, release_handler, install_file, + ["P1I", filename:join(P1IDir, "relup")]), + + {ok,"P1H",[{extra, gott}]} = + rpc:call(Node, release_handler, check_install_release, ["P1I"]), + {error,_} = rpc:call(Node, release_handler, check_install_release, ["P1J"]), + {ok,"P1H",[{extra, gott}]} = + rpc:call(Node, release_handler, install_release, ["P1I"]), + + ?check_running_app_client(Node,a,"1.1"), + X2 = rpc:call(Node, a, a, []), + {key2, newval2} = lists:keyfind(key2, 1, X2), + {key1, val1} = lists:keyfind(key1, 1, X2), + {ok, bval} = rpc:call(Node, a, b, []), + + %% Unpack P2A on master + {ok, "P2A"} = unpack_release(PrivDir,"rel3"), + + %% Unpack and install P2A on client + P2ADir = filename:join([MasterRoot, "releases", "P2A"]), + {ok, "P2A"} = + rpc:call(Node, release_handler, set_unpacked, + [filename:join(P2ADir, "rel3.rel"),[]]), + + ok = rpc:call(Node, release_handler, install_file, + ["P2A", filename:join(P2ADir, "start.boot")]), + ok = rpc:call(Node, release_handler, install_file, + ["P2A", filename:join(P2ADir, "sys.config")]), + ok = rpc:call(Node, release_handler, install_file, + ["P2A", filename:join(P2ADir, "relup")]), + + {ok, "P1I", [new_emu]} = + rpc:call(Node, release_handler, check_install_release, ["P2A"]), + ok = rpc:call(Node, release_handler, make_permanent, ["P1I"]), + ?check_release_client(Node,"P1I",permanent,["a-1.1"]), + + %% since the install_release below reboot the node... + check_disallowed_calls(), + cover_client(TestNode,Node,stop_cover), + + {ok, "P1I", [new_emu]} = + rpc:call(Node, release_handler, install_release, ["P2A"]), + %% Reboots the client ! + + check_reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_4(TestNode,Node). + +client1_4(TestNode,Node) -> + ?print(["client1_4 start"]), + + %% Check that P2A is in use. + ?check_release_client(Node,"P2A",current,["a-1.1"]), + ?check_running_app_client(Node,a,"1.1"), + X = rpc:call(Node, a, a, []), + {key2, newval2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + {ok, bval} = rpc:call(Node, a, b, []), + + %% Reboot from P1I + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_5(TestNode,Node). + +client1_5(TestNode,Node) -> + ?print(["client1_5 start"]), + + %% Check that P1I is used + {ok, "P1I", [new_emu]} = + rpc:call(Node, release_handler, check_install_release, ["P2A"]), + + %% since the install_release below will reboot the node... + check_disallowed_calls(), + cover_client(TestNode,Node,stop_cover), + + %% Install P2A again + {ok, "P1I", [new_emu]} = + rpc:call(Node, release_handler, install_release, ["P2A"]), + + %% We are rebooted again. + check_reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_6(TestNode,Node). + +client1_6(TestNode,Node) -> + ?print(["client1_6 start"]), + + %% Check that P2A is used + ?check_release_client(Node,"P2A",current,["a-1.1"]), + ?check_running_app_client(Node,a,"1.1"), + X = rpc:call(Node, a, a, []), + {key2, newval2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + {ok, bval} = rpc:call(Node, a, b, []), + + %% Make P2A permanent + ok = rpc:call(Node, release_handler, make_permanent, ["P2A"]), + + %% Reboot from P2A + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_7(TestNode,Node). + +client1_7(TestNode,Node) -> + ?print(["client1_7 start"]), + + %% Check that P2A is used + ?check_release_client(Node,"P2A",permanent,["a-1.1"]), + + %% since the reboot_old_release below will reboot the node + check_disallowed_calls(), + cover_client(TestNode,Node,stop_cover), + + %% Install old P1H + rpc:call(Node, release_handler, reboot_old_release, ["P1H"]), + %% We are rebooted. + check_reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_8(TestNode,Node). + +client1_8(TestNode,Node) -> + ?print(["client1_8 start"]), + + %% Check that P1H is permanent + ?check_release_client(Node,"P1H",permanent,["a-1.0"]), + X = rpc:call(Node, a, a, []), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + %% Remove P1I and P2I from client + ok = rpc:call(Node, release_handler, set_removed, ["P2A"]), + ok = rpc:call(Node, release_handler, set_removed, ["P1I"]), + + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_9(TestNode,Node). + +client1_9(TestNode,Node) -> + ?print(["client1_9 start"]), + + %% Check that P2A and P1I does not exists and that PiH is permanent. + Rels = rpc:call(Node, release_handler, which_releases, []), + false = lists:keysearch("P2A", 2, Rels), + false = lists:keysearch("P1I", 2, Rels), + ?check_release_client(Node,"P1H",permanent,["a-1.0"]), + X = rpc:call(Node, a, a, []), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + %% since the reboot_old_release below will reboot the node + check_disallowed_calls(), + cover_client(TestNode,Node,stop_cover), + + %% Install old P1G + rpc:call(Node, release_handler, reboot_old_release, ["P1G"]), + %% We are rebooted. + check_reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_10(TestNode,Node). + +client1_10(TestNode,Node) -> + ?print(["client1_10 start"]), + + %% Check that P1G is permanent + ?check_release_client(Node,"P1G",permanent,[]), + ?check_release_client(Node,"P1H",old,["a-1.0"]), + {error,client_node} = rpc:call(Node,release_handler,remove_release,["P1H"]), + ok = rpc:call(Node, release_handler, set_removed, ["P1H"]), + + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_11(TestNode,Node). + +client1_11(TestNode,Node) -> + ?print(["client1_11 start"]), + + %% Check that P1G is permanent + ?check_release_client(Node,"P1G",permanent,[]), + + check_disallowed_calls(), + stop_client(TestNode,Node), %% TEST IS OK !! + net_kernel:monitor_nodes(false), + + ok = release_handler:remove_release("P2A"), + ok = release_handler:remove_release("P1I"), + ok = release_handler:remove_release("P1H"), + ok. + +%% Start tracing of the file module on the client node. This module +%% shall never be called, since +%% 1) the node is a client from the release_handler's point of view, +%% so all file access should be done via rpc calls to the master +%% 2) it is started with erl_prim_loader loader set to 'inet' so all +%% code loading should be done via the inet to the master +%% (OTP-9142) +%% This function is called each time the client node is started and to +%% check if a call has been made, call check_disallowed_node/0 +trace_disallowed_calls(Node) -> + MasterProc = self(), + rpc:call(Node,dbg,tracer,[process,{fun(T,_) -> MasterProc ! T end,[]}]), + rpc:call(Node,dbg,p,[all,call]), + rpc:call(Node,dbg,tp,[file,[]]). + +check_disallowed_calls() -> + receive + Trace when element(1,Trace)==trace -> + exit({disallowed_function_call,Trace}) + after 0 -> + ok + end. + +start_client(TestNode,Client) -> + {Start, Node} = do_start_client(Client,test_host()), + Cmd = lists:concat(["env NODENAME=",Client," ", + filename:join(code:root_dir(), Start)]), + ?print([{start_client,Client},Cmd]), + Res = os:cmd(Cmd), + ?print([{start_client,result},Res]), + receive + {nodeup, Node} -> + wait_started(TestNode,Node) + after 30000 -> + ?print([{start_client,failed,Node},net_adm:ping(Node)]), + ?fail({"can not start", Node}) + end. + +do_start_client(Client, Host) -> + Node = list_to_atom(lists:concat([Client,"@",Host])), + Start = filename:join(["clients", "type1", Node, "bin", "start"]), + {Start, Node}. + +reboot(TestNode,Node) -> + cover_client(TestNode,Node,stop_cover), + rpc:call(Node, init, reboot, []), + check_reboot(TestNode,Node). + +%% This way of checking that the node is rebooted will only work if +%% the nodes are automatically re-connected after the reboot. This +%% happens for master/client (when sasl is started on the client). +check_reboot(TestNode,Node) -> + receive + {nodedown, Node} -> + receive + {nodeup, Node} -> wait_started(TestNode,Node) + after 30000 -> + ?fail({Node, "not rebooted",net_adm:ping(Node)}) + end + after 30000 -> + ?fail({Node, "not closing down",net_adm:ping(Node)}) + end. + +stop_client(TestNode,Node) -> + cover_client(TestNode,Node,stop_cover), + rpc:call(Node, init, stop, []), + receive + {nodedown, Node} -> ok + after 30000 -> + ?fail({Node, "not stopping"}) + end. + +wait_started(TestNode,Node) -> + case rpc:call(Node, init, get_status, []) of + {started, _} -> + cover_client(TestNode,Node,start_cover), + Node; + _ -> + timer:sleep(1000), + wait_started(TestNode,Node) + end. + +cover_client(TestNode,Node,Func) -> + R = rpc:call(TestNode,release_handler_SUITE,Func,[Node]), + ?print([{Func,Node,R}]). + + +%%----------------------------------------------------------------- +%% This test starts a client node which uses this node as master +%% for the release_handler. +%% The client node has the name cli2@HOSTNAME. +%% The client node is not allowed to do ANY release updates +%% as it also have another (non-existing) master node. +%% +%% The to_erl /tmp/cli2@HOSTNAME/ command can be used to connect +%% to the client node. +%% run_erl logs for the client can be found in the directory: +%% code:root_dir() ++ "/clients/type1/cli2@HOSTNAME/log +%%----------------------------------------------------------------- +client2(TestNode,PrivDir,ClientSname) -> + TestHost = test_host(), + ?print(["client2 start"]), + + %% Clean up if previous test case failed + release_handler:remove_release("P1H"), + + ok = net_kernel:monitor_nodes(true), + Node = start_client(TestNode,ClientSname), + + %% Check env var for SASL on client node + ?print([{sasl_env, Node}, rpc:call(Node, application, get_all_env, [sasl])]), + SaslEnv = rpc:call(Node, application, get_all_env, [sasl]), + {_,CliDir} = lists:keyfind(client_directory,1,SaslEnv), + {_,[Master,Master2]} = lists:keyfind(masters,1,SaslEnv), + {_,StartCli} = lists:keyfind(start_prg,1,SaslEnv), + Root = code:root_dir(), + true = (CliDir =:= filename:join([Root,"clients","type1",Node])), + true = (StartCli =:= filename:join([CliDir,"bin","start"])), + true = (Master =:= node()), + true = (Master2 =:= list_to_atom("master2@"++TestHost)), + + {ok, "P1H"} = unpack_release(PrivDir,"rel1"), + + {error,{bad_masters,[Master2]}} = + rpc:call(Node, release_handler, set_unpacked, + [filename:join([Root, "releases", "P1H", "rel1.rel"]),[]]), + + {error,{no_such_release,"P1H"}} = + rpc:call(Node, release_handler, check_install_release, ["P1H"]), + + stop_client(TestNode,Node), %% TEST IS OK !! + net_kernel:monitor_nodes(false), + + release_handler:remove_release("P1H"), + ok. + + +stop(Now) -> + %% The timestamp is only used for debugging. It is printed by + %% release_handler_SUITE also. + R = init:stop(), + erlang:display({init_stop,Now,R}), + R. + +unpack_p1h(TestNode,PrivDir) -> + {ok, "P1H"} = unpack_release(PrivDir,"rel1"), + ?check_release("P1H",unpacked,["a-1.0"]), + ok. + +permanent_p1h(TestNode) -> + ?check_release("P1H",unpacked,["a-1.0"]), + {ok,"P1G",[new_appl]} = release_handler:install_release("P1H"), + ?check_release("P1H",current,["a-1.0"]), + ok = release_handler:make_permanent("P1H"), + ?check_release("P1H",permanent,["a-1.0"]), + ok. + + +reg_proc(Name) -> + catch unregister(Name), + Pid = spawn_link(?MODULE, registered_loop, [Name]), + global:register_name(Name, Pid), + ok. + +registered_loop(_Name) -> + receive + kill -> + exit(killed) + end. + +check_release(TestNode,Node,Vsn,Status,Apps,Line) -> + case rpc:call(Node,release_handler,which_releases,[]) of + {badrpc,_}=Error -> + ?fail_line(Line,{check_release,Node,Vsn,Status,Error}); + Rels -> + ?print_line(Line,["check_release:", Rels]), + {"SASL-test", Vsn, Libs, Status} = lists:keyfind(Vsn, 2, Rels), + true = lists:all(fun(App) -> lists:member(App,Libs) end,Apps), + ok + end. + +check_running_app(TestNode,Node,App,Vsn,Line) -> + case rpc:call(Node,application,which_applications,[]) of + {badrpc,_}=Error -> + ?fail_line(Line,{check_running_app,Node,App,Vsn,Error}); + Apps -> + ?print_line(Line,["check_running_app:", Apps]), + {App, _, Vsn} = lists:keyfind(a, 1, Apps), + ok + end. + +test_host() -> + {ok,Host} = inet:gethostname(), + Host. + +unpack_release(PrivDir,Rel) -> + copy(filename:join([PrivDir,Rel,Rel++".tar.gz"]), + filename:join(code:root_dir(),releases)), + release_handler:unpack_release(Rel). + +copy(Src, DestDir) -> + Dest = filename:join(DestDir, filename:basename(Src)), + {ok,_} = file:copy(Src, Dest), + ok. + diff --git a/lib/sasl/test/overload_SUITE.erl b/lib/sasl/test/overload_SUITE.erl new file mode 100644 index 0000000000..92b1aaed6e --- /dev/null +++ b/lib/sasl/test/overload_SUITE.erl @@ -0,0 +1,175 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. 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(overload_SUITE). +-include("test_server.hrl"). + +-compile(export_all). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +all() -> [info, set_config_data, set_env_vars, request, timeout]. +all(suite) -> all(). + +init_per_testcase(_Case,Config) -> + restart_sasl(), + Config. + +end_per_testcase(Case,Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +%%%----------------------------------------------------------------- +info(suite) -> []; +info(_Config) -> + ?line Info = overload:get_overload_info(), + ?line [{total_intensity,0.0}, + {accept_intensity,0.0}, + {max_intensity,0.8}, + {weight,0.1}, + {total_requests,0}, + {accepted_requests,0}] = Info. + +%%%----------------------------------------------------------------- +set_config_data(suite) -> []; +set_config_data(_Config) -> + ?line InfoDefault = overload:get_overload_info(), + ?line ok = check_info(0.8,0.1,InfoDefault), + ?line ok = overload:set_config_data(0.5,0.4), + ?line Info1 = overload:get_overload_info(), + ?line ok = check_info(0.5,0.4,Info1), + ok. + +%%%----------------------------------------------------------------- +set_env_vars(suite) -> []; +set_env_vars(_Config) -> + ?line InfoDefault = overload:get_overload_info(), + ?line ok = check_info(0.8,0.1,InfoDefault), + ?line ok = application:set_env(sasl,overload_max_intensity,0.5), + ?line ok = application:set_env(sasl,overload_weight,0.4), + ?line ok = application:stop(sasl), + ?line ok = application:start(sasl), + ?line Info1 = overload:get_overload_info(), + ?line ok = check_info(0.5,0.4,Info1), + ok. +set_env_vars(cleanup,_Config) -> + application:unset_env(sasl,overload_max_intensity), + application:unset_env(sasl,overload_weight), + ok. + +%%%----------------------------------------------------------------- +request(suite) -> []; +request(_Config) -> + %% Find number of request that can be done with default settings + %% and no delay + ?line overload:set_config_data(0.8, 0.1), + ?line NDefault = do_many_requests(0), + ?line restart_sasl(), + ?line ?t:format("NDefault: ~p",[NDefault]), + + %% Check that the number of requests increases when max_intensity + %% increases + ?line overload:set_config_data(2, 0.1), + ?line NLargeMI = do_many_requests(0), + ?line restart_sasl(), + ?line ?t:format("NLargeMI: ~p",[NLargeMI]), + ?line true = NLargeMI > NDefault, + + %% Check that the number of requests decreases when weight + %% increases + ?line overload:set_config_data(0.8, 1), + ?line NLargeWeight = do_many_requests(0), + ?line restart_sasl(), + ?line ?t:format("NLargeWeight: ~p",[NLargeWeight]), + ?line true = NLargeWeight < NDefault, + + %% Check that number of requests increases when delay between + %% requests increases. + %% (Keeping same config and comparing to large weight in order to + %% minimize the time needed for this case.) + ?line overload:set_config_data(0.8, 1), + ?line NLargeTime = do_many_requests(500), + ?line restart_sasl(), + ?line ?t:format("NLargeTime: ~p",[NLargeTime]), + ?line true = NLargeTime > NLargeWeight, + ok. + +%%%----------------------------------------------------------------- +timeout(suite) -> []; +timeout(_Config) -> + ?line overload:set_config_data(0.8, 1), + ?line _N = do_many_requests(0), + + %% Check that the overload alarm is raised + ?line [{overload,_}] = alarm_handler:get_alarms(), + + %% Fake a clear timeout in overload.erl and check that, since it + %% came very soon after the overload situation, the alarm is not + %% cleared + ?line overload ! timeout, + ?line timer:sleep(1000), + ?line [{overload,_}] = alarm_handler:get_alarms(), + + %% A bit later, try again and check that this time the alarm is + %% cleared + ?line overload ! timeout, + ?line timer:sleep(1000), + ?line [] = alarm_handler:get_alarms(), + + ok. + + +%%%----------------------------------------------------------------- +%%% INTERNAL FUNCTIONS + +%%%----------------------------------------------------------------- +%%% Call overload:request/0 up to 30 times with the given time delay +%%% between. Stop when 'reject' is returned. +do_many_requests(T) -> + 30 - do_requests(30,T). + +do_requests(0,_) -> + ?t:fail(never_rejected); +do_requests(N,T) -> + case overload:request() of + accept -> + timer:sleep(T), + do_requests(N-1,T); + reject -> + N + end. + +%%%----------------------------------------------------------------- +%%% Restart the sasl application +restart_sasl() -> + application:stop(sasl), + application:start(sasl), + ok. + +%%%----------------------------------------------------------------- +%%% Check that max_intensity and weight is set as expected +check_info(MI,W,Info) -> + case {lists:keyfind(max_intensity,1,Info), lists:keyfind(weight,1,Info)} of + {{_,MI},{_,W}} -> ok; + _ -> ?t:fail({unexpected_info,MI,W,Info}) + end. + + diff --git a/lib/sasl/test/rb_SUITE.erl b/lib/sasl/test/rb_SUITE.erl new file mode 100644 index 0000000000..b53c382609 --- /dev/null +++ b/lib/sasl/test/rb_SUITE.erl @@ -0,0 +1,606 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. 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(rb_SUITE). +-include("test_server.hrl"). + +-compile(export_all). + +-define(SUP,rb_SUITE_sup). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +all() -> + no_group_cases() ++ [{group,running_error_logger}]. + +no_group_cases() -> + [help, + start_error_stop]. + +groups() -> + [{running_error_logger,[shuffle],[show, + list, + rescan, + start_stop_log, + grep, + filter_filter, + filter_date, + filter_filter_and_date, + filter_re_no + ]}]. + + +all(suite) -> + no_group_cases() ++ + [{conf, + install_mf_h, + element(3,lists:keyfind(running_error_logger,1,groups())), + remove_mf_h} + ]. + + +init_per_suite(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line RbDir = filename:join(PrivDir,rb), + ?line ok = file:make_dir(RbDir), + NewConfig = [{rb_dir,RbDir}|Config], + reset_sasl(NewConfig), + NewConfig. + +end_per_suite(_Config) -> + ok. + +init_per_group(running_error_logger,Config) -> + install_mf_h(Config). + +end_per_group(running_error_logger,Config) -> + remove_mf_h(Config). + +init_per_testcase(_Case,Config) -> + case whereis(?SUP) of + undefined -> ok; + Pid -> kill(Pid) + end, + empty_error_logs(Config), + Config. + +kill(Pid) -> + Ref = erlang:monitor(process,Pid), + exit(Pid,kill), + receive {'DOWN', Ref, process, Pid, _Info} -> ok end. + +end_per_testcase(Case,Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + + +%%%----------------------------------------------------------------- + +help() -> help(suite). +help(suite) -> []; +help(_Config) -> + ?line Help = capture(fun() -> rb:h() end), + ?line "Report Browser Tool - usage" = hd(Help), + ?line "rb:stop - stop the rb_server" = lists:last(Help), + ok. + + +start_error_stop() -> start_error_stop(suite). +start_error_stop(suite) -> []; +start_error_stop(Config) -> + ?line RbDir = ?config(rb_dir,Config), + + ?line {error,{"cannot locate report directory",_}} = rb:start(), + + + ?line ok = application:set_env(sasl,error_logger_mf_dir,"invaliddir"), + ?line ok = application:set_env(sasl,error_logger_mf_maxbytes,1000), + ?line ok = application:set_env(sasl,error_logger_mf_maxfiles,2), + ?line restart_sasl(), + ?line {error,{"cannot read the index file",_}} = rb:start(), + ?line ok = application:set_env(sasl,error_logger_mf_dir,RbDir), + ?line restart_sasl(), + ?line {ok,_} = rb:start(), + + ?line ok = rb:stop(), + ok. + + +%% start_opts(suite) -> []; +%% start_opts(Config) -> +%% PrivDir = ?config(priv_dir,Config), +%% RbDir = filename:join(PrivDir,rb_opts), +%% ok = file:make_dir(RbDir), + + +install_mf_h(Config) -> + ?line RbDir = ?config(rb_dir,Config), + ?line ok = application:set_env(sasl,error_logger_mf_dir,RbDir), + ?line ok = application:set_env(sasl,error_logger_mf_maxbytes,5000), + ?line ok = application:set_env(sasl,error_logger_mf_maxfiles,2), + ?line restart_sasl(), + Config. + +remove_mf_h(_Config) -> + ok. + + + +show() -> show(suite). +show(suite) -> []; +show(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + %% Show all reports + ?line All = check_report(fun() -> rb:show() end,OutFile), + + %% Show by number + ?line [{_,First}] = check_report(fun() -> rb:show(1) end,OutFile), + ?line {1,First} = lists:keyfind(1,1,All), + + %% Show by type + ?line [{_,CR}] = check_report(fun() -> rb:show(crash_report) end,OutFile), + ?line true = contains(CR,"rb_test_crash"), + ?line [{_,EC},{_,EM}] = check_report(fun() -> rb:show(error) end,OutFile), + ?line true = contains(EC,"rb_test_crash"), + ?line true = contains(EM,"rb_test_error_msg"), + ?line [{_,ER}] = check_report(fun() -> rb:show(error_report) end,OutFile), + ?line true = contains(ER,"rb_test_error"), + ?line [{_,IR}] = check_report(fun() -> rb:show(info_report) end,OutFile), + ?line true = contains(IR,"rb_test_info"), + ?line [{_,IM}] = check_report(fun() -> rb:show(info_msg) end,OutFile), + ?line true = contains(IM,"rb_test_info_msg"), + ?line [_|_] = check_report(fun() -> rb:show(progress) end,OutFile), + ?line [{_,SR}] = check_report(fun() -> rb:show(supervisor_report) end, + OutFile), + ?line true = contains(SR,"child_terminated"), + ?line true = contains(SR,"{rb_SUITE,rb_test_crash}"), + + ok. + +list() -> list(suite). +list(suite) -> []; +list(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + ?line All = capture(fun() -> rb:list() end), + ?line [{crash_report,[_]=CR}, + {error,[_,_]=EM}, + {error_report,[_]=ER}, + {info_msg,[_]=IM}, + {info_report,[_]=IR}, + {progress,[_|_]=P}, + {supervisor_report,[_]=SR}] = sort_list(All), + + ?line [{crash_report,CR}] = + sort_list(capture(fun() -> rb:list(crash_report) end)), + ?line [{error,EM}] = + sort_list(capture(fun() -> rb:list(error) end)), + ?line [{error_report,ER}] = + sort_list(capture(fun() -> rb:list(error_report) end)), + ?line [{info_msg,IM}] = + sort_list(capture(fun() -> rb:list(info_msg) end)), + ?line [{info_report,IR}] = + sort_list(capture(fun() -> rb:list(info_report) end)), + ?line [{progress,P}] = + sort_list(capture(fun() -> rb:list(progress) end)), + ?line [{supervisor_report,SR}] = + sort_list(capture(fun() -> rb:list(supervisor_report) end)), + + ok. + + +grep() -> grep(suite). +grep(suite) -> []; +grep(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + ?line [{_,S}, + {_,CR}, + {_,EC}, + {_,IM}, + {_,IR}, + {_,EM}, + {_,ER}]= check_report(fun() -> rb:grep("rb_test_") end,OutFile), + ?line true = contains(S, "rb_test_crash"), + ?line true = contains(CR, "rb_test_crash"), + ?line true = contains(EC, "rb_test_crash"), + ?line true = contains(IM, "rb_test_info_msg"), + ?line true = contains(IR, "rb_test_info"), + ?line true = contains(EM, "rb_test_error_msg"), + ?line true = contains(ER, "rb_test_error"), + ok. + + +filter_filter() -> filter_filter(suite). +filter_filter(suite) -> []; +filter_filter(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + ?line All = check_report(fun() -> rb:show() end,OutFile), + + ?line ER = [_] = rb_filter([{rb_SUITE,rb_test_error}],OutFile), + ?line [] = rb_filter([{rb_SUITE,rb_test}],OutFile), + ?line _E = [_,_] = rb_filter([{rb_SUITE,"rb_test",re}],OutFile), + ?line AllButER = rb_filter([{rb_SUITE,rb_test_error,no}],OutFile), + + {_,AllRep} = lists:unzip(All), + {_,ERRep} = lists:unzip(ER), + {_,AllButERRep} = lists:unzip(AllButER), + ?line AllButERRep = AllRep -- ERRep, + + ok. + +filter_date() -> filter_date(suite). +filter_date(suite) -> []; +filter_date(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + + %% Insert some reports in the error log and start rb + init_error_logs(), + Between1 = calendar:local_time(), + timer:sleep(1000), + Between2 = calendar:local_time(), + ?line ok = start_rb(OutFile), + + ?line All = check_report(fun() -> rb:show() end,OutFile), + + Before = calendar:gregorian_seconds_to_datetime( + calendar:datetime_to_gregorian_seconds(calendar:local_time()) - 10), + After = calendar:gregorian_seconds_to_datetime( + calendar:datetime_to_gregorian_seconds(calendar:local_time()) + 1), + + ?line All = rb_filter([],{Before,from},OutFile), + ?line All = rb_filter([],{After,to},OutFile), + ?line [] = rb_filter([],{Before,to},OutFile), + ?line [] = rb_filter([],{After,from},OutFile), + ?line All = rb_filter([],{Before,After},OutFile), + + %%?t:format("~p~n",[All]), + ?line AllButLast = [{N-1,R} || {N,R} <- tl(All)], + ?line AllButLast = rb_filter([],{Before,Between1},OutFile), + + ?line Last = hd(All), + ?line [Last] = rb_filter([],{Between2,After},OutFile), + + ok. + +filter_filter_and_date() -> filter_filter_and_date(suite). +filter_filter_and_date(suite) -> []; +filter_filter_and_date(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + + %% Insert some reports in the error log and start rb + init_error_logs(), + Between1 = calendar:local_time(), + timer:sleep(1000), + Between2 = calendar:local_time(), + ?line error_logger:error_report([{rb_SUITE,rb_test_filter}]), + ?line ok = start_rb(OutFile), + + Before = calendar:gregorian_seconds_to_datetime( + calendar:datetime_to_gregorian_seconds(calendar:local_time()) - 10), + After = calendar:gregorian_seconds_to_datetime( + calendar:datetime_to_gregorian_seconds(calendar:local_time()) + 1), + + ?line All = check_report(fun() -> rb:show() end,OutFile), + ?line Last = hd(All), + + ?line [_,_,_] = rb_filter([{rb_SUITE,"rb_test",re}],{Before,After},OutFile), + ?line [_,_] = rb_filter([{rb_SUITE,"rb_test",re}],{Before,Between1},OutFile), + ?line [_] = rb_filter([{rb_SUITE,"rb_test",re}],{Between2,After},OutFile), + ?line [_] = rb_filter([{rb_SUITE,rb_test_filter}],{Before,After},OutFile), + ?line [] = rb_filter([{rb_SUITE,rb_test_filter}],{Before,Between1},OutFile), + ?line [Last] = rb_filter([{rb_SUITE,rb_test_filter,no}],{Between2,After},OutFile), + ?line {_,Str} = Last, + ?line false = contains(Str,"rb_test_filter"), + + ok. + + +filter_re_no() -> filter_re_no(suite). +filter_re_no(suite) -> []; +filter_re_no(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + ?line All = check_report(fun() -> rb:show() end,OutFile), + + ?line E = [_,_] = rb_filter([{rb_SUITE,"rb_test",re}],OutFile), + ?line AllButE = rb_filter([{rb_SUITE,"rb_test",re,no}],OutFile), + + {_,AllRep} = lists:unzip(All), + {_,ERep} = lists:unzip(E), + {_,AllButERep} = lists:unzip(AllButE), + ?line AllButERep = AllRep -- ERep, + + ok. + + +rescan() -> rescan(suite). +rescan(suite) -> []; +rescan(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Start rb + ?line ok = start_rb(OutFile), + + %% Insert one more report and check that the list is longer. Note + %% that there might be two more reports, since the progress report + %% from starting rb_server might not be included before the rescan. + ?line AllBefore = capture(fun() -> rb:list() end), + ?line error_logger:error_report([{rb_SUITE,rb_test_rescan}]), + ?line ok = rb:rescan(), + ?line AllAfter = capture(fun() -> rb:list() end), + ?line Diff = length(AllAfter) - length(AllBefore), + ?line true = (Diff >= 1), + + ok. + + +start_stop_log() -> start_stop_log(suite). +start_stop_log(suite) -> []; +start_stop_log(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + ?line ok = file:write_file(OutFile,[]), + + %% Start rb and check that show is printed to standard_io + ?line ok = start_rb(), + ?line StdioResult = [_|_] = capture(fun() -> rb:show(1) end), + ?line {ok,<<>>} = file:read_file(OutFile), + + %% Start log and check that show is printed to log and not to standad_io + ?line ok = rb:start_log(OutFile), + ?line [] = capture(fun() -> rb:show(1) end), + ?line {ok,Bin} = file:read_file(OutFile), + ?line true = (Bin =/= <<>>), + + %% Stop log and check that show is printed to standard_io and not to log + ?line ok = rb:stop_log(), + ?line ok = file:write_file(OutFile,[]), + ?line StdioResult = capture(fun() -> rb:show(1) end), + ?line {ok,<<>>} = file:read_file(OutFile), + + %% Test that standard_io is used if log file can not be opened + ?line ok = rb:start_log(filename:join(nonexistingdir,"newfile.txt")), + ?line StdioResult = capture(fun() -> rb:show(1) end), + ?line {ok,<<>>} = file:read_file(OutFile), + + ok. + + +%%%----------------------------------------------------------------- +%%% INTERNAL FUNCTIONS + +restart_sasl() -> + application:stop(sasl), + ok = application:start(sasl), + wait_for_sasl(). + +reset_sasl(Config) -> + application:unset_env(sasl,error_logger_mf_dir), + application:unset_env(sasl,error_logger_mf_maxbytes), + application:unset_env(sasl,error_logger_mf_maxfiles), + empty_error_logs(Config). + +empty_error_logs(Config) -> + application:stop(sasl), + catch delete_content(?config(rb_dir, Config)), + ok = application:start(sasl), + wait_for_sasl(). + +wait_for_sasl() -> + wait_for_sasl(50). +wait_for_sasl(0) -> + ?t:fail("sasl application did not start within 5 seconds"); +wait_for_sasl(N) -> + case lists:keymember(sasl,1,application:which_applications()) of + true -> + ok; + false -> + timer:sleep(100), + wait_for_sasl(N-1) + end. + +start_rb(OutFile) -> + do_start_rb([{start_log,OutFile}]). +start_rb() -> + do_start_rb([]). + +do_start_rb(Opts) -> + {ok,Pid} = rb:start(Opts), + + %% Wait for process to started, then wait a little bit more + sys:get_status(Pid), + timer:sleep(500), + + %% Make sure printouts (e.g. from rb:list(), come to the test log, + %% and that they can be captured. + group_leader(group_leader(),Pid), + ok. + + +delete_tree(Dir) -> + case filelib:is_dir(Dir) of + true -> + delete_content(Dir), + file:del_dir(Dir); + false -> + ok = file:delete(Dir) + end. + +delete_content(Dir) -> + {ok,Files} = file:list_dir(Dir), + lists:foreach(fun(File) -> delete_tree(filename:join(Dir,File)) end, + Files). + +init_error_logs() -> + ?line error_logger:error_report([{rb_SUITE,rb_test_error}]), + ?line error_logger:error_msg("rb_test_error_msg"), + ?line error_logger:info_report([{rb_SUITE,rb_test_info}]), + ?line error_logger:info_msg("rb_test_info_msg"), + ?line _Pid = start(), + ?line Ref = erlang:monitor(process,?MODULE), + ?line gen_server:cast(?MODULE,crash), + ?line receive {'DOWN',Ref,process,_,{rb_SUITE,rb_test_crash}} -> ok + after 2000 -> + ?t:format("Got: ~p~n",[process_info(self(),messages)]), + ?t:fail("rb_SUITE server never died") + end, + ?line erlang:demonitor(Ref), + ?line wait_for_server(), + ok. + +wait_for_server() -> + case whereis(?MODULE) of + undefined -> + wait_for_server(); + Pid -> + timer:sleep(100), % allow the supervisor report to be written + Pid + end. + +capture(Fun) -> + ?t:capture_start(), + ok = Fun(), + timer:sleep(1000), + ?t:capture_stop(), + string:tokens(lists:append(?t:capture_get()),"\n"). + + +rb_filter(Filter,OutFile) -> + check_report(fun() -> rb:filter(Filter) end, OutFile). +rb_filter(Filter,Dates,OutFile) -> + check_report(fun() -> rb:filter(Filter,Dates) end, OutFile). + + +%% This function first empties the given report file, then executes +%% the fun and returns a list of {N,Report}, where Report is a report +%% read from the file and N is an integer. The newest report has the +%% lowest number. +%% If the fun was a call to rb:show() (i.e. with no arguments), then +%% the numbering (N) will be the same as rb's own numbering (as shown +%% by rb:list()). +check_report(Fun,File) -> + file:delete(File), + rb:rescan([{start_log,File}]), + ok = Fun(), + {ok,Bin} = file:read_file(File), + Reports = split_reports(binary_to_list(Bin),[],[]), + lists:zip(lists:seq(1,length(Reports)),Reports). + +-define(report_header_line,"\n===============================================================================\n"). +split_reports([],Report,Reports) -> + add_report(Report,Reports); +split_reports(Text,Report,Reports) -> + case Text of + ?report_header_line++Rest -> + {Heading,PrevReport} = lists:splitwith(fun($\n) -> false; + (_) -> true + end, + Report), + split_reports(Rest, + ?report_header_line++Heading, + add_report(PrevReport,Reports)); + [Ch|Rest] -> + split_reports(Rest,[Ch|Report],Reports) + end. + +add_report(Report,Reports) -> + case string:strip(Report,both,$\n) of + [] -> Reports; + Report1 -> [lists:reverse(Report1)|Reports] + end. + +%% Returns true if Substr is a substring of Str. +contains(Str,Substr) -> + 0 =/= string:str(Str,Substr). + +%% Sort the result of rb_list after report type +sort_list(List) -> + sort_list(List,dict:new()). +sort_list([H|T],D) -> + case re:run(H,"\s+[0-9]+\s+([a-z_]+)",[{capture,all_but_first,list}]) of + nomatch -> + sort_list(T,D); + {match,[TypeStr]} -> + sort_list(T,dict:append(list_to_atom(TypeStr),H,D)) + end; +sort_list([],D) -> + lists:sort(dict:to_list(D)). + + +%%%----------------------------------------------------------------- +%%% A dummy supervisor and gen_server used for creating crash- and +%%% supervisor reports +start() -> + {ok,Pid} = + supervisor:start_link({local, ?SUP}, ?MODULE, i_am_supervisor), + unlink(Pid), + Pid. +start_server() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, i_am_server, []). +init(i_am_server) -> + {ok, state}; +init(i_am_supervisor) -> + AChild = {?SUP,{?MODULE,start_server,[]}, + permanent,2000,worker,[?MODULE]}, + {ok,{{one_for_all,1,1}, [AChild]}}. +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. +handle_cast(crash, State) -> + exit({rb_SUITE,rb_test_crash}), + {noreply, State}. +handle_info(_Info, State) -> + {noreply, State}. +terminate(_Reason, _State) -> + ok. + diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl new file mode 100644 index 0000000000..efa775f344 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE.erl @@ -0,0 +1,1651 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. 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(release_handler_SUITE). + +-include_lib("common_test/include/ct.hrl"). + +-compile(export_all). + +% Default timetrap timeout (set in init_per_testcase). +%-define(default_timeout, ?t:minutes(40)). +-define(default_timeout, ?t:minutes(10)). + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +init_per_suite(Config) -> + application:start(sasl), + Config. + +end_per_suite(_Config) -> + ok. + +all() -> + case os:type() of + {unix, _} -> unix_cases(); + {win32, _} -> win32_cases() + end. + +unix_cases() -> + RunErl = filename:join([code:root_dir(),"bin","run_erl"]), + RunErlCases = case filelib:is_file(RunErl) of + true -> [{group, release}]; + false -> [no_run_erl] + end, + [target_system] ++ RunErlCases ++ cases(). + +win32_cases() -> + cases(). + +%% Cases that can be run on all platforms +cases() -> + [otp_2740, otp_2760, otp_5761, instructions, eval_appup]. + +groups() -> + [{release,[], + [ + {group,release_single}, + {group,release_gg} + ]}, + {release_single,[], + [ + upgrade, + client1, + client2 + ]}, + {release_gg,[], + [ + upgrade_gg + ]}]. + +%% {group,release} +%% Top group for all cases using run_erl +init_per_group(release, Config) -> + Dog = ?t:timetrap(?default_timeout), + P1gInstall = filename:join(priv_dir(Config),p1g_install), + ok = do_create_p1g(Config,P1gInstall), + ok = create_p1h(Config), + ?t:timetrap_cancel(Dog); + +%% {group,release_single} +%% Subgroup of {group,release}, contains all cases that are not +%% related to global_group +init_per_group(release_single, Config) -> + Dog = ?t:timetrap(?default_timeout), + + %% Create some more releases to upgrade to + ok = create_p1i(Config), + ok = create_p2a(Config), + + ?t:timetrap_cancel(Dog); + +%% {group,release_gg} +%% Subgroup of {group,release}. global_group tests. +init_per_group(release_gg, Config0) -> + Config = [{sname_prefix,release_gg}|Config0], + + PrivDir = priv_dir(Config), + Dog = ?t:timetrap(?default_timeout), + + reg_print_proc(), %% starts a printer process on this node + + Snames = [Gg1Sname,Gg2Sname,Gg3Sname,Gg4Sname,Gg5Sname,Gg6Sname] = + gg_node_snames(Config), + + %% kill all possible nodes which are to be used + ok = stop_nodes([node_name(Sname) || Sname <- Snames]), + + %% For gg1, gg3, gg4 and gg5: create a target system running + %% P1G, and with P1H unpacked. + %% For gg2 and gg6: create a target system running P1H. + %% Use gg2 for unpacking and permanenting P1H. + ok = copy_installed(Config,p1g_install,[Gg2Sname]), + InstallNode = unpack_p1h(Config,Gg2Sname), + ok = copy_installed(Config,Gg2Sname,[Gg1Sname,Gg3Sname,Gg4Sname,Gg5Sname]), + ok = permanent_p1h(InstallNode), + ok = stop_nodes([InstallNode]), + ok = copy_installed(Config,Gg2Sname,[Gg6Sname]), + + %% Replace the sys.config files + %% The reason for not creating the releases with these configs in + %% the first place (create_p1g, create_p1h) is that then the + %% InstallNode (gg2) will be very slow started since it will try + %% to synch with the other nodes in the global group. + %% Also, the rpc call for installing the P1H release (in + %% permanent_p1h/1) would return {rpc,nodedown} due to change of + %% global groups. + lists:foreach( + fun(Sname) -> + ReleasesDir = filename:join([PrivDir,Sname,"releases"]), + write_term_file(filename:join([ReleasesDir,"P1G","sys.config"]), + gg_config([Gg1Sname,Gg3Sname,Gg4Sname,Gg5Sname])), + write_term_file(filename:join([ReleasesDir,"P1H","sys.config"]), + gg_config([Gg1Sname,Gg2Sname,Gg4Sname, + Gg5Sname,Gg6Sname])) + end, + Snames), + + ?t:timetrap_cancel(Dog), + [{snames,Snames}|Config]. + + +end_per_group(release, Config) -> + Dog = ?t:timetrap(?default_timeout), + stop_print_proc(), + delete_release(Config), + ?t:timetrap_cancel(Dog), + Config; +end_per_group(_GroupName, Config) -> + Config. + + +init_per_testcase(Case, Config0) -> + Dog = test_server:timetrap(?default_timeout), + Config = [{sname_prefix,Case},{watchdog, Dog}|Config0], + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ?t:format("~n======= init_per_testcase done =======~n",[]), + Config. + +end_per_testcase(Case, Config) -> + ?t:format("~n======= start end_per_testcase =======~n",[]), + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + + %% DEBUG + case ?config(tc_status,Config) of + ok -> + ok; + _Fail -> + %% save logs from master and client nodes + PrivDir = priv_dir(Config), + SaveDir = filename:join(PrivDir,save), + FailDir = filename:join(SaveDir,lists:concat(["failed-",Case])), + ok = filelib:ensure_dir(filename:join(FailDir,"*")), + + LogDirs = filelib:wildcard(filename:join([PrivDir,"*",log])), + + lists:foreach( + fun(LogDir) -> + ["log",Sname|_] = lists:reverse(filename:split(LogDir)), + copy_tree(Config,LogDir,Sname,FailDir) + end, + LogDirs), + + case filelib:is_file("sasl_erl_crash.dump") of + true -> + copy_file("sasl_erl_crash.dump",FailDir); + _ -> + ok + end + + end, + %% End DEBUG + + %% Remove any remaining sasl_erl_crash.dump + %% These can occur when a new master@<host> is started, before + %% the old usage of the name is unregistered, causing the node to + %% terminate. (This has no effect on the test case, as the node is + %% immediately restarted by heart and the test cases wait until + %% the node is actually up and running -- see wait_nodes_up/2) + file:delete("sasl_erl_crash.dump"), + + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +gg_node_snames(Config) -> + [tc_sname(Config,X) || X <- [gg1,gg2,gg3,gg4,gg5,gg6]]. + + +%%%----------------------------------------------------------------- +%%% TEST CASES + + +%% Executed instead of release group when no run_erl program exists +no_run_erl(Config) when is_list(Config) -> + {comment, "No run_erl program"}. + + + +%% Test upgrade and downgrade of erts +upgrade(Conf) when is_list(Conf) -> + reg_print_proc(), %% starts a printer process on test_server node + ?t:format("upgrade ~p~n",[reg_print_proc]), + PrivDir = priv_dir(Conf), + Sname = tc_sname(Conf), % nodename for use in this testcase + + %% Copy the P1G release to a directory for use in this testcase + ok = copy_installed(Conf,p1g_install,[Sname]), + + %% start the test node + [TestNode] = start_nodes(Conf,[Sname],"upgrade start"), + + %% unpack and install P1H + ok = rpc_inst(TestNode, install_1, [PrivDir]), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_1"), + + %% reinstall P1H and make it permanent + ok = rpc_inst(TestNode, install_2, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_2",[a]), + + %% check that P1H is permanent, unpack and install P1I, unpack and install P2A + TestNodeInit1 = rpc:call(TestNode,erlang,whereis,[init]), + ok = rpc_inst(TestNode, install_3, [PrivDir]), + stop_cover(TestNode), + ok = rpc_inst(TestNode, install_3a, []), + wait_nodes_up([{TestNode,TestNodeInit1}],"install_3",[a]), + + %% check that P2A is used, reboot from P1I + ok = rpc_inst(TestNode, install_4, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_4",[a]), + + %% check that P1I, reinstall P2A + TestNodeInit2 = rpc:call(TestNode,erlang,whereis,[init]), + ok = rpc_inst(TestNode, install_5, []), + stop_cover(TestNode), + ok = rpc_inst(TestNode, install_5a, []), + wait_nodes_up([{TestNode,TestNodeInit2}],"install_5",[a]), + + %% check that P2A is used, make P2A permanent + ok = rpc_inst(TestNode, install_6, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_6",[a]), + + %% check that P2A is permanent, install old P1H + TestNodeInit3 = rpc:call(TestNode,erlang,whereis,[init]), + stop_cover(TestNode), + ok = rpc_inst(TestNode, install_7, []), + wait_nodes_up([{TestNode,TestNodeInit3}],"install_7",[a]), + + %% check that P1H is permanent, remove P1I and P2A + ok = rpc_inst(TestNode, install_8, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_8",[a]), + + %% check that P1H is permanent, reboot old P1G + TestNodeInit4 = rpc:call(TestNode,erlang,whereis,[init]), + stop_cover(TestNode), + ok = rpc_inst(TestNode, install_9, []), + wait_nodes_up([{TestNode,TestNodeInit4}],"install_9"), + + %% check that P1G is permanent, remove P1H + ok = rpc_inst(TestNode, install_10, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_10"), + + %% check that P1G is permanent + ok = rpc_inst(TestNode, install_11, []), + + ok. + +upgrade(cleanup,Config) -> + TestNode = tc_full_node_name(Config), + ok = stop_nodes([TestNode]). + +reboot_and_wait(Node,Tag) -> + reboot_and_wait(Node,Tag,[]). + +reboot_and_wait(Node,Tag,Apps) -> + InitPid = rpc:call(Node,erlang,whereis,[init]), + ok = rpc:call(Node,init,reboot,[]), + wait_nodes_up([{Node,InitPid}],Tag,Apps). + + +%% Test upgrade and downgrade of erts, diskless +client1(Conf) when is_list(Conf) -> + reg_print_proc(), %% starts a printer process on test_server node + PrivDir = priv_dir(Conf), + Master = tc_sname(Conf,master), + Client = tc_sname(Conf,client), + MasterDir = filename:join(PrivDir,Master), + + %% Copy the P1G release to a directory for use in this testcase + ok = copy_installed(Conf,p1g_install,[Master]), + ok = copy_client(Conf,Master,Client,"start_cli1"), + + %% start the master node + [TestNode] = start_nodes(Conf,[Master],"client1"), + + ok = rpc_inst(TestNode, client1_1, [PrivDir,MasterDir,Client]), + + ok. + +client1(cleanup,Config) -> + MasterNode = tc_full_node_name(Config,master), + ClientNode = tc_full_node_name(Config,client), + ok = stop_nodes([MasterNode,ClientNode]). + + + +%% Test diskless release handling when illegal master node +client2(Conf) when is_list(Conf) -> + reg_print_proc(), %% starts a printer process on test_server node + PrivDir = priv_dir(Conf), + Master = tc_sname(Conf,master), + Client = tc_sname(Conf,client), + + %% Copy the P1G release to a directory for use in this testcase + ok = copy_installed(Conf,p1g_install,[Master]), + ok = copy_client(Conf,Master,Client,"start_cli2"), + + %% start the master node + [TestNode] = start_nodes(Conf,[Master],"client2"), + + ok = rpc_inst(TestNode, client2, [PrivDir,Client]), + + ok. + +client2(cleanup,Config) -> + MasterNode = tc_full_node_name(Config,master), + ClientNode = tc_full_node_name(Config,client), + ok = stop_nodes([MasterNode,ClientNode]). + + + +%% Test instructions _not_ tested by the installer module. +instructions(Conf) when is_list(Conf) -> + DataDir = ?config(data_dir, Conf), + + Dir = filename:join(DataDir, "c"), + true = code:add_path(Dir), + check_bstate("no", []), + ok = application:start(c), + ok = wait_for(bb), + check_bstate("first", []), + FirstBB = whereis(bb), + + case whereis(cc) of + Pid when is_pid(Pid) -> ok; + _ -> ?t:fail("cc not started") + end, + + %% Stop and start cc process + S1 = [point_of_no_return, + {stop, [aa]}, + {apply, {?MODULE, no_cc, []}}, + {start, [aa]}], + {ok, _} = release_handler_1:eval_script(S1, [], []), + + case whereis(cc) of + Pid2 when is_pid(Pid2) -> ok; + _ -> ?t:fail("cc not started") + end, + + %% Make bb run old version of b. + S2 = [point_of_no_return, + {remove, {b, soft_purge, soft_purge}}], + {ok, [{b, soft_purge}]} = release_handler_1:eval_script(S2, [], []), + check_bstate("first", [FirstBB]), + + false = code:is_loaded(b), + {error,{old_processes,b}} = release_handler_1:eval_script(S2,[],[]), + check_bstate("first", [FirstBB]), + + %% Let supervisor restart bb with new code + S3 = [point_of_no_return, + {purge, [b]}], + {ok, []} = release_handler_1:eval_script(S3, [], []), + ok = wait_for(bb), + check_bstate("second", []), + SecondBB = whereis(bb), + + if + SecondBB =:= FirstBB -> ?t:fail("bb not killed"); + true -> ok + end, + + %% Restart bb yet another time + ok = application:stop(c), + ok = application:start(c), + ok = wait_for(bb), + check_bstate("third", []), + ThirdBB = whereis(bb), + + case ThirdBB of + _ when is_pid(ThirdBB) -> ok; + undefined -> ?t:fail("bb not started") + end, + + %% Make bb run old version of b. + %%c:l(b), + check_bstate("third", []), + false = code:purge(b), + check_bstate("third", []), + {module,b} = code:load_file(b), + check_bstate("third", [ThirdBB]), + + %% Let supervisor restart bb yet another time + S4 = [point_of_no_return, + {remove, {b, brutal_purge, soft_purge}}], + {ok, HopefullyEmpty} = release_handler_1:eval_script(S4, [], []), + ok = wait_for(bb), + FourthBB = whereis(bb), + + case HopefullyEmpty of + [{b, soft_purge}] -> + %% The process managed to start between purge and delete + check_bstate("fourth", [FourthBB]); + [] -> + %% The process started after delete + check_bstate("fourth", []) + end, + + application:stop(c), + check_bstate("no", []), + ok. + +instructions(cleanup,Conf) -> + application:stop(c), + really_del_code([aa,b,c_sup]), + code:del_path(filename:join(?config(data_dir,Conf), "c")), + ok. + +really_del_code(Mods) -> + lists:foreach(fun(Mod) -> + code:purge(Mod), % remove old code + code:delete(Mod),% make current code old + code:purge(Mod) % remove old code + end, + Mods). + +check_bstate(Slogan,ExpectedProcs) -> + BB = whereis(bb), + ActualProcs = lists:sort([P || P <- processes(), + erlang:check_process_code(P, b)]), + ExpectedProcs2 = lists:sort(ExpectedProcs), + ?t:format("check_bstate:~n~p~n~p~n", + [{"bb process", Slogan, BB}, + {"Processes running old b code", ActualProcs}]), + if + Slogan =:= "no", BB =/= undefined -> + ?t:fail("instructions failed; process bb is running"); + Slogan =/= "no", BB =:= undefined -> + ?t:fail("instructions failed; process bb is not running"); + ExpectedProcs2 =:= [], ActualProcs =/= ExpectedProcs2 -> + ?t:fail("instructions failed; old b processes are running"); + ActualProcs =/= ExpectedProcs2 -> + ?t:fail("instructions failed; wrong number of old b processes are running"); + true -> + ok + end. + +wait_for(Name) -> + case whereis(Name) of + undefined -> + timer:sleep(100), + wait_for(Name); + Pid when is_pid(Pid) -> + ok + end. + +no_cc() -> + case whereis(cc) of + Pid when is_pid(Pid) -> ?t:fail("cc not stopped"); + _ -> ok + end. + + + +%%%----------------------------------------------------------------- +%%% Testing of reported bugs and other tickets. +%%%----------------------------------------------------------------- + +%%----------------------------------------------------------------- +%% Ticket: OTP-2740 +%% Slogan: vsn not numeric doesn't work so good in release_handling +%%----------------------------------------------------------------- +%% Test vsn. +otp_2740(Conf) -> + DataDir = ?config(data_dir, Conf), + Dir = filename:join(DataDir, "otp_2740"), + true = code:add_path(Dir), + + {module, vsn_numeric} = c:l(vsn_numeric), + {module, vsn_tuple} = c:l(vsn_tuple), + {module, vsn_list} = c:l(vsn_list), + {module, vsn_atom} = c:l(vsn_atom), + {module, vsn_string} = c:l(vsn_string), + + 231894 = release_handler_1:get_current_vsn(vsn_numeric), + {tuple,["of",terms]} = release_handler_1:get_current_vsn(vsn_tuple), + [list,"of",{some,terms}] = release_handler_1:get_current_vsn(vsn_list), + atom = release_handler_1:get_current_vsn(vsn_atom), + "a string" = release_handler_1:get_current_vsn(vsn_string), + + true = code:del_path(Dir), + ok. + +%%----------------------------------------------------------------- +%% Ticket: OTP-2760 +%% Slogan: when an application is removed from a node it is not unloaded +%%----------------------------------------------------------------- +%% Test that when an application is removed from a node it is also unloaded. +otp_2760(Conf) -> + PrivDir = priv_dir(Conf), + Dir = filename:join(PrivDir,"otp_2760"), + DataDir = ?config(data_dir,Conf), + LibDir = filename:join([DataDir,app1_app2,lib1]), + + Rel1 = create_and_install_fake_first_release(Dir,[{app1,"1.0",LibDir}]), + Rel2 = create_fake_upgrade_release(Dir,"after",[],{Rel1,Rel1,[LibDir]}), + Rel2Dir = filename:dirname(Rel2), + + %% Start a node with Rel1.boot and check that the app1 module is loaded + {ok, Node} = t_start_node(otp_2760, Rel1, []), + {file, _} = rpc:call(Node, code, is_loaded, [app1]), + + %% Execute the relup script and check that app1 is unloaded + {ok, [{"after", [{_Rel1Vsn, _Descr, Script}], _}]} = + file:consult(filename:join(Rel2Dir, "relup")), + {ok, []} = rpc:call(Node, release_handler_1, eval_script, + [Script, [], []]), + false = rpc:call(Node, code, is_loaded, [app1]), + + true = stop_node(Node), + ok. + +%% Test upgrade using other filesystem than the defined in OTP and +%% option {update_paths, true} +otp_5761(Conf) when is_list(Conf) -> + + %% In the following test case, the release upgrade is somewhat + %% simplified (since it is not this procedure in itself we want to + %% test, but that application code directories are set correctly.) + %% Existing Erlang release is used as base, instead of creating + %% a new one. + + %% Set some paths + PrivDir = priv_dir(Conf), + Dir = filename:join(PrivDir,"otp_5761"), + RelDir = filename:join(?config(data_dir, Conf), "app1_app2"), + LibDir1 = filename:join(RelDir, "lib1"), + LibDir2 = filename:join(RelDir, "lib2"), + + %% Create the releases + Rel1 = create_and_install_fake_first_release(Dir, + [{app1,"1.0",LibDir1}, + {app2,"1.0",LibDir1}]), + Rel2 = create_fake_upgrade_release(Dir, + "2", + [{app1,"2.0",LibDir2}, + {app2,"1.0",LibDir2}], + {Rel1,Rel1,[LibDir1]}), + Rel1Dir = filename:dirname(Rel1), + Rel2Dir = filename:dirname(Rel2), + + %% Start a slave node + {ok, Node} = t_start_node(otp_5761, Rel1, filename:join(Rel1Dir,"sys.config")), + + %% Bind some variable names that will be used in patternmatching below + App11Dir = filename:join([LibDir1, "app1-1.0"]), + App12Dir = filename:join([LibDir2, "app1-2.0"]), + App2aDir = filename:join([LibDir1, "app2-1.0"]), + App2bDir = filename:join([LibDir2, "app2-1.0"]), + + %% Make sure correct code paths are used + App11Dir = rpc:call(Node, code, lib_dir, [app1]), + App2aDir = rpc:call(Node, code, lib_dir, [app2]), + + %% Unpack rel2 (make sure it does not work if an AppDir is bad) + LibDir3 = filename:join(RelDir, "lib3"), + {error, {no_such_directory, _}} = + rpc:call(Node, release_handler, set_unpacked, + [Rel2++".rel", [{app1,"2.0",LibDir2}, {app2,"1.0",LibDir3}]]), + {ok, RelVsn2} = + rpc:call(Node, release_handler, set_unpacked, + [Rel2++".rel", [{app1,"2.0",LibDir2}, {app2,"1.0",LibDir2}]]), + ok = rpc:call(Node, release_handler, install_file, + [RelVsn2, filename:join(Rel2Dir, "relup")]), + ok = rpc:call(Node, release_handler, install_file, + [RelVsn2, filename:join(Rel2Dir, "start.boot")]), + ok = rpc:call(Node, release_handler, install_file, + [RelVsn2, filename:join(Rel2Dir, "sys.config")]), + + %% Install RelVsn2 without {update_paths, true} option + {ok, RelVsn1, []} = + rpc:call(Node, release_handler, install_release, [RelVsn2]), + App12Dir = rpc:call(Node, code, lib_dir, [app1]), + App2aDir = rpc:call(Node, code, lib_dir, [app2]), + + %% Install RelVsn1 again + {ok, RelVsn1, []} = + rpc:call(Node, release_handler, install_release, [RelVsn1]), + + %% Install RelVsn2 with {update_paths, true} option + {ok, RelVsn1, []} = + rpc:call(Node, release_handler, install_release, + [RelVsn2, [{update_paths, true}]]), + App12Dir = rpc:call(Node, code, lib_dir, [app1]), + App2bDir = rpc:call(Node, code, lib_dir, [app2]), + + %% Install RelVsn1 again + {ok, RelVsn1, []} = + rpc:call(Node, release_handler, install_release, + [RelVsn1, [{update_paths, true}]]), + App11Dir = rpc:call(Node, code, lib_dir, [app1]), + App2aDir = rpc:call(Node, code, lib_dir, [app2]), + + %% Stop the slave node + true = stop_node(Node), + ok. + +%% Test upgrade and downgrade of applications +eval_appup(Conf) when is_list(Conf) -> + + %% OTP-6162 + %% Create an ETS table which is updated by app1 if there is any + %% change made to the application configuration parameter 'var' + %% (see config_change/3 in myrel/lib1|2/app1-1|2.0/src/app1.erl) + ets:new(otp_6162, [set, public, named_table]), + + %% Set some paths + RelDir = filename:join(?config(data_dir, Conf), "app1_app2"), + App11Dir = filename:join([RelDir, "lib1", "app1-1.0"]), + App12Dir = filename:join([RelDir, "lib2", "app1-2.0"]), + EbinDir = filename:join(App11Dir, "ebin"), + + %% Start app1-1.0 + code:add_patha(EbinDir), + ok = application:start(app1), + App11Dir = code:lib_dir(app1), + ok = gen_server:call(harry, error), + + %% Upgrade to app1-2.0 + {ok, []} = release_handler:upgrade_app(app1, App12Dir), + App12Dir = code:lib_dir(app1), + error = gen_server:call(harry, error), + + %% OTP-6162 + %% Value of config parameter 'var' should now be 'val2' + %% (see myrel/lib2/app1-2.0/ebin/app1.app) + [{var,val2}] = ets:lookup(otp_6162, var), + + %% Downgrade to app1-1.0 + {ok, []} = release_handler:downgrade_app(app1,"1.0",App11Dir), + App11Dir = code:lib_dir(app1), + ok = gen_server:call(harry, error), + + %% OTP-6162 + %% Value of config parameter 'var' should now be 'val1' + %% (see myrel/lib1/app1-1.0/ebin/app1.app) + [{var,val1}] = ets:lookup(otp_6162, var), + + ok = application:stop(app1), + ok = application:unload(app1), + + true = code:del_path(EbinDir), + ok. + + +%% Test the example/target_system.erl module +target_system(Conf) when is_list(Conf) -> + PrivDir = priv_dir(Conf), + DataDir = ?config(data_dir,Conf), + + TargetCreateDir = filename:join([PrivDir,"target_system","create"]), + TargetInstallDir = filename:join([PrivDir,"target_system","install"]), + + ok = filelib:ensure_dir(filename:join(TargetCreateDir,"xx")), + ok = filelib:ensure_dir(filename:join(TargetInstallDir,"xx")), + + + %% Create the .rel file + ErtsVsn = erlang:system_info(version), + RelName = filename:join(TargetCreateDir,"ts-1.0"), + RelFile = RelName++".rel", + RelVsn = "R1A", + create_rel_file(RelFile,RelName,RelVsn,ErtsVsn,[{a, "1.0"}]), + + %% Build the target_system module + ExamplesEbin = filename:join([code:lib_dir(sasl),examples,ebin]), + TSPath = + case filelib:is_file(filename:join(ExamplesEbin,"target_system.beam")) of + true -> + ExamplesEbin; + false -> + {ok,_} = + compile:file(filename:join(DataDir,"target_system.erl"), + [{outdir,TargetCreateDir}]), + TargetCreateDir + end, + code:add_path(TSPath), + + %% Create the release + target_system:create(RelName,[{path,[filename:join([DataDir, + lib, + "a-1.0", + ebin])]}]), + + %% Install the release + target_system:install(RelName,TargetInstallDir), + + code:del_path(TSPath), + + %% Check that all files exist in installation + true = filelib:is_dir(filename:join(TargetInstallDir,"erts-"++ErtsVsn)), + LibDir = filename:join(TargetInstallDir,lib), + {ok,KernelVsn} = application:get_key(kernel,vsn), + {ok,StdlibVsn} = application:get_key(stdlib,vsn), + {ok,SaslVsn} = application:get_key(sasl,vsn), + true = filelib:is_dir(filename:join(LibDir,"kernel-"++KernelVsn)), + true = filelib:is_dir(filename:join(LibDir,"stdlib-"++StdlibVsn)), + true = filelib:is_dir(filename:join(LibDir,"sasl-"++SaslVsn)), + true = filelib:is_dir(filename:join(LibDir,"a-1.0")), + RelDir = filename:join(TargetInstallDir,releases), + true = filelib:is_regular(filename:join(RelDir,"RELEASES")), + true = filelib:is_regular(filename:join(RelDir,"start_erl.data")), + true = filelib:is_regular(filename:join(RelDir, + filename:basename(RelFile))), + true = filelib:is_dir(filename:join(RelDir,RelVsn)), + true = filelib:is_regular(filename:join([RelDir,RelVsn,"start.boot"])), + BinDir = filename:join(TargetInstallDir,bin), + true = filelib:is_regular(filename:join(BinDir,"start.boot")), + true = filelib:is_regular(filename:join(BinDir,erl)), + true = filelib:is_regular(filename:join(BinDir,start_erl)), + true = filelib:is_regular(filename:join(BinDir,start)), + true = filelib:is_regular(filename:join(BinDir,epmd)), + true = filelib:is_regular(filename:join(BinDir,run_erl)), + true = filelib:is_regular(filename:join(BinDir,to_erl)), + + %% Check content of files + {ok,SED} = file:read_file(filename:join(RelDir,"start_erl.data")), + [ErtsVsn,RelVsn] = string:tokens(binary_to_list(SED),"\s\n"), + ok. + + + +%%%================================================================= +%%% Testing global groups. +%%%================================================================= + +%% This test case involves P1G and P1H with the sys.config as +%% specified in gg_config/1. The test case checks that the global +%% group information is correct before and after the upgrade and also +%% after terminating one of the nodes. The flow is as follows: +%% 1. Start all four nodes of global group gg1 with P1G +%% 2. Terminate one of the nodes, and upgrade the others to P1H. P1H +%% config adds to more nodes to the global group. +%% 3. Start the two remaining nodes with P1H +upgrade_gg(Conf) -> + [Gg1Sname,Gg2Sname,Gg3Sname,Gg4Sname,Gg5Sname,Gg6Sname] = + ?config(snames,Conf), + + %% start gg1, gg3, gg4, gg5 and check that global group info is ok + Nodes1 = [Gg1,Gg3,Gg4,Gg5] = + start_nodes(Conf,[Gg1Sname,Gg3Sname,Gg4Sname,Gg5Sname],"upgrade_gg"), + + %% Give some time to synch nodes, then check global group info. + timer:sleep(1000), + [check_gg_info(Node,Nodes1,[],Nodes1--[Node]) || Node <- Nodes1], + + %% register a process on each of the nodes + ok = rpc:call(Gg1, installer, reg_proc, [reg1]), + ok = rpc:call(Gg3, installer, reg_proc, [reg3]), + ok = rpc:call(Gg4, installer, reg_proc, [reg4]), + ok = rpc:call(Gg5, installer, reg_proc, [reg5]), + are_names_reg_gg(Gg1, [reg1, reg3, reg4, reg5]), + + %% Stop gg3, then upgrade gg1, gg4 and gg5 to P1H + ok = stop_nodes([Gg3]), + + ok = install_release_changed_gg(Gg1,"P1H"), + ok = install_release_changed_gg(Gg4,"P1H"), + ok = install_release_changed_gg(Gg5,"P1H"), + + %% Check global group info + Gg2 = node_name(Gg2Sname), + Gg6 = node_name(Gg6Sname), + Nodes2 = [Gg1,Gg4,Gg5], + [check_gg_info(Node,Nodes2,[Gg2,Gg6],Nodes2--[Node]) || Node <- Nodes2], + + %% start gg2 and gg6 + [Gg2,Gg6] = start_nodes(Conf,[Gg2Sname,Gg6Sname],"upgrade_gg start gg2/gg6"), + + %% reg proc on each of the nodes + ok = rpc:call(Gg2, installer, reg_proc, [reg2]), + ok = rpc:call(Gg6, installer, reg_proc, [reg6]), + are_names_reg_gg(Gg1, [reg1, reg2, reg4, reg5, reg6]), + + %% Check global group info + Nodes3 = [Gg1,Gg2,Gg4,Gg5,Gg6], + [check_gg_info(Node,Nodes3,[],Nodes3--[Node]) || Node <- Nodes3], + + ok. + +upgrade_gg(cleanup,Config) -> + Snames = ?config(snames,Config), + NodeNames = [node_name(Sname) || Sname <- Snames], + ok = stop_nodes(NodeNames). + + + + +%%%================================================================= +%%% Misceleaneous functions +%%%================================================================= +stop_nodes(Nodes) -> + ?t:format("Stopping nodes: ~p~n",[Nodes]), + Running = + lists:foldl(fun(Node,Acc) -> + Now = now(), + stop_cover(Node), + case rpc:call(Node,installer,stop,[Now]) of + {badrpc,nodedown} -> + Acc; + Other -> + ?t:format("Stop ~p(~p): ~p~n", + [Node,Now,Other]), + [Node|Acc] + end + end, [], Nodes), + wait_nodes_down(Running). + + +wait_nodes_down(Nodes) -> + ?t:format( "wait_nodes_down ~p:",[Nodes]), + wait_nodes_down(Nodes, 30). + +wait_nodes_down(Nodes, 0) -> + test_server:fail({error, {"could not kill nodes", Nodes}}); +wait_nodes_down(Nodes, N) -> + Fun = fun(Node, A) -> + case net_adm:ping(Node) of + pong -> + ?t:format( " net_adm:ping(~p) = pong", [Node]), + [Node|A]; + pang -> + ?t:format( " net_adm:ping(~p) = pang", [Node]), + A + end + end, + Pang = lists:foldl(Fun, [], Nodes), + case Pang of + [] -> + ?t:format("",[]), + ok; + _ -> + timer:sleep(1000), + wait_nodes_down(Pang, N-1) + end. + + + +wait_nodes_up(Nodes, Tag) -> + wait_nodes_up(Nodes, Tag, []). + +wait_nodes_up(Nodes0, Tag, Apps) -> + ?t:format("wait_nodes_up(~p, ~p, ~p):",[Nodes0, Tag, Apps]), + Nodes = fix_nodes(Nodes0), + wait_nodes_up(Nodes, Tag, lists:umerge(Apps,[kernel,stdlib,sasl]), 30). + +fix_nodes([{Node,InitPid}|Nodes]) -> + [{Node,InitPid} | fix_nodes(Nodes)]; +fix_nodes([Node|Nodes]) -> + [{Node,fake_init_pid} | fix_nodes(Nodes)]; +fix_nodes([]) -> + []. + +wait_nodes_up(Nodes, Tag, Apps, 0) -> + test_server:fail({error, {"nodes not started", Nodes, Tag, Apps}}); +wait_nodes_up(Nodes, Tag, Apps, N) -> + Fun = + fun(NodeInfo={Node,OldInitPid}, A) -> + case rpc:call(Node, application, which_applications, []) of + {badrpc, nodedown} -> + ?t:format( " ~p = {badarg, nodedown}",[Node]), + [NodeInfo | A]; + List when is_list(List)-> + ?t:format( " ~p = [~p]",[Node, List]), + case lists:all(fun(App) -> + lists:keymember(App,1,List) + end, Apps) of + true -> + case rpc:call(Node,erlang,whereis,[init]) of + OldInitPid -> + [NodeInfo | A]; + _ -> + start_cover(Node), + A + end; + false -> + [NodeInfo | A] + end + end + end, + Pang = lists:foldl(Fun,[],Nodes), + case Pang of + [] -> + ?t:format("",[]), + ok; + _ -> + timer:sleep(1000), + wait_nodes_up(Pang, Tag, Apps, N-1) + end. + + + + +are_names_reg_gg(Node, Names) -> + ?t:format( "are_names_reg_gg ~p~n",[Names]), + are_names_reg_gg(Node, Names, 30). + +are_names_reg_gg(Node, Names, N) -> + case lists:sort(rpc:call(Node, global, registered_names, [])) of + Names -> + ok; + Regs when N > 0 -> + timer:sleep(1000), + ?t:format( "are_names_reg_gg Regs ~p~n",[Regs]), + are_names_reg_gg(Node, Names, N-1); + Regs -> + ?t:fail({error, {"Names not registered", + {{"should :", Names}, + {"was :", Regs}}}}) + end. + + + +t_start_node(Name, Boot, SysConfig) -> + Args = + case Boot of + [] -> []; + _ -> " -boot " ++ Boot + end ++ + case SysConfig of + [] -> []; + _ -> " -config " ++ SysConfig + end, + test_server:start_node(Name, slave, [{args, Args}]). + +stop_node(Node) -> + ?t:stop_node(Node). + + +copy_client(Conf,Master,Sname,StartScript) -> + io:format("copy_client(Conf)"), + + DataDir = ?config(data_dir, Conf), + MasterDir = filename:join(priv_dir(Conf),Master), + + {ok,Host} = inet:gethostname(), + {ok,IpTuple} = inet:getaddr(Host,inet), + IpAddr = inet_parse:ntoa(IpTuple), + + CliNode = node_name(Sname), + + Cli = filename:join([MasterDir, "clients", "type1", CliNode]), + ok = filelib:ensure_dir(filename:join([Cli,"bin","."])), + ok = filelib:ensure_dir(filename:join([Cli,"releases","."])), + ok = filelib:ensure_dir(filename:join([Cli,"log","."])), + + P1GOrig = filename:join([MasterDir, "releases", "P1G"]), + ok = copy_tree(Conf,P1GOrig,filename:join(Cli,"releases")), + + ok = subst_file(filename:join([DataDir, "clients", StartScript]), + filename:join([Cli,"bin","start"]), + [{"ROOT",MasterDir}, + {"MASTER",atom_to_list(Master)}, + {"IPADDR",IpAddr}], + [{chmod,8#0755}]), + + StartErlData = filename:join([MasterDir, "releases", "start_erl.data"]), + CliRelDir = filename:join([Cli, "releases"]), + copy_file(StartErlData, CliRelDir), + + RR = filename:join([MasterDir, "releases", "RELEASES"]), + copy_file(RR, CliRelDir), + + ok. + + +delete_release(Conf) -> + PrivDir = priv_dir(Conf), + + {ok, OrigWd} = file:get_cwd(), + + ok = file:set_cwd(PrivDir), + ?t:format("======== current dir ~p~n",[PrivDir]), + {ok, Dirs} = file:list_dir(PrivDir), + ?t:format("======== deleting ~p~n",[Dirs]), + + ok = delete_release_os(Dirs), + ?t:format("======== remaining ~p~n",[file:list_dir(PrivDir)]), + ok = file:set_cwd(OrigWd), + ok. + + +delete_release_os(Dirs) -> + case os:type() of + {unix, _} -> + delete_release_unix(Dirs); + {win32, _} -> + delete_release_win32(Dirs); + Os -> + test_server:fail({error, {not_yet_implemented_os, Os}}) + end. + + +delete_release_unix([]) -> + ok; +delete_release_unix(["save"|Dirs]) -> + delete_release_unix(Dirs); +delete_release_unix([Dir|Dirs]) -> + Rm = string:concat("rm -rf ", Dir), + ?t:format("============== COMMAND ~p~n",[Rm]), + case file:list_dir(Dir) of + {error, enotdir} -> + ok; + X -> + ?t:format("------- Dir ~p~n ~p~n",[Dir, X]) + end, + case os:cmd(Rm) of + [] -> + ?t:format("------- Result of COMMAND ~p~n",[ok]); + Y -> + ?t:format("!!!!!!! delete ERROR Dir ~p Error ~p~n",[Dir, Y]), + ?t:format("------- ls -al ~p~n",[os:cmd("ls -al " ++ Dir)]) + end, + + delete_release_unix(Dirs). + +delete_release_win32([]) -> + ok; +delete_release_win32(["save"|Dirs]) -> + delete_release_win32(Dirs); +delete_release_win32([Dir|Dirs]) -> + Rm = string:concat("rmdir /s ", Dir), + [] = os:cmd(Rm), + delete_release_win32(Dirs). + + +node_name(Sname) when is_atom(Sname) -> + {ok,Host} = inet:gethostname(), + list_to_atom(atom_to_list(Sname) ++ "@" ++ Host). + +copy_file(Src, Dest) -> + copy_file(Src, Dest, []). +copy_file(Src, Dest, Opts) -> + case file:copy(Src,Dest) of + {ok,_} -> + preserve(Src,Dest,Opts), + chmod(Dest,Opts), + ok; + {error,eisdir} -> + NewDest = filename:join(Dest, filename:basename(Src)), + case file:copy(Src,NewDest) of + {ok,_} -> + preserve(Src,NewDest,Opts), + chmod(NewDest,Opts); + {error,Reason} -> + copy_error(Src,Dest,Reason) + end; + {error,Reason} -> + copy_error(Src,Dest,Reason) + end. + +preserve(Src,Dest,Opts) -> + case lists:member(preserve, Opts) of + true -> + {ok, FileInfo} = file:read_file_info(Src), + ok = file:write_file_info(Dest, FileInfo); + false -> + ok + end. + +chmod(Dest,Opts) -> + case lists:keyfind(chmod,1,Opts) of + {chmod,Mode} -> + ok = file:change_mode(Dest, Mode); + false -> + ok + end. + + + +copy_error(Src, Dest, Reason) -> + io:format("Copy ~s to ~s failed: ~s\n", + [Src,Dest,file:format_error(Reason)]), + ?t:fail(file_copy_failed). + +copy_tree(Conf, Src, DestDir) -> + case catch copy_tree(Conf, Src, filename:basename(Src), DestDir) of + ok -> + ok; + {'EXIT', {{badmatch,Error},_Stack}} -> + %% Most probably, an erl_tar call has failed. + %% Known to happen on some platforms (symbolic_link_too_long) + Error; + {'EXIT', Reason} -> + {error, Reason} + end. + +copy_tree(Conf, Src, NewName, DestDir) -> + PrivDir = priv_dir(Conf), + TempTarName = filename:join(PrivDir, "temp_tar_file.tar"), + %% Not compressing tar file here since that would increase test + %% suite time by almost 100%, and the tar file is deleted + %% imediately anyway. + {ok,Tar} = erl_tar:open(TempTarName, [write]), + ok = erl_tar:add(Tar, Src, NewName, []), + ok = erl_tar:close(Tar), + ok = erl_tar:extract(TempTarName, [{cwd,DestDir}]), + ok = file:delete(TempTarName), + ok. + +%% subst_file(Src, Dest, Vars) +%% Src = Dest = string(), filename and path +%% Vars = [{Var, Val}] +%% Var = Val = string() +%% Substitute all occurrences of %Var% for Val in Src, using the list +%% of variables in Vars. Result is written to Dest. +%% +subst_file(Src, Dest, Vars) -> + subst_file(Src, Dest, Vars, []). +subst_file(Src, Dest, Vars, Opts) -> + {ok, Bin} = file:read_file(Src), + Conts = binary_to_list(Bin), + NConts = subst(Conts, Vars), + ok = file:write_file(Dest, NConts), + preserve(Src,Dest,Opts), + chmod(Dest,Opts). + +subst(Str, Vars) -> + subst(Str, Vars, []). + +subst([$%, C| Rest], Vars, Result) when $A =< C, C =< $Z -> + subst_var([C| Rest], Vars, Result, []); +subst([$%, C| Rest], Vars, Result) when $a =< C, C =< $z -> + subst_var([C| Rest], Vars, Result, []); +subst([$%, C| Rest], Vars, Result) when C == $_ -> + subst_var([C| Rest], Vars, Result, []); +subst([C| Rest], Vars, Result) -> + subst(Rest, Vars, [C| Result]); +subst([], _Vars, Result) -> + lists:reverse(Result). + +subst_var([$%| Rest], Vars, Result, VarAcc) -> + Key = lists:reverse(VarAcc), + case lists:keysearch(Key, 1, Vars) of + {value, {Key, Value}} -> + subst(Rest, Vars, lists:reverse(Value, Result)); + false -> + subst(Rest, Vars, [$%| VarAcc ++ [$%| Result]]) + end; +subst_var([C| Rest], Vars, Result, VarAcc) -> + subst_var(Rest, Vars, Result, [C| VarAcc]); +subst_var([], Vars, Result, VarAcc) -> + subst([], Vars, [VarAcc ++ [$%| Result]]). + + +priv_dir(Conf) -> + filename:absname(?config(priv_dir, Conf)). % Get rid of trailing slash + +latest_version(Dir) -> + List = filelib:wildcard(Dir ++ "*"), + lists:last(lists:sort(List)). + +%% A printer process which receives messages from other nodes and +%% prints in the log +reg_print_proc() -> + catch unregister(rh_print), + Pid = spawn_link(?MODULE, rh_print, []), + register(rh_print, Pid), + ok. + +rh_print() -> + receive + {print, {Module,Line}, [H|T]} -> + ?t:format("=== ~p:~p - ~p",[Module,Line,H]), + lists:foreach(fun(Term) -> ?t:format(" ~p",[Term]) end, T), + ?t:format("",[]), + rh_print(); + kill -> + exit(normal) + end. + +stop_print_proc() -> + case whereis(rh_print) of %%removes the printer process + undefined -> + ok; + Pid when is_pid(Pid) -> + rh_print ! kill + end. + +%% Create the first target release, vsn P1G. This release is used for +%% all test cases in {group,release} +create_p1g(Conf,Sname) -> + do_create_p1g(Conf,filename:join(priv_dir(Conf),Sname)). + +do_create_p1g(Conf,TargetDir) -> + PrivDir = priv_dir(Conf), + DataDir = ?config(data_dir,Conf), + ErtsVsn = "4.4", + ErtsDir = "erts-"++ErtsVsn, + + %% Create dirs + BinDir = filename:join(TargetDir,bin), + ReleasesDir = filename:join(TargetDir,releases), + LogDir = filename:join(TargetDir,log), + ok = filelib:ensure_dir(filename:join(BinDir,"*")), + ok = filelib:ensure_dir(filename:join(ReleasesDir,"*")), + ok = filelib:ensure_dir(filename:join(LogDir,"*")), + + %% Copy stuff + ErtsLatest = latest_version(filename:join(code:root_dir(),"erts")), + ok = copy_tree(Conf, ErtsLatest, ErtsDir, TargetDir), + ErtsBinDir = filename:join([TargetDir,ErtsDir,bin]), + copy_file(filename:join([ErtsBinDir, "epmd"]), BinDir, [preserve]), + copy_file(filename:join([ErtsBinDir, "run_erl"]), BinDir, [preserve]), + copy_file(filename:join([ErtsBinDir, "to_erl"]), BinDir, [preserve]), + + copy_file(filename:join(DataDir, "../installer.beam"), + filename:join([DataDir,lib,"installer-1.0",ebin])), + + %% Create .rel, .script and .boot files + RelName = "rel0", + RelVsn = "P1G", + RelDir = filename:join(PrivDir,RelName), + RelFileName = filename:join(RelDir,RelName), + RelFile = RelFileName ++ ".rel", + ok = filelib:ensure_dir(RelFile), + LibPath = filename:join([DataDir,lib,"*",ebin]), + + TarFile = create_basic_release(RelFile, RelVsn, {ErtsVsn,false}, + LibPath, [], [], [], []), + + %% Extract tar file in target directory (i.e. same directory as erts etc.) + ok = erl_tar:extract(TarFile, [{cwd, TargetDir}, compressed]), + + %% Create start_erl.data + StartErlDataFile = filename:join([ReleasesDir, "start_erl.data"]), + StartErlData = io_lib:fwrite("~s ~s~n", [ErtsVsn, RelVsn]), + ok = file:write_file(StartErlDataFile, StartErlData), + + %% Create RELEASES + ok = release_handler:create_RELEASES(TargetDir,ReleasesDir,RelFile,[]), + + %% Create start_erl + ok = subst_file(filename:join([ErtsBinDir,"start_erl.src"]), + filename:join([BinDir,"start_erl"]), + [{"EMU","beam"}], + [{chmod,8#0755}]), + + %% Create start script + %% Using a customized start script from DataDir where some options + %% (heart and nodename) are added compared to the start.src in the + %% erlang distribution. + ok = subst_file(filename:join(DataDir, "start"), + filename:join([BinDir, "start"]), + [{"ROOT",TargetDir}], + [preserve]), + ok. + +%% Create version P1H - which is P1G + a-1.0 +%% Must have run create_p1g first!! +create_p1h(Conf) -> + create_upgrade_release(Conf,"rel1","P1H",{"4.4",false},[{a,"1.0"}], + [{a,[{key2,val2}]}],{"rel0",[new_appl]}). + +%% Create version P1I - which is P1H, but with application a upgraded to a-1.1 +%% Must have run create_p1h first!! +create_p1i(Conf) -> + create_upgrade_release(Conf,"rel2","P1I",{"4.4",false},[{a,"1.1"}], + [{a,[{key2,newval2}]}], + {"rel1",[{extra,gott}]}). + +%% Create version P2A - which is P1I, but with erts-<latest> +%% Must have run create_p1i first!! +create_p2a(Conf) -> + ErtsVsn = erlang:system_info(version), + create_upgrade_release(Conf,"rel3","P2A",{ErtsVsn,code:root_dir()}, + [{a,"1.1"}],[{a,[{key2,newval2}]}], + {"rel2",[new_emu]}). + +%% Create a release tar package which can be installed on top of P1G +create_upgrade_release(Conf,RelName,RelVsn,Erts,Apps,Config,{UpFromName,Descr}) -> + PrivDir = priv_dir(Conf), + DataDir = ?config(data_dir,Conf), + + RelDir = filename:join(PrivDir,RelName), + RelFileName = filename:join(RelDir,RelName), + RelFile = RelFileName ++ ".rel", + ok = filelib:ensure_dir(RelFile), + LibPath = filename:join([DataDir,lib,"*",ebin]), + + UpFrom = [{filename:join([PrivDir,UpFromName,UpFromName]),Descr}], + + create_basic_release(RelFile, RelVsn, Erts, LibPath, + Apps, Config, UpFrom, []), + ok. + +%% Create .rel, .script, .boot, sys.config and tar +create_basic_release(RelFile,RelVsn,{ErtsVsn,ErtsDir},LibPath,ExtraApps,Config,UpFrom,DownTo) -> + RelDir = filename:dirname(RelFile), + RelFileName = filename:rootname(RelFile), + + %% Create .rel file + create_installer_rel_file(RelFile,RelVsn,ErtsVsn,ExtraApps), + + %% Generate .script and .boot + ok = systools:make_script(RelFileName, + [{path,[LibPath]}, + {outdir,RelDir}]), + + %% Generate relup + ok = systools:make_relup(RelFileName,UpFrom,DownTo,[{path,[LibPath]}, + {outdir,RelDir}]), + + %% Create sys.config + ok = write_term_file(filename:join(RelDir,"sys.config"),Config), + + + %% Create tar file (i.e. collect all lib/app-*/* and system files) + ok = systools:make_tar(RelFileName, + [{path,[LibPath]}, + {outdir,RelDir} | + case ErtsDir of + false -> []; + _ -> [{erts,ErtsDir}] + end]), + + RelFileName ++ ".tar.gz". + +%% Create a .rel file +create_installer_rel_file(RelFile,RelVsn,ErtsVsn,ExtraApps) -> + create_rel_file(RelFile,"SASL-test",RelVsn,ErtsVsn, + [{installer,"1.0"}|ExtraApps]). + +create_rel_file(RelFile,RelName,RelVsn,ErtsVsn,ExtraApps) -> + {ok,KernelVsn} = application:get_key(kernel,vsn), + {ok,StdlibVsn} = application:get_key(stdlib,vsn), + {ok,SaslVsn} = application:get_key(sasl,vsn), + application:load(tools), + {ok,ToolsVsn} = application:get_key(tools,vsn), + application:load(runtime_tools), + {ok,RuntimeToolsVsn} = application:get_key(runtime_tools,vsn), + + RelFileContent = {release, + {RelName, RelVsn}, + {erts, ErtsVsn}, + [{kernel, KernelVsn}, + {stdlib, StdlibVsn}, + {sasl, SaslVsn}, + {runtime_tools, RuntimeToolsVsn}, + {tools, ToolsVsn} | + ExtraApps]}, + ok = write_term_file(RelFile,RelFileContent). + +%% Insert a term in a file, which can be read with file:consult/1. +write_term_file(File,Term) -> + ok = file:write_file(File,io_lib:format("~p.~n",[Term])). + + +%% Check that global group info is correct +check_gg_info(Node,OtherAlive,OtherDead,Synced) -> + GGI = rpc:call(Node, global_group, info, []), + GI = rpc:call(Node, global, info,[]), + try do_check_gg_info(OtherAlive,OtherDead,Synced,GGI,GI) + catch _:E -> + ?t:format("~ncheck_gg_info failed for ~p: ~p~nwhen GGI was: ~p~n" + "and GI was: ~p~n", + [Node,E,GGI,GI]), + ?t:fail("check_gg_info failed") + end. + +do_check_gg_info(OtherAlive,OtherDead,Synced,GGI,GI) -> + {_,gg1} = lists:keyfind(own_group_name,1,GGI), + {_,synced} = lists:keyfind(state,1,GGI), + {_,AllNodes} = lists:keyfind(own_group_nodes,1,GGI), + true = lists:sort(AllNodes) =:= lists:sort(OtherAlive++OtherDead), + {_,[]} = lists:keyfind(sync_error,1,GGI), + {_,[{gg2,[_,_]}]} = lists:keyfind(other_groups,1,GGI), + + %% There is a known bug in global_group (OTP-9177) which causes + %% the following to fail every now and then: + %% {_,SyncedNodes} = lists:keyfind(synced_nodes,1,GGI), + %% true = lists:sort(SyncedNodes) =:= lists:sort(Synced), + %% {_,NoContact} = lists:keyfind(no_contact,1,GGI), + %% true = lists:sort(NoContact) =:= lists:sort(OtherDead), + + %% Therefore we use global:info instead for this part + {state,_,_,SyncedNodes,_,_,_,_,_,_,_} = GI, + true = lists:sort(SyncedNodes) =:= lists:sort(Synced), + + %% .. and we only check that all OtherDead are listed as + %% no_contact (due to th bug there might be more nodes in this + %% list) + {_,NoContact} = lists:keyfind(no_contact,1,GGI), + true = + lists:sort(OtherDead) =:= + lists:sort([NC || NC <- NoContact,lists:member(NC,OtherDead)]), + + ok. + +%% Return the configuration (to be inserted in sys.config) for global group tests +gg_config(Snames) -> + Nodes = [node_name(Sname) || Sname <- Snames], + [{kernel, [{sync_nodes_optional, Nodes}, + {sync_nodes_timeout, 10000}, + {global_groups, + [{gg1, Nodes}, + {gg2, [node_name(Sname) || Sname <- [ggq,ggw]]}]}]}, + {a, [{key2, val2}]}]. + +%% Start a node with short name SnameStr, and unpack P1H +unpack_p1h(Conf,Sname) -> + PrivDir = priv_dir(Conf), + [Node] = start_nodes(Conf,[Sname],"create_p1h"), + ok = rpc_inst(Node, unpack_p1h, [PrivDir]), + Node. + +%% On the given node, install P1H and make it permanent +%% This function is to be called after unpack_p1h/2, with the same node. +permanent_p1h(Node) -> + ok = rpc_inst(Node, permanent_p1h, []). + +%% For each node in ToNodes, create a target installation which is +%% indentical to the target installation for FromNode. +copy_installed(Conf,FromNode,ToNodes) -> + PrivDir = priv_dir(Conf), + DataDir = ?config(data_dir,Conf), + lists:foreach( + fun(Node) -> + ok = copy_tree(Conf,filename:join(PrivDir,FromNode),Node,PrivDir), + NodeDir = filename:join(PrivDir,Node), + ok = subst_file(filename:join(DataDir, "start"), + filename:join([NodeDir, "bin", "start"]), + [{"ROOT",NodeDir}]), + LogDir = filename:join(NodeDir,log), + {ok,Logs} = file:list_dir(LogDir), + lists:foreach(fun(Log) -> + file:delete(filename:join(LogDir,Log)) + end, + Logs) + end, + ToNodes). + +start_nodes(Conf,Snames,Tag) -> + PrivDir = priv_dir(Conf), + Nodes = + lists:map( + fun(Sname) -> + NodeDir = filename:join(PrivDir,Sname), + Node = node_name(Sname), + + Script = filename:join([NodeDir,"bin","start"]), + Cmd = "env NODENAME="++atom_to_list(Sname) ++ " " ++ Script, + %% {ok,StartFile} = file:read_file(Cmd), + %% io:format("~s:\n~s~n~n",[Start,binary_to_list(StartFile)]), + Res = os:cmd(Cmd), + io:format("Start ~p: ~p~n=>\t~p~n", [Sname,Cmd,Res]), + Node + end, + Snames), + wait_nodes_up(Nodes,Tag), + Nodes. + +tc_sname(Config) -> + tc_sname(Config,""). +tc_sname(Config,Fix) when is_atom(Fix) -> + tc_sname(Config,atom_to_list(Fix)); +tc_sname(Config,Fix) when is_list(Fix) -> + list_to_atom( + atom_to_list(?MODULE) + ++ "-" ++ atom_to_list(?config(sname_prefix, Config)) ++ + case Fix of + "" -> ""; + _ -> "-" ++ Fix + end). + +tc_full_node_name(Config) -> + tc_full_node_name(Config,""). +tc_full_node_name(Config,Fix) -> + node_name(tc_sname(Config,Fix)). + + +%% When installing a release for which the sys.config includes added +%% or changed global group(s), this node (test_sever@host) will be +%% disconnected from the test node (Node) by global_group.erl. This +%% will cause the rpc:call to terminate with {badrpc,nodedown} even if +%% the installation succeeds. This function installs the release, +%% accepts the faulty return value and then checks if the release was +%% successfully installed. +install_release_changed_gg(Node,RelVsn) -> + stop_cover(Node), + {badrpc,nodedown} = rpc:call(Node,release_handler,install_release,[RelVsn]), + timer:sleep(100), + wait_installed(Node,RelVsn,4). + +wait_installed(Node,RelVsn,0) -> + ?t:fail("install_release_changed_gg failed for " ++ RelVsn ++ + " on " ++ atom_to_list(Node)); +wait_installed(Node,RelVsn,N) -> + Rels = rpc:call(Node,release_handler,which_releases,[]), + case lists:keyfind(RelVsn, 2, Rels) of + {"SASL-test", RelVsn, _Libs, current} -> + start_cover(Node), + ok; + _ -> + timer:sleep(500), + wait_installed(Node,RelVsn,N-1) + end. + +%% Start/stop cover measurements on the given node +start_cover(Node) -> + cover_fun(Node,start). +stop_cover(Node) -> + cover_fun(Node,stop). + +cover_fun(Node,Func) -> + case ?t:is_cover() of + true -> + cover:Func(Node); + false -> + ok + end. + +%%%----------------------------------------------------------------- +%%% Create a fake release .... + +%% This function will create and install a release build on the +%% current running OTP release. It includes kernel, stdlib and sasl, +%% and possibly other applications if they are listed in AppDirs = +%% [{App,Vsn,LibDir}] +create_and_install_fake_first_release(Dir,AppDirs) -> + %% Create the first release + {RelName,RelVsn} = init:script_id(), + {Rel,_} = create_fake_release(Dir,RelName,RelVsn,AppDirs), + ReleasesDir = filename:join(Dir, "releases"), + RelDir = filename:dirname(Rel), + + %% And install it + RelVsnDir = filename:join(ReleasesDir, RelVsn), + ok = filelib:ensure_dir(filename:join(RelVsnDir,"*")), + + ok = copy_file(Rel++".rel",RelVsnDir), + ok = copy_file(Rel++".boot",filename:join(RelVsnDir, "start.boot")), + ok = copy_file(filename:join(RelDir,"sys.config"),RelVsnDir), + + ok = release_handler:create_RELEASES(code:root_dir(), + ReleasesDir, + Rel++".rel", + AppDirs), + + Rel. + +%% This function create a new release, including a relup file. It can +%% be upgraded to from the release created by +%% create_and_install_fake_first_release/2. Unpack first by calls to +%% release_handler:set_unpacked and release_handler:install_file. +create_fake_upgrade_release(Dir,RelVsn,AppDirs,{UpFrom,DownTo,ExtraLibs}) -> + %% Create a new release + {RelName,_} = init:script_id(), + {Rel,Paths} = create_fake_release(Dir,RelName,RelVsn,AppDirs), + RelDir = filename:dirname(Rel), + + %% And a relup file so it can be upgraded to + RelupPath = Paths ++ [filename:join([Lib,"*","ebin"]) || Lib <- ExtraLibs], + ok = systools:make_relup(Rel,[UpFrom],[DownTo], + [{path,RelupPath}, + {outdir,RelDir}]), + + Rel. + + +create_fake_release(Dir,RelName,RelVsn,AppDirs) -> + %% Create .rel files + RelDir = filename:join(Dir,"rel_" ++ RelVsn), + Rel = filename:join([RelDir,"rel_" ++ RelVsn]), + ok = filelib:ensure_dir(Rel), + ErtsVsn = erlang:system_info(version), + + {Apps,Paths} = + lists:foldl(fun({App,Vsn,Lib},{As,Ps}) -> + {[{App,Vsn}|As], + lists:umerge([filename:join([Lib,"*",ebin])],Ps)} + end, + {[],[]}, + AppDirs), + + create_rel_file(Rel++".rel",RelName,RelVsn,ErtsVsn,Apps), + + %% Generate boot scripts + ok = systools:make_script(Rel,[local, + {path, Paths}, + {outdir,RelDir}]), + ok = copy_file(Rel++".boot", filename:join(RelDir,"start.boot")), + + %% Use an own 'releases' directory - we don't want to change the + %% contents of $OTP_ROOT/releases + %% Inform SASL about this via sys.config + ReleasesDir = filename:join(Dir, "releases"), + Config = [{sasl,[{releases_dir,ReleasesDir}]}], + ok = write_term_file(filename:join(RelDir,"sys.config"), Config), + + {Rel,Paths}. + + +rpc_inst(Node,Func,Args) -> + rpc:call(Node,installer,Func,[node()|Args]). diff --git a/lib/sasl/test/release_handler_SUITE_data/Makefile.src b/lib/sasl/test/release_handler_SUITE_data/Makefile.src new file mode 100644 index 0000000000..85e25fdc2f --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/Makefile.src @@ -0,0 +1,108 @@ +EFLAGS=+debug_info + +P2B= \ + P2B/a-2.0/ebin/a.beam \ + P2B/a-2.0/ebin/a_sup.beam + +LIB= \ + lib/a-1.1/ebin/a.beam \ + lib/a-1.1/ebin/a_sup.beam \ + lib/a-1.0/ebin/a.beam \ + lib/a-1.0/ebin/a_sup.beam \ + +APP= \ + app1_app2/lib1/app1-1.0/ebin/app1_sup.@EMULATOR@ \ + app1_app2/lib1/app1-1.0/ebin/app1_server.@EMULATOR@ \ + app1_app2/lib1/app1-1.0/ebin/app1.@EMULATOR@ \ + app1_app2/lib1/app2-1.0/ebin/app2_sup.@EMULATOR@ \ + app1_app2/lib1/app2-1.0/ebin/app2_server.@EMULATOR@ \ + app1_app2/lib1/app2-1.0/ebin/app2.@EMULATOR@ \ + app1_app2/lib2/app1-2.0/ebin/app1_sup.@EMULATOR@ \ + app1_app2/lib2/app1-2.0/ebin/app1_server.@EMULATOR@ \ + app1_app2/lib2/app1-2.0/ebin/app1.@EMULATOR@ \ + app1_app2/lib2/app2-1.0/ebin/app2_sup.@EMULATOR@ \ + app1_app2/lib2/app2-1.0/ebin/app2_server.@EMULATOR@ \ + app1_app2/lib2/app2-1.0/ebin/app2.@EMULATOR@ + +OTP2740= \ + otp_2740/vsn_atom.@EMULATOR@ \ + otp_2740/vsn_list.@EMULATOR@ \ + otp_2740/vsn_numeric.@EMULATOR@ \ + otp_2740/vsn_tuple.@EMULATOR@ \ + otp_2740/vsn_string.@EMULATOR@ + +C= \ + c/aa.@EMULATOR@ \ + c/b.@EMULATOR@ \ + c/c_sup.@EMULATOR@ + + +all: $(P2B) $(LIB) $(APP) $(OTP2740) $(C) + +P2B/a-2.0/ebin/a.@EMULATOR@: P2B/a-2.0/src/a.erl + erlc $(EFLAGS) -oP2B/a-2.0/ebin P2B/a-2.0/src/a.erl +P2B/a-2.0/ebin/a_sup.@EMULATOR@: P2B/a-2.0/src/a_sup.erl + erlc $(EFLAGS) -oP2B/a-2.0/ebin P2B/a-2.0/src/a_sup.erl + + +lib/a-1.0/ebin/a.@EMULATOR@: lib/a-1.0/src/a.erl + erlc $(EFLAGS) -olib/a-1.0/ebin lib/a-1.0/src/a.erl +lib/a-1.0/ebin/a_sup.@EMULATOR@: lib/a-1.0/src/a_sup.erl + erlc $(EFLAGS) -olib/a-1.0/ebin lib/a-1.0/src/a_sup.erl + + +lib/a-1.1/ebin/a.@EMULATOR@: lib/a-1.1/src/a.erl + erlc $(EFLAGS) -olib/a-1.1/ebin lib/a-1.1/src/a.erl +lib/a-1.1/ebin/a_sup.@EMULATOR@: lib/a-1.1/src/a_sup.erl + erlc $(EFLAGS) -olib/a-1.1/ebin lib/a-1.1/src/a_sup.erl + + +app1_app2/lib1/app1-1.0/ebin/app1_sup.@EMULATOR@: app1_app2/lib1/app1-1.0/src/app1_sup.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app1-1.0/ebin app1_app2/lib1/app1-1.0/src/app1_sup.erl +app1_app2/lib1/app1-1.0/ebin/app1_server.@EMULATOR@: app1_app2/lib1/app1-1.0/src/app1_server.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app1-1.0/ebin app1_app2/lib1/app1-1.0/src/app1_server.erl +app1_app2/lib1/app1-1.0/ebin/app1.@EMULATOR@: app1_app2/lib1/app1-1.0/src/app1.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app1-1.0/ebin app1_app2/lib1/app1-1.0/src/app1.erl + + +app1_app2/lib1/app2-1.0/ebin/app2_sup.@EMULATOR@: app1_app2/lib1/app2-1.0/src/app2_sup.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app2-1.0/ebin app1_app2/lib1/app2-1.0/src/app2_sup.erl +app1_app2/lib1/app2-1.0/ebin/app2_server.@EMULATOR@: app1_app2/lib1/app2-1.0/src/app2_server.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app2-1.0/ebin app1_app2/lib1/app2-1.0/src/app2_server.erl +app1_app2/lib1/app2-1.0/ebin/app2.@EMULATOR@: app1_app2/lib1/app2-1.0/src/app2.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app2-1.0/ebin app1_app2/lib1/app2-1.0/src/app2.erl + + +app1_app2/lib2/app1-2.0/ebin/app1_sup.@EMULATOR@: app1_app2/lib2/app1-2.0/src/app1_sup.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app1-2.0/ebin app1_app2/lib2/app1-2.0/src/app1_sup.erl +app1_app2/lib2/app1-2.0/ebin/app1_server.@EMULATOR@: app1_app2/lib2/app1-2.0/src/app1_server.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app1-2.0/ebin app1_app2/lib2/app1-2.0/src/app1_server.erl +app1_app2/lib2/app1-2.0/ebin/app1.@EMULATOR@: app1_app2/lib2/app1-2.0/src/app1.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app1-2.0/ebin app1_app2/lib2/app1-2.0/src/app1.erl + + +app1_app2/lib2/app2-1.0/ebin/app2_sup.@EMULATOR@: app1_app2/lib2/app2-1.0/src/app2_sup.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app2-1.0/ebin app1_app2/lib2/app2-1.0/src/app2_sup.erl +app1_app2/lib2/app2-1.0/ebin/app2_server.@EMULATOR@: app1_app2/lib2/app2-1.0/src/app2_server.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app2-1.0/ebin app1_app2/lib2/app2-1.0/src/app2_server.erl +app1_app2/lib2/app2-1.0/ebin/app2.@EMULATOR@: app1_app2/lib2/app2-1.0/src/app2.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app2-1.0/ebin app1_app2/lib2/app2-1.0/src/app2.erl + + +otp_2740/vsn_atom.@EMULATOR@: otp_2740/vsn_atom.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_atom.erl +otp_2740/vsn_list.@EMULATOR@: otp_2740/vsn_list.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_list.erl +otp_2740/vsn_numeric.@EMULATOR@: otp_2740/vsn_numeric.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_numeric.erl +otp_2740/vsn_tuple.@EMULATOR@: otp_2740/vsn_tuple.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_tuple.erl +otp_2740/vsn_string.@EMULATOR@: otp_2740/vsn_string.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_string.erl + +c/aa.@EMULATOR@: c/aa.erl + erlc $(EFLAGS) -oc c/aa.erl +c/b.@EMULATOR@: c/b.erl + erlc $(EFLAGS) -oc c/b.erl +c/c_sup.@EMULATOR@: c/c_sup.erl + erlc $(EFLAGS) -oc c/c_sup.erl diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app new file mode 100644 index 0000000000..200cfcfe47 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app @@ -0,0 +1,8 @@ +{application, a, + [{description, "A CXC 138 11"}, + {vsn, "2.0"}, + {modules, [{a, 1}, {a_sup,1}]}, + {registered, [a_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {a_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl new file mode 100644 index 0000000000..cfe38b55ce --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl @@ -0,0 +1,47 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a). + + +-behaviour(gen_server). + +%% External exports +-export([start_link/0, a/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2]). + +start_link() -> gen_server:start_link({local, aa}, a, [], []). + +a() -> gen_server:call(aa, a). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, state}. + +handle_call(a, _From, State) -> + X = application:get_all_env(a), + {reply, X, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl @@ -0,0 +1,37 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, a_sup}, a_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {a, + {a, start_link, []}, + permanent, 2000, worker, [a]}, + {ok, {SupFlags, [Config]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/ebin/app1.app b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/ebin/app1.app new file mode 100644 index 0000000000..0489cb2595 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/ebin/app1.app @@ -0,0 +1,9 @@ +{application, app1, + [{description, "very simple example application"}, + {id, "app1"}, + {vsn, "1.0"}, + {modules, [app1, app1_sup, app1_server]}, + {registered, [harry]}, + {applications, [kernel, stdlib, sasl]}, + {env, [{var,val1}]}, + {mod, {app1, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1.erl new file mode 100644 index 0000000000..f123c8f470 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1.erl @@ -0,0 +1,22 @@ +-module(app1). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). +-export([config_change/3]). + +start(_Type, _StartArgs) -> + case app1_sup:start_link() of + {ok, Pid} -> + {ok, Pid}; + Error -> + Error + end. + +stop(_State) -> + ok. + +config_change(Changed, _New, _Removed) -> + catch ets:insert(otp_6162, hd(Changed)), + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_server.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_server.erl new file mode 100644 index 0000000000..9b49e772cc --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_server.erl @@ -0,0 +1,32 @@ +-module(app1_server). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +start_link() -> + gen_server:start_link({local, harry}, ?MODULE, [], []). + +init([]) -> + {ok, []}. + +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_sup.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_sup.erl new file mode 100644 index 0000000000..e6ad9b6967 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_sup.erl @@ -0,0 +1,17 @@ +-module(app1_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +init([]) -> + AChild = {harry,{app1_server,start_link,[]}, + permanent,2000,worker,[app1_server]}, + {ok,{{one_for_all,0,1}, [AChild]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/ebin/app2.app b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/ebin/app2.app new file mode 100644 index 0000000000..d48018cbda --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/ebin/app2.app @@ -0,0 +1,9 @@ +{application, app2, + [{description, "very simple example application"}, + {id, "app2"}, + {vsn, "1.0"}, + {modules, [app2, app2_sup, app2_server]}, + {registered, [ginny]}, + {applications, [kernel, stdlib, sasl]}, + {env, []}, + {mod, {app2, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2.erl new file mode 100644 index 0000000000..a41c39730c --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2.erl @@ -0,0 +1,17 @@ +-module(app2). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + +start(_Type, _StartArgs) -> + case app2_sup:start_link() of + {ok, Pid} -> + {ok, Pid}; + Error -> + Error + end. + +stop(_State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_server.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_server.erl new file mode 100644 index 0000000000..d8440230ff --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_server.erl @@ -0,0 +1,32 @@ +-module(app2_server). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +start_link() -> + gen_server:start_link({local, ginny}, ?MODULE, [], []). + +init([]) -> + {ok, []}. + +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_sup.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_sup.erl new file mode 100644 index 0000000000..80b0952d4b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_sup.erl @@ -0,0 +1,17 @@ +-module(app2_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +init([]) -> + AChild = {ginny,{app2_server,start_link,[]}, + permanent,2000,worker,[app2_server]}, + {ok,{{one_for_all,0,1}, [AChild]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.app b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.app new file mode 100644 index 0000000000..3c65adfbb3 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.app @@ -0,0 +1,9 @@ +{application, app1, + [{description, "very simple example application"}, + {id, "app1"}, + {vsn, "2.0"}, + {modules, [app1, app1_sup, app1_server]}, + {registered, [harry]}, + {applications, [kernel, stdlib, sasl]}, + {env, [{var,val2}]}, + {mod, {app1, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.appup b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.appup new file mode 100644 index 0000000000..e5e0cbda0e --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.appup @@ -0,0 +1,4 @@ +{"2.0", + [{"1.0", [{load_module, app1_server}]}], + [{"1.0", [{load_module, app1_server}]}] +}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1.erl new file mode 100644 index 0000000000..f123c8f470 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1.erl @@ -0,0 +1,22 @@ +-module(app1). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). +-export([config_change/3]). + +start(_Type, _StartArgs) -> + case app1_sup:start_link() of + {ok, Pid} -> + {ok, Pid}; + Error -> + Error + end. + +stop(_State) -> + ok. + +config_change(Changed, _New, _Removed) -> + catch ets:insert(otp_6162, hd(Changed)), + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_server.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_server.erl new file mode 100644 index 0000000000..660d095ebf --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_server.erl @@ -0,0 +1,35 @@ +-module(app1_server). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +start_link() -> + gen_server:start_link({local, harry}, ?MODULE, [], []). + +init([]) -> + {ok, []}. + +handle_call(error, _From, State) -> + Reply = error, + {reply, Reply, State}; +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_sup.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_sup.erl new file mode 100644 index 0000000000..e6ad9b6967 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_sup.erl @@ -0,0 +1,17 @@ +-module(app1_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +init([]) -> + AChild = {harry,{app1_server,start_link,[]}, + permanent,2000,worker,[app1_server]}, + {ok,{{one_for_all,0,1}, [AChild]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/ebin/app2.app b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/ebin/app2.app new file mode 100644 index 0000000000..d48018cbda --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/ebin/app2.app @@ -0,0 +1,9 @@ +{application, app2, + [{description, "very simple example application"}, + {id, "app2"}, + {vsn, "1.0"}, + {modules, [app2, app2_sup, app2_server]}, + {registered, [ginny]}, + {applications, [kernel, stdlib, sasl]}, + {env, []}, + {mod, {app2, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2.erl new file mode 100644 index 0000000000..a41c39730c --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2.erl @@ -0,0 +1,17 @@ +-module(app2). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + +start(_Type, _StartArgs) -> + case app2_sup:start_link() of + {ok, Pid} -> + {ok, Pid}; + Error -> + Error + end. + +stop(_State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_server.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_server.erl new file mode 100644 index 0000000000..d8440230ff --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_server.erl @@ -0,0 +1,32 @@ +-module(app2_server). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +start_link() -> + gen_server:start_link({local, ginny}, ?MODULE, [], []). + +init([]) -> + {ok, []}. + +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_sup.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_sup.erl new file mode 100644 index 0000000000..80b0952d4b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_sup.erl @@ -0,0 +1,17 @@ +-module(app2_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +init([]) -> + AChild = {ginny,{app2_server,start_link,[]}, + permanent,2000,worker,[app2_server]}, + {ok,{{one_for_all,0,1}, [AChild]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/c/aa.erl b/lib/sasl/test/release_handler_SUITE_data/c/aa.erl new file mode 100644 index 0000000000..1c853c85b2 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/c/aa.erl @@ -0,0 +1,41 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(aa). + + +-behaviour(gen_server). + +%% External exports +-export([start_link/0]). +%% Internal exports +-export([init/1, handle_info/2, terminate/2]). + +start_link() -> gen_server:start_link({local, cc}, aa, [], []). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, state}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/c/b.erl b/lib/sasl/test/release_handler_SUITE_data/c/b.erl new file mode 100644 index 0000000000..d8426a515e --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/c/b.erl @@ -0,0 +1,38 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(b). + + +%% External exports +-export([start_link/0]). +%% Internal exports +-export([init/0]). + +start_link() -> {ok, proc_lib:spawn_link(b, init, [])}. + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init() -> + register(bb, self()), + loop(). + +loop() -> + receive + hej -> ok + end. diff --git a/lib/sasl/test/release_handler_SUITE_data/c/c.app b/lib/sasl/test/release_handler_SUITE_data/c/c.app new file mode 100644 index 0000000000..908a94cf2d --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/c/c.app @@ -0,0 +1,8 @@ +{application, c, + [{description, "C CXC 138 11"}, + {vsn, "1.0"}, + {modules, [b, {aa, 1}, {c_sup,1}]}, + {registered, [cc,bb,c_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {c_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/c/c_sup.erl b/lib/sasl/test/release_handler_SUITE_data/c/c_sup.erl new file mode 100644 index 0000000000..069eb3b99b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/c/c_sup.erl @@ -0,0 +1,40 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(c_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, c_sup}, c_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config1 = {c, + {aa, start_link, []}, + permanent, 2000, worker, [aa]}, + Config2 = {b, + {b, start_link, []}, + permanent, 2000, worker, [b]}, + {ok, {SupFlags, [Config1, Config2]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/clients/start_cli1 b/lib/sasl/test/release_handler_SUITE_data/clients/start_cli1 new file mode 100755 index 0000000000..ee3d8c97cf --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/clients/start_cli1 @@ -0,0 +1,38 @@ +#!/bin/sh +# +# This program invokes the erlang emulator by calling run_erl. +# It should only be used at an embedded target system. +# It should be modified to give the correct flags to erl (via start_erl), +# e.g -mode embedded -sname XXX +# +# Usage: start [Data] +# + +if [ "x${NODENAME}" = "x" ] +then + echo "ERROR: Variable \$NODENAME is not set!!" + exit 1 +fi + +TESTHOST=`hostname | sed 's/[.].*//'` +IPADDR=%IPADDR% + +ROOTDIR=%ROOT% +CLIENTDIR=$ROOTDIR/clients/type1/$NODENAME@$TESTHOST + +RELDIR=$CLIENTDIR/releases + +# Note that this scripts is modified an copied to $CLIENTDIR/bin/start +# in release_handler_SUITE:copy_client - therefore HEART_COMMAND is as follows: +HEART_COMMAND=$CLIENTDIR/bin/start +HW_WD_DISABLE=true +export HW_WD_DISABLE HEART_COMMAND + +START_ERL_DATA=${1:-$RELDIR/start_erl.data} + +if [ ! -d /tmp/$NODENAME@$TESTHOST ] +then + mkdir /tmp/$NODENAME@$TESTHOST +fi + +$ROOTDIR/bin/run_erl /tmp/$NODENAME@$TESTHOST/ $CLIENTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA -heart -sname $NODENAME -sasl start_prg \\\"$CLIENTDIR/bin/start\\\" masters \[\\'%MASTER%@$TESTHOST\\'\] client_directory \\\"$CLIENTDIR\\\" -loader inet -id $NODENAME -hosts $IPADDR" > $CLIENTDIR/log/run_erl.out 2>&1 & diff --git a/lib/sasl/test/release_handler_SUITE_data/clients/start_cli2 b/lib/sasl/test/release_handler_SUITE_data/clients/start_cli2 new file mode 100755 index 0000000000..88912cf884 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/clients/start_cli2 @@ -0,0 +1,37 @@ +#!/bin/sh +# +# This program invokes the erlang emulator by calling run_erl. +# It should only be used at an embedded target system. +# It should be modified to give the correct flags to erl (via start_erl), +# e.g -mode embedded -sname XXX +# +# Usage: start [Data] +# + +if [ "x${NODENAME}" = "x" ] +then + echo "ERROR: Variable \$NODENAME is not set!!" + exit 1 +fi + +TESTHOST=`hostname | sed 's/[.].*//'` + +ROOTDIR=%ROOT% +CLIENTDIR=$ROOTDIR/clients/type1/$NODENAME@$TESTHOST + +RELDIR=$CLIENTDIR/releases + +# Note that this scripts is modified an copied to $CLIENTDIR/bin/start +# in release_handler_SUITE:copy_client - therefore HEART_COMMAND is as follows: +HEART_COMMAND=$CLIENTDIR/bin/start +HW_WD_DISABLE=true +export HW_WD_DISABLE HEART_COMMAND + +START_ERL_DATA=${1:-$RELDIR/start_erl.data} + +if [ ! -d /tmp/$NODENAME@$TESTHOST ] +then + mkdir /tmp/$NODENAME@$TESTHOST +fi + +$ROOTDIR/bin/run_erl /tmp/$NODENAME@$TESTHOST/ $CLIENTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA -heart -sname $NODENAME -sasl start_prg \\\"$CLIENTDIR/bin/start\\\" masters \[\\'%MASTER%@$TESTHOST\\',\\'master2@$TESTHOST\\'\] client_directory \\\"$CLIENTDIR\\\"" > /dev/null 2>&1 & diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app new file mode 100644 index 0000000000..e938137f67 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app @@ -0,0 +1,8 @@ +{application, a, + [{description, "A CXC 138 11"}, + {vsn, "1.0"}, + {modules, [{a, 1}, {a_sup,1}]}, + {registered, [a_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {a_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.app new file mode 100644 index 0000000000..e938137f67 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.app @@ -0,0 +1,8 @@ +{application, a, + [{description, "A CXC 138 11"}, + {vsn, "1.0"}, + {modules, [{a, 1}, {a_sup,1}]}, + {registered, [a_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {a_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.erl new file mode 100644 index 0000000000..bb500bed69 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.erl @@ -0,0 +1,49 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a). + + +-behaviour(gen_server). + +-vsn(1). + +%% External exports +-export([start_link/0, a/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2]). + +start_link() -> gen_server:start_link({local, aa}, a, [], []). + +a() -> gen_server:call(aa, a). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, state}. + +handle_call(a, _From, State) -> + X = application:get_all_env(a), + {reply, X, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a_sup.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a_sup.erl @@ -0,0 +1,37 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, a_sup}, a_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {a, + {a, start_link, []}, + permanent, 2000, worker, [a]}, + {ok, {SupFlags, [Config]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app new file mode 100644 index 0000000000..1c3053b2fa --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app @@ -0,0 +1,8 @@ +{application, a, + [{description, "A CXC 138 11"}, + {vsn, "1.1"}, + {modules, [{a, 2}, {a_sup,1}]}, + {registered, [a_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {a_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup new file mode 100644 index 0000000000..05db4cb541 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup @@ -0,0 +1,3 @@ +{"1.1", + [{"1.0",[{update,a,{advanced,extra_par}}]}], + []}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl new file mode 100644 index 0000000000..c082ad5339 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl @@ -0,0 +1,54 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a). + + +-behaviour(gen_server). + +%% External exports +-export([start_link/0, a/0, b/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3]). + +start_link() -> gen_server:start_link({local, aa}, a, [], []). + +a() -> gen_server:call(aa, a). +b() -> gen_server:call(aa, b). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, {state, bval}}. + +handle_call(a, _From, State) -> + X = application:get_all_env(a), + {reply, X, State}; + +handle_call(b, _From, State) -> + {reply, {ok, element(2, State)}, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(1, Extra, State) -> + {ok, {state, bval}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a_sup.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a_sup.erl @@ -0,0 +1,37 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, a_sup}, a_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {a, + {a, start_link, []}, + permanent, 2000, worker, [a]}, + {ok, {SupFlags, [Config]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/ebin/installer.app b/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/ebin/installer.app new file mode 100644 index 0000000000..6f77317f6a --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/ebin/installer.app @@ -0,0 +1,6 @@ +{application, installer, + [{description, "Installer application"}, + {vsn, "1.0"}, + {modules, [{installer, 1}]}, + {registered, []}, + {applications, [kernel, stdlib, sasl]}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/src/installer.erl b/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/src/installer.erl new file mode 120000 index 0000000000..c2f93b822d --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/src/installer.erl @@ -0,0 +1 @@ +../../../../installer.erl
\ No newline at end of file diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_atom.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_atom.erl new file mode 100644 index 0000000000..883688c231 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_atom.erl @@ -0,0 +1,26 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_atom). + +-vsn(atom). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_list.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_list.erl new file mode 100644 index 0000000000..34c38307ba --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_list.erl @@ -0,0 +1,26 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_list). + +-vsn([list, "of", {some, terms}]). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_numeric.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_numeric.erl new file mode 100644 index 0000000000..6bf52753fd --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_numeric.erl @@ -0,0 +1,26 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_numeric). + +-vsn(231894). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_string.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_string.erl new file mode 100644 index 0000000000..aa430a0bb3 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_string.erl @@ -0,0 +1,26 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_string). + +-vsn("a string"). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_tuple.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_tuple.erl new file mode 100644 index 0000000000..3ff1018994 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_tuple.erl @@ -0,0 +1,26 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_tuple). + +-vsn({tuple, ["of", terms]}). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/start b/lib/sasl/test/release_handler_SUITE_data/start new file mode 100755 index 0000000000..45e526c15f --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/start @@ -0,0 +1,29 @@ +#!/bin/sh +# +# This program invokes the erlang emulator by calling run_erl. +# It should only be used at an embedded target system. +# It should be modified to give the correct flags to erl (via start_erl), +# e.g -mode embedded -sname XXX +# +# Usage: start [Data] +# +ROOTDIR=%ROOT% + +if [ "x${NODENAME}" = "x" ] +then + echo "ERROR: Variable \$NODENAME is not set!!" + exit 1 +fi + +if [ -z "$RELDIR" ] +then + RELDIR=$ROOTDIR/releases +fi + +HEART_COMMAND=$ROOTDIR/bin/start +HW_WD_DISABLE=true +export HW_WD_DISABLE HEART_COMMAND + +START_ERL_DATA=${1:-$RELDIR/start_erl.data} + +$ROOTDIR/bin/run_erl /tmp/ $ROOTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA -heart -sname $NODENAME" > $ROOTDIR/log/run_erl.out 2>&1 & diff --git a/lib/sasl/test/release_handler_SUITE_data/target_system.erl b/lib/sasl/test/release_handler_SUITE_data/target_system.erl new file mode 120000 index 0000000000..4d36c59632 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/target_system.erl @@ -0,0 +1 @@ +../../examples/src/target_system.erl
\ No newline at end of file diff --git a/lib/sasl/test/sasl.cover b/lib/sasl/test/sasl.cover new file mode 100644 index 0000000000..d19d3d0180 --- /dev/null +++ b/lib/sasl/test/sasl.cover @@ -0,0 +1,2 @@ +{incl_app,sasl,details}. + diff --git a/lib/sasl/test/sasl.spec b/lib/sasl/test/sasl.spec new file mode 100644 index 0000000000..f3de90c9aa --- /dev/null +++ b/lib/sasl/test/sasl.spec @@ -0,0 +1 @@ +{suites,"../sasl_test",all}. diff --git a/lib/sasl/test/sasl_SUITE.erl b/lib/sasl/test/sasl_SUITE.erl new file mode 100644 index 0000000000..454095db6a --- /dev/null +++ b/lib/sasl/test/sasl_SUITE.erl @@ -0,0 +1,98 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. 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(sasl_SUITE). +-include_lib("common_test/include/ct.hrl"). + + +% Default timetrap timeout (set in init_per_testcase). +-define(default_timeout, ?t:minutes(1)). +-define(application, sasl). + +% Test server specific exports +-export([all/0,groups/0,init_per_group/2,end_per_group/2]). +-export([init_per_testcase/2, end_per_testcase/2]). + +% Test cases must be exported. +-export([app_test/1, + log_mf_h_env/1]). + +all() -> + [app_test, log_mf_h_env]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +init_per_testcase(_Case, Config) -> + ?line Dog=test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. +end_per_testcase(_Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +app_test(Config) when is_list(Config) -> + ?line ?t:app_test(sasl, allow), + ok. + +%% OTP-9185 - fail sasl start if some but not all log_mf_h env vars +%% are given. +log_mf_h_env(Config) -> + PrivDir = ?config(priv_dir,Config), + LogDir = filename:join(PrivDir,sasl_SUITE_log_dir), + ok = file:make_dir(LogDir), + application:stop(sasl), + SaslEnv = application:get_all_env(sasl), + lists:foreach(fun({E,_V}) -> application:unset_env(sasl,E) end, SaslEnv), + + ok = application:set_env(sasl,error_logger_mf_dir,LogDir), + match_error(missing_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_maxbytes,"xx"), + match_error(bad_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_maxbytes,50000), + match_error(missing_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_maxfiles,"xx"), + match_error(bad_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_maxfiles,2), + ok = application:unset_env(sasl,error_logger_mf_dir), + match_error(missing_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_dir,xx), + match_error(bad_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_dir,LogDir), + ok = application:start(sasl). + + +%%----------------------------------------------------------------- +%% Internal +match_error(Expected,{error,{bad_return,{_,{'EXIT',{Expected,{sasl,_}}}}}}) -> + ok; +match_error(Expected,Actual) -> + ?t:fail({unexpected_return,Expected,Actual}). diff --git a/lib/sasl/test/systools_SUITE.erl b/lib/sasl/test/systools_SUITE.erl new file mode 100644 index 0000000000..9190b111ef --- /dev/null +++ b/lib/sasl/test/systools_SUITE.erl @@ -0,0 +1,2112 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. 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 suite for the systools module. +%% +%% The systools module is a wrapper for a number of modules that +%% handle large parts of the release building functionality +%% (e.g. checking app files, building a tar file, building +%% release upgrad scripts. +%% + + +-module(systools_SUITE). + +%-define(debug, true). + +-include_lib("test_server/include/test_server.hrl"). +-define(format(S, A), ok). +-define(datadir, ?config(data_dir, Config)). +-define(privdir, ?config(priv_dir, Config)). +-define(copydir, ?config(copy_dir, Config)). + +-include_lib("kernel/include/file.hrl"). + +-export([all/0,suite/0,groups/0,init_per_group/2,end_per_group/2]). + +-export([ script_options/1, normal_script/1, no_mod_vsn_script/1, + wildcard_script/1, variable_script/1, + abnormal_script/1, src_tests_script/1, crazy_script/1, + warn_shadow_script/1, + included_script/1, included_override_script/1, + included_fail_script/1, included_bug_script/1, exref_script/1]). +-export([ tar_options/1, normal_tar/1, no_mod_vsn_tar/1, variable_tar/1, + src_tests_tar/1, shadow_tar/1, var_tar/1, + exref_tar/1, link_tar/1]). +-export([ normal_relup/1, abnormal_relup/1, no_appup_relup/1, + bad_appup_relup/1, app_start_type_relup/1, otp_3065/1]). +-export([ + otp_6226/1]). +-export([init_per_suite/1, end_per_suite/1, + init_per_testcase/2, end_per_testcase/2]). + +-import(lists, [foldl/3]). + +-define(default_timeout, ?t:minutes(20)). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [{group, script}, {group, tar}, {group, relup}, + {group, tickets}]. + +groups() -> + [{script, [], + [script_options, normal_script, no_mod_vsn_script, + wildcard_script, variable_script, abnormal_script, + src_tests_script, crazy_script, warn_shadow_script, + included_script, included_override_script, + included_fail_script, included_bug_script, exref_script, + otp_3065]}, + {tar, [], + [tar_options, normal_tar, no_mod_vsn_tar, variable_tar, + src_tests_tar, shadow_tar, var_tar, + exref_tar, link_tar]}, + {relup, [], + [normal_relup, abnormal_relup, no_appup_relup, + bad_appup_relup, app_start_type_relup]}, + {tickets, [], [otp_6226]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +init_per_suite(Config) when is_list(Config) -> + %% Make of copy of the data directory. + DataDir = ?datadir, + PrivDir = ?privdir, + ?line CopyDir = fname(PrivDir, "datacopy"), + ?line TarFile = fname(PrivDir, "datacopy.tgz"), + ?line {ok, Tar} = erl_tar:open(TarFile, [write, compressed]), + ?line ok = erl_tar:add(Tar, DataDir, CopyDir, [compressed]), + ?line ok = erl_tar:close(Tar), + ?line ok = erl_tar:extract(TarFile, [compressed]), + ?line ok = file:delete(TarFile), + + %% Compile source files in the copy directory. + ?line Sources = filelib:wildcard(fname([CopyDir,'*','*','*','*','*.erl'])), + ?line lists:foreach(fun compile_source/1, Sources), + + %% To use in end_per_testcase + Path = code:get_path(), + {ok,Cwd} = file:get_cwd(), + + [{copy_dir, CopyDir}, {cwd,Cwd}, {path,Path} | Config]. + +compile_source(File) -> + %% The compiler will no longer create a Beam file + %% with a module name that does not match the output + %% file, so we must compile to a binary and write + %% the output file ourselves. + U = filename:dirname(filename:dirname(File)), + Base = filename:rootname(filename:basename(File)), + OutFile = filename:join([U,"ebin",Base++".beam"]), + OutFileTemp = OutFile ++ "#", + {ok,_,Code} = compile:file(File, [binary]), + ok = file:write_file(OutFileTemp, Code), + file:rename(OutFileTemp, OutFile). + +end_per_suite(Conf) when is_list(Conf) -> + %% Nothing. + Conf. + +init_per_testcase(link_tar, Config) -> + case os:type() of + {unix, _} -> init_per_testcase(dummy, Config); + {win32, _} -> {skip, "Skip on windows"} + end; +init_per_testcase(_Case, Config) -> + ?line Dog = test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. + +end_per_testcase(_Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + case {?config(path,Config),?config(cwd,Config)} of + {undefined,undefined} -> + ok; + {Path,Cwd} -> + true = code:set_path(Path), + ok = file:set_cwd(Cwd) + end, + ok. + + + +%% Usage: +%% systools:make_script("RelName") +%% Make a boot file from RelName.rel. +%% Generates RelName.{script,boot} +%% systools:make_tar("RelName") +%% Make a release package from RelName.rel. +%% Generates RelName.tar,Z +%% systools:script2boot(File) +%% File.script -> File.boot +%% systools:make_relup("Target", ["UpFromRel"...], ["DownToRel"...], Opts) +%% Gather all appup scripts to the relup file +%% + + +%% make_script +%% +script_options(suite) -> []; +script_options(doc) -> + ["Check illegal script options."]; +script_options(Config) when is_list(Config) -> + ?line {'EXIT',{{badarg,[{path,["Path",12,"Another"]}]}, _}} = + (catch systools:make_script("release", [{path,["Path",12,"Another"]}])), + ?line {'EXIT',{{badarg,[sillent]}, _}} = + (catch systools:make_script("release", + [{path,["Path","Another"]},sillent])), + ?line {'EXIT',{{badarg,[locall]}, _}} = + (catch systools:make_script("release", + [{path,["Path","Another"]},locall])), + ?line {'EXIT',{{badarg,[src_testsxx]}, _}} = + (catch systools:make_script("release", + [{path,["Path"]},src_testsxx])), + ?line {'EXIT',{{badarg,[{variables, {"TEST", "/home/lib"}}]}, _}} = + (catch systools:make_script("release", + [{variables, {"TEST", "/home/lib"}}])), + ?line {'EXIT',{{badarg,[{variables, [{a, b}, {"a", "b"}]}]}, _}} = + (catch systools:make_script("release", + [{variables, [{a, b}, {"a", "b"}]}])), + ?line {'EXIT',{{badarg,[exreff]}, _}} = + (catch systools:make_script("release", + [{path,["Path","Another"]},exreff])), + ?line {'EXIT',{{badarg,[{exref,["appl"]}]}, _}} = + (catch systools:make_script("release", [{exref,["appl"]}])), + ?line {'EXIT',{{badarg,[{machine, "appl"}]}, _}} = + (catch systools:make_script("release", [{machine,"appl"}])), + ok. + + +%% make_script +%% +normal_script(suite) -> []; +normal_script(doc) -> + ["Check that make_script handles normal case."]; +normal_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P1 = fname([LibDir, 'db-2.1', ebin]), + ?line P2 = fname([LibDir, 'fe-3.1', ebin]), + + ?line true = code:add_patha(P1), + ?line true = code:add_patha(P2), + + ?line ok = file:set_cwd(LatestDir), + + ?line ok = systools:make_script(filename:basename(LatestName)), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% Check the same but w. silent flag + ?line {ok, _, []} = systools:make_script(LatestName, [silent]), + + %% Use the local option + ?line ok = systools:make_script(LatestName, [local]), + ?line ok = check_script_path(LatestName), + + %% use the path option + ?line code:set_path(PSAVE), % Restore path + %% Mess up std path: + ?line true = code:add_patha(fname([LibDir, 'db-1.0', ebin])), + ?line true = code:add_patha(fname([LibDir, 'fe-2.1', ebin])), + + ?line error = systools:make_script(LatestName), %should fail + ?line ok = systools:make_script(LatestName,[{path, [P1, P2]}]), + + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), % Restore path + ok. + + +%% make_script +%% +no_mod_vsn_script(suite) -> []; +no_mod_vsn_script(doc) -> + ["Check that make_script handles normal case.", + "Modules specified without version in .app file (db-3.1)." + "Note that this is now the normal way - i.e. systools now " + "ignores the module versions in the .app file."]; +no_mod_vsn_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest_no_mod_vsn,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P1 = fname([LibDir, 'db-3.1', ebin]), + ?line P2 = fname([LibDir, 'fe-3.1', ebin]), + + ?line true = code:add_patha(P1), + ?line true = code:add_patha(P2), + + ?line ok = file:set_cwd(LatestDir), + + ?line ok = systools:make_script(filename:basename(LatestName)), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% Check the same but w. silent flag + ?line {ok, _, []} = systools:make_script(LatestName, [silent]), + + %% Use the local option + ?line ok = systools:make_script(LatestName, [local]), + ?line ok = check_script_path(LatestName), + + %% use the path option + ?line code:set_path(PSAVE), % Restore path + %% Mess up std path: + ?line true = code:add_patha(fname([LibDir, 'db-1.0', ebin])), + ?line true = code:add_patha(fname([LibDir, 'fe-2.1', ebin])), + + ?line error = systools:make_script(LatestName), %should fail + ?line ok = systools:make_script(LatestName, + [{path, [P1, P2]}]), + + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), % Restore path + ok. + + +%% make_script +%% +wildcard_script(suite) -> []; +wildcard_script(doc) -> + ["Check that make_script handles wildcards in path."]; +wildcard_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line WildDir = fname([LibDir, '*', ebin]), + + ?line ok = file:set_cwd(LatestDir), + + ?line error = systools:make_script(filename:basename(LatestName)), + + ?line ok = systools:make_script(LatestName, + [{path, [WildDir]}]), + + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + ?line ok = file:set_cwd(OldDir), + ok. + + +%% make_script +%% +variable_script(suite) -> []; +variable_script(doc) -> + ["Add own installation dependent variable in script."]; +variable_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line ok = systools:make_script(LatestName, + [{path, P}, + {variables, [{"TEST", LibDir}]}]), + + %% Check variables + ?line ok = check_var_script_file([fname(['$TEST', 'db-2.1', ebin]), + fname(['$TEST', 'fe-3.1', ebin])], + P, + LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +abnormal_script(suite) -> []; +abnormal_script(doc) -> + ["Abnormal cases."]; +abnormal_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + + ?line ok = file:set_cwd(LatestDir), + ?line LibDir = fname([DataDir, d_bad_app_vsn, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + %% Check wrong app vsn + ?line error = systools:make_script(LatestName, [{path, P}]), + ?line {error, + systools_make, + [{error_reading, {db, {no_valid_version, + {{"should be","2.1"}, + {"found file", _, "2.0"}}}}}]} = + systools:make_script(LatestName, [silent, {path, P}]), + + ?line ok = file:set_cwd(OldDir), + ok. + + +%% make_script +%% +src_tests_script(suite) -> []; +src_tests_script(doc) -> + ["Do not check date of object file or that source code can be found."]; +src_tests_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_missing_src, lib]), + ?line P1 = fname([LibDir, 'db-2.1', ebin]), + ?line P2 = fname([LibDir, 'fe-3.1', ebin]), + N = [P1, P2], + + ?line ok = file:set_cwd(LatestDir), + + %% Manipulate the modification date of a beam file so it seems + %% older than its .erl file + ?line Erl = filename:join([P1,"..","src","db1.erl"]), + ?line {ok, FileInfo=#file_info{mtime={{Y,M,D},T}}} = file:read_file_info(Erl), + ?line Beam = filename:join(P1,"db1.beam"), + ?line ok=file:write_file_info(Beam, FileInfo#file_info{mtime={{Y-1,M,D},T}}), + + %% Remove a .erl file + ?line Erl2 = filename:join([P1,"..","src","db2.erl"]), + ?line file:delete(Erl2), + + %% Then make script - two warnings should be issued when + %% src_tests is given + %% 1. old object code for db1.beam + %% 2. missing source code for db2.beam + ?line {ok, _, [{warning,{obj_out_of_date,_}}, + {warning,{source_not_found,_}}]} = + systools:make_script(LatestName, [silent, {path, N}, src_tests]), + + %% Without the src_tests option, no warning should be issued + ?line {ok, _, []} = + systools:make_script(LatestName, [silent, {path, N}]), + + %% Check that the old no_module_tests option (from the time when + %% it was default to do the src_test) is ignored + ?line {ok, _, [{warning,{obj_out_of_date,_}}, + {warning,{source_not_found,_}}]} = + systools:make_script(LatestName, [silent, + {path, N}, + no_module_tests, + src_tests]), + + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), + ok. + +%% make_script +%% +warn_shadow_script(suite) -> []; +warn_shadow_script(doc) -> + ["Check that jam file out of date warning doesn't", + "shadow bad module version error."]; +warn_shadow_script(Config) when is_list(Config) -> + %% This test has been removed since the 'vsn' attribute is + %% not used any more, starting with R6. No warning + %% 'obj_out_of_date' seemed to be generated. + true. + + +%% make_script +%% +crazy_script(suite) -> []; +crazy_script(doc) -> + ["Do the crazy cases."]; +crazy_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest, Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + %% Run with bad path + ?line error = systools:make_script(LatestName), + ?line {error, _, [{error_reading, _}, {error_reading, _}]} = + systools:make_script(LatestName, [silent]), + + %% Run with .rel file lacking kernel + ?line {LatestDir2, LatestName2} = create_script(latest_nokernel, Config), + ?line ok = file:set_cwd(LatestDir2), + + ?line error = systools:make_script(LatestName2), + ?line {error, _, {missing_mandatory_app,[kernel,stdlib]}} = + systools:make_script(LatestName2, [silent,{path,P}]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +included_script(suite) -> []; +included_script(doc) -> + ["Check that make_script handles generation of script", + "for applications with included applications."]; +included_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(inc1, Config), + ?line ok = file:set_cwd(LatestDir), + ?line ok = systools:make_script(LatestName), + ?line ok = check_include_script(LatestName, + [t1, t2, t3, t5, t4, t6], + [t1, t3, t6]), + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +included_override_script(suite) -> []; +included_override_script(doc) -> + ["Check that make_script handles generation of script", + "for applications with included applications which are override by", + "the .rel file."]; +included_override_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(inc2, Config), + ?line ok = file:set_cwd(LatestDir), + ?line ok = systools:make_script(LatestName), + ?line ok = check_include_script(LatestName, + [t1, t2, t3, t4, t6, t5], + [t1, t3, t6, t5]), + + ?line {_, LatestName1} = create_include_files(inc3, Config), + ?line ok = systools:make_script(LatestName1), + ?line ok = check_include_script(LatestName1, + [t3, t5, t4, t6, t1, t2], + [t3, t6, t1, t2]), + + ?line {_, LatestName2} = create_include_files(inc4, Config), + ?line ok = systools:make_script(LatestName2), + ?line ok = check_include_script(LatestName2, + [t3, t4, t6, t5, t1, t2], + [t3, t6, t5, t1, t2]), + + ?line {_, LatestName3} = create_include_files(inc5, Config), + ?line ok = systools:make_script(LatestName3), + ?line ok = check_include_script(LatestName3, + [t3, t4, t6, t1, t2], + [t3, t6, t1, t2]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +included_fail_script(suite) -> []; +included_fail_script(doc) -> + ["Check that make_script handles errors then generating", + "script with included applications."]; +included_fail_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(inc6, Config), + ?line ok = file:set_cwd(LatestDir), + ?line {error, _, {undefined_applications,[t2]}} = + systools:make_script(LatestName, [silent]), + + ?line {_, LatestName1} = create_include_files(inc7, Config), + ?line {error, _, {duplicate_include,[{{t5,t7,_,_},{t5,t6,_,_}}]}} = + systools:make_script(LatestName1, [silent]), + + ?line {_, LatestName3} = create_include_files(inc9, Config), + ?line {error, _, {circular_dependencies,[{t10,_},{t8,_}]}} = + systools:make_script(LatestName3, [silent]), + + ?line {_, LatestName4} = create_include_files(inc10, Config), + ?line {error, _, [{error_reading,{t9,{override_include,[t7]}}}]} = + systools:make_script(LatestName4, [silent]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +included_bug_script(suite) -> []; +included_bug_script(doc) -> + ["Check that make_script handles generation of script", + "with difficult dependency for included applications."]; +included_bug_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(inc11, Config), + ?line ok = file:set_cwd(LatestDir), + ?line ok = systools:make_script(LatestName), + ?line ok = check_include_script(LatestName, + [t13, t11, t12], + [t11, t12]), + ?line ok = file:set_cwd(OldDir), + ok. + + +%% make_script +%% +otp_3065(suite) -> []; +otp_3065(doc) -> + ["Circular dependencies in systools:make_script()."]; +otp_3065(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(otp_3065, Config), + ?line ok = file:set_cwd(LatestDir), + ?line ok = systools:make_script(LatestName), + ?line ok = check_include_script(LatestName, + [aa12, chAts, chTraffic], + [chTraffic]), + ?line ok = file:set_cwd(OldDir), + ok. + + +%% make_script +%% +exref_script(suite) -> []; +exref_script(doc) -> + ["Check that make_script exref option works."]; +exref_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, [{path,P}, silent]), + + %% Complete exref + ?line {ok, _, W1} = + systools:make_script(LatestName, [exref, {path,P}, silent]), + ?line check_exref_warnings(with_db1, W1), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% Only exref the db application. + ?line {ok, _, W2} = + systools:make_script(LatestName, [{exref,[db]}, {path,P}, silent]), + ?line check_exref_warnings(with_db1, W2), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% Only exref the fe application. + ?line {ok, _, W3} = + systools:make_script(LatestName, [{exref,[fe]}, {path,P}, silent]), + ?line check_exref_warnings(without_db1, W3), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% exref the db and stdlib applications. + ?line {ok, _, W4} = + systools:make_script(LatestName, [{exref,[db,stdlib]}, {path,P}, silent]), + ?line check_exref_warnings(with_db1, W4), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), % Restore path + ok. + +check_exref_warnings(with_db1, W) -> + case get_exref(undef, W) of + {ok, [{db2,non_existing_func,0}, + {fe2,non_existing_func,0}, + {lists,non_existing_func,1}]} -> + ok; + {ok, L} -> + test_server:fail({exref_warning_undef, L}); + _E -> + test_server:fail({bad_undef,_E}) + end; +check_exref_warnings(without_db1, W) -> + case get_exref(undef, W) of + false -> + ok; + {ok, L} -> + test_server:fail({exref_warning_undef, L}) + end. + +get_exref(undef, W) -> filter(no_hipe(get_exref1(exref_undef, W))). + +filter(false) -> + false; +filter({ok, W}) -> + {ok, filter(W)}; +filter(L) -> + lists:filter(fun%({hipe_consttab,_,_}) -> false; + ({int,_,_}) -> false; + ({i,_,_}) -> false; + ({crypto,_,_}) -> false; + (_) -> true + end, + L). + +get_exref1(T, [{warning, {T, Value}}|_]) -> {ok, Value}; +get_exref1(T, [_|W]) -> get_exref1(T, W); +get_exref1(_, []) -> false. + +no_hipe(false) -> + false; +no_hipe({ok, Value}) -> + case erlang:system_info(hipe_architecture) of + undefined -> + Hipe = "hipe", + Fun = fun({M,_,_}) -> not lists:prefix(Hipe, atom_to_list(M)) end, + NewValue = lists:filter(Fun, Value), + {ok, NewValue}; + _Arch -> + {ok, Value} + end. + +%% tar_options +%% +tar_options(suite) -> []; +tar_options(doc) -> + ["Check illegal tar options."]; +tar_options(Config) when is_list(Config) -> + ?line {'EXIT',{{badarg,[{path,["Path",12,"Another"]}]}, _}} = + (catch systools:make_tar("release", [{path,["Path",12,"Another"]}])), + ?line {'EXIT',{{badarg,[sillent]}, _}} = + (catch systools:make_tar("release", + [{path,["Path","Another"]},sillent])), + ?line {'EXIT',{{badarg,[{dirs,["dirs"]}]}, _}} = + (catch systools:make_tar("release", [{dirs, ["dirs"]}])), + ?line {'EXIT',{{badarg,[{erts, illegal}]}, _}} = + (catch systools:make_tar("release", [{erts, illegal}])), + ?line {'EXIT',{{badarg,[src_testsxx]}, _}} = + (catch systools:make_tar("release", + [{path,["Path"]},src_testsxx])), + ?line {'EXIT',{{badarg,[{variables, [{a, b}, {"a", "b"}]}]}, _}} = + (catch systools:make_tar("release", + [{variables, [{a, b}, {"a", "b"}]}])), + ?line {'EXIT',{{badarg,[{var_tar, illegal}]}, _}} = + (catch systools:make_tar("release", [{var_tar, illegal}])), + ?line {'EXIT',{{badarg,[exreff]}, _}} = + (catch systools:make_tar("release", + [{path,["Path","Another"]},exreff])), + ?line {'EXIT',{{badarg,[{exref,["appl"]}]}, _}} = + (catch systools:make_tar("release", [{exref,["appl"]}])), + ?line {'EXIT',{{badarg,[{machine, "appl"}]}, _}} = + (catch systools:make_tar("release", [{machine,"appl"}])), + ok. + + +%% normal_tar +%% +normal_tar(suite) -> []; +normal_tar(doc) -> + ["Check normal case"]; +normal_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]), + ?line ok = systools:make_tar(LatestName, [{path, P}]), + ?line ok = check_tar(fname([lib,'db-2.1',ebin,'db.app']), LatestName), + ?line {ok, _, _} = systools:make_tar(LatestName, [{path, P}, silent]), + ?line ok = check_tar(fname([lib,'fe-3.1',ebin,'fe.app']), LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% no_mod_vsn_tar +%% +no_mod_vsn_tar(suite) -> []; +no_mod_vsn_tar(doc) -> + ["Check normal case", + "Modules specified without version in .app file (db-3.1)." + "Note that this is now the normal way - i.e. systools now " + "ignores the module versions in the .app file."]; +no_mod_vsn_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest_no_mod_vsn,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-3.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]), + ?line ok = systools:make_tar(LatestName, [{path, P}]), + ?line ok = check_tar(fname([lib,'db-3.1',ebin,'db.app']), LatestName), + ?line {ok, _, _} = systools:make_tar(LatestName, [{path, P}, silent]), + ?line ok = check_tar(fname([lib,'fe-3.1',ebin,'fe.app']), LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% variable_tar +%% +variable_tar(suite) -> []; +variable_tar(doc) -> + ["Use variable and create separate tar (included in generated tar)."]; +variable_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, + [silent, + {path, P}, + {variables,[{"TEST", LibDir}]}]), + + ?line ok = systools:make_tar(LatestName, [{path, P}, + {variables,[{"TEST", LibDir}]}]), + ?line ok = check_var_tar("TEST", LatestName), + + ?line {ok, _, _} = systools:make_tar(LatestName, + [{path, P}, silent, + {variables,[{"TEST", LibDir}]}]), + ?line ok = check_var_tar("TEST", LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% link_tar +%% +link_tar(suite) -> []; +link_tar(doc) -> + ["Check that symlinks in applications are handled correctly"]; +link_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_links, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + %% Make some links + ?line Db1Erl = fname(['db-2.1',src,'db1.erl']), + ?line NormalDb1Erl = fname([DataDir,d_normal,lib,Db1Erl]), + ?line LinkDb1Erl = fname([LibDir, Db1Erl]), + ?line ok = file:make_symlink(NormalDb1Erl, LinkDb1Erl), + ?line Db1Beam = fname(['db-2.1',ebin,'db1.beam']), + ?line NormalDb1Beam = fname([DataDir,d_normal,lib,Db1Beam]), + ?line LinkDb1Beam = fname([LibDir, Db1Beam]), + ?line ok = file:make_symlink(NormalDb1Beam, LinkDb1Beam), + ?line FeApp = fname(['fe-3.1',ebin,'fe.app']), + ?line NormalFeApp = fname([DataDir,d_normal,lib,FeApp]), + ?line LinkFeApp = fname([LibDir, FeApp]), + ?line ok = file:make_symlink(NormalFeApp, LinkFeApp), + + %% Create the tar and check that the linked files are included as + %% regular files + ?line ok = file:set_cwd(LatestDir), + + ?line {ok,_,[]} = systools:make_script(LatestName, [{path, P},silent]), + + ?line {ok, _, []} = systools:make_tar(LatestName, [{path, P}, silent]), + ?line ok = check_tar_regular(?privdir, + [fname([lib,FeApp]), + fname([lib,Db1Beam])], + LatestName), + + ?line {ok, _, []} = systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}]), + ?line ok = check_tar_regular(?privdir, + [fname([lib,FeApp]), + fname([lib,Db1Beam]), + fname([lib,Db1Erl])], + LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% src_tests_tar +%% +src_tests_tar(suite) -> []; +src_tests_tar(doc) -> + ["Do not check date of object file or that source code can be found."]; +src_tests_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_missing_src, lib]), + ?line P1 = fname([LibDir, 'db-2.1', ebin]), + ?line P2 = fname([LibDir, 'fe-3.1', ebin]), + P = [P1, P2], + + ?line ok = file:set_cwd(LatestDir), + + %% Manipulate the modification date of a beam file so it seems + %% older than the .erl file + Erl = filename:join([P1,"..","src","db1.erl"]), + {ok, FileInfo=#file_info{mtime={{Y,M,D},T}}} = file:read_file_info(Erl), + Beam = filename:join(P1,"db1.beam"), + ok = file:write_file_info(Beam, FileInfo#file_info{mtime={{Y-1,M,D},T}}), + + %% Remove a .erl file + ?line Erl2 = filename:join([P1,"..","src","db2.erl"]), + ?line file:delete(Erl2), + + ?line ok = systools:make_script(LatestName, [{path, P}]), + + %% Then make tar - two warnings should be issued when + %% src_tests is given + %% 1. old object code for db1.beam + %% 2. missing source code for db2.beam + ?line {ok, _, [{warning,{obj_out_of_date,_}}, + {warning,{source_not_found,_}}]} = + systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}, + src_tests]), + ?line ok = check_tar(fname([lib,'db-2.1',src,'db1.erl']), LatestName), + + %% Without the src_tests option, no warning should be issued + ?line {ok, _, []} = systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}]), + ?line ok = check_tar(fname([lib,'db-2.1',src,'db1.erl']), LatestName), + + %% Check that the old no_module_tests option (from the time when + %% it was default to do the src_test) is ignored + ?line {ok, _, [{warning,{obj_out_of_date,_}}, + {warning,{source_not_found,_}}]} = + systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}, + no_module_tests, + src_tests]), + ?line ok = check_tar(fname([lib,'db-2.1',src,'db1.erl']), LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% shadow_tar +%% +shadow_tar(suite) -> []; +shadow_tar(doc) -> + ["Check that jam file out of date warning doesn't", + "shadow bad module version error."]; +shadow_tar(Config) when is_list(Config) -> + % This test has been commented out since the 'vsn' attribute is not used + % any more, starting with R6. No warning 'obj_out_of_date' seemed to be + % generated. + true; +shadow_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, 'd_bad_mod+warn', lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {error, _, _} = systools:make_tar(LatestName, [{path, P}, silent]), + ?line {error, _, _} = systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}]), + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), + ok. + + +%% var_tar +%% +var_tar(suite) -> []; +var_tar(doc) -> + ["Check that make_tar handles generation and placement of tar", + "files for variables outside the main tar file.", + "Test the {var_tar, include | ownfile | omit} option."]; +var_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, + [silent, + {path, P}, + {variables,[{"TEST", LibDir}]}]), + + ?line ok = systools:make_tar(LatestName, [{path, P}, + {var_tar, ownfile}, + {variables,[{"TEST", LibDir}]}]), + + ?line true = exists_tar_file("TEST"), %% Also removes the file ! + ?line {error, {not_generated, _}} = check_var_tar("TEST", LatestName), + + ?line ok = systools:make_tar(LatestName, [{path, P}, + {var_tar, omit}, + {variables,[{"TEST", LibDir}]}]), + + ?line {error, {not_generated, _}} = check_var_tar("TEST", LatestName), + ?line false = exists_tar_file("TEST"), + + ?line ok = systools:make_tar(LatestName, [{path, P}, + {var_tar, include}, + {variables,[{"TEST", LibDir}]}]), + + ?line ok = check_var_tar("TEST", LatestName), + ?line false = exists_tar_file("TEST"), + + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), + ok. + + +%% exref_tar +%% +exref_tar(suite) -> []; +exref_tar(doc) -> + ["Check exref option."]; +exref_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]), + + %% Complete exref + ?line {ok, _, W1} = + systools:make_tar(LatestName, [exref, {path, P}, silent]), + ?line check_exref_warnings(with_db1, W1), + ?line ok = check_tar(fname([lib,'db-2.1',ebin,'db.app']), LatestName), + + %% Only exref the db application. + ?line {ok, _, W2} = + systools:make_tar(LatestName, [{exref, [db]}, {path, P}, silent]), + ?line check_exref_warnings(with_db1, W2), + ?line ok = check_tar(fname([lib,'fe-3.1',ebin,'fe.app']), LatestName), + + %% Only exref the fe application. + ?line {ok, _, W3} = + systools:make_tar(LatestName, [{exref, [fe]}, {path, P}, silent]), + ?line check_exref_warnings(without_db1, W3), + ?line ok = check_tar(fname([lib,'db-2.1',ebin,'db.app']), LatestName), + + %% exref the db and stdlib applications. + ?line {ok, _, W4} = + systools:make_tar(LatestName, [{exref, [db, stdlib]}, + {path, P}, silent]), + ?line check_exref_warnings(with_db1, W4), + ?line ok = check_tar(fname([lib,'fe-3.1',ebin,'fe.app']), LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% The relup stuff. +%% +%% + + +%% make_relup +%% +normal_relup(suite) -> []; +normal_relup(doc) -> + ["Check normal case"]; +normal_relup(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir,LatestName} = create_script(latest0,Config), + ?line {_LatestDir1,LatestName1} = create_script(latest1,Config), + ?line {_LatestDir2,LatestName2} = create_script(latest2,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = [fname([DataDir, d_normal, lib])], + ?line P = [fname([LibDir, '*', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + %% OTP-2561: Check that the option 'restart_emulator' generates a + %% "restart_new_emulator" instruction. + ?line {ok, _ , _, []} = + systools:make_relup(LatestName, [LatestName1], [LatestName1], + [{path, P},restart_emulator,silent]), + ?line ok = check_relup([{db, "2.1"}], [{db, "1.0"}]), + ?line ok = check_restart_emulator(), + + %% This is the ultra normal case + ?line ok = systools:make_relup(LatestName, [LatestName1], [LatestName1], + [{path, P}]), + ?line ok = check_relup([{db, "2.1"}], [{db, "1.0"}]), + ?line {ok, _, _, []} = + systools:make_relup(LatestName, [LatestName1], [LatestName1], + [{path, P}, silent]), + ?line ok = check_relup([{db, "2.1"}], [{db, "1.0"}]), + + %% Check that warnings get through + ?line ok = systools:make_relup(LatestName, [LatestName2], [LatestName1], + [{path, P}]), + ?line ok = check_relup([{fe, "3.1"}, {db, "2.1"}], [{db, "1.0"}]), + ?line {ok, _, _, [{erts_vsn_changed, _}]} = + systools:make_relup(LatestName, [LatestName2], [LatestName1], + [{path, P}, silent]), + ?line ok = check_relup([{fe, "3.1"}, {db, "2.1"}], [{db, "1.0"}]), + + ?line ok = file:set_cwd(OldDir), + ok. + + +%% This test fails if wrong version numbers are seen in the relup file +%% or if any application is missing. This was triggered by OTP-1360. +check_relup(UpVsnL, DnVsnL) -> + {ok, [{_V1, [{_, _, Up}], [{_, _, Dn}]}]} = file:consult(relup), + [] = foldl(fun(X, Acc) -> + true = lists:member(X, Acc), + lists:delete(X, Acc) end, + UpVsnL, + [{App, Vsn} || {load_object_code,{App,Vsn,_}} <- Up]), + [] = foldl(fun(X, Acc) -> + true = lists:member(X, Acc), + lists:delete(X, Acc) end, + DnVsnL, + [{App, Vsn} || {load_object_code,{App,Vsn,_}} <- Dn]), + ok. + +check_restart_emulator() -> + {ok, [{_V1, [{_, _, Up}], [{_, _, Dn}]}]} = file:consult(relup), + restart_new_emulator = lists:last(Up), + restart_new_emulator = lists:last(Dn), + ok. + +%% make_relup +%% +no_appup_relup(suite) -> []; +no_appup_relup(doc) -> + ["Check that appup files may be missing, but only if we don't need them."]; +no_appup_relup(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir,LatestName} = create_script(latest_small,Config), + ?line {_LatestDir0,LatestName0} = create_script(latest_small0,Config), + ?line {_LatestDir1,LatestName1} = create_script(latest_small1,Config), + + ?line DataDir = filename:absname(?copydir), + ?line P1 = [fname([DataDir, d_no_appup, lib, 'fe-3.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + %% Check that appup might be missing + ?line ok = + systools:make_relup(LatestName, [LatestName], [], [{path, P1}]), + ?line {ok,_, _, []} = + systools:make_relup(LatestName, [LatestName], [], + [silent, {path, P1}]), + + %% Check that appup might NOT be missing when we need it + ?line error = + systools:make_relup(LatestName, [LatestName0], [], [{path, P1}]), + ?line {error,_,{file_problem, {_,{error,{open,_,_}}}}} = + systools:make_relup(LatestName, [], [LatestName0], + [silent, {path, P1}]), + + %% Check that appups missing vsn traps + ?line P2 = [fname([DataDir, d_no_appup, lib, 'fe-2.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line error = + systools:make_relup(LatestName0, [LatestName1], [], [{path, P2}]), + ?line {error,_,{no_relup, _, _, _}} = + systools:make_relup(LatestName0, [], [LatestName1], + [silent, {path, P2}]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_relup +%% +bad_appup_relup(suite) -> []; +bad_appup_relup(doc) -> + ["Check that badly written appup files are detected"]; +bad_appup_relup(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir,LatestName} = create_script(latest_small,Config), + ?line {_LatestDir0,LatestName0} = create_script(latest_small0,Config), + + ?line DataDir = filename:absname(?copydir), + ?line N2 = [fname([DataDir, d_bad_appup, lib, 'fe-3.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + %% Check that bad appup is trapped + ?line error = + systools:make_relup(LatestName, [LatestName0], [], [{path, N2}]), + ?line {error,_,{file_problem, {_, {error, {parse,_, _}}}}} = + systools:make_relup(LatestName, [], [LatestName0], + [silent, {path, N2}]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_relup +%% +abnormal_relup(suite) -> []; +abnormal_relup(doc) -> + ["Check some abnormal cases"]; +abnormal_relup(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir,LatestName} = create_script(latest0,Config), + ?line {_LatestDir1,LatestName1} = create_script(latest1,Config), + + %% Check wrong app vsn + ?line DataDir = filename:absname(?copydir), + ?line P = [fname([DataDir, d_bad_app_vsn, lib, 'db-2.1', ebin]), + fname([DataDir, d_bad_app_vsn, lib, 'fe-3.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line error = systools:make_relup(LatestName, [LatestName1], [LatestName1], + [{path, P}]), + ?line R0 = systools:make_relup(LatestName, [LatestName1], [LatestName1], + [silent, {path, P}]), + ?line {error,systools_make, + [{error_reading,{db,{no_valid_version, + {{"should be","2.1"}, + {"found file", _, "2.0"}}}}}]} = R0, + ?line ok = file:set_cwd(OldDir), + ok. + + +%% Check that application start type is used in relup +app_start_type_relup(suite) -> + []; +app_start_type_relup(doc) -> + ["Release upgrade file with various application start types"]; +app_start_type_relup(Config) when is_list(Config) -> + ?line PrivDir = ?config(priv_dir, Config), + ?line {Dir1,Name1} = create_script(latest_app_start_type1,Config), + ?line {Dir2,Name2} = create_script(latest_app_start_type2,Config), + ?line Release1 = filename:join(Dir1,Name1), + ?line Release2 = filename:join(Dir2,Name2), + + ?line {ok, Release2Relup, systools_relup, []} = systools:make_relup(Release2, [Release1], [Release1], [{outdir, PrivDir}, silent]), + ?line {"2", [{"1",[], UpInstructions}], [{"1",[], DownInstructions}]} = Release2Relup, + %% ?t:format("Up: ~p",[UpInstructions]), + %% ?t:format("Dn: ~p",[DownInstructions]), + ?line [{load_object_code, {mnesia, _, _}}, + {load_object_code, {sasl, _, _}}, + {load_object_code, {webtool, _, _}}, + {load_object_code, {snmp, _, _}}, + {load_object_code, {xmerl, _, _}}, + point_of_no_return + | UpInstructionsT] = UpInstructions, + ?line true = lists:member({apply,{application,start,[mnesia,permanent]}}, UpInstructionsT), + ?line true = lists:member({apply,{application,start,[sasl,transient]}}, UpInstructionsT), + ?line true = lists:member({apply,{application,start,[webtool,temporary]}}, UpInstructionsT), + ?line true = lists:member({apply,{application,load,[snmp]}}, UpInstructionsT), + ?line false = lists:any(fun({apply,{application,_,[xmerl|_]}}) -> true; (_) -> false end, UpInstructionsT), + ?line [point_of_no_return | DownInstructionsT] = DownInstructions, + ?line true = lists:member({apply,{application,stop,[mnesia]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,stop,[sasl]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,stop,[webtool]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,stop,[snmp]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,stop,[xmerl]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[mnesia]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[sasl]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[webtool]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[snmp]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[xmerl]}}, DownInstructionsT), + ok. + + +otp_6226(suite) -> + []; +otp_6226(doc) -> + ["{outdir,Dir} option for systools:make_script()"]; +otp_6226(Config) when is_list(Config) -> + PrivDir = ?privdir, + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest0,Config), + ?line {_LatestDir, LatestName1} = create_script(latest1,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'db-1.0', ebin]), + fname([LibDir, 'fe-3.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + + %% Create an outdir1 directory + ?line ok = file:make_dir("outdir1"), + + %% ==== Now test systools:make_script ==== + %% a) badarg + ?line {'EXIT', {{badarg,[{outdir,outdir1}]}, _}} = + (catch systools:make_script(LatestName, [{outdir,outdir1}, + {path,P},silent])), + + %% b) absolute path + Outdir1 = filename:join(PrivDir, "outdir1"), + ?line {ok,_,[]} = systools:make_script(LatestName, [{outdir,Outdir1}, + {path,P},silent]), + ?line Script1 = filename:join(Outdir1, LatestName ++ ".script"), + ?line Boot1 = filename:join(Outdir1, LatestName ++ ".boot"), + ?line true = filelib:is_file(Script1), + ?line true = filelib:is_file(Boot1), + ?line ok = file:delete(Script1), + ?line ok = file:delete(Boot1), + + %% c) relative path + ?line {ok,_,[]} = systools:make_script(LatestName, [{outdir,"./outdir1"}, + {path,P},silent]), + ?line true = filelib:is_file(Script1), + ?line true = filelib:is_file(Boot1), + ?line ok = file:delete(Script1), + ?line ok = file:delete(Boot1), + + %% d) absolute but incorrect path + ?line Outdir2 = filename:join(PrivDir, "outdir2"), + ?line Script2 = filename:join(Outdir2, LatestName ++ ".script"), + ?line {error,_,{open,Script2,_}} = + systools:make_script(LatestName, [{outdir,Outdir2},{path,P},silent]), + + %% e) relative but incorrect path + ?line {error,_,{open,_,_}} = + systools:make_script(LatestName, [{outdir,"./outdir2"},{path,P},silent]), + + %% f) with .rel in another directory than cwd + ?line ok = file:set_cwd(Outdir1), + ?line {ok,_,[]} = systools:make_script(filename:join(PrivDir, LatestName), + [{outdir,"."},{path,P},silent]), + ?line true = filelib:is_file(LatestName ++ ".script"), + ?line true = filelib:is_file(LatestName ++ ".boot"), + ?line ok = file:delete(LatestName ++ ".script"), + ?line ok = file:delete(LatestName ++ ".boot"), + ?line ok = file:set_cwd(LatestDir), + + %% ==== Now test systools:make_tar ===== + ?line {ok,_,[]} = systools:make_script(LatestName, [{path,P},silent]), + %% a) badarg + ?line {'EXIT', {{badarg, [{outdir,outdir1}]}, _}} = + (catch systools:make_tar(LatestName,[{outdir,outdir1},{path,P},silent])), + + %% b) absolute path + ?line {ok,_,[]} = systools:make_tar(LatestName, [{outdir,Outdir1}, + {path,P},silent]), + ?line Tar1 = filename:join(Outdir1,LatestName++".tar.gz"), + ?line true = filelib:is_file(Tar1), + ?line ok = file:delete(Tar1), + + %% c) relative path + ?line {ok,_,[]} = systools:make_tar(LatestName, [{outdir,"./outdir1"}, + {path,P},silent]), + ?line true = filelib:is_file(Tar1), + ?line ok = file:delete(Tar1), + + %% d) absolute but incorrect path + ?line Tar2 = filename:join(Outdir2,LatestName++".tar.gz"), + ?line {error,_,{tar_error,{open,Tar2,{Tar2,enoent}}}} = + systools:make_tar(LatestName, [{outdir,Outdir2},{path,P},silent]), + + %% e) relative but incorrect path + ?line {error,_,{tar_error,{open,_,_}}} = + systools:make_tar(LatestName, [{outdir,"./outdir2"},{path,P},silent]), + + %% f) with .rel in another directory than cwd + ?line ok = file:set_cwd(Outdir1), + ?line {ok,_,[]} = systools:make_tar(filename:join(PrivDir, LatestName), + [{outdir,"."},{path,P},silent]), + ?line true = filelib:is_file(Tar1), + ?line ok = file:set_cwd(LatestDir), + + %% ===== Now test systools:make_relup ===== + %% a) badarg + ?line {'EXIT', {{badarg, [{outdir,outdir1}]}, _}} = + (catch systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,outdir1}, + {path,P},silent])), + + %% b) absolute path + Relup = filename:join(Outdir1, "relup"), + ?line {ok,_,_,[]} = systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,Outdir1}, + {path,P},silent]), + ?line true = filelib:is_file(Relup), + ?line ok = file:delete(Relup), + + %% c) relative path + ?line {ok,_,_,[]} = systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,"./outdir1"}, + {path,P},silent]), + ?line true = filelib:is_file(Relup), + ?line ok = file:delete(Relup), + + %% d) absolute but incorrect path + ?line {error,_,{file_problem,{"relup",enoent}}} = + systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,Outdir2},{path,P},silent]), + + %% e) relative but incorrect path + ?line {error,_,{file_problem,{"relup",enoent}}} = + systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,"./outdir2"},{path,P},silent]), + + %% f) with .rel in another directory than cwd + %% -- not necessary to test since relup by default is placed in + %% cwd, not in the same directory as the .rel file -- + + %% Change back to previous working directory + ?line ok = file:set_cwd(OldDir), + ok. + + +%%%%%% +%%%%%% Utilities +%%%%%% + +check_script_path(RelName) -> + {ok, [Conts]} = read_script_file(RelName), + {script, {_, _}, ListOfThings} = Conts, + case lists:keysearch(path, 1, ListOfThings) of + {value, {path, [$$,$R,$O,$O,$T | _]}} -> %"$ROOT..." + false; + _ -> ok + end. + +check_var_script_file(VarDirs, NoExistDirs, RelName) -> + {ok, [Conts]} = read_script_file(RelName), + {script, {_, _}, ListOfThings} = Conts, + AllPaths = lists:append(lists:map(fun({path, P}) -> P; + (_) -> [] + end, + ListOfThings)), + case lists:filter(fun(VarDir) -> lists:member(VarDir, AllPaths) end, + VarDirs) of + VarDirs -> + ok; + _ -> + test_server:fail("All variable dirs not in generated script") + end, + case lists:filter(fun(NoExistDir) -> lists:member(NoExistDir, AllPaths) end, + NoExistDirs) of + [] -> + ok; + _ -> + test_server:fail("Unexpected dirs in generated script") + end. + +check_include_script(RelName, ExpectedLoad, ExpectedStart) -> + {ok, [Conts]} = read_script_file(RelName), + {script, {_, _}, ListOfThings} = Conts, + + %% Check that the applications are loaded in given order ! + ActualLoad = + [App || {apply,{application,load,[{application,App,_}]}} <- ListOfThings, + App=/=kernel, + App=/=stdlib], + + if ActualLoad =:= ExpectedLoad -> ok; + true -> test_server:fail({bad_load_order, ActualLoad, ExpectedLoad}) + end, + + %% Check that applications are started in given order ! + ActualStart = + [App || {apply,{application,start_boot,[App|_]}} <- ListOfThings, + App =/= kernel, + App =/= stdlib], + + if ActualStart =:= ExpectedStart -> ok; + true -> test_server:fail({bad_start_order, ActualStart,ExpectedStart}) + end, + + ok. + +read_script_file(RelName) -> + file:consult(RelName ++ ".script"). + +check_var_tar(Variable, RelName) -> + Expected = tar_name(Variable), + case check_tar(Expected,RelName) of + ok -> + ok; + {error, {erroneous_tar_file, _, missing, _}} -> + {error, {not_generated, Expected}} + end. + +exists_tar_file(Name) -> + File = tar_name(Name), + case filelib:is_regular(File) of + true -> + ok = file:delete(File), + true; + _ -> + false + end. + +%% Take a snap of the generated tar file and check if a certain +%% file is included. +%% This ensures at least that the tar file is generated. +check_tar(File, RelName) -> + TarContents = tar_contents(RelName), + case lists:member(File,TarContents) of + true -> ok; + _ -> {error, {erroneous_tar_file, tar_name(RelName), missing, File}} + end. + +%% Check that the given files exist in the tar file, and that they are +%% not symlinks +check_tar_regular(PrivDir, Files, RelName) -> + TmpDir = fname(PrivDir,tmp), + ok = file:make_dir(TmpDir), + ok = erl_tar:extract(tar_name(RelName), + [{files,Files},{cwd,TmpDir},compressed]), + R = lists:foldl(fun(File,Acc) -> + case file:read_link_info(fname(TmpDir,File)) of + {ok,#file_info{type=regular}} -> + Acc; + {ok,#file_info{type=Other}} -> + [{File,Other}|Acc]; + _ -> + [{File,missing}|Acc] + end + end, + [], + Files), + delete_tree(TmpDir), + case R of + [] -> + ok; + NotThere -> + {error,{erroneous_tar_file,tar_name(RelName),NotThere}} + end. + +delete_tree(Dir) -> + case filelib:is_dir(Dir) of + true -> + {ok,Files} = file:list_dir(Dir), + lists:foreach(fun(File) -> delete_tree(filename:join(Dir,File)) end, + Files), + file:del_dir(Dir); + false -> + ok = file:delete(Dir) + end. + +tar_contents(Name) -> + {ok, Cont} = erl_tar:table(Name ++ ".tar.gz", [compressed]), + Cont. + +tar_name(Name) -> + Name ++ ".tar.gz". + +create_script(latest,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest), + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 3\", \"LATEST\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"~s\"}, {stdlib, \"~s\"}, \n" + " {db, \"2.1\"}, {fe, \"3.1\"}]}.\n", + [KernelVer,StdlibVer]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_no_mod_vsn,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest), + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 3\", \"LATESTNOMOD\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"~s\"}, {stdlib, \"~s\"}, \n" + " {db, \"3.1\"}, {fe, \"3.1\"}]}.\n", + [KernelVer,StdlibVer]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest0,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-1'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST0\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {db, \"2.1\"}, {fe, \"3.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest1,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST1\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {db, \"1.0\"}, {fe, \"3.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest2,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-2'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 1\", \"LATEST2\"}, \n" + " {erts, \"4.3\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {db, \"1.0\"}, {fe, \"2.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_small,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-small'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST_SMALL\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {fe, \"3.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_small0,Config) -> %Differs in fe vsn + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-small0'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST_SMALL0\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {fe, \"2.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_small1,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-small1'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST_SMALL1\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {fe, \"500.18.7\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_nokernel,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-nokernel'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 3\", \"LATEST_NOKERNEL\"}, \n" + " {erts, \"4.4\"}, \n" + " [{db, \"2.1\"}, {fe, \"3.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_app_start_type1,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest_app_start_type1), + ?line ErtsVer = erlang:system_info(version), + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line RelfileContent = + {release,{"Test release", "1"}, + {erts,ErtsVer}, + [{kernel,KernelVer}, + {stdlib,StdlibVer}]}, + ?line io:format(Fd,"~p.~n",[RelfileContent]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_app_start_type2,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest_app_start_type2), + ?line ErtsVer = erlang:system_info(version), + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line OtherApps = [{mnesia,permanent}, + {sasl,transient}, + {webtool,temporary}, + {snmp,load}, + {xmerl,none}], + ?line lists:foreach(fun({App,_}) -> application:load(App) end, + OtherApps), + ?line Loaded = application:loaded_applications(), + ?line OtherAppsRel = + lists:map(fun({App,StartType}) -> + {_,_,Ver} = lists:keyfind(App,1,Loaded), + {App,Ver,StartType} + end, + OtherApps), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line RelfileContent = + {release,{"Test release", "2"}, + {erts,ErtsVer}, + [{kernel,KernelVer}, + {stdlib,StdlibVer} | OtherAppsRel]}, + ?line io:format(Fd,"~p.~n",[RelfileContent]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}. + +create_include_files(inc1, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc1), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\"}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\"}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc2, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc2), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t6 does not include t5 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\", [t4]}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\"}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc3, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc3), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t3 does not include t2 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\"}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\", []}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc4, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc4), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t3 does not include t2 ! + %% t6 does not include t5 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\", [t4]}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\", []}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc5, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc5), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t6 does not include t5 ! + %% exclude t5. + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\", [t4]}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\", []}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc6, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc6), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t3 does include non existing t2 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\"}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc7, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc7), + create_apps(PrivDir), + create_app(t7, PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t7 and t6 does include t5 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t7, \"1.0\"}, {t6, \"1.0\"}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\"}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc8, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc8), + create_circular_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t8 uses t9 and t10 includes t9 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t8, \"1.0\"}, {t9, \"1.0\"}, {t10, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc9, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc9), + create_circular_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t8 uses t9, t9 uses t10 and t10 includes t8 ==> circular !! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t8, \"1.0\"}, {t9, \"1.0\"}, {t10, \"1.0\", [t8]}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc10, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc10), + create_circular_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t9 tries to include not specified (in .app file) application ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t8, \"1.0\"}, {t9, \"1.0\", [t7]}, {t10, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc11, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc11), + create_apps2(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t11, \"1.0\"}, \n" + " {t12, \"1.0\"}, \n" + " {t13, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(otp_3065, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, otp_3065), + create_apps_3065(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {chAts, \"1.0\"}, {aa12, \"1.0\"}, \n" + " {chTraffic, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}. + +create_apps(Dir) -> + T1 = "{application, t1,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [kernel, stdlib]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't1.app'), list_to_binary(T1)), + + T2 = "{application, t2,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t1]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't2.app'), list_to_binary(T2)), + + T3 = "{application, t3,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t2]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't3.app'), list_to_binary(T3)), + + T4 = "{application, t4,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t3]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't4.app'), list_to_binary(T4)), + + T5 = "{application, t5,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t3]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't5.app'), list_to_binary(T5)), + + T6 = "{application, t6,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t4, t5]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't6.app'), list_to_binary(T6)). + +create_app(t7, Dir) -> + T7 = "{application, t7,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t5]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't7.app'), list_to_binary(T7)). + +create_circular_apps(Dir) -> + T8 = "{application, t8,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t9]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't8.app'), list_to_binary(T8)), + + T9 = "{application, t9,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t10]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't9.app'), list_to_binary(T9)), + + T10 = "{application, t10,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t8, t9]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't10.app'), list_to_binary(T10)). + +create_apps2(Dir) -> + T11 = "{application, t11,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t13]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't11.app'), list_to_binary(T11)), + + T12 = "{application, t12,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t11]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't12.app'), list_to_binary(T12)), + + T13 = "{application, t13,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't13.app'), list_to_binary(T13)). + + + +create_apps_3065(Dir) -> + T11 = "{application, chTraffic,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [chAts]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 'chTraffic.app'), list_to_binary(T11)), + + T12 = "{application, chAts,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [aa12]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 'chAts.app'), list_to_binary(T12)), + + T13 = "{application, aa12,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [chAts]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 'aa12.app'), list_to_binary(T13)). + +fname(N) -> + filename:join(N). + +fname(Dir, Basename) -> + filename:join(Dir, Basename). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..d375768b99 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.0"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..d3bd85cda6 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..4ab13c1bae --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {{advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..aa5bfa8098 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..0696e2494c --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,7 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {mod, {fe1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..dac4c00851 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1" [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..191919f8d3 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..d3bd85cda6 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..2db69eba76 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..cdbb9ef532 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.1"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..3e0ac3c3c9 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}, {db3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db3.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..4ab13c1bae --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {{advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..aa5bfa8098 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..191919f8d3 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..d3bd85cda6 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..4ab13c1bae --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {{advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..cdbb9ef532 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.1"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app new file mode 100644 index 0000000000..47ea248720 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "2.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.appup new file mode 100644 index 0000000000..2db69eba76 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..0696e2494c --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,7 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {mod, {fe1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app new file mode 100644 index 0000000000..22530ee335 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "1.0"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app new file mode 100644 index 0000000000..7243a0a96a --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "1.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..202d7f1234 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app @@ -0,0 +1,7 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {mod, {db1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db1.erl new file mode 100644 index 0000000000..7cddf6bb63 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db1.erl @@ -0,0 +1,13 @@ +-module(db1). +-vsn("1.0"). + +-export([a/0]). + +a() -> + lists:non_existing_func("dummy_list"), + b(). + +b() -> + fe2:non_existing_func(), + db2:non_existing_func(), + fe1:a(). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.app new file mode 100644 index 0000000000..97643561eb --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.app @@ -0,0 +1,7 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "3.1"}, + {modules, [db1, db2]}, + {registered, []}, + {applications, []}, + {mod, {db1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.appup new file mode 100644 index 0000000000..e636eee158 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app new file mode 100644 index 0000000000..c7ba1dfe91 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "2.1.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe1.erl new file mode 100644 index 0000000000..aa5bfa8098 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app new file mode 100644 index 0000000000..47ea248720 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "2.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe1.erl new file mode 100644 index 0000000000..aa5bfa8098 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..0696e2494c --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,7 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {mod, {fe1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..2db69eba76 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..8aca89b2c7 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,7 @@ +-module(fe1). +-vsn("1.0"). + +-export([a/0]). + +a() -> + ok. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.app b/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.app new file mode 100644 index 0000000000..e33314be7a --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.app @@ -0,0 +1,6 @@ +{application, kernel, + [{description, "FAKE KERNEL"}, + {vsn, "1.0"}, + {modules, []}, + {registered, []}, + {applications, []}]}. diff --git a/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.appup b/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.appup new file mode 100644 index 0000000000..c53f07591f --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.appup @@ -0,0 +1,12 @@ +%% +%% Fake release upgrade script for kernel +%% + +{ + "4.4.1", + [ + ], + + [ + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.app b/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.app new file mode 100644 index 0000000000..288fcfe74e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.app @@ -0,0 +1,6 @@ +{application, stdlib, + [{description, "FAKE STDLIB"}, + {vsn, "1.0"}, + {modules, []}, + {registered, []}, + {applications, []}]}. diff --git a/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.appup b/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.appup new file mode 100644 index 0000000000..b521eb5d71 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.appup @@ -0,0 +1,12 @@ +%% +%% Fake release upgrade script for stdlib +%% + +{ + "1.1", + [ + ], + + [ + ] +}. diff --git a/lib/sasl/test/systools_rc_SUITE.erl b/lib/sasl/test/systools_rc_SUITE.erl new file mode 100644 index 0000000000..bb93f38fa7 --- /dev/null +++ b/lib/sasl/test/systools_rc_SUITE.erl @@ -0,0 +1,488 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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% +%% +-module(systools_rc_SUITE). + +-include_lib("test_server/include/test_server.hrl"). +-include_lib("sasl/src/systools.hrl"). +-export([all/0,groups/0,init_per_group/2,end_per_group/2, + syntax_check/1, translate/1, translate_app/1]). + +%%----------------------------------------------------------------- +%% erl -compile systools_rc_SUITE @i ../src/ @i ../../test_server/include/ +%% c(systools_rc_SUITE, [{i, "../src"}, {i, "../../test_server/include"}]). +%%----------------------------------------------------------------- +all() -> + [syntax_check, translate, translate_app]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +syntax_check(suite) -> []; +syntax_check(Config) when is_list(Config) -> + PreApps = + [#application{name = test, + description = "TEST", + vsn = "0.1", + modules = [{foo,1},{bar,1},{baz,1},{old_mod,1}], + regs = [], + mod = {sasl, []}}, + #application{name = snmp, + description = "SNMP", + vsn = "1.0", + modules = [snmp], + regs = [], + mod = {snmp, []}}], + Apps = + [#application{name = test, + description = "TEST", + vsn = "1.0", + modules = [{foo,1},{bar,1},{baz,1},{new_mod,1}], + regs = [], + mod = {sasl, []}}], + S1 = [ + {update, bar, {advanced, extra}, brutal_purge, brutal_purge, []}, + {update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, baz, 5000, soft, brutal_purge, brutal_purge, []}, + {add_module, new_mod}, + {remove_application, snmp} + ], + ?line {ok, _} = systools_rc:translate_scripts([S1], Apps, PreApps), + S2 = [ + {apply, {m, f, [a]}}, + {load_object_code, {tst, "1.0", [new_mod]}}, + point_of_no_return, + {update, bar, {advanced, extra}, brutal_purge, brutal_purge, []}, + {update, foo, soft, soft_purge, soft_purge, [bar]}, + {load, {new_mod, soft_purge, soft_purge}}, + {remove, {old_mod, soft_purge, soft_purge}}, + {purge, [m1, m2]}, + {suspend, [m1]}, + {code_change, [{m1, extra}]}, + {resume, [m1]}, + {stop, [m3,m4]}, + {start, [m3,m4]}, + {sync_nodes, id1, {m, f, [a]}}, + {sync_nodes, id2, [cp1, cp2]}, + {apply, {m,f,[a]}}, + restart_new_emulator + ], + ?line {ok, _} = systools_rc:translate_scripts([S2], Apps, []), + S3 = [{apply, {m, f, a}}], + ?line {error, _, _} = systools_rc:translate_scripts([S3], Apps, []), + S3_1 = [{apply, {m, 3, a}}], + ?line {error, _, _} = systools_rc:translate_scripts([S3_1], Apps, []), + S4 = [{apply, {m, f}}], + ?line {error, _, _} = systools_rc:translate_scripts([S4], Apps, []), + S5 = [{load_object_code, hej}], + ?line {error, _, _} = systools_rc:translate_scripts([S5], Apps, []), + S6 = [{load_object_code, {342, "1.0", [foo]}}], + ?line {error, _, _} = systools_rc:translate_scripts([S6], Apps, []), + S7 = [{load_object_code, {tets, "1.0", foo}}], + ?line {error, _, _} = systools_rc:translate_scripts([S7], Apps, []), + S8 = [{suspend, [m1]}, point_of_no_return], + ?line {error, _, _} = systools_rc:translate_scripts([S8], Apps, []), + S9 = [{update, ba, {advanced, extra}, brutal_purge, brutal_purge, []}], + ?line {error, _, _} = systools_rc:translate_scripts([S9], Apps, []), + S10 = [{update, bar, {advanced, extra}, brutal_purge, brutal_purge, [baz]}], + ?line {error, _, _} = systools_rc:translate_scripts([S10], Apps, []), + S11 = [{update, bar, {advanced, extra}, brutal_purge, brutal_purge, [ba]}], + ?line {error, _, _} = systools_rc:translate_scripts([S11], Apps, []), + S12 = [{update, bar, advanced, brutal_purge, brutal_purge, []}], + ?line {error, _, _} = systools_rc:translate_scripts([S12], Apps, []), + S13 = [{update, bar, {advanced, extra}, rutal_purge, brutal_purge, [ba]}], + ?line {error, _, _} = systools_rc:translate_scripts([S13], Apps, []), + S14 = [{update, bar, {advanced, extra}, brutal_purge, rutal_purge, [ba]}], + ?line {error, _, _} = systools_rc:translate_scripts([S14], Apps, []), + S15 = [{update, bar, {advanced, extra}, brutal_purge, brutal_purge, ba}], + ?line {error, _, _} = systools_rc:translate_scripts([S15], Apps, []), + S16 = [{code_change, [module]}], + ?line {error, _, _} = systools_rc:translate_scripts([S16], Apps, []), + ?line ok. + +translate(suite) -> []; +translate(Config) when is_list(Config) -> + Apps = + [#application{name = test, + description = "TEST", + vsn = "1.0", + modules = [{foo,1},{bar,1},{baz,1}, + {x,1},{y,1},{z,1}], + regs = [], + mod = {sasl, []}}], + %% Simple translation (1) + Up1 = [{update, foo, soft, soft_purge, soft_purge, []}], + ?line {ok, X1} = systools_rc:translate_scripts([Up1], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[foo]}] = X1, + + %% Simple translation (2) + Up2 = [{update, foo, {advanced, extra}, soft_purge, soft_purge, []}], + ?line {ok, X2} = systools_rc:translate_scripts([Up2], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {load,{foo,soft_purge,soft_purge}}, + {code_change, up, [{foo, extra}]}, + {resume,[foo]}] = X2, + + ?line {ok, X22} = systools_rc:translate_scripts(dn,[Up2], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {code_change, down, [{foo, extra}]}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[foo]}] = X22, + + Up2a = [{update, foo, static, default, {advanced,extra}, + soft_purge, soft_purge, []}], + ?line {ok, X2a} = systools_rc:translate_scripts([Up2a], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {load,{foo,soft_purge,soft_purge}}, + {code_change, up, [{foo, extra}]}, + {resume,[foo]}] = X2a, + + ?line {ok, X22a} = systools_rc:translate_scripts(dn,[Up2a], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {load,{foo,soft_purge,soft_purge}}, + {code_change, down, [{foo, extra}]}, + {resume,[foo]}] = X22a, + + %% Simple dependency (1) + Up3 = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, soft, soft_purge, soft_purge, []}], + ?line {ok, X31} = systools_rc:translate_scripts([Up3], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X31, + ?line {ok, X32} = systools_rc:translate_scripts(dn,[Up3], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X32, + + Up3a = [{update, foo, static, default, soft, soft_purge, soft_purge, [bar]}, + {update, bar, static, default, soft, soft_purge, soft_purge, []}], + ?line {ok, X3a1} = systools_rc:translate_scripts([Up3a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo, bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X3a1, + ?line {ok, X3a2} = systools_rc:translate_scripts(dn,[Up3a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X3a2, + + %% Simple dependency (2) + Up4 = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, {advanced, []}, soft_purge, soft_purge, []}], + ?line {ok, X4} = systools_rc:translate_scripts(up,[Up4], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{bar,[]}]}, + {resume,[bar,foo]}] = X4, + + ?line {ok, X42} = systools_rc:translate_scripts(dn,[Up4], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {code_change,down,[{bar,[]}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X42, + + Up4a = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, static, infinity, {advanced, []}, + soft_purge, soft_purge, []}], + ?line {ok, X4a} = systools_rc:translate_scripts(up,[Up4a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,{bar,infinity}]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{bar,[]}]}, + {resume,[bar,foo]}] = X4a, + + ?line {ok, X42a} = systools_rc:translate_scripts(dn,[Up4a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,{bar,infinity}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {code_change,down,[{bar,[]}]}, + {resume,[bar,foo]}] = X42a, + + Up4b = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, dynamic, infinity, {advanced, []}, + soft_purge, soft_purge, []}], + ?line {ok, X4b} = systools_rc:translate_scripts(up,[Up4b], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,{bar,infinity}]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{bar,[]}]}, + {resume,[bar,foo]}] = X4b, + + ?line {ok, X42b} = systools_rc:translate_scripts(dn,[Up4b], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,{bar,infinity}]}, + {code_change,down,[{bar,[]}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X42b, + + %% More complex dependency + Up5 = [{update, foo, soft, soft_purge, soft_purge, [bar, baz]}, + {update, bar, {advanced, []}, soft_purge, soft_purge, []}, + {update, baz, {advanced, baz}, soft_purge, soft_purge, [bar]}], + ?line {ok, X5} = systools_rc:translate_scripts([Up5], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[foo,baz,bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{baz,baz},{bar,[]}]}, + {resume,[bar,baz,foo]}] = X5, + + ?line {ok, X52} = systools_rc:translate_scripts(dn,[Up5], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[foo,baz,bar]}, + {code_change,down,[{baz,baz},{bar,[]}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,baz,foo]}] = X52, + + Up5a = [{update, foo, dynamic, infinity, soft, soft_purge, + soft_purge, [bar, baz]}, + {update, bar, static, 7000, {advanced, []}, soft_purge, + soft_purge, []}, + {update, baz, dynamic, default, {advanced, baz}, soft_purge, + soft_purge, [bar]}], + ?line {ok, X5a} = systools_rc:translate_scripts([Up5a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[{foo,infinity},baz,{bar,7000}]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{baz,baz}, {bar,[]}]}, + {resume,[bar,baz,foo]}] = X5a, + + ?line {ok, X52a} = systools_rc:translate_scripts(dn,[Up5a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[{foo,infinity},baz,{bar,7000}]}, + {code_change,down,[{baz,baz}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {code_change,down,[{bar,[]}]}, + {resume,[bar,baz,foo]}] = X52a, + + Up5b = [{update, foo, dynamic, infinity, soft, soft_purge, + soft_purge, [bar, baz]}, + {update, bar, dynamic, 7000, {advanced, []}, soft_purge, + soft_purge, []}, + {update, baz, static, default, {advanced, baz}, soft_purge, + soft_purge, [bar]}], + ?line {ok, X5b} = systools_rc:translate_scripts([Up5b], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[{foo,infinity},baz,{bar,7000}]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{baz,baz},{bar,[]}]}, + {resume,[bar,baz,foo]}] = X5b, + + ?line {ok, X52b} = systools_rc:translate_scripts(dn,[Up5b], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[{foo,infinity},baz,{bar,7000}]}, + {code_change,down,[{bar,[]}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {code_change,down,[{baz,baz}]}, + {resume,[bar,baz,foo]}] = X52b, + + %% Simple circular dependency (1) + Up6 = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, soft, soft_purge, soft_purge, [foo]}], + ?line {ok, X61} = systools_rc:translate_scripts([Up6], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X61, + ?line {ok, X62} = systools_rc:translate_scripts(dn,[Up6], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X62, + + %% Simple circular dependency (2) + Up7 = [{update, foo, soft, soft_purge, soft_purge, [bar, baz]}, + {update, bar, soft, soft_purge, soft_purge, [foo]}, + {update, baz, soft, soft_purge, soft_purge, [bar]}], + ?line {ok, X71} = systools_rc:translate_scripts([Up7], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {suspend,[foo,bar,baz]}, + {load,{baz,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[baz, bar, foo]}] = X71, + ?line {ok, X72} = systools_rc:translate_scripts(dn,[Up7], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {suspend,[foo,bar,baz]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {resume,[baz,bar,foo]}] = X72, + + %% Complex circular dependencies, check only order + %% + Up8 = [{update, foo, soft, soft_purge, soft_purge, [baz]}, + {update, y, soft, soft_purge, soft_purge, [bar]}, + {update, x, soft, soft_purge, soft_purge, [y, z]}, + {update, z, soft, soft_purge, soft_purge, [x]}, + {update, bar, soft, soft_purge, soft_purge, [baz]}, + {update, baz, soft, soft_purge, soft_purge, [bar]}], + ?line {ok, X8} = systools_rc:translate_scripts([Up8], Apps, []), + ?line {value, {suspend, Order}} = lists:keysearch(suspend, 1, X8), + ?line Rest = case lists:reverse(Order) of + [bar, baz | R] -> R; + [baz, bar | R] -> R + end, + ?line case Rest of + [y, z, x, foo] -> ok; + [y, x, z, foo] -> ok; + [foo, y, z, x] -> ok; + [foo, y, x, z] -> ok; + [y, foo, z, x] -> ok; + [y, foo, x, z] -> ok + end, + + %% Check that order among other instructions isn't changed + Up9 = [{update, foo, soft, soft_purge, soft_purge, [baz]}, + {apply, {m, f, [1]}}, + {update, y, soft, soft_purge, soft_purge, [bar]}, + {apply, {m, f, [2]}}, + {update, x, soft, soft_purge, soft_purge, [y, z]}, + {apply, {m, f, [3]}}, + {update, z, soft, soft_purge, soft_purge, [x]}, + {apply, {m, f, [4]}}, + {update, bar, soft, soft_purge, soft_purge, [baz]}, + {apply, {m, f, [5]}}, + {update, baz, soft, soft_purge, soft_purge, [bar]}, + {apply, {m, f, [6]}}], + ?line {ok, X9} = systools_rc:translate_scripts([Up9], Apps, []), + Other2 = [X || {apply, {m, f, [X]}} <- X9], + ?line [1,2,3,4,5,6] = lists:sort(Other2), + ?line ok. + + +translate_app(suite) -> []; +translate_app(Config) when is_list(Config) -> + PreApps = + [#application{name = test, + description = "TEST", + vsn = "1.0", + modules = [{foo,1},{bar,1},{baz,1}], + regs = [], + mod = {sasl, []}}, + #application{name = pelle, + description = "PELLE", + vsn = "1.0", + modules = [pelle, kalle], + regs = [], + mod = {pelle, []}}], + Apps = + [#application{name = test, + description = "TEST", + vsn = "1.0", + modules = [{foo,1},{bar,1},{baz,1}], + regs = [], + mod = {sasl, []}}], + %% Simple translation (1) + Up1 = [{add_module, foo}, + {add_module, bar}], + ?line {ok, X1} = systools_rc:translate_scripts([Up1], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}] = X1, + + %% Simple translation (2) + Up2 = [{add_application, test}], + ?line {ok, X2} = systools_rc:translate_scripts([Up2], Apps, []), +io:format("X2=~p~n", [X2]), + ?line [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}, + {load,{baz,brutal_purge,brutal_purge}}, + {apply,{application,start,[test,permanent]}}] = X2, + + %% Simple translation (3) + Up3 = [{remove_application, pelle}], + ?line {ok, X3} = systools_rc:translate_scripts([Up3], Apps, PreApps), + ?line [point_of_no_return, + {apply,{application,stop,[pelle]}}, + {remove,{pelle,brutal_purge,brutal_purge}}, + {remove,{kalle,brutal_purge,brutal_purge}}, + {purge,[pelle,kalle]}, + {apply,{application,unload,[pelle]}}] = X3, + ?line ok. |