diff options
Diffstat (limited to 'lib/kernel/test/application_SUITE.erl')
-rw-r--r-- | lib/kernel/test/application_SUITE.erl | 2734 |
1 files changed, 2734 insertions, 0 deletions
diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl new file mode 100644 index 0000000000..313b50f976 --- /dev/null +++ b/lib/kernel/test/application_SUITE.erl @@ -0,0 +1,2734 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(application_SUITE). + +-include("test_server.hrl"). + +-export([all/1, failover/1, failover_comp/1, permissions/1, load/1, reported_bugs/1, + load_use_cache/1, + otp_1586/1, otp_2078/1, otp_2012/1, otp_2718/1, otp_2973/1, + otp_3002/1, otp_3184/1, otp_4066/1, otp_4227/1, otp_5363/1, + otp_5606/1, + start_phases/1, get_key/1, + permit_false_start_local/1, permit_false_start_dist/1, script_start/1, + nodedown_start/1, init2973/0, loop2973/0, loop5606/1]). + +-export([config_change/1, + distr_changed/1, distr_changed_tc1/1, distr_changed_tc2/1, + shutdown_func/1, do_shutdown/1]). + +-define(TESTCASE, testcase_name). +-define(testcase, ?config(?TESTCASE, Config)). + +-export([init_per_testcase/2, fin_per_testcase/2, start_type/0, + start_phase/0, conf_change/0]). +% Default timetrap timeout (set in init_per_testcase). +-define(default_timeout, ?t:minutes(2)). + +all(suite) -> + [failover, failover_comp, permissions, load, + load_use_cache, reported_bugs, + start_phases, script_start, nodedown_start, + permit_false_start_local, permit_false_start_dist, + get_key, distr_changed, config_change, shutdown_func]. + + +init_per_testcase(otp_2973=Case, Config) -> + code:add_path(?config(data_dir,Config)), + ?line Dog = test_server:timetrap(?default_timeout), + [{?TESTCASE, Case}, {watchdog, Dog}|Config]; +init_per_testcase(Case, Config) -> + ?line Dog = test_server:timetrap(?default_timeout), + [{?TESTCASE, Case}, {watchdog, Dog}|Config]. + +fin_per_testcase(otp_2973, Config) -> + code:del_path(?config(data_dir,Config)), + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok; +fin_per_testcase(_Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +-define(UNTIL(Seq), loop_until_true(fun() -> Seq end)). + +-record(st, { + normal = 0, + local = 0, + takeover = 0, + failover = 0 + }). + +loop_until_true(Fun) -> + case Fun() of + true -> + ok; + _ -> + timer:sleep(100), + loop_until_true(Fun) + end. + +%%----------------------------------------------------------------- +%% Should be started in a CC view with: +%% erl -sname XXX -rsh ctrsh where XX not in [cp1, cp2, cp3] +%%----------------------------------------------------------------- +failover(suite) -> []; +failover(doc) -> + ["Tests failover and takeover for distributed applications. Tests", + "start, load etc implicitly."]; +failover(Conf) when is_list(Conf) -> + %% start a help process to check the start type + StPid = spawn_link(?MODULE, start_type, []), + ?line yes = global:register_name(st_type, StPid), + + NodeNames = [Ncp1, Ncp2, Ncp3] = node_names([cp1, cp2, cp3], Conf), + NoSyncTime = config_fun_fast(config_fo(NodeNames)), + WithSyncTime = config_fun(config_fo(NodeNames)), + + % Test [cp1, cp2, cp3] + ?line {ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf), + ?line {ok, Cp2} = start_node_config(Ncp2, NoSyncTime, Conf), + ?line {ok, Cp3} = start_node_config(Ncp3, WithSyncTime, Conf), + Cps = [Cp1, Cp2, Cp3], + ?line wait_for_ready_net(), + + % Start app1 and make sure cp1 starts it + ?line {[ok,ok,ok],[]} = + rpc:multicall(Cps, application, load, [app1()]), + ?line ?UNTIL(is_loaded(app1, Cps)), + ?line {[ok,ok,ok],[]} = + rpc:multicall(Cps, application, start, [app1, permanent]), + ?line ?UNTIL(is_started(app1, Cp1)), + ?line false = is_started(app1, Cp2), + ?line ok = get_start_type(#st{normal = 3}), + + % Stop cp1 and make sure cp2 starts app1 + stop_node_nice(Cp1), + ?line ?UNTIL(is_started(app1, Cp2)), + ?line ok = get_start_type(#st{normal = 3}), + + % Restart cp1 and make sure it restarts app1 + ?line {ok, Cp1_2} = start_node_config(Ncp1, NoSyncTime, Conf), + ?line global:sync(), + ?line ok = rpc:call(Cp1_2, application, load, [app1()]), + ?line ok = rpc:call(Cp1_2, application, start, [app1, permanent]), + ?line ?UNTIL(is_started(app1, Cp1)), + ?line ?UNTIL(not is_started(app1, Cp2)), + ?line ok = get_start_type(#st{takeover = 3}), + + % Test [{cp1, cp2}, cp3] + % Start app_sp and make sure cp2 starts it (cp1 has more apps started) + ?line {[ok,ok,ok],[]} = + rpc:multicall([Cp1_2, Cp2, Cp3], application, load, [app_sp()]), + ?line {[ok,ok,ok],[]} = + rpc:multicall([Cp1_2, Cp2, Cp3], application, start,[app_sp,permanent]), + ?line ?UNTIL(is_started(app_sp, Cp2)), + ?line false = is_started(app_sp, Cp1), + ?line false = is_started(app_sp, Cp3), + ?line ok = get_start_type(#st{normal = 3}), + + % Stop cp2 and make sure cp1 starts app_sp + stop_node_nice(Cp2), + ?line ?UNTIL(is_started(app_sp, Cp1_2)), + ?line ok = get_start_type(#st{failover = 3}), + + % Stop cp1 and make sure cp3 starts app_sp + stop_node_nice(Cp1_2), + ?line ?UNTIL(is_started(app_sp, Cp3)), + ?line ok = get_start_type(#st{normal = 3, failover = 3}), + + % Restart cp2 and make sure it restarts app_sp + ?line {ok, Cp2_2} = start_node_config(Ncp2, NoSyncTime, Conf), + ?line global:sync(), + ?line ok = rpc:call(Cp2_2, application, load, [app_sp()]), + ?line ok = rpc:call(Cp2_2, application, start, [app_sp, permanent]), + ?line ?UNTIL(is_started(app_sp, Cp2_2)), + ?line ?UNTIL(not is_started(app_sp, Cp3)), + ?line ok = get_start_type(#st{takeover = 3}), + + % Restart cp1 and make sure it doesn't restart app_sp + ?line {ok, Cp1_3} = start_node_config(Ncp1, NoSyncTime, Conf), + ?line global:sync(), + ?line ok = rpc:call(Cp1_3, application, load, [app_sp()]), + ?line ok = rpc:call(Cp1_3, application, start, [app_sp, permanent]), + test_server:sleep(500), + ?line false = is_started(app_sp, Cp1_3), + ?line true = is_started(app_sp, Cp2_2), + + % Force takeover to cp1 + ?line ok = rpc:call(Cp1_3, application, takeover, [app_sp, permanent]), + ?line ?UNTIL(is_started(app_sp, Cp1_3)), + ?line ?UNTIL(not is_started(app_sp, Cp2_2)), + ?line ok = get_start_type(#st{takeover = 3}), + + %% Kill one child process and see that it is started with type local + PP = global:whereis_name({ch,3}), + exit(PP, kill), + ?line ok = get_start_type(#st{local = 1}), + + global:send(st_type, kill), + + stop_node_nice(Cp1_3), + stop_node_nice(Cp2_2), + stop_node_nice(Cp3), + ok. + +%%----------------------------------------------------------------- +%% Should be started in a CC view with: +%% erl -sname XXX -rsh ctrsh where XX not in [cp1, cp2, cp3] +%%----------------------------------------------------------------- +failover_comp(suite) -> []; +failover_comp(doc) -> + ["Tests failover and takeover for distributed applications. Tests", + "start, load etc implicitly. The applications do not use start_phases," + "i.e the failover should be trasfered to normal start type."]; +failover_comp(Conf) when is_list(Conf) -> + %% start a help process to check the start type + StPid = spawn_link(?MODULE, start_type, []), + ?line yes = global:register_name(st_type, StPid), + + NodeNames = [Ncp1, Ncp2, Ncp3] = node_names([cp1, cp2, cp3], Conf), + NoSyncTime = config_fun_fast(config(NodeNames)), + WithSyncTime = config_fun(config(NodeNames)), + + % Test [cp1, cp2, cp3] + ?line {ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf), + ?line {ok, Cp2} = start_node_config(Ncp2, NoSyncTime, Conf), + ?line {ok, Cp3} = start_node_config(Ncp3, WithSyncTime, Conf), + Cps = [Cp1, Cp2, Cp3], + ?line wait_for_ready_net(), + + % Start app1 and make sure cp1 starts it + ?line {[ok,ok,ok],[]} = + rpc:multicall(Cps, application, load, [app1()]), + ?line ?UNTIL(is_loaded(app1, Cps)), + ?line {[ok,ok,ok],[]} = + rpc:multicall(Cps, application, start, [app1, permanent]), + ?line ?UNTIL(is_started(app1, Cp1)), + ?line false = is_started(app1, Cp2), + ?line ok = get_start_type(#st{normal = 3}), + + % Stop cp1 and make sure cp2 starts app1 + stop_node_nice(Cp1), + ?line ?UNTIL(is_started(app1, Cp2)), + ?line ok = get_start_type(#st{normal = 3}), + + % Restart cp1 and make sure it restarts app1 + ?line {ok, Cp1_2} = start_node_config(Ncp1, NoSyncTime, Conf), + ?line global:sync(), + ?line ok = rpc:call(Cp1_2, application, load, [app1()]), + ?line ?UNTIL(is_loaded(app1, Cp1_2)), + ?line ok = rpc:call(Cp1_2, application, start, [app1, permanent]), + ?line ?UNTIL(is_started(app1, Cp1_2)), + ?line ?UNTIL(not is_started(app1, Cp2)), + ?line ok = get_start_type(#st{takeover = 3}), + + % Test [{cp1, cp2}, cp3] + % Start app3 and make sure cp2 starts it (cp1 has more apps started) + ?line {[ok,ok,ok],[]} = + rpc:multicall([Cp1_2, Cp2, Cp3], application, load, [app3()]), + ?line ?UNTIL(is_loaded(app3, [Cp1_2, Cp2, Cp3])), + ?line {[ok,ok,ok],[]} = + rpc:multicall([Cp1_2, Cp2, Cp3], application, start,[app3,permanent]), + ?line ?UNTIL(is_started(app3, Cp2)), + ?line false = is_started(app3, Cp1), + ?line false = is_started(app3, Cp3), + ?line ok = get_start_type(#st{normal = 3}), + + % Stop cp2 and make sure cp1 starts app3 + stop_node_nice(Cp2), + ?line ?UNTIL(is_started(app3, Cp1_2)), + ?line ok = get_start_type(#st{normal = 3}), + + % Stop cp1 and make sure cp3 starts app3 + stop_node_nice(Cp1_2), + ?line ?UNTIL(is_started(app3, Cp3)), + ?line ok = get_start_type(#st{normal = 6}), + + % Restart cp2 and make sure it restarts app3 + ?line {ok, Cp2_2} = start_node_config(Ncp2, NoSyncTime, Conf), + ?line global:sync(), + ?line ok = rpc:call(Cp2_2, application, load, [app3()]), + ?line ?UNTIL(is_loaded(app3, Cp2_2)), + ?line ok = rpc:call(Cp2_2, application, start, [app3, permanent]), + ?line ?UNTIL(is_started(app3, Cp2_2)), + ?line ?UNTIL(not is_started(app3, Cp3)), + ?line ok = get_start_type(#st{takeover = 3}), + + % Restart cp1 and make sure it doesn't restart app3 + ?line {ok, Cp1_3} = start_node_config(Ncp1, NoSyncTime, Conf), + ?line global:sync(), + ?line ok = rpc:call(Cp1_3, application, load, [app3()]), + ?line true = is_loaded(app3, Cp1_3), + ?line ok = rpc:call(Cp1_3, application, start, [app3, permanent]), + test_server:sleep(5000), + ?line false = is_started(app3, Cp1_3), + ?line true = is_started(app3, Cp2_2), + + % Force takeover to cp1 + ?line ok = rpc:call(Cp1_3, application, takeover, [app3, permanent]), + ?line ?UNTIL(is_started(app3, Cp1_3)), + ?line ?UNTIL(not is_started(app3, Cp2_2)), + ?line ok = get_start_type(#st{takeover = 3}), + + %% Kill one child process and see that it is started with type local + PP = global:whereis_name({ch,3}), + exit(PP, kill), + ?line ok = get_start_type(#st{local = 1}), + + global:send(st_type, kill), + + stop_node_nice(Cp1_3), + stop_node_nice(Cp2_2), + stop_node_nice(Cp3), + ok. + +%%----------------------------------------------------------------- +%% Should be started in a CC view with: +%% erl -sname XXX -rsh ctrsh where XX not in [cp1, cp2, cp3] +%%----------------------------------------------------------------- +permissions(suite) -> []; +permissions(doc) -> + ["Tests permissions for distributed applications."]; +permissions(Conf) when is_list(Conf) -> + + NodeNames = [Ncp1, Ncp2, Ncp3] = node_names([cp1, cp2, cp3], Conf), + NoSyncTime = config_fun_fast(config2(NodeNames)), + WithSyncTime = config_fun(config2(NodeNames)), + + % Test [cp1, cp2, cp3] + ?line {ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf), + ?line {ok, Cp2} = start_node_config(Ncp2, NoSyncTime, Conf), + ?line {ok, Cp3} = start_node_config(Ncp3, WithSyncTime, Conf), + Cps = [Cp1, Cp2, Cp3], + ?line wait_for_ready_net(), + + % Start app1 and make sure cp1 starts it + ?line {[ok,ok,ok],[]} = + rpc:multicall(Cps, application, load, [app1()]), + ?line ?UNTIL(is_loaded(app1, Cps)), + ?line {[ok,ok,ok],[]} = + rpc:multicall(Cps, application, start, [app1, permanent]), + ?line ?UNTIL(is_started(app1, Cp1)), + ?line false = is_started(app1, Cp2), + + % Unpermit app1 on cp1, make sure cp2 starts it + ?line ok = rpc:call(Cp1, application, permit, [app1, false]), + ?line false = is_started(app1, Cp1), + ?line true = is_started(app1, Cp2), + + % Unpermit app1 on cp2, make sure cp3 starts it + ?line ok = rpc:call(Cp2, application, permit, [app1, false]), + ?line false = is_started(app1, Cp1), + ?line false = is_started(app1, Cp2), + ?line true = is_started(app1, Cp3), + + % Permit cp2 again + ?line ok = rpc:call(Cp2, application, permit, [app1, true]), + ?line false = is_started(app1, Cp1), + ?line false = is_started(app1, Cp3), + ?line true = is_started(app1, Cp2), + + % Start app3, make sure noone starts it + ?line {[ok,ok,ok],[]} = + rpc:multicall(Cps, application, load, [app3()]), + ?line ?UNTIL(is_loaded(app3, Cps)), + ?line {[ok,ok,ok],[]} = + rpc:multicall(Cps, application, start, [app3, permanent]), + test_server:sleep(1000), + ?line false = is_started(app3, Cp1), + ?line false = is_started(app3, Cp2), + ?line false = is_started(app3, Cp3), + + % Permit app3 on Cp3 + ?line ok = rpc:call(Cp3, application, permit, [app3, true]), + ?line true = is_started(app3, Cp3), + + % Permit app3 on Cp2, make sure it starts it + ?line ok = rpc:call(Cp2, application, permit, [app3, true]), + ?line true = is_started(app3, Cp2), + ?line false = is_started(app3, Cp3), + + % Permit app3 on Cp1, make sure it doesn't start it + ?line ok = rpc:call(Cp1, application, permit, [app3, true]), + ?line false = is_started(app3, Cp1), + ?line true = is_started(app3, Cp2), + ?line false = is_started(app3, Cp3), + + % Stop Cp2, make sure Cp1 starts app3 + stop_node_nice(Cp2), + ?line ?UNTIL(is_started(app3, Cp1)), + + stop_node_nice(Cp1), + stop_node_nice(Cp3), + ok. + +%%----------------------------------------------------------------- +%% Should be started in a CC view with: +%% erl -sname XXX -rsh ctrsh where XX not in [cp1, cp2, cp3] +%%----------------------------------------------------------------- +load(suite) -> []; +load(doc) -> + ["Tests loading of distributed applications."]; +load(Conf) when is_list(Conf) -> + NodeNames = [Ncp1, Ncp2, Ncp3] = node_names([cp1, cp2, cp3], Conf), + NoSyncTime = config_fun_fast(config3(NodeNames)), + WithSyncTime = config_fun(config3(NodeNames)), + + % Test [cp1, cp2, cp3] + ?line {ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf), + ?line {ok, Cp2} = start_node_config(Ncp2, NoSyncTime, Conf), + ?line {ok, Cp3} = start_node_config(Ncp3, WithSyncTime, Conf), + Cps = [Cp1, Cp2, Cp3], + ?line wait_for_ready_net(), + + ?line {[ok,ok,ok],[]} = + rpc:multicall(Cps, application, load, [app1(), d1(NodeNames)]), + ?line ?UNTIL(is_loaded(app1, Cps)), + ?line {[ok,ok,ok],[]} = + rpc:multicall(Cps, application, start, [app1, permanent]), + ?line ?UNTIL(is_started(app1, Cp1)), + ?line false = is_started(app1, Cp2), + ?line false = is_started(app1, Cp3), + + % Load app1 with different specs and make sure we get an error + ?line {[{error,_},{error,_}],[]} = + rpc:multicall([Cp1, Cp2], application, load, [app1(), d1(NodeNames)]), + ?line {error, _} = rpc:call(Cp3, application, load, [app1(), d2(NodeNames)]), + + stop_node_nice(Cp1), + stop_node_nice(Cp2), + stop_node_nice(Cp3), + ok. + +%%----------------------------------------------------------------- +%% Same test as load/1, only with code path cache enabled. +%%----------------------------------------------------------------- +load_use_cache(suite) -> []; +load_use_cache(doc) -> + ["Tests loading of distributed applications. Code path cache enabled."]; +load_use_cache(Conf) when is_list(Conf) -> + NodeNames = [Ncp1, Ncp2, Ncp3] = node_names([cp1, cp2, cp3], Conf), + NoSyncTime = config_fun_fast(config3(NodeNames)), + WithSyncTime = config_fun(config3(NodeNames)), + + % Test [cp1, cp2, cp3] + ?line {ok, Cp1} = start_node_with_cache(Ncp1, NoSyncTime, Conf), + ?line {ok, Cp2} = start_node_with_cache(Ncp2, NoSyncTime, Conf), + ?line {ok, Cp3} = start_node_with_cache(Ncp3, WithSyncTime, Conf), + Cps = [Cp1, Cp2, Cp3], + ?line wait_for_ready_net(), + + ?line {[ok,ok,ok],[]} = + rpc:multicall(Cps, application, load, [app1(), d1(NodeNames)]), + ?line ?UNTIL(is_loaded(app1, Cps)), + ?line {[ok,ok,ok],[]} = + rpc:multicall(Cps, application, start, [app1, permanent]), + ?line ?UNTIL(is_started(app1, Cp1)), + ?line false = is_started(app1, Cp2), + + % Load app1 with different specs and make sure we get an error + ?line {[{error,_},{error,_}],[]} = + rpc:multicall([Cp1, Cp2], application, load, [app1(), d1(NodeNames)]), + ?line {error, _} = rpc:call(Cp3, application, load, [app1(), d2(NodeNames)]), + + stop_node_nice(Cp1), + stop_node_nice(Cp2), + stop_node_nice(Cp3), + ok. + +%%----------------------------------------------------------------- +%% Should be started in a CC view with: +%% erl -sname XXX -rsh ctrsh where XX not in [cp1, cp2, cp3] +%%----------------------------------------------------------------- +start_phases(suite) -> []; +start_phases(doc) -> + ["Tests new start phases and failover."]; +start_phases(Conf) when is_list(Conf) -> + %% start a help process to check the start type + SpPid = spawn_link(?MODULE, start_phase, []), + ?line yes = global:register_name(start_phase, SpPid), + + NodeNames = [Ncp1, _Ncp2, _Ncp3] = node_names([cp1, cp2, cp3], Conf), + WithSyncTime = config_fun(config_sf(NodeNames)), + + ?line {ok, Cp1} = start_node_config_sf(Ncp1, WithSyncTime, Conf), + ?line wait_for_ready_net(), + + %%============================= + %%Example 1 in the user's guide + %%============================= + ?line ok = rpc:call(Cp1, application, load, [myApp, + d_any3(myApp, NodeNames)]), + ?line ?UNTIL(is_loaded(myApp, Cp1)), + ?line ok = rpc:call(Cp1, application, start, [myApp, permanent]), + ?line ?UNTIL(is_started(myApp, Cp1)), + ?line ok = get_start_phase({sp, 0, 1, 0, 0, 1}), + ?line ok = rpc:call(Cp1, application, stop, [myApp]), + + %%============================= + %%Example 2 in the user's guide + %%============================= + ?line ok = rpc:call(Cp1, application, load, [topApp, + d_any3(topApp, NodeNames)]), + ?line ?UNTIL(is_loaded(topApp, Cp1)), + ?line ok = rpc:call(Cp1, application, start, [topApp, permanent]), + ?line ?UNTIL(is_started(topApp, Cp1)), + ?line ok = get_start_phase({sp, 0, 1, 0, 0, 1}), + ?line ok = rpc:call(Cp1, application, stop, [topApp]), + + %%============================= + %%Example 3 in the user's guide + %%============================= + ?line ok = rpc:call(Cp1, application, load, [topApp2, + d_any3(topApp2, NodeNames)]), + ?line ?UNTIL(is_loaded(topApp2, Cp1)), + ?line ok = rpc:call(Cp1, application, start, [topApp2, permanent]), + ?line ?UNTIL(is_started(topApp2, Cp1)), + ?line ok = get_start_phase({sp, 0, 2, 0, 0, 3}), + ?line ok = rpc:call(Cp1, application, stop, [topApp2]), + + %%============================= + %%Example 4 in the user's guide + %%============================= + ?line ok = rpc:call(Cp1, application, load, [topApp3, + d_any3(topApp3, NodeNames)]), + ?line ?UNTIL(is_loaded(topApp3, Cp1)), + ?line ok = rpc:call(Cp1, application, start, [topApp3, permanent]), + ?line ?UNTIL(is_started(topApp3, Cp1)), + ?line ok = get_start_phase({sp, 1, 3, 3, 2, 4}), + ?line ok = rpc:call(Cp1, application, stop, [topApp3]), + + global:send(start_phase, kill), + + stop_node_nice(Cp1), + ok. + + +script_start(doc) -> + ["Start distributed applications from within a boot script. Test ", + "same as failover."]; +script_start(suite) -> []; +script_start(Conf) when is_list(Conf) -> + %% start a help process to check the start type + StPid = spawn_link(?MODULE, start_type, []), + ?line yes = global:register_name(st_type, StPid), + + + % Create the .app files and the boot script + ?line ok = create_app(), + ?line {{KernelVer,StdlibVer}, _} = create_script("latest"), + ?line case is_real_system(KernelVer, StdlibVer) of + true -> + Options = []; + false -> + Options = [local] + end, + ?line ok = systools:make_script("latest", Options), + + NodeNames = [Ncp1, Ncp2, Ncp3] = node_names([cp1, cp2, cp3], Conf), + NoSyncTime = config_fun_fast(config_fo(NodeNames)), + WithSyncTime = config_fun(config_fo(NodeNames)), + + % Test [cp1, cp2, cp3] + ?line {ok, Cp1} = start_node_boot_config(Ncp1, NoSyncTime, Conf, latest), + ?line {ok, Cp2} = start_node_boot_config(Ncp2, NoSyncTime, Conf, latest), + ?line {ok, Cp3} = start_node_boot_config(Ncp3, WithSyncTime, Conf, latest), + ?line wait_for_ready_net(), + + ?line ?UNTIL(is_started(app1, Cp1)), + ?line ?UNTIL(is_started(app2, Cp1)), + ?line ?UNTIL(is_started(app_sp, Cp1)), + ?line false = is_started(app1, Cp2), + ?line ok = get_start_type(#st{normal = 9}), + + % Stop cp1 and make sure cp2 starts app1, app2 normally (no + % start_phases defined) and app_sp as failover (start_phases + % defined) + stop_node_nice(Cp1), + ?line ?UNTIL(is_started(app1, Cp2)), + ?line ?UNTIL(is_started(app2, Cp2)), + ?line ?UNTIL(is_started(app_sp, Cp2)), + ?line ok = get_start_type(#st{normal = 6, failover = 3}), + + % Restart cp1, Cp1 takesover app1 and app2 + ?line {ok, Cp1_2} = start_node_boot_config(Ncp1, NoSyncTime, Conf, latest), + ?line global:sync(), + ?line ?UNTIL(is_started(app1, Cp1_2)), + ?line false = is_started(app1, Cp2), + ?line ?UNTIL(is_started(app2, Cp1_2)), + ?line true = is_started(app_sp, Cp2), + ?line ?UNTIL(not is_started(app1, Cp2)), + ?line ?UNTIL(not is_started(app2, Cp2)), + ?line ok = get_start_type(#st{takeover = 6}), + + % Stop cp2 and make sure cp1 starts app_sp. + ?line false = is_started(app_sp, Cp1_2), + stop_node_nice(Cp2), + ?line ?UNTIL(is_started(app_sp, Cp1_2)), + ?line ok = get_start_type(#st{failover = 3}), + + % Stop cp1 and make sure cp3 starts app1, app2 and app_sp + stop_node_nice(Cp1_2), + ?line ?UNTIL(is_started(app_sp, Cp3)), + ?line ?UNTIL(is_started(app1, Cp3)), + ?line ?UNTIL(is_started(app2, Cp3)), + ?line ok = get_start_type(#st{normal = 6, failover = 3}), + + % Restart cp2 and make sure it takesover app1, app2 and app_sp + ?line {ok, Cp2_2} = start_node_boot_config(Ncp2, NoSyncTime, Conf, latest), + ?line global:sync(), + ?line ?UNTIL(is_started(app_sp, Cp2_2)), + ?line ?UNTIL(is_started(app1, Cp2_2)), + ?line ?UNTIL(is_started(app2, Cp2_2)), + ?line ?UNTIL(not is_started(app_sp, Cp3)), + ?line ?UNTIL(not is_started(app1, Cp3)), + ?line ?UNTIL(not is_started(app2, Cp3)), + ?line ok = get_start_type(#st{takeover = 9}), + + % Restart cp1 and make sure it takesover app1, app2 + ?line {ok, Cp1_3} = start_node_boot_config(Ncp1, NoSyncTime, Conf, latest), + ?line global:sync(), + ?line ?UNTIL(is_started(app1, Cp1_3)), + ?line ?UNTIL(is_started(app2, Cp1_3)), + ?line false = is_started(app_sp, Cp1_3), + ?line true = is_started(app_sp, Cp2_2), + ?line ?UNTIL(not is_started(app1, Cp2_2)), + ?line ?UNTIL(not is_started(app2, Cp2_2)), + ?line ok = get_start_type(#st{takeover = 6}), + + % Force takeover to cp1 + ?line ok = rpc:call(Cp1_3, application, takeover, [app_sp, permanent]), + ?line ?UNTIL(is_started(app_sp, Cp1_3)), + ?line ?UNTIL(not is_started(app_sp, Cp2_2)), + ?line ok = get_start_type(#st{takeover = 3}), + + %% Kill one child process and see that it is started with type local + PP = global:whereis_name({ch,3}), + exit(PP, kill), + ?line ok = get_start_type(#st{local = 1}), + + global:send(st_type, kill), + + stop_node_nice(Cp1_3), + stop_node_nice(Cp2_2), + stop_node_nice(Cp3), + + ?line ok = file:delete("latest.boot"), + ?line ok = file:delete("latest.rel"), + ?line ok = file:delete("latest.script"), + + ok. + +permit_false_start_local(doc) -> + ["Start local applications with permission false. Set", + "permit true on different nodes."]; +permit_false_start_local(suite) -> []; +permit_false_start_local(Conf) when is_list(Conf) -> + %% This configuration does not start dist_ac. + Config = write_config_file(fun config_perm/1, Conf), + + % Test [cp1, cp2, cp3] + [Ncp1, Ncp2, Ncp3] = node_names([cp1, cp2, cp3], Conf), + ?line {ok, Cp1} = start_node(Ncp1, Config), + ?line {ok, Cp2} = start_node(Ncp2, Config), + ?line {ok, Cp3} = start_node(Ncp3, Config), + ?line wait_for_ready_net(), + + ?line {[ok,ok,ok],[]} = + rpc:multicall([Cp1, Cp2, Cp3], application, load, [app1()]), + ?line {[ok,ok,ok],[]} = + rpc:multicall([Cp1, Cp2, Cp3], application, start, [app1, permanent]), + ?line {[ok,ok,ok],[]} = + rpc:multicall([Cp1, Cp2, Cp3], application, load, [app2()]), + ?line {[ok,ok,ok],[]} = + rpc:multicall([Cp1, Cp2, Cp3], application, start, [app2, permanent]), + ?line {[ok,ok,ok],[]} = + rpc:multicall([Cp1, Cp2, Cp3], application, load, [app3()]), + + test_server:sleep(1000), + ?line false = is_started(app1, Cp1), + ?line false = is_started(app1, Cp2), + ?line false = is_started(app1, Cp3), + + %Permit a not started application + ?line ok = rpc:call(Cp1, application, permit, [app3, true]), + test_server:sleep(1000), + ?line false = is_started(app3, Cp1), + ?line false = is_started(app3, Cp2), + ?line false = is_started(app3, Cp3), + + %Permit a not loaded application + ?line {error,{not_loaded,app_notloaded}} = + rpc:call(Cp1, application, permit, [app_notloaded, true]), + test_server:sleep(1000), + ?line false = is_started(app_notloaded, Cp1), + ?line false = is_started(app_notloaded, Cp2), + ?line false = is_started(app_notloaded, Cp3), + + %Unpermit a not started application + ?line ok = rpc:call(Cp1, application, permit, [app3, false]), + test_server:sleep(1000), + ?line false = is_started(app3, Cp1), + ?line false = is_started(app3, Cp2), + ?line false = is_started(app3, Cp3), + + %Unpermit a not loaded application + ?line {error,{not_loaded,app_notloaded}} = + rpc:call(Cp1, application, permit, [app_notloaded, false]), + test_server:sleep(1000), + ?line false = is_started(app_notloaded, Cp1), + ?line false = is_started(app_notloaded, Cp2), + ?line false = is_started(app_notloaded, Cp3), + + % Permit app1 on CP1 and make sure it is started + ?line ok = rpc:call(Cp1, application, permit, [app1, true]), + ?line ?UNTIL(is_started(app1, Cp1)), + ?line false = is_started(app1, Cp2), + ?line false = is_started(app1, Cp3), + + % Permit it again + ?line ok = rpc:call(Cp1, application, permit, [app1, true]), + test_server:sleep(1000), + ?line true = is_started(app1, Cp1), + ?line false = is_started(app1, Cp2), + ?line false = is_started(app1, Cp3), + + % Permit app2 on CP1 and make sure it is started + ?line ok = rpc:call(Cp1, application, permit, [app2, true]), + ?line ?UNTIL(is_started(app2, Cp1)), + ?line false = is_started(app2, Cp2), + ?line false = is_started(app2, Cp3), + + % Permit app1 on CP2 and make sure it is started + ?line ok = rpc:call(Cp2, application, permit, [app1, true]), + ?line ?UNTIL(is_started(app1, Cp2)), + ?line true = is_started(app1, Cp1), + ?line false = is_started(app1, Cp3), + + % Unpermit app1 on CP1 and make sure it is stopped + ?line ok = rpc:call(Cp1, application, permit, [app1, false]), + ?line ?UNTIL(false =:= is_started(app1, Cp1)), + ?line true = is_started(app1, Cp2), + ?line false = is_started(app1, Cp3), + + % Unpermit it agin + ?line ok = rpc:call(Cp1, application, permit, [app1, false]), + test_server:sleep(1000), + ?line false = is_started(app1, Cp1), + ?line true = is_started(app1, Cp2), + ?line false = is_started(app1, Cp3), + + % Permit app1 on CP1 and make sure it is started + ?line ok = rpc:call(Cp1, application, permit, [app1, true]), + ?line ?UNTIL(is_started(app1, Cp1)), + ?line true = is_started(app1, Cp2), + ?line false = is_started(app1, Cp3), + + % Unpermit app1 on CP1 and make sure it is stopped + ?line ok = rpc:call(Cp1, application, permit, [app1, false]), + ?line ?UNTIL(false =:= is_started(app1, Cp1)), + ?line true = is_started(app1, Cp2), + ?line false = is_started(app1, Cp3), + + % Unpermit app1 on CP2 and make sure it is stopped + ?line ok = rpc:call(Cp2, application, permit, [app1, false]), + test_server:sleep(1000), + ?line ?UNTIL(false =:= is_started(app1, Cp2)), + ?line false = is_started(app1, Cp1), + ?line false = is_started(app1, Cp3), + + % Unpermit app2 on CP1 and make sure it is stopped + ?line ok = rpc:call(Cp1, application, permit, [app2, false]), + ?line ?UNTIL(false =:= is_started(app2, Cp2)), + ?line false = is_started(app2, Cp1), + ?line false = is_started(app2, Cp3), + + stop_node_nice(Cp1), + stop_node_nice(Cp2), + stop_node_nice(Cp3), + ok. + + +permit_false_start_dist(doc) -> + ["Start distributed applications with permission false. Set", + "permit true on different nodes."]; +permit_false_start_dist(suite) -> []; +permit_false_start_dist(Conf) when is_list(Conf) -> + NodeNames = [Ncp1, Ncp2, Ncp3] = node_names([cp1, cp2, cp3], Conf), + NoSyncTime = config_fun_fast(config_perm2(NodeNames)), + WithSyncTime = config_fun(config_perm2(NodeNames)), + + % Test [cp1, cp2, cp3] + ?line {ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf), + ?line {ok, Cp2} = start_node_config(Ncp2, NoSyncTime, Conf), + ?line {ok, Cp3} = start_node_config(Ncp3, WithSyncTime, Conf), + Cps = [Cp1, Cp2, Cp3], + ?line wait_for_ready_net(), + + ?line {[ok,ok,ok],[]} = + rpc:multicall(Cps, application, load, [app1()]), + ?line ?UNTIL(is_loaded(app1, Cps)), + ?line {[ok,ok,ok],[]} = + rpc:multicall(Cps, application, start, [app1, permanent]), + ?line {[ok,ok,ok],[]} = + rpc:multicall(Cps, application, load, [app2()]), + + test_server:sleep(1000), + ?line false = is_started(app1, Cp1), + ?line false = is_started(app1, Cp2), + ?line false = is_started(app1, Cp3), + + %Permit a not started application + ?line ok = rpc:call(Cp1, application, permit, [app2, true]), + test_server:sleep(1000), + ?line false = is_started(app2, Cp1), + ?line false = is_started(app2, Cp2), + ?line false = is_started(app2, Cp3), + + %Permit a not loaded application + ?line {error,{not_loaded,app3}} = + rpc:call(Cp1, application, permit, [app3, true]), + test_server:sleep(1000), + ?line false = is_started(app3, Cp1), + ?line false = is_started(app3, Cp2), + ?line false = is_started(app3, Cp3), + + %Unpermit a not started application + ?line ok = rpc:call(Cp1, application, permit, [app2, false]), + ?line {[ok,ok,ok],[]} = + rpc:multicall([Cp1, Cp2, Cp3], application, start, [app2, permanent]), + test_server:sleep(1000), + ?line false = is_started(app2, Cp1), + ?line false = is_started(app2, Cp2), + ?line false = is_started(app2, Cp3), + + %Unpermit a not loaded application + ?line {error,{not_loaded,app3}} = + rpc:call(Cp1, application, permit, [app3, false]), + ?line {[ok,ok,ok],[]} = + rpc:multicall(Cps, application, load, [app3()]), + ?line ?UNTIL(is_loaded(app3, Cps)), + ?line {[ok,ok,ok],[]} = + rpc:multicall(Cps, application, start, [app3, permanent]), + test_server:sleep(1000), + ?line false = is_started(app3, Cp1), + ?line false = is_started(app3, Cp2), + ?line false = is_started(app3, Cp3), + + % Permit app1 on CP1 and make sure it is started + ?line ok = rpc:call(Cp1, application, permit, [app1, true]), + ?line ?UNTIL(is_started(app1, Cp1)), + ?line false = is_started(app1, Cp2), + ?line false = is_started(app1, Cp3), + + % Permit it again + ?line ok = rpc:call(Cp1, application, permit, [app1, true]), + ?line ?UNTIL(is_started(app1, Cp1)), + ?line false = is_started(app1, Cp2), + ?line false = is_started(app1, Cp3), + + % Permit app2 on CP1 and make sure it is started + ?line ok = rpc:call(Cp1, application, permit, [app2, true]), + ?line ?UNTIL(is_started(app2, Cp1)), + ?line false = is_started(app2, Cp2), + ?line false = is_started(app2, Cp3), + + % Permit app1 on CP2 and make sure it is not started + ?line ok = rpc:call(Cp2, application, permit, [app1, true]), + test_server:sleep(1000), + ?line true = is_started(app1, Cp1), + ?line false = is_started(app1, Cp2), + ?line false = is_started(app1, Cp3), + + % Crash CP1 and make sure app1, but not app2, is started on CP2 + stop_node_nice(Cp1), + ?line ?UNTIL(is_started(app1, Cp2)), + ?line false = is_started(app2, Cp2), + + % Restart CP1 again, check nothing is running on it + ?line {ok, Cp1_2} = start_node_config(Ncp1, NoSyncTime, Conf), + ?line global:sync(), + ?line ok = rpc:call(Cp1_2, application, load, [app1()]), + ?line ?UNTIL(is_loaded(app1, Cp1_2)), + ?line ok = rpc:call(Cp1_2, application, start, [app1, permanent]), + ?line ok = rpc:call(Cp1_2, application, load, [app2()]), + ?line ?UNTIL(is_loaded(app2, Cp1_2)), + ?line ok = rpc:call(Cp1_2, application, start, [app2, permanent]), + ?line ok = rpc:call(Cp1_2, application, load, [app3()]), + ?line ?UNTIL(is_loaded(app3, Cp1_2)), + ?line ok = rpc:call(Cp1_2, application, start, [app3, permanent]), + ?line false = is_started(app1, Cp1_2), + ?line false = is_started(app2, Cp1_2), + + % Permit app3 on CP3 and make sure it is started + ?line ok = rpc:call(Cp3, application, permit, [app3, true]), + ?line ?UNTIL(is_started(app3, Cp3)), + ?line false = is_started(app3, Cp1_2), + ?line false = is_started(app3, Cp2), + + % Permit app3 on CP1 and make sure it is moved there from CP3 + ?line ok = rpc:call(Cp1_2, application, permit, [app3, true]), + ?line ?UNTIL(is_started(app3, Cp1_2)), + ?line false = is_started(app3, Cp2), + ?line false = is_started(app3, Cp3), + + % Unpermit app3 on CP3 and CP1 and make sure it is stopped + ?line ok = rpc:call(Cp3, application, permit, [app3, false]), + ?line ok = rpc:call(Cp1_2, application, permit, [app3, false]), + ?line ?UNTIL(false =:= is_started(app3, Cp1_2)), + ?line false = is_started(app3, Cp2), + ?line false = is_started(app3, Cp3), + + stop_node_nice(Cp1_2), + stop_node_nice(Cp2), + stop_node_nice(Cp3), + ok. + +nodedown_start(doc) -> + ["app1 distributed as [cp1, cp2]. Call application:start(app1) on", + "cp2, but not on cp1. Kill cp1. Make sure app1 is started on cp2."]; +nodedown_start(suite) -> []; +nodedown_start(Conf) when is_list(Conf) -> + NodeNames = [Ncp1, Ncp2] = node_names([cp1, cp2], Conf), + NoSyncTime = config_fun_fast(config4(NodeNames)), + WithSyncTime = config_fun(config4(NodeNames)), + + % Test [cp1, cp2] + ?line {ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf), + ?line {ok, Cp2} = start_node_config(Ncp2, WithSyncTime, Conf), + ?line wait_for_ready_net(), + + % Start app1 and make sure cp1 starts it + ?line {[ok,ok],[]} = + rpc:multicall([Cp1, Cp2], application, load, [app1()]), + ?line _ = rpc:cast(Cp2, application, start, [app1, permanent]), + test_server:sleep(1000), + + % Crash CP1 and make sure app1 is started on CP2 + stop_node_nice(Cp1), + ?line ?UNTIL(is_started(app1, Cp2)), + + stop_node_nice(Cp2), + ok. + +%%%----------------------------------------------------------------- +%%% Testing of reported bugs and other tickets. +%%%----------------------------------------------------------------- +reported_bugs(suite) -> [otp_1586, otp_2078, otp_2012, otp_2718, + otp_2973, otp_3002, otp_3184, otp_4066, + otp_4227, otp_5363, otp_5606]. + +%%----------------------------------------------------------------- +%% Ticket: OTP-1586 +%% Slogan: recursive load of applications fails +%%----------------------------------------------------------------- +otp_1586(suite) -> []; +otp_1586(doc) -> + ["Test recursive load of applications."]; +otp_1586(Conf) when is_list(Conf) -> + Dir = ?config(priv_dir,Conf), + {ok, Fd} = file:open(filename:join(Dir, "app5.app"), write), + w_app5(Fd), + file:close(Fd), + ?line code:add_patha(Dir), + ?line ok = application:load(app4()), + ?line ok = application:unload(app4), + ok. + +%%----------------------------------------------------------------- +%% Ticket: OTP-2078 +%% Slogan: start of distrib apps fails when the nodes start +%% simultaneously +%%----------------------------------------------------------------- +otp_2078(suite) -> []; +otp_2078(doc) -> + ["Test start of distrib apps fails when the nodes start simultaneously."]; +otp_2078(Conf) when is_list(Conf) -> + NodeNames = [Ncp1, Ncp2] = node_names([cp1, cp2], Conf), + NoSyncTime = config_fun_fast(config4(NodeNames)), + WithSyncTime = config_fun(config4(NodeNames)), + + % Test [cp1, cp2] + ?line {ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf), + ?line {ok, Cp2} = start_node_config(Ncp2, WithSyncTime, Conf), + Cps = [Cp1, Cp2], + ?line wait_for_ready_net(), + + % Start app1 and make sure cp1 starts it + ?line {[ok,ok],[]} = + rpc:multicall(Cps, application, load, [app1()]), + ?line ?UNTIL(is_loaded(app1, Cps)), + ?line ok = rpc:call(Cp1, application, start, [app1, permanent]), + ?line ?UNTIL(is_started(app1, Cp1)), + ?line false = is_started(app1, Cp2), + + % Start app1 on cp2; make sure it works (the bug was that this start + % returned error) + ?line ok = rpc:call(Cp2, application, start, [app1, permanent]), + ?line true = is_started(app1, Cp1), + ?line false = is_started(app1, Cp2), + + stop_node_nice(Cp1), + stop_node_nice(Cp2), + ok. + +otp_2012(suite) -> []; +otp_2012(doc) -> + ["Test change of configuration parameters without changing code."]; +otp_2012(Conf) when is_list(Conf) -> + %% start a help process to check the config change + CcPid = spawn_link(?MODULE, conf_change, []), + ?line yes = global:register_name(conf_change, CcPid), + + % Write a .app file + {ok, Fd} = file:open("app1.app", write), + w_app1(Fd), + file:close(Fd), + {ok, Fd2} = file:open("app2.app", write), + w_app1(Fd2), + file:close(Fd2), + + % Start app1 + ?line ok = application:load(app1()), + ?line ok = application:start(app1, permanent), + + %% Read the current configuration parameters, and change them + EnvBefore = application_controller:prep_config_change(), + application_controller:test_change_apps([app1],[[{app1,[{new1, hi}, + {new2, moi}]}]]), + ?line ok = application_controller:config_change(EnvBefore), + ?line ok = get_conf_change([{[], [{new1, hi}, {new2, moi}], []}]), + + % Start app2 + ?line ok = application:load(app2()), + ?line ok = application:start(app2, permanent), + + %% Read the current configuration parameters, and change them again + EnvBefore2 = application_controller:prep_config_change(), + application_controller:test_change_apps([app1],[[{app1,[{new1, hello}, + {new3, mors}]}]]), + application_controller:test_change_apps([app2],[[{app2,[{new1, si}, + {new2, no}]}]]), + _EnvBefore22 = application_controller:prep_config_change(), + ?line ok = application_controller:config_change(EnvBefore2), + + ?line ok = get_conf_change([{[],[{new1,si},{new2,no}],[]}, + {[{new1,hello}],[{new3,mors}],[new2]}]), + + ?line ok = application:stop(app1), + ?line ok = application:stop(app2), + ok. + +%%----------------------------------------------------------------- +%% Ticket: OTP-2718 +%% Slogan: transient app which fails during start is ignored +%%----------------------------------------------------------------- +otp_2718(suite) -> []; +otp_2718(doc) -> + ["Test fail of transient app at start."]; +otp_2718(Conf) when is_list(Conf) -> + ?line {ok, Cp1} = start_node_args(cp1, "-pa " ++ ?config(data_dir,Conf)), + ?line wait_for_ready_net(), + + %% normal exit from the application + ?line ok = rpc:call(Cp1, application, load, [app_trans_normal()]), + ?line ?UNTIL(is_loaded(trans_normal, Cp1)), + ?line {error, {{'EXIT',normal},_}} = + rpc:call(Cp1, application, start, [trans_normal, transient]), + test_server:sleep(2000), + ?line false = is_started(trans_normal, Cp1), + + %% abnormal exit from the application + ?line ok = rpc:call(Cp1, application, load, [app_trans_abnormal()]), + ?line {error, {bad_return,{{trans_abnormal_sup,start,[normal,[]]}, + {'EXIT',abnormal}}}} = + rpc:call(Cp1, application, start, [trans_abnormal, transient]), + test_server:sleep(3000), + ?line {badrpc,nodedown} = which_applications(Cp1), + ok. + +%%----------------------------------------------------------------- +%% Ticket: OTP-2973 +%% Slogan: application:start does not test if an appl is already starting... +%%----------------------------------------------------------------- +otp_2973(suite) -> []; +otp_2973(doc) -> + ["Test of two processes simultanously starting the same application."]; +otp_2973(Conf) when is_list(Conf) -> + % Write a .app file + {ok, Fd} = file:open("app0.app", write), + w_app(Fd, app0()), + file:close(Fd), + + ?line Pid1 = spawn_link(?MODULE, init2973, []), + ?line Pid2 = spawn_link(?MODULE, init2973, []), + + ?line Pid1 ! {start, self(), app0}, + ?line Pid2 ! {start, self(), app0}, + + ?line {Res1, Res2} = receive + {Pid1, res, Res1x} -> + receive + {Pid2, res, Res2x} -> + {Res1x, Res2x} + after 2000 -> + ?line test_server:fail(timeout_pid2) + end; + {Pid2, res, Res2x} -> + receive + {Pid1, res, Res1x} -> + {Res1x, Res2x} + after 2000 -> + ?line test_server:fail(timeout_pid1) + end + end, + + %% Stop it. Inteferes with other global. + ?line ok = application:stop(app0), + + %% Test result. + case {Res1, Res2} of + {ok, ok} -> + ok; + _ -> + ?line Txt = io_lib:format("Illegal results from start: ~p ~p ", + [Res1, Res2]), + ?line test_server:fail(lists:flatten(Txt)) + end, + + + % Write a .app file + ?line {ok, Fda} = file:open("app_start_error.app", write), + ?line w_app_start_error(Fda), + ?line file:close(Fda), + + ?line Pid1 ! {start, self(), app_start_error}, + ?line Pid2 ! {start, self(), app_start_error}, + + ?line {Res1a, Res2a} = receive + {Pid1, res, Res1y} -> + receive + {Pid2, res, Res2y} -> + {Res1y, Res2y} + after 2000 -> + ?line test_server:fail(timeout_pid2) + end; + {Pid2, res, Res2y} -> + receive + {Pid1, res, Res1y} -> + {Res1y, Res2y} + after 2000 -> + ?line test_server:fail(timeout_pid1) + end + end, + + case {Res1a, Res2a} of + {{error,{'start error',{app_start_error,start,[normal,[]]}}}, + {error,{'start error',{app_start_error,start,[normal,[]]}}}} -> + ok; + _ -> + ?line Txta = io_lib:format("Illegal results from start ~p ~p ",[Res1a, Res2a]), + ?line test_server:fail(lists:flatten(Txta)) + end, + + ok. + + + +%%----------------------------------------------------------------- +%% Ticket: OTP-3184 +%% Slogan: crash the node if permanent appl has illegal env parameter values +%%----------------------------------------------------------------- +otp_3184(suite) -> []; +otp_3184(doc) -> + ["When a distributed application is started the permit flag is checked " + "that the permit flag is not changed during the start. " + "Te check must only be made if the application is started on the own node"]; +otp_3184(Conf) when is_list(Conf) -> + NodeNames = [Ncp1, Ncp2] = node_names([cp1, cp2], Conf), + NoSyncTime = config_fun_fast(config3184(NodeNames)), + WithSyncTime = config_fun(config3184(NodeNames)), + + % Test [cp1, cp2] + ?line {ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf), + ?line {ok, Cp2} = start_node_config(Ncp2, WithSyncTime, Conf), + ?line wait_for_ready_net(), + + % Start app1 and make sure it is not started + ?line {[ok,ok],[]} = + rpc:multicall([Cp1, Cp2], application, load, [app1()]), + test_server:sleep(3000), + ?line false = is_started(app1, Cp1), + ?line false = is_started(app1, Cp2), + + % Start app1 on cp1 + ?line ok = rpc:call(Cp1, application, permit, [app1, true]), + ?line ok = rpc:call(Cp1, application, start, [app1, permanent]), + ?line ok = rpc:call(Cp2, application, start, [app1, permanent]), + ?line ?UNTIL(is_started(app1, Cp1)), + ?line false = is_started(app1, Cp2), + + % Check that the application is marked as running in application_controller + ?line X = rpc:call(Cp1, application_controller, info, []), + ?line {value, {running, Xrunning}} = lists:keysearch(running, 1, X), + ?line {value, Xapp1} = lists:keysearch(app1, 1, Xrunning), + ?line {app1, _Xpid} = Xapp1, + + ?line Y = rpc:call(Cp2, application_controller, info, []), + ?line {value, {running, Yrunning}} = lists:keysearch(running, 1, Y), + ?line {value, Yapp1} = lists:keysearch(app1, 1, Yrunning), + ?line {app1, {distributed, Cp1}} = Yapp1, + + stop_node_nice(Cp1), + stop_node_nice(Cp2), + ok. + +%%----------------------------------------------------------------- +%% Ticket: OTP-3002 +%% Slogan: crash the node if permanent appl has illegal env parameter values +%%----------------------------------------------------------------- +otp_3002(suite) -> []; +otp_3002(doc) -> + ["crash the node if permanent appl has illegal env parameter values."]; +otp_3002(Conf) when is_list(Conf) -> + % Create the boot script + ?line {{KernelVer,StdlibVer}, {LatestDir, LatestName}} = + create_script_3002("script_3002"), + ?t:format(0, "LatestDir = ~p~n", [LatestDir]), + ?t:format(0, "LatestName = ~p~n", [LatestName]), + + ?line case is_real_system(KernelVer, StdlibVer) of + true -> + Options = []; + false -> + Options = [local] + end, + + ?line ok = systools:make_script("script_3002", Options), + ?line ok = systools:script2boot("script_3002"), + + ?line {error, timeout} = start_node_boot_3002(cp1, "script_3002"), + + ?line ok = file:delete("script_3002.boot"), + ?line ok = file:delete("script_3002.rel"), + ?line ok = file:delete("script_3002.script"), + ok. + +%%----------------------------------------------------------------- +%% Ticket: OTP-4066 +%% Slogan: dist_ac crashed if a distributed application that it +%% didn't know of was stopped by another dist_ac (bad_match +%% when it received dist_ac_app_stopped). +%%----------------------------------------------------------------- + +otp_4066(suite) -> []; +otp_4066(doc) -> ["Check that application stop don't cause dist_ac crash"]; +otp_4066(Conf) when is_list(Conf) -> + % Write config files + [Ncp1, Ncp2] = node_names([cp1, cp2], Conf), + Host = from($@, atom_to_list(node())), + Cp1 = list_to_atom(Ncp1 ++ "@" ++ Host), + Cp2 = list_to_atom(Ncp2 ++ "@" ++ Host), + AllNodes = [Cp1, Cp2], + App1Nodes = {app1, AllNodes}, + + Dir = ?config(priv_dir,Conf), + ?line {ok, FdC} = file:open(filename:join(Dir, "otp_4066.config"), write), + ?line write_config(FdC, config_4066(AllNodes, 5000, [App1Nodes])), + ?line file:close(FdC), + + % Write the app1.app file + ?line {ok, FdA12} = file:open(filename:join(Dir, "app1.app"), write), + ?line w_app1(FdA12), + ?line file:close(FdA12), + + Args1 = "-pa " ++ Dir ++ " -config " ++ filename:join(Dir, "otp_4066"), + Args2 = "-pa " ++ Dir ++ " -kernel start_dist_ac true", + + ?line {ok, Cp2} = start_node_args(Ncp2, Args2), + %% Cp1 syncs with cp2 (which is known to be up). + ?line {ok, Cp1} = start_node_args(Ncp1, Args1), + ?line wait_for_ready_net(), + + ?line ok = rpc:call(Cp1, application, start, [app1]), + ?line wait_until_started(app1, [Cp1]), + ?line test_server:format("--- App1 started at Cp1 ---~n", []), + ?line print_dac_state(AllNodes), + + % Cp2 previously crashed on this stop + ?line ok = rpc:call(Cp1, application, stop, [app1]), + ?line wait_until_stopped(app1, [Cp1]), + ?line test_server:format("--- App1 stopped at Cp1 ---~n", []), + ?line print_dac_state(AllNodes), + + ?line ok = rpc:call(Cp1, application, start, [app1]), + ?line wait_until_started(app1, [Cp1]), + ?line test_server:format("--- App1 started at Cp1 ---~n", []), + ?line print_dac_state(AllNodes), + + ?line ok = rpc:call(Cp2, application, load, [app1, App1Nodes]), + ?line ok = rpc:call(Cp2, application, start, [app1]), + ?line test_server:format("--- App1 started at Cp2 ---~n", []), + ?line print_dac_state(AllNodes), + + + ?line stop_node_nice(Cp1), + ?line wait_until_started(app1, [Cp2]), + ?line test_server:format("--- Cp1 crashed; failover to Cp2 ---~n", []), + ?line print_dac_state(Cp2), + + ?line stop_node_nice(Cp2), + ok. + +config_4066(SyncNodesOptional, SyncNodesTimeout, Distributed) -> + [{kernel, [{sync_nodes_optional,SyncNodesOptional}, + {sync_nodes_timeout, SyncNodesTimeout}, + {distributed, Distributed}]}]. + +write_config(Fd, Config) -> + io:format(Fd, "~p.~n", [Config]). + +print_dac_state(Node) when is_atom(Node) -> + State = gen_server:call({dist_ac, Node}, info), + test_server:format(" * dist_ac state on node ~p:~n ~p~n", + [Node, State]); +print_dac_state(Nodes) when is_list(Nodes) -> + lists:foreach(fun (N) -> print_dac_state(N) end, Nodes). + + +%%----------------------------------------------------------------- +%% Ticket: OTP-4227 +%% Slogan: Bad return value from application. +%%----------------------------------------------------------------- +otp_4227(suite) -> []; +otp_4227(doc) -> + ["Test start of depending app when required app crashed."]; +otp_4227(Conf) when is_list(Conf) -> + NodeNames = [Ncp1, Ncp2] = node_names([cp1, cp2], Conf), + NoSyncTime = config_fun_fast(config_4227(NodeNames)), + WithSyncTime = config_fun(config_4227(NodeNames)), + + %% Test [cp1, cp2] + ?line {ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf), + ?line {ok, Cp2} = start_node_config(Ncp2, WithSyncTime, Conf), + Cps = [Cp1, Cp2], + ?line wait_for_ready_net(), + + %% Try to start app10 which should fail since app9 is not started + ?line {[ok,ok],[]} = + rpc:multicall(Cps, application, load, [app9()]), + ?line ?UNTIL(is_loaded(app9, Cps)), + ?line {[ok,ok],[]} = + rpc:multicall(Cps, application, load, [app10_dep9()]), + ?line {error, {not_started, app9}} = + rpc:call(Cp1, application, start, [app10]), + + %% Start app9 and brutally kill it, then try to start app10 + ?line ok = rpc:call(Cp1, application, start, [app9]), + ?line test_server:sleep(1000), + ?line Pid9 = rpc:call(Cp1, erlang, whereis, [ch_sup19]), + ?line true = erlang:is_pid(Pid9), + ?line true = erlang:exit(Pid9, kill), + ?line test_server:sleep(1000), + + %% This gave {error, no_report} before the patch + ?line {error, {not_running, app9}} = + rpc:call(Cp1, application, start, [app10]), + + ?line stop_node_nice(Cp1), + ?line stop_node_nice(Cp2), + ok. + +config_4227([Ncp1, Ncp2]) -> + fun(Fd, SyncNodesTimeout) -> + M = from($@, atom_to_list(node())), + io:format(Fd, + "[{kernel, " + " [{sync_nodes_optional, ['~s@~s','~s@~s']}," + " {sync_nodes_timeout, ~w}," + " {start_dist_ac, true}," + " {distributed, " + " [{app9, ['~s@~s','~s@~s']}, " + " {app10, ['~s@~s','~s@~s']}]}]}].~n", + [Ncp1, M, Ncp2, M, + SyncNodesTimeout, + Ncp1, M, Ncp2, M, + Ncp1, M, Ncp2, M]) + end. + +%%----------------------------------------------------------------- +%% Ticket: OTP-5363 +%% Slogan: Slow termination in application_master +%%----------------------------------------------------------------- +otp_5363(Conf) when is_list(Conf) -> + %% When stopping an application, all processes having the + %% application master as group leader should get killed. + %% The killing was done in an inefficient way. + %% In this test case, we will not test the efficiency of + %% the code, but only that the correct processes ARE killed. + + OldPath = code:get_path(), + code:add_patha(?config(data_dir,Conf)), + try + ?line ok = application:load(app_group_leader()), + ?line ok = application:start(group_leader), + ?line case whereis(nisse) of + Pid when is_pid(Pid) -> + ?line Mref = erlang:monitor(process, Pid), + ?line ok = application:stop(group_leader), + receive + {'DOWN',Mref,_,_,_} -> ok + end, + ?line undefined = whereis(nisse); + Bad -> + ?line io:format("~p\n", [Bad]), + ?t:fail() + end + after + code:set_path(OldPath) + end, + ok. + +%%----------------------------------------------------------------- +%% Ticket: OTP-5606 +%% Slogan: Problems with starting a distributed application +%%----------------------------------------------------------------- +otp_5606(suite) -> []; +otp_5606(doc) -> + ["Test of several processes simultanously starting the same " + "distributed application."]; +otp_5606(Conf) when is_list(Conf) -> + + %% Write a config file + Dir = ?config(priv_dir, Conf), + {ok, Fd} = file:open(filename:join(Dir, "sys.config"), write), + NodeNames = [Ncp1, Ncp2] = node_names([cp1, cp2], Conf), + (config4(NodeNames))(Fd, 10000), + file:close(Fd), + Config = filename:join(Dir, "sys"), + + %% Test [cp1, cp2] + ?line {ok, Cp1} = start_node(Ncp1, Config), + ?line {ok, Cp2} = start_node(Ncp2, Config), + Cps = [Cp1, Cp2], + ?line wait_for_ready_net(), + + %% Load app1 on both nodes + ?line {[ok, ok], []} = + rpc:multicall(Cps, application, load, [app1()]), + + %% Attempt to start app1 from different processes simultaneously + ?line Pid11 = spawn_link(Cp1, ?MODULE, loop5606, [self()]), + ?line Pid12 = spawn_link(Cp1, ?MODULE, loop5606, [self()]), + ?line Pid13 = spawn_link(Cp1, ?MODULE, loop5606, [self()]), + ?line Pid2 = spawn_link(Cp2, ?MODULE, loop5606, [self()]), + + ?line Pid2 ! start, + ?line Pid11 ! start, + ?line Pid12 ! start, + ?line Pid13 ! start, + + ResL = otp_5606_loop([]), + + case ResL of + [ok, ok, ok, ok] -> + ok; + [Res1, Res2, Res3, Res4] -> + Txt = io_lib:format("Illegal results from start ~p ~p ~p ~p", + [Res1, Res2, Res3, Res4]), + ?line test_server:fail(lists:flatten(Txt)) + end, + + ?line {error, {already_started, app1}} = + rpc:call(Cp1, application, start, [app1]), + + stop_node_nice(Cp1), + stop_node_nice(Cp2), + ok. + +otp_5606_loop(ResL) when length(ResL)<4 -> + receive + {_Pid, Res} -> + otp_5606_loop([Res|ResL]) + after 5000 -> + ?line test_server:fail(timeout_waiting_for_res) + end; +otp_5606_loop(ResL) -> + ResL. + +loop5606(Pid) -> + receive + start -> + Res = application:start(app1), + Pid ! {self(), Res} + end. + + +%%----------------------------------------------------------------- +%% Should be started in a CC view with: +%% erl -sname XXX -rsh ctrsh where XX not in [cp1, cp2, cp3] +%%----------------------------------------------------------------- +get_key(suite) -> []; +get_key(doc) -> + ["Tests read the .app keys."]; +get_key(Conf) when is_list(Conf) -> + NodeNames = [Ncp1, _Ncp2, _Ncp3] = node_names([cp1, cp2, cp3], Conf), + WithSyncTime = config_fun(config_inc(NodeNames)), + + % Test [cp1, cp2, cp3] + ?line {ok, Cp1} = start_node_config(Ncp1, WithSyncTime, Conf), + + ?line ok = rpc:call(Cp1, application, load, [appinc(), d3(NodeNames)]), + ?line ?UNTIL(is_loaded(appinc, Cp1)), + ?line ok = rpc:call(Cp1, application, start, [appinc, permanent]), + ?line ?UNTIL(is_started(appinc, Cp1)), + + ?line {ok, "Test of new app file, including appnew"} = + rpc:call(Cp1, application, get_key, [appinc, description]), + ?line {ok, "CXC 138 ai"} = rpc:call(Cp1, application, get_key, [appinc ,id]), + ?line {ok, "2.0"} = rpc:call(Cp1, application, get_key, [appinc, vsn]), + ?line {ok, [kernel]} = rpc:call(Cp1, application, get_key, [appinc, applications]), + ?line {ok, [appinc1, appinc2]} = + rpc:call(Cp1, application, get_key, [appinc, included_applications]), + ?line {ok, []} = rpc:call(Cp1, application, get_key, [appinc, registered]), + ?line {ok, [{init, [kalle]}, {takeover, []}, {go, [sune]}]} = + rpc:call(Cp1, application, get_key, [appinc, start_phases]), + ?line {ok, Env} = rpc:call(Cp1, application, get_key, [appinc ,env]), + ?line [{included_applications,[appinc1,appinc2]}, + {own2,val2},{own_env1,value1}] = lists:sort(Env), + ?line {ok, []} = rpc:call(Cp1, application, get_key, [appinc, modules]), + ?line {ok, {application_starter, [ch_sup, {appinc, 41, 43}] }} = + rpc:call(Cp1, application, get_key, [appinc, mod]), + ?line {ok, infinity} = rpc:call(Cp1, application, get_key, [appinc, maxP]), + ?line {ok, infinity} = rpc:call(Cp1, application, get_key, [appinc, maxT]), + ?line undefined = rpc:call(Cp1, application, get_key, [appinc, very_unknown]), + + ?line {ok, [{description, "Test of new app file, including appnew"}, + {id, "CXC 138 ai"}, + {vsn, "2.0"}, + {modules, []}, + {maxP, infinity}, + {maxT, infinity}, + {registered, []}, + {included_applications, [appinc1, appinc2]}, + {applications, [kernel]}, + {env, Env}, + {mod, {application_starter, [ch_sup, {appinc, 41, 43}] }}, + {start_phases, [{init, [kalle]}, {takeover, []}, {go, [sune]}]}]} = + rpc:call(Cp1, application, get_all_key, [appinc]), + ?line [{included_applications,[appinc1,appinc2]}, + {own2,val2},{own_env1,value1}] = lists:sort(Env), + + ?line {ok, "Test of new app file, including appnew"} = + gen_server:call({global, {ch,41}}, {get_pid_key, description}), + ?line {ok, "CXC 138 ai"} = + gen_server:call({global, {ch,41}}, {get_pid_key, id}), + ?line {ok, "2.0"} = + gen_server:call({global, {ch,41}}, {get_pid_key, vsn}), + ?line {ok, [kernel]} = + gen_server:call({global, {ch,41}}, {get_pid_key, applications}), + ?line {ok, [appinc1, appinc2]} = + gen_server:call({global, {ch,41}}, {get_pid_key, included_applications}), + ?line {ok, []} = + gen_server:call({global, {ch,41}}, {get_pid_key, registered}), + ?line {ok, [{init, [kalle]}, {takeover, []}, {go, [sune]}]} = + gen_server:call({global, {ch,41}}, {get_pid_key, start_phases}), + ?line {ok, Env} = gen_server:call({global, {ch,41}}, {get_pid_key, env}), + ?line [{included_applications,[appinc1,appinc2]}, + {own2,val2},{own_env1,value1}] = lists:sort(Env), + ?line {ok, []} = + gen_server:call({global, {ch,41}}, {get_pid_key, modules}), + ?line {ok, {application_starter, [ch_sup, {appinc, 41, 43}] }} = + gen_server:call({global, {ch,41}}, {get_pid_key, mod}), + ?line {ok, infinity} = + gen_server:call({global, {ch,41}}, {get_pid_key, maxP}), + ?line {ok, infinity} = + gen_server:call({global, {ch,41}}, {get_pid_key, maxT}), + ?line undefined = + gen_server:call({global, {ch,41}}, {get_pid_key, very_unknown}), + + + + ?line {ok, [{description, "Test of new app file, including appnew"}, + {id, "CXC 138 ai"}, + {vsn, "2.0"}, + {modules, []}, + {maxP, infinity}, + {maxT, infinity}, + {registered, []}, + {included_applications, [appinc1, appinc2]}, + {applications, [kernel]}, + {env, Env}, + {mod, {application_starter, [ch_sup, {appinc, 41, 43}] }}, + {start_phases, [{init, [kalle]}, {takeover, []}, {go, [sune]}]}]} = + gen_server:call({global, {ch,41}}, get_pid_all_key), + ?line [{included_applications,[appinc1,appinc2]}, + {own2,val2},{own_env1,value1}] = lists:sort(Env), + + stop_node_nice(Cp1), + ok. + +%%%----------------------------------------------------------------- +%%% Testing of change of distributed parameter. +%%%----------------------------------------------------------------- +distr_changed(suite) -> [distr_changed_tc1, distr_changed_tc2]. + +distr_changed_tc1(suite) -> []; +distr_changed_tc1(doc) -> ["Test change of distributed parameter."]; +distr_changed_tc1(Conf) when is_list(Conf) -> + + {OldKernel, OldEnv, {Cp1, Cp2, Cp3}, {_Ncp1, _Ncp2, _Ncp3}, _Config2} = + distr_changed_prep(Conf), + + ?line NewDist = {distributed, [{app1, [Cp3]}, + {app2, 5000, [Cp2]}, + {app3, [Cp3, {Cp1, Cp2}]}, + {app6, [Cp1, {Cp3, Cp2}]}, + {app7, 1000, [Cp3]}, + {app8, [Cp1, {Cp2, Cp3}]}]}, + + ?line NewKernel = [{kernel, lists:keyreplace(distributed, 1, OldKernel, NewDist)}], + ?line ok = rpc:call(Cp1, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + ?line ok = rpc:call(Cp2, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + ?line ok = rpc:call(Cp3, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + + ?line {[ok,ok,ok],[]} = + rpc:multicall([Cp1, Cp2, Cp3], + application_controller, config_change, [OldEnv]), + + ?line test_server:sleep(7000), + + ?line DcInfo1 = rpc:call(Cp1, dist_ac, info, []), + ?line DcInfo2 = rpc:call(Cp2, dist_ac, info, []), + ?line DcInfo3 = rpc:call(Cp3, dist_ac, info, []), + + ?line DcWa1 = which_applications(Cp1), + ?line DcWa2 = which_applications(Cp2), + ?line DcWa3 = which_applications(Cp3), + + ?line Wa1 = lists:foldl(fun({A1, _N1, _V1}, AccIn) -> [A1 | AccIn] end, + [], DcWa1), + ?line Wa2 = lists:foldl(fun({A2, _N2, _V2}, AccIn) -> [A2 | AccIn] end, + [], DcWa2), + ?line Wa3 = lists:foldl(fun({A3, _N3, _V3}, AccIn) -> [A3 | AccIn] end, + [], DcWa3), + ?line case lists:sort(Wa1) of + [app1, app2, app3, kernel, stdlib] -> + ok; + EWa1 -> + X1 = io_lib:format("distribution error: Cp1 ~p ",[EWa1]), + ?line test_server:fail(lists:flatten(X1)) + end, + + ?line case lists:sort(Wa2) of + [app6, app8, kernel, stdlib] -> + ok; + EWa2 -> + X2 = io_lib:format("distribution error: Cp2 ~p ",[EWa2]), + ?line test_server:fail(lists:flatten(X2)) + end, + + ?line case lists:sort(Wa3) of + [app7, kernel, stdlib] -> + ok; + EWa3 -> + X3 = io_lib:format("distribution error: Cp3 ~p ",[EWa3]), + ?line test_server:fail(lists:flatten(X3)) + end, + + ?line DcInfo1n = rpc:call(Cp1, dist_ac, info, []), + ?line DcInfo2n = rpc:call(Cp2, dist_ac, info, []), + ?line DcInfo3n = rpc:call(Cp3, dist_ac, info, []), + + %% Added afterwards. Got rid of some warnings for unused variables. + ?line true = DcInfo1 =:= DcInfo1n, + ?line true = DcInfo2 =:= DcInfo2n, + ?line true = DcInfo3 =:= DcInfo3n, + + stop_node_nice(Cp1), + stop_node_nice(Cp2), + stop_node_nice(Cp3), + + ?line ok = file:delete("dc.boot"), + ?line ok = file:delete("dc.rel"), + ?line ok = file:delete("dc.script"), + + ok. + +distr_changed_tc2(suite) -> []; +distr_changed_tc2(doc) -> ["Test change of distributed parameter, " + "move appls by crashing a node."]; +distr_changed_tc2(Conf) when is_list(Conf) -> + + {OldKernel, OldEnv, {Cp1, Cp2, Cp3}, {Ncp1, _Ncp2, _Ncp3}, Config2} = + distr_changed_prep(Conf), + + ?line NewDist = {distributed, [{app1, [Cp3]}, + {app2, 5000, [Cp2]}, + {app3, [Cp3, {Cp1, Cp2}]}, + {app6, [Cp1, {Cp3, Cp2}]}, + {app7, 1000, [Cp3]}, + {app8, [Cp1, {Cp2, Cp3}]}]}, + + ?line NewKernel = [{kernel, lists:keyreplace(distributed, 1, OldKernel, NewDist)}], + ?line ok = rpc:call(Cp1, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + ?line ok = rpc:call(Cp2, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + ?line ok = rpc:call(Cp3, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + + ?line {[ok,ok,ok],[]} = + rpc:multicall([Cp1, Cp2, Cp3], + application_controller, config_change, [OldEnv]), + + ?line test_server:sleep(4000), + ?line stop_node_nice(Cp1), + ?line test_server:sleep(10000), + +% ?line _DcInfo1 = rpc:call(Cp1, dist_ac, info, []), + ?line _DcInfo2 = rpc:call(Cp2, dist_ac, info, []), + ?line _DcInfo3 = rpc:call(Cp3, dist_ac, info, []), +% ?t:format(0,"#### DcInfo1 ~n~p~n",[_DcInfo1]), + +% ?line DcWa1 = which_applications(Cp1), + ?line DcWa2 = which_applications(Cp2), + ?line DcWa3 = which_applications(Cp3), + +% ?line Wa1 = lists:foldl(fun({A1, _N1, _V1}, AccIn) -> [A1 | AccIn] end, +% [], DcWa1), + ?line Wa2 = lists:foldl(fun({A2, _N2, _V2}, AccIn) -> [A2 | AccIn] end, + [], DcWa2), + ?line Wa3 = lists:foldl(fun({A3, _N3, _V3}, AccIn) -> [A3 | AccIn] end, + [], DcWa3), + + + ?line case lists:sort(Wa2) of + [app2, app6, app8, kernel, stdlib] -> + ok; + EWa2 -> + X2 = io_lib:format("distribution error: Cp2 ~p ",[EWa2]), + ?line test_server:fail(lists:flatten(X2)) + end, + + ?line case lists:sort(Wa3) of + [app1, app3, app7, kernel, stdlib] -> + ok; + EWa3 -> + X3 = io_lib:format("distribution error: Cp3 ~p ",[EWa3]), + ?line test_server:fail(lists:flatten(X3)) + end, + + + ?line {ok, Cp1} = start_node_boot(Ncp1, Config2, dc), + ?line test_server:sleep(10000), + + ?line _DcInfo1rs = rpc:call(Cp1, dist_ac, info, []), + ?line _DcInfo2rs = rpc:call(Cp2, dist_ac, info, []), + ?line _DcInfo3rs = rpc:call(Cp3, dist_ac, info, []), + + ?line DcWa1rs = which_applications(Cp1), + ?line DcWa2rs = which_applications(Cp2), + ?line DcWa3rs = which_applications(Cp3), + + ?line Wa1rs = lists:foldl(fun({A1, _N1, _V1}, AccIn) -> [A1 | AccIn] end, + [], DcWa1rs), + ?line Wa2rs = lists:foldl(fun({A2, _N2, _V2}, AccIn) -> [A2 | AccIn] end, + [], DcWa2rs), + ?line Wa3rs = lists:foldl(fun({A3, _N3, _V3}, AccIn) -> [A3 | AccIn] end, + [], DcWa3rs), + + ?line case lists:sort(Wa1rs) of + [app6, app8, kernel, stdlib] -> + ok; + EWa1rs -> + X1rs = io_lib:format("distribution error: Cp1 ~p ",[EWa1rs]), + ?line test_server:fail(lists:flatten(X1rs)) + end, + + ?line case lists:sort(Wa2rs) of + [app2, kernel, stdlib] -> + ok; + EWa2rs -> + X2rs = io_lib:format("distribution error: Cp2 ~p ",[EWa2rs]), + ?line test_server:fail(lists:flatten(X2rs)) + end, + + ?line case lists:sort(Wa3rs) of + [app1, app3, app7, kernel, stdlib] -> + ok; + EWa3rs -> + X3rs = io_lib:format("distribution error: Cp3 ~p ",[EWa3rs]), + ?line test_server:fail(lists:flatten(X3rs)) + end, + + + stop_node_nice(Cp1), + stop_node_nice(Cp2), + stop_node_nice(Cp3), + + ?line ok = file:delete("dc.boot"), + ?line ok = file:delete("dc.rel"), + ?line ok = file:delete("dc.script"), + + ok. + + + +%%%----------------------------------------------------------------- +%%% Testing of application configuration change +%%%----------------------------------------------------------------- +config_change(suite) -> + []; +config_change(doc) -> + ["Test change of application configuration"]; +config_change(Conf) when is_list(Conf) -> + + %% Change to data_dir + ?line {ok, CWD} = file:get_cwd(), + ?line DataDir = ?config(data_dir, Conf), + ?line ok = file:set_cwd(DataDir), + + %% Find out application data from boot script + ?line Boot = filename:join([code:root_dir(), "bin", "start.boot"]), + ?line {ok, Bin} = file:read_file(Boot), + ?line Appls = get_appls(binary_to_term(Bin)), + + %% Simulate contents of "sys.config" + ?line Config = [{stdlib, [{par1,sys},{par2,sys}]}, + "t1", + "t2.config", + filename:join([DataDir, "subdir", "t3"]), + {stdlib, [{par6,sys}]}], + + %% Order application_controller to update configuration + ?line ok = application_controller:change_application_data(Appls, + Config), + + %% Check that stdlib parameters are correctly set + ?line Env = application:get_all_env(stdlib), + ?line {value, {par1,sys}} = lists:keysearch(par1, 1, Env), + ?line {value, {par2,t1}} = lists:keysearch(par2, 1, Env), + ?line {value, {par3,t1}} = lists:keysearch(par3, 1, Env), + ?line {value, {par4,t2}} = lists:keysearch(par4, 1, Env), + ?line {value, {par5,t3}} = lists:keysearch(par5, 1, Env), + ?line {value, {par6,sys}} = lists:keysearch(par6, 1, Env), + + ?line ok = file:set_cwd(CWD). + +%% This function is stolen from SASL module release_handler, OTP R10B +get_appls({script, _, Script}) -> + get_appls(Script, []). + +%% kernel is taken care of separately +get_appls([{kernelProcess, application_controller, + {application_controller, start, [App]}} | T], Res) -> + get_appls(T, [App | Res]); +%% other applications but kernel +get_appls([{apply, {application, load, [App]}} | T], Res) -> + get_appls(T, [App | Res]); +get_appls([_ | T], Res) -> + get_appls(T, Res); +get_appls([], Res) -> + Res. + +%%%----------------------------------------------------------------- +%%% Tests the 'shutdown_func' kernel config parameter +%%%----------------------------------------------------------------- +shutdown_func(suite) -> + []; +shutdown_func(doc) -> + ["Tests the 'shutdown_func' kernel config parameter"]; +shutdown_func(Config) when is_list(Config) -> + ?line {ok,Cp1} = start_node(?MODULE_STRING++"_shutdown_func"), + ?line wait_for_ready_net(), + ?line Tag = make_ref(), + ?line ok = rpc:call(Cp1, application, set_env, + [kernel, shutdown_func, {?MODULE, do_shutdown}]), + ?line ok = rpc:call(Cp1, application, set_env, + [kernel, shutdown_func_test, {self(), Tag}]), + ?line _ = rpc:call(Cp1, init, stop, []), + ?line receive + {Pid, Tag, shutting_down, shutdown} -> + ?line Mref = erlang:monitor(process, Pid), + ?line Pid ! {self(), Tag, ok}, + receive + {'DOWN', Mref, _, Pid, noconnection} -> + ok + after 10000 -> + test_server:fail(timeout) + end + after 10000 -> + test_server:fail(timeout) + end. + + + +do_shutdown(Reason) -> + {ok, {Pid, Tag}} = application:get_env(kernel, shutdown_func_test), + Pid ! {self(), Tag, shutting_down, Reason}, + receive + {Pid, Tag, ok} -> ok + end. + + + +%%----------------------------------------------------------------- +%% Utility functions +%%----------------------------------------------------------------- +app0() -> + {application, app0, + [{description, "ERTS CXC 138 10"}, + {vsn, "2.0"}, + {modules, []}, + {registered, []}, + {applications, [kernel]}, + {mod, {ch_sup, {app0, 77, 80}}}]}. + +app1() -> + {application, app1, + [{description, "ERTS CXC 138 10"}, + {vsn, "2.0"}, + {modules, []}, + {registered, []}, + {applications, [kernel]}, + {mod, {ch_sup, {app1, 1, 3}}}]}. + +app2() -> + {application, app2, + [{description, "ERTS CXC 138 10"}, + {vsn, "2.0"}, + {modules, []}, + {registered, []}, + {applications, [kernel]}, + {mod, {ch_sup, {app2, 4, 6}}}]}. + +app3() -> + {application, app3, + [{description, "ERTS CXC 138 10"}, + {vsn, "2.0"}, + {modules, []}, + {registered, []}, + {applications, [kernel]}, + {mod, {ch_sup, {app3, 7, 9}}}]}. + +app4() -> + {application, app4, + [{description, "ERTS CXC 138 10"}, + {vsn, "2.0"}, + {applications, [kernel]}, + {included_applications, [app5]}, + {mod, {ch_sup, {app3, 7, 9}}}]}. + +app5() -> + {application, app5, + [{description, "ERTS CXC 138 10"}, + {vsn, "2.0"}, + {applications, [kernel]}, + {mod, {ch_sup, {app3, 7, 9}}}]}. + +app6() -> + {application, app6, + [{description, "ERTS CXC 138 10"}, + {vsn, "2.0"}, + {modules, []}, + {registered, []}, + {applications, [kernel]}, + {mod, {ch_sup, {app6, 10, 12}}}]}. + +app7() -> + {application, app7, + [{description, "ERTS CXC 138 10"}, + {vsn, "2.0"}, + {modules, []}, + {registered, []}, + {applications, [kernel]}, + {mod, {ch_sup, {app7, 13, 15}}}]}. + +app8() -> + {application, app8, + [{description, "ERTS CXC 138 10"}, + {vsn, "2.0"}, + {modules, []}, + {registered, []}, + {applications, [kernel]}, + {mod, {ch_sup, {app7, 16, 18}}}]}. + +app9() -> + {application, app9, + [{description, "ERTS CXC 138 10"}, + {vsn, "2.0"}, + {modules, []}, + {registered, []}, + {applications, [kernel]}, + {mod, {ch_sup, {app9, 19, 19}}}]}. + +app10_dep9() -> + {application, app10, + [{description, "ERTS CXC 138 10"}, + {vsn, "2.0"}, + {modules, []}, + {registered, []}, + {applications, [kernel, app9]}, + {mod, {ch_sup, {app10, 20, 20}}}]}. + +appinc() -> + {application, appinc, + [{description, "Test of new app file, including appnew"}, + {id, "CXC 138 ai"}, + {vsn, "2.0"}, + {applications, [kernel]}, + {modules, []}, + {registered, []}, + {env, [{own_env1, value1}, {own2, val2}]}, + {included_applications, [appinc1, appinc2]}, + {start_phases, [{init, [kalle]}, {takeover, []}, {go, [sune]}]}, + {mod, {application_starter, [ch_sup, {appinc, 41, 43}] }}]}. + + +app_sp() -> + {application, app_sp, + [{description, "ERTS CXC 138 10"}, + {vsn, "2.0"}, + {start_phases, [{init, [kurt]}, {go, [sune]}]}, + {applications, [kernel]}, + {modules, []}, + {registered, []}, + {mod, {application_starter, [ch_sup, {app_sp, 31, 33}] }}]}. + +app_trans_normal() -> + {application, trans_normal, + [{description, "A CXC 138 11"}, + {vsn, "1.0"}, + {modules, [{transient, 1}, {trans_normal_sup,1}]}, + {registered, [trans_normal_sup]}, + {applications, [kernel, stdlib]}, + {mod, {trans_normal_sup, []}}]}. + +app_trans_abnormal() -> + {application, trans_abnormal, + [{description, "A CXC 138 11"}, + {vsn, "1.0"}, + {modules, [{transient, 1}, {trans_abnormal_sup,1}]}, + {registered, [trans_abnormal_sup]}, + {applications, [kernel, stdlib]}, + {mod, {trans_abnormal_sup, []}}]}. + +app_start_error() -> + {application, app_start_error, + [{description, "ERTS CXC 138 10"}, + {vsn, "2.0"}, + {modules, []}, + {registered, []}, + {applications, [kernel]}, + {mod, {app_start_error, []}}]}. + +app_group_leader() -> + {application, group_leader, + [{description, "GROUP_LEADER CXC 138 11"}, + {vsn, "1.0"}, + {modules, [group_leader,group_leader_sup]}, + {registered, [group_leader_sup]}, + {applications, [kernel,stdlib]}, + {mod, {group_leader_sup, []}}]}. + + +d1([Ncp1, Ncp2, Ncp3]) -> + M = from($@, atom_to_list(node())), + {app1, [list_to_atom(Ncp1 ++ "@" ++ M), + list_to_atom(Ncp2 ++ "@" ++ M), + list_to_atom(Ncp3 ++ "@" ++ M)]}. + +d2([Ncp1, _Ncp2, Ncp3]) -> + M = from($@, atom_to_list(node())), + {app1, [list_to_atom(Ncp1 ++ "@" ++ M), + list_to_atom(Ncp3 ++ "@" ++ M)]}. + +d3([Ncp1, Ncp2, Ncp3]) -> + M = from($@, atom_to_list(node())), + {appinc, [list_to_atom(Ncp1 ++ "@" ++ M), + list_to_atom(Ncp2 ++ "@" ++ M), + list_to_atom(Ncp3 ++ "@" ++ M)]}. + +d_any3(Any, [Ncp1, Ncp2, Ncp3]) -> + M = from($@, atom_to_list(node())), + {Any, [list_to_atom(Ncp1 ++ "@" ++ M), + list_to_atom(Ncp2 ++ "@" ++ M), + list_to_atom(Ncp3 ++ "@" ++ M)]}. + + +config([Ncp1, Ncp2, Ncp3]) -> + fun(Fd, SyncNodesTimeout) -> + M = from($@, atom_to_list(node())), + io:format(Fd, "[{kernel, [{sync_nodes_optional, " + "['~s@~s','~s@~s','~s@~s']}," + "{sync_nodes_timeout, ~w}," + "{distributed, [{app1, ['~s@~s', '~s@~s', '~s@~s']}," + "{app2, 1000, ['~s@~s', '~s@~s', '~s@~s']}," + "{app3, 1000, [{'~s@~s', '~s@~s'}, '~s@~s']}]}]}].~n", + [Ncp1, M, Ncp2, M, Ncp3, M, + SyncNodesTimeout, Ncp1, M, Ncp2, M, Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M]) + end. + +config2([Ncp1, Ncp2, Ncp3]) -> + fun(Fd, SyncNodesTimeout) -> + M = from($@, atom_to_list(node())), + io:format(Fd, "[{kernel, [{sync_nodes_optional, " + "['~s@~s','~s@~s','~s@~s']}," + "{sync_nodes_timeout, ~w}," + "{permissions, [{app3, false}]}," + "{distributed, [{app1, ['~s@~s', '~s@~s', '~s@~s']}," + "{app2, 10000, ['~s@~s', '~s@~s', '~s@~s']}," + "{app3, 5000, [{'~s@~s', '~s@~s'}, '~s@~s']}]}]}].~n", + [Ncp1, M, Ncp2, M, Ncp3, M, + SyncNodesTimeout, + Ncp1, M, Ncp2, M, Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M]) + end. + +config3([Ncp1, Ncp2, Ncp3]) -> + fun(Fd, SyncNodesTimeout) -> + M = from($@, atom_to_list(node())), + io:format(Fd, "[{kernel, [{sync_nodes_optional, " + "['~s@~s','~s@~s','~s@~s']}," + "{sync_nodes_timeout, ~w}," + "{start_dist_ac, true}," + "{permissions, [{app3, false}]}]}].~n", + [Ncp1, M, Ncp2, M, Ncp3, M, + SyncNodesTimeout]) + end. + +config4([Ncp1, Ncp2]) -> + fun(Fd, SyncNodesTimeout) -> + M = from($@, atom_to_list(node())), + io:format(Fd, "[{kernel, [{sync_nodes_optional, " + "['~s@~s','~s@~s']}," + "{sync_nodes_timeout, ~w}," + "{start_dist_ac, true}," + "{distributed, [{app1, ['~s@~s', '~s@~s']}]}]}].~n", + [Ncp1, M, Ncp2, M, SyncNodesTimeout, + Ncp1, M, Ncp2, M]) + end. + +config3184([Ncp1, Ncp2]) -> + fun(Fd, SyncNodesTimeout) -> + M = from($@, atom_to_list(node())), + io:format(Fd, "[{kernel, [{sync_nodes_optional, " + "['~s@~s','~s@~s']}," + "{sync_nodes_timeout, ~w}," + "{permissions, [{app1, false}]}," + "{distributed, [{app1, ['~s@~s', '~s@~s']}]}]}].~n", + [Ncp1, M, Ncp2, M, SyncNodesTimeout, + Ncp1, M, Ncp2, M]) + end. + +config_perm(Fd) -> + io:format(Fd, "[{kernel, [{permissions, " + "[{app1, false}, {app2, false}, {app3, false}]} ]}].~n",[]). + +config_perm2([Ncp1, Ncp2, Ncp3]) -> + fun(Fd, SyncNodesTimeout) -> + M = from($@, atom_to_list(node())), + io:format(Fd, "[{kernel, [{sync_nodes_optional, " + "['~s@~s','~s@~s','~s@~s']}," + "{sync_nodes_timeout, ~w}," + "{permissions, [{app1, false}, {app2, false}, {app3, false}]}," + "{distributed, [{app1, ['~s@~s', '~s@~s', '~s@~s']}," + "{app2, 10000, ['~s@~s', '~s@~s', '~s@~s']}," + "{app3, 5000, [{'~s@~s', '~s@~s'}, '~s@~s']}]}]}].~n", + [Ncp1, M, Ncp2, M, Ncp3, M, + SyncNodesTimeout, + Ncp1, M, Ncp2, M, Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M]) + end. + +config_inc([Ncp1, Ncp2, Ncp3]) -> + fun(Fd, SyncNodesTimeout) -> + M = from($@, atom_to_list(node())), + io:format(Fd, "[{kernel, [{sync_nodes_optional, " + "['~s@~s','~s@~s','~s@~s']}," + "{sync_nodes_timeout, ~w}," + "{distributed, [{appinc, ['~s@~s', '~s@~s', '~s@~s']}," + "{app2, 10000, ['~s@~s', '~s@~s', '~s@~s']}," + "{app3, 5000, [{'~s@~s', '~s@~s'}, '~s@~s']}]}]}].~n", + [Ncp1, M, Ncp2, M, Ncp3, M, + SyncNodesTimeout, + Ncp1, M, Ncp2, M, Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M]) + end. + +config_sf([Ncp1, Ncp2, Ncp3]) -> + fun(Fd, SyncNodesTimeout) -> + M = from($@, atom_to_list(node())), + io:format(Fd, "[{kernel, [{sync_nodes_optional, " + "['~s@~s','~s@~s','~s@~s']}," + "{sync_nodes_timeout, ~w}," + "{distributed, [{myApp, ['~s@~s', '~s@~s', '~s@~s']}," + "{topApp, ['~s@~s', '~s@~s', '~s@~s']}," + "{inclOne, ['~s@~s', '~s@~s', '~s@~s']}," + "{inclTwo, ['~s@~s', '~s@~s', '~s@~s']}," + "{inclTwoTop, ['~s@~s', '~s@~s', '~s@~s']}," + "{incl2A, ['~s@~s', '~s@~s', '~s@~s']}," + "{incl2B, ['~s@~s', '~s@~s', '~s@~s']}," + "{with, ['~s@~s', '~s@~s', '~s@~s']}," + "{wrapper, ['~s@~s', '~s@~s', '~s@~s']}]}]}].~n", + [Ncp1, M, Ncp2, M, Ncp3, M, + SyncNodesTimeout, + Ncp1, M, Ncp2, M, Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M]) + end. + +config_fo([Ncp1, Ncp2, Ncp3]) -> + fun(Fd, SyncNodesTimeout) -> + M = from($@, atom_to_list(node())), + io:format(Fd, "[{kernel, [{sync_nodes_optional, " + "['~s@~s','~s@~s','~s@~s']}," + "{sync_nodes_timeout, ~w}," + "{distributed, [{app1, ['~s@~s', '~s@~s', '~s@~s']}," + "{app2, 2000, ['~s@~s', '~s@~s', '~s@~s']}," + "{app_sp, 1000, [{'~s@~s', '~s@~s'}, '~s@~s']}]}]}].~n", + [Ncp1, M, Ncp2, M, Ncp3, M, + SyncNodesTimeout, + Ncp1, M, Ncp2, M, Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M]) + end. + +config_dc([Ncp1, Ncp2, Ncp3]) -> + fun(Fd, SyncNodesTimeout) -> + M = from($@, atom_to_list(node())), + io:format(Fd, "[{kernel, [{sync_nodes_optional, " + "['~s@~s','~s@~s','~s@~s']}," + "{sync_nodes_timeout, ~w}," + "{distributed, [{app1, ['~s@~s', '~s@~s']}," + " {app2, 10000, ['~s@~s']}," + " {app3, [{'~s@~s', '~s@~s'}]}, " + " {app6, [{'~s@~s', '~s@~s'}]}, " + " {app7, ['~s@~s']}, " + " {app8, ['~s@~s', {'~s@~s', '~s@~s'}]}" + " ]}]}].~n", + [Ncp1, M, Ncp2, M, Ncp3, M, + SyncNodesTimeout, + Ncp1, M, Ncp2, M, + Ncp1, M, + Ncp1, M, Ncp2, M, + Ncp3, M, Ncp2, M, + Ncp3, M, + Ncp2, M, Ncp1, M, Ncp3, M]) + end. + +config_dc2([Ncp1, Ncp2, Ncp3]) -> + fun(Fd) -> + M = from($@, atom_to_list(node())), + io:format(Fd, "[{kernel, [{sync_nodes_optional, " + "['~s@~s','~s@~s','~s@~s']}," + "{sync_nodes_timeout, 10000}," + "{distributed, [{app1, ['~s@~s']}," + " {app2, 5000, ['~s@~s']}," + " {app3, ['~s@~s', {'~s@~s', '~s@~s'}]}, " + " {app6, ['~s@~s', {'~s@~s', '~s@~s'}]}, " + " {app7, 1000, ['~s@~s']}, " + " {app8, ['~s@~s', {'~s@~s', '~s@~s'}]}" + " ]}]}].~n", + [Ncp1, M, Ncp2, M, Ncp3, M, + Ncp3, M, + Ncp2, M, + Ncp3, M, Ncp1, M, Ncp2, M, + Ncp1, M, Ncp3, M, Ncp2, M, + Ncp3, M, + Ncp1, M, Ncp2, M, Ncp3, M]) + end. + +w_app1(Fd) -> + io:format(Fd, "~p.\n", [app1()]). + +w_app2(Fd) -> + io:format(Fd, "~p.\n", [app2()]). + +w_app3(Fd) -> + io:format(Fd, "~p.\n", [app3()]). + +w_app5(Fd) -> + io:format(Fd, "~p.\n", [app5()]). + +w_app6(Fd) -> + io:format(Fd, "~p.\n", [app6()]). + +w_app7(Fd) -> + io:format(Fd, "~p.\n", [app7()]). + +w_app8(Fd) -> + io:format(Fd, "~p.\n", [app8()]). + +w_app_start_error(Fd) -> + io:format(Fd, "~p.\n", [app_start_error()]). + +w_app(Fd, AppData) -> + io:format(Fd, "~p.\n", [AppData]). + +from(H, [H | T]) -> T; +from(H, [_ | T]) -> from(H, T); +from(_H, []) -> []. + +is_loaded(Name, [Node | Nodes]) -> + Apps = rpc:call(Node, application, loaded_applications, []), + case lists:keysearch(Name, 1, Apps) of + {value, _} -> is_loaded(Name, Nodes); + false -> false + end; +is_loaded(_Name, []) -> + true; +is_loaded(Name, Node) -> + is_loaded(Name, [Node]). + +is_started(Name, Node) -> + Apps = which_applications(Node), + case lists:keysearch(Name, 1, Apps) of + {value, _} -> true; + false -> false + end. + +% Waits until application Name is started on at least one node. +wait_until_started(Name, Nodes) -> + case lists:member(true, + lists:map(fun (N) -> + is_started(Name, N) + end, + Nodes)) of + true -> + true; + false -> + test_server:sleep(500), + wait_until_started(Name, Nodes) + end. + +% Waits until application Name is stopped on all nodes. +wait_until_stopped(Name, Nodes) -> + case lists:member(true, + lists:map(fun (N) -> + is_started(Name, N) + end, + Nodes)) of + false -> + true; + true -> + test_server:sleep(500), + wait_until_stopped(Name, Nodes) + end. + +%% The test server has no support for starting nodes in parallel. To +%% avoid long delays a small sync_nodes_timeout is used. Use this +%% function when starting all nodes but the last one, and when +%% restarting nodes (then use global:sync() to synchronize). +config_fun_fast(SysConfigFun) -> + fun(Fd) -> SysConfigFun(Fd, 1) end. + +config_fun(SysConfigFun) -> + fun(Fd) -> SysConfigFun(Fd, 10000) end. + +start_node_config(Name, SysConfigFun, Conf) -> + ConfigFile = write_config_file(SysConfigFun, Conf), + start_node(Name, ConfigFile, ""). + +start_node(Name) -> + Pa = filename:dirname(code:which(?MODULE)), + test_server:start_node(Name, slave, [{args, " -pa " ++ Pa}]). + +start_node(Name, ConfigFile) -> + start_node(Name, ConfigFile, ""). + +start_node(Name, ConfigFile, ExtraArgs) -> + Pa = filename:dirname(code:which(?MODULE)), + test_server:start_node(Name, slave, [{args, + " -pa " ++ Pa ++ + " -config " ++ ConfigFile ++ + ExtraArgs}]). + +start_node_with_cache(Name, SysConfigFun, Conf) -> + ConfigFile = write_config_file(SysConfigFun, Conf), + start_node(Name, ConfigFile, " -code_path_cache"). + +start_node_args(Name, Args) -> + Pa = filename:dirname(code:which(?MODULE)), + test_server:start_node(Name, slave, [{args, " -pa " ++ Pa ++ " " ++ Args}]). + +start_node_boot_3002(Name, Boot) -> + Pa = filename:dirname(code:which(?MODULE)), + ?t:format(0, "start_node_boot ~p~n", + [" -pa " ++ Pa ++ " -env ERL_CRASH_DUMP erl_crash_dump." ++ + atom_to_list(Name) ++ " -boot " ++ Boot ++ + " -sasl dummy \"missing "]), + test_server:start_node(Name, slave, + [{args, " -pa " ++ Pa ++ + " -env ERL_CRASH_DUMP erl_crash_dump." ++ + atom_to_list(Name) ++ " -boot " ++ Boot ++ + " -sasl dummy \"missing "}]). + +start_node_boot_config(Name, SysConfigFun, Conf, Boot) -> + ConfigFile = write_config_file(SysConfigFun, Conf), + start_node(Name, ConfigFile, " -boot " ++ atom_to_list(Boot)). + +start_node_boot(Name, Config, Boot) -> + Pa = filename:dirname(code:which(?MODULE)), + ?t:format(0, "start_node_boot ~p~n",[" -pa " ++ Pa ++ " -config " ++ Config ++ + " -boot " ++ atom_to_list(Boot)]), + test_server:start_node(Name, slave, [{args, " -pa " ++ Pa ++ " -config " ++ Config ++ + " -boot " ++ atom_to_list(Boot)}]). + +start_node_config_sf(Name, SysConfigFun, Conf) -> + ConfigFile = write_config_file(SysConfigFun, Conf), + DataDir = ?config(data_dir, Conf), % is it used? + start_node(Name, ConfigFile, " -pa " ++ DataDir). + +write_config_file(SysConfigFun, Conf) -> + Dir = ?config(priv_dir, Conf), + {ok, Fd} = file:open(filename:join(Dir, "sys.config"), write), + SysConfigFun(Fd), + file:close(Fd), + filename:join(Dir,"sys"). + +node_names(Names, Config) -> + [node_name(Name, Config) || Name <- Names]. + +node_name(Name, Config) -> + U = "_", + {{Y,M,D}, {H,Min,S}} = calendar:now_to_local_time(now()), + Date = io_lib:format("~4w_~2..0w_~2..0w__~2..0w_~2..0w_~2..0w", + [Y,M,D, H,Min,S]), + L = lists:flatten(Date), + lists:concat([Name,U,?testcase,U,U,L]). + +stop_node_nice(Node) when is_atom(Node) -> + ?line test_server:stop_node(Node); +stop_node_nice(Nodes) when is_list(Nodes) -> + ?line lists:foreach(fun (N) -> stop_node_nice(N) end, Nodes). + + +get_start_type(Expected) -> + get_start_type(Expected, 30*5, #st{}). + +get_start_type(_Expected, 0, Ack) -> + test_server:format("====== ~p ======~n", [Ack]), + test_server:fail(not_valid_start_type); +get_start_type(Expected, Times, Ack0) -> + #st{normal = N0, local = L0, takeover = T0, failover = F0} = Ack0, + global:send(st_type, {st, read, self()}), + receive + {st, N, L, T, F} -> + Ack = #st{normal = N0 + N, local = L0 + L, + takeover = T0 + T, failover = F0 + F}, + if + Ack =:= Expected -> + ok; + true -> + timer:sleep(200), + get_start_type(Expected, Times-1, Ack) + end + after 30*1000 -> + get_start_type(Expected, 0, Ack0) + end. + +start_type() -> + st(0, 0, 0, 0). + +st(Normal, Local, Takeover, Failover) -> + receive + {st, normal} -> + st(Normal+1, Local, Takeover, Failover); + {st, local} -> + st(Normal, Local+1, Takeover, Failover); + {st, takeover} -> + st(Normal, Local, Takeover+1, Failover); + {st, failover} -> + st(Normal, Local, Takeover, Failover+1); + {st, read, From} -> + From ! {st, Normal, Local, Takeover, Failover}, + st(0, 0, 0, 0); + kill -> + exit(normal) + end. + + +get_start_phase(Expected) -> + global:send(start_phase, {sp, read, self()}), + receive + Expected -> + ok; + {sp, T1, I1, So1, Sp1, G1} -> + test_server:format("=============== {sp,T,I,So,Sp,G} ~p ~n",[" "]), + test_server:format("=========== got ~p ~n", + [{sp, T1, I1, So1, Sp1, G1}]), + test_server:format("====== expected ~p ~n", [Expected]), + test_server:fail(not_valid_start_phase) + after 5000 -> + test_server:fail(not_valid_start_phase) + end. + +start_phase() -> + sp(0, 0, 0, 0, 0). + +sp(Top, Init, Some, Spec, Go) -> + receive + {sp, top} -> + sp(Top+1, Init, Some, Spec, Go); + {sp, init} -> + sp(Top, Init+1, Some, Spec, Go); + {sp, some} -> + sp(Top, Init, Some+1, Spec, Go); + {sp, spec} -> + sp(Top, Init, Some, Spec+1, Go); + {sp, go} -> + sp(Top, Init, Some, Spec, Go+1); + {sp, read, From} -> + From ! {sp, Top, Init, Some, Spec, Go}, + sp(0, 0, 0, 0, 0); + kill -> + exit(normal) + end. + +get_conf_change(Expected) -> + global:send(conf_change, {cc, read, self()}), + receive + {cc, Expected} -> + ok; + {cc, List} -> + ?line test_server:format("====== ~p ======~n",[{cc, List}]), + ?line test_server:fail(not_valid_conf_change) + after 5000 -> + ?line test_server:fail(not_valid_conf_change_to) + end. + +conf_change() -> + cc([]). + +cc(List) -> + receive + {cc, New} -> + cc(List ++ New); + {cc, read, From} -> + From ! {cc, List}, + cc([]); + kill -> + exit(normal) + end. + + + +create_app() -> + ?line Dir = "./", + ?line App1 = Dir ++ "app1", + ?line {ok, Fd1} = file:open(App1++".app",write), + ?line io:format(Fd1, "~p. \n", [app1()]), + ?line file:close(Fd1), + ?line App2 = Dir ++ "app2", + ?line {ok, Fd2} = file:open(App2++".app",write), + ?line io:format(Fd2, "~p. \n", [app2()]), + ?line file:close(Fd2), + ?line App3 = Dir ++ "app_sp", + ?line {ok, Fd3} = file:open(App3++".app",write), + ?line io:format(Fd3, "~p. \n", [app_sp()]), + ?line file:close(Fd3), + ok. + + +create_script(ScriptName) -> + ?line Dir = "./", + ?line Name = Dir ++ ScriptName, + ?line Apps = 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" + " {app1, \"2.0\"}, {app2, \"2.0\"}, {app_sp, \"2.0\"}]}.\n", + [KernelVer,StdlibVer]), + ?line file:close(Fd), + {{KernelVer,StdlibVer}, + {filename:dirname(Name), filename:basename(Name)}}. + + + +create_script_dc(ScriptName) -> + ?line Dir = "./", + ?line Name = Dir ++ ScriptName, + ?line Apps = 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" + " {app1, \"2.0\"}, {app2, \"2.0\"}, {app3, \"2.0\"}, \n" + " {app6, \"2.0\"}, {app7, \"2.0\"}, {app8, \"2.0\"}]}.\n", + [KernelVer,StdlibVer]), + ?line file:close(Fd), + {{KernelVer,StdlibVer}, + {filename:dirname(Name), filename:basename(Name)}}. + + +create_script_3002(ScriptName) -> + ?line Dir = "./", + ?line Name = Dir ++ ScriptName, + ?line Apps = which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line {value,{_,_,SaslVer}} = lists:keysearch(sasl,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" + " {sasl, \"~s\"}]}.\n", + [KernelVer, StdlibVer, SaslVer]), + ?line file:close(Fd), + {{KernelVer,StdlibVer}, + {filename:dirname(Name), filename:basename(Name)}}. + + + +distr_changed_prep(Conf) when is_list(Conf) -> + + % Write .app files + ?line {ok, Fd1} = file:open("app1.app", write), + ?line w_app1(Fd1), + ?line file:close(Fd1), + ?line {ok, Fd2} = file:open("app2.app", write), + ?line w_app2(Fd2), + ?line file:close(Fd2), + ?line {ok, Fd3} = file:open("app3.app", write), + ?line w_app3(Fd3), + ?line file:close(Fd3), + ?line {ok, Fd4} = file:open("app6.app", write), + ?line w_app6(Fd4), + ?line file:close(Fd4), + ?line {ok, Fd5} = file:open("app7.app", write), + ?line w_app7(Fd5), + ?line file:close(Fd5), + ?line {ok, Fd6} = file:open("app8.app", write), + ?line w_app8(Fd6), + ?line file:close(Fd6), + + + % Create the .app files and the boot script + ?line {{KernelVer,StdlibVer}, _} = create_script_dc("dc"), + + ?line case is_real_system(KernelVer, StdlibVer) of + true -> + Options = []; + false -> + Options = [local] + end, + + ?line ok = systools:make_script("dc", Options), + + NodeNames = [Ncp1, Ncp2, Ncp3] = node_names([cp1, cp2, cp3], Conf), + NoSyncTime = config_fun_fast(config_dc(NodeNames)), + WithSyncTime = config_fun(config_dc(NodeNames)), + + ?line Dir = ?config(priv_dir,Conf), + ?line {ok, Fd_dc2} = file:open(filename:join(Dir, "sys2.config"), write), + ?line (config_dc2(NodeNames))(Fd_dc2), + ?line file:close(Fd_dc2), + ?line Config2 = filename:join(Dir, "sys2"), + + % Test [cp1, cp2, cp3] + ?line {ok, Cp1} = start_node_boot_config(Ncp1, NoSyncTime, Conf, dc), + ?line {ok, Cp2} = start_node_boot_config(Ncp2, NoSyncTime, Conf, dc), + ?line {ok, Cp3} = start_node_boot_config(Ncp3, WithSyncTime, Conf, dc), + ?line global:sync(), + + %% Read the current configuration parameters, and change them + ?line OldEnv = rpc:call(Cp1, application_controller, prep_config_change, []), + ?line {value, {kernel, OldKernel}} = lists:keysearch(kernel, 1, OldEnv), + {OldKernel, OldEnv, {Cp1, Cp2, Cp3}, {Ncp1, Ncp2, Ncp3}, Config2}. + + +%%% Copied from init_SUITE.erl. +is_real_system(KernelVsn, StdlibVsn) -> + LibDir = code:lib_dir(), + case file:read_file_info(LibDir ++ "/kernel-" ++ KernelVsn) of + {ok, _} -> + case file:read_file_info(LibDir ++ "/stdlib-" ++ StdlibVsn) of + {ok, _} -> + true; + _ -> + false + end; + _ -> + false + end. + +init2973() -> + loop2973(). + + +loop2973() -> + receive + {start, From, App} -> + Res = application:start(App), + From ! {self(), res, Res}, + loop2973(); + + kill -> + exit(normal) + end. + +wait_for_ready_net() -> + Nodes = lists:sort([node() | nodes()]), + ?UNTIL(begin + lists:all(fun(N) -> Nodes =:= get_known(N) end, Nodes) and + lists:all(fun(N) -> + LNs = rpc:call(N, erlang, nodes, []), + Nodes =:= lists:sort([N | LNs]) + end, Nodes) + end). + +get_known(Node) -> + case catch gen_server:call({global_name_server,Node}, get_known) of + {'EXIT', _} -> + [list, without, nodenames]; + Known -> + lists:sort([Node | Known]) + end. + +which_applications() -> + application_controller:which_applications(infinity). + +which_applications(Node) -> + rpc:call(Node, application, which_applications, [infinity]). |