%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
-module(application_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2
]).
-export([failover/1, failover_comp/1, permissions/1, load/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, get_env/1,
set_env/1, set_env_persistent/1, set_env_errors/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, persistent_env/1,
distr_changed_tc1/1, distr_changed_tc2/1,
ensure_started/1, ensure_all_started/1,
shutdown_func/1, do_shutdown/1, shutdown_timeout/1, shutdown_deadlock/1,
config_relative_paths/1]).
-define(TESTCASE, testcase_name).
-define(testcase, proplists:get_value(?TESTCASE, Config)).
-export([init_per_testcase/2, end_per_testcase/2, start_type/0,
start_phase/0, conf_change/0]).
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap,{minutes,2}}].
all() ->
[failover, failover_comp, permissions, load,
load_use_cache, ensure_started, {group, reported_bugs}, start_phases,
script_start, nodedown_start, permit_false_start_local,
permit_false_start_dist, get_key, get_env, ensure_all_started,
set_env, set_env_persistent, set_env_errors,
{group, distr_changed}, config_change, shutdown_func, shutdown_timeout,
shutdown_deadlock, config_relative_paths,
persistent_env].
groups() ->
[{reported_bugs, [],
[otp_1586, otp_2078, otp_2012, otp_2718, otp_2973,
otp_3002, otp_3184, otp_4066, otp_4227, otp_5363,
otp_5606]},
{distr_changed, [],
[distr_changed_tc1, distr_changed_tc2]}].
init_per_suite(Config) ->
Config.
end_per_suite(_Config) ->
ok.
init_per_group(_GroupName, Config) ->
Config.
end_per_group(_GroupName, Config) ->
Config.
init_per_testcase(otp_2973=Case, Config) ->
code:add_path(proplists:get_value(data_dir,Config)),
[{?TESTCASE, Case}|Config];
init_per_testcase(Case, Config) ->
[{?TESTCASE, Case}|Config].
end_per_testcase(otp_2973, Config) ->
code:del_path(proplists:get_value(data_dir,Config)),
ok;
end_per_testcase(_Case, _Config) ->
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]
%%-----------------------------------------------------------------
%% 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, []),
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]
{ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf),
{ok, Cp2} = start_node_config(Ncp2, NoSyncTime, Conf),
{ok, Cp3} = start_node_config(Ncp3, WithSyncTime, Conf),
Cps = [Cp1, Cp2, Cp3],
wait_for_ready_net(),
%% Start app1 and make sure cp1 starts it
{[ok,ok,ok],[]} =
rpc:multicall(Cps, application, load, [app1()]),
?UNTIL(is_loaded(app1, Cps)),
{[ok,ok,ok],[]} =
rpc:multicall(Cps, application, start, [app1, permanent]),
?UNTIL(is_started(app1, Cp1)),
false = is_started(app1, Cp2),
ok = get_start_type(#st{normal = 3}),
%% Stop cp1 and make sure cp2 starts app1
stop_node_nice(Cp1),
?UNTIL(is_started(app1, Cp2)),
ok = get_start_type(#st{normal = 3}),
%% Restart cp1 and make sure it restarts app1
{ok, Cp1_2} = start_node_config(Ncp1, NoSyncTime, Conf),
global:sync(),
ok = rpc:call(Cp1_2, application, load, [app1()]),
ok = rpc:call(Cp1_2, application, start, [app1, permanent]),
?UNTIL(is_started(app1, Cp1)),
?UNTIL(not is_started(app1, Cp2)),
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)
{[ok,ok,ok],[]} =
rpc:multicall([Cp1_2, Cp2, Cp3], application, load, [app_sp()]),
{[ok,ok,ok],[]} =
rpc:multicall([Cp1_2, Cp2, Cp3], application, start,[app_sp,permanent]),
?UNTIL(is_started(app_sp, Cp2)),
false = is_started(app_sp, Cp1),
false = is_started(app_sp, Cp3),
ok = get_start_type(#st{normal = 3}),
%% Stop cp2 and make sure cp1 starts app_sp
stop_node_nice(Cp2),
?UNTIL(is_started(app_sp, Cp1_2)),
ok = get_start_type(#st{failover = 3}),
%% Stop cp1 and make sure cp3 starts app_sp
stop_node_nice(Cp1_2),
?UNTIL(is_started(app_sp, Cp3)),
ok = get_start_type(#st{normal = 3, failover = 3}),
%% Restart cp2 and make sure it restarts app_sp
{ok, Cp2_2} = start_node_config(Ncp2, NoSyncTime, Conf),
global:sync(),
ok = rpc:call(Cp2_2, application, load, [app_sp()]),
ok = rpc:call(Cp2_2, application, start, [app_sp, permanent]),
?UNTIL(is_started(app_sp, Cp2_2)),
?UNTIL(not is_started(app_sp, Cp3)),
ok = get_start_type(#st{takeover = 3}),
%% Restart cp1 and make sure it doesn't restart app_sp
{ok, Cp1_3} = start_node_config(Ncp1, NoSyncTime, Conf),
global:sync(),
ok = rpc:call(Cp1_3, application, load, [app_sp()]),
ok = rpc:call(Cp1_3, application, start, [app_sp, permanent]),
ct:sleep(500),
false = is_started(app_sp, Cp1_3),
true = is_started(app_sp, Cp2_2),
%% Force takeover to cp1
ok = rpc:call(Cp1_3, application, takeover, [app_sp, permanent]),
?UNTIL(is_started(app_sp, Cp1_3)),
?UNTIL(not is_started(app_sp, Cp2_2)),
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),
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]
%%-----------------------------------------------------------------
%% 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 transfered 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, []),
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]
{ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf),
{ok, Cp2} = start_node_config(Ncp2, NoSyncTime, Conf),
{ok, Cp3} = start_node_config(Ncp3, WithSyncTime, Conf),
Cps = [Cp1, Cp2, Cp3],
wait_for_ready_net(),
%% Start app1 and make sure cp1 starts it
{[ok,ok,ok],[]} =
rpc:multicall(Cps, application, load, [app1()]),
?UNTIL(is_loaded(app1, Cps)),
{[ok,ok,ok],[]} =
rpc:multicall(Cps, application, start, [app1, permanent]),
?UNTIL(is_started(app1, Cp1)),
false = is_started(app1, Cp2),
ok = get_start_type(#st{normal = 3}),
%% Stop cp1 and make sure cp2 starts app1
stop_node_nice(Cp1),
?UNTIL(is_started(app1, Cp2)),
ok = get_start_type(#st{normal = 3}),
%% Restart cp1 and make sure it restarts app1
{ok, Cp1_2} = start_node_config(Ncp1, NoSyncTime, Conf),
global:sync(),
ok = rpc:call(Cp1_2, application, load, [app1()]),
?UNTIL(is_loaded(app1, Cp1_2)),
ok = rpc:call(Cp1_2, application, start, [app1, permanent]),
?UNTIL(is_started(app1, Cp1_2)),
?UNTIL(not is_started(app1, Cp2)),
ok = get_start_type(#st{takeover = 3}),
%% Test [{cp1, cp2}, cp3]
%% Start app3 and make sure cp2 starts it (cp1 has more apps started)
{[ok,ok,ok],[]} =
rpc:multicall([Cp1_2, Cp2, Cp3], application, load, [app3()]),
?UNTIL(is_loaded(app3, [Cp1_2, Cp2, Cp3])),
{[ok,ok,ok],[]} =
rpc:multicall([Cp1_2, Cp2, Cp3], application, start,[app3,permanent]),
?UNTIL(is_started(app3, Cp2)),
false = is_started(app3, Cp1),
false = is_started(app3, Cp3),
ok = get_start_type(#st{normal = 3}),
%% Stop cp2 and make sure cp1 starts app3
stop_node_nice(Cp2),
?UNTIL(is_started(app3, Cp1_2)),
ok = get_start_type(#st{normal = 3}),
%% Stop cp1 and make sure cp3 starts app3
stop_node_nice(Cp1_2),
?UNTIL(is_started(app3, Cp3)),
ok = get_start_type(#st{normal = 6}),
%% Restart cp2 and make sure it restarts app3
{ok, Cp2_2} = start_node_config(Ncp2, NoSyncTime, Conf),
global:sync(),
ok = rpc:call(Cp2_2, application, load, [app3()]),
?UNTIL(is_loaded(app3, Cp2_2)),
ok = rpc:call(Cp2_2, application, start, [app3, permanent]),
?UNTIL(is_started(app3, Cp2_2)),
?UNTIL(not is_started(app3, Cp3)),
ok = get_start_type(#st{takeover = 3}),
%% Restart cp1 and make sure it doesn't restart app3
{ok, Cp1_3} = start_node_config(Ncp1, NoSyncTime, Conf),
global:sync(),
ok = rpc:call(Cp1_3, application, load, [app3()]),
true = is_loaded(app3, Cp1_3),
ok = rpc:call(Cp1_3, application, start, [app3, permanent]),
ct:sleep(5000),
false = is_started(app3, Cp1_3),
true = is_started(app3, Cp2_2),
%% Force takeover to cp1
ok = rpc:call(Cp1_3, application, takeover, [app3, permanent]),
?UNTIL(is_started(app3, Cp1_3)),
?UNTIL(not is_started(app3, Cp2_2)),
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),
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]
%%-----------------------------------------------------------------
%% 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]
{ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf),
{ok, Cp2} = start_node_config(Ncp2, NoSyncTime, Conf),
{ok, Cp3} = start_node_config(Ncp3, WithSyncTime, Conf),
Cps = [Cp1, Cp2, Cp3],
wait_for_ready_net(),
%% Start app1 and make sure cp1 starts it
{[ok,ok,ok],[]} =
rpc:multicall(Cps, application, load, [app1()]),
?UNTIL(is_loaded(app1, Cps)),
{[ok,ok,ok],[]} =
rpc:multicall(Cps, application, start, [app1, permanent]),
?UNTIL(is_started(app1, Cp1)),
false = is_started(app1, Cp2),
%% Unpermit app1 on cp1, make sure cp2 starts it
ok = rpc:call(Cp1, application, permit, [app1, false]),
false = is_started(app1, Cp1),
true = is_started(app1, Cp2),
%% Unpermit app1 on cp2, make sure cp3 starts it
ok = rpc:call(Cp2, application, permit, [app1, false]),
false = is_started(app1, Cp1),
false = is_started(app1, Cp2),
true = is_started(app1, Cp3),
%% Permit cp2 again
ok = rpc:call(Cp2, application, permit, [app1, true]),
false = is_started(app1, Cp1),
false = is_started(app1, Cp3),
true = is_started(app1, Cp2),
%% Start app3, make sure noone starts it
{[ok,ok,ok],[]} =
rpc:multicall(Cps, application, load, [app3()]),
?UNTIL(is_loaded(app3, Cps)),
{[ok,ok,ok],[]} =
rpc:multicall(Cps, application, start, [app3, permanent]),
ct:sleep(1000),
false = is_started(app3, Cp1),
false = is_started(app3, Cp2),
false = is_started(app3, Cp3),
%% Permit app3 on Cp3
ok = rpc:call(Cp3, application, permit, [app3, true]),
true = is_started(app3, Cp3),
%% Permit app3 on Cp2, make sure it starts it
ok = rpc:call(Cp2, application, permit, [app3, true]),
true = is_started(app3, Cp2),
false = is_started(app3, Cp3),
%% Permit app3 on Cp1, make sure it doesn't start it
ok = rpc:call(Cp1, application, permit, [app3, true]),
false = is_started(app3, Cp1),
true = is_started(app3, Cp2),
false = is_started(app3, Cp3),
%% Stop Cp2, make sure Cp1 starts app3
stop_node_nice(Cp2),
?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]
%%-----------------------------------------------------------------
%% 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]
{ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf),
{ok, Cp2} = start_node_config(Ncp2, NoSyncTime, Conf),
{ok, Cp3} = start_node_config(Ncp3, WithSyncTime, Conf),
Cps = [Cp1, Cp2, Cp3],
wait_for_ready_net(),
{[ok,ok,ok],[]} =
rpc:multicall(Cps, application, load, [app1(), d1(NodeNames)]),
?UNTIL(is_loaded(app1, Cps)),
{[ok,ok,ok],[]} =
rpc:multicall(Cps, application, start, [app1, permanent]),
?UNTIL(is_started(app1, Cp1)),
false = is_started(app1, Cp2),
false = is_started(app1, Cp3),
%% Load app1 with different specs and make sure we get an error
{[{error,_},{error,_}],[]} =
rpc:multicall([Cp1, Cp2], application, load, [app1(), d1(NodeNames)]),
{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.
%%-----------------------------------------------------------------
%% 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]
{ok, Cp1} = start_node_with_cache(Ncp1, NoSyncTime, Conf),
{ok, Cp2} = start_node_with_cache(Ncp2, NoSyncTime, Conf),
{ok, Cp3} = start_node_with_cache(Ncp3, WithSyncTime, Conf),
Cps = [Cp1, Cp2, Cp3],
wait_for_ready_net(),
{[ok,ok,ok],[]} =
rpc:multicall(Cps, application, load, [app1(), d1(NodeNames)]),
?UNTIL(is_loaded(app1, Cps)),
{[ok,ok,ok],[]} =
rpc:multicall(Cps, application, start, [app1, permanent]),
?UNTIL(is_started(app1, Cp1)),
false = is_started(app1, Cp2),
%% Load app1 with different specs and make sure we get an error
{[{error,_},{error,_}],[]} =
rpc:multicall([Cp1, Cp2], application, load, [app1(), d1(NodeNames)]),
{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]
%%-----------------------------------------------------------------
%% 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, []),
yes = global:register_name(start_phase, SpPid),
NodeNames = [Ncp1, _Ncp2, _Ncp3] = node_names([cp1, cp2, cp3], Conf),
WithSyncTime = config_fun(config_sf(NodeNames)),
{ok, Cp1} = start_node_config_sf(Ncp1, WithSyncTime, Conf),
wait_for_ready_net(),
%%=============================
%%Example 1 in the user's guide
%%=============================
ok = rpc:call(Cp1, application, load, [myApp,
d_any3(myApp, NodeNames)]),
?UNTIL(is_loaded(myApp, Cp1)),
ok = rpc:call(Cp1, application, start, [myApp, permanent]),
?UNTIL(is_started(myApp, Cp1)),
ok = get_start_phase({sp, 0, 1, 0, 0, 1}),
ok = rpc:call(Cp1, application, stop, [myApp]),
%%=============================
%%Example 2 in the user's guide
%%=============================
ok = rpc:call(Cp1, application, load, [topApp,
d_any3(topApp, NodeNames)]),
?UNTIL(is_loaded(topApp, Cp1)),
ok = rpc:call(Cp1, application, start, [topApp, permanent]),
?UNTIL(is_started(topApp, Cp1)),
ok = get_start_phase({sp, 0, 1, 0, 0, 1}),
ok = rpc:call(Cp1, application, stop, [topApp]),
%%=============================
%%Example 3 in the user's guide
%%=============================
ok = rpc:call(Cp1, application, load, [topApp2,
d_any3(topApp2, NodeNames)]),
?UNTIL(is_loaded(topApp2, Cp1)),
ok = rpc:call(Cp1, application, start, [topApp2, permanent]),
?UNTIL(is_started(topApp2, Cp1)),
ok = get_start_phase({sp, 0, 2, 0, 0, 3}),
ok = rpc:call(Cp1, application, stop, [topApp2]),
%%=============================
%%Example 4 in the user's guide
%%=============================
ok = rpc:call(Cp1, application, load, [topApp3,
d_any3(topApp3, NodeNames)]),
?UNTIL(is_loaded(topApp3, Cp1)),
ok = rpc:call(Cp1, application, start, [topApp3, permanent]),
?UNTIL(is_started(topApp3, Cp1)),
ok = get_start_phase({sp, 1, 3, 3, 2, 4}),
ok = rpc:call(Cp1, application, stop, [topApp3]),
global:send(start_phase, kill),
stop_node_nice(Cp1),
ok.
%% Start distributed applications from within a boot script. Test
%% same as failover.
script_start(Conf) when is_list(Conf) ->
%% start a help process to check the start type
StPid = spawn_link(?MODULE, start_type, []),
yes = global:register_name(st_type, StPid),
%% Create the .app files and the boot script
ok = create_app(),
{{KernelVer,StdlibVer}, _} = create_script("latest"),
case is_real_system(KernelVer, StdlibVer) of
true ->
Options = [];
false ->
Options = [local]
end,
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]
{ok, Cp1} = start_node_boot_config(Ncp1, NoSyncTime, Conf, latest),
{ok, Cp2} = start_node_boot_config(Ncp2, NoSyncTime, Conf, latest),
{ok, Cp3} = start_node_boot_config(Ncp3, WithSyncTime, Conf, latest),
wait_for_ready_net(),
?UNTIL(is_started(app1, Cp1)),
?UNTIL(is_started(app2, Cp1)),
?UNTIL(is_started(app_sp, Cp1)),
false = is_started(app1, Cp2),
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),
?UNTIL(is_started(app1, Cp2)),
?UNTIL(is_started(app2, Cp2)),
?UNTIL(is_started(app_sp, Cp2)),
ok = get_start_type(#st{normal = 6, failover = 3}),
%% Restart cp1, Cp1 takesover app1 and app2
{ok, Cp1_2} = start_node_boot_config(Ncp1, NoSyncTime, Conf, latest),
global:sync(),
?UNTIL(is_started(app1, Cp1_2)),
false = is_started(app1, Cp2),
?UNTIL(is_started(app2, Cp1_2)),
true = is_started(app_sp, Cp2),
?UNTIL(not is_started(app1, Cp2)),
?UNTIL(not is_started(app2, Cp2)),
ok = get_start_type(#st{takeover = 6}),
%% Stop cp2 and make sure cp1 starts app_sp.
false = is_started(app_sp, Cp1_2),
stop_node_nice(Cp2),
?UNTIL(is_started(app_sp, Cp1_2)),
ok = get_start_type(#st{failover = 3}),
%% Stop cp1 and make sure cp3 starts app1, app2 and app_sp
stop_node_nice(Cp1_2),
?UNTIL(is_started(app_sp, Cp3)),
?UNTIL(is_started(app1, Cp3)),
?UNTIL(is_started(app2, Cp3)),
ok = get_start_type(#st{normal = 6, failover = 3}),
%% Restart cp2 and make sure it takesover app1, app2 and app_sp
{ok, Cp2_2} = start_node_boot_config(Ncp2, NoSyncTime, Conf, latest),
global:sync(),
?UNTIL(is_started(app_sp, Cp2_2)),
?UNTIL(is_started(app1, Cp2_2)),
?UNTIL(is_started(app2, Cp2_2)),
?UNTIL(not is_started(app_sp, Cp3)),
?UNTIL(not is_started(app1, Cp3)),
?UNTIL(not is_started(app2, Cp3)),
ok = get_start_type(#st{takeover = 9}),
%% Restart cp1 and make sure it takesover app1, app2
{ok, Cp1_3} = start_node_boot_config(Ncp1, NoSyncTime, Conf, latest),
global:sync(),
?UNTIL(is_started(app1, Cp1_3)),
?UNTIL(is_started(app2, Cp1_3)),
false = is_started(app_sp, Cp1_3),
true = is_started(app_sp, Cp2_2),
?UNTIL(not is_started(app1, Cp2_2)),
?UNTIL(not is_started(app2, Cp2_2)),
ok = get_start_type(#st{takeover = 6}),
%% Force takeover to cp1
ok = rpc:call(Cp1_3, application, takeover, [app_sp, permanent]),
?UNTIL(is_started(app_sp, Cp1_3)),
?UNTIL(not is_started(app_sp, Cp2_2)),
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),
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 = file:delete("latest.boot"),
ok = file:delete("latest.rel"),
ok = file:delete("latest.script"),
ok.
%% Start local applications with permission false. Set
%% permit true on different nodes.
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),
{ok, Cp1} = start_node(Ncp1, Config),
{ok, Cp2} = start_node(Ncp2, Config),
{ok, Cp3} = start_node(Ncp3, Config),
wait_for_ready_net(),
{[ok,ok,ok],[]} =
rpc:multicall([Cp1, Cp2, Cp3], application, load, [app1()]),
{[ok,ok,ok],[]} =
rpc:multicall([Cp1, Cp2, Cp3], application, start, [app1, permanent]),
{[ok,ok,ok],[]} =
rpc:multicall([Cp1, Cp2, Cp3], application, load, [app2()]),
{[ok,ok,ok],[]} =
rpc:multicall([Cp1, Cp2, Cp3], application, start, [app2, permanent]),
{[ok,ok,ok],[]} =
rpc:multicall([Cp1, Cp2, Cp3], application, load, [app3()]),
ct:sleep(1000),
false = is_started(app1, Cp1),
false = is_started(app1, Cp2),
false = is_started(app1, Cp3),
%% Permit a not started application
ok = rpc:call(Cp1, application, permit, [app3, true]),
ct:sleep(1000),
false = is_started(app3, Cp1),
false = is_started(app3, Cp2),
false = is_started(app3, Cp3),
%% Permit a not loaded application
{error,{not_loaded,app_notloaded}} =
rpc:call(Cp1, application, permit, [app_notloaded, true]),
ct:sleep(1000),
false = is_started(app_notloaded, Cp1),
false = is_started(app_notloaded, Cp2),
false = is_started(app_notloaded, Cp3),
%% Unpermit a not started application
ok = rpc:call(Cp1, application, permit, [app3, false]),
ct:sleep(1000),
false = is_started(app3, Cp1),
false = is_started(app3, Cp2),
false = is_started(app3, Cp3),
%% Unpermit a not loaded application
{error,{not_loaded,app_notloaded}} =
rpc:call(Cp1, application, permit, [app_notloaded, false]),
ct:sleep(1000),
false = is_started(app_notloaded, Cp1),
false = is_started(app_notloaded, Cp2),
false = is_started(app_notloaded, Cp3),
%% Permit app1 on CP1 and make sure it is started
ok = rpc:call(Cp1, application, permit, [app1, true]),
?UNTIL(is_started(app1, Cp1)),
false = is_started(app1, Cp2),
false = is_started(app1, Cp3),
%% Permit it again
ok = rpc:call(Cp1, application, permit, [app1, true]),
ct:sleep(1000),
true = is_started(app1, Cp1),
false = is_started(app1, Cp2),
false = is_started(app1, Cp3),
%% Permit app2 on CP1 and make sure it is started
ok = rpc:call(Cp1, application, permit, [app2, true]),
?UNTIL(is_started(app2, Cp1)),
false = is_started(app2, Cp2),
false = is_started(app2, Cp3),
%% Permit app1 on CP2 and make sure it is started
ok = rpc:call(Cp2, application, permit, [app1, true]),
?UNTIL(is_started(app1, Cp2)),
true = is_started(app1, Cp1),
false = is_started(app1, Cp3),
%% Unpermit app1 on CP1 and make sure it is stopped
ok = rpc:call(Cp1, application, permit, [app1, false]),
?UNTIL(false =:= is_started(app1, Cp1)),
true = is_started(app1, Cp2),
false = is_started(app1, Cp3),
%% Unpermit it agin
ok = rpc:call(Cp1, application, permit, [app1, false]),
ct:sleep(1000),
false = is_started(app1, Cp1),
true = is_started(app1, Cp2),
false = is_started(app1, Cp3),
%% Permit app1 on CP1 and make sure it is started
ok = rpc:call(Cp1, application, permit, [app1, true]),
?UNTIL(is_started(app1, Cp1)),
true = is_started(app1, Cp2),
false = is_started(app1, Cp3),
%% Unpermit app1 on CP1 and make sure it is stopped
ok = rpc:call(Cp1, application, permit, [app1, false]),
?UNTIL(false =:= is_started(app1, Cp1)),
true = is_started(app1, Cp2),
false = is_started(app1, Cp3),
%% Unpermit app1 on CP2 and make sure it is stopped
ok = rpc:call(Cp2, application, permit, [app1, false]),
ct:sleep(1000),
?UNTIL(false =:= is_started(app1, Cp2)),
false = is_started(app1, Cp1),
false = is_started(app1, Cp3),
%% Unpermit app2 on CP1 and make sure it is stopped
ok = rpc:call(Cp1, application, permit, [app2, false]),
?UNTIL(false =:= is_started(app2, Cp2)),
false = is_started(app2, Cp1),
false = is_started(app2, Cp3),
stop_node_nice(Cp1),
stop_node_nice(Cp2),
stop_node_nice(Cp3),
ok.
%% Start distributed applications with permission false. Set
%% permit true on different nodes.
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]
{ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf),
{ok, Cp2} = start_node_config(Ncp2, NoSyncTime, Conf),
{ok, Cp3} = start_node_config(Ncp3, WithSyncTime, Conf),
Cps = [Cp1, Cp2, Cp3],
wait_for_ready_net(),
{[ok,ok,ok],[]} =
rpc:multicall(Cps, application, load, [app1()]),
?UNTIL(is_loaded(app1, Cps)),
{[ok,ok,ok],[]} =
rpc:multicall(Cps, application, start, [app1, permanent]),
{[ok,ok,ok],[]} =
rpc:multicall(Cps, application, load, [app2()]),
ct:sleep(1000),
false = is_started(app1, Cp1),
false = is_started(app1, Cp2),
false = is_started(app1, Cp3),
%% Permit a not started application
ok = rpc:call(Cp1, application, permit, [app2, true]),
ct:sleep(1000),
false = is_started(app2, Cp1),
false = is_started(app2, Cp2),
false = is_started(app2, Cp3),
%% Permit a not loaded application
{error,{not_loaded,app3}} =
rpc:call(Cp1, application, permit, [app3, true]),
ct:sleep(1000),
false = is_started(app3, Cp1),
false = is_started(app3, Cp2),
false = is_started(app3, Cp3),
%% Unpermit a not started application
ok = rpc:call(Cp1, application, permit, [app2, false]),
{[ok,ok,ok],[]} =
rpc:multicall([Cp1, Cp2, Cp3], application, start, [app2, permanent]),
ct:sleep(1000),
false = is_started(app2, Cp1),
false = is_started(app2, Cp2),
false = is_started(app2, Cp3),
%% Unpermit a not loaded application
{error,{not_loaded,app3}} =
rpc:call(Cp1, application, permit, [app3, false]),
{[ok,ok,ok],[]} =
rpc:multicall(Cps, application, load, [app3()]),
?UNTIL(is_loaded(app3, Cps)),
{[ok,ok,ok],[]} =
rpc:multicall(Cps, application, start, [app3, permanent]),
ct:sleep(1000),
false = is_started(app3, Cp1),
false = is_started(app3, Cp2),
false = is_started(app3, Cp3),
%% Permit app1 on CP1 and make sure it is started
ok = rpc:call(Cp1, application, permit, [app1, true]),
?UNTIL(is_started(app1, Cp1)),
false = is_started(app1, Cp2),
false = is_started(app1, Cp3),
%% Permit it again
ok = rpc:call(Cp1, application, permit, [app1, true]),
?UNTIL(is_started(app1, Cp1)),
false = is_started(app1, Cp2),
false = is_started(app1, Cp3),
%% Permit app2 on CP1 and make sure it is started
ok = rpc:call(Cp1, application, permit, [app2, true]),
?UNTIL(is_started(app2, Cp1)),
false = is_started(app2, Cp2),
false = is_started(app2, Cp3),
%% Permit app1 on CP2 and make sure it is not started
ok = rpc:call(Cp2, application, permit, [app1, true]),
ct:sleep(1000),
true = is_started(app1, Cp1),
false = is_started(app1, Cp2),
false = is_started(app1, Cp3),
%% Crash CP1 and make sure app1, but not app2, is started on CP2
stop_node_nice(Cp1),
?UNTIL(is_started(app1, Cp2)),
false = is_started(app2, Cp2),
%% Restart CP1 again, check nothing is running on it
{ok, Cp1_2} = start_node_config(Ncp1, NoSyncTime, Conf),
global:sync(),
ok = rpc:call(Cp1_2, application, load, [app1()]),
?UNTIL(is_loaded(app1, Cp1_2)),
ok = rpc:call(Cp1_2, application, start, [app1, permanent]),
ok = rpc:call(Cp1_2, application, load, [app2()]),
?UNTIL(is_loaded(app2, Cp1_2)),
ok = rpc:call(Cp1_2, application, start, [app2, permanent]),
ok = rpc:call(Cp1_2, application, load, [app3()]),
?UNTIL(is_loaded(app3, Cp1_2)),
ok = rpc:call(Cp1_2, application, start, [app3, permanent]),
false = is_started(app1, Cp1_2),
false = is_started(app2, Cp1_2),
%% Permit app3 on CP3 and make sure it is started
ok = rpc:call(Cp3, application, permit, [app3, true]),
?UNTIL(is_started(app3, Cp3)),
false = is_started(app3, Cp1_2),
false = is_started(app3, Cp2),
%% Permit app3 on CP1 and make sure it is moved there from CP3
ok = rpc:call(Cp1_2, application, permit, [app3, true]),
?UNTIL(is_started(app3, Cp1_2)),
false = is_started(app3, Cp2),
false = is_started(app3, Cp3),
%% Unpermit app3 on CP3 and CP1 and make sure it is stopped
ok = rpc:call(Cp3, application, permit, [app3, false]),
ok = rpc:call(Cp1_2, application, permit, [app3, false]),
?UNTIL(false =:= is_started(app3, Cp1_2)),
false = is_started(app3, Cp2),
false = is_started(app3, Cp3),
stop_node_nice(Cp1_2),
stop_node_nice(Cp2),
stop_node_nice(Cp3),
ok.
%% 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(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]
{ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf),
{ok, Cp2} = start_node_config(Ncp2, WithSyncTime, Conf),
wait_for_ready_net(),
%% Start app1 and make sure cp1 starts it
{[ok,ok],[]} =
rpc:multicall([Cp1, Cp2], application, load, [app1()]),
_ = rpc:cast(Cp2, application, start, [app1, permanent]),
ct:sleep(1000),
%% Crash CP1 and make sure app1 is started on CP2
stop_node_nice(Cp1),
?UNTIL(is_started(app1, Cp2)),
stop_node_nice(Cp2),
ok.
%% Test application:ensure_started/1.
ensure_started(_Conf) ->
{ok, Fd} = file:open("app1.app", [write]),
w_app1(Fd),
file:close(Fd),
ok = application:ensure_started(app1),
ok = application:ensure_started(app1),
{error, {already_started, app1}} = application:start(app1),
ok = application:stop(app1),
{error,{"no such file or directory", _ }} = application:ensure_started(hopefully_not_an_existing_app_file),
ok = application:ensure_started(app1, permanent),
ok = application:ensure_started(app1, permanent),
ok = application:stop(app1),
ok = application:unload(app1),
ok.
%% Test application:ensure_all_started/1-2.
ensure_all_started(_Conf) ->
{ok, Fd1} = file:open("app1.app", [write]),
w_app1(Fd1),
file:close(Fd1),
{ok, Fd9} = file:open("app9.app", [write]),
w_app9(Fd9),
file:close(Fd9),
{ok, Fd10} = file:open("app10.app", [write]),
w_app10_dep9(Fd10),
file:close(Fd10),
{ok, FdErr} = file:open("app_chain_error.app", [write]),
w_app(FdErr, app_chain_error()),
file:close(FdErr),
{ok, FdErr2} = file:open("app_chain_error2.app", [write]),
w_app(FdErr2, app_chain_error2()),
file:close(FdErr2),
%% Single app start/stop
false = lists:keyfind(app1, 1, application:which_applications()),
{ok, [app1]} = application:ensure_all_started(app1), % app1 started
{app1, _, _} = lists:keyfind(app1, 1, application:which_applications()),
{ok, []} = application:ensure_all_started(app1), % no start needed
ok = application:stop(app1),
false = lists:keyfind(app1, 1, application:which_applications()),
ok = application:unload(app1),
%% App or dependency not found.
Name = hopefully_not_an_existing_app_file,
{error,{Name, {"no such file or directory", _ }}} =
application:ensure_all_started(Name),
%% Start dependencies.
{error, {not_started, app9}} = application:start(app10),
{ok, [app9,app10]} = application:ensure_all_started(app10, temporary),
{app9, _, _} = lists:keyfind(app9, 1, application:which_applications()),
{app10, _, _} = lists:keyfind(app10, 1, application:which_applications()),
%% Only report apps/dependencies that actually needed to start
ok = application:stop(app10),
ok = application:unload(app10),
{ok, [app10]} = application:ensure_all_started(app10, temporary),
ok = application:stop(app9),
ok = application:unload(app9),
ok = application:stop(app10),
ok = application:unload(app10),
%% Deeper failure chain. We have the following dependencies:
%% app_chain_error -> app_chain_error2
%% app_chain_error2 -> app10
%% app_chain_error2 -> hopefully_not_an_existing_app
%% app10 -> app 9
%% First we have none running and we expect to have neither app9
%% nor app10 running after failing to start
%% hopefully_not_an_existing_app
{error, {hopefully_not_an_existing_app, {"no such file or directory", _}}}=
application:ensure_all_started(app_chain_error),
false = lists:keyfind(app9, 1, application:which_applications()),
false = lists:keyfind(app10, 1, application:which_applications()),
false = lists:keyfind(app_chain_error2,1,application:which_applications()),
false = lists:keyfind(app_chain_error, 1, application:which_applications()),
%% Here we will have app9 already running, and app10 should be
%% able to boot fine.
%% In this dependency failing, we expect app9 to still be running, but
%% not app10 after failing to start hopefully_not_an_existing_app
{ok, [app9]} = application:ensure_all_started(app9, temporary),
{error, {hopefully_not_an_existing_app, {"no such file or directory", _}}}=
application:ensure_all_started(app_chain_error),
{app9, _, _} = lists:keyfind(app9, 1, application:which_applications()),
false = lists:keyfind(app10, 1, application:which_applications()),
false = lists:keyfind(app_chain_error2,1,application:which_applications()),
false = lists:keyfind(app_chain_error, 1, application:which_applications()),
ok = application:stop(app9),
ok = application:unload(app9),
ok = application:unload(app10),
ok = application:unload(app_chain_error2),
ok = application:unload(app_chain_error),
ok.
%%%-----------------------------------------------------------------
%%% Testing of reported bugs and other tickets.
%%%-----------------------------------------------------------------
%%-----------------------------------------------------------------
%% Ticket: OTP-1586
%% Slogan: recursive load of applications fails
%%-----------------------------------------------------------------
%% Test recursive load of applications.
otp_1586(Conf) when is_list(Conf) ->
Dir = proplists:get_value(priv_dir,Conf),
{ok, Fd} = file:open(filename:join(Dir, "app5.app"), [write]),
w_app5(Fd),
file:close(Fd),
try
true = code:add_patha(Dir),
ok = application:load(app4()),
ok = application:unload(app4)
after
_ = code:del_path(Dir)
end.
%%-----------------------------------------------------------------
%% Ticket: OTP-2078
%% Slogan: start of distrib apps fails when the nodes start
%% simultaneously
%%-----------------------------------------------------------------
%% 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]
{ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf),
{ok, Cp2} = start_node_config(Ncp2, WithSyncTime, Conf),
Cps = [Cp1, Cp2],
wait_for_ready_net(),
%% Start app1 and make sure cp1 starts it
{[ok,ok],[]} =
rpc:multicall(Cps, application, load, [app1()]),
?UNTIL(is_loaded(app1, Cps)),
ok = rpc:call(Cp1, application, start, [app1, permanent]),
?UNTIL(is_started(app1, Cp1)),
false = is_started(app1, Cp2),
%% Start app1 on cp2; make sure it works (the bug was that this start
%% returned error)
ok = rpc:call(Cp2, application, start, [app1, permanent]),
true = is_started(app1, Cp1),
false = is_started(app1, Cp2),
stop_node_nice(Cp1),
stop_node_nice(Cp2),
ok.
%% 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, []),
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
ok = application:load(app1()),
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}]}]]),
ok = application_controller:config_change(EnvBefore),
ok = get_conf_change([{[], [{new1, hi}, {new2, moi}], []}]),
%% Start app2
ok = application:load(app2()),
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(),
ok = application_controller:config_change(EnvBefore2),
ok = get_conf_change([{[],[{new1,si},{new2,no}],[]},
{[{new1,hello}],[{new3,mors}],[new2]}]),
ok = application:stop(app1),
ok = application:stop(app2),
ok.
%%-----------------------------------------------------------------
%% Ticket: OTP-2718
%% Slogan: transient app which fails during start is ignored
%%-----------------------------------------------------------------
%% Test fail of transient app at start.
otp_2718(Conf) when is_list(Conf) ->
{ok, Cp1} = start_node_args(cp1, "-pa " ++ proplists:get_value(data_dir,Conf)),
wait_for_ready_net(),
%% normal exit from the application
ok = rpc:call(Cp1, application, load, [app_trans_normal()]),
?UNTIL(is_loaded(trans_normal, Cp1)),
{error, {{'EXIT',normal},_}} =
rpc:call(Cp1, application, start, [trans_normal, transient]),
ct:sleep(2000),
false = is_started(trans_normal, Cp1),
%% abnormal exit from the application
ok = rpc:call(Cp1, application, load, [app_trans_abnormal()]),
{error, {bad_return,{{trans_abnormal_sup,start,[normal,[]]},
{'EXIT',abnormal}}}} =
rpc:call(Cp1, application, start, [trans_abnormal, transient]),
ct:sleep(3000),
{badrpc,nodedown} = which_applications(Cp1),
ok.
%%-----------------------------------------------------------------
%% Ticket: OTP-2973
%% Slogan: application:start does not test if an appl is already starting...
%%-----------------------------------------------------------------
%% 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),
Pid1 = spawn_link(?MODULE, init2973, []),
Pid2 = spawn_link(?MODULE, init2973, []),
Pid1 ! {start, self(), app0},
Pid2 ! {start, self(), app0},
{Res1, Res2} = receive
{Pid1, res, Res1x} ->
receive
{Pid2, res, Res2x} ->
{Res1x, Res2x}
after 2000 ->
ct:fail(timeout_pid2)
end;
{Pid2, res, Res2x} ->
receive
{Pid1, res, Res1x} ->
{Res1x, Res2x}
after 2000 ->
ct:fail(timeout_pid1)
end
end,
%% Stop it. Inteferes with other global.
ok = application:stop(app0),
%% Test result.
case {Res1, Res2} of
{ok, ok} ->
ok;
_ ->
Txt = io_lib:format("Illegal results from start: ~p ~p ",
[Res1, Res2]),
ct:fail(lists:flatten(Txt))
end,
%% Write a .app file
{ok, Fda} = file:open("app_start_error.app", [write]),
w_app_start_error(Fda),
file:close(Fda),
Pid1 ! {start, self(), app_start_error},
Pid2 ! {start, self(), app_start_error},
{Res1a, Res2a} = receive
{Pid1, res, Res1y} ->
receive
{Pid2, res, Res2y} ->
{Res1y, Res2y}
after 2000 ->
ct:fail(timeout_pid2)
end;
{Pid2, res, Res2y} ->
receive
{Pid1, res, Res1y} ->
{Res1y, Res2y}
after 2000 ->
ct: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;
_ ->
Txta = io_lib:format("Illegal results from start ~p ~p ",[Res1a, Res2a]),
ct:fail(lists:flatten(Txta))
end,
ok.
%%-----------------------------------------------------------------
%% Ticket: OTP-3184
%% Slogan: crash the node if permanent appl has illegal env parameter values
%%-----------------------------------------------------------------
%% When a distributed application is started the permit flag is checked
%% that the permit flag is not changed during the start.
%% The 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]
{ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf),
{ok, Cp2} = start_node_config(Ncp2, WithSyncTime, Conf),
wait_for_ready_net(),
%% Start app1 and make sure it is not started
{[ok,ok],[]} =
rpc:multicall([Cp1, Cp2], application, load, [app1()]),
ct:sleep(3000),
false = is_started(app1, Cp1),
false = is_started(app1, Cp2),
%% Start app1 on cp1
ok = rpc:call(Cp1, application, permit, [app1, true]),
ok = rpc:call(Cp1, application, start, [app1, permanent]),
ok = rpc:call(Cp2, application, start, [app1, permanent]),
?UNTIL(is_started(app1, Cp1)),
false = is_started(app1, Cp2),
%% Check that the application is marked as running in application_controller
X = rpc:call(Cp1, application_controller, info, []),
{value, {running, Xrunning}} = lists:keysearch(running, 1, X),
{value, Xapp1} = lists:keysearch(app1, 1, Xrunning),
{app1, _Xpid} = Xapp1,
Y = rpc:call(Cp2, application_controller, info, []),
{value, {running, Yrunning}} = lists:keysearch(running, 1, Y),
{value, Yapp1} = lists:keysearch(app1, 1, Yrunning),
{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
%%-----------------------------------------------------------------
%% crash the node if permanent appl has illegal env parameter values.
otp_3002(Conf) when is_list(Conf) ->
%% Create the boot script
{{KernelVer,StdlibVer}, {LatestDir, LatestName}} =
create_script_3002("script_3002"),
ct:pal(?HI_VERBOSITY, "LatestDir = ~p~n", [LatestDir]),
ct:pal(?HI_VERBOSITY, "LatestName = ~p~n", [LatestName]),
case is_real_system(KernelVer, StdlibVer) of
true ->
Options = [];
false ->
Options = [local]
end,
ok = systools:make_script("script_3002", Options),
ok = systools:script2boot("script_3002"),
{error, timeout} = start_node_boot_3002(cp1, "script_3002"),
ok = file:delete("script_3002.boot"),
ok = file:delete("script_3002.rel"),
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).
%%-----------------------------------------------------------------
%% 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 = proplists:get_value(priv_dir,Conf),
{ok, FdC} = file:open(filename:join(Dir, "otp_4066.config"), [write]),
write_config(FdC, config_4066(AllNodes, 5000, [App1Nodes])),
file:close(FdC),
%% Write the app1.app file
{ok, FdA12} = file:open(filename:join(Dir, "app1.app"), [write]),
w_app1(FdA12),
file:close(FdA12),
Args1 = "-pa " ++ Dir ++ " -config " ++ filename:join(Dir, "otp_4066"),
Args2 = "-pa " ++ Dir ++ " -kernel start_dist_ac true",
{ok, Cp2} = start_node_args(Ncp2, Args2),
%% Cp1 syncs with cp2 (which is known to be up).
{ok, Cp1} = start_node_args(Ncp1, Args1),
wait_for_ready_net(),
ok = rpc:call(Cp1, application, start, [app1]),
wait_until_started(app1, [Cp1]),
io:format("--- App1 started at Cp1 ---~n", []),
print_dac_state(AllNodes),
%% Cp2 previously crashed on this stop
ok = rpc:call(Cp1, application, stop, [app1]),
wait_until_stopped(app1, [Cp1]),
io:format("--- App1 stopped at Cp1 ---~n", []),
print_dac_state(AllNodes),
ok = rpc:call(Cp1, application, start, [app1]),
wait_until_started(app1, [Cp1]),
io:format("--- App1 started at Cp1 ---~n", []),
print_dac_state(AllNodes),
ok = rpc:call(Cp2, application, load, [app1, App1Nodes]),
ok = rpc:call(Cp2, application, start, [app1]),
io:format("--- App1 started at Cp2 ---~n", []),
print_dac_state(AllNodes),
stop_node_nice(Cp1),
wait_until_started(app1, [Cp2]),
io:format("--- Cp1 crashed; failover to Cp2 ---~n", []),
print_dac_state(Cp2),
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),
io: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.
%%-----------------------------------------------------------------
%% 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]
{ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf),
{ok, Cp2} = start_node_config(Ncp2, WithSyncTime, Conf),
Cps = [Cp1, Cp2],
wait_for_ready_net(),
%% Try to start app10 which should fail since app9 is not started
{[ok,ok],[]} =
rpc:multicall(Cps, application, load, [app9()]),
?UNTIL(is_loaded(app9, Cps)),
{[ok,ok],[]} =
rpc:multicall(Cps, application, load, [app10_dep9()]),
{error, {not_started, app9}} =
rpc:call(Cp1, application, start, [app10]),
%% Start app9 and brutally kill it, then try to start app10
ok = rpc:call(Cp1, application, start, [app9]),
ct:sleep(1000),
Pid9 = rpc:call(Cp1, erlang, whereis, [ch_sup19]),
true = erlang:is_pid(Pid9),
true = erlang:exit(Pid9, kill),
ct:sleep(1000),
%% This gave {error, no_report} before the patch
{error, {not_running, app9}} =
rpc:call(Cp1, application, start, [app10]),
stop_node_nice(Cp1),
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(proplists:get_value(data_dir,Conf)),
try
ok = application:load(app_group_leader()),
ok = application:start(group_leader),
case whereis(nisse) of
Pid when is_pid(Pid) ->
Mref = erlang:monitor(process, Pid),
ok = application:stop(group_leader),
receive
{'DOWN',Mref,_,_,_} -> ok
end,
undefined = whereis(nisse);
Bad ->
io:format("~p\n", [Bad]),
ct:fail(failed)
end
after
code:set_path(OldPath)
end,
ok.
%%-----------------------------------------------------------------
%% Ticket: OTP-5606
%% Slogan: Problems with starting a distributed application
%%-----------------------------------------------------------------
%% Test of several processes simultaneously starting the same
%% distributed application.
otp_5606(Conf) when is_list(Conf) ->
%% Write a config file
Dir = proplists:get_value(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]
{ok, Cp1} = start_node(Ncp1, Config),
{ok, Cp2} = start_node(Ncp2, Config),
Cps = [Cp1, Cp2],
wait_for_ready_net(),
%% Load app1 on both nodes
{[ok, ok], []} =
rpc:multicall(Cps, application, load, [app1()]),
%% Attempt to start app1 from different processes simultaneously
Pid11 = spawn_link(Cp1, ?MODULE, loop5606, [self()]),
Pid12 = spawn_link(Cp1, ?MODULE, loop5606, [self()]),
Pid13 = spawn_link(Cp1, ?MODULE, loop5606, [self()]),
Pid2 = spawn_link(Cp2, ?MODULE, loop5606, [self()]),
Pid2 ! start,
Pid11 ! start,
Pid12 ! start,
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]),
ct:fail(lists:flatten(Txt))
end,
{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 ->
ct:fail(timeout_waiting_for_res)
end;
otp_5606_loop(ResL) ->
ResL.
loop5606(Pid) ->
receive
start ->
Res = application:start(app1),
Pid ! {self(), Res}
end.
%% Tests get_env/* functions.
get_env(Conf) when is_list(Conf) ->
ok = application:set_env(kernel, new_var, new_val),
{ok, new_val} = application:get_env(kernel, new_var),
undefined = application:get_env(undefined_app, a),
undefined = application:get_env(kernel, error_logger_xyz),
default = application:get_env(kernel, error_logger_xyz, default),
ok.
%%-----------------------------------------------------------------
%% Should be started in a CC view with:
%% erl -sname XXX -rsh ctrsh where XX not in [cp1, cp2, cp3]
%%-----------------------------------------------------------------
%% 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]
{ok, Cp1} = start_node_config(Ncp1, WithSyncTime, Conf),
ok = rpc:call(Cp1, application, load, [appinc(), d3(NodeNames)]),
?UNTIL(is_loaded(appinc, Cp1)),
ok = rpc:call(Cp1, application, start, [appinc, permanent]),
?UNTIL(is_started(appinc, Cp1)),
{ok, "Test of new app file, including appnew"} =
rpc:call(Cp1, application, get_key, [appinc, description]),
{ok, "CXC 138 ai"} = rpc:call(Cp1, application, get_key, [appinc ,id]),
{ok, "2.0"} = rpc:call(Cp1, application, get_key, [appinc, vsn]),
{ok, [kernel]} = rpc:call(Cp1, application, get_key, [appinc, applications]),
{ok, [appinc1, appinc2]} =
rpc:call(Cp1, application, get_key, [appinc, included_applications]),
{ok, []} = rpc:call(Cp1, application, get_key, [appinc, registered]),
{ok, [{init, [kalle]}, {takeover, []}, {go, [sune]}]} =
rpc:call(Cp1, application, get_key, [appinc, start_phases]),
{ok, Env} = rpc:call(Cp1, application, get_key, [appinc ,env]),
[{own2,val2},{own_env1,value1}] = lists:sort(Env),
{ok, []} = rpc:call(Cp1, application, get_key, [appinc, modules]),
{ok, {application_starter, [ch_sup, {appinc, 41, 43}] }} =
rpc:call(Cp1, application, get_key, [appinc, mod]),
{ok, infinity} = rpc:call(Cp1, application, get_key, [appinc, maxP]),
{ok, infinity} = rpc:call(Cp1, application, get_key, [appinc, maxT]),
undefined = rpc:call(Cp1, application, get_key, [appinc, very_unknown]),
{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]),
[{own2,val2},{own_env1,value1}] = lists:sort(Env),
{ok, "Test of new app file, including appnew"} =
gen_server:call({global, {ch,41}}, {get_pid_key, description}),
{ok, "CXC 138 ai"} =
gen_server:call({global, {ch,41}}, {get_pid_key, id}),
{ok, "2.0"} =
gen_server:call({global, {ch,41}}, {get_pid_key, vsn}),
{ok, [kernel]} =
gen_server:call({global, {ch,41}}, {get_pid_key, applications}),
{ok, [appinc1, appinc2]} =
gen_server:call({global, {ch,41}}, {get_pid_key, included_applications}),
{ok, []} =
gen_server:call({global, {ch,41}}, {get_pid_key, registered}),
{ok, [{init, [kalle]}, {takeover, []}, {go, [sune]}]} =
gen_server:call({global, {ch,41}}, {get_pid_key, start_phases}),
{ok, Env} = gen_server:call({global, {ch,41}}, {get_pid_key, env}),
[{own2,val2},{own_env1,value1}] = lists:sort(Env),
{ok, []} =
gen_server:call({global, {ch,41}}, {get_pid_key, modules}),
{ok, {application_starter, [ch_sup, {appinc, 41, 43}] }} =
gen_server:call({global, {ch,41}}, {get_pid_key, mod}),
{ok, infinity} =
gen_server:call({global, {ch,41}}, {get_pid_key, maxP}),
{ok, infinity} =
gen_server:call({global, {ch,41}}, {get_pid_key, maxT}),
undefined =
gen_server:call({global, {ch,41}}, {get_pid_key, very_unknown}),
{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),
[{own2,val2},{own_env1,value1}] = lists:sort(Env),
stop_node_nice(Cp1),
ok.
%%%-----------------------------------------------------------------
%%% Testing of change of distributed parameter.
%%%-----------------------------------------------------------------
%% 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),
NewDist = {distributed, [{app1, [Cp3]},
{app2, 5000, [Cp2]},
{app3, [Cp3, {Cp1, Cp2}]},
{app6, [Cp1, {Cp3, Cp2}]},
{app7, 1000, [Cp3]},
{app8, [Cp1, {Cp2, Cp3}]}]},
NewKernel = [{kernel, lists:keyreplace(distributed, 1, OldKernel, NewDist)}],
ok = rpc:call(Cp1, application_controller, test_change_apps,
[[kernel], [NewKernel]]),
ok = rpc:call(Cp2, application_controller, test_change_apps,
[[kernel], [NewKernel]]),
ok = rpc:call(Cp3, application_controller, test_change_apps,
[[kernel], [NewKernel]]),
{[ok,ok,ok],[]} =
rpc:multicall([Cp1, Cp2, Cp3],
application_controller, config_change, [OldEnv]),
ct:sleep(7000),
DcInfo1 = rpc:call(Cp1, dist_ac, info, []),
DcInfo2 = rpc:call(Cp2, dist_ac, info, []),
DcInfo3 = rpc:call(Cp3, dist_ac, info, []),
DcWa1 = which_applications(Cp1),
DcWa2 = which_applications(Cp2),
DcWa3 = which_applications(Cp3),
Wa1 = lists:foldl(fun({A1, _N1, _V1}, AccIn) -> [A1 | AccIn] end,
[], DcWa1),
Wa2 = lists:foldl(fun({A2, _N2, _V2}, AccIn) -> [A2 | AccIn] end,
[], DcWa2),
Wa3 = lists:foldl(fun({A3, _N3, _V3}, AccIn) -> [A3 | AccIn] end,
[], DcWa3),
case lists:sort(Wa1) of
[app1, app2, app3, kernel, stdlib] ->
ok;
EWa1 ->
X1 = io_lib:format("distribution error: Cp1 ~p ",[EWa1]),
ct:fail(lists:flatten(X1))
end,
case lists:sort(Wa2) of
[app6, app8, kernel, stdlib] ->
ok;
EWa2 ->
X2 = io_lib:format("distribution error: Cp2 ~p ",[EWa2]),
ct:fail(lists:flatten(X2))
end,
case lists:sort(Wa3) of
[app7, kernel, stdlib] ->
ok;
EWa3 ->
X3 = io_lib:format("distribution error: Cp3 ~p ",[EWa3]),
ct:fail(lists:flatten(X3))
end,
DcInfo1n = rpc:call(Cp1, dist_ac, info, []),
DcInfo2n = rpc:call(Cp2, dist_ac, info, []),
DcInfo3n = rpc:call(Cp3, dist_ac, info, []),
%% Added afterwards. Got rid of some warnings for unused variables.
true = DcInfo1 =:= DcInfo1n,
true = DcInfo2 =:= DcInfo2n,
true = DcInfo3 =:= DcInfo3n,
stop_node_nice(Cp1),
stop_node_nice(Cp2),
stop_node_nice(Cp3),
ok = file:delete("dc.boot"),
ok = file:delete("dc.rel"),
ok = file:delete("dc.script"),
ok.
%% 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),
NewDist = {distributed, [{app1, [Cp3]},
{app2, 5000, [Cp2]},
{app3, [Cp3, {Cp1, Cp2}]},
{app6, [Cp1, {Cp3, Cp2}]},
{app7, 1000, [Cp3]},
{app8, [Cp1, {Cp2, Cp3}]}]},
NewKernel = [{kernel, lists:keyreplace(distributed, 1, OldKernel, NewDist)}],
ok = rpc:call(Cp1, application_controller, test_change_apps,
[[kernel], [NewKernel]]),
ok = rpc:call(Cp2, application_controller, test_change_apps,
[[kernel], [NewKernel]]),
ok = rpc:call(Cp3, application_controller, test_change_apps,
[[kernel], [NewKernel]]),
{[ok,ok,ok],[]} =
rpc:multicall([Cp1, Cp2, Cp3],
application_controller, config_change, [OldEnv]),
ct:sleep(4000),
stop_node_nice(Cp1),
ct:sleep(10000),
_DcInfo2 = rpc:call(Cp2, dist_ac, info, []),
_DcInfo3 = rpc:call(Cp3, dist_ac, info, []),
DcWa2 = which_applications(Cp2),
DcWa3 = which_applications(Cp3),
Wa2 = lists:foldl(fun({A2, _N2, _V2}, AccIn) -> [A2 | AccIn] end,
[], DcWa2),
Wa3 = lists:foldl(fun({A3, _N3, _V3}, AccIn) -> [A3 | AccIn] end,
[], DcWa3),
case lists:sort(Wa2) of
[app2, app6, app8, kernel, stdlib] ->
ok;
EWa2 ->
X2 = io_lib:format("distribution error: Cp2 ~p ",[EWa2]),
ct:fail(lists:flatten(X2))
end,
case lists:sort(Wa3) of
[app1, app3, app7, kernel, stdlib] ->
ok;
EWa3 ->
X3 = io_lib:format("distribution error: Cp3 ~p ",[EWa3]),
ct:fail(lists:flatten(X3))
end,
{ok, Cp1} = start_node_boot(Ncp1, Config2, dc),
ct:sleep(10000),
_DcInfo1rs = rpc:call(Cp1, dist_ac, info, []),
_DcInfo2rs = rpc:call(Cp2, dist_ac, info, []),
_DcInfo3rs = rpc:call(Cp3, dist_ac, info, []),
DcWa1rs = which_applications(Cp1),
DcWa2rs = which_applications(Cp2),
DcWa3rs = which_applications(Cp3),
Wa1rs = lists:foldl(fun({A1, _N1, _V1}, AccIn) -> [A1 | AccIn] end,
[], DcWa1rs),
Wa2rs = lists:foldl(fun({A2, _N2, _V2}, AccIn) -> [A2 | AccIn] end,
[], DcWa2rs),
Wa3rs = lists:foldl(fun({A3, _N3, _V3}, AccIn) -> [A3 | AccIn] end,
[], DcWa3rs),
case lists:sort(Wa1rs) of
[app6, app8, kernel, stdlib] ->
ok;
EWa1rs ->
X1rs = io_lib:format("distribution error: Cp1 ~p ",[EWa1rs]),
ct:fail(lists:flatten(X1rs))
end,
case lists:sort(Wa2rs) of
[app2, kernel, stdlib] ->
ok;
EWa2rs ->
X2rs = io_lib:format("distribution error: Cp2 ~p ",[EWa2rs]),
ct:fail(lists:flatten(X2rs))
end,
case lists:sort(Wa3rs) of
[app1, app3, app7, kernel, stdlib] ->
ok;
EWa3rs ->
X3rs = io_lib:format("distribution error: Cp3 ~p ",[EWa3rs]),
ct:fail(lists:flatten(X3rs))
end,
stop_node_nice(Cp1),
stop_node_nice(Cp2),
stop_node_nice(Cp3),
ok = file:delete("dc.boot"),
ok = file:delete("dc.rel"),
ok = file:delete("dc.script"),
ok.
%%%-----------------------------------------------------------------
%%% Testing of application configuration change
%%%-----------------------------------------------------------------
%% Test change of application configuration.
config_change(Conf) when is_list(Conf) ->
%% Change to data_dir
{ok, CWD} = file:get_cwd(),
DataDir = proplists:get_value(data_dir, Conf),
ok = file:set_cwd(DataDir),
%% Find out application data from boot script
Boot = filename:join([code:root_dir(), "bin", "start.boot"]),
{ok, Bin} = file:read_file(Boot),
Appls0 = get_appls(binary_to_term(Bin)),
%% And add app1 in order to test OTP-11864 - included config files
%% not read for new (not already loaded) applications
Appls = [app1() | Appls0],
%% Simulate contents of "sys.config"
Config = [{stdlib, [{par1,sys},{par2,sys}]},
"t1",
"t2.config",
filename:join([DataDir, "subdir", "t3"]),
{stdlib, [{par6,sys}]},
"t4.config"],
%% Check that app1 is not loaded
false = lists:keymember(app1,1,application:loaded_applications()),
%% Order application_controller to update configuration
ok = application_controller:change_application_data(Appls,
Config),
%% Check that stdlib parameters are correctly set
Env = application:get_all_env(stdlib),
{value, {par1,sys}} = lists:keysearch(par1, 1, Env),
{value, {par2,t1}} = lists:keysearch(par2, 1, Env),
{value, {par3,t1}} = lists:keysearch(par3, 1, Env),
{value, {par4,t2}} = lists:keysearch(par4, 1, Env),
{value, {par5,t3}} = lists:keysearch(par5, 1, Env),
{value, {par6,sys}} = lists:keysearch(par6, 1, Env),
%% Check that app1 parameters are correctly set after loading
[] = application:get_all_env(app1),
application:load(app1()),
App1Env = application:get_all_env(app1),
{value, {par1,t4}} = lists:keysearch(par1, 1, App1Env),
application:unload(app1),
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.
%% Test set_env/1.
set_env(Conf) when is_list(Conf) ->
ok = application:set_env([{appinc, [{own2, persist}, {not_in_app, persist}]},
{unknown_app, [{key, persist}]}]),
%% own_env1 and own2 are set in appinc
undefined = application:get_env(appinc, own_env1),
{ok, persist} = application:get_env(appinc, own2),
{ok, persist} = application:get_env(appinc, not_in_app),
{ok, persist} = application:get_env(unknown_app, key),
ok = application:load(appinc()),
{ok, value1} = application:get_env(appinc, own_env1),
{ok, val2} = application:get_env(appinc, own2),
{ok, persist} = application:get_env(appinc, not_in_app),
{ok, persist} = application:get_env(unknown_app, key),
%% On reload, values are lost
ok = application:unload(appinc),
ok = application:load(appinc()),
{ok, value1} = application:get_env(appinc, own_env1),
{ok, val2} = application:get_env(appinc, own2),
undefined = application:get_env(appinc, not_in_app),
%% Clean up
ok = application:unload(appinc).
%% Test set_env/2 with persistent true.
set_env_persistent(Conf) when is_list(Conf) ->
Opts = [{persistent, true}],
ok = application:set_env([{appinc, [{own2, persist}, {not_in_app, persist}]},
{unknown_app, [{key, persist}]}], Opts),
%% own_env1 and own2 are set in appinc
undefined = application:get_env(appinc, own_env1),
{ok, persist} = application:get_env(appinc, own2),
{ok, persist} = application:get_env(appinc, not_in_app),
{ok, persist} = application:get_env(unknown_app, key),
ok = application:load(appinc()),
{ok, value1} = application:get_env(appinc, own_env1),
{ok, persist} = application:get_env(appinc, own2),
{ok, persist} = application:get_env(appinc, not_in_app),
{ok, persist} = application:get_env(unknown_app, key),
%% On reload, values are not lost
ok = application:unload(appinc),
ok = application:load(appinc()),
{ok, value1} = application:get_env(appinc, own_env1),
{ok, persist} = application:get_env(appinc, own2),
{ok, persist} = application:get_env(appinc, not_in_app),
%% Clean up
ok = application:unload(appinc).
set_env_errors(Conf) when is_list(Conf) ->
"application: 1; application name must be an atom" =
badarg_msg(fun() -> application:set_env([{1, []}]) end),
"application: foo; parameters must be a list" =
badarg_msg(fun() -> application:set_env([{foo, bar}]) end),
"invalid application config: foo_bar" =
badarg_msg(fun() -> application:set_env([foo_bar]) end),
"application: foo; invalid parameter name: 1" =
badarg_msg(fun() -> application:set_env([{foo, [{1, 2}]}]) end),
"application: foo; invalid parameter: config" =
badarg_msg(fun() -> application:set_env([{foo, [config]}]) end),
"application: kernel; erroneous parameter: distributed" =
badarg_msg(fun() -> application:set_env([{kernel, [{distributed, config}]}]) end),
%% This will raise in the future
ct:capture_start(),
_ = application:set_env([{foo, []}, {foo, []}]),
timer:sleep(100),
ct:capture_stop(),
[_ | _] = string:find(ct:capture_get(), "duplicate application config: foo"),
ct:capture_start(),
_ = application:set_env([{foo, [{bar, baz}, {bar, bat}]}]),
timer:sleep(100),
ct:capture_stop(),
[_ | _] = string:find(ct:capture_get(), "application: foo; duplicate parameter: bar"),
ok.
badarg_msg(Fun) ->
try Fun() of
_ -> ct:fail(try_succeeded)
catch
error:{badarg, Msg} -> Msg
end.
%% Test set_env/4 and unset_env/3 with persistent true.
persistent_env(Conf) when is_list(Conf) ->
ok = application:set_env(appinc, own2, persist, [{persistent, true}]),
ok = application:set_env(appinc, key1, persist, [{persistent, true}]),
%% own_env1 and own2 are set in appinc
ok = application:load(appinc()),
{ok, value1} = application:get_env(appinc, own_env1),
{ok, persist} = application:get_env(appinc, own2),
{ok, persist} = application:get_env(appinc, key1),
%% Changing the environment after loaded reflects and should persist
ok = application:set_env(appinc, own_env1, persist, [{persistent, true}]),
{ok, persist} = application:get_env(appinc, own_env1),
{ok, persist} = application:get_env(appinc, own2),
{ok, persist} = application:get_env(appinc, key1),
%% On reload, own_env1, own2 and key1 should all persist
ok = application:unload(appinc),
ok = application:load(appinc()),
{ok, persist} = application:get_env(appinc, own_env1),
{ok, persist} = application:get_env(appinc, own2),
{ok, persist} = application:get_env(appinc, key1),
%% Unset own_env1 and key1, own2 should still persist
ok = application:unset_env(appinc, own_env1, [{persistent, true}]),
ok = application:unset_env(appinc, key1, [{persistent, true}]),
undefined = application:get_env(appinc, own_env1),
{ok, persist} = application:get_env(appinc, own2),
undefined = application:get_env(appinc, key1),
%% own_env1 should be back to its application value on reload
ok = application:unload(appinc),
ok = application:load(appinc()),
{ok, value1} = application:get_env(appinc, own_env1),
{ok, persist} = application:get_env(appinc, own2),
undefined = application:get_env(appinc, key1),
%% Clean up
ok = application:unload(appinc).
%%%-----------------------------------------------------------------
%%% Tests the 'shutdown_func' kernel config parameter
%%%-----------------------------------------------------------------
%% Tests the 'shutdown_func' kernel config parameter.
shutdown_func(Config) when is_list(Config) ->
{ok,Cp1} = start_node(?MODULE_STRING++"_shutdown_func"),
wait_for_ready_net(),
Tag = make_ref(),
ok = rpc:call(Cp1, application, set_env,
[kernel, shutdown_func, {?MODULE, do_shutdown}]),
ok = rpc:call(Cp1, application, set_env,
[kernel, shutdown_func_test, {self(), Tag}]),
_ = rpc:call(Cp1, init, stop, []),
receive
{Pid, Tag, shutting_down, shutdown} ->
Mref = erlang:monitor(process, Pid),
Pid ! {self(), Tag, ok},
receive
{'DOWN', Mref, _, Pid, noconnection} ->
ok
after 10000 ->
ct:fail(timeout)
end
after 10000 ->
ct: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.
%%%-----------------------------------------------------------------
%%% Tests the 'shutdown_timeout' kernel config parameter
%%%-----------------------------------------------------------------
shutdown_timeout(Config) when is_list(Config) ->
DataDir = proplists:get_value(data_dir,Config),
{ok,Cp1} = start_node(?MODULE_STRING++"_shutdown_timeout"),
wait_for_ready_net(),
ok = rpc:call(Cp1, application, set_env, [kernel, shutdown_timeout, 1000]),
rpc:call(Cp1, code, add_path, [filename:join([DataDir,deadlock])]),
ok = rpc:call(Cp1, application, start, [sasl]),
ok = rpc:call(Cp1, application, start, [deadlock]),
rpc:call(Cp1, deadlock, restart_and_fail, []),
ok = net_kernel:monitor_nodes(true),
_ = rpc:call(Cp1, init, stop, []),
receive
{nodedown,Cp1} ->
ok
after 10000 ->
ct:fail("timeout 10 sec: node termination hangs")
end,
ok.
%%%-----------------------------------------------------------------
%%% Provokes a (previous) application shutdown deadlock
%%%-----------------------------------------------------------------
shutdown_deadlock(Config) when is_list(Config) ->
DataDir = proplists:get_value(data_dir,Config),
code:add_path(filename:join([DataDir,deadlock])),
%% ok = rpc:call(Cp1, application, start, [sasl]),
ok = application:start(deadlock),
Tester = self(),
application:set_env(deadlock, fail_stop, Tester),
spawn(fun() -> Tester ! {stop, application:stop(deadlock)} end),
receive
{deadlock, Server} ->
spawn(fun() ->
Master = application_controller:get_master(deadlock),
Child = application_master:get_child(Master),
Tester ! {child, Child}
end),
timer:sleep(100),
erlang:display({self(), "Sending Continue", Server}),
Server ! continue
end,
[_|_] = application:which_applications(),
application:unload(deadlock), % clean up!
ok.
%%-----------------------------------------------------------------
%% Relative paths in sys.config
%%-----------------------------------------------------------------
config_relative_paths(Config) ->
Dir = ?config(priv_dir,Config),
SubDir = filename:join(Dir,"subdir"),
Sys = filename:join(SubDir,"sys.config"),
ok = filelib:ensure_dir(Sys),
ok = file:write_file(Sys,"[\"../up.config\",\"current\"].\n"),
Up = filename:join(Dir,"up.config"),
ok = file:write_file(Up,"[{app1,[{key1,value}]}].\n"),
{ok,Cwd} = file:get_cwd(),
Current1 = filename:join(Cwd,"current.config"),
ok = file:write_file(Current1,"[{app1,[{key2,value1}]}].\n"),
N1 = list_to_atom(lists:concat([?FUNCTION_NAME,"_1"])),
{ok,Node1} = start_node(N1,filename:rootname(Sys)),
ok = rpc:call(Node1, application, load, [app1()]),
{ok, value} = rpc:call(Node1, application, get_env,[app1,key1]),
{ok, value1} = rpc:call(Node1, application, get_env,[app1,key2]),
Current2 = filename:join(SubDir,"current.config"),
ok = file:write_file(Current2,"[{app1,[{key2,value2}]}].\n"),
N2 = list_to_atom(lists:concat([?FUNCTION_NAME,"_2"])),
{ok, Node2} = start_node(N2,filename:rootname(Sys)),
ok = rpc:call(Node2, application, load, [app1()]),
{ok, value} = rpc:call(Node2, application, get_env,[app1,key1]),
{ok, value2} = rpc:call(Node2, application, get_env,[app1,key2]),
stop_node_nice([Node1,Node2]),
ok.
%%-----------------------------------------------------------------
%% 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_chain_error() ->
{application, app_chain_error,
[{description, "ERTS CXC 138 ce"},
{vsn, "2.0"},
{modules, []},
{registered, []},
{applications, [kernel, app_chain_error2]},
{mod, {ch_sup, {app_chain_error, 20,20}}}]}.
app_chain_error2() ->
{application, app_chain_error2,
[{description, "ERTS CXC 138 ce2"},
{vsn, "2.0"},
{modules, []},
{registered, []},
{applications, [kernel, app10, hopefully_not_an_existing_app]},
{mod, {ch_sup, {app_chain_error2, 21,21}}}]}.
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_app9(Fd) ->
io:format(Fd, "~p.\n", [app9()]).
w_app10_dep9(Fd) ->
io:format(Fd, "~p.\n", [app10_dep9()]).
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 ->
ct: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 ->
ct: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)),
ct:pal(?HI_VERBOSITY, "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)),
ct:pal(?HI_VERBOSITY,
"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 = proplists:get_value(data_dir, Conf), % is it used?
start_node(Name, ConfigFile, " -pa " ++ DataDir).
write_config_file(SysConfigFun, Conf) ->
Dir = proplists:get_value(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 = "_",
L = integer_to_list(erlang:unique_integer([positive])),
lists:concat([Name,U,?testcase,U,U,L]).
stop_node_nice(Node) when is_atom(Node) ->
test_server:stop_node(Node);
stop_node_nice(Nodes) when is_list(Nodes) ->
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) ->
io:format("====== ~p ======~n", [Ack]),
ct: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} ->
io:format("=============== {sp,T,I,So,Sp,G} ~p ~n",[" "]),
io:format("=========== got ~p ~n",
[{sp, T1, I1, So1, Sp1, G1}]),
io:format("====== expected ~p ~n", [Expected]),
ct:fail(not_valid_start_phase)
after 5000 ->
ct: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} ->
io:format("====== ~p ======~n",[{cc, List}]),
ct:fail(not_valid_conf_change)
after 5000 ->
ct: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() ->
Dir = "./",
App1 = Dir ++ "app1",
{ok, Fd1} = file:open(App1++".app",[write]),
io:format(Fd1, "~p. \n", [app1()]),
file:close(Fd1),
App2 = Dir ++ "app2",
{ok, Fd2} = file:open(App2++".app",[write]),
io:format(Fd2, "~p. \n", [app2()]),
file:close(Fd2),
App3 = Dir ++ "app_sp",
{ok, Fd3} = file:open(App3++".app",[write]),
io:format(Fd3, "~p. \n", [app_sp()]),
file:close(Fd3),
ok.
create_script(ScriptName) ->
Dir = "./",
Name = Dir ++ ScriptName,
Apps = which_applications(),
{value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps),
{value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps),
{ok,Fd} = file:open(Name++".rel",[write]),
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]),
file:close(Fd),
{{KernelVer,StdlibVer},
{filename:dirname(Name), filename:basename(Name)}}.
create_script_dc(ScriptName) ->
Dir = "./",
Name = Dir ++ ScriptName,
Apps = which_applications(),
{value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps),
{value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps),
{ok,Fd} = file:open(Name++".rel",[write]),
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]),
file:close(Fd),
{{KernelVer,StdlibVer},
{filename:dirname(Name), filename:basename(Name)}}.
create_script_3002(ScriptName) ->
Dir = "./",
Name = Dir ++ ScriptName,
Apps = which_applications(),
{value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps),
{value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps),
{value,{_,_,SaslVer}} = lists:keysearch(sasl,1,Apps),
{ok,Fd} = file:open(Name++".rel",[write]),
io:format(Fd,
"{release, {\"Test release 3\", \"LATEST\"}, \n"
" {erts, \"4.4\"}, \n"
" [{kernel, \"~s\"}, {stdlib, \"~s\"}, \n"
" {sasl, \"~s\"}]}.\n",
[KernelVer, StdlibVer, SaslVer]),
file:close(Fd),
{{KernelVer,StdlibVer},
{filename:dirname(Name), filename:basename(Name)}}.
distr_changed_prep(Conf) when is_list(Conf) ->
%% Write .app files
{ok, Fd1} = file:open("app1.app", [write]),
w_app1(Fd1),
file:close(Fd1),
{ok, Fd2} = file:open("app2.app", [write]),
w_app2(Fd2),
file:close(Fd2),
{ok, Fd3} = file:open("app3.app", [write]),
w_app3(Fd3),
file:close(Fd3),
{ok, Fd4} = file:open("app6.app", [write]),
w_app6(Fd4),
file:close(Fd4),
{ok, Fd5} = file:open("app7.app", [write]),
w_app7(Fd5),
file:close(Fd5),
{ok, Fd6} = file:open("app8.app", [write]),
w_app8(Fd6),
file:close(Fd6),
%% Create the .app files and the boot script
{{KernelVer,StdlibVer}, _} = create_script_dc("dc"),
case is_real_system(KernelVer, StdlibVer) of
true ->
Options = [];
false ->
Options = [local]
end,
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)),
Dir = proplists:get_value(priv_dir,Conf),
{ok, Fd_dc2} = file:open(filename:join(Dir, "sys2.config"), [write]),
(config_dc2(NodeNames))(Fd_dc2),
file:close(Fd_dc2),
Config2 = filename:join(Dir, "sys2"),
%% Test [cp1, cp2, cp3]
{ok, Cp1} = start_node_boot_config(Ncp1, NoSyncTime, Conf, dc),
{ok, Cp2} = start_node_boot_config(Ncp2, NoSyncTime, Conf, dc),
{ok, Cp3} = start_node_boot_config(Ncp3, WithSyncTime, Conf, dc),
global:sync(),
%% Read the current configuration parameters, and change them
OldEnv = rpc:call(Cp1, application_controller, prep_config_change, []),
{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]).