aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/test/application_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/test/application_SUITE.erl')
-rw-r--r--lib/kernel/test/application_SUITE.erl2734
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]).